mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-07-30 03:14:40 +00:00
Split JSON patch out into a new module (#99)
Co-authored-by: Abhijeet Kasurde <akasurde@redhat.com>
This commit is contained in:
4
changelogs/fragments/99-json-patch-module.yaml
Normal file
4
changelogs/fragments/99-json-patch-module.yaml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
major_changes:
|
||||||
|
- k8s - deprecate merge_type=json. The JSON patch functionality has never worked (https://github.com/ansible-collections/kubernetes.core/pull/99).
|
||||||
|
- k8s_json_patch - split JSON patch functionality out into a separate module (https://github.com/ansible-collections/kubernetes.core/pull/99).
|
||||||
@@ -90,6 +90,13 @@
|
|||||||
tags: [ info, k8s ]
|
tags: [ info, k8s ]
|
||||||
tags:
|
tags:
|
||||||
- always
|
- always
|
||||||
|
- name: Include json_patch.yml
|
||||||
|
include_tasks:
|
||||||
|
file: tasks/json_patch.yml
|
||||||
|
apply:
|
||||||
|
tags: [ json_patch, k8s ]
|
||||||
|
tags:
|
||||||
|
- always
|
||||||
- name: Include lists.yml
|
- name: Include lists.yml
|
||||||
include_tasks:
|
include_tasks:
|
||||||
file: tasks/lists.yml
|
file: tasks/lists.yml
|
||||||
|
|||||||
170
molecule/default/tasks/json_patch.yml
Normal file
170
molecule/default/tasks/json_patch.yml
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
- vars:
|
||||||
|
namespace: json-patch
|
||||||
|
pod: json-patch
|
||||||
|
deployment: json-patch
|
||||||
|
|
||||||
|
block:
|
||||||
|
- name: Ensure namespace exists
|
||||||
|
kubernetes.core.k8s:
|
||||||
|
kind: namespace
|
||||||
|
name: "{{ namespace }}"
|
||||||
|
|
||||||
|
- name: Create a simple pod
|
||||||
|
kubernetes.core.k8s:
|
||||||
|
definition:
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
namespace: "{{ namespace }}"
|
||||||
|
name: "{{ pod }}"
|
||||||
|
labels:
|
||||||
|
label1: foo
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: busybox:musl
|
||||||
|
name: busybox
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
- while true; do echo $(date); sleep 10; done
|
||||||
|
wait: yes
|
||||||
|
|
||||||
|
- name: Add a label and replace the image in checkmode
|
||||||
|
kubernetes.core.k8s_json_patch:
|
||||||
|
kind: Pod
|
||||||
|
namespace: "{{ namespace }}"
|
||||||
|
name: "{{ pod }}"
|
||||||
|
patch:
|
||||||
|
- op: add
|
||||||
|
path: /metadata/labels/label2
|
||||||
|
value: bar
|
||||||
|
- op: replace
|
||||||
|
path: /spec/containers/0/image
|
||||||
|
value: busybox:glibc
|
||||||
|
check_mode: yes
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert patch was made
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result.changed
|
||||||
|
- result.result.metadata.labels.label2 == "bar"
|
||||||
|
- result.result.spec.containers[0].image == "busybox:glibc"
|
||||||
|
|
||||||
|
- name: Describe pod
|
||||||
|
kubernetes.core.k8s_info:
|
||||||
|
kind: Pod
|
||||||
|
name: "{{ pod }}"
|
||||||
|
namespace: "{{ namespace }}"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert pod has not changed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result.resources[0].metadata.labels.label2 is not defined
|
||||||
|
- result.resources[0].spec.containers[0].image == "busybox:musl"
|
||||||
|
|
||||||
|
- name: Add a label and replace the image
|
||||||
|
kubernetes.core.k8s_json_patch:
|
||||||
|
kind: Pod
|
||||||
|
namespace: "{{ namespace }}"
|
||||||
|
name: "{{ pod }}"
|
||||||
|
patch:
|
||||||
|
- op: add
|
||||||
|
path: /metadata/labels/label2
|
||||||
|
value: bar
|
||||||
|
- op: replace
|
||||||
|
path: /spec/containers/0/image
|
||||||
|
value: busybox:glibc
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert patch was made
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result.changed
|
||||||
|
|
||||||
|
- name: Describe pod
|
||||||
|
kubernetes.core.k8s_info:
|
||||||
|
kind: Pod
|
||||||
|
name: "{{ pod }}"
|
||||||
|
namespace: "{{ namespace }}"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that both patch operations have been applied
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result.resources[0].metadata.labels.label2 == "bar"
|
||||||
|
- result.resources[0].spec.containers[0].image == "busybox:glibc"
|
||||||
|
|
||||||
|
- name: Apply the same patch to the pod
|
||||||
|
kubernetes.core.k8s_json_patch:
|
||||||
|
kind: Pod
|
||||||
|
namespace: "{{ namespace }}"
|
||||||
|
name: "{{ pod }}"
|
||||||
|
patch:
|
||||||
|
- op: add
|
||||||
|
path: /metadata/labels/label2
|
||||||
|
value: bar
|
||||||
|
- op: replace
|
||||||
|
path: /spec/containers/0/image
|
||||||
|
value: busybox:glibc
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that no changes were made
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not result.changed
|
||||||
|
|
||||||
|
- name: Create a simple deployment
|
||||||
|
kubernetes.core.k8s:
|
||||||
|
wait: yes
|
||||||
|
definition:
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
namespace: "{{ namespace }}"
|
||||||
|
name: "{{ deployment }}"
|
||||||
|
labels:
|
||||||
|
name: "{{ deployment }}"
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: busybox
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: busybox
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: busybox
|
||||||
|
image: busybox
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
- while true; do echo $(date); sleep 10; done
|
||||||
|
|
||||||
|
- name: Apply patch and wait for deployment to be ready
|
||||||
|
kubernetes.core.k8s_json_patch:
|
||||||
|
kind: Deployment
|
||||||
|
namespace: "{{ namespace }}"
|
||||||
|
name: "{{ deployment }}"
|
||||||
|
patch:
|
||||||
|
- op: replace
|
||||||
|
path: /spec/replicas
|
||||||
|
value: 3
|
||||||
|
wait: yes
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert all replicas are available
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result.result.status.availableReplicas == 3
|
||||||
|
|
||||||
|
always:
|
||||||
|
- name: Ensure namespace has been deleted
|
||||||
|
kubernetes.core.k8s:
|
||||||
|
kind: Namespace
|
||||||
|
name: "{{ namespace }}"
|
||||||
|
state: absent
|
||||||
|
ignore_errors: yes
|
||||||
@@ -134,115 +134,6 @@
|
|||||||
- merge_out.resources[0].spec.template.spec.containers | list | length == 1
|
- merge_out.resources[0].spec.template.spec.containers | list | length == 1
|
||||||
- merge_out.resources[0].spec.template.spec.containers[0].image == 'python'
|
- merge_out.resources[0].spec.template.spec.containers[0].image == 'python'
|
||||||
|
|
||||||
# Json
|
|
||||||
- name: create simple pod
|
|
||||||
kubernetes.core.k8s:
|
|
||||||
namespace: "{{ k8s_patch_namespace }}"
|
|
||||||
definition:
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Pod
|
|
||||||
metadata:
|
|
||||||
name: "{{ k8s_json }}-pod"
|
|
||||||
labels:
|
|
||||||
name: "{{ k8s_json }}-pod"
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- args:
|
|
||||||
- /bin/sh
|
|
||||||
- -c
|
|
||||||
- while true; do echo $(date); sleep 10; done
|
|
||||||
image: python:3.7-alpine
|
|
||||||
imagePullPolicy: Always
|
|
||||||
name: alpine
|
|
||||||
|
|
||||||
- name: Patch pod - update container image
|
|
||||||
kubernetes.core.k8s:
|
|
||||||
kind: Pod
|
|
||||||
namespace: "{{ k8s_patch_namespace }}"
|
|
||||||
name: "{{ k8s_json }}-pod"
|
|
||||||
merge_type:
|
|
||||||
- json
|
|
||||||
definition:
|
|
||||||
- op: replace
|
|
||||||
path: /spec/containers/0/image
|
|
||||||
value: python:3.8-alpine
|
|
||||||
register: pod_patch
|
|
||||||
|
|
||||||
- name: assert that patch was performed
|
|
||||||
assert:
|
|
||||||
that:
|
|
||||||
- pod_patch.changed
|
|
||||||
|
|
||||||
- name: describe Pod after patching
|
|
||||||
kubernetes.core.k8s_info:
|
|
||||||
kind: Pod
|
|
||||||
name: "{{ k8s_json }}-pod"
|
|
||||||
namespace: "{{ k8s_patch_namespace }}"
|
|
||||||
register: describe_pod
|
|
||||||
|
|
||||||
- name: assert that image name has changed
|
|
||||||
assert:
|
|
||||||
that:
|
|
||||||
- describe_pod.resources[0].spec.containers[0].image == 'python:3.8-alpine'
|
|
||||||
|
|
||||||
- name: create a simple nginx deployment
|
|
||||||
kubernetes.core.k8s:
|
|
||||||
namespace: "{{ k8s_patch_namespace }}"
|
|
||||||
definition:
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: "{{ k8s_json }}-depl"
|
|
||||||
labels:
|
|
||||||
name: "{{ k8s_json }}-depl"
|
|
||||||
spec:
|
|
||||||
replicas: 2
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: nginx
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx-container
|
|
||||||
image: nginx
|
|
||||||
args:
|
|
||||||
- /bin/sh
|
|
||||||
- -c
|
|
||||||
- while true; do echo $(date); sleep 10; done
|
|
||||||
|
|
||||||
- name: Patch Nginx deployment command
|
|
||||||
kubernetes.core.k8s:
|
|
||||||
kind: Deployment
|
|
||||||
namespace: "{{ k8s_patch_namespace }}"
|
|
||||||
name: "{{ k8s_json }}-depl"
|
|
||||||
merge_type:
|
|
||||||
- json
|
|
||||||
definition:
|
|
||||||
- op: add
|
|
||||||
path: '/spec/template/spec/containers/0/args/-'
|
|
||||||
value: 'touch /var/log'
|
|
||||||
register: patch_out
|
|
||||||
|
|
||||||
- name: assert that patch succeed
|
|
||||||
assert:
|
|
||||||
that:
|
|
||||||
- patch_out.changed
|
|
||||||
|
|
||||||
- name: describe deployment after patching
|
|
||||||
kubernetes.core.k8s_info:
|
|
||||||
kind: Deployment
|
|
||||||
name: "{{ k8s_json }}-depl"
|
|
||||||
namespace: "{{ k8s_patch_namespace }}"
|
|
||||||
register: describe_depl
|
|
||||||
|
|
||||||
- name: assert that args changed on deployment
|
|
||||||
assert:
|
|
||||||
that:
|
|
||||||
- describe_depl.resources[0].spec.template.spec.containers[0].args | length == 4
|
|
||||||
|
|
||||||
always:
|
always:
|
||||||
- name: Ensure namespace has been deleted
|
- name: Ensure namespace has been deleted
|
||||||
kubernetes.core.k8s:
|
kubernetes.core.k8s:
|
||||||
|
|||||||
@@ -97,17 +97,6 @@ except ImportError as e:
|
|||||||
K8S_IMP_ERR = traceback.format_exc()
|
K8S_IMP_ERR = traceback.format_exc()
|
||||||
|
|
||||||
|
|
||||||
JSON_PATCH_IMP_ERR = None
|
|
||||||
try:
|
|
||||||
import jsonpatch
|
|
||||||
HAS_JSON_PATCH = True
|
|
||||||
jsonpatch_import_exception = None
|
|
||||||
except ImportError as e:
|
|
||||||
HAS_JSON_PATCH = False
|
|
||||||
jsonpatch_import_exception = e
|
|
||||||
JSON_PATCH_IMP_ERR = traceback.format_exc()
|
|
||||||
|
|
||||||
|
|
||||||
def configuration_digest(configuration):
|
def configuration_digest(configuration):
|
||||||
m = hashlib.sha256()
|
m = hashlib.sha256()
|
||||||
for k in AUTH_ARG_MAP:
|
for k in AUTH_ARG_MAP:
|
||||||
@@ -841,42 +830,16 @@ class K8sAnsibleMixin(object):
|
|||||||
self.fail_json(msg=msg, **result)
|
self.fail_json(msg=msg, **result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def json_patch(self, existing, definition, merge_type):
|
|
||||||
if merge_type == "json":
|
|
||||||
if not HAS_JSON_PATCH:
|
|
||||||
error = {
|
|
||||||
"msg": missing_required_lib('jsonpatch'),
|
|
||||||
"exception": JSON_PATCH_IMP_ERR,
|
|
||||||
"error": to_native(jsonpatch_import_exception)
|
|
||||||
}
|
|
||||||
return None, error
|
|
||||||
try:
|
|
||||||
patch = jsonpatch.JsonPatch([definition])
|
|
||||||
result_patch = patch.apply(existing.to_dict())
|
|
||||||
return result_patch, None
|
|
||||||
except jsonpatch.InvalidJsonPatch as e:
|
|
||||||
error = {
|
|
||||||
"msg": "invalid json patch",
|
|
||||||
"error": to_native(e)
|
|
||||||
}
|
|
||||||
return None, error
|
|
||||||
except jsonpatch.JsonPatchConflict as e:
|
|
||||||
error = {
|
|
||||||
"msg": "patch could not be applied due to conflict situation",
|
|
||||||
"error": to_native(e)
|
|
||||||
}
|
|
||||||
return None, error
|
|
||||||
return definition, None
|
|
||||||
|
|
||||||
def patch_resource(self, resource, definition, existing, name, namespace, merge_type=None):
|
def patch_resource(self, resource, definition, existing, name, namespace, merge_type=None):
|
||||||
|
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:
|
try:
|
||||||
params = dict(name=name, namespace=namespace)
|
params = dict(name=name, namespace=namespace)
|
||||||
if merge_type:
|
if merge_type:
|
||||||
params['content_type'] = 'application/{0}-patch+json'.format(merge_type)
|
params['content_type'] = 'application/{0}-patch+json'.format(merge_type)
|
||||||
patch_data, error = self.json_patch(existing, definition, merge_type)
|
k8s_obj = resource.patch(definition, **params).to_dict()
|
||||||
if error is not None:
|
|
||||||
return None, error
|
|
||||||
k8s_obj = resource.patch(patch_data, **params).to_dict()
|
|
||||||
match, diffs = self.diff_objects(existing.to_dict(), k8s_obj)
|
match, diffs = self.diff_objects(existing.to_dict(), k8s_obj)
|
||||||
error = {}
|
error = {}
|
||||||
return k8s_obj, {}
|
return k8s_obj, {}
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ options:
|
|||||||
C(['strategic-merge', 'merge']), which is ideal for using the same parameters on resource kinds that
|
C(['strategic-merge', 'merge']), which is ideal for using the same parameters on resource kinds that
|
||||||
combine Custom Resources and built-in resources.
|
combine Custom Resources and built-in resources.
|
||||||
- mutually exclusive with C(apply)
|
- mutually exclusive with C(apply)
|
||||||
|
- I(merge_type=json) is deprecated and will be removed in version 3.0.0. Please use M(kubernetes.core.k8s_json_patch) instead.
|
||||||
choices:
|
choices:
|
||||||
- json
|
- json
|
||||||
- merge
|
- merge
|
||||||
|
|||||||
280
plugins/modules/k8s_json_patch.py
Normal file
280
plugins/modules/k8s_json_patch.py
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
#!/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(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:
|
||||||
|
obj, error = json_patch(existing.to_dict(), patch)
|
||||||
|
if error:
|
||||||
|
module.fail_json(**error)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
obj = resource.patch(patch, name=name, namespace=namespace, content_type="application/json-patch+json").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
|
||||||
|
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()
|
||||||
Reference in New Issue
Block a user