Skip to content

Commit 9539f6b

Browse files
committed
Add fetch and EnsureAllContent methods
Signed-off-by: apostasie <[email protected]>
1 parent b2a7039 commit 9539f6b

File tree

2 files changed

+219
-0
lines changed

2 files changed

+219
-0
lines changed

pkg/cmd/image/ensure.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package image
18+
19+
import (
20+
"context"
21+
"errors"
22+
"fmt"
23+
"net/http"
24+
"os"
25+
26+
distributionref "github.com/distribution/reference"
27+
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
28+
29+
containerd "github.com/containerd/containerd/v2/client"
30+
"github.com/containerd/containerd/v2/core/images"
31+
"github.com/containerd/log"
32+
33+
"github.com/containerd/nerdctl/v2/pkg/api/types"
34+
"github.com/containerd/nerdctl/v2/pkg/containerdutil"
35+
"github.com/containerd/nerdctl/v2/pkg/errutil"
36+
"github.com/containerd/nerdctl/v2/pkg/imgutil/dockerconfigresolver"
37+
"github.com/containerd/nerdctl/v2/pkg/imgutil/fetch"
38+
"github.com/containerd/nerdctl/v2/pkg/platformutil"
39+
)
40+
41+
func EnsureAllContent(ctx context.Context, client *containerd.Client, srcName string, options types.GlobalCommandOptions) error {
42+
// Get the image from the srcName
43+
imageService := client.ImageService()
44+
img, err := imageService.Get(ctx, srcName)
45+
if err != nil {
46+
fmt.Println("Failed getting imageservice")
47+
return err
48+
}
49+
50+
provider := containerdutil.NewProvider(client)
51+
snapshotter := containerdutil.SnapshotService(client, options.Snapshotter)
52+
// Read the image
53+
imagesList, _ := read(ctx, provider, snapshotter, img.Target)
54+
// Iterate through the list
55+
for _, i := range imagesList {
56+
err = ensureOne(ctx, client, srcName, img.Target, i.platform, options)
57+
if err != nil {
58+
return err
59+
}
60+
}
61+
62+
return nil
63+
}
64+
65+
func ensureOne(ctx context.Context, client *containerd.Client, rawRef string, target ocispec.Descriptor, platform ocispec.Platform, options types.GlobalCommandOptions) error {
66+
67+
named, err := distributionref.ParseDockerRef(rawRef)
68+
if err != nil {
69+
return err
70+
}
71+
refDomain := distributionref.Domain(named)
72+
// if platform == nil {
73+
// platform = platforms.DefaultSpec()
74+
//}
75+
pltf := []ocispec.Platform{platform}
76+
platformComparer := platformutil.NewMatchComparerFromOCISpecPlatformSlice(pltf)
77+
78+
_, _, _, missing, err := images.Check(ctx, client.ContentStore(), target, platformComparer)
79+
if err != nil {
80+
return err
81+
}
82+
83+
if len(missing) > 0 {
84+
// Get a resolver
85+
var dOpts []dockerconfigresolver.Opt
86+
if options.InsecureRegistry {
87+
log.G(ctx).Warnf("skipping verifying HTTPS certs for %q", refDomain)
88+
dOpts = append(dOpts, dockerconfigresolver.WithSkipVerifyCerts(true))
89+
}
90+
dOpts = append(dOpts, dockerconfigresolver.WithHostsDirs(options.HostsDir))
91+
resolver, err := dockerconfigresolver.New(ctx, refDomain, dOpts...)
92+
if err != nil {
93+
return err
94+
}
95+
config := &fetch.Config{
96+
Resolver: resolver,
97+
RemoteOpts: []containerd.RemoteOpt{},
98+
Platforms: pltf,
99+
ProgressOutput: os.Stderr,
100+
}
101+
102+
err = fetch.Fetch(ctx, client, rawRef, config)
103+
104+
if err != nil {
105+
// In some circumstance (e.g. people just use 80 port to support pure http), the error will contain message like "dial tcp <port>: connection refused".
106+
if !errors.Is(err, http.ErrSchemeMismatch) && !errutil.IsErrConnectionRefused(err) {
107+
return err
108+
}
109+
if options.InsecureRegistry {
110+
log.G(ctx).WithError(err).Warnf("server %q does not seem to support HTTPS, falling back to plain HTTP", refDomain)
111+
dOpts = append(dOpts, dockerconfigresolver.WithPlainHTTP(true))
112+
resolver, err = dockerconfigresolver.New(ctx, refDomain, dOpts...)
113+
if err != nil {
114+
return err
115+
}
116+
config.Resolver = resolver
117+
return fetch.Fetch(ctx, client, rawRef, config)
118+
}
119+
log.G(ctx).WithError(err).Errorf("server %q does not seem to support HTTPS", refDomain)
120+
log.G(ctx).Info("Hint: you may want to try --insecure-registry to allow plain HTTP (if you are in a trusted network)")
121+
}
122+
123+
return err
124+
}
125+
126+
return nil
127+
}

