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:
commit
b0240cf45a
68 changed files with 2297 additions and 513 deletions
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
|
@ -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
|
||||
|
|
2
.github/workflows/docs.yml
vendored
2
.github/workflows/docs.yml
vendored
|
@ -12,7 +12,7 @@ jobs:
|
|||
deploy:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
|
2
.github/workflows/e2e-managed.yml
vendored
2
.github/workflows/e2e-managed.yml
vendored
|
@ -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'
|
||||
|
||||
|
|
4
.github/workflows/e2e.yml
vendored
4
.github/workflows/e2e.yml
vendored
|
@ -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'
|
||||
|
||||
|
|
4
.github/workflows/helm.yml
vendored
4
.github/workflows/helm.yml
vendored
|
@ -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
|
||||
|
||||
|
|
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -183,7 +183,7 @@ func newExternalSecretV1Beta1() *esv1beta1.ExternalSecret {
|
|||
},
|
||||
DataFrom: []esv1beta1.ExternalSecretDataFromRemoteRef{
|
||||
{
|
||||
Extract: esv1beta1.ExternalSecretDataRemoteRef{
|
||||
Extract: &esv1beta1.ExternalSecretDataRemoteRef{
|
||||
Key: "key",
|
||||
Property: "property",
|
||||
Version: "version",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
|
@ -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{},
|
||||
},
|
||||
},
|
||||
}
|
62
apis/externalsecrets/v1beta1/secretstore_validator.go
Normal file
62
apis/externalsecrets/v1beta1/secretstore_validator.go
Normal 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)
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 }}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
|
@ -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 }}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
24
docs/eso-blogs.md
Normal 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
9
docs/eso-demos.md
Normal 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
16
docs/eso-talks.md
Normal 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)
|
||||
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
282
docs/spec.md
282
docs/spec.md
|
@ -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
27
go.mod
|
@ -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
109
go.sum
|
@ -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=
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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)
|
||||
|
|
100
pkg/controllers/webhookconfig/suite_test.go
Normal file
100
pkg/controllers/webhookconfig/suite_test.go
Normal 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())
|
||||
})
|
186
pkg/controllers/webhookconfig/webhookconfig.go
Normal file
186
pkg/controllers/webhookconfig/webhookconfig.go
Normal 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
|
||||
}
|
324
pkg/controllers/webhookconfig/webhookconfig_test.go
Normal file
324
pkg/controllers/webhookconfig/webhookconfig_test.go
Normal 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(),
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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{},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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{},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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{},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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{},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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{},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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{},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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{},
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue