Skip to content

Commit bc016f5

Browse files
committed
WIP
1 parent f149279 commit bc016f5

File tree

8 files changed

+326
-149
lines changed

8 files changed

+326
-149
lines changed

coderd/database/models.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/querier.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries.sql.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

provisioner/terraform/parse.go

Lines changed: 110 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
package terraform
22

33
import (
4+
"context"
45
"encoding/json"
56
"fmt"
7+
"os"
68
"path/filepath"
79
"sort"
810
"strings"
911

12+
"github.com/hashicorp/hcl/v2"
13+
"github.com/hashicorp/hcl/v2/hclparse"
14+
"github.com/hashicorp/hcl/v2/hclsyntax"
1015
"github.com/hashicorp/terraform-config-inspect/tfconfig"
1116
"github.com/mitchellh/go-wordwrap"
1217
"golang.org/x/xerrors"
@@ -28,6 +33,109 @@ func (s *server) Parse(sess *provisionersdk.Session, _ *proto.ParseRequest, _ <-
2833
return provisionersdk.ParseErrorf("load module: %s", formatDiagnostics(sess.WorkDirectory, diags))
2934
}
3035

36+
workspaceTags, err := s.loadWorkspaceTags(ctx, module)
37+
if err != nil {
38+
return provisionersdk.ParseErrorf("can't load workspace tags: %v", err)
39+
}
40+
41+
templateVariables, err := loadTerraformVariables(module)
42+
if err != nil {
43+
return provisionersdk.ParseErrorf("can't load template variables: %v", err)
44+
}
45+
46+
return &proto.ParseComplete{
47+
TemplateVariables: templateVariables,
48+
WorkspaceTags: workspaceTags,
49+
}
50+
}
51+
52+
var rootTemplateSchema = &hcl.BodySchema{
53+
Blocks: []hcl.BlockHeaderSchema{
54+
{
55+
Type: "data",
56+
LabelNames: []string{"type", "name"},
57+
},
58+
},
59+
}
60+
61+
var coderWorkspaceTagsSchema = &hcl.BodySchema{
62+
Attributes: []hcl.AttributeSchema{
63+
{
64+
Name: "tags",
65+
},
66+
},
67+
}
68+
69+
func (s *server) loadWorkspaceTags(ctx context.Context, module *tfconfig.Module) (map[string]string, error) {
70+
workspaceTags := map[string]string{}
71+
72+
for _, dataResource := range module.DataResources {
73+
if dataResource.Type != "coder_workspace_tags" {
74+
s.logger.Debug(ctx, "skip resource as it is not a coder_workspace_tags", "resource_name", dataResource.Name)
75+
continue
76+
}
77+
78+
var file *hcl.File
79+
var diags hcl.Diagnostics
80+
parser := hclparse.NewParser()
81+
if strings.HasSuffix(dataResource.Pos.Filename, ".tf") {
82+
file, diags = parser.ParseHCLFile(dataResource.Pos.Filename)
83+
} else {
84+
s.logger.Debug(ctx, "only .tf files can be parsed", "filename", dataResource.Pos.Filename)
85+
continue
86+
}
87+
88+
if diags.HasErrors() {
89+
return nil, xerrors.Errorf("can't parse the resource file: %s", diags.Error())
90+
}
91+
92+
// Parse root to find "coder_workspace_tags"
93+
content, _, diags := file.Body.PartialContent(rootTemplateSchema)
94+
if diags.HasErrors() {
95+
return nil, xerrors.Errorf("can't parse the resource file: %s", diags.Error())
96+
}
97+
98+
for _, block := range content.Blocks {
99+
// Parse "coder_workspace_tags" to find all key-value tags
100+
resContent, _, diags := block.Body.PartialContent(coderWorkspaceTagsSchema)
101+
if diags.HasErrors() {
102+
return nil, xerrors.Errorf(`can't parse the resource coder_workspace_tags: %s`, diags.Error())
103+
}
104+
105+
expr := resContent.Attributes["tags"].Expr
106+
tagsExpr, ok := expr.(*hclsyntax.ObjectConsExpr)
107+
if !ok {
108+
return nil, xerrors.Errorf(`"tags" attribute is expected to be a key-value map`)
109+
}
110+
111+
// Parse key-value entries in "coder_workspace_tags"
112+
for _, tagItem := range tagsExpr.Items {
113+
key, err := previewFileContent(tagItem.KeyExpr.Range())
114+
if err != nil {
115+
return nil, xerrors.Errorf("can't preview the resource file: %v", err)
116+
}
117+
value, err := previewFileContent(tagItem.ValueExpr.Range())
118+
if err != nil {
119+
return nil, xerrors.Errorf("can't preview the resource file: %v", err)
120+
}
121+
122+
s.logger.Info(ctx, "workspace tag found", "key", key, "value", value)
123+
workspaceTags[key] = value
124+
}
125+
}
126+
}
127+
return workspaceTags, nil // TODO
128+
}
129+
130+
func previewFileContent(fileRange hcl.Range) (string, error) {
131+
body, err := os.ReadFile(fileRange.Filename)
132+
if err != nil {
133+
return "", err
134+
}
135+
136+
}
137+
138+
func loadTerraformVariables(module *tfconfig.Module) ([]*proto.TemplateVariable, error) {
31139
// Sort variables by (filename, line) to make the ordering consistent
32140
variables := make([]*tfconfig.Variable, 0, len(module.Variables))
33141
for _, v := range module.Variables {
@@ -38,17 +146,14 @@ func (s *server) Parse(sess *provisionersdk.Session, _ *proto.ParseRequest, _ <-
38146
})
39147

40148
var templateVariables []*proto.TemplateVariable
41-
42149
for _, v := range variables {
43150
mv, err := convertTerraformVariable(v)
44151
if err != nil {
45-
return provisionersdk.ParseErrorf("can't convert the Terraform variable to a managed one: %s", err)
152+
return nil, err
46153
}
47154
templateVariables = append(templateVariables, mv)
48155
}
49-
return &proto.ParseComplete{
50-
TemplateVariables: templateVariables,
51-
}
156+
return templateVariables, nil
52157
}
53158

