Repository URL to install this package:
|
Version:
6.0.0 ▾
|
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (C), 2018 Red Hat | Ansible
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = r"""
module: k8s_json_patch
short_description: Apply JSON patch operations to existing objects
description:
- This module is used to apply RFC 6902 JSON patch operations only.
- Use the M(kubernetes.core.k8s) module for strategic merge or JSON merge operations.
- The jsonpatch library is required for check mode.
version_added: 2.0.0
author:
- Mike Graves (@gravesm)
options:
api_version:
description:
- Use to specify the API version.
- Use in conjunction with I(kind), I(name), and I(namespace) to identify a specific object.
type: str
default: v1
aliases:
- api
- version
kind:
description:
- Use to specify an object model.
- Use in conjunction with I(api_version), I(name), and I(namespace) to identify a specific object.
type: str
required: yes
namespace:
description:
- Use to specify an object namespace.
- Use in conjunction with I(api_version), I(kind), and I(name) to identify a specific object.
type: str
name:
description:
- Use to specify an object name.
- Use in conjunction with I(api_version), I(kind), and I(namespace) to identify a specific object.
type: str
required: yes
patch:
description:
- List of JSON patch operations.
required: yes
type: list
elements: dict
extends_documentation_fragment:
- kubernetes.core.k8s_auth_options
- kubernetes.core.k8s_wait_options
requirements:
- "python >= 3.6"
- "kubernetes >= 12.0.0"
- "PyYAML >= 3.11"
- "jsonpatch"
"""
EXAMPLES = r"""
- name: Apply multiple patch operations to an existing Pod
kubernetes.core.k8s_json_patch:
kind: Pod
namespace: testing
name: mypod
patch:
- op: add
path: /metadata/labels/app
value: myapp
- op: replace
patch: /spec/containers/0/image
value: nginx
"""
RETURN = r"""
result:
description: The modified object.
returned: success
type: dict
contains:
api_version:
description: The versioned schema of this representation of an object.
returned: success
type: str
kind:
description: The REST resource this object represents.
returned: success
type: str
metadata:
description: Standard object metadata. Includes name, namespace, annotations, labels, etc.
returned: success
type: dict
spec:
description: Specific attributes of the object. Will vary based on the I(api_version) and I(kind).
returned: success
type: dict
status:
description: Current status details for the object.
returned: success
type: dict
duration:
description: Elapsed time of task in seconds.
returned: when C(wait) is true
type: int
sample: 48
error:
description: The error when patching the object.
returned: error
type: dict
sample: {
"msg": "Failed to import the required Python library (jsonpatch) ...",
"exception": "Traceback (most recent call last): ..."
}
"""
import copy
import traceback
from ansible.module_utils.basic import missing_required_lib
from ansible.module_utils._text import to_native
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,
WAIT_ARG_SPEC,
)
from ansible_collections.kubernetes.core.plugins.module_utils.common import (
get_api_client,
K8sAnsibleMixin,
)
try:
from kubernetes.dynamic.exceptions import DynamicApiError
except ImportError:
# kubernetes library check happens in common.py
pass
JSON_PATCH_IMPORT_ERR = None
try:
import jsonpatch
HAS_JSON_PATCH = True
except ImportError:
HAS_JSON_PATCH = False
JSON_PATCH_IMPORT_ERR = traceback.format_exc()
JSON_PATCH_ARGS = {
"api_version": {"default": "v1", "aliases": ["api", "version"]},
"kind": {"type": "str", "required": True},
"namespace": {"type": "str"},
"name": {"type": "str", "required": True},
"patch": {"type": "list", "required": True, "elements": "dict"},
}
def json_patch(existing, patch):
if not HAS_JSON_PATCH:
error = {
"msg": missing_required_lib("jsonpatch"),
"exception": JSON_PATCH_IMPORT_ERR,
}
return None, error
try:
patch = jsonpatch.JsonPatch(patch)
patched = patch.apply(existing)
return patched, None
except jsonpatch.InvalidJsonPatch as e:
error = {"msg": "Invalid JSON patch", "exception": e}
return None, error
except jsonpatch.JsonPatchConflict as e:
error = {"msg": "Patch could not be applied due to a conflict", "exception": e}
return None, error
def execute_module(k8s_module, module):
kind = module.params.get("kind")
api_version = module.params.get("api_version")
name = module.params.get("name")
namespace = module.params.get("namespace")
patch = module.params.get("patch")
wait = module.params.get("wait")
wait_sleep = module.params.get("wait_sleep")
wait_timeout = module.params.get("wait_timeout")
wait_condition = None
if module.params.get("wait_condition") and module.params.get("wait_condition").get(
"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)
try:
existing = resource.get(name=name, namespace=namespace)
except DynamicApiError as exc:
msg = "Failed to retrieve requested object: {0}".format(exc.body)
module.fail_json(
msg=build_error_msg(kind, name, msg),
error=exc.status,
status=exc.status,
reason=exc.reason,
)
except ValueError as exc:
msg = "Failed to retrieve requested object: {0}".format(to_native(exc))
module.fail_json(
msg=build_error_msg(kind, name, msg), error="", status="", reason=""
)
if module.check_mode and not k8s_module.supports_dry_run:
obj, error = json_patch(existing.to_dict(), patch)
if error:
module.fail_json(**error)
else:
params = {}
if module.check_mode:
params["dry_run"] = "All"
try:
obj = resource.patch(
patch,
name=name,
namespace=namespace,
content_type="application/json-patch+json",
**params
).to_dict()
except DynamicApiError as exc:
msg = "Failed to patch existing object: {0}".format(exc.body)
module.fail_json(
msg=msg, error=exc.status, status=exc.status, reason=exc.reason
)
except Exception as exc:
msg = "Failed to patch existing object: {0}".format(exc)
module.fail_json(msg=msg, error=to_native(exc), status="", reason="")
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
)
match, diffs = k8s_module.diff_objects(existing.to_dict(), obj)
result["changed"] = not match
if module._diff:
result["diff"] = diffs
if not success:
msg = "Resource update timed out"
module.fail_json(msg=msg, **result)
module.exit_json(**result)
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)
if __name__ == "__main__":
main()