Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 1 addition & 54 deletions cmd/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func (cli *CLI) inspectModule(opts Options, dir string, filterFiles []string) (t
}

// Setup runners
rootRunner, moduleRunners, err := cli.setupRunners(opts, dir)
rootRunner, moduleRunners, err := tflint.BuildRunners(cli.loader, cli.config, cli.originalWorkingDir, dir)
if err != nil {
return issues, changes, err
}
Expand Down Expand Up @@ -196,59 +196,6 @@ By setting TFLINT_LOG=trace, you can confirm the changes made by the autofix and
return issues, changes, nil
}

func (cli *CLI) setupRunners(_ Options, dir string) (*tflint.Runner, []*tflint.Runner, error) {
rootMod, diags := cli.loader.LoadRootModule(dir)
if diags.HasErrors() {
return nil, []*tflint.Runner{}, fmt.Errorf("Failed to load the root module; %w", diags)
}

files, diags := cli.loader.LoadConfigDirFiles(dir)
if diags.HasErrors() {
return nil, []*tflint.Runner{}, fmt.Errorf("Failed to list configuration files; %w", diags)
}
annotations := map[string]tflint.Annotations{}
for path, file := range files {
ants, lexDiags := tflint.NewAnnotations(path, file)
diags = diags.Extend(lexDiags)
annotations[path] = ants
}
if diags.HasErrors() {
return nil, []*tflint.Runner{}, fmt.Errorf("Failed to parse annotations; %w", diags)
}

variables, diags := cli.loader.LoadValuesFiles(dir, cli.config.Varfiles...)
if diags.HasErrors() {
return nil, []*tflint.Runner{}, fmt.Errorf("Failed to load values files; %w", diags)
}
cliVars, diags := terraform.ParseVariableValues(cli.config.Variables, rootMod.Variables)
if diags.HasErrors() {
return nil, []*tflint.Runner{}, fmt.Errorf("Failed to parse variables; %w", diags)
}
variables = append(variables, cliVars)

configs, diags := terraform.BuildConfig(
rootMod,
cli.loader.ModuleWalker(cli.config.CallModuleType),
cli.originalWorkingDir,
variables...,
)
if diags.HasErrors() {
return nil, []*tflint.Runner{}, fmt.Errorf("Failed to build configurations; %w", diags)
}

runner, err := tflint.NewRunner(cli.originalWorkingDir, cli.config, annotations, configs, variables...)
if err != nil {
return nil, []*tflint.Runner{}, fmt.Errorf("Failed to initialize a runner; %w", err)
}

moduleRunners, err := tflint.NewModuleRunners(runner)
if err != nil {
return nil, []*tflint.Runner{}, fmt.Errorf("Failed to prepare rule checking; %w", err)
}

return runner, moduleRunners, nil
}

