summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnsible Core Team <info@ansible.com>2020-03-09 09:40:30 +0000
committerAnsible Core Team <info@ansible.com>2020-03-09 09:40:30 +0000
commit9d2d1370382f0790b0d9049640e19781497e1456 (patch)
treeef380da8550da8dcb1ab2ac79135664d29d321f0
parent54b2c8f1131bdcbe19e43b72816c9107b67ae8ca (diff)
downloadansible-9d2d1370382f0790b0d9049640e19781497e1456.tar.gz
Migrated to community.kubernetes
-rw-r--r--lib/ansible/module_utils/k8s/__init__.py0
-rw-r--r--lib/ansible/module_utils/k8s/common.py290
-rw-r--r--lib/ansible/module_utils/k8s/raw.py519
-rw-r--r--lib/ansible/module_utils/k8s/scale.py243
l---------lib/ansible/modules/clustering/k8s/_k8s_facts.py1
l---------lib/ansible/modules/clustering/k8s/_k8s_raw.py1
-rw-r--r--lib/ansible/modules/clustering/k8s/k8s.py274
-rw-r--r--lib/ansible/modules/clustering/k8s/k8s_auth.py336
-rw-r--r--lib/ansible/modules/clustering/k8s/k8s_info.py179
-rw-r--r--lib/ansible/modules/clustering/k8s/k8s_scale.py129
-rw-r--r--lib/ansible/modules/clustering/k8s/k8s_service.py267
l---------lib/ansible/modules/clustering/openshift/_openshift_raw.py1
l---------lib/ansible/modules/clustering/openshift/_openshift_scale.py1
-rw-r--r--lib/ansible/plugins/doc_fragments/k8s_auth_options.py95
-rw-r--r--lib/ansible/plugins/doc_fragments/k8s_name_options.py45
-rw-r--r--lib/ansible/plugins/doc_fragments/k8s_resource_options.py29
-rw-r--r--lib/ansible/plugins/doc_fragments/k8s_scale_options.py39
-rw-r--r--lib/ansible/plugins/doc_fragments/k8s_state_options.py27
-rw-r--r--lib/ansible/plugins/filter/k8s.py40
-rw-r--r--lib/ansible/plugins/inventory/k8s.py360
-rw-r--r--lib/ansible/plugins/inventory/openshift.py201
l---------lib/ansible/plugins/lookup/_openshift.py1
-rw-r--r--lib/ansible/plugins/lookup/k8s.py299
-rw-r--r--test/integration/targets/k8s/README.md23
-rw-r--r--test/integration/targets/k8s/aliases2
-rw-r--r--test/integration/targets/k8s/defaults/main.yml32
-rw-r--r--test/integration/targets/k8s/files/crd-resource.yml20
-rw-r--r--test/integration/targets/k8s/files/kuard-extra-property.yml21
-rw-r--r--test/integration/targets/k8s/files/kuard-invalid-type.yml20
-rw-r--r--test/integration/targets/k8s/files/setup-crd.yml14
-rw-r--r--test/integration/targets/k8s/meta/main.yml2
-rw-r--r--test/integration/targets/k8s/tasks/append_hash.yml68
-rw-r--r--test/integration/targets/k8s/tasks/apply.yml277
-rw-r--r--test/integration/targets/k8s/tasks/crd.yml71
-rw-r--r--test/integration/targets/k8s/tasks/delete.yml101
-rw-r--r--test/integration/targets/k8s/tasks/full_test.yml375
-rw-r--r--test/integration/targets/k8s/tasks/lists.yml140
-rw-r--r--test/integration/targets/k8s/tasks/main.yml92
-rw-r--r--test/integration/targets/k8s/tasks/older_openshift_fail.yml69
-rw-r--r--test/integration/targets/k8s/tasks/openshift.yml61
-rw-r--r--test/integration/targets/k8s/tasks/validate_installed.yml125
-rw-r--r--test/integration/targets/k8s/tasks/validate_not_installed.yml23
-rw-r--r--test/integration/targets/k8s/tasks/waiter.yml355
-rw-r--r--test/sanity/ignore.txt33
44 files changed, 0 insertions, 5301 deletions
diff --git a/lib/ansible/module_utils/k8s/__init__.py b/lib/ansible/module_utils/k8s/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
--- a/lib/ansible/module_utils/k8s/__init__.py
+++ /dev/null
diff --git a/lib/ansible/module_utils/k8s/common.py b/lib/ansible/module_utils/k8s/common.py
deleted file mode 100644
index d86659f009..0000000000
--- a/lib/ansible/module_utils/k8s/common.py
+++ /dev/null
@@ -1,290 +0,0 @@
-# Copyright 2018 Red Hat | Ansible
-#
-# This file is part of Ansible
-#
-# Ansible is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Ansible is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-
-from __future__ import absolute_import, division, print_function
-
-import copy
-import json
-import os
-import traceback
-
-
-from ansible.module_utils.basic import AnsibleModule, missing_required_lib
-from ansible.module_utils.common.dict_transformations import recursive_diff
-from ansible.module_utils.six import iteritems, string_types
-from ansible.module_utils._text import to_native
-
-K8S_IMP_ERR = None
-try:
- import kubernetes
- import openshift
- from openshift.dynamic import DynamicClient
- from openshift.dynamic.exceptions import ResourceNotFoundError, ResourceNotUniqueError
- HAS_K8S_MODULE_HELPER = True
- k8s_import_exception = None
-except ImportError as e:
- HAS_K8S_MODULE_HELPER = False
- k8s_import_exception = e
- K8S_IMP_ERR = traceback.format_exc()
-
-YAML_IMP_ERR = None
-try:
- import yaml
- HAS_YAML = True
-except ImportError:
- YAML_IMP_ERR = traceback.format_exc()
- HAS_YAML = False
-
-try:
- import urllib3
- urllib3.disable_warnings()
-except ImportError:
- pass
-
-
-def list_dict_str(value):
- if isinstance(value, list):
- return value
- elif isinstance(value, dict):
- return value
- elif isinstance(value, string_types):
- return value
- raise TypeError
-
-
-ARG_ATTRIBUTES_BLACKLIST = ('property_path',)
-
-COMMON_ARG_SPEC = {
- 'state': {
- 'default': 'present',
- 'choices': ['present', 'absent'],
- },
- 'force': {
- 'type': 'bool',
- 'default': False,
- },
- 'resource_definition': {
- 'type': list_dict_str,
- 'aliases': ['definition', 'inline']
- },
- 'src': {
- 'type': 'path',
- },
- 'kind': {},
- 'name': {},
- 'namespace': {},
- 'api_version': {
- 'default': 'v1',
- 'aliases': ['api', 'version'],
- },
-}
-
-AUTH_ARG_SPEC = {
- 'kubeconfig': {
- 'type': 'path',
- },
- 'context': {},
- 'host': {},
- 'api_key': {
- 'no_log': True,
- },
- 'username': {},
- 'password': {
- 'no_log': True,
- },
- 'validate_certs': {
- 'type': 'bool',
- 'aliases': ['verify_ssl'],
- },
- 'ca_cert': {
- 'type': 'path',
- 'aliases': ['ssl_ca_cert'],
- },
- 'client_cert': {
- 'type': 'path',
- 'aliases': ['cert_file'],
- },
- 'client_key': {
- 'type': 'path',
- 'aliases': ['key_file'],
- },
- 'proxy': {},
- 'persist_config': {
- 'type': 'bool',
- },
-}
-
-# Map kubernetes-client parameters to ansible parameters
-AUTH_ARG_MAP = {
- 'kubeconfig': 'kubeconfig',
- 'context': 'context',
- 'host': 'host',
- 'api_key': 'api_key',
- 'username': 'username',
- 'password': 'password',
- 'verify_ssl': 'validate_certs',
- 'ssl_ca_cert': 'ca_cert',
- 'cert_file': 'client_cert',
- 'key_file': 'client_key',
- 'proxy': 'proxy',
- 'persist_config': 'persist_config',
-}
-
-
-class K8sAnsibleMixin(object):
- _argspec_cache = None
-
- @property
- def argspec(self):
- """
- Introspect the model properties, and return an Ansible module arg_spec dict.
- :return: dict
- """
- if self._argspec_cache:
- return self._argspec_cache
- argument_spec = copy.deepcopy(COMMON_ARG_SPEC)
- argument_spec.update(copy.deepcopy(AUTH_ARG_SPEC))
- self._argspec_cache = argument_spec
- return self._argspec_cache
-
- def get_api_client(self, **auth_params):
- auth_params = auth_params or getattr(self, 'params', {})
- auth = {}
-
- # If authorization variables aren't defined, look for them in environment variables
- for true_name, arg_name in AUTH_ARG_MAP.items():
- if auth_params.get(arg_name) is None:
- env_value = os.getenv('K8S_AUTH_{0}'.format(arg_name.upper()), None) or os.getenv('K8S_AUTH_{0}'.format(true_name.upper()), None)
- if env_value is not None:
- if AUTH_ARG_SPEC[arg_name].get('type') == 'bool':
- env_value = env_value.lower() not in ['0', 'false', 'no']
- auth[true_name] = env_value
- else:
- auth[true_name] = auth_params[arg_name]
-
- def auth_set(*names):
- return all([auth.get(name) for name in names])
-
- if auth_set('username', 'password', 'host') or auth_set('api_key', 'host'):
- # We have enough in the parameters to authenticate, no need to load incluster or kubeconfig
- pass
- elif auth_set('kubeconfig') or auth_set('context'):
- kubernetes.config.load_kube_config(auth.get('kubeconfig'), auth.get('context'), persist_config=auth.get('persist_config'))
- else:
- # First try to do incluster config, then kubeconfig
- try:
- kubernetes.config.load_incluster_config()
- except kubernetes.config.ConfigException:
- kubernetes.config.load_kube_config(auth.get('kubeconfig'), auth.get('context'), persist_config=auth.get('persist_config'))
-
- # Override any values in the default configuration with Ansible parameters
- configuration = kubernetes.client.Configuration()
- for key, value in iteritems(auth):
- if key in AUTH_ARG_MAP.keys() and value is not None:
- if key == 'api_key':
- setattr(configuration, key, {'authorization': "Bearer {0}".format(value)})
- else:
- setattr(configuration, key, value)
-
- kubernetes.client.Configuration.set_default(configuration)
- return DynamicClient(kubernetes.client.ApiClient(configuration))
-
- def find_resource(self, kind, api_version, fail=False):
- for attribute in ['kind', 'name', 'singular_name']:
- try:
- return self.client.resources.get(**{'api_version': api_version, attribute: kind})
- except (ResourceNotFoundError, ResourceNotUniqueError):
- pass
- try:
- return self.client.resources.get(api_version=api_version, short_names=[kind])
- except (ResourceNotFoundError, ResourceNotUniqueError):
- if fail:
- self.fail(msg='Failed to find exact match for {0}.{1} by [kind, name, singularName, shortNames]'.format(api_version, kind))
-
- def kubernetes_facts(self, kind, api_version, name=None, namespace=None, label_selectors=None, field_selectors=None):
- resource = self.find_resource(kind, api_version)
- if not resource:
- return dict(resources=[])
- try:
- result = resource.get(name=name,
- namespace=namespace,
- label_selector=','.join(label_selectors),
- field_selector=','.join(field_selectors)).to_dict()
- except openshift.dynamic.exceptions.NotFoundError:
- return dict(resources=[])
-
- if 'items' in result:
- return dict(resources=result['items'])
- else:
- return dict(resources=[result])
-
- def remove_aliases(self):
- """
- The helper doesn't know what to do with aliased keys
- """
- for k, v in iteritems(self.argspec):
- if 'aliases' in v:
- for alias in v['aliases']:
- if alias in self.params:
- self.params.pop(alias)
-
- def load_resource_definitions(self, src):
- """ Load the requested src path """
- result = None
- path = os.path.normpath(src)
- if not os.path.exists(path):
- self.fail(msg="Error accessing {0}. Does the file exist?".format(path))
- try:
- with open(path, 'r') as f:
- result = list(yaml.safe_load_all(f))
- except (IOError, yaml.YAMLError) as exc:
- self.fail(msg="Error loading resource_definition: {0}".format(exc))
- return result
-
- @staticmethod
- def diff_objects(existing, new):
- result = dict()
- diff = recursive_diff(existing, new)
- if diff:
- result['before'] = diff[0]
- result['after'] = diff[1]
- return not diff, result
-
-
-class KubernetesAnsibleModule(AnsibleModule, K8sAnsibleMixin):
- resource_definition = None
- api_version = None
- kind = None
-
- def __init__(self, *args, **kwargs):
-
- kwargs['argument_spec'] = self.argspec
- AnsibleModule.__init__(self, *args, **kwargs)
-
- if not HAS_K8S_MODULE_HELPER:
- self.fail_json(msg=missing_required_lib('openshift'), exception=K8S_IMP_ERR,
- error=to_native(k8s_import_exception))
- self.openshift_version = openshift.__version__
-
- if not HAS_YAML:
- self.fail_json(msg=missing_required_lib("PyYAML"), exception=YAML_IMP_ERR)
-
- def execute_module(self):
- raise NotImplementedError()
-
- def fail(self, msg=None):
- self.fail_json(msg=msg)
diff --git a/lib/ansible/module_utils/k8s/raw.py b/lib/ansible/module_utils/k8s/raw.py
deleted file mode 100644
index 06272b8158..0000000000
--- a/lib/ansible/module_utils/k8s/raw.py
+++ /dev/null
@@ -1,519 +0,0 @@
-#
-# Copyright 2018 Red Hat | Ansible
-#
-# This file is part of Ansible
-#
-# Ansible is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Ansible is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-
-from __future__ import absolute_import, division, print_function
-
-import copy
-from datetime import datetime
-from distutils.version import LooseVersion
-import time
-import sys
-import traceback
-
-from ansible.module_utils.basic import missing_required_lib
-from ansible.module_utils.k8s.common import AUTH_ARG_SPEC, COMMON_ARG_SPEC
-from ansible.module_utils.six import string_types
-from ansible.module_utils.k8s.common import KubernetesAnsibleModule
-from ansible.module_utils.common.dict_transformations import dict_merge
-
-
-try:
- import yaml
- from openshift.dynamic.exceptions import DynamicApiError, NotFoundError, ConflictError, ForbiddenError, KubernetesValidateMissing
- import urllib3
-except ImportError:
- # Exceptions handled in common
- pass
-
-try:
- import kubernetes_validate
- HAS_KUBERNETES_VALIDATE = True
-except ImportError:
- HAS_KUBERNETES_VALIDATE = False
-
-K8S_CONFIG_HASH_IMP_ERR = None
-try:
- from openshift.helper.hashes import generate_hash
- HAS_K8S_CONFIG_HASH = True
-except ImportError:
- K8S_CONFIG_HASH_IMP_ERR = traceback.format_exc()
- HAS_K8S_CONFIG_HASH = False
-
-HAS_K8S_APPLY = None
-try:
- from openshift.dynamic.apply import apply_object
- HAS_K8S_APPLY = True
-except ImportError:
- HAS_K8S_APPLY = False
-
-
-class KubernetesRawModule(KubernetesAnsibleModule):
-
- @property
- def validate_spec(self):
- return dict(
- fail_on_error=dict(type='bool'),
- version=dict(),
- strict=dict(type='bool', default=True)
- )
-
- @property
- def condition_spec(self):
- return dict(
- type=dict(),
- status=dict(default=True, choices=[True, False, "Unknown"]),
- reason=dict()
- )
-
- @property
- def argspec(self):
- argument_spec = copy.deepcopy(COMMON_ARG_SPEC)
- argument_spec.update(copy.deepcopy(AUTH_ARG_SPEC))
- argument_spec['merge_type'] = dict(type='list', choices=['json', 'merge', 'strategic-merge'])
- argument_spec['wait'] = dict(type='bool', default=False)
- argument_spec['wait_sleep'] = dict(type='int', default=5)
- argument_spec['wait_timeout'] = dict(type='int', default=120)
- argument_spec['wait_condition'] = dict(type='dict', default=None, options=self.condition_spec)
- argument_spec['validate'] = dict(type='dict', default=None, options=self.validate_spec)
- argument_spec['append_hash'] = dict(type='bool', default=False)
- argument_spec['apply'] = dict(type='bool', default=False)
- return argument_spec
-
- def __init__(self, k8s_kind=None, *args, **kwargs):
- self.client = None
- self.warnings = []
-
- mutually_exclusive = [
- ('resource_definition', 'src'),
- ('merge_type', 'apply'),
- ]
-
- KubernetesAnsibleModule.__init__(self, *args,
- mutually_exclusive=mutually_exclusive,
- supports_check_mode=True,
- **kwargs)
- self.kind = k8s_kind or self.params.get('kind')
- self.api_version = self.params.get('api_version')
- self.name = self.params.get('name')
- self.namespace = self.params.get('namespace')
- resource_definition = self.params.get('resource_definition')
- validate = self.params.get('validate')
- if validate:
- if LooseVersion(self.openshift_version) < LooseVersion("0.8.0"):
- self.fail_json(msg="openshift >= 0.8.0 is required for validate")
- self.append_hash = self.params.get('append_hash')
- if self.append_hash:
- if not HAS_K8S_CONFIG_HASH:
- self.fail_json(msg=missing_required_lib("openshift >= 0.7.2", reason="for append_hash"),
- exception=K8S_CONFIG_HASH_IMP_ERR)
- if self.params['merge_type']:
- if LooseVersion(self.openshift_version) < LooseVersion("0.6.2"):
- self.fail_json(msg=missing_required_lib("openshift >= 0.6.2", reason="for merge_type"))
- self.apply = self.params.get('apply', False)
- if self.apply:
- if not HAS_K8S_APPLY:
- self.fail_json(msg=missing_required_lib("openshift >= 0.9.2", reason="for apply"))
-
- if resource_definition:
- if isinstance(resource_definition, string_types):
- try:
- self.resource_definitions = yaml.safe_load_all(resource_definition)
- except (IOError, yaml.YAMLError) as exc:
- self.fail(msg="Error loading resource_definition: {0}".format(exc))
- elif isinstance(resource_definition, list):
- self.resource_definitions = resource_definition
- else:
- self.resource_definitions = [resource_definition]
- src = self.params.get('src')
- if src:
- self.resource_definitions = self.load_resource_definitions(src)
- try:
- self.resource_definitions = [item for item in self.resource_definitions if item]
- except AttributeError:
- pass
-
- if not resource_definition and not src:
- implicit_definition = dict(
- kind=self.kind,
- apiVersion=self.api_version,
- metadata=dict(name=self.name)
- )
- if self.namespace:
- implicit_definition['metadata']['namespace'] = self.namespace
- self.resource_definitions = [implicit_definition]
-
- def flatten_list_kind(self, list_resource, definitions):
- flattened = []
- parent_api_version = list_resource.group_version if list_resource else None
- parent_kind = list_resource.kind[:-4] if list_resource else None
- for definition in definitions.get('items', []):
- resource = self.find_resource(definition.get('kind', parent_kind), definition.get('apiVersion', parent_api_version), fail=True)
- flattened.append((resource, self.set_defaults(resource, definition)))
- return flattened
-
- def execute_module(self):
- changed = False
- results = []
- try:
- self.client = self.get_api_client()
- # Hopefully the kubernetes client will provide its own exception class one day
- except (urllib3.exceptions.RequestError) as e:
- self.fail_json(msg="Couldn't connect to Kubernetes: %s" % str(e))
-
- flattened_definitions = []
- for definition in self.resource_definitions:
- kind = definition.get('kind', self.kind)
- api_version = definition.get('apiVersion', self.api_version)
- if kind.endswith('List'):
- resource = self.find_resource(kind, api_version, fail=False)
- flattened_definitions.extend(self.flatten_list_kind(resource, definition))
- else:
- resource = self.find_resource(kind, api_version, fail=True)
- flattened_definitions.append((resource, definition))
-
- for (resource, definition) in flattened_definitions:
- kind = definition.get('kind', self.kind)
- api_version = definition.get('apiVersion', self.api_version)
- definition = self.set_defaults(resource, definition)
- self.warnings = []
- if self.params['validate'] is not None:
- self.warnings = self.validate(definition)
- result = self.perform_action(resource, definition)
- result['warnings'] = self.warnings
- changed = changed or result['changed']
- results.append(result)
-
- if len(results) == 1:
- self.exit_json(**results[0])
-
- self.exit_json(**{
- 'changed': changed,
- 'result': {
- 'results': results
- }
- })
-
- def validate(self, resource):
- def _prepend_resource_info(resource, msg):
- return "%s %s: %s" % (resource['kind'], resource['metadata']['name'], msg)
-
- try:
- warnings, errors = self.client.validate(resource, self.params['validate'].get('version'), self.params['validate'].get('strict'))
- except KubernetesValidateMissing:
- self.fail_json(msg="kubernetes-validate python library is required to validate resources")
-
- if errors and self.params['validate']['fail_on_error']:
- self.fail_json(msg="\n".join([_prepend_resource_info(resource, error) for error in errors]))
- else:
- return [_prepend_resource_info(resource, msg) for msg in warnings + errors]
-
- def set_defaults(self, resource, definition):
- definition['kind'] = resource.kind
- definition['apiVersion'] = resource.group_version
- metadata = definition.get('metadata', {})
- if self.name and not metadata.get('name'):
- metadata['name'] = self.name
- if resource.namespaced and self.namespace and not metadata.get('namespace'):
- metadata['namespace'] = self.namespace
- definition['metadata'] = metadata
- return definition
-
- def perform_action(self, resource, definition):
- result = {'changed': False, 'result': {}}
- state = self.params.get('state', None)
- force = self.params.get('force', False)
- name = definition['metadata'].get('name')
- namespace = definition['metadata'].get('namespace')
- existing = None
- wait = self.params.get('wait')
- wait_sleep = self.params.get('wait_sleep')
- wait_timeout = self.params.get('wait_timeout')
- wait_condition = None
- if self.params.get('wait_condition') and self.params['wait_condition'].get('type'):
- wait_condition = self.params['wait_condition']
-
- self.remove_aliases()
-
- try:
- # ignore append_hash for resources other than ConfigMap and Secret
- if self.append_hash and definition['kind'] in ['ConfigMap', 'Secret']:
- name = '%s-%s' % (name, generate_hash(definition))
- definition['metadata']['name'] = name
- params = dict(name=name)
- if namespace:
- params['namespace'] = namespace
- existing = resource.get(**params)
- except NotFoundError:
- # Remove traceback so that it doesn't show up in later failures
- try:
- sys.exc_clear()
- except AttributeError:
- # no sys.exc_clear on python3
- pass
- except ForbiddenError as exc:
- if definition['kind'] in ['Project', 'ProjectRequest'] and state != 'absent':
- return self.create_project_request(definition)
- self.fail_json(msg='Failed to retrieve requested object: {0}'.format(exc.body),
- error=exc.status, status=exc.status, reason=exc.reason)
- except DynamicApiError as exc:
- self.fail_json(msg='Failed to retrieve requested object: {0}'.format(exc.body),
- error=exc.status, status=exc.status, reason=exc.reason)
-
- if state == 'absent':
- result['method'] = "delete"
- if not existing:
- # The object already does not exist
- return result
- else:
- # Delete the object
- result['changed'] = True
- if not self.check_mode:
- try:
- k8s_obj = resource.delete(**params)
- result['result'] = k8s_obj.to_dict()
- except DynamicApiError as exc:
- self.fail_json(msg="Failed to delete object: {0}".format(exc.body),
- error=exc.status, status=exc.status, reason=exc.reason)
- if wait:
- success, resource, duration = self.wait(resource, definition, wait_sleep, wait_timeout, 'absent')
- result['duration'] = duration
- if not success:
- self.fail_json(msg="Resource deletion timed out", **result)
- return result
- else:
- if self.apply:
- if self.check_mode:
- ignored, k8s_obj = apply_object(resource, definition)
- else:
- try:
- k8s_obj = resource.apply(definition, namespace=namespace).to_dict()
- except DynamicApiError as exc:
- msg = "Failed to apply object: {0}".format(exc.body)
- if self.warnings:
- msg += "\n" + "\n ".join(self.warnings)
- self.fail_json(msg=msg, error=exc.status, status=exc.status, reason=exc.reason)
- success = True
- result['result'] = k8s_obj
- if wait:
- success, result['result'], result['duration'] = self.wait(resource, definition, wait_sleep, wait_timeout, condition=wait_condition)
- if existing:
- existing = existing.to_dict()
- else:
- existing = {}
- match, diffs = self.diff_objects(existing, result['result'])
- result['changed'] = not match
- result['diff'] = diffs
- result['method'] = 'apply'
- if not success:
- self.fail_json(msg="Resource apply timed out", **result)
- return result
-
- if not existing:
- if self.check_mode:
- k8s_obj = definition
- else:
- try:
- k8s_obj = resource.create(definition, namespace=namespace).to_dict()
- except ConflictError:
- # Some resources, like ProjectRequests, can't be created multiple times,
- # because the resources that they create don't match their kind
- # In this case we'll mark it as unchanged and warn the user
- self.warn("{0} was not found, but creating it returned a 409 Conflict error. This can happen \
- if the resource you are creating does not directly create a resource of the same kind.".format(name))
- return result
- except DynamicApiError as exc:
- msg = "Failed to create object: {0}".format(exc.body)
- if self.warnings:
- msg += "\n" + "\n ".join(self.warnings)
- self.fail_json(msg=msg, error=exc.status, status=exc.status, reason=exc.reason)
- success = True
- result['result'] = k8s_obj
- if wait and not self.check_mode:
- success, result['result'], result['duration'] = self.wait(resource, definition, wait_sleep, wait_timeout, condition=wait_condition)
- result['changed'] = True
- result['method'] = 'create'
- if not success:
- self.fail_json(msg="Resource creation timed out", **result)
- return result
-
- match = False
- diffs = []
-
- if existing and force:
- if self.check_mode:
- k8s_obj = definition
- else:
- try:
- k8s_obj = resource.replace(definition, name=name, namespace=namespace, append_hash=self.append_hash).to_dict()
- except DynamicApiError as exc:
- msg = "Failed to replace object: {0}".format(exc.body)
- if self.warnings:
- msg += "\n" + "\n ".join(self.warnings)
- self.fail_json(msg=msg, error=exc.status, status=exc.status, reason=exc.reason)
- match, diffs = self.diff_objects(existing.to_dict(), k8s_obj)
- success = True
- result['result'] = k8s_obj
- if wait:
- success, result['result'], result['duration'] = self.wait(resource, definition, wait_sleep, wait_timeout, condition=wait_condition)
- match, diffs = self.diff_objects(existing.to_dict(), result['result'])
- result['changed'] = not match
- result['method'] = 'replace'
- result['diff'] = diffs
- if not success:
- self.fail_json(msg="Resource replacement timed out", **result)
- return result
-
- # Differences exist between the existing obj and requested params
- if self.check_mode:
- k8s_obj = dict_merge(existing.to_dict(), definition)
- else:
- if LooseVersion(self.openshift_version) < LooseVersion("0.6.2"):
- k8s_obj, error = self.patch_resource(resource, definition, existing, name,
- namespace)
- else:
- for merge_type in self.params['merge_type'] or ['strategic-merge', 'merge']:
- k8s_obj, error = self.patch_resource(resource, definition, existing, name,
- namespace, merge_type=merge_type)
- if not error:
- break
- if error:
- self.fail_json(**error)
-
- success = True
- result['result'] = k8s_obj
- if wait:
- success, result['result'], result['duration'] = self.wait(resource, definition, wait_sleep, wait_timeout, condition=wait_condition)
- match, diffs = self.diff_objects(existing.to_dict(), result['result'])
- result['changed'] = not match
- result['method'] = 'patch'
- result['diff'] = diffs
-
- if not success:
- self.fail_json(msg="Resource update timed out", **result)
- return result
-
- def patch_resource(self, resource, definition, existing, name, namespace, merge_type=None):
- try:
- params = dict(name=name, namespace=namespace)
- if merge_type:
- params['content_type'] = 'application/{0}-patch+json'.format(merge_type)
- k8s_obj = resource.patch(definition, **params).to_dict()
- match, diffs = self.diff_objects(existing.to_dict(), k8s_obj)
- error = {}
- return k8s_obj, {}
- except DynamicApiError as exc:
- msg = "Failed to patch object: {0}".format(exc.body)
- if self.warnings:
- msg += "\n" + "\n ".join(self.warnings)
- error = dict(msg=msg, error=exc.status, status=exc.status, reason=exc.reason, warnings=self.warnings)
- return None, error
-
- def create_project_request(self, definition):
- definition['kind'] = 'ProjectRequest'
- result = {'changed': False, 'result': {}}
- resource = self.find_resource('ProjectRequest', definition['apiVersion'], fail=True)
- if not self.check_mode:
- try:
- k8s_obj = resource.create(definition)
- result['result'] = k8s_obj.to_dict()
- except DynamicApiError as exc:
- self.fail_json(msg="Failed to create object: {0}".format(exc.body),
- error=exc.status, status=exc.status, reason=exc.reason)
- result['changed'] = True
- result['method'] = 'create'
- return result
-
- def _wait_for(self, resource, name, namespace, predicate, sleep, timeout, state):
- start = datetime.now()
-
- def _wait_for_elapsed():
- return (datetime.now() - start).seconds
-
- response = None
- while _wait_for_elapsed() < timeout:
- try:
- response = resource.get(name=name, namespace=namespace)
- if predicate(response):
- if response:
- return True, response.to_dict(), _wait_for_elapsed()
- else:
- return True, {}, _wait_for_elapsed()
- time.sleep(sleep)
- except NotFoundError:
- if state == 'absent':
- return True, {}, _wait_for_elapsed()
- if response:
- response = response.to_dict()
- return False, response, _wait_for_elapsed()
-
- def wait(self, resource, definition, sleep, timeout, state='present', condition=None):
-
- def _deployment_ready(deployment):
- # FIXME: frustratingly bool(deployment.status) is True even if status is empty
- # Furthermore deployment.status.availableReplicas == deployment.status.replicas == None if status is empty
- return (deployment.status and deployment.status.replicas is not None and
- deployment.status.availableReplicas == deployment.status.replicas and
- deployment.status.observedGeneration == deployment.metadata.generation)
-
- def _pod_ready(pod):
- return (pod.status and pod.status.containerStatuses is not None and
- all([container.ready for container in pod.status.containerStatuses]))
-
- def _daemonset_ready(daemonset):
- return (daemonset.status and daemonset.status.desiredNumberScheduled is not None and
- daemonset.status.numberReady == daemonset.status.desiredNumberScheduled and
- daemonset.status.observedGeneration == daemonset.metadata.generation)
-
- def _custom_condition(resource):
- if not resource.status or not resource.status.conditions:
- return False
- match = [x for x in resource.status.conditions if x.type == condition['type']]
- if not match:
- return False
- # There should never be more than one condition of a specific type
- match = match[0]
- if match.status == 'Unknown':
- if match.status == condition['status']:
- if 'reason' not in condition:
- return True
- if condition['reason']:
- return match.reason == condition['reason']
- return False
- status = True if match.status == 'True' else False
- if status == condition['status']:
- if condition.get('reason'):
- return match.reason == condition['reason']
- return True
- return False
-
- def _resource_absent(resource):
- return not resource
-
- waiter = dict(
- Deployment=_deployment_ready,
- DaemonSet=_daemonset_ready,
- Pod=_pod_ready
- )
- kind = definition['kind']
- if state == 'present' and not condition:
- predicate = waiter.get(kind, lambda x: x)
- elif state == 'present' and condition:
- predicate = _custom_condition
- else:
- predicate = _resource_absent
- return self._wait_for(resource, definition['metadata']['name'], definition['metadata'].get('namespace'), predicate, sleep, timeout, state)
diff --git a/lib/ansible/module_utils/k8s/scale.py b/lib/ansible/module_utils/k8s/scale.py
deleted file mode 100644
index 8a2a27fbd0..0000000000
--- a/lib/ansible/module_utils/k8s/scale.py
+++ /dev/null
@@ -1,243 +0,0 @@
-#
-# Copyright 2018 Red Hat | Ansible
-#
-# This file is part of Ansible
-#
-# Ansible is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Ansible is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-
-from __future__ import absolute_import, division, print_function
-
-import copy
-import math
-import time
-
-from ansible.module_utils.k8s.common import AUTH_ARG_SPEC, COMMON_ARG_SPEC
-from ansible.module_utils.k8s.common import KubernetesAnsibleModule
-from ansible.module_utils.six import string_types
-
-try:
- import yaml
- from openshift import watch
- from openshift.dynamic.client import ResourceInstance
- from openshift.helper.exceptions import KubernetesException
-except ImportError as exc:
- class KubernetesException(Exception):
- pass
-
-
-SCALE_ARG_SPEC = {
- 'replicas': {'type': 'int', 'required': True},
- 'current_replicas': {'type': 'int'},
- 'resource_version': {},
- 'wait': {'type': 'bool', 'default': True},
- 'wait_timeout': {'type': 'int', 'default': 20},
-}
-
-
-class KubernetesAnsibleScaleModule(KubernetesAnsibleModule):
-
- def __init__(self, k8s_kind=None, *args, **kwargs):
- self.client = None
- self.warnings = []
-
- mutually_exclusive = [
- ('resource_definition', 'src'),
- ]
-
- KubernetesAnsibleModule.__init__(self, *args,
- mutually_exclusive=mutually_exclusive,
- supports_check_mode=True,
- **kwargs)
- self.kind = k8s_kind or self.params.get('kind')
- self.api_version = self.params.get('api_version')
- self.name = self.params.get('name')
- self.namespace = self.params.get('namespace')
- resource_definition = self.params.get('resource_definition')
-
- if resource_definition:
- if isinstance(resource_definition, string_types):
- try:
- self.resource_definitions = yaml.safe_load_all(resource_definition)
- except (IOError, yaml.YAMLError) as exc:
- self.fail(msg="Error loading resource_definition: {0}".format(exc))
- elif isinstance(resource_definition, list):
- self.resource_definitions = resource_definition
- else:
- self.resource_definitions = [resource_definition]
- src = self.params.get('src')
- if src:
- self.resource_definitions = self.load_resource_definitions(src)
-
- if not resource_definition and not src:
- implicit_definition = dict(
- kind=self.kind,
- apiVersion=self.api_version,
- metadata=dict(name=self.name)
- )
- if self.namespace:
- implicit_definition['metadata']['namespace'] = self.namespace
- self.resource_definitions = [implicit_definition]
-
- def execute_module(self):
- definition = self.resource_definitions[0]
-
- self.client = self.get_api_client()
-
- name = definition['metadata']['name']
- namespace = definition['metadata'].get('namespace')
- api_version = definition['apiVersion']
- kind = definition['kind']
- current_replicas = self.params.get('current_replicas')
- replicas = self.params.get('replicas')
- resource_version = self.params.get('resource_version')
-
- wait = self.params.get('wait')
- wait_time = self.params.get('wait_timeout')
- existing = None
- existing_count = None
- return_attributes = dict(changed=False, result=dict())
-
- resource = self.find_resource(kind, api_version, fail=True)
-
- try:
- existing = resource.get(name=name, namespace=namespace)
- return_attributes['result'] = existing.to_dict()
- except KubernetesException as exc:
- self.fail_json(msg='Failed to retrieve requested object: {0}'.format(exc),
- error=exc.value.get('status'))
-
- if self.kind == 'job':
- existing_count = existing.spec.parallelism
- elif hasattr(existing.spec, 'replicas'):
- existing_count = existing.spec.replicas
-
- if existing_count is None:
- self.fail_json(msg='Failed to retrieve the available count for the requested object.')
-
- if resource_version and resource_version != existing.metadata.resourceVersion:
- self.exit_json(**return_attributes)
-
- if current_replicas is not None and existing_count != current_replicas:
- self.exit_json(**return_attributes)
-
- if existing_count != replicas:
- return_attributes['changed'] = True
- if not self.check_mode:
- if self.kind == 'job':
- existing.spec.parallelism = replicas
- k8s_obj = resource.patch(existing.to_dict())
- else:
- k8s_obj = self.scale(resource, existing, replicas, wait, wait_time)
- return_attributes['result'] = k8s_obj.to_dict()
-
- self.exit_json(**return_attributes)
-
- @property
- def argspec(self):
- args = copy.deepcopy(COMMON_ARG_SPEC)
- args.pop('state')
- args.pop('force')
- args.update(AUTH_ARG_SPEC)
- args.update(SCALE_ARG_SPEC)
- return args
-
- def scale(self, resource, existing_object, replicas, wait, wait_time):
- name = existing_object.metadata.name
- namespace = existing_object.metadata.namespace
-
- if not hasattr(resource, 'scale'):
- self.fail_json(
- msg="Cannot perform scale on resource of kind {0}".format(resource.kind)
- )
-
- scale_obj = {'metadata': {'name': name, 'namespace': namespace}, 'spec': {'replicas': replicas}}
-
- return_obj = None
- stream = None
-
- if wait:
- w, stream = self._create_stream(resource, namespace, wait_time)
-
- try:
- resource.scale.patch(body=scale_obj)
- except Exception as exc:
- self.fail_json(
- msg="Scale request failed: {0}".format(exc)
- )
-
- if wait and stream is not None:
- return_obj = self._read_stream(resource, w, stream, name, replicas)
-
- if not return_obj:
- return_obj = self._wait_for_response(resource, name, namespace)
-
- return return_obj
-
- def _create_stream(self, resource, namespace, wait_time):
- """ Create a stream of events for the object """
- w = None
- stream = None
- try:
- w = watch.Watch()
- w._api_client = self.client.client
- if namespace:
- stream = w.stream(resource.get, serialize=False, namespace=namespace, timeout_seconds=wait_time)
- else:
- stream = w.stream(resource.get, serialize=False, namespace=namespace, timeout_seconds=wait_time)
- except KubernetesException:
- pass
- return w, stream
-
- def _read_stream(self, resource, watcher, stream, name, replicas):
- """ Wait for ready_replicas to equal the requested number of replicas. """
- return_obj = None
- try:
- for event in stream:
- if event.get('object'):
- obj = ResourceInstance(resource, event['object'])
- if obj.metadata.name == name and hasattr(obj, 'status'):
- if replicas == 0:
- if not hasattr(obj.status, 'readyReplicas') or not obj.status.readyReplicas:
- return_obj = obj
- watcher.stop()
- break
- if hasattr(obj.status, 'readyReplicas') and obj.status.readyReplicas == replicas:
- return_obj = obj
- watcher.stop()
- break
- except Exception as exc:
- self.fail_json(msg="Exception reading event stream: {0}".format(exc))
-
- if not return_obj:
- self.fail_json(msg="Error fetching the patched object. Try a higher wait_timeout value.")
- if replicas and return_obj.status.readyReplicas is None:
- self.fail_json(msg="Failed to fetch the number of ready replicas. Try a higher wait_timeout value.")
- if replicas and return_obj.status.readyReplicas != replicas:
- self.fail_json(msg="Number of ready replicas is {0}. Failed to reach {1} ready replicas within "
- "the wait_timeout period.".format(return_obj.status.ready_replicas, replicas))
- return return_obj
-
- def _wait_for_response(self, resource, name, namespace):
- """ Wait for an API response """
- tries = 0
- half = math.ceil(20 / 2)
- obj = None
-
- while tries <= half:
- obj = resource.get(name=name, namespace=namespace)
- if obj:
- break
- tries += 2
- time.sleep(2)
- return obj
diff --git a/lib/ansible/modules/clustering/k8s/_k8s_facts.py b/lib/ansible/modules/clustering/k8s/_k8s_facts.py
deleted file mode 120000
index 667980ed38..0000000000
--- a/lib/ansible/modules/clustering/k8s/_k8s_facts.py
+++ /dev/null
@@ -1 +0,0 @@
-k8s_info.py \ No newline at end of file
diff --git a/lib/ansible/modules/clustering/k8s/_k8s_raw.py b/lib/ansible/modules/clustering/k8s/_k8s_raw.py
deleted file mode 120000
index b0ee072f96..0000000000
--- a/lib/ansible/modules/clustering/k8s/_k8s_raw.py
+++ /dev/null
@@ -1 +0,0 @@
-k8s.py \ No newline at end of file
diff --git a/lib/ansible/modules/clustering/k8s/k8s.py b/lib/ansible/modules/clustering/k8s/k8s.py
deleted file mode 100644
index f3938bf39c..0000000000
--- a/lib/ansible/modules/clustering/k8s/k8s.py
+++ /dev/null
@@ -1,274 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2018, Chris Houseknecht <@chouseknecht>
-# 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
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = '''
-
-module: k8s
-
-short_description: Manage Kubernetes (K8s) objects
-
-version_added: "2.6"
-
-author:
- - "Chris Houseknecht (@chouseknecht)"
- - "Fabian von Feilitzsch (@fabianvf)"
-
-description:
- - Use the OpenShift Python client to perform CRUD operations on K8s objects.
- - Pass the object definition from a source file or inline. See examples for reading
- files and using Jinja templates or vault-encrypted files.
- - Access to the full range of K8s APIs.
- - Use the M(k8s_info) module to obtain a list of items about an object of type C(kind)
- - Authenticate using either a config file, certificates, password or token.
- - Supports check mode.
-
-extends_documentation_fragment:
- - k8s_state_options
- - k8s_name_options
- - k8s_resource_options
- - k8s_auth_options
-
-notes:
- - If your OpenShift Python library is not 0.9.0 or newer and you are trying to
- remove an item from an associative array/dictionary, for example a label or
- an annotation, you will need to explicitly set the value of the item to be
- removed to `null`. Simply deleting the entry in the dictionary will not
- remove it from openshift or kubernetes.
-
-options:
- merge_type:
- description:
- - Whether to override the default patch merge approach with a specific type. By default, the strategic
- merge will typically be used.
- - For example, Custom Resource Definitions typically aren't updatable by the usual strategic merge. You may
- want to use C(merge) if you see "strategic merge patch format is not supported"
- - See U(https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment)
- - Requires openshift >= 0.6.2
- - If more than one merge_type is given, the merge_types will be tried in order
- - If openshift >= 0.6.2, this defaults to C(['strategic-merge', 'merge']), which is ideal for using the same parameters
- on resource kinds that combine Custom Resources and built-in resources. For openshift < 0.6.2, the default
- is simply C(strategic-merge).
- - mutually exclusive with C(apply)
- choices:
- - json
- - merge
- - strategic-merge
- type: list
- version_added: "2.7"
- wait:
- description:
- - Whether to wait for certain resource kinds to end up in the desired state. By default the module exits once Kubernetes has
- received the request
- - Implemented for C(state=present) for C(Deployment), C(DaemonSet) and C(Pod), and for C(state=absent) for all resource kinds.
- - For resource kinds without an implementation, C(wait) returns immediately unless C(wait_condition) is set.
- default: no
- type: bool
- version_added: "2.8"
- wait_sleep:
- description:
- - Number of seconds to sleep between checks.
- default: 5
- version_added: "2.9"
- wait_timeout:
- description:
- - How long in seconds to wait for the resource to end up in the desired state. Ignored if C(wait) is not set.
- default: 120
- version_added: "2.8"
- wait_condition:
- description:
- - Specifies a custom condition on the status to wait for. Ignored if C(wait) is not set or is set to False.
- suboptions:
- type:
- description:
- - The type of condition to wait for. For example, the C(Pod) resource will set the C(Ready) condition (among others)
- - Required if you are specifying a C(wait_condition). If left empty, the C(wait_condition) field will be ignored.
- - The possible types for a condition are specific to each resource type in Kubernetes. See the API documentation of the status field
- for a given resource to see possible choices.
- status:
- description:
- - The value of the status field in your desired condition.
- - For example, if a C(Deployment) is paused, the C(Progressing) C(type) will have the C(Unknown) status.
- choices:
- - True
- - False
- - Unknown
- reason:
- description:
- - The value of the reason field in your desired condition
- - For example, if a C(Deployment) is paused, The C(Progressing) C(type) will have the C(DeploymentPaused) reason.
- - The possible reasons in a condition are specific to each resource type in Kubernetes. See the API documentation of the status field
- for a given resource to see possible choices.
- version_added: "2.8"
- validate:
- description:
- - how (if at all) to validate the resource definition against the kubernetes schema.
- Requires the kubernetes-validate python module
- suboptions:
- fail_on_error:
- description: whether to fail on validation errors.
- required: yes
- type: bool
- version:
- description: version of Kubernetes to validate against. defaults to Kubernetes server version
- strict:
- description: whether to fail when passing unexpected properties
- default: no
- type: bool
- version_added: "2.8"
- append_hash:
- description:
- - Whether to append a hash to a resource name for immutability purposes
- - Applies only to ConfigMap and Secret resources
- - The parameter will be silently ignored for other resource kinds
- - The full definition of an object is needed to generate the hash - this means that deleting an object created with append_hash
- will only work if the same object is passed with state=absent (alternatively, just use state=absent with the name including
- the generated hash and append_hash=no)
- type: bool
- version_added: "2.8"
- apply:
- description:
- - C(apply) compares the desired resource definition with the previously supplied resource definition,
- ignoring properties that are automatically generated
- - C(apply) works better with Services than 'force=yes'
- - mutually exclusive with C(merge_type)
- type: bool
- version_added: "2.9"
-
-requirements:
- - "python >= 2.7"
- - "openshift >= 0.6"
- - "PyYAML >= 3.11"
-'''
-
-EXAMPLES = '''
-- name: Create a k8s namespace
- k8s:
- name: testing
- api_version: v1
- kind: Namespace
- state: present
-
-- name: Create a Service object from an inline definition
- k8s:
- state: present
- definition:
- apiVersion: v1
- kind: Service
- metadata:
- name: web
- namespace: testing
- labels:
- app: galaxy
- service: web
- spec:
- selector:
- app: galaxy
- service: web
- ports:
- - protocol: TCP
- targetPort: 8000
- name: port-8000-tcp
- port: 8000
-
-- name: Remove an existing Service object
- k8s:
- state: absent
- api_version: v1
- kind: Service
- namespace: testing
- name: web
-
-# Passing the object definition from a file
-
-- name: Create a Deployment by reading the definition from a local file
- k8s:
- state: present
- src: /testing/deployment.yml
-
-- name: >-
- Read definition file from the Ansible controller file system.
- If the definition file has been encrypted with Ansible Vault it will automatically be decrypted.
- k8s:
- state: present
- definition: "{{ lookup('file', '/testing/deployment.yml') | from_yaml }}"
-
-- name: Read definition file from the Ansible controller file system after Jinja templating
- k8s:
- state: present
- definition: "{{ lookup('template', '/testing/deployment.yml') | from_yaml }}"
-
-- name: fail on validation errors
- k8s:
- state: present
- definition: "{{ lookup('template', '/testing/deployment.yml') | from_yaml }}"
- validate:
- fail_on_error: yes
-
-- name: warn on validation errors, check for unexpected properties
- k8s:
- state: present
- definition: "{{ lookup('template', '/testing/deployment.yml') | from_yaml }}"
- validate:
- fail_on_error: no
- strict: yes
-'''
-
-RETURN = '''
-result:
- description:
- - The created, patched, or otherwise present object. Will be empty in the case of a deletion.
- returned: success
- type: complex
- contains:
- api_version:
- description: The versioned schema of this representation of an object.
- returned: success
- type: str
- kind:
- description: Represents the REST resource this object represents.
- returned: success
- type: str
- metadata:
- description: Standard object metadata. Includes name, namespace, annotations, labels, etc.
- returned: success
- type: complex
- spec:
- description: Specific attributes of the object. Will vary based on the I(api_version) and I(kind).
- returned: success
- type: complex
- status:
- description: Current status details for the object.
- returned: success
- type: complex
- items:
- description: Returned only when multiple yaml documents are passed to src or resource_definition
- returned: when resource_definition or src contains list of objects
- type: list
- duration:
- description: elapsed time of task in seconds
- returned: when C(wait) is true
- type: int
- sample: 48
-'''
-
-from ansible.module_utils.k8s.raw import KubernetesRawModule
-
-
-def main():
- KubernetesRawModule().execute_module()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/clustering/k8s/k8s_auth.py b/lib/ansible/modules/clustering/k8s/k8s_auth.py
deleted file mode 100644
index 8dfd2efcb6..0000000000
--- a/lib/ansible/modules/clustering/k8s/k8s_auth.py
+++ /dev/null
@@ -1,336 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2018, KubeVirt Team <@kubevirt>
-# 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
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = '''
-
-module: k8s_auth
-
-short_description: Authenticate to Kubernetes clusters which require an explicit login step
-
-version_added: "2.8"
-
-author: KubeVirt Team (@kubevirt)
-
-description:
- - "This module handles authenticating to Kubernetes clusters requiring I(explicit) authentication procedures,
- meaning ones where a client logs in (obtains an authentication token), performs API operations using said
- token and then logs out (revokes the token). An example of a Kubernetes distribution requiring this module
- is OpenShift."
- - "On the other hand a popular configuration for username+password authentication is one utilizing HTTP Basic
- Auth, which does not involve any additional login/logout steps (instead login credentials can be attached
- to each and every API call performed) and as such is handled directly by the C(k8s) module (and other
- resource–specific modules) by utilizing the C(host), C(username) and C(password) parameters. Please
- consult your preferred module's documentation for more details."
-
-options:
- state:
- description:
- - If set to I(present) connect to the API server using the URL specified in C(host) and attempt to log in.
- - If set to I(absent) attempt to log out by revoking the authentication token specified in C(api_key).
- default: present
- choices:
- - present
- - absent
- host:
- description:
- - Provide a URL for accessing the API server.
- required: true
- username:
- description:
- - Provide a username for authenticating with the API server.
- password:
- description:
- - Provide a password for authenticating with the API server.
- ca_cert:
- description:
- - "Path to a CA certificate file used to verify connection to the API server. The full certificate chain
- must be provided to avoid certificate validation errors."
- aliases: [ ssl_ca_cert ]
- validate_certs:
- description:
- - "Whether or not to verify the API server's SSL certificates."
- type: bool
- default: true
- aliases: [ verify_ssl ]
- api_key:
- description:
- - When C(state) is set to I(absent), this specifies the token to revoke.
-
-requirements:
- - python >= 2.7
- - urllib3
- - requests
- - requests-oauthlib
-'''
-
-EXAMPLES = '''
-- hosts: localhost
- module_defaults:
- group/k8s:
- host: https://k8s.example.com/
- ca_cert: ca.pem
- tasks:
- - block:
- # It's good practice to store login credentials in a secure vault and not
- # directly in playbooks.
- - include_vars: k8s_passwords.yml
-
- - name: Log in (obtain access token)
- k8s_auth:
- username: admin
- password: "{{ k8s_admin_password }}"
- register: k8s_auth_results
-
- # Previous task provides the token/api_key, while all other parameters
- # are taken from module_defaults
- - name: Get a list of all pods from any namespace
- k8s_info:
- api_key: "{{ k8s_auth_results.k8s_auth.api_key }}"
- kind: Pod
- register: pod_list
-
- always:
- - name: If login succeeded, try to log out (revoke access token)
- when: k8s_auth_results.k8s_auth.api_key is defined
- k8s_auth:
- state: absent
- api_key: "{{ k8s_auth_results.k8s_auth.api_key }}"
-'''
-
-# Returned value names need to match k8s modules parameter names, to make it
-# easy to pass returned values of k8s_auth to other k8s modules.
-# Discussion: https://github.com/ansible/ansible/pull/50807#discussion_r248827899
-RETURN = '''
-k8s_auth:
- description: Kubernetes authentication facts.
- returned: success
- type: complex
- contains:
- api_key:
- description: Authentication token.
- returned: success
- type: str
- host:
- description: URL for accessing the API server.
- returned: success
- type: str
- ca_cert:
- description: Path to a CA certificate file used to verify connection to the API server.
- returned: success
- type: str
- validate_certs:
- description: "Whether or not to verify the API server's SSL certificates."
- returned: success
- type: bool
- username:
- description: Username for authenticating with the API server.
- returned: success
- type: str
-'''
-
-
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils.six.moves.urllib_parse import urlparse, parse_qs, urlencode
-
-# 3rd party imports
-try:
- import requests
- HAS_REQUESTS = True
-except ImportError:
- HAS_REQUESTS = False
-
-try:
- from requests_oauthlib import OAuth2Session
- HAS_REQUESTS_OAUTH = True
-except ImportError:
- HAS_REQUESTS_OAUTH = False
-
-try:
- from urllib3.util import make_headers
- HAS_URLLIB3 = True
-except ImportError:
- HAS_URLLIB3 = False
-
-
-K8S_AUTH_ARG_SPEC = {
- 'state': {
- 'default': 'present',
- 'choices': ['present', 'absent'],
- },
- 'host': {'required': True},
- 'username': {},
- 'password': {'no_log': True},
- 'ca_cert': {'type': 'path', 'aliases': ['ssl_ca_cert']},
- 'validate_certs': {
- 'type': 'bool',
- 'default': True,
- 'aliases': ['verify_ssl']
- },
- 'api_key': {'no_log': True},
-}
-
-
-class KubernetesAuthModule(AnsibleModule):
- def __init__(self):
- AnsibleModule.__init__(
- self,
- argument_spec=K8S_AUTH_ARG_SPEC,
- required_if=[
- ('state', 'present', ['username', 'password']),
- ('state', 'absent', ['api_key']),
- ]
- )
-
- if not HAS_REQUESTS:
- self.fail("This module requires the python 'requests' package. Try `pip install requests`.")
-
- if not HAS_REQUESTS_OAUTH:
- self.fail("This module requires the python 'requests-oauthlib' package. Try `pip install requests-oauthlib`.")
-
- if not HAS_URLLIB3:
- self.fail("This module requires the python 'urllib3' package. Try `pip install urllib3`.")
-
- def execute_module(self):
- state = self.params.get('state')
- verify_ssl = self.params.get('validate_certs')
- ssl_ca_cert = self.params.get('ca_cert')
-
- self.auth_username = self.params.get('username')
- self.auth_password = self.params.get('password')
- self.auth_api_key = self.params.get('api_key')
- self.con_host = self.params.get('host')
-
- # python-requests takes either a bool or a path to a ca file as the 'verify' param
- if verify_ssl and ssl_ca_cert:
- self.con_verify_ca = ssl_ca_cert # path
- else:
- self.con_verify_ca = verify_ssl # bool
-
- # Get needed info to access authorization APIs
- self.openshift_discover()
-
- if state == 'present':
- new_api_key = self.openshift_login()
- result = dict(
- host=self.con_host,
- validate_certs=verify_ssl,
- ca_cert=ssl_ca_cert,
- api_key=new_api_key,
- username=self.auth_username,
- )
- else:
- self.openshift_logout()
- result = dict()
-
- self.exit_json(changed=False, k8s_auth=result)
-
- def openshift_discover(self):
- url = '{0}/.well-known/oauth-authorization-server'.format(self.con_host)
- ret = requests.get(url, verify=self.con_verify_ca)
-
- if ret.status_code != 200:
- self.fail_request("Couldn't find OpenShift's OAuth API", method='GET', url=url,
- reason=ret.reason, status_code=ret.status_code)
-
- try:
- oauth_info = ret.json()
-
- self.openshift_auth_endpoint = oauth_info['authorization_endpoint']
- self.openshift_token_endpoint = oauth_info['token_endpoint']
- except Exception as e:
- self.fail_json(msg="Something went wrong discovering OpenShift OAuth details.",
- exception=traceback.format_exc())
-
- def openshift_login(self):
- os_oauth = OAuth2Session(client_id='openshift-challenging-client')
- authorization_url, state = os_oauth.authorization_url(self.openshift_auth_endpoint,
- state="1", code_challenge_method='S256')
- auth_headers = make_headers(basic_auth='{0}:{1}'.format(self.auth_username, self.auth_password))
-
- # Request authorization code using basic auth credentials
- ret = os_oauth.get(
- authorization_url,
- headers={'X-Csrf-Token': state, 'authorization': auth_headers.get('authorization')},
- verify=self.con_verify_ca,
- allow_redirects=False
- )
-
- if ret.status_code != 302:
- self.fail_request("Authorization failed.", method='GET', url=authorization_url,
- reason=ret.reason, status_code=ret.status_code)
-
- # In here we have `code` and `state`, I think `code` is the important one
- qwargs = {}
- for k, v in parse_qs(urlparse(ret.headers['Location']).query).items():
- qwargs[k] = v[0]
- qwargs['grant_type'] = 'authorization_code'
-
- # Using authorization code given to us in the Location header of the previous request, request a token
- ret = os_oauth.post(
- self.openshift_token_endpoint,
- headers={
- 'Accept': 'application/json',
- 'Content-Type': 'application/x-www-form-urlencoded',
- # This is just base64 encoded 'openshift-challenging-client:'
- 'Authorization': 'Basic b3BlbnNoaWZ0LWNoYWxsZW5naW5nLWNsaWVudDo='
- },
- data=urlencode(qwargs),
- verify=self.con_verify_ca
- )
-
- if ret.status_code != 200:
- self.fail_request("Failed to obtain an authorization token.", method='POST',
- url=self.openshift_token_endpoint,
- reason=ret.reason, status_code=ret.status_code)
-
- return ret.json()['access_token']
-
- def openshift_logout(self):
- url = '{0}/apis/oauth.openshift.io/v1/oauthaccesstokens/{1}'.format(self.con_host, self.auth_api_key)
- headers = {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json',
- 'Authorization': 'Bearer {0}'.format(self.auth_api_key)
- }
- json = {
- "apiVersion": "oauth.openshift.io/v1",
- "kind": "DeleteOptions"
- }
-
- ret = requests.delete(url, headers=headers, json=json, verify=self.con_verify_ca)
- # Ignore errors, the token will time out eventually anyway
-
- def fail(self, msg=None):
- self.fail_json(msg=msg)
-
- def fail_request(self, msg, **kwargs):
- req_info = {}
- for k, v in kwargs.items():
- req_info['req_' + k] = v
- self.fail_json(msg=msg, **req_info)
-
-
-def main():
- module = KubernetesAuthModule()
- try:
- module.execute_module()
- except Exception as e:
- module.fail_json(msg=str(e), exception=traceback.format_exc())
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/clustering/k8s/k8s_info.py b/lib/ansible/modules/clustering/k8s/k8s_info.py
deleted file mode 100644
index 99a8fd8cec..0000000000
--- a/lib/ansible/modules/clustering/k8s/k8s_info.py
+++ /dev/null
@@ -1,179 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2018, Will Thames <@willthames>
-# 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
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = '''
-module: k8s_info
-
-short_description: Describe Kubernetes (K8s) objects
-
-version_added: "2.7"
-
-author:
- - "Will Thames (@willthames)"
-
-description:
- - Use the OpenShift Python client to perform read operations on K8s objects.
- - Access to the full range of K8s APIs.
- - Authenticate using either a config file, certificates, password or token.
- - Supports check mode.
- - This module was called C(k8s_facts) before Ansible 2.9. The usage did not change.
-
-options:
- api_version:
- description:
- - Use to specify the API version. in conjunction with I(kind), I(name), and I(namespace) to identify a
- specific object.
- default: v1
- aliases:
- - api
- - version
- kind:
- description:
- - Use to specify an object model. Use in conjunction with I(api_version), I(name), and I(namespace) to identify a
- specific object.
- required: yes
- name:
- description:
- - Use to specify an object name. Use in conjunction with I(api_version), I(kind) and I(namespace) to identify a
- specific object.
- namespace:
- description:
- - Use to specify an object namespace. Use in conjunction with I(api_version), I(kind), and I(name)
- to identify a specific object.
- label_selectors:
- description: List of label selectors to use to filter results
- field_selectors:
- description: List of field selectors to use to filter results
-
-extends_documentation_fragment:
- - k8s_auth_options
-
-requirements:
- - "python >= 2.7"
- - "openshift >= 0.6"
- - "PyYAML >= 3.11"
-'''
-
-EXAMPLES = '''
-- name: Get an existing Service object
- k8s_info:
- api_version: v1
- kind: Service
- name: web
- namespace: testing
- register: web_service
-
-- name: Get a list of all service objects
- k8s_info:
- api_version: v1
- kind: Service
- namespace: testing
- register: service_list
-
-- name: Get a list of all pods from any namespace
- k8s_info:
- kind: Pod
- register: pod_list
-
-- name: Search for all Pods labelled app=web
- k8s_info:
- kind: Pod
- label_selectors:
- - app = web
- - tier in (dev, test)
-
-- name: Search for all running pods
- k8s_info:
- kind: Pod
- field_selectors:
- - status.phase=Running
-'''
-
-RETURN = '''
-resources:
- description:
- - The object(s) that exists
- returned: success
- type: complex
- contains:
- api_version:
- description: The versioned schema of this representation of an object.
- returned: success
- type: str
- kind:
- description: Represents the REST resource this object represents.
- returned: success
- type: str
- metadata:
- description: Standard object metadata. Includes name, namespace, annotations, labels, etc.
- returned: success
- type: dict
- spec:
- description: Specific attributes of the object. Will vary based on the I(api_version) and I(kind).
- returned: success
- type: dict
- status:
- description: Current status details for the object.
- returned: success
- type: dict
-'''
-
-
-from ansible.module_utils.k8s.common import KubernetesAnsibleModule, AUTH_ARG_SPEC
-import copy
-
-
-class KubernetesInfoModule(KubernetesAnsibleModule):
-
- def __init__(self, *args, **kwargs):
- KubernetesAnsibleModule.__init__(self, *args,
- supports_check_mode=True,
- **kwargs)
- if self._name == 'k8s_facts':
- self.deprecate("The 'k8s_facts' module has been renamed to 'k8s_info'", version='2.13')
-
- def execute_module(self):
- self.client = self.get_api_client()
-
- self.exit_json(changed=False,
- **self.kubernetes_facts(self.params['kind'],
- self.params['api_version'],
- self.params['name'],
- self.params['namespace'],
- self.params['label_selectors'],
- self.params['field_selectors']))
-
- @property
- def argspec(self):
- args = copy.deepcopy(AUTH_ARG_SPEC)
- args.update(
- dict(
- kind=dict(required=True),
- api_version=dict(default='v1', aliases=['api', 'version']),
- name=dict(),
- namespace=dict(),
- label_selectors=dict(type='list', default=[]),
- field_selectors=dict(type='list', default=[]),
- )
- )
- return args
-
-
-def main():
- KubernetesInfoModule().execute_module()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/clustering/k8s/k8s_scale.py b/lib/ansible/modules/clustering/k8s/k8s_scale.py
deleted file mode 100644
index 2afb0f959f..0000000000
--- a/lib/ansible/modules/clustering/k8s/k8s_scale.py
+++ /dev/null
@@ -1,129 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2018, Chris Houseknecht <@chouseknecht>
-# 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
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = '''
-
-module: k8s_scale
-
-short_description: Set a new size for a Deployment, ReplicaSet, Replication Controller, or Job.
-
-version_added: "2.5"
-
-author:
- - "Chris Houseknecht (@chouseknecht)"
- - "Fabian von Feilitzsch (@fabianvf)"
-
-description:
- - Similar to the kubectl scale command. Use to set the number of replicas for a Deployment, ReplicaSet,
- or Replication Controller, or the parallelism attribute of a Job. Supports check mode.
-
-extends_documentation_fragment:
- - k8s_name_options
- - k8s_auth_options
- - k8s_resource_options
- - k8s_scale_options
-
-requirements:
- - "python >= 2.7"
- - "openshift >= 0.6"
- - "PyYAML >= 3.11"
-'''
-
-EXAMPLES = '''
-- name: Scale deployment up, and extend timeout
- k8s_scale:
- api_version: v1
- kind: Deployment
- name: elastic
- namespace: myproject
- replicas: 3
- wait_timeout: 60
-
-- name: Scale deployment down when current replicas match
- k8s_scale:
- api_version: v1
- kind: Deployment
- name: elastic
- namespace: myproject
- current_replicas: 3
- replicas: 2
-
-- name: Increase job parallelism
- k8s_scale:
- api_version: batch/v1
- kind: job
- name: pi-with-timeout
- namespace: testing
- replicas: 2
-
-# Match object using local file or inline definition
-
-- name: Scale deployment based on a file from the local filesystem
- k8s_scale:
- src: /myproject/elastic_deployment.yml
- replicas: 3
- wait: no
-
-- name: Scale deployment based on a template output
- k8s_scale:
- resource_definition: "{{ lookup('template', '/myproject/elastic_deployment.yml') | from_yaml }}"
- replicas: 3
- wait: no
-
-- name: Scale deployment based on a file from the Ansible controller filesystem
- k8s_scale:
- resource_definition: "{{ lookup('file', '/myproject/elastic_deployment.yml') | from_yaml }}"
- replicas: 3
- wait: no
-'''
-
-RETURN = '''
-result:
- description:
- - If a change was made, will return the patched object, otherwise returns the existing object.
- returned: success
- type: complex
- contains:
- api_version:
- description: The versioned schema of this representation of an object.
- returned: success
- type: str
- kind:
- description: Represents the REST resource this object represents.
- returned: success
- type: str
- metadata:
- description: Standard object metadata. Includes name, namespace, annotations, labels, etc.
- returned: success
- type: complex
- spec:
- description: Specific attributes of the object. Will vary based on the I(api_version) and I(kind).
- returned: success
- type: complex
- status:
- description: Current status details for the object.
- returned: success
- type: complex
-'''
-
-from ansible.module_utils.k8s.scale import KubernetesAnsibleScaleModule
-
-
-def main():
- KubernetesAnsibleScaleModule().execute_module()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/clustering/k8s/k8s_service.py b/lib/ansible/modules/clustering/k8s/k8s_service.py
deleted file mode 100644
index 82a898283c..0000000000
--- a/lib/ansible/modules/clustering/k8s/k8s_service.py
+++ /dev/null
@@ -1,267 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2018, KubeVirt Team <@kubevirt>
-# 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
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = '''
-
-module: k8s_service
-
-short_description: Manage Services on Kubernetes
-
-version_added: "2.8"
-
-author: KubeVirt Team (@kubevirt)
-
-description:
- - Use Openshift Python SDK to manage Services on Kubernetes
-
-extends_documentation_fragment:
- - k8s_auth_options
-
-options:
- resource_definition:
- description:
- - A partial YAML definition of the Service object being created/updated. Here you can define Kubernetes
- Service Resource parameters not covered by this module's parameters.
- - "NOTE: I(resource_definition) has lower priority than module parameters. If you try to define e.g.
- I(metadata.namespace) here, that value will be ignored and I(metadata) used instead."
- aliases:
- - definition
- - inline
- type: dict
- state:
- description:
- - Determines if an object should be created, patched, or deleted. When set to C(present), an object will be
- created, if it does not already exist. If set to C(absent), an existing object will be deleted. If set to
- C(present), an existing object will be patched, if its attributes differ from those specified using
- module options and I(resource_definition).
- default: present
- choices:
- - present
- - absent
- force:
- description:
- - If set to C(True), and I(state) is C(present), an existing object will be replaced.
- default: false
- type: bool
- merge_type:
- description:
- - Whether to override the default patch merge approach with a specific type. By default, the strategic
- merge will typically be used.
- - For example, Custom Resource Definitions typically aren't updatable by the usual strategic merge. You may
- want to use C(merge) if you see "strategic merge patch format is not supported"
- - See U(https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment)
- - Requires openshift >= 0.6.2
- - If more than one merge_type is given, the merge_types will be tried in order
- - If openshift >= 0.6.2, this defaults to C(['strategic-merge', 'merge']), which is ideal for using the same parameters
- on resource kinds that combine Custom Resources and built-in resources. For openshift < 0.6.2, the default
- is simply C(strategic-merge).
- choices:
- - json
- - merge
- - strategic-merge
- type: list
- name:
- description:
- - Use to specify a Service object name.
- required: true
- type: str
- namespace:
- description:
- - Use to specify a Service object namespace.
- required: true
- type: str
- type:
- description:
- - Specifies the type of Service to create.
- - See U(https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types)
- choices:
- - NodePort
- - ClusterIP
- - LoadBalancer
- - ExternalName
- ports:
- description:
- - A list of ports to expose.
- - U(https://kubernetes.io/docs/concepts/services-networking/service/#multi-port-services)
- type: list
- selector:
- description:
- - Label selectors identify objects this Service should apply to.
- - U(https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/)
- type: dict
-
-requirements:
- - python >= 2.7
- - openshift >= 0.6.2
-'''
-
-EXAMPLES = '''
-- name: Expose https port with ClusterIP
- k8s_service:
- state: present
- name: test-https
- namespace: default
- ports:
- - port: 443
- protocol: TCP
- selector:
- key: special
-
-- name: Expose https port with ClusterIP using spec
- k8s_service:
- state: present
- name: test-https
- namespace: default
- inline:
- spec:
- ports:
- - port: 443
- protocol: TCP
- selector:
- key: special
-'''
-
-RETURN = '''
-result:
- description:
- - The created, patched, or otherwise present Service object. Will be empty in the case of a deletion.
- returned: success
- type: complex
- contains:
- api_version:
- description: The versioned schema of this representation of an object.
- returned: success
- type: str
- kind:
- description: Always 'Service'.
- returned: success
- type: str
- metadata:
- description: Standard object metadata. Includes name, namespace, annotations, labels, etc.
- returned: success
- type: complex
- spec:
- description: Specific attributes of the object. Will vary based on the I(api_version) and I(kind).
- returned: success
- type: complex
- status:
- description: Current status details for the object.
- returned: success
- type: complex
-'''
-
-import copy
-import traceback
-
-from collections import defaultdict
-
-from ansible.module_utils.k8s.common import AUTH_ARG_SPEC, COMMON_ARG_SPEC
-from ansible.module_utils.k8s.raw import KubernetesRawModule
-
-
-SERVICE_ARG_SPEC = {
- 'state': {
- 'default': 'present',
- 'choices': ['present', 'absent'],
- },
- 'force': {
- 'type': 'bool',
- 'default': False,
- },
- 'resource_definition': {
- 'type': 'dict',
- 'aliases': ['definition', 'inline']
- },
- 'name': {'required': True},
- 'namespace': {'required': True},
- 'merge_type': {'type': 'list', 'choices': ['json', 'merge', 'strategic-merge']},
- 'selector': {'type': 'dict'},
- 'type': {
- 'type': 'str',
- 'choices': [
- 'NodePort', 'ClusterIP', 'LoadBalancer', 'ExternalName'
- ],
- },
- 'ports': {'type': 'list'},
-}
-
-
-class KubernetesService(KubernetesRawModule):
- def __init__(self, *args, **kwargs):
- super(KubernetesService, self).__init__(*args, k8s_kind='Service', **kwargs)
-
- @staticmethod
- def merge_dicts(x, y):
- for k in set(x.keys()).union(y.keys()):
- if k in x and k in y:
- if isinstance(x[k], dict) and isinstance(y[k], dict):
- yield (k, dict(KubernetesService.merge_dicts(x[k], y[k])))
- else:
- yield (k, y[k])
- elif k in x:
- yield (k, x[k])
- else:
- yield (k, y[k])
-
- @property
- def argspec(self):
- """ argspec property builder """
- argument_spec = copy.deepcopy(AUTH_ARG_SPEC)
- argument_spec.update(SERVICE_ARG_SPEC)
- return argument_spec
-
- def execute_module(self):
- """ Module execution """
- self.client = self.get_api_client()
-
- api_version = 'v1'
- selector = self.params.get('selector')
- service_type = self.params.get('type')
- ports = self.params.get('ports')
-
- definition = defaultdict(defaultdict)
-
- definition['kind'] = 'Service'
- definition['apiVersion'] = api_version
-
- def_spec = definition['spec']
- def_spec['type'] = service_type
- def_spec['ports'] = ports
- def_spec['selector'] = selector
-
- def_meta = definition['metadata']
- def_meta['name'] = self.params.get('name')
- def_meta['namespace'] = self.params.get('namespace')
-
- # 'resource_definition:' has lower priority than module parameters
- definition = dict(self.merge_dicts(self.resource_definitions[0], definition))
-
- resource = self.find_resource('Service', api_version, fail=True)
- definition = self.set_defaults(resource, definition)
- result = self.perform_action(resource, definition)
-
- self.exit_json(**result)
-
-
-def main():
- module = KubernetesService()
- try:
- module.execute_module()
- except Exception as e:
- module.fail_json(msg=str(e), exception=traceback.format_exc())
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/clustering/openshift/_openshift_raw.py b/lib/ansible/modules/clustering/openshift/_openshift_raw.py
deleted file mode 120000
index 53f9af49a8..0000000000
--- a/lib/ansible/modules/clustering/openshift/_openshift_raw.py
+++ /dev/null
@@ -1 +0,0 @@
-../k8s/k8s.py \ No newline at end of file
diff --git a/lib/ansible/modules/clustering/openshift/_openshift_scale.py b/lib/ansible/modules/clustering/openshift/_openshift_scale.py
deleted file mode 120000
index bfa5417ea0..0000000000
--- a/lib/ansible/modules/clustering/openshift/_openshift_scale.py
+++ /dev/null
@@ -1 +0,0 @@
-../k8s/k8s_scale.py \ No newline at end of file
diff --git a/lib/ansible/plugins/doc_fragments/k8s_auth_options.py b/lib/ansible/plugins/doc_fragments/k8s_auth_options.py
deleted file mode 100644
index 186202e9f5..0000000000
--- a/lib/ansible/plugins/doc_fragments/k8s_auth_options.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright: (c) 2018, Red Hat | Ansible
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-# Options for authenticating with the API.
-
-
-class ModuleDocFragment(object):
-
- DOCUMENTATION = r'''
-options:
- host:
- description:
- - Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.
- type: str
- api_key:
- description:
- - Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment variable.
- type: str
- kubeconfig:
- description:
- - Path to an existing Kubernetes config file. If not provided, and no other connection
- options are provided, the openshift client will attempt to load the default
- configuration file from I(~/.kube/config.json). Can also be specified via K8S_AUTH_KUBECONFIG environment
- variable.
- type: path
- context:
- description:
- - The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment variable.
- type: str
- username:
- description:
- - Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME environment
- variable.
- - Please note that this only works with clusters configured to use HTTP Basic Auth. If your cluster has a
- different form of authentication (e.g. OAuth2 in OpenShift), this option will not work as expected and you
- should look into the C(k8s_auth) module, as that might do what you need.
- type: str
- password:
- description:
- - Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD environment
- variable.
- - Please read the description of the C(username) option for a discussion of when this option is applicable.
- type: str
- client_cert:
- description:
- - Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE environment
- variable.
- type: path
- aliases: [ cert_file ]
- client_key:
- description:
- - Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE environment
- variable.
- type: path
- aliases: [ key_file ]
- 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 K8S_AUTH_SSL_CA_CERT environment variable.
- type: path
- aliases: [ ssl_ca_cert ]
- validate_certs:
- description:
- - Whether or not to verify the API server's SSL certificates. Can also be specified via K8S_AUTH_VERIFY_SSL
- environment variable.
- type: bool
- aliases: [ verify_ssl ]
- proxy:
- description:
- - The URL of an HTTP proxy to use for the connection. Can also be specified via K8S_AUTH_PROXY environment variable.
- - Please note that this module does not pick up typical proxy settings from the environment (e.g. HTTP_PROXY).
- version_added: "2.9"
- persist_config:
- description:
- - Whether or not to save the kube config refresh tokens.
- Can also be specified via K8S_AUTH_PERSIST_CONFIG environment variable.
- - When the k8s context is using a user credentials with refresh tokens (like oidc or gke/gcloud auth),
- the token is refreshed by the k8s python client library but not saved by default. So the old refresh token can
- expire and the next auth might fail. Setting this flag to true will tell the k8s python client to save the
- new refresh token to the kube config file.
- - Default to false.
- - Please note that the current version of the k8s python client library does not support setting this flag to True yet.
- - "The fix for this k8s python library is here: https://github.com/kubernetes-client/python-base/pull/169"
- type: bool
- version_added: "2.10"
-notes:
- - "The OpenShift Python client wraps the K8s Python client, providing full access to
- all of the APIS and models available on both platforms. For API version details and
- additional information visit https://github.com/openshift/openshift-restclient-python"
- - "To avoid SSL certificate validation errors when C(validate_certs) is I(True), the full
- certificate chain for the API server must be provided via C(ca_cert) or in the
- kubeconfig file."
-'''
diff --git a/lib/ansible/plugins/doc_fragments/k8s_name_options.py b/lib/ansible/plugins/doc_fragments/k8s_name_options.py
deleted file mode 100644
index 74375cc17a..0000000000
--- a/lib/ansible/plugins/doc_fragments/k8s_name_options.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright: (c) 2018, Red Hat | Ansible
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-# Options for selecting or identifying a specific K8s object
-
-
-class ModuleDocFragment(object):
-
- DOCUMENTATION = r'''
-options:
- api_version:
- description:
- - Use to specify the API version. Use to create, delete, or discover an object without providing a full
- resource definition. Use in conjunction with I(kind), I(name), and I(namespace) to identify a
- specific object. If I(resource definition) is provided, the I(apiVersion) from the I(resource_definition)
- will override this option.
- type: str
- default: v1
- aliases:
- - api
- - version
- kind:
- description:
- - Use to specify an object model. Use to create, delete, or discover an object without providing a full
- resource definition. Use in conjunction with I(api_version), I(name), and I(namespace) to identify a
- specific object. If I(resource definition) is provided, the I(kind) from the I(resource_definition)
- will override this option.
- type: str
- name:
- description:
- - Use to specify an object name. Use to create, delete, or discover an object without providing a full
- resource definition. Use in conjunction with I(api_version), I(kind) and I(namespace) to identify a
- specific object. If I(resource definition) is provided, the I(metadata.name) value from the
- I(resource_definition) will override this option.
- type: str
- namespace:
- description:
- - Use to specify an object namespace. Useful when creating, deleting, or discovering an object without
- providing a full resource definition. Use in conjunction with I(api_version), I(kind), and I(name)
- to identify a specfic object. If I(resource definition) is provided, the I(metadata.namespace) value
- from the I(resource_definition) will override this option.
- type: str
-'''
diff --git a/lib/ansible/plugins/doc_fragments/k8s_resource_options.py b/lib/ansible/plugins/doc_fragments/k8s_resource_options.py
deleted file mode 100644
index 6e35a8ea3f..0000000000
--- a/lib/ansible/plugins/doc_fragments/k8s_resource_options.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright: (c) 2018, Red Hat | Ansible
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-# Options for providing an object configuration
-
-
-class ModuleDocFragment(object):
-
- DOCUMENTATION = r'''
-options:
- resource_definition:
- description:
- - "Provide a valid YAML definition (either as a string, list, or dict) for an object when creating or updating. NOTE: I(kind), I(api_version), I(name),
- and I(namespace) will be overwritten by corresponding values found in the provided I(resource_definition)."
- aliases:
- - definition
- - inline
- src:
- description:
- - "Provide a path to a file containing a valid YAML definition of an object or objects to be created or updated. Mutually
- exclusive with I(resource_definition). NOTE: I(kind), I(api_version), I(name), and I(namespace) will be
- overwritten by corresponding values found in the configuration read in from the I(src) file."
- - Reads from the local file system. To read from the Ansible controller's file system, including vaulted files, use the file lookup
- plugin or template lookup plugin, combined with the from_yaml filter, and pass the result to
- I(resource_definition). See Examples below.
- type: path
-'''
diff --git a/lib/ansible/plugins/doc_fragments/k8s_scale_options.py b/lib/ansible/plugins/doc_fragments/k8s_scale_options.py
deleted file mode 100644
index 11b156c1fc..0000000000
--- a/lib/ansible/plugins/doc_fragments/k8s_scale_options.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright: (c) 2018, Red Hat | Ansible
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-# Options used by scale modules.
-
-
-class ModuleDocFragment(object):
-
- DOCUMENTATION = r'''
-options:
- replicas:
- description:
- - The desired number of replicas.
- type: int
- current_replicas:
- description:
- - For Deployment, ReplicaSet, Replication Controller, only scale, if the number of existing replicas
- matches. In the case of a Job, update parallelism only if the current parallelism value matches.
- type: int
- resource_version:
- description:
- - Only attempt to scale, if the current object version matches.
- type: str
- wait:
- description:
- - For Deployment, ReplicaSet, Replication Controller, wait for the status value of I(ready_replicas) to change
- to the number of I(replicas). In the case of a Job, this option is ignored.
- type: bool
- default: yes
- wait_timeout:
- description:
- - When C(wait) is I(True), the number of seconds to wait for the I(ready_replicas) status to equal I(replicas).
- If the status is not reached within the allotted time, an error will result. In the case of a Job, this option
- is ignored.
- type: int
- default: 20
-'''
diff --git a/lib/ansible/plugins/doc_fragments/k8s_state_options.py b/lib/ansible/plugins/doc_fragments/k8s_state_options.py
deleted file mode 100644
index 724962068d..0000000000
--- a/lib/ansible/plugins/doc_fragments/k8s_state_options.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright: (c) 2018, Red Hat | Ansible
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-# Options for specifying object state
-
-
-class ModuleDocFragment(object):
-
- DOCUMENTATION = r'''
-options:
- state:
- description:
- - Determines if an object should be created, patched, or deleted. When set to C(present), an object will be
- created, if it does not already exist. If set to C(absent), an existing object will be deleted. If set to
- C(present), an existing object will be patched, if its attributes differ from those specified using
- I(resource_definition) or I(src).
- type: str
- default: present
- choices: [ absent, present ]
- force:
- description:
- - If set to C(yes), and I(state) is C(present), an existing object will be replaced.
- type: bool
- default: no
-'''
diff --git a/lib/ansible/plugins/filter/k8s.py b/lib/ansible/plugins/filter/k8s.py
deleted file mode 100644
index f6cb057985..0000000000
--- a/lib/ansible/plugins/filter/k8s.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright (c) 2017 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
-
-
-ANSIBLE_METADATA = {
- 'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'
-}
-
-
-try:
- from openshift.helper.hashes import generate_hash
- HAS_GENERATE_HASH = True
-except ImportError:
- HAS_GENERATE_HASH = False
-
-from ansible.errors import AnsibleFilterError
-
-
-def k8s_config_resource_name(resource):
- if not HAS_GENERATE_HASH:
- raise AnsibleFilterError("k8s_config_resource_name requires openshift>=0.7.2")
- try:
- return resource['metadata']['name'] + '-' + generate_hash(resource)
- except KeyError:
- raise AnsibleFilterError("resource must have a metadata.name key to generate a resource name")
-
-
-# ---- Ansible filters ----
-class FilterModule(object):
-
- def filters(self):
- return {
- 'k8s_config_resource_name': k8s_config_resource_name
- }
diff --git a/lib/ansible/plugins/inventory/k8s.py b/lib/ansible/plugins/inventory/k8s.py
deleted file mode 100644
index 9ea955410d..0000000000
--- a/lib/ansible/plugins/inventory/k8s.py
+++ /dev/null
@@ -1,360 +0,0 @@
-# Copyright (c) 2018 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
-
-DOCUMENTATION = '''
- name: k8s
- plugin_type: inventory
- author:
- - Chris Houseknecht <@chouseknecht>
- - Fabian von Feilitzsch <@fabianvf>
-
- short_description: Kubernetes (K8s) inventory source
-
- description:
- - Fetch containers and services for one or more clusters
- - Groups by cluster name, namespace, namespace_services, namespace_pods, and labels
- - Uses k8s.(yml|yaml) YAML configuration file to set parameter values.
-
- options:
- plugin:
- description: token that ensures this is a source file for the 'k8s' plugin.
- required: True
- choices: ['k8s']
- connections:
- description:
- - Optional list of cluster connection settings. If no connections are provided, the default
- I(~/.kube/config) and active context will be used, and objects will be returned for all namespaces
- the active user is authorized to access.
- name:
- description:
- - Optional name to assign to the cluster. If not provided, a name is constructed from the server
- and port.
- kubeconfig:
- description:
- - Path to an existing Kubernetes config file. If not provided, and no other connection
- options are provided, the OpenShift client will attempt to load the default
- configuration file from I(~/.kube/config.json). Can also be specified via K8S_AUTH_KUBECONFIG
- environment variable.
- context:
- description:
- - The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment
- variable.
- host:
- description:
- - Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.
- api_key:
- description:
- - Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment
- variable.
- username:
- description:
- - Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME
- environment variable.
- password:
- description:
- - Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD
- environment variable.
- client_cert:
- description:
- - Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE
- environment variable.
- aliases: [ cert_file ]
- client_key:
- description:
- - Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE
- environment variable.
- aliases: [ key_file ]
- ca_cert:
- description:
- - Path to a CA certificate used to authenticate with the API. Can also be specified via
- K8S_AUTH_SSL_CA_CERT environment variable.
- aliases: [ ssl_ca_cert ]
- validate_certs:
- description:
- - "Whether or not to verify the API server's SSL certificates. Can also be specified via
- K8S_AUTH_VERIFY_SSL environment variable."
- type: bool
- aliases: [ verify_ssl ]
- namespaces:
- description:
- - List of namespaces. If not specified, will fetch all containers for all namespaces user is authorized
- to access.
-
- requirements:
- - "python >= 2.7"
- - "openshift >= 0.6"
- - "PyYAML >= 3.11"
-'''
-
-EXAMPLES = '''
-# File must be named k8s.yaml or k8s.yml
-
-# Authenticate with token, and return all pods and services for all namespaces
-plugin: k8s
-connections:
- - host: https://192.168.64.4:8443
- token: xxxxxxxxxxxxxxxx
- validate_certs: false
-
-# Use default config (~/.kube/config) file and active context, and return objects for a specific namespace
-plugin: k8s
-connections:
- - namespaces:
- - testing
-
-# Use a custom config file, and a specific context.
-plugin: k8s
-connections:
- - kubeconfig: /path/to/config
- context: 'awx/192-168-64-4:8443/developer'
-'''
-
-import json
-
-from ansible.errors import AnsibleError
-from ansible.module_utils.k8s.common import K8sAnsibleMixin, HAS_K8S_MODULE_HELPER, k8s_import_exception
-from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
-
-try:
- from openshift.dynamic.exceptions import DynamicApiError
-except ImportError:
- pass
-
-
-def format_dynamic_api_exc(exc):
- if exc.body:
- if exc.headers and exc.headers.get('Content-Type') == 'application/json':
- message = json.loads(exc.body).get('message')
- if message:
- return message
- return exc.body
- else:
- return '%s Reason: %s' % (exc.status, exc.reason)
-
-
-class K8sInventoryException(Exception):
- pass
-
-
-class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable, K8sAnsibleMixin):
- NAME = 'k8s'
-
- transport = 'kubectl'
-
- def parse(self, inventory, loader, path, cache=True):
- super(InventoryModule, self).parse(inventory, loader, path)
- cache_key = self._get_cache_prefix(path)
- config_data = self._read_config_data(path)
- self.setup(config_data, cache, cache_key)
-
- def setup(self, config_data, cache, cache_key):
- connections = config_data.get('connections')
-
- if not HAS_K8S_MODULE_HELPER:
- raise K8sInventoryException(
- "This module requires the OpenShift Python client. Try `pip install openshift`. Detail: {0}".format(k8s_import_exception)
- )
-
- source_data = None
- if cache and cache_key in self._cache:
- try:
- source_data = self._cache[cache_key]
- except KeyError:
- pass
-
- if not source_data:
- self.fetch_objects(connections)
-
- def fetch_objects(self, connections):
-
- if connections:
- if not isinstance(connections, list):
- raise K8sInventoryException("Expecting connections to be a list.")
-
- for connection in connections:
- if not isinstance(connection, dict):
- raise K8sInventoryException("Expecting connection to be a dictionary.")
- client = self.get_api_client(**connection)
- name = connection.get('name', self.get_default_host_name(client.configuration.host))
- if connection.get('namespaces'):
- namespaces = connection['namespaces']
- else:
- namespaces = self.get_available_namespaces(client)
- for namespace in namespaces:
- self.get_pods_for_namespace(client, name, namespace)
- self.get_services_for_namespace(client, name, namespace)
- else:
- client = self.get_api_client()
- name = self.get_default_host_name(client.configuration.host)
- namespaces = self.get_available_namespaces(client)
- for namespace in namespaces:
- self.get_pods_for_namespace(client, name, namespace)
- self.get_services_for_namespace(client, name, namespace)
-
- @staticmethod
- def get_default_host_name(host):
- return host.replace('https://', '').replace('http://', '').replace('.', '-').replace(':', '_')
-
- def get_available_namespaces(self, client):
- v1_namespace = client.resources.get(api_version='v1', kind='Namespace')
- try:
- obj = v1_namespace.get()
- except DynamicApiError as exc:
- self.display.debug(exc)
- raise K8sInventoryException('Error fetching Namespace list: %s' % format_dynamic_api_exc(exc))
- return [namespace.metadata.name for namespace in obj.items]
-
- def get_pods_for_namespace(self, client, name, namespace):
- v1_pod = client.resources.get(api_version='v1', kind='Pod')
- try:
- obj = v1_pod.get(namespace=namespace)
- except DynamicApiError as exc:
- self.display.debug(exc)
- raise K8sInventoryException('Error fetching Pod list: %s' % format_dynamic_api_exc(exc))
-
- namespace_group = 'namespace_{0}'.format(namespace)
- namespace_pods_group = '{0}_pods'.format(namespace_group)
-
- self.inventory.add_group(name)
- self.inventory.add_group(namespace_group)
- self.inventory.add_child(name, namespace_group)
- self.inventory.add_group(namespace_pods_group)
- self.inventory.add_child(namespace_group, namespace_pods_group)
-
- for pod in obj.items:
- pod_name = pod.metadata.name
- pod_groups = []
- pod_annotations = {} if not pod.metadata.annotations else dict(pod.metadata.annotations)
-
- if pod.metadata.labels:
- # create a group for each label_value
- for key, value in pod.metadata.labels:
- group_name = 'label_{0}_{1}'.format(key, value)
- if group_name not in pod_groups:
- pod_groups.append(group_name)
- self.inventory.add_group(group_name)
- pod_labels = dict(pod.metadata.labels)
- else:
- pod_labels = {}
-
- if not pod.status.containerStatuses:
- continue
-
- for container in pod.status.containerStatuses:
- # add each pod_container to the namespace group, and to each label_value group
- container_name = '{0}_{1}'.format(pod.metadata.name, container.name)
- self.inventory.add_host(container_name)
- self.inventory.add_child(namespace_pods_group, container_name)
- if pod_groups:
- for group in pod_groups:
- self.inventory.add_child(group, container_name)
-
- # Add hostvars
- self.inventory.set_variable(container_name, 'object_type', 'pod')
- self.inventory.set_variable(container_name, 'labels', pod_labels)
- self.inventory.set_variable(container_name, 'annotations', pod_annotations)
- self.inventory.set_variable(container_name, 'cluster_name', pod.metadata.clusterName)
- self.inventory.set_variable(container_name, 'pod_node_name', pod.spec.nodeName)
- self.inventory.set_variable(container_name, 'pod_name', pod.spec.name)
- self.inventory.set_variable(container_name, 'pod_host_ip', pod.status.hostIP)
- self.inventory.set_variable(container_name, 'pod_phase', pod.status.phase)
- self.inventory.set_variable(container_name, 'pod_ip', pod.status.podIP)
- self.inventory.set_variable(container_name, 'pod_self_link', pod.metadata.selfLink)
- self.inventory.set_variable(container_name, 'pod_resource_version', pod.metadata.resourceVersion)
- self.inventory.set_variable(container_name, 'pod_uid', pod.metadata.uid)
- self.inventory.set_variable(container_name, 'container_name', container.image)
- self.inventory.set_variable(container_name, 'container_image', container.image)
- if container.state.running:
- self.inventory.set_variable(container_name, 'container_state', 'Running')
- if container.state.terminated:
- self.inventory.set_variable(container_name, 'container_state', 'Terminated')
- if container.state.waiting:
- self.inventory.set_variable(container_name, 'container_state', 'Waiting')
- self.inventory.set_variable(container_name, 'container_ready', container.ready)
- self.inventory.set_variable(container_name, 'ansible_remote_tmp', '/tmp/')
- self.inventory.set_variable(container_name, 'ansible_connection', self.transport)
- self.inventory.set_variable(container_name, 'ansible_{0}_pod'.format(self.transport),
- pod_name)
- self.inventory.set_variable(container_name, 'ansible_{0}_container'.format(self.transport),
- container.name)
- self.inventory.set_variable(container_name, 'ansible_{0}_namespace'.format(self.transport),
- namespace)
-
- def get_services_for_namespace(self, client, name, namespace):
- v1_service = client.resources.get(api_version='v1', kind='Service')
- try:
- obj = v1_service.get(namespace=namespace)
- except DynamicApiError as exc:
- self.display.debug(exc)
- raise K8sInventoryException('Error fetching Service list: %s' % format_dynamic_api_exc(exc))
-
- namespace_group = 'namespace_{0}'.format(namespace)
- namespace_services_group = '{0}_services'.format(namespace_group)
-
- self.inventory.add_group(name)
- self.inventory.add_group(namespace_group)
- self.inventory.add_child(name, namespace_group)
- self.inventory.add_group(namespace_services_group)
- self.inventory.add_child(namespace_group, namespace_services_group)
-
- for service in obj.items:
- service_name = service.metadata.name
- service_labels = {} if not service.metadata.labels else dict(service.metadata.labels)
- service_annotations = {} if not service.metadata.annotations else dict(service.metadata.annotations)
-
- self.inventory.add_host(service_name)
-
- if service.metadata.labels:
- # create a group for each label_value
- for key, value in service.metadata.labels:
- group_name = 'label_{0}_{1}'.format(key, value)
- self.inventory.add_group(group_name)
- self.inventory.add_child(group_name, service_name)
-
- try:
- self.inventory.add_child(namespace_services_group, service_name)
- except AnsibleError as e:
- raise
-
- ports = [{'name': port.name,
- 'port': port.port,
- 'protocol': port.protocol,
- 'targetPort': port.targetPort,
- 'nodePort': port.nodePort} for port in service.spec.ports or []]
-
- # add hostvars
- self.inventory.set_variable(service_name, 'object_type', 'service')
- self.inventory.set_variable(service_name, 'labels', service_labels)
- self.inventory.set_variable(service_name, 'annotations', service_annotations)
- self.inventory.set_variable(service_name, 'cluster_name', service.metadata.clusterName)
- self.inventory.set_variable(service_name, 'ports', ports)
- self.inventory.set_variable(service_name, 'type', service.spec.type)
- self.inventory.set_variable(service_name, 'self_link', service.metadata.selfLink)
- self.inventory.set_variable(service_name, 'resource_version', service.metadata.resourceVersion)
- self.inventory.set_variable(service_name, 'uid', service.metadata.uid)
-
- if service.spec.externalTrafficPolicy:
- self.inventory.set_variable(service_name, 'external_traffic_policy',
- service.spec.externalTrafficPolicy)
- if service.spec.externalIPs:
- self.inventory.set_variable(service_name, 'external_ips', service.spec.externalIPs)
-
- if service.spec.externalName:
- self.inventory.set_variable(service_name, 'external_name', service.spec.externalName)
-
- if service.spec.healthCheckNodePort:
- self.inventory.set_variable(service_name, 'health_check_node_port',
- service.spec.healthCheckNodePort)
- if service.spec.loadBalancerIP:
- self.inventory.set_variable(service_name, 'load_balancer_ip',
- service.spec.loadBalancerIP)
- if service.spec.selector:
- self.inventory.set_variable(service_name, 'selector', dict(service.spec.selector))
-
- if hasattr(service.status.loadBalancer, 'ingress') and service.status.loadBalancer.ingress:
- load_balancer = [{'hostname': ingress.hostname,
- 'ip': ingress.ip} for ingress in service.status.loadBalancer.ingress]
- self.inventory.set_variable(service_name, 'load_balancer', load_balancer)
diff --git a/lib/ansible/plugins/inventory/openshift.py b/lib/ansible/plugins/inventory/openshift.py
deleted file mode 100644
index db3392cde2..0000000000
--- a/lib/ansible/plugins/inventory/openshift.py
+++ /dev/null
@@ -1,201 +0,0 @@
-# Copyright (c) 2018 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
-
-DOCUMENTATION = '''
- name: openshift
- plugin_type: inventory
- author:
- - Chris Houseknecht <@chouseknecht>
-
- short_description: OpenShift inventory source
-
- description:
- - Fetch containers, services and routes for one or more clusters
- - Groups by cluster name, namespace, namespace_services, namespace_pods, namespace_routes, and labels
- - Uses openshift.(yml|yaml) YAML configuration file to set parameter values.
-
- options:
- plugin:
- description: token that ensures this is a source file for the 'openshift' plugin.
- required: True
- choices: ['openshift']
- connections:
- description:
- - Optional list of cluster connection settings. If no connections are provided, the default
- I(~/.kube/config) and active context will be used, and objects will be returned for all namespaces
- the active user is authorized to access.
- name:
- description:
- - Optional name to assign to the cluster. If not provided, a name is constructed from the server
- and port.
- kubeconfig:
- description:
- - Path to an existing Kubernetes config file. If not provided, and no other connection
- options are provided, the OpenShift client will attempt to load the default
- configuration file from I(~/.kube/config.json). Can also be specified via K8S_AUTH_KUBECONFIG
- environment variable.
- context:
- description:
- - The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment
- variable.
- host:
- description:
- - Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.
- api_key:
- description:
- - Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment
- variable.
- username:
- description:
- - Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME
- environment variable.
- password:
- description:
- - Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD
- environment variable.
- client_cert:
- description:
- - Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE
- environment variable.
- aliases: [ cert_file ]
- client_key:
- description:
- - Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE
- environment variable.
- aliases: [ key_file ]
- ca_cert:
- description:
- - Path to a CA certificate used to authenticate with the API. Can also be specified via
- K8S_AUTH_SSL_CA_CERT environment variable.
- aliases: [ ssl_ca_cert ]
- validate_certs:
- description:
- - "Whether or not to verify the API server's SSL certificates. Can also be specified via
- K8S_AUTH_VERIFY_SSL environment variable."
- type: bool
- aliases: [ verify_ssl ]
- namespaces:
- description:
- - List of namespaces. If not specified, will fetch all containers for all namespaces user is authorized
- to access.
-
- requirements:
- - "python >= 2.7"
- - "openshift >= 0.6"
- - "PyYAML >= 3.11"
-'''
-
-EXAMPLES = '''
-# File must be named openshift.yaml or openshift.yml
-
-# Authenticate with token, and return all pods and services for all namespaces
-plugin: openshift
-connections:
- - host: https://192.168.64.4:8443
- api_key: xxxxxxxxxxxxxxxx
- verify_ssl: false
-
-# Use default config (~/.kube/config) file and active context, and return objects for a specific namespace
-plugin: openshift
-connections:
- - namespaces:
- - testing
-
-# Use a custom config file, and a specific context.
-plugin: openshift
-connections:
- - kubeconfig: /path/to/config
- context: 'awx/192-168-64-4:8443/developer'
-'''
-
-from ansible.plugins.inventory.k8s import K8sInventoryException, InventoryModule as K8sInventoryModule, format_dynamic_api_exc
-
-try:
- from openshift.dynamic.exceptions import DynamicApiError
-except ImportError:
- pass
-
-
-class InventoryModule(K8sInventoryModule):
- NAME = 'openshift'
-
- transport = 'oc'
-
- def fetch_objects(self, connections):
- super(InventoryModule, self).fetch_objects(connections)
-
- if connections:
- if not isinstance(connections, list):
- raise K8sInventoryException("Expecting connections to be a list.")
-
- for connection in connections:
- client = self.get_api_client(**connection)
- name = connection.get('name', self.get_default_host_name(client.configuration.host))
- if connection.get('namespaces'):
- namespaces = connection['namespaces']
- else:
- namespaces = self.get_available_namespaces(client)
- for namespace in namespaces:
- self.get_routes_for_namespace(client, name, namespace)
- else:
- client = self.get_api_client()
- name = self.get_default_host_name(client.configuration.host)
- namespaces = self.get_available_namespaces(client)
- for namespace in namespaces:
- self.get_routes_for_namespace(client, name, namespace)
-
- def get_routes_for_namespace(self, client, name, namespace):
- v1_route = client.resources.get(api_version='v1', kind='Route')
- try:
- obj = v1_route.get(namespace=namespace)
- except DynamicApiError as exc:
- self.display.debug(exc)
- raise K8sInventoryException('Error fetching Routes list: %s' % format_dynamic_api_exc(exc))
-
- namespace_group = 'namespace_{0}'.format(namespace)
- namespace_routes_group = '{0}_routes'.format(namespace_group)
-
- self.inventory.add_group(name)
- self.inventory.add_group(namespace_group)
- self.inventory.add_child(name, namespace_group)
- self.inventory.add_group(namespace_routes_group)
- self.inventory.add_child(namespace_group, namespace_routes_group)
- for route in obj.items:
- route_name = route.metadata.name
- route_annotations = {} if not route.metadata.annotations else dict(route.metadata.annotations)
-
- self.inventory.add_host(route_name)
-
- if route.metadata.labels:
- # create a group for each label_value
- for key, value in route.metadata.labels:
- group_name = 'label_{0}_{1}'.format(key, value)
- self.inventory.add_group(group_name)
- self.inventory.add_child(group_name, route_name)
- route_labels = dict(route.metadata.labels)
- else:
- route_labels = {}
-
- self.inventory.add_child(namespace_routes_group, route_name)
-
- # add hostvars
- self.inventory.set_variable(route_name, 'labels', route_labels)
- self.inventory.set_variable(route_name, 'annotations', route_annotations)
- self.inventory.set_variable(route_name, 'cluster_name', route.metadata.clusterName)
- self.inventory.set_variable(route_name, 'object_type', 'route')
- self.inventory.set_variable(route_name, 'self_link', route.metadata.selfLink)
- self.inventory.set_variable(route_name, 'resource_version', route.metadata.resourceVersion)
- self.inventory.set_variable(route_name, 'uid', route.metadata.uid)
-
- if route.spec.host:
- self.inventory.set_variable(route_name, 'host', route.spec.host)
-
- if route.spec.path:
- self.inventory.set_variable(route_name, 'path', route.spec.path)
-
- if hasattr(route.spec.port, 'targetPort') and route.spec.port.targetPort:
- self.inventory.set_variable(route_name, 'port', dict(route.spec.port))
diff --git a/lib/ansible/plugins/lookup/_openshift.py b/lib/ansible/plugins/lookup/_openshift.py
deleted file mode 120000
index b0ee072f96..0000000000
--- a/lib/ansible/plugins/lookup/_openshift.py
+++ /dev/null
@@ -1 +0,0 @@
-k8s.py \ No newline at end of file
diff --git a/lib/ansible/plugins/lookup/k8s.py b/lib/ansible/plugins/lookup/k8s.py
deleted file mode 100644
index aa2affcfb7..0000000000
--- a/lib/ansible/plugins/lookup/k8s.py
+++ /dev/null
@@ -1,299 +0,0 @@
-#
-# Copyright 2018 Red Hat | Ansible
-#
-# This file is part of Ansible
-#
-# Ansible is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Ansible is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-
-from __future__ import (absolute_import, division, print_function)
-
-__metaclass__ = type
-
-DOCUMENTATION = """
- lookup: k8s
-
- version_added: "2.5"
-
- short_description: Query the K8s API
-
- description:
- - Uses the OpenShift Python client to fetch a specific object by name, all matching objects within a
- namespace, or all matching objects for all namespaces, as well as information about the cluster.
- - Provides access the full range of K8s APIs.
- - Enables authentication via config file, certificates, password or token.
-
- options:
- cluster_info:
- description:
- - Use to specify the type of cluster information you are attempting to retrieve. Will take priority
- over all the other options.
- api_version:
- description:
- - Use to specify the API version. If I(resource definition) is provided, the I(apiVersion) from the
- I(resource_definition) will override this option.
- default: v1
- kind:
- description:
- - Use to specify an object model. If I(resource definition) is provided, the I(kind) from a
- I(resource_definition) will override this option.
- required: true
- resource_name:
- description:
- - Fetch a specific object by name. If I(resource definition) is provided, the I(metadata.name) value
- from the I(resource_definition) will override this option.
- namespace:
- description:
- - Limit the objects returned to a specific namespace. If I(resource definition) is provided, the
- I(metadata.namespace) value from the I(resource_definition) will override this option.
- label_selector:
- description:
- - Additional labels to include in the query. Ignored when I(resource_name) is provided.
- field_selector:
- description:
- - Specific fields on which to query. Ignored when I(resource_name) is provided.
- resource_definition:
- description:
- - "Provide a YAML configuration for an object. NOTE: I(kind), I(api_version), I(resource_name),
- and I(namespace) will be overwritten by corresponding values found in the provided I(resource_definition)."
- src:
- description:
- - "Provide a path to a file containing a valid YAML definition of an object dated. Mutually
- exclusive with I(resource_definition). NOTE: I(kind), I(api_version), I(resource_name), and I(namespace)
- will be overwritten by corresponding values found in the configuration read in from the I(src) file."
- - Reads from the local file system. To read from the Ansible controller's file system, use the file lookup
- plugin or template lookup plugin, combined with the from_yaml filter, and pass the result to
- I(resource_definition). See Examples below.
- host:
- description:
- - Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.
- api_key:
- description:
- - Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment variable.
- kubeconfig:
- description:
- - Path to an existing Kubernetes config file. If not provided, and no other connection
- options are provided, the openshift client will attempt to load the default
- configuration file from I(~/.kube/config.json). Can also be specified via K8S_AUTH_KUBECONFIG environment
- variable.
- context:
- description:
- - The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment
- variable.
- username:
- description:
- - Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME environment
- variable.
- password:
- description:
- - Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD environment
- variable.
- client_cert:
- description:
- - Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE
- environment
- variable.
- aliases: [ cert_file ]
- client_key:
- description:
- - Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE environment
- variable.
- aliases: [ key_file ]
- ca_cert:
- description:
- - Path to a CA certificate used to authenticate with the API. Can also be specified via K8S_AUTH_SSL_CA_CERT
- environment variable.
- aliases: [ ssl_ca_cert ]
- validate_certs:
- description:
- - Whether or not to verify the API server's SSL certificates. Can also be specified via K8S_AUTH_VERIFY_SSL
- environment variable.
- type: bool
- aliases: [ verify_ssl ]
-
- requirements:
- - "python >= 2.7"
- - "openshift >= 0.6"
- - "PyYAML >= 3.11"
-
- notes:
- - "The OpenShift Python client wraps the K8s Python client, providing full access to
- all of the APIS and models available on both platforms. For API version details and
- additional information visit https://github.com/openshift/openshift-restclient-python"
-"""
-
-EXAMPLES = """
-- name: Fetch a list of namespaces
- set_fact:
- projects: "{{ lookup('k8s', api_version='v1', kind='Namespace') }}"
-
-- name: Fetch all deployments
- set_fact:
- deployments: "{{ lookup('k8s', kind='Deployment') }}"
-
-- name: Fetch all deployments in a namespace
- set_fact:
- deployments: "{{ lookup('k8s', kind='Deployment', namespace='testing') }}"
-
-- name: Fetch a specific deployment by name
- set_fact:
- deployments: "{{ lookup('k8s', kind='Deployment', namespace='testing', resource_name='elastic') }}"
-
-- name: Fetch with label selector
- set_fact:
- service: "{{ lookup('k8s', kind='Service', label_selector='app=galaxy') }}"
-
-# Use parameters from a YAML config
-
-- name: Load config from the Ansible controller filesystem
- set_fact:
- config: "{{ lookup('file', 'service.yml') | from_yaml }}"
-
-- name: Using the config (loaded from a file in prior task), fetch the latest version of the object
- set_fact:
- service: "{{ lookup('k8s', resource_definition=config) }}"
-
-- name: Use a config from the local filesystem
- set_fact:
- service: "{{ lookup('k8s', src='service.yml') }}"
-"""
-
-RETURN = """
- _list:
- description:
- - One ore more object definitions returned from the API.
- type: complex
- contains:
- api_version:
- description: The versioned schema of this representation of an object.
- returned: success
- type: str
- kind:
- description: Represents the REST resource this object represents.
- returned: success
- type: str
- metadata:
- description: Standard object metadata. Includes name, namespace, annotations, labels, etc.
- returned: success
- type: complex
- spec:
- description: Specific attributes of the object. Will vary based on the I(api_version) and I(kind).
- returned: success
- type: complex
- status:
- description: Current status details for the object.
- returned: success
- type: complex
-"""
-
-from ansible.plugins.lookup import LookupBase
-
-from ansible.module_utils.k8s.common import K8sAnsibleMixin
-
-from ansible.errors import AnsibleError
-
-
-try:
- from openshift.dynamic import DynamicClient
- from openshift.dynamic.exceptions import NotFoundError
- HAS_K8S_MODULE_HELPER = True
- k8s_import_exception = None
-except ImportError as e:
- HAS_K8S_MODULE_HELPER = False
- k8s_import_exception = e
-
-try:
- import yaml
- HAS_YAML = True
-except ImportError:
- HAS_YAML = False
-
-
-class KubernetesLookup(K8sAnsibleMixin):
-
- def __init__(self):
-
- if not HAS_K8S_MODULE_HELPER:
- raise Exception(
- "Requires the OpenShift Python client. Try `pip install openshift`. Detail: {0}".format(k8s_import_exception)
- )
-
- if not HAS_YAML:
- raise Exception(
- "Requires PyYAML. Try `pip install PyYAML`"
- )
-
- self.kind = None
- self.name = None
- self.namespace = None
- self.api_version = None
- self.label_selector = None
- self.field_selector = None
- self.include_uninitialized = None
- self.resource_definition = None
- self.helper = None
- self.connection = {}
-
- def fail(self, msg=None):
- raise AnsibleError(msg)
-
- def run(self, terms, variables=None, **kwargs):
- self.params = kwargs
- self.client = self.get_api_client()
-
- cluster_info = kwargs.get('cluster_info')
- if cluster_info == 'version':
- return [self.client.version]
- if cluster_info == 'api_groups':
- return [self.client.resources.api_groups]
-
- self.kind = kwargs.get('kind')
- self.name = kwargs.get('resource_name')
- self.namespace = kwargs.get('namespace')
- self.api_version = kwargs.get('api_version', 'v1')
- self.label_selector = kwargs.get('label_selector')
- self.field_selector = kwargs.get('field_selector')
- self.include_uninitialized = kwargs.get('include_uninitialized', False)
-
- resource_definition = kwargs.get('resource_definition')
- src = kwargs.get('src')
- if src:
- resource_definition = self.load_resource_definitions(src)[0]
- if resource_definition:
- self.kind = resource_definition.get('kind', self.kind)
- self.api_version = resource_definition.get('apiVersion', self.api_version)
- self.name = resource_definition.get('metadata', {}).get('name', self.name)
- self.namespace = resource_definition.get('metadata', {}).get('namespace', self.namespace)
-
- if not self.kind:
- raise AnsibleError(
- "Error: no Kind specified. Use the 'kind' parameter, or provide an object YAML configuration "
- "using the 'resource_definition' parameter."
- )
-
- resource = self.find_resource(self.kind, self.api_version, fail=True)
- try:
- k8s_obj = resource.get(name=self.name, namespace=self.namespace, label_selector=self.label_selector, field_selector=self.field_selector)
- except NotFoundError:
- return []
-
- if self.name:
- return [k8s_obj.to_dict()]
-
- return k8s_obj.to_dict().get('items')
-
-
-class LookupModule(LookupBase):
-
- def run(self, terms, variables=None, **kwargs):
- return KubernetesLookup().run(terms, variables=variables, **kwargs)
diff --git a/test/integration/targets/k8s/README.md b/test/integration/targets/k8s/README.md
deleted file mode 100644
index 7cb72b16e2..0000000000
--- a/test/integration/targets/k8s/README.md
+++ /dev/null
@@ -1,23 +0,0 @@
-Wait tests
-----------
-
-wait tests require at least one node, and don't work on the normal k8s
-openshift-origin container as provided by ansible-test --docker -v k8s
-
-minikube, Kubernetes from Docker or any other Kubernetes service will
-suffice.
-
-If kubectl is already using the right config file and context, you can
-just do
-
-```
-cd test/integration/targets/k8s
-./runme.sh -vv
-```
-
-otherwise set one or both of `K8S_AUTH_KUBECONFIG` and `K8S_AUTH_CONTEXT`
-and use the same command
-
-
-
-
diff --git a/test/integration/targets/k8s/aliases b/test/integration/targets/k8s/aliases
deleted file mode 100644
index 326e691050..0000000000
--- a/test/integration/targets/k8s/aliases
+++ /dev/null
@@ -1,2 +0,0 @@
-cloud/openshift
-shippable/cloud/group1
diff --git a/test/integration/targets/k8s/defaults/main.yml b/test/integration/targets/k8s/defaults/main.yml
deleted file mode 100644
index 68fde7c412..0000000000
--- a/test/integration/targets/k8s/defaults/main.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-recreate_crd_default_merge_expectation: recreate_crd is not failed
-
-k8s_pod_metadata:
- labels:
- app: "{{ k8s_pod_name }}"
-
-k8s_pod_spec:
- containers:
- - image: "{{ k8s_pod_image }}"
- imagePullPolicy: Always
- name: "{{ k8s_pod_name }}"
- command: "{{ k8s_pod_command }}"
- readinessProbe:
- initialDelaySeconds: 15
- exec:
- command:
- - /bin/true
- resources:
- limits:
- cpu: "100m"
- memory: "100Mi"
- ports: "{{ k8s_pod_ports }}"
-
-k8s_pod_command: []
-
-k8s_pod_ports: []
-
-k8s_pod_template:
- metadata: "{{ k8s_pod_metadata }}"
- spec: "{{ k8s_pod_spec }}"
-
-k8s_openshift: yes
diff --git a/test/integration/targets/k8s/files/crd-resource.yml b/test/integration/targets/k8s/files/crd-resource.yml
deleted file mode 100644
index 9804d4d14e..0000000000
--- a/test/integration/targets/k8s/files/crd-resource.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-apiVersion: certmanager.k8s.io/v1alpha1
-kind: Certificate
-metadata:
- name: acme-crt
-spec:
- secretName: acme-crt-secret
- dnsNames:
- - foo.example.com
- - bar.example.com
- acme:
- config:
- - ingressClass: nginx
- domains:
- - foo.example.com
- - bar.example.com
- issuerRef:
- name: letsencrypt-prod
- # We can reference ClusterIssuers by changing the kind here.
- # The default value is Issuer (i.e. a locally namespaced Issuer)
- kind: Issuer
diff --git a/test/integration/targets/k8s/files/kuard-extra-property.yml b/test/integration/targets/k8s/files/kuard-extra-property.yml
deleted file mode 100644
index 2d5b799434..0000000000
--- a/test/integration/targets/k8s/files/kuard-extra-property.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-apiVersion: extensions/v1beta1
-kind: Deployment
-metadata:
- labels:
- app: kuard
- name: kuard
- namespace: default
-spec:
- replicas: 3
- selector:
- matchLabels:
- app: kuard
- unwanted: value
- template:
- metadata:
- labels:
- app: kuard
- spec:
- containers:
- - image: gcr.io/kuar-demo/kuard-amd64:1
- name: kuard
diff --git a/test/integration/targets/k8s/files/kuard-invalid-type.yml b/test/integration/targets/k8s/files/kuard-invalid-type.yml
deleted file mode 100644
index 7a5f73d910..0000000000
--- a/test/integration/targets/k8s/files/kuard-invalid-type.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-apiVersion: extensions/v1beta1
-kind: Deployment
-metadata:
- labels:
- app: kuard
- name: kuard
- namespace: default
-spec:
- replicas: hello
- selector:
- matchLabels:
- app: kuard
- template:
- metadata:
- labels:
- app: kuard
- spec:
- containers:
- - image: gcr.io/kuar-demo/kuard-amd64:1
- name: kuard
diff --git a/test/integration/targets/k8s/files/setup-crd.yml b/test/integration/targets/k8s/files/setup-crd.yml
deleted file mode 100644
index a8e2d51e80..0000000000
--- a/test/integration/targets/k8s/files/setup-crd.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-apiVersion: apiextensions.k8s.io/v1beta1
-kind: CustomResourceDefinition
-metadata:
- name: certificates.certmanager.k8s.io
-spec:
- group: certmanager.k8s.io
- version: v1alpha1
- scope: Namespaced
- names:
- kind: Certificate
- plural: certificates
- shortNames:
- - cert
- - certs
diff --git a/test/integration/targets/k8s/meta/main.yml b/test/integration/targets/k8s/meta/main.yml
deleted file mode 100644
index 1810d4bec9..0000000000
--- a/test/integration/targets/k8s/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - setup_remote_tmp_dir
diff --git a/test/integration/targets/k8s/tasks/append_hash.yml b/test/integration/targets/k8s/tasks/append_hash.yml
deleted file mode 100644
index 876e876a29..0000000000
--- a/test/integration/targets/k8s/tasks/append_hash.yml
+++ /dev/null
@@ -1,68 +0,0 @@
-- block:
- - name: Ensure that append_hash namespace exists
- k8s:
- kind: Namespace
- name: append-hash
-
- - name: create k8s_resource variable
- set_fact:
- k8s_resource:
- metadata:
- name: config-map-test
- namespace: append-hash
- apiVersion: v1
- kind: ConfigMap
- data:
- hello: world
-
- - name: Create config map
- k8s:
- definition: "{{ k8s_resource }}"
- append_hash: yes
- register: k8s_configmap1
-
- - name: check configmap is created with a hash
- assert:
- that:
- - k8s_configmap1 is changed
- - k8s_configmap1.result.metadata.name != 'config-map-test'
- - k8s_configmap1.result.metadata.name[:-10] == 'config-map-test-'
-
- - name: recreate same config map
- k8s:
- definition: "{{ k8s_resource }}"
- append_hash: yes
- register: k8s_configmap2
-
- - name: check configmaps are different
- assert:
- that:
- - k8s_configmap2 is not changed
- - k8s_configmap1.result.metadata.name == k8s_configmap2.result.metadata.name
-
- - name: add key to config map
- k8s:
- definition:
- metadata:
- name: config-map-test
- namespace: append-hash
- apiVersion: v1
- kind: ConfigMap
- data:
- hello: world
- another: value
- append_hash: yes
- register: k8s_configmap3
-
- - name: check configmaps are different
- assert:
- that:
- - k8s_configmap3 is changed
- - k8s_configmap1.result.metadata.name != k8s_configmap3.result.metadata.name
-
- always:
- - name: ensure that namespace is removed
- k8s:
- kind: Namespace
- name: append-hash
- state: absent
diff --git a/test/integration/targets/k8s/tasks/apply.yml b/test/integration/targets/k8s/tasks/apply.yml
deleted file mode 100644
index cf51123124..0000000000
--- a/test/integration/targets/k8s/tasks/apply.yml
+++ /dev/null
@@ -1,277 +0,0 @@
-- block:
- - python_requirements_info:
- dependencies:
- - openshift
- - kubernetes
-
- - set_fact:
- apply_namespace: apply
-
- - name: ensure namespace exists
- k8s:
- definition:
- apiVersion: v1
- kind: Namespace
- metadata:
- name: "{{ apply_namespace }}"
-
- - name: add a configmap
- k8s:
- name: "apply-configmap"
- namespace: "{{ apply_namespace }}"
- definition:
- kind: ConfigMap
- apiVersion: v1
- data:
- one: "1"
- two: "2"
- three: "3"
- apply: yes
- register: k8s_configmap
-
- - name: check configmap was created
- assert:
- that:
- - k8s_configmap is changed
- - k8s_configmap.result.metadata.annotations|default(False)
-
- - name: add same configmap again
- k8s:
- definition:
- kind: ConfigMap
- apiVersion: v1
- metadata:
- name: "apply-configmap"
- namespace: "{{ apply_namespace }}"
- data:
- one: "1"
- two: "2"
- three: "3"
- apply: yes
- register: k8s_configmap_2
-
- - name: check nothing changed
- assert:
- that:
- - k8s_configmap_2 is not changed
-
- - name: add same configmap again with check mode on
- k8s:
- definition:
- kind: ConfigMap
- apiVersion: v1
- metadata:
- name: "apply-configmap"
- namespace: "{{ apply_namespace }}"
- data:
- one: "1"
- two: "2"
- three: "3"
- apply: yes
- check_mode: yes
- register: k8s_configmap_check
-
- - name: check nothing changed
- assert:
- that:
- - k8s_configmap_check is not changed
-
- - name: add same configmap again but using name and namespace args
- k8s:
- name: "apply-configmap"
- namespace: "{{ apply_namespace }}"
- definition:
- kind: ConfigMap
- apiVersion: v1
- data:
- one: "1"
- two: "2"
- three: "3"
- apply: yes
- register: k8s_configmap_2a
-
- - name: check nothing changed
- assert:
- that:
- - k8s_configmap_2a is not changed
-
- - name: update configmap
- k8s:
- definition:
- kind: ConfigMap
- apiVersion: v1
- metadata:
- name: "apply-configmap"
- namespace: "{{ apply_namespace }}"
- data:
- one: "1"
- three: "3"
- four: "4"
- apply: yes
- register: k8s_configmap_3
-
- - name: ensure that configmap has been correctly updated
- assert:
- that:
- - k8s_configmap_3 is changed
- - "'four' in k8s_configmap_3.result.data"
- - "'two' not in k8s_configmap_3.result.data"
-
- - name: add a service
- k8s:
- definition:
- apiVersion: v1
- kind: Service
- metadata:
- name: apply-svc
- namespace: "{{ apply_namespace }}"
- spec:
- selector:
- app: whatever
- ports:
- - name: http
- port: 8080
- targetPort: 8080
- type: NodePort
- apply: yes
- register: k8s_service
-
- - name: add exactly same service
- k8s:
- definition:
- apiVersion: v1
- kind: Service
- metadata:
- name: apply-svc
- namespace: "{{ apply_namespace }}"
- spec:
- selector:
- app: whatever
- ports:
- - name: http
- port: 8080
- targetPort: 8080
- type: NodePort
- apply: yes
- register: k8s_service_2
-
- - name: check nothing changed
- assert:
- that:
- - k8s_service_2 is not changed
-
- - name: change service ports
- k8s:
- definition:
- apiVersion: v1
- kind: Service
- metadata:
- name: apply-svc
- namespace: "{{ apply_namespace }}"
- spec:
- selector:
- app: whatever
- ports:
- - name: http
- port: 8081
- targetPort: 8081
- type: NodePort
- apply: yes
- register: k8s_service_3
-
- - name: check ports are correct
- assert:
- that:
- - k8s_service_3 is changed
- - k8s_service_3.result.spec.ports | length == 1
- - k8s_service_3.result.spec.ports[0].port == 8081
-
- - name: insert new service port
- k8s:
- definition:
- apiVersion: v1
- kind: Service
- metadata:
- name: apply-svc
- namespace: "{{ apply_namespace }}"
- spec:
- selector:
- app: whatever
- ports:
- - name: mesh
- port: 8080
- targetPort: 8080
- - name: http
- port: 8081
- targetPort: 8081
- type: NodePort
- apply: yes
- register: k8s_service_4
-
- - name: check ports are correct
- assert:
- that:
- - k8s_service_4 is changed
- - k8s_service_4.result.spec.ports | length == 2
- - k8s_service_4.result.spec.ports[0].port == 8080
- - k8s_service_4.result.spec.ports[1].port == 8081
-
- - name: remove new service port (check mode)
- k8s:
- definition:
- apiVersion: v1
- kind: Service
- metadata:
- name: apply-svc
- namespace: "{{ apply_namespace }}"
- spec:
- selector:
- app: whatever
- ports:
- - name: http
- port: 8081
- targetPort: 8081
- type: NodePort
- apply: yes
- check_mode: yes
- register: k8s_service_check
-
- - name: check ports are correct
- assert:
- that:
- - k8s_service_check is changed
- - k8s_service_check.result.spec.ports | length == 1
- - k8s_service_check.result.spec.ports[0].port == 8081
-
- - name: remove new service port
- k8s:
- definition:
- apiVersion: v1
- kind: Service
- metadata:
- name: apply-svc
- namespace: "{{ apply_namespace }}"
- spec:
- selector:
- app: whatever
- ports:
- - name: http
- port: 8081
- targetPort: 8081
- type: NodePort
- apply: yes
- register: k8s_service_5
-
- - name: check ports are correct
- assert:
- that:
- - k8s_service_5 is changed
- - k8s_service_5.result.spec.ports | length == 1
- - k8s_service_5.result.spec.ports[0].port == 8081
-
- always:
- - name: remove namespace
- k8s:
- kind: Namespace
- name: "{{ apply_namespace }}"
- state: absent
diff --git a/test/integration/targets/k8s/tasks/crd.yml b/test/integration/targets/k8s/tasks/crd.yml
deleted file mode 100644
index c9e47632f9..0000000000
--- a/test/integration/targets/k8s/tasks/crd.yml
+++ /dev/null
@@ -1,71 +0,0 @@
-# TODO: This is the only way I could get the kubeconfig, I don't know why. Running the lookup outside of debug seems to return an empty string
-#- debug: msg={{ lookup('env', 'K8S_AUTH_KUBECONFIG') }}
-# register: kubeconfig
-
-# Kubernetes resources
-
-- block:
- - name: Create a namespace
- k8s:
- name: crd
- kind: Namespace
-
- - name: install custom resource definitions
- k8s:
- definition: "{{ lookup('file', role_path + '/files/setup-crd.yml') }}"
-
- - name: pause 5 seconds to avoid race condition
- pause:
- seconds: 5
-
- - name: create custom resource definition
- k8s:
- definition: "{{ lookup('file', role_path + '/files/crd-resource.yml') }}"
- namespace: crd
- apply: "{{ create_crd_with_apply | default(omit) }}"
- register: create_crd
-
- - name: patch custom resource definition
- k8s:
- definition: "{{ lookup('file', role_path + '/files/crd-resource.yml') }}"
- namespace: crd
- register: recreate_crd
- ignore_errors: yes
-
- - name: assert that recreating crd is as expected
- assert:
- that:
- - recreate_crd_default_merge_expectation
-
- - block:
- - name: recreate custom resource definition with merge_type
- k8s:
- definition: "{{ lookup('file', role_path + '/files/crd-resource.yml') }}"
- merge_type: merge
- namespace: crd
- register: recreate_crd_with_merge
-
- - name: recreate custom resource definition with merge_type list
- k8s:
- definition: "{{ lookup('file', role_path + '/files/crd-resource.yml') }}"
- merge_type:
- - strategic-merge
- - merge
- namespace: crd
- register: recreate_crd_with_merge_list
- when: recreate_crd is successful
-
-
- - name: remove crd
- k8s:
- definition: "{{ lookup('file', role_path + '/files/crd-resource.yml') }}"
- namespace: crd
- state: absent
-
- always:
- - name: remove crd namespace
- k8s:
- kind: Namespace
- name: crd
- state: absent
- ignore_errors: yes
diff --git a/test/integration/targets/k8s/tasks/delete.yml b/test/integration/targets/k8s/tasks/delete.yml
deleted file mode 100644
index fef6e5e9f6..0000000000
--- a/test/integration/targets/k8s/tasks/delete.yml
+++ /dev/null
@@ -1,101 +0,0 @@
-- name: ensure that there are actually some nodes
- k8s_info:
- kind: Node
- register: nodes
-
-- block:
- - set_fact:
- delete_namespace: delete
-
- - name: ensure namespace exists
- k8s:
- definition:
- apiVersion: v1
- kind: Namespace
- metadata:
- name: "{{ delete_namespace }}"
-
- - name: add a daemonset
- k8s:
- definition:
- apiVersion: extensions/v1beta1
- kind: DaemonSet
- metadata:
- name: delete-daemonset
- namespace: "{{ delete_namespace }}"
- spec:
- selector:
- matchLabels:
- app: "{{ k8s_pod_name }}"
- template: "{{ k8s_pod_template }}"
- wait: yes
- wait_timeout: 180
- vars:
- k8s_pod_name: delete-ds
- k8s_pod_image: gcr.io/kuar-demo/kuard-amd64:1
- register: ds
-
- - name: check that daemonset wait worked
- assert:
- that:
- - ds.result.status.currentNumberScheduled == ds.result.status.desiredNumberScheduled
-
- - name: check if pods exist
- k8s_info:
- namespace: "{{ delete_namespace }}"
- kind: Pod
- label_selectors:
- - "app={{ k8s_pod_name }}"
- vars:
- k8s_pod_name: delete-ds
- register: pods_create
-
- - name: assert that there are pods
- assert:
- that:
- - pods_create.resources
-
- - name: remove the daemonset
- k8s:
- kind: DaemonSet
- name: delete-daemonset
- namespace: "{{ delete_namespace }}"
- state: absent
- wait: yes
-
- - name: show status of pods
- k8s_info:
- namespace: "{{ delete_namespace }}"
- kind: Pod
- label_selectors:
- - "app={{ k8s_pod_name }}"
- vars:
- k8s_pod_name: delete-ds
-
- - name: wait for background deletion
- pause:
- seconds: 30
-
- - name: check if pods still exist
- k8s_info:
- namespace: "{{ delete_namespace }}"
- kind: Pod
- label_selectors:
- - "app={{ k8s_pod_name }}"
- vars:
- k8s_pod_name: delete-ds
- register: pods_delete
-
- - name: assert that deleting the daemonset deleted the pods
- assert:
- that:
- - not pods_delete.resources
-
- always:
- - name: remove namespace
- k8s:
- kind: Namespace
- name: "{{ delete_namespace }}"
- state: absent
-
- when: (nodes.resources | length) > 0
diff --git a/test/integration/targets/k8s/tasks/full_test.yml b/test/integration/targets/k8s/tasks/full_test.yml
deleted file mode 100644
index fdf3d700dc..0000000000
--- a/test/integration/targets/k8s/tasks/full_test.yml
+++ /dev/null
@@ -1,375 +0,0 @@
-# TODO: This is the only way I could get the kubeconfig, I don't know why. Running the lookup outside of debug seems to return an empty string
-#- debug: msg={{ lookup('env', 'K8S_AUTH_KUBECONFIG') }}
-# register: kubeconfig
-
-# Kubernetes resources
-
-- include_tasks: delete.yml
-- include_tasks: apply.yml
-- include_tasks: waiter.yml
-
-- block:
- - name: Create a namespace
- k8s:
- name: testing
- kind: Namespace
- register: output
-
- - name: show output
- debug:
- var: output
-
- - name: Setting validate_certs to true causes a failure
- k8s:
- name: testing
- kind: Namespace
- validate_certs: yes
- ignore_errors: yes
- register: output
-
- - name: assert that validate_certs caused a failure (and therefore was correctly translated to verify_ssl)
- assert:
- that:
- - output is failed
-
- - name: k8s_info works with empty resources
- k8s_info:
- kind: Deployment
- namespace: testing
- api_version: extensions/v1beta1
- register: k8s_info
-
- - name: assert that k8s_info is in correct format
- assert:
- that:
- - "'resources' in k8s_info"
- - not k8s_info.resources
-
- - name: Create a service
- k8s:
- state: present
- resource_definition: &svc
- apiVersion: v1
- kind: Service
- metadata:
- name: web
- namespace: testing
- labels:
- app: galaxy
- service: web
- spec:
- selector:
- app: galaxy
- service: web
- ports:
- - protocol: TCP
- targetPort: 8000
- name: port-8000-tcp
- port: 8000
- register: output
-
- - name: show output
- debug:
- var: output
-
- - name: Create the service again
- k8s:
- state: present
- resource_definition: *svc
- register: output
-
- - name: Service creation should be idempotent
- assert:
- that: not output.changed
-
- - name: Create a ConfigMap
- k8s:
- kind: ConfigMap
- name: test-force-update
- namespace: testing
- definition:
- data:
- key: value
-
- - name: Force update ConfigMap
- k8s:
- kind: ConfigMap
- name: test-force-update
- namespace: testing
- definition:
- data:
- key: newvalue
- force: yes
-
- - name: Create PVC
- k8s:
- state: present
- inline: &pvc
- apiVersion: v1
- kind: PersistentVolumeClaim
- metadata:
- name: elastic-volume
- namespace: testing
- spec:
- resources:
- requests:
- storage: 5Gi
- accessModes:
- - ReadWriteOnce
-
- - name: Show output
- debug:
- var: output
-
- - name: Create the PVC again
- k8s:
- state: present
- inline: *pvc
-
- - name: PVC creation should be idempotent
- assert:
- that: not output.changed
-
- - name: Create deployment
- k8s:
- state: present
- inline: &deployment
- apiVersion: extensions/v1beta1
- kind: Deployment
- metadata:
- name: elastic
- labels:
- app: galaxy
- service: elastic
- namespace: testing
- spec:
- template:
- metadata:
- labels:
- app: galaxy
- service: elastic
- spec:
- containers:
- - name: elastic
- volumeMounts:
- - mountPath: /usr/share/elasticsearch/data
- name: elastic-volume
- command: ['elasticsearch']
- image: 'ansible/galaxy-elasticsearch:2.4.6'
- volumes:
- - name: elastic-volume
- persistentVolumeClaim:
- claimName: elastic-volume
- replicas: 1
- strategy:
- type: RollingUpdate
- register: output
-
- - name: Show output
- debug:
- var: output
-
- - name: Create deployment again
- k8s:
- state: present
- inline: *deployment
- register: output
-
- - name: Deployment creation should be idempotent
- assert:
- that: not output.changed
-
- - debug:
- var: k8s_openshift
-
- - include: openshift.yml
- when: k8s_openshift | bool
-
- ### Type tests
- - name: Create a namespace from a string
- k8s:
- definition: |+
- ---
- kind: Namespace
- apiVersion: v1
- metadata:
- name: testing1
-
- - name: Namespace should exist
- k8s_info:
- kind: Namespace
- api_version: v1
- name: testing1
- register: k8s_info_testing1
- failed_when: not k8s_info_testing1.resources or k8s_info_testing1.resources[0].status.phase != "Active"
-
- - name: Create resources from a multidocument yaml string
- k8s:
- definition: |+
- ---
- kind: Namespace
- apiVersion: v1
- metadata:
- name: testing2
- ---
- kind: Namespace
- apiVersion: v1
- metadata:
- name: testing3
-
- - name: Lookup namespaces
- k8s_info:
- api_version: v1
- kind: Namespace
- name: "{{ item }}"
- loop:
- - testing2
- - testing3
- register: k8s_namespaces
-
- - name: Resources should exist
- assert:
- that: item.resources[0].status.phase == 'Active'
- loop: "{{ k8s_namespaces.results }}"
-
- - name: Delete resources from a multidocument yaml string
- k8s:
- state: absent
- definition: |+
- ---
- kind: Namespace
- apiVersion: v1
- metadata:
- name: testing2
- ---
- kind: Namespace
- apiVersion: v1
- metadata:
- name: testing3
-
- - name: Lookup namespaces
- k8s_info:
- api_version: v1
- kind: Namespace
- name: "{{ item }}"
- loop:
- - testing2
- - testing3
- register: k8s_namespaces
-
- - name: Resources should not exist
- assert:
- that:
- - not item.resources or item.resources[0].status.phase == "Terminating"
- loop: "{{ k8s_namespaces.results }}"
-
- - name: Create resources from a list
- k8s:
- definition:
- - kind: Namespace
- apiVersion: v1
- metadata:
- name: testing4
- - kind: Namespace
- apiVersion: v1
- metadata:
- name: testing5
-
- - name: Lookup namespaces
- k8s_info:
- api_version: v1
- kind: Namespace
- name: "{{ item }}"
- loop:
- - testing4
- - testing5
- register: k8s_namespaces
-
- - name: Resources should exist
- assert:
- that: item.resources[0].status.phase == 'Active'
- loop: "{{ k8s_namespaces.results }}"
-
- - name: Delete resources from a list
- k8s:
- state: absent
- definition:
- - kind: Namespace
- apiVersion: v1
- metadata:
- name: testing4
- - kind: Namespace
- apiVersion: v1
- metadata:
- name: testing5
-
- - k8s_info:
- api_version: v1
- kind: Namespace
- name: "{{ item }}"
- loop:
- - testing4
- - testing5
- register: k8s_info
-
- - name: Resources are terminating if still in results
- assert:
- that: not item.resources or item.resources[0].status.phase == "Terminating"
- loop: "{{ k8s_info.results }}"
-
- - name: Create resources from a yaml string ending with ---
- k8s:
- definition: |+
- ---
- kind: Namespace
- apiVersion: v1
- metadata:
- name: testing6
- ---
-
- - name: Namespace should exist
- k8s_info:
- kind: Namespace
- api_version: v1
- name: testing6
- register: k8s_info_testing6
- failed_when: not k8s_info_testing6.resources or k8s_info_testing6.resources[0].status.phase != "Active"
-
- - include_tasks: crd.yml
- - include_tasks: lists.yml
- - include_tasks: append_hash.yml
-
- always:
- - name: Delete all namespaces
- k8s:
- state: absent
- definition:
- - kind: Namespace
- apiVersion: v1
- metadata:
- name: testing
- - kind: Namespace
- apiVersion: v1
- metadata:
- name: testing1
- - kind: Namespace
- apiVersion: v1
- metadata:
- name: testing2
- - kind: Namespace
- apiVersion: v1
- metadata:
- name: testing3
- - kind: Namespace
- apiVersion: v1
- metadata:
- name: testing4
- - kind: Namespace
- apiVersion: v1
- metadata:
- name: testing5
- - kind: Namespace
- apiVersion: v1
- metadata:
- name: testing6
- ignore_errors: yes
diff --git a/test/integration/targets/k8s/tasks/lists.yml b/test/integration/targets/k8s/tasks/lists.yml
deleted file mode 100644
index 4d464df734..0000000000
--- a/test/integration/targets/k8s/tasks/lists.yml
+++ /dev/null
@@ -1,140 +0,0 @@
----
-
-- name: Ensure testing1 namespace exists
- k8s:
- api_version: v1
- kind: Namespace
- name: testing1
-
-- block:
- - name: Create configmaps
- k8s:
- namespace: testing1
- definition:
- apiVersion: v1
- kind: ConfigMapList
- items: '{{ configmaps }}'
-
- - name: Get ConfigMaps
- k8s_info:
- api_version: v1
- kind: ConfigMap
- namespace: testing1
- label_selectors:
- - app=test
- register: cms
-
- - name: All three configmaps should exist
- assert:
- that: item.data.a is defined
- with_items: '{{ cms.resources }}'
-
- - name: Delete configmaps
- k8s:
- state: absent
- namespace: testing1
- definition:
- apiVersion: v1
- kind: ConfigMapList
- items: '{{ configmaps }}'
-
- - name: Get ConfigMaps
- k8s_info:
- api_version: v1
- kind: ConfigMap
- namespace: testing1
- label_selectors:
- - app=test
- register: cms
-
- - name: All three configmaps should not exist
- assert:
- that: not cms.resources
- vars:
- configmaps:
- - metadata:
- name: list-example-1
- labels:
- app: test
- data:
- a: first
- - metadata:
- name: list-example-2
- labels:
- app: test
- data:
- a: second
- - metadata:
- name: list-example-3
- labels:
- app: test
- data:
- a: third
-
-- block:
- - name: Create list of arbitrary resources
- k8s:
- namespace: testing1
- definition:
- apiVersion: v1
- kind: List
- namespace: testing1
- items: '{{ resources }}'
-
- - name: Get the created resources
- k8s_info:
- api_version: '{{ item.apiVersion }}'
- kind: '{{ item.kind }}'
- namespace: testing1
- name: '{{ item.metadata.name }}'
- register: list_resources
- with_items: '{{ resources }}'
-
- - name: All resources should exist
- assert:
- that: ((list_resources.results | sum(attribute="resources", start=[])) | length) == (resources | length)
-
- - name: Delete list of arbitrary resources
- k8s:
- state: absent
- namespace: testing1
- definition:
- apiVersion: v1
- kind: List
- namespace: testing1
- items: '{{ resources }}'
-
- - name: Get the resources
- k8s_info:
- api_version: '{{ item.apiVersion }}'
- kind: '{{ item.kind }}'
- namespace: testing1
- name: '{{ item.metadata.name }}'
- register: list_resources
- with_items: '{{ resources }}'
-
- - name: The resources should not exist
- assert:
- that: not ((list_resources.results | sum(attribute="resources", start=[])) | length)
- vars:
- resources:
- - apiVersion: v1
- kind: ConfigMap
- metadata:
- name: list-example-4
- data:
- key: value
- - apiVersion: v1
- kind: Service
- metadata:
- name: list-example-svc
- labels:
- app: test
- spec:
- selector:
- app: test
- ports:
- - protocol: TCP
- targetPort: 8000
- name: port-8000-tcp
- port: 8000
diff --git a/test/integration/targets/k8s/tasks/main.yml b/test/integration/targets/k8s/tasks/main.yml
deleted file mode 100644
index bb11bbc686..0000000000
--- a/test/integration/targets/k8s/tasks/main.yml
+++ /dev/null
@@ -1,92 +0,0 @@
-- set_fact:
- virtualenv: "{{ remote_tmp_dir }}/virtualenv"
- virtualenv_command: "{{ ansible_python_interpreter }} -m virtualenv"
-
-- set_fact:
- virtualenv_interpreter: "{{ virtualenv }}/bin/python"
-
-- pip:
- name: virtualenv
-
-# Test graceful failure for missing kubernetes-validate
-
-- pip:
- name:
- - openshift>=0.9.2
- - coverage
- virtualenv: "{{ virtualenv }}"
- virtualenv_command: "{{ virtualenv_command }}"
- virtualenv_site_packages: no
-
-- include_tasks: validate_not_installed.yml
- vars:
- ansible_python_interpreter: "{{ virtualenv_interpreter }}"
-
-- file:
- path: "{{ virtualenv }}"
- state: absent
- no_log: yes
-
-# Test validate with kubernetes-validate
-
-- pip:
- name:
- - kubernetes-validate==1.12.0
- - openshift>=0.9.2
- - coverage
- virtualenv: "{{ virtualenv }}"
- virtualenv_command: "{{ virtualenv_command }}"
- virtualenv_site_packages: no
-
-- include_tasks: validate_installed.yml
- vars:
- ansible_python_interpreter: "{{ virtualenv_interpreter }}"
- playbook_namespace: ansible-test-k8s-validate
-
-- file:
- path: "{{ virtualenv }}"
- state: absent
- no_log: yes
-
-# Test graceful failure for older versions of openshift
-
-- pip:
- name:
- - openshift==0.6.0
- - kubernetes==6.0.0
- - coverage
- virtualenv: "{{ virtualenv }}"
- virtualenv_command: "{{ virtualenv_command }}"
- virtualenv_site_packages: no
-
-- include_tasks: older_openshift_fail.yml
- vars:
- ansible_python_interpreter: "{{ virtualenv_interpreter }}"
- recreate_crd_default_merge_expectation: recreate_crd is failed
- playbook_namespace: ansible-test-k8s-older-openshift
-
-- file:
- path: "{{ virtualenv }}"
- state: absent
- no_log: yes
-
-# Run full test suite
-
-- pip:
- name:
- - openshift>=0.9.2
- - coverage
- virtualenv: "{{ virtualenv }}"
- virtualenv_command: "{{ virtualenv_command }}"
- virtualenv_site_packages: no
-
-- include_tasks: full_test.yml
- vars:
- ansible_python_interpreter: "{{ virtualenv_interpreter }}"
- create_crd_with_apply: no
- playbook_namespace: ansible-test-k8s-full
-
-- file:
- path: "{{ virtualenv }}"
- state: absent
- no_log: yes
diff --git a/test/integration/targets/k8s/tasks/older_openshift_fail.yml b/test/integration/targets/k8s/tasks/older_openshift_fail.yml
deleted file mode 100644
index 2acf3d2175..0000000000
--- a/test/integration/targets/k8s/tasks/older_openshift_fail.yml
+++ /dev/null
@@ -1,69 +0,0 @@
- - python_requirements_info:
- dependencies:
- - openshift==0.6.0
- - kubernetes==6.0.0
-
- # append_hash
- - name: use append_hash with ConfigMap
- k8s:
- definition:
- metadata:
- name: config-map-test
- namespace: "{{ playbook_namespace }}"
- apiVersion: v1
- kind: ConfigMap
- data:
- hello: world
- append_hash: yes
- ignore_errors: yes
- register: k8s_append_hash
-
- - name: assert that append_hash fails gracefully
- assert:
- that:
- - k8s_append_hash is failed
- - "'Failed to import the required Python library (openshift >= 0.7.2)' in k8s_append_hash.msg"
- - "'. This is required for append_hash.' in k8s_append_hash.msg"
-
- # validate
- - name: attempt to use validate with older openshift
- k8s:
- definition:
- metadata:
- name: config-map-test
- namespace: "{{ playbook_namespace }}"
- apiVersion: v1
- kind: ConfigMap
- data:
- hello: world
- validate:
- fail_on_error: yes
- ignore_errors: yes
- register: k8s_validate
-
- - name: assert that validate fails gracefully
- assert:
- that:
- - k8s_validate is failed
- - "k8s_validate.msg == 'openshift >= 0.8.0 is required for validate'"
-
- # apply
- - name: attempt to use apply with older openshift
- k8s:
- definition:
- metadata:
- name: config-map-test
- namespace: "{{ playbook_namespace }}"
- apiVersion: v1
- kind: ConfigMap
- data:
- hello: world
- apply: yes
- ignore_errors: yes
- register: k8s_apply
-
- - name: assert that apply fails gracefully
- assert:
- that:
- - k8s_apply is failed
- - "k8s_apply.msg.startswith('Failed to import the required Python library (openshift >= 0.9.2)')"
diff --git a/test/integration/targets/k8s/tasks/openshift.yml b/test/integration/targets/k8s/tasks/openshift.yml
deleted file mode 100644
index f4a9006119..0000000000
--- a/test/integration/targets/k8s/tasks/openshift.yml
+++ /dev/null
@@ -1,61 +0,0 @@
-# OpenShift Resources
-- name: Create a project
- k8s:
- name: testing
- kind: Project
- api_version: v1
- apply: no
- register: output
-
-- name: show output
- debug:
- var: output
-
-- name: Create deployment config
- k8s:
- state: present
- inline: &dc
- apiVersion: v1
- kind: DeploymentConfig
- metadata:
- name: elastic
- labels:
- app: galaxy
- service: elastic
- namespace: testing
- spec:
- template:
- metadata:
- labels:
- app: galaxy
- service: elastic
- spec:
- containers:
- - name: elastic
- volumeMounts:
- - mountPath: /usr/share/elasticsearch/data
- name: elastic-volume
- command: ['elasticsearch']
- image: 'ansible/galaxy-elasticsearch:2.4.6'
- volumes:
- - name: elastic-volume
- persistentVolumeClaim:
- claimName: elastic-volume
- replicas: 1
- strategy:
- type: Rolling
- register: output
-
-- name: Show output
- debug:
- var: output
-
-- name: Create deployment config again
- k8s:
- state: present
- inline: *dc
- register: output
-
-- name: DC creation should be idempotent
- assert:
- that: not output.changed
diff --git a/test/integration/targets/k8s/tasks/validate_installed.yml b/test/integration/targets/k8s/tasks/validate_installed.yml
deleted file mode 100644
index 224bd2eb68..0000000000
--- a/test/integration/targets/k8s/tasks/validate_installed.yml
+++ /dev/null
@@ -1,125 +0,0 @@
-- block:
- - name: Create a namespace
- k8s:
- name: "{{ playbook_namespace }}"
- kind: Namespace
-
- - copy:
- src: files
- dest: "{{ remote_tmp_dir }}"
-
- - name: incredibly simple ConfigMap
- k8s:
- definition:
- apiVersion: v1
- kind: ConfigMap
- metadata:
- name: hello
- namespace: "{{ playbook_namespace }}"
- validate:
- fail_on_error: yes
- register: k8s_with_validate
-
- - name: assert that k8s_with_validate succeeds
- assert:
- that:
- - k8s_with_validate is successful
-
- - name: extra property does not fail without strict
- k8s:
- src: "{{ remote_tmp_dir }}/files/kuard-extra-property.yml"
- namespace: "{{ playbook_namespace }}"
- validate:
- fail_on_error: yes
- strict: no
-
- - name: extra property fails with strict
- k8s:
- src: "{{ remote_tmp_dir }}/files/kuard-extra-property.yml"
- namespace: "{{ playbook_namespace }}"
- validate:
- fail_on_error: yes
- strict: yes
- ignore_errors: yes
- register: extra_property
-
- - name: check that extra property fails with strict
- assert:
- that:
- - extra_property is failed
-
- - name: invalid type fails at validation stage
- k8s:
- src: "{{ remote_tmp_dir }}/files/kuard-invalid-type.yml"
- namespace: "{{ playbook_namespace }}"
- validate:
- fail_on_error: yes
- strict: no
- ignore_errors: yes
- register: invalid_type
-
- - name: check that invalid type fails
- assert:
- that:
- - invalid_type is failed
-
- - name: invalid type fails with warnings when fail_on_error is False
- k8s:
- src: "{{ remote_tmp_dir }}/files/kuard-invalid-type.yml"
- namespace: "{{ playbook_namespace }}"
- validate:
- fail_on_error: no
- strict: no
- ignore_errors: yes
- register: invalid_type_no_fail
-
- - name: check that invalid type fails
- assert:
- that:
- - invalid_type_no_fail is failed
-
- - name: setup custom resource definition
- k8s:
- src: "{{ remote_tmp_dir }}/files/setup-crd.yml"
-
- - name: wait a few seconds
- pause:
- seconds: 5
-
- - name: add custom resource definition
- k8s:
- src: "{{ remote_tmp_dir }}/files/crd-resource.yml"
- namespace: "{{ playbook_namespace }}"
- validate:
- fail_on_error: yes
- strict: yes
- register: unknown_kind
-
- - name: check that unknown kind warns
- assert:
- that:
- - unknown_kind is successful
- - "'warnings' in unknown_kind"
-
- always:
- - name: remove custom resource
- k8s:
- definition: "{{ lookup('file', role_path + '/files/crd-resource.yml') }}"
- namespace: "{{ playbook_namespace }}"
- state: absent
- ignore_errors: yes
-
- - name: remove custom resource definitions
- k8s:
- definition: "{{ lookup('file', role_path + '/files/setup-crd.yml') }}"
- state: absent
-
- - name: Delete namespace
- k8s:
- state: absent
- definition:
- - kind: Namespace
- apiVersion: v1
- metadata:
- name: "{{ playbook_namespace }}"
- ignore_errors: yes
diff --git a/test/integration/targets/k8s/tasks/validate_not_installed.yml b/test/integration/targets/k8s/tasks/validate_not_installed.yml
deleted file mode 100644
index ecd17f7ea9..0000000000
--- a/test/integration/targets/k8s/tasks/validate_not_installed.yml
+++ /dev/null
@@ -1,23 +0,0 @@
- - python_requirements_info:
- dependencies:
- - openshift
- - kubernetes
- - kubernetes-validate
-
- - k8s:
- definition:
- apiVersion: v1
- kind: ConfigMap
- metadata:
- name: hello
- namespace: default
- validate:
- fail_on_error: yes
- ignore_errors: yes
- register: k8s_no_validate
-
- - name: assert that k8s_no_validate fails gracefully
- assert:
- that:
- - k8s_no_validate is failed
- - "k8s_no_validate.msg == 'kubernetes-validate python library is required to validate resources'"
diff --git a/test/integration/targets/k8s/tasks/waiter.yml b/test/integration/targets/k8s/tasks/waiter.yml
deleted file mode 100644
index 757d7899ac..0000000000
--- a/test/integration/targets/k8s/tasks/waiter.yml
+++ /dev/null
@@ -1,355 +0,0 @@
-- name: ensure that there are actually some nodes
- k8s_info:
- kind: Node
- register: nodes
-
-- block:
- - set_fact:
- wait_namespace: wait
-
- - name: ensure namespace exists
- k8s:
- definition:
- apiVersion: v1
- kind: Namespace
- metadata:
- name: "{{ wait_namespace }}"
-
- - name: add a simple pod
- k8s:
- definition:
- apiVersion: v1
- kind: Pod
- metadata:
- name: "{{ k8s_pod_name }}"
- namespace: "{{ wait_namespace }}"
- spec: "{{ k8s_pod_spec }}"
- wait: yes
- vars:
- k8s_pod_name: wait-pod
- k8s_pod_image: alpine:3.8
- k8s_pod_command:
- - sleep
- - "10000"
- register: wait_pod
- ignore_errors: yes
-
- - name: assert that pod creation succeeded
- assert:
- that:
- - wait_pod is successful
-
- - name: add a daemonset
- k8s:
- definition:
- apiVersion: extensions/v1beta1
- kind: DaemonSet
- metadata:
- name: wait-daemonset
- namespace: "{{ wait_namespace }}"
- spec:
- selector:
- matchLabels:
- app: "{{ k8s_pod_name }}"
- template: "{{ k8s_pod_template }}"
- wait: yes
- wait_sleep: 3
- wait_timeout: 180
- vars:
- k8s_pod_name: wait-ds
- k8s_pod_image: gcr.io/kuar-demo/kuard-amd64:1
- register: ds
-
- - name: check that daemonset wait worked
- assert:
- that:
- - ds.result.status.currentNumberScheduled == ds.result.status.desiredNumberScheduled
-
- - name: update a daemonset in check_mode
- k8s:
- definition:
- apiVersion: extensions/v1beta1
- kind: DaemonSet
- metadata:
- name: wait-daemonset
- namespace: "{{ wait_namespace }}"
- spec:
- selector:
- matchLabels:
- app: "{{ k8s_pod_name }}"
- updateStrategy:
- type: RollingUpdate
- template: "{{ k8s_pod_template }}"
- wait: yes
- wait_sleep: 3
- wait_timeout: 180
- vars:
- k8s_pod_name: wait-ds
- k8s_pod_image: gcr.io/kuar-demo/kuard-amd64:2
- register: update_ds_check_mode
-
- - name: check that check_mode returned changed
- assert:
- that:
- - update_ds_check_mode is changed
-
- - name: update a daemonset
- k8s:
- definition:
- apiVersion: extensions/v1beta1
- kind: DaemonSet
- metadata:
- name: wait-daemonset
- namespace: "{{ wait_namespace }}"
- spec:
- selector:
- matchLabels:
- app: "{{ k8s_pod_name }}"
- updateStrategy:
- type: RollingUpdate
- template: "{{ k8s_pod_template }}"
- wait: yes
- wait_sleep: 3
- wait_timeout: 180
- vars:
- k8s_pod_name: wait-ds
- k8s_pod_image: gcr.io/kuar-demo/kuard-amd64:3
- register: ds
-
- - name: get updated pods
- k8s_info:
- api_version: v1
- kind: Pod
- namespace: "{{ wait_namespace }}"
- label_selectors:
- - app=wait-ds
- register: updated_ds_pods
-
- - name: check that daemonset wait worked
- assert:
- that:
- - ds.result.status.currentNumberScheduled == ds.result.status.desiredNumberScheduled
- - updated_ds_pods.resources[0].spec.containers[0].image.endswith(":3")
-
- - name: add a crashing pod
- k8s:
- definition:
- apiVersion: v1
- kind: Pod
- metadata:
- name: "{{ k8s_pod_name }}"
- namespace: "{{ wait_namespace }}"
- spec: "{{ k8s_pod_spec }}"
- wait: yes
- wait_sleep: 1
- wait_timeout: 30
- vars:
- k8s_pod_name: wait-crash-pod
- k8s_pod_image: alpine:3.8
- k8s_pod_command:
- - /bin/false
- register: crash_pod
- ignore_errors: yes
-
- - name: check that task failed
- assert:
- that:
- - crash_pod is failed
-
- - name: use a non-existent image
- k8s:
- definition:
- apiVersion: v1
- kind: Pod
- metadata:
- name: "{{ k8s_pod_name }}"
- namespace: "{{ wait_namespace }}"
- spec: "{{ k8s_pod_spec }}"
- wait: yes
- wait_sleep: 1
- wait_timeout: 30
- vars:
- k8s_pod_name: wait-no-image-pod
- k8s_pod_image: i_made_this_up:and_this_too
- register: no_image_pod
- ignore_errors: yes
-
- - name: check that task failed
- assert:
- that:
- - no_image_pod is failed
-
- - name: add a deployment
- k8s:
- definition:
- apiVersion: extensions/v1beta1
- kind: Deployment
- metadata:
- name: wait-deploy
- namespace: "{{ wait_namespace }}"
- spec:
- replicas: 3
- selector:
- matchLabels:
- app: "{{ k8s_pod_name }}"
- template: "{{ k8s_pod_template }}"
- wait: yes
- vars:
- k8s_pod_name: wait-deploy
- k8s_pod_image: gcr.io/kuar-demo/kuard-amd64:1
- k8s_pod_ports:
- - containerPort: 8080
- name: http
- protocol: TCP
-
- register: deploy
-
- - name: check that deployment wait worked
- assert:
- that:
- - deploy.result.status.availableReplicas == deploy.result.status.replicas
-
- - name: update a deployment
- k8s:
- definition:
- apiVersion: extensions/v1beta1
- kind: Deployment
- metadata:
- name: wait-deploy
- namespace: "{{ wait_namespace }}"
- spec:
- replicas: 3
- selector:
- matchLabels:
- app: "{{ k8s_pod_name }}"
- template: "{{ k8s_pod_template }}"
- wait: yes
- vars:
- k8s_pod_name: wait-deploy
- k8s_pod_image: gcr.io/kuar-demo/kuard-amd64:2
- k8s_pod_ports:
- - containerPort: 8080
- name: http
- protocol: TCP
- register: update_deploy
-
- - name: get updated pods
- k8s_info:
- api_version: v1
- kind: Pod
- namespace: "{{ wait_namespace }}"
- label_selectors:
- - app=wait-deploy
- register: updated_deploy_pods
-
- - name: check that deployment wait worked
- assert:
- that:
- - deploy.result.status.availableReplicas == deploy.result.status.replicas
- - updated_deploy_pods.resources[0].spec.containers[0].image.endswith(":2")
-
- - name: pause a deployment
- k8s:
- definition:
- apiVersion: extensions/v1beta1
- kind: Deployment
- metadata:
- name: wait-deploy
- namespace: "{{ wait_namespace }}"
- spec:
- paused: True
- apply: no
- wait: yes
- wait_condition:
- type: Progressing
- status: Unknown
- reason: DeploymentPaused
- register: pause_deploy
-
- - name: check that paused deployment wait worked
- assert:
- that:
- - condition.reason == "DeploymentPaused"
- - condition.status == "Unknown"
- vars:
- condition: '{{ pause_deploy.result.status.conditions | json_query("[?type==`Progressing`]") | first }}'
-
- - name: add a service based on the deployment
- k8s:
- definition:
- apiVersion: v1
- kind: Service
- metadata:
- name: wait-svc
- namespace: "{{ wait_namespace }}"
- spec:
- selector:
- app: "{{ k8s_pod_name }}"
- ports:
- - port: 8080
- targetPort: 8080
- protocol: TCP
- wait: yes
- vars:
- k8s_pod_name: wait-deploy
- register: service
-
- - name: assert that waiting for service works
- assert:
- that:
- - service is successful
-
- - name: add a crashing deployment
- k8s:
- definition:
- apiVersion: extensions/v1beta1
- kind: Deployment
- metadata:
- name: wait-crash-deploy
- namespace: "{{ wait_namespace }}"
- spec:
- replicas: 3
- selector:
- matchLabels:
- app: "{{ k8s_pod_name }}"
- template: "{{ k8s_pod_template }}"
- wait: yes
- vars:
- k8s_pod_name: wait-crash-deploy
- k8s_pod_image: alpine:3.8
- k8s_pod_command:
- - /bin/false
- register: wait_crash_deploy
- ignore_errors: yes
-
- - name: check that task failed
- assert:
- that:
- - wait_crash_deploy is failed
-
- - name: remove Pod with very short timeout
- k8s:
- api_version: v1
- kind: Pod
- name: wait-pod
- namespace: "{{ wait_namespace }}"
- state: absent
- wait: yes
- wait_sleep: 2
- wait_timeout: 5
- ignore_errors: yes
- register: short_wait_remove_pod
-
- - name: check that task failed
- assert:
- that:
- - short_wait_remove_pod is failed
-
- always:
- - name: remove namespace
- k8s:
- kind: Namespace
- name: "{{ wait_namespace }}"
- state: absent
-
- when: (nodes.resources | length) > 0
diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt
index c403c13468..e545d49f7b 100644
--- a/test/sanity/ignore.txt
+++ b/test/sanity/ignore.txt
@@ -96,9 +96,6 @@ lib/ansible/module_utils/ismount.py future-import-boilerplate
lib/ansible/module_utils/ismount.py metaclass-boilerplate
lib/ansible/module_utils/json_utils.py future-import-boilerplate
lib/ansible/module_utils/json_utils.py metaclass-boilerplate
-lib/ansible/module_utils/k8s/common.py metaclass-boilerplate
-lib/ansible/module_utils/k8s/raw.py metaclass-boilerplate
-lib/ansible/module_utils/k8s/scale.py metaclass-boilerplate
lib/ansible/module_utils/net_tools/netbox/netbox_utils.py future-import-boilerplate
lib/ansible/module_utils/netapp.py future-import-boilerplate
lib/ansible/module_utils/netapp.py metaclass-boilerplate
@@ -1677,26 +1674,6 @@ lib/ansible/modules/cloud/vmware_httpapi/vmware_cis_category_info.py validate-mo
lib/ansible/modules/cloud/vmware_httpapi/vmware_cis_category_info.py validate-modules:parameter-list-no-elements
lib/ansible/modules/cloud/vmware_httpapi/vmware_core_info.py validate-modules:doc-required-mismatch
lib/ansible/modules/cloud/vmware_httpapi/vmware_core_info.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/clustering/k8s/k8s.py validate-modules:doc-default-does-not-match-spec
-lib/ansible/modules/clustering/k8s/k8s.py validate-modules:doc-missing-type
-lib/ansible/modules/clustering/k8s/k8s.py validate-modules:doc-required-mismatch
-lib/ansible/modules/clustering/k8s/k8s.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/clustering/k8s/k8s.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/clustering/k8s/k8s.py validate-modules:return-syntax-error
-lib/ansible/modules/clustering/k8s/k8s_auth.py validate-modules:doc-missing-type
-lib/ansible/modules/clustering/k8s/k8s_auth.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/clustering/k8s/k8s_info.py validate-modules:doc-missing-type
-lib/ansible/modules/clustering/k8s/k8s_info.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/clustering/k8s/k8s_info.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/clustering/k8s/k8s_scale.py validate-modules:doc-missing-type
-lib/ansible/modules/clustering/k8s/k8s_scale.py validate-modules:doc-required-mismatch
-lib/ansible/modules/clustering/k8s/k8s_scale.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/clustering/k8s/k8s_scale.py validate-modules:return-syntax-error
-lib/ansible/modules/clustering/k8s/k8s_service.py validate-modules:doc-missing-type
-lib/ansible/modules/clustering/k8s/k8s_service.py validate-modules:mutually_exclusive-unknown
-lib/ansible/modules/clustering/k8s/k8s_service.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/clustering/k8s/k8s_service.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/clustering/k8s/k8s_service.py validate-modules:return-syntax-error
lib/ansible/modules/commands/command.py validate-modules:doc-missing-type
lib/ansible/modules/commands/command.py validate-modules:nonexistent-parameter-documented
lib/ansible/modules/commands/command.py validate-modules:parameter-list-no-elements
@@ -4287,16 +4264,6 @@ lib/ansible/plugins/doc_fragments/iosxr.py future-import-boilerplate
lib/ansible/plugins/doc_fragments/iosxr.py metaclass-boilerplate
lib/ansible/plugins/doc_fragments/junos.py future-import-boilerplate
lib/ansible/plugins/doc_fragments/junos.py metaclass-boilerplate
-lib/ansible/plugins/doc_fragments/k8s_auth_options.py future-import-boilerplate
-lib/ansible/plugins/doc_fragments/k8s_auth_options.py metaclass-boilerplate
-lib/ansible/plugins/doc_fragments/k8s_name_options.py future-import-boilerplate
-lib/ansible/plugins/doc_fragments/k8s_name_options.py metaclass-boilerplate
-lib/ansible/plugins/doc_fragments/k8s_resource_options.py future-import-boilerplate
-lib/ansible/plugins/doc_fragments/k8s_resource_options.py metaclass-boilerplate
-lib/ansible/plugins/doc_fragments/k8s_scale_options.py future-import-boilerplate
-lib/ansible/plugins/doc_fragments/k8s_scale_options.py metaclass-boilerplate
-lib/ansible/plugins/doc_fragments/k8s_state_options.py future-import-boilerplate
-lib/ansible/plugins/doc_fragments/k8s_state_options.py metaclass-boilerplate
lib/ansible/plugins/doc_fragments/meraki.py future-import-boilerplate
lib/ansible/plugins/doc_fragments/meraki.py metaclass-boilerplate
lib/ansible/plugins/doc_fragments/netapp.py future-import-boilerplate