Skip to content

Commit

Permalink
Split JSON patch out into a new module (#99)
Browse files Browse the repository at this point in the history
Co-authored-by: Abhijeet Kasurde <akasurde@redhat.com>
  • Loading branch information
gravesm and Akasurde authored May 24, 2021
1 parent 1f47931 commit 5b5777d
Show file tree
Hide file tree
Showing 7 changed files with 467 additions and 151 deletions.
4 changes: 4 additions & 0 deletions changelogs/fragments/99-json-patch-module.yaml
Original file line number Diff line number Diff line change
@@ -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).
7 changes: 7 additions & 0 deletions molecule/default/converge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@
tags: [ info, k8s ]
tags:
- always
- name: Include json_patch.yml
include_tasks:
file: tasks/json_patch.yml
apply:
tags: [ json_patch, k8s ]
tags:
- always
- name: Include lists.yml
include_tasks:
file: tasks/lists.yml
Expand Down
170 changes: 170 additions & 0 deletions molecule/default/tasks/json_patch.yml
Original file line number Diff line number Diff line change
@@ -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
109 changes: 0 additions & 109 deletions molecule/default/tasks/merge_type.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,115 +134,6 @@
- merge_out.resources[0].spec.template.spec.containers | list | length == 1
- 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:
- name: Ensure namespace has been deleted
kubernetes.core.k8s:
Expand Down
47 changes: 5 additions & 42 deletions plugins/module_utils/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,17 +97,6 @@
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):
m = hashlib.sha256()
for k in AUTH_ARG_MAP:
Expand Down Expand Up @@ -841,42 +830,16 @@ def build_error_msg(kind, name, msg):
self.fail_json(msg=msg, **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):
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 merge_type:
params['content_type'] = 'application/{0}-patch+json'.format(merge_type)
patch_data, error = self.json_patch(existing, definition, merge_type)
if error is not None:
return None, error
k8s_obj = resource.patch(patch_data, **params).to_dict()
k8s_obj = resource.patch(definition, **params).to_dict()
match, diffs = self.diff_objects(existing.to_dict(), k8s_obj)
error = {}
return k8s_obj, {}
Expand Down
1 change: 1 addition & 0 deletions plugins/modules/k8s.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
C(['strategic-merge', 'merge']), which is ideal for using the same parameters on resource kinds that
combine Custom Resources and built-in resources.
- 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:
- json
- merge
Expand Down
Loading

0 comments on commit 5b5777d

Please sign in to comment.