Skip to content

Commit 4e0f11b

Browse files
feat: new push/pull (#243)
Signed-off-by: Timur Tuktamyshev <[email protected]> Signed-off-by: Pavel Okhlopkov <[email protected]> Co-authored-by: Pavel Okhlopkov <[email protected]>
1 parent ffd37ad commit 4e0f11b

File tree

22 files changed

+927
-183
lines changed

22 files changed

+927
-183
lines changed

internal/layout.go

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,32 @@ package internal
1818

1919
import "path"
2020

21-
// deckhouse repo structure
22-
// root-segment:<version>
23-
// root-segment/install:<version>
24-
// root-segment/install-standalone:<version>
25-
// root-segment/release-channel:<version>
26-
// root-segment/modules/<module-name>:<version>
27-
// root-segment/modules/<module-name>/releases:<version>
28-
// root-segment/modules/<module-name>/extra/<module-extra-name>:<version>
21+
// deckhouse repo structure (relative to root path like registry.deckhouse.io/deckhouse/fe)
22+
//
23+
// Platform:
24+
//
25+
// <root>:<version> - Deckhouse main image
26+
// <root>/release-channel:<channel> - Release channel metadata
27+
// <root>/install:<version> - Installer image
28+
// <root>/install-standalone:<version> - Standalone installer
29+
//
30+
// Security:
31+
//
32+
// <root>/security/<security-name>:<version> - Security databases (trivy-db, trivy-bdu, etc.)
33+
//
34+
// Modules:
35+
//
36+
// <root>/modules/<module-name>:<version> - Module main image
37+
// <root>/modules/<module-name>/release:<channel> - Module release channel metadata
38+
// <root>/modules/<module-name>/extra/<extra-name>:<version> - Module extra images
2939
const (
3040
InstallSegment = "install"
3141
InstallStandaloneSegment = "install-standalone"
3242
ReleaseChannelSegment = "release-channel"
3343

34-
ModulesSegment = "modules"
35-
ModulesExtraSegment = "extra"
36-
ModulesReleasesSegment = "releases"
44+
ModulesSegment = "modules"
45+
ModulesReleaseSegment = "release"
46+
ModulesExtraSegment = "extra"
3747

3848
SecuritySegment = "security"
3949

@@ -49,9 +59,9 @@ var pathByMirrorType = map[MirrorType]string{
4959
MirrorTypeDeckhouseInstallStandalone: InstallStandaloneSegment,
5060
MirrorTypeDeckhouseReleaseChannels: ReleaseChannelSegment,
5161

52-
MirrorTypeModules: ModulesSegment,
53-
MirrorTypeModulesReleaseChannels: ModulesReleasesSegment,
54-
MirrorTypeModulesExtra: ModulesExtraSegment,
62+
// Module paths are relative to modules/<module-name>/ directory
63+
MirrorTypeModules: "", // Module main image at root of module dir
64+
MirrorTypeModulesReleaseChannels: ModulesReleaseSegment, // modules/<name>/release
5565

5666
MirrorTypeSecurity: SecuritySegment,
5767
MirrorTypeSecurityTrivyDBSegment: path.Join(SecuritySegment, SecurityTrivyDBSegment),

internal/mirror.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ const (
2525
MirrorTypeDeckhouseReleaseChannels
2626
MirrorTypeModules
2727
MirrorTypeModulesReleaseChannels
28-
MirrorTypeModulesExtra
2928
MirrorTypeSecurity
3029
MirrorTypeSecurityTrivyDBSegment
3130
MirrorTypeSecurityTrivyBDUSegment

internal/mirror/cmd/pull/flags/flags.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,9 @@ var (
5555
SourceRegistryPassword string
5656
DeckhouseLicenseToken string
5757

58-
DoGOSTDigest bool
59-
NoPullResume bool
58+
DoGOSTDigest bool
59+
NoPullResume bool
60+
IgnoreSuspend bool
6061

6162
NoPlatform bool
6263
NoSecurityDB bool
@@ -161,6 +162,12 @@ module-name@=v1.3.0+stable → exact tag match: include only v1.3.0 and and publ
161162
false,
162163
"Do not continue last unfinished pull operation and start from scratch.",
163164
)
165+
flagSet.BoolVar(
166+
&IgnoreSuspend,
167+
"ignore-suspend",
168+
false,
169+
"Ignore suspended release channels and continue mirroring. Use with caution.",
170+
)
164171
flagSet.BoolVar(
165172
&NoPlatform,
166173
"no-platform",

internal/mirror/cmd/pull/pull.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ import (
2323
"fmt"
2424
"log/slog"
2525
"os"
26+
"os/signal"
2627
"path"
2728
"path/filepath"
29+
"syscall"
2830
"time"
2931

3032
"github.com/Masterminds/semver/v3"
@@ -102,11 +104,23 @@ func NewCommand() *cobra.Command {
102104
}
103105

104106
func pull(cmd *cobra.Command, _ []string) error {
107+
// Set up graceful cancellation on Ctrl+C
108+
parentCtx := cmd.Context()
109+
if parentCtx == nil {
110+
parentCtx = context.Background()
111+
}
112+
ctx, cancel := signal.NotifyContext(parentCtx, syscall.SIGINT, syscall.SIGTERM)
113+
defer cancel()
114+
105115
puller := NewPuller(cmd)
106116

107117
puller.logger.Infof("d8 version: %s", version.Version)
108118

109-
if err := puller.Execute(cmd.Context()); err != nil {
119+
if err := puller.Execute(ctx); err != nil {
120+
if errors.Is(err, context.Canceled) {
121+
puller.logger.WarnLn("Operation cancelled by user")
122+
return nil
123+
}
110124
return ErrPullFailed
111125
}
112126

@@ -164,6 +178,7 @@ func buildPullParams(logger params.Logger) *params.PullParams {
164178
SkipSecurityDatabases: pullflags.NoSecurityDB,
165179
SkipModules: pullflags.NoModules,
166180
OnlyExtraImages: pullflags.OnlyExtraImages,
181+
IgnoreSuspend: pullflags.IgnoreSuspend,
167182
DeckhouseTag: pullflags.DeckhouseTag,
168183
SinceVersion: pullflags.SinceVersion,
169184
}
@@ -276,6 +291,7 @@ func (p *Puller) Execute(ctx context.Context) error {
276291
SkipSecurity: pullflags.NoSecurityDB,
277292
SkipModules: pullflags.NoModules,
278293
OnlyExtraImages: pullflags.OnlyExtraImages,
294+
IgnoreSuspend: pullflags.IgnoreSuspend,
279295
ModuleFilter: filter,
280296
BundleDir: pullflags.ImagesBundlePath,
281297
BundleChunkSize: pullflags.ImagesBundleChunkSizeGB * 1000 * 1000 * 1000,

internal/mirror/cmd/push/push.go

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ import (
2323
"io"
2424
"log/slog"
2525
"os"
26+
"os/signal"
2627
"path"
2728
"path/filepath"
2829
"strings"
30+
"syscall"
2931
"time"
3032

3133
"github.com/google/go-containerregistry/pkg/authn"
@@ -36,6 +38,7 @@ import (
3638
"github.com/deckhouse/deckhouse/pkg/registry"
3739
regclient "github.com/deckhouse/deckhouse/pkg/registry/client"
3840

41+
"github.com/deckhouse/deckhouse-cli/internal/mirror"
3942
"github.com/deckhouse/deckhouse-cli/internal/mirror/chunked"
4043
"github.com/deckhouse/deckhouse-cli/internal/mirror/operations"
4144
"github.com/deckhouse/deckhouse-cli/internal/version"
@@ -188,7 +191,7 @@ func pushStaticPackages(pushParams *params.PushParams, logger params.Logger, cli
188191
}
189192

190193
if err = pkg.Close(); err != nil {
191-
logger.Warnf("Could not close bundle package %s: %w", pkgName, err)
194+
logger.Warnf("Could not close bundle package %s: %v", pkgName, err)
192195
}
193196
}
194197
return nil
@@ -289,6 +292,11 @@ func (p *Pusher) Execute() error {
289292
return err
290293
}
291294

295+
// Use new push service when NEW_PULL env is set
296+
if os.Getenv("NEW_PULL") == "true" {
297+
return p.executeNewPush()
298+
}
299+
292300
if err := p.pushStaticPackages(); err != nil {
293301
return err
294302
}
@@ -300,6 +308,63 @@ func (p *Pusher) Execute() error {
300308
return nil
301309
}
302310

311+
// executeNewPush runs the push using the push service.
312+
// This service expects the bundle to have the exact same structure as the registry:
313+
// - Each OCI layout's relative path becomes its registry segment
314+
// - Works with unified bundles where pull saved the structure as-is
315+
func (p *Pusher) executeNewPush() error {
316+
// Set up graceful cancellation on Ctrl+C
317+
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
318+
defer cancel()
319+
320+
logger := dkplog.NewNop()
321+
322+
if log.DebugLogLevel() >= 3 {
323+
logger = dkplog.NewLogger(dkplog.WithLevel(slog.LevelDebug))
324+
}
325+
326+
// Create registry client
327+
clientOpts := &regclient.Options{
328+
Insecure: p.pushParams.Insecure,
329+
TLSSkipVerify: p.pushParams.SkipTLSVerification,
330+
Logger: logger,
331+
}
332+
333+
if p.pushParams.RegistryAuth != nil {
334+
clientOpts.Auth = p.pushParams.RegistryAuth
335+
}
336+
337+
var client registry.Client
338+
client = regclient.NewClientWithOptions(p.pushParams.RegistryHost, clientOpts)
339+
340+
// Scope to the registry path
341+
if p.pushParams.RegistryPath != "" {
342+
client = client.WithSegment(p.pushParams.RegistryPath)
343+
}
344+
345+
svc := mirror.NewPushService(
346+
client,
347+
&mirror.PushServiceOptions{
348+
BundleDir: p.pushParams.BundleDir,
349+
WorkingDir: p.pushParams.WorkingDir,
350+
},
351+
logger.Named("push"),
352+
p.logger.(*log.SLogger),
353+
)
354+
355+
err := svc.Push(ctx)
356+
if err != nil {
357+
// Handle context cancellation gracefully
358+
if errors.Is(err, context.Canceled) {
359+
p.logger.WarnLn("Operation cancelled by user")
360+
return nil
361+
}
362+
return err
363+
}
364+
365+
return nil
366+
}
367+
303368
// validateRegistryAccess validates access to the registry
304369
func (p *Pusher) validateRegistryAccess() error {
305370
p.logger.InfoLn("Validating registry access")

internal/mirror/modules/layout.go

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,15 +117,20 @@ type ImageLayouts struct {
117117
platform v1.Platform
118118
workingDir string
119119

120-
Modules *regimage.ImageLayout
120+
// Modules is the main module image layout (modules/<name>/)
121+
Modules *regimage.ImageLayout
122+
// ModulesReleaseChannels is the release channel layout (modules/<name>/release/)
121123
ModulesReleaseChannels *regimage.ImageLayout
122-
ModulesExtra *regimage.ImageLayout
124+
// ExtraImages holds layouts for each extra image (modules/<name>/extra/<extra-name>/)
125+
// Key is the extra image name (e.g., "scanner", "enforcer")
126+
ExtraImages map[string]*regimage.ImageLayout
123127
}
124128

125129
func NewImageLayouts(rootFolder string) *ImageLayouts {
126130
l := &ImageLayouts{
127-
workingDir: rootFolder,
128-
platform: v1.Platform{Architecture: "amd64", OS: "linux"},
131+
workingDir: rootFolder,
132+
platform: v1.Platform{Architecture: "amd64", OS: "linux"},
133+
ExtraImages: make(map[string]*regimage.ImageLayout),
129134
}
130135

131136
return l
@@ -144,15 +149,31 @@ func (l *ImageLayouts) setLayoutByMirrorType(rootFolder string, mirrorType inter
144149
l.Modules = layout
145150
case internal.MirrorTypeModulesReleaseChannels:
146151
l.ModulesReleaseChannels = layout
147-
case internal.MirrorTypeModulesExtra:
148-
l.ModulesExtra = layout
149152
default:
150153
return fmt.Errorf("wrong mirror type in modules image layout: %v", mirrorType)
151154
}
152155

153156
return nil
154157
}
155158

159+
// GetOrCreateExtraLayout returns or creates a layout for a specific extra image.
160+
// Extra images are stored under: modules/<name>/extra/<extra-name>/
161+
func (l *ImageLayouts) GetOrCreateExtraLayout(extraName string) (*regimage.ImageLayout, error) {
162+
if existing, ok := l.ExtraImages[extraName]; ok {
163+
return existing, nil
164+
}
165+
166+
// Create layout at modules/<module-name>/extra/<extra-name>/
167+
layoutPath := filepath.Join(l.workingDir, "extra", extraName)
168+
layout, err := regimage.NewImageLayout(layoutPath)
169+
if err != nil {
170+
return nil, fmt.Errorf("create extra image layout for %s: %w", extraName, err)
171+
}
172+
173+
l.ExtraImages[extraName] = layout
174+
return layout, nil
175+
}
176+
156177
// AsList returns a list of layout.Path's in it. Undefined path's are not included in the list.
157178
func (l *ImageLayouts) AsList() []layout.Path {
158179
paths := make([]layout.Path, 0)
@@ -162,8 +183,11 @@ func (l *ImageLayouts) AsList() []layout.Path {
162183
if l.ModulesReleaseChannels != nil {
163184
paths = append(paths, l.ModulesReleaseChannels.Path())
164185
}
165-
if l.ModulesExtra != nil {
166-
paths = append(paths, l.ModulesExtra.Path())
186+
// Add all extra image layouts
187+
for _, extraLayout := range l.ExtraImages {
188+
if extraLayout != nil {
189+
paths = append(paths, extraLayout.Path())
190+
}
167191
}
168192
return paths
169193
}

0 commit comments

Comments
 (0)