def main(): module = AnsibleModule(argument_spec=dict( binary_path=dict(type='path'), chart_ref=dict(type='path', required=True), chart_repo_url=dict(type='str'), chart_version=dict(type='str'), include_crds=dict(type='bool', default=False), output_dir=dict(type='path'), release_values=dict(type='dict', default={}, aliases=['values']), values_files=dict(type='list', default=[], elements='str'), update_repo_cache=dict(type='bool', default=False)), supports_check_mode=True) check_mode = module.check_mode bin_path = module.params.get('binary_path') chart_ref = module.params.get('chart_ref') chart_repo_url = module.params.get('chart_repo_url') chart_version = module.params.get('chart_version') include_crds = module.params.get('include_crds') output_dir = module.params.get('output_dir') release_values = module.params.get('release_values') values_files = module.params.get('values_files') update_repo_cache = module.params.get('update_repo_cache') if not IMP_YAML: module.fail_json(msg=missing_required_lib("yaml"), exception=IMP_YAML_ERR) helm_cmd = bin_path or module.get_bin_path('helm', required=True) if update_repo_cache: update_cmd = helm_cmd + " repo update" run_helm(module, update_cmd) tmpl_cmd = template(helm_cmd, chart_ref, chart_repo_url=chart_repo_url, chart_version=chart_version, output_dir=output_dir, release_values=release_values, values_files=values_files, include_crds=include_crds) if not check_mode: rc, out, err = run_helm(module, tmpl_cmd) else: out = err = "" rc = 0 module.exit_json(failed=False, changed=True, command=tmpl_cmd, stdout=out, stderr=err, rc=rc)
def helmdiff_check(module, helm_cmd, release_name, chart_ref, release_values, values_files=None, chart_version=None, replace=False): """ Use helm diff to determine if a release would change by upgrading a chart. """ cmd = helm_cmd + " diff upgrade" cmd += " " + release_name cmd += " " + chart_ref if chart_version is not None: cmd += " " + "--version=" + chart_version if not replace: cmd += " " + "--reset-values" if release_values != {}: fd, path = tempfile.mkstemp(suffix='.yml') with open(path, 'w') as yaml_file: yaml.dump(release_values, yaml_file, default_flow_style=False) cmd += " -f=" + path if values_files: for values_file in values_files: cmd += " -f=" + values_file rc, out, err = run_helm(module, cmd) return len(out.strip()) > 0
def fetch_chart_info(module, command, chart_ref): """ Get chart info """ inspect_command = command + " show chart " + chart_ref rc, out, err = run_helm(module, inspect_command) return yaml.safe_load(out)
def test_run_helm_with_params(): module = MockedModule() module.params = { "api_key": "my-api-key", "ca_cert": "my-ca-cert", "host": "some-host", "context": "my-context", "release_namespace": "a-release-namespace", "validate_certs": False, } run_helm(module, "helm foo") assert module.r["command"] == "helm foo" assert module.r["environ_update"]["HELM_KUBEAPISERVER"] == "some-host" assert module.r["environ_update"]["HELM_KUBECONTEXT"] == "my-context" assert module.r["environ_update"]["HELM_KUBETOKEN"] == "my-api-key" assert module.r["environ_update"][ "HELM_NAMESPACE"] == "a-release-namespace" assert module.r["environ_update"]["KUBECONFIG"] assert not os.path.exists(module.r["environ_update"]["KUBECONFIG"])
def has_plugin(command, plugin): """ Check if helm plugin is installed. """ cmd = command + " plugin list" rc, out, err = run_helm(module, cmd) for line in out.splitlines(): if line.startswith("NAME"): continue name, _rest = line.split(None, 1) if name == plugin: return True return False
def get_repository_status(module, command, repository_name): list_command = command + " repo list --output=yaml" rc, out, err = run_helm(module, list_command, fails_on_error=False) # no repo => rc=1 and 'no repositories to show' in output if rc == 1 and "no repositories to show" in err: return None elif rc != 0: module.fail_json( msg= "Failure when executing Helm command. Exited {0}.\nstdout: {1}\nstderr: {2}" .format(rc, out, err), command=list_command) return get_repository(yaml.safe_load(out), repository_name)
def get_release_status(module, command, release_name): """ Get Release state from deployed release """ list_command = command + " list --output=yaml --filter " + release_name rc, out, err = run_helm(module, list_command) release = get_release(yaml.safe_load(out), release_name) if release is None: # not install return None release['values'] = get_values(module, command, release_name) return release
def get_release_status(module, command, release_name): list_command = command + " list --output=yaml --filter " + release_name rc, out, err = run_helm(module, list_command) if rc != 0: module.fail_json( msg= "Failure when executing Helm command. Exited {0}.\nstdout: {1}\nstderr: {2}" .format(rc, out, err), command=list_command) release = get_release(yaml.safe_load(out), release_name) if release is None: # not install return None release['values'] = get_values(module, command, release_name) return release
def main(): global module module = AnsibleModule( argument_spec=dict( binary_path=dict(type='path'), chart_ref=dict(type='path'), chart_repo_url=dict(type='str'), chart_version=dict(type='str'), release_name=dict(type='str', required=True, aliases=['name']), release_namespace=dict(type='str', required=True, aliases=['namespace']), release_state=dict(default='present', choices=['present', 'absent'], aliases=['state']), release_values=dict(type='dict', default={}, aliases=['values']), values_files=dict(type='list', default=[], elements='str'), update_repo_cache=dict(type='bool', default=False), # Helm options disable_hook=dict(type='bool', default=False), force=dict(type='bool', default=False), context=dict(type='str', aliases=['kube_context'], fallback=(env_fallback, ['K8S_AUTH_CONTEXT'])), kubeconfig=dict(type='path', aliases=['kubeconfig_path'], fallback=(env_fallback, ['K8S_AUTH_KUBECONFIG'])), purge=dict(type='bool', default=True), wait=dict(type='bool', default=False), wait_timeout=dict(type='str'), atomic=dict(type='bool', default=False), create_namespace=dict(type='bool', default=False), replace=dict(type='bool', default=False), skip_crds=dict(type='bool', default=False), # Generic auth key host=dict(type='str', fallback=(env_fallback, ['K8S_AUTH_HOST'])), ca_cert=dict(type='path', aliases=['ssl_ca_cert'], fallback=(env_fallback, ['K8S_AUTH_SSL_CA_CERT'])), validate_certs=dict(type='bool', default=True, aliases=['verify_ssl'], fallback=(env_fallback, ['K8S_AUTH_VERIFY_SSL'])), api_key=dict(type='str', no_log=True, fallback=(env_fallback, ['K8S_AUTH_API_KEY'])) ), required_if=[ ('release_state', 'present', ['release_name', 'chart_ref']), ('release_state', 'absent', ['release_name']) ], mutually_exclusive=[ ("context", "ca_cert"), ("context", "validate_certs"), ("kubeconfig", "ca_cert"), ("kubeconfig", "validate_certs") ], supports_check_mode=True, ) if not IMP_YAML: module.fail_json(msg=missing_required_lib("yaml"), exception=IMP_YAML_ERR) changed = False bin_path = module.params.get('binary_path') chart_ref = module.params.get('chart_ref') chart_repo_url = module.params.get('chart_repo_url') chart_version = module.params.get('chart_version') release_name = module.params.get('release_name') release_state = module.params.get('release_state') release_values = module.params.get('release_values') values_files = module.params.get('values_files') update_repo_cache = module.params.get('update_repo_cache') # Helm options disable_hook = module.params.get('disable_hook') force = module.params.get('force') purge = module.params.get('purge') wait = module.params.get('wait') wait_timeout = module.params.get('wait_timeout') atomic = module.params.get('atomic') create_namespace = module.params.get('create_namespace') replace = module.params.get('replace') skip_crds = module.params.get('skip_crds') if bin_path is not None: helm_cmd_common = bin_path else: helm_cmd_common = module.get_bin_path('helm', required=True) if update_repo_cache: run_repo_update(module, helm_cmd_common) # Get real/deployed release status release_status = get_release_status(module, helm_cmd_common, release_name) # keep helm_cmd_common for get_release_status in module_exit_json helm_cmd = helm_cmd_common if release_state == "absent" and release_status is not None: if replace: module.fail_json(msg="replace is not applicable when state is absent") helm_cmd = delete(helm_cmd, release_name, purge, disable_hook) changed = True elif release_state == "present": if chart_version is not None: helm_cmd += " --version=" + chart_version if chart_repo_url is not None: helm_cmd += " --repo=" + chart_repo_url # Fetch chart info to have real version and real name for chart_ref from archive, folder or url chart_info = fetch_chart_info(module, helm_cmd, chart_ref) if release_status is None: # Not installed helm_cmd = deploy(helm_cmd, release_name, release_values, chart_ref, wait, wait_timeout, disable_hook, False, values_files=values_files, atomic=atomic, create_namespace=create_namespace, replace=replace, skip_crds=skip_crds) changed = True else: if has_plugin(helm_cmd_common, "diff") and not chart_repo_url: would_change = helmdiff_check(module, helm_cmd_common, release_name, chart_ref, release_values, values_files, chart_version, replace) else: module.warn("The default idempotency check can fail to report changes in certain cases. " "Install helm diff for better results.") would_change = default_check(release_status, chart_info, release_values, values_files) if force or would_change: helm_cmd = deploy(helm_cmd, release_name, release_values, chart_ref, wait, wait_timeout, disable_hook, force, values_files=values_files, atomic=atomic, create_namespace=create_namespace, replace=replace, skip_crds=skip_crds) changed = True if module.check_mode: check_status = { 'values': { "current": {}, "declared": {}, } } if release_status: check_status['values']['current'] = release_status['values'] check_status['values']['declared'] = release_status module.exit_json( changed=changed, command=helm_cmd, status=check_status, stdout='', stderr='', ) elif not changed: module.exit_json( changed=False, status=release_status, stdout='', stderr='', command=helm_cmd, ) rc, out, err = run_helm(module, helm_cmd) module.exit_json( changed=changed, stdout=out, stderr=err, status=get_release_status(module, helm_cmd_common, release_name), command=helm_cmd, )
def run_repo_update(module, command): """ Run Repo update """ repo_update_command = command + " repo update" rc, out, err = run_helm(module, repo_update_command)
def main(): module = AnsibleModule( argument_spec=dict( binary_path=dict(type='path'), release_namespace=dict(type='str', aliases=['namespace']), plugin_name=dict(type='str', ), # Helm options context=dict(type='str', aliases=['kube_context'], fallback=(env_fallback, ['K8S_AUTH_CONTEXT'])), kubeconfig=dict(type='path', aliases=['kubeconfig_path'], fallback=(env_fallback, ['K8S_AUTH_KUBECONFIG'])), # Generic auth key host=dict(type='str', fallback=(env_fallback, ['K8S_AUTH_HOST'])), ca_cert=dict(type='path', aliases=['ssl_ca_cert'], fallback=(env_fallback, ['K8S_AUTH_SSL_CA_CERT'])), validate_certs=dict(type='bool', default=True, aliases=['verify_ssl'], fallback=(env_fallback, ['K8S_AUTH_VERIFY_SSL'])), api_key=dict(type='str', no_log=True, fallback=(env_fallback, ['K8S_AUTH_API_KEY']))), mutually_exclusive=[("context", "ca_cert"), ("context", "validate_certs"), ("kubeconfig", "ca_cert"), ("kubeconfig", "validate_certs")], supports_check_mode=True, ) bin_path = module.params.get('binary_path') if bin_path is not None: helm_cmd_common = bin_path else: helm_cmd_common = 'helm' helm_cmd_common = module.get_bin_path(helm_cmd_common, required=True) helm_cmd_common += " plugin" plugin_name = module.params.get('plugin_name') helm_plugin_list = helm_cmd_common + " list" rc, out, err = run_helm(module, helm_plugin_list) if rc != 0 or (out == '' and err == ''): module.fail_json( msg="Failed to get Helm plugin info", command=helm_plugin_list, stdout=out, stderr=err, rc=rc, ) plugin_list = [] if out: for line in out.splitlines(): if line.startswith("NAME"): continue name, version, description = line.split('\t', 3) name = name.strip() version = version.strip() description = description.strip() if plugin_name is None: plugin_list.append({ 'name': name, 'version': version, 'description': description, }) continue if plugin_name == name: plugin_list.append({ 'name': name, 'version': version, 'description': description, }) break module.exit_json( changed=True, command=helm_plugin_list, stdout=out, stderr=err, rc=rc, plugin_list=plugin_list, )
def main(): global module module = AnsibleModule( argument_spec=dict( binary_path=dict(type='path'), repo_name=dict(type='str', aliases=['name'], required=True), repo_url=dict(type='str', aliases=['url']), repo_username=dict(type='str', aliases=['username']), repo_password=dict(type='str', aliases=['password'], no_log=True), repo_state=dict(default='present', choices=['present', 'absent'], aliases=['state']), ), required_together=[['repo_username', 'repo_password']], required_if=[ ('repo_state', 'present', ['repo_url']), ], supports_check_mode=True, ) if not IMP_YAML: module.fail_json(msg=missing_required_lib("yaml"), exception=IMP_YAML_ERR) changed = False bin_path = module.params.get('binary_path') repo_name = module.params.get('repo_name') repo_url = module.params.get('repo_url') repo_username = module.params.get('repo_username') repo_password = module.params.get('repo_password') repo_state = module.params.get('repo_state') if bin_path is not None: helm_cmd = bin_path else: helm_cmd = module.get_bin_path('helm', required=True) repository_status = get_repository_status(module, helm_cmd, repo_name) if repo_state == "absent" and repository_status is not None: helm_cmd = delete_repository(helm_cmd, repo_name) changed = True elif repo_state == "present": if repository_status is None: helm_cmd = install_repository(helm_cmd, repo_name, repo_url, repo_username, repo_password) changed = True elif repository_status['url'] != repo_url: module.fail_json( msg="Repository already have a repository named {0}".format( repo_name)) if module.check_mode: module.exit_json(changed=changed) elif not changed: module.exit_json(changed=False, repo_name=repo_name, repo_url=repo_url) rc, out, err = run_helm(module, helm_cmd) if repo_password is not None: helm_cmd = helm_cmd.replace(repo_password, '******') if rc != 0: module.fail_json( msg= "Failure when executing Helm command. Exited {0}.\nstdout: {1}\nstderr: {2}" .format(rc, out, err), command=helm_cmd) module.exit_json(changed=changed, stdout=out, stderr=err, command=helm_cmd)
def main(): module = AnsibleModule( argument_spec=dict( binary_path=dict(type='path'), release_namespace=dict(type='str', aliases=['namespace']), state=dict(type='str', default='present', choices=['present', 'absent']), plugin_path=dict(type='str', ), plugin_name=dict(type='str', ), # Helm options context=dict(type='str', aliases=['kube_context'], fallback=(env_fallback, ['K8S_AUTH_CONTEXT'])), kubeconfig=dict(type='path', aliases=['kubeconfig_path'], fallback=(env_fallback, ['K8S_AUTH_KUBECONFIG'])), # Generic auth key host=dict(type='str', fallback=(env_fallback, ['K8S_AUTH_HOST'])), ca_cert=dict(type='path', aliases=['ssl_ca_cert'], fallback=(env_fallback, ['K8S_AUTH_SSL_CA_CERT'])), validate_certs=dict(type='bool', default=True, aliases=['verify_ssl'], fallback=(env_fallback, ['K8S_AUTH_VERIFY_SSL'])), api_key=dict(type='str', no_log=True, fallback=(env_fallback, ['K8S_AUTH_API_KEY']))), supports_check_mode=True, required_if=[ ("state", "present", ("plugin_path", )), ("state", "absent", ("plugin_name", )), ], mutually_exclusive=[('plugin_name', 'plugin_path'), ("context", "ca_cert"), ("context", "validate_certs"), ("kubeconfig", "ca_cert"), ("kubeconfig", "validate_certs")], ) bin_path = module.params.get('binary_path') state = module.params.get('state') if bin_path is not None: helm_cmd_common = bin_path else: helm_cmd_common = 'helm' helm_cmd_common = module.get_bin_path(helm_cmd_common, required=True) helm_cmd_common += " plugin" if state == 'present': helm_cmd_common += " install %s" % module.params.get('plugin_path') if not module.check_mode: rc, out, err = run_helm(module, helm_cmd_common, fails_on_error=False) else: rc, out, err = (0, '', '') if rc == 1 and 'plugin already exists' in err: module.exit_json(failed=False, changed=False, msg="Plugin already exists", command=helm_cmd_common, stdout=out, stderr=err, rc=rc) elif rc == 0: module.exit_json( failed=False, changed=True, msg="Plugin installed successfully", command=helm_cmd_common, stdout=out, stderr=err, rc=rc, ) else: module.fail_json( msg="Failure when executing Helm command.", command=helm_cmd_common, stdout=out, stderr=err, rc=rc, ) elif state == 'absent': plugin_name = module.params.get('plugin_name') helm_plugin_list = helm_cmd_common + " list" rc, out, err = run_helm(module, helm_plugin_list) if rc != 0 or (out == '' and err == ''): module.fail_json( msg="Failed to get Helm plugin info", command=helm_plugin_list, stdout=out, stderr=err, rc=rc, ) if out: found = False for line in out.splitlines(): if line.startswith("NAME"): continue name, dummy, dummy = line.split('\t', 3) name = name.strip() if name == plugin_name: found = True break if found: helm_uninstall_cmd = "%s uninstall %s" % (helm_cmd_common, plugin_name) if not module.check_mode: rc, out, err = run_helm(module, helm_uninstall_cmd, fails_on_error=False) else: rc, out, err = (0, '', '') if rc == 0: module.exit_json(changed=True, msg="Plugin uninstalled successfully", command=helm_uninstall_cmd, stdout=out, stderr=err, rc=rc) module.fail_json( msg="Failed to get Helm plugin uninstall", command=helm_uninstall_cmd, stdout=out, stderr=err, rc=rc, ) else: module.exit_json( failed=False, changed=False, msg="Plugin not found or is already uninstalled", command=helm_plugin_list, stdout=out, stderr=err, rc=rc)
def test_run_helm_naked(): module = MockedModule() run_helm(module, "helm foo") assert module.r["command"] == "helm foo" assert module.r["environ_update"] == {}