diff --git a/cmd/nodeagent/main.go b/cmd/nodeagent/main.go index 220c34258..434b70a31 100644 --- a/cmd/nodeagent/main.go +++ b/cmd/nodeagent/main.go @@ -25,7 +25,7 @@ import ( var gitCommit string var version string - + func getEnv(key, fallback string) string { if value, ok := os.LookupEnv(key); ok { return value diff --git a/cmd/orbctl/readsecret.go b/cmd/orbctl/readsecret.go index da7e5b633..a5a9c0a74 100644 --- a/cmd/orbctl/readsecret.go +++ b/cmd/orbctl/readsecret.go @@ -13,8 +13,9 @@ func readSecretCommand(rv rootValues) *cobra.Command { return &cobra.Command{ Use: "readsecret [path]", - Short: "Decrypt and print to stdout", - Args: cobra.ExactArgs(1), + Short: "Print a secrets decrypted value to stdout", + Long: "Print a secrets decrypted value to stdout.\nIf no path is provided, a secret can interactively be chosen from a list of all possible secrets", + Args: cobra.MaximumNArgs(1), Example: `orbctl readsecret k8s.kubeconfig > ~/.kube/config`, RunE: func(cmd *cobra.Command, args []string) error { @@ -23,6 +24,11 @@ func readSecretCommand(rv rootValues) *cobra.Command { return errFunc(cmd) } + path := "" + if len(args) > 0 { + path = args[0] + } + value, err := orbiter.ReadSecret( gitClient, orb.AdaptFunc(logger, @@ -30,7 +36,7 @@ func readSecretCommand(rv rootValues) *cobra.Command { gitCommit, false, false), - args[0]) + path) if err != nil { panic(err) } diff --git a/cmd/orbctl/writesecret.go b/cmd/orbctl/writesecret.go index 65347c47c..667b3df0b 100644 --- a/cmd/orbctl/writesecret.go +++ b/cmd/orbctl/writesecret.go @@ -17,9 +17,10 @@ func writeSecretCommand(rv rootValues) *cobra.Command { file string stdin bool cmd = &cobra.Command{ - Use: "writesecret [name]", - Short: "Encrypt and push", - Args: cobra.ExactArgs(1), + Use: "writesecret [path]", + Short: "Encrypt a secret and push it to the repository", + Long: "Encrypt a secret and push it to the repository.\nIf no path is provided, a secret can interactively be chosen from a list of all possible secrets", + Args: cobra.MaximumNArgs(1), Example: `orbctl writesecret mystaticprovider.bootstrapkey --file ~/.ssh/my-orb-bootstrap orbctl writesecret mystaticprovider.bootstrapkey_pub --file ~/.ssh/my-orb-bootstrap.pub orbctl writesecret mygceprovider.google_application_credentials_value --value "$(cat $GOOGLE_APPLICATION_CREDENTIALS)" `, @@ -27,9 +28,9 @@ orbctl writesecret mygceprovider.google_application_credentials_value --value "$ ) flags := cmd.Flags() - flags.StringVar(&value, "value", "", "Secret phrase value used for encrypting and decrypting secrets") - flags.StringVarP(&file, "file", "s", "", "Secret phrase file used for encrypting and decrypting secrets") - flags.BoolVar(&stdin, "stdin", false, "Read Secret phrase used for encrypting and decrypting secrets from standard input") + flags.StringVar(&value, "value", "", "Secret value to encrypt") + flags.StringVarP(&file, "file", "s", "", "File containing the value to encrypt") + flags.BoolVar(&stdin, "stdin", false, "Value to encrypt is read from standard input") cmd.RunE = func(cmd *cobra.Command, args []string) error { @@ -43,6 +44,11 @@ orbctl writesecret mygceprovider.google_application_credentials_value --value "$ return errFunc(cmd) } + path := "" + if len(args) > 0 { + path = args[0] + } + if err := orbiter.WriteSecret( gitClient, orb.AdaptFunc(logger, @@ -50,7 +56,7 @@ orbctl writesecret mygceprovider.google_application_credentials_value --value "$ gitCommit, false, false), - args[0], + path, s); err != nil { panic(err) } diff --git a/go.mod b/go.mod index 8b35fca88..73bfaa57f 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/go-logr/logr v0.1.0 github.com/goombaio/dag v0.0.0-20181006234417-a8874b1f72ff github.com/imdario/mergo v0.3.8 // indirect + github.com/manifoldco/promptui v0.7.0 github.com/mitchellh/mapstructure v1.1.2 github.com/pkg/errors v0.8.1 github.com/robfig/cron v1.2.0 diff --git a/go.sum b/go.sum index e28db3b20..162b79b2d 100644 --- a/go.sum +++ b/go.sum @@ -43,6 +43,10 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= @@ -174,6 +178,8 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= +github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= @@ -193,6 +199,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= +github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw= +github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -200,6 +208,12 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/manifoldco/promptui v0.7.0 h1:3l11YT8tm9MnwGFQ4kETwkzpAwY2Jt9lCrumCUW4+z4= +github.com/manifoldco/promptui v0.7.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ= +github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -341,6 +355,7 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/internal/operator/orbiter/adapt.go b/internal/operator/orbiter/adapt.go index c9ee28e75..42de71798 100644 --- a/internal/operator/orbiter/adapt.go +++ b/internal/operator/orbiter/adapt.go @@ -6,7 +6,7 @@ import ( "gopkg.in/yaml.v3" ) -type AdaptFunc func(desired *Tree, secrets *Tree, current *Tree) (EnsureFunc, DestroyFunc, ReadSecretFunc, WriteSecretFunc, error) +type AdaptFunc func(desired *Tree, secrets *Tree, current *Tree) (EnsureFunc, DestroyFunc, map[string]*Secret, error) func parse(gitClient *git.Client) (desired *Tree, secrets *Tree, err error) { diff --git a/internal/operator/orbiter/destroy.go b/internal/operator/orbiter/destroy.go index dcc8d2591..0d444830e 100644 --- a/internal/operator/orbiter/destroy.go +++ b/internal/operator/orbiter/destroy.go @@ -15,7 +15,7 @@ func Destroy(gitClient *git.Client, adapt AdaptFunc) error { } treeCurrent := &Tree{} - _, destroy, _, _, err := adapt(treeDesired, treeSecrets, treeCurrent) + _, destroy, _, err := adapt(treeDesired, treeSecrets, treeCurrent) if err != nil { return err } diff --git a/internal/operator/orbiter/kinds/clusters/kubernetes/adapt.go b/internal/operator/orbiter/kinds/clusters/kubernetes/adapt.go index e4375374b..1d4aa9464 100644 --- a/internal/operator/orbiter/kinds/clusters/kubernetes/adapt.go +++ b/internal/operator/orbiter/kinds/clusters/kubernetes/adapt.go @@ -17,24 +17,28 @@ func AdaptFunc( deployOrbiterAndBoom bool, ensureProviders func(psf orbiter.PushSecretsFunc, nodeAgentsCurrent map[string]*common.NodeAgentCurrent, nodeAgentsDesired map[string]*common.NodeAgentSpec) (map[string]interface{}, error), destroyProviders func() (map[string]interface{}, error)) orbiter.AdaptFunc { - return func(desiredTree *orbiter.Tree, secretsTree *orbiter.Tree, currentTree *orbiter.Tree) (ensureFunc orbiter.EnsureFunc, destroyFunc orbiter.DestroyFunc, readSecretFunc orbiter.ReadSecretFunc, writeSecretFunc orbiter.WriteSecretFunc, err error) { + return func(desiredTree *orbiter.Tree, secretsTree *orbiter.Tree, currentTree *orbiter.Tree) (ensureFunc orbiter.EnsureFunc, destroyFunc orbiter.DestroyFunc, secrets map[string]*orbiter.Secret, err error) { defer func() { err = errors.Wrapf(err, "building %s failed", desiredTree.Common.Kind) }() desiredKind := &DesiredV0{Common: *desiredTree.Common} if err := desiredTree.Original.Decode(desiredKind); err != nil { - return nil, nil, nil, nil, errors.Wrap(err, "parsing desired state failed") + return nil, nil, nil, errors.Wrap(err, "parsing desired state failed") } desiredKind.Common.Version = "v0" desiredTree.Parsed = desiredKind + if desiredKind.Spec.Verbose && !logger.IsVerbose() { + logger = logger.Verbose() + } + secretsKind := &SecretsV0{ Common: *secretsTree.Common, Secrets: Secrets{Kubeconfig: &orbiter.Secret{Masterkey: orb.Masterkey}}, } if err := secretsTree.Original.Decode(secretsKind); err != nil { - return nil, nil, nil, nil, errors.Wrap(err, "parsing secrets failed") + return nil, nil, nil, errors.Wrap(err, "parsing secrets failed") } secretsKind.Common.Version = "v0" secretsTree.Parsed = secretsKind @@ -45,7 +49,7 @@ func AdaptFunc( if deployOrbiterAndBoom && secretsKind.Secrets.Kubeconfig.Value != "" { if err := ensureArtifacts(logger, secretsKind.Secrets.Kubeconfig, orb, takeoff, desiredKind.Spec.Versions.Orbiter, desiredKind.Spec.Versions.Boom); err != nil { - return nil, nil, nil, nil, err + return nil, nil, nil, err } } @@ -58,10 +62,6 @@ func AdaptFunc( Current: *current, } - secretsMap := map[string]*orbiter.Secret{ - "kubeconfig": secretsKind.Secrets.Kubeconfig, - } - return func(psf orbiter.PushSecretsFunc, nodeAgentsCurrent map[string]*common.NodeAgentCurrent, nodeAgentsDesired map[string]*common.NodeAgentSpec) (err error) { defer func() { err = errors.Wrapf(err, "ensuring %s failed", desiredKind.Common.Kind) @@ -95,10 +95,8 @@ func AdaptFunc( } return destroy(providers, secretsKind.Secrets.Kubeconfig) - }, func(path []string) (string, error) { - return orbiter.AdaptReadSecret(path, nil, secretsMap) - }, func(path []string, value string) error { - return orbiter.AdaptWriteSecret(path, value, nil, secretsMap) + }, map[string]*orbiter.Secret{ + "kubeconfig": secretsKind.Secrets.Kubeconfig, }, nil } } diff --git a/internal/operator/orbiter/kinds/clusters/kubernetes/desired.go b/internal/operator/orbiter/kinds/clusters/kubernetes/desired.go index af08b5b19..da12ef59e 100644 --- a/internal/operator/orbiter/kinds/clusters/kubernetes/desired.go +++ b/internal/operator/orbiter/kinds/clusters/kubernetes/desired.go @@ -6,7 +6,6 @@ import ( type DesiredV0 struct { Common orbiter.Common `yaml:",inline"` - Deps map[string]*orbiter.Tree Spec struct { Verbose bool Versions struct { diff --git a/internal/operator/orbiter/kinds/clusters/kubernetes/secrets.go b/internal/operator/orbiter/kinds/clusters/kubernetes/secrets.go index ec1807262..a319be489 100644 --- a/internal/operator/orbiter/kinds/clusters/kubernetes/secrets.go +++ b/internal/operator/orbiter/kinds/clusters/kubernetes/secrets.go @@ -7,7 +7,6 @@ import ( type SecretsV0 struct { Common orbiter.Common `yaml:",inline"` Secrets Secrets - Deps map[string]*orbiter.Tree } type Secrets struct { diff --git a/internal/operator/orbiter/kinds/loadbalancers/dynamic/adapt.go b/internal/operator/orbiter/kinds/loadbalancers/dynamic/adapt.go index e3c8e75dc..bcfa98bd7 100644 --- a/internal/operator/orbiter/kinds/loadbalancers/dynamic/adapt.go +++ b/internal/operator/orbiter/kinds/loadbalancers/dynamic/adapt.go @@ -14,16 +14,16 @@ import ( ) func AdaptFunc(remoteUser string) orbiter.AdaptFunc { - return func(desiredTree *orbiter.Tree, secretsTree *orbiter.Tree, currentTree *orbiter.Tree) (ensureFunc orbiter.EnsureFunc, destroyFunc orbiter.DestroyFunc, readSecretFunc orbiter.ReadSecretFunc, writeSecretFunc orbiter.WriteSecretFunc, err error) { + return func(desiredTree *orbiter.Tree, secretsTree *orbiter.Tree, currentTree *orbiter.Tree) (ensureFunc orbiter.EnsureFunc, destroyFunc orbiter.DestroyFunc, secrets map[string]*orbiter.Secret, err error) { defer func() { err = errors.Wrapf(err, "building %s failed", desiredTree.Common.Kind) }() desiredKind := &DesiredV0{Common: desiredTree.Common} if err := desiredTree.Original.Decode(desiredKind); err != nil { - return nil, nil, nil, nil, errors.Wrapf(err, "unmarshaling desired state for kind %s failed", desiredTree.Common.Kind) + return nil, nil, nil, errors.Wrapf(err, "unmarshaling desired state for kind %s failed", desiredTree.Common.Kind) } if err := desiredKind.Validate(); err != nil { - return nil, nil, nil, nil, err + return nil, nil, nil, err } desiredKind.Common.Version = "v0" desiredTree.Parsed = desiredKind @@ -253,7 +253,7 @@ http { } return nil } - return nil, nil, nil, nil, nil + return nil, nil, nil, nil } } diff --git a/internal/operator/orbiter/kinds/orb/adapt.go b/internal/operator/orbiter/kinds/orb/adapt.go index 0a3c467ff..83b70008a 100644 --- a/internal/operator/orbiter/kinds/orb/adapt.go +++ b/internal/operator/orbiter/kinds/orb/adapt.go @@ -16,21 +16,25 @@ func AdaptFunc( orbiterCommit string, oneoff bool, deployOrbiterAndBoom bool) orbiter.AdaptFunc { - return func(desiredTree *orbiter.Tree, secretsTree *orbiter.Tree, currentTree *orbiter.Tree) (ensureFunc orbiter.EnsureFunc, destroyFunc orbiter.DestroyFunc, readSecretFunc orbiter.ReadSecretFunc, writeSecretFunc orbiter.WriteSecretFunc, err error) { + return func(desiredTree *orbiter.Tree, secretsTree *orbiter.Tree, currentTree *orbiter.Tree) (ensureFunc orbiter.EnsureFunc, destroyFunc orbiter.DestroyFunc, secrets map[string]*orbiter.Secret, err error) { defer func() { err = errors.Wrapf(err, "building %s failed", desiredTree.Common.Kind) }() desiredKind := &DesiredV0{Common: desiredTree.Common} if err := desiredTree.Original.Decode(desiredKind); err != nil { - return nil, nil, nil, nil, errors.Wrap(err, "parsing desired state failed") + return nil, nil, nil, errors.Wrap(err, "parsing desired state failed") } desiredKind.Common.Version = "v0" desiredTree.Parsed = desiredKind + if desiredKind.Spec.Verbose && !logger.IsVerbose() { + logger = logger.Verbose() + } + secretsKind := &SecretsV0{Common: secretsTree.Common} if err := secretsTree.Original.Decode(secretsKind); err != nil { - return nil, nil, nil, nil, errors.Wrap(err, "parsing secrets failed") + return nil, nil, nil, errors.Wrap(err, "parsing secrets failed") } secretsKind.Common.Version = "v0" secretsTree.Parsed = secretsKind @@ -38,8 +42,8 @@ func AdaptFunc( providerCurrents := make(map[string]*orbiter.Tree) providerEnsurers := make([]orbiter.EnsureFunc, 0) providerDestroyers := make([]orbiter.DestroyFunc, 0) - depSecretReaders := make(map[string]orbiter.ReadSecretFunc) - depSecretWriters := make(map[string]orbiter.WriteSecretFunc) + secrets = make(map[string]*orbiter.Secret) + for provID, providerTree := range desiredKind.Deps.Providers { providerCurrent := &orbiter.Tree{} @@ -80,17 +84,17 @@ func AdaptFunc( // updatesDisabled = append(updatesDisabled, desiredKind.Spec.ControlPlane.Pool) // } - providerEnsurer, providerDestroyer, providerSecretReader, providerSecretWriter, err := static.AdaptFunc(logger, orb.Masterkey, provID)(providerTree, providerSecretsTree, providerCurrent) + providerEnsurer, providerDestroyer, providerSecrets, err := static.AdaptFunc(logger, orb.Masterkey, provID)(providerTree, providerSecretsTree, providerCurrent) if err != nil { - return nil, nil, nil, nil, err + return nil, nil, nil, err } providerEnsurers = append(providerEnsurers, providerEnsurer) providerDestroyers = append(providerDestroyers, providerDestroyer) - depSecretReaders[provID] = providerSecretReader - depSecretWriters[provID] = providerSecretWriter - // subassemblers[provIdx] = static.New(providerPath, generalOverwriteSpec, staticadapter.New(providerlogger, providerID, "/healthz", updatesDisabled, cfg.NodeAgent)) + for path, secret := range providerSecrets { + secrets[orbiter.JoinPath(provID, path)] = secret + } default: - return nil, nil, nil, nil, errors.Errorf("unknown provider kind %s", providerTree.Common.Kind) + return nil, nil, nil, errors.Errorf("unknown provider kind %s", providerTree.Common.Kind) } } @@ -141,23 +145,24 @@ func AdaptFunc( clusterSecretsTree, ok := secretsKind.Deps.Clusters[clusterID] if !ok { - return nil, nil, nil, nil, errors.Errorf("no secrets found for cluster %s", clusterID) + return nil, nil, nil, errors.Errorf("no secrets found for cluster %s", clusterID) } switch clusterTree.Common.Kind { case "orbiter.caos.ch/KubernetesCluster": - clusterEnsurer, clusterDestroyer, clusterSecretReader, clusterSecretWriter, err := kubernetes.AdaptFunc(logger, orb, orbiterCommit, clusterID, oneoff, deployOrbiterAndBoom, ensureProviders, destroyProviders)(clusterTree, clusterSecretsTree, clusterCurrent) + clusterEnsurer, clusterDestroyer, clusterSecrets, err := kubernetes.AdaptFunc(logger, orb, orbiterCommit, clusterID, oneoff, deployOrbiterAndBoom, ensureProviders, destroyProviders)(clusterTree, clusterSecretsTree, clusterCurrent) if err != nil { - return nil, nil, nil, nil, err + return nil, nil, nil, err } clusterEnsurers = append(clusterEnsurers, clusterEnsurer) clusterDestroyers = append(clusterDestroyers, clusterDestroyer) - depSecretReaders[clusterID] = clusterSecretReader - depSecretWriters[clusterID] = clusterSecretWriter + for path, secret := range clusterSecrets { + secrets[orbiter.JoinPath(clusterID, path)] = secret + } // subassemblers[provIdx] = static.New(providerPath, generalOverwriteSpec, staticadapter.New(providerlogger, providerID, "/healthz", updatesDisabled, cfg.NodeAgent)) default: - return nil, nil, nil, nil, errors.Errorf("unknown cluster kind %s", clusterTree.Common.Kind) + return nil, nil, nil, errors.Errorf("unknown cluster kind %s", clusterTree.Common.Kind) } } @@ -194,10 +199,6 @@ func AdaptFunc( } } return nil - }, func(path []string) (string, error) { - return orbiter.AdaptReadSecret(path, depSecretReaders, nil) - }, func(path []string, value string) error { - return orbiter.AdaptWriteSecret(path, value, depSecretWriters, nil) - }, nil + }, secrets, nil } } diff --git a/internal/operator/orbiter/kinds/orb/model.go b/internal/operator/orbiter/kinds/orb/model.go index bf0cf5b79..4d225315c 100644 --- a/internal/operator/orbiter/kinds/orb/model.go +++ b/internal/operator/orbiter/kinds/orb/model.go @@ -9,7 +9,10 @@ type Deps struct { type DesiredV0 struct { Common *orbiter.Common `yaml:",inline"` - Deps Deps + Spec struct { + Verbose bool + } + Deps Deps } type SecretsV0 struct { diff --git a/internal/operator/orbiter/kinds/providers/static/adapt.go b/internal/operator/orbiter/kinds/providers/static/adapt.go index a593a6d0e..4d04e1f53 100644 --- a/internal/operator/orbiter/kinds/providers/static/adapt.go +++ b/internal/operator/orbiter/kinds/providers/static/adapt.go @@ -10,17 +10,21 @@ import ( ) func AdaptFunc(logger logging.Logger, masterkey string, id string) orbiter.AdaptFunc { - return func(desiredTree *orbiter.Tree, secretsTree *orbiter.Tree, currentTree *orbiter.Tree) (ensureFunc orbiter.EnsureFunc, destroyFunc orbiter.DestroyFunc, readSecretFunc orbiter.ReadSecretFunc, writeSecretFunc orbiter.WriteSecretFunc, err error) { + return func(desiredTree *orbiter.Tree, secretsTree *orbiter.Tree, currentTree *orbiter.Tree) (ensureFunc orbiter.EnsureFunc, destroyFunc orbiter.DestroyFunc, secrets map[string]*orbiter.Secret, err error) { defer func() { err = errors.Wrapf(err, "building %s failed", desiredTree.Common.Kind) }() desiredKind := &DesiredV0{Common: desiredTree.Common} if err := desiredTree.Original.Decode(desiredKind); err != nil { - return nil, nil, nil, nil, errors.Wrap(err, "parsing desired state failed") + return nil, nil, nil, errors.Wrap(err, "parsing desired state failed") } desiredKind.Common.Version = "v0" desiredTree.Parsed = desiredKind + if desiredKind.Spec.Verbose && !logger.IsVerbose() { + logger = logger.Verbose() + } + secretsKind := &SecretsV0{ Common: secretsTree.Common, Secrets: Secrets{ @@ -31,8 +35,24 @@ func AdaptFunc(logger logging.Logger, masterkey string, id string) orbiter.Adapt }, } if err := secretsTree.Original.Decode(secretsKind); err != nil { - return nil, nil, nil, nil, errors.Wrap(err, "parsing secrets failed") + return nil, nil, nil, errors.Wrap(err, "parsing secrets failed") + } + if secretsKind.Secrets.BootstrapKeyPrivate == nil { + secretsKind.Secrets.BootstrapKeyPrivate = &orbiter.Secret{Masterkey: masterkey} + } + + if secretsKind.Secrets.BootstrapKeyPublic == nil { + secretsKind.Secrets.BootstrapKeyPublic = &orbiter.Secret{Masterkey: masterkey} + } + + if secretsKind.Secrets.MaintenanceKeyPrivate == nil { + secretsKind.Secrets.MaintenanceKeyPrivate = &orbiter.Secret{Masterkey: masterkey} + } + + if secretsKind.Secrets.MaintenanceKeyPublic == nil { + secretsKind.Secrets.MaintenanceKeyPublic = &orbiter.Secret{Masterkey: masterkey} } + secretsKind.Common.Version = "v0" secretsTree.Parsed = secretsKind @@ -41,12 +61,12 @@ func AdaptFunc(logger logging.Logger, masterkey string, id string) orbiter.Adapt // case "orbiter.caos.ch/ExternalLoadBalancer": // return []orbiter.Assembler{external.New(depPath, generalOverwriteSpec, externallbadapter.New())}, nil case "orbiter.caos.ch/DynamicLoadBalancer": - if _, _, _, _, err = dynamic.AdaptFunc(desiredKind.Spec.RemoteUser)(desiredKind.Deps, nil, lbCurrent); err != nil { - return nil, nil, nil, nil, err + if _, _, _, err = dynamic.AdaptFunc(desiredKind.Spec.RemoteUser)(desiredKind.Deps, nil, lbCurrent); err != nil { + return nil, nil, nil, err } // return []orbiter.Assembler{dynamic.New(depPath, generalOverwriteSpec, dynamiclbadapter.New(kind.Spec.RemoteUser))}, nil default: - return nil, nil, nil, nil, errors.Errorf("unknown loadbalancing kind %s", desiredKind.Deps.Common.Kind) + return nil, nil, nil, errors.Errorf("unknown loadbalancing kind %s", desiredKind.Deps.Common.Kind) } current := &Current{ @@ -58,21 +78,15 @@ func AdaptFunc(logger logging.Logger, masterkey string, id string) orbiter.Adapt } currentTree.Parsed = current - secretsMap := map[string]*orbiter.Secret{ - "bootstrapkeyprivate": secretsKind.Secrets.BootstrapKeyPrivate, - "bootstrapkeypublic": secretsKind.Secrets.BootstrapKeyPublic, - "maintenancekeyprivate": secretsKind.Secrets.MaintenanceKeyPrivate, - "maintenancekeypublic": secretsKind.Secrets.MaintenanceKeyPublic, - } - return func(psf orbiter.PushSecretsFunc, nodeAgentsCurrent map[string]*common.NodeAgentCurrent, nodeAgentsDesired map[string]*common.NodeAgentSpec) (err error) { return errors.Wrapf(ensure(desiredKind, current, secretsKind, psf, nodeAgentsDesired, lbCurrent.Parsed, masterkey, logger, id), "ensuring %s failed", desiredKind.Common.Kind) }, func() error { return destroy(logger, desiredKind, current, secretsKind.Secrets, id) - }, func(path []string) (string, error) { - return orbiter.AdaptReadSecret(path, nil, secretsMap) - }, func(path []string, value string) error { - return orbiter.AdaptWriteSecret(path, value, nil, secretsMap) + }, map[string]*orbiter.Secret{ + "bootstrapkeyprivate": secretsKind.Secrets.BootstrapKeyPrivate, + "bootstrapkeypublic": secretsKind.Secrets.BootstrapKeyPublic, + "maintenancekeyprivate": secretsKind.Secrets.MaintenanceKeyPrivate, + "maintenancekeypublic": secretsKind.Secrets.MaintenanceKeyPublic, }, nil } } diff --git a/internal/operator/orbiter/rwsecret.go b/internal/operator/orbiter/rwsecret.go index f07c30e15..63360d73a 100644 --- a/internal/operator/orbiter/rwsecret.go +++ b/internal/operator/orbiter/rwsecret.go @@ -1,121 +1,89 @@ package orbiter import ( + "fmt" + "sort" "strings" - "github.com/pkg/errors" + "github.com/manifoldco/promptui" "github.com/caos/orbiter/internal/git" ) -type ReadSecretFunc func(path []string) (string, error) - -type WriteSecretFunc func(path []string, value string) error - -func AdaptReadSecret(path []string, deps map[string]ReadSecretFunc, mapping map[string]*Secret) (string, error) { - - if len(path) == 0 { - return "", errors.New("no path provided") - } - - key := path[0] - - if len(path) == 1 { - if len(mapping) == 0 { - return "", errors.New("kind does not need or support secrets") - } - - secret, ok := mapping[key] - if !ok { - return "", errors.Errorf("unknown secret %s", key) - } - return secret.Value, nil - } - - if len(deps) == 0 { - return "", errors.Errorf("kind does not need or support dependencies") +func JoinPath(base string, append ...string) string { + for _, item := range append { + base = fmt.Sprintf("%s.%s", base, item) } + return base +} - read, ok := deps[key] - if !ok { - return "", errors.Errorf("dependency %s not found", key) - } +func ReadSecret(gitClient *git.Client, adapt AdaptFunc, path string) (string, error) { - if read == nil { - return "", errors.Errorf("dependency %s does not need or support secrets", key) + secret, _, err := findSecret(gitClient, adapt, path) + if err != nil { + return "", err } - val, err := read(path[1:]) - return val, errors.Wrapf(err, "reading secret from dependency %s failed", key) + return secret.Value, nil } -func AdaptWriteSecret(path []string, value string, deps map[string]WriteSecretFunc, mapping map[string]*Secret) error { +func WriteSecret(gitClient *git.Client, adapt AdaptFunc, path, value string) error { - if len(path) == 0 { - return errors.New("no path provided") + secret, tree, err := findSecret(gitClient, adapt, path) + if err != nil { + return err } - key := path[0] + secret.Value = value - if len(path) == 1 { - if len(mapping) == 0 { - return errors.New("kind does not need or support secrets") - } + return pushSecretsFunc(gitClient, tree)() +} - secret, ok := mapping[key] - if !ok { - return errors.Errorf("unknown secret %s", key) - } - secret.Value = value - return nil +func findSecret(gitClient *git.Client, adapt AdaptFunc, path string) (*Secret, *Tree, error) { + treeDesired, treeSecrets, err := parse(gitClient) + if err != nil { + return nil, nil, err } - if len(deps) == 0 { - return errors.Errorf("kind does not need or support dependencies") + _, _, secrets, err := adapt(treeDesired, treeSecrets, &Tree{}) + if err != nil { + return nil, nil, err } - write, ok := deps[key] - if !ok { - return errors.Errorf("dependency %s not found", key) + if path != "" { + sec, err := exactSecret(secrets, path) + return sec, treeSecrets, err } - if write == nil { - return errors.Errorf("dependency %s does not need or support secrets", key) + items := make([]string, 0, len(secrets)) + for key := range secrets { + items = append(items, key) } - return errors.Wrapf(write(path[1:], value), "reading secret from dependency %s failed", key) -} - -func ReadSecret(gitClient *git.Client, adapt AdaptFunc, path string) (string, error) { + sort.Slice(items, func(i, j int) bool { + iDots := strings.Count(items[i], ".") + jDots := strings.Count(items[j], ".") + return iDots < jDots || iDots == jDots && items[i] < items[j] + }) - treeDesired, treeSecrets, err := parse(gitClient) - if err != nil { - return "", err + prompt := promptui.Select{ + Label: "Select Secret", + Items: items, } - _, _, read, _, err := adapt(treeDesired, treeSecrets, &Tree{}) + _, result, err := prompt.Run() if err != nil { - return "", err + return nil, nil, err } - return read(strings.Split(path, ".")) + sec, err := exactSecret(secrets, result) + return sec, treeSecrets, err } -func WriteSecret(gitClient *git.Client, adapt AdaptFunc, path, value string) error { - - treeDesired, treeSecrets, err := parse(gitClient) - if err != nil { - return err - } - - _, _, _, write, err := adapt(treeDesired, treeSecrets, &Tree{}) - if err != nil { - return err - } - - if err := write(strings.Split(path, "."), value); err != nil { - return err +func exactSecret(secrets map[string]*Secret, path string) (*Secret, error) { + secret, ok := secrets[path] + if !ok { + return nil, fmt.Errorf("Secret %s not found", path) } - - return pushSecretsFunc(gitClient, treeSecrets)() + return secret, nil } diff --git a/internal/operator/orbiter/takeoff.go b/internal/operator/orbiter/takeoff.go index 4ce2c6f28..0c986a132 100644 --- a/internal/operator/orbiter/takeoff.go +++ b/internal/operator/orbiter/takeoff.go @@ -24,7 +24,7 @@ func Takeoff(ctx context.Context, logger logging.Logger, gitClient *git.Client, } treeCurrent := &Tree{} - ensure, _, _, _, err := adapt(treeDesired, treeSecrets, treeCurrent) + ensure, _, _, err := adapt(treeDesired, treeSecrets, treeCurrent) if err != nil { logger.Error(err) return