Skip to content

Commit 9d2effa

Browse files
committed
Ensure all layers are here when tagging
Signed-off-by: apostasie <[email protected]>
1 parent 2a388a7 commit 9d2effa

File tree

2 files changed

+59
-5
lines changed

2 files changed

+59
-5
lines changed

pkg/cmd/image/tag.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525

2626
"github.com/containerd/nerdctl/v2/pkg/api/types"
2727
"github.com/containerd/nerdctl/v2/pkg/idutil/imagewalker"
28+
"github.com/containerd/nerdctl/v2/pkg/imgutil"
2829
"github.com/containerd/nerdctl/v2/pkg/referenceutil"
2930
)
3031

@@ -59,17 +60,23 @@ func Tag(ctx context.Context, client *containerd.Client, options types.ImageTagO
5960
}
6061
defer done(ctx)
6162

62-
image, err := imageService.Get(ctx, srcName)
63+
img, err := imageService.Get(ctx, srcName)
6364
if err != nil {
6465
return err
6566
}
66-
image.Name = target.String()
67-
if _, err = imageService.Create(ctx, image); err != nil {
67+
img.Name = target.String()
68+
69+
err = imgutil.EnsureAllContent(ctx, client, srcName, img.Target, "", options.GOptions)
70+
if err != nil {
71+
return err
72+
}
73+
74+
if _, err = imageService.Create(ctx, img); err != nil {
6875
if errdefs.IsAlreadyExists(err) {
69-
if err = imageService.Delete(ctx, image.Name); err != nil {
76+
if err = imageService.Delete(ctx, img.Name); err != nil {
7077
return err
7178
}
72-
if _, err = imageService.Create(ctx, image); err != nil {
79+
if _, err = imageService.Create(ctx, img); err != nil {
7380
return err
7481
}
7582
} else {

pkg/imgutil/imgutil.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import (
4444
"github.com/containerd/nerdctl/v2/pkg/idutil/imagewalker"
4545
"github.com/containerd/nerdctl/v2/pkg/imgutil/dockerconfigresolver"
4646
"github.com/containerd/nerdctl/v2/pkg/imgutil/pull"
47+
"github.com/containerd/nerdctl/v2/pkg/platformutil"
4748
)
4849

4950
// EnsuredImage contains the image existed in containerd and its metadata.
@@ -102,6 +103,52 @@ func GetExistingImage(ctx context.Context, client *containerd.Client, snapshotte
102103
return res, nil
103104
}
104105

106+
func EnsureAllContent(ctx context.Context, client *containerd.Client, rawRef string, target ocispec.Descriptor, platform string, options types.GlobalCommandOptions) error {
107+
named, err := distributionref.ParseDockerRef(rawRef)
108+
if err != nil {
109+
return err
110+
}
111+
refDomain := distributionref.Domain(named)
112+
var platformComparer platforms.MatchComparer
113+
if platform != "" {
114+
parsed, err := platforms.Parse(platform)
115+
if err != nil {
116+
return err
117+
}
118+
platformComparer = platformutil.NewMatchComparerFromOCISpecPlatformSlice([]ocispec.Platform{parsed})
119+
} else {
120+
platformComparer = platforms.Default()
121+
}
122+
123+
_, _, _, missing, err := images.Check(ctx, client.ContentStore(), target, platformComparer)
124+
if err != nil {
125+
return err
126+
}
127+
128+
if len(missing) > 0 {
129+
// Get a resolver
130+
var dOpts []dockerconfigresolver.Opt
131+
if options.InsecureRegistry {
132+
log.G(ctx).Warnf("skipping verifying HTTPS certs for %q", refDomain)
133+
dOpts = append(dOpts, dockerconfigresolver.WithSkipVerifyCerts(true))
134+
}
135+
dOpts = append(dOpts, dockerconfigresolver.WithHostsDirs(options.HostsDir))
136+
resolver, err := dockerconfigresolver.New(ctx, refDomain, dOpts...)
137+
if err != nil {
138+
return err
139+
}
140+
141+
opts := []containerd.RemoteOpt{
142+
containerd.WithResolver(resolver),
143+
containerd.WithPlatformMatcher(platformComparer),
144+
}
145+
_, err = client.Fetch(ctx, rawRef, opts...)
146+
return err
147+
}
148+
149+
return nil
150+
}
151+
105152
// EnsureImage ensures the image.
106153
//
107154
// # When insecure is set, skips verifying certs, and also falls back to HTTP when the registry does not speak HTTPS

0 commit comments

Comments
 (0)