Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion c7n/version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# Generated via tools/dev/devpkg.py
version = "0.9.52a3.post3"
version = "0.9.52a3.post4"
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ dependencies = [
"urllib3>2",
]
name = "c7n"
version = "0.9.52a3.post3"
version = "0.9.52a3.post4"
description = "Cloud Custodian - Policy Rules Engine"
readme = "README.md"
classifiers = [
Expand Down
2 changes: 1 addition & 1 deletion tools/c7n_awscc/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ dependencies = [
"jsonpatch<2.0,>=1.32",
]
name = "c7n_awscc"
version = "0.1.37a3.post3"
version = "0.1.37a3.post4"
readme = "readme.md"
description = "Cloud Custodian - AWS Cloud Control Provider"
classifiers = [
Expand Down
2 changes: 1 addition & 1 deletion tools/c7n_azure/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ dependencies = [
"requests<3.0.0,>=2.22.0",
]
name = "c7n_azure"
version = "0.7.51a3.post3"
version = "0.7.51a3.post4"
description = "Cloud Custodian - Azure Support"
readme = "readme.md"
classifiers = [
Expand Down
59 changes: 55 additions & 4 deletions tools/c7n_gcp/c7n_gcp/resources/cloudrun.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,64 @@
# Copyright The Cloud Custodian Authors.
# SPDX-License-Identifier: Apache-2.0
import copy

from c7n_gcp.provider import resources
from c7n_gcp.query import QueryResourceManager, TypeInfo
from c7n_gcp.filters import IamPolicyFilter
from c7n_gcp.filters.iampolicy import IamPolicyValueFilter
from c7n.exceptions import PolicyExecutionError
from c7n.utils import local_session, jmespath_search

# The only fields the Knative schema defines at the root of a Service or a Job.
KNATIVE_FIELDS = ('apiVersion', 'kind', 'metadata', 'spec', 'status')

# Prefixes of the annotations c7n keeps on a resource. Only the colon form is
# written to a nested object; the dotted `c7n.metrics` is set at the root alone.
# Nesting matches on the colon form only, because kubernetes annotation keys -
# user controlled, and carried in `metadata.annotations` - may contain a dot but
# never a colon, and this is a whole object replace: a key dropped from the body
# is a key deleted from the service.
C7N_ANNOTATION = 'c7n:'
C7N_ROOT_ANNOTATIONS = (C7N_ANNOTATION, 'c7n.')


def _without_annotations(value):
"Copy a value, dropping any c7n annotation held anywhere beneath it."
if isinstance(value, dict):
return {k: _without_annotations(v) for k, v in value.items()
if not k.startswith(C7N_ANNOTATION)}
if isinstance(value, list):
return [_without_annotations(v) for v in value]
# everything else in a decoded api response is an immutable scalar
return value


def knative_body(resource):
"""Copy a resource into a replace request body, dropping non schema fields.

Cloud Run replaces the whole object, so the body is the resource itself
rather than a fragment, and the api rejects the request with a 400 on any
field it can't place. A resource carries two kinds it can't: the top level
`labels` key that augment() flattens out of the metadata, and the
annotations the policy's filters leave behind - at the root, and nested
within `spec` when a list-item filter matched on something there.
Comment on lines +37 to +42

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks - this is a helpful comment. The part that initially threw me here was that most other labeling operations specify something like updateMask='labels' and the error message in cloud-custodian#11034 had a URL with updateMask=metadata.labels in it but that option doesn't seem to be available to Python until v2 of this service.

The replaceService() docs do call out:

Only the spec and metadata labels and annotations are modifiable.

But "whole object replace" does still feel like the right way to think about it 👍


A root field that is neither raises rather than being dropped. Under a
whole object replace a field left out of the body is deleted from the
service, so a field this doesn't recognize is one it cannot safely send
or safely omit.
"""
unrecognized = {
k for k in resource
if k not in KNATIVE_FIELDS and k != 'labels'
and not k.startswith(C7N_ROOT_ANNOTATIONS)
}
if unrecognized:
raise PolicyExecutionError(
'%s %s has fields absent from the cloud run schema: %s' % (
resource.get('kind', 'resource'),
resource.get('metadata', {}).get('name'),
', '.join(sorted(unrecognized))))
return {k: _without_annotations(v) for k, v in resource.items() if k in KNATIVE_FIELDS}


@resources.register("cloud-run-service")
class CloudRunService(QueryResourceManager):
Expand Down Expand Up @@ -35,7 +86,7 @@ def get_label_params(resource, all_labels):
location = metadata['labels']['cloud.googleapis.com/location']
namespace = metadata['namespace']
svc_name = metadata['name']
body = copy.deepcopy(resource)
body = knative_body(resource)
body['metadata']['labels'] = all_labels
return {
'name': 'projects/{}/locations/{}/services/{}'.format(
Expand Down Expand Up @@ -98,7 +149,7 @@ def get_label_params(resource, all_labels):
metadata = resource['metadata']
namespace = metadata['namespace']
job_name = metadata['name']
body = copy.deepcopy(resource)
body = knative_body(resource)
body['metadata']['labels'] = all_labels
return {
'name': 'namespaces/{}/jobs/{}'.format(namespace, job_name),
Expand Down
2 changes: 1 addition & 1 deletion tools/c7n_gcp/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ dependencies = [
"retrying<2.0.0,>=1.3.3",
]
name = "c7n_gcp"
version = "0.4.51a3.post3"
version = "0.4.51a3.post4"
description = "Cloud Custodian - Google Cloud Provider"
readme = "readme.md"
classifiers = [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"headers": {
"content-type": "application/json; charset=UTF-8",
"status": "200"
},
"body": {
"apiVersion": "serving.knative.dev/v1",
"kind": "ServiceList",
"items": [
{
"apiVersion": "serving.knative.dev/v1",
"kind": "Service",
"metadata": {
"name": "hello",
"namespace": "cloud-custodian",
"selfLink": "/apis/serving.knative.dev/v1/namespaces/cloud-custodian/services/hello",
"uid": "46b28549-6c5c-4245-bc8d-9cec06d262b6",
"resourceVersion": "AAX4MAndusc",
"generation": 1,
"labels": {
"cloud.googleapis.com/location": "us-central1"
},
"annotations": {
"run.googleapis.com/ingress": "all"
},
"creationTimestamp": "2023-03-31T10:53:45.882066Z"
},
"spec": {
"template": {
"metadata": {
"name": "hello-00001-tiv",
"annotations": {}
},
"spec": {
"containerConcurrency": 80,
"timeoutSeconds": 300,
"containers": [
{
"image": "us-docker.pkg.dev/cloudrun/container/hello",
"ports": [{"name": "http1", "containerPort": 8080}]
}
]
}
},
"traffic": [{"percent": 100, "latestRevision": true}]
},
"status": {
"observedGeneration": 1,
"conditions": [
{"type": "Ready", "status": "True"}
],
"url": "https://hello-g3kdvpt64a-uc.a.run.app"
}
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"headers": {
"content-type": "application/json; charset=UTF-8",
"status": "200"
},
"body": {
"apiVersion": "serving.knative.dev/v1",
"kind": "Service",
"metadata": {
"name": "hello",
"namespace": "cloud-custodian",
"selfLink": "/apis/serving.knative.dev/v1/namespaces/cloud-custodian/services/hello",
"uid": "46b28549-6c5c-4245-bc8d-9cec06d262b6",
"resourceVersion": "AAX4MBndusc",
"generation": 2,
"labels": {
"cloud.googleapis.com/location": "us-central1",
"custodian_status": "resource_policy-notify-2026_08_21__0_0"
},
"annotations": {
"run.googleapis.com/ingress": "all"
},
"creationTimestamp": "2023-03-31T10:53:45.882066Z"
},
"spec": {
"template": {
"metadata": {
"name": "hello-00001-tiv",
"annotations": {}
},
"spec": {
"containerConcurrency": 80,
"timeoutSeconds": 300,
"containers": [
{
"image": "us-docker.pkg.dev/cloudrun/container/hello",
"ports": [{"name": "http1", "containerPort": 8080}]
}
]
}
},
"traffic": [{"percent": 100, "latestRevision": true}]
},
"status": {
"observedGeneration": 2,
"conditions": [
{"type": "Ready", "status": "True"}
],
"url": "https://hello-g3kdvpt64a-uc.a.run.app"
}
}
}
Loading