Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

internal/getproviders: Allow basedir for local search to be symlink #25692

Merged
merged 1 commit into from
Aug 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions internal/getproviders/filesystem_search.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,25 @@ import (
// directory structure conventions.
func SearchLocalDirectory(baseDir string) (map[addrs.Provider]PackageMetaList, error) {
ret := make(map[addrs.Provider]PackageMetaList)

// We don't support symlinks at intermediate points inside the directory
// heirarchy because that could potentially cause our walk to get into
// an infinite loop, but as a measure of pragmatism we'll allow the
// top-level location itself to be a symlink, so that a user can
// potentially keep their plugins in a non-standard location but use a
// symlink to help Terraform find them anyway.
originalBaseDir := baseDir
if finalDir, err := filepath.EvalSymlinks(baseDir); err == nil {
log.Printf("[TRACE] getproviders.SearchLocalDirectory: %s is a symlink to %s", baseDir, finalDir)
baseDir = finalDir
} else {
// We'll eat this particular error because if we're somehow able to
// find plugins via baseDir below anyway then we'd rather do that than
// hard fail, but we'll log it in case it's useful for diagnosing why
// discovery didn't produce the expected outcome.
log.Printf("[TRACE] getproviders.SearchLocalDirectory: failed to resolve symlinks for %s: %s", baseDir, err)
}

err := filepath.Walk(baseDir, func(fullPath string, info os.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("cannot search %s: %s", fullPath, err)
Expand All @@ -45,6 +64,15 @@ func SearchLocalDirectory(baseDir string) (map[addrs.Provider]PackageMetaList, e
if len(parts) < 3 {
// Likely a prefix of a valid path, so we'll ignore it and visit
// the full valid path on a later call.

if (info.Mode() & os.ModeSymlink) != 0 {
// We don't allow symlinks for intermediate steps in the
// heirarchy because otherwise this walk would risk getting
// itself into an infinite loop, but if we do find one then
// we'll warn about it to help with debugging.
log.Printf("[WARN] Provider plugin search ignored symlink %s: only the base directory %s may be a symlink", fullPath, originalBaseDir)
}

return nil
}

Expand Down
52 changes: 52 additions & 0 deletions internal/getproviders/filesystem_search_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package getproviders

import (
"path/filepath"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform/addrs"
)

func TestSearchLocalDirectory(t *testing.T) {
tests := []struct {
Fixture string
Subdir string
Want map[addrs.Provider]PackageMetaList
}{
{
"symlinks",
"symlink",
map[addrs.Provider]PackageMetaList{
addrs.MustParseProviderSourceString("example.com/foo/bar"): {
{
Provider: addrs.MustParseProviderSourceString("example.com/foo/bar"),
Version: MustParseVersion("1.0.0"),
TargetPlatform: Platform{OS: "linux", Arch: "amd64"},
Filename: "terraform-provider-bar_1.0.0_linux_amd64.zip",
Location: PackageLocalDir("testdata/search-local-directory/symlinks/real/example.com/foo/bar/1.0.0/linux_amd64"),
},
},
// This search doesn't find example.net/foo/bar because only
// the top-level search directory is supported as being a
// symlink, and so we ignore the example.net symlink to
// example.com that is one level deeper.
},
},
}

for _, test := range tests {
t.Run(test.Fixture, func(t *testing.T) {
fullDir := filepath.Join("testdata/search-local-directory", test.Fixture, test.Subdir)
got, err := SearchLocalDirectory(fullDir)
if err != nil {
t.Errorf("unexpected error: %s", err)
}
want := test.Want

if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong result\n%s", diff)
}
})
}
}