From 9d416d65f72b8ca6a54005ab7f3ae7b97ea09ef7 Mon Sep 17 00:00:00 2001 From: Andrea Luzzardi Date: Mon, 17 May 2021 11:12:46 -0700 Subject: [PATCH] secret input type, simplify state format Signed-off-by: Andrea Luzzardi --- cmd/dagger/cmd/input/list.go | 7 +-- cmd/dagger/cmd/input/secret.go | 37 ++++++++++++-- dagger/environment.go | 9 ++-- dagger/state/input.go | 93 ++++++++++++++-------------------- dagger/state/state.go | 28 ++-------- 5 files changed, 83 insertions(+), 91 deletions(-) diff --git a/cmd/dagger/cmd/input/list.go b/cmd/dagger/cmd/input/list.go index 1ce2b636..22929be0 100644 --- a/cmd/dagger/cmd/input/list.go +++ b/cmd/dagger/cmd/input/list.go @@ -10,6 +10,7 @@ import ( "dagger.io/go/cmd/dagger/logger" "dagger.io/go/dagger" "dagger.io/go/dagger/compiler" + "dagger.io/go/dagger/state" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -84,9 +85,9 @@ var listCmd = &cobra.Command{ }, } -func isUserSet(env *dagger.EnvironmentState, val *compiler.Value) bool { - for _, i := range env.Inputs { - if val.Path().String() == i.Key { +func isUserSet(env *state.State, val *compiler.Value) bool { + for key := range env.Inputs { + if val.Path().String() == key { return true } } diff --git a/cmd/dagger/cmd/input/secret.go b/cmd/dagger/cmd/input/secret.go index 7701d046..24e4f29c 100644 --- a/cmd/dagger/cmd/input/secret.go +++ b/cmd/dagger/cmd/input/secret.go @@ -1,14 +1,20 @@ package input import ( + "fmt" + "syscall" + + "dagger.io/go/cmd/dagger/logger" + "dagger.io/go/dagger/state" "github.com/spf13/cobra" "github.com/spf13/viper" + "golang.org/x/term" ) var secretCmd = &cobra.Command{ - Use: "secret TARGET VALUE", + Use: "secret [-f] []", Short: "Add an encrypted input secret", - Args: cobra.ExactArgs(2), + Args: cobra.RangeArgs(1, 2), PreRun: func(cmd *cobra.Command, args []string) { // Fix Viper bug for duplicate flags: // https://github.com/spf13/viper/issues/233 @@ -17,14 +23,35 @@ var secretCmd = &cobra.Command{ } }, Run: func(cmd *cobra.Command, args []string) { - // lg := logger.New() - // ctx := lg.WithContext(cmd.Context()) + lg := logger.New() + ctx := lg.WithContext(cmd.Context()) - panic("not implemented") + var secret string + if len(args) == 1 { + // No value specified: prompt terminal + fmt.Print("Secret: ") + data, err := term.ReadPassword(syscall.Stdin) + if err != nil { + lg.Fatal().Err(err).Msg("unable to read secret from terminal") + } + fmt.Println("") + secret = string(data) + } else { + // value specified: read it + secret = readInput(ctx, args[1]) + } + + updateEnvironmentInput( + ctx, + args[0], + state.SecretInput(secret), + ) }, } func init() { + secretCmd.Flags().BoolP("file", "f", false, "Read value from file") + if err := viper.BindPFlags(secretCmd.Flags()); err != nil { panic(err) } diff --git a/dagger/environment.go b/dagger/environment.go index b4048cd4..cddf579f 100644 --- a/dagger/environment.go +++ b/dagger/environment.go @@ -11,7 +11,6 @@ import ( cueflow "cuelang.org/go/tools/flow" "dagger.io/go/dagger/compiler" "dagger.io/go/dagger/state" - "dagger.io/go/pkg/cuetils" "dagger.io/go/stdlib" "github.com/opentracing/opentracing-go" @@ -43,15 +42,15 @@ func NewEnvironment(st *state.State) (*Environment, error) { } // Prepare inputs - for _, input := range st.Inputs { - v, err := input.Value.Compile(st) + for key, input := range st.Inputs { + v, err := input.Compile(st) if err != nil { return nil, err } - if input.Key == "" { + if key == "" { err = e.input.FillPath(cue.MakePath(), v) } else { - err = e.input.FillPath(cue.ParsePath(input.Key), v) + err = e.input.FillPath(cue.ParsePath(key), v) } if err != nil { return nil, err diff --git a/dagger/state/input.go b/dagger/state/input.go index c1ad5676..9c34bdd6 100644 --- a/dagger/state/input.go +++ b/dagger/state/input.go @@ -24,25 +24,12 @@ import ( // Under the hood, an artifact is encoded as a LLB pipeline, and // attached to the cue configuration as a // -type InputType string - -const ( - InputTypeDir InputType = "dir" - InputTypeGit InputType = "git" - InputTypeDocker InputType = "docker" - InputTypeText InputType = "text" - InputTypeJSON InputType = "json" - InputTypeYAML InputType = "yaml" - InputTypeFile InputType = "file" - InputTypeEmpty InputType = "" -) type Input struct { - Type InputType `yaml:"type,omitempty"` - Dir *dirInput `yaml:"dir,omitempty"` Git *gitInput `yaml:"git,omitempty"` Docker *dockerInput `yaml:"docker,omitempty"` + Secret *secretInput `yaml:"secret,omitempty"` Text *textInput `yaml:"text,omitempty"` JSON *jsonInput `yaml:"json,omitempty"` YAML *yamlInput `yaml:"yaml,omitempty"` @@ -50,32 +37,31 @@ type Input struct { } func (i Input) Compile(state *State) (*compiler.Value, error) { - switch i.Type { - case InputTypeDir: + switch { + case i.Dir != nil: return i.Dir.Compile(state) - case InputTypeGit: + case i.Git != nil: return i.Git.Compile(state) - case InputTypeDocker: + case i.Docker != nil: return i.Docker.Compile(state) - case InputTypeText: + case i.Text != nil: return i.Text.Compile(state) - case InputTypeJSON: + case i.Secret != nil: + return i.Secret.Compile(state) + case i.JSON != nil: return i.JSON.Compile(state) - case InputTypeYAML: + case i.YAML != nil: return i.YAML.Compile(state) - case InputTypeFile: + case i.File != nil: return i.File.Compile(state) - case "": - return nil, fmt.Errorf("input has not been set") default: - return nil, fmt.Errorf("unsupported input type: %s", i.Type) + return nil, fmt.Errorf("input has not been set") } } // An input artifact loaded from a local directory func DirInput(path string, include []string) Input { return Input{ - Type: InputTypeDir, Dir: &dirInput{ Path: path, Include: include, @@ -124,7 +110,6 @@ type gitInput struct { func GitInput(remote, ref, dir string) Input { return Input{ - Type: InputTypeGit, Git: &gitInput{ Remote: remote, Ref: ref, @@ -149,7 +134,6 @@ func (git gitInput) Compile(_ *State) (*compiler.Value, error) { // An input artifact loaded from a docker container func DockerInput(ref string) Input { return Input{ - Type: InputTypeDocker, Docker: &dockerInput{ Ref: ref, }, @@ -166,63 +150,62 @@ func (i dockerInput) Compile(_ *State) (*compiler.Value, error) { // An input value encoded as text func TextInput(data string) Input { + i := textInput(data) return Input{ - Type: InputTypeText, - Text: &textInput{ - Data: data, - }, + Text: &i, } } -type textInput struct { - Data string `json:"data,omitempty"` -} +type textInput string func (i textInput) Compile(_ *State) (*compiler.Value, error) { - return compiler.Compile("", fmt.Sprintf("%q", i.Data)) + return compiler.Compile("", fmt.Sprintf("%q", i)) +} + +// A secret input value +func SecretInput(data string) Input { + i := secretInput(data) + return Input{ + Secret: &i, + } +} + +type secretInput string + +func (i secretInput) Compile(_ *State) (*compiler.Value, error) { + return compiler.Compile("", fmt.Sprintf("%q", i)) } // An input value encoded as JSON func JSONInput(data string) Input { + i := jsonInput(data) return Input{ - Type: InputTypeJSON, - JSON: &jsonInput{ - Data: data, - }, + JSON: &i, } } -type jsonInput struct { - // Marshalled JSON data - Data string `json:"data,omitempty"` -} +type jsonInput string func (i jsonInput) Compile(_ *State) (*compiler.Value, error) { - return compiler.DecodeJSON("", []byte(i.Data)) + return compiler.DecodeJSON("", []byte(i)) } // An input value encoded as YAML func YAMLInput(data string) Input { + i := yamlInput(data) return Input{ - Type: InputTypeYAML, - YAML: &yamlInput{ - Data: data, - }, + YAML: &i, } } -type yamlInput struct { - // Marshalled YAML data - Data string `json:"data,omitempty"` -} +type yamlInput string func (i yamlInput) Compile(_ *State) (*compiler.Value, error) { - return compiler.DecodeYAML("", []byte(i.Data)) + return compiler.DecodeYAML("", []byte(i)) } func FileInput(data string) Input { return Input{ - Type: InputTypeFile, File: &fileInput{ Path: data, }, diff --git a/dagger/state/state.go b/dagger/state/state.go index 619824c5..49480ebd 100644 --- a/dagger/state/state.go +++ b/dagger/state/state.go @@ -11,17 +11,12 @@ type State struct { Name string `yaml:"name,omitempty"` // User Inputs - Inputs []inputKV `yaml:"inputs,omitempty"` + Inputs map[string]Input `yaml:"inputs,omitempty"` // Computed values Computed string `yaml:"-"` } -type inputKV struct { - Key string `yaml:"key,omitempty"` - Value Input `yaml:"value,omitempty"` -} - // Cue module containing the environment plan // The input's top-level artifact is used as a module directory. func (s *State) PlanSource() Input { @@ -29,15 +24,10 @@ func (s *State) PlanSource() Input { } func (s *State) SetInput(key string, value Input) error { - for i, inp := range s.Inputs { - if inp.Key != key { - continue - } - // Remove existing inputs with the same key - s.Inputs = append(s.Inputs[:i], s.Inputs[i+1:]...) + if s.Inputs == nil { + s.Inputs = make(map[string]Input) } - - s.Inputs = append(s.Inputs, inputKV{Key: key, Value: value}) + s.Inputs[key] = value return nil } @@ -45,14 +35,6 @@ func (s *State) SetInput(key string, value Input) error { // For example RemoveInputs("foo.bar") will remove all inputs // at foo.bar, foo.bar.baz, etc. func (s *State) RemoveInputs(key string) error { - newInputs := make([]inputKV, 0, len(s.Inputs)) - for _, i := range s.Inputs { - if i.Key == key { - continue - } - newInputs = append(newInputs, i) - } - s.Inputs = newInputs - + delete(s.Inputs, key) return nil }