github.com/terraform-linters/tflint-plugin-sdk@v0.22.0/tflint/interface.go (about) 1 package tflint 2 3 import ( 4 "github.com/hashicorp/hcl/v2" 5 "github.com/terraform-linters/tflint-plugin-sdk/hclext" 6 "github.com/terraform-linters/tflint-plugin-sdk/terraform/addrs" 7 "github.com/zclconf/go-cty/cty" 8 ) 9 10 // RuleSet is a list of rules that a plugin should provide. 11 // Normally, plugins can use BuiltinRuleSet directly, 12 // but you can also use custom rulesets that satisfy this interface. 13 // The actual implementation can be found in plugin/host2plugin.GRPCServer. 14 type RuleSet interface { 15 // RuleSetName is the name of the ruleset. This method is not expected to be overridden. 16 RuleSetName() string 17 18 // RuleSetVersion is the version of the plugin. This method is not expected to be overridden. 19 RuleSetVersion() string 20 21 // RuleNames is a list of rule names provided by the plugin. This method is not expected to be overridden. 22 RuleNames() []string 23 24 // VersionConstraint declares the version of TFLint the plugin will work with. Default is no constraint. 25 VersionConstraint() string 26 27 // ConfigSchema returns the ruleset plugin config schema. 28 // If you return a schema, TFLint will extract the config from .tflint.hcl based on that schema 29 // and pass it to ApplyConfig. This schema should be a schema inside of "plugin" block. 30 // If you don't need a config that controls the entire plugin, you don't need to override this method. 31 // 32 // It is recommended to use hclext.ImpliedBodySchema to generate the schema from the structure: 33 // 34 // ``` 35 // type myPluginConfig struct { 36 // Style string `hclext:"style"` 37 // Description string `hclext:"description,optional"` 38 // Detail Detail `hclext:"detail,block"` 39 // } 40 // 41 // config := &myPluginConfig{} 42 // hclext.ImpliedBodySchema(config) 43 // ``` 44 ConfigSchema() *hclext.BodySchema 45 46 // ApplyGlobalConfig applies the common config to the ruleset. 47 // This is not supposed to be overridden from custom rulesets. 48 // Override the ApplyConfig if you want to apply the plugin's custom configuration. 49 ApplyGlobalConfig(*Config) error 50 51 // ApplyConfig applies the configuration to the ruleset. 52 // Custom rulesets can override this method to reflect the plugin's custom configuration. 53 // 54 // You can reflect the body in the structure by using hclext.DecodeBody: 55 // 56 // ``` 57 // type myPluginConfig struct { 58 // Style string `hclext:"style"` 59 // Description string `hclext:"description,optional"` 60 // Detail Detail `hclext:"detail,block"` 61 // } 62 // 63 // config := &myPluginConfig{} 64 // hclext.DecodeBody(body, nil, config) 65 // ``` 66 ApplyConfig(*hclext.BodyContent) error 67 68 // NewRunner returns a new runner based on the original runner. 69 // Custom rulesets can override this method to inject a custom runner. 70 NewRunner(Runner) (Runner, error) 71 72 // BuiltinImpl returns the receiver itself as BuiltinRuleSet. 73 // This is not supposed to be overridden from custom rulesets. 74 BuiltinImpl() *BuiltinRuleSet 75 76 // All Ruleset must embed the builtin ruleset. 77 mustEmbedBuiltinRuleSet() 78 } 79 80 // Runner acts as a client for each plugin to query the host process about the Terraform configurations. 81 // The actual implementation can be found in plugin/plugin2host.GRPCClient. 82 type Runner interface { 83 // GetOriginalwd returns the original working directory. 84 // Normally this is equal to os.Getwd(), but differs if --chdir or --recursive is used. 85 // If you need the absolute path of the file, joining with the original working directory is appropriate. 86 GetOriginalwd() (string, error) 87 88 // GetModulePath returns the current module path address. 89 GetModulePath() (addrs.Module, error) 90 91 // GetResourceContent retrieves the content of resources based on the passed schema. 92 // The schema allows you to specify attributes and blocks that describe the structure needed for the inspection: 93 // 94 // ``` 95 // runner.GetResourceContent("aws_instance", &hclext.BodySchema{ 96 // Attributes: []hclext.AttributeSchema{{Name: "instance_type"}}, 97 // Blocks: []hclext.BlockSchema{ 98 // { 99 // Type: "ebs_block_device", 100 // Body: &hclext.BodySchema{Attributes: []hclext.AttributeSchema{{Name: "volume_size"}}}, 101 // }, 102 // }, 103 // }, nil) 104 // ``` 105 GetResourceContent(resourceName string, schema *hclext.BodySchema, option *GetModuleContentOption) (*hclext.BodyContent, error) 106 107 // GetProviderContent retrieves the content of providers based on the passed schema. 108 // This method is GetResourceContent for providers. 109 GetProviderContent(providerName string, schema *hclext.BodySchema, option *GetModuleContentOption) (*hclext.BodyContent, error) 110 111 // GetModuleContent retrieves the content of the module based on the passed schema. 112 // GetResourceContent/GetProviderContent are syntactic sugar for GetModuleContent, which you can use to access other structures. 113 GetModuleContent(schema *hclext.BodySchema, option *GetModuleContentOption) (*hclext.BodyContent, error) 114 115 // GetFile returns the hcl.File object. 116 // This is low level API for accessing information such as comments and syntax. 117 // When accessing resources, expressions, etc, it is recommended to use high-level APIs. 118 GetFile(filename string) (*hcl.File, error) 119 120 // GetFiles returns a map[string]hcl.File object, where the key is the file name. 121 // This is low level API for accessing information such as comments and syntax. 122 GetFiles() (map[string]*hcl.File, error) 123 124 // WalkExpressions traverses expressions in all files by the passed walker. 125 // The walker can be passed any structure that satisfies the `tflint.ExprWalker` 126 // interface, or a `tflint.ExprWalkFunc`. Example of passing function: 127 // 128 // ``` 129 // runner.WalkExpressions(tflint.ExprWalkFunc(func (expr hcl.Expression) hcl.Diagnostics { 130 // // Write code here 131 // })) 132 // ``` 133 // 134 // If you pass ExprWalkFunc, the function will be called for every expression. 135 // Note that it behaves differently in native HCL syntax and JSON syntax. 136 // 137 // In the HCL syntax, `var.foo` and `var.bar` in `[var.foo, var.bar]` are 138 // also passed to the walker. In other words, it traverses expressions recursively. 139 // To avoid redundant checks, the walker should check the kind of expression. 140 // 141 // In the JSON syntax, only an expression of an attribute seen from the top 142 // level of the file is passed. In other words, it doesn't traverse expressions 143 // recursively. This is a limitation of JSON syntax. 144 WalkExpressions(walker ExprWalker) hcl.Diagnostics 145 146 // DecodeRuleConfig fetches the rule's configuration and reflects the result in the 2nd argument. 147 // The argument is expected to be a pointer to a structure tagged with hclext: 148 // 149 // ``` 150 // type myRuleConfig struct { 151 // Style string `hclext:"style"` 152 // Description string `hclext:"description,optional"` 153 // Detail Detail `hclext:"detail,block"` 154 // } 155 // 156 // config := &myRuleConfig{} 157 // runner.DecodeRuleConfig("my_rule", config) 158 // ``` 159 // 160 // See the hclext.DecodeBody documentation and examples for more details. 161 DecodeRuleConfig(ruleName string, ret interface{}) error 162 163 // EvaluateExpr evaluates an expression and assigns its value to a Go value target, 164 // which must be a pointer or a function. Any other type of target will trigger a panic. 165 // 166 // For pointers, if the expression value cannot be assigned to the target, an error is returned. 167 // Some examples of this include unknown values (like variables without defaults or 168 // aws_instance.foo.arn), null values, and sensitive values (for variables with sensitive = true). 169 // 170 // These errors be handled with errors.Is(): 171 // 172 // ``` 173 // var val string 174 // err := runner.EvaluateExpr(expr, &val, nil) 175 // if err != nil { 176 // if errors.Is(err, tflint.ErrUnknownValue) { 177 // // Ignore unknown values 178 // return nil 179 // } 180 // if errors.Is(err, tflint.ErrNullValue) { 181 // // Ignore null values because null means that the value is not set 182 // return nil 183 // } 184 // if errors.Is(err, tflint.ErrSensitive) { 185 // // Ignore sensitive values 186 // return nil 187 // } 188 // return err 189 // } 190 // ``` 191 // 192 // However, if the target is cty.Value, these errors will not be returned. 193 // 194 // Here are the types that can be passed as the target: string, int, bool, []string, 195 // []int, []bool, map[string]string, map[string]int, map[string]bool, and cty.Value. 196 // Passing any other type will result in a panic, but you can make an exception by 197 // passing wantType as an option. 198 // 199 // ``` 200 // type complexVal struct { 201 // Key string `cty:"key"` 202 // Enabled bool `cty:"enabled"` 203 // } 204 // 205 // wantType := cty.List(cty.Object(map[string]cty.Type{ 206 // "key": cty.String, 207 // "enabled": cty.Bool, 208 // })) 209 // 210 // var complexVals []complexVal 211 // runner.EvaluateExpr(expr, &compleVals, &tflint.EvaluateExprOption{WantType: &wantType}) 212 // ``` 213 // 214 // For functions (callbacks), the assigned value is used as an argument to execute 215 // the function. If a value cannot be assigned to the argument type, the execution 216 // is skipped instead of returning an error. This is useful when it's always acceptable 217 // to ignore exceptional values. 218 // 219 // Here's an example of how you can pass a function to EvaluateExpr: 220 // 221 // ``` 222 // runner.EvaluateExpr(expr, func (val string) error { 223 // // Test value 224 // }, nil) 225 // ``` 226 EvaluateExpr(expr hcl.Expression, target interface{}, option *EvaluateExprOption) error 227 228 // EmitIssue sends an issue to TFLint. You need to pass the message of the issue and the range. 229 EmitIssue(rule Rule, message string, issueRange hcl.Range) error 230 231 // EmitIssueWithFix is similar to EmitIssue, but it also supports autofix. 232 // If you pass a function that rewrites the source code to the last argument, 233 // TFLint will apply the fix when the --fix option is specified. 234 // 235 // The function is passed a tflint.Fixer that can be used to rewrite the source code. 236 // See the tflint.Fixer interface for more details. 237 // 238 // Issues emitted using this function are automatically marked as fixable. 239 // However, if you don't want to support autofix only under certain conditions (e.g. JSON syntax), 240 // you can return tflint.ErrFixNotSupported from the fix function. 241 // In this case, the issue will not be marked as fixable and the fix will not be applied. 242 // 243 // As a best practice for autofix, we recommend minimizing the amount of code changed at once. 244 // If fixes for the same range conflict within the same rule, Fixer will return an error. 245 EmitIssueWithFix(rule Rule, message string, issueRange hcl.Range, fixFunc func(f Fixer) error) error 246 247 // EnsureNoError is a helper for error handling. Depending on the type of error generated by EvaluateExpr, 248 // determine whether to exit, skip, or continue. If it is continued, the passed function will be executed. 249 // 250 // Deprecated: Use EvaluateExpr with a function callback. e.g. EvaluateExpr(expr, func (val T) error {}, ...) 251 EnsureNoError(error, func() error) error 252 } 253 254 // Fixer is a tool to rewrite HCL source code. 255 // The actual implementation is in the internal.Fixer. 256 type Fixer interface { 257 // ReplaceText rewrites the given range of source code to a new text. 258 // If the range is overlapped with a previous rewrite range, it returns an error. 259 // 260 // Either string or tflint.TextNode is valid as an argument. 261 // TextNode can be obtained with fixer.TextAt(range). 262 // If the argument is a TextNode, and the range is contained in the replacement range, 263 // this function automatically minimizes the replacement range as much as possible. 264 // 265 // For example, if the source code is "(foo)", ReplaceText(range, "[foo]") 266 // rewrites the whole "(foo)". But ReplaceText(range, "[", TextAt(fooRange), "]") 267 // rewrites only "(" and ")". This is useful to avoid unintended conflicts. 268 ReplaceText(hcl.Range, ...any) error 269 270 // InsertTextBefore inserts the given text before the given range. 271 InsertTextBefore(hcl.Range, string) error 272 273 // InsertTextAfter inserts the given text after the given range. 274 InsertTextAfter(hcl.Range, string) error 275 276 // Remove removes the given range of source code. 277 Remove(hcl.Range) error 278 279 // RemoveAttribute removes the given attribute from the source code. 280 // The difference from Remove is that it removes the attribute 281 // and the associated newlines, indentations, and comments. 282 // This only works for HCL native syntax. JSON syntax is not supported 283 // and returns tflint.ErrFixNotSupported. 284 RemoveAttribute(*hcl.Attribute) error 285 286 // RemoveBlock removes the given block from the source code. 287 // The difference from Remove is that it removes the block 288 // and the associated newlines, indentations, and comments. 289 // This only works for HCL native syntax. JSON syntax is not supported 290 // and returns tflint.ErrFixNotSupported. 291 RemoveBlock(*hcl.Block) error 292 293 // RemoveExtBlock removes the given block from the source code. 294 // This is similar to RemoveBlock, but it works for hclext.Block. 295 RemoveExtBlock(*hclext.Block) error 296 297 // TextAt returns a text node at the given range. 298 // This is expected to be passed as an argument to ReplaceText. 299 // Note this doesn't take into account the changes made by the fixer in a rule. 300 TextAt(hcl.Range) TextNode 301 302 // ValueText returns a text representation of the given cty.Value. 303 // Values are always converted to a single line. For more pretty-printing, 304 // implement your own conversion function. 305 // 306 // This function is inspired by hclwrite.TokensForValue. 307 // https://github.com/hashicorp/hcl/blob/v2.16.2/hclwrite/generate.go#L26 308 ValueText(cty.Value) string 309 310 // RangeTo returns a range from the given start position to the given text. 311 // Note that it doesn't check if the text is actually in the range. 312 RangeTo(to string, filename string, start hcl.Pos) hcl.Range 313 } 314 315 // Rule is the interface that the plugin's rules should satisfy. 316 type Rule interface { 317 // Name will be displayed with a message of an issue and will be the identifier used to control 318 // the behavior of this rule in the configuration file etc. 319 // Therefore, it is expected that this will not duplicate the rule names provided by other plugins. 320 Name() string 321 322 // Enabled indicates whether the rule is enabled by default. 323 Enabled() bool 324 325 // Severity indicates the severity of the rule. 326 Severity() Severity 327 328 // Link allows you to add a reference link to the rule. 329 Link() string 330 331 // Metadata allows you to set any metadata to the rule. 332 // This value is never referenced by the SDK and can be used for your custom ruleset. 333 Metadata() interface{} 334 335 // Check is the entrypoint of the rule. You can fetch Terraform configurations and send issues via Runner. 336 Check(Runner) error 337 338 // All rules must embed the default rule. 339 mustEmbedDefaultRule() 340 }