Skip to content

Commit e03b575

Browse files
authored
Merge pull request #60 from terraform-linters/add_runner_config
tflint: Add `Runner.Config()`
2 parents aa1706d + 103a95a commit e03b575

File tree

13 files changed

+822
-22
lines changed

13 files changed

+822
-22
lines changed

helper/runner.go

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package helper
22

33
import (
4+
"fmt"
5+
46
"github.com/hashicorp/go-version"
57
"github.com/hashicorp/hcl/v2"
68
"github.com/hashicorp/hcl/v2/gohcl"
79
"github.com/terraform-linters/tflint-plugin-sdk/terraform"
10+
"github.com/terraform-linters/tflint-plugin-sdk/terraform/configs"
811
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
912
"github.com/zclconf/go-cty/cty/gocty"
1013
)
@@ -203,6 +206,74 @@ func (r *Runner) Backend() (*terraform.Backend, error) {
203206
return nil, nil
204207
}
205208

209+
// Config returns the Terraform configuration
210+
func (r *Runner) Config() (*configs.Config, error) {
211+
config := &configs.Config{
212+
Module: &configs.Module{},
213+
}
214+
215+
for _, file := range r.Files {
216+
content, diags := file.Body.Content(configFileSchema)
217+
if diags.HasErrors() {
218+
return nil, diags
219+
}
220+
221+
for _, block := range content.Blocks {
222+
switch block.Type {
223+
case "terraform":
224+
content, diags := block.Body.Content(terraformBlockSchema)
225+
if diags.HasErrors() {
226+
return nil, diags
227+
}
228+
229+
for _, block := range content.Blocks {
230+
switch block.Type {
231+
case "backend":
232+
config.Module.Backend = &terraform.Backend{
233+
Type: block.Labels[0],
234+
TypeRange: block.LabelRanges[0],
235+
Config: block.Body,
236+
DeclRange: block.DefRange,
237+
}
238+
case "required_providers":
239+
// TODO
240+
case "provider_meta":
241+
// TODO
242+
default:
243+
continue
244+
}
245+
}
246+
case "provider":
247+
// TODO
248+
case "variable":
249+
// TODO
250+
case "locals":
251+
// TODO
252+
case "output":
253+
// TODO
254+
case "module":
255+
call, diags := simpleDecodeModuleCallBlock(block)
256+
if diags.HasErrors() {
257+
return nil, diags
258+
}
259+
config.Module.ModuleCalls[call.Name] = call
260+
case "resource":
261+
resource, diags := simpleDecodeResouceBlock(block)
262+
if diags.HasErrors() {
263+
return nil, diags
264+
}
265+
config.Module.ManagedResources[fmt.Sprintf("%s.%s", resource.Type, resource.Name)] = resource
266+
case "data":
267+
// TODO
268+
default:
269+
continue
270+
}
271+
}
272+
}
273+
274+
return nil, nil
275+
}
276+
206277
// EvaluateExpr returns a value of the passed expression.
207278
// Note that there is no evaluation, no type conversion, etc.
208279
func (r *Runner) EvaluateExpr(expr hcl.Expression, ret interface{}) error {
@@ -505,3 +576,65 @@ func decodeProviderConfigRef(expr hcl.Expression) (*terraform.ProviderConfigRef,
505576

506577
return ref, nil
507578
}
579+
580+
// configFileSchema is the schema for the top-level of a config file.
581+
// @see https://github.com/hashicorp/terraform/blob/v0.13.2/configs/parser_config.go#L197-L239
582+
var configFileSchema = &hcl.BodySchema{
583+
Blocks: []hcl.BlockHeaderSchema{
584+
{
585+
Type: "terraform",
586+
},
587+
{
588+
Type: "required_providers",
589+
},
590+
{
591+
Type: "provider",
592+
LabelNames: []string{"name"},
593+
},
594+
{
595+
Type: "variable",
596+
LabelNames: []string{"name"},
597+
},
598+
{
599+
Type: "locals",
600+
},
601+
{
602+
Type: "output",
603+
LabelNames: []string{"name"},
604+
},
605+
{
606+
Type: "module",
607+
LabelNames: []string{"name"},
608+
},
609+
{
610+
Type: "resource",
611+
LabelNames: []string{"type", "name"},
612+
},
613+
{
614+
Type: "data",
615+
LabelNames: []string{"type", "name"},
616+
},
617+
},
618+
}
619+
620+
// terraformBlockSchema is the schema for a top-level "terraform" block in a configuration file.
621+
// @see https://github.com/hashicorp/terraform/blob/v0.13.2/configs/parser_config.go#L241-L261
622+
var terraformBlockSchema = &hcl.BodySchema{
623+
Attributes: []hcl.AttributeSchema{
624+
{Name: "required_version"},
625+
{Name: "experiments"},
626+
},
627+
Blocks: []hcl.BlockHeaderSchema{
628+
{
629+
Type: "backend",
630+
LabelNames: []string{"type"},
631+
},
632+
{
633+
Type: "required_providers",
634+
},
635+
{
636+
Type: "provider_meta",
637+
LabelNames: []string{"provider"},
638+
},
639+
},
640+
}

terraform/addrs/module.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package addrs
2+
3+
// Module is an alternative representation of addrs.Module.
4+
// https://github.com/hashicorp/terraform/blob/v0.13.1/addrs/module.go#L17
5+
type Module []string

terraform/configs/config.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package configs
2+
3+
import (
4+
"github.com/hashicorp/go-version"
5+
"github.com/hashicorp/hcl/v2"
6+
"github.com/terraform-linters/tflint-plugin-sdk/terraform/addrs"
7+
)
8+
9+
// Config is an alternative representation of configs.Config.
10+
// https://github.com/hashicorp/terraform/blob/v0.13.1/configs/config.go#L22-L78
11+
type Config struct {
12+
// Root *Config
13+
// Parent *Config
14+
Path addrs.Module
15+
// Children map[string]*Config
16+
Module *Module
17+
CallRange hcl.Range
18+
SourceAddr string
19+
SourceAddrRange hcl.Range
20+
Version *version.Version
21+
}

terraform/configs/module.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package configs
2+
3+
import (
4+
"github.com/terraform-linters/tflint-plugin-sdk/terraform"
5+
"github.com/terraform-linters/tflint-plugin-sdk/terraform/experiments"
6+
)
7+
8+
// Module is an alternative representation of configs.Module.
9+
// https://github.com/hashicorp/terraform/blob/v0.13.1/configs/module.go#L14-L45
10+
type Module struct {
11+
SourceDir string
12+
13+
CoreVersionConstraints []terraform.VersionConstraint
14+
15+
ActiveExperiments experiments.Set
16+
17+
Backend *terraform.Backend
18+
ProviderConfigs map[string]*Provider
19+
ProviderRequirements *RequiredProviders
20+
ProviderLocalNames map[terraform.Provider]string
21+
ProviderMetas map[terraform.Provider]*ProviderMeta
22+
23+
Variables map[string]*Variable
24+
Locals map[string]*Local
25+
Outputs map[string]*Output
26+
27+
ModuleCalls map[string]*terraform.ModuleCall
28+
29+
ManagedResources map[string]*terraform.Resource
30+
DataResources map[string]*terraform.Resource
31+
}

terraform/configs/named_values.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package configs
2+
3+
import (
4+
"github.com/hashicorp/hcl/v2"
5+
"github.com/zclconf/go-cty/cty"
6+
)
7+
8+
// Variable is an alternative representation of configs.Variable.
9+
// https://github.com/hashicorp/terraform/blob/v0.13.1/configs/named_values.go#L21-L32
10+
type Variable struct {
11+
Name string
12+
Description string
13+
Default cty.Value
14+
Type cty.Type
15+
ParsingMode VariableParsingMode
16+
Validations []*VariableValidation
17+
18+
DescriptionSet bool
19+
20+
DeclRange hcl.Range
21+
}
22+
23+
// VariableParsingMode is an alternative representation of configs.VariableParsingMode.
24+
// https://github.com/hashicorp/terraform/blob/v0.13.1/configs/named_values.go#L226-L234
25+
type VariableParsingMode rune
26+
27+
// VariableParseLiteral is a variable parsing mode that just takes the given
28+
// string directly as a cty.String value.
29+
const VariableParseLiteral VariableParsingMode = 'L'
30+
31+
// VariableParseHCL is a variable parsing mode that attempts to parse the given
32+
// string as an HCL expression and returns the result.
33+
const VariableParseHCL VariableParsingMode = 'H'
34+
35+
// VariableValidation is an alternative representation of configs.VariableValidation.
36+
// https://github.com/hashicorp/terraform/blob/v0.13.1/configs/named_values.go#L273-L289
37+
type VariableValidation struct {
38+
// Condition is an expression that refers to the variable being tested
39+
// and contains no other references. The expression must return true
40+
// to indicate that the value is valid or false to indicate that it is
41+
// invalid. If the expression produces an error, that's considered a bug
42+
// in the module defining the validation rule, not an error in the caller.
43+
Condition hcl.Expression
44+
45+
// ErrorMessage is one or more full sentences, which would need to be in
46+
// English for consistency with the rest of the error message output but
47+
// can in practice be in any language as long as it ends with a period.
48+
// The message should describe what is required for the condition to return
49+
// true in a way that would make sense to a caller of the module.
50+
ErrorMessage string
51+
52+
DeclRange hcl.Range
53+
}
54+
55+
// Local is an alternative representation of configs.Local.
56+
// https://github.com/hashicorp/terraform/blob/v0.13.1/configs/named_values.go#L485-L490
57+
type Local struct {
58+
Name string
59+
Expr hcl.Expression
60+
61+
DeclRange hcl.Range
62+
}
63+
64+
// Output is an alternative representation of configs.Output.
65+
// https://github.com/hashicorp/terraform/blob/v0.13.1/configs/named_values.go#L422-L433
66+
type Output struct {
67+
Name string
68+
Description string
69+
Expr hcl.Expression
70+
// DependsOn []hcl.Traversal
71+
Sensitive bool
72+
73+
DescriptionSet bool
74+
SensitiveSet bool
75+
76+
DeclRange hcl.Range
77+
}

terraform/configs/provider.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package configs
2+
3+
import (
4+
"github.com/hashicorp/hcl/v2"
5+
"github.com/terraform-linters/tflint-plugin-sdk/terraform"
6+
)
7+
8+
// Provider is an alternative representation of configs.Provider.
9+
// https://github.com/hashicorp/terraform/blob/v0.13.1/configs/provider.go#L17-L28
10+
type Provider struct {
11+
Name string
12+
NameRange hcl.Range
13+
Alias string
14+
AliasRange *hcl.Range // nil if no alias set
15+
16+
Version terraform.VersionConstraint
17+
18+
Config hcl.Body
19+
20+
DeclRange hcl.Range
21+
}
22+
23+
// ProviderMeta is an alternative representation of configs.ProviderMeta.
24+
// https://github.com/hashicorp/terraform/blob/v0.13.1/configs/provider_meta.go#L7-L13
25+
type ProviderMeta struct {
26+
Provider string
27+
Config hcl.Body
28+
29+
ProviderRange hcl.Range
30+
DeclRange hcl.Range
31+
}
32+
33+
// RequiredProvider is an alternative representation of configs.RequiredProvider.
34+
// https://github.com/hashicorp/terraform/blob/v0.13.1/configs/provider_requirements.go#L14-L20
35+
type RequiredProvider struct {
36+
Name string
37+
Source string
38+
Type terraform.Provider
39+
Requirement terraform.VersionConstraint
40+
DeclRange hcl.Range
41+
}
42+
43+
// RequiredProviders is an alternative representation of configs.RequiredProviders.
44+
// https://github.com/hashicorp/terraform/blob/v0.13.1/configs/provider_requirements.go#L22-L25
45+
type RequiredProviders struct {
46+
RequiredProviders map[string]*RequiredProvider
47+
DeclRange hcl.Range
48+
}

terraform/experiments/experiment.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package experiments
2+
3+
// Experiment is an alternative representation of experiments.Experiment.
4+
// https://github.com/hashicorp/terraform/blob/v0.13.1/experiments/experiment.go#L5
5+
type Experiment string
6+
7+
// Set is an alternative representation of experiments.Set.
8+
// https://github.com/hashicorp/terraform/blob/v0.13.1/experiments/set.go#L5
9+
type Set map[Experiment]struct{}

tflint/client/client.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
hcl "github.com/hashicorp/hcl/v2"
1111
"github.com/terraform-linters/tflint-plugin-sdk/terraform"
12+
"github.com/terraform-linters/tflint-plugin-sdk/terraform/configs"
1213
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
1314
"github.com/zclconf/go-cty/cty/gocty"
1415
)
@@ -150,6 +151,26 @@ func (c *Client) Backend() (*terraform.Backend, error) {
150151
return backend, nil
151152
}
152153

154+
// Config calls the server-side Config method and returns the Terraform configuration.
155+
func (c *Client) Config() (*configs.Config, error) {
156+
log.Print("[DEBUG] Accessing to Config")
157+
158+
var response ConfigResponse
159+
if err := c.rpcClient.Call("Plugin.Config", ConfigRequest{}, &response); err != nil {
160+
return nil, err
161+
}
162+
if response.Err != nil {
163+
return nil, response.Err
164+
}
165+
166+
config, diags := decodeConfig(response.Config)
167+
if diags.HasErrors() {
168+
return nil, diags
169+
}
170+
171+
return config, nil
172+
}
173+
153174
// EvaluateExpr calls the server-side EvalExpr method and reflects the response
154175
// in the passed argument.
155176
func (c *Client) EvaluateExpr(expr hcl.Expression, ret interface{}) error {

0 commit comments

Comments
 (0)