Skip to content

Commit 5089955

Browse files
authored
Merge pull request #928 from jamie-stinson/master
feat: add ignore annotation support for skipping reloads
2 parents 404c370 + 58ad781 commit 5089955

File tree

4 files changed

+142
-0
lines changed

4 files changed

+142
-0
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,21 @@ This pattern allows fine-grained reload control — workloads only restart if th
153153
1. ✅ You want to reload a workload only if it references a ConfigMap or Secret that has been explicitly tagged with `reloader.stakater.com/match: "true"`.
154154
1. ✅ Use this when you want full control over which shared or system-wide resources trigger reloads. Great in multi-tenant clusters or shared configs.
155155
156+
### ⛔ Resource-Level Ignore Annotation
157+
158+
When you need to prevent specific ConfigMaps or Secrets from triggering any reloads, use the ignore annotation on the resource itself:
159+
160+
```yaml
161+
apiVersion: v1
162+
kind: ConfigMap # or Secret
163+
metadata:
164+
name: my-config
165+
annotations:
166+
reloader.stakater.com/ignore: "true"
167+
```
168+
169+
This instructs Reloader to skip all reload logic for that resource across all workloads.
170+
156171
### 4. ⚙️ Workload-Specific Rollout Strategy
157172
158173
By default, Reloader uses the **rollout** strategy — it updates the pod template to trigger a new rollout. This works well in most cases, but it can cause problems if you're using GitOps tools like ArgoCD, which detect this as configuration drift.

internal/pkg/handler/upgrade.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,11 @@ func upgradeResource(clients kube.Clients, config util.Config, upgradeFuncs call
264264
}
265265
}
266266

267+
ignoreResourceAnnotatonValue := config.ResourceAnnotations[options.IgnoreResourceAnnotation]
268+
if ignoreResourceAnnotatonValue == "true" {
269+
return nil
270+
}
271+
267272
// find correct annotation and update the resource
268273
annotations := upgradeFuncs.AnnotationsFunc(resource)
269274
annotationValue, found := annotations[config.Annotation]

