105 lines
3.7 KiB
Go
105 lines
3.7 KiB
Go
|
package configupgrade
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"fmt"
|
||
|
|
||
|
"github.com/hashicorp/terraform/tfdiags"
|
||
|
|
||
|
hcl2 "github.com/hashicorp/hcl2/hcl"
|
||
|
)
|
||
|
|
||
|
// 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 Upgrade(input ModuleSources) (ModuleSources, tfdiags.Diagnostics) {
|
||
|
ret := make(ModuleSources)
|
||
|
var diags tfdiags.Diagnostics
|
||
|
|
||
|
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")
|
||
|
|
||
|
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,
|
||
|
),
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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.
|
||
|
ret[name] = src
|
||
|
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.
|
||
|
ret[name] = src
|
||
|
}
|
||
|
|
||
|
// Generate our versions.tf file that both records the fact that we now
|
||
|
// require Terraform Core 0.12 and gathers all of the provider version
|
||
|
// requirements that might previously have been scattered in various
|
||
|
// "provider" blocks elsewhere.
|
||
|
versionsName := ret.UnusedFilename("versions.tf")
|
||
|
// TODO: Actually populate this.
|
||
|
ret[versionsName] = make([]byte, 0)
|
||
|
|
||
|
return ret, diags
|
||
|
}
|