Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
4485ba8
aws - add s3files file-system, mount-target and access-point resource…
rondodev Sep 15, 2026
46f044b
aws - add s3tables, table-bucket and table resources (#10973)
rondodev Sep 17, 2026
6cd074b
aws - eventbridge - adding event-connection resource (#11007)
rondodev Sep 17, 2026
73c8e15
aws - ssm - add tagging support for managed instances, ops items, pat…
rondodev Sep 22, 2026
f2fa1c2
adding rds-global-cluster resource (#11092)
rondodev Sep 22, 2026
8e098f4
aws - ec2 - ssm-compliance - honor advertised default for states (#10…
anxkhn Sep 22, 2026
f4d6649
wafv2 cloudfront fix (#11127)
jismail3000 Sep 23, 2026
2437497
aws - dsql-cluster - register consecutive-aws-backups filter (#11133)
rondodev Sep 24, 2026
8a74d41
gcp - add vertex-ai-metadata-store-artifact resource (#11015)
jimfulton Sep 25, 2026
88594fc
aws - organizations - pull policy types from boto model (#11042)
mattheidelbaugh Sep 25, 2026
be9e869
aws - add aiops investigation groups resource type (#10899)
mattheidelbaugh Sep 25, 2026
5217be4
aws - add qbusiness resource types (#10990)
mattheidelbaugh Sep 25, 2026
4dd35b6
azure - metric filter - support parent-scoped dimensions (#11101)
dreakj12 Sep 28, 2026
be9983a
aws - expand iot support (#11098)
mattheidelbaugh Sep 28, 2026
cc0c61c
testing - add diff coverage recipes to Makefile (#10630)
KodyMullins Sep 29, 2026
26fdff1
gcp - add workspace-user resource for Workspace MFA reporting (#10950)
jimfulton Sep 29, 2026
27a1313
azure - machine-learning-model-version - fetch anonymous resources (#…
toastdriven Sep 29, 2026
d4d53ff
azure - add machine learning data container archive action (#11063)
jimfulton Sep 29, 2026
2e6971f
azure - machine-learning-job - include archived resources (#11100)
jimfulton Sep 29, 2026
a1530aa
aws - budget - surface notifications and filter expression (#11116)
mrinalpravi Sep 30, 2026
f8bb1d3
azure - network-security-group - add remove-rules action (#11137)
liamade Sep 30, 2026
81c4bcc
azure - add machine learning job cancel action (#11059)
jimfulton Sep 30, 2026
5541cb9
fix(kube) - include the resource type in the resource cache key (#11168)
howbazaar Oct 1, 2026
d02cc3d
core - resolver - include request headers in uri cache key (#11148)
enivek Oct 1, 2026
ec8d701
releng - prep 0.9.53 release (#11162)
ajkerrigan Oct 1, 2026
bf126f3
aws - sagemaker-endpoint - add metrics filter (#11078)
jimfulton Oct 1, 2026
ac8d120
gcp - kms-cryptokey - add set-iam-policy action (#11150)
KodyMullins Oct 7, 2026
5e2b8fa
aws - securityhub - truncate oversize string detail values (#11161)
jtroup Oct 7, 2026
4deef78
aws - security-group - paginate describe_security_groups (#11176)
nihalrm Oct 7, 2026
fb151ca
gcp - organization and project - add dlp-discovery-configs filter (#1…
liamade Oct 7, 2026
b2d948f
aws - invoke-lambda - fix KeyError when a qualifier is specified (#11…
UsmanQT Oct 8, 2026
631a70c
azure - disk - add snapshot action (#11190)
liamade Oct 8, 2026
617ff1a
gcp - bucket - add set public access action (#11189)
KodyMullins Oct 8, 2026
a75de19
gcp - vertex-ai-publisher-model - add metrics filter support (#11107)
dreakj12 Oct 9, 2026
4ec03b6
aws - ec2 - update tag back-fill and enable for cloud trail mode (#11…
jimfulton Oct 13, 2026
556f808
gcp - project - scope metrics filter to one project at a time (#11153)
jimfulton Oct 13, 2026
5380fef
azure - add machine learning compute cluster resource (#11117)
jimfulton Oct 13, 2026
5d868ef
azure - add machine-learning-environment-version resource (#11159)
liamade Oct 13, 2026
0249e62
aws - cloudtrail - add set-event-selectors action (#11195)
KodyMullins Oct 13, 2026
9464343
gcp - loadbalancer-target-https-proxy - add ssl-policy filter (#11163)
KodyMullins Oct 13, 2026
9105ca8
aws - kafka - add kafka-broker child resource with metrics filter (#1…
scott-sixfeetup Oct 14, 2026
5ce2e42
gcp - log-project-sink - add sink-overlap filter (#10673)
KodyMullins Oct 14, 2026
8bf784b
azure - vm - allow resize to accept a map (#11191)
KodyMullins Oct 14, 2026
0a3490c
aws - account - add set-access-analyzer action (#11178)
proton0210 Oct 17, 2026
bcae859
gcp - instance - add set-ip-forwarding action (#10640)
mgogoulos Oct 17, 2026
435bfa2
aws - ami - add set-disabled action (#10487)
scott-sixfeetup Oct 17, 2026
9631a4b
azure - add machine-learning-schedule resource (#11214)
liamade Oct 17, 2026
bb5cd95
aws - sagemaker-space - add update action (#11212)
liamade Oct 17, 2026
1f5bd1d
aws - cfn - skip ROLLBACK_COMPLETE stacks in tag actions (#11215)
sayaksatpathi Oct 17, 2026
2af058a
gcp - app-engine-service-version - fetch full view and add audit mode…
liamade Oct 19, 2026
c62b221
aws - secrets-manager - resolve secrets by arn as well as name (#11182)
Saadanjum0 Oct 19, 2026
bcbfb73
aws - cloudtrail - skip shadow trails in tag actions (#11187)
UsmanQT Oct 19, 2026
9c41e7e
core - value - add duration value_type (#11223)
liamade Oct 20, 2026
d61e7e4
aws - sagemaker-endpoint - add async inference metrics (#11194)
liamade Oct 20, 2026
cfc9f90
aws - vpc-endpoint - skip service-details filter on endpoints without…
areddyn Oct 20, 2026
eb662dd
aws - apigw-domain-name - use API-provided ARN for private custom dom…
cvas22 Oct 20, 2026
b377a9d
core - resolver - skip blank rows in value_from csv2dict (#11227)
Thanatos9404 Oct 20, 2026
c7401e1
azure - firewall - stop set-firewall-rules corrupting rules across re…
liamade Oct 20, 2026
aa8512a
azure - machine-learning-job - avoid crashing on recently deleted par…
toastdriven Oct 20, 2026
4285239
gcp - set-iam-policy - add auditConfig support for Data Access audit …
mgogoulos Oct 20, 2026
95a39af
gcp - subnet - set-flow-log - add logConfig parameters (#10642)
mgogoulos Oct 20, 2026
142aa23
k8s - add api call stats and metrics (#11197)
howbazaar Oct 20, 2026
addc02c
gcp - add api call stats and metrics (#11242)
howbazaar Oct 20, 2026
0fa7d19
azure - add api call stats and metrics (#11243)
howbazaar Oct 21, 2026
851b930
Merge remote-tracking branch 'origin/main' into kevin/chore/c7n-0.9.54a1
kevin-birkett Oct 22, 2026
15b4f8a
chore: bump prerelease versions to 0.9.54a1+0fa7d19f5 (ENG-9376)
kevin-birkett Oct 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
env:
UV_VERSION: "0.7.12"
UV_VERSION: "0.12.19"
DEFAULT_PY_VERSION: "3.12"

permissions: {}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/functional.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
workflow_dispatch: {}

env:
UV_VERSION: "0.7.12"
UV_VERSION: "0.12.19"
DEFAULT_PY_VERSION: "3.12"

permissions: {}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: "Release"
on:
workflow_dispatch: {}
env:
UV_VERSION: "0.7.12"
UV_VERSION: "0.12.19"
DEFAULT_PY_VERSION: "3.12"

permissions: {}
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
.eggs/
.terraform
.terraform.lock.hcl
*.tfstate*
# c7n/version.py
src/
deps/
Expand Down
43 changes: 43 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,49 @@ test-coverage:
--cov tools/c7n_oci/c7n_oci \
tests tools $(ARGS)

diff-coverage:
@echo "Running tests with coverage for changed files..."
@CHANGED_TEST_FILES=$$(git diff --name-only origin/main | grep -E "test_.*\.py$$" || true); \
if [ -z "$$CHANGED_TEST_FILES" ]; then \
echo "Error: No test files changed compared to origin/main"; \
echo "Use 'make diff-coverage-full' to run all tests for a provider, or specify TEST_PATH=<path>"; \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The no-test branch points to nonexistent make diff-coverage-full and TEST_PATH interfaces, so developers cannot follow its recovery instructions. Point to an existing command or implement those interfaces.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Makefile, line 53:

<comment>The no-test branch points to nonexistent `make diff-coverage-full` and `TEST_PATH` interfaces, so developers cannot follow its recovery instructions. Point to an existing command or implement those interfaces.</comment>

<file context>
@@ -45,6 +45,49 @@ test-coverage:
+	@CHANGED_TEST_FILES=$$(git diff --name-only origin/main | grep -E "test_.*\.py$$" || true); \
+	if [ -z "$$CHANGED_TEST_FILES" ]; then \
+		echo "Error: No test files changed compared to origin/main"; \
+		echo "Use 'make diff-coverage-full' to run all tests for a provider, or specify TEST_PATH=<path>"; \
+		exit 1; \
+	fi; \
</file context>
Suggested change
echo "Use 'make diff-coverage-full' to run all tests for a provider, or specify TEST_PATH=<path>"; \
echo "Run 'make test-coverage' for full coverage, or invoke 'uv run pytest <path>' directly"; \

exit 1; \
fi; \
echo "Running changed test files:"; \
echo "$$CHANGED_TEST_FILES" | sed 's/^/ - /'; \
uv run pytest $$CHANGED_TEST_FILES -n auto \
--cov-config .coveragerc \
--cov-report xml \
--cov .
@echo ""
@echo "Generating diff coverage report..."
@uv tool run diff-cover coverage.xml \
--compare-branch=origin/main \
--src-roots . \
--exclude "tests/**" \
--exclude "tools/**/tests/**" \
--exclude "**/test_*.py" \
--html-report htmlcov/diff-coverage.html
@echo ""
@echo "HTML report generated: htmlcov/diff-coverage.html"
@echo "Run 'make view-diff-coverage-linux' to open the report in your browser"

view-coverage-linux:
@if [ ! -f htmlcov/index.html ]; then \
echo "Error: htmlcov/index.html not found"; \
echo "Run 'make test-coverage' first to generate the full coverage report"; \
exit 1; \
fi
@xdg-open htmlcov/index.html 2>/dev/null || open htmlcov/index.html 2>/dev/null || echo "Please open htmlcov/index.html manually"

view-diff-coverage-linux:
@if [ ! -f htmlcov/diff-coverage.html ]; then \
echo "Error: htmlcov/diff-coverage.html not found"; \
echo "Run 'make diff-coverage' first to generate the diff coverage report"; \
exit 1; \
fi
@xdg-open htmlcov/diff-coverage.html 2>/dev/null || open htmlcov/diff-coverage.html 2>/dev/null || echo "Please open htmlcov/diff-coverage.html manually"

test-functional:
# note this will provision real resources in a cloud environment
C7N_FUNCTIONAL=yes AWS_DEFAULT_REGION=us-east-2 pytest tests -m functional $(ARGS)
Expand Down
2 changes: 1 addition & 1 deletion c7n/actions/invoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def process(self, resources, event=None):

params = dict(FunctionName=self.data['function'])
if self.data.get('qualifier'):
params['Qualifier'] = self.data['Qualifier']
params['Qualifier'] = self.data['qualifier']

if self.data.get('async', True):
params['InvocationType'] = 'Event'
Expand Down
171 changes: 171 additions & 0 deletions c7n/data/sagemaker_metrics.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# The metrics SageMaker publishes, and the dimensions that identify them.
#
# Maintained by hand/agent.
# See tests/terraform/sagemaker_endpoint_metrics/probe_metrics.py
#
# Each section names the metric table it came from. The namespace is in the
# prose above that table. A dimension entry is one row of the following
# dimensions table: the names identifying a single time series.
#
# kind names a variety of the resource, for metrics only some varieties
# publish; leave it out when every variety publishes the section's metrics.
# Endpoints come in three kinds: a "classic" endpoint attaches a model to each
# production variant, while an "inference-component" endpoint carries an
# execution role and hosts models as components placed on a variant. The two
# report invocations under different dimensions, which the documentation
# lists together without saying which applies when. An "async" endpoint
# carries an AsyncInferenceConfig; its metrics have a page of their own:
# https://docs.aws.amazon.com/sagemaker/latest/dg/async-inference-monitor.html
#
# Only the metrics of resources c7n filters on are listed; the page also
# covers jobs, Ground Truth, Feature Store and pipelines.
#
# The documentation's dimensions tables say which dimensions a metric can be
# filtered by, not which sets exist, and CloudWatch identifies a series by
# the exact set. Only sets observed to carry data are listed here -- see
# tests/terraform/sagemaker_endpoint_metrics/probe_metrics.py. Notably
# instance type is published only alongside AvailabilityZone and Region, so
# the documented "EndpointName, VariantName, InstanceType" is absent.

- title: Endpoint metrics
resource: sagemaker-endpoint
namespace: /aws/sagemaker/Endpoints
metrics:
- CPUUtilization
- DiskUtilization
- GPUMemoryUtilization
- GPUUtilization
- MemoryUtilization
dimensions:
- EndpointName, VariantName

# Reservations exist only where a component reserves part of an instance,
# and the normalized metrics report utilization against them. Observed for a
# component-hosting endpoint and not for a classic one.
- title: Endpoint metrics
resource: sagemaker-endpoint
kind: inference-component
namespace: /aws/sagemaker/Endpoints
metrics:
- CPUReservation
- CPUUtilizationNormalized
- GPUMemoryUtilizationNormalized
- GPUReservation
- GPUUtilizationNormalized
- MemoryReservation
dimensions:
- EndpointName, VariantName

- title: Endpoint invocation metrics
resource: sagemaker-endpoint
kind: classic
namespace: AWS/SageMaker
metrics:
- ConcurrentRequestsPerModel
- Invocation4XXErrors
- Invocation5XXErrors
- InvocationModelErrors
- Invocations
- InvocationsPerInstance
- ModelLatency
- ModelSetupTime
- OverheadLatency
- MidStreamErrors
- FirstChunkLatency
- FirstChunkModelLatency
- FirstChunkOverheadLatency
dimensions:
- EndpointName, VariantName

# A component-hosting endpoint reports its invocations per component, with
# no EndpointName dimension.
- title: Endpoint invocation metrics
resource: sagemaker-endpoint
kind: inference-component
namespace: AWS/SageMaker
metrics:
- ConcurrentRequestsPerCopy
- Invocation4XXErrors
- Invocation5XXErrors
- InvocationModelErrors
- Invocations
- InvocationsPerCopy
- ModelLatency
- ModelSetupTime
- OverheadLatency
- MidStreamErrors
- FirstChunkLatency
- FirstChunkModelLatency
- FirstChunkOverheadLatency
dimensions:
- InferenceComponentName

- title: Multi-model endpoint model loading metrics
resource: sagemaker-endpoint
kind: classic
namespace: AWS/SageMaker
metrics:
- ModelLoadingWaitTime
- ModelUnloadingTime
- ModelDownloadingTime
- ModelLoadingTime
- ModelCacheHit
dimensions:
- EndpointName, VariantName

- title: Multi-model endpoint model instance metrics
resource: sagemaker-endpoint
kind: classic
namespace: /aws/sagemaker/Endpoints
metrics:
- LoadedModelCount
dimensions:
- EndpointName, VariantName

# The only real-time invocation metrics an async endpoint publishes. The page
# gives them no dimensions; the error counts' are by analogy with ModelLatency,
# since they weren't observed.
- title: Common Endpoint Metrics
resource: sagemaker-endpoint
kind: async
namespace: AWS/SageMaker
metrics:
- Invocation4XXErrors
- Invocation5XXErrors
- ModelLatency
dimensions:
- EndpointName, VariantName

# The queue belongs to the endpoint rather than to a variant.
- title: Asynchronous Inference Endpoint Metrics
resource: sagemaker-endpoint
kind: async
namespace: AWS/SageMaker
metrics:
- ApproximateBacklogSize
- ApproximateBacklogSizePerInstance
- ApproximateAgeOfOldestRequest
- HasBacklogWithoutCapacity
dimensions:
- EndpointName

# The page misspells InvocationsProcessed as InvocationsProcesssed.
# NotificationFailures and ExpiredRequests weren't observed, so their
# dimensions are the page's.
- title: Asynchronous Inference Endpoint Metrics
resource: sagemaker-endpoint
kind: async
namespace: AWS/SageMaker
metrics:
- RequestDownloadFailures
- ResponseUploadFailures
- NotificationFailures
- RequestDownloadLatency
- ResponseUploadLatency
- ExpiredRequests
- InvocationFailures
- InvocationsProcessed
- TimeInBacklog
- TotalProcessingTime
dimensions:
- EndpointName, VariantName
30 changes: 29 additions & 1 deletion c7n/filters/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
type_schema,
parse_cidr,
parse_date,
parse_duration,
jmespath_search,
jmespath_compile
)
Expand Down Expand Up @@ -111,10 +112,17 @@ def mod(x, y):
'mod': mod}


# Comparisons between two timedeltas. Other operators never match a
# duration, and mod would divide by zero on a PT0S value.
DURATION_OPERATORS = {
'eq', 'equal', 'ne', 'not-equal', 'gt', 'greater-than', 'ge', 'gte',
'le', 'lte', 'lt', 'less-than'}


VALUE_TYPES = [
'age', 'integer', 'expiration', 'normalize', 'size',
'cidr', 'cidr_size', 'swap', 'resource_count', 'expr',
'unique_size', 'date', 'version', 'float']
'unique_size', 'date', 'version', 'float', 'duration']


class FilterRegistry(PluginRegistry):
Expand Down Expand Up @@ -655,6 +663,21 @@ def validate(self):
raise PolicyValidationError(
"value_type: date with invalid date value:%s",
self.data.get('value', ''))
elif self.data.get('value_type') == 'duration':
if 'op' in self.data and self.data['op'] not in DURATION_OPERATORS:
raise PolicyValidationError(
"value_type: duration can't be used with op: %s" % self.data['op'])
if ('value' not in self.data or
'value_from' in self.data or
'value_path' in self.data):
raise PolicyValidationError(
"value_type: duration needs a literal value; "
"value_from and value_path aren't supported")
if parse_duration(self.data['value']) is None:
raise PolicyValidationError(
"value_type: duration needs an ISO 8601 duration in weeks, days, "
"hours, minutes or seconds, such as PT1H (years and months aren't "
"supported), got: %s" % self.data['value'])
if 'key' not in self.data and 'key' in self.required_keys:
raise PolicyValidationError(
"Missing 'key' in value filter %s" % self.data)
Expand Down Expand Up @@ -758,6 +781,9 @@ def match(self, i):
# value type conversion
if self.vtype is not None:
v, r = self.process_value_type(self.v, r, i)
# a resource value that isn't a duration never matches, even for not-equal
if self.vtype == 'duration' and r is None:
return False
else:
v = self.v

Expand Down Expand Up @@ -813,6 +839,8 @@ def process_value_type(self, sentinel, value, resource):
return value, sentinel
elif self.vtype == 'date':
return parse_date(sentinel), parse_date(value)
elif self.vtype == 'duration':
return parse_duration(sentinel), parse_duration(value)
elif self.vtype == 'age':
if not isinstance(sentinel, datetime.datetime):
sentinel = datetime.datetime.now(tz=tzutc()) - timedelta(sentinel)
Expand Down
34 changes: 34 additions & 0 deletions c7n/filters/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ class MetricsFilter(Filter):
Rather than skipping those resources, "missing-value: 0" causes the
policy to treat their request counts as 0.

Missing values only make sense when the statistic represents a
total, such as a sum or count.

Note the default statistic for metrics is Average.

The ``period-start`` key allows you to align the metric window in two ways.
Expand All @@ -89,7 +92,38 @@ class MetricsFilter(Filter):
value: 30
op: less-than
period-start: start-of-day

Cloudwatch metrics are time series identified by a metric name,
a metric namespace, and by key-value pairs called "dimensions". A
"dimension set" identifies the possible keys and is often as
simple as a single element that is a resource identifier, like
BucketName.

Metric filters automatically supply a dimension that is the resource
identifier.

Additional dimensions can be provided to narrow the metric data
used:

.. code-block:: yaml

dimensions:
StorageType: StandardStorage
"""
#
# Note: metric filters are implemented by:
#
# - enumerating resources for a resource type.
#
# - For each resource:
#
# - Fetch the metric for the resource, identified by the metric
# name, namespace, and resource dimensions.
#
# - Evaluate the condition against the data.
# The filter matches if the condition holds for every data point.
# If there are no data, the filter doesn't match.
# A missing value can be supplied to be used when there are no data.

schema = type_schema(
'metrics',
Expand Down
Loading
Loading