소스 검색

Merged main; updated kubernetes file

William Young 4 년 전
부모
커밋
b38b263e19

+ 1 - 0
ADOPTERS.md

@@ -14,6 +14,7 @@
 - [Pier Insurance](https://www.pier.digital/)
 - [Polarpoint](https://www.polarpoint.io/)
 - [Radio France](https://www.radiofrance.fr/)
+- [Amadeus](https://amadeus.com/)
 
 
 Countless others that can't disclose that information! :)

+ 1 - 14
apis/externalsecrets/v1beta1/secretstore_kubernetes_types.go

@@ -32,13 +32,6 @@ type KubernetesServer struct {
 	// see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider
 	// +optional
 	CAProvider *CAProvider `json:"caProvider,omitempty"`
-
-	// there's still room for impersonation or proxy settings:
-	// Impersonate-User
-	// Impersonate-Group
-	// Impersonate-Extra-( extra name )
-	// Impersonate-Uid
-	// Proxy Settings
 }
 
 // Configures a store to sync secrets with a Kubernetes instance.
@@ -68,9 +61,7 @@ type KubernetesAuth struct {
 
 	// points to a service account that should be used for authentication
 	// +optional
-	ServiceAccount *ServiceAccountAuth `json:"serviceAccount,omitempty"`
-
-	// possibly exec or webhook
+	ServiceAccount *esmeta.ServiceAccountSelector `json:"serviceAccount,omitempty"`
 }
 
 type CertAuth struct {
@@ -81,7 +72,3 @@ type CertAuth struct {
 type TokenAuth struct {
 	BearerToken esmeta.SecretKeySelector `json:"bearerToken,omitempty"`
 }
-
-type ServiceAccountAuth struct {
-	ServiceAccountRef esmeta.ServiceAccountSelector `json:"serviceAccount,omitempty"`
-}

+ 1 - 17
apis/externalsecrets/v1beta1/zz_generated.deepcopy.go

@@ -1099,7 +1099,7 @@ func (in *KubernetesAuth) DeepCopyInto(out *KubernetesAuth) {
 	}
 	if in.ServiceAccount != nil {
 		in, out := &in.ServiceAccount, &out.ServiceAccount
-		*out = new(ServiceAccountAuth)
+		*out = new(metav1.ServiceAccountSelector)
 		(*in).DeepCopyInto(*out)
 	}
 }
@@ -1576,22 +1576,6 @@ func (in *SenhaseguraProvider) DeepCopy() *SenhaseguraProvider {
 	return out
 }
 
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *ServiceAccountAuth) DeepCopyInto(out *ServiceAccountAuth) {
-	*out = *in
-	in.ServiceAccountRef.DeepCopyInto(&out.ServiceAccountRef)
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountAuth.
-func (in *ServiceAccountAuth) DeepCopy() *ServiceAccountAuth {
-	if in == nil {
-		return nil
-	}
-	out := new(ServiceAccountAuth)
-	in.DeepCopyInto(out)
-	return out
-}
-
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *TemplateFrom) DeepCopyInto(out *TemplateFrom) {
 	*out = *in

+ 11 - 16
config/crds/bases/external-secrets.io_clustersecretstores.yaml

@@ -2001,22 +2001,17 @@ spec:
                             description: points to a service account that should be
                               used for authentication
                             properties:
-                              serviceAccount:
-                                description: A reference to a ServiceAccount resource.
-                                properties:
-                                  name:
-                                    description: The name of the ServiceAccount resource
-                                      being referred to.
-                                    type: string
-                                  namespace:
-                                    description: Namespace of the resource being referred
-                                      to. Ignored if referent is not cluster-scoped.
-                                      cluster-scoped defaults to the namespace of
-                                      the referent.
-                                    type: string
-                                required:
-                                - name
-                                type: object
+                              name:
+                                description: The name of the ServiceAccount resource
+                                  being referred to.
+                                type: string
+                              namespace:
+                                description: Namespace of the resource being referred
+                                  to. Ignored if referent is not cluster-scoped. cluster-scoped
+                                  defaults to the namespace of the referent.
+                                type: string
+                            required:
+                            - name
                             type: object
                           token:
                             description: use static token to authenticate with

+ 11 - 16
config/crds/bases/external-secrets.io_secretstores.yaml

@@ -2001,22 +2001,17 @@ spec:
                             description: points to a service account that should be
                               used for authentication
                             properties:
-                              serviceAccount:
-                                description: A reference to a ServiceAccount resource.
-                                properties:
-                                  name:
-                                    description: The name of the ServiceAccount resource
-                                      being referred to.
-                                    type: string
-                                  namespace:
-                                    description: Namespace of the resource being referred
-                                      to. Ignored if referent is not cluster-scoped.
-                                      cluster-scoped defaults to the namespace of
-                                      the referent.
-                                    type: string
-                                required:
-                                - name
-                                type: object
+                              name:
+                                description: The name of the ServiceAccount resource
+                                  being referred to.
+                                type: string
+                              namespace:
+                                description: Namespace of the resource being referred
+                                  to. Ignored if referent is not cluster-scoped. cluster-scoped
+                                  defaults to the namespace of the referent.
+                                type: string
+                            required:
+                            - name
                             type: object
                           token:
                             description: use static token to authenticate with

+ 16 - 24
deploy/crds/bundle.yaml

@@ -1813,18 +1813,14 @@ spec:
                             serviceAccount:
                               description: points to a service account that should be used for authentication
                               properties:
-                                serviceAccount:
-                                  description: A reference to a ServiceAccount resource.
-                                  properties:
-                                    name:
-                                      description: The name of the ServiceAccount resource being referred to.
-                                      type: string
-                                    namespace:
-                                      description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent.
-                                      type: string
-                                  required:
-                                    - name
-                                  type: object
+                                name:
+                                  description: The name of the ServiceAccount resource being referred to.
+                                  type: string
+                                namespace:
+                                  description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent.
+                                  type: string
+                              required:
+                                - name
                               type: object
                             token:
                               description: use static token to authenticate with
@@ -4635,18 +4631,14 @@ spec:
                             serviceAccount:
                               description: points to a service account that should be used for authentication
                               properties:
-                                serviceAccount:
-                                  description: A reference to a ServiceAccount resource.
-                                  properties:
-                                    name:
-                                      description: The name of the ServiceAccount resource being referred to.
-                                      type: string
-                                    namespace:
-                                      description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent.
-                                      type: string
-                                  required:
-                                    - name
-                                  type: object
+                                name:
+                                  description: The name of the ServiceAccount resource being referred to.
+                                  type: string
+                                namespace:
+                                  description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent.
+                                  type: string
+                              required:
+                                - name
                               type: object
                             token:
                               description: use static token to authenticate with

+ 2 - 0
docs/guides-templating.md

@@ -93,6 +93,8 @@ In addition to that you can use over 200+ [sprig functions](http://masterminds.g
 
 | jwkPublicKeyPem | Takes an json-serialized JWK and returns an PEM block of type `PUBLIC KEY` that contains the public key. [See here](https://golang.org/pkg/crypto/x509/#MarshalPKIXPublicKey) for details. |
 | jwkPrivateKeyPem | Takes an json-serialized JWK as `string` and returns an PEM block of type `PRIVATE KEY` that contains the private key in PKCS #8 format. [See here](https://golang.org/pkg/crypto/x509/#MarshalPKCS8PrivateKey) for details. |
+| toYaml | Takes an interface, marshals it to yaml. It returns a string, even on marshal error (empty string). |
+| fromYaml | Function converts a YAML document into a map[string]interface{}. |
 
 ## Migrating from v1
 

+ 144 - 82
docs/provider-kubernetes.md

@@ -1,31 +1,66 @@
-External Secrets Operator allows to retrieve in-cluster secrets or from a remote Kubernetes Cluster.
+External Secrets Operator allows to retrieve secrets from a Kubernetes Cluster - this can be either a remote cluster or the local where the operator runs in.
 
-### Authentication
+A `SecretStore` points to a **specific namespace** in the target Kubernetes Cluster. You are able to retrieve all secrets from that particular namespace given you have the correct set of RBAC permissions.
 
-It's possible to authenticate against the Kubernetes API using client certificates or a bearer token. Authentication using a service account has not yet been implemented. The operator enforces that exactly one authentication method is used.
+The `SecretStore` reconciler checks if you have read access for secrets in that namespace using `SelfSubjectRulesReview`. See below on how to set that up properly.
 
-**NOTE:** `SelfSubjectAccessReview` permission is required for the service account in order to validation work properly.
+### External Secret Spec
 
-## Example
+This provider supports the use of the `Property` field. With it you point to the key of the remote secret. If you leave it empty it will json encode all key/value pairs.
 
-### In-cluster secrets using a Token
+```yaml
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+  name: example
+spec:
+  refreshInterval: 1h
+  secretStoreRef:
+    kind: SecretStore
+    name: example               # name of the SecretStore (or kind specified)
+  target:
+    name: secret-to-be-created  # name of the k8s Secret to be created
+  data:
+  - secretKey: extra
+    remoteRef:
+      key: secret-example
+      property: extra
+```
+
+#### find by tag & name
 
-1. Create a K8s Secret with a client token for the default service account
+You can fetch secrets based on labels or names matching a regexp:
 
 ```yaml
-apiVersion: v1
-kind: Secret
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
 metadata:
-  name: mydefaulttoken
-  annotations:
-    kubernetes.io/service-account.name: default
-type: kubernetes.io/service-account-token
+  name: example
+spec:
+  refreshInterval: 1h
+  secretStoreRef:
+    kind: SecretStore
+    name: example
+  target:
+    name: secret-to-be-created
+  dataFrom:
+  - find:
+      name:
+        # match secret name with regexp
+        regexp: "key-.*"
+  - find:
+      tags:
+        # fetch secrets based on label combination
+        app: "nginx"
 ```
-2. Create a SecretStore
 
-The Servers `url` won't be present as it will default to `kubernetes.default`, add a proper value if needed. In this example the Certificate Authority is fetched using the referenced `caProvider`.
+### Target API-Server Configuration
 
-The `auth` section indicates that the type `token` will be used for authentication, it includes the path to fetch the token. Set `remoteNamespace` to the name of the namespace where your target secrets reside.
+The servers `url` can be omitted and defaults to `kubernetes.default`. You **have to** provide a CA certificate in order to connect to the API Server securely.
+For your convenience, each namespace has a ConfigMap `kube-root-ca.crt` that contains the CA certificate of the internal API Server (see `RootCAConfigMap` [feature gate](https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/)).
+Use that if you want to connect to the same API server.
+If you want to connect to a remote API Server you need to fetch it and store it inside the cluster as ConfigMap or Secret.
+You may also define it inline as base64 encoded value using the `caBundle` property.
 
 ```yaml
 apiVersion: external-secrets.io/v1beta1
@@ -35,71 +70,91 @@ metadata:
 spec:
   provider:
     kubernetes:
+      remoteNamespace: default
       server:
+        url: "https://myapiserver.tld"
         caProvider:
-          type: Secret
-          name: mydefaulttoken
+          type: ConfigMap
+          name: kube-root-ca.crt
           key: ca.crt
-      auth:
-        token:
-          bearerToken:
-            name: mydefaulttoken
-            key: token
-      remoteNamespace: default
 ```
-3. Create the local secret that will be synced
+
+### Authentication
+
+It's possible to authenticate against the Kubernetes API using client certificates, a bearer token or service account. The operator enforces that exactly one authentication method is used. You can not use the service account that is mounted inside the operator, this is by design to avoid reading secrets across namespaces.
+
+**NOTE:** `SelfSubjectRulesReview` permission is required in order to validation work properly. Please use the following role as reference:
+
+```yaml
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  namespace: default
+  name: eso-store-role
+rules:
+- apiGroups: [""]
+  resources:
+  - secrets
+  verbs:
+  - get
+  - list
+  - watch
+- apiGroups:
+  - authorization.k8s.io
+  resources:
+  - selfsubjectrulesreviews
+  verbs:
+  - create
+```
+
+#### Authenticating with BearerToken
+
+Create a Kubernetes secret with a client token. There are many ways to acquire such a token, please refer to the [Kubernetes Authentication docs](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authentication-strategies).
 
 ```yaml
 apiVersion: v1
 kind: Secret
 metadata:
-  name: secret-example
+  name: mydefaulttoken
 data:
-  extra: YmFyCg==
+  token: "...."
 ```
-4. Finally create the ExternalSecret resource
+
+Create a SecretStore: The `auth` section indicates that the type `token` will be used for authentication, it includes the path to fetch the token. Set `remoteNamespace` to the name of the namespace where your target secrets reside.
 
 ```yaml
 apiVersion: external-secrets.io/v1beta1
-kind: ExternalSecret
+kind: SecretStore
 metadata:
   name: example
 spec:
-  refreshInterval: 1h
-  secretStoreRef:
-    kind: SecretStore
-    name: example               # name of the SecretStore (or kind specified)
-  target:
-    name: secret-to-be-created  # name of the k8s Secret to be created
-    creationPolicy: Owner
-  data:
-  - secretKey: extra
-    remoteRef:
-      key: secret-example
-      property: extra
+  provider:
+    kubernetes:
+      server:
+        # ...
+      auth:
+        token:
+          bearerToken:
+            name: mydefaulttoken
+            key: token
+      remoteNamespace: default
 ```
 
-### Remote Secret using a Token
+#### Authenticating with ServiceAccount
 
-1. Create a K8s Secret with the encoded base64 ca and client token.
+Create a Kubernetes Service Account, please refer to the [Service Account Tokens Documentation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#service-account-tokens) on how they work and how to create them.
 
-```yaml
-apiVersion: v1
-kind: Secret
-metadata:
-  name: cluster-secrets
-data:
-  # Fill with your encoded base64 CA
-  certificate-authority-data: Cg==
-stringData:
-  # Fill with your string Token
-  bearerToken: "my-token"
 ```
-2. Create a SecretStore
+$ kubectl create serviceaccount my-store
+```
 
-The Server section specifies the `url` of the remote Kubernetes API. In this example the Certificate Authority is fetch using the encoded base64 `caBundle`.
+This Service Account needs permissions to read `Secret` and create `SelfSubjectRulesReview` resources. Please see the above role.
+
+```
+$ kubectl create rolebinding my-store --role=eso-store-role --serviceaccount=default:my-store
+```
 
-The `auth` section indicates that the  `token` type will be used for authentication, it includes the path to fetch the token.
+Create a SecretStore: the `auth` section indicates that the type `serviceAccount` will be used for authentication.
 
 ```yaml
 apiVersion: external-secrets.io/v1beta1
@@ -109,37 +164,44 @@ metadata:
 spec:
   provider:
     kubernetes:
-      # If not remoteNamesapce is provided, default     namespace is used
-      remoteNamespace: remote-namespace
       server:
-        url: https://remote.kubernetes.api-server.address
-        # Add your encoded base64 to caBundle
-        caBundle: Cg==
+        # ...
       auth:
-        # Adds referenced bearerToken
-        token:
-          bearerToken:
-            name: cluster-secrets
-            key: bearerToken
+        serviceAccount:
+          name: "my-store"
+          namespace: "" # only ClusterSecretStore
+      remoteNamespace: default
+```
+
+#### Authenticating with Client Certificates
+
+Create a Kubernetes secret which contains the client key and certificate. See [Generate Certificates Documentations](https://kubernetes.io/docs/tasks/administer-cluster/certificates/) on how to create them.
+
 ```
-4. Finally create the ExternalSecret resource
+$ kubectl create secret tls tls-secret --cert=path/to/tls.cert --key=path/to/tls.key
+```
+
+Reference the `tls-secret` in the SecretStore
 
 ```yaml
 apiVersion: external-secrets.io/v1beta1
-kind: ExternalSecret
+kind: SecretStore
 metadata:
   name: example
 spec:
-  refreshInterval: 1h
-  secretStoreRef:
-    kind: SecretStore
-    name: example               # name of the SecretStore (or kind specified)
-  target:
-    name: secret-to-be-created  # name of the k8s Secret to be created
-    creationPolicy: Owner
-  data:
-  - secretKey: extra
-    remoteRef:
-      key: secret-remote-example
-      property: extra
-```
+  provider:
+    kubernetes:
+      server:
+        # ...
+      auth:
+        cert:
+          clientCert:
+            name: "tls-secret"
+            key: "tls.crt"
+            namespace: "foobar" # only ClusterSecretStore
+          clientKey:
+            name: "tls-secret"
+            key: "tls.key"
+            namespace: "foobar" # only ClusterSecretStore
+      remoteNamespace: default
+```

+ 1 - 31
docs/spec.md

@@ -2739,9 +2739,7 @@ TokenAuth
 <td>
 <code>serviceAccount</code></br>
 <em>
-<a href="#external-secrets.io/v1beta1.ServiceAccountAuth">
-ServiceAccountAuth
-</a>
+github.com/external-secrets/external-secrets/apis/meta/v1.ServiceAccountSelector
 </em>
 </td>
 <td>
@@ -3911,34 +3909,6 @@ bool
 </tr>
 </tbody>
 </table>
-<h3 id="external-secrets.io/v1beta1.ServiceAccountAuth">ServiceAccountAuth
-</h3>
-<p>
-(<em>Appears on:</em>
-<a href="#external-secrets.io/v1beta1.KubernetesAuth">KubernetesAuth</a>)
-</p>
-<p>
-</p>
-<table>
-<thead>
-<tr>
-<th>Field</th>
-<th>Description</th>
-</tr>
-</thead>
-<tbody>
-<tr>
-<td>
-<code>serviceAccount</code></br>
-<em>
-github.com/external-secrets/external-secrets/apis/meta/v1.ServiceAccountSelector
-</em>
-</td>
-<td>
-</td>
-</tr>
-</tbody>
-</table>
 <h3 id="external-secrets.io/v1beta1.TemplateEngineVersion">TemplateEngineVersion
 (<code>string</code> alias)</p></h3>
 <p>

+ 11 - 7
e2e/suites/provider/cases/azure/provider.go

@@ -15,6 +15,7 @@ package azure
 import (
 	"context"
 	"os"
+	"sync"
 	"time"
 
 	"github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault"
@@ -47,23 +48,26 @@ type azureProvider struct {
 func newazureProvider(f *framework.Framework, clientID, clientSecret, tenantID, vaultURL string) *azureProvider {
 	clientCredentialsConfig := kvauth.NewClientCredentialsConfig(clientID, clientSecret, tenantID)
 	clientCredentialsConfig.Resource = "https://vault.azure.net"
-	authorizer, err := clientCredentialsConfig.Authorizer()
-	if err != nil {
-		Fail(err.Error())
-	}
 	basicClient := keyvault.New()
-	basicClient.Authorizer = authorizer
-
 	prov := &azureProvider{
 		framework:    f,
+		client:       &basicClient,
 		clientID:     clientID,
 		clientSecret: clientSecret,
 		tenantID:     tenantID,
 		vaultURL:     vaultURL,
-		client:       &basicClient,
 	}
 
+	o := &sync.Once{}
 	BeforeEach(func() {
+		// run authorizor only if this spec is called
+		o.Do(func() {
+			authorizer, err := clientCredentialsConfig.Authorizer()
+			if err != nil {
+				Fail(err.Error())
+			}
+			prov.client.Authorizer = authorizer
+		})
 		prov.CreateSecretStore()
 	})
 

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

@@ -20,6 +20,7 @@ import (
 	_ "github.com/external-secrets/external-secrets/e2e/suites/provider/cases/aws/secretsmanager"
 	_ "github.com/external-secrets/external-secrets/e2e/suites/provider/cases/azure"
 	_ "github.com/external-secrets/external-secrets/e2e/suites/provider/cases/gcp"
+	_ "github.com/external-secrets/external-secrets/e2e/suites/provider/cases/kubernetes"
 	_ "github.com/external-secrets/external-secrets/e2e/suites/provider/cases/template"
 	_ "github.com/external-secrets/external-secrets/e2e/suites/provider/cases/vault"
 )

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

@@ -0,0 +1,135 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package kubernetes
+
+import (
+	"fmt"
+
+	// nolint
+	. "github.com/onsi/ginkgo/v2"
+	v1 "k8s.io/api/core/v1"
+
+	esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+	"github.com/external-secrets/external-secrets/e2e/framework"
+	"github.com/external-secrets/external-secrets/e2e/suites/provider/cases/common"
+)
+
+const referentAuth = "with referent auth"
+
+var _ = Describe("[kubernetes] ", Label("kubernetes"), func() {
+	f := framework.New("eso-kubernetes")
+	prov := NewProvider(f)
+
+	DescribeTable("sync secrets",
+		framework.TableFunc(f,
+			prov),
+		Entry(common.JSONDataWithProperty(f)),
+		Entry(common.JSONDataWithoutTargetName(f)),
+		Entry(common.JSONDataWithTemplate(f)),
+		Entry(common.DataPropertyDockerconfigJSON(f)),
+		Entry(common.SSHKeySyncDataProperty(f)),
+		Entry(common.JSONDataFromSync(f)),
+		Entry(FindByTag(f)),
+		Entry(FindByName(f)),
+
+		framework.Compose(referentAuth, f, common.JSONDataWithProperty, withReferentStore),
+		framework.Compose(referentAuth, f, common.JSONDataWithoutTargetName, withReferentStore),
+	)
+})
+
+func withReferentStore(tc *framework.TestCase) {
+	tc.ExternalSecret.Spec.SecretStoreRef.Name = referentStoreName(tc.Framework)
+	tc.ExternalSecret.Spec.SecretStoreRef.Kind = esapi.ClusterSecretStoreKind
+}
+
+const (
+	secretValue1 = "{\"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) {
+		const namePrefix = "e2e-find-name-%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")
+		secretValue := "{\"foo1\":\"foo1-val\"}"
+		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{
+				secretKeyOne:   []byte(secretValue),
+				secretKeyTwo:   []byte(secretValue),
+				secretKeyThree: []byte(secretValue),
+			},
+		}
+		tc.ExternalSecret.Spec.DataFrom = []esapi.ExternalSecretDataFromRemoteRef{
+			{
+				Find: &esapi.ExternalSecretFind{
+					Name: &esapi.FindName{
+						RegExp: fmt.Sprintf("e2e-find-name-%s.+", f.Namespace.Name),
+					},
+				},
+			},
+		}
+	}
+}
+
+// This case creates multiple secrets with simple key/value pairs and syncs them using multiple .Spec.Data blocks.
+func FindByTag(f *framework.Framework) (string, func(*framework.TestCase)) {
+	return "[common] should find secrets by tags using .DataFrom[]", func(tc *framework.TestCase) {
+		const namePrefix = "e2e-find-name-%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")
+		tc.Secrets = map[string]framework.SecretEntry{
+			secretKeyOne: {
+				Value: secretValue1,
+				Tags: map[string]string{
+					"test": f.Namespace.Name,
+				}},
+			secretKeyTwo: {
+				Value: secretValue1,
+				Tags: map[string]string{
+					"test": f.Namespace.Name,
+				}},
+			secretKeyThree: {
+				Value: secretValue1,
+				Tags: map[string]string{
+					"noop": f.Namespace.Name,
+				}},
+		}
+		tc.ExpectedSecret = &v1.Secret{
+			Type: v1.SecretTypeOpaque,
+			Data: map[string][]byte{
+				fmt.Sprintf("e2e-find-name-%s-one", f.Namespace.Name): []byte(secretValue1),
+				fmt.Sprintf("e2e-find-name-%s-two", f.Namespace.Name): []byte(secretValue1),
+			},
+		}
+		tc.ExternalSecret.Spec.DataFrom = []esapi.ExternalSecretDataFromRemoteRef{
+			{
+				Find: &esapi.ExternalSecretFind{
+					Tags: map[string]string{
+						"test": f.Namespace.Name,
+					},
+				},
+			},
+		}
+	}
+}

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

@@ -0,0 +1,190 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package kubernetes
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+
+	// nolint
+	. "github.com/onsi/ginkgo/v2"
+
+	// nolint
+	. "github.com/onsi/gomega"
+	v1 "k8s.io/api/core/v1"
+	rbac "k8s.io/api/rbac/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+	"github.com/external-secrets/external-secrets/e2e/framework"
+)
+
+type Provider struct {
+	framework *framework.Framework
+}
+
+func NewProvider(f *framework.Framework) *Provider {
+	prov := &Provider{
+		framework: f,
+	}
+	BeforeEach(prov.BeforeEach)
+	return prov
+}
+
+func (s *Provider) CreateSecret(key string, val framework.SecretEntry) {
+	secret := &v1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      key,
+			Namespace: s.framework.Namespace.Name,
+			Labels:    val.Tags,
+		},
+		Data: make(map[string][]byte),
+	}
+	stringMap := make(map[string]string)
+	err := json.Unmarshal([]byte(val.Value), &stringMap)
+	Expect(err).ToNot(HaveOccurred())
+
+	for k, v := range stringMap {
+		secret.Data[k] = []byte(v)
+	}
+	err = s.framework.CRClient.Create(context.Background(), secret)
+	Expect(err).ToNot(HaveOccurred())
+}
+
+func (s *Provider) BeforeEach() {
+	s.CreateStore()
+	s.CreateReferentStore()
+}
+
+func (s *Provider) DeleteSecret(key string) {
+	secret := &v1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      key,
+			Namespace: s.framework.Namespace.Name,
+		},
+	}
+	err := s.framework.CRClient.Delete(context.Background(), secret, &client.DeleteOptions{})
+	Expect(err).ToNot(HaveOccurred())
+}
+
+func makeDefaultStore(suffix, namespace string) (*rbac.Role, *rbac.RoleBinding, *esv1beta1.SecretStore) {
+	roleName := fmt.Sprintf("%s-%s", "allow-eso-secret-read", suffix)
+	role := &rbac.Role{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      roleName,
+			Namespace: namespace,
+		},
+		Rules: []rbac.PolicyRule{
+			{
+				APIGroups: []string{""},
+				Resources: []string{"secrets"},
+				Verbs:     []string{"get", "list", "watch", "create", "update", "delete", "patch"},
+			},
+			{
+				Verbs:     []string{"create"},
+				APIGroups: []string{"authorization.k8s.io"},
+				Resources: []string{"selfsubjectrulesreviews"},
+			},
+		},
+	}
+
+	rb := &rbac.RoleBinding{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      fmt.Sprintf("%s-%s", "eso-rb", suffix),
+			Namespace: namespace,
+		},
+		Subjects: []rbac.Subject{
+			{
+				Kind:      "ServiceAccount",
+				Name:      "default",
+				Namespace: namespace,
+			},
+		},
+		RoleRef: rbac.RoleRef{
+			Kind:     "Role",
+			Name:     roleName,
+			APIGroup: "rbac.authorization.k8s.io",
+		},
+	}
+
+	store := &esv1beta1.SecretStore{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      namespace,
+			Namespace: namespace,
+		},
+		Spec: esv1beta1.SecretStoreSpec{
+			Provider: &esv1beta1.SecretStoreProvider{
+				Kubernetes: &esv1beta1.KubernetesProvider{
+					Server: esv1beta1.KubernetesServer{
+						CAProvider: &esv1beta1.CAProvider{
+							Type: esv1beta1.CAProviderTypeConfigMap,
+							Name: "kube-root-ca.crt",
+							Key:  "ca.crt",
+						},
+					},
+					Auth: esv1beta1.KubernetesAuth{
+						ServiceAccount: &esmeta.ServiceAccountSelector{
+							Name: "default",
+						},
+					},
+					RemoteNamespace: namespace,
+				},
+			},
+		},
+	}
+
+	return role, rb, store
+}
+
+func (s *Provider) CreateStore() {
+	rb, role, store := makeDefaultStore("", s.framework.Namespace.Name)
+
+	err := s.framework.CRClient.Create(context.Background(), role)
+	Expect(err).ToNot(HaveOccurred())
+
+	err = s.framework.CRClient.Create(context.Background(), rb)
+	Expect(err).ToNot(HaveOccurred())
+
+	err = s.framework.CRClient.Create(context.Background(), store)
+	Expect(err).ToNot(HaveOccurred())
+}
+
+func (s *Provider) CreateReferentStore() {
+	rb, role, store := makeDefaultStore("referent", s.framework.Namespace.Name)
+	// ServiceAccount Namespace is not set, this will be inferred
+	// from the ExternalSecret
+	css := &esv1beta1.ClusterSecretStore{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: referentStoreName(s.framework),
+		},
+		Spec: store.Spec,
+	}
+	css.Spec.Provider.Kubernetes.Server.CAProvider.Namespace = &s.framework.Namespace.Name
+
+	err := s.framework.CRClient.Create(context.Background(), role)
+	Expect(err).ToNot(HaveOccurred())
+
+	err = s.framework.CRClient.Create(context.Background(), rb)
+	Expect(err).ToNot(HaveOccurred())
+
+	err = s.framework.CRClient.Create(context.Background(), css)
+	Expect(err).ToNot(HaveOccurred())
+}
+
+func referentStoreName(f *framework.Framework) string {
+	return fmt.Sprintf("%s-referent", f.Namespace.Name)
+}

