mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-05-08 05:52:37 +00:00
handle aliases for lookup and inventory plugins for authentication options (#500)
Honor aliases for lookup and inventory plugins rebase and extend the following PR #71 ISSUE TYPE Bugfix Pull Request Reviewed-by: Mike Graves <mgraves@redhat.com>
This commit is contained in:
3
changelogs/fragments/498-k8s-honor-aliases.yaml
Normal file
3
changelogs/fragments/498-k8s-honor-aliases.yaml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
bugfixes:
|
||||||
|
- common - handle ``aliases`` passed from inventory and lookup plugins.
|
||||||
|
- module_utils/k8s/client.py - fix issue when trying to authenticate with host, client_cert and client_key parameters only.
|
||||||
@@ -118,9 +118,10 @@ import json
|
|||||||
|
|
||||||
from ansible.errors import AnsibleError
|
from ansible.errors import AnsibleError
|
||||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
||||||
K8sAnsibleMixin,
|
|
||||||
HAS_K8S_MODULE_HELPER,
|
HAS_K8S_MODULE_HELPER,
|
||||||
k8s_import_exception,
|
k8s_import_exception,
|
||||||
|
)
|
||||||
|
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||||
get_api_client,
|
get_api_client,
|
||||||
)
|
)
|
||||||
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
|
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
|
||||||
@@ -146,7 +147,7 @@ class K8sInventoryException(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable, K8sAnsibleMixin):
|
class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||||
NAME = "kubernetes.core.k8s"
|
NAME = "kubernetes.core.k8s"
|
||||||
|
|
||||||
connection_plugin = "kubernetes.core.kubectl"
|
connection_plugin = "kubernetes.core.kubectl"
|
||||||
|
|||||||
@@ -180,10 +180,12 @@ from ansible.errors import AnsibleError
|
|||||||
from ansible.module_utils.common._collections_compat import KeysView
|
from ansible.module_utils.common._collections_compat import KeysView
|
||||||
from ansible.module_utils.common.validation import check_type_bool
|
from ansible.module_utils.common.validation import check_type_bool
|
||||||
|
|
||||||
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
|
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
|
||||||
K8sAnsibleMixin,
|
|
||||||
get_api_client,
|
get_api_client,
|
||||||
)
|
)
|
||||||
|
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.resource import (
|
||||||
|
create_definitions,
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
enable_turbo_mode = check_type_bool(os.environ.get("ENABLE_TURBO_MODE"))
|
enable_turbo_mode = check_type_bool(os.environ.get("ENABLE_TURBO_MODE"))
|
||||||
@@ -210,7 +212,7 @@ except ImportError as e:
|
|||||||
k8s_import_exception = e
|
k8s_import_exception = e
|
||||||
|
|
||||||
|
|
||||||
class KubernetesLookup(K8sAnsibleMixin):
|
class KubernetesLookup(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
if not HAS_K8S_MODULE_HELPER:
|
if not HAS_K8S_MODULE_HELPER:
|
||||||
@@ -240,7 +242,7 @@ class KubernetesLookup(K8sAnsibleMixin):
|
|||||||
|
|
||||||
cluster_info = kwargs.get("cluster_info")
|
cluster_info = kwargs.get("cluster_info")
|
||||||
if cluster_info == "version":
|
if cluster_info == "version":
|
||||||
return [self.client.version]
|
return [self.client.client.version]
|
||||||
if cluster_info == "api_groups":
|
if cluster_info == "api_groups":
|
||||||
if isinstance(self.client.resources.api_groups, KeysView):
|
if isinstance(self.client.resources.api_groups, KeysView):
|
||||||
return [list(self.client.resources.api_groups)]
|
return [list(self.client.resources.api_groups)]
|
||||||
@@ -257,7 +259,12 @@ class KubernetesLookup(K8sAnsibleMixin):
|
|||||||
resource_definition = kwargs.get("resource_definition")
|
resource_definition = kwargs.get("resource_definition")
|
||||||
src = kwargs.get("src")
|
src = kwargs.get("src")
|
||||||
if src:
|
if src:
|
||||||
resource_definition = self.load_resource_definitions(src)[0]
|
definitions = create_definitions(params=dict(src=src))
|
||||||
|
if definitions:
|
||||||
|
self.kind = definitions[0].kind
|
||||||
|
self.name = definitions[0].name
|
||||||
|
self.namespace = definitions[0].namespace
|
||||||
|
self.api_version = definitions[0].api_version or "v1"
|
||||||
if resource_definition:
|
if resource_definition:
|
||||||
self.kind = resource_definition.get("kind", self.kind)
|
self.kind = resource_definition.get("kind", self.kind)
|
||||||
self.api_version = resource_definition.get("apiVersion", self.api_version)
|
self.api_version = resource_definition.get("apiVersion", self.api_version)
|
||||||
@@ -272,14 +279,15 @@ class KubernetesLookup(K8sAnsibleMixin):
|
|||||||
"using the 'resource_definition' parameter."
|
"using the 'resource_definition' parameter."
|
||||||
)
|
)
|
||||||
|
|
||||||
resource = self.find_resource(self.kind, self.api_version, fail=True)
|
resource = self.client.resource(self.kind, self.api_version)
|
||||||
try:
|
try:
|
||||||
k8s_obj = resource.get(
|
params = dict(
|
||||||
name=self.name,
|
name=self.name,
|
||||||
namespace=self.namespace,
|
namespace=self.namespace,
|
||||||
label_selector=self.label_selector,
|
label_selector=self.label_selector,
|
||||||
field_selector=self.field_selector,
|
field_selector=self.field_selector,
|
||||||
)
|
)
|
||||||
|
k8s_obj = self.client.get(resource, **params)
|
||||||
except NotFoundError:
|
except NotFoundError:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,9 @@ def _create_auth_spec(module=None, **kwargs) -> Dict:
|
|||||||
auth[true_name] = module.params.get(arg_name)
|
auth[true_name] = module.params.get(arg_name)
|
||||||
elif arg_name in kwargs and kwargs.get(arg_name) is not None:
|
elif arg_name in kwargs and kwargs.get(arg_name) is not None:
|
||||||
auth[true_name] = kwargs.get(arg_name)
|
auth[true_name] = kwargs.get(arg_name)
|
||||||
|
elif true_name in kwargs and kwargs.get(true_name) is not None:
|
||||||
|
# Aliases in kwargs
|
||||||
|
auth[true_name] = kwargs.get(true_name)
|
||||||
elif arg_name == "proxy_headers":
|
elif arg_name == "proxy_headers":
|
||||||
# specific case for 'proxy_headers' which is a dictionary
|
# specific case for 'proxy_headers' which is a dictionary
|
||||||
proxy_headers = {}
|
proxy_headers = {}
|
||||||
@@ -131,7 +134,11 @@ def _create_configuration(auth: Dict):
|
|||||||
# 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("/")
|
||||||
|
|
||||||
if auth_set("username", "password", "host") or auth_set("api_key", "host"):
|
if (
|
||||||
|
auth_set("username", "password", "host")
|
||||||
|
or auth_set("api_key", "host")
|
||||||
|
or auth_set("cert_file", "key_file", "host")
|
||||||
|
):
|
||||||
# We have enough in the parameters to authenticate, no need to load incluster or kubeconfig
|
# We have enough in the parameters to authenticate, no need to load incluster or kubeconfig
|
||||||
pass
|
pass
|
||||||
elif auth_set("kubeconfig") or auth_set("context"):
|
elif auth_set("kubeconfig") or auth_set("context"):
|
||||||
@@ -346,10 +353,14 @@ def get_api_client(module=None, **kwargs: Optional[Any]) -> K8SClient:
|
|||||||
msg = "Could not create API client: {0}".format(e)
|
msg = "Could not create API client: {0}".format(e)
|
||||||
raise CoreException(msg) from e
|
raise CoreException(msg) from e
|
||||||
|
|
||||||
|
dry_run = False
|
||||||
|
if module:
|
||||||
|
dry_run = module.params.get("dry_run", False)
|
||||||
|
|
||||||
k8s_client = K8SClient(
|
k8s_client = K8SClient(
|
||||||
configuration=configuration,
|
configuration=configuration,
|
||||||
client=client,
|
client=client,
|
||||||
dry_run=module.params.get("dry_run", False),
|
dry_run=dry_run,
|
||||||
)
|
)
|
||||||
|
|
||||||
return k8s_client
|
return k8s_client
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
---
|
||||||
|
- name: Create inventory files
|
||||||
|
hosts: localhost
|
||||||
|
gather_facts: false
|
||||||
|
|
||||||
|
collections:
|
||||||
|
- kubernetes.core
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- role: setup_kubeconfig
|
||||||
|
kubeconfig_operation: 'save'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Create inventory files
|
||||||
|
copy:
|
||||||
|
content: "{{ item.content }}"
|
||||||
|
dest: "{{ item.path }}"
|
||||||
|
vars:
|
||||||
|
hostname: "{{ lookup('file', user_credentials_dir + '/host_data.txt') }}"
|
||||||
|
test_cert_file: "{{ user_credentials_dir | realpath + '/cert_file_data.txt' }}"
|
||||||
|
test_key_file: "{{ user_credentials_dir | realpath + '/key_file_data.txt' }}"
|
||||||
|
test_ca_cert: "{{ user_credentials_dir | realpath + '/ssl_ca_cert_data.txt' }}"
|
||||||
|
with_items:
|
||||||
|
- path: "test_inventory_aliases_with_ssl_k8s.yml"
|
||||||
|
content: |
|
||||||
|
---
|
||||||
|
plugin: kubernetes.core.k8s
|
||||||
|
connections:
|
||||||
|
- namespaces:
|
||||||
|
- inventory
|
||||||
|
host: "{{ hostname }}"
|
||||||
|
cert_file: "{{ test_cert_file }}"
|
||||||
|
key_file: "{{ test_key_file }}"
|
||||||
|
verify_ssl: true
|
||||||
|
ssl_ca_cert: "{{ test_ca_cert }}"
|
||||||
|
- path: "test_inventory_aliases_no_ssl_k8s.yml"
|
||||||
|
content: |
|
||||||
|
---
|
||||||
|
plugin: kubernetes.core.k8s
|
||||||
|
connections:
|
||||||
|
- namespaces:
|
||||||
|
- inventory
|
||||||
|
host: "{{ hostname }}"
|
||||||
|
cert_file: "{{ test_cert_file }}"
|
||||||
|
key_file: "{{ test_key_file }}"
|
||||||
|
verify_ssl: false
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
- name: Delete inventory namespace
|
||||||
|
hosts: localhost
|
||||||
|
connection: local
|
||||||
|
gather_facts: true
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- role: setup_kubeconfig
|
||||||
|
kubeconfig_operation: 'revert'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Delete temporary files
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: "{{ user_credentials_dir ~ '/' ~ item }}"
|
||||||
|
ignore_errors: true
|
||||||
|
with_items:
|
||||||
|
- test_inventory_aliases_with_ssl_k8s.yml
|
||||||
|
- test_inventory_aliases_no_ssl_k8s.yml
|
||||||
|
- ssl_ca_cert_data.txt
|
||||||
|
- key_file_data.txt
|
||||||
|
- cert_file_data.txt
|
||||||
|
- host_data.txt
|
||||||
|
|
||||||
|
- name: Remove inventory namespace
|
||||||
|
k8s:
|
||||||
|
api_version: v1
|
||||||
|
kind: Namespace
|
||||||
|
name: inventory
|
||||||
|
state: absent
|
||||||
@@ -88,15 +88,3 @@
|
|||||||
- name: Assert the file content matches expectations
|
- name: Assert the file content matches expectations
|
||||||
assert:
|
assert:
|
||||||
that: (slurped_file.content|b64decode) == file_content
|
that: (slurped_file.content|b64decode) == file_content
|
||||||
|
|
||||||
- name: Delete inventory namespace
|
|
||||||
hosts: localhost
|
|
||||||
connection: local
|
|
||||||
gather_facts: no
|
|
||||||
tasks:
|
|
||||||
- name: Remove inventory namespace
|
|
||||||
k8s:
|
|
||||||
api_version: v1
|
|
||||||
kind: Namespace
|
|
||||||
name: inventory
|
|
||||||
state: absent
|
|
||||||
|
|||||||
@@ -2,7 +2,28 @@
|
|||||||
|
|
||||||
set -eux
|
set -eux
|
||||||
|
|
||||||
|
export ANSIBLE_ROLES_PATH="../"
|
||||||
|
USER_CREDENTIALS_DIR=$(pwd)
|
||||||
|
|
||||||
|
ansible-playbook playbooks/delete_resources.yml -e "user_credentials_dir=${USER_CREDENTIALS_DIR}" "$@"
|
||||||
|
|
||||||
|
{
|
||||||
export ANSIBLE_INVENTORY_ENABLED=kubernetes.core.k8s,yaml
|
export ANSIBLE_INVENTORY_ENABLED=kubernetes.core.k8s,yaml
|
||||||
export ANSIBLE_PYTHON_INTERPRETER=auto_silent
|
export ANSIBLE_PYTHON_INTERPRETER=auto_silent
|
||||||
|
|
||||||
ansible-playbook playbooks/play.yml -i playbooks/test.inventory_k8s.yml "$@"
|
ansible-playbook playbooks/play.yml -i playbooks/test.inventory_k8s.yml "$@" &&
|
||||||
|
|
||||||
|
ansible-playbook playbooks/create_resources.yml -e "user_credentials_dir=${USER_CREDENTIALS_DIR}" "$@" &&
|
||||||
|
|
||||||
|
ansible-inventory -i playbooks/test_inventory_aliases_with_ssl_k8s.yml --list "$@" &&
|
||||||
|
|
||||||
|
ansible-inventory -i playbooks/test_inventory_aliases_no_ssl_k8s.yml --list "$@" &&
|
||||||
|
|
||||||
|
unset ANSIBLE_INVENTORY_ENABLED &&
|
||||||
|
|
||||||
|
ansible-playbook playbooks/delete_resources.yml -e "user_credentials_dir=${USER_CREDENTIALS_DIR}" "$@"
|
||||||
|
|
||||||
|
} || {
|
||||||
|
ansible-playbook playbooks/delete_resources.yml -e "user_credentials_dir=${USER_CREDENTIALS_DIR}" "$@"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
@@ -2,3 +2,6 @@
|
|||||||
test_namespace:
|
test_namespace:
|
||||||
- app-development-one
|
- app-development-one
|
||||||
- app-development-two
|
- app-development-two
|
||||||
|
- app-development-three
|
||||||
|
configmap_data: "This is a simple config map data."
|
||||||
|
configmap_name: "test-configmap"
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
pre_test2: "{{ lookup('kubernetes.core.k8s', kind='Namespace', resource_name=test_namespace[0]) }}"
|
pre_test2: "{{ lookup('kubernetes.core.k8s', kind='Namespace', resource_name=test_namespace[0]) }}"
|
||||||
pre_test3: "{{ query('kubernetes.core.k8s', kind='Namespace', label_selector='namespace_label=app_development') }}"
|
pre_test3: "{{ query('kubernetes.core.k8s', kind='Namespace', label_selector='namespace_label=app_development') }}"
|
||||||
pre_test4: "{{ query('kubernetes.core.k8s', kind='Namespace', resource_name=test_namespace[0]) }}"
|
pre_test4: "{{ query('kubernetes.core.k8s', kind='Namespace', resource_name=test_namespace[0]) }}"
|
||||||
|
cluster_version: "{{ query('kubernetes.core.k8s', cluster_info='version') }}"
|
||||||
|
cluster_api_groups: "{{ query('kubernetes.core.k8s', cluster_info='api_groups') }}"
|
||||||
|
|
||||||
# https://github.com/ansible-collections/kubernetes.core/issues/147
|
# https://github.com/ansible-collections/kubernetes.core/issues/147
|
||||||
- name: Create a namespace with label
|
- name: Create a namespace with label
|
||||||
@@ -101,6 +103,130 @@
|
|||||||
- test8 is mapping
|
- test8 is mapping
|
||||||
- test9 is mapping
|
- test9 is mapping
|
||||||
|
|
||||||
|
# test using resource_definition
|
||||||
|
- k8s:
|
||||||
|
name: "{{ test_namespace[2] }}"
|
||||||
|
kind: Namespace
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
configmap_def:
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: "{{ configmap_name }}"
|
||||||
|
namespace: "{{ test_namespace[2] }}"
|
||||||
|
data:
|
||||||
|
value: "{{ configmap_data }}"
|
||||||
|
|
||||||
|
- name: Create simple configmap
|
||||||
|
k8s:
|
||||||
|
definition: "{{ configmap_def }}"
|
||||||
|
|
||||||
|
- name: Retrieve configmap using resource_definition parameter
|
||||||
|
set_fact:
|
||||||
|
result_configmap: "{{ lookup('kubernetes.core.k8s', resource_definition=configmap_def) }}"
|
||||||
|
|
||||||
|
- name: Validate configmap result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result_configmap.apiVersion == 'v1'
|
||||||
|
- result_configmap.metadata.name == "{{ configmap_name }}"
|
||||||
|
- result_configmap.metadata.namespace == "{{ test_namespace[2] }}"
|
||||||
|
- result_configmap.data.value == "{{ configmap_data }}"
|
||||||
|
|
||||||
|
# test lookup plugin using src parameter
|
||||||
|
- block:
|
||||||
|
- name: Create temporary file to store content
|
||||||
|
tempfile:
|
||||||
|
suffix: ".yaml"
|
||||||
|
register: tmpfile
|
||||||
|
|
||||||
|
- name: Copy content into file
|
||||||
|
copy:
|
||||||
|
content: |
|
||||||
|
kind: ConfigMap
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: "{{ configmap_name }}"
|
||||||
|
namespace: "{{ test_namespace[2] }}"
|
||||||
|
dest: "{{ tmpfile.path }}"
|
||||||
|
|
||||||
|
- name: Retrieve configmap using src parameter
|
||||||
|
set_fact:
|
||||||
|
src_configmap: "{{ lookup('kubernetes.core.k8s', src=tmpfile.path) }}"
|
||||||
|
|
||||||
|
- name: Validate configmap result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- src_configmap.apiVersion == 'v1'
|
||||||
|
- src_configmap.metadata.name == "{{ configmap_name }}"
|
||||||
|
- src_configmap.metadata.namespace == "{{ test_namespace[2] }}"
|
||||||
|
- src_configmap.data.value == "{{ configmap_data }}"
|
||||||
|
|
||||||
|
always:
|
||||||
|
- name: Delete temporary file created
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: "{{ tmpfile.path }}"
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
# test using aliases for user authentication
|
||||||
|
- block:
|
||||||
|
- name: Create temporary directory to save user credentials
|
||||||
|
tempfile:
|
||||||
|
state: directory
|
||||||
|
suffix: ".config"
|
||||||
|
register: tmpdir
|
||||||
|
|
||||||
|
- include_role:
|
||||||
|
name: setup_kubeconfig
|
||||||
|
vars:
|
||||||
|
user_credentials_dir: "{{ tmpdir.path }}"
|
||||||
|
kubeconfig_operation: "save"
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
cluster_host: "{{ lookup('file', tmpdir.path + '/host_data.txt') }}"
|
||||||
|
user_cert_file: "{{ tmpdir.path }}/cert_file_data.txt"
|
||||||
|
user_key_file: "{{ tmpdir.path }}/key_file_data.txt"
|
||||||
|
ssl_ca_cert: "{{ tmpdir.path }}/ssl_ca_cert_data.txt"
|
||||||
|
|
||||||
|
- name: Retrieve configmap using authentication aliases (validate_certs=false)
|
||||||
|
set_fact:
|
||||||
|
configmap_no_ssl: "{{ lookup('kubernetes.core.k8s', host=cluster_host, cert_file=user_cert_file, key_file=user_key_file, verify_ssl=false, resource_definition=configmap_def) }}"
|
||||||
|
|
||||||
|
- name: Validate configmap result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- configmap_no_ssl.apiVersion == 'v1'
|
||||||
|
- configmap_no_ssl.metadata.name == "{{ configmap_name }}"
|
||||||
|
- configmap_no_ssl.metadata.namespace == "{{ test_namespace[2] }}"
|
||||||
|
- configmap_no_ssl.data.value == "{{ configmap_data }}"
|
||||||
|
|
||||||
|
- name: Retrieve configmap using authentication aliases (validate_certs=true)
|
||||||
|
set_fact:
|
||||||
|
configmap_with_ssl: "{{ lookup('kubernetes.core.k8s', host=cluster_host, cert_file=user_cert_file, key_file=user_key_file, ssl_ca_cert=ssl_ca_cert, verify_ssl=true, resource_definition=configmap_def) }}"
|
||||||
|
|
||||||
|
- name: Validate configmap result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- configmap_with_ssl.apiVersion == 'v1'
|
||||||
|
- configmap_with_ssl.metadata.name == "{{ configmap_name }}"
|
||||||
|
- configmap_with_ssl.metadata.namespace == "{{ test_namespace[2] }}"
|
||||||
|
- configmap_with_ssl.data.value == "{{ configmap_data }}"
|
||||||
|
|
||||||
|
always:
|
||||||
|
- name: Delete temporary directory
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: "{{ tmpdir.path }}"
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- include_role:
|
||||||
|
name: setup_kubeconfig
|
||||||
|
ignore_errors: true
|
||||||
|
vars:
|
||||||
|
kubeconfig_operation: revert
|
||||||
|
|
||||||
always:
|
always:
|
||||||
- name: Ensure that namespace is removed
|
- name: Ensure that namespace is removed
|
||||||
k8s:
|
k8s:
|
||||||
@@ -110,4 +236,5 @@
|
|||||||
with_items:
|
with_items:
|
||||||
- one
|
- one
|
||||||
- two
|
- two
|
||||||
|
- three
|
||||||
ignore_errors: true
|
ignore_errors: true
|
||||||
|
|||||||
1
tests/integration/targets/setup_kubeconfig/aliases
Normal file
1
tests/integration/targets/setup_kubeconfig/aliases
Normal file
@@ -0,0 +1 @@
|
|||||||
|
disabled
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
# When set to 'revert', the role will copy saved kubeconfig to the default location
|
||||||
|
# When set to 'save', the role will copy default kubeconfig to the custom location
|
||||||
|
kubeconfig_operation: "revert"
|
||||||
|
kubeconfig_default_path: "~/.kube/config"
|
||||||
|
kubeconfig_custom_path: "~/.kube/customconfig"
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright: (c) 2022, Aubin Bikouo <@abikouo>
|
||||||
|
# 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
|
||||||
|
|
||||||
|
|
||||||
|
DOCUMENTATION = r"""
|
||||||
|
|
||||||
|
module: test_inventory_read_credentials
|
||||||
|
|
||||||
|
short_description: Generate cert_file, key_file, host and server certificate
|
||||||
|
|
||||||
|
author:
|
||||||
|
- Aubin Bikouo (@abikouo)
|
||||||
|
|
||||||
|
description:
|
||||||
|
- This module is used for integration testing only for this collection
|
||||||
|
- The module load a kube_config file and generate parameters used to authenticate the client.
|
||||||
|
|
||||||
|
options:
|
||||||
|
kube_config:
|
||||||
|
description:
|
||||||
|
- Path to a valid kube config file to test.
|
||||||
|
type: path
|
||||||
|
required: yes
|
||||||
|
dest_dir:
|
||||||
|
description:
|
||||||
|
- Path to a directory where file will be generated.
|
||||||
|
type: path
|
||||||
|
required: yes
|
||||||
|
"""
|
||||||
|
|
||||||
|
EXAMPLES = r"""
|
||||||
|
- name: Generate authentication parameters for current context
|
||||||
|
test_inventory_read_credentials:
|
||||||
|
kube_config: ~/.kube/config
|
||||||
|
dest_dir: /tmp
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
RETURN = """
|
||||||
|
auth:
|
||||||
|
description:
|
||||||
|
- User information used to authenticate to the cluster.
|
||||||
|
returned: always
|
||||||
|
type: complex
|
||||||
|
contains:
|
||||||
|
cert_file:
|
||||||
|
description:
|
||||||
|
- Path to the generated user certificate file.
|
||||||
|
type: str
|
||||||
|
key_file:
|
||||||
|
description:
|
||||||
|
- Path to the generated user key file.
|
||||||
|
type: str
|
||||||
|
ssl_ca_cert:
|
||||||
|
description:
|
||||||
|
- Path to the generated server certificate file.
|
||||||
|
type: str
|
||||||
|
host:
|
||||||
|
description:
|
||||||
|
- Path to the file containing cluster host.
|
||||||
|
type: str
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||||
|
|
||||||
|
try:
|
||||||
|
from kubernetes import client, config
|
||||||
|
from kubernetes.dynamic import DynamicClient, LazyDiscoverer
|
||||||
|
|
||||||
|
HAS_KUBERNETES_MODULE = True
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
HAS_KUBERNETES_MODULE = False
|
||||||
|
|
||||||
|
|
||||||
|
class K8SInventoryTestModule(AnsibleModule):
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
argument_spec = dict(
|
||||||
|
kube_config=dict(required=True, type="path"),
|
||||||
|
dest_dir=dict(required=True, type="path"),
|
||||||
|
)
|
||||||
|
|
||||||
|
super(K8SInventoryTestModule, self).__init__(argument_spec=argument_spec)
|
||||||
|
|
||||||
|
if not HAS_KUBERNETES_MODULE:
|
||||||
|
self.fail_json(msg=missing_required_lib("kubernetes"))
|
||||||
|
|
||||||
|
self.execute_module()
|
||||||
|
|
||||||
|
def execute_module(self):
|
||||||
|
|
||||||
|
dest_dir = os.path.abspath(self.params.get("dest_dir"))
|
||||||
|
kubeconfig_path = self.params.get("kube_config")
|
||||||
|
if not os.path.isdir(dest_dir):
|
||||||
|
self.fail_json(
|
||||||
|
msg="The following {0} does not exist or is not a directory.".format(
|
||||||
|
dest_dir
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not os.path.isfile(kubeconfig_path):
|
||||||
|
self.fail_json(
|
||||||
|
msg="The following {0} does not exist or is not a valid file.".format(
|
||||||
|
kubeconfig_path
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
client_config = type.__call__(client.Configuration)
|
||||||
|
config.load_kube_config(
|
||||||
|
config_file=kubeconfig_path, client_configuration=client_config
|
||||||
|
)
|
||||||
|
DynamicClient(client.ApiClient(client_config), discoverer=LazyDiscoverer)
|
||||||
|
|
||||||
|
result = dict(host=os.path.join(dest_dir, "host_data.txt"))
|
||||||
|
# create file containing host information
|
||||||
|
with open(result["host"], "w") as fd:
|
||||||
|
fd.write(client_config.host)
|
||||||
|
for key in ("cert_file", "key_file", "ssl_ca_cert"):
|
||||||
|
dest_file = os.path.join(dest_dir, "{0}_data.txt".format(key))
|
||||||
|
shutil.copyfile(getattr(client_config, key), dest_file)
|
||||||
|
result[key] = dest_file
|
||||||
|
|
||||||
|
self.exit_json(auth=result)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
K8SInventoryTestModule()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
46
tests/integration/targets/setup_kubeconfig/tasks/main.yml
Normal file
46
tests/integration/targets/setup_kubeconfig/tasks/main.yml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
---
|
||||||
|
- fail:
|
||||||
|
msg: "kubeconfig_operation must be one of 'revert' or 'save'"
|
||||||
|
when: kubeconfig_operation not in ["revert", "save"]
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
src_kubeconfig: "{{ (kubeconfig_operation == 'save') | ternary(kubeconfig_default_path, kubeconfig_custom_path) }}"
|
||||||
|
dest_kubeconfig: "{{ (kubeconfig_operation == 'save') | ternary(kubeconfig_custom_path, kubeconfig_default_path) }}"
|
||||||
|
|
||||||
|
- name: check if source kubeconfig exists
|
||||||
|
stat:
|
||||||
|
path: "{{ src_kubeconfig }}"
|
||||||
|
register: _src
|
||||||
|
|
||||||
|
- name: check if destination kubeconfig exists
|
||||||
|
stat:
|
||||||
|
path: "{{ dest_kubeconfig }}"
|
||||||
|
register: _dest
|
||||||
|
|
||||||
|
- fail:
|
||||||
|
msg: "Both {{ src_kubeconfig }} and {{ dest_kubeconfig }} do not exist."
|
||||||
|
when:
|
||||||
|
- not _src.stat.exists
|
||||||
|
- not _dest.stat.exists
|
||||||
|
|
||||||
|
- name: Generate user cert_file, key_file, and hostname
|
||||||
|
block:
|
||||||
|
- name: Generate user credentials files
|
||||||
|
test_inventory_read_credentials:
|
||||||
|
kube_config: "{{ (_src.stat.exists) | ternary(src_kubeconfig, dest_kubeconfig) }}"
|
||||||
|
dest_dir: "{{ user_credentials_dir }}"
|
||||||
|
when: user_credentials_dir is defined
|
||||||
|
|
||||||
|
- block:
|
||||||
|
- name: "Copy {{ src_kubeconfig }} into {{ dest_kubeconfig }}"
|
||||||
|
copy:
|
||||||
|
remote_src: true
|
||||||
|
src: "{{ src_kubeconfig }}"
|
||||||
|
dest: "{{ dest_kubeconfig }}"
|
||||||
|
|
||||||
|
- name: "Delete {{ src_kubeconfig }}"
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: "{{ src_kubeconfig }}"
|
||||||
|
|
||||||
|
when: _src.stat.exists
|
||||||
@@ -160,3 +160,25 @@ def test_load_kube_config_from_dict():
|
|||||||
|
|
||||||
assert expected_configuration.items() <= actual_configuration.__dict__.items()
|
assert expected_configuration.items() <= actual_configuration.__dict__.items()
|
||||||
_remove_temp_file()
|
_remove_temp_file()
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_auth_spec_with_aliases_in_kwargs():
|
||||||
|
auth_options = {
|
||||||
|
"host": TEST_HOST,
|
||||||
|
"cert_file": TEST_CLIENT_CERT,
|
||||||
|
"ssl_ca_cert": TEST_CERTIFICATE_AUTH,
|
||||||
|
"key_file": TEST_CLIENT_KEY,
|
||||||
|
"verify_ssl": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
expected_auth_spec = {
|
||||||
|
"host": TEST_HOST,
|
||||||
|
"cert_file": TEST_CLIENT_CERT,
|
||||||
|
"ssl_ca_cert": TEST_CERTIFICATE_AUTH,
|
||||||
|
"key_file": TEST_CLIENT_KEY,
|
||||||
|
"verify_ssl": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
actual_auth_spec = _create_auth_spec(module=None, **auth_options)
|
||||||
|
for key, value in expected_auth_spec.items():
|
||||||
|
assert value == actual_auth_spec.get(key)
|
||||||
|
|||||||
Reference in New Issue
Block a user