internal/pkg/handler/upgrade_test.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ var (
5252
arsConfigmapWithConfigMapAutoAnnotation = "testconfigmapwithconfigmapautoannotationdeployment-handler-" + testutil.RandSeq(5)
5353
arsSecretWithExcludeSecretAnnotation = "testsecretwithsecretexcludeannotationdeployment-handler-" + testutil.RandSeq(5)
5454
arsConfigmapWithExcludeConfigMapAnnotation = "testconfigmapwithconfigmapexcludeannotationdeployment-handler-" + testutil.RandSeq(5)
55+
arsConfigmapWithIgnoreAnnotation = "testconfigmapWithIgnoreAnnotation-handler-" + testutil.RandSeq(5)
56+
arsSecretWithIgnoreAnnotation = "testsecretWithIgnoreAnnotation-handler-" + testutil.RandSeq(5)
5557

5658
ersNamespace = "test-handler-" + testutil.RandSeq(5)
5759
ersConfigmapName = "testconfigmap-handler-" + testutil.RandSeq(5)
@@ -75,6 +77,8 @@ var (
7577
ersConfigmapWithConfigMapAutoAnnotation = "testconfigmapwithconfigmapautoannotationdeployment-handler-" + testutil.RandSeq(5)
7678
ersSecretWithSecretExcludeAnnotation = "testsecretwithsecretexcludeannotationdeployment-handler-" + testutil.RandSeq(5)
7779
ersConfigmapWithConfigMapExcludeAnnotation = "testconfigmapwithconfigmapexcludeannotationdeployment-handler-" + testutil.RandSeq(5)
80+
ersConfigmapWithIgnoreAnnotation = "testconfigmapWithIgnoreAnnotation-handler-" + testutil.RandSeq(5)
81+
ersSecretWithIgnoreAnnotation = "testsecretWithIgnoreAnnotation-handler-" + testutil.RandSeq(5)
7882
)
7983

8084
func TestMain(m *testing.M) {
@@ -215,6 +219,35 @@ func setupArs() {
215219
logrus.Errorf("Error in configmap creation: %v", err)
216220
}
217221

222+
// Creating configmap with ignore annotation
223+
_, err = testutil.CreateConfigMap(clients.KubernetesClient, arsNamespace, arsConfigmapWithIgnoreAnnotation, "www.google.com")
224+
if err != nil {
225+
logrus.Errorf("Error in configmap creation: %v", err)
226+
}
227+
// Patch with ignore annotation
228+
cmClient := clients.KubernetesClient.CoreV1().ConfigMaps(arsNamespace)
229+
patch := []byte(`{"metadata":{"annotations":{"reloader.stakater.com/ignore":"true"}}}`)
230+
_, _ = cmClient.Patch(context.TODO(), arsConfigmapWithIgnoreAnnotation, patchtypes.MergePatchType, patch, metav1.PatchOptions{})
231+
232+
// Creating secret with ignore annotation
233+
_, err = testutil.CreateSecret(clients.KubernetesClient, arsNamespace, arsSecretWithIgnoreAnnotation, data)
234+
if err != nil {
235+
logrus.Errorf("Error in secret creation: %v", err)
236+
}
237+
secretClient := clients.KubernetesClient.CoreV1().Secrets(arsNamespace)
238+
_, _ = secretClient.Patch(context.TODO(), arsSecretWithIgnoreAnnotation, patchtypes.MergePatchType, patch, metav1.PatchOptions{})
239+
240+
// Creating Deployment referencing configmap with ignore annotation
241+
_, err = testutil.CreateDeployment(clients.KubernetesClient, arsConfigmapWithIgnoreAnnotation, arsNamespace, true)
242+
if err != nil {
243+
logrus.Errorf("Error in Deployment with configmap ignore annotation creation: %v", err)
244+
}
245+
// Creating Deployment referencing secret with ignore annotation
246+
_, err = testutil.CreateDeployment(clients.KubernetesClient, arsSecretWithIgnoreAnnotation, arsNamespace, true)
247+
if err != nil {
248+
logrus.Errorf("Error in Deployment with secret ignore annotation creation: %v", err)
249+
}
250+
218251
// Creating Deployment with configmap
219252
_, err = testutil.CreateDeployment(clients.KubernetesClient, arsConfigmapName, arsNamespace, true)
220253
if err != nil {
@@ -854,6 +887,34 @@ func setupErs() {
854887
logrus.Errorf("Error in configmap creation: %v", err)
855888
}
856889

890+
// Creating configmap with ignore annotation
891+
_, err = testutil.CreateConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapWithIgnoreAnnotation, "www.google.com")
892+
if err != nil {
893+
logrus.Errorf("Error in configmap creation: %v", err)
894+
}
895+
cmClient := clients.KubernetesClient.CoreV1().ConfigMaps(ersNamespace)
896+
patch := []byte(`{"metadata":{"annotations":{"reloader.stakater.com/ignore":"true"}}}`)
897+
_, _ = cmClient.Patch(context.TODO(), ersConfigmapWithIgnoreAnnotation, patchtypes.MergePatchType, patch, metav1.PatchOptions{})
898+
899+
// Creating secret with ignore annotation
900+
_, err = testutil.CreateSecret(clients.KubernetesClient, ersNamespace, ersSecretWithIgnoreAnnotation, data)
901+
if err != nil {
902+
logrus.Errorf("Error in secret creation: %v", err)
903+
}
904+
secretClient := clients.KubernetesClient.CoreV1().Secrets(ersNamespace)
905+
_, _ = secretClient.Patch(context.TODO(), ersSecretWithIgnoreAnnotation, patchtypes.MergePatchType, patch, metav1.PatchOptions{})
906+
907+
// Creating Deployment referencing configmap with ignore annotation
908+
_, err = testutil.CreateDeployment(clients.KubernetesClient, ersConfigmapWithIgnoreAnnotation, ersNamespace, true)
909+
if err != nil {
910+
logrus.Errorf("Error in Deployment with configmap ignore annotation creation: %v", err)
911+
}
912+
// Creating Deployment referencing secret with ignore annotation
913+
_, err = testutil.CreateDeployment(clients.KubernetesClient, ersSecretWithIgnoreAnnotation, ersNamespace, true)
914+
if err != nil {
915+
logrus.Errorf("Error in Deployment with secret ignore annotation creation: %v", err)
916+
}
917+
857918
// Creating Deployment with configmap
858919
_, err = testutil.CreateDeployment(clients.KubernetesClient, ersConfigmapName, ersNamespace, true)
859920
if err != nil {
@@ -2737,6 +2798,65 @@ func TestFailedRollingUpgradeUsingArs(t *testing.T) {
27372798
}
27382799
}
27392800

2801+
func TestIgnoreAnnotationNoReloadUsingArs(t *testing.T) {
2802+
options.ReloadStrategy = constants.AnnotationsReloadStrategy
2803+
envVarPostfix := constants.ConfigmapEnvVarPostfix
2804+
2805+
shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigmapWithIgnoreAnnotation, "www.stakater.com")
2806+
config := getConfigWithAnnotations(envVarPostfix, arsConfigmapWithIgnoreAnnotation, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation)
2807+
config.ResourceAnnotations = map[string]string{"reloader.stakater.com/ignore": "true"}
2808+
deploymentFuncs := GetDeploymentRollingUpgradeFuncs()
2809+
collectors := getCollectors()
2810+
2811+
err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy)
2812+
if err != nil {
2813+
t.Errorf("Rolling upgrade failed for Deployment with Configmap and ignore annotation using ARS")
2814+
}
2815+
2816+
// Ensure deployment is NOT updated
2817+
updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs)
2818+
if updated {
2819+
t.Errorf("Deployment was updated but should not have been")
2820+
}
2821+
2822+
// Ensure counters remain zero
2823+
if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 0 {
2824+
t.Errorf("Reload counter should not have increased")
2825+
}
2826+
if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 0 {
2827+
t.Errorf("Reload counter by namespace should not have increased")
2828+
}
2829+
}
2830+
func TestIgnoreAnnotationNoReloadUsingErs(t *testing.T) {
2831+
options.ReloadStrategy = constants.EnvVarsReloadStrategy
2832+
envVarPostfix := constants.ConfigmapEnvVarPostfix
2833+
2834+
shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapWithIgnoreAnnotation, "www.stakater.com")
2835+
config := getConfigWithAnnotations(envVarPostfix, ersConfigmapWithIgnoreAnnotation, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation)
2836+
config.ResourceAnnotations = map[string]string{"reloader.stakater.com/ignore": "true"}
2837+
deploymentFuncs := GetDeploymentRollingUpgradeFuncs()
2838+
collectors := getCollectors()
2839+
2840+
err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy)
2841+
if err != nil {
2842+
t.Errorf("Rolling upgrade failed for Deployment with Configmap and ignore annotation using ERS")
2843+
}
2844+
2845+
// Ensure deployment is NOT updated
2846+
updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs)
2847+
if updated {
2848+
t.Errorf("Deployment was updated but should not have been (ERS)")
2849+
}
2850+
2851+
// Ensure counters remain zero
2852+
if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 0 {
2853+
t.Errorf("Reload counter should not have increased (ERS)")
2854+
}
2855+
if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 0 {
2856+
t.Errorf("Reload counter by namespace should not have increased (ERS)")
2857+
}
2858+
}
2859+
27402860
func testRollingUpgradeInvokeDeleteStrategyErs(t *testing.T, clients kube.Clients, config util.Config, upgradeFuncs callbacks.RollingUpgradeFuncs, collectors metrics.Collectors, envVarPostfix string) {
27412861
err := PerformAction(clients, config, upgradeFuncs, collectors, nil, invokeDeleteStrategy)
27422862
time.Sleep(5 * time.Second)

internal/pkg/options/flags.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ var (
2222
SecretUpdateOnChangeAnnotation = "secret.reloader.stakater.com/reload"
2323
// ReloaderAutoAnnotation is an annotation to detect changes in secrets/configmaps
2424
ReloaderAutoAnnotation = "reloader.stakater.com/auto"
25+
// IgnoreResourceAnnotation is an annotation to ignore changes in secrets/configmaps
26+
IgnoreResourceAnnotation = "reloader.stakater.com/ignore"
2527
// ConfigmapReloaderAutoAnnotation is an annotation to detect changes in configmaps
2628
ConfigmapReloaderAutoAnnotation = "configmap.reloader.stakater.com/auto"
2729
// SecretReloaderAutoAnnotation is an annotation to detect changes in secrets

0 commit comments

Comments
 (0)