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