We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Lambda error processing event-rule resource
I'm facing an error when using the aws.event-rule resource in cloudtrail mode:
the lambda function exits with this error after getting the resource_id:
AttributeError: type object 'resource_type' has no attribute 'filter_type'

Can you help me making this work ?

Here is the detailed stacktrace:
````
type object 'resource_type' has no attribute 'filter_type': AttributeError
Traceback (most recent call last):
File "/var/task/custodian_policy.py", line 4, in run
return handler.dispatch_event(event, context)
File "/var/task/c7n/handler.py", line 91, in dispatch_event
p.push(event, context)
File "/var/task/c7n/policy.py", line 776, in push
return mode.run(event, lambda_ctx)
File "/var/task/c7n/policy.py", line 441, in run
resources = self.resolve_resources(event)
File "/var/task/c7n/policy.py", line 418, in resolve_resources
resources = self.policy.resource_manager.get_resources(resource_ids)
File "/var/task/c7n/query.py", line 441, in get_resources
resources = self.source.get_resources(ids)
File "/var/task/c7n/query.py", line 212, in get_resources
return self.query.get(self.manager, ids)
File "/var/task/c7n/query.py", line 93, in get
if m.filter_type == 'list':
AttributeError: type object 'resource_type' has no attribute 'filter_type'

````

