Skip to content

Commit 3d79360

Browse files
committed
Add --resource-names flag to customize monitored resources
This commit adds the ability to selectively monitor specific Kubernetes resource types using the --resource-names flag. This addresses issue #1388. Changes: - Add --resource-names flag with default value covering all resource types - Implement dynamic types map construction based on specified resources - Add validation and logging for resource names configuration - Add warning when using custom resource names (poor UX if parent resources like Deployments are not monitored) - Fail fast if no valid resources are specified Testing: - Add comprehensive E2E test (test/e2e_test_resource_names_flag.sh) - Add kustomize configuration example (test/kustomize-resource-names/) - Add GitHub Actions workflow for CI testing (.github/workflows/kind-cluster-resource-names.yaml) - Workflow properly deploys policy-controller before running tests The default behavior remains unchanged (all resources monitored), ensuring backward compatibility. Signed-off-by: 0xiso <6024009+0xiso@users.noreply.github.com>
1 parent e09f67f commit 3d79360

File tree

4 files changed

+454
-14
lines changed

4 files changed

+454
-14
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Copyright 2024 The Sigstore Authors.
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+
15+
name: Test policy-controller with --resource-names flag
16+
17+
on:
18+
pull_request:
19+
branches: [ 'main', 'release-*' ]
20+
21+
defaults:
22+
run:
23+
shell: bash
24+
25+
permissions: read-all
26+
27+
jobs:
28+
resource-names-flag-test:
29+
name: Test --resource-names flag functionality
30+
runs-on: ubuntu-latest
31+
32+
strategy:
33+
fail-fast: false # Keep running if one leg fails.
34+
matrix:
35+
k8s-version:
36+
- v1.31.x
37+
- v1.32.x
38+
- v1.33.x
39+
- v1.34.x
40+
41+
env:
42+
KO_DOCKER_REPO: "registry.local:5000/policy-controller"
43+
SCAFFOLDING_RELEASE_VERSION: "v0.7.27"
44+
GO111MODULE: on
45+
GOFLAGS: -ldflags=-s -ldflags=-w
46+
KOCACHE: ~/ko
47+
48+
steps:
49+
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
50+
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
51+
with:
52+
go-version-file: './go.mod'
53+
check-latest: true
54+
55+
# will use the latest release available for ko
56+
- uses: ko-build/setup-ko@d006021bd0c28d1ce33a07e7943d48b079944c8d # v0.9
57+
58+
- uses: imranismail/setup-kustomize@2ba527d4d055ab63514ba50a99456fc35684947f # v2.1.0
59+
60+
- name: Install yq
61+
uses: mikefarah/yq@065b200af9851db0d5132f50bc10b1406ea5c0a8 # v4.50.1
62+
63+
- name: Setup mirror
64+
uses: chainguard-dev/actions/setup-mirror@main
65+
with:
66+
mirror: mirror.gcr.io
67+
68+
- uses: sigstore/cosign-installer@3454372f43399081ed03b604cb2d021dabca52bb # v3.8.2
69+
70+
- name: Install cluster + sigstore
71+
uses: sigstore/scaffolding/actions/setup@main
72+
with:
73+
k8s-version: ${{ matrix.k8s-version }}
74+
version: ${{ env.SCAFFOLDING_RELEASE_VERSION }}
75+
76+
- name: Install policy-controller
77+
env:
78+
GIT_HASH: ${{ github.sha }}
79+
GIT_VERSION: ci
80+
LDFLAGS: ""
81+
POLICY_CONTROLLER_YAML: test/kustomize-resource-names/policy-controller-e2e.yaml
82+
KO_PREFIX: registry.local:5000/policy-controller
83+
POLICY_CONTROLLER_ARCHS: linux/amd64
84+
run: |
85+
make ko-policy-controller
86+
87+
- name: Run --resource-names flag tests
88+
run: |
89+
./test/e2e_test_resource_names_flag.sh
90+
91+
- name: Collect diagnostics
92+
if: ${{ failure() }}
93+
uses: chainguard-dev/actions/kind-diag@6b6aeacf5f8f4854707392e7708773a7f510b611 # main

cmd/webhook/main.go

Lines changed: 87 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"fmt"
2222
"log"
2323
"os"
24+
"strings"
2425
"time"
2526

2627
policyduckv1beta1 "github.com/sigstore/policy-controller/pkg/apis/duck/v1beta1"
@@ -60,7 +61,13 @@ import (
6061
cwebhook "github.com/sigstore/policy-controller/pkg/webhook"
6162
)
6263

