2017-05-04 20:32:18 +02:00
|
|
|
package discovery
|
|
|
|
|
|
|
|
import (
|
2017-06-01 20:34:47 +02:00
|
|
|
"archive/zip"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
2017-05-04 20:32:18 +02:00
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
2017-06-01 20:34:47 +02:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2017-06-13 03:22:47 +02:00
|
|
|
"reflect"
|
2017-06-01 20:34:47 +02:00
|
|
|
"regexp"
|
|
|
|
"strings"
|
2017-05-04 20:32:18 +02:00
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
2017-06-01 20:34:47 +02:00
|
|
|
const testProviderFile = "test provider binary"
|
|
|
|
|
|
|
|
// return the directory listing for the "test" provider
|
|
|
|
func testListingHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.Write([]byte(versionList))
|
|
|
|
}
|
|
|
|
|
2017-06-19 22:20:10 +02:00
|
|
|
func testChecksumHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
// this exact plugin has a signnature and checksum file
|
|
|
|
if r.URL.Path == "/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS" {
|
|
|
|
http.ServeFile(w, r, "testdata/terraform-provider-template_0.1.0_SHA256SUMS")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if r.URL.Path == "/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS.sig" {
|
|
|
|
http.ServeFile(w, r, "testdata/terraform-provider-template_0.1.0_SHA256SUMS.sig")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-06-21 21:24:13 +02:00
|
|
|
// this this checksum file is corrupt and doesn't match the sig
|
|
|
|
if r.URL.Path == "/terraform-provider-badsig/0.1.0/terraform-provider-badsig_0.1.0_SHA256SUMS" {
|
|
|
|
http.ServeFile(w, r, "testdata/terraform-provider-badsig_0.1.0_SHA256SUMS")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if r.URL.Path == "/terraform-provider-badsig/0.1.0/terraform-provider-badsig_0.1.0_SHA256SUMS.sig" {
|
|
|
|
http.ServeFile(w, r, "testdata/terraform-provider-badsig_0.1.0_SHA256SUMS.sig")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-06-19 22:20:10 +02:00
|
|
|
http.Error(w, "signtaure files not found", http.StatusNotFound)
|
|
|
|
}
|
|
|
|
|
2017-06-01 20:34:47 +02:00
|
|
|
// returns a 200 for a valid provider url, using the patch number for the
|
|
|
|
// plugin protocol version.
|
|
|
|
func testHandler(w http.ResponseWriter, r *http.Request) {
|
2017-06-09 19:56:31 +02:00
|
|
|
if r.URL.Path == "/terraform-provider-test/" {
|
2017-06-01 20:34:47 +02:00
|
|
|
testListingHandler(w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
parts := strings.Split(r.URL.Path, "/")
|
2017-06-09 19:56:31 +02:00
|
|
|
if len(parts) != 4 {
|
2017-06-01 20:34:47 +02:00
|
|
|
http.Error(w, "not found", http.StatusNotFound)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-06-09 19:56:31 +02:00
|
|
|
filename := parts[3]
|
2017-06-01 20:34:47 +02:00
|
|
|
|
2017-06-13 03:22:47 +02:00
|
|
|
reg := regexp.MustCompile(`(terraform-provider-test)_(\d).(\d).(\d)_([^_]+)_([^._]+).zip`)
|
2017-06-01 20:34:47 +02:00
|
|
|
|
|
|
|
fileParts := reg.FindStringSubmatch(filename)
|
|
|
|
if len(fileParts) != 7 {
|
|
|
|
http.Error(w, "invalid provider: "+filename, http.StatusNotFound)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
w.Header().Set(protocolVersionHeader, fileParts[4])
|
|
|
|
|
|
|
|
// write a dummy file
|
|
|
|
z := zip.NewWriter(w)
|
2017-06-13 03:22:47 +02:00
|
|
|
fn := fmt.Sprintf("%s_v%s.%s.%s_x%s", fileParts[1], fileParts[2], fileParts[3], fileParts[4], fileParts[4])
|
|
|
|
f, err := z.Create(fn)
|
2017-06-01 20:34:47 +02:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
io.WriteString(f, testProviderFile)
|
|
|
|
z.Close()
|
|
|
|
}
|
|
|
|
|
2017-05-31 23:38:55 +02:00
|
|
|
func testReleaseServer() *httptest.Server {
|
2017-05-04 20:32:18 +02:00
|
|
|
handler := http.NewServeMux()
|
2017-06-09 19:56:31 +02:00
|
|
|
handler.HandleFunc("/terraform-provider-test/", testHandler)
|
2017-06-19 22:20:10 +02:00
|
|
|
handler.HandleFunc("/terraform-provider-template/", testChecksumHandler)
|
2017-06-21 21:24:13 +02:00
|
|
|
handler.HandleFunc("/terraform-provider-badsig/", testChecksumHandler)
|
2017-05-04 20:32:18 +02:00
|
|
|
|
2017-05-31 23:38:55 +02:00
|
|
|
return httptest.NewServer(handler)
|
|
|
|
}
|
|
|
|
|
2017-06-01 20:34:47 +02:00
|
|
|
func TestMain(m *testing.M) {
|
2017-05-31 23:38:55 +02:00
|
|
|
server := testReleaseServer()
|
2017-06-01 20:34:47 +02:00
|
|
|
releaseHost = server.URL
|
2017-05-04 20:32:18 +02:00
|
|
|
|
2017-06-01 20:34:47 +02:00
|
|
|
os.Exit(m.Run())
|
|
|
|
}
|
2017-05-04 20:32:18 +02:00
|
|
|
|
2017-06-01 20:34:47 +02:00
|
|
|
func TestVersionListing(t *testing.T) {
|
2017-05-04 20:32:18 +02:00
|
|
|
versions, err := listProviderVersions("test")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2017-05-31 23:38:55 +02:00
|
|
|
Versions(versions).Sort()
|
|
|
|
|
|
|
|
expected := []string{
|
|
|
|
"1.2.4",
|
|
|
|
"1.2.3",
|
|
|
|
"1.2.1",
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(versions) != len(expected) {
|
|
|
|
t.Fatalf("Received wrong number of versions. expected: %q, got: %q", expected, versions)
|
2017-05-04 20:32:18 +02:00
|
|
|
}
|
|
|
|
|
2017-05-31 23:38:55 +02:00
|
|
|
for i, v := range versions {
|
|
|
|
if v.String() != expected[i] {
|
|
|
|
t.Fatalf("incorrect version: %q, expected %q", v, expected[i])
|
2017-05-04 20:32:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-01 23:17:14 +02:00
|
|
|
func TestCheckProtocolVersions(t *testing.T) {
|
|
|
|
if checkPlugin(providerURL("test", VersionStr("1.2.3").MustParse().String()), 4) {
|
|
|
|
t.Fatal("protocol version 4 is not compatible")
|
2017-05-04 20:46:20 +02:00
|
|
|
}
|
|
|
|
|
2017-06-01 23:17:14 +02:00
|
|
|
if !checkPlugin(providerURL("test", VersionStr("1.2.3").MustParse().String()), 3) {
|
|
|
|
t.Fatal("protocol version 3 should be compatible")
|
2017-06-01 20:34:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-13 03:27:13 +02:00
|
|
|
func TestProviderInstallerGet(t *testing.T) {
|
2017-06-01 20:34:47 +02:00
|
|
|
tmpDir, err := ioutil.TempDir("", "tf-plugin")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
defer os.RemoveAll(tmpDir)
|
|
|
|
|
2017-06-01 23:17:14 +02:00
|
|
|
// attempt to use an incompatible protocol version
|
2017-06-13 03:22:47 +02:00
|
|
|
i := &ProviderInstaller{
|
|
|
|
Dir: tmpDir,
|
|
|
|
PluginProtocolVersion: 5,
|
2017-06-17 16:52:30 +02:00
|
|
|
SkipVerify: true,
|
2017-06-13 03:22:47 +02:00
|
|
|
}
|
|
|
|
_, err = i.Get("test", AllVersions)
|
2017-06-20 03:48:42 +02:00
|
|
|
if err != ErrorNoVersionCompatible {
|
2017-06-13 03:22:47 +02:00
|
|
|
t.Fatal("want error for incompatible version")
|
2017-06-01 23:17:14 +02:00
|
|
|
}
|
2017-06-01 20:34:47 +02:00
|
|
|
|
2017-06-13 03:22:47 +02:00
|
|
|
i = &ProviderInstaller{
|
|
|
|
Dir: tmpDir,
|
|
|
|
PluginProtocolVersion: 3,
|
2017-06-17 16:52:30 +02:00
|
|
|
SkipVerify: true,
|
2017-06-13 03:22:47 +02:00
|
|
|
}
|
2017-06-20 03:48:42 +02:00
|
|
|
|
|
|
|
{
|
|
|
|
_, err := i.Get("test", ConstraintStr(">9.0.0").MustParse())
|
|
|
|
if err != ErrorNoSuitableVersion {
|
|
|
|
t.Fatal("want error for mismatching constraints")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
_, err := i.Get("nonexist", AllVersions)
|
|
|
|
if err != ErrorNoSuchProvider {
|
|
|
|
t.Fatal("want error for no such provider")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-13 03:22:47 +02:00
|
|
|
gotMeta, err := i.Get("test", AllVersions)
|
2017-06-01 20:34:47 +02:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2017-06-01 23:17:14 +02:00
|
|
|
// we should have version 1.2.3
|
2017-06-13 03:22:47 +02:00
|
|
|
dest := filepath.Join(tmpDir, "terraform-provider-test_v1.2.3_x3")
|
|
|
|
|
|
|
|
wantMeta := PluginMeta{
|
|
|
|
Name: "test",
|
|
|
|
Version: VersionStr("1.2.3"),
|
|
|
|
Path: dest,
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(gotMeta, wantMeta) {
|
|
|
|
t.Errorf("wrong result meta\ngot: %#v\nwant: %#v", gotMeta, wantMeta)
|
|
|
|
}
|
|
|
|
|
2017-06-01 20:34:47 +02:00
|
|
|
f, err := ioutil.ReadFile(dest)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// provider should have been unzipped
|
|
|
|
if string(f) != testProviderFile {
|
|
|
|
t.Fatalf("test provider contains: %q", f)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-06-13 03:27:13 +02:00
|
|
|
func TestProviderInstallerPurgeUnused(t *testing.T) {
|
|
|
|
tmpDir, err := ioutil.TempDir("", "tf-plugin")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
defer os.RemoveAll(tmpDir)
|
|
|
|
|
|
|
|
unwantedPath := filepath.Join(tmpDir, "terraform-provider-test_v0.0.1_x2")
|
|
|
|
wantedPath := filepath.Join(tmpDir, "terraform-provider-test_v1.2.3_x3")
|
|
|
|
|
|
|
|
f, err := os.Create(unwantedPath)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
f.Close()
|
|
|
|
f, err = os.Create(wantedPath)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
f.Close()
|
|
|
|
|
|
|
|
i := &ProviderInstaller{
|
|
|
|
Dir: tmpDir,
|
|
|
|
PluginProtocolVersion: 3,
|
2017-06-17 16:52:30 +02:00
|
|
|
SkipVerify: true,
|
2017-06-13 03:27:13 +02:00
|
|
|
}
|
|
|
|
purged, err := i.PurgeUnused(map[string]PluginMeta{
|
|
|
|
"test": PluginMeta{
|
|
|
|
Name: "test",
|
|
|
|
Version: VersionStr("1.2.3"),
|
|
|
|
Path: wantedPath,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if got, want := purged.Count(), 1; got != want {
|
|
|
|
t.Errorf("wrong purged count %d; want %d", got, want)
|
|
|
|
}
|
|
|
|
if got, want := purged.Newest().Path, unwantedPath; got != want {
|
|
|
|
t.Errorf("wrong purged path %s; want %s", got, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
files, err := ioutil.ReadDir(tmpDir)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
gotFilenames := make([]string, len(files))
|
|
|
|
for i, info := range files {
|
|
|
|
gotFilenames[i] = info.Name()
|
|
|
|
}
|
|
|
|
wantFilenames := []string{"terraform-provider-test_v1.2.3_x3"}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(gotFilenames, wantFilenames) {
|
|
|
|
t.Errorf("wrong filenames after purge\ngot: %#v\nwant: %#v", gotFilenames, wantFilenames)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-19 22:20:10 +02:00
|
|
|
// Test fetching a provider's checksum file while verifying its signature.
|
|
|
|
func TestProviderChecksum(t *testing.T) {
|
|
|
|
// we only need the checksum, as getter is doing the actual file comparison.
|
|
|
|
sha256sum, err := getProviderChecksum("template", "0.1.0")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the expected checksum for our os/arch
|
|
|
|
sumData, err := ioutil.ReadFile("testdata/terraform-provider-template_0.1.0_SHA256SUMS")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := checksumForFile(sumData, providerFileName("template", "0.1.0"))
|
|
|
|
|
|
|
|
if sha256sum != expected {
|
|
|
|
t.Fatalf("expected: %s\ngot %s\n", sha256sum, expected)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-21 21:24:13 +02:00
|
|
|
// Test fetching a provider's checksum file witha bad signature
|
|
|
|
func TestProviderChecksumBadSignature(t *testing.T) {
|
|
|
|
// we only need the checksum, as getter is doing the actual file comparison.
|
|
|
|
sha256sum, err := getProviderChecksum("badsig", "0.1.0")
|
|
|
|
if err == nil {
|
|
|
|
t.Fatal("expcted error")
|
|
|
|
}
|
|
|
|
|
|
|
|
if !strings.Contains(err.Error(), "signature") {
|
|
|
|
t.Fatal("expected signature error, got:", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if sha256sum != "" {
|
|
|
|
t.Fatal("expected no checksum, got:", sha256sum)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-04 20:32:18 +02:00
|
|
|
const versionList = `<!DOCTYPE html>
|
|
|
|
<html>
|
|
|
|
<body>
|
|
|
|
<ul>
|
|
|
|
<li>
|
|
|
|
<a href="../">../</a>
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
<a href="/terraform-provider-test/1.2.3/">terraform-provider-test_1.2.3</a>
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
<a href="/terraform-provider-test/1.2.1/">terraform-provider-test_1.2.1</a>
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
<a href="/terraform-provider-test/1.2.4/">terraform-provider-test_1.2.4</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
<footer>
|
|
|
|
Proudly fronted by <a href="https://fastly.com/?utm_source=hashicorp" target="_TOP">Fastly</a>
|
|
|
|
</footer>
|
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
`
|