Repository: cloud-custodian/cloud-custodian

Issue:
schema - validation returning incorrect information in python3
both running: `0beaa591 aws.health-event filter for additional resources (#2943)`

but i've observed this in earlier commits as well

python 3:
```
root@b56a4f8e8732:~# python --version
Python 3.7.1
root@b56a4f8e8732:~# cat test.yml
policies:
  - name: ec2
    resource: ec2
    actions:
      - type: terminate
        force: asdf
root@b56a4f8e8732:~# custodian version
0.8.31.2
root@b56a4f8e8732:~# custodian validate test.yml
2018-11-01 19:52:15,447: root:ERROR specific_error failed, traceback, followed by fallback
Traceback (most recent call last):
  File "/src/c7n/schema.py", line 64, in validate
    resp = specific_error(errors[0])
  File "/src/c7n/schema.py", line 111, in specific_error
    return specific_error(e)
  File "/src/c7n/schema.py", line 131, in specific_error
    if e.absolute_schema_path[vidx] == found:
IndexError: deque index out of range
2018-11-01 19:52:15,559: custodian.commands:ERROR Configuration invalid: test.yml
2018-11-01 19:52:15,562: custodian.commands:ERROR {'name': 'ec2', 'resource': 'ec2', 'actions': [{'type': 'terminate', 'force': 'asdf'}]} is not valid under any of the given schemas

Failed validating 'anyOf' in schema['properties']['policies']['items']:
    {'anyOf': [{'$ref': '#/definitions/resources/aws.iam-group/policy'},
               {'$ref': '#/definitions/resources/aws.iam-role/policy'},
               {'$ref': '#/definitions/resources/aws.iam-user/policy'},
               {'$ref': '#/definitions/resources/aws.iam-policy/policy'},
               {'$ref': '#/definitions/resources/aws.iam-profile/policy'},
               {'$ref': '#/definitions/resources/aws.iam-certificate/policy'},
               {'$ref': '#/definitions/resources/aws.account/policy'},
               {'$ref': '#/definitions/resources/aws.acm-certificate/policy'},
               {'$ref': '#/definitions/resources/aws.ami/policy'},
               {'$ref': '#/definitions/resources/aws.rest-account/policy'},
               {'$ref': '#/definitions/resources/aws.rest-api/policy'},
               {'$ref': '#/definitions/resources/aws.rest-stage/policy'},
               {'$ref': '#/definitions/resources/aws.rest-resource/policy'},
               {'$ref': '#/definitions/resources/aws.shield-protection/policy'},
               {'$ref': '#/definitions/resources/aws.shield-attack/policy'},
               {'$ref': '#/definitions/resources/aws.app-elb/policy'},
               {'$ref': '#/definitions/resources/aws.app-elb-target-group/policy'},
               {'$ref': '#/definitions/resources/aws.ec2/policy'},
               {'$ref': '#/definitions/resources/aws.asg/policy'},
               {'$ref': '#/definitions/resources/aws.launch-config/policy'},
               {'$ref': '#/definitions/resources/aws.lambda/policy'},
               {'$ref': '#/definitions/resources/aws.batch-compute/policy'},
               {'$ref': '#/definitions/resources/aws.batch-definition/policy'},
               {'$ref': '#/definitions/resources/aws.cfn/policy'},
               {'$ref': '#/definitions/resources/aws.distribution/policy'},
               {'$ref': '#/definitions/resources/aws.streaming-distribution/policy'},
               {'$ref': '#/definitions/resources/aws.cloudsearch/policy'},
               {'$ref': '#/definitions/resources/aws.cloudtrail/policy'},
               {'$ref': '#/definitions/resources/aws.codecommit/policy'},
               {'$ref': '#/definitions/resources/aws.codebuild/policy'},
               {'$ref': '#/definitions/resources/aws.codepipeline/policy'},
               {'$ref': '#/definitions/resources/aws.identity-pool/policy'},
               {'$ref': '#/definitions/resources/aws.user-pool/policy'},
               {'$ref': '#/definitions/resources/aws.config-rule/policy'},
               {'$ref': '#/definitions/resources/aws.alarm/policy'},
               {'$ref': '#/definitions/resources/aws.event-rule/policy'},
               {'$ref': '#/definitions/resources/aws.event-rule-target/policy'},
               {'$ref': '#/definitions/resources/aws.log-group/policy'},
               {'$ref': '#/definitions/resources/aws.directory/policy'},
               {'$ref': '#/definitions/resources/aws.cloud-directory/policy'},
               {'$ref': '#/definitions/resources/aws.directconnect/policy'},
               {'$ref': '#/definitions/resources/aws.dms-instance/policy'},
               {'$ref': '#/definitions/resources/aws.dms-endpoint/policy'},
               {'$ref': '#/definitions/resources/aws.dynamodb-table/policy'},
               {'$ref': '#/definitions/resources/aws.dynamodb-backup/policy'},
               {'$ref': '#/definitions/resources/aws.dynamodb-stream/policy'},
               {'$ref': '#/definitions/resources/aws.dax/policy'},
               {'$ref': '#/definitions/resources/aws.datapipeline/policy'},
               {'$ref': '#/definitions/resources/aws.kms/policy'},
               {'$ref': '#/definitions/resources/aws.kms-key/policy'},
               {'$ref': '#/definitions/resources/aws.ebs-snapshot/policy'},
               {'$ref': '#/definitions/resources/aws.ebs/policy'},
               {'$ref': '#/definitions/resources/aws.ecr/policy'},
               {'$ref': '#/definitions/resources/aws.ecs/policy'},
               {'$ref': '#/definitions/resources/aws.ecs-service/policy'},
               {'$ref': '#/definitions/resources/aws.ecs-task/policy'},
               {'$ref': '#/definitions/resources/aws.ecs-task-definition/policy'},
               {'$ref': '#/definitions/resources/aws.ecs-container-instance/policy'},
               {'$ref': '#/definitions/resources/aws.efs/policy'},
               {'$ref': '#/definitions/resources/aws.efs-mount-target/policy'},
               {'$ref': '#/definitions/resources/aws.cache-cluster/policy'},
               {'$ref': '#/definitions/resources/aws.cache-subnet-group/policy'},
               {'$ref': '#/definitions/resources/aws.cache-snapshot/policy'},
               {'$ref': '#/definitions/resources/aws.elasticbeanstalk/policy'},
               {'$ref': '#/definitions/resources/aws.elasticbeanstalk-environment/policy'},
               {'$ref': '#/definitions/resources/aws.elasticsearch/policy'},
               {'$ref': '#/definitions/resources/aws.elb/policy'},
               {'$ref': '#/definitions/resources/aws.eks/policy'},
               {'$ref': '#/definitions/resources/aws.emr/policy'},
               {'$ref': '#/definitions/resources/aws.gamelift-build/policy'},
               {'$ref': '#/definitions/resources/aws.gamelift-fleet/policy'},
               {'$ref': '#/definitions/resources/aws.glacier/policy'},
               {'$ref': '#/definitions/resources/aws.glue-connection/policy'},
               {'$ref': '#/definitions/resources/aws.health-event/policy'},
               {'$ref': '#/definitions/resources/aws.hsm/policy'},
               {'$ref': '#/definitions/resources/aws.hsm-hapg/policy'},
               {'$ref': '#/definitions/resources/aws.hsm-client/policy'},
               {'$ref': '#/definitions/resources/aws.iot/policy'},
               {'$ref': '#/definitions/resources/aws.kinesis/policy'},
               {'$ref': '#/definitions/resources/aws.firehose/policy'},
               {'$ref': '#/definitions/resources/aws.kinesis-analytics/policy'},
               {'$ref': '#/definitions/resources/aws.ml-model/policy'},
               {'$ref': '#/definitions/resources/aws.message-broker/policy'},
               {'$ref': '#/definitions/resources/aws.opswork-stack/policy'},
               {'$ref': '#/definitions/resources/aws.opswork-cm/policy'},
               {'$ref': '#/definitions/resources/aws.rds/policy'},
               {'$ref': '#/definitions/resources/aws.rds-subscription/policy'},
               {'$ref': '#/definitions/resources/aws.rds-snapshot/policy'},
               {'$ref': '#/definitions/resources/aws.rds-subnet-group/policy'},
               {'$ref': '#/definitions/resources/aws.rds-param-group/policy'},
               {'$ref': '#/definitions/resources/aws.rds-cluster-param-group/policy'},
               {'$ref': '#/definitions/resources/aws.rds-cluster/policy'},
               {'$ref': '#/definitions/resources/aws.rds-cluster-snapshot/policy'},
               {'$ref': '#/definitions/resources/aws.redshift/policy'},
               {'$ref': '#/definitions/resources/aws.redshift-subnet-group/policy'},
               {'$ref': '#/definitions/resources/aws.redshift-snapshot/policy'},
               {'$ref': '#/definitions/resources/aws.hostedzone/policy'},
               {'$ref': '#/definitions/resources/aws.healthcheck/policy'},
               {'$ref': '#/definitions/resources/aws.rrset/policy'},
               {'$ref': '#/definitions/resources/aws.r53domain/policy'},
               {'$ref': '#/definitions/resources/aws.s3/policy'},
               {'$ref': '#/definitions/resources/aws.sagemaker-notebook/policy'},
               {'$ref': '#/definitions/resources/aws.sagemaker-job/policy'},
               {'$ref': '#/definitions/resources/aws.sagemaker-endpoint/policy'},
               {'$ref': '#/definitions/resources/aws.sagemaker-endpoint-config/policy'},
               {'$ref': '#/definitions/resources/aws.sagemaker-model/policy'},
               {'$ref': '#/definitions/resources/aws.secrets-manager/policy'},
               {'$ref': '#/definitions/resources/aws.step-machine/policy'},
               {'$ref': '#/definitions/resources/aws.simpledb/policy'},
               {'$ref': '#/definitions/resources/aws.snowball-cluster/policy'},
               {'$ref': '#/definitions/resources/aws.snowball/policy'},
               {'$ref': '#/definitions/resources/aws.sns/policy'},
               {'$ref': '#/definitions/resources/aws.storage-gateway/policy'},
               {'$ref': '#/definitions/resources/aws.sqs/policy'},
               {'$ref': '#/definitions/resources/aws.ssm-parameter/policy'},
               {'$ref': '#/definitions/resources/aws.ssm-managed-instance/policy'},
               {'$ref': '#/definitions/resources/aws.ssm-activation/policy'},
               {'$ref': '#/definitions/resources/aws.support-case/policy'},
               {'$ref': '#/definitions/resources/aws.vpc/policy'},
               {'$ref': '#/definitions/resources/aws.subnet/policy'},
               {'$ref': '#/definitions/resources/aws.security-group/policy'},
               {'$ref': '#/definitions/resources/aws.eni/policy'},
               {'$ref': '#/definitions/resources/aws.route-table/policy'},
               {'$ref': '#/definitions/resources/aws.peering-connection/policy'},
               {'$ref': '#/definitions/resources/aws.network-acl/policy'},
               {'$ref': '#/definitions/resources/aws.network-addr/policy'},
               {'$ref': '#/definitions/resources/aws.customer-gateway/policy'},
               {'$ref': '#/definitions/resources/aws.internet-gateway/policy'},
               {'$ref': '#/definitions/resources/aws.nat-gateway/policy'},
               {'$ref': '#/definitions/resources/aws.vpn-connection/policy'},
               {'$ref': '#/definitions/resources/aws.vpn-gateway/policy'},
               {'$ref': '#/definitions/resources/aws.vpc-endpoint/policy'},
               {'$ref': '#/definitions/resources/aws.key-pair/policy'},
               {'$ref': '#/definitions/resources/aws.waf/policy'},
               {'$ref': '#/definitions/resources/aws.waf-regional/policy'}]}

On instance['policies'][0]:
    {'actions': [{'force': 'asdf', 'type': 'terminate'}],
     'name': 'ec2',
     'resource': 'ec2'}
2018-11-01 19:52:15,563: custodian.commands:ERROR 'terminate' is not one of ['notify']

Failed validating 'enum' in schema[0]['properties']['type']:
    {'enum': ['notify']}

On instance['type']:
    'terminate'
root@b56a4f8e8732:/src# git log --oneline --max-count 1
0beaa591 aws.health-event filter for additional resources (#2943)
```

