YAML 내용 비교하기 (using Python)
`vim -d old_file new_file` 또는 diff 등 명령으로 파일에서 변경된 내용을 확인할 수 있지만,
이렇게 비교를 하면, 내용이 동일하더라도 특정 내용의 행이 위/아래로 이동한 경우에는 다른 파일로 간주된다.
즉, 내용을 비교하고 싶은데 문서의 Format이 변경된 것 때문에 다른 파일로 인식되는 문제가 생긴다.
따라서 서로 다른 버전의 YAML file을 비교할 때는 두 파일을 Parsing해서 구조체에 넣고, 특정 항목이 추가되었는지 또는 삭제되었는지 또는 변경되었는지를 일일히 확인하는 것이 제일 정확한다.
Istio project의 Utility code를 살펴보니, 이런 기능을 하는 python code가 있어서 인용해봤다.
https://github.com/istio/istio/blob/master/bin/diff_yaml.py
GitHub - istio/istio: Connect, secure, control, and observe services.
Connect, secure, control, and observe services. Contribute to istio/istio development by creating an account on GitHub.
github.com
혹시 GitHub에서 Folder 구조가 바뀌거나 diff_yaml.py 도구가 제거될 것을 우려해서 여기 blog에 흔적을 남긴다.
diff_yaml.py (source code 열람)
#!/usr/bin/env python
from __future__ import print_function
import argparse
import datadiff
import sys
import yaml
def by_resource_name(res):
if res is None:
return ""
return "{}::{}::{}" .format(res['apiVersion' ],
res['kind' ],
res['metadata' ]['name' ])
def keydiff(k0, k1):
k0s = set (k0)
k1s = set (k1)
added = k1s - k0s
removed = k0s - k1s
common = k0s.intersection(k1s)
return added, removed, common
def drop_keys(res, k1, k2):
if k2 in res[k1]:
del res[k1][k2]
def normalize_configmap(res):
try:
if res['kind' ] != "ConfigMap" :
return res
data = res['data' ]
for k in data:
try:
op = yaml.safe_load_all(data[k])
data[k] = list(op)
except yaml.YAMLError as ex:
print (ex)
return res
except KeyError as ke:
if 'kind' in str(ke) or 'data' in str(ke):
return res
raise
def normalize_ports(res):
try:
spec = res["spec" ]
if spec is None:
return res
ports = sorted(spec['ports' ], key=lambda x: x["port" ])
spec['ports' ] = ports
return res
except KeyError as ke:
if 'spec' in str(ke) or 'ports' in str(ke) or 'port' in str(ke):
return res
raise
def normalize_res(res, args):
if not res:
return res
if args.ignore_labels:
drop_keys(res, "metadata" , "labels" )
if args.ignore_namespace:
drop_keys(res, "metadata" , "namespace" )
res = normalize_ports(res)
res = normalize_configmap(res)
return res
def normalize(rl, args):
for i in range(len(rl)):
rl[i] = normalize_res(rl[i], args)
return rl
def compare(args):
j0 = normalize(list(yaml.safe_load_all(open(args.orig))), args)
j1 = normalize(list(yaml.safe_load_all(open(args.new))), args)
q0 = {by_resource_name(res): res for res in j0 if res is not None}
q1 = {by_resource_name(res): res for res in j1 if res is not None}
added, removed, common = keydiff(q0.keys(), q1.keys())
changed = 0
for k in sorted(common):
if q0[k] != q1[k]:
changed += 1
print ("## +++ " , args.new)
print ("## --- " , args.orig)
print ("## Added:" , len(added))
print ("## Removed:" , len(removed))
print ("## Updated:" , changed)
print ("## Unchanged:" , len(common) - changed)
for k in sorted(added):
print ("+" , k)
for k in sorted(removed):
print ("-" , k)
print ("##" , "*" * 25)
for k in sorted(common):
if q0[k] != q1[k]:
print ("## " , k)
s0 = yaml.safe_dump(q0[k], default_flow_style=False, indent=2)
s1 = yaml.safe_dump(q1[k], default_flow_style=False, indent=2)
print (datadiff.diff(s0, s1, fromfile=args.orig, tofile=args.new))
return changed + len(added) + len(removed)
def main(args):
return compare(args)
def get_parser():
parser = argparse.ArgumentParser(
description="Compare kubernetes yaml files" )
parser.add_argument("orig" )
parser.add_argument("new" )
parser.add_argument("--ignore-namespace" , action="store_true" , default=False,
help ="Ignore namespace during comparison" )
parser.add_argument("--ignore-labels" , action="store_true" , default=False,
help ="Ignore resource labels during comparison" )
parser.add_argument("--ignore-annotations" , action="store_true" , default=False,
help ="Ignore annotations during comparison" )
return parser
if __name__ == "__main__" :
parser = get_parser()
args = parser.parse_args()
sys.exit(main(args))
diff_yaml.py 명령 따라하기
위 python code를 diff_yaml.py 파일명으로 저장하고, 아래와 같이 필요한 Python package를 설치하고, Run 한다.
$ pip install --upgrade pip
$ python -m pip install datadiff
$ ./diff_yaml.py /tmp/metallb-1.yaml /tmp/metallb-2.yaml
+ apps/v1::Deployment_Test::controller
- apps/v1::Deployment::controller
$
$ diff /tmp/metallb-1.yaml /tmp/metallb-2.yaml
3d2
< kind: Namespace
5d3
< name: metallb-system
8a7,8
> name: metallb-system
> kind: Namespace
352d351
< kind: Deployment
400a400
> kind: Deployment_Test
위 결과를 보면, 일반적인 diff와 내용이 다르다는 것을 알 수 있다.
가장 큰 차이점은 diff_yaml.py는 수정된 Line과 Column을 출력하지 않는다. 왜냐하면, 내용 자체의 변경 여부가 관심사이지 어떤 내용이 다른 행으로 이동했는지 또는 삭제, 추가되었는지 중요하지 않기 때문이다.
JSON 내용 비교하기 (using Python)
JSON 내용을 비교하는 CLI 도구는 아래 Web Docs를 참고하길 ~~~
GitHub - xlwings/jsondiff: Diff JSON and JSON-like structures in Python
Diff JSON and JSON-like structures in Python. Contribute to xlwings/jsondiff development by creating an account on GitHub.
github.com
json-diff
Generates diff between two JSON files
pypi.org