client.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  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 volcengine
  14. import (
  15. "context"
  16. "encoding/json"
  17. "errors"
  18. "fmt"
  19. "github.com/volcengine/volcengine-go-sdk/service/kms"
  20. corev1 "k8s.io/api/core/v1"
  21. esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  22. )
  23. const (
  24. notImplemented = "not implemented"
  25. )
  26. var _ esapi.SecretsClient = &Client{}
  27. // Client is a client for the Volcengine provider.
  28. type Client struct {
  29. kms kms.KMSAPI
  30. }
  31. // NewClient creates a new Volcengine client.
  32. func NewClient(kms kms.KMSAPI) *Client {
  33. return &Client{
  34. kms: kms,
  35. }
  36. }
  37. // GetSecret retrieves a secret value from Volcengine Secrets Manager.
  38. func (c *Client) GetSecret(ctx context.Context, ref esapi.ExternalSecretDataRemoteRef) ([]byte, error) {
  39. return c.getSecretValue(ctx, ref)
  40. }
  41. // SecretExists checks if a secret exists in Volcengine Secrets Manager.
  42. func (c *Client) SecretExists(ctx context.Context, remoteRef esapi.PushSecretRemoteRef) (bool, error) {
  43. secretName := remoteRef.GetRemoteKey()
  44. if secretName == "" {
  45. return false, errors.New("secret name is empty")
  46. }
  47. _, err := c.kms.DescribeSecretWithContext(ctx, &kms.DescribeSecretInput{
  48. SecretName: &secretName,
  49. })
  50. if err != nil {
  51. return false, err
  52. }
  53. return true, nil
  54. }
  55. // Validate checks if the provider is configured correctly.
  56. func (c *Client) Validate() (esapi.ValidationResult, error) {
  57. if c.kms != nil {
  58. return esapi.ValidationResultReady, nil
  59. }
  60. return esapi.ValidationResultError, errors.New("kms client is not initialized")
  61. }
  62. // GetSecretMap retrieves a secret value and unmarshals it as a map.
  63. func (c *Client) GetSecretMap(ctx context.Context, ref esapi.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  64. value, err := c.getSecretValue(ctx, ref)
  65. if err != nil {
  66. return nil, err
  67. }
  68. var rawSecretMap map[string]json.RawMessage
  69. if err := json.Unmarshal(value, &rawSecretMap); err != nil {
  70. return nil, fmt.Errorf("failed to unmarshal secret: %w", err)
  71. }
  72. secretMap := make(map[string][]byte, len(rawSecretMap))
  73. for key, value := range rawSecretMap {
  74. secretMap[key] = []byte(value)
  75. }
  76. return secretMap, nil
  77. }
  78. // GetAllSecrets retrieves all secrets matching the given criteria.
  79. func (c *Client) GetAllSecrets(_ context.Context, _ esapi.ExternalSecretFind) (map[string][]byte, error) {
  80. return nil, errors.New(notImplemented)
  81. }
  82. // PushSecret creates or updates a secret in Volcengine Secrets Manager.
  83. func (c *Client) PushSecret(_ context.Context, _ *corev1.Secret, _ esapi.PushSecretData) error {
  84. return errors.New(notImplemented)
  85. }
  86. // DeleteSecret deletes a secret from Volcengine Secrets Manager.
  87. func (c *Client) DeleteSecret(_ context.Context, _ esapi.PushSecretRemoteRef) error {
  88. return errors.New(notImplemented)
  89. }
  90. // Close is a no-op for the Volcengine client.
  91. func (c *Client) Close(_ context.Context) error {
  92. return nil
  93. }
  94. func (c *Client) getSecretValue(ctx context.Context, ref esapi.ExternalSecretDataRemoteRef) ([]byte, error) {
  95. output, err := c.kms.GetSecretValueWithContext(ctx, &kms.GetSecretValueInput{
  96. SecretName: &ref.Key,
  97. VersionID: resolveVersion(ref),
  98. })
  99. if err != nil {
  100. return nil, err
  101. }
  102. if output.SecretValue == nil {
  103. return nil, fmt.Errorf("secret %s has no value", ref.Key)
  104. }
  105. secret := []byte(*output.SecretValue)
  106. if ref.Property == "" {
  107. return secret, nil
  108. }
  109. return extractProperty(secret, ref.Property)
  110. }
  111. func extractProperty(secret []byte, property string) ([]byte, error) {
  112. var secretMap map[string]json.RawMessage
  113. if err := json.Unmarshal(secret, &secretMap); err != nil {
  114. return nil, fmt.Errorf("failed to unmarshal secret: %w", err)
  115. }
  116. value, ok := secretMap[property]
  117. if !ok {
  118. return nil, fmt.Errorf("property %q not found in secret", property)
  119. }
  120. var s string
  121. if json.Unmarshal(value, &s) == nil {
  122. return []byte(s), nil
  123. }
  124. return value, nil
  125. }
  126. func resolveVersion(ref esapi.ExternalSecretDataRemoteRef) *string {
  127. if ref.Version != "" {
  128. return &ref.Version
  129. }
  130. return nil
  131. }