mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-03-26 21:33:02 +00:00
* Cleanup gha * test by removing matrix excludes * Rename sanity tests * trigger integration tests * Fix ansible-lint workflow * Fix concurrency * Add ansible-lint config * Add ansible-lint config * Fix integration and lint issues * integration wf * fix yamllint issues * fix yamllint issues * update readme and add ignore-2.16.txt * fix ansible-doc * Add version * Use /dev/random to generate random data The GHA environment has difficultly generating entropy. Trying to read from /dev/urandom just blocks forever. We don't care if the random data is cryptographically secure; it's just garbage data for the test. Read from /dev/random, instead. This is only used during the k8s_copy test target. This also removes the custom test module that was being used to generate the files. It's not worth maintaining this for two task that can be replaced with some simple command/shell tasks. * Fix saniry errors * test github_action fix * Address review comments * Remove default types * review comments * isort fixes * remove tags * Add setuptools to venv * Test gh changes * update changelog * update ignore-2.16 * Fix indentation in inventory plugin example * Update .github/workflows/integration-tests.yaml * Update integration-tests.yaml --------- Co-authored-by: Mike Graves <mgraves@redhat.com> Co-authored-by: Bikouo Aubin <79859644+abikouo@users.noreply.github.com>
234 lines
7.9 KiB
Python
234 lines
7.9 KiB
Python
# 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,
|
|
ResourceTimeout,
|
|
)
|
|
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,
|
|
hide_fields,
|
|
)
|
|
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 get_definitions(svc, params):
|
|
try:
|
|
definitions = create_definitions(params)
|
|
except Exception as e:
|
|
msg = "Failed to load resource definition: {0}".format(e)
|
|
raise CoreException(msg) from e
|
|
|
|
delete_all = params.get("delete_all")
|
|
src = params.get("src")
|
|
resource_definition = params.get("resource_definition")
|
|
name = params.get("name")
|
|
state = params.get("state")
|
|
|
|
if (
|
|
delete_all
|
|
and state == "absent"
|
|
and name is None
|
|
and resource_definition is None
|
|
and src is None
|
|
):
|
|
# Delete all resources in the namespace for the specified resource type
|
|
if params.get("kind") is None:
|
|
raise CoreException(
|
|
"'kind' option is required to specify the resource type."
|
|
)
|
|
|
|
resource = svc.find_resource(
|
|
params.get("kind"), params.get("api_version"), fail=True
|
|
)
|
|
definitions = svc.retrieve_all(
|
|
resource,
|
|
params.get("namespace"),
|
|
params.get("label_selectors"),
|
|
)
|
|
|
|
return definitions
|
|
|
|
|
|
def run_module(module) -> None:
|
|
results = []
|
|
changed = False
|
|
client = get_api_client(module)
|
|
svc = K8sService(client, module)
|
|
|
|
definitions = get_definitions(svc, module.params)
|
|
|
|
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")
|
|
hidden_fields = params.get("hidden_fields")
|
|
|
|
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, hidden_fields)
|
|
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"] = hide_fields(instance, hidden_fields)
|
|
if not success:
|
|
raise ResourceTimeout(
|
|
'"{0}" "{1}": Timed out waiting on resource'.format(
|
|
definition["kind"], origin_name
|
|
),
|
|
result,
|
|
)
|
|
|
|
return result
|