python 2:
```
root@14d3a54e2523:~# python --version
Python 2.7.15
root@14d3a54e2523:~# cat test.yml
policies:
  - name: ec2
    resource: ec2
    actions:
      - type: terminate
        force: asdf
root@14d3a54e2523:~# custodian version
0.8.31.2
root@14d3a54e2523:~# custodian validate test.yml
2018-11-01 19:52:15,402: custodian.commands:ERROR Configuration invalid: test.yml
2018-11-01 19:52:15,402: custodian.commands:ERROR 'asdf' is not of type u'boolean'

Failed validating u'type' in schema[1][u'properties']['force']:
    {u'type': u'boolean'}

On instance['force']:
    'asdf'
2018-11-01 19:52:15,402: custodian.commands:ERROR ec2
root@14d3a54e2523:~# cd /src
root@14d3a54e2523:/src# git log --oneline --max-count 1
0beaa591 aws.health-event filter for additional resources (#2943)
```


Discussion:
Interesting I had noticed this but hadn’t realized it was differential on version
i did fix the inline traceback on this, but there still some idiosyncrasies on getting the actual validation cleaned up for py3 that needs work.

Resolving patch:
diff --git a/c7n/schema.py b/c7n/schema.py
--- a/c7n/schema.py
+++ b/c7n/schema.py
@@ -118,20 +118,14 @@ def specific_error(error):
             if '$ref' in v and v['$ref'].rsplit('/', 2)[-1] == t:
                 found = idx
                 break
+
         if found is not None:
-            # Try to walk back an element/type ref to the specific
-            # error
-            spath = list(error.context[0].absolute_schema_path)
-            spath.reverse()
-            slen = len(spath)
-            if 'oneOf' in spath:
-                idx = spath.index('oneOf')
-            elif 'anyOf' in spath:
-                idx = spath.index('anyOf')
-            vidx = slen - idx
             for e in error.context:
-                if e.absolute_schema_path[vidx] == found:
-                    return e
+                for el in reversed(e.absolute_schema_path):
+                    if isinstance(el, int):
+                        if el == found:
+                            return e
+                        break
     return error
 
 


Test patch:
diff --git a/tests/test_schema.py b/tests/test_schema.py
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -73,6 +73,20 @@ def test_duplicate_policies(self):
         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'}]}]}
+        result = 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(self):
         data = {
             "policies": [