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
17package images
18
19import (
20	"fmt"
21
22	"github.com/containerd/containerd"
23	"github.com/containerd/containerd/cmd/ctr/commands"
24	"github.com/containerd/containerd/cmd/ctr/commands/content"
25	"github.com/containerd/containerd/images"
26	"github.com/containerd/containerd/log"
27	"github.com/containerd/containerd/platforms"
28	"github.com/opencontainers/image-spec/identity"
29	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
30	"github.com/pkg/errors"
31	"github.com/urfave/cli"
32)
33
34var pullCommand = cli.Command{
35	Name:      "pull",
36	Usage:     "pull an image from a remote",
37	ArgsUsage: "[flags] <ref>",
38	Description: `Fetch and prepare an image for use in containerd.
39
40After pulling an image, it should be ready to use the same reference in a run
41command. As part of this process, we do the following:
42
431. Fetch all resources into containerd.
442. Prepare the snapshot filesystem with the pulled resources.
453. Register metadata for the image.
46`,
47	Flags: append(append(commands.RegistryFlags, append(commands.SnapshotterFlags, commands.LabelFlag)...),
48		cli.StringSliceFlag{
49			Name:  "platform",
50			Usage: "Pull content from a specific platform",
51			Value: &cli.StringSlice{},
52		},
53		cli.BoolFlag{
54			Name:  "all-platforms",
55			Usage: "pull content and metadata from all platforms",
56		},
57		cli.BoolFlag{
58			Name:  "all-metadata",
59			Usage: "Pull metadata for all platforms",
60		},
61		cli.BoolFlag{
62			Name:  "print-chainid",
63			Usage: "Print the resulting image's chain ID",
64		},
65	),
66	Action: func(context *cli.Context) error {
67		var (
68			ref = context.Args().First()
69		)
70		if ref == "" {
71			return fmt.Errorf("please provide an image reference to pull")
72		}
73
74		client, ctx, cancel, err := commands.NewClient(context)
75		if err != nil {
76			return err
77		}
78		defer cancel()
79
80		ctx, done, err := client.WithLease(ctx)
81		if err != nil {
82			return err
83		}
84		defer done(ctx)
85
86		config, err := content.NewFetchConfig(ctx, context)
87		if err != nil {
88			return err
89		}
90
91		img, err := content.Fetch(ctx, client, ref, config)
92		if err != nil {
93			return err
94		}
95
96		log.G(ctx).WithField("image", ref).Debug("unpacking")
97
98		// TODO: Show unpack status
99
100		var p []ocispec.Platform
101		if context.Bool("all-platforms") {
102			p, err = images.Platforms(ctx, client.ContentStore(), img.Target)
103			if err != nil {
104				return errors.Wrap(err, "unable to resolve image platforms")
105			}
106		} else {
107			for _, s := range context.StringSlice("platform") {
108				ps, err := platforms.Parse(s)
109				if err != nil {
110					return errors.Wrapf(err, "unable to parse platform %s", s)
111				}
112				p = append(p, ps)
113			}
114		}
115		if len(p) == 0 {
116			p = append(p, platforms.DefaultSpec())
117		}
118
119		for _, platform := range p {
120			fmt.Printf("unpacking %s %s...\n", platforms.Format(platform), img.Target.Digest)
121			i := containerd.NewImageWithPlatform(client, img, platforms.Only(platform))
122			err = i.Unpack(ctx, context.String("snapshotter"))
123			if err != nil {
124				return err
125			}
126			if context.Bool("print-chainid") {
127				diffIDs, err := i.RootFS(ctx)
128				if err != nil {
129					return err
130				}
131				chainID := identity.ChainID(diffIDs).String()
132				fmt.Printf("image chain ID: %s\n", chainID)
133			}
134		}
135
136		fmt.Println("done")
137		return nil
138	},
139}
140