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 }