Skip to content

Commit

Permalink
Move evaluation impl to under the terraform package (#1510)
Browse files Browse the repository at this point in the history
  • Loading branch information
wata727 authored Sep 23, 2022
1 parent a09464b commit fe14578
Show file tree
Hide file tree
Showing 21 changed files with 1,430 additions and 1,681 deletions.
7 changes: 4 additions & 3 deletions cmd/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/spf13/afero"
"github.com/terraform-linters/tflint-plugin-sdk/hclext"
"github.com/terraform-linters/tflint/plugin"
"github.com/terraform-linters/tflint/terraform"
"github.com/terraform-linters/tflint/tflint"
)

Expand Down Expand Up @@ -125,9 +126,9 @@ func (cli *CLI) setupRunners(opts Options, cfg *tflint.Config, dir string) ([]*t
if err != nil {
return []*tflint.Runner{}, fmt.Errorf("Failed to load values files; %w", err)
}
cliVars, err := tflint.ParseTFVariables(cfg.Variables, configs.Module.Variables)
if err != nil {
return []*tflint.Runner{}, fmt.Errorf("Failed to parse variables; %w", err)
cliVars, diags := terraform.ParseVariableValues(cfg.Variables, configs.Module.Variables)
if diags.HasErrors() {
return []*tflint.Runner{}, fmt.Errorf("Failed to parse variables; %w", diags)
}
variables = append(variables, cliVars)

Expand Down
7 changes: 4 additions & 3 deletions langserver/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/spf13/afero"
"github.com/terraform-linters/tflint-plugin-sdk/hclext"
"github.com/terraform-linters/tflint/plugin"
"github.com/terraform-linters/tflint/terraform"
"github.com/terraform-linters/tflint/tflint"
)

Expand Down Expand Up @@ -146,9 +147,9 @@ func (h *handler) inspect() (map[string][]lsp.Diagnostic, error) {
if err != nil {
return ret, fmt.Errorf("Failed to load values files: %w", err)
}
cliVars, err := tflint.ParseTFVariables(h.config.Variables, configs.Module.Variables)
if err != nil {
return ret, fmt.Errorf("Failed to parse variables: %w", err)
cliVars, diags := terraform.ParseVariableValues(h.config.Variables, configs.Module.Variables)
if diags.HasErrors() {
return ret, fmt.Errorf("Failed to parse variables: %w", diags)
}
variables = append(variables, cliVars)

Expand Down
42 changes: 38 additions & 4 deletions plugin/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ func (s *GRPCServer) EvaluateExpr(expr hcl.Expression, opts sdk.EvaluateExprOpti
runner = s.rootRunner
}

val, err := runner.EvaluateExpr(expr, *opts.WantType)
if err != nil {
return val, err
val, diags := runner.Ctx.EvaluateExpr(expr, *opts.WantType)
if diags.HasErrors() {
return val, diags
}

if val.HasMark(marks.Sensitive) {
Expand All @@ -111,7 +111,41 @@ func (s *GRPCServer) EvaluateExpr(expr hcl.Expression, opts sdk.EvaluateExprOpti
log.Printf("[INFO] %s. TFLint ignores expressions with sensitive values.", err)
return cty.NullVal(cty.NilType), err
}
return val, err

if *opts.WantType == cty.DynamicPseudoType {
return val, nil
}

err := cty.Walk(val, func(path cty.Path, v cty.Value) (bool, error) {
if !v.IsKnown() {
err := fmt.Errorf(
"unknown value found in %s:%d%w",
expr.Range().Filename,
expr.Range().Start.Line,
sdk.ErrUnknownValue,
)
log.Printf("[INFO] %s. TFLint can only evaluate provided variables and skips dynamic values.", err)
return false, err
}

if v.IsNull() {
err := fmt.Errorf(
"null value found in %s:%d%w",
expr.Range().Filename,
expr.Range().Start.Line,
sdk.ErrNullValue,
)
log.Printf("[INFO] %s. TFLint ignores expressions with null values.", err)
return false, err
}

return true, nil
})
if err != nil {
return cty.NullVal(cty.NilType), err
}

return val, nil
}

// EmitIssue stores an issue in the server based on passed rule, message, and location.
Expand Down
98 changes: 97 additions & 1 deletion plugin/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,10 +357,19 @@ func TestEvaluateExpr(t *testing.T) {
variable "foo" {
default = "bar"
}
variable "sensitive" {
sensitive = true
default = "foo"
}`})
}
variable "no_default" {}
variable "null" {
type = string
default = null
}
`})
rootRunner := tflint.TestRunner(t, map[string]string{"main.tf": `
variable "foo" {
default = "baz"
Expand Down Expand Up @@ -415,6 +424,93 @@ variable "foo" {
return err == nil || !errors.Is(err, sdk.ErrSensitive)
},
},
{
Name: "no default",
Args: func() (hcl.Expression, sdk.EvaluateExprOption) {
return hclExpr(`var.no_default`), sdk.EvaluateExprOption{WantType: &cty.String, ModuleCtx: sdk.SelfModuleCtxType}
},
Want: cty.NullVal(cty.NilType),
ErrCheck: func(err error) bool {
return err == nil || !errors.Is(err, sdk.ErrUnknownValue)
},
},
{
Name: "no default as cty.Value",
Args: func() (hcl.Expression, sdk.EvaluateExprOption) {
return hclExpr(`var.no_default`), sdk.EvaluateExprOption{WantType: &cty.DynamicPseudoType, ModuleCtx: sdk.SelfModuleCtxType}
},
Want: cty.DynamicVal,
ErrCheck: neverHappend,
},
{
Name: "no default value in object",
Args: func() (hcl.Expression, sdk.EvaluateExprOption) {
ty := cty.Object(map[string]cty.Type{"value": cty.String})
return hclExpr(`{ value = var.no_default }`), sdk.EvaluateExprOption{WantType: &ty, ModuleCtx: sdk.SelfModuleCtxType}
},
Want: cty.NullVal(cty.NilType),
ErrCheck: func(err error) bool {
return err == nil || !errors.Is(err, sdk.ErrUnknownValue)
},
},
{
Name: "null",
Args: func() (hcl.Expression, sdk.EvaluateExprOption) {
return hclExpr(`var.null`), sdk.EvaluateExprOption{WantType: &cty.String, ModuleCtx: sdk.SelfModuleCtxType}
},
Want: cty.NullVal(cty.NilType),
ErrCheck: func(err error) bool {
return err == nil || !errors.Is(err, sdk.ErrNullValue)
},
},
{
Name: "null as cty.Value",
Args: func() (hcl.Expression, sdk.EvaluateExprOption) {
return hclExpr(`var.null`), sdk.EvaluateExprOption{WantType: &cty.DynamicPseudoType, ModuleCtx: sdk.SelfModuleCtxType}
},
Want: cty.NullVal(cty.String),
ErrCheck: neverHappend,
},
{
Name: "null value in object",
Args: func() (hcl.Expression, sdk.EvaluateExprOption) {
ty := cty.Object(map[string]cty.Type{"value": cty.String})
return hclExpr(`{ value = var.null }`), sdk.EvaluateExprOption{WantType: &ty, ModuleCtx: sdk.SelfModuleCtxType}
},
Want: cty.NullVal(cty.NilType),
ErrCheck: func(err error) bool {
return err == nil || !errors.Is(err, sdk.ErrNullValue)
},
},
{
Name: "unevaluable",
Args: func() (hcl.Expression, sdk.EvaluateExprOption) {
return hclExpr(`module.instance.output`), sdk.EvaluateExprOption{WantType: &cty.String, ModuleCtx: sdk.SelfModuleCtxType}
},
Want: cty.NullVal(cty.NilType),
ErrCheck: func(err error) bool {
return err == nil || !errors.Is(err, sdk.ErrUnknownValue)
},
},
{
Name: "unevaluable as cty.Value",
Args: func() (hcl.Expression, sdk.EvaluateExprOption) {
return hclExpr(`module.instance.output`), sdk.EvaluateExprOption{WantType: &cty.DynamicPseudoType, ModuleCtx: sdk.SelfModuleCtxType}
},
Want: cty.DynamicVal,
ErrCheck: neverHappend,
},
{
Name: "unevaluable value in object",
Args: func() (hcl.Expression, sdk.EvaluateExprOption) {
ty := cty.Object(map[string]cty.Type{"value": cty.String})
return hclExpr(`{ value = module.instance.output }`), sdk.EvaluateExprOption{WantType: &ty, ModuleCtx: sdk.SelfModuleCtxType}
},
Want: cty.NullVal(cty.NilType),
ErrCheck: func(err error) bool {
return err == nil || !errors.Is(err, sdk.ErrUnknownValue)
},
},
}

for _, test := range tests {
Expand Down
4 changes: 4 additions & 0 deletions terraform/addrs/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ func (r ResourceInstance) String() string {
return r.Resource.String() + r.Key.String()
}

func (r ResourceInstance) ContainingResource() Resource {
return r.Resource
}

// ResourceMode defines which lifecycle applies to a given resource. Each
// resource lifecycle has a slightly different address format.
type ResourceMode rune
Expand Down
Loading

0 comments on commit fe14578

Please sign in to comment.