Jelajahi Sumber

Merge branch 'main' into beach-team

Signed-off-by: Gustavo Carvalho <gusfcarvalho@gmail.com>
Gustavo Carvalho 3 tahun lalu
induk
melakukan
e54347aca6
82 mengubah file dengan 1792 tambahan dan 290 penghapusan
  1. 1 1
      .github/workflows/ci.yml
  2. 2 2
      .github/workflows/e2e.yml
  3. 2 2
      .github/workflows/helm.yml
  4. 1 1
      .github/workflows/release.yml
  5. 1 1
      Dockerfile.standalone
  6. 3 37
      Makefile
  7. 1 1
      apis/externalsecrets/v1alpha1/secretstore_azurekv_types.go
  8. 22 4
      apis/externalsecrets/v1beta1/externalsecret_types.go
  9. 1 1
      apis/externalsecrets/v1beta1/secretstore_azurekv_types.go
  10. 15 2
      apis/externalsecrets/v1beta1/secretstore_ibm_types.go
  11. 28 0
      apis/externalsecrets/v1beta1/secretstore_types.go
  12. 0 25
      apis/externalsecrets/v1beta1/secretstore_vault_types.go
  13. 58 0
      apis/externalsecrets/v1beta1/zz_generated.deepcopy.go
  14. 32 18
      cmd/root.go
  15. 34 6
      config/crds/bases/external-secrets.io_clusterexternalsecrets.yaml
  16. 26 8
      config/crds/bases/external-secrets.io_clustersecretstores.yaml
  17. 32 6
      config/crds/bases/external-secrets.io_externalsecrets.yaml
  18. 26 8
      config/crds/bases/external-secrets.io_secretstores.yaml
  19. 8 0
      config/crds/bases/kustomization.yaml
  20. 2 2
      deploy/charts/external-secrets/Chart.yaml
  21. 10 0
      deploy/charts/external-secrets/README.md
  22. 0 1
      deploy/charts/external-secrets/README.md.gotmpl
  23. 13 2
      deploy/charts/external-secrets/templates/cert-controller-service.yaml
  24. 4 0
      deploy/charts/external-secrets/templates/deployment.yaml
  25. 13 2
      deploy/charts/external-secrets/templates/service.yaml
  26. 14 4
      deploy/charts/external-secrets/templates/webhook-service.yaml
  27. 37 0
      deploy/charts/external-secrets/values.yaml
  28. 95 26
      deploy/crds/bundle.yaml
  29. 105 0
      docs/guides-datafrom-rewrite.md
  30. 7 2
      docs/guides-getallsecrets.md
  31. 2 0
      docs/guides-metrics.md
  32. TEMPAT SAMPAH
      docs/pictures/screenshot_container_auth_create_1.png
  33. TEMPAT SAMPAH
      docs/pictures/screenshot_container_auth_create_2.png
  34. TEMPAT SAMPAH
      docs/pictures/screenshot_container_auth_create_3.png
  35. TEMPAT SAMPAH
      docs/pictures/screenshot_container_auth_create_button.png
  36. TEMPAT SAMPAH
      docs/pictures/screenshot_container_auth_create_group.png
  37. TEMPAT SAMPAH
      docs/pictures/screenshot_container_auth_create_group_1.png
  38. TEMPAT SAMPAH
      docs/pictures/screenshot_container_auth_create_group_2.png
  39. TEMPAT SAMPAH
      docs/pictures/screenshot_container_auth_create_group_3.png
  40. TEMPAT SAMPAH
      docs/pictures/screenshot_container_auth_create_group_4.png
  41. TEMPAT SAMPAH
      docs/pictures/screenshot_container_auth_iam_left.png
  42. 1 1
      docs/provider-1password-automation.md
  43. 21 7
      docs/provider-azure-key-vault.md
  44. 66 7
      docs/provider-ibm-secrets-manager.md
  45. 24 0
      docs/snippets/azkv-pkcs12-cert-external-secret.yaml
  46. 24 0
      docs/snippets/datafrom-rewrite-conflict.yaml
  47. 18 0
      docs/snippets/datafrom-rewrite-invalid-characters.yaml
  48. 20 0
      docs/snippets/datafrom-rewrite-remove-path.yaml
  49. 14 0
      docs/snippets/full-external-secret.yaml
  50. 22 0
      docs/snippets/ibm-container-auth-volume.yaml
  51. 4 0
      docs/snippets/ibm-secret-store.yaml
  52. 216 11
      docs/spec.md
  53. 0 1
      e2e/run.sh
  54. 1 0
      e2e/suites/provider/cases/akeyless/akeyless.go
  55. 1 0
      e2e/suites/provider/cases/alibaba/alibaba.go
  56. 1 0
      e2e/suites/provider/cases/aws/parameterstore/parameterstore.go
  57. 1 0
      e2e/suites/provider/cases/aws/secretsmanager/secretsmanager.go
  58. 1 0
      e2e/suites/provider/cases/azure/azure_secret.go
  59. 37 0
      e2e/suites/provider/cases/common/common.go
  60. 50 2
      e2e/suites/provider/cases/common/find_by_name.go
  61. 2 0
      e2e/suites/provider/cases/gcp/gcp.go
  62. 1 0
      e2e/suites/provider/cases/gitlab/gitlab.go
  63. 1 0
      e2e/suites/provider/cases/kubernetes/kubernetes.go
  64. 1 0
      e2e/suites/provider/cases/oracle/oracle.go
  65. 12 0
      e2e/suites/provider/cases/vault/vault.go
  66. 17 17
      go.mod
  67. 45 38
      go.sum
  68. 7 0
      hack/api-docs/mkdocs.yml
  69. 33 0
      hack/crd.generate.sh
  70. 35 0
      hack/helm.generate.sh
  71. 2 0
      pkg/controllers/clusterexternalsecret/clusterexternalsecret_controller.go
  72. 26 5
      pkg/controllers/externalsecret/externalsecret_controller.go
  73. 168 0
      pkg/controllers/externalsecret/externalsecret_controller_test.go
  74. 5 1
      pkg/provider/akeyless/akeyless_api.go
  75. 68 19
      pkg/provider/ibm/provider.go
  76. 13 0
      pkg/provider/ibm/provider_test.go
  77. 21 4
      pkg/provider/onepassword/fake/fake.go
  78. 7 14
      pkg/provider/onepassword/onepassword.go
  79. 1 1
      pkg/provider/onepassword/onepassword_test.go
  80. 3 0
      pkg/provider/vault/vault.go
  81. 49 0
      pkg/utils/utils.go
  82. 158 0
      pkg/utils/utils_test.go

+ 1 - 1
.github/workflows/ci.yml

@@ -259,7 +259,7 @@ jobs:
 
       - name: Install cosign
         if: github.ref == 'refs/heads/main' && env.GHCR_USERNAME != ''
-        uses: sigstore/cosign-installer@v2.4.1
+        uses: sigstore/cosign-installer@v2.5.0
 
       - name: Sign Artifacts to main release channel
         if: github.ref == 'refs/heads/main' && env.GHCR_USERNAME != ''

+ 2 - 2
.github/workflows/e2e.yml

@@ -8,7 +8,7 @@ env:
   # Common versions
   GO_VERSION: '1.17'
   GOLANGCI_VERSION: 'v1.33'
-  GINKGO_VERSION: 'v2.1.3'
+  GINKGO_VERSION: 'v2.1.4'
   DOCKER_BUILDX_VERSION: 'v0.4.2'
   KIND_VERSION: 'v0.11.1'
   KIND_IMAGE: 'kindest/node:v1.23.3'
@@ -35,7 +35,7 @@ jobs:
   # Branch-based pull request
   integration-trusted:
     runs-on: ubuntu-latest
-    if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
+    if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && github.actor !='dependabot[bot]'
     steps:
 
     - name: Branch based PR checkout

+ 2 - 2
.github/workflows/helm.yml

@@ -26,7 +26,7 @@ jobs:
           make helm.generate
 
       - name: Set up Helm
-        uses: azure/setup-helm@v3.1
+        uses: azure/setup-helm@v3.3
         with:
           version: v3.4.2
 
@@ -62,7 +62,7 @@ jobs:
           git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
 
       - name: Set up Helm
-        uses: azure/setup-helm@v3.1
+        uses: azure/setup-helm@v3.3
         with:
           version: v3.4.2
 

+ 1 - 1
.github/workflows/release.yml

@@ -126,7 +126,7 @@ jobs:
 
       - name: Install cosign
         if: env.GHCR_USERNAME != ''
-        uses: sigstore/cosign-installer@v2.4.1
+        uses: sigstore/cosign-installer@v2.5.0
 
       - name: Sign Container Image
         if: env.GHCR_USERNAME != ''

+ 1 - 1
Dockerfile.standalone

@@ -1,5 +1,5 @@
 # This version of Dockerfile is for building without external dependencies.
-FROM golang:1.18-alpine AS builder
+FROM golang:1.19.0-alpine AS builder
 ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64
 WORKDIR /app
 # Avoid invalidating the `go mod download` cache when only code has changed.

+ 3 - 37
Makefile

@@ -17,7 +17,7 @@ all: $(addprefix build-,$(ARCH))
 export IMAGE_REGISTRY ?= ghcr.io/external-secrets/external-secrets
 
 #Valid licenses for license.check
-LICENSES ?= Apache-2.0|MIT|BSD-3-Clause|ISC|MPL-2.0|BSD-2-Clause|Unknown
+LICENSES ?= Apache-2.0|MIT|BSD-3-Clause|ISC|MPL-2.0|BSD-2-Clause
 BUNDLE_DIR     ?= deploy/crds
 CRD_DIR     ?= config/crds
 
@@ -145,15 +145,7 @@ fmt: lint.check ## Ensure consistent code style
 	@$(OK) Ensured consistent code style
 
 generate: ## Generate code and crds
