forked from toolshed/abra
update
This commit is contained in:
@@ -7,4 +7,3 @@
|
||||
/bin
|
||||
dist/
|
||||
tests/integration/.bats
|
||||
.profile/
|
||||
|
||||
@@ -40,13 +40,13 @@ check:
|
||||
(echo "gofmt: formatting issue - run 'make format' to resolve" && exit 1)
|
||||
|
||||
test:
|
||||
@go test ./... -short -failfast -cover -v -p 1
|
||||
@go test ./... -short -cover -v -p 1
|
||||
|
||||
integration-test:
|
||||
@go test ./... -short=false -failfast -cover -v -p 1 -count=1
|
||||
|
||||
find-tests:
|
||||
@find . -iname "*_test.go"
|
||||
@find . -name "*_test.go"
|
||||
|
||||
find-integration-test:
|
||||
@find . -iname "*_e2e_test.go"
|
||||
|
||||
@@ -1,260 +0,0 @@
|
||||
package recipe
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"coopcloud.tech/abra/cli/internal"
|
||||
"coopcloud.tech/abra/pkg/client"
|
||||
"coopcloud.tech/abra/pkg/config"
|
||||
gitPkg "coopcloud.tech/abra/pkg/git"
|
||||
"coopcloud.tech/abra/pkg/log"
|
||||
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
||||
"coopcloud.tech/tagcmp"
|
||||
"github.com/distribution/reference"
|
||||
)
|
||||
|
||||
func TestUpgradePinning(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping e2e test in short mode")
|
||||
}
|
||||
|
||||
setupTestRecipeCatalogue(t)
|
||||
// Test recipe is at nginx:1.21.6 and unpinned
|
||||
// https://git.coopcloud.tech/toolshed/abra-test-recipe/commit/77d752b3c61a71bfb2c45f40ec07c9d626a0e580
|
||||
setupTestRecipe(t, "77d752b3c61a71bfb2c45f40ec07c9d626a0e580")
|
||||
|
||||
// We should find nginx >1.21.6 (minor version bump), upgrade to
|
||||
// it and pin to the digest. Test works by reading TUI output.
|
||||
out := runUpgrade(t, "abra-test-recipe", false, true, true, true)
|
||||
match := regexp.MustCompile(`tag upgraded and pinned from 1\.21\.6 to 1\.(\d+)\.\d+@sha256:[a-f0-9]{64}`).FindStringSubmatch(out)
|
||||
if match == nil {
|
||||
t.Fatalf("expected a pinned minor upgrade, got:\n%s", out)
|
||||
}
|
||||
if minor, _ := strconv.Atoi(match[1]); minor <= 21 {
|
||||
t.Fatalf("expected a minor version above 1.21, got:\n%s", out)
|
||||
}
|
||||
image := getActualRecipeImage(t, "abra-test-recipe", "app")
|
||||
if !regexp.MustCompile(`^nginx:1\.` + match[1] + `\.\d+@sha256:[a-f0-9]{64}$`).MatchString(image) {
|
||||
t.Fatalf("got image %s, want nginx:1.%s.x@sha256:<64 hex>", image, match[1])
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that --pindigests should pin a digest even if the recipe is at the latest version already.
|
||||
func TestPinningNoUpgrade(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping e2e test in short mode")
|
||||
}
|
||||
|
||||
setupTestRecipeCatalogue(t)
|
||||
// Test recipe is at nginx:1.21.6 and unpinned
|
||||
// https://git.coopcloud.tech/toolshed/abra-test-recipe/commit/77d752b3c61a71bfb2c45f40ec07c9d626a0e580
|
||||
setupTestRecipe(t, "77d752b3c61a71bfb2c45f40ec07c9d626a0e580")
|
||||
|
||||
// Upgrade with --pindigests should pin the current image, even if there's no new upgrade available.
|
||||
out := runUpgrade(t, "abra-test-recipe", true, false, true, true)
|
||||
if !strings.Contains(out, "tag upgraded and pinned from 1.21.6 to 1.21.6@sha256:") {
|
||||
t.Fatalf("expected current image to be pinned, got:\n%s", out)
|
||||
}
|
||||
image := getActualRecipeImage(t, "abra-test-recipe", "app")
|
||||
if !regexp.MustCompile(`^nginx:1\.21\.6@sha256:[a-f0-9]{64}$`).MatchString(image) {
|
||||
t.Fatalf("got image %s, want nginx:1.21.6@sha256:<64 hex>", image)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that --pindigests works on recipes that have already pinned their digest
|
||||
func TestOverwriteExistingPin(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping e2e test in short mode")
|
||||
}
|
||||
|
||||
setupTestRecipeCatalogue(t)
|
||||
// Test recipe is at nginx:1.21.5 and pinned
|
||||
// https://git.coopcloud.tech/toolshed/abra-test-recipe/commit/4f0642719d2e2a568f4350cf7eea182438d58be3
|
||||
setupTestRecipe(t, "4f0642719d2e2a568f4350cf7eea182438d58be3")
|
||||
|
||||
out := runUpgrade(t, "abra-test-recipe", true, false, false, true)
|
||||
if !strings.Contains(out, "tag upgraded and pinned from 1.21.5 to 1.21.6@sha256:") {
|
||||
t.Fatalf("should ignore --pindigests if recipe already pins, got:\n%s", out)
|
||||
}
|
||||
image := getActualRecipeImage(t, "abra-test-recipe", "app")
|
||||
if !regexp.MustCompile(`^nginx:1\.21\.6@sha256:[a-f0-9]{64}$`).MatchString(image) {
|
||||
t.Fatalf("got image %s, want 1.21.6@sha256...", image)
|
||||
}
|
||||
if strings.Contains(image, "0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31") {
|
||||
t.Fatalf("updated image tag but kept digest: %s", image)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpgradeAll(t *testing.T) {
|
||||
t.Skip() // Massive integration test, all recipes. 15+ minutes to run.
|
||||
if testing.Short() {
|
||||
t.Skip("skipping e2e test in short mode")
|
||||
}
|
||||
|
||||
setupTestRecipeCatalogue(t)
|
||||
|
||||
catalogue, err := recipePkg.ReadRecipeCatalogue(false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(catalogue) == 0 {
|
||||
t.Fatal("recipe catalogue is empty")
|
||||
}
|
||||
|
||||
for _, rm := range catalogue {
|
||||
t.Run(rm.Name, func(t *testing.T) {
|
||||
recipe := recipePkg.Get(rm.Name)
|
||||
if err := recipe.EnsureExists(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
runUpgrade(t, rm.Name, false, false, true, false)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func getActualRecipeImage(t *testing.T, recipe, service string) string {
|
||||
t.Helper()
|
||||
r := recipePkg.Get(recipe)
|
||||
cfg, err := r.GetComposeConfig(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, s := range cfg.Services {
|
||||
if s.Name == service {
|
||||
return s.Image
|
||||
}
|
||||
}
|
||||
t.Fatalf("service %s not found", service)
|
||||
return ""
|
||||
}
|
||||
|
||||
// Use a temporary ABRA_DIR for the whole run
|
||||
func TestMain(m *testing.M) {
|
||||
dir, err := os.MkdirTemp("", "abra-test-")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
os.Setenv("ABRA_DIR", dir)
|
||||
config.ABRA_DIR = dir
|
||||
config.CATALOGUE_DIR = filepath.Join(dir, "catalogue")
|
||||
config.RECIPES_JSON = filepath.Join(config.CATALOGUE_DIR, "recipes.json")
|
||||
|
||||
code := m.Run()
|
||||
os.RemoveAll(dir)
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
// Give each test its own empty recipes directory.
|
||||
func setupTestRecipeCatalogue(t *testing.T) {
|
||||
t.Helper()
|
||||
config.RECIPES_DIR = t.TempDir()
|
||||
}
|
||||
|
||||
// Use a test recipe at the given commit.
|
||||
func setupTestRecipe(t *testing.T, commit string) {
|
||||
t.Helper()
|
||||
trd := filepath.Join(config.RECIPES_DIR, "abra-test-recipe")
|
||||
if err := gitPkg.Clone(trd, "https://git.coopcloud.tech/toolshed/abra-test-recipe"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r := recipePkg.Get("abra-test-recipe")
|
||||
if _, err := r.EnsureVersion(commit); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func runUpgrade(t *testing.T, recipe string, patch, minor, pinDigests, chaos bool) string {
|
||||
t.Helper()
|
||||
|
||||
internal.NoInput = true
|
||||
internal.Offline = true
|
||||
internal.Chaos = chaos
|
||||
internal.Patch = patch
|
||||
internal.Minor = minor
|
||||
internal.PinDigests = pinDigests
|
||||
|
||||
var out bytes.Buffer
|
||||
log.Logger.SetOutput(&out)
|
||||
defer log.Logger.SetOutput(os.Stdout)
|
||||
oldFatal := log.Fatal
|
||||
log.Fatal = func(message any, keyvals ...any) {
|
||||
t.Fatalf("recipe upgrade %s: %v\n%s", recipe, message, out.String())
|
||||
}
|
||||
defer func() { log.Fatal = oldFatal }()
|
||||
|
||||
RecipeUpgradeCommand.Run(RecipeUpgradeCommand, []string{recipe})
|
||||
return out.String()
|
||||
}
|
||||
|
||||
func TestPinningWithNoRegistryUpgrades(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping e2e test in short mode")
|
||||
}
|
||||
img, err := reference.ParseNormalizedNamed("nginx")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tags, err := client.GetRegistryTags(img)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var latest tagcmp.Tag
|
||||
found := false
|
||||
for _, value := range tags {
|
||||
candidate, err := tagcmp.Parse(value)
|
||||
if err != nil || candidate.Suffix != "" {
|
||||
continue
|
||||
}
|
||||
if !found || latest.IsLessThan(candidate) {
|
||||
latest = candidate
|
||||
found = true
|
||||
}
|
||||
}
|
||||
current := "nginx:" + latest.String()
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
patch, pin, existing bool
|
||||
}{
|
||||
{"unpinned skip", true, false, false},
|
||||
{"pin current", true, true, false},
|
||||
{"keep existing pin", true, false, true},
|
||||
{"pin without bump flag", false, true, false},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
setupTestRecipeCatalogue(t)
|
||||
// https://git.coopcloud.tech/toolshed/abra-test-recipe/commit/77d752b3c61a71bfb2c45f40ec07c9d626a0e580
|
||||
setupTestRecipe(t, "77d752b3c61a71bfb2c45f40ec07c9d626a0e580")
|
||||
r := recipePkg.Get("abra-test-recipe")
|
||||
digest := ""
|
||||
if tc.existing {
|
||||
digest, err = client.GetImageMultiplatformDigest(img, latest.String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if _, err := r.UpdateTag("nginx", latest.String(), digest); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
out := runUpgrade(t, "abra-test-recipe", tc.patch, false, tc.pin, true)
|
||||
got := getActualRecipeImage(t, "abra-test-recipe", "app")
|
||||
if tc.pin || tc.existing {
|
||||
if !strings.HasPrefix(got, current+"@sha256:") {
|
||||
t.Fatalf("expected current tag pinned, got %s", got)
|
||||
}
|
||||
} else if got != current {
|
||||
t.Fatalf("got %s, want %s", got, current)
|
||||
}
|
||||
if strings.Contains(out, "can upgrade service:") {
|
||||
t.Fatalf("reported nonexistent upgrades: %s", out)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -46,8 +46,6 @@ func GetImageMultiplatformDigest(img reference.Named, tag string) (string, error
|
||||
return "", errors.New(i18n.G("failed to parse image %s:%s, saw: %s", img.Name(), tag, err.Error()))
|
||||
}
|
||||
|
||||
// Use the credentials of the docker cli, which enables using a private
|
||||
// registry by running docker login on the machine that abra is executed.
|
||||
rc := regclient.New(regclient.WithDockerCreds())
|
||||
|
||||
m, err := rc.ManifestHead(context.Background(), r)
|
||||
|
||||
Reference in New Issue
Block a user