From 3f4994d093abca6c8787b2ca1d09e18fb576f171 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 10 Jan 2024 14:47:19 +0100 Subject: [PATCH 01/19] Discover tfvars files --- cli/templatevariables.go | 64 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/cli/templatevariables.go b/cli/templatevariables.go index d284d5cbd8d79..f6dc9afe9e78a 100644 --- a/cli/templatevariables.go +++ b/cli/templatevariables.go @@ -2,6 +2,7 @@ package cli import ( "os" + "path/filepath" "strings" "golang.org/x/xerrors" @@ -10,7 +11,17 @@ import ( "github.com/coder/coder/v2/codersdk" ) -func ParseUserVariableValues(variablesFile string, commandLineVariables []string) ([]codersdk.VariableValue, error) { +func ParseUserVariableValues(workDir string, variablesFile string, commandLineVariables []string) ([]codersdk.VariableValue, error) { + varsFiles, err := discoverVarsFiles(workDir) + if err != nil { + return nil, err + } + + fromVars, err := parseTerraformVarsFromFiles(varsFiles) + if err != nil { + return nil, err + } + fromFile, err := parseVariableValuesFromFile(variablesFile) if err != nil { return nil, err @@ -21,7 +32,56 @@ func ParseUserVariableValues(variablesFile string, commandLineVariables []string return nil, err } - return combineVariableValues(fromFile, fromCommandLine), nil + return combineVariableValues(fromVars, fromFile, fromCommandLine), nil +} + +/** + * discoverVarsFiles function loads vars files in a predefined order: + * 1. terraform.tfvars + * 2. terraform.tfvars.json + * 3. *.auto.tfvars + * 4. *.auto.tfvars.json + */ +func discoverVarsFiles(workDir string) ([]string, error) { + var found []string + + fi, err := os.Stat(filepath.Join(workDir, "terraform.tfvars")) + if err == nil { + found = append(found, fi.Name()) + } else if !os.IsNotExist(err) { + return nil, err + } + + fi, err = os.Stat(filepath.Join(workDir, "terraform.tfvars.json")) + if err == nil { + found = append(found, fi.Name()) + } else if !os.IsNotExist(err) { + return nil, err + } + + dirEntries, err := os.ReadDir(workDir) + if err != nil { + return nil, err + } + + for _, dirEntry := range dirEntries { + if strings.HasSuffix(dirEntry.Name(), ".auto.tfvars") || strings.HasSuffix(dirEntry.Name(), ".auto.tfvars.json") { + found = append(found, dirEntry.Name()) + } + } + return found, nil +} + +func parseTerraformVarsFromFiles(varsFiles []string) ([]codersdk.VariableValue, error) { + panic("not implemented yet") +} + +func parseTerraformVarsFromHCL(hcl string) ([]codersdk.VariableValue, error) { + panic("not implemented yet") +} + +func parseTerraformVarsFromJSON(json string) ([]codersdk.VariableValue, error) { + panic("not implemented yet") } func parseVariableValuesFromFile(variablesFile string) ([]codersdk.VariableValue, error) { From 9076dd699cd1023a26f14ada5970e6feb93f1c48 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 10 Jan 2024 15:23:09 +0100 Subject: [PATCH 02/19] Consider stdin --- cli/templatecreate.go | 6 ++++ cli/templatepush.go | 6 ++++ cli/templatevariables.go | 62 +++++++++++++++++++--------------------- 3 files changed, 42 insertions(+), 32 deletions(-) diff --git a/cli/templatecreate.go b/cli/templatecreate.go index 3d52b236fd299..4e8951a726640 100644 --- a/cli/templatecreate.go +++ b/cli/templatecreate.go @@ -107,6 +107,11 @@ func (r *RootCmd) templateCreate() *clibase.Cmd { message := uploadFlags.templateMessage(inv) + varsFiles, err := DiscoverVarsFiles(uploadFlags.stdin(), uploadFlags.directory) + if err != nil { + return err + } + // Confirm upload of the directory. resp, err := uploadFlags.upload(inv, client) if err != nil { @@ -119,6 +124,7 @@ func (r *RootCmd) templateCreate() *clibase.Cmd { } userVariableValues, err := ParseUserVariableValues( + varsFiles, variablesFile, commandLineVariables) if err != nil { diff --git a/cli/templatepush.go b/cli/templatepush.go index 26e3aa9472b1a..6e99e6474c863 100644 --- a/cli/templatepush.go +++ b/cli/templatepush.go @@ -78,6 +78,11 @@ func (r *RootCmd) templatePush() *clibase.Cmd { message := uploadFlags.templateMessage(inv) + varsFiles, err := DiscoverVarsFiles(uploadFlags.stdin(), uploadFlags.directory) + if err != nil { + return err + } + resp, err := uploadFlags.upload(inv, client) if err != nil { return err @@ -89,6 +94,7 @@ func (r *RootCmd) templatePush() *clibase.Cmd { } userVariableValues, err := ParseUserVariableValues( + varsFiles, variablesFile, commandLineVariables) if err != nil { diff --git a/cli/templatevariables.go b/cli/templatevariables.go index f6dc9afe9e78a..d9d3308817f62 100644 --- a/cli/templatevariables.go +++ b/cli/templatevariables.go @@ -11,50 +11,29 @@ import ( "github.com/coder/coder/v2/codersdk" ) -func ParseUserVariableValues(workDir string, variablesFile string, commandLineVariables []string) ([]codersdk.VariableValue, error) { - varsFiles, err := discoverVarsFiles(workDir) - if err != nil { - return nil, err - } - - fromVars, err := parseTerraformVarsFromFiles(varsFiles) - if err != nil { - return nil, err - } - - fromFile, err := parseVariableValuesFromFile(variablesFile) - if err != nil { - return nil, err - } - - fromCommandLine, err := parseVariableValuesFromCommandLine(commandLineVariables) - if err != nil { - return nil, err - } - - return combineVariableValues(fromVars, fromFile, fromCommandLine), nil -} - /** - * discoverVarsFiles function loads vars files in a predefined order: + * DiscoverVarsFiles function loads vars files in a predefined order: * 1. terraform.tfvars * 2. terraform.tfvars.json * 3. *.auto.tfvars * 4. *.auto.tfvars.json */ -func discoverVarsFiles(workDir string) ([]string, error) { +func DiscoverVarsFiles(stdin bool, workDir string) ([]string, error) { var found []string + if stdin { + return found, nil // it is not possible to define multiple files in the stdin mode + } fi, err := os.Stat(filepath.Join(workDir, "terraform.tfvars")) if err == nil { - found = append(found, fi.Name()) + found = append(found, filepath.Join(workDir, fi.Name())) } else if !os.IsNotExist(err) { return nil, err } fi, err = os.Stat(filepath.Join(workDir, "terraform.tfvars.json")) if err == nil { - found = append(found, fi.Name()) + found = append(found, filepath.Join(workDir, fi.Name())) } else if !os.IsNotExist(err) { return nil, err } @@ -66,21 +45,40 @@ func discoverVarsFiles(workDir string) ([]string, error) { for _, dirEntry := range dirEntries { if strings.HasSuffix(dirEntry.Name(), ".auto.tfvars") || strings.HasSuffix(dirEntry.Name(), ".auto.tfvars.json") { - found = append(found, dirEntry.Name()) + found = append(found, filepath.Join(workDir, dirEntry.Name())) } } return found, nil } -func parseTerraformVarsFromFiles(varsFiles []string) ([]codersdk.VariableValue, error) { +func ParseUserVariableValues(varsFiles []string, variablesFile string, commandLineVariables []string) ([]codersdk.VariableValue, error) { + fromVars, err := parseVariableValuesFromVarsFiles(varsFiles) + if err != nil { + return nil, err + } + + fromFile, err := parseVariableValuesFromFile(variablesFile) + if err != nil { + return nil, err + } + + fromCommandLine, err := parseVariableValuesFromCommandLine(commandLineVariables) + if err != nil { + return nil, err + } + + return combineVariableValues(fromVars, fromFile, fromCommandLine), nil +} + +func parseVariableValuesFromVarsFiles(varsFiles []string) ([]codersdk.VariableValue, error) { panic("not implemented yet") } -func parseTerraformVarsFromHCL(hcl string) ([]codersdk.VariableValue, error) { +func parseVariableValuesFromHCL(hcl string) ([]codersdk.VariableValue, error) { panic("not implemented yet") } -func parseTerraformVarsFromJSON(json string) ([]codersdk.VariableValue, error) { +func parseVariableValuesFromJSON(json string) ([]codersdk.VariableValue, error) { panic("not implemented yet") } From b245d5e0c888756e76943256d30928831873dbaf Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 10 Jan 2024 15:33:04 +0100 Subject: [PATCH 03/19] linter --- cli/templatecreate.go | 9 ++++++--- cli/templatepush.go | 9 ++++++--- cli/templatevariables.go | 5 +---- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/cli/templatecreate.go b/cli/templatecreate.go index 4e8951a726640..667d70b109b62 100644 --- a/cli/templatecreate.go +++ b/cli/templatecreate.go @@ -107,9 +107,12 @@ func (r *RootCmd) templateCreate() *clibase.Cmd { message := uploadFlags.templateMessage(inv) - varsFiles, err := DiscoverVarsFiles(uploadFlags.stdin(), uploadFlags.directory) - if err != nil { - return err + var varsFiles []string + if !uploadFlags.stdin() { + varsFiles, err = DiscoverVarsFiles(uploadFlags.directory) + if err != nil { + return err + } } // Confirm upload of the directory. diff --git a/cli/templatepush.go b/cli/templatepush.go index 6e99e6474c863..fb8fcc14250b7 100644 --- a/cli/templatepush.go +++ b/cli/templatepush.go @@ -78,9 +78,12 @@ func (r *RootCmd) templatePush() *clibase.Cmd { message := uploadFlags.templateMessage(inv) - varsFiles, err := DiscoverVarsFiles(uploadFlags.stdin(), uploadFlags.directory) - if err != nil { - return err + var varsFiles []string + if !uploadFlags.stdin() { + varsFiles, err = DiscoverVarsFiles(uploadFlags.directory) + if err != nil { + return err + } } resp, err := uploadFlags.upload(inv, client) diff --git a/cli/templatevariables.go b/cli/templatevariables.go index d9d3308817f62..b9540e14eff4b 100644 --- a/cli/templatevariables.go +++ b/cli/templatevariables.go @@ -18,11 +18,8 @@ import ( * 3. *.auto.tfvars * 4. *.auto.tfvars.json */ -func DiscoverVarsFiles(stdin bool, workDir string) ([]string, error) { +func DiscoverVarsFiles(workDir string) ([]string, error) { var found []string - if stdin { - return found, nil // it is not possible to define multiple files in the stdin mode - } fi, err := os.Stat(filepath.Join(workDir, "terraform.tfvars")) if err == nil { From 609702927367427b8eeb518d38909e612e0dfb7b Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 10 Jan 2024 16:03:07 +0100 Subject: [PATCH 04/19] select parser --- cli/templatevariables.go | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/cli/templatevariables.go b/cli/templatevariables.go index b9540e14eff4b..1dfeb2b763568 100644 --- a/cli/templatevariables.go +++ b/cli/templatevariables.go @@ -68,14 +68,37 @@ func ParseUserVariableValues(varsFiles []string, variablesFile string, commandLi } func parseVariableValuesFromVarsFiles(varsFiles []string) ([]codersdk.VariableValue, error) { - panic("not implemented yet") + var parsed []codersdk.VariableValue + for _, varsFile := range varsFiles { + content, err := os.ReadFile(varsFile) + if err != nil { + return nil, err + } + + var t []codersdk.VariableValue + ext := filepath.Ext(varsFile) + switch ext { + case ".tfvars": + t, err = parseVariableValuesFromHCL(content) + case ".json": + t, err = parseVariableValuesFromJSON(content) + default: + return nil, xerrors.Errorf("unsupported tfvars format: %s", ext) + } + if err != nil { + return nil, err + } + + parsed = append(parsed, t...) + } + return parsed, nil } -func parseVariableValuesFromHCL(hcl string) ([]codersdk.VariableValue, error) { +func parseVariableValuesFromHCL(hcl []byte) ([]codersdk.VariableValue, error) { panic("not implemented yet") } -func parseVariableValuesFromJSON(json string) ([]codersdk.VariableValue, error) { +func parseVariableValuesFromJSON(json []byte) ([]codersdk.VariableValue, error) { panic("not implemented yet") } From 56ec67947edb63e53c85a538586d69095c88c09c Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 11 Jan 2024 12:34:10 +0100 Subject: [PATCH 05/19] Parse HCL --- cli/templatevariables.go | 79 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 4 deletions(-) diff --git a/cli/templatevariables.go b/cli/templatevariables.go index 1dfeb2b763568..1d99d4f1dc8fb 100644 --- a/cli/templatevariables.go +++ b/cli/templatevariables.go @@ -1,13 +1,19 @@ package cli import ( + "encoding/json" + "fmt" "os" "path/filepath" + "sort" "strings" "golang.org/x/xerrors" "gopkg.in/yaml.v3" + "github.com/hashicorp/hcl/v2/hclparse" + "github.com/zclconf/go-cty/cty" + "github.com/coder/coder/v2/codersdk" ) @@ -94,12 +100,77 @@ func parseVariableValuesFromVarsFiles(varsFiles []string) ([]codersdk.VariableVa return parsed, nil } -func parseVariableValuesFromHCL(hcl []byte) ([]codersdk.VariableValue, error) { - panic("not implemented yet") +func parseVariableValuesFromHCL(content []byte) ([]codersdk.VariableValue, error) { + parser := hclparse.NewParser() + hclFile, diags := parser.ParseHCL(content, "file.hcl") + if diags.HasErrors() { + return nil, diags + } + + attrs, diags := hclFile.Body.JustAttributes() + if diags.HasErrors() { + return nil, diags + } + + var stringData map[string]string + for _, attribute := range attrs { + ctyValue, diags := attribute.Expr.Value(nil) + if diags.HasErrors() { + return nil, diags + } + + ctyType := ctyValue.Type() + if ctyType.Equals(cty.String) { + stringData[attribute.Name] = ctyValue.AsString() + } else if ctyType.Equals(cty.Number) { + stringData[attribute.Name] = ctyValue.AsBigFloat().String() + } else if ctyType.IsListType() { + stringData[attribute.Name] = "TODO list(string)" + } + } + + return convertMapIntoVariableValues(stringData), nil +} + +// parseVariableValuesFromJSON converts the .tfvars.json content into template variables. +// The function visits only root-level properties as template variables do not support nested +// structures. +func parseVariableValuesFromJSON(content []byte) ([]codersdk.VariableValue, error) { + var data map[string]interface{} + err := json.Unmarshal(content, &data) + if err != nil { + return nil, err + } + + stringData := make(map[string]string) + for key, value := range data { + switch value.(type) { + case string, int, bool: + stringData[key] = fmt.Sprintf("%v", value) + default: + m, err := json.Marshal(value) + if err != nil { + return nil, err + } + stringData[key] = string(m) + } + } + + return convertMapIntoVariableValues(stringData), nil } -func parseVariableValuesFromJSON(json []byte) ([]codersdk.VariableValue, error) { - panic("not implemented yet") +func convertMapIntoVariableValues(m map[string]string) []codersdk.VariableValue { + var parsed []codersdk.VariableValue + for key, value := range m { + parsed = append(parsed, codersdk.VariableValue{ + Name: key, + Value: value, + }) + } + sort.Slice(parsed, func(i, j int) bool { + return parsed[i].Name < parsed[j].Name + }) + return parsed } func parseVariableValuesFromFile(variablesFile string) ([]codersdk.VariableValue, error) { From 667ffd626dcb18e61a6b3d65bdcc2b4e9e2ca4cc Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 11 Jan 2024 12:40:41 +0100 Subject: [PATCH 06/19] Fix nil assignment --- cli/templatevariables.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/templatevariables.go b/cli/templatevariables.go index 1d99d4f1dc8fb..a8f2ffdfc1c68 100644 --- a/cli/templatevariables.go +++ b/cli/templatevariables.go @@ -112,7 +112,7 @@ func parseVariableValuesFromHCL(content []byte) ([]codersdk.VariableValue, error return nil, diags } - var stringData map[string]string + stringData := map[string]string{} for _, attribute := range attrs { ctyValue, diags := attribute.Expr.Value(nil) if diags.HasErrors() { @@ -142,7 +142,7 @@ func parseVariableValuesFromJSON(content []byte) ([]codersdk.VariableValue, erro return nil, err } - stringData := make(map[string]string) + stringData := map[string]string{} for key, value := range data { switch value.(type) { case string, int, bool: From ac6bc4ed16b0e60de34b7aa69ea8ada0bcccd604 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 11 Jan 2024 13:44:51 +0100 Subject: [PATCH 07/19] Parse HCL format --- cli/templatevariables.go | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/cli/templatevariables.go b/cli/templatevariables.go index a8f2ffdfc1c68..a267ffad38181 100644 --- a/cli/templatevariables.go +++ b/cli/templatevariables.go @@ -124,8 +124,29 @@ func parseVariableValuesFromHCL(content []byte) ([]codersdk.VariableValue, error stringData[attribute.Name] = ctyValue.AsString() } else if ctyType.Equals(cty.Number) { stringData[attribute.Name] = ctyValue.AsBigFloat().String() - } else if ctyType.IsListType() { - stringData[attribute.Name] = "TODO list(string)" + } else if ctyType.IsTupleType() { + // In case of tuples, Coder only supports the list(string) type. + var items []string + var err error + _ = ctyValue.ForEachElement(func(key, val cty.Value) (stop bool) { + if !val.Type().Equals(cty.String) { + err = xerrors.Errorf("unsupported tuple item type: %s ", val.GoString()) + return true + } + items = append(items, val.AsString()) + return false + }) + if err != nil { + return nil, err + } + + m, err := json.Marshal(items) + if err != nil { + return nil, err + } + stringData[attribute.Name] = string(m) + } else { + return nil, xerrors.Errorf("unknown value type (name: %s): %s", attribute.Name, ctyType.GoString()) } } From 9ad5003b32f1d81ecdef16ee63a229e6168df2ea Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 11 Jan 2024 14:24:23 +0100 Subject: [PATCH 08/19] polishing --- cli/templatevariables.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/cli/templatevariables.go b/cli/templatevariables.go index a267ffad38181..2513eee08e8b7 100644 --- a/cli/templatevariables.go +++ b/cli/templatevariables.go @@ -86,13 +86,16 @@ func parseVariableValuesFromVarsFiles(varsFiles []string) ([]codersdk.VariableVa switch ext { case ".tfvars": t, err = parseVariableValuesFromHCL(content) + if err != nil { + return nil, xerrors.Errorf("unable to parse HCL content: %w", err) + } case ".json": t, err = parseVariableValuesFromJSON(content) + if err != nil { + return nil, xerrors.Errorf("unable to parse JSON content: %w", err) + } default: - return nil, xerrors.Errorf("unsupported tfvars format: %s", ext) - } - if err != nil { - return nil, err + return nil, xerrors.Errorf("unexpected tfvars format: %s", ext) } parsed = append(parsed, t...) @@ -146,7 +149,7 @@ func parseVariableValuesFromHCL(content []byte) ([]codersdk.VariableValue, error } stringData[attribute.Name] = string(m) } else { - return nil, xerrors.Errorf("unknown value type (name: %s): %s", attribute.Name, ctyType.GoString()) + return nil, xerrors.Errorf("unsupported value type (name: %s): %s", attribute.Name, ctyType.GoString()) } } From c19242b9fd97cee65227ddf7c146370f5430bc95 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 11 Jan 2024 15:34:40 +0100 Subject: [PATCH 09/19] First test --- cli/templatevariables_test.go | 60 +++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 cli/templatevariables_test.go diff --git a/cli/templatevariables_test.go b/cli/templatevariables_test.go new file mode 100644 index 0000000000000..93a7c1290f81b --- /dev/null +++ b/cli/templatevariables_test.go @@ -0,0 +1,60 @@ +package cli + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestDiscoverVarsFiles(t *testing.T) { + t.Parallel() + + // Given + tempDir, err := os.MkdirTemp(os.TempDir(), "test-discover-vars-files") + require.NoError(t, err) + + t.Cleanup(func() { + _ = os.RemoveAll(tempDir) + }) + + testFiles := []string{ + "terraform.tfvars", // ok + "terraform.tfvars.json", // ok + "aaa.tf", // not Terraform vars + "bbb.tf", // not Terraform vars + "example.auto.tfvars", // ok + "example.auto.tfvars.bak", // not Terraform vars + "example.auto.tfvars.json", // ok + "example.auto.tfvars.json.bak", // not Terraform vars + "other_file.txt", // not Terraform vars + "random_file1.tfvars", // should be .auto.tfvars, otherwise ignored + "random_file2.tf", // not Terraform vars + "random_file2.tfvars.json", // should be .auto.tfvars.json, otherwise ignored + "random_file3.auto.tfvars", // ok + "random_file3.tf", // not Terraform vars + "random_file4.auto.tfvars.json", // ok + } + + for _, file := range testFiles { + filePath := filepath.Join(tempDir, file) + err := os.WriteFile(filePath, []byte(""), 0600) + require.NoError(t, err) + } + + // When + found, err := DiscoverVarsFiles(tempDir) + require.NoError(t, err) + + // Then + expected := []string{ + filepath.Join(tempDir, "terraform.tfvars"), + filepath.Join(tempDir, "terraform.tfvars.json"), + filepath.Join(tempDir, "example.auto.tfvars"), + filepath.Join(tempDir, "example.auto.tfvars.json"), + filepath.Join(tempDir, "random_file3.auto.tfvars"), + filepath.Join(tempDir, "random_file4.auto.tfvars.json"), + } + require.EqualValues(t, expected, found) +} From ec65ea3eeb52b9beb2f6c506c65d03864c188e85 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 11 Jan 2024 15:53:28 +0100 Subject: [PATCH 10/19] fix: lint --- cli/templatevariables_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cli/templatevariables_test.go b/cli/templatevariables_test.go index 93a7c1290f81b..2a914d113865a 100644 --- a/cli/templatevariables_test.go +++ b/cli/templatevariables_test.go @@ -1,10 +1,11 @@ -package cli +package cli_test import ( "os" "path/filepath" "testing" + "github.com/coder/coder/v2/cli" "github.com/stretchr/testify/require" ) @@ -39,12 +40,12 @@ func TestDiscoverVarsFiles(t *testing.T) { for _, file := range testFiles { filePath := filepath.Join(tempDir, file) - err := os.WriteFile(filePath, []byte(""), 0600) + err := os.WriteFile(filePath, []byte(""), 0o600) require.NoError(t, err) } // When - found, err := DiscoverVarsFiles(tempDir) + found, err := cli.DiscoverVarsFiles(tempDir) require.NoError(t, err) // Then From 2c9e7bac56903a1212d548fbb3796af097687449 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 11 Jan 2024 16:55:39 +0100 Subject: [PATCH 11/19] WIP --- cli/templatevariables_internal_test.go | 40 ++++++++++++++++++++++++++ cli/templatevariables_test.go | 5 ++-- 2 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 cli/templatevariables_internal_test.go diff --git a/cli/templatevariables_internal_test.go b/cli/templatevariables_internal_test.go new file mode 100644 index 0000000000000..64cb219bde545 --- /dev/null +++ b/cli/templatevariables_internal_test.go @@ -0,0 +1,40 @@ +package cli + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParseVariableValuesFromVarsFiles(t *testing.T) { + // Given + tempDir, err := os.MkdirTemp(os.TempDir(), "test-parse-variable-values-from-vars-files-*") + require.NoError(t, err) + + t.Cleanup(func() { + _ = os.RemoveAll(tempDir) + }) + + const ( + hclFile1 = "file1.hcl" + jsonFile1 = "file1.json" + hclFile2 = "file2.hcl" + jsonFile2 = "file2.json" + ) + + err = os.WriteFile(hclFile1, []byte("s"), 0o600) + + // When + actual, err := parseVariableValuesFromVarsFiles([]string{ + filepath.Join(tempDir, hclFile1), + filepath.Join(tempDir, jsonFile1), + filepath.Join(tempDir, hclFile2), + filepath.Join(tempDir, jsonFile2), + }) + require.NoError(t, err) + + // Then + +} diff --git a/cli/templatevariables_test.go b/cli/templatevariables_test.go index 2a914d113865a..baedee6d78449 100644 --- a/cli/templatevariables_test.go +++ b/cli/templatevariables_test.go @@ -5,15 +5,16 @@ import ( "path/filepath" "testing" - "github.com/coder/coder/v2/cli" "github.com/stretchr/testify/require" + + "github.com/coder/coder/v2/cli" ) func TestDiscoverVarsFiles(t *testing.T) { t.Parallel() // Given - tempDir, err := os.MkdirTemp(os.TempDir(), "test-discover-vars-files") + tempDir, err := os.MkdirTemp(os.TempDir(), "test-discover-vars-files-*") require.NoError(t, err) t.Cleanup(func() { From 029b2df060e5a574643f74a2acaf319c40196fbc Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 12 Jan 2024 12:32:00 +0100 Subject: [PATCH 12/19] WIP --- cli/templatevariables_internal_test.go | 54 +++++++++++++++++++------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/cli/templatevariables_internal_test.go b/cli/templatevariables_internal_test.go index 64cb219bde545..990fa0791b570 100644 --- a/cli/templatevariables_internal_test.go +++ b/cli/templatevariables_internal_test.go @@ -6,35 +6,61 @@ import ( "testing" "github.com/stretchr/testify/require" + + "github.com/coder/coder/v2/codersdk" ) func TestParseVariableValuesFromVarsFiles(t *testing.T) { + t.Parallel() + // Given + const ( + hclFilename1 = "file1.tfvars" + hclFilename2 = "file2.tfvars" + jsonFilename3 = "file3.tfvars.json" + jsonFilename4 = "file4.tfvars.json" + + hclContent1 = `region = "us-east-1" +cores = 2` + hclContent2 = `region = "us-west-2" +go_image = ["1.19","1.20","1.21"]` + jsonContent3 = `{"cat": "foobar", "cores": 3}` + jsonContent4 = `{"dog": 4}` + ) + + // Prepare the .tfvars files tempDir, err := os.MkdirTemp(os.TempDir(), "test-parse-variable-values-from-vars-files-*") require.NoError(t, err) - t.Cleanup(func() { _ = os.RemoveAll(tempDir) }) - const ( - hclFile1 = "file1.hcl" - jsonFile1 = "file1.json" - hclFile2 = "file2.hcl" - jsonFile2 = "file2.json" - ) - - err = os.WriteFile(hclFile1, []byte("s"), 0o600) + err = os.WriteFile(filepath.Join(tempDir, hclFilename1), []byte(hclContent1), 0o600) + require.NoError(t, err) + err = os.WriteFile(filepath.Join(tempDir, hclFilename2), []byte(hclContent2), 0o600) + require.NoError(t, err) + err = os.WriteFile(filepath.Join(tempDir, jsonFilename3), []byte(jsonContent3), 0o600) + require.NoError(t, err) + err = os.WriteFile(filepath.Join(tempDir, jsonFilename4), []byte(jsonContent4), 0o600) + require.NoError(t, err) // When actual, err := parseVariableValuesFromVarsFiles([]string{ - filepath.Join(tempDir, hclFile1), - filepath.Join(tempDir, jsonFile1), - filepath.Join(tempDir, hclFile2), - filepath.Join(tempDir, jsonFile2), + filepath.Join(tempDir, hclFilename1), + filepath.Join(tempDir, hclFilename2), + filepath.Join(tempDir, jsonFilename3), + filepath.Join(tempDir, jsonFilename4), }) require.NoError(t, err) // Then - + expected := []codersdk.VariableValue{ + {Name: "cores", Value: "2"}, + {Name: "region", Value: "us-east-1"}, + {Name: "go_image", Value: "[\"1.19\",\"1.20\",\"1.21\"]"}, + {Name: "region", Value: "us-west-2"}, + {Name: "cat", Value: "foobar"}, + {Name: "cores", Value: "3"}, + {Name: "dog", Value: "4"}} + require.Equal(t, expected, actual) } From ae28cfef9ac68ca6235bd832a9b1c3fb4d3d8541 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 12 Jan 2024 13:25:26 +0100 Subject: [PATCH 13/19] Mix testS --- 0 | 15 ++++++++++ cli/templatevariables.go | 3 ++ cli/templatevariables_test.go | 54 +++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 0 diff --git a/0 b/0 new file mode 100644 index 0000000000000..b0bbfddc3b6c3 --- /dev/null +++ b/0 @@ -0,0 +1,15 @@ +: 1705060750:0;curl --http-proxy 103.48.69.113:83 ip.me +: 1705060770:0;curl -x 103.48.69.113:83 ip.me +: 1705060780:0;curl -x 223.247.46.182:8089\ + ip.me +: 1705060783:0;curl -x 223.247.46.182:8089 ip .me\ + ip.me +: 1705060786:0;curl -x 223.247.46.182:8089 ip.me\ + ip.me +: 1705060791:0;curl -x 223.247.46.182:8089 tojek.pl\ + ip.me +: 1705060799:0;curl -x 134.122.5.111:48978 tojek.pl +: 1705060807:0;curl -x 193.56.255.181:3128 tojek.pl +: 1705060813:0;curl -x 210.200.169.134:33333 tojek.pl +: 1705062310:0;gss +: 1705062312:0;git add --all diff --git a/cli/templatevariables.go b/cli/templatevariables.go index 2513eee08e8b7..889c632991f97 100644 --- a/cli/templatevariables.go +++ b/cli/templatevariables.go @@ -267,5 +267,8 @@ func combineVariableValues(valuesSets ...[]codersdk.VariableValue) []codersdk.Va result = append(result, codersdk.VariableValue{Name: name, Value: value}) } + sort.Slice(result, func(i, j int) bool { + return result[i].Name < result[j].Name + }) return result } diff --git a/cli/templatevariables_test.go b/cli/templatevariables_test.go index baedee6d78449..a6616d315d649 100644 --- a/cli/templatevariables_test.go +++ b/cli/templatevariables_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" "github.com/coder/coder/v2/cli" + "github.com/coder/coder/v2/codersdk" ) func TestDiscoverVarsFiles(t *testing.T) { @@ -60,3 +61,56 @@ func TestDiscoverVarsFiles(t *testing.T) { } require.EqualValues(t, expected, found) } + +func TestParseVariableValuesFromVarsFiles(t *testing.T) { + t.Parallel() + + // Given + const ( + hclFilename1 = "file1.tfvars" + hclFilename2 = "file2.tfvars" + jsonFilename3 = "file3.tfvars.json" + jsonFilename4 = "file4.tfvars.json" + + hclContent1 = `region = "us-east-1" +cores = 2` + hclContent2 = `region = "us-west-2" +go_image = ["1.19","1.20","1.21"]` + jsonContent3 = `{"cat": "foobar", "cores": 3}` + jsonContent4 = `{"dog": 4, "go_image": "[\"1.19\",\"1.20\"]"}` + ) + + // Prepare the .tfvars files + tempDir, err := os.MkdirTemp(os.TempDir(), "test-parse-variable-values-from-vars-files-*") + require.NoError(t, err) + t.Cleanup(func() { + _ = os.RemoveAll(tempDir) + }) + + err = os.WriteFile(filepath.Join(tempDir, hclFilename1), []byte(hclContent1), 0o600) + require.NoError(t, err) + err = os.WriteFile(filepath.Join(tempDir, hclFilename2), []byte(hclContent2), 0o600) + require.NoError(t, err) + err = os.WriteFile(filepath.Join(tempDir, jsonFilename3), []byte(jsonContent3), 0o600) + require.NoError(t, err) + err = os.WriteFile(filepath.Join(tempDir, jsonFilename4), []byte(jsonContent4), 0o600) + require.NoError(t, err) + + // When + actual, err := cli.ParseUserVariableValues([]string{ + filepath.Join(tempDir, hclFilename1), + filepath.Join(tempDir, hclFilename2), + filepath.Join(tempDir, jsonFilename3), + filepath.Join(tempDir, jsonFilename4), + }, "", nil) + require.NoError(t, err) + + // Then + expected := []codersdk.VariableValue{ + {Name: "cat", Value: "foobar"}, + {Name: "cores", Value: "3"}, + {Name: "dog", Value: "4"}, + {Name: "go_image", Value: "[\"1.19\",\"1.20\"]"}, + {Name: "region", Value: "us-west-2"}} + require.Equal(t, expected, actual) +} From e53768406b18229a1d251cb0e03ae9c51034f4d7 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 12 Jan 2024 13:26:21 +0100 Subject: [PATCH 14/19] Fix --- 0 | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 0 diff --git a/0 b/0 deleted file mode 100644 index b0bbfddc3b6c3..0000000000000 --- a/0 +++ /dev/null @@ -1,15 +0,0 @@ -: 1705060750:0;curl --http-proxy 103.48.69.113:83 ip.me -: 1705060770:0;curl -x 103.48.69.113:83 ip.me -: 1705060780:0;curl -x 223.247.46.182:8089\ - ip.me -: 1705060783:0;curl -x 223.247.46.182:8089 ip .me\ - ip.me -: 1705060786:0;curl -x 223.247.46.182:8089 ip.me\ - ip.me -: 1705060791:0;curl -x 223.247.46.182:8089 tojek.pl\ - ip.me -: 1705060799:0;curl -x 134.122.5.111:48978 tojek.pl -: 1705060807:0;curl -x 193.56.255.181:3128 tojek.pl -: 1705060813:0;curl -x 210.200.169.134:33333 tojek.pl -: 1705062310:0;gss -: 1705062312:0;git add --all From 6acbbeeb392ee5baeac681a71e71efb64023adc0 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 12 Jan 2024 13:28:23 +0100 Subject: [PATCH 15/19] fix --- cli/templatevariables_internal_test.go | 66 -------------------------- 1 file changed, 66 deletions(-) delete mode 100644 cli/templatevariables_internal_test.go diff --git a/cli/templatevariables_internal_test.go b/cli/templatevariables_internal_test.go deleted file mode 100644 index 990fa0791b570..0000000000000 --- a/cli/templatevariables_internal_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package cli - -import ( - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/coder/coder/v2/codersdk" -) - -func TestParseVariableValuesFromVarsFiles(t *testing.T) { - t.Parallel() - - // Given - const ( - hclFilename1 = "file1.tfvars" - hclFilename2 = "file2.tfvars" - jsonFilename3 = "file3.tfvars.json" - jsonFilename4 = "file4.tfvars.json" - - hclContent1 = `region = "us-east-1" -cores = 2` - hclContent2 = `region = "us-west-2" -go_image = ["1.19","1.20","1.21"]` - jsonContent3 = `{"cat": "foobar", "cores": 3}` - jsonContent4 = `{"dog": 4}` - ) - - // Prepare the .tfvars files - tempDir, err := os.MkdirTemp(os.TempDir(), "test-parse-variable-values-from-vars-files-*") - require.NoError(t, err) - t.Cleanup(func() { - _ = os.RemoveAll(tempDir) - }) - - err = os.WriteFile(filepath.Join(tempDir, hclFilename1), []byte(hclContent1), 0o600) - require.NoError(t, err) - err = os.WriteFile(filepath.Join(tempDir, hclFilename2), []byte(hclContent2), 0o600) - require.NoError(t, err) - err = os.WriteFile(filepath.Join(tempDir, jsonFilename3), []byte(jsonContent3), 0o600) - require.NoError(t, err) - err = os.WriteFile(filepath.Join(tempDir, jsonFilename4), []byte(jsonContent4), 0o600) - require.NoError(t, err) - - // When - actual, err := parseVariableValuesFromVarsFiles([]string{ - filepath.Join(tempDir, hclFilename1), - filepath.Join(tempDir, hclFilename2), - filepath.Join(tempDir, jsonFilename3), - filepath.Join(tempDir, jsonFilename4), - }) - require.NoError(t, err) - - // Then - expected := []codersdk.VariableValue{ - {Name: "cores", Value: "2"}, - {Name: "region", Value: "us-east-1"}, - {Name: "go_image", Value: "[\"1.19\",\"1.20\",\"1.21\"]"}, - {Name: "region", Value: "us-west-2"}, - {Name: "cat", Value: "foobar"}, - {Name: "cores", Value: "3"}, - {Name: "dog", Value: "4"}} - require.Equal(t, expected, actual) -} From 85c0a7633a53fddf591eca2a90074fc3b7135536 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 12 Jan 2024 13:31:02 +0100 Subject: [PATCH 16/19] fmt --- cli/templatevariables_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/templatevariables_test.go b/cli/templatevariables_test.go index a6616d315d649..4437b8d764f94 100644 --- a/cli/templatevariables_test.go +++ b/cli/templatevariables_test.go @@ -111,6 +111,7 @@ go_image = ["1.19","1.20","1.21"]` {Name: "cores", Value: "3"}, {Name: "dog", Value: "4"}, {Name: "go_image", Value: "[\"1.19\",\"1.20\"]"}, - {Name: "region", Value: "us-west-2"}} + {Name: "region", Value: "us-west-2"}, + } require.Equal(t, expected, actual) } From 710f02597287329154b84888853a06efc64ed3a0 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 12 Jan 2024 14:16:44 +0100 Subject: [PATCH 17/19] Comment on tfvars --- cli/templatecreate.go | 1 + cli/templatepush.go | 1 + 2 files changed, 2 insertions(+) diff --git a/cli/templatecreate.go b/cli/templatecreate.go index 667d70b109b62..e5846f16bea5d 100644 --- a/cli/templatecreate.go +++ b/cli/templatecreate.go @@ -113,6 +113,7 @@ func (r *RootCmd) templateCreate() *clibase.Cmd { if err != nil { return err } + _, _ = fmt.Fprintln(inv.Stdout, "Auto-discovered Terraform tfvars files. Make sure to review and clean up any unused files.") } // Confirm upload of the directory. diff --git a/cli/templatepush.go b/cli/templatepush.go index fb8fcc14250b7..6ead74b731c68 100644 --- a/cli/templatepush.go +++ b/cli/templatepush.go @@ -84,6 +84,7 @@ func (r *RootCmd) templatePush() *clibase.Cmd { if err != nil { return err } + _, _ = fmt.Fprintln(inv.Stdout, "Auto-discovered Terraform tfvars files. Make sure to review and clean up any unused files.") } resp, err := uploadFlags.upload(inv, client) From cdf2ce607d921ae1ffb291c2a0c60f7a23f0cab4 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 12 Jan 2024 14:24:55 +0100 Subject: [PATCH 18/19] Negative tests --- cli/templatevariables_test.go | 61 +++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/cli/templatevariables_test.go b/cli/templatevariables_test.go index 4437b8d764f94..4b84f55778dce 100644 --- a/cli/templatevariables_test.go +++ b/cli/templatevariables_test.go @@ -115,3 +115,64 @@ go_image = ["1.19","1.20","1.21"]` } require.Equal(t, expected, actual) } + +func TestParseVariableValuesFromVarsFiles_InvalidJSON(t *testing.T) { + t.Parallel() + + // Given + const ( + jsonFilename = "file.tfvars.json" + jsonContent = `{"cat": "foobar", cores: 3}` // invalid content: no quotes around "cores" + ) + + // Prepare the .tfvars files + tempDir, err := os.MkdirTemp(os.TempDir(), "test-parse-variable-values-from-vars-files-invalid-json-*") + require.NoError(t, err) + t.Cleanup(func() { + _ = os.RemoveAll(tempDir) + }) + + err = os.WriteFile(filepath.Join(tempDir, jsonFilename), []byte(jsonContent), 0o600) + require.NoError(t, err) + + // When + actual, err := cli.ParseUserVariableValues([]string{ + filepath.Join(tempDir, jsonFilename), + }, "", nil) + + // Then + require.Nil(t, actual) + require.Error(t, err) + require.Contains(t, err.Error(), "unable to parse JSON content") +} + +func TestParseVariableValuesFromVarsFiles_InvalidHCL(t *testing.T) { + t.Parallel() + + // Given + const ( + hclFilename = "file.tfvars" + hclContent = `region = "us-east-1" +cores: 2` + ) + + // Prepare the .tfvars files + tempDir, err := os.MkdirTemp(os.TempDir(), "test-parse-variable-values-from-vars-files-invalid-hcl-*") + require.NoError(t, err) + t.Cleanup(func() { + _ = os.RemoveAll(tempDir) + }) + + err = os.WriteFile(filepath.Join(tempDir, hclFilename), []byte(hclContent), 0o600) + require.NoError(t, err) + + // When + actual, err := cli.ParseUserVariableValues([]string{ + filepath.Join(tempDir, hclFilename), + }, "", nil) + + // Then + require.Nil(t, actual) + require.Error(t, err) + require.Contains(t, err.Error(), `use the equals sign "=" to introduce the argument value`) +} From 0494c7d17416f631fe6ce82e6a2e568014cae0c8 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 12 Jan 2024 14:36:58 +0100 Subject: [PATCH 19/19] Fix: pebkac --- cli/templatecreate.go | 5 ++++- cli/templatepush.go | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/cli/templatecreate.go b/cli/templatecreate.go index e5846f16bea5d..09acfe205480a 100644 --- a/cli/templatecreate.go +++ b/cli/templatecreate.go @@ -113,7 +113,10 @@ func (r *RootCmd) templateCreate() *clibase.Cmd { if err != nil { return err } - _, _ = fmt.Fprintln(inv.Stdout, "Auto-discovered Terraform tfvars files. Make sure to review and clean up any unused files.") + + if len(varsFiles) > 0 { + _, _ = fmt.Fprintln(inv.Stdout, "Auto-discovered Terraform tfvars files. Make sure to review and clean up any unused files.") + } } // Confirm upload of the directory. diff --git a/cli/templatepush.go b/cli/templatepush.go index 6ead74b731c68..c1099a67bdf92 100644 --- a/cli/templatepush.go +++ b/cli/templatepush.go @@ -84,7 +84,10 @@ func (r *RootCmd) templatePush() *clibase.Cmd { if err != nil { return err } - _, _ = fmt.Fprintln(inv.Stdout, "Auto-discovered Terraform tfvars files. Make sure to review and clean up any unused files.") + + if len(varsFiles) > 0 { + _, _ = fmt.Fprintln(inv.Stdout, "Auto-discovered Terraform tfvars files. Make sure to review and clean up any unused files.") + } } resp, err := uploadFlags.upload(inv, client)