mirror of
https://github.com/stakater/Reloader.git
synced 2026-05-17 06:06:39 +00:00
feat: Add missing tests for edge cases and all other workload types for pod annotations
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
package advanced
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
@@ -9,21 +12,27 @@ import (
|
||||
|
||||
var _ = Describe("Job Workload Recreation Tests", func() {
|
||||
var (
|
||||
jobName string
|
||||
configMapName string
|
||||
secretName string
|
||||
jobName string
|
||||
configMapName string
|
||||
secretName string
|
||||
spcName string
|
||||
vaultSecretPath string
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
jobName = utils.RandName("job")
|
||||
configMapName = utils.RandName("cm")
|
||||
secretName = utils.RandName("secret")
|
||||
spcName = utils.RandName("spc")
|
||||
vaultSecretPath = fmt.Sprintf("secret/%s", utils.RandName("vault"))
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
_ = utils.DeleteJob(ctx, kubeClient, testNamespace, jobName)
|
||||
_ = utils.DeleteConfigMap(ctx, kubeClient, testNamespace, configMapName)
|
||||
_ = utils.DeleteSecret(ctx, kubeClient, testNamespace, secretName)
|
||||
_ = utils.DeleteSecretProviderClass(ctx, csiClient, testNamespace, spcName)
|
||||
_ = utils.DeleteVaultSecret(ctx, kubeClient, restConfig, vaultSecretPath)
|
||||
})
|
||||
|
||||
Context("Job with ConfigMap reference", func() {
|
||||
@@ -40,8 +49,8 @@ var _ = Describe("Job Workload Recreation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
originalUID := string(job.UID)
|
||||
|
||||
By("Waiting for Job to exist")
|
||||
err = utils.WaitForJobExists(ctx, kubeClient, testNamespace, jobName, utils.DeploymentReady)
|
||||
By("Waiting for Job to be ready")
|
||||
err = utils.WaitForJobReady(ctx, kubeClient, testNamespace, jobName, utils.DeploymentReady)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
@@ -69,8 +78,8 @@ var _ = Describe("Job Workload Recreation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
originalUID := string(job.UID)
|
||||
|
||||
By("Waiting for Job to exist")
|
||||
err = utils.WaitForJobExists(ctx, kubeClient, testNamespace, jobName, utils.DeploymentReady)
|
||||
By("Waiting for Job to be ready")
|
||||
err = utils.WaitForJobReady(ctx, kubeClient, testNamespace, jobName, utils.DeploymentReady)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Secret")
|
||||
@@ -99,8 +108,8 @@ var _ = Describe("Job Workload Recreation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
originalUID := string(job.UID)
|
||||
|
||||
By("Waiting for Job to exist")
|
||||
err = utils.WaitForJobExists(ctx, kubeClient, testNamespace, jobName, utils.DeploymentReady)
|
||||
By("Waiting for Job to be ready")
|
||||
err = utils.WaitForJobReady(ctx, kubeClient, testNamespace, jobName, utils.DeploymentReady)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
@@ -129,8 +138,8 @@ var _ = Describe("Job Workload Recreation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
originalUID := string(job.UID)
|
||||
|
||||
By("Waiting for Job to exist")
|
||||
err = utils.WaitForJobExists(ctx, kubeClient, testNamespace, jobName, utils.DeploymentReady)
|
||||
By("Waiting for Job to be ready")
|
||||
err = utils.WaitForJobReady(ctx, kubeClient, testNamespace, jobName, utils.DeploymentReady)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
@@ -160,8 +169,8 @@ var _ = Describe("Job Workload Recreation Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
originalUID := string(job.UID)
|
||||
|
||||
By("Waiting for Job to exist")
|
||||
err = utils.WaitForJobExists(ctx, kubeClient, testNamespace, jobName, utils.DeploymentReady)
|
||||
By("Waiting for Job to be ready")
|
||||
err = utils.WaitForJobReady(ctx, kubeClient, testNamespace, jobName, utils.DeploymentReady)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Secret")
|
||||
@@ -175,4 +184,70 @@ var _ = Describe("Job Workload Recreation Tests", func() {
|
||||
Expect(recreated).To(BeTrue(), "Job with valueFrom.secretKeyRef should be recreated when Secret changes")
|
||||
})
|
||||
})
|
||||
|
||||
Context("Job with SecretProviderClass reference", Label("csi"), func() {
|
||||
BeforeEach(func() {
|
||||
// Skip if CSI driver not installed
|
||||
if !utils.IsCSIDriverInstalled(ctx, csiClient) {
|
||||
Skip("CSI secrets store driver not installed - skipping CSI test")
|
||||
}
|
||||
// Skip if Vault CSI provider not installed
|
||||
if !utils.IsVaultProviderInstalled(ctx, kubeClient) {
|
||||
Skip("Vault CSI provider not installed - skipping CSI test")
|
||||
}
|
||||
})
|
||||
|
||||
It("should recreate Job when Vault secret changes", func() {
|
||||
By("Creating a secret in Vault")
|
||||
err := utils.CreateVaultSecret(
|
||||
ctx, kubeClient, restConfig, vaultSecretPath, map[string]string{"api_key": "initial-value-v1"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating a SecretProviderClass pointing to Vault secret")
|
||||
_, err = utils.CreateSecretProviderClassWithSecret(
|
||||
ctx, csiClient, testNamespace, spcName,
|
||||
vaultSecretPath, "api_key",
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating a Job with CSI volume and SPC reload annotation")
|
||||
job, err := utils.CreateJob(ctx, kubeClient, testNamespace, jobName,
|
||||
utils.WithJobCSIVolume(spcName),
|
||||
utils.WithJobAnnotations(utils.BuildSecretProviderClassReloadAnnotation(spcName)))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
originalUID := string(job.UID)
|
||||
|
||||
By("Waiting for Job to be ready")
|
||||
err = utils.WaitForJobReady(ctx, kubeClient, testNamespace, jobName, utils.DeploymentReady)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Finding the SPCPS created by CSI driver")
|
||||
spcpsName, err := utils.FindSPCPSForSPC(
|
||||
ctx, csiClient, testNamespace, spcName, utils.DeploymentReady,
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
GinkgoWriter.Printf("Found SPCPS: %s\n", spcpsName)
|
||||
|
||||
By("Getting initial SPCPS version")
|
||||
initialVersion, err := utils.GetSPCPSVersion(ctx, csiClient, testNamespace, spcpsName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
GinkgoWriter.Printf("Initial SPCPS version: %s\n", initialVersion)
|
||||
|
||||
By("Updating the Vault secret")
|
||||
err = utils.UpdateVaultSecret(
|
||||
ctx, kubeClient, restConfig, vaultSecretPath, map[string]string{"api_key": "updated-value-v2"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for CSI driver to sync the new secret version")
|
||||
err = utils.WaitForSPCPSVersionChange(ctx, csiClient, testNamespace, spcpsName, initialVersion, 10*time.Second)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
GinkgoWriter.Println("CSI driver synced new secret version")
|
||||
|
||||
By("Waiting for Job to be recreated (new UID)")
|
||||
_, recreated, err := utils.WaitForJobRecreated(ctx, kubeClient, testNamespace, jobName, originalUID,
|
||||
utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(recreated).To(BeTrue(), "Job should be recreated with new UID when Vault secret changes")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,187 +0,0 @@
|
||||
package advanced
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/stakater/Reloader/test/e2e/utils"
|
||||
)
|
||||
|
||||
var _ = Describe("Pod Template Annotations Tests", func() {
|
||||
var (
|
||||
deploymentName string
|
||||
configMapName string
|
||||
secretName string
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
deploymentName = utils.RandName("deploy")
|
||||
configMapName = utils.RandName("cm")
|
||||
secretName = utils.RandName("secret")
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
_ = utils.DeleteDeployment(ctx, kubeClient, testNamespace, deploymentName)
|
||||
_ = utils.DeleteConfigMap(ctx, kubeClient, testNamespace, configMapName)
|
||||
_ = utils.DeleteSecret(ctx, kubeClient, testNamespace, secretName)
|
||||
})
|
||||
|
||||
Context("Annotations on pod template metadata only", func() {
|
||||
It("should reload when using annotation on pod template metadata (not deployment metadata)", func() {
|
||||
By("Creating a ConfigMap")
|
||||
_, err := utils.CreateConfigMap(ctx, kubeClient, testNamespace, configMapName,
|
||||
map[string]string{"POD_CONFIG": "initial"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating a Deployment with annotation ONLY on pod template")
|
||||
_, err = utils.CreateDeployment(ctx, kubeClient, testNamespace, deploymentName,
|
||||
utils.WithConfigMapEnvFrom(configMapName),
|
||||
utils.WithPodTemplateAnnotations(utils.BuildConfigMapReloadAnnotation(configMapName)),
|
||||
// Note: No WithAnnotations - annotation only on pod template
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be ready")
|
||||
err = utils.WaitForDeploymentReady(ctx, kubeClient, testNamespace, deploymentName, utils.DeploymentReady)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"POD_CONFIG": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded")
|
||||
reloaded, err := utils.WaitForDeploymentReloaded(ctx, kubeClient, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment should reload when annotation is on pod template metadata")
|
||||
})
|
||||
})
|
||||
|
||||
Context("Annotations on both deployment and pod template metadata", func() {
|
||||
It("should reload when annotations are on both deployment and pod template", func() {
|
||||
By("Creating a ConfigMap")
|
||||
_, err := utils.CreateConfigMap(ctx, kubeClient, testNamespace, configMapName,
|
||||
map[string]string{"BOTH_CONFIG": "initial"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating a Deployment with annotation on BOTH deployment and pod template")
|
||||
_, err = utils.CreateDeployment(ctx, kubeClient, testNamespace, deploymentName,
|
||||
utils.WithConfigMapEnvFrom(configMapName),
|
||||
utils.WithAnnotations(utils.BuildConfigMapReloadAnnotation(configMapName)),
|
||||
utils.WithPodTemplateAnnotations(utils.BuildConfigMapReloadAnnotation(configMapName)),
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be ready")
|
||||
err = utils.WaitForDeploymentReady(ctx, kubeClient, testNamespace, deploymentName, utils.DeploymentReady)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"BOTH_CONFIG": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded")
|
||||
reloaded, err := utils.WaitForDeploymentReloaded(ctx, kubeClient, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment should reload when annotations are on both locations")
|
||||
})
|
||||
})
|
||||
|
||||
Context("auto=true annotation on pod template", func() {
|
||||
It("should reload when auto annotation is on pod template metadata", func() {
|
||||
By("Creating a ConfigMap")
|
||||
_, err := utils.CreateConfigMap(ctx, kubeClient, testNamespace, configMapName,
|
||||
map[string]string{"AUTO_POD_CONFIG": "initial"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating a Deployment with auto=true annotation on pod template")
|
||||
_, err = utils.CreateDeployment(ctx, kubeClient, testNamespace, deploymentName,
|
||||
utils.WithConfigMapEnvFrom(configMapName),
|
||||
utils.WithPodTemplateAnnotations(utils.BuildAutoTrueAnnotation()),
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be ready")
|
||||
err = utils.WaitForDeploymentReady(ctx, kubeClient, testNamespace, deploymentName, utils.DeploymentReady)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the ConfigMap")
|
||||
err = utils.UpdateConfigMap(ctx, kubeClient, testNamespace, configMapName, map[string]string{"AUTO_POD_CONFIG": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded")
|
||||
reloaded, err := utils.WaitForDeploymentReloaded(ctx, kubeClient, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment with auto=true on pod template should reload")
|
||||
})
|
||||
})
|
||||
|
||||
Context("Secret annotation on pod template", func() {
|
||||
It("should reload when secret reload annotation is on pod template", func() {
|
||||
By("Creating a Secret")
|
||||
_, err := utils.CreateSecretFromStrings(ctx, kubeClient, testNamespace, secretName,
|
||||
map[string]string{"POD_SECRET": "initial"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating a Deployment with secret reload annotation on pod template")
|
||||
_, err = utils.CreateDeployment(ctx, kubeClient, testNamespace, deploymentName,
|
||||
utils.WithSecretEnvFrom(secretName),
|
||||
utils.WithPodTemplateAnnotations(utils.BuildSecretReloadAnnotation(secretName)),
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be ready")
|
||||
err = utils.WaitForDeploymentReady(ctx, kubeClient, testNamespace, deploymentName, utils.DeploymentReady)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Secret")
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName, map[string]string{"POD_SECRET": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be reloaded")
|
||||
reloaded, err := utils.WaitForDeploymentReloaded(ctx, kubeClient, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment should reload when secret annotation is on pod template")
|
||||
})
|
||||
})
|
||||
|
||||
Context("Mismatched annotations (different resources)", func() {
|
||||
It("should NOT reload when pod template has ConfigMap annotation but we update Secret", func() {
|
||||
By("Creating both ConfigMap and Secret")
|
||||
_, err := utils.CreateConfigMap(ctx, kubeClient, testNamespace, configMapName,
|
||||
map[string]string{"CONFIG": "value"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
_, err = utils.CreateSecretFromStrings(ctx, kubeClient, testNamespace, secretName,
|
||||
map[string]string{"SECRET": "initial"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating a Deployment with ConfigMap annotation on pod template but using Secret")
|
||||
_, err = utils.CreateDeployment(ctx, kubeClient, testNamespace, deploymentName,
|
||||
utils.WithSecretEnvFrom(secretName),
|
||||
utils.WithPodTemplateAnnotations(utils.BuildConfigMapReloadAnnotation(configMapName)),
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be ready")
|
||||
err = utils.WaitForDeploymentReady(ctx, kubeClient, testNamespace, deploymentName, utils.DeploymentReady)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Secret (not the ConfigMap)")
|
||||
err = utils.UpdateSecretFromStrings(ctx, kubeClient, testNamespace, secretName, map[string]string{"SECRET": "updated"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying Deployment was NOT reloaded (negative test)")
|
||||
time.Sleep(utils.NegativeTestWait)
|
||||
reloaded, err := utils.WaitForDeploymentReloaded(ctx, kubeClient, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "Deployment should NOT reload when we update different resource than annotated")
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -21,6 +21,7 @@ var (
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
testEnv *utils.TestEnvironment
|
||||
registry *utils.AdapterRegistry
|
||||
)
|
||||
|
||||
func TestAnnotations(t *testing.T) {
|
||||
@@ -40,6 +41,23 @@ var _ = BeforeSuite(func() {
|
||||
restConfig = testEnv.RestConfig
|
||||
testNamespace = testEnv.Namespace
|
||||
|
||||
registry = utils.NewAdapterRegistry(kubeClient)
|
||||
|
||||
// Register optional adapters if CRDs are installed
|
||||
if utils.IsArgoRolloutsInstalled(ctx, testEnv.RolloutsClient) {
|
||||
GinkgoWriter.Println("Argo Rollouts detected, registering ArgoRolloutAdapter")
|
||||
registry.RegisterAdapter(utils.NewArgoRolloutAdapter(testEnv.RolloutsClient))
|
||||
} else {
|
||||
GinkgoWriter.Println("Argo Rollouts not detected, skipping ArgoRolloutAdapter registration")
|
||||
}
|
||||
|
||||
if utils.HasDeploymentConfigSupport(testEnv.DiscoveryClient) && testEnv.OpenShiftClient != nil {
|
||||
GinkgoWriter.Println("OpenShift detected, registering DeploymentConfigAdapter")
|
||||
registry.RegisterAdapter(utils.NewDeploymentConfigAdapter(testEnv.OpenShiftClient))
|
||||
} else {
|
||||
GinkgoWriter.Println("OpenShift not detected, skipping DeploymentConfigAdapter registration")
|
||||
}
|
||||
|
||||
deployValues := map[string]string{
|
||||
"reloader.reloadStrategy": "annotations",
|
||||
"reloader.watchGlobally": "false", // Only watch own namespace to prevent cross-talk between test suites
|
||||
|
||||
@@ -17,6 +17,7 @@ var _ = Describe("Exclude Annotation Tests", func() {
|
||||
configMapName2 string
|
||||
secretName string
|
||||
secretName2 string
|
||||
workloadName string
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
@@ -25,6 +26,7 @@ var _ = Describe("Exclude Annotation Tests", func() {
|
||||
configMapName2 = utils.RandName("cm2")
|
||||
secretName = utils.RandName("secret")
|
||||
secretName2 = utils.RandName("secret2")
|
||||
workloadName = utils.RandName("workload")
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
@@ -185,6 +187,58 @@ var _ = Describe("Exclude Annotation Tests", func() {
|
||||
})
|
||||
})
|
||||
|
||||
Context("Exclude annotation on pod template", func() {
|
||||
DescribeTable("should NOT reload when exclude annotation is on pod template only",
|
||||
func(workloadType utils.WorkloadType) {
|
||||
adapter := registry.Get(workloadType)
|
||||
if adapter == nil {
|
||||
Skip(fmt.Sprintf("%s adapter not available (CRD not installed)", workloadType))
|
||||
}
|
||||
|
||||
By("Creating two ConfigMaps")
|
||||
_, err := utils.CreateConfigMap(ctx, kubeClient, testNamespace, configMapName,
|
||||
map[string]string{"key": "initial"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
_, err = utils.CreateConfigMap(ctx, kubeClient, testNamespace, configMapName2,
|
||||
map[string]string{"key2": "initial2"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating workload with auto=true and exclude annotation on pod template ONLY")
|
||||
err = adapter.Create(ctx, testNamespace, workloadName, utils.WorkloadConfig{
|
||||
ConfigMapName: configMapName,
|
||||
UseConfigMapEnvFrom: true,
|
||||
PodTemplateAnnotations: utils.MergeAnnotations(
|
||||
utils.BuildAutoTrueAnnotation(),
|
||||
utils.BuildConfigMapExcludeAnnotation(configMapName),
|
||||
),
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
DeferCleanup(func() { _ = adapter.Delete(ctx, testNamespace, workloadName) })
|
||||
|
||||
By("Waiting for workload to be ready")
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.DeploymentReady)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the excluded ConfigMap")
|
||||
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)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "%s should NOT reload with exclude on pod template", workloadType)
|
||||
},
|
||||
Entry("Deployment", utils.WorkloadDeployment),
|
||||
Entry("DaemonSet", utils.WorkloadDaemonSet),
|
||||
Entry("StatefulSet", utils.WorkloadStatefulSet),
|
||||
Entry("ArgoRollout", Label("argo"), utils.WorkloadArgoRollout),
|
||||
Entry("DeploymentConfig", Label("openshift"), utils.WorkloadDeploymentConfig))
|
||||
})
|
||||
|
||||
Context("SecretProviderClass exclude annotation", Label("csi"), func() {
|
||||
var (
|
||||
spcName string
|
||||
|
||||
@@ -97,5 +97,43 @@ var _ = Describe("Pause Period Tests", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(paused).To(BeFalse(), "Deployment should NOT have paused-at annotation without pause-period")
|
||||
})
|
||||
|
||||
It("should pause Deployment when pause-period annotation is on pod template", func() {
|
||||
By("Creating a ConfigMap")
|
||||
_, err := utils.CreateConfigMap(ctx, kubeClient, testNamespace, configMapName,
|
||||
map[string]string{"key": "initial"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating a Deployment with pause-period annotation on pod template ONLY")
|
||||
_, err = utils.CreateDeployment(ctx, kubeClient, testNamespace, deploymentName,
|
||||
utils.WithConfigMapEnvFrom(configMapName),
|
||||
utils.WithPodTemplateAnnotations(utils.MergeAnnotations(
|
||||
utils.BuildConfigMapReloadAnnotation(configMapName),
|
||||
utils.BuildPausePeriodAnnotation("10s"),
|
||||
)),
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Deployment to be ready")
|
||||
err = utils.WaitForDeploymentReady(ctx, kubeClient, testNamespace, deploymentName, utils.DeploymentReady)
|
||||
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 Deployment to be reloaded")
|
||||
reloaded, err := utils.WaitForDeploymentReloaded(ctx, kubeClient, testNamespace, deploymentName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "Deployment should have been reloaded")
|
||||
|
||||
By("Verifying Deployment has paused-at annotation")
|
||||
paused, err := utils.WaitForDeploymentPaused(ctx, kubeClient, testNamespace, deploymentName,
|
||||
utils.AnnotationDeploymentPausedAt, utils.ShortTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(paused).To(BeTrue(), "Deployment should have paused-at annotation with pause-period on pod template")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package annotations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
@@ -13,11 +14,13 @@ var _ = Describe("Search and Match Annotation Tests", func() {
|
||||
var (
|
||||
deploymentName string
|
||||
configMapName string
|
||||
workloadName string
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
deploymentName = utils.RandName("deploy")
|
||||
configMapName = utils.RandName("cm")
|
||||
workloadName = utils.RandName("workload")
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
@@ -163,4 +166,49 @@ var _ = Describe("Search and Match Annotation Tests", func() {
|
||||
Expect(reloaded2).To(BeFalse(), "Deployment without search annotation should NOT reload")
|
||||
})
|
||||
})
|
||||
|
||||
Context("with search annotation on pod template", func() {
|
||||
DescribeTable("should reload when search annotation is on pod template only",
|
||||
func(workloadType utils.WorkloadType) {
|
||||
adapter := registry.Get(workloadType)
|
||||
if adapter == nil {
|
||||
Skip(fmt.Sprintf("%s adapter not available (CRD not installed)", workloadType))
|
||||
}
|
||||
|
||||
By("Creating a ConfigMap with match annotation")
|
||||
_, err := utils.CreateConfigMap(ctx, kubeClient, testNamespace, configMapName,
|
||||
map[string]string{"key": "initial"},
|
||||
utils.BuildMatchAnnotation())
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating workload with search annotation on pod template ONLY")
|
||||
err = adapter.Create(ctx, testNamespace, workloadName, utils.WorkloadConfig{
|
||||
ConfigMapName: configMapName,
|
||||
UseConfigMapEnvFrom: true,
|
||||
PodTemplateAnnotations: utils.BuildSearchAnnotation(),
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
DeferCleanup(func() { _ = adapter.Delete(ctx, testNamespace, workloadName) })
|
||||
|
||||
By("Waiting for workload to be ready")
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.DeploymentReady)
|
||||
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)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "%s should reload with search annotation on pod template", workloadType)
|
||||
},
|
||||
Entry("Deployment", utils.WorkloadDeployment),
|
||||
Entry("DaemonSet", utils.WorkloadDaemonSet),
|
||||
Entry("StatefulSet", utils.WorkloadStatefulSet),
|
||||
Entry("ArgoRollout", Label("argo"), utils.WorkloadArgoRollout),
|
||||
Entry("DeploymentConfig", Label("openshift"), utils.WorkloadDeploymentConfig))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -194,7 +194,9 @@ var _ = Describe("Workload Reload Tests", func() {
|
||||
Expect(reloaded).To(BeTrue(), "%s should have been reloaded when Vault secret changed", workloadType)
|
||||
}, Entry("Deployment", Label("csi"), utils.WorkloadDeployment),
|
||||
Entry("DaemonSet", Label("csi"), utils.WorkloadDaemonSet),
|
||||
Entry("StatefulSet", Label("csi"), utils.WorkloadStatefulSet))
|
||||
Entry("StatefulSet", Label("csi"), utils.WorkloadStatefulSet),
|
||||
Entry("ArgoRollout", Label("csi", "argo"), utils.WorkloadArgoRollout),
|
||||
Entry("DeploymentConfig", Label("csi", "openshift"), utils.WorkloadDeploymentConfig))
|
||||
|
||||
// Auto=true annotation tests
|
||||
DescribeTable("should reload with auto=true annotation when ConfigMap changes",
|
||||
@@ -377,7 +379,9 @@ var _ = Describe("Workload Reload Tests", func() {
|
||||
Expect(reloaded).To(BeFalse(), "%s should NOT reload when only SPCPS labels change", workloadType)
|
||||
}, Entry("Deployment", Label("csi"), utils.WorkloadDeployment),
|
||||
Entry("DaemonSet", Label("csi"), utils.WorkloadDaemonSet),
|
||||
Entry("StatefulSet", Label("csi"), utils.WorkloadStatefulSet))
|
||||
Entry("StatefulSet", Label("csi"), utils.WorkloadStatefulSet),
|
||||
Entry("ArgoRollout", Label("csi", "argo"), utils.WorkloadArgoRollout),
|
||||
Entry("DeploymentConfig", Label("csi", "openshift"), utils.WorkloadDeploymentConfig))
|
||||
|
||||
// CronJob special handling - triggers a Job instead of annotation
|
||||
Context("CronJob (special handling)", func() {
|
||||
@@ -803,6 +807,366 @@ var _ = Describe("Workload Reload Tests", func() {
|
||||
Expect(reloaded).To(BeFalse(), "Deployment with auto=false should NOT have been reloaded")
|
||||
})
|
||||
})
|
||||
|
||||
// ============================================================
|
||||
// POD TEMPLATE ANNOTATION TESTS
|
||||
// These tests verify that annotations placed on the pod template
|
||||
// (spec.template.metadata.annotations) work the same as annotations
|
||||
// placed on the workload metadata (metadata.annotations).
|
||||
// ============================================================
|
||||
Context("Pod Template Annotations", func() {
|
||||
DescribeTable("should reload when ConfigMap annotation is on pod template only",
|
||||
func(workloadType utils.WorkloadType) {
|
||||
adapter := registry.Get(workloadType)
|
||||
if adapter == nil {
|
||||
Skip(fmt.Sprintf("%s adapter not available (CRD not installed)", workloadType))
|
||||
}
|
||||
|
||||
By("Creating a ConfigMap")
|
||||
_, err := utils.CreateConfigMap(ctx, kubeClient, testNamespace, configMapName,
|
||||
map[string]string{"key": "initial"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating workload with ConfigMap annotation on pod template ONLY")
|
||||
err = adapter.Create(ctx, testNamespace, workloadName, utils.WorkloadConfig{
|
||||
ConfigMapName: configMapName,
|
||||
UseConfigMapEnvFrom: true,
|
||||
PodTemplateAnnotations: utils.BuildConfigMapReloadAnnotation(configMapName),
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
DeferCleanup(func() { _ = adapter.Delete(ctx, testNamespace, workloadName) })
|
||||
|
||||
By("Waiting for workload to be ready")
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.DeploymentReady)
|
||||
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)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "%s should reload with pod template annotation", workloadType)
|
||||
},
|
||||
Entry("Deployment", utils.WorkloadDeployment),
|
||||
Entry("DaemonSet", utils.WorkloadDaemonSet),
|
||||
Entry("StatefulSet", utils.WorkloadStatefulSet),
|
||||
Entry("ArgoRollout", Label("argo"), utils.WorkloadArgoRollout),
|
||||
Entry("DeploymentConfig", Label("openshift"), utils.WorkloadDeploymentConfig))
|
||||
|
||||
DescribeTable("should reload when Secret annotation is on pod template only",
|
||||
func(workloadType utils.WorkloadType) {
|
||||
adapter := registry.Get(workloadType)
|
||||
if adapter == nil {
|
||||
Skip(fmt.Sprintf("%s adapter not available (CRD not installed)", workloadType))
|
||||
}
|
||||
|
||||
By("Creating a Secret")
|
||||
_, err := utils.CreateSecretFromStrings(ctx, kubeClient, testNamespace, secretName,
|
||||
map[string]string{"password": "initial"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating workload with Secret annotation on pod template ONLY")
|
||||
err = adapter.Create(ctx, testNamespace, workloadName, utils.WorkloadConfig{
|
||||
SecretName: secretName,
|
||||
UseSecretEnvFrom: true,
|
||||
PodTemplateAnnotations: utils.BuildSecretReloadAnnotation(secretName),
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
DeferCleanup(func() { _ = adapter.Delete(ctx, testNamespace, workloadName) })
|
||||
|
||||
By("Waiting for workload to be ready")
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.DeploymentReady)
|
||||
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)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "%s should reload with pod template annotation", workloadType)
|
||||
},
|
||||
Entry("Deployment", utils.WorkloadDeployment),
|
||||
Entry("DaemonSet", utils.WorkloadDaemonSet),
|
||||
Entry("StatefulSet", utils.WorkloadStatefulSet),
|
||||
Entry("ArgoRollout", Label("argo"), utils.WorkloadArgoRollout),
|
||||
Entry("DeploymentConfig", Label("openshift"), utils.WorkloadDeploymentConfig))
|
||||
|
||||
DescribeTable("should reload when auto=true annotation is on pod template only",
|
||||
func(workloadType utils.WorkloadType) {
|
||||
adapter := registry.Get(workloadType)
|
||||
if adapter == nil {
|
||||
Skip(fmt.Sprintf("%s adapter not available (CRD not installed)", workloadType))
|
||||
}
|
||||
|
||||
By("Creating a ConfigMap")
|
||||
_, err := utils.CreateConfigMap(ctx, kubeClient, testNamespace, configMapName,
|
||||
map[string]string{"key": "initial"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating workload with auto=true annotation on pod template ONLY")
|
||||
err = adapter.Create(ctx, testNamespace, workloadName, utils.WorkloadConfig{
|
||||
ConfigMapName: configMapName,
|
||||
UseConfigMapEnvFrom: true,
|
||||
PodTemplateAnnotations: utils.BuildAutoTrueAnnotation(),
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
DeferCleanup(func() { _ = adapter.Delete(ctx, testNamespace, workloadName) })
|
||||
|
||||
By("Waiting for workload to be ready")
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.DeploymentReady)
|
||||
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)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "%s with auto=true on pod template should reload", workloadType)
|
||||
},
|
||||
Entry("Deployment", utils.WorkloadDeployment),
|
||||
Entry("DaemonSet", utils.WorkloadDaemonSet),
|
||||
Entry("StatefulSet", utils.WorkloadStatefulSet),
|
||||
Entry("ArgoRollout", Label("argo"), utils.WorkloadArgoRollout),
|
||||
Entry("DeploymentConfig", Label("openshift"), utils.WorkloadDeploymentConfig))
|
||||
|
||||
DescribeTable("should reload when SecretProviderClass annotation is on pod template only",
|
||||
func(workloadType utils.WorkloadType) {
|
||||
if !utils.IsCSIDriverInstalled(ctx, csiClient) {
|
||||
Skip("CSI secrets store driver not installed")
|
||||
}
|
||||
if !utils.IsVaultProviderInstalled(ctx, kubeClient) {
|
||||
Skip("Vault CSI provider not installed")
|
||||
}
|
||||
|
||||
adapter := registry.Get(workloadType)
|
||||
if adapter == nil {
|
||||
Skip(fmt.Sprintf("%s adapter not available (CRD not installed)", workloadType))
|
||||
}
|
||||
|
||||
By("Creating a secret in Vault")
|
||||
err := utils.CreateVaultSecret(ctx, kubeClient, restConfig, vaultSecretPath,
|
||||
map[string]string{"api_key": "initial-value-v1"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating a SecretProviderClass pointing to Vault secret")
|
||||
_, err = utils.CreateSecretProviderClassWithSecret(ctx, csiClient, testNamespace, spcName,
|
||||
vaultSecretPath, "api_key")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating workload with SPC annotation on pod template ONLY")
|
||||
err = adapter.Create(ctx, testNamespace, workloadName, utils.WorkloadConfig{
|
||||
SPCName: spcName,
|
||||
UseCSIVolume: true,
|
||||
PodTemplateAnnotations: utils.BuildSecretProviderClassReloadAnnotation(spcName),
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
DeferCleanup(func() { _ = adapter.Delete(ctx, testNamespace, workloadName) })
|
||||
|
||||
By("Waiting for workload to be ready")
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.DeploymentReady)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Finding the SPCPS created by CSI driver")
|
||||
spcpsName, err := utils.FindSPCPSForDeployment(ctx, csiClient, kubeClient, testNamespace,
|
||||
workloadName, utils.DeploymentReady)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Getting initial SPCPS version")
|
||||
initialVersion, err := utils.GetSPCPSVersion(ctx, csiClient, testNamespace, spcpsName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Vault secret")
|
||||
err = utils.UpdateVaultSecret(ctx, kubeClient, restConfig, vaultSecretPath,
|
||||
map[string]string{"api_key": "updated-value-v2"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for CSI driver to sync the new secret version")
|
||||
err = utils.WaitForSPCPSVersionChange(ctx, csiClient, testNamespace, spcpsName,
|
||||
initialVersion, 10*time.Second)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for workload to be reloaded")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, testNamespace, workloadName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "%s should reload with SPC annotation on pod template", workloadType)
|
||||
},
|
||||
Entry("Deployment", Label("csi"), utils.WorkloadDeployment),
|
||||
Entry("DaemonSet", Label("csi"), utils.WorkloadDaemonSet),
|
||||
Entry("StatefulSet", Label("csi"), utils.WorkloadStatefulSet),
|
||||
Entry("ArgoRollout", Label("csi", "argo"), utils.WorkloadArgoRollout),
|
||||
Entry("DeploymentConfig", Label("csi", "openshift"), utils.WorkloadDeploymentConfig))
|
||||
|
||||
DescribeTable("should reload when secretproviderclass auto annotation is on pod template only",
|
||||
func(workloadType utils.WorkloadType) {
|
||||
if !utils.IsCSIDriverInstalled(ctx, csiClient) {
|
||||
Skip("CSI secrets store driver not installed")
|
||||
}
|
||||
if !utils.IsVaultProviderInstalled(ctx, kubeClient) {
|
||||
Skip("Vault CSI provider not installed")
|
||||
}
|
||||
|
||||
adapter := registry.Get(workloadType)
|
||||
if adapter == nil {
|
||||
Skip(fmt.Sprintf("%s adapter not available (CRD not installed)", workloadType))
|
||||
}
|
||||
|
||||
By("Creating a secret in Vault")
|
||||
err := utils.CreateVaultSecret(ctx, kubeClient, restConfig, vaultSecretPath,
|
||||
map[string]string{"api_key": "initial-value-v1"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating a SecretProviderClass pointing to Vault secret")
|
||||
_, err = utils.CreateSecretProviderClassWithSecret(ctx, csiClient, testNamespace, spcName,
|
||||
vaultSecretPath, "api_key")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating workload with SPC auto annotation on pod template ONLY")
|
||||
err = adapter.Create(ctx, testNamespace, workloadName, utils.WorkloadConfig{
|
||||
SPCName: spcName,
|
||||
UseCSIVolume: true,
|
||||
PodTemplateAnnotations: utils.BuildSecretProviderClassAutoAnnotation(),
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
DeferCleanup(func() { _ = adapter.Delete(ctx, testNamespace, workloadName) })
|
||||
|
||||
By("Waiting for workload to be ready")
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.DeploymentReady)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Finding the SPCPS created by CSI driver")
|
||||
spcpsName, err := utils.FindSPCPSForDeployment(ctx, csiClient, kubeClient, testNamespace,
|
||||
workloadName, utils.DeploymentReady)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Getting initial SPCPS version")
|
||||
initialVersion, err := utils.GetSPCPSVersion(ctx, csiClient, testNamespace, spcpsName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Updating the Vault secret")
|
||||
err = utils.UpdateVaultSecret(ctx, kubeClient, restConfig, vaultSecretPath,
|
||||
map[string]string{"api_key": "updated-value-v2"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for CSI driver to sync the new secret version")
|
||||
err = utils.WaitForSPCPSVersionChange(ctx, csiClient, testNamespace, spcpsName,
|
||||
initialVersion, 10*time.Second)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for workload to be reloaded")
|
||||
reloaded, err := adapter.WaitReloaded(ctx, testNamespace, workloadName,
|
||||
utils.AnnotationLastReloadedFrom, utils.ReloadTimeout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "%s should reload with SPC auto on pod template", workloadType)
|
||||
},
|
||||
Entry("Deployment", Label("csi"), utils.WorkloadDeployment),
|
||||
Entry("DaemonSet", Label("csi"), utils.WorkloadDaemonSet),
|
||||
Entry("StatefulSet", Label("csi"), utils.WorkloadStatefulSet),
|
||||
Entry("ArgoRollout", Label("csi", "argo"), utils.WorkloadArgoRollout),
|
||||
Entry("DeploymentConfig", Label("csi", "openshift"), utils.WorkloadDeploymentConfig))
|
||||
|
||||
DescribeTable("should reload when annotations are on both workload and pod template",
|
||||
func(workloadType utils.WorkloadType) {
|
||||
adapter := registry.Get(workloadType)
|
||||
if adapter == nil {
|
||||
Skip(fmt.Sprintf("%s adapter not available (CRD not installed)", workloadType))
|
||||
}
|
||||
|
||||
By("Creating a ConfigMap")
|
||||
_, err := utils.CreateConfigMap(ctx, kubeClient, testNamespace, configMapName,
|
||||
map[string]string{"key": "initial"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating workload with annotations on BOTH workload metadata and pod template")
|
||||
err = adapter.Create(ctx, testNamespace, workloadName, utils.WorkloadConfig{
|
||||
ConfigMapName: configMapName,
|
||||
UseConfigMapEnvFrom: true,
|
||||
Annotations: utils.BuildConfigMapReloadAnnotation(configMapName),
|
||||
PodTemplateAnnotations: utils.BuildConfigMapReloadAnnotation(configMapName),
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
DeferCleanup(func() { _ = adapter.Delete(ctx, testNamespace, workloadName) })
|
||||
|
||||
By("Waiting for workload to be ready")
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.DeploymentReady)
|
||||
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)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeTrue(), "%s should reload with annotations on both locations", workloadType)
|
||||
},
|
||||
Entry("Deployment", utils.WorkloadDeployment),
|
||||
Entry("DaemonSet", utils.WorkloadDaemonSet),
|
||||
Entry("StatefulSet", utils.WorkloadStatefulSet),
|
||||
Entry("ArgoRollout", Label("argo"), utils.WorkloadArgoRollout),
|
||||
Entry("DeploymentConfig", Label("openshift"), utils.WorkloadDeploymentConfig))
|
||||
|
||||
DescribeTable("should NOT reload when pod template has ConfigMap annotation but Secret is updated",
|
||||
func(workloadType utils.WorkloadType) {
|
||||
adapter := registry.Get(workloadType)
|
||||
if adapter == nil {
|
||||
Skip(fmt.Sprintf("%s adapter not available (CRD not installed)", workloadType))
|
||||
}
|
||||
|
||||
By("Creating a ConfigMap and Secret")
|
||||
_, err := utils.CreateConfigMap(ctx, kubeClient, testNamespace, configMapName,
|
||||
map[string]string{"key": "value"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
_, err = utils.CreateSecretFromStrings(ctx, kubeClient, testNamespace, secretName,
|
||||
map[string]string{"password": "initial"}, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating workload with ConfigMap annotation on pod template but using Secret")
|
||||
err = adapter.Create(ctx, testNamespace, workloadName, utils.WorkloadConfig{
|
||||
SecretName: secretName,
|
||||
UseSecretEnvFrom: true,
|
||||
PodTemplateAnnotations: utils.BuildConfigMapReloadAnnotation(configMapName),
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
DeferCleanup(func() { _ = adapter.Delete(ctx, testNamespace, workloadName) })
|
||||
|
||||
By("Waiting for workload to be ready")
|
||||
err = adapter.WaitReady(ctx, testNamespace, workloadName, utils.DeploymentReady)
|
||||
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)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(reloaded).To(BeFalse(), "%s should NOT reload when updating different resource than annotated", workloadType)
|
||||
},
|
||||
Entry("Deployment", utils.WorkloadDeployment),
|
||||
Entry("DaemonSet", utils.WorkloadDaemonSet),
|
||||
Entry("StatefulSet", utils.WorkloadStatefulSet),
|
||||
Entry("ArgoRollout", Label("argo"), utils.WorkloadArgoRollout),
|
||||
Entry("DeploymentConfig", Label("openshift"), utils.WorkloadDeploymentConfig))
|
||||
})
|
||||
})
|
||||
|
||||
// ============================================================
|
||||
@@ -998,7 +1362,9 @@ var _ = Describe("Workload Reload Tests", func() {
|
||||
Expect(found).To(BeTrue(), "%s should have STAKATER_ env var after Vault secret change", workloadType)
|
||||
}, Entry("Deployment", Label("csi"), utils.WorkloadDeployment),
|
||||
Entry("DaemonSet", Label("csi"), utils.WorkloadDaemonSet),
|
||||
Entry("StatefulSet", Label("csi"), utils.WorkloadStatefulSet))
|
||||
Entry("StatefulSet", Label("csi"), utils.WorkloadStatefulSet),
|
||||
Entry("ArgoRollout", Label("csi", "argo"), utils.WorkloadArgoRollout),
|
||||
Entry("DeploymentConfig", Label("csi", "openshift"), utils.WorkloadDeploymentConfig))
|
||||
|
||||
// Negative tests for env var strategy
|
||||
DescribeTable("should NOT add STAKATER_ env var when only ConfigMap labels change",
|
||||
@@ -1148,7 +1514,9 @@ var _ = Describe("Workload Reload Tests", func() {
|
||||
workloadType)
|
||||
}, Entry("Deployment", Label("csi"), utils.WorkloadDeployment),
|
||||
Entry("DaemonSet", Label("csi"), utils.WorkloadDaemonSet),
|
||||
Entry("StatefulSet", Label("csi"), utils.WorkloadStatefulSet))
|
||||
Entry("StatefulSet", Label("csi"), utils.WorkloadStatefulSet),
|
||||
Entry("ArgoRollout", Label("csi", "argo"), utils.WorkloadArgoRollout),
|
||||
Entry("DeploymentConfig", Label("csi", "openshift"), utils.WorkloadDeploymentConfig))
|
||||
|
||||
// CSI auto annotation with EnvVar strategy and real Vault
|
||||
It("should add STAKATER_ env var with secretproviderclass auto annotation", Label("csi"), func() {
|
||||
|
||||
@@ -245,9 +245,10 @@ func execInVaultPod(ctx context.Context, kubeClient kubernetes.Interface, restCo
|
||||
return fmt.Errorf("creating executor: %w", err)
|
||||
}
|
||||
|
||||
var stderr bytes.Buffer
|
||||
var stdout, stderr bytes.Buffer
|
||||
err = exec.StreamWithContext(
|
||||
ctx, remotecommand.StreamOptions{
|
||||
Stdout: &stdout,
|
||||
Stderr: &stderr,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -193,9 +193,21 @@ func AddInitContainerWithVolumes(spec *corev1.PodSpec, cmName, secretName string
|
||||
spec.InitContainers = append(spec.InitContainers, init)
|
||||
}
|
||||
|
||||
// ApplyWorkloadConfig applies all WorkloadConfig settings to a PodSpec.
|
||||
// This single function replaces all the duplicate buildXxxOptions functions.
|
||||
func ApplyWorkloadConfig(spec *corev1.PodSpec, cfg WorkloadConfig) {
|
||||
// ApplyWorkloadConfig applies all WorkloadConfig settings to a PodTemplateSpec.
|
||||
// This includes both pod template annotations and pod spec configuration.
|
||||
func ApplyWorkloadConfig(template *corev1.PodTemplateSpec, cfg WorkloadConfig) {
|
||||
// Apply pod template annotations
|
||||
if len(cfg.PodTemplateAnnotations) > 0 {
|
||||
if template.Annotations == nil {
|
||||
template.Annotations = make(map[string]string)
|
||||
}
|
||||
for k, v := range cfg.PodTemplateAnnotations {
|
||||
template.Annotations[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Apply pod spec configuration
|
||||
spec := &template.Spec
|
||||
if cfg.UseConfigMapEnvFrom && cfg.ConfigMapName != "" {
|
||||
AddEnvFromSource(spec, 0, cfg.ConfigMapName, false)
|
||||
}
|
||||
|
||||
@@ -897,6 +897,35 @@ func WithJobSecretKeyRef(secretName, key, envVarName string) JobOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithJobCSIVolume adds a CSI volume referencing a SecretProviderClass to a Job.
|
||||
func WithJobCSIVolume(spcName string) JobOption {
|
||||
return func(j *batchv1.Job) {
|
||||
volumeName := csiVolumeName(spcName)
|
||||
mountPath := csiMountPath(spcName)
|
||||
|
||||
j.Spec.Template.Spec.Volumes = append(j.Spec.Template.Spec.Volumes, corev1.Volume{
|
||||
Name: volumeName,
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
CSI: &corev1.CSIVolumeSource{
|
||||
Driver: CSIDriverName,
|
||||
ReadOnly: ptr.To(true),
|
||||
VolumeAttributes: map[string]string{
|
||||
"secretProviderClass": spcName,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
j.Spec.Template.Spec.Containers[0].VolumeMounts = append(
|
||||
j.Spec.Template.Spec.Containers[0].VolumeMounts,
|
||||
corev1.VolumeMount{
|
||||
Name: volumeName,
|
||||
MountPath: mountPath,
|
||||
ReadOnly: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// baseJobResource creates a base Job template.
|
||||
func baseJobResource(namespace, name string) *batchv1.Job {
|
||||
labels := map[string]string{"app": name}
|
||||
|
||||
@@ -288,6 +288,26 @@ func WaitForJobExists(ctx context.Context, client kubernetes.Interface, namespac
|
||||
)
|
||||
}
|
||||
|
||||
// WaitForJobReady waits for a Job to have at least one active or succeeded pod.
|
||||
// This ensures the Job has actually started running before proceeding.
|
||||
func WaitForJobReady(ctx context.Context, client kubernetes.Interface, namespace, name string, timeout time.Duration) error {
|
||||
return wait.PollUntilContextTimeout(
|
||||
ctx, DefaultInterval, timeout, true, func(ctx context.Context) (bool, error) {
|
||||
job, err := client.BatchV1().Jobs(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Job is ready if it has at least one active or succeeded pod
|
||||
if job.Status.Active > 0 || job.Status.Succeeded > 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// GetPodLogs retrieves logs from pods matching the given label selector.
|
||||
func GetPodLogs(ctx context.Context, client kubernetes.Interface, namespace, labelSelector string) (string, error) {
|
||||
pods, err := client.CoreV1().Pods(namespace).List(
|
||||
|
||||
@@ -33,7 +33,8 @@ type WorkloadConfig struct {
|
||||
ConfigMapName string
|
||||
SecretName string
|
||||
SPCName string
|
||||
Annotations map[string]string
|
||||
Annotations map[string]string // Annotations for workload metadata (e.g., Deployment.metadata.annotations)
|
||||
PodTemplateAnnotations map[string]string // Annotations for pod template metadata (e.g., Deployment.spec.template.metadata.annotations)
|
||||
UseConfigMapEnvFrom bool
|
||||
UseSecretEnvFrom bool
|
||||
UseConfigMapVolume bool
|
||||
|
||||
@@ -116,7 +116,7 @@ func buildRolloutOptions(cfg WorkloadConfig) []RolloutOption {
|
||||
r.Annotations[k] = v
|
||||
}
|
||||
}
|
||||
ApplyWorkloadConfig(&r.Spec.Template.Spec, cfg)
|
||||
ApplyWorkloadConfig(&r.Spec.Template, cfg)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,8 @@ func buildCronJobOptions(cfg WorkloadConfig) []CronJobOption {
|
||||
cj.Annotations[k] = v
|
||||
}
|
||||
}
|
||||
ApplyWorkloadConfig(&cj.Spec.JobTemplate.Spec.Template.Spec, cfg)
|
||||
// CronJob has nested JobTemplate
|
||||
ApplyWorkloadConfig(&cj.Spec.JobTemplate.Spec.Template, cfg)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func buildDaemonSetOptions(cfg WorkloadConfig) []DaemonSetOption {
|
||||
ds.Annotations[k] = v
|
||||
}
|
||||
}
|
||||
ApplyWorkloadConfig(&ds.Spec.Template.Spec, cfg)
|
||||
ApplyWorkloadConfig(&ds.Spec.Template, cfg)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func buildDeploymentOptions(cfg WorkloadConfig) []DeploymentOption {
|
||||
d.Annotations[k] = v
|
||||
}
|
||||
}
|
||||
ApplyWorkloadConfig(&d.Spec.Template.Spec, cfg)
|
||||
ApplyWorkloadConfig(&d.Spec.Template, cfg)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ func buildJobOptions(cfg WorkloadConfig) []JobOption {
|
||||
job.Annotations[k] = v
|
||||
}
|
||||
}
|
||||
ApplyWorkloadConfig(&job.Spec.Template.Spec, cfg)
|
||||
ApplyWorkloadConfig(&job.Spec.Template, cfg)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ func buildDeploymentConfigOptions(cfg WorkloadConfig) []DCOption {
|
||||
}
|
||||
}
|
||||
if dc.Spec.Template != nil {
|
||||
ApplyWorkloadConfig(&dc.Spec.Template.Spec, cfg)
|
||||
ApplyWorkloadConfig(dc.Spec.Template, cfg)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func buildStatefulSetOptions(cfg WorkloadConfig) []StatefulSetOption {
|
||||
sts.Annotations[k] = v
|
||||
}
|
||||
}
|
||||
ApplyWorkloadConfig(&sts.Spec.Template.Spec, cfg)
|
||||
ApplyWorkloadConfig(&sts.Spec.Template, cfg)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user