26
CloudSpec = namedtuple('CloudSpec', ['label', 'name', 'config', 'exception'])
28
class CloudMismatch(JujuAssertionError):
29
"""The clouds did not match in some way."""
32
super(CloudMismatch, self).__init__('Cloud mismatch')
35
class NameMismatch(JujuAssertionError):
36
"""The cloud names did not match."""
39
super(NameMismatch, self).__init__('Name mismatch')
42
class NotRaised(Exception):
43
"""An expected exception was not raised."""
46
msg = 'Expected exception not raised: {}'.format(
48
super(NotRaised, self).__init__(msg)
51
CloudSpec = namedtuple('CloudSpec', [
52
'label', 'name', 'config', 'exception', 'xfail_bug'])
55
def cloud_spec(label, name, config, exception=None, xfail_bug=None):
56
"""Generate a CloudSpec, with defaults.
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
65
return CloudSpec(label, name, config, exception, xfail_bug)
68
def xfail(spec, bug, xfail_exception):
69
"""Return a variant of a CloudSpec that is expected to fail.
71
Wrapping the original spec improves maintainability, because the xfail can
72
be removed to restore the original value.
74
return CloudSpec(spec.label, spec.name, spec.config, xfail_exception, bug)
29
77
def assess_cloud(client, cloud_name, example_cloud):
78
"""Assess interactively adding a cloud.
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.
30
85
clouds = client.env.read_clouds()
31
86
if len(clouds['clouds']) > 0:
32
87
raise AssertionError('Clouds already present!')
35
90
if len(clouds['clouds']) == 0:
36
91
raise JujuAssertionError('Clouds missing!')
37
92
if clouds['clouds'].keys() != [cloud_name]:
38
raise JujuAssertionError('Name mismatch')
39
94
if clouds['clouds'][cloud_name] != example_cloud:
40
95
sys.stderr.write('\nExpected:\n')
41
96
yaml.dump(example_cloud, sys.stderr)
42
97
sys.stderr.write('\nActual:\n')
43
98
yaml.dump(clouds['clouds'][cloud_name], sys.stderr)
44
raise JujuAssertionError('Cloud mismatch')
47
def iter_clouds(clouds):
48
yield CloudSpec('bogus-type', 'bogus-type', {'type': 'bogus'},
49
exception=TypeNotAccepted)
50
for cloud_name, cloud in clouds.items():
51
yield CloudSpec(cloud_name, cloud_name, cloud, exception=None)
53
for cloud_name, cloud in clouds.items():
54
yield CloudSpec('long-name-{}'.format(cloud_name), 'A' * 4096, cloud,
56
yield CloudSpec('invalid-name-{}'.format(cloud_name), 'invalid/name',
57
cloud, exception=NameNotAccepted)
102
def iter_clouds(clouds, endpoint_validation):
103
"""Iterate through CloudSpecs."""
104
yield cloud_spec('bogus-type', 'bogus-type', {'type': 'bogus'},
105
exception=TypeNotAccepted)
106
for cloud_name, cloud in clouds.items():
107
spec = cloud_spec(cloud_name, cloud_name, cloud)
108
if cloud['type'] == 'manual' and endpoint_validation:
109
spec = xfail(spec, 1649721, InvalidEndpoint)
112
for cloud_name, cloud in clouds.items():
113
spec = xfail(cloud_spec('long-name-{}'.format(cloud_name), 'A' * 4096,
114
cloud, NameNotAccepted), 1641970, NameMismatch)
115
if cloud['type'] == 'manual' and endpoint_validation:
116
spec = xfail(spec, 1649721, InvalidEndpoint)
119
cloud_spec('invalid-name-{}'.format(cloud_name), 'invalid/name',
120
cloud, NameNotAccepted), 1641981, None)
121
if cloud['type'] == 'manual' and endpoint_validation:
122
spec = xfail(spec, 1649721, InvalidEndpoint)
59
125
if cloud['type'] not in ('maas', 'manual', 'vsphere'):
60
126
variant = deepcopy(cloud)
61
127
variant_name = 'bogus-auth-{}'.format(cloud_name)
62
128
variant['auth-types'] = ['asdf']
63
yield CloudSpec(variant_name, cloud_name, variant,
129
yield cloud_spec(variant_name, cloud_name, variant,
66
132
if 'endpoint' in cloud:
67
133
variant = deepcopy(cloud)
70
136
for region in variant['regions'].values():
71
137
region['endpoint'] = variant['endpoint']
72
138
variant_name = 'long-endpoint-{}'.format(cloud_name)
73
yield CloudSpec(variant_name, cloud_name, variant, exception=None)
139
spec = cloud_spec(variant_name, cloud_name, variant,
141
if variant['type'] == 'vsphere' or not endpoint_validation:
142
spec = xfail(spec, 1641970, CloudMismatch)
75
145
for region_name in cloud.get('regions', {}).keys():
76
146
if cloud['type'] == 'vsphere':
80
150
region['endpoint'] = 'A' * 4096
81
151
variant_name = 'long-endpoint-{}-{}'.format(cloud_name,
83
yield CloudSpec(variant_name, cloud_name, variant,
87
def assess_all_clouds(client, clouds):
153
spec = cloud_spec(variant_name, cloud_name, variant,
155
if not endpoint_validation:
156
spec = xfail(spec, 1641970, CloudMismatch)
160
def assess_all_clouds(client, cloud_specs):
161
"""Test all the supplied cloud_specs and return the results.
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.
90
170
client.env.load_yaml()
91
for cloud_label, cloud_name, cloud, expected in iter_clouds(clouds):
92
sys.stdout.write('Testing {}.\n'.format(cloud_label))
171
for cloud_spec in cloud_specs:
172
sys.stdout.write('Testing {}.\n'.format(cloud_spec.label))
95
assess_cloud(client, cloud_name, cloud)
174
if cloud_spec.exception is None:
175
assess_cloud(client, cloud_spec.name, cloud_spec.config)
98
assess_cloud(client, cloud_name, cloud)
178
assess_cloud(client, cloud_spec.name, cloud_spec.config)
179
except cloud_spec.exception:
103
'Expected exception not raised: {}'.format(expected))
182
raise NotRaised(cloud_spec.exception)
104
183
except Exception as e:
105
184
logging.exception(e)
106
failed.add(cloud_label)
185
failed.add(cloud_spec.label)
108
succeeded.add(cloud_label)
187
if cloud_spec.xfail_bug is not None:
189
cloud_spec.xfail_bug, set()).add(cloud_spec.label)
191
succeeded.add(cloud_spec.label)
110
193
client.env.clouds = {'clouds': {}}
111
194
client.env.dump_yaml(client.env.juju_home, {})
112
return succeeded, failed
195
return succeeded, xfailed, failed
115
198
def write_status(status, tests):
132
215
args = parse_args()
133
216
juju_bin = args.juju_bin
134
version = EnvJujuClient.get_version(juju_bin)
217
version = ModelClient.get_version(juju_bin)
135
218
client_class = get_client_class(version)
136
219
if client_class.config_class is not JujuData:
137
220
logging.warn('This test does not support old jujus.')
138
221
with open(args.example_clouds) as f:
139
222
clouds = yaml.safe_load(f)['clouds']
223
endpoint_validation = bool(not re.match('2\.1[^\d]', version))
224
cloud_specs = iter_clouds(clouds, endpoint_validation)
140
225
with temp_dir() as juju_home:
141
226
env = JujuData('foo', config=None, juju_home=juju_home)
142
227
client = client_class(env, version, juju_bin)
143
succeeded, failed = assess_all_clouds(client, clouds)
228
succeeded, xfailed, failed = assess_all_clouds(client, cloud_specs)
144
229
write_status('Succeeded', succeeded)
230
for bug, failures in sorted(xfailed.items()):
231
write_status('Expected fail (bug #{})'.format(bug), failures)
145
232
write_status('Failed', failed)
146
233
if len(failed) > 0: