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

Merge pull request #17 from external-secrets/add-main-controller-logic

Add main controller logic
This commit is contained in:
Lucas Severo Alves 2021-02-03 17:47:34 +01:00 committed by GitHub
commit e8fb686e05
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 174 additions and 17 deletions

View file

@ -34,7 +34,7 @@ make run
Apply the sample resources:
```shell
kubectl apply -f config/samples/external-secrets_v1alpha1_secretstore.yaml
kubectl applt -f config/samples/external-secrets_v1alpha1_externalsecret.yaml
kubectl apply -f config/samples/external-secrets_v1alpha1_externalsecret.yaml
```
We will add more documentation once we have the implementation for the different providers.

View file

@ -15,6 +15,8 @@ limitations under the License.
package v1alpha1
import (
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
@ -32,6 +34,7 @@ type GenericStore interface {
GetObjectMeta() *metav1.ObjectMeta
GetSpec() *SecretStoreSpec
GetNamespacedName() string
}
// +kubebuilder:object:root:false
@ -46,6 +49,10 @@ func (c *SecretStore) GetSpec() *SecretStoreSpec {
return &c.Spec
}
func (c *SecretStore) GetNamespacedName() string {
return fmt.Sprintf("%s/%s", c.Namespace, c.Name)
}
func (c *SecretStore) Copy() GenericStore {
return c.DeepCopy()
}
@ -65,3 +72,7 @@ func (c *ClusterSecretStore) GetSpec() *SecretStoreSpec {
func (c *ClusterSecretStore) Copy() GenericStore {
return c.DeepCopy()
}
func (c *ClusterSecretStore) GetNamespacedName() string {
return fmt.Sprintf("%s/%s", c.Namespace, c.Name)
}

View file

@ -11,7 +11,7 @@ spec:
name: secret-to-be-created
creationPolicy: Owner
refreshInternal: 1h
refreshInterval: 1h
data:
- secretKey: secret-key-to-be-managed

View file

@ -9,11 +9,11 @@ spec:
awssm:
auth:
secretRef:
accessKeyID:
accessKeyIDSecretRef:
name: awssm-secret
key: access-key
secretAccessKey:
secretAccessKeySecretRef:
name: awssm-secret
key: secret-access-key

5
go.mod
View file

@ -4,9 +4,12 @@ go 1.13
require (
github.com/go-logr/logr v0.1.0
github.com/kr/pretty v0.2.0 // indirect
github.com/onsi/ginkgo v1.11.0
github.com/onsi/gomega v1.8.1
github.com/stretchr/testify v1.4.0
github.com/stretchr/testify v1.6.1
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c // indirect
k8s.io/api v0.17.2
k8s.io/apimachinery v0.17.2
k8s.io/client-go v0.17.2

9
go.sum
View file

@ -183,6 +183,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
@ -270,6 +272,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
@ -397,6 +401,8 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
@ -412,6 +418,9 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo=
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View file

@ -16,16 +16,32 @@ package externalsecret
import (
"context"
"fmt"
"time"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
"github.com/external-secrets/external-secrets/pkg/provider"
// Loading registered providers.
_ "github.com/external-secrets/external-secrets/pkg/provider/register"
schema "github.com/external-secrets/external-secrets/pkg/provider/schema"
utils "github.com/external-secrets/external-secrets/pkg/utils"
)
// Reconciler reconciles a ExternalSecret object.
const (
requeueAfter = time.Second * 30
)
// ExternalSecretReconciler reconciles a ExternalSecret object.
type Reconciler struct {
client.Client
Log logr.Logger
@ -36,16 +52,113 @@ type Reconciler struct {
// +kubebuilder:rbac:groups=external-secrets.io,resources=externalsecrets/status,verbs=get;update;patch
func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
_ = context.Background()
_ = r.Log.WithValues("externalsecret", req.NamespacedName)
ctx := context.Background()
log := r.Log.WithValues("ExternalSecret", req.NamespacedName)
// your logic here
var externalSecret esv1alpha1.ExternalSecret
err := r.Get(ctx, req.NamespacedName, &externalSecret)
if err != nil {
log.Error(err, "could not get ExternalSecret")
return ctrl.Result{}, client.IgnoreNotFound(err)
}
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: externalSecret.Name,
Namespace: externalSecret.Namespace,
},
}
store, err := r.getStore(ctx, &externalSecret)
if err != nil {
log.Error(err, "could not get store reference")
return ctrl.Result{RequeueAfter: requeueAfter}, nil
}
log = log.WithValues("SecretStore", store.GetNamespacedName())
storeProvider, err := schema.GetProvider(store)
if err != nil {
log.Error(err, "could not get store provider")
return ctrl.Result{RequeueAfter: requeueAfter}, nil
}
providerClient, err := storeProvider.New(ctx, store, r.Client, req.Namespace)
if err != nil {
log.Error(err, "could not get provider client")
return ctrl.Result{RequeueAfter: requeueAfter}, nil
}
_, err = ctrl.CreateOrUpdate(ctx, r.Client, secret, func() error {
err = controllerutil.SetControllerReference(&externalSecret, &secret.ObjectMeta, r.Scheme)
if err != nil {
return fmt.Errorf("could not set ExternalSecret controller reference: %w", err)
}
secret.Labels = externalSecret.Labels
secret.Annotations = externalSecret.Annotations
secret.Data, err = r.getProviderSecretData(ctx, providerClient, &externalSecret)
if err != nil {
return fmt.Errorf("could not get secret data from provider: %w", err)
}
return nil
})
if err != nil {
log.Error(err, "could not reconcile ExternalSecret")
return ctrl.Result{RequeueAfter: requeueAfter}, nil
}
return ctrl.Result{}, nil
}
func (r *Reconciler) getStore(ctx context.Context, externalSecret *esv1alpha1.ExternalSecret) (esv1alpha1.GenericStore, error) {
// TODO: Implement getting ClusterSecretStore
var secretStore esv1alpha1.SecretStore
ref := types.NamespacedName{
Name: externalSecret.Spec.SecretStoreRef.Name,
Namespace: externalSecret.Namespace,
}
err := r.Get(ctx, ref, &secretStore)
if err != nil {
return nil, fmt.Errorf("could not get SecretStore %q, %w", ref.Name, err)
}
return &secretStore, nil
}
func (r *Reconciler) getProviderSecretData(ctx context.Context, providerClient provider.Provider, externalSecret *esv1alpha1.ExternalSecret) (map[string][]byte, error) {
providerData := make(map[string][]byte)
for _, remoteRef := range externalSecret.Spec.DataFrom {
secretMap, err := providerClient.GetSecretMap(ctx, remoteRef)
if err != nil {
return nil, fmt.Errorf("key %q from ExternalSecret %q: %w", remoteRef.Key, externalSecret.Name, err)
}
providerData = utils.Merge(providerData, secretMap)
}
for _, secretRef := range externalSecret.Spec.Data {
secretData, err := providerClient.GetSecret(ctx, secretRef.RemoteRef)
if err != nil {
return nil, fmt.Errorf("key %q from ExternalSecret %q: %w", secretRef.RemoteRef.Key, externalSecret.Name, err)
}
providerData[secretRef.SecretKey] = secretData
}
return providerData, nil
}
func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&esv1alpha1.ExternalSecret{}).
Owns(&corev1.Secret{}).
Complete(r)
}

