providerstore.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  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 clientmanager
  14. import (
  15. "context"
  16. "fmt"
  17. corev1 "k8s.io/api/core/v1"
  18. "k8s.io/apimachinery/pkg/types"
  19. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  20. esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  21. esv2alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v2alpha1"
  22. pb "github.com/external-secrets/external-secrets/proto/provider"
  23. adapterstore "github.com/external-secrets/external-secrets/providers/v2/adapter/store"
  24. "github.com/external-secrets/external-secrets/providers/v2/common/grpc"
  25. )
  26. const runtimeRefKindClusterProviderClass = "ClusterProviderClass"
  27. func (m *Manager) getV2ProviderStoreClient(ctx context.Context, storeName, callerNamespace string) (esv1.SecretsClient, error) {
  28. var store esv2alpha1.ProviderStore
  29. storeKey := types.NamespacedName{
  30. Name: storeName,
  31. Namespace: callerNamespace,
  32. }
  33. if err := m.client.Get(ctx, storeKey, &store); err != nil {
  34. return nil, fmt.Errorf("failed to get ProviderStore %q: %w", storeName, err)
  35. }
  36. if m.enableFloodgate {
  37. if err := assertV2StoreIsUsable(&store); err != nil {
  38. return nil, err
  39. }
  40. }
  41. return m.getOrCreateProviderStoreClient(ctx, &store, callerNamespace, callerNamespace)
  42. }
  43. func (m *Manager) getV2ClusterProviderStoreClient(ctx context.Context, storeName, callerNamespace string) (esv1.SecretsClient, error) {
  44. var store esv2alpha1.ClusterProviderStore
  45. storeKey := types.NamespacedName{
  46. Name: storeName,
  47. }
  48. if err := m.client.Get(ctx, storeKey, &store); err != nil {
  49. return nil, fmt.Errorf("failed to get ClusterProviderStore %q: %w", storeName, err)
  50. }
  51. shouldProcess, err := m.validateProviderStoreNamespaceConditions(store.Spec.Conditions, callerNamespace)
  52. if err != nil {
  53. return nil, err
  54. }
  55. if !shouldProcess {
  56. return nil, fmt.Errorf(errClusterProviderStoreDenied, storeName, callerNamespace)
  57. }
  58. if m.enableFloodgate {
  59. if err := assertV2StoreIsUsable(&store); err != nil {
  60. return nil, err
  61. }
  62. }
  63. effectiveBackendNamespace := store.Spec.BackendRef.Namespace
  64. if effectiveBackendNamespace == "" {
  65. effectiveBackendNamespace = callerNamespace
  66. }
  67. return m.getOrCreateProviderStoreClient(ctx, &store, callerNamespace, effectiveBackendNamespace)
  68. }
  69. func (m *Manager) getOrCreateProviderStoreClient(ctx context.Context, store esv2alpha1.GenericStore, callerNamespace, effectiveBackendNamespace string) (esv1.SecretsClient, error) {
  70. cacheKeyType := v2ProviderStoreCacheKey
  71. isClusterScoped := false
  72. if store.GetKind() == esv1.ClusterProviderStoreKindStr {
  73. cacheKeyType = v2ClusterProviderStoreCache
  74. isClusterScoped = true
  75. }
  76. cacheKey := clientKey{
  77. providerType: cacheKeyType,
  78. v2ProviderName: store.GetName(),
  79. v2ProviderNamespace: callerNamespace,
  80. }
  81. if cached, ok := m.clientMap[cacheKey]; ok {
  82. if cached.v2ProviderGeneration == store.GetGeneration() {
  83. clientManagerMetrics.RecordCacheHit(providerMetricsLabelForScope(isClusterScoped))
  84. return cached.client, nil
  85. }
  86. clientManagerMetrics.RecordCacheInvalidation(providerMetricsLabelForScope(isClusterScoped), cacheInvalidationGeneration)
  87. delete(m.clientMap, cacheKey)
  88. }
  89. runtimeRef := store.GetRuntimeRef()
  90. runtimeKind := runtimeRef.Kind
  91. if runtimeKind == "" {
  92. runtimeKind = runtimeRefKindClusterProviderClass
  93. }
  94. if runtimeKind != runtimeRefKindClusterProviderClass {
  95. return nil, fmt.Errorf("unsupported runtimeRef kind %q", runtimeKind)
  96. }
  97. var runtimeClass esv1alpha1.ClusterProviderClass
  98. if err := m.client.Get(ctx, types.NamespacedName{Name: runtimeRef.Name}, &runtimeClass); err != nil {
  99. return nil, fmt.Errorf("failed to get %s %q: %w", runtimeRefKindClusterProviderClass, runtimeRef.Name, err)
  100. }
  101. if runtimeClass.Spec.Address == "" {
  102. return nil, fmt.Errorf("provider address is required in %s %q", runtimeRefKindClusterProviderClass, runtimeRef.Name)
  103. }
  104. tlsSecretNamespace := grpc.ResolveTLSSecretNamespace(runtimeClass.Spec.Address, "", "", effectiveBackendNamespace)
  105. tlsConfig, err := grpc.LoadClientTLSConfig(ctx, m.client, runtimeClass.Spec.Address, tlsSecretNamespace)
  106. if err != nil {
  107. return nil, fmt.Errorf("failed to load TLS config for %s %q: %w", runtimeRefKindClusterProviderClass, runtimeRef.Name, err)
  108. }
  109. pool := getGlobalV2ConnectionPool()
  110. grpcClient, err := pool.Get(ctx, runtimeClass.Spec.Address, tlsConfig)
  111. if err != nil {
  112. return nil, fmt.Errorf("failed to get gRPC client from pool for %s %q: %w", runtimeRefKindClusterProviderClass, runtimeRef.Name, err)
  113. }
  114. m.v2PooledConnections = append(m.v2PooledConnections, v2PooledConnection{
  115. address: runtimeClass.Spec.Address,
  116. tlsConfig: tlsConfig,
  117. })
  118. backendRef := store.GetBackendRef()
  119. providerRef := &pb.ProviderReference{
  120. ApiVersion: backendRef.APIVersion,
  121. Kind: backendRef.Kind,
  122. Name: backendRef.Name,
  123. Namespace: effectiveBackendNamespace,
  124. StoreRefKind: store.GetKind(),
  125. }
  126. wrappedClient := adapterstore.NewClient(grpcClient, providerRef, callerNamespace)
  127. m.clientMap[cacheKey] = &clientVal{
  128. client: wrappedClient,
  129. v2ProviderGeneration: store.GetGeneration(),
  130. }
  131. return wrappedClient, nil
  132. }
  133. func (m *Manager) validateProviderStoreNamespaceConditions(conditions []esv2alpha1.StoreNamespaceCondition, ns string) (bool, error) {
  134. if len(conditions) == 0 {
  135. return true, nil
  136. }
  137. translated := make([]esv1.ClusterSecretStoreCondition, 0, len(conditions))
  138. for _, condition := range conditions {
  139. translated = append(translated, esv1.ClusterSecretStoreCondition{
  140. NamespaceSelector: condition.NamespaceSelector,
  141. Namespaces: append([]string(nil), condition.Namespaces...),
  142. NamespaceRegexes: append([]string(nil), condition.NamespaceRegexes...),
  143. })
  144. }
  145. return m.validateNamespaceConditions(translated, ns)
  146. }
  147. func assertV2StoreIsUsable(store esv2alpha1.GenericStore) error {
  148. if store == nil {
  149. return nil
  150. }
  151. condition := getProviderStoreCondition(store.GetStoreStatus(), esv2alpha1.ProviderStoreReady)
  152. if condition == nil || condition.Status != corev1.ConditionTrue {
  153. return fmt.Errorf(errSecretStoreNotReady, store.GetKind(), store.GetName())
  154. }
  155. return nil
  156. }
  157. func getProviderStoreCondition(status esv2alpha1.ProviderStoreStatus, condType esv2alpha1.ProviderStoreConditionType) *esv2alpha1.ProviderStoreCondition {
  158. for i := range status.Conditions {
  159. if status.Conditions[i].Type == condType {
  160. return &status.Conditions[i]
  161. }
  162. }
  163. return nil
  164. }