diff --git a/Makefile b/Makefile index 5b70dab3..b0fdb9a6 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,10 @@ DOCKER_IMAGE ?= containersol/externalsecret-operator -DOCKER_TAG ?= backend-1password +DOCKER_TAG ?= $(shell grep -Po 'Version = "\K.*?(?=")' version/version.go) # export these if you want to use AWS secrets manager AWS_ACCESS_KEY_ID ?= AKIACONFIGUREME AWS_SECRET_ACCESS_KEY ?= Secretsecretconfigureme -AWS_REGION ?= eu-west-1 +AWS_DEFAULT_REGION ?= eu-west-1 NAMESPACE ?= "default" @@ -19,13 +19,12 @@ push: .PHONY: deploy .EXPORT_ALL_VARIABLES: deploy deploy: - envsubst < ./deploy/onepassword-namespace.yaml | kubectl apply -f - - envsubst < ./deploy/onepassword-configmap.yaml | kubectl apply -n ${NAMESPACE} -f - kubectl apply -n $(NAMESPACE) -f ./deploy/service_account.yaml kubectl apply -n $(NAMESPACE) -f ./deploy/role.yaml envsubst < ./deploy/role_binding.yaml | kubectl apply -n $(NAMESPACE) -f - kubectl apply -n $(NAMESPACE) -f ./deploy/crds/externalsecret-operator_v1alpha1_externalsecret_crd.yaml - envsubst < deploy/operator-onepassword.yaml | kubectl apply -n $(NAMESPACE) -f - + envsubst < deploy/operator-config.yaml | kubectl apply -n $(NAMESPACE) -f - + envsubst < deploy/operator.yaml | kubectl apply -n $(NAMESPACE) -f - .PHONY: test test: diff --git a/README.md b/README.md index c942eed7..8b889e10 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,6 @@ and custom resource definitions: ```shell -export AWS_ACCESS_KEY_ID=AKIACONFIGUREME -export AWS_SECRET_ACCESS_KEY=Secretsecretconfigureme -export AWS_REGION=eu-west-1 make deploy ``` @@ -46,7 +43,7 @@ Given a secret defined in AWS Secrets Manager: and an `ExternalSecret` resource definition like this one: ```yaml -% cat deploy/crds/externalsecret-operator_v1alpha1_externalsecret_cr.yaml +% cat ./deploy/crds/examples/externalsecret-asm.yaml apiVersion: externalsecret-operator.container-solutions.com/v1alpha1 kind: ExternalSecret metadata: @@ -60,7 +57,7 @@ The operator fetches the secret from AWS Secrets Manager and injects it as a secret: ```shell -% kubectl apply -f deploy/crds/externalsecret-operator_v1alpha1_externalsecret_cr.yaml +% kubectl apply -f ./deploy/crds/examples/externalsecret-asm.yaml % kubectl get secret example-externalsecret -o jsonpath='{.data.example-externalsecret-key}' | base64 -d this string is a secret ``` diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 6ca45d5f..729292a1 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -7,6 +7,8 @@ import ( "os" "runtime" + "github.com/ContainerSolutions/externalsecret-operator/pkg/secrets" + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) _ "k8s.io/client-go/plugin/pkg/client/auth" @@ -61,6 +63,12 @@ func main() { printVersion() + err := secrets.BackendInitFromEnv() + if err != nil { + log.Error(err, "Failed to initialize backends") + os.Exit(1) + } + namespace, err := k8sutil.GetWatchNamespace() if err != nil { log.Error(err, "Failed to get watch namespace") diff --git a/deploy/crds/examples/externalsecret-asm.yaml b/deploy/crds/examples/externalsecret-asm.yaml new file mode 100644 index 00000000..06b43979 --- /dev/null +++ b/deploy/crds/examples/externalsecret-asm.yaml @@ -0,0 +1,7 @@ +apiVersion: externalsecret-operator.container-solutions.com/v1alpha1 +kind: ExternalSecret +metadata: + name: example-externalsecret-asm +spec: + Key: example-externalsecret-key + Backend: asm-example diff --git a/deploy/crds/examples/externalsecret-dummy.yaml b/deploy/crds/examples/externalsecret-dummy.yaml new file mode 100644 index 00000000..a3f38287 --- /dev/null +++ b/deploy/crds/examples/externalsecret-dummy.yaml @@ -0,0 +1,7 @@ +apiVersion: externalsecret-operator.container-solutions.com/v1alpha1 +kind: ExternalSecret +metadata: + name: example-externalsecret +spec: + Key: example-externalsecret-key + Backend: dummy-example diff --git a/deploy/crds/examples/externalsecret-dummy2.yaml b/deploy/crds/examples/externalsecret-dummy2.yaml new file mode 100644 index 00000000..f8a01c2c --- /dev/null +++ b/deploy/crds/examples/externalsecret-dummy2.yaml @@ -0,0 +1,7 @@ +apiVersion: externalsecret-operator.container-solutions.com/v1alpha1 +kind: ExternalSecret +metadata: + name: example-externalsecret-2 +spec: + Key: example-externalsecret-key + Backend: dummy-example diff --git a/deploy/crds/externalsecret-operator_v1alpha1_externalsecret_cr.yaml b/deploy/crds/externalsecret-operator_v1alpha1_externalsecret_cr.yaml index 447a0f69..c6f40185 100644 --- a/deploy/crds/externalsecret-operator_v1alpha1_externalsecret_cr.yaml +++ b/deploy/crds/externalsecret-operator_v1alpha1_externalsecret_cr.yaml @@ -4,4 +4,4 @@ metadata: name: example-externalsecret spec: Key: example-externalsecret - Backend: onepassword + Backend: asm-example diff --git a/deploy/operator-aws.yaml b/deploy/operator-aws.yaml deleted file mode 100644 index 38622390..00000000 --- a/deploy/operator-aws.yaml +++ /dev/null @@ -1,36 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: externalsecret-operator -spec: - replicas: 1 - selector: - matchLabels: - name: externalsecret-operator - template: - metadata: - labels: - name: externalsecret-operator - spec: - serviceAccountName: externalsecret-operator - containers: - - name: externalsecret-operator - image: containersol/externalsecret-operator:0.0.1 - command: - - externalsecret-operator - imagePullPolicy: Always - env: - - name: WATCH_NAMESPACE - value: "" - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: OPERATOR_NAME - value: "externalsecret-operator" - - name: AWS_ACCESS_KEY_ID - value: ${AWS_ACCESS_KEY_ID} - - name: AWS_SECRET_ACCESS_KEY - value: ${AWS_SECRET_ACCESS_KEY} - - name: AWS_REGION - value: ${AWS_REGION} diff --git a/deploy/operator-config.json b/deploy/operator-config.json new file mode 100644 index 00000000..d466cd20 --- /dev/null +++ b/deploy/operator-config.json @@ -0,0 +1,9 @@ +{ + "Name": "asm-example", + "Type": "asm", + "Parameters": { + "accessKeyID": "$AWS_ACCESS_KEY_ID", + "region": "$AWS_REGION", + "secretAccessKey": "$AWS_SECRET_ACCESS_KEY" + } +} diff --git a/deploy/operator-config.yaml b/deploy/operator-config.yaml new file mode 100644 index 00000000..1c01127f --- /dev/null +++ b/deploy/operator-config.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Secret +metadata: + name: operator-config +type: Opaque +stringData: + operator-config.json: |- + { + "Name": "asm-example", + "Type": "asm", + "Parameters": { + "accessKeyID": "$AWS_ACCESS_KEY_ID", + "region": "$AWS_DEFAULT_REGION", + "secretAccessKey": "$AWS_SECRET_ACCESS_KEY" + } + } \ No newline at end of file diff --git a/deploy/operator.yaml b/deploy/operator.yaml index fd691942..c4005629 100644 --- a/deploy/operator.yaml +++ b/deploy/operator.yaml @@ -15,11 +15,10 @@ spec: serviceAccountName: externalsecret-operator containers: - name: externalsecret-operator - # Replace this with the built image name - image: REPLACE_IMAGE + image: containersol/externalsecret-operator:0.0.2 command: - externalsecret-operator - imagePullPolicy: Always + imagePullPolicy: Never env: - name: WATCH_NAMESPACE value: "" @@ -29,3 +28,8 @@ spec: fieldPath: metadata.name - name: OPERATOR_NAME value: "externalsecret-operator" + - name: OPERATOR_CONFIG + valueFrom: + secretKeyRef: + name: operator-config + key: operator-config.json diff --git a/pkg/apis/externalsecretoperator/v1alpha1/externalsecret_types.go b/pkg/apis/externalsecretoperator/v1alpha1/externalsecret_types.go index 2256928e..6757842a 100644 --- a/pkg/apis/externalsecretoperator/v1alpha1/externalsecret_types.go +++ b/pkg/apis/externalsecretoperator/v1alpha1/externalsecret_types.go @@ -14,10 +14,10 @@ type ExternalSecretSpec struct { // Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file // Add custom validation using kubebuilder tags: https://book.kubebuilder.io/beyond_basics/generating_crd.html - // The ExternalSecretBackend to use to retrieve the secret - Backend string `json:"Backend"` + // The Backend to use to retrieve the secret + Backend string // The Key of the secret held in the ExternalBackend - Key string `json:"Key"` + Key string } // ExternalSecretStatus defines the observed state of ExternalSecret @@ -27,10 +27,10 @@ type ExternalSecretStatus struct { // Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file // Add custom validation using kubebuilder tags: https://book.kubebuilder.io/beyond_basics/generating_crd.html - // The ExternalSecretBackend to use to retrieve the secret - Backend string `json:"Backend"` + // The Backend to use to retrieve the secret + Backend string // The Key of the secret held in the ExternalBackend - Key string `json:"Key"` + Key string } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/apis/externalsecretoperator/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/externalsecretoperator/v1alpha1/zz_generated.deepcopy.go index 788eccec..d1c994e6 100644 --- a/pkg/apis/externalsecretoperator/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/externalsecretoperator/v1alpha1/zz_generated.deepcopy.go @@ -1,21 +1,5 @@ // +build !ignore_autogenerated -/* -Copyright The Kubernetes Authors. - -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. -*/ - // Code generated by deepcopy-gen. DO NOT EDIT. package v1alpha1 diff --git a/pkg/apis/externalsecretoperator/v1alpha1/zz_generated.openapi.go b/pkg/apis/externalsecretoperator/v1alpha1/zz_generated.openapi.go index bd534941..7f33b152 100644 --- a/pkg/apis/externalsecretoperator/v1alpha1/zz_generated.openapi.go +++ b/pkg/apis/externalsecretoperator/v1alpha1/zz_generated.openapi.go @@ -67,7 +67,23 @@ func schema_pkg_apis_externalsecretoperator_v1alpha1_ExternalSecretSpec(ref comm Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ Description: "ExternalSecretSpec defines the desired state of ExternalSecret", - Properties: map[string]spec.Schema{}, + Properties: map[string]spec.Schema{ + "backend": { + SchemaProps: spec.SchemaProps{ + Description: "The Backend to use to retrieve the secret", + Type: []string{"string"}, + Format: "", + }, + }, + "key": { + SchemaProps: spec.SchemaProps{ + Description: "The Key of the secret held in the ExternalBackend", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"backend", "key"}, }, }, Dependencies: []string{}, @@ -79,7 +95,23 @@ func schema_pkg_apis_externalsecretoperator_v1alpha1_ExternalSecretStatus(ref co Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ Description: "ExternalSecretStatus defines the observed state of ExternalSecret", - Properties: map[string]spec.Schema{}, + Properties: map[string]spec.Schema{ + "backend": { + SchemaProps: spec.SchemaProps{ + Description: "The Backend to use to retrieve the secret", + Type: []string{"string"}, + Format: "", + }, + }, + "key": { + SchemaProps: spec.SchemaProps{ + Description: "The Key of the secret held in the ExternalBackend", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"backend", "key"}, }, }, Dependencies: []string{}, diff --git a/pkg/controller/externalsecret/externalsecret_controller.go b/pkg/controller/externalsecret/externalsecret_controller.go index f3df6189..a292728d 100644 --- a/pkg/controller/externalsecret/externalsecret_controller.go +++ b/pkg/controller/externalsecret/externalsecret_controller.go @@ -46,9 +46,6 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error { return err } - // Initialize the secret backends - initSecretBackends() - // Watch for changes to primary resource ExternalSecret err = c.Watch(&source.Kind{Type: &externalsecretoperatorv1alpha1.ExternalSecret{}}, &handler.EnqueueRequestForObject{}) if err != nil { diff --git a/pkg/controller/externalsecret/externalsecret_functions.go b/pkg/controller/externalsecret/externalsecret_functions.go index 03c26065..556bfb55 100644 --- a/pkg/controller/externalsecret/externalsecret_functions.go +++ b/pkg/controller/externalsecret/externalsecret_functions.go @@ -10,34 +10,12 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" ) -func initSecretBackends() { - // TODO: backends should be created on the fly according to CRDs - asm := secrets.NewAWSSecretsManagerBackend() - if err := asm.Init(); err != nil { - log.Error(err, "Failed to initialize AWS Secrets Manager Backend") - } - - dummy := secrets.NewDummySecretsBackend() - dummy.Init("-value") - secrets.BackendRegister("dummy", dummy) - log.Info("Initialized Dummy backend") - - secrets.BackendRegister("asm", asm) - log.Info("Initialized Amazon Secret Manager backend") - - onepasswordClient := secrets.OnePasswordCliClient{} - vault := "Personal" - onepassword := secrets.NewOnePasswordBackend(vault, onepasswordClient) - onepassword.Init() - secrets.BackendRegister("onepassword", onepassword) - log.Info("Initialized 1password backend") -} - func newSecretForCR(cr *externalsecretoperatorv1alpha1.ExternalSecret) (*corev1.Secret, error) { - backend, ok := secrets.Backends[cr.Spec.Backend] + backend, ok := secrets.BackendInstances[cr.Spec.Backend] if !ok { return nil, fmt.Errorf("Cannot find backend: %v", cr.Spec.Backend) } + value, err := backend.Get(cr.Spec.Key) secret := map[string][]byte{cr.Spec.Key: []byte(value)} diff --git a/pkg/controller/externalsecret/externalsecret_functions_test.go b/pkg/controller/externalsecret/externalsecret_functions_test.go index fe0c609d..d04774e6 100644 --- a/pkg/controller/externalsecret/externalsecret_functions_test.go +++ b/pkg/controller/externalsecret/externalsecret_functions_test.go @@ -12,9 +12,9 @@ func TestNewSecretForCR(t *testing.T) { key := "key" suffix := "-value" - dummy := secrets.NewDummySecretsBackend() - dummy.Init("-value") - secrets.BackendRegister("dummy", dummy) + secrets.BackendRegister("dummy", secrets.NewDummySecretsBackend) + secrets.BackendInstantiate("dummy", "dummy") + secrets.BackendInstances["dummy"].Init(map[string]string{"suffix": "-value"}) Convey("Given an ExternalSecret resource", t, func() { externalSecret := v1alpha1.ExternalSecret{ diff --git a/pkg/secrets/asm.go b/pkg/secrets/asm.go index 69576745..c8a6c49c 100644 --- a/pkg/secrets/asm.go +++ b/pkg/secrets/asm.go @@ -1,7 +1,11 @@ package secrets import ( + "fmt" + "reflect" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/secretsmanager" "github.com/aws/aws-sdk-go/service/secretsmanager/secretsmanageriface" @@ -10,24 +14,33 @@ import ( type AWSSecretsManagerBackend struct { Backend SecretsManager secretsmanageriface.SecretsManagerAPI + config *aws.Config + session *session.Session +} + +func init() { + BackendRegister("asm", NewAWSSecretsManagerBackend) } -func NewAWSSecretsManagerBackend() *AWSSecretsManagerBackend { +func NewAWSSecretsManagerBackend() BackendIface { backend := &AWSSecretsManagerBackend{} - backend.Init() return backend } func (s *AWSSecretsManagerBackend) Init(params ...interface{}) error { - session, err := session.NewSession() + var err error + + s.config, err = awsConfigFromParams(params...) if err != nil { return err } - _, err = session.Config.Credentials.Get() + + s.session, err = session.NewSession(s.config) if err != nil { return err } - s.SecretsManager = secretsmanager.New(session) + + s.SecretsManager = secretsmanager.New(s.session) return nil } @@ -40,6 +53,10 @@ func (s *AWSSecretsManagerBackend) Get(key string) (string, error) { return "", err } + if s.SecretsManager == nil { + return "", fmt.Errorf("backend not initialized") + } + output, err := s.SecretsManager.GetSecretValue(input) if err != nil { return "", err @@ -47,3 +64,53 @@ func (s *AWSSecretsManagerBackend) Get(key string) (string, error) { return *output.SecretString, nil } + +func awsConfigFromParams(params ...interface{}) (*aws.Config, error) { + + paramMap, err := paramsToMap(params...) + if err != nil { + return nil, err + } + + accessKeyID := paramMap["accessKeyID"] + secretAccessKey := paramMap["secretAccessKey"] + region := paramMap["region"] + + return &aws.Config{ + Region: aws.String(region), + Credentials: credentials.NewStaticCredentials( + accessKeyID, + secretAccessKey, + ""), + }, nil +} + +func paramsToMap(params ...interface{}) (map[string]string, error) { + + paramKeys := []string{"accessKeyID", "secretAccessKey", "region"} + + if len(params) < 1 { + return nil, fmt.Errorf("Invalid init parameters: not found %v", paramKeys) + } + + paramType := reflect.TypeOf(params[0]) + if paramType != reflect.TypeOf(map[string]string{}) { + return nil, fmt.Errorf("Invalid init parameters: expected `map[string]string` found `%v", paramType) + } + + paramMap := params[0].(map[string]string) + + for _, key := range paramKeys { + paramValue, found := paramMap[key] + if !found { + return nil, fmt.Errorf("Invalid init paramters: expected `%v` not found", key) + } + + paramType := reflect.TypeOf(paramValue) + if paramType.Kind() != reflect.String { + return nil, fmt.Errorf("Invalid init paramters: expected `%v` of type `string` got `%v`", key, paramType) + } + } + + return paramMap, nil +} diff --git a/pkg/secrets/asm_test.go b/pkg/secrets/asm_test.go index 8474b8d8..949fd68f 100644 --- a/pkg/secrets/asm_test.go +++ b/pkg/secrets/asm_test.go @@ -3,6 +3,7 @@ package secrets import ( "testing" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/secretsmanager" "github.com/aws/aws-sdk-go/service/secretsmanager/secretsmanageriface" . "github.com/smartystreets/goconvey/convey" @@ -27,7 +28,7 @@ func TestGet(t *testing.T) { expectedValue := secretValue Convey("Given an initialized AWSSecretsManagerBackend", t, func() { - backend := NewAWSSecretsManagerBackend() + backend := AWSSecretsManagerBackend{} backend.SecretsManager = &mockedSecretsManager{} Convey("When retrieving a secret", func() { actualValue, err := backend.Get(secretKey) @@ -38,3 +39,30 @@ func TestGet(t *testing.T) { }) }) } + +func TestAWSConfigFromParams(t *testing.T) { + + Convey("Given a set of params", t, func() { + expectedAccessKeyID := "AKIABLABLA" + expectedSecretAccessKey := "SMAMSLscSercreasdas" + expectedRegion := "eu-west-1" + + params := map[string]string{ + "accessKeyID": expectedAccessKeyID, + "secretAccessKey": expectedSecretAccessKey, + "region": expectedRegion, + } + + Convey("When creating AWS Config from them", func() { + config, err := awsConfigFromParams(params) + So(err, ShouldBeNil) + Convey("Credentials are created correctly", func() { + actualCredentials, err := config.Credentials.Get() + So(err, ShouldBeNil) + So(aws.StringValue(config.Region), ShouldEqual, expectedRegion) + So(actualCredentials.AccessKeyID, ShouldEqual, expectedAccessKeyID) + So(actualCredentials.SecretAccessKey, ShouldEqual, expectedSecretAccessKey) + }) + }) + }) +} diff --git a/pkg/secrets/backend.go b/pkg/secrets/backend.go index 20588451..0771ae5f 100644 --- a/pkg/secrets/backend.go +++ b/pkg/secrets/backend.go @@ -1,12 +1,12 @@ package secrets -//Backends is a map of backends -var Backends map[string]BackendIface +import ( + "fmt" + "sync" +) -//Backend is a Backend to a Secret Store -type Backend struct { - Name string -} +//Backend is a secret store backend +type Backend struct{} //BackendIface is an interface to a Backend type BackendIface interface { @@ -14,11 +14,56 @@ type BackendIface interface { Get(string) (string, error) } -//BackendRegister adds a Backend to the Backends map -func BackendRegister(name string, backend BackendIface) { - if Backends == nil { - Backends = make(map[string]BackendIface) +// BackendInstances are instantiated backends +var BackendInstances map[string]BackendIface + +// BackendFunctions is a map of functions that return Backends +var BackendFunctions map[string]func() BackendIface + +var initLock sync.Mutex + +// BackendInstantiate instantiates a Backend of type `backendType` +func BackendInstantiate(name string, backendType string) error { + if BackendInstances == nil { + BackendInstances = make(map[string]BackendIface) + } + + function, found := BackendFunctions[backendType] + if !found { + return fmt.Errorf("Unkown backend type: %v", backendType) } - Backends[name] = backend + BackendInstances[name] = function() + + return nil +} + +// BackendRegister registers a new backend type with name `name` +// function is a function that returns a backend of that type +func BackendRegister(name string, function func() BackendIface) { + if BackendFunctions == nil { + BackendFunctions = make(map[string]func() BackendIface) + } + + BackendFunctions[name] = function +} + +// BackendInitFromEnv initializes a backend looking into Env for config data +func BackendInitFromEnv() error { + initLock.Lock() + defer initLock.Unlock() + + config, err := BackendConfigFromEnv() + if err != nil { + return err + } + + err = BackendInstantiate(config.Name, config.Type) + if err != nil { + return err + } + + err = BackendInstances[config.Name].Init(config.Parameters) + + return err } diff --git a/pkg/secrets/config.go b/pkg/secrets/config.go new file mode 100644 index 00000000..c72542a9 --- /dev/null +++ b/pkg/secrets/config.go @@ -0,0 +1,36 @@ +package secrets + +import ( + "encoding/json" + "fmt" + "os" +) + +//ConfigEnvVar holds the name of the Environment Variable scanned for config +const ConfigEnvVar string = "OPERATOR_CONFIG" + +//BackendConfig represent configuration information for secret backend +type BackendConfig struct { + Name string + Type string + Parameters map[string]string +} + +// BackendConfigFromJSON returns a BackendConfig object based on the string data passed as parameter +func BackendConfigFromJSON(data string) (*BackendConfig, error) { + backendConfig := &BackendConfig{} + err := json.Unmarshal([]byte(data), backendConfig) + if err != nil { + return nil, err + } + return backendConfig, nil +} + +//BackendConfigFromEnv parse BackendConfiguration from environment variable +func BackendConfigFromEnv() (*BackendConfig, error) { + data := os.Getenv(ConfigEnvVar) + if len(data) == 0 { + return nil, fmt.Errorf("Cannot find config: `%v` not set", ConfigEnvVar) + } + return BackendConfigFromJSON(data) +} diff --git a/pkg/secrets/config_test.go b/pkg/secrets/config_test.go new file mode 100644 index 00000000..319b226d --- /dev/null +++ b/pkg/secrets/config_test.go @@ -0,0 +1,29 @@ +package secrets + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestBackendConfigFromJSON(t *testing.T) { + Convey("Given a JSON backend config string", t, func() { + configData := `{ + "Name": "dummy-example", + "Type": "dummy", + "Parameters": { + "Suffix": "-ohlord" + } + }` + + Convey("When creating a BackendConfig object", func() { + backendConfig, err := BackendConfigFromJSON(configData) + So(err, ShouldBeNil) + Convey("The data in BackendConfig is as expected", func() { + So(backendConfig.Name, ShouldEqual, "dummy-example") + So(backendConfig.Type, ShouldEqual, "dummy") + So(backendConfig.Parameters, ShouldResemble, map[string]string{"Suffix": "-ohlord"}) + }) + }) + }) +} diff --git a/pkg/secrets/dummy.go b/pkg/secrets/dummy.go index 5d6ca904..a37a26a8 100644 --- a/pkg/secrets/dummy.go +++ b/pkg/secrets/dummy.go @@ -6,14 +6,19 @@ type DummySecretsBackend struct { suffix string } +func init() { + BackendRegister("dummy", NewDummySecretsBackend) +} + // NewDummySecretsBackend gives you an new DummySecretsBackend -func NewDummySecretsBackend() *DummySecretsBackend { +func NewDummySecretsBackend() BackendIface { return &DummySecretsBackend{} } // Init implements SecretsBackend interface, sets the suffix func (d *DummySecretsBackend) Init(parameters ...interface{}) error { - d.suffix = parameters[0].(string) + params := parameters[0].(map[string]string) + d.suffix = params["suffix"] return nil } diff --git a/version/version.go b/version/version.go index e3e130bf..2a0eddf9 100644 --- a/version/version.go +++ b/version/version.go @@ -1,5 +1,5 @@ package version var ( - Version = "0.0.1" + Version = "0.0.2" )