1
0
Fork 0
mirror of https://github.com/external-secrets/external-secrets.git synced 2024-12-14 11:57:59 +00:00

Fix merge conflict

This commit is contained in:
Sebastián Gómez 2022-03-07 13:59:58 -05:00
commit b0240cf45a
68 changed files with 2297 additions and 513 deletions

View file

@ -45,7 +45,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Setup Go
uses: actions/setup-go@v2
@ -77,7 +77,7 @@ jobs:
# version, but we prefer this action because it leaves 'annotations' (i.e.
# it comments on PRs to point out linter violations).
- name: Lint
uses: golangci/golangci-lint-action@v2
uses: golangci/golangci-lint-action@v3.1.0
with:
version: ${{ env.GOLANGCI_VERSION }}
skip-pkg-cache: true
@ -91,7 +91,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Setup Go
uses: actions/setup-go@v2
@ -132,7 +132,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Fetch History
run: git fetch --prune --unshallow
@ -199,7 +199,7 @@ jobs:
install: true
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Fetch History
run: git fetch --prune --unshallow

View file

@ -12,7 +12,7 @@ jobs:
deploy:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 0

View file

@ -92,7 +92,7 @@ jobs:
# Check out merge commit
- name: Fork based /ok-to-test-managed checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
ref: 'refs/pull/${{ github.event.client_payload.pull_request.number }}/merge'

View file

@ -38,7 +38,7 @@ jobs:
steps:
- name: Branch based PR checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
# <insert integration tests needing secrets>
@ -101,7 +101,7 @@ jobs:
# Check out merge commit
- name: Fork based /ok-to-test checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
ref: 'refs/pull/${{ github.event.client_payload.pull_request.number }}/merge'

View file

@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
fetch-depth: 0
@ -52,7 +52,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
fetch-depth: 0

View file

@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
fetch-depth: 0
@ -70,7 +70,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
fetch-depth: 0

View file

