mirror of
https://github.com/stakater/Reloader.git
synced 2026-08-19 20:16:28 +00:00
Merge branch 'v2' into refactor/public-config-reload-metadata
This commit is contained in:
@@ -73,7 +73,7 @@ jobs:
|
||||
- name: Get version for chart from helm repo
|
||||
id: chart_eval
|
||||
run: |
|
||||
current_chart_version=$(helm search repo stakater/reloader | tail -n 1 | awk '{print $2}')
|
||||
current_chart_version=$(helm search repo stakater/reloader-v2 | tail -n 1 | awk '{print $2}')
|
||||
echo "CURRENT_CHART_VERSION=$(echo ${current_chart_version})" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get Updated Chart version from Chart.yaml
|
||||
@@ -82,7 +82,10 @@ jobs:
|
||||
with:
|
||||
cmd: yq e '.version' deployments/kubernetes/chart/reloader/Chart.yaml
|
||||
|
||||
# Skip the version-increase gate on first publish, when no reloader-v2
|
||||
# chart exists yet in the repo (CURRENT_CHART_VERSION is empty).
|
||||
- name: Check Version
|
||||
if: steps.chart_eval.outputs.CURRENT_CHART_VERSION != ''
|
||||
uses: aleoyakas/check-semver-increased-action@415c9c60054c2442c03478b6dd96a195deac6695 # v1
|
||||
id: check-version
|
||||
with:
|
||||
@@ -90,7 +93,7 @@ jobs:
|
||||
previous-version: ${{ steps.chart_eval.outputs.CURRENT_CHART_VERSION }}
|
||||
|
||||
- name: Fail if Helm Chart version isnt updated
|
||||
if: steps.check-version.outputs.is-version-increased != 'true'
|
||||
if: steps.chart_eval.outputs.CURRENT_CHART_VERSION != '' && steps.check-version.outputs.is-version-increased != 'true'
|
||||
run: |
|
||||
echo "Helm Chart Version wasnt updated"
|
||||
exit 1
|
||||
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
- name: Get version for chart from helm repo
|
||||
id: chart_eval
|
||||
run: |
|
||||
current_chart_version=$(helm search repo stakater/reloader | tail -n 1 | awk '{print $2}')
|
||||
current_chart_version=$(helm search repo stakater/reloader-v2 | tail -n 1 | awk '{print $2}')
|
||||
echo "CURRENT_CHART_VERSION=$(echo ${current_chart_version})" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get Updated Chart version from Chart.yaml
|
||||
@@ -63,7 +63,10 @@ jobs:
|
||||
with:
|
||||
cmd: yq e '.version' deployments/kubernetes/chart/reloader/Chart.yaml
|
||||
|
||||
# Skip the version-increase gate on first publish, when no reloader-v2
|
||||
# chart exists yet in the repo (CURRENT_CHART_VERSION is empty).
|
||||
- name: Check Version
|
||||
if: steps.chart_eval.outputs.CURRENT_CHART_VERSION != ''
|
||||
uses: aleoyakas/check-semver-increased-action@415c9c60054c2442c03478b6dd96a195deac6695 # v1
|
||||
id: check-version
|
||||
with:
|
||||
@@ -71,7 +74,7 @@ jobs:
|
||||
previous-version: ${{ steps.chart_eval.outputs.CURRENT_CHART_VERSION }}
|
||||
|
||||
- name: Fail if Helm Chart version isnt updated
|
||||
if: steps.check-version.outputs.is-version-increased != 'true'
|
||||
if: steps.chart_eval.outputs.CURRENT_CHART_VERSION != '' && steps.check-version.outputs.is-version-increased != 'true'
|
||||
run: |
|
||||
echo "Helm Chart Version wasnt updated"
|
||||
exit 1
|
||||
@@ -88,12 +91,15 @@ jobs:
|
||||
|
||||
- name: Publish Helm chart to ghcr.io
|
||||
run: |
|
||||
# Pull the enterprise subchart from private GHCR (uses the login above);
|
||||
# the vendored charts/*.tgz is gitignored, so it isn't present on the runner.
|
||||
helm dependency build ./deployments/kubernetes/chart/reloader
|
||||
helm package ./deployments/kubernetes/chart/reloader --destination ./packaged-chart
|
||||
helm push ./packaged-chart/*.tgz oci://ghcr.io/stakater/charts
|
||||
rm -rf ./packaged-chart
|
||||
|
||||
- name: Sign artifacts with Cosign
|
||||
run: cosign sign --yes ghcr.io/stakater/charts/reloader:${{ steps.new_chart_version.outputs.result }}
|
||||
run: cosign sign --yes ghcr.io/stakater/charts/reloader-v2:${{ steps.new_chart_version.outputs.result }}
|
||||
|
||||
- name: Publish Helm chart to gh-pages
|
||||
uses: stefanprodan/helm-gh-pages@0ad2bb377311d61ac04ad9eb6f252fb68e207260 # v1.7.0
|
||||
@@ -114,7 +120,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PUBLISH_TOKEN }}
|
||||
WITH_V: false
|
||||
CUSTOM_TAG: chart-v${{ steps.new_chart_version.outputs.result }}
|
||||
CUSTOM_TAG: reloader-v2-chart-v${{ steps.new_chart_version.outputs.result }}
|
||||
|
||||
- name: Notify Slack
|
||||
uses: 8398a7/action-slack@77eaa4f1c608a7d68b38af4e3f739dcd8cba273e # v3
|
||||
|
||||
@@ -3,7 +3,7 @@ name: Release Helm chart
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "chart-v*"
|
||||
- "reloader-v2-chart-v*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
run: |
|
||||
gh release create "$tag" \
|
||||
--repo="$GITHUB_REPOSITORY" \
|
||||
--title="Helm chart ${tag#chart-}" \
|
||||
--title="Helm chart reloader-v2 ${tag#reloader-v2-chart-v}" \
|
||||
--generate-notes
|
||||
|
||||
- name: Notify Slack
|
||||
|
||||
@@ -147,7 +147,8 @@ jobs:
|
||||
run: |
|
||||
echo GHCR_IMAGE_REPOSITORY=${{env.REGISTRY}}/$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV
|
||||
|
||||
# tag this image as latest as it will be used in plain manifests
|
||||
# v2 branch: publish a floating `v2` tag instead of `latest` so v2
|
||||
# releases never overwrite the v1 `latest` tag used by plain manifests.
|
||||
- name: Build and Push Docker Image to ghcr registry
|
||||
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
|
||||
with:
|
||||
@@ -157,7 +158,7 @@ jobs:
|
||||
push: true
|
||||
platforms: linux/amd64,linux/arm,linux/arm64
|
||||
tags: |
|
||||
${{ env.GHCR_IMAGE_REPOSITORY }}:${{ steps.generate_tag.outputs.RELEASE_VERSION }},${{ env.GHCR_IMAGE_REPOSITORY }}:latest
|
||||
${{ env.GHCR_IMAGE_REPOSITORY }}:${{ steps.generate_tag.outputs.RELEASE_VERSION }},${{ env.GHCR_IMAGE_REPOSITORY }}:v2
|
||||
build-args: |
|
||||
VERSION=${{ steps.generate_tag.outputs.RELEASE_VERSION }}
|
||||
COMMIT=${{ github.sha }}
|
||||
|
||||
+19
-19
@@ -55,7 +55,15 @@ func newReloaderCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func run(cmd *cobra.Command, args []string) error {
|
||||
if err := flags.ApplyFlags(cfg); err != nil {
|
||||
// Configure logging first so ApplyFlags can surface namespace-scope warnings
|
||||
// through a ready logger instead of returning them to the caller.
|
||||
log, err := configureLogging(flags.LoggingFlags())
|
||||
if err != nil {
|
||||
return fmt.Errorf("configuring logging: %w", err)
|
||||
}
|
||||
controllerruntime.SetLogger(log)
|
||||
|
||||
if err := flags.ApplyFlags(cfg, log); err != nil {
|
||||
return fmt.Errorf("applying flags: %w", err)
|
||||
}
|
||||
|
||||
@@ -73,19 +81,12 @@ func run(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
log, err := configureLogging(cfg.LogFormat, cfg.LogLevel)
|
||||
if err != nil {
|
||||
return fmt.Errorf("configuring logging: %w", err)
|
||||
}
|
||||
|
||||
controllerruntime.SetLogger(log)
|
||||
|
||||
log.Info("Starting Reloader")
|
||||
|
||||
if cfg.WatchedNamespace != "" {
|
||||
log.Info("watching single namespace", "namespace", cfg.WatchedNamespace)
|
||||
} else {
|
||||
if cfg.IsGlobalMode() {
|
||||
log.Info("watching all namespaces")
|
||||
} else {
|
||||
log.Info("watching scoped namespaces", "namespaces", cfg.WatchedNamespaces)
|
||||
}
|
||||
|
||||
if len(cfg.NamespaceSelectors) > 0 {
|
||||
@@ -150,14 +151,13 @@ func run(cmd *cobra.Command, args []string) error {
|
||||
return fmt.Errorf("setting up reconcilers: %w", err)
|
||||
}
|
||||
|
||||
// Skip metadata publisher when ConfigMaps are ignored (no RBAC permissions)
|
||||
if !cfg.IsResourceIgnored("configmaps") {
|
||||
if err := mgr.Add(metadata.Runnable(mgr.GetClient(), cfg, log)); err != nil {
|
||||
log.Error(err, "Failed to add metadata publisher")
|
||||
// Non-fatal, continue starting
|
||||
}
|
||||
} else {
|
||||
log.Info("skipping metadata publisher (configmaps ignored)")
|
||||
// Meta-info is internal instance metadata and is always published. The
|
||||
// publisher builds its own uncached client (see metadata.Runnable) because
|
||||
// the ConfigMap lives in Reloader's own namespace, which the manager cache
|
||||
// does not cover in scoped mode.
|
||||
if err := mgr.Add(metadata.Runnable(mgr.GetConfig(), mgr.GetScheme(), cfg, log)); err != nil {
|
||||
log.Error(err, "Failed to add metadata publisher")
|
||||
// Non-fatal, continue starting
|
||||
}
|
||||
|
||||
if cfg.EnablePProf {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
apiVersion: v1
|
||||
name: reloader
|
||||
apiVersion: v2
|
||||
name: reloader-v2
|
||||
description: Reloader chart that runs on kubernetes
|
||||
version: 2.2.12
|
||||
appVersion: v1.4.17
|
||||
version: 2.0.0
|
||||
appVersion: v2.0.0
|
||||
keywords:
|
||||
- Reloader
|
||||
- kubernetes
|
||||
@@ -17,3 +17,9 @@ maintainers:
|
||||
email: rasheed@stakater.com
|
||||
- name: faizanahmad055
|
||||
email: faizan@stakater.com
|
||||
dependencies:
|
||||
- name: reloader-enterprise
|
||||
alias: enterprise
|
||||
version: 0.1.0
|
||||
repository: oci://ghcr.io/stakater/charts
|
||||
condition: enterprise.enabled
|
||||
|
||||
@@ -10,13 +10,13 @@ helm repo add stakater https://stakater.github.io/stakater-charts
|
||||
|
||||
helm repo update
|
||||
|
||||
helm install stakater/reloader # For helm3 add --generate-name flag or set the release name
|
||||
helm install stakater/reloader-v2 # For helm3 add --generate-name flag or set the release name
|
||||
|
||||
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-v2 -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-v2 --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
|
||||
helm install stakater/reloader-v2 --set reloader.ignoreJobs=true --set reloader.ignoreCronJobs=true --generate-name # Install Reloader ignoring Jobs and CronJobs from reload monitoring
|
||||
```
|
||||
|
||||
## Uninstalling
|
||||
@@ -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 not watched for reloads unless you list it explicitly; the chart only grants it a minimal Role for Reloader's internal meta-info ConfigMap (and leader-election in HA). 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` |
|
||||
@@ -169,6 +170,62 @@ helm uninstall {{RELEASE_NAME}} -n {{NAMESPACE}}
|
||||
### Deprecation Notice
|
||||
- `serviceMonitor` will be removed in future releases in favor of `PodMonitor`
|
||||
|
||||
## Enterprise mode
|
||||
|
||||
Reloader Enterprise adds a console, gateway, and cache (dragonfly) alongside the
|
||||
operator. It ships as an optional subchart gated by `enterprise.enabled`.
|
||||
|
||||
Enabling it requires **four** changes, because the operator image is swapped manually:
|
||||
|
||||
1. Turn on the subchart and set the component hostnames:
|
||||
|
||||
```yaml
|
||||
enterprise:
|
||||
enabled: true
|
||||
global:
|
||||
consoleHost: reloader-enterprise-console.apps.example.com
|
||||
gatewayHost: reloader-enterprise-gateway.apps.example.com
|
||||
```
|
||||
|
||||
2. Swap the operator image to the enterprise image:
|
||||
|
||||
```yaml
|
||||
image:
|
||||
repository: ghcr.io/stakater/reloader-enterprise
|
||||
tag: v0.0.43 # enterprise operator image tag (v-prefixed); use the latest release
|
||||
```
|
||||
|
||||
> **Version note:** three different numbers are in play here, don't mix them up:
|
||||
> - `image.tag` above is the **enterprise operator image** tag (e.g. `v0.0.43`).
|
||||
> - the `reloader-enterprise` **subchart** version (`0.1.0`, in `Chart.yaml`
|
||||
> dependencies) is the Helm chart version, not an image tag.
|
||||
> - the console and gateway images have their own tags again, set under
|
||||
> `enterprise.console.*` / `enterprise.gateway.*`.
|
||||
|
||||
3. Provide an image pull secret — the enterprise operator, console, and gateway
|
||||
images are in **private GHCR**:
|
||||
|
||||
```bash
|
||||
kubectl create secret docker-registry saap-dockerconfigjson \
|
||||
--docker-server=ghcr.io \
|
||||
--docker-username=<user> --docker-password=<token> \
|
||||
-n <release-namespace>
|
||||
```
|
||||
|
||||
```yaml
|
||||
global:
|
||||
imagePullSecrets:
|
||||
- name: saap-dockerconfigjson
|
||||
```
|
||||
|
||||
4. Deeper enterprise component settings pass through under `enterprise.console.*`,
|
||||
`enterprise.gateway.*`, and `enterprise.dragonfly.*` (see the reloader-enterprise
|
||||
chart values for the full list). Values you do not override fall back to the
|
||||
subchart defaults.
|
||||
|
||||
When `enterprise.enabled=false` (the default) none of the enterprise components are
|
||||
rendered and the operator uses the community image.
|
||||
|
||||
## Release Process
|
||||
|
||||
_Helm chart versioning_: The Reloader Helm chart is maintained in this repository. The Helm chart has its own semantic versioning. Helm charts and code releases are separate artifacts and separately versioned. Manifest making strategy relies on Kustomize. The Reloader Helm chart manages the two artifacts with these two fields:
|
||||
|
||||
@@ -5,3 +5,13 @@
|
||||
{{ .Values.reloader.custom_annotations.secret | default "secret.reloader.stakater.com/reload" }}: "foo-secret"
|
||||
|
||||
- After successful installation, your pods will get rolling updates when a change in data of configmap or secret will happen.
|
||||
{{- if .Values.enterprise.enabled }}
|
||||
{{- $usingCommunityImage := ternary (eq .Values.image.name "stakater/reloader") (eq .Values.image.repository "ghcr.io/stakater/reloader") (not (empty .Values.global.imageRegistry)) }}
|
||||
{{- if $usingCommunityImage }}
|
||||
|
||||
WARNING: enterprise.enabled=true but the operator image still points at the
|
||||
community image. The operator will run, but without enterprise features. To use
|
||||
the enterprise operator, override {{ if .Values.global.imageRegistry }}image.name{{ else }}image.repository{{ end }}/image.tag — see the
|
||||
chart README "Enterprise mode".
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -88,6 +88,231 @@ Create the namespace selector if it does not watch globally
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Namespaces to watch in scoped mode: exactly the user-supplied reloader.namespaces,
|
||||
trimmed, de-duped and sorted. The release namespace is intentionally NOT added here
|
||||
— Reloader watches only what the user asked for. An empty result is not necessarily
|
||||
global mode: with watchGlobally=false it becomes single-namespace mode (the release
|
||||
namespace, injected via --namespaces by reloader-effectiveNamespaces-csv); only with
|
||||
watchGlobally=true does empty mean watch-all.
|
||||
Returns a JSON-encoded list; consumers use mustFromJson to iterate.
|
||||
*/}}
|
||||
{{- define "reloader-watchNamespaces" -}}
|
||||
{{- $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 -}}
|
||||
{{- $clean | uniq | sortAlpha | toJson -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Comma-joined form of reloader-watchNamespaces, for the --namespaces CLI flag.
|
||||
*/}}
|
||||
{{- define "reloader-watchNamespaces-csv" -}}
|
||||
{{- include "reloader-watchNamespaces" . | mustFromJson | join "," -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
The effective watched namespaces for the --namespaces CLI flag — the single
|
||||
chart-side source of truth for watch scope, so the binary never has to fall back
|
||||
to the KUBERNETES_NAMESPACE env:
|
||||
- scoped mode -> the cleaned reloader.namespaces list
|
||||
- single-namespace mode -> the release namespace (watchGlobally=false, no list)
|
||||
- global mode -> empty (no --namespaces flag; watch all)
|
||||
Returns a comma-joined string ("" in global mode).
|
||||
*/}}
|
||||
{{- define "reloader-effectiveNamespaces-csv" -}}
|
||||
{{- $watch := include "reloader-watchNamespaces" . | mustFromJson -}}
|
||||
{{- if $watch -}}
|
||||
{{- $watch | join "," -}}
|
||||
{{- else if not .Values.reloader.watchGlobally -}}
|
||||
{{- .Values.namespace | default .Release.Namespace -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Whether Reloader runs in scoped mode. This is the single source of truth for the
|
||||
scoped-vs-global decision: it is true only when the cleaned watch list
|
||||
(reloader-watchNamespaces) is non-empty. Gate on this rather than the raw
|
||||
.Values.reloader.namespaces, which is truthy even for values like " , " that trim
|
||||
to an empty list (those must fall through to global/single-namespace mode).
|
||||
Returns "true" (truthy) or "" (falsy).
|
||||
*/}}
|
||||
{{- define "reloader-isScoped" -}}
|
||||
{{- if include "reloader-watchNamespaces" . | mustFromJson -}}
|
||||
true
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Fails the render on an inconsistent namespace configuration: reloader.namespaces
|
||||
(scoped mode) requires reloader.watchGlobally=false. Included from deployment.yaml
|
||||
so it is validated once regardless of which templates render.
|
||||
*/}}
|
||||
{{- define "reloader-validate-namespaces" -}}
|
||||
{{- if and .Values.reloader.watchGlobally (include "reloader-isScoped" .) -}}
|
||||
{{- fail "reloader.namespaces is set but reloader.watchGlobally is true; set reloader.watchGlobally=false to use scoped namespace mode." -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
RBAC rules Reloader needs in its own (release) namespace, independent of the
|
||||
watched namespaces. Reloader publishes an internal meta-info ConfigMap there in
|
||||
every mode, so configmap write access is always granted. In scoped mode the
|
||||
release namespace is not covered by the watch RBAC, so under HA the leader-election
|
||||
leases and the events it emits are granted here too; in global/single mode those
|
||||
are already covered by the ClusterRole or the single-namespace Role.
|
||||
Expects the root context ($) as its argument.
|
||||
*/}}
|
||||
{{- define "reloader-release-rules" }}
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- get
|
||||
- create
|
||||
- update
|
||||
- patch
|
||||
{{- if and (include "reloader-isScoped" .) .Values.reloader.enableHA }}
|
||||
- apiGroups:
|
||||
- "coordination.k8s.io"
|
||||
resources:
|
||||
- leases
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
- update
|
||||
- apiGroups:
|
||||
- ""
|
||||
- "events.k8s.io"
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
- update
|
||||
{{- end }}
|
||||
{{- 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
|
||||
- watch
|
||||
- update
|
||||
- patch
|
||||
{{- end }}
|
||||
- apiGroups:
|
||||
- "apps"
|
||||
resources:
|
||||
- deployments
|
||||
- daemonsets
|
||||
- statefulsets
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- watch
|
||||
- update
|
||||
- patch
|
||||
{{- if .Values.reloader.ignoreCronJobs }}{{- else }}
|
||||
- apiGroups:
|
||||
- "batch"
|
||||
resources:
|
||||
- cronjobs
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- watch
|
||||
- update
|
||||
- patch
|
||||
{{- end }}
|
||||
{{- if .Values.reloader.ignoreJobs }}{{- else }}
|
||||
- apiGroups:
|
||||
- "batch"
|
||||
resources:
|
||||
- jobs
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- list
|
||||
- get
|
||||
- watch
|
||||
{{- end }}
|
||||
{{- 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:
|
||||
- ""
|
||||
- "events.k8s.io"
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
- update
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Normalizes global.imagePullSecrets to a list of objects with name fields.
|
||||
Supports both of these in values.yaml:
|
||||
|
||||
@@ -74,9 +74,9 @@ rules:
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- watch
|
||||
- update
|
||||
- patch
|
||||
- watch
|
||||
{{- if .Values.reloader.ignoreCronJobs }}{{- else }}
|
||||
- apiGroups:
|
||||
- "batch"
|
||||
@@ -124,9 +124,11 @@ rules:
|
||||
{{- end}}
|
||||
- apiGroups:
|
||||
- ""
|
||||
- "events.k8s.io"
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
- update
|
||||
{{- end }}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{{- include "reloader-validate-namespaces" . -}}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
@@ -143,12 +144,6 @@ spec:
|
||||
fieldRef:
|
||||
fieldPath: {{ $value | quote}}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if eq .Values.reloader.watchGlobally false }}
|
||||
- name: KUBERNETES_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
{{- end }}
|
||||
|
||||
- name: RELOADER_NAMESPACE
|
||||
@@ -215,7 +210,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) (include "reloader-effectiveNamespaces-csv" .) (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 }}"
|
||||
@@ -236,7 +231,10 @@ spec:
|
||||
{{- else if .Values.reloader.ignoreCronJobs }}
|
||||
- "--ignored-workload-types=cronjobs"
|
||||
{{- end }}
|
||||
{{- if .Values.reloader.ignoreNamespaces }}
|
||||
{{- if (include "reloader-effectiveNamespaces-csv" .) }}
|
||||
- "--namespaces={{ include "reloader-effectiveNamespaces-csv" . }}"
|
||||
{{- end }}
|
||||
{{- if and .Values.reloader.ignoreNamespaces .Values.reloader.watchGlobally }}
|
||||
- "--namespaces-to-ignore={{ .Values.reloader.ignoreNamespaces }}"
|
||||
{{- end }}
|
||||
{{- if (include "reloader-namespaceSelector" .) }}
|
||||
@@ -252,7 +250,7 @@ spec:
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.reloader.enableCSIIntegration }}
|
||||
- "--enable-csi-integration"
|
||||
- "--enable-csi-integration=true"
|
||||
{{- end }}
|
||||
{{- if .Values.reloader.custom_annotations }}
|
||||
{{- if .Values.reloader.custom_annotations.configmap }}
|
||||
|
||||
@@ -1,9 +1,31 @@
|
||||
{{- 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 (include "reloader-isScoped" .) }}
|
||||
{{- 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,103 +41,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
|
||||
- watch
|
||||
- update
|
||||
- patch
|
||||
{{- end }}
|
||||
- apiGroups:
|
||||
- "apps"
|
||||
resources:
|
||||
- deployments
|
||||
- daemonsets
|
||||
- statefulsets
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- update
|
||||
- patch
|
||||
- watch
|
||||
- apiGroups:
|
||||
- "batch"
|
||||
resources:
|
||||
- cronjobs
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- watch
|
||||
- update
|
||||
- patch
|
||||
- apiGroups:
|
||||
- "batch"
|
||||
resources:
|
||||
- jobs
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- list
|
||||
- get
|
||||
- watch
|
||||
{{- 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 }}
|
||||
|
||||
---
|
||||
@@ -137,14 +64,5 @@ metadata:
|
||||
name: {{ template "reloader-fullname" . }}-metadata-role
|
||||
namespace: {{ .Values.namespace | default .Release.Namespace }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- watch
|
||||
- create
|
||||
- update
|
||||
{{- include "reloader-release-rules" . }}
|
||||
{{- 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 (include "reloader-isScoped" .) }}
|
||||
{{- 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 }}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
suite: Enterprise image swap
|
||||
templates:
|
||||
- deployment.yaml
|
||||
tests:
|
||||
- it: uses the community image by default
|
||||
asserts:
|
||||
- equal:
|
||||
path: spec.template.spec.containers[0].image
|
||||
value: ghcr.io/stakater/reloader:v2.0.0
|
||||
# The operator image is swapped manually via image.*; enterprise.enabled only
|
||||
# toggles the subchart and must NOT change the operator image on its own.
|
||||
- it: enterprise.enabled alone does not change the operator image
|
||||
set:
|
||||
enterprise:
|
||||
enabled: true
|
||||
asserts:
|
||||
- equal:
|
||||
path: spec.template.spec.containers[0].image
|
||||
value: ghcr.io/stakater/reloader:v2.0.0
|
||||
- it: uses the enterprise image when the image block is overridden
|
||||
set:
|
||||
image:
|
||||
repository: ghcr.io/stakater/reloader-enterprise
|
||||
tag: test-tag
|
||||
asserts:
|
||||
- equal:
|
||||
path: spec.template.spec.containers[0].image
|
||||
value: ghcr.io/stakater/reloader-enterprise:test-tag
|
||||
@@ -0,0 +1,134 @@
|
||||
suite: Namespace watch flags & validation
|
||||
templates:
|
||||
- deployment.yaml
|
||||
release:
|
||||
name: reloader
|
||||
namespace: reloader-ns
|
||||
tests:
|
||||
# ---------------------------------------------------------------- scoped mode
|
||||
- it: scoped mode passes the watched namespaces via --namespaces
|
||||
set:
|
||||
reloader:
|
||||
watchGlobally: false
|
||||
namespaces:
|
||||
- team-a
|
||||
- team-b
|
||||
asserts:
|
||||
- contains:
|
||||
path: spec.template.spec.containers[0].args
|
||||
content: "--namespaces=team-a,team-b"
|
||||
|
||||
- it: namespaces are de-duplicated and sorted for the --namespaces flag
|
||||
set:
|
||||
reloader:
|
||||
watchGlobally: false
|
||||
namespaces:
|
||||
- team-b
|
||||
- team-a
|
||||
- team-a
|
||||
asserts:
|
||||
- contains:
|
||||
path: spec.template.spec.containers[0].args
|
||||
content: "--namespaces=team-a,team-b"
|
||||
|
||||
- it: a comma-separated string value is accepted for reloader.namespaces
|
||||
set:
|
||||
reloader:
|
||||
watchGlobally: false
|
||||
namespaces: "team-a,team-b"
|
||||
asserts:
|
||||
- contains:
|
||||
path: spec.template.spec.containers[0].args
|
||||
content: "--namespaces=team-a,team-b"
|
||||
|
||||
# ----------------------------------------------------------- single-ns mode
|
||||
- it: single-namespace mode passes the release namespace via --namespaces
|
||||
set:
|
||||
reloader:
|
||||
watchGlobally: false
|
||||
asserts:
|
||||
- contains:
|
||||
path: spec.template.spec.containers[0].args
|
||||
content: "--namespaces=reloader-ns"
|
||||
|
||||
- it: whitespace-only namespaces fall through to single-namespace mode
|
||||
set:
|
||||
reloader:
|
||||
watchGlobally: false
|
||||
namespaces:
|
||||
- " "
|
||||
- " "
|
||||
asserts:
|
||||
- contains:
|
||||
path: spec.template.spec.containers[0].args
|
||||
content: "--namespaces=reloader-ns"
|
||||
|
||||
- it: KUBERNETES_NAMESPACE env is no longer injected in single-namespace mode
|
||||
set:
|
||||
reloader:
|
||||
watchGlobally: false
|
||||
asserts:
|
||||
- notContains:
|
||||
path: spec.template.spec.containers[0].env
|
||||
content:
|
||||
name: KUBERNETES_NAMESPACE
|
||||
any: true
|
||||
|
||||
# ---------------------------------------------------------------- global mode
|
||||
- it: global mode does not pass a --namespaces flag
|
||||
set:
|
||||
reloader:
|
||||
watchGlobally: true
|
||||
logLevel: info
|
||||
asserts:
|
||||
- notContains:
|
||||
path: spec.template.spec.containers[0].args
|
||||
content: "--namespaces=reloader-ns"
|
||||
- notContains:
|
||||
path: spec.template.spec.containers[0].args
|
||||
content: "--namespaces=team-a"
|
||||
|
||||
- it: namespaces-to-ignore is only emitted in global mode
|
||||
set:
|
||||
reloader:
|
||||
watchGlobally: true
|
||||
ignoreNamespaces: "kube-system"
|
||||
asserts:
|
||||
- contains:
|
||||
path: spec.template.spec.containers[0].args
|
||||
content: "--namespaces-to-ignore=kube-system"
|
||||
|
||||
- it: namespaces-to-ignore is dropped in single-namespace mode
|
||||
set:
|
||||
reloader:
|
||||
watchGlobally: false
|
||||
ignoreNamespaces: "kube-system"
|
||||
asserts:
|
||||
- notContains:
|
||||
path: spec.template.spec.containers[0].args
|
||||
content: "--namespaces-to-ignore=kube-system"
|
||||
|
||||
# ------------------------------------------------------------- validation
|
||||
- it: fails to render when reloader.namespaces is set but watchGlobally is true
|
||||
set:
|
||||
reloader:
|
||||
watchGlobally: true
|
||||
namespaces:
|
||||
- team-a
|
||||
asserts:
|
||||
- failedTemplate:
|
||||
errorMessage: "reloader.namespaces is set but reloader.watchGlobally is true; set reloader.watchGlobally=false to use scoped namespace mode."
|
||||
|
||||
- it: does not fail validation for whitespace-only namespaces with watchGlobally true
|
||||
set:
|
||||
reloader:
|
||||
watchGlobally: true
|
||||
namespaces:
|
||||
- " "
|
||||
asserts:
|
||||
# whitespace trims to an empty watch list, so this is global mode, not scoped -> renders fine
|
||||
- hasDocuments:
|
||||
count: 1
|
||||
- notContains:
|
||||
path: spec.template.spec.containers[0].args
|
||||
content: "--namespaces=reloader-ns"
|
||||
@@ -0,0 +1,234 @@
|
||||
suite: Namespace-scoped RBAC
|
||||
templates:
|
||||
- role.yaml
|
||||
- rolebinding.yaml
|
||||
- clusterrole.yaml
|
||||
release:
|
||||
name: reloader
|
||||
namespace: reloader-ns
|
||||
tests:
|
||||
# ---------------------------------------------------------------- scoped mode
|
||||
- it: scoped mode renders a Role + RoleBinding per watched namespace plus the metadata role, and no ClusterRole
|
||||
set:
|
||||
reloader:
|
||||
watchGlobally: false
|
||||
namespaces:
|
||||
- team-a
|
||||
- team-b
|
||||
asserts:
|
||||
# 2 watched Roles + 1 metadata Role
|
||||
- hasDocuments:
|
||||
count: 3
|
||||
template: role.yaml
|
||||
- hasDocuments:
|
||||
count: 3
|
||||
template: rolebinding.yaml
|
||||
- hasDocuments:
|
||||
count: 0
|
||||
template: clusterrole.yaml
|
||||
|
||||
- it: scoped mode grants the namespaced workload rules in a watched namespace
|
||||
set:
|
||||
reloader:
|
||||
watchGlobally: false
|
||||
namespaces:
|
||||
- team-a
|
||||
- team-b
|
||||
template: role.yaml
|
||||
documentSelector:
|
||||
path: metadata.namespace
|
||||
value: team-a
|
||||
asserts:
|
||||
- isKind:
|
||||
of: Role
|
||||
- equal:
|
||||
path: rules[0].apiGroups[0]
|
||||
value: ""
|
||||
- contains:
|
||||
path: rules[0].resources
|
||||
content: secrets
|
||||
- contains:
|
||||
path: rules[0].resources
|
||||
content: configmaps
|
||||
- contains:
|
||||
path: rules
|
||||
content:
|
||||
apiGroups:
|
||||
- "apps"
|
||||
resources:
|
||||
- deployments
|
||||
- daemonsets
|
||||
- statefulsets
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- watch
|
||||
- update
|
||||
- patch
|
||||
|
||||
- it: scoped RoleBinding in a watched namespace binds the release ServiceAccount to the namespaced Role
|
||||
set:
|
||||
reloader:
|
||||
watchGlobally: false
|
||||
namespaces:
|
||||
- team-a
|
||||
- team-b
|
||||
template: rolebinding.yaml
|
||||
documentSelector:
|
||||
path: metadata.namespace
|
||||
value: team-b
|
||||
asserts:
|
||||
- isKind:
|
||||
of: RoleBinding
|
||||
- equal:
|
||||
path: roleRef.name
|
||||
value: reloader-reloader-v2-role
|
||||
- equal:
|
||||
path: subjects[0].namespace
|
||||
value: reloader-ns
|
||||
|
||||
# ------------------------------------------------------------- metadata role
|
||||
- it: metadata role is created in the release namespace with configmap write in scoped mode
|
||||
set:
|
||||
reloader:
|
||||
watchGlobally: false
|
||||
namespaces:
|
||||
- team-a
|
||||
template: role.yaml
|
||||
documentSelector:
|
||||
path: metadata.name
|
||||
value: reloader-reloader-v2-metadata-role
|
||||
asserts:
|
||||
- equal:
|
||||
path: metadata.namespace
|
||||
value: reloader-ns
|
||||
- contains:
|
||||
path: rules[0].resources
|
||||
content: configmaps
|
||||
- contains:
|
||||
path: rules[0].verbs
|
||||
content: create
|
||||
|
||||
- it: metadata role also grants leases and events in the release namespace under HA + scoped mode
|
||||
set:
|
||||
reloader:
|
||||
watchGlobally: false
|
||||
enableHA: true
|
||||
namespaces:
|
||||
- team-a
|
||||
template: role.yaml
|
||||
documentSelector:
|
||||
path: metadata.name
|
||||
value: reloader-reloader-v2-metadata-role
|
||||
asserts:
|
||||
- contains:
|
||||
path: rules
|
||||
content:
|
||||
apiGroups:
|
||||
- "coordination.k8s.io"
|
||||
resources:
|
||||
- leases
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
- update
|
||||
|
||||
- it: metadata role does NOT grant leases when HA is disabled in scoped mode
|
||||
set:
|
||||
reloader:
|
||||
watchGlobally: false
|
||||
enableHA: false
|
||||
namespaces:
|
||||
- team-a
|
||||
template: role.yaml
|
||||
documentSelector:
|
||||
path: metadata.name
|
||||
value: reloader-reloader-v2-metadata-role
|
||||
asserts:
|
||||
- notContains:
|
||||
path: rules
|
||||
content:
|
||||
apiGroups:
|
||||
- "coordination.k8s.io"
|
||||
resources:
|
||||
- leases
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
- update
|
||||
|
||||
# ----------------------------------------------------------- single-ns mode
|
||||
- it: single-namespace mode (watchGlobally=false, no list) renders one Role in the release namespace and no ClusterRole
|
||||
set:
|
||||
reloader:
|
||||
watchGlobally: false
|
||||
asserts:
|
||||
# 1 single Role + 1 metadata Role
|
||||
- hasDocuments:
|
||||
count: 2
|
||||
template: role.yaml
|
||||
- hasDocuments:
|
||||
count: 0
|
||||
template: clusterrole.yaml
|
||||
- equal:
|
||||
path: metadata.namespace
|
||||
value: reloader-ns
|
||||
template: role.yaml
|
||||
documentIndex: 0
|
||||
|
||||
# ---------------------------------------------------------------- global mode
|
||||
- it: global mode renders a ClusterRole and only the metadata Role (no watched Roles)
|
||||
set:
|
||||
reloader:
|
||||
watchGlobally: true
|
||||
asserts:
|
||||
- hasDocuments:
|
||||
count: 1
|
||||
template: clusterrole.yaml
|
||||
- isKind:
|
||||
of: ClusterRole
|
||||
template: clusterrole.yaml
|
||||
# only the metadata role remains from role.yaml
|
||||
- hasDocuments:
|
||||
count: 1
|
||||
template: role.yaml
|
||||
- equal:
|
||||
path: metadata.name
|
||||
value: reloader-reloader-v2-metadata-role
|
||||
template: role.yaml
|
||||
documentIndex: 0
|
||||
|
||||
# -------------------------------------------------------- input normalization
|
||||
- it: whitespace-only namespaces fall through to single-namespace mode (no watched Roles)
|
||||
set:
|
||||
reloader:
|
||||
watchGlobally: false
|
||||
namespaces:
|
||||
- " "
|
||||
- " "
|
||||
asserts:
|
||||
# cleaned list is empty -> single Role + metadata Role, both in release ns
|
||||
- hasDocuments:
|
||||
count: 2
|
||||
template: role.yaml
|
||||
- equal:
|
||||
path: metadata.namespace
|
||||
value: reloader-ns
|
||||
template: role.yaml
|
||||
documentIndex: 0
|
||||
|
||||
- it: rbac disabled renders no Roles, RoleBindings, or ClusterRole
|
||||
set:
|
||||
reloader:
|
||||
watchGlobally: false
|
||||
rbac:
|
||||
enabled: false
|
||||
namespaces:
|
||||
- team-a
|
||||
asserts:
|
||||
- hasDocuments:
|
||||
count: 0
|
||||
template: role.yaml
|
||||
- hasDocuments:
|
||||
count: 0
|
||||
template: rolebinding.yaml
|
||||
@@ -10,6 +10,11 @@ global:
|
||||
#imagePullSecrets:
|
||||
# - my-pull-secret
|
||||
|
||||
## Required when enterprise.enabled=true (shared with the enterprise subchart)
|
||||
consoleHost: ""
|
||||
gatewayHost: ""
|
||||
gatewayScheme: https
|
||||
|
||||
kubernetes:
|
||||
host: https://kubernetes.default
|
||||
|
||||
@@ -19,10 +24,42 @@ fullnameOverride: ""
|
||||
image:
|
||||
name: stakater/reloader
|
||||
repository: ghcr.io/stakater/reloader
|
||||
tag: v1.4.17
|
||||
tag: v2.0.0
|
||||
# digest: sha256:1234567
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
# Reloader Enterprise components (console + gateway + dragonfly cache).
|
||||
# When enabled, ALSO swap the operator image to the enterprise image and provide
|
||||
# global.imagePullSecrets — see the chart README "Enterprise mode".
|
||||
# NOTE: if global.imageRegistry is set, the operator image resolves from
|
||||
# global.imageRegistry + image.name (image.repository is ignored); in that mode
|
||||
# override image.name, not image.repository.
|
||||
enterprise:
|
||||
enabled: false
|
||||
console:
|
||||
enabled: true
|
||||
# deployment:
|
||||
# image:
|
||||
# tag: ""
|
||||
# OpenShift: router auto-creates a Route from the Ingress; this annotation
|
||||
# sets edge TLS termination on that Route.
|
||||
# ingress:
|
||||
# annotations:
|
||||
# route.openshift.io/termination: edge
|
||||
gateway:
|
||||
enabled: true
|
||||
dragonfly:
|
||||
enabled: true
|
||||
# deployment:
|
||||
# image:
|
||||
# Gateway images are published v-prefixed (WITH_V: true); latest is v0.0.23.
|
||||
# tag: ""
|
||||
# ingress:
|
||||
# annotations:
|
||||
# route.openshift.io/termination: edge
|
||||
dragonfly:
|
||||
fullnameOverride: reloader-enterprise-dragonfly
|
||||
|
||||
reloader:
|
||||
autoReloadAll: false
|
||||
isArgoRollouts: false
|
||||
@@ -45,11 +82,20 @@ 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 you must also set
|
||||
# watchGlobally=false. Reloader watches exactly these namespaces and the chart creates a
|
||||
# namespaced Role + RoleBinding in each one — no ClusterRole is created. Reloader's own
|
||||
# (release) namespace is NOT watched for reloads; it only receives a small metadata Role
|
||||
# for the internal meta-info ConfigMap and (under HA) leader-election leases/events.
|
||||
# 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
|
||||
enablePProf: false
|
||||
# Set to true to enable CSI / SecretProviderClass integration
|
||||
enableCSIIntegration: false
|
||||
# Address to start pprof server on. Default is ":6060"
|
||||
pprofAddr: ":6060"
|
||||
@@ -136,7 +182,7 @@ reloader:
|
||||
labels:
|
||||
provider: stakater
|
||||
group: com.stakater.platform
|
||||
version: v1.4.14
|
||||
version: v1.4.19
|
||||
# Support for extra environment variables.
|
||||
env:
|
||||
# Open supports Key value pair as environment variables.
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
@@ -225,10 +226,11 @@ func BindFlags(fs *pflag.FlagSet, cfg *config.Config) {
|
||||
"Annotation to indicate when a deployment was paused by Reloader",
|
||||
)
|
||||
|
||||
// Watched namespace (for single-namespace mode)
|
||||
fs.String(
|
||||
"watch-namespace", cfg.WatchedNamespace,
|
||||
"Namespace to watch (empty for all namespaces)",
|
||||
// Watched namespaces (scoped mode). Empty means watch all namespaces;
|
||||
// KUBERNETES_NAMESPACE (env) is used as a single-namespace fallback.
|
||||
fs.StringSlice(
|
||||
"namespaces", nil,
|
||||
"explicit list of namespaces to watch (scoped mode; creates no ClusterRole)",
|
||||
)
|
||||
|
||||
// Alerting
|
||||
@@ -265,9 +267,17 @@ func BindFlags(fs *pflag.FlagSet, cfg *config.Config) {
|
||||
_ = v.BindEnv("alert-proxy", "ALERT_PROXY", "ALERT_WEBHOOK_PROXY")
|
||||
}
|
||||
|
||||
// ApplyFlags applies flag values from viper to the config struct.
|
||||
// Call this after parsing flags.
|
||||
func ApplyFlags(cfg *config.Config) error {
|
||||
// LoggingFlags returns the log format and level from parsed flags/env. The
|
||||
// caller uses these to configure logging before ApplyFlags runs, so ApplyFlags
|
||||
// can log warnings through a ready logger.
|
||||
func LoggingFlags() (format, level string) {
|
||||
return v.GetString("log-format"), v.GetString("log-level")
|
||||
}
|
||||
|
||||
// ApplyFlags applies flag values from viper to the config struct. Call this
|
||||
// after parsing flags. It finalizes namespace scope and logs any warnings it
|
||||
// produces through the given logger.
|
||||
func ApplyFlags(cfg *config.Config, log logr.Logger) error {
|
||||
// Boolean flags
|
||||
cfg.AutoReloadAll = v.GetBool("auto-reload-all")
|
||||
cfg.SyncAfterRestart = v.GetBool("sync-after-restart")
|
||||
@@ -296,9 +306,16 @@ func ApplyFlags(cfg *config.Config) error {
|
||||
cfg.MetricsAddr = v.GetString("metrics-addr")
|
||||
cfg.HealthAddr = v.GetString("health-addr")
|
||||
cfg.PProfAddr = v.GetString("pprof-addr")
|
||||
cfg.WatchedNamespace = v.GetString("watch-namespace")
|
||||
if cfg.WatchedNamespace == "" {
|
||||
cfg.WatchedNamespace = v.GetString("KUBERNETES_NAMESPACE")
|
||||
// Namespace scope: an explicit --namespaces list takes precedence (scoped
|
||||
// mode); otherwise fall back to KUBERNETES_NAMESPACE for single-namespace
|
||||
// mode; an empty result means global (all-namespaces) mode.
|
||||
// Trim and drop empty entries from the slice to prevent empty strings from
|
||||
// being treated as "watch all namespaces" by controller-runtime.
|
||||
cfg.WatchedNamespaces = trimAndDropEmptyStrings(v.GetStringSlice("namespaces"))
|
||||
if len(cfg.WatchedNamespaces) == 0 {
|
||||
if ns := v.GetString("KUBERNETES_NAMESPACE"); ns != "" {
|
||||
cfg.WatchedNamespaces = []string{ns}
|
||||
}
|
||||
}
|
||||
|
||||
// Leader election
|
||||
@@ -383,6 +400,20 @@ func ApplyFlags(cfg *config.Config) error {
|
||||
cfg.LeaderElection.RetryPeriod = 2 * time.Second
|
||||
}
|
||||
|
||||
// Namespace-selector and namespaces-to-ignore are only honored in global
|
||||
// mode; in scoped or single-namespace mode the watched set is already
|
||||
// explicit, so drop them and log where it happens.
|
||||
if !cfg.IsGlobalMode() {
|
||||
if len(cfg.NamespaceSelectors) > 0 {
|
||||
log.Info("namespace-selector is set but is only honored in global mode; ignoring it")
|
||||
cfg.NamespaceSelectors = nil
|
||||
cfg.NamespaceSelectorStrings = nil
|
||||
}
|
||||
if len(cfg.IgnoredNamespaces) > 0 {
|
||||
log.Info("namespaces-to-ignore is set but is only honored in global mode; ignoring it")
|
||||
cfg.IgnoredNamespaces = nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -414,3 +445,21 @@ func splitAndTrim(s string) []string {
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// trimAndDropEmptyStrings trims whitespace from each string in a slice and drops empty entries.
|
||||
func trimAndDropEmptyStrings(ss []string) []string {
|
||||
if len(ss) == 0 {
|
||||
return nil
|
||||
}
|
||||
result := make([]string, 0, len(ss))
|
||||
for _, s := range ss {
|
||||
s = strings.TrimSpace(s)
|
||||
if s != "" {
|
||||
result = append(result, s)
|
||||
}
|
||||
}
|
||||
if len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
@@ -67,7 +68,7 @@ func TestBindFlags(t *testing.T) {
|
||||
"ignore-annotation",
|
||||
"pause-deployment-annotation",
|
||||
"pause-deployment-time-annotation",
|
||||
"watch-namespace",
|
||||
"namespaces",
|
||||
"alert-on-reload",
|
||||
"alert-webhook-url",
|
||||
"alert-sink",
|
||||
@@ -94,7 +95,7 @@ func TestBindFlags_DefaultValues(t *testing.T) {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if err := ApplyFlags(cfg); err != nil {
|
||||
if err := ApplyFlags(cfg, logr.Discard()); err != nil {
|
||||
t.Fatalf("ApplyFlags() error = %v", err)
|
||||
}
|
||||
|
||||
@@ -128,7 +129,7 @@ func TestBindFlags_CustomValues(t *testing.T) {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if err := ApplyFlags(cfg); err != nil {
|
||||
if err := ApplyFlags(cfg, logr.Discard()); err != nil {
|
||||
t.Fatalf("ApplyFlags() error = %v", err)
|
||||
}
|
||||
|
||||
@@ -170,7 +171,7 @@ func TestApplyFlags_SecretProviderClassAnnotations(t *testing.T) {
|
||||
if err := fs.Parse(nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
if err := ApplyFlags(cfg); err != nil {
|
||||
if err := ApplyFlags(cfg, logr.Discard()); err != nil {
|
||||
t.Fatalf("ApplyFlags() error = %v", err)
|
||||
}
|
||||
defaults := config.DefaultAnnotations()
|
||||
@@ -197,7 +198,7 @@ func TestApplyFlags_SecretProviderClassAnnotations(t *testing.T) {
|
||||
if err := fs.Parse(args); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
if err := ApplyFlags(cfg); err != nil {
|
||||
if err := ApplyFlags(cfg, logr.Discard()); err != nil {
|
||||
t.Fatalf("ApplyFlags() error = %v", err)
|
||||
}
|
||||
if cfg.Annotations.SecretProviderClassAuto != "spc.example.com/auto" {
|
||||
@@ -220,7 +221,7 @@ func TestApplyFlags_ExcludeAnnotations(t *testing.T) {
|
||||
if err := fs.Parse(nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
if err := ApplyFlags(cfg); err != nil {
|
||||
if err := ApplyFlags(cfg, logr.Discard()); err != nil {
|
||||
t.Fatalf("ApplyFlags() error = %v", err)
|
||||
}
|
||||
defaults := config.DefaultAnnotations()
|
||||
@@ -243,7 +244,7 @@ func TestApplyFlags_ExcludeAnnotations(t *testing.T) {
|
||||
if err := fs.Parse(args); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
if err := ApplyFlags(cfg); err != nil {
|
||||
if err := ApplyFlags(cfg, logr.Discard()); err != nil {
|
||||
t.Fatalf("ApplyFlags() error = %v", err)
|
||||
}
|
||||
if cfg.Annotations.ConfigmapExclude != "cm.example.com/exclude" {
|
||||
@@ -263,7 +264,7 @@ func TestApplyFlags_IgnoreAnnotation(t *testing.T) {
|
||||
if err := fs.Parse(nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
if err := ApplyFlags(cfg); err != nil {
|
||||
if err := ApplyFlags(cfg, logr.Discard()); err != nil {
|
||||
t.Fatalf("ApplyFlags() error = %v", err)
|
||||
}
|
||||
if cfg.Annotations.Ignore != config.DefaultAnnotations().Ignore {
|
||||
@@ -278,7 +279,7 @@ func TestApplyFlags_IgnoreAnnotation(t *testing.T) {
|
||||
if err := fs.Parse([]string{"--ignore-annotation=my.company.com/reloader-ignore"}); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
if err := ApplyFlags(cfg); err != nil {
|
||||
if err := ApplyFlags(cfg, logr.Discard()); err != nil {
|
||||
t.Fatalf("ApplyFlags() error = %v", err)
|
||||
}
|
||||
if cfg.Annotations.Ignore != "my.company.com/reloader-ignore" {
|
||||
@@ -315,7 +316,7 @@ func TestApplyFlags_BooleanStrings(t *testing.T) {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
err := ApplyFlags(cfg)
|
||||
err := ApplyFlags(cfg, logr.Discard())
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ApplyFlags() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
@@ -345,7 +346,7 @@ func TestApplyFlags_CommaSeparatedLists(t *testing.T) {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if err := ApplyFlags(cfg); err != nil {
|
||||
if err := ApplyFlags(cfg, logr.Discard()); err != nil {
|
||||
t.Fatalf("ApplyFlags() error = %v", err)
|
||||
}
|
||||
|
||||
@@ -380,7 +381,7 @@ func TestApplyFlags_Selectors(t *testing.T) {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if err := ApplyFlags(cfg); err != nil {
|
||||
if err := ApplyFlags(cfg, logr.Discard()); err != nil {
|
||||
t.Fatalf("ApplyFlags() error = %v", err)
|
||||
}
|
||||
|
||||
@@ -411,7 +412,7 @@ func TestApplyFlags_InvalidSelector(t *testing.T) {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
err := ApplyFlags(cfg)
|
||||
err := ApplyFlags(cfg, logr.Discard())
|
||||
if err == nil {
|
||||
t.Error("ApplyFlags() should return error for invalid selector")
|
||||
}
|
||||
@@ -463,7 +464,7 @@ func TestApplyFlags_AlertingEnvVars(t *testing.T) {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if err := ApplyFlags(cfg); err != nil {
|
||||
if err := ApplyFlags(cfg, logr.Discard()); err != nil {
|
||||
t.Fatalf("ApplyFlags() error = %v", err)
|
||||
}
|
||||
|
||||
@@ -496,7 +497,7 @@ func TestApplyFlags_LegacyProxyEnvVar(t *testing.T) {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if err := ApplyFlags(cfg); err != nil {
|
||||
if err := ApplyFlags(cfg, logr.Discard()); err != nil {
|
||||
t.Fatalf("ApplyFlags() error = %v", err)
|
||||
}
|
||||
|
||||
@@ -513,7 +514,7 @@ func TestApplyFlagsCSIIntegration(t *testing.T) {
|
||||
if err := fs.Parse([]string{"--enable-csi-integration=true"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := ApplyFlags(cfg); err != nil {
|
||||
if err := ApplyFlags(cfg, logr.Discard()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !cfg.CSIIntegrationEnabled {
|
||||
@@ -584,3 +585,167 @@ func TestSplitAndTrim(t *testing.T) {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyFlags_NamespacesScoped(t *testing.T) {
|
||||
resetViper()
|
||||
cfg := NewDefault()
|
||||
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
||||
BindFlags(fs, cfg)
|
||||
|
||||
if err := fs.Parse([]string{"--namespaces=team-a,team-b"}); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
if err := ApplyFlags(cfg, logr.Discard()); err != nil {
|
||||
t.Fatalf("ApplyFlags() error = %v", err)
|
||||
}
|
||||
|
||||
if len(cfg.WatchedNamespaces) != 2 {
|
||||
t.Fatalf("WatchedNamespaces length = %d, want 2", len(cfg.WatchedNamespaces))
|
||||
}
|
||||
if cfg.WatchedNamespaces[0] != "team-a" || cfg.WatchedNamespaces[1] != "team-b" {
|
||||
t.Errorf("WatchedNamespaces = %v", cfg.WatchedNamespaces)
|
||||
}
|
||||
if cfg.IsGlobalMode() {
|
||||
t.Errorf("explicit namespaces should not be global mode")
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyFlags_NamespacesFromEnv(t *testing.T) {
|
||||
resetViper()
|
||||
t.Setenv("KUBERNETES_NAMESPACE", "single-ns")
|
||||
cfg := NewDefault()
|
||||
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
||||
BindFlags(fs, cfg)
|
||||
|
||||
if err := fs.Parse([]string{}); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
if err := ApplyFlags(cfg, logr.Discard()); err != nil {
|
||||
t.Fatalf("ApplyFlags() error = %v", err)
|
||||
}
|
||||
|
||||
if len(cfg.WatchedNamespaces) != 1 || cfg.WatchedNamespaces[0] != "single-ns" {
|
||||
t.Errorf("WatchedNamespaces = %v, want [single-ns]", cfg.WatchedNamespaces)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyFlags_NamespacesGlobal(t *testing.T) {
|
||||
resetViper()
|
||||
t.Setenv("KUBERNETES_NAMESPACE", "")
|
||||
cfg := NewDefault()
|
||||
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
||||
BindFlags(fs, cfg)
|
||||
|
||||
if err := fs.Parse([]string{}); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
if err := ApplyFlags(cfg, logr.Discard()); err != nil {
|
||||
t.Fatalf("ApplyFlags() error = %v", err)
|
||||
}
|
||||
|
||||
if len(cfg.WatchedNamespaces) != 0 {
|
||||
t.Errorf("WatchedNamespaces = %v, want empty (global)", cfg.WatchedNamespaces)
|
||||
}
|
||||
if !cfg.IsGlobalMode() {
|
||||
t.Errorf("no namespaces and no env should be global mode")
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyFlags_NamespacesTrimsEmptyEntries(t *testing.T) {
|
||||
resetViper()
|
||||
cfg := NewDefault()
|
||||
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
||||
BindFlags(fs, cfg)
|
||||
|
||||
if err := fs.Parse([]string{"--namespaces=team-a, ,team-b,"}); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
if err := ApplyFlags(cfg, logr.Discard()); err != nil {
|
||||
t.Fatalf("ApplyFlags() error = %v", err)
|
||||
}
|
||||
|
||||
if len(cfg.WatchedNamespaces) != 2 {
|
||||
t.Fatalf("WatchedNamespaces length = %d, want 2", len(cfg.WatchedNamespaces))
|
||||
}
|
||||
if cfg.WatchedNamespaces[0] != "team-a" || cfg.WatchedNamespaces[1] != "team-b" {
|
||||
t.Errorf("WatchedNamespaces = %v, want [team-a team-b]", cfg.WatchedNamespaces)
|
||||
}
|
||||
if cfg.IsGlobalMode() {
|
||||
t.Errorf("trimmed namespaces should not be global mode")
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyFlags_NamespacesAllEmptyIsGlobal(t *testing.T) {
|
||||
resetViper()
|
||||
t.Setenv("KUBERNETES_NAMESPACE", "")
|
||||
cfg := NewDefault()
|
||||
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
||||
BindFlags(fs, cfg)
|
||||
|
||||
if err := fs.Parse([]string{"--namespaces=, ,"}); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
if err := ApplyFlags(cfg, logr.Discard()); err != nil {
|
||||
t.Fatalf("ApplyFlags() error = %v", err)
|
||||
}
|
||||
|
||||
if len(cfg.WatchedNamespaces) != 0 {
|
||||
t.Errorf("WatchedNamespaces = %v, want empty (global)", cfg.WatchedNamespaces)
|
||||
}
|
||||
if !cfg.IsGlobalMode() {
|
||||
t.Errorf("all-empty namespaces should be global mode")
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyFlags must finalize a self-consistent config: in scoped mode it enforces
|
||||
// namespace-scope semantics (clears selector/ignore lists) and logs a warning
|
||||
// for each dropped setting.
|
||||
func TestApplyFlags_ScopedClearsSelectorsAndIgnores(t *testing.T) {
|
||||
resetViper()
|
||||
cfg := NewDefault()
|
||||
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
||||
BindFlags(fs, cfg)
|
||||
|
||||
if err := fs.Parse([]string{
|
||||
"--namespaces=team-a",
|
||||
"--namespace-selector=env=prod",
|
||||
"--namespaces-to-ignore=kube-system",
|
||||
}); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
if err := ApplyFlags(cfg, logr.Discard()); err != nil {
|
||||
t.Fatalf("ApplyFlags() error = %v", err)
|
||||
}
|
||||
|
||||
if len(cfg.NamespaceSelectors) != 0 || len(cfg.NamespaceSelectorStrings) != 0 {
|
||||
t.Errorf("scoped mode should clear namespace selectors, got %v", cfg.NamespaceSelectorStrings)
|
||||
}
|
||||
if len(cfg.IgnoredNamespaces) != 0 {
|
||||
t.Errorf("scoped mode should clear ignored namespaces, got %v", cfg.IgnoredNamespaces)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyFlags_GlobalKeepsSelectorsNoWarnings(t *testing.T) {
|
||||
resetViper()
|
||||
t.Setenv("KUBERNETES_NAMESPACE", "")
|
||||
cfg := NewDefault()
|
||||
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
||||
BindFlags(fs, cfg)
|
||||
|
||||
if err := fs.Parse([]string{
|
||||
"--namespace-selector=env=prod",
|
||||
"--namespaces-to-ignore=kube-system",
|
||||
}); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
if err := ApplyFlags(cfg, logr.Discard()); err != nil {
|
||||
t.Fatalf("ApplyFlags() error = %v", err)
|
||||
}
|
||||
|
||||
if !cfg.IsGlobalMode() {
|
||||
t.Fatalf("no --namespaces should be global mode")
|
||||
}
|
||||
if len(cfg.NamespaceSelectors) != 1 || len(cfg.IgnoredNamespaces) != 1 {
|
||||
t.Errorf("global mode should keep selectors and ignored namespaces")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,20 @@ func AddOptionalSchemes(argoRolloutsEnabled, deploymentConfigEnabled, csiEnabled
|
||||
}
|
||||
}
|
||||
|
||||
// buildDefaultNamespaces returns the controller-runtime cache namespace scoping
|
||||
// for the given watched namespaces. An empty input yields nil, meaning the cache
|
||||
// watches all namespaces.
|
||||
func buildDefaultNamespaces(namespaces []string) map[string]cache.Config {
|
||||
if len(namespaces) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make(map[string]cache.Config, len(namespaces))
|
||||
for _, ns := range namespaces {
|
||||
out[ns] = cache.Config{}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ManagerOptions contains options for creating a new Manager.
|
||||
type ManagerOptions struct {
|
||||
Config *config.Config
|
||||
@@ -52,9 +66,18 @@ type ManagerOptions struct {
|
||||
Collectors *metrics.Collectors
|
||||
}
|
||||
|
||||
// NewManager creates a new controller-runtime manager with the given options.
|
||||
// This follows controller-runtime and operator-sdk conventions for leader election.
|
||||
// NewManager creates a new controller-runtime manager using the ambient cluster
|
||||
// configuration (in-cluster or kubeconfig). It follows controller-runtime and
|
||||
// operator-sdk conventions for leader election.
|
||||
func NewManager(opts ManagerOptions) (ctrl.Manager, error) {
|
||||
return NewManagerWithRestConfig(opts, ctrl.GetConfigOrDie())
|
||||
}
|
||||
|
||||
// NewManagerWithRestConfig creates a new controller-runtime manager with the
|
||||
// given rest.Config. NewManager delegates here; tests can call it directly with
|
||||
// a pre-existing cluster configuration (set cfg.MetricsAddr/HealthAddr to "0" to
|
||||
// disable those servers and avoid port conflicts).
|
||||
func NewManagerWithRestConfig(opts ManagerOptions, restConfig *rest.Config) (ctrl.Manager, error) {
|
||||
cfg := opts.Config
|
||||
le := cfg.LeaderElection
|
||||
|
||||
@@ -79,16 +102,14 @@ func NewManager(opts ManagerOptions) (ctrl.Manager, error) {
|
||||
RetryPeriod: &le.RetryPeriod,
|
||||
}
|
||||
|
||||
if cfg.WatchedNamespace != "" {
|
||||
if nsScope := buildDefaultNamespaces(cfg.WatchedNamespaces); nsScope != nil {
|
||||
mgrOpts.Cache = cache.Options{
|
||||
DefaultNamespaces: map[string]cache.Config{
|
||||
cfg.WatchedNamespace: {},
|
||||
},
|
||||
DefaultNamespaces: nsScope,
|
||||
}
|
||||
opts.Log.Info("namespace filtering enabled", "namespace", cfg.WatchedNamespace)
|
||||
opts.Log.Info("namespace filtering enabled", "namespaces", cfg.WatchedNamespaces)
|
||||
}
|
||||
|
||||
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), mgrOpts)
|
||||
mgr, err := ctrl.NewManager(restConfig, mgrOpts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating manager: %w", err)
|
||||
}
|
||||
@@ -107,45 +128,6 @@ func NewManager(opts ManagerOptions) (ctrl.Manager, error) {
|
||||
return mgr, nil
|
||||
}
|
||||
|
||||
// NewManagerWithRestConfig creates a new controller-runtime manager with the given rest.Config.
|
||||
// This is useful for testing where you have a pre-existing cluster configuration.
|
||||
func NewManagerWithRestConfig(opts ManagerOptions, restConfig *rest.Config) (ctrl.Manager, error) {
|
||||
cfg := opts.Config
|
||||
le := cfg.LeaderElection
|
||||
|
||||
mgrOpts := ctrl.Options{
|
||||
Scheme: runtimeScheme,
|
||||
Metrics: ctrlmetrics.Options{
|
||||
BindAddress: "0", // Disable metrics server in tests
|
||||
},
|
||||
HealthProbeBindAddress: "0", // Disable health probes in tests
|
||||
|
||||
// Leader election configuration
|
||||
LeaderElection: cfg.EnableHA,
|
||||
LeaderElectionID: le.LockName,
|
||||
LeaderElectionNamespace: le.Namespace,
|
||||
LeaderElectionReleaseOnCancel: le.ReleaseOnCancel,
|
||||
LeaseDuration: &le.LeaseDuration,
|
||||
RenewDeadline: &le.RenewDeadline,
|
||||
RetryPeriod: &le.RetryPeriod,
|
||||
}
|
||||
|
||||
if cfg.WatchedNamespace != "" {
|
||||
mgrOpts.Cache = cache.Options{
|
||||
DefaultNamespaces: map[string]cache.Config{
|
||||
cfg.WatchedNamespace: {},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
mgr, err := ctrl.NewManager(restConfig, mgrOpts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating manager: %w", err)
|
||||
}
|
||||
|
||||
return mgr, nil
|
||||
}
|
||||
|
||||
// SetupReconcilers sets up all reconcilers with the manager.
|
||||
func SetupReconcilers(mgr ctrl.Manager, cfg *config.Config, log logr.Logger, collectors *metrics.Collectors) error {
|
||||
registry := workload.NewRegistry(
|
||||
|
||||
@@ -9,6 +9,25 @@ import (
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
)
|
||||
|
||||
func TestBuildDefaultNamespaces(t *testing.T) {
|
||||
if got := buildDefaultNamespaces(nil); got != nil {
|
||||
t.Errorf("empty input should return nil, got %v", got)
|
||||
}
|
||||
if got := buildDefaultNamespaces([]string{}); got != nil {
|
||||
t.Errorf("empty slice should return nil, got %v", got)
|
||||
}
|
||||
|
||||
got := buildDefaultNamespaces([]string{"team-a", "team-b", "team-c"})
|
||||
if len(got) != 3 {
|
||||
t.Fatalf("expected 3 entries, got %d", len(got))
|
||||
}
|
||||
for _, ns := range []string{"team-a", "team-b", "team-c"} {
|
||||
if _, ok := got[ns]; !ok {
|
||||
t.Errorf("missing namespace %q in %v", ns, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddOptionalSchemesRegistersCSI(t *testing.T) {
|
||||
// Reset to a clean scheme for the test.
|
||||
runtimeScheme = runtime.NewScheme()
|
||||
|
||||
+12
-6
@@ -54,10 +54,10 @@ type Config struct {
|
||||
EnablePProf bool `json:"enablePProf"`
|
||||
PProfAddr string `json:"pprofAddr,omitempty"`
|
||||
|
||||
Alerting AlertingConfig `json:"alerting"`
|
||||
LeaderElection LeaderElectionConfig `json:"leaderElection"`
|
||||
WatchedNamespace string `json:"watchedNamespace,omitempty"`
|
||||
SyncPeriod time.Duration `json:"syncPeriod"`
|
||||
Alerting AlertingConfig `json:"alerting"`
|
||||
LeaderElection LeaderElectionConfig `json:"leaderElection"`
|
||||
WatchedNamespaces []string `json:"watchedNamespaces,omitempty"`
|
||||
SyncPeriod time.Duration `json:"syncPeriod"`
|
||||
}
|
||||
|
||||
// AnnotationConfig holds customizable annotation keys.
|
||||
@@ -137,8 +137,8 @@ func NewDefault() *Config {
|
||||
RetryPeriod: 2 * time.Second,
|
||||
ReleaseOnCancel: true,
|
||||
},
|
||||
WatchedNamespace: "",
|
||||
SyncPeriod: 0,
|
||||
WatchedNamespaces: []string{},
|
||||
SyncPeriod: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,3 +195,9 @@ func (c *Config) IsNamespaceIgnored(namespace string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsGlobalMode reports whether Reloader watches all namespaces. Global mode is
|
||||
// the absence of an explicit watched-namespace list.
|
||||
func (c *Config) IsGlobalMode() bool {
|
||||
return len(c.WatchedNamespaces) == 0
|
||||
}
|
||||
|
||||
@@ -218,3 +218,14 @@ func TestConfig_IsNamespaceIgnored(t *testing.T) {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsGlobalMode(t *testing.T) {
|
||||
c := &Config{WatchedNamespaces: nil}
|
||||
if !c.IsGlobalMode() {
|
||||
t.Errorf("empty WatchedNamespaces should be global mode")
|
||||
}
|
||||
c.WatchedNamespaces = []string{"team-a"}
|
||||
if c.IsGlobalMode() {
|
||||
t.Errorf("non-empty WatchedNamespaces should not be global mode")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
apivalidation "k8s.io/apimachinery/pkg/util/validation"
|
||||
|
||||
"github.com/stakater/Reloader/internal/pkg/workload"
|
||||
)
|
||||
@@ -97,6 +98,19 @@ func (c *Config) Validate() error {
|
||||
)
|
||||
}
|
||||
|
||||
// Watched namespaces must be valid DNS-1123 labels; an invalid entry would
|
||||
// otherwise fail deep inside the controller-runtime cache with an opaque error.
|
||||
for _, ns := range c.WatchedNamespaces {
|
||||
if msgs := apivalidation.IsDNS1123Label(ns); len(msgs) > 0 {
|
||||
errs = append(
|
||||
errs, ValidationError{
|
||||
Field: "WatchedNamespaces",
|
||||
Message: fmt.Sprintf("invalid namespace %q: %s", ns, strings.Join(msgs, "; ")),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
c.IgnoredResources = normalizeToLower(c.IgnoredResources)
|
||||
|
||||
// Normalize ignored workloads to canonical Kind values (e.g., "cronjobs" -> "CronJob")
|
||||
|
||||
@@ -194,6 +194,40 @@ func TestConfig_Validate_InvalidIgnoredWorkload(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_Validate_WatchedNamespaces(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
namespaces []string
|
||||
wantErr bool
|
||||
}{
|
||||
{"nil is valid (global mode)", nil, false},
|
||||
{"empty is valid (global mode)", []string{}, false},
|
||||
{"single valid label", []string{"team-a"}, false},
|
||||
{"multiple valid labels", []string{"team-a", "team-b", "kube-system"}, false},
|
||||
{"uppercase is invalid", []string{"Team-A"}, true},
|
||||
{"underscore is invalid", []string{"team_a"}, true},
|
||||
{"trailing dash is invalid", []string{"team-"}, true},
|
||||
{"one invalid among valid", []string{"team-a", "Bad_NS!"}, true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(
|
||||
tt.name, func(t *testing.T) {
|
||||
cfg := NewDefault()
|
||||
cfg.WatchedNamespaces = tt.namespaces
|
||||
|
||||
err := cfg.Validate()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Fatalf("Validate() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
if tt.wantErr && !strings.Contains(err.Error(), "WatchedNamespaces") {
|
||||
t.Errorf("error should mention WatchedNamespaces, got: %v", err)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_Validate_MultipleErrors(t *testing.T) {
|
||||
cfg := NewDefault()
|
||||
cfg.ReloadStrategy = "invalid"
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"github.com/go-logr/logr"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/stakater/Reloader/internal/pkg/workload"
|
||||
@@ -77,10 +79,21 @@ func PublishMetaInfoConfigMap(ctx context.Context, c client.Client, cfg *config.
|
||||
return publisher.Publish(ctx)
|
||||
}
|
||||
|
||||
// Runnable returns a controller-runtime Runnable that publishes the metadata ConfigMap
|
||||
// when the manager starts. This ensures the cache is ready before accessing the API.
|
||||
func Runnable(c client.Client, cfg *config.Config, log logr.Logger) RunnableFunc {
|
||||
// Runnable returns a controller-runtime Runnable that publishes the meta-info
|
||||
// ConfigMap when the manager starts. It builds its own uncached client from the
|
||||
// given rest config and scheme: the ConfigMap lives in Reloader's own namespace,
|
||||
// which the manager cache does not cover in scoped mode, so a cache-backed client
|
||||
// cannot read or write it there. Meta-info is internal instance metadata and is
|
||||
// always published regardless of which resources are watched.
|
||||
func Runnable(restConfig *rest.Config, scheme *runtime.Scheme, cfg *config.Config, log logr.Logger) RunnableFunc {
|
||||
return func(ctx context.Context) error {
|
||||
c, err := client.New(restConfig, client.Options{Scheme: scheme})
|
||||
if err != nil {
|
||||
log.Error(err, "Failed to create client for meta info configmap publisher")
|
||||
// Non-fatal, don't return error to avoid crashing the manager
|
||||
<-ctx.Done()
|
||||
return nil
|
||||
}
|
||||
if err := PublishMetaInfoConfigMap(ctx, c, cfg, log); err != nil {
|
||||
log.Error(err, "Failed to create metadata ConfigMap")
|
||||
// Non-fatal, don't return error to avoid crashing the manager
|
||||
|
||||
@@ -47,13 +47,18 @@ var _ = Describe("Multi-Container Tests", Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, deploymentName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"shared-key": "updated"})
|
||||
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 multiple containers should be reloaded")
|
||||
})
|
||||
@@ -81,13 +86,18 @@ var _ = Describe("Multi-Container Tests", Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, deploymentName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the first ConfigMap")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key1": "updated1"})
|
||||
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 be reloaded when first container's ConfigMap changes")
|
||||
})
|
||||
|
||||
@@ -53,13 +53,18 @@ var _ = Describe("Regex Pattern Tests", func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, deploymentName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the matching ConfigMap")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, matchingCM, map[string]string{"key": "updated"})
|
||||
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 be reloaded when matching ConfigMap changes")
|
||||
})
|
||||
@@ -87,14 +92,19 @@ var _ = Describe("Regex Pattern Tests", func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, deploymentName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the non-matching ConfigMap")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, nonMatchingCM, map[string]string{"other": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (pattern mismatch)")
|
||||
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 non-matching ConfigMap changes")
|
||||
})
|
||||
@@ -120,13 +130,18 @@ var _ = Describe("Regex Pattern Tests", func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, deploymentName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the matching Secret")
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, matchingSecret, map[string]string{"password": "updated"})
|
||||
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 be reloaded when matching Secret changes")
|
||||
})
|
||||
|
||||
@@ -47,12 +47,16 @@ var _ = Describe("Auto Reload Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap data")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
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")
|
||||
})
|
||||
@@ -75,12 +79,16 @@ var _ = Describe("Auto Reload Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Secret data")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName, map[string]string{"password": "updated"})
|
||||
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 Secret change")
|
||||
})
|
||||
@@ -108,12 +116,16 @@ var _ = Describe("Auto Reload Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"config": "updated"})
|
||||
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 ConfigMap change")
|
||||
})
|
||||
@@ -145,12 +157,16 @@ var _ = Describe("Auto Reload Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"config": "updated"})
|
||||
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 have been reloaded for ConfigMap change")
|
||||
})
|
||||
@@ -180,12 +196,16 @@ var _ = Describe("Auto Reload Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Secret")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName, map[string]string{"secret": "updated"})
|
||||
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 have been reloaded for Secret change")
|
||||
})
|
||||
@@ -221,12 +241,16 @@ var _ = Describe("Auto Reload Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the second ConfigMap (auto-detected)")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName2, map[string]string{"key2": "updated"})
|
||||
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 have been reloaded for auto-detected ConfigMap change")
|
||||
})
|
||||
|
||||
@@ -61,12 +61,16 @@ var _ = Describe("Combination Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the auto-detected ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
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 auto-detected ConfigMap changes")
|
||||
})
|
||||
@@ -95,12 +99,16 @@ var _ = Describe("Combination Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the explicitly listed ConfigMap (not mounted)")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName2, map[string]string{"extra": "updated"})
|
||||
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 explicitly listed ConfigMap changes")
|
||||
})
|
||||
@@ -129,12 +137,16 @@ var _ = Describe("Combination Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the explicitly listed Secret")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName2, map[string]string{"api-key": "updated"})
|
||||
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 explicitly listed Secret changes")
|
||||
})
|
||||
@@ -166,13 +178,17 @@ var _ = Describe("Combination Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the excluded ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName2, map[string]string{"excluded": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (negative test)")
|
||||
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 ConfigMap changes")
|
||||
})
|
||||
@@ -202,12 +218,16 @@ var _ = Describe("Combination Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the non-excluded ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
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 ConfigMap changes")
|
||||
})
|
||||
@@ -237,13 +257,17 @@ var _ = Describe("Combination Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the excluded Secret")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName2, map[string]string{"excluded": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (negative test)")
|
||||
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 Secret changes")
|
||||
})
|
||||
@@ -270,12 +294,16 @@ var _ = Describe("Combination Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the second ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName2, map[string]string{"key2": "updated"})
|
||||
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 any of the listed ConfigMaps changes")
|
||||
})
|
||||
@@ -300,12 +328,16 @@ var _ = Describe("Combination Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the first Secret")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName, map[string]string{"key1": "updated"})
|
||||
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 any of the listed Secrets changes")
|
||||
})
|
||||
@@ -333,12 +365,16 @@ var _ = Describe("Combination Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Secret")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName, map[string]string{"password": "updated"})
|
||||
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 Secret changes with both annotations present")
|
||||
})
|
||||
|
||||
@@ -66,13 +66,17 @@ var _ = Describe("Exclude Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the excluded ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (excluded ConfigMap)")
|
||||
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 ConfigMap changes")
|
||||
})
|
||||
@@ -103,12 +107,16 @@ var _ = Describe("Exclude Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the non-excluded ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName2, map[string]string{"key2": "updated2"})
|
||||
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 ConfigMap changes")
|
||||
})
|
||||
@@ -141,13 +149,17 @@ var _ = Describe("Exclude Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the excluded Secret")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName, map[string]string{"password": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (excluded Secret)")
|
||||
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 Secret changes")
|
||||
})
|
||||
@@ -178,12 +190,16 @@ var _ = Describe("Exclude Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the non-excluded Secret")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName2, map[string]string{"password2": "updated2"})
|
||||
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 Secret changes")
|
||||
})
|
||||
@@ -225,13 +241,17 @@ var _ = Describe("Exclude Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the excluded ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying workload was NOT reloaded (excluded ConfigMap)")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := adapter.WaitReloaded(ctx, testNamespace, workloadName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ShortTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, testNamespace, workloadName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "%s should NOT reload with exclude on pod template", workloadType)
|
||||
},
|
||||
|
||||
@@ -49,12 +49,16 @@ var _ = Describe("Pause Period Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap data")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
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 have been reloaded")
|
||||
|
||||
@@ -83,12 +87,16 @@ var _ = Describe("Pause Period Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap data")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
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 have been reloaded")
|
||||
|
||||
@@ -124,12 +132,16 @@ var _ = Describe("Pause Period Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap data")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
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 have been reloaded")
|
||||
|
||||
|
||||
@@ -50,13 +50,17 @@ var _ = Describe("Resource Ignore Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap data")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (negative test)")
|
||||
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 ConfigMap has ignore=true")
|
||||
})
|
||||
@@ -80,13 +84,17 @@ var _ = Describe("Resource Ignore Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Secret data")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName, map[string]string{"password": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (negative test)")
|
||||
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 Secret has ignore=true")
|
||||
})
|
||||
|
||||
@@ -50,12 +50,16 @@ var _ = Describe("Search and Match Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap data")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
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 search annotation should reload when ConfigMap has match annotation")
|
||||
})
|
||||
@@ -78,13 +82,17 @@ var _ = Describe("Search and Match Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap data")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (negative test)")
|
||||
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 ConfigMap lacks match annotation")
|
||||
})
|
||||
@@ -106,13 +114,17 @@ var _ = Describe("Search and Match Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap data")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (negative test)")
|
||||
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 without search annotation should NOT reload even when ConfigMap has match")
|
||||
})
|
||||
@@ -149,18 +161,24 @@ var _ = Describe("Search and Match Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap data")
|
||||
// Capture the reload-annotation baselines before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload1, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
priorReload2, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName2, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for first Deployment to be reloaded")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload1, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment with search annotation should reload")
|
||||
|
||||
By("Verifying second Deployment was NOT reloaded")
|
||||
reloaded2, err := adapter.WaitReloaded(ctx, testNamespace, deploymentName2,
|
||||
utils.AnnotationLastReloadedFrom, utils.ShortTimeout)
|
||||
reloaded2, err := adapter.WaitReloadedFrom(ctx, testNamespace, deploymentName2,
|
||||
utils.AnnotationLastReloadedFrom, priorReload2, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded2).To(BeFalse(), "Deployment without search annotation should NOT reload")
|
||||
})
|
||||
@@ -196,12 +214,16 @@ var _ = Describe("Search and Match Annotation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
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 search annotation on pod template", workloadType)
|
||||
},
|
||||
|
||||
@@ -47,13 +47,18 @@ var _ = Describe("Argo Rollout Strategy Tests", func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, rolloutName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, rolloutName, utils.AnnotationLastReloadedFrom)
|
||||
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 Rollout to be reloaded with annotation")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, testNamespace, rolloutName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, testNamespace, rolloutName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Argo Rollout should be reloaded with default rollout strategy")
|
||||
})
|
||||
|
||||
@@ -58,13 +58,17 @@ var _ = Describe("Reference Method Tests", func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"config_key": "updated_value"})
|
||||
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 with valueFrom.configMapKeyRef should reload", workloadType)
|
||||
},
|
||||
@@ -107,13 +111,17 @@ var _ = Describe("Reference Method Tests", func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Secret")
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName, map[string]string{"secret_key": "updated_secret"})
|
||||
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 with valueFrom.secretKeyRef should reload", workloadType)
|
||||
},
|
||||
@@ -154,13 +162,17 @@ var _ = Describe("Reference Method Tests", func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"config.yaml": "updated"})
|
||||
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 with projected ConfigMap volume should reload", workloadType)
|
||||
},
|
||||
@@ -196,13 +208,17 @@ var _ = Describe("Reference Method Tests", func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Secret")
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName, map[string]string{"credentials": "updated"})
|
||||
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 with projected Secret volume should reload", workloadType)
|
||||
},
|
||||
@@ -246,13 +262,17 @@ var _ = Describe("Reference Method Tests", func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"config.yaml": "updated"})
|
||||
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 when ConfigMap in mixed projected volume changes", workloadType)
|
||||
},
|
||||
@@ -296,13 +316,17 @@ var _ = Describe("Reference Method Tests", func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Secret")
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName, map[string]string{"credentials": "updated"})
|
||||
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 when Secret in mixed projected volume changes", workloadType)
|
||||
},
|
||||
@@ -343,13 +367,17 @@ var _ = Describe("Reference Method Tests", func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"INIT_VAR": "updated"})
|
||||
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 with init container ConfigMap should reload", workloadType)
|
||||
},
|
||||
@@ -385,13 +413,17 @@ var _ = Describe("Reference Method Tests", func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Secret")
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName, map[string]string{"INIT_SECRET": "updated"})
|
||||
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 with init container Secret should reload", workloadType)
|
||||
},
|
||||
@@ -429,13 +461,17 @@ var _ = Describe("Reference Method Tests", func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"config.yaml": "updated"})
|
||||
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 with init container ConfigMap volume should reload", workloadType)
|
||||
},
|
||||
@@ -471,13 +507,17 @@ var _ = Describe("Reference Method Tests", func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Secret")
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName, map[string]string{"credentials": "updated"})
|
||||
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 with init container Secret volume should reload", workloadType)
|
||||
},
|
||||
@@ -520,13 +560,17 @@ var _ = Describe("Reference Method Tests", func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"auto_config_key": "updated"})
|
||||
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 with auto=true and valueFrom should reload", workloadType)
|
||||
},
|
||||
|
||||
+115
-44
@@ -64,13 +64,17 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap data")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
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 have been reloaded", workloadType)
|
||||
},
|
||||
@@ -106,13 +110,17 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Secret data")
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName, map[string]string{"password": "updated"})
|
||||
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 have been reloaded", workloadType)
|
||||
},
|
||||
@@ -149,13 +157,17 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap data")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
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 with auto=true should have been reloaded", workloadType)
|
||||
},
|
||||
@@ -192,14 +204,18 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating only the ConfigMap labels (no data change)")
|
||||
err = utils.UpdateConfigMapLabels(ctx, kubeClient, testNamespace, configMapName, map[string]string{"new-label": "new-value"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying workload was NOT reloaded (negative test)")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := adapter.WaitReloaded(ctx, testNamespace, workloadName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ShortTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom, priorReload, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "%s should NOT reload when only ConfigMap labels change", workloadType)
|
||||
},
|
||||
@@ -235,14 +251,18 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating only the Secret labels (no data change)")
|
||||
err = utils.UpdateSecretLabels(ctx, kubeClient, testNamespace, secretName, map[string]string{"new-label": "new-value"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying workload was NOT reloaded (negative test)")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := adapter.WaitReloaded(ctx, testNamespace, workloadName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ShortTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom, priorReload, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "%s should NOT reload when only Secret labels change", workloadType)
|
||||
},
|
||||
@@ -369,13 +389,17 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap data")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"config.yaml": "setting: updated"})
|
||||
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 with volume-mounted ConfigMap should have been reloaded", workloadType)
|
||||
},
|
||||
@@ -410,13 +434,17 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Secret data")
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName, map[string]string{"credentials.yaml": "secret: updated"})
|
||||
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 with volume-mounted Secret should have been reloaded", workloadType)
|
||||
},
|
||||
@@ -451,14 +479,18 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap data")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying workload is NOT reloaded (negative test)")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := adapter.WaitReloaded(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom,
|
||||
utils.ShortTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom, priorReload, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "%s without Reloader annotation should NOT be reloaded", workloadType)
|
||||
},
|
||||
@@ -507,13 +539,19 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger: Reloader can react
|
||||
// to the ConfigMap update before WaitReloaded captures its baseline, which would
|
||||
// otherwise record the already-reloaded value and time out (TOCTOU race).
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the second ConfigMap")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName2, map[string]string{"key2": "updated-value2"})
|
||||
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 when second ConfigMap changes", workloadType)
|
||||
},
|
||||
@@ -556,13 +594,18 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the TOCTOU
|
||||
// race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the second Secret")
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName2, map[string]string{"key2": "updated-value2"})
|
||||
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 when second Secret changes", workloadType)
|
||||
},
|
||||
@@ -598,13 +641,17 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("First update to ConfigMap")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "v2"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for first reload")
|
||||
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())
|
||||
|
||||
@@ -666,13 +713,17 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Secret")
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName, map[string]string{"secret": "updated"})
|
||||
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 when Secret changes", workloadType)
|
||||
},
|
||||
@@ -708,14 +759,18 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap data")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying workload is NOT reloaded (auto=false)")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := adapter.WaitReloaded(ctx, testNamespace, workloadName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ShortTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom, priorReload, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "%s with auto=false should NOT be reloaded", workloadType)
|
||||
},
|
||||
@@ -759,13 +814,17 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
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 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 pod template annotation", workloadType)
|
||||
},
|
||||
@@ -801,13 +860,17 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Secret")
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName, map[string]string{"password": "updated"})
|
||||
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 pod template annotation", workloadType)
|
||||
},
|
||||
@@ -843,13 +906,17 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
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 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 with auto=true on pod template should reload", workloadType)
|
||||
},
|
||||
@@ -886,13 +953,17 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
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 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 annotations on both locations", workloadType)
|
||||
},
|
||||
@@ -932,14 +1003,18 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Secret (not the ConfigMap)")
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName, map[string]string{"password": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying workload was NOT reloaded (negative test)")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := adapter.WaitReloaded(ctx, testNamespace, workloadName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ShortTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, testNamespace, workloadName, utils.AnnotationLastReloadedFrom, priorReload, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "%s should NOT reload when updating different resource than annotated", workloadType)
|
||||
},
|
||||
@@ -1016,8 +1091,7 @@ 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)
|
||||
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 ConfigMap change", workloadType)
|
||||
},
|
||||
@@ -1061,8 +1135,7 @@ 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)
|
||||
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 Secret change", workloadType)
|
||||
},
|
||||
@@ -1109,8 +1182,7 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
|
||||
By("Verifying workload does NOT have STAKATER_ env var")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
found, err := adapter.WaitEnvVar(ctx, testNamespace, workloadName, utils.StakaterEnvVarPrefix,
|
||||
utils.ShortTimeout)
|
||||
found, err := adapter.WaitEnvVarFrom(ctx, testNamespace, workloadName, utils.StakaterEnvVarPrefix, "", utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(found).To(BeFalse(), "%s should NOT have STAKATER_ env var for label-only change", workloadType)
|
||||
},
|
||||
@@ -1154,8 +1226,7 @@ var _ = Describe("Workload Reload Tests", Serial, func() {
|
||||
|
||||
By("Verifying workload does NOT have STAKATER_ env var")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
found, err := adapter.WaitEnvVar(ctx, testNamespace, workloadName, utils.StakaterEnvVarPrefix,
|
||||
utils.ShortTimeout)
|
||||
found, err := adapter.WaitEnvVarFrom(ctx, testNamespace, workloadName, utils.StakaterEnvVarPrefix, "", utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(found).To(BeFalse(), "%s should NOT have STAKATER_ env var for label-only change", workloadType)
|
||||
},
|
||||
|
||||
+34
-10
@@ -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())
|
||||
@@ -207,6 +216,11 @@ var _ = Describe("CSI SecretProviderClass Tests", Label("csi"), Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, deploymentName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap (should NOT trigger reload)")
|
||||
err = utils.UpdateConfigMap(
|
||||
ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
@@ -214,9 +228,9 @@ var _ = Describe("CSI SecretProviderClass Tests", Label("csi"), Serial, func() {
|
||||
|
||||
By("Verifying Deployment was NOT reloaded for ConfigMap change")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := adapter.WaitReloaded(
|
||||
reloaded, err := adapter.WaitReloadedFrom(
|
||||
ctx, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ShortTimeout,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ShortTimeout,
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "SPC auto annotation should not trigger reload for ConfigMap changes")
|
||||
@@ -230,6 +244,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 +259,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")
|
||||
@@ -281,15 +300,20 @@ var _ = Describe("CSI SecretProviderClass Tests", Label("csi"), Serial, func() {
|
||||
err = adapter.WaitReady(ctx, testNamespace, deploymentName, utils.WorkloadReadyTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap (should trigger reload with auto=true)")
|
||||
err = utils.UpdateConfigMap(
|
||||
ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment WAS reloaded for ConfigMap 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(), "Combined auto=true should trigger reload for ConfigMap changes")
|
||||
|
||||
@@ -65,12 +65,16 @@ var _ = Describe("Auto Reload All Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, autoNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, autoNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded (autoReloadAll=true)")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, autoNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, autoNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment without annotations should reload when autoReloadAll=true")
|
||||
})
|
||||
@@ -93,13 +97,17 @@ var _ = Describe("Auto Reload All Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, autoNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, autoNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (auto=false overrides autoReloadAll)")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := adapter.WaitReloaded(ctx, autoNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ShortTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, autoNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "Deployment with auto=false should NOT reload even with autoReloadAll=true")
|
||||
})
|
||||
|
||||
@@ -69,13 +69,17 @@ var _ = Describe("Ignore Resources Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Secret")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, ignoreNS, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, ignoreNS, secretName, map[string]string{"password": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (ignoreSecrets=true)")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := adapter.WaitReloaded(ctx, ignoreNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ShortTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, ignoreNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "Deployment should NOT reload when ignoreSecrets=true")
|
||||
})
|
||||
@@ -98,12 +102,16 @@ var _ = Describe("Ignore Resources Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, ignoreNS, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, ignoreNS, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded (ConfigMap should still work)")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, ignoreNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, ignoreNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "ConfigMap changes should still trigger reload with ignoreSecrets=true")
|
||||
})
|
||||
@@ -146,13 +154,17 @@ var _ = Describe("Ignore Resources Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, ignoreNS, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, ignoreNS, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (ignoreConfigMaps=true)")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := adapter.WaitReloaded(ctx, ignoreNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ShortTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, ignoreNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "Deployment should NOT reload when ignoreConfigMaps=true")
|
||||
})
|
||||
@@ -175,12 +187,16 @@ var _ = Describe("Ignore Resources Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Secret")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, ignoreNS, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, ignoreNS, secretName, map[string]string{"password": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded (Secret should still work)")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, ignoreNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, ignoreNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Secret changes should still trigger reload with ignoreConfigMaps=true")
|
||||
})
|
||||
|
||||
@@ -64,13 +64,17 @@ var _ = Describe("Ignored Workloads Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := cronJobAdapter.GetPodTemplateAnnotation(ctx, ignoreNS, cronJobName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, ignoreNS, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying CronJob was NOT reloaded (ignoreCronJobs=true)")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := cronJobAdapter.WaitReloaded(ctx, ignoreNS, cronJobName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ShortTimeout)
|
||||
reloaded, err := cronJobAdapter.WaitReloadedFrom(ctx, ignoreNS, cronJobName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "CronJob should NOT reload when ignoreCronJobs=true")
|
||||
})
|
||||
@@ -98,12 +102,16 @@ var _ = Describe("Ignored Workloads Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := deploymentAdapter.GetPodTemplateAnnotation(ctx, ignoreNS, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, ignoreNS, configMapName, map[string]string{"key": "updated-deploy"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded (Deployment should still work)")
|
||||
reloaded, err := deploymentAdapter.WaitReloaded(ctx, ignoreNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err := deploymentAdapter.WaitReloadedFrom(ctx, ignoreNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment should still reload with ignoreCronJobs=true")
|
||||
})
|
||||
@@ -143,13 +151,17 @@ var _ = Describe("Ignored Workloads Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := cronJobAdapter.GetPodTemplateAnnotation(ctx, ignoreNS, cronJobName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, ignoreNS, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying CronJob was NOT reloaded")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := cronJobAdapter.WaitReloaded(ctx, ignoreNS, cronJobName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ShortTimeout)
|
||||
reloaded, err := cronJobAdapter.WaitReloadedFrom(ctx, ignoreNS, cronJobName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "CronJob should NOT reload when ignoreCronJobs=true and ignoreJobs=true")
|
||||
})
|
||||
|
||||
@@ -73,13 +73,17 @@ var _ = Describe("Namespace Ignore Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, ignoredNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, ignoredNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (ignored namespace)")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := adapter.WaitReloaded(ctx, ignoredNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ShortTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, ignoredNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "Deployment in ignored namespace should NOT be reloaded")
|
||||
})
|
||||
@@ -102,12 +106,16 @@ var _ = Describe("Namespace Ignore Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, watchedNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, watchedNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, watchedNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, watchedNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment in non-ignored namespace should be reloaded")
|
||||
})
|
||||
|
||||
@@ -74,12 +74,16 @@ var _ = Describe("Namespace Selector Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, matchingNS, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, matchingNS, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, matchingNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, matchingNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment in matching namespace should be reloaded")
|
||||
})
|
||||
@@ -102,13 +106,17 @@ var _ = Describe("Namespace Selector Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, nonMatchingNS, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, nonMatchingNS, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (non-matching namespace)")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := adapter.WaitReloaded(ctx, nonMatchingNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ShortTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, nonMatchingNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "Deployment in non-matching namespace should NOT be reloaded")
|
||||
})
|
||||
|
||||
@@ -60,13 +60,17 @@ var _ = Describe("Reload On Create Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating the ConfigMap that the Deployment references")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, createNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = utils.CreateConfigMap(ctx, kubeClient, createNamespace, configMapName,
|
||||
map[string]string{"key": "value"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded (reloadOnCreate=true)")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, createNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, createNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment should reload when referenced ConfigMap is created")
|
||||
})
|
||||
@@ -86,13 +90,17 @@ var _ = Describe("Reload On Create Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating the Secret that the Deployment references")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, createNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = utils.CreateSecretFromStrings(ctx, kubeClient, createNamespace, secretName,
|
||||
map[string]string{"password": "secret"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded (reloadOnCreate=true)")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, createNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, createNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment should reload when referenced Secret is created")
|
||||
})
|
||||
@@ -127,14 +135,18 @@ var _ = Describe("Reload On Create Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating the ConfigMap that the Deployment references")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, createNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = utils.CreateConfigMap(ctx, kubeClient, createNamespace, configMapName,
|
||||
map[string]string{"key": "value"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (reloadOnCreate=false)")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := adapter.WaitReloaded(ctx, createNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ShortTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, createNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "Deployment should NOT reload on create when reloadOnCreate=false")
|
||||
})
|
||||
|
||||
@@ -65,12 +65,16 @@ var _ = Describe("Reload On Delete Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Deleting the ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, deleteNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.DeleteConfigMap(ctx, kubeClient, deleteNamespace, configMapName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded (reloadOnDelete=true)")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, deleteNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, deleteNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment should reload when referenced ConfigMap is deleted")
|
||||
})
|
||||
@@ -94,12 +98,16 @@ var _ = Describe("Reload On Delete Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Deleting the Secret")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, deleteNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.DeleteSecret(ctx, kubeClient, deleteNamespace, secretName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded (reloadOnDelete=true)")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, deleteNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, deleteNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment should reload when referenced Secret is deleted")
|
||||
})
|
||||
@@ -139,13 +147,17 @@ var _ = Describe("Reload On Delete Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Deleting the ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, deleteNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.DeleteConfigMap(ctx, kubeClient, deleteNamespace, configMapName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (reloadOnDelete=false)")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := adapter.WaitReloaded(ctx, deleteNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ShortTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, deleteNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "Deployment should NOT reload on delete when reloadOnDelete=false")
|
||||
})
|
||||
|
||||
@@ -70,12 +70,16 @@ var _ = Describe("Resource Label Selector Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the labeled ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, resourceNS, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, resourceNS, matchingCM, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, resourceNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, resourceNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment should be reloaded when labeled ConfigMap changes")
|
||||
})
|
||||
@@ -98,13 +102,17 @@ var _ = Describe("Resource Label Selector Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the unlabeled ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, resourceNS, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, resourceNS, nonMatchingCM, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (unlabeled ConfigMap)")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := adapter.WaitReloaded(ctx, resourceNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ShortTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, resourceNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "Deployment should NOT reload when unlabeled ConfigMap changes")
|
||||
})
|
||||
|
||||
@@ -68,12 +68,16 @@ var _ = Describe("Watch Globally Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, testNamespace, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded (same namespace should work)")
|
||||
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 in Reloader's namespace should reload with watchGlobally=false")
|
||||
})
|
||||
@@ -96,13 +100,17 @@ var _ = Describe("Watch Globally Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap in the other namespace")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, otherNS, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, otherNS, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (different namespace with watchGlobally=false)")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := adapter.WaitReloaded(ctx, otherNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ShortTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, otherNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "Deployment in other namespace should NOT reload with watchGlobally=false")
|
||||
})
|
||||
@@ -151,12 +159,16 @@ var _ = Describe("Watch Globally Flag Tests", Serial, func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, globalNS, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, globalNS, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded (watchGlobally=true)")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, globalNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, globalNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment in any namespace should reload with watchGlobally=true")
|
||||
})
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
package flags
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/stakater/Reloader/test/e2e/utils"
|
||||
)
|
||||
|
||||
var _ = Describe("Watch Namespaces (scoped mode) Flag Tests", Serial, func() {
|
||||
var (
|
||||
deploymentName string
|
||||
configMapName string
|
||||
watchedNS string
|
||||
unwatchedNS string
|
||||
adapter *utils.DeploymentAdapter
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
deploymentName = utils.RandName("deploy")
|
||||
configMapName = utils.RandName("cm")
|
||||
watchedNS = "watched-" + utils.RandName("ns")
|
||||
unwatchedNS = "unwatched-" + utils.RandName("ns")
|
||||
adapter = utils.NewDeploymentAdapter(kubeClient)
|
||||
|
||||
// The watched namespace must exist before install: in scoped mode the
|
||||
// chart creates a Role/RoleBinding in it.
|
||||
Expect(utils.CreateNamespace(ctx, kubeClient, watchedNS)).To(Succeed())
|
||||
Expect(utils.CreateNamespace(ctx, kubeClient, unwatchedNS)).To(Succeed())
|
||||
|
||||
err := deployReloaderWithFlags(map[string]string{
|
||||
"reloader.watchGlobally": "false",
|
||||
"reloader.namespaces": fmt.Sprintf("{%s}", watchedNS),
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(waitForReloaderReady()).To(Succeed())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
_ = utils.DeleteDeployment(ctx, kubeClient, watchedNS, deploymentName)
|
||||
_ = utils.DeleteConfigMap(ctx, kubeClient, watchedNS, configMapName)
|
||||
_ = utils.DeleteDeployment(ctx, kubeClient, unwatchedNS, deploymentName)
|
||||
_ = utils.DeleteConfigMap(ctx, kubeClient, unwatchedNS, configMapName)
|
||||
_ = undeployReloader()
|
||||
_ = utils.DeleteNamespace(ctx, kubeClient, watchedNS)
|
||||
_ = utils.DeleteNamespace(ctx, kubeClient, unwatchedNS)
|
||||
})
|
||||
|
||||
It("should reload workloads in a watched namespace", func() {
|
||||
By("Creating a ConfigMap in the watched namespace")
|
||||
_, err := utils.CreateConfigMap(ctx, kubeClient, watchedNS, configMapName,
|
||||
map[string]string{"key": "initial"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating a Deployment in the watched namespace with auto annotation")
|
||||
_, err = utils.CreateDeployment(ctx, kubeClient, watchedNS, deploymentName,
|
||||
utils.WithConfigMapEnvFrom(configMapName),
|
||||
utils.WithAnnotations(utils.BuildAutoTrueAnnotation()),
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be ready")
|
||||
Expect(adapter.WaitReady(ctx, watchedNS, deploymentName, utils.WorkloadReadyTimeout)).To(Succeed())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, watchedNS, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, watchedNS, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded (watched namespace should work)")
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, watchedNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment in a watched namespace should reload")
|
||||
})
|
||||
|
||||
It("should NOT reload workloads in an unwatched namespace", func() {
|
||||
By("Creating a ConfigMap in an unwatched namespace")
|
||||
_, err := utils.CreateConfigMap(ctx, kubeClient, unwatchedNS, configMapName,
|
||||
map[string]string{"key": "initial"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating a Deployment in an unwatched namespace with auto annotation")
|
||||
_, err = utils.CreateDeployment(ctx, kubeClient, unwatchedNS, deploymentName,
|
||||
utils.WithConfigMapEnvFrom(configMapName),
|
||||
utils.WithAnnotations(utils.BuildAutoTrueAnnotation()),
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be ready")
|
||||
Expect(adapter.WaitReady(ctx, unwatchedNS, deploymentName, utils.WorkloadReadyTimeout)).To(Succeed())
|
||||
|
||||
By("Updating the ConfigMap in the unwatched namespace")
|
||||
// Capture the reload-annotation baseline before the trigger to avoid the
|
||||
// TOCTOU race where Reloader reloads before WaitReloaded records its baseline.
|
||||
priorReload, err := adapter.GetPodTemplateAnnotation(ctx, unwatchedNS, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, unwatchedNS, configMapName, map[string]string{"key": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (namespace not in --namespaces)")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, unwatchedNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "Deployment in an unwatched namespace should NOT reload")
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Watch Multiple Namespaces (scoped mode) Flag Tests", Serial, func() {
|
||||
var (
|
||||
deploymentName string
|
||||
configMapName string
|
||||
watchedA string
|
||||
watchedB string
|
||||
unwatchedNS string
|
||||
adapter *utils.DeploymentAdapter
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
deploymentName = utils.RandName("deploy")
|
||||
configMapName = utils.RandName("cm")
|
||||
watchedA = "watched-a-" + utils.RandName("ns")
|
||||
watchedB = "watched-b-" + utils.RandName("ns")
|
||||
unwatchedNS = "unwatched-" + utils.RandName("ns")
|
||||
adapter = utils.NewDeploymentAdapter(kubeClient)
|
||||
|
||||
// Both watched namespaces must exist before install: in scoped mode the
|
||||
// chart creates a Role/RoleBinding in each.
|
||||
Expect(utils.CreateNamespace(ctx, kubeClient, watchedA)).To(Succeed())
|
||||
Expect(utils.CreateNamespace(ctx, kubeClient, watchedB)).To(Succeed())
|
||||
Expect(utils.CreateNamespace(ctx, kubeClient, unwatchedNS)).To(Succeed())
|
||||
|
||||
err := deployReloaderWithFlags(map[string]string{
|
||||
"reloader.watchGlobally": "false",
|
||||
"reloader.namespaces": fmt.Sprintf("{%s,%s}", watchedA, watchedB),
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(waitForReloaderReady()).To(Succeed())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
for _, ns := range []string{watchedA, watchedB, unwatchedNS} {
|
||||
_ = utils.DeleteDeployment(ctx, kubeClient, ns, deploymentName)
|
||||
_ = utils.DeleteConfigMap(ctx, kubeClient, ns, configMapName)
|
||||
}
|
||||
_ = undeployReloader()
|
||||
for _, ns := range []string{watchedA, watchedB, unwatchedNS} {
|
||||
_ = utils.DeleteNamespace(ctx, kubeClient, ns)
|
||||
}
|
||||
})
|
||||
|
||||
It("should reload workloads across all watched namespaces but not an unwatched one", func() {
|
||||
// Set up an annotated Deployment + ConfigMap in every namespace (both
|
||||
// watched ones and the unwatched control).
|
||||
allNS := []string{watchedA, watchedB, unwatchedNS}
|
||||
for _, ns := range allNS {
|
||||
By("Creating a ConfigMap and Deployment in " + ns)
|
||||
_, err := utils.CreateConfigMap(ctx, kubeClient, ns, configMapName,
|
||||
map[string]string{"key": "initial"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = utils.CreateDeployment(ctx, kubeClient, ns, deploymentName,
|
||||
utils.WithConfigMapEnvFrom(configMapName),
|
||||
utils.WithAnnotations(utils.BuildAutoTrueAnnotation()),
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(adapter.WaitReady(ctx, ns, deploymentName, utils.WorkloadReadyTimeout)).To(Succeed())
|
||||
}
|
||||
|
||||
// Capture reload baselines before triggering, then update every ConfigMap.
|
||||
priorReload := map[string]string{}
|
||||
for _, ns := range allNS {
|
||||
pv, err := adapter.GetPodTemplateAnnotation(ctx, ns, deploymentName, utils.AnnotationLastReloadedFrom)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
priorReload[ns] = pv
|
||||
}
|
||||
for _, ns := range allNS {
|
||||
By("Updating the ConfigMap in " + ns)
|
||||
Expect(utils.UpdateConfigMap(ctx, kubeClient, ns, configMapName,
|
||||
map[string]string{"key": "updated"})).To(Succeed())
|
||||
}
|
||||
|
||||
By("Verifying workloads in BOTH watched namespaces reloaded")
|
||||
for _, ns := range []string{watchedA, watchedB} {
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, ns, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload[ns], utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment in watched namespace %s should reload", ns)
|
||||
}
|
||||
|
||||
By("Verifying the workload in the unwatched namespace did NOT reload")
|
||||
reloaded, err := adapter.WaitReloadedFrom(ctx, unwatchedNS, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, priorReload[unwatchedNS], utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "Deployment in unwatched namespace %s should NOT reload", unwatchedNS)
|
||||
})
|
||||
})
|
||||
@@ -66,12 +66,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