Get secrets in settings (#604)

* Get secrets in settings

Signed-off-by: jolheiser <john.olheiser@gmail.com>

* Add error test

Signed-off-by: jolheiser <john.olheiser@gmail.com>

* Add docs

Signed-off-by: jolheiser <john.olheiser@gmail.com>
This commit is contained in:
John Olheiser 2021-12-13 13:33:07 -06:00 committed by GitHub
parent 3bee9044f1
commit 9e8d1a9294
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 80 additions and 6 deletions

View file

@ -14,6 +14,21 @@ pipeline:
+ secrets: [ docker_username, docker_password ] + secrets: [ docker_username, docker_password ]
``` ```
Alternatively, you can get a `setting` from secrets using the `from_secret` syntax.
In this example, the secret named `secret_token` would be passed to the pipeline as `PLUGIN_TOKEN`.
**NOTE:** the `from_secret` syntax only works with the newer `settings` block.
```diff
pipeline:
docker:
image: my-plugin
settings:
+ token:
+ from_secret: secret_token
```
Please note parameter expressions are subject to pre-processing. When using secrets in parameter expressions they should be escaped. Please note parameter expressions are subject to pre-processing. When using secrets in parameter expressions they should be escaped.
```diff ```diff

View file

@ -72,7 +72,7 @@ func (c *Compiler) createProcess(name string, container *yaml.Container, section
} }
if !detached { if !detached {
if err := paramsToEnv(container.Settings, environment); err != nil { if err := paramsToEnv(container.Settings, environment, c.secrets); err != nil {
log.Error().Err(err).Msg("paramsToEnv") log.Error().Err(err).Msg("paramsToEnv")
} }
} }

View file

@ -13,7 +13,7 @@ import (
// paramsToEnv uses reflection to convert a map[string]interface to a list // paramsToEnv uses reflection to convert a map[string]interface to a list
// of environment variables. // of environment variables.
func paramsToEnv(from map[string]interface{}, to map[string]string) (err error) { func paramsToEnv(from map[string]interface{}, to map[string]string, secrets map[string]Secret) (err error) {
if to == nil { if to == nil {
return fmt.Errorf("no map to write to") return fmt.Errorf("no map to write to")
} }
@ -21,7 +21,7 @@ func paramsToEnv(from map[string]interface{}, to map[string]string) (err error)
if v == nil || len(k) == 0 { if v == nil || len(k) == 0 {
continue continue
} }
to[sanitizeParamKey(k)], err = sanitizeParamValue(v) to[sanitizeParamKey(k)], err = sanitizeParamValue(v, secrets)
if err != nil { if err != nil {
return err return err
} }
@ -48,7 +48,7 @@ func isComplex(t reflect.Kind) bool {
} }
} }
func sanitizeParamValue(v interface{}) (string, error) { func sanitizeParamValue(v interface{}, secrets map[string]Secret) (string, error) {
t := reflect.TypeOf(v) t := reflect.TypeOf(v)
vv := reflect.ValueOf(v) vv := reflect.ValueOf(v)
@ -66,6 +66,16 @@ func sanitizeParamValue(v interface{}) (string, error) {
return fmt.Sprintf("%v", vv.Float()), nil return fmt.Sprintf("%v", vv.Float()), nil
case reflect.Map: case reflect.Map:
if fromSecret, ok := v.(map[string]interface{}); ok {
if secretNameI, ok := fromSecret["from_secret"]; ok {
if secretName, ok := secretNameI.(string); ok {
if secret, ok := secrets[secretName]; ok {
return secret.Value, nil
}
return "", fmt.Errorf("no secret found for %q", secretName)
}
}
}
ymlOut, _ := yaml.Marshal(vv.Interface()) ymlOut, _ := yaml.Marshal(vv.Interface())
out, _ := yml.ToJSON(ymlOut) out, _ := yml.ToJSON(ymlOut)
return string(out), nil return string(out), nil
@ -78,7 +88,7 @@ func sanitizeParamValue(v interface{}) (string, error) {
in := make([]string, vv.Len()) in := make([]string, vv.Len())
for i := 0; i < vv.Len(); i++ { for i := 0; i < vv.Len(); i++ {
var err error var err error
if in[i], err = sanitizeParamValue(vv.Index(i).Interface()); err != nil { if in[i], err = sanitizeParamValue(vv.Index(i).Interface(), secrets); err != nil {
return "", err return "", err
} }
} }

View file

@ -4,6 +4,7 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
) )
func TestParamsToEnv(t *testing.T) { func TestParamsToEnv(t *testing.T) {
@ -20,6 +21,7 @@ func TestParamsToEnv(t *testing.T) {
"from.address": "noreply@example.com", "from.address": "noreply@example.com",
"tags": stringsToInterface("next", "latest"), "tags": stringsToInterface("next", "latest"),
"tag": stringsToInterface("next"), "tag": stringsToInterface("next"),
"my_secret": map[string]interface{}{"from_secret": "secret_token"},
} }
want := map[string]string{ want := map[string]string{
"PLUGIN_STRING": "stringz", "PLUGIN_STRING": "stringz",
@ -33,12 +35,59 @@ func TestParamsToEnv(t *testing.T) {
"PLUGIN_FROM_ADDRESS": "noreply@example.com", "PLUGIN_FROM_ADDRESS": "noreply@example.com",
"PLUGIN_TAG": "next", "PLUGIN_TAG": "next",
"PLUGIN_TAGS": "next,latest", "PLUGIN_TAGS": "next,latest",
"PLUGIN_MY_SECRET": "FooBar",
}
secrets := map[string]Secret{
"secret_token": {Name: "secret_token", Value: "FooBar", Match: nil},
} }
got := map[string]string{} got := map[string]string{}
assert.NoError(t, paramsToEnv(from, got)) assert.NoError(t, paramsToEnv(from, got, secrets))
assert.EqualValues(t, want, got, "Problem converting plugin parameters to environment variables") assert.EqualValues(t, want, got, "Problem converting plugin parameters to environment variables")
} }
func TestYAMLToParamsToEnv(t *testing.T) {
fromYAML := []byte(`skip: ~
string: stringz
int: 1
float: 1.2
bool: true
slice: [1, 2, 3]
my_secret:
from_secret: secret_token
`)
var from map[string]interface{}
err := yaml.Unmarshal(fromYAML, &from)
assert.NoError(t, err)
want := map[string]string{
"PLUGIN_STRING": "stringz",
"PLUGIN_INT": "1",
"PLUGIN_FLOAT": "1.2",
"PLUGIN_BOOL": "true",
"PLUGIN_SLICE": "1,2,3",
"PLUGIN_MY_SECRET": "FooBar",
}
secrets := map[string]Secret{
"secret_token": {Name: "secret_token", Value: "FooBar", Match: nil},
}
got := map[string]string{}
assert.NoError(t, paramsToEnv(from, got, secrets))
assert.EqualValues(t, want, got, "Problem converting plugin parameters to environment variables")
}
func TestYAMLToParamsToEnvError(t *testing.T) {
fromYAML := []byte(`my_secret:
from_secret: not_a_secret
`)
var from map[string]interface{}
err := yaml.Unmarshal(fromYAML, &from)
assert.NoError(t, err)
secrets := map[string]Secret{
"secret_token": {Name: "secret_token", Value: "FooBar", Match: nil},
}
assert.Error(t, paramsToEnv(from, make(map[string]string), secrets))
}
func stringsToInterface(val ...string) []interface{} { func stringsToInterface(val ...string) []interface{} {
res := make([]interface{}, len(val)) res := make([]interface{}, len(val))
for i := range val { for i := range val {