Repository: cloud-custodian/cloud-custodian

Issue:
 Failed to validate policy 'copy-related-tag' is not one of ['invoke-lambda']
Need help understanding why the `copy-related-tag`  action for the resource `ebs-snapshot` is not getting validated.

**Policy**

team-tag-ebs-snapshot-audit.yml
```
policies:
  - name: team-tag-ebs-snapshot-audit
    resource: ebs-snapshot
    description: |
      Cloud Custodian EBS Snapshot Team Tag Audit
    comments: |
      Copy EBS volume tag with key "Team" to EBS snapshots
    filters:
      - or:
        - "tag:Team": empty
        - "tag:Team": absent
    actions:
      - type: copy-related-tag
        resource: ebs
        skip_missing: True
        key: VolumeId
        tags: 'Team'
```

**Error**
```
 $  custodian run --dryrun -s output team-tag-ebs-snapshot-audit.yml

2019-10-28 18:22:39,695: root:ERROR specific_error failed, traceback, followed by fallback
Traceback (most recent call last):
  File "/home/ubuntu/c7n_mailer/local/lib/python2.7/site-packages/c7n-0.8.45.1-py2.7.egg/c7n/schema.py", line 55, in validate
    resp = policy_error_scope(specific_error(errors[0]), data)
  File "/home/ubuntu/c7n_mailer/local/lib/python2.7/site-packages/c7n-0.8.45.1-py2.7.egg/c7n/schema.py", line 127, in specific_error
    return specific_error(e)
  File "/home/ubuntu/c7n_mailer/local/lib/python2.7/site-packages/c7n-0.8.45.1-py2.7.egg/c7n/schema.py", line 116, in specific_error
    if v['$ref'].rsplit('/', 2)[1].endswith(r):
KeyError: u'$ref'
Traceback (most recent call last):
  File "/home/ubuntu/c7n_mailer/bin/custodian", line 11, in <module>
    load_entry_point('c7n', 'console_scripts', 'custodian')()
  File "/home/ubuntu/c7n_mailer/local/lib/python2.7/site-packages/c7n-0.8.45.1-py2.7.egg/c7n/cli.py", line 374, in main
    command(config)
  File "/home/ubuntu/c7n_mailer/local/lib/python2.7/site-packages/c7n-0.8.45.1-py2.7.egg/c7n/commands.py", line 65, in _load_policies
    collection = policy_load(options, fp, validate=validate, vars=vars)
  File "/home/ubuntu/c7n_mailer/local/lib/python2.7/site-packages/c7n-0.8.45.1-py2.7.egg/c7n/policy.py", line 63, in load
    errors[1], errors[0]))
c7n.exceptions.PolicyValidationError: Failed to validate policy 'copy-related-tag' is not one of ['invoke-lambda']

Failed validating 'enum' in schema[0]['properties']['type']:
    {'enum': ['invoke-lambda']}

On instance['type']:
    'copy-related-tag'
 {'resource': 'ebs-snapshot', 'name': 'team-tag-ebs-snapshot-audit', 'filters': [{'or': [{'tag:Team': 'empty'}, {'tag:Team': 'absent'}]}], 'description': 'Cloud Custodian EBS Snapshot Team Tag Audit\n', 'actions': [{'skip_missing': True, 'tags': 'Team', 'resource': 'ebs', 'key': 'VolumeId', 'type': 'copy-related-tag'}], 'comments': 'Copy EBS volume tag with key "Team" to EBS snapshot\n'} is not valid under any of the given schemas

Failed validating u'anyOf' in schema[u'properties'][u'policies'][u'items']:
    {u'anyOf': [{u'$ref': u'#/definitions/resources/aws.ssm-managed-instance/policy'},
                {u'$ref': u'#/definitions/resources/aws.glue-table/policy'},
                {u'$ref': u'#/definitions/resources/aws.sagemaker-job/policy'},
                {u'$ref': u'#/definitions/resources/aws.datapipeline/policy'},
                {u'$ref': u'#/definitions/resources/aws.kafka/policy'},
                {u'$ref': u'#/definitions/resources/aws.firehose/policy'},
                {u'$ref': u'#/definitions/resources/aws.support-case/policy'},
                {u'$ref': u'#/definitions/resources/aws.cloudhsm-cluster/policy'},
                {u'$ref': u'#/definitions/resources/aws.efs-mount-target/policy'},
                {u'$ref': u'#/definitions/resources/aws.opswork-stack/policy'},
                {u'$ref': u'#/definitions/resources/aws.elb/policy'},
                {u'$ref': u'#/definitions/resources/aws.lambda-layer/policy'},
                {u'$ref': u'#/definitions/resources/aws.redshift/policy'},
                {u'$ref': u'#/definitions/resources/aws.redshift-snapshot/policy'},
                {u'$ref': u'#/definitions/resources/aws.dms-instance/policy'},
                {u'$ref': u'#/definitions/resources/aws.dynamodb-backup/policy'},
                {u'$ref': u'#/definitions/resources/aws.rds-reserved/policy'},
                {u'$ref': u'#/definitions/resources/aws.rds-subnet-group/policy'},
                {u'$ref': u'#/definitions/resources/aws.elasticbeanstalk/policy'},
                {u'$ref': u'#/definitions/resources/aws.route-table/policy'},
                {u'$ref': u'#/definitions/resources/aws.internet-gateway/policy'},
                {u'$ref': u'#/definitions/resources/aws.ami/policy'},
                {u'$ref': u'#/definitions/resources/aws.cache-cluster/policy'},
                {u'$ref': u'#/definitions/resources/aws.kinesis/policy'},
                {u'$ref': u'#/definitions/resources/aws.lightsail-elb/policy'},
                {u'$ref': u'#/definitions/resources/aws.config-rule/policy'},
                {u'$ref': u'#/definitions/resources/aws.snowball/policy'},
                {u'$ref': u'#/definitions/resources/aws.hostedzone/policy'},
                {u'$ref': u'#/definitions/resources/aws.dlm-policy/policy'},
                {u'$ref': u'#/definitions/resources/aws.transit-gateway/policy'},
                {u'$ref': u'#/definitions/resources/aws.dax/policy'},
                {u'$ref': u'#/definitions/resources/aws.ml-model/policy'},
                {u'$ref': u'#/definitions/resources/aws.glue-crawler/policy'},
                {u'$ref': u'#/definitions/resources/aws.network-addr/policy'},
                {u'$ref': u'#/definitions/resources/aws.asg/policy'},
                {u'$ref': u'#/definitions/resources/aws.vpc-endpoint/policy'},
                {u'$ref': u'#/definitions/resources/aws.emr/policy'},
                {u'$ref': u'#/definitions/resources/aws.kinesis-analytics/policy'},
                {u'$ref': u'#/definitions/resources/aws.iam-user/policy'},
                {u'$ref': u'#/definitions/resources/aws.cfn/policy'},
                {u'$ref': u'#/definitions/resources/aws.vpn-gateway/policy'},
                {u'$ref': u'#/definitions/resources/aws.dynamodb-stream/policy'},
                {u'$ref': u'#/definitions/resources/aws.ecs-container-instance/policy'},
                {u'$ref': u'#/definitions/resources/aws.app-elb/policy'},
                {u'$ref': u'#/definitions/resources/aws.ec2/policy'},
                {u'$ref': u'#/definitions/resources/aws.ebs/policy'},
                {u'$ref': u'#/definitions/resources/aws.message-broker/policy'},
                {u'$ref': u'#/definitions/resources/aws.storage-gateway/policy'},
                {u'$ref': u'#/definitions/resources/aws.sagemaker-endpoint-config/policy'},
                {u'$ref': u'#/definitions/resources/aws.rds-cluster/policy'},
                {u'$ref': u'#/definitions/resources/aws.hsm-client/policy'},
                {u'$ref': u'#/definitions/resources/aws.event-rule/policy'},
                {u'$ref': u'#/definitions/resources/aws.kms/policy'},
                {u'$ref': u'#/definitions/resources/aws.log-group/policy'},
                {u'$ref': u'#/definitions/resources/aws.lightsail-db/policy'},
                {u'$ref': u'#/definitions/resources/aws.backup-plan/policy'},
                {u'$ref': u'#/definitions/resources/aws.secrets-manager/policy'},
                {u'$ref': u'#/definitions/resources/aws.network-acl/policy'},
                {u'$ref': u'#/definitions/resources/aws.rds-cluster-snapshot/policy'},
                {u'$ref': u'#/definitions/resources/aws.rds-subscription/policy'},
                {u'$ref': u'#/definitions/resources/aws.cloud-directory/policy'},
                {u'$ref': u'#/definitions/resources/aws.hsm/policy'},
                {u'$ref': u'#/definitions/resources/aws.identity-pool/policy'},
                {u'$ref': u'#/definitions/resources/aws.ecr/policy'},
                {u'$ref': u'#/definitions/resources/aws.codebuild/policy'},
                {u'$ref': u'#/definitions/resources/aws.directconnect/policy'},
                {u'$ref': u'#/definitions/resources/aws.sagemaker-endpoint/policy'},
                {u'$ref': u'#/definitions/resources/aws.opswork-cm/policy'},
                {u'$ref': u'#/definitions/resources/aws.ecs-service/policy'},
                {u'$ref': u'#/definitions/resources/aws.nat-gateway/policy'},
                {u'$ref': u'#/definitions/resources/aws.rds/policy'},
                {u'$ref': u'#/definitions/resources/aws.key-pair/policy'},
                {u'$ref': u'#/definitions/resources/aws.app-elb-target-group/policy'},
                {u'$ref': u'#/definitions/resources/aws.rds-cluster-param-group/policy'},
                {u'$ref': u'#/definitions/resources/aws.elasticsearch/policy'},
                {u'$ref': u'#/definitions/resources/aws.waf/policy'},
                {u'$ref': u'#/definitions/resources/aws.ecs/policy'},
                {u'$ref': u'#/definitions/resources/aws.eks/policy'},
                {u'$ref': u'#/definitions/resources/aws.rest-resource/policy'},
                {u'$ref': u'#/definitions/resources/aws.lambda/policy'},
                {u'$ref': u'#/definitions/resources/aws.dms-endpoint/policy'},
                {u'$ref': u'#/definitions/resources/aws.glue-connection/policy'},
                {u'$ref': u'#/definitions/resources/aws.rest-vpclink/policy'},
                {u'$ref': u'#/definitions/resources/aws.transit-attachment/policy'},
                {u'$ref': u'#/definitions/resources/aws.elasticbeanstalk-environment/policy'},
                {u'$ref': u'#/definitions/resources/aws.vpn-connection/policy'},
                {u'$ref': u'#/definitions/resources/aws.iam-role/policy'},
                {u'$ref': u'#/definitions/resources/aws.batch-definition/policy'},
                {u'$ref': u'#/definitions/resources/aws.sagemaker-transform-job/policy'},
                {u'$ref': u'#/definitions/resources/aws.dynamodb-table/policy'},
                {u'$ref': u'#/definitions/resources/aws.hsm-hapg/policy'},
                {u'$ref': u'#/definitions/resources/aws.ecs-task-definition/policy'},
                {u'$ref': u'#/definitions/resources/aws.glue-job/policy'},
                {u'$ref': u'#/definitions/resources/aws.shield-protection/policy'},
                {u'$ref': u'#/definitions/resources/aws.cache-subnet-group/policy'},
                {u'$ref': u'#/definitions/resources/aws.sagemaker-model/policy'},
                {u'$ref': u'#/definitions/resources/aws.ops-item/policy'},
                {u'$ref': u'#/definitions/resources/aws.snowball-cluster/policy'},
                {u'$ref': u'#/definitions/resources/aws.iam-profile/policy'},
                {u'$ref': u'#/definitions/resources/aws.sns/policy'},
                {u'$ref': u'#/definitions/resources/aws.glue-database/policy'},
                {u'$ref': u'#/definitions/resources/aws.event-rule-target/policy'},
                {u'$ref': u'#/definitions/resources/aws.vpc/policy'},
                {u'$ref': u'#/definitions/resources/aws.ecs-task/policy'},
                {u'$ref': u'#/definitions/resources/aws.codepipeline/policy'},
                {u'$ref': u'#/definitions/resources/aws.step-machine/policy'},
                {u'$ref': u'#/definitions/resources/aws.rest-stage/policy'},
                {u'$ref': u'#/definitions/resources/aws.account/policy'},
                {u'$ref': u'#/definitions/resources/aws.rds-param-group/policy'},
                {u'$ref': u'#/definitions/resources/aws.security-group/policy'},
                {u'$ref': u'#/definitions/resources/aws.rest-api/policy'},
                {u'$ref': u'#/definitions/resources/aws.acm-certificate/policy'},
                {u'$ref': u'#/definitions/resources/aws.customer-gateway/policy'},
                {u'$ref': u'#/definitions/resources/aws.codecommit/policy'},
                {u'$ref': u'#/definitions/resources/aws.gamelift-fleet/policy'},
                {u'$ref': u'#/definitions/resources/aws.distribution/policy'},
                {u'$ref': u'#/definitions/resources/aws.fsx/policy'},
                {u'$ref': u'#/definitions/resources/aws.ssm-parameter/policy'},
                {u'$ref': u'#/definitions/resources/aws.simpledb/policy'},
                {u'$ref': u'#/definitions/resources/aws.waf-regional/policy'},
                {u'$ref': u'#/definitions/resources/aws.cloudtrail/policy'},
                {u'$ref': u'#/definitions/resources/aws.config-recorder/policy'},
                {u'$ref': u'#/definitions/resources/aws.cache-snapshot/policy'},
                {u'$ref': u'#/definitions/resources/aws.cloudsearch/policy'},
                {u'$ref': u'#/definitions/resources/aws.rrset/policy'},
                {u'$ref': u'#/definitions/resources/aws.peering-connection/policy'},
                {u'$ref': u'#/definitions/resources/aws.streaming-distribution/policy'},
                {u'$ref': u'#/definitions/resources/aws.glacier/policy'},
                {u'$ref': u'#/definitions/resources/aws.eni/policy'},
                {u'$ref': u'#/definitions/resources/aws.efs/policy'},
                {u'$ref': u'#/definitions/resources/aws.redshift-subnet-group/policy'},
                {u'$ref': u'#/definitions/resources/aws.iam-certificate/policy'},
                {u'$ref': u'#/definitions/resources/aws.gamelift-build/policy'},
                {u'$ref': u'#/definitions/resources/aws.user-pool/policy'},
                {u'$ref': u'#/definitions/resources/aws.rds-snapshot/policy'},
                {u'$ref': u'#/definitions/resources/aws.shield-attack/policy'},
                {u'$ref': u'#/definitions/resources/aws.launch-config/policy'},
                {u'$ref': u'#/definitions/resources/aws.workspaces/policy'},
                {u'$ref': u'#/definitions/resources/aws.subnet/policy'},
                {u'$ref': u'#/definitions/resources/aws.sqs/policy'},
                {u'$ref': u'#/definitions/resources/aws.healthcheck/policy'},
                {u'$ref': u'#/definitions/resources/aws.kms-key/policy'},
                {u'$ref': u'#/definitions/resources/aws.iot/policy'},
                {u'$ref': u'#/definitions/resources/aws.rest-account/policy'},
                {u'$ref': u'#/definitions/resources/aws.batch-compute/policy'},
                {u'$ref': u'#/definitions/resources/aws.iam-policy/policy'},
                {u'$ref': u'#/definitions/resources/aws.fsx-backup/policy'},
                {u'$ref': u'#/definitions/resources/aws.launch-template-version/policy'},
                {u'$ref': u'#/definitions/resources/aws.ebs-snapshot/policy'},
                {u'$ref': u'#/definitions/resources/aws.ssm-activation/policy'},
                {u'$ref': u'#/definitions/resources/aws.health-event/policy'},
                {u'$ref': u'#/definitions/resources/aws.iam-group/policy'},
                {u'$ref': u'#/definitions/resources/aws.alarm/policy'},
                {u'$ref': u'#/definitions/resources/aws.r53domain/policy'},
                {u'$ref': u'#/definitions/resources/aws.glue-dev-endpoint/policy'},
                {u'$ref': u'#/definitions/resources/aws.s3/policy'},
                {u'$ref': u'#/definitions/resources/aws.sagemaker-notebook/policy'},
                {u'$ref': u'#/definitions/resources/aws.directory/policy'},
                {u'$ref': u'#/definitions/resources/aws.ec2-reserved/policy'},
                {u'$ref': u'#/definitions/resources/aws.lightsail-instance/policy'}]}

On instance[u'policies'][0]:
    {'actions': [{'key': 'VolumeId',
                  'resource': 'ebs',
                  'skip_missing': True,
                  'tags': 'Team',
                  'type': 'copy-related-tag'}],
     'comments': 'Copy EBS volume tag with key "Team" to EBS snapshot\n',
     'description': 'Cloud Custodian EBS Snapshot Team Tag Audit\n',
     'filters': [{'or': [{'tag:Team': 'empty'}, {'tag:Team': 'absent'}]}],
     'name': 'team-tag-ebs-snapshot-audit',
     'resource': 'ebs-snapshot'}
```



