mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-07-29 10:54:41 +00:00
add support for in-memory kubeconfig (#212)
add support for in-memory kubeconfig SUMMARY k8s module support now authentication with kubeconfig parameter as file and dict. Closes #139 ISSUE TYPE Feature Pull Request COMPONENT NAME ADDITIONAL INFORMATION Reviewed-by: Mike Graves <mgraves@redhat.com> Reviewed-by: None <None>
This commit is contained in:
2
changelogs/fragments/212-in-memory-kubeconfig.yml
Normal file
2
changelogs/fragments/212-in-memory-kubeconfig.yml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
minor_changes:
|
||||||
|
- add support for in-memory kubeconfig in addition to file for k8s modules. (https://github.com/ansible-collections/kubernetes.core/pull/212).
|
||||||
@@ -60,6 +60,12 @@
|
|||||||
environment:
|
environment:
|
||||||
K8S_AUTH_KUBECONFIG: ~/.kube/customconfig
|
K8S_AUTH_KUBECONFIG: ~/.kube/customconfig
|
||||||
|
|
||||||
|
- name: Using in-memory kubeconfig should succeed
|
||||||
|
kubernetes.core.k8s:
|
||||||
|
name: testing
|
||||||
|
kind: Namespace
|
||||||
|
kubeconfig: "{{ lookup('file', '~/.kube/customconfig') | from_yaml }}"
|
||||||
|
|
||||||
always:
|
always:
|
||||||
- name: Return kubeconfig
|
- name: Return kubeconfig
|
||||||
copy:
|
copy:
|
||||||
|
|||||||
@@ -177,10 +177,43 @@
|
|||||||
that:
|
that:
|
||||||
- result is successful
|
- result is successful
|
||||||
- "'warnings' in result"
|
- "'warnings' in result"
|
||||||
|
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
ansible_python_interpreter: "{{ virtualenv_interpreter }}"
|
ansible_python_interpreter: "{{ virtualenv_interpreter }}"
|
||||||
|
|
||||||
|
- name: stat default kube config
|
||||||
|
stat:
|
||||||
|
path: "~/.kube/config"
|
||||||
|
register: _stat
|
||||||
|
|
||||||
|
- name: validate that in-memory kubeconfig authentication failed for kubernetes < 17.17.0
|
||||||
|
block:
|
||||||
|
- set_fact:
|
||||||
|
virtualenv_kubeconfig: "{{ remote_tmp_dir }}/kubeconfig"
|
||||||
|
|
||||||
|
- pip:
|
||||||
|
name:
|
||||||
|
- "kubernetes<17.17.0"
|
||||||
|
virtualenv: "{{ virtualenv_kubeconfig }}"
|
||||||
|
virtualenv_command: "{{ virtualenv_command }}"
|
||||||
|
virtualenv_site_packages: false
|
||||||
|
|
||||||
|
- name: list namespace using in-memory kubeconfig
|
||||||
|
k8s_info:
|
||||||
|
kind: Namespace
|
||||||
|
kubeconfig: "{{ lookup('file', '~/.kube/config') | from_yaml }}"
|
||||||
|
register: _result
|
||||||
|
ignore_errors: true
|
||||||
|
vars:
|
||||||
|
ansible_python_interpreter: "{{ virtualenv_kubeconfig }}/bin/python"
|
||||||
|
|
||||||
|
- name: assert that task failed with proper message
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- '"kubernetes >= 17.17.0 is required" in _result.msg'
|
||||||
|
when:
|
||||||
|
- _stat.stat.exists
|
||||||
|
- _stat.stat.readable
|
||||||
|
- _stat.stat.isreg
|
||||||
always:
|
always:
|
||||||
- name: Remove temp directory
|
- name: Remove temp directory
|
||||||
file:
|
file:
|
||||||
|
|||||||
@@ -194,6 +194,24 @@ class ActionModule(ActionBase):
|
|||||||
except AnsibleError:
|
except AnsibleError:
|
||||||
raise AnsibleActionFail("%s does not exist in local filesystem" % local_path)
|
raise AnsibleActionFail("%s does not exist in local filesystem" % local_path)
|
||||||
|
|
||||||
|
def get_kubeconfig(self, kubeconfig, remote_transport, new_module_args):
|
||||||
|
if isinstance(kubeconfig, string_types):
|
||||||
|
# find the kubeconfig in the expected search path
|
||||||
|
if not remote_transport:
|
||||||
|
# kubeconfig is local
|
||||||
|
# find in expected paths
|
||||||
|
kubeconfig = self._find_needle('files', kubeconfig)
|
||||||
|
|
||||||
|
# decrypt kubeconfig found
|
||||||
|
actual_file = self._loader.get_real_file(kubeconfig, decrypt=True)
|
||||||
|
new_module_args['kubeconfig'] = actual_file
|
||||||
|
|
||||||
|
elif isinstance(kubeconfig, dict):
|
||||||
|
new_module_args['kubeconfig'] = kubeconfig
|
||||||
|
else:
|
||||||
|
raise AnsibleActionFail("Error while reading kubeconfig parameter - "
|
||||||
|
"a string or dict expected, but got %s instead" % type(kubeconfig))
|
||||||
|
|
||||||
def run(self, tmp=None, task_vars=None):
|
def run(self, tmp=None, task_vars=None):
|
||||||
''' handler for k8s options '''
|
''' handler for k8s options '''
|
||||||
if task_vars is None:
|
if task_vars is None:
|
||||||
@@ -211,22 +229,15 @@ class ActionModule(ActionBase):
|
|||||||
new_module_args = copy.deepcopy(self._task.args)
|
new_module_args = copy.deepcopy(self._task.args)
|
||||||
|
|
||||||
kubeconfig = self._task.args.get('kubeconfig', None)
|
kubeconfig = self._task.args.get('kubeconfig', None)
|
||||||
# find the kubeconfig in the expected search path
|
if kubeconfig:
|
||||||
if kubeconfig and not remote_transport:
|
|
||||||
# kubeconfig is local
|
|
||||||
try:
|
try:
|
||||||
# find in expected paths
|
self.get_kubeconfig(kubeconfig, remote_transport, new_module_args)
|
||||||
kubeconfig = self._find_needle('files', kubeconfig)
|
|
||||||
except AnsibleError as e:
|
except AnsibleError as e:
|
||||||
result['failed'] = True
|
result['failed'] = True
|
||||||
result['msg'] = to_text(e)
|
result['msg'] = to_text(e)
|
||||||
result['exception'] = traceback.format_exc()
|
result['exception'] = traceback.format_exc()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# decrypt kubeconfig found
|
|
||||||
actual_file = self._loader.get_real_file(kubeconfig, decrypt=True)
|
|
||||||
new_module_args['kubeconfig'] = actual_file
|
|
||||||
|
|
||||||
# find the file in the expected search path
|
# find the file in the expected search path
|
||||||
src = self._task.args.get('src', None)
|
src = self._task.args.get('src', None)
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ options:
|
|||||||
options are provided, the Kubernetes client will attempt to load the default
|
options are provided, the Kubernetes client will attempt to load the default
|
||||||
configuration file from I(~/.kube/config). Can also be specified via K8S_AUTH_KUBECONFIG environment
|
configuration file from I(~/.kube/config). Can also be specified via K8S_AUTH_KUBECONFIG environment
|
||||||
variable.
|
variable.
|
||||||
type: path
|
- The kubernetes configuration can be provided as dictionary. This feature requires a python kubernetes client version >= 17.17.0. Added in version 2.2.0.
|
||||||
|
type: raw
|
||||||
context:
|
context:
|
||||||
description:
|
description:
|
||||||
- The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment variable.
|
- The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment variable.
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ AUTH_PROXY_HEADERS_SPEC = dict(
|
|||||||
|
|
||||||
AUTH_ARG_SPEC = {
|
AUTH_ARG_SPEC = {
|
||||||
'kubeconfig': {
|
'kubeconfig': {
|
||||||
'type': 'path',
|
'type': 'raw',
|
||||||
},
|
},
|
||||||
'context': {},
|
'context': {},
|
||||||
'host': {},
|
'host': {},
|
||||||
|
|||||||
@@ -120,9 +120,8 @@ def get_api_client(module=None, **kwargs):
|
|||||||
|
|
||||||
def _raise_or_fail(exc, msg):
|
def _raise_or_fail(exc, msg):
|
||||||
if module:
|
if module:
|
||||||
module.fail_json(msg % to_native(exc))
|
module.fail_json(msg=msg % to_native(exc))
|
||||||
raise exc
|
raise exc
|
||||||
|
|
||||||
# If authorization variables aren't defined, look for them in environment variables
|
# If authorization variables aren't defined, look for them in environment variables
|
||||||
for true_name, arg_name in AUTH_ARG_MAP.items():
|
for true_name, arg_name in AUTH_ARG_MAP.items():
|
||||||
if module and module.params.get(arg_name) is not None:
|
if module and module.params.get(arg_name) is not None:
|
||||||
@@ -150,6 +149,22 @@ def get_api_client(module=None, **kwargs):
|
|||||||
def auth_set(*names):
|
def auth_set(*names):
|
||||||
return all(auth.get(name) for name in names)
|
return all(auth.get(name) for name in names)
|
||||||
|
|
||||||
|
def _load_config():
|
||||||
|
kubeconfig = auth.get('kubeconfig')
|
||||||
|
optional_arg = {
|
||||||
|
'context': auth.get('context'),
|
||||||
|
'persist_config': auth.get('persist_config'),
|
||||||
|
}
|
||||||
|
if kubeconfig:
|
||||||
|
if isinstance(kubeconfig, string_types):
|
||||||
|
kubernetes.config.load_kube_config(config_file=kubeconfig, **optional_arg)
|
||||||
|
elif isinstance(kubeconfig, dict):
|
||||||
|
if LooseVersion(kubernetes.__version__) < LooseVersion("17.17"):
|
||||||
|
_raise_or_fail(Exception("kubernetes >= 17.17.0 is required to use in-memory kubeconfig."), 'Failed to load kubeconfig due to: %s')
|
||||||
|
kubernetes.config.load_kube_config_from_dict(config_dict=kubeconfig, **optional_arg)
|
||||||
|
else:
|
||||||
|
kubernetes.config.load_kube_config(config_file=None, **optional_arg)
|
||||||
|
|
||||||
if auth_set('host'):
|
if auth_set('host'):
|
||||||
# Removing trailing slashes if any from hostname
|
# Removing trailing slashes if any from hostname
|
||||||
auth['host'] = auth.get('host').rstrip('/')
|
auth['host'] = auth.get('host').rstrip('/')
|
||||||
@@ -159,7 +174,7 @@ def get_api_client(module=None, **kwargs):
|
|||||||
pass
|
pass
|
||||||
elif auth_set('kubeconfig') or auth_set('context'):
|
elif auth_set('kubeconfig') or auth_set('context'):
|
||||||
try:
|
try:
|
||||||
kubernetes.config.load_kube_config(auth.get('kubeconfig'), auth.get('context'), persist_config=auth.get('persist_config'))
|
_load_config()
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
_raise_or_fail(err, 'Failed to load kubeconfig due to %s')
|
_raise_or_fail(err, 'Failed to load kubeconfig due to %s')
|
||||||
|
|
||||||
@@ -169,7 +184,7 @@ def get_api_client(module=None, **kwargs):
|
|||||||
kubernetes.config.load_incluster_config()
|
kubernetes.config.load_incluster_config()
|
||||||
except kubernetes.config.ConfigException:
|
except kubernetes.config.ConfigException:
|
||||||
try:
|
try:
|
||||||
kubernetes.config.load_kube_config(auth.get('kubeconfig'), auth.get('context'), persist_config=auth.get('persist_config'))
|
_load_config()
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
_raise_or_fail(err, 'Failed to load kubeconfig due to %s')
|
_raise_or_fail(err, 'Failed to load kubeconfig due to %s')
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user