summaryrefslogtreecommitdiffstats
path: root/helm/plugin/tasks.py
diff options
context:
space:
mode:
Diffstat (limited to 'helm/plugin/tasks.py')
-rw-r--r--helm/plugin/tasks.py504
1 files changed, 504 insertions, 0 deletions
diff --git a/helm/plugin/tasks.py b/helm/plugin/tasks.py
new file mode 100644
index 0000000..9c0e2fe
--- /dev/null
+++ b/helm/plugin/tasks.py
@@ -0,0 +1,504 @@
+# ============LICENSE_START==========================================
+# ===================================================================
+# Copyright (c) 2018-2020 AT&T
+# Copyright (c) 2020 Pantheon.tech. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END============================================
+
+import shutil
+import errno
+import sys
+import pwd
+import grp
+import os
+import re
+import getpass
+import subprocess
+import json
+import base64
+import yaml
+try:
+ from urllib.request import Request, urlopen
+except ImportError:
+ from urllib2 import Request, urlopen
+
+from cloudify import ctx
+from cloudify import exceptions
+from cloudify.decorators import operation
+from cloudify.exceptions import OperationRetry
+from cloudify.exceptions import NonRecoverableError
+from cloudify_rest_client.exceptions import CloudifyClientError
+
+
+def debug_log_mask_credentials(_command_str):
+ debug_str = _command_str
+ if _command_str.find("@") != -1:
+ head, end = _command_str.rsplit('@', 1)
+ proto, auth = head.rsplit('//', 1)
+ uname, passwd = auth.rsplit(':', 1)
+ debug_str = _command_str.replace(passwd, "************")
+ ctx.logger.debug('command {0}.'.format(debug_str))
+
+def execute_command(_command):
+ debug_log_mask_credentials(_command)
+
+ subprocess_args = {
+ 'args': _command.split(),
+ 'stdout': subprocess.PIPE,
+ 'stderr': subprocess.PIPE
+ }
+
+ debug_log_mask_credentials(str(subprocess_args))
+ try:
+ process = subprocess.Popen(**subprocess_args)
+ output, error = process.communicate()
+ except Exception as e:
+ ctx.logger.debug(str(e))
+ return False
+
+ debug_log_mask_credentials(_command)
+ ctx.logger.debug('output: {0} '.format(output))
+ ctx.logger.debug('error: {0} '.format(error))
+ ctx.logger.debug('process.returncode: {0} '.format(process.returncode))
+
+ if process.returncode:
+ ctx.logger.error('Error was returned while running helm command')
+ return False
+
+ return output
+
+
+def configure_admin_conf():
+ # Add the kubeadmin config to environment
+ agent_user = getpass.getuser()
+ uid = pwd.getpwnam(agent_user).pw_uid
+ gid = grp.getgrnam('docker').gr_gid
+ admin_file_dest = os.path.join(os.path.expanduser('~'), 'admin.conf')
+
+ execute_command(
+ 'sudo cp {0} {1}'.format('/etc/kubernetes/admin.conf',
+ admin_file_dest))
+ execute_command('sudo chown {0}:{1} {2}'.format(uid, gid, admin_file_dest))
+
+ with open(os.path.join(os.path.expanduser('~'), '.bashrc'),
+ 'a') as outfile:
+ outfile.write('export KUBECONFIG=$HOME/admin.conf')
+ os.environ['KUBECONFIG'] = admin_file_dest
+
+
+def get_current_helm_value(chart_name):
+ tiller_host = str(ctx.node.properties['tiller_ip']) + ':' + str(
+ ctx.node.properties['tiller_port'])
+ config_dir_root = str(ctx.node.properties['config_dir'])
+ config_dir = config_dir_root + str(ctx.deployment.id) + '/'
+ if str_to_bool(ctx.node.properties['tls_enable']):
+ getValueCommand = subprocess.Popen(
+ ["helm", "get", "values", "-a", chart_name, '--host', tiller_host,
+ '--tls', '--tls-ca-cert', config_dir + 'ca.cert.pem',
+ '--tls-cert',
+ config_dir + 'helm.cert.pem', '--tls-key',
+ config_dir + 'helm.key.pem'], stdout=subprocess.PIPE)
+ else:
+ getValueCommand = subprocess.Popen(
+ ["helm", "get", "values", "-a", chart_name, '--host', tiller_host],
+ stdout=subprocess.PIPE)
+ value = getValueCommand.communicate()[0]
+ valueMap = {}
+ valueMap = yaml.safe_load(value)
+ ctx.instance.runtime_properties['current-helm-value'] = valueMap
+
+
+def get_helm_history(chart_name):
+ tiller_host = str(ctx.node.properties['tiller_ip']) + ':' + str(
+ ctx.node.properties['tiller_port'])
+ config_dir_root = str(ctx.node.properties['config_dir'])
+ config_dir = config_dir_root + str(ctx.deployment.id) + '/'
+ if str_to_bool(ctx.node.properties['tls_enable']):
+ getHistoryCommand = subprocess.Popen(
+ ["helm", "history", chart_name, '--host', tiller_host, '--tls',
+ '--tls-ca-cert', config_dir + 'ca.cert.pem', '--tls-cert',
+ config_dir + 'helm.cert.pem', '--tls-key',
+ config_dir + 'helm.key.pem'], stdout=subprocess.PIPE)
+ else:
+ getHistoryCommand = subprocess.Popen(
+ ["helm", "history", chart_name, '--host', tiller_host],
+ stdout=subprocess.PIPE)
+ history = getHistoryCommand.communicate()[0]
+ history_start_output = [line.strip() for line in history.split('\n') if
+ line.strip()]
+ for index in range(len(history_start_output)):
+ history_start_output[index] = history_start_output[index].replace('\t',
+ ' ')
+ ctx.instance.runtime_properties['helm-history'] = history_start_output
+
+
+def tls():
+ if str_to_bool(ctx.node.properties['tls_enable']):
+ config_dir_root = str(ctx.node.properties['config_dir'])
+ config_dir = config_dir_root + str(ctx.deployment.id) + '/'
+ tls_command = ' --tls --tls-ca-cert ' + config_dir + 'ca.cert.pem ' \
+ '--tls-cert ' + \
+ config_dir + 'helm.cert.pem --tls-key ' + config_dir + \
+ 'helm.key.pem '
+ ctx.logger.debug(tls_command)
+ return tls_command
+ else:
+ return ''
+
+
+def tiller_host():
+ tiller_host = ' --host ' + str(
+ ctx.node.properties['tiller_ip']) + ':' + str(
+ ctx.node.properties['tiller_port']) + ' '
+ ctx.logger.debug(tiller_host)
+ return tiller_host
+
+
+def str_to_bool(s):
+ s = str(s)
+ if s == 'True' or s == 'true':
+ return True
+ elif s == 'False' or s == 'false':
+ return False
+ else:
+ raise ValueError('Require [Tt]rue or [Ff]alse; got: {0}'.format(s))
+
+
+def get_config_json(config_json, config_path, config_opt_f, config_file_nm):
+ config_obj = {}
+ config_obj = json.loads(config_json)
+ config_file = config_path + config_file_nm + ".yaml"
+ gen_config_file(config_file, config_obj)
+ config_opt_f = config_opt_f + " -f " + config_file
+ return config_opt_f
+
+
+def pop_config_info(url, config_file, f_format, repo_user, repo_user_passwd):
+ if url.find("@") != -1:
+ head, end = url.rsplit('@', 1)
+ head, auth = head.rsplit('//', 1)
+ url = head + '//' + end
+ username, password = auth.rsplit(':', 1)
+ request = Request(url)
+ base64string = base64.encodestring(
+ '%s:%s' % (username, password)).replace('\n', '')
+ request.add_header("Authorization", "Basic %s" % base64string)
+ response = urlopen(request)
+ elif repo_user != '' and repo_user_passwd != '':
+ request = Request(url)
+ base64string = base64.b64encode('%s:%s' % (repo_user, repo_user_passwd))
+ request.add_header("Authorization", "Basic %s" % base64string)
+ response = urlopen(request)
+ else:
+ response = urlopen(url)
+
+ config_obj = {}
+ if f_format == 'json':
+ config_obj = json.load(response)
+ elif f_format == 'yaml':
+ config_obj = yaml.load(response)
+ else:
+ raise NonRecoverableError("Unable to get config input format.")
+
+ gen_config_file(config_file, config_obj)
+
+
+def gen_config_file(config_file, config_obj):
+ try:
+ with open(config_file, 'w') as outfile:
+ yaml.safe_dump(config_obj, outfile, default_flow_style=False)
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ raise
+
+
+def gen_config_str(config_file, config_opt_f):
+ try:
+ with open(config_file, 'w') as outfile:
+ yaml.safe_dump(config_opt_f, outfile, default_flow_style=False)
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ raise
+
+
+def get_rem_config(config_url, config_input_format, config_path, config_opt_f, config_file_nm, repo_user, repo_user_passwd):
+ ctx.logger.debug("config_url=" + config_url)
+ f_cnt = 0
+ # urls = config_url.split()
+ urls = [x.strip() for x in config_url.split(',')]
+ if len(urls) > 1:
+ for url in urls:
+ f_cnt = f_cnt + 1
+ config_file = config_path + config_file_nm + str(f_cnt) + ".yaml"
+ pop_config_info(url, config_file, config_input_format, repo_user, repo_user_passwd)
+ config_opt_f = config_opt_f + " -f " + config_file
+ else:
+ config_file = config_path + config_file_nm + ".yaml"
+ pop_config_info(config_url, config_file, config_input_format, repo_user, repo_user_passwd)
+ config_opt_f = config_opt_f + " -f " + config_file
+
+ return config_opt_f
+
+
+def get_config_str(config_file):
+ if os.path.isfile(config_file):
+ with open(config_file, 'r') as config_f:
+ return config_f.read().replace('\n', '')
+ return ''
+
+
+def opt(config_file):
+ opt_str = get_config_str(config_file)
+ if opt_str != '':
+ return opt_str.replace("'", "")
+ return opt_str
+
+def repo(repo_url, repo_user, repo_user_passwd):
+ if repo_user != '' and repo_user_passwd != '' and repo_url.find("@") == -1:
+ proto, ip = repo_url.rsplit('//', 1)
+ return proto + '//' + repo_user + ':' + repo_user_passwd + '@' + ip
+ else:
+ return repo_url
+
+
+@operation
+def config(**kwargs):
+ # create helm value file on K8s master
+ configJson = str(ctx.node.properties['config'])
+ configUrl = str(ctx.node.properties['config_url'])
+ configUrlInputFormat = str(ctx.node.properties['config_format'])
+ runtime_config = str(ctx.node.properties['runtime_config']) # json
+ componentName = ctx.node.properties['component_name']
+ config_dir_root = str(ctx.node.properties['config_dir'])
+ stable_repo_url = str(ctx.node.properties['stable_repo_url'])
+ config_opt_set = str(ctx.node.properties['config_set'])
+ repo_user = str(ctx.node.properties['repo_user'])
+ repo_user_passwd = str(ctx.node.properties['repo_user_password'])
+ ctx.logger.debug("debug " + configJson + runtime_config)
+ # load input config
+ config_dir = config_dir_root + str(ctx.deployment.id)
+
+ if not os.path.exists(config_dir):
+ try:
+ os.makedirs(config_dir)
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ raise
+
+ ctx.logger.debug('tls-enable type ' + str(
+ type(str_to_bool(ctx.node.properties['tls_enable']))))
+
+ # create TLS cert files
+ if str_to_bool(ctx.node.properties['tls_enable']):
+ ctx.logger.debug('tls enable')
+ ca_value = ctx.node.properties['ca']
+ cert_value = ctx.node.properties['cert']
+ key_value = ctx.node.properties['key']
+ ca = open(config_dir + '/ca.cert.pem', "w+")
+ ca.write(ca_value)
+ ca.close()
+ cert = open(config_dir + '/helm.cert.pem', "w+")
+ cert.write(cert_value)
+ cert.close()
+ key = open(config_dir + '/helm.key.pem', "w+")
+ key.write(key_value)
+ key.close()
+ else:
+ ctx.logger.debug('tls disable')
+
+ config_path = config_dir + '/' + componentName + '/'
+ ctx.logger.debug(config_path)
+
+ if os.path.exists(config_path):
+ shutil.rmtree(config_path)
+
+ try:
+ os.makedirs(config_path)
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ raise
+
+ config_opt_f = ""
+ if configJson == '' and configUrl == '':
+ ctx.logger.debug("Will use default HELM value")
+ elif configJson == '' and configUrl != '':
+ config_opt_f = get_rem_config(configUrl, configUrlInputFormat, config_path, config_opt_f, "rc", repo_user, repo_user_passwd)
+ elif configJson != '' and configUrl == '':
+ config_opt_f = get_config_json(configJson, config_path, config_opt_f, "lc")
+ else:
+ raise NonRecoverableError("Unable to get config input")
+
+ ctx.logger.debug("debug check runtime config")
+ if runtime_config == '':
+ ctx.logger.debug("there is no runtime config value")
+ else:
+ config_opt_f = get_config_json(runtime_config, config_path, config_opt_f, "rt")
+
+ if configUrl != '' or configJson != '' or runtime_config != '':
+ config_file = config_path + ".config_file"
+ gen_config_str(config_file, config_opt_f)
+
+ if config_opt_set != '':
+ config_file = config_path + ".config_set"
+ config_opt_set = " --set " + config_opt_set
+ gen_config_str(config_file, config_opt_set)
+
+ output = execute_command(
+ 'helm init --client-only --stable-repo-url ' + repo(stable_repo_url, repo_user, repo_user_passwd))
+ if output == False:
+ raise NonRecoverableError("helm init failed")
+
+
+@operation
+def start(**kwargs):
+ # install the ONAP Helm chart
+ # get properties from node
+ repo_user = str(ctx.node.properties['repo_user'])
+ repo_user_passwd = str(ctx.node.properties['repo_user_password'])
+ chartRepo = ctx.node.properties['chart_repo_url']
+ componentName = ctx.node.properties['component_name']
+ chartVersion = str(ctx.node.properties['chart_version'])
+ config_dir_root = str(ctx.node.properties['config_dir'])
+ namespace = ctx.node.properties['namespace']
+
+ config_path = config_dir_root + str(
+ ctx.deployment.id) + '/' + componentName + '/'
+ chart = chartRepo + "/" + componentName + "-" + str(chartVersion) + ".tgz"
+ chartName = namespace + "-" + componentName
+ config_file = config_path + ".config_file"
+ config_set = config_path + ".config_set"
+ installCommand = 'helm install ' + repo(chart, repo_user, repo_user_passwd) + ' --name ' + chartName + \
+ ' --namespace ' + namespace + opt(config_file) + \
+ opt(config_set) + tiller_host() + tls()
+
+ output = execute_command(installCommand)
+ if output == False:
+ return ctx.operation.retry(
+ message='helm install failed, re-try after 5 second ',
+ retry_after=5)
+
+ get_current_helm_value(chartName)
+ get_helm_history(chartName)
+
+
+@operation
+def stop(**kwargs):
+ # delete the ONAP helm chart
+ # configure_admin_conf()
+ # get properties from node
+ namespace = ctx.node.properties['namespace']
+ component = ctx.node.properties['component_name']
+ chartName = namespace + "-" + component
+ config_dir_root = str(ctx.node.properties['config_dir'])
+ # Delete helm chart
+ command = 'helm delete --purge ' + chartName + tiller_host() + tls()
+ output = execute_command(command)
+ if output == False:
+ raise NonRecoverableError("helm delete failed")
+ config_path = config_dir_root + str(
+ ctx.deployment.id) + '/' + component
+
+ if os.path.exists(config_path):
+ shutil.rmtree(config_path)
+
+
+@operation
+def upgrade(**kwargs):
+ config_dir_root = str(ctx.node.properties['config_dir'])
+ componentName = ctx.node.properties['component_name']
+ namespace = ctx.node.properties['namespace']
+ repo_user = kwargs['repo_user']
+ repo_user_passwd = kwargs['repo_user_passwd']
+ configJson = kwargs['config']
+ chartRepo = kwargs['chart_repo']
+ chartVersion = kwargs['chart_version']
+ config_set = kwargs['config_set']
+ config_json = kwargs['config_json']
+ config_url = kwargs['config_url']
+ config_format = kwargs['config_format']
+ config_path = config_dir_root + str(
+ ctx.deployment.id) + '/' + componentName + '/'
+
+ # ctx.logger.debug('debug ' + str(configJson))
+ chartName = namespace + "-" + componentName
+ chart = chartRepo + "/" + componentName + "-" + chartVersion + ".tgz"
+
+ config_opt_f = ""
+ if config_json == '' and config_url == '':
+ ctx.logger.debug("Will use default HELM values")
+ elif config_json == '' and config_url != '':
+ config_opt_f = get_rem_config(config_url, config_format, config_path, config_opt_f, "ru", repo_user, repo_user_passwd)
+ elif config_json != '' and config_url == '':
+ config_opt_f = get_config_json(config_json, config_path, config_opt_f, "lu")
+ else:
+ raise NonRecoverableError("Unable to get upgrade config input")
+
+ config_upd = ""
+ if config_url != '' or config_json != '':
+ config_upd = config_path + ".config_upd"
+ gen_config_str(config_upd, config_opt_f)
+
+ config_upd_set = ""
+ if config_set != '':
+ config_upd_set = config_path + ".config_upd_set"
+ config_opt_set = " --set " + config_set
+ gen_config_str(config_upd_set, config_opt_set)
+
+ upgradeCommand = 'helm upgrade ' + chartName + ' ' + repo(chart, repo_user, repo_user_passwd) + opt(config_upd) + \
+ opt(config_upd_set) + tiller_host() + tls()
+
+ output = execute_command(upgradeCommand)
+ if output == False:
+ return ctx.operation.retry(
+ message='helm upgrade failed, re-try after 5 second ',
+ retry_after=5)
+ get_current_helm_value(chartName)
+ get_helm_history(chartName)
+
+
+@operation
+def rollback(**kwargs):
+ # rollback to some revision
+ componentName = ctx.node.properties['component_name']
+ namespace = ctx.node.properties['namespace']
+ revision = kwargs['revision']
+ # configure_admin_conf()
+ chartName = namespace + "-" + componentName
+ rollbackCommand = 'helm rollback ' + chartName + ' ' + revision + tiller_host() + tls()
+ output = execute_command(rollbackCommand)
+ if output == False:
+ return ctx.operation.retry(
+ message='helm rollback failed, re-try after 5 second ',
+ retry_after=5)
+ get_current_helm_value(chartName)
+ get_helm_history(chartName)
+
+@operation
+def status(**kwargs):
+ componentName = ctx.node.properties['component_name']
+ namespace = ctx.node.properties['namespace']
+
+ chartName = namespace + "-" + componentName
+ statusCommand = 'helm status ' + chartName + tiller_host() + tls()
+ output = execute_command(statusCommand)
+ if output == False:
+ return ctx.operation.retry(
+ message='helm status failed, re-try after 5 second ',
+ retry_after=5)
+
+ status_output = [line.strip() for line in output.split('\n') if
+ line.strip()]
+ for index in range(len(status_output)):
+ status_output[index] = status_output[index].replace('\t', ' ')
+ ctx.instance.runtime_properties['install-status'] = status_output