Skip to content

Commit 3ace65a

Browse files
committed
refactoring
1 parent 73729c9 commit 3ace65a

14 files changed

Lines changed: 532 additions & 164 deletions

deployments/kubernetes/chart/reloader/templates/deployment.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,26 @@ spec:
274274
{{- if .Values.reloader.custom_annotations.configmap_auto }}
275275
- "--configmap-auto-annotation"
276276
- "{{ .Values.reloader.custom_annotations.configmap_auto }}"
277+
{{- end }}
278+
{{- if .Values.reloader.custom_annotations.configmap_exclude }}
279+
- "--configmap-exclude-annotation"
280+
- "{{ .Values.reloader.custom_annotations.configmap_exclude }}"
281+
{{- end }}
282+
{{- if .Values.reloader.custom_annotations.secret_exclude }}
283+
- "--secret-exclude-annotation"
284+
- "{{ .Values.reloader.custom_annotations.secret_exclude }}"
285+
{{- end }}
286+
{{- if .Values.reloader.custom_annotations.secretproviderclass }}
287+
- "--secretproviderclass-annotation"
288+
- "{{ .Values.reloader.custom_annotations.secretproviderclass }}"
289+
{{- end }}
290+
{{- if .Values.reloader.custom_annotations.secretproviderclass_auto }}
291+
- "--secretproviderclass-auto-annotation"
292+
- "{{ .Values.reloader.custom_annotations.secretproviderclass_auto }}"
293+
{{- end }}
294+
{{- if .Values.reloader.custom_annotations.secretproviderclass_exclude }}
295+
- "--secretproviderclass-exclude-annotation"
296+
- "{{ .Values.reloader.custom_annotations.secretproviderclass_exclude }}"
277297
{{- end }}
278298
{{- if .Values.reloader.custom_annotations.search }}
279299
- "--auto-search-annotation"

deployments/kubernetes/chart/reloader/values.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,11 @@ reloader:
216216
# custom_annotations:
217217
# configmap: "my.company.com/configmap"
218218
# secret: "my.company.com/secret"
219+
# configmap_exclude: "my.company.com/configmap-exclude"
220+
# secret_exclude: "my.company.com/secret-exclude"
221+
# secretproviderclass: "my.company.com/secretproviderclass"
222+
# secretproviderclass_auto: "my.company.com/secretproviderclass-auto"
223+
# secretproviderclass_exclude: "my.company.com/secretproviderclass-exclude"
219224
# ignore: "my.company.com/reloader-ignore"
220225
custom_annotations: {}
221226

