def setUp(self): if not self.validator: self.validator = JsonSchemaValidator(generate())
def test_schema(self): try: schema = generate() JsonSchemaValidator.check_schema(schema) except Exception: self.fail("Invalid schema")
class SchemaTest(BaseTest): validator = None def findError(self, data, validator): e = best_match(validator.iter_errors(data)) ex = specific_error(list(validator.iter_errors(data))[0]) return e, ex def setUp(self): if not self.validator: self.validator = JsonSchemaValidator(generate()) def get_validator(self, data): # return a jsonschema validator for the policy data # use the policy loader to load the resource types self.policy_loader.load_data(data, file_uri='memory://', validate=False) rtypes = StructureParser().get_resource_types(data) return self.policy_loader.validator.gen_schema(tuple(rtypes)) def test_empty_skeleton(self): self.assertEqual( self.policy_loader.validator.validate({"policies": []}), []) def test_empty_with_lazy_load(self): empty_registry = PluginRegistry('stuff') self.patch(schema, 'clouds', empty_registry) policy_schema = generate() self.assertEqual(policy_schema['properties']['policies']['items'], {'type': 'object'}) def test_duplicate_policies(self): data = { "policies": [{ "name": "monday-morning", "resource": "ec2" }, { "name": "monday-morning", "resource": "ec2" }] } # use the policy loader to load the resource types self.policy_loader.load_data(data, file_uri='memory://', validate=False) result = self.policy_loader.validator.validate(data) self.assertEqual(len(result), 2) self.assertTrue(isinstance(result[0], ValueError)) self.assertTrue("monday-morning" in str(result[0])) def test_py3_policy_error(self): data = { 'policies': [{ 'name': 'policy-ec2', 'resource': 'ec2', 'actions': [{ 'type': 'terminate', 'force': 'asdf' }] }] } self.policy_loader.load_data(data, file_uri='memory://', validate=False) result = self.policy_loader.validator.validate(data) self.assertEqual(len(result), 2) err, policy = result self.assertTrue( "'asdf' is not of type 'boolean'" in str(err).replace("u'", "'")) self.assertEqual(policy, 'policy-ec2') def test_semantic_error_common_filter_provider_prefixed(self): data = { 'policies': [{ 'name': 'test', 'resource': 's3', 'filters': [{ 'type': 'metrics', 'name': 'BucketSizeBytes', 'dimensions': [{ 'StorageType': 'StandardStorage' }], 'days': 7, 'value': 100, 'op': 'gte' }] }] } # load s3 resource validator = self.get_validator(data) errors = list(validator.iter_errors(data)) self.assertEqual(len(errors), 1) error = specific_error(errors[0]) self.assertIn( "[{'StorageType': 'StandardStorage'}] is not of type 'object'", str(error)) def test_semantic_mode_error(self): data = { 'policies': [{ 'name': 'test', 'resource': 'ec2', 'mode': { 'type': 'periodic', 'scheduled': 'oops' } }] } validator = self.get_validator(data) errors = list(validator.iter_errors(data)) self.assertEqual(len(errors), 1) error = specific_error(errors[0]) self.assertTrue( len(errors[0].absolute_schema_path) < len( error.absolute_schema_path)) self.assertTrue("'scheduled' was unexpected" in str(error)) self.assertTrue(len(str(error)) < 2000) def test_semantic_error_policy_scope(self): data = { 'policies': [{ 'actions': [{ 'key': 'AES3000', 'type': 'encryption', 'value': 'This resource should have AES3000 encryption' }], 'description': 'Identify resources which lack our outrageous cipher', 'name': 'bogus-policy', 'resource': 'aws.waf' }] } load_resources(('aws.waf', )) validator = self.policy_loader.validator.gen_schema(('aws.waf', )) errors = list(validator.iter_errors(data)) self.assertEqual(len(errors), 1) error = policy_error_scope(specific_error(errors[0]), data) self.assertTrue("policy:bogus-policy" in error.message) def test_semantic_error(self): data = { "policies": [{ "name": "test", "resource": "ec2", "filters": { "type": "ebs", "skipped_devices": [] }, }] } load_resources(('aws.ec2', )) validator = self.policy_loader.validator.gen_schema(('aws.ec2', )) # probably should just ditch this test errors = list(validator.iter_errors(data)) self.assertEqual(len(errors), 1) error = specific_error(errors[0]) self.assertTrue( len(errors[0].absolute_schema_path) < len( error.absolute_schema_path)) self.assertTrue("'skipped_devices': []" in error.message) self.assertTrue("u'type': u'ebs'" in error.message or "'type': 'ebs'" in error.message) @mock.patch("c7n.schema.specific_error") def test_handle_specific_error_fail(self, mock_specific_error): from jsonschema.exceptions import ValidationError data = { "policies": [{ "name": "test", "resource": "aws.ec2", "filters": { "type": "ebs", "invalid": [] }, }] } mock_specific_error.side_effect = ValueError( "The specific error crapped out hard") load_resources(('aws.ec2', )) resp = validate(data) # if it is 2, then we know we got the exception from specific_error self.assertEqual(len(resp), 2) self.assertIsInstance(resp[0], ValidationError) self.assertIsInstance(resp[1], ValidationError) def test_semantic_error_with_nested_resource_key(self): data = { 'policies': [{ 'name': 'team-tag-ebs-snapshot-audit', 'resource': 'ebs-snapshot', 'actions': [{ 'type': 'copy-related-tag', 'resource': 'ebs', 'skip_missing': True, 'key': 'VolumeId', 'tags': 'Team' }] }] } load_resources(('aws.ebs', )) validator = self.get_validator(data) errors = list(validator.iter_errors(data)) self.assertEqual(len(errors), 1) error = specific_error(errors[0]) self.assertTrue('Team' in error.message) def test_vars_and_tags(self): data = { "vars": { "alpha": 1, "beta": 2 }, "policies": [{ "name": "test", "resource": "ec2", "tags": ["controls"] }], } load_resources(('aws.ec2', )) validator = self.get_validator(data) self.assertEqual(list(validator.iter_errors(data)), []) def test_metadata(self): data = { "policies": [{ "name": "test", "resource": "ec2", "metadata": { "createdBy": "Totoro" } }], } load_resources(('aws.ec2', )) validator = self.get_validator(data) self.assertEqual(list(validator.iter_errors(data)), []) def test_semantic_error_on_value_derived(self): data = { "policies": [{ "name": "test", "resource": "ec2", "filters": [{ "type": "ebs", "skipped_devices": [] }], }] } validator = self.get_validator(data) errors = list(validator.iter_errors(data)) self.assertEqual(len(errors), 1) error = specific_error(errors[0]) self.assertTrue( len(errors[0].absolute_schema_path) < len( error.absolute_schema_path)) self.assertTrue( "Additional properties are not allowed " in error.message) self.assertTrue("'skipped_devices' was unexpected" in error.message) def test_invalid_resource_type(self): data = { "policies": [{ "name": "instance-policy", "resource": "ec3", "filters": [] }] } self.assertRaises(PolicyValidationError, self.get_validator, data) def xtest_value_filter_short_form_invalid(self): # this tests helps smoke out overly permissive schemas rtypes = ('aws.elb', ) load_resources(rtypes) for rtype in rtypes: data = { "policies": [{ "name": "instance-policy", "resource": rtype, "filters": [{ "tag:Role": "webserver" }], }] } validator = self.policy_loader.validator.gen_schema((rtype, )) # Disable standard value short form validator.schema["definitions"]["filters"]["valuekv"] = { "type": "number" } errors = list(validator.iter_errors(data)) self.assertEqual(len(errors), 1) def test_nested_bool_operators(self): data = { "policies": [{ "name": "test", "resource": "ec2", "filters": [{ "or": [ { "tag:Role": "webserver" }, { "type": "value", "key": "x", "value": [] }, { "and": [{ "tag:Name": "cattle" }, { "tag:Env": "prod" }] }, ] }], }] } load_resources(('aws.ec2', )) validator = self.policy_loader.validator.gen_schema(('aws.ec2', )) errors = list(validator.iter_errors(data)) self.assertEqual(errors, []) def test_bool_operator_child_validation(self): data = { 'policies': [{ 'name': 'test', 'resource': 'ec2', 'filters': [{ 'or': [{ 'type': 'imagex', 'key': 'tag:Foo', 'value': 'a' }] }] }] } load_resources(('aws.ec2', )) validator = self.policy_loader.validator.gen_schema(('aws.ec2', )) errors = list(validator.iter_errors(data)) self.assertTrue(errors) def test_value_filter_short_form(self): data = { "policies": [{ "name": "instance-policy", "resource": "elb", "filters": [{ "tag:Role": "webserver" }], }] } validator = self.get_validator(data) errors = list(validator.iter_errors(data)) self.assertEqual(errors, []) def test_event_inherited_value_filter(self): data = { "policies": [{ "name": "test", "resource": "ec2", "filters": [{ "type": "event", "key": "detail.requestParameters", "value": "absent", }], }] } errors = list(self.validator.iter_errors(data)) self.assertEqual(errors, []) def test_ebs_inherited_value_filter(self): data = { "policies": [{ "name": "test", "resource": "ec2", "filters": [{ "type": "ebs", "key": "Encrypted", "value": False, "skip-devices": ["/dev/sda1", "/dev/xvda"], }], }] } errors = list(self.validator.iter_errors(data)) self.assertEqual(errors, []) def test_offhours_stop(self): data = { "policies": [{ "name": "ec2-offhours-stop", "resource": "ec2", "filters": [ { "tag:aws:autoscaling:groupName": "absent" }, { "type": "offhour", "tag": "c7n_downtime", "default_tz": "et", "offhour": 19, }, ], }] } validator = self.get_validator(data) errors = list(validator.iter_errors(data)) self.assertEqual(len(errors), 0) def test_instance_age(self): data = { "policies": [{ "name": "ancient-instances", "resource": "ec2", "query": [{ "instance-state-name": "running" }], "filters": [{ "days": 60, "type": "instance-age" }], }] } errors = list(self.get_validator(data).iter_errors(data)) self.assertEqual(len(errors), 0) def test_mark_for_op(self): data = { "policies": [{ "name": "ebs-mark-delete", "resource": "ebs", "filters": [], "actions": [{ "type": "mark-for-op", "op": "delete", "days": 30 }], }] } validator = self.get_validator(data) errors = list(validator.iter_errors(data)) self.assertEqual(len(errors), 0) def test_runtime(self): data = { "policies": [{ "name": "test", "resource": "s3", "mode": { "execution-options": { "metrics_enabled": False }, "type": "periodic", "schedule": "xyz", "runtime": None }, }] } self.policy_loader.load_data(data, file_uri='memory://', validate=False) def errors_with(runtime): data['policies'][0]['mode']['runtime'] = runtime return self.policy_loader.validator.validate(data) self.assertEqual(len(errors_with("python2.7")), 0) self.assertEqual(len(errors_with("python3.6")), 0) self.assertEqual(len(errors_with("python4.5")), 2) def test_element_resolve(self): vocab = resource_vocabulary() self.assertEqual( ElementSchema.resolve(vocab, 'mode.periodic').type, 'periodic') self.assertEqual(ElementSchema.resolve(vocab, 'aws.ec2').type, 'ec2') self.assertEqual( ElementSchema.resolve(vocab, 'aws.ec2.actions.stop').type, 'stop') self.assertRaises(ValueError, ElementSchema.resolve, vocab, 'aws.ec2.actions.foo') def test_element_doc(self): class A: pass class B: """Hello World xyz """ class C(B): pass class D(ValueFilter): pass class E(ValueFilter): """Something""" class F(D): pass class G(E): pass self.assertEqual(ElementSchema.doc(G), "Something") self.assertEqual(ElementSchema.doc(D), "") self.assertEqual(ElementSchema.doc(F), "") self.assertEqual(ElementSchema.doc(B), "Hello World\n\nxyz")