runtime: support for dynamic secrets (#1861)

* runtime: replace CreateCredentials with CreateSecret

* runtime/kubernetes: secrets support

* runtime: CreateSecret => WithSecret

* runtime: use map[string]string for secrets

* runtime/kubernetes: update to use kv secrets

* Fix merge conflict (missing import)

Co-authored-by: Asim Aslam <asim@aslam.me>
This commit is contained in:
ben-toogood 2020-07-29 13:41:50 +01:00 committed by GitHub
parent 3d1ba914fc
commit 006bbefaf3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 27 additions and 49 deletions

View File

@ -3,7 +3,6 @@ package kubernetes
import (
"encoding/base64"
"errors"
"fmt"
"strings"
"sync"
@ -450,9 +449,8 @@ func (k *kubernetes) Create(s *runtime.Service, opts ...runtime.CreateOption) er
options.Image = k.getImage(s, options)
// create a secret for the credentials if some where provided
if len(options.Credentials) > 0 {
secret, err := k.createCredentials(s, options)
if err != nil {
if len(options.Secrets) > 0 {
if err := k.createCredentials(s, options); err != nil {
if logger.V(logger.WarnLevel, logger.DefaultLogger) {
logger.Warnf("Error generating auth credentials for service: %v", err)
}
@ -462,9 +460,6 @@ func (k *kubernetes) Create(s *runtime.Service, opts ...runtime.CreateOption) er
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("Generated auth credentials for service %v", s.Name)
}
// pass the secret name to the client via the credentials option
options.Credentials = secret
}
// create new service
@ -689,33 +684,27 @@ func (k *kubernetes) getImage(s *runtime.Service, options runtime.CreateOptions)
return ""
}
func (k *kubernetes) createCredentials(service *runtime.Service, options runtime.CreateOptions) (string, error) {
// validate the creds
comps := strings.Split(options.Credentials, ":")
if len(comps) != 2 {
return "", errors.New("Invalid credentials, expected format 'user:pass'")
func (k *kubernetes) createCredentials(service *runtime.Service, options runtime.CreateOptions) error {
data := make(map[string]string, len(options.Secrets))
for key, value := range options.Secrets {
data[key] = base64.StdEncoding.EncodeToString([]byte(value))
}
// construct the k8s secret object
secret := &client.Secret{
Type: "Opaque",
Data: map[string]string{
"id": base64.StdEncoding.EncodeToString([]byte(comps[0])),
"secret": base64.StdEncoding.EncodeToString([]byte(comps[1])),
},
Data: data,
Metadata: &client.Metadata{
Name: credentialsName(service),
Namespace: options.Namespace,
},
}
// create options specify the namespace
ns := client.CreateNamespace(options.Namespace)
// crete the secret in kubernetes
name := credentialsName(service)
err := k.client.Create(&client.Resource{Kind: "secret", Name: name, Value: secret}, ns)
return name, err
return k.client.Create(&client.Resource{
Kind: "secret", Name: name, Value: secret,
}, client.CreateNamespace(options.Namespace))
}
func credentialsName(service *runtime.Service) string {

View File

@ -75,22 +75,14 @@ func newService(s *runtime.Service, c runtime.CreateOptions) *service {
env = append(env, client.EnvVar{Name: evarPair[0], Value: evarPair[1]})
}
// if credentials were provided, pass them to the service
if len(c.Credentials) > 0 {
// if secrets were provided, pass them to the service
for key := range c.Secrets {
env = append(env, client.EnvVar{
Name: "MICRO_AUTH_ID",
Name: key,
ValueFrom: &client.EnvVarSource{
SecretKeyRef: &client.SecretKeySelector{
Name: c.Credentials, Key: "id",
},
},
})
env = append(env, client.EnvVar{
Name: "MICRO_AUTH_SECRET",
ValueFrom: &client.EnvVarSource{
SecretKeyRef: &client.SecretKeySelector{
Name: c.Credentials, Key: "secret",
Name: credentialsName(s),
Key: key,
},
},
})

View File

@ -270,16 +270,9 @@ func (r *localRuntime) Create(s *runtime.Service, opts ...runtime.CreateOption)
options.Args = []string{"run", "."}
}
// pass credentials as env vars
if len(options.Credentials) > 0 {
// validate the creds
comps := strings.Split(options.Credentials, ":")
if len(comps) != 2 {
return errors.New("Invalid credentials, expected format 'user:pass'")
}
options.Env = append(options.Env, "MICRO_AUTH_ID", comps[0])
options.Env = append(options.Env, "MICRO_AUTH_SECRET", comps[1])
// pass secrets as env vars
for key, value := range options.Secrets {
options.Env = append(options.Env, key, value)
}
if _, ok := r.namespaces[options.Namespace]; !ok {

View File

@ -82,8 +82,8 @@ type CreateOptions struct {
Namespace string
// Specify the context to use
Context context.Context
// Credentials for the service to use
Credentials string
// Secrets to use
Secrets map[string]string
}
// ReadOptions queries runtime services
@ -128,10 +128,14 @@ func CreateContext(ctx context.Context) CreateOption {
}
}
// CreateCredentials sets the credentials to start the service with
func CreateCredentials(user, pass string) CreateOption {
// WithSecret sets a secret to provide the service with
func WithSecret(key, value string) CreateOption {
return func(o *CreateOptions) {
o.Credentials = user + ":" + pass
if o.Secrets == nil {
o.Secrets = map[string]string{key: value}
} else {
o.Secrets[key] = value
}
}
}