Merge pull request #481 from ansible-collections/2.x-refactor

Refactor common.py
This commit is contained in:
Mike Graves
2022-06-23 15:28:44 -04:00
committed by GitHub
46 changed files with 3846 additions and 1662 deletions

View File

@@ -0,0 +1,3 @@
---
major_changes:
- refactor K8sAnsibleMixin into module_utils/k8s/ (https://github.com/ansible-collections/kubernetes.core/pull/481).

View File

@@ -130,6 +130,7 @@ except ImportError as e:
def configuration_digest(configuration, **kwargs):
"""This function has been deprecated and will be removed in version 4.0.0."""
m = hashlib.sha256()
for k in AUTH_ARG_MAP:
if not hasattr(configuration, k):
@@ -150,6 +151,8 @@ def configuration_digest(configuration, **kwargs):
class unique_string(str):
"""This function has been deprecated and will be removed in version 4.0.0."""
_low = None
def __hash__(self):
@@ -169,6 +172,7 @@ class unique_string(str):
def get_api_client(module=None, **kwargs):
"""This function has been deprecated and will be removed in version 4.0.0. Please use module_utils.k8s.client.get_api_client() instead."""
auth = {}
def _raise_or_fail(exc, msg):
@@ -340,6 +344,11 @@ get_api_client._pool = {}
class K8sAnsibleMixin(object):
def __init__(self, module, pyyaml_required=True, *args, **kwargs):
module.deprecate(
msg="The K8sAnsibleMixin class has been deprecated and refactored into the module_utils/k8s/ directory.",
version="4.0.0",
collection_name="kubernetes.core",
)
if not HAS_K8S_MODULE_HELPER:
module.fail_json(
msg=missing_required_lib("kubernetes"),

View File

@@ -24,6 +24,9 @@ from abc import ABCMeta, abstractmethod
import tarfile
# from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import AnsibleModule
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
)
from ansible.module_utils._text import to_native
try:
@@ -376,12 +379,17 @@ class K8SCopyToPod(K8SCopy):
)
def check_pod(k8s_ansible_mixin, module):
resource = k8s_ansible_mixin.find_resource("Pod", None, True)
def check_pod(svc):
module = svc.module
namespace = module.params.get("namespace")
name = module.params.get("pod")
container = module.params.get("container")
try:
resource = svc.find_resource("Pod", None, True)
except CoreException as e:
module.fail_json(msg=to_native(e))
def _fail(exc):
arg = {}
if hasattr(exc, "body"):
@@ -398,7 +406,7 @@ def check_pod(k8s_ansible_mixin, module):
module.fail_json(msg=msg, **arg)
try:
result = resource.get(name=name, namespace=namespace)
result = svc.client.get(resource, name=name, namespace=namespace)
containers = [
c["name"] for c in result.to_dict()["status"]["containerStatuses"]
]

View File

@@ -0,0 +1,355 @@
# Copyright: (c) 2021, Red Hat | Ansible
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
import os
import hashlib
from typing import Any, Dict, List, Optional
from ansible.module_utils.six import iteritems, string_types
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
AUTH_ARG_MAP,
AUTH_ARG_SPEC,
AUTH_PROXY_HEADERS_SPEC,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
requires as _requires,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
)
try:
from ansible_collections.kubernetes.core.plugins.module_utils import (
k8sdynamicclient,
)
from ansible_collections.kubernetes.core.plugins.module_utils.client.discovery import (
LazyDiscoverer,
)
except ImportError:
# Handled in module setup
pass
try:
import kubernetes
from kubernetes.dynamic.exceptions import (
ResourceNotFoundError,
ResourceNotUniqueError,
)
from kubernetes.dynamic.resource import Resource
except ImportError:
# kubernetes import error is handled in module setup
# This is defined only for the sake of Ansible's checked import requirement
Resource = Any # type: ignore
try:
import urllib3
urllib3.disable_warnings()
except ImportError:
# Handled in module setup
pass
_pool = {}
class unique_string(str):
_low = None
def __hash__(self):
return id(self)
def __eq__(self, other):
return self is other
def lower(self):
if self._low is None:
lower = str.lower(self)
if str.__eq__(lower, self):
self._low = self
else:
self._low = unique_string(lower)
return self._low
def _create_auth_spec(module=None, **kwargs) -> Dict:
auth: Dict = {}
# If authorization variables aren't defined, look for them in environment variables
for true_name, arg_name in AUTH_ARG_MAP.items():
if module and module.params.get(arg_name) is not None:
auth[true_name] = module.params.get(arg_name)
elif arg_name in kwargs and kwargs.get(arg_name) is not None:
auth[true_name] = kwargs.get(arg_name)
elif arg_name == "proxy_headers":
# specific case for 'proxy_headers' which is a dictionary
proxy_headers = {}
for key in AUTH_PROXY_HEADERS_SPEC.keys():
env_value = os.getenv(
"K8S_AUTH_PROXY_HEADERS_{0}".format(key.upper()), None
)
if env_value is not None:
if AUTH_PROXY_HEADERS_SPEC[key].get("type") == "bool":
env_value = env_value.lower() not in ["0", "false", "no"]
proxy_headers[key] = env_value
if proxy_headers is not {}:
auth[true_name] = proxy_headers
else:
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
return auth
def _load_config(auth: Dict) -> None:
kubeconfig = auth.get("kubeconfig")
optional_arg = {
"context": auth.get("context"),
"persist_config": auth.get("persist_config"),
}
if kubeconfig:
if isinstance(kubeconfig, string_types):
kubernetes.config.load_kube_config(config_file=kubeconfig, **optional_arg)
elif isinstance(kubeconfig, dict):
kubernetes.config.load_kube_config_from_dict(
config_dict=kubeconfig, **optional_arg
)
else:
kubernetes.config.load_kube_config(config_file=None, **optional_arg)
def _create_configuration(auth: Dict):
def auth_set(*names: list) -> bool:
return all(auth.get(name) for name in names)
if auth_set("host"):
# Removing trailing slashes if any from hostname
auth["host"] = auth.get("host").rstrip("/")
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"):
try:
_load_config(auth)
except Exception as err:
raise err
else:
# First try to do incluster config, then kubeconfig
try:
kubernetes.config.load_incluster_config()
except kubernetes.config.ConfigException:
try:
_load_config(auth)
except Exception as err:
raise err
# Override any values in the default configuration with Ansible parameters
# As of kubernetes-client v12.0.0, get_default_copy() is required here
try:
configuration = kubernetes.client.Configuration().get_default_copy()
except AttributeError:
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)}
)
elif key == "proxy_headers":
headers = urllib3.util.make_headers(**value)
setattr(configuration, key, headers)
else:
setattr(configuration, key, value)
return configuration
def _create_headers(module=None, **kwargs):
header_map = {
"impersonate_user": "Impersonate-User",
"impersonate_groups": "Impersonate-Group",
}
headers = {}
for arg_name, header_name in header_map.items():
value = None
if module and module.params.get(arg_name) is not None:
value = module.params.get(arg_name)
elif arg_name in kwargs and kwargs.get(arg_name) is not None:
value = kwargs.get(arg_name)
else:
value = os.getenv("K8S_AUTH_{0}".format(arg_name.upper()), None)
if value is not None:
if AUTH_ARG_SPEC[arg_name].get("type") == "list":
value = [x for x in value.split(",") if x != ""]
if value:
headers[header_name] = value
return headers
def _configuration_digest(configuration, **kwargs) -> str:
m = hashlib.sha256()
for k in AUTH_ARG_MAP:
if not hasattr(configuration, k):
v = None
else:
v = getattr(configuration, k)
if v and k in ["ssl_ca_cert", "cert_file", "key_file"]:
with open(str(v), "r") as fd:
content = fd.read()
m.update(content.encode())
else:
m.update(str(v).encode())
for k, v in kwargs.items():
content = "{0}: {1}".format(k, v)
m.update(content.encode())
digest = m.hexdigest()
return digest
def _set_header(client, header, value):
if isinstance(value, list):
for v in value:
client.set_default_header(header_name=unique_string(header), header_value=v)
else:
client.set_default_header(header_name=header, header_value=value)
def cache(func):
def wrapper(*args, **kwargs):
client = None
hashable_kwargs = {}
for k, v in kwargs.items():
if isinstance(v, list):
hashable_kwargs[k] = ",".join(sorted(v))
else:
hashable_kwargs[k] = v
digest = _configuration_digest(*args, **hashable_kwargs)
if digest in _pool:
client = _pool[digest]
else:
client = func(*args, **kwargs)
_pool[digest] = client
return client
return wrapper
@cache
def create_api_client(configuration, **headers):
client = kubernetes.client.ApiClient(configuration)
for header, value in headers.items():
_set_header(client, header, value)
return k8sdynamicclient.K8SDynamicClient(client, discoverer=LazyDiscoverer)
class K8SClient:
"""A Client class for K8S modules.
This class has the primary purpose to proxy the kubernetes client and resource objects.
If there is a need for other methods or attributes to be proxied, they can be added here.
"""
def __init__(self, configuration, client, dry_run: bool = False) -> None:
self.configuration = configuration
self.client = client
self.dry_run = dry_run
@property
def resources(self) -> List[Any]:
return self.client.resources
def _find_resource_with_prefix(
self, prefix: str, kind: str, api_version: str
) -> Resource:
for attribute in ["kind", "name", "singular_name"]:
try:
return self.client.resources.get(
**{"prefix": prefix, "api_version": api_version, attribute: kind}
)
except (ResourceNotFoundError, ResourceNotUniqueError):
pass
return self.client.resources.get(
prefix=prefix, api_version=api_version, short_names=[kind]
)
def resource(self, kind: str, api_version: str) -> Resource:
"""Fetch a kubernetes client resource.
This will attempt to find a kubernetes resource trying, in order, kind,
name, singular_name and short_names.
"""
try:
if api_version == "v1":
return self._find_resource_with_prefix("api", kind, api_version)
except ResourceNotFoundError:
pass
return self._find_resource_with_prefix(None, kind, api_version)
def _ensure_dry_run(self, params: Dict) -> Dict:
if self.dry_run:
params["dry_run"] = True
return params
def validate(
self, resource, version: Optional[str] = None, strict: Optional[bool] = False
):
return self.client.validate(resource, version, strict)
def get(self, resource, **params):
return resource.get(**params)
def delete(self, resource, **params):
return resource.delete(**self._ensure_dry_run(params))
def apply(self, resource, definition, namespace, **params):
return resource.apply(
definition, namespace=namespace, **self._ensure_dry_run(params)
)
def create(self, resource, definition, **params):
return resource.create(definition, **self._ensure_dry_run(params))
def replace(self, resource, definition, **params):
return resource.replace(definition, **self._ensure_dry_run(params))
def patch(self, resource, definition, **params):
return resource.patch(definition, **self._ensure_dry_run(params))
def get_api_client(module=None, **kwargs: Optional[Any]) -> K8SClient:
auth_spec = _create_auth_spec(module, **kwargs)
if module:
requires = module.requires
else:
requires = _requires
if isinstance(auth_spec.get("kubeconfig"), dict):
requires("kubernetes", "17.17.0", "to use in-memory config")
if auth_spec.get("no_proxy"):
requires("kubernetes", "19.15.0", "to use the no_proxy feature")
try:
configuration = _create_configuration(auth_spec)
headers = _create_headers(module, **kwargs)
client = create_api_client(configuration, **headers)
except kubernetes.config.ConfigException as e:
msg = "Could not create API client: {0}".format(e)
raise CoreException(msg) from e
k8s_client = K8SClient(
configuration=configuration,
client=client,
dry_run=module.params.get("dry_run", False),
)
return k8s_client

View File

@@ -0,0 +1,171 @@
import traceback
from typing import Optional
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
LooseVersion,
)
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import missing_required_lib
from ansible.module_utils.common.text.converters import to_text
class AnsibleK8SModule:
"""A base module class for K8S modules.
This class should be used instead of directly using AnsibleModule. If there
is a need for other methods or attributes to be proxied, they can be added
here.
"""
default_settings = {
"check_k8s": True,
"check_pyyaml": True,
"module_class": AnsibleModule,
}
def __init__(self, **kwargs) -> None:
local_settings = {}
for key in AnsibleK8SModule.default_settings:
try:
local_settings[key] = kwargs.pop(key)
except KeyError:
local_settings[key] = AnsibleK8SModule.default_settings[key]
self.settings = local_settings
self._module = self.settings["module_class"](**kwargs)
if self.settings["check_k8s"]:
self.requires("kubernetes")
self.has_at_least("kubernetes", "12.0.0", warn=True)
if self.settings["check_pyyaml"]:
self.requires("pyyaml")
@property
def check_mode(self):
return self._module.check_mode
@property
def _diff(self):
return self._module._diff
@property
def _name(self):
return self._module._name
@property
def params(self):
return self._module.params
def warn(self, *args, **kwargs):
return self._module.warn(*args, **kwargs)
def deprecate(self, *args, **kwargs):
return self._module.deprecate(*args, **kwargs)
def debug(self, *args, **kwargs):
return self._module.debug(*args, **kwargs)
def exit_json(self, *args, **kwargs):
return self._module.exit_json(*args, **kwargs)
def fail_json(self, *args, **kwargs):
return self._module.fail_json(*args, **kwargs)
def fail_from_exception(self, exception):
msg = to_text(exception)
tb = "".join(
traceback.format_exception(None, exception, exception.__traceback__)
)
return self.fail_json(msg=msg, exception=tb)
def has_at_least(
self, dependency: str, minimum: Optional[str] = None, warn: bool = False
) -> bool:
supported = has_at_least(dependency, minimum)
if not supported and warn:
self.warn(
"{0}<{1} is not supported or tested. Some features may not work.".format(
dependency, minimum
)
)
return supported
def requires(
self,
dependency: str,
minimum: Optional[str] = None,
reason: Optional[str] = None,
) -> None:
try:
requires(dependency, minimum, reason=reason)
except Exception as e:
self.fail_json(msg=to_text(e))
def gather_versions() -> dict:
versions = {}
try:
import jsonpatch
versions["jsonpatch"] = jsonpatch.__version__
except ImportError:
pass
try:
import kubernetes
versions["kubernetes"] = kubernetes.__version__
except ImportError:
pass
try:
import kubernetes_validate
versions["kubernetes-validate"] = kubernetes_validate.__version__
except ImportError:
pass
try:
import yaml
versions["pyyaml"] = yaml.__version__
except ImportError:
pass
return versions
def has_at_least(dependency: str, minimum: Optional[str] = None) -> bool:
"""Check if a specific dependency is present at a minimum version.
If a minimum version is not specified it will check only that the
dependency is present.
"""
dependencies = gather_versions()
current = dependencies.get(dependency)
if current is not None:
if minimum is None:
return True
supported = LooseVersion(current) >= LooseVersion(minimum)
return supported
return False
def requires(
dependency: str, minimum: Optional[str] = None, reason: Optional[str] = None
) -> None:
"""Fail if a specific dependency is not present at a minimum version.
If a minimum version is not specified it will require only that the
dependency is present. This function raises an exception when the
dependency is not found at the required version.
"""
if not has_at_least(dependency, minimum):
if minimum is not None:
lib = "{0}>={1}".format(dependency, minimum)
else:
lib = dependency
raise Exception(missing_required_lib(lib, reason=reason))

View File

@@ -0,0 +1,12 @@
# Copyright: (c) 2021, Red Hat | Ansible
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
class CoreException(Exception):
pass
class ResourceTimeout(CoreException):
def __init__(self, message="", result=None):
self.result = result or {}
super().__init__(message)

View File

@@ -0,0 +1,129 @@
# Copyright: (c) 2021, Red Hat | Ansible
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
import os
from typing import cast, Dict, Iterable, List, Optional, Union
from ansible.module_utils.six import string_types
try:
import yaml
except ImportError:
# Handled in module setup
pass
class ResourceDefinition(dict):
"""Representation of a resource definition.
This is a thin wrapper around a dictionary representation of a resource
definition, with a few properties defined for conveniently accessing the
commonly used fields.
"""
@property
def kind(self) -> Optional[str]:
return self.get("kind")
@property
def api_version(self) -> Optional[str]:
return self.get("apiVersion")
@property
def namespace(self) -> Optional[str]:
metadata = self.get("metadata", {})
return metadata.get("namespace")
@property
def name(self) -> Optional[str]:
metadata = self.get("metadata", {})
return metadata.get("name")
def create_definitions(params: Dict) -> List[ResourceDefinition]:
"""Create a list of ResourceDefinitions from module inputs.
This will take the module's inputs and return a list of ResourceDefintion
objects. The resource definitions returned by this function should be as
complete a definition as we can create based on the input. Any *List kinds
will be removed and replaced by the resources contained in it.
"""
if params.get("resource_definition"):
d = cast(Union[str, List, Dict], params.get("resource_definition"))
definitions = from_yaml(d)
elif params.get("src"):
d = cast(str, params.get("src"))
definitions = from_file(d)
else:
# We'll create an empty definition and let merge_params set values
# from the module parameters.
definitions = [{}]
resource_definitions: List[Dict] = []
for definition in definitions:
merge_params(definition, params)
kind = cast(Optional[str], definition.get("kind"))
if kind and kind.endswith("List"):
resource_definitions += flatten_list_kind(definition, params)
else:
resource_definitions.append(definition)
return list(map(ResourceDefinition, resource_definitions))
def from_yaml(definition: Union[str, List, Dict]) -> Iterable[Dict]:
"""Load resource definitions from a yaml definition."""
definitions: List[Dict] = []
if isinstance(definition, string_types):
definitions += yaml.safe_load_all(definition)
elif isinstance(definition, list):
for item in definition:
if isinstance(item, string_types):
definitions += yaml.safe_load_all(item)
else:
definitions.append(item)
else:
definition = cast(Dict, definition)
definitions.append(definition)
return filter(None, definitions)
def from_file(filepath: str) -> Iterable[Dict]:
"""Load resource definitions from a path to a yaml file."""
path = os.path.normpath(filepath)
with open(path, "rb") as f:
definitions = list(yaml.safe_load_all(f))
return filter(None, definitions)
def merge_params(definition: Dict, params: Dict) -> Dict:
"""Merge module parameters with the resource definition.
Fields in the resource definition take precedence over module parameters.
"""
definition.setdefault("kind", params.get("kind"))
definition.setdefault("apiVersion", params.get("api_version"))
metadata = definition.setdefault("metadata", {})
# The following should only be set if we have values for them
if params.get("namespace"):
metadata.setdefault("namespace", params.get("namespace"))
if params.get("name"):
metadata.setdefault("name", params.get("name"))
if params.get("generate_name"):
metadata.setdefault("generateName", params.get("generate_name"))
return definition
def flatten_list_kind(definition: Dict, params: Dict) -> List[Dict]:
"""Replace *List kind with the items it contains.
This will take a definition for a *List resource and return a list of
definitions for the items contained within the List.
"""
items = []
kind = cast(str, definition.get("kind"))[:-4]
api_version = definition.get("apiVersion")
for item in definition.get("items", []):
item.setdefault("kind", kind)
item.setdefault("apiVersion", api_version)
items.append(merge_params(item, params))
return items

View File

