plugin/discovery: removing deprecated functions
This commit is contained in:
parent
ce5e66e178
commit
f83d5866fe
|
@ -10,12 +10,9 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/net/html"
|
|
||||||
|
|
||||||
getter "github.com/hashicorp/go-getter"
|
getter "github.com/hashicorp/go-getter"
|
||||||
multierror "github.com/hashicorp/go-multierror"
|
multierror "github.com/hashicorp/go-multierror"
|
||||||
|
|
||||||
|
@ -27,19 +24,10 @@ import (
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Releases are located by parsing the html listing from releases.hashicorp.com.
|
// Releases are located by querying the terraform registry.
|
||||||
//
|
|
||||||
// The URL for releases follows the pattern:
|
|
||||||
// https://releases.hashicorp.com/terraform-provider-name/<x.y.z>/terraform-provider-name_<x.y.z>_<os>_<arch>.<ext>
|
|
||||||
//
|
|
||||||
// The plugin protocol version will be saved with the release and returned in
|
|
||||||
// the header X-TERRAFORM_PROTOCOL_VERSION.
|
|
||||||
|
|
||||||
const protocolVersionHeader = "x-terraform-protocol-version"
|
const protocolVersionHeader = "x-terraform-protocol-version"
|
||||||
|
|
||||||
//var releaseHost = "https://releases.hashicorp.com"
|
|
||||||
var releaseHost = "https://tf-registry-staging.herokuapp.com"
|
|
||||||
|
|
||||||
var httpClient *http.Client
|
var httpClient *http.Client
|
||||||
|
|
||||||
var errVersionNotFound = errors.New("version not found")
|
var errVersionNotFound = errors.New("version not found")
|
||||||
|
@ -117,7 +105,7 @@ type ProviderInstaller struct {
|
||||||
// be presented alongside context about what is being installed, and thus the
|
// be presented alongside context about what is being installed, and thus the
|
||||||
// error messages do not redundantly include such information.
|
// error messages do not redundantly include such information.
|
||||||
func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, error) {
|
func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, error) {
|
||||||
// a little bit of initialization
|
// a little bit of initialization.
|
||||||
if i.OS == "" {
|
if i.OS == "" {
|
||||||
i.OS = runtime.GOOS
|
i.OS = runtime.GOOS
|
||||||
}
|
}
|
||||||
|
@ -125,14 +113,15 @@ func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, e
|
||||||
i.Arch = runtime.GOARCH
|
i.Arch = runtime.GOARCH
|
||||||
}
|
}
|
||||||
if i.registry == nil {
|
if i.registry == nil {
|
||||||
i.registry = registry.NewClient(i.Services, nil, nil)
|
i.registry = registry.NewClient(i.Services, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get a full listing of versions for the requested provider
|
||||||
allVersions, err := i.listProviderVersions(provider)
|
allVersions, err := i.listProviderVersions(provider)
|
||||||
|
|
||||||
// TODO: return multiple errors
|
// TODO: return multiple errors
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return PluginMeta{}, err
|
return PluginMeta{}, ErrorNoSuchProvider
|
||||||
}
|
}
|
||||||
if len(allVersions.Versions) == 0 {
|
if len(allVersions.Versions) == 0 {
|
||||||
return PluginMeta{}, ErrorNoSuitableVersion
|
return PluginMeta{}, ErrorNoSuitableVersion
|
||||||
|
@ -144,71 +133,39 @@ func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, e
|
||||||
return PluginMeta{}, ErrorNoSuitableVersion
|
return PluginMeta{}, ErrorNoSuitableVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort them newest to oldest
|
// sort them newest to oldest. The newest version wins!
|
||||||
sort.Sort(response.Collection(versions))
|
response.Collection(versions).Sort()
|
||||||
// the winning version is the newest
|
|
||||||
versionMeta := versions[0]
|
versionMeta := versions[0]
|
||||||
// get a Version from the version string
|
v := VersionStr(versionMeta.Version).MustParse()
|
||||||
// we already know this will not error from the preceding functions
|
|
||||||
v, _ := VersionStr(versionMeta.Version).Parse()
|
|
||||||
|
|
||||||
// Ensure that our installation directory exists
|
// check platform compatibility
|
||||||
err = os.MkdirAll(i.Dir, os.ModePerm)
|
if err := i.checkPlatformCompatibility(versionMeta); err != nil {
|
||||||
if err != nil {
|
// filter the list of versions to those that support the requested OS_ARCH
|
||||||
return PluginMeta{}, fmt.Errorf("failed to create plugin dir %s: %s", i.Dir, err)
|
// reset the "current" versionMeta
|
||||||
|
// versionMeta = filteredVersions[0]
|
||||||
|
return PluginMeta{}, ErrorNoVersionCompatible
|
||||||
}
|
}
|
||||||
|
|
||||||
// check plugin protocol compatibility
|
// check protocol compatibility
|
||||||
// We only validate the most recent version that meets the version constraints.
|
if err := i.checkPluginProtocol(versionMeta); err != nil {
|
||||||
// see RFC TF-055: Provider Protocol Versioning for more information
|
closestMatch, err := i.findProtocolCompatibleVersion(versions)
|
||||||
protoString := strconv.Itoa(int(i.PluginProtocolVersion))
|
if err == nil {
|
||||||
protocolVersion, err := VersionStr(protoString).Parse()
|
if err := i.checkPlatformCompatibility(closestMatch); err != nil {
|
||||||
if err != nil {
|
// This is where we give up instead of leap-frogging every version to check protocol & platform
|
||||||
return PluginMeta{}, fmt.Errorf("invalid plugin protocol version: %q", i.PluginProtocolVersion)
|
return PluginMeta{}, ErrorNoSuitableVersion
|
||||||
}
|
|
||||||
protocolConstraint, err := protocolVersion.MinorUpgradeConstraintStr().Parse()
|
|
||||||
if err != nil {
|
|
||||||
// This should not fail if the preceding function succeeded.
|
|
||||||
return PluginMeta{}, fmt.Errorf("invalid plugin protocol version: %q", protocolVersion.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, p := range versionMeta.Protocols {
|
|
||||||
proPro, err := VersionStr(p).Parse()
|
|
||||||
if err != nil {
|
|
||||||
// invalid protocol reported by the registry. Move along.
|
|
||||||
log.Printf("[WARN] invalid provider protocol version %q found in the registry", provider, versionMeta.Version)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !protocolConstraint.Allows(proPro) {
|
|
||||||
// TODO: get most recent compatible plugin and return a handy-dandy string for the user
|
|
||||||
// latest, err := getNewestCompatiblePlugin
|
|
||||||
// i.Ui.output|info): "the latest version of plugin BLAH which supports protocol BLAH is BLAH"
|
|
||||||
// Add this to your provider block:
|
|
||||||
// version = ~BLAH
|
|
||||||
// and if none is found, return ErrorNoVersionCompatible
|
|
||||||
return PluginMeta{}, fmt.Errorf("The latest version of plugin %q does not support plugin protocol version %q", provider, protocolVersion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var downloadURLs *response.TerraformProviderPlatformLocation
|
|
||||||
// check plugin platform compatibility
|
|
||||||
for _, p := range versionMeta.Platforms {
|
|
||||||
if p.Arch == i.Arch && p.OS == i.OS {
|
|
||||||
downloadURLs, err = i.listProviderDownloadURLs(provider, versionMeta.Version)
|
|
||||||
if err != nil {
|
|
||||||
return PluginMeta{}, fmt.Errorf("Problem getting ")
|
|
||||||
}
|
}
|
||||||
break
|
// This is a placeholder message.
|
||||||
|
i.Ui.Error(fmt.Sprintf("the most recent version of %s to match your platform is %s", provider, closestMatch))
|
||||||
|
return PluginMeta{}, ErrorNoVersionCompatible
|
||||||
}
|
}
|
||||||
// TODO: return the most recent compatible versions
|
return PluginMeta{}, ErrorNoVersionCompatibleWithPlatform
|
||||||
// return PluginMeta{}, ErrorNoVersionCompatibleWithPlatform
|
|
||||||
return PluginMeta{}, fmt.Errorf("The latest version of plugin %q does not support the requested platform %s %s", provider, i.OS, i.Arch)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
downloadURLs, err := i.listProviderDownloadURLs(provider, versionMeta.Version)
|
||||||
providerURL := downloadURLs.DownloadURL
|
providerURL := downloadURLs.DownloadURL
|
||||||
|
|
||||||
if !i.SkipVerify {
|
if !i.SkipVerify {
|
||||||
sha256, err := i.getProviderChecksum(provider, downloadURLs)
|
sha256, err := i.getProviderChecksum(downloadURLs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return PluginMeta{}, err
|
return PluginMeta{}, err
|
||||||
}
|
}
|
||||||
|
@ -283,7 +240,7 @@ func (i *ProviderInstaller) install(provider string, version Version, url string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Link or copy the cached binary into our install dir so the
|
// Link or copy the cached binary into our install dir so the
|
||||||
// normal resolution machinery can find it.
|
// normal resolution machinery can find it.
|
||||||
filename := filepath.Base(cached)
|
filename := filepath.Base(cached)
|
||||||
targetPath := filepath.Join(i.Dir, filename)
|
targetPath := filepath.Join(i.Dir, filename)
|
||||||
|
|
||||||
|
@ -351,7 +308,6 @@ func (i *ProviderInstaller) install(provider string, version Version, url string
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,44 +343,7 @@ func (i *ProviderInstaller) PurgeUnused(used map[string]PluginMeta) (PluginMetaS
|
||||||
return removed, errs
|
return removed, errs
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plugins are referred to by the short name, but all URLs and files will use
|
func (i *ProviderInstaller) getProviderChecksum(urls *response.TerraformProviderPlatformLocation) (string, error) {
|
||||||
// the full name prefixed with terraform-<plugin_type>-
|
|
||||||
func (i *ProviderInstaller) providerName(name string) string {
|
|
||||||
return "terraform-provider-" + name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *ProviderInstaller) providerFileName(name, version string) string {
|
|
||||||
os := i.OS
|
|
||||||
arch := i.Arch
|
|
||||||
if os == "" {
|
|
||||||
os = runtime.GOOS
|
|
||||||
}
|
|
||||||
if arch == "" {
|
|
||||||
arch = runtime.GOARCH
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s_%s_%s_%s.zip", i.providerName(name), version, os, arch)
|
|
||||||
}
|
|
||||||
|
|
||||||
// providerVersionsURL returns the path to the released versions directory for the provider:
|
|
||||||
// https://releases.hashicorp.com/terraform-provider-name/
|
|
||||||
func (i *ProviderInstaller) providerVersionsURL(name string) string {
|
|
||||||
return releaseHost + "/" + i.providerName(name) + "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
// providerURL returns the full path to the provider file, using the current OS
|
|
||||||
// and ARCH:
|
|
||||||
// .../terraform-provider-name_<x.y.z>/terraform-provider-name_<x.y.z>_<os>_<arch>.<ext>
|
|
||||||
func (i *ProviderInstaller) providerURL(name, version string) string {
|
|
||||||
return fmt.Sprintf("%s%s/%s", i.providerVersionsURL(name), version, i.providerFileName(name, version))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *ProviderInstaller) providerChecksumURL(name, version string) string {
|
|
||||||
fileName := fmt.Sprintf("%s_%s_SHA256SUMS", i.providerName(name), version)
|
|
||||||
u := fmt.Sprintf("%s%s/%s", i.providerVersionsURL(name), version, fileName)
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *ProviderInstaller) getProviderChecksum(name string, urls *response.TerraformProviderPlatformLocation) (string, error) {
|
|
||||||
checksums, err := getPluginSHA256SUMs(urls.ShasumsURL, urls.ShasumsSignatureURL)
|
checksums, err := getPluginSHA256SUMs(urls.ShasumsURL, urls.ShasumsSignatureURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -440,6 +359,86 @@ func (i *ProviderInstaller) listProviderVersions(name string) (*response.Terrafo
|
||||||
return versions, err
|
return versions, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *ProviderInstaller) listProviderDownloadURLs(name, version string) (*response.TerraformProviderPlatformLocation, error) {
|
||||||
|
urls, err := i.registry.TerraformProviderLocation(regsrc.NewTerraformProvider(name, i.OS, i.Arch), version)
|
||||||
|
if urls == nil {
|
||||||
|
return nil, fmt.Errorf("No download urls found for provider %s", name)
|
||||||
|
}
|
||||||
|
return urls, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// REVIEWER QUESTION: this ends up swallowing a bunch of errors from
|
||||||
|
// checkPluginProtocol. Do they need to be percolated up better, or would
|
||||||
|
// debug messages would suffice in these situations?
|
||||||
|
func (i *ProviderInstaller) findProtocolCompatibleVersion(versions []*response.TerraformProviderVersion) (*response.TerraformProviderVersion, error) {
|
||||||
|
for _, version := range versions {
|
||||||
|
if err := i.checkPluginProtocol(version); err == nil {
|
||||||
|
return version, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, ErrorNoVersionCompatible
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ProviderInstaller) checkPluginProtocol(versionMeta *response.TerraformProviderVersion) error {
|
||||||
|
// TODO: should this be a different error? We should probably differentiate between
|
||||||
|
// no compatible versions and no protocol versions listed at all
|
||||||
|
// No protocols at all!
|
||||||
|
if len(versionMeta.Protocols) == 0 {
|
||||||
|
return fmt.Errorf("no plugin protocol versions listed")
|
||||||
|
}
|
||||||
|
|
||||||
|
protoString := strconv.Itoa(int(i.PluginProtocolVersion))
|
||||||
|
protocolVersion, err := VersionStr(protoString).Parse()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid plugin protocol version: %q", i.PluginProtocolVersion)
|
||||||
|
}
|
||||||
|
protocolConstraint, err := protocolVersion.MinorUpgradeConstraintStr().Parse()
|
||||||
|
if err != nil {
|
||||||
|
// This should not fail if the preceding function succeeded.
|
||||||
|
return fmt.Errorf("invalid plugin protocol version: %q", protocolVersion.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range versionMeta.Protocols {
|
||||||
|
proPro, err := VersionStr(p).Parse()
|
||||||
|
if err != nil {
|
||||||
|
// invalid protocol reported by the registry. Move along.
|
||||||
|
log.Printf("[WARN] invalid provider protocol version %q found in the registry", versionMeta.Version)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// success!
|
||||||
|
if protocolConstraint.Allows(proPro) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorNoVersionCompatible
|
||||||
|
}
|
||||||
|
|
||||||
|
// REVIEWER QUESTION (again): this ends up swallowing a bunch of errors from
|
||||||
|
// checkPluginProtocol. Do they need to be percolated up better, or would
|
||||||
|
// debug messages would suffice in these situations?
|
||||||
|
func (i *ProviderInstaller) findPlatformCompatibleVersion(versions []*response.TerraformProviderVersion) (*response.TerraformProviderVersion, error) {
|
||||||
|
for _, version := range versions {
|
||||||
|
if err := i.checkPlatformCompatibility(version); err == nil {
|
||||||
|
return version, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrorNoVersionCompatibleWithPlatform
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ProviderInstaller) checkPlatformCompatibility(versionMeta *response.TerraformProviderVersion) error {
|
||||||
|
if len(versionMeta.Platforms) == 0 {
|
||||||
|
return fmt.Errorf("no supported provider platforms listed")
|
||||||
|
}
|
||||||
|
for _, p := range versionMeta.Platforms {
|
||||||
|
if p.Arch == i.Arch && p.OS == i.OS {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("version %s does not support the requested platform %s_%s", versionMeta.Version, i.OS, i.Arch)
|
||||||
|
}
|
||||||
|
|
||||||
// take the list of available versions for a plugin, and filter out those that
|
// take the list of available versions for a plugin, and filter out those that
|
||||||
// don't fit the constraints.
|
// don't fit the constraints.
|
||||||
func allowedVersions(available *response.TerraformProviderVersions, required Constraints) []*response.TerraformProviderVersion {
|
func allowedVersions(available *response.TerraformProviderVersions, required Constraints) []*response.TerraformProviderVersion {
|
||||||
|
@ -455,83 +454,9 @@ func allowedVersions(available *response.TerraformProviderVersions, required Con
|
||||||
allowed = append(allowed, v)
|
allowed = append(allowed, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return allowed
|
return allowed
|
||||||
}
|
}
|
||||||
|
|
||||||
// return a list of the plugin versions at the given URL
|
|
||||||
func listPluginVersions(url string) ([]Version, error) {
|
|
||||||
resp, err := httpClient.Get(url)
|
|
||||||
if err != nil {
|
|
||||||
// http library produces a verbose error message that includes the
|
|
||||||
// URL being accessed, etc.
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
body, _ := ioutil.ReadAll(resp.Body)
|
|
||||||
log.Printf("[ERROR] failed to fetch plugin versions from %s\n%s\n%s", url, resp.Status, body)
|
|
||||||
|
|
||||||
switch resp.StatusCode {
|
|
||||||
case http.StatusNotFound, http.StatusForbidden:
|
|
||||||
// These are treated as indicative of the given name not being
|
|
||||||
// a valid provider name at all.
|
|
||||||
return nil, ErrorNoSuchProvider
|
|
||||||
|
|
||||||
default:
|
|
||||||
// All other errors are assumed to be operational problems.
|
|
||||||
return nil, fmt.Errorf("error accessing %s: %s", url, resp.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := html.Parse(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
names := []string{}
|
|
||||||
|
|
||||||
// all we need to do is list links on the directory listing page that look like plugins
|
|
||||||
var f func(*html.Node)
|
|
||||||
f = func(n *html.Node) {
|
|
||||||
if n.Type == html.ElementNode && n.Data == "a" {
|
|
||||||
c := n.FirstChild
|
|
||||||
if c != nil && c.Type == html.TextNode && strings.HasPrefix(c.Data, "terraform-") {
|
|
||||||
names = append(names, c.Data)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
|
||||||
f(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f(body)
|
|
||||||
|
|
||||||
return versionsFromNames(names), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse the list of directory names into a sorted list of available versions
|
|
||||||
func versionsFromNames(names []string) []Version {
|
|
||||||
var versions []Version
|
|
||||||
for _, name := range names {
|
|
||||||
parts := strings.SplitN(name, "_", 2)
|
|
||||||
if len(parts) == 2 && parts[1] != "" {
|
|
||||||
v, err := VersionStr(parts[1]).Parse()
|
|
||||||
if err != nil {
|
|
||||||
// filter invalid versions scraped from the page
|
|
||||||
log.Printf("[WARN] invalid version found for %q: %s", name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
versions = append(versions, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return versions
|
|
||||||
}
|
|
||||||
|
|
||||||
func checksumForFile(sums []byte, name string) string {
|
func checksumForFile(sums []byte, name string) string {
|
||||||
for _, line := range strings.Split(string(sums), "\n") {
|
for _, line := range strings.Split(string(sums), "\n") {
|
||||||
parts := strings.Fields(line)
|
parts := strings.Fields(line)
|
||||||
|
@ -579,11 +504,27 @@ func getFile(url string) ([]byte, error) {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetReleaseHost() string {
|
// ProviderProtocolTooOld is a message sent to the CLI UI if the provider's
|
||||||
return releaseHost
|
// supported protocol versions are too old for the user's version of terraform,
|
||||||
}
|
// but an older version of the provider is compatible.
|
||||||
|
const providerProtocolTooOld = `Provider %q v%s is not compatible with Terraform %s.
|
||||||
|
|
||||||
func (i *ProviderInstaller) listProviderDownloadURLs(name, version string) (*response.TerraformProviderPlatformLocation, error) {
|
Provider version %s is the earliest compatible version.
|
||||||
urls, err := i.registry.TerraformProviderLocation(regsrc.NewTerraformProvider(name, i.OS, i.Arch), version)
|
Select it with the following version constraint:
|
||||||
return urls, err
|
|
||||||
}
|
version = %q
|
||||||
|
`
|
||||||
|
|
||||||
|
// ProviderProtocolTooNew is a message sent to the CLI UI if the provider's
|
||||||
|
// supported protocol versions are too new for the user's version of terraform,
|
||||||
|
// and the user could either upgrade terraform or choose an older version of the
|
||||||
|
// provider
|
||||||
|
const providerProtocolTooNew = `Provider %q v%s is not compatible with Terraform %s.
|
||||||
|
|
||||||
|
Provider version v%s is the latest compatible version. Select
|
||||||
|
it with the following constraint:
|
||||||
|
|
||||||
|
version = %q
|
||||||
|
|
||||||
|
Alternatively, upgrade to the latest version of Terraform for compatibility with newer provider releases.
|
||||||
|
`
|
||||||
|
|
|
@ -2,30 +2,80 @@ package discovery
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/registry"
|
||||||
|
"github.com/hashicorp/terraform/registry/response"
|
||||||
|
"github.com/hashicorp/terraform/svchost"
|
||||||
|
"github.com/hashicorp/terraform/svchost/disco"
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
const testProviderFile = "test provider binary"
|
const testProviderFile = "test provider binary"
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
server := testReleaseServer()
|
||||||
|
l, err := net.Listen("tcp", "127.0.0.1:8080")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUnstartedServer creates a listener. Close that listener and replace
|
||||||
|
// with the one we created.
|
||||||
|
server.Listener.Close()
|
||||||
|
server.Listener = l
|
||||||
|
server.Start()
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
||||||
// return the directory listing for the "test" provider
|
// return the directory listing for the "test" provider
|
||||||
func testListingHandler(w http.ResponseWriter, r *http.Request) {
|
func testListingHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Write([]byte(versionList))
|
parts := strings.Split(r.URL.Path, "/")
|
||||||
|
if len(parts) != 6 {
|
||||||
|
http.Error(w, "not found", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
provider := parts[4]
|
||||||
|
if provider == "test" {
|
||||||
|
js, err := json.Marshal(versionList)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Write(js)
|
||||||
|
}
|
||||||
|
http.Error(w, ErrorNoSuchProvider.Error(), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the download URLs for the "test" provider
|
||||||
|
func testDownloadHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
js, err := json.Marshal(downloadURLs)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Write(js)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testChecksumHandler(w http.ResponseWriter, r *http.Request) {
|
func testChecksumHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
// this exact plugin has a signnature and checksum file
|
// this exact plugin has a signature and checksum file
|
||||||
if r.URL.Path == "/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS" {
|
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")
|
http.ServeFile(w, r, "testdata/terraform-provider-template_0.1.0_SHA256SUMS")
|
||||||
return
|
return
|
||||||
|
@ -51,32 +101,25 @@ func testChecksumHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
// returns a 200 for a valid provider url, using the patch number for the
|
// returns a 200 for a valid provider url, using the patch number for the
|
||||||
// plugin protocol version.
|
// plugin protocol version.
|
||||||
func testHandler(w http.ResponseWriter, r *http.Request) {
|
func testHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.URL.Path == "/terraform-provider-test/" {
|
if strings.HasSuffix(r.URL.Path, "/versions") {
|
||||||
testListingHandler(w, r)
|
testListingHandler(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.Contains(r.URL.Path, "/download") {
|
||||||
|
testDownloadHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
parts := strings.Split(r.URL.Path, "/")
|
parts := strings.Split(r.URL.Path, "/")
|
||||||
if len(parts) != 4 {
|
if len(parts) != 7 {
|
||||||
http.Error(w, "not found", http.StatusNotFound)
|
http.Error(w, "not found", http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
filename := parts[3]
|
|
||||||
|
|
||||||
reg := regexp.MustCompile(`(terraform-provider-test)_(\d).(\d).(\d)_([^_]+)_([^._]+).zip`)
|
|
||||||
|
|
||||||
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
|
// write a dummy file
|
||||||
z := zip.NewWriter(w)
|
z := zip.NewWriter(w)
|
||||||
fn := fmt.Sprintf("%s_v%s.%s.%s_x%s", fileParts[1], fileParts[2], fileParts[3], fileParts[4], fileParts[4])
|
fn := fmt.Sprintf("%s_v%s", parts[4], parts[5])
|
||||||
f, err := z.Create(fn)
|
f, err := z.Create(fn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -87,33 +130,42 @@ func testHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
func testReleaseServer() *httptest.Server {
|
func testReleaseServer() *httptest.Server {
|
||||||
handler := http.NewServeMux()
|
handler := http.NewServeMux()
|
||||||
handler.HandleFunc("/terraform-provider-test/", testHandler)
|
handler.HandleFunc("/v1/providers/terraform-providers/", testHandler)
|
||||||
handler.HandleFunc("/terraform-provider-template/", testChecksumHandler)
|
handler.HandleFunc("/terraform-provider-template/", testChecksumHandler)
|
||||||
handler.HandleFunc("/terraform-provider-badsig/", testChecksumHandler)
|
handler.HandleFunc("/terraform-provider-badsig/", testChecksumHandler)
|
||||||
|
handler.HandleFunc("/.well-known/terraform.json", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
io.WriteString(w, `{"modules.v1":"http://localhost/v1/modules/", "providers.v1":"http://localhost/v1/providers/"}`)
|
||||||
|
})
|
||||||
|
|
||||||
return httptest.NewServer(handler)
|
return httptest.NewUnstartedServer(handler)
|
||||||
}
|
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
|
||||||
server := testReleaseServer()
|
|
||||||
releaseHost = server.URL
|
|
||||||
|
|
||||||
os.Exit(m.Run())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVersionListing(t *testing.T) {
|
func TestVersionListing(t *testing.T) {
|
||||||
i := &ProviderInstaller{}
|
server := testReleaseServer()
|
||||||
versions, err := i.listProviderVersions("test")
|
server.Start()
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
i := newProviderInstaller(server)
|
||||||
|
|
||||||
|
allVersions, err := i.listProviderVersions("test")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
Versions(versions).Sort()
|
var versions []*response.TerraformProviderVersion
|
||||||
|
|
||||||
expected := []string{
|
for _, v := range allVersions.Versions {
|
||||||
"1.2.4",
|
versions = append(versions, v)
|
||||||
"1.2.3",
|
}
|
||||||
"1.2.1",
|
|
||||||
|
response.Collection(versions).Sort()
|
||||||
|
|
||||||
|
expected := []*response.TerraformProviderVersion{
|
||||||
|
{Version: "1.2.4"},
|
||||||
|
{Version: "1.2.3"},
|
||||||
|
{Version: "1.2.1"},
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(versions) != len(expected) {
|
if len(versions) != len(expected) {
|
||||||
|
@ -121,24 +173,60 @@ func TestVersionListing(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, v := range versions {
|
for i, v := range versions {
|
||||||
if v.String() != expected[i] {
|
if v.Version != expected[i].Version {
|
||||||
t.Fatalf("incorrect version: %q, expected %q", v, expected[i])
|
t.Fatalf("incorrect version: %q, expected %q", v, expected[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckProtocolVersions(t *testing.T) {
|
func TestCheckProtocolVersions(t *testing.T) {
|
||||||
i := &ProviderInstaller{}
|
tests := []struct {
|
||||||
if checkPlugin(i.providerURL("test", VersionStr("1.2.3").MustParse().String()), 4) {
|
VersionMeta *response.TerraformProviderVersion
|
||||||
t.Fatal("protocol version 4 is not compatible")
|
Err bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
&response.TerraformProviderVersion{
|
||||||
|
Protocols: []string{"1", "2"},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&response.TerraformProviderVersion{
|
||||||
|
Protocols: []string{"4"},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&response.TerraformProviderVersion{
|
||||||
|
Protocols: []string{"4.2"},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if !checkPlugin(i.providerURL("test", VersionStr("1.2.3").MustParse().String()), 3) {
|
server := testReleaseServer()
|
||||||
t.Fatal("protocol version 3 should be compatible")
|
server.Start()
|
||||||
|
defer server.Close()
|
||||||
|
i := newProviderInstaller(server)
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
err := i.checkPluginProtocol(test.VersionMeta)
|
||||||
|
if test.Err {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("succeeded; want error")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProviderInstallerGet(t *testing.T) {
|
func TestProviderInstallerGet(t *testing.T) {
|
||||||
|
server := testReleaseServer()
|
||||||
|
server.Start()
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
tmpDir, err := ioutil.TempDir("", "tf-plugin")
|
tmpDir, err := ioutil.TempDir("", "tf-plugin")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -152,17 +240,20 @@ func TestProviderInstallerGet(t *testing.T) {
|
||||||
PluginProtocolVersion: 5,
|
PluginProtocolVersion: 5,
|
||||||
SkipVerify: true,
|
SkipVerify: true,
|
||||||
Ui: cli.NewMockUi(),
|
Ui: cli.NewMockUi(),
|
||||||
|
registry: registry.NewClient(Disco(server), nil),
|
||||||
}
|
}
|
||||||
_, err = i.Get("test", AllVersions)
|
_, err = i.Get("test", AllVersions)
|
||||||
if err != ErrorNoVersionCompatible {
|
|
||||||
|
if err != ErrorNoVersionCompatibleWithPlatform {
|
||||||
t.Fatal("want error for incompatible version")
|
t.Fatal("want error for incompatible version")
|
||||||
}
|
}
|
||||||
|
|
||||||
i = &ProviderInstaller{
|
i = &ProviderInstaller{
|
||||||
Dir: tmpDir,
|
Dir: tmpDir,
|
||||||
PluginProtocolVersion: 3,
|
PluginProtocolVersion: 4,
|
||||||
SkipVerify: true,
|
SkipVerify: true,
|
||||||
Ui: cli.NewMockUi(),
|
Ui: cli.NewMockUi(),
|
||||||
|
registry: registry.NewClient(Disco(server), nil),
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -184,12 +275,12 @@ func TestProviderInstallerGet(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// we should have version 1.2.3
|
// we should have version 1.2.4
|
||||||
dest := filepath.Join(tmpDir, "terraform-provider-test_v1.2.3_x3")
|
dest := filepath.Join(tmpDir, "terraform-provider-test_v1.2.4")
|
||||||
|
|
||||||
wantMeta := PluginMeta{
|
wantMeta := PluginMeta{
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Version: VersionStr("1.2.3"),
|
Version: VersionStr("1.2.4"),
|
||||||
Path: dest,
|
Path: dest,
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(gotMeta, wantMeta) {
|
if !reflect.DeepEqual(gotMeta, wantMeta) {
|
||||||
|
@ -209,6 +300,9 @@ func TestProviderInstallerGet(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProviderInstallerPurgeUnused(t *testing.T) {
|
func TestProviderInstallerPurgeUnused(t *testing.T) {
|
||||||
|
server := testReleaseServer()
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
tmpDir, err := ioutil.TempDir("", "tf-plugin")
|
tmpDir, err := ioutil.TempDir("", "tf-plugin")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -235,6 +329,7 @@ func TestProviderInstallerPurgeUnused(t *testing.T) {
|
||||||
PluginProtocolVersion: 3,
|
PluginProtocolVersion: 3,
|
||||||
SkipVerify: true,
|
SkipVerify: true,
|
||||||
Ui: cli.NewMockUi(),
|
Ui: cli.NewMockUi(),
|
||||||
|
registry: registry.NewClient(Disco(server), nil),
|
||||||
}
|
}
|
||||||
purged, err := i.PurgeUnused(map[string]PluginMeta{
|
purged, err := i.PurgeUnused(map[string]PluginMeta{
|
||||||
"test": PluginMeta{
|
"test": PluginMeta{
|
||||||
|
@ -272,66 +367,102 @@ func TestProviderInstallerPurgeUnused(t *testing.T) {
|
||||||
|
|
||||||
// Test fetching a provider's checksum file while verifying its signature.
|
// Test fetching a provider's checksum file while verifying its signature.
|
||||||
func TestProviderChecksum(t *testing.T) {
|
func TestProviderChecksum(t *testing.T) {
|
||||||
i := &ProviderInstaller{}
|
tests := []struct {
|
||||||
|
URLs *response.TerraformProviderPlatformLocation
|
||||||
// we only need the checksum, as getter is doing the actual file comparison.
|
Err bool
|
||||||
sha256sum, err := i.getProviderChecksum("template", "0.1.0")
|
}{
|
||||||
if err != nil {
|
{
|
||||||
t.Fatal(err)
|
&response.TerraformProviderPlatformLocation{
|
||||||
|
ShasumsURL: "http://127.0.0.1:8080/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS",
|
||||||
|
ShasumsSignatureURL: "http://127.0.0.1:8080/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS.sig",
|
||||||
|
Filename: "terraform-provider-template_0.1.0_darwin_amd64.zip",
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&response.TerraformProviderPlatformLocation{
|
||||||
|
ShasumsURL: "http://127.0.0.1:8080/terraform-provider-badsig/0.1.0/terraform-provider-badsig_0.1.0_SHA256SUMS",
|
||||||
|
ShasumsSignatureURL: "http://127.0.0.1:8080/terraform-provider-badsig/0.1.0/terraform-provider-badsig_0.1.0_SHA256SUMS.sig",
|
||||||
|
Filename: "terraform-provider-template_0.1.0_darwin_amd64.zip",
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the expected checksum for our os/arch
|
i := ProviderInstaller{}
|
||||||
sumData, err := ioutil.ReadFile("testdata/terraform-provider-template_0.1.0_SHA256SUMS")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected := checksumForFile(sumData, i.providerFileName("template", "0.1.0"))
|
for _, test := range tests {
|
||||||
|
sha256sum, err := i.getProviderChecksum(test.URLs)
|
||||||
|
if test.Err {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("succeeded; want error")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
if sha256sum != expected {
|
// get the expected checksum for our os/arch
|
||||||
t.Fatalf("expected: %s\ngot %s\n", sha256sum, expected)
|
sumData, err := ioutil.ReadFile("testdata/terraform-provider-template_0.1.0_SHA256SUMS")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := checksumForFile(sumData, test.URLs.Filename)
|
||||||
|
|
||||||
|
if sha256sum != expected {
|
||||||
|
t.Fatalf("expected: %s\ngot %s\n", sha256sum, expected)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test fetching a provider's checksum file witha bad signature
|
// newProviderInstaller returns a minimally-initialized ProviderInstaller
|
||||||
func TestProviderChecksumBadSignature(t *testing.T) {
|
func newProviderInstaller(s *httptest.Server) ProviderInstaller {
|
||||||
i := &ProviderInstaller{}
|
return ProviderInstaller{
|
||||||
|
registry: registry.NewClient(Disco(s), nil),
|
||||||
// we only need the checksum, as getter is doing the actual file comparison.
|
OS: runtime.GOOS,
|
||||||
sha256sum, err := i.getProviderChecksum("badsig", "0.1.0")
|
Arch: runtime.GOARCH,
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const versionList = `<!DOCTYPE html>
|
// Disco return a *disco.Disco mapping registry.terraform.io, localhost,
|
||||||
<html>
|
// localhost.localdomain, and example.com to the test server.
|
||||||
<body>
|
func Disco(s *httptest.Server) *disco.Disco {
|
||||||
<ul>
|
services := map[string]interface{}{
|
||||||
<li>
|
// Note that both with and without trailing slashes are supported behaviours
|
||||||
<a href="../">../</a>
|
"modules.v1": fmt.Sprintf("%s/v1/modules", s.URL),
|
||||||
</li>
|
"providers.v1": fmt.Sprintf("%s/v1/providers", s.URL),
|
||||||
<li>
|
}
|
||||||
<a href="/terraform-provider-test/1.2.3/">terraform-provider-test_1.2.3</a>
|
d := disco.New()
|
||||||
</li>
|
|
||||||
<li>
|
d.ForceHostServices(svchost.Hostname("registry.terraform.io"), services)
|
||||||
<a href="/terraform-provider-test/1.2.1/">terraform-provider-test_1.2.1</a>
|
d.ForceHostServices(svchost.Hostname("localhost"), services)
|
||||||
</li>
|
d.ForceHostServices(svchost.Hostname("localhost.localdomain"), services)
|
||||||
<li>
|
d.ForceHostServices(svchost.Hostname("example.com"), services)
|
||||||
<a href="/terraform-provider-test/1.2.4/">terraform-provider-test_1.2.4</a>
|
return d
|
||||||
</li>
|
}
|
||||||
</ul>
|
|
||||||
<footer>
|
var versionList = response.TerraformProvider{
|
||||||
Proudly fronted by <a href="https://fastly.com/?utm_source=hashicorp" target="_TOP">Fastly</a>
|
ID: "test",
|
||||||
</footer>
|
Versions: []*response.TerraformProviderVersion{
|
||||||
</body>
|
{Version: "1.2.1"},
|
||||||
</html>
|
{Version: "1.2.3"},
|
||||||
`
|
{
|
||||||
|
Version: "1.2.4",
|
||||||
|
Protocols: []string{"4"},
|
||||||
|
Platforms: []*response.TerraformProviderPlatform{
|
||||||
|
{
|
||||||
|
OS: "darwin",
|
||||||
|
Arch: "amd64",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var downloadURLs = response.TerraformProviderPlatformLocation{
|
||||||
|
ShasumsURL: "https://registry.terraform.io/terraform-provider-template/1.2.4/terraform-provider-test_1.2.4_SHA256SUMS",
|
||||||
|
ShasumsSignatureURL: "https://registry.terraform.io/terraform-provider-template/1.2.4/terraform-provider-test_1.2.4_SHA256SUMS.sig",
|
||||||
|
Filename: "terraform-provider-template_1.2.4_darwin_amd64.zip",
|
||||||
|
DownloadURL: "http://127.0.0.1:8080/v1/providers/terraform-providers/terraform-provider-test/1.2.4/terraform-provider-test_1.2.4_darwin_amd64.zip",
|
||||||
|
}
|
||||||
|
|
|
@ -4,5 +4,5 @@ package regsrc
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// PublicRegistryHost is a FriendlyHost that represents the public registry.
|
// PublicRegistryHost is a FriendlyHost that represents the public registry.
|
||||||
PublicRegistryHost = NewFriendlyHost("tf-registry-staging.herokuapp.com")
|
PublicRegistryHost = NewFriendlyHost("registry.terraform.io")
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package response
|
package response
|
||||||
|
|
||||||
import version "github.com/hashicorp/go-version"
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
version "github.com/hashicorp/go-version"
|
||||||
|
)
|
||||||
|
|
||||||
// TerraformProvider is the response structure for all required information for
|
// TerraformProvider is the response structure for all required information for
|
||||||
// Terraform to choose a download URL. It must include all versions and all
|
// Terraform to choose a download URL. It must include all versions and all
|
||||||
|
@ -48,21 +52,15 @@ type TerraformProviderPlatformLocation struct {
|
||||||
ShasumsSignatureURL string `json:"shasums_signature_url"`
|
ShasumsSignatureURL string `json:"shasums_signature_url"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collection type implements the sort.Sort interface so that
|
// Collection type for TerraformProviderVersion
|
||||||
// an array of TerraformProviderVersion can be sorted.
|
|
||||||
type Collection []*TerraformProviderVersion
|
type Collection []*TerraformProviderVersion
|
||||||
|
|
||||||
func (v Collection) Len() int {
|
// Sort sorts versions from newest to oldest.
|
||||||
return len(v)
|
func (v Collection) Sort() {
|
||||||
}
|
sort.Slice(v, func(i, j int) bool {
|
||||||
|
versionA, _ := version.NewVersion(v[i].Version)
|
||||||
|
versionB, _ := version.NewVersion(v[j].Version)
|
||||||
|
|
||||||
func (v Collection) Less(i, j int) bool {
|
return versionA.GreaterThan(versionB)
|
||||||
versionA, _ := version.NewVersion(v[i].Version)
|
})
|
||||||
versionB, _ := version.NewVersion(v[j].Version)
|
|
||||||
|
|
||||||
return versionA.LessThan(versionB)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v Collection) Swap(i, j int) {
|
|
||||||
v[i], v[j] = v[j], v[i]
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue