provider.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. /*
  2. Copyright © 2025 ESO Maintainer Team
  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 doppler
  14. import (
  15. "context"
  16. "errors"
  17. "fmt"
  18. "os"
  19. "strconv"
  20. "time"
  21. kclient "sigs.k8s.io/controller-runtime/pkg/client"
  22. "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
  23. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  24. dclient "github.com/external-secrets/external-secrets/providers/v1/doppler/client"
  25. "github.com/external-secrets/external-secrets/runtime/esutils"
  26. )
  27. const (
  28. errNewClient = "unable to create DopplerClient : %s"
  29. errInvalidStore = "invalid store: %s"
  30. errDopplerStore = "missing or invalid Doppler SecretStore"
  31. )
  32. // Provider is a Doppler secrets provider implementing NewClient and ValidateStore for the esv1.Provider interface.
  33. type Provider struct{}
  34. // https://github.com/external-secrets/external-secrets/issues/644
  35. var _ esv1.SecretsClient = &Client{}
  36. var _ esv1.Provider = &Provider{}
  37. // Capabilities returns the provider's supported capabilities.
  38. func (p *Provider) Capabilities() esv1.SecretStoreCapabilities {
  39. return esv1.SecretStoreReadOnly
  40. }
  41. // NewClient creates a new Doppler client.
  42. func (p *Provider) NewClient(ctx context.Context, store esv1.GenericStore, kube kclient.Client, namespace string) (esv1.SecretsClient, error) {
  43. storeSpec := store.GetSpec()
  44. if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.Doppler == nil {
  45. return nil, errors.New(errDopplerStore)
  46. }
  47. dopplerStoreSpec := storeSpec.Provider.Doppler
  48. // Default Key to dopplerToken if not specified
  49. if dopplerStoreSpec.Auth.SecretRef.DopplerToken.Key == "" {
  50. storeSpec.Provider.Doppler.Auth.SecretRef.DopplerToken.Key = "dopplerToken"
  51. }
  52. client := &Client{
  53. kube: kube,
  54. store: dopplerStoreSpec,
  55. namespace: namespace,
  56. storeKind: store.GetObjectKind().GroupVersionKind().Kind,
  57. }
  58. if err := client.setAuth(ctx); err != nil {
  59. return nil, err
  60. }
  61. doppler, err := dclient.NewDopplerClient(client.dopplerToken)
  62. if err != nil {
  63. return nil, fmt.Errorf(errNewClient, err)
  64. }
  65. if customBaseURL, found := os.LookupEnv(customBaseURLEnvVar); found {
  66. if err := doppler.SetBaseURL(customBaseURL); err != nil {
  67. return nil, fmt.Errorf(errNewClient, err)
  68. }
  69. }
  70. if customVerifyTLS, found := os.LookupEnv(verifyTLSOverrideEnvVar); found {
  71. customVerifyTLS, err := strconv.ParseBool(customVerifyTLS)
  72. if err == nil {
  73. doppler.VerifyTLS = customVerifyTLS
  74. }
  75. }
  76. // Wrap the Doppler client with retry logic if retrySettings are configured
  77. wrappedClient, err := p.setRetrySettings(doppler, storeSpec)
  78. if err != nil {
  79. return nil, err
  80. }
  81. client.doppler = wrappedClient
  82. client.project = client.store.Project
  83. client.config = client.store.Config
  84. client.nameTransformer = client.store.NameTransformer
  85. client.format = client.store.Format
  86. return client, nil
  87. }
  88. // ValidateStore validates the Doppler provider configuration.
  89. func (p *Provider) ValidateStore(store esv1.GenericStore) (admission.Warnings, error) {
  90. storeSpec := store.GetSpec()
  91. dopplerStoreSpec := storeSpec.Provider.Doppler
  92. dopplerTokenSecretRef := dopplerStoreSpec.Auth.SecretRef.DopplerToken
  93. if err := esutils.ValidateSecretSelector(store, dopplerTokenSecretRef); err != nil {
  94. return nil, fmt.Errorf(errInvalidStore, err)
  95. }
  96. if dopplerTokenSecretRef.Name == "" {
  97. return nil, fmt.Errorf(errInvalidStore, "dopplerToken.name cannot be empty")
  98. }
  99. return nil, nil
  100. }
  101. func (p *Provider) setRetrySettings(doppler *dclient.DopplerClient, storeSpec *esv1.SecretStoreSpec) (SecretsClientInterface, error) {
  102. if storeSpec.RetrySettings == nil {
  103. return doppler, nil
  104. }
  105. maxRetries := 3 // default value
  106. retryInterval := time.Duration(0)
  107. if storeSpec.RetrySettings.MaxRetries != nil {
  108. maxRetries = int(*storeSpec.RetrySettings.MaxRetries)
  109. }
  110. if storeSpec.RetrySettings.RetryInterval != nil {
  111. var err error
  112. retryInterval, err = time.ParseDuration(*storeSpec.RetrySettings.RetryInterval)
  113. if err != nil {
  114. return nil, fmt.Errorf(errNewClient, fmt.Errorf("invalid retry interval: %w", err))
  115. }
  116. }
  117. return newRetryableClient(doppler, maxRetries, retryInterval), nil
  118. }
  119. // NewProvider creates a new Provider instance.
  120. func NewProvider() esv1.Provider {
  121. return &Provider{}
  122. }
  123. // ProviderSpec returns the provider specification for registration.
  124. func ProviderSpec() *esv1.SecretStoreProvider {
  125. return &esv1.SecretStoreProvider{
  126. Doppler: &esv1.DopplerProvider{},
  127. }
  128. }
  129. // MaintenanceStatus returns the maintenance status of the provider.
  130. func MaintenanceStatus() esv1.MaintenanceStatus {
  131. return esv1.MaintenanceStatusMaintained
  132. }