122 lines
3.9 KiB
Go
122 lines
3.9 KiB
Go
package configupgrade
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/terraform/tfdiags"
|
|
|
|
hcl2 "github.com/hashicorp/hcl2/hcl"
|
|
hcl2write "github.com/hashicorp/hcl2/hclwrite"
|
|
)
|
|
|
|
// Upgrade takes some input module sources and produces a new ModuleSources
|
|
// that should be equivalent to the input but use the configuration idioms
|
|
// associated with the new configuration loader.
|
|
//
|
|
// The result of this function will probably not be accepted by this function,
|
|
// because it will contain constructs that are known only to the new
|
|
// loader.
|
|
//
|
|
// The result may include additional files that were not present in the
|
|
// input. The result may also include nil entries for filenames that were
|
|
// present in the input, indicating that these files should be deleted.
|
|
// In particular, file renames are represented as a new entry accompanied
|
|
// by a nil entry for the old name.
|
|
//
|
|
// If the returned diagnostics contains errors, the caller should not write
|
|
// the resulting sources to disk since they will probably be incomplete. If
|
|
// only warnings are present then the files may be written to disk. Most
|
|
// warnings are also represented as "TF-UPGRADE-TODO:" comments in the
|
|
// generated source files so that users can visit them all and decide what to
|
|
// do with them.
|
|
func (u *Upgrader) Upgrade(input ModuleSources) (ModuleSources, tfdiags.Diagnostics) {
|
|
ret := make(ModuleSources)
|
|
var diags tfdiags.Diagnostics
|
|
|
|
an, err := u.analyze(input)
|
|
if err != nil {
|
|
diags = diags.Append(err)
|
|
return ret, diags
|
|
}
|
|
|
|
for name, src := range input {
|
|
ext := fileExt(name)
|
|
if ext == "" {
|
|
// This should never happen because we ignore files that don't
|
|
// have our conventional extensions during LoadModule, but we'll
|
|
// silently pass through such files assuming that the caller
|
|
// has been tampering with the sources map somehow.
|
|
ret[name] = src
|
|
continue
|
|
}
|
|
|
|
isJSON := (ext == ".tf.json")
|
|
|
|
// The legacy loader allowed JSON syntax inside files named just .tf,
|
|
// so we'll detect that case and rename them here so that the new
|
|
// loader will accept the JSON. However, JSON files are usually
|
|
// generated so we'll also generate a warning to the user to update
|
|
// whatever program generated the file to use the new name.
|
|
if !isJSON {
|
|
trimSrc := bytes.TrimSpace(src)
|
|
if len(trimSrc) > 0 && (trimSrc[0] == '{' || trimSrc[0] == '[') {
|
|
isJSON = true
|
|
|
|
// Rename in the output
|
|
ret[name] = nil // mark for deletion
|
|
oldName := name
|
|
name = input.UnusedFilename(name + ".json")
|
|
ret[name] = src
|
|
|
|
diags = diags.Append(&hcl2.Diagnostic{
|
|
Severity: hcl2.DiagWarning,
|
|
Summary: "JSON configuration file was renamed",
|
|
Detail: fmt.Sprintf(
|
|
"The file %q appears to be in JSON format, so it was renamed to %q. If this file is generated by another program, that program must be updated to use this new name.",
|
|
oldName, name,
|
|
),
|
|
})
|
|
continue
|
|
}
|
|
}
|
|
|
|
if isJSON {
|
|
// We don't do any automatic rewriting for JSON files, since they
|
|
// are usually generated and thus it's the generating program that
|
|
// needs to be updated, rather than its output.
|
|
diags = diags.Append(&hcl2.Diagnostic{
|
|
Severity: hcl2.DiagWarning,
|
|
Summary: "JSON configuration file was not rewritten",
|
|
Detail: fmt.Sprintf(
|
|
"The JSON configuration file %q was skipped, because JSON files are assumed to be generated. The program that generated this file may need to be updated for changes to the configuration language.",
|
|
name,
|
|
),
|
|
})
|
|
continue
|
|
}
|
|
|
|
// TODO: Actually rewrite this .tf file.
|
|
result, fileDiags := u.upgradeNativeSyntaxFile(name, src, an)
|
|
diags = diags.Append(fileDiags)
|
|
if fileDiags.HasErrors() {
|
|
// Leave unchanged, then.
|
|
ret[name] = src
|
|
continue
|
|
}
|
|
|
|
ret[name] = hcl2write.Format(result.Content)
|
|
}
|
|
|
|
versionsName := ret.UnusedFilename("versions.tf")
|
|
ret[versionsName] = []byte(newVersionConstraint)
|
|
|
|
return ret, diags
|
|
}
|
|
|
|
const newVersionConstraint = `
|
|
terraform {
|
|
required_version = ">= 0.12"
|
|
}
|
|
`
|