func launchPlugins(config *tflint.Config, fix bool) (*plugin.Plugin, error) {
// Lookup plugins
rulesetPlugin, err := plugin.Discovery(config)
Expand Down
45 changes: 3 additions & 42 deletions langserver/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,50 +171,11 @@ func (h *handler) inspect() (map[string][]lsp.Diagnostic, error) {
return ret, fmt.Errorf("Failed to prepare loading: %w", err)
}

rootMod, diags := loader.LoadRootModule(".")
if diags.HasErrors() {
return ret, fmt.Errorf("Failed to load the root module: %w", diags)
}
files, diags := loader.LoadConfigDirFiles(".")
if diags.HasErrors() {
return ret, fmt.Errorf("Failed to list configuration files: %w", diags)
}
annotations := map[string]tflint.Annotations{}
for path, file := range files {
ants, lexDiags := tflint.NewAnnotations(path, file)
diags = diags.Extend(lexDiags)
annotations[path] = ants
}

variables, diags := loader.LoadValuesFiles(".", h.config.Varfiles...)
if diags.HasErrors() {
return ret, fmt.Errorf("Failed to load values files: %w", diags)
}
cliVars, diags := terraform.ParseVariableValues(h.config.Variables, rootMod.Variables)
if diags.HasErrors() {
return ret, fmt.Errorf("Failed to parse variables: %w", diags)
}
variables = append(variables, cliVars)

configs, diags := terraform.BuildConfig(
rootMod,
loader.ModuleWalker(h.config.CallModuleType),
h.rootDir,
variables...,
)
if diags.HasErrors() {
return ret, fmt.Errorf("Failed to build configurations: %w", diags)
}

runner, err := tflint.NewRunner(h.rootDir, h.config, annotations, configs, variables...)
if err != nil {
return ret, fmt.Errorf("Failed to initialize a runner: %w", err)
}
runners, err := tflint.NewModuleRunners(runner)
runner, runners, err := tflint.BuildRunners(loader, h.config, h.rootDir, ".")
if err != nil {
return ret, fmt.Errorf("Failed to prepare rule checking: %w", err)
return ret, err
}
runners = append(runners, runner)
runners = append(runners, runner) // langserver iterates a single slice incl. root

config := h.config.ToPluginConfig()
for name, ruleset := range h.plugin.RuleSets {
Expand Down
62 changes: 62 additions & 0 deletions tflint/runner_set.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package tflint

import (
"fmt"

"github.com/terraform-linters/tflint/terraform"
)

// BuildRunners loads the module rooted at dir using the given loader and config,
// returning the root runner and its module runners.
func BuildRunners(loader *terraform.Loader, config *Config, workingDir, dir string) (*Runner, []*Runner, error) {
rootMod, diags := loader.LoadRootModule(dir)
if diags.HasErrors() {
return nil, []*Runner{}, fmt.Errorf("Failed to load the root module; %w", diags)
}

files, diags := loader.LoadConfigDirFiles(dir)
if diags.HasErrors() {
return nil, []*Runner{}, fmt.Errorf("Failed to list configuration files; %w", diags)
}
annotations := map[string]Annotations{}
for path, file := range files {
ants, lexDiags := NewAnnotations(path, file)
diags = diags.Extend(lexDiags)
annotations[path] = ants
}
if diags.HasErrors() {
return nil, []*Runner{}, fmt.Errorf("Failed to parse annotations; %w", diags)
}

variables, diags := loader.LoadValuesFiles(dir, config.Varfiles...)
if diags.HasErrors() {
return nil, []*Runner{}, fmt.Errorf("Failed to load values files; %w", diags)
}
cliVars, diags := terraform.ParseVariableValues(config.Variables, rootMod.Variables)
if diags.HasErrors() {
return nil, []*Runner{}, fmt.Errorf("Failed to parse variables; %w", diags)
}
variables = append(variables, cliVars)

configs, diags := terraform.BuildConfig(
rootMod,
loader.ModuleWalker(config.CallModuleType),
workingDir,
variables...,
)
if diags.HasErrors() {
return nil, []*Runner{}, fmt.Errorf("Failed to build configurations; %w", diags)
}

runner, err := NewRunner(workingDir, config, annotations, configs, variables...)
if err != nil {
return nil, []*Runner{}, fmt.Errorf("Failed to initialize a runner; %w", err)
}

moduleRunners, err := NewModuleRunners(runner)
if err != nil {
return nil, []*Runner{}, fmt.Errorf("Failed to prepare rule checking; %w", err)
}

return runner, moduleRunners, nil
}
111 changes: 111 additions & 0 deletions tflint/runner_set_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package tflint

import (
"os"
"testing"

"github.com/spf13/afero"
"github.com/terraform-linters/tflint/terraform"
"github.com/zclconf/go-cty/cty"
)

func TestBuildRunners(t *testing.T) {
for _, tc := range []struct {
name string
files map[string]string
varfiles []string
wantModuleRunners int
wantVariable string
wantVariableValue cty.Value
wantAnnotated string
}{
{
name: "variable from auto-loaded varfile",
files: map[string]string{
"main.tf": `
variable "instance_type" {}

resource "aws_instance" "main" {
instance_type = var.instance_type // tflint-ignore: aws_instance_invalid_type
}
`,
"custom.auto.tfvars": `instance_type = "t2.micro"`,
},
wantModuleRunners: 0,
wantVariable: "instance_type",
wantVariableValue: cty.StringVal("t2.micro"),
wantAnnotated: "main.tf",
},
{
name: "variable from explicit varfile",
files: map[string]string{
"main.tf": `
variable "instance_type" {}
`,
"custom.tfvars": `instance_type = "m5.xlarge"`,
},
varfiles: []string{"custom.tfvars"},
wantModuleRunners: 0,
wantVariable: "instance_type",
wantVariableValue: cty.StringVal("m5.xlarge"),
},
{
name: "with local module",
files: map[string]string{
"main.tf": `
variable "instance_type" {}

module "child" {
source = "./module"
}
`,
"module/main.tf": `
resource "aws_instance" "child" {}
`,
"custom.auto.tfvars": `instance_type = "t2.large"`,
},
wantModuleRunners: 1,
wantVariable: "instance_type",
wantVariableValue: cty.StringVal("t2.large"),
},
} {
t.Run(tc.name, func(t *testing.T) {
fs := afero.Afero{Fs: afero.NewMemMapFs()}
for name, src := range tc.files {
if err := fs.WriteFile(name, []byte(src), os.ModePerm); err != nil {
t.Fatal(err)
}
}

wd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
loader, err := terraform.NewLoader(fs, wd)
if err != nil {
t.Fatal(err)
}

config := EmptyConfig()
config.Varfiles = tc.varfiles

runner, moduleRunners, err := BuildRunners(loader, config, wd, ".")
if err != nil {
t.Fatalf("Unexpected error occurred: %s", err)
}

if len(moduleRunners) != tc.wantModuleRunners {
t.Errorf("Expected %d module runners, got %d", tc.wantModuleRunners, len(moduleRunners))
}

got := runner.Ctx.VariableValues[""][tc.wantVariable]
if !got.RawEquals(tc.wantVariableValue) {
t.Errorf("Expected variable %q to resolve to %#v, got %#v", tc.wantVariable, tc.wantVariableValue, got)
}

if tc.wantAnnotated != "" && len(runner.annotations[tc.wantAnnotated]) == 0 {
t.Errorf("Expected annotations to be attached for %q, got none", tc.wantAnnotated)
}
})
}
}
Loading