def test_own_document_examples(self): examples_path = os.path.join(os.getcwd(), 'examples') example_files = [ os.path.join(examples_path, f) for f in os.listdir(examples_path) if os.path.isfile(os.path.join(examples_path, f)) ] validated_manifests = [] for example_file in example_files: with open(example_file) as f: documents = yaml.safe_load_all(f.read()) # If the example file doesn't have a document with # armada/Manifest/v1 then skip validating it as the example could # merely be an override. has_manifest = any(x['schema'] == 'armada/Manifest/v1' for x in documents) if not has_manifest: continue validated_manifests.append(example_file) valid, _ = validate.validate_armada_documents(list(documents)) self.assertTrue(valid) self.assertTrue(validated_manifests)
def test_build_armada_manifest_with_missing_chart_grps_fails(self): """Validate that attempting to build a manifest with missing chart groups fails. """ self.documents[6]['data']['chart_groups'] = ['missing-chart-groups'] valid, details = validate.validate_armada_documents(self.documents) self.assertFalse(valid)
def test_build_chart_deps_with_missing_dependency_fails(self): """Validate that attempting to build a chart that points to a missing dependency fails. """ self.documents[1]['data']['dependencies'] = ['missing-dependency'] valid, details = validate.validate_armada_documents(self.documents) self.assertFalse(valid)
def test_validate_armada_yaml_passes(self): template = '{}/resources/valid_armada_document.yaml'.format( self.basepath) with open(template) as f: documents = yaml.safe_load_all(f.read()) valid, details = validate.validate_armada_documents(list(documents)) self.assertTrue(valid)
def on_post(self, req, resp): try: if req.content_type == 'application/json': self.logger.debug("Validating manifest based on reference.") json_body = self.req_json(req) if json_body.get('href', None): self.logger.debug( "Validating manifest from reference %s." % json_body.get('href')) data = ReferenceResolver.resolve_reference( json_body.get('href')) documents = list() for d in data: documents.extend(list(yaml.safe_load_all(d.decode()))) else: resp.status = falcon.HTTP_400 return else: manifest = self.req_yaml(req) documents = list(manifest) self.logger.debug("Validating set of %d documents." % len(documents)) result, details = validate_armada_documents(documents) resp.content_type = 'application/json' resp_body = { 'kind': 'Status', 'apiVersion': 'v1.0', 'metadata': {}, 'reason': 'Validation', 'details': {}, } error_details = [m for m in details if m.get('error', False)] resp_body['details']['errorCount'] = len(error_details) resp_body['details']['messageList'] = details if result: resp.status = falcon.HTTP_200 resp_body['status'] = 'Success' resp_body['message'] = 'Armada validations succeeded' resp_body['code'] = 200 else: resp.status = falcon.HTTP_400 resp_body['status'] = 'Failure' resp_body['message'] = 'Armada validations failed' resp_body['code'] = 400 resp.body = json.dumps(resp_body) except Exception as ex: err_message = 'Failed to validate Armada Manifest' self.logger.error(err_message, exc_info=ex) self.return_error(resp, falcon.HTTP_400, message=err_message)
def pre_flight_ops(self): """Perform a series of checks and operations to ensure proper deployment. """ LOG.info("Performing pre-flight operations.") # Ensure Tiller is available and manifest is valid if not self.tiller.tiller_status(): raise tiller_exceptions.TillerServicesUnavailableException() valid, details = validate.validate_armada_documents(self.documents) if details: for msg in details: if msg.get('error', False): LOG.error(msg.get('message', 'Unknown validation error.')) else: LOG.debug(msg.get('message', 'Validation succeeded.')) if not valid: raise validate_exceptions.InvalidManifestException( error_messages=details) result, msg_list = validate.validate_armada_manifests(self.documents) if not result: raise validate_exceptions.InvalidArmadaObjectException( details=','.join([m.get('message') for m in msg_list])) # Purge known releases that have failed and are in the current yaml manifest_data = self.manifest.get(KEYWORD_ARMADA, {}) prefix = manifest_data.get(KEYWORD_PREFIX, '') failed_releases = self.get_releases_by_status(STATUS_FAILED) for release in failed_releases: for group in manifest_data.get(KEYWORD_GROUPS, []): for ch in group.get(KEYWORD_CHARTS, []): ch_release_name = release_prefix( prefix, ch.get('chart', {}).get('chart_name')) if release[0] == ch_release_name: LOG.info( 'Purging failed release %s ' 'before deployment', release[0]) self.tiller.uninstall_release(release[0]) # Clone the chart sources # # We only support a git source type right now, which can also # handle git:// local paths as well repos = {} for group in manifest_data.get(KEYWORD_GROUPS, []): for ch in group.get(KEYWORD_CHARTS, []): self.tag_cloned_repo(ch, repos) for dep in ch.get('chart', {}).get('dependencies', []): self.tag_cloned_repo(dep, repos)
def _document_checker(self, doc, ovr=None): # Validate document or raise the appropriate exception try: valid, details = validate.validate_armada_documents(doc) except (RuntimeError, TypeError): raise override_exceptions.InvalidOverrideValueException(ovr) if not valid: if ovr: raise override_exceptions.InvalidOverrideValueException(ovr) else: raise validate_exceptions.InvalidManifestException( error_messages=details)
def update_manifests(self): if self.values: for value in self.values: merging_values = self._load_yaml_file(value) self.update_document(merging_values) if self.overrides: for override in self.overrides: new_value = override.split('=')[1] doc_path = override.split('=')[0].split(":") data_path = doc_path.pop().split('.') self.override_manifest_value(doc_path, data_path, new_value) try: validate.validate_armada_documents(self.documents) except Exception: raise override_exceptions.InvalidOverrideValueException( self.overrides) return self.documents
def test_validate_invalid_chart_armada_manifest(self): template = '{}/resources/valid_armada_document.yaml'.format( self.basepath) with open(template) as f: documents = list(yaml.safe_load_all(f.read())) mariadb_document = [ d for d in documents if d['metadata']['name'] == 'mariadb' ][0] del mariadb_document['data']['release'] _, error_messages = validate.validate_armada_documents(documents) expected_error = self._build_error_message( 'armada/Chart/v1', 'mariadb', "'release' is a required property") self.assertEqual(1, len(error_messages)) self.assertEqual(expected_error, error_messages[0]['message'])
def invoke(self): if not self.ctx.obj.get('api', False): doc_data = ReferenceResolver.resolve_reference(self.locations) documents = list() for d in doc_data: documents.extend(list(yaml.safe_load_all(d.decode()))) try: valid, details = validate_armada_documents(documents) if not documents: self.logger.warn('No documents to validate.') elif valid: self.logger.info('Successfully validated: %s', self.locations) else: self.logger.info('Validation failed: %s', self.locations) for m in details: self.logger.info('Validation details: %s', str(m)) except Exception: raise Exception('Exception raised during ' 'validation: %s', self.locations) else: if len(self.locations) > 1: self.logger.error( "Cannot specify multiple locations " "when using validate API." ) return client = self.ctx.obj.get('CLIENT') resp = client.post_validate(self.locations[0]) if resp.get('code') == 200: self.logger.info('Successfully validated: %s', self.locations) else: self.logger.error("Validation failed: %s", self.locations) for m in resp.get('details', {}).get('messageList', []): self.logger.info("Validation details: %s", str(m))
def test_validate_chart_group_with_values(self): test_chart_group = """ --- schema: armada/ChartGroup/v1 metadata: name: kubernetes-proxy schema: metadata/Document/v1 data: description: Kubernetes proxy name: kubernetes-proxy sequenced: true chart_group: - proxy --- schema: armada/Chart/v1 metadata: name: proxy schema: metadata/Document/v1 data: chart_name: proxy timeout: 600 release: kubernetes-proxy source: subpath: proxy type: local location: "/etc/genesis/armada/assets/charts" namespace: kube-system upgrade: no_hooks: true values: images: tags: proxy: gcr.io/google_containers/hyperkube-amd64:v1.8.6 network: kubernetes_netloc: 127.0.0.1:6553 dependencies: - helm-toolkit --- schema: armada/Chart/v1 metadata: name: helm-toolkit schema: metadata/Document/v1 data: chart_name: helm-toolkit wait: timeout: 600 release: helm-toolkit source: reference: master subpath: helm-toolkit location: https://git.openstack.org/openstack/openstack-helm-infra type: git namespace: helm-toolkit upgrade: no_hooks: true values: {} dependencies: [] """ chart_group = yaml.safe_load_all(test_chart_group) is_valid, error = validate.validate_armada_documents(list(chart_group)) self.assertTrue(is_valid)
def _validate_documents(self, req, resp, documents): result, details = validate.validate_armada_documents(documents) return self._format_validation_response(req, resp, result, details)