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 ]
```
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.
```diff

View file

@ -72,7 +72,7 @@ func (c *Compiler) createProcess(name string, container *yaml.Container, section
}
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")
}
}

View file

@ -13,7 +13,7 @@ import (
// paramsToEnv uses reflection to convert a map[string]interface to a list
// 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 {
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 {
continue
}
to[sanitizeParamKey(k)], err = sanitizeParamValue(v)
to[sanitizeParamKey(k)], err = sanitizeParamValue(v, secrets)
if err != nil {
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)
vv := reflect.ValueOf(v)
@ -66,6 +66,16 @@ func sanitizeParamValue(v interface{}) (string, error) {
return fmt.Sprintf("%v", vv.Float()), nil
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())
out, _ := yml.ToJSON(ymlOut)
return string(out), nil
@ -78,7 +88,7 @@ func sanitizeParamValue(v interface{}) (string, error) {
in := make([]string, vv.Len())
for i := 0; i < vv.Len(); i++ {
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
}
}

View file

@ -4,6 +4,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
)
func TestParamsToEnv(t *testing.T) {
@ -20,6 +21,7 @@ func TestParamsToEnv(t *testing.T) {
"from.address": "noreply@example.com",
"tags": stringsToInterface("next", "latest"),
"tag": stringsToInterface("next"),
"my_secret": map[string]interface{}{"from_secret": "secret_token"},
}
want := map[string]string{
"PLUGIN_STRING": "stringz",
@ -33,12 +35,59 @@ func TestParamsToEnv(t *testing.T) {
"PLUGIN_FROM_ADDRESS": "noreply@example.com",
"PLUGIN_TAG": "next",
"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{}
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")
}
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{} {
res := make([]interface{}, len(val))
for i := range val {