54159
// Converts a Terraform variable to a template-wide variable, processed by Coder.

provisioner/terraform/parse_test.go

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func TestParse(t *testing.T) {
2323
// If ErrorContains is not empty, then the ParseComplete should have an Error containing the given string
2424
ErrorContains string
2525
}{
26-
{
26+
/*{
2727
Name: "single-variable",
2828
Files: map[string]string{
2929
"main.tf": `variable "A" {
@@ -200,6 +200,58 @@ func TestParse(t *testing.T) {
200200
},
201201
},
202202
},
203+
},*/
204+
{
205+
Name: "workspace-tags",
206+
Files: map[string]string{
207+
"parameters.tf": `data "coder_parameter" "os_selector" {
208+
name = "os_selector"
209+
display_name = "Operating System"
210+
mutable = false
211+
212+
default = "osx"
213+
214+
option {
215+
icon = "/icons/linux.png"
216+
name = "Linux"
217+
value = "linux"
218+
}
219+
option {
220+
icon = "/icons/osx.png"
221+
name = "OSX"
222+
value = "osx"
223+
}
224+
option {
225+
icon = "/icons/windows.png"
226+
name = "Windows"
227+
value = "windows"
228+
}
229+
}
230+
231+
data "coder_parameter" "feature_cache_enabled" {
232+
name = "feature_cache_enabled"
233+
display_name = "Enable cache?"
234+
type = "bool"
235+
236+
default = false
237+
}
238+
239+
data "coder_parameter" "feature_debug_enabled" {
240+
name = "feature_debug_enabled"
241+
display_name = "Enable debug?"
242+
type = "bool"
243+
244+
default = true
245+
}`,
246+
"tags.tf": `data "coder_workspace_tags" "custom_workspace_tags" {
247+
tags = {
248+
"cluster" = "developers"
249+
"os" = data.coder_parameter.os_selector.value
250+
"debug" = "${data.coder_parameter.feature_debug_enabled.value}+12345"
251+
"cache" = data.coder_parameter.feature_cache_enabled.value == "true" ? "nix-with-cache" : "no-cache"
252+
}
253+
}`,
254+
},
203255
},
204256
}
205257

0 commit comments

Comments
 (0)