provider.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  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 pulumi
  13. import (
  14. "context"
  15. "errors"
  16. "fmt"
  17. esc "github.com/pulumi/esc-sdk/sdk/go"
  18. kclient "sigs.k8s.io/controller-runtime/pkg/client"
  19. "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
  20. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  21. "github.com/external-secrets/external-secrets/pkg/utils"
  22. "github.com/external-secrets/external-secrets/pkg/utils/resolvers"
  23. )
  24. type Provider struct{}
  25. var _ esv1beta1.Provider = &Provider{}
  26. const (
  27. errClusterStoreRequiresNamespace = "cluster store requires namespace"
  28. errCannotResolveSecretKeyRef = "cannot resolve secret key ref: %w"
  29. errStoreIsNil = "store is nil"
  30. errNoStoreTypeOrWrongStoreType = "no store type or wrong store type"
  31. errOrganizationIsRequired = "organization is required"
  32. errEnvironmentIsRequired = "environment is required"
  33. errProjectIsRequired = "project is required"
  34. errSecretRefNameIsRequired = "secretRef.name is required"
  35. errSecretRefKeyIsRequired = "secretRef.key is required"
  36. )
  37. func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
  38. cfg, err := getConfig(store)
  39. if err != nil {
  40. return nil, err
  41. }
  42. storeKind := store.GetKind()
  43. if storeKind == esv1beta1.ClusterSecretStoreKind && doesConfigDependOnNamespace(cfg) {
  44. return nil, errors.New(errClusterStoreRequiresNamespace)
  45. }
  46. accessToken, err := loadAccessTokenSecret(ctx, cfg.AccessToken, kube, storeKind, namespace)
  47. if err != nil {
  48. return nil, err
  49. }
  50. configuration := esc.NewConfiguration()
  51. configuration.UserAgent = "external-secrets-operator"
  52. configuration.Servers = esc.ServerConfigurations{
  53. esc.ServerConfiguration{
  54. URL: cfg.APIURL,
  55. },
  56. }
  57. authCtx := esc.NewAuthContext(accessToken)
  58. escClient := esc.NewClient(configuration)
  59. return &client{
  60. escClient: *escClient,
  61. authCtx: authCtx,
  62. project: cfg.Project,
  63. environment: cfg.Environment,
  64. organization: cfg.Organization,
  65. }, nil
  66. }
  67. func loadAccessTokenSecret(ctx context.Context, ref *esv1beta1.PulumiProviderSecretRef, kube kclient.Client, storeKind, namespace string) (string, error) {
  68. acctoken, err := resolvers.SecretKeyRef(ctx, kube, storeKind, namespace, ref.SecretRef)
  69. if err != nil {
  70. return "", fmt.Errorf(errCannotResolveSecretKeyRef, err)
  71. }
  72. return acctoken, nil
  73. }
  74. func doesConfigDependOnNamespace(cfg *esv1beta1.PulumiProvider) bool {
  75. if cfg.AccessToken.SecretRef != nil && cfg.AccessToken.SecretRef.Namespace == nil {
  76. return true
  77. }
  78. return false
  79. }
  80. func getConfig(store esv1beta1.GenericStore) (*esv1beta1.PulumiProvider, error) {
  81. if store == nil {
  82. return nil, errors.New(errStoreIsNil)
  83. }
  84. spec := store.GetSpec()
  85. if spec == nil || spec.Provider == nil || spec.Provider.Pulumi == nil {
  86. return nil, errors.New(errNoStoreTypeOrWrongStoreType)
  87. }
  88. cfg := spec.Provider.Pulumi
  89. if cfg.APIURL == "" {
  90. cfg.APIURL = "https://api.pulumi.com/api/esc"
  91. }
  92. if cfg.Organization == "" {
  93. return nil, errors.New(errOrganizationIsRequired)
  94. }
  95. if cfg.Environment == "" {
  96. return nil, errors.New(errEnvironmentIsRequired)
  97. }
  98. if cfg.Project == "" {
  99. return nil, errors.New(errProjectIsRequired)
  100. }
  101. err := validateStoreSecretRef(store, cfg.AccessToken)
  102. if err != nil {
  103. return nil, err
  104. }
  105. return cfg, nil
  106. }
  107. func validateStoreSecretRef(store esv1beta1.GenericStore, ref *esv1beta1.PulumiProviderSecretRef) error {
  108. if ref != nil {
  109. if err := utils.ValidateReferentSecretSelector(store, *ref.SecretRef); err != nil {
  110. return err
  111. }
  112. }
  113. return validateSecretRef(ref)
  114. }
  115. func validateSecretRef(ref *esv1beta1.PulumiProviderSecretRef) error {
  116. if ref.SecretRef != nil {
  117. if ref.SecretRef.Name == "" {
  118. return errors.New(errSecretRefNameIsRequired)
  119. }
  120. if ref.SecretRef.Key == "" {
  121. return errors.New(errSecretRefKeyIsRequired)
  122. }
  123. }
  124. return nil
  125. }
  126. func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnings, error) {
  127. _, err := getConfig(store)
  128. return nil, err
  129. }
  130. func (p *Provider) Capabilities() esv1beta1.SecretStoreCapabilities {
  131. return esv1beta1.SecretStoreReadOnly
  132. }
  133. func init() {
  134. esv1beta1.Register(&Provider{}, &esv1beta1.SecretStoreProvider{
  135. Pulumi: &esv1beta1.PulumiProvider{},
  136. })
  137. }