mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-03-26 21:33:02 +00:00
helm - add support for in-memory kubeconfig (#497)
helm - add support for in-memory kubeconfig SUMMARY closes #492 ISSUE TYPE Feature Pull Request Reviewed-by: Mike Graves <mgraves@redhat.com> Reviewed-by: Bikouo Aubin <None>
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
minor_changes:
|
||||
- helm, helm_plugin, helm_info, helm_plugin_info, kubectl - add support for in-memory kubeconfig. (https://github.com/ansible-collections/kubernetes.core/issues/492).
|
||||
@@ -75,6 +75,7 @@ DOCUMENTATION = r"""
|
||||
kubectl_kubeconfig:
|
||||
description:
|
||||
- Path to a kubectl config file. Defaults to I(~/.kube/config)
|
||||
- The configuration can be provided as dictionary. Added in version 2.4.0.
|
||||
default: ''
|
||||
vars:
|
||||
- name: ansible_kubectl_kubeconfig
|
||||
@@ -175,6 +176,8 @@ import os
|
||||
import os.path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import json
|
||||
|
||||
from ansible.parsing.yaml.loader import AnsibleLoader
|
||||
from ansible.errors import AnsibleError, AnsibleFileNotFound
|
||||
@@ -222,6 +225,12 @@ class Connection(ConnectionBase):
|
||||
self.transport_cmd = kwargs.get(cmd_arg, shutil.which(self.transport))
|
||||
if not self.transport_cmd:
|
||||
raise AnsibleError("{0} command not found in PATH".format(self.transport))
|
||||
self._file_to_delete = None
|
||||
|
||||
def delete_temporary_file(self):
|
||||
if self._file_to_delete is not None:
|
||||
os.remove(self._file_to_delete)
|
||||
self._file_to_delete = None
|
||||
|
||||
def _build_exec_cmd(self, cmd):
|
||||
"""Build the local kubectl exec command to run cmd on remote_host"""
|
||||
@@ -244,6 +253,18 @@ class Connection(ConnectionBase):
|
||||
self.connection_options[key], str(skip_verify_ssl).lower()
|
||||
)
|
||||
)
|
||||
elif key.endswith("kubeconfig") and self.get_option(key) != "":
|
||||
kubeconfig_path = self.get_option(key)
|
||||
if isinstance(kubeconfig_path, dict):
|
||||
fd, tmpfile = tempfile.mkstemp()
|
||||
with os.fdopen(fd, "w") as fp:
|
||||
json.dump(kubeconfig_path, fp)
|
||||
kubeconfig_path = tmpfile
|
||||
self._file_to_delete = tmpfile
|
||||
|
||||
cmd_arg = self.connection_options[key]
|
||||
local_cmd += [cmd_arg, kubeconfig_path]
|
||||
censored_local_cmd += [cmd_arg, kubeconfig_path]
|
||||
elif (
|
||||
not key.endswith("container")
|
||||
and self.get_option(key)
|
||||
@@ -311,6 +332,7 @@ class Connection(ConnectionBase):
|
||||
)
|
||||
|
||||
stdout, stderr = p.communicate(in_data)
|
||||
self.delete_temporary_file()
|
||||
return (p.returncode, stdout, stderr)
|
||||
|
||||
def _prefix_login_path(self, remote_path):
|
||||
@@ -363,6 +385,7 @@ class Connection(ConnectionBase):
|
||||
"kubectl connection requires dd command in the container to put files"
|
||||
)
|
||||
stdout, stderr = p.communicate()
|
||||
self.delete_temporary_file()
|
||||
|
||||
if p.returncode != 0:
|
||||
raise AnsibleError(
|
||||
@@ -401,6 +424,7 @@ class Connection(ConnectionBase):
|
||||
)
|
||||
)
|
||||
stdout, stderr = p.communicate()
|
||||
self.delete_temporary_file()
|
||||
|
||||
if p.returncode != 0:
|
||||
raise AnsibleError(
|
||||
|
||||
@@ -30,7 +30,8 @@ options:
|
||||
description:
|
||||
- Helm option to specify kubeconfig path to use.
|
||||
- If the value is not specified in the task, the value of environment variable C(K8S_AUTH_KUBECONFIG) will be used instead.
|
||||
type: path
|
||||
- The configuration can be provided as dictionary. Added in version 2.4.0.
|
||||
type: raw
|
||||
aliases: [ kubeconfig_path ]
|
||||
host:
|
||||
description:
|
||||
|
||||
@@ -7,14 +7,14 @@ from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
from contextlib import contextmanager
|
||||
import os
|
||||
import tempfile
|
||||
import traceback
|
||||
import re
|
||||
import json
|
||||
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
|
||||
from ansible.module_utils.six import string_types
|
||||
|
||||
try:
|
||||
import yaml
|
||||
@@ -25,11 +25,18 @@ except ImportError:
|
||||
HAS_YAML = False
|
||||
|
||||
|
||||
@contextmanager
|
||||
def prepare_helm_environ_update(module):
|
||||
environ_update = {}
|
||||
file_to_cleam_up = None
|
||||
kubeconfig_path = module.params.get("kubeconfig")
|
||||
kubeconfig_path = None
|
||||
if module.params.get("kubeconfig") is not None:
|
||||
kubeconfig = module.params.get("kubeconfig")
|
||||
if isinstance(kubeconfig, string_types):
|
||||
kubeconfig_path = kubeconfig
|
||||
elif isinstance(kubeconfig, dict):
|
||||
fd, kubeconfig_path = tempfile.mkstemp()
|
||||
with os.fdopen(fd, "w") as fp:
|
||||
json.dump(kubeconfig, fp)
|
||||
module.add_cleanup_file(kubeconfig_path)
|
||||
if module.params.get("context") is not None:
|
||||
environ_update["HELM_KUBECONTEXT"] = module.params.get("context")
|
||||
if module.params.get("release_namespace"):
|
||||
@@ -44,23 +51,19 @@ def prepare_helm_environ_update(module):
|
||||
validate_certs=module.params["validate_certs"],
|
||||
ca_cert=module.params["ca_cert"],
|
||||
)
|
||||
file_to_cleam_up = kubeconfig_path
|
||||
module.add_cleanup_file(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)
|
||||
return environ_update
|
||||
|
||||
|
||||
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)
|
||||
environ_update = prepare_helm_environ_update(module)
|
||||
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(
|
||||
@@ -118,7 +121,7 @@ def get_helm_plugin_list(module, helm_bin=None):
|
||||
"""
|
||||
if not helm_bin:
|
||||
return []
|
||||
helm_plugin_list = helm_bin + " list"
|
||||
helm_plugin_list = helm_bin + " plugin list"
|
||||
rc, out, err = run_helm(module, helm_plugin_list)
|
||||
if rc != 0 or (out == "" and err == ""):
|
||||
module.fail_json(
|
||||
@@ -162,3 +165,9 @@ def get_helm_version(module, helm_bin):
|
||||
if m:
|
||||
return m.group(1)
|
||||
return None
|
||||
|
||||
|
||||
def get_helm_binary(module):
|
||||
return module.params.get("binary_path") or module.get_bin_path(
|
||||
"helm", required=True
|
||||
)
|
||||
|
||||
44
plugins/module_utils/helm_args_common.py
Normal file
44
plugins/module_utils/helm_args_common.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from ansible.module_utils.basic import env_fallback
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
HELM_AUTH_ARG_SPEC = dict(
|
||||
binary_path=dict(type="path"),
|
||||
context=dict(
|
||||
type="str",
|
||||
aliases=["kube_context"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_CONTEXT"]),
|
||||
),
|
||||
kubeconfig=dict(
|
||||
type="raw",
|
||||
aliases=["kubeconfig_path"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_KUBECONFIG"]),
|
||||
),
|
||||
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"]),
|
||||
),
|
||||
)
|
||||
|
||||
HELM_AUTH_MUTUALLY_EXCLUSIVE = [
|
||||
("context", "ca_cert"),
|
||||
("context", "validate_certs"),
|
||||
("kubeconfig", "ca_cert"),
|
||||
("kubeconfig", "validate_certs"),
|
||||
]
|
||||
@@ -336,6 +336,7 @@ command:
|
||||
import re
|
||||
import tempfile
|
||||
import traceback
|
||||
import copy
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
|
||||
LooseVersion,
|
||||
)
|
||||
@@ -348,13 +349,17 @@ except ImportError:
|
||||
IMP_YAML_ERR = traceback.format_exc()
|
||||
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
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
run_helm,
|
||||
get_values,
|
||||
get_helm_plugin_list,
|
||||
parse_helm_plugin_list,
|
||||
get_helm_version,
|
||||
get_helm_binary,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm_args_common import (
|
||||
HELM_AUTH_ARG_SPEC,
|
||||
)
|
||||
|
||||
|
||||
@@ -531,13 +536,12 @@ def load_values_files(values_files):
|
||||
return values
|
||||
|
||||
|
||||
def get_plugin_version(command, plugin):
|
||||
def get_plugin_version(helm_bin, plugin):
|
||||
"""
|
||||
Check if helm plugin is installed and return corresponding version
|
||||
"""
|
||||
|
||||
cmd = command + " plugin"
|
||||
rc, output, err = get_helm_plugin_list(module, helm_bin=cmd)
|
||||
rc, output, err = get_helm_plugin_list(module, helm_bin=helm_bin)
|
||||
out = parse_helm_plugin_list(module, output=output.splitlines())
|
||||
|
||||
if not out:
|
||||
@@ -612,11 +616,10 @@ def default_check(release_status, chart_info, values=None, values_files=None):
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
global module
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
binary_path=dict(type="path"),
|
||||
def argument_spec():
|
||||
arg_spec = copy.deepcopy(HELM_AUTH_ARG_SPEC)
|
||||
arg_spec.update(
|
||||
dict(
|
||||
chart_ref=dict(type="path"),
|
||||
chart_repo_url=dict(type="str"),
|
||||
chart_version=dict(type="str"),
|
||||
@@ -629,19 +632,8 @@ def main():
|
||||
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"),
|
||||
@@ -652,23 +644,15 @@ def main():
|
||||
replace=dict(type="bool", default=False),
|
||||
skip_crds=dict(type="bool", default=False),
|
||||
history_max=dict(type="int"),
|
||||
# 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"])
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
return arg_spec
|
||||
|
||||
|
||||
def main():
|
||||
global module
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec(),
|
||||
required_if=[
|
||||
("release_state", "present", ["release_name", "chart_ref"]),
|
||||
("release_state", "absent", ["release_name"]),
|
||||
@@ -687,7 +671,6 @@ def main():
|
||||
|
||||
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")
|
||||
@@ -712,10 +695,8 @@ def main():
|
||||
history_max = module.params.get("history_max")
|
||||
timeout = module.params.get("timeout")
|
||||
|
||||
if bin_path is not None:
|
||||
helm_cmd_common = bin_path
|
||||
else:
|
||||
helm_cmd_common = module.get_bin_path("helm", required=True)
|
||||
helm_cmd_common = get_helm_binary(module)
|
||||
helm_bin = helm_cmd_common
|
||||
|
||||
if update_repo_cache:
|
||||
run_repo_update(module, helm_cmd_common)
|
||||
@@ -808,7 +789,7 @@ def main():
|
||||
|
||||
else:
|
||||
|
||||
helm_diff_version = get_plugin_version(helm_cmd_common, "diff")
|
||||
helm_diff_version = get_plugin_version(helm_bin, "diff")
|
||||
if helm_diff_version and (
|
||||
not chart_repo_url
|
||||
or (
|
||||
|
||||
@@ -112,6 +112,7 @@ status:
|
||||
"""
|
||||
|
||||
import traceback
|
||||
import copy
|
||||
|
||||
try:
|
||||
import yaml
|
||||
@@ -121,10 +122,15 @@ except ImportError:
|
||||
IMP_YAML_ERR = traceback.format_exc()
|
||||
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
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
run_helm,
|
||||
get_values,
|
||||
get_helm_binary,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm_args_common import (
|
||||
HELM_AUTH_ARG_SPEC,
|
||||
HELM_AUTH_MUTUALLY_EXCLUSIVE,
|
||||
)
|
||||
|
||||
|
||||
@@ -176,63 +182,34 @@ def get_release_status(module, command, release_name, release_state):
|
||||
return release
|
||||
|
||||
|
||||
def argument_spec():
|
||||
arg_spec = copy.deepcopy(HELM_AUTH_ARG_SPEC)
|
||||
arg_spec.update(
|
||||
dict(
|
||||
release_name=dict(type="str", required=True, aliases=["name"]),
|
||||
release_namespace=dict(type="str", required=True, aliases=["namespace"]),
|
||||
release_state=dict(type="list", default=[], elements="str"),
|
||||
)
|
||||
)
|
||||
return arg_spec
|
||||
|
||||
|
||||
def main():
|
||||
global module
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
binary_path=dict(type="path"),
|
||||
release_name=dict(type="str", required=True, aliases=["name"]),
|
||||
release_namespace=dict(type="str", required=True, aliases=["namespace"]),
|
||||
# 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"])
|
||||
),
|
||||
release_state=dict(type="list", default=[], elements="str"),
|
||||
),
|
||||
mutually_exclusive=[
|
||||
("context", "ca_cert"),
|
||||
("context", "validate_certs"),
|
||||
("kubeconfig", "ca_cert"),
|
||||
("kubeconfig", "validate_certs"),
|
||||
],
|
||||
argument_spec=argument_spec(),
|
||||
mutually_exclusive=HELM_AUTH_MUTUALLY_EXCLUSIVE,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
if not IMP_YAML:
|
||||
module.fail_json(msg=missing_required_lib("yaml"), exception=IMP_YAML_ERR)
|
||||
|
||||
bin_path = module.params.get("binary_path")
|
||||
release_name = module.params.get("release_name")
|
||||
release_state = module.params.get("release_state")
|
||||
|
||||
if bin_path is not None:
|
||||
helm_cmd_common = bin_path
|
||||
else:
|
||||
helm_cmd_common = module.get_bin_path("helm", required=True)
|
||||
helm_cmd_common = get_helm_binary(module)
|
||||
|
||||
release_status = get_release_status(
|
||||
module, helm_cmd_common, release_name, release_state
|
||||
|
||||
@@ -108,21 +108,24 @@ rc:
|
||||
sample: 1
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, env_fallback
|
||||
import copy
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
run_helm,
|
||||
get_helm_plugin_list,
|
||||
parse_helm_plugin_list,
|
||||
get_helm_binary,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm_args_common import (
|
||||
HELM_AUTH_ARG_SPEC,
|
||||
HELM_AUTH_MUTUALLY_EXCLUSIVE,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
binary_path=dict(type="path"),
|
||||
state=dict(
|
||||
type="str", default="present", choices=["present", "absent", "latest"]
|
||||
),
|
||||
def argument_spec():
|
||||
arg_spec = copy.deepcopy(HELM_AUTH_ARG_SPEC)
|
||||
arg_spec.update(
|
||||
dict(
|
||||
plugin_path=dict(
|
||||
type="str",
|
||||
),
|
||||
@@ -132,60 +135,38 @@ def main():
|
||||
plugin_version=dict(
|
||||
type="str",
|
||||
),
|
||||
# Helm options
|
||||
context=dict(
|
||||
state=dict(
|
||||
type="str",
|
||||
aliases=["kube_context"],
|
||||
fallback=(env_fallback, ["K8S_AUTH_CONTEXT"]),
|
||||
default="present",
|
||||
choices=["present", "absent", "latest"],
|
||||
),
|
||||
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"])
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
return arg_spec
|
||||
|
||||
|
||||
def mutually_exclusive():
|
||||
mutually_ex = copy.deepcopy(HELM_AUTH_MUTUALLY_EXCLUSIVE)
|
||||
mutually_ex.append(("plugin_name", "plugin_path"))
|
||||
return mutually_ex
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec(),
|
||||
supports_check_mode=True,
|
||||
required_if=[
|
||||
("state", "present", ("plugin_path",)),
|
||||
("state", "absent", ("plugin_name",)),
|
||||
("state", "latest", ("plugin_name",)),
|
||||
],
|
||||
mutually_exclusive=[
|
||||
("plugin_name", "plugin_path"),
|
||||
("context", "ca_cert"),
|
||||
("context", "validate_certs"),
|
||||
("kubeconfig", "ca_cert"),
|
||||
("kubeconfig", "validate_certs"),
|
||||
],
|
||||
mutually_exclusive=mutually_exclusive(),
|
||||
)
|
||||
|
||||
bin_path = module.params.get("binary_path")
|
||||
state = module.params.get("state")
|
||||
helm_bin = get_helm_binary(module)
|
||||
|
||||
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"
|
||||
helm_cmd_common = helm_bin + " plugin"
|
||||
|
||||
if state == "present":
|
||||
helm_cmd_common += " install %s" % module.params.get("plugin_path")
|
||||
@@ -227,7 +208,7 @@ def main():
|
||||
)
|
||||
elif state == "absent":
|
||||
plugin_name = module.params.get("plugin_name")
|
||||
rc, output, err = get_helm_plugin_list(module, helm_bin=helm_cmd_common)
|
||||
rc, output, err = get_helm_plugin_list(module, helm_bin=helm_bin)
|
||||
out = parse_helm_plugin_list(module, output=output.splitlines())
|
||||
|
||||
if not out:
|
||||
@@ -281,7 +262,7 @@ def main():
|
||||
)
|
||||
elif state == "latest":
|
||||
plugin_name = module.params.get("plugin_name")
|
||||
rc, output, err = get_helm_plugin_list(module, helm_bin=helm_cmd_common)
|
||||
rc, output, err = get_helm_plugin_list(module, helm_bin=helm_bin)
|
||||
out = parse_helm_plugin_list(module, output=output.splitlines())
|
||||
|
||||
if not out:
|
||||
|
||||
@@ -70,73 +70,43 @@ rc:
|
||||
sample: 1
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, env_fallback
|
||||
import copy
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
get_helm_plugin_list,
|
||||
parse_helm_plugin_list,
|
||||
get_helm_binary,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm_args_common import (
|
||||
HELM_AUTH_ARG_SPEC,
|
||||
HELM_AUTH_MUTUALLY_EXCLUSIVE,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
binary_path=dict(type="path"),
|
||||
|
||||
argument_spec = copy.deepcopy(HELM_AUTH_ARG_SPEC)
|
||||
argument_spec.update(
|
||||
dict(
|
||||
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"),
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
mutually_exclusive=HELM_AUTH_MUTUALLY_EXCLUSIVE,
|
||||
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"
|
||||
helm_bin = get_helm_binary(module)
|
||||
|
||||
plugin_name = module.params.get("plugin_name")
|
||||
|
||||
plugin_list = []
|
||||
|
||||
rc, output, err = get_helm_plugin_list(module, helm_bin=helm_cmd_common)
|
||||
rc, output, err = get_helm_plugin_list(module, helm_bin=helm_bin)
|
||||
|
||||
out = parse_helm_plugin_list(module, output=output.splitlines())
|
||||
|
||||
@@ -155,7 +125,7 @@ def main():
|
||||
|
||||
module.exit_json(
|
||||
changed=True,
|
||||
command=helm_cmd_common + " list",
|
||||
command=helm_bin + " plugin list",
|
||||
stdout=output,
|
||||
stderr=err,
|
||||
rc=rc,
|
||||
|
||||
@@ -97,6 +97,21 @@ options:
|
||||
type: path
|
||||
aliases: [ ssl_ca_cert ]
|
||||
version_added: "2.3.0"
|
||||
context:
|
||||
description:
|
||||
- Helm option to specify which kubeconfig context to use.
|
||||
- If the value is not specified in the task, the value of environment variable C(K8S_AUTH_CONTEXT) will be used instead.
|
||||
type: str
|
||||
aliases: [ kube_context ]
|
||||
version_added: "2.4.0"
|
||||
kubeconfig:
|
||||
description:
|
||||
- Helm option to specify kubeconfig path to use.
|
||||
- If the value is not specified in the task, the value of environment variable C(K8S_AUTH_KUBECONFIG) will be used instead.
|
||||
- The configuration can be provided as dictionary.
|
||||
type: raw
|
||||
aliases: [ kubeconfig_path ]
|
||||
version_added: "2.4.0"
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
@@ -145,6 +160,7 @@ msg:
|
||||
"""
|
||||
|
||||
import traceback
|
||||
import copy
|
||||
|
||||
try:
|
||||
import yaml
|
||||
@@ -154,8 +170,15 @@ except ImportError:
|
||||
IMP_YAML_ERR = traceback.format_exc()
|
||||
IMP_YAML = False
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, env_fallback, missing_required_lib
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import run_helm
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
run_helm,
|
||||
get_helm_binary,
|
||||
)
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm_args_common import (
|
||||
HELM_AUTH_ARG_SPEC,
|
||||
HELM_AUTH_MUTUALLY_EXCLUSIVE,
|
||||
)
|
||||
|
||||
|
||||
# Get repository from all repositories added
|
||||
@@ -215,12 +238,10 @@ def delete_repository(command, repository_name):
|
||||
return remove_command
|
||||
|
||||
|
||||
def main():
|
||||
global module
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
binary_path=dict(type="path"),
|
||||
def argument_spec():
|
||||
arg_spec = copy.deepcopy(HELM_AUTH_ARG_SPEC)
|
||||
arg_spec.update(
|
||||
dict(
|
||||
repo_name=dict(type="str", aliases=["name"], required=True),
|
||||
repo_url=dict(type="str", aliases=["url"]),
|
||||
repo_username=dict(type="str", aliases=["username"]),
|
||||
@@ -229,25 +250,19 @@ def main():
|
||||
default="present", choices=["present", "absent"], aliases=["state"]
|
||||
),
|
||||
pass_credentials=dict(type="bool", default=False, no_log=True),
|
||||
# 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"])
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
return arg_spec
|
||||
|
||||
|
||||
def main():
|
||||
global module
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec(),
|
||||
required_together=[["repo_username", "repo_password"]],
|
||||
required_if=[("repo_state", "present", ["repo_url"])],
|
||||
mutually_exclusive=HELM_AUTH_MUTUALLY_EXCLUSIVE,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
@@ -256,7 +271,6 @@ def main():
|
||||
|
||||
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")
|
||||
@@ -264,10 +278,7 @@ def main():
|
||||
repo_state = module.params.get("repo_state")
|
||||
pass_credentials = module.params.get("pass_credentials")
|
||||
|
||||
if bin_path is not None:
|
||||
helm_cmd = bin_path
|
||||
else:
|
||||
helm_cmd = module.get_bin_path("helm", required=True)
|
||||
helm_cmd = get_helm_binary(module)
|
||||
|
||||
repository_status = get_repository_status(module, helm_cmd, repo_name)
|
||||
|
||||
|
||||
@@ -181,7 +181,10 @@ except ImportError:
|
||||
IMP_YAML = False
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import run_helm
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
run_helm,
|
||||
get_helm_binary,
|
||||
)
|
||||
|
||||
|
||||
def template(
|
||||
@@ -266,7 +269,6 @@ def main():
|
||||
)
|
||||
|
||||
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")
|
||||
@@ -284,7 +286,7 @@ def main():
|
||||
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)
|
||||
helm_cmd = get_helm_binary(module)
|
||||
|
||||
if update_repo_cache:
|
||||
update_cmd = helm_cmd + " repo update"
|
||||
|
||||
@@ -26,3 +26,4 @@ test_namespace:
|
||||
- "helm-local-path-002"
|
||||
- "helm-local-path-003"
|
||||
- "helm-dep"
|
||||
- "helm-kubeconfig"
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
- name: Test Skip CRDS feature in helm chart install
|
||||
include_tasks: test_crds.yml
|
||||
|
||||
- name: Test in-memory kubeconfig
|
||||
include_tasks: tests_in_memory_kubeconfig.yml
|
||||
|
||||
- name: Clean helm install
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
---
|
||||
- set_fact:
|
||||
custom_kubeconfig_path: "~/.kube/customconfig"
|
||||
default_kubeconfig_path: "~/.kube/config"
|
||||
helm_in_mem_kubeconf_ns: "{{ test_namespace[11] }}"
|
||||
|
||||
- block:
|
||||
- name: Copy default kubeconfig
|
||||
copy:
|
||||
remote_src: true
|
||||
src: "{{ default_kubeconfig_path }}"
|
||||
dest: "{{ custom_kubeconfig_path }}"
|
||||
|
||||
- name: Delete default kubeconfig
|
||||
file:
|
||||
path: "{{ default_kubeconfig_path }}"
|
||||
state: absent
|
||||
|
||||
- set_fact:
|
||||
custom_kubeconfig: "{{ lookup('file', custom_kubeconfig_path) | from_yaml }}"
|
||||
no_log: true
|
||||
|
||||
# helm_plugin and helm_plugin_info
|
||||
- name: Install subenv plugin
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
kubeconfig: "{{ custom_kubeconfig }}"
|
||||
state: present
|
||||
plugin_path: https://github.com/hydeenoble/helm-subenv
|
||||
register: plugin
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- plugin is changed
|
||||
|
||||
- name: Gather info about all plugin
|
||||
helm_plugin_info:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
kubeconfig: "{{ custom_kubeconfig }}"
|
||||
register: plugin_info
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- '"plugin_list" in plugin_info'
|
||||
- plugin_info.plugin_list != []
|
||||
|
||||
# helm_repository, helm, helm_info
|
||||
- name: Add test_bitnami chart repository
|
||||
helm_repository:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: test_bitnami
|
||||
kubeconfig: "{{ custom_kubeconfig }}"
|
||||
repo_url: https://charts.bitnami.com/bitnami
|
||||
register: repository
|
||||
|
||||
- name: Assert that repository was added
|
||||
assert:
|
||||
that:
|
||||
- repository is changed
|
||||
|
||||
- name: Install chart from repository added before
|
||||
helm:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: rabbitmq
|
||||
chart_ref: test_bitnami/rabbitmq
|
||||
namespace: "{{ helm_in_mem_kubeconf_ns }}"
|
||||
update_repo_cache: true
|
||||
kubeconfig: "{{ custom_kubeconfig }}"
|
||||
create_namespace: true
|
||||
register: deploy
|
||||
|
||||
- name: Assert chart was successfully deployed
|
||||
assert:
|
||||
that:
|
||||
- deploy is changed
|
||||
|
||||
- name: Get chart content
|
||||
helm_info:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
kubeconfig: "{{ custom_kubeconfig }}"
|
||||
name: "rabbitmq"
|
||||
namespace: "{{ helm_in_mem_kubeconf_ns }}"
|
||||
register: chart_info
|
||||
|
||||
- name: Assert chart was successfully deployed
|
||||
assert:
|
||||
that:
|
||||
- '"status" in chart_info'
|
||||
- chart_info.status.status is defined
|
||||
- chart_info.status.status == "deployed"
|
||||
|
||||
- name: Remove chart
|
||||
helm:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: rabbitmq
|
||||
namespace: "{{ helm_in_mem_kubeconf_ns }}"
|
||||
kubeconfig: "{{ custom_kubeconfig }}"
|
||||
state: absent
|
||||
register: remove_chart
|
||||
|
||||
- name: Assert chart was successfully removed
|
||||
assert:
|
||||
that:
|
||||
- remove_chart is changed
|
||||
|
||||
- name: Get chart content
|
||||
helm_info:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
kubeconfig: "{{ custom_kubeconfig }}"
|
||||
name: "rabbitmq"
|
||||
namespace: "{{ helm_in_mem_kubeconf_ns }}"
|
||||
register: chart_info
|
||||
|
||||
- name: Assert chart was successfully deployed
|
||||
assert:
|
||||
that:
|
||||
- '"status" not in chart_info'
|
||||
|
||||
- name: Remove chart repository
|
||||
helm_repository:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: test_bitnami
|
||||
kubeconfig: "{{ custom_kubeconfig }}"
|
||||
state: absent
|
||||
register: remove
|
||||
|
||||
- name: Assert that repository was removed
|
||||
assert:
|
||||
that:
|
||||
- remove is changed
|
||||
|
||||
always:
|
||||
- name: Return kubeconfig
|
||||
copy:
|
||||
remote_src: true
|
||||
src: "{{ custom_kubeconfig_path }}"
|
||||
dest: "{{ default_kubeconfig_path }}"
|
||||
ignore_errors: true
|
||||
|
||||
- name: Delete custom config
|
||||
file:
|
||||
path: "{{ custom_kubeconfig_path }}"
|
||||
state: absent
|
||||
ignore_errors: true
|
||||
|
||||
- name: Remove subenv plugin
|
||||
helm_plugin:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
plugin_name: subenv
|
||||
state: absent
|
||||
ignore_errors: true
|
||||
|
||||
- name: Delete namespace
|
||||
k8s:
|
||||
kind: Namespace
|
||||
name: "{{ helm_in_mem_kubeconf_ns }}"
|
||||
ignore_errors: true
|
||||
|
||||
- name: Delete helm repository
|
||||
helm_repository:
|
||||
binary_path: "{{ helm_binary }}"
|
||||
name: test_bitnami
|
||||
state: absent
|
||||
ignore_errors: true
|
||||
@@ -7,8 +7,9 @@ from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
import os.path
|
||||
|
||||
import yaml
|
||||
import tempfile
|
||||
import json
|
||||
|
||||
|
||||
from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
|
||||
@@ -31,11 +32,22 @@ class MockedModule:
|
||||
}
|
||||
|
||||
self.r = {}
|
||||
self.files_to_delete = []
|
||||
|
||||
def run_command(self, command, environ_update=None):
|
||||
self.r = {"command": command, "environ_update": environ_update}
|
||||
return 0, "", ""
|
||||
|
||||
def add_cleanup_file(self, file_path):
|
||||
self.files_to_delete.append(file_path)
|
||||
|
||||
def do_cleanup_files(self):
|
||||
for file in self.files_to_delete:
|
||||
try:
|
||||
os.remove(file)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def test_write_temp_kubeconfig_server_only():
|
||||
file_name = write_temp_kubeconfig("ff")
|
||||
@@ -97,4 +109,60 @@ def test_run_helm_with_params():
|
||||
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"])
|
||||
assert os.path.exists(module.r["environ_update"]["KUBECONFIG"])
|
||||
module.do_cleanup_files()
|
||||
|
||||
|
||||
def test_run_helm_with_kubeconfig():
|
||||
|
||||
custom_config = {
|
||||
"apiVersion": "v1",
|
||||
"clusters": [
|
||||
{
|
||||
"cluster": {
|
||||
"certificate-authority-data": "LS0tLS1CRUdJTiBDRV",
|
||||
"server": "https://api.cluster.testing:6443",
|
||||
},
|
||||
"name": "api-cluster-testing:6443",
|
||||
}
|
||||
],
|
||||
"contexts": [
|
||||
{
|
||||
"context": {
|
||||
"cluster": "api-cluster-testing:6443",
|
||||
"namespace": "default",
|
||||
"user": "kubeadmin",
|
||||
},
|
||||
"name": "context-1",
|
||||
}
|
||||
],
|
||||
"current-context": "context-1",
|
||||
"kind": "Config",
|
||||
"users": [
|
||||
{
|
||||
"name": "developer",
|
||||
"user": {"token": "sha256~jbIvVieBC_8W6Pb-iH5vqC_BvvPHIxQMxUPLDnYvHYM"},
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
# kubeconfig defined as path
|
||||
_fd, tmpfile_name = tempfile.mkstemp()
|
||||
with os.fdopen(_fd, "w") as fp:
|
||||
yaml.dump(custom_config, fp)
|
||||
|
||||
k1_module = MockedModule()
|
||||
k1_module.params = {"kubeconfig": tmpfile_name}
|
||||
run_helm(k1_module, "helm foo")
|
||||
assert k1_module.r["environ_update"] == {"KUBECONFIG": tmpfile_name}
|
||||
os.remove(tmpfile_name)
|
||||
|
||||
# kubeconfig defined as string
|
||||
k2_module = MockedModule()
|
||||
k2_module.params = {"kubeconfig": custom_config}
|
||||
run_helm(k2_module, "helm foo")
|
||||
|
||||
assert os.path.exists(k2_module.r["environ_update"]["KUBECONFIG"])
|
||||
with open(k2_module.r["environ_update"]["KUBECONFIG"]) as f:
|
||||
assert json.loads(f.read()) == custom_config
|
||||
k2_module.do_cleanup_files()
|
||||
|
||||
Reference in New Issue
Block a user