mirror of
https://github.com/stakater/Reloader.git
synced 2026-02-14 09:59:50 +00:00
fix: Controller not respecting ignore* flags
This commit is contained in:
committed by
Felix
parent
b8edc25177
commit
dd0807e951
@@ -1,3 +1,3 @@
|
|||||||
# Code of Conduct
|
# Code of Conduct
|
||||||
|
|
||||||
Reloader follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
|
Reloader follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md).
|
||||||
|
|||||||
23
README.md
23
README.md
@@ -329,13 +329,30 @@ Reloader supports multiple strategies for triggering rolling updates when a watc
|
|||||||
|------|-------------|
|
|------|-------------|
|
||||||
| `--resources-to-ignore=configmaps` | Ignore ConfigMaps (only one type can be ignored at a time) |
|
| `--resources-to-ignore=configmaps` | Ignore ConfigMaps (only one type can be ignored at a time) |
|
||||||
| `--resources-to-ignore=secrets` | Ignore Secrets (cannot combine with configMaps) |
|
| `--resources-to-ignore=secrets` | Ignore Secrets (cannot combine with configMaps) |
|
||||||
|
| `--ignored-workload-types=jobs,cronjobs` | Ignore specific workload types from reload monitoring |
|
||||||
| `--resource-label-selector=key=value` | Only watch ConfigMaps/Secrets with matching labels |
|
| `--resource-label-selector=key=value` | Only watch ConfigMaps/Secrets with matching labels |
|
||||||
|
|
||||||
> **⚠️ Note:**
|
> **⚠️ Note:**
|
||||||
> Only **one** resource type can be ignored at a time.
|
>
|
||||||
> Trying to ignore **both `configmaps` and `secrets`** will cause an error in Reloader.
|
> Only **one** resource type can be ignored at a time.
|
||||||
|
> Trying to ignore **both `configmaps` and `secrets`** will cause an error in Reloader.
|
||||||
> ✅ **Workaround:** Scale the Reloader deployment to `0` replicas if you want to disable it completely.
|
> ✅ **Workaround:** Scale the Reloader deployment to `0` replicas if you want to disable it completely.
|
||||||
|
|
||||||
|
**💡 Workload Type Examples:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ignore only Jobs
|
||||||
|
--ignored-workload-types=jobs
|
||||||
|
|
||||||
|
# Ignore only CronJobs
|
||||||
|
--ignored-workload-types=cronjobs
|
||||||
|
|
||||||
|
# Ignore both (comma-separated)
|
||||||
|
--ignored-workload-types=jobs,cronjobs
|
||||||
|
```
|
||||||
|
|
||||||
|
> **🔧 Use Case:** Ignoring workload types is useful when you don't want certain types of workloads to be automatically reloaded.
|
||||||
|
|
||||||
#### 3. 🧩 Namespace Filtering
|
#### 3. 🧩 Namespace Filtering
|
||||||
|
|
||||||
| Flag | Description |
|
| Flag | Description |
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ helm install stakater/reloader # For helm3 add --generate-name flag or set the r
|
|||||||
helm install {{RELEASE_NAME}} stakater/reloader -n {{NAMESPACE}} --set reloader.watchGlobally=false # By default, Reloader watches in all namespaces. To watch in single namespace, set watchGlobally=false
|
helm install {{RELEASE_NAME}} stakater/reloader -n {{NAMESPACE}} --set reloader.watchGlobally=false # By default, Reloader watches in all namespaces. To watch in single namespace, set watchGlobally=false
|
||||||
|
|
||||||
helm install stakater/reloader --set reloader.watchGlobally=false --namespace test --generate-name # Install Reloader in `test` namespace which will only watch `Deployments`, `Daemonsets` `Statefulsets` and `Rollouts` in `test` namespace.
|
helm install stakater/reloader --set reloader.watchGlobally=false --namespace test --generate-name # Install Reloader in `test` namespace which will only watch `Deployments`, `Daemonsets` `Statefulsets` and `Rollouts` in `test` namespace.
|
||||||
|
|
||||||
|
helm install stakater/reloader --set reloader.ignoreJobs=true --set reloader.ignoreCronJobs=true --generate-name # Install Reloader ignoring Jobs and CronJobs from reload monitoring
|
||||||
```
|
```
|
||||||
|
|
||||||
## Uninstalling
|
## Uninstalling
|
||||||
@@ -48,6 +50,8 @@ helm uninstall {{RELEASE_NAME}} -n {{NAMESPACE}}
|
|||||||
| `reloader.isOpenshift` | Enable OpenShift DeploymentConfigs. Valid value are either `true` or `false` | boolean | `false` |
|
| `reloader.isOpenshift` | Enable OpenShift DeploymentConfigs. Valid value are either `true` or `false` | boolean | `false` |
|
||||||
| `reloader.ignoreSecrets` | To ignore secrets. Valid value are either `true` or `false`. Either `ignoreSecrets` or `ignoreConfigMaps` can be ignored, not both at the same time | boolean | `false` |
|
| `reloader.ignoreSecrets` | To ignore secrets. Valid value are either `true` or `false`. Either `ignoreSecrets` or `ignoreConfigMaps` can be ignored, not both at the same time | boolean | `false` |
|
||||||
| `reloader.ignoreConfigMaps` | To ignore configmaps. Valid value are either `true` or `false` | boolean | `false` |
|
| `reloader.ignoreConfigMaps` | To ignore configmaps. Valid value are either `true` or `false` | boolean | `false` |
|
||||||
|
| `reloader.ignoreJobs` | To ignore jobs from reload monitoring. Valid value are either `true` or `false`. Translates to `--ignored-workload-types=jobs` | boolean | `false` |
|
||||||
|
| `reloader.ignoreCronJobs` | To ignore CronJobs from reload monitoring. Valid value are either `true` or `false`. Translates to `--ignored-workload-types=cronjobs` | boolean | `false` |
|
||||||
| `reloader.reloadOnCreate` | Enable reload on create events. Valid value are either `true` or `false` | boolean | `false` |
|
| `reloader.reloadOnCreate` | Enable reload on create events. Valid value are either `true` or `false` | boolean | `false` |
|
||||||
| `reloader.reloadOnDelete` | Enable reload on delete events. Valid value are either `true` or `false` | boolean | `false` |
|
| `reloader.reloadOnDelete` | Enable reload on delete events. Valid value are either `true` or `false` | boolean | `false` |
|
||||||
| `reloader.syncAfterRestart` | Enable sync after Reloader restarts for **Add** events, works only when reloadOnCreate is `true`. Valid value are either `true` or `false` | boolean | `false` |
|
| `reloader.syncAfterRestart` | Enable sync after Reloader restarts for **Add** events, works only when reloadOnCreate is `true`. Valid value are either `true` or `false` | boolean | `false` |
|
||||||
@@ -59,7 +63,7 @@ helm uninstall {{RELEASE_NAME}} -n {{NAMESPACE}}
|
|||||||
| `reloader.watchGlobally` | Allow Reloader to watch in all namespaces (`true`) or just in a single namespace (`false`) | boolean | `true` |
|
| `reloader.watchGlobally` | Allow Reloader to watch in all namespaces (`true`) or just in a single namespace (`false`) | boolean | `true` |
|
||||||
| `reloader.enableHA` | Enable leadership election allowing you to run multiple replicas | boolean | `false` |
|
| `reloader.enableHA` | Enable leadership election allowing you to run multiple replicas | boolean | `false` |
|
||||||
| `reloader.enablePProf` | Enables pprof for profiling | boolean | `false` |
|
| `reloader.enablePProf` | Enables pprof for profiling | boolean | `false` |
|
||||||
| `reloader.pprofAddr` | Address to start pprof server on | string | `:6060` |
|
| `reloader.pprofAddr` | Address to start pprof server on | string | `:6060` |
|
||||||
| `reloader.readOnlyRootFileSystem` | Enforce readOnlyRootFilesystem | boolean | `false` |
|
| `reloader.readOnlyRootFileSystem` | Enforce readOnlyRootFilesystem | boolean | `false` |
|
||||||
| `reloader.legacy.rbac` | | boolean | `false` |
|
| `reloader.legacy.rbac` | | boolean | `false` |
|
||||||
| `reloader.matchLabels` | Pod labels to match | map | `{}` |
|
| `reloader.matchLabels` | Pod labels to match | map | `{}` |
|
||||||
@@ -116,6 +120,10 @@ helm uninstall {{RELEASE_NAME}} -n {{NAMESPACE}}
|
|||||||
- Only one of these resources can be ignored at a time:
|
- Only one of these resources can be ignored at a time:
|
||||||
- `ignoreConfigMaps` **or** `ignoreSecrets`
|
- `ignoreConfigMaps` **or** `ignoreSecrets`
|
||||||
- Trying to ignore both will cause Helm template compilation errors
|
- Trying to ignore both will cause Helm template compilation errors
|
||||||
|
- The `ignoreJobs` and `ignoreCronJobs` flags can be used together or individually
|
||||||
|
- When both are enabled, translates to `--ignored-workload-types=jobs,cronjobs`
|
||||||
|
- When used individually, translates to `--ignored-workload-types=jobs` or `--ignored-workload-types=cronjobs`
|
||||||
|
- These flags prevent Reloader from monitoring and reloading the specified workload types
|
||||||
|
|
||||||
### Special Integrations
|
### Special Integrations
|
||||||
- OpenShift (`DeploymentConfig`) and Argo Rollouts support must be **explicitly enabled**
|
- OpenShift (`DeploymentConfig`) and Argo Rollouts support must be **explicitly enabled**
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ spec:
|
|||||||
|
|
||||||
- name: RELOADER_DEPLOYMENT_NAME
|
- name: RELOADER_DEPLOYMENT_NAME
|
||||||
value: {{ template "reloader-fullname" . }}
|
value: {{ template "reloader-fullname" . }}
|
||||||
|
|
||||||
{{- if .Values.reloader.enableHA }}
|
{{- if .Values.reloader.enableHA }}
|
||||||
- name: POD_NAME
|
- name: POD_NAME
|
||||||
valueFrom:
|
valueFrom:
|
||||||
@@ -210,7 +210,7 @@ spec:
|
|||||||
{{- . | toYaml | nindent 10 }}
|
{{- . | toYaml | nindent 10 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if or (.Values.reloader.logFormat) (.Values.reloader.logLevel) (.Values.reloader.ignoreSecrets) (.Values.reloader.ignoreNamespaces) (include "reloader-namespaceSelector" .) (.Values.reloader.resourceLabelSelector) (.Values.reloader.ignoreConfigMaps) (.Values.reloader.custom_annotations) (eq .Values.reloader.isArgoRollouts true) (eq .Values.reloader.reloadOnCreate true) (eq .Values.reloader.reloadOnDelete true) (ne .Values.reloader.reloadStrategy "default") (.Values.reloader.enableHA) (.Values.reloader.autoReloadAll)}}
|
{{- if or (.Values.reloader.logFormat) (.Values.reloader.logLevel) (.Values.reloader.ignoreSecrets) (.Values.reloader.ignoreNamespaces) (include "reloader-namespaceSelector" .) (.Values.reloader.resourceLabelSelector) (.Values.reloader.ignoreConfigMaps) (.Values.reloader.custom_annotations) (eq .Values.reloader.isArgoRollouts true) (eq .Values.reloader.reloadOnCreate true) (eq .Values.reloader.reloadOnDelete true) (ne .Values.reloader.reloadStrategy "default") (.Values.reloader.enableHA) (.Values.reloader.autoReloadAll) (.Values.reloader.ignoreJobs) (.Values.reloader.ignoreCronJobs)}}
|
||||||
args:
|
args:
|
||||||
{{- if .Values.reloader.logFormat }}
|
{{- if .Values.reloader.logFormat }}
|
||||||
- "--log-format={{ .Values.reloader.logFormat }}"
|
- "--log-format={{ .Values.reloader.logFormat }}"
|
||||||
@@ -224,6 +224,13 @@ spec:
|
|||||||
{{- if .Values.reloader.ignoreConfigMaps }}
|
{{- if .Values.reloader.ignoreConfigMaps }}
|
||||||
- "--resources-to-ignore=configMaps"
|
- "--resources-to-ignore=configMaps"
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- if and (.Values.reloader.ignoreJobs) (.Values.reloader.ignoreCronJobs) }}
|
||||||
|
- "--ignored-workload-types=jobs,cronjobs"
|
||||||
|
{{- else if .Values.reloader.ignoreJobs }}
|
||||||
|
- "--ignored-workload-types=jobs"
|
||||||
|
{{- else if .Values.reloader.ignoreCronJobs }}
|
||||||
|
- "--ignored-workload-types=cronjobs"
|
||||||
|
{{- end }}
|
||||||
{{- if .Values.reloader.ignoreNamespaces }}
|
{{- if .Values.reloader.ignoreNamespaces }}
|
||||||
- "--namespaces-to-ignore={{ .Values.reloader.ignoreNamespaces }}"
|
- "--namespaces-to-ignore={{ .Values.reloader.ignoreNamespaces }}"
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|||||||
@@ -61,3 +61,44 @@ tests:
|
|||||||
valueFrom:
|
valueFrom:
|
||||||
fieldRef:
|
fieldRef:
|
||||||
fieldPath: metadata.name
|
fieldPath: metadata.name
|
||||||
|
|
||||||
|
- it: sets ignored-workload-types argument when ignoreJobs is true
|
||||||
|
set:
|
||||||
|
reloader:
|
||||||
|
ignoreJobs: true
|
||||||
|
asserts:
|
||||||
|
- contains:
|
||||||
|
path: spec.template.spec.containers[0].args
|
||||||
|
content: "--ignored-workload-types=jobs"
|
||||||
|
|
||||||
|
- it: sets ignored-workload-types argument when ignoreCronJobs is true
|
||||||
|
set:
|
||||||
|
reloader:
|
||||||
|
ignoreCronJobs: true
|
||||||
|
asserts:
|
||||||
|
- contains:
|
||||||
|
path: spec.template.spec.containers[0].args
|
||||||
|
content: "--ignored-workload-types=cronjobs"
|
||||||
|
|
||||||
|
- it: sets ignored-workload-types argument when both ignoreJobs and ignoreCronJobs are true
|
||||||
|
set:
|
||||||
|
reloader:
|
||||||
|
ignoreJobs: true
|
||||||
|
ignoreCronJobs: true
|
||||||
|
asserts:
|
||||||
|
- contains:
|
||||||
|
path: spec.template.spec.containers[0].args
|
||||||
|
content: "--ignored-workload-types=jobs,cronjobs"
|
||||||
|
|
||||||
|
- it: does not set ignored-workload-types argument when both ignoreJobs and ignoreCronJobs are false
|
||||||
|
set:
|
||||||
|
reloader:
|
||||||
|
ignoreJobs: false
|
||||||
|
ignoreCronJobs: false
|
||||||
|
asserts:
|
||||||
|
- notContains:
|
||||||
|
path: spec.template.spec.containers[0].args
|
||||||
|
content: "--ignored-workload-types=jobs"
|
||||||
|
- notContains:
|
||||||
|
path: spec.template.spec.containers[0].args
|
||||||
|
content: "--ignored-workload-types=cronjobs"
|
||||||
|
|||||||
@@ -27,7 +27,11 @@ reloader:
|
|||||||
isOpenshift: false
|
isOpenshift: false
|
||||||
ignoreSecrets: false
|
ignoreSecrets: false
|
||||||
ignoreConfigMaps: false
|
ignoreConfigMaps: false
|
||||||
|
# Set to true to exclude Job workloads from automatic reload monitoring
|
||||||
|
# Useful when you don't want Jobs to be restarted when their referenced ConfigMaps/Secrets change
|
||||||
ignoreJobs: false
|
ignoreJobs: false
|
||||||
|
# Set to true to exclude CronJob workloads from automatic reload monitoring
|
||||||
|
# Useful when you don't want CronJobs to be restarted when their referenced ConfigMaps/Secrets change
|
||||||
ignoreCronJobs: false
|
ignoreCronJobs: false
|
||||||
reloadOnCreate: false
|
reloadOnCreate: false
|
||||||
reloadOnDelete: false
|
reloadOnDelete: false
|
||||||
@@ -84,7 +88,7 @@ reloader:
|
|||||||
# - key: "node-role.kubernetes.io/infra-worker"
|
# - key: "node-role.kubernetes.io/infra-worker"
|
||||||
# operator: "Exists"
|
# operator: "Exists"
|
||||||
affinity: {}
|
affinity: {}
|
||||||
|
|
||||||
volumeMounts: []
|
volumeMounts: []
|
||||||
volumes: []
|
volumes: []
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ var (
|
|||||||
WebhookUrl = ""
|
WebhookUrl = ""
|
||||||
// ResourcesToIgnore is a list of resources to ignore when watching for changes
|
// ResourcesToIgnore is a list of resources to ignore when watching for changes
|
||||||
ResourcesToIgnore = []string{}
|
ResourcesToIgnore = []string{}
|
||||||
|
// WorkloadTypesToIgnore is a list of workload types to ignore when watching for changes
|
||||||
|
WorkloadTypesToIgnore = []string{}
|
||||||
// NamespacesToIgnore is a list of namespace names to ignore when watching for changes
|
// NamespacesToIgnore is a list of namespace names to ignore when watching for changes
|
||||||
NamespacesToIgnore = []string{}
|
NamespacesToIgnore = []string{}
|
||||||
// NamespaceSelectors is a list of namespace selectors to watch for changes
|
// NamespaceSelectors is a list of namespace selectors to watch for changes
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ func ConfigureReloaderFlags(cmd *cobra.Command) {
|
|||||||
cmd.PersistentFlags().StringVar(&options.LogLevel, "log-level", "info", "Log level to use (trace, debug, info, warning, error, fatal and panic)")
|
cmd.PersistentFlags().StringVar(&options.LogLevel, "log-level", "info", "Log level to use (trace, debug, info, warning, error, fatal and panic)")
|
||||||
cmd.PersistentFlags().StringVar(&options.WebhookUrl, "webhook-url", "", "webhook to trigger instead of performing a reload")
|
cmd.PersistentFlags().StringVar(&options.WebhookUrl, "webhook-url", "", "webhook to trigger instead of performing a reload")
|
||||||
cmd.PersistentFlags().StringSliceVar(&options.ResourcesToIgnore, "resources-to-ignore", options.ResourcesToIgnore, "list of resources to ignore (valid options 'configMaps' or 'secrets')")
|
cmd.PersistentFlags().StringSliceVar(&options.ResourcesToIgnore, "resources-to-ignore", options.ResourcesToIgnore, "list of resources to ignore (valid options 'configMaps' or 'secrets')")
|
||||||
|
cmd.PersistentFlags().StringSliceVar(&options.WorkloadTypesToIgnore, "ignored-workload-types", options.WorkloadTypesToIgnore, "list of workload types to ignore (valid options: 'jobs', 'cronjobs', or both)")
|
||||||
cmd.PersistentFlags().StringSliceVar(&options.NamespacesToIgnore, "namespaces-to-ignore", options.NamespacesToIgnore, "list of namespaces to ignore")
|
cmd.PersistentFlags().StringSliceVar(&options.NamespacesToIgnore, "namespaces-to-ignore", options.NamespacesToIgnore, "list of namespaces to ignore")
|
||||||
cmd.PersistentFlags().StringSliceVar(&options.NamespaceSelectors, "namespace-selector", options.NamespaceSelectors, "list of key:value labels to filter on for namespaces")
|
cmd.PersistentFlags().StringSliceVar(&options.NamespaceSelectors, "namespace-selector", options.NamespaceSelectors, "list of key:value labels to filter on for namespaces")
|
||||||
cmd.PersistentFlags().StringSliceVar(&options.ResourceSelectors, "resource-label-selector", options.ResourceSelectors, "list of key:value labels to filter on for configmaps and secrets")
|
cmd.PersistentFlags().StringSliceVar(&options.ResourceSelectors, "resource-label-selector", options.ResourceSelectors, "list of key:value labels to filter on for configmaps and secrets")
|
||||||
@@ -112,3 +113,16 @@ func GetIgnoredResourcesList() (List, error) {
|
|||||||
|
|
||||||
return ignoredResourcesList, nil
|
return ignoredResourcesList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetIgnoredWorkloadTypesList() (List, error) {
|
||||||
|
|
||||||
|
ignoredWorkloadTypesList := options.WorkloadTypesToIgnore
|
||||||
|
|
||||||
|
for _, v := range ignoredWorkloadTypesList {
|
||||||
|
if v != "jobs" && v != "cronjobs" {
|
||||||
|
return nil, fmt.Errorf("'ignored-workload-types' accepts 'jobs', 'cronjobs', or both, not '%s'", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ignoredWorkloadTypesList, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package util
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stakater/Reloader/internal/pkg/options"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -45,3 +46,141 @@ func TestGetHashFromConfigMap(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetIgnoredWorkloadTypesList(t *testing.T) {
|
||||||
|
// Save original state
|
||||||
|
originalWorkloadTypes := options.WorkloadTypesToIgnore
|
||||||
|
defer func() {
|
||||||
|
options.WorkloadTypesToIgnore = originalWorkloadTypes
|
||||||
|
}()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
workloadTypes []string
|
||||||
|
expectError bool
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Both jobs and cronjobs",
|
||||||
|
workloadTypes: []string{"jobs", "cronjobs"},
|
||||||
|
expectError: false,
|
||||||
|
expected: []string{"jobs", "cronjobs"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Only jobs",
|
||||||
|
workloadTypes: []string{"jobs"},
|
||||||
|
expectError: false,
|
||||||
|
expected: []string{"jobs"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Only cronjobs",
|
||||||
|
workloadTypes: []string{"cronjobs"},
|
||||||
|
expectError: false,
|
||||||
|
expected: []string{"cronjobs"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty list",
|
||||||
|
workloadTypes: []string{},
|
||||||
|
expectError: false,
|
||||||
|
expected: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid workload type",
|
||||||
|
workloadTypes: []string{"invalid"},
|
||||||
|
expectError: true,
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Mixed valid and invalid",
|
||||||
|
workloadTypes: []string{"jobs", "invalid"},
|
||||||
|
expectError: true,
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Duplicate values",
|
||||||
|
workloadTypes: []string{"jobs", "jobs"},
|
||||||
|
expectError: false,
|
||||||
|
expected: []string{"jobs", "jobs"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// Set the global option
|
||||||
|
options.WorkloadTypesToIgnore = tt.workloadTypes
|
||||||
|
|
||||||
|
result, err := GetIgnoredWorkloadTypesList()
|
||||||
|
|
||||||
|
if tt.expectError && err == nil {
|
||||||
|
t.Errorf("Expected error but got none")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tt.expectError && err != nil {
|
||||||
|
t.Errorf("Expected no error but got: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tt.expectError {
|
||||||
|
if len(result) != len(tt.expected) {
|
||||||
|
t.Errorf("Expected %v, got %v", tt.expected, result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, expected := range tt.expected {
|
||||||
|
if i >= len(result) || result[i] != expected {
|
||||||
|
t.Errorf("Expected %v, got %v", tt.expected, result)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListContains(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
list List
|
||||||
|
item string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "List contains item",
|
||||||
|
list: List{"jobs", "cronjobs"},
|
||||||
|
item: "jobs",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "List does not contain item",
|
||||||
|
list: List{"jobs"},
|
||||||
|
item: "cronjobs",
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty list",
|
||||||
|
list: List{},
|
||||||
|
item: "jobs",
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Case sensitive matching",
|
||||||
|
list: List{"jobs", "cronjobs"},
|
||||||
|
item: "Jobs",
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiple occurrences",
|
||||||
|
list: List{"jobs", "jobs", "cronjobs"},
|
||||||
|
item: "jobs",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := tt.list.Contains(tt.item)
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("Expected %v, got %v", tt.expected, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/stakater/Reloader/internal/pkg/constants"
|
"github.com/stakater/Reloader/internal/pkg/constants"
|
||||||
"github.com/stakater/Reloader/internal/pkg/options"
|
"github.com/stakater/Reloader/internal/pkg/options"
|
||||||
|
"github.com/stakater/Reloader/internal/pkg/util"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
@@ -74,6 +75,8 @@ type ReloaderOptions struct {
|
|||||||
WebhookUrl string `json:"webhookUrl"`
|
WebhookUrl string `json:"webhookUrl"`
|
||||||
// ResourcesToIgnore is a list of resource types to ignore (e.g., "configmaps" or "secrets")
|
// ResourcesToIgnore is a list of resource types to ignore (e.g., "configmaps" or "secrets")
|
||||||
ResourcesToIgnore []string `json:"resourcesToIgnore"`
|
ResourcesToIgnore []string `json:"resourcesToIgnore"`
|
||||||
|
// WorkloadTypesToIgnore is a list of workload types to ignore (e.g., "jobs" or "cronjobs")
|
||||||
|
WorkloadTypesToIgnore []string `json:"workloadTypesToIgnore"`
|
||||||
// NamespaceSelectors is a list of label selectors to filter namespaces to watch
|
// NamespaceSelectors is a list of label selectors to filter namespaces to watch
|
||||||
NamespaceSelectors []string `json:"namespaceSelectors"`
|
NamespaceSelectors []string `json:"namespaceSelectors"`
|
||||||
// ResourceSelectors is a list of label selectors to filter ConfigMaps and Secrets to watch
|
// ResourceSelectors is a list of label selectors to filter ConfigMaps and Secrets to watch
|
||||||
@@ -182,6 +185,32 @@ func GetResourceLabelSelector(slice []string) (string, error) {
|
|||||||
// ShouldReload checks if a resource should be reloaded based on its annotations and the provided options.
|
// ShouldReload checks if a resource should be reloaded based on its annotations and the provided options.
|
||||||
func ShouldReload(config Config, resourceType string, annotations Map, podAnnotations Map, options *ReloaderOptions) ReloadCheckResult {
|
func ShouldReload(config Config, resourceType string, annotations Map, podAnnotations Map, options *ReloaderOptions) ReloadCheckResult {
|
||||||
|
|
||||||
|
// Check if this workload type should be ignored
|
||||||
|
if len(options.WorkloadTypesToIgnore) > 0 {
|
||||||
|
ignoredWorkloadTypes, err := util.GetIgnoredWorkloadTypesList()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Failed to parse ignored workload types: %v", err)
|
||||||
|
} else {
|
||||||
|
// Map Kubernetes resource types to CLI-friendly names for comparison
|
||||||
|
var resourceToCheck string
|
||||||
|
switch resourceType {
|
||||||
|
case "Job":
|
||||||
|
resourceToCheck = "jobs"
|
||||||
|
case "CronJob":
|
||||||
|
resourceToCheck = "cronjobs"
|
||||||
|
default:
|
||||||
|
resourceToCheck = resourceType // For other types, use as-is
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if current resource type should be ignored
|
||||||
|
if ignoredWorkloadTypes.Contains(resourceToCheck) {
|
||||||
|
return ReloadCheckResult{
|
||||||
|
ShouldReload: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ignoreResourceAnnotatonValue := config.ResourceAnnotations[options.IgnoreResourceAnnotation]
|
ignoreResourceAnnotatonValue := config.ResourceAnnotations[options.IgnoreResourceAnnotation]
|
||||||
if ignoreResourceAnnotatonValue == "true" {
|
if ignoreResourceAnnotatonValue == "true" {
|
||||||
return ReloadCheckResult{
|
return ReloadCheckResult{
|
||||||
@@ -304,6 +333,7 @@ func GetCommandLineOptions() *ReloaderOptions {
|
|||||||
CommandLineOptions.EnableHA = options.EnableHA
|
CommandLineOptions.EnableHA = options.EnableHA
|
||||||
CommandLineOptions.WebhookUrl = options.WebhookUrl
|
CommandLineOptions.WebhookUrl = options.WebhookUrl
|
||||||
CommandLineOptions.ResourcesToIgnore = options.ResourcesToIgnore
|
CommandLineOptions.ResourcesToIgnore = options.ResourcesToIgnore
|
||||||
|
CommandLineOptions.WorkloadTypesToIgnore = options.WorkloadTypesToIgnore
|
||||||
CommandLineOptions.NamespaceSelectors = options.NamespaceSelectors
|
CommandLineOptions.NamespaceSelectors = options.NamespaceSelectors
|
||||||
CommandLineOptions.ResourceSelectors = options.ResourceSelectors
|
CommandLineOptions.ResourceSelectors = options.ResourceSelectors
|
||||||
CommandLineOptions.NamespacesToIgnore = options.NamespacesToIgnore
|
CommandLineOptions.NamespacesToIgnore = options.NamespacesToIgnore
|
||||||
|
|||||||
224
pkg/common/common_test.go
Normal file
224
pkg/common/common_test.go
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stakater/Reloader/internal/pkg/options"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestShouldReload_IgnoredWorkloadTypes(t *testing.T) {
|
||||||
|
// Save original state
|
||||||
|
originalWorkloadTypes := options.WorkloadTypesToIgnore
|
||||||
|
defer func() {
|
||||||
|
options.WorkloadTypesToIgnore = originalWorkloadTypes
|
||||||
|
}()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
ignoredWorkloadTypes []string
|
||||||
|
resourceType string
|
||||||
|
shouldReload bool
|
||||||
|
description string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Jobs ignored - Job should not reload",
|
||||||
|
ignoredWorkloadTypes: []string{"jobs"},
|
||||||
|
resourceType: "Job",
|
||||||
|
shouldReload: false,
|
||||||
|
description: "When jobs are ignored, Job resources should not be reloaded",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Jobs ignored - CronJob should reload",
|
||||||
|
ignoredWorkloadTypes: []string{"jobs"},
|
||||||
|
resourceType: "CronJob",
|
||||||
|
shouldReload: true,
|
||||||
|
description: "When jobs are ignored, CronJob resources should still be processed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "CronJobs ignored - CronJob should not reload",
|
||||||
|
ignoredWorkloadTypes: []string{"cronjobs"},
|
||||||
|
resourceType: "CronJob",
|
||||||
|
shouldReload: false,
|
||||||
|
description: "When cronjobs are ignored, CronJob resources should not be reloaded",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "CronJobs ignored - Job should reload",
|
||||||
|
ignoredWorkloadTypes: []string{"cronjobs"},
|
||||||
|
resourceType: "Job",
|
||||||
|
shouldReload: true,
|
||||||
|
description: "When cronjobs are ignored, Job resources should still be processed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Both ignored - Job should not reload",
|
||||||
|
ignoredWorkloadTypes: []string{"jobs", "cronjobs"},
|
||||||
|
resourceType: "Job",
|
||||||
|
shouldReload: false,
|
||||||
|
description: "When both are ignored, Job resources should not be reloaded",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Both ignored - CronJob should not reload",
|
||||||
|
ignoredWorkloadTypes: []string{"jobs", "cronjobs"},
|
||||||
|
resourceType: "CronJob",
|
||||||
|
shouldReload: false,
|
||||||
|
description: "When both are ignored, CronJob resources should not be reloaded",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Both ignored - Deployment should reload",
|
||||||
|
ignoredWorkloadTypes: []string{"jobs", "cronjobs"},
|
||||||
|
resourceType: "Deployment",
|
||||||
|
shouldReload: true,
|
||||||
|
description: "When both are ignored, other workload types should still be processed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "None ignored - Job should reload",
|
||||||
|
ignoredWorkloadTypes: []string{},
|
||||||
|
resourceType: "Job",
|
||||||
|
shouldReload: true,
|
||||||
|
description: "When nothing is ignored, all workload types should be processed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "None ignored - CronJob should reload",
|
||||||
|
ignoredWorkloadTypes: []string{},
|
||||||
|
resourceType: "CronJob",
|
||||||
|
shouldReload: true,
|
||||||
|
description: "When nothing is ignored, all workload types should be processed",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// Set the ignored workload types
|
||||||
|
options.WorkloadTypesToIgnore = tt.ignoredWorkloadTypes
|
||||||
|
|
||||||
|
// Create minimal test config and options
|
||||||
|
config := Config{
|
||||||
|
ResourceName: "test-resource",
|
||||||
|
Annotation: "configmap.reloader.stakater.com/reload",
|
||||||
|
}
|
||||||
|
|
||||||
|
annotations := Map{
|
||||||
|
"configmap.reloader.stakater.com/reload": "test-config",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create ReloaderOptions with the ignored workload types
|
||||||
|
opts := &ReloaderOptions{
|
||||||
|
WorkloadTypesToIgnore: tt.ignoredWorkloadTypes,
|
||||||
|
AutoReloadAll: true, // Enable auto-reload to simplify test
|
||||||
|
ReloaderAutoAnnotation: "reloader.stakater.com/auto",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call ShouldReload
|
||||||
|
result := ShouldReload(config, tt.resourceType, annotations, Map{}, opts)
|
||||||
|
|
||||||
|
// Check the result
|
||||||
|
if result.ShouldReload != tt.shouldReload {
|
||||||
|
t.Errorf("For resource type %s with ignored types %v, expected ShouldReload=%v, got=%v",
|
||||||
|
tt.resourceType, tt.ignoredWorkloadTypes, tt.shouldReload, result.ShouldReload)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("✓ %s", tt.description)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShouldReload_IgnoredWorkloadTypes_ValidationError(t *testing.T) {
|
||||||
|
// Save original state
|
||||||
|
originalWorkloadTypes := options.WorkloadTypesToIgnore
|
||||||
|
defer func() {
|
||||||
|
options.WorkloadTypesToIgnore = originalWorkloadTypes
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Test with invalid workload type - should still continue processing
|
||||||
|
options.WorkloadTypesToIgnore = []string{"invalid"}
|
||||||
|
|
||||||
|
config := Config{
|
||||||
|
ResourceName: "test-resource",
|
||||||
|
Annotation: "configmap.reloader.stakater.com/reload",
|
||||||
|
}
|
||||||
|
|
||||||
|
annotations := Map{
|
||||||
|
"configmap.reloader.stakater.com/reload": "test-config",
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := &ReloaderOptions{
|
||||||
|
WorkloadTypesToIgnore: []string{"invalid"},
|
||||||
|
AutoReloadAll: true, // Enable auto-reload to simplify test
|
||||||
|
ReloaderAutoAnnotation: "reloader.stakater.com/auto",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should not panic and should continue with normal processing
|
||||||
|
result := ShouldReload(config, "Job", annotations, Map{}, opts)
|
||||||
|
|
||||||
|
// Since validation failed, it should continue with normal processing (should reload)
|
||||||
|
if !result.ShouldReload {
|
||||||
|
t.Errorf("Expected ShouldReload=true when validation fails, got=%v", result.ShouldReload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that validates the fix for issue #996
|
||||||
|
func TestShouldReload_IssueRBACPermissionFixed(t *testing.T) {
|
||||||
|
// Save original state
|
||||||
|
originalWorkloadTypes := options.WorkloadTypesToIgnore
|
||||||
|
defer func() {
|
||||||
|
options.WorkloadTypesToIgnore = originalWorkloadTypes
|
||||||
|
}()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
ignoredWorkloadTypes []string
|
||||||
|
resourceType string
|
||||||
|
description string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Issue #996 - ignoreJobs prevents Job processing",
|
||||||
|
ignoredWorkloadTypes: []string{"jobs"},
|
||||||
|
resourceType: "Job",
|
||||||
|
description: "Job resources are skipped entirely, preventing RBAC permission errors",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Issue #996 - ignoreCronJobs prevents CronJob processing",
|
||||||
|
ignoredWorkloadTypes: []string{"cronjobs"},
|
||||||
|
resourceType: "CronJob",
|
||||||
|
description: "CronJob resources are skipped entirely, preventing RBAC permission errors",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Issue #996 - both ignored prevent both types",
|
||||||
|
ignoredWorkloadTypes: []string{"jobs", "cronjobs"},
|
||||||
|
resourceType: "Job",
|
||||||
|
description: "Job resources are skipped entirely when both types are ignored",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// Set the ignored workload types
|
||||||
|
options.WorkloadTypesToIgnore = tt.ignoredWorkloadTypes
|
||||||
|
|
||||||
|
config := Config{
|
||||||
|
ResourceName: "test-resource",
|
||||||
|
Annotation: "configmap.reloader.stakater.com/reload",
|
||||||
|
}
|
||||||
|
|
||||||
|
annotations := Map{
|
||||||
|
"configmap.reloader.stakater.com/reload": "test-config",
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := &ReloaderOptions{
|
||||||
|
WorkloadTypesToIgnore: tt.ignoredWorkloadTypes,
|
||||||
|
AutoReloadAll: true, // Enable auto-reload to simplify test
|
||||||
|
ReloaderAutoAnnotation: "reloader.stakater.com/auto",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call ShouldReload
|
||||||
|
result := ShouldReload(config, tt.resourceType, annotations, Map{}, opts)
|
||||||
|
|
||||||
|
// Should not reload when workload type is ignored
|
||||||
|
if result.ShouldReload {
|
||||||
|
t.Errorf("Expected ShouldReload=false for ignored workload type %s, got=%v",
|
||||||
|
tt.resourceType, result.ShouldReload)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("✓ %s", tt.description)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user