Files
nomad/e2e/consulcompat/shared_download_test.go
Tim Gross d261d58ea2 build: update hc-install to current (#24199)
Installing Vault and Consul from releases.hashicorp.com via `hc-install` has
been failing intermittently. Update the `hc-install` binaries to be current and
add one retry to downloads for our compat tests so that we can get builds more
reliably green while the underlying issue is being debugged.
2024-10-15 10:07:58 -04:00

152 lines
3.9 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package consulcompat
import (
"context"
"encoding/json"
"os"
"os/exec"
"path/filepath"
"runtime"
"testing"
"time"
"github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/go-set/v3"
"github.com/hashicorp/go-version"
"github.com/shoenig/test/must"
)
const (
binDir = "consul-bins"
minConsulVersion = "1.16.0"
// environment variable to pick only one Consul version for testing
exactConsulVersionEnv = "NOMAD_E2E_CONSULCOMPAT_CONSUL_VERSION"
)
func downloadConsulBuild(t *testing.T, b build, baseDir string) {
path := filepath.Join(baseDir, binDir, b.Version)
must.NoError(t, os.MkdirAll(path, 0755))
if _, err := os.Stat(filepath.Join(path, "consul")); !os.IsNotExist(err) {
t.Log("download: already have consul at", path)
return
}
t.Log("download: installing consul at", path)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, "hc-install", "install",
"-version", b.Version, "-path", path, "consul")
bs, err := cmd.CombinedOutput()
if err != nil {
t.Logf("download: failed to download %s, retrying once: %v", b.Version, err)
cmd = exec.CommandContext(ctx, "hc-install", "install",
"-version", b.Version, "-path", path, "consul")
bs, err = cmd.CombinedOutput()
}
must.NoError(t, err, must.Sprintf("failed to download consul %s: %s", b.Version, string(bs)))
}
func getMinimumVersion(t *testing.T) *version.Version {
v, err := version.NewVersion(minConsulVersion)
must.NoError(t, err)
return v
}
type build struct {
Version string `json:"version"`
OS string `json:"os"`
Arch string `json:"arch"`
URL string `json:"url"`
}
func (b build) String() string { return b.Version }
func (b build) compare(o build) int {
B := version.Must(version.NewVersion(b.Version))
O := version.Must(version.NewVersion(o.Version))
return B.Compare(O)
}
type consulJSON struct {
Versions map[string]struct {
Builds []build `json:"builds"`
} `json:"versions"`
}
func keep(b build) bool {
exactVersion := os.Getenv(exactConsulVersionEnv)
if exactVersion != "" {
if b.Version != exactVersion {
return false
}
}
switch {
case b.OS != runtime.GOOS:
return false
case b.Arch != runtime.GOARCH:
return false
default:
return true
}
}
// A tracker keeps track of the set of patch versions for each minor version.
// The patch versions are stored in a treeset so we can grab the highest patch
// version of each minor version at the end.
type tracker map[int]*set.TreeSet[build]
func (t tracker) add(v *version.Version, b build) {
y := v.Segments()[1] // minor version
// create the treeset for this minor version if needed
if _, exists := t[y]; !exists {
cmp := func(g, h build) int { return g.compare(h) }
t[y] = set.NewTreeSet[build](cmp)
}
// insert the patch version into the set of patch versions for this minor version
t[y].Insert(b)
}
func scanConsulVersions(t *testing.T, minimum *version.Version) *set.Set[build] {
httpClient := cleanhttp.DefaultClient()
httpClient.Timeout = 1 * time.Minute
response, err := httpClient.Get("https://releases.hashicorp.com/consul/index.json")
must.NoError(t, err, must.Sprint("unable to download consul versions index"))
var payload consulJSON
must.NoError(t, json.NewDecoder(response.Body).Decode(&payload))
must.Close(t, response.Body)
// sort the versions for the Y in each consul version X.Y.Z
// this only works for consul 1.Y.Z which is fine for now
track := make(tracker)
for s, obj := range payload.Versions {
v, err := version.NewVersion(s)
must.NoError(t, err, must.Sprint("unable to parse consul version"))
if !usable(v, minimum) {
continue
}
for _, build := range obj.Builds {
if keep(build) {
track.add(v, build)
}
}
}
// take the latest patch version for each minor version
result := set.New[build](len(track))
for _, tree := range track {
max := tree.Max()
result.Insert(max)
}
return result
}