pushsecret_controller_v2.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. /*
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package pushsecret
  13. import (
  14. "context"
  15. "fmt"
  16. "strings"
  17. corev1 "k8s.io/api/core/v1"
  18. "k8s.io/apimachinery/pkg/types"
  19. esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  20. esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  21. "github.com/external-secrets/external-secrets/runtime/clientmanager"
  22. )
  23. // isV2SecretStore checks if the referenced SecretStore is a v2 API version.
  24. func (r *Reconciler) isV2SecretStore(ctx context.Context, storeRef esv1alpha1.PushSecretStoreRef, namespace string) bool {
  25. if storeRef.Kind == esapi.ProviderKindStr || storeRef.Kind == esapi.ClusterProviderKindStr {
  26. if storeRef.APIVersion == "" {
  27. return true
  28. }
  29. return storeRef.APIVersion == esapi.SchemeGroupVersion.String()
  30. }
  31. // Check the apiVersion field first if specified
  32. if storeRef.APIVersion != "" {
  33. return false
  34. }
  35. if storeRef.Kind == esapi.ClusterProviderKindStr {
  36. var store esapi.ClusterProvider
  37. err := r.Client.Get(ctx, types.NamespacedName{Name: storeRef.Name}, &store)
  38. return err == nil
  39. }
  40. // For backwards compatibility, try to fetch as namespaced v2 Provider.
  41. var store esapi.Provider
  42. storeKey := types.NamespacedName{Name: storeRef.Name, Namespace: namespace}
  43. err := r.Client.Get(ctx, storeKey, &store)
  44. return err == nil
  45. }
  46. // GetSecretStoresV2 retrieves both v1 and v2 Providers.
  47. func (r *Reconciler) GetSecretStoresV2(ctx context.Context, ps esv1alpha1.PushSecret) (map[esv1alpha1.PushSecretStoreRef]interface{}, error) {
  48. stores := make(map[esv1alpha1.PushSecretStoreRef]interface{})
  49. for _, refStore := range ps.Spec.SecretStoreRefs {
  50. // Check if this is a v2 Provider
  51. if r.isV2SecretStore(ctx, refStore, ps.Namespace) {
  52. if refStore.Kind == esapi.ClusterProviderKindStr {
  53. var store esapi.ClusterProvider
  54. storeKey := types.NamespacedName{Name: refStore.Name}
  55. if err := r.Client.Get(ctx, storeKey, &store); err != nil {
  56. return nil, fmt.Errorf("failed to get v2 ClusterProvider %s: %w", refStore.Name, err)
  57. }
  58. stores[refStore] = &store
  59. continue
  60. }
  61. var store esapi.Provider
  62. storeKey := types.NamespacedName{Name: refStore.Name, Namespace: ps.Namespace}
  63. if err := r.Client.Get(ctx, storeKey, &store); err != nil {
  64. return nil, fmt.Errorf("failed to get v2 Provider %s: %w", refStore.Name, err)
  65. }
  66. stores[refStore] = &store
  67. continue
  68. } else {
  69. // Get v1 SecretStore (existing implementation)
  70. store, err := r.getSecretStoreFromName(ctx, refStore, ps.Namespace)
  71. if err != nil {
  72. return nil, err
  73. }
  74. stores[refStore] = store
  75. }
  76. }
  77. return stores, nil
  78. }
  79. // PushSecretToProvidersV2 pushes secret data to both v1 stores and v2 providers.
  80. func (r *Reconciler) PushSecretToProvidersV2(
  81. ctx context.Context,
  82. stores map[esv1alpha1.PushSecretStoreRef]interface{},
  83. ps esv1alpha1.PushSecret,
  84. secret *corev1.Secret,
  85. mgr *clientmanager.Manager,
  86. ) (esv1alpha1.SyncedPushSecretsMap, error) {
  87. out := make(esv1alpha1.SyncedPushSecretsMap)
  88. for ref := range stores {
  89. var err error
  90. out, err = r.handlePushSecretDataForStore(ctx, ps, secret, out, mgr, ref.Name, ref.Kind)
  91. if err != nil {
  92. return out, err
  93. }
  94. }
  95. return out, nil
  96. }
  97. // DeleteSecretFromProvidersV2 removes secrets from v2 providers when they're no longer needed.
  98. func (r *Reconciler) DeleteSecretFromProvidersV2(ctx context.Context, ps *esv1alpha1.PushSecret, newMap esv1alpha1.SyncedPushSecretsMap, stores map[esv1alpha1.PushSecretStoreRef]interface{}) (esv1alpha1.SyncedPushSecretsMap, error) {
  99. out := mergeSecretState(newMap, ps.Status.SyncedPushSecrets)
  100. mgr := clientmanager.NewManager(r.Client, r.ControllerClass, false)
  101. defer func() {
  102. _ = mgr.Close(ctx)
  103. }()
  104. for storeName, oldData := range ps.Status.SyncedPushSecrets {
  105. // Parse store name format "Kind/Name"
  106. parts := strings.Split(storeName, "/")
  107. if len(parts) != 2 {
  108. continue
  109. }
  110. storeKind := parts[0]
  111. storeNameOnly := parts[1]
  112. // Find the matching store
  113. var found bool
  114. for ref := range stores {
  115. if ref.Kind == storeKind && ref.Name == storeNameOnly {
  116. found = true
  117. break
  118. }
  119. }
  120. if !found {
  121. // Store no longer referenced, skip deletion
  122. continue
  123. }
  124. secretClient, err := mgr.Get(ctx, esapi.SecretStoreRef{
  125. Name: storeNameOnly,
  126. Kind: storeKind,
  127. }, ps.Namespace, nil)
  128. if err != nil {
  129. return out, fmt.Errorf("could not get secrets client for store %v: %w", storeName, err)
  130. }
  131. newData, ok := newMap[storeName]
  132. if !ok {
  133. err = r.DeleteAllSecretsFromStore(ctx, secretClient, oldData)
  134. if err != nil {
  135. return out, err
  136. }
  137. delete(out, storeName)
  138. continue
  139. }
  140. for oldEntry, oldRef := range oldData {
  141. if _, stillExists := newData[oldEntry]; !stillExists {
  142. err = r.DeleteSecretFromStore(ctx, secretClient, oldRef)
  143. if err != nil {
  144. return out, err
  145. }
  146. delete(out[storeName], oldEntry)
  147. }
  148. }
  149. }
  150. return out, nil
  151. }