mirror of
https://github.com/docker/compose.git
synced 2025-07-27 07:34:10 +02:00
TestCase for the secrets init container
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
parent
85b3cbd6ea
commit
4bfab35007
@ -7,11 +7,10 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/ecs-plugin/pkg/amazon/cloudformation"
|
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/cli"
|
"github.com/compose-spec/compose-go/cli"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
amazon "github.com/docker/ecs-plugin/pkg/amazon/backend"
|
amazon "github.com/docker/ecs-plugin/pkg/amazon/backend"
|
||||||
|
"github.com/docker/ecs-plugin/pkg/amazon/cloudformation"
|
||||||
"github.com/docker/ecs-plugin/pkg/docker"
|
"github.com/docker/ecs-plugin/pkg/docker"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@ -60,13 +59,11 @@ func ConvertCommand(dockerCli command.Cli, options *composeOptions) *cobra.Comma
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
json, err := cloudformation.Marshall(template)
|
json, err := cloudformation.Marshall(template)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Failed to generate JSON: %s\n", err)
|
return err
|
||||||
} else {
|
|
||||||
fmt.Printf("%s\n", string(json))
|
|
||||||
}
|
}
|
||||||
|
fmt.Printf("%s\n", string(json))
|
||||||
return nil
|
return nil
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ require (
|
|||||||
github.com/morikuni/aec v1.0.0 // indirect
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
github.com/onsi/ginkgo v1.11.0 // indirect
|
github.com/onsi/ginkgo v1.11.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/sirupsen/logrus v1.6.0
|
github.com/sirupsen/logrus v1.6.0
|
||||||
github.com/smartystreets/goconvey v1.6.4 // indirect
|
github.com/smartystreets/goconvey v1.6.4 // indirect
|
||||||
github.com/spf13/cobra v0.0.5
|
github.com/spf13/cobra v0.0.5
|
||||||
|
@ -6,8 +6,6 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/awslabs/goformation/v4/cloudformation/secretsmanager"
|
|
||||||
|
|
||||||
ecsapi "github.com/aws/aws-sdk-go/service/ecs"
|
ecsapi "github.com/aws/aws-sdk-go/service/ecs"
|
||||||
"github.com/aws/aws-sdk-go/service/elbv2"
|
"github.com/aws/aws-sdk-go/service/elbv2"
|
||||||
cloudmapapi "github.com/aws/aws-sdk-go/service/servicediscovery"
|
cloudmapapi "github.com/aws/aws-sdk-go/service/servicediscovery"
|
||||||
@ -17,6 +15,7 @@ import (
|
|||||||
"github.com/awslabs/goformation/v4/cloudformation/elasticloadbalancingv2"
|
"github.com/awslabs/goformation/v4/cloudformation/elasticloadbalancingv2"
|
||||||
"github.com/awslabs/goformation/v4/cloudformation/iam"
|
"github.com/awslabs/goformation/v4/cloudformation/iam"
|
||||||
"github.com/awslabs/goformation/v4/cloudformation/logs"
|
"github.com/awslabs/goformation/v4/cloudformation/logs"
|
||||||
|
"github.com/awslabs/goformation/v4/cloudformation/secretsmanager"
|
||||||
cloudmap "github.com/awslabs/goformation/v4/cloudformation/servicediscovery"
|
cloudmap "github.com/awslabs/goformation/v4/cloudformation/servicediscovery"
|
||||||
"github.com/awslabs/goformation/v4/cloudformation/tags"
|
"github.com/awslabs/goformation/v4/cloudformation/tags"
|
||||||
"github.com/compose-spec/compose-go/compatibility"
|
"github.com/compose-spec/compose-go/compatibility"
|
||||||
|
@ -7,72 +7,109 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// return codes:
|
type secret struct {
|
||||||
// 1: failed to read secret from env
|
name string
|
||||||
// 2: failed to parse hierarchical secret
|
keys []string
|
||||||
// 3: failed to write secret content into file
|
}
|
||||||
|
|
||||||
|
const secretsFolder = "/run/secrets"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
for _, name := range os.Args[1:] {
|
secrets := parseInput(os.Args[1:])
|
||||||
|
|
||||||
|
for _, secret := range secrets {
|
||||||
|
err := createSecretFiles(secret, secretsFolder)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSecretFiles(secret secret, path string) error {
|
||||||
|
value, ok := os.LookupEnv(secret.name)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("%q variable not set", secret.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
secrets := filepath.Join(path, secret.name)
|
||||||
|
|
||||||
|
if len(secret.keys) == 0 {
|
||||||
|
// raw secret
|
||||||
|
fmt.Printf("inject secret %q info %s\n", secret.name, secrets)
|
||||||
|
return ioutil.WriteFile(secrets, []byte(value), 0444)
|
||||||
|
}
|
||||||
|
|
||||||
|
var unmarshalled interface{}
|
||||||
|
err := json.Unmarshal([]byte(value), &unmarshalled)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "%q secret is not a valid JSON document", secret.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
dict, ok := unmarshalled.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return errors.Wrapf(err, "%q secret is not a JSON dictionary", secret.name)
|
||||||
|
}
|
||||||
|
err = os.MkdirAll(secrets, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if contains(secret.keys, "*") {
|
||||||
|
var keys []string
|
||||||
|
for k := range dict {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
secret.keys = keys
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, k := range secret.keys {
|
||||||
|
path := filepath.Join(secrets, k)
|
||||||
|
fmt.Printf("inject secret %q info %s\n", k, path)
|
||||||
|
|
||||||
|
v, ok := dict[k]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("%q secret has no %q key", secret.name, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
var raw []byte
|
||||||
|
if s, ok := v.(string); ok {
|
||||||
|
raw = []byte(s)
|
||||||
|
} else {
|
||||||
|
raw, err = json.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(path, raw, 0444)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseInput parse secret to be dumped into secret files with syntax `VARIABLE_NAME[:COMA_SEPARATED_KEYS]`
|
||||||
|
func parseInput(input []string) []secret {
|
||||||
|
var secrets []secret
|
||||||
|
for _, name := range input {
|
||||||
i := strings.Index(name, ":")
|
i := strings.Index(name, ":")
|
||||||
var keys []string
|
var keys []string
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
keys = strings.Split(name[i+1:], ",")
|
keys = strings.Split(name[i+1:], ",")
|
||||||
name = name[:i]
|
name = name[:i]
|
||||||
}
|
}
|
||||||
value, ok := os.LookupEnv(name)
|
secrets = append(secrets, secret{
|
||||||
if !ok {
|
name: name,
|
||||||
fmt.Fprintf(os.Stderr, "%q variable not set", name)
|
keys: keys,
|
||||||
os.Exit(1)
|
})
|
||||||
}
|
|
||||||
|
|
||||||
secrets := filepath.Join("/run/secrets", name)
|
|
||||||
|
|
||||||
if len(keys) == 0 {
|
|
||||||
// raw secret
|
|
||||||
fmt.Printf("inject secret %q info %s\n", name, secrets)
|
|
||||||
err := ioutil.WriteFile(secrets, []byte(value), 0444)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
|
||||||
os.Exit(3)
|
|
||||||
}
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
var unmarshalled interface{}
|
|
||||||
err := json.Unmarshal([]byte(value), &unmarshalled)
|
|
||||||
if err == nil {
|
|
||||||
if dict, ok := unmarshalled.(map[string]interface{}); ok {
|
|
||||||
os.MkdirAll(secrets, 0555)
|
|
||||||
for k, v := range dict {
|
|
||||||
if !contains(keys, k) && !contains(keys, "*") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
path := filepath.Join(secrets, k)
|
|
||||||
fmt.Printf("inject secret %q info %s\n", k, path)
|
|
||||||
|
|
||||||
var raw []byte
|
|
||||||
if s, ok := v.(string); ok {
|
|
||||||
raw = []byte(s)
|
|
||||||
} else {
|
|
||||||
raw, err = json.Marshal(v)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ioutil.WriteFile(path, raw, 0444)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
|
||||||
os.Exit(3)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return secrets
|
||||||
}
|
}
|
||||||
|
|
||||||
func contains(keys []string, s string) bool {
|
func contains(keys []string, s string) bool {
|
||||||
|
105
ecs/secrets/main_test.go
Normal file
105
ecs/secrets/main_test.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gotest.tools/v3/assert"
|
||||||
|
"gotest.tools/v3/fs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseSecrets(t *testing.T) {
|
||||||
|
secrets := parseInput([]string{
|
||||||
|
"foo",
|
||||||
|
"bar:*",
|
||||||
|
"zot:key0,key1",
|
||||||
|
})
|
||||||
|
assert.Check(t, len(secrets) == 3)
|
||||||
|
assert.Check(t, secrets[0].name == "foo")
|
||||||
|
assert.Check(t, secrets[0].keys == nil)
|
||||||
|
|
||||||
|
assert.Check(t, secrets[1].name == "bar")
|
||||||
|
assert.Check(t, len(secrets[1].keys) == 1)
|
||||||
|
assert.Check(t, secrets[1].keys[0] == "*")
|
||||||
|
|
||||||
|
assert.Check(t, secrets[2].name == "zot")
|
||||||
|
assert.Check(t, len(secrets[2].keys) == 2)
|
||||||
|
assert.Check(t, secrets[2].keys[0] == "key0")
|
||||||
|
assert.Check(t, secrets[2].keys[1] == "key1")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRawSecret(t *testing.T) {
|
||||||
|
dir := fs.NewDir(t, "secrets").Path()
|
||||||
|
os.Setenv("raw", "something_secret")
|
||||||
|
defer os.Unsetenv("raw")
|
||||||
|
|
||||||
|
err := createSecretFiles(secret{
|
||||||
|
name: "raw",
|
||||||
|
keys: nil,
|
||||||
|
}, dir)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
file, err := ioutil.ReadFile(filepath.Join(dir, "raw"))
|
||||||
|
assert.NilError(t, err)
|
||||||
|
content := string(file)
|
||||||
|
assert.Equal(t, content, "something_secret")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectedKeysSecret(t *testing.T) {
|
||||||
|
dir := fs.NewDir(t, "secrets").Path()
|
||||||
|
os.Setenv("json", `
|
||||||
|
{
|
||||||
|
"foo": "bar",
|
||||||
|
"zot": "qix"
|
||||||
|
}`)
|
||||||
|
defer os.Unsetenv("json")
|
||||||
|
|
||||||
|
err := createSecretFiles(secret{
|
||||||
|
name: "json",
|
||||||
|
keys: []string{"foo"},
|
||||||
|
}, dir)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
file, err := ioutil.ReadFile(filepath.Join(dir, "json", "foo"))
|
||||||
|
assert.NilError(t, err)
|
||||||
|
content := string(file)
|
||||||
|
assert.Equal(t, content, "bar")
|
||||||
|
|
||||||
|
_, err = os.Stat(filepath.Join(dir, "json", "zot"))
|
||||||
|
assert.Check(t, os.IsNotExist(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllKeysSecret(t *testing.T) {
|
||||||
|
dir := fs.NewDir(t, "secrets").Path()
|
||||||
|
os.Setenv("json", `
|
||||||
|
{
|
||||||
|
"foo": "bar",
|
||||||
|
"zot": "qix"
|
||||||
|
}`)
|
||||||
|
defer os.Unsetenv("json")
|
||||||
|
|
||||||
|
err := createSecretFiles(secret{
|
||||||
|
name: "json",
|
||||||
|
keys: []string{"*"},
|
||||||
|
}, dir)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
file, err := ioutil.ReadFile(filepath.Join(dir, "json", "foo"))
|
||||||
|
assert.NilError(t, err)
|
||||||
|
content := string(file)
|
||||||
|
assert.Equal(t, content, "bar")
|
||||||
|
|
||||||
|
file, err = ioutil.ReadFile(filepath.Join(dir, "json", "zot"))
|
||||||
|
assert.NilError(t, err)
|
||||||
|
content = string(file)
|
||||||
|
assert.Equal(t, content, "qix")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnknownSecret(t *testing.T) {
|
||||||
|
dir := fs.NewDir(t, "secrets").Path()
|
||||||
|
|
||||||
|
err := createSecretFiles(secret{
|
||||||
|
name: "not_set",
|
||||||
|
keys: nil,
|
||||||
|
}, dir)
|
||||||
|
assert.Check(t, err != nil)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user