|
|
@@ -19,211 +19,438 @@ import (
|
|
|
"time"
|
|
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
|
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
"k8s.io/apimachinery/pkg/types"
|
|
|
|
|
|
esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
|
|
|
-
|
|
|
+ esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
|
|
|
. "github.com/onsi/ginkgo/v2"
|
|
|
. "github.com/onsi/gomega"
|
|
|
)
|
|
|
|
|
|
type testCase struct {
|
|
|
store esapi.GenericStore
|
|
|
+ ps *esv1alpha1.PushSecret
|
|
|
assert func()
|
|
|
}
|
|
|
|
|
|
-var _ = Describe("SecretStore reconcile", func() {
|
|
|
+const (
|
|
|
+ defaultStoreName = "default-store"
|
|
|
+ defaultControllerClass = "test-ctrl"
|
|
|
+)
|
|
|
+
|
|
|
+var _ = Describe("SecretStore Controller", func() {
|
|
|
var test *testCase
|
|
|
|
|
|
BeforeEach(func() {
|
|
|
test = makeDefaultTestcase()
|
|
|
})
|
|
|
|
|
|
- AfterEach(func() {
|
|
|
- Expect(k8sClient.Delete(context.Background(), test.store)).ToNot(HaveOccurred())
|
|
|
+ Context("Reconcile Logic", func() {
|
|
|
+ AfterEach(func() {
|
|
|
+ Expect(k8sClient.Delete(context.Background(), test.store)).ToNot(HaveOccurred())
|
|
|
+ })
|
|
|
+
|
|
|
+ // an invalid provider config should be reflected
|
|
|
+ // in the store status condition
|
|
|
+ invalidProvider := func(tc *testCase) {
|
|
|
+ tc.assert = func() {
|
|
|
+ Eventually(func() bool {
|
|
|
+ ss := tc.store.Copy()
|
|
|
+ err := k8sClient.Get(context.Background(), types.NamespacedName{
|
|
|
+ Name: defaultStoreName,
|
|
|
+ Namespace: ss.GetObjectMeta().Namespace,
|
|
|
+ }, ss)
|
|
|
+ if err != nil {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ status := ss.GetStatus()
|
|
|
+ if len(status.Conditions) != 1 {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return status.Conditions[0].Reason == esapi.ReasonInvalidProviderConfig &&
|
|
|
+ hasEvent(tc.store.GetTypeMeta().Kind, ss.GetName(), esapi.ReasonInvalidProviderConfig)
|
|
|
+ }).
|
|
|
+ WithTimeout(time.Second * 10).
|
|
|
+ WithPolling(time.Second).
|
|
|
+ Should(BeTrue())
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // if controllerClass does not match the controller
|
|
|
+ // should not touch this store
|
|
|
+ ignoreControllerClass := func(tc *testCase) {
|
|
|
+ spc := tc.store.GetSpec()
|
|
|
+ spc.Controller = "something-else"
|
|
|
+ tc.assert = func() {
|
|
|
+ Consistently(func() bool {
|
|
|
+ ss := tc.store.Copy()
|
|
|
+ err := k8sClient.Get(context.Background(), types.NamespacedName{
|
|
|
+ Name: defaultStoreName,
|
|
|
+ Namespace: ss.GetObjectMeta().Namespace,
|
|
|
+ }, ss)
|
|
|
+ if err != nil {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ return len(ss.GetStatus().Conditions) == 0
|
|
|
+ }).
|
|
|
+ WithTimeout(time.Second * 3).
|
|
|
+ WithPolling(time.Millisecond * 500).
|
|
|
+ Should(BeTrue())
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ validProvider := func(tc *testCase) {
|
|
|
+ spc := tc.store.GetSpec()
|
|
|
+ spc.Provider.Vault = nil
|
|
|
+ spc.Provider.Fake = &esapi.FakeProvider{
|
|
|
+ Data: []esapi.FakeProviderData{},
|
|
|
+ }
|
|
|
+
|
|
|
+ tc.assert = func() {
|
|
|
+ Eventually(func() bool {
|
|
|
+ ss := tc.store.Copy()
|
|
|
+ err := k8sClient.Get(context.Background(), types.NamespacedName{
|
|
|
+ Name: defaultStoreName,
|
|
|
+ Namespace: ss.GetNamespace(),
|
|
|
+ }, ss)
|
|
|
+ if err != nil {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(ss.GetStatus().Conditions) != 1 {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ return ss.GetStatus().Conditions[0].Reason == esapi.ReasonStoreValid &&
|
|
|
+ ss.GetStatus().Conditions[0].Type == esapi.SecretStoreReady &&
|
|
|
+ ss.GetStatus().Conditions[0].Status == corev1.ConditionTrue &&
|
|
|
+ hasEvent(tc.store.GetTypeMeta().Kind, ss.GetName(), esapi.ReasonStoreValid)
|
|
|
+ }).
|
|
|
+ WithTimeout(time.Second * 10).
|
|
|
+ WithPolling(time.Second).
|
|
|
+ Should(BeTrue())
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ readWrite := func(tc *testCase) {
|
|
|
+ spc := tc.store.GetSpec()
|
|
|
+ spc.Provider.Vault = nil
|
|
|
+ spc.Provider.Fake = &esapi.FakeProvider{
|
|
|
+ Data: []esapi.FakeProviderData{},
|
|
|
+ }
|
|
|
+
|
|
|
+ tc.assert = func() {
|
|
|
+ Eventually(func() bool {
|
|
|
+ ss := tc.store.Copy()
|
|
|
+ err := k8sClient.Get(context.Background(), types.NamespacedName{
|
|
|
+ Name: defaultStoreName,
|
|
|
+ Namespace: ss.GetNamespace(),
|
|
|
+ }, ss)
|
|
|
+ if err != nil {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ if ss.GetStatus().Capabilities != esapi.SecretStoreReadWrite {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ return true
|
|
|
+ }).
|
|
|
+ WithTimeout(time.Second * 10).
|
|
|
+ WithPolling(time.Second).
|
|
|
+ Should(BeTrue())
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // an unknown store validation result should be reflected
|
|
|
+ // in the store status condition
|
|
|
+ validationUnknown := func(tc *testCase) {
|
|
|
+ spc := tc.store.GetSpec()
|
|
|
+ spc.Provider.Vault = nil
|
|
|
+ validationResultUnknown := esapi.ValidationResultUnknown
|
|
|
+ spc.Provider.Fake = &esapi.FakeProvider{
|
|
|
+ Data: []esapi.FakeProviderData{},
|
|
|
+ ValidationResult: &validationResultUnknown,
|
|
|
+ }
|
|
|
+
|
|
|
+ tc.assert = func() {
|
|
|
+ Eventually(func() bool {
|
|
|
+ ss := tc.store.Copy()
|
|
|
+ err := k8sClient.Get(context.Background(), types.NamespacedName{
|
|
|
+ Name: defaultStoreName,
|
|
|
+ Namespace: ss.GetNamespace(),
|
|
|
+ }, ss)
|
|
|
+ if err != nil {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(ss.GetStatus().Conditions) != 1 {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ return ss.GetStatus().Conditions[0].Reason == esapi.ReasonValidationUnknown &&
|
|
|
+ ss.GetStatus().Conditions[0].Type == esapi.SecretStoreReady &&
|
|
|
+ ss.GetStatus().Conditions[0].Status == corev1.ConditionTrue &&
|
|
|
+ hasEvent(tc.store.GetTypeMeta().Kind, ss.GetName(), esapi.ReasonValidationUnknown)
|
|
|
+ }).
|
|
|
+ WithTimeout(time.Second * 5).
|
|
|
+ WithPolling(time.Second).
|
|
|
+ Should(BeTrue())
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ DescribeTable("Provider Configuration", func(muts ...func(tc *testCase)) {
|
|
|
+ for _, mut := range muts {
|
|
|
+ mut(test)
|
|
|
+ }
|
|
|
+ err := k8sClient.Create(context.Background(), test.store.Copy())
|
|
|
+ Expect(err).ToNot(HaveOccurred())
|
|
|
+ test.assert()
|
|
|
+ },
|
|
|
+ // Namespaced store tests
|
|
|
+ Entry("[namespace] invalid provider should set InvalidStore condition", invalidProvider),
|
|
|
+ Entry("[namespace] should ignore stores with non-matching controller class", ignoreControllerClass),
|
|
|
+ Entry("[namespace] valid provider should have status=ready", validProvider),
|
|
|
+ Entry("[namespace] valid provider should have capabilities=ReadWrite", readWrite),
|
|
|
+ Entry("[cluster] validation unknown status should set ValidationUnknown condition", validationUnknown),
|
|
|
+
|
|
|
+ // Cluster store tests
|
|
|
+ Entry("[cluster] invalid provider should set InvalidStore condition", invalidProvider, useClusterStore),
|
|
|
+ Entry("[cluster] should ignore stores with non-matching controller class", ignoreControllerClass, useClusterStore),
|
|
|
+ Entry("[cluster] valid provider should have status=ready", validProvider, useClusterStore),
|
|
|
+ Entry("[cluster] valid provider should have capabilities=ReadWrite", readWrite, useClusterStore),
|
|
|
+ Entry("[cluster] validation unknown status should set ValidationUnknown condition", validationUnknown, useClusterStore),
|
|
|
+ )
|
|
|
})
|
|
|
|
|
|
- // an invalid provider config should be reflected
|
|
|
- // in the store status condition
|
|
|
- invalidProvider := func(tc *testCase) {
|
|
|
- tc.assert = func() {
|
|
|
- Eventually(func() bool {
|
|
|
- ss := tc.store.Copy()
|
|
|
- err := k8sClient.Get(context.Background(), types.NamespacedName{
|
|
|
- Name: defaultStoreName,
|
|
|
- Namespace: ss.GetObjectMeta().Namespace,
|
|
|
- }, ss)
|
|
|
- if err != nil {
|
|
|
- return false
|
|
|
- }
|
|
|
- status := ss.GetStatus()
|
|
|
- if len(status.Conditions) != 1 {
|
|
|
- return false
|
|
|
- }
|
|
|
- return status.Conditions[0].Reason == esapi.ReasonInvalidProviderConfig &&
|
|
|
- hasEvent(tc.store.GetTypeMeta().Kind, ss.GetName(), esapi.ReasonInvalidProviderConfig)
|
|
|
+ Context("Finalizer Management", func() {
|
|
|
+ BeforeEach(func() {
|
|
|
+ // Setup valid provider for finalizer tests
|
|
|
+ spc := test.store.GetSpec()
|
|
|
+ spc.Provider.Vault = nil
|
|
|
+ spc.Provider.Fake = &esapi.FakeProvider{
|
|
|
+ Data: []esapi.FakeProviderData{},
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ AfterEach(func() {
|
|
|
+ cleanupResources(test)
|
|
|
+ })
|
|
|
+
|
|
|
+ DescribeTable("Finalizer Addition", func(muts ...func(tc *testCase)) {
|
|
|
+ for _, mut := range muts {
|
|
|
+ mut(test)
|
|
|
+ }
|
|
|
+
|
|
|
+ Expect(k8sClient.Create(context.Background(), test.store)).ToNot(HaveOccurred())
|
|
|
+ Expect(k8sClient.Create(context.Background(), test.ps)).ToNot(HaveOccurred())
|
|
|
+
|
|
|
+ Eventually(func() []string {
|
|
|
+ return getStoreFinalizers(test.store)
|
|
|
}).
|
|
|
WithTimeout(time.Second * 10).
|
|
|
WithPolling(time.Second).
|
|
|
- Should(BeTrue())
|
|
|
- }
|
|
|
- }
|
|
|
+ Should(ContainElement(secretStoreFinalizer))
|
|
|
+ },
|
|
|
+ Entry("[namespace] should add finalizer when PushSecret with DeletionPolicy=Delete is created", usePushSecret),
|
|
|
+ Entry("[cluster] should add finalizer when PushSecret with DeletionPolicy=Delete is created", usePushSecret, useClusterStore),
|
|
|
+ )
|
|
|
|
|
|
- // if controllerClass does not match the controller
|
|
|
- // should not touch this store
|
|
|
- ignoreControllerClass := func(tc *testCase) {
|
|
|
- spc := tc.store.GetSpec()
|
|
|
- spc.Controller = "something-else"
|
|
|
- tc.assert = func() {
|
|
|
- Consistently(func() bool {
|
|
|
- ss := tc.store.Copy()
|
|
|
- err := k8sClient.Get(context.Background(), types.NamespacedName{
|
|
|
- Name: defaultStoreName,
|
|
|
- Namespace: ss.GetObjectMeta().Namespace,
|
|
|
- }, ss)
|
|
|
- if err != nil {
|
|
|
- return true
|
|
|
- }
|
|
|
- return len(ss.GetStatus().Conditions) == 0
|
|
|
+ DescribeTable("Finalizer Removal on PushSecret Deletion", func(muts ...func(tc *testCase)) {
|
|
|
+ for _, mut := range muts {
|
|
|
+ mut(test)
|
|
|
+ }
|
|
|
+
|
|
|
+ test.store.SetFinalizers([]string{secretStoreFinalizer})
|
|
|
+ Expect(k8sClient.Create(context.Background(), test.store)).ToNot(HaveOccurred())
|
|
|
+ Expect(k8sClient.Create(context.Background(), test.ps)).ToNot(HaveOccurred())
|
|
|
+ Expect(k8sClient.Delete(context.Background(), test.ps)).ToNot(HaveOccurred())
|
|
|
+
|
|
|
+ Eventually(func() []string {
|
|
|
+ return getStoreFinalizers(test.store)
|
|
|
+ }).
|
|
|
+ WithTimeout(time.Second * 10).
|
|
|
+ WithPolling(time.Second).
|
|
|
+ ShouldNot(ContainElement(secretStoreFinalizer))
|
|
|
+ },
|
|
|
+ Entry("[namespace] should remove finalizer when PushSecret is deleted", usePushSecret),
|
|
|
+ Entry("[cluster] should remove finalizer when PushSecret is deleted", usePushSecret, useClusterStore),
|
|
|
+ )
|
|
|
+
|
|
|
+ DescribeTable("Store Deletion Prevention", func(muts ...func(tc *testCase)) {
|
|
|
+ for _, mut := range muts {
|
|
|
+ mut(test)
|
|
|
+ }
|
|
|
+
|
|
|
+ Expect(k8sClient.Create(context.Background(), test.store)).ToNot(HaveOccurred())
|
|
|
+ Expect(k8sClient.Create(context.Background(), test.ps)).ToNot(HaveOccurred())
|
|
|
+
|
|
|
+ // Wait for finalizer to be added
|
|
|
+ Eventually(func() []string {
|
|
|
+ return getStoreFinalizers(test.store)
|
|
|
+ }).
|
|
|
+ WithTimeout(time.Second * 10).
|
|
|
+ WithPolling(time.Second).
|
|
|
+ Should(ContainElement(secretStoreFinalizer))
|
|
|
+
|
|
|
+ Expect(k8sClient.Delete(context.Background(), test.store)).ToNot(HaveOccurred())
|
|
|
+
|
|
|
+ Consistently(func() []string {
|
|
|
+ return getStoreFinalizers(test.store)
|
|
|
}).
|
|
|
WithTimeout(time.Second * 3).
|
|
|
WithPolling(time.Millisecond * 500).
|
|
|
- Should(BeTrue())
|
|
|
- }
|
|
|
- }
|
|
|
+ Should(ContainElement(secretStoreFinalizer))
|
|
|
+ },
|
|
|
+ Entry("[namespace] should prevent deletion when finalizer exists", usePushSecret),
|
|
|
+ Entry("[cluster] should prevent deletion when finalizer exists", usePushSecret, useClusterStore),
|
|
|
+ )
|
|
|
|
|
|
- validProvider := func(tc *testCase) {
|
|
|
- spc := tc.store.GetSpec()
|
|
|
- spc.Provider.Vault = nil
|
|
|
- spc.Provider.Fake = &esapi.FakeProvider{
|
|
|
- Data: []esapi.FakeProviderData{},
|
|
|
- }
|
|
|
+ DescribeTable("Complete Deletion Flow", func(muts ...func(tc *testCase)) {
|
|
|
+ for _, mut := range muts {
|
|
|
+ mut(test)
|
|
|
+ }
|
|
|
+
|
|
|
+ Expect(k8sClient.Create(context.Background(), test.store)).ToNot(HaveOccurred())
|
|
|
+ Expect(k8sClient.Create(context.Background(), test.ps)).ToNot(HaveOccurred())
|
|
|
+ Expect(k8sClient.Delete(context.Background(), test.store)).ToNot(HaveOccurred())
|
|
|
+ Expect(k8sClient.Delete(context.Background(), test.ps)).ToNot(HaveOccurred())
|
|
|
|
|
|
- tc.assert = func() {
|
|
|
Eventually(func() bool {
|
|
|
- ss := tc.store.Copy()
|
|
|
err := k8sClient.Get(context.Background(), types.NamespacedName{
|
|
|
- Name: defaultStoreName,
|
|
|
- Namespace: ss.GetNamespace(),
|
|
|
- }, ss)
|
|
|
- if err != nil {
|
|
|
- return false
|
|
|
- }
|
|
|
-
|
|
|
- if len(ss.GetStatus().Conditions) != 1 {
|
|
|
- return false
|
|
|
- }
|
|
|
-
|
|
|
- return ss.GetStatus().Conditions[0].Reason == esapi.ReasonStoreValid &&
|
|
|
- ss.GetStatus().Conditions[0].Type == esapi.SecretStoreReady &&
|
|
|
- ss.GetStatus().Conditions[0].Status == corev1.ConditionTrue &&
|
|
|
- hasEvent(tc.store.GetTypeMeta().Kind, ss.GetName(), esapi.ReasonStoreValid)
|
|
|
+ Name: test.store.GetName(),
|
|
|
+ Namespace: test.store.GetNamespace(),
|
|
|
+ }, test.store)
|
|
|
+ return apierrors.IsNotFound(err)
|
|
|
}).
|
|
|
WithTimeout(time.Second * 10).
|
|
|
WithPolling(time.Second).
|
|
|
Should(BeTrue())
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
+ },
|
|
|
+ Entry("[namespace] should allow deletion when both Store and PushSecret are deleted", usePushSecret),
|
|
|
+ Entry("[cluster] should allow deletion when both Store and PushSecret are deleted", usePushSecret, useClusterStore),
|
|
|
+ )
|
|
|
|
|
|
- readWrite := func(tc *testCase) {
|
|
|
- spc := tc.store.GetSpec()
|
|
|
- spc.Provider.Vault = nil
|
|
|
- spc.Provider.Fake = &esapi.FakeProvider{
|
|
|
- Data: []esapi.FakeProviderData{},
|
|
|
- }
|
|
|
+ DescribeTable("Multiple PushSecrets Scenario", func(muts ...func(tc *testCase)) {
|
|
|
+ for _, mut := range muts {
|
|
|
+ mut(test)
|
|
|
+ }
|
|
|
|
|
|
- tc.assert = func() {
|
|
|
- Eventually(func() bool {
|
|
|
- ss := tc.store.Copy()
|
|
|
- err := k8sClient.Get(context.Background(), types.NamespacedName{
|
|
|
- Name: defaultStoreName,
|
|
|
- Namespace: ss.GetNamespace(),
|
|
|
- }, ss)
|
|
|
- if err != nil {
|
|
|
- return false
|
|
|
- }
|
|
|
+ ps2 := test.ps.DeepCopy()
|
|
|
+ ps2.Name = "push-secret-2"
|
|
|
|
|
|
- if ss.GetStatus().Capabilities != esapi.SecretStoreReadWrite {
|
|
|
- return false
|
|
|
- }
|
|
|
+ Expect(k8sClient.Create(context.Background(), test.store)).ToNot(HaveOccurred())
|
|
|
+ Expect(k8sClient.Create(context.Background(), test.ps)).ToNot(HaveOccurred())
|
|
|
+ Expect(k8sClient.Create(context.Background(), ps2)).ToNot(HaveOccurred())
|
|
|
|
|
|
- return true
|
|
|
+ // Wait for finalizer to be added
|
|
|
+ Eventually(func() []string {
|
|
|
+ return getStoreFinalizers(test.store)
|
|
|
}).
|
|
|
WithTimeout(time.Second * 10).
|
|
|
WithPolling(time.Second).
|
|
|
- Should(BeTrue())
|
|
|
- }
|
|
|
+ Should(ContainElement(secretStoreFinalizer))
|
|
|
|
|
|
- }
|
|
|
+ Expect(k8sClient.Delete(context.Background(), test.ps)).ToNot(HaveOccurred())
|
|
|
|
|
|
- // an unknown store validation result should be reflected
|
|
|
- // in the store status condition
|
|
|
- validationUnknown := func(tc *testCase) {
|
|
|
- spc := tc.store.GetSpec()
|
|
|
- spc.Provider.Vault = nil
|
|
|
- validationResultUnknown := esapi.ValidationResultUnknown
|
|
|
- spc.Provider.Fake = &esapi.FakeProvider{
|
|
|
- Data: []esapi.FakeProviderData{},
|
|
|
- ValidationResult: &validationResultUnknown,
|
|
|
- }
|
|
|
+ // Finalizer should remain because ps2 still exists
|
|
|
+ Consistently(func() []string {
|
|
|
+ return getStoreFinalizers(test.store)
|
|
|
+ }).
|
|
|
+ WithTimeout(time.Second * 3).
|
|
|
+ WithPolling(time.Millisecond * 500).
|
|
|
+ Should(ContainElement(secretStoreFinalizer))
|
|
|
|
|
|
- tc.assert = func() {
|
|
|
- Eventually(func() bool {
|
|
|
- ss := tc.store.Copy()
|
|
|
+ // Cleanup
|
|
|
+ Expect(k8sClient.Delete(context.Background(), ps2)).ToNot(HaveOccurred())
|
|
|
+ },
|
|
|
+ Entry("[namespace] finalizer should remain when other PushSecrets exist", usePushSecret),
|
|
|
+ Entry("[cluster] finalizer should remain when other PushSecrets exist", usePushSecret, useClusterStore),
|
|
|
+ )
|
|
|
+
|
|
|
+ DescribeTable("DeletionPolicy Change", func(muts ...func(tc *testCase)) {
|
|
|
+ for _, mut := range muts {
|
|
|
+ mut(test)
|
|
|
+ }
|
|
|
+
|
|
|
+ Expect(k8sClient.Create(context.Background(), test.store)).ToNot(HaveOccurred())
|
|
|
+ Expect(k8sClient.Create(context.Background(), test.ps)).ToNot(HaveOccurred())
|
|
|
+
|
|
|
+ // Wait for finalizer to be added
|
|
|
+ Eventually(func() []string {
|
|
|
+ return getStoreFinalizers(test.store)
|
|
|
+ }).
|
|
|
+ WithTimeout(time.Second * 10).
|
|
|
+ WithPolling(time.Second).
|
|
|
+ Should(ContainElement(secretStoreFinalizer))
|
|
|
+
|
|
|
+ // Update PushSecret to DeletionPolicy=None
|
|
|
+ Eventually(func() error {
|
|
|
err := k8sClient.Get(context.Background(), types.NamespacedName{
|
|
|
- Name: defaultStoreName,
|
|
|
- Namespace: ss.GetNamespace(),
|
|
|
- }, ss)
|
|
|
- if err != nil {
|
|
|
- return false
|
|
|
- }
|
|
|
-
|
|
|
- if len(ss.GetStatus().Conditions) != 1 {
|
|
|
- return false
|
|
|
- }
|
|
|
-
|
|
|
- return ss.GetStatus().Conditions[0].Reason == esapi.ReasonValidationUnknown &&
|
|
|
- ss.GetStatus().Conditions[0].Type == esapi.SecretStoreReady &&
|
|
|
- ss.GetStatus().Conditions[0].Status == corev1.ConditionTrue &&
|
|
|
- hasEvent(tc.store.GetTypeMeta().Kind, ss.GetName(), esapi.ReasonValidationUnknown)
|
|
|
+ Name: test.ps.Name,
|
|
|
+ Namespace: test.ps.Namespace,
|
|
|
+ }, test.ps)
|
|
|
+ Expect(err).ToNot(HaveOccurred())
|
|
|
+ test.ps.Spec.DeletionPolicy = esv1alpha1.PushSecretDeletionPolicyNone
|
|
|
+ return k8sClient.Update(context.Background(), test.ps)
|
|
|
}).
|
|
|
- WithTimeout(time.Second * 5).
|
|
|
+ WithTimeout(time.Second * 10).
|
|
|
WithPolling(time.Second).
|
|
|
- Should(BeTrue())
|
|
|
+ Should(Succeed())
|
|
|
+
|
|
|
+ Eventually(func() []string {
|
|
|
+ return getStoreFinalizers(test.store)
|
|
|
+ }).
|
|
|
+ WithTimeout(time.Second * 10).
|
|
|
+ WithPolling(time.Second).
|
|
|
+ ShouldNot(ContainElement(secretStoreFinalizer))
|
|
|
+ },
|
|
|
+ Entry("[namespace] should remove finalizer when DeletionPolicy changes to None", usePushSecret),
|
|
|
+ Entry("[cluster] should remove finalizer when DeletionPolicy changes to None", usePushSecret, useClusterStore),
|
|
|
+ )
|
|
|
+ })
|
|
|
+
|
|
|
+})
|
|
|
+
|
|
|
+func cleanupResources(test *testCase) {
|
|
|
+ if test.ps != nil {
|
|
|
+ err := k8sClient.Delete(context.Background(), test.ps)
|
|
|
+ if err != nil && !apierrors.IsNotFound(err) {
|
|
|
+ Expect(err).ToNot(HaveOccurred())
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- DescribeTable("Controller Reconcile logic", func(muts ...func(tc *testCase)) {
|
|
|
- for _, mut := range muts {
|
|
|
- mut(test)
|
|
|
- }
|
|
|
- err := k8sClient.Create(context.Background(), test.store.Copy())
|
|
|
+ err := k8sClient.Delete(context.Background(), test.store)
|
|
|
+ if err != nil && !apierrors.IsNotFound(err) {
|
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
- test.assert()
|
|
|
- },
|
|
|
- // namespaced store
|
|
|
- Entry("[namespace] invalid provider with secretStore should set InvalidStore condition", invalidProvider),
|
|
|
- Entry("[namespace] ignore stores with non-matching class", ignoreControllerClass),
|
|
|
- Entry("[namespace] valid provider has status=ready", validProvider),
|
|
|
- Entry("[namespace] valid provider has capabilities=ReadWrite", readWrite),
|
|
|
- Entry("[namespace] validation unknown status should set ValidationUnknown condition", validationUnknown),
|
|
|
-
|
|
|
- // cluster store
|
|
|
- Entry("[cluster] invalid provider with secretStore should set InvalidStore condition", invalidProvider, useClusterStore),
|
|
|
- Entry("[cluster] ignore stores with non-matching class", ignoreControllerClass, useClusterStore),
|
|
|
- Entry("[cluster] valid provider has status=ready", validProvider, useClusterStore),
|
|
|
- Entry("[cluster] valid provider has capabilities=ReadWrite", readWrite, useClusterStore),
|
|
|
- Entry("[cluster] validation unknown status should set ValidationUnknown condition", validationUnknown, useClusterStore),
|
|
|
- )
|
|
|
+ }
|
|
|
|
|
|
-})
|
|
|
+ Eventually(func() bool {
|
|
|
+ err := k8sClient.Get(context.Background(), types.NamespacedName{
|
|
|
+ Name: test.store.GetName(),
|
|
|
+ Namespace: test.store.GetNamespace(),
|
|
|
+ }, test.store)
|
|
|
+ return apierrors.IsNotFound(err)
|
|
|
+ }).
|
|
|
+ WithTimeout(time.Second * 10).
|
|
|
+ WithPolling(time.Second).
|
|
|
+ Should(BeTrue())
|
|
|
+}
|
|
|
|
|
|
-const (
|
|
|
- defaultStoreName = "default-store"
|
|
|
- defaultControllerClass = "test-ctrl"
|
|
|
-)
|
|
|
+func getStoreFinalizers(store esapi.GenericStore) []string {
|
|
|
+ err := k8sClient.Get(context.Background(), types.NamespacedName{
|
|
|
+ Name: store.GetName(),
|
|
|
+ Namespace: store.GetNamespace(),
|
|
|
+ }, store)
|
|
|
+ if err != nil {
|
|
|
+ return []string{}
|
|
|
+ }
|
|
|
+ return store.GetFinalizers()
|
|
|
+}
|
|
|
|
|
|
func makeDefaultTestcase() *testCase {
|
|
|
return &testCase{
|
|
|
@@ -267,6 +494,32 @@ func useClusterStore(tc *testCase) {
|
|
|
},
|
|
|
Spec: *spc,
|
|
|
}
|
|
|
+
|
|
|
+ if tc.ps != nil {
|
|
|
+ tc.ps.Spec.SecretStoreRefs[0].Kind = esapi.ClusterSecretStoreKind
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func usePushSecret(tc *testCase) {
|
|
|
+ tc.ps = &esv1alpha1.PushSecret{
|
|
|
+ ObjectMeta: metav1.ObjectMeta{
|
|
|
+ Name: "push-secret",
|
|
|
+ Namespace: "default",
|
|
|
+ },
|
|
|
+ Spec: esv1alpha1.PushSecretSpec{
|
|
|
+ DeletionPolicy: esv1alpha1.PushSecretDeletionPolicyDelete,
|
|
|
+ Selector: esv1alpha1.PushSecretSelector{
|
|
|
+ Secret: &esv1alpha1.PushSecretSecret{
|
|
|
+ Name: "foo",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ SecretStoreRefs: []esv1alpha1.PushSecretStoreRef{
|
|
|
+ {
|
|
|
+ Name: defaultStoreName,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
func hasEvent(involvedKind, name, reason string) bool {
|