mirror of
https://github.com/stakater/Reloader.git
synced 2026-08-20 12:36:26 +00:00
Merge pull request #1162 from michal-marszalek-h2oai/951-namespace-scope-rbac
feat: scoped multi-namespace RBAC mode (Role per namespace, no ClusterRole)
This commit is contained in:
@@ -56,11 +56,12 @@ helm uninstall {{RELEASE_NAME}} -n {{NAMESPACE}}
|
||||
| `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.reloadStrategy` | Strategy to trigger resource restart, set to either `default`, `env-vars` or `annotations` | enumeration | `default` |
|
||||
| `reloader.ignoreNamespaces` | List of comma separated namespaces to ignore, if multiple are provided, they are combined with the AND operator | string | `""` |
|
||||
| `reloader.ignoreNamespaces` | List of comma separated namespaces to ignore, if multiple are provided, they are combined with the AND operator. Only honored when `reloader.watchGlobally` is `true`; in single-namespace and scoped (`reloader.namespaces`) modes the watched set is already explicit and this value is ignored. | string | `""` |
|
||||
| `reloader.namespaceSelector` | List of comma separated k8s label selectors for namespaces selection. The parameter only used when `reloader.watchGlobally` is `true`. See [LIST and WATCH filtering](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#list-and-watch-filtering) for more details on label-selector | string | `""` |
|
||||
| `reloader.resourceLabelSelector` | List of comma separated label selectors, if multiple are provided they are combined with the AND operator | string | `""` |
|
||||
| `reloader.logFormat` | Set type of log format. Value could be either `json` or `""` | string | `""` |
|
||||
| `reloader.watchGlobally` | Allow Reloader to watch in all namespaces (`true`) or just in a single namespace (`false`) | boolean | `true` |
|
||||
| `reloader.namespaces` | Explicit namespaces to watch (scoped mode). When non-empty and `reloader.watchGlobally` is `false`, Reloader watches exactly these namespaces and the chart creates a namespace-scoped Role + RoleBinding in each (no ClusterRole). The release namespace is always included automatically. Accepts either a YAML list (`["team-a","team-b"]`) or a comma-separated string (`"team-a,team-b"`). | list/string | `[]` |
|
||||
| `reloader.enableHA` | Enable leadership election allowing you to run multiple replicas | boolean | `false` |
|
||||
| `reloader.enablePProf` | Enables pprof for profiling | boolean | `false` |
|
||||
| `reloader.pprofAddr` | Address to start pprof server on | string | `:6060` |
|
||||
|
||||
@@ -88,6 +88,136 @@ Create the namespace selector if it does not watch globally
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Effective set of namespaces to watch in scoped mode: the release namespace
|
||||
(always included so the meta-info ConfigMap, HA leases and events keep working)
|
||||
plus the user-supplied reloader.namespaces, deduped and sorted.
|
||||
Returns a JSON-encoded list; consumers use mustFromJson to iterate.
|
||||
*/}}
|
||||
{{- define "reloader-watchNamespaces" -}}
|
||||
{{- $relNs := .Values.namespace | default .Release.Namespace -}}
|
||||
{{- $ns := .Values.reloader.namespaces | default list -}}
|
||||
{{- if kindIs "string" $ns -}}
|
||||
{{- $ns = splitList "," $ns -}}
|
||||
{{- end -}}
|
||||
{{- $clean := list -}}
|
||||
{{- range $ns -}}
|
||||
{{- $t := . | toString | trim -}}
|
||||
{{- if $t -}}
|
||||
{{- $clean = append $clean $t -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- $all := concat (list $relNs) $clean | uniq | sortAlpha -}}
|
||||
{{- $all | toJson -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Comma-joined form of reloader-watchNamespaces, for the --namespaces CLI flag.
|
||||
*/}}
|
||||
{{- define "reloader-watchNamespaces-csv" -}}
|
||||
{{- include "reloader-watchNamespaces" . | mustFromJson | join "," -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
The namespaced RBAC rules granted to Reloader in every watched namespace.
|
||||
Shared between the single-namespace Role and the per-namespace scoped Roles so
|
||||
the rule set is defined once. Expects the root context ($) as its argument.
|
||||
*/}}
|
||||
{{- define "reloader-namespaced-rules" }}
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
{{- if .Values.reloader.ignoreSecrets }}{{- else }}
|
||||
- secrets
|
||||
{{- end }}
|
||||
{{- if .Values.reloader.ignoreConfigMaps }}{{- else }}
|
||||
- configmaps
|
||||
{{- end }}
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- watch
|
||||
{{- if and (.Capabilities.APIVersions.Has "apps.openshift.io/v1") (.Values.reloader.isOpenshift) }}
|
||||
- apiGroups:
|
||||
- "apps.openshift.io"
|
||||
- ""
|
||||
resources:
|
||||
- deploymentconfigs
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- update
|
||||
- patch
|
||||
{{- end }}
|
||||
{{- if and (.Capabilities.APIVersions.Has "argoproj.io/v1alpha1") (.Values.reloader.isArgoRollouts) }}
|
||||
- apiGroups:
|
||||
- "argoproj.io"
|
||||
- ""
|
||||
resources:
|
||||
- rollouts
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- update
|
||||
- patch
|
||||
{{- end }}
|
||||
- apiGroups:
|
||||
- "apps"
|
||||
resources:
|
||||
- deployments
|
||||
- daemonsets
|
||||
- statefulsets
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- update
|
||||
- patch
|
||||
- apiGroups:
|
||||
- "batch"
|
||||
resources:
|
||||
- cronjobs
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- apiGroups:
|
||||
- "batch"
|
||||
resources:
|
||||
- jobs
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- list
|
||||
- get
|
||||
{{- if .Values.reloader.enableHA }}
|
||||
- apiGroups:
|
||||
- "coordination.k8s.io"
|
||||
resources:
|
||||
- leases
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
- update
|
||||
{{- end}}
|
||||
{{- if .Values.reloader.enableCSIIntegration }}
|
||||
- apiGroups:
|
||||
- "secrets-store.csi.x-k8s.io"
|
||||
resources:
|
||||
- secretproviderclasspodstatuses
|
||||
- secretproviderclasses
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- watch
|
||||
{{- end}}
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Normalizes global.imagePullSecrets to a list of objects with name fields.
|
||||
Supports both of these in values.yaml:
|
||||
|
||||
@@ -144,7 +144,7 @@ spec:
|
||||
fieldPath: {{ $value | quote}}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if eq .Values.reloader.watchGlobally false }}
|
||||
{{- if and (eq .Values.reloader.watchGlobally false) (not .Values.reloader.namespaces) }}
|
||||
- name: KUBERNETES_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
@@ -213,7 +213,7 @@ spec:
|
||||
{{- . | toYaml | nindent 10 }}
|
||||
{{- 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) (.Values.reloader.ignoreJobs) (.Values.reloader.ignoreCronJobs) (.Values.reloader.enableCSIIntegration)}}
|
||||
{{- if or (.Values.reloader.logFormat) (.Values.reloader.logLevel) (.Values.reloader.ignoreSecrets) (and .Values.reloader.ignoreNamespaces .Values.reloader.watchGlobally) (.Values.reloader.namespaces) (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) (.Values.reloader.enableCSIIntegration)}}
|
||||
args:
|
||||
{{- if .Values.reloader.logFormat }}
|
||||
- "--log-format={{ .Values.reloader.logFormat }}"
|
||||
@@ -234,7 +234,10 @@ spec:
|
||||
{{- else if .Values.reloader.ignoreCronJobs }}
|
||||
- "--ignored-workload-types=cronjobs"
|
||||
{{- end }}
|
||||
{{- if .Values.reloader.ignoreNamespaces }}
|
||||
{{- if .Values.reloader.namespaces }}
|
||||
- "--namespaces={{ include "reloader-watchNamespaces-csv" . }}"
|
||||
{{- end }}
|
||||
{{- if and .Values.reloader.ignoreNamespaces .Values.reloader.watchGlobally }}
|
||||
- "--namespaces-to-ignore={{ .Values.reloader.ignoreNamespaces }}"
|
||||
{{- end }}
|
||||
{{- if (include "reloader-namespaceSelector" .) }}
|
||||
|
||||
@@ -1,9 +1,34 @@
|
||||
{{- if and (not (.Values.reloader.watchGlobally)) (.Values.reloader.rbac.enabled) }}
|
||||
{{- if (.Capabilities.APIVersions.Has "rbac.authorization.k8s.io/v1") }}
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
{{ else }}
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
{{- if and .Values.reloader.watchGlobally .Values.reloader.namespaces }}
|
||||
{{- fail "reloader.namespaces is set but reloader.watchGlobally is true; set reloader.watchGlobally=false to use scoped namespace mode." }}
|
||||
{{- end }}
|
||||
{{- if and (not (.Values.reloader.watchGlobally)) (.Values.reloader.rbac.enabled) }}
|
||||
{{- $apiVersion := "rbac.authorization.k8s.io/v1" }}
|
||||
{{- if not (.Capabilities.APIVersions.Has "rbac.authorization.k8s.io/v1") }}
|
||||
{{- $apiVersion = "rbac.authorization.k8s.io/v1beta1" }}
|
||||
{{- end }}
|
||||
{{- if .Values.reloader.namespaces }}
|
||||
{{- range $ns := (include "reloader-watchNamespaces" . | mustFromJson) }}
|
||||
apiVersion: {{ $apiVersion }}
|
||||
kind: Role
|
||||
metadata:
|
||||
annotations:
|
||||
{{ include "reloader-helm3.annotations" $ | indent 4 }}
|
||||
labels:
|
||||
{{ include "reloader-labels.chart" $ | indent 4 }}
|
||||
{{- if $.Values.reloader.rbac.labels }}
|
||||
{{ tpl (toYaml $.Values.reloader.rbac.labels) $ | indent 4 }}
|
||||
{{- end }}
|
||||
{{- if $.Values.reloader.matchLabels }}
|
||||
{{ tpl (toYaml $.Values.reloader.matchLabels) $ | indent 4 }}
|
||||
{{- end }}
|
||||
name: {{ template "reloader-fullname" $ }}-role
|
||||
namespace: {{ $ns }}
|
||||
rules:
|
||||
{{- include "reloader-namespaced-rules" $ }}
|
||||
---
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
apiVersion: {{ $apiVersion }}
|
||||
kind: Role
|
||||
metadata:
|
||||
annotations:
|
||||
@@ -19,98 +44,8 @@ metadata:
|
||||
name: {{ template "reloader-fullname" . }}-role
|
||||
namespace: {{ .Values.namespace | default .Release.Namespace }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
{{- if .Values.reloader.ignoreSecrets }}{{- else }}
|
||||
- secrets
|
||||
{{- include "reloader-namespaced-rules" . }}
|
||||
{{- end }}
|
||||
{{- if .Values.reloader.ignoreConfigMaps }}{{- else }}
|
||||
- configmaps
|
||||
{{- end }}
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- watch
|
||||
{{- if and (.Capabilities.APIVersions.Has "apps.openshift.io/v1") (.Values.reloader.isOpenshift) }}
|
||||
- apiGroups:
|
||||
- "apps.openshift.io"
|
||||
- ""
|
||||
resources:
|
||||
- deploymentconfigs
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- update
|
||||
- patch
|
||||
{{- end }}
|
||||
{{- if and (.Capabilities.APIVersions.Has "argoproj.io/v1alpha1") (.Values.reloader.isArgoRollouts) }}
|
||||
- apiGroups:
|
||||
- "argoproj.io"
|
||||
- ""
|
||||
resources:
|
||||
- rollouts
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- update
|
||||
- patch
|
||||
{{- end }}
|
||||
- apiGroups:
|
||||
- "apps"
|
||||
resources:
|
||||
- deployments
|
||||
- daemonsets
|
||||
- statefulsets
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- update
|
||||
- patch
|
||||
- apiGroups:
|
||||
- "batch"
|
||||
resources:
|
||||
- cronjobs
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- apiGroups:
|
||||
- "batch"
|
||||
resources:
|
||||
- jobs
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- list
|
||||
- get
|
||||
{{- if .Values.reloader.enableHA }}
|
||||
- apiGroups:
|
||||
- "coordination.k8s.io"
|
||||
resources:
|
||||
- leases
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
- update
|
||||
{{- end}}
|
||||
{{- if .Values.reloader.enableCSIIntegration }}
|
||||
- apiGroups:
|
||||
- "secrets-store.csi.x-k8s.io"
|
||||
resources:
|
||||
- secretproviderclasspodstatuses
|
||||
- secretproviderclasses
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- watch
|
||||
{{- end}}
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
{{- end }}
|
||||
|
||||
---
|
||||
|
||||
@@ -1,9 +1,37 @@
|
||||
{{- if and (not (.Values.reloader.watchGlobally)) (.Values.reloader.rbac.enabled) }}
|
||||
{{- if (.Capabilities.APIVersions.Has "rbac.authorization.k8s.io/v1") }}
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
{{ else }}
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
{{- $apiVersion := "rbac.authorization.k8s.io/v1" }}
|
||||
{{- if not (.Capabilities.APIVersions.Has "rbac.authorization.k8s.io/v1") }}
|
||||
{{- $apiVersion = "rbac.authorization.k8s.io/v1beta1" }}
|
||||
{{- end }}
|
||||
{{- if .Values.reloader.namespaces }}
|
||||
{{- range $ns := (include "reloader-watchNamespaces" . | mustFromJson) }}
|
||||
apiVersion: {{ $apiVersion }}
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
{{ include "reloader-helm3.annotations" $ | indent 4 }}
|
||||
labels:
|
||||
{{ include "reloader-labels.chart" $ | indent 4 }}
|
||||
{{- if $.Values.reloader.rbac.labels }}
|
||||
{{ tpl (toYaml $.Values.reloader.rbac.labels) $ | indent 4 }}
|
||||
{{- end }}
|
||||
{{- if $.Values.reloader.matchLabels }}
|
||||
{{ tpl (toYaml $.Values.reloader.matchLabels) $ | indent 4 }}
|
||||
{{- end }}
|
||||
name: {{ template "reloader-fullname" $ }}-role-binding
|
||||
namespace: {{ $ns }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: {{ template "reloader-fullname" $ }}-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ template "reloader-serviceAccountName" $ }}
|
||||
namespace: {{ $.Values.namespace | default $.Release.Namespace }}
|
||||
---
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
apiVersion: {{ $apiVersion }}
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
@@ -27,6 +55,7 @@ subjects:
|
||||
name: {{ template "reloader-serviceAccountName" . }}
|
||||
namespace: {{ .Values.namespace | default .Release.Namespace }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
---
|
||||
{{- if .Values.reloader.rbac.enabled }}
|
||||
|
||||
@@ -45,6 +45,14 @@ reloader:
|
||||
logFormat: "" # json
|
||||
logLevel: info # Log level to use (trace, debug, info, warning, error, fatal and panic)
|
||||
watchGlobally: true
|
||||
# Scoped mode: explicit list of namespaces to watch. When non-empty (and watchGlobally
|
||||
# is false), Reloader watches exactly these namespaces and the chart creates a namespace
|
||||
# scoped Role + RoleBinding in each one — no ClusterRole is created. The release namespace
|
||||
# is always included automatically. Leave empty ([]) for the default single-namespace or
|
||||
# global behavior controlled by watchGlobally.
|
||||
# Accepts either a YAML list (e.g. ["team-a", "team-b"]) or a comma-separated string
|
||||
# (e.g. "team-a,team-b")
|
||||
namespaces: []
|
||||
# Set to true to enable leadership election allowing you to run multiple replicas
|
||||
enableHA: false
|
||||
# Set to true to enable pprof for profiling
|
||||
|
||||
@@ -18,6 +18,13 @@ reloader:
|
||||
ignoreNamespaces: "" # Comma separated list of namespaces to ignore
|
||||
logFormat: "" #json
|
||||
watchGlobally: true
|
||||
# Scoped mode: explicit list of namespaces to watch. When non-empty (and watchGlobally
|
||||
# is false), Reloader watches exactly these namespaces and the chart creates a namespace
|
||||
# scoped Role + RoleBinding in each one — no ClusterRole is created. The release namespace
|
||||
# is always included automatically.
|
||||
# Accepts either a YAML list (e.g. ["team-a", "team-b"]) or a comma-separated string
|
||||
# (e.g. "team-a,team-b")
|
||||
namespaces: []
|
||||
# Set to true if you have a pod security policy that enforces readOnlyRootFilesystem
|
||||
readOnlyRootFileSystem: false
|
||||
legacy:
|
||||
|
||||
@@ -102,6 +102,21 @@ func getHAEnvs() (string, string) {
|
||||
return podName, podNamespace
|
||||
}
|
||||
|
||||
// resolveWatchNamespaces determines the set of namespaces to watch and whether
|
||||
// Reloader runs in global (all-namespaces) mode. Precedence:
|
||||
// 1. an explicit --namespaces list (scoped mode) — watch exactly those namespaces;
|
||||
// 2. the KUBERNETES_NAMESPACE env var (single-namespace mode);
|
||||
// 3. otherwise watch all namespaces (global mode).
|
||||
func resolveWatchNamespaces(namespaces []string, kubernetesNamespace string) ([]string, bool) {
|
||||
if len(namespaces) > 0 {
|
||||
return namespaces, false
|
||||
}
|
||||
if len(kubernetesNamespace) > 0 {
|
||||
return []string{kubernetesNamespace}, false
|
||||
}
|
||||
return []string{v1.NamespaceAll}, true
|
||||
}
|
||||
|
||||
// namespaceWatchScopeMessage returns the startup log message describing the
|
||||
// namespace scope Reloader will watch when KUBERNETES_NAMESPACE is unset
|
||||
// (global mode). It reflects --namespaces-to-ignore so the log is not
|
||||
@@ -124,11 +139,9 @@ func startReloader(cmd *cobra.Command, args []string) {
|
||||
}
|
||||
|
||||
logrus.Info("Starting Reloader")
|
||||
isGlobal := false
|
||||
currentNamespace := os.Getenv("KUBERNETES_NAMESPACE")
|
||||
if len(currentNamespace) == 0 {
|
||||
currentNamespace = v1.NamespaceAll
|
||||
isGlobal = true
|
||||
watchNamespaces, isGlobal := resolveWatchNamespaces(options.Namespaces, os.Getenv("KUBERNETES_NAMESPACE"))
|
||||
if !isGlobal && len(options.Namespaces) > 0 {
|
||||
logrus.Infof("Watching scoped namespaces: %s", strings.Join(watchNamespaces, ", "))
|
||||
}
|
||||
|
||||
// create the clientset
|
||||
@@ -142,17 +155,21 @@ func startReloader(cmd *cobra.Command, args []string) {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
|
||||
ignoredNamespacesList := options.NamespacesToIgnore
|
||||
if isGlobal {
|
||||
logrus.Warn(namespaceWatchScopeMessage(ignoredNamespacesList))
|
||||
}
|
||||
// namespaces-to-ignore and namespace-selector only make sense when watching all
|
||||
// namespaces. In single-namespace and scoped modes the watched set is already
|
||||
// explicit, so both are intentionally left empty.
|
||||
ignoredNamespacesList := []string{}
|
||||
namespaceLabelSelector := ""
|
||||
|
||||
if isGlobal {
|
||||
ignoredNamespacesList = options.NamespacesToIgnore
|
||||
logrus.Warn(namespaceWatchScopeMessage(ignoredNamespacesList))
|
||||
namespaceLabelSelector, err = common.GetNamespaceLabelSelector(options.NamespaceSelectors)
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
} else if len(options.NamespacesToIgnore) > 0 {
|
||||
logrus.Warnf("namespaces-to-ignore is set but is only honored in global mode (watchGlobally=true); ignoring it.")
|
||||
}
|
||||
|
||||
resourceLabelSelector, err := common.GetResourceLabelSelector(options.ResourceSelectors)
|
||||
@@ -175,31 +192,33 @@ func startReloader(cmd *cobra.Command, args []string) {
|
||||
collectors := metrics.SetupPrometheusEndpoint()
|
||||
|
||||
var controllers []*controller.Controller
|
||||
for k := range kube.ResourceMap {
|
||||
if k == constants.SecretProviderClassController && !shouldRunCSIController() {
|
||||
continue
|
||||
}
|
||||
for _, currentNamespace := range watchNamespaces {
|
||||
for k := range kube.ResourceMap {
|
||||
if k == constants.SecretProviderClassController && !shouldRunCSIController() {
|
||||
continue
|
||||
}
|
||||
|
||||
if ignoredResourcesList.Contains(k) || (len(namespaceLabelSelector) == 0 && k == "namespaces") {
|
||||
continue
|
||||
}
|
||||
if ignoredResourcesList.Contains(k) || (len(namespaceLabelSelector) == 0 && k == "namespaces") {
|
||||
continue
|
||||
}
|
||||
|
||||
c, err := controller.NewController(clientset, k, currentNamespace, ignoredNamespacesList, namespaceLabelSelector, resourceLabelSelector, collectors)
|
||||
if err != nil {
|
||||
logrus.Fatalf("%s", err)
|
||||
}
|
||||
c, err := controller.NewController(clientset, k, currentNamespace, ignoredNamespacesList, namespaceLabelSelector, resourceLabelSelector, collectors)
|
||||
if err != nil {
|
||||
logrus.Fatalf("%s", err)
|
||||
}
|
||||
|
||||
controllers = append(controllers, c)
|
||||
controllers = append(controllers, c)
|
||||
|
||||
// If HA is enabled we only run the controller when
|
||||
if options.EnableHA {
|
||||
continue
|
||||
// If HA is enabled we only run the controller when
|
||||
if options.EnableHA {
|
||||
continue
|
||||
}
|
||||
// Now let's start the controller
|
||||
stop := make(chan struct{})
|
||||
defer close(stop)
|
||||
logrus.Infof("Starting Controller to watch resource type: %s in namespace: %s", k, currentNamespace)
|
||||
go c.Run(1, stop)
|
||||
}
|
||||
// Now let's start the controller
|
||||
stop := make(chan struct{})
|
||||
defer close(stop)
|
||||
logrus.Infof("Starting Controller to watch resource type: %s", k)
|
||||
go c.Run(1, stop)
|
||||
}
|
||||
|
||||
// Run leadership election
|
||||
|
||||
@@ -1,6 +1,65 @@
|
||||
package cmd
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestResolveWatchNamespaces(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
namespaces []string
|
||||
kubernetesNamespace string
|
||||
wantNamespaces []string
|
||||
wantGlobal bool
|
||||
}{
|
||||
{
|
||||
name: "scoped mode takes precedence over env",
|
||||
namespaces: []string{"team-a", "team-b"},
|
||||
kubernetesNamespace: "reloader-system",
|
||||
wantNamespaces: []string{"team-a", "team-b"},
|
||||
wantGlobal: false,
|
||||
},
|
||||
{
|
||||
name: "scoped mode with single namespace",
|
||||
namespaces: []string{"team-a"},
|
||||
kubernetesNamespace: "",
|
||||
wantNamespaces: []string{"team-a"},
|
||||
wantGlobal: false,
|
||||
},
|
||||
{
|
||||
name: "single namespace mode from env",
|
||||
namespaces: nil,
|
||||
kubernetesNamespace: "reloader-system",
|
||||
wantNamespaces: []string{"reloader-system"},
|
||||
wantGlobal: false,
|
||||
},
|
||||
{
|
||||
name: "global mode when nothing set",
|
||||
namespaces: nil,
|
||||
kubernetesNamespace: "",
|
||||
wantNamespaces: []string{v1.NamespaceAll},
|
||||
wantGlobal: true,
|
||||
},
|
||||
{
|
||||
name: "empty list falls back to env",
|
||||
namespaces: []string{},
|
||||
kubernetesNamespace: "reloader-system",
|
||||
wantNamespaces: []string{"reloader-system"},
|
||||
wantGlobal: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotNamespaces, gotGlobal := resolveWatchNamespaces(tt.namespaces, tt.kubernetesNamespace)
|
||||
assert.Equal(t, tt.wantNamespaces, gotNamespaces)
|
||||
assert.Equal(t, tt.wantGlobal, gotGlobal)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNamespaceWatchScopeMessage(t *testing.T) {
|
||||
tests := []struct {
|
||||
|
||||
@@ -76,6 +76,9 @@ var (
|
||||
ResourcesToIgnore = []string{}
|
||||
// WorkloadTypesToIgnore is a list of workload types to ignore when watching for changes
|
||||
WorkloadTypesToIgnore = []string{}
|
||||
// Namespaces is an explicit list of namespaces to watch (scoped mode). When non-empty,
|
||||
// Reloader watches exactly these namespaces and requires no ClusterRole.
|
||||
Namespaces = []string{}
|
||||
// NamespacesToIgnore is a list of namespace names to ignore when watching for changes
|
||||
NamespacesToIgnore = []string{}
|
||||
// NamespaceSelectors is a list of namespace selectors to watch for changes
|
||||
|
||||
@@ -97,6 +97,7 @@ func ConfigureReloaderFlags(cmd *cobra.Command) {
|
||||
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.WorkloadTypesToIgnore, "ignored-workload-types", options.WorkloadTypesToIgnore, "list of workload types to ignore (valid options: 'jobs', 'cronjobs', or both)")
|
||||
cmd.PersistentFlags().StringSliceVar(&options.Namespaces, "namespaces", options.Namespaces, "explicit list of namespaces to watch (scoped mode; creates no ClusterRole)")
|
||||
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.ResourceSelectors, "resource-label-selector", options.ResourceSelectors, "list of key:value labels to filter on for configmaps and secrets")
|
||||
|
||||
@@ -152,6 +152,11 @@ var _ = Describe("Multi-Container Tests", Serial, func() {
|
||||
initialVersion, err := utils.GetSPCPSVersion(ctx, csiClient, testNamespace, spcpsName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger: Reloader reacts to the
|
||||
// same SPCPS update the test waits on below, so it may reload before WaitReloaded runs.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Vault secret")
|
||||
err = utils.UpdateVaultSecret(ctx, kubeClient, restConfig, vaultSecretPath, map[string]string{
|
||||
"api_key": "updated-init-value",
|
||||
@@ -163,8 +168,8 @@ var _ = Describe("Multi-Container Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment with init container using CSI volume should be reloaded")
|
||||
})
|
||||
@@ -199,6 +204,10 @@ var _ = Describe("Multi-Container Tests", Serial, func() {
|
||||
initialVersion, err := utils.GetSPCPSVersion(ctx, csiClient, testNamespace, spcpsName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the baseline before the trigger — see the sibling test above.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Vault secret")
|
||||
err = utils.UpdateVaultSecret(ctx, kubeClient, restConfig, vaultSecretPath, map[string]string{
|
||||
"api_key": "updated-init-auto-value",
|
||||
@@ -210,8 +219,8 @@ var _ = Describe("Multi-Container Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment with init container CSI volume and auto=true should be reloaded")
|
||||
})
|
||||
|
||||
@@ -243,6 +243,11 @@ var _ = Describe("Auto Reload Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
GinkgoWriter.Printf("Initial SPCPS version: %s\n", initialVersion)
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger: Reloader reacts to the
|
||||
// same SPCPS update the test waits on below, so it may reload before WaitReloaded runs.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Vault secret")
|
||||
err = utils.UpdateVaultSecret(ctx, kubeClient, restConfig, vaultSecretPath, map[string]string{"api_key": "updated-value-v2"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
@@ -253,8 +258,8 @@ var _ = Describe("Auto Reload Annotation Tests", func() {
|
||||
GinkgoWriter.Println("CSI driver synced new secret version")
|
||||
|
||||
By("Waiting for Deployment to be reloaded")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment should have been reloaded for Vault secret change")
|
||||
})
|
||||
@@ -305,6 +310,11 @@ var _ = Describe("Auto Reload Annotation Tests", func() {
|
||||
initialVersion, err := utils.GetSPCPSVersion(ctx, csiClient, testNamespace, spcpsName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the baseline before the trigger to avoid racing Reloader's own reaction
|
||||
// to the SPCPS update below.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Vault secret (should trigger reload)")
|
||||
err = utils.UpdateVaultSecret(ctx, kubeClient, restConfig, vaultSecretPath, map[string]string{"api_key": "updated-value-v2"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
@@ -314,8 +324,8 @@ var _ = Describe("Auto Reload Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded for SPC change")
|
||||
reloaded, err = adapter.WaitReloaded(ctx, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err = adapter.WaitReloadedFrom(ctx, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment should have been reloaded for Vault secret change")
|
||||
})
|
||||
@@ -349,6 +359,11 @@ var _ = Describe("Auto Reload Annotation Tests", func() {
|
||||
initialVersion, err := utils.GetSPCPSVersion(ctx, csiClient, testNamespace, spcpsName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the baseline before the trigger to avoid racing Reloader's own reaction
|
||||
// to the SPCPS update below.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Vault secret")
|
||||
err = utils.UpdateVaultSecret(ctx, kubeClient, restConfig, vaultSecretPath, map[string]string{"api_key": "updated-value-v2"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
@@ -358,8 +373,8 @@ var _ = Describe("Auto Reload Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment with auto=true should have been reloaded for Vault secret change")
|
||||
})
|
||||
|
||||
@@ -304,6 +304,11 @@ var _ = Describe("Exclude Annotation Tests", func() {
|
||||
initialVersion, err := utils.GetSPCPSVersion(ctx, csiClient, testNamespace, spcpsName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the baseline before the trigger so an erroneous reload happening while we
|
||||
// wait for the CSI sync below is still detected.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Vault secret for excluded SPC")
|
||||
err = utils.UpdateVaultSecret(ctx, kubeClient, restConfig, vaultSecretPath, map[string]string{
|
||||
"api_key": "updated-excluded-value",
|
||||
@@ -316,8 +321,8 @@ var _ = Describe("Exclude Annotation Tests", func() {
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (excluded SPC)")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := adapter.WaitReloaded(ctx, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ShortTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "Deployment should NOT reload when excluded SecretProviderClassPodStatus changes")
|
||||
})
|
||||
@@ -365,6 +370,11 @@ var _ = Describe("Exclude Annotation Tests", func() {
|
||||
initialVersion, err := utils.GetSPCPSVersion(ctx, csiClient, testNamespace, spcpsName2)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the baseline before the trigger to avoid racing Reloader's own reaction
|
||||
// to the SPCPS update below.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Vault secret for non-excluded SPC")
|
||||
err = utils.UpdateVaultSecret(ctx, kubeClient, restConfig, vaultSecretPath2, map[string]string{
|
||||
"api_key": "updated-nonexcluded-value",
|
||||
@@ -376,8 +386,8 @@ var _ = Describe("Exclude Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment should reload when non-excluded SecretProviderClassPodStatus changes")
|
||||
})
|
||||
|
||||
@@ -178,6 +178,11 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
GinkgoWriter.Printf("Initial SPCPS version: %s\n", initialVersion)
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger: Reloader reacts to the
|
||||
// same SPCPS update the test waits on below, so it may reload before WaitReloaded runs.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Vault secret")
|
||||
err = utils.UpdateVaultSecret(ctx, kubeClient, restConfig, vaultSecretPath, map[string]string{"api_key": "updated-value-v2"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
@@ -189,8 +194,8 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
GinkgoWriter.Println("CSI driver synced new secret version")
|
||||
|
||||
By("Waiting for workload to be reloaded")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom,
|
||||
utils.ReloadTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom,
|
||||
priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "%s should have been reloaded when Vault secret changed", workloadType)
|
||||
}, Entry("Deployment", Label("csi"), utils.WorkloadDeployment),
|
||||
@@ -1041,6 +1046,11 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
initialVersion, err := utils.GetSPCPSVersion(ctx, csiClient, testNamespace, spcpsName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the baseline before the trigger to avoid racing Reloader's own
|
||||
// reaction to the SPCPS update below.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Vault secret")
|
||||
err = utils.UpdateVaultSecret(ctx, kubeClient, restConfig, vaultSecretPath, map[string]string{"api_key": "updated-value-v2"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
@@ -1051,8 +1061,8 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for workload to be reloaded")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, testNamespace, workloadName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, testNamespace, workloadName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "%s should reload with SPC annotation on pod template", workloadType)
|
||||
},
|
||||
@@ -1108,6 +1118,11 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
initialVersion, err := utils.GetSPCPSVersion(ctx, csiClient, testNamespace, spcpsName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the baseline before the trigger to avoid racing Reloader's own
|
||||
// reaction to the SPCPS update below.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Vault secret")
|
||||
err = utils.UpdateVaultSecret(ctx, kubeClient, restConfig, vaultSecretPath, map[string]string{"api_key": "updated-value-v2"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
@@ -1118,8 +1133,8 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for workload to be reloaded")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, testNamespace, workloadName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, testNamespace, workloadName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "%s should reload with SPC auto on pod template", workloadType)
|
||||
},
|
||||
@@ -1411,8 +1426,11 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for workload to have STAKATER_ env var")
|
||||
found, err := adapter.WaitEnvVar(ctx, testNamespace, workloadName, utils.StakaterEnvVarPrefix,
|
||||
utils.ReloadTimeout)
|
||||
// Baseline "" (workload is fresh, no STAKATER_ env var yet): Reloader may have
|
||||
// reloaded already while we waited for the CSI sync above, so the wait must not
|
||||
// capture its own baseline now.
|
||||
found, err := adapter.WaitEnvVarFrom(ctx, testNamespace, workloadName, utils.StakaterEnvVarPrefix,
|
||||
"", utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(found).To(BeTrue(), "%s should have STAKATER_ env var after Vault secret change", workloadType)
|
||||
}, Entry("Deployment", Label("csi"), utils.WorkloadDeployment),
|
||||
@@ -1630,8 +1648,9 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to have STAKATER_ env var")
|
||||
found, err := adapter.WaitEnvVar(ctx, testNamespace, workloadName, utils.StakaterEnvVarPrefix,
|
||||
utils.ReloadTimeout)
|
||||
// Baseline "" (fresh Deployment): avoids racing Reloader's reaction to the SPCPS update.
|
||||
found, err := adapter.WaitEnvVarFrom(ctx, testNamespace, workloadName, utils.StakaterEnvVarPrefix,
|
||||
"", utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(found).To(BeTrue(), "Deployment with SPC auto annotation should have STAKATER_ env var")
|
||||
})
|
||||
@@ -1691,8 +1710,10 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
|
||||
By("Verifying Deployment does NOT have STAKATER_ env var")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
found, err := adapter.WaitEnvVar(ctx, testNamespace, workloadName, utils.StakaterEnvVarPrefix,
|
||||
utils.ShortTimeout)
|
||||
// Baseline "" (fresh Deployment): an erroneous reload that already happened during the
|
||||
// CSI sync wait above must still be detected as a failure.
|
||||
found, err := adapter.WaitEnvVarFrom(ctx, testNamespace, workloadName, utils.StakaterEnvVarPrefix,
|
||||
"", utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(found).To(BeFalse(), "Deployment should NOT have STAKATER_ env var for excluded SPCPS change")
|
||||
})
|
||||
@@ -1747,8 +1768,9 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to have STAKATER_ env var")
|
||||
found, err := adapter.WaitEnvVar(ctx, testNamespace, workloadName,
|
||||
utils.StakaterEnvVarPrefix, utils.ReloadTimeout)
|
||||
// Baseline "" (fresh Deployment): avoids racing Reloader's reaction to the SPCPS update.
|
||||
found, err := adapter.WaitEnvVarFrom(ctx, testNamespace, workloadName,
|
||||
utils.StakaterEnvVarPrefix, "", utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(found).To(BeTrue(), "Deployment with init container CSI should have STAKATER_ env var")
|
||||
})
|
||||
|
||||
@@ -72,6 +72,11 @@ var _ = Describe("CSI SecretProviderClass Tests", Label("csi"), Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
GinkgoWriter.Printf("Initial SPCPS version: %s\n", initialVersion)
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger: Reloader reacts to the
|
||||
// same SPCPS update the test waits on below, so it may reload before WaitReloaded runs.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Vault secret")
|
||||
err = utils.UpdateVaultSecret(
|
||||
ctx, kubeClient, restConfig, vaultSecretPath, map[string]string{"api_key": "updated-value-v2"})
|
||||
@@ -83,9 +88,9 @@ var _ = Describe("CSI SecretProviderClass Tests", Label("csi"), Serial, func() {
|
||||
GinkgoWriter.Println("CSI driver synced new secret version")
|
||||
|
||||
By("Waiting for Deployment to be reloaded by Reloader")
|
||||
reloaded, err := adapter.WaitReloaded(
|
||||
reloaded, err := adapter.WaitReloadedFrom(
|
||||
ctx, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout,
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment should have been reloaded after Vault secret change")
|
||||
@@ -124,6 +129,10 @@ var _ = Describe("CSI SecretProviderClass Tests", Label("csi"), Serial, func() {
|
||||
|
||||
By("First update to Vault secret")
|
||||
initialVersion, _ := utils.GetSPCPSVersion(ctx, csiClient, testNamespace, spcpsName)
|
||||
// Capture the baseline before the trigger to avoid racing Reloader's own reaction
|
||||
// to the SPCPS update below.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateVaultSecret(
|
||||
ctx, kubeClient, restConfig, vaultSecretPath, map[string]string{"password": "pass-v2"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
@@ -133,9 +142,9 @@ var _ = Describe("CSI SecretProviderClass Tests", Label("csi"), Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for first reload")
|
||||
reloaded, err := adapter.WaitReloaded(
|
||||
reloaded, err := adapter.WaitReloadedFrom(
|
||||
ctx, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout,
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue())
|
||||
@@ -230,6 +239,11 @@ var _ = Describe("CSI SecretProviderClass Tests", Label("csi"), Serial, func() {
|
||||
By("Getting SPCPS version before Vault update")
|
||||
initialVersion, _ := utils.GetSPCPSVersion(ctx, csiClient, testNamespace, spcpsName)
|
||||
|
||||
// Capture the baseline before the trigger to avoid racing Reloader's own reaction
|
||||
// to the SPCPS update below.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Vault secret (should trigger reload)")
|
||||
err = utils.UpdateVaultSecret(
|
||||
ctx, kubeClient, restConfig, vaultSecretPath, map[string]string{"token": "token-v2"})
|
||||
@@ -240,9 +254,9 @@ var _ = Describe("CSI SecretProviderClass Tests", Label("csi"), Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment WAS reloaded for Vault secret change")
|
||||
reloaded, err = adapter.WaitReloaded(
|
||||
reloaded, err = adapter.WaitReloadedFrom(
|
||||
ctx, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout,
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "SPC auto annotation should trigger reload for Vault secret changes")
|
||||
|
||||
@@ -108,6 +108,119 @@ var _ = Describe("Watch Globally Flag Tests", Serial, func() {
|
||||
})
|
||||
})
|
||||
|
||||
Context("with scoped namespaces list (watchGlobally=false + reloader.namespaces)", func() {
|
||||
var scopedNS string
|
||||
|
||||
BeforeEach(func() {
|
||||
scopedNS = "scoped-" + utils.RandName("ns")
|
||||
Expect(utils.CreateNamespace(ctx, kubeClient, scopedNS)).To(Succeed())
|
||||
Expect(utils.CreateNamespace(ctx, kubeClient, otherNS)).To(Succeed())
|
||||
|
||||
// Watch only scopedNS explicitly; the release namespace (testNamespace)
|
||||
// is auto-included by the chart. otherNS is intentionally left out.
|
||||
err := deployReloaderWithFlags(map[string]string{
|
||||
"reloader.watchGlobally": "false",
|
||||
"reloader.namespaces": "{" + scopedNS + "}",
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(waitForReloaderReady()).To(Succeed())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
_ = utils.DeleteDeployment(ctx, kubeClient, scopedNS, deploymentName)
|
||||
_ = utils.DeleteConfigMap(ctx, kubeClient, scopedNS, configMapName)
|
||||
_ = undeployReloader()
|
||||
_ = utils.DeleteNamespace(ctx, kubeClient, scopedNS)
|
||||
_ = utils.DeleteNamespace(ctx, kubeClient, otherNS)
|
||||
})
|
||||
|
||||
It("should reload workloads in a listed namespace", func() {
|
||||
By("Creating a ConfigMap in the listed namespace")
|
||||
_, err := utils.CreateConfigMap(ctx, kubeClient, scopedNS, configMapName,
|
||||
map[string]string{"key": "initial"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating a Deployment in the listed namespace with auto annotation")
|
||||
_, err = utils.CreateDeployment(ctx, kubeClient, scopedNS, deploymentName,
|
||||
utils.WithConfigMapEnvFrom(configMapName),
|
||||
utils.WithAnnotations(utils.BuildAutoTrueAnnotation()),
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be ready")
|
||||
err = adapter.WaitReady(ctx, scopedNS, deploymentName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, scopedNS, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, scopedNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment in a listed namespace should reload")
|
||||
})
|
||||
|
||||
It("should reload workloads in Reloader's auto-included release namespace", func() {
|
||||
By("Creating a ConfigMap in Reloader's namespace")
|
||||
_, err := utils.CreateConfigMap(ctx, kubeClient, testNamespace, configMapName,
|
||||
map[string]string{"key": "initial"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating a Deployment in Reloader's namespace with auto annotation")
|
||||
_, err = utils.CreateDeployment(ctx, kubeClient, testNamespace, deploymentName,
|
||||
utils.WithConfigMapEnvFrom(configMapName),
|
||||
utils.WithAnnotations(utils.BuildAutoTrueAnnotation()),
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be ready")
|
||||
err = adapter.WaitReady(ctx, testNamespace, deploymentName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded (release namespace is auto-included)")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment in Reloader's auto-included namespace should reload")
|
||||
})
|
||||
|
||||
It("should NOT reload workloads in an unlisted namespace", func() {
|
||||
By("Creating a ConfigMap in an unlisted namespace")
|
||||
_, err := utils.CreateConfigMap(ctx, kubeClient, otherNS, configMapName,
|
||||
map[string]string{"key": "initial"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating a Deployment in an unlisted namespace with auto annotation")
|
||||
_, err = utils.CreateDeployment(ctx, kubeClient, otherNS, deploymentName,
|
||||
utils.WithConfigMapEnvFrom(configMapName),
|
||||
utils.WithAnnotations(utils.BuildAutoTrueAnnotation()),
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be ready")
|
||||
err = adapter.WaitReady(ctx, otherNS, deploymentName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap in the unlisted namespace")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, otherNS, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (namespace not in the list)")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := adapter.WaitReloaded(ctx, otherNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "Deployment in an unlisted namespace should NOT reload")
|
||||
})
|
||||
})
|
||||
|
||||
Context("with watchGlobally=true flag (default)", func() {
|
||||
var globalNS string
|
||||
|
||||
|
||||
@@ -69,12 +69,27 @@ type WorkloadAdapter interface {
|
||||
|
||||
// WaitReloaded waits for the workload to have the reload annotation.
|
||||
// Returns true if the annotation was found, false if timeout occurred.
|
||||
// It captures the baseline annotation value at call time, which races with Reloader
|
||||
// if the reload trigger happened earlier — prefer WaitReloadedFrom in that case.
|
||||
WaitReloaded(ctx context.Context, namespace, name, annotationKey string, timeout time.Duration) (bool, error)
|
||||
|
||||
// WaitReloadedFrom waits for the reload annotation to be present with a value different
|
||||
// from priorValue. Capture priorValue (via GetPodTemplateAnnotation) BEFORE performing
|
||||
// the change that triggers the reload; capturing it afterwards can observe the already
|
||||
// reloaded value and then wait for a further change that never comes.
|
||||
WaitReloadedFrom(ctx context.Context, namespace, name, annotationKey, priorValue string, timeout time.Duration) (bool, error)
|
||||
|
||||
// WaitEnvVar waits for the workload to have a STAKATER_ env var (for envvars strategy).
|
||||
// Returns true if the env var was found, false if timeout occurred.
|
||||
// It captures the baseline env var value at call time, which races with Reloader
|
||||
// if the reload trigger happened earlier — prefer WaitEnvVarFrom in that case.
|
||||
WaitEnvVar(ctx context.Context, namespace, name, prefix string, timeout time.Duration) (bool, error)
|
||||
|
||||
// WaitEnvVarFrom waits for a STAKATER_ env var whose value differs from priorValue.
|
||||
// Capture priorValue BEFORE performing the change that triggers the reload
|
||||
// (an empty priorValue means the env var is expected to appear).
|
||||
WaitEnvVarFrom(ctx context.Context, namespace, name, prefix, priorValue string, timeout time.Duration) (bool, error)
|
||||
|
||||
// SupportsEnvVarStrategy returns true if the workload supports env var reload strategy.
|
||||
// CronJob does not support this as it uses job creation instead.
|
||||
SupportsEnvVarStrategy() bool
|
||||
|
||||
@@ -58,6 +58,12 @@ func (a *ArgoRolloutAdapter) WaitReady(ctx context.Context, namespace, name stri
|
||||
// Captures the current annotation value first to avoid false positives from prior reloads.
|
||||
func (a *ArgoRolloutAdapter) WaitReloaded(ctx context.Context, namespace, name, annotationKey string, timeout time.Duration) (bool, error) {
|
||||
priorValue, _ := a.GetPodTemplateAnnotation(ctx, namespace, name, annotationKey)
|
||||
return a.WaitReloadedFrom(ctx, namespace, name, annotationKey, priorValue, timeout)
|
||||
}
|
||||
|
||||
// WaitReloadedFrom waits for the reload annotation to be present with a value different from
|
||||
// priorValue, which the caller captured before triggering the reload.
|
||||
func (a *ArgoRolloutAdapter) WaitReloadedFrom(ctx context.Context, namespace, name, annotationKey, priorValue string, timeout time.Duration) (bool, error) {
|
||||
watchFunc := func(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
|
||||
return a.rolloutsClient.ArgoprojV1alpha1().Rollouts(namespace).Watch(ctx, opts)
|
||||
}
|
||||
@@ -72,6 +78,12 @@ func (a *ArgoRolloutAdapter) WaitEnvVar(ctx context.Context, namespace, name, pr
|
||||
if r, err := a.rolloutsClient.ArgoprojV1alpha1().Rollouts(namespace).Get(ctx, name, metav1.GetOptions{}); err == nil {
|
||||
priorValue = GetEnvVarValueByPrefix(r.Spec.Template.Spec.Containers, prefix)
|
||||
}
|
||||
return a.WaitEnvVarFrom(ctx, namespace, name, prefix, priorValue, timeout)
|
||||
}
|
||||
|
||||
// WaitEnvVarFrom waits for a STAKATER_ env var whose value differs from priorValue, which the
|
||||
// caller captured before triggering the reload.
|
||||
func (a *ArgoRolloutAdapter) WaitEnvVarFrom(ctx context.Context, namespace, name, prefix, priorValue string, timeout time.Duration) (bool, error) {
|
||||
watchFunc := func(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
|
||||
return a.rolloutsClient.ArgoprojV1alpha1().Rollouts(namespace).Watch(ctx, opts)
|
||||
}
|
||||
|
||||
@@ -50,6 +50,12 @@ func (a *CronJobAdapter) WaitReady(ctx context.Context, namespace, name string,
|
||||
// Captures the current annotation value first to avoid false positives from prior reloads.
|
||||
func (a *CronJobAdapter) WaitReloaded(ctx context.Context, namespace, name, annotationKey string, timeout time.Duration) (bool, error) {
|
||||
priorValue, _ := a.GetPodTemplateAnnotation(ctx, namespace, name, annotationKey)
|
||||
return a.WaitReloadedFrom(ctx, namespace, name, annotationKey, priorValue, timeout)
|
||||
}
|
||||
|
||||
// WaitReloadedFrom waits for the reload annotation to be present with a value different from
|
||||
// priorValue, which the caller captured before triggering the reload.
|
||||
func (a *CronJobAdapter) WaitReloadedFrom(ctx context.Context, namespace, name, annotationKey, priorValue string, timeout time.Duration) (bool, error) {
|
||||
watchFunc := func(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
|
||||
return a.client.BatchV1().CronJobs(namespace).Watch(ctx, opts)
|
||||
}
|
||||
@@ -62,6 +68,11 @@ func (a *CronJobAdapter) WaitEnvVar(ctx context.Context, namespace, name, prefix
|
||||
return false, ErrUnsupportedOperation
|
||||
}
|
||||
|
||||
// WaitEnvVarFrom returns an error because CronJobs don't support env var reload strategy.
|
||||
func (a *CronJobAdapter) WaitEnvVarFrom(ctx context.Context, namespace, name, prefix, priorValue string, timeout time.Duration) (bool, error) {
|
||||
return false, ErrUnsupportedOperation
|
||||
}
|
||||
|
||||
// SupportsEnvVarStrategy returns false as CronJobs don't support env var reload strategy.
|
||||
func (a *CronJobAdapter) SupportsEnvVarStrategy() bool {
|
||||
return false
|
||||
|
||||
@@ -50,6 +50,12 @@ func (a *DaemonSetAdapter) WaitReady(ctx context.Context, namespace, name string
|
||||
// Captures the current annotation value first to avoid false positives from prior reloads.
|
||||
func (a *DaemonSetAdapter) WaitReloaded(ctx context.Context, namespace, name, annotationKey string, timeout time.Duration) (bool, error) {
|
||||
priorValue, _ := a.GetPodTemplateAnnotation(ctx, namespace, name, annotationKey)
|
||||
return a.WaitReloadedFrom(ctx, namespace, name, annotationKey, priorValue, timeout)
|
||||
}
|
||||
|
||||
// WaitReloadedFrom waits for the reload annotation to be present with a value different from
|
||||
// priorValue, which the caller captured before triggering the reload.
|
||||
func (a *DaemonSetAdapter) WaitReloadedFrom(ctx context.Context, namespace, name, annotationKey, priorValue string, timeout time.Duration) (bool, error) {
|
||||
watchFunc := func(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
|
||||
return a.client.AppsV1().DaemonSets(namespace).Watch(ctx, opts)
|
||||
}
|
||||
@@ -64,6 +70,12 @@ func (a *DaemonSetAdapter) WaitEnvVar(ctx context.Context, namespace, name, pref
|
||||
if ds, err := a.client.AppsV1().DaemonSets(namespace).Get(ctx, name, metav1.GetOptions{}); err == nil {
|
||||
priorValue = GetEnvVarValueByPrefix(ds.Spec.Template.Spec.Containers, prefix)
|
||||
}
|
||||
return a.WaitEnvVarFrom(ctx, namespace, name, prefix, priorValue, timeout)
|
||||
}
|
||||
|
||||
// WaitEnvVarFrom waits for a STAKATER_ env var whose value differs from priorValue, which the
|
||||
// caller captured before triggering the reload.
|
||||
func (a *DaemonSetAdapter) WaitEnvVarFrom(ctx context.Context, namespace, name, prefix, priorValue string, timeout time.Duration) (bool, error) {
|
||||
watchFunc := func(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
|
||||
return a.client.AppsV1().DaemonSets(namespace).Watch(ctx, opts)
|
||||
}
|
||||
|
||||
@@ -51,6 +51,12 @@ func (a *DeploymentAdapter) WaitReady(ctx context.Context, namespace, name strin
|
||||
// does not cause a false positive — the condition triggers only when the value changes.
|
||||
func (a *DeploymentAdapter) WaitReloaded(ctx context.Context, namespace, name, annotationKey string, timeout time.Duration) (bool, error) {
|
||||
priorValue, _ := a.GetPodTemplateAnnotation(ctx, namespace, name, annotationKey)
|
||||
return a.WaitReloadedFrom(ctx, namespace, name, annotationKey, priorValue, timeout)
|
||||
}
|
||||
|
||||
// WaitReloadedFrom waits for the reload annotation to be present with a value different from
|
||||
// priorValue, which the caller captured before triggering the reload.
|
||||
func (a *DeploymentAdapter) WaitReloadedFrom(ctx context.Context, namespace, name, annotationKey, priorValue string, timeout time.Duration) (bool, error) {
|
||||
watchFunc := func(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
|
||||
return a.client.AppsV1().Deployments(namespace).Watch(ctx, opts)
|
||||
}
|
||||
@@ -66,6 +72,12 @@ func (a *DeploymentAdapter) WaitEnvVar(ctx context.Context, namespace, name, pre
|
||||
if d, err := a.client.AppsV1().Deployments(namespace).Get(ctx, name, metav1.GetOptions{}); err == nil {
|
||||
priorValue = GetEnvVarValueByPrefix(d.Spec.Template.Spec.Containers, prefix)
|
||||
}
|
||||
return a.WaitEnvVarFrom(ctx, namespace, name, prefix, priorValue, timeout)
|
||||
}
|
||||
|
||||
// WaitEnvVarFrom waits for a STAKATER_ env var whose value differs from priorValue, which the
|
||||
// caller captured before triggering the reload.
|
||||
func (a *DeploymentAdapter) WaitEnvVarFrom(ctx context.Context, namespace, name, prefix, priorValue string, timeout time.Duration) (bool, error) {
|
||||
watchFunc := func(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
|
||||
return a.client.AppsV1().Deployments(namespace).Watch(ctx, opts)
|
||||
}
|
||||
|
||||
@@ -54,11 +54,22 @@ func (a *JobAdapter) WaitReloaded(ctx context.Context, namespace, name, annotati
|
||||
return false, ErrUnsupportedOperation
|
||||
}
|
||||
|
||||
// WaitReloadedFrom returns an error because Jobs are recreated, not updated.
|
||||
// Use the Recreatable interface (GetOriginalUID + WaitRecreated) instead.
|
||||
func (a *JobAdapter) WaitReloadedFrom(ctx context.Context, namespace, name, annotationKey, priorValue string, timeout time.Duration) (bool, error) {
|
||||
return false, ErrUnsupportedOperation
|
||||
}
|
||||
|
||||
// WaitEnvVar returns an error because Jobs don't support env var reload strategy.
|
||||
func (a *JobAdapter) WaitEnvVar(ctx context.Context, namespace, name, prefix string, timeout time.Duration) (bool, error) {
|
||||
return false, ErrUnsupportedOperation
|
||||
}
|
||||
|
||||
// WaitEnvVarFrom returns an error because Jobs don't support env var reload strategy.
|
||||
func (a *JobAdapter) WaitEnvVarFrom(ctx context.Context, namespace, name, prefix, priorValue string, timeout time.Duration) (bool, error) {
|
||||
return false, ErrUnsupportedOperation
|
||||
}
|
||||
|
||||
// WaitRecreated waits for the Job to be recreated with a different UID using watches.
|
||||
func (a *JobAdapter) WaitRecreated(ctx context.Context, namespace, name, originalUID string, timeout time.Duration) (string, bool, error) {
|
||||
watchFunc := func(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
|
||||
|
||||
@@ -60,6 +60,12 @@ func (a *DeploymentConfigAdapter) WaitReady(ctx context.Context, namespace, name
|
||||
// Captures the current annotation value first to avoid false positives from prior reloads.
|
||||
func (a *DeploymentConfigAdapter) WaitReloaded(ctx context.Context, namespace, name, annotationKey string, timeout time.Duration) (bool, error) {
|
||||
priorValue, _ := a.GetPodTemplateAnnotation(ctx, namespace, name, annotationKey)
|
||||
return a.WaitReloadedFrom(ctx, namespace, name, annotationKey, priorValue, timeout)
|
||||
}
|
||||
|
||||
// WaitReloadedFrom waits for the reload annotation to be present with a value different from
|
||||
// priorValue, which the caller captured before triggering the reload.
|
||||
func (a *DeploymentConfigAdapter) WaitReloadedFrom(ctx context.Context, namespace, name, annotationKey, priorValue string, timeout time.Duration) (bool, error) {
|
||||
watchFunc := func(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
|
||||
return a.openshiftClient.AppsV1().DeploymentConfigs(namespace).Watch(ctx, opts)
|
||||
}
|
||||
@@ -74,6 +80,12 @@ func (a *DeploymentConfigAdapter) WaitEnvVar(ctx context.Context, namespace, nam
|
||||
if dc, err := a.openshiftClient.AppsV1().DeploymentConfigs(namespace).Get(ctx, name, metav1.GetOptions{}); err == nil && dc.Spec.Template != nil {
|
||||
priorValue = GetEnvVarValueByPrefix(dc.Spec.Template.Spec.Containers, prefix)
|
||||
}
|
||||
return a.WaitEnvVarFrom(ctx, namespace, name, prefix, priorValue, timeout)
|
||||
}
|
||||
|
||||
// WaitEnvVarFrom waits for a STAKATER_ env var whose value differs from priorValue, which the
|
||||
// caller captured before triggering the reload.
|
||||
func (a *DeploymentConfigAdapter) WaitEnvVarFrom(ctx context.Context, namespace, name, prefix, priorValue string, timeout time.Duration) (bool, error) {
|
||||
watchFunc := func(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
|
||||
return a.openshiftClient.AppsV1().DeploymentConfigs(namespace).Watch(ctx, opts)
|
||||
}
|
||||
|
||||
@@ -50,6 +50,12 @@ func (a *StatefulSetAdapter) WaitReady(ctx context.Context, namespace, name stri
|
||||
// Captures the current annotation value first to avoid false positives from prior reloads.
|
||||
func (a *StatefulSetAdapter) WaitReloaded(ctx context.Context, namespace, name, annotationKey string, timeout time.Duration) (bool, error) {
|
||||
priorValue, _ := a.GetPodTemplateAnnotation(ctx, namespace, name, annotationKey)
|
||||
return a.WaitReloadedFrom(ctx, namespace, name, annotationKey, priorValue, timeout)
|
||||
}
|
||||
|
||||
// WaitReloadedFrom waits for the reload annotation to be present with a value different from
|
||||
// priorValue, which the caller captured before triggering the reload.
|
||||
func (a *StatefulSetAdapter) WaitReloadedFrom(ctx context.Context, namespace, name, annotationKey, priorValue string, timeout time.Duration) (bool, error) {
|
||||
watchFunc := func(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
|
||||
return a.client.AppsV1().StatefulSets(namespace).Watch(ctx, opts)
|
||||
}
|
||||
@@ -64,6 +70,12 @@ func (a *StatefulSetAdapter) WaitEnvVar(ctx context.Context, namespace, name, pr
|
||||
if sts, err := a.client.AppsV1().StatefulSets(namespace).Get(ctx, name, metav1.GetOptions{}); err == nil {
|
||||
priorValue = GetEnvVarValueByPrefix(sts.Spec.Template.Spec.Containers, prefix)
|
||||
}
|
||||
return a.WaitEnvVarFrom(ctx, namespace, name, prefix, priorValue, timeout)
|
||||
}
|
||||
|
||||
// WaitEnvVarFrom waits for a STAKATER_ env var whose value differs from priorValue, which the
|
||||
// caller captured before triggering the reload.
|
||||
func (a *StatefulSetAdapter) WaitEnvVarFrom(ctx context.Context, namespace, name, prefix, priorValue string, timeout time.Duration) (bool, error) {
|
||||
watchFunc := func(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
|
||||
return a.client.AppsV1().StatefulSets(namespace).Watch(ctx, opts)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user