pkg/imgutil/fetch/fetch.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package fetch
18+
19+
import (
20+
"context"
21+
"io"
22+
23+
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
24+
25+
containerd "github.com/containerd/containerd/v2/client"
26+
"github.com/containerd/containerd/v2/core/images"
27+
"github.com/containerd/containerd/v2/core/remotes"
28+
"github.com/containerd/log"
29+
30+
"github.com/containerd/nerdctl/v2/pkg/imgutil/jobs"
31+
"github.com/containerd/nerdctl/v2/pkg/platformutil"
32+
)
33+
34+
// Config for content fetch
35+
type Config struct {
36+
// Resolver
37+
Resolver remotes.Resolver
38+
// ProgressOutput to display progress
39+
ProgressOutput io.Writer
40+
// RemoteOpts, e.g. containerd.WithPullUnpack.
41+
//
42+
// Regardless to RemoteOpts, the following opts are always set:
43+
// WithResolver, WithImageHandler, WithSchema1Conversion
44+
//
45+
// RemoteOpts related to unpacking can be set only when len(Platforms) is 1.
46+
RemoteOpts []containerd.RemoteOpt
47+
Platforms []ocispec.Platform // empty for all-platforms
48+
}
49+
50+
func Fetch(ctx context.Context, client *containerd.Client, ref string, config *Config) error {
51+
ongoing := jobs.New(ref)
52+
53+
pctx, stopProgress := context.WithCancel(ctx)
54+
progress := make(chan struct{})
55+
56+
go func() {
57+
if config.ProgressOutput != nil {
58+
// no progress bar, because it hides some debug logs
59+
jobs.ShowProgress(pctx, ongoing, client.ContentStore(), config.ProgressOutput)
60+
}
61+
close(progress)
62+
}()
63+
64+
h := images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
65+
if desc.MediaType != images.MediaTypeDockerSchema1Manifest {
66+
ongoing.Add(desc)
67+
}
68+
return nil, nil
69+
})
70+
71+
log.G(pctx).WithField("image", ref).Debug("fetching")
72+
platformMC := platformutil.NewMatchComparerFromOCISpecPlatformSlice(config.Platforms)
73+
opts := []containerd.RemoteOpt{
74+
containerd.WithResolver(config.Resolver),
75+
containerd.WithImageHandler(h),
76+
//nolint:staticcheck
77+
containerd.WithSchema1Conversion, //lint:ignore SA1019 nerdctl should support schema1 as well.
78+
containerd.WithPlatformMatcher(platformMC),
79+
}
80+
opts = append(opts, config.RemoteOpts...)
81+
82+
// Note that client.Fetch does not unpack
83+
_, err := client.Fetch(pctx, ref, opts...)
84+
85+
stopProgress()
86+
if err != nil {
87+
return err
88+
}
89+
90+
<-progress
91+
return nil
92+
}

0 commit comments

Comments
 (0)