Skip to content

Commit 494e5d8

Browse files
authored
Check if Kibana files are up-to-date (#1168)
* Check if Kibana files are up-to-date APM dashboards and index pattern are stored in Kibana. Add test to ensure that those files are kept up-to-date. implements #1042
1 parent 11b38a3 commit 494e5d8

File tree

3 files changed

+138
-0
lines changed

3 files changed

+138
-0
lines changed

Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,8 @@ release-manager-snapshot:
9393
.PHONY: release-manager-release
9494
release-manager-release:
9595
./_beats/dev-tools/run_with_go_ver $(MAKE) release
96+
97+
.PHONY: are-kibana-objects-updated
98+
are-kibana-objects-updated: python-env
99+
@$(MAKE) clean update
100+
@$(PYTHON_ENV)/bin/python ./script/are_kibana_saved_objects_updated.py ${BEATS_VERSION}

_beats/libbeat/tests/system/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@ texttable==0.9.1
2828
urllib3==1.22
2929
websocket-client==0.47.0
3030
parameterized==0.6.1
31+
jsondiff
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
#!/usr/bin/env python
2+
3+
from __future__ import print_function
4+
5+
import argparse
6+
7+
import requests
8+
import os
9+
import json
10+
import jsondiff
11+
import sys
12+
13+
14+
def fetch_kibana_file(repo_path, branch, file_name):
15+
path = "{}/{}/{}".format(repo_path, branch, file_name)
16+
rsp = requests.get(path)
17+
if rsp.status_code != 200:
18+
print("failed to query '{}'".format(path))
19+
return 1
20+
return rsp.json()
21+
22+
23+
def json_val(v1, v2):
24+
try:
25+
return (json.loads(v1), json.loads(v2))
26+
except:
27+
return (v1, v2)
28+
29+
30+
def find_key(item):
31+
if "id" in item:
32+
return "id"
33+
elif "name" in item:
34+
return "name"
35+
elif "type" in item:
36+
return "type"
37+
elif "query" in item:
38+
return "query"
39+
else:
40+
return ""
41+
42+
43+
def find_item(inp, key, val):
44+
for entry in inp:
45+
if not isinstance(entry, dict):
46+
return ""
47+
if key in entry and entry[key] == val:
48+
return entry
49+
return ""
50+
51+
52+
def build_key(k1, k2):
53+
if k1 == "":
54+
return k2
55+
if k2 == "":
56+
return k1
57+
return "{}.{}".format(k1, k2)
58+
59+
60+
def iterate(val_id, key, v1, v2):
61+
ret_val = 0
62+
if isinstance(v1, dict) and isinstance(v2, dict):
63+
for k, v in v1.items():
64+
ret_val = max(ret_val, iterate(val_id, build_key(key, k), *json_val(v, v2[k] if k in v2 else "")))
65+
elif isinstance(v1, list) and isinstance(v2, list):
66+
v1, v2 = json_val(v1, v2)
67+
# assumption: an array only contains items of same type
68+
if len(v1) > 0 and isinstance(v1[0], dict):
69+
for item in v1:
70+
qkey = find_key(item)
71+
if qkey == "":
72+
print("Script is missing type to compare {}".format(item))
73+
return 3
74+
75+
item2 = find_item(v2, qkey, item[qkey])
76+
ret_val = max(ret_val, iterate(val_id, build_key(key, "{}={}".format(qkey, item[qkey])), item, item2))
77+
else:
78+
v1, v2 = sorted(v1), sorted(v2)
79+
for item1, item2 in zip(v1, v2):
80+
ret_val = max(ret_val, iterate(val_id, key, *json_val(item1, item2)))
81+
else:
82+
d = jsondiff.diff(*json_val(v1, v2))
83+
if d:
84+
ret_val = 2
85+
print("Difference for id '{}' for key '{}'".format(val_id, key))
86+
try:
87+
print(json.dumps(d, indent=4))
88+
except:
89+
print(d)
90+
print("Value in APM Server: {}".format(v1))
91+
print("Value in Kibana: {}".format(v2))
92+
print("---")
93+
return ret_val
94+
95+
96+
def main():
97+
parser = argparse.ArgumentParser()
98+
parser.add_argument('branch')
99+
parser.add_argument('-D', '--dashboards', type=str,
100+
default='src/core_plugins/kibana/server/tutorials/apm/saved_objects/saved_objects.json',
101+
help='dashboards file path')
102+
parser.add_argument('-I', '--index-pattern', type=str,
103+
default='src/core_plugins/kibana/server/tutorials/apm/saved_objects/index_pattern.json',
104+
help='index-pattern file path')
105+
parser.add_argument('-P', '--repo-path', type=str,
106+
default='https://raw.githubusercontent.com/elastic/kibana',
107+
help='base repository path')
108+
args = parser.parse_args()
109+
exit_val = 0
110+
111+
print("---- Comparing Dashboards:")
112+
k = fetch_kibana_file(args.repo_path, args.branch, args.dashboards)
113+
with open(os.path.abspath(os.path.join('_meta', 'kibana.generated', '6', 'dashboard', 'apm-dashboards.json'))) as f:
114+
s = json.load(f)["objects"]
115+
for idx in range(len(k)):
116+
exit_val = max(exit_val, iterate(k[idx]["id"], "", s[idx], k[idx]))
117+
if exit_val == 0:
118+
print("up-to-date")
119+
120+
print("---- Comparing Index Pattern:")
121+
k = fetch_kibana_file(args.repo_path, args.branch, args.index_pattern)
122+
with open(os.path.abspath(os.path.join('_meta', 'kibana.generated', '6', 'index-pattern', 'apmserver.json'))) as f:
123+
s = json.load(f)["objects"][0]
124+
exit_val = max(exit_val, iterate(k["id"], "", s, k))
125+
if exit_val == 0:
126+
print("up-to-date")
127+
128+
return exit_val
129+
130+
131+
if __name__ == '__main__':
132+
sys.exit(main())

0 commit comments

Comments
 (0)