24
34
def _build_proof_response(response_data):
25
# Build up a root messages list of errors that can be used to help the
26
# client go through them all easily.
35
# Build up a root messages list of errors and warnings that can be used to
36
# help the client go through them all easily.
27
37
error_messages = []
28
39
if response_data['errors']:
29
40
for bundle in response_data['errors']:
30
prefix = bundle['name']
31
for service_name, service in bundle['services'].items():
33
msg = '%s: %s' % (prefix, err['message'])
42
for service_name, errors in bundle['services'].items():
44
msg = '{}/{}: {}'.format(
45
name, service_name, err['message'])
34
46
error_messages.append(msg)
36
48
for relation_error in bundle['relations']:
37
msg = '%s: %s' % (prefix, relation_error['message'])
49
msg = '{}: {}'.format(name, relation_error['message'])
38
50
error_messages.append(msg)
52
for service_name, warnings in bundle['warnings'].items():
53
for warning in warnings:
54
msg = '{}/{}: {}'.format(
55
name, service_name, warning['message'])
56
warning_messages.append(msg)
41
59
response_data['error_messages'].extend(error_messages)
62
response_data['warning_messages'].extend(warning_messages)
45
65
def _proof_bundles_charms_exist(db, bundle_info, services, series=None):
46
"""Verify the charms the bundle requests to deploy exist in the db."""
66
"""Verify the charms the bundle requests to deploy exist in the db.
68
Modifies the bundle_info dictionary in place.
49
72
# Verify we can find the charms at all.
56
79
if not charm_found:
58
81
charm_description.get_process(),
59
'Could not find charm for service: %s' % name)
82
'Could not find charm for service.')
61
84
found_charms[name] = Charm(charm_found)
62
85
except ProofError, exc:
63
86
# We could not find the charm. No other verification can
65
bundle_info['services'][name] = []
88
bundle_info['services'].setdefault(name, [])
66
89
bundle_info['services'][name].append({
67
90
'message': exc.msg,
68
91
'debug_info': exc.debug_info,
70
return found_charms, bundle_info
73
96
def _proof_bundles_relations(bundle_info, bundle_config, charms):
79
102
- Verify that if no specific interfaces are defined, we can find at least
80
103
one common interface between the two services.
105
Modifies the bundle_info dictionary in place.
83
107
deployment = CharmworldDeployment(bundle_config)
84
108
relations = deployment.get_relations()
89
113
# Begin the actual interface checks.
90
114
for e_a, e_b in relations:
149
173
if not found and not common:
150
174
msg = "Invalid relation requested: {}:{}".format(
151
175
check_requires if check_requires else "",
152
check_provides if check_provides else ""
176
check_provides if check_provides else "",
155
179
msg = "Invalid relation on interface: {}".format(
159
183
bundle_info['relations'].append({
161
'debug_info': [repr(EndpointPair(e_a, e_b))]
185
'debug_info': [repr(EndpointPair(e_a, e_b))],
176
200
'message': msg % (
177
201
endpoints[0].service,
178
202
endpoints[1].service),
179
'debug_info': [repr(EndpointPair(e_a, e_b))]
203
'debug_info': [repr(EndpointPair(e_a, e_b))],
185
def _proof_charm_config(bundle_info, bundle_config, charm_name, charm):
207
def _proof_charm_config(bundle_info, charm_data, charm_name, charm):
186
208
"""Given a charm, proof the config is valid for the charm found.
188
210
If the deployer attempts to set illegal or non-existent config values to
189
211
the charm treat these as proof errors.
213
Modifies the bundle_info dictionary in place.
191
check_config = bundle_config['services'][charm_name].get('options')
215
options = charm_data.get('options')
193
217
# There's no config specified to validate.
195
for test_key, test_value in check_config.iteritems():
219
for test_key, test_value in options.iteritems():
197
221
CharmProof.check_config(charm, test_key, test_value)
198
222
except ProofError, exc:
199
if charm_name not in bundle_info['services']:
200
bundle_info['services'][charm_name] = []
223
bundle_info['services'].setdefault(charm_name, [])
202
224
bundle_info['services'][charm_name].append({
203
225
'message': exc.msg,
204
226
'debug_info': exc.debug_info,
209
def proof_deployer(request):
210
"""Check the deployer for proof errors."""
231
def _proof_charm_constraints(bundle_info, charm_data, charm_name):
232
"""Check the charm constraints.
234
Modifies the bundle_info dictionary in place.
236
constraints = charm_data.get('constraints')
237
if constraints is None:
239
if not check_constraints(constraints):
240
# check_constraints returns False if the constraints are invalid or if
241
# they are comma-separated. So now we need to parse them to see if
242
# they are invalid. If they are invalid *and* comma-separated we
243
# cannot discern that and just report the invalid constraints.
245
parse_constraints(constraints)
246
except ValueError as exc:
247
bundle_info['services'].setdefault(charm_name, []).append(
248
dict(message=exc.message))
250
# Comma-separated constraints are just a warning.
251
msg = ('The constraints are comma-separated, '
252
'which is deprecated.')
253
bundle_info['warnings'].setdefault(charm_name, []).append(
257
def proof_deployer_file(request):
258
"""Check the bundle for proof errors."""
211
259
# The top level is for general deployer file errors.
212
260
# The errors here may be an error about the deployer file itself, or an
213
261
# object that describes the issues found with a bundle within the deployer
264
314
# For the charms we did find in the system, verify that their
265
315
# config is valid for the values allowed by the charm we found.
266
316
for name, charm in found_charms.iteritems():
267
bundle_info = _proof_charm_config(
268
bundle_info, bundle_config, name, charm)
317
charm_data = bundle_config['services'][name]
319
bundle_info, charm_data, name, charm)
321
_proof_charm_constraints(
322
bundle_info, charm_data, name)
269
323
response_data['services_info'][name] = {
270
324
'series': charm.series,
271
325
'name': charm.name,
275
329
# Verify that the relations specified in the bundle are valid for the
276
330
# interfaces exposed on the charms that were found.
277
bundle_info = _proof_bundles_relations(
331
_proof_bundles_relations(
278
332
bundle_info, bundle_config, found_charms)
280
# If there are errors in this bundles services or relation, make sure
334
# If there are errors in this bundle's services or relation, make sure
281
335
# we append the info to the response data that gets sent back to the
283
if bundle_info['services'].keys() or bundle_info['relations']:
337
if (bundle_info['services'] or
338
bundle_info['relations'] or
339
bundle_info['warnings']):
284
340
response_data['errors'].append(bundle_info)
286
342
# After proofing each deployer file return with any failure info
288
return json_response(200, _build_proof_response(response_data))
344
_build_proof_response(response_data)
345
return json_response(200, response_data)