Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release-0.11] Recursive diff #370

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 59 additions & 3 deletions openshift/dynamic/apply.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@
'imagePullSecrets': 'name',
'containers.volumeMounts': 'mountPath',
'containers.volumeDevices': 'devicePath',
'containers.envVars': 'name',
'containers.env': 'name',
'containers.ports': 'containerPort',
'initContainers.volumeMounts': 'mountPath',
'initContainers.volumeDevices': 'devicePath',
'initContainers.envVars': 'name',
'initContainers.env': 'name',
'initContainers.ports': 'containerPort',
'ephemeralContainers.volumeMounts': 'mountPath',
'ephemeralContainers.volumeDevices': 'devicePath',
'ephemeralContainers.envVars': 'name',
'ephemeralContainers.env': 'name',
'ephemeralContainers.ports': 'containerPort',
}

Expand Down Expand Up @@ -183,6 +183,62 @@ def list_merge(last_applied, actual, desired, position):
return desired


def recursive_list_diff(list1, list2, position=None):
result = (list(), list())
if position in STRATEGIC_MERGE_PATCH_KEYS:
patch_merge_key = STRATEGIC_MERGE_PATCH_KEYS[position]
dict1 = list_to_dict(list1, patch_merge_key, position)
dict2 = list_to_dict(list2, patch_merge_key, position)
dict1_keys = set(dict1.keys())
dict2_keys = set(dict2.keys())
for key in dict1_keys - dict2_keys:
result[0].append(dict1[key])
for key in dict2_keys - dict1_keys:
result[1].append(dict2[key])
for key in dict1_keys & dict2_keys:
diff = recursive_diff(dict1[key], dict2[key], position)
if diff:
# reinsert patch merge key to relate changes in other keys to
# a specific list element
diff[0].update({patch_merge_key: dict1[key][patch_merge_key]})
diff[1].update({patch_merge_key: dict2[key][patch_merge_key]})
result[0].append(diff[0])
result[1].append(diff[1])
if result[0] or result[1]:
return result
elif list1 != list2:
return (list1, list2)
return None


def recursive_diff(dict1, dict2, position=None):
if not position:
if 'kind' in dict1 and dict1.get('kind') == dict2.get('kind'):
position = dict1['kind']
left = dict((k, v) for (k, v) in dict1.items() if k not in dict2)
right = dict((k, v) for (k, v) in dict2.items() if k not in dict1)
for k in (set(dict1.keys()) & set(dict2.keys())):
if position:
this_position = "%s.%s" % (position, k)
if isinstance(dict1[k], dict) and isinstance(dict2[k], dict):
result = recursive_diff(dict1[k], dict2[k], this_position)
if result:
left[k] = result[0]
right[k] = result[1]
elif isinstance(dict1[k], list) and isinstance(dict2[k], list):
result = recursive_list_diff(dict1[k], dict2[k], this_position)
if result:
left[k] = result[0]
right[k] = result[1]
elif dict1[k] != dict2[k]:
left[k] = dict1[k]
right[k] = dict2[k]
if left or right:
return left, right
else:
return None


def get_deletions(last_applied, desired):
patch = {}
for k, last_applied_value in last_applied.items():
Expand Down
4 changes: 1 addition & 3 deletions python-openshift.spec
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ BuildRequires: python-setuptools
BuildRequires: git

Requires: python2
Requires: python2-dictdiffer
Requires: python2-kubernetes
Requires: python2-string_utils
Requires: python-requests
Expand All @@ -55,7 +54,6 @@ BuildRequires: %{py3}-setuptools
BuildRequires: git

Requires: %{py3}
Requires: %{py3}-dictdiffer
Requires: %{py3}-kubernetes
Requires: %{py3}-string_utils
Requires: %{py3}-requests
Expand Down Expand Up @@ -92,7 +90,7 @@ Python client for the OpenShift API
#the requirements are also done in an non-backwards compatible way
%if 0%{?rhel}
sed -i -e "s/find_packages(include='openshift.*')/['openshift', 'openshift.dynamic', 'openshift.helper']/g" setup.py
sed -i -e '30s/^/REQUIRES = [\n "dictdiffer",\n "jinja2",\n "kubernetes",\n "setuptools",\n "six",\n "ruamel.yaml",\n "python-string-utils",\n]\n/g' setup.py
sed -i -e '30s/^/REQUIRES = [\n "jinja2",\n "kubernetes",\n "setuptools",\n "six",\n "ruamel.yaml",\n "python-string-utils",\n]\n/g' setup.py
sed -i -e "s/extract_requirements('requirements.txt')/REQUIRES/g" setup.py
#sed -i -e '14,21d' setup.py
%endif
Expand Down
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
dictdiffer
jinja2
kubernetes ~= 11.0.0
python-string-utils
Expand Down
1 change: 0 additions & 1 deletion test-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,3 @@ pytest
pytest-bdd
pytest-cov
PyYAML
dictdiffer
69 changes: 69 additions & 0 deletions test/unit/test_diff.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from openshift.dynamic.apply import recursive_diff

tests = [
dict(
before = dict(
kind="Service",
metadata=dict(name="foo"),
spec=dict(ports=[dict(port=8080, name="http")])
),
after = dict(
kind="Service",
metadata=dict(name="foo"),
spec=dict(ports=[dict(port=8080, name="http")])
),
expected = None
),
dict(
before = dict(
kind="Service",
metadata=dict(name="foo"),
spec=dict(ports=[dict(port=8080, name="http")])
),
after = dict(
kind="Service",
metadata=dict(name="foo"),
spec=dict(ports=[dict(port=8081, name="http")])
),
expected = (
dict(spec=dict(ports=[dict(port=8080, name="http")])),
dict(spec=dict(ports=[dict(port=8081, name="http")]))
)
),
dict(
before = dict(
kind="Service",
metadata=dict(name="foo"),
spec=dict(ports=[dict(port=8080, name="http"), dict(port=8081, name="https")])
),
after = dict(
kind="Service",
metadata=dict(name="foo"),
spec=dict(ports=[dict(port=8081, name="https"), dict(port=8080, name="http")])
),
expected = None
),

dict(
before = dict(
kind="Pod",
metadata=dict(name="foo"),
spec=dict(containers=[dict(name="busybox", image="busybox",
env=[dict(name="hello", value="world"),
dict(name="another", value="next")])])
),
after = dict(
kind="Pod",
metadata=dict(name="foo"),
spec=dict(containers=[dict(name="busybox", image="busybox",
env=[dict(name="hello", value="everyone")])])
),
expected=(dict(spec=dict(containers=[dict(name="busybox", env=[dict(name="another", value="next"), dict(name="hello", value="world")])])),
dict(spec=dict(containers=[dict(name="busybox", env=[dict(name="hello", value="everyone")])])))
),
]


def test_diff():
for test in tests:
assert(recursive_diff(test['before'], test['after']) == test['expected'])