From 01aee8439ac99b5b3520dac4f9928431fe506ce5 Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Wed, 13 May 2026 20:54:38 -0700 Subject: [PATCH] feat(incusos): build and publish multiple variants --- .moon/toolchains.yml | 2 +- cmd/imgcli/script_test.go | 77 ++++++-- cmd/imgcli/testdata/script/publish.txtar | 10 ++ go.mod | 22 ++- go.sum | 23 ++- go.work | 2 +- go.work.sum | 155 ++--------------- internal/cli/publish.go | 5 +- internal/cli/root_test.go | 18 +- internal/providers/incusos/provider.go | 151 +++++++++------- internal/providers/incusos/provider_test.go | 179 +++++++++++++++++-- internal/publish/mocks/catalog_client.go | 82 ++++++++- internal/publish/release.go | 107 +++++++++++- internal/publish/release_test.go | 184 +++++++++++++++++++- schemas/go.mod | 3 +- schemas/go.sum | 6 +- 16 files changed, 760 insertions(+), 266 deletions(-) diff --git a/.moon/toolchains.yml b/.moon/toolchains.yml index 4212555..fa5f99a 100644 --- a/.moon/toolchains.yml +++ b/.moon/toolchains.yml @@ -15,4 +15,4 @@ node: syncVersionManagerConfig: 'nvm' go: - version: '1.26.2' + version: '1.26.3' diff --git a/cmd/imgcli/script_test.go b/cmd/imgcli/script_test.go index 71d7562..36c2dce 100644 --- a/cmd/imgcli/script_test.go +++ b/cmd/imgcli/script_test.go @@ -31,8 +31,7 @@ const ( testEnvIncusOSCDNURL = "IMGCLI_TEST_INCUSOS_CDN_URL" testEnvFixtureInjector = "IMGCLI_TEST_FIXTURE_INJECTOR" - testImgsrvToken = "testtok.imgcli-script" - testVersion = "2026.05.06" + testVersion = "2026.05.06" fixtureSourcePath = "/202604261712/x86_64/IncusOS_202604261712.img.gz" fixtureSourceBody = "fixture IncusOS source image bytes\n" @@ -79,7 +78,7 @@ func setupScript(env *testscript.Env) error { } tb.Helper() - imgsrvEnv := imgsrvtest.Start(tb, imgsrvtest.WithCASPromotion(), imgsrvtest.WithAPIToken(testImgsrvToken)) + imgsrvEnv := imgsrvtest.Start(tb, imgsrvtest.WithCASPromotion(), imgsrvtest.WithAPIToken()) cdnServer := newFixtureCDNServer(tb) env.Values[testscriptEnvKey{}] = imgsrvEnv @@ -88,7 +87,7 @@ func setupScript(env *testscript.Env) error { env.Setenv("IMGCLI_CACHE_DIR", filepath.Join(env.WorkDir, "cache")) env.Setenv("IMGCLI_CACHE_MAX_SIZE", "0") env.Setenv("IMGSRV_URL", imgsrvEnv.BaseURL()) - env.Setenv("IMGSRV_TOKEN", testImgsrvToken) + env.Setenv("IMGSRV_TOKEN", imgsrvEnv.ClientOptions().BearerToken) env.Setenv("PUBLISH_VERSION", testVersion) env.Setenv(testEnvIncusOSCDNURL, cdnServer.URL) env.Setenv(testEnvFixtureInjector, "1") @@ -156,7 +155,7 @@ func verifyPublish(ts *testscript.TestScript, neg bool, args []string) { result := readPublishResult(ts, args[0]) assertPublishResult(ts, result, args[1], args[2], args[3]) - verifyPublishedArtifact(ts, args[1], args[2], args[3], result.Artifacts[0].ServerArtifactID) + verifyPublishedArtifacts(ts, args[1], args[2], args[3], result.Artifacts) } func readPublishResult(ts *testscript.TestScript, path string) publish.ReleaseResult { @@ -187,23 +186,41 @@ func assertPublishResult( if !containsString(result.Aliases, alias) { ts.Fatalf("publish result aliases = %v, want %q", result.Aliases, alias) } - if len(result.Artifacts) != 1 { - ts.Fatalf("publish result artifacts length = %d, want 1", len(result.Artifacts)) + if len(result.Artifacts) != 2 { + ts.Fatalf("publish result artifacts length = %d, want 2", len(result.Artifacts)) } - artifact := result.Artifacts[0] - if artifact.OperatingSystem != "incusos" || artifact.Architecture != "x86_64" || - artifact.Format != imgsrv.ArtifactFormatRawGZ { - ts.Fatalf("publish result artifact = %+v, want incusos x86_64 raw.gz", artifact) + for _, wantVariant := range []string{"default", "secureboot"} { + artifact, ok := publishedArtifactByVariant(result.Artifacts, wantVariant) + if !ok { + ts.Fatalf("publish result artifacts missing variant %q: %+v", wantVariant, result.Artifacts) + } + if artifact.OperatingSystem != "incusos" || artifact.Architecture != "x86_64" || + artifact.Format != imgsrv.ArtifactFormatRawGZ { + ts.Fatalf("publish result artifact = %+v, want incusos x86_64 raw.gz", artifact) + } } } -func verifyPublishedArtifact( +func publishedArtifactByVariant( + artifacts []publish.PublishedReleaseArtifact, + variant string, +) (publish.PublishedReleaseArtifact, bool) { + for _, artifact := range artifacts { + if artifact.Variant == variant { + return artifact, true + } + } + + return publish.PublishedReleaseArtifact{}, false +} + +func verifyPublishedArtifacts( ts *testscript.TestScript, image string, publishedVersion string, alias string, - artifactID string, + publishedArtifacts []publish.PublishedReleaseArtifact, ) { env, ok := ts.Value(testscriptEnvKey{}).(*imgsrvtest.Env) if !ok || env == nil { @@ -222,13 +239,37 @@ func verifyPublishedArtifact( if err != nil { ts.Fatalf("resolve manifest: %v", err) } - if len(manifest.Artifacts) != 1 { - ts.Fatalf("manifest artifacts length = %d, want 1", len(manifest.Artifacts)) + if len(manifest.Artifacts) != len(publishedArtifacts) { + ts.Fatalf("manifest artifacts length = %d, want %d", len(manifest.Artifacts), len(publishedArtifacts)) } - artifact := manifest.Artifacts[0].Artifact - if artifact.ID.String() != artifactID { - ts.Fatalf("manifest artifact ID = %q, want %q", artifact.ID.String(), artifactID) + + publishedByVariant := map[string]publish.PublishedReleaseArtifact{} + for _, artifact := range publishedArtifacts { + publishedByVariant[artifact.Variant] = artifact + } + + for _, manifestArtifact := range manifest.Artifacts { + artifact := manifestArtifact.Artifact + published, ok := publishedByVariant[artifact.Variant] + if !ok { + ts.Fatalf("manifest artifact variant %q was not in publish result", artifact.Variant) + } + if artifact.ID.String() != published.ServerArtifactID { + ts.Fatalf("manifest artifact ID = %q, want %q", artifact.ID.String(), published.ServerArtifactID) + } + verifyArtifactDownload(ts, client, image, publishedVersion, published.ServerArtifactID) } +} + +func verifyArtifactDownload( + ts *testscript.TestScript, + client *imgsrv.Client, + image string, + publishedVersion string, + artifactID string, +) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() download, err := client.Catalog().OpenArtifactDownload( ctx, diff --git a/cmd/imgcli/testdata/script/publish.txtar b/cmd/imgcli/testdata/script/publish.txtar index afff34d..1037dc4 100644 --- a/cmd/imgcli/testdata/script/publish.txtar +++ b/cmd/imgcli/testdata/script/publish.txtar @@ -7,8 +7,11 @@ stdout '"aliases":\["latest"\]' stdout '"operatingSystem":"incusos"' stdout '"architecture":"x86_64"' stdout '"format":"raw.gz"' +stdout '"variant":"default"' +stdout '"variant":"secureboot"' ! stderr . exists out/script-image-default-amd64.raw.gz +exists out/script-image-secureboot-amd64.raw.gz cp stdout publish.json verify-publish publish.json script-image 2026.05.06 latest @@ -27,4 +30,11 @@ incusos: { format: "raw.gz" } } + variants: secureboot: { + source: version: "202604261712" + artifact: { + architecture: "amd64" + format: "raw.gz" + } + } } diff --git a/go.mod b/go.mod index 529af32..2a17e97 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/meigma/imgcli -go 1.26.1 +go 1.26.3 require ( charm.land/log/v2 v2.0.0 @@ -9,7 +9,7 @@ require ( github.com/gofrs/flock v0.13.0 github.com/lxc/incus-os/incus-osd v0.0.0-20260505023852-d32ba1f13f6f github.com/meigma/imgcli/schemas v0.0.0-20260505154605-5bbbe47a1e06 - github.com/meigma/imgsrv v0.0.0-20260507005312-4a67655d031d + github.com/meigma/imgsrv v0.1.1-0.20260514031114-285280443f6b github.com/rogpeppe/go-internal v1.14.1 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 @@ -19,6 +19,7 @@ require ( ) require ( + cel.dev/expr v0.25.1 // indirect charm.land/lipgloss/v2 v2.0.1 // indirect cuelabs.dev/go/oci/ociregistry v0.0.0-20251212221603-3adeb8663819 // indirect dario.cat/mergo v1.0.2 // indirect @@ -26,6 +27,7 @@ require ( github.com/FuturFusion/migration-manager v0.6.9 // indirect github.com/FuturFusion/operations-center v0.5.8 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect @@ -43,6 +45,7 @@ require ( github.com/containerd/platforms v1.0.0-rc.4 // indirect github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/go-connections v0.7.0 // indirect github.com/docker/go-units v0.5.0 // indirect @@ -58,6 +61,8 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect + github.com/goccy/go-json v0.10.6 // indirect + github.com/google/cel-go v0.28.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect @@ -67,11 +72,20 @@ require ( github.com/klauspost/compress v1.18.6 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/klauspost/crc32 v1.3.0 // indirect + github.com/lestrrat-go/blackmagic v1.0.4 // indirect + github.com/lestrrat-go/dsig v1.2.1 // indirect + github.com/lestrrat-go/dsig-secp256k1 v1.0.0 // indirect + github.com/lestrrat-go/httpcc v1.0.1 // indirect + github.com/lestrrat-go/httprc/v3 v3.0.5 // indirect + github.com/lestrrat-go/jwx/v3 v3.1.1 // indirect + github.com/lestrrat-go/option/v2 v2.0.0 // indirect github.com/lucasb-eyer/go-colorful v1.4.0 // indirect github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e // indirect github.com/lxc/incus/v7 v7.0.0 // indirect github.com/magiconair/properties v1.8.10 // indirect github.com/mattn/go-runewidth v0.0.23 // indirect + github.com/meigma/authkit v0.3.0 // indirect + github.com/meigma/go-simplestreams v0.1.0 // indirect github.com/mfridman/interpolate v0.0.2 // indirect github.com/minio/crc64nvme v1.1.1 // indirect github.com/minio/md5-simd v1.1.2 // indirect @@ -105,6 +119,7 @@ require ( github.com/rivo/uniseg v0.4.7 // indirect github.com/rs/xid v1.6.0 // indirect github.com/sagikazarmark/locafero v0.12.0 // indirect + github.com/segmentio/asm v1.2.1 // indirect github.com/sethvargo/go-retry v0.3.0 // indirect github.com/shirou/gopsutil/v4 v4.26.4 // indirect github.com/sirupsen/logrus v1.9.4 // indirect @@ -117,6 +132,7 @@ require ( github.com/tinylib/msgp v1.6.1 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect + github.com/valyala/fastjson v1.6.10 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/zeebo/xxh3 v1.1.0 // indirect @@ -141,6 +157,8 @@ require ( golang.org/x/sys v0.43.0 // indirect golang.org/x/text v0.36.0 // indirect golang.org/x/tools v0.44.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260223185530-2f722ef697dc // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260427160629-7cedc36a6bc4 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 941dba4..81c5222 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= charm.land/lipgloss/v2 v2.0.1 h1:6Xzrn49+Py1Um5q/wZG1gWgER2+7dUyZ9XMEufqPSys= charm.land/lipgloss/v2 v2.0.1/go.mod h1:KjPle2Qd3YmvP1KL5OMHiHysGcNwq6u83MUjYkFvEkM= charm.land/log/v2 v2.0.0 h1:SY3Cey7ipx86/MBXQHwsguOT6X1exT94mmJRdzTNs+s= @@ -18,6 +19,7 @@ github.com/FuturFusion/operations-center v0.5.8 h1:Wf5B3ULMzSqju1u7wVgZbyJWHGd/Q github.com/FuturFusion/operations-center v0.5.8/go.mod h1:jmh2YszsUl3Mi5pyZnzndZZykImGJEUgtK+dzAiFFos= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= @@ -58,6 +60,7 @@ github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfv github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c= @@ -94,8 +97,10 @@ github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7 github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= +github.com/google/cel-go v0.28.0 h1:KjSWstCpz/MN5t4a8gnGJNIYUsJRpdi/r97xWDphIQc= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -123,6 +128,13 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA= +github.com/lestrrat-go/dsig v1.2.1 h1:MwxzZhE4+4fguHi+uDALKVlC3Cn+O1QU1Q/F8D7hVIc= +github.com/lestrrat-go/dsig-secp256k1 v1.0.0 h1:JpDe4Aybfl0soBvoVwjqDbp+9S1Y2OM7gcrVVMFPOzY= +github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= +github.com/lestrrat-go/httprc/v3 v3.0.5 h1:S+Mb4L2I+bM6JGTibLmxExhyTOqnXjqx+zi9MoXw/TM= +github.com/lestrrat-go/jwx/v3 v3.1.1 h1:yd9AdPmZ4INnQ7k42IrzXYpnEG803+SrQ6hdMvzHJzw= +github.com/lestrrat-go/option/v2 v2.0.0 h1:XxrcaJESE1fokHy3FpaQ/cXW8ZsIdWcdFzzLOcID3Ss= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4= @@ -141,10 +153,12 @@ github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3Ry github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5LJHI= github.com/mdelapenya/tlscert v0.2.0/go.mod h1:O4njj3ELLnJjGdkN7M/vIVCpZ+Cf0L6muqOG4tLSl8o= +github.com/meigma/authkit v0.3.0 h1:oyfzV+l6B1gDHfCx4zg97L0Km92GKVQ3hwKsjLrIHZU= +github.com/meigma/go-simplestreams v0.1.0 h1:yHKFUyRK4rPWO1Z5wDMAWymeRXAFFSiWQTsVUK73j90= github.com/meigma/imgcli/schemas v0.0.0-20260505154605-5bbbe47a1e06 h1:v/8R2UInUH6gsz099SZQwPapXkFRh+Q5p3fSplImoTw= github.com/meigma/imgcli/schemas v0.0.0-20260505154605-5bbbe47a1e06/go.mod h1:vgdSiTx7yikg0x4QmozD0dh4AYM90ZVALn0k45amZuQ= -github.com/meigma/imgsrv v0.0.0-20260507005312-4a67655d031d h1:YDvuNrpeEj810COQoVUaY3puq3vCC0IKSC9WuLJvy58= -github.com/meigma/imgsrv v0.0.0-20260507005312-4a67655d031d/go.mod h1:aRf/9hRpxhb53jgUmZdo7XVOqQkEn2FEzVqmGEZtx3I= +github.com/meigma/imgsrv v0.1.1-0.20260514031114-285280443f6b h1:gX5XmW2NRM3hTIPeLF8XcQyfrHot8KOiOz0HXdX3ej8= +github.com/meigma/imgsrv v0.1.1-0.20260514031114-285280443f6b/go.mod h1:aRLYyMvkliIpkrgivTsT9BS2npsG7S+csYvZK5+sFz8= github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI= @@ -219,6 +233,7 @@ github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= +github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= github.com/shirou/gopsutil/v4 v4.26.4 h1:B4SXVbcwTyrocPHEmWBC4uCYr4Xcu3MK1TXqbprAOWY= @@ -241,6 +256,7 @@ github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= @@ -255,6 +271,7 @@ github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYI github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= +github.com/valyala/fastjson v1.6.10 h1:/yjJg8jaVQdYR3arGxPE2X5z89xrlhS0eGXdv+ADTh4= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= @@ -317,6 +334,8 @@ golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= +google.golang.org/genproto/googleapis/api v0.0.0-20260223185530-2f722ef697dc h1:ULD+ToGXUIU6Pkzr1ARxdyvwfHbelw+agoFDRbLg4TU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260427160629-7cedc36a6bc4 h1:tEkOQcXgF6dH1G+MVKZrfpYvozGrzb91k6ha7jireSM= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/go.work b/go.work index 491cbc6..ea60813 100644 --- a/go.work +++ b/go.work @@ -1,4 +1,4 @@ -go 1.26.1 +go 1.26.3 use ( . diff --git a/go.work.sum b/go.work.sum index d1fc3d0..7b6c37d 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,16 +1,11 @@ cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= -dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= -github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/FuturFusion/vsock v0.0.0-20260219213046-d78a7104f821/go.mod h1:0atKpUm0hXZMv6+9Kf5crGqV9KKtvkPAElG7/9CCFwU= github.com/LINBIT/golinstor v0.60.0/go.mod h1:TXAMGiskT4fY/koTCOt6qqF60uWobDqUHoB8srNeCnY= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= -github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Rican7/retry v0.3.1/go.mod h1:CxSDrhAyXmTMeEuRAnArMu1FHu48vtfjLREWqVl7Vw0= github.com/Yiling-J/theine-go v0.6.2/go.mod h1:08QpMa5JZ2pKN+UJCRrCasWYO1IKCdl54Xa836rpmDU= github.com/adhocore/gronx v1.19.6/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg= @@ -32,8 +27,6 @@ github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.23/go.mod h1:M8l3mw github.com/aws/aws-sdk-go-v2/service/s3 v1.100.1/go.mod h1:L2dcoOgS2VSgbPLvpak2NyUPsO1TBN7M45Z4H7DlRc4= github.com/aws/smithy-go v1.25.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/aymanbagabas/go-udiff v0.4.1/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= @@ -41,50 +34,27 @@ github.com/breml/newline-after-block v0.0.0-20251225141726-84337171eef7/go.mod h github.com/brianvoe/gofakeit/v7 v7.14.0/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA= github.com/brunoga/deep v1.3.1/go.mod h1:GDV6dnXqn80ezsLSZ5Wlv1PdKAWAO4L5PnKYtv2dgaI= github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cenkalti/hub v1.0.2/go.mod h1:8LAFAZcCasb83vfxatMUnZHRoQcffho2ELpHb+kaTJU= github.com/cenkalti/rpc2 v1.0.5/go.mod h1:2yfU5b86vOr16+iY1jN3MvT6Kxc9Nf8j5iZWwUf7iaw= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I= github.com/checkpoint-restore/go-criu/v8 v8.2.0/go.mod h1:HVKJ1dK+bowJcFI1MtdL2ECIuY+/AtRMHzD9Lqa4uA4= github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= -github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= -github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= -github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= -github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= -github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= -github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/containerd/platforms v1.0.0-rc.4 h1:M42JrUT4zfZTqtkUwkr0GzmUWbfyO5VO0Q5b3op97T4= -github.com/containerd/platforms v1.0.0-rc.4/go.mod h1:lKlMXyLybmBedS/JJm11uDofzI8L2v0J2ZbYvNsbq1A= github.com/cowsql/go-cowsql v1.22.0/go.mod h1:+QzPcM7QRPIBI8XhsKJ47iUtxGY53lsYGX51G1WQ/4s= -github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= -github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:YTIHhz/QFSYnu/EhlF2SpU2Uk+32abacUYA5ZPljz1A= -github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= -github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= -github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c= -github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0/go.mod h1:56wL82FO0bfMU5RvfXoIwSOP2ggqqxT+tAfNEIyxuHw= github.com/dsnet/golib/memfile v1.0.0/go.mod h1:tXGNW9q3RwvWt1VV2qrRKlSSz0npnh12yftCSCy2T64= -github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustinkirkland/golang-petname v0.0.0-20260215035315-f0c533e9ce9b/go.mod h1:8AuBTZBRSFqEYBPYULd+NN474/zZBLP+6WeT5S9xlAc= github.com/eapache/channels v1.1.0/go.mod h1:jMm2qB5Ubtg9zLd+inMZd2/NUvXgzmWXsDaLyQIGfH0= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= -github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= github.com/expr-lang/expr v1.17.8/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= @@ -92,8 +62,6 @@ github.com/fabien-marty/slog-helpers v0.0.0-20240624063600-773d61849b89/go.mod h github.com/fabien-marty/tracerr v0.0.0-20240624051446-7f090eca46ee/go.mod h1:eqKGnFoVPY3Ng/iRRYfMxOum60YwQh7ZzYILKbYI7UY= github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= github.com/flosch/pongo2/v6 v6.0.0/go.mod h1:CuDpFm47R0uGGE7z13/tTlt1Y6zdxvr2RLT5LJhsHEU= github.com/foxboron/go-uefi v0.0.0-20251010190908-d29549a44f29/go.mod h1:sqQZKX1X86EAN4C07n6DcbGC/DCN36BNaX/uNvjzmfk= @@ -105,15 +73,8 @@ github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeekl github.com/gdamore/tcell/v2 v2.13.9/go.mod h1:+Wfe208WDdB7INEtCsNrAN6O2m+wsTPk1RAovjaILlo= github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= github.com/go-co-op/gocron/v2 v2.21.1/go.mod h1:5lEiCKk1oVJV39Zg7/YG10OnaVrDAV5GGR6O0663k6U= -github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= -github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= github.com/go-json-experiment/json v0.0.0-20250813024750-ebf49471dced/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M= -github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.30.2/go.mod h1:mAf2pIOVXjTEBrwUMGKkCWKKPs9NheYGabeB04txQSc= @@ -124,6 +85,7 @@ github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwm github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw= +github.com/google/cel-go v0.28.0/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-eventlog v0.0.3-0.20250422210130-7c3cc8ffe6c4/go.mod h1:7huE5P8w2NTObSwSJjboHmB7ioBNblkijdzoVa2skfQ= @@ -151,14 +113,6 @@ github.com/hexdigest/gowrap v1.4.1/go.mod h1:s+1hE6qakgdaaLqgdwPAj5qKYVBCSbPJhEb github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/insomniacslk/dhcp v0.0.0-20260407060928-11b94ed970f2/go.mod h1:qfvBmyDNp+/liLEYWRvqny/PEz9hGe2Dz833eXILSmo= -github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= -github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw= -github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= -github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= -github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jaypipes/pcidb v1.1.1/go.mod h1:x27LT2krrUgjf875KxQXKB0Ha/YXLdZRVmw6hH0G7g8= github.com/jedib0t/go-pretty/v6 v6.7.8/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU= github.com/jeremija/gosubmit v0.2.8/go.mod h1:Ui+HS073lCFREXBbdfrJzMB57OI/bdxTiLtrDHHhFPI= @@ -170,12 +124,6 @@ github.com/k-sone/critbitgo v1.4.0/go.mod h1:7E6pyoyADnFxlUBEKcnfS49b7SUAQGMK+OA github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/karlseguin/ccache/v3 v3.0.6/go.mod h1:b0qfdUOHl4vJgKFQN41paXIdBb3acAtyX2uWrBAZs1w= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= -github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= -github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= -github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM= -github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/knadh/koanf/maps v0.1.2/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= github.com/knadh/koanf/parsers/yaml v1.1.0/go.mod h1:HHmcHXUrp9cOPcuC+2wrr44GTUB0EC+PyfN3HZD9tFg= @@ -189,17 +137,20 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw= +github.com/lestrrat-go/dsig v1.2.1/go.mod h1:RD2eOaidyPvpc7IJQoO3Qq52RWdy8ZcJs8lrOnoa1Kc= +github.com/lestrrat-go/dsig-secp256k1 v1.0.0/go.mod h1:CxUgAhssb8FToqbL8NjSPoGQlnO4w3LG1P0qPWQm/NU= +github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= +github.com/lestrrat-go/httprc/v3 v3.0.5/go.mod h1:mSMtkZW92Z98M5YoNNztbRGxbXHql7tSitCvaxvo9l0= +github.com/lestrrat-go/jwx/v3 v3.1.1/go.mod h1:uw/MN2M/Xiu4FhwcIwH11Zsh9JWx9SWzgALl7/uIEkU= +github.com/lestrrat-go/option/v2 v2.0.0/go.mod h1:oSySsmzMoR0iRzCDCaUfsCzxQHUEuhOViQObyy7S6Vg= github.com/lmittmann/tint v1.1.3/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= -github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/lxc/distrobuilder/v3 v3.3.2-0.20260410004922-5828176a7f61/go.mod h1:ZcIe6WsvX5vt7cdzu7a5tqRi8v6DX1+9+EmsREbW7L4= github.com/lxc/go-lxc v0.0.0-20260316180011-3af4ce000ed7/go.mod h1:3UTWXVcHfgxE7JM4ZUnsy6bDA8L1vuzwJbJRF6dlB90= github.com/lxc/incus/v6 v6.23.1-0.20260327174201-6acde8bd711a/go.mod h1:efEbxmSexfg8VyYQnBgNQz0dZZLci3s90xcU+VXoCYc= -github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= -github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/maniartech/signals v1.3.1/go.mod h1:AbE8Yy9ZjKCWNU/VhQ+0Ea9KOaTWHp6aOfdLBe5m1iM= github.com/matryer/moq v0.4.0/go.mod h1:kUfalaLk7TcyXhrhonBYQ2Ewun63+/xGbZ7/MzzzC4Y= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.14.44/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ= github.com/mdlayher/arp v0.0.0-20220512170110-6706a2966875/go.mod h1:kfOoFJuHWp76v1RgZCb9/gVUc7XdY877S2uVYbNliGc= @@ -209,37 +160,14 @@ github.com/mdlayher/netx v0.0.0-20230430222610-7e21880baee8/go.mod h1:qhZhwMDNWw github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4= github.com/mdlayher/socket v0.6.0/go.mod h1:q7vozUAnxSqnjHc12Fik5yUKIzfZ8ITCfMkhOtE9z18= github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE= -github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= -github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= +github.com/meigma/authkit v0.3.0/go.mod h1:1BYKxIGBuofA5fnjUipx4VexecXykoclytwB7ODouFY= +github.com/meigma/go-simplestreams v0.1.0/go.mod h1:HruoU+2tMlDEkWoOgJ7uJ/JkbjymEjlSY7f7NMBwOMw= github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= -github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI= -github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= -github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= -github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.1.0 h1:QEt5IStDpxgGjEdtOgpiZ5QhmSl3ax7qy61vi2SwHO8= -github.com/minio/minio-go/v7 v7.1.0/go.mod h1:Dm7WS1AgLmBa0NcQD6SeJnJf+K/EUW3GR7Ks6olB3OA= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= -github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8= -github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= -github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg= -github.com/moby/moby/api v1.54.2/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= -github.com/moby/moby/client v0.4.1 h1:DMQgisVoMkmMs7fp3ROSdiBnoAu8+vo3GggFl06M/wY= -github.com/moby/moby/client v0.4.1/go.mod h1:z52C9O2POPOsnxZAy//WtKcQ32P+jT/NGeXu/7nfjGQ= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= -github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= -github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= -github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= -github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= -github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= -github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= -github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= -github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw= github.com/muesli/crunchy v0.4.1-0.20210519044311-9cd68953298f/go.mod h1:9k4x6xdSbb7WwtAVy0iDjaiDjIk6Wa5AgUIqp+HqOpU= github.com/muesli/mango v0.2.0/go.mod h1:5XFpbC8jY5UUv89YQciiXNlbi+iJgt29VDC5xbzrLL4= @@ -247,8 +175,6 @@ github.com/muesli/mango-cobra v1.3.0/go.mod h1:Cj1ZrBu3806Qw7UjxnAUgE+7tllUBj1NC github.com/muesli/mango-pflag v0.2.0/go.mod h1:X9LT1p/pbGA1wjvEbtwnixujKErkP0jVmrxwrw3fL0Y= github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig= github.com/muhlemmer/httpforwarded v0.1.0/go.mod h1:yo9czKedo2pdZhoXe+yDkGVbU0TJ0q9oQ90BVoDEtw0= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/natefinch/wrap v0.2.0/go.mod h1:6gMHlAl12DwYEfKP3TkuykYUfLSEAvHw67itm4/KAS8= github.com/nwidger/jsoncolor v0.3.2/go.mod h1:Cs34umxLbJvgBMnVNVqhji9BhoT/N/KinHqZptQ7cf4= github.com/oauth2-proxy/mockoidc v0.0.0-20240214162133-caebfff84d25/go.mod h1:eDjgYHYDJbPLBLsyZ6qRaugP0mX8vePOhZ5id1fdzJw= @@ -267,69 +193,40 @@ github.com/openfga/openfga v1.11.5/go.mod h1:dqouURLTeDwoHxpiVHp/z9Ak4sfoVJ+24YE github.com/orcaman/concurrent-map/v2 v2.0.1/go.mod h1:9Eq3TG2oBe5FirmYWQfYO5iH1q0Jv47PLaNK++uCdOM= github.com/osrg/gobgp/v4 v4.5.0/go.mod h1:pgu8waqTvZUYl4eQuPrKNOaVwhHv7Zt9YymuzCaX7f8= github.com/ovn-kubernetes/libovsdb v0.8.1/go.mod h1:ZlnHLzagmLOSvyd9qfxBIZp6wOSOw0IsRsc+6lNUGbU= -github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= -github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/pierrec/lz4/v4 v4.1.26/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= github.com/pires/go-proxyproto v0.12.0/go.mod h1:qUvfqUMEoX7T8g0q7TQLDnhMjdTrxnG0hvpMn+7ePNI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.10/go.mod h1:bJ1a7uDhrX/4OII+agvy28lzRvQrmIQuaHrcI1HbeGA= github.com/pkg/xattr v0.4.12/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU= -github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/pressly/goose/v3 v3.27.1 h1:6uEvcprBybDmW4hcz3gYujhARhye+GoWKhEWyzD5sh4= -github.com/pressly/goose/v3 v3.27.1/go.mod h1:maruOxsPnIG2yHHyo8UqKWXYKFcH7Q76csUV7+7KYoM= -github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= -github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= -github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= -github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= -github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= -github.com/prometheus/otlptranslator v1.0.0 h1:s0LJW/iN9dkIH+EnhiD3BlkkP5QVIUVEoIwkU+A6qos= -github.com/prometheus/otlptranslator v1.0.0/go.mod h1:vRYWnXvI6aWGpsdY/mOT/cbeVRBlPWtBNDb7kGR3uKM= -github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/quasilyte/go-ruleguard/dsl v0.3.23/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/rivo/tview v0.42.0/go.mod h1:cSfIYfhpSGCjp3r/ECJb+GKS7cGJnqV8vfjQPwoXyfY= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rootless-containers/proto/go-proto v0.0.0-20260207013450-f6ee952d53d9/go.mod h1:LLjEAc6zmycfeN7/1fxIphWQPjHpTt7ElqT7eVf8e4A= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= -github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= -github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= github.com/rung/go-safecast v1.0.1/go.mod h1:dzUcUS2UMtbfVc7w6mx/Ur3UYcpXEZC+WilISksJ4P8= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= github.com/schollz/progressbar/v3 v3.19.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec= +github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= -github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= -github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= -github.com/shirou/gopsutil/v4 v4.26.4 h1:B4SXVbcwTyrocPHEmWBC4uCYr4Xcu3MK1TXqbprAOWY= -github.com/shirou/gopsutil/v4 v4.26.4/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/shogo82148/logrus-slog-hook v0.1.0/go.mod h1:D2Ge8IJO5/lN5LXQ4Aq5NezL1iajb08s/xNCUru8+0Y= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= -github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/smallstep/pkcs7 v0.2.1/go.mod h1:RcXHsMfL+BzH8tRhmrF1NkkpebKpq3JEM66cOFxanf0= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/testcontainers/testcontainers-go v0.40.0/go.mod h1:FSXV5KQtX2HAMlm7U3APNyLkkap35zNLxukw9oBi/MY= -github.com/testcontainers/testcontainers-go v0.42.0 h1:He3IhTzTZOygSXLJPMX7n44XtK+qhjat1nI9cneBbUY= -github.com/testcontainers/testcontainers-go v0.42.0/go.mod h1:vZjdY1YmUA1qEForxOIOazfsrdyORJAbhi0bp8plN30= github.com/testcontainers/testcontainers-go/modules/openfga v0.40.0/go.mod h1:zwLB9Af9gw2ViufG7oZT/DpmubgKhnpleOWItgcnLVY= -github.com/testcontainers/testcontainers-go/modules/postgres v0.42.0 h1:GCbb1ndrF7OTDiIvxXyItaDab4qkzTFJ48LKFdM7EIo= -github.com/testcontainers/testcontainers-go/modules/postgres v0.42.0/go.mod h1:IRPBaI8jXdrNfD0e4Zm7Fbcgaz5shKxOQv4axiL09xs= github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/timpalpant/gzran v0.0.0-20201127163450-7b631e56f57b/go.mod h1:yTxMuBKYLrj6gYYtK3gK0ifBhjiBYtD3URZiNK7vBt0= -github.com/tinylib/msgp v1.6.1 h1:ESRv8eL3u+DNHUoSAAQRE50Hm162zqAnBoGv9PzScPY= -github.com/tinylib/msgp v1.6.1/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= -github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= -github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= -github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA= github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo= +github.com/valyala/fastjson v1.6.10/go.mod h1:e6FubmQouUNP73jtMLmcbxS6ydWIpOfhz34TSfO3JaE= github.com/vbatts/go-mtree v0.7.0/go.mod h1:EjdpFC+LZy1TXbRGNa1MKKgjQ+7ew3foMFJK8o4/TdY= github.com/vektra/mockery/v3 v3.6.4/go.mod h1:z9Wr23Ha8etImqQwS3boTNR9WkjX6tIklW5c88DRkSw= github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= @@ -341,51 +238,25 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1: github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zclconf/go-cty v1.17.0/go.mod h1:wqFzcImaLTI6A5HfsRwB0nj5n0MRZFwmey8YoFPPs3U= -github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs= -github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s= github.com/zitadel/logging v0.7.0/go.mod h1:9A6h9feBF/3u0IhA4uffdzSDY7mBaf7RE78H5sFMINQ= github.com/ztrue/tracerr v0.4.0/go.mod h1:PaFfYlas0DfmXNpo7Eay4MFhZUONqvXM+T2HyGPpngk= -go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= -go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= -go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= -go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs= -go.opentelemetry.io/otel/exporters/prometheus v0.65.0 h1:jOveH/b4lU9HT7y+Gfamf18BqlOuz2PWEvs8yM7Q6XE= -go.opentelemetry.io/otel/exporters/prometheus v0.65.0/go.mod h1:i1P8pcumauPtUI4YNopea1dhzEMuEqWP1xoUZDylLHo= -go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= -go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= -go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= -go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= -go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= -go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= -go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= -go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.starlark.net v0.0.0-20260326113308-fadfc96def35/go.mod h1:Iue6g6iirlfLoVi/DYCi5/x0h/bAOuWF3dULTKpt2Vo= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= -go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= go4.org/mem v0.0.0-20240501181205-ae6ca9944745/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= -golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= -golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c/go.mod h1:TpUTTEp9frx7rTdLpC9gFG9kdI7zVLFTFFlqaH2Cncw= golang.org/x/telemetry v0.0.0-20260409153401-be6f6cb8b1fa/go.mod h1:kHjTxDEnAu6/Nl9lDkzjWpR+bmKfxeiRuSDlsMb70gE= golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= -golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= diff --git a/internal/cli/publish.go b/internal/cli/publish.go index 9a53ad2..011f895 100644 --- a/internal/cli/publish.go +++ b/internal/cli/publish.go @@ -57,7 +57,10 @@ func newPublishCommand(rt *runtime) *cobra.Command { return err } - publisher, err := publish.NewPublisher(catalogClient, uploader) + publisher, err := publish.NewPublisher(catalogClient, uploader, publish.PublisherOptions{ + Timeout: pubConfig.timeout, + PollInterval: pubConfig.pollInterval, + }) if err != nil { return err } diff --git a/internal/cli/root_test.go b/internal/cli/root_test.go index 5b7c686..03edfb6 100644 --- a/internal/cli/root_test.go +++ b/internal/cli/root_test.go @@ -416,6 +416,7 @@ incusos: { Once() publishCatalog.EXPECT(). AddArtifact(mock.Anything, "published-image", "v1.0.0", imgsrv.AddArtifactRequest{ + Variant: "default", OperatingSystem: "incusos", Architecture: "x86_64", Format: imgsrv.ArtifactFormatRawGZ, @@ -425,6 +426,7 @@ incusos: { }). Return(imgsrv.Artifact{ ID: "artifact-1", + Variant: "default", OperatingSystem: "incusos", Architecture: "x86_64", Format: imgsrv.ArtifactFormatRawGZ, @@ -435,7 +437,21 @@ incusos: { Once() publishCatalog.EXPECT(). PublishVersion(mock.Anything, "published-image", "v1.0.0"). - Return(imgsrv.ImageVersion{Version: "v1.0.0", State: imgsrv.ImageVersionStatePublished}, nil). + Return(imgsrv.PublishJob{ + ID: "publish-job-1", + ImageName: "published-image", + Version: "v1.0.0", + State: imgsrv.PublishJobStateQueued, + }, nil). + Once() + publishCatalog.EXPECT(). + GetPublishJob(mock.Anything, "publish-job-1"). + Return(imgsrv.PublishJob{ + ID: "publish-job-1", + ImageName: "published-image", + Version: "v1.0.0", + State: imgsrv.PublishJobStateSucceeded, + }, nil). Once() publishCatalog.EXPECT(). PutAlias(mock.Anything, "published-image", "latest", imgsrv.PutAliasRequest{Version: "v1.0.0"}). diff --git a/internal/providers/incusos/provider.go b/internal/providers/incusos/provider.go index 5d1c52f..87069fc 100644 --- a/internal/providers/incusos/provider.go +++ b/internal/providers/incusos/provider.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "path/filepath" + "sort" "strings" "github.com/meigma/imgcli/internal/providers" @@ -77,90 +78,120 @@ func (p *Provider) Build(ctx context.Context, req providers.BuildRequest) (provi return providers.BuildResult{}, errors.New("incusos image injector is required") } - variantName, variant, err := singleVariant(p.config) + artifacts, err := planArtifacts(req, p.config) if err != nil { return providers.BuildResult{}, err } - imageType, err := imageTypeForFormat(variant.Artifact.Format) - if err != nil { - return providers.BuildResult{}, err - } - - outputPath, err := artifactOutputPath(req, variantName, variant.Artifact) + seed, err := p.options.SeedBuilder.BuildSeed(ctx, p.config) if err != nil { return providers.BuildResult{}, err } - source := resolveSource(p.config.Defaults, variant.Source) - asset, err := p.options.Catalog.ResolveImage(ctx, ImageQuery{ - Channel: source.Channel, - Version: source.Version, - Architecture: variant.Artifact.Architecture, - Type: imageType, - }) - if err != nil { - return providers.BuildResult{}, err - } + builtArtifacts := make([]providers.BuiltArtifact, 0, len(artifacts)) + for _, artifact := range artifacts { + source := resolveSource(p.config.Defaults, artifact.variant.Source) + asset, err := p.options.Catalog.ResolveImage(ctx, ImageQuery{ + Channel: source.Channel, + Version: source.Version, + Architecture: artifact.variant.Artifact.Architecture, + Type: artifact.imageType, + }) + if err != nil { + return providers.BuildResult{}, err + } - downloaded, err := p.options.Downloader.DownloadImage(ctx, asset) - if err != nil { - return providers.BuildResult{}, err - } + downloaded, err := p.options.Downloader.DownloadImage(ctx, asset) + if err != nil { + return providers.BuildResult{}, err + } - seed, err := p.options.SeedBuilder.BuildSeed(ctx, p.config) - if err != nil { - return providers.BuildResult{}, err - } + customized, err := p.options.ImageInjector.InjectSeed(ctx, downloaded, seed, artifact.plan.OutputPath) + if err != nil { + return providers.BuildResult{}, err + } - customized, err := p.options.ImageInjector.InjectSeed(ctx, downloaded, seed, outputPath) - if err != nil { - return providers.BuildResult{}, err + builtArtifacts = append(builtArtifacts, providers.BuiltArtifact{ + Plan: artifact.plan, + Path: customized.Path, + Size: customized.Size, + SHA256: customized.SHA256, + }) } - artifactPlan := providers.ArtifactPlan{ - Key: core.ArtifactKey(variantName), - Variant: variantName, - Architecture: variant.Artifact.Architecture, - OperatingSystem: artifactOperatingSystem(variant.Artifact), - Format: variant.Artifact.Format, - MediaType: artifactMediaType(variant.Artifact), - OutputPath: outputPath, - Labels: variant.Artifact.Labels, - Annotations: variant.Artifact.Annotations, - } plan := req.Plan plan.Provider = providerName plan.OutputDir = outputDir(req) - plan.Artifacts = []providers.ArtifactPlan{artifactPlan} + plan.Artifacts = make([]providers.ArtifactPlan, 0, len(artifacts)) + for _, artifact := range artifacts { + plan.Artifacts = append(plan.Artifacts, artifact.plan) + } return providers.BuildResult{ - Plan: plan, - Artifacts: []providers.BuiltArtifact{ - { - Plan: artifactPlan, - Path: customized.Path, - Size: customized.Size, - SHA256: customized.SHA256, - }, - }, + Plan: plan, + Artifacts: builtArtifacts, }, nil } -func singleVariant(config Config) (core.VariantName, incusosschema.Variant, error) { - switch len(config.Variants) { - case 0: - return "", incusosschema.Variant{}, errors.New("incusos build requires exactly one variant, got 0") - case 1: - for name, variant := range config.Variants { - return name, variant, nil +type plannedArtifact struct { + variant incusosschema.Variant + imageType ImageType + plan providers.ArtifactPlan +} + +func planArtifacts(req providers.BuildRequest, config Config) ([]plannedArtifact, error) { + if len(config.Variants) == 0 { + return nil, errors.New("incusos build requires at least one variant") + } + + names := make([]string, 0, len(config.Variants)) + for name := range config.Variants { + names = append(names, string(name)) + } + sort.Strings(names) + + artifacts := make([]plannedArtifact, 0, len(names)) + outputPaths := map[string]core.VariantName{} + for _, name := range names { + variantName := core.VariantName(name) + variant := config.Variants[variantName] + imageType, err := imageTypeForFormat(variant.Artifact.Format) + if err != nil { + return nil, err + } + outputPath, err := artifactOutputPath(req, variantName, variant.Artifact) + if err != nil { + return nil, err + } + if previous, exists := outputPaths[outputPath]; exists { + return nil, fmt.Errorf( + "incusos artifact output path %q is used by variants %q and %q", + outputPath, + previous, + variantName, + ) + } + outputPaths[outputPath] = variantName + + artifactPlan := providers.ArtifactPlan{ + Key: core.ArtifactKey(variantName), + Variant: variantName, + Architecture: variant.Artifact.Architecture, + OperatingSystem: artifactOperatingSystem(variant.Artifact), + Format: variant.Artifact.Format, + MediaType: artifactMediaType(variant.Artifact), + OutputPath: outputPath, + Labels: variant.Artifact.Labels, + Annotations: variant.Artifact.Annotations, } + artifacts = append(artifacts, plannedArtifact{ + variant: variant, + imageType: imageType, + plan: artifactPlan, + }) } - return "", incusosschema.Variant{}, fmt.Errorf( - "incusos build requires exactly one variant, got %d", - len(config.Variants), - ) + return artifacts, nil } func artifactOutputPath( diff --git a/internal/providers/incusos/provider_test.go b/internal/providers/incusos/provider_test.go index 56143be..df57278 100644 --- a/internal/providers/incusos/provider_test.go +++ b/internal/providers/incusos/provider_test.go @@ -161,6 +161,172 @@ func TestProviderBuildCreatesCustomizedImage(t *testing.T) { } } +func TestProviderBuildCreatesMultipleVariantsInStableOrder(t *testing.T) { + ctx := context.Background() + outputDir := t.TempDir() + asset := ImageAsset{ + Version: Version("202604261712"), + Architecture: core.Architecture("amd64"), + Type: ImageTypeRaw, + URL: "https://example.invalid/incusos.img.gz", + SHA256: "source-sha", + Size: 42, + } + downloaded := DownloadedImage{ + Asset: asset, + Path: "/cache/source.img.gz", + SHA256: "source-sha", + Size: 42, + } + seed := SeedArchive{Data: []byte("seed")} + customized := CustomizedImage{ + Source: downloaded, + Size: 99, + SHA256: "custom-sha", + } + catalog := &recordingCatalog{asset: asset} + downloader := &recordingDownloader{image: downloaded} + seedBuilder := &recordingSeedBuilder{seed: seed} + injector := &recordingImageInjector{image: customized} + config := Config{ + Defaults: &incusosschema.Defaults{ + Source: &incusosschema.Source{ + Channel: ChannelStable, + Version: Version("202604202240"), + }, + }, + Seed: &incusosschema.Seed{}, + Variants: map[core.VariantName]incusosschema.Variant{ + "secureboot": { + Artifact: core.ArtifactIntent{ + Architecture: core.Architecture("amd64"), + Format: core.ArtifactFormat("raw.gz"), + }, + }, + "default": { + Source: &incusosschema.Source{ + Channel: ChannelTesting, + Version: Version("202604261712"), + }, + Artifact: core.ArtifactIntent{ + Architecture: core.Architecture("amd64"), + Format: core.ArtifactFormat("raw.gz"), + }, + }, + }, + } + provider := New(config, Options{ + Catalog: catalog, + Downloader: downloader, + SeedBuilder: seedBuilder, + ImageInjector: injector, + }) + + result, err := provider.Build(ctx, providers.BuildRequest{ + Plan: providers.Plan{ + Image: core.Image{Name: core.Name("test-image")}, + }, + OutputDir: outputDir, + }) + + require.NoError(t, err) + assert.Equal(t, []ImageQuery{ + { + Channel: ChannelTesting, + Version: Version("202604261712"), + Architecture: core.Architecture("amd64"), + Type: ImageTypeRaw, + }, + { + Channel: ChannelStable, + Version: Version("202604202240"), + Architecture: core.Architecture("amd64"), + Type: ImageTypeRaw, + }, + }, catalog.queries) + assert.Equal(t, []Config{config}, seedBuilder.configs) + require.Len(t, result.Artifacts, 2) + assert.Equal(t, []providers.ArtifactPlan{ + { + Key: core.ArtifactKey("default"), + Variant: core.VariantName("default"), + Architecture: core.Architecture("amd64"), + OperatingSystem: "incusos", + Format: core.ArtifactFormat("raw.gz"), + MediaType: "application/gzip", + OutputPath: filepath.Join(outputDir, "test-image-default-amd64.raw.gz"), + }, + { + Key: core.ArtifactKey("secureboot"), + Variant: core.VariantName("secureboot"), + Architecture: core.Architecture("amd64"), + OperatingSystem: "incusos", + Format: core.ArtifactFormat("raw.gz"), + MediaType: "application/gzip", + OutputPath: filepath.Join(outputDir, "test-image-secureboot-amd64.raw.gz"), + }, + }, result.Plan.Artifacts) + assert.Equal(t, result.Plan.Artifacts[0], result.Artifacts[0].Plan) + assert.Equal(t, result.Plan.Artifacts[1], result.Artifacts[1].Plan) + assert.Equal(t, []injectCall{ + { + image: downloaded, + seed: seed, + outputPath: filepath.Join(outputDir, "test-image-default-amd64.raw.gz"), + }, + { + image: downloaded, + seed: seed, + outputPath: filepath.Join(outputDir, "test-image-secureboot-amd64.raw.gz"), + }, + }, injector.calls) +} + +func TestProviderBuildRejectsDuplicateOutputPathsBeforeBuild(t *testing.T) { + catalog := &recordingCatalog{asset: ImageAsset{}} + downloader := &recordingDownloader{} + seedBuilder := &recordingSeedBuilder{seed: SeedArchive{Data: []byte("seed")}} + injector := &recordingImageInjector{} + config := Config{ + Seed: &incusosschema.Seed{}, + Variants: map[core.VariantName]incusosschema.Variant{ + "default": { + Artifact: core.ArtifactIntent{ + Architecture: core.Architecture("amd64"), + Format: core.ArtifactFormat("raw.gz"), + Filename: "same.img.gz", + }, + }, + "secureboot": { + Artifact: core.ArtifactIntent{ + Architecture: core.Architecture("amd64"), + Format: core.ArtifactFormat("raw.gz"), + Filename: "same.img.gz", + }, + }, + }, + } + provider := New(config, Options{ + Catalog: catalog, + Downloader: downloader, + SeedBuilder: seedBuilder, + ImageInjector: injector, + }) + + result, err := provider.Build(context.Background(), providers.BuildRequest{ + Plan: providers.Plan{Image: core.Image{Name: core.Name("test-image")}}, + OutputDir: t.TempDir(), + }) + + require.Error(t, err) + require.ErrorContains(t, err, "incusos artifact output path") + assert.Empty(t, result) + assert.Empty(t, catalog.queries) + assert.Empty(t, downloader.assets) + assert.Empty(t, seedBuilder.configs) + assert.Empty(t, injector.calls) +} + func TestProviderBuildErrors(t *testing.T) { catalogErr := errors.New("catalog failed") downloadErr := errors.New("download failed") @@ -204,18 +370,7 @@ func TestProviderBuildErrors(t *testing.T) { Variants: map[core.VariantName]incusosschema.Variant{}, }, options: optionsWithout(func(_ *Options) {}), - wantErr: "incusos build requires exactly one variant, got 0", - }, - { - name: "multiple variants", - config: Config{ - Variants: map[core.VariantName]incusosschema.Variant{ - "default": {}, - "other": {}, - }, - }, - options: optionsWithout(func(_ *Options) {}), - wantErr: "incusos build requires exactly one variant, got 2", + wantErr: "incusos build requires at least one variant", }, { name: "unsupported format", diff --git a/internal/publish/mocks/catalog_client.go b/internal/publish/mocks/catalog_client.go index f40860f..1dfad14 100644 --- a/internal/publish/mocks/catalog_client.go +++ b/internal/publish/mocks/catalog_client.go @@ -254,23 +254,89 @@ func (_c *MockCatalogClient_CreateImage_Call) RunAndReturn(run func(context1 con return _c } +// GetPublishJob provides a mock function for the type MockCatalogClient +func (_mock *MockCatalogClient) GetPublishJob(context1 context.Context, s string) (client.PublishJob, error) { + ret := _mock.Called(context1, s) + + if len(ret) == 0 { + panic("no return value specified for GetPublishJob") + } + + var r0 client.PublishJob + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, string) (client.PublishJob, error)); ok { + return returnFunc(context1, s) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, string) client.PublishJob); ok { + r0 = returnFunc(context1, s) + } else { + r0 = ret.Get(0).(client.PublishJob) + } + if returnFunc, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = returnFunc(context1, s) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockCatalogClient_GetPublishJob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPublishJob' +type MockCatalogClient_GetPublishJob_Call struct { + *mock.Call +} + +// GetPublishJob is a helper method to define mock.On call +// - context1 context.Context +// - s string +func (_e *MockCatalogClient_Expecter) GetPublishJob(context1 interface{}, s interface{}) *MockCatalogClient_GetPublishJob_Call { + return &MockCatalogClient_GetPublishJob_Call{Call: _e.mock.On("GetPublishJob", context1, s)} +} + +func (_c *MockCatalogClient_GetPublishJob_Call) Run(run func(context1 context.Context, s string)) *MockCatalogClient_GetPublishJob_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 string + if args[1] != nil { + arg1 = args[1].(string) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *MockCatalogClient_GetPublishJob_Call) Return(publishJob client.PublishJob, err error) *MockCatalogClient_GetPublishJob_Call { + _c.Call.Return(publishJob, err) + return _c +} + +func (_c *MockCatalogClient_GetPublishJob_Call) RunAndReturn(run func(context1 context.Context, s string) (client.PublishJob, error)) *MockCatalogClient_GetPublishJob_Call { + _c.Call.Return(run) + return _c +} + // PublishVersion provides a mock function for the type MockCatalogClient -func (_mock *MockCatalogClient) PublishVersion(context1 context.Context, s string, s1 string) (client.ImageVersion, error) { +func (_mock *MockCatalogClient) PublishVersion(context1 context.Context, s string, s1 string) (client.PublishJob, error) { ret := _mock.Called(context1, s, s1) if len(ret) == 0 { panic("no return value specified for PublishVersion") } - var r0 client.ImageVersion + var r0 client.PublishJob var r1 error - if returnFunc, ok := ret.Get(0).(func(context.Context, string, string) (client.ImageVersion, error)); ok { + if returnFunc, ok := ret.Get(0).(func(context.Context, string, string) (client.PublishJob, error)); ok { return returnFunc(context1, s, s1) } - if returnFunc, ok := ret.Get(0).(func(context.Context, string, string) client.ImageVersion); ok { + if returnFunc, ok := ret.Get(0).(func(context.Context, string, string) client.PublishJob); ok { r0 = returnFunc(context1, s, s1) } else { - r0 = ret.Get(0).(client.ImageVersion) + r0 = ret.Get(0).(client.PublishJob) } if returnFunc, ok := ret.Get(1).(func(context.Context, string, string) error); ok { r1 = returnFunc(context1, s, s1) @@ -316,12 +382,12 @@ func (_c *MockCatalogClient_PublishVersion_Call) Run(run func(context1 context.C return _c } -func (_c *MockCatalogClient_PublishVersion_Call) Return(imageVersion client.ImageVersion, err error) *MockCatalogClient_PublishVersion_Call { - _c.Call.Return(imageVersion, err) +func (_c *MockCatalogClient_PublishVersion_Call) Return(publishJob client.PublishJob, err error) *MockCatalogClient_PublishVersion_Call { + _c.Call.Return(publishJob, err) return _c } -func (_c *MockCatalogClient_PublishVersion_Call) RunAndReturn(run func(context1 context.Context, s string, s1 string) (client.ImageVersion, error)) *MockCatalogClient_PublishVersion_Call { +func (_c *MockCatalogClient_PublishVersion_Call) RunAndReturn(run func(context1 context.Context, s string, s1 string) (client.PublishJob, error)) *MockCatalogClient_PublishVersion_Call { _c.Call.Return(run) return _c } diff --git a/internal/publish/release.go b/internal/publish/release.go index 2ab5612..7a5696d 100644 --- a/internal/publish/release.go +++ b/internal/publish/release.go @@ -6,16 +6,23 @@ import ( "fmt" "net/http" "strings" + "time" imgsrv "github.com/meigma/imgsrv/client" ) +const ( + defaultPublisherTimeout = time.Minute + defaultPublisherPollInterval = time.Second +) + // CatalogClient is the imgsrv catalog operation seam used by the release publisher. type CatalogClient interface { CreateImage(context.Context, imgsrv.CreateImageRequest) (imgsrv.Image, error) CreateDraftVersion(context.Context, string, imgsrv.CreateDraftVersionRequest) (imgsrv.ImageVersion, error) AddArtifact(context.Context, string, string, imgsrv.AddArtifactRequest) (imgsrv.Artifact, error) - PublishVersion(context.Context, string, string) (imgsrv.ImageVersion, error) + PublishVersion(context.Context, string, string) (imgsrv.PublishJob, error) + GetPublishJob(context.Context, string) (imgsrv.PublishJob, error) PutAlias(context.Context, string, string, imgsrv.PutAliasRequest) (imgsrv.Alias, error) } @@ -23,6 +30,13 @@ type CatalogClient interface { type Publisher struct { uploader *Uploader catalog CatalogClient + options PublisherOptions +} + +// PublisherOptions configures release publication. +type PublisherOptions struct { + Timeout time.Duration + PollInterval time.Duration } // ReleaseRequest describes one image release publication. @@ -76,17 +90,35 @@ type uploadedReleaseArtifact struct { } // NewPublisher constructs a release publisher. -func NewPublisher(catalog CatalogClient, uploader *Uploader) (*Publisher, error) { +func NewPublisher(catalog CatalogClient, uploader *Uploader, options ...PublisherOptions) (*Publisher, error) { if catalog == nil { return nil, errors.New("configure imgsrv publisher: catalog client is required") } if uploader == nil { return nil, errors.New("configure imgsrv publisher: uploader is required") } + if len(options) > 1 { + return nil, errors.New("configure imgsrv publisher: at most one options value is supported") + } + + publisherOptions := PublisherOptions{ + Timeout: defaultPublisherTimeout, + PollInterval: defaultPublisherPollInterval, + } + if len(options) == 1 { + publisherOptions = options[0] + } + if publisherOptions.Timeout <= 0 { + return nil, errors.New("configure imgsrv publisher: publish timeout must be positive") + } + if publisherOptions.PollInterval <= 0 { + return nil, errors.New("configure imgsrv publisher: publish poll interval must be positive") + } return &Publisher{ uploader: uploader, catalog: catalog, + options: publisherOptions, }, nil } @@ -147,7 +179,7 @@ func (p *Publisher) PublishRelease(ctx context.Context, request ReleaseRequest) result.Artifacts = append(result.Artifacts, published) } - publishedVersion, err := p.catalog.PublishVersion(ctx, request.ImageName, request.Version) + publishJob, err := p.catalog.PublishVersion(ctx, request.ImageName, request.Version) if err != nil { return ReleaseResult{}, fmt.Errorf( "publish imgsrv version %s %s: %w", @@ -156,7 +188,10 @@ func (p *Publisher) PublishRelease(ctx context.Context, request ReleaseRequest) err, ) } - result.State = publishedVersion.State + if _, err := p.waitPublished(ctx, publishJob); err != nil { + return ReleaseResult{}, err + } + result.State = imgsrv.ImageVersionStatePublished for _, alias := range request.Aliases { if _, err := p.catalog.PutAlias(ctx, request.ImageName, alias, imgsrv.PutAliasRequest{ @@ -219,6 +254,7 @@ func (p *Publisher) addArtifact( artifact uploadedReleaseArtifact, ) (PublishedReleaseArtifact, error) { added, err := p.catalog.AddArtifact(ctx, request.ImageName, request.Version, imgsrv.AddArtifactRequest{ + Variant: artifact.request.Variant, OperatingSystem: artifact.request.OperatingSystem, Architecture: artifact.request.Architecture, Format: artifact.request.Format, @@ -238,7 +274,7 @@ func (p *Publisher) addArtifact( return PublishedReleaseArtifact{ ArtifactKey: artifact.request.Key, - Variant: artifact.request.Variant, + Variant: added.Variant, LocalPath: artifact.request.LocalPath, ServerArtifactID: added.ID.String(), OperatingSystem: added.OperatingSystem, @@ -250,6 +286,67 @@ func (p *Publisher) addArtifact( }, nil } +func (p *Publisher) waitPublished(ctx context.Context, job imgsrv.PublishJob) (imgsrv.PublishJob, error) { + finalJob, err := p.publishJobResult(job) + if err != nil { + return imgsrv.PublishJob{}, err + } + if finalJob.State == imgsrv.PublishJobStateSucceeded { + return finalJob, nil + } + + waitCtx, cancel := context.WithTimeout(ctx, p.options.Timeout) + defer cancel() + + ticker := time.NewTicker(p.options.PollInterval) + defer ticker.Stop() + + last := job + for { + select { + case <-waitCtx.Done(): + if errors.Is(waitCtx.Err(), context.DeadlineExceeded) { + return imgsrv.PublishJob{}, fmt.Errorf( + "publish imgsrv job %s did not finish before timeout; last state was %q", + last.ID, + last.State, + ) + } + return imgsrv.PublishJob{}, fmt.Errorf("wait for imgsrv publish job %s: %w", job.ID, waitCtx.Err()) + case <-ticker.C: + current, err := p.catalog.GetPublishJob(waitCtx, job.ID.String()) + if err != nil { + return imgsrv.PublishJob{}, fmt.Errorf("get imgsrv publish job %s status: %w", job.ID, err) + } + finalJob, err := p.publishJobResult(current) + if err != nil { + return imgsrv.PublishJob{}, err + } + if finalJob.State == imgsrv.PublishJobStateSucceeded { + return finalJob, nil + } + last = current + } + } +} + +func (p *Publisher) publishJobResult(job imgsrv.PublishJob) (imgsrv.PublishJob, error) { + switch job.State { + case imgsrv.PublishJobStateSucceeded: + return job, nil + case imgsrv.PublishJobStateFailed: + message := "unknown failure" + if job.FailureMessage != nil && strings.TrimSpace(*job.FailureMessage) != "" { + message = strings.TrimSpace(*job.FailureMessage) + } + return imgsrv.PublishJob{}, fmt.Errorf("publish imgsrv job %s failed: %s", job.ID, message) + case imgsrv.PublishJobStateQueued, imgsrv.PublishJobStateRunning: + return imgsrv.PublishJob{}, nil + default: + return imgsrv.PublishJob{}, fmt.Errorf("publish imgsrv job %s entered unsupported state %q", job.ID, job.State) + } +} + func uploadArtifact(artifact ReleaseArtifact) Artifact { return Artifact{ Path: artifact.LocalPath, diff --git a/internal/publish/release_test.go b/internal/publish/release_test.go index 244c19d..1269bc2 100644 --- a/internal/publish/release_test.go +++ b/internal/publish/release_test.go @@ -25,7 +25,7 @@ func TestPublisherPublishesReleaseAndAliases(t *testing.T) { artifactPath := writePublishTestArtifact(t, "artifact.raw.gz", artifactBody) events := []string{} - expectReadyUpload(t, uploads, artifactPath, int64(len(artifactBody)), "abc123", "application/gzip") + expectReadyUpload(t, uploads, artifactPath, int64(len(artifactBody)), "abc123") catalog.EXPECT(). CreateImage(mock.Anything, imgsrv.CreateImageRequest{Name: "incusos"}). Run(func(_ context.Context, _ imgsrv.CreateImageRequest) { events = append(events, "create-image") }). @@ -40,6 +40,7 @@ func TestPublisherPublishesReleaseAndAliases(t *testing.T) { Once() catalog.EXPECT(). AddArtifact(mock.Anything, "incusos", "v1.0.0", imgsrv.AddArtifactRequest{ + Variant: "default", OperatingSystem: "incusos", Architecture: "x86_64", Format: imgsrv.ArtifactFormatRawGZ, @@ -52,6 +53,7 @@ func TestPublisherPublishesReleaseAndAliases(t *testing.T) { }). Return(imgsrv.Artifact{ ID: "artifact-1", + Variant: "default", OperatingSystem: "incusos", Architecture: "x86_64", Format: imgsrv.ArtifactFormatRawGZ, @@ -63,7 +65,22 @@ func TestPublisherPublishesReleaseAndAliases(t *testing.T) { catalog.EXPECT(). PublishVersion(mock.Anything, "incusos", "v1.0.0"). Run(func(_ context.Context, _ string, _ string) { events = append(events, "publish-version") }). - Return(imgsrv.ImageVersion{Version: "v1.0.0", State: imgsrv.ImageVersionStatePublished}, nil). + Return(imgsrv.PublishJob{ + ID: "publish-job-1", + ImageName: "incusos", + Version: "v1.0.0", + State: imgsrv.PublishJobStateQueued, + }, nil). + Once() + catalog.EXPECT(). + GetPublishJob(mock.Anything, "publish-job-1"). + Run(func(_ context.Context, _ string) { events = append(events, "get-publish-job") }). + Return(imgsrv.PublishJob{ + ID: "publish-job-1", + ImageName: "incusos", + Version: "v1.0.0", + State: imgsrv.PublishJobStateSucceeded, + }, nil). Once() catalog.EXPECT(). PutAlias(mock.Anything, "incusos", "latest", imgsrv.PutAliasRequest{Version: "v1.0.0"}). @@ -113,11 +130,124 @@ func TestPublisherPublishesReleaseAndAliases(t *testing.T) { "create-version", "add-artifact", "publish-version", + "get-publish-job", "alias-latest", "alias-prod", }, events) } +func TestPublisherPublishesMultipleArtifactVariants(t *testing.T) { + uploads := mocks.NewMockUploadsClient(t) + catalog := mocks.NewMockCatalogClient(t) + defaultBody := bytes.Repeat([]byte("a"), int(publish.MinPartSizeBytes)) + secureBootBody := bytes.Repeat([]byte("b"), int(publish.MinPartSizeBytes)) + defaultPath := writePublishTestArtifact(t, "default.raw.gz", defaultBody) + secureBootPath := writePublishTestArtifact(t, "secureboot.raw.gz", secureBootBody) + defaultArtifact := releaseTestArtifact(defaultPath, int64(len(defaultBody))) + secureBootArtifact := releaseTestArtifact(secureBootPath, int64(len(secureBootBody))) + secureBootArtifact.Key = "secureboot" + secureBootArtifact.Variant = "secureboot" + secureBootArtifact.Digest = "def456" + + expectReadyUpload(t, uploads, defaultPath, int64(len(defaultBody)), "abc123") + expectReadyUpload(t, uploads, secureBootPath, int64(len(secureBootBody)), "def456") + catalog.EXPECT(). + CreateImage(mock.Anything, imgsrv.CreateImageRequest{Name: "incusos"}). + Return(imgsrv.Image{Name: "incusos"}, nil). + Once() + catalog.EXPECT(). + CreateDraftVersion(mock.Anything, "incusos", imgsrv.CreateDraftVersionRequest{Version: "v1.0.0"}). + Return(imgsrv.ImageVersion{Version: "v1.0.0", State: imgsrv.ImageVersionStateDraft}, nil). + Once() + catalog.EXPECT(). + AddArtifact(mock.Anything, "incusos", "v1.0.0", imgsrv.AddArtifactRequest{ + Variant: "default", + OperatingSystem: "incusos", + Architecture: "x86_64", + Format: imgsrv.ArtifactFormatRawGZ, + PrimaryBlobDigest: "sha256:abc123", + PrimaryBlobSizeBytes: int64(len(defaultBody)), + PrimaryMediaType: "application/gzip", + }). + Return(imgsrv.Artifact{ + ID: "artifact-1", + Variant: "default", + OperatingSystem: "incusos", + Architecture: "x86_64", + Format: imgsrv.ArtifactFormatRawGZ, + PrimaryBlobDigest: "sha256:abc123", + PrimaryBlobSizeBytes: int64(len(defaultBody)), + PrimaryMediaType: "application/gzip", + }, nil). + Once() + catalog.EXPECT(). + AddArtifact(mock.Anything, "incusos", "v1.0.0", imgsrv.AddArtifactRequest{ + Variant: "secureboot", + OperatingSystem: "incusos", + Architecture: "x86_64", + Format: imgsrv.ArtifactFormatRawGZ, + PrimaryBlobDigest: "sha256:def456", + PrimaryBlobSizeBytes: int64(len(secureBootBody)), + PrimaryMediaType: "application/gzip", + }). + Return(imgsrv.Artifact{ + ID: "artifact-2", + Variant: "secureboot", + OperatingSystem: "incusos", + Architecture: "x86_64", + Format: imgsrv.ArtifactFormatRawGZ, + PrimaryBlobDigest: "sha256:def456", + PrimaryBlobSizeBytes: int64(len(secureBootBody)), + PrimaryMediaType: "application/gzip", + }, nil). + Once() + expectPublishJob(catalog, "incusos", "v1.0.0") + + publisher := newReleaseTestPublisher(t, catalog, uploads) + result, err := publisher.PublishRelease(context.Background(), publish.ReleaseRequest{ + ImageName: "incusos", + Version: "v1.0.0", + Artifacts: []publish.ReleaseArtifact{ + defaultArtifact, + secureBootArtifact, + }, + }) + + require.NoError(t, err) + assert.Equal(t, publish.ReleaseResult{ + Image: "incusos", + Version: "v1.0.0", + State: imgsrv.ImageVersionStatePublished, + Aliases: []string{}, + Artifacts: []publish.PublishedReleaseArtifact{ + { + ArtifactKey: "root", + Variant: "default", + LocalPath: defaultPath, + ServerArtifactID: "artifact-1", + OperatingSystem: "incusos", + Architecture: "x86_64", + Format: imgsrv.ArtifactFormatRawGZ, + Digest: "sha256:abc123", + Size: int64(len(defaultBody)), + MediaType: "application/gzip", + }, + { + ArtifactKey: "secureboot", + Variant: "secureboot", + LocalPath: secureBootPath, + ServerArtifactID: "artifact-2", + OperatingSystem: "incusos", + Architecture: "x86_64", + Format: imgsrv.ArtifactFormatRawGZ, + Digest: "sha256:def456", + Size: int64(len(secureBootBody)), + MediaType: "application/gzip", + }, + }, + }, result) +} + func TestPublisherFailsOnDraftVersionConflict(t *testing.T) { uploads := mocks.NewMockUploadsClient(t) catalog := mocks.NewMockCatalogClient(t) @@ -125,7 +255,7 @@ func TestPublisherFailsOnDraftVersionConflict(t *testing.T) { artifactPath := writePublishTestArtifact(t, "artifact.raw.gz", artifactBody) conflict := &imgsrv.ProblemError{HTTPStatus: http.StatusConflict, Title: "Conflict"} - expectReadyUpload(t, uploads, artifactPath, int64(len(artifactBody)), "abc123", "application/gzip") + expectReadyUpload(t, uploads, artifactPath, int64(len(artifactBody)), "abc123") catalog.EXPECT(). CreateImage(mock.Anything, imgsrv.CreateImageRequest{Name: "incusos"}). Return(imgsrv.Image{Name: "incusos"}, nil). @@ -205,7 +335,7 @@ func TestPublisherSurfacesPartialAliasFailure(t *testing.T) { artifactPath := writePublishTestArtifact(t, "artifact.raw.gz", artifactBody) aliasErr := errors.New("policy denied") - expectReadyUpload(t, uploads, artifactPath, int64(len(artifactBody)), "abc123", "application/gzip") + expectReadyUpload(t, uploads, artifactPath, int64(len(artifactBody)), "abc123") catalog.EXPECT(). CreateImage(mock.Anything, imgsrv.CreateImageRequest{Name: "incusos"}). Return(imgsrv.Image{Name: "incusos"}, nil). @@ -218,6 +348,7 @@ func TestPublisherSurfacesPartialAliasFailure(t *testing.T) { AddArtifact(mock.Anything, "incusos", "v1.0.0", mock.Anything). Return(imgsrv.Artifact{ ID: "artifact-1", + Variant: "default", OperatingSystem: "incusos", Architecture: "x86_64", Format: imgsrv.ArtifactFormatRawGZ, @@ -228,7 +359,21 @@ func TestPublisherSurfacesPartialAliasFailure(t *testing.T) { Once() catalog.EXPECT(). PublishVersion(mock.Anything, "incusos", "v1.0.0"). - Return(imgsrv.ImageVersion{Version: "v1.0.0", State: imgsrv.ImageVersionStatePublished}, nil). + Return(imgsrv.PublishJob{ + ID: "publish-job-1", + ImageName: "incusos", + Version: "v1.0.0", + State: imgsrv.PublishJobStateQueued, + }, nil). + Once() + catalog.EXPECT(). + GetPublishJob(mock.Anything, "publish-job-1"). + Return(imgsrv.PublishJob{ + ID: "publish-job-1", + ImageName: "incusos", + Version: "v1.0.0", + State: imgsrv.PublishJobStateSucceeded, + }, nil). Once() catalog.EXPECT(). PutAlias(mock.Anything, "incusos", "latest", imgsrv.PutAliasRequest{Version: "v1.0.0"}). @@ -263,11 +408,10 @@ func expectReadyUpload( path string, size int64, sha256 string, - mediaType string, ) { t.Helper() - mediaTypeHint := mediaType + mediaTypeHint := "application/gzip" filenameHint := filepath.Base(path) uploads.EXPECT(). BeginUpload(mock.Anything, imgsrv.BeginUploadRequest{ @@ -294,6 +438,27 @@ func releaseTestArtifact(path string, size int64) publish.ReleaseArtifact { } } +func expectPublishJob(catalog *mocks.MockCatalogClient, image string, version string) { + catalog.EXPECT(). + PublishVersion(mock.Anything, image, version). + Return(imgsrv.PublishJob{ + ID: "publish-job-1", + ImageName: image, + Version: version, + State: imgsrv.PublishJobStateQueued, + }, nil). + Once() + catalog.EXPECT(). + GetPublishJob(mock.Anything, "publish-job-1"). + Return(imgsrv.PublishJob{ + ID: "publish-job-1", + ImageName: image, + Version: version, + State: imgsrv.PublishJobStateSucceeded, + }, nil). + Once() +} + func newReleaseTestPublisher( t *testing.T, catalog publish.CatalogClient, @@ -307,7 +472,10 @@ func newReleaseTestPublisher( Timeout: time.Second, PollInterval: time.Nanosecond, }) - publisher, err := publish.NewPublisher(catalog, uploader) + publisher, err := publish.NewPublisher(catalog, uploader, publish.PublisherOptions{ + Timeout: time.Second, + PollInterval: time.Nanosecond, + }) require.NoError(t, err) return publisher } diff --git a/schemas/go.mod b/schemas/go.mod index e5a9b9e..696ef6d 100644 --- a/schemas/go.mod +++ b/schemas/go.mod @@ -17,6 +17,7 @@ require ( github.com/emicklei/proto v1.14.3 // indirect github.com/go-jose/go-jose/v4 v4.1.4 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/lib/pq v1.10.9 // indirect github.com/lxc/incus/v7 v7.0.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/muhlemmer/gu v0.3.1 // indirect @@ -34,6 +35,6 @@ require ( golang.org/x/sync v0.20.0 // indirect golang.org/x/text v0.36.0 // indirect google.golang.org/protobuf v1.36.11 // indirect - gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/schemas/go.sum b/schemas/go.sum index 3f75caa..39e0dbf 100644 --- a/schemas/go.sum +++ b/schemas/go.sum @@ -26,8 +26,7 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= -github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lxc/incus-os/incus-osd v0.0.0-20260505023852-d32ba1f13f6f h1:2wNTDCa/sf8xUOjfDHglPUpj5W4UncvuGKH3w6w/QSU= github.com/lxc/incus-os/incus-osd v0.0.0-20260505023852-d32ba1f13f6f/go.mod h1:0/gjLA2CMoYq0N93elIlQ6+tBMwtBtE8Rr2BWgQRB/c= github.com/lxc/incus/v7 v7.0.0 h1:xLz1Q1Xk+yCNL148MFBOSWWrzJVOS1N6PcS0zd8usSc= @@ -75,7 +74,6 @@ golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=