mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-07-28 18:34:42 +00:00
helm: add support for the K8S_ envvars (#319)
Add support for: - K8S_AUTH_HOST - K8S_AUTH_API_KEY - K8S_AUTH_VERIFY_SSL - K8S_AUTH_SSL_CA_CERT This commit also refactor the way we pass K8S related configuration to `helm`: All the calls are now done in a new module_utils module (`helm.py`). The handling of the `kube_*` variables has also been moved in this new module. We need https://github.com/helm/helm/pull/8622 to be able to ignore the certificate validation. As a workaround, the generate a temporary kubeconfig configuration file. Closes: #279
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
minor_changes:
|
||||||
|
- The Helm modules (``helm``, ``helm_info``, ``helm_plugin``, ``helm_plugin_info``, ``helm_plugin_repository``) accept
|
||||||
|
the K8S environment variables like the other modules of the collections.
|
||||||
@@ -12,6 +12,9 @@
|
|||||||
- name: "Install {{ helm_version }}"
|
- name: "Install {{ helm_version }}"
|
||||||
include_tasks: install.yml
|
include_tasks: install.yml
|
||||||
|
|
||||||
|
- name: "Ensure we honor the environment variables"
|
||||||
|
include_tasks: test_read_envvars.yml
|
||||||
|
|
||||||
- name: tests_repository
|
- name: tests_repository
|
||||||
include_tasks: tests_repository.yml
|
include_tasks: tests_repository.yml
|
||||||
|
|
||||||
|
|||||||
10
molecule/default/roles/helm/tasks/test_read_envvars.yml
Normal file
10
molecule/default/roles/helm/tasks/test_read_envvars.yml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
- name: Pass a bogus server through the K8S_AUTH_HOST environment variable and ensure helm fails as expected
|
||||||
|
helm:
|
||||||
|
binary_path: "{{ helm_binary }}"
|
||||||
|
state: absent
|
||||||
|
name: does-not-exist
|
||||||
|
namespace: "{{ helm_namespace }}"
|
||||||
|
environment:
|
||||||
|
K8S_AUTH_HOST: somewhere
|
||||||
|
register: _helm_result
|
||||||
|
failed_when: '"http://somewhere/version" not in _helm_result.stderr'
|
||||||
@@ -31,4 +31,29 @@ options:
|
|||||||
- If the value is not specified in the task, the value of environment variable C(K8S_AUTH_KUBECONFIG) will be used instead.
|
- If the value is not specified in the task, the value of environment variable C(K8S_AUTH_KUBECONFIG) will be used instead.
|
||||||
type: path
|
type: path
|
||||||
aliases: [ kubeconfig_path ]
|
aliases: [ kubeconfig_path ]
|
||||||
|
host:
|
||||||
|
description:
|
||||||
|
- Provide a URL for accessing the API. Can also be specified via C(K8S_AUTH_HOST) environment variable.
|
||||||
|
type: str
|
||||||
|
version_added: "1.2.0"
|
||||||
|
api_key:
|
||||||
|
description:
|
||||||
|
- Token used to authenticate with the API. Can also be specified via C(K8S_AUTH_API_KEY) environment variable.
|
||||||
|
type: str
|
||||||
|
version_added: "1.2.0"
|
||||||
|
validate_certs:
|
||||||
|
description:
|
||||||
|
- Whether or not to verify the API server's SSL certificates. Can also be specified via C(K8S_AUTH_VERIFY_SSL)
|
||||||
|
environment variable.
|
||||||
|
type: bool
|
||||||
|
aliases: [ verify_ssl ]
|
||||||
|
default: True
|
||||||
|
version_added: "1.2.0"
|
||||||
|
ca_cert:
|
||||||
|
description:
|
||||||
|
- Path to a CA certificate used to authenticate with the API. The full certificate chain must be provided to
|
||||||
|
avoid certificate validation errors. Can also be specified via C(K8S_AUTH_SSL_CA_CERT) environment variable.
|
||||||
|
type: path
|
||||||
|
aliases: [ ssl_ca_cert ]
|
||||||
|
version_added: "1.2.0"
|
||||||
'''
|
'''
|
||||||
|
|||||||
119
plugins/module_utils/helm.py
Normal file
119
plugins/module_utils/helm.py
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright: (c) 2020, Ansible Project
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
|
||||||
|
from contextlib import contextmanager
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from ansible.module_utils.basic import missing_required_lib
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
import yaml
|
||||||
|
HAS_YAML = True
|
||||||
|
except ImportError:
|
||||||
|
YAML_IMP_ERR = traceback.format_exc()
|
||||||
|
HAS_YAML = False
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def prepare_helm_environ_update(module):
|
||||||
|
environ_update = {}
|
||||||
|
file_to_cleam_up = None
|
||||||
|
kubeconfig_path = module.params.get('kubeconfig')
|
||||||
|
if module.params.get('kube_context') is not None:
|
||||||
|
environ_update["HELM_KUBECONTEXT"] = module.params.get('kube_context')
|
||||||
|
if module.params.get('release_namespace'):
|
||||||
|
environ_update["HELM_NAMESPACE"] = module.params.get('release_namespace')
|
||||||
|
if module.params.get("api_key"):
|
||||||
|
environ_update["HELM_KUBETOKEN"] = module.params["api_key"]
|
||||||
|
if module.params.get("host"):
|
||||||
|
environ_update["HELM_KUBEAPISERVER"] = module.params["host"]
|
||||||
|
if module.params.get("validate_certs") is False or module.params.get("ca_cert"):
|
||||||
|
kubeconfig_path = write_temp_kubeconfig(
|
||||||
|
module.params["host"],
|
||||||
|
validate_certs=module.params["validate_certs"],
|
||||||
|
ca_cert=module.params["ca_cert"])
|
||||||
|
file_to_cleam_up = kubeconfig_path
|
||||||
|
if kubeconfig_path is not None:
|
||||||
|
environ_update["KUBECONFIG"] = kubeconfig_path
|
||||||
|
|
||||||
|
try:
|
||||||
|
yield environ_update
|
||||||
|
finally:
|
||||||
|
if file_to_cleam_up:
|
||||||
|
os.remove(file_to_cleam_up)
|
||||||
|
|
||||||
|
|
||||||
|
def run_helm(module, command, fails_on_error=True):
|
||||||
|
if not HAS_YAML:
|
||||||
|
module.fail_json(msg=missing_required_lib("PyYAML"), exception=YAML_IMP_ERR)
|
||||||
|
|
||||||
|
with prepare_helm_environ_update(module) as environ_update:
|
||||||
|
rc, out, err = module.run_command(command, environ_update=environ_update)
|
||||||
|
if fails_on_error and rc != 0:
|
||||||
|
module.fail_json(
|
||||||
|
msg="Failure when executing Helm command. Exited {0}.\nstdout: {1}\nstderr: {2}".format(rc, out, err),
|
||||||
|
stdout=out,
|
||||||
|
stderr=err,
|
||||||
|
command=command,
|
||||||
|
)
|
||||||
|
return rc, out, err
|
||||||
|
|
||||||
|
|
||||||
|
def get_values(module, command, release_name):
|
||||||
|
"""
|
||||||
|
Get Values from deployed release
|
||||||
|
"""
|
||||||
|
if not HAS_YAML:
|
||||||
|
module.fail_json(msg=missing_required_lib("PyYAML"), exception=YAML_IMP_ERR)
|
||||||
|
|
||||||
|
get_command = command + " get values --output=yaml " + release_name
|
||||||
|
|
||||||
|
rc, out, err = run_helm(module, get_command)
|
||||||
|
# Helm 3 return "null" string when no values are set
|
||||||
|
if out.rstrip("\n") == "null":
|
||||||
|
return {}
|
||||||
|
return yaml.safe_load(out)
|
||||||
|
|
||||||
|
|
||||||
|
def write_temp_kubeconfig(server, validate_certs=True, ca_cert=None):
|
||||||
|
# Workaround until https://github.com/helm/helm/pull/8622 is merged
|
||||||
|
content = {
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Config",
|
||||||
|
"clusters": [
|
||||||
|
{
|
||||||
|
"cluster": {
|
||||||
|
"server": server,
|
||||||
|
},
|
||||||
|
"name": "generated-cluster"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"contexts": [
|
||||||
|
{
|
||||||
|
"context": {
|
||||||
|
"cluster": "generated-cluster"
|
||||||
|
},
|
||||||
|
"name": "generated-context"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"current-context": "generated-context"
|
||||||
|
}
|
||||||
|
|
||||||
|
if not validate_certs:
|
||||||
|
content["clusters"][0]["cluster"]["insecure-skip-tls-verify"] = True
|
||||||
|
if ca_cert:
|
||||||
|
content["clusters"][0]["cluster"]["certificate-authority"] = ca_cert
|
||||||
|
|
||||||
|
_fd, file_name = tempfile.mkstemp()
|
||||||
|
with os.fdopen(_fd, 'w') as fp:
|
||||||
|
yaml.dump(content, fp)
|
||||||
|
return file_name
|
||||||
@@ -266,34 +266,7 @@ except ImportError:
|
|||||||
IMP_YAML = False
|
IMP_YAML = False
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib, env_fallback
|
from ansible.module_utils.basic import AnsibleModule, missing_required_lib, env_fallback
|
||||||
|
from ansible_collections.community.kubernetes.plugins.module_utils.helm import run_helm, get_values
|
||||||
module = None
|
|
||||||
|
|
||||||
|
|
||||||
def exec_command(command):
|
|
||||||
rc, out, err = module.run_command(command)
|
|
||||||
if rc != 0:
|
|
||||||
module.fail_json(
|
|
||||||
msg="Failure when executing Helm command. Exited {0}.\nstdout: {1}\nstderr: {2}".format(rc, out, err),
|
|
||||||
stdout=out,
|
|
||||||
stderr=err,
|
|
||||||
command=command,
|
|
||||||
)
|
|
||||||
return rc, out, err
|
|
||||||
|
|
||||||
|
|
||||||
def get_values(command, release_name):
|
|
||||||
"""
|
|
||||||
Get Values from deployed release
|
|
||||||
"""
|
|
||||||
|
|
||||||
get_command = command + " get values --output=yaml " + release_name
|
|
||||||
|
|
||||||
rc, out, err = exec_command(get_command)
|
|
||||||
# Helm 3 return "null" string when no values are set
|
|
||||||
if out.rstrip("\n") == "null":
|
|
||||||
return {}
|
|
||||||
return yaml.safe_load(out)
|
|
||||||
|
|
||||||
|
|
||||||
def get_release(state, release_name):
|
def get_release(state, release_name):
|
||||||
@@ -308,40 +281,40 @@ def get_release(state, release_name):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_release_status(command, release_name):
|
def get_release_status(module, command, release_name):
|
||||||
"""
|
"""
|
||||||
Get Release state from deployed release
|
Get Release state from deployed release
|
||||||
"""
|
"""
|
||||||
|
|
||||||
list_command = command + " list --output=yaml --filter " + release_name
|
list_command = command + " list --output=yaml --filter " + release_name
|
||||||
|
|
||||||
rc, out, err = exec_command(list_command)
|
rc, out, err = run_helm(module, list_command)
|
||||||
|
|
||||||
release = get_release(yaml.safe_load(out), release_name)
|
release = get_release(yaml.safe_load(out), release_name)
|
||||||
|
|
||||||
if release is None: # not install
|
if release is None: # not install
|
||||||
return None
|
return None
|
||||||
|
|
||||||
release['values'] = get_values(command, release_name)
|
release['values'] = get_values(module, command, release_name)
|
||||||
|
|
||||||
return release
|
return release
|
||||||
|
|
||||||
|
|
||||||
def run_repo_update(command):
|
def run_repo_update(module, command):
|
||||||
"""
|
"""
|
||||||
Run Repo update
|
Run Repo update
|
||||||
"""
|
"""
|
||||||
repo_update_command = command + " repo update"
|
repo_update_command = command + " repo update"
|
||||||
rc, out, err = exec_command(repo_update_command)
|
rc, out, err = run_helm(module, repo_update_command)
|
||||||
|
|
||||||
|
|
||||||
def fetch_chart_info(command, chart_ref):
|
def fetch_chart_info(module, command, chart_ref):
|
||||||
"""
|
"""
|
||||||
Get chart info
|
Get chart info
|
||||||
"""
|
"""
|
||||||
inspect_command = command + " show chart " + chart_ref
|
inspect_command = command + " show chart " + chart_ref
|
||||||
|
|
||||||
rc, out, err = exec_command(inspect_command)
|
rc, out, err = run_helm(module, inspect_command)
|
||||||
|
|
||||||
return yaml.safe_load(out)
|
return yaml.safe_load(out)
|
||||||
|
|
||||||
@@ -440,11 +413,23 @@ def main():
|
|||||||
atomic=dict(type='bool', default=False),
|
atomic=dict(type='bool', default=False),
|
||||||
create_namespace=dict(type='bool', default=False),
|
create_namespace=dict(type='bool', default=False),
|
||||||
replace=dict(type='bool', default=False),
|
replace=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=[
|
required_if=[
|
||||||
('release_state', 'present', ['release_name', 'chart_ref']),
|
('release_state', 'present', ['release_name', 'chart_ref']),
|
||||||
('release_state', 'absent', ['release_name'])
|
('release_state', 'absent', ['release_name'])
|
||||||
],
|
],
|
||||||
|
mutually_exclusive=[
|
||||||
|
("context", "ca_cert"),
|
||||||
|
("context", "validate_certs"),
|
||||||
|
("kubeconfig", "ca_cert"),
|
||||||
|
("kubeconfig", "validate_certs")
|
||||||
|
],
|
||||||
supports_check_mode=True,
|
supports_check_mode=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -458,7 +443,6 @@ def main():
|
|||||||
chart_repo_url = module.params.get('chart_repo_url')
|
chart_repo_url = module.params.get('chart_repo_url')
|
||||||
chart_version = module.params.get('chart_version')
|
chart_version = module.params.get('chart_version')
|
||||||
release_name = module.params.get('release_name')
|
release_name = module.params.get('release_name')
|
||||||
release_namespace = module.params.get('release_namespace')
|
|
||||||
release_state = module.params.get('release_state')
|
release_state = module.params.get('release_state')
|
||||||
release_values = module.params.get('release_values')
|
release_values = module.params.get('release_values')
|
||||||
values_files = module.params.get('values_files')
|
values_files = module.params.get('values_files')
|
||||||
@@ -467,8 +451,6 @@ def main():
|
|||||||
# Helm options
|
# Helm options
|
||||||
disable_hook = module.params.get('disable_hook')
|
disable_hook = module.params.get('disable_hook')
|
||||||
force = module.params.get('force')
|
force = module.params.get('force')
|
||||||
kube_context = module.params.get('context')
|
|
||||||
kubeconfig_path = module.params.get('kubeconfig')
|
|
||||||
purge = module.params.get('purge')
|
purge = module.params.get('purge')
|
||||||
wait = module.params.get('wait')
|
wait = module.params.get('wait')
|
||||||
wait_timeout = module.params.get('wait_timeout')
|
wait_timeout = module.params.get('wait_timeout')
|
||||||
@@ -481,19 +463,11 @@ def main():
|
|||||||
else:
|
else:
|
||||||
helm_cmd_common = module.get_bin_path('helm', required=True)
|
helm_cmd_common = module.get_bin_path('helm', required=True)
|
||||||
|
|
||||||
if kube_context is not None:
|
|
||||||
helm_cmd_common += " --kube-context " + kube_context
|
|
||||||
|
|
||||||
if kubeconfig_path is not None:
|
|
||||||
helm_cmd_common += " --kubeconfig " + kubeconfig_path
|
|
||||||
|
|
||||||
if update_repo_cache:
|
if update_repo_cache:
|
||||||
run_repo_update(helm_cmd_common)
|
run_repo_update(module, helm_cmd_common)
|
||||||
|
|
||||||
helm_cmd_common += " --namespace=" + release_namespace
|
|
||||||
|
|
||||||
# Get real/deployed release status
|
# Get real/deployed release status
|
||||||
release_status = get_release_status(helm_cmd_common, release_name)
|
release_status = get_release_status(module, helm_cmd_common, release_name)
|
||||||
|
|
||||||
# keep helm_cmd_common for get_release_status in module_exit_json
|
# keep helm_cmd_common for get_release_status in module_exit_json
|
||||||
helm_cmd = helm_cmd_common
|
helm_cmd = helm_cmd_common
|
||||||
@@ -512,7 +486,7 @@ def main():
|
|||||||
helm_cmd += " --repo=" + chart_repo_url
|
helm_cmd += " --repo=" + chart_repo_url
|
||||||
|
|
||||||
# Fetch chart info to have real version and real name for chart_ref from archive, folder or url
|
# Fetch chart info to have real version and real name for chart_ref from archive, folder or url
|
||||||
chart_info = fetch_chart_info(helm_cmd, chart_ref)
|
chart_info = fetch_chart_info(module, helm_cmd, chart_ref)
|
||||||
|
|
||||||
if release_status is None: # Not installed
|
if release_status is None: # Not installed
|
||||||
helm_cmd = deploy(helm_cmd, release_name, release_values, chart_ref, wait, wait_timeout,
|
helm_cmd = deploy(helm_cmd, release_name, release_values, chart_ref, wait, wait_timeout,
|
||||||
@@ -563,13 +537,13 @@ def main():
|
|||||||
command=helm_cmd,
|
command=helm_cmd,
|
||||||
)
|
)
|
||||||
|
|
||||||
rc, out, err = exec_command(helm_cmd)
|
rc, out, err = run_helm(module, helm_cmd)
|
||||||
|
|
||||||
module.exit_json(
|
module.exit_json(
|
||||||
changed=changed,
|
changed=changed,
|
||||||
stdout=out,
|
stdout=out,
|
||||||
stderr=err,
|
stderr=err,
|
||||||
status=get_release_status(helm_cmd_common, release_name),
|
status=get_release_status(module, helm_cmd_common, release_name),
|
||||||
command=helm_cmd,
|
command=helm_cmd,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -99,27 +99,7 @@ except ImportError:
|
|||||||
IMP_YAML = False
|
IMP_YAML = False
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib, env_fallback
|
from ansible.module_utils.basic import AnsibleModule, missing_required_lib, env_fallback
|
||||||
|
from ansible_collections.community.kubernetes.plugins.module_utils.helm import run_helm, get_values
|
||||||
module = None
|
|
||||||
|
|
||||||
|
|
||||||
# Get Values from deployed release
|
|
||||||
def get_values(command, release_name):
|
|
||||||
get_command = command + " get values --output=yaml " + release_name
|
|
||||||
|
|
||||||
rc, out, err = module.run_command(get_command)
|
|
||||||
|
|
||||||
if rc != 0:
|
|
||||||
module.fail_json(
|
|
||||||
msg="Failure when executing Helm command. Exited {0}.\nstdout: {1}\nstderr: {2}".format(rc, out, err),
|
|
||||||
command=get_command
|
|
||||||
)
|
|
||||||
|
|
||||||
# Helm 3 return "null" string when no values are set
|
|
||||||
if out.rstrip("\n") == "null":
|
|
||||||
return {}
|
|
||||||
else:
|
|
||||||
return yaml.safe_load(out)
|
|
||||||
|
|
||||||
|
|
||||||
# Get Release from all deployed releases
|
# Get Release from all deployed releases
|
||||||
@@ -132,10 +112,10 @@ def get_release(state, release_name):
|
|||||||
|
|
||||||
|
|
||||||
# Get Release state from deployed release
|
# Get Release state from deployed release
|
||||||
def get_release_status(command, release_name):
|
def get_release_status(module, command, release_name):
|
||||||
list_command = command + " list --output=yaml --filter " + release_name
|
list_command = command + " list --output=yaml --filter " + release_name
|
||||||
|
|
||||||
rc, out, err = module.run_command(list_command)
|
rc, out, err = run_helm(module, list_command)
|
||||||
|
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
module.fail_json(
|
module.fail_json(
|
||||||
@@ -148,7 +128,7 @@ def get_release_status(command, release_name):
|
|||||||
if release is None: # not install
|
if release is None: # not install
|
||||||
return None
|
return None
|
||||||
|
|
||||||
release['values'] = get_values(command, release_name)
|
release['values'] = get_values(module, command, release_name)
|
||||||
|
|
||||||
return release
|
return release
|
||||||
|
|
||||||
@@ -165,7 +145,19 @@ def main():
|
|||||||
# Helm options
|
# Helm options
|
||||||
context=dict(type='str', aliases=['kube_context'], fallback=(env_fallback, ['K8S_AUTH_CONTEXT'])),
|
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'])),
|
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,
|
supports_check_mode=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -174,26 +166,13 @@ def main():
|
|||||||
|
|
||||||
bin_path = module.params.get('binary_path')
|
bin_path = module.params.get('binary_path')
|
||||||
release_name = module.params.get('release_name')
|
release_name = module.params.get('release_name')
|
||||||
release_namespace = module.params.get('release_namespace')
|
|
||||||
|
|
||||||
# Helm options
|
|
||||||
kube_context = module.params.get('context')
|
|
||||||
kubeconfig_path = module.params.get('kubeconfig')
|
|
||||||
|
|
||||||
if bin_path is not None:
|
if bin_path is not None:
|
||||||
helm_cmd_common = bin_path
|
helm_cmd_common = bin_path
|
||||||
else:
|
else:
|
||||||
helm_cmd_common = module.get_bin_path('helm', required=True)
|
helm_cmd_common = module.get_bin_path('helm', required=True)
|
||||||
|
|
||||||
if kube_context is not None:
|
release_status = get_release_status(module, helm_cmd_common, release_name)
|
||||||
helm_cmd_common += " --kube-context " + kube_context
|
|
||||||
|
|
||||||
if kubeconfig_path is not None:
|
|
||||||
helm_cmd_common += " --kubeconfig " + kubeconfig_path
|
|
||||||
|
|
||||||
helm_cmd_common += " --namespace=" + release_namespace
|
|
||||||
|
|
||||||
release_status = get_release_status(helm_cmd_common, release_name)
|
|
||||||
|
|
||||||
if release_status is not None:
|
if release_status is not None:
|
||||||
module.exit_json(changed=False, status=release_status)
|
module.exit_json(changed=False, status=release_status)
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ rc:
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule, env_fallback
|
from ansible.module_utils.basic import AnsibleModule, env_fallback
|
||||||
|
from ansible_collections.community.kubernetes.plugins.module_utils.helm import run_helm
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@@ -109,6 +110,12 @@ def main():
|
|||||||
# Helm options
|
# Helm options
|
||||||
context=dict(type='str', aliases=['kube_context'], fallback=(env_fallback, ['K8S_AUTH_CONTEXT'])),
|
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'])),
|
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,
|
supports_check_mode=True,
|
||||||
required_if=[
|
required_if=[
|
||||||
@@ -116,18 +123,17 @@ def main():
|
|||||||
("state", "absent", ("plugin_name",)),
|
("state", "absent", ("plugin_name",)),
|
||||||
],
|
],
|
||||||
mutually_exclusive=[
|
mutually_exclusive=[
|
||||||
['plugin_name', 'plugin_path'],
|
('plugin_name', 'plugin_path'),
|
||||||
|
("context", "ca_cert"),
|
||||||
|
("context", "validate_certs"),
|
||||||
|
("kubeconfig", "ca_cert"),
|
||||||
|
("kubeconfig", "validate_certs")
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
bin_path = module.params.get('binary_path')
|
bin_path = module.params.get('binary_path')
|
||||||
release_namespace = module.params.get('release_namespace')
|
|
||||||
state = module.params.get('state')
|
state = module.params.get('state')
|
||||||
|
|
||||||
# Helm options
|
|
||||||
kube_context = module.params.get('context')
|
|
||||||
kubeconfig_path = module.params.get('kubeconfig')
|
|
||||||
|
|
||||||
if bin_path is not None:
|
if bin_path is not None:
|
||||||
helm_cmd_common = bin_path
|
helm_cmd_common = bin_path
|
||||||
else:
|
else:
|
||||||
@@ -137,18 +143,10 @@ def main():
|
|||||||
|
|
||||||
helm_cmd_common += " plugin"
|
helm_cmd_common += " plugin"
|
||||||
|
|
||||||
if kube_context is not None:
|
|
||||||
helm_cmd_common += " --kube-context " + kube_context
|
|
||||||
|
|
||||||
if kubeconfig_path is not None:
|
|
||||||
helm_cmd_common += " --kubeconfig " + kubeconfig_path
|
|
||||||
|
|
||||||
helm_cmd_common += " --namespace=" + release_namespace
|
|
||||||
|
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
helm_cmd_common += " install %s" % module.params.get('plugin_path')
|
helm_cmd_common += " install %s" % module.params.get('plugin_path')
|
||||||
if not module.check_mode:
|
if not module.check_mode:
|
||||||
rc, out, err = module.run_command(helm_cmd_common)
|
rc, out, err = run_helm(module, helm_cmd_common, fails_on_error=False)
|
||||||
else:
|
else:
|
||||||
rc, out, err = (0, '', '')
|
rc, out, err = (0, '', '')
|
||||||
|
|
||||||
@@ -183,7 +181,7 @@ def main():
|
|||||||
elif state == 'absent':
|
elif state == 'absent':
|
||||||
plugin_name = module.params.get('plugin_name')
|
plugin_name = module.params.get('plugin_name')
|
||||||
helm_plugin_list = helm_cmd_common + " list"
|
helm_plugin_list = helm_cmd_common + " list"
|
||||||
rc, out, err = module.run_command(helm_plugin_list)
|
rc, out, err = run_helm(module, helm_plugin_list)
|
||||||
if rc != 0 or (out == '' and err == ''):
|
if rc != 0 or (out == '' and err == ''):
|
||||||
module.fail_json(
|
module.fail_json(
|
||||||
msg="Failed to get Helm plugin info",
|
msg="Failed to get Helm plugin info",
|
||||||
@@ -206,7 +204,7 @@ def main():
|
|||||||
if found:
|
if found:
|
||||||
helm_uninstall_cmd = "%s uninstall %s" % (helm_cmd_common, plugin_name)
|
helm_uninstall_cmd = "%s uninstall %s" % (helm_cmd_common, plugin_name)
|
||||||
if not module.check_mode:
|
if not module.check_mode:
|
||||||
rc, out, err = module.run_command(helm_uninstall_cmd)
|
rc, out, err = run_helm(module, helm_uninstall_cmd, fails_on_error=False)
|
||||||
else:
|
else:
|
||||||
rc, out, err = (0, '', '')
|
rc, out, err = (0, '', '')
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ rc:
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule, env_fallback
|
from ansible.module_utils.basic import AnsibleModule, env_fallback
|
||||||
|
from ansible_collections.community.kubernetes.plugins.module_utils.helm import run_helm
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@@ -88,16 +89,23 @@ def main():
|
|||||||
# Helm options
|
# Helm options
|
||||||
context=dict(type='str', aliases=['kube_context'], fallback=(env_fallback, ['K8S_AUTH_CONTEXT'])),
|
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'])),
|
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,
|
supports_check_mode=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
bin_path = module.params.get('binary_path')
|
bin_path = module.params.get('binary_path')
|
||||||
release_namespace = module.params.get('release_namespace')
|
|
||||||
|
|
||||||
# Helm options
|
|
||||||
kube_context = module.params.get('context')
|
|
||||||
kubeconfig_path = module.params.get('kubeconfig')
|
|
||||||
|
|
||||||
if bin_path is not None:
|
if bin_path is not None:
|
||||||
helm_cmd_common = bin_path
|
helm_cmd_common = bin_path
|
||||||
@@ -108,17 +116,9 @@ def main():
|
|||||||
|
|
||||||
helm_cmd_common += " plugin"
|
helm_cmd_common += " plugin"
|
||||||
|
|
||||||
if kube_context is not None:
|
|
||||||
helm_cmd_common += " --kube-context " + kube_context
|
|
||||||
|
|
||||||
if kubeconfig_path is not None:
|
|
||||||
helm_cmd_common += " --kubeconfig " + kubeconfig_path
|
|
||||||
|
|
||||||
helm_cmd_common += " --namespace=" + release_namespace
|
|
||||||
|
|
||||||
plugin_name = module.params.get('plugin_name')
|
plugin_name = module.params.get('plugin_name')
|
||||||
helm_plugin_list = helm_cmd_common + " list"
|
helm_plugin_list = helm_cmd_common + " list"
|
||||||
rc, out, err = module.run_command(helm_plugin_list)
|
rc, out, err = run_helm(module, helm_plugin_list)
|
||||||
if rc != 0 or (out == '' and err == ''):
|
if rc != 0 or (out == '' and err == ''):
|
||||||
module.fail_json(
|
module.fail_json(
|
||||||
msg="Failed to get Helm plugin info",
|
msg="Failed to get Helm plugin info",
|
||||||
|
|||||||
@@ -121,8 +121,7 @@ except ImportError:
|
|||||||
IMP_YAML = False
|
IMP_YAML = False
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||||
|
from ansible_collections.community.kubernetes.plugins.module_utils.helm import run_helm
|
||||||
module = None
|
|
||||||
|
|
||||||
|
|
||||||
# Get repository from all repositories added
|
# Get repository from all repositories added
|
||||||
@@ -135,10 +134,10 @@ def get_repository(state, repo_name):
|
|||||||
|
|
||||||
|
|
||||||
# Get repository status
|
# Get repository status
|
||||||
def get_repository_status(command, repository_name):
|
def get_repository_status(module, command, repository_name):
|
||||||
list_command = command + " repo list --output=yaml"
|
list_command = command + " repo list --output=yaml"
|
||||||
|
|
||||||
rc, out, err = module.run_command(list_command)
|
rc, out, err = run_helm(module, list_command, fails_on_error=False)
|
||||||
|
|
||||||
# no repo => rc=1 and 'no repositories to show' in output
|
# no repo => rc=1 and 'no repositories to show' in output
|
||||||
if rc == 1 and "no repositories to show" in err:
|
if rc == 1 and "no repositories to show" in err:
|
||||||
@@ -208,7 +207,7 @@ def main():
|
|||||||
else:
|
else:
|
||||||
helm_cmd = module.get_bin_path('helm', required=True)
|
helm_cmd = module.get_bin_path('helm', required=True)
|
||||||
|
|
||||||
repository_status = get_repository_status(helm_cmd, repo_name)
|
repository_status = get_repository_status(module, helm_cmd, repo_name)
|
||||||
|
|
||||||
if repo_state == "absent" and repository_status is not None:
|
if repo_state == "absent" and repository_status is not None:
|
||||||
helm_cmd = delete_repository(helm_cmd, repo_name)
|
helm_cmd = delete_repository(helm_cmd, repo_name)
|
||||||
@@ -225,7 +224,7 @@ def main():
|
|||||||
elif not changed:
|
elif not changed:
|
||||||
module.exit_json(changed=False, repo_name=repo_name, repo_url=repo_url)
|
module.exit_json(changed=False, repo_name=repo_name, repo_url=repo_url)
|
||||||
|
|
||||||
rc, out, err = module.run_command(helm_cmd)
|
rc, out, err = run_helm(module, helm_cmd)
|
||||||
|
|
||||||
if repo_password is not None:
|
if repo_password is not None:
|
||||||
helm_cmd = helm_cmd.replace(repo_password, '******')
|
helm_cmd = helm_cmd.replace(repo_password, '******')
|
||||||
|
|||||||
1
test-requirements.txt
Normal file
1
test-requirements.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
kubernetes-validate
|
||||||
100
tests/unit/module_utils/test_helm.py
Normal file
100
tests/unit/module_utils/test_helm.py
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright: (c) 2020, Ansible Project
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
|
from ansible_collections.community.kubernetes.plugins.module_utils.helm import (
|
||||||
|
run_helm,
|
||||||
|
write_temp_kubeconfig,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MockedModule:
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
self.params = {
|
||||||
|
"api_key": None,
|
||||||
|
"ca_cert": None,
|
||||||
|
"host": None,
|
||||||
|
"kube_context": None,
|
||||||
|
"kubeconfig": None,
|
||||||
|
"release_namespace": None,
|
||||||
|
"validate_certs": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
self.r = {}
|
||||||
|
|
||||||
|
def run_command(self, command, environ_update=None):
|
||||||
|
self.r = {"command": command, "environ_update": environ_update}
|
||||||
|
return 0, "", ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_write_temp_kubeconfig_server_only():
|
||||||
|
file_name = write_temp_kubeconfig("ff")
|
||||||
|
try:
|
||||||
|
with open(file_name, "r") as fd:
|
||||||
|
content = yaml.load(fd)
|
||||||
|
finally:
|
||||||
|
os.remove(file_name)
|
||||||
|
|
||||||
|
assert content == {
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"clusters": [{"cluster": {"server": "ff"}, "name": "generated-cluster"}],
|
||||||
|
"contexts": [
|
||||||
|
{"context": {"cluster": "generated-cluster"}, "name": "generated-context"}
|
||||||
|
],
|
||||||
|
"current-context": "generated-context",
|
||||||
|
"kind": "Config",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_write_temp_kubeconfig_server_inscure_certs():
|
||||||
|
file_name = write_temp_kubeconfig("ff", False, "my-certificate")
|
||||||
|
try:
|
||||||
|
with open(file_name, "r") as fd:
|
||||||
|
content = yaml.load(fd)
|
||||||
|
finally:
|
||||||
|
os.remove(file_name)
|
||||||
|
|
||||||
|
assert content["clusters"][0]["cluster"]["insecure-skip-tls-verify"] is True
|
||||||
|
assert (
|
||||||
|
content["clusters"][0]["cluster"]["certificate-authority"] == "my-certificate"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_helm_naked():
|
||||||
|
module = MockedModule()
|
||||||
|
run_helm(module, "helm foo")
|
||||||
|
|
||||||
|
assert module.r["command"] == "helm foo"
|
||||||
|
assert module.r["environ_update"] == {}
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_helm_with_params():
|
||||||
|
module = MockedModule()
|
||||||
|
module.params = {
|
||||||
|
"api_key": "my-api-key",
|
||||||
|
"ca_cert": "my-ca-cert",
|
||||||
|
"host": "some-host",
|
||||||
|
"kube_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"])
|
||||||
2
tests/unit/requirements.txt
Normal file
2
tests/unit/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pytest
|
||||||
|
PyYAML
|
||||||
Reference in New Issue
Block a user