View file

@ -27,7 +27,7 @@ import (
type SecretsManager struct{}
// New constructs a SecretsManager Provider.
func (sm *SecretsManager) New(ctx context.Context, store esv1alpha1.SecretStoreProvider, kube client.Client, namespace string) (provider.Provider, error) {
func (sm *SecretsManager) New(ctx context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string) (provider.Provider, error) {
return sm, nil // stub
}

View file

@ -28,7 +28,7 @@ var _ provider.Provider = &Client{}
// Client is a fake client for testing.
type Client struct {
NewFn func(context.Context, esv1alpha1.SecretStoreProvider, client.Client,
NewFn func(context.Context, esv1alpha1.GenericStore, client.Client,
string) (provider.Provider, error)
GetSecretFn func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error)
GetSecretMapFn func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error)
@ -45,7 +45,7 @@ func New() *Client {
},
}
v.NewFn = func(context.Context, esv1alpha1.SecretStoreProvider, client.Client, string) (provider.Provider, error) {
v.NewFn = func(context.Context, esv1alpha1.GenericStore, client.Client, string) (provider.Provider, error) {
return nil, nil
}
@ -84,14 +84,14 @@ func (v *Client) WithGetSecretMap(secData map[string][]byte, err error) *Client
}
// WithNew wraps the fake provider factory function.
func (v *Client) WithNew(f func(context.Context, esv1alpha1.SecretStoreProvider, client.Client,
func (v *Client) WithNew(f func(context.Context, esv1alpha1.GenericStore, client.Client,
string) (provider.Provider, error)) *Client {
v.NewFn = f
return v
}
// New returns a new fake provider.
func (v *Client) New(ctx context.Context, store esv1alpha1.SecretStoreProvider, kube client.Client, namespace string) (provider.Provider, error) {
func (v *Client) New(ctx context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string) (provider.Provider, error) {
client, err := v.NewFn(ctx, store, kube, namespace)
if err != nil {
return nil, err

View file

@ -25,7 +25,7 @@ import (
// Provider is a common interface for interacting with secret backends.
type Provider interface {
// New constructs a SecretsManager Provider
New(ctx context.Context, store esv1alpha1.SecretStoreProvider, kube client.Client, namespace string) (Provider, error)
New(ctx context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string) (Provider, error)
// GetSecret returns a single secret from the provider
GetSecret(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error)

View file

@ -27,7 +27,7 @@ import (
type PP struct{}
// New constructs a SecretsManager Provider.
func (p *PP) New(ctx context.Context, store esv1alpha1.SecretStoreProvider, kube client.Client, namespace string) (provider.Provider, error) {
func (p *PP) New(ctx context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string) (provider.Provider, error) {
return p, nil
}

21
pkg/utils/utils.go Normal file
View file

@ -0,0 +1,21 @@
/*
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 utils
// Merge maps.
func Merge(src, dst map[string][]byte) map[string][]byte {
for k, v := range dst {
src[k] = v
}
return src
}