1711.6.4
by Aaron Bentley
Initial assess_add_cloud test. |
1 |
#!/usr/bin/env python
|
2 |
||
3 |
from argparse import ArgumentParser |
|
1727.2.16
by Aaron Bentley
Test iter_clouds. |
4 |
from collections import namedtuple |
1727.2.13
by Aaron Bentley
More fixes. |
5 |
from copy import deepcopy |
1711.6.5
by Aaron Bentley
Use external clouds.yal file for assessment. |
6 |
import logging |
1812.2.1
by Aaron Bentley
Assume no endpoint validation for 2.1. |
7 |
import re |
1711.6.5
by Aaron Bentley
Use external clouds.yal file for assessment. |
8 |
import sys |
1711.6.4
by Aaron Bentley
Initial assess_add_cloud test. |
9 |
|
1711.6.5
by Aaron Bentley
Use external clouds.yal file for assessment. |
10 |
import yaml |
1711.6.4
by Aaron Bentley
Initial assess_add_cloud test. |
11 |
|
12 |
from jujupy import ( |
|
1727.2.9
by Aaron Bentley
Expect AuthNotAccepted where auth is bogus. |
13 |
AuthNotAccepted, |
1850.1.16
by Aaron Bentley
Switch assess_add_cloud to ModelClient name. |
14 |
ModelClient, |
1711.6.4
by Aaron Bentley
Initial assess_add_cloud test. |
15 |
get_client_class, |
1803.1.1
by Aaron Bentley
Fix add-cloud-interactive. |
16 |
InvalidEndpoint, |
1711.6.4
by Aaron Bentley
Initial assess_add_cloud test. |
17 |
JujuData, |
1727.2.18
by Aaron Bentley
Check invalid name handling. |
18 |
NameNotAccepted, |
1727.2.17
by Aaron Bentley
Add bogus type testing. |
19 |
TypeNotAccepted, |
1711.6.4
by Aaron Bentley
Initial assess_add_cloud test. |
20 |
)
|
21 |
from utility import ( |
|
22 |
add_arg_juju_bin, |
|
1711.6.5
by Aaron Bentley
Use external clouds.yal file for assessment. |
23 |
JujuAssertionError, |
1711.6.4
by Aaron Bentley
Initial assess_add_cloud test. |
24 |
temp_dir, |
25 |
)
|
|
26 |
||
27 |
||
1794.2.1
by Aaron Bentley
Add expected-failure support. |
28 |
class CloudMismatch(JujuAssertionError): |
1794.2.2
by Aaron Bentley
Clean-up and docs. |
29 |
"""The clouds did not match in some way."""
|
1794.2.1
by Aaron Bentley
Add expected-failure support. |
30 |
|
31 |
def __init__(self): |
|
32 |
super(CloudMismatch, self).__init__('Cloud mismatch') |
|
33 |
||
34 |
||
35 |
class NameMismatch(JujuAssertionError): |
|
1794.2.2
by Aaron Bentley
Clean-up and docs. |
36 |
"""The cloud names did not match."""
|
1794.2.1
by Aaron Bentley
Add expected-failure support. |
37 |
|
38 |
def __init__(self): |
|
39 |
super(NameMismatch, self).__init__('Name mismatch') |
|
40 |
||
41 |
||
42 |
class NotRaised(Exception): |
|
1794.2.2
by Aaron Bentley
Clean-up and docs. |
43 |
"""An expected exception was not raised."""
|
1794.2.1
by Aaron Bentley
Add expected-failure support. |
44 |
|
45 |
def __init__(self): |
|
46 |
msg = 'Expected exception not raised: {}'.format( |
|
47 |
cloud_spec.exception) |
|
48 |
super(NotRaised, self).__init__(msg) |
|
49 |
||
50 |
||
1794.2.2
by Aaron Bentley
Clean-up and docs. |
51 |
CloudSpec = namedtuple('CloudSpec', [ |
52 |
'label', 'name', 'config', 'exception', 'xfail_bug']) |
|
1794.2.1
by Aaron Bentley
Add expected-failure support. |
53 |
|
54 |
||
55 |
def cloud_spec(label, name, config, exception=None, xfail_bug=None): |
|
1794.2.2
by Aaron Bentley
Clean-up and docs. |
56 |
"""Generate a CloudSpec, with defaults.
|
57 |
||
58 |
:param label: The label to display in test results.
|
|
59 |
:param name: The name to use for the cloud.
|
|
60 |
:param config: The cloud-config.
|
|
61 |
:param exception: The exception that is expected to be raised (if any).
|
|
62 |
:param xfail_bug: If this CloudSpec represents an expected failure, the
|
|
63 |
bug number.
|
|
64 |
"""
|
|
1794.2.1
by Aaron Bentley
Add expected-failure support. |
65 |
return CloudSpec(label, name, config, exception, xfail_bug) |
66 |
||
67 |
||
68 |
def xfail(spec, bug, xfail_exception): |
|
1794.2.3
by Aaron Bentley
Implement expected failures and get tests passing. |
69 |
"""Return a variant of a CloudSpec that is expected to fail.
|
70 |
||
71 |
Wrapping the original spec improves maintainability, because the xfail can
|
|
72 |
be removed to restore the original value.
|
|
73 |
"""
|
|
1794.2.1
by Aaron Bentley
Add expected-failure support. |
74 |
return CloudSpec(spec.label, spec.name, spec.config, xfail_exception, bug) |
1727.2.16
by Aaron Bentley
Test iter_clouds. |
75 |
|
76 |
||
1727.2.3
by Aaron Bentley
Test long cloud names. |
77 |
def assess_cloud(client, cloud_name, example_cloud): |
1794.2.2
by Aaron Bentley
Clean-up and docs. |
78 |
"""Assess interactively adding a cloud.
|
79 |
||
80 |
Will raise an exception
|
|
81 |
- If no clouds are present after interactive add-cloud.
|
|
82 |
- If the resulting cloud name doesn't match the supplied cloud-name.
|
|
83 |
- If the cloud data doesn't match the supplied cloud data.
|
|
84 |
"""
|
|
1711.6.10
by Aaron Bentley
Extract and use JujuData.read_clouds. |
85 |
clouds = client.env.read_clouds() |
1711.6.9
by Aaron Bentley
Extract cloud-loading to a separate function. |
86 |
if len(clouds['clouds']) > 0: |
1711.6.4
by Aaron Bentley
Initial assess_add_cloud test. |
87 |
raise AssertionError('Clouds already present!') |
1727.2.8
by Aaron Bentley
Merged aci-updates into interative-add-cloud-error. |
88 |
client.add_cloud_interactive(cloud_name, example_cloud) |
1711.6.10
by Aaron Bentley
Extract and use JujuData.read_clouds. |
89 |
clouds = client.env.read_clouds() |
1711.6.9
by Aaron Bentley
Extract cloud-loading to a separate function. |
90 |
if len(clouds['clouds']) == 0: |
1711.6.5
by Aaron Bentley
Use external clouds.yal file for assessment. |
91 |
raise JujuAssertionError('Clouds missing!') |
1727.2.3
by Aaron Bentley
Test long cloud names. |
92 |
if clouds['clouds'].keys() != [cloud_name]: |
1794.2.1
by Aaron Bentley
Add expected-failure support. |
93 |
raise NameMismatch() |
1727.2.3
by Aaron Bentley
Test long cloud names. |
94 |
if clouds['clouds'][cloud_name] != example_cloud: |
1711.6.5
by Aaron Bentley
Use external clouds.yal file for assessment. |
95 |
sys.stderr.write('\nExpected:\n') |
96 |
yaml.dump(example_cloud, sys.stderr) |
|
97 |
sys.stderr.write('\nActual:\n') |
|
1727.2.3
by Aaron Bentley
Test long cloud names. |
98 |
yaml.dump(clouds['clouds'][cloud_name], sys.stderr) |
1794.2.1
by Aaron Bentley
Add expected-failure support. |
99 |
raise CloudMismatch() |
1711.6.4
by Aaron Bentley
Initial assess_add_cloud test. |
100 |
|
101 |
||
1812.2.1
by Aaron Bentley
Assume no endpoint validation for 2.1. |
102 |
def iter_clouds(clouds, endpoint_validation): |
1794.2.2
by Aaron Bentley
Clean-up and docs. |
103 |
"""Iterate through CloudSpecs."""
|
1794.2.1
by Aaron Bentley
Add expected-failure support. |
104 |
yield cloud_spec('bogus-type', 'bogus-type', {'type': 'bogus'}, |
1794.2.2
by Aaron Bentley
Clean-up and docs. |
105 |
exception=TypeNotAccepted) |
1727.2.1
by Aaron Bentley
Test with 4096-char endpoints. |
106 |
for cloud_name, cloud in clouds.items(): |
1808.2.1
by Aaron Bentley
Update with expected endpoint failures for manual. |
107 |
spec = cloud_spec(cloud_name, cloud_name, cloud) |
1812.2.1
by Aaron Bentley
Assume no endpoint validation for 2.1. |
108 |
if cloud['type'] == 'manual' and endpoint_validation: |
1808.2.1
by Aaron Bentley
Update with expected endpoint failures for manual. |
109 |
spec = xfail(spec, 1649721, InvalidEndpoint) |
110 |
yield spec |
|
1727.2.1
by Aaron Bentley
Test with 4096-char endpoints. |
111 |
|
112 |
for cloud_name, cloud in clouds.items(): |
|
1808.2.1
by Aaron Bentley
Update with expected endpoint failures for manual. |
113 |
spec = xfail(cloud_spec('long-name-{}'.format(cloud_name), 'A' * 4096, |
114 |
cloud, NameNotAccepted), 1641970, NameMismatch) |
|
1812.2.1
by Aaron Bentley
Assume no endpoint validation for 2.1. |
115 |
if cloud['type'] == 'manual' and endpoint_validation: |
1808.2.1
by Aaron Bentley
Update with expected endpoint failures for manual. |
116 |
spec = xfail(spec, 1649721, InvalidEndpoint) |
117 |
yield spec |
|
118 |
spec = xfail( |
|
1794.2.1
by Aaron Bentley
Add expected-failure support. |
119 |
cloud_spec('invalid-name-{}'.format(cloud_name), 'invalid/name', |
1794.2.7
by Aaron Bentley
Fix bug number. |
120 |
cloud, NameNotAccepted), 1641981, None) |
1812.2.1
by Aaron Bentley
Assume no endpoint validation for 2.1. |
121 |
if cloud['type'] == 'manual' and endpoint_validation: |
1808.2.1
by Aaron Bentley
Update with expected endpoint failures for manual. |
122 |
spec = xfail(spec, 1649721, InvalidEndpoint) |
123 |
yield spec |
|
1727.2.9
by Aaron Bentley
Expect AuthNotAccepted where auth is bogus. |
124 |
|
125 |
if cloud['type'] not in ('maas', 'manual', 'vsphere'): |
|
126 |
variant = deepcopy(cloud) |
|
127 |
variant_name = 'bogus-auth-{}'.format(cloud_name) |
|
128 |
variant['auth-types'] = ['asdf'] |
|
1794.2.1
by Aaron Bentley
Add expected-failure support. |
129 |
yield cloud_spec(variant_name, cloud_name, variant, |
1794.2.2
by Aaron Bentley
Clean-up and docs. |
130 |
AuthNotAccepted) |
1727.2.3
by Aaron Bentley
Test long cloud names. |
131 |
|
1727.2.4
by Aaron Bentley
Skip only endpoint tests due to lack of endpoint. |
132 |
if 'endpoint' in cloud: |
133 |
variant = deepcopy(cloud) |
|
134 |
variant['endpoint'] = 'A' * 4096 |
|
135 |
if variant['type'] == 'vsphere': |
|
136 |
for region in variant['regions'].values(): |
|
137 |
region['endpoint'] = variant['endpoint'] |
|
138 |
variant_name = 'long-endpoint-{}'.format(cloud_name) |
|
1803.1.1
by Aaron Bentley
Fix add-cloud-interactive. |
139 |
spec = cloud_spec(variant_name, cloud_name, variant, |
140 |
InvalidEndpoint) |
|
1873.1.2
by Aaron Bentley
add-cloud expects vsphere validation after 2.1. |
141 |
if not endpoint_validation: |
1803.1.1
by Aaron Bentley
Fix add-cloud-interactive. |
142 |
spec = xfail(spec, 1641970, CloudMismatch) |
143 |
yield spec |
|
1727.2.4
by Aaron Bentley
Skip only endpoint tests due to lack of endpoint. |
144 |
|
1727.2.16
by Aaron Bentley
Test iter_clouds. |
145 |
for region_name in cloud.get('regions', {}).keys(): |
146 |
if cloud['type'] == 'vsphere': |
|
147 |
continue
|
|
148 |
variant = deepcopy(cloud) |
|
149 |
region = variant['regions'][region_name] |
|
150 |
region['endpoint'] = 'A' * 4096 |
|
151 |
variant_name = 'long-endpoint-{}-{}'.format(cloud_name, |
|
152 |
region_name) |
|
1812.2.1
by Aaron Bentley
Assume no endpoint validation for 2.1. |
153 |
spec = cloud_spec(variant_name, cloud_name, variant, |
154 |
InvalidEndpoint) |
|
155 |
if not endpoint_validation: |
|
156 |
spec = xfail(spec, 1641970, CloudMismatch) |
|
157 |
yield spec |
|
1794.2.1
by Aaron Bentley
Add expected-failure support. |
158 |
|
1727.2.1
by Aaron Bentley
Test with 4096-char endpoints. |
159 |
|
1794.2.5
by Aaron Bentley
Make assess_all_clouds more testable, test expected failures. |
160 |
def assess_all_clouds(client, cloud_specs): |
1794.2.6
by Aaron Bentley
Cleanup. |
161 |
"""Test all the supplied cloud_specs and return the results.
|
162 |
||
163 |
Returns a tuple of succeeded, expected_failed, and failed.
|
|
164 |
succeeded and failed are sets of cloud labels. expected_failed is a dict
|
|
165 |
linking a given bug to its associated failures.
|
|
166 |
"""
|
|
1711.6.7
by Aaron Bentley
Extract assessment to assess_all_clouds. |
167 |
succeeded = set() |
1794.2.5
by Aaron Bentley
Make assess_all_clouds more testable, test expected failures. |
168 |
xfailed = {} |
1711.6.7
by Aaron Bentley
Extract assessment to assess_all_clouds. |
169 |
failed = set() |
1711.6.9
by Aaron Bentley
Extract cloud-loading to a separate function. |
170 |
client.env.load_yaml() |
1794.2.5
by Aaron Bentley
Make assess_all_clouds more testable, test expected failures. |
171 |
for cloud_spec in cloud_specs: |
1794.2.1
by Aaron Bentley
Add expected-failure support. |
172 |
sys.stdout.write('Testing {}.\n'.format(cloud_spec.label)) |
1711.6.7
by Aaron Bentley
Extract assessment to assess_all_clouds. |
173 |
try: |
1794.2.1
by Aaron Bentley
Add expected-failure support. |
174 |
if cloud_spec.exception is None: |
175 |
assess_cloud(client, cloud_spec.name, cloud_spec.config) |
|
1727.2.9
by Aaron Bentley
Expect AuthNotAccepted where auth is bogus. |
176 |
else: |
177 |
try: |
|
1794.2.1
by Aaron Bentley
Add expected-failure support. |
178 |
assess_cloud(client, cloud_spec.name, cloud_spec.config) |
179 |
except cloud_spec.exception: |
|
1727.2.9
by Aaron Bentley
Expect AuthNotAccepted where auth is bogus. |
180 |
pass
|
181 |
else: |
|
1794.2.1
by Aaron Bentley
Add expected-failure support. |
182 |
raise NotRaised(cloud_spec.exception) |
1711.6.7
by Aaron Bentley
Extract assessment to assess_all_clouds. |
183 |
except Exception as e: |
184 |
logging.exception(e) |
|
1794.2.1
by Aaron Bentley
Add expected-failure support. |
185 |
failed.add(cloud_spec.label) |
1711.6.7
by Aaron Bentley
Extract assessment to assess_all_clouds. |
186 |
else: |
1794.2.1
by Aaron Bentley
Add expected-failure support. |
187 |
if cloud_spec.xfail_bug is not None: |
1794.2.5
by Aaron Bentley
Make assess_all_clouds more testable, test expected failures. |
188 |
xfailed.setdefault( |
1794.2.1
by Aaron Bentley
Add expected-failure support. |
189 |
cloud_spec.xfail_bug, set()).add(cloud_spec.label) |
190 |
else: |
|
191 |
succeeded.add(cloud_spec.label) |
|
1711.6.7
by Aaron Bentley
Extract assessment to assess_all_clouds. |
192 |
finally: |
193 |
client.env.clouds = {'clouds': {}} |
|
194 |
client.env.dump_yaml(client.env.juju_home, {}) |
|
1794.2.5
by Aaron Bentley
Make assess_all_clouds more testable, test expected failures. |
195 |
return succeeded, xfailed, failed |
1711.6.7
by Aaron Bentley
Extract assessment to assess_all_clouds. |
196 |
|
197 |
||
1711.6.8
by Aaron Bentley
Fix empty list handling. |
198 |
def write_status(status, tests): |
199 |
if len(tests) == 0: |
|
200 |
test_str = 'none' |
|
201 |
else: |
|
202 |
test_str = ', '.join(sorted(tests)) |
|
203 |
sys.stdout.write('{}: {}\n'.format(status, test_str)) |
|
204 |
||
205 |
||
1711.6.4
by Aaron Bentley
Initial assess_add_cloud test. |
206 |
def parse_args(): |
207 |
parser = ArgumentParser() |
|
1711.6.5
by Aaron Bentley
Use external clouds.yal file for assessment. |
208 |
parser.add_argument('example_clouds', |
209 |
help='A clouds.yaml file to use for testing.') |
|
1711.6.4
by Aaron Bentley
Initial assess_add_cloud test. |
210 |
add_arg_juju_bin(parser) |
211 |
return parser.parse_args() |
|
212 |
||
213 |
||
214 |
def main(): |
|
1711.6.5
by Aaron Bentley
Use external clouds.yal file for assessment. |
215 |
args = parse_args() |
216 |
juju_bin = args.juju_bin |
|
1850.1.16
by Aaron Bentley
Switch assess_add_cloud to ModelClient name. |
217 |
version = ModelClient.get_version(juju_bin) |
1711.6.4
by Aaron Bentley
Initial assess_add_cloud test. |
218 |
client_class = get_client_class(version) |
219 |
if client_class.config_class is not JujuData: |
|
220 |
logging.warn('This test does not support old jujus.') |
|
1711.6.5
by Aaron Bentley
Use external clouds.yal file for assessment. |
221 |
with open(args.example_clouds) as f: |
222 |
clouds = yaml.safe_load(f)['clouds'] |
|
1812.2.1
by Aaron Bentley
Assume no endpoint validation for 2.1. |
223 |
endpoint_validation = bool(not re.match('2\.1[^\d]', version)) |
224 |
cloud_specs = iter_clouds(clouds, endpoint_validation) |
|
1711.6.4
by Aaron Bentley
Initial assess_add_cloud test. |
225 |
with temp_dir() as juju_home: |
226 |
env = JujuData('foo', config=None, juju_home=juju_home) |
|
227 |
client = client_class(env, version, juju_bin) |
|
1794.2.5
by Aaron Bentley
Make assess_all_clouds more testable, test expected failures. |
228 |
succeeded, xfailed, failed = assess_all_clouds(client, cloud_specs) |
1711.6.8
by Aaron Bentley
Fix empty list handling. |
229 |
write_status('Succeeded', succeeded) |
1794.2.5
by Aaron Bentley
Make assess_all_clouds more testable, test expected failures. |
230 |
for bug, failures in sorted(xfailed.items()): |
1794.2.1
by Aaron Bentley
Add expected-failure support. |
231 |
write_status('Expected fail (bug #{})'.format(bug), failures) |
1711.6.8
by Aaron Bentley
Fix empty list handling. |
232 |
write_status('Failed', failed) |
1739
by Aaron Bentley
Use exit status 1 on error. |
233 |
if len(failed) > 0: |
234 |
return 1 |
|
235 |
return 0 |
|
1711.6.4
by Aaron Bentley
Initial assess_add_cloud test. |
236 |
|
237 |
||
238 |
if __name__ == '__main__': |
|
1739
by Aaron Bentley
Use exit status 1 on error. |
239 |
sys.exit(main()) |