package terraform import ( "fmt" "io/fs" "os" "path" "path/filepath" "runtime" "strings" "github.com/pkg/errors" "github.com/sirupsen/logrus" error2 "github.com/snyk/driftctl/pkg/terraform/error" "github.com/snyk/driftctl/pkg/output" ) type HomeDirInterface interface { Dir() (string, error) } type ProviderInstaller struct { downloader ProviderDownloaderInterface config ProviderConfig homeDir string } func NewProviderInstaller(config ProviderConfig) (*ProviderInstaller, error) { return &ProviderInstaller{ NewProviderDownloader(), config, config.ConfigDir, }, nil } func (p *ProviderInstaller) Install() (string, error) { providerDir := p.getProviderDirectory() providerPath := p.getBinaryPath() info, err := os.Stat(providerPath) if err != nil && os.IsNotExist(err) { logrus.WithFields(logrus.Fields{ "path": providerPath, }).Debug("provider not found, downloading ...") output.Printf("Downloading terraform provider: %s\n", p.config.Key) err := p.downloader.Download( p.config.GetDownloadUrl(), providerDir, ) if err != nil { if notFoundErr, ok := err.(error2.ProviderNotFoundError); ok { notFoundErr.Version = p.config.Version return "", notFoundErr } return "", err } logrus.Debug("Download successful") } if info != nil && info.IsDir() { return "", errors.Errorf( "found directory instead of provider binary in %s", providerPath, ) } if info != nil { logrus.WithFields(logrus.Fields{ "path": providerPath, }).Debug("Found existing provider") } return p.getBinaryPath(), nil } func (p ProviderInstaller) getProviderDirectory() string { return path.Join(p.homeDir, fmt.Sprintf(".driftctl/plugins/%s_%s/", runtime.GOOS, runtime.GOARCH)) } // Handle postfixes in binary names func (p *ProviderInstaller) getBinaryPath() string { providerDir := p.getProviderDirectory() binaryName := p.config.GetBinaryName() _, err := os.Stat(path.Join(providerDir, binaryName)) if err != nil && os.IsNotExist(err) { _ = filepath.WalkDir(providerDir, func(filePath string, d fs.DirEntry, err error) error { if d != nil && strings.HasPrefix(d.Name(), p.config.GetBinaryName()) { binaryName = d.Name() } return nil }) } return path.Join(providerDir, binaryName) }