pushsecret_controller_v2.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /*
  2. Copyright © The ESO Authors
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. https://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package pushsecret
  14. import (
  15. "context"
  16. "fmt"
  17. "maps"
  18. "strings"
  19. corev1 "k8s.io/api/core/v1"
  20. apierrors "k8s.io/apimachinery/pkg/api/errors"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. "k8s.io/apimachinery/pkg/types"
  23. "sigs.k8s.io/controller-runtime/pkg/client"
  24. esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  25. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  26. esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  27. "github.com/external-secrets/external-secrets/runtime/clientmanager"
  28. )
  29. // GetSecretStoresV2 retrieves both v1 and v2 Providers.
  30. func (r *Reconciler) GetSecretStoresV2(ctx context.Context, ps esv1alpha1.PushSecret) (map[esv1alpha1.PushSecretStoreRef]any, error) {
  31. stores := make(map[esv1alpha1.PushSecretStoreRef]any)
  32. for _, refStore := range ps.Spec.SecretStoreRefs {
  33. if refStore.LabelSelector != nil {
  34. resolvedStores, err := r.getSecretStoresFromSelectorV2(ctx, refStore, ps.Namespace)
  35. if err != nil {
  36. return nil, err
  37. }
  38. maps.Copy(stores, resolvedStores)
  39. continue
  40. }
  41. if refStore.Kind == "" {
  42. store, err := r.getSecretStoreFromName(ctx, refStore, ps.Namespace)
  43. if err == nil {
  44. stores[refStore] = store
  45. continue
  46. }
  47. if !apierrors.IsNotFound(err) {
  48. return nil, err
  49. }
  50. if !clientmanager.V2ProvidersEnabled() {
  51. return nil, err
  52. }
  53. }
  54. store, ok, err := r.resolveV2Store(ctx, refStore, ps.Namespace)
  55. if err != nil {
  56. return nil, err
  57. }
  58. if ok {
  59. stores[refStore] = store
  60. continue
  61. }
  62. // Get v1 SecretStore (existing implementation)
  63. store, err = r.getSecretStoreFromName(ctx, refStore, ps.Namespace)
  64. if err != nil {
  65. return nil, err
  66. }
  67. stores[refStore] = store
  68. }
  69. return stores, nil
  70. }
  71. func (r *Reconciler) getSecretStoresFromSelectorV2(ctx context.Context, storeRef esv1alpha1.PushSecretStoreRef, namespace string) (map[esv1alpha1.PushSecretStoreRef]any, error) {
  72. selector, err := metav1.LabelSelectorAsSelector(storeRef.LabelSelector)
  73. if err != nil {
  74. return nil, fmt.Errorf("could not convert labels: %w", err)
  75. }
  76. listOptions := &client.ListOptions{LabelSelector: selector}
  77. stores := make(map[esv1alpha1.PushSecretStoreRef]any)
  78. switch storeRef.Kind {
  79. case esapi.ProviderKindStr:
  80. listOptions.Namespace = namespace
  81. var providerList esv1.ProviderList
  82. if err := r.List(ctx, &providerList, listOptions); err != nil {
  83. return nil, fmt.Errorf("could not list Providers: %w", err)
  84. }
  85. for i := range providerList.Items {
  86. store := &providerList.Items[i]
  87. stores[esv1alpha1.PushSecretStoreRef{Name: store.Name, Kind: esapi.ProviderKindStr}] = store
  88. }
  89. case esapi.ClusterProviderKindStr:
  90. var providerList esv1.ClusterProviderList
  91. if err := r.List(ctx, &providerList, listOptions); err != nil {
  92. return nil, fmt.Errorf("could not list ClusterProviders: %w", err)
  93. }
  94. for i := range providerList.Items {
  95. store := &providerList.Items[i]
  96. stores[esv1alpha1.PushSecretStoreRef{Name: store.Name, Kind: esapi.ClusterProviderKindStr}] = store
  97. }
  98. case esv1.ClusterSecretStoreKind:
  99. var storeList esv1.ClusterSecretStoreList
  100. if err := r.List(ctx, &storeList, listOptions); err != nil {
  101. return nil, fmt.Errorf("could not list cluster Secret Stores: %w", err)
  102. }
  103. for i := range storeList.Items {
  104. store := &storeList.Items[i]
  105. stores[esv1alpha1.PushSecretStoreRef{Name: store.Name, Kind: esv1.ClusterSecretStoreKind}] = store
  106. }
  107. default:
  108. listOptions.Namespace = namespace
  109. var storeList esv1.SecretStoreList
  110. if err := r.List(ctx, &storeList, listOptions); err != nil {
  111. return nil, fmt.Errorf("could not list Secret Stores: %w", err)
  112. }
  113. for i := range storeList.Items {
  114. store := &storeList.Items[i]
  115. stores[esv1alpha1.PushSecretStoreRef{Name: store.Name, Kind: esv1.SecretStoreKind}] = store
  116. }
  117. }
  118. return stores, nil
  119. }
  120. func (r *Reconciler) resolveV2Store(ctx context.Context, storeRef esv1alpha1.PushSecretStoreRef, namespace string) (any, bool, error) {
  121. if storeRef.APIVersion != "" && storeRef.APIVersion != esapi.SchemeGroupVersion.String() {
  122. return nil, false, nil
  123. }
  124. if storeRef.Name == "" {
  125. return nil, false, nil
  126. }
  127. switch storeRef.Kind {
  128. case esapi.ClusterProviderKindStr:
  129. var store esapi.ClusterProvider
  130. storeKey := types.NamespacedName{Name: storeRef.Name}
  131. if err := r.Client.Get(ctx, storeKey, &store); err != nil {
  132. return nil, true, fmt.Errorf("failed to get v2 ClusterProvider %s: %w", storeRef.Name, err)
  133. }
  134. return &store, true, nil
  135. case esapi.ProviderKindStr:
  136. var store esapi.Provider
  137. storeKey := types.NamespacedName{Name: storeRef.Name, Namespace: namespace}
  138. if err := r.Client.Get(ctx, storeKey, &store); err != nil {
  139. return nil, true, fmt.Errorf("failed to get v2 Provider %s: %w", storeRef.Name, err)
  140. }
  141. return &store, true, nil
  142. case "":
  143. var provider esapi.Provider
  144. providerKey := types.NamespacedName{Name: storeRef.Name, Namespace: namespace}
  145. if err := r.Client.Get(ctx, providerKey, &provider); err == nil {
  146. return &provider, true, nil
  147. }
  148. var clusterProvider esapi.ClusterProvider
  149. clusterProviderKey := types.NamespacedName{Name: storeRef.Name}
  150. if err := r.Client.Get(ctx, clusterProviderKey, &clusterProvider); err == nil {
  151. return &clusterProvider, true, nil
  152. }
  153. }
  154. return nil, false, nil
  155. }
  156. // PushSecretToProvidersV2 pushes secret data to both v1 stores and v2 providers.
  157. func (r *Reconciler) PushSecretToProvidersV2(
  158. ctx context.Context,
  159. stores map[esv1alpha1.PushSecretStoreRef]any,
  160. ps esv1alpha1.PushSecret,
  161. secret *corev1.Secret,
  162. mgr *clientmanager.Manager,
  163. ) (esv1alpha1.SyncedPushSecretsMap, error) {
  164. out := make(esv1alpha1.SyncedPushSecretsMap)
  165. for ref, store := range stores {
  166. si, ok := resolvedStoreInfo(ref, store)
  167. if !ok {
  168. continue
  169. }
  170. var err error
  171. out, err = r.handlePushSecretDataForStore(ctx, ps, secret, out, mgr, si)
  172. if err != nil {
  173. return out, err
  174. }
  175. }
  176. return out, nil
  177. }
  178. // DeleteSecretFromProvidersV2 removes secrets from v2 providers when they're no longer needed.
  179. func (r *Reconciler) DeleteSecretFromProvidersV2(
  180. ctx context.Context,
  181. ps *esv1alpha1.PushSecret,
  182. newMap esv1alpha1.SyncedPushSecretsMap,
  183. _ map[esv1alpha1.PushSecretStoreRef]any,
  184. ) (esv1alpha1.SyncedPushSecretsMap, error) {
  185. out := mergeSecretState(newMap, ps.Status.SyncedPushSecrets)
  186. mgr := clientmanager.NewManager(r.Client, r.ControllerClass, false)
  187. defer func() {
  188. _ = mgr.Close(ctx)
  189. }()
  190. for storeName, oldData := range ps.Status.SyncedPushSecrets {
  191. // Parse store name format "Kind/Name"
  192. parts := strings.Split(storeName, "/")
  193. if len(parts) != 2 {
  194. continue
  195. }
  196. storeKind := parts[0]
  197. storeNameOnly := parts[1]
  198. secretClient, err := mgr.Get(ctx, esapi.SecretStoreRef{
  199. Name: storeNameOnly,
  200. Kind: storeKind,
  201. }, ps.Namespace, nil)
  202. if err != nil {
  203. return out, fmt.Errorf("could not get secrets client for store %v: %w", storeName, err)
  204. }
  205. newData, ok := newMap[storeName]
  206. if !ok {
  207. err = r.DeleteAllSecretsFromStore(ctx, secretClient, oldData)
  208. if err != nil {
  209. return out, err
  210. }
  211. delete(out, storeName)
  212. continue
  213. }
  214. for oldEntry, oldRef := range oldData {
  215. if _, stillExists := newData[oldEntry]; !stillExists {
  216. err = r.DeleteSecretFromStore(ctx, secretClient, oldRef)
  217. if err != nil {
  218. return out, err
  219. }
  220. delete(out[storeName], oldEntry)
  221. }
  222. }
  223. }
  224. return out, nil
  225. }