push_secret.go 4.6 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 secretmanager
  13. import (
  14. "bytes"
  15. "errors"
  16. "fmt"
  17. "maps"
  18. "cloud.google.com/go/secretmanager/apiv1/secretmanagerpb"
  19. "github.com/tidwall/sjson"
  20. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  21. "github.com/external-secrets/external-secrets/pkg/utils/metadata"
  22. )
  23. type PushSecretMetadataMergePolicy string
  24. const (
  25. PushSecretMetadataMergePolicyReplace PushSecretMetadataMergePolicy = "Replace"
  26. PushSecretMetadataMergePolicyMerge PushSecretMetadataMergePolicy = "Merge"
  27. )
  28. type PushSecretMetadataSpec struct {
  29. Annotations map[string]string `json:"annotations,omitempty"`
  30. Labels map[string]string `json:"labels,omitempty"`
  31. Topics []string `json:"topics,omitempty"`
  32. MergePolicy PushSecretMetadataMergePolicy `json:"mergePolicy,omitempty"`
  33. CMEKKeyName string `json:"cmekKeyName,omitempty"`
  34. ReplicationLocation string `json:"replicationLocation,omitempty"`
  35. }
  36. func newPushSecretBuilder(payload []byte, data esv1.PushSecretData) (pushSecretBuilder, error) {
  37. if data.GetProperty() == "" {
  38. return &psBuilder{
  39. payload: payload,
  40. pushSecretData: data,
  41. }, nil
  42. }
  43. if data.GetMetadata() != nil {
  44. return nil, errors.New("cannot specify metadata and property at the same time")
  45. }
  46. return &propertyPSBuilder{
  47. payload: payload,
  48. pushSecretData: data,
  49. }, nil
  50. }
  51. type pushSecretBuilder interface {
  52. buildMetadata(annotations, labels map[string]string, topics []*secretmanagerpb.Topic) (map[string]string, map[string]string, []string, error)
  53. needUpdate(original []byte) bool
  54. buildData(original []byte) ([]byte, error)
  55. }
  56. type psBuilder struct {
  57. payload []byte
  58. pushSecretData esv1.PushSecretData
  59. }
  60. func (b *psBuilder) buildMetadata(_, labels map[string]string, _ []*secretmanagerpb.Topic) (map[string]string, map[string]string, []string, error) {
  61. if manager, ok := labels[managedByKey]; !ok || manager != managedByValue {
  62. return nil, nil, nil, fmt.Errorf("secret %v is not managed by external secrets", b.pushSecretData.GetRemoteKey())
  63. }
  64. var meta *metadata.PushSecretMetadata[PushSecretMetadataSpec]
  65. if b.pushSecretData.GetMetadata() != nil {
  66. var err error
  67. meta, err = metadata.ParseMetadataParameters[PushSecretMetadataSpec](b.pushSecretData.GetMetadata())
  68. if err != nil {
  69. return nil, nil, nil, fmt.Errorf("failed to parse PushSecret metadata: %w", err)
  70. }
  71. }
  72. var spec PushSecretMetadataSpec
  73. if meta != nil {
  74. spec = meta.Spec
  75. }
  76. newLabels := map[string]string{}
  77. maps.Copy(newLabels, spec.Labels)
  78. if spec.MergePolicy == PushSecretMetadataMergePolicyMerge {
  79. // Keep labels from the existing GCP Secret Manager Secret
  80. maps.Copy(newLabels, labels)
  81. }
  82. newLabels[managedByKey] = managedByValue
  83. return spec.Annotations, newLabels, spec.Topics, nil
  84. }
  85. func (b *psBuilder) needUpdate(original []byte) bool {
  86. if original == nil {
  87. return true
  88. }
  89. return !bytes.Equal(b.payload, original)
  90. }
  91. func (b *psBuilder) buildData(_ []byte) ([]byte, error) {
  92. return b.payload, nil
  93. }
  94. type propertyPSBuilder struct {
  95. payload []byte
  96. pushSecretData esv1.PushSecretData
  97. }
  98. func (b *propertyPSBuilder) buildMetadata(annotations, labels map[string]string, topics []*secretmanagerpb.Topic) (map[string]string, map[string]string, []string, error) {
  99. newAnnotations := map[string]string{}
  100. newLabels := map[string]string{}
  101. if annotations != nil {
  102. newAnnotations = annotations
  103. }
  104. if labels != nil {
  105. newLabels = labels
  106. }
  107. newLabels[managedByKey] = managedByValue
  108. result := make([]string, 0, len(topics))
  109. for _, t := range topics {
  110. result = append(result, t.Name)
  111. }
  112. return newAnnotations, newLabels, result, nil
  113. }
  114. func (b *propertyPSBuilder) needUpdate(original []byte) bool {
  115. if original == nil {
  116. return true
  117. }
  118. val := getDataByProperty(original, b.pushSecretData.GetProperty())
  119. return !val.Exists() || val.String() != string(b.payload)
  120. }
  121. func (b *propertyPSBuilder) buildData(original []byte) ([]byte, error) {
  122. var base []byte
  123. if original != nil {
  124. base = original
  125. }
  126. return sjson.SetBytes(base, b.pushSecretData.GetProperty(), b.payload)
  127. }