Repository navigation
chore: merge upstream cloud-custodian and restamp 0.9.53a1+bf0b50dbc #24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
caad897
ace09fe
c5f66e5
a694b6f
37231fc
3f8ee95
7e316d6
df800e9
8f458b3
3555adb
4d971e9
8f999d4
4597a9b
32c349d
32ff971
4cdfda0
de842db
2185ebd
74e83ce
4be0ee2
427a1a3
7ef6b61
e4c099e
9c22e8b
f852abe
944447a
8693ccc
7ec4027
2a85d60
951b906
6235eba
6af640c
bf0b50d
a415910
28e92b0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,11 +2,34 @@ | |
| # Copyright The Cloud Custodian Authors. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| import typing | ||
|
|
||
| from c7n import deprecated | ||
| from c7n.executor import ThreadPoolExecutor | ||
| from c7n.utils import jmespath_search | ||
|
|
||
|
|
||
| class ElementJSONSchema(typing.TypedDict, total=False): | ||
| """The shape of a filter/action's ``schema`` class attribute. | ||
|
|
||
| This is a JSON Schema (draft-07) fragment, not a standalone | ||
| document: it gets embedded into the combined policy schema (see | ||
| ``c7n.schema.process_resource``) at | ||
| ``#/definitions/resources/<resource>/(actions|filters)/<name>``, | ||
| and is what validates a single filter/action block in policy | ||
| YAML, e.g. ``{'type': 'update', 'rotationPeriod': '...'}``. | ||
|
|
||
| Elements normally build this via the ``type_schema()`` helper | ||
| (``c7n.utils.type_schema``) rather than writing it by hand. | ||
| """ | ||
|
|
||
| type: str | ||
| properties: dict[str, typing.Any] | ||
| required: list[str] | ||
| additionalProperties: bool | ||
| allOf: list[dict[str, typing.Any]] | ||
|
|
||
|
|
||
| class Element: | ||
| """Parent base class for filters and actions. | ||
| """ | ||
|
|
@@ -16,7 +39,8 @@ class Element: | |
|
|
||
| executor_factory = ThreadPoolExecutor | ||
|
|
||
| schema = {'type': 'object'} | ||
| schema: ElementJSONSchema = {'type': 'object'} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: When the package is type-checked, this closed annotation makes existing element subclasses invalid: several schemas use top-level Prompt for AI agents |
||
|
|
||
| # schema aliases get hoisted into a jsonschema definition | ||
| # location, and then referenced inline. | ||
| schema_alias = None | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,7 +6,9 @@ | |
| from concurrent.futures import as_completed | ||
| from datetime import datetime, timedelta | ||
|
|
||
| from c7n.actions import BaseAction | ||
| from botocore.exceptions import ClientError | ||
|
|
||
| from c7n.actions import AutoTagUser, BaseAction | ||
| from c7n.exceptions import PolicyValidationError | ||
| from c7n.filters import Filter, MetricsFilter | ||
| from c7n.filters.core import parse_date, ValueFilter | ||
|
|
@@ -17,7 +19,8 @@ | |
| QueryResourceManager, | ||
| TypeInfo, DescribeSource, ConfigSource, DescribeWithResourceTags) | ||
| from c7n.resolver import ValuesFrom | ||
| from c7n.tags import universal_augment | ||
| from c7n.tags import ( | ||
| RemoveTag, Tag, TagActionFilter, TagDelayedAction, universal_augment) | ||
| from c7n.utils import type_schema, local_session, chunks, get_retry, jmespath_search | ||
|
|
||
|
|
||
|
|
@@ -688,6 +691,32 @@ def process(self, resources): | |
| logGroupName=r['logGroupName'], **params) | ||
|
|
||
|
|
||
| class DescribeDashboard(DescribeSource): | ||
|
|
||
| def augment(self, resources): | ||
| # Dashboards aren't supported by the resource groups tagging api, | ||
| # fetch their tags from cloudwatch directly. | ||
| resources = super().augment(resources) | ||
| client = local_session(self.manager.session_factory).client('cloudwatch') | ||
| results = [] | ||
| for r, arn in zip(resources, self.manager.get_arns(resources)): | ||
| try: | ||
| tags = self.manager.retry( | ||
| client.list_tags_for_resource, ResourceARN=arn).get('Tags', []) | ||
| except ClientError as e: | ||
| if e.response['Error']['Code'] not in ( | ||
| 'ResourceNotFound', 'ResourceNotFoundException'): | ||
| raise | ||
| # the dashboard was deleted between enumeration and augment | ||
| self.manager.log.warning( | ||
| "Resource not found: list_tags_for_resource using %s" % { | ||
| 'ResourceARN': arn}) | ||
| continue | ||
| r['Tags'] = tags | ||
| results.append(r) | ||
| return results | ||
|
|
||
|
|
||
| @resources.register("cloudwatch-dashboard") | ||
| class CloudWatchDashboard(QueryResourceManager): | ||
| class resource_type(TypeInfo): | ||
|
|
@@ -698,14 +727,71 @@ class resource_type(TypeInfo): | |
| id = "DashboardName" | ||
| name = "DashboardName" | ||
| cfn_type = "AWS::CloudWatch::Dashboard" | ||
| universal_taggable = object() | ||
| global_resource = True | ||
| permissions_augment = ("cloudwatch:ListTagsForResource",) | ||
|
|
||
| source_mapping = { | ||
| "describe": DescribeWithResourceTags, | ||
| "describe": DescribeDashboard, | ||
| } | ||
|
|
||
|
|
||
| @CloudWatchDashboard.action_registry.register('tag') | ||
| class DashboardTag(Tag): | ||
| """Add tags to a cloudwatch dashboard | ||
|
|
||
| :example: | ||
|
|
||
| .. code-block:: yaml | ||
|
|
||
| policies: | ||
| - name: cloudwatch-dashboard-tag-owner | ||
| resource: aws.cloudwatch-dashboard | ||
| filters: | ||
| - "tag:Owner": absent | ||
| actions: | ||
| - type: tag | ||
| key: Owner | ||
| value: platform-team | ||
| """ | ||
|
|
||
| permissions = ("cloudwatch:TagResource",) | ||
|
|
||
| def process_resource_set(self, client, resource_set, tags): | ||
| for arn in self.manager.get_arns(resource_set): | ||
| self.manager.retry(client.tag_resource, ResourceARN=arn, Tags=tags) | ||
|
Comment on lines
+759
to
+761
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If a dashboard is deleted after augmentation but before mutation, the new Prompt To Fix With AIThis is a comment left during a code review.
Path: c7n/resources/cw.py
Line: 759-761
Comment:
**Dashboard deletion aborts tagging**
If a dashboard is deleted after augmentation but before mutation, the new `TagResource` call propagates `ResourceNotFound` and aborts the entire policy run. The new read path already treats this race as benign, but both the tag action here and the remove-tag action at lines 784–787 leave it unhandled. These actions should skip a dashboard that disappeared and continue processing the rest.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Handle Prompt for AI agents |
||
|
|
||
|
|
||
| @CloudWatchDashboard.action_registry.register('remove-tag') | ||
| class DashboardRemoveTag(RemoveTag): | ||
| """Remove tags from a cloudwatch dashboard | ||
|
|
||
| :example: | ||
|
|
||
| .. code-block:: yaml | ||
|
|
||
| policies: | ||
| - name: cloudwatch-dashboard-remove-owner | ||
| resource: aws.cloudwatch-dashboard | ||
| filters: | ||
| - "tag:Owner": present | ||
| actions: | ||
| - type: remove-tag | ||
| tags: [Owner] | ||
| """ | ||
|
|
||
| permissions = ("cloudwatch:UntagResource",) | ||
|
|
||
| def process_resource_set(self, client, resource_set, tag_keys): | ||
| for arn in self.manager.get_arns(resource_set): | ||
| self.manager.retry( | ||
| client.untag_resource, ResourceARN=arn, TagKeys=tag_keys) | ||
|
|
||
|
|
||
| CloudWatchDashboard.filter_registry.register('marked-for-op', TagActionFilter) | ||
| CloudWatchDashboard.action_registry.register('mark-for-op', TagDelayedAction) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Policies using Prompt for AI agents |
||
| CloudWatchDashboard.action_registry.register('auto-tag-user', AutoTagUser) | ||
|
|
||
|
|
||
| @resources.register("destination") | ||
| class Destination(QueryResourceManager): | ||
| class resource_type(TypeInfo): | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: When a vars file supplies a typed policy value such as
max-resources: "{limit}", schema validation rejects the placeholder beforep.expand_variablescan restore the integer. Passvars=varstopolicy_load, or validate only after variable expansion.Prompt for AI agents