@ -28,7 +28,7 @@ func (alpha *ExternalSecret) ConvertTo(betaRaw conversion.Hub) error {
v1beta1DataFrom := make([]esv1beta1.ExternalSecretDataFromRemoteRef, 0)
for _, v1alpha1RemoteRef := range alpha.Spec.DataFrom {
v1beta1RemoteRef := esv1beta1.ExternalSecretDataFromRemoteRef{
Extract: esv1beta1.ExternalSecretDataRemoteRef{
Extract: &esv1beta1.ExternalSecretDataRemoteRef{
Key: v1alpha1RemoteRef.Key,
Property: v1alpha1RemoteRef.Property,
Version: v1alpha1RemoteRef.Version,

View file

@ -183,7 +183,7 @@ func newExternalSecretV1Beta1() *esv1beta1.ExternalSecret {
},
DataFrom: []esv1beta1.ExternalSecretDataFromRemoteRef{
{
Extract: esv1beta1.ExternalSecretDataRemoteRef{
Extract: &esv1beta1.ExternalSecretDataRemoteRef{
Key: "key",
Property: "property",
Version: "version",

View file

@ -161,15 +161,19 @@ type ExternalSecretDataRemoteRef struct {
Property string `json:"property,omitempty"`
}
// +kubebuilder:validation:MinProperties=1
// +kubebuilder:validation:MaxProperties=1
type ExternalSecretDataFromRemoteRef struct {
// Used to extract multiple key/value pairs from one secret
// +optional
Extract ExternalSecretDataRemoteRef `json:"extract,omitempty"`
Extract *ExternalSecretDataRemoteRef `json:"extract,omitempty"`
// Used to find secrets based on tags or regular expressions
// +optional
Find ExternalSecretFind `json:"find,omitempty"`
Find *ExternalSecretFind `json:"find,omitempty"`
}
// +kubebuilder:validation:MinProperties=1
// +kubebuilder:validation:MaxProperties=1
type ExternalSecretFind struct {
// Finds secrets based on the name.
// +optional

View file

@ -12,36 +12,47 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package provider
package v1beta1
import (
"context"
"sigs.k8s.io/controller-runtime/pkg/client"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
)
// +kubebuilder:object:root=false
// +kubebuilder:object:generate:false
// +k8s:deepcopy-gen:interfaces=nil
// +k8s:deepcopy-gen=nil
// Provider is a common interface for interacting with secret backends.
type Provider interface {
// NewClient constructs a SecretsManager Provider
NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (SecretsClient, error)
NewClient(ctx context.Context, store GenericStore, kube client.Client, namespace string) (SecretsClient, error)
// ValidateStore checks if the provided store is valid
ValidateStore(store GenericStore) error
}
// +kubebuilder:object:root=false
// +kubebuilder:object:generate:false
// +k8s:deepcopy-gen:interfaces=nil
// +k8s:deepcopy-gen=nil
// SecretsClient provides access to secrets.
type SecretsClient interface {
// GetSecret returns a single secret from the provider
GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error)
GetSecret(ctx context.Context, ref ExternalSecretDataRemoteRef) ([]byte, error)
// Validate checks if the client is configured correctly
// and is able to retrieve secrets from the provider
Validate() error
// GetSecretMap returns multiple k/v pairs from the provider
GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error)
GetSecretMap(ctx context.Context, ref ExternalSecretDataRemoteRef) (map[string][]byte, error)
// GetAllSecrets returns multiple k/v pairs from the provider
GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error)
GetAllSecrets(ctx context.Context, ref ExternalSecretFind) (map[string][]byte, error)
Close(ctx context.Context) error
}

View file

@ -12,27 +12,24 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package schema
package v1beta1
import (
"encoding/json"
"fmt"
"sync"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
"github.com/external-secrets/external-secrets/pkg/provider"
)
var builder map[string]provider.Provider
var builder map[string]Provider
var buildlock sync.RWMutex
func init() {
builder = make(map[string]provider.Provider)
builder = make(map[string]Provider)
}
// Register a store backend type. Register panics if a
// backend with the same store is already registered.
func Register(s provider.Provider, storeSpec *esv1beta1.SecretStoreProvider) {
func Register(s Provider, storeSpec *SecretStoreProvider) {
storeName, err := getProviderName(storeSpec)
if err != nil {
panic(fmt.Sprintf("store error registering schema: %s", err.Error()))
@ -50,7 +47,7 @@ func Register(s provider.Provider, storeSpec *esv1beta1.SecretStoreProvider) {
// ForceRegister adds to store schema, overwriting a store if
// already registered. Should only be used for testing.
func ForceRegister(s provider.Provider, storeSpec *esv1beta1.SecretStoreProvider) {
func ForceRegister(s Provider, storeSpec *SecretStoreProvider) {
storeName, err := getProviderName(storeSpec)
if err != nil {
panic(fmt.Sprintf("store error registering schema: %s", err.Error()))
@ -62,7 +59,7 @@ func ForceRegister(s provider.Provider, storeSpec *esv1beta1.SecretStoreProvider
}
// GetProviderByName returns the provider implementation by name.
func GetProviderByName(name string) (provider.Provider, bool) {
func GetProviderByName(name string) (Provider, bool) {
buildlock.RLock()
f, ok := builder[name]
buildlock.RUnlock()
@ -70,8 +67,11 @@ func GetProviderByName(name string) (provider.Provider, bool) {
}
// GetProvider returns the provider from the generic store.
func GetProvider(s esv1beta1.GenericStore) (provider.Provider, error) {
func GetProvider(s GenericStore) (Provider, error) {
spec := s.GetSpec()
if spec == nil {
return nil, fmt.Errorf("no spec found in %#v", s)
}
storeName, err := getProviderName(spec.Provider)
if err != nil {
return nil, fmt.Errorf("store error for %s: %w", s.GetName(), err)
@ -90,7 +90,7 @@ func GetProvider(s esv1beta1.GenericStore) (provider.Provider, error) {
// getProviderName returns the name of the configured provider
// or an error if the provider is not configured.
func getProviderName(storeSpec *esv1beta1.SecretStoreProvider) (string, error) {
func getProviderName(storeSpec *SecretStoreProvider) (string, error) {
storeBytes, err := json.Marshal(storeSpec)
if err != nil || storeBytes == nil {
return "", fmt.Errorf("failed to marshal store spec: %w", err)

View file

@ -11,7 +11,7 @@ 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 schema
package v1beta1
import (
"context"
@ -19,9 +19,6 @@ import (
"github.com/stretchr/testify/assert"
"sigs.k8s.io/controller-runtime/pkg/client"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
"github.com/external-secrets/external-secrets/pkg/provider"
)
type PP struct{}
@ -29,22 +26,22 @@ type PP struct{}
const shouldBeRegistered = "provider should be registered"
// New constructs a SecretsManager Provider.
func (p *PP) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) {
func (p *PP) NewClient(ctx context.Context, store GenericStore, kube client.Client, namespace string) (SecretsClient, error) {
return p, nil
}
// GetSecret returns a single secret from the provider.
func (p *PP) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
func (p *PP) GetSecret(ctx context.Context, ref ExternalSecretDataRemoteRef) ([]byte, error) {
return []byte("NOOP"), nil
}
// GetSecretMap returns multiple k/v pairs from the provider.
func (p *PP) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
func (p *PP) GetSecretMap(ctx context.Context, ref ExternalSecretDataRemoteRef) (map[string][]byte, error) {
return map[string][]byte{}, nil
}
// Empty GetAllSecrets.
func (p *PP) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
func (p *PP) GetAllSecrets(ctx context.Context, ref ExternalSecretFind) (map[string][]byte, error) {
// TO be implemented
return map[string][]byte{}, nil
}
@ -57,6 +54,10 @@ func (p *PP) Validate() error {
return nil
}
func (p *PP) ValidateStore(store GenericStore) error {
return nil
}
// TestRegister tests if the Register function
// (1) panics if it tries to register something invalid
// (2) stores the correct provider.
@ -66,22 +67,22 @@ func TestRegister(t *testing.T) {
name string
expPanic bool
expExists bool
provider *esv1beta1.SecretStoreProvider
provider *SecretStoreProvider
}{
{
test: "should panic when given an invalid provider",
name: "aws",
expPanic: true,
expExists: false,
provider: &esv1beta1.SecretStoreProvider{},
provider: &SecretStoreProvider{},
},
{
test: "should register an correct provider",
name: "aws",
expExists: false,
provider: &esv1beta1.SecretStoreProvider{
AWS: &esv1beta1.AWSProvider{
Service: esv1beta1.AWSServiceSecretsManager,
provider: &SecretStoreProvider{
AWS: &AWSProvider{
Service: AWSServiceSecretsManager,
},
},
},
@ -90,9 +91,9 @@ func TestRegister(t *testing.T) {
name: "aws",
expPanic: true,
expExists: true,
provider: &esv1beta1.SecretStoreProvider{
AWS: &esv1beta1.AWSProvider{
Service: esv1beta1.AWSServiceSecretsManager,
provider: &SecretStoreProvider{
AWS: &AWSProvider{
Service: AWSServiceSecretsManager,
},
},
},
@ -109,10 +110,10 @@ func TestRegister(t *testing.T) {
}
}
func runTest(t *testing.T, name string, provider *esv1beta1.SecretStoreProvider, expPanic bool) {
func runTest(t *testing.T, name string, provider *SecretStoreProvider, expPanic bool) {
testProvider := &PP{}
secretStore := &esv1beta1.SecretStore{
Spec: esv1beta1.SecretStoreSpec{
secretStore := &SecretStore{
Spec: SecretStoreSpec{
Provider: provider,
},
}
@ -135,19 +136,19 @@ func runTest(t *testing.T, name string, provider *esv1beta1.SecretStoreProvider,
// ForceRegister is used by other tests, we should ensure it works as expected.
func TestForceRegister(t *testing.T) {
testProvider := &PP{}
provider := &esv1beta1.SecretStoreProvider{
AWS: &esv1beta1.AWSProvider{
Service: esv1beta1.AWSServiceParameterStore,
provider := &SecretStoreProvider{
AWS: &AWSProvider{
Service: AWSServiceParameterStore,
},
}
secretStore := &esv1beta1.SecretStore{
Spec: esv1beta1.SecretStoreSpec{
secretStore := &SecretStore{
Spec: SecretStoreSpec{
Provider: provider,
},
}
ForceRegister(testProvider, &esv1beta1.SecretStoreProvider{
AWS: &esv1beta1.AWSProvider{
Service: esv1beta1.AWSServiceParameterStore,
ForceRegister(testProvider, &SecretStoreProvider{
AWS: &AWSProvider{
Service: AWSServiceParameterStore,
},
})
p1, ok := GetProviderByName("aws")
@ -164,10 +165,10 @@ func TestRegisterGCP(t *testing.T) {
assert.False(t, ok, "provider should not be registered")
testProvider := &PP{}
secretStore := &esv1beta1.SecretStore{
Spec: esv1beta1.SecretStoreSpec{
Provider: &esv1beta1.SecretStoreProvider{
GCPSM: &esv1beta1.GCPSMProvider{},
secretStore := &SecretStore{
Spec: SecretStoreSpec{
Provider: &SecretStoreProvider{
GCPSM: &GCPSMProvider{},
},
},
}

View file

@ -0,0 +1,62 @@
/*
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 v1beta1
import (
"context"
"fmt"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)
var _ admission.CustomValidator = &GenericStoreValidator{}
const (
errInvalidStore = "invalid store"
)
type GenericStoreValidator struct{}
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type.
func (r *GenericStoreValidator) ValidateCreate(ctx context.Context, obj runtime.Object) error {
st, ok := obj.(GenericStore)
if !ok {
return fmt.Errorf(errInvalidStore)
}
return validateStore(st)
}
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type.
func (r *GenericStoreValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error {
st, ok := newObj.(GenericStore)
if !ok {
return fmt.Errorf(errInvalidStore)
}
return validateStore(st)
}
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type.
func (r *GenericStoreValidator) ValidateDelete(ctx context.Context, obj runtime.Object) error {
return nil
}
func validateStore(store GenericStore) error {
provider, err := GetProvider(store)
if err != nil {
return err
}
return provider.ValidateStore(store)
}

View file

@ -21,11 +21,13 @@ import (
func (c *SecretStore) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(c).
WithValidator(&GenericStoreValidator{}).
Complete()
}
func (c *ClusterSecretStore) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(c).
WithValidator(&GenericStoreValidator{}).
Complete()
}

View file

@ -422,8 +422,16 @@ func (in *ExternalSecretData) DeepCopy() *ExternalSecretData {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExternalSecretDataFromRemoteRef) DeepCopyInto(out *ExternalSecretDataFromRemoteRef) {
*out = *in
out.Extract = in.Extract
in.Find.DeepCopyInto(&out.Find)
if in.Extract != nil {
in, out := &in.Extract, &out.Extract
*out = new(ExternalSecretDataRemoteRef)
**out = **in
}
if in.Find != nil {
in, out := &in.Find, &out.Find
*out = new(ExternalSecretFind)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalSecretDataFromRemoteRef.
@ -794,6 +802,21 @@ func (in *GCPWorkloadIdentity) DeepCopy() *GCPWorkloadIdentity {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GenericStoreValidator) DeepCopyInto(out *GenericStoreValidator) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericStoreValidator.
func (in *GenericStoreValidator) DeepCopy() *GenericStoreValidator {
if in == nil {
return nil
}
out := new(GenericStoreValidator)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GitlabAuth) DeepCopyInto(out *GitlabAuth) {
*out = *in

View file

@ -28,12 +28,13 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"github.com/external-secrets/external-secrets/pkg/controllers/crds"
"github.com/external-secrets/external-secrets/pkg/controllers/webhookconfig"
)
var certcontrollerCmd = &cobra.Command{
Use: "certcontroller",
Short: "Controller to manage certificates for external secrets CRDs",
Long: `Controller to manage certificates for external secrets CRDs.
Short: "Controller to manage certificates for external secrets CRDs and ValidatingWebhookConfigs",
Long: `Controller to manage certificates for external secrets CRDs and ValidatingWebhookConfigs.
For more information visit https://external-secrets.io`,
Run: func(cmd *cobra.Command, args []string) {
var lvl zapcore.Level
@ -46,11 +47,12 @@ var certcontrollerCmd = &cobra.Command{
ctrl.SetLogger(logger)
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: metricsAddr,
Port: 9443,
LeaderElection: enableLeaderElection,
LeaderElectionID: "crd-certs-controller",
Scheme: scheme,
MetricsBindAddress: metricsAddr,
HealthProbeBindAddress: healthzAddr,
Port: 9443,
LeaderElection: enableLeaderElection,
LeaderElectionID: "crd-certs-controller",
ClientDisableCacheFor: []client.Object{
// the client creates a ListWatch for all resource kinds that
// are requested with .Get().
@ -59,31 +61,48 @@ var certcontrollerCmd = &cobra.Command{
// that he owns.
// see #721
&v1.Secret{},
}})
},
})
if err != nil {
setupLog.Error(err, "unable to start manager")
os.Exit(1)
}
crds := &crds.Reconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("webhook-certs-updater"),
Scheme: mgr.GetScheme(),
SvcName: serviceName,
SvcNamespace: serviceNamespace,
SecretName: secretName,
SecretNamespace: secretNamespace,
RequeueInterval: crdRequeueInterval,
CrdResources: []string{"externalsecrets.external-secrets.io", "clustersecretstores.external-secrets.io", "secretstores.external-secrets.io"},
CAName: "external-secrets",
CAOrganization: "external-secrets",
RestartOnSecretRefresh: false,
}
if err := crds.SetupWithManager(mgr, controller.Options{
crdctrl := crds.New(mgr.GetClient(), mgr.GetScheme(),
ctrl.Log.WithName("controllers").WithName("webhook-certs-updater"),
crdRequeueInterval, serviceName, serviceNamespace, secretName, secretNamespace, []string{
"externalsecrets.external-secrets.io",
"clustersecretstores.external-secrets.io",
"secretstores.external-secrets.io",
})
if err := crdctrl.SetupWithManager(mgr, controller.Options{
MaxConcurrentReconciles: concurrent,
}); err != nil {
setupLog.Error(err, errCreateController, "controller", "CustomResourceDefinition")
os.Exit(1)
}
whc := webhookconfig.New(mgr.GetClient(), mgr.GetScheme(),
ctrl.Log.WithName("controllers").WithName("webhook-certs-updater"),
serviceName, serviceNamespace,
secretName, secretNamespace, crdRequeueInterval)
if err := whc.SetupWithManager(mgr, controller.Options{
MaxConcurrentReconciles: concurrent,
}); err != nil {
setupLog.Error(err, errCreateController, "controller", "WebhookConfig")
os.Exit(1)
}
err = mgr.AddReadyzCheck("crd-inject", crdctrl.ReadyCheck)
if err != nil {
setupLog.Error(err, "unable to add crd readyz check")
os.Exit(1)
}
err = mgr.AddReadyzCheck("validation-webhook-inject", whc.ReadyCheck)
if err != nil {
setupLog.Error(err, "unable to add webhook readyz check")
os.Exit(1)
}
setupLog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
@ -96,6 +115,7 @@ func init() {
rootCmd.AddCommand(certcontrollerCmd)
certcontrollerCmd.Flags().StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
certcontrollerCmd.Flags().StringVar(&healthzAddr, "healthz-addr", ":8081", "The address the health endpoint binds to.")
certcontrollerCmd.Flags().StringVar(&serviceName, "service-name", "external-secrets-webhook", "Webhook service name")
certcontrollerCmd.Flags().StringVar(&serviceNamespace, "service-namespace", "default", "Webhook service namespace")
certcontrollerCmd.Flags().StringVar(&secretName, "secret-name", "external-secrets-webhook", "Secret to store certs for webhook")

View file

@ -22,11 +22,12 @@ import (
"github.com/spf13/cobra"
"go.uber.org/zap/zapcore"
v1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
// To allow using gcp auth.
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
_ "k8s.io/client-go/plugin/pkg/client/auth"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
@ -44,6 +45,7 @@ var (
dnsName string
certDir string
metricsAddr string
healthzAddr string
controllerClass string
enableLeaderElection bool
concurrent int
@ -64,6 +66,7 @@ func init() {
_ = clientgoscheme.AddToScheme(scheme)
_ = esv1beta1.AddToScheme(scheme)
_ = esv1alpha1.AddToScheme(scheme)
_ = apiextensionsv1.AddToScheme(scheme)
}
var rootCmd = &cobra.Command{

View file

@ -17,6 +17,7 @@ package cmd
import (
"context"
"net/http"
"os"
"os/signal"
"syscall"
@ -65,12 +66,12 @@ var webhookCmd = &cobra.Command{
logger := zap.New(zap.Level(lvl))
ctrl.SetLogger(logger)
setupLog.Info("validating certs")
err = crds.CheckCerts(c, dnsName, time.Now().Add(time.Hour))
err = waitForCerts(c, time.Minute*2)
if err != nil {
setupLog.Error(err, "error checking certs")
setupLog.Error(err, "unable to validate certificates")
os.Exit(1)
}
ctx, cancel := context.WithCancel(context.Background())
go func(c crds.CertInfo, dnsName string, every time.Duration) {
sigs := make(chan os.Signal, 1)
@ -92,10 +93,11 @@ var webhookCmd = &cobra.Command{
}(c, dnsName, certCheckInterval)
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: metricsAddr,
Port: 9443,
CertDir: certDir,
Scheme: scheme,
MetricsBindAddress: metricsAddr,
HealthProbeBindAddress: healthzAddr,
Port: 9443,
CertDir: certDir,
})
if err != nil {
setupLog.Error(err, "unable to start manager")
@ -125,6 +127,15 @@ var webhookCmd = &cobra.Command{
setupLog.Error(err, errCreateWebhook, "webhook", "ClusterSecretStore-v1alpha1")
os.Exit(1)
}
err = mgr.AddReadyzCheck("certs", func(_ *http.Request) error {
return crds.CheckCerts(c, dnsName, time.Now().Add(time.Hour))
})
if err != nil {
setupLog.Error(err, "unable to add certs readyz check")
os.Exit(1)
}
setupLog.Info("starting manager")
if err := mgr.Start(ctx); err != nil {
setupLog.Error(err, "problem running manager")
@ -133,9 +144,33 @@ var webhookCmd = &cobra.Command{
},
}
// waitForCerts waits until the certificates become ready.
// If they don't become ready within a given time duration
// this function returns an error.
// certs are generated by the certcontroller.
func waitForCerts(c crds.CertInfo, timeout time.Duration) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
for {
setupLog.Info("validating certs")
err := crds.CheckCerts(c, dnsName, time.Now().Add(time.Hour))
if err == nil {
return nil
}
if err != nil {
setupLog.Error(err, "invalid certs. retrying...")
<-time.After(time.Second * 10)
}
if ctx.Err() != nil {
return ctx.Err()
}
}
}
func init() {
rootCmd.AddCommand(webhookCmd)
webhookCmd.Flags().StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
webhookCmd.Flags().StringVar(&healthzAddr, "healthz-addr", ":8081", "The address the health endpoint binds to.")
webhookCmd.Flags().StringVar(&dnsName, "dns-name", "localhost", "DNS name to validate certificates with")
webhookCmd.Flags().StringVar(&certDir, "cert-dir", "/tmp/k8s-webhook-server/serving-certs", "path to check for certs")
webhookCmd.Flags().StringVar(&loglevel, "loglevel", "info", "loglevel to use, one of: debug, info, warn, error, dpanic, panic, fatal")

View file

@ -320,6 +320,8 @@ spec:
Provider data If multiple entries are specified, the Secret keys
are merged in the specified order
items:
maxProperties: 1
minProperties: 1
properties:
extract:
description: Used to extract multiple key/value pairs from one
@ -341,6 +343,8 @@ spec:
type: object
find:
description: Used to find secrets based on tags or regular expressions
maxProperties: 1
minProperties: 1
properties:
name:
description: Finds secrets based on the name.

View file

@ -61,6 +61,12 @@ spec:
- containerPort: {{ .Values.certController.prometheus.service.port }}
protocol: TCP
name: metrics
readinessProbe:
httpGet:
port: 8081
path: /readyz
initialDelaySeconds: 20
periodSeconds: 5
{{- with .Values.certController.extraEnv }}
env:
{{- toYaml . | nindent 12 }}

View file

@ -16,6 +16,31 @@ rules:
- "watch"
- "update"
- "patch"
- apiGroups:
- "admissionregistration.k8s.io"
resources:
- "validatingwebhookconfigurations"
verbs:
- "get"
- "list"
- "watch"
- "update"
- "patch"
- apiGroups:
- ""
resources:
- "endpoints"
verbs:
- "list"
- "get"
- "watch"
- apiGroups:
- ""
resources:
- "events"
verbs:
- "create"
- "patch"
- apiGroups:
- ""
resources:

View file

@ -0,0 +1,41 @@
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: secretstore-validate
labels:
external-secrets.io/component: webhook
webhooks:
- name: "validate.secretstore.external-secrets.io"
rules:
- apiGroups: ["external-secrets.io"]
apiVersions: ["v1beta1"]
operations: ["CREATE", "UPDATE", "DELETE"]
resources: ["secretstores"]
scope: "Namespaced"
clientConfig:
service:
namespace: {{ .Release.Namespace | quote }}
name: {{ include "external-secrets.fullname" . }}-webhook
path: /validate-external-secrets-io-v1beta1-secretstore
# will be set by controller
caBundle: Cg==
admissionReviewVersions: ["v1", "v1beta1"]
sideEffects: None
timeoutSeconds: 5
- name: "validate.clustersecretstore.external-secrets.io"
rules:
- apiGroups: ["external-secrets.io"]
apiVersions: ["v1beta1"]
operations: ["CREATE", "UPDATE", "DELETE"]
resources: ["clustersecretstores"]
scope: "Cluster"
clientConfig:
service:
namespace: {{ .Release.Namespace | quote }}
name: {{ include "external-secrets.fullname" . }}-webhook
path: /validate-external-secrets-io-v1beta1-clustersecretstore
caBundle: Cg== # will be set by controller
admissionReviewVersions: ["v1", "v1beta1"]
sideEffects: None
timeoutSeconds: 5

View file

@ -63,8 +63,9 @@ spec:
protocol: TCP
name: webhook
readinessProbe:
tcpSocket:
port: 9443
httpGet:
port: 8081
path: /readyz
initialDelaySeconds: 20
periodSeconds: 5
{{- with .Values.webhook.extraEnv }}

View file

@ -2,6 +2,7 @@ apiVersion: v1
kind: Secret
metadata:
name: {{ include "external-secrets.fullname" . }}-webhook
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "external-secrets-webhook.labels" . | nindent 4 }}
external-secrets.io/component : webhook

View file

@ -2,6 +2,7 @@ apiVersion: v1
kind: Service
metadata:
name: {{ include "external-secrets.fullname" . }}-webhook
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "external-secrets-webhook.labels" . | nindent 4 }}
external-secrets.io/component : webhook

View file

@ -2244,6 +2244,8 @@ spec:
dataFrom:
description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order
items:
maxProperties: 1
minProperties: 1
properties:
extract:
description: Used to extract multiple key/value pairs from one secret
@ -2262,6 +2264,8 @@ spec:
type: object
find:
description: Used to find secrets based on tags or regular expressions
maxProperties: 1
minProperties: 1
properties:
name:
description: Finds secrets based on the name.

View file

@ -98,7 +98,7 @@ You should also consider using Kubernetes' admission control system (e.g.
fine-grained access control.
## Running multiple Controller
You can run multiple controller within the cluster. One controller can be
You can run multiple controllers within the cluster. One controller can be
limited to only process `SecretStores` with a predefined `spec.controller`
field.

24
docs/eso-blogs.md Normal file
View file

@ -0,0 +1,24 @@
# ESO Blogs
A list of blogs written by people all over the community. Feel free to let us know if you are writing about ESO at some place! We would be happy to mention you here!
## [Tutorial: How to Set External-Secrets with Azure KeyVault](https://blog.container-solutions.com/tutorial-external-secrets-with-azure-keyvault)
Gustavo writes about how to setup ESO with Azure Key Vault and adds an guide on how to make it a bit more secure with OPA (Open Policy Agent). [How to Set External-Secrets with Azure KeyVault](https://blog.container-solutions.com/tutorial-external-secrets-with-azure-keyvault)
## [Tutorial: How to Set External-Secrets with GCP Secret Manager](https://blog.container-solutions.com/tutorial-how-to-set-external-secrets-with-gcp-secret-manager)
Gustavo writes about how to setup ESO with GCP Secret Manager. He also shows you how to make a simple multi tenant setup with a ClusterSecretStore. [How to Set External-Secrets with GCP Secret Manager](https://blog.container-solutions.com/tutorial-how-to-set-external-secrets-with-gcp-secret-manager)
## [Tutorial: How to Set External-Secrets with Hashicorp Vault](https://blog.container-solutions.com/tutorialexternal-secrets-with-hashicorp-vault)
Gustavo writes about how to setup ESO with Hashicorp Vault. He also shows you how to make this scale with multiple replicas of the operator and leader election enabled to lead balance handling synchronization work. [How to Set External-Secrets with Hashicorp Vault](https://blog.container-solutions.com/tutorialexternal-secrets-with-hashicorp-vault)
## [Tutorial: How to Set External-Secrets with AWS](https://blog.container-solutions.com/tutorial-how-to-set-external-secrets-with-aws)
Gustavo writes about how to setup ESO with AWS Secrets Manager. He also shows you how to limit access and give granular permissions with better policies and roles for your service accounts to use. [How to Set External-Secrets with AWS](https://blog.container-solutions.com/tutorial-how-to-set-external-secrets-with-aws)
## [Kubernetes Hardening Tutorial Part 2: Network](https://blog.gitguardian.com/kubernetes-tutorial-part-2-network/)
Tiexin Guo Writes about Kubernetes hardening in this series of blogs. He mentions ESO as one of the convenient options when dealing with secrets in Kubernetes, and how to use it with AWS Secret Manager using AWS credentials. [Kubernetes Hardening Tutorial Part 2: Network](https://blog.gitguardian.com/kubernetes-tutorial-part-2-network/)

9
docs/eso-demos.md Normal file
View file

@ -0,0 +1,9 @@
# ESO Demos
A list of demos given by people going through simple setups with ESO. Feel free to let us know if you have a demo that you want to include here!
## GCP SM + AWS SM + Azure Key Vault Demo
This was an old demo going through an old version of ESO. Most of it is still valid, but beware of CRD and breaking change differences.
[![GCP SM + AWS SM + Azure Key Vault Demo](https://img.youtube.com/vi/L6tn1YdMkF8/0.jpg)](https://www.youtube.com/watch?v=L6tn1YdMkF8)

16
docs/eso-talks.md Normal file
View file

@ -0,0 +1,16 @@
# ESO Talks
A list of talks given by people at conferences and events. Feel free to let us know if you are talking about ESO at some place! We would be happy to mention you here!
## Kubernetes Community Days UK
[![Kubernetes Community Days UK](https://img.youtube.com/vi/IsoQWxw3Nk4/0.jpg)](https://www.youtube.com/watch?v=IsoQWxw3Nk4)
## CNCF Community Groups Canada
[![CNCF Community Groups Canada](https://img.youtube.com/vi/CsJgfHQA0SQ/0.jpg)](https://www.youtube.com/watch?v=CsJgfHQA0SQ)
## Container Days Hamburg
[![Container Days Hamburg](https://img.youtube.com/vi/_ZDkHLRYujo/0.jpg)](https://www.youtube.com/watch?v=_ZDkHLRYujo)

View file

@ -2,7 +2,7 @@
> NOTE: this feature is experimental and not highly tested
Controller classes are a proprierty set during the deployment that allows multiple controllers to work in a group of workloard. It works by separating which secretStores are going to be attributed to which controller. For the behavior of a single controller, no extra configuration is needed.
Controller classes are a proprierty set during the deployment that allows multiple controllers to work in a group of workload. It works by separating which secretStores are going to be attributed to which controller. For the behavior of a single controller, no extra configuration is needed.
## Setting up Controller Class
@ -16,4 +16,4 @@ helm install custom-external-secrets external-secrets/external-secrets --set con
Now, any `ExternalSecret` bound to this secret store will be evaluated by the operator with the controllerClass custom.
> Note: Any SecretStore without `spec.controller` set will be considered as valid by any operator, regardless of their respective controllerClasses.
> Note: Any SecretStore without `spec.controller` set will be considered as valid by any operator, regardless of their respective controllerClasses.

View file

@ -13,7 +13,7 @@ See url for what region you you are accessing.
![userOCID-details](./pictures/screenshot_region.png)
Select tenancy in the top right to see your user OCID as shown below.
![tenancyOCID-details](./pictures/tenancy.png)
![tenancyOCID-details](./pictures/screenshot_tenancy_OCID.png)
Select your user in the top right to see your user OCID as shown below.
![region-details](./pictures/screenshot_user_OCID.png)

View file

@ -631,6 +631,7 @@ string
</h3>
<p>
(<em>Appears on:</em>
<a href="#external-secrets.io/v1alpha1.KubernetesServer">KubernetesServer</a>,
<a href="#external-secrets.io/v1alpha1.VaultProvider">VaultProvider</a>)
</p>
<p>
@ -714,6 +715,44 @@ string
<td></td>
</tr></tbody>
</table>
<h3 id="external-secrets.io/v1alpha1.CertAuth">CertAuth
</h3>
<p>
(<em>Appears on:</em>
<a href="#external-secrets.io/v1alpha1.KubernetesAuth">KubernetesAuth</a>)
</p>
<p>
</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>clientCert</code></br>
<em>
github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
</em>
</td>
<td>
</td>
</tr>
<tr>
<td>
<code>clientKey</code></br>
<em>
github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
</em>
</td>
<td>
</td>
</tr>
</tbody>
</table>
<h3 id="external-secrets.io/v1alpha1.ClusterSecretStore">ClusterSecretStore
</h3>
<p>
@ -1979,6 +2018,179 @@ string
</tr>
</tbody>
</table>
<h3 id="external-secrets.io/v1alpha1.KubernetesAuth">KubernetesAuth
</h3>
<p>
(<em>Appears on:</em>
<a href="#external-secrets.io/v1alpha1.KubernetesProvider">KubernetesProvider</a>)
</p>
<p>
</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>cert</code></br>
<em>
<a href="#external-secrets.io/v1alpha1.CertAuth">
CertAuth
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>has both clientCert and clientKey as secretKeySelector</p>
</td>
</tr>
<tr>
<td>
<code>token</code></br>
<em>
<a href="#external-secrets.io/v1alpha1.TokenAuth">
TokenAuth
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>use static token to authenticate with</p>
</td>
</tr>
<tr>
<td>
<code>serviceAccount</code></br>
<em>
<a href="#external-secrets.io/v1alpha1.ServiceAccountAuth">
ServiceAccountAuth
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>points to a service account that should be used for authentication</p>
</td>
</tr>
</tbody>
</table>
<h3 id="external-secrets.io/v1alpha1.KubernetesProvider">KubernetesProvider
</h3>
<p>
(<em>Appears on:</em>
<a href="#external-secrets.io/v1alpha1.SecretStoreProvider">SecretStoreProvider</a>)
</p>
<p>
<p>Configures a store to sync secrets with a Kubernetes instance.</p>
</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>server</code></br>
<em>
<a href="#external-secrets.io/v1alpha1.KubernetesServer">
KubernetesServer
</a>
</em>
</td>
<td>
<p>configures the Kubernetes server Address.</p>
</td>
</tr>
<tr>
<td>
<code>auth</code></br>
<em>
<a href="#external-secrets.io/v1alpha1.KubernetesAuth">
KubernetesAuth
</a>
</em>
</td>
<td>
<p>Auth configures how secret-manager authenticates with a Kubernetes instance.</p>
</td>
</tr>
<tr>
<td>
<code>remoteNamespace</code></br>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>Remote namespace to fetch the secrets from</p>
</td>
</tr>
</tbody>
</table>
<h3 id="external-secrets.io/v1alpha1.KubernetesServer">KubernetesServer
</h3>
<p>
(<em>Appears on:</em>
<a href="#external-secrets.io/v1alpha1.KubernetesProvider">KubernetesProvider</a>)
</p>
<p>
</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>url</code></br>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>configures the Kubernetes server Address.</p>
</td>
</tr>
<tr>
<td>
<code>caBundle</code></br>
<em>
[]byte
</em>
</td>
<td>
<em>(Optional)</em>
<p>CABundle is a base64-encoded CA certificate</p>
</td>
</tr>
<tr>
<td>
<code>caProvider</code></br>
<em>
<a href="#external-secrets.io/v1alpha1.CAProvider">
CAProvider
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>see: <a href="https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider">https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider</a></p>
</td>
</tr>
</tbody>
</table>
<h3 id="external-secrets.io/v1alpha1.OracleAuth">OracleAuth
</h3>
<p>
@ -2418,6 +2630,20 @@ WebhookProvider
</tr>
<tr>
<td>
<code>kubernetes</code></br>
<em>
<a href="#external-secrets.io/v1alpha1.KubernetesProvider">
KubernetesProvider
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>Kubernetes configures this store to sync secrets using a Kubernetes cluster provider</p>
</td>
</tr>
<tr>
<td>
<code>fake</code></br>
<em>
<a href="#external-secrets.io/v1alpha1.FakeProvider">
@ -2683,6 +2909,34 @@ Kubernetes meta/v1.Time
</tr>
</tbody>
</table>
<h3 id="external-secrets.io/v1alpha1.ServiceAccountAuth">ServiceAccountAuth
</h3>
<p>
(<em>Appears on:</em>
<a href="#external-secrets.io/v1alpha1.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/v1alpha1.TemplateEngineVersion">TemplateEngineVersion
(<code>string</code> alias)</p></h3>
<p>
@ -2814,6 +3068,34 @@ string
</tr>
</tbody>
</table>
<h3 id="external-secrets.io/v1alpha1.TokenAuth">TokenAuth
</h3>
<p>
(<em>Appears on:</em>
<a href="#external-secrets.io/v1alpha1.KubernetesAuth">KubernetesAuth</a>)
</p>
<p>
</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>bearerToken</code></br>
<em>
github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
</em>
</td>
<td>
</td>
</tr>
</tbody>
</table>
<h3 id="external-secrets.io/v1alpha1.VaultAppRole">VaultAppRole
</h3>
<p>

27
go.mod
View file

@ -34,27 +34,27 @@ replace (
require (
cloud.google.com/go v0.100.2 // indirect
cloud.google.com/go/iam v0.2.0
cloud.google.com/go/iam v0.3.0
cloud.google.com/go/secretmanager v1.2.0
github.com/Azure/azure-sdk-for-go v61.5.0+incompatible
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11
github.com/IBM/go-sdk-core/v5 v5.9.2
github.com/IBM/secrets-manager-go-sdk v1.0.31
github.com/IBM/secrets-manager-go-sdk v1.0.36
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.2
github.com/PaesslerAG/jsonpath v0.1.1
github.com/ahmetb/gen-crd-api-reference-docs v0.3.0
github.com/akeylesslabs/akeyless-go-cloud-id v0.3.2
github.com/akeylesslabs/akeyless-go-cloud-id v0.3.4
github.com/akeylesslabs/akeyless-go/v2 v2.15.25
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1473
github.com/aws/aws-sdk-go v1.38.6
github.com/aws/aws-sdk-go v1.41.13
github.com/crossplane/crossplane-runtime v0.15.1
github.com/go-logr/logr v1.2.2
github.com/golang-jwt/jwt/v4 v4.2.0
github.com/google/go-cmp v0.5.7
github.com/google/uuid v1.2.0
github.com/google/uuid v1.3.0
github.com/googleapis/gax-go/v2 v2.1.1
github.com/hashicorp/vault/api v1.3.1
github.com/hashicorp/vault/api v1.4.1
github.com/huandu/xstrings v1.3.2 // indirect
github.com/lestrrat-go/jwx v1.2.1
github.com/onsi/ginkgo/v2 v2.1.3
@ -72,8 +72,8 @@ require (
go.uber.org/zap v1.21.0
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
google.golang.org/api v0.68.0
google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00
google.golang.org/api v0.70.0
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf
google.golang.org/grpc v1.44.0
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919
@ -88,7 +88,7 @@ require (
)
require (
cloud.google.com/go/compute v1.2.0 // indirect
cloud.google.com/go/compute v1.3.0 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.24 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect
@ -103,7 +103,6 @@ require (
github.com/armon/go-metrics v0.3.10 // indirect
github.com/armon/go-radix v1.0.0 // indirect
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect
github.com/aws/aws-sdk-go-v2 v0.23.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
@ -148,7 +147,7 @@ require (
github.com/hashicorp/go-version v1.4.0 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/hcl v1.0.1-vault-3 // indirect
github.com/hashicorp/vault/sdk v0.3.0 // indirect
github.com/hashicorp/vault/sdk v0.4.1 // indirect
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
@ -192,9 +191,9 @@ require (
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/mod v0.5.0 // indirect
golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d // indirect
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a // indirect
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect
golang.org/x/tools v0.1.7 // indirect

109
go.sum
View file

@ -37,15 +37,15 @@ cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUM
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
cloud.google.com/go/compute v1.2.0 h1:EKki8sSdvDU0OO9mAXGwPXOTOgPz2l08R0/IutDH11I=
cloud.google.com/go/compute v1.2.0/go.mod h1:xlogom/6gr8RJGBe7nT2eGsQYAFUbbv8dbC29qE3Xmw=
cloud.google.com/go/compute v1.3.0 h1:mPL/MzDDYHsh5tHRS9mhmhWlcgClCrCa6ApQCU6wnHI=
cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
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.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c=
cloud.google.com/go/iam v0.2.0 h1:Ouq6qif4mZdXkb3SiFMpxvu0JQJB1Yid9TsZ23N6hg8=
cloud.google.com/go/iam v0.2.0/go.mod h1:BCK88+tmjAwnZYfOSizmKCTSFjJHCa18t3DpdGEY13Y=
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=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
@ -89,11 +89,11 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp
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/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/IBM/go-sdk-core/v5 v5.8.0/go.mod h1:+YbdhrjCHC84ls4MeBp+Hj4NZCni+tDAc0XQUqRO9Jc=
github.com/IBM/go-sdk-core/v5 v5.9.1/go.mod h1:axE2JrRq79gIJTjKPBwV6gWHswvVptBjbcvvCPIxARM=
github.com/IBM/go-sdk-core/v5 v5.9.2 h1:QKB5JwhlZfRvFHqcOwMeu/Dis/Q7qCBxrQLhx04onMc=
github.com/IBM/go-sdk-core/v5 v5.9.2/go.mod h1:YlOwV9LeuclmT/qi/LAK2AsobbAP42veV0j68/rlZsE=
github.com/IBM/secrets-manager-go-sdk v1.0.31 h1:KRRyeEvlKkkZb90njgReOrK92+IyS6L19vpkzk27300=
github.com/IBM/secrets-manager-go-sdk v1.0.31/go.mod h1:0Juj6ER/LpDqJ49nw705MNyXSHsHodgztFdkXz5ttxs=
github.com/IBM/secrets-manager-go-sdk v1.0.36 h1:ZZYbGmyAk6hhOmw7KO0rE5Wul2w0dB6RC52lHoVsl24=
github.com/IBM/secrets-manager-go-sdk v1.0.36/go.mod h1:EsDl09sHI264poINfIdVvtemdgD/YFeSGs3kjvVFeHw=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
@ -112,8 +112,8 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/ahmetb/gen-crd-api-reference-docs v0.3.0 h1:+XfOU14S4bGuwyvCijJwhhBIjYN+YXS18jrCY2EzJaY=
github.com/ahmetb/gen-crd-api-reference-docs v0.3.0/go.mod h1:TdjdkYhlOifCQWPs1UdTma97kQQMozf5h26hTuG70u8=
github.com/akeylesslabs/akeyless-go-cloud-id v0.3.2 h1:1h4udX3Y5KgSG0m4Th2bHfaYxZB9fbngiij9PrKEp6c=
github.com/akeylesslabs/akeyless-go-cloud-id v0.3.2/go.mod h1:ionnWiARf5TYoFGuHS7syh51/7lYosZejpZbnECFJcU=
github.com/akeylesslabs/akeyless-go-cloud-id v0.3.4 h1:vTckjyBhHOBiOWSC/oaEU2Oo4OH5eAlQiwKu2RMxsFg=
github.com/akeylesslabs/akeyless-go-cloud-id v0.3.4/go.mod h1:As/RomC2w/fa3y+yHRlVHPmkbP+zrKBFRow41y5dk+E=
github.com/akeylesslabs/akeyless-go/v2 v2.15.25 h1:9iztd2fXVfj4b3LRsyvGNRheFoz8IFs/+K4GaAshZc4=
github.com/akeylesslabs/akeyless-go/v2 v2.15.25/go.mod h1:uOdXD49NCCe4rexeSc2aBU5Qv4KZgJE6YlbtYalvb+I=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@ -138,11 +138,8 @@ github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:l
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef h1:46PFijGLmAjMPwCCCo7Jf0W6f9slllCkkv7vyc1yOSg=
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=
github.com/aws/aws-sdk-go v1.38.6 h1:h0AKIaz/A1kEJ50HxCv7tL1GW+KbxYbp75+lZ/nvFOI=
github.com/aws/aws-sdk-go v1.38.6/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go-v2 v0.23.0 h1:+E1q1LLSfHSDn/DzOtdJOX+pLZE2HiNV2yO5AjZINwM=
github.com/aws/aws-sdk-go-v2 v0.23.0/go.mod h1:2LhT7UgHOXK3UXONKI5OMgIyoQL6zTAw/jwIeX6yqzw=
github.com/aws/aws-sdk-go v1.41.13 h1:wGgr6jkHdGExF33phfOqijFq7ZF+h7a6FXvJc77GpTc=
github.com/aws/aws-sdk-go v1.41.13/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
@ -286,7 +283,6 @@ github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
github.com/go-openapi/strfmt v0.20.2/go.mod h1:43urheQI9dNtE5lTZQfuFJvjYJKPrxicATpEfZwHUNk=
github.com/go-openapi/strfmt v0.21.1 h1:G6s2t5V5kGCHLVbSdZ/6lI8Wm4OzoPFkc3/cjAsKQrM=
github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
@ -297,40 +293,15 @@ github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
github.com/gobuffalo/flect v0.1.5/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
github.com/gobuffalo/flect v0.2.3 h1:f/ZukRnSNA/DUpSNDadko7Qc0PhGvsew35p/2tu+CRY=
github.com/gobuffalo/flect v0.2.3/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc=
github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
github.com/goccy/go-json v0.4.8 h1:TfwOxfSp8hXH+ivoOk36RyDNmXATUETRdaNWDaZglf8=
github.com/goccy/go-json v0.4.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@ -430,8 +401,8 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLe
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
@ -530,10 +501,10 @@ github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO2aRM=
github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw=
github.com/hashicorp/vault/sdk v0.3.0 h1:kR3dpxNkhh/wr6ycaJYqp6AFT/i2xaftbfnwZduTKEY=
github.com/hashicorp/vault/sdk v0.3.0/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0=
github.com/hashicorp/vault/api v1.4.1 h1:mWLfPT0RhxBitjKr6swieCEP2v5pp/M//t70S3kMLRo=
github.com/hashicorp/vault/api v1.4.1/go.mod h1:LkMdrZnWNrFaQyYYazWVn7KshilfDidgVBq6YiTq/bM=
github.com/hashicorp/vault/sdk v0.4.1 h1:3SaHOJY687jY1fnB61PtL0cOkKItphrbLmux7T92HBo=
github.com/hashicorp/vault/sdk v0.4.1/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I=
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
@ -559,7 +530,6 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
@ -576,16 +546,12 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
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/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
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=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@ -623,8 +589,6 @@ github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPK
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
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=
@ -729,7 +693,6 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
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/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
@ -779,8 +742,6 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
@ -797,8 +758,6 @@ github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXY
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
@ -818,7 +777,6 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
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=
@ -890,7 +848,6 @@ go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lL
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=
go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4=
go.mongodb.org/mongo-driver v1.5.1/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw=
go.mongodb.org/mongo-driver v1.7.5 h1:ny3p0reEpgsR2cfA5cjgwFZg3Cv/ofFh/8jbhGtz9VI=
go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@ -935,7 +892,6 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -1040,12 +996,13 @@ golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy
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-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d h1:1n1fc535VhN8SYtD4cDUyNlfpAF2ROMM9+11equK3hs=
golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
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=
@ -1071,7 +1028,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -1090,13 +1046,10 @@ golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1170,12 +1123,13 @@ golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBc
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=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a h1:ppl5mZgokTT8uPkmYOyEUmPTr3ypaKkg5eFOGrAmxxE=
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1204,9 +1158,6 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
@ -1308,10 +1259,9 @@ google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUb
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.66.0/go.mod h1:I1dmXYpX7HGwz/ejRxwQp2qj5bFAz93HiCU1C1oYd9M=
google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
google.golang.org/api v0.68.0 h1:9eJiHhwJKIYX6sX2fUZxQLi7pDRA/MYu8c12q6WbJik=
google.golang.org/api v0.68.0/go.mod h1:sOM8pTpwgflXRhz+oC8H2Dr+UcbMqkPPWNJo88Q7TH8=
google.golang.org/api v0.70.0 h1:67zQnAE0T2rB0A3CwLSas0K+SbVzSxP+zTLkQLexeiw=
google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
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=
@ -1391,12 +1341,11 @@ google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ6
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=
google.golang.org/genproto v0.0.0-20220114231437-d2e6a121cae0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220201184016-50beb8ab5c44/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220204002441-d6cc3cc0770e/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00 h1:zmf8Yq9j+IyTpps+paSkmHkSu5fJlRKy69LxRzc17Q0=
google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf h1:SVYXkUz2yZS9FWb2Gm8ivSlbNQzL2Z/NpPKE3RG2jWk=
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=

View file

@ -67,6 +67,10 @@ nav:
- FluxCD: examples-gitops-using-fluxcd.md
- Anchore Engine: examples-anchore-engine-credentials.md
- Jenkins: examples-jenkins-kubernetes-credentials.md
- External Resources:
- Talks: eso-talks.md
- Demos: eso-demos.md
- Blogs: eso-blogs.md
- References:
- API specification: spec.md
- Contributing:

View file

@ -15,28 +15,22 @@ package crds
import (
"context"
"encoding/json"
"time"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
)
const (
crdGroup = "apiextensions.k8s.io"
crdKind = "CustomResourceDefinition"
crdVersion = "v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
type testCase struct {
crd unstructured.Unstructured
crd2 unstructured.Unstructured
crd *apiextensions.CustomResourceDefinition
crd2 *apiextensions.CustomResourceDefinition
service corev1.Service
secret corev1.Secret
assert func()
@ -50,31 +44,27 @@ var _ = Describe("CRD reconcile", func() {
})
AfterEach(func() {
// To improve later on with proper clean up.
ctx := context.Background()
k8sClient.Delete(ctx, &test.secret)
k8sClient.Delete(ctx, &test.service)
deleteCRD(test.crd)
deleteCRD(test.crd2)
})
// a invalid provider config should be reflected
// in the store status condition
PatchesCRD := func(tc *testCase) {
tc.assert = func() {
Consistently(func() bool {
ss := unstructured.Unstructured{}
ss.SetGroupVersionKind(schema.GroupVersionKind{Kind: crdKind, Version: crdVersion, Group: crdGroup})
Eventually(func() bool {
crd := &apiextensions.CustomResourceDefinition{}
err := k8sClient.Get(context.Background(), types.NamespacedName{
Name: "secretstores.test.io",
}, &ss)
}, crd)
if err != nil {
return false
}
val, ok, err := unstructured.NestedString(ss.Object, "spec", "conversion", "webhook", "clientConfig", "service", "name")
if err != nil || !ok {
return false
}
want, ok, err := unstructured.NestedString(tc.crd.Object, "spec", "conversion", "webhook", "clientConfig", "service", "name")
if err != nil || !ok {
return false
}
return want != val
return crd.Spec.Conversion.Webhook.ClientConfig.Service.Name !=
tc.crd.Spec.Conversion.Webhook.ClientConfig.Service.Name
}).
WithTimeout(time.Second * 10).
WithPolling(time.Second).
@ -87,23 +77,15 @@ var _ = Describe("CRD reconcile", func() {
ignoreNonTargetCRDs := func(tc *testCase) {
tc.assert = func() {
Consistently(func() bool {
ss := unstructured.Unstructured{}
ss.SetGroupVersionKind(schema.GroupVersionKind{Kind: crdKind, Version: crdVersion, Group: crdGroup})
crd := &apiextensions.CustomResourceDefinition{}
err := k8sClient.Get(context.Background(), types.NamespacedName{
Name: "some-other.test.io",
}, &ss)
}, crd)
if err != nil {
return false
}
got, ok, err := unstructured.NestedString(ss.Object, "spec", "conversion", "webhook", "clientConfig", "service", "name")
if !ok || err != nil {
return false
}
want, ok, err := unstructured.NestedString(tc.crd2.Object, "spec", "conversion", "webhook", "clientConfig", "service", "name")
if !ok || err != nil {
return false
}
return got == want
return crd.Spec.Conversion.Webhook.ClientConfig.Service.Name ==
tc.crd2.Spec.Conversion.Webhook.ClientConfig.Service.Name
}).
WithTimeout(time.Second * 3).
WithPolling(time.Millisecond * 500).
@ -116,21 +98,47 @@ var _ = Describe("CRD reconcile", func() {
mut(test)
}
ctx := context.Background()
k8sClient.Create(ctx, &test.secret)
k8sClient.Create(ctx, &test.service)
k8sClient.Create(ctx, &test.crd)
k8sClient.Create(ctx, &test.crd2)
err := k8sClient.Create(ctx, &test.secret)
Expect(err).ToNot(HaveOccurred())
err = k8sClient.Create(ctx, &test.service)
Expect(err).ToNot(HaveOccurred())
err = k8sClient.Create(ctx, test.crd)
Expect(err).ToNot(HaveOccurred())
err = k8sClient.Create(ctx, test.crd2)
Expect(err).ToNot(HaveOccurred())
test.assert()
},
Entry("[namespace] Ignore non Target CRDs", ignoreNonTargetCRDs),
Entry("[namespace] Patch target CRDs", PatchesCRD),
)
})
func makeUnstructuredCRD(plural, group string) unstructured.Unstructured {
crd := apiextensions.CustomResourceDefinition{
func deleteCRD(crd *apiextensions.CustomResourceDefinition) {
err := k8sClient.Delete(context.Background(), crd, client.GracePeriodSeconds(0))
if err != nil && !apierrors.IsNotFound(err) {
Fail("unable to delete crd " + crd.Name)
return
}
Eventually(func() bool {
err := k8sClient.Get(context.Background(), types.NamespacedName{
Name: crd.Name,
}, crd)
if err == nil {
// force delete by removing finalizers
// note: we can not delete a CRD with an invalid caBundle field
cpy := crd.DeepCopy()
controllerutil.RemoveFinalizer(cpy, "customresourcecleanup.apiextensions.k8s.io")
p := client.MergeFrom(crd)
k8sClient.Patch(context.Background(), cpy, p)
return false
}
return apierrors.IsNotFound(err)
}).WithTimeout(time.Second * 5).WithPolling(time.Second).Should(BeTrue())
}
func makeCRD(plural, group string) *apiextensions.CustomResourceDefinition {
return &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: plural + "." + group,
},
@ -160,7 +168,7 @@ func makeUnstructuredCRD(plural, group string) unstructured.Unstructured {
Webhook: &apiextensions.WebhookConversion{
ConversionReviewVersions: []string{"v1"},
ClientConfig: &apiextensions.WebhookClientConfig{
CABundle: []byte("foobar"),
CABundle: []byte(`Cg==`),
Service: &apiextensions.ServiceReference{
Name: "webhook",
Namespace: "default",
@ -170,14 +178,6 @@ func makeUnstructuredCRD(plural, group string) unstructured.Unstructured {
},
},
}
marshal, _ := json.Marshal(crd)
unmarshal := make(map[string]interface{})
json.Unmarshal(marshal, &unmarshal)
u := unstructured.Unstructured{
Object: unmarshal,
}
u.SetGroupVersionKind(schema.GroupVersionKind{Kind: crdKind, Version: "v1", Group: crdGroup})
return u
}
func makeSecret() corev1.Secret {
@ -217,8 +217,8 @@ func makeDefaultTestcase() *testCase {
assert: func() {
// this is a noop by default
},
crd: makeUnstructuredCRD("secretstores", "test.io"),
crd2: makeUnstructuredCRD("some-other", "test.io"),
crd: makeCRD("secretstores", "test.io"),
crd2: makeCRD("some-other", "test.io"),
secret: makeSecret(),
service: makeService(),
}

View file

@ -22,19 +22,20 @@ import (
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
"math/big"
"net/http"
"os"
"path/filepath"
"sync"
"time"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
@ -49,31 +50,49 @@ const (
caKeyName = "ca.key"
certValidityDuration = 10 * 365 * 24 * time.Hour
LookaheadInterval = 90 * 24 * time.Hour
)
type WebhookType int
const (
Validating WebhookType = iota
Mutating
CRDConversion
errResNotReady = "resource not ready: %s"
errSubsetsNotReady = "subsets not ready"
errAddressesNotReady = "addresses not ready"
)
type Reconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
recorder record.EventRecorder
SvcName string
SvcNamespace string
SecretName string
SecretNamespace string
CrdResources []string
dnsName string
CAName string
CAOrganization string
RestartOnSecretRefresh bool
RequeueInterval time.Duration
Log logr.Logger
Scheme *runtime.Scheme
recorder record.EventRecorder
SvcName string
SvcNamespace string
SecretName string
SecretNamespace string
CrdResources []string
dnsName string
CAName string
CAOrganization string
RequeueInterval time.Duration
// the controller is ready when all crds are injected
rdyMu *sync.Mutex
readyStatusMap map[string]bool
}
func New(k8sClient client.Client, scheme *runtime.Scheme, logger logr.Logger,
interval time.Duration, svcName, svcNamespace, secretName, secretNamespace string, resources []string) *Reconciler {
return &Reconciler{
Client: k8sClient,
Log: logger,
Scheme: scheme,
SvcName: svcName,
SvcNamespace: svcNamespace,
SecretName: secretName,
SecretNamespace: secretNamespace,
RequeueInterval: interval,
CrdResources: resources,
CAName: "external-secrets",
CAOrganization: "external-secrets",
rdyMu: &sync.Mutex{},
readyStatusMap: map[string]bool{},
}
}
type CertInfo struct {
@ -82,10 +101,6 @@ type CertInfo struct {
KeyName string
CAName string
}
type WebhookInfo struct {
Name string
Type WebhookType
}
func contains(s []string, e string) bool {
for _, a := range s {
@ -95,44 +110,62 @@ func contains(s []string, e string) bool {
}
return false
}
func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := r.Log.WithValues("CustomResourceDefinition", req.NamespacedName)
if contains(r.CrdResources, req.NamespacedName.Name) {
err := r.updateCRD(ctx, req)
if err != nil {
log.Error(err, "failed to inject conversion webhook")
r.rdyMu.Lock()
r.readyStatusMap[req.NamespacedName.Name] = false
r.rdyMu.Unlock()
return ctrl.Result{}, err
}
r.rdyMu.Lock()
r.readyStatusMap[req.NamespacedName.Name] = true
r.rdyMu.Unlock()
}
return ctrl.Result{RequeueAfter: r.RequeueInterval}, nil
}
func (r *Reconciler) ConvertToWebhookInfo() []WebhookInfo {
info := make([]WebhookInfo, len(r.CrdResources))
for p, v := range r.CrdResources {
r := WebhookInfo{
Name: v,
Type: CRDConversion,
// ReadyCheck reviews if all webhook configs have been injected into the CRDs
// and if the referenced webhook service is ready.
func (r *Reconciler) ReadyCheck(_ *http.Request) error {
for _, res := range r.CrdResources {
r.rdyMu.Lock()
rdy := r.readyStatusMap[res]
r.rdyMu.Unlock()
if !rdy {
return fmt.Errorf(errResNotReady, res)
}
info[p] = r
}
return info
var eps corev1.Endpoints
err := r.Get(context.TODO(), types.NamespacedName{
Name: r.SvcName,
Namespace: r.SvcNamespace,
}, &eps)
if err != nil {
return err
}
if len(eps.Subsets) == 0 {
return fmt.Errorf(errSubsetsNotReady)
}
if len(eps.Subsets[0].Addresses) == 0 {
return fmt.Errorf(errAddressesNotReady)
}
return nil
}
func (r *Reconciler) SetupWithManager(mgr ctrl.Manager, opts controller.Options) error {
crdGVK := schema.GroupVersionKind{Group: "apiextensions.k8s.io", Version: "v1", Kind: "CustomResourceDefinition"}
res := &unstructured.Unstructured{}
res.SetGroupVersionKind(crdGVK)
r.recorder = mgr.GetEventRecorderFor("custom-resource-definition")
return ctrl.NewControllerManagedBy(mgr).
WithOptions(opts).
For(res).
For(&apiext.CustomResourceDefinition{}).
Complete(r)
}
func (r *Reconciler) updateCRD(ctx context.Context, req ctrl.Request) error {
crdGVK := schema.GroupVersionKind{Group: "apiextensions.k8s.io", Version: "v1", Kind: "CustomResourceDefinition"}
secret := corev1.Secret{}
secretName := types.NamespacedName{
Name: r.SecretName,
@ -142,16 +175,15 @@ func (r *Reconciler) updateCRD(ctx context.Context, req ctrl.Request) error {
if err != nil {
return err
}
updatedResource := &unstructured.Unstructured{}
updatedResource.SetGroupVersionKind(crdGVK)
if err := r.Get(ctx, req.NamespacedName, updatedResource); err != nil {
var updatedResource apiext.CustomResourceDefinition
if err := r.Get(ctx, req.NamespacedName, &updatedResource); err != nil {
return err
}
svc := types.NamespacedName{
Name: r.SvcName,
Namespace: r.SvcNamespace,
}
if err := injectSvcToConversionWebhook(updatedResource, svc); err != nil {
if err := injectService(&updatedResource, svc); err != nil {
return err
}
r.dnsName = fmt.Sprintf("%v.%v.svc", r.SvcName, r.SvcNamespace)
@ -164,45 +196,35 @@ func (r *Reconciler) updateCRD(ctx context.Context, req ctrl.Request) error {
if err != nil {
return err
}
if err := injectCertToConversionWebhook(updatedResource, artifacts.CertPEM); err != nil {
if err := injectCert(&updatedResource, artifacts.CertPEM); err != nil {
return err
}
}
if err := r.Update(ctx, updatedResource); err != nil {
if err := r.Update(ctx, &updatedResource); err != nil {
return err
}
return nil
}
func injectSvcToConversionWebhook(crd *unstructured.Unstructured, svc types.NamespacedName) error {
_, found, err := unstructured.NestedMap(crd.Object, "spec", "conversion", "webhook", "clientConfig")
if err != nil {
return err
}
if !found {
return errors.New("`conversion.webhook.clientConfig` field not found in CustomResourceDefinition")
}
if err := unstructured.SetNestedField(crd.Object, svc.Name, "spec", "conversion", "webhook", "clientConfig", "service", "name"); err != nil {
return err
}
if err := unstructured.SetNestedField(crd.Object, svc.Namespace, "spec", "conversion", "webhook", "clientConfig", "service", "namespace"); err != nil {
return err
func injectService(crd *apiext.CustomResourceDefinition, svc types.NamespacedName) error {
if crd.Spec.Conversion == nil ||
crd.Spec.Conversion.Webhook == nil ||
crd.Spec.Conversion.Webhook.ClientConfig == nil ||
crd.Spec.Conversion.Webhook.ClientConfig.Service == nil {
return fmt.Errorf("unexpected crd conversion webhook config")
}
crd.Spec.Conversion.Webhook.ClientConfig.Service.Namespace = svc.Namespace
crd.Spec.Conversion.Webhook.ClientConfig.Service.Name = svc.Name
return nil
}
func injectCertToConversionWebhook(crd *unstructured.Unstructured, certPem []byte) error {
_, found, err := unstructured.NestedMap(crd.Object, "spec", "conversion", "webhook", "clientConfig")
if err != nil {
return err
func injectCert(crd *apiext.CustomResourceDefinition, certPem []byte) error {
if crd.Spec.Conversion == nil ||
crd.Spec.Conversion.Webhook == nil ||
crd.Spec.Conversion.Webhook.ClientConfig == nil {
return fmt.Errorf("unexpected crd conversion webhook config")
}
if !found {
return errors.New("`conversion.webhook.clientConfig` field not found in CustomResourceDefinition")
}
if err := unstructured.SetNestedField(crd.Object, base64.StdEncoding.EncodeToString(certPem), "spec", "conversion", "webhook", "clientConfig", "caBundle"); err != nil {
return err
}
crd.Spec.Conversion.Webhook.ClientConfig.CABundle = certPem
return nil
}
@ -289,18 +311,12 @@ func (r *Reconciler) refreshCertIfNeeded(secret *corev1.Secret) (bool, error) {
if err := r.refreshCerts(true, secret); err != nil {
return false, err
}
if r.RestartOnSecretRefresh {
os.Exit(0)
}
return true, nil
}
if !r.validServerCert(secret.Data[caCertName], secret.Data[certName], secret.Data[keyName]) {
if err := r.refreshCerts(false, secret); err != nil {
return false, err
}
if r.RestartOnSecretRefresh {
os.Exit(0)
}
return true, nil
}
return true, nil
@ -450,21 +466,23 @@ func (r *Reconciler) writeSecret(cert, key []byte, caArtifacts *KeyPairArtifacts
return r.Update(context.Background(), secret)
}
// CheckCerts verifies that certificates exist in a given fs location
// and if they're valid.
func CheckCerts(c CertInfo, dnsName string, at time.Time) error {
certFile := c.CertDir + "/" + c.CertName
certFile := filepath.Join(c.CertDir, c.CertName)
_, err := os.Stat(certFile)
if err != nil {
return err
}
ca, err := os.ReadFile(c.CertDir + "/" + c.CAName)
ca, err := os.ReadFile(filepath.Join(c.CertDir, c.CAName))
if err != nil {
return err
}
cert, err := os.ReadFile(c.CertDir + "/" + c.CertName)
cert, err := os.ReadFile(filepath.Join(c.CertDir, c.CertName))
if err != nil {
return err
}
key, err := os.ReadFile(c.CertDir + "/" + c.KeyName)
key, err := os.ReadFile(filepath.Join(c.CertDir, c.KeyName))
if err != nil {
return err
}

View file

@ -18,7 +18,6 @@ import (
"context"
"crypto/rsa"
"crypto/x509"
"encoding/json"
"os"
"testing"
"time"
@ -26,15 +25,12 @@ import (
corev1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
client "sigs.k8s.io/controller-runtime/pkg/client/fake"
)
const (
setupError = "Could not setup test"
errorSearchingField = "Error when searching for field"
failedCreateCaCerts = "could not create ca certificates:%v"
failedCreateServerCerts = "could not create server certificates:%v"
invalidCerts = "generated certificates are invalid:%v,%v"
@ -92,21 +88,6 @@ func newCRD() apiextensionsv1.CustomResourceDefinition {
},
}
}
func TestConvertToWebhookInfo(t *testing.T) {
rec := newReconciler()
info := rec.ConvertToWebhookInfo()
if len(info) != 3 {
t.Errorf("Convert to WebhookInfo failed. Total resources:%d", len(info))
}
for _, v := range info {
if v.Type != CRDConversion {
t.Errorf("Convert to WebhookInfo failed. wrong type:%v", v.Type)
}
if v.Name != "one" && v.Name != "two" && v.Name != "three" {
t.Errorf("Convert to WebhookInfo failed. wrong name:%v", v.Name)
}
}
}
func TestUpdateCRD(t *testing.T) {
rec := newReconciler()
@ -123,51 +104,26 @@ func TestUpdateCRD(t *testing.T) {
}
err := rec.updateCRD(ctx, req)
if err != nil {
t.Errorf("Failed updating CRD:%v", err)
t.Errorf("Failed updating CRD: %v", err)
}
}
func TestInjectSvcToConversionWebhook(t *testing.T) {
svc := newService()
crd := newCRD()
crdunmarshalled := make(map[string]interface{})
crdJSON, err := json.Marshal(crd)
if err != nil {
t.Fatal(setupError)
}
err = json.Unmarshal(crdJSON, &crdunmarshalled)
if err != nil {
t.Fatal(setupError)
}
u := unstructured.Unstructured{
Object: crdunmarshalled,
}
name := types.NamespacedName{
Name: svc.Name,
Namespace: svc.Namespace,
}
err = injectSvcToConversionWebhook(&u, name)
err := injectService(&crd, name)
if err != nil {
t.Errorf("Failed: error when injecting: %v", err)
}
val, found, err := unstructured.NestedString(u.Object, "spec", "conversion", "webhook", "clientConfig", "service", "name")
if err != nil {
t.Error(errorSearchingField)
}
if !found {
t.Error("fieldNotFound")
}
val := crd.Spec.Conversion.Webhook.ClientConfig.Service.Name
if val != "foo" {
t.Errorf("Wrong service name injected: %v", val)
}
val, found, err = unstructured.NestedString(u.Object, "spec", "conversion", "webhook", "clientConfig", "service", "namespace")
if err != nil {
t.Error(errorSearchingField)
}
if !found {
t.Error("fieldNotFound")
}
val = crd.Spec.Conversion.Webhook.ClientConfig.Service.Namespace
if val != "default" {
t.Errorf("Wrong service namespace injected: %v", val)
}
@ -176,31 +132,12 @@ func TestInjectSvcToConversionWebhook(t *testing.T) {
func TestInjectCertToConversionWebhook(t *testing.T) {
certPEM := []byte("foobar")
crd := newCRD()
crdunmarshalled := make(map[string]interface{})
crdJSON, err := json.Marshal(crd)
if err != nil {
t.Fatal(setupError)
}
err = json.Unmarshal(crdJSON, &crdunmarshalled)
if err != nil {
t.Fatal(setupError)
}
u := unstructured.Unstructured{
Object: crdunmarshalled,
}
err = injectCertToConversionWebhook(&u, certPEM)
err := injectCert(&crd, certPEM)
if err != nil {
t.Errorf("Failed: error when injecting: %v", err)
}
val, found, err := unstructured.NestedString(u.Object, "spec", "conversion", "webhook", "clientConfig", "caBundle")
if err != nil {
t.Error(errorSearchingField)
}
if !found {
t.Error("fieldNotFound")
}
if val != "Zm9vYmFy" {
t.Errorf("Wrong certificate name injected: %v", val)
if string(crd.Spec.Conversion.Webhook.ClientConfig.CABundle) != "foobar" {
t.Errorf("Wrong certificate name injected: %v", string(crd.Spec.Conversion.Webhook.ClientConfig.CABundle))
}
}
func TestPopulateSecret(t *testing.T) {

View file

@ -18,6 +18,7 @@ import (
"context"
"path/filepath"
"testing"
"time"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@ -73,19 +74,11 @@ var _ = BeforeSuite(func() {
Expect(err).ToNot(HaveOccurred())
Expect(k8sClient).ToNot(BeNil())
err = (&Reconciler{
Client: k8sClient,
Scheme: k8sManager.GetScheme(),
Log: ctrl.Log.WithName("controllers").WithName("CustomResourceDefinition"),
SvcName: "foo",
SvcNamespace: "default",
SecretName: "foo",
SecretNamespace: "default",
CrdResources: []string{"externalsecrets.test.io", "secretstores.test.io", "clustersecretstores.test.io"},
CAName: "external-secrets",
CAOrganization: "external-secrets",
RestartOnSecretRefresh: false,
}).SetupWithManager(k8sManager, controller.Options{})
rec := New(k8sClient, k8sManager.GetScheme(), log, time.Second*1,
"foo", "default", "foo", "default", []string{
"secretstores.test.io",
})
rec.SetupWithManager(k8sManager, controller.Options{})
Expect(err).ToNot(HaveOccurred())
go func() {

View file

@ -36,11 +36,9 @@ import (
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
"github.com/external-secrets/external-secrets/pkg/controllers/secretstore"
"github.com/external-secrets/external-secrets/pkg/provider"
// Loading registered providers.
_ "github.com/external-secrets/external-secrets/pkg/provider/register"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
"github.com/external-secrets/external-secrets/pkg/utils"
)
@ -135,7 +133,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
return ctrl.Result{}, nil
}
storeProvider, err := schema.GetProvider(store)
storeProvider, err := esv1beta1.GetProvider(store)
if err != nil {
log.Error(err, errStoreProvider)
syncCallsError.With(syncCallsMetricLabels).Inc()
@ -393,19 +391,19 @@ func (r *Reconciler) getStore(ctx context.Context, externalSecret *esv1beta1.Ext
}
// getProviderSecretData returns the provider's secret data with the provided ExternalSecret.
func (r *Reconciler) getProviderSecretData(ctx context.Context, providerClient provider.SecretsClient, externalSecret *esv1beta1.ExternalSecret) (map[string][]byte, error) {
func (r *Reconciler) getProviderSecretData(ctx context.Context, providerClient esv1beta1.SecretsClient, externalSecret *esv1beta1.ExternalSecret) (map[string][]byte, error) {
providerData := make(map[string][]byte)
for _, remoteRef := range externalSecret.Spec.DataFrom {
var secretMap map[string][]byte
var err error
if len(remoteRef.Find.Tags) > 0 || remoteRef.Find.Name != nil {
secretMap, err = providerClient.GetAllSecrets(ctx, remoteRef.Find)
if remoteRef.Find != nil {
secretMap, err = providerClient.GetAllSecrets(ctx, *remoteRef.Find)
if err != nil {
return nil, fmt.Errorf(errGetSecretKey, remoteRef.Extract.Key, externalSecret.Name, err)
}
} else if remoteRef.Extract.Key != "" {
secretMap, err = providerClient.GetSecretMap(ctx, remoteRef.Extract)
} else if remoteRef.Extract != nil {
secretMap, err = providerClient.GetSecretMap(ctx, *remoteRef.Extract)
if err != nil {
return nil, fmt.Errorf(errGetSecretKey, remoteRef.Extract.Key, externalSecret.Name, err)
}

View file

@ -30,8 +30,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
"github.com/external-secrets/external-secrets/pkg/provider"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
"github.com/external-secrets/external-secrets/pkg/provider/testing/fake"
)
@ -187,7 +185,9 @@ var _ = Describe("ExternalSecret controller", func() {
}
return true
},
checkExternalSecret: func(es *esv1beta1.ExternalSecret) {},
checkExternalSecret: func(es *esv1beta1.ExternalSecret) {
// noop by default
},
secretStore: &esv1beta1.SecretStore{
ObjectMeta: metav1.ObjectMeta{
Name: ExternalSecretStore,
@ -540,7 +540,7 @@ var _ = Describe("ExternalSecret controller", func() {
}
tc.externalSecret.Spec.DataFrom = []esv1beta1.ExternalSecretDataFromRemoteRef{
{
Extract: esv1beta1.ExternalSecretDataRemoteRef{
Extract: &esv1beta1.ExternalSecretDataRemoteRef{
Key: "datamap",
},
},
@ -684,7 +684,7 @@ var _ = Describe("ExternalSecret controller", func() {
tc.externalSecret.Spec.Data = []esv1beta1.ExternalSecretData{}
tc.externalSecret.Spec.DataFrom = []esv1beta1.ExternalSecretDataFromRemoteRef{
{
Extract: esv1beta1.ExternalSecretDataRemoteRef{
Extract: &esv1beta1.ExternalSecretDataRemoteRef{
Key: remoteKey,
},
},
@ -726,7 +726,7 @@ var _ = Describe("ExternalSecret controller", func() {
tc.externalSecret.Spec.Data = []esv1beta1.ExternalSecretData{}
tc.externalSecret.Spec.DataFrom = []esv1beta1.ExternalSecretDataFromRemoteRef{
{
Extract: esv1beta1.ExternalSecretDataRemoteRef{
Extract: &esv1beta1.ExternalSecretDataRemoteRef{
Key: remoteKey,
},
},
@ -791,7 +791,7 @@ var _ = Describe("ExternalSecret controller", func() {
tc.externalSecret.Spec.Data = nil
tc.externalSecret.Spec.DataFrom = []esv1beta1.ExternalSecretDataFromRemoteRef{
{
Extract: esv1beta1.ExternalSecretDataRemoteRef{
Extract: &esv1beta1.ExternalSecretDataRemoteRef{
Key: remoteKey,
},
},
@ -813,7 +813,7 @@ var _ = Describe("ExternalSecret controller", func() {
tc.externalSecret.Spec.Data = nil
tc.externalSecret.Spec.DataFrom = []esv1beta1.ExternalSecretDataFromRemoteRef{
{
Find: esv1beta1.ExternalSecretFind{
Find: &esv1beta1.ExternalSecretFind{
Name: &esv1beta1.FindName{
RegExp: "foobar",
},
@ -844,7 +844,7 @@ var _ = Describe("ExternalSecret controller", func() {
tc.externalSecret.Spec.DataFrom = []esv1beta1.ExternalSecretDataFromRemoteRef{
{
Extract: esv1beta1.ExternalSecretDataRemoteRef{
Extract: &esv1beta1.ExternalSecretDataRemoteRef{
Key: remoteKey,
},
},
@ -925,7 +925,7 @@ var _ = Describe("ExternalSecret controller", func() {
// a SecretSyncedError status condition must be set
storeConstructErrCondition := func(tc *testCase) {
fakeProvider.WithNew(func(context.Context, esv1beta1.GenericStore, client.Client,
string) (provider.SecretsClient, error) {
string) (esv1beta1.SecretsClient, error) {
return nil, fmt.Errorf("artificial constructor error")
})
tc.checkCondition = func(es *esv1beta1.ExternalSecret) bool {
@ -1392,7 +1392,7 @@ func externalSecretConditionShouldBe(name, ns string, ct esv1beta1.ExternalSecre
func init() {
fakeProvider = fake.New()
schema.ForceRegister(fakeProvider, &esv1beta1.SecretStoreProvider{
esv1beta1.ForceRegister(fakeProvider, &esv1beta1.SecretStoreProvider{
AWS: &esv1beta1.AWSProvider{
Service: esv1beta1.AWSServiceSecretsManager,
},

View file

@ -25,7 +25,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
)
const (
@ -77,7 +76,7 @@ func reconcile(ctx context.Context, req ctrl.Request, ss esapi.GenericStore, cl
// if it fails sets a condition and writes events.
func validateStore(ctx context.Context, namespace string, store esapi.GenericStore,
client client.Client, recorder record.EventRecorder) error {
storeProvider, err := schema.GetProvider(store)
storeProvider, err := esapi.GetProvider(store)
if err != nil {
cond := NewSecretStoreCondition(esapi.SecretStoreReady, v1.ConditionFalse, esapi.ReasonInvalidStore, errUnableGetProvider)
SetExternalSecretCondition(store, *cond)

View file

@ -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 webhookconfig
import (
"context"
"path/filepath"
"testing"
"time"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/envtest"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
)
var cfg *rest.Config
var k8sClient client.Client
var testEnv *envtest.Environment
var cancel context.CancelFunc
var reconciler *Reconciler
const (
ctrlSvcName = "foo"
ctrlSvcNamespace = "default"
ctrlSecretName = "foo"
ctrlSecretNamespace = "default"
)
func TestAPIs(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Controller Suite")
}
var _ = BeforeSuite(func() {
log := zap.New(zap.WriteTo(GinkgoWriter))
logf.SetLogger(log)
By("bootstrapping test environment")
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "deploy", "crds")},
}
var ctx context.Context
ctx, cancel = context.WithCancel(context.Background())
var err error
cfg, err = testEnv.Start()
Expect(err).ToNot(HaveOccurred())
Expect(cfg).ToNot(BeNil())
err = esapi.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{
Scheme: scheme.Scheme,
MetricsBindAddress: "0", // avoid port collision when testing
})
Expect(err).ToNot(HaveOccurred())
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
Expect(err).ToNot(HaveOccurred())
Expect(k8sClient).ToNot(BeNil())
reconciler = New(k8sClient, k8sManager.GetScheme(), ctrl.Log, ctrlSvcName, ctrlSvcNamespace, ctrlSecretName, ctrlSecretNamespace, time.Second)
reconciler.SetupWithManager(k8sManager, controller.Options{})
Expect(err).ToNot(HaveOccurred())
go func() {
defer GinkgoRecover()
Expect(k8sManager.Start(ctx)).ToNot(HaveOccurred())
}()
})
var _ = AfterSuite(func() {
By("tearing down the test environment")
cancel() // stop manager
err := testEnv.Stop()
Expect(err).ToNot(HaveOccurred())
})

View file

@ -0,0 +1,186 @@
/*
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 webhookconfig
import (
"context"
"encoding/base64"
"fmt"
"net/http"
"strings"
"sync"
"time"
"github.com/go-logr/logr"
admissionregistration "k8s.io/api/admissionregistration/v1"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
)
type Reconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
recorder record.EventRecorder
RequeueDuration time.Duration
SvcName string
SvcNamespace string
SecretName string
SecretNamespace string
rdyMu *sync.Mutex
ready bool
}
func New(k8sClient client.Client, scheme *runtime.Scheme,
log logr.Logger, svcName, svcNamespace, secretName, secretNamespace string,
requeueInterval time.Duration) *Reconciler {
return &Reconciler{
Client: k8sClient,
Scheme: scheme,
Log: log,
RequeueDuration: requeueInterval,
SvcName: svcName,
SvcNamespace: svcNamespace,
SecretName: secretName,
SecretNamespace: secretNamespace,
rdyMu: &sync.Mutex{},
ready: false,
}
}
const (
wellKnownLabelKey = "external-secrets.io/component"
wellKnownLabelValue = "webhook"
ReasonUpdateFailed = "UpdateFailed"
errWebhookNotReady = "webhook not ready"
errSubsetsNotReady = "subsets not ready"
errAddressesNotReady = "addresses not ready"
errCACertNotReady = "ca cert not yet ready"
caCertName = "ca.crt"
)
func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := r.Log.WithValues("Webhookconfig", req.NamespacedName)
var cfg admissionregistration.ValidatingWebhookConfiguration
err := r.Get(ctx, req.NamespacedName, &cfg)
if apierrors.IsNotFound(err) {
return ctrl.Result{}, nil
} else if err != nil {
log.Error(err, "unable to get Webhookconfig")
return ctrl.Result{}, err
}
if cfg.Labels[wellKnownLabelKey] != wellKnownLabelValue {
log.Info("ignoring webhook due to missing labels", wellKnownLabelKey, wellKnownLabelValue)
return ctrl.Result{}, nil
}
log.Info("updating webhook config")
err = r.updateConfig(ctx, &cfg)
if err != nil {
log.Error(err, "could not update webhook config")
r.recorder.Eventf(&cfg, v1.EventTypeWarning, ReasonUpdateFailed, err.Error())
return ctrl.Result{
RequeueAfter: time.Minute,
}, err
}
log.Info("updated webhook config")
// right now we only have one single
// webhook config we care about
r.rdyMu.Lock()
defer r.rdyMu.Unlock()
r.ready = true
return ctrl.Result{
RequeueAfter: r.RequeueDuration,
}, nil
}
func (r *Reconciler) SetupWithManager(mgr ctrl.Manager, opts controller.Options) error {
r.recorder = mgr.GetEventRecorderFor("validating-webhook-configuration")
return ctrl.NewControllerManagedBy(mgr).
WithOptions(opts).
For(&admissionregistration.ValidatingWebhookConfiguration{}).
Complete(r)
}
func (r *Reconciler) ReadyCheck(_ *http.Request) error {
r.rdyMu.Lock()
defer r.rdyMu.Unlock()
if !r.ready {
return fmt.Errorf(errWebhookNotReady)
}
var eps v1.Endpoints
err := r.Get(context.TODO(), types.NamespacedName{
Name: r.SvcName,
Namespace: r.SvcNamespace,
}, &eps)
if err != nil {
return err
}
if len(eps.Subsets) == 0 {
return fmt.Errorf(errSubsetsNotReady)
}
if len(eps.Subsets[0].Addresses) == 0 {
return fmt.Errorf(errAddressesNotReady)
}
return nil
}
// reads the ca cert and updates the webhook config.
func (r *Reconciler) updateConfig(ctx context.Context, cfg *admissionregistration.ValidatingWebhookConfiguration) error {
secret := v1.Secret{}
secretName := types.NamespacedName{
Name: r.SecretName,
Namespace: r.SecretNamespace,
}
err := r.Get(context.Background(), secretName, &secret)
if err != nil {
return err
}
crt, ok := secret.Data[caCertName]
if !ok {
return fmt.Errorf(errCACertNotReady)
}
if err := r.inject(cfg, r.SvcName, r.SvcNamespace, crt); err != nil {
return err
}
return r.Update(ctx, cfg)
}
func (r *Reconciler) inject(cfg *admissionregistration.ValidatingWebhookConfiguration, svcName, svcNamespace string, certData []byte) error {
r.Log.Info("injecting ca certificate and service names", "cacrt", base64.StdEncoding.EncodeToString(certData), "name", cfg.Name)
for idx, w := range cfg.Webhooks {
if !strings.HasSuffix(w.Name, "external-secrets.io") {
r.Log.Info("skipping webhook", "name", cfg.Name, "webhook-name", w.Name)
continue
}
// we just patch the relevant fields
cfg.Webhooks[idx].ClientConfig.Service.Name = svcName
cfg.Webhooks[idx].ClientConfig.Service.Namespace = svcNamespace
cfg.Webhooks[idx].ClientConfig.CABundle = certData
}
return nil
}

View file

@ -0,0 +1,324 @@
/*
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 webhookconfig
import (
"bytes"
"context"
"time"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
admissionregistration "k8s.io/api/admissionregistration/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/pointer"
)
const defaultCACert = `-----BEGIN CERTIFICATE-----
MIIDRjCCAi6gAwIBAgIBADANBgkqhkiG9w0BAQsFADA2MRkwFwYDVQQKExBleHRl
cm5hbC1zZWNyZXRzMRkwFwYDVQQDExBleHRlcm5hbC1zZWNyZXRzMB4XDTIyMDIx
NzEwMDYxMFoXDTMyMDIxNTExMDYxMFowNjEZMBcGA1UEChMQZXh0ZXJuYWwtc2Vj
cmV0czEZMBcGA1UEAxMQZXh0ZXJuYWwtc2VjcmV0czCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAKSINgqU2dBdX8JpPjRHWSdpxuoltGl6xXmQHOhbTXAt
/STDu7oi6eiFgepQ2QHuWLGwZgbbYnEhtLvw4dUwPcLyv6WIdeiUSA4pdFxL7asc
WV4tjiRkRTJVrixJTxXpry/CsPqXBlvnu1YGESkrLOYCmA2xnDH8voEBbwYvXXB9
3g5rOJncSh/7g+H55ZFFyWrIPyDUnfwE3CREjZXpsagFhRYpkuRlXTnU6t0OTEEh
qLHlZ+ebUzL8NaegEgEHD32PrQPXpls1yurIrsA+I6McWkXGykykYHVK+1a1pL1g
e+PBkegtwtX+EmB2ux7PVVeB4TTYqzCKbnObW4mJLZkCAwEAAaNfMF0wDgYDVR0P
AQH/BAQDAgKkMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHgSu/Im2gyu4TU0
AWrMSFbtoVokMBsGA1UdEQQUMBKCEGV4dGVybmFsLXNlY3JldHMwDQYJKoZIhvcN
AQELBQADggEBAJU88jCcPsAHN8DKLu+QMCoKYbeftX4gXxyoijGSde2w2O8NPtMP
awu4Y5x3LNTwyIIxXi78UD0RI53GbUgHvS+X9v6CC2IZMS65xqKR+EsjzEh7Ldbm
vZoF4ZDnfb2s5SK6MeYf67BE7XWpGfbHmjt6h80xsYjL6ovcik+dlu/AixMyLslS
tDbMybAR8kR0zdQLYcZq7XEX5QsOO8qBn5rTfD6MiYik8ZrP7FqUMHyVpHiBuNio
krnSOvynvuA9mlf2F2727dMt2Ij9uER+9QnhWBQex1h8CwALmm2k9G5Gt+RjB8oe
lNjvmHAXUfOE/cbD7EP++X17kWt41FjmePc=
-----END CERTIFICATE-----
`
type testCase struct {
vwc *admissionregistration.ValidatingWebhookConfiguration
service *corev1.Service
endpoints *corev1.Endpoints
secret *corev1.Secret
assert func()
}
var _ = Describe("ValidatingWebhookConfig reconcile", Ordered, func() {
var test *testCase
BeforeEach(func() {
test = makeDefaultTestcase()
})
AfterEach(func() {
ctx := context.Background()
k8sClient.Delete(ctx, test.vwc)
k8sClient.Delete(ctx, test.secret)
k8sClient.Delete(ctx, test.service)
k8sClient.Delete(ctx, test.endpoints)
})
// Should patch VWC
PatchAndReady := func(tc *testCase) {
tc.endpoints.Subsets = nil
// endpoints become ready in a moment
go func() {
<-time.After(time.Second * 4)
eps := makeEndpoints()
err := k8sClient.Update(context.Background(), eps)
Expect(err).ToNot(HaveOccurred())
}()
tc.assert = func() {
Eventually(func() bool {
// the controller should become ready at some point!
err := reconciler.ReadyCheck(nil)
return err == nil
}).
WithTimeout(time.Second * 10).
WithPolling(time.Second).
Should(BeTrue())
Eventually(func() bool {
var vwc admissionregistration.ValidatingWebhookConfiguration
err := k8sClient.Get(context.Background(), types.NamespacedName{
Name: tc.vwc.Name,
}, &vwc)
if err != nil {
return false
}
for _, wc := range vwc.Webhooks {
if !bytes.Equal(wc.ClientConfig.CABundle, []byte(defaultCACert)) {
return false
}
if wc.ClientConfig.Service == nil {
return false
}
if wc.ClientConfig.Service.Name != ctrlSvcName {
return false
}
if wc.ClientConfig.Service.Namespace != ctrlSvcNamespace {
return false
}
}
return true
}).
WithTimeout(time.Second * 10).
WithPolling(time.Second).
Should(BeTrue())
}
}
IgnoreNoMatch := func(tc *testCase) {
delete(tc.vwc.ObjectMeta.Labels, wellKnownLabelKey)
tc.assert = func() {
Consistently(func() bool {
var vwc admissionregistration.ValidatingWebhookConfiguration
err := k8sClient.Get(context.Background(), types.NamespacedName{
Name: tc.vwc.Name,
}, &vwc)
if err != nil {
return false
}
for _, wc := range vwc.Webhooks {
if bytes.Equal(wc.ClientConfig.CABundle, []byte(defaultCACert)) {
return false
}
if wc.ClientConfig.Service.Name == ctrlSvcName {
return false
}
if wc.ClientConfig.Service.Namespace == ctrlSvcNamespace {
return false
}
}
return true
}).
WithTimeout(time.Second * 10).
WithPolling(time.Second).
Should(BeTrue())
}
}
// Should patch and update VWC after requeue duration has passed
PatchAndUpdate := func(tc *testCase) {
foobar := "new value"
// ca cert will change after some time
go func() {
<-time.After(time.Second * 4)
sec := makeSecret()
sec.Data[caCertName] = []byte(foobar)
err := k8sClient.Update(context.Background(), sec)
Expect(err).ToNot(HaveOccurred())
}()
tc.assert = func() {
Eventually(func() bool {
var vwc admissionregistration.ValidatingWebhookConfiguration
err := k8sClient.Get(context.Background(), types.NamespacedName{
Name: tc.vwc.Name,
}, &vwc)
if err != nil {
return false
}
for _, wc := range vwc.Webhooks {
if !bytes.Equal(wc.ClientConfig.CABundle, []byte(foobar)) {
return false
}
if wc.ClientConfig.Service == nil {
return false
}
if wc.ClientConfig.Service.Name != ctrlSvcName {
return false
}
if wc.ClientConfig.Service.Namespace != ctrlSvcNamespace {
return false
}
}
return true
}).
WithTimeout(time.Second * 10).
WithPolling(time.Second).
Should(BeTrue())
}
}
DescribeTable("Controller Reconcile logic", func(muts ...func(tc *testCase)) {
for _, mut := range muts {
mut(test)
}
ctx := context.Background()
err := k8sClient.Create(ctx, test.vwc)
Expect(err).ToNot(HaveOccurred())
err = k8sClient.Create(ctx, test.secret)
Expect(err).ToNot(HaveOccurred())
err = k8sClient.Create(ctx, test.service)
Expect(err).ToNot(HaveOccurred())
err = k8sClient.Create(ctx, test.endpoints)
Expect(err).ToNot(HaveOccurred())
test.assert()
},
Entry("should patch matching webhook configs", PatchAndReady),
Entry("should update vwc with new ca cert after requeue duration", PatchAndUpdate),
Entry("should ignore when vwc labels are missing", IgnoreNoMatch),
)
})
func makeValidatingWebhookConfig() *admissionregistration.ValidatingWebhookConfiguration {
return &admissionregistration.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: "name-shouldnt-matter",
Labels: map[string]string{
wellKnownLabelKey: wellKnownLabelValue,
},
},
Webhooks: []admissionregistration.ValidatingWebhook{
{
Name: "secretstores.external-secrets.io",
SideEffects: (*admissionregistration.SideEffectClass)(pointer.StringPtr(string(admissionregistration.SideEffectClassNone))),
AdmissionReviewVersions: []string{"v1"},
ClientConfig: admissionregistration.WebhookClientConfig{
CABundle: []byte("Cg=="),
Service: &admissionregistration.ServiceReference{
Name: "noop",
Namespace: "noop",
Path: pointer.StringPtr("/validate-secretstore"),
},
},
},
{
Name: "clustersecretstores.external-secrets.io",
SideEffects: (*admissionregistration.SideEffectClass)(pointer.StringPtr(string(admissionregistration.SideEffectClassNone))),
AdmissionReviewVersions: []string{"v1"},
ClientConfig: admissionregistration.WebhookClientConfig{
CABundle: []byte("Cg=="),
Service: &admissionregistration.ServiceReference{
Name: "noop",
Namespace: "noop",
Path: pointer.StringPtr("/validate-clustersecretstore"),
},
},
},
},
}
}
func makeSecret() *corev1.Secret {
return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: ctrlSecretName,
Namespace: ctrlSecretNamespace,
},
Data: map[string][]byte{
caCertName: []byte(defaultCACert),
},
}
}
func makeService() *corev1.Service {
return &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: ctrlSvcName,
Namespace: ctrlSvcNamespace,
},
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{
{
Name: "http",
Port: 80,
},
},
},
}
}
func makeEndpoints() *corev1.Endpoints {
return &corev1.Endpoints{
ObjectMeta: metav1.ObjectMeta{
Name: ctrlSvcName,
Namespace: ctrlSvcNamespace,
},
Subsets: []corev1.EndpointSubset{
{
Addresses: []corev1.EndpointAddress{
{
IP: "1.2.3.4",
},
},
},
},
}
}
func makeDefaultTestcase() *testCase {
return &testCase{
assert: func() {
// this is a noop by default
},
vwc: makeValidatingWebhookConfig(),
secret: makeSecret(),
service: makeService(),
endpoints: makeEndpoints(),
}
}

View file

@ -24,8 +24,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
"github.com/external-secrets/external-secrets/pkg/provider"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
"github.com/external-secrets/external-secrets/pkg/utils"
)
@ -56,17 +54,21 @@ type akeylessVaultInterface interface {
}
func init() {
schema.Register(&Provider{}, &esv1beta1.SecretStoreProvider{
esv1beta1.Register(&Provider{}, &esv1beta1.SecretStoreProvider{
Akeyless: &esv1beta1.AkeylessProvider{},
})
}
// NewClient constructs a new secrets client based on the provided store.
func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) {
func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
return newClient(ctx, store, kube, namespace)
}
func newClient(_ context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) {
func (p *Provider) ValidateStore(store esv1beta1.GenericStore) error {
return nil
}
func newClient(_ context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
akl := &akeylessBase{
kube: kube,
store: store,

View file

@ -26,9 +26,7 @@ import (
kclient "sigs.k8s.io/controller-runtime/pkg/client"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
"github.com/external-secrets/external-secrets/pkg/provider"
"github.com/external-secrets/external-secrets/pkg/provider/aws/util"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
"github.com/external-secrets/external-secrets/pkg/utils"
)
@ -165,7 +163,7 @@ func (kms *KeyManagementService) GetSecretMap(ctx context.Context, ref esv1beta1
}
// NewClient constructs a new secrets client based on the provided store.
func (kms *KeyManagementService) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (provider.SecretsClient, error) {
func (kms *KeyManagementService) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
storeSpec := store.GetSpec()
alibabaSpec := storeSpec.Provider.Alibaba
iStore := &Client{
@ -196,8 +194,12 @@ func (kms *KeyManagementService) Validate() error {
return nil
}
func (kms *KeyManagementService) ValidateStore(store esv1beta1.GenericStore) error {
return nil
}
func init() {
schema.Register(&KeyManagementService{}, &esv1beta1.SecretStoreProvider{
esv1beta1.Register(&KeyManagementService{}, &esv1beta1.SecretStoreProvider{
Alibaba: &esv1beta1.AlibabaProvider{},
})
}

View file

@ -18,15 +18,15 @@ import (
"context"
"fmt"
"github.com/aws/aws-sdk-go/aws/endpoints"
"sigs.k8s.io/controller-runtime/pkg/client"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
"github.com/external-secrets/external-secrets/pkg/provider"
awsauth "github.com/external-secrets/external-secrets/pkg/provider/aws/auth"
"github.com/external-secrets/external-secrets/pkg/provider/aws/parameterstore"
"github.com/external-secrets/external-secrets/pkg/provider/aws/secretsmanager"
"github.com/external-secrets/external-secrets/pkg/provider/aws/util"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
"github.com/external-secrets/external-secrets/pkg/utils"
)
// Provider satisfies the provider interface.
@ -35,14 +35,62 @@ type Provider struct{}
const (
errUnableCreateSession = "unable to create session: %w"
errUnknownProviderService = "unknown AWS Provider Service: %s"
errRegionNotFound = "region not found: %s"
)
// NewClient constructs a new secrets client based on the provided store.
func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) {
func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
return newClient(ctx, store, kube, namespace, awsauth.DefaultSTSProvider)
}
func newClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string, assumeRoler awsauth.STSProvider) (provider.SecretsClient, error) {
func (p *Provider) ValidateStore(store esv1beta1.GenericStore) error {
prov, err := util.GetAWSProvider(store)
if err != nil {
return err
}
err = validateRegion(prov)
if err != nil {
return err
}
// case: static credentials
if prov.Auth.SecretRef != nil {
if err := utils.ValidateSecretSelector(store, prov.Auth.SecretRef.AccessKeyID); err != nil {
return fmt.Errorf("invalid Auth.SecretRef.AccessKeyID: %w", err)
}
if err := utils.ValidateSecretSelector(store, prov.Auth.SecretRef.SecretAccessKey); err != nil {
return fmt.Errorf("invalid Auth.SecretRef.SecretAccessKey: %w", err)
}
}
// case: jwt credentials
if prov.Auth.JWTAuth != nil && prov.Auth.JWTAuth.ServiceAccountRef != nil {
if err := utils.ValidateServiceAccountSelector(store, *prov.Auth.JWTAuth.ServiceAccountRef); err != nil {
return fmt.Errorf("invalid Auth.JWT.ServiceAccountRef: %w", err)
}
}
return nil
}
func validateRegion(prov *esv1beta1.AWSProvider) error {
resolver := endpoints.DefaultResolver()
partitions := resolver.(endpoints.EnumPartitions).Partitions()
found := false
for _, p := range partitions {
for id := range p.Regions() {
if id == prov.Region {
found = true
}
}
}
if !found {
return fmt.Errorf(errRegionNotFound, prov.Region)
}
return nil
}
func newClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string, assumeRoler awsauth.STSProvider) (esv1beta1.SecretsClient, error) {
prov, err := util.GetAWSProvider(store)
if err != nil {
return nil, err
@ -63,7 +111,7 @@ func newClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Cl
}
func init() {
schema.Register(&Provider{}, &esv1beta1.SecretStoreProvider{
esv1beta1.Register(&Provider{}, &esv1beta1.SecretStoreProvider{
AWS: &esv1beta1.AWSProvider{},
})
}

View file

@ -21,6 +21,8 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/stretchr/testify/assert"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/pointer"
clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
@ -146,3 +148,198 @@ func TestProvider(t *testing.T) {
})
}
}
const validRegion = "eu-central-1"
func TestValidateStore(t *testing.T) {
type args struct {
store esv1beta1.GenericStore
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "invalid region",
wantErr: true,
args: args{
store: &esv1beta1.SecretStore{
Spec: esv1beta1.SecretStoreSpec{
Provider: &esv1beta1.SecretStoreProvider{
AWS: &esv1beta1.AWSProvider{
Region: "noop.",
},
},
},
},
},
},
{
name: "valid region",
args: args{
store: &esv1beta1.SecretStore{
Spec: esv1beta1.SecretStoreSpec{
Provider: &esv1beta1.SecretStoreProvider{
AWS: &esv1beta1.AWSProvider{
Region: validRegion,
},
},
},
},
},
},
{
name: "invalid static creds auth / AccessKeyID",
wantErr: true,
args: args{
store: &esv1beta1.SecretStore{
Spec: esv1beta1.SecretStoreSpec{
Provider: &esv1beta1.SecretStoreProvider{
AWS: &esv1beta1.AWSProvider{
Region: validRegion,
Auth: esv1beta1.AWSAuth{
SecretRef: &esv1beta1.AWSAuthSecretRef{
AccessKeyID: esmeta.SecretKeySelector{
Name: "foobar",
Namespace: pointer.StringPtr("unacceptable"),
},
},
},
},
},
},
},
},
},
{
name: "invalid static creds auth / SecretAccessKey",
wantErr: true,
args: args{
store: &esv1beta1.SecretStore{
Spec: esv1beta1.SecretStoreSpec{
Provider: &esv1beta1.SecretStoreProvider{
AWS: &esv1beta1.AWSProvider{
Region: validRegion,
Auth: esv1beta1.AWSAuth{
SecretRef: &esv1beta1.AWSAuthSecretRef{
SecretAccessKey: esmeta.SecretKeySelector{
Name: "foobar",
Namespace: pointer.StringPtr("unacceptable"),
},
},
},
},
},
},
},
},
},
{
name: "invalid static creds auth / SecretAccessKey missing namespace",
wantErr: true,
args: args{
store: &esv1beta1.ClusterSecretStore{
TypeMeta: v1.TypeMeta{
Kind: esv1beta1.ClusterSecretStoreKind,
},
Spec: esv1beta1.SecretStoreSpec{
Provider: &esv1beta1.SecretStoreProvider{
AWS: &esv1beta1.AWSProvider{
Region: validRegion,
Auth: esv1beta1.AWSAuth{
SecretRef: &esv1beta1.AWSAuthSecretRef{
SecretAccessKey: esmeta.SecretKeySelector{
Name: "foobar",
},
},
},
},
},
},
},
},
},
{
name: "invalid static creds auth / AccessKeyID missing namespace",
wantErr: true,
args: args{
store: &esv1beta1.ClusterSecretStore{
TypeMeta: v1.TypeMeta{
Kind: esv1beta1.ClusterSecretStoreKind,
},
Spec: esv1beta1.SecretStoreSpec{
Provider: &esv1beta1.SecretStoreProvider{
AWS: &esv1beta1.AWSProvider{
Region: validRegion,
Auth: esv1beta1.AWSAuth{
SecretRef: &esv1beta1.AWSAuthSecretRef{
AccessKeyID: esmeta.SecretKeySelector{
Name: "foobar",
},
},
},
},
},
},
},
},
},
{
name: "invalid jwt auth: missing sa selector namespace",
wantErr: true,
args: args{
store: &esv1beta1.ClusterSecretStore{
TypeMeta: v1.TypeMeta{
Kind: esv1beta1.ClusterSecretStoreKind,
},
Spec: esv1beta1.SecretStoreSpec{
Provider: &esv1beta1.SecretStoreProvider{
AWS: &esv1beta1.AWSProvider{
Region: validRegion,
Auth: esv1beta1.AWSAuth{
JWTAuth: &esv1beta1.AWSJWTAuth{
ServiceAccountRef: &esmeta.ServiceAccountSelector{
Name: "foobar",
},
},
},
},
},
},
},
},
},
{
name: "invalid jwt auth: not allowed sa selector namespace",
wantErr: true,
args: args{
store: &esv1beta1.SecretStore{
Spec: esv1beta1.SecretStoreSpec{
Provider: &esv1beta1.SecretStoreProvider{
AWS: &esv1beta1.AWSProvider{
Region: validRegion,
Auth: esv1beta1.AWSAuth{
JWTAuth: &esv1beta1.AWSJWTAuth{
ServiceAccountRef: &esmeta.ServiceAccountSelector{
Name: "foobar",
Namespace: pointer.StringPtr("unacceptable"),
},
},
},
},
},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := &Provider{}
if err := p.ValidateStore(tt.args.store); (err != nil) != tt.wantErr {
t.Errorf("Provider.ValidateStore() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

View file

@ -42,6 +42,10 @@ func (sm *Client) GetSecretValue(in *awssm.GetSecretValueInput) (*awssm.GetSecre
return nil, fmt.Errorf("test case not found")
}
func (sm *Client) ListSecrets(*awssm.ListSecretsInput) (*awssm.ListSecretsOutput, error) {
return nil, nil
}
func (sm *Client) cacheKeyForInput(in *awssm.GetSecretValueInput) string {
var secretID, versionID string
if in.SecretId != nil {

View file

@ -32,8 +32,7 @@ import (
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
smmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
"github.com/external-secrets/external-secrets/pkg/provider"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
"github.com/external-secrets/external-secrets/pkg/utils"
)
const (
@ -54,6 +53,13 @@ const (
errMissingClientIDSecret = "missing accessKeyID/secretAccessKey in store config"
errFindSecret = "could not find secret %s/%s: %w"
errFindDataKey = "no data for %q in secret '%s/%s'"
errInvalidStore = "invalid store"
errInvalidStoreSpec = "invalid store spec"
errInvalidStoreProv = "invalid store provider"
errInvalidAzureProv = "invalid azure keyvault provider"
errInvalidSecRefClientID = "invalid AuthSecretRef.ClientID: %w"
errInvalidSecRefClientSecret = "invalid AuthSecretRef.ClientSecret: %w"
)
// interface to keyvault.BaseClient.
@ -73,17 +79,17 @@ type Azure struct {
}
func init() {
schema.Register(&Azure{}, &esv1beta1.SecretStoreProvider{
esv1beta1.Register(&Azure{}, &esv1beta1.SecretStoreProvider{
AzureKV: &esv1beta1.AzureKVProvider{},
})
}
// NewClient constructs a new secrets client based on the provided store.
func (a *Azure) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) {
func (a *Azure) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
return newClient(ctx, store, kube, namespace)
}
func newClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) {
func newClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
provider, err := getProvider(store)
if err != nil {
return nil, err
@ -117,6 +123,36 @@ func getProvider(store esv1beta1.GenericStore) (*esv1beta1.AzureKVProvider, erro
return spc.Provider.AzureKV, nil
}
func (a *Azure) ValidateStore(store esv1beta1.GenericStore) error {
if store == nil {
return fmt.Errorf(errInvalidStore)
}
spc := store.GetSpec()
if spc == nil {
return fmt.Errorf(errInvalidStoreSpec)
}
if spc.Provider == nil {
return fmt.Errorf(errInvalidStoreProv)
}
p := spc.Provider.AzureKV
if p == nil {
return fmt.Errorf(errInvalidAzureProv)
}
if p.AuthSecretRef != nil {
if p.AuthSecretRef.ClientID != nil {
if err := utils.ValidateSecretSelector(store, *p.AuthSecretRef.ClientID); err != nil {
return fmt.Errorf(errInvalidSecRefClientID, err)
}
}
if p.AuthSecretRef.ClientSecret != nil {
if err := utils.ValidateSecretSelector(store, *p.AuthSecretRef.ClientSecret); err != nil {
return fmt.Errorf(errInvalidSecRefClientSecret, err)
}
}
}
return nil
}
// Implements store.Client.GetAllSecrets Interface.
// Retrieves a map[string][]byte with the secret names as key and the secret itself as the calue.
func (a *Azure) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {

View file

@ -31,7 +31,6 @@ import (
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
v1 "github.com/external-secrets/external-secrets/apis/meta/v1"
fake "github.com/external-secrets/external-secrets/pkg/provider/azure/keyvault/fake"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
utils "github.com/external-secrets/external-secrets/pkg/utils"
)
@ -104,7 +103,7 @@ func TestNewClientManagedIdentityNoNeedForCredentials(t *testing.T) {
}}},
}
provider, err := schema.GetProvider(&store)
provider, err := esv1beta1.GetProvider(&store)
tassert.Nil(t, err, "the return err should be nil")
k8sClient := clientfake.NewClientBuilder().Build()
secretClient, err := provider.NewClient(context.Background(), &store, k8sClient, namespace)
@ -132,7 +131,7 @@ func TestNewClientNoCreds(t *testing.T) {
TenantID: &tenantID,
}}},
}
provider, err := schema.GetProvider(&store)
provider, err := esv1beta1.GetProvider(&store)
tassert.Nil(t, err, "the return err should be nil")
k8sClient := clientfake.NewClientBuilder().Build()
_, err = provider.NewClient(context.Background(), &store, k8sClient, namespace)
@ -556,3 +555,65 @@ func makeValidFind() *esv1beta1.ExternalSecretFind {
Tags: map[string]string{},
}
}
func TestValidateStore(t *testing.T) {
type args struct {
auth esv1beta1.AzureKVAuth
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "empty auth",
wantErr: false,
},
{
name: "empty client id",
wantErr: false,
args: args{
auth: esv1beta1.AzureKVAuth{},
},
},
{
name: "invalid client id",
wantErr: true,
args: args{
auth: esv1beta1.AzureKVAuth{
ClientID: &v1.SecretKeySelector{
Namespace: pointer.StringPtr("invalid"),
},
},
},
},
{
name: "invalid client secret",
wantErr: true,
args: args{
auth: esv1beta1.AzureKVAuth{
ClientSecret: &v1.SecretKeySelector{
Namespace: pointer.StringPtr("invalid"),
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := &Azure{}
store := &esv1beta1.SecretStore{
Spec: esv1beta1.SecretStoreSpec{
Provider: &esv1beta1.SecretStoreProvider{
AzureKV: &esv1beta1.AzureKVProvider{
AuthSecretRef: &tt.args.auth,
},
},
},
}
if err := a.ValidateStore(store); (err != nil) != tt.wantErr {
t.Errorf("Azure.ValidateStore() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

View file

@ -21,8 +21,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
"github.com/external-secrets/external-secrets/pkg/provider"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
)
var (
@ -35,7 +33,7 @@ type Provider struct {
config *esv1beta1.FakeProvider
}
func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) {
func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
cfg, err := getProvider(store)
if err != nil {
return nil, err
@ -99,8 +97,12 @@ func (p *Provider) Validate() error {
return nil
}
func (p *Provider) ValidateStore(store esv1beta1.GenericStore) error {
return nil
}
func init() {
schema.Register(&Provider{}, &esv1beta1.SecretStoreProvider{
esv1beta1.Register(&Provider{}, &esv1beta1.SecretStoreProvider{
Fake: &esv1beta1.FakeProvider{},
})
}

View file

@ -30,8 +30,6 @@ import (
kclient "sigs.k8s.io/controller-runtime/pkg/client"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
"github.com/external-secrets/external-secrets/pkg/provider"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
"github.com/external-secrets/external-secrets/pkg/utils"
)
@ -52,6 +50,13 @@ const (
errUninitalizedGCPProvider = "provider GCP is not initialized"
errClientGetSecretAccess = "unable to access Secret from SecretManager Client: %w"
errJSONSecretUnmarshal = "unable to unmarshal secret: %w"
errInvalidStore = "invalid store"
errInvalidStoreSpec = "invalid store spec"
errInvalidStoreProv = "invalid store provider"
errInvalidGCPProv = "invalid gcp secrets manager provider"
errInvalidAuthSecretRef = "invalid auth secret ref: %w"
errInvalidWISARef = "invalid workload identity service account reference: %w"
)
type GoogleSecretManagerClient interface {
@ -132,7 +137,7 @@ func serviceAccountTokenSource(ctx context.Context, store esv1beta1.GenericStore
}
// NewClient constructs a GCP Provider.
func (sm *ProviderGCP) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (provider.SecretsClient, error) {
func (sm *ProviderGCP) 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.GCPSM == nil {
return nil, fmt.Errorf(errGCPSMStore)
@ -270,8 +275,36 @@ func (sm *ProviderGCP) Validate() error {
return nil
}
func (sm *ProviderGCP) ValidateStore(store esv1beta1.GenericStore) error {
if store == nil {
return fmt.Errorf(errInvalidStore)
}
spc := store.GetSpec()
if spc == nil {
return fmt.Errorf(errInvalidStoreSpec)
}
if spc.Provider == nil {
return fmt.Errorf(errInvalidStoreProv)
}
p := spc.Provider.GCPSM
if p == nil {
return fmt.Errorf(errInvalidGCPProv)
}
if p.Auth.SecretRef != nil {
if err := utils.ValidateSecretSelector(store, p.Auth.SecretRef.SecretAccessKey); err != nil {
return fmt.Errorf(errInvalidAuthSecretRef, err)
}
}
if p.Auth.WorkloadIdentity != nil {
if err := utils.ValidateServiceAccountSelector(store, p.Auth.WorkloadIdentity.ServiceAccountRef); err != nil {
return fmt.Errorf(errInvalidWISARef, err)
}
}
return nil
}
func init() {
schema.Register(&ProviderGCP{}, &esv1beta1.SecretStoreProvider{
esv1beta1.Register(&ProviderGCP{}, &esv1beta1.SecretStoreProvider{
GCPSM: &esv1beta1.GCPSMProvider{},
})
}

View file

@ -21,8 +21,10 @@ import (
"testing"
secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/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"
fakesm "github.com/external-secrets/external-secrets/pkg/provider/gcp/secretmanager/fake"
)
@ -210,3 +212,65 @@ func ErrorContains(out error, want string) bool {
}
return strings.Contains(out.Error(), want)
}
func TestValidateStore(t *testing.T) {
type args struct {
auth esv1beta1.GCPSMAuth
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "empty auth",
wantErr: false,
},
{
name: "invalid secret ref",
wantErr: true,
args: args{
auth: esv1beta1.GCPSMAuth{
SecretRef: &esv1beta1.GCPSMAuthSecretRef{
SecretAccessKey: v1.SecretKeySelector{
Name: "foo",
Namespace: pointer.StringPtr("invalid"),
},
},
},
},
},
{
name: "invalid wi sa ref",
wantErr: true,
args: args{
auth: esv1beta1.GCPSMAuth{
WorkloadIdentity: &esv1beta1.GCPWorkloadIdentity{
ServiceAccountRef: v1.ServiceAccountSelector{
Name: "foo",
Namespace: pointer.StringPtr("invalid"),
},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
sm := &ProviderGCP{}
store := &esv1beta1.SecretStore{
Spec: esv1beta1.SecretStoreSpec{
Provider: &esv1beta1.SecretStoreProvider{
GCPSM: &esv1beta1.GCPSMProvider{
Auth: tt.args.auth,
},
},
},
}
if err := sm.ValidateStore(store); (err != nil) != tt.wantErr {
t.Errorf("ProviderGCP.ValidateStore() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

View file

@ -27,8 +27,6 @@ import (
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
"github.com/external-secrets/external-secrets/e2e/framework/log"
"github.com/external-secrets/external-secrets/pkg/provider"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
"github.com/external-secrets/external-secrets/pkg/utils"
)
@ -63,7 +61,7 @@ type gClient struct {
}
func init() {
schema.Register(&Gitlab{}, &esv1beta1.SecretStoreProvider{
esv1beta1.Register(&Gitlab{}, &esv1beta1.SecretStoreProvider{
Gitlab: &esv1beta1.GitlabProvider{},
})
}
@ -108,7 +106,7 @@ func NewGitlabProvider() *Gitlab {
}
// Method on Gitlab Provider to set up client with credentials and populate projectID.
func (g *Gitlab) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (provider.SecretsClient, error) {
func (g *Gitlab) 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.Gitlab == nil {
return nil, fmt.Errorf("no store type or wrong store type")
@ -220,3 +218,7 @@ func (g *Gitlab) Close(ctx context.Context) error {
func (g *Gitlab) Validate() error {
return nil
}
func (g *Gitlab) ValidateStore(store esv1beta1.GenericStore) error {
return nil
}

View file

@ -27,8 +27,6 @@ import (
kclient "sigs.k8s.io/controller-runtime/pkg/client"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
"github.com/external-secrets/external-secrets/pkg/provider"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
"github.com/external-secrets/external-secrets/pkg/utils"
)
@ -323,7 +321,11 @@ func (ibm *providerIBM) Validate() error {
return nil
}
func (ibm *providerIBM) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (provider.SecretsClient, error) {
func (ibm *providerIBM) ValidateStore(store esv1beta1.GenericStore) error {
return nil
}
func (ibm *providerIBM) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
storeSpec := store.GetSpec()
ibmSpec := storeSpec.Provider.IBM
@ -376,7 +378,7 @@ func (ibm *providerIBM) NewClient(ctx context.Context, store esv1beta1.GenericSt
}
func init() {
schema.Register(&providerIBM{}, &esv1beta1.SecretStoreProvider{
esv1beta1.Register(&providerIBM{}, &esv1beta1.SecretStoreProvider{
IBM: &esv1beta1.IBMProvider{},
})
}

View file

@ -27,8 +27,6 @@ import (
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/provider"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
"github.com/external-secrets/external-secrets/pkg/utils"
)
@ -51,7 +49,7 @@ type ProviderKubernetes struct {
Client KClient
}
var _ provider.SecretsClient = &ProviderKubernetes{}
var _ esv1beta1.SecretsClient = &ProviderKubernetes{}
type BaseClient struct {
kube kclient.Client
@ -65,13 +63,13 @@ type BaseClient struct {
}
func init() {
schema.Register(&ProviderKubernetes{}, &esv1beta1.SecretStoreProvider{
esv1beta1.Register(&ProviderKubernetes{}, &esv1beta1.SecretStoreProvider{
Kubernetes: &esv1beta1.KubernetesProvider{},
})
}
// NewClient constructs a Kubernetes Provider.
func (k *ProviderKubernetes) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (provider.SecretsClient, error) {
func (k *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")
@ -233,3 +231,7 @@ func (k *BaseClient) fetchSecretKey(ctx context.Context, key esmeta.SecretKeySel
func (k *ProviderKubernetes) Validate() error {
return nil
}
func (k *ProviderKubernetes) ValidateStore(store esv1beta1.GenericStore) error {
return nil
}

View file

@ -27,9 +27,7 @@ import (
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/provider"
"github.com/external-secrets/external-secrets/pkg/provider/aws/util"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
"github.com/external-secrets/external-secrets/pkg/utils"
)
@ -124,7 +122,7 @@ func (vms *VaultManagementService) GetSecretMap(ctx context.Context, ref esv1bet
}
// NewClient constructs a new secrets client based on the provided store.
func (vms *VaultManagementService) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (provider.SecretsClient, error) {
func (vms *VaultManagementService) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
storeSpec := store.GetSpec()
oracleSpec := storeSpec.Provider.Oracle
@ -225,8 +223,12 @@ func (vms *VaultManagementService) Validate() error {
return nil
}
func (vms *VaultManagementService) ValidateStore(store esv1beta1.GenericStore) error {
return nil
}
func init() {
schema.Register(&VaultManagementService{}, &esv1beta1.SecretStoreProvider{
esv1beta1.Register(&VaultManagementService{}, &esv1beta1.SecretStoreProvider{
Oracle: &esv1beta1.OracleProvider{},
})
}

View file

@ -20,16 +20,14 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
"github.com/external-secrets/external-secrets/pkg/provider"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
)
var _ provider.Provider = &Client{}
var _ esv1beta1.Provider = &Client{}
// Client is a fake client for testing.
type Client struct {
NewFn func(context.Context, esv1beta1.GenericStore, client.Client,
string) (provider.SecretsClient, error)
string) (esv1beta1.SecretsClient, error)
GetSecretFn func(context.Context, esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error)
GetSecretMapFn func(context.Context, esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error)
GetAllSecretsFn func(context.Context, esv1beta1.ExternalSecretFind) (map[string][]byte, error)
@ -49,7 +47,7 @@ func New() *Client {
},
}
v.NewFn = func(context.Context, esv1beta1.GenericStore, client.Client, string) (provider.SecretsClient, error) {
v.NewFn = func(context.Context, esv1beta1.GenericStore, client.Client, string) (esv1beta1.SecretsClient, error) {
return v, nil
}
@ -58,7 +56,7 @@ func New() *Client {
// RegisterAs registers the fake client in the schema.
func (v *Client) RegisterAs(provider *esv1beta1.SecretStoreProvider) {
schema.ForceRegister(v, provider)
esv1beta1.ForceRegister(v, provider)
}
// GetSecret implements the provider.Provider interface.
@ -92,6 +90,10 @@ func (v *Client) Validate() error {
return nil
}
func (v *Client) ValidateStore(store esv1beta1.GenericStore) error {
return nil
}
// WithGetSecretMap wraps the secret data map returned by this fake provider.
func (v *Client) WithGetSecretMap(secData map[string][]byte, err error) *Client {
v.GetSecretMapFn = func(context.Context, esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
@ -110,13 +112,13 @@ func (v *Client) WithGetAllSecrets(secData map[string][]byte, err error) *Client
// WithNew wraps the fake provider factory function.
func (v *Client) WithNew(f func(context.Context, esv1beta1.GenericStore, client.Client,
string) (provider.SecretsClient, error)) *Client {
string) (esv1beta1.SecretsClient, error)) *Client {
v.NewFn = f
return v
}
// NewClient returns a new fake provider.
func (v *Client) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) {
func (v *Client) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
c, err := v.NewFn(ctx, store, kube, namespace)
if err != nil {
return nil, err

View file

@ -37,13 +37,12 @@ import (
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/provider"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
"github.com/external-secrets/external-secrets/pkg/utils"
)
var (
_ provider.Provider = &connector{}
_ provider.SecretsClient = &client{}
_ esv1beta1.Provider = &connector{}
_ esv1beta1.SecretsClient = &client{}
)
const (
@ -79,6 +78,19 @@ const (
errUnknownCAProvider = "unknown caProvider type given"
errCANamespace = "cannot read secret for CAProvider due to missing namespace on kind ClusterSecretStore"
errInvalidStore = "invalid store"
errInvalidStoreSpec = "invalid store spec"
errInvalidStoreProv = "invalid store provider"
errInvalidVaultProv = "invalid vault provider"
errInvalidAppRoleSec = "invalid Auth.AppRole.SecretRef: %w"
errInvalidClientCert = "invalid Auth.Cert.ClientCert: %w"
errInvalidCertSec = "invalid Auth.Cert.SecretRef: %w"
errInvalidJwtSec = "invalid Auth.Jwt.SecretRef: %w"
errInvalidKubeSA = "invalid Auth.Kubernetes.ServiceAccountRef: %w"
errInvalidKubeSec = "invalid Auth.Kubernetes.SecretRef: %w"
errInvalidLdapSec = "invalid Auth.Ldap.SecretRef: %w"
errInvalidTokenRef = "invalid Auth.TokenSecretRef: %w"
)
type Client interface {
@ -101,7 +113,7 @@ type client struct {
}
func init() {
schema.Register(&connector{
esv1beta1.Register(&connector{
newVaultClient: newVaultClient,
}, &esv1beta1.SecretStoreProvider{
Vault: &esv1beta1.VaultProvider{},
@ -116,7 +128,7 @@ type connector struct {
newVaultClient func(c *vault.Config) (Client, error)
}
func (c *connector) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (provider.SecretsClient, error) {
func (c *connector) 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.Vault == nil {
return nil, errors.New(errVaultStore)
@ -158,6 +170,64 @@ func (c *connector) NewClient(ctx context.Context, store esv1beta1.GenericStore,
return vStore, nil
}
func (c *connector) ValidateStore(store esv1beta1.GenericStore) error {
if store == nil {
return fmt.Errorf(errInvalidStore)
}
spc := store.GetSpec()
if spc == nil {
return fmt.Errorf(errInvalidStoreSpec)
}
if spc.Provider == nil {
return fmt.Errorf(errInvalidStoreProv)
}
p := spc.Provider.Vault
if p == nil {
return fmt.Errorf(errInvalidVaultProv)
}
if p.Auth.AppRole != nil {
if err := utils.ValidateSecretSelector(store, p.Auth.AppRole.SecretRef); err != nil {
return fmt.Errorf(errInvalidAppRoleSec, err)
}
}
if p.Auth.Cert != nil {
if err := utils.ValidateSecretSelector(store, p.Auth.Cert.ClientCert); err != nil {
return fmt.Errorf(errInvalidClientCert, err)
}
if err := utils.ValidateSecretSelector(store, p.Auth.Cert.SecretRef); err != nil {
return fmt.Errorf(errInvalidCertSec, err)
}
}
if p.Auth.Jwt != nil {
if err := utils.ValidateSecretSelector(store, p.Auth.Jwt.SecretRef); err != nil {
return fmt.Errorf(errInvalidJwtSec, err)
}
}
if p.Auth.Kubernetes != nil {
if p.Auth.Kubernetes.ServiceAccountRef != nil {
if err := utils.ValidateServiceAccountSelector(store, *p.Auth.Kubernetes.ServiceAccountRef); err != nil {
return fmt.Errorf(errInvalidKubeSA, err)
}
}
if p.Auth.Kubernetes.SecretRef != nil {
if err := utils.ValidateSecretSelector(store, *p.Auth.Kubernetes.SecretRef); err != nil {
return fmt.Errorf(errInvalidKubeSec, err)
}
}
}
if p.Auth.Ldap != nil {
if err := utils.ValidateSecretSelector(store, p.Auth.Ldap.SecretRef); err != nil {
return fmt.Errorf(errInvalidLdapSec, err)
}
}
if p.Auth.TokenSecretRef != nil {
if err := utils.ValidateSecretSelector(store, *p.Auth.TokenSecretRef); err != nil {
return fmt.Errorf(errInvalidTokenRef, err)
}
}
return nil
}
// Empty GetAllSecrets.
func (v *client) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
// TO be implemented

View file

@ -29,6 +29,7 @@ import (
vault "github.com/hashicorp/vault/api"
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"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
@ -1070,3 +1071,142 @@ func TestGetSecretPath(t *testing.T) {
})
}
}
func TestValidateStore(t *testing.T) {
type args struct {
auth esv1beta1.VaultAuth
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "empty auth",
args: args{},
},
{
name: "invalid approle with namespace",
args: args{
auth: esv1beta1.VaultAuth{
AppRole: &esv1beta1.VaultAppRole{
SecretRef: esmeta.SecretKeySelector{
Namespace: pointer.StringPtr("invalid"),
},
},
},
},
wantErr: true,
},
{
name: "invalid clientcert",
args: args{
auth: esv1beta1.VaultAuth{
Cert: &esv1beta1.VaultCertAuth{
ClientCert: esmeta.SecretKeySelector{
Namespace: pointer.StringPtr("invalid"),
},
},
},
},
wantErr: true,
},
{
name: "invalid cert secret",
args: args{
auth: esv1beta1.VaultAuth{
Cert: &esv1beta1.VaultCertAuth{
SecretRef: esmeta.SecretKeySelector{
Namespace: pointer.StringPtr("invalid"),
},
},
},
},
wantErr: true,
},
{
name: "invalid jwt secret",
args: args{
auth: esv1beta1.VaultAuth{
Jwt: &esv1beta1.VaultJwtAuth{
SecretRef: esmeta.SecretKeySelector{
Namespace: pointer.StringPtr("invalid"),
},
},
},
},
wantErr: true,
},
{
name: "invalid kubernetes sa",
args: args{
auth: esv1beta1.VaultAuth{
Kubernetes: &esv1beta1.VaultKubernetesAuth{
ServiceAccountRef: &esmeta.ServiceAccountSelector{
Namespace: pointer.StringPtr("invalid"),
},
},
},
},
wantErr: true,
},
{
name: "invalid kubernetes secret",
args: args{
auth: esv1beta1.VaultAuth{
Kubernetes: &esv1beta1.VaultKubernetesAuth{
SecretRef: &esmeta.SecretKeySelector{
Namespace: pointer.StringPtr("invalid"),
},
},
},
},
wantErr: true,
},
{
name: "invalid ldap secret",
args: args{
auth: esv1beta1.VaultAuth{
Ldap: &esv1beta1.VaultLdapAuth{
SecretRef: esmeta.SecretKeySelector{
Namespace: pointer.StringPtr("invalid"),
},
},
},
},
wantErr: true,
},
{
name: "invalid token secret",
args: args{
auth: esv1beta1.VaultAuth{
TokenSecretRef: &esmeta.SecretKeySelector{
Namespace: pointer.StringPtr("invalid"),
},
},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &connector{
newVaultClient: nil,
}
store := &esv1beta1.SecretStore{
Spec: esv1beta1.SecretStoreSpec{
Provider: &esv1beta1.SecretStoreProvider{
Vault: &esv1beta1.VaultProvider{
Auth: tt.args.auth,
},
},
},
}
if err := c.ValidateStore(store); (err != nil) != tt.wantErr {
t.Errorf("connector.ValidateStore() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

View file

@ -34,8 +34,6 @@ import (
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/provider"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
"github.com/external-secrets/external-secrets/pkg/template/v2"
)
@ -51,12 +49,12 @@ type WebHook struct {
}
func init() {
schema.Register(&Provider{}, &esv1beta1.SecretStoreProvider{
esv1beta1.Register(&Provider{}, &esv1beta1.SecretStoreProvider{
Webhook: &esv1beta1.WebhookProvider{},
})
}
func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) {
func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
whClient := &WebHook{
kube: kube,
store: store,
@ -74,6 +72,10 @@ func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore,
return whClient, nil
}
func (p *Provider) ValidateStore(store esv1beta1.GenericStore) error {
return nil
}
func getProvider(store esv1beta1.GenericStore) (*esv1beta1.WebhookProvider, error) {
spc := store.GetSpec()
if spc == nil || spc.Provider == nil || spc.Provider.Webhook == nil {

View file

@ -28,7 +28,6 @@ import (
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/provider"
)
type testCase struct {
@ -268,7 +267,7 @@ func runTestCase(tc testCase, t *testing.T) {
}
}
func testGetSecretMap(tc testCase, t *testing.T, client provider.SecretsClient) {
func testGetSecretMap(tc testCase, t *testing.T, client esv1beta1.SecretsClient) {
testRef := esv1beta1.ExternalSecretDataRemoteRef{
Key: tc.Args.Key,
Version: tc.Args.Version,
@ -293,7 +292,7 @@ func testGetSecretMap(tc testCase, t *testing.T, client provider.SecretsClient)
}
}
func testGetSecret(tc testCase, t *testing.T, client provider.SecretsClient) {
func testGetSecret(tc testCase, t *testing.T, client esv1beta1.SecretsClient) {
testRef := esv1beta1.ExternalSecretDataRemoteRef{
Key: tc.Args.Key,
Version: tc.Args.Version,

View file

@ -30,8 +30,6 @@ import (
kclient "sigs.k8s.io/controller-runtime/pkg/client"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
"github.com/external-secrets/external-secrets/pkg/provider"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
"github.com/external-secrets/external-secrets/pkg/provider/yandex/lockbox/client"
"github.com/external-secrets/external-secrets/pkg/provider/yandex/lockbox/client/grpc"
)
@ -66,7 +64,7 @@ func newLockboxProvider(yandexCloudCreator client.YandexCloudCreator) *lockboxPr
}
// NewClient constructs a Yandex Lockbox Provider.
func (p *lockboxProvider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (provider.SecretsClient, error) {
func (p *lockboxProvider) 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.YandexLockbox == nil {
return nil, fmt.Errorf("received invalid Yandex Lockbox SecretStore resource")
@ -218,6 +216,10 @@ func (p *lockboxProvider) cleanUpIamTokenMap() {
}
}
func (p *lockboxProvider) ValidateStore(store esv1beta1.GenericStore) error {
return nil
}
// lockboxSecretsClient is a secrets client for Yandex Lockbox.
type lockboxSecretsClient struct {
lockboxClient client.LockboxClient
@ -327,7 +329,7 @@ func init() {
}
}()
schema.Register(
esv1beta1.Register(
lockboxProvider,
&esv1beta1.SecretStoreProvider{
YandexLockbox: &esv1beta1.YandexLockboxProvider{},

View file

@ -34,7 +34,6 @@ import (
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/provider/schema"
"github.com/external-secrets/external-secrets/pkg/provider/yandex/lockbox/client/fake"
)
@ -58,7 +57,7 @@ func TestNewClient(t *testing.T) {
},
},
}
provider, err := schema.GetProvider(store)
provider, err := esv1beta1.GetProvider(store)
tassert.Nil(t, err)
k8sClient := clientfake.NewClientBuilder().Build()

View file

@ -21,6 +21,9 @@ import (
"fmt"
"reflect"
"strings"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
)
// MergeByteMap merges map of byte slices.
@ -66,3 +69,31 @@ func ErrorContains(out error, want string) bool {
}
return strings.Contains(out.Error(), want)
}
// ValidateSecretSelector just checks if the namespace field is present/absent
// depending on the secret store type.
// We MUST NOT check the name or key property here. It MAY be defaulted by the provider.
func ValidateSecretSelector(store esv1beta1.GenericStore, ref esmeta.SecretKeySelector) error {
clusterScope := store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind
if clusterScope && ref.Namespace == nil {
return fmt.Errorf("cluster scope requires namespace")
}
if !clusterScope && ref.Namespace != nil {
return fmt.Errorf("namespace not allowed with namespaced SecretStore")
}
return nil
}
// ValidateServiceAccountSelector just checks if the namespace field is present/absent
// depending on the secret store type.
// We MUST NOT check the name or key property here. It MAY be defaulted by the provider.
func ValidateServiceAccountSelector(store esv1beta1.GenericStore, ref esmeta.ServiceAccountSelector) error {
clusterScope := store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind
if clusterScope && ref.Namespace == nil {
return fmt.Errorf("cluster scope requires namespace")
}
if !clusterScope && ref.Namespace != nil {
return fmt.Errorf("namespace not allowed with namespaced SecretStore")
}
return nil
}