|
| 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 | +} |
0 commit comments