support merge_type`json` (#83)

Fixes #54
This commit is contained in:
abikouo
2021-04-28 15:06:43 +02:00
committed by GitHub
parent d29f8c1eb7
commit 963aa3fbe6
7 changed files with 308 additions and 2 deletions

View File

@@ -87,7 +87,7 @@ jobs:
# The 3.3.0 release of molecule introduced a breaking change. See
# https://github.com/ansible-community/molecule/issues/3083
- name: Install molecule and openshift dependencies
run: pip install ansible "molecule<3.3.0" yamllint openshift flake8
run: pip install ansible "molecule<3.3.0" yamllint openshift flake8 jsonpatch
# The latest release doesn't work with Molecule currently.
# See: https://github.com/ansible-community/molecule/issues/2757

View File

@@ -0,0 +1,3 @@
---
bugfixes:
- k8s - fix merge_type option when set to json (https://github.com/ansible-collections/kubernetes.core/issues/54).

View File

@@ -133,6 +133,14 @@
tags:
- always
- name: Include merge_type.yml
include_tasks:
file: tasks/merge_type.yml
apply:
tags: [ merge_type, k8s ]
tags:
- always
roles:
- role: helm
tags:

View File

@@ -0,0 +1,252 @@
- block:
- name: Define common facts
set_fact:
k8s_patch_namespace: "patch"
k8s_strategic_merge: "strategic-merge"
k8s_merge: "json-merge"
k8s_json: "json-patch"
- name: Ensure the namespace exist
kubernetes.core.k8s:
kind: namespace
name: "{{ k8s_patch_namespace }}"
# Strategic merge
- name: create a simple nginx deployment
kubernetes.core.k8s:
namespace: "{{ k8s_patch_namespace }}"
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: "{{ k8s_strategic_merge }}"
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: "{{ k8s_strategic_merge }}-ctr"
image: nginx
tolerations:
- effect: NoSchedule
key: dedicated
value: "test-strategic-merge"
- name: patch service using strategic merge
kubernetes.core.k8s:
kind: Deployment
namespace: "{{ k8s_patch_namespace }}"
name: "{{ k8s_strategic_merge }}"
definition:
spec:
template:
spec:
containers:
- name: "{{ k8s_strategic_merge }}-ctr-2"
image: redis
register: depl_patch
- name: validate that resource was patched
assert:
that:
- depl_patch.changed
- name: describe "{{ k8s_strategic_merge }}" deployment
kubernetes.core.k8s_info:
kind: Deployment
name: "{{ k8s_strategic_merge }}"
namespace: "{{ k8s_patch_namespace }}"
register: deployment_out
- name: assert that deployment contains expected images
assert:
that:
- deployment_out.resources[0].spec.template.spec.containers | selectattr('image','equalto','nginx') | list | length == 1
- deployment_out.resources[0].spec.template.spec.containers | selectattr('image','equalto','redis') | list | length == 1
# Json merge
- name: create a simple nginx deployment (testing merge patch)
kubernetes.core.k8s:
namespace: "{{ k8s_patch_namespace }}"
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: "{{ k8s_merge }}"
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: "{{ k8s_merge }}-ctr"
image: nginx
tolerations:
- effect: NoSchedule
key: dedicated
value: "test-strategic-merge"
- name: patch service using json merge patch
kubernetes.core.k8s:
kind: Deployment
namespace: "{{ k8s_patch_namespace }}"
name: "{{ k8s_merge }}"
merge_type:
- merge
definition:
spec:
template:
spec:
containers:
- name: "{{ k8s_merge }}-ctr-2"
image: python
register: merge_patch
- name: validate that resource was patched
assert:
that:
- merge_patch.changed
- name: describe "{{ k8s_merge }}" deployment
kubernetes.core.k8s_info:
kind: Deployment
name: "{{ k8s_merge }}"
namespace: "{{ k8s_patch_namespace }}"
register: merge_out
- name: assert that deployment contains expected images
assert:
that:
- 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:
kind: namespace
name: "{{ k8s_patch_namespace }}"
state: absent
ignore_errors: yes

View File

@@ -106,6 +106,17 @@ except ImportError as e:
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:
@@ -851,12 +862,42 @@ class K8sAnsibleMixin(object):
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):
try:
params = dict(name=name, namespace=namespace)
if merge_type:
params['content_type'] = 'application/{0}-patch+json'.format(merge_type)
k8s_obj = resource.patch(definition, **params).to_dict()
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()
match, diffs = self.diff_objects(existing.to_dict(), k8s_obj)
error = {}
return k8s_obj, {}

View File

@@ -136,6 +136,7 @@ requirements:
- "python >= 2.7"
- "openshift >= 0.6"
- "PyYAML >= 3.11"
- "jsonpatch"
'''
EXAMPLES = r'''

View File

@@ -1,2 +1,3 @@
openshift>=0.6.2
requests-oauthlib
jsonpatch