command: Change 0.13upgrade default to versions.tf

Instead of using providers.tf as the default output file for the
upgrader, we now default to versions.tf. This means that if the
configuration has no `required_providers` blocks at all, or has
multiple, the provider version requirements will be stored in the
versions.tf file.

We now also update the versions.tf file to set a `required_version`
attribute in the first `terraform` block, with value ">= 0.13". This
is similar to the behaviour of the 0.12upgrade command, and signals that
the configuration should not be used with older versions of Terraform.
This commit is contained in:
Alisdair McDiarmid 2020-05-07 15:40:45 -04:00
parent 01a3376ead
commit a740b739e0
21 changed files with 135 additions and 10 deletions

View File

@ -247,8 +247,9 @@ func (c *ZeroThirteenUpgradeCommand) Run(args []string) int {
return 1
}
// Default output filename is "providers.tf"
filename := path.Join(dir, "providers.tf")
// Default output filename is "versions.tf", which is also where the
// 0.12upgrade command added the required_version constraint.
filename := path.Join(dir, "versions.tf")
// Special case: if we only have one file with a required providers
// block, output to that file instead.
@ -322,14 +323,22 @@ func (c *ZeroThirteenUpgradeCommand) Run(args []string) int {
var first *hclwrite.Block
var rest []*hclwrite.Block
// First terraform block in the first file. Declared at this scope so
// that it can be used to write the version constraint later, if this
// is the "versions.tf" file.
var tfBlock *hclwrite.Block
if len(requiredProviderBlocks) > 0 {
// If we already have one or more required provider blocks, we'll rewrite
// the first one, and remove the rest.
first, rest = requiredProviderBlocks[0], requiredProviderBlocks[1:]
// Set the terraform block here for later use to update the
// required version constraint.
tfBlock = parentBlocks[first]
} else {
// Otherwise, find or a create a terraform block, and add a new
// empty required providers block to it.
var tfBlock *hclwrite.Block
for _, rootBlock := range root.Blocks() {
if rootBlock.Type() == "terraform" {
tfBlock = rootBlock
@ -404,6 +413,14 @@ func (c *ZeroThirteenUpgradeCommand) Run(args []string) int {
}
}
// If this is the "versions.tf" file, add a new version constraint to
// the first terraform block. If this isn't the "versions.tf" file,
// we'll update that file separately.
versionsFilename := path.Join(dir, "versions.tf")
if filename == versionsFilename {
tfBlock.Body().SetAttributeValue("required_version", cty.StringVal(">= 0.13"))
}
// Remove the rest of the blocks (and the parent block, if it's empty)
for _, rpBlock := range rest {
tfBlock := parentBlocks[rpBlock]
@ -440,6 +457,84 @@ func (c *ZeroThirteenUpgradeCommand) Run(args []string) int {
return 1
}
// If the file we just updated was not a "versions.tf" file, add or
// update that file to set the required version constraint in the first
// terraform block.
if filename != versionsFilename {
var file *hclwrite.File
// If the versions file doesn't exist, just create a new empty file
if _, err := os.Stat(versionsFilename); os.IsNotExist(err) {
file = hclwrite.NewEmptyFile()
} else if err != nil {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Unable to read configuration file",
fmt.Sprintf("Error when reading configuration file %q: %s", versionsFilename, err),
))
c.showDiagnostics(diags)
return 1
} else {
// Versions file already exists, so load and parse it
config, err := ioutil.ReadFile(versionsFilename)
if err != nil {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Unable to read configuration file",
fmt.Sprintf("Error when reading configuration file %q: %s", versionsFilename, err),
))
c.showDiagnostics(diags)
return 1
}
var parseDiags hcl.Diagnostics
file, parseDiags = hclwrite.ParseConfig(config, filename, hcl.InitialPos)
diags = diags.Append(parseDiags)
}
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
// Find or create a terraform block
root := file.Body()
var tfBlock *hclwrite.Block
for _, rootBlock := range root.Blocks() {
if rootBlock.Type() == "terraform" {
tfBlock = rootBlock
break
}
}
if tfBlock == nil {
tfBlock = root.AppendNewBlock("terraform", nil)
}
// Set the required version attribute
tfBlock.Body().SetAttributeValue("required_version", cty.StringVal(">= 0.13"))
// Write the config back to the file
f, err := os.OpenFile(versionsFilename, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Unable to open configuration file for writing",
fmt.Sprintf("Error when reading configuration file %q: %s", filename, err),
))
c.showDiagnostics(diags)
return 1
}
_, err = file.WriteTo(f)
if err != nil {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Unable to rewrite configuration file",
fmt.Sprintf("Error when rewriting configuration file %q: %s", filename, err),
))
c.showDiagnostics(diags)
return 1
}
}
// After successfully writing the new configuration, remove all other
// required provider blocks from remaining configuration files.
for _, path := range rewritePaths {
@ -600,8 +695,8 @@ func (c *ZeroThirteenUpgradeCommand) Help() string {
helpText := `
Usage: terraform 0.13upgrade [module-dir]
Generates a "providers.tf" configuration file which includes source
configuration for every non-default provider.
Updates module configuration files to add provider source attributes and
merge multiple required_providers blocks into one.
`
return strings.TrimSpace(helpText)
}

View File

@ -93,7 +93,7 @@ func TestZeroThirteenUpgrade_success(t *testing.T) {
"preserves comments": "013upgrade-preserves-comments",
"multiple blocks": "013upgrade-multiple-blocks",
"multiple files": "013upgrade-multiple-files",
"existing providers.tf": "013upgrade-existing-providers-tf",
"existing versions.tf": "013upgrade-existing-versions-tf",
"skipped files": "013upgrade-skipped-files",
}
for name, testPath := range testCases {

View File

@ -1,3 +0,0 @@
# This is a file called providers.tf which does not originally have a
# required_providers block.
resource foo_resource a {}

View File

@ -1,7 +1,9 @@
# This is a file called providers.tf which does not originally have a
# This is a file called versions.tf which does not originally have a
# required_providers block.
resource foo_resource a {}
terraform {
required_version = ">= 0.13"
required_providers {
bar = {
source = "hashicorp/bar"

View File

@ -0,0 +1,7 @@
# This is a file called versions.tf which does not originally have a
# required_providers block.
resource foo_resource a {}
terraform {
required_version = ">= 0.12"
}

View File

@ -0,0 +1,3 @@
terraform {
required_version = ">= 0.13"
}

View File

@ -9,4 +9,5 @@ terraform {
source = "hashicorp/foo"
}
}
required_version = ">= 0.13"
}

View File

@ -13,4 +13,5 @@ terraform {
# https://www.terraform.io/docs/configuration/providers.html#provider-source
}
}
required_version = ">= 0.13"
}

View File

@ -10,4 +10,5 @@ terraform {
source = "hashicorp/foo"
}
}
required_version = ">= 0.13"
}

View File

@ -12,4 +12,5 @@ terraform {
version = "0.5"
}
}
required_version = ">= 0.13"
}

View File

@ -13,4 +13,5 @@ terraform {
version = "1.0.0"
}
}
required_version = ">= 0.13"
}

View File

@ -0,0 +1,3 @@
terraform {
required_version = ">= 0.13"
}

View File

@ -0,0 +1,3 @@
terraform {
required_version = ">= 0.13"
}

View File

@ -0,0 +1,3 @@
terraform {
required_version = ">= 0.13"
}

View File

@ -0,0 +1,3 @@
terraform {
required_version = ">= 0.13"
}

View File

@ -4,4 +4,5 @@ terraform {
source = "hashicorp/foo"
}
}
required_version = ">= 0.13"
}

View File

@ -0,0 +1,3 @@
terraform {
required_version = ">= 0.13"
}