rules/provider_version.go (112 lines of code) (raw):

package rules import ( "fmt" "strings" goverison "github.com/hashicorp/go-version" "github.com/terraform-linters/tflint-plugin-sdk/hclext" "github.com/terraform-linters/tflint-plugin-sdk/tflint" "github.com/zclconf/go-cty/cty" ) var _ tflint.Rule = new(ProviderVersionRule) type ProviderVersionRule struct { tflint.DefaultRule ProviderName string ProviderSource string Version string RecommendedConstraint string MustExist bool } func NewProviderVersionRule(providerName, providerSource, ver, recConstr string, mustExist bool) *ProviderVersionRule { return &ProviderVersionRule{ ProviderName: providerName, ProviderSource: providerSource, Version: ver, MustExist: mustExist, RecommendedConstraint: recConstr, } } func (m *ProviderVersionRule) Name() string { return fmt.Sprintf("provider_%s_version_constraint", m.ProviderName) } func (m *ProviderVersionRule) Enabled() bool { return true } func (m *ProviderVersionRule) Severity() tflint.Severity { return tflint.ERROR } func (m *ProviderVersionRule) Check(r tflint.Runner) error { ver, err := goverison.NewVersion(m.Version) if err != nil { return fmt.Errorf("invalid version constraint: %s", err) } content, err := r.GetModuleContent(&hclext.BodySchema{ Blocks: []hclext.BlockSchema{ { Type: "terraform", Body: &hclext.BodySchema{ Blocks: []hclext.BlockSchema{ { Type: "required_providers", Body: &hclext.BodySchema{ Attributes: []hclext.AttributeSchema{ { Name: m.ProviderName, }, }, }, }, }, }, }, }, }, &tflint.GetModuleContentOption{ExpandMode: tflint.ExpandModeNone}) if err != nil { return err } if len(content.Blocks) == 0 { return nil } providerFound := false requiredProviderFound := false for _, tb := range content.Blocks { for _, rpb := range tb.Body.Blocks { requiredProviderFound = true providerAttr, ok := rpb.Body.Attributes[m.ProviderName] if !ok { continue } providerFound = true provider := struct { Source string `cty:"source"` Version string `cty:"version"` }{} wantType := cty.Object(map[string]cty.Type{ "source": cty.String, "version": cty.String, }) if err = r.EvaluateExpr(providerAttr.Expr, &provider, &tflint.EvaluateExprOption{WantType: &wantType}); err != nil { return err } if !strings.EqualFold(provider.Source, m.ProviderSource) { return r.EmitIssue(m, fmt.Sprintf("provider `%s`'s source should be %s, got %s", m.ProviderName, m.ProviderSource, provider.Source), providerAttr.Range) } constraint, err := goverison.NewConstraint(provider.Version) if err != nil { return fmt.Errorf("invalid version constraint: %s", err) } if constraint.Check(ver) { continue } if err = r.EmitIssue(m, fmt.Sprintf("provider `%s`'s version should satisfy %s, got %s. Recommended version constraint `%s`", m.ProviderName, m.Version, provider.Version, m.RecommendedConstraint), providerAttr.Range); err != nil { return err } } } if !requiredProviderFound { return nil } if !providerFound && m.MustExist { return r.EmitIssue(m, fmt.Sprintf("`%s` provider should be declared in the `required_providers` block", m.ProviderName), content.Blocks[0].DefRange) } return nil }