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.