internal/pkg/config/flags.go

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,26 @@ func BindFlags(fs *pflag.FlagSet, cfg *Config) {
182182
"secret-annotation", cfg.Annotations.SecretReload,
183183
"Annotation to detect changes in secrets, specified by name",
184184
)
185+
fs.String(
186+
"configmap-exclude-annotation", cfg.Annotations.ConfigmapExclude,
187+
"Annotation to exclude named configmaps from triggering reloads",
188+
)
189+
fs.String(
190+
"secret-exclude-annotation", cfg.Annotations.SecretExclude,
191+
"Annotation to exclude named secrets from triggering reloads",
192+
)
193+
fs.String(
194+
"secretproviderclass-auto-annotation", cfg.Annotations.SecretProviderClassAuto,
195+
"Annotation to detect changes in secret provider classes (CSI)",
196+
)
197+
fs.String(
198+
"secretproviderclass-annotation", cfg.Annotations.SecretProviderClassReload,
199+
"Annotation to detect changes in secret provider classes (CSI), specified by name",
200+
)
201+
fs.String(
202+
"secretproviderclass-exclude-annotation", cfg.Annotations.SecretProviderClassExclude,
203+
"Annotation to exclude named secret provider classes (CSI) from triggering reloads",
204+
)
185205
fs.String(
186206
"auto-search-annotation", cfg.Annotations.Search,
187207
"Annotation to detect changes in configmaps or secrets tagged with special match annotation",
@@ -190,6 +210,10 @@ func BindFlags(fs *pflag.FlagSet, cfg *Config) {
190210
"search-match-annotation", cfg.Annotations.Match,
191211
"Annotation to mark secrets or configmaps to match the search",
192212
)
213+
fs.String(
214+
"ignore-annotation", cfg.Annotations.Ignore,
215+
"Annotation to ignore changes on watched resources",
216+
)
193217
fs.String(
194218
"pause-deployment-annotation", cfg.Annotations.PausePeriod,
195219
"Annotation to define the time period to pause a deployment after a configmap/secret change",
@@ -289,23 +313,17 @@ func ApplyFlags(cfg *Config) error {
289313
cfg.Annotations.SecretAuto = v.GetString("secret-auto-annotation")
290314
cfg.Annotations.ConfigmapReload = v.GetString("configmap-annotation")
291315
cfg.Annotations.SecretReload = v.GetString("secret-annotation")
316+
cfg.Annotations.ConfigmapExclude = v.GetString("configmap-exclude-annotation")
317+
cfg.Annotations.SecretExclude = v.GetString("secret-exclude-annotation")
318+
cfg.Annotations.SecretProviderClassAuto = v.GetString("secretproviderclass-auto-annotation")
319+
cfg.Annotations.SecretProviderClassReload = v.GetString("secretproviderclass-annotation")
320+
cfg.Annotations.SecretProviderClassExclude = v.GetString("secretproviderclass-exclude-annotation")
292321
cfg.Annotations.Search = v.GetString("auto-search-annotation")
293322
cfg.Annotations.Match = v.GetString("search-match-annotation")
323+
cfg.Annotations.Ignore = v.GetString("ignore-annotation")
294324
cfg.Annotations.PausePeriod = v.GetString("pause-deployment-annotation")
295325
cfg.Annotations.PausedAt = v.GetString("pause-deployment-time-annotation")
296326

297-
// SecretProviderClass annotations have no dedicated CLI flag (parity with
298-
// master); keep the configured defaults.
299-
if cfg.Annotations.SecretProviderClassAuto == "" {
300-
cfg.Annotations.SecretProviderClassAuto = DefaultAnnotations().SecretProviderClassAuto
301-
}
302-
if cfg.Annotations.SecretProviderClassReload == "" {
303-
cfg.Annotations.SecretProviderClassReload = DefaultAnnotations().SecretProviderClassReload
304-
}
305-
if cfg.Annotations.SecretProviderClassExclude == "" {
306-
cfg.Annotations.SecretProviderClassExclude = DefaultAnnotations().SecretProviderClassExclude
307-
}
308-
309327
// Alerting
310328
cfg.Alerting.Enabled = v.GetBool("alert-on-reload")
311329
cfg.Alerting.WebhookURL = v.GetString("alert-webhook-url")

internal/pkg/config/flags_test.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,14 @@ func TestBindFlags(t *testing.T) {
5555
"secret-auto-annotation",
5656
"configmap-annotation",
5757
"secret-annotation",
58+
"configmap-exclude-annotation",
59+
"secret-exclude-annotation",
60+
"secretproviderclass-auto-annotation",
61+
"secretproviderclass-annotation",
62+
"secretproviderclass-exclude-annotation",
5863
"auto-search-annotation",
5964
"search-match-annotation",
65+
"ignore-annotation",
6066
"pause-deployment-annotation",
6167
"pause-deployment-time-annotation",
6268
"watch-namespace",
@@ -153,6 +159,131 @@ func TestBindFlags_CustomValues(t *testing.T) {
153159
}
154160
}
155161

162+
func TestApplyFlags_SecretProviderClassAnnotations(t *testing.T) {
163+
// Defaults are preserved when the flags are not provided.
164+
resetViper()
165+
cfg := NewDefault()
166+
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
167+
BindFlags(fs, cfg)
168+
if err := fs.Parse(nil); err != nil {
169+
t.Fatalf("Parse() error = %v", err)
170+
}
171+
if err := ApplyFlags(cfg); err != nil {
172+
t.Fatalf("ApplyFlags() error = %v", err)
173+
}
174+
defaults := DefaultAnnotations()
175+
if cfg.Annotations.SecretProviderClassAuto != defaults.SecretProviderClassAuto {
176+
t.Errorf("SecretProviderClassAuto = %q, want default %q", cfg.Annotations.SecretProviderClassAuto, defaults.SecretProviderClassAuto)
177+
}
178+
if cfg.Annotations.SecretProviderClassReload != defaults.SecretProviderClassReload {
179+
t.Errorf("SecretProviderClassReload = %q, want default %q", cfg.Annotations.SecretProviderClassReload, defaults.SecretProviderClassReload)
180+
}
181+
if cfg.Annotations.SecretProviderClassExclude != defaults.SecretProviderClassExclude {
182+
t.Errorf("SecretProviderClassExclude = %q, want default %q", cfg.Annotations.SecretProviderClassExclude, defaults.SecretProviderClassExclude)
183+
}
184+
185+
// Custom values are applied from the flags.
186+
resetViper()
187+
cfg = NewDefault()
188+
fs = pflag.NewFlagSet("test", pflag.ContinueOnError)
189+
BindFlags(fs, cfg)
190+
args := []string{
191+
"--secretproviderclass-auto-annotation=spc.example.com/auto",
192+
"--secretproviderclass-annotation=spc.example.com/reload",
193+
"--secretproviderclass-exclude-annotation=spc.example.com/exclude",
194+
}
195+
if err := fs.Parse(args); err != nil {
196+
t.Fatalf("Parse() error = %v", err)
197+
}
198+
if err := ApplyFlags(cfg); err != nil {
199+
t.Fatalf("ApplyFlags() error = %v", err)
200+
}
201+
if cfg.Annotations.SecretProviderClassAuto != "spc.example.com/auto" {
202+
t.Errorf("SecretProviderClassAuto = %q, want %q", cfg.Annotations.SecretProviderClassAuto, "spc.example.com/auto")
203+
}
204+
if cfg.Annotations.SecretProviderClassReload != "spc.example.com/reload" {
205+
t.Errorf("SecretProviderClassReload = %q, want %q", cfg.Annotations.SecretProviderClassReload, "spc.example.com/reload")
206+
}
207+
if cfg.Annotations.SecretProviderClassExclude != "spc.example.com/exclude" {
208+
t.Errorf("SecretProviderClassExclude = %q, want %q", cfg.Annotations.SecretProviderClassExclude, "spc.example.com/exclude")
209+
}
210+
}
211+
212+
func TestApplyFlags_ExcludeAnnotations(t *testing.T) {
213+
// Defaults are preserved when the flags are not provided.
214+
resetViper()
215+
cfg := NewDefault()
216+
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
217+
BindFlags(fs, cfg)
218+
if err := fs.Parse(nil); err != nil {
219+
t.Fatalf("Parse() error = %v", err)
220+
}
221+
if err := ApplyFlags(cfg); err != nil {
222+
t.Fatalf("ApplyFlags() error = %v", err)
223+
}
224+
defaults := DefaultAnnotations()
225+
if cfg.Annotations.ConfigmapExclude != defaults.ConfigmapExclude {
226+
t.Errorf("ConfigmapExclude = %q, want default %q", cfg.Annotations.ConfigmapExclude, defaults.ConfigmapExclude)
227+
}
228+
if cfg.Annotations.SecretExclude != defaults.SecretExclude {
229+
t.Errorf("SecretExclude = %q, want default %q", cfg.Annotations.SecretExclude, defaults.SecretExclude)
230+
}
231+
232+
// Custom values are applied from the flags.
233+
resetViper()
234+
cfg = NewDefault()
235+
fs = pflag.NewFlagSet("test", pflag.ContinueOnError)
236+
BindFlags(fs, cfg)
237+
args := []string{
238+
"--configmap-exclude-annotation=cm.example.com/exclude",
239+
"--secret-exclude-annotation=sec.example.com/exclude",
240+
}
241+
if err := fs.Parse(args); err != nil {
242+
t.Fatalf("Parse() error = %v", err)
243+
}
244+
if err := ApplyFlags(cfg); err != nil {
245+
t.Fatalf("ApplyFlags() error = %v", err)
246+
}
247+
if cfg.Annotations.ConfigmapExclude != "cm.example.com/exclude" {
248+
t.Errorf("ConfigmapExclude = %q, want %q", cfg.Annotations.ConfigmapExclude, "cm.example.com/exclude")
249+
}
250+
if cfg.Annotations.SecretExclude != "sec.example.com/exclude" {
251+
t.Errorf("SecretExclude = %q, want %q", cfg.Annotations.SecretExclude, "sec.example.com/exclude")
252+
}
253+
}
254+
255+
func TestApplyFlags_IgnoreAnnotation(t *testing.T) {
256+
// Default is preserved when the flag is not provided.
257+
resetViper()
258+
cfg := NewDefault()
259+
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
260+
BindFlags(fs, cfg)
261+
if err := fs.Parse(nil); err != nil {
262+
t.Fatalf("Parse() error = %v", err)
263+
}
264+
if err := ApplyFlags(cfg); err != nil {
265+
t.Fatalf("ApplyFlags() error = %v", err)
266+
}
267+
if cfg.Annotations.Ignore != DefaultAnnotations().Ignore {
268+
t.Errorf("Ignore = %q, want default %q", cfg.Annotations.Ignore, DefaultAnnotations().Ignore)
269+
}
270+
271+
// Custom value is applied from the flag.
272+
resetViper()
273+
cfg = NewDefault()
274+
fs = pflag.NewFlagSet("test", pflag.ContinueOnError)
275+
BindFlags(fs, cfg)
276+
if err := fs.Parse([]string{"--ignore-annotation=my.company.com/reloader-ignore"}); err != nil {
277+
t.Fatalf("Parse() error = %v", err)
278+
}
279+
if err := ApplyFlags(cfg); err != nil {
280+
t.Fatalf("ApplyFlags() error = %v", err)
281+
}
282+
if cfg.Annotations.Ignore != "my.company.com/reloader-ignore" {
283+
t.Errorf("Ignore = %q, want %q", cfg.Annotations.Ignore, "my.company.com/reloader-ignore")
284+
}
285+
}
286+
156287
func TestApplyFlags_BooleanStrings(t *testing.T) {
157288
tests := []struct {
158289
name string

internal/pkg/controller/manager.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ func SetupReconcilers(mgr ctrl.Manager, cfg *config.Config, log logr.Logger, col
246246
},
247247
mgr.GetAPIReader(),
248248
)
249-
if err := spcReconciler.SetupWithManager(mgr); err != nil {
249+
if err := SetupSecretProviderClassReconciler(mgr, spcReconciler); err != nil {
250250
return fmt.Errorf("setting up secretproviderclass reconciler: %w", err)
251251
}
252252
log.Info("CSI SecretProviderClass reconciler enabled")

internal/pkg/controller/resource_reconciler.go

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ import (
2121

2222
// ResourceReconcilerDeps holds shared dependencies for resource reconcilers.
2323
type ResourceReconcilerDeps struct {
24-
Client client.Client
24+
Client client.Client
25+
// APIReader is an optional non-cached reader for ResolveChange lookups.
26+
APIReader client.Reader
2527
Log logr.Logger
2628
Config *config.Config
2729
ReloadService *reload.Service
@@ -47,6 +49,16 @@ type ResourceConfig[T client.Object] struct {
4749

4850
// CreatePredicates creates the predicates for this resource type.
4951
CreatePredicates func(cfg *config.Config, hasher *reload.Hasher) predicate.Predicate
52+
53+
// ResolveChange derives the change from a second object instead of CreateChange;
54+
// ok=false skips the event (CSI: change comes from the parent SecretProviderClass).
55+
ResolveChange func(ctx context.Context, reader client.Reader, log logr.Logger, resource T) (reload.ResourceChange, bool)
56+
57+
// SkipOnNotFound treats a missing object as a no-op (CSI deletes SPCPS as pods roll).
58+
SkipOnNotFound bool
59+
60+
// BuildFilter overrides the default BuildEventFilter for the watch.
61+
BuildFilter func(cfg *config.Config, hasher *reload.Hasher) predicate.Predicate
5062
}
5163

5264
// ResourceReconciler is a generic reconciler for ConfigMaps and Secrets.
@@ -102,23 +114,43 @@ func (r *ResourceReconciler[T]) Reconcile(ctx context.Context, req ctrl.Request)
102114
return ctrl.Result{}, nil
103115
}
104116

117+
change, ok := r.buildChange(ctx, log, resource)
118+
if !ok {
119+
r.Collectors.RecordSkipped("resolve_skipped")
120+
r.Collectors.RecordReconcile("success", time.Since(startTime))
121+
return ctrl.Result{}, nil
122+
}
123+
105124
result, err := r.reloadHandler().Process(
106-
ctx, req.Namespace, req.Name, r.ResourceType,
125+
ctx, change.GetNamespace(), change.GetName(), r.ResourceType,
107126
func(workloads []workload.Workload) []reload.ReloadDecision {
108-
return r.ReloadService.Process(r.CreateChange(resource, reload.EventTypeUpdate), workloads)
127+
return r.ReloadService.Process(change, workloads)
109128
}, log,
110129
)
111130

112131
r.recordReconcile(startTime, err)
113132
return result, err
114133
}
115134

135+
// buildChange returns the change via ResolveChange when set, else CreateChange.
136+
func (r *ResourceReconciler[T]) buildChange(ctx context.Context, log logr.Logger, resource T) (reload.ResourceChange, bool) {
137+
if r.ResolveChange != nil {
138+
return r.ResolveChange(ctx, r.APIReader, log, resource)
139+
}
140+
return r.CreateChange(resource, reload.EventTypeUpdate), true
141+
}
142+
116143
func (r *ResourceReconciler[T]) handleNotFound(
117144
ctx context.Context,
118145
req ctrl.Request,
119146
log logr.Logger,
120147
startTime time.Time,
121148
) (ctrl.Result, error) {
149+
if r.SkipOnNotFound {
150+
r.Collectors.RecordSkipped("not_found")
151+
r.Collectors.RecordReconcile("success", time.Since(startTime))
152+
return ctrl.Result{}, nil
153+
}
122154
if r.Config.ReloadOnDelete {
123155
r.Collectors.RecordEventReceived("delete", string(r.ResourceType))
124156
result, err := r.handleDelete(ctx, req, log)
@@ -176,19 +208,19 @@ func (r *ResourceReconciler[T]) reloadHandler() *ReloadHandler {
176208

177209
// SetupWithManager sets up the controller with the Manager.
178210
func (r *ResourceReconciler[T]) SetupWithManager(mgr ctrl.Manager, forObject T) error {
179-
// Capture the moment the controller is wired up (before the manager starts
180-
// watching). Resources that already exist are replayed during the initial
181-
// cache sync with an older creation timestamp; the create predicate uses
182-
// this to ignore those replays while still honoring genuine creates that
183-
// arrive afterwards.
184-
startTime := time.Now()
211+
var filter predicate.Predicate
212+
if r.BuildFilter != nil {
213+
filter = r.BuildFilter(r.Config, r.ReloadService.Hasher())
214+
} else {
215+
// time.Now() lets the create predicate ignore initial-sync replays of
216+
// pre-existing resources (older creation timestamps) while honoring later creates.
217+
filter = BuildEventFilter(
218+
r.CreatePredicates(r.Config, r.ReloadService.Hasher()),
219+
r.Config, time.Now(),
220+
)
221+
}
185222
return ctrl.NewControllerManagedBy(mgr).
186223
For(forObject).
187-
WithEventFilter(
188-
BuildEventFilter(
189-
r.CreatePredicates(r.Config, r.ReloadService.Hasher()),
190-
r.Config, startTime,
191-
),
192-
).
224+
WithEventFilter(filter).
193225
Complete(r)
194226
}

0 commit comments

Comments
 (0)