Left: | ||
Right: |
OLD | NEW |
---|---|
1 __metaclass__ = type | 1 __metaclass__ = type |
2 | 2 |
3 from datetime import ( | 3 from datetime import ( |
4 datetime, | 4 datetime, |
5 timedelta, | 5 timedelta, |
6 ) | 6 ) |
7 from email.utils import parseaddr | 7 from email.utils import parseaddr |
8 from inspect import getargspec | 8 from inspect import getargspec |
9 import json | 9 import json |
10 from os.path import ( | 10 from os.path import ( |
11 join, | 11 join, |
12 ) | 12 ) |
13 import re | 13 import re |
14 from urllib import unquote | 14 from urllib import unquote |
15 import yaml | |
16 | 15 |
17 from gridfs.errors import NoFile | 16 from gridfs.errors import NoFile |
18 import pymongo | 17 import pymongo |
19 from pyramid.httpexceptions import ( | 18 from pyramid.httpexceptions import ( |
20 HTTPFound, | 19 HTTPFound, |
21 HTTPNotFound, | 20 HTTPNotFound, |
22 ) | 21 ) |
23 from pyramid.view import view_config | 22 from pyramid.view import view_config |
24 from webob import Response | 23 from webob import Response |
25 | 24 |
25 from charmworld.lib.proof import ( | |
26 BundleProof, | |
27 CharmProof, | |
28 ProofError, | |
29 ) | |
26 from charmworld.models import ( | 30 from charmworld.models import ( |
27 Bundle, | 31 Bundle, |
28 BundledCharmDescription, | 32 BundledCharmDescription, |
29 Charm, | 33 Charm, |
30 CharmFileSet, | 34 CharmFileSet, |
31 FeaturedSource, | 35 FeaturedSource, |
32 getfs, | 36 getfs, |
33 QADataSource, | 37 QADataSource, |
34 resolve_charm_from_description, | 38 resolve_charm_from_description, |
35 ) | 39 ) |
(...skipping 447 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
483 else: | 487 else: |
484 query = { | 488 query = { |
485 'basket_name': bundle_bits['basket'], | 489 'basket_name': bundle_bits['basket'], |
486 'name': bundle_bits['bundle'], | 490 'name': bundle_bits['bundle'], |
487 'promulgated': True | 491 'promulgated': True |
488 } | 492 } |
489 | 493 |
490 bundle = Bundle.from_query(query, self.request.db) | 494 bundle = Bundle.from_query(query, self.request.db) |
491 return bundle_id, trailing, bundle | 495 return bundle_id, trailing, bundle |
492 | 496 |
493 def _proof_bundle(self, request): | 497 def _proof_bundle(self, request): |
gary.poster
2013/10/18 20:10:28
I think this method is getting too big. Could we
| |
494 """Check the bundle provided for proof errors.""" | 498 """Check the bundle provided for proof errors.""" |
495 # The top level is for general deployer file errors. | 499 # The top level is for general deployer file errors. |
496 response_data = { | 500 response_data = { |
497 'errors': [], | 501 'errors': [], |
rharding
2013/10/18 19:36:14
this is the list of actual errors. They can be glo
gary.poster
2013/10/18 20:10:28
Add as comment?
| |
502 'error_messages': [], | |
rharding
2013/10/18 19:36:14
this is a summary of all error messages provided a
gary.poster
2013/10/18 20:10:28
s/marco/proof/ and add as comment?
| |
503 'debug_info': [], | |
rharding
2013/10/18 19:36:14
and a global level 'debug_info' to match the repre
gary.poster
2013/10/18 20:10:28
Add as comment? :-)
| |
498 } | 504 } |
499 bundle_string = request.params.get('deployer_file') | 505 bundle_string = request.params.get('deployer_file') |
500 bundle_format = request.params.get('bundle_format', 'yaml') | 506 bundle_format = request.params.get('bundle_format', 'yaml') |
501 bundle_data = None | 507 bundle_data = None |
502 | 508 |
503 if not bundle_string: | 509 if not bundle_string: |
504 response_data['errors'].append('No deployer file data received.') | 510 response_data['error_messages'].append( |
511 'No deployer file data received.') | |
505 return json_response(400, response_data) | 512 return json_response(400, response_data) |
506 | 513 |
507 if bundle_format == 'yaml': | 514 try: |
rharding
2013/10/18 19:36:14
moved the old checks to the try:catch for the Proo
gary.poster
2013/10/18 20:10:28
Ah, I see. I essentially was suggesting the old a
| |
508 try: | 515 bundle_data = BundleProof.check_parseable_deployer_file( |
509 bundle_data = yaml.safe_load(bundle_string) | 516 bundle_string, |
510 except yaml.YAMLError: | 517 format=bundle_format) |
511 response_data['errors'].append( | 518 except ProofError, exc: |
512 'Could not parse the yaml provided.') | 519 # If we cannot parse the config file there's nothing more to do. |
513 return json_response(400, response_data) | 520 # Return immediately. |
521 response_data['error_messages'].append(exc.msg) | |
522 response_data['debug_info'].append(exc.debug_info) | |
523 return json_response(400, response_data) | |
514 | 524 |
515 # Proof each bundle in the deployer file. | 525 # Proof each bundle in the deployer file. |
516 for bundle_name, bundle_config in bundle_data.items(): | 526 for bundle_name, bundle_config in bundle_data.items(): |
517 has_bundle_error = False | 527 bundle_info = { |
518 bundle_info = { | 528 'name': bundle_name, |
519 'name': bundle_name, | 529 'services': {}, |
520 'services': {}, | 530 'relations': {}, |
521 'relations': {}, | 531 } |
522 } | 532 found_charms = {} |
523 | 533 |
524 for name, charm in bundle_config.get('services').items(): | 534 # Verify we can find the charms at all. |
gary.poster
2013/10/18 20:10:28
Per my suggestion to divide this up a bit, could t
| |
525 charm_description = BundledCharmDescription( | 535 for name, charm in bundle_config.get('services').items(): |
526 'proof', charm, bundle_config.get('series')) | 536 try: |
537 charm_found = BundleProof.check_service_exists( | |
gary.poster
2013/10/18 20:10:28
As before: in this case I think keeping the code h
| |
538 self.request.db, name, charm, bundle_config) | |
539 found_charms[name] = Charm(charm_found) | |
540 except ProofError, exc: | |
541 # We could not find the charm. No other verification can | |
542 # happen. | |
543 bundle_info['services'][name] = [] | |
544 bundle_info['services'][name].append({ | |
545 'message': exc.msg, | |
546 'debug_info': exc.debug_info, | |
547 }) | |
527 | 548 |
528 if not resolve_charm_from_description( | 549 # For the charms we did find in the system, verify that their |
529 self.request.db, charm_description): | 550 # config is valid for the values allowed by the charm we found. |
gary.poster
2013/10/18 20:10:28
Per my suggestion to divide this up a bit, could t
| |
530 has_bundle_error = True | 551 for name, charm in found_charms.iteritems(): |
531 # The error is keyed to the charm and includes a | 552 check_config = bundle_config['services'][name].get('options') |
532 # general message + the description's thought process. | 553 if not check_config: |
533 bundle_info['services'][name] = { | 554 # There's no config specified to validate. |
534 'message': 'Could not find charm: ' + name, | 555 continue |
535 'process': charm_description.get_process() | 556 for test_key, test_value in check_config.iteritems(): |
536 } | 557 try: |
558 CharmProof.check_config(charm, test_key, test_value) | |
559 except ProofError, exc: | |
560 if name not in bundle_info['services']: | |
561 bundle_info['services'][name] = [] | |
537 | 562 |
538 if has_bundle_error: | 563 bundle_info['services'][name].append({ |
539 response_data['errors'].append(bundle_info) | 564 'message': exc.msg, |
565 'debug_info': exc.debug_info, | |
566 }) | |
540 | 567 |
541 return json_response(200, response_data) | 568 # If there are errors in this bundles services, make sure we |
569 # append the info to the response data that gets sent back to the | |
570 # user. | |
571 if bundle_info['services'].keys(): | |
572 response_data['errors'].append(bundle_info) | |
573 | |
574 # Build up a root messages list of errors that can be used to help the | |
575 # client go through them all easily. | |
576 error_messages = [] | |
577 if response_data['errors']: | |
578 for bundle in response_data['errors']: | |
579 prefix = bundle['name'] | |
580 for service_name, service in bundle['services'].items(): | |
581 for err in service: | |
582 msg = '%s: %s' % (prefix, err['message']) | |
583 error_messages.append(msg) | |
584 if error_messages: | |
585 response_data['error_messages'].extend(error_messages) | |
586 | |
587 # After proofing each deployer file return with any failure info | |
588 # found. | |
589 return json_response(200, response_data) | |
542 | 590 |
543 def charm(self, path=None): | 591 def charm(self, path=None): |
544 """Retrieve a charm according to its API ID (the path prefix).""" | 592 """Retrieve a charm according to its API ID (the path prefix).""" |
545 if path is None: | 593 if path is None: |
546 raise HTTPNotFound(self.request.path) | 594 raise HTTPNotFound(self.request.path) |
547 charm_id, trailing, charm_data = self._find_charm(path) | 595 charm_id, trailing, charm_data = self._find_charm(path) |
548 | 596 |
549 if path[0] == 'resolve-icon': | 597 if path[0] == 'resolve-icon': |
550 return self._resolve_charm_icon_url(path[1:]) | 598 return self._resolve_charm_icon_url(path[1:]) |
551 | 599 |
(...skipping 293 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
845 'series': series, | 893 'series': series, |
846 'name': name, | 894 'name': name, |
847 }, sort=[('store_data.revision', pymongo.DESCENDING)]) | 895 }, sort=[('store_data.revision', pymongo.DESCENDING)]) |
848 if charm is not None: | 896 if charm is not None: |
849 api_id = self._get_api_id(Charm(charm)) | 897 api_id = self._get_api_id(Charm(charm)) |
850 # Charm ID should match up to, but not including the revision. | 898 # Charm ID should match up to, but not including the revision. |
851 revisionless_charm_id = charm_id.split('-', 1)[0] | 899 revisionless_charm_id = charm_id.split('-', 1)[0] |
852 if not api_id.startswith(revisionless_charm_id): | 900 if not api_id.startswith(revisionless_charm_id): |
853 charm = None | 901 charm = None |
854 return charm_id, trailing, charm | 902 return charm_id, trailing, charm |
OLD | NEW |