Discussion:
thanks for the report, our jsonschema error helper failed on the error message, which resulted in that generic top level error message.  fixing the helper now outputs.

```
> custodian validate -c foo.yml -v
2019-10-29 07:56:58,964: custodian.commands:ERROR Configuration invalid: foo.yml
2019-10-29 07:56:58,965: custodian.commands:ERROR Error on policy:team-tag-ebs-snapshot-audit resource:ebs-snapshot
'Team' is not valid under any of the given schemas

Failed validating 'oneOf' in schema[2]['properties']['tags']:
    {'oneOf': [{'enum': ['*']}, {'type': 'array'}]}

On instance['tags']:
    'Team'
2019-10-29 07:56:58,965: custodian.commands:ERROR team-tag-ebs-snapshot-audit
```

which is to say `tags` should be an array of strings or the string `*`


Resolving patch:
diff --git a/c7n/schema.py b/c7n/schema.py
--- a/c7n/schema.py
+++ b/c7n/schema.py
@@ -114,7 +114,7 @@ def specific_error(error):
     if r is not None:
         found = None
         for idx, v in enumerate(error.validator_value):
-            if v['$ref'].rsplit('/', 2)[1].endswith(r):
+            if '$ref' in v and v['$ref'].rsplit('/', 2)[1].endswith(r):
                 found = idx
                 break
         if found is not None:


Test patch:
diff --git a/tests/test_schema.py b/tests/test_schema.py
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -285,6 +285,22 @@ def test_handle_specific_error_fail(self, mock_specific_error):
         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'}]}]}
+        errors = list(self.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},