+ 17 - 14
go.mod

@@ -40,7 +40,7 @@ require (
 	github.com/Azure/go-autorest/autorest v0.11.27
 	github.com/Azure/go-autorest/autorest/adal v0.9.19
 	github.com/Azure/go-autorest/autorest/azure/auth v0.5.11
-	github.com/AzureAD/microsoft-authentication-library-for-go v0.5.0
+	github.com/AzureAD/microsoft-authentication-library-for-go v0.5.1
 	github.com/IBM/go-sdk-core/v5 v5.10.0
 	github.com/IBM/secrets-manager-go-sdk v1.0.44
 	github.com/Masterminds/goutils v1.1.1 // indirect
@@ -49,8 +49,8 @@ require (
 	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.7
-	github.com/aliyun/alibaba-cloud-sdk-go v1.61.1628
-	github.com/aws/aws-sdk-go v1.44.28
+	github.com/aliyun/alibaba-cloud-sdk-go v1.61.1638
+	github.com/aws/aws-sdk-go v1.44.33
 	github.com/crossplane/crossplane-runtime v0.15.1
 	github.com/go-logr/logr v1.2.3
 	github.com/go-test/deep v1.0.4 // indirect
@@ -77,7 +77,7 @@ require (
 	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-20220507011949-2cf3adece122
+	golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
 	golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401
 	google.golang.org/api v0.82.0
 	google.golang.org/genproto v0.0.0-20220527130721-00d5c0f3be58
@@ -86,22 +86,23 @@ require (
 	grpc.go4.org v0.0.0-20170609214715-11d0a25b4919
 	k8s.io/api v0.24.0
 	k8s.io/apiextensions-apiserver v0.24.0
-	k8s.io/apimachinery v0.24.0
+	k8s.io/apimachinery v0.24.1
 	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.0.0-20210415151418-c5206de65a78
+	software.sslmate.com/src/go-pkcs12 v0.2.0
 )
 
 require github.com/1Password/connect-sdk-go v1.4.0
 
 require (
-	github.com/argoproj/argo-cd/v2 v2.3.3
+	github.com/argoproj/argo-cd/v2 v2.4.0
 	github.com/fluxcd/helm-controller/api v0.22.0
-	github.com/fluxcd/pkg/apis/meta v0.14.1
+	github.com/fluxcd/pkg/apis/meta v0.14.2
 	github.com/fluxcd/source-controller/api v0.24.1
 	github.com/maxbrunsfeld/counterfeiter/v6 v6.5.0
+	sigs.k8s.io/yaml v1.3.0
 )
 
 require (
@@ -116,10 +117,13 @@ require (
 	github.com/Azure/go-autorest/tracing v0.6.0 // indirect
 	github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd // indirect
 	github.com/Masterminds/semver/v3 v3.1.1 // indirect
+	github.com/Microsoft/go-winio v0.4.17 // indirect
 	github.com/PaesslerAG/gval v1.0.0 // indirect
+	github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
 	github.com/PuerkitoBio/purell v1.1.1 // indirect
 	github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
-	github.com/argoproj/gitops-engine v0.6.2 // indirect
+	github.com/acomagu/bufpipe v1.0.3 // indirect
+	github.com/argoproj/gitops-engine v0.7.0 // indirect
 	github.com/argoproj/pkg v0.11.1-0.20211203175135-36c59d8fafe0 // indirect
 	github.com/armon/go-metrics v0.3.10 // indirect
 	github.com/armon/go-radix v1.0.0 // indirect
@@ -148,8 +152,8 @@ require (
 	github.com/ghodss/yaml v1.0.0 // indirect
 	github.com/go-errors/errors v1.0.1 // indirect
 	github.com/go-git/gcfg v1.5.0 // indirect
-	github.com/go-git/go-billy/v5 v5.0.0 // indirect
-	github.com/go-git/go-git/v5 v5.2.0 // indirect
+	github.com/go-git/go-billy/v5 v5.3.1 // indirect
+	github.com/go-git/go-git/v5 v5.4.2 // indirect
 	github.com/go-logr/zapr v1.2.0 // indirect
 	github.com/go-openapi/errors v0.19.8 // indirect
 	github.com/go-openapi/jsonpointer v0.19.5 // indirect
@@ -204,7 +208,7 @@ require (
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
-	github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd // indirect
+	github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
 	github.com/klauspost/compress v1.13.6 // indirect
 	github.com/kylelemons/godebug v1.1.0 // indirect
 	github.com/leodido/go-urn v1.2.1 // indirect
@@ -257,7 +261,7 @@ require (
 	github.com/vmihailenco/go-tinylfu v0.2.1 // indirect
 	github.com/vmihailenco/msgpack/v5 v5.3.4 // indirect
 	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
-	github.com/xanzy/ssh-agent v0.2.1 // indirect
+	github.com/xanzy/ssh-agent v0.3.0 // indirect
 	github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect
 	go.mongodb.org/mongo-driver v1.7.5 // indirect
 	go.opencensus.io v0.23.0 // indirect
@@ -298,5 +302,4 @@ require (
 	sigs.k8s.io/kustomize/api v0.10.1 // indirect
 	sigs.k8s.io/kustomize/kyaml v0.13.0 // indirect
 	sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
-	sigs.k8s.io/yaml v1.3.0 // indirect
 )

+ 135 - 30
go.sum

@@ -27,6 +27,7 @@ cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aD
 cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
 cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
 cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
+cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM=
 cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
 cloud.google.com/go v0.100.2 h1:t9Iw5QH5v4XtlEQaCtUY7x6sCABps8sW0acw7e2WQ6Y=
 cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
@@ -45,6 +46,7 @@ cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLq
 cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
 cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
 cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
+cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
 cloud.google.com/go/iam v0.3.0 h1:exkAomrVUuzx9kWFI1wm3KI0uoDeUFPB4kKGzx6x+Gc=
 cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
 cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
@@ -58,6 +60,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
 cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
 cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
+code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
+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=
@@ -97,8 +101,8 @@ 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.0 h1:tEQkdQjWTZP3KvPmCoUnKUuVwmhwq8XF9TAhDjzXkEg=
-github.com/AzureAD/microsoft-authentication-library-for-go v0.5.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4=
+github.com/AzureAD/microsoft-authentication-library-for-go v0.5.1 h1:BWe8a+f/t+7KY7zH2mqygeUD0t8hNFXe08p1Pb3/jKE=
+github.com/AzureAD/microsoft-authentication-library-for-go v0.5.1/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=
@@ -129,7 +133,10 @@ github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZC
 github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
 github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8=
 github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
+github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
 github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
+github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
+github.com/Microsoft/go-winio v0.4.17 h1:iT12IBVClFevaf8PuVyi3UmZOVh4OqnaLxDTW2O6j3w=
 github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
 github.com/Microsoft/hcsshim v0.8.22/go.mod h1:91uVCVzvX2QD16sMCenoxxXo6L1wJnLMX2PSufFMtF0=
 github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
@@ -140,6 +147,9 @@ github.com/PaesslerAG/gval v1.0.0/go.mod h1:y/nm5yEyTeX6av0OfKJNp9rBNj2XrGhAf5+v
 github.com/PaesslerAG/jsonpath v0.1.0/go.mod h1:4BzmtoM/PI8fPO4aQGIusjGxGir2BzcV0grWtFzq1Y8=
 github.com/PaesslerAG/jsonpath v0.1.1 h1:c1/AToHQMVsduPAa4Vh6xp2U0evy4t8SWp8imEsylIk=
 github.com/PaesslerAG/jsonpath v0.1.1/go.mod h1:lVboNxFGal/VwW6d9JzIy56bUsYAP6tH/x80vjnCseY=
+github.com/PagerDuty/go-pagerduty v1.5.0/go.mod h1:txr8VbObXdk2RkqF+C2an4qWssdGY99fK26XYUDjh+4=
+github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ=
+github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
 github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
 github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
@@ -150,6 +160,8 @@ github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWX
 github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
 github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d/go.mod h1:WML6KOYjeU8N6YyusMjj2qRvaPNUEvrQvaxuFcMRFJY=
 github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
+github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
+github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
 github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
 github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
 github.com/ahmetb/gen-crd-api-reference-docs v0.3.0 h1:+XfOU14S4bGuwyvCijJwhhBIjYN+YXS18jrCY2EzJaY=
@@ -159,8 +171,6 @@ github.com/akeylesslabs/akeyless-go-cloud-id v0.3.4 h1:vTckjyBhHOBiOWSC/oaEU2Oo4
 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.7 h1:/sSA9bn+QpeUORcHQdSvy6ErqMxD/7FpWHzfNLl/NIk=
 github.com/akeylesslabs/akeyless-go/v2 v2.16.7/go.mod h1:uOdXD49NCCe4rexeSc2aBU5Qv4KZgJE6YlbtYalvb+I=
-github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
-github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
 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=
@@ -170,9 +180,10 @@ github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZp
 github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
 github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j82Yjf3KFv7ApYzUI=
 github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
+github.com/alicebob/miniredis/v2 v2.14.2 h1:VeoqKUAsJfT2af61nDE7qhBzqn3J6xjnt9MFAbdrEtg=
 github.com/alicebob/miniredis/v2 v2.14.2/go.mod h1:gquAfGbzn92jvtrSC69+6zZnwSODVXVpYDRaGhWaL6I=
-github.com/aliyun/alibaba-cloud-sdk-go v1.61.1628 h1:RlAuuoF9NsnxoG+jZGnsK+GNyDGwiwPWdJuUQ0eyabo=
-github.com/aliyun/alibaba-cloud-sdk-go v1.61.1628/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
+github.com/aliyun/alibaba-cloud-sdk-go v1.61.1638 h1:Z0fWGbf/Bf8EFnANKzX+sFl7OPb3MdLHes7GzDgSgGQ=
+github.com/aliyun/alibaba-cloud-sdk-go v1.61.1638/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
 github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
@@ -182,11 +193,11 @@ github.com/antonmedv/expr v1.8.9/go.mod h1:5qsM3oLGDND7sDmQGDXHkYfkjYMUX14qsgqmH
 github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
 github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
 github.com/appscode/go v0.0.0-20190808133642-1d4ef1f1c1e0/go.mod h1:iy07dV61Z7QQdCKJCIvUoDL21u6AIceRhZzyleh2ymc=
-github.com/argoproj/argo-cd/v2 v2.3.3 h1:SE1Cb8MDqP5T2iUtyL9bwpKYIvpGkVUsgzKCAqZZ5Vc=
-github.com/argoproj/argo-cd/v2 v2.3.3/go.mod h1:EuPH0/rLe/FdHGwQ/EVucspMnF3v4qd7da6N6CG+oJA=
-github.com/argoproj/gitops-engine v0.6.2 h1:hM+pQeplCeIPAvfAmr1f91+ykxqaU0GAzuxVujqlKHM=
-github.com/argoproj/gitops-engine v0.6.2/go.mod h1:pRgVpLW7pZqf7n3COJ7UcDepk4cI61LAcJd64Q3Jq/c=
-github.com/argoproj/notifications-engine v0.3.1-0.20220127183449-91deed20b998/go.mod h1:5mKv7zEgI3NO0L+fsuRSwBSY9EIXSuyIsDND8O8TTIw=
+github.com/argoproj/argo-cd/v2 v2.4.0 h1:p0dlrY0yIj7K7K/QtLPvJuNsDYIiXyWxUbou0E9P16k=
+github.com/argoproj/argo-cd/v2 v2.4.0/go.mod h1:v/qJlqOYdszqe7WtNSKOppNy3AhRKa9cHSGFw6Pg7lg=
+github.com/argoproj/gitops-engine v0.7.0 h1:X6W8VP9bWTe74wWxAV3i8KZ0yBmre5DU8g+GWH09FCo=
+github.com/argoproj/gitops-engine v0.7.0/go.mod h1:pRgVpLW7pZqf7n3COJ7UcDepk4cI61LAcJd64Q3Jq/c=
+github.com/argoproj/notifications-engine v0.3.1-0.20220430155844-567361917320/go.mod h1:R3zlopt+/juYlebQc9Jarn9vBQ2xZruWOWjUNkfGY9M=
 github.com/argoproj/pkg v0.11.1-0.20211203175135-36c59d8fafe0 h1:Cfp7rO/HpVxnwlRqJe0jHiBbZ77ZgXhB6HWlYD02Xdc=
 github.com/argoproj/pkg v0.11.1-0.20211203175135-36c59d8fafe0/go.mod h1:ra+bQPmbVAoEL+gYSKesuigt4m49i3Qa3mE/xQcjCiA=
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
@@ -213,8 +224,8 @@ github.com/aws/aws-sdk-go v1.33.16/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZve
 github.com/aws/aws-sdk-go v1.35.24/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k=
 github.com/aws/aws-sdk-go v1.38.49/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
 github.com/aws/aws-sdk-go v1.41.13/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
-github.com/aws/aws-sdk-go v1.44.28 h1:h/OAqEqY18wq//v6h4GNPMmCkxuzSDrWuGyrvSiRqf4=
-github.com/aws/aws-sdk-go v1.44.28/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
+github.com/aws/aws-sdk-go v1.44.33 h1:OoLO99CdssiyOISnZknsQfIqESOyuMgy7pLrPW7RLKg=
+github.com/aws/aws-sdk-go v1.44.33/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
 github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
 github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg=
 github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
@@ -246,7 +257,9 @@ github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QH
 github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
 github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
 github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
+github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
 github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
 github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
@@ -278,6 +291,7 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH
 github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
 github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
 github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
@@ -368,8 +382,10 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
 github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
 github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
+github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=
 github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws=
 github.com/euank/go-kmsg-parser v2.0.0+incompatible/go.mod h1:MhmAMZ8V4CYH4ybgdRwPr2TU5ThnS43puaKEMpja1uw=
 github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
 github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
@@ -384,6 +400,7 @@ github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+ne
 github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
 github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
 github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
 github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
 github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
@@ -399,8 +416,9 @@ github.com/fluxcd/pkg/apis/acl v0.0.3/go.mod h1:XPts6lRJ9C9fIF9xVWofmQwftvhY25n1
 github.com/fluxcd/pkg/apis/kustomize v0.4.1 h1:YgIF9TJ23pH66W/gYlEu+DeH1pU3tS4xYlRc5AQzk58=
 github.com/fluxcd/pkg/apis/kustomize v0.4.1/go.mod h1:U9rfSgDHaQd74PgPKt9DprtuzT+i1m18zlHxatq7c5Y=
 github.com/fluxcd/pkg/apis/meta v0.12.2/go.mod h1:Z26X5uTU5LxAyWETGueRQY7TvdPaGfKU7Wye9bdUlho=
-github.com/fluxcd/pkg/apis/meta v0.14.1 h1:lPDs9yV67DnwalHPb13bbnDkAatALfUiAMRHjUm4UBw=
 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.24.1 h1:bFpfajE09k/xNIWkgNZtmtPrw/dS0zWUYMYxUUWu+jI=
 github.com/fluxcd/source-controller/api v0.24.1/go.mod h1:+raHSQaSGlk1PqgLc0joVk1KTnf1K/lQcgxdEdZ/mk8=
 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
@@ -423,6 +441,7 @@ github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo
 github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
 github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
 github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
+github.com/gfleury/go-bitbucket-v1 v0.0.0-20220301131131-8e7ed04b843e/go.mod h1:LB3osS9X2JMYmTzcCArHHLrndBAfcVLQAvUddfs+ONs=
 github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
@@ -437,12 +456,13 @@ github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6
 github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
 github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
 github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
-github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
-github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
-github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M=
-github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
-github.com/go-git/go-git/v5 v5.2.0 h1:YPBLG/3UK1we1ohRkncLjaXWLW+HKp5QNM/jTli2JgI=
-github.com/go-git/go-git/v5 v5.2.0/go.mod h1:kh02eMX+wdqqxgNMEyq8YgwlIOsDOa9homkUq1PoTMs=
+github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
+github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34=
+github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
+github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8=
+github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
+github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4=
+github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -456,13 +476,13 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
 github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
 github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
 github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
-github.com/go-logr/logr v0.3.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
 github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
 github.com/go-logr/logr v1.0.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
 github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/go-logr/zapr v0.4.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
 github.com/go-logr/zapr v1.2.0 h1:n4JnPI1T3Qq1SFEi/F8rwLrZERp2bso19PJZDB9dayk=
 github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro=
@@ -584,6 +604,7 @@ github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71
 github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
 github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
 github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
+github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -632,6 +653,7 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
 github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-github/v35 v35.3.0/go.mod h1:yWB7uCcVWaUbUP74Aq3whuMySRMatyRmq5U9FTNlbio=
 github.com/google/go-github/v41 v41.0.0 h1:HseJrM2JFf2vfiZJ8anY2hqBjdfY1Vlj/K27ueww4gg=
 github.com/google/go-github/v41 v41.0.0/go.mod h1:XgmCA5H323A9rtgExdTcnDkcqp6S30AVACCBDOonIxg=
 github.com/google/go-jsonnet v0.18.0/go.mod h1:C3fTzyVJDslXdiTqw/bTFk7vSGyCtH3MGRbDfvEwGd0=
@@ -706,10 +728,13 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
 github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw=
 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
 github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
 github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
+github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
 github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
 github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
 github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
 github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -719,8 +744,10 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n
 github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
 github.com/hashicorp/go-getter v1.4.0/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY=
 github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
+github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
 github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
 github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
+github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
 github.com/hashicorp/go-hclog v1.1.0 h1:QsGcniKx5/LuX2eYoeL+Np3UKYPNaN7YKpTh29h8rbw=
 github.com/hashicorp/go-hclog v1.1.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
 github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
@@ -729,6 +756,7 @@ github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjh
 github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g=
 github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
 github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
 github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
 github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
 github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM=
@@ -736,6 +764,7 @@ github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ3
 github.com/hashicorp/go-retryablehttp v0.5.1/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
 github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
 github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
+github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
 github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
 github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ=
 github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
@@ -765,6 +794,7 @@ github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2I
 github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
 github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 github.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4=
 github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
@@ -777,8 +807,14 @@ github.com/hashicorp/hcl v1.0.1-vault-3 h1:V95v5KSTu6DB5huDSKiq4uAfILEuNigK/+qPE
 github.com/hashicorp/hcl v1.0.1-vault-3/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=
 github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
 github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
+github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
+github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
 github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
+github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
 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.5.0 h1:Bp6yc2bn7CWkOrVIzFT/Qurzx528bdavF3nz590eu28=
@@ -804,11 +840,11 @@ github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq
 github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
 github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
 github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
+github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
 github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
 github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
@@ -823,7 +859,9 @@ github.com/itchyny/timefmt-go v0.1.2/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrn
 github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
+github.com/jeremywohl/flatten v1.0.1/go.mod h1:4AmD/VxjWcI5SRB0n6szE2A6s2fsNHDLO0nAlMHgfLQ=
 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
 github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
 github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
 github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
@@ -858,11 +896,13 @@ github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0t
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
 github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
+github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
+github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
 github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
-github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
-github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
+github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
+github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
@@ -885,6 +925,7 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/ktrysmt/go-bitbucket v0.9.40/go.mod h1:FWxy2UK7GlK5b0NSJGc5hPqnssVlkNnsChvyuOf/Xno=
 github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
 github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
@@ -913,6 +954,7 @@ github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1
 github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
 github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5/go.mod h1:c2mYKRyMb1BPkO5St0c/ps62L4S0W2NAkaTXj9qEI+0=
 github.com/lusis/slack-test v0.0.0-20190426140909-c40012f20018/go.mod h1:sFlOUpQL1YcjhFVXhg1CG8ZASEs/Mf1oVb6H75JL/zg=
+github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
 github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
@@ -926,6 +968,8 @@ github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7
 github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
 github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 github.com/malexdev/utfutil v0.0.0-20180510171754-00c8d4a8e7a8/go.mod h1:UtpLyb/EupVKXF/N0b4NRe1DNg+QYJsnsHQ038romhM=
+github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
+github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@@ -939,6 +983,7 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
 github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
+github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
@@ -955,12 +1000,15 @@ github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182aff
 github.com/maxbrunsfeld/counterfeiter/v6 v6.5.0 h1:rBhB9Rls+yb8kA4x5a/cWxOufWfXt24E+kq4YlbGj3g=
 github.com/maxbrunsfeld/counterfeiter/v6 v6.5.0/go.mod h1:fJ0UAZc1fx3xZhU4eSHQDJ1ApFmTVhp5VTpV9tm2ogg=
 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
+github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
 github.com/mindprince/gonvml v0.0.0-20190828220739-9ebdce4bb989/go.mod h1:2eu9pRWp8mo84xCg6KswZ+USQHjwgRhNp06sozOdsTY=
 github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
 github.com/minio/minio-go/v7 v7.0.2/go.mod h1:dJ80Mv2HeGkYLH1sqS/ksz07ON6csH3S6JUMSQ2zAns=
 github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
 github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
 github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
 github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
 github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
 github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
@@ -976,6 +1024,7 @@ github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUb
 github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
 github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
@@ -1096,6 +1145,7 @@ github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTK
 github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
+github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
 github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
 github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
 github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
@@ -1115,6 +1165,7 @@ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
 github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
 github.com/pquerna/cachecontrol v0.0.0-20180306154005-525d0eb5f91d/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@@ -1182,6 +1233,7 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
 github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
 github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
+github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
 github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
 github.com/sanity-io/litter v1.2.0/go.mod h1:JF6pZUFgu2Q0sBZ+HSV35P8TVPI1TTzEwyu9FXAw2W4=
 github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
@@ -1217,6 +1269,7 @@ github.com/sony/sonyflake v1.0.0/go.mod h1:Jv3cfhf/UFtolOTTRd3q4Nl6ENqM+KfyZ5Pse
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
 github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
@@ -1227,6 +1280,7 @@ github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tL
 github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
 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/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
@@ -1240,6 +1294,7 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM
 github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
 github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
 github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
+github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
 github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
 github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
 github.com/storageos/go-api v2.2.0+incompatible/go.mod h1:ZrLn+e0ZuF3Y65PNF6dIwbJPZqfmtCXxFm9ckv0agOY=
@@ -1281,11 +1336,12 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
 github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
-github.com/undefinedlabs/go-mpatch v1.0.6/go.mod h1:TyJZDQ/5AgyN7FSLiBJ8RO9u2c6wbtRvK827b6AVqY4=
 github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
 github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
 github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
 github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
 github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
@@ -1298,10 +1354,11 @@ github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAh
 github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
 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.0 h1:b2iMQHgZ1V+NyRqLRJVv6RFfr4xnd/AASeS/PETYL0Y=
 github.com/xanzy/go-gitlab v0.68.0/go.mod h1:o4yExCtdaqlM8YGdDJWuZoBmfxBsmA9TPEjs9mx1UO4=
-github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
-github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
+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=
 github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
 github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
@@ -1330,8 +1387,11 @@ go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
 go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
 go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
+go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
 go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
+go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
 go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
+go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs=
 go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=
 go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE=
 go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc=
@@ -1353,16 +1413,26 @@ go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
 go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
 go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc=
 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.31.0/go.mod h1:SY9qHHUES6W3oZnO1H2W8NvsSovIoXRg/A1AH9px8+I=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4=
 go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
+go.opentelemetry.io/otel v1.6.1/go.mod h1:blzUabWHkX6LJewxvadmzafgh/wnvBSDBdOuwkAtrWQ=
+go.opentelemetry.io/otel v1.6.3/go.mod h1:7BgNga5fNlF/iZjG06hM3yofffp0ofKCDwSXx1GC4dI=
 go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM=
+go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.6.3/go.mod h1:NEu79Xo32iVb+0gVNV8PMd7GoWqnyDXRlj04yFjqz40=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.6.3/go.mod h1:UJmXdiVVBaZ63umRUTwJuCMAV//GCMvDiQwn703/GoY=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.6.3/go.mod h1:ycItY/esVj8c0dKgYTOztTERXtPzcfDU/0o8EdwCjoA=
 go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
 go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
 go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=
+go.opentelemetry.io/otel/sdk v1.6.3/go.mod h1:A4iWF7HTXa+GWL/AaqESz28VuSBIcZ+0CV+IzJ5NMiQ=
 go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE=
 go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE=
 go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
+go.opentelemetry.io/otel/trace v1.6.1/go.mod h1:RkFRM1m0puWIq10oxImnGEduNBzxiN7TXluRBtE+5j0=
+go.opentelemetry.io/otel/trace v1.6.3/go.mod h1:GNJQusJlUgZl9/TQBPKU/Y/ty+0iVB5fjhKeJGZPGFs=
 go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
+go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
 go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc=
 go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
 go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@@ -1407,19 +1477,23 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 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=
 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/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-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 h1:NvGWuYG8dkDHFSKksI1P9faiVJ9rayE6l0+ouWVIDs8=
-golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122/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-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
+golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/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=
@@ -1467,9 +1541,11 @@ golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hM
 golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
 golang.org/x/mod v0.5.1-0.20210830214625-1b1db11ec8f4/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
 golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
 golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
+golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1496,6 +1572,7 @@ golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -1522,12 +1599,15 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v
 golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
+golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
 golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
 golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@@ -1541,7 +1621,9 @@ golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su
 golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220526153639-5463443f8c37 h1:lUkvobShwKsOesNfWWlCS5q7fnbG1MEliIzwu886fn8=
 golang.org/x/net v0.0.0-20220526153639-5463443f8c37/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1559,6 +1641,7 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ
 golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
 golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
@@ -1578,6 +1661,7 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4=
 golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1591,7 +1675,6 @@ golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1609,6 +1692,8 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1625,6 +1710,7 @@ golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1654,6 +1740,7 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210301091718-77cc2087c03b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1662,7 +1749,9 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210412220455-f1c623a9e750/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1674,13 +1763,16 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1745,6 +1837,7 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw
 golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -1770,6 +1863,7 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK
 golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
 golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
 golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
 golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
@@ -1847,7 +1941,9 @@ google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6
 google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
 google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
 google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
+google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU=
 google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
+google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw=
 google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
 google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
 google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
@@ -1857,8 +1953,10 @@ 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.82.0 h1:h6EGeZuzhoKSS7BUznzkW+2wHZ+4Ubd6rsVvvh3dRkw=
 google.golang.org/api v0.82.0/go.mod h1:Ld58BeTlL9DIYr2M2ajvoSqmGLei0BMn+kVBmkam1os=
+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=
+google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
@@ -1930,8 +2028,12 @@ google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEc
 google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
 google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
 google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20211021150943-2b146023228c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
@@ -1984,6 +2086,7 @@ google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD
 google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
 google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
 google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
+google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
 google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
 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=
@@ -2180,4 +2283,6 @@ 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.0.0-20210415151418-c5206de65a78 h1:SqYE5+A2qvRhErbsXFfUEUmpWEKxxRSMgGLkvRAFOV4=
 software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78/go.mod h1:B7Wf0Ya4DHF9Yw+qfZuJijQYkWicqDa+79Ytmmq3Kjg=
+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=
 sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=

+ 195 - 0
pkg/provider/kubernetes/auth.go

@@ -0,0 +1,195 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package kubernetes
+
+import (
+	"context"
+	"fmt"
+
+	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/types"
+
+	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+)
+
+const (
+	errInvalidClusterStoreMissingNamespace = "missing namespace"
+	errFetchCredentials                    = "could not fetch credentials: %w"
+	errMissingCredentials                  = "missing credentials: \"%s\""
+	errEmptyKey                            = "key %s found but empty"
+	errGetKubeSA                           = "cannot get Kubernetes service account %q: %w"
+	errGetKubeSASecrets                    = "cannot find secrets bound to service account: %q"
+	errGetKubeSANoToken                    = "cannot find token in secrets bound to service account: %q"
+)
+
+func (k *BaseClient) setAuth(ctx context.Context) error {
+	err := k.setCA(ctx)
+	if err != nil {
+		return err
+	}
+	if k.store.Auth.Token != nil {
+		k.BearerToken, err = k.fetchSecretKey(ctx, k.store.Auth.Token.BearerToken)
+		if err != nil {
+			return fmt.Errorf("could not fetch Auth.Token.BearerToken: %w", err)
+		}
+		return nil
+	}
+	if k.store.Auth.ServiceAccount != nil {
+		k.BearerToken, err = k.secretKeyRefForServiceAccount(ctx, k.store.Auth.ServiceAccount)
+		if err != nil {
+			return fmt.Errorf("could not fetch Auth.ServiceAccount: %w", err)
+		}
+		return nil
+	}
+	if k.store.Auth.Cert != nil {
+		return k.setClientCert(ctx)
+	}
+	return fmt.Errorf("no credentials provided")
+}
+
+func (k *BaseClient) setCA(ctx context.Context) error {
+	if k.store.Server.CABundle != nil {
+		k.CA = k.store.Server.CABundle
+		return nil
+	}
+	if k.store.Server.CAProvider != nil {
+		var ca []byte
+		var err error
+		switch k.store.Server.CAProvider.Type {
+		case esv1beta1.CAProviderTypeConfigMap:
+			keySelector := esmeta.SecretKeySelector{
+				Name:      k.store.Server.CAProvider.Name,
+				Namespace: k.store.Server.CAProvider.Namespace,
+				Key:       k.store.Server.CAProvider.Key,
+			}
+			ca, err = k.fetchConfigMapKey(ctx, keySelector)
+			if err != nil {
+				return fmt.Errorf("unable to fetch Server.CAProvider ConfigMap: %w", err)
+			}
+		case esv1beta1.CAProviderTypeSecret:
+			keySelector := esmeta.SecretKeySelector{
+				Name:      k.store.Server.CAProvider.Name,
+				Namespace: k.store.Server.CAProvider.Namespace,
+				Key:       k.store.Server.CAProvider.Key,
+			}
+			ca, err = k.fetchSecretKey(ctx, keySelector)
+			if err != nil {
+				return fmt.Errorf("unable to fetch Server.CAProvider Secret: %w", err)
+			}
+		}
+		k.CA = ca
+		return nil
+	}
+	return fmt.Errorf("no Certificate Authority provided")
+}
+
+func (k *BaseClient) setClientCert(ctx context.Context) error {
+	var err error
+	k.Certificate, err = k.fetchSecretKey(ctx, k.store.Auth.Cert.ClientCert)
+	if err != nil {
+		return fmt.Errorf("unable to fetch client certificate: %w", err)
+	}
+	k.Key, err = k.fetchSecretKey(ctx, k.store.Auth.Cert.ClientKey)
+	if err != nil {
+		return fmt.Errorf("unable to fetch client key: %w", err)
+	}
+	return nil
+}
+
+func (k *BaseClient) secretKeyRefForServiceAccount(ctx context.Context, serviceAccountRef *esmeta.ServiceAccountSelector) ([]byte, error) {
+	serviceAccount := &corev1.ServiceAccount{}
+	ref := types.NamespacedName{
+		Namespace: k.namespace,
+		Name:      serviceAccountRef.Name,
+	}
+	if (k.storeKind == esv1beta1.ClusterSecretStoreKind) &&
+		(serviceAccountRef.Namespace != nil) {
+		ref.Namespace = *serviceAccountRef.Namespace
+	}
+	err := k.kube.Get(ctx, ref, serviceAccount)
+	if err != nil {
+		return nil, fmt.Errorf(errGetKubeSA, ref.Name, err)
+	}
+	if len(serviceAccount.Secrets) == 0 {
+		return nil, fmt.Errorf(errGetKubeSASecrets, ref.Name)
+	}
+	for _, tokenRef := range serviceAccount.Secrets {
+		retval, err := k.fetchSecretKey(ctx, esmeta.SecretKeySelector{
+			Name:      tokenRef.Name,
+			Namespace: &ref.Namespace,
+			Key:       "token",
+		})
+		if err != nil {
+			continue
+		}
+		return retval, nil
+	}
+	return nil, fmt.Errorf(errGetKubeSANoToken, ref.Name)
+}
+
+func (k *BaseClient) fetchSecretKey(ctx context.Context, key esmeta.SecretKeySelector) ([]byte, error) {
+	keySecret := &corev1.Secret{}
+	objectKey := types.NamespacedName{
+		Name:      key.Name,
+		Namespace: k.namespace,
+	}
+	// only ClusterStore is allowed to set namespace (and then it's required)
+	if k.storeKind == esv1beta1.ClusterSecretStoreKind {
+		if key.Namespace == nil {
+			return nil, fmt.Errorf(errInvalidClusterStoreMissingNamespace)
+		}
+		objectKey.Namespace = *key.Namespace
+	}
+	err := k.kube.Get(ctx, objectKey, keySecret)
+	if err != nil {
+		return nil, fmt.Errorf(errFetchCredentials, err)
+	}
+	val, ok := keySecret.Data[key.Key]
+	if !ok {
+		return nil, fmt.Errorf(errMissingCredentials, key.Key)
+	}
+	if len(val) == 0 {
+		return nil, fmt.Errorf(errEmptyKey, key.Key)
+	}
+	return val, nil
+}
+
+func (k *BaseClient) fetchConfigMapKey(ctx context.Context, key esmeta.SecretKeySelector) ([]byte, error) {
+	configMap := &corev1.ConfigMap{}
+	objectKey := types.NamespacedName{
+		Name:      key.Name,
+		Namespace: k.namespace,
+	}
+	// only ClusterStore is allowed to set namespace (and then it's required)
+	if k.storeKind == esv1beta1.ClusterSecretStoreKind {
+		if key.Namespace == nil {
+			return nil, fmt.Errorf(errInvalidClusterStoreMissingNamespace)
+		}
+		objectKey.Namespace = *key.Namespace
+	}
+	err := k.kube.Get(ctx, objectKey, configMap)
+	if err != nil {
+		return nil, fmt.Errorf(errFetchCredentials, err)
+	}
+	val, ok := configMap.Data[key.Key]
+	if !ok {
+		return nil, fmt.Errorf(errMissingCredentials, key.Key)
+	}
+	if val == "" {
+		return nil, fmt.Errorf(errEmptyKey, key.Key)
+	}
+	return []byte(val), nil
+}

+ 265 - 0
pkg/provider/kubernetes/auth_test.go

@@ -0,0 +1,265 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package kubernetes
+
+import (
+	"context"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/utils/pointer"
+	kclient "sigs.k8s.io/controller-runtime/pkg/client"
+	fclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
+
+	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+	v1 "github.com/external-secrets/external-secrets/apis/meta/v1"
+)
+
+func TestSetAuth(t *testing.T) {
+	type fields struct {
+		kube      kclient.Client
+		store     *esv1beta1.KubernetesProvider
+		namespace string
+		storeKind string
+	}
+	type want struct {
+		Certificate []byte
+		Key         []byte
+		CA          []byte
+		BearerToken []byte
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		want    want
+		wantErr bool
+	}{
+		{
+			name: "should return err if no ca provided",
+			fields: fields{
+				store: &esv1beta1.KubernetesProvider{
+					Server: esv1beta1.KubernetesServer{},
+				},
+			},
+			want:    want{},
+			wantErr: true,
+		},
+		{
+			name: "should return err if no auth provided",
+			fields: fields{
+				store: &esv1beta1.KubernetesProvider{
+					Server: esv1beta1.KubernetesServer{
+						CABundle: []byte("1234"),
+					},
+				},
+			},
+			want: want{
+				CA: []byte("1234"),
+			},
+			wantErr: true,
+		},
+		{
+			name: "should fetch ca from Secret",
+			fields: fields{
+				namespace: "default",
+				kube: fclient.NewClientBuilder().WithObjects(&corev1.Secret{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "foobar",
+						Namespace: "default",
+					},
+					Data: map[string][]byte{
+						"cert": []byte("1234"),
+					},
+				}).Build(),
+				store: &esv1beta1.KubernetesProvider{
+					Server: esv1beta1.KubernetesServer{
+						CAProvider: &esv1beta1.CAProvider{
+							Type: esv1beta1.CAProviderTypeSecret,
+							Name: "foobar",
+							Key:  "cert",
+						},
+					},
+				},
+			},
+			want: want{
+				CA: []byte("1234"),
+			},
+			wantErr: true,
+		},
+		{
+			name: "should fetch ca from ConfigMap",
+			fields: fields{
+				namespace: "default",
+				kube: fclient.NewClientBuilder().WithObjects(&corev1.ConfigMap{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "foobar",
+						Namespace: "default",
+					},
+					Data: map[string]string{
+						"cert": "1234",
+					},
+				}).Build(),
+				store: &esv1beta1.KubernetesProvider{
+					Server: esv1beta1.KubernetesServer{
+						CAProvider: &esv1beta1.CAProvider{
+							Type: esv1beta1.CAProviderTypeConfigMap,
+							Name: "foobar",
+							Key:  "cert",
+						},
+					},
+				},
+			},
+			want: want{
+				CA: []byte("1234"),
+			},
+			wantErr: true,
+		},
+		{
+			name: "should set token from secret",
+			fields: fields{
+				namespace: "default",
+				kube: fclient.NewClientBuilder().WithObjects(&corev1.Secret{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "foobar",
+						Namespace: "default",
+					},
+					Data: map[string][]byte{
+						"token": []byte("mytoken"),
+					},
+				}).Build(),
+				store: &esv1beta1.KubernetesProvider{
+					Server: esv1beta1.KubernetesServer{
+						CABundle: []byte("1234"),
+					},
+					Auth: esv1beta1.KubernetesAuth{
+						Token: &esv1beta1.TokenAuth{
+							BearerToken: v1.SecretKeySelector{
+								Name:      "foobar",
+								Namespace: pointer.String("shouldnotberelevant"),
+								Key:       "token",
+							},
+						},
+					},
+				},
+			},
+			want: want{
+				CA:          []byte("1234"),
+				BearerToken: []byte("mytoken"),
+			},
+			wantErr: false,
+		},
+		{
+			name: "should set client cert from secret",
+			fields: fields{
+				namespace: "default",
+				kube: fclient.NewClientBuilder().WithObjects(&corev1.Secret{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "mycert",
+						Namespace: "default",
+					},
+					Data: map[string][]byte{
+						"cert": []byte("my-cert"),
+						"key":  []byte("my-key"),
+					},
+				}).Build(),
+				store: &esv1beta1.KubernetesProvider{
+					Server: esv1beta1.KubernetesServer{
+						CABundle: []byte("1234"),
+					},
+					Auth: esv1beta1.KubernetesAuth{
+						Cert: &esv1beta1.CertAuth{
+							ClientCert: v1.SecretKeySelector{
+								Name: "mycert",
+								Key:  "cert",
+							},
+							ClientKey: v1.SecretKeySelector{
+								Name: "mycert",
+								Key:  "key",
+							},
+						},
+					},
+				},
+			},
+			want: want{
+				CA:          []byte("1234"),
+				Certificate: []byte("my-cert"),
+				Key:         []byte("my-key"),
+			},
+			wantErr: false,
+		},
+		{
+			name: "should set token from service account",
+			fields: fields{
+				namespace: "default",
+				kube: fclient.NewClientBuilder().WithObjects(&corev1.ServiceAccount{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "my-sa",
+						Namespace: "default",
+					},
+					Secrets: []corev1.ObjectReference{
+						{Name: "sa-token", Namespace: "default"},
+					},
+				}, &corev1.Secret{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "sa-token",
+						Namespace: "default",
+					},
+					Data: map[string][]byte{
+						"token": []byte("my-sa-token"),
+					},
+				}).Build(),
+				store: &esv1beta1.KubernetesProvider{
+					Server: esv1beta1.KubernetesServer{
+						CABundle: []byte("1234"),
+					},
+					Auth: esv1beta1.KubernetesAuth{
+						ServiceAccount: &v1.ServiceAccountSelector{
+							Name:      "my-sa",
+							Namespace: pointer.String("shouldnotberelevant"),
+						},
+					},
+				},
+			},
+			want: want{
+				CA:          []byte("1234"),
+				BearerToken: []byte("my-sa-token"),
+			},
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			k := &BaseClient{
+				kube:      tt.fields.kube,
+				store:     tt.fields.store,
+				namespace: tt.fields.namespace,
+				storeKind: tt.fields.storeKind,
+			}
+			if err := k.setAuth(context.Background()); (err != nil) != tt.wantErr {
+				t.Errorf("BaseClient.setAuth() error = %v, wantErr %v", err, tt.wantErr)
+			}
+			w := want{
+				Certificate: k.Certificate,
+				Key:         k.Key,
+				CA:          k.CA,
+				BearerToken: k.BearerToken,
+			}
+			if !cmp.Equal(w, tt.want) {
+				t.Errorf("unexpected value: expected %#v, got %#v", tt.want, w)
+			}
+		})
+	}
+}

+ 112 - 175
pkg/provider/kubernetes/kubernetes.go

@@ -16,41 +16,33 @@ package kubernetes
 
 import (
 	"context"
+	"encoding/json"
 	"fmt"
 
 	authv1 "k8s.io/api/authorization/v1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
+	labels "k8s.io/apimachinery/pkg/labels"
 	"k8s.io/client-go/kubernetes"
 	"k8s.io/client-go/rest"
 	kclient "sigs.k8s.io/controller-runtime/pkg/client"
 
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
-	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+	"github.com/external-secrets/external-secrets/pkg/find"
 	"github.com/external-secrets/external-secrets/pkg/utils"
 )
 
-const (
-	errPropertyNotFound                    = "property field not found on extrenal secrets"
-	errKubernetesCredSecretName            = "kubernetes credentials are empty"
-	errInvalidClusterStoreMissingNamespace = "invalid clusterStore missing Cert namespace"
-	errFetchCredentialsSecret              = "could not fetch Credentials secret: %w"
-	errMissingCredentials                  = "missing Credentials: %v"
-	errUninitalizedKubernetesProvider      = "provider kubernetes is not initialized"
-	errEmptyKey                            = "key %s found but empty"
-)
-
 // https://github.com/external-secrets/external-secrets/issues/644
 var _ esv1beta1.SecretsClient = &ProviderKubernetes{}
 var _ esv1beta1.Provider = &ProviderKubernetes{}
 
 type KClient interface {
 	Get(ctx context.Context, name string, opts metav1.GetOptions) (*corev1.Secret, error)
+	List(ctx context.Context, opts metav1.ListOptions) (*corev1.SecretList, error)
 }
 
 type RClient interface {
-	Create(ctx context.Context, SelfSubjectAccessReview *authv1.SelfSubjectAccessReview, opts metav1.CreateOptions) (*authv1.SelfSubjectAccessReview, error)
+	Create(ctx context.Context, selfSubjectRulesReview *authv1.SelfSubjectRulesReview, opts metav1.CreateOptions) (*authv1.SelfSubjectRulesReview, error)
 }
 
 // ProviderKubernetes is a provider for Kubernetes.
@@ -58,6 +50,8 @@ type ProviderKubernetes struct {
 	Client       KClient
 	ReviewClient RClient
 	Namespace    string
+	store        *esv1beta1.KubernetesProvider
+	storeKind    string
 }
 
 var _ esv1beta1.SecretsClient = &ProviderKubernetes{}
@@ -65,8 +59,8 @@ var _ esv1beta1.SecretsClient = &ProviderKubernetes{}
 type BaseClient struct {
 	kube        kclient.Client
 	store       *esv1beta1.KubernetesProvider
-	namespace   string
 	storeKind   string
+	namespace   string
 	Certificate []byte
 	Key         []byte
 	CA          []byte
@@ -85,32 +79,41 @@ func (k *ProviderKubernetes) Capabilities() esv1beta1.SecretStoreCapabilities {
 }
 
 // NewClient constructs a Kubernetes Provider.
-func (k *ProviderKubernetes) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
+func (p *ProviderKubernetes) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
 	storeSpec := store.GetSpec()
 	if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.Kubernetes == nil {
 		return nil, fmt.Errorf("no store type or wrong store type")
 	}
 	storeSpecKubernetes := storeSpec.Provider.Kubernetes
 
-	bStore := BaseClient{
+	client := BaseClient{
 		kube:      kube,
 		store:     storeSpecKubernetes,
 		namespace: namespace,
 		storeKind: store.GetObjectKind().GroupVersionKind().Kind,
 	}
+	p.Namespace = client.store.RemoteNamespace
+	p.store = storeSpecKubernetes
+	p.storeKind = store.GetObjectKind().GroupVersionKind().Kind
 
-	if err := bStore.setAuth(ctx); err != nil {
+	// allow SecretStore controller validation to pass
+	// when using referent namespace.
+	if client.storeKind == esv1beta1.ClusterSecretStoreKind && client.namespace == "" && isReferentSpec(storeSpecKubernetes) {
+		return p, nil
+	}
+
+	if err := client.setAuth(ctx); err != nil {
 		return nil, err
 	}
 
 	config := &rest.Config{
-		Host:        bStore.store.Server.URL,
-		BearerToken: string(bStore.BearerToken),
+		Host:        client.store.Server.URL,
+		BearerToken: string(client.BearerToken),
 		TLSClientConfig: rest.TLSClientConfig{
 			Insecure: false,
-			CertData: bStore.Certificate,
-			KeyData:  bStore.Key,
-			CAData:   bStore.CA,
+			CertData: client.Certificate,
+			KeyData:  client.Key,
+			CAData:   client.CA,
 		},
 	}
 
@@ -118,15 +121,34 @@ func (k *ProviderKubernetes) NewClient(ctx context.Context, store esv1beta1.Gene
 	if err != nil {
 		return nil, fmt.Errorf("error configuring clientset: %w", err)
 	}
+	p.Client = kubeClientSet.CoreV1().Secrets(client.store.RemoteNamespace)
+	p.ReviewClient = kubeClientSet.AuthorizationV1().SelfSubjectRulesReviews()
+	return p, nil
+}
 
-	k.Client = kubeClientSet.CoreV1().Secrets(bStore.store.RemoteNamespace)
-	k.Namespace = bStore.store.RemoteNamespace
-	k.ReviewClient = kubeClientSet.AuthorizationV1().SelfSubjectAccessReviews()
-
-	return k, nil
+func isReferentSpec(prov *esv1beta1.KubernetesProvider) bool {
+	if prov.Auth.Cert != nil {
+		if prov.Auth.Cert.ClientCert.Namespace == nil {
+			return true
+		}
+		if prov.Auth.Cert.ClientKey.Namespace == nil {
+			return true
+		}
+	}
+	if prov.Auth.ServiceAccount != nil {
+		if prov.Auth.ServiceAccount.Namespace == nil {
+			return true
+		}
+	}
+	if prov.Auth.Token != nil {
+		if prov.Auth.Token.BearerToken.Namespace == nil {
+			return true
+		}
+	}
+	return false
 }
 
-func (k *ProviderKubernetes) Close(ctx context.Context) error {
+func (p *ProviderKubernetes) Close(ctx context.Context) error {
 	return nil
 }
 
@@ -135,180 +157,95 @@ func (k *ProviderKubernetes) SetSecret(ctx context.Context, value []byte, remote
 	return fmt.Errorf("not implemented")
 }
 
-func (k *ProviderKubernetes) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
-	if ref.Property == "" {
-		return nil, fmt.Errorf(errPropertyNotFound)
-	}
-
-	payload, err := k.GetSecretMap(ctx, ref)
-
+func (p *ProviderKubernetes) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
+	secretMap, err := p.GetSecretMap(ctx, ref)
 	if err != nil {
 		return nil, err
 	}
-
-	val, ok := payload[ref.Property]
-	if !ok {
-		return nil, fmt.Errorf("property %s does not exist in key %s", ref.Property, ref.Key)
+	if ref.Property != "" {
+		val, ok := secretMap[ref.Property]
+		if !ok {
+			return nil, fmt.Errorf("property %s does not exist in key %s", ref.Property, ref.Key)
+		}
+		return val, nil
 	}
-	return val, nil
-}
-
-func (k *ProviderKubernetes) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
-	if utils.IsNil(k.Client) {
-		return nil, fmt.Errorf(errUninitalizedKubernetesProvider)
+	strMap := make(map[string]string)
+	for k, v := range secretMap {
+		strMap[k] = string(v)
 	}
-	opts := metav1.GetOptions{}
-	secretOut, err := k.Client.Get(ctx, ref.Key, opts)
-
+	jsonStr, err := json.Marshal(strMap)
 	if err != nil {
-		return nil, err
-	}
-
-	var payload map[string][]byte
-	if len(secretOut.Data) != 0 {
-		payload = secretOut.Data
+		return nil, fmt.Errorf("unabled to marshal json: %w", err)
 	}
-
-	return payload, nil
+	return jsonStr, nil
 }
 
-func (k *ProviderKubernetes) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
-	return nil, fmt.Errorf("not implemented")
-}
-
-func (k *BaseClient) setAuth(ctx context.Context) error {
-	var err error
-	if len(k.store.Server.CABundle) > 0 {
-		k.CA = k.store.Server.CABundle
-	} else if k.store.Server.CAProvider != nil {
-		keySelector := esmeta.SecretKeySelector{
-			Name:      k.store.Server.CAProvider.Name,
-			Namespace: k.store.Server.CAProvider.Namespace,
-			Key:       k.store.Server.CAProvider.Key,
-		}
-		k.CA, err = k.fetchSecretKey(ctx, keySelector, "CA")
-		if err != nil {
-			return err
-		}
-	} else {
-		return fmt.Errorf("no Certificate Authority provided")
-	}
-
-	if k.store.Auth.Token != nil {
-		k.BearerToken, err = k.fetchSecretKey(ctx, k.store.Auth.Token.BearerToken, "bearerToken")
-		if err != nil {
-			return err
-		}
-	} else if k.store.Auth.ServiceAccount != nil {
-		return fmt.Errorf("not implemented yet")
-	} else if k.store.Auth.Cert != nil {
-		k.Certificate, err = k.fetchSecretKey(ctx, k.store.Auth.Cert.ClientCert, "cert")
-		if err != nil {
-			return err
-		}
-		k.Key, err = k.fetchSecretKey(ctx, k.store.Auth.Cert.ClientKey, "key")
-		if err != nil {
-			return err
-		}
-	} else {
-		return fmt.Errorf("no credentials provided")
+func (p *ProviderKubernetes) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
+	secret, err := p.Client.Get(ctx, ref.Key, metav1.GetOptions{})
+	if err != nil {
+		return nil, err
 	}
-
-	return nil
+	return secret.Data, nil
 }
 
-func (k *BaseClient) fetchSecretKey(ctx context.Context, key esmeta.SecretKeySelector, component string) ([]byte, error) {
-	keySecret := &corev1.Secret{}
-	keySecretName := key.Name
-	if keySecretName == "" {
-		return nil, fmt.Errorf(errKubernetesCredSecretName)
-	}
-	objectKey := types.NamespacedName{
-		Name:      keySecretName,
-		Namespace: k.namespace,
+func (p *ProviderKubernetes) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
+	if ref.Tags != nil {
+		return p.findByTags(ctx, ref)
 	}
-	// only ClusterStore is allowed to set namespace (and then it's required)
-	if k.storeKind == esv1beta1.ClusterSecretStoreKind {
-		if key.Namespace == nil {
-			return nil, fmt.Errorf(errInvalidClusterStoreMissingNamespace)
-		}
-		objectKey.Namespace = *key.Namespace
+	if ref.Name != nil {
+		return p.findByName(ctx, ref)
 	}
+	return nil, fmt.Errorf("unexpected find operator: %#v", ref)
+}
 
-	err := k.kube.Get(ctx, objectKey, keySecret)
+func (p *ProviderKubernetes) findByTags(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
+	// empty/nil tags = everything
+	sel, err := labels.ValidatedSelectorFromSet(ref.Tags)
 	if err != nil {
-		return nil, fmt.Errorf(errFetchCredentialsSecret, err)
+		return nil, fmt.Errorf("unable to validate selector tags: %w", err)
 	}
-
-	val, ok := keySecret.Data[key.Key]
-	if !ok {
-		return nil, fmt.Errorf(errMissingCredentials, component)
+	secrets, err := p.Client.List(ctx, metav1.ListOptions{LabelSelector: sel.String()})
+	if err != nil {
+		return nil, fmt.Errorf("unable to list secrets: %w", err)
 	}
-
-	if len(val) == 0 {
-		return nil, fmt.Errorf(errEmptyKey, component)
+	data := make(map[string][]byte)
+	for _, secret := range secrets.Items {
+		jsonStr, err := json.Marshal(convertMap(secret.Data))
+		if err != nil {
+			return nil, err
+		}
+		data[secret.Name] = jsonStr
 	}
-	return val, nil
+	return utils.ConvertKeys(ref.ConversionStrategy, data)
 }
 
-func (k *ProviderKubernetes) Validate() (esv1beta1.ValidationResult, error) {
-	ctx := context.Background()
-
-	authReview, err := k.ReviewClient.Create(ctx, &authv1.SelfSubjectAccessReview{
-		Spec: authv1.SelfSubjectAccessReviewSpec{
-			ResourceAttributes: &authv1.ResourceAttributes{
-				Resource:  "secrets",
-				Namespace: k.Namespace,
-				Verb:      "get",
-			},
-		},
-	}, metav1.CreateOptions{})
-
+func (p *ProviderKubernetes) findByName(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
+	secrets, err := p.Client.List(ctx, metav1.ListOptions{})
 	if err != nil {
-		return esv1beta1.ValidationResultUnknown, fmt.Errorf("could not verify if client is valid: %w", err)
-	}
-
-	if !authReview.Status.Allowed {
-		return esv1beta1.ValidationResultError, fmt.Errorf("client is not allowed to get secrets")
+		return nil, fmt.Errorf("unable to list secrets: %w", err)
 	}
-
-	return esv1beta1.ValidationResultReady, nil
-}
-
-func (k *ProviderKubernetes) ValidateStore(store esv1beta1.GenericStore) error {
-	storeSpec := store.GetSpec()
-	k8sSpec := storeSpec.Provider.Kubernetes
-	if k8sSpec.Server.CABundle == nil && k8sSpec.Server.CAProvider == nil {
-		return fmt.Errorf("a CABundle or CAProvider is required")
+	matcher, err := find.New(*ref.Name)
+	if err != nil {
+		return nil, err
 	}
-
-	if k8sSpec.Auth.Cert != nil {
-		if k8sSpec.Auth.Cert.ClientCert.Name == "" {
-			return fmt.Errorf("ClientCert.Name cannot be empty")
-		}
-		if k8sSpec.Auth.Cert.ClientCert.Key == "" {
-			return fmt.Errorf("ClientCert.Key cannot be empty")
-		}
-		if err := utils.ValidateSecretSelector(store, k8sSpec.Auth.Cert.ClientCert); err != nil {
-			return err
-		}
-	} else if k8sSpec.Auth.Token != nil {
-		if k8sSpec.Auth.Token.BearerToken.Name == "" {
-			return fmt.Errorf("BearerToken.Name cannot be empty")
-		}
-		if k8sSpec.Auth.Token.BearerToken.Key == "" {
-			return fmt.Errorf("BearerToken.Key cannot be empty")
+	data := make(map[string][]byte)
+	for _, secret := range secrets.Items {
+		if !matcher.MatchName(secret.Name) {
+			continue
 		}
-		if err := utils.ValidateSecretSelector(store, k8sSpec.Auth.Token.BearerToken); err != nil {
-			return err
+		jsonStr, err := json.Marshal(convertMap(secret.Data))
+		if err != nil {
+			return nil, err
 		}
-	} else {
-		return fmt.Errorf("an Auth type must be specified")
+		data[secret.Name] = jsonStr
 	}
+	return utils.ConvertKeys(ref.ConversionStrategy, data)
+}
 
-	if k8sSpec.Auth.Cert != nil && k8sSpec.Auth.Token != nil {
-		return fmt.Errorf("only one authentication method is allowed")
+func convertMap(in map[string][]byte) map[string]string {
+	out := make(map[string]string)
+	for k, v := range in {
+		out[k] = string(v)
 	}
-
-	return nil
+	return out
 }

+ 394 - 339
pkg/provider/kubernetes/kubernetes_test.go

@@ -16,14 +16,14 @@ package kubernetes
 import (
 	"context"
 	"errors"
-	"fmt"
 	"reflect"
-	"strings"
 	"testing"
 
-	authv1 "k8s.io/api/authorization/v1"
+	"github.com/stretchr/testify/assert"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/utils/pointer"
+	kclient "sigs.k8s.io/controller-runtime/pkg/client"
 	fclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
 
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
@@ -31,14 +31,32 @@ import (
 )
 
 const (
-	errTestFetchCredentialsSecret = "test could not fetch Credentials secret failed"
-	errTestAuthValue              = "test failed key didn't match expected value"
-	errSomethingWentWrong         = "Something went wrong"
-	errExpectedErr                = "wanted error got nil"
+	errSomethingWentWrong = "Something went wrong"
+	testCertificate       = `-----BEGIN CERTIFICATE-----
+MIIDHTCCAgWgAwIBAgIRAKC4yxy9QGocND+6avTf7BgwDQYJKoZIhvcNAQELBQAw
+EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0yMTAzMjAyMDA4MDhaFw0yMTAzMjAyMDM4
+MDhaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQC3o6/JdZEqNbqNRkopHhJtJG5c4qS5d0tQ/kZYpfD/v/izAYum4Nzj
+aG15owr92/11W0pxPUliRLti3y6iScTs+ofm2D7p4UXj/Fnho/2xoWSOoWAodgvW
+Y8jh8A0LQALZiV/9QsrJdXZdS47DYZLsQ3z9yFC/CdXkg1l7AQ3fIVGKdrQBr9kE
+1gEDqnKfRxXI8DEQKXr+CKPUwCAytegmy0SHp53zNAvY+kopHytzmJpXLoEhxq4e
+ugHe52vXHdh/HJ9VjNp0xOH1waAgAGxHlltCW0PVd5AJ0SXROBS/a3V9sZCbCrJa
+YOOonQSEswveSv6PcG9AHvpNPot2Xs6hAgMBAAGjbjBsMA4GA1UdDwEB/wQEAwIC
+pDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
+BBR00805mrpoonp95RmC3B6oLl+cGTAVBgNVHREEDjAMggpnb29ibGUuY29tMA0G
+CSqGSIb3DQEBCwUAA4IBAQAipc1b6JrEDayPjpz5GM5krcI8dCWVd8re0a9bGjjN
+ioWGlu/eTr5El0ffwCNZ2WLmL9rewfHf/bMvYz3ioFZJ2OTxfazqYXNggQz6cMfa
+lbedDCdt5XLVX2TyerGvFram+9Uyvk3l0uM7rZnwAmdirG4Tv94QRaD3q4xTj/c0
+mv+AggtK0aRFb9o47z/BypLdk5mhbf3Mmr88C8XBzEnfdYyf4JpTlZrYLBmDCu5d
+9RLLsjXxhag8xqMtd1uLUM8XOTGzVWacw8iGY+CTtBKqyA+AE6/bDwZvEwVtsKtC
+QJ85ioEpy00NioqcF0WyMZH80uMsPycfpnl5uF7RkW8u
+-----END CERTIFICATE-----`
 )
 
 type fakeClient struct {
-	secretMap map[string]corev1.Secret
+	t                   *testing.T
+	secretMap           map[string]corev1.Secret
+	expectedListOptions metav1.ListOptions
 }
 
 func (fk fakeClient) Get(ctx context.Context, name string, opts metav1.GetOptions) (*corev1.Secret, error) {
@@ -50,359 +68,396 @@ func (fk fakeClient) Get(ctx context.Context, name string, opts metav1.GetOption
 	return &secret, nil
 }
 
-type fakeReviewClient struct {
-	authReview *authv1.SelfSubjectAccessReview
-}
-
-func (fk fakeReviewClient) Create(ctx context.Context, selfSubjectAccessReview *authv1.SelfSubjectAccessReview, opts metav1.CreateOptions) (*authv1.SelfSubjectAccessReview, error) {
-	if fk.authReview == nil {
-		return nil, errors.New(errSomethingWentWrong)
+func (fk fakeClient) List(ctx context.Context, opts metav1.ListOptions) (*corev1.SecretList, error) {
+	assert.Equal(fk.t, fk.expectedListOptions, opts)
+	list := &corev1.SecretList{}
+	for _, v := range fk.secretMap {
+		list.Items = append(list.Items, v)
 	}
-	return fk.authReview, nil
+	return list, nil
 }
 
-func TestKubernetesSecretManagerGetSecret(t *testing.T) {
-	expected := make(map[string][]byte)
-	value := "bar"
-	expected["foo"] = []byte(value)
-	mysecret := corev1.Secret{Data: expected}
-	mysecretmap := make(map[string]corev1.Secret)
-	mysecretmap["Key"] = mysecret
-
-	fk := fakeClient{secretMap: mysecretmap}
-	kp := ProviderKubernetes{Client: fk}
-
-	ref := esv1beta1.ExternalSecretDataRemoteRef{Key: "Key", Property: "foo"}
-	ctx := context.Background()
-
-	output, _ := kp.GetSecret(ctx, ref)
-
-	if string(output) != value {
-		t.Error("missing match value of the secret")
-	}
-
-	ref = esv1beta1.ExternalSecretDataRemoteRef{Key: "Key2", Property: "foo"}
-	_, err := kp.GetSecret(ctx, ref)
-
-	if err.Error() != errSomethingWentWrong {
-		t.Error("test failed")
-	}
-
-	ref = esv1beta1.ExternalSecretDataRemoteRef{Key: "Key", Property: "foo2"}
-	_, err = kp.GetSecret(ctx, ref)
-	expectedError := fmt.Sprintf("property %s does not exist in key %s", ref.Property, ref.Key)
-	if err.Error() != expectedError {
-		t.Error("test not existing property failed")
-	}
-
-	kp = ProviderKubernetes{Client: nil}
-	_, err = kp.GetSecret(ctx, ref)
-
-	if err.Error() != errUninitalizedKubernetesProvider {
-		t.Error("test nil Client failed")
-	}
-
-	ref = esv1beta1.ExternalSecretDataRemoteRef{Key: "Key", Property: ""}
-	_, err = kp.GetSecret(ctx, ref)
-
-	if err.Error() != "property field not found on extrenal secrets" {
-		t.Error("test nil Property failed")
-	}
-}
-
-func TestKubernetesSecretManagerGetSecretMap(t *testing.T) {
-	expected := make(map[string][]byte)
-	value := "bar"
-	expected["foo"] = []byte(value)
-	expected["foo2"] = []byte(value)
-	mysecret := corev1.Secret{Data: expected}
-	mysecretmap := make(map[string]corev1.Secret)
-	mysecretmap["Key"] = mysecret
-
-	fk := fakeClient{secretMap: mysecretmap}
-	kp := ProviderKubernetes{Client: fk}
-
-	ref := esv1beta1.ExternalSecretDataRemoteRef{Key: "Key", Property: ""}
-	ctx := context.Background()
-
-	output, err := kp.GetSecretMap(ctx, ref)
-
-	if err != nil {
-		t.Error("test failed")
-	}
-	if !reflect.DeepEqual(output, expected) {
-		t.Error("Objects are not equal")
-	}
-}
-
-func TestKubernetesSecretManagerSetAuth(t *testing.T) {
-	secretName := "good-name"
-	CABundle := "CABundle"
-	kp := esv1beta1.KubernetesProvider{Server: esv1beta1.KubernetesServer{}}
-
-	fs := &corev1.Secret{
-		ObjectMeta: metav1.ObjectMeta{Name: secretName},
-		Data:       make(map[string][]byte),
-	}
-	fs.Data["cert"] = []byte("secret-cert")
-	fs.Data["ca"] = []byte("secret-ca")
-	fs.Data["bearerToken"] = []byte("bearerToken")
-
-	fs2 := &corev1.Secret{
-		ObjectMeta: metav1.ObjectMeta{Name: "secret-for-the-key"},
-		Data:       make(map[string][]byte),
-	}
-	fs2.Data["key"] = []byte("secret-key")
-
-	fk := fclient.NewClientBuilder().WithObjects(fs, fs2).Build()
-	bc := BaseClient{fk, &kp, "", "", nil, nil, nil, nil}
-
-	ctx := context.Background()
-
-	err := bc.setAuth(ctx)
-
-	if err.Error() != "no Certificate Authority provided" {
-		fmt.Println(err.Error())
-		t.Error("test no Certificate Authority provided failed")
-	}
-
-	kp.Server.CAProvider = &esv1beta1.CAProvider{
-		Type:      esv1beta1.CAProviderTypeConfigMap,
-		Name:      fs.ObjectMeta.Name,
-		Namespace: &fs.ObjectMeta.Namespace,
-		Key:       "ca",
-	}
-
-	bc.setAuth(ctx)
-
-	if string(bc.CA) != "secret-ca" {
-		t.Error("failed to set CA provider")
-	}
-
-	kp.Server.CABundle = []byte(CABundle)
-
-	err = bc.setAuth(ctx)
-
-	if err.Error() != "no credentials provided" {
-		fmt.Println(err.Error())
-		t.Error("test kubernetes credentials not empty failed")
-	}
-
-	if string(bc.CA) != CABundle {
-		t.Error("failed to set CA provider")
-	}
-
-	kp = esv1beta1.KubernetesProvider{
-		Auth: esv1beta1.KubernetesAuth{
-			Cert: &esv1beta1.CertAuth{
-				ClientCert: v1.SecretKeySelector{
-					Name: "fake-name",
+func TestGetSecret(t *testing.T) {
+	type fields struct {
+		Client       KClient
+		ReviewClient RClient
+		Namespace    string
+	}
+	tests := []struct {
+		name   string
+		fields fields
+		ref    esv1beta1.ExternalSecretDataRemoteRef
+
+		want    []byte
+		wantErr bool
+	}{
+		{
+			name: "err GetSecretMap",
+			fields: fields{
+				Client: fakeClient{
+					t:         t,
+					secretMap: map[string]corev1.Secret{},
 				},
+				Namespace: "default",
+			},
+			ref: esv1beta1.ExternalSecretDataRemoteRef{
+				Key:      "mysec",
+				Property: "token",
 			},
+			wantErr: true,
 		},
-	}
-	kp.Server.CABundle = []byte(CABundle)
-
-	err = bc.setAuth(ctx)
-
-	if err.Error() != "could not fetch Credentials secret: secrets \"fake-name\" not found" {
-		fmt.Println(err.Error())
-		t.Error(errTestFetchCredentialsSecret)
-	}
-
-	kp.Auth.Cert.ClientCert.Name = fs.ObjectMeta.Name
-
-	err = bc.setAuth(ctx)
-
-	if err.Error() != fmt.Errorf(errMissingCredentials, "cert").Error() {
-		fmt.Println(err.Error())
-		t.Error(errTestFetchCredentialsSecret)
-	}
-
-	kp.Auth.Cert.ClientCert.Key = "cert"
-	kp.Auth.Cert.ClientKey.Name = "secret-for-the-key"
-
-	err = bc.setAuth(ctx)
-
-	if err.Error() != fmt.Errorf(errMissingCredentials, "key").Error() {
-		fmt.Println(err.Error())
-		t.Error(errTestFetchCredentialsSecret)
-	}
-	kp.Auth.Cert.ClientKey.Key = "key"
-
-	bc.setAuth(ctx)
-
-	kp.Auth.Token = &esv1beta1.TokenAuth{BearerToken: v1.SecretKeySelector{Name: secretName}}
-
-	err = bc.setAuth(ctx)
-
-	if err.Error() != fmt.Errorf(errMissingCredentials, "bearerToken").Error() {
-		fmt.Println(err.Error())
-		t.Error(errTestFetchCredentialsSecret)
-	}
-
-	kp.Auth.Token = &esv1beta1.TokenAuth{BearerToken: v1.SecretKeySelector{Name: secretName, Key: "bearerToken"}}
-
-	err = bc.setAuth(ctx)
-
-	if err != nil {
-		fmt.Println(err.Error())
-		t.Error(errTestFetchCredentialsSecret)
-	}
-	if string(bc.CA) != CABundle {
-		t.Error(errTestAuthValue)
-	}
-	if string(bc.Certificate) != "secret-cert" {
-		t.Error(errTestAuthValue)
-	}
-	if string(bc.Key) != "secret-key" {
-		t.Errorf(errTestAuthValue)
-	}
-	if string(bc.BearerToken) != "bearerToken" {
-		t.Error(errTestAuthValue)
-	}
-}
-func TestValidateStore(t *testing.T) {
-	p := ProviderKubernetes{}
-	store := &esv1beta1.SecretStore{
-		Spec: esv1beta1.SecretStoreSpec{
-			Provider: &esv1beta1.SecretStoreProvider{
-				Kubernetes: &esv1beta1.KubernetesProvider{},
+		{
+			name: "wrong property",
+			fields: fields{
+				Client: fakeClient{
+					t: t,
+					secretMap: map[string]corev1.Secret{
+						"mysec": {
+							Data: map[string][]byte{
+								"token": []byte(`foobar`),
+							},
+						},
+					},
+				},
+				Namespace: "default",
 			},
+			ref: esv1beta1.ExternalSecretDataRemoteRef{
+				Key:      "mysec",
+				Property: "not-the-token",
+			},
+			wantErr: true,
 		},
-	}
-	secretName := "my-secret-name"
-	secretKey := "my-secert-key"
-	err := p.ValidateStore(store)
-	if err == nil {
-		t.Errorf(errExpectedErr)
-	} else if err.Error() != "a CABundle or CAProvider is required" {
-		t.Errorf("service CA test failed, got %v", err.Error())
-	}
-
-	bundle := []byte("ca-bundle")
-	store.Spec.Provider.Kubernetes.Server.CABundle = bundle
-	err = p.ValidateStore(store)
-	if err == nil {
-		t.Errorf(errExpectedErr)
-	} else if err.Error() != "an Auth type must be specified" {
-		t.Errorf("empty Auth test failed")
-	}
-	store.Spec.Provider.Kubernetes.Auth = esv1beta1.KubernetesAuth{Cert: &esv1beta1.CertAuth{}}
-	err = p.ValidateStore(store)
-	if err == nil {
-		t.Errorf(errExpectedErr)
-	} else if err.Error() != "ClientCert.Name cannot be empty" {
-		t.Errorf("KeySelector test failed: expected clientCert name is required, got %v", err)
-	}
-	store.Spec.Provider.Kubernetes.Auth.Cert.ClientCert.Name = secretName
-	err = p.ValidateStore(store)
-	if err == nil {
-		t.Errorf(errExpectedErr)
-	} else if err.Error() != "ClientCert.Key cannot be empty" {
-		t.Errorf("KeySelector test failed: expected clientCert Key is required, got %v", err)
-	}
-	store.Spec.Provider.Kubernetes.Auth.Cert.ClientCert.Key = secretKey
-	ns := "ns-one"
-	store.Spec.Provider.Kubernetes.Auth.Cert.ClientCert.Namespace = &ns
-	err = p.ValidateStore(store)
-	if err == nil {
-		t.Errorf(errExpectedErr)
-	} else if err.Error() != "namespace not allowed with namespaced SecretStore" {
-		t.Errorf("KeySelector test failed: expected namespace not allowed, got %v", err)
-	}
-	store.Spec.Provider.Kubernetes.Auth = esv1beta1.KubernetesAuth{Token: &esv1beta1.TokenAuth{}}
-	err = p.ValidateStore(store)
-	if err == nil {
-		t.Errorf(errExpectedErr)
-	} else if err.Error() != "BearerToken.Name cannot be empty" {
-		t.Errorf("KeySelector test failed: expected bearer token name is required, got %v", err)
-	}
-	store.Spec.Provider.Kubernetes.Auth.Token.BearerToken.Name = secretName
-	err = p.ValidateStore(store)
-	if err == nil {
-		t.Errorf(errExpectedErr)
-	} else if err.Error() != "BearerToken.Key cannot be empty" {
-		t.Errorf("KeySelector test failed: expected bearer token key is required, got %v", err)
-	}
-	store.Spec.Provider.Kubernetes.Auth.Token.BearerToken.Key = secretKey
-	store.Spec.Provider.Kubernetes.Auth.Token.BearerToken.Namespace = &ns
-	err = p.ValidateStore(store)
-	if err == nil {
-		t.Errorf(errExpectedErr)
-	} else if err.Error() != "namespace not allowed with namespaced SecretStore" {
-		t.Errorf("KeySelector test failed: expected namespace not allowed, got %v", err)
-	}
-	store.Spec.Provider.Kubernetes.Auth = esv1beta1.KubernetesAuth{
-		Cert: &esv1beta1.CertAuth{
-			ClientCert: v1.SecretKeySelector{
-				Name: secretName,
-				Key:  secretKey,
+		{
+			name: "successful case",
+			fields: fields{
+				Client: fakeClient{
+					t: t,
+					secretMap: map[string]corev1.Secret{
+						"mysec": {
+							Data: map[string][]byte{
+								"token": []byte(`foobar`),
+							},
+						},
+					},
+				},
+				Namespace: "default",
 			},
+			ref: esv1beta1.ExternalSecretDataRemoteRef{
+				Key:      "mysec",
+				Property: "token",
+			},
+			want: []byte(`foobar`),
 		},
-		Token: &esv1beta1.TokenAuth{
-			BearerToken: v1.SecretKeySelector{
-				Name: secretName,
-				Key:  secretKey,
+		{
+			name: "successful case without property",
+			fields: fields{
+				Client: fakeClient{
+					t: t,
+					secretMap: map[string]corev1.Secret{
+						"mysec": {
+							Data: map[string][]byte{
+								"token": []byte(`foobar`),
+							},
+						},
+					},
+				},
+				Namespace: "default",
+			},
+			ref: esv1beta1.ExternalSecretDataRemoteRef{
+				Key: "mysec",
 			},
+			want: []byte(`{"token":"foobar"}`),
 		},
 	}
-	err = p.ValidateStore(store)
-	if err == nil {
-		t.Errorf(errExpectedErr)
-	} else if err.Error() != "only one authentication method is allowed" {
-		t.Errorf("KeySelector test failed: expected only one auth method allowed, got %v", err)
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			p := &ProviderKubernetes{
+				Client:       tt.fields.Client,
+				ReviewClient: tt.fields.ReviewClient,
+				Namespace:    tt.fields.Namespace,
+			}
+			got, err := p.GetSecret(context.Background(), tt.ref)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("ProviderKubernetes.GetSecret() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("ProviderKubernetes.GetSecret() = %v, want %v", got, tt.want)
+			}
+		})
 	}
 }
 
-func ErrorContains(out error, want string) bool {
-	if out == nil {
-		return want == ""
+func TestNewClient(t *testing.T) {
+	type fields struct {
+		Client       KClient
+		ReviewClient RClient
+		Namespace    string
+	}
+	type args struct {
+		store     esv1beta1.GenericStore
+		kube      kclient.Client
+		namespace string
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		want    bool
+		wantErr bool
+	}{
+		{
+			name:   "invalid store",
+			fields: fields{},
+			args: args{
+				store: &esv1beta1.ClusterSecretStore{
+					TypeMeta: metav1.TypeMeta{
+						Kind: esv1beta1.ClusterSecretStoreKind,
+					},
+					Spec: esv1beta1.SecretStoreSpec{
+						Provider: &esv1beta1.SecretStoreProvider{},
+					},
+				},
+				kube: fclient.NewClientBuilder().Build(),
+			},
+			wantErr: true,
+		},
+		{
+			name:   "test referent auth return",
+			fields: fields{},
+			args: args{
+				store: &esv1beta1.ClusterSecretStore{
+					TypeMeta: metav1.TypeMeta{
+						Kind: esv1beta1.ClusterSecretStoreKind,
+					},
+					Spec: esv1beta1.SecretStoreSpec{
+						Provider: &esv1beta1.SecretStoreProvider{
+							Kubernetes: &esv1beta1.KubernetesProvider{
+								Server: esv1beta1.KubernetesServer{
+									CABundle: []byte(testCertificate),
+								},
+								Auth: esv1beta1.KubernetesAuth{
+									Token: &esv1beta1.TokenAuth{
+										BearerToken: v1.SecretKeySelector{
+											Name: "foo",
+											Key:  "token",
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+				namespace: "",
+				kube:      fclient.NewClientBuilder().Build(),
+			},
+			want: true,
+		},
+		{
+			name:   "auth fail results in error",
+			fields: fields{},
+			args: args{
+				store: &esv1beta1.ClusterSecretStore{
+					TypeMeta: metav1.TypeMeta{
+						Kind: esv1beta1.ClusterSecretStoreKind,
+					},
+					Spec: esv1beta1.SecretStoreSpec{
+						Provider: &esv1beta1.SecretStoreProvider{
+							Kubernetes: &esv1beta1.KubernetesProvider{
+								Server: esv1beta1.KubernetesServer{
+									CABundle: []byte(testCertificate),
+								},
+								RemoteNamespace: "remote",
+								Auth: esv1beta1.KubernetesAuth{
+									Token: &esv1beta1.TokenAuth{
+										BearerToken: v1.SecretKeySelector{
+											Name:      "foo",
+											Namespace: pointer.String("default"),
+											Key:       "token",
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+				namespace: "foobarothernamespace",
+				kube:      fclient.NewClientBuilder().Build(),
+			},
+			wantErr: true,
+		},
+		{
+			name:   "test auth",
+			fields: fields{},
+			args: args{
+				store: &esv1beta1.ClusterSecretStore{
+					TypeMeta: metav1.TypeMeta{
+						Kind: esv1beta1.ClusterSecretStoreKind,
+					},
+					Spec: esv1beta1.SecretStoreSpec{
+						Provider: &esv1beta1.SecretStoreProvider{
+							Kubernetes: &esv1beta1.KubernetesProvider{
+								Server: esv1beta1.KubernetesServer{
+									CABundle: []byte(testCertificate),
+								},
+								RemoteNamespace: "remote",
+								Auth: esv1beta1.KubernetesAuth{
+									Token: &esv1beta1.TokenAuth{
+										BearerToken: v1.SecretKeySelector{
+											Name:      "foo",
+											Namespace: pointer.String("default"),
+											Key:       "token",
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+				namespace: "foobarothernamespace",
+				kube: fclient.NewClientBuilder().WithObjects(&corev1.Secret{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "foo",
+						Namespace: "default",
+					},
+					Data: map[string][]byte{
+						"token": []byte("1234"),
+					},
+				}).Build(),
+			},
+			want: true,
+		},
 	}
-	if want == "" {
-		return false
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			p := &ProviderKubernetes{
+				Client:       tt.fields.Client,
+				ReviewClient: tt.fields.ReviewClient,
+				Namespace:    tt.fields.Namespace,
+			}
+			got, err := p.NewClient(context.Background(), tt.args.store, tt.args.kube, tt.args.namespace)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("ProviderKubernetes.NewClient() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if tt.want {
+				assert.NotNil(t, got)
+			} else {
+				assert.Nil(t, got)
+			}
+		})
 	}
-	return strings.Contains(out.Error(), want)
 }
 
-func TestValidate(t *testing.T) {
-	authReview := authv1.SelfSubjectAccessReview{
-		Status: authv1.SubjectAccessReviewStatus{
-			Allowed: true,
+func TestGetAllSecrets(t *testing.T) {
+	type fields struct {
+		Client       KClient
+		ReviewClient RClient
+		Namespace    string
+	}
+	type args struct {
+		ctx context.Context
+		ref esv1beta1.ExternalSecretFind
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		want    map[string][]byte
+		wantErr bool
+	}{
+		{
+			name: "use regex",
+			fields: fields{
+				Client: fakeClient{
+					t: t,
+					secretMap: map[string]corev1.Secret{
+						"mysec": {
+							ObjectMeta: metav1.ObjectMeta{
+								Name: "mysec",
+							},
+							Data: map[string][]byte{
+								"token": []byte(`foo`),
+							},
+						},
+						"other": {
+							ObjectMeta: metav1.ObjectMeta{
+								Name: "other",
+							},
+							Data: map[string][]byte{
+								"token": []byte(`bar`),
+							},
+						},
+					},
+				},
+			},
+			args: args{
+				ref: esv1beta1.ExternalSecretFind{
+					Name: &esv1beta1.FindName{
+						RegExp: "other",
+					},
+				},
+			},
+			want: map[string][]byte{
+				"other": []byte(`{"token":"bar"}`),
+			},
 		},
-	}
-	fakeClient := fakeReviewClient{authReview: &authReview}
-	k := ProviderKubernetes{ReviewClient: fakeClient}
-	validationResult, err := k.Validate()
-	if err != nil {
-		t.Errorf("Test Failed! %v", err)
-	}
-	if validationResult != esv1beta1.ValidationResultReady {
-		t.Errorf("Test Failed! Wanted could not indicate validationResult is %s, got: %s", esv1beta1.ValidationResultReady, validationResult)
-	}
-
-	authReview = authv1.SelfSubjectAccessReview{
-		Status: authv1.SubjectAccessReviewStatus{
-			Allowed: false,
+		{
+			name: "use tags/labels",
+			fields: fields{
+				Client: fakeClient{
+					t: t,
+					expectedListOptions: metav1.ListOptions{
+						LabelSelector: "app=foobar",
+					},
+					secretMap: map[string]corev1.Secret{
+						"mysec": {
+							ObjectMeta: metav1.ObjectMeta{
+								Name: "mysec",
+							},
+							Data: map[string][]byte{
+								"token": []byte(`foo`),
+							},
+						},
+						"other": {
+							ObjectMeta: metav1.ObjectMeta{
+								Name: "other",
+							},
+							Data: map[string][]byte{
+								"token": []byte(`bar`),
+							},
+						},
+					},
+				},
+			},
+			args: args{
+				ref: esv1beta1.ExternalSecretFind{
+					Tags: map[string]string{
+						"app": "foobar",
+					},
+				},
+			},
+			want: map[string][]byte{
+				"mysec": []byte(`{"token":"foo"}`),
+				"other": []byte(`{"token":"bar"}`),
+			},
 		},
 	}
-	fakeClient = fakeReviewClient{authReview: &authReview}
-	k = ProviderKubernetes{ReviewClient: fakeClient}
-	validationResult, err = k.Validate()
-	if err.Error() != "client is not allowed to get secrets" {
-		t.Errorf("Test Failed! Wanted client is not allowed to get secrets got: %v", err)
-	}
-	if validationResult != esv1beta1.ValidationResultError {
-		t.Errorf("Test Failed! Wanted could not indicate validationResult is %s, got: %s", esv1beta1.ValidationResultError, validationResult)
-	}
-
-	fakeClient = fakeReviewClient{}
-	k = ProviderKubernetes{ReviewClient: fakeClient}
-	validationResult, err = k.Validate()
-	if err.Error() != "could not verify if client is valid: Something went wrong" {
-		t.Errorf("Test Failed! Wanted could not verify if client is valid: Something went wrong got: %v", err)
-	}
-	if validationResult != esv1beta1.ValidationResultUnknown {
-		t.Errorf("Test Failed! Wanted could not indicate validationResult is %s, got: %s", esv1beta1.ValidationResultUnknown, validationResult)
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			p := &ProviderKubernetes{
+				Client:       tt.fields.Client,
+				ReviewClient: tt.fields.ReviewClient,
+				Namespace:    tt.fields.Namespace,
+			}
+			got, err := p.GetAllSecrets(tt.args.ctx, tt.args.ref)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("ProviderKubernetes.GetAllSecrets() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("ProviderKubernetes.GetAllSecrets() = %v, want %v", got, tt.want)
+			}
+		})
 	}
 }

+ 100 - 0
pkg/provider/kubernetes/validate.go

@@ -0,0 +1,100 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package kubernetes
+
+import (
+	"context"
+	"fmt"
+
+	authv1 "k8s.io/api/authorization/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+	"github.com/external-secrets/external-secrets/pkg/utils"
+)
+
+func (p *ProviderKubernetes) ValidateStore(store esv1beta1.GenericStore) error {
+	storeSpec := store.GetSpec()
+	k8sSpec := storeSpec.Provider.Kubernetes
+	if k8sSpec.Server.CABundle == nil && k8sSpec.Server.CAProvider == nil {
+		return fmt.Errorf("a CABundle or CAProvider is required")
+	}
+	if store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind &&
+		k8sSpec.Server.CAProvider != nil &&
+		k8sSpec.Server.CAProvider.Namespace == nil {
+		return fmt.Errorf("CAProvider.namespace must not be empty with ClusterSecretStore")
+	}
+	if k8sSpec.Auth.Cert != nil {
+		if k8sSpec.Auth.Cert.ClientCert.Name == "" {
+			return fmt.Errorf("ClientCert.Name cannot be empty")
+		}
+		if k8sSpec.Auth.Cert.ClientCert.Key == "" {
+			return fmt.Errorf("ClientCert.Key cannot be empty")
+		}
+		if err := utils.ValidateSecretSelector(store, k8sSpec.Auth.Cert.ClientCert); err != nil {
+			return err
+		}
+	}
+	if k8sSpec.Auth.Token != nil {
+		if k8sSpec.Auth.Token.BearerToken.Name == "" {
+			return fmt.Errorf("BearerToken.Name cannot be empty")
+		}
+		if k8sSpec.Auth.Token.BearerToken.Key == "" {
+			return fmt.Errorf("BearerToken.Key cannot be empty")
+		}
+		if err := utils.ValidateSecretSelector(store, k8sSpec.Auth.Token.BearerToken); err != nil {
+			return err
+		}
+	}
+	if k8sSpec.Auth.ServiceAccount != nil {
+		if err := utils.ValidateReferentServiceAccountSelector(store, *k8sSpec.Auth.ServiceAccount); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (p *ProviderKubernetes) Validate() (esv1beta1.ValidationResult, error) {
+	// when using referent namespace we can not validate the token
+	// because the namespace is not known yet when Validate() is called
+	// from the SecretStore controller.
+	if p.storeKind == esv1beta1.ClusterSecretStoreKind && isReferentSpec(p.store) {
+		return esv1beta1.ValidationResultUnknown, nil
+	}
+	ctx := context.Background()
+	t := authv1.SelfSubjectRulesReview{
+		Spec: authv1.SelfSubjectRulesReviewSpec{
+			Namespace: p.Namespace,
+		},
+	}
+	authReview, err := p.ReviewClient.Create(ctx, &t, metav1.CreateOptions{})
+	if err != nil {
+		return esv1beta1.ValidationResultUnknown, fmt.Errorf("could not verify if client is valid: %w", err)
+	}
+	for _, rev := range authReview.Status.ResourceRules {
+		if contains("secrets", rev.Resources) && contains("get", rev.Verbs) {
+			return esv1beta1.ValidationResultReady, nil
+		}
+	}
+	return esv1beta1.ValidationResultError, fmt.Errorf("client is not allowed to get secrets")
+}
+
+func contains(sub string, args []string) bool {
+	for _, k := range args {
+		if k == sub {
+			return true
+		}
+	}
+	return false
+}

+ 357 - 0
pkg/provider/kubernetes/validate_test.go

@@ -0,0 +1,357 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package kubernetes
+
+import (
+	"context"
+	"errors"
+	"reflect"
+	"testing"
+
+	authv1 "k8s.io/api/authorization/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/utils/pointer"
+
+	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+	v1 "github.com/external-secrets/external-secrets/apis/meta/v1"
+)
+
+type fakeReviewClient struct {
+	authReview *authv1.SelfSubjectRulesReview
+}
+
+func (fk fakeReviewClient) Create(ctx context.Context, selfSubjectAccessReview *authv1.SelfSubjectRulesReview, opts metav1.CreateOptions) (*authv1.SelfSubjectRulesReview, error) {
+	if fk.authReview == nil {
+		return nil, errors.New(errSomethingWentWrong)
+	}
+	return fk.authReview, nil
+}
+
+func TestValidateStore(t *testing.T) {
+	type fields struct {
+		Client       KClient
+		ReviewClient RClient
+		Namespace    string
+	}
+
+	tests := []struct {
+		name    string
+		fields  fields
+		store   esv1beta1.GenericStore
+		wantErr bool
+	}{
+		{
+			name: "empty ca",
+			store: &esv1beta1.SecretStore{
+				Spec: esv1beta1.SecretStoreSpec{
+					Provider: &esv1beta1.SecretStoreProvider{
+						Kubernetes: &esv1beta1.KubernetesProvider{},
+					},
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "invalid client cert name",
+			store: &esv1beta1.SecretStore{
+				Spec: esv1beta1.SecretStoreSpec{
+					Provider: &esv1beta1.SecretStoreProvider{
+						Kubernetes: &esv1beta1.KubernetesProvider{
+							Server: esv1beta1.KubernetesServer{
+								CABundle: []byte("1234"),
+							},
+							Auth: esv1beta1.KubernetesAuth{
+								Cert: &esv1beta1.CertAuth{
+									ClientCert: v1.SecretKeySelector{
+										Name: "",
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "invalid client cert key",
+			store: &esv1beta1.SecretStore{
+				Spec: esv1beta1.SecretStoreSpec{
+					Provider: &esv1beta1.SecretStoreProvider{
+						Kubernetes: &esv1beta1.KubernetesProvider{
+							Server: esv1beta1.KubernetesServer{
+								CABundle: []byte("1234"),
+							},
+							Auth: esv1beta1.KubernetesAuth{
+								Cert: &esv1beta1.CertAuth{
+									ClientCert: v1.SecretKeySelector{
+										Name: "foobar",
+										Key:  "",
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "invalid client cert secretRef",
+			store: &esv1beta1.SecretStore{
+				Spec: esv1beta1.SecretStoreSpec{
+					Provider: &esv1beta1.SecretStoreProvider{
+						Kubernetes: &esv1beta1.KubernetesProvider{
+							Server: esv1beta1.KubernetesServer{
+								CABundle: []byte("1234"),
+							},
+							Auth: esv1beta1.KubernetesAuth{
+								Cert: &esv1beta1.CertAuth{
+									ClientCert: v1.SecretKeySelector{
+										Name:      "foobar",
+										Key:       "foobar",
+										Namespace: pointer.String("noop"),
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "invalid client token auth name",
+			store: &esv1beta1.SecretStore{
+				Spec: esv1beta1.SecretStoreSpec{
+					Provider: &esv1beta1.SecretStoreProvider{
+						Kubernetes: &esv1beta1.KubernetesProvider{
+							Server: esv1beta1.KubernetesServer{
+								CABundle: []byte("1234"),
+							},
+							Auth: esv1beta1.KubernetesAuth{
+								Token: &esv1beta1.TokenAuth{
+									BearerToken: v1.SecretKeySelector{
+										Name: "",
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "invalid client token auth key",
+			store: &esv1beta1.SecretStore{
+				Spec: esv1beta1.SecretStoreSpec{
+					Provider: &esv1beta1.SecretStoreProvider{
+						Kubernetes: &esv1beta1.KubernetesProvider{
+							Server: esv1beta1.KubernetesServer{
+								CABundle: []byte("1234"),
+							},
+							Auth: esv1beta1.KubernetesAuth{
+								Token: &esv1beta1.TokenAuth{
+									BearerToken: v1.SecretKeySelector{
+										Name: "foobar",
+										Key:  "",
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "invalid client token auth namespace",
+			store: &esv1beta1.SecretStore{
+				Spec: esv1beta1.SecretStoreSpec{
+					Provider: &esv1beta1.SecretStoreProvider{
+						Kubernetes: &esv1beta1.KubernetesProvider{
+							Server: esv1beta1.KubernetesServer{
+								CABundle: []byte("1234"),
+							},
+							Auth: esv1beta1.KubernetesAuth{
+								Token: &esv1beta1.TokenAuth{
+									BearerToken: v1.SecretKeySelector{
+										Name:      "foobar",
+										Key:       "foobar",
+										Namespace: pointer.String("nop"),
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "invalid service account auth name",
+			store: &esv1beta1.SecretStore{
+				Spec: esv1beta1.SecretStoreSpec{
+					Provider: &esv1beta1.SecretStoreProvider{
+						Kubernetes: &esv1beta1.KubernetesProvider{
+							Server: esv1beta1.KubernetesServer{
+								CABundle: []byte("1234"),
+							},
+							Auth: esv1beta1.KubernetesAuth{
+								ServiceAccount: &v1.ServiceAccountSelector{
+									Name:      "foobar",
+									Namespace: pointer.String("foobar"),
+								},
+							},
+						},
+					},
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "valid auth",
+			store: &esv1beta1.SecretStore{
+				Spec: esv1beta1.SecretStoreSpec{
+					Provider: &esv1beta1.SecretStoreProvider{
+						Kubernetes: &esv1beta1.KubernetesProvider{
+							Server: esv1beta1.KubernetesServer{
+								CABundle: []byte("1234"),
+							},
+							Auth: esv1beta1.KubernetesAuth{
+								ServiceAccount: &v1.ServiceAccountSelector{
+									Name: "foobar",
+								},
+							},
+						},
+					},
+				},
+			},
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			k := &ProviderKubernetes{
+				Client:       tt.fields.Client,
+				ReviewClient: tt.fields.ReviewClient,
+				Namespace:    tt.fields.Namespace,
+			}
+			if err := k.ValidateStore(tt.store); (err != nil) != tt.wantErr {
+				t.Errorf("ProviderKubernetes.ValidateStore() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
+
+func TestValidate(t *testing.T) {
+	successReview := authv1.SelfSubjectRulesReview{
+		Status: authv1.SubjectRulesReviewStatus{
+			ResourceRules: []authv1.ResourceRule{
+				{
+					Verbs:     []string{"get"},
+					Resources: []string{"secrets"},
+				},
+			},
+		},
+	}
+	failReview := authv1.SelfSubjectRulesReview{
+		Status: authv1.SubjectRulesReviewStatus{
+			ResourceRules: []authv1.ResourceRule{
+				{
+					Verbs:     []string{"update"},
+					Resources: []string{"secrets"},
+				},
+			},
+		},
+	}
+
+	type fields struct {
+		Client       KClient
+		ReviewClient RClient
+		Namespace    string
+		store        *esv1beta1.KubernetesProvider
+		storeKind    string
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		want    esv1beta1.ValidationResult
+		wantErr bool
+	}{
+		{
+			name: "empty ns should return unknown for referent auth",
+			fields: fields{
+				storeKind: esv1beta1.ClusterSecretStoreKind,
+				store: &esv1beta1.KubernetesProvider{
+					Auth: esv1beta1.KubernetesAuth{
+						ServiceAccount: &v1.ServiceAccountSelector{
+							Name: "foobar",
+						},
+					},
+				},
+				ReviewClient: fakeReviewClient{authReview: &successReview},
+			},
+			want:    esv1beta1.ValidationResultUnknown,
+			wantErr: false,
+		},
+		{
+			name: "review results in unknown",
+			fields: fields{
+				Namespace:    "default",
+				ReviewClient: fakeReviewClient{},
+			},
+			want:    esv1beta1.ValidationResultUnknown,
+			wantErr: true,
+		},
+		{
+			name: "not allowed results in error",
+			fields: fields{
+				Namespace:    "default",
+				ReviewClient: fakeReviewClient{authReview: &failReview},
+			},
+			want:    esv1beta1.ValidationResultError,
+			wantErr: true,
+		},
+		{
+			name: "allowed results in no error",
+			fields: fields{
+				Namespace:    "default",
+				ReviewClient: fakeReviewClient{authReview: &successReview},
+			},
+			want:    esv1beta1.ValidationResultReady,
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			k := &ProviderKubernetes{
+				Client:       tt.fields.Client,
+				ReviewClient: tt.fields.ReviewClient,
+				Namespace:    tt.fields.Namespace,
+				store:        tt.fields.store,
+				storeKind:    tt.fields.storeKind,
+			}
+			got, err := k.Validate()
+			if (err != nil) != tt.wantErr {
+				t.Errorf("ProviderKubernetes.Validate() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("ProviderKubernetes.Validate() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}

+ 3 - 0
pkg/template/v2/template.go

@@ -32,6 +32,9 @@ var tplFuncs = tpl.FuncMap{
 
 	"jwkPublicKeyPem":  jwkPublicKeyPem,
 	"jwkPrivateKeyPem": jwkPrivateKeyPem,
+
+	"toYaml":   toYAML,
+	"fromYaml": fromYAML,
 }
 
 // So other templating calls can use the same extra functions.

+ 27 - 3
pkg/template/v2/template_test.go

@@ -159,7 +159,7 @@ func TestExecute(t *testing.T) {
 			},
 		},
 		{
-			name: "fromJSON func",
+			name: "fromJson func",
 			tpl: map[string][]byte{
 				"foo": []byte("{{ $var := .secret | fromJson }}{{ $var.foo }}"),
 			},
@@ -171,7 +171,7 @@ func TestExecute(t *testing.T) {
 			},
 		},
 		{
-			name: "from & toJSON func",
+			name: "from & toJson func",
 			tpl: map[string][]byte{
 				"foo": []byte("{{ $var := .secret | fromJson }}{{ $var.foo | toJson }}"),
 			},
@@ -182,6 +182,30 @@ func TestExecute(t *testing.T) {
 				"foo": []byte(`{"baz":"bang"}`),
 			},
 		},
+		{
+			name: "fromJson & toYaml func",
+			tpl: map[string][]byte{
+				"foo": []byte("{{ $var := .secret | fromJson | toYaml }}{{ $var }}"),
+			},
+			data: map[string][]byte{
+				"secret": []byte(`{"foo": "bar"}`),
+			},
+			expetedData: map[string][]byte{
+				"foo": []byte(`foo: bar`),
+			},
+		},
+		{
+			name: "fromYaml & toJson func",
+			tpl: map[string][]byte{
+				"foo": []byte("{{ $var := .secret | fromYaml | toJson }}{{ $var }}"),
+			},
+			data: map[string][]byte{
+				"secret": []byte(`foo: bar`),
+			},
+			expetedData: map[string][]byte{
+				"foo": []byte(`{"foo":"bar"}`),
+			},
+		},
 		{
 			name: "use sprig functions",
 			tpl: map[string][]byte{
@@ -302,7 +326,7 @@ func TestExecute(t *testing.T) {
 			expErr: "unable to decode pkcs12",
 		},
 		{
-			name: "fromJSON error",
+			name: "fromJson error",
 			tpl: map[string][]byte{
 				"key": []byte(`{{ "{ # no json # }" | fromJson }}`),
 			},

+ 48 - 0
pkg/template/v2/yaml.go

@@ -0,0 +1,48 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package template
+
+import (
+	"strings"
+
+	"sigs.k8s.io/yaml"
+)
+
+// toYAML takes an interface, marshals it to yaml, and returns a string. It will
+// always return a string, even on marshal error (empty string).
+//
+// This is designed to be called from a template.
+func toYAML(v interface{}) string {
+	data, err := yaml.Marshal(v)
+	if err != nil {
+		// Swallow errors inside of a template.
+		return ""
+	}
+	return strings.TrimSuffix(string(data), "\n")
+}
+
+// fromYAML converts a YAML document into a map[string]interface{}.
+//
+// This is not a general-purpose YAML parser, and will not parse all valid
+// YAML documents. Additionally, because its intended use is within templates
+// it tolerates errors. It will insert the returned error message string into
+// m["Error"] in the returned map.
+func fromYAML(str string) map[string]interface{} {
+	m := map[string]interface{}{}
+
+	if err := yaml.Unmarshal([]byte(str), &m); err != nil {
+		m["Error"] = err.Error()
+	}
+	return m
+}