64+
const (
65+
defaultResourceNames = "pods,replicasets,deployments,statefulsets,daemonsets,jobs,cronjobs"
66+
)
67+
6368
var (
69+
validResourceTypes = []string{"pods", "replicasets", "deployments", "statefulsets", "daemonsets", "jobs", "cronjobs"}
70+
6471
// webhookName holds the name of the validating and mutating webhook
6572
// configuration resources dispatching admission requests to policy-controller.
6673
// It is also the name of the webhook which is injected by the controller
@@ -103,6 +110,10 @@ var (
103110
// trustrootResyncPeriod holds the interval which the TrustRoot will resync
104111
// This is essential for triggering a reconcile update for potentially stale TUF metadata.
105112
trustrootResyncPeriod = flag.Duration("trustroot-resync-period", 24*time.Hour, "The resync period for ClusterImagePolicies. The default is 24h.")
113+
114+
// resourceNames holds the comma-separated list of Kubernetes resources to monitor.
115+
// https://github.com/sigstore/policy-controller/issues/1388
116+
resourceNames = flag.String("resource-names", defaultResourceNames, "Comma-separated list of Kubernetes resources to monitor. WARNING: If parent resources (e.g., Deployments, ReplicaSets) are not monitored, users will not receive policy violation feedback when creating them, even though the Pods they create may violate policies.")
106117
)
107118

108119
func main() {
@@ -118,6 +129,13 @@ func main() {
118129

119130
flag.Parse()
120131

132+
// Parse and validate resource names
133+
resourceList := parseResourceNames(*resourceNames)
134+
types = buildTypesMap(resourceList, *resourceNames != defaultResourceNames)
135+
136+
// Update ValidResourceNames to match the configured resources
137+
common.ValidResourceNames = sets.NewString(resourceList...)
138+
121139
// If TUF has been disabled do not try to set it up.
122140
if !*disableTUF {
123141
// If they provided an alternate TUF root file to use, read it here.
@@ -139,11 +157,6 @@ func main() {
139157
ctx = clusterimagepolicy.ToContext(ctx, *policyResyncPeriod)
140158
ctx = pctuf.ToContext(ctx, *trustrootResyncPeriod)
141159

142-
// This must match the set of resources we configure in
143-
// cmd/webhook/main.go in the "types" map.
144-
common.ValidResourceNames = sets.NewString("replicasets", "deployments",
145-
"pods", "cronjobs", "jobs", "statefulsets", "daemonsets")
146-
147160
v := version.GetVersionInfo()
148161
vJSON, _ := v.JSONString()
149162
log.Printf("%v", vJSON)
@@ -199,17 +212,77 @@ func (c *crdEphemeralContainers) SupportedVerbs() []admissionregistrationv1.Oper
199212
}
200213
}
201214

202-
var types = map[schema.GroupVersionKind]resourcesemantics.GenericCRD{
203-
corev1.SchemeGroupVersion.WithKind("Pod"): &crdEphemeralContainers{GenericCRD: &duckv1.Pod{}},
215+
// types holds the mapping of Kubernetes resource types to their CRD handlers.
216+
// This is initialized in main() based on the --resource-names flag.
217+
var types map[schema.GroupVersionKind]resourcesemantics.GenericCRD
218+
219+
// parseResourceNames parses a comma-separated list of resource names and returns a cleaned slice.
220+
func parseResourceNames(input string) []string {
221+
parts := strings.Split(input, ",")
222+
result := make([]string, 0, len(parts))
223+
for _, part := range parts {
224+
trimmed := strings.TrimSpace(strings.ToLower(part))
225+
if trimmed != "" {
226+
result = append(result, trimmed)
227+
}
228+
}
229+
return result
230+
}
231+
232+
// buildTypesMap constructs the types map based on the provided resource names.
233+
// Resource names must be in lowercase plural form (e.g., "pods", "deployments").
234+
func buildTypesMap(resourceNames []string, isCustom bool) map[schema.GroupVersionKind]resourcesemantics.GenericCRD {
235+
typesMap := make(map[schema.GroupVersionKind]resourcesemantics.GenericCRD)
236+
var validResources, invalidResources []string
237+
238+
for _, name := range resourceNames {
239+
switch name {
240+
case "pods":
241+
typesMap[corev1.SchemeGroupVersion.WithKind("Pod")] = &crdEphemeralContainers{GenericCRD: &duckv1.Pod{}}
242+
validResources = append(validResources, name)
243+
case "replicasets":
244+
typesMap[appsv1.SchemeGroupVersion.WithKind("ReplicaSet")] = &crdNoStatusUpdatesOrDeletes{GenericCRD: &policyduckv1beta1.PodScalable{}}
245+
validResources = append(validResources, name)
246+
case "deployments":
247+
typesMap[appsv1.SchemeGroupVersion.WithKind("Deployment")] = &crdNoStatusUpdatesOrDeletes{GenericCRD: &policyduckv1beta1.PodScalable{}}
248+
validResources = append(validResources, name)
249+
case "statefulsets":
250+
typesMap[appsv1.SchemeGroupVersion.WithKind("StatefulSet")] = &crdNoStatusUpdatesOrDeletes{GenericCRD: &policyduckv1beta1.PodScalable{}}
251+
validResources = append(validResources, name)
252+
case "daemonsets":
253+
typesMap[appsv1.SchemeGroupVersion.WithKind("DaemonSet")] = &crdNoStatusUpdatesOrDeletes{GenericCRD: &duckv1.WithPod{}}
254+
validResources = append(validResources, name)
255+
case "jobs":
256+
typesMap[batchv1.SchemeGroupVersion.WithKind("Job")] = &crdNoStatusUpdatesOrDeletes{GenericCRD: &duckv1.WithPod{}}
257+
validResources = append(validResources, name)
258+
case "cronjobs":
259+
typesMap[batchv1.SchemeGroupVersion.WithKind("CronJob")] = &crdNoStatusUpdatesOrDeletes{GenericCRD: &duckv1.CronJob{}}
260+
typesMap[batchv1beta1.SchemeGroupVersion.WithKind("CronJob")] = &crdNoStatusUpdatesOrDeletes{GenericCRD: &duckv1.CronJob{}}
261+
validResources = append(validResources, name)
262+
default:
263+
invalidResources = append(invalidResources, name)
264+
}
265+
}
204266

205-
appsv1.SchemeGroupVersion.WithKind("ReplicaSet"): &crdNoStatusUpdatesOrDeletes{GenericCRD: &policyduckv1beta1.PodScalable{}},
206-
appsv1.SchemeGroupVersion.WithKind("Deployment"): &crdNoStatusUpdatesOrDeletes{GenericCRD: &policyduckv1beta1.PodScalable{}},
207-
appsv1.SchemeGroupVersion.WithKind("StatefulSet"): &crdNoStatusUpdatesOrDeletes{GenericCRD: &policyduckv1beta1.PodScalable{}},
208-
appsv1.SchemeGroupVersion.WithKind("DaemonSet"): &crdNoStatusUpdatesOrDeletes{GenericCRD: &duckv1.WithPod{}},
209-
batchv1.SchemeGroupVersion.WithKind("Job"): &crdNoStatusUpdatesOrDeletes{GenericCRD: &duckv1.WithPod{}},
267+
if len(invalidResources) > 0 {
268+
log.Printf("WARNING: Invalid resource names will be ignored: %v. Valid options: %v", invalidResources, validResourceTypes)
269+
}
270+
271+
if len(validResources) == 0 {
272+
log.Fatalf("No valid resources specified. At least one resource type must be monitored. Valid options: %v", validResourceTypes)
273+
}
274+
275+
if isCustom {
276+
log.Printf("WARNING: Using custom resource names may result in poor user experience. "+
277+
"If parent resources (e.g., Deployments, ReplicaSets) are not monitored, users will not receive policy violation feedback "+
278+
"when creating those resources, even though the Pods they create may violate policies. "+
279+
"It is recommended to monitor all resource types unless you have a specific reason to exclude some. "+
280+
"Current monitored resources: %v", validResources)
281+
} else {
282+
log.Printf("Monitoring resources: %v", validResources)
283+
}
210284

211-
batchv1.SchemeGroupVersion.WithKind("CronJob"): &crdNoStatusUpdatesOrDeletes{GenericCRD: &duckv1.CronJob{}},
212-
batchv1beta1.SchemeGroupVersion.WithKind("CronJob"): &crdNoStatusUpdatesOrDeletes{GenericCRD: &duckv1.CronJob{}},
285+
return typesMap
213286
}
214287

215288
var typesCIP = map[schema.GroupVersionKind]resourcesemantics.GenericCRD{

0 commit comments

Comments
 (0)