-	@go run sigs.k8s.io/controller-tools/cmd/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."
-	@go run sigs.k8s.io/controller-tools/cmd/controller-gen crd paths="./..." output:crd:artifacts:config=$(CRD_DIR)/bases
-# Remove extra header lines in generated CRDs
-	@for i in $(CRD_DIR)/bases/*.yaml; do \
-  		tail -n +2 <"$$i" >"$$i.bkp" && \
-  		cp "$$i.bkp" "$$i" && \
-  		rm "$$i.bkp"; \
-  	done
-	@yq e '.spec.conversion.strategy = "Webhook" | .spec.conversion.webhook.conversionReviewVersions = ["v1"] | .spec.conversion.webhook.clientConfig.service.name = "kubernetes" | .spec.conversion.webhook.clientConfig.service.namespace = "default" |	.spec.conversion.webhook.clientConfig.service.path = "/convert"' $(CRD_DIR)/bases/*  > $(BUNDLE_DIR)/bundle.yaml
+	@./hack/crd.generate.sh $(BUNDLE_DIR) $(CRD_DIR)
 	@$(OK) Finished generating deepcopy and crds
 
 # ====================================================================================
@@ -190,33 +182,7 @@ helm.build: helm.generate ## Build helm chart
 	@$(OK) helm package
 
 helm.generate:
-# Split the generated bundle yaml file to inject control flags
-	@for i in $(BUNDLE_DIR)/*.yaml; do \
-		yq e -Ns '"$(HELM_DIR)/templates/crds/" + .spec.names.singular' "$$i"; \
-	done
-# Add helm if statement for controlling the install of CRDs
-	@for i in $(HELM_DIR)/templates/crds/*.yml; do \
-		export CRDS_FLAG_NAME="create$$(yq e '.spec.names.kind' $$i)"; \
-		cp "$$i" "$$i.bkp"; \
-		if [[ "$$CRDS_FLAG_NAME" == *"Cluster"* ]]; then \
-			echo "{{- if and (.Values.installCRDs) (.Values.crds.$$CRDS_FLAG_NAME) }}" > "$$i"; \
-		elif [[ "$$CRDS_FLAG_NAME" == *"PushSecret"* ]]; then \
-			echo "{{- if and (.Values.installCRDs) (.Values.crds.$$CRDS_FLAG_NAME) }}" > "$$i"; \
-		else \
-			echo "{{- if .Values.installCRDs }}" > "$$i"; \
-		fi; \
-		cat "$$i.bkp" >> "$$i" && \
-		echo "{{- end }}" >> "$$i" && \
-		rm "$$i.bkp" && \
-		if [[ "$$OSTYPE" == "darwin"* ]]; then \
-		  SEDPRG="gsed"; \
-		else \
-		  SEDPRG="sed"; \
-		fi; \
-		$$SEDPRG -i 's/name: kubernetes/name: {{ include "external-secrets.fullname" . }}-webhook/g' "$$i" && \
-		$$SEDPRG -i 's/namespace: default/namespace: {{ .Release.Namespace | quote }}/g' "$$i" && \
-		mv "$$i" "$${i%.yml}.yaml"; \
-	done
+	./hack/helm.generate.sh $(BUNDLE_DIR) $(HELM_DIR)
 	@$(OK) Finished generating helm chart files
 
 # ====================================================================================

+ 1 - 1
apis/externalsecrets/v1alpha1/secretstore_azurekv_types.go

@@ -27,7 +27,7 @@ const (
 	// Using service principal to authenticate, which needs a tenantId, a clientId and a clientSecret.
 	AzureServicePrincipal AzureAuthType = "ServicePrincipal"
 
-	// Using Managed Identity to authenticate. Used with aad-pod-identity installed in the clister.
+	// Using Managed Identity to authenticate. Used with aad-pod-identity installed in the cluster.
 	AzureManagedIdentity AzureAuthType = "ManagedIdentity"
 
 	// Using Workload Identity service accounts to authenticate.

+ 22 - 4
apis/externalsecrets/v1beta1/externalsecret_types.go

@@ -184,7 +184,7 @@ type ExternalSecretDataRemoteRef struct {
 	ConversionStrategy ExternalSecretConversionStrategy `json:"conversionStrategy,omitempty"`
 
 	// +optional
-	// Used to define a conversion Strategy
+	// Used to define a decoding Strategy
 	// +kubebuilder:default="None"
 	DecodingStrategy ExternalSecretDecodingStrategy `json:"decodingStrategy,omitempty"`
 }
@@ -212,8 +212,6 @@ const (
 	ExternalSecretDecodeNone      ExternalSecretDecodingStrategy = "None"
 )
 
-// +kubebuilder:validation:MinProperties=1
-// +kubebuilder:validation:MaxProperties=1
 type ExternalSecretDataFromRemoteRef struct {
 	// Used to extract multiple key/value pairs from one secret
 	// +optional
@@ -221,8 +219,26 @@ type ExternalSecretDataFromRemoteRef struct {
 	// Used to find secrets based on tags or regular expressions
 	// +optional
 	Find *ExternalSecretFind `json:"find,omitempty"`
+
+	// Used to rewrite secret Keys after getting them from the secret Provider
+	// Multiple Rewrite operations can be provided. They are applied in a layered order (first to last)
+	// +optional
+	Rewrite []ExternalSecretRewrite `json:"rewrite,omitempty"`
 }
 
+type ExternalSecretRewrite struct {
+	// Used to rewrite with regular expressions.
+	// The resulting key will be the output of a regexp.ReplaceAll operation.
+	// +optional
+	Regexp *ExternalSecretRewriteRegexp `json:"regexp,omitempty"`
+}
+
+type ExternalSecretRewriteRegexp struct {
+	// Used to define the regular expression of a re.Compiler.
+	Source string `json:"source"`
+	// Used to define the target pattern of a ReplaceAll operation.
+	Target string `json:"target"`
+}
 type ExternalSecretFind struct {
 	// A root path to start the find operations.
 	// +optional
@@ -241,7 +257,7 @@ type ExternalSecretFind struct {
 	ConversionStrategy ExternalSecretConversionStrategy `json:"conversionStrategy,omitempty"`
 
 	// +optional
-	// Used to define a conversion Strategy
+	// Used to define a decoding Strategy
 	// +kubebuilder:default="None"
 	DecodingStrategy ExternalSecretDecodingStrategy `json:"decodingStrategy,omitempty"`
 }
@@ -255,6 +271,7 @@ type FindName struct {
 // ExternalSecretSpec defines the desired state of ExternalSecret.
 type ExternalSecretSpec struct {
 	SecretStoreRef SecretStoreRef `json:"secretStoreRef"`
+	// +kubebuilder:default={creationPolicy:Owner,deletionPolicy:Retain}
 	// +optional
 	Target ExternalSecretTarget `json:"target,omitempty"`
 
@@ -307,6 +324,7 @@ const (
 	ReasonUnavailableStore     = "UnavailableStore"
 	ReasonProviderClientConfig = "InvalidProviderClientConfig"
 	ReasonUpdateFailed         = "UpdateFailed"
+	ReasonDeprecated           = "ParameterDeprecated"
 	ReasonUpdated              = "Updated"
 	ReasonDeleted              = "Deleted"
 )

+ 1 - 1
apis/externalsecrets/v1beta1/secretstore_azurekv_types.go

@@ -27,7 +27,7 @@ const (
 	// Using service principal to authenticate, which needs a tenantId, a clientId and a clientSecret.
 	AzureServicePrincipal AzureAuthType = "ServicePrincipal"
 
-	// Using Managed Identity to authenticate. Used with aad-pod-identity installed in the clister.
+	// Using Managed Identity to authenticate. Used with aad-pod-identity installed in the cluster.
 	AzureManagedIdentity AzureAuthType = "ManagedIdentity"
 
 	// Using Workload Identity service accounts to authenticate.

+ 15 - 2
apis/externalsecrets/v1beta1/secretstore_ibm_types.go

@@ -28,12 +28,25 @@ type IBMProvider struct {
 	ServiceURL *string `json:"serviceUrl,omitempty"`
 }
 
+// +kubebuilder:validation:MinProperties=1
+// +kubebuilder:validation:MaxProperties=1
 type IBMAuth struct {
-	SecretRef IBMAuthSecretRef `json:"secretRef"`
+	SecretRef     IBMAuthSecretRef     `json:"secretRef,omitempty"`
+	ContainerAuth IBMAuthContainerAuth `json:"containerAuth,omitempty"`
 }
 
 type IBMAuthSecretRef struct {
 	// The SecretAccessKey is used for authentication
-	// +optional
 	SecretAPIKey esmeta.SecretKeySelector `json:"secretApiKeySecretRef,omitempty"`
 }
+
+// IBM Container-based auth with IAM Trusted Profile.
+type IBMAuthContainerAuth struct {
+	// the IBM Trusted Profile
+	Profile string `json:"profile"`
+
+	// Location the token is mounted on the pod
+	TokenLocation string `json:"tokenLocation,omitempty"`
+
+	IAMEndpoint string `json:"iamEndpoint,omitempty"`
+}

+ 28 - 0
apis/externalsecrets/v1beta1/secretstore_types.go

@@ -107,6 +107,34 @@ type SecretStoreProvider struct {
 	Senhasegura *SenhaseguraProvider `json:"senhasegura,omitempty"`
 }
 
+type CAProviderType string
+
+const (
+	CAProviderTypeSecret    CAProviderType = "Secret"
+	CAProviderTypeConfigMap CAProviderType = "ConfigMap"
+)
+
+// Used to provide custom certificate authority (CA) certificates
+// for a secret store. The CAProvider points to a Secret or ConfigMap resource
+// that contains a PEM-encoded certificate.
+type CAProvider struct {
+	// The type of provider to use such as "Secret", or "ConfigMap".
+	// +kubebuilder:validation:Enum="Secret";"ConfigMap"
+	Type CAProviderType `json:"type"`
+
+	// The name of the object located at the provider type.
+	Name string `json:"name"`
+
+	// The key where the CA certificate can be found in the Secret or ConfigMap.
+	// +kubebuilder:validation:Optional
+	Key string `json:"key,omitempty"`
+
+	// The namespace the Provider type is in.
+	// Can only be defined when used in a ClusterSecretStore.
+	// +optional
+	Namespace *string `json:"namespace,omitempty"`
+}
+
 type SecretStoreRetrySettings struct {
 	MaxRetries    *int32  `json:"maxRetries,omitempty"`
 	RetryInterval *string `json:"retryInterval,omitempty"`

+ 0 - 25
apis/externalsecrets/v1beta1/secretstore_vault_types.go

@@ -25,31 +25,6 @@ const (
 	VaultKVStoreV2 VaultKVStoreVersion = "v2"
 )
 
-type CAProviderType string
-
-const (
-	CAProviderTypeSecret    CAProviderType = "Secret"
-	CAProviderTypeConfigMap CAProviderType = "ConfigMap"
-)
-
-// Defines a location to fetch the cert for the vault provider from.
-type CAProvider struct {
-	// The type of provider to use such as "Secret", or "ConfigMap".
-	// +kubebuilder:validation:Enum="Secret";"ConfigMap"
-	Type CAProviderType `json:"type"`
-
-	// The name of the object located at the provider type.
-	Name string `json:"name"`
-
-	// The key the value inside of the provider type to use, only used with "Secret" type
-	// +kubebuilder:validation:Optional
-	Key string `json:"key,omitempty"`
-
-	// The namespace the Provider type is in.
-	// +optional
-	Namespace *string `json:"namespace,omitempty"`
-}
-
 // Configures an store to sync secrets using a HashiCorp Vault
 // KV backend.
 type VaultProvider struct {

+ 58 - 0
apis/externalsecrets/v1beta1/zz_generated.deepcopy.go

@@ -578,6 +578,13 @@ func (in *ExternalSecretDataFromRemoteRef) DeepCopyInto(out *ExternalSecretDataF
 		*out = new(ExternalSecretFind)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.Rewrite != nil {
+		in, out := &in.Rewrite, &out.Rewrite
+		*out = make([]ExternalSecretRewrite, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalSecretDataFromRemoteRef.
@@ -669,6 +676,41 @@ func (in *ExternalSecretList) DeepCopyObject() runtime.Object {
 	return nil
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ExternalSecretRewrite) DeepCopyInto(out *ExternalSecretRewrite) {
+	*out = *in
+	if in.Regexp != nil {
+		in, out := &in.Regexp, &out.Regexp
+		*out = new(ExternalSecretRewriteRegexp)
+		**out = **in
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalSecretRewrite.
+func (in *ExternalSecretRewrite) DeepCopy() *ExternalSecretRewrite {
+	if in == nil {
+		return nil
+	}
+	out := new(ExternalSecretRewrite)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ExternalSecretRewriteRegexp) DeepCopyInto(out *ExternalSecretRewriteRegexp) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalSecretRewriteRegexp.
+func (in *ExternalSecretRewriteRegexp) DeepCopy() *ExternalSecretRewriteRegexp {
+	if in == nil {
+		return nil
+	}
+	out := new(ExternalSecretRewriteRegexp)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ExternalSecretSpec) DeepCopyInto(out *ExternalSecretSpec) {
 	*out = *in
@@ -1035,6 +1077,7 @@ func (in *GitlabSecretRef) DeepCopy() *GitlabSecretRef {
 func (in *IBMAuth) DeepCopyInto(out *IBMAuth) {
 	*out = *in
 	in.SecretRef.DeepCopyInto(&out.SecretRef)
+	out.ContainerAuth = in.ContainerAuth
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IBMAuth.
@@ -1047,6 +1090,21 @@ func (in *IBMAuth) DeepCopy() *IBMAuth {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *IBMAuthContainerAuth) DeepCopyInto(out *IBMAuthContainerAuth) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IBMAuthContainerAuth.
+func (in *IBMAuthContainerAuth) DeepCopy() *IBMAuthContainerAuth {
+	if in == nil {
+		return nil
+	}
+	out := new(IBMAuthContainerAuth)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *IBMAuthSecretRef) DeepCopyInto(out *IBMAuthSecretRef) {
 	*out = *in

+ 32 - 18
cmd/root.go

@@ -51,8 +51,12 @@ var (
 	healthzAddr                           string
 	controllerClass                       string
 	enableLeaderElection                  bool
+	enableSecretsCache                    bool
+	enableConfigMapsCache                 bool
 	concurrent                            int
 	port                                  int
+	clientQPS                             float32
+	clientBurst                           int
 	loglevel                              string
 	namespace                             string
 	enableClusterStoreReconciler          bool
@@ -85,6 +89,19 @@ var rootCmd = &cobra.Command{
 	Long:  `For more information visit https://external-secrets.io`,
 	Run: func(cmd *cobra.Command, args []string) {
 		var lvl zapcore.Level
+		// the client creates a ListWatch for all resource kinds that
+		// are requested with .Get().
+		// We want to avoid to cache all secrets or configmaps in memory.
+		// The ES controller uses v1.PartialObjectMetadata for the secrets
+		// that he owns.
+		// see #721
+		cacheList := make([]client.Object, 0)
+		if !enableSecretsCache {
+			cacheList = append(cacheList, &v1.Secret{})
+		}
+		if !enableConfigMapsCache {
+			cacheList = append(cacheList, &v1.ConfigMap{})
+		}
 		err := lvl.UnmarshalText([]byte(loglevel))
 		if err != nil {
 			setupLog.Error(err, "error unmarshalling loglevel")
@@ -92,24 +109,17 @@ var rootCmd = &cobra.Command{
 		}
 		logger := zap.New(zap.Level(lvl))
 		ctrl.SetLogger(logger)
-
-		mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
-			Scheme:             scheme,
-			MetricsBindAddress: metricsAddr,
-			Port:               9443,
-			LeaderElection:     enableLeaderElection,
-			LeaderElectionID:   "external-secrets-controller",
-			ClientDisableCacheFor: []client.Object{
-				// the client creates a ListWatch for all resource kinds that
-				// are requested with .Get().
-				// We want to avoid to cache all secrets or configmaps in memory.
-				// The ES controller uses v1.PartialObjectMetadata for the secrets
-				// that he owns.
-				// see #721
-				&v1.Secret{},
-				&v1.ConfigMap{},
-			},
-			Namespace: namespace,
+		config := ctrl.GetConfigOrDie()
+		config.QPS = clientQPS
+		config.Burst = clientBurst
+		mgr, err := ctrl.NewManager(config, ctrl.Options{
+			Scheme:                scheme,
+			MetricsBindAddress:    metricsAddr,
+			Port:                  9443,
+			LeaderElection:        enableLeaderElection,
+			LeaderElectionID:      "external-secrets-controller",
+			ClientDisableCacheFor: cacheList,
+			Namespace:             namespace,
 		})
 		if err != nil {
 			setupLog.Error(err, "unable to start manager")
@@ -199,11 +209,15 @@ func init() {
 		"Enable leader election for controller manager. "+
 			"Enabling this will ensure there is only one active controller manager.")
 	rootCmd.Flags().IntVar(&concurrent, "concurrent", 1, "The number of concurrent ExternalSecret reconciles.")
+	rootCmd.Flags().Float32Var(&clientQPS, "client-qps", 0, "QPS configuration to be passed to rest.Client")
+	rootCmd.Flags().IntVar(&clientBurst, "client-burst", 0, "Maximum Burst allowed to be passed to rest.Client")
 	rootCmd.Flags().StringVar(&loglevel, "loglevel", "info", "loglevel to use, one of: debug, info, warn, error, dpanic, panic, fatal")
 	rootCmd.Flags().StringVar(&namespace, "namespace", "", "watch external secrets scoped in the provided namespace only. ClusterSecretStore can be used but only work if it doesn't reference resources from other namespaces")
 	rootCmd.Flags().BoolVar(&enableClusterStoreReconciler, "enable-cluster-store-reconciler", true, "Enable cluster store reconciler.")
 	rootCmd.Flags().BoolVar(&enableClusterExternalSecretReconciler, "enable-cluster-external-secret-reconciler", true, "Enable cluster external secret reconciler.")
 	rootCmd.Flags().BoolVar(&enablePushSecretReconciler, "experimental-enable-push-secret-reconciler", false, "Enable push secret reconciler.")
+	rootCmd.Flags().BoolVar(&enableSecretsCache, "enable-secrets-caching", false, "Enable secrets caching for external-secrets pod.")
+	rootCmd.Flags().BoolVar(&enableConfigMapsCache, "enable-configmaps-caching", false, "Enable secrets caching for external-secrets pod.")
 	rootCmd.Flags().DurationVar(&storeRequeueInterval, "store-requeue-interval", time.Minute*5, "Default Time duration between reconciling (Cluster)SecretStores")
 	rootCmd.Flags().BoolVar(&enableFloodGate, "enable-flood-gate", true, "Enable flood gate. External secret will be reconciled only if the ClusterStore or Store have an healthy or unknown state.")
 	rootCmd.Flags().BoolVar(&enableAWSSession, "experimental-enable-aws-session-cache", false, "Enable experimental AWS session cache. External secret will reuse the AWS session without creating a new one on each request.")

+ 34 - 6
config/crds/bases/external-secrets.io_clusterexternalsecrets.yaml

@@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.9.0
+    controller-gen.kubebuilder.io/version: v0.9.2
   creationTimestamp: null
   name: clusterexternalsecrets.external-secrets.io
 spec:
@@ -77,7 +77,7 @@ spec:
                               type: string
                             decodingStrategy:
                               default: None
-                              description: Used to define a conversion Strategy
+                              description: Used to define a decoding Strategy
                               type: string
                             key:
                               description: Key is the key used in the Provider, mandatory
@@ -110,8 +110,6 @@ spec:
                       Provider data If multiple entries are specified, the Secret
                       keys are merged in the specified order
                     items:
-                      maxProperties: 1
-                      minProperties: 1
                       properties:
                         extract:
                           description: Used to extract multiple key/value pairs from
@@ -123,7 +121,7 @@ spec:
                               type: string
                             decodingStrategy:
                               default: None
-                              description: Used to define a conversion Strategy
+                              description: Used to define a decoding Strategy
                               type: string
                             key:
                               description: Key is the key used in the Provider, mandatory
@@ -154,7 +152,7 @@ spec:
                               type: string
                             decodingStrategy:
                               default: None
-                              description: Used to define a conversion Strategy
+                              description: Used to define a decoding Strategy
                               type: string
                             name:
                               description: Finds secrets based on the name.
@@ -172,6 +170,32 @@ spec:
                               description: Find secrets based on tags.
                               type: object
                           type: object
+                        rewrite:
+                          description: Used to rewrite secret Keys after getting them
+                            from the secret Provider Multiple Rewrite operations can
+                            be provided. They are applied in a layered order (first
+                            to last)
+                          items:
+                            properties:
+                              regexp:
+                                description: Used to rewrite with regular expressions.
+                                  The resulting key will be the output of a regexp.ReplaceAll
+                                  operation.
+                                properties:
+                                  source:
+                                    description: Used to define the regular expression
+                                      of a re.Compiler.
+                                    type: string
+                                  target:
+                                    description: Used to define the target pattern
+                                      of a ReplaceAll operation.
+                                    type: string
+                                required:
+                                - source
+                                - target
+                                type: object
+                            type: object
+                          type: array
                       type: object
                     type: array
                   refreshInterval:
@@ -196,6 +220,9 @@ spec:
                     - name
                     type: object
                   target:
+                    default:
+                      creationPolicy: Owner
+                      deletionPolicy: Retain
                     description: ExternalSecretTarget defines the Kubernetes Secret
                       to be created There can be only one target per ExternalSecret.
                     properties:
@@ -344,6 +371,7 @@ spec:
                       are ANDed.
                     type: object
                 type: object
+                x-kubernetes-map-type: atomic
               refreshTime:
                 description: The time in which the controller should reconcile it's
                   objects and recheck namespaces for labels.

+ 26 - 8
config/crds/bases/external-secrets.io_clustersecretstores.yaml

@@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.9.0
+    controller-gen.kubebuilder.io/version: v0.9.2
   creationTimestamp: null
   name: clustersecretstores.external-secrets.io
 spec:
@@ -1907,7 +1907,25 @@ spec:
                       auth:
                         description: Auth configures how secret-manager authenticates
                           with the IBM secrets manager.
+                        maxProperties: 1
+                        minProperties: 1
                         properties:
+                          containerAuth:
+                            description: IBM Container-based auth with IAM Trusted
+                              Profile.
+                            properties:
+                              iamEndpoint:
+                                type: string
+                              profile:
+                                description: the IBM Trusted Profile
+                                type: string
+                              tokenLocation:
+                                description: Location the token is mounted on the
+                                  pod
+                                type: string
+                            required:
+                            - profile
+                            type: object
                           secretRef:
                             properties:
                               secretApiKeySecretRef:
@@ -1931,8 +1949,6 @@ spec:
                                     type: string
                                 type: object
                             type: object
-                        required:
-                        - secretRef
                         type: object
                       serviceUrl:
                         description: ServiceURL is the Endpoint URL that is specific
@@ -2057,8 +2073,8 @@ spec:
                             description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider'
                             properties:
                               key:
-                                description: The key the value inside of the provider
-                                  type to use, only used with "Secret" type
+                                description: The key where the CA certificate can
+                                  be found in the Secret or ConfigMap.
                                 type: string
                               name:
                                 description: The name of the object located at the
@@ -2066,6 +2082,7 @@ spec:
                                 type: string
                               namespace:
                                 description: The namespace the Provider type is in.
+                                  Can only be defined when used in a ClusterSecretStore.
                                 type: string
                               type:
                                 description: The type of provider to use such as "Secret",
@@ -2590,15 +2607,16 @@ spec:
                           Vault server certificate.
                         properties:
                           key:
-                            description: The key the value inside of the provider
-                              type to use, only used with "Secret" type
+                            description: The key where the CA certificate can be found
+                              in the Secret or ConfigMap.
                             type: string
                           name:
                             description: The name of the object located at the provider
                               type.
                             type: string
                           namespace:
-                            description: The namespace the Provider type is in.
+                            description: The namespace the Provider type is in. Can
+                              only be defined when used in a ClusterSecretStore.
                             type: string
                           type:
                             description: The type of provider to use such as "Secret",

+ 32 - 6
config/crds/bases/external-secrets.io_externalsecrets.yaml

@@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.9.0
+    controller-gen.kubebuilder.io/version: v0.9.2
   creationTimestamp: null
   name: externalsecrets.external-secrets.io
 spec:
@@ -312,7 +312,7 @@ spec:
                           type: string
                         decodingStrategy:
                           default: None
-                          description: Used to define a conversion Strategy
+                          description: Used to define a decoding Strategy
                           type: string
                         key:
                           description: Key is the key used in the Provider, mandatory
@@ -345,8 +345,6 @@ spec:
                   Provider data If multiple entries are specified, the Secret keys
                   are merged in the specified order
                 items:
-                  maxProperties: 1
-                  minProperties: 1
                   properties:
                     extract:
                       description: Used to extract multiple key/value pairs from one
@@ -358,7 +356,7 @@ spec:
                           type: string
                         decodingStrategy:
                           default: None
-                          description: Used to define a conversion Strategy
+                          description: Used to define a decoding Strategy
                           type: string
                         key:
                           description: Key is the key used in the Provider, mandatory
@@ -388,7 +386,7 @@ spec:
                           type: string
                         decodingStrategy:
                           default: None
-                          description: Used to define a conversion Strategy
+                          description: Used to define a decoding Strategy
                           type: string
                         name:
                           description: Finds secrets based on the name.
@@ -406,6 +404,31 @@ spec:
                           description: Find secrets based on tags.
                           type: object
                       type: object
+                    rewrite:
+                      description: Used to rewrite secret Keys after getting them
+                        from the secret Provider Multiple Rewrite operations can be
+                        provided. They are applied in a layered order (first to last)
+                      items:
+                        properties:
+                          regexp:
+                            description: Used to rewrite with regular expressions.
+                              The resulting key will be the output of a regexp.ReplaceAll
+                              operation.
+                            properties:
+                              source:
+                                description: Used to define the regular expression
+                                  of a re.Compiler.
+                                type: string
+                              target:
+                                description: Used to define the target pattern of
+                                  a ReplaceAll operation.
+                                type: string
+                            required:
+                            - source
+                            - target
+                            type: object
+                        type: object
+                      type: array
                   type: object
                 type: array
               refreshInterval:
@@ -430,6 +453,9 @@ spec:
                 - name
                 type: object
               target:
+                default:
+                  creationPolicy: Owner
+                  deletionPolicy: Retain
                 description: ExternalSecretTarget defines the Kubernetes Secret to
                   be created There can be only one target per ExternalSecret.
                 properties:

+ 26 - 8
config/crds/bases/external-secrets.io_secretstores.yaml

@@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.9.0
+    controller-gen.kubebuilder.io/version: v0.9.2
   creationTimestamp: null
   name: secretstores.external-secrets.io
 spec:
@@ -1907,7 +1907,25 @@ spec:
                       auth:
                         description: Auth configures how secret-manager authenticates
                           with the IBM secrets manager.
+                        maxProperties: 1
+                        minProperties: 1
                         properties:
+                          containerAuth:
+                            description: IBM Container-based auth with IAM Trusted
+                              Profile.
+                            properties:
+                              iamEndpoint:
+                                type: string
+                              profile:
+                                description: the IBM Trusted Profile
+                                type: string
+                              tokenLocation:
+                                description: Location the token is mounted on the
+                                  pod
+                                type: string
+                            required:
+                            - profile
+                            type: object
                           secretRef:
                             properties:
                               secretApiKeySecretRef:
@@ -1931,8 +1949,6 @@ spec:
                                     type: string
                                 type: object
                             type: object
-                        required:
-                        - secretRef
                         type: object
                       serviceUrl:
                         description: ServiceURL is the Endpoint URL that is specific
@@ -2057,8 +2073,8 @@ spec:
                             description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider'
                             properties:
                               key:
-                                description: The key the value inside of the provider
-                                  type to use, only used with "Secret" type
+                                description: The key where the CA certificate can
+                                  be found in the Secret or ConfigMap.
                                 type: string
                               name:
                                 description: The name of the object located at the
@@ -2066,6 +2082,7 @@ spec:
                                 type: string
                               namespace:
                                 description: The namespace the Provider type is in.
+                                  Can only be defined when used in a ClusterSecretStore.
                                 type: string
                               type:
                                 description: The type of provider to use such as "Secret",
@@ -2590,15 +2607,16 @@ spec:
                           Vault server certificate.
                         properties:
                           key:
-                            description: The key the value inside of the provider
-                              type to use, only used with "Secret" type
+                            description: The key where the CA certificate can be found
+                              in the Secret or ConfigMap.
                             type: string
                           name:
                             description: The name of the object located at the provider
                               type.
                             type: string
                           namespace:
-                            description: The namespace the Provider type is in.
+                            description: The namespace the Provider type is in. Can
+                              only be defined when used in a ClusterSecretStore.
                             type: string
                           type:
                             description: The type of provider to use such as "Secret",

+ 8 - 0
config/crds/bases/kustomization.yaml

@@ -0,0 +1,8 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+resources:
+  - external-secrets.io_clusterexternalsecrets.yaml
+  - external-secrets.io_clustersecretstores.yaml
+  - external-secrets.io_externalsecrets.yaml
+  - external-secrets.io_secretstores.yaml

+ 2 - 2
deploy/charts/external-secrets/Chart.yaml

@@ -2,8 +2,8 @@ apiVersion: v2
 name: external-secrets
 description: External secret management for Kubernetes
 type: application
-version: "0.5.8"
-appVersion: "v0.5.8"
+version: "0.5.9"
+appVersion: "v0.5.9"
 kubeVersion: ">= 1.19.0-0"
 keywords:
   - kubernetes-external-secrets

+ 10 - 0
deploy/charts/external-secrets/README.md

@@ -47,6 +47,9 @@ The command removes all the Kubernetes components associated with the chart and
 | certController.image.repository | string | `"ghcr.io/external-secrets/external-secrets"` |  |
 | certController.image.tag | string | `""` |  |
 | certController.imagePullSecrets | list | `[]` |  |
+| certController.metrics.service.annotations | object | `{}` | Additional service annotations |
+| certController.metrics.service.enabled | bool | `false` | Enable if you use another monitoring tool than Prometheus to scrape the metrics |
+| certController.metrics.service.port | int | `8080` | Metrics service port to scrape |
 | certController.nameOverride | string | `""` |  |
 | certController.nodeSelector | object | `{}` |  |
 | certController.podAnnotations | object | `{}` | Annotations to add to Pod |
@@ -76,6 +79,7 @@ The command removes all the Kubernetes components associated with the chart and
 | crds.createClusterSecretStore | bool | `true` | If true, create CRDs for Cluster Secret Store. |
 | createOperator | bool | `true` | Specifies whether an external secret operator deployment be created. |
 | deploymentAnnotations | object | `{}` | Annotations to add to Deployment |
+| dnsConfig | object | `{}` | Specifies `dnsOptions` to deployment |
 | extraArgs | object | `{}` |  |
 | extraEnv | list | `[]` |  |
 | extraVolumeMounts | list | `[]` |  |
@@ -87,6 +91,9 @@ The command removes all the Kubernetes components associated with the chart and
 | imagePullSecrets | list | `[]` |  |
 | installCRDs | bool | `true` | If set, install and upgrade CRDs through helm chart. |
 | leaderElect | bool | `false` | If true, external-secrets will perform leader election between instances to ensure no more than one instance of external-secrets operates at a time. |
+| metrics.service.annotations | object | `{}` | Additional service annotations |
+| metrics.service.enabled | bool | `false` | Enable if you use another monitoring tool than Prometheus to scrape the metrics |
+| metrics.service.port | int | `8080` | Metrics service port to scrape |
 | nameOverride | string | `""` |  |
 | nodeSelector | object | `{}` |  |
 | podAnnotations | object | `{}` | Annotations to add to Pod |
@@ -130,6 +137,9 @@ The command removes all the Kubernetes components associated with the chart and
 | webhook.image.tag | string | `""` | The image tag to use. The default is the chart appVersion. |
 | webhook.imagePullSecrets | list | `[]` |  |
 | webhook.lookaheadInterval | string | `""` | Specifices the lookaheadInterval for certificate validity |
+| webhook.metrics.service.annotations | object | `{}` | Additional service annotations |
+| webhook.metrics.service.enabled | bool | `false` | Enable if you use another monitoring tool than Prometheus to scrape the metrics |
+| webhook.metrics.service.port | int | `8080` | Metrics service port to scrape |
 | webhook.nameOverride | string | `""` |  |
 | webhook.nodeSelector | object | `{}` |  |
 | webhook.podAnnotations | object | `{}` | Annotations to add to Pod |

+ 0 - 1
deploy/charts/external-secrets/README.md.gotmpl

@@ -1,4 +1,3 @@
-{{- $valuesYAML := "https://github.com/external-secrets/external-secrets/blob/master/deploy/charts/external-secrets/values.yaml" -}}
 {{- $chartRepo := "https://charts.external-secrets.io" -}}
 {{- $org := "external-secrets" -}}
 # External Secrets

+ 13 - 2
deploy/charts/external-secrets/templates/cert-controller-service.yaml

@@ -1,20 +1,31 @@
-{{- if and .Values.certController.create .Values.certController.prometheus.enabled }}
+{{- if or (and .Values.certController.create .Values.certController.prometheus.enabled) (and .Values.certController.create .Values.certController.metrics.service.enabled) }}
 apiVersion: v1
 kind: Service
 metadata:
   name: {{ include "external-secrets.fullname" . }}-cert-controller-metrics
   labels:
     {{- include "external-secrets.labels" . | nindent 4 }}
+  {{- if .Values.certController.prometheus.enabled }}
   annotations:
     prometheus.io/path: "/metrics"
     prometheus.io/scrape: "true"
     prometheus.io/port: {{ .Values.certController.prometheus.service.port | quote }}
+  {{- else }}
+  {{- with .Values.metrics.service.annotations }}
+  annotations:
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
+  {{- end }}
 spec:
   type: ClusterIP
   ports:
+  {{- if .Values.certController.prometheus.enabled }}
   - port: {{ .Values.certController.prometheus.service.port }}
+  {{- else }}
+  - port: {{ .Values.certController.metrics.service.port }}
+  {{- end }}
     protocol: TCP
     name: metrics
   selector:
     {{- include "external-secrets-cert-controller.selectorLabels" . | nindent 4 }}
-{{- end }}
+{{- end }}

+ 4 - 0
deploy/charts/external-secrets/templates/deployment.yaml

@@ -93,6 +93,10 @@ spec:
           volumeMounts:
           {{- toYaml .Values.extraVolumeMounts | nindent 12 }}
           {{- end }}
+      {{- if .Values.dnsConfig }}
+      dnsConfig:
+          {{- toYaml .Values.dnsConfig | nindent 8 }}
+      {{- end }}
       {{- if .Values.extraVolumes }}
       volumes:
       {{- toYaml .Values.extraVolumes | nindent 8 }}

+ 13 - 2
deploy/charts/external-secrets/templates/service.yaml

@@ -1,4 +1,4 @@
-{{- if .Values.prometheus.enabled }}
+{{- if or .Values.prometheus.enabled .Values.metrics.service.enabled }}
 apiVersion: v1
 kind: Service
 metadata:
@@ -6,16 +6,27 @@ metadata:
   namespace: {{ .Release.Namespace | quote }}
   labels:
     {{- include "external-secrets.labels" . | nindent 4 }}
+  {{- if .Values.prometheus.enabled }}
   annotations:
     prometheus.io/path: "/metrics"
     prometheus.io/scrape: "true"
     prometheus.io/port: {{ .Values.prometheus.service.port | quote }}
+  {{- else }}
+  {{- with .Values.metrics.service.annotations }}
+  annotations:
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
+  {{- end }}
 spec:
   type: ClusterIP
   ports:
+    {{- if .Values.prometheus.enabled }}
     - port: {{ .Values.prometheus.service.port }}
+    {{- else }}
+    - port: {{ .Values.metrics.service.port }}
+    {{- end }}
       protocol: TCP
       name: metrics
   selector:
     {{- include "external-secrets.selectorLabels" . | nindent 4 }}
-{{- end }}
+{{- end }}

+ 14 - 4
deploy/charts/external-secrets/templates/webhook-service.yaml

@@ -12,6 +12,11 @@ metadata:
     prometheus.io/path: "/metrics"
     prometheus.io/scrape: "true"
     prometheus.io/port: {{ .Values.prometheus.service.port | quote }}
+  {{- else }}
+  {{- with .Values.metrics.service.annotations }}
+  annotations:
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
   {{- end }}
 spec:
   type: ClusterIP
@@ -20,12 +25,17 @@ spec:
     targetPort: {{ .Values.webhook.port }}
     protocol: TCP
     name: webhook
-  {{- if .Values.webhook.prometheus.enabled}}
-  - port: {{ .Values.webhook.prometheus.service.port}}
-    targetPort: {{ .Values.webhook.prometheus.service.port}}
+  {{- if or .Values.webhook.prometheus.enabled .Values.webhook.metrics.service.enabled }}
+  {{- if .Values.webhook.prometheus.enabled }}
+  - port: {{ .Values.webhook.prometheus.service.port }}
+    targetPort: {{ .Values.webhook.prometheus.service.port }}
+  {{- else }}
+  - port: {{ .Values.webhook.metrics.service.port }}
+    targetPort: {{ .Values.webhook.metrics.service.port }}
+  {{- end }}
     protocol: TCP
     name: metrics
   {{- end }}
   selector:
     {{- include "external-secrets-webhook.selectorLabels" . | nindent 4 }}
-{{- end }}
+{{- end }}

+ 37 - 0
deploy/charts/external-secrets/values.yaml

@@ -121,6 +121,17 @@ serviceMonitor:
   # -- Timeout if metrics can't be retrieved in given time interval
   scrapeTimeout: 25s
 
+metrics:
+  service:
+    # -- Enable if you use another monitoring tool than Prometheus to scrape the metrics
+    enabled: false
+
+    # -- Metrics service port to scrape
+    port: 8080
+
+    # -- Additional service annotations
+    annotations: {}
+
 nodeSelector: {}
 
 tolerations: []
@@ -206,6 +217,17 @@ webhook:
     # -- Timeout if metrics can't be retrieved in given time interval
     scrapeTimeout: 25s
 
+  metrics:
+    service:
+      # -- Enable if you use another monitoring tool than Prometheus to scrape the metrics
+      enabled: false
+
+      # -- Metrics service port to scrape
+      port: 8080
+
+      # -- Additional service annotations
+      annotations: {}
+
     ## -- Extra environment variables to add to container.
   extraEnv: []
 
@@ -305,12 +327,24 @@ certController:
     # -- Timeout if metrics can't be retrieved in given time interval
     scrapeTimeout: 25s
 
+  metrics:
+    service:
+      # -- Enable if you use another monitoring tool than Prometheus to scrape the metrics
+      enabled: false
+
+      # -- Metrics service port to scrape
+      port: 8080
+
+      # -- Additional service annotations
+      annotations: {}
+
     ## -- Extra environment variables to add to container.
   extraEnv: []
 
     ## -- Map of extra arguments to pass to container.
   extraArgs: {}
 
+
     ## -- Extra volumes to pass to pod.
   extraVolumes: []
 
@@ -340,3 +374,6 @@ certController:
       # requests:
       #   cpu: 10m
       #   memory: 32Mi
+
+# -- Specifies `dnsOptions` to deployment
+dnsConfig: {}

+ 95 - 26
deploy/crds/bundle.yaml

@@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.9.0
+    controller-gen.kubebuilder.io/version: v0.9.2
   creationTimestamp: null
   name: clusterexternalsecrets.external-secrets.io
 spec:
@@ -67,7 +67,7 @@ spec:
                                 type: string
                               decodingStrategy:
                                 default: None
-                                description: Used to define a conversion Strategy
+                                description: Used to define a decoding Strategy
                                 type: string
                               key:
                                 description: Key is the key used in the Provider, mandatory
@@ -94,8 +94,6 @@ spec:
                     dataFrom:
                       description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order
                       items:
-                        maxProperties: 1
-                        minProperties: 1
                         properties:
                           extract:
                             description: Used to extract multiple key/value pairs from one secret
@@ -106,7 +104,7 @@ spec:
                                 type: string
                               decodingStrategy:
                                 default: None
-                                description: Used to define a conversion Strategy
+                                description: Used to define a decoding Strategy
                                 type: string
                               key:
                                 description: Key is the key used in the Provider, mandatory
@@ -132,7 +130,7 @@ spec:
                                 type: string
                               decodingStrategy:
                                 default: None
-                                description: Used to define a conversion Strategy
+                                description: Used to define a decoding Strategy
                                 type: string
                               name:
                                 description: Finds secrets based on the name.
@@ -150,6 +148,25 @@ spec:
                                 description: Find secrets based on tags.
                                 type: object
                             type: object
+                          rewrite:
+                            description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last)
+                            items:
+                              properties:
+                                regexp:
+                                  description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation.
+                                  properties:
+                                    source:
+                                      description: Used to define the regular expression of a re.Compiler.
+                                      type: string
+                                    target:
+                                      description: Used to define the target pattern of a ReplaceAll operation.
+                                      type: string
+                                  required:
+                                    - source
+                                    - target
+                                  type: object
+                              type: object
+                            type: array
                         type: object
                       type: array
                     refreshInterval:
@@ -169,6 +186,9 @@ spec:
                         - name
                       type: object
                     target:
+                      default:
+                        creationPolicy: Owner
+                        deletionPolicy: Retain
                       description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret.
                       properties:
                         creationPolicy:
@@ -294,6 +314,7 @@ spec:
                       description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.
                       type: object
                   type: object
+                  x-kubernetes-map-type: atomic
                 refreshTime:
                   description: The time in which the controller should reconcile it's objects and recheck namespaces for labels.
                   type: string
@@ -359,7 +380,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.9.0
+    controller-gen.kubebuilder.io/version: v0.9.2
   creationTimestamp: null
   name: clustersecretstores.external-secrets.io
 spec:
@@ -1771,7 +1792,23 @@ spec:
                       properties:
                         auth:
                           description: Auth configures how secret-manager authenticates with the IBM secrets manager.
+                          maxProperties: 1
+                          minProperties: 1
                           properties:
+                            containerAuth:
+                              description: IBM Container-based auth with IAM Trusted Profile.
+                              properties:
+                                iamEndpoint:
+                                  type: string
+                                profile:
+                                  description: the IBM Trusted Profile
+                                  type: string
+                                tokenLocation:
+                                  description: Location the token is mounted on the pod
+                                  type: string
+                              required:
+                                - profile
+                              type: object
                             secretRef:
                               properties:
                                 secretApiKeySecretRef:
@@ -1788,8 +1825,6 @@ spec:
                                       type: string
                                   type: object
                               type: object
-                          required:
-                            - secretRef
                           type: object
                         serviceUrl:
                           description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance
@@ -1880,13 +1915,13 @@ spec:
                               description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider'
                               properties:
                                 key:
-                                  description: The key the value inside of the provider type to use, only used with "Secret" type
+                                  description: The key where the CA certificate can be found in the Secret or ConfigMap.
                                   type: string
                                 name:
                                   description: The name of the object located at the provider type.
                                   type: string
                                 namespace:
-                                  description: The namespace the Provider type is in.
+                                  description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore.
                                   type: string
                                 type:
                                   description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -2252,13 +2287,13 @@ spec:
                           description: The provider for the CA bundle to use to validate Vault server certificate.
                           properties:
                             key:
-                              description: The key the value inside of the provider type to use, only used with "Secret" type
+                              description: The key where the CA certificate can be found in the Secret or ConfigMap.
                               type: string
                             name:
                               description: The name of the object located at the provider type.
                               type: string
                             namespace:
-                              description: The namespace the Provider type is in.
+                              description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore.
                               type: string
                             type:
                               description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -2526,7 +2561,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.9.0
+    controller-gen.kubebuilder.io/version: v0.9.2
   creationTimestamp: null
   name: externalsecrets.external-secrets.io
 spec:
@@ -2801,7 +2836,7 @@ spec:
                             type: string
                           decodingStrategy:
                             default: None
-                            description: Used to define a conversion Strategy
+                            description: Used to define a decoding Strategy
                             type: string
                           key:
                             description: Key is the key used in the Provider, mandatory
@@ -2828,8 +2863,6 @@ spec:
                 dataFrom:
                   description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order
                   items:
-                    maxProperties: 1
-                    minProperties: 1
                     properties:
                       extract:
                         description: Used to extract multiple key/value pairs from one secret
@@ -2840,7 +2873,7 @@ spec:
                             type: string
                           decodingStrategy:
                             default: None
-                            description: Used to define a conversion Strategy
+                            description: Used to define a decoding Strategy
                             type: string
                           key:
                             description: Key is the key used in the Provider, mandatory
@@ -2866,7 +2899,7 @@ spec:
                             type: string
                           decodingStrategy:
                             default: None
-                            description: Used to define a conversion Strategy
+                            description: Used to define a decoding Strategy
                             type: string
                           name:
                             description: Finds secrets based on the name.
@@ -2884,6 +2917,25 @@ spec:
                             description: Find secrets based on tags.
                             type: object
                         type: object
+                      rewrite:
+                        description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last)
+                        items:
+                          properties:
+                            regexp:
+                              description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation.
+                              properties:
+                                source:
+                                  description: Used to define the regular expression of a re.Compiler.
+                                  type: string
+                                target:
+                                  description: Used to define the target pattern of a ReplaceAll operation.
+                                  type: string
+                              required:
+                                - source
+                                - target
+                              type: object
+                          type: object
+                        type: array
                     type: object
                   type: array
                 refreshInterval:
@@ -2903,6 +2955,9 @@ spec:
                     - name
                   type: object
                 target:
+                  default:
+                    creationPolicy: Owner
+                    deletionPolicy: Retain
                   description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret.
                   properties:
                     creationPolicy:
@@ -3048,7 +3103,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.9.0
+    controller-gen.kubebuilder.io/version: v0.9.2
   creationTimestamp: null
   name: pushsecrets.external-secrets.io
 spec:
@@ -4608,7 +4663,23 @@ spec:
                       properties:
                         auth:
                           description: Auth configures how secret-manager authenticates with the IBM secrets manager.
+                          maxProperties: 1
+                          minProperties: 1
                           properties:
+                            containerAuth:
+                              description: IBM Container-based auth with IAM Trusted Profile.
+                              properties:
+                                iamEndpoint:
+                                  type: string
+                                profile:
+                                  description: the IBM Trusted Profile
+                                  type: string
+                                tokenLocation:
+                                  description: Location the token is mounted on the pod
+                                  type: string
+                              required:
+                                - profile
+                              type: object
                             secretRef:
                               properties:
                                 secretApiKeySecretRef:
@@ -4625,8 +4696,6 @@ spec:
                                       type: string
                                   type: object
                               type: object
-                          required:
-                            - secretRef
                           type: object
                         serviceUrl:
                           description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance
@@ -4717,13 +4786,13 @@ spec:
                               description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider'
                               properties:
                                 key:
-                                  description: The key the value inside of the provider type to use, only used with "Secret" type
+                                  description: The key where the CA certificate can be found in the Secret or ConfigMap.
                                   type: string
                                 name:
                                   description: The name of the object located at the provider type.
                                   type: string
                                 namespace:
-                                  description: The namespace the Provider type is in.
+                                  description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore.
                                   type: string
                                 type:
                                   description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -5089,13 +5158,13 @@ spec:
                           description: The provider for the CA bundle to use to validate Vault server certificate.
                           properties:
                             key:
-                              description: The key the value inside of the provider type to use, only used with "Secret" type
+                              description: The key where the CA certificate can be found in the Secret or ConfigMap.
                               type: string
                             name:
                               description: The name of the object located at the provider type.
                               type: string
                             namespace:
-                              description: The namespace the Provider type is in.
+                              description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore.
                               type: string
                             type:
                               description: The type of provider to use such as "Secret", or "ConfigMap".

+ 105 - 0
docs/guides-datafrom-rewrite.md

@@ -0,0 +1,105 @@
+# Rewriting Keys in DataFrom
+
+When calling out an ExternalSecret with `dataFrom.extract` or `dataFrom.find`, it is possible that you end up with a kubernetes secret that has conflicts in the key names, or that you simply want to remove a common path from the secret keys.
+
+In order to do so, it is possible to define a set of rewrite operations using `dataFrom.rewrite`. These operations can be stacked, hence allowing complex manipulations of the secret keys.
+
+Rewrite operations are all applied before `ConversionStrategy` is applied.
+
+## Methods
+
+### Regexp
+This method implements rewriting through the use of regular expressions. It needs a `source` and a `target` field. The source field is where the definition of the matching regular expression goes, where the `target` field is where the replacing expression goes.
+
+Some considerations about the impelementation of Regexp Rewrite:
+
+1. The input of a subsequent rewrite operation are the outputs of the previous rewrite.
+2. If a given set of keys do not match any Rewrite operation, there will be no error. Rather, the original keys will be used.
+3. If a `source` is not a compilable `regexp` expression, an error will be produced and the external secret goes into a error state.
+
+## Examples
+### Removing a common path from find operations
+The following ExternalSecret:
+```yaml
+{% include 'datafrom-rewrite-remove-path.yaml' %}
+```
+Will get all the secrets matching `path/to/my/secrets/*` and then rewrite them by removing the common path away.
+
+In this example, if we had the following secrets available in the provider:
+```
+path/to/my/secrets/username
+path/to/my/secrets/password
+```
+the output kubernetes secret would be:
+```yaml
+apiVersion: v1
+kind: Secret
+type: Opaque
+data:
+    username: ...
+    password: ...
+```
+### Avoiding key collisions
+The following ExternalSecret:
+```yaml
+{% include 'datafrom-rewrite-conflict.yaml' %}
+
+```
+Will allow two secrets with the same JSON keys to be imported into a Kubernetes Secret without any conflict.
+In this example, if we had the following secrets available in the provider:
+```json
+{
+    "my-secrets-dev": {
+        "password": "bar",
+     },
+    "my-secrets-prod": {
+        "password": "safebar",
+     }
+}
+```
+the output kubernetes secret would be:
+```yaml
+apiVersion: v1
+kind: Secret
+type: Opaque
+data:
+    dev_password: YmFy #bar
+    prod_password: c2FmZWJhcg== #safebar
+```
+
+### Remove invalid characters
+The following ExternalSecret:
+```yaml
+{% include 'datafrom-rewrite-invalid-characters.yaml' %}
+
+```
+Will remove invalid characters from the secret key.
+In this example, if we had the following secrets available in the provider:
+```json
+{
+    "development": {
+        "foo/bar": "1111",
+        "foo$baz": "2222"
+    }
+}
+```
+the output kubernetes secret would be:
+```yaml
+apiVersion: v1
+kind: Secret
+type: Opaque
+data:
+    foo_bar: MTExMQ== #1111
+    foo_baz: MjIyMg== #2222
+```
+
+## Limitations
+
+Regexp Rewrite is based on golang `regexp`, which in turns implements `RE2` regexp language. There a a series of known limitations to this implementation, such as:
+
+* Lack of ability to do lookaheads or lookbehinds;
+* Lack of negation expressions;
+* Lack of support to conditionl branches;
+* Lack of support to possessive repetitions.
+
+A list of compatibility and known limitations considering other commonly used regexp frameworks (such as PCRE and PERL) are listed [here](https://github.com/google/re2/wiki/Syntax).

+ 7 - 2
docs/guides-getallsecrets.md

@@ -3,7 +3,7 @@
 In some use cases, it might be impractical to bundle all sensitive information into a single secret, or even it is not possible to fully know a given secret name. In such cases, it is possible that an user might need to sync multiple secrets from an external provider into a single Kubernetes Secret. This is possible to be done in external-secrets with the `dataFrom.find` option.
 
 !!! note
-    The secret's contents as defined in the provider are going to be stored in the kubernetes secret as a single key. Currently, it is not possible to apply any decoding Strategy during a find operation.
+    The secret's contents as defined in the provider are going to be stored in the kubernetes secret as a single key. Currently, it possible to apply a decoding Strategy during a find operation, but only at the secret level (e.g. if a secret is a JSON with some B64 encoded data within, `decodingStrategy: Auto` would not decode it)
 
 
 ### Fetching secrets matching a given name pattern
@@ -33,7 +33,12 @@ Some providers support filtering out a find operation only to a given path, inst
 ### Avoiding name conflicts
 By default, kubernetes Secrets accepts only a given range of characters. `Find` operations will automatically replace any not allowed character with a `_`. So if we have a given secret `a_c` and `a/c` would lead to a naming conflict. 
 
-It is not entirely possible to avoid this behavior, but setting `dataFrom.find.conversionStrategy: Unicode` reduces the collision probability. When using `Unicode`, any invalid character will be replaced by its unicode, in the form of `_UXXXX_`. In this case, the available kubernetes keys would be `a_c` and `a_U2215_c`, hence avoiding most of possible conflicts.
+
+If you happen to have a case where a conflict is happening, you can use the `rewrite` block to apply a regexp on one of the find operations (for more information please refer to [Rewriting Keys from DataFrom](guides-datafrom-rewrite.md)).
+
+You can also set  `dataFrom.find.conversionStrategy: Unicode` to reduce the collistion probability. When using `Unicode`, any invalid character will be replaced by its unicode, in the form of `_UXXXX_`. In this case, the available kubernetes keys would be `a_c` and `a_U2215_c`, hence avoiding most of possible conflicts.
+
+
 
 !!! note "PRs welcome"
     Some providers might not have the implementation needed for fetching multiple secrets. If that's your case, please feel free to contribute!

+ 2 - 0
docs/guides-metrics.md

@@ -2,6 +2,8 @@
 
 The External Secrets Operator exposes its Prometheus metrics in the `/metrics` path. To enable it, set the `serviceMonitor.enabled` Helm flag to `true`. In addition you can also set `webhook.serviceMonitor.enabled=true` and `certController.serviceMonitor.enabled=true` to create `ServiceMonitor` resources for the other components.
 
+If you are using a different monitoring tool that also needs a `/metrics` endpoint, you can set the `metrics.service.enabled` Helm flag to `true`. In addition you can also set `webhook.metrics.service.enabled` and `certController.metrics.service.enabled` to scrape the other components.
+
 The Operator has the metrics inherited from Kubebuilder plus some custom metrics with the `externalsecret` prefix.
 
 ## External Secret Metrics

TEMPAT SAMPAH
docs/pictures/screenshot_container_auth_create_1.png


TEMPAT SAMPAH
docs/pictures/screenshot_container_auth_create_2.png


TEMPAT SAMPAH
docs/pictures/screenshot_container_auth_create_3.png


TEMPAT SAMPAH
docs/pictures/screenshot_container_auth_create_button.png


TEMPAT SAMPAH
docs/pictures/screenshot_container_auth_create_group.png


TEMPAT SAMPAH
docs/pictures/screenshot_container_auth_create_group_1.png


TEMPAT SAMPAH
docs/pictures/screenshot_container_auth_create_group_2.png


TEMPAT SAMPAH
docs/pictures/screenshot_container_auth_create_group_3.png


TEMPAT SAMPAH
docs/pictures/screenshot_container_auth_create_group_4.png


TEMPAT SAMPAH
docs/pictures/screenshot_container_auth_iam_left.png


+ 1 - 1
docs/provider-1password-automation.md

@@ -32,7 +32,7 @@ _**The 1Password API calls the entries in vaults 'Items'. These docs use the sam
     * External Secrets does not run this server. See [Deploy a Connect Server](#deploy-a-connect-server).
     * One Connect Server is needed per 1Password Automation Environment.
     * Many Vaults can be added to an Automation Environment, and Tokens can be generated in that Environment with access to any set or subset of those Vaults.
-* 1Password Connect Server version 1.3.0 or higher. 1.3.0 and 1.5.0 have been tested.
+* 1Password Connect Server version 1.5.6 or higher.
 
 ### Setup Authentication
 _Authentication requires a `1password-credentials.json` file provided to the Connect Server, and a related 'Access Token' for the client in this provider to authenticate to that Connect Server. Both of these are generated by 1Password._

+ 21 - 7
docs/provider-azure-key-vault.md

@@ -11,6 +11,13 @@ We support Service Principals, Managed Identity and Workload Identity authentica
 
 To use Managed Identity authentication, you should use [aad-pod-identity](https://azure.github.io/aad-pod-identity/docs/) to assign the identity to external-secrets operator. To add the selector to external-secrets operator, use `podLabels` in your values.yaml in case of Helm installation of external-secrets.
 
+Minimum required permissions are `Get` over secret and certificate permissions. This can be done by adding a Key Vault access policy:
+
+```sh
+KUBELET_IDENTITY_OBJECT_ID=$(az aks show --resource-group <AKS_CLUSTER_RG_NAME> --name <AKS_CLUSTER_NAME> --query 'identityProfile.kubeletidentity.objectId' -o tsv)
+az keyvault set-policy --name kv-name-with-certs --object-id "$KUBELET_IDENTITY_OBJECT_ID" --certificate-permissions get --secret-permissions get
+```
+
 #### Service Principal key authentication
 
 A service Principal client and Secret is created and the JSON keyfile is stored in a `Kind=Secret`. The `ClientID` and `ClientSecret` should be configured for the secret. This service principal should have proper access rights to the keyvault to be managed by the operator
@@ -89,23 +96,30 @@ Azure KeyVault manages different [object types](https://docs.microsoft.com/en-us
 | `key`         | A JWK which contains the public key. Azure KeyVault does **not** export the private key. You may want to use [template functions](guides-templating.md) to transform this JWK into PEM encoded PKIX ASN.1 DER format. |
 | `certificate` | The raw CER contents of the x509 certificate. You may want to use [template functions](guides-templating.md) to transform this into your desired encoding                                                             |
 
-
 ### Creating external secret
 
 To create a kubernetes secret from the Azure Key vault secret a `Kind=ExternalSecret` is needed.
 
-You can manage keys/secrets/certificates saved inside the keyvault , by setting a "/" prefixed type in the secret name , the default type is a `secret`. other supported values are `cert` and `key`
-
-to select all secrets inside the key vault or all tags inside a secret, you can use the `dataFrom` directive
+You can manage keys/secrets/certificates saved inside the keyvault , by setting a "/" prefixed type in the secret name, the default type is a `secret`. Other supported values are `cert` and `key`.
 
 ```yaml
 {% include 'azkv-external-secret.yaml' %}
 ```
+
+The operator will fetch the Azure Key vault secret and inject it as a `Kind=Secret`. Then the Kubernetes secret can be fetched by issuing:
+
+```sh
+kubectl get secret secret-to-be-created -n <namespace> | -o jsonpath='{.data.dev-secret-test}' | base64 -d
+```
+
+To select all secrets inside the key vault or all tags inside a secret, you can use the `dataFrom` directive:
+
 ```yaml
 {% include 'azkv-datafrom-external-secret.yaml' %}
 ```
 
-The operator will fetch the Azure Key vault secret and inject it as a `Kind=Secret`
+To get a PKCS#12 certificate from Azure Key Vault and inject it as a `Kind=Secret` of type `kubernetes.io/tls`:
+
+```yaml
+{% include 'azkv-pkcs12-cert-external-secret.yaml' %}
 ```
-kubectl get secret secret-to-be-created -n <namespace> | -o jsonpath='{.data.dev-secret-test}' | base64 -d
-```

+ 66 - 7
docs/provider-ibm-secrets-manager.md

@@ -4,7 +4,11 @@ External Secrets Operator integrates with [IBM Secret Manager](https://www.ibm.c
 
 ### Authentication
 
-At the moment, we only support API key authentication for this provider. To generate your key (for test purposes we are going to generate from your user), first got to your (Access IAM) page:
+We support API key and trusted profile container authentication for this provider.
+
+#### API key secret
+
+To generate your key (for test purposes we are going to generate from your user), first got to your (Access IAM) page:
 
 ![iam](./pictures/screenshot_api_keys_iam.png)
 
@@ -24,16 +28,68 @@ You have created a key. Press the eyeball to show the key. Copy or save it becau
 
 ![iam-create-success](./pictures/screenshot_api_keys_create_successful.png)
 
-
-
-#### API key secret
-
 Create a secret containing your apiKey:
 
 ```shell
 kubectl create secret generic ibm-secret --from-literal=apiKey='API_KEY_VALUE'
 ```
 
+#### Trusted Profile Container Auth
+
+To create the trusted profile, first got to your (Access IAM) page:
+
+![iam](./pictures/screenshot_api_keys_iam.png)
+
+On the left, click "Access groups":
+
+![iam-left](./pictures/screenshot_container_auth_create_group.png)
+
+Pick a name and description for your group:
+
+![iam-left](./pictures/screenshot_container_auth_create_group_1.png)
+
+Click on "Access Policies":
+
+![iam-left](./pictures/screenshot_container_auth_create_group_2.png)
+
+Click on "Assign Access", select "IAM services", and pick "Secrets Manager" from the pick-list:
+
+![iam-left](./pictures/screenshot_container_auth_create_group_3.png)
+
+Scope to "All resources" or "Resources based on selected attributes", select "SecretsReader":
+
+![iam-left](./pictures/screenshot_container_auth_create_group_4.png)
+
+Click "Add" and "Assign" to save the access group.
+
+Next, on the left, click "Trusted profiles":
+
+![iam-left](./pictures/screenshot_container_auth_iam_left.png)
+
+Press "Create":
+
+![iam-create-button](./pictures/screenshot_container_auth_create_button.png)
+
+Pick a name and description for your profile:
+
+![iam-create-key](./pictures/screenshot_container_auth_create_1.png)
+
+Scope the profile's access.
+
+The compute service type will be "Red Hat OpenShift on IBM Cloud".  Additional restriction can be configured based on cloud or cluster metadata, or if "Specific resources" is selected, restriction to a specific cluster.
+
+![iam-create-key](./pictures/screenshot_container_auth_create_2.png)
+
+Click "Add" next to the previously created access group and then "Create", to associate the necessary service permissions.
+
+![iam-create-key](./pictures/screenshot_container_auth_create_3.png)
+
+To use the container-based authentication, it is necessary to map the API server `serviceAccountToken` auth token to the "external-secrets" and "external-secrets-webhook" deployment descriptors. Example below:
+
+```yaml
+{% include 'ibm-container-auth-volume.yaml' %}
+```
+
 ### Update secret store
 Be sure the `ibm` provider is listed in the `Kind=SecretStore`
 
@@ -42,6 +98,9 @@ Be sure the `ibm` provider is listed in the `Kind=SecretStore`
 ```
 **NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in `secretApiKeySecretRef` with the namespace where the secret resides.
 
+**NOTE:** Only `secretApiKeySecretRef` or `containerAuth` should be specified, depending on authentication me
+thod being used.
+
 To find your serviceURL, under your Secrets Manager resource, go to "Endpoints" on the left.
 Note: Use the url without the `/api` suffix that is presented in the UI.
 See here for a list of [publicly available endpoints](https://cloud.ibm.com/apidocs/secrets-manager#getting-started-endpoints).
@@ -51,13 +110,13 @@ See here for a list of [publicly available endpoints](https://cloud.ibm.com/apid
 ### Secret Types
 We support the following secret types of [IBM Secrets Manager](https://cloud.ibm.com/apidocs/secrets-manager):
 
-* `arbitrary`
+* `arbitrary` 
 * `username_password`
 * `iam_credentials`
 * `imported_cert`
 * `public_cert`
 * `private_cert`
-* `kv`
+* `kv` 
 
 To define the type of secret you would like to sync you need to prefix the secret id with the desired type. If the secret type is not specified it is defaulted to `arbitrary`:
 

+ 24 - 0
docs/snippets/azkv-pkcs12-cert-external-secret.yaml

@@ -0,0 +1,24 @@
+{% raw %}
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+  name: mycert
+spec:
+  refreshInterval: 24h
+  secretStoreRef:
+    kind: ClusterSecretStore
+    name: kv-mycert
+  target:
+    template:
+      type: kubernetes.io/tls
+      engineVersion: v2
+      data:
+        tls.crt: "{{ .mycert | b64dec | pkcs12cert }}"
+        tls.key: "{{ .mycert | b64dec | pkcs12key }}"
+  data:
+  - secretKey: mycert
+    remoteRef:
+      # Azure Key Vault certificates must be fetched as secret/cert-name
+      key: secret/mycert
+
+{% endraw %}

+ 24 - 0
docs/snippets/datafrom-rewrite-conflict.yaml

@@ -0,0 +1,24 @@
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+  name: example
+spec:
+  refreshInterval: 1h
+  secretStoreRef:
+    kind: SecretStore
+    name: backend
+  target:
+    name: secret-to-be-created
+  dataFrom:
+  - extract:
+      key: my-secrets-dev
+    rewrite:
+    - regexp:
+        source: "(.*)"
+        target: "dev-$1"      
+  - extract:
+      key: my-secrets-prod
+    rewrite:
+    - regexp:
+        source: "(.*)"
+        target: "prod-$1"

+ 18 - 0
docs/snippets/datafrom-rewrite-invalid-characters.yaml

@@ -0,0 +1,18 @@
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+  name: example
+spec:
+  refreshInterval: 1h
+  secretStoreRef:
+    kind: SecretStore
+    name: backend
+  target:
+    name: secret-to-be-created
+  dataFrom:
+  - extract:
+      key: development
+    rewrite:
+    - regexp:
+        source: "[^a-zA-Z0-9 -]"
+        target: "_"

+ 20 - 0
docs/snippets/datafrom-rewrite-remove-path.yaml

@@ -0,0 +1,20 @@
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+  name: example
+spec:
+  refreshInterval: 1h
+  secretStoreRef:
+    kind: SecretStore
+    name: backend
+  target:
+    name: secret-to-be-created
+  dataFrom:
+  - find:
+      path: path/to/my
+      name: 
+        regexp: secrets
+    rewrite:
+    - regexp:
+        source: "path/to/my/secrets/(.*)"
+        target: "$1"

+ 14 - 0
docs/snippets/full-external-secret.yaml

@@ -85,14 +85,28 @@ spec:
       property: provider-key-property
       conversionStrategy: Default
       decodingStrategy: Auto
+    rewrite:
+    - regexp:
+        source: "foo"
+        target: "bar"
+    - regexp:
+        source: "exp-(.*?)-ression"
+        target: "rewriting-$1-with-groups"
   - find:
       path: path-to-filter
+          source: "exp-(.*?)-ression"
+          target: "rewriting-$1-with-groups"
       name:
         regexp: ".*foobar.*"
       tags:
         foo: bar
       conversionStrategy: Unicode
       decodingStrategy: Base64
+    rewrite:
+    - regexp:
+        source: "foo"
+        target: "bar"
+    - regexp:
 
 status:
   # refreshTime is the time and date the external secret was fetched and

+ 22 - 0
docs/snippets/ibm-container-auth-volume.yaml

@@ -0,0 +1,22 @@
+...
+spec:
+  ...
+  template:
+    ...
+    spec:
+      containers:
+        ...
+        volumeMounts:
+        - mountPath: /var/run/secrets/tokens
+           name: sa-token
+      ...
+      volumes:
+      - name: sa-token
+        projected:
+          defaultMode: 420
+          sources:
+          - serviceAccountToken:
+              audience: iam
+              expirationSeconds: 3600
+              path: sa-token
+...

+ 4 - 0
docs/snippets/ibm-secret-store.yaml

@@ -7,6 +7,10 @@ spec:
     ibm:
       serviceUrl: "https://SECRETS_MANAGER_ID.REGION.secrets-manager.appdomain.cloud"
       auth:
+        containerAuth:
+          profile: "test container auth profile"
+          tokenLocation: "/var/run/secrets/tokens/sa-token"
+          iamEndpoint: "https://iam.cloud.ibm.com"
         secretRef:
           secretApiKeySecretRef:
             name: ibm-secret

+ 216 - 11
docs/spec.md

@@ -494,7 +494,7 @@ is ServicePrincipal.</p>
 </tr>
 </thead>
 <tbody><tr><td><p>&#34;ManagedIdentity&#34;</p></td>
-<td><p>Using Managed Identity to authenticate. Used with aad-pod-identity installed in the clister.</p>
+<td><p>Using Managed Identity to authenticate. Used with aad-pod-identity installed in the cluster.</p>
 </td>
 </tr><tr><td><p>&#34;ServicePrincipal&#34;</p></td>
 <td><p>Using service principal to authenticate, which needs a tenantId, a clientId and a clientSecret.</p>
@@ -649,11 +649,13 @@ string
 </h3>
 <p>
 (<em>Appears on:</em>
-<a href="#external-secrets.io/v1beta1.KubernetesServer">KubernetesServer</a>, 
+<a href="#external-secrets.io/v1beta1.KubernetesServer">KubernetesServer</a>,
 <a href="#external-secrets.io/v1beta1.VaultProvider">VaultProvider</a>)
 </p>
 <p>
-<p>Defines a location to fetch the cert for the vault provider from.</p>
+<p>Used to provide custom certificate authority (CA) certificates
+for a secret store. The CAProvider points to a Secret or ConfigMap resource
+that contains a PEM-encoded certificate.</p>
 </p>
 <table>
 <thead>
@@ -695,7 +697,7 @@ string
 </em>
 </td>
 <td>
-<p>The key the value inside of the provider type to use, only used with &ldquo;Secret&rdquo; type</p>
+<p>The key where the CA certificate can be found in the Secret or ConfigMap.</p>
 </td>
 </tr>
 <tr>
@@ -707,7 +709,8 @@ string
 </td>
 <td>
 <em>(Optional)</em>
-<p>The namespace the Provider type is in.</p>
+<p>The namespace the Provider type is in.
+Can only be defined when used in a ClusterSecretStore.</p>
 </td>
 </tr>
 </tbody>
@@ -1385,7 +1388,7 @@ ExternalSecretStatus
 (<code>string</code> alias)</p></h3>
 <p>
 (<em>Appears on:</em>
-<a href="#external-secrets.io/v1beta1.ExternalSecretDataRemoteRef">ExternalSecretDataRemoteRef</a>, 
+<a href="#external-secrets.io/v1beta1.ExternalSecretDataRemoteRef">ExternalSecretDataRemoteRef</a>,
 <a href="#external-secrets.io/v1beta1.ExternalSecretFind">ExternalSecretFind</a>)
 </p>
 <p>
@@ -1519,13 +1522,27 @@ ExternalSecretFind
 <p>Used to find secrets based on tags or regular expressions</p>
 </td>
 </tr>
+<tr>
+<td>
+<code>rewrite</code></br>
+<em>
+<a href="#external-secrets.io/v1beta1.ExternalSecretRewrite">
+[]ExternalSecretRewrite
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Used to rewrite secret Keys after getting them from the secret Provider</p>
+</td>
+</tr>
 </tbody>
 </table>
 <h3 id="external-secrets.io/v1beta1.ExternalSecretDataRemoteRef">ExternalSecretDataRemoteRef
 </h3>
 <p>
 (<em>Appears on:</em>
-<a href="#external-secrets.io/v1beta1.ExternalSecretData">ExternalSecretData</a>, 
+<a href="#external-secrets.io/v1beta1.ExternalSecretData">ExternalSecretData</a>,
 <a href="#external-secrets.io/v1beta1.ExternalSecretDataFromRemoteRef">ExternalSecretDataFromRemoteRef</a>)
 </p>
 <p>
@@ -1602,8 +1619,48 @@ ExternalSecretConversionStrategy
 <p>Used to define a conversion Strategy</p>
 </td>
 </tr>
+<tr>
+<td>
+<code>decodingStrategy</code></br>
+<em>
+<a href="#external-secrets.io/v1beta1.ExternalSecretDecodingStrategy">
+ExternalSecretDecodingStrategy
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Used to define a conversion Strategy</p>
+</td>
+</tr>
 </tbody>
 </table>
+<h3 id="external-secrets.io/v1beta1.ExternalSecretDecodingStrategy">ExternalSecretDecodingStrategy
+(<code>string</code> alias)</p></h3>
+<p>
+(<em>Appears on:</em>
+<a href="#external-secrets.io/v1beta1.ExternalSecretDataRemoteRef">ExternalSecretDataRemoteRef</a>,
+<a href="#external-secrets.io/v1beta1.ExternalSecretFind">ExternalSecretFind</a>)
+</p>
+<p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Value</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody><tr><td><p>&#34;Auto&#34;</p></td>
+<td></td>
+</tr><tr><td><p>&#34;Base64&#34;</p></td>
+<td></td>
+</tr><tr><td><p>&#34;Base64URL&#34;</p></td>
+<td></td>
+</tr><tr><td><p>&#34;None&#34;</p></td>
+<td></td>
+</tr></tbody>
+</table>
 <h3 id="external-secrets.io/v1beta1.ExternalSecretDeletionPolicy">ExternalSecretDeletionPolicy
 (<code>string</code> alias)</p></h3>
 <p>
@@ -1707,6 +1764,20 @@ ExternalSecretConversionStrategy
 <p>Used to define a conversion Strategy</p>
 </td>
 </tr>
+<tr>
+<td>
+<code>decodingStrategy</code></br>
+<em>
+<a href="#external-secrets.io/v1beta1.ExternalSecretDecodingStrategy">
+ExternalSecretDecodingStrategy
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Used to define a conversion Strategy</p>
+</td>
+</tr>
 </tbody>
 </table>
 <h3 id="external-secrets.io/v1beta1.ExternalSecretMetadataPolicy">ExternalSecretMetadataPolicy
@@ -1730,11 +1801,83 @@ ExternalSecretConversionStrategy
 <td></td>
 </tr></tbody>
 </table>
+<h3 id="external-secrets.io/v1beta1.ExternalSecretRewrite">ExternalSecretRewrite
+</h3>
+<p>
+(<em>Appears on:</em>
+<a href="#external-secrets.io/v1beta1.ExternalSecretDataFromRemoteRef">ExternalSecretDataFromRemoteRef</a>)
+</p>
+<p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>regexp</code></br>
+<em>
+<a href="#external-secrets.io/v1beta1.ExternalSecretRewriteRegexp">
+ExternalSecretRewriteRegexp
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Rewrite using regular expressions</p>
+</td>
+</tr>
+</tbody>
+</table>
+<h3 id="external-secrets.io/v1beta1.ExternalSecretRewriteRegexp">ExternalSecretRewriteRegexp
+</h3>
+<p>
+(<em>Appears on:</em>
+<a href="#external-secrets.io/v1beta1.ExternalSecretRewrite">ExternalSecretRewrite</a>)
+</p>
+<p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>source</code></br>
+<em>
+string
+</em>
+</td>
+<td>
+<p>Regular expression to use as a re.Compiler.</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>target</code></br>
+<em>
+string
+</em>
+</td>
+<td>
+<p>Target output for a replace operation.</p>
+</td>
+</tr>
+</tbody>
+</table>
 <h3 id="external-secrets.io/v1beta1.ExternalSecretSpec">ExternalSecretSpec
 </h3>
 <p>
 (<em>Appears on:</em>
-<a href="#external-secrets.io/v1beta1.ClusterExternalSecretSpec">ClusterExternalSecretSpec</a>, 
+<a href="#external-secrets.io/v1beta1.ClusterExternalSecretSpec">ClusterExternalSecretSpec</a>,
 <a href="#external-secrets.io/v1beta1.ExternalSecret">ExternalSecret</a>)
 </p>
 <p>
@@ -2615,6 +2758,69 @@ IBMAuthSecretRef
 <td>
 </td>
 </tr>
+<tr>
+<td>
+<code>containerAuth</code></br>
+<em>
+<a href="#external-secrets.io/v1beta1.IBMAuthContainerAuth">
+IBMAuthContainerAuth
+</a>
+</em>
+</td>
+<td>
+</td>
+</tr>
+</tbody>
+</table>
+<h3 id="external-secrets.io/v1beta1.IBMAuthContainerAuth">IBMAuthContainerAuth
+</h3>
+<p>
+(<em>Appears on:</em>
+<a href="#external-secrets.io/v1beta1.IBMAuth">IBMAuth</a>)
+</p>
+<p>
+<p>IBM Container-based auth with IAM Trusted Profile.</p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>profile</code></br>
+<em>
+string
+</em>
+</td>
+<td>
+<p>the IBM Trusted Profile</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>tokenLocation</code></br>
+<em>
+string
+</em>
+</td>
+<td>
+<p>Location the token is mounted on the pod</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>iamEndpoint</code></br>
+<em>
+string
+</em>
+</td>
+<td>
+</td>
+</tr>
 </tbody>
 </table>
 <h3 id="external-secrets.io/v1beta1.IBMAuthSecretRef">IBMAuthSecretRef
@@ -2641,7 +2847,6 @@ github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
 </em>
 </td>
 <td>
-<em>(Optional)</em>
 <p>The SecretAccessKey is used for authentication</p>
 </td>
 </tr>
@@ -3594,7 +3799,7 @@ string
 </h3>
 <p>
 (<em>Appears on:</em>
-<a href="#external-secrets.io/v1beta1.ClusterSecretStore">ClusterSecretStore</a>, 
+<a href="#external-secrets.io/v1beta1.ClusterSecretStore">ClusterSecretStore</a>,
 <a href="#external-secrets.io/v1beta1.SecretStore">SecretStore</a>)
 </p>
 <p>
@@ -3666,7 +3871,7 @@ int
 </h3>
 <p>
 (<em>Appears on:</em>
-<a href="#external-secrets.io/v1beta1.ClusterSecretStore">ClusterSecretStore</a>, 
+<a href="#external-secrets.io/v1beta1.ClusterSecretStore">ClusterSecretStore</a>,
 <a href="#external-secrets.io/v1beta1.SecretStore">SecretStore</a>)
 </p>
 <p>

+ 0 - 1
e2e/run.sh

@@ -46,7 +46,6 @@ until kubectl get secret | grep -q -e ^external-secrets-e2e-token; do \
 done
 
 echo -e "Starting the e2e test pod ${E2E_IMAGE_REGISTRY}:${VERSION}"
-
 kubectl run --rm \
   --attach \
   --restart=Never \

+ 1 - 0
e2e/suites/provider/cases/akeyless/akeyless.go

@@ -31,6 +31,7 @@ var _ = Describe("[akeyless]", Label("akeyless"), func() {
 		Entry(common.SimpleDataSync(f)),
 		Entry(common.NestedJSONWithGJSON(f)),
 		Entry(common.JSONDataFromSync(f)),
+		Entry(common.JSONDataFromRewrite(f)),
 		Entry(common.JSONDataWithProperty(f)),
 		Entry(common.JSONDataWithTemplate(f)),
 		Entry(common.DockerJSONConfig(f)),

+ 1 - 0
e2e/suites/provider/cases/alibaba/alibaba.go

@@ -31,6 +31,7 @@ var _ = Describe("[alibaba]", Label("alibaba"), func() {
 		Entry(common.SimpleDataSync(f)),
 		Entry(common.NestedJSONWithGJSON(f)),
 		Entry(common.JSONDataFromSync(f)),
+		Entry(common.JSONDataFromRewrite(f)),
 		Entry(common.JSONDataWithProperty(f)),
 		Entry(common.JSONDataWithTemplate(f)),
 		Entry(common.DockerJSONConfig(f)),

+ 1 - 0
e2e/suites/provider/cases/aws/parameterstore/parameterstore.go

@@ -33,6 +33,7 @@ var _ = Describe("[aws] ", Label("aws", "parameterstore"), func() {
 		Entry(common.SimpleDataSync(f)),
 		Entry(common.NestedJSONWithGJSON(f)),
 		Entry(common.JSONDataFromSync(f)),
+		Entry(common.JSONDataFromRewrite(f)),
 		Entry(common.JSONDataWithProperty(f)),
 		Entry(common.JSONDataWithTemplate(f)),
 		Entry(common.DockerJSONConfig(f)),

+ 1 - 0
e2e/suites/provider/cases/aws/secretsmanager/secretsmanager.go

@@ -33,6 +33,7 @@ var _ = Describe("[aws] ", Label("aws", "secretsmanager"), func() {
 		Entry(common.SimpleDataSync(f)),
 		Entry(common.NestedJSONWithGJSON(f)),
 		Entry(common.JSONDataFromSync(f)),
+		Entry(common.JSONDataFromRewrite(f)),
 		Entry(common.JSONDataWithProperty(f)),
 		Entry(common.JSONDataWithTemplate(f)),
 		Entry(common.DockerJSONConfig(f)),

+ 1 - 0
e2e/suites/provider/cases/azure/azure_secret.go

@@ -30,6 +30,7 @@ var _ = Describe("[azure]", Label("azure", "keyvault", "secret"), func() {
 		Entry(common.SimpleDataSync(f)),
 		Entry(common.NestedJSONWithGJSON(f)),
 		Entry(common.JSONDataFromSync(f)),
+		Entry(common.JSONDataFromRewrite(f)),
 		Entry(common.JSONDataWithProperty(f)),
 		Entry(common.JSONDataWithTemplate(f)),
 		Entry(common.DockerJSONConfig(f)),

+ 37 - 0
e2e/suites/provider/cases/common/common.go

@@ -291,6 +291,43 @@ func JSONDataFromSync(f *framework.Framework) (string, func(*framework.TestCase)
 	}
 }
 
+// This case creates one secret with json values and syncs them using a single .Spec.DataFrom block.
+func JSONDataFromRewrite(f *framework.Framework) (string, func(*framework.TestCase)) {
+	return "[common] should sync and rewrite secrets with dataFrom", func(tc *framework.TestCase) {
+		secretKey1 := fmt.Sprintf("%s-%s", f.Namespace.Name, "one")
+		targetSecretKey1 := "username"
+		targetSecretValue1 := "myuser.name"
+		targetSecretKey2 := "address"
+		targetSecretValue2 := "happy street"
+		secretValue := fmt.Sprintf("{ %q: %q, %q: %q }", targetSecretKey1, targetSecretValue1, targetSecretKey2, targetSecretValue2)
+		tc.Secrets = map[string]framework.SecretEntry{
+			secretKey1: {Value: secretValue},
+		}
+		tc.ExpectedSecret = &v1.Secret{
+			Type: v1.SecretTypeOpaque,
+			Data: map[string][]byte{
+				"my_username": []byte(targetSecretValue1),
+				"my_address":  []byte(targetSecretValue2),
+			},
+		}
+		tc.ExternalSecret.Spec.DataFrom = []esv1beta1.ExternalSecretDataFromRemoteRef{
+			{
+				Extract: &esv1beta1.ExternalSecretDataRemoteRef{
+					Key: secretKey1,
+				},
+				Rewrite: []esv1beta1.ExternalSecretRewrite{
+					{
+						Regexp: &esv1beta1.ExternalSecretRewriteRegexp{
+							Source: "(.*)",
+							Target: "my_$1",
+						},
+					},
+				},
+			},
+		}
+	}
+}
+
 // This case creates a secret with a nested json value. It is synced into two secrets.
 // The values from the nested data are extracted using gjson.
 // not supported by: vault.

+ 50 - 2
e2e/suites/provider/cases/common/find_by_name.go

@@ -21,6 +21,10 @@ import (
 	"github.com/external-secrets/external-secrets/e2e/framework"
 )
 
+const (
+	findValue = "{\"foo1\":\"foo1-val\"}"
+)
+
 // This case creates multiple secrets with simple key/value pairs and syncs them using multiple .Spec.Data blocks.
 func FindByName(f *framework.Framework) (string, func(*framework.TestCase)) {
 	return "[common] should find secrets by name using .DataFrom[]", func(tc *framework.TestCase) {
@@ -28,7 +32,7 @@ func FindByName(f *framework.Framework) (string, func(*framework.TestCase)) {
 		secretKeyOne := fmt.Sprintf(namePrefix, f.Namespace.Name, "one")
 		secretKeyTwo := fmt.Sprintf(namePrefix, f.Namespace.Name, "two")
 		secretKeyThree := fmt.Sprintf(namePrefix, f.Namespace.Name, "three")
-		secretValue := "{\"foo1\":\"foo1-val\"}"
+		secretValue := findValue
 		tc.Secrets = map[string]framework.SecretEntry{
 			secretKeyOne:   {Value: secretValue},
 			secretKeyTwo:   {Value: secretValue},
@@ -54,12 +58,56 @@ func FindByName(f *framework.Framework) (string, func(*framework.TestCase)) {
 	}
 }
 
+// This case creates multiple secrets with simple key/value pairs and syncs them using multiple .Spec.Data blocks.
+func FindByNameAndRewrite(f *framework.Framework) (string, func(*framework.TestCase)) {
+	return "[common] should find and rewrite secrets by name using .DataFrom[]", func(tc *framework.TestCase) {
+		const namePrefix = "e2e_find_and_rewrite_%s_%s"
+		secretKeyOne := fmt.Sprintf(namePrefix, f.Namespace.Name, "one")
+		secretKeyTwo := fmt.Sprintf(namePrefix, f.Namespace.Name, "two")
+		secretKeyThree := fmt.Sprintf(namePrefix, f.Namespace.Name, "three")
+		expectedKeyOne := fmt.Sprintf("%s_%s", f.Namespace.Name, "one")
+		expectedKeyTwo := fmt.Sprintf("%s_%s", f.Namespace.Name, "two")
+		expectedKeyThree := fmt.Sprintf("%s_%s", f.Namespace.Name, "three")
+		secretValue := findValue
+		tc.Secrets = map[string]framework.SecretEntry{
+			secretKeyOne:   {Value: secretValue},
+			secretKeyTwo:   {Value: secretValue},
+			secretKeyThree: {Value: secretValue},
+		}
+		tc.ExpectedSecret = &v1.Secret{
+			Type: v1.SecretTypeOpaque,
+			Data: map[string][]byte{
+				expectedKeyOne:   []byte(secretValue),
+				expectedKeyTwo:   []byte(secretValue),
+				expectedKeyThree: []byte(secretValue),
+			},
+		}
+		tc.ExternalSecret.Spec.DataFrom = []esapi.ExternalSecretDataFromRemoteRef{
+			{
+				Find: &esapi.ExternalSecretFind{
+					Name: &esapi.FindName{
+						RegExp: fmt.Sprintf("e2e_find_and_rewrite_%s.+", f.Namespace.Name),
+					},
+				},
+				Rewrite: []esapi.ExternalSecretRewrite{
+					{
+						Regexp: &esapi.ExternalSecretRewriteRegexp{
+							Source: "e2e_find_and_rewrite_(.*)",
+							Target: "$1",
+						},
+					},
+				},
+			},
+		}
+	}
+}
+
 func FindByNameWithPath(f *framework.Framework) (string, func(*framework.TestCase)) {
 	return "[common] should find secrets by name with path", func(tc *framework.TestCase) {
 		secretKeyOne := fmt.Sprintf("e2e-find-name-%s-one", f.Namespace.Name)
 		secretKeyTwo := fmt.Sprintf("%s-two", f.Namespace.Name)
 		secretKeythree := fmt.Sprintf("%s-three", f.Namespace.Name)
-		secretValue := "{\"foo1\":\"foo1-val\"}"
+		secretValue := findValue
 		tc.Secrets = map[string]framework.SecretEntry{
 			secretKeyOne:   {Value: secretValue},
 			secretKeyTwo:   {Value: secretValue},

+ 2 - 0
e2e/suites/provider/cases/gcp/gcp.go

@@ -38,6 +38,7 @@ var _ = Describe("[gcp]", Label("gcp", "secretsmanager"), func() {
 		Entry(common.SimpleDataSync(f)),
 		Entry(common.JSONDataWithProperty(f)),
 		Entry(common.JSONDataFromSync(f)),
+		Entry(common.JSONDataFromRewrite(f)),
 		Entry(common.NestedJSONWithGJSON(f)),
 		Entry(common.JSONDataWithTemplate(f)),
 		Entry(common.DockerJSONConfig(f)),
@@ -47,6 +48,7 @@ var _ = Describe("[gcp]", Label("gcp", "secretsmanager"), func() {
 		Entry(common.SyncWithoutTargetName(f)),
 		Entry(common.JSONDataWithoutTargetName(f)),
 		Entry(common.FindByName(f)),
+		Entry(common.FindByNameAndRewrite(f)),
 		Entry(common.FindByNameWithPath(f)),
 		Entry(common.FindByTag(f)),
 		Entry(common.FindByTagWithPath(f)),

+ 1 - 0
e2e/suites/provider/cases/gitlab/gitlab.go

@@ -34,6 +34,7 @@ var _ = Describe("[gitlab]", Label("gitlab"), func() {
 		Entry(common.SimpleDataSync(f)),
 		Entry(common.JSONDataWithProperty(f)),
 		Entry(common.JSONDataFromSync(f)),
+		Entry(common.JSONDataFromRewrite(f)),
 		Entry(common.NestedJSONWithGJSON(f)),
 		Entry(common.JSONDataWithTemplate(f)),
 		Entry(common.SyncWithoutTargetName(f)),

+ 1 - 0
e2e/suites/provider/cases/kubernetes/kubernetes.go

@@ -41,6 +41,7 @@ var _ = Describe("[kubernetes] ", Label("kubernetes"), func() {
 		Entry(common.DataPropertyDockerconfigJSON(f)),
 		Entry(common.SSHKeySyncDataProperty(f)),
 		Entry(common.JSONDataFromSync(f)),
+		Entry(common.JSONDataFromRewrite(f)),
 		Entry(FindByTag(f)),
 		Entry(FindByName(f)),
 

+ 1 - 0
e2e/suites/provider/cases/oracle/oracle.go

@@ -29,6 +29,7 @@ var _ = Describe("[oracle]", Label("oracle"), func() {
 		Entry(common.SimpleDataSync(f)),
 		Entry(common.NestedJSONWithGJSON(f)),
 		Entry(common.JSONDataFromSync(f)),
+		Entry(common.JSONDataFromRewrite(f)),
 		Entry(common.JSONDataWithProperty(f)),
 		Entry(common.JSONDataWithTemplate(f)),
 		Entry(common.DockerJSONConfig(f)),

+ 12 - 0
e2e/suites/provider/cases/vault/vault.go

@@ -43,7 +43,9 @@ var _ = Describe("[vault]", Label("vault"), func() {
 		framework.TableFunc(f, prov),
 		// uses token auth
 		framework.Compose(withTokenAuth, f, common.FindByName, useTokenAuth),
+		framework.Compose(withTokenAuth, f, common.FindByNameAndRewrite, useTokenAuth),
 		framework.Compose(withTokenAuth, f, common.JSONDataFromSync, useTokenAuth),
+		framework.Compose(withTokenAuth, f, common.JSONDataFromRewrite, useTokenAuth),
 		framework.Compose(withTokenAuth, f, common.JSONDataWithProperty, useTokenAuth),
 		framework.Compose(withTokenAuth, f, common.JSONDataWithTemplate, useTokenAuth),
 		framework.Compose(withTokenAuth, f, common.DataPropertyDockerconfigJSON, useTokenAuth),
@@ -52,40 +54,50 @@ var _ = Describe("[vault]", Label("vault"), func() {
 		framework.Compose(withTokenAuth, f, common.DecodingPolicySync, useTokenAuth),
 		// use cert auth
 		framework.Compose(withCertAuth, f, common.FindByName, useCertAuth),
+		framework.Compose(withCertAuth, f, common.FindByNameAndRewrite, useCertAuth),
 		framework.Compose(withCertAuth, f, common.JSONDataFromSync, useCertAuth),
+		framework.Compose(withCertAuth, f, common.JSONDataFromRewrite, useCertAuth),
 		framework.Compose(withCertAuth, f, common.JSONDataWithProperty, useCertAuth),
 		framework.Compose(withCertAuth, f, common.JSONDataWithTemplate, useCertAuth),
 		framework.Compose(withCertAuth, f, common.DataPropertyDockerconfigJSON, useCertAuth),
 		framework.Compose(withCertAuth, f, common.JSONDataWithoutTargetName, useCertAuth),
 		// use approle auth
 		framework.Compose(withApprole, f, common.FindByName, useApproleAuth),
+		framework.Compose(withApprole, f, common.FindByNameAndRewrite, useApproleAuth),
 		framework.Compose(withApprole, f, common.JSONDataFromSync, useApproleAuth),
+		framework.Compose(withApprole, f, common.JSONDataFromRewrite, useApproleAuth),
 		framework.Compose(withApprole, f, common.JSONDataWithProperty, useApproleAuth),
 		framework.Compose(withApprole, f, common.JSONDataWithTemplate, useApproleAuth),
 		framework.Compose(withApprole, f, common.DataPropertyDockerconfigJSON, useApproleAuth),
 		framework.Compose(withApprole, f, common.JSONDataWithoutTargetName, useApproleAuth),
 		// use v1 provider
 		framework.Compose(withV1, f, common.JSONDataFromSync, useV1Provider),
+		framework.Compose(withV1, f, common.JSONDataFromRewrite, useV1Provider),
 		framework.Compose(withV1, f, common.JSONDataWithProperty, useV1Provider),
 		framework.Compose(withV1, f, common.JSONDataWithTemplate, useV1Provider),
 		framework.Compose(withV1, f, common.DataPropertyDockerconfigJSON, useV1Provider),
 		framework.Compose(withV1, f, common.JSONDataWithoutTargetName, useV1Provider),
 		// use jwt provider
 		framework.Compose(withJWT, f, common.FindByName, useJWTProvider),
+		framework.Compose(withJWT, f, common.FindByNameAndRewrite, useJWTProvider),
 		framework.Compose(withJWT, f, common.JSONDataFromSync, useJWTProvider),
+		framework.Compose(withJWT, f, common.JSONDataFromRewrite, useJWTProvider),
 		framework.Compose(withJWT, f, common.JSONDataWithProperty, useJWTProvider),
 		framework.Compose(withJWT, f, common.JSONDataWithTemplate, useJWTProvider),
 		framework.Compose(withJWT, f, common.DataPropertyDockerconfigJSON, useJWTProvider),
 		framework.Compose(withJWT, f, common.JSONDataWithoutTargetName, useJWTProvider),
 		// use jwt k8s provider
 		framework.Compose(withJWTK8s, f, common.JSONDataFromSync, useJWTK8sProvider),
+		framework.Compose(withJWTK8s, f, common.JSONDataFromRewrite, useJWTK8sProvider),
 		framework.Compose(withJWTK8s, f, common.JSONDataWithProperty, useJWTK8sProvider),
 		framework.Compose(withJWTK8s, f, common.JSONDataWithTemplate, useJWTK8sProvider),
 		framework.Compose(withJWTK8s, f, common.DataPropertyDockerconfigJSON, useJWTK8sProvider),
 		framework.Compose(withJWTK8s, f, common.JSONDataWithoutTargetName, useJWTK8sProvider),
 		// use kubernetes provider
 		framework.Compose(withK8s, f, common.FindByName, useKubernetesProvider),
+		framework.Compose(withK8s, f, common.FindByNameAndRewrite, useKubernetesProvider),
 		framework.Compose(withK8s, f, common.JSONDataFromSync, useKubernetesProvider),
+		framework.Compose(withK8s, f, common.JSONDataFromRewrite, useKubernetesProvider),
 		framework.Compose(withK8s, f, common.JSONDataWithProperty, useKubernetesProvider),
 		framework.Compose(withK8s, f, common.JSONDataWithTemplate, useKubernetesProvider),
 		framework.Compose(withK8s, f, common.DataPropertyDockerconfigJSON, useKubernetesProvider),

+ 17 - 17
go.mod

@@ -35,12 +35,12 @@ replace (
 require (
 	cloud.google.com/go v0.102.0 // indirect
 	cloud.google.com/go/iam v0.3.0
-	cloud.google.com/go/secretmanager v1.4.0
+	cloud.google.com/go/secretmanager v1.5.0
 	github.com/Azure/azure-sdk-for-go v66.0.0+incompatible
-	github.com/Azure/go-autorest/autorest v0.11.27
+	github.com/Azure/go-autorest/autorest v0.11.28
 	github.com/Azure/go-autorest/autorest/adal v0.9.20
 	github.com/Azure/go-autorest/autorest/azure/auth v0.5.11
-	github.com/AzureAD/microsoft-authentication-library-for-go v0.5.2
+	github.com/AzureAD/microsoft-authentication-library-for-go v0.5.3
 	github.com/IBM/go-sdk-core/v5 v5.10.1
 	github.com/IBM/secrets-manager-go-sdk v1.0.44
 	github.com/Masterminds/goutils v1.1.1 // indirect
@@ -48,10 +48,10 @@ require (
 	github.com/PaesslerAG/jsonpath v0.1.1
 	github.com/ahmetb/gen-crd-api-reference-docs v0.3.0
 	github.com/akeylesslabs/akeyless-go-cloud-id v0.3.4
-	github.com/akeylesslabs/akeyless-go/v2 v2.16.8
+	github.com/akeylesslabs/akeyless-go/v2 v2.17.0
 	github.com/aliyun/alibaba-cloud-sdk-go v1.61.1673
 	github.com/aws/aws-sdk-go v1.44.52
-	github.com/crossplane/crossplane-runtime v0.16.0
+	github.com/crossplane/crossplane-runtime v0.17.0
 	github.com/go-logr/logr v1.2.3
 	github.com/go-test/deep v1.0.4 // indirect
 	github.com/golang-jwt/jwt/v4 v4.4.2
@@ -60,28 +60,28 @@ require (
 	github.com/googleapis/gax-go/v2 v2.4.0
 	github.com/hashicorp/vault/api v1.7.2
 	github.com/hashicorp/vault/api/auth/approle v0.2.0
-	github.com/hashicorp/vault/api/auth/kubernetes v0.1.0
+	github.com/hashicorp/vault/api/auth/kubernetes v0.2.0
 	github.com/hashicorp/vault/api/auth/ldap v0.1.0
 	github.com/huandu/xstrings v1.3.2 // indirect
 	github.com/lestrrat-go/jwx v1.2.25
 	github.com/onsi/ginkgo/v2 v2.1.4
-	github.com/onsi/gomega v1.19.0
+	github.com/onsi/gomega v1.20.0
 	github.com/oracle/oci-go-sdk/v56 v56.1.0
 	github.com/prometheus/client_golang v1.12.2
 	github.com/prometheus/client_model v0.2.0
-	github.com/spf13/cobra v1.4.0
+	github.com/spf13/cobra v1.5.0
 	github.com/stretchr/testify v1.8.0
 	github.com/tidwall/gjson v1.14.1
-	github.com/xanzy/go-gitlab v0.68.2
+	github.com/xanzy/go-gitlab v0.70.0
 	github.com/yandex-cloud/go-genproto v0.0.0-20220314102905-1acaee8ca7eb
 	github.com/yandex-cloud/go-sdk v0.0.0-20220314105123-d0c2a928feb6
 	github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a
 	go.uber.org/zap v1.21.0
-	golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
+	golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
 	golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2
-	google.golang.org/api v0.87.0
+	google.golang.org/api v0.88.0
 	google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f
-	google.golang.org/grpc v1.47.0
+	google.golang.org/grpc v1.48.0
 	gopkg.in/yaml.v3 v3.0.1
 	grpc.go4.org v0.0.0-20170609214715-11d0a25b4919
 	k8s.io/api v0.24.0
@@ -90,17 +90,17 @@ require (
 	k8s.io/client-go v0.23.5
 	k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
 	sigs.k8s.io/controller-runtime v0.11.2
-	sigs.k8s.io/controller-tools v0.9.0
-	software.sslmate.com/src/go-pkcs12 v0.2.0
+	sigs.k8s.io/controller-tools v0.9.2
+	software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78
 )
 
-require github.com/1Password/connect-sdk-go v1.4.0
+require github.com/1Password/connect-sdk-go v1.5.0
 
 require (
 	github.com/argoproj/argo-cd/v2 v2.4.6
-	github.com/fluxcd/helm-controller/api v0.22.1
+	github.com/fluxcd/helm-controller/api v0.22.2
 	github.com/fluxcd/pkg/apis/meta v0.14.2
-	github.com/fluxcd/source-controller/api v0.25.10
+	github.com/fluxcd/source-controller/api v0.25.11
 	github.com/maxbrunsfeld/counterfeiter/v6 v6.5.0
 	gotest.tools/v3 v3.0.3
 	sigs.k8s.io/yaml v1.3.0

+ 45 - 38
go.sum

@@ -57,8 +57,8 @@ cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2k
 cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
 cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
 cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
-cloud.google.com/go/secretmanager v1.4.0 h1:Cl+kDYvKHjPQ1l2DZDr2FG/cXUzNGCZkh05BARgddo8=
-cloud.google.com/go/secretmanager v1.4.0/go.mod h1:h2VZz7Svt1W9/YVl7mfcX9LddvS6SOLOvMoOXBhYT1k=
+cloud.google.com/go/secretmanager v1.5.0 h1:XdbW+Fx5amsRzjHeFbDAQI2v2VUkSl3BWEgkQD6z8hY=
+cloud.google.com/go/secretmanager v1.5.0/go.mod h1:5C9kM+RwSpkURNovKySkNvGQLUaOgyoR5W0RUx2SyHQ=
 cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
 cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
 cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
@@ -70,8 +70,8 @@ code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFj
 code.gitea.io/sdk/gitea v0.15.1/go.mod h1:klY2LVI3s3NChzIk/MzMn7G1FHrfU7qd63iSMVoHRBA=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/1Password/connect-sdk-go v1.4.0 h1:c1cR22z69E634ZxEhjsBI08FNEcDBuM57IKMFDk04aM=
-github.com/1Password/connect-sdk-go v1.4.0/go.mod h1:ADZd9XFWwbBcnAayv7hVm9LTF0WkyoJ37jVA6BRtqzE=
+github.com/1Password/connect-sdk-go v1.5.0 h1:F0WJcLSzGg3iXEDY49/ULdszYKsQLGTzn+2cyYXqiyk=
+github.com/1Password/connect-sdk-go v1.5.0/go.mod h1:TdynFeyvaRoackENbJ8RfJokH+WAowAu1MLmUbdMq6s=
 github.com/Azure/azure-sdk-for-go v55.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
 github.com/Azure/azure-sdk-for-go v66.0.0+incompatible h1:bmmC38SlE8/E81nNADlgmVGurPWMHDX2YNXVQMrBpEE=
 github.com/Azure/azure-sdk-for-go v66.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
@@ -83,8 +83,8 @@ github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK
 github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
 github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
 github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
-github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A=
-github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U=
+github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM=
+github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA=
 github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
 github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
 github.com/Azure/go-autorest/autorest/adal v0.9.20 h1:gJ3E98kMpFB1MFqQCvA1yFab8vthOeD4VlFRQULxahg=
@@ -107,17 +107,15 @@ github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+Z
 github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
 github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
 github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
-github.com/AzureAD/microsoft-authentication-library-for-go v0.5.2 h1:BGX4OiGP9htYSd6M3pAZctcUUSruhIAUVkv2X0Cn9yE=
-github.com/AzureAD/microsoft-authentication-library-for-go v0.5.2/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4=
+github.com/AzureAD/microsoft-authentication-library-for-go v0.5.3 h1:TsFCaaF5tR4XN8b4zLVl/J4qMb0nf80Q4CXcpXDNJDY=
+github.com/AzureAD/microsoft-authentication-library-for-go v0.5.3/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
 github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
 github.com/GoogleCloudPlatform/k8s-cloud-provider v1.16.1-0.20210702024009-ea6160c1d0e3/go.mod h1:8XasY4ymP2V/tn2OOV9ZadmiTE1FIB/h3W+yNlPttKw=
-github.com/HdrHistogram/hdrhistogram-go v1.0.1 h1:GX8GAYDuhlFQnI2fRDHQhTlkHMz8bEn0jTI6LJU0mpw=
-github.com/HdrHistogram/hdrhistogram-go v1.0.1 h1:GX8GAYDuhlFQnI2fRDHQhTlkHMz8bEn0jTI6LJU0mpw=
-github.com/HdrHistogram/hdrhistogram-go v1.0.1/go.mod h1:BWJ+nMSHY3L41Zj7CA3uXnloDp7xxV0YvstAE7nKTaM=
-github.com/HdrHistogram/hdrhistogram-go v1.0.1/go.mod h1:BWJ+nMSHY3L41Zj7CA3uXnloDp7xxV0YvstAE7nKTaM=
+github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM=
+github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
 github.com/IBM/go-sdk-core/v5 v5.9.5/go.mod h1:YlOwV9LeuclmT/qi/LAK2AsobbAP42veV0j68/rlZsE=
 github.com/IBM/go-sdk-core/v5 v5.10.1 h1:IEpjDJyB7okrC6bJ7fPZqBiOv+16VichT6kZXAz9bbQ=
 github.com/IBM/go-sdk-core/v5 v5.10.1/go.mod h1:u/33BzPy8sthgEhSeBnf6/kPCqwvC9VKw5byfqQfbe0=
@@ -175,8 +173,8 @@ github.com/ahmetb/gen-crd-api-reference-docs v0.3.0/go.mod h1:TdjdkYhlOifCQWPs1U
 github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
 github.com/akeylesslabs/akeyless-go-cloud-id v0.3.4 h1:vTckjyBhHOBiOWSC/oaEU2Oo4OH5eAlQiwKu2RMxsFg=
 github.com/akeylesslabs/akeyless-go-cloud-id v0.3.4/go.mod h1:As/RomC2w/fa3y+yHRlVHPmkbP+zrKBFRow41y5dk+E=
-github.com/akeylesslabs/akeyless-go/v2 v2.16.8 h1:tjcaT3BHDsSmypDVNCQXDUj/lK0JB9DJKsf8AASYBXU=
-github.com/akeylesslabs/akeyless-go/v2 v2.16.8/go.mod h1:uOdXD49NCCe4rexeSc2aBU5Qv4KZgJE6YlbtYalvb+I=
+github.com/akeylesslabs/akeyless-go/v2 v2.17.0 h1:dC257qIL63tuqKCqDLCKblwudwh7gimm4/GrkXUHchc=
+github.com/akeylesslabs/akeyless-go/v2 v2.17.0/go.mod h1:uOdXD49NCCe4rexeSc2aBU5Qv4KZgJE6YlbtYalvb+I=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -335,12 +333,13 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
 github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/crossplane/crossplane-runtime v0.16.0 h1:NstJdHeK3C+u3By0vQjOG1Y6+v53JYOy00IgCL9GHAw=
-github.com/crossplane/crossplane-runtime v0.16.0/go.mod h1:IPT3HTsovwmbw3i+SdsOyaC3r3b7TW+otBMmZsHLnSU=
+github.com/crossplane/crossplane-runtime v0.17.0 h1:gt2JcOYcVBw/luQToq2hUkoersL12ICuV0YzKI5lyCs=
+github.com/crossplane/crossplane-runtime v0.17.0/go.mod h1:IPT3HTsovwmbw3i+SdsOyaC3r3b7TW+otBMmZsHLnSU=
 github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
 github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -414,8 +413,8 @@ github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
 github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
 github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 github.com/flosch/pongo2 v0.0.0-20181225140029-79872a7b2769/go.mod h1:tbAXHifHQWNSpWbiJHpJTZH5fi3XHhDMdP//vuz9WS4=
-github.com/fluxcd/helm-controller/api v0.22.1 h1:J+i6AZMj0SCuQxcpHmyf1qmPdgDKP0nTkyS4/oLHx0M=
-github.com/fluxcd/helm-controller/api v0.22.1/go.mod h1:2xuHOYjbRv86ekTYkF7VzTwu5hEHYawrdi7FZrvpr4g=
+github.com/fluxcd/helm-controller/api v0.22.2 h1:nh0GZBsUE0gNzm4PmPa4aOoqYlbZbpGt2pcIL9S2184=
+github.com/fluxcd/helm-controller/api v0.22.2/go.mod h1:GfD9TFgLyn81VgwZN1yM47/aUdz0SwTSyRAGmC8xZ+c=
 github.com/fluxcd/pkg/apis/acl v0.0.3 h1:Lw0ZHdpnO4G7Zy9KjrzwwBmDZQuy4qEjaU/RvA6k1lc=
 github.com/fluxcd/pkg/apis/acl v0.0.3/go.mod h1:XPts6lRJ9C9fIF9xVWofmQwftvhY25n1ps7W9xw0XLU=
 github.com/fluxcd/pkg/apis/kustomize v0.4.1 h1:YgIF9TJ23pH66W/gYlEu+DeH1pU3tS4xYlRc5AQzk58=
@@ -423,8 +422,8 @@ github.com/fluxcd/pkg/apis/kustomize v0.4.1/go.mod h1:U9rfSgDHaQd74PgPKt9DprtuzT
 github.com/fluxcd/pkg/apis/meta v0.14.1/go.mod h1:1uJkTJGSZWrZxL5PFpx1IxGLrFmT1Cd0C2fFWrbv77I=
 github.com/fluxcd/pkg/apis/meta v0.14.2 h1:/Hf7I/Vz01vv3m7Qx7DtQvrzAL1oVt0MJcLb/I1Y1HE=
 github.com/fluxcd/pkg/apis/meta v0.14.2/go.mod h1:ijZ61VG/8T3U17gj0aFL3fdtZL+mulD6V8VrLLUCAgM=
-github.com/fluxcd/source-controller/api v0.25.10 h1:nwOB6Awy6mLlysEHfmqmk6Ek5yebYQ8kYq0lv+bSKb8=
-github.com/fluxcd/source-controller/api v0.25.10/go.mod h1:5kihSWjg+gIXLPTTXbe6AnY+g+iDmP+CY4g6nFqublc=
+github.com/fluxcd/source-controller/api v0.25.11 h1:1YbH5vlQ1k96tbRKTdQHCBsBVMgpp/QaRpKG/r29koQ=
+github.com/fluxcd/source-controller/api v0.25.11/go.mod h1:5kihSWjg+gIXLPTTXbe6AnY+g+iDmP+CY4g6nFqublc=
 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
 github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
@@ -824,14 +823,13 @@ github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn
 github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
 github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
 github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
-github.com/hashicorp/vault/api v1.3.0/go.mod h1:EabNQLI0VWbWoGlA+oBLC8PXmR9D60aUVgQGvangFWQ=
 github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw=
 github.com/hashicorp/vault/api v1.7.2 h1:kawHE7s/4xwrdKbkmwQi0wYaIeUhk5ueek7ljuezCVQ=
 github.com/hashicorp/vault/api v1.7.2/go.mod h1:xbfA+1AvxFseDzxxdWaL0uO99n1+tndus4GCrtouy0M=
 github.com/hashicorp/vault/api/auth/approle v0.2.0 h1:mdNYwDRp+tqvJmyfbkaHLLePGYDY27mOFtBZBb7va/I=
 github.com/hashicorp/vault/api/auth/approle v0.2.0/go.mod h1:w4PwYaLJmGq0cMss0ZAV9b49vcrpB6SKxMMLUp4voR8=
-github.com/hashicorp/vault/api/auth/kubernetes v0.1.0 h1:6BtyahbF4aQp8gg3ww0A/oIoqzbhpNP1spXU3nHE0n0=
-github.com/hashicorp/vault/api/auth/kubernetes v0.1.0/go.mod h1:Pdgk78uIs0mgDOLvc3a+h/vYIT9rznw2sz+ucuH9024=
+github.com/hashicorp/vault/api/auth/kubernetes v0.2.0 h1:ScdzRAF8JZIdJYP4oprZKsIS4GSTCaTP4iG2JJlJDvA=
+github.com/hashicorp/vault/api/auth/kubernetes v0.2.0/go.mod h1:2BKADs9mwqAycDK/6tiHRh2sX0SPnC0DN4wHjJoAirw=
 github.com/hashicorp/vault/api/auth/ldap v0.1.0 h1:runn+BIRU6/QcGirhstoJIqO+plVuTN/zf401tbB5H0=
 github.com/hashicorp/vault/api/auth/ldap v0.1.0/go.mod h1:vl3YZyt+bRtTHvVqKWeOTCI5I40t31t0S48efipZq64=
 github.com/hashicorp/vault/sdk v0.3.0/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0=
@@ -1120,8 +1118,9 @@ github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7
 github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
 github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
 github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
-github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
 github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
+github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q=
+github.com/onsi/gomega v1.20.0/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=
 github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
@@ -1290,8 +1289,9 @@ github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHN
 github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
 github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
 github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
-github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
 github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
+github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
+github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
 github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@@ -1364,8 +1364,8 @@ github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV
 github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
 github.com/whilp/git-urls v0.0.0-20191001220047-6db9661140c0/go.mod h1:2rx5KE5FLD0HRfkkpyn8JwbVLBdhgeiOb2D2D9LLKM4=
 github.com/xanzy/go-gitlab v0.60.0/go.mod h1:F0QEXwmqiBUxCgJm8fE9S+1veX4XC9Z4cfaAbqwk4YM=
-github.com/xanzy/go-gitlab v0.68.2 h1:bRVpa+czzpR2j2UV9oeJRU3SO40ieOHlgKlVwW0LRBw=
-github.com/xanzy/go-gitlab v0.68.2/go.mod h1:o4yExCtdaqlM8YGdDJWuZoBmfxBsmA9TPEjs9mx1UO4=
+github.com/xanzy/go-gitlab v0.70.0 h1:zJ8WukB5psMcfmQctHsiG/PyqLqLIdD05wCLwdPNEBg=
+github.com/xanzy/go-gitlab v0.70.0/go.mod h1:o4yExCtdaqlM8YGdDJWuZoBmfxBsmA9TPEjs9mx1UO4=
 github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
 github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
 github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
@@ -1491,6 +1491,7 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh
 golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
@@ -1498,10 +1499,10 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
 golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
+golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1627,6 +1628,7 @@ golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su
 golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.0.0-20220621193019-9d032be2e588/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ=
 golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
@@ -1793,12 +1795,15 @@ golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220624220833-87e55d714810 h1:rHZQSjJdAI4Xf5Qzeh2bBc5YJIkPFVM6oDtMFYmgws0=
 golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
@@ -1917,6 +1922,7 @@ gomodules.xyz/notify v0.1.0/go.mod h1:wGy0vLXGpabCg0j9WbjzXf7pM7Khz11FqCLtBbTujP
 gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
 gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
 gonum.org/v1/gonum v0.6.2/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU=
+gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
 gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
 gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
 gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
@@ -1965,8 +1971,9 @@ google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69
 google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
 google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
 google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
-google.golang.org/api v0.87.0 h1:pUQVF/F+X7Tl1lo4LJoJf5BOpjtmINU80p9XpYTU2p4=
-google.golang.org/api v0.87.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
+google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g=
+google.golang.org/api v0.88.0 h1:MPwxQRqpyskYhr2iNyfsQ8R06eeyhe7UEuR30p136ZQ=
+google.golang.org/api v0.88.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
 google.golang.org/appengine v1.0.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -2061,7 +2068,6 @@ google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2
 google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
 google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
 google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
-google.golang.org/genproto v0.0.0-20220405205423-9d709892a2bf/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
 google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
 google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
 google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
@@ -2072,6 +2078,7 @@ google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP
 google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
 google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
 google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
+google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
 google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f h1:hJ/Y5SqPXbarffmAsApliUlcvMU+wScNGfyop4bZm8o=
 google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
 google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
@@ -2112,8 +2119,9 @@ google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ5
 google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
 google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
 google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8=
 google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
+google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w=
+google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -2185,7 +2193,6 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
 gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20220512140231-539c8e751b99/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
@@ -2279,8 +2286,8 @@ sigs.k8s.io/controller-runtime v0.11.0/go.mod h1:KKwLiTooNGu+JmLZGn9Sl3Gjmfj66eM
 sigs.k8s.io/controller-runtime v0.11.2 h1:H5GTxQl0Mc9UjRJhORusqfJCIjBO8UtUxGggCwL1rLA=
 sigs.k8s.io/controller-runtime v0.11.2/go.mod h1:P6QCzrEjLaZGqHsfd+os7JQ+WFZhvB8MRFsn4dWF7O4=
 sigs.k8s.io/controller-tools v0.8.0/go.mod h1:qE2DXhVOiEq5ijmINcFbqi9GZrrUjzB1TuJU0xa6eoY=
-sigs.k8s.io/controller-tools v0.9.0 h1:b/vSEPpA8hiMiyzDfLbZdCn3hoAcy3/868OHhYtHY9w=
-sigs.k8s.io/controller-tools v0.9.0/go.mod h1:NUkn8FTV3Sad3wWpSK7dt/145qfuQ8CKJV6j4jHC5rM=
+sigs.k8s.io/controller-tools v0.9.2 h1:AkTE3QAdz9LS4iD3EJvHyYxBkg/g9fTbgiYsrcsFCcM=
+sigs.k8s.io/controller-tools v0.9.2/go.mod h1:NUkn8FTV3Sad3wWpSK7dt/145qfuQ8CKJV6j4jHC5rM=
 sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs=
 sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y=
 sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY=
@@ -2299,6 +2306,6 @@ sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
 sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
 sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
 sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
-software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE=
-software.sslmate.com/src/go-pkcs12 v0.2.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ=
+software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78 h1:SqYE5+A2qvRhErbsXFfUEUmpWEKxxRSMgGLkvRAFOV4=
+software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78/go.mod h1:B7Wf0Ya4DHF9Yw+qfZuJijQYkWicqDa+79Ytmmq3Kjg=
 sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=

+ 7 - 0
hack/api-docs/mkdocs.yml

@@ -3,6 +3,7 @@ repo_url: https://github.com/external-secrets/external-secrets
 repo_name: External Secrets Operator
 site_dir: ../../site
 docs_dir: ../../docs
+edit_uri: ./edit/main/docs/
 remote_branch: gh-pages
 theme:
   name: material
@@ -18,6 +19,11 @@ plugins:
       lang: en
   - macros:
       include_dir: docs/snippets
+copyright: |
+  &copy; 2022 The external-secrets Authors.<br/>
+  &copy; 2022 The Linux Foundation. All rights reserved.<br/><br/>
+  The Linux Foundation has registered trademarks and uses trademarks.<br/>
+  For a list of trademarks of The Linux Foundation, please see our <a href="https://www.linuxfoundation.org/trademark-usage/">Trademark Usage page</a>.
 extra:
   version:
     provider: mike
@@ -46,6 +52,7 @@ nav:
     - Getting Multiple Secrets: guides-getallsecrets.md
     - Multi Tenancy: guides-multi-tenancy.md
     - Metrics: guides-metrics.md
+    - Rewriting Keys: guides-datafrom-rewrite.md
     - Upgrading to v1beta1: guides-v1beta1.md
     - Using Latest Image: guides-using-latest-image.md
   - Provider:

+ 33 - 0
hack/crd.generate.sh

@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+BUNDLE_DIR="${1}"
+CRD_DIR="${2}"
+BUNDLE_YAML="${BUNDLE_DIR}/bundle.yaml"
+
+cd "${SCRIPT_DIR}"/../
+
+go run sigs.k8s.io/controller-tools/cmd/controller-gen \
+  object:headerFile="hack/boilerplate.go.txt" \
+  paths="./..."
+go run sigs.k8s.io/controller-tools/cmd/controller-gen crd \
+  paths="./..." \
+  output:crd:artifacts:config="${CRD_DIR}/bases"
+
+# Remove extra header lines in generated CRDs
+# This is needed for building the helm chart
+for f in "${CRD_DIR}"/bases/*.yaml; do
+  if [[ $f == *kustomization.yaml ]];
+  then
+      continue;
+  fi;
+  tail -n +2 < "$f" > "$f.bkp"
+  cp "$f.bkp" "$f"
+  rm "$f.bkp"
+done
+
+shopt -s extglob
+yq e \
+    '.spec.conversion.strategy = "Webhook" | .spec.conversion.webhook.conversionReviewVersions = ["v1"] | .spec.conversion.webhook.clientConfig.service.name = "kubernetes" | .spec.conversion.webhook.clientConfig.service.namespace = "default" |	.spec.conversion.webhook.clientConfig.service.path = "/convert"' \
+    "${CRD_DIR}"/bases/!(kustomization).yaml > "${BUNDLE_YAML}"

+ 35 - 0
hack/helm.generate.sh

@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+set -euo pipefail
+SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+BUNDLE_DIR="${1}"
+HELM_DIR="${2}"
+
+if [[ "$OSTYPE" == "darwin"* ]]; then
+  SEDPRG="gsed"
+else
+  SEDPRG="sed"
+fi
+
+cd "${SCRIPT_DIR}"/../
+
+# Split the generated bundle yaml file to inject control flags
+yq e -Ns "\"${HELM_DIR}/templates/crds/\" + .spec.names.singular" ${BUNDLE_DIR}/bundle.yaml
+
+# Add helm if statement for controlling the install of CRDs
+for i in "${HELM_DIR}"/templates/crds/*.yml; do
+  export CRDS_FLAG_NAME="create$(yq e '.spec.names.kind' $i)"
+  cp "$i" "$i.bkp"
+  if [[ "$CRDS_FLAG_NAME" == *"Cluster"* ]]; then
+    echo "{{- if and (.Values.installCRDs) (.Values.crds.$CRDS_FLAG_NAME) }}" > "$i"
+  elif [[ "$$CRDS_FLAG_NAME" == *"PushSecret"* ]]; then 
+			echo "{{- if and (.Values.installCRDs) (.Values.crds.$$CRDS_FLAG_NAME) }}" > "$$i"
+  else
+    echo "{{- if .Values.installCRDs }}" > "$i"
+  fi
+  cat "$i.bkp" >> "$i"
+  echo "{{- end }}" >> "$i"
+  rm "$i.bkp"
+  $SEDPRG -i 's/name: kubernetes/name: {{ include "external-secrets.fullname" . }}-webhook/g' "$i"
+  $SEDPRG -i 's/namespace: default/namespace: {{ .Release.Namespace | quote }}/g' "$i"
+  mv "$i" "${i%.yml}.yaml"
+done

+ 2 - 0
pkg/controllers/clusterexternalsecret/clusterexternalsecret_controller.go

@@ -16,6 +16,7 @@ package clusterexternalsecret
 
 import (
 	"context"
+	"sort"
 	"time"
 
 	"github.com/go-logr/logr"
@@ -130,6 +131,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
 	setFailedNamespaces(&clusterExternalSecret, failedNamespaces)
 
 	if len(provisionedNamespaces) > 0 {
+		sort.Strings(provisionedNamespaces)
 		clusterExternalSecret.Status.ProvisionedNamespaces = provisionedNamespaces
 	}
 

+ 26 - 5
pkg/controllers/externalsecret/externalsecret_controller.go

@@ -51,6 +51,8 @@ const (
 	errGetES                 = "could not get ExternalSecret"
 	errConvert               = "could not apply conversion strategy to keys: %v"
 	errDecode                = "could not apply decoding strategy to %v[%d]: %v"
+	errRewrite               = "could not rewrite spec.dataFrom[%d]: %v"
+	errInvalidKeys           = "secret keys from spec.dataFrom.%v[%d] can only have alphanumeric,'-', '_' or '.' characters. Convert them using rewrite (https://external-secrets.io/latest/guides-datafrom-rewrite)"
 	errUpdateSecret          = "could not update Secret"
 	errPatchStatus           = "unable to patch status"
 	errGetSecretStore        = "could not get SecretStore %q, %w"
@@ -533,9 +535,20 @@ func (r *Reconciler) getProviderSecretData(ctx context.Context, providerClient e
 			if err != nil {
 				return nil, err
 			}
-			secretMap, err = utils.ConvertKeys(remoteRef.Find.ConversionStrategy, secretMap)
+			secretMap, err = utils.RewriteMap(remoteRef.Rewrite, secretMap)
 			if err != nil {
-				return nil, fmt.Errorf(errConvert, err)
+				return nil, fmt.Errorf(errRewrite, i, err)
+			}
+			if len(remoteRef.Rewrite) == 0 {
+				// ConversionStrategy is deprecated. Use RewriteMap instead.
+				r.recorder.Event(externalSecret, v1.EventTypeWarning, esv1beta1.ReasonDeprecated, fmt.Sprintf("dataFrom[%d].find.conversionStrategy=%v is deprecated and will be removed in further releases. Use dataFrom.rewrite instead", i, remoteRef.Find.ConversionStrategy))
+				secretMap, err = utils.ConvertKeys(remoteRef.Find.ConversionStrategy, secretMap)
+				if err != nil {
+					return nil, fmt.Errorf(errConvert, err)
+				}
+			}
+			if !utils.ValidateKeys(secretMap) {
+				return nil, fmt.Errorf(errInvalidKeys, "find", i)
 			}
 			secretMap, err = utils.DecodeMap(remoteRef.Find.DecodingStrategy, secretMap)
 			if err != nil {
@@ -550,16 +563,24 @@ func (r *Reconciler) getProviderSecretData(ctx context.Context, providerClient e
 			if err != nil {
 				return nil, err
 			}
-			secretMap, err = utils.ConvertKeys(remoteRef.Extract.ConversionStrategy, secretMap)
+			secretMap, err = utils.RewriteMap(remoteRef.Rewrite, secretMap)
 			if err != nil {
-				return nil, fmt.Errorf(errConvert, err)
+				return nil, fmt.Errorf(errRewrite, i, err)
+			}
+			if len(remoteRef.Rewrite) == 0 {
+				secretMap, err = utils.ConvertKeys(remoteRef.Extract.ConversionStrategy, secretMap)
+				if err != nil {
+					return nil, fmt.Errorf(errConvert, err)
+				}
+			}
+			if !utils.ValidateKeys(secretMap) {
+				return nil, fmt.Errorf(errInvalidKeys, "extract", i)
 			}
 			secretMap, err = utils.DecodeMap(remoteRef.Extract.DecodingStrategy, secretMap)
 			if err != nil {
 				return nil, fmt.Errorf(errDecode, "spec.dataFrom", i, err)
 			}
 		}
-
 		providerData = utils.MergeByteMap(providerData, secretMap)
 	}
 

+ 168 - 0
pkg/controllers/externalsecret/externalsecret_controller_test.go

@@ -945,6 +945,139 @@ var _ = Describe("ExternalSecret controller", func() {
 		}
 	}
 
+	// with rewrite all keys from a dataFrom operation
+	// should be put with new rewriting into the secret
+	syncAndRewriteWithDataFrom := func(tc *testCase) {
+		tc.externalSecret.Spec.Data = nil
+		tc.externalSecret.Spec.DataFrom = []esv1beta1.ExternalSecretDataFromRemoteRef{
+			{
+				Extract: &esv1beta1.ExternalSecretDataRemoteRef{
+					Key: remoteKey,
+				},
+				Rewrite: []esv1beta1.ExternalSecretRewrite{{
+					Regexp: &esv1beta1.ExternalSecretRewriteRegexp{
+						Source: "(.*)",
+						Target: "new-$1",
+					},
+				}},
+			},
+			{
+				Extract: &esv1beta1.ExternalSecretDataRemoteRef{
+					Key: remoteKey,
+				},
+				Rewrite: []esv1beta1.ExternalSecretRewrite{{
+					Regexp: &esv1beta1.ExternalSecretRewriteRegexp{
+						Source: "(.*)",
+						Target: "old-$1",
+					},
+				}},
+			},
+		}
+		fakeProvider.WithGetSecretMap(map[string][]byte{
+			"foo": []byte(FooValue),
+			"bar": []byte(BarValue),
+		}, nil)
+		tc.checkSecret = func(es *esv1beta1.ExternalSecret, secret *v1.Secret) {
+			// check values
+			Expect(string(secret.Data["new-foo"])).To(Equal(FooValue))
+			Expect(string(secret.Data["new-bar"])).To(Equal(BarValue))
+			Expect(string(secret.Data["old-foo"])).To(Equal(FooValue))
+			Expect(string(secret.Data["old-bar"])).To(Equal(BarValue))
+		}
+	}
+	// with rewrite keys from dataFrom
+	// should error if keys are not compliant
+	invalidExtractKeysErrCondition := func(tc *testCase) {
+		tc.externalSecret.Spec.Data = nil
+		tc.externalSecret.Spec.DataFrom = []esv1beta1.ExternalSecretDataFromRemoteRef{
+			{
+				Extract: &esv1beta1.ExternalSecretDataRemoteRef{
+					Key: remoteKey,
+				},
+				Rewrite: []esv1beta1.ExternalSecretRewrite{{
+					Regexp: &esv1beta1.ExternalSecretRewriteRegexp{
+						Source: "(.*)",
+						Target: "$1",
+					},
+				}},
+			},
+		}
+		fakeProvider.WithGetSecretMap(map[string][]byte{
+			"foo/bar": []byte(FooValue),
+			"bar/foo": []byte(BarValue),
+		}, nil)
+		tc.checkCondition = func(es *esv1beta1.ExternalSecret) bool {
+			cond := GetExternalSecretCondition(es.Status, esv1beta1.ExternalSecretReady)
+			if cond == nil || cond.Status != v1.ConditionFalse || cond.Reason != esv1beta1.ConditionReasonSecretSyncedError {
+				return false
+			}
+			return true
+		}
+		tc.checkCondition = func(es *esv1beta1.ExternalSecret) bool {
+			cond := GetExternalSecretCondition(es.Status, esv1beta1.ExternalSecretReady)
+			if cond == nil || cond.Status != v1.ConditionFalse || cond.Reason != esv1beta1.ConditionReasonSecretSyncedError {
+				return false
+			}
+			return true
+		}
+		tc.checkExternalSecret = func(es *esv1beta1.ExternalSecret) {
+			Eventually(func() bool {
+				Expect(syncCallsError.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metric)).To(Succeed())
+				return metric.GetCounter().GetValue() >= 2.0
+			}, timeout, interval).Should(BeTrue())
+			Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1beta1.ExternalSecretReady, v1.ConditionFalse, 1.0)).To(BeTrue())
+			Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1beta1.ExternalSecretReady, v1.ConditionTrue, 0.0)).To(BeTrue())
+		}
+
+	}
+	// with rewrite keys from dataFrom
+	// should error if keys are not compliant
+	invalidFindKeysErrCondition := func(tc *testCase) {
+		tc.externalSecret.Spec.Data = nil
+		tc.externalSecret.Spec.DataFrom = []esv1beta1.ExternalSecretDataFromRemoteRef{
+			{
+				Find: &esv1beta1.ExternalSecretFind{
+					Name: &esv1beta1.FindName{
+						RegExp: ".*",
+					},
+				},
+				Rewrite: []esv1beta1.ExternalSecretRewrite{{
+					Regexp: &esv1beta1.ExternalSecretRewriteRegexp{
+						Source: "(.*)",
+						Target: "$1",
+					},
+				}},
+			},
+		}
+		fakeProvider.WithGetAllSecrets(map[string][]byte{
+			"foo/bar": []byte(FooValue),
+			"bar/foo": []byte(BarValue),
+		}, nil)
+		tc.checkCondition = func(es *esv1beta1.ExternalSecret) bool {
+			cond := GetExternalSecretCondition(es.Status, esv1beta1.ExternalSecretReady)
+			if cond == nil || cond.Status != v1.ConditionFalse || cond.Reason != esv1beta1.ConditionReasonSecretSyncedError {
+				return false
+			}
+			return true
+		}
+		tc.checkCondition = func(es *esv1beta1.ExternalSecret) bool {
+			cond := GetExternalSecretCondition(es.Status, esv1beta1.ExternalSecretReady)
+			if cond == nil || cond.Status != v1.ConditionFalse || cond.Reason != esv1beta1.ConditionReasonSecretSyncedError {
+				return false
+			}
+			return true
+		}
+		tc.checkExternalSecret = func(es *esv1beta1.ExternalSecret) {
+			Eventually(func() bool {
+				Expect(syncCallsError.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metric)).To(Succeed())
+				return metric.GetCounter().GetValue() >= 2.0
+			}, timeout, interval).Should(BeTrue())
+			Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1beta1.ExternalSecretReady, v1.ConditionFalse, 1.0)).To(BeTrue())
+			Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1beta1.ExternalSecretReady, v1.ConditionTrue, 0.0)).To(BeTrue())
+		}
+
+	}
+
 	// with dataFrom all properties from the specified secret
 	// should be put into the secret
 	syncWithDataFrom := func(tc *testCase) {
@@ -966,6 +1099,37 @@ var _ = Describe("ExternalSecret controller", func() {
 			Expect(string(secret.Data["bar"])).To(Equal(BarValue))
 		}
 	}
+	// with dataFrom.Find the change is on the called method GetAllSecrets
+	// all keys should be put into the secret
+	syncAndRewriteDataFromFind := func(tc *testCase) {
+		tc.externalSecret.Spec.Data = nil
+		tc.externalSecret.Spec.DataFrom = []esv1beta1.ExternalSecretDataFromRemoteRef{
+			{
+				Find: &esv1beta1.ExternalSecretFind{
+					Name: &esv1beta1.FindName{
+						RegExp: "foobar",
+					},
+				},
+				Rewrite: []esv1beta1.ExternalSecretRewrite{
+					{
+						Regexp: &esv1beta1.ExternalSecretRewriteRegexp{
+							Source: "(.*)",
+							Target: "new-$1",
+						},
+					},
+				},
+			},
+		}
+		fakeProvider.WithGetAllSecrets(map[string][]byte{
+			"foo": []byte(FooValue),
+			"bar": []byte(BarValue),
+		}, nil)
+		tc.checkSecret = func(es *esv1beta1.ExternalSecret, secret *v1.Secret) {
+			// check values
+			Expect(string(secret.Data["new-foo"])).To(Equal(FooValue))
+			Expect(string(secret.Data["new-bar"])).To(Equal(BarValue))
+		}
+	}
 
 	// with dataFrom.Find the change is on the called method GetAllSecrets
 	// all keys should be put into the secret
@@ -1284,7 +1448,11 @@ var _ = Describe("ExternalSecret controller", func() {
 		Entry("should refresh secret map when provider secret changes when using a template", refreshSecretValueMapTemplate),
 		Entry("should not refresh secret value when provider secret changes but refreshInterval is zero", refreshintervalZero),
 		Entry("should fetch secret using dataFrom", syncWithDataFrom),
+		Entry("should rewrite secret using dataFrom", syncAndRewriteWithDataFrom),
+		Entry("should not automatically convert from extract if rewrite is used", invalidExtractKeysErrCondition),
 		Entry("should fetch secret using dataFrom.find", syncDataFromFind),
+		Entry("should rewrite secret using dataFrom.find", syncAndRewriteDataFromFind),
+		Entry("should not automatically convert from find if rewrite is used", invalidFindKeysErrCondition),
 		Entry("should fetch secret using dataFrom and a template", syncWithDataFromTemplate),
 		Entry("should set error condition when provider errors", providerErrCondition),
 		Entry("should set an error condition when store does not exist", storeMissingErrCondition),

+ 5 - 1
pkg/provider/akeyless/akeyless_api.go

@@ -132,8 +132,12 @@ func (a *akeylessBase) GetRotatedSecrets(secretName, token string, version int32
 		return "", fmt.Errorf("can't get rotated secret value: %w", err)
 	}
 
-	val, ok := gsvOut["value"]
+	valI, ok := gsvOut["value"]
 	if ok {
+		val, convert := valI.(map[string]interface{})
+		if !convert {
+			return "", fmt.Errorf("failure converting key from gsvOut")
+		}
 		if _, ok := val["payload"]; ok {
 			return fmt.Sprintf("%v", val["payload"]), nil
 		} else if _, ok := val["target_value"]; ok {

+ 68 - 19
pkg/provider/ibm/provider.go

@@ -17,6 +17,7 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
+	"os"
 	"strconv"
 	"strings"
 	"time"
@@ -555,16 +556,29 @@ func (ibm *providerIBM) ValidateStore(store esv1beta1.GenericStore) error {
 	if ibmSpec.ServiceURL == nil {
 		return fmt.Errorf("serviceURL is required")
 	}
-	secretRef := ibmSpec.Auth.SecretRef.SecretAPIKey
-	err := utils.ValidateSecretSelector(store, secretRef)
-	if err != nil {
-		return err
-	}
-	if secretRef.Name == "" {
-		return fmt.Errorf("secretAPIKey.name cannot be empty")
-	}
-	if secretRef.Key == "" {
-		return fmt.Errorf("secretAPIKey.key cannot be empty")
+
+	containerRef := ibmSpec.Auth.ContainerAuth
+	secretKeyRef := ibmSpec.Auth.SecretRef.SecretAPIKey
+	if utils.IsNil(containerRef.Profile) || (containerRef.Profile == "") {
+		// proceed with API Key Auth validation
+		err := utils.ValidateSecretSelector(store, secretKeyRef)
+		if err != nil {
+			return err
+		}
+		if secretKeyRef.Name == "" {
+			return fmt.Errorf("secretAPIKey.name cannot be empty")
+		}
+		if secretKeyRef.Key == "" {
+			return fmt.Errorf("secretAPIKey.key cannot be empty")
+		}
+	} else {
+		// proceed with container auth
+		if containerRef.TokenLocation == "" {
+			containerRef.TokenLocation = "/var/run/secrets/tokens/vault-token"
+		}
+		if _, err := os.Open(containerRef.TokenLocation); err != nil {
+			return fmt.Errorf("cannot read container auth token %s. %w", containerRef.TokenLocation, err)
+		}
 	}
 	return nil
 }
@@ -585,16 +599,51 @@ func (ibm *providerIBM) NewClient(ctx context.Context, store esv1beta1.GenericSt
 		storeKind: store.GetObjectKind().GroupVersionKind().Kind,
 	}
 
-	if err := iStore.setAuth(ctx); err != nil {
-		return nil, err
-	}
+	var err error
+	var secretsManager *sm.SecretsManagerV1
+	containerAuthProfile := iStore.store.Auth.ContainerAuth.Profile
+	if containerAuthProfile != "" {
+		// container-based auth
+		containerAuthToken := iStore.store.Auth.ContainerAuth.TokenLocation
+		containerAuthEndpoint := iStore.store.Auth.ContainerAuth.IAMEndpoint
+
+		if containerAuthToken == "" {
+			// API default path
+			containerAuthToken = "/var/run/secrets/tokens/vault-token"
+		}
+		if containerAuthEndpoint == "" {
+			// API default path
+			containerAuthEndpoint = "https://iam.cloud.ibm.com"
+		}
 
-	secretsManager, err := sm.NewSecretsManagerV1(&sm.SecretsManagerV1Options{
-		URL: *storeSpec.Provider.IBM.ServiceURL,
-		Authenticator: &core.IamAuthenticator{
-			ApiKey: string(iStore.credentials),
-		},
-	})
+		authenticator, err := core.NewContainerAuthenticatorBuilder().
+			SetIAMProfileName(containerAuthProfile).
+			SetCRTokenFilename(containerAuthToken).
+			SetURL(containerAuthEndpoint).
+			Build()
+		if err != nil {
+			return nil, fmt.Errorf(errIBMClient, err)
+		}
+		secretsManager, err = sm.NewSecretsManagerV1(&sm.SecretsManagerV1Options{
+			URL:           *storeSpec.Provider.IBM.ServiceURL,
+			Authenticator: authenticator,
+		})
+		if err != nil {
+			return nil, fmt.Errorf(errIBMClient, err)
+		}
+	} else {
+		// API Key-based auth
+		if err := iStore.setAuth(ctx); err != nil {
+			return nil, err
+		}
+
+		secretsManager, err = sm.NewSecretsManagerV1(&sm.SecretsManagerV1Options{
+			URL: *storeSpec.Provider.IBM.ServiceURL,
+			Authenticator: &core.IamAuthenticator{
+				ApiKey: string(iStore.credentials),
+			},
+		})
+	}
 
 	// Setup retry options, but only if present
 	if storeSpec.RetrySettings != nil {

+ 13 - 0
pkg/provider/ibm/provider_test.go

@@ -134,6 +134,8 @@ func TestValidateStore(t *testing.T) {
 	}
 	url := "my-url"
 	store.Spec.Provider.IBM.ServiceURL = &url
+	var nilProfile esv1beta1.IBMAuthContainerAuth
+	store.Spec.Provider.IBM.Auth.ContainerAuth = nilProfile
 	err = p.ValidateStore(store)
 	if err == nil {
 		t.Errorf(errExpectedErr)
@@ -150,6 +152,17 @@ func TestValidateStore(t *testing.T) {
 	} else if err.Error() != "namespace not allowed with namespaced SecretStore" {
 		t.Errorf("KeySelector test failed: expected namespace not allowed, got %v", err)
 	}
+
+	// add container auth test
+	store.Spec.Provider.IBM = &esv1beta1.IBMProvider{}
+	store.Spec.Provider.IBM.ServiceURL = &url
+	store.Spec.Provider.IBM.Auth.ContainerAuth.Profile = "Trusted IAM Profile"
+	store.Spec.Provider.IBM.Auth.ContainerAuth.TokenLocation = "/a/path/to/nowhere/that/should/exist"
+	err = p.ValidateStore(store)
+	expected := "cannot read container auth token"
+	if !ErrorContains(err, expected) {
+		t.Errorf("ProfileSelector test failed: %s, expected: '%s'", err.Error(), expected)
+	}
 }
 
 // test the sm<->gcp interface

+ 21 - 4
pkg/provider/onepassword/fake/fake.go

@@ -53,8 +53,11 @@ func (mockClient *OnePasswordMockClient) GetVaultByUUID(uuid string) (*onepasswo
 	return &onepassword.Vault{}, nil
 }
 
-// GetVaultByTitle unused fake.
+// GetVaultByTitle returns a vault, you must preload, only one.
 func (mockClient *OnePasswordMockClient) GetVaultByTitle(uuid string) (*onepassword.Vault, error) {
+	if len(mockClient.MockVaults[uuid]) != 0 {
+		return &mockClient.MockVaults[uuid][0], nil
+	}
 	return &onepassword.Vault{}, nil
 }
 
@@ -82,9 +85,18 @@ func (mockClient *OnePasswordMockClient) GetItem(itemUUID, vaultUUID string) (*o
 	return &onepassword.Item{}, errors.New("status 400: Invalid Item UUID")
 }
 
-// GetItemByUUID unused fake.
-func (mockClient *OnePasswordMockClient) GetItemByUUID(uuid, vaultQuery string) (*onepassword.Item, error) {
-	return &onepassword.Item{}, nil
+// GetItemByUUID returns a *onepassword.Item, you must preload.
+func (mockClient *OnePasswordMockClient) GetItemByUUID(itemUUID, vaultUUID string) (*onepassword.Item, error) {
+	for _, item := range mockClient.MockItems[vaultUUID] {
+		if item.ID == itemUUID {
+			// load the fields that GetItemsByTitle does not
+			item.Fields = mockClient.MockItemFields[vaultUUID][itemUUID]
+
+			return &item, nil
+		}
+	}
+
+	return &onepassword.Item{}, errors.New("status 400: Invalid Item UUID")
 }
 
 // GetItemByTitle unused fake.
@@ -124,6 +136,11 @@ func (mockClient *OnePasswordMockClient) DeleteItemByID(itemUUID, vaultQuery str
 	return nil
 }
 
+// DeleteItemByTitle unused fake.
+func (mockClient *OnePasswordMockClient) DeleteItemByTitle(title, vaultQuery string) error {
+	return nil
+}
+
 // GetFiles unused fake.
 func (mockClient *OnePasswordMockClient) GetFiles(itemQuery, vaultQuery string) ([]onepassword.File, error) {
 	return []onepassword.File{}, nil

+ 7 - 14
pkg/provider/onepassword/onepassword.go

@@ -45,7 +45,6 @@ const (
 	errFetchK8sSecret                             = "could not fetch ConnectToken Secret: %w"
 	errMissingToken                               = "missing Secret Token"
 	errGetVault                                   = "error finding 1Password Vault: %w"
-	errExpectedOneVault                           = "expected one 1Password Vault matching %w"
 	errExpectedOneItem                            = "expected one 1Password Item matching %w"
 	errGetItem                                    = "error finding 1Password Item: %w"
 	errKeyNotFound                                = "key not found in 1Password Vaults: %w"
@@ -183,7 +182,7 @@ func (provider *ProviderOnePassword) GetSecret(ctx context.Context, ref esv1beta
 // to be able to retrieve secrets from the provider.
 func (provider *ProviderOnePassword) Validate() (esv1beta1.ValidationResult, error) {
 	for vaultName := range provider.vaults {
-		_, err := provider.client.GetItems(vaultName)
+		_, err := provider.client.GetVaultByTitle(vaultName)
 		if err != nil {
 			return esv1beta1.ValidationResultError, err
 		}
@@ -221,15 +220,12 @@ func (provider *ProviderOnePassword) GetAllSecrets(ctx context.Context, ref esv1
 	secretData := make(map[string][]byte)
 	sortedVaults := sortVaults(provider.vaults)
 	for _, vaultName := range sortedVaults {
-		vaults, err := provider.client.GetVaultsByTitle(vaultName)
+		vault, err := provider.client.GetVaultByTitle(vaultName)
 		if err != nil {
 			return nil, fmt.Errorf(errGetVault, err)
 		}
-		if len(vaults) != 1 {
-			return nil, fmt.Errorf(errExpectedOneVault, fmt.Errorf(incorrectCountFormat, vaultName, len(vaults)))
-		}
 
-		err = provider.getAllForVault(vaults[0].ID, ref, secretData)
+		err = provider.getAllForVault(vault.ID, ref, secretData)
 		if err != nil {
 			return nil, err
 		}
@@ -246,22 +242,19 @@ func (provider *ProviderOnePassword) Close(ctx context.Context) error {
 func (provider *ProviderOnePassword) findItem(name string) (*onepassword.Item, error) {
 	sortedVaults := sortVaults(provider.vaults)
 	for _, vaultName := range sortedVaults {
-		vaults, err := provider.client.GetVaultsByTitle(vaultName)
+		vault, err := provider.client.GetVaultByTitle(vaultName)
 		if err != nil {
 			return nil, fmt.Errorf(errGetVault, err)
 		}
-		if len(vaults) != 1 {
-			return nil, fmt.Errorf(errExpectedOneVault, fmt.Errorf(incorrectCountFormat, vaultName, len(vaults)))
-		}
 
 		// use GetItemsByTitle instead of GetItemByTitle in order to handle length cases
-		items, err := provider.client.GetItemsByTitle(name, vaults[0].ID)
+		items, err := provider.client.GetItemsByTitle(name, vault.ID)
 		if err != nil {
 			return nil, fmt.Errorf(errGetItem, err)
 		}
 		switch {
 		case len(items) == 1:
-			return provider.client.GetItem(items[0].ID, items[0].Vault.ID)
+			return provider.client.GetItemByUUID(items[0].ID, items[0].Vault.ID)
 		case len(items) > 1:
 			return nil, fmt.Errorf(errExpectedOneItem, fmt.Errorf(incorrectCountFormat, name, len(items)))
 		}
@@ -311,7 +304,7 @@ func (provider *ProviderOnePassword) getFields(item *onepassword.Item, property
 }
 
 func (provider *ProviderOnePassword) getAllFields(item onepassword.Item, ref esv1beta1.ExternalSecretFind, secretData map[string][]byte) error {
-	i, err := provider.client.GetItem(item.ID, item.Vault.ID)
+	i, err := provider.client.GetItemByUUID(item.ID, item.Vault.ID)
 	if err != nil {
 		return fmt.Errorf(errGetItem, err)
 	}

+ 1 - 1
pkg/provider/onepassword/onepassword_test.go

@@ -172,7 +172,7 @@ func TestFindItem(t *testing.T) {
 				{
 					checkNote:    "two vaults",
 					findItemName: myItem,
-					expectedErr:  fmt.Errorf(errExpectedOneVault, fmt.Errorf("'my-vault', got 2")),
+					expectedErr:  fmt.Errorf("key not found in 1Password Vaults: my-item in: map[my-shared-vault:2 my-vault:1]"),
 				},
 			},
 		},

+ 3 - 0
pkg/provider/vault/vault.go

@@ -485,6 +485,9 @@ func (v *client) listSecrets(ctx context.Context, path string) ([]string, error)
 		return nil, err
 	}
 	secret, err := v.logical.ListWithContext(ctx, url)
+	if secret == nil {
+		return nil, fmt.Errorf("provided path %v does not contain any secrets", url)
+	}
 	if err != nil {
 		return nil, fmt.Errorf(errReadSecret, err)
 	}

+ 49 - 0
pkg/utils/utils.go

@@ -24,6 +24,7 @@ import (
 	"net"
 	"net/url"
 	"reflect"
+	"regexp"
 	"strings"
 	"time"
 	"unicode"
@@ -40,6 +41,34 @@ func MergeByteMap(dst, src map[string][]byte) map[string][]byte {
 	return dst
 }
 
+func RewriteMap(operations []esv1beta1.ExternalSecretRewrite, in map[string][]byte) (map[string][]byte, error) {
+	out := in
+	var err error
+	for i, op := range operations {
+		if op.Regexp != nil {
+			out, err = RewriteRegexp(*op.Regexp, out)
+			if err != nil {
+				return nil, fmt.Errorf("failed rewriting operation[%v]: %w", i, err)
+			}
+		}
+	}
+	return out, nil
+}
+
+// RewriteRegexp rewrites a single Regexp Rewrite Operation.
+func RewriteRegexp(operation esv1beta1.ExternalSecretRewriteRegexp, in map[string][]byte) (map[string][]byte, error) {
+	out := make(map[string][]byte)
+	re, err := regexp.Compile(operation.Source)
+	if err != nil {
+		return nil, err
+	}
+	for key, value := range in {
+		newKey := re.ReplaceAllString(key, operation.Target)
+		out[newKey] = value
+	}
+	return out, nil
+}
+
 // DecodeValues decodes values from a secretMap.
 func DecodeMap(strategy esv1beta1.ExternalSecretDecodingStrategy, in map[string][]byte) (map[string][]byte, error) {
 	out := make(map[string][]byte, len(in))
@@ -69,6 +98,9 @@ func Decode(strategy esv1beta1.ExternalSecretDecodingStrategy, in []byte) ([]byt
 		return out, nil
 	case esv1beta1.ExternalSecretDecodeNone:
 		return in, nil
+	// default when stored version is v1alpha1
+	case "":
+		return in, nil
 	case esv1beta1.ExternalSecretDecodeAuto:
 		out, err := Decode(esv1beta1.ExternalSecretDecodeBase64, in)
 		if err != nil {
@@ -84,6 +116,21 @@ func Decode(strategy esv1beta1.ExternalSecretDecodingStrategy, in []byte) ([]byt
 	}
 }
 
+func ValidateKeys(in map[string][]byte) bool {
+	for key := range in {
+		for _, v := range key {
+			if !unicode.IsNumber(v) &&
+				!unicode.IsLetter(v) &&
+				v != '-' &&
+				v != '.' &&
+				v != '_' {
+				return false
+			}
+		}
+	}
+	return true
+}
+
 // ConvertKeys converts a secret map into a valid key.
 // Replaces any non-alphanumeric characters depending on convert strategy.
 func ConvertKeys(strategy esv1beta1.ExternalSecretConversionStrategy, in map[string][]byte) (map[string][]byte, error) {
@@ -112,6 +159,8 @@ func convert(strategy esv1beta1.ExternalSecretConversionStrategy, str string) st
 				newName[rk] = "_"
 			case esv1beta1.ExternalSecretConversionUnicode:
 				newName[rk] = fmt.Sprintf("_U%04x_", rv)
+			default:
+				newName[rk] = string(rv)
 			}
 		} else {
 			newName[rk] = string(rv)

+ 158 - 0
pkg/utils/utils_test.go

@@ -335,3 +335,161 @@ func TestValidate(t *testing.T) {
 		t.Errorf("Connection problem: %v", err)
 	}
 }
+
+func TestRewriteRegexp(t *testing.T) {
+	type args struct {
+		operations []esv1beta1.ExternalSecretRewrite
+		in         map[string][]byte
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    map[string][]byte
+		wantErr bool
+	}{
+		{
+			name: "replace of a single key",
+			args: args{
+				operations: []esv1beta1.ExternalSecretRewrite{
+					{
+						Regexp: &esv1beta1.ExternalSecretRewriteRegexp{
+							Source: "-",
+							Target: "_",
+						},
+					},
+				},
+				in: map[string][]byte{
+					"foo-bar": []byte("bar"),
+				},
+			},
+			want: map[string][]byte{
+				"foo_bar": []byte("bar"),
+			},
+		},
+		{
+			name: "no operation",
+			args: args{
+				operations: []esv1beta1.ExternalSecretRewrite{
+					{
+						Regexp: &esv1beta1.ExternalSecretRewriteRegexp{
+							Source: "hello",
+							Target: "world",
+						},
+					},
+				},
+				in: map[string][]byte{
+					"foo": []byte("bar"),
+				},
+			},
+			want: map[string][]byte{
+				"foo": []byte("bar"),
+			},
+		},
+		{
+			name: "removing prefix from keys",
+			args: args{
+				operations: []esv1beta1.ExternalSecretRewrite{
+					{
+						Regexp: &esv1beta1.ExternalSecretRewriteRegexp{
+							Source: "^my/initial/path/",
+							Target: "",
+						},
+					},
+				},
+				in: map[string][]byte{
+					"my/initial/path/foo": []byte("bar"),
+				},
+			},
+			want: map[string][]byte{
+				"foo": []byte("bar"),
+			},
+		},
+		{
+			name: "using un-named capture groups",
+			args: args{
+				operations: []esv1beta1.ExternalSecretRewrite{
+					{
+						Regexp: &esv1beta1.ExternalSecretRewriteRegexp{
+							Source: "f(.*)o",
+							Target: "a_new_path_$1",
+						},
+					},
+				},
+				in: map[string][]byte{
+					"foo":      []byte("bar"),
+					"foodaloo": []byte("barr"),
+				},
+			},
+			want: map[string][]byte{
+				"a_new_path_o":      []byte("bar"),
+				"a_new_path_oodalo": []byte("barr"),
+			},
+		},
+		{
+			name: "using named and numbered capture groups",
+			args: args{
+				operations: []esv1beta1.ExternalSecretRewrite{
+					{
+						Regexp: &esv1beta1.ExternalSecretRewriteRegexp{
+							Source: "f(?P<content>.*)o",
+							Target: "a_new_path_${content}_${1}",
+						},
+					},
+				},
+				in: map[string][]byte{
+					"foo":  []byte("bar"),
+					"floo": []byte("barr"),
+				},
+			},
+			want: map[string][]byte{
+				"a_new_path_o_o":   []byte("bar"),
+				"a_new_path_lo_lo": []byte("barr"),
+			},
+		},
+		{
+			name: "using sequenced rewrite operations",
+			args: args{
+				operations: []esv1beta1.ExternalSecretRewrite{
+					{
+						Regexp: &esv1beta1.ExternalSecretRewriteRegexp{
+							Source: "my/(.*?)/bar/(.*)",
+							Target: "$1-$2",
+						},
+					},
+					{
+						Regexp: &esv1beta1.ExternalSecretRewriteRegexp{
+							Source: "-",
+							Target: "_",
+						},
+					},
+					{
+						Regexp: &esv1beta1.ExternalSecretRewriteRegexp{
+							Source: "ass",
+							Target: "***",
+						},
+					},
+				},
+				in: map[string][]byte{
+					"my/app/bar/key":      []byte("bar"),
+					"my/app/bar/password": []byte("barr"),
+				},
+			},
+			want: map[string][]byte{
+				"app_key":      []byte("bar"),
+				"app_p***word": []byte("barr"),
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := RewriteMap(tt.args.operations, tt.args.in)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("RewriteMap() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("RewriteMap() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}