mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
internal/providercache: installFromLocalDir
We previously skipped this one because it wasn't strictly necessary for replicating the old "terraform init" behavior, but we do need it to work so that things like the -plugin-dir option can behave correctly. Linking packages from other cache directories and installing from unpacked directories are fundamentally the same operation because a cache directory is really just a collection of unpacked packages, so here we refactor the LinkFromOtherCache functionality to actually be in installFromLocalDir, and LinkFromOtherCache becomes a wrapper for the installFromLocalDir function that just calculates the source and target directories automatically and invalidates the metaCache.
This commit is contained in:
parent
c81eebe0ac
commit
ff55e1a1cd
@ -4,10 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/internal/copydir"
|
|
||||||
"github.com/hashicorp/terraform/internal/getproviders"
|
"github.com/hashicorp/terraform/internal/getproviders"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -58,71 +55,12 @@ func (d *Dir) LinkFromOtherCache(entry *CachedProvider) error {
|
|||||||
currentPath := entry.PackageDir
|
currentPath := entry.PackageDir
|
||||||
log.Printf("[TRACE] providercache.Dir.LinkFromOtherCache: linking %s v%s from existing cache %s to %s", entry.Provider, entry.Version, currentPath, newPath)
|
log.Printf("[TRACE] providercache.Dir.LinkFromOtherCache: linking %s v%s from existing cache %s to %s", entry.Provider, entry.Version, currentPath, newPath)
|
||||||
|
|
||||||
absNew, err := filepath.Abs(newPath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to make new path %s absolute: %s", newPath, err)
|
|
||||||
}
|
|
||||||
absCurrent, err := filepath.Abs(currentPath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to make existing cache path %s absolute: %s", currentPath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Before we do anything else, we'll do a quick check to make sure that
|
|
||||||
// these two paths are not pointing at the same physical directory on
|
|
||||||
// disk. This compares the files by their OS-level device and directory
|
|
||||||
// entry identifiers, not by their virtual filesystem paths.
|
|
||||||
if same, err := copydir.SameFile(absNew, absCurrent); same {
|
|
||||||
return fmt.Errorf("cannot link existing cache path %s to itself", newPath)
|
|
||||||
} else if err != nil {
|
|
||||||
return fmt.Errorf("failed to determine if %s and %s are the same: %s", currentPath, newPath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invalidate our metaCache so that subsequent read calls will re-scan to
|
// Invalidate our metaCache so that subsequent read calls will re-scan to
|
||||||
// incorporate any changes we make here.
|
// incorporate any changes we make here.
|
||||||
d.metaCache = nil
|
d.metaCache = nil
|
||||||
|
|
||||||
// Delete anything that's already present at this path first.
|
// We re-use the process of installing from a local directory here, because
|
||||||
err = os.RemoveAll(newPath)
|
// the two operations are fundamentally the same: symlink if possible,
|
||||||
if err != nil && !os.IsNotExist(err) {
|
// deep-copy otherwise.
|
||||||
return fmt.Errorf("failed to remove existing %s before linking it to %s: %s", currentPath, newPath, err)
|
return installFromLocalDir(context.TODO(), currentPath, newPath)
|
||||||
}
|
|
||||||
|
|
||||||
// We'll prefer to create a symlink if possible, but we'll fall back to
|
|
||||||
// a recursive copy if symlink creation fails. It could fail for a number
|
|
||||||
// of reasons, including being on Windows 8 without administrator
|
|
||||||
// privileges or being on a legacy filesystem like FAT that has no way
|
|
||||||
// to represent a symlink. (Generalized symlink support for Windows was
|
|
||||||
// introduced in a Windows 10 minor update.)
|
|
||||||
//
|
|
||||||
// We'd prefer to use a relative path for the symlink to reduce the risk
|
|
||||||
// of it being broken by moving things around later, but we'll fall back
|
|
||||||
// on the absolute path we already calculated if that isn't possible
|
|
||||||
// (e.g. because the two paths are on different "volumes" on an OS with
|
|
||||||
// that concept, like Windows with drive letters and UNC host/share names.)
|
|
||||||
linkTarget, err := filepath.Rel(newPath, absCurrent)
|
|
||||||
if err != nil {
|
|
||||||
linkTarget = absCurrent
|
|
||||||
}
|
|
||||||
|
|
||||||
parentDir := filepath.Dir(absNew)
|
|
||||||
err = os.MkdirAll(parentDir, 0755)
|
|
||||||
if err != nil && os.IsExist(err) {
|
|
||||||
return fmt.Errorf("failed to create parent directories leading to %s: %s", newPath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.Symlink(linkTarget, absNew)
|
|
||||||
if err == nil {
|
|
||||||
// Success, then!
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we get down here then symlinking failed and we need a deep copy
|
|
||||||
// instead.
|
|
||||||
err = copydir.CopyDir(absNew, absCurrent)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to either symlink or copy %s to %s: %s", absCurrent, absNew, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we got here then apparently our copy succeeded, so we're done.
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,13 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
getter "github.com/hashicorp/go-getter"
|
getter "github.com/hashicorp/go-getter"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/httpclient"
|
"github.com/hashicorp/terraform/httpclient"
|
||||||
|
"github.com/hashicorp/terraform/internal/copydir"
|
||||||
)
|
)
|
||||||
|
|
||||||
// We borrow the "unpack a zip file into a target directory" logic from
|
// We borrow the "unpack a zip file into a target directory" logic from
|
||||||
@ -68,6 +71,68 @@ func installFromLocalArchive(ctx context.Context, filename string, targetDir str
|
|||||||
return unzip.Decompress(targetDir, filename, true)
|
return unzip.Decompress(targetDir, filename, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// installFromLocalDir is the implementation of both installing a package from
|
||||||
|
// a local directory source _and_ of linking a package from another cache
|
||||||
|
// in LinkFromOtherCache, because they both do fundamentally the same
|
||||||
|
// operation: symlink if possible, or deep-copy otherwise.
|
||||||
func installFromLocalDir(ctx context.Context, sourceDir string, targetDir string) error {
|
func installFromLocalDir(ctx context.Context, sourceDir string, targetDir string) error {
|
||||||
return fmt.Errorf("installFromLocalDir not yet implemented")
|
absNew, err := filepath.Abs(targetDir)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to make target path %s absolute: %s", targetDir, err)
|
||||||
|
}
|
||||||
|
absCurrent, err := filepath.Abs(sourceDir)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to make source path %s absolute: %s", sourceDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Before we do anything else, we'll do a quick check to make sure that
|
||||||
|
// these two paths are not pointing at the same physical directory on
|
||||||
|
// disk. This compares the files by their OS-level device and directory
|
||||||
|
// entry identifiers, not by their virtual filesystem paths.
|
||||||
|
if same, err := copydir.SameFile(absNew, absCurrent); same {
|
||||||
|
return fmt.Errorf("cannot install existing provider directory %s to itself", targetDir)
|
||||||
|
} else if err != nil {
|
||||||
|
return fmt.Errorf("failed to determine if %s and %s are the same: %s", sourceDir, targetDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete anything that's already present at this path first.
|
||||||
|
err = os.RemoveAll(targetDir)
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("failed to remove existing %s before linking it to %s: %s", sourceDir, targetDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll prefer to create a symlink if possible, but we'll fall back to
|
||||||
|
// a recursive copy if symlink creation fails. It could fail for a number
|
||||||
|
// of reasons, including being on Windows 8 without administrator
|
||||||
|
// privileges or being on a legacy filesystem like FAT that has no way
|
||||||
|
// to represent a symlink. (Generalized symlink support for Windows was
|
||||||
|
// introduced in a Windows 10 minor update.)
|
||||||
|
//
|
||||||
|
// We use an absolute path for the symlink to reduce the risk of it being
|
||||||
|
// broken by moving things around later, since the source directory is
|
||||||
|
// likely to be a shared directory independent on any particular target
|
||||||
|
// and thus we can't assume that they will move around together.
|
||||||
|
linkTarget := absCurrent
|
||||||
|
|
||||||
|
parentDir := filepath.Dir(absNew)
|
||||||
|
err = os.MkdirAll(parentDir, 0755)
|
||||||
|
if err != nil && os.IsExist(err) {
|
||||||
|
return fmt.Errorf("failed to create parent directories leading to %s: %s", targetDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.Symlink(linkTarget, absNew)
|
||||||
|
if err == nil {
|
||||||
|
// Success, then!
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get down here then symlinking failed and we need a deep copy
|
||||||
|
// instead.
|
||||||
|
err = copydir.CopyDir(absNew, absCurrent)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to either symlink or copy %s to %s: %s", absCurrent, absNew, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got here then apparently our copy succeeded, so we're done.
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user