github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/configs/configupgrade/upgrade.go (about)

     1  package configupgrade
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  
     7  	"github.com/hashicorp/terraform/tfdiags"
     8  
     9  	hcl2 "github.com/hashicorp/hcl/v2"
    10  	hcl2write "github.com/hashicorp/hcl/v2/hclwrite"
    11  )
    12  
    13  // Upgrade takes some input module sources and produces a new ModuleSources
    14  // that should be equivalent to the input but use the configuration idioms
    15  // associated with the new configuration loader.
    16  //
    17  // The result of this function will probably not be accepted by this function,
    18  // because it will contain constructs that are known only to the new
    19  // loader.
    20  //
    21  // The result may include additional files that were not present in the
    22  // input. The result may also include nil entries for filenames that were
    23  // present in the input, indicating that these files should be deleted.
    24  // In particular, file renames are represented as a new entry accompanied
    25  // by a nil entry for the old name.
    26  //
    27  // If the returned diagnostics contains errors, the caller should not write
    28  // the resulting sources to disk since they will probably be incomplete. If
    29  // only warnings are present then the files may be written to disk. Most
    30  // warnings are also represented as "TF-UPGRADE-TODO:" comments in the
    31  // generated source files so that users can visit them all and decide what to
    32  // do with them.
    33  func (u *Upgrader) Upgrade(input ModuleSources, dir string) (ModuleSources, tfdiags.Diagnostics) {
    34  	ret := make(ModuleSources)
    35  	var diags tfdiags.Diagnostics
    36  
    37  	an, err := u.analyze(input)
    38  	if err != nil {
    39  		diags = diags.Append(err)
    40  		return ret, diags
    41  	}
    42  	an.ModuleDir = dir
    43  
    44  	for name, src := range input {
    45  		ext := fileExt(name)
    46  		if ext == "" {
    47  			// This should never happen because we ignore files that don't
    48  			// have our conventional extensions during LoadModule, but we'll
    49  			// silently pass through such files assuming that the caller
    50  			// has been tampering with the sources map somehow.
    51  			ret[name] = src
    52  			continue
    53  		}
    54  
    55  		isJSON := (ext == ".tf.json")
    56  
    57  		// The legacy loader allowed JSON syntax inside files named just .tf,
    58  		// so we'll detect that case and rename them here so that the new
    59  		// loader will accept the JSON. However, JSON files are usually
    60  		// generated so we'll also generate a warning to the user to update
    61  		// whatever program generated the file to use the new name.
    62  		if !isJSON {
    63  			trimSrc := bytes.TrimSpace(src)
    64  			if len(trimSrc) > 0 && (trimSrc[0] == '{' || trimSrc[0] == '[') {
    65  				isJSON = true
    66  
    67  				// Rename in the output
    68  				ret[name] = nil // mark for deletion
    69  				oldName := name
    70  				name = input.UnusedFilename(name + ".json")
    71  				ret[name] = src
    72  
    73  				diags = diags.Append(&hcl2.Diagnostic{
    74  					Severity: hcl2.DiagWarning,
    75  					Summary:  "JSON configuration file was renamed",
    76  					Detail: fmt.Sprintf(
    77  						"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.",
    78  						oldName, name,
    79  					),
    80  				})
    81  				continue
    82  			}
    83  		}
    84  
    85  		if isJSON {
    86  			// We don't do any automatic rewriting for JSON files, since they
    87  			// are usually generated and thus it's the generating program that
    88  			// needs to be updated, rather than its output.
    89  			diags = diags.Append(&hcl2.Diagnostic{
    90  				Severity: hcl2.DiagWarning,
    91  				Summary:  "JSON configuration file was not rewritten",
    92  				Detail: fmt.Sprintf(
    93  					"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.",
    94  					name,
    95  				),
    96  			})
    97  			ret[name] = src // unchanged
    98  			continue
    99  		}
   100  
   101  		// TODO: Actually rewrite this .tf file.
   102  		result, fileDiags := u.upgradeNativeSyntaxFile(name, src, an)
   103  		diags = diags.Append(fileDiags)
   104  		if fileDiags.HasErrors() {
   105  			// Leave unchanged, then.
   106  			ret[name] = src
   107  			continue
   108  		}
   109  
   110  		ret[name] = hcl2write.Format(result.Content)
   111  	}
   112  
   113  	versionsName := ret.UnusedFilename("versions.tf")
   114  	ret[versionsName] = []byte(newVersionConstraint)
   115  
   116  	return ret, diags
   117  }
   118  
   119  const newVersionConstraint = `
   120  terraform {
   121    required_version = ">= 0.12"
   122  }
   123  `