provider.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  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. errSecretRefNameIsRequired = "secretRef.name is required"
  34. errSecretRefKeyIsRequired = "secretRef.key is required"
  35. )
  36. func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
  37. cfg, err := getConfig(store)
  38. if err != nil {
  39. return nil, err
  40. }
  41. storeKind := store.GetKind()
  42. if storeKind == esv1beta1.ClusterSecretStoreKind && doesConfigDependOnNamespace(cfg) {
  43. return nil, errors.New(errClusterStoreRequiresNamespace)
  44. }
  45. accessToken, err := loadAccessTokenSecret(ctx, cfg.AccessToken, kube, storeKind, namespace)
  46. if err != nil {
  47. return nil, err
  48. }
  49. configuration := esc.NewConfiguration()
  50. configuration.UserAgent = "external-secrets-operator"
  51. configuration.Servers = esc.ServerConfigurations{
  52. esc.ServerConfiguration{
  53. URL: cfg.APIURL,
  54. },
  55. }
  56. authCtx := esc.NewAuthContext(accessToken)
  57. escClient := esc.NewClient(configuration)
  58. return &client{
  59. escClient: *escClient,
  60. authCtx: authCtx,
  61. environment: cfg.Environment,
  62. organization: cfg.Organization,
  63. }, nil
  64. }
  65. func loadAccessTokenSecret(ctx context.Context, ref *esv1beta1.PulumiProviderSecretRef, kube kclient.Client, storeKind, namespace string) (string, error) {
  66. acctoken, err := resolvers.SecretKeyRef(ctx, kube, storeKind, namespace, ref.SecretRef)
  67. if err != nil {
  68. return "", fmt.Errorf(errCannotResolveSecretKeyRef, err)
  69. }
  70. return acctoken, nil
  71. }
  72. func doesConfigDependOnNamespace(cfg *esv1beta1.PulumiProvider) bool {
  73. if cfg.AccessToken.SecretRef != nil && cfg.AccessToken.SecretRef.Namespace == nil {
  74. return true
  75. }
  76. return false
  77. }
  78. func getConfig(store esv1beta1.GenericStore) (*esv1beta1.PulumiProvider, error) {
  79. if store == nil {
  80. return nil, errors.New(errStoreIsNil)
  81. }
  82. spec := store.GetSpec()
  83. if spec == nil || spec.Provider == nil || spec.Provider.Pulumi == nil {
  84. return nil, errors.New(errNoStoreTypeOrWrongStoreType)
  85. }
  86. cfg := spec.Provider.Pulumi
  87. if cfg.APIURL == "" {
  88. cfg.APIURL = "https://api.pulumi.com/api/preview"
  89. }
  90. if cfg.Organization == "" {
  91. return nil, errors.New(errOrganizationIsRequired)
  92. }
  93. if cfg.Environment == "" {
  94. return nil, errors.New(errEnvironmentIsRequired)
  95. }
  96. err := validateStoreSecretRef(store, cfg.AccessToken)
  97. if err != nil {
  98. return nil, err
  99. }
  100. return cfg, nil
  101. }
  102. func validateStoreSecretRef(store esv1beta1.GenericStore, ref *esv1beta1.PulumiProviderSecretRef) error {
  103. if ref != nil {
  104. if err := utils.ValidateReferentSecretSelector(store, *ref.SecretRef); err != nil {
  105. return err
  106. }
  107. }
  108. return validateSecretRef(ref)
  109. }
  110. func validateSecretRef(ref *esv1beta1.PulumiProviderSecretRef) error {
  111. if ref.SecretRef != nil {
  112. if ref.SecretRef.Name == "" {
  113. return errors.New(errSecretRefNameIsRequired)
  114. }
  115. if ref.SecretRef.Key == "" {
  116. return errors.New(errSecretRefKeyIsRequired)
  117. }
  118. }
  119. return nil
  120. }
  121. func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnings, error) {
  122. _, err := getConfig(store)
  123. return nil, err
  124. }
  125. func (p *Provider) Capabilities() esv1beta1.SecretStoreCapabilities {
  126. return esv1beta1.SecretStoreReadOnly
  127. }
  128. func init() {
  129. esv1beta1.Register(&Provider{}, &esv1beta1.SecretStoreProvider{
  130. Pulumi: &esv1beta1.PulumiProvider{},
  131. })
  132. }