forked from toolshed/abra
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4181daac82 | ||
|
|
9475381e23 | ||
|
|
7b5c2f4723 | ||
|
|
3ba7470a55 |
@@ -304,9 +304,9 @@ interface.`),
|
||||
}
|
||||
resolvedDigest = digest
|
||||
|
||||
ok, err = recipe.UpdatePinnedTag(image, upgradeTag, digest)
|
||||
ok, err = recipe.UpdatePinnedTag(service.Name, upgradeTag, digest)
|
||||
} else {
|
||||
ok, err = recipe.UpdateTag(image, upgradeTag)
|
||||
ok, err = recipe.UpdateTag(service.Name, upgradeTag)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
||||
+12
-5
@@ -13,8 +13,6 @@ import (
|
||||
"github.com/regclient/regclient/types/ref"
|
||||
)
|
||||
|
||||
var rc = regclient.New()
|
||||
|
||||
// GetRegistryTags retrieves all tags of an image from a container registry.
|
||||
func GetRegistryTags(img reference.Named) ([]string, error) {
|
||||
var tags []string
|
||||
@@ -41,13 +39,22 @@ func GetRegistryTags(img reference.Named) ([]string, error) {
|
||||
func GetImageMultiplatformDigest(img reference.Named, tag string) (string, error) {
|
||||
r, err := ref.New(fmt.Sprintf("%s:%s", img.Name(), tag))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("parsing reference %s:%s: %w", img.Name(), tag, err)
|
||||
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)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("fetching manifest for %s:%s: %w", img.Name(), tag, err)
|
||||
return "", errors.New(i18n.G("failed to fetch manifest for %s:%s, saw: %s", img.Name(), tag, err.Error()))
|
||||
}
|
||||
|
||||
return m.GetDescriptor().Digest.String(), nil
|
||||
digest := m.GetDescriptor().Digest.String()
|
||||
if digest == "" {
|
||||
return "", errors.New(i18n.G("registry did not return a digest for %s:%s", img.Name(), tag))
|
||||
}
|
||||
|
||||
return digest, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/distribution/reference"
|
||||
)
|
||||
|
||||
func TestGetImageMultiplatformDigest(t *testing.T) {
|
||||
img, err := reference.ParseNormalizedNamed("nginx")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
digest, err := GetImageMultiplatformDigest(img, "1.21.6")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !regexp.MustCompile(`^sha256:[a-f0-9]{64}$`).MatchString(digest) {
|
||||
t.Fatalf("unexpected digest %s", digest)
|
||||
}
|
||||
|
||||
again, err := GetImageMultiplatformDigest(img, "1.21.6")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if again != digest {
|
||||
t.Errorf("got %s on second lookup, want %s", again, digest)
|
||||
}
|
||||
|
||||
other, err := GetImageMultiplatformDigest(img, "1.21.5")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if other == digest {
|
||||
t.Errorf("expected different digests for 1.21.5 and 1.21.6, got %s", digest)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetImageMultiplatformDigestMissingTag(t *testing.T) {
|
||||
img, err := reference.ParseNormalizedNamed("nginx")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := GetImageMultiplatformDigest(img, "this-tag-does-not-exist"); err == nil {
|
||||
t.Fatal("expected an error for a missing tag")
|
||||
}
|
||||
}
|
||||
+82
-59
@@ -7,7 +7,7 @@
|
||||
msgid ""
|
||||
msgstr "Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-09-10 00:13-0400\n"
|
||||
"POT-Creation-Date: 2026-09-29 21:11-0400\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -304,7 +304,7 @@ msgstr ""
|
||||
msgid "%s has no secrets to generate, skipping..."
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:107
|
||||
#: ./pkg/recipe/compose.go:108
|
||||
#, c-format
|
||||
msgid "%s has no version label? try running \"abra recipe sync %s\" first?"
|
||||
msgstr ""
|
||||
@@ -324,12 +324,12 @@ msgstr ""
|
||||
msgid "%s is already fetched"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:299
|
||||
#: ./pkg/recipe/compose.go:270
|
||||
#, c-format
|
||||
msgid "%s is already set, nothing to do?"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:72
|
||||
#: ./pkg/recipe/compose.go:73
|
||||
#, c-format
|
||||
msgid "%s is missing a compose.yml or compose.*.yml file?"
|
||||
msgstr ""
|
||||
@@ -568,12 +568,12 @@ msgstr ""
|
||||
msgid "--local detected, running %s on local work station"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:261 ./pkg/recipe/files.go:18
|
||||
#: ./pkg/lint/recipe.go:268 ./pkg/recipe/files.go:18
|
||||
#, c-format
|
||||
msgid ".env.sample for %s couldn't be read: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:133
|
||||
#: ./pkg/lint/recipe.go:140
|
||||
msgid ".env.sample provided"
|
||||
msgstr ""
|
||||
|
||||
@@ -638,17 +638,17 @@ msgstr ""
|
||||
msgid "CHAOS DEPLOY"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:47
|
||||
#: ./pkg/recipe/compose.go:48
|
||||
#, c-format
|
||||
msgid "COMPOSE_FILE (=\"%s\") parsing failed?"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:58
|
||||
#: ./pkg/recipe/compose.go:59
|
||||
#, c-format
|
||||
msgid "COMPOSE_FILE detected (%s), loading %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:38
|
||||
#: ./pkg/recipe/compose.go:39
|
||||
#, c-format
|
||||
msgid "COMPOSE_FILE detected, loading %s"
|
||||
msgstr ""
|
||||
@@ -1644,7 +1644,7 @@ msgid "\n"
|
||||
"\n"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:203
|
||||
#: ./pkg/lint/recipe.go:210
|
||||
#, c-format
|
||||
msgid "\n"
|
||||
"lint %s: %s"
|
||||
@@ -1737,7 +1737,7 @@ msgstr ""
|
||||
msgid "accepts at most 2 args with --local/-l"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:315
|
||||
#: ./pkg/recipe/compose.go:286
|
||||
#, c-format
|
||||
msgid "add '- \"%s\"' manually to the 'app' service in %s"
|
||||
msgstr ""
|
||||
@@ -1764,11 +1764,15 @@ msgstr ""
|
||||
msgid "all containers did really go away"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:131
|
||||
msgid "all images use a digest"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:82
|
||||
msgid "all images use a tag"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:162
|
||||
#: ./pkg/lint/recipe.go:169
|
||||
msgid "all services have images"
|
||||
msgstr ""
|
||||
|
||||
@@ -2221,7 +2225,7 @@ msgstr ""
|
||||
msgid "config not found: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:169
|
||||
#: ./pkg/lint/recipe.go:176
|
||||
msgid "config version are vendored"
|
||||
msgstr ""
|
||||
|
||||
@@ -2230,14 +2234,14 @@ msgstr ""
|
||||
msgid "connection timed out for %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:257
|
||||
#: ./pkg/recipe/compose.go:144
|
||||
#, c-format
|
||||
msgid "considering %s config(s) for label update"
|
||||
msgid "considering %s config(s) for image update"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:123
|
||||
#: ./pkg/recipe/compose.go:228
|
||||
#, c-format
|
||||
msgid "considering %s config(s) for tag update"
|
||||
msgid "considering %s config(s) for label update"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/undeploy.go:141 ./cli/server/prune.go:53
|
||||
@@ -2258,7 +2262,7 @@ msgstr ""
|
||||
msgid "context lacks Docker endpoint"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:295
|
||||
#: ./pkg/recipe/compose.go:266
|
||||
#, c-format
|
||||
msgid "coop-cloud.${STACK_NAME}.version=%s"
|
||||
msgstr ""
|
||||
@@ -2303,7 +2307,7 @@ msgstr ""
|
||||
msgid "create <domain> [flags]"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:134
|
||||
#: ./pkg/lint/recipe.go:141
|
||||
msgid "create an example .env.sample"
|
||||
msgstr ""
|
||||
|
||||
@@ -2418,7 +2422,7 @@ msgstr ""
|
||||
msgid "deploy in progress 🟠"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:147
|
||||
#: ./pkg/lint/recipe.go:154
|
||||
msgid "deploy labels stanza present"
|
||||
msgstr ""
|
||||
|
||||
@@ -2644,7 +2648,7 @@ msgstr ""
|
||||
msgid "ensure 'version: \"3.8\"' in compose configs"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:163
|
||||
#: ./pkg/lint/recipe.go:170
|
||||
msgid "ensure \"image: ...\" set on all services"
|
||||
msgstr ""
|
||||
|
||||
@@ -2682,7 +2686,7 @@ msgstr ""
|
||||
msgid "err: %v, "
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/lint.go:82 ./cli/recipe/lint.go:144 ./pkg/lint/recipe.go:132 ./pkg/lint/recipe.go:139 ./pkg/lint/recipe.go:146 ./pkg/lint/recipe.go:153 ./pkg/lint/recipe.go:161 ./pkg/lint/recipe.go:168 ./pkg/lint/recipe.go:175
|
||||
#: ./cli/recipe/lint.go:82 ./cli/recipe/lint.go:144 ./pkg/lint/recipe.go:139 ./pkg/lint/recipe.go:146 ./pkg/lint/recipe.go:153 ./pkg/lint/recipe.go:160 ./pkg/lint/recipe.go:168 ./pkg/lint/recipe.go:175 ./pkg/lint/recipe.go:182
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
@@ -2801,6 +2805,11 @@ msgstr ""
|
||||
msgid "failed to create volume %s on %s: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/client/registry.go:51
|
||||
#, c-format
|
||||
msgid "failed to fetch manifest for %s:%s, saw: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/secret/secret.go:104
|
||||
#, c-format
|
||||
msgid "failed to generate random bytes: %w"
|
||||
@@ -2839,11 +2848,16 @@ msgstr ""
|
||||
msgid "failed to migrate app config: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/client/registry.go:24
|
||||
#: ./pkg/client/registry.go:22
|
||||
#, c-format
|
||||
msgid "failed to parse image %s, saw: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/client/registry.go:42
|
||||
#, c-format
|
||||
msgid "failed to parse image %s:%s, saw: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:437
|
||||
#, c-format
|
||||
msgid "failed to parse image for %s in %s: %s"
|
||||
@@ -3216,11 +3230,11 @@ msgstr ""
|
||||
msgid "images pruned: %d; space reclaimed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:148
|
||||
#: ./pkg/lint/recipe.go:155
|
||||
msgid "include \"deploy: labels: ...\" stanza"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:155
|
||||
#: ./pkg/lint/recipe.go:162
|
||||
msgid "include \"traefik.enable=true\" deploy label"
|
||||
msgstr ""
|
||||
|
||||
@@ -3374,7 +3388,7 @@ msgstr ""
|
||||
msgid "invalid detach escape keys, using default: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:504
|
||||
#: ./pkg/lint/recipe.go:533
|
||||
msgid "invalid lightweight tag detected"
|
||||
msgstr ""
|
||||
|
||||
@@ -3435,12 +3449,12 @@ msgstr ""
|
||||
msgid "lint <recipe> [flags]"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:187
|
||||
#: ./pkg/lint/recipe.go:194
|
||||
#, c-format
|
||||
msgid "linting for critical errors in %s configs"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:215
|
||||
#: ./pkg/lint/recipe.go:222
|
||||
#, c-format
|
||||
msgid "linting successful, %s is well configured"
|
||||
msgstr ""
|
||||
@@ -3632,7 +3646,7 @@ msgstr ""
|
||||
msgid "name"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:141
|
||||
#: ./pkg/lint/recipe.go:148
|
||||
msgid "name a servce 'app'"
|
||||
msgstr ""
|
||||
|
||||
@@ -3710,7 +3724,7 @@ msgstr ""
|
||||
msgid "no .git directory in %s?"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:29
|
||||
#: ./pkg/recipe/compose.go:30
|
||||
#, c-format
|
||||
msgid "no COMPOSE_FILE detected, loading default: %s"
|
||||
msgstr ""
|
||||
@@ -3761,7 +3775,7 @@ msgstr ""
|
||||
msgid "no domain provided"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:314
|
||||
#: ./pkg/recipe/compose.go:285
|
||||
msgid "no existing label found, automagic insertion not supported yet"
|
||||
msgstr ""
|
||||
|
||||
@@ -3933,11 +3947,11 @@ msgstr ""
|
||||
msgid "one of <src>/<dest> arguments must take $SERVICE:$PATH form"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:140
|
||||
#: ./pkg/lint/recipe.go:147
|
||||
msgid "one service named 'app'"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:176
|
||||
#: ./pkg/lint/recipe.go:183
|
||||
msgid "only annotated tags used for recipe version"
|
||||
msgstr ""
|
||||
|
||||
@@ -3971,11 +3985,6 @@ msgstr ""
|
||||
msgid "parsed %s for %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:160
|
||||
#, c-format
|
||||
msgid "parsed %s from %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/cmd.go:134 ./cli/app/cmd.go:180
|
||||
#, c-format
|
||||
msgid "parsed following command arguments: %s"
|
||||
@@ -4224,7 +4233,7 @@ msgstr ""
|
||||
msgid "recipe %s does not exist?"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:212
|
||||
#: ./pkg/lint/recipe.go:219
|
||||
#, c-format
|
||||
msgid "recipe '%s' failed lint checks:\n"
|
||||
"%s"
|
||||
@@ -4248,6 +4257,11 @@ msgstr ""
|
||||
msgid "ref"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/client/registry.go:56
|
||||
#, c-format
|
||||
msgid "registry did not return a digest for %s:%s"
|
||||
msgstr ""
|
||||
|
||||
#. translators: `recipe release` command
|
||||
#: ./cli/recipe/release.go:35
|
||||
msgid "release <recipe> [version] [flags]"
|
||||
@@ -4346,6 +4360,11 @@ msgstr ""
|
||||
msgid "removing configs failed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:120
|
||||
#, c-format
|
||||
msgid "removing digest from %s for %s, use --pindigests to pin the new tag"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/upstream/stack/remove.go:148
|
||||
#, c-format
|
||||
msgid "removing network %s"
|
||||
@@ -4371,7 +4390,7 @@ msgstr ""
|
||||
msgid "removing volumes failed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:177
|
||||
#: ./pkg/lint/recipe.go:184
|
||||
msgid "replace lightweight tag with annotated tag"
|
||||
msgstr ""
|
||||
|
||||
@@ -4454,7 +4473,7 @@ msgstr ""
|
||||
msgid "retrieved %s as target container on %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:59
|
||||
#: ./pkg/recipe/compose.go:60
|
||||
#, c-format
|
||||
msgid "retrieved %s configs for %s"
|
||||
msgstr ""
|
||||
@@ -4625,7 +4644,7 @@ msgstr ""
|
||||
msgid "secret %s is > %d chars when combined with %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:481
|
||||
#: ./pkg/lint/recipe.go:510
|
||||
#, c-format
|
||||
msgid "secret %s is longer than 12 characters"
|
||||
msgstr ""
|
||||
@@ -5025,7 +5044,7 @@ msgstr ""
|
||||
msgid "sync [flags]"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:309
|
||||
#: ./pkg/recipe/compose.go:280
|
||||
#, c-format
|
||||
msgid "synced label %s to service %s"
|
||||
msgstr ""
|
||||
@@ -5122,7 +5141,7 @@ msgstr ""
|
||||
msgid "toggle non-interactive mode"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:154
|
||||
#: ./pkg/lint/recipe.go:161
|
||||
msgid "traefik routing enabled"
|
||||
msgstr ""
|
||||
|
||||
@@ -5250,6 +5269,11 @@ msgstr ""
|
||||
msgid "unable to fetch tags in %s: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:181
|
||||
#, c-format
|
||||
msgid "unable to find image %s for %s in %s, please update it manually"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/move.go:301
|
||||
#, c-format
|
||||
msgid "unable to get container matching %s: %s"
|
||||
@@ -5270,7 +5294,7 @@ msgstr ""
|
||||
msgid "unable to inspect container for %s: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:496
|
||||
#: ./pkg/lint/recipe.go:525
|
||||
#, c-format
|
||||
msgid "unable to list local tags for %s"
|
||||
msgstr ""
|
||||
@@ -5284,7 +5308,7 @@ msgstr ""
|
||||
msgid "unable to look up server context for %s: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/fetch.go:77 ./pkg/git/read.go:26 ./pkg/lint/recipe.go:491 ./pkg/recipe/git.go:243
|
||||
#: ./cli/recipe/fetch.go:77 ./pkg/git/read.go:26 ./pkg/lint/recipe.go:520 ./pkg/recipe/git.go:243
|
||||
#, c-format
|
||||
msgid "unable to open %s: %s"
|
||||
msgstr ""
|
||||
@@ -5314,11 +5338,6 @@ msgstr ""
|
||||
msgid "unable to parse %s, error was: %s, skipping upgrade for %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:154
|
||||
#, c-format
|
||||
msgid "unable to parse %s, skipping"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/list.go:167
|
||||
#, c-format
|
||||
msgid "unable to parse %s, skipping as upgrade option"
|
||||
@@ -5334,6 +5353,11 @@ msgstr ""
|
||||
msgid "unable to parse deploy version: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:204
|
||||
#, c-format
|
||||
msgid "unable to parse image %s: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/run.go:176
|
||||
#, c-format
|
||||
msgid "unable to proceed, %s does not exist?"
|
||||
@@ -5541,16 +5565,11 @@ msgstr ""
|
||||
msgid "updating %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:176 ./pkg/recipe/compose.go:303
|
||||
#: ./pkg/recipe/compose.go:187 ./pkg/recipe/compose.go:274
|
||||
#, c-format
|
||||
msgid "updating %s to %s in %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:236
|
||||
#, c-format
|
||||
msgid "updating pinned reference %s to %s in %s"
|
||||
msgstr ""
|
||||
|
||||
#. translators: `abra upgrade` command for autocompletion
|
||||
#: ./cli/run.go:103
|
||||
msgid "upgrade"
|
||||
@@ -5590,6 +5609,10 @@ msgstr ""
|
||||
msgid "upload your recipe to git.coopcloud.tech/coop-cloud/..."
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:132
|
||||
msgid "use a digest for all images"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:83
|
||||
msgid "use a tag for all images"
|
||||
msgstr ""
|
||||
@@ -5640,7 +5663,7 @@ msgstr ""
|
||||
msgid "validated %s as server argument"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:170
|
||||
#: ./pkg/lint/recipe.go:177
|
||||
msgid "vendor config versions in an abra.sh"
|
||||
msgstr ""
|
||||
|
||||
@@ -5791,7 +5814,7 @@ msgstr ""
|
||||
msgid "waiting for %d task(s) to reach terminal state"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:21 ./pkg/lint/recipe.go:67 ./pkg/lint/recipe.go:74 ./pkg/lint/recipe.go:81 ./pkg/lint/recipe.go:88 ./pkg/lint/recipe.go:95 ./pkg/lint/recipe.go:102 ./pkg/lint/recipe.go:109 ./pkg/lint/recipe.go:116 ./pkg/lint/recipe.go:123
|
||||
#: ./pkg/lint/recipe.go:21 ./pkg/lint/recipe.go:67 ./pkg/lint/recipe.go:74 ./pkg/lint/recipe.go:81 ./pkg/lint/recipe.go:88 ./pkg/lint/recipe.go:95 ./pkg/lint/recipe.go:102 ./pkg/lint/recipe.go:109 ./pkg/lint/recipe.go:116 ./pkg/lint/recipe.go:123 ./pkg/lint/recipe.go:130
|
||||
msgid "warn"
|
||||
msgstr ""
|
||||
|
||||
|
||||
Binary file not shown.
+106
-65
@@ -2,7 +2,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-09-10 00:13-0400\n"
|
||||
"POT-Creation-Date: 2026-09-29 21:11-0400\n"
|
||||
"PO-Revision-Date: 2026-09-01 22:35+0000\n"
|
||||
"Last-Translator: chasqui <chasqui@cryptolab.net>\n"
|
||||
"Language-Team: Spanish <https://translate.coopcloud.tech/projects/co-op-"
|
||||
@@ -438,7 +438,7 @@ msgstr "¿%s no tiene versiones publicadas?"
|
||||
msgid "%s has no secrets to generate, skipping..."
|
||||
msgstr "%s no tiene secretos para generar, omitiendo..."
|
||||
|
||||
#: pkg/recipe/compose.go:107
|
||||
#: pkg/recipe/compose.go:108
|
||||
#, c-format
|
||||
msgid "%s has no version label? try running \"abra recipe sync %s\" first?"
|
||||
msgstr ""
|
||||
@@ -460,12 +460,12 @@ msgstr "%s ya está desplegado"
|
||||
msgid "%s is already fetched"
|
||||
msgstr "%s ya se ha obtenido"
|
||||
|
||||
#: pkg/recipe/compose.go:299
|
||||
#: pkg/recipe/compose.go:270
|
||||
#, c-format
|
||||
msgid "%s is already set, nothing to do?"
|
||||
msgstr "%s ya está establecido, ¿nada qué hacer?"
|
||||
|
||||
#: pkg/recipe/compose.go:72
|
||||
#: pkg/recipe/compose.go:73
|
||||
#, c-format
|
||||
msgid "%s is missing a compose.yml or compose.*.yml file?"
|
||||
msgstr "¿%s no tiene un archivo compose.yml o compose.*.yml?"
|
||||
@@ -715,12 +715,12 @@ msgstr ""
|
||||
msgid "--local detected, running %s on local work station"
|
||||
msgstr "--local detectado, ejecutando %s en la estación de trabajo local"
|
||||
|
||||
#: pkg/lint/recipe.go:261 pkg/recipe/files.go:18
|
||||
#: pkg/lint/recipe.go:268 pkg/recipe/files.go:18
|
||||
#, c-format
|
||||
msgid ".env.sample for %s couldn't be read: %s"
|
||||
msgstr "archivo de entorno env.sample para %s no pudo leerse: %s"
|
||||
|
||||
#: pkg/lint/recipe.go:133
|
||||
#: pkg/lint/recipe.go:140
|
||||
msgid ".env.sample provided"
|
||||
msgstr "Se proporcionó .env.sample"
|
||||
|
||||
@@ -831,17 +831,17 @@ msgstr "🌀 CAOS"
|
||||
msgid "CHAOS DEPLOY"
|
||||
msgstr "DESPLIEGUE CAÓTICO"
|
||||
|
||||
#: pkg/recipe/compose.go:47
|
||||
#: pkg/recipe/compose.go:48
|
||||
#, c-format
|
||||
msgid "COMPOSE_FILE (=\"%s\") parsing failed?"
|
||||
msgstr "COMPOSE_FILE (=\"%s\") ¿Fallo el análisis?"
|
||||
|
||||
#: pkg/recipe/compose.go:58
|
||||
#: pkg/recipe/compose.go:59
|
||||
#, c-format
|
||||
msgid "COMPOSE_FILE detected (%s), loading %s"
|
||||
msgstr "COMPOSE_FILE detectado (%s), cargando %s"
|
||||
|
||||
#: pkg/recipe/compose.go:38
|
||||
#: pkg/recipe/compose.go:39
|
||||
#, c-format
|
||||
msgid "COMPOSE_FILE detected, loading %s"
|
||||
msgstr "COMPOSE_FILE detectado, cargando %s"
|
||||
@@ -2387,7 +2387,7 @@ msgstr ""
|
||||
"\"nada de qué preocuparse\".\n"
|
||||
"\n"
|
||||
|
||||
#: pkg/lint/recipe.go:203
|
||||
#: pkg/lint/recipe.go:210
|
||||
#, c-format
|
||||
msgid ""
|
||||
"\n"
|
||||
@@ -2481,7 +2481,7 @@ msgstr ""
|
||||
msgid "accepts at most 2 args with --local/-l"
|
||||
msgstr "acepta como máximo 2 argumentos con --local/-l"
|
||||
|
||||
#: pkg/recipe/compose.go:315
|
||||
#: pkg/recipe/compose.go:286
|
||||
#, c-format
|
||||
msgid "add '- \"%s\"' manually to the 'app' service in %s"
|
||||
msgstr "agrega '- \"%s\"' manualmente al servicio de la 'app' en %s"
|
||||
@@ -2509,11 +2509,16 @@ msgstr "todos"
|
||||
msgid "all containers did really go away"
|
||||
msgstr "todos los contenedores realmente desaparecieron"
|
||||
|
||||
#: pkg/lint/recipe.go:131
|
||||
#, fuzzy
|
||||
msgid "all images use a digest"
|
||||
msgstr "todas las imágenes usan una etiqueta"
|
||||
|
||||
#: pkg/lint/recipe.go:82
|
||||
msgid "all images use a tag"
|
||||
msgstr "todas las imágenes usan una etiqueta"
|
||||
|
||||
#: pkg/lint/recipe.go:162
|
||||
#: pkg/lint/recipe.go:169
|
||||
msgid "all services have images"
|
||||
msgstr "todos los servicios tienen imágenes"
|
||||
|
||||
@@ -3003,7 +3008,7 @@ msgstr "archivo de configuración cargado desde: %s"
|
||||
msgid "config not found: %s"
|
||||
msgstr "configuración no encontrada: %s"
|
||||
|
||||
#: pkg/lint/recipe.go:169
|
||||
#: pkg/lint/recipe.go:176
|
||||
msgid "config version are vendored"
|
||||
msgstr "la versión de configuración está vendida"
|
||||
|
||||
@@ -3012,19 +3017,19 @@ msgstr "la versión de configuración está vendida"
|
||||
msgid "connection timed out for %s"
|
||||
msgstr "tiempo de conexión agotado para %s"
|
||||
|
||||
#: pkg/recipe/compose.go:257
|
||||
#: pkg/recipe/compose.go:144
|
||||
#, fuzzy, c-format
|
||||
msgid "considering %s config(s) for image update"
|
||||
msgstr ""
|
||||
"considerando la(s) configuración(es) %s para la actualización de etiquetas "
|
||||
"(tags)"
|
||||
|
||||
#: pkg/recipe/compose.go:228
|
||||
#, c-format
|
||||
msgid "considering %s config(s) for label update"
|
||||
msgstr ""
|
||||
"considerando la(s) configuración(es) %s para la actualización de etiquetas"
|
||||
|
||||
#: pkg/recipe/compose.go:123
|
||||
#, c-format
|
||||
msgid "considering %s config(s) for tag update"
|
||||
msgstr ""
|
||||
"considerando la(s) configuración(es) %s para la actualización de etiquetas "
|
||||
"(tags)"
|
||||
|
||||
#: cli/app/undeploy.go:141 cli/server/prune.go:53
|
||||
#, c-format
|
||||
msgid "containers pruned: %d; space reclaimed: %s"
|
||||
@@ -3043,7 +3048,7 @@ msgstr "el contexto para %s ya existe"
|
||||
msgid "context lacks Docker endpoint"
|
||||
msgstr "el contexto no tiene punto final de Docker"
|
||||
|
||||
#: pkg/recipe/compose.go:295
|
||||
#: pkg/recipe/compose.go:266
|
||||
#, c-format
|
||||
msgid "coop-cloud.${STACK_NAME}.version=%s"
|
||||
msgstr ""
|
||||
@@ -3088,7 +3093,7 @@ msgstr "copiar <aplicacion> <origen> <destino> [opciones]"
|
||||
msgid "create <domain> [flags]"
|
||||
msgstr "crear <aplicacion> [opciones]"
|
||||
|
||||
#: pkg/lint/recipe.go:134
|
||||
#: pkg/lint/recipe.go:141
|
||||
msgid "create an example .env.sample"
|
||||
msgstr "crear un ejemplo de .env.sample"
|
||||
|
||||
@@ -3204,7 +3209,7 @@ msgstr "argh! falló el despliegue 🛑"
|
||||
msgid "deploy in progress 🟠"
|
||||
msgstr "despliegue en proceso 🟠"
|
||||
|
||||
#: pkg/lint/recipe.go:147
|
||||
#: pkg/lint/recipe.go:154
|
||||
msgid "deploy labels stanza present"
|
||||
msgstr "stanza de etiquetas de despliegue presente"
|
||||
|
||||
@@ -3435,7 +3440,7 @@ msgid "ensure 'version: \"3.8\"' in compose configs"
|
||||
msgstr ""
|
||||
"asegúrate que 'version: \"3.8\"' esté en las configuraciones de compose"
|
||||
|
||||
#: pkg/lint/recipe.go:163
|
||||
#: pkg/lint/recipe.go:170
|
||||
msgid "ensure \"image: ...\" set on all services"
|
||||
msgstr "asegúrate que \"image: ...\" esté establecido en todos los servicios"
|
||||
|
||||
@@ -3477,9 +3482,9 @@ msgstr "archivo de entorno (env) para %s tiene problemas: %s"
|
||||
msgid "err: %v, "
|
||||
msgstr "error: %v, "
|
||||
|
||||
#: cli/recipe/lint.go:82 cli/recipe/lint.go:144 pkg/lint/recipe.go:132
|
||||
#: pkg/lint/recipe.go:139 pkg/lint/recipe.go:146 pkg/lint/recipe.go:153
|
||||
#: pkg/lint/recipe.go:161 pkg/lint/recipe.go:168 pkg/lint/recipe.go:175
|
||||
#: cli/recipe/lint.go:82 cli/recipe/lint.go:144 pkg/lint/recipe.go:139
|
||||
#: pkg/lint/recipe.go:146 pkg/lint/recipe.go:153 pkg/lint/recipe.go:160
|
||||
#: pkg/lint/recipe.go:168 pkg/lint/recipe.go:175 pkg/lint/recipe.go:182
|
||||
msgid "error"
|
||||
msgstr "error"
|
||||
|
||||
@@ -3602,6 +3607,11 @@ msgstr "🛑 argh! error al crear secreto %s"
|
||||
msgid "failed to create volume %s on %s: %s"
|
||||
msgstr "🛑 error al crear el volumen %s en %s: %s"
|
||||
|
||||
#: pkg/client/registry.go:51
|
||||
#, fuzzy, c-format
|
||||
msgid "failed to fetch manifest for %s:%s, saw: %s"
|
||||
msgstr "🛑 error al analizar la imagen %s; en: %s: %s"
|
||||
|
||||
#: pkg/secret/secret.go:104
|
||||
#, c-format
|
||||
msgid "failed to generate random bytes: %w"
|
||||
@@ -3640,11 +3650,16 @@ msgstr "🛑 error al coincidir con el servicio seleccionado"
|
||||
msgid "failed to migrate app config: %s"
|
||||
msgstr "🛑 error al migrar la configuración de la aplicación: %s"
|
||||
|
||||
#: pkg/client/registry.go:24
|
||||
#: pkg/client/registry.go:22
|
||||
#, c-format
|
||||
msgid "failed to parse image %s, saw: %s"
|
||||
msgstr "🛑 error al analizar la imagen %s; detectado: %s"
|
||||
|
||||
#: pkg/client/registry.go:42
|
||||
#, fuzzy, c-format
|
||||
msgid "failed to parse image %s:%s, saw: %s"
|
||||
msgstr "🛑 error al analizar la imagen %s; detectado: %s"
|
||||
|
||||
#: pkg/recipe/git.go:437
|
||||
#, c-format
|
||||
msgid "failed to parse image for %s in %s: %s"
|
||||
@@ -4027,11 +4042,11 @@ msgstr "Ignorar cambios no confirmados en recetas"
|
||||
msgid "images pruned: %d; space reclaimed: %s"
|
||||
msgstr "imágenes descartadas: %d; espacio recuperado: %s"
|
||||
|
||||
#: pkg/lint/recipe.go:148
|
||||
#: pkg/lint/recipe.go:155
|
||||
msgid "include \"deploy: labels: ...\" stanza"
|
||||
msgstr "incluir el bloque \"deploy: labels: ...\" stanza"
|
||||
|
||||
#: pkg/lint/recipe.go:155
|
||||
#: pkg/lint/recipe.go:162
|
||||
msgid "include \"traefik.enable=true\" deploy label"
|
||||
msgstr "incluir la etiqueta de despliegue \"traefik.enable=true\""
|
||||
|
||||
@@ -4196,7 +4211,7 @@ msgstr ""
|
||||
"teclas de escape de separación inválidas, utilizando el valor "
|
||||
"predeterminado: %s"
|
||||
|
||||
#: pkg/lint/recipe.go:504
|
||||
#: pkg/lint/recipe.go:533
|
||||
msgid "invalid lightweight tag detected"
|
||||
msgstr "etiqueta ligera inválida detectada"
|
||||
|
||||
@@ -4258,12 +4273,12 @@ msgstr ""
|
||||
msgid "lint <recipe> [flags]"
|
||||
msgstr "validar <receta> [opciones]"
|
||||
|
||||
#: pkg/lint/recipe.go:187
|
||||
#: pkg/lint/recipe.go:194
|
||||
#, c-format
|
||||
msgid "linting for critical errors in %s configs"
|
||||
msgstr "buscando errores críticos en las configuraciones de %s"
|
||||
|
||||
#: pkg/lint/recipe.go:215
|
||||
#: pkg/lint/recipe.go:222
|
||||
#, c-format
|
||||
msgid "linting successful, %s is well configured"
|
||||
msgstr "linting exitoso, %s está bien configurado"
|
||||
@@ -4468,7 +4483,7 @@ msgstr ""
|
||||
msgid "name"
|
||||
msgstr "nombre"
|
||||
|
||||
#: pkg/lint/recipe.go:141
|
||||
#: pkg/lint/recipe.go:148
|
||||
msgid "name a servce 'app'"
|
||||
msgstr "nombrar un servicio 'app'"
|
||||
|
||||
@@ -4557,7 +4572,7 @@ msgstr "no se encontró etiqueta %s para %s"
|
||||
msgid "no .git directory in %s?"
|
||||
msgstr "¿no hay un directorio .git en %s?"
|
||||
|
||||
#: pkg/recipe/compose.go:29
|
||||
#: pkg/recipe/compose.go:30
|
||||
#, c-format
|
||||
msgid "no COMPOSE_FILE detected, loading default: %s"
|
||||
msgstr "no se detectó COMPOSE_FILE, cargando el predeterminado: %s"
|
||||
@@ -4608,7 +4623,7 @@ msgstr "no se encontraron contenedores que coincidan con el filtro %v?"
|
||||
msgid "no domain provided"
|
||||
msgstr "no se proporcionó dominio"
|
||||
|
||||
#: pkg/recipe/compose.go:314
|
||||
#: pkg/recipe/compose.go:285
|
||||
msgid "no existing label found, automagic insertion not supported yet"
|
||||
msgstr ""
|
||||
"no se encontró ninguna etiqueta existente, la inserción automágica no es "
|
||||
@@ -4792,11 +4807,11 @@ msgstr ""
|
||||
msgid "one of <src>/<dest> arguments must take $SERVICE:$PATH form"
|
||||
msgstr "uno de los argumentos <src>/<dest> debe tener la forma $SERVICE:$PATH"
|
||||
|
||||
#: pkg/lint/recipe.go:140
|
||||
#: pkg/lint/recipe.go:147
|
||||
msgid "one service named 'app'"
|
||||
msgstr "un servicio llamado 'app'"
|
||||
|
||||
#: pkg/lint/recipe.go:176
|
||||
#: pkg/lint/recipe.go:183
|
||||
msgid "only annotated tags used for recipe version"
|
||||
msgstr "solo se usaron etiquetas anotadas para la versión de la receta"
|
||||
|
||||
@@ -4834,11 +4849,6 @@ msgstr "arriba"
|
||||
msgid "parsed %s for %s"
|
||||
msgstr "parseado %s para %s"
|
||||
|
||||
#: pkg/recipe/compose.go:160
|
||||
#, c-format
|
||||
msgid "parsed %s from %s"
|
||||
msgstr "parseado %s de %s"
|
||||
|
||||
#: cli/app/cmd.go:134 cli/app/cmd.go:180
|
||||
#, c-format
|
||||
msgid "parsed following command arguments: %s"
|
||||
@@ -5099,7 +5109,7 @@ msgstr "receta"
|
||||
msgid "recipe %s does not exist?"
|
||||
msgstr "¿la receta %s no existe?"
|
||||
|
||||
#: pkg/lint/recipe.go:212
|
||||
#: pkg/lint/recipe.go:219
|
||||
#, c-format
|
||||
msgid ""
|
||||
"recipe '%s' failed lint checks:\n"
|
||||
@@ -5126,6 +5136,11 @@ msgstr "reducir la longitud de los nombres secretos a 12 caracteres"
|
||||
msgid "ref"
|
||||
msgstr ""
|
||||
|
||||
#: pkg/client/registry.go:56
|
||||
#, fuzzy, c-format
|
||||
msgid "registry did not return a digest for %s:%s"
|
||||
msgstr "🛑 error al analizar la imagen %s; en: %s: %s"
|
||||
|
||||
#. translators: `recipe release` command
|
||||
#: cli/recipe/release.go:35
|
||||
msgid "release <recipe> [version] [flags]"
|
||||
@@ -5224,6 +5239,11 @@ msgstr "eliminando configuración %s"
|
||||
msgid "removing configs failed: %s"
|
||||
msgstr "eliminación de configuraciones fallida: %s"
|
||||
|
||||
#: pkg/recipe/compose.go:120
|
||||
#, c-format
|
||||
msgid "removing digest from %s for %s, use --pindigests to pin the new tag"
|
||||
msgstr ""
|
||||
|
||||
#: pkg/upstream/stack/remove.go:148
|
||||
#, c-format
|
||||
msgid "removing network %s"
|
||||
@@ -5249,7 +5269,7 @@ msgstr "eliminación de volumen %s fallida: %s"
|
||||
msgid "removing volumes failed: %s"
|
||||
msgstr "error al eliminar volúmenes: %s"
|
||||
|
||||
#: pkg/lint/recipe.go:177
|
||||
#: pkg/lint/recipe.go:184
|
||||
msgid "replace lightweight tag with annotated tag"
|
||||
msgstr "reemplazar etiqueta ligera por etiqueta anotada"
|
||||
|
||||
@@ -5332,7 +5352,7 @@ msgstr "recuperado %s como servicio con respaldo (backup) habilitado"
|
||||
msgid "retrieved %s as target container on %s"
|
||||
msgstr "recuperado %s como contenedor destino en %s"
|
||||
|
||||
#: pkg/recipe/compose.go:59
|
||||
#: pkg/recipe/compose.go:60
|
||||
#, c-format
|
||||
msgid "retrieved %s configs for %s"
|
||||
msgstr "recuperado %s configuración(es) para %s"
|
||||
@@ -5511,7 +5531,7 @@ msgstr "buscando abra.sh para la versión de %s"
|
||||
msgid "secret %s is > %d chars when combined with %s"
|
||||
msgstr "el secreto %s tiene > %d caracteres cuando se combina con %s"
|
||||
|
||||
#: pkg/lint/recipe.go:481
|
||||
#: pkg/lint/recipe.go:510
|
||||
#, c-format
|
||||
msgid "secret %s is longer than 12 characters"
|
||||
msgstr "el secreto %s es más largo que 12 caracteres"
|
||||
@@ -5930,7 +5950,7 @@ msgstr "¿Modo swarm no habilitado en el servidor local?"
|
||||
msgid "sync [flags]"
|
||||
msgstr "sincronizar [opciones]"
|
||||
|
||||
#: pkg/recipe/compose.go:309
|
||||
#: pkg/recipe/compose.go:280
|
||||
#, c-format
|
||||
msgid "synced label %s to service %s"
|
||||
msgstr "etiqueta sincronizada %s con el servicio %s"
|
||||
@@ -6028,7 +6048,7 @@ msgstr "las opciones tmpfs son incompatibles con el tipo volume"
|
||||
msgid "toggle non-interactive mode"
|
||||
msgstr "activar modo no interactivo"
|
||||
|
||||
#: pkg/lint/recipe.go:154
|
||||
#: pkg/lint/recipe.go:161
|
||||
msgid "traefik routing enabled"
|
||||
msgstr "enrutamiento de traefik habilitado"
|
||||
|
||||
@@ -6161,6 +6181,11 @@ msgstr "no se pudo detectar el remoto SSH para %s"
|
||||
msgid "unable to fetch tags in %s: %s"
|
||||
msgstr "no se pudieron obtener las etiquetas en %s: %s"
|
||||
|
||||
#: pkg/recipe/compose.go:181
|
||||
#, c-format
|
||||
msgid "unable to find image %s for %s in %s, please update it manually"
|
||||
msgstr ""
|
||||
|
||||
#: cli/app/move.go:301
|
||||
#, c-format
|
||||
msgid "unable to get container matching %s: %s"
|
||||
@@ -6181,7 +6206,7 @@ msgstr "no se pudo hacer un soft reset en %s: %s"
|
||||
msgid "unable to inspect container for %s: %s"
|
||||
msgstr "no se pudo inspeccionar el contenedor para %s: %s"
|
||||
|
||||
#: pkg/lint/recipe.go:496
|
||||
#: pkg/lint/recipe.go:525
|
||||
#, c-format
|
||||
msgid "unable to list local tags for %s"
|
||||
msgstr "no se pudieron listar las etiquetas locales para %s"
|
||||
@@ -6195,7 +6220,7 @@ msgstr "no se pudo localizar el comando git; no se puede mostrar el diff"
|
||||
msgid "unable to look up server context for %s: %s"
|
||||
msgstr "no se pudo obtener el contexto del servidor para %s: %s"
|
||||
|
||||
#: cli/recipe/fetch.go:77 pkg/git/read.go:26 pkg/lint/recipe.go:491
|
||||
#: cli/recipe/fetch.go:77 pkg/git/read.go:26 pkg/lint/recipe.go:520
|
||||
#: pkg/recipe/git.go:243
|
||||
#, c-format
|
||||
msgid "unable to open %s: %s"
|
||||
@@ -6227,11 +6252,6 @@ msgid "unable to parse %s, error was: %s, skipping upgrade for %s"
|
||||
msgstr ""
|
||||
"no se pudo analizar %s, el error fue: %s, omitiendo la actualización para %s"
|
||||
|
||||
#: pkg/recipe/compose.go:154
|
||||
#, c-format
|
||||
msgid "unable to parse %s, skipping"
|
||||
msgstr "no se pudo parsear %s; se omitirá"
|
||||
|
||||
#: cli/app/list.go:167
|
||||
#, c-format
|
||||
msgid "unable to parse %s, skipping as upgrade option"
|
||||
@@ -6247,6 +6267,11 @@ msgstr "no se pudo parsear el valor '%s' como bool: %s"
|
||||
msgid "unable to parse deploy version: %s"
|
||||
msgstr "no se puede analizar la versión de despliegue: %s"
|
||||
|
||||
#: pkg/recipe/compose.go:204
|
||||
#, fuzzy, c-format
|
||||
msgid "unable to parse image %s: %s"
|
||||
msgstr "no se pudo crear %s: %s"
|
||||
|
||||
#: cli/run.go:176
|
||||
#, c-format
|
||||
msgid "unable to proceed, %s does not exist?"
|
||||
@@ -6461,16 +6486,11 @@ msgstr "arriba"
|
||||
msgid "updating %s"
|
||||
msgstr "actualizando %s"
|
||||
|
||||
#: pkg/recipe/compose.go:176 pkg/recipe/compose.go:303
|
||||
#: pkg/recipe/compose.go:187 pkg/recipe/compose.go:274
|
||||
#, c-format
|
||||
msgid "updating %s to %s in %s"
|
||||
msgstr "actualizando %s a %s en %s"
|
||||
|
||||
#: pkg/recipe/compose.go:236
|
||||
#, fuzzy, c-format
|
||||
msgid "updating pinned reference %s to %s in %s"
|
||||
msgstr "actualizando %s a %s en %s"
|
||||
|
||||
#. translators: `abra upgrade` command for autocompletion
|
||||
#: cli/run.go:103
|
||||
msgid "upgrade"
|
||||
@@ -6510,6 +6530,11 @@ msgstr "actualizando el servicio %s de %s a %s (etiqueta fijada: %s)"
|
||||
msgid "upload your recipe to git.coopcloud.tech/coop-cloud/..."
|
||||
msgstr "sube tu receta a git.coopcloud.tech/coop-cloud/..."
|
||||
|
||||
#: pkg/lint/recipe.go:132
|
||||
#, fuzzy
|
||||
msgid "use a digest for all images"
|
||||
msgstr "usar una etiqueta para todas las imágenes"
|
||||
|
||||
#: pkg/lint/recipe.go:83
|
||||
msgid "use a tag for all images"
|
||||
msgstr "usar una etiqueta para todas las imágenes"
|
||||
@@ -6561,7 +6586,7 @@ msgstr "validado %s como argumento de receta"
|
||||
msgid "validated %s as server argument"
|
||||
msgstr "validado %s como argumento de servidor"
|
||||
|
||||
#: pkg/lint/recipe.go:170
|
||||
#: pkg/lint/recipe.go:177
|
||||
msgid "vendor config versions in an abra.sh"
|
||||
msgstr "versiones de configuración del proveedor en un abra.sh"
|
||||
|
||||
@@ -6718,7 +6743,7 @@ msgstr "esperando a que %d tarea(s) alcancen un estado terminal"
|
||||
#: pkg/lint/recipe.go:21 pkg/lint/recipe.go:67 pkg/lint/recipe.go:74
|
||||
#: pkg/lint/recipe.go:81 pkg/lint/recipe.go:88 pkg/lint/recipe.go:95
|
||||
#: pkg/lint/recipe.go:102 pkg/lint/recipe.go:109 pkg/lint/recipe.go:116
|
||||
#: pkg/lint/recipe.go:123
|
||||
#: pkg/lint/recipe.go:123 pkg/lint/recipe.go:130
|
||||
msgid "warn"
|
||||
msgstr "advertencia"
|
||||
|
||||
@@ -6792,6 +6817,22 @@ msgstr ""
|
||||
msgid "{name: %s, "
|
||||
msgstr "{nombre: %s, "
|
||||
|
||||
#, fuzzy, c-format
|
||||
#~ msgid "unable to parse %s: %s"
|
||||
#~ msgstr "no se pudo abrir %s: %s"
|
||||
|
||||
#, c-format
|
||||
#~ msgid "parsed %s from %s"
|
||||
#~ msgstr "parseado %s de %s"
|
||||
|
||||
#, c-format
|
||||
#~ msgid "unable to parse %s, skipping"
|
||||
#~ msgstr "no se pudo parsear %s; se omitirá"
|
||||
|
||||
#, fuzzy, c-format
|
||||
#~ msgid "updating pinned reference %s to %s in %s"
|
||||
#~ msgstr "actualizando %s a %s en %s"
|
||||
|
||||
#~ msgid "removed freshly created commit"
|
||||
#~ msgstr "eliminado commit recién creado"
|
||||
|
||||
|
||||
+67
-96
@@ -6,6 +6,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"coopcloud.tech/abra/pkg/formatter"
|
||||
@@ -110,140 +111,110 @@ func (r Recipe) GetVersionLabelLocal() (string, error) {
|
||||
return label, nil
|
||||
}
|
||||
|
||||
// UpdateTag updates an image tag in-place on file system local compose files.
|
||||
func (r Recipe) UpdateTag(image, tag string) (bool, error) {
|
||||
fullPattern := fmt.Sprintf("%s/compose**yml", r.Dir)
|
||||
image = formatter.StripTagMeta(image)
|
||||
// UpdateTag updates the image tag of a service in-place on file system local
|
||||
// compose files. An existing digest is dropped, since it would still point at
|
||||
// the image of the old tag.
|
||||
func (r Recipe) UpdateTag(serviceName, tag string) (bool, error) {
|
||||
return r.updateServiceImage(serviceName, func(image string) (string, error) {
|
||||
if strings.Contains(image, "@") {
|
||||
log.Warn(i18n.G("removing digest from %s for %s, use --pindigests to pin the new tag", image, serviceName))
|
||||
}
|
||||
return setImageTag(image, tag, "")
|
||||
})
|
||||
}
|
||||
|
||||
composeFiles, err := filepath.Glob(fullPattern)
|
||||
// UpdatePinnedTag updates the image of a service to a pinned version
|
||||
// (image:tag@sha256:...) in-place on file system local compose files.
|
||||
func (r Recipe) UpdatePinnedTag(serviceName, tag, digest string) (bool, error) {
|
||||
return r.updateServiceImage(serviceName, func(image string) (string, error) {
|
||||
return setImageTag(image, tag, digest)
|
||||
})
|
||||
}
|
||||
|
||||
// updateServiceImage rewrites the image of a service in all local compose
|
||||
// files which define it. Only image lines matching the current image exactly
|
||||
// are touched, so other services using a variant of the same image (e.g.
|
||||
// nginx:1.21.0-alpine) are left as is.
|
||||
func (r Recipe) updateServiceImage(serviceName string, rewrite func(image string) (string, error)) (bool, error) {
|
||||
composeFiles, err := filepath.Glob(fmt.Sprintf("%s/compose**yml", r.Dir))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
log.Debug(i18n.G("considering %s config(s) for tag update", strings.Join(composeFiles, ", ")))
|
||||
log.Debug(i18n.G("considering %s config(s) for image update", strings.Join(composeFiles, ", ")))
|
||||
|
||||
sampleEnv, err := r.SampleEnv()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
updated := false
|
||||
for _, composeFile := range composeFiles {
|
||||
opts := stack.Deploy{Composefiles: []string{composeFile}}
|
||||
|
||||
sampleEnv, err := r.SampleEnv()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
compose, err := loader.LoadComposefile(opts, sampleEnv)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, service := range compose.Services {
|
||||
if service.Image == "" {
|
||||
if service.Name != serviceName || service.Image == "" {
|
||||
continue // may be a compose.$optional.yml file
|
||||
}
|
||||
|
||||
img, _ := reference.ParseNormalizedNamed(service.Image)
|
||||
old := service.Image
|
||||
new, err := rewrite(old)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if old == new {
|
||||
continue
|
||||
}
|
||||
|
||||
bytes, err := os.ReadFile(composeFile)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var composeTag string
|
||||
switch img.(type) {
|
||||
case reference.NamedTagged:
|
||||
composeTag = img.(reference.NamedTagged).Tag()
|
||||
default:
|
||||
log.Debug(i18n.G("unable to parse %s, skipping", img))
|
||||
imageLine := regexp.MustCompile(`(?m)^(\s*image:\s*["']?)` + regexp.QuoteMeta(old) + `(["']?\s*(#.*)?)$`)
|
||||
if !imageLine.Match(bytes) {
|
||||
// the image is set via a variable, e.g. nginx:${VERSION}
|
||||
log.Warn(i18n.G("unable to find image %s for %s in %s, please update it manually", old, serviceName, composeFile))
|
||||
continue
|
||||
}
|
||||
|
||||
composeImage := formatter.StripTagMeta(reference.Path(img))
|
||||
replacedBytes := imageLine.ReplaceAllString(string(bytes), "${1}"+strings.ReplaceAll(new, "$", "$$")+"${2}")
|
||||
|
||||
log.Debug(i18n.G("parsed %s from %s", composeTag, service.Image))
|
||||
log.Debug(i18n.G("updating %s to %s in %s", old, new, composeFile))
|
||||
|
||||
if image == composeImage {
|
||||
bytes, err := ioutil.ReadFile(composeFile)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
old := fmt.Sprintf("%s:%s", composeImage, composeTag)
|
||||
new := fmt.Sprintf("%s:%s", composeImage, tag)
|
||||
if old == new {
|
||||
continue
|
||||
}
|
||||
|
||||
replacedBytes := strings.Replace(string(bytes), old, new, -1)
|
||||
|
||||
log.Debug(i18n.G("updating %s to %s in %s", old, new, compose.Filename))
|
||||
|
||||
if err := os.WriteFile(compose.Filename, []byte(replacedBytes), 0o764); err != nil {
|
||||
return false, err
|
||||
}
|
||||
updated = true
|
||||
if err := os.WriteFile(composeFile, []byte(replacedBytes), 0o764); err != nil {
|
||||
return false, err
|
||||
}
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
|
||||
return updated, nil
|
||||
}
|
||||
|
||||
// UpdatePinnedTag updates an image reference to a pinned version (image:tag@sha256:...) in-place on local compose files.
|
||||
func (r Recipe) UpdatePinnedTag(image, tag, digest string) (bool, error) {
|
||||
glob := fmt.Sprintf("%s/compose**yml", r.Dir)
|
||||
image = formatter.StripTagMeta(image)
|
||||
|
||||
composeFiles, err := filepath.Glob(glob)
|
||||
if err != nil {
|
||||
return false, err
|
||||
// setImageTag replaces the tag and digest of an image while keeping the image
|
||||
// name as written (e.g. including the registry host). An empty digest leaves
|
||||
// the image unpinned.
|
||||
func setImageTag(image, tag, digest string) (string, error) {
|
||||
if _, err := reference.ParseNormalizedNamed(image); err != nil {
|
||||
return "", errors.New(i18n.G("unable to parse image %s: %s", image, err))
|
||||
}
|
||||
|
||||
updated := false
|
||||
for _, composeFile := range composeFiles {
|
||||
opts := stack.Deploy{Composefiles: []string{composeFile}}
|
||||
|
||||
sampleEnv, err := r.SampleEnv()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
compose, err := loader.LoadComposefile(opts, sampleEnv)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, service := range compose.Services {
|
||||
if service.Image == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
img, _ := reference.ParseNormalizedNamed(formatter.RemoveSha(service.Image))
|
||||
composeImage := formatter.StripTagMeta(reference.Path(img))
|
||||
|
||||
if image == composeImage {
|
||||
bytes, err := ioutil.ReadFile(composeFile)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
old := service.Image
|
||||
new := fmt.Sprintf("%s:%s@%s", composeImage, tag, digest)
|
||||
|
||||
if old == new {
|
||||
continue
|
||||
}
|
||||
|
||||
replacedBytes := strings.Replace(string(bytes), old, new, -1)
|
||||
|
||||
log.Debug(i18n.G("updating pinned reference %s to %s in %s", old, new, compose.Filename))
|
||||
|
||||
if err := os.WriteFile(compose.Filename, []byte(replacedBytes), 0o764); err != nil {
|
||||
return false, err
|
||||
}
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
name := formatter.RemoveSha(image)
|
||||
if i := strings.LastIndex(name, ":"); i > strings.LastIndex(name, "/") {
|
||||
name = name[:i] // strip the tag, but not a registry port
|
||||
}
|
||||
|
||||
return updated, nil
|
||||
new := fmt.Sprintf("%s:%s", name, tag)
|
||||
if digest != "" {
|
||||
new = fmt.Sprintf("%s@%s", new, digest)
|
||||
}
|
||||
|
||||
return new, nil
|
||||
}
|
||||
|
||||
// UpdateLabel updates a label in-place on file system local compose files.
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
package recipe
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"coopcloud.tech/abra/pkg/client"
|
||||
"github.com/distribution/reference"
|
||||
)
|
||||
|
||||
const (
|
||||
testDigest = "sha256:2bcabc23b45489fb0885d69a06ba1d648aeda973fae7bb981bafbb884165e514"
|
||||
testNewDigest = "sha256:908dc23e643a1447dbfb2e189ed268bfde6a51a5bf9a34d3dd3440a24f58ccf7"
|
||||
testAlpineDigest = "sha256:a74534e76ee1121d418fa7394ca930eb67440deda413848bc67c68138535b989"
|
||||
)
|
||||
|
||||
func writeTestRecipe(t *testing.T, compose, sampleEnv string) Recipe {
|
||||
t.Helper()
|
||||
|
||||
dir := t.TempDir()
|
||||
r := Recipe{
|
||||
Name: "test_recipe",
|
||||
Dir: dir,
|
||||
ComposePath: path.Join(dir, "compose.yml"),
|
||||
SampleEnvPath: path.Join(dir, ".env.sample"),
|
||||
}
|
||||
|
||||
if err := os.WriteFile(r.ComposePath, []byte(compose), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(r.SampleEnvPath, []byte("TYPE=test_recipe\n"+sampleEnv), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// composeWithImages returns a compose file with the services "app" and "db"
|
||||
// using the given images, in that order.
|
||||
func composeWithImages(images ...string) string {
|
||||
out := "---\nversion: \"3.8\"\n\nservices:\n"
|
||||
names := []string{"app", "db"}
|
||||
for i, image := range images {
|
||||
out += " " + names[i] + ":\n image: " + image + "\n"
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// serviceImages returns the image of each service, as loaded from the recipe
|
||||
// compose files.
|
||||
func serviceImages(t *testing.T, r Recipe) map[string]string {
|
||||
t.Helper()
|
||||
|
||||
config, err := r.GetComposeConfig(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
images := make(map[string]string)
|
||||
for _, service := range config.Services {
|
||||
images[service.Name] = service.Image
|
||||
}
|
||||
return images
|
||||
}
|
||||
|
||||
func assertImages(t *testing.T, r Recipe, want map[string]string) {
|
||||
t.Helper()
|
||||
|
||||
got := serviceImages(t, r)
|
||||
if len(got) != len(want) {
|
||||
t.Fatalf("got services %v, want %v", got, want)
|
||||
}
|
||||
for service, image := range want {
|
||||
if got[service] != image {
|
||||
t.Errorf("service %s: got image %s, want %s", service, got[service], image)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdatePinnedTag(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
images []string
|
||||
sampleEnv string
|
||||
service string
|
||||
tag string
|
||||
digest string
|
||||
wantUpdated bool
|
||||
wantImages map[string]string
|
||||
}{
|
||||
{
|
||||
name: "pin unpinned image",
|
||||
images: []string{"nginx:1.21.0"},
|
||||
service: "app",
|
||||
tag: "1.21.6",
|
||||
digest: testDigest,
|
||||
wantUpdated: true,
|
||||
wantImages: map[string]string{"app": "nginx:1.21.6@" + testDigest},
|
||||
},
|
||||
{
|
||||
name: "pin same tag",
|
||||
images: []string{"nginx:1.21.6"},
|
||||
service: "app",
|
||||
tag: "1.21.6",
|
||||
digest: testDigest,
|
||||
wantUpdated: true,
|
||||
wantImages: map[string]string{"app": "nginx:1.21.6@" + testDigest},
|
||||
},
|
||||
{
|
||||
name: "replace existing digest",
|
||||
images: []string{"nginx:1.21.6@" + testDigest},
|
||||
service: "app",
|
||||
tag: "1.31.6",
|
||||
digest: testNewDigest,
|
||||
wantUpdated: true,
|
||||
wantImages: map[string]string{"app": "nginx:1.31.6@" + testNewDigest},
|
||||
},
|
||||
{
|
||||
name: "already pinned",
|
||||
images: []string{"nginx:1.21.6@" + testDigest},
|
||||
service: "app",
|
||||
tag: "1.21.6",
|
||||
digest: testDigest,
|
||||
wantUpdated: false,
|
||||
wantImages: map[string]string{"app": "nginx:1.21.6@" + testDigest},
|
||||
},
|
||||
{
|
||||
name: "only the given service is pinned",
|
||||
images: []string{"nginx:1.21.0", "postgres:15"},
|
||||
service: "app",
|
||||
tag: "1.21.6",
|
||||
digest: testDigest,
|
||||
wantUpdated: true,
|
||||
wantImages: map[string]string{"app": "nginx:1.21.6@" + testDigest, "db": "postgres:15"},
|
||||
},
|
||||
{
|
||||
name: "same image in another service is left alone",
|
||||
images: []string{"nginx:1.21.0", "nginx:1.21.0-alpine"},
|
||||
service: "app",
|
||||
tag: "1.21.6",
|
||||
digest: testDigest,
|
||||
wantUpdated: true,
|
||||
wantImages: map[string]string{"app": "nginx:1.21.6@" + testDigest, "db": "nginx:1.21.0-alpine"},
|
||||
},
|
||||
{
|
||||
name: "keep registry host",
|
||||
images: []string{"ghcr.io/foo/bar:1.0"},
|
||||
service: "app",
|
||||
tag: "1.1",
|
||||
digest: testDigest,
|
||||
wantUpdated: true,
|
||||
wantImages: map[string]string{"app": "ghcr.io/foo/bar:1.1@" + testDigest},
|
||||
},
|
||||
{
|
||||
name: "keep registry port",
|
||||
images: []string{"localhost:5000/foo/bar:1.0"},
|
||||
service: "app",
|
||||
tag: "1.1",
|
||||
digest: testDigest,
|
||||
wantUpdated: true,
|
||||
wantImages: map[string]string{"app": "localhost:5000/foo/bar:1.1@" + testDigest},
|
||||
},
|
||||
{
|
||||
name: "quoted image",
|
||||
images: []string{`"nginx:1.21.0"`},
|
||||
service: "app",
|
||||
tag: "1.21.6",
|
||||
digest: testDigest,
|
||||
wantUpdated: true,
|
||||
wantImages: map[string]string{"app": "nginx:1.21.6@" + testDigest},
|
||||
},
|
||||
{
|
||||
name: "skip image with variable",
|
||||
images: []string{"nginx:${NGINX_VERSION}"},
|
||||
sampleEnv: "NGINX_VERSION=1.21.0\n",
|
||||
service: "app",
|
||||
tag: "1.21.6",
|
||||
digest: testDigest,
|
||||
wantUpdated: false,
|
||||
wantImages: map[string]string{"app": "nginx:1.21.0"},
|
||||
},
|
||||
{
|
||||
name: "no matching service",
|
||||
images: []string{"nginx:1.21.0"},
|
||||
service: "db",
|
||||
tag: "1.21.6",
|
||||
digest: testDigest,
|
||||
wantUpdated: false,
|
||||
wantImages: map[string]string{"app": "nginx:1.21.0"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
r := writeTestRecipe(t, composeWithImages(test.images...), test.sampleEnv)
|
||||
|
||||
updated, err := r.UpdatePinnedTag(test.service, test.tag, test.digest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if updated != test.wantUpdated {
|
||||
t.Errorf("got updated %v, want %v", updated, test.wantUpdated)
|
||||
}
|
||||
|
||||
assertImages(t, r, test.wantImages)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdatePinnedTagInvalidImage(t *testing.T) {
|
||||
r := writeTestRecipe(t, composeWithImages("Nginx:1.21.0"), "")
|
||||
|
||||
if _, err := r.UpdatePinnedTag("app", "1.21.6", testDigest); err == nil {
|
||||
t.Fatal("expected an error for an invalid image")
|
||||
}
|
||||
|
||||
assertImages(t, r, map[string]string{"app": "Nginx:1.21.0"})
|
||||
}
|
||||
|
||||
func TestUpdateTag(t *testing.T) {
|
||||
r := writeTestRecipe(t, composeWithImages("nginx:1.21.0", "nginx:1.21.0-alpine"), "")
|
||||
|
||||
updated, err := r.UpdateTag("app", "1.21.6")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !updated {
|
||||
t.Error("expected image to be updated")
|
||||
}
|
||||
|
||||
assertImages(t, r, map[string]string{"app": "nginx:1.21.6", "db": "nginx:1.21.0-alpine"})
|
||||
|
||||
updated, err = r.UpdateTag("app", "1.21.6")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if updated {
|
||||
t.Error("expected no update for the same tag")
|
||||
}
|
||||
}
|
||||
|
||||
// An existing digest points at the old image, keeping it after a tag
|
||||
// upgrade would silently deploy the previous version under the new tag.
|
||||
func TestUpdateTagDropsStaleDigest(t *testing.T) {
|
||||
r := writeTestRecipe(t, composeWithImages("nginx:1.21.6@"+testDigest), "")
|
||||
|
||||
updated, err := r.UpdateTag("app", "1.31.6")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !updated {
|
||||
t.Error("expected image to be updated")
|
||||
}
|
||||
|
||||
assertImages(t, r, map[string]string{"app": "nginx:1.31.6"})
|
||||
}
|
||||
|
||||
// Services using variants of the same image must each be pinned to the
|
||||
// digest of their own tag, in whichever order they are upgraded.
|
||||
func TestUpdatePinnedTagSharedImageIntegration(t *testing.T) {
|
||||
img, err := reference.ParseNormalizedNamed("nginx")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
upgradeTags := map[string]string{"app": "1.21.6", "db": "1.21.6-alpine"}
|
||||
|
||||
digests := make(map[string]string)
|
||||
for service, tag := range upgradeTags {
|
||||
digest, err := client.GetImageMultiplatformDigest(img, tag)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !regexp.MustCompile(`^sha256:[a-f0-9]{64}$`).MatchString(digest) {
|
||||
t.Fatalf("unexpected digest %s for %s", digest, tag)
|
||||
}
|
||||
digests[service] = digest
|
||||
}
|
||||
if digests["app"] == digests["db"] {
|
||||
t.Fatalf("expected different digests for %v, got %s", upgradeTags, digests["app"])
|
||||
}
|
||||
|
||||
for _, order := range [][]string{{"app", "db"}, {"db", "app"}} {
|
||||
t.Run(order[0]+" first", func(t *testing.T) {
|
||||
r := writeTestRecipe(t, composeWithImages("nginx:1.21.0", "nginx:1.21.0-alpine"), "")
|
||||
|
||||
for _, service := range order {
|
||||
updated, err := r.UpdatePinnedTag(service, upgradeTags[service], digests[service])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !updated {
|
||||
t.Errorf("expected %s to be updated", service)
|
||||
}
|
||||
}
|
||||
|
||||
assertImages(t, r, map[string]string{
|
||||
"app": "nginx:1.21.6@" + digests["app"],
|
||||
"db": "nginx:1.21.6-alpine@" + digests["db"],
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user