Here is the policy i'm using:
```
policies:
  - name: tlogs-modify-prefixrules
    resource: aws.event-rule
    filters:
      - or: 
        - type: event
          key: detail.requestParameters.name
          value: "prefix-.*"
          op: Regex
        - type: value
          key: detail.requestParameters.rule
          value: "prefix-.*"
          op: Regex
    mode:
      function-prefix: "prefix-"
      type: cloudtrail
      role: arn:aws:iam::{account_id}:role/prefix/prefix_custodian_core
      events:
        - source: events.amazonaws.com
          event: DeleteRule
          ids: requestParameters.name
        - source: events.amazonaws.com
          event: DisableRule
          ids: requestParameters.name
        - source: events.amazonaws.com
          event: RemoveTargets
          ids: requestParameters.name
        - source: events.amazonaws.com
          event: PutRule
          ids: requestParameters.name
    description: |
          Alerts when prefix event-rules are modified    
    actions:
      - type: invoke-lambda
        function: prefix-lambda-notifier
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `c7n/resources/cw.py`
Content:
```
1 # Copyright 2016-2017 Capital One Services, LLC
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 from __future__ import absolute_import, division, print_function, unicode_literals
15 
16 from concurrent.futures import as_completed
17 from datetime import datetime, timedelta
18 
19 from c7n.actions import BaseAction
20 from c7n.filters import Filter
21 from c7n.filters.iamaccess import CrossAccountAccessFilter
22 from c7n.query import QueryResourceManager, ChildResourceManager
23 from c7n.manager import resources
24 from c7n.resolver import ValuesFrom
25 from c7n.tags import universal_augment, register_universal_tags
26 from c7n.utils import type_schema, local_session, chunks, get_retry
27 
28 
29 @resources.register('alarm')
30 class Alarm(QueryResourceManager):
31 
32     class resource_type(object):
33         service = 'cloudwatch'
34         type = 'alarm'
35         enum_spec = ('describe_alarms', 'MetricAlarms', None)
36         id = 'AlarmArn'
37         filter_name = 'AlarmNames'
38         filter_type = 'list'
39         name = 'AlarmName'
40         date = 'AlarmConfigurationUpdatedTimestamp'
41         dimension = None
42         config_type = 'AWS::CloudWatch::Alarm'
43 
44     retry = staticmethod(get_retry(('Throttled',)))
45 
46 
47 @Alarm.action_registry.register('delete')
48 class AlarmDelete(BaseAction):
49     """Delete a cloudwatch alarm.
50 
51     :example:
52 
53     .. code-block:: yaml
54 
55             policies:
56               - name: cloudwatch-delete-stale-alarms
57                 resource: alarm
58                 filters:
59                   - type: value
60                     value_type: age
61                     key: StateUpdatedTimestamp
62                     value: 30
63                     op: ge
64                   - StateValue: INSUFFICIENT_DATA
65                 actions:
66                   - delete
67     """
68 
69     schema = type_schema('delete')
70     permissions = ('cloudwatch:DeleteAlarms',)
71 
72     def process(self, resources):
73         client = local_session(
74             self.manager.session_factory).client('cloudwatch')
75 
76         for resource_set in chunks(resources, size=100):
77             self.manager.retry(
78                 client.delete_alarms,
79                 AlarmNames=[r['AlarmName'] for r in resource_set])
80 
81 
82 @resources.register('event-rule')
83 class EventRule(QueryResourceManager):
84 
85     class resource_type(object):
86         service = 'events'
87         type = 'event-rule'
88         enum_spec = ('list_rules', 'Rules', None)
89         name = "Name"
90         id = "Name"
91         filter_name = "NamePrefix"
92         filer_type = "scalar"
93         dimension = "RuleName"
94 
95 
96 @resources.register('event-rule-target')
97 class EventRuleTarget(ChildResourceManager):
98 
99     class resource_type(object):
100         service = 'events'
101         type = 'event-rule-target'
102         enum_spec = ('list_targets_by_rule', 'Targets', None)
103         parent_spec = ('event-rule', 'Rule', True)
104         name = id = 'Id'
105         dimension = None
106         filter_type = filter_name = None
107 
108 
109 @EventRuleTarget.filter_registry.register('cross-account')
110 class CrossAccountFilter(CrossAccountAccessFilter):
111 
112     schema = type_schema(
113         'cross-account',
114         # white list accounts
115         whitelist_from=ValuesFrom.schema,
116         whitelist={'type': 'array', 'items': {'type': 'string'}})
117 
118     # dummy permission
119     permissions = ('events:ListTargetsByRule',)
120 
121     def __call__(self, r):
122         account_id = r['Arn'].split(':', 5)[4]
123         return account_id not in self.accounts
124 
125 
126 @EventRuleTarget.action_registry.register('delete')
127 class DeleteTarget(BaseAction):
128 
129     schema = type_schema('delete')
130     permissions = ('events:RemoveTargets',)
131 
132     def process(self, resources):
133         client = local_session(self.manager.session_factory).client('events')
134         rule_targets = {}
135         for r in resources:
136             rule_targets.setdefault(r['c7n:parent-id'], []).append(r['Id'])
137 
138         for rule_id, target_ids in rule_targets.items():
139             client.remove_targets(
140                 Ids=target_ids,
141                 Rule=rule_id)
142 
143 
144 @resources.register('log-group')
145 class LogGroup(QueryResourceManager):
146 
147     class resource_type(object):
148         service = 'logs'
149         type = 'log-group'
150         enum_spec = ('describe_log_groups', 'logGroups', None)
151         name = 'logGroupName'
152         id = 'arn'
153         filter_name = 'logGroupNamePrefix'
154         filter_type = 'scalar'
155         dimension = 'LogGroupName'
156         date = 'creationTime'
157 
158     augment = universal_augment
159 
160     def get_arns(self, resources):
161         # log group arn in resource describe has ':*' suffix, not all
162         # apis can use that form, so normalize to standard arn.
163         return [r['arn'][:-2] for r in resources]
164 
165 
166 register_universal_tags(LogGroup.filter_registry, LogGroup.action_registry)
167 
168 
169 @LogGroup.action_registry.register('retention')
170 class Retention(BaseAction):
171     """Action to set the retention period (in days) for CloudWatch log groups
172 
173     :example:
174 
175     .. code-block:: yaml
176 
177             policies:
178               - name: cloudwatch-set-log-group-retention
179                 resource: log-group
180                 actions:
181                   - type: retention
182                     days: 200
183     """
184 
185     schema = type_schema('retention', days={'type': 'integer'})
186     permissions = ('logs:PutRetentionPolicy',)
187 
188     def process(self, resources):
189         client = local_session(self.manager.session_factory).client('logs')
190         days = self.data['days']
191         for r in resources:
192             client.put_retention_policy(
193                 logGroupName=r['logGroupName'],
194                 retentionInDays=days)
195 
196 
197 @LogGroup.action_registry.register('delete')
198 class Delete(BaseAction):
199     """
200 
201     :example:
202 
203     .. code-block:: yaml
204 
205             policies:
206               - name: cloudwatch-delete-stale-log-group
207                 resource: log-group
208                 filters:
209                   - type: last-write
210                     days: 182.5
211                 actions:
212                   - delete
213     """
214 
215     schema = type_schema('delete')
216     permissions = ('logs:DeleteLogGroup',)
217 
218     def process(self, resources):
219         client = local_session(self.manager.session_factory).client('logs')
220         for r in resources:
221             client.delete_log_group(logGroupName=r['logGroupName'])
222 
223 
224 @LogGroup.filter_registry.register('last-write')
225 class LastWriteDays(Filter):
226     """Filters CloudWatch log groups by last write
227 
228     :example:
229 
230     .. code-block:: yaml
231 
232             policies:
233               - name: cloudwatch-stale-groups
234                 resource: log-group
235                 filters:
236                   - type: last-write
237                     days: 60
238     """
239 
240     schema = type_schema(
241         'last-write', days={'type': 'number'})
242     permissions = ('logs:DescribeLogStreams',)
243 
244     def process(self, resources, event=None):
245         client = local_session(self.manager.session_factory).client('logs')
246         self.date_threshold = datetime.utcnow() - timedelta(
247             days=self.data['days'])
248         return [r for r in resources if self.check_group(client, r)]
249 
250     def check_group(self, client, group):
251         streams = client.describe_log_streams(
252             logGroupName=group['logGroupName'],
253             orderBy='LastEventTime',
254             descending=True,
255             limit=3).get('logStreams')
256         group['streams'] = streams
257         if not streams:
258             last_timestamp = group['creationTime']
259         elif streams[0]['storedBytes'] == 0:
260             last_timestamp = streams[0]['creationTime']
261         else:
262             last_timestamp = streams[0]['lastIngestionTime']
263 
264         last_write = datetime.fromtimestamp(last_timestamp / 1000.0)
265         group['lastWrite'] = last_write
266         return self.date_threshold > last_write
267 
268 
269 @LogGroup.filter_registry.register('cross-account')
270 class LogCrossAccountFilter(CrossAccountAccessFilter):
271 
272     schema = type_schema(
273         'cross-account',
274         # white list accounts
275         whitelist_from=ValuesFrom.schema,
276         whitelist={'type': 'array', 'items': {'type': 'string'}})
277 
278     permissions = ('logs:DescribeSubscriptionFilters',)
279 
280     def process(self, resources, event=None):
281         client = local_session(self.manager.session_factory).client('logs')
282         accounts = self.get_accounts()
283         results = []
284         with self.executor_factory(max_workers=2) as w:
285             futures = []
286             for rset in chunks(resources, 50):
287                 futures.append(
288                     w.submit(
289                         self.process_resource_set, client, accounts, rset))
290             for f in as_completed(futures):
291                 if f.exception():
292                     self.log.error(
293                         "Error checking log groups cross-account %s",
294                         f.exception())
295                     continue
296                 results.extend(f.result())
297         return results
298 
299     def process_resource_set(self, client, accounts, resources):
300         results = []
301         for r in resources:
302             found = False
303             filters = self.manager.retry(
304                 client.describe_subscription_filters,
305                 logGroupName=r['logGroupName']).get('subscriptionFilters', ())
306             for f in filters:
307                 if 'destinationArn' not in f:
308                     continue
309                 account_id = f['destinationArn'].split(':', 5)[4]
310                 if account_id not in accounts:
311                     r.setdefault('c7n:CrossAccountViolations', []).append(
312                         account_id)
313                     found = True
314             if found:
315                 results.append(r)
316         return results
317
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/c7n/resources/cw.py b/c7n/resources/cw.py
--- a/c7n/resources/cw.py
+++ b/c7n/resources/cw.py
@@ -17,7 +17,7 @@
 from datetime import datetime, timedelta
 
 from c7n.actions import BaseAction
