diff --git a/preview_test.go b/preview_test.go index b0ea9b3..59a8761 100644 --- a/preview_test.go +++ b/preview_test.go @@ -49,13 +49,25 @@ func Test_Extract(t *testing.T) { dir: "badparam", failPreview: true, }, + { + name: "sometags", + dir: "sometags", + expTags: map[string]string{ + "string": "foo", + "number": "42", + "bool": "true", + // null tags are omitted + }, + unknownTags: []string{ + "complex", "map", "list", + }, + }, { name: "simple static values", dir: "static", expTags: map[string]string{ "zone": "developers", }, - unknownTags: []string{}, params: map[string]assertParam{ "region": ap().value("us"). def("us"). @@ -510,7 +522,18 @@ func Test_Extract(t *testing.T) { // Assert tags validTags := output.WorkspaceTags.Tags() - assert.Equal(t, tc.expTags, validTags) + for k, expected := range tc.expTags { + tag, ok := validTags[k] + if !ok { + t.Errorf("expected tag %q to be present in output, but it was not", k) + continue + } + if tag != expected { + assert.JSONEqf(t, expected, tag, "tag %q does not match expected, nor is it a json equivalent", k) + } + } + assert.Equal(t, len(tc.expTags), len(output.WorkspaceTags.Tags()), "unexpected number of tags in output") + assert.ElementsMatch(t, tc.unknownTags, output.WorkspaceTags.UnusableTags().SafeNames()) // Assert params diff --git a/testdata/sometags/main.tf b/testdata/sometags/main.tf new file mode 100644 index 0000000..3bbdb93 --- /dev/null +++ b/testdata/sometags/main.tf @@ -0,0 +1,29 @@ +terraform { + required_providers { + coder = { + source = "coder/coder" + version = "2.4.0-pre0" + } + } +} + +data "coder_workspace_tags" "custom_workspace_tags" { + tags = { + "string" = "foo" + "number" = 42 + "bool" = true + "list" = ["a", "b", "c"] + "map" = { + "key1" = "value1" + "key2" = "value2" + } + "complex" = { + "nested_list" = [1, 2, 3] + "nested" = { + "key" = "value" + } + } + "null" = null + } +} + diff --git a/testdata/sometags/skipe2e b/testdata/sometags/skipe2e new file mode 100644 index 0000000..8bcda2e --- /dev/null +++ b/testdata/sometags/skipe2e @@ -0,0 +1 @@ +Complex types are not supported by coder provider \ No newline at end of file diff --git a/testdata/static/main.tf b/testdata/static/main.tf index 8e67ac5..0260e72 100644 --- a/testdata/static/main.tf +++ b/testdata/static/main.tf @@ -15,6 +15,7 @@ terraform { data "coder_workspace_tags" "custom_workspace_tags" { tags = { "zone" = "developers" + "null" = null } } diff --git a/workspacetags.go b/workspacetags.go index d3f6c76..e6e9c3b 100644 --- a/workspacetags.go +++ b/workspacetags.go @@ -47,23 +47,14 @@ func workspaceTags(modules terraform.Modules, files map[string]*hcl.File) (types continue } - // tagsObj, ok := tagsAttr.HCLAttribute().Expr.(*hclsyntax.ObjectConsExpr) - // if !ok { - // diags = diags.Append(&hcl.Diagnostic{ - // Severity: hcl.DiagError, - // Summary: "Incorrect type for \"tags\" attribute", - // // TODO: better error message for types - // Detail: fmt.Sprintf(`"tags" attribute must be an 'ObjectConsExpr', but got %T`, tagsAttr.HCLAttribute().Expr), - // Subject: &tagsAttr.HCLAttribute().NameRange, - // Context: &tagsAttr.HCLAttribute().Range, - // Expression: tagsAttr.HCLAttribute().Expr, - // EvalContext: block.Context().Inner(), - // }) - // continue - //} - var tags []types.Tag tagsValue.ForEachElement(func(key cty.Value, val cty.Value) (stop bool) { + if val.IsNull() { + // null tags with null values are omitted + // This matches the behavior of `terraform apply`` + return false + } + r := tagsAttr.HCLAttribute().Expr.Range() tag, tagDiag := newTag(&r, files, key, val) if tagDiag != nil { @@ -75,15 +66,7 @@ func workspaceTags(modules terraform.Modules, files map[string]*hcl.File) (types return false }) - // for _, item := range tagsObj.Items { - // tag, tagDiag := newTag(tagsObj, files, item, evCtx) - // if tagDiag != nil { - // diags = diags.Append(tagDiag) - // continue - // } - // - // tags = append(tags, tag) - //} + tagBlocks = append(tagBlocks, types.TagBlock{ Tags: tags, Block: block, @@ -96,71 +79,41 @@ func workspaceTags(modules terraform.Modules, files map[string]*hcl.File) (types // newTag creates a workspace tag from its hcl expression. func newTag(srcRange *hcl.Range, _ map[string]*hcl.File, key, val cty.Value) (types.Tag, *hcl.Diagnostic) { - // key, kdiags := expr.KeyExpr.Value(evCtx) - // val, vdiags := expr.ValueExpr.Value(evCtx) - - // TODO: ??? - - // if kdiags.HasErrors() { - // key = cty.UnknownVal(cty.String) - //} - // if vdiags.HasErrors() { - // val = cty.UnknownVal(cty.String) - //} - if key.IsKnown() && key.Type() != cty.String { return types.Tag{}, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid key type for tags", Detail: fmt.Sprintf("Key must be a string, but got %s", key.Type().FriendlyName()), - //Subject: &r, - Context: srcRange, - //Expression: expr.KeyExpr, - //EvalContext: evCtx, - } - } - - if val.IsKnown() && val.Type() != cty.String { - fr := "" - if !val.Type().Equals(cty.NilType) { - fr = val.Type().FriendlyName() - } - // r := expr.ValueExpr.Range() - return types.Tag{}, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid value type for tag", - Detail: fmt.Sprintf("Value must be a string, but got %s", fr), - //Subject: &r, - Context: srcRange, - //Expression: expr.ValueExpr, - //EvalContext: evCtx, + Context: srcRange, } } tag := types.Tag{ Key: types.HCLString{ Value: key, - //ValueDiags: kdiags, - //ValueExpr: expr.KeyExpr, }, Value: types.HCLString{ Value: val, - //ValueDiags: vdiags, - //ValueExpr: expr.ValueExpr, }, } - // ks, err := source(expr.KeyExpr.Range(), files) - // if err == nil { - // src := string(ks) - // tag.Key.Source = &src - //} - // - // vs, err := source(expr.ValueExpr.Range(), files) - // if err == nil { - // src := string(vs) - // tag.Value.Source = &src - //} + switch val.Type() { + case cty.String, cty.Bool, cty.Number: + // These types are supported and can be safely converted to a string. + default: + fr := "" + if !val.Type().Equals(cty.NilType) { + fr = val.Type().FriendlyName() + } + + // Unsupported types will be treated as errors. + tag.Value.ValueDiags = tag.Value.ValueDiags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: fmt.Sprintf("Invalid value type for tag %q", tag.KeyString()), + Detail: fmt.Sprintf("Value must be a string, but got %s.", fr), + Context: srcRange, + }) + } return tag, nil }