From 2ee9f6cf8d863e468c8366c9867346970d63c305 Mon Sep 17 00:00:00 2001 From: Roman Berezkin Date: Mon, 13 Jul 2026 04:06:12 +0300 Subject: [PATCH 1/4] [mirror] Fix --deckhouse-tag pull failing on unrelated suspended channels Signed-off-by: Roman Berezkin --- internal/mirror/platform/platform.go | 110 +++++++-- internal/mirror/platform/platform_test.go | 51 ++++ .../mirror/platform/pull_platform_test.go | 222 ++++++++++++++++-- internal/mirror/pull_test.go | 48 ++++ 4 files changed, 393 insertions(+), 38 deletions(-) diff --git a/internal/mirror/platform/platform.go b/internal/mirror/platform/platform.go index 96bdc1790..c53f0b8aa 100644 --- a/internal/mirror/platform/platform.go +++ b/internal/mirror/platform/platform.go @@ -27,6 +27,7 @@ import ( "maps" "path/filepath" "slices" + "strconv" "strings" "time" @@ -305,20 +306,26 @@ type parsedTags struct { // channelVersions represents the fetched versions from release channels type channelVersions map[string]*semver.Version +// suspendedChannels is the set of release channels marked "suspend": true in their version.json. +// Empty when --ignore-suspend is set. +type suspendedChannels map[string]struct{} + // versionsToMirror determines which Deckhouse release versions should be mirrored // It collects current versions from all release channels and filters available releases // to include only versions that should be mirrored based on the mirroring strategy func (svc *Service) versionsToMirror(ctx context.Context, tagsToMirror []string) (*VersionsToMirrorResult, error) { isTagsMirror := len(tagsToMirror) > 0 if isTagsMirror { - svc.userLogger.Infof("Skipped releases lookup as tag %q is specifically requested with --deckhouse-tag", svc.options.TargetTag) + // Release channels are still read below: they resolve channel-name tags and channel + // aliases. Only the rock-solid..alpha version range discovery is skipped. + svc.userLogger.Infof("Skipped releases range discovery as tag %q is specifically requested with --deckhouse-tag", svc.options.TargetTag) } // Parse input tags into categories parsed := svc.parseInputTags(tagsToMirror) // Fetch current versions from all release channels - channelVersions, err := svc.fetchReleaseChannelVersions(ctx) + channelVersions, suspended, err := svc.fetchReleaseChannelVersions(ctx) if err != nil && !errors.Is(err, ErrSomeChannelsFailed) { return nil, fmt.Errorf("failed to fetch release channel versions: %w", err) } @@ -330,6 +337,14 @@ func (svc *Service) versionsToMirror(ctx context.Context, tagsToMirror []string) // Match channels and versions based on requested tags versions, matchedChannels := svc.matchChannelsToTags(tagsToMirror, channelVersions, parsed.semverVersions) + // Refuse only when the request actually resolves to a suspended channel. In full discovery + // every channel is matched, so any suspension is fatal, as before. A specific --deckhouse-tag + // that matches no channel is unaffected by suspensions elsewhere in the registry. + // Runs before expandVersionRange, which relies on the alpha and rock-solid snapshots. + if err := checkSuspendedChannels(matchedChannels, suspended); err != nil { + return nil, err + } + // If specific tags requested, return immediately if isTagsMirror { return &VersionsToMirrorResult{ @@ -403,31 +418,41 @@ func (svc *Service) parseInputTags(tags []string) parsedTags { return result } -// fetchReleaseChannelVersions retrieves current versions from all release channels -func (svc *Service) fetchReleaseChannelVersions(ctx context.Context) (channelVersions, error) { +// fetchReleaseChannelVersions retrieves current versions from all release channels. +// Suspended channels are reported through suspendedChannels rather than failing the fetch: +// whether a suspension is fatal depends on the channels the request resolves to, which is +// only known after matchChannelsToTags. +func (svc *Service) fetchReleaseChannelVersions(ctx context.Context) (channelVersions, suspendedChannels, error) { defaultChannels := internal.GetAllDefaultReleaseChannels() allChannels := slices.Concat(defaultChannels, []string{internal.LTSChannel}) channelResults := make(map[string]releaseChannelVersionResult, len(allChannels)) + suspended := make(suspendedChannels, len(allChannels)) // - LTS exists: fetch all channels (default + LTS), missing default channels are OK // - LTS doesn't exist: all default channels are required - ltsVersion, ltsErr := svc.getReleaseChannelVersionFromRegistry(ctx, internal.LTSChannel) + ltsVersion, ltsSuspended, ltsErr := svc.getReleaseChannelVersionFromRegistry(ctx, internal.LTSChannel) if ltsErr != nil && !errors.Is(ltsErr, client.ErrImageNotFound) { - return nil, fmt.Errorf("get LTS release channel: %w", ltsErr) + return nil, nil, fmt.Errorf("get LTS release channel: %w", ltsErr) } + // hasLTS means "found", not "found and not suspended": a suspended LTS channel must not + // flip the CSE branch into requiring every default channel. hasLTS := ltsErr == nil if hasLTS { // Other channels will be appended below (if exists) channelResults[internal.LTSChannel] = releaseChannelVersionResult{ver: ltsVersion} + + if ltsSuspended { + suspended[internal.LTSChannel] = struct{}{} + } } for _, channel := range defaultChannels { - version, err := svc.getReleaseChannelVersionFromRegistry(ctx, channel) + version, channelSuspended, err := svc.getReleaseChannelVersionFromRegistry(ctx, channel) // Real error (network, auth) - fail immediately if err != nil && !errors.Is(err, client.ErrImageNotFound) { - return nil, fmt.Errorf("failed to get release channel version from registry for channel %s: %w", channel, err) + return nil, nil, fmt.Errorf("failed to get release channel version from registry for channel %s: %w", channel, err) } // Missing default channels are OK when LTS is present (e.g., CSE edition) @@ -435,11 +460,17 @@ func (svc *Service) fetchReleaseChannelVersions(ctx context.Context) (channelVer continue } + if channelSuspended { + suspended[channel] = struct{}{} + } + channelResults[channel] = releaseChannelVersionResult{ver: version, err: err} } // Validate and extract successful channel versions - return svc.validateChannelResults(channelResults) + versions, err := svc.validateChannelResults(channelResults) + + return versions, suspended, err } var ErrSomeChannelsFailed = errors.New("some channels failed to fetch") @@ -506,6 +537,37 @@ func (svc *Service) tagMatchesChannel(tag, channelName string, channelVersion *s return tag == channelName || tag == "v"+channelVersion.String() } +// checkSuspendedChannels refuses the pull when a channel the request resolves to is suspended. +// Suspension of channels the request does not resolve to is not an error. +func checkSuspendedChannels(matchedChannels []string, suspended suspendedChannels) error { + if len(suspended) == 0 { + return nil + } + + blocked := make([]string, 0, len(suspended)) + + for _, channel := range matchedChannels { + if _, ok := suspended[channel]; ok { + blocked = append(blocked, strconv.Quote(channel)) + } + } + + if len(blocked) == 0 { + return nil + } + + // matchedChannels comes from a map, so sort for a stable message. + slices.Sort(blocked) + + noun := "channel" + if len(blocked) > 1 { + noun = "channels" + } + + return fmt.Errorf("source registry contains suspended release %s %s, try again later (use --ignore-suspend to override)", + noun, strings.Join(blocked, ", ")) +} + // expandVersionRange expands the version range for full discovery mode func (svc *Service) expandVersionRange(ctx context.Context, channelVersions channelVersions, baseVersions []*semver.Version) ([]*semver.Version, error) { semverConstraint, hasSemverConstraint := svc.options.IncludeConstraint.(*modules.SemanticVersionConstraint) @@ -766,36 +828,40 @@ func mapKeysToSlice(m map[string]struct{}) []string { } // getReleaseChannelVersionFromRegistry retrieves the current version for a specific release channel -// It fetches the release image and metadata, validates the channel is not suspended, -// and stores the image in the layout for later use -func (svc *Service) getReleaseChannelVersionFromRegistry(ctx context.Context, releaseChannel string) (*semver.Version, error) { +// It fetches the release image and metadata, and reports whether the channel is suspended. +// +// Suspension is reported as data, not as an error: at this point we do not yet know whether the +// request resolves to this channel. versionsToMirror is the enforcement point. +// A suspended channel still yields its version (needed to match it against the requested tag), +// but never enters the download list. +func (svc *Service) getReleaseChannelVersionFromRegistry(ctx context.Context, releaseChannel string) (*semver.Version, bool, error) { image, err := svc.deckhouseService.ReleaseChannels().GetImage(ctx, releaseChannel) if err != nil { - return nil, fmt.Errorf("get %s release channel image: %w", releaseChannel, err) + return nil, false, fmt.Errorf("get %s release channel image: %w", releaseChannel, err) } meta, err := svc.deckhouseService.ReleaseChannels().GetMetadata(ctx, releaseChannel) if err != nil { - return nil, fmt.Errorf("cannot get %s release channel version.json: %w", releaseChannel, err) - } - - if meta.Suspend && !svc.options.IgnoreSuspend { - return nil, fmt.Errorf("source registry contains suspended release channel %q, try again later (use --ignore-suspend to override)", releaseChannel) + return nil, false, fmt.Errorf("cannot get %s release channel version.json: %w", releaseChannel, err) } ver, err := semver.NewVersion(meta.Version) if err != nil { - return nil, fmt.Errorf("release channel version is not semver %q: %w", meta.Version, err) + return nil, false, fmt.Errorf("release channel version is not semver %q: %w", meta.Version, err) + } + + if meta.Suspend && !svc.options.IgnoreSuspend { + return ver, true, nil } digest, err := image.Digest() if err != nil { - return nil, fmt.Errorf("cannot get %s release channel image digest: %w", releaseChannel, err) + return nil, false, fmt.Errorf("cannot get %s release channel image digest: %w", releaseChannel, err) } imageMeta, err := image.GetMetadata() if err != nil { - return nil, fmt.Errorf("cannot get %s release channel image tag reference: %w", releaseChannel, err) + return nil, false, fmt.Errorf("cannot get %s release channel image tag reference: %w", releaseChannel, err) } svc.userLogger.Debugf("image reference: %s@%s", imageMeta, digest.String()) @@ -804,7 +870,7 @@ func (svc *Service) getReleaseChannelVersionFromRegistry(ctx context.Context, re // Just record in downloadList for later pull svc.downloadList.DeckhouseReleaseChannel[imageMeta.GetTagReference()] = puller.NewImageMeta(meta.Version, imageMeta.GetTagReference(), &digest) - return ver, nil + return ver, false, nil } func (svc *Service) pullDeckhousePlatform(ctx context.Context, tagsToMirror []string) error { diff --git a/internal/mirror/platform/platform_test.go b/internal/mirror/platform/platform_test.go index 04bcb7589..7217ae76f 100644 --- a/internal/mirror/platform/platform_test.go +++ b/internal/mirror/platform/platform_test.go @@ -312,3 +312,54 @@ func TestService_versionsToMirror_Deduplication(t *testing.T) { // No custom tags assert.Empty(t, result.CustomTags) } + +func TestCheckSuspendedChannels(t *testing.T) { + tests := []struct { + name string + matchedChannels []string + suspended suspendedChannels + wantErr string + }{ + { + name: "no suspended channels", + matchedChannels: []string{"alpha", "stable"}, + suspended: suspendedChannels{}, + }, + { + name: "suspension outside of matched channels is ignored", + matchedChannels: []string{"stable"}, + suspended: suspendedChannels{"rock-solid": {}}, + }, + { + name: "no matched channels", + matchedChannels: nil, + suspended: suspendedChannels{"rock-solid": {}}, + }, + { + name: "single matched suspended channel keeps the legacy message", + matchedChannels: []string{"rock-solid"}, + suspended: suspendedChannels{"rock-solid": {}}, + wantErr: `source registry contains suspended release channel "rock-solid", try again later (use --ignore-suspend to override)`, + }, + { + name: "multiple matched suspended channels are sorted and pluralized", + matchedChannels: []string{"rock-solid", "stable", "alpha"}, + suspended: suspendedChannels{"rock-solid": {}, "alpha": {}}, + wantErr: `source registry contains suspended release channels "alpha", "rock-solid", try again later (use --ignore-suspend to override)`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := checkSuspendedChannels(tt.matchedChannels, tt.suspended) + + if tt.wantErr == "" { + require.NoError(t, err) + return + } + + require.Error(t, err) + assert.Equal(t, tt.wantErr, err.Error()) + }) + } +} diff --git a/internal/mirror/platform/pull_platform_test.go b/internal/mirror/platform/pull_platform_test.go index b55b23515..0e4d6be15 100644 --- a/internal/mirror/platform/pull_platform_test.go +++ b/internal/mirror/platform/pull_platform_test.go @@ -55,25 +55,27 @@ userLogger *log.SLogger, } } -// suspendedStub returns a stub where the alpha release channel is suspended -// (version.json contains "suspend": true). -func suspendedStub() localreg.Client { - // reuse the full stub but replace the alpha release-channel image +// suspendedStubWith returns a stub with the five standard release channels +// where every channel listed in suspendedChannels carries "suspend": true in +// its version.json. extraTags are published to the root, install and +// install-standalone repositories only, so they match no channel version. +func suspendedStubWith(suspendedChannels []string, extraTags ...string) localreg.Client { reg := upfake.NewRegistry(stubRootURL) - // Standard channels — alpha is suspended. - channels := map[string]struct { - version string - suspend bool - }{ - "alpha": {version: "v1.72.10", suspend: true}, - "beta": {version: "v1.71.0"}, - "early-access": {version: "v1.70.0"}, - "stable": {version: "v1.69.0"}, - "rock-solid": {version: "v1.68.0"}, + suspended := make(map[string]bool, len(suspendedChannels)) + for _, ch := range suspendedChannels { + suspended[ch] = true } - for ch, data := range channels { - content := fmt.Sprintf(`{"version":%q,"suspend":%v}`, data.version, data.suspend) + + channels := map[string]string{ + "alpha": "v1.72.10", + "beta": "v1.71.0", + "early-access": "v1.70.0", + "stable": "v1.69.0", + "rock-solid": "v1.68.0", + } + for ch, version := range channels { + content := fmt.Sprintf(`{"version":%q,"suspend":%v}`, version, suspended[ch]) img := upfake.NewImageBuilder().WithFile("version.json", content).MustBuild() reg.MustAddImage("release-channel", ch, img) } @@ -81,6 +83,7 @@ func suspendedStub() localreg.Client { // Root + installer repos (same as default stub). versionTags := []string{"alpha", "beta", "early-access", "stable", "rock-solid", "v1.72.10", "v1.71.0", "v1.70.0", "v1.69.0", "v1.68.0", "pr12345"} + versionTags = append(versionTags, extraTags...) for _, tag := range versionTags { img := upfake.NewImageBuilder(). WithFile("version.json", `{"version":"v1.72.10"}`). @@ -93,6 +96,37 @@ func suspendedStub() localreg.Client { return pkgclient.Adapt(upfake.NewClient(reg)) } +// suspendedStub returns a stub where the alpha release channel is suspended +// (version.json contains "suspend": true). +func suspendedStub() localreg.Client { + return suspendedStubWith([]string{"alpha"}) +} + +// suspendedLTSOnlyStub builds a CSE-like registry whose only release channel +// is a suspended LTS. extraTags are published to the root, install and +// install-standalone repositories alongside the LTS version tag. +func suspendedLTSOnlyStub(ltsVersion string, extraTags ...string) localreg.Client { + reg := upfake.NewRegistry(stubRootURL) + + channelImg := upfake.NewImageBuilder(). + WithFile("version.json", fmt.Sprintf(`{"version":%q,"suspend":true}`, ltsVersion)). + MustBuild() + reg.MustAddImage("release-channel", "lts", channelImg) + + tags := append([]string{ltsVersion}, extraTags...) + for _, tag := range tags { + img := upfake.NewImageBuilder(). + WithFile("version.json", fmt.Sprintf(`{"version":%q}`, ltsVersion)). + WithFile("deckhouse/candi/images_digests.json", `{}`). + MustBuild() + reg.MustAddImage("", tag, img) + reg.MustAddImage("install", tag, img) + reg.MustAddImage("install-standalone", tag, img) + } + + return pkgclient.Adapt(upfake.NewClient(reg)) +} + // ---- validatePlatformAccess behaviour ---- func TestPullPlatform_ErrorWhenRegistryEmpty(t *testing.T) { @@ -155,6 +189,162 @@ func TestPullPlatform_IgnoreSuspend_SucceedsWithSuspendedChannel(t *testing.T) { assert.GreaterOrEqual(t, len(svc.downloadList.Deckhouse), 5) } +// ---- TargetTag vs suspended channels ---- + +// TestPullPlatform_DryRun_SemverTag_UnrelatedChannelSuspended_Succeeds is the +// regression for `d8 mirror pull --deckhouse-tag vX.Y.Z` aborting with +// +// failed to get release channel version from registry for channel rock-solid: +// source registry contains suspended release channel "rock-solid", try again later +// +// when the requested version does not belong to the suspended channel at all. +// A suspension may only block pulls that resolve to the suspended channel; +// pinning an unrelated tag must succeed and must not enqueue the suspended +// channel alias. +func TestPullPlatform_DryRun_SemverTag_UnrelatedChannelSuspended_Succeeds(t *testing.T) { + logger := dkplog.NewLogger(dkplog.WithLevel(slog.LevelWarn)) + userLogger := log.NewSLogger(slog.LevelWarn) + + // v1.71.5 exists in the registry but is not the current version of any channel. + svc := newDryRunService( + suspendedStubWith([]string{"rock-solid"}, "v1.71.5"), + &Options{TargetTag: "v1.71.5"}, + logger, + userLogger, + ) + err := svc.PullPlatform(context.Background()) + require.NoError(t, err, + "a suspended channel unrelated to the requested tag must not abort the pull") + + rootURL := stubRootURL + assert.Contains(t, svc.downloadList.Deckhouse, rootURL+":v1.71.5") + assert.Len(t, svc.downloadList.Deckhouse, 1, + "only the requested tag must be enqueued for download") + assert.NotContains(t, svc.downloadList.DeckhouseReleaseChannel, + rootURL+"/release-channel:rock-solid", + "suspended channel alias must not be enqueued") +} + +// TestPullPlatform_DryRun_SemverTag_MatchingSuspendedChannel_Fails pins the +// conservative half of the contract: a tag equal to a suspended channel's +// current version resolves to that channel (its alias would be bundled), so +// the pull is refused unless --ignore-suspend is given. +func TestPullPlatform_DryRun_SemverTag_MatchingSuspendedChannel_Fails(t *testing.T) { + logger := dkplog.NewLogger(dkplog.WithLevel(slog.LevelWarn)) + userLogger := log.NewSLogger(slog.LevelWarn) + + // v1.72.10 is the current version of the suspended alpha channel. + svc := newDryRunService(suspendedStub(), &Options{TargetTag: "v1.72.10"}, logger, userLogger) + err := svc.PullPlatform(context.Background()) + + require.Error(t, err) + assert.Contains(t, err.Error(), "suspended") + assert.Contains(t, err.Error(), `"alpha"`) +} + +func TestPullPlatform_DryRun_ChannelTag_SuspendedChannel_Fails(t *testing.T) { + logger := dkplog.NewLogger(dkplog.WithLevel(slog.LevelWarn)) + userLogger := log.NewSLogger(slog.LevelWarn) + + svc := newDryRunService(suspendedStub(), &Options{TargetTag: "alpha"}, logger, userLogger) + err := svc.PullPlatform(context.Background()) + + require.Error(t, err, "requesting a suspended channel by name must be refused") + assert.Contains(t, err.Error(), "suspended") + assert.Contains(t, err.Error(), `"alpha"`) +} + +func TestPullPlatform_DryRun_ChannelTag_OtherChannelSuspended_Succeeds(t *testing.T) { + logger := dkplog.NewLogger(dkplog.WithLevel(slog.LevelWarn)) + userLogger := log.NewSLogger(slog.LevelWarn) + + // stable is healthy; only alpha is suspended. + svc := newDryRunService(suspendedStub(), &Options{TargetTag: "stable"}, logger, userLogger) + err := svc.PullPlatform(context.Background()) + require.NoError(t, err, + "a suspended channel must not block pulling another, healthy channel") + + rootURL := stubRootURL + assert.Contains(t, svc.downloadList.Deckhouse, rootURL+":v1.69.0") + assert.Contains(t, svc.downloadList.DeckhouseReleaseChannel, + rootURL+"/release-channel:stable") + assert.NotContains(t, svc.downloadList.DeckhouseReleaseChannel, + rootURL+"/release-channel:alpha", + "suspended channel alias must not be enqueued") +} + +func TestPullPlatform_DryRun_CustomTag_ChannelSuspended_Succeeds(t *testing.T) { + logger := dkplog.NewLogger(dkplog.WithLevel(slog.LevelWarn)) + userLogger := log.NewSLogger(slog.LevelWarn) + + svc := newDryRunService(suspendedStub(), &Options{TargetTag: "pr12345"}, logger, userLogger) + err := svc.PullPlatform(context.Background()) + require.NoError(t, err, + "custom tags match no channel and must not be affected by suspensions") + + assert.Contains(t, svc.downloadList.Deckhouse, stubRootURL+":pr12345") + assert.Len(t, svc.downloadList.Deckhouse, 1) +} + +func TestPullPlatform_DryRun_IgnoreSuspend_TagMatchingSuspendedChannel_Succeeds(t *testing.T) { + logger := dkplog.NewLogger(dkplog.WithLevel(slog.LevelWarn)) + userLogger := log.NewSLogger(slog.LevelWarn) + + svc := newDryRunService( + suspendedStub(), + &Options{TargetTag: "v1.72.10", IgnoreSuspend: true}, + logger, + userLogger, + ) + err := svc.PullPlatform(context.Background()) + require.NoError(t, err, + "--ignore-suspend must override the suspension on the matched channel") + + rootURL := stubRootURL + assert.Contains(t, svc.downloadList.Deckhouse, rootURL+":v1.72.10") + assert.Contains(t, svc.downloadList.DeckhouseReleaseChannel, + rootURL+"/release-channel:alpha", + "with --ignore-suspend the matched channel alias is enqueued as usual") +} + +// TestPullPlatform_DryRun_FullDiscovery_SuspendedLTS_Fails pins two contracts +// at once: a suspended LTS still counts as present, so missing default +// channels stay non-fatal (CSE registries), and full discovery resolves to +// every fetched channel, so the suspension itself is what aborts the pull. +func TestPullPlatform_DryRun_FullDiscovery_SuspendedLTS_Fails(t *testing.T) { + logger := dkplog.NewLogger(dkplog.WithLevel(slog.LevelWarn)) + userLogger := log.NewSLogger(slog.LevelWarn) + + svc := newDryRunService(suspendedLTSOnlyStub("v1.68.0"), nil, logger, userLogger) + err := svc.PullPlatform(context.Background()) + + require.Error(t, err) + assert.Contains(t, err.Error(), `suspended release channel "lts"`, + "the failure must come from the suspension, not from the missing default channels") +} + +// TestPullPlatform_DryRun_SuspendedLTS_UnrelatedTag_Succeeds covers the CSE +// variant of the unrelated-suspension scenario: the only channel (LTS) is +// suspended, the requested tag does not match it, so the pull must succeed. +func TestPullPlatform_DryRun_SuspendedLTS_UnrelatedTag_Succeeds(t *testing.T) { + logger := dkplog.NewLogger(dkplog.WithLevel(slog.LevelWarn)) + userLogger := log.NewSLogger(slog.LevelWarn) + + svc := newDryRunService( + suspendedLTSOnlyStub("v1.68.0", "v1.71.5"), + &Options{TargetTag: "v1.71.5"}, + logger, + userLogger, + ) + err := svc.PullPlatform(context.Background()) + require.NoError(t, err) + + assert.Contains(t, svc.downloadList.Deckhouse, stubRootURL+":v1.71.5") + assert.NotContains(t, svc.downloadList.DeckhouseReleaseChannel, + stubRootURL+"/release-channel:lts", + "suspended LTS alias must not be enqueued") +} + // ---- TargetTag: specific semver ---- func TestPullPlatform_DryRun_SemverTag_PopulatesDownloadList(t *testing.T) { diff --git a/internal/mirror/pull_test.go b/internal/mirror/pull_test.go index 370d8f075..ac076e4db 100644 --- a/internal/mirror/pull_test.go +++ b/internal/mirror/pull_test.go @@ -190,6 +190,54 @@ func TestPull_SuspendedChannel_ReturnsError(t *testing.T) { assert.Contains(t, err.Error(), "suspended") } +// TestPull_TargetTag_UnrelatedSuspendedChannel_Succeeds is the end-to-end +// regression for `d8 mirror pull --deckhouse-tag vX.Y.Z` aborting when an +// unrelated release channel is suspended. A suspension may only block pulls +// that resolve to the suspended channel. +func TestPull_TargetTag_UnrelatedSuspendedChannel_Succeeds(t *testing.T) { + reg := upfake.NewRegistry(pullStubRootURL) + channelVersions := map[string]struct { + ver string + suspend bool + }{ + "alpha": {"v1.72.10", false}, + "beta": {"v1.71.0", false}, + "early-access": {"v1.70.0", false}, + "stable": {"v1.69.0", false}, + "rock-solid": {"v1.68.0", true}, + } + for ch, data := range channelVersions { + content := `{"version":"` + data.ver + `"}` + if data.suspend { + content = `{"version":"` + data.ver + `","suspend":true}` + } + img := upfake.NewImageBuilder().WithFile("version.json", content).MustBuild() + reg.MustAddImage("release-channel", ch, img) + reg.MustAddImage("release-channel", data.ver, img) + } + img := upfake.NewImageBuilder(). + WithFile("version.json", `{"version":"v1.71.5"}`). + WithFile("deckhouse/candi/images_digests.json", `{}`). + MustBuild() + for _, tag := range []string{"v1.72.10", "v1.71.5", "v1.71.0", "v1.70.0", "v1.69.0", "v1.68.0"} { + reg.MustAddImage("", tag, img) + reg.MustAddImage("install", tag, img) + reg.MustAddImage("install-standalone", tag, img) + } + + // v1.71.5 is not the current version of any channel; rock-solid is suspended. + svc := newPullService(t, pkgclient.Adapt(upfake.NewClient(reg)), "v1.71.5", &PullServiceOptions{ + SkipSecurity: true, + SkipModules: true, + SkipInstaller: true, + IgnoreSuspend: false, + }) + + _, err := svc.Pull(context.Background()) + require.NoError(t, err, + "suspended rock-solid must not block --deckhouse-tag pull of an unrelated version") +} + // --------------------------------------------------------------------------- // Success path tests // --------------------------------------------------------------------------- From 49faea9837af1f1d354d859f0388e55c4f66a16c Mon Sep 17 00:00:00 2001 From: Roman Berezkin Date: Tue, 14 Jul 2026 15:44:27 +0300 Subject: [PATCH 2/4] Remove ambigious bool return type Signed-off-by: Roman Berezkin --- internal/mirror/platform/platform.go | 38 ++++++++++++++++------------ 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/internal/mirror/platform/platform.go b/internal/mirror/platform/platform.go index c53f0b8aa..749bd1251 100644 --- a/internal/mirror/platform/platform.go +++ b/internal/mirror/platform/platform.go @@ -430,7 +430,7 @@ func (svc *Service) fetchReleaseChannelVersions(ctx context.Context) (channelVer // - LTS exists: fetch all channels (default + LTS), missing default channels are OK // - LTS doesn't exist: all default channels are required - ltsVersion, ltsSuspended, ltsErr := svc.getReleaseChannelVersionFromRegistry(ctx, internal.LTSChannel) + ltsInfo, ltsErr := svc.getReleaseChannelVersionFromRegistry(ctx, internal.LTSChannel) if ltsErr != nil && !errors.Is(ltsErr, client.ErrImageNotFound) { return nil, nil, fmt.Errorf("get LTS release channel: %w", ltsErr) } @@ -441,15 +441,15 @@ func (svc *Service) fetchReleaseChannelVersions(ctx context.Context) (channelVer if hasLTS { // Other channels will be appended below (if exists) - channelResults[internal.LTSChannel] = releaseChannelVersionResult{ver: ltsVersion} + channelResults[internal.LTSChannel] = releaseChannelVersionResult{ver: ltsInfo.version} - if ltsSuspended { + if ltsInfo.suspended { suspended[internal.LTSChannel] = struct{}{} } } for _, channel := range defaultChannels { - version, channelSuspended, err := svc.getReleaseChannelVersionFromRegistry(ctx, channel) + info, err := svc.getReleaseChannelVersionFromRegistry(ctx, channel) // Real error (network, auth) - fail immediately if err != nil && !errors.Is(err, client.ErrImageNotFound) { return nil, nil, fmt.Errorf("failed to get release channel version from registry for channel %s: %w", channel, err) @@ -460,11 +460,11 @@ func (svc *Service) fetchReleaseChannelVersions(ctx context.Context) (channelVer continue } - if channelSuspended { + if info.suspended { suspended[channel] = struct{}{} } - channelResults[channel] = releaseChannelVersionResult{ver: version, err: err} + channelResults[channel] = releaseChannelVersionResult{ver: info.version, err: err} } // Validate and extract successful channel versions @@ -827,41 +827,47 @@ func mapKeysToSlice(m map[string]struct{}) []string { return keys } -// getReleaseChannelVersionFromRegistry retrieves the current version for a specific release channel -// It fetches the release image and metadata, and reports whether the channel is suspended. +// releaseChannelInfo is the current state a release channel publishes in its version.json: +// the version it points to and whether it is suspended. +type releaseChannelInfo struct { + version *semver.Version + suspended bool +} + +// getReleaseChannelVersionFromRegistry retrieves the current state of a specific release channel. // // Suspension is reported as data, not as an error: at this point we do not yet know whether the // request resolves to this channel. versionsToMirror is the enforcement point. // A suspended channel still yields its version (needed to match it against the requested tag), // but never enters the download list. -func (svc *Service) getReleaseChannelVersionFromRegistry(ctx context.Context, releaseChannel string) (*semver.Version, bool, error) { +func (svc *Service) getReleaseChannelVersionFromRegistry(ctx context.Context, releaseChannel string) (releaseChannelInfo, error) { image, err := svc.deckhouseService.ReleaseChannels().GetImage(ctx, releaseChannel) if err != nil { - return nil, false, fmt.Errorf("get %s release channel image: %w", releaseChannel, err) + return releaseChannelInfo{}, fmt.Errorf("get %s release channel image: %w", releaseChannel, err) } meta, err := svc.deckhouseService.ReleaseChannels().GetMetadata(ctx, releaseChannel) if err != nil { - return nil, false, fmt.Errorf("cannot get %s release channel version.json: %w", releaseChannel, err) + return releaseChannelInfo{}, fmt.Errorf("cannot get %s release channel version.json: %w", releaseChannel, err) } ver, err := semver.NewVersion(meta.Version) if err != nil { - return nil, false, fmt.Errorf("release channel version is not semver %q: %w", meta.Version, err) + return releaseChannelInfo{}, fmt.Errorf("release channel version is not semver %q: %w", meta.Version, err) } if meta.Suspend && !svc.options.IgnoreSuspend { - return ver, true, nil + return releaseChannelInfo{version: ver, suspended: true}, nil } digest, err := image.Digest() if err != nil { - return nil, false, fmt.Errorf("cannot get %s release channel image digest: %w", releaseChannel, err) + return releaseChannelInfo{}, fmt.Errorf("cannot get %s release channel image digest: %w", releaseChannel, err) } imageMeta, err := image.GetMetadata() if err != nil { - return nil, false, fmt.Errorf("cannot get %s release channel image tag reference: %w", releaseChannel, err) + return releaseChannelInfo{}, fmt.Errorf("cannot get %s release channel image tag reference: %w", releaseChannel, err) } svc.userLogger.Debugf("image reference: %s@%s", imageMeta, digest.String()) @@ -870,7 +876,7 @@ func (svc *Service) getReleaseChannelVersionFromRegistry(ctx context.Context, re // Just record in downloadList for later pull svc.downloadList.DeckhouseReleaseChannel[imageMeta.GetTagReference()] = puller.NewImageMeta(meta.Version, imageMeta.GetTagReference(), &digest) - return ver, false, nil + return releaseChannelInfo{version: ver}, nil } func (svc *Service) pullDeckhousePlatform(ctx context.Context, tagsToMirror []string) error { From 8e89b828c37a203448b813cf5ec0e27f7e0382cb Mon Sep 17 00:00:00 2001 From: Roman Berezkin Date: Tue, 14 Jul 2026 15:51:18 +0300 Subject: [PATCH 3/4] Rename release channel fetchers to match what they return - `fetchReleaseChannels` returns the versions map plus the suspended set, so the name no longer promises only versions. - `getReleaseChannelInfoFromRegistry` pairs with its `releaseChannelInfo` result. Signed-off-by: Roman Berezkin --- internal/mirror/platform/platform.go | 17 +++++++++-------- internal/mirror/platform/pull_platform_test.go | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/internal/mirror/platform/platform.go b/internal/mirror/platform/platform.go index 749bd1251..e73e65d78 100644 --- a/internal/mirror/platform/platform.go +++ b/internal/mirror/platform/platform.go @@ -325,7 +325,7 @@ func (svc *Service) versionsToMirror(ctx context.Context, tagsToMirror []string) parsed := svc.parseInputTags(tagsToMirror) // Fetch current versions from all release channels - channelVersions, suspended, err := svc.fetchReleaseChannelVersions(ctx) + channelVersions, suspended, err := svc.fetchReleaseChannels(ctx) if err != nil && !errors.Is(err, ErrSomeChannelsFailed) { return nil, fmt.Errorf("failed to fetch release channel versions: %w", err) } @@ -418,11 +418,12 @@ func (svc *Service) parseInputTags(tags []string) parsedTags { return result } -// fetchReleaseChannelVersions retrieves current versions from all release channels. +// fetchReleaseChannels retrieves the current state of every release channel: +// the version each one points to and which of them are suspended. // Suspended channels are reported through suspendedChannels rather than failing the fetch: // whether a suspension is fatal depends on the channels the request resolves to, which is // only known after matchChannelsToTags. -func (svc *Service) fetchReleaseChannelVersions(ctx context.Context) (channelVersions, suspendedChannels, error) { +func (svc *Service) fetchReleaseChannels(ctx context.Context) (channelVersions, suspendedChannels, error) { defaultChannels := internal.GetAllDefaultReleaseChannels() allChannels := slices.Concat(defaultChannels, []string{internal.LTSChannel}) channelResults := make(map[string]releaseChannelVersionResult, len(allChannels)) @@ -430,7 +431,7 @@ func (svc *Service) fetchReleaseChannelVersions(ctx context.Context) (channelVer // - LTS exists: fetch all channels (default + LTS), missing default channels are OK // - LTS doesn't exist: all default channels are required - ltsInfo, ltsErr := svc.getReleaseChannelVersionFromRegistry(ctx, internal.LTSChannel) + ltsInfo, ltsErr := svc.getReleaseChannelInfoFromRegistry(ctx, internal.LTSChannel) if ltsErr != nil && !errors.Is(ltsErr, client.ErrImageNotFound) { return nil, nil, fmt.Errorf("get LTS release channel: %w", ltsErr) } @@ -449,7 +450,7 @@ func (svc *Service) fetchReleaseChannelVersions(ctx context.Context) (channelVer } for _, channel := range defaultChannels { - info, err := svc.getReleaseChannelVersionFromRegistry(ctx, channel) + info, err := svc.getReleaseChannelInfoFromRegistry(ctx, channel) // Real error (network, auth) - fail immediately if err != nil && !errors.Is(err, client.ErrImageNotFound) { return nil, nil, fmt.Errorf("failed to get release channel version from registry for channel %s: %w", channel, err) @@ -736,7 +737,7 @@ func filterVersionsByConstraint(versions []*semver.Version, constraint *modules. // filterChannelsByConstraint drops release channels whose current snapshot // version falls outside the user-supplied constraint. Channels that resolve -// to a missing version (defensive — fetchReleaseChannelVersions normally +// to a missing version (defensive — fetchReleaseChannels normally // guarantees this) are kept so we don't silently delete metadata for sources // we cannot evaluate against the constraint. func filterChannelsByConstraint(channels []string, channelVersions channelVersions, constraint *modules.SemanticVersionConstraint) []string { @@ -834,13 +835,13 @@ type releaseChannelInfo struct { suspended bool } -// getReleaseChannelVersionFromRegistry retrieves the current state of a specific release channel. +// getReleaseChannelInfoFromRegistry retrieves the current state of a specific release channel. // // Suspension is reported as data, not as an error: at this point we do not yet know whether the // request resolves to this channel. versionsToMirror is the enforcement point. // A suspended channel still yields its version (needed to match it against the requested tag), // but never enters the download list. -func (svc *Service) getReleaseChannelVersionFromRegistry(ctx context.Context, releaseChannel string) (releaseChannelInfo, error) { +func (svc *Service) getReleaseChannelInfoFromRegistry(ctx context.Context, releaseChannel string) (releaseChannelInfo, error) { image, err := svc.deckhouseService.ReleaseChannels().GetImage(ctx, releaseChannel) if err != nil { return releaseChannelInfo{}, fmt.Errorf("get %s release channel image: %w", releaseChannel, err) diff --git a/internal/mirror/platform/pull_platform_test.go b/internal/mirror/platform/pull_platform_test.go index 0e4d6be15..e210867bb 100644 --- a/internal/mirror/platform/pull_platform_test.go +++ b/internal/mirror/platform/pull_platform_test.go @@ -939,7 +939,7 @@ func ltsOnlySourceStub(ver string) localreg.Client { // // The root cause was platform.NewService seeding the downloadList rootURL // from registryService.GetRoot() (non-edition root) while -// getReleaseChannelVersionFromRegistry fed the same map with edition-scoped +// getReleaseChannelInfo fed the same map with edition-scoped // keys obtained through deckhouseService. The fix is to seed the rootURL // with registryService.GetEditionRoot() so every key in the downloadList // carries the edition segment. From c762d141ab0ec343cc3bb55e92ced7c53ddbda31 Mon Sep 17 00:00:00 2001 From: Roman Berezkin Date: Wed, 15 Jul 2026 16:11:56 +0300 Subject: [PATCH 4/4] Bump dependencies Signed-off-by: Roman Berezkin --- go.mod | 21 ++++++-------- go.sum | 87 ++++++++++++++-------------------------------------------- 2 files changed, 30 insertions(+), 78 deletions(-) diff --git a/go.mod b/go.mod index f4a73606c..959e7f3a7 100644 --- a/go.mod +++ b/go.mod @@ -41,10 +41,10 @@ require ( github.com/werf/werf/v2 v2.69.0 gitlab.com/greyxor/slogor v1.2.11 go.cypherpunks.ru/gogost/v5 v5.13.0 - golang.org/x/crypto v0.49.0 + golang.org/x/crypto v0.54.0 golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 - golang.org/x/image v0.39.0 - golang.org/x/term v0.41.0 + golang.org/x/image v0.44.0 + golang.org/x/term v0.45.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.34.8 k8s.io/apiextensions-apiserver v0.34.8 @@ -402,7 +402,6 @@ require ( github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 // indirect github.com/jmoiron/sqlx v1.3.5 // indirect github.com/jonboulle/clockwork v0.5.0 // indirect - github.com/josharian/intern v1.0.0 // indirect github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kelseyhightower/envconfig v1.4.0 // indirect @@ -422,7 +421,6 @@ require ( github.com/looplab/fsm v1.0.2 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect - github.com/mailru/easyjson v0.9.0 // indirect github.com/manifoldco/promptui v0.9.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -617,15 +615,14 @@ require ( go.uber.org/zap v1.27.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/mod v0.34.0 // indirect - golang.org/x/net v0.52.0 // indirect + golang.org/x/mod v0.37.0 // indirect + golang.org/x/net v0.57.0 // indirect golang.org/x/oauth2 v0.35.0 // indirect - golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.42.0 // indirect - golang.org/x/text v0.36.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/text v0.40.0 // indirect golang.org/x/time v0.12.0 // indirect - golang.org/x/tools v0.43.0 // indirect - gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + golang.org/x/tools v0.47.0 // indirect google.golang.org/api v0.230.0 // indirect google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect diff --git a/go.sum b/go.sum index 7560dc248..df49d9c49 100644 --- a/go.sum +++ b/go.sum @@ -299,7 +299,6 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -500,10 +499,7 @@ github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.15.0+incompatible h1:8KpYO/Xl/ZudZs5RNOEhWMBY4hmzlZhhRd9cu+jrZP4= github.com/emicklei/go-restful v2.15.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= -github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= @@ -552,8 +548,6 @@ github.com/fsouza/go-dockerclient v1.11.1 h1:i5Vk9riDxW2uP9pVS5FYkpquMTFT5lsx2pt github.com/fsouza/go-dockerclient v1.11.1/go.mod h1:UfjOOaspAq+RGh7GX1aZ0HeWWGHQWWsh+H5BgEWB3Pk= github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw= github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= -github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= -github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/fxamacker/cbor/v2 v2.9.2 h1:X4Ksno9+x3cz0TZv69ec1hxP/+tymuR8PXQJyDwfh78= github.com/fxamacker/cbor/v2 v2.9.2/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/gammazero/deque v0.2.1 h1:qSdsbG6pgp6nL7A0+K/B7s12mcCY/5l5SIUpMOl+dC0= @@ -630,15 +624,11 @@ github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+ github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA= -github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0= github.com/go-openapi/jsonpointer v0.23.1 h1:1HBACs7XIwR2RcmItfdSFlALhGbe6S92p0ry4d1GWg4= github.com/go-openapi/jsonpointer v0.23.1/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= -github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE= -github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw= github.com/go-openapi/jsonreference v0.21.6 h1:NZ5nGfnaM1n4I43Xjm1e5/M2GjOwQwndQz22uhxwD+Y= github.com/go-openapi/jsonreference v0.21.6/go.mod h1:xzbgtQ3ZbWxvET3AxdzCJlJt6vkovbf+IfSPJjD0tUY= github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco= @@ -652,29 +642,20 @@ github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-openapi/swag v0.25.5 h1:pNkwbUEeGwMtcgxDr+2GBPAk4kT+kJ+AaB+TMKAg+TU= github.com/go-openapi/swag v0.25.5/go.mod h1:B3RT6l8q7X803JRxa2e59tHOiZlX1t8viplOcs9CwTA= github.com/go-openapi/swag/cmdutils v0.25.5 h1:yh5hHrpgsw4NwM9KAEtaDTXILYzdXh/I8Whhx9hKj7c= github.com/go-openapi/swag/cmdutils v0.25.5/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= -github.com/go-openapi/swag/conv v0.26.0 h1:5yGGsPYI1ZCva93U0AoKi/iZrNhaJEjr324YVsiD89I= -github.com/go-openapi/swag/conv v0.26.0/go.mod h1:tpAmIL7X58VPnHHiSO4uE3jBeRamGsFsfdDeDtb5ECE= github.com/go-openapi/swag/conv v0.26.1 h1:slr5FVkg9Wc3Y5zcwenD8Sd/PQ94b2I/QJI7N7KTBpg= github.com/go-openapi/swag/conv v0.26.1/go.mod h1:mvQXgPptZk9GTrFgGwWvT4q+dN+zQej9JfmGwnipz1A= github.com/go-openapi/swag/fileutils v0.25.5 h1:B6JTdOcs2c0dBIs9HnkyTW+5gC+8NIhVBUwERkFhMWk= github.com/go-openapi/swag/fileutils v0.25.5/go.mod h1:V3cT9UdMQIaH4WiTrUc9EPtVA4txS0TOmRURmhGF4kc= -github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo= -github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU= github.com/go-openapi/swag/jsonname v0.26.1 h1:VReupaV6WxlAsCn0e4DUfgV6bPmINnPpyJDLqSfNPcE= github.com/go-openapi/swag/jsonname v0.26.1/go.mod h1:OvdW6BoWoj33pTfi7x9vFrgmT+fk7aw0BRwvCE0YOuc= -github.com/go-openapi/swag/jsonutils v0.26.0 h1:FawFML2iAXsPqmERscuMPIHmFsoP1tOqWkxBaKNMsnA= -github.com/go-openapi/swag/jsonutils v0.26.0/go.mod h1:2VmA0CJlyFqgawOaPI9psnjFDqzyivIqLYN34t9p91E= github.com/go-openapi/swag/jsonutils v0.26.1 h1:2hdBfFkHg+7Wrz2VsCbeyR6hzkRDs7AztnMR2u84yOY= github.com/go-openapi/swag/jsonutils v0.26.1/go.mod h1:U+RMJH3wa+6BRiphuRtIyI8fW9HPFqFQ4sHk2oRx0UQ= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0 h1:apqeINu/ICHouqiRZbyFvuDge5jCmmLTqGQ9V95EaOM= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0/go.mod h1:AyM6QT8uz5IdKxk5akv0y6u4QvcL9GWERt0Jx/F/R8Y= github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.1 h1:1CD7NiLLb/TXl3tOnFYU4b+mNfb5rtgHkaA+q7RMYYQ= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.1/go.mod h1:ZWafc8nMdYzTE3uYY6W86f0n46+IF0g4uUyRhJw/kXc= github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU= github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g= github.com/go-openapi/swag/mangling v0.25.5 h1:hyrnvbQRS7vKePQPHHDso+k6CGn5ZBs5232UqWZmJZw= @@ -683,20 +664,14 @@ github.com/go-openapi/swag/netutils v0.25.5 h1:LZq2Xc2QI8+7838elRAaPCeqJnHODfSyO github.com/go-openapi/swag/netutils v0.25.5/go.mod h1:lHbtmj4m57APG/8H7ZcMMSWzNqIQcu0RFiXrPUara14= github.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M= github.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII= -github.com/go-openapi/swag/typeutils v0.26.0 h1:2kdEwdiNWy+JJdOvu5MA2IIg2SylWAFuuyQIKYybfq4= -github.com/go-openapi/swag/typeutils v0.26.0/go.mod h1:oovDuIUvTrEHVMqWilQzKzV4YlSKgyZmFh7AlfABNVE= github.com/go-openapi/swag/typeutils v0.26.1 h1:yg42FgMzRR6PVQ3M3qHz1s+Y6/P4HoJ3cBarXa3OVnU= github.com/go-openapi/swag/typeutils v0.26.1/go.mod h1:VfnV+oUtSP2vCSCn2aJgnr8OevUYemyIzzS1VOzS10o= -github.com/go-openapi/swag/yamlutils v0.26.0 h1:H7O8l/8NJJQ/oiReEN+oMpnGMyt8G0hl460nRZxhLMQ= -github.com/go-openapi/swag/yamlutils v0.26.0/go.mod h1:1evKEGAtP37Pkwcc7EWMF0hedX0/x3Rkvei2wtG/TbU= github.com/go-openapi/swag/yamlutils v0.26.1 h1:0TSLK+lXs9vfIhAWzBeI/lOzEnIoot6WTCO1aAeWFTk= github.com/go-openapi/swag/yamlutils v0.26.1/go.mod h1:7W5b7PRX9MxwL7TjeG7H8HkyBGRsIDRObhyMWFgBI2M= -github.com/go-openapi/testify/enable/yaml/v2 v2.4.2 h1:5zRca5jw7lzVREKCZVNBpysDNBjj74rBh0N2BGQbSR0= -github.com/go-openapi/testify/enable/yaml/v2 v2.4.2/go.mod h1:XVevPw5hUXuV+5AkI1u1PeAm27EQVrhXTTCPAF85LmE= github.com/go-openapi/testify/enable/yaml/v2 v2.5.1 h1:q9NtHwK4qHF7yZziBPvZyv7zWAIk8ok88Gh2mR6Jpc8= -github.com/go-openapi/testify/v2 v2.4.2 h1:tiByHpvE9uHrrKjOszax7ZvKB7QOgizBWGBLuq0ePx4= -github.com/go-openapi/testify/v2 v2.4.2/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= +github.com/go-openapi/testify/enable/yaml/v2 v2.5.1/go.mod h1:JW0MXIotCYps/XsgJnG3a8Q7rE5xAiBwoOD5OfaIQBk= github.com/go-openapi/testify/v2 v2.5.1 h1:TMdhCaw8fUNraVSf3Omoob1dO/AzBfhtFAPW0an6sBo= +github.com/go-openapi/testify/v2 v2.5.1/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= github.com/go-ozzo/ozzo-validation v3.6.0+incompatible h1:msy24VGS42fKO9K1vLz82/GeYW1cILu7Nuuj1N3BBkE= @@ -1195,7 +1170,6 @@ github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwA github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f h1:ENpDacvnr8faw5ugQmEF1QYk+f/Y9lXFvuYmRxykago= github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f/go.mod h1:KDSfL7qe5ZfQqvlDMkVjCztbmcpp/c8M77vhQP8ZPvk= @@ -1277,10 +1251,7 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= -github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -1561,8 +1532,6 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1: github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= 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.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= @@ -1991,8 +1960,6 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= -go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= @@ -2024,8 +1991,8 @@ golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58 golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= -golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= +golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -2040,8 +2007,8 @@ golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0 golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww= -golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA= +golang.org/x/image v0.44.0 h1:+tDekMZED9+LrtB3G5xzRggpVh9CARjZqROla3R3R+I= +golang.org/x/image v0.44.0/go.mod h1:V8K3KE9KKKE+pLpQDOeN18w9oacNSvy1tDOirTu4xtY= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -2067,8 +2034,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= -golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= +golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= +golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -2122,8 +2089,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= -golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= +golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2147,8 +2114,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2237,8 +2204,8 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -2248,8 +2215,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= -golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= +golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= +golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2264,8 +2231,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= -golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= +golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= +golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2325,14 +2292,12 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= -golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= -golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= +golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= +golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= -gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -2516,14 +2481,10 @@ k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/klog/v2 v2.40.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= -k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= -k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= -k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= k8s.io/kube-openapi v0.0.0-20260603220949-865597e52e25 h1:mPMaPMpBij2V1Wv/fR+HW124vVGXXvOSS9ver/9yjWs= k8s.io/kube-openapi v0.0.0-20260603220949-865597e52e25/go.mod h1:V/QaCUYDa+0QpcHhVVc5l99Uz56wEMEXBSj9oCDkNDY= k8s.io/kubectl v0.34.8 h1:XL1NjkaEkQrK6Telc/ajfQHsRlalXQJwFQe61iFUteI= @@ -2532,8 +2493,6 @@ k8s.io/metrics v0.34.8 h1:LWUkytv2+xGUGOjUIpl+yVWdq7+B8H5CeZugeI+zWSY= k8s.io/metrics v0.34.8/go.mod h1:eTAgo9Ipndb547bm7JSGH5kNuT0eYt+VRR3bePoq6Zk= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= -k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= k8s.io/utils v0.0.0-20260507154919-ff6756f316d2 h1:wU4tMEhLGgIbLvXQb1cfN+EcM0wf7zC6CPF+C79jroc= k8s.io/utils v0.0.0-20260507154919-ff6756f316d2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= kubevirt.io/api v1.6.2 h1:aoqZ4KsbOyDjLnuDw7H9wEgE/YTd/q5BBmYeQjJNizc= @@ -2556,8 +2515,6 @@ rsc.io/letsencrypt v0.0.3 h1:H7xDfhkaFFSYEJlKeq38RwX2jYcnTeHuDQyT+mMNMwM= rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= -sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= sigs.k8s.io/controller-runtime v0.22.0 h1:mTOfibb8Hxwpx3xEkR56i7xSjB+nH4hZG37SrlCY5e0= sigs.k8s.io/controller-runtime v0.22.0/go.mod h1:FwiwRjkRPbiN+zp2QRp7wlTCzbUXxZ/D4OzuQUDwBHY= sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= @@ -2573,8 +2530,6 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= -sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/structured-merge-diff/v6 v6.3.2 h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8= sigs.k8s.io/structured-merge-diff/v6 v6.3.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=