common.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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 providerstore
  14. import (
  15. "context"
  16. "fmt"
  17. "time"
  18. "github.com/go-logr/logr"
  19. corev1 "k8s.io/api/core/v1"
  20. "k8s.io/apimachinery/pkg/api/meta"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. ctrl "sigs.k8s.io/controller-runtime"
  23. "sigs.k8s.io/controller-runtime/pkg/client"
  24. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  25. esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  26. esv2alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v2alpha1"
  27. clusterprovidermetrics "github.com/external-secrets/external-secrets/pkg/controllers/clusterprovider"
  28. providermetrics "github.com/external-secrets/external-secrets/pkg/controllers/provider"
  29. "github.com/external-secrets/external-secrets/runtime/clientmanager"
  30. )
  31. const defaultRuntimeRefKind = "ClusterProviderClass"
  32. func setReadyCondition(store esv2alpha1.GenericStore, status corev1.ConditionStatus, reason, message string) {
  33. condition := esv2alpha1.ProviderStoreCondition{
  34. Type: esv2alpha1.ProviderStoreReady,
  35. Status: status,
  36. LastTransitionTime: metav1.Now(),
  37. Reason: reason,
  38. Message: message,
  39. }
  40. current := store.GetStoreStatus()
  41. for i := range current.Conditions {
  42. if current.Conditions[i].Type != condition.Type {
  43. continue
  44. }
  45. if current.Conditions[i].Status == condition.Status {
  46. condition.LastTransitionTime = current.Conditions[i].LastTransitionTime
  47. }
  48. current.Conditions[i] = condition
  49. store.SetStoreStatus(current)
  50. return
  51. }
  52. current.Conditions = append(current.Conditions, condition)
  53. store.SetStoreStatus(current)
  54. }
  55. func validateStore(ctx context.Context, kubeClient client.Client, store esv2alpha1.GenericStore, sourceNamespace string) error {
  56. mgr := clientmanager.NewManager(kubeClient, "", false)
  57. defer func() {
  58. _ = mgr.Close(ctx)
  59. }()
  60. secretClient, err := mgr.Get(ctx, esv1.SecretStoreRef{
  61. Name: store.GetName(),
  62. Kind: store.GetKind(),
  63. }, sourceNamespace, nil)
  64. if err != nil {
  65. return err
  66. }
  67. _, err = secretClient.Validate()
  68. return err
  69. }
  70. func assertRuntimeClassReady(ctx context.Context, kubeClient client.Client, runtimeRef esv2alpha1.StoreRuntimeRef) error {
  71. runtimeKind := runtimeRef.Kind
  72. if runtimeKind == "" {
  73. runtimeKind = defaultRuntimeRefKind
  74. }
  75. if runtimeKind != defaultRuntimeRefKind {
  76. return fmt.Errorf("unsupported runtimeRef kind %q", runtimeKind)
  77. }
  78. var runtimeClass esv1alpha1.ClusterProviderClass
  79. if err := kubeClient.Get(ctx, client.ObjectKey{Name: runtimeRef.Name}, &runtimeClass); err != nil {
  80. return fmt.Errorf("failed to get ClusterProviderClass %q: %w", runtimeRef.Name, err)
  81. }
  82. condition := meta.FindStatusCondition(runtimeClass.Status.Conditions, "Ready")
  83. if condition == nil || condition.Status != metav1.ConditionTrue {
  84. return fmt.Errorf("ClusterProviderClass %q is not ready", runtimeRef.Name)
  85. }
  86. return nil
  87. }
  88. func runtimeRefMatchesClusterProviderClass(runtimeRef esv2alpha1.StoreRuntimeRef, runtimeClass *esv1alpha1.ClusterProviderClass) bool {
  89. runtimeKind := runtimeRef.Kind
  90. if runtimeKind == "" {
  91. runtimeKind = defaultRuntimeRefKind
  92. }
  93. return runtimeKind == defaultRuntimeRefKind && runtimeRef.Name == runtimeClass.Name
  94. }
  95. func findProviderStoresForRuntimeClass(ctx context.Context, kubeClient client.Client, runtimeClass *esv1alpha1.ClusterProviderClass) []ctrl.Request {
  96. var stores esv2alpha1.ProviderStoreList
  97. if err := kubeClient.List(ctx, &stores); err != nil {
  98. return nil
  99. }
  100. requests := make([]ctrl.Request, 0, len(stores.Items))
  101. for i := range stores.Items {
  102. if !runtimeRefMatchesClusterProviderClass(stores.Items[i].Spec.RuntimeRef, runtimeClass) {
  103. continue
  104. }
  105. requests = append(requests, ctrl.Request{
  106. NamespacedName: client.ObjectKeyFromObject(&stores.Items[i]),
  107. })
  108. }
  109. return requests
  110. }
  111. func findClusterProviderStoresForRuntimeClass(ctx context.Context, kubeClient client.Client, runtimeClass *esv1alpha1.ClusterProviderClass) []ctrl.Request {
  112. var stores esv2alpha1.ClusterProviderStoreList
  113. if err := kubeClient.List(ctx, &stores); err != nil {
  114. return nil
  115. }
  116. requests := make([]ctrl.Request, 0, len(stores.Items))
  117. for i := range stores.Items {
  118. if !runtimeRefMatchesClusterProviderClass(stores.Items[i].Spec.RuntimeRef, runtimeClass) {
  119. continue
  120. }
  121. requests = append(requests, ctrl.Request{
  122. NamespacedName: client.ObjectKeyFromObject(&stores.Items[i]),
  123. })
  124. }
  125. return requests
  126. }
  127. func updateStatus(ctx context.Context, statusWriter client.StatusWriter, store esv2alpha1.GenericStore, requeueAfter time.Duration, log logr.Logger) (ctrl.Result, error) {
  128. if err := statusWriter.Update(ctx, store); err != nil {
  129. log.Error(err, "failed to update status")
  130. return ctrl.Result{}, err
  131. }
  132. return ctrl.Result{RequeueAfter: requeueAfter}, nil
  133. }
  134. func getReadyCondition(status esv2alpha1.ProviderStoreStatus) *esv2alpha1.ProviderStoreCondition {
  135. for i := range status.Conditions {
  136. if status.Conditions[i].Type == esv2alpha1.ProviderStoreReady {
  137. return &status.Conditions[i]
  138. }
  139. }
  140. return nil
  141. }
  142. func recordProviderStoreCompatibilityMetrics(store *esv2alpha1.ProviderStore, duration time.Duration) {
  143. if store == nil {
  144. return
  145. }
  146. providermetrics.RecordReconcileDuration(store.GetName(), store.GetNamespace(), store.GetLabels(), duration.Seconds())
  147. condition := getReadyCondition(store.Status)
  148. if condition == nil {
  149. return
  150. }
  151. providermetrics.UpdateStatusCondition(
  152. store.GetName(),
  153. store.GetNamespace(),
  154. store.GetLabels(),
  155. string(condition.Type),
  156. string(condition.Status),
  157. )
  158. }
  159. func recordClusterProviderStoreCompatibilityMetrics(store *esv2alpha1.ClusterProviderStore, duration time.Duration) {
  160. if store == nil {
  161. return
  162. }
  163. clusterprovidermetrics.RecordReconcileDuration(store.GetName(), duration.Seconds())
  164. condition := getReadyCondition(store.Status)
  165. if condition == nil {
  166. return
  167. }
  168. clusterprovidermetrics.UpdateStatusCondition(
  169. store.GetName(),
  170. string(condition.Type),
  171. string(condition.Status),
  172. )
  173. }