1
1
package terraform
2
2
3
3
import (
4
+ "context"
4
5
"encoding/json"
5
6
"fmt"
7
+ "os"
6
8
"path/filepath"
7
9
"sort"
8
10
"strings"
9
11
12
+ "github.com/hashicorp/hcl/v2"
13
+ "github.com/hashicorp/hcl/v2/hclparse"
14
+ "github.com/hashicorp/hcl/v2/hclsyntax"
10
15
"github.com/hashicorp/terraform-config-inspect/tfconfig"
11
16
"github.com/mitchellh/go-wordwrap"
12
17
"golang.org/x/xerrors"
@@ -28,6 +33,109 @@ func (s *server) Parse(sess *provisionersdk.Session, _ *proto.ParseRequest, _ <-
28
33
return provisionersdk .ParseErrorf ("load module: %s" , formatDiagnostics (sess .WorkDirectory , diags ))
29
34
}
30
35
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 ) {
31
139
// Sort variables by (filename, line) to make the ordering consistent
32
140
variables := make ([]* tfconfig.Variable , 0 , len (module .Variables ))
33
141
for _ , v := range module .Variables {
@@ -38,17 +146,14 @@ func (s *server) Parse(sess *provisionersdk.Session, _ *proto.ParseRequest, _ <-
38
146
})
39
147
40
148
var templateVariables []* proto.TemplateVariable
41
-
42
149
for _ , v := range variables {
43
150
mv , err := convertTerraformVariable (v )
44
151
if err != nil {
45
- return provisionersdk . ParseErrorf ( "can't convert the Terraform variable to a managed one: %s" , err )
152
+ return nil , err
46
153
}
47
154
templateVariables = append (templateVariables , mv )
48
155
}
49
- return & proto.ParseComplete {
50
- TemplateVariables : templateVariables ,
51
- }
156
+ return templateVariables , nil
52
157
}
53
158
54
159
// Converts a Terraform variable to a template-wide variable, processed by Coder.
0 commit comments