Repository: cloud-custodian/cloud-custodian

Issue:
Issue: Security Group Actions Schema bug
Seems to be an issue in the way schemas are validating. Tried in both python2 and 3, with slightly different behavior in both. See gist: https://gist.github.com/andrewalexander/9c528ccba3b62e743b5d858503ae5c4f

Long story short:
- For actions, we should be able to do one of the following, but can only do the last two (`['remove', 'isolation-group']` and `'add'` by itself)
```
['add', 'remove']
['remove', 'isolation-group']
['add']
```
- Seems to be valid under two of the above; could be getting confused on which happy path to take (since it satisfies two separate schema definitions)
- When split out into two separate actions statements, both the old SG (that we were supposed to remove) and the new one are left attached. Example actions that seem to be what we want, but leaves both SGs instead of just the one from the 'add':
```
    actions:
      - type: modify-security-groups
        remove: matched
        isolation-group: sg-deadbeef
      - type: modify-security-groups
        add: sg-bdca74ce
```
- Errors are different in different python versions; python2 seemed to point to issue (duplicate matching schemas); python3 seemed to treat the policy as a lambda/event-based policy. Unsure based on error of `custodian.commands:ERROR 'security-group' is not one of ['event']`

Might just be as simple as swapping the `oneOf` to an embedded `enum` to more explicitly force a happy path.



Discussion:

Resolving patch:
diff --git a/c7n/actions.py b/c7n/actions.py
--- a/c7n/actions.py
+++ b/c7n/actions.py
@@ -197,10 +197,10 @@ class ModifyVpcSecurityGroupsAction(Action):
                 {'type': 'string', 'pattern': '^sg-*'},
                 {'type': 'array', 'items': {
                     'type': 'string', 'pattern': '^sg-*'}}]}},
-        'oneOf': [
-            {'required': ['isolation-group', 'remove']},
-            {'required': ['add', 'remove']},
-            {'required': ['add']}]
+        'anyOf': [
+            {'required': ['isolation-group', 'remove', 'type']},
+            {'required': ['add', 'remove', 'type']},
+            {'required': ['add', 'type']}]
     }
 
     def get_groups(self, resources, metadata_key=None):


Test patch:
diff --git a/tests/data/placebo/test_zone_shield_enable/#route53.ListHostedZones_1.json# b/tests/data/placebo/test_zone_shield_enable/#route53.ListHostedZones_1.json#
deleted file mode 100644
--- a/tests/data/placebo/test_zone_shield_enable/#route53.ListHostedZones_1.json#
+++ /dev/null
@@ -1,29 +0,0 @@
-{
-    "status_code": 200, 
-    "data": {
-        "HostedZones": [
-            {
-                "ResourceRecordSetCount": 12, 
-                "CallerReference": "D4507AE8-8060-D0B4-XXXX-B7353DCF3EVV", 
-                "Config": {
-                    "PrivateZone": false
-                }, 
-                "Id": "/hostedzone/XXXXURLYV5DGGG", 
-                "Name": "invitro.cloud."
-            }
-        ], 
-        "IsTruncated": false, 
-        "ResponseMetadata": {
-            "RetryAttempts": 0, 
-            "HTTPStatusCode": 200, 
-            "RequestId": "b7809efa-a9d4-11e7-8453-e15117599e76", 
-            "HTTPHeaders": {
-                "x-amzn-requestid": "b7809efa-a9d4-11e7-8453-e15117599e76", 
-                "date": "Thu, 05 Oct 2017 13:54:33 GMT", 
-                "content-length": "1283", 
-                "content-type": "text/xml"
-            }
-        }, 
-        "MaxItems": "100"
-    }
-}