-from c7n.filters import Filter
+from c7n.filters import Filter, MetricsFilter
 from c7n.filters.iamaccess import CrossAccountAccessFilter
 from c7n.query import QueryResourceManager, ChildResourceManager
 from c7n.manager import resources
@@ -89,8 +89,15 @@
         name = "Name"
         id = "Name"
         filter_name = "NamePrefix"
-        filer_type = "scalar"
-        dimension = "RuleName"
+        filter_type = "scalar"
+        dimension = None
+
+
+@EventRule.filter_registry.register('metrics')
+class EventRuleMetrics(MetricsFilter):
+
+    def get_dimensions(self, resource):
+        return [{'Name': 'RuleName', 'Value': resource['Name']}]
 
 
 @resources.register('event-rule-target')
@@ -281,7 +288,7 @@
         client = local_session(self.manager.session_factory).client('logs')
         accounts = self.get_accounts()
         results = []
-        with self.executor_factory(max_workers=2) as w:
+        with self.executor_factory(max_workers=1) as w:
             futures = []
             for rset in chunks(resources, 50):
                 futures.append(

{"golden_diff": "diff --git a/c7n/resources/cw.py b/c7n/resources/cw.py\n--- a/c7n/resources/cw.py\n+++ b/c7n/resources/cw.py\n@@ -17,7 +17,7 @@\n from datetime import datetime, timedelta\n \n from c7n.actions import BaseAction\n-from c7n.filters import Filter\n+from c7n.filters import Filter, MetricsFilter\n from c7n.filters.iamaccess import CrossAccountAccessFilter\n from c7n.query import QueryResourceManager, ChildResourceManager\n from c7n.manager import resources\n@@ -89,8 +89,15 @@\n         name = \"Name\"\n         id = \"Name\"\n         filter_name = \"NamePrefix\"\n-        filer_type = \"scalar\"\n-        dimension = \"RuleName\"\n+        filter_type = \"scalar\"\n+        dimension = None\n+\n+\n+@EventRule.filter_registry.register('metrics')\n+class EventRuleMetrics(MetricsFilter):\n+\n+    def get_dimensions(self, resource):\n+        return [{'Name': 'RuleName', 'Value': resource['Name']}]\n \n \n @resources.register('event-rule-target')\n@@ -281,7 +288,7 @@\n         client = local_session(self.manager.session_factory).client('logs')\n         accounts = self.get_accounts()\n         results = []\n-        with self.executor_factory(max_workers=2) as w:\n+        with self.executor_factory(max_workers=1) as w:\n             futures = []\n             for rset in chunks(resources, 50):\n                 futures.append(\n", "issue": "Lambda error processing event-rule resource\nI'm facing an error when using the aws.event-rule resource in cloudtrail mode:\r\nthe lambda function exits with this error after getting the resource_id:\r\nAttributeError: type object 'resource_type' has no attribute 'filter_type'\r\n\r\nCan you help me making this work ?\r\n\r\nHere is the detailed stacktrace:\r\n````\r\ntype object 'resource_type' has no attribute 'filter_type': AttributeError\r\nTraceback (most recent call last):\r\nFile \"/var/task/custodian_policy.py\", line 4, in run\r\nreturn handler.dispatch_event(event, context)\r\nFile \"/var/task/c7n/handler.py\", line 91, in dispatch_event\r\np.push(event, context)\r\nFile \"/var/task/c7n/policy.py\", line 776, in push\r\nreturn mode.run(event, lambda_ctx)\r\nFile \"/var/task/c7n/policy.py\", line 441, in run\r\nresources = self.resolve_resources(event)\r\nFile \"/var/task/c7n/policy.py\", line 418, in resolve_resources\r\nresources = self.policy.resource_manager.get_resources(resource_ids)\r\nFile \"/var/task/c7n/query.py\", line 441, in get_resources\r\nresources = self.source.get_resources(ids)\r\nFile \"/var/task/c7n/query.py\", line 212, in get_resources\r\nreturn self.query.get(self.manager, ids)\r\nFile \"/var/task/c7n/query.py\", line 93, in get\r\nif m.filter_type == 'list':\r\nAttributeError: type object 'resource_type' has no attribute 'filter_type'\r\n\r\n````\r\n\r\nHere is the policy i'm using:\r\n```\r\npolicies:\r\n  - name: tlogs-modify-prefixrules\r\n    resource: aws.event-rule\r\n    filters:\r\n      - or: \r\n        - type: event\r\n          key: detail.requestParameters.name\r\n          value: \"prefix-.*\"\r\n          op: Regex\r\n        - type: value\r\n          key: detail.requestParameters.rule\r\n          value: \"prefix-.*\"\r\n          op: Regex\r\n    mode:\r\n      function-prefix: \"prefix-\"\r\n      type: cloudtrail\r\n      role: arn:aws:iam::{account_id}:role/prefix/prefix_custodian_core\r\n      events:\r\n        - source: events.amazonaws.com\r\n          event: DeleteRule\r\n          ids: requestParameters.name\r\n        - source: events.amazonaws.com\r\n          event: DisableRule\r\n          ids: requestParameters.name\r\n        - source: events.amazonaws.com\r\n          event: RemoveTargets\r\n          ids: requestParameters.name\r\n        - source: events.amazonaws.com\r\n          event: PutRule\r\n          ids: requestParameters.name\r\n    description: |\r\n          Alerts when prefix event-rules are modified    \r\n    actions:\r\n      - type: invoke-lambda\r\n        function: prefix-lambda-notifier\r\n```\r\n\n", "before_files": [{"content": "# Copyright 2016-2017 Capital One Services, LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nfrom concurrent.futures import as_completed\nfrom datetime import datetime, timedelta\n\nfrom c7n.actions import BaseAction\nfrom c7n.filters import Filter\nfrom c7n.filters.iamaccess import CrossAccountAccessFilter\nfrom c7n.query import QueryResourceManager, ChildResourceManager\nfrom c7n.manager import resources\nfrom c7n.resolver import ValuesFrom\nfrom c7n.tags import universal_augment, register_universal_tags\nfrom c7n.utils import type_schema, local_session, chunks, get_retry\n\n\n@resources.register('alarm')\nclass Alarm(QueryResourceManager):\n\n    class resource_type(object):\n        service = 'cloudwatch'\n        type = 'alarm'\n        enum_spec = ('describe_alarms', 'MetricAlarms', None)\n        id = 'AlarmArn'\n        filter_name = 'AlarmNames'\n        filter_type = 'list'\n        name = 'AlarmName'\n        date = 'AlarmConfigurationUpdatedTimestamp'\n        dimension = None\n        config_type = 'AWS::CloudWatch::Alarm'\n\n    retry = staticmethod(get_retry(('Throttled',)))\n\n\n@Alarm.action_registry.register('delete')\nclass AlarmDelete(BaseAction):\n    \"\"\"Delete a cloudwatch alarm.\n\n    :example:\n\n    .. code-block:: yaml\n\n            policies:\n              - name: cloudwatch-delete-stale-alarms\n                resource: alarm\n                filters:\n                  - type: value\n                    value_type: age\n                    key: StateUpdatedTimestamp\n                    value: 30\n                    op: ge\n                  - StateValue: INSUFFICIENT_DATA\n                actions:\n                  - delete\n    \"\"\"\n\n    schema = type_schema('delete')\n    permissions = ('cloudwatch:DeleteAlarms',)\n\n    def process(self, resources):\n        client = local_session(\n            self.manager.session_factory).client('cloudwatch')\n\n        for resource_set in chunks(resources, size=100):\n            self.manager.retry(\n                client.delete_alarms,\n                AlarmNames=[r['AlarmName'] for r in resource_set])\n\n\n@resources.register('event-rule')\nclass EventRule(QueryResourceManager):\n\n    class resource_type(object):\n        service = 'events'\n        type = 'event-rule'\n        enum_spec = ('list_rules', 'Rules', None)\n        name = \"Name\"\n        id = \"Name\"\n        filter_name = \"NamePrefix\"\n        filer_type = \"scalar\"\n        dimension = \"RuleName\"\n\n\n@resources.register('event-rule-target')\nclass EventRuleTarget(ChildResourceManager):\n\n    class resource_type(object):\n        service = 'events'\n        type = 'event-rule-target'\n        enum_spec = ('list_targets_by_rule', 'Targets', None)\n        parent_spec = ('event-rule', 'Rule', True)\n        name = id = 'Id'\n        dimension = None\n        filter_type = filter_name = None\n\n\n@EventRuleTarget.filter_registry.register('cross-account')\nclass CrossAccountFilter(CrossAccountAccessFilter):\n\n    schema = type_schema(\n        'cross-account',\n        # white list accounts\n        whitelist_from=ValuesFrom.schema,\n        whitelist={'type': 'array', 'items': {'type': 'string'}})\n\n    # dummy permission\n    permissions = ('events:ListTargetsByRule',)\n\n    def __call__(self, r):\n        account_id = r['Arn'].split(':', 5)[4]\n        return account_id not in self.accounts\n\n\n@EventRuleTarget.action_registry.register('delete')\nclass DeleteTarget(BaseAction):\n\n    schema = type_schema('delete')\n    permissions = ('events:RemoveTargets',)\n\n    def process(self, resources):\n        client = local_session(self.manager.session_factory).client('events')\n        rule_targets = {}\n        for r in resources:\n            rule_targets.setdefault(r['c7n:parent-id'], []).append(r['Id'])\n\n        for rule_id, target_ids in rule_targets.items():\n            client.remove_targets(\n                Ids=target_ids,\n                Rule=rule_id)\n\n\n@resources.register('log-group')\nclass LogGroup(QueryResourceManager):\n\n    class resource_type(object):\n        service = 'logs'\n        type = 'log-group'\n        enum_spec = ('describe_log_groups', 'logGroups', None)\n        name = 'logGroupName'\n        id = 'arn'\n        filter_name = 'logGroupNamePrefix'\n        filter_type = 'scalar'\n        dimension = 'LogGroupName'\n        date = 'creationTime'\n\n    augment = universal_augment\n\n    def get_arns(self, resources):\n        # log group arn in resource describe has ':*' suffix, not all\n        # apis can use that form, so normalize to standard arn.\n        return [r['arn'][:-2] for r in resources]\n\n\nregister_universal_tags(LogGroup.filter_registry, LogGroup.action_registry)\n\n\n@LogGroup.action_registry.register('retention')\nclass Retention(BaseAction):\n    \"\"\"Action to set the retention period (in days) for CloudWatch log groups\n\n    :example:\n\n    .. code-block:: yaml\n\n            policies:\n              - name: cloudwatch-set-log-group-retention\n                resource: log-group\n                actions:\n                  - type: retention\n                    days: 200\n    \"\"\"\n\n    schema = type_schema('retention', days={'type': 'integer'})\n    permissions = ('logs:PutRetentionPolicy',)\n\n    def process(self, resources):\n        client = local_session(self.manager.session_factory).client('logs')\n        days = self.data['days']\n        for r in resources:\n            client.put_retention_policy(\n                logGroupName=r['logGroupName'],\n                retentionInDays=days)\n\n\n@LogGroup.action_registry.register('delete')\nclass Delete(BaseAction):\n    \"\"\"\n\n    :example:\n\n    .. code-block:: yaml\n\n            policies:\n              - name: cloudwatch-delete-stale-log-group\n                resource: log-group\n                filters:\n                  - type: last-write\n                    days: 182.5\n                actions:\n                  - delete\n    \"\"\"\n\n    schema = type_schema('delete')\n    permissions = ('logs:DeleteLogGroup',)\n\n    def process(self, resources):\n        client = local_session(self.manager.session_factory).client('logs')\n        for r in resources:\n            client.delete_log_group(logGroupName=r['logGroupName'])\n\n\n@LogGroup.filter_registry.register('last-write')\nclass LastWriteDays(Filter):\n    \"\"\"Filters CloudWatch log groups by last write\n\n    :example:\n\n    .. code-block:: yaml\n\n            policies:\n              - name: cloudwatch-stale-groups\n                resource: log-group\n                filters:\n                  - type: last-write\n                    days: 60\n    \"\"\"\n\n    schema = type_schema(\n        'last-write', days={'type': 'number'})\n    permissions = ('logs:DescribeLogStreams',)\n\n    def process(self, resources, event=None):\n        client = local_session(self.manager.session_factory).client('logs')\n        self.date_threshold = datetime.utcnow() - timedelta(\n            days=self.data['days'])\n        return [r for r in resources if self.check_group(client, r)]\n\n    def check_group(self, client, group):\n        streams = client.describe_log_streams(\n            logGroupName=group['logGroupName'],\n            orderBy='LastEventTime',\n            descending=True,\n            limit=3).get('logStreams')\n        group['streams'] = streams\n        if not streams:\n            last_timestamp = group['creationTime']\n        elif streams[0]['storedBytes'] == 0:\n            last_timestamp = streams[0]['creationTime']\n        else:\n            last_timestamp = streams[0]['lastIngestionTime']\n\n        last_write = datetime.fromtimestamp(last_timestamp / 1000.0)\n        group['lastWrite'] = last_write\n        return self.date_threshold > last_write\n\n\n@LogGroup.filter_registry.register('cross-account')\nclass LogCrossAccountFilter(CrossAccountAccessFilter):\n\n    schema = type_schema(\n        'cross-account',\n        # white list accounts\n        whitelist_from=ValuesFrom.schema,\n        whitelist={'type': 'array', 'items': {'type': 'string'}})\n\n    permissions = ('logs:DescribeSubscriptionFilters',)\n\n    def process(self, resources, event=None):\n        client = local_session(self.manager.session_factory).client('logs')\n        accounts = self.get_accounts()\n        results = []\n        with self.executor_factory(max_workers=2) as w:\n            futures = []\n            for rset in chunks(resources, 50):\n                futures.append(\n                    w.submit(\n                        self.process_resource_set, client, accounts, rset))\n            for f in as_completed(futures):\n                if f.exception():\n                    self.log.error(\n                        \"Error checking log groups cross-account %s\",\n                        f.exception())\n                    continue\n                results.extend(f.result())\n        return results\n\n    def process_resource_set(self, client, accounts, resources):\n        results = []\n        for r in resources:\n            found = False\n            filters = self.manager.retry(\n                client.describe_subscription_filters,\n                logGroupName=r['logGroupName']).get('subscriptionFilters', ())\n            for f in filters:\n                if 'destinationArn' not in f:\n                    continue\n                account_id = f['destinationArn'].split(':', 5)[4]\n                if account_id not in accounts:\n                    r.setdefault('c7n:CrossAccountViolations', []).append(\n                        account_id)\n                    found = True\n            if found:\n                results.append(r)\n        return results\n", "path": "c7n/resources/cw.py"}], "after_files": [{"content": "# Copyright 2016-2017 Capital One Services, LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nfrom concurrent.futures import as_completed\nfrom datetime import datetime, timedelta\n\nfrom c7n.actions import BaseAction\nfrom c7n.filters import Filter, MetricsFilter\nfrom c7n.filters.iamaccess import CrossAccountAccessFilter\nfrom c7n.query import QueryResourceManager, ChildResourceManager\nfrom c7n.manager import resources\nfrom c7n.resolver import ValuesFrom\nfrom c7n.tags import universal_augment, register_universal_tags\nfrom c7n.utils import type_schema, local_session, chunks, get_retry\n\n\n@resources.register('alarm')\nclass Alarm(QueryResourceManager):\n\n    class resource_type(object):\n        service = 'cloudwatch'\n        type = 'alarm'\n        enum_spec = ('describe_alarms', 'MetricAlarms', None)\n        id = 'AlarmArn'\n        filter_name = 'AlarmNames'\n        filter_type = 'list'\n        name = 'AlarmName'\n        date = 'AlarmConfigurationUpdatedTimestamp'\n        dimension = None\n        config_type = 'AWS::CloudWatch::Alarm'\n\n    retry = staticmethod(get_retry(('Throttled',)))\n\n\n@Alarm.action_registry.register('delete')\nclass AlarmDelete(BaseAction):\n    \"\"\"Delete a cloudwatch alarm.\n\n    :example:\n\n    .. code-block:: yaml\n\n            policies:\n              - name: cloudwatch-delete-stale-alarms\n                resource: alarm\n                filters:\n                  - type: value\n                    value_type: age\n                    key: StateUpdatedTimestamp\n                    value: 30\n                    op: ge\n                  - StateValue: INSUFFICIENT_DATA\n                actions:\n                  - delete\n    \"\"\"\n\n    schema = type_schema('delete')\n    permissions = ('cloudwatch:DeleteAlarms',)\n\n    def process(self, resources):\n        client = local_session(\n            self.manager.session_factory).client('cloudwatch')\n\n        for resource_set in chunks(resources, size=100):\n            self.manager.retry(\n                client.delete_alarms,\n                AlarmNames=[r['AlarmName'] for r in resource_set])\n\n\n@resources.register('event-rule')\nclass EventRule(QueryResourceManager):\n\n    class resource_type(object):\n        service = 'events'\n        type = 'event-rule'\n        enum_spec = ('list_rules', 'Rules', None)\n        name = \"Name\"\n        id = \"Name\"\n        filter_name = \"NamePrefix\"\n        filter_type = \"scalar\"\n        dimension = None\n\n\n@EventRule.filter_registry.register('metrics')\nclass EventRuleMetrics(MetricsFilter):\n\n    def get_dimensions(self, resource):\n        return [{'Name': 'RuleName', 'Value': resource['Name']}]\n\n\n@resources.register('event-rule-target')\nclass EventRuleTarget(ChildResourceManager):\n\n    class resource_type(object):\n        service = 'events'\n        type = 'event-rule-target'\n        enum_spec = ('list_targets_by_rule', 'Targets', None)\n        parent_spec = ('event-rule', 'Rule', True)\n        name = id = 'Id'\n        dimension = None\n        filter_type = filter_name = None\n\n\n@EventRuleTarget.filter_registry.register('cross-account')\nclass CrossAccountFilter(CrossAccountAccessFilter):\n\n    schema = type_schema(\n        'cross-account',\n        # white list accounts\n        whitelist_from=ValuesFrom.schema,\n        whitelist={'type': 'array', 'items': {'type': 'string'}})\n\n    # dummy permission\n    permissions = ('events:ListTargetsByRule',)\n\n    def __call__(self, r):\n        account_id = r['Arn'].split(':', 5)[4]\n        return account_id not in self.accounts\n\n\n@EventRuleTarget.action_registry.register('delete')\nclass DeleteTarget(BaseAction):\n\n    schema = type_schema('delete')\n    permissions = ('events:RemoveTargets',)\n\n    def process(self, resources):\n        client = local_session(self.manager.session_factory).client('events')\n        rule_targets = {}\n        for r in resources:\n            rule_targets.setdefault(r['c7n:parent-id'], []).append(r['Id'])\n\n        for rule_id, target_ids in rule_targets.items():\n            client.remove_targets(\n                Ids=target_ids,\n                Rule=rule_id)\n\n\n@resources.register('log-group')\nclass LogGroup(QueryResourceManager):\n\n    class resource_type(object):\n        service = 'logs'\n        type = 'log-group'\n        enum_spec = ('describe_log_groups', 'logGroups', None)\n        name = 'logGroupName'\n        id = 'arn'\n        filter_name = 'logGroupNamePrefix'\n        filter_type = 'scalar'\n        dimension = 'LogGroupName'\n        date = 'creationTime'\n\n    augment = universal_augment\n\n    def get_arns(self, resources):\n        # log group arn in resource describe has ':*' suffix, not all\n        # apis can use that form, so normalize to standard arn.\n        return [r['arn'][:-2] for r in resources]\n\n\nregister_universal_tags(LogGroup.filter_registry, LogGroup.action_registry)\n\n\n@LogGroup.action_registry.register('retention')\nclass Retention(BaseAction):\n    \"\"\"Action to set the retention period (in days) for CloudWatch log groups\n\n    :example:\n\n    .. code-block:: yaml\n\n            policies:\n              - name: cloudwatch-set-log-group-retention\n                resource: log-group\n                actions:\n                  - type: retention\n                    days: 200\n    \"\"\"\n\n    schema = type_schema('retention', days={'type': 'integer'})\n    permissions = ('logs:PutRetentionPolicy',)\n\n    def process(self, resources):\n        client = local_session(self.manager.session_factory).client('logs')\n        days = self.data['days']\n        for r in resources:\n            client.put_retention_policy(\n                logGroupName=r['logGroupName'],\n                retentionInDays=days)\n\n\n@LogGroup.action_registry.register('delete')\nclass Delete(BaseAction):\n    \"\"\"\n\n    :example:\n\n    .. code-block:: yaml\n\n            policies:\n              - name: cloudwatch-delete-stale-log-group\n                resource: log-group\n                filters:\n                  - type: last-write\n                    days: 182.5\n                actions:\n                  - delete\n    \"\"\"\n\n    schema = type_schema('delete')\n    permissions = ('logs:DeleteLogGroup',)\n\n    def process(self, resources):\n        client = local_session(self.manager.session_factory).client('logs')\n        for r in resources:\n            client.delete_log_group(logGroupName=r['logGroupName'])\n\n\n@LogGroup.filter_registry.register('last-write')\nclass LastWriteDays(Filter):\n    \"\"\"Filters CloudWatch log groups by last write\n\n    :example:\n\n    .. code-block:: yaml\n\n            policies:\n              - name: cloudwatch-stale-groups\n                resource: log-group\n                filters:\n                  - type: last-write\n                    days: 60\n    \"\"\"\n\n    schema = type_schema(\n        'last-write', days={'type': 'number'})\n    permissions = ('logs:DescribeLogStreams',)\n\n    def process(self, resources, event=None):\n        client = local_session(self.manager.session_factory).client('logs')\n        self.date_threshold = datetime.utcnow() - timedelta(\n            days=self.data['days'])\n        return [r for r in resources if self.check_group(client, r)]\n\n    def check_group(self, client, group):\n        streams = client.describe_log_streams(\n            logGroupName=group['logGroupName'],\n            orderBy='LastEventTime',\n            descending=True,\n            limit=3).get('logStreams')\n        group['streams'] = streams\n        if not streams:\n            last_timestamp = group['creationTime']\n        elif streams[0]['storedBytes'] == 0:\n            last_timestamp = streams[0]['creationTime']\n        else:\n            last_timestamp = streams[0]['lastIngestionTime']\n\n        last_write = datetime.fromtimestamp(last_timestamp / 1000.0)\n        group['lastWrite'] = last_write\n        return self.date_threshold > last_write\n\n\n@LogGroup.filter_registry.register('cross-account')\nclass LogCrossAccountFilter(CrossAccountAccessFilter):\n\n    schema = type_schema(\n        'cross-account',\n        # white list accounts\n        whitelist_from=ValuesFrom.schema,\n        whitelist={'type': 'array', 'items': {'type': 'string'}})\n\n    permissions = ('logs:DescribeSubscriptionFilters',)\n\n    def process(self, resources, event=None):\n        client = local_session(self.manager.session_factory).client('logs')\n        accounts = self.get_accounts()\n        results = []\n        with self.executor_factory(max_workers=1) as w:\n            futures = []\n            for rset in chunks(resources, 50):\n                futures.append(\n                    w.submit(\n                        self.process_resource_set, client, accounts, rset))\n            for f in as_completed(futures):\n                if f.exception():\n                    self.log.error(\n                        \"Error checking log groups cross-account %s\",\n                        f.exception())\n                    continue\n                results.extend(f.result())\n        return results\n\n    def process_resource_set(self, client, accounts, resources):\n        results = []\n        for r in resources:\n            found = False\n            filters = self.manager.retry(\n                client.describe_subscription_filters,\n                logGroupName=r['logGroupName']).get('subscriptionFilters', ())\n            for f in filters:\n                if 'destinationArn' not in f:\n                    continue\n                account_id = f['destinationArn'].split(':', 5)[4]\n                if account_id not in accounts:\n                    r.setdefault('c7n:CrossAccountViolations', []).append(\n                        account_id)\n                    found = True\n            if found:\n                results.append(r)\n        return results\n", "path": "c7n/resources/cw.py"}]}