Cloudify support for OOM
[oom.git] / cloudify-onap / plugins / onap-installation-plugin / k8s_installer / common / resources_services.py
1 ########
2 # Copyright (c) 2017 GigaSpaces Technologies Ltd. All rights reserved
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 #        http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 #    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 #    * See the License for the specific language governing permissions and
14 #    * limitations under the License.
15
16 import subprocess
17
18 import cloudify_kubernetes.tasks as kubernetes_plugin
19 import yaml
20 from cloudify import ctx
21 from cloudify.exceptions import NonRecoverableError
22
23 import constants
24 import deployment_result
25 import time
26 import ast
27 import json
28 import base64
29
30 SERVICES_FILE_PARTS_SEPARATOR = '---'
31
32
33 def create_resoruces():
34     ctx.logger.info('Creating resources')
35     apps_path = _retrieve_root_path()
36
37     if not apps_path:
38         ctx.logger.warn(
39             'Apps dir is not defined. Skipping!'
40         )
41
42         return
43
44     helm_app = ctx.node.properties.get('path', None)
45
46     yaml_file = prepare_content(helm_app)
47
48     yaml_content_parts = yaml_file.split(SERVICES_FILE_PARTS_SEPARATOR)
49
50     for yaml_content_part in yaml_content_parts:
51         if yaml_content_part:
52             yaml_content = _apply_readiness_workaround(yaml_content_part)
53             if yaml_content:
54                 create_resource(yaml_content)
55
56     ctx.logger.info('Resource created successfully')
57
58 def delete_resoruces():
59
60     ctx.logger.info('Deleting resources')
61     apps_path = _retrieve_root_path()
62
63     if not apps_path:
64         ctx.logger.warn(
65             'Apps dir is not defined. Skipping!'
66         )
67         return
68
69     helm_app = ctx.node.properties.get('path', None)
70
71     yaml_file = prepare_content(helm_app)
72
73     yaml_content_parts = yaml_file.split(SERVICES_FILE_PARTS_SEPARATOR)
74
75     for yaml_content_part in yaml_content_parts:
76         if yaml_content_part:
77             yaml_content = _apply_readiness_workaround(yaml_content_part)
78             if yaml_content:
79                 delete_resource(yaml_content)
80
81         ctx.logger.info('Resources deleted successfully')
82
83
84 def prepare_content(resource):
85     helm_path = _retrieve_helm_cli_path()
86     yaml_file = render_chart(resource, _retrieve_root_path(), helm_path)
87
88     return yaml_file
89
90
91 def create_resource(yaml_content_dict):
92     ctx.logger.debug("Loading yaml: {}".format(yaml_content_dict))
93
94     if yaml_content_dict.get('kind', '') == 'PersistentVolumeClaim':
95         ctx.logger.debug("PersistentVolumeClaim custom handling")
96         kubernetes_plugin.custom_resource_create(definition=yaml_content_dict, api_mapping=_get_persistent_volume_mapping_claim_api())
97     else:
98         kubernetes_plugin.resource_create(definition=yaml_content_dict)
99
100     deployment_result.save_deployment_result('resource_{0}'.format(yaml_content_dict['metadata']['name']))
101
102 def delete_resource(yaml_content_dict):
103     ctx.logger.debug("Loading yaml: {}".format(yaml_content_dict))
104
105     deployment_result.save_deployment_result('resource_{0}'.format(yaml_content_dict['metadata']['name']))
106     if yaml_content_dict.get('kind', '') == 'PersistentVolumeClaim':
107         ctx.logger.debug("PersistentVolumeClaim custom handling")
108         kubernetes_plugin.custom_resource_delete(definition=yaml_content_dict, api_mapping=_get_persistent_volume_mapping_claim_api())
109     else:
110         kubernetes_plugin.resource_delete(definition=yaml_content_dict)
111
112
113 def render_chart(app, app_root_path, helm_cli_path):
114     app_chart_path = "{}/{}/".format(app_root_path, app)
115     ctx.logger.debug('App chart path = {}'.format(app_chart_path))
116     return _exec_helm_template(helm_cli_path, app_chart_path)
117
118
119 def _exec_helm_template(helm_path, chart):
120     cmd = '{0} template {1}'.format(helm_path, chart)
121     ctx.logger.debug('Executing helm template cmd: {}'.format(cmd))
122     rendered = subprocess.Popen(cmd.split(" "), stdout=subprocess.PIPE).stdout.read().decode()
123
124     return rendered
125
126 def _get_persistent_volume_mapping_claim_api():
127     api_mapping = {
128       'create' : {
129         'api': 'CoreV1Api',
130         'method': 'create_namespaced_persistent_volume_claim',
131         'payload': 'V1PersistentVolumeClaim'
132       },
133       'read' : {
134         'api': 'CoreV1Api',
135         'method': 'read_namespaced_persistent_volume_claim',
136       },
137       'delete': {
138         'api': 'CoreV1Api',
139         'method': 'delete_namespaced_persistent_volume_claim',
140         'payload': 'V1DeleteOptions'
141       }
142     }
143
144     return api_mapping
145
146
147 def _apply_readiness_workaround(yaml_file):
148     b64_env = _get_k8s_b64_env()
149
150     input_dict = yaml.load(yaml_file)
151
152     try:
153         init_containers = input_dict['spec']['template']['metadata']['annotations'][
154             'pod.beta.kubernetes.io/init-containers']
155         init_cont_list = eval(init_containers)
156
157         new_init_cont_list = list()
158         new_cont = None
159         for init_cont in init_cont_list:
160             if "oomk8s/readiness-check" in init_cont['image']:
161                 init_cont['image'] = "clfy/oomk8s-cfy-readiness-check:1.0.1"
162                 #init_cont['imagePullPolicy'] = "IfNotPresent"
163                 init_cont['env'].append(b64_env)
164                 new_cont = init_cont
165                 new_init_cont_list.append(json.dumps(init_cont))
166
167         new_payload = ",".join(new_init_cont_list)
168
169         if new_cont:
170             input_dict['spec']['template']['metadata']['annotations'].pop('pod.beta.kubernetes.io/init-containers')
171             input_dict['spec']['template']['metadata']['annotations']['pod.beta.kubernetes.io/init-containers'] = '[{}]'.format(new_payload)
172
173
174     except KeyError as ke:
175         ctx.logger.debug('Readiness section is not found.')
176
177     return input_dict
178
179
180 def _get_k8s_b64():
181     target_relationship = _retrieve_managed_by_master()
182
183     k8s_config = target_relationship.node.properties.get('configuration').get('file_content')
184
185     if not k8s_config:
186         raise Exception("Cannot find kubernetes config")
187
188     k8s_config_plain = yaml.dump(k8s_config, allow_unicode=True)
189
190     k8s_config_b64 = base64.b64encode(k8s_config_plain)
191
192     return k8s_config_b64
193
194
195 def _get_k8s_b64_env():
196     env = dict()
197     env['name'] = 'K8S_CONFIG_B64'
198     env['value'] = _get_k8s_b64()
199     return env
200
201
202 def _retrieve_root_path():
203     target_relationship = _retrieve_depends_on()
204
205     apps_root_path = target_relationship.instance.runtime_properties.get(constants.RT_APPS_ROOT_PATH, None)
206
207     ctx.logger.debug("Retrived apps root path = {}".format(apps_root_path))
208
209     return apps_root_path
210
211 def _retrieve_helm_cli_path():
212     target_relationship = _retrieve_depends_on()
213
214     helm_cli_path = target_relationship.instance.runtime_properties.get(constants.RT_HELM_CLI_PATH, None)
215
216     ctx.logger.debug("Retrived helm clis path = {}".format(helm_cli_path))
217
218     return helm_cli_path
219
220 def _retrieve_depends_on():
221     result = None
222     for relationship in ctx.instance.relationships:
223         if relationship.type == 'cloudify.relationships.depends_on':
224             return relationship.target
225
226 def _retrieve_managed_by_master():
227     result = None
228     for relationship in ctx.instance.relationships:
229         if relationship.type == 'cloudify.kubernetes.relationships.managed_by_master':
230             return relationship.target