Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add range to terraform_required_version errors #178

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

rdimartino
Copy link
Contributor

Fixes #177

I wondered about sorting the blocks / filenames or looking for main.tf, but decided to keep it simple.

A couple questions came up for me:

  • Is it ok to use the first block? Should we sort by filename or line number or something?

  • Pulling an arbitrary filename from the map of files doesn't feel great. It seems weird for the error to suggest placing the required_version in variables.tf for instance. Maybe we should just show the directory if no "terraform" blocks are found? We could parse that out of the filename.

Let me know what you think.

Thanks!

Comment on lines 89 to 116
} else {
// If there are no "terraform" blocks, create a hcl.Range for a file

// Grab a single filename from keys of files map
var filename string
for k := range files {
filename = k
break
}

missingRange = hcl.Range{
Filename: filename,
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say definitely skip this else logic. Picking the first block is good but a random file is not.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, in my case at least, completely skipping this logic wouldn't solve the issue I ran into #177. I had several local modules and none of them had a "terraform" block. When I ran tflint for the first time I got a bunch of

Warning: terraform "required_version" attribute is required (terraform_required_version)

  on  line 0:
   (source code not available)

And was very confused about where these were coming from.

I agree that a random file is not ideal. What about pointing to the directory?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In theory, Filename should be a filename. If you can show a directory contributes useful output and doesn't break things that sounds fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, I've updated the else logic. In the case where files are missing a "terraform" block, it now references either the single file in the module or it points to the directory if there are multiple files. The test cases "no terraform block" and "multiple files no terraform blocks" cover these scenarios. Without the else logic, both of those cases would report the confusing range: on line 0:.

In my local example that lead to the creation of the issue, the output now looks like:

❯ tflint --recursive --only terraform_required_version
2 issue(s) found:

Warning: terraform "required_version" attribute is required (terraform_required_version)

  on modules/deployment/ line 0:
   (source code not available)

Reference: https://github.com/terraform-linters/tflint-ruleset-terraform/blob/v0.7.0/docs/rules/terraform_required_version.md

Warning: terraform "required_version" attribute is required (terraform_required_version)

  on modules/dns/ line 0:
   (source code not available)

Reference: https://github.com/terraform-linters/tflint-ruleset-terraform/blob/v0.7.0/docs/rules/terraform_required_version.md

Warning: terraform "required_version" attribute is required (terraform_required_version)

  on modules/email/ line 0:
   (source code not available)

Reference: https://github.com/terraform-linters/tflint-ruleset-terraform/blob/v0.7.0/docs/rules/terraform_required_version.md

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned in terraform-linters/tflint#1790 (comment), I favor a random file over passing directory path as Filename.
I agree that this is not ideal, but I think it's a reasonable choice considering the integration with other tools.

However, we should always attach the same range so that it does not change each time you run it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe main.tf if it's there otherwise preserve the current behavior. Encouraging a random file, which will need to be alphabetically first to be deterministic, is unpredictable and awkward.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An arbitrary file may be awkward, sure, but IMHO certainly better than providing no context for the source of the issue at all.

In my case pointing to main.tf would've resolved the issue in #177, so I'm happy to do that, but want to share my 2 cents.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last idea is to report on a main.tf file, whether or not it's there. The source code won't be there but you'll get a range printed with (source unavailable) or similar.

certainly better than providing no context

The other side of this coin is that static analysis tools have a duty to not provide wrong advice. False detection positives will happen but the remediation advice should be clear/specific. Picking an arbitrary file and telling the user "put this config there" seems to risk giving bad advice (more specific than the context allows).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Picking an arbitrary file and telling the user "put this config there" seems to risk giving bad advice (more specific than the context allows).

Agreed with this concern. My proposed policy doesn't allow for the issue to be "create a new file and write to it."

From the perspective of avoiding false positives, maybe there is a way to not report an issue if the "terraform" block is missing. However, I'm not sure if this is the behavior that users want.

On the other hand, any potential misleading behavior may be mitigated by other rules such as terraform_standard_module_structure.

Last idea is to report on a main.tf file, whether or not it's there. The source code won't be there but you'll get a range printed with (source unavailable) or similar.

I've thought about this idea too, but ultimately it brings up issues that aren't well-handled in LSP or Problem Matchers, so I'm not positive about it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As an aside, instead of main.tf, maybe you could follow the official guidelines and recommend terraform.tf instead.
https://developer.hashicorp.com/terraform/language/style#file-names

@rdimartino rdimartino force-pushed the rd/terraform_required_version branch from 5bc9e8f to 5874361 Compare May 21, 2024 17:03
// If there are no "terraform" blocks, create a hcl.Range for the files

// Find the directory common to all files
var commonPath []string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per L49 this rule only runs in root modules. So it should be sufficient to just take the module path and not do all this work around inferring from the list of files, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds reasonable to me, but I'm admittedly new to terraform and definitely new to this code base, so I'm not sure what assumptions are safe to make.

It was also unclear to me a direct way to get the module path. runner.Ctx.ModulePath looked promising, but I wasn't sure how to handle a addrs.ModuleInstance.

If it's straight forward to do then, yeah, I think this could be simplified a lot too to something like:

...
} else {
    if len(files) == 1 {
        for filename := range files {
            missingRange = hcl.Range{ Filename: filename }
        }
    } else {
        missingRange = hcl.Range{ Filename: < module path > }
    }
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I guess there's no official interface for getting the path on disk, just the path in Terraform (module.*). That said, each runner should only operate in a single module (directory). So I think it should be enough to just do filepath.Dir(files[0])?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds great to me. I've updated it, so I pull a single file from the map. If it's the only file, use it for the hcl.Range otherwise if there are multiple files, use filepath.Dir to reference the directory of the module.

@rdimartino rdimartino force-pushed the rd/terraform_required_version branch from 0e4dc91 to ba18d88 Compare May 23, 2024 23:38
@rdimartino rdimartino force-pushed the rd/terraform_required_version branch from ba18d88 to 1d00e6d Compare June 2, 2024 01:05
@rdimartino
Copy link
Contributor Author

@bendrucker @wata727 Please take a look. I tried to incorporate the feedback you both provided

moduleDirectory := filepath.Dir(file)

// If there are multiple files, look for terraform.tf or main.tf (in that order)
for _, basename := range []string{"terraform.tf", "main.tf"} {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The result of this may be non-deterministic because the order of the loops is undefined.
https://go.dev/doc/go1#iteration

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a slice, not a map, so the order will be deterministic.

if len(files) == 1 {
return r.emitIssue(hcl.Range{
Filename: file,
Start: hcl.InitialPos,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may need to specify the End as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should that also be hcl.InitialPos?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

No source specified for terraform_required_version warning in a local module
3 participants