1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
#!/usr/bin/env python
# Copyright (c) Authors of Clover
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
import os
import redis
import subprocess
import sys
import yaml
#istioctl='$HOME/istio-0.6.0/bin/istioctl'
# The assumption is that istioctl is already in the user's path
ISTIOCTL='istioctl'
def cmd_exists(cmd):
return any(
os.access(os.path.join(path, cmd), os.X_OK)
for path in os.environ["PATH"].split(os.pathsep)
)
def load_route_rules(rr_yaml_path):
if not cmd_exists(ISTIOCTL):
print('%s does not exist in PATH, please export istioctl to PATH' % istioctl)
return False
# TODO(s3wong): load yaml and verify it does indeed contain route rule
cmd = ISTIOCTL + ' create -f ' + rr_yaml_path
output = subprocess.check_output(cmd, shell=True)
if not output:
print('Route rule creation failed: %s' % output)
return False
return True
def delete_route_rules(rr_yaml_path, namespace):
if not cmd_exists(ISTIOCTL):
print('%s does not exist in PATH, please export istioctl to PATH' % istioctl)
return False
# TODO(s3wong): load yaml and verify it does indeed contain route rule
cmd = ISTIOCTL + ' delete -f ' + rr_yaml_path + ' -n ' + namespace
output = subprocess.check_output(cmd, shell=True)
if not output or not 'Deleted' in output:
print('Route rule deletion failed: %s' % output)
return False
return True
def get_route_rules():
if not cmd_exists(ISTIOCTL):
print('%s does not exist in PATH, please export istioctl to PATH' % istioctl)
return None
cmd = ISTIOCTL + ' get routerules -o yaml'
output = subprocess.check_output(cmd, shell=True)
if not output:
print('No route rule configured')
return None
docs = []
for raw_doc in output.split('\n---'):
try:
docs.append(yaml.load(raw_doc))
except SyntaxError:
print('syntax error: %s' % raw_doc)
return docs
def parse_route_rules(routerules):
ret_list = []
if not routerules:
print('No routerules')
return ret_list
for routerule in routerules:
if not routerule or routerule == 'None': continue
print('routerule is %s' % routerule)
if routerule.get('kind') != 'RouteRule': continue
ret_rr_dict = {}
spec = routerule.get('spec')
if not spec: continue
ret_rr_dict['service'] = spec.get('destination').get('name')
ret_rr_dict['rules'] = spec.get('route')
ret_list.append(ret_rr_dict)
return ret_list
def _derive_key_from_test_id(test_id):
return 'route-rules-' + str(test_id)
def set_route_rules(test_id):
r = redis.StrictRedis(host='localhost', port=6379, db=0)
key = _derive_key_from_test_id(test_id)
rr = get_route_rules()
r.set(key, rr)
def fetch_route_rules(test_id):
r = redis.StrictRedis(host='localhost', port=6379, db=0)
key = _derive_key_from_test_id(test_id)
rr = r.get(key)
return yaml.load(rr)
'''
The format of result_dict is expected to be:
{
'service': <service name>,
<version string 1>: <integer representation of version string 1 occurrances during test>,
<version string 2>: <integer representation of version string 2 occurrances during test>,
...
}
'''
def validate_weighted_route_rules(result_dict, test_id=None):
print('validate_weighted_route_rules: test id %s' % test_id)
svc_name = result_dict.get('service')
if not test_id:
rr_list = parse_route_rules(get_route_rules())
else:
rr_list = parse_route_rules(fetch_route_rules(test_id))
errors = []
ret = True
for rr in rr_list:
route_rules = rr.get('rules')
if not route_rules:
break
for rule in route_rules:
version = rule.get('labels').get('version')
weight = rule.get('weight')
if not weight: weight = 1
if abs(weight - result_dict[version]) > 10:
err = 'svc %s version %s expected to get %d, but got %d' % (svc_name, version, weight, result_dict[version])
ret = False
else:
err = 'svc %s version %s expected to get %d, got %d. Validation succeeded' % (svc_name, version, weight, result_dict[version])
errors.append(err)
return ret, errors
|