mirror of
https://github.com/stakater/Reloader.git
synced 2026-02-14 18:09:50 +00:00
Merge pull request #928 from jamie-stinson/master
feat: add ignore annotation support for skipping reloads
This commit is contained in:
15
README.md
15
README.md
@@ -153,6 +153,21 @@ This pattern allows fine-grained reload control — workloads only restart if th
|
||||
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"`.
|
||||
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.
|
||||
|
||||
### ⛔ Resource-Level Ignore Annotation
|
||||
|
||||
When you need to prevent specific ConfigMaps or Secrets from triggering any reloads, use the ignore annotation on the resource itself:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap # or Secret
|
||||
metadata:
|
||||
name: my-config
|
||||
annotations:
|
||||
reloader.stakater.com/ignore: "true"
|
||||
```
|
||||
|
||||
This instructs Reloader to skip all reload logic for that resource across all workloads.
|
||||
|
||||
### 4. ⚙️ Workload-Specific Rollout Strategy
|
||||
|
||||
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.
|
||||
|
||||
@@ -264,6 +264,11 @@ func upgradeResource(clients kube.Clients, config util.Config, upgradeFuncs call
|
||||
}
|
||||
}
|
||||
|
||||
ignoreResourceAnnotatonValue := config.ResourceAnnotations[options.IgnoreResourceAnnotation]
|
||||
if ignoreResourceAnnotatonValue == "true" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// find correct annotation and update the resource
|
||||
annotations := upgradeFuncs.AnnotationsFunc(resource)
|
||||
annotationValue, found := annotations[config.Annotation]
|
||||
|
||||
@@ -52,6 +52,8 @@ var (
|
||||
arsConfigmapWithConfigMapAutoAnnotation = "testconfigmapwithconfigmapautoannotationdeployment-handler-" + testutil.RandSeq(5)
|
||||
arsSecretWithExcludeSecretAnnotation = "testsecretwithsecretexcludeannotationdeployment-handler-" + testutil.RandSeq(5)
|
||||
arsConfigmapWithExcludeConfigMapAnnotation = "testconfigmapwithconfigmapexcludeannotationdeployment-handler-" + testutil.RandSeq(5)
|
||||
arsConfigmapWithIgnoreAnnotation = "testconfigmapWithIgnoreAnnotation-handler-" + testutil.RandSeq(5)
|
||||
arsSecretWithIgnoreAnnotation = "testsecretWithIgnoreAnnotation-handler-" + testutil.RandSeq(5)
|
||||
|
||||
ersNamespace = "test-handler-" + testutil.RandSeq(5)
|
||||
ersConfigmapName = "testconfigmap-handler-" + testutil.RandSeq(5)
|
||||
@@ -75,6 +77,8 @@ var (
|
||||
ersConfigmapWithConfigMapAutoAnnotation = "testconfigmapwithconfigmapautoannotationdeployment-handler-" + testutil.RandSeq(5)
|
||||
ersSecretWithSecretExcludeAnnotation = "testsecretwithsecretexcludeannotationdeployment-handler-" + testutil.RandSeq(5)
|
||||
ersConfigmapWithConfigMapExcludeAnnotation = "testconfigmapwithconfigmapexcludeannotationdeployment-handler-" + testutil.RandSeq(5)
|
||||
ersConfigmapWithIgnoreAnnotation = "testconfigmapWithIgnoreAnnotation-handler-" + testutil.RandSeq(5)
|
||||
ersSecretWithIgnoreAnnotation = "testsecretWithIgnoreAnnotation-handler-" + testutil.RandSeq(5)
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
@@ -215,6 +219,35 @@ func setupArs() {
|
||||
logrus.Errorf("Error in configmap creation: %v", err)
|
||||
}
|
||||
|
||||
// Creating configmap with ignore annotation
|
||||
_, err = testutil.CreateConfigMap(clients.KubernetesClient, arsNamespace, arsConfigmapWithIgnoreAnnotation, "www.google.com")
|
||||
if err != nil {
|
||||
logrus.Errorf("Error in configmap creation: %v", err)
|
||||
}
|
||||
// Patch with ignore annotation
|
||||
cmClient := clients.KubernetesClient.CoreV1().ConfigMaps(arsNamespace)
|
||||
patch := []byte(`{"metadata":{"annotations":{"reloader.stakater.com/ignore":"true"}}}`)
|
||||
_, _ = cmClient.Patch(context.TODO(), arsConfigmapWithIgnoreAnnotation, patchtypes.MergePatchType, patch, metav1.PatchOptions{})
|
||||
|
||||
// Creating secret with ignore annotation
|
||||
_, err = testutil.CreateSecret(clients.KubernetesClient, arsNamespace, arsSecretWithIgnoreAnnotation, data)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error in secret creation: %v", err)
|
||||
}
|
||||
secretClient := clients.KubernetesClient.CoreV1().Secrets(arsNamespace)
|
||||
_, _ = secretClient.Patch(context.TODO(), arsSecretWithIgnoreAnnotation, patchtypes.MergePatchType, patch, metav1.PatchOptions{})
|
||||
|
||||
// Creating Deployment referencing configmap with ignore annotation
|
||||
_, err = testutil.CreateDeployment(clients.KubernetesClient, arsConfigmapWithIgnoreAnnotation, arsNamespace, true)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error in Deployment with configmap ignore annotation creation: %v", err)
|
||||
}
|
||||
// Creating Deployment referencing secret with ignore annotation
|
||||
_, err = testutil.CreateDeployment(clients.KubernetesClient, arsSecretWithIgnoreAnnotation, arsNamespace, true)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error in Deployment with secret ignore annotation creation: %v", err)
|
||||
}
|
||||
|
||||
// Creating Deployment with configmap
|
||||
_, err = testutil.CreateDeployment(clients.KubernetesClient, arsConfigmapName, arsNamespace, true)
|
||||
if err != nil {
|
||||
@@ -854,6 +887,34 @@ func setupErs() {
|
||||
logrus.Errorf("Error in configmap creation: %v", err)
|
||||
}
|
||||
|
||||
// Creating configmap with ignore annotation
|
||||
_, err = testutil.CreateConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapWithIgnoreAnnotation, "www.google.com")
|
||||
if err != nil {
|
||||
logrus.Errorf("Error in configmap creation: %v", err)
|
||||
}
|
||||
cmClient := clients.KubernetesClient.CoreV1().ConfigMaps(ersNamespace)
|
||||
patch := []byte(`{"metadata":{"annotations":{"reloader.stakater.com/ignore":"true"}}}`)
|
||||
_, _ = cmClient.Patch(context.TODO(), ersConfigmapWithIgnoreAnnotation, patchtypes.MergePatchType, patch, metav1.PatchOptions{})
|
||||
|
||||
// Creating secret with ignore annotation
|
||||
_, err = testutil.CreateSecret(clients.KubernetesClient, ersNamespace, ersSecretWithIgnoreAnnotation, data)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error in secret creation: %v", err)
|
||||
}
|
||||
secretClient := clients.KubernetesClient.CoreV1().Secrets(ersNamespace)
|
||||
_, _ = secretClient.Patch(context.TODO(), ersSecretWithIgnoreAnnotation, patchtypes.MergePatchType, patch, metav1.PatchOptions{})
|
||||
|
||||
// Creating Deployment referencing configmap with ignore annotation
|
||||
_, err = testutil.CreateDeployment(clients.KubernetesClient, ersConfigmapWithIgnoreAnnotation, ersNamespace, true)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error in Deployment with configmap ignore annotation creation: %v", err)
|
||||
}
|
||||
// Creating Deployment referencing secret with ignore annotation
|
||||
_, err = testutil.CreateDeployment(clients.KubernetesClient, ersSecretWithIgnoreAnnotation, ersNamespace, true)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error in Deployment with secret ignore annotation creation: %v", err)
|
||||
}
|
||||
|
||||
// Creating Deployment with configmap
|
||||
_, err = testutil.CreateDeployment(clients.KubernetesClient, ersConfigmapName, ersNamespace, true)
|
||||
if err != nil {
|
||||
@@ -2737,6 +2798,65 @@ func TestFailedRollingUpgradeUsingArs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIgnoreAnnotationNoReloadUsingArs(t *testing.T) {
|
||||
options.ReloadStrategy = constants.AnnotationsReloadStrategy
|
||||
envVarPostfix := constants.ConfigmapEnvVarPostfix
|
||||
|
||||
shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigmapWithIgnoreAnnotation, "www.stakater.com")
|
||||
config := getConfigWithAnnotations(envVarPostfix, arsConfigmapWithIgnoreAnnotation, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation)
|
||||
config.ResourceAnnotations = map[string]string{"reloader.stakater.com/ignore": "true"}
|
||||
deploymentFuncs := GetDeploymentRollingUpgradeFuncs()
|
||||
collectors := getCollectors()
|
||||
|
||||
err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy)
|
||||
if err != nil {
|
||||
t.Errorf("Rolling upgrade failed for Deployment with Configmap and ignore annotation using ARS")
|
||||
}
|
||||
|
||||
// Ensure deployment is NOT updated
|
||||
updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs)
|
||||
if updated {
|
||||
t.Errorf("Deployment was updated but should not have been")
|
||||
}
|
||||
|
||||
// Ensure counters remain zero
|
||||
if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 0 {
|
||||
t.Errorf("Reload counter should not have increased")
|
||||
}
|
||||
if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 0 {
|
||||
t.Errorf("Reload counter by namespace should not have increased")
|
||||
}
|
||||
}
|
||||
func TestIgnoreAnnotationNoReloadUsingErs(t *testing.T) {
|
||||
options.ReloadStrategy = constants.EnvVarsReloadStrategy
|
||||
envVarPostfix := constants.ConfigmapEnvVarPostfix
|
||||
|
||||
shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapWithIgnoreAnnotation, "www.stakater.com")
|
||||
config := getConfigWithAnnotations(envVarPostfix, ersConfigmapWithIgnoreAnnotation, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation)
|
||||
config.ResourceAnnotations = map[string]string{"reloader.stakater.com/ignore": "true"}
|
||||
deploymentFuncs := GetDeploymentRollingUpgradeFuncs()
|
||||
collectors := getCollectors()
|
||||
|
||||
err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy)
|
||||
if err != nil {
|
||||
t.Errorf("Rolling upgrade failed for Deployment with Configmap and ignore annotation using ERS")
|
||||
}
|
||||
|
||||
// Ensure deployment is NOT updated
|
||||
updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs)
|
||||
if updated {
|
||||
t.Errorf("Deployment was updated but should not have been (ERS)")
|
||||
}
|
||||
|
||||
// Ensure counters remain zero
|
||||
if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 0 {
|
||||
t.Errorf("Reload counter should not have increased (ERS)")
|
||||
}
|
||||
if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 0 {
|
||||
t.Errorf("Reload counter by namespace should not have increased (ERS)")
|
||||
}
|
||||
}
|
||||
|
||||
func testRollingUpgradeInvokeDeleteStrategyErs(t *testing.T, clients kube.Clients, config util.Config, upgradeFuncs callbacks.RollingUpgradeFuncs, collectors metrics.Collectors, envVarPostfix string) {
|
||||
err := PerformAction(clients, config, upgradeFuncs, collectors, nil, invokeDeleteStrategy)
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
@@ -22,6 +22,8 @@ var (
|
||||
SecretUpdateOnChangeAnnotation = "secret.reloader.stakater.com/reload"
|
||||
// ReloaderAutoAnnotation is an annotation to detect changes in secrets/configmaps
|
||||
ReloaderAutoAnnotation = "reloader.stakater.com/auto"
|
||||
// IgnoreResourceAnnotation is an annotation to ignore changes in secrets/configmaps
|
||||
IgnoreResourceAnnotation = "reloader.stakater.com/ignore"
|
||||
// ConfigmapReloaderAutoAnnotation is an annotation to detect changes in configmaps
|
||||
ConfigmapReloaderAutoAnnotation = "configmap.reloader.stakater.com/auto"
|
||||
// SecretReloaderAutoAnnotation is an annotation to detect changes in secrets
|
||||
|
||||
Reference in New Issue
Block a user