@@ -0,0 +1,199 @@
# Copyright: (c) 2021, Red Hat | Ansible
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from typing import Dict
from ansible.module_utils._text import to_native
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
get_api_client,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.resource import (
create_definitions,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
K8sService,
diff_objects,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
ResourceTimeout,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.waiter import exists
from ansible_collections.kubernetes.core.plugins.module_utils.selector import (
LabelSelectorFilter,
)
def validate(client, module, resource):
def _prepend_resource_info(resource, msg):
return "%s %s: %s" % (resource["kind"], resource["metadata"]["name"], msg)
module.requires("kubernetes-validate")
warnings, errors = client.validate(
resource,
module.params["validate"].get("version"),
module.params["validate"].get("strict"),
)
if errors and module.params["validate"]["fail_on_error"]:
module.fail_json(
msg="\n".join([_prepend_resource_info(resource, error) for error in errors])
)
return [_prepend_resource_info(resource, msg) for msg in warnings + errors]
def run_module(module) -> None:
results = []
changed = False
client = get_api_client(module)
svc = K8sService(client, module)
try:
definitions = create_definitions(module.params)
except Exception as e:
msg = "Failed to load resource definition: {0}".format(e)
raise CoreException(msg) from e
for definition in definitions:
result = {"changed": False, "result": {}}
warnings = []
if module.params.get("validate") is not None:
warnings = validate(client, module, definition)
try:
result = perform_action(svc, definition, module.params)
except Exception as e:
try:
error = e.result
except AttributeError:
error = {}
try:
error["reason"] = e.__cause__.reason
except AttributeError:
pass
error["msg"] = to_native(e)
if warnings:
error.setdefault("warnings", []).extend(warnings)
if module.params.get("continue_on_error"):
result["error"] = error
else:
module.fail_json(**error)
if warnings:
result.setdefault("warnings", []).extend(warnings)
changed |= result["changed"]
results.append(result)
if len(results) == 1:
module.exit_json(**results[0])
module.exit_json(**{"changed": changed, "result": {"results": results}})
def perform_action(svc, definition: Dict, params: Dict) -> Dict:
origin_name = definition["metadata"].get("name")
namespace = definition["metadata"].get("namespace")
label_selectors = params.get("label_selectors")
state = params.get("state", None)
kind = definition.get("kind")
api_version = definition.get("apiVersion")
result = {"changed": False, "result": {}}
instance = {}
resource = svc.find_resource(kind, api_version, fail=True)
definition["kind"] = resource.kind
definition["apiVersion"] = resource.group_version
existing = svc.retrieve(resource, definition)
if state == "absent":
if exists(existing) and existing.kind.endswith("List"):
instance = []
for item in existing.items:
r = svc.delete(resource, item, existing)
instance.append(r)
else:
instance = svc.delete(resource, definition, existing)
result["method"] = "delete"
if exists(existing):
result["changed"] = True
else:
if label_selectors:
filter_selector = LabelSelectorFilter(label_selectors)
if not filter_selector.isMatching(definition):
result["changed"] = False
result["msg"] = (
"resource 'kind={kind},name={name},namespace={namespace}' "
"filtered by label_selectors.".format(
kind=kind,
name=origin_name,
namespace=namespace,
)
)
return result
if params.get("apply"):
instance = svc.apply(resource, definition, existing)
result["method"] = "apply"
elif not existing:
if state == "patched":
result.setdefault("warnings", []).append(
"resource 'kind={kind},name={name}' was not found but will not be "
"created as 'state' parameter has been set to '{state}'".format(
kind=kind, name=definition["metadata"].get("name"), state=state
)
)
return result
instance = svc.create(resource, definition)
result["method"] = "create"
result["changed"] = True
elif params.get("force", False):
instance = svc.replace(resource, definition, existing)
result["method"] = "replace"
else:
instance = svc.update(resource, definition, existing)
result["method"] = "update"
# If needed, wait and/or create diff
success = True
if result["method"] == "delete":
# wait logic is a bit different for delete as `instance` may be a status object
if params.get("wait") and not svc.module.check_mode:
success, waited, duration = svc.wait(resource, definition)
result["duration"] = duration
else:
if params.get("wait") and not svc.module.check_mode:
success, instance, duration = svc.wait(resource, instance)
result["duration"] = duration
if result["method"] not in ("create", "delete"):
if existing:
existing = existing.to_dict()
else:
existing = {}
match, diffs = diff_objects(existing, instance)
if match and diffs:
result.setdefault("warnings", []).append(
"No meaningful diff was generated, but the API may not be idempotent "
"(only metadata.generation or metadata.resourceVersion were changed)"
)
result["changed"] = not match
if svc.module._diff:
result["diff"] = diffs
result["result"] = instance
if not success:
raise ResourceTimeout(
'"{0}" "{1}": Timed out waiting on resource'.format(
definition["kind"], origin_name
),
result,
)
return result

View File

@@ -0,0 +1,502 @@
# Copyright: (c) 2021, Red Hat | Ansible
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from typing import Any, Dict, List, Optional, Tuple
from ansible_collections.kubernetes.core.plugins.module_utils.hashes import (
generate_hash,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.waiter import (
Waiter,
exists,
resource_absent,
get_waiter,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
requires,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
)
from ansible.module_utils.common.dict_transformations import dict_merge
from ansible.module_utils._text import to_native
try:
from kubernetes.dynamic.exceptions import (
NotFoundError,
ResourceNotFoundError,
ResourceNotUniqueError,
ConflictError,
ForbiddenError,
MethodNotAllowedError,
BadRequestError,
)
except ImportError:
# Handled in module setup
pass
try:
from kubernetes.dynamic.resource import Resource, ResourceInstance
except ImportError:
# These are defined only for the sake of Ansible's checked import requirement
Resource = Any # type: ignore
ResourceInstance = Any # type: ignore
try:
from ansible_collections.kubernetes.core.plugins.module_utils.apply import (
apply_object,
)
except ImportError:
# Handled in module setup
pass
try:
from ansible_collections.kubernetes.core.plugins.module_utils.apply import (
recursive_diff,
)
except ImportError:
from ansible.module_utils.common.dict_transformations import recursive_diff
try:
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
_encode_stringdata,
)
except ImportError:
# Handled in module setup
pass
class K8sService:
"""A Service class for K8S modules.
This class has the primary purpose is to perform work on the cluster (e.g., create, apply, replace, update, delete).
"""
def __init__(self, client, module) -> None:
self.client = client
self.module = module
def find_resource(
self, kind: str, api_version: str, fail: bool = False
) -> Optional[Resource]:
try:
return self.client.resource(kind, api_version)
except (ResourceNotFoundError, ResourceNotUniqueError):
if fail:
raise CoreException(
"Failed to find exact match for %s.%s by [kind, name, singularName, shortNames]"
% (api_version, kind)
)
def wait(
self, resource: Resource, instance: Dict
) -> Tuple[bool, Optional[Dict], int]:
wait_sleep = self.module.params.get("wait_sleep")
wait_timeout = self.module.params.get("wait_timeout")
wait_condition = None
if self.module.params.get("wait_condition") and self.module.params[
"wait_condition"
].get("type"):
wait_condition = self.module.params["wait_condition"]
state = "present"
if self.module.params.get("state") == "absent":
state = "absent"
label_selectors = self.module.params.get("label_selectors")
waiter = get_waiter(
self.client, resource, condition=wait_condition, state=state
)
return waiter.wait(
timeout=wait_timeout,
sleep=wait_sleep,
name=instance["metadata"].get("name"),
namespace=instance["metadata"].get("namespace"),
label_selectors=label_selectors,
)
def create_project_request(self, definition: Dict) -> Dict:
definition["kind"] = "ProjectRequest"
results = {"changed": False, "result": {}}
resource = self.find_resource(
"ProjectRequest", definition["apiVersion"], fail=True
)
if not self.module.check_mode:
try:
k8s_obj = self.client.create(resource, definition)
results["result"] = k8s_obj.to_dict()
except Exception as e:
reason = e.body if hasattr(e, "body") else e
msg = "Failed to create object: {0}".format(reason)
raise CoreException(msg) from e
results["changed"] = True
return results
def patch_resource(
self,
resource: Resource,
definition: Dict,
name: str,
namespace: str,
merge_type: str = None,
) -> Dict:
if merge_type == "json":
self.module.deprecate(
msg="json as a merge_type value is deprecated. Please use the k8s_json_patch module instead.",
version="3.0.0",
collection_name="kubernetes.core",
)
try:
params = dict(name=name, namespace=namespace)
if self.module.check_mode:
params["dry_run"] = "All"
if merge_type:
params["content_type"] = "application/{0}-patch+json".format(merge_type)
return self.client.patch(resource, definition, **params).to_dict()
except Exception as e:
reason = e.body if hasattr(e, "body") else e
msg = "Failed to patch object: {0}".format(reason)
raise CoreException(msg) from e
def retrieve(self, resource: Resource, definition: Dict) -> ResourceInstance:
state = self.module.params.get("state", None)
append_hash = self.module.params.get("append_hash", False)
name = definition["metadata"].get("name")
generate_name = definition["metadata"].get("generateName")
namespace = definition["metadata"].get("namespace")
label_selectors = self.module.params.get("label_selectors")
existing: ResourceInstance = None
try:
# ignore append_hash for resources other than ConfigMap and Secret
if append_hash and definition["kind"] in ["ConfigMap", "Secret"]:
if name:
name = "%s-%s" % (name, generate_hash(definition))
definition["metadata"]["name"] = name
elif generate_name:
definition["metadata"]["generateName"] = "%s-%s" % (
generate_name,
generate_hash(definition),
)
params = {}
if name:
params["name"] = name
if namespace:
params["namespace"] = namespace
if label_selectors:
params["label_selector"] = ",".join(label_selectors)
if "name" in params or "label_selector" in params:
existing = self.client.get(resource, **params)
except (NotFoundError, MethodNotAllowedError):
pass
except ForbiddenError as e:
if (
definition["kind"] in ["Project", "ProjectRequest"]
and state != "absent"
):
return self.create_project_request(definition)
reason = e.body if hasattr(e, "body") else e
msg = "Failed to retrieve requested object: {0}".format(reason)
raise CoreException(msg) from e
except Exception as e:
reason = e.body if hasattr(e, "body") else e
msg = "Failed to retrieve requested object: {0}".format(reason)
raise CoreException(msg) from e
return existing
def find(
self,
kind: str,
api_version: str,
name: str = None,
namespace: Optional[str] = None,
label_selectors: Optional[List[str]] = None,
field_selectors: Optional[List[str]] = None,
wait: Optional[bool] = False,
wait_sleep: Optional[int] = 5,
wait_timeout: Optional[int] = 120,
state: Optional[str] = "present",
condition: Optional[Dict] = None,
) -> Dict:
resource = self.find_resource(kind, api_version)
api_found = bool(resource)
if not api_found:
return dict(
resources=[],
msg='Failed to find API for resource with apiVersion "{0}" and kind "{1}"'.format(
api_version, kind
),
api_found=False,
)
if not label_selectors:
label_selectors = []
if not field_selectors:
field_selectors = []
result = {"resources": [], "api_found": True}
# With a timeout of 0 the waiter will do a single check and return, effectively not waiting.
if not wait:
wait_timeout = 0
if state == "present":
predicate = exists
else:
predicate = resource_absent
waiter = Waiter(self.client, resource, predicate)
# This is an initial check to get the resource or resources that we then need to wait on individually.
try:
success, resources, duration = waiter.wait(
timeout=wait_timeout,
sleep=wait_sleep,
name=name,
namespace=namespace,
label_selectors=label_selectors,
field_selectors=field_selectors,
)
except BadRequestError:
return result
except CoreException as e:
result["msg"] = to_native(e)
return result
# There is either no result or there is a List resource with no items
if (
not resources
or resources["kind"].endswith("List")
and not resources.get("items")
):
return result
instances = resources.get("items") or [resources]
if not wait:
result["resources"] = instances
return result
# Now wait for the specified state of any resource instances we have found.
waiter = get_waiter(self.client, resource, state=state, condition=condition)
for instance in instances:
name = instance["metadata"].get("name")
namespace = instance["metadata"].get("namespace")
success, res, duration = waiter.wait(
timeout=wait_timeout,
sleep=wait_sleep,
name=name,
namespace=namespace,
)
if not success:
raise CoreException(
"Failed to gather information about %s(s) even"
" after waiting for %s seconds" % (res.get("kind"), duration)
)
result["resources"].append(res)
return result
def create(self, resource: Resource, definition: Dict) -> Dict:
namespace = definition["metadata"].get("namespace")
name = definition["metadata"].get("name")
results = {"changed": False, "result": {}}
if self.module.check_mode and not self.client.dry_run:
k8s_obj = _encode_stringdata(definition)
else:
params = {}
if self.module.check_mode:
params["dry_run"] = "All"
try:
k8s_obj = self.client.create(
resource, definition, namespace=namespace, **params
).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.module.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 results
except Exception as e:
reason = e.body if hasattr(e, "body") else e
msg = "Failed to create object: {0}".format(reason)
raise CoreException(msg) from e
return k8s_obj
def apply(
self,
resource: Resource,
definition: Dict,
existing: Optional[ResourceInstance] = None,
) -> Dict:
namespace = definition["metadata"].get("namespace")
server_side_apply = self.module.params.get("server_side_apply")
if server_side_apply:
requires("kubernetes", "19.15.0", reason="to use server side apply")
if self.module.check_mode and not self.client.dry_run:
ignored, patch = apply_object(resource, _encode_stringdata(definition))
if existing:
k8s_obj = dict_merge(existing.to_dict(), patch)
else:
k8s_obj = patch
else:
try:
params = {}
if self.module.check_mode:
params["dry_run"] = "All"
if server_side_apply:
params["server_side"] = True
params.update(server_side_apply)
k8s_obj = self.client.apply(
resource, definition, namespace=namespace, **params
).to_dict()
except Exception as e:
reason = e.body if hasattr(e, "body") else e
msg = "Failed to apply object: {0}".format(reason)
raise CoreException(msg) from e
return k8s_obj
def replace(
self,
resource: Resource,
definition: Dict,
existing: ResourceInstance,
) -> Dict:
append_hash = self.module.params.get("append_hash", False)
name = definition["metadata"].get("name")
namespace = definition["metadata"].get("namespace")
if self.module.check_mode and not self.module.client.dry_run:
k8s_obj = _encode_stringdata(definition)
else:
params = {}
if self.module.check_mode:
params["dry_run"] = "All"
try:
k8s_obj = self.client.replace(
resource,
definition,
name=name,
namespace=namespace,
append_hash=append_hash,
**params
).to_dict()
except Exception as e:
reason = e.body if hasattr(e, "body") else e
msg = "Failed to replace object: {0}".format(reason)
raise CoreException(msg) from e
return k8s_obj
def update(
self, resource: Resource, definition: Dict, existing: ResourceInstance
) -> Dict:
name = definition["metadata"].get("name")
namespace = definition["metadata"].get("namespace")
if self.module.check_mode and not self.client.dry_run:
k8s_obj = dict_merge(existing.to_dict(), _encode_stringdata(definition))
else:
exception = None
for merge_type in self.module.params.get("merge_type") or [
"strategic-merge",
"merge",
]:
try:
k8s_obj = self.patch_resource(
resource,
definition,
name,
namespace,
merge_type=merge_type,
)
exception = None
except CoreException as e:
exception = e
continue
break
if exception:
raise exception
return k8s_obj
def delete(
self,
resource: Resource,
definition: Dict,
existing: Optional[ResourceInstance] = None,
) -> Dict:
delete_options = self.module.params.get("delete_options")
label_selectors = self.module.params.get("label_selectors")
name = definition["metadata"].get("name")
namespace = definition["metadata"].get("namespace")
params = {}
if not exists(existing):
return {}
# Delete the object
if self.module.check_mode and not self.client.dry_run:
return {}
if name:
params["name"] = name
if namespace:
params["namespace"] = namespace
if label_selectors:
params["label_selector"] = ",".join(label_selectors)
if delete_options:
body = {
"apiVersion": "v1",
"kind": "DeleteOptions",
}
body.update(delete_options)
params["body"] = body
if self.module.check_mode:
params["dry_run"] = "All"
try:
k8s_obj = self.client.delete(resource, **params).to_dict()
except Exception as e:
reason = e.body if hasattr(e, "body") else e
msg = "Failed to delete object: {0}".format(reason)
raise CoreException(msg) from e
return k8s_obj
def diff_objects(existing: Dict, new: Dict) -> Tuple[bool, Dict]:
result = {}
diff = recursive_diff(existing, new)
if not diff:
return True, result
result["before"] = diff[0]
result["after"] = diff[1]
if list(result["after"].keys()) != ["metadata"] or list(
result["before"].keys()
) != ["metadata"]:
return False, result
# If only metadata.generation and metadata.resourceVersion changed, ignore it
ignored_keys = set(["generation", "resourceVersion"])
if not set(result["after"]["metadata"].keys()).issubset(ignored_keys):
return False, result
if not set(result["before"]["metadata"].keys()).issubset(ignored_keys):
return False, result
return True, result

View File

@@ -0,0 +1,238 @@
import time
from functools import partial
from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Union
from ansible.module_utils.parsing.convert_bool import boolean
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
)
try:
from kubernetes.dynamic.exceptions import NotFoundError
from kubernetes.dynamic.resource import Resource, ResourceField, ResourceInstance
except ImportError:
# These are defined only for the sake of Ansible's checked import requirement
Resource = Any # type: ignore
ResourceInstance = Any # type: ignore
pass
try:
from urllib3.exceptions import HTTPError
except ImportError:
# Handled during module setup
pass
def deployment_ready(deployment: ResourceInstance) -> bool:
# FIXME: frustratingly bool(deployment.status) is True even if status is empty
# Furthermore deployment.status.availableReplicas == deployment.status.replicas == None if status is empty
# deployment.status.replicas is None is perfectly ok if desired replicas == 0
# Scaling up means that we also need to check that we're not in a
# situation where status.replicas == status.availableReplicas
# but spec.replicas != status.replicas
return bool(
deployment.status
and deployment.spec.replicas == (deployment.status.replicas or 0)
and deployment.status.availableReplicas == deployment.status.replicas
and deployment.status.observedGeneration == deployment.metadata.generation
and not deployment.status.unavailableReplicas
)
def pod_ready(pod: ResourceInstance) -> bool:
return bool(
pod.status
and pod.status.containerStatuses is not None
and all(container.ready for container in pod.status.containerStatuses)
)
def daemonset_ready(daemonset: ResourceInstance) -> bool:
return bool(
daemonset.status
and daemonset.status.desiredNumberScheduled is not None
and daemonset.status.updatedNumberScheduled
== daemonset.status.desiredNumberScheduled
and daemonset.status.numberReady == daemonset.status.desiredNumberScheduled
and daemonset.status.observedGeneration == daemonset.metadata.generation
and not daemonset.status.unavailableReplicas
)
def statefulset_ready(statefulset: ResourceInstance) -> bool:
# These may be None
updated_replicas = statefulset.status.updatedReplicas or 0
ready_replicas = statefulset.status.readyReplicas or 0
return bool(
statefulset.status
and statefulset.spec.updateStrategy.type == "RollingUpdate"
and statefulset.status.observedGeneration
== (statefulset.metadata.generation or 0)
and statefulset.status.updateRevision == statefulset.status.currentRevision
and updated_replicas == statefulset.spec.replicas
and ready_replicas == statefulset.spec.replicas
and statefulset.status.replicas == statefulset.spec.replicas
)
def custom_condition(condition: Dict, resource: ResourceInstance) -> bool:
if not resource.status or not resource.status.conditions:
return False
matches = [x for x in resource.status.conditions if x.type == condition["type"]]
if not matches:
return False
# There should never be more than one condition of a specific type
match: ResourceField = matches[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 == boolean(condition["status"], strict=False):
if condition.get("reason"):
return match.reason == condition["reason"]
return True
return False
def resource_absent(resource: ResourceInstance) -> bool:
return not exists(resource)
def exists(resource: Optional[ResourceInstance]) -> bool:
"""Simple predicate to check for existence of a resource.
While a List type resource technically always exists, this will only return
true if the List contains items."""
return bool(resource) and not empty_list(resource)
RESOURCE_PREDICATES = {
"DaemonSet": daemonset_ready,
"Deployment": deployment_ready,
"Pod": pod_ready,
"StatefulSet": statefulset_ready,
}
def empty_list(resource: ResourceInstance) -> bool:
return resource["kind"].endswith("List") and not resource.get("items")
def clock(total: int, interval: int) -> Iterator[int]:
start = time.monotonic()
yield 0
while (time.monotonic() - start) < total:
time.sleep(interval)
yield int(time.monotonic() - start)
class Waiter:
def __init__(
self, client, resource: Resource, predicate: Callable[[ResourceInstance], bool]
):
self.client = client
self.resource = resource
self.predicate = predicate
def wait(
self,
timeout: int,
sleep: int,
name: Optional[str] = None,
namespace: Optional[str] = None,
label_selectors: Optional[List[str]] = None,
field_selectors: Optional[List[str]] = None,
) -> Tuple[bool, Dict, int]:
params = {}
if name:
params["name"] = name
if namespace:
params["namespace"] = namespace
if label_selectors:
params["label_selector"] = ",".join(label_selectors)
if field_selectors:
params["field_selector"] = ",".join(field_selectors)
instance = {}
response = None
elapsed = 0
for i in clock(timeout, sleep):
exception = None
elapsed = i
try:
response = self.client.get(self.resource, **params)
except NotFoundError:
response = None
# Retry connection errors as it may be intermittent network issues
except HTTPError as e:
exception = e
if self.predicate(response):
break
if exception:
msg = (
"Exception '{0}' raised while trying to get resource using {1}".format(
exception, params
)
)
raise CoreException(msg) from exception
if response:
instance = response.to_dict()
return self.predicate(response), instance, elapsed
class DummyWaiter:
"""A no-op waiter that simply returns the item being waited on.
No API call will be made with this waiter; the function returns
immediately. This waiter is useful for waiting on resource instances in
check mode, for example.
"""
def wait(
self,
definition: Dict,
timeout: int,
sleep: int,
label_selectors: Optional[List[str]] = None,
) -> Tuple[bool, Optional[Dict], int]:
return True, definition, 0
# The better solution would be typing.Protocol, but this is only in 3.8+
SupportsWait = Union[Waiter, DummyWaiter]
def get_waiter(
client,
resource: Resource,
state: str = "present",
condition: Optional[Dict] = None,
check_mode: Optional[bool] = False,
) -> SupportsWait:
"""Create a Waiter object based on the specified resource.
This is a convenience method for creating a waiter from a resource.
Based on the arguments and the kind of resource, an appropriate waiter
will be returned. A waiter can also be created directly, of course.
"""
if check_mode:
return DummyWaiter()
if state == "present":
if condition:
predicate: Callable[[ResourceInstance], bool] = partial(
custom_condition, condition
)
else:
predicate = RESOURCE_PREDICATES.get(resource.kind, exists)
else:
predicate = resource_absent
return Waiter(client, resource, predicate)

View File

@@ -391,6 +391,15 @@ from ansible_collections.kubernetes.core.plugins.module_utils.args_common import
RESOURCE_ARG_SPEC,
DELETE_OPTS_ARG_SPEC,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
AnsibleK8SModule,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.runner import (
run_module,
)
def validate_spec():
@@ -437,28 +446,6 @@ def argspec():
return argument_spec
def execute_module(module, k8s_ansible_mixin):
k8s_ansible_mixin.module = module
k8s_ansible_mixin.argspec = module.argument_spec
k8s_ansible_mixin.check_mode = k8s_ansible_mixin.module.check_mode
k8s_ansible_mixin.params = k8s_ansible_mixin.module.params
k8s_ansible_mixin.fail_json = k8s_ansible_mixin.module.fail_json
k8s_ansible_mixin.fail = k8s_ansible_mixin.module.fail_json
k8s_ansible_mixin.exit_json = k8s_ansible_mixin.module.exit_json
k8s_ansible_mixin.warn = k8s_ansible_mixin.module.warn
k8s_ansible_mixin.warnings = []
k8s_ansible_mixin.kind = k8s_ansible_mixin.params.get("kind")
k8s_ansible_mixin.api_version = k8s_ansible_mixin.params.get("api_version")
k8s_ansible_mixin.name = k8s_ansible_mixin.params.get("name")
k8s_ansible_mixin.generate_name = k8s_ansible_mixin.params.get("generate_name")
k8s_ansible_mixin.namespace = k8s_ansible_mixin.params.get("namespace")
k8s_ansible_mixin.check_library_version()
k8s_ansible_mixin.set_resource_definitions(module)
k8s_ansible_mixin.execute_module()
def main():
mutually_exclusive = [
("resource_definition", "src"),
@@ -467,19 +454,17 @@ def main():
("template", "src"),
("name", "generate_name"),
]
module = AnsibleModule(
module = AnsibleK8SModule(
module_class=AnsibleModule,
argument_spec=argspec(),
mutually_exclusive=mutually_exclusive,
supports_check_mode=True,
)
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
K8sAnsibleMixin,
get_api_client,
)
k8s_ansible_mixin = K8sAnsibleMixin(module)
k8s_ansible_mixin.client = get_api_client(module=module)
execute_module(module, k8s_ansible_mixin)
try:
run_module(module)
except CoreException as e:
module.fail_from_exception(e)
if __name__ == "__main__":

View File

@@ -141,36 +141,32 @@ apis:
import copy
import traceback
from collections import defaultdict
HAS_K8S = False
try:
from ansible_collections.kubernetes.core.plugins.module_utils.client.resource import (
ResourceList,
)
except ImportError:
# Handled during module setup
pass
HAS_K8S = True
except ImportError as e:
K8S_IMP_ERR = e
K8S_IMP_EXC = traceback.format_exc()
from ansible.module_utils._text import to_native
from ansible.module_utils.basic import missing_required_lib
from ansible.module_utils.parsing.convert_bool import boolean
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
AnsibleModule,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
AnsibleK8SModule,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
)
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
AUTH_ARG_SPEC,
)
def execute_module(module, client):
invalidate_cache = boolean(
module.params.get("invalidate_cache", True), strict=False
)
if invalidate_cache:
if module.params.get("invalidate_cache"):
client.resources.invalidate_cache()
results = defaultdict(dict)
for resource in list(client.resources):
@@ -204,7 +200,7 @@ def execute_module(module, client):
version_info = {
"client": version,
"server": client.version,
"server": client.client.version,
}
module.exit_json(
changed=False, apis=results, connection=connection, version=version_info
@@ -218,18 +214,18 @@ def argspec():
def main():
module = AnsibleModule(argument_spec=argspec(), supports_check_mode=True)
if not HAS_K8S:
module.fail_json(
msg=missing_required_lib("kubernetes"),
exception=K8S_IMP_EXC,
error=to_native(K8S_IMP_ERR),
)
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
module = AnsibleK8SModule(
module_class=AnsibleModule, argument_spec=argspec(), supports_check_mode=True
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
get_api_client,
)
execute_module(module, client=get_api_client(module=module))
try:
execute_module(module, client=get_api_client(module=module))
except CoreException as e:
module.fail_from_exception(e)
if __name__ == "__main__":

View File

@@ -142,6 +142,19 @@ import copy
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
AnsibleModule,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
get_api_client,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
AnsibleK8SModule,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
K8sService,
)
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
AUTH_ARG_SPEC,
)
@@ -171,23 +184,9 @@ def argspec():
def execute_module(module):
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
K8sAnsibleMixin,
get_api_client,
)
k8s_ansible_mixin = K8sAnsibleMixin(module, pyyaml_required=False)
k8s_ansible_mixin.check_library_version()
k8s_ansible_mixin.module = module
k8s_ansible_mixin.argspec = module.argument_spec
k8s_ansible_mixin.params = k8s_ansible_mixin.module.params
k8s_ansible_mixin.fail_json = k8s_ansible_mixin.module.fail_json
k8s_ansible_mixin.fail = k8s_ansible_mixin.module.fail_json
k8s_ansible_mixin.client = get_api_client(module=module)
containers = check_pod(k8s_ansible_mixin, module)
client = get_api_client(module=module)
svc = K8sService(client, module)
containers = check_pod(svc)
if len(containers) > 1 and module.params.get("container") is None:
module.fail_json(
msg="Pod contains more than 1 container, option 'container' should be set"
@@ -195,9 +194,9 @@ def execute_module(module):
state = module.params.get("state")
if state == "to_pod":
k8s_copy = K8SCopyToPod(module, k8s_ansible_mixin.client)
k8s_copy = K8SCopyToPod(module, client.client)
else:
k8s_copy = K8SCopyFromPod(module, k8s_ansible_mixin.client)
k8s_copy = K8SCopyFromPod(module, client.client)
try:
k8s_copy.run()
@@ -206,15 +205,20 @@ def execute_module(module):
def main():
module = AnsibleModule(
module = AnsibleK8SModule(
module_class=AnsibleModule,
argument_spec=argspec(),
check_pyyaml=False,
mutually_exclusive=[("local_path", "content")],
required_if=[("state", "from_pod", ["local_path"])],
required_one_of=[["local_path", "content"]],
supports_check_mode=True,
)
execute_module(module)
try:
execute_module(module)
except CoreException as e:
module.fail_from_exception(e)
if __name__ == "__main__":

View File

@@ -136,6 +136,16 @@ from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule impo
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
AUTH_ARG_SPEC,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
get_api_client,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
AnsibleK8SModule,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
)
from ansible.module_utils._text import to_native
try:
@@ -244,30 +254,9 @@ def filter_pods(pods, force, ignore_daemonset, delete_emptydir_data):
class K8sDrainAnsible(object):
def __init__(self, module):
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
K8sAnsibleMixin,
get_api_client,
)
def __init__(self, module, client):
self._module = module
self._k8s_ansible_mixin = K8sAnsibleMixin(module)
self._k8s_ansible_mixin.client = get_api_client(module=self._module)
self._k8s_ansible_mixin.module = self._module
self._k8s_ansible_mixin.argspec = self._module.argument_spec
self._k8s_ansible_mixin.check_mode = self._module.check_mode
self._k8s_ansible_mixin.params = self._module.params
self._k8s_ansible_mixin.fail_json = self._module.fail_json
self._k8s_ansible_mixin.fail = self._module.fail_json
self._k8s_ansible_mixin.exit_json = self._module.exit_json
self._k8s_ansible_mixin.warn = self._module.warn
self._k8s_ansible_mixin.warnings = []
self._api_instance = core_v1_api.CoreV1Api(
self._k8s_ansible_mixin.client.client
)
self._k8s_ansible_mixin.check_library_version()
self._api_instance = core_v1_api.CoreV1Api(client.client)
# delete options
self._drain_options = module.params.get("delete_options", {})
@@ -503,7 +492,7 @@ def argspec():
def main():
module = AnsibleModule(argument_spec=argspec())
module = AnsibleK8SModule(module_class=AnsibleModule, argument_spec=argspec())
if not HAS_EVICTION_API:
module.fail_json(
@@ -512,8 +501,12 @@ def main():
error=to_native(k8s_import_exception),
)
k8s_drain = K8sDrainAnsible(module)
k8s_drain.execute_module()
try:
client = get_api_client(module=module)
k8s_drain = K8sDrainAnsible(module, client.client)
k8s_drain.execute_module()
except CoreException as e:
module.fail_from_exception(e)
if __name__ == "__main__":

View File

@@ -138,6 +138,15 @@ from ansible.module_utils._text import to_native
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
AUTH_ARG_SPEC,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
AnsibleK8SModule,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
get_api_client,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
)
try:
from kubernetes.client.apis import core_v1_api
@@ -157,10 +166,9 @@ def argspec():
return spec
def execute_module(module, k8s_ansible_mixin):
def execute_module(module, client):
# Load kubernetes.client.Configuration
api = core_v1_api.CoreV1Api(k8s_ansible_mixin.client.client)
api = core_v1_api.CoreV1Api(client.client)
# hack because passing the container as None breaks things
optional_kwargs = {}
@@ -228,18 +236,18 @@ def execute_module(module, k8s_ansible_mixin):
def main():
module = AnsibleModule(
module = AnsibleK8SModule(
module_class=AnsibleModule,
check_pyyaml=False,
argument_spec=argspec(),
supports_check_mode=True,
)
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
K8sAnsibleMixin,
get_api_client,
)
k8s_ansible_mixin = K8sAnsibleMixin(module)
k8s_ansible_mixin.client = get_api_client(module=module)
execute_module(module, k8s_ansible_mixin)
try:
client = get_api_client(module)
execute_module(module, client.client)
except CoreException as e:
module.fail_from_exception(e)
if __name__ == "__main__":

View File

@@ -155,10 +155,22 @@ from ansible_collections.kubernetes.core.plugins.module_utils.args_common import
AUTH_ARG_SPEC,
WAIT_ARG_SPEC,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
AnsibleK8SModule,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
get_api_client,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
K8sService,
)
def execute_module(module, k8s_ansible_mixin):
facts = k8s_ansible_mixin.kubernetes_facts(
def execute_module(module, svc):
facts = svc.find(
module.params["kind"],
module.params["api_version"],
name=module.params["name"],
@@ -190,19 +202,15 @@ def argspec():
def main():
module = AnsibleModule(argument_spec=argspec(), supports_check_mode=True)
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
K8sAnsibleMixin,
get_api_client,
module = AnsibleK8SModule(
module_class=AnsibleModule, argument_spec=argspec(), supports_check_mode=True
)
k8s_ansible_mixin = K8sAnsibleMixin(module)
k8s_ansible_mixin.client = get_api_client(module=module)
k8s_ansible_mixin.fail_json = module.fail_json
k8s_ansible_mixin.fail = module.fail_json
k8s_ansible_mixin.exit_json = module.exit_json
k8s_ansible_mixin.warn = module.warn
execute_module(module, k8s_ansible_mixin)
try:
client = get_api_client(module)
svc = K8sService(client, module)
execute_module(module, svc)
except CoreException as e:
module.fail_from_exception(e)
if __name__ == "__main__":

View File

@@ -136,10 +136,22 @@ from ansible_collections.kubernetes.core.plugins.module_utils.args_common import
AUTH_ARG_SPEC,
WAIT_ARG_SPEC,
)
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
get_api_client,
K8sAnsibleMixin,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
AnsibleK8SModule,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
diff_objects,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.waiter import (
get_waiter,
)
try:
from kubernetes.dynamic.exceptions import DynamicApiError
@@ -185,7 +197,7 @@ def json_patch(existing, patch):
return None, error
def execute_module(k8s_module, module):
def execute_module(module, client):
kind = module.params.get("kind")
api_version = module.params.get("api_version")
name = module.params.get("name")
@@ -200,19 +212,14 @@ def execute_module(k8s_module, module):
"type"
):
wait_condition = module.params["wait_condition"]
# definition is needed for wait
definition = {
"kind": kind,
"metadata": {"name": name, "namespace": namespace},
}
def build_error_msg(kind, name, msg):
return "%s %s: %s" % (kind, name, msg)
resource = k8s_module.find_resource(kind, api_version, fail=True)
resource = client.resource(kind, api_version)
try:
existing = resource.get(name=name, namespace=namespace)
existing = client.get(resource, name=name, namespace=namespace)
except DynamicApiError as exc:
msg = "Failed to retrieve requested object: {0}".format(exc.body)
module.fail_json(
@@ -227,7 +234,7 @@ def execute_module(k8s_module, module):
msg=build_error_msg(kind, name, msg), error="", status="", reason=""
)
if module.check_mode and not k8s_module.supports_dry_run:
if module.check_mode and not client.dry_run:
obj, error = json_patch(existing.to_dict(), patch)
if error:
module.fail_json(**error)
@@ -236,7 +243,8 @@ def execute_module(k8s_module, module):
if module.check_mode:
params["dry_run"] = "All"
try:
obj = resource.patch(
obj = client.patch(
resource,
patch,
name=name,
namespace=namespace,
@@ -255,10 +263,11 @@ def execute_module(k8s_module, module):
success = True
result = {"result": obj}
if wait and not module.check_mode:
success, result["result"], result["duration"] = k8s_module.wait(
resource, definition, wait_sleep, wait_timeout, condition=wait_condition
waiter = get_waiter(client, resource, condition=wait_condition)
success, result["result"], result["duration"] = waiter.wait(
wait_timeout, wait_sleep, name, namespace
)
match, diffs = k8s_module.diff_objects(existing.to_dict(), obj)
match, diffs = diff_objects(existing.to_dict(), obj)
result["changed"] = not match
if module._diff:
result["diff"] = diffs
@@ -274,13 +283,14 @@ def main():
args = copy.deepcopy(AUTH_ARG_SPEC)
args.update(copy.deepcopy(WAIT_ARG_SPEC))
args.update(JSON_PATCH_ARGS)
module = AnsibleModule(argument_spec=args, supports_check_mode=True)
k8s_module = K8sAnsibleMixin(module)
k8s_module.params = module.params
k8s_module.check_library_version()
client = get_api_client(module)
k8s_module.client = client
execute_module(k8s_module, module)
module = AnsibleK8SModule(
module_class=AnsibleModule, argument_spec=args, supports_check_mode=True
)
try:
client = get_api_client(module)
execute_module(module, client)
except CoreException as e:
module.fail_from_exception(e)
if __name__ == "__main__":

View File

@@ -128,12 +128,22 @@ import copy
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
AnsibleModule,
)
from ansible.module_utils.six import PY2
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
AUTH_ARG_SPEC,
NAME_ARG_SPEC,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
get_api_client,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
AnsibleK8SModule,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
K8sService,
)
def argspec():
@@ -151,32 +161,30 @@ def argspec():
return args
def execute_module(module, k8s_ansible_mixin):
name = module.params.get("name")
namespace = module.params.get("namespace")
label_selector = ",".join(module.params.get("label_selectors", {}))
def execute_module(svc, params):
name = params.get("name")
namespace = params.get("namespace")
label_selector = ",".join(params.get("label_selectors", {}))
if name and label_selector:
module.fail(msg="Only one of name or label_selectors can be provided")
raise CoreException("Only one of name or label_selectors can be provided")
resource = k8s_ansible_mixin.find_resource(
module.params["kind"], module.params["api_version"], fail=True
)
v1_pods = k8s_ansible_mixin.find_resource("Pod", "v1", fail=True)
resource = svc.find_resource(params["kind"], params["api_version"], fail=True)
v1_pods = svc.find_resource("Pod", "v1", fail=True)
if "log" not in resource.subresources:
if not name:
module.fail(
msg="name must be provided for resources that do not support the log subresource"
raise CoreException(
"name must be provided for resources that do not support the log subresource"
)
instance = resource.get(name=name, namespace=namespace)
label_selector = ",".join(extract_selectors(module, instance))
label_selector = ",".join(extract_selectors(instance))
resource = v1_pods
if label_selector:
instances = v1_pods.get(namespace=namespace, label_selector=label_selector)
if not instances.items:
module.fail(
msg="No pods in namespace {0} matched selector {1}".format(
raise CoreException(
"No pods in namespace {0} matched selector {1}".format(
namespace, label_selector
)
)
@@ -185,33 +193,32 @@ def execute_module(module, k8s_ansible_mixin):
resource = v1_pods
kwargs = {}
if module.params.get("container"):
kwargs["query_params"] = dict(container=module.params["container"])
if params.get("container"):
kwargs["query_params"] = {"container": params["container"]}
if module.params.get("since_seconds"):
if params.get("since_seconds"):
kwargs.setdefault("query_params", {}).update(
{"sinceSeconds": module.params["since_seconds"]}
{"sinceSeconds": params["since_seconds"]}
)
if module.params.get("previous"):
kwargs.setdefault("query_params", {}).update(
{"previous": module.params["previous"]}
)
if params.get("previous"):
kwargs.setdefault("query_params", {}).update({"previous": params["previous"]})
log = serialize_log(
resource.log.get(name=name, namespace=namespace, serialize=False, **kwargs)
response = resource.log.get(
name=name, namespace=namespace, serialize=False, **kwargs
)
log = response.data.decode("utf8")
module.exit_json(changed=False, log=log, log_lines=log.split("\n"))
return {"changed": False, "log": log, "log_lines": log.split("\n")}
def extract_selectors(module, instance):
def extract_selectors(instance):
# Parses selectors on an object based on the specifications documented here:
# https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors
selectors = []
if not instance.spec.selector:
module.fail(
msg="{0} {1} does not support the log subresource directly, and no Pod selector was found on the object".format(
raise CoreException(
"{0} {1} does not support the log subresource directly, and no Pod selector was found on the object".format(
"/".join(instance.group, instance.apiVersion), instance.kind
)
)
@@ -245,8 +252,8 @@ def extract_selectors(module, instance):
)
)
else:
module.fail(
msg="The k8s_log module does not support the {0} matchExpression operator".format(
raise CoreException(
"The k8s_log module does not support the {0} matchExpression operator".format(
operator.lower()
)
)
@@ -254,22 +261,18 @@ def extract_selectors(module, instance):
return selectors
def serialize_log(response):
if PY2:
return response.data
return response.data.decode("utf8")
def main():
module = AnsibleModule(argument_spec=argspec(), supports_check_mode=True)
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
K8sAnsibleMixin,
get_api_client,
module = AnsibleK8SModule(
module_class=AnsibleModule, argument_spec=argspec(), supports_check_mode=True
)
k8s_ansible_mixin = K8sAnsibleMixin(module)
k8s_ansible_mixin.client = get_api_client(module=module)
execute_module(module, k8s_ansible_mixin)
try:
client = get_api_client(module=module)
svc = K8sService(client, module)
result = execute_module(svc, module.params)
module.exit_json(**result)
except CoreException as e:
module.fail_from_exception(e)
if __name__ == "__main__":

View File

@@ -86,12 +86,23 @@ from ansible_collections.kubernetes.core.plugins.module_utils.args_common import
AUTH_ARG_SPEC,
NAME_ARG_SPEC,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
get_api_client,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
AnsibleK8SModule,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
K8sService,
)
def get_managed_resource(module):
def get_managed_resource(kind):
managed_resource = {}
kind = module.params["kind"]
if kind == "DaemonSet":
managed_resource["kind"] = "ControllerRevision"
managed_resource["api_version"] = "apps/v1"
@@ -99,14 +110,17 @@ def get_managed_resource(module):
managed_resource["kind"] = "ReplicaSet"
managed_resource["api_version"] = "apps/v1"
else:
module.fail(msg="Cannot perform rollback on resource of kind {0}".format(kind))
raise CoreException(
"Cannot perform rollback on resource of kind {0}".format(kind)
)
return managed_resource
def execute_module(module, k8s_ansible_mixin):
def execute_module(svc):
results = []
module = svc.module
resources = k8s_ansible_mixin.kubernetes_facts(
resources = svc.find(
module.params["kind"],
module.params["api_version"],
module.params["name"],
@@ -117,14 +131,16 @@ def execute_module(module, k8s_ansible_mixin):
changed = False
for resource in resources["resources"]:
result = perform_action(module, k8s_ansible_mixin, resource)
result = perform_action(svc, resource)
changed = result["changed"] or changed
results.append(result)
module.exit_json(**{"changed": changed, "rollback_info": results})
def perform_action(module, k8s_ansible_mixin, resource):
def perform_action(svc, resource):
module = svc.module
if module.params["kind"] == "DaemonSet":
current_revision = resource["metadata"]["generation"]
elif module.params["kind"] == "Deployment":
@@ -132,8 +148,8 @@ def perform_action(module, k8s_ansible_mixin, resource):
"deployment.kubernetes.io/revision"
]
managed_resource = get_managed_resource(module)
managed_resources = k8s_ansible_mixin.kubernetes_facts(
managed_resource = get_managed_resource(module.params["kind"])
managed_resources = svc.find(
managed_resource["kind"],
managed_resource["api_version"],
"",
@@ -185,7 +201,7 @@ def perform_action(module, k8s_ansible_mixin, resource):
rollback = resource
if not module.check_mode:
rollback = k8s_ansible_mixin.client.request(
rollback = svc.client.client.request(
"PATCH",
"/apis/{0}/namespaces/{1}/{2}/{3}".format(
module.params["api_version"],
@@ -242,15 +258,16 @@ def get_previous_revision(all_resources, current_revision):
def main():
module = AnsibleModule(argument_spec=argspec(), supports_check_mode=True)
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
K8sAnsibleMixin,
get_api_client,
module = AnsibleK8SModule(
module_class=AnsibleModule, argument_spec=argspec(), supports_check_mode=True
)
k8s_ansible_mixin = K8sAnsibleMixin(module)
k8s_ansible_mixin.client = get_api_client(module=module)
execute_module(module, k8s_ansible_mixin)
try:
client = get_api_client(module=module)
svc = K8sService(client, module)
execute_module(svc)
except CoreException as e:
module.fail_from_exception(e)
if __name__ == "__main__":

View File

@@ -143,6 +143,14 @@ result:
import copy
try:
from kubernetes.dynamic.exceptions import NotFoundError
except ImportError:
# Handled in module setup
pass
from ansible.module_utils._text import to_native
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
AnsibleModule,
)
@@ -151,7 +159,25 @@ from ansible_collections.kubernetes.core.plugins.module_utils.args_common import
RESOURCE_ARG_SPEC,
NAME_ARG_SPEC,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
get_api_client,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
AnsibleK8SModule,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
ResourceTimeout,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
diff_objects,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.waiter import (
get_waiter,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.resource import (
create_definitions,
)
SCALE_ARG_SPEC = {
"replicas": {"type": "int", "required": True},
@@ -163,27 +189,20 @@ SCALE_ARG_SPEC = {
}
def execute_module(
module,
k8s_ansible_mixin,
):
k8s_ansible_mixin.set_resource_definitions(module)
definition = k8s_ansible_mixin.resource_definitions[0]
name = definition["metadata"]["name"]
namespace = definition["metadata"].get("namespace")
api_version = definition["apiVersion"]
kind = definition["kind"]
def execute_module(client, module):
current_replicas = module.params.get("current_replicas")
replicas = module.params.get("replicas")
resource_version = module.params.get("resource_version")
definitions = create_definitions(module.params)
definition = definitions[0]
name = definition["metadata"].get("name")
namespace = definition["metadata"].get("namespace")
api_version = definition["apiVersion"]
kind = definition["kind"]
label_selectors = module.params.get("label_selectors")
if not label_selectors:
label_selectors = []
continue_on_error = module.params.get("continue_on_error")
wait = module.params.get("wait")
wait_time = module.params.get("wait_timeout")
wait_sleep = module.params.get("wait_sleep")
@@ -195,12 +214,7 @@ def execute_module(
if wait:
return_attributes["duration"] = 0
resource = k8s_ansible_mixin.find_resource(kind, api_version, fail=True)
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
NotFoundError,
)
resource = client.resource(kind, api_version)
multiple_scale = False
try:
existing = resource.get(
@@ -211,11 +225,10 @@ def execute_module(
multiple_scale = len(existing_items) > 1
else:
existing_items = [existing]
except NotFoundError as exc:
module.fail_json(
msg="Failed to retrieve requested object: {0}".format(exc),
error=exc.value.get("status"),
)
except NotFoundError as e:
reason = e.body if hasattr(e, "body") else e
msg = "Failed to retrieve requested object: {0}".format(reason)
raise CoreException(msg) from e
if multiple_scale:
# when scaling multiple resource, the 'result' is changed to 'results' and is a list
@@ -278,23 +291,28 @@ def execute_module(
if module.check_mode:
result["result"] = existing.to_dict()
else:
result["result"] = resource.patch(existing.to_dict()).to_dict()
result["result"] = client.patch(
resource, existing.to_dict()
).to_dict()
else:
result = scale(
module,
k8s_ansible_mixin,
resource,
existing,
replicas,
wait,
wait_time,
wait_sleep,
)
try:
result = scale(
client,
module,
resource,
existing,
replicas,
wait,
wait_time,
wait_sleep,
)
except CoreException as e:
module.fail_json(msg=to_native(e))
changed = changed or result["changed"]
else:
name = existing.metadata.name
namespace = existing.metadata.namespace
existing = resource.get(name=name, namespace=namespace)
existing = client.get(resource, name=name, namespace=namespace)
result = {"changed": False, "result": existing.to_dict()}
if module._diff:
result["diff"] = {}
@@ -320,8 +338,8 @@ def argspec():
def scale(
client,
module,
k8s_ansible_mixin,
resource,
existing_object,
replicas,
@@ -334,8 +352,8 @@ def scale(
kind = existing_object.kind
if not hasattr(resource, "scale"):
module.fail_json(
msg="Cannot perform scale on resource of kind {0}".format(resource.kind)
raise CoreException(
"Cannot perform scale on resource of kind {0}".format(resource.kind)
)
scale_obj = {
@@ -344,32 +362,37 @@ def scale(
"spec": {"replicas": replicas},
}
existing = resource.get(name=name, namespace=namespace)
existing = client.get(resource, name=name, namespace=namespace)
result = dict()
if module.check_mode:
k8s_obj = copy.deepcopy(existing.to_dict())
k8s_obj["spec"]["replicas"] = replicas
match, diffs = k8s_ansible_mixin.diff_objects(existing.to_dict(), k8s_obj)
if wait:
result["duration"] = 0
result["result"] = k8s_obj
else:
try:
resource.scale.patch(body=scale_obj)
except Exception as exc:
module.fail_json(msg="Scale request failed: {0}".format(exc))
except Exception as e:
reason = e.body if hasattr(e, "body") else e
msg = "Scale request failed: {0}".format(reason)
raise CoreException(msg) from e
k8s_obj = resource.get(name=name, namespace=namespace).to_dict()
k8s_obj = client.get(resource, name=name, namespace=namespace).to_dict()
result["result"] = k8s_obj
if wait and not module.check_mode:
success, result["result"], result["duration"] = k8s_ansible_mixin.wait(
resource, scale_obj, wait_sleep, wait_time
if wait:
waiter = get_waiter(client, resource)
success, result["result"], result["duration"] = waiter.wait(
timeout=wait_time,
sleep=wait_sleep,
name=name,
namespace=namespace,
)
if not success:
module.fail_json(msg="Resource scaling timed out", **result)
raise ResourceTimeout("Resource scaling timed out", **result)
match, diffs = k8s_ansible_mixin.diff_objects(existing.to_dict(), k8s_obj)
match, diffs = diff_objects(existing.to_dict(), result["result"])
result["changed"] = not match
if module._diff:
result["diff"] = diffs
@@ -381,19 +404,18 @@ def main():
mutually_exclusive = [
("resource_definition", "src"),
]
module = AnsibleModule(
module = AnsibleK8SModule(
module_class=AnsibleModule,
argument_spec=argspec(),
mutually_exclusive=mutually_exclusive,
supports_check_mode=True,
)
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
K8sAnsibleMixin,
get_api_client,
)
k8s_ansible_mixin = K8sAnsibleMixin(module)
k8s_ansible_mixin.client = get_api_client(module=module)
execute_module(module, k8s_ansible_mixin)
try:
client = get_api_client(module=module)
execute_module(client, module)
except CoreException as e:
module.fail_from_exception(e)
if __name__ == "__main__":

View File

@@ -154,6 +154,22 @@ from ansible_collections.kubernetes.core.plugins.module_utils.args_common import
COMMON_ARG_SPEC,
RESOURCE_ARG_SPEC,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
AnsibleK8SModule,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
get_api_client,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
K8sService,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.resource import (
create_definitions,
)
SERVICE_ARG_SPEC = {
"apply": {"type": "bool", "default": False},
@@ -195,10 +211,35 @@ def argspec():
return argument_spec
def execute_module(module, k8s_ansible_mixin):
"""Module execution"""
k8s_ansible_mixin.set_resource_definitions(module)
def perform_action(svc, resource, definition, params):
state = params.get("state", None)
result = {}
existing = svc.retrieve(resource, definition)
if state == "absent":
result = svc.delete(resource, definition, existing)
result["method"] = "delete"
else:
if params.get("apply"):
result = svc.apply(resource, definition, existing)
result["method"] = "apply"
elif not existing:
result = svc.create(resource, definition)
result["method"] = "create"
elif params.get("force", False):
result = svc.replace(resource, definition, existing)
result["method"] = "replace"
else:
result = svc.update(resource, definition, existing)
result["method"] = "update"
return result
def execute_module(svc):
"""Module execution"""
module = svc.module
api_version = "v1"
selector = module.params.get("selector")
service_type = module.params.get("type")
@@ -218,28 +259,30 @@ def execute_module(module, k8s_ansible_mixin):
def_meta["name"] = module.params.get("name")
def_meta["namespace"] = module.params.get("namespace")
# 'resource_definition:' has lower priority than module parameters
definition = dict(
merge_dicts(k8s_ansible_mixin.resource_definitions[0], definition)
)
definitions = create_definitions(module.params)
resource = k8s_ansible_mixin.find_resource("Service", api_version, fail=True)
definition = k8s_ansible_mixin.set_defaults(resource, definition)
result = k8s_ansible_mixin.perform_action(resource, definition)
# 'resource_definition:' has lower priority than module parameters
definition = dict(merge_dicts(definitions[0], definition))
resource = svc.find_resource("Service", api_version, fail=True)
result = perform_action(svc, resource, definition, module.params)
module.exit_json(**result)
def main():
module = AnsibleModule(argument_spec=argspec(), supports_check_mode=True)
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
K8sAnsibleMixin,
get_api_client,
module = AnsibleK8SModule(
module_class=AnsibleModule,
argument_spec=argspec(),
supports_check_mode=True,
)
k8s_ansible_mixin = K8sAnsibleMixin(module)
k8s_ansible_mixin.client = get_api_client(module=module)
execute_module(module, k8s_ansible_mixin)
try:
client = get_api_client(module=module)
svc = K8sService(client, module)
execute_module(svc)
except CoreException as e:
module.fail_from_exception(e)
if __name__ == "__main__":

View File

@@ -126,22 +126,29 @@ result:
import copy
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
K8sAnsibleMixin,
get_api_client,
from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
AnsibleModule,
)
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
AUTH_ARG_SPEC,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
get_api_client,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
AnsibleK8SModule,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
)
try:
from kubernetes.client.api import core_v1_api
from kubernetes.client.exceptions import ApiException
except ImportError:
# ImportError are managed by the common module already.
# ImportErrors are handled during module setup
pass
@@ -191,21 +198,9 @@ def argspec():
class K8sTaintAnsible:
def __init__(self, module):
def __init__(self, module, client):
self.module = module
self.k8s_ansible_mixin = K8sAnsibleMixin(module=self.module)
self.k8s_ansible_mixin.client = get_api_client(module=self.module)
self.k8s_ansible_mixin.module = self.module
self.k8s_ansible_mixin.argspec = self.module.argument_spec
self.k8s_ansible_mixin.check_mode = self.module.check_mode
self.k8s_ansible_mixin.params = self.module.params
self.k8s_ansible_mixin.fail_json = self.module.fail_json
self.k8s_ansible_mixin.fail = self.module.fail_json
self.k8s_ansible_mixin.exit_json = self.module.exit_json
self.k8s_ansible_mixin.warn = self.module.warn
self.k8s_ansible_mixin.warnings = []
self.api_instance = core_v1_api.CoreV1Api(self.k8s_ansible_mixin.client.client)
self.k8s_ansible_mixin.check_library_version()
self.api_instance = core_v1_api.CoreV1Api(client.client)
self.changed = False
def get_node(self, name):
@@ -301,12 +296,17 @@ class K8sTaintAnsible:
def main():
module = AnsibleModule(
module = AnsibleK8SModule(
module_class=AnsibleModule,
argument_spec=argspec(),
supports_check_mode=True,
)
k8s_taint = K8sTaintAnsible(module)
k8s_taint.execute_module()
try:
client = get_api_client(module)
k8s_taint = K8sTaintAnsible(module, client.client)
k8s_taint.execute_module()
except CoreException as e:
module.fail_from_exception(e)
if __name__ == "__main__":

View File

@@ -1,4 +1,6 @@
kubernetes-validate
coverage==4.5.4
mock
pytest
pytest-xdist
pytest-mock

2
tests/config.yml Normal file
View File

@@ -0,0 +1,2 @@
modules:
python_requires: ">=3.6"

View File

@@ -5,7 +5,7 @@
# This is a job definition that runs for 10 minutes and won't gracefully
# shutdown. It allows us to test foreground vs background deletion.
job_definition:
apiVersion: v1
apiVersion: batch/v1
kind: Job
metadata:
name: "{{ gc_name }}"

View File

@@ -97,6 +97,7 @@
- name: patch service using json merge patch
kubernetes.core.k8s:
kind: Deployment
api_version: apps/v1
namespace: "{{ k8s_patch_namespace }}"
name: "{{ k8s_merge }}"
merge_type:

View File

@@ -52,7 +52,7 @@
assert:
that:
- patch_resource.changed
- patch_resource.result.results | selectattr('warning', 'defined') | list | length == 1
- patch_resource.result.results | selectattr('warnings', 'defined') | list | length == 1
- name: Ensure namespace {{ patch_only_namespace[0] }} was patched correctly
kubernetes.core.k8s_info:

View File

@@ -150,7 +150,7 @@
- name: Reapply the earlier deployment
k8s:
definition:
api_version: apps/v1
apiVersion: apps/v1
kind: Deployment
metadata:
name: scale-deploy

View File

@@ -42,7 +42,7 @@
- assert:
that:
- k8s_no_validate is failed
- "k8s_no_validate.msg == 'kubernetes-validate python library is required to validate resources'"
- "'Failed to import the required Python library (kubernetes-validate)' in k8s_no_validate.msg"
- file:
path: "{{ virtualenv }}"
@@ -201,7 +201,7 @@
- name: assert that task failed with proper message
assert:
that:
- '"kubernetes >= 17.17.0 is required" in _result.msg'
- '"This is required to use in-memory config." in _result.msg'
when:
- _stat.stat.exists
- _stat.stat.readable

View File

@@ -1,262 +1,178 @@
tests/integration/targets/k8s_scale/files/deployment.yaml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart/templates/configmap.yaml yamllint!skip
plugins/module_utils/__init__.py compile-2.6!skip
plugins/module_utils/__init__.py compile-2.7!skip
plugins/module_utils/__init__.py compile-3.5!skip
plugins/module_utils/__init__.py future-import-boilerplate!skip
plugins/module_utils/__init__.py import-2.6!skip
plugins/module_utils/__init__.py import-2.7!skip
plugins/module_utils/__init__.py import-3.5!skip
plugins/module_utils/__init__.py metaclass-boilerplate!skip
plugins/module_utils/ansiblemodule.py compile-2.6!skip
plugins/module_utils/ansiblemodule.py compile-2.7!skip
plugins/module_utils/ansiblemodule.py compile-3.5!skip
plugins/module_utils/ansiblemodule.py future-import-boilerplate!skip
plugins/module_utils/ansiblemodule.py import-2.6!skip
plugins/module_utils/ansiblemodule.py import-2.7!skip
plugins/module_utils/ansiblemodule.py import-3.5!skip
plugins/module_utils/ansiblemodule.py metaclass-boilerplate!skip
plugins/module_utils/apply.py compile-2.6!skip
plugins/module_utils/apply.py compile-2.7!skip
plugins/module_utils/apply.py compile-3.5!skip
plugins/module_utils/apply.py future-import-boilerplate!skip
plugins/module_utils/apply.py import-2.6!skip
plugins/module_utils/apply.py import-2.7!skip
plugins/module_utils/apply.py import-3.5!skip
plugins/module_utils/apply.py metaclass-boilerplate!skip
plugins/module_utils/args_common.py compile-2.6!skip
plugins/module_utils/args_common.py compile-2.7!skip
plugins/module_utils/args_common.py compile-3.5!skip
plugins/module_utils/args_common.py future-import-boilerplate!skip
plugins/module_utils/args_common.py import-2.6!skip
plugins/module_utils/args_common.py import-2.7!skip
plugins/module_utils/args_common.py import-3.5!skip
plugins/module_utils/args_common.py metaclass-boilerplate!skip
plugins/module_utils/client/discovery.py future-import-boilerplate!skip
plugins/module_utils/client/discovery.py import-2.6!skip
plugins/module_utils/client/discovery.py import-2.7!skip
plugins/module_utils/client/discovery.py import-3.5!skip
plugins/module_utils/client/discovery.py import-3.6!skip
plugins/module_utils/client/discovery.py import-3.7!skip
plugins/module_utils/client/discovery.py import-3.8!skip
plugins/module_utils/client/discovery.py import-3.9!skip
plugins/module_utils/client/discovery.py metaclass-boilerplate!skip
plugins/module_utils/client/resource.py import-2.6!skip
plugins/module_utils/client/resource.py import-2.7!skip
plugins/module_utils/client/resource.py import-3.5!skip
plugins/module_utils/client/resource.py import-3.6!skip
plugins/module_utils/client/resource.py import-3.7!skip
plugins/module_utils/client/resource.py import-3.8!skip
plugins/module_utils/client/resource.py import-3.9!skip
plugins/module_utils/common.py compile-2.6!skip
plugins/module_utils/common.py compile-2.7!skip
plugins/module_utils/common.py compile-3.5!skip
plugins/module_utils/common.py future-import-boilerplate!skip
plugins/module_utils/common.py import-2.6!skip
plugins/module_utils/common.py import-2.7!skip
plugins/module_utils/common.py import-3.5!skip
plugins/module_utils/common.py metaclass-boilerplate!skip
plugins/module_utils/exceptions.py compile-2.6!skip
plugins/module_utils/exceptions.py compile-2.7!skip
plugins/module_utils/exceptions.py compile-3.5!skip
plugins/module_utils/exceptions.py future-import-boilerplate!skip
plugins/module_utils/exceptions.py import-2.6!skip
plugins/module_utils/exceptions.py import-2.7!skip
plugins/module_utils/exceptions.py import-3.5!skip
plugins/module_utils/exceptions.py metaclass-boilerplate!skip
plugins/module_utils/hashes.py compile-2.6!skip
plugins/module_utils/hashes.py compile-2.7!skip
plugins/module_utils/hashes.py compile-3.5!skip
plugins/module_utils/hashes.py future-import-boilerplate!skip
plugins/module_utils/hashes.py import-2.6!skip
plugins/module_utils/hashes.py import-2.7!skip
plugins/module_utils/hashes.py import-3.5!skip
plugins/module_utils/hashes.py metaclass-boilerplate!skip
plugins/module_utils/helm.py compile-2.6!skip
plugins/module_utils/helm.py compile-2.7!skip
plugins/module_utils/helm.py compile-3.5!skip
plugins/module_utils/helm.py future-import-boilerplate!skip
plugins/module_utils/helm.py import-2.6!skip
plugins/module_utils/helm.py import-2.7!skip
plugins/module_utils/helm.py import-3.5!skip
plugins/module_utils/helm.py metaclass-boilerplate!skip
plugins/module_utils/k8sdynamicclient.py compile-2.6!skip
plugins/module_utils/k8sdynamicclient.py compile-2.7!skip
plugins/module_utils/k8sdynamicclient.py compile-3.5!skip
plugins/module_utils/k8sdynamicclient.py future-import-boilerplate!skip
plugins/module_utils/k8sdynamicclient.py import-2.6!skip
plugins/module_utils/k8sdynamicclient.py import-2.7!skip
plugins/module_utils/k8sdynamicclient.py import-3.5!skip
plugins/module_utils/k8sdynamicclient.py import-3.6!skip
plugins/module_utils/k8sdynamicclient.py import-3.7!skip
plugins/module_utils/k8sdynamicclient.py import-3.8!skip
plugins/module_utils/k8sdynamicclient.py import-3.9!skip
plugins/module_utils/k8sdynamicclient.py metaclass-boilerplate!skip
plugins/modules/__init__.py compile-2.6!skip
plugins/modules/__init__.py compile-2.7!skip
plugins/modules/__init__.py compile-3.5!skip
plugins/modules/__init__.py future-import-boilerplate!skip
plugins/modules/__init__.py import-2.6!skip
plugins/modules/__init__.py import-2.7!skip
plugins/modules/__init__.py import-3.5!skip
plugins/modules/__init__.py metaclass-boilerplate!skip
plugins/modules/helm.py compile-2.6!skip
plugins/modules/helm.py compile-2.7!skip
plugins/modules/helm.py compile-3.5!skip
plugins/modules/helm.py future-import-boilerplate!skip
plugins/modules/helm.py import-2.6!skip
plugins/modules/helm.py import-2.7!skip
plugins/modules/helm.py import-3.5!skip
plugins/modules/helm.py metaclass-boilerplate!skip
plugins/modules/helm_info.py compile-2.6!skip
plugins/modules/helm_info.py compile-2.7!skip
plugins/modules/helm_info.py compile-3.5!skip
plugins/modules/helm_info.py future-import-boilerplate!skip
plugins/modules/helm_info.py import-2.6!skip
plugins/modules/helm_info.py import-2.7!skip
plugins/modules/helm_info.py import-3.5!skip
plugins/modules/helm_info.py metaclass-boilerplate!skip
plugins/modules/helm_plugin.py compile-2.6!skip
plugins/modules/helm_plugin.py compile-2.7!skip
plugins/modules/helm_plugin.py compile-3.5!skip
plugins/modules/helm_plugin.py future-import-boilerplate!skip
plugins/modules/helm_plugin.py import-2.6!skip
plugins/modules/helm_plugin.py import-2.7!skip
plugins/modules/helm_plugin.py import-3.5!skip
plugins/modules/helm_plugin.py metaclass-boilerplate!skip
plugins/modules/helm_plugin_info.py compile-2.6!skip
plugins/modules/helm_plugin_info.py compile-2.7!skip
plugins/modules/helm_plugin_info.py compile-3.5!skip
plugins/modules/helm_plugin_info.py future-import-boilerplate!skip
plugins/modules/helm_plugin_info.py import-2.6!skip
plugins/modules/helm_plugin_info.py import-2.7!skip
plugins/modules/helm_plugin_info.py import-3.5!skip
plugins/modules/helm_plugin_info.py metaclass-boilerplate!skip
plugins/modules/helm_repository.py compile-2.6!skip
plugins/modules/helm_repository.py compile-2.7!skip
plugins/modules/helm_repository.py compile-3.5!skip
plugins/modules/helm_repository.py future-import-boilerplate!skip
plugins/modules/helm_repository.py import-2.6!skip
plugins/modules/helm_repository.py import-2.7!skip
plugins/modules/helm_repository.py import-3.5!skip
plugins/modules/helm_repository.py metaclass-boilerplate!skip
plugins/modules/helm_template.py compile-2.6!skip
plugins/modules/helm_template.py compile-2.7!skip
plugins/modules/helm_template.py compile-3.5!skip
plugins/modules/helm_template.py future-import-boilerplate!skip
plugins/modules/helm_template.py import-2.6!skip
plugins/modules/helm_template.py import-2.7!skip
plugins/modules/helm_template.py import-3.5!skip
plugins/modules/helm_template.py metaclass-boilerplate!skip
plugins/modules/k8s.py compile-2.6!skip
plugins/modules/k8s.py compile-2.7!skip
plugins/modules/k8s.py compile-3.5!skip
plugins/modules/k8s.py future-import-boilerplate!skip
plugins/modules/k8s.py import-2.6!skip
plugins/modules/k8s.py import-2.7!skip
plugins/modules/k8s.py import-3.5!skip
plugins/modules/k8s.py metaclass-boilerplate!skip
plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s.py validate-modules:return-syntax-error
plugins/modules/k8s_cluster_info.py compile-2.6!skip
plugins/modules/k8s_cluster_info.py compile-2.7!skip
plugins/modules/k8s_cluster_info.py compile-3.5!skip
plugins/modules/k8s_cluster_info.py future-import-boilerplate!skip
plugins/modules/k8s_cluster_info.py import-2.6!skip
plugins/modules/k8s_cluster_info.py import-2.7!skip
plugins/modules/k8s_cluster_info.py import-3.5!skip
plugins/modules/k8s_cluster_info.py metaclass-boilerplate!skip
plugins/modules/k8s_exec.py compile-2.6!skip
plugins/modules/k8s_exec.py compile-2.7!skip
plugins/modules/k8s_exec.py compile-3.5!skip
plugins/modules/k8s_exec.py future-import-boilerplate!skip
plugins/modules/k8s_exec.py import-2.6!skip
plugins/modules/k8s_exec.py import-2.7!skip
plugins/modules/k8s_exec.py import-3.5!skip
plugins/modules/k8s_exec.py metaclass-boilerplate!skip
plugins/modules/k8s_info.py compile-2.6!skip
plugins/modules/k8s_info.py compile-2.7!skip
plugins/modules/k8s_info.py compile-3.5!skip
plugins/modules/k8s_info.py future-import-boilerplate!skip
plugins/modules/k8s_info.py import-2.6!skip
plugins/modules/k8s_info.py import-2.7!skip
plugins/modules/k8s_info.py import-3.5!skip
plugins/modules/k8s_info.py metaclass-boilerplate!skip
plugins/modules/k8s_json_patch.py compile-2.6!skip
plugins/modules/k8s_json_patch.py compile-2.7!skip
plugins/modules/k8s_json_patch.py compile-3.5!skip
plugins/modules/k8s_json_patch.py future-import-boilerplate!skip
plugins/modules/k8s_json_patch.py import-2.6!skip
plugins/modules/k8s_json_patch.py import-2.7!skip
plugins/modules/k8s_json_patch.py import-3.5!skip
plugins/modules/k8s_json_patch.py metaclass-boilerplate!skip
plugins/modules/k8s_log.py compile-2.6!skip
plugins/modules/k8s_log.py compile-2.7!skip
plugins/modules/k8s_log.py compile-3.5!skip
plugins/modules/k8s_log.py future-import-boilerplate!skip
plugins/modules/k8s_log.py import-2.6!skip
plugins/modules/k8s_log.py import-2.7!skip
plugins/modules/k8s_log.py import-3.5!skip
plugins/modules/k8s_log.py metaclass-boilerplate!skip
plugins/modules/k8s_rollback.py compile-2.6!skip
plugins/modules/k8s_rollback.py compile-2.7!skip
plugins/modules/k8s_rollback.py compile-3.5!skip
plugins/modules/k8s_rollback.py future-import-boilerplate!skip
plugins/modules/k8s_rollback.py import-2.6!skip
plugins/modules/k8s_rollback.py import-2.7!skip
plugins/modules/k8s_rollback.py import-3.5!skip
plugins/modules/k8s_rollback.py metaclass-boilerplate!skip
plugins/modules/k8s_scale.py compile-2.6!skip
plugins/modules/k8s_scale.py compile-2.7!skip
plugins/modules/k8s_scale.py compile-3.5!skip
plugins/modules/k8s_scale.py future-import-boilerplate!skip
plugins/modules/k8s_scale.py import-2.6!skip
plugins/modules/k8s_scale.py import-2.7!skip
plugins/modules/k8s_scale.py import-3.5!skip
plugins/modules/k8s_scale.py metaclass-boilerplate!skip
plugins/modules/k8s_scale.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_scale.py validate-modules:return-syntax-error
plugins/modules/k8s_service.py compile-2.6!skip
plugins/modules/k8s_service.py compile-2.7!skip
plugins/modules/k8s_service.py compile-3.5!skip
plugins/modules/k8s_service.py future-import-boilerplate!skip
plugins/modules/k8s_service.py import-2.6!skip
plugins/modules/k8s_service.py import-2.7!skip
plugins/modules/k8s_service.py import-3.5!skip
plugins/modules/k8s_service.py metaclass-boilerplate!skip
plugins/modules/k8s_service.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_service.py validate-modules:return-syntax-error
tests/unit/module_utils/fixtures/definitions.yml yamllint!skip
tests/unit/module_utils/fixtures/deployments.yml yamllint!skip
tests/unit/module_utils/fixtures/pods.yml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart/templates/configmap.yaml yamllint!skip
tests/integration/targets/k8s_scale/files/deployment.yaml yamllint!skip
tests/sanity/refresh_ignore_files shebang!skip
tests/unit/module_utils/test_discoverer.py future-import-boilerplate!skip
tests/unit/module_utils/test_discoverer.py metaclass-boilerplate!skip
plugins/modules/k8s_cp.py compile-2.6!skip
plugins/modules/k8s_cp.py compile-2.7!skip
plugins/modules/k8s_cp.py import-2.6!skip
plugins/modules/k8s_cp.py import-2.7!skip
tests/integration/targets/k8s_copy/library/kubectl_file_compare.py compile-2.6!skip
tests/integration/targets/k8s_copy/library/kubectl_file_compare.py compile-2.7!skip
tests/integration/targets/k8s_copy/library/kubectl_file_compare.py compile-3.5!skip
tests/unit/module_utils/test_selector.py future-import-boilerplate!skip
tests/unit/module_utils/test_selector.py metaclass-boilerplate!skip
plugins/module_utils/selector.py future-import-boilerplate!skip
plugins/module_utils/selector.py metaclass-boilerplate!skip
plugins/lookup/kustomize.py future-import-boilerplate!skip
plugins/lookup/kustomize.py metaclass-boilerplate!skip
tests/integration/targets/helm/library/helm_test_version.py metaclass-boilerplate!skip
tests/integration/targets/helm/library/helm_test_version.py future-import-boilerplate!skip
plugins/modules/k8s_taint.py compile-2.7!skip
plugins/modules/k8s_taint.py compile-3.5!skip
plugins/modules/k8s_taint.py future-import-boilerplate!skip
plugins/modules/k8s_taint.py import-2.7!skip
plugins/modules/k8s_taint.py import-3.5!skip
plugins/modules/k8s_taint.py metaclass-boilerplate!skip
plugins/modules/k8s.py validate-modules:return-syntax-error
plugins/modules/k8s_scale.py validate-modules:return-syntax-error
plugins/modules/k8s_service.py validate-modules:return-syntax-error
plugins/modules/k8s_taint.py validate-modules:return-syntax-error
plugins/doc_fragments/k8s_name_options.py future-import-boilerplate!skip
plugins/doc_fragments/k8s_auth_options.py future-import-boilerplate!skip
plugins/doc_fragments/helm_common_options.py future-import-boilerplate!skip
plugins/doc_fragments/k8s_state_options.py future-import-boilerplate!skip
plugins/doc_fragments/k8s_wait_options.py future-import-boilerplate!skip
plugins/doc_fragments/k8s_scale_options.py future-import-boilerplate!skip
plugins/doc_fragments/k8s_delete_options.py future-import-boilerplate!skip
plugins/doc_fragments/__init__.py future-import-boilerplate!skip
plugins/doc_fragments/k8s_resource_options.py future-import-boilerplate!skip
plugins/module_utils/helm.py future-import-boilerplate!skip
plugins/module_utils/apply.py future-import-boilerplate!skip
plugins/module_utils/hashes.py future-import-boilerplate!skip
plugins/module_utils/version.py future-import-boilerplate!skip
plugins/module_utils/_version.py future-import-boilerplate!skip
plugins/module_utils/copy.py future-import-boilerplate!skip
plugins/module_utils/args_common.py future-import-boilerplate!skip
plugins/module_utils/__init__.py future-import-boilerplate!skip
plugins/module_utils/selector.py future-import-boilerplate!skip
plugins/module_utils/k8sdynamicclient.py future-import-boilerplate!skip
plugins/module_utils/common.py future-import-boilerplate!skip
plugins/module_utils/ansiblemodule.py future-import-boilerplate!skip
plugins/module_utils/exceptions.py future-import-boilerplate!skip
plugins/module_utils/client/resource.py future-import-boilerplate!skip
plugins/module_utils/client/discovery.py future-import-boilerplate!skip
plugins/module_utils/k8s/resource.py future-import-boilerplate!skip
plugins/module_utils/k8s/core.py future-import-boilerplate!skip
plugins/module_utils/k8s/waiter.py future-import-boilerplate!skip
plugins/module_utils/k8s/client.py future-import-boilerplate!skip
plugins/module_utils/k8s/runner.py future-import-boilerplate!skip
plugins/module_utils/k8s/service.py future-import-boilerplate!skip
plugins/module_utils/k8s/exceptions.py future-import-boilerplate!skip
plugins/connection/kubectl.py future-import-boilerplate!skip
plugins/inventory/k8s.py future-import-boilerplate!skip
plugins/lookup/k8s.py future-import-boilerplate!skip
plugins/lookup/kustomize.py future-import-boilerplate!skip
plugins/modules/k8s_scale.py future-import-boilerplate!skip
plugins/modules/helm_template.py future-import-boilerplate!skip
plugins/modules/k8s_exec.py future-import-boilerplate!skip
plugins/modules/helm.py future-import-boilerplate!skip
plugins/modules/helm_plugin_info.py future-import-boilerplate!skip
plugins/modules/helm_info.py future-import-boilerplate!skip
plugins/modules/helm_repository.py future-import-boilerplate!skip
plugins/modules/k8s_rollback.py future-import-boilerplate!skip
plugins/modules/k8s_log.py future-import-boilerplate!skip
plugins/modules/k8s_drain.py future-import-boilerplate!skip
plugins/modules/helm_plugin.py future-import-boilerplate!skip
plugins/modules/k8s_taint.py future-import-boilerplate!skip
plugins/modules/k8s.py future-import-boilerplate!skip
plugins/modules/k8s_service.py future-import-boilerplate!skip
plugins/modules/k8s_cluster_info.py future-import-boilerplate!skip
plugins/modules/k8s_info.py future-import-boilerplate!skip
plugins/modules/k8s_cp.py future-import-boilerplate!skip
plugins/modules/__init__.py future-import-boilerplate!skip
plugins/modules/k8s_json_patch.py future-import-boilerplate!skip
plugins/action/k8s_info.py future-import-boilerplate!skip
plugins/filter/k8s.py future-import-boilerplate!skip
tests/unit/conftest.py future-import-boilerplate!skip
tests/unit/utils/ansible_module_mock.py future-import-boilerplate!skip
tests/unit/module_utils/test_helm.py future-import-boilerplate!skip
tests/unit/module_utils/test_marshal.py future-import-boilerplate!skip
tests/unit/module_utils/test_discoverer.py future-import-boilerplate!skip
tests/unit/module_utils/test_hashes.py future-import-boilerplate!skip
tests/unit/module_utils/test_resource.py future-import-boilerplate!skip
tests/unit/module_utils/test_service.py future-import-boilerplate!skip
tests/unit/module_utils/test_waiter.py future-import-boilerplate!skip
tests/unit/module_utils/test_common.py future-import-boilerplate!skip
tests/unit/module_utils/test_selector.py future-import-boilerplate!skip
tests/unit/module_utils/test_apply.py future-import-boilerplate!skip
tests/unit/module_utils/test_runner.py future-import-boilerplate!skip
tests/unit/module_utils/test_client.py future-import-boilerplate!skip
tests/unit/module_utils/test_core.py future-import-boilerplate!skip
tests/unit/modules/test_helm_template_module.py future-import-boilerplate!skip
tests/unit/modules/test_helm_template.py future-import-boilerplate!skip
tests/unit/modules/test_module_helm.py future-import-boilerplate!skip
tests/unit/action/test_remove_omit.py future-import-boilerplate!skip
plugins/doc_fragments/k8s_name_options.py metaclass-boilerplate!skip
plugins/doc_fragments/k8s_auth_options.py metaclass-boilerplate!skip
plugins/doc_fragments/helm_common_options.py metaclass-boilerplate!skip
plugins/doc_fragments/k8s_state_options.py metaclass-boilerplate!skip
plugins/doc_fragments/k8s_wait_options.py metaclass-boilerplate!skip
plugins/doc_fragments/k8s_scale_options.py metaclass-boilerplate!skip
plugins/doc_fragments/k8s_delete_options.py metaclass-boilerplate!skip
plugins/doc_fragments/__init__.py metaclass-boilerplate!skip
plugins/doc_fragments/k8s_resource_options.py metaclass-boilerplate!skip
plugins/module_utils/helm.py metaclass-boilerplate!skip
plugins/module_utils/apply.py metaclass-boilerplate!skip
plugins/module_utils/hashes.py metaclass-boilerplate!skip
plugins/module_utils/version.py metaclass-boilerplate!skip
plugins/module_utils/_version.py metaclass-boilerplate!skip
plugins/module_utils/copy.py metaclass-boilerplate!skip
plugins/module_utils/copy.py compile-2.6!skip
plugins/module_utils/copy.py compile-2.7!skip
plugins/module_utils/copy.py import-2.6!skip
plugins/module_utils/copy.py import-2.7!skip
plugins/module_utils/args_common.py metaclass-boilerplate!skip
plugins/module_utils/__init__.py metaclass-boilerplate!skip
plugins/module_utils/selector.py metaclass-boilerplate!skip
plugins/module_utils/k8sdynamicclient.py metaclass-boilerplate!skip
plugins/module_utils/common.py metaclass-boilerplate!skip
plugins/module_utils/ansiblemodule.py metaclass-boilerplate!skip
plugins/module_utils/exceptions.py metaclass-boilerplate!skip
plugins/module_utils/client/resource.py metaclass-boilerplate!skip
plugins/module_utils/client/discovery.py metaclass-boilerplate!skip
plugins/module_utils/k8s/resource.py metaclass-boilerplate!skip
plugins/module_utils/k8s/core.py metaclass-boilerplate!skip
plugins/module_utils/k8s/waiter.py metaclass-boilerplate!skip
plugins/module_utils/k8s/client.py metaclass-boilerplate!skip
plugins/module_utils/k8s/runner.py metaclass-boilerplate!skip
plugins/module_utils/k8s/service.py metaclass-boilerplate!skip
plugins/module_utils/k8s/exceptions.py metaclass-boilerplate!skip
plugins/connection/kubectl.py metaclass-boilerplate!skip
plugins/inventory/k8s.py metaclass-boilerplate!skip
plugins/lookup/k8s.py metaclass-boilerplate!skip
plugins/lookup/kustomize.py metaclass-boilerplate!skip
plugins/modules/k8s_scale.py metaclass-boilerplate!skip
plugins/modules/helm_template.py metaclass-boilerplate!skip
plugins/modules/k8s_exec.py metaclass-boilerplate!skip
plugins/modules/helm.py metaclass-boilerplate!skip
plugins/modules/helm_plugin_info.py metaclass-boilerplate!skip
plugins/modules/helm_info.py metaclass-boilerplate!skip
plugins/modules/helm_repository.py metaclass-boilerplate!skip
plugins/modules/k8s_rollback.py metaclass-boilerplate!skip
plugins/modules/k8s_log.py metaclass-boilerplate!skip
plugins/modules/k8s_drain.py metaclass-boilerplate!skip
plugins/modules/helm_plugin.py metaclass-boilerplate!skip
plugins/modules/k8s_taint.py metaclass-boilerplate!skip
plugins/modules/k8s.py metaclass-boilerplate!skip
plugins/modules/k8s_service.py metaclass-boilerplate!skip
plugins/modules/k8s_cluster_info.py metaclass-boilerplate!skip
plugins/modules/k8s_info.py metaclass-boilerplate!skip
plugins/modules/k8s_cp.py metaclass-boilerplate!skip
plugins/modules/__init__.py metaclass-boilerplate!skip
plugins/modules/k8s_json_patch.py metaclass-boilerplate!skip
plugins/action/k8s_info.py metaclass-boilerplate!skip
plugins/filter/k8s.py metaclass-boilerplate!skip
tests/unit/conftest.py metaclass-boilerplate!skip
tests/unit/utils/ansible_module_mock.py metaclass-boilerplate!skip
tests/unit/module_utils/test_helm.py metaclass-boilerplate!skip
tests/unit/module_utils/test_marshal.py metaclass-boilerplate!skip
tests/unit/module_utils/test_discoverer.py metaclass-boilerplate!skip
tests/unit/module_utils/test_hashes.py metaclass-boilerplate!skip
tests/unit/module_utils/test_resource.py metaclass-boilerplate!skip
tests/unit/module_utils/test_service.py metaclass-boilerplate!skip
tests/unit/module_utils/test_waiter.py metaclass-boilerplate!skip
tests/unit/module_utils/test_common.py metaclass-boilerplate!skip
tests/unit/module_utils/test_selector.py metaclass-boilerplate!skip
tests/unit/module_utils/test_apply.py metaclass-boilerplate!skip
tests/unit/module_utils/test_runner.py metaclass-boilerplate!skip
tests/unit/module_utils/test_client.py metaclass-boilerplate!skip
tests/unit/module_utils/test_core.py metaclass-boilerplate!skip
tests/unit/modules/test_helm_template_module.py metaclass-boilerplate!skip
tests/unit/modules/test_helm_template.py metaclass-boilerplate!skip
tests/unit/modules/test_module_helm.py metaclass-boilerplate!skip
tests/unit/action/test_remove_omit.py metaclass-boilerplate!skip

View File

@@ -1,262 +1,178 @@
tests/integration/targets/k8s_scale/files/deployment.yaml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart/templates/configmap.yaml yamllint!skip
plugins/module_utils/__init__.py compile-2.6!skip
plugins/module_utils/__init__.py compile-2.7!skip
plugins/module_utils/__init__.py compile-3.5!skip
plugins/module_utils/__init__.py future-import-boilerplate!skip
plugins/module_utils/__init__.py import-2.6!skip
plugins/module_utils/__init__.py import-2.7!skip
plugins/module_utils/__init__.py import-3.5!skip
plugins/module_utils/__init__.py metaclass-boilerplate!skip
plugins/module_utils/ansiblemodule.py compile-2.6!skip
plugins/module_utils/ansiblemodule.py compile-2.7!skip
plugins/module_utils/ansiblemodule.py compile-3.5!skip
plugins/module_utils/ansiblemodule.py future-import-boilerplate!skip
plugins/module_utils/ansiblemodule.py import-2.6!skip
plugins/module_utils/ansiblemodule.py import-2.7!skip
plugins/module_utils/ansiblemodule.py import-3.5!skip
plugins/module_utils/ansiblemodule.py metaclass-boilerplate!skip
plugins/module_utils/apply.py compile-2.6!skip
plugins/module_utils/apply.py compile-2.7!skip
plugins/module_utils/apply.py compile-3.5!skip
plugins/module_utils/apply.py future-import-boilerplate!skip
plugins/module_utils/apply.py import-2.6!skip
plugins/module_utils/apply.py import-2.7!skip
plugins/module_utils/apply.py import-3.5!skip
plugins/module_utils/apply.py metaclass-boilerplate!skip
plugins/module_utils/args_common.py compile-2.6!skip
plugins/module_utils/args_common.py compile-2.7!skip
plugins/module_utils/args_common.py compile-3.5!skip
plugins/module_utils/args_common.py future-import-boilerplate!skip
plugins/module_utils/args_common.py import-2.6!skip
plugins/module_utils/args_common.py import-2.7!skip
plugins/module_utils/args_common.py import-3.5!skip
plugins/module_utils/args_common.py metaclass-boilerplate!skip
plugins/module_utils/client/discovery.py future-import-boilerplate!skip
plugins/module_utils/client/discovery.py import-2.6!skip
plugins/module_utils/client/discovery.py import-2.7!skip
plugins/module_utils/client/discovery.py import-3.5!skip
plugins/module_utils/client/discovery.py import-3.6!skip
plugins/module_utils/client/discovery.py import-3.7!skip
plugins/module_utils/client/discovery.py import-3.8!skip
plugins/module_utils/client/discovery.py import-3.9!skip
plugins/module_utils/client/discovery.py metaclass-boilerplate!skip
plugins/module_utils/client/resource.py import-2.6!skip
plugins/module_utils/client/resource.py import-2.7!skip
plugins/module_utils/client/resource.py import-3.5!skip
plugins/module_utils/client/resource.py import-3.6!skip
plugins/module_utils/client/resource.py import-3.7!skip
plugins/module_utils/client/resource.py import-3.8!skip
plugins/module_utils/client/resource.py import-3.9!skip
plugins/module_utils/common.py compile-2.6!skip
plugins/module_utils/common.py compile-2.7!skip
plugins/module_utils/common.py compile-3.5!skip
plugins/module_utils/common.py future-import-boilerplate!skip
plugins/module_utils/common.py import-2.6!skip
plugins/module_utils/common.py import-2.7!skip
plugins/module_utils/common.py import-3.5!skip
plugins/module_utils/common.py metaclass-boilerplate!skip
plugins/module_utils/exceptions.py compile-2.6!skip
plugins/module_utils/exceptions.py compile-2.7!skip
plugins/module_utils/exceptions.py compile-3.5!skip
plugins/module_utils/exceptions.py future-import-boilerplate!skip
plugins/module_utils/exceptions.py import-2.6!skip
plugins/module_utils/exceptions.py import-2.7!skip
plugins/module_utils/exceptions.py import-3.5!skip
plugins/module_utils/exceptions.py metaclass-boilerplate!skip
plugins/module_utils/hashes.py compile-2.6!skip
plugins/module_utils/hashes.py compile-2.7!skip
plugins/module_utils/hashes.py compile-3.5!skip
plugins/module_utils/hashes.py future-import-boilerplate!skip
plugins/module_utils/hashes.py import-2.6!skip
plugins/module_utils/hashes.py import-2.7!skip
plugins/module_utils/hashes.py import-3.5!skip
plugins/module_utils/hashes.py metaclass-boilerplate!skip
plugins/module_utils/helm.py compile-2.6!skip
plugins/module_utils/helm.py compile-2.7!skip
plugins/module_utils/helm.py compile-3.5!skip
plugins/module_utils/helm.py future-import-boilerplate!skip
plugins/module_utils/helm.py import-2.6!skip
plugins/module_utils/helm.py import-2.7!skip
plugins/module_utils/helm.py import-3.5!skip
plugins/module_utils/helm.py metaclass-boilerplate!skip
plugins/module_utils/k8sdynamicclient.py compile-2.6!skip
plugins/module_utils/k8sdynamicclient.py compile-2.7!skip
plugins/module_utils/k8sdynamicclient.py compile-3.5!skip
plugins/module_utils/k8sdynamicclient.py future-import-boilerplate!skip
plugins/module_utils/k8sdynamicclient.py import-2.6!skip
plugins/module_utils/k8sdynamicclient.py import-2.7!skip
plugins/module_utils/k8sdynamicclient.py import-3.5!skip
plugins/module_utils/k8sdynamicclient.py import-3.6!skip
plugins/module_utils/k8sdynamicclient.py import-3.7!skip
plugins/module_utils/k8sdynamicclient.py import-3.8!skip
plugins/module_utils/k8sdynamicclient.py import-3.9!skip
plugins/module_utils/k8sdynamicclient.py metaclass-boilerplate!skip
plugins/modules/__init__.py compile-2.6!skip
plugins/modules/__init__.py compile-2.7!skip
plugins/modules/__init__.py compile-3.5!skip
plugins/modules/__init__.py future-import-boilerplate!skip
plugins/modules/__init__.py import-2.6!skip
plugins/modules/__init__.py import-2.7!skip
plugins/modules/__init__.py import-3.5!skip
plugins/modules/__init__.py metaclass-boilerplate!skip
plugins/modules/helm.py compile-2.6!skip
plugins/modules/helm.py compile-2.7!skip
plugins/modules/helm.py compile-3.5!skip
plugins/modules/helm.py future-import-boilerplate!skip
plugins/modules/helm.py import-2.6!skip
plugins/modules/helm.py import-2.7!skip
plugins/modules/helm.py import-3.5!skip
plugins/modules/helm.py metaclass-boilerplate!skip
plugins/modules/helm_info.py compile-2.6!skip
plugins/modules/helm_info.py compile-2.7!skip
plugins/modules/helm_info.py compile-3.5!skip
plugins/modules/helm_info.py future-import-boilerplate!skip
plugins/modules/helm_info.py import-2.6!skip
plugins/modules/helm_info.py import-2.7!skip
plugins/modules/helm_info.py import-3.5!skip
plugins/modules/helm_info.py metaclass-boilerplate!skip
plugins/modules/helm_plugin.py compile-2.6!skip
plugins/modules/helm_plugin.py compile-2.7!skip
plugins/modules/helm_plugin.py compile-3.5!skip
plugins/modules/helm_plugin.py future-import-boilerplate!skip
plugins/modules/helm_plugin.py import-2.6!skip
plugins/modules/helm_plugin.py import-2.7!skip
plugins/modules/helm_plugin.py import-3.5!skip
plugins/modules/helm_plugin.py metaclass-boilerplate!skip
plugins/modules/helm_plugin_info.py compile-2.6!skip
plugins/modules/helm_plugin_info.py compile-2.7!skip
plugins/modules/helm_plugin_info.py compile-3.5!skip
plugins/modules/helm_plugin_info.py future-import-boilerplate!skip
plugins/modules/helm_plugin_info.py import-2.6!skip
plugins/modules/helm_plugin_info.py import-2.7!skip
plugins/modules/helm_plugin_info.py import-3.5!skip
plugins/modules/helm_plugin_info.py metaclass-boilerplate!skip
plugins/modules/helm_repository.py compile-2.6!skip
plugins/modules/helm_repository.py compile-2.7!skip
plugins/modules/helm_repository.py compile-3.5!skip
plugins/modules/helm_repository.py future-import-boilerplate!skip
plugins/modules/helm_repository.py import-2.6!skip
plugins/modules/helm_repository.py import-2.7!skip
plugins/modules/helm_repository.py import-3.5!skip
plugins/modules/helm_repository.py metaclass-boilerplate!skip
plugins/modules/helm_template.py compile-2.6!skip
plugins/modules/helm_template.py compile-2.7!skip
plugins/modules/helm_template.py compile-3.5!skip
plugins/modules/helm_template.py future-import-boilerplate!skip
plugins/modules/helm_template.py import-2.6!skip
plugins/modules/helm_template.py import-2.7!skip
plugins/modules/helm_template.py import-3.5!skip
plugins/modules/helm_template.py metaclass-boilerplate!skip
plugins/modules/k8s.py compile-2.6!skip
plugins/modules/k8s.py compile-2.7!skip
plugins/modules/k8s.py compile-3.5!skip
plugins/modules/k8s.py future-import-boilerplate!skip
plugins/modules/k8s.py import-2.6!skip
plugins/modules/k8s.py import-2.7!skip
plugins/modules/k8s.py import-3.5!skip
plugins/modules/k8s.py metaclass-boilerplate!skip
plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s.py validate-modules:return-syntax-error
plugins/modules/k8s_cluster_info.py compile-2.6!skip
plugins/modules/k8s_cluster_info.py compile-2.7!skip
plugins/modules/k8s_cluster_info.py compile-3.5!skip
plugins/modules/k8s_cluster_info.py future-import-boilerplate!skip
plugins/modules/k8s_cluster_info.py import-2.6!skip
plugins/modules/k8s_cluster_info.py import-2.7!skip
plugins/modules/k8s_cluster_info.py import-3.5!skip
plugins/modules/k8s_cluster_info.py metaclass-boilerplate!skip
plugins/modules/k8s_exec.py compile-2.6!skip
plugins/modules/k8s_exec.py compile-2.7!skip
plugins/modules/k8s_exec.py compile-3.5!skip
plugins/modules/k8s_exec.py future-import-boilerplate!skip
plugins/modules/k8s_exec.py import-2.6!skip
plugins/modules/k8s_exec.py import-2.7!skip
plugins/modules/k8s_exec.py import-3.5!skip
plugins/modules/k8s_exec.py metaclass-boilerplate!skip
plugins/modules/k8s_info.py compile-2.6!skip
plugins/modules/k8s_info.py compile-2.7!skip
plugins/modules/k8s_info.py compile-3.5!skip
plugins/modules/k8s_info.py future-import-boilerplate!skip
plugins/modules/k8s_info.py import-2.6!skip
plugins/modules/k8s_info.py import-2.7!skip
plugins/modules/k8s_info.py import-3.5!skip
plugins/modules/k8s_info.py metaclass-boilerplate!skip
plugins/modules/k8s_json_patch.py compile-2.6!skip
plugins/modules/k8s_json_patch.py compile-2.7!skip
plugins/modules/k8s_json_patch.py compile-3.5!skip
plugins/modules/k8s_json_patch.py future-import-boilerplate!skip
plugins/modules/k8s_json_patch.py import-2.6!skip
plugins/modules/k8s_json_patch.py import-2.7!skip
plugins/modules/k8s_json_patch.py import-3.5!skip
plugins/modules/k8s_json_patch.py metaclass-boilerplate!skip
plugins/modules/k8s_log.py compile-2.6!skip
plugins/modules/k8s_log.py compile-2.7!skip
plugins/modules/k8s_log.py compile-3.5!skip
plugins/modules/k8s_log.py future-import-boilerplate!skip
plugins/modules/k8s_log.py import-2.6!skip
plugins/modules/k8s_log.py import-2.7!skip
plugins/modules/k8s_log.py import-3.5!skip
plugins/modules/k8s_log.py metaclass-boilerplate!skip
plugins/modules/k8s_rollback.py compile-2.6!skip
plugins/modules/k8s_rollback.py compile-2.7!skip
plugins/modules/k8s_rollback.py compile-3.5!skip
plugins/modules/k8s_rollback.py future-import-boilerplate!skip
plugins/modules/k8s_rollback.py import-2.6!skip
plugins/modules/k8s_rollback.py import-2.7!skip
plugins/modules/k8s_rollback.py import-3.5!skip
plugins/modules/k8s_rollback.py metaclass-boilerplate!skip
plugins/modules/k8s_scale.py compile-2.6!skip
plugins/modules/k8s_scale.py compile-2.7!skip
plugins/modules/k8s_scale.py compile-3.5!skip
plugins/modules/k8s_scale.py future-import-boilerplate!skip
plugins/modules/k8s_scale.py import-2.6!skip
plugins/modules/k8s_scale.py import-2.7!skip
plugins/modules/k8s_scale.py import-3.5!skip
plugins/modules/k8s_scale.py metaclass-boilerplate!skip
plugins/modules/k8s_scale.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_scale.py validate-modules:return-syntax-error
plugins/modules/k8s_service.py compile-2.6!skip
plugins/modules/k8s_service.py compile-2.7!skip
plugins/modules/k8s_service.py compile-3.5!skip
plugins/modules/k8s_service.py future-import-boilerplate!skip
plugins/modules/k8s_service.py import-2.6!skip
plugins/modules/k8s_service.py import-2.7!skip
plugins/modules/k8s_service.py import-3.5!skip
plugins/modules/k8s_service.py metaclass-boilerplate!skip
plugins/modules/k8s_service.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_service.py validate-modules:return-syntax-error
tests/unit/module_utils/fixtures/definitions.yml yamllint!skip
tests/unit/module_utils/fixtures/deployments.yml yamllint!skip
tests/unit/module_utils/fixtures/pods.yml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart/templates/configmap.yaml yamllint!skip
tests/integration/targets/k8s_scale/files/deployment.yaml yamllint!skip
tests/sanity/refresh_ignore_files shebang!skip
tests/unit/module_utils/test_discoverer.py future-import-boilerplate!skip
tests/unit/module_utils/test_discoverer.py metaclass-boilerplate!skip
plugins/modules/k8s_cp.py compile-2.6!skip
plugins/modules/k8s_cp.py compile-2.7!skip
plugins/modules/k8s_cp.py import-2.6!skip
plugins/modules/k8s_cp.py import-2.7!skip
tests/integration/targets/k8s_copy/library/kubectl_file_compare.py compile-2.6!skip
tests/integration/targets/k8s_copy/library/kubectl_file_compare.py compile-2.7!skip
tests/integration/targets/k8s_copy/library/kubectl_file_compare.py compile-3.5!skip
tests/unit/module_utils/test_selector.py future-import-boilerplate!skip
tests/unit/module_utils/test_selector.py metaclass-boilerplate!skip
plugins/module_utils/selector.py future-import-boilerplate!skip
plugins/module_utils/selector.py metaclass-boilerplate!skip
plugins/lookup/kustomize.py future-import-boilerplate!skip
plugins/lookup/kustomize.py metaclass-boilerplate!skip
tests/integration/targets/helm/library/helm_test_version.py metaclass-boilerplate!skip
tests/integration/targets/helm/library/helm_test_version.py future-import-boilerplate!skip
plugins/modules/k8s_taint.py compile-2.7!skip
plugins/modules/k8s_taint.py compile-3.5!skip
plugins/modules/k8s_taint.py future-import-boilerplate!skip
plugins/modules/k8s_taint.py import-2.7!skip
plugins/modules/k8s_taint.py import-3.5!skip
plugins/modules/k8s_taint.py metaclass-boilerplate!skip
plugins/modules/k8s.py validate-modules:return-syntax-error
plugins/modules/k8s_scale.py validate-modules:return-syntax-error
plugins/modules/k8s_service.py validate-modules:return-syntax-error
plugins/modules/k8s_taint.py validate-modules:return-syntax-error
plugins/doc_fragments/k8s_name_options.py future-import-boilerplate!skip
plugins/doc_fragments/k8s_auth_options.py future-import-boilerplate!skip
plugins/doc_fragments/helm_common_options.py future-import-boilerplate!skip
plugins/doc_fragments/k8s_state_options.py future-import-boilerplate!skip
plugins/doc_fragments/k8s_wait_options.py future-import-boilerplate!skip
plugins/doc_fragments/k8s_scale_options.py future-import-boilerplate!skip
plugins/doc_fragments/k8s_delete_options.py future-import-boilerplate!skip
plugins/doc_fragments/__init__.py future-import-boilerplate!skip
plugins/doc_fragments/k8s_resource_options.py future-import-boilerplate!skip
plugins/module_utils/helm.py future-import-boilerplate!skip
plugins/module_utils/apply.py future-import-boilerplate!skip
plugins/module_utils/hashes.py future-import-boilerplate!skip
plugins/module_utils/version.py future-import-boilerplate!skip
plugins/module_utils/_version.py future-import-boilerplate!skip
plugins/module_utils/copy.py future-import-boilerplate!skip
plugins/module_utils/args_common.py future-import-boilerplate!skip
plugins/module_utils/__init__.py future-import-boilerplate!skip
plugins/module_utils/selector.py future-import-boilerplate!skip
plugins/module_utils/k8sdynamicclient.py future-import-boilerplate!skip
plugins/module_utils/common.py future-import-boilerplate!skip
plugins/module_utils/ansiblemodule.py future-import-boilerplate!skip
plugins/module_utils/exceptions.py future-import-boilerplate!skip
plugins/module_utils/client/resource.py future-import-boilerplate!skip
plugins/module_utils/client/discovery.py future-import-boilerplate!skip
plugins/module_utils/k8s/resource.py future-import-boilerplate!skip
plugins/module_utils/k8s/core.py future-import-boilerplate!skip
plugins/module_utils/k8s/waiter.py future-import-boilerplate!skip
plugins/module_utils/k8s/client.py future-import-boilerplate!skip
plugins/module_utils/k8s/runner.py future-import-boilerplate!skip
plugins/module_utils/k8s/service.py future-import-boilerplate!skip
plugins/module_utils/k8s/exceptions.py future-import-boilerplate!skip
plugins/connection/kubectl.py future-import-boilerplate!skip
plugins/inventory/k8s.py future-import-boilerplate!skip
plugins/lookup/k8s.py future-import-boilerplate!skip
plugins/lookup/kustomize.py future-import-boilerplate!skip
plugins/modules/k8s_scale.py future-import-boilerplate!skip
plugins/modules/helm_template.py future-import-boilerplate!skip
plugins/modules/k8s_exec.py future-import-boilerplate!skip
plugins/modules/helm.py future-import-boilerplate!skip
plugins/modules/helm_plugin_info.py future-import-boilerplate!skip
plugins/modules/helm_info.py future-import-boilerplate!skip
plugins/modules/helm_repository.py future-import-boilerplate!skip
plugins/modules/k8s_rollback.py future-import-boilerplate!skip
plugins/modules/k8s_log.py future-import-boilerplate!skip
plugins/modules/k8s_drain.py future-import-boilerplate!skip
plugins/modules/helm_plugin.py future-import-boilerplate!skip
plugins/modules/k8s_taint.py future-import-boilerplate!skip
plugins/modules/k8s.py future-import-boilerplate!skip
plugins/modules/k8s_service.py future-import-boilerplate!skip
plugins/modules/k8s_cluster_info.py future-import-boilerplate!skip
plugins/modules/k8s_info.py future-import-boilerplate!skip
plugins/modules/k8s_cp.py future-import-boilerplate!skip
plugins/modules/__init__.py future-import-boilerplate!skip
plugins/modules/k8s_json_patch.py future-import-boilerplate!skip
plugins/action/k8s_info.py future-import-boilerplate!skip
plugins/filter/k8s.py future-import-boilerplate!skip
tests/unit/conftest.py future-import-boilerplate!skip
tests/unit/utils/ansible_module_mock.py future-import-boilerplate!skip
tests/unit/module_utils/test_helm.py future-import-boilerplate!skip
tests/unit/module_utils/test_marshal.py future-import-boilerplate!skip
tests/unit/module_utils/test_discoverer.py future-import-boilerplate!skip
tests/unit/module_utils/test_hashes.py future-import-boilerplate!skip
tests/unit/module_utils/test_resource.py future-import-boilerplate!skip
tests/unit/module_utils/test_service.py future-import-boilerplate!skip
tests/unit/module_utils/test_waiter.py future-import-boilerplate!skip
tests/unit/module_utils/test_common.py future-import-boilerplate!skip
tests/unit/module_utils/test_selector.py future-import-boilerplate!skip
tests/unit/module_utils/test_apply.py future-import-boilerplate!skip
tests/unit/module_utils/test_runner.py future-import-boilerplate!skip
tests/unit/module_utils/test_client.py future-import-boilerplate!skip
tests/unit/module_utils/test_core.py future-import-boilerplate!skip
tests/unit/modules/test_helm_template_module.py future-import-boilerplate!skip
tests/unit/modules/test_helm_template.py future-import-boilerplate!skip
tests/unit/modules/test_module_helm.py future-import-boilerplate!skip
tests/unit/action/test_remove_omit.py future-import-boilerplate!skip
plugins/doc_fragments/k8s_name_options.py metaclass-boilerplate!skip
plugins/doc_fragments/k8s_auth_options.py metaclass-boilerplate!skip
plugins/doc_fragments/helm_common_options.py metaclass-boilerplate!skip
plugins/doc_fragments/k8s_state_options.py metaclass-boilerplate!skip
plugins/doc_fragments/k8s_wait_options.py metaclass-boilerplate!skip
plugins/doc_fragments/k8s_scale_options.py metaclass-boilerplate!skip
plugins/doc_fragments/k8s_delete_options.py metaclass-boilerplate!skip
plugins/doc_fragments/__init__.py metaclass-boilerplate!skip
plugins/doc_fragments/k8s_resource_options.py metaclass-boilerplate!skip
plugins/module_utils/helm.py metaclass-boilerplate!skip
plugins/module_utils/apply.py metaclass-boilerplate!skip
plugins/module_utils/hashes.py metaclass-boilerplate!skip
plugins/module_utils/version.py metaclass-boilerplate!skip
plugins/module_utils/_version.py metaclass-boilerplate!skip
plugins/module_utils/copy.py metaclass-boilerplate!skip
plugins/module_utils/copy.py compile-2.6!skip
plugins/module_utils/copy.py compile-2.7!skip
plugins/module_utils/copy.py import-2.6!skip
plugins/module_utils/copy.py import-2.7!skip
plugins/module_utils/args_common.py metaclass-boilerplate!skip
plugins/module_utils/__init__.py metaclass-boilerplate!skip
plugins/module_utils/selector.py metaclass-boilerplate!skip
plugins/module_utils/k8sdynamicclient.py metaclass-boilerplate!skip
plugins/module_utils/common.py metaclass-boilerplate!skip
plugins/module_utils/ansiblemodule.py metaclass-boilerplate!skip
plugins/module_utils/exceptions.py metaclass-boilerplate!skip
plugins/module_utils/client/resource.py metaclass-boilerplate!skip
plugins/module_utils/client/discovery.py metaclass-boilerplate!skip
plugins/module_utils/k8s/resource.py metaclass-boilerplate!skip
plugins/module_utils/k8s/core.py metaclass-boilerplate!skip
plugins/module_utils/k8s/waiter.py metaclass-boilerplate!skip
plugins/module_utils/k8s/client.py metaclass-boilerplate!skip
plugins/module_utils/k8s/runner.py metaclass-boilerplate!skip
plugins/module_utils/k8s/service.py metaclass-boilerplate!skip
plugins/module_utils/k8s/exceptions.py metaclass-boilerplate!skip
plugins/connection/kubectl.py metaclass-boilerplate!skip
plugins/inventory/k8s.py metaclass-boilerplate!skip
plugins/lookup/k8s.py metaclass-boilerplate!skip
plugins/lookup/kustomize.py metaclass-boilerplate!skip
plugins/modules/k8s_scale.py metaclass-boilerplate!skip
plugins/modules/helm_template.py metaclass-boilerplate!skip
plugins/modules/k8s_exec.py metaclass-boilerplate!skip
plugins/modules/helm.py metaclass-boilerplate!skip
plugins/modules/helm_plugin_info.py metaclass-boilerplate!skip
plugins/modules/helm_info.py metaclass-boilerplate!skip
plugins/modules/helm_repository.py metaclass-boilerplate!skip
plugins/modules/k8s_rollback.py metaclass-boilerplate!skip
plugins/modules/k8s_log.py metaclass-boilerplate!skip
plugins/modules/k8s_drain.py metaclass-boilerplate!skip
plugins/modules/helm_plugin.py metaclass-boilerplate!skip
plugins/modules/k8s_taint.py metaclass-boilerplate!skip
plugins/modules/k8s.py metaclass-boilerplate!skip
plugins/modules/k8s_service.py metaclass-boilerplate!skip
plugins/modules/k8s_cluster_info.py metaclass-boilerplate!skip
plugins/modules/k8s_info.py metaclass-boilerplate!skip
plugins/modules/k8s_cp.py metaclass-boilerplate!skip
plugins/modules/__init__.py metaclass-boilerplate!skip
plugins/modules/k8s_json_patch.py metaclass-boilerplate!skip
plugins/action/k8s_info.py metaclass-boilerplate!skip
plugins/filter/k8s.py metaclass-boilerplate!skip
tests/unit/conftest.py metaclass-boilerplate!skip
tests/unit/utils/ansible_module_mock.py metaclass-boilerplate!skip
tests/unit/module_utils/test_helm.py metaclass-boilerplate!skip
tests/unit/module_utils/test_marshal.py metaclass-boilerplate!skip
tests/unit/module_utils/test_discoverer.py metaclass-boilerplate!skip
tests/unit/module_utils/test_hashes.py metaclass-boilerplate!skip
tests/unit/module_utils/test_resource.py metaclass-boilerplate!skip
tests/unit/module_utils/test_service.py metaclass-boilerplate!skip
tests/unit/module_utils/test_waiter.py metaclass-boilerplate!skip
tests/unit/module_utils/test_common.py metaclass-boilerplate!skip
tests/unit/module_utils/test_selector.py metaclass-boilerplate!skip
tests/unit/module_utils/test_apply.py metaclass-boilerplate!skip
tests/unit/module_utils/test_runner.py metaclass-boilerplate!skip
tests/unit/module_utils/test_client.py metaclass-boilerplate!skip
tests/unit/module_utils/test_core.py metaclass-boilerplate!skip
tests/unit/modules/test_helm_template_module.py metaclass-boilerplate!skip
tests/unit/modules/test_helm_template.py metaclass-boilerplate!skip
tests/unit/modules/test_module_helm.py metaclass-boilerplate!skip
tests/unit/action/test_remove_omit.py metaclass-boilerplate!skip

View File

@@ -1,248 +1,31 @@
tests/integration/targets/k8s_scale/files/deployment.yaml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart/templates/configmap.yaml yamllint!skip
plugins/module_utils/__init__.py compile-2.6!skip
plugins/module_utils/__init__.py compile-2.7!skip
plugins/module_utils/__init__.py compile-3.5!skip
plugins/module_utils/__init__.py future-import-boilerplate!skip
plugins/module_utils/__init__.py import-2.6!skip
plugins/module_utils/__init__.py import-2.7!skip
plugins/module_utils/__init__.py import-3.5!skip
plugins/module_utils/__init__.py metaclass-boilerplate!skip
plugins/module_utils/ansiblemodule.py compile-2.6!skip
plugins/module_utils/ansiblemodule.py compile-2.7!skip
plugins/module_utils/ansiblemodule.py compile-3.5!skip
plugins/module_utils/ansiblemodule.py future-import-boilerplate!skip
plugins/module_utils/ansiblemodule.py import-2.6!skip
plugins/module_utils/ansiblemodule.py import-2.7!skip
plugins/module_utils/ansiblemodule.py import-3.5!skip
plugins/module_utils/ansiblemodule.py metaclass-boilerplate!skip
plugins/module_utils/apply.py compile-2.6!skip
plugins/module_utils/apply.py compile-2.7!skip
plugins/module_utils/apply.py compile-3.5!skip
plugins/module_utils/apply.py future-import-boilerplate!skip
plugins/module_utils/apply.py import-2.6!skip
plugins/module_utils/apply.py import-2.7!skip
plugins/module_utils/apply.py import-3.5!skip
plugins/module_utils/apply.py metaclass-boilerplate!skip
plugins/module_utils/args_common.py compile-2.6!skip
plugins/module_utils/args_common.py compile-2.7!skip
plugins/module_utils/args_common.py compile-3.5!skip
plugins/module_utils/args_common.py future-import-boilerplate!skip
plugins/module_utils/args_common.py import-2.6!skip
plugins/module_utils/args_common.py import-2.7!skip
plugins/module_utils/args_common.py import-3.5!skip
plugins/module_utils/args_common.py metaclass-boilerplate!skip
plugins/module_utils/client/discovery.py future-import-boilerplate!skip
plugins/module_utils/client/discovery.py import-2.6!skip
plugins/module_utils/client/discovery.py import-2.7!skip
plugins/module_utils/client/discovery.py import-3.5!skip
plugins/module_utils/client/discovery.py import-3.6!skip
plugins/module_utils/client/discovery.py import-3.7!skip
plugins/module_utils/client/discovery.py import-3.8!skip
plugins/module_utils/client/discovery.py import-3.9!skip
plugins/module_utils/client/discovery.py import-3.10!skip
plugins/module_utils/client/discovery.py metaclass-boilerplate!skip
plugins/module_utils/client/resource.py import-2.6!skip
plugins/module_utils/client/resource.py import-2.7!skip
plugins/module_utils/client/resource.py import-3.5!skip
plugins/module_utils/client/resource.py import-3.6!skip
plugins/module_utils/client/resource.py import-3.7!skip
plugins/module_utils/client/resource.py import-3.8!skip
plugins/module_utils/client/resource.py import-3.9!skip
plugins/module_utils/client/resource.py import-3.10!skip
plugins/module_utils/common.py compile-2.6!skip
plugins/module_utils/common.py compile-2.7!skip
plugins/module_utils/common.py compile-3.5!skip
plugins/module_utils/common.py future-import-boilerplate!skip
plugins/module_utils/common.py import-2.6!skip
plugins/module_utils/common.py import-2.7!skip
plugins/module_utils/common.py import-3.5!skip
plugins/module_utils/common.py metaclass-boilerplate!skip
plugins/module_utils/exceptions.py compile-2.6!skip
plugins/module_utils/exceptions.py compile-2.7!skip
plugins/module_utils/exceptions.py compile-3.5!skip
plugins/module_utils/exceptions.py future-import-boilerplate!skip
plugins/module_utils/exceptions.py import-2.6!skip
plugins/module_utils/exceptions.py import-2.7!skip
plugins/module_utils/exceptions.py import-3.5!skip
plugins/module_utils/exceptions.py metaclass-boilerplate!skip
plugins/module_utils/hashes.py compile-2.6!skip
plugins/module_utils/hashes.py compile-2.7!skip
plugins/module_utils/hashes.py compile-3.5!skip
plugins/module_utils/hashes.py future-import-boilerplate!skip
plugins/module_utils/hashes.py import-2.6!skip
plugins/module_utils/hashes.py import-2.7!skip
plugins/module_utils/hashes.py import-3.5!skip
plugins/module_utils/hashes.py metaclass-boilerplate!skip
plugins/module_utils/helm.py compile-2.6!skip
plugins/module_utils/helm.py compile-2.7!skip
plugins/module_utils/helm.py compile-3.5!skip
plugins/module_utils/helm.py future-import-boilerplate!skip
plugins/module_utils/helm.py import-2.6!skip
plugins/module_utils/helm.py import-2.7!skip
plugins/module_utils/helm.py import-3.5!skip
plugins/module_utils/helm.py metaclass-boilerplate!skip
plugins/module_utils/k8sdynamicclient.py compile-2.6!skip
plugins/module_utils/k8sdynamicclient.py compile-2.7!skip
plugins/module_utils/k8sdynamicclient.py compile-3.5!skip
plugins/module_utils/k8sdynamicclient.py future-import-boilerplate!skip
plugins/module_utils/k8sdynamicclient.py import-2.6!skip
plugins/module_utils/k8sdynamicclient.py import-2.7!skip
plugins/module_utils/k8sdynamicclient.py import-3.5!skip
plugins/module_utils/k8sdynamicclient.py import-3.6!skip
plugins/module_utils/k8sdynamicclient.py import-3.7!skip
plugins/module_utils/k8sdynamicclient.py import-3.8!skip
plugins/module_utils/k8sdynamicclient.py import-3.9!skip
plugins/module_utils/k8sdynamicclient.py import-3.10!skip
plugins/module_utils/k8sdynamicclient.py metaclass-boilerplate!skip
plugins/modules/__init__.py compile-2.6!skip
plugins/modules/__init__.py compile-2.7!skip
plugins/modules/__init__.py compile-3.5!skip
plugins/modules/__init__.py future-import-boilerplate!skip
plugins/modules/__init__.py import-2.6!skip
plugins/modules/__init__.py import-2.7!skip
plugins/modules/__init__.py import-3.5!skip
plugins/modules/__init__.py metaclass-boilerplate!skip
plugins/modules/helm.py compile-2.6!skip
plugins/modules/helm.py compile-2.7!skip
plugins/modules/helm.py compile-3.5!skip
plugins/modules/helm.py future-import-boilerplate!skip
plugins/modules/helm.py import-2.6!skip
plugins/modules/helm.py import-2.7!skip
plugins/modules/helm.py import-3.5!skip
plugins/modules/helm.py metaclass-boilerplate!skip
plugins/modules/helm_info.py compile-2.6!skip
plugins/modules/helm_info.py compile-2.7!skip
plugins/modules/helm_info.py compile-3.5!skip
plugins/modules/helm_info.py future-import-boilerplate!skip
plugins/modules/helm_info.py import-2.6!skip
plugins/modules/helm_info.py import-2.7!skip
plugins/modules/helm_info.py import-3.5!skip
plugins/modules/helm_info.py metaclass-boilerplate!skip
plugins/modules/helm_plugin.py compile-2.6!skip
plugins/modules/helm_plugin.py compile-2.7!skip
plugins/modules/helm_plugin.py compile-3.5!skip
plugins/modules/helm_plugin.py future-import-boilerplate!skip
plugins/modules/helm_plugin.py import-2.6!skip
plugins/modules/helm_plugin.py import-2.7!skip
plugins/modules/helm_plugin.py import-3.5!skip
plugins/modules/helm_plugin.py metaclass-boilerplate!skip
plugins/modules/helm_plugin_info.py compile-2.6!skip
plugins/modules/helm_plugin_info.py compile-2.7!skip
plugins/modules/helm_plugin_info.py compile-3.5!skip
plugins/modules/helm_plugin_info.py future-import-boilerplate!skip
plugins/modules/helm_plugin_info.py import-2.6!skip
plugins/modules/helm_plugin_info.py import-2.7!skip
plugins/modules/helm_plugin_info.py import-3.5!skip
plugins/modules/helm_plugin_info.py metaclass-boilerplate!skip
plugins/modules/helm_repository.py compile-2.6!skip
plugins/modules/helm_repository.py compile-2.7!skip
plugins/modules/helm_repository.py compile-3.5!skip
plugins/modules/helm_repository.py future-import-boilerplate!skip
plugins/modules/helm_repository.py import-2.6!skip
plugins/modules/helm_repository.py import-2.7!skip
plugins/modules/helm_repository.py import-3.5!skip
plugins/modules/helm_repository.py metaclass-boilerplate!skip
plugins/modules/helm_template.py compile-2.6!skip
plugins/modules/helm_template.py compile-2.7!skip
plugins/modules/helm_template.py compile-3.5!skip
plugins/modules/helm_template.py future-import-boilerplate!skip
plugins/modules/helm_template.py import-2.6!skip
plugins/modules/helm_template.py import-2.7!skip
plugins/modules/helm_template.py import-3.5!skip
plugins/modules/helm_template.py metaclass-boilerplate!skip
plugins/modules/k8s.py compile-2.6!skip
plugins/modules/k8s.py compile-2.7!skip
plugins/modules/k8s.py compile-3.5!skip
plugins/modules/k8s.py future-import-boilerplate!skip
plugins/modules/k8s.py import-2.6!skip
plugins/modules/k8s.py import-2.7!skip
plugins/modules/k8s.py import-3.5!skip
plugins/modules/k8s.py metaclass-boilerplate!skip
plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s.py validate-modules:return-syntax-error
plugins/modules/k8s_cluster_info.py compile-2.6!skip
plugins/modules/k8s_cluster_info.py compile-2.7!skip
plugins/modules/k8s_cluster_info.py compile-3.5!skip
plugins/modules/k8s_cluster_info.py future-import-boilerplate!skip
plugins/modules/k8s_cluster_info.py import-2.6!skip
plugins/modules/k8s_cluster_info.py import-2.7!skip
plugins/modules/k8s_cluster_info.py import-3.5!skip
plugins/modules/k8s_cluster_info.py metaclass-boilerplate!skip
plugins/modules/k8s_exec.py compile-2.6!skip
plugins/modules/k8s_exec.py compile-2.7!skip
plugins/modules/k8s_exec.py compile-3.5!skip
plugins/modules/k8s_exec.py future-import-boilerplate!skip
plugins/modules/k8s_exec.py import-2.6!skip
plugins/modules/k8s_exec.py import-2.7!skip
plugins/modules/k8s_exec.py import-3.5!skip
plugins/modules/k8s_exec.py metaclass-boilerplate!skip
plugins/modules/k8s_info.py compile-2.6!skip
plugins/modules/k8s_info.py compile-2.7!skip
plugins/modules/k8s_info.py compile-3.5!skip
plugins/modules/k8s_info.py future-import-boilerplate!skip
plugins/modules/k8s_info.py import-2.6!skip
plugins/modules/k8s_info.py import-2.7!skip
plugins/modules/k8s_info.py import-3.5!skip
plugins/modules/k8s_info.py metaclass-boilerplate!skip
plugins/modules/k8s_json_patch.py compile-2.6!skip
plugins/modules/k8s_json_patch.py compile-2.7!skip
plugins/modules/k8s_json_patch.py compile-3.5!skip
plugins/modules/k8s_json_patch.py future-import-boilerplate!skip
plugins/modules/k8s_json_patch.py import-2.6!skip
plugins/modules/k8s_json_patch.py import-2.7!skip
plugins/modules/k8s_json_patch.py import-3.5!skip
plugins/modules/k8s_json_patch.py metaclass-boilerplate!skip
plugins/modules/k8s_log.py compile-2.6!skip
plugins/modules/k8s_log.py compile-2.7!skip
plugins/modules/k8s_log.py compile-3.5!skip
plugins/modules/k8s_log.py future-import-boilerplate!skip
plugins/modules/k8s_log.py import-2.6!skip
plugins/modules/k8s_log.py import-2.7!skip
plugins/modules/k8s_log.py import-3.5!skip
plugins/modules/k8s_log.py metaclass-boilerplate!skip
plugins/modules/k8s_rollback.py compile-2.6!skip
plugins/modules/k8s_rollback.py compile-2.7!skip
plugins/modules/k8s_rollback.py compile-3.5!skip
plugins/modules/k8s_rollback.py future-import-boilerplate!skip
plugins/modules/k8s_rollback.py import-2.6!skip
plugins/modules/k8s_rollback.py import-2.7!skip
plugins/modules/k8s_rollback.py import-3.5!skip
plugins/modules/k8s_rollback.py metaclass-boilerplate!skip
plugins/modules/k8s_scale.py compile-2.6!skip
plugins/modules/k8s_scale.py compile-2.7!skip
plugins/modules/k8s_scale.py compile-3.5!skip
plugins/modules/k8s_scale.py future-import-boilerplate!skip
plugins/modules/k8s_scale.py import-2.6!skip
plugins/modules/k8s_scale.py import-2.7!skip
plugins/modules/k8s_scale.py import-3.5!skip
plugins/modules/k8s_scale.py metaclass-boilerplate!skip
plugins/modules/k8s_scale.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_scale.py validate-modules:return-syntax-error
plugins/modules/k8s_service.py compile-2.6!skip
plugins/modules/k8s_service.py compile-2.7!skip
plugins/modules/k8s_service.py compile-3.5!skip
plugins/modules/k8s_service.py future-import-boilerplate!skip
plugins/modules/k8s_service.py import-2.6!skip
plugins/modules/k8s_service.py import-2.7!skip
plugins/modules/k8s_service.py import-3.5!skip
plugins/modules/k8s_service.py metaclass-boilerplate!skip
plugins/modules/k8s_service.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_service.py validate-modules:return-syntax-error
tests/unit/module_utils/fixtures/definitions.yml yamllint!skip
tests/unit/module_utils/fixtures/deployments.yml yamllint!skip
tests/unit/module_utils/fixtures/pods.yml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart/templates/configmap.yaml yamllint!skip
tests/integration/targets/k8s_scale/files/deployment.yaml yamllint!skip
tests/sanity/refresh_ignore_files shebang!skip
plugins/modules/k8s_cp.py compile-2.6!skip
plugins/modules/k8s_cp.py compile-2.7!skip
plugins/modules/k8s_cp.py import-2.6!skip
plugins/modules/k8s_cp.py import-2.7!skip
plugins/module_utils/selector.py future-import-boilerplate!skip
plugins/module_utils/selector.py metaclass-boilerplate!skip
plugins/modules/k8s_taint.py compile-2.7!skip
plugins/modules/k8s_taint.py compile-3.5!skip
plugins/modules/k8s_taint.py future-import-boilerplate!skip
plugins/modules/k8s_taint.py import-2.7!skip
plugins/modules/k8s_taint.py import-3.5!skip
plugins/modules/k8s_taint.py metaclass-boilerplate!skip
plugins/modules/k8s_taint.py validate-modules:return-syntax-error
plugins/modules/k8s.py validate-modules:return-syntax-error
plugins/modules/k8s_scale.py validate-modules:return-syntax-error
plugins/modules/k8s_service.py validate-modules:return-syntax-error
plugins/modules/k8s_taint.py validate-modules:return-syntax-error

View File

@@ -1,196 +1,31 @@
tests/integration/targets/k8s_scale/files/deployment.yaml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart/templates/configmap.yaml yamllint!skip
plugins/module_utils/__init__.py compile-2.7!skip
plugins/module_utils/__init__.py compile-3.5!skip
plugins/module_utils/__init__.py future-import-boilerplate!skip
plugins/module_utils/__init__.py import-2.7!skip
plugins/module_utils/__init__.py import-3.5!skip
plugins/module_utils/__init__.py metaclass-boilerplate!skip
plugins/module_utils/ansiblemodule.py compile-2.7!skip
plugins/module_utils/ansiblemodule.py compile-3.5!skip
plugins/module_utils/ansiblemodule.py future-import-boilerplate!skip
plugins/module_utils/ansiblemodule.py import-2.7!skip
plugins/module_utils/ansiblemodule.py import-3.5!skip
plugins/module_utils/ansiblemodule.py metaclass-boilerplate!skip
plugins/module_utils/apply.py compile-2.7!skip
plugins/module_utils/apply.py compile-3.5!skip
plugins/module_utils/apply.py future-import-boilerplate!skip
plugins/module_utils/apply.py import-2.7!skip
plugins/module_utils/apply.py import-3.5!skip
plugins/module_utils/apply.py metaclass-boilerplate!skip
plugins/module_utils/args_common.py compile-2.7!skip
plugins/module_utils/args_common.py compile-3.5!skip
plugins/module_utils/args_common.py future-import-boilerplate!skip
plugins/module_utils/args_common.py import-2.7!skip
plugins/module_utils/args_common.py import-3.5!skip
plugins/module_utils/args_common.py metaclass-boilerplate!skip
plugins/module_utils/client/discovery.py future-import-boilerplate!skip
plugins/module_utils/client/discovery.py import-2.7!skip
plugins/module_utils/client/discovery.py import-3.5!skip
plugins/module_utils/client/discovery.py import-3.6!skip
plugins/module_utils/client/discovery.py import-3.7!skip
plugins/module_utils/client/discovery.py import-3.8!skip
plugins/module_utils/client/discovery.py import-3.9!skip
plugins/module_utils/client/discovery.py import-3.10!skip
plugins/module_utils/client/discovery.py metaclass-boilerplate!skip
plugins/module_utils/client/resource.py import-2.7!skip
plugins/module_utils/client/resource.py import-3.5!skip
plugins/module_utils/client/resource.py import-3.6!skip
plugins/module_utils/client/resource.py import-3.7!skip
plugins/module_utils/client/resource.py import-3.8!skip
plugins/module_utils/client/resource.py import-3.9!skip
plugins/module_utils/client/resource.py import-3.10!skip
plugins/module_utils/common.py compile-2.7!skip
plugins/module_utils/common.py compile-3.5!skip
plugins/module_utils/common.py future-import-boilerplate!skip
plugins/module_utils/common.py import-2.7!skip
plugins/module_utils/common.py import-3.5!skip
plugins/module_utils/common.py metaclass-boilerplate!skip
plugins/module_utils/exceptions.py compile-2.7!skip
plugins/module_utils/exceptions.py compile-3.5!skip
plugins/module_utils/exceptions.py future-import-boilerplate!skip
plugins/module_utils/exceptions.py import-2.7!skip
plugins/module_utils/exceptions.py import-3.5!skip
plugins/module_utils/exceptions.py metaclass-boilerplate!skip
plugins/module_utils/hashes.py compile-2.7!skip
plugins/module_utils/hashes.py compile-3.5!skip
plugins/module_utils/hashes.py future-import-boilerplate!skip
plugins/module_utils/hashes.py import-2.7!skip
plugins/module_utils/hashes.py import-3.5!skip
plugins/module_utils/hashes.py metaclass-boilerplate!skip
plugins/module_utils/helm.py compile-2.7!skip
plugins/module_utils/helm.py compile-3.5!skip
plugins/module_utils/helm.py future-import-boilerplate!skip
plugins/module_utils/helm.py import-2.7!skip
plugins/module_utils/helm.py import-3.5!skip
plugins/module_utils/helm.py metaclass-boilerplate!skip
plugins/module_utils/k8sdynamicclient.py compile-2.7!skip
plugins/module_utils/k8sdynamicclient.py compile-3.5!skip
plugins/module_utils/k8sdynamicclient.py future-import-boilerplate!skip
plugins/module_utils/k8sdynamicclient.py import-2.7!skip
plugins/module_utils/k8sdynamicclient.py import-3.5!skip
plugins/module_utils/k8sdynamicclient.py import-3.6!skip
plugins/module_utils/k8sdynamicclient.py import-3.7!skip
plugins/module_utils/k8sdynamicclient.py import-3.8!skip
plugins/module_utils/k8sdynamicclient.py import-3.9!skip
plugins/module_utils/k8sdynamicclient.py import-3.10!skip
plugins/module_utils/k8sdynamicclient.py metaclass-boilerplate!skip
plugins/modules/__init__.py compile-2.7!skip
plugins/modules/__init__.py compile-3.5!skip
plugins/modules/__init__.py future-import-boilerplate!skip
plugins/modules/__init__.py import-2.7!skip
plugins/modules/__init__.py import-3.5!skip
plugins/modules/__init__.py metaclass-boilerplate!skip
plugins/modules/helm.py compile-2.7!skip
plugins/modules/helm.py compile-3.5!skip
plugins/modules/helm.py future-import-boilerplate!skip
plugins/modules/helm.py import-2.7!skip
plugins/modules/helm.py import-3.5!skip
plugins/modules/helm.py metaclass-boilerplate!skip
plugins/modules/helm_info.py compile-2.7!skip
plugins/modules/helm_info.py compile-3.5!skip
plugins/modules/helm_info.py future-import-boilerplate!skip
plugins/modules/helm_info.py import-2.7!skip
plugins/modules/helm_info.py import-3.5!skip
plugins/modules/helm_info.py metaclass-boilerplate!skip
plugins/modules/helm_plugin.py compile-2.7!skip
plugins/modules/helm_plugin.py compile-3.5!skip
plugins/modules/helm_plugin.py future-import-boilerplate!skip
plugins/modules/helm_plugin.py import-2.7!skip
plugins/modules/helm_plugin.py import-3.5!skip
plugins/modules/helm_plugin.py metaclass-boilerplate!skip
plugins/modules/helm_plugin_info.py compile-2.7!skip
plugins/modules/helm_plugin_info.py compile-3.5!skip
plugins/modules/helm_plugin_info.py future-import-boilerplate!skip
plugins/modules/helm_plugin_info.py import-2.7!skip
plugins/modules/helm_plugin_info.py import-3.5!skip
plugins/modules/helm_plugin_info.py metaclass-boilerplate!skip
plugins/modules/helm_repository.py compile-2.7!skip
plugins/modules/helm_repository.py compile-3.5!skip
plugins/modules/helm_repository.py future-import-boilerplate!skip
plugins/modules/helm_repository.py import-2.7!skip
plugins/modules/helm_repository.py import-3.5!skip
plugins/modules/helm_repository.py metaclass-boilerplate!skip
plugins/modules/helm_template.py compile-2.7!skip
plugins/modules/helm_template.py compile-3.5!skip
plugins/modules/helm_template.py future-import-boilerplate!skip
plugins/modules/helm_template.py import-2.7!skip
plugins/modules/helm_template.py import-3.5!skip
plugins/modules/helm_template.py metaclass-boilerplate!skip
plugins/modules/k8s.py compile-2.7!skip
plugins/modules/k8s.py compile-3.5!skip
plugins/modules/k8s.py future-import-boilerplate!skip
plugins/modules/k8s.py import-2.7!skip
plugins/modules/k8s.py import-3.5!skip
plugins/modules/k8s.py metaclass-boilerplate!skip
plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s.py validate-modules:return-syntax-error
plugins/modules/k8s_cluster_info.py compile-2.7!skip
plugins/modules/k8s_cluster_info.py compile-3.5!skip
plugins/modules/k8s_cluster_info.py future-import-boilerplate!skip
plugins/modules/k8s_cluster_info.py import-2.7!skip
plugins/modules/k8s_cluster_info.py import-3.5!skip
plugins/modules/k8s_cluster_info.py metaclass-boilerplate!skip
plugins/modules/k8s_exec.py compile-2.7!skip
plugins/modules/k8s_exec.py compile-3.5!skip
plugins/modules/k8s_exec.py future-import-boilerplate!skip
plugins/modules/k8s_exec.py import-2.7!skip
plugins/modules/k8s_exec.py import-3.5!skip
plugins/modules/k8s_exec.py metaclass-boilerplate!skip
plugins/modules/k8s_info.py compile-2.7!skip
plugins/modules/k8s_info.py compile-3.5!skip
plugins/modules/k8s_info.py future-import-boilerplate!skip
plugins/modules/k8s_info.py import-2.7!skip
plugins/modules/k8s_info.py import-3.5!skip
plugins/modules/k8s_info.py metaclass-boilerplate!skip
plugins/modules/k8s_json_patch.py compile-2.7!skip
plugins/modules/k8s_json_patch.py compile-3.5!skip
plugins/modules/k8s_json_patch.py future-import-boilerplate!skip
plugins/modules/k8s_json_patch.py import-2.7!skip
plugins/modules/k8s_json_patch.py import-3.5!skip
plugins/modules/k8s_json_patch.py metaclass-boilerplate!skip
plugins/modules/k8s_log.py compile-2.7!skip
plugins/modules/k8s_log.py compile-3.5!skip
plugins/modules/k8s_log.py future-import-boilerplate!skip
plugins/modules/k8s_log.py import-2.7!skip
plugins/modules/k8s_log.py import-3.5!skip
plugins/modules/k8s_log.py metaclass-boilerplate!skip
plugins/modules/k8s_rollback.py compile-2.7!skip
plugins/modules/k8s_rollback.py compile-3.5!skip
plugins/modules/k8s_rollback.py future-import-boilerplate!skip
plugins/modules/k8s_rollback.py import-2.7!skip
plugins/modules/k8s_rollback.py import-3.5!skip
plugins/modules/k8s_rollback.py metaclass-boilerplate!skip
plugins/modules/k8s_scale.py compile-2.7!skip
plugins/modules/k8s_scale.py compile-3.5!skip
plugins/modules/k8s_scale.py future-import-boilerplate!skip
plugins/modules/k8s_scale.py import-2.7!skip
plugins/modules/k8s_scale.py import-3.5!skip
plugins/modules/k8s_scale.py metaclass-boilerplate!skip
plugins/modules/k8s_scale.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_scale.py validate-modules:return-syntax-error
plugins/modules/k8s_service.py compile-2.7!skip
plugins/modules/k8s_service.py compile-3.5!skip
plugins/modules/k8s_service.py future-import-boilerplate!skip
plugins/modules/k8s_service.py import-2.7!skip
plugins/modules/k8s_service.py import-3.5!skip
plugins/modules/k8s_service.py metaclass-boilerplate!skip
plugins/modules/k8s_service.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_service.py validate-modules:return-syntax-error
tests/unit/module_utils/fixtures/definitions.yml yamllint!skip
tests/unit/module_utils/fixtures/deployments.yml yamllint!skip
tests/unit/module_utils/fixtures/pods.yml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart/templates/configmap.yaml yamllint!skip
tests/integration/targets/k8s_scale/files/deployment.yaml yamllint!skip
tests/sanity/refresh_ignore_files shebang!skip
plugins/modules/k8s_cp.py compile-2.7!skip
plugins/modules/k8s_cp.py import-2.7!skip
plugins/module_utils/selector.py future-import-boilerplate!skip
plugins/module_utils/selector.py metaclass-boilerplate!skip
plugins/modules/k8s_taint.py compile-2.7!skip
plugins/modules/k8s_taint.py compile-3.5!skip
plugins/modules/k8s_taint.py future-import-boilerplate!skip
plugins/modules/k8s_taint.py import-2.7!skip
plugins/modules/k8s_taint.py import-3.5!skip
plugins/modules/k8s_taint.py metaclass-boilerplate!skip
plugins/modules/k8s.py validate-modules:return-syntax-error
plugins/modules/k8s_scale.py validate-modules:return-syntax-error
plugins/modules/k8s_service.py validate-modules:return-syntax-error
plugins/modules/k8s_taint.py validate-modules:return-syntax-error
plugins/module_utils/copy.py future-import-boilerplate!skip
plugins/module_utils/copy.py metaclass-boilerplate!skip

View File

@@ -1,196 +1,31 @@
tests/integration/targets/k8s_scale/files/deployment.yaml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart/templates/configmap.yaml yamllint!skip
plugins/module_utils/__init__.py compile-2.7!skip
plugins/module_utils/__init__.py compile-3.5!skip
plugins/module_utils/__init__.py future-import-boilerplate!skip
plugins/module_utils/__init__.py import-2.7!skip
plugins/module_utils/__init__.py import-3.5!skip
plugins/module_utils/__init__.py metaclass-boilerplate!skip
plugins/module_utils/ansiblemodule.py compile-2.7!skip
plugins/module_utils/ansiblemodule.py compile-3.5!skip
plugins/module_utils/ansiblemodule.py future-import-boilerplate!skip
plugins/module_utils/ansiblemodule.py import-2.7!skip
plugins/module_utils/ansiblemodule.py import-3.5!skip
plugins/module_utils/ansiblemodule.py metaclass-boilerplate!skip
plugins/module_utils/apply.py compile-2.7!skip
plugins/module_utils/apply.py compile-3.5!skip
plugins/module_utils/apply.py future-import-boilerplate!skip
plugins/module_utils/apply.py import-2.7!skip
plugins/module_utils/apply.py import-3.5!skip
plugins/module_utils/apply.py metaclass-boilerplate!skip
plugins/module_utils/args_common.py compile-2.7!skip
plugins/module_utils/args_common.py compile-3.5!skip
plugins/module_utils/args_common.py future-import-boilerplate!skip
plugins/module_utils/args_common.py import-2.7!skip
plugins/module_utils/args_common.py import-3.5!skip
plugins/module_utils/args_common.py metaclass-boilerplate!skip
plugins/module_utils/client/discovery.py future-import-boilerplate!skip
plugins/module_utils/client/discovery.py import-2.7!skip
plugins/module_utils/client/discovery.py import-3.5!skip
plugins/module_utils/client/discovery.py import-3.6!skip
plugins/module_utils/client/discovery.py import-3.7!skip
plugins/module_utils/client/discovery.py import-3.8!skip
plugins/module_utils/client/discovery.py import-3.9!skip
plugins/module_utils/client/discovery.py import-3.10!skip
plugins/module_utils/client/discovery.py metaclass-boilerplate!skip
plugins/module_utils/client/resource.py import-2.7!skip
plugins/module_utils/client/resource.py import-3.5!skip
plugins/module_utils/client/resource.py import-3.6!skip
plugins/module_utils/client/resource.py import-3.7!skip
plugins/module_utils/client/resource.py import-3.8!skip
plugins/module_utils/client/resource.py import-3.9!skip
plugins/module_utils/client/resource.py import-3.10!skip
plugins/module_utils/common.py compile-2.7!skip
plugins/module_utils/common.py compile-3.5!skip
plugins/module_utils/common.py future-import-boilerplate!skip
plugins/module_utils/common.py import-2.7!skip
plugins/module_utils/common.py import-3.5!skip
plugins/module_utils/common.py metaclass-boilerplate!skip
plugins/module_utils/exceptions.py compile-2.7!skip
plugins/module_utils/exceptions.py compile-3.5!skip
plugins/module_utils/exceptions.py future-import-boilerplate!skip
plugins/module_utils/exceptions.py import-2.7!skip
plugins/module_utils/exceptions.py import-3.5!skip
plugins/module_utils/exceptions.py metaclass-boilerplate!skip
plugins/module_utils/hashes.py compile-2.7!skip
plugins/module_utils/hashes.py compile-3.5!skip
plugins/module_utils/hashes.py future-import-boilerplate!skip
plugins/module_utils/hashes.py import-2.7!skip
plugins/module_utils/hashes.py import-3.5!skip
plugins/module_utils/hashes.py metaclass-boilerplate!skip
plugins/module_utils/helm.py compile-2.7!skip
plugins/module_utils/helm.py compile-3.5!skip
plugins/module_utils/helm.py future-import-boilerplate!skip
plugins/module_utils/helm.py import-2.7!skip
plugins/module_utils/helm.py import-3.5!skip
plugins/module_utils/helm.py metaclass-boilerplate!skip
plugins/module_utils/k8sdynamicclient.py compile-2.7!skip
plugins/module_utils/k8sdynamicclient.py compile-3.5!skip
plugins/module_utils/k8sdynamicclient.py future-import-boilerplate!skip
plugins/module_utils/k8sdynamicclient.py import-2.7!skip
plugins/module_utils/k8sdynamicclient.py import-3.5!skip
plugins/module_utils/k8sdynamicclient.py import-3.6!skip
plugins/module_utils/k8sdynamicclient.py import-3.7!skip
plugins/module_utils/k8sdynamicclient.py import-3.8!skip
plugins/module_utils/k8sdynamicclient.py import-3.9!skip
plugins/module_utils/k8sdynamicclient.py import-3.10!skip
plugins/module_utils/k8sdynamicclient.py metaclass-boilerplate!skip
plugins/modules/__init__.py compile-2.7!skip
plugins/modules/__init__.py compile-3.5!skip
plugins/modules/__init__.py future-import-boilerplate!skip
plugins/modules/__init__.py import-2.7!skip
plugins/modules/__init__.py import-3.5!skip
plugins/modules/__init__.py metaclass-boilerplate!skip
plugins/modules/helm.py compile-2.7!skip
plugins/modules/helm.py compile-3.5!skip
plugins/modules/helm.py future-import-boilerplate!skip
plugins/modules/helm.py import-2.7!skip
plugins/modules/helm.py import-3.5!skip
plugins/modules/helm.py metaclass-boilerplate!skip
plugins/modules/helm_info.py compile-2.7!skip
plugins/modules/helm_info.py compile-3.5!skip
plugins/modules/helm_info.py future-import-boilerplate!skip
plugins/modules/helm_info.py import-2.7!skip
plugins/modules/helm_info.py import-3.5!skip
plugins/modules/helm_info.py metaclass-boilerplate!skip
plugins/modules/helm_plugin.py compile-2.7!skip
plugins/modules/helm_plugin.py compile-3.5!skip
plugins/modules/helm_plugin.py future-import-boilerplate!skip
plugins/modules/helm_plugin.py import-2.7!skip
plugins/modules/helm_plugin.py import-3.5!skip
plugins/modules/helm_plugin.py metaclass-boilerplate!skip
plugins/modules/helm_plugin_info.py compile-2.7!skip
plugins/modules/helm_plugin_info.py compile-3.5!skip
plugins/modules/helm_plugin_info.py future-import-boilerplate!skip
plugins/modules/helm_plugin_info.py import-2.7!skip
plugins/modules/helm_plugin_info.py import-3.5!skip
plugins/modules/helm_plugin_info.py metaclass-boilerplate!skip
plugins/modules/helm_repository.py compile-2.7!skip
plugins/modules/helm_repository.py compile-3.5!skip
plugins/modules/helm_repository.py future-import-boilerplate!skip
plugins/modules/helm_repository.py import-2.7!skip
plugins/modules/helm_repository.py import-3.5!skip
plugins/modules/helm_repository.py metaclass-boilerplate!skip
plugins/modules/helm_template.py compile-2.7!skip
plugins/modules/helm_template.py compile-3.5!skip
plugins/modules/helm_template.py future-import-boilerplate!skip
plugins/modules/helm_template.py import-2.7!skip
plugins/modules/helm_template.py import-3.5!skip
plugins/modules/helm_template.py metaclass-boilerplate!skip
plugins/modules/k8s.py compile-2.7!skip
plugins/modules/k8s.py compile-3.5!skip
plugins/modules/k8s.py future-import-boilerplate!skip
plugins/modules/k8s.py import-2.7!skip
plugins/modules/k8s.py import-3.5!skip
plugins/modules/k8s.py metaclass-boilerplate!skip
plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s.py validate-modules:return-syntax-error
plugins/modules/k8s_cluster_info.py compile-2.7!skip
plugins/modules/k8s_cluster_info.py compile-3.5!skip
plugins/modules/k8s_cluster_info.py future-import-boilerplate!skip
plugins/modules/k8s_cluster_info.py import-2.7!skip
plugins/modules/k8s_cluster_info.py import-3.5!skip
plugins/modules/k8s_cluster_info.py metaclass-boilerplate!skip
plugins/modules/k8s_exec.py compile-2.7!skip
plugins/modules/k8s_exec.py compile-3.5!skip
plugins/modules/k8s_exec.py future-import-boilerplate!skip
plugins/modules/k8s_exec.py import-2.7!skip
plugins/modules/k8s_exec.py import-3.5!skip
plugins/modules/k8s_exec.py metaclass-boilerplate!skip
plugins/modules/k8s_info.py compile-2.7!skip
plugins/modules/k8s_info.py compile-3.5!skip
plugins/modules/k8s_info.py future-import-boilerplate!skip
plugins/modules/k8s_info.py import-2.7!skip
plugins/modules/k8s_info.py import-3.5!skip
plugins/modules/k8s_info.py metaclass-boilerplate!skip
plugins/modules/k8s_json_patch.py compile-2.7!skip
plugins/modules/k8s_json_patch.py compile-3.5!skip
plugins/modules/k8s_json_patch.py future-import-boilerplate!skip
plugins/modules/k8s_json_patch.py import-2.7!skip
plugins/modules/k8s_json_patch.py import-3.5!skip
plugins/modules/k8s_json_patch.py metaclass-boilerplate!skip
plugins/modules/k8s_log.py compile-2.7!skip
plugins/modules/k8s_log.py compile-3.5!skip
plugins/modules/k8s_log.py future-import-boilerplate!skip
plugins/modules/k8s_log.py import-2.7!skip
plugins/modules/k8s_log.py import-3.5!skip
plugins/modules/k8s_log.py metaclass-boilerplate!skip
plugins/modules/k8s_rollback.py compile-2.7!skip
plugins/modules/k8s_rollback.py compile-3.5!skip
plugins/modules/k8s_rollback.py future-import-boilerplate!skip
plugins/modules/k8s_rollback.py import-2.7!skip
plugins/modules/k8s_rollback.py import-3.5!skip
plugins/modules/k8s_rollback.py metaclass-boilerplate!skip
plugins/modules/k8s_scale.py compile-2.7!skip
plugins/modules/k8s_scale.py compile-3.5!skip
plugins/modules/k8s_scale.py future-import-boilerplate!skip
plugins/modules/k8s_scale.py import-2.7!skip
plugins/modules/k8s_scale.py import-3.5!skip
plugins/modules/k8s_scale.py metaclass-boilerplate!skip
plugins/modules/k8s_scale.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_scale.py validate-modules:return-syntax-error
plugins/modules/k8s_service.py compile-2.7!skip
plugins/modules/k8s_service.py compile-3.5!skip
plugins/modules/k8s_service.py future-import-boilerplate!skip
plugins/modules/k8s_service.py import-2.7!skip
plugins/modules/k8s_service.py import-3.5!skip
plugins/modules/k8s_service.py metaclass-boilerplate!skip
plugins/modules/k8s_service.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_service.py validate-modules:return-syntax-error
tests/unit/module_utils/fixtures/definitions.yml yamllint!skip
tests/unit/module_utils/fixtures/deployments.yml yamllint!skip
tests/unit/module_utils/fixtures/pods.yml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart/templates/configmap.yaml yamllint!skip
tests/integration/targets/k8s_scale/files/deployment.yaml yamllint!skip
tests/sanity/refresh_ignore_files shebang!skip
plugins/modules/k8s_cp.py compile-2.7!skip
plugins/modules/k8s_cp.py import-2.7!skip
plugins/module_utils/selector.py future-import-boilerplate!skip
plugins/module_utils/selector.py metaclass-boilerplate!skip
plugins/modules/k8s_taint.py compile-2.7!skip
plugins/modules/k8s_taint.py compile-3.5!skip
plugins/modules/k8s_taint.py future-import-boilerplate!skip
plugins/modules/k8s_taint.py import-2.7!skip
plugins/modules/k8s_taint.py import-3.5!skip
plugins/modules/k8s_taint.py metaclass-boilerplate!skip
plugins/modules/k8s.py validate-modules:return-syntax-error
plugins/modules/k8s_scale.py validate-modules:return-syntax-error
plugins/modules/k8s_service.py validate-modules:return-syntax-error
plugins/modules/k8s_taint.py validate-modules:return-syntax-error
plugins/module_utils/copy.py future-import-boilerplate!skip
plugins/module_utils/copy.py metaclass-boilerplate!skip

View File

@@ -1,255 +1,171 @@
tests/integration/targets/k8s_scale/files/deployment.yaml yamllint!skip
plugins/module_utils/client/discovery.py import-3.6!skip
plugins/module_utils/client/discovery.py import-3.7!skip
plugins/module_utils/client/discovery.py import-3.8!skip
plugins/module_utils/client/resource.py import-3.6!skip
plugins/module_utils/client/resource.py import-3.7!skip
plugins/module_utils/client/resource.py import-3.8!skip
plugins/module_utils/k8sdynamicclient.py import-3.6!skip
plugins/module_utils/k8sdynamicclient.py import-3.7!skip
plugins/module_utils/k8sdynamicclient.py import-3.8!skip
plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_scale.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_service.py validate-modules:parameter-type-not-in-doc
tests/unit/module_utils/fixtures/definitions.yml yamllint!skip
tests/unit/module_utils/fixtures/deployments.yml yamllint!skip
tests/unit/module_utils/fixtures/pods.yml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/appversionless-chart/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart-v2/templates/configmap.yaml yamllint!skip
tests/integration/targets/helm/files/test-chart/templates/configmap.yaml yamllint!skip
plugins/module_utils/__init__.py compile-2.6!skip
plugins/module_utils/__init__.py compile-2.7!skip
plugins/module_utils/__init__.py compile-3.5!skip
plugins/module_utils/__init__.py future-import-boilerplate!skip
plugins/module_utils/__init__.py import-2.6!skip
plugins/module_utils/__init__.py import-2.7!skip
plugins/module_utils/__init__.py import-3.5!skip
plugins/module_utils/__init__.py metaclass-boilerplate!skip
plugins/module_utils/ansiblemodule.py compile-2.6!skip
plugins/module_utils/ansiblemodule.py compile-2.7!skip
plugins/module_utils/ansiblemodule.py compile-3.5!skip
plugins/module_utils/ansiblemodule.py future-import-boilerplate!skip
plugins/module_utils/ansiblemodule.py import-2.6!skip
plugins/module_utils/ansiblemodule.py import-2.7!skip
plugins/module_utils/ansiblemodule.py import-3.5!skip
plugins/module_utils/ansiblemodule.py metaclass-boilerplate!skip
plugins/module_utils/apply.py compile-2.6!skip
plugins/module_utils/apply.py compile-2.7!skip
plugins/module_utils/apply.py compile-3.5!skip
plugins/module_utils/apply.py future-import-boilerplate!skip
plugins/module_utils/apply.py import-2.6!skip
plugins/module_utils/apply.py import-2.7!skip
plugins/module_utils/apply.py import-3.5!skip
plugins/module_utils/apply.py metaclass-boilerplate!skip
plugins/module_utils/args_common.py compile-2.6!skip
plugins/module_utils/args_common.py compile-2.7!skip
plugins/module_utils/args_common.py compile-3.5!skip
plugins/module_utils/args_common.py future-import-boilerplate!skip
plugins/module_utils/args_common.py import-2.6!skip
plugins/module_utils/args_common.py import-2.7!skip
plugins/module_utils/args_common.py import-3.5!skip
plugins/module_utils/args_common.py metaclass-boilerplate!skip
plugins/module_utils/client/discovery.py future-import-boilerplate!skip
plugins/module_utils/client/discovery.py import-2.6!skip
plugins/module_utils/client/discovery.py import-2.7!skip
plugins/module_utils/client/discovery.py import-3.5!skip
plugins/module_utils/client/discovery.py import-3.6!skip
plugins/module_utils/client/discovery.py import-3.7!skip
plugins/module_utils/client/discovery.py import-3.8!skip
plugins/module_utils/client/discovery.py metaclass-boilerplate!skip
plugins/module_utils/client/resource.py import-2.6!skip
plugins/module_utils/client/resource.py import-2.7!skip
plugins/module_utils/client/resource.py import-3.5!skip
plugins/module_utils/client/resource.py import-3.6!skip
plugins/module_utils/client/resource.py import-3.7!skip
plugins/module_utils/client/resource.py import-3.8!skip
plugins/module_utils/common.py compile-2.6!skip
plugins/module_utils/common.py compile-2.7!skip
plugins/module_utils/common.py compile-3.5!skip
plugins/module_utils/common.py future-import-boilerplate!skip
plugins/module_utils/common.py import-2.6!skip
plugins/module_utils/common.py import-2.7!skip
plugins/module_utils/common.py import-3.5!skip
plugins/module_utils/common.py metaclass-boilerplate!skip
plugins/module_utils/exceptions.py compile-2.6!skip
plugins/module_utils/exceptions.py compile-2.7!skip
plugins/module_utils/exceptions.py compile-3.5!skip
plugins/module_utils/exceptions.py future-import-boilerplate!skip
plugins/module_utils/exceptions.py import-2.6!skip
plugins/module_utils/exceptions.py import-2.7!skip
plugins/module_utils/exceptions.py import-3.5!skip
plugins/module_utils/exceptions.py metaclass-boilerplate!skip
plugins/module_utils/hashes.py compile-2.6!skip
plugins/module_utils/hashes.py compile-2.7!skip
plugins/module_utils/hashes.py compile-3.5!skip
plugins/module_utils/hashes.py future-import-boilerplate!skip
plugins/module_utils/hashes.py import-2.6!skip
plugins/module_utils/hashes.py import-2.7!skip
plugins/module_utils/hashes.py import-3.5!skip
plugins/module_utils/hashes.py metaclass-boilerplate!skip
plugins/module_utils/helm.py compile-2.6!skip
plugins/module_utils/helm.py compile-2.7!skip
plugins/module_utils/helm.py compile-3.5!skip
plugins/module_utils/helm.py future-import-boilerplate!skip
plugins/module_utils/helm.py import-2.6!skip
plugins/module_utils/helm.py import-2.7!skip
plugins/module_utils/helm.py import-3.5!skip
plugins/module_utils/helm.py metaclass-boilerplate!skip
plugins/module_utils/k8sdynamicclient.py compile-2.6!skip
plugins/module_utils/k8sdynamicclient.py compile-2.7!skip
plugins/module_utils/k8sdynamicclient.py compile-3.5!skip
plugins/module_utils/k8sdynamicclient.py future-import-boilerplate!skip
plugins/module_utils/k8sdynamicclient.py import-2.6!skip
plugins/module_utils/k8sdynamicclient.py import-2.7!skip
plugins/module_utils/k8sdynamicclient.py import-3.5!skip
plugins/module_utils/k8sdynamicclient.py import-3.6!skip
plugins/module_utils/k8sdynamicclient.py import-3.7!skip
plugins/module_utils/k8sdynamicclient.py import-3.8!skip
plugins/module_utils/k8sdynamicclient.py metaclass-boilerplate!skip
plugins/modules/__init__.py compile-2.6!skip
plugins/modules/__init__.py compile-2.7!skip
plugins/modules/__init__.py compile-3.5!skip
plugins/modules/__init__.py future-import-boilerplate!skip
plugins/modules/__init__.py import-2.6!skip
plugins/modules/__init__.py import-2.7!skip
plugins/modules/__init__.py import-3.5!skip
plugins/modules/__init__.py metaclass-boilerplate!skip
plugins/modules/helm.py compile-2.6!skip
plugins/modules/helm.py compile-2.7!skip
plugins/modules/helm.py compile-3.5!skip
plugins/modules/helm.py future-import-boilerplate!skip
plugins/modules/helm.py import-2.6!skip
plugins/modules/helm.py import-2.7!skip
plugins/modules/helm.py import-3.5!skip
plugins/modules/helm.py metaclass-boilerplate!skip
plugins/modules/helm_info.py compile-2.6!skip
plugins/modules/helm_info.py compile-2.7!skip
plugins/modules/helm_info.py compile-3.5!skip
plugins/modules/helm_info.py future-import-boilerplate!skip
plugins/modules/helm_info.py import-2.6!skip
plugins/modules/helm_info.py import-2.7!skip
plugins/modules/helm_info.py import-3.5!skip
plugins/modules/helm_info.py metaclass-boilerplate!skip
plugins/modules/helm_plugin.py compile-2.6!skip
plugins/modules/helm_plugin.py compile-2.7!skip
plugins/modules/helm_plugin.py compile-3.5!skip
plugins/modules/helm_plugin.py future-import-boilerplate!skip
plugins/modules/helm_plugin.py import-2.6!skip
plugins/modules/helm_plugin.py import-2.7!skip
plugins/modules/helm_plugin.py import-3.5!skip
plugins/modules/helm_plugin.py metaclass-boilerplate!skip
plugins/modules/helm_plugin_info.py compile-2.6!skip
plugins/modules/helm_plugin_info.py compile-2.7!skip
plugins/modules/helm_plugin_info.py compile-3.5!skip
plugins/modules/helm_plugin_info.py future-import-boilerplate!skip
plugins/modules/helm_plugin_info.py import-2.6!skip
plugins/modules/helm_plugin_info.py import-2.7!skip
plugins/modules/helm_plugin_info.py import-3.5!skip
plugins/modules/helm_plugin_info.py metaclass-boilerplate!skip
plugins/modules/helm_repository.py compile-2.6!skip
plugins/modules/helm_repository.py compile-2.7!skip
plugins/modules/helm_repository.py compile-3.5!skip
plugins/modules/helm_repository.py future-import-boilerplate!skip
plugins/modules/helm_repository.py import-2.6!skip
plugins/modules/helm_repository.py import-2.7!skip
plugins/modules/helm_repository.py import-3.5!skip
plugins/modules/helm_repository.py metaclass-boilerplate!skip
plugins/modules/helm_template.py compile-2.6!skip
plugins/modules/helm_template.py compile-2.7!skip
plugins/modules/helm_template.py compile-3.5!skip
plugins/modules/helm_template.py future-import-boilerplate!skip
plugins/modules/helm_template.py import-2.6!skip
plugins/modules/helm_template.py import-2.7!skip
plugins/modules/helm_template.py import-3.5!skip
plugins/modules/helm_template.py metaclass-boilerplate!skip
plugins/modules/k8s.py compile-2.6!skip
plugins/modules/k8s.py compile-2.7!skip
plugins/modules/k8s.py compile-3.5!skip
plugins/modules/k8s.py future-import-boilerplate!skip
plugins/modules/k8s.py import-2.6!skip
plugins/modules/k8s.py import-2.7!skip
plugins/modules/k8s.py import-3.5!skip
plugins/modules/k8s.py metaclass-boilerplate!skip
plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_cluster_info.py compile-2.6!skip
plugins/modules/k8s_cluster_info.py compile-2.7!skip
plugins/modules/k8s_cluster_info.py compile-3.5!skip
plugins/modules/k8s_cluster_info.py future-import-boilerplate!skip
plugins/modules/k8s_cluster_info.py import-2.6!skip
plugins/modules/k8s_cluster_info.py import-2.7!skip
plugins/modules/k8s_cluster_info.py import-3.5!skip
plugins/modules/k8s_cluster_info.py metaclass-boilerplate!skip
plugins/modules/k8s_exec.py compile-2.6!skip
plugins/modules/k8s_exec.py compile-2.7!skip
plugins/modules/k8s_exec.py compile-3.5!skip
plugins/modules/k8s_exec.py future-import-boilerplate!skip
plugins/modules/k8s_exec.py import-2.6!skip
plugins/modules/k8s_exec.py import-2.7!skip
plugins/modules/k8s_exec.py import-3.5!skip
plugins/modules/k8s_exec.py metaclass-boilerplate!skip
plugins/modules/k8s_info.py compile-2.6!skip
plugins/modules/k8s_info.py compile-2.7!skip
plugins/modules/k8s_info.py compile-3.5!skip
plugins/modules/k8s_info.py future-import-boilerplate!skip
plugins/modules/k8s_info.py import-2.6!skip
plugins/modules/k8s_info.py import-2.7!skip
plugins/modules/k8s_info.py import-3.5!skip
plugins/modules/k8s_info.py metaclass-boilerplate!skip
plugins/modules/k8s_json_patch.py compile-2.6!skip
plugins/modules/k8s_json_patch.py compile-2.7!skip
plugins/modules/k8s_json_patch.py compile-3.5!skip
plugins/modules/k8s_json_patch.py future-import-boilerplate!skip
plugins/modules/k8s_json_patch.py import-2.6!skip
plugins/modules/k8s_json_patch.py import-2.7!skip
plugins/modules/k8s_json_patch.py import-3.5!skip
plugins/modules/k8s_json_patch.py metaclass-boilerplate!skip
plugins/modules/k8s_log.py compile-2.6!skip
plugins/modules/k8s_log.py compile-2.7!skip
plugins/modules/k8s_log.py compile-3.5!skip
plugins/modules/k8s_log.py future-import-boilerplate!skip
plugins/modules/k8s_log.py import-2.6!skip
plugins/modules/k8s_log.py import-2.7!skip
plugins/modules/k8s_log.py import-3.5!skip
plugins/modules/k8s_log.py metaclass-boilerplate!skip
plugins/modules/k8s_rollback.py compile-2.6!skip
plugins/modules/k8s_rollback.py compile-2.7!skip
plugins/modules/k8s_rollback.py compile-3.5!skip
plugins/modules/k8s_rollback.py future-import-boilerplate!skip
plugins/modules/k8s_rollback.py import-2.6!skip
plugins/modules/k8s_rollback.py import-2.7!skip
plugins/modules/k8s_rollback.py import-3.5!skip
plugins/modules/k8s_rollback.py metaclass-boilerplate!skip
plugins/modules/k8s_scale.py compile-2.6!skip
plugins/modules/k8s_scale.py compile-2.7!skip
plugins/modules/k8s_scale.py compile-3.5!skip
plugins/modules/k8s_scale.py future-import-boilerplate!skip
plugins/modules/k8s_scale.py import-2.6!skip
plugins/modules/k8s_scale.py import-2.7!skip
plugins/modules/k8s_scale.py import-3.5!skip
plugins/modules/k8s_scale.py metaclass-boilerplate!skip
plugins/modules/k8s_scale.py validate-modules:parameter-type-not-in-doc
plugins/modules/k8s_service.py compile-2.6!skip
plugins/modules/k8s_service.py compile-2.7!skip
plugins/modules/k8s_service.py compile-3.5!skip
plugins/modules/k8s_service.py future-import-boilerplate!skip
plugins/modules/k8s_service.py import-2.6!skip
plugins/modules/k8s_service.py import-2.7!skip
plugins/modules/k8s_service.py import-3.5!skip
plugins/modules/k8s_service.py metaclass-boilerplate!skip
plugins/modules/k8s_service.py validate-modules:parameter-type-not-in-doc
tests/integration/targets/k8s_scale/files/deployment.yaml yamllint!skip
tests/sanity/refresh_ignore_files shebang!skip
tests/unit/module_utils/test_discoverer.py future-import-boilerplate!skip
tests/unit/module_utils/test_discoverer.py metaclass-boilerplate!skip
plugins/modules/k8s_cp.py compile-2.6!skip
plugins/modules/k8s_cp.py compile-2.7!skip
plugins/modules/k8s_cp.py import-2.6!skip
plugins/modules/k8s_cp.py import-2.7!skip
tests/integration/targets/k8s_copy/library/kubectl_file_compare.py compile-2.6!skip
tests/integration/targets/k8s_copy/library/kubectl_file_compare.py compile-2.7!skip
tests/integration/targets/k8s_copy/library/kubectl_file_compare.py compile-3.5!skip
tests/unit/module_utils/test_selector.py future-import-boilerplate!skip
tests/unit/module_utils/test_selector.py metaclass-boilerplate!skip
plugins/module_utils/selector.py future-import-boilerplate!skip
plugins/module_utils/selector.py metaclass-boilerplate!skip
plugins/lookup/kustomize.py future-import-boilerplate!skip
plugins/lookup/kustomize.py metaclass-boilerplate!skip
tests/integration/targets/helm/library/helm_test_version.py metaclass-boilerplate!skip
tests/integration/targets/helm/library/helm_test_version.py future-import-boilerplate!skip
plugins/modules/k8s_taint.py compile-2.7!skip
plugins/modules/k8s_taint.py compile-3.5!skip
plugins/modules/k8s_taint.py future-import-boilerplate!skip
plugins/modules/k8s_taint.py import-2.7!skip
plugins/modules/k8s_taint.py import-3.5!skip
plugins/modules/k8s_taint.py metaclass-boilerplate!skip
plugins/doc_fragments/k8s_name_options.py future-import-boilerplate!skip
plugins/doc_fragments/k8s_auth_options.py future-import-boilerplate!skip
plugins/doc_fragments/helm_common_options.py future-import-boilerplate!skip
plugins/doc_fragments/k8s_state_options.py future-import-boilerplate!skip
plugins/doc_fragments/k8s_wait_options.py future-import-boilerplate!skip
plugins/doc_fragments/k8s_scale_options.py future-import-boilerplate!skip
plugins/doc_fragments/k8s_delete_options.py future-import-boilerplate!skip
plugins/doc_fragments/__init__.py future-import-boilerplate!skip
plugins/doc_fragments/k8s_resource_options.py future-import-boilerplate!skip
plugins/module_utils/helm.py future-import-boilerplate!skip
plugins/module_utils/apply.py future-import-boilerplate!skip
plugins/module_utils/hashes.py future-import-boilerplate!skip
plugins/module_utils/version.py future-import-boilerplate!skip
plugins/module_utils/_version.py future-import-boilerplate!skip
plugins/module_utils/copy.py future-import-boilerplate!skip
plugins/module_utils/args_common.py future-import-boilerplate!skip
plugins/module_utils/__init__.py future-import-boilerplate!skip
plugins/module_utils/selector.py future-import-boilerplate!skip
plugins/module_utils/k8sdynamicclient.py future-import-boilerplate!skip
plugins/module_utils/common.py future-import-boilerplate!skip
plugins/module_utils/ansiblemodule.py future-import-boilerplate!skip
plugins/module_utils/exceptions.py future-import-boilerplate!skip
plugins/module_utils/client/resource.py future-import-boilerplate!skip
plugins/module_utils/client/discovery.py future-import-boilerplate!skip
plugins/module_utils/k8s/resource.py future-import-boilerplate!skip
plugins/module_utils/k8s/core.py future-import-boilerplate!skip
plugins/module_utils/k8s/waiter.py future-import-boilerplate!skip
plugins/module_utils/k8s/client.py future-import-boilerplate!skip
plugins/module_utils/k8s/runner.py future-import-boilerplate!skip
plugins/module_utils/k8s/service.py future-import-boilerplate!skip
plugins/module_utils/k8s/exceptions.py future-import-boilerplate!skip
plugins/connection/kubectl.py future-import-boilerplate!skip
plugins/inventory/k8s.py future-import-boilerplate!skip
plugins/lookup/k8s.py future-import-boilerplate!skip
plugins/lookup/kustomize.py future-import-boilerplate!skip
plugins/modules/k8s_scale.py future-import-boilerplate!skip
plugins/modules/helm_template.py future-import-boilerplate!skip
plugins/modules/k8s_exec.py future-import-boilerplate!skip
plugins/modules/helm.py future-import-boilerplate!skip
plugins/modules/helm_plugin_info.py future-import-boilerplate!skip
plugins/modules/helm_info.py future-import-boilerplate!skip
plugins/modules/helm_repository.py future-import-boilerplate!skip
plugins/modules/k8s_rollback.py future-import-boilerplate!skip
plugins/modules/k8s_log.py future-import-boilerplate!skip
plugins/modules/k8s_drain.py future-import-boilerplate!skip
plugins/modules/helm_plugin.py future-import-boilerplate!skip
plugins/modules/k8s_taint.py future-import-boilerplate!skip
plugins/modules/k8s.py future-import-boilerplate!skip
plugins/modules/k8s_service.py future-import-boilerplate!skip
plugins/modules/k8s_cluster_info.py future-import-boilerplate!skip
plugins/modules/k8s_info.py future-import-boilerplate!skip
plugins/modules/k8s_cp.py future-import-boilerplate!skip
plugins/modules/__init__.py future-import-boilerplate!skip
plugins/modules/k8s_json_patch.py future-import-boilerplate!skip
plugins/action/k8s_info.py future-import-boilerplate!skip
plugins/filter/k8s.py future-import-boilerplate!skip
tests/unit/conftest.py future-import-boilerplate!skip
tests/unit/utils/ansible_module_mock.py future-import-boilerplate!skip
tests/unit/module_utils/test_helm.py future-import-boilerplate!skip
tests/unit/module_utils/test_marshal.py future-import-boilerplate!skip
tests/unit/module_utils/test_discoverer.py future-import-boilerplate!skip
tests/unit/module_utils/test_hashes.py future-import-boilerplate!skip
tests/unit/module_utils/test_resource.py future-import-boilerplate!skip
tests/unit/module_utils/test_service.py future-import-boilerplate!skip
tests/unit/module_utils/test_waiter.py future-import-boilerplate!skip
tests/unit/module_utils/test_common.py future-import-boilerplate!skip
tests/unit/module_utils/test_selector.py future-import-boilerplate!skip
tests/unit/module_utils/test_apply.py future-import-boilerplate!skip
tests/unit/module_utils/test_runner.py future-import-boilerplate!skip
tests/unit/module_utils/test_client.py future-import-boilerplate!skip
tests/unit/module_utils/test_core.py future-import-boilerplate!skip
tests/unit/modules/test_helm_template_module.py future-import-boilerplate!skip
tests/unit/modules/test_helm_template.py future-import-boilerplate!skip
tests/unit/modules/test_module_helm.py future-import-boilerplate!skip
tests/unit/action/test_remove_omit.py future-import-boilerplate!skip
plugins/doc_fragments/k8s_name_options.py metaclass-boilerplate!skip
plugins/doc_fragments/k8s_auth_options.py metaclass-boilerplate!skip
plugins/doc_fragments/helm_common_options.py metaclass-boilerplate!skip
plugins/doc_fragments/k8s_state_options.py metaclass-boilerplate!skip
plugins/doc_fragments/k8s_wait_options.py metaclass-boilerplate!skip
plugins/doc_fragments/k8s_scale_options.py metaclass-boilerplate!skip
plugins/doc_fragments/k8s_delete_options.py metaclass-boilerplate!skip
plugins/doc_fragments/__init__.py metaclass-boilerplate!skip
plugins/doc_fragments/k8s_resource_options.py metaclass-boilerplate!skip
plugins/module_utils/helm.py metaclass-boilerplate!skip
plugins/module_utils/apply.py metaclass-boilerplate!skip
plugins/module_utils/hashes.py metaclass-boilerplate!skip
plugins/module_utils/version.py metaclass-boilerplate!skip
plugins/module_utils/_version.py metaclass-boilerplate!skip
plugins/module_utils/copy.py metaclass-boilerplate!skip
plugins/module_utils/copy.py compile-2.6!skip
plugins/module_utils/copy.py compile-2.7!skip
plugins/module_utils/copy.py import-2.6!skip
plugins/module_utils/copy.py import-2.7!skip
plugins/module_utils/args_common.py metaclass-boilerplate!skip
plugins/module_utils/__init__.py metaclass-boilerplate!skip
plugins/module_utils/selector.py metaclass-boilerplate!skip
plugins/module_utils/k8sdynamicclient.py metaclass-boilerplate!skip
plugins/module_utils/common.py metaclass-boilerplate!skip
plugins/module_utils/ansiblemodule.py metaclass-boilerplate!skip
plugins/module_utils/exceptions.py metaclass-boilerplate!skip
plugins/module_utils/client/resource.py metaclass-boilerplate!skip
plugins/module_utils/client/discovery.py metaclass-boilerplate!skip
plugins/module_utils/k8s/resource.py metaclass-boilerplate!skip
plugins/module_utils/k8s/core.py metaclass-boilerplate!skip
plugins/module_utils/k8s/waiter.py metaclass-boilerplate!skip
plugins/module_utils/k8s/client.py metaclass-boilerplate!skip
plugins/module_utils/k8s/runner.py metaclass-boilerplate!skip
plugins/module_utils/k8s/service.py metaclass-boilerplate!skip
plugins/module_utils/k8s/exceptions.py metaclass-boilerplate!skip
plugins/connection/kubectl.py metaclass-boilerplate!skip
plugins/inventory/k8s.py metaclass-boilerplate!skip
plugins/lookup/k8s.py metaclass-boilerplate!skip
plugins/lookup/kustomize.py metaclass-boilerplate!skip
plugins/modules/k8s_scale.py metaclass-boilerplate!skip
plugins/modules/helm_template.py metaclass-boilerplate!skip
plugins/modules/k8s_exec.py metaclass-boilerplate!skip
plugins/modules/helm.py metaclass-boilerplate!skip
plugins/modules/helm_plugin_info.py metaclass-boilerplate!skip
plugins/modules/helm_info.py metaclass-boilerplate!skip
plugins/modules/helm_repository.py metaclass-boilerplate!skip
plugins/modules/k8s_rollback.py metaclass-boilerplate!skip
plugins/modules/k8s_log.py metaclass-boilerplate!skip
plugins/modules/k8s_drain.py metaclass-boilerplate!skip
plugins/modules/helm_plugin.py metaclass-boilerplate!skip
plugins/modules/k8s_taint.py metaclass-boilerplate!skip
plugins/modules/k8s.py metaclass-boilerplate!skip
plugins/modules/k8s_service.py metaclass-boilerplate!skip
plugins/modules/k8s_cluster_info.py metaclass-boilerplate!skip
plugins/modules/k8s_info.py metaclass-boilerplate!skip
plugins/modules/k8s_cp.py metaclass-boilerplate!skip
plugins/modules/__init__.py metaclass-boilerplate!skip
plugins/modules/k8s_json_patch.py metaclass-boilerplate!skip
plugins/action/k8s_info.py metaclass-boilerplate!skip
plugins/filter/k8s.py metaclass-boilerplate!skip
tests/unit/conftest.py metaclass-boilerplate!skip
tests/unit/utils/ansible_module_mock.py metaclass-boilerplate!skip
tests/unit/module_utils/test_helm.py metaclass-boilerplate!skip
tests/unit/module_utils/test_marshal.py metaclass-boilerplate!skip
tests/unit/module_utils/test_discoverer.py metaclass-boilerplate!skip
tests/unit/module_utils/test_hashes.py metaclass-boilerplate!skip
tests/unit/module_utils/test_resource.py metaclass-boilerplate!skip
tests/unit/module_utils/test_service.py metaclass-boilerplate!skip
tests/unit/module_utils/test_waiter.py metaclass-boilerplate!skip
tests/unit/module_utils/test_common.py metaclass-boilerplate!skip
tests/unit/module_utils/test_selector.py metaclass-boilerplate!skip
tests/unit/module_utils/test_apply.py metaclass-boilerplate!skip
tests/unit/module_utils/test_runner.py metaclass-boilerplate!skip
tests/unit/module_utils/test_client.py metaclass-boilerplate!skip
tests/unit/module_utils/test_core.py metaclass-boilerplate!skip
tests/unit/modules/test_helm_template_module.py metaclass-boilerplate!skip
tests/unit/modules/test_helm_template.py metaclass-boilerplate!skip
tests/unit/modules/test_module_helm.py metaclass-boilerplate!skip
tests/unit/action/test_remove_omit.py metaclass-boilerplate!skip

View File

@@ -1,39 +1,124 @@
#!/usr/bin/env python3
import itertools
from pathlib import Path
target_dir = Path('.')
ignore_dir = target_dir / "tests" / "sanity"
module_dir = target_dir / "plugins" / "modules"
module_utils_dir = target_dir / "plugins" / "module_utils"
ignore_dir.mkdir(parents=True, exist_ok=True)
# Mapping of Ansible versions to supported Python versions
ANSIBLE_VERSIONS = {
"2.9": ["3.6", "3.7", "3.8"],
"2.10": ["3.6", "3.7", "3.8", "3.9"],
"2.11": ["3.6", "3.7", "3.8", "3.9"],
"2.12": ["3.6", "3.7", "3.8", "3.9", "3.10"],
"2.13": ["3.6", "3.7", "3.8", "3.9", "3.10"],
"2.14": ["3.6", "3.7", "3.8", "3.9", "3.10"],
}
skip_list = [
"compile-2.6!skip", # Py3.8+
"compile-2.7!skip", # Py3.8+
"compile-3.5!skip", # Py3.8+
"import-2.6!skip", # Py3.8+
"import-2.7!skip", # Py3.8+
"import-3.5!skip", # Py3.8+
"future-import-boilerplate!skip", # Py2 only
"metaclass-boilerplate!skip", # Py2 only
IMPORT_SKIPS = [
"plugins/module_utils/client/discovery.py",
"plugins/module_utils/client/resource.py",
"plugins/module_utils/k8sdynamicclient.py",
]
for version in ["2.9", "2.10", "2.11", "2.12"]:
ignore_file = ignore_dir / f"ignore-{version}.txt"
ignore_content = ignore_file.read_text().split("\n")
ignore_content.append(f"tests/sanity/refresh_ignore_files shebang!skip")
# Adds validate-modules:parameter-type-not-in-doc
PARAM_TYPE_SKIPS = [
"plugins/modules/k8s.py",
"plugins/modules/k8s_scale.py",
"plugins/modules/k8s_service.py",
]
for f in module_dir.glob("*.py"):
if f.is_symlink():
continue
for test in skip_list:
ignore_content.append(f"{f} {test}")
for f in module_utils_dir.glob("*.py"):
if f.is_symlink():
continue
for test in skip_list:
ignore_content.append(f"{f} {test}")
ignore_file = ignore_dir / f"ignore-{version}.txt"
ignore_file.write_text("\n".join(sorted(set(ignore_content))).lstrip("\n"))
# Adds validate-modules:return-syntax-error
RETURN_SYNTAX_SKIPS = [
"plugins/modules/k8s.py",
"plugins/modules/k8s_scale.py",
"plugins/modules/k8s_service.py",
"plugins/modules/k8s_taint.py",
]
YAML_LINT_SKIPS = [
"tests/unit/module_utils/fixtures/definitions.yml",
"tests/unit/module_utils/fixtures/deployments.yml",
"tests/unit/module_utils/fixtures/pods.yml",
"tests/integration/targets/helm/files/appversionless-chart-v2/templates/configmap.yaml",
"tests/integration/targets/helm/files/appversionless-chart/templates/configmap.yaml",
"tests/integration/targets/helm/files/test-chart-v2/templates/configmap.yaml",
"tests/integration/targets/helm/files/test-chart/templates/configmap.yaml",
"tests/integration/targets/k8s_scale/files/deployment.yaml",
]
# Add shebang!skip
SHEBANG_SKIPS = [
"tests/sanity/refresh_ignore_files",
]
def import_skips(*versions):
for f in IMPORT_SKIPS:
for v in versions:
yield f"{f} import-{v}!skip"
def param_type_skips():
for f in PARAM_TYPE_SKIPS:
yield f"{f} validate-modules:parameter-type-not-in-doc"
def return_syntax_skips(ansible_version):
if ansible_version != "2.9":
for f in RETURN_SYNTAX_SKIPS:
yield f"{f} validate-modules:return-syntax-error"
else:
yield
def yaml_lint_skips():
for f in YAML_LINT_SKIPS:
yield f"{f} yamllint!skip"
def shebang_skips():
for f in SHEBANG_SKIPS:
yield f"{f} shebang!skip"
def import_boilerplate(path, ansible_version):
if ansible_version in ("2.9", "2.10", "2.11"):
for f in (p for p in path.glob("**/*.py") if not p.is_symlink()):
yield f"{f} future-import-boilerplate!skip"
else:
yield
def metaclass_boilerplate(path, ansible_version):
if ansible_version in ("2.9", "2.10", "2.11"):
for f in (p for p in path.glob("**/*.py") if not p.is_symlink()):
yield f"{f} metaclass-boilerplate!skip"
else:
yield
def main():
target_dir = Path('.')
sanity_dir = target_dir / "tests" / "sanity"
plugins = target_dir / "plugins"
units = target_dir / "tests" / "unit"
for ansible, python in ANSIBLE_VERSIONS.items():
with open(sanity_dir / f"ignore-{ansible}.txt", "w") as fp:
ignores = itertools.chain(
import_skips(*python),
param_type_skips(),
yaml_lint_skips(),
shebang_skips(),
return_syntax_skips(ansible),
import_boilerplate(plugins, ansible),
import_boilerplate(units, ansible),
metaclass_boilerplate(plugins, ansible),
metaclass_boilerplate(units, ansible))
for f in filter(None, ignores):
fp.write(f + "\n")
if __name__ == "__main__":
main()

44
tests/unit/conftest.py Normal file
View File

@@ -0,0 +1,44 @@
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import json
import sys
from io import BytesIO
import pytest
import ansible.module_utils.basic
from ansible.module_utils.six import string_types
from ansible.module_utils._text import to_bytes
from ansible.module_utils.common._collections_compat import MutableMapping
@pytest.fixture
def stdin(mocker, request):
old_args = ansible.module_utils.basic._ANSIBLE_ARGS
ansible.module_utils.basic._ANSIBLE_ARGS = None
old_argv = sys.argv
sys.argv = ["ansible_unittest"]
if isinstance(request.param, string_types):
args = request.param
elif isinstance(request.param, MutableMapping):
if "ANSIBLE_MODULE_ARGS" not in request.param:
request.param = {"ANSIBLE_MODULE_ARGS": request.param}
if "_ansible_remote_tmp" not in request.param["ANSIBLE_MODULE_ARGS"]:
request.param["ANSIBLE_MODULE_ARGS"]["_ansible_remote_tmp"] = "/tmp"
if "_ansible_keep_remote_files" not in request.param["ANSIBLE_MODULE_ARGS"]:
request.param["ANSIBLE_MODULE_ARGS"]["_ansible_keep_remote_files"] = False
args = json.dumps(request.param)
else:
raise Exception("Malformed data to the stdin pytest fixture")
fake_stdin = BytesIO(to_bytes(args, errors="surrogate_or_strict"))
mocker.patch("ansible.module_utils.basic.sys.stdin", mocker.MagicMock())
mocker.patch("ansible.module_utils.basic.sys.stdin.buffer", fake_stdin)
yield fake_stdin
ansible.module_utils.basic._ANSIBLE_ARGS = old_args
sys.argv = old_argv

View File

@@ -0,0 +1,34 @@
---
kind: Namespace
apiVersion: v1
metadata:
name: test-1
---
kind: Pod
apiVersion: v1
metadata:
name: pod-1
namespace: test-1
spec:
containers:
- image: busybox
name: busybox
---
kind: PodList
apiVersion: v1
metadata: {}
items:
- kind: Pod
apiVersion: v1
metadata:
name: pod-1
namespace: test-1
spec:
containers:
- image: busybox
name: busybox
---
kind: ConfigMapList
apiVersion: v1
metadata: {}
items: []

View File

@@ -0,0 +1,48 @@
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: deploy-1
namespace: test-1
generation: 1
spec:
replicas: 2
selector:
matchLabels:
app: foo
template:
metadata:
labels:
app: foo
spec:
containers:
- image: busybox
name: busybox
status:
availableReplicas: 2
replicas: 2
observedGeneration: 1
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: deploy-2
namespace: test-1
generation: 1
spec:
replicas: 2
selector:
matchLabels:
app: foo
template:
metadata:
labels:
app: foo
spec:
containers:
- image: busybox
name: busybox
status:
availableReplicas: 1
replicas: 2
observedGeneration: 1

View File

@@ -0,0 +1,63 @@
---
kind: Pod
apiVersion: v1
metadata:
namespace: test-1
name: pod-1
spec:
containers:
- image: busybox
name: busybox
status:
containerStatuses:
- name: busybox
ready: true
conditions:
- type: "www.example.com/gate"
status: "True"
---
kind: Pod
apiVersion: v1
metadata:
namespace: test-1
name: pod-2
spec:
containers:
- image: busybox
name: busybox
---
kind: Pod
apiVersion: v1
metadata:
namespace: test-1
name: pod-3
spec:
containers:
- image: busybox
name: busybox
status:
phase: Pending
conditions:
- type: "www.example.com/gate"
status: "Unknown"
containerStatuses:
- name: busybox
ready: true
---
kind: Pod
apiVersion: v1
metadata:
namespace: test-1
name: pod-4
spec:
containers:
- image: busybox
name: busybox
status:
phase: Pending
conditions:
- type: "www.example.com/other"
status: "Unknown"
containerStatuses:
- name: busybox
ready: true

View File

@@ -0,0 +1,162 @@
import os
import base64
import tempfile
import yaml
import mock
from mock import MagicMock
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
_create_auth_spec,
_create_configuration,
)
TEST_HOST = "test-host"
TEST_SSL_HOST = "https://test-host"
TEST_CLIENT_CERT = "/dev/null"
TEST_CLIENT_KEY = "/dev/null"
TEST_CERTIFICATE_AUTH = "/dev/null"
TEST_DATA = "test-data"
TEST_BEARER_TOKEN = "Bearer %s" % base64.standard_b64encode(TEST_DATA.encode()).decode()
TEST_KUBE_CONFIG = {
"current-context": "federal-context",
"contexts": [
{
"name": "simple_token",
"context": {"cluster": "default", "user": "simple_token"},
}
],
"clusters": [{"name": "default", "cluster": {"server": TEST_HOST}}],
"users": [
{
"name": "ssl-no_file",
"user": {
"token": TEST_BEARER_TOKEN,
"client-certificate": TEST_CLIENT_CERT,
"client-key": TEST_CLIENT_KEY,
},
}
],
}
_temp_files = []
def _remove_temp_file():
for f in _temp_files:
try:
os.remove(f)
except FileNotFoundError:
pass
def _create_temp_file(content=""):
handler, name = tempfile.mkstemp()
_temp_files.append(name)
os.write(handler, str.encode(content))
os.close(handler)
return name
def test_create_auth_spec_ssl_no_options():
module = MagicMock()
module.params = {}
actual_auth_spec = _create_auth_spec(module)
assert "proxy_headers" in actual_auth_spec
def test_create_auth_spec_ssl_options():
ssl_options = {
"host": TEST_SSL_HOST,
"token": TEST_BEARER_TOKEN,
"client_cert": TEST_CLIENT_CERT,
"client_key": TEST_CLIENT_KEY,
"ca_cert": TEST_CERTIFICATE_AUTH,
"validate_certs": True,
}
expected_auth_spec = {
"host": TEST_SSL_HOST,
"cert_file": TEST_CLIENT_CERT,
"key_file": TEST_CLIENT_KEY,
"ssl_ca_cert": TEST_CERTIFICATE_AUTH,
"verify_ssl": True,
"proxy_headers": {},
}
module = MagicMock()
module.params = ssl_options
actual_auth_spec = _create_auth_spec(module)
assert expected_auth_spec.items() <= actual_auth_spec.items()
def test_create_auth_spec_ssl_options_no_verify():
ssl_options = {
"host": TEST_SSL_HOST,
"token": TEST_BEARER_TOKEN,
"client_cert": TEST_CLIENT_CERT,
"client_key": TEST_CLIENT_KEY,
"validate_certs": False,
}
expected_auth_spec = {
"host": TEST_SSL_HOST,
"cert_file": TEST_CLIENT_CERT,
"key_file": TEST_CLIENT_KEY,
"verify_ssl": False,
"proxy_headers": {},
}
module = MagicMock()
module.params = ssl_options
actual_auth_spec = _create_auth_spec(module)
assert expected_auth_spec.items() <= actual_auth_spec.items()
@mock.patch.dict(os.environ, {"K8S_AUTH_PROXY_HEADERS_PROXY_BASIC_AUTH": "foo:bar"})
@mock.patch.dict(os.environ, {"K8S_AUTH_PROXY_HEADERS_USER_AGENT": "foo/1.0"})
@mock.patch.dict(os.environ, {"K8S_AUTH_CERT_FILE": TEST_CLIENT_CERT})
def test_create_auth_spec_ssl_proxy():
expected_auth_spec = {
"kubeconfig": "~/.kube/customconfig",
"verify_ssl": True,
"cert_file": TEST_CLIENT_CERT,
"proxy_headers": {"proxy_basic_auth": "foo:bar", "user_agent": "foo/1.0"},
}
module = MagicMock()
options = {"validate_certs": True, "kubeconfig": "~/.kube/customconfig"}
module.params = options
actual_auth_spec = _create_auth_spec(module)
assert expected_auth_spec.items() <= actual_auth_spec.items()
def test_load_kube_config_from_file_path():
config_file = _create_temp_file(yaml.safe_dump(TEST_KUBE_CONFIG))
auth = {"kubeconfig": config_file, "context": "simple_token"}
actual_configuration = _create_configuration(auth)
expected_configuration = {
"host": TEST_HOST,
"kubeconfig": config_file,
"context": "simple_token",
}
assert expected_configuration.items() <= actual_configuration.__dict__.items()
_remove_temp_file()
def test_load_kube_config_from_dict():
auth_spec = {"kubeconfig": TEST_KUBE_CONFIG, "context": "simple_token"}
actual_configuration = _create_configuration(auth_spec)
expected_configuration = {
"host": TEST_HOST,
"kubeconfig": TEST_KUBE_CONFIG,
"context": "simple_token",
}
assert expected_configuration.items() <= actual_configuration.__dict__.items()
_remove_temp_file()

View File

@@ -0,0 +1,91 @@
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import json
import kubernetes
import pytest
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
AnsibleK8SModule,
)
MINIMAL_K8S_VERSION = "12.0.0"
UNSUPPORTED_K8S_VERSION = "11.0.0"
@pytest.mark.parametrize("stdin", [{}], indirect=["stdin"])
def test_no_warn(monkeypatch, stdin, capfd):
monkeypatch.setattr(kubernetes, "__version__", MINIMAL_K8S_VERSION)
module = AnsibleK8SModule(argument_spec={})
with pytest.raises(SystemExit):
module.exit_json()
out, err = capfd.readouterr()
return_value = json.loads(out)
assert return_value.get("exception") is None
assert return_value.get("warnings") is None
assert return_value.get("failed") is None
@pytest.mark.parametrize("stdin", [{}], indirect=["stdin"])
def test_warn_on_k8s_version(monkeypatch, stdin, capfd):
monkeypatch.setattr(kubernetes, "__version__", UNSUPPORTED_K8S_VERSION)
module = AnsibleK8SModule(argument_spec={})
with pytest.raises(SystemExit):
module.exit_json()
out, err = capfd.readouterr()
return_value = json.loads(out)
assert return_value.get("warnings") is not None
warnings = return_value["warnings"]
assert len(warnings) == 1
assert "kubernetes" in warnings[0]
assert MINIMAL_K8S_VERSION in warnings[0]
dependencies = [
["18.20.0", "12.0.1", False],
["18.20.0", "18.20.0", True],
["12.0.1", "18.20.0", True],
]
@pytest.mark.parametrize(
"stdin,desired,actual,result", [({}, *d) for d in dependencies], indirect=["stdin"]
)
def test_has_at_least(monkeypatch, stdin, desired, actual, result, capfd):
monkeypatch.setattr(kubernetes, "__version__", actual)
module = AnsibleK8SModule(argument_spec={})
assert module.has_at_least("kubernetes", desired) is result
dependencies = [
["kubernetes", "18.20.0", "(kubernetes>=18.20.0)"],
["foobar", "1.0.0", "(foobar>=1.0.0)"],
["foobar", None, "(foobar)"],
]
@pytest.mark.parametrize(
"stdin,dependency,version,msg", [({}, *d) for d in dependencies], indirect=["stdin"]
)
def test_requires_fails_with_message(
monkeypatch, stdin, dependency, version, msg, capfd
):
monkeypatch.setattr(kubernetes, "__version__", "12.0.0")
module = AnsibleK8SModule(argument_spec={})
with pytest.raises(SystemExit):
module.requires(dependency, version)
out, err = capfd.readouterr()
return_value = json.loads(out)
assert return_value.get("failed")
assert msg in return_value.get("msg")

View File

@@ -0,0 +1,187 @@
import os
from pathlib import Path
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.resource import (
create_definitions,
flatten_list_kind,
from_file,
from_yaml,
merge_params,
)
def test_create_definitions_loads_from_definition():
params = {
"resource_definition": {
"kind": "Pod",
"apiVersion": "v1",
"metadata": {"name": "foo", "namespace": "bar"},
}
}
results = create_definitions(params)
assert len(results) == 1
assert results[0].kind == "Pod"
assert results[0].api_version == "v1"
assert results[0].name == "foo"
assert results[0].namespace == "bar"
def test_create_definitions_loads_from_file():
current = Path(os.path.dirname(os.path.abspath(__file__)))
params = {"src": current / "fixtures/definitions.yml"}
results = create_definitions(params)
assert len(results) == 3
assert results[0].kind == "Namespace"
assert results[1].kind == "Pod"
def test_create_definitions_loads_from_params():
params = {
"kind": "Pod",
"api_version": "v1",
"name": "foo",
"namespace": "foobar",
}
results = create_definitions(params)
assert len(results) == 1
assert results[0] == {
"kind": "Pod",
"apiVersion": "v1",
"metadata": {"name": "foo", "namespace": "foobar"},
}
def test_create_definitions_loads_list_kind():
params = {
"resource_definition": {
"kind": "PodList",
"apiVersion": "v1",
"items": [
{"kind": "Pod", "metadata": {"name": "foo"}},
{"kind": "Pod", "metadata": {"name": "bar"}},
],
}
}
results = create_definitions(params)
assert len(results) == 2
assert results[0].name == "foo"
assert results[1].name == "bar"
def test_merge_params_does_not_overwrite():
definition = {
"kind": "Pod",
"apiVersion": "v1",
"metadata": {"name": "foo", "namespace": "bar"},
}
params = {
"kind": "Service",
"api_version": "v2",
"name": "baz",
"namespace": "gaz",
}
result = merge_params(definition, params)
assert result == definition
def test_merge_params_adds_module_params():
params = {
"kind": "Pod",
"api_version": "v1",
"namespace": "bar",
"generate_name": "foo-",
}
result = merge_params({}, params)
assert result == {
"kind": "Pod",
"apiVersion": "v1",
"metadata": {"generateName": "foo-", "namespace": "bar"},
}
def test_from_yaml_loads_string_docs():
definition = """
kind: Pod
apiVersion: v1
metadata:
name: foo
namespace: bar
---
kind: ConfigMap
apiVersion: v1
metadata:
name: foo
namespace: bar
"""
result = list(from_yaml(definition))
assert result[0]["kind"] == "Pod"
assert result[1]["kind"] == "ConfigMap"
def test_from_yaml_loads_list():
definition = [
"""
kind: Pod
apiVersion: v1
metadata:
name: foo
namespace: bar
""",
"""
kind: ConfigMap
apiVersion: v1
metadata:
name: foo
namespace: bar
""",
{
"kind": "Pod",
"apiVersion": "v1",
"metadata": {"name": "baz", "namespace": "bar"},
},
]
result = list(from_yaml(definition))
assert len(result) == 3
assert result[0]["kind"] == "Pod"
assert result[1]["kind"] == "ConfigMap"
assert result[2]["metadata"]["name"] == "baz"
def test_from_yaml_loads_dictionary():
definition = {
"kind": "Pod",
"apiVersion": "v1",
"metadata": {"name": "foo", "namespace": "bar"},
}
result = list(from_yaml(definition))
assert result[0]["kind"] == "Pod"
def test_from_file_loads_definitions():
current = Path(os.path.dirname(os.path.abspath(__file__)))
result = list(from_file(current / "fixtures/definitions.yml"))
assert result[0]["kind"] == "Namespace"
assert result[1]["kind"] == "Pod"
def test_flatten_list_kind_flattens():
definition = {
"kind": "PodList",
"apiVersion": "v1",
"items": [
{"kind": "Pod", "metadata": {"name": "foo"}},
{"kind": "Pod", "metadata": {"name": "bar"}},
],
}
result = flatten_list_kind(definition, {"namespace": "foobar"})
assert len(result) == 2
assert result[0]["kind"] == "Pod"
assert result[0]["apiVersion"] == "v1"
assert result[0]["metadata"]["name"] == "foo"
assert result[0]["metadata"]["namespace"] == "foobar"
assert result[1]["kind"] == "Pod"
assert result[1]["apiVersion"] == "v1"
assert result[1]["metadata"]["name"] == "bar"
assert result[1]["metadata"]["namespace"] == "foobar"

View File

@@ -0,0 +1,135 @@
import pytest
from copy import deepcopy
from unittest.mock import Mock
from kubernetes.dynamic.resource import ResourceInstance
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.runner import (
perform_action,
)
definition = {
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "foo",
"labels": {"environment": "production", "app": "nginx"},
"namespace": "foo",
},
"spec": {
"containers": [
{
"name": "nginx",
"image": "nginx:1.14.2",
"command": ["/bin/sh", "-c", "sleep 10"],
}
]
},
}
modified_def = deepcopy(definition)
modified_def["metadata"]["labels"]["environment"] = "testing"
@pytest.mark.parametrize(
"action, params, existing, instance, expected",
[
(
"delete",
{"state": "absent"},
{},
{},
{"changed": False, "method": "delete", "result": {}},
),
(
"delete",
{"state": "absent"},
definition,
{"kind": "Status"},
{"changed": True, "method": "delete", "result": {"kind": "Status"}},
),
(
"apply",
{"apply": "yes"},
{},
definition,
{"changed": True, "method": "apply", "result": definition},
),
(
"create",
{"state": "patched"},
{},
{},
{
"changed": False,
"result": {},
"warnings": [
"resource 'kind=Pod,name=foo' was not found but will not be created as 'state' parameter has been set to 'patched'"
],
},
),
(
"create",
{},
{},
definition,
{"changed": True, "method": "create", "result": definition},
),
(
"replace",
{"force": "yes"},
definition,
definition,
{"changed": False, "method": "replace", "result": definition},
),
(
"replace",
{"force": "yes"},
definition,
modified_def,
{"changed": True, "method": "replace", "result": modified_def},
),
(
"update",
{},
definition,
definition,
{"changed": False, "method": "update", "result": definition},
),
(
"update",
{},
definition,
modified_def,
{"changed": True, "method": "update", "result": modified_def},
),
(
"create",
{"label_selectors": ["app=foo"]},
{},
definition,
{
"changed": False,
"msg": "resource 'kind=Pod,name=foo,namespace=foo' filtered by label_selectors.",
},
),
(
"create",
{"label_selectors": ["app=nginx"]},
{},
definition,
{"changed": True, "method": "create", "result": definition},
),
],
)
def test_perform_action(action, params, existing, instance, expected):
svc = Mock()
svc.find_resource.return_value = Mock(
kind=definition["kind"], group_version=definition["apiVersion"]
)
svc.retrieve.return_value = ResourceInstance(None, existing) if existing else None
spec = {action + ".return_value": instance}
svc.configure_mock(**spec)
result = perform_action(svc, definition, params)
assert expected.items() <= result.items()

View File

@@ -0,0 +1,292 @@
from unittest.mock import Mock
import pytest
from kubernetes.dynamic.resource import ResourceInstance, Resource
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
K8sService,
diff_objects,
)
from kubernetes.dynamic.exceptions import NotFoundError
pod_definition = {
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "foo",
"labels": {"environment": "production", "app": "nginx"},
"namespace": "foo",
},
"spec": {
"containers": [
{
"name": "nginx",
"image": "nginx:1.14.2",
"command": ["/bin/sh", "-c", "sleep 10"],
}
]
},
}
pod_definition_updated = {
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "foo",
"labels": {"environment": "testing", "app": "nginx"},
"namespace": "bar",
},
"spec": {
"containers": [
{
"name": "nginx",
"image": "nginx:1.14.2",
"command": ["/bin/sh", "-c", "sleep 10"],
}
]
},
}
@pytest.fixture(scope="module")
def mock_pod_resource_instance():
return ResourceInstance(None, pod_definition)
@pytest.fixture(scope="module")
def mock_pod_updated_resource_instance():
return ResourceInstance(None, pod_definition_updated)
def test_diff_objects_no_diff():
match, diff = diff_objects(pod_definition, pod_definition)
assert match is True
assert diff == {}
def test_diff_objects_meta_diff():
match, diff = diff_objects(pod_definition, pod_definition_updated)
assert match is False
assert diff["before"] == {
"metadata": {"labels": {"environment": "production"}, "namespace": "foo"}
}
assert diff["after"] == {
"metadata": {"labels": {"environment": "testing"}, "namespace": "bar"}
}
def test_diff_objects_spec_diff():
pod_definition_updated = {
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "foo",
"labels": {"environment": "production", "app": "nginx"},
"namespace": "foo",
},
"spec": {
"containers": [
{
"name": "busybox",
"image": "busybox",
"command": ["/bin/sh", "-c", "sleep 3600"],
}
]
},
}
match, diff = diff_objects(pod_definition, pod_definition_updated)
assert match is False
assert diff["before"]["spec"] == pod_definition["spec"]
assert diff["after"]["spec"] == pod_definition_updated["spec"]
def test_find_resource():
mock_pod_resource = Resource(
api_version="v1", kind="Pod", namespaced=False, preferred=True, prefix="api"
)
spec = {"resource.return_value": mock_pod_resource}
client = Mock(**spec)
svc = K8sService(client, Mock())
resource = svc.find_resource("Pod", "v1")
assert isinstance(resource, Resource)
assert resource.to_dict().items() <= mock_pod_resource.to_dict().items()
def test_service_delete_existing_resource(mock_pod_resource_instance):
spec = {"delete.side_effect": [mock_pod_resource_instance]}
client = Mock(**spec)
module = Mock(
params={"delete_options": {"gracePeriodSeconds": 2}}, check_mode=False
)
resource = Mock()
svc = K8sService(client, module)
result = svc.delete(resource, pod_definition, mock_pod_resource_instance)
assert isinstance(result, dict)
assert result == mock_pod_resource_instance.to_dict()
client.delete.assert_called_with(
resource,
name=pod_definition["metadata"]["name"],
namespace=pod_definition["metadata"]["namespace"],
body={"apiVersion": "v1", "kind": "DeleteOptions", "gracePeriodSeconds": 2},
)
def test_service_delete_no_existing_resource():
module = Mock()
module.params = {}
module.check_mode = False
client = Mock()
client.delete.return_value = mock_pod_resource_instance
svc = K8sService(client, module)
result = svc.delete(Mock(), pod_definition)
assert result == {}
client.delete.assert_not_called()
def test_service_delete_existing_resource_check_mode(mock_pod_resource_instance):
module = Mock(params={}, check_mode=True)
client = Mock(dry_run=False)
client.delete.return_value = mock_pod_resource_instance
svc = K8sService(client, module)
result = svc.delete(Mock(), pod_definition, mock_pod_resource_instance)
assert result == {}
client.delete.assert_not_called()
def test_service_create_resource(mock_pod_resource_instance):
spec = {"create.side_effect": [mock_pod_resource_instance]}
client = Mock(**spec)
module = Mock()
module.params = {}
module.check_mode = False
svc = K8sService(client, module)
result = svc.create(Mock(), pod_definition)
assert result == mock_pod_resource_instance.to_dict()
def test_service_create_resource_check_mode():
client = Mock(dry_run=False)
client.create.return_value = mock_pod_resource_instance
module = Mock(params={}, check_mode=True)
svc = K8sService(client, module)
result = svc.create(Mock(), pod_definition)
assert result == pod_definition
client.create.assert_not_called()
def test_service_retrieve_existing_resource(mock_pod_resource_instance):
spec = {"get.side_effect": [mock_pod_resource_instance]}
client = Mock(**spec)
module = Mock()
module.params = {}
svc = K8sService(client, module)
results = svc.retrieve(Mock(), pod_definition)
assert isinstance(results, ResourceInstance)
assert results.to_dict() == pod_definition
def test_service_retrieve_no_existing_resource():
spec = {"get.side_effect": [NotFoundError(Mock())]}
client = Mock(**spec)
module = Mock()
module.params = {}
svc = K8sService(client, module)
results = svc.retrieve(Mock(), pod_definition)
assert results is None
def test_create_project_request():
project_definition = {
"apiVersion": "v1",
"kind": "ProjectRequest",
"metadata": {"name": "test"},
}
spec = {"create.side_effect": [ResourceInstance(None, project_definition)]}
client = Mock(**spec)
module = Mock()
module.check_mode = False
module.params = {"state": "present"}
svc = K8sService(client, module)
results = svc.create_project_request(project_definition)
assert isinstance(results, dict)
assert results["changed"] is True
assert results["result"] == project_definition
def test_service_apply_existing_resource(mock_pod_resource_instance):
spec = {"apply.side_effect": [mock_pod_resource_instance]}
client = Mock(**spec)
module = Mock()
module.params = {"apply": True}
module.check_mode = False
svc = K8sService(client, module)
result = svc.apply(Mock(), pod_definition_updated, mock_pod_resource_instance)
assert result == mock_pod_resource_instance.to_dict()
def test_service_replace_existing_resource(mock_pod_resource_instance):
spec = {"replace.side_effect": [mock_pod_resource_instance]}
client = Mock(**spec)
module = Mock()
module.params = {}
module.check_mode = False
svc = K8sService(client, module)
result = svc.replace(Mock(), pod_definition, mock_pod_resource_instance)
assert result == mock_pod_resource_instance.to_dict()
def test_service_update_existing_resource(mock_pod_resource_instance):
spec = {"replace.side_effect": [mock_pod_resource_instance]}
client = Mock(**spec)
module = Mock()
module.params = {}
module.check_mode = False
svc = K8sService(client, module)
result = svc.replace(Mock(), pod_definition, mock_pod_resource_instance)
assert result == mock_pod_resource_instance.to_dict()
def test_service_find(mock_pod_resource_instance):
spec = {"get.side_effect": [mock_pod_resource_instance]}
client = Mock(**spec)
module = Mock()
module.params = {}
module.check_mode = False
svc = K8sService(client, module)
results = svc.find("Pod", "v1", name="foo", namespace="foo")
assert isinstance(results, dict)
assert results["api_found"] is True
assert results["resources"] is not []
assert len(results["resources"]) == 1
assert results["resources"][0] == pod_definition
def test_service_find_error():
spec = {"get.side_effect": [NotFoundError(Mock())]}
client = Mock(**spec)
module = Mock()
module.params = {}
module.check_mode = False
svc = K8sService(client, module)
results = svc.find("Pod", "v1", name="foo", namespace="foo")
assert isinstance(results, dict)
assert results["api_found"] is True
assert results["resources"] == []

View File

@@ -0,0 +1,122 @@
import os
import time
from pathlib import Path
from unittest.mock import Mock
import pytest
import yaml
from kubernetes.dynamic.resource import ResourceInstance
from kubernetes.dynamic.exceptions import NotFoundError
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.waiter import (
clock,
custom_condition,
deployment_ready,
DummyWaiter,
exists,
get_waiter,
pod_ready,
resource_absent,
Waiter,
)
def resources(filepath):
current = Path(os.path.dirname(os.path.abspath(__file__)))
with open(current / filepath) as fp:
return [ResourceInstance(None, d) for d in yaml.safe_load_all(fp)]
RESOURCES = resources("fixtures/definitions.yml")
PODS = resources("fixtures/pods.yml")
DEPLOYMENTS = resources("fixtures/deployments.yml")
def test_clock_times_out():
start = time.monotonic()
for x in clock(5, 1):
pass
elapsed = int(time.monotonic() - start)
assert x == 5
assert 5 <= elapsed <= 6
@pytest.mark.parametrize(
"resource,expected",
zip(RESOURCES + [None, {}], [True, True, True, False, False, False]),
)
def test_exists_and_absent_checks_for_existence(resource, expected):
assert exists(resource) is expected
assert resource_absent(resource) is not expected
@pytest.mark.parametrize("pod,expected", zip(PODS, [True, False, True, True]))
def test_pod_ready_checks_readiness(pod, expected):
assert pod_ready(pod) is expected
@pytest.mark.parametrize("pod,expected", zip(PODS, [True, False, False, False]))
def test_custom_condition_checks_readiness(pod, expected):
condition = {"type": "www.example.com/gate", "status": "True"}
assert custom_condition(condition, pod) is expected
@pytest.mark.parametrize("deployment,expected", zip(DEPLOYMENTS, [True, False]))
def test_deployment_ready_checks_readiness(deployment, expected):
assert deployment_ready(deployment) is expected
def test_dummywaiter_returns_resource_immediately():
resource = {
"kind": "Pod",
"apiVersion": "v1",
"metadata": {"name": "foopod", "namespace": "foobar"},
}
result, instance, elapsed = DummyWaiter().wait(resource, 10, 100)
assert result is True
assert instance == resource
assert elapsed == 0
def test_waiter_waits_for_missing_resource():
spec = {"get.side_effect": NotFoundError(Mock())}
client = Mock(**spec)
resource = Mock()
result, instance, elapsed = Waiter(client, resource, exists).wait(
timeout=3,
sleep=1,
name=RESOURCES[0]["metadata"].get("name"),
namespace=RESOURCES[0]["metadata"].get("namespace"),
)
assert result is False
assert instance == {}
assert abs(elapsed - 3) <= 1
@pytest.mark.parametrize("resource,expected", zip(RESOURCES, [True, True, True, False]))
def test_waiter_waits_for_resource_to_exist(resource, expected):
result = resource.to_dict()
spec = {"get.side_effect": [NotFoundError(Mock()), resource, resource, resource]}
client = Mock(**spec)
success, instance, elapsed = Waiter(client, Mock(), exists).wait(
timeout=3,
sleep=1,
name=result["metadata"].get("name"),
namespace=result["metadata"].get("namespace"),
)
assert success is expected
assert instance == result
assert abs(elapsed - 2) <= 1
def test_get_waiter_returns_correct_waiter():
assert get_waiter(Mock(), PODS[0]).predicate == pod_ready
waiter = get_waiter(Mock(), PODS[0], check_mode=True)
assert isinstance(waiter, DummyWaiter)
assert get_waiter(Mock(), PODS[0], state="absent").predicate == resource_absent
assert (
get_waiter(
Mock(), PODS[0], condition={"type": "Ready", "status": "True"}
).predicate.func
== custom_condition
)