74 lines
2.5 KiB
Go
74 lines
2.5 KiB
Go
|
package providercache
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"fmt"
|
||
|
"io/ioutil"
|
||
|
"net/http"
|
||
|
|
||
|
getter "github.com/hashicorp/go-getter"
|
||
|
|
||
|
"github.com/hashicorp/terraform/httpclient"
|
||
|
)
|
||
|
|
||
|
// We borrow the "unpack a zip file into a target directory" logic from
|
||
|
// go-getter, even though we're not otherwise using go-getter here.
|
||
|
// (We don't need the same flexibility as we have for modules, because
|
||
|
// providers _always_ come from provider registries, which have a very
|
||
|
// specific protocol and set of expectations.)
|
||
|
var unzip = getter.ZipDecompressor{}
|
||
|
|
||
|
func installFromHTTPURL(ctx context.Context, url string, targetDir string) error {
|
||
|
// When we're installing from an HTTP URL we expect the URL to refer to
|
||
|
// a zip file. We'll fetch that into a temporary file here and then
|
||
|
// delegate to installFromLocalArchive below to actually extract it.
|
||
|
// (We're not using go-getter here because its HTTP getter has a bunch
|
||
|
// of extraneous functionality we don't need or want, like indirection
|
||
|
// through X-Terraform-Get header, attempting partial fetches for
|
||
|
// files that already exist, etc.)
|
||
|
|
||
|
httpClient := httpclient.New()
|
||
|
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("invalid provider download request: %s", err)
|
||
|
}
|
||
|
resp, err := httpClient.Do(req)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer resp.Body.Close()
|
||
|
|
||
|
if resp.StatusCode != http.StatusOK {
|
||
|
return fmt.Errorf("unsuccessful request to %s: %s", url, resp.Status)
|
||
|
}
|
||
|
|
||
|
f, err := ioutil.TempFile("", "terraform-provider")
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("failed to open temporary file to download from %s", url)
|
||
|
}
|
||
|
defer f.Close()
|
||
|
|
||
|
// We'll borrow go-getter's "cancelable copy" implementation here so that
|
||
|
// the download can potentially be interrupted partway through.
|
||
|
n, err := getter.Copy(ctx, f, resp.Body)
|
||
|
if err == nil && n < resp.ContentLength {
|
||
|
err = fmt.Errorf("incorrect response size: expected %d bytes, but got %d bytes", resp.ContentLength, n)
|
||
|
}
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// If we managed to download successfully then we can now delegate to
|
||
|
// installFromLocalArchive for extraction.
|
||
|
archiveFilename := f.Name()
|
||
|
return installFromLocalArchive(ctx, archiveFilename, targetDir)
|
||
|
}
|
||
|
|
||
|
func installFromLocalArchive(ctx context.Context, filename string, targetDir string) error {
|
||
|
return unzip.Decompress(targetDir, filename, true)
|
||
|
}
|
||
|
|
||
|
func installFromLocalDir(ctx context.Context, sourceDir string, targetDir string) error {
|
||
|
return fmt.Errorf("installFromLocalDir not yet implemented")
|
||
|
}
|