github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/command/meta_dependencies.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package command
     5  
     6  import (
     7  	"log"
     8  	"os"
     9  
    10  	"github.com/terramate-io/tf/depsfile"
    11  	"github.com/terramate-io/tf/tfdiags"
    12  )
    13  
    14  // dependenclyLockFilename is the filename of the dependency lock file.
    15  //
    16  // This file should live in the same directory as the .tf files for the
    17  // root module of the configuration, alongside the .terraform directory
    18  // as long as that directory's path isn't overridden by the TF_DATA_DIR
    19  // environment variable.
    20  //
    21  // We always expect to find this file in the current working directory
    22  // because that should also be the root module directory.
    23  //
    24  // Some commands have legacy command line arguments that make the root module
    25  // directory something other than the root module directory; when using those,
    26  // the lock file will be written in the "wrong" place (the current working
    27  // directory instead of the root module directory) but we do that intentionally
    28  // to match where the ".terraform" directory would also be written in that
    29  // case. Eventually we will phase out those legacy arguments in favor of the
    30  // global -chdir=... option, which _does_ preserve the intended invariant
    31  // that the root module directory is always the current working directory.
    32  const dependencyLockFilename = ".terraform.lock.hcl"
    33  
    34  // lockedDependencies reads the dependency lock information from the lock file
    35  // in the current working directory.
    36  //
    37  // If the lock file doesn't exist at the time of the call, lockedDependencies
    38  // indicates success and returns an empty Locks object. If the file does
    39  // exist then the result is either a representation of the contents of that
    40  // file at the instant of the call or error diagnostics explaining some way
    41  // in which the lock file is invalid.
    42  //
    43  // The result is a snapshot of the locked dependencies at the time of the call
    44  // and does not update as a result of calling replaceLockedDependencies
    45  // or any other modification method.
    46  func (m *Meta) lockedDependencies() (*depsfile.Locks, tfdiags.Diagnostics) {
    47  	// We check that the file exists first, because the underlying HCL
    48  	// parser doesn't distinguish that error from other error types
    49  	// in a machine-readable way but we want to treat that as a success
    50  	// with no locks. There is in theory a race condition here in that
    51  	// the file could be created or removed in the meantime, but we're not
    52  	// promising to support two concurrent dependency installation processes.
    53  	_, err := os.Stat(dependencyLockFilename)
    54  	if os.IsNotExist(err) {
    55  		return m.annotateDependencyLocksWithOverrides(depsfile.NewLocks()), nil
    56  	}
    57  
    58  	ret, diags := depsfile.LoadLocksFromFile(dependencyLockFilename)
    59  	return m.annotateDependencyLocksWithOverrides(ret), diags
    60  }
    61  
    62  // replaceLockedDependencies creates or overwrites the lock file in the
    63  // current working directory to contain the information recorded in the given
    64  // locks object.
    65  func (m *Meta) replaceLockedDependencies(new *depsfile.Locks) tfdiags.Diagnostics {
    66  	return depsfile.SaveLocksToFile(new, dependencyLockFilename)
    67  }
    68  
    69  // annotateDependencyLocksWithOverrides modifies the given Locks object in-place
    70  // to track as overridden any provider address that's subject to testing
    71  // overrides, development overrides, or "unmanaged provider" status.
    72  //
    73  // This is just an implementation detail of the lockedDependencies method,
    74  // not intended for use anywhere else.
    75  func (m *Meta) annotateDependencyLocksWithOverrides(ret *depsfile.Locks) *depsfile.Locks {
    76  	if ret == nil {
    77  		return ret
    78  	}
    79  
    80  	for addr := range m.ProviderDevOverrides {
    81  		log.Printf("[DEBUG] Provider %s is overridden by dev_overrides", addr)
    82  		ret.SetProviderOverridden(addr)
    83  	}
    84  	for addr := range m.UnmanagedProviders {
    85  		log.Printf("[DEBUG] Provider %s is overridden as an \"unmanaged provider\"", addr)
    86  		ret.SetProviderOverridden(addr)
    87  	}
    88  	if m.testingOverrides != nil {
    89  		for addr := range m.testingOverrides.Providers {
    90  			log.Printf("[DEBUG] Provider %s is overridden in Meta.testingOverrides", addr)
    91  			ret.SetProviderOverridden(addr)
    92  		}
    93  	}
    94  
    95  	return ret
    96  }