github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/cmd/ctr/commands/images/import.go (about)

     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 images
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"os"
    23  	"time"
    24  
    25  	"github.com/containerd/containerd"
    26  	"github.com/containerd/containerd/cmd/ctr/commands"
    27  	"github.com/containerd/containerd/images/archive"
    28  	"github.com/containerd/containerd/log"
    29  	"github.com/urfave/cli"
    30  )
    31  
    32  var importCommand = cli.Command{
    33  	Name:      "import",
    34  	Usage:     "import images",
    35  	ArgsUsage: "[flags] <in>",
    36  	Description: `Import images from a tar stream.
    37  Implemented formats:
    38  - oci.v1
    39  - docker.v1.1
    40  - docker.v1.2
    41  
    42  
    43  For OCI v1, you may need to specify --base-name because an OCI archive may
    44  contain only partial image references (tags without the base image name).
    45  If no base image name is provided, a name will be generated as "import-%{yyyy-MM-dd}".
    46  
    47  e.g.
    48    $ ctr images import --base-name foo/bar foobar.tar
    49  
    50  If foobar.tar contains an OCI ref named "latest" and anonymous ref "sha256:deadbeef", the command will create
    51  "foo/bar:latest" and "foo/bar@sha256:deadbeef" images in the containerd store.
    52  `,
    53  	Flags: append([]cli.Flag{
    54  		cli.StringFlag{
    55  			Name:  "base-name",
    56  			Value: "",
    57  			Usage: "base image name for added images, when provided only images with this name prefix are imported",
    58  		},
    59  		cli.BoolFlag{
    60  			Name:  "digests",
    61  			Usage: "whether to create digest images (default: false)",
    62  		},
    63  		cli.StringFlag{
    64  			Name:  "index-name",
    65  			Usage: "image name to keep index as, by default index is discarded",
    66  		},
    67  		cli.BoolFlag{
    68  			Name:  "all-platforms",
    69  			Usage: "imports content for all platforms, false by default",
    70  		},
    71  		cli.BoolFlag{
    72  			Name:  "no-unpack",
    73  			Usage: "skip unpacking the images, false by default",
    74  		},
    75  		cli.BoolFlag{
    76  			Name:  "compress-blobs",
    77  			Usage: "compress uncompressed blobs when creating manifest (Docker format only)",
    78  		},
    79  	}, commands.SnapshotterFlags...),
    80  
    81  	Action: func(context *cli.Context) error {
    82  		var (
    83  			in   = context.Args().First()
    84  			opts []containerd.ImportOpt
    85  		)
    86  
    87  		prefix := context.String("base-name")
    88  		if prefix == "" {
    89  			prefix = fmt.Sprintf("import-%s", time.Now().Format("2006-01-02"))
    90  			opts = append(opts, containerd.WithImageRefTranslator(archive.AddRefPrefix(prefix)))
    91  		} else {
    92  			// When provided, filter out references which do not match
    93  			opts = append(opts, containerd.WithImageRefTranslator(archive.FilterRefPrefix(prefix)))
    94  		}
    95  
    96  		if context.Bool("digests") {
    97  			opts = append(opts, containerd.WithDigestRef(archive.DigestTranslator(prefix)))
    98  		}
    99  
   100  		if idxName := context.String("index-name"); idxName != "" {
   101  			opts = append(opts, containerd.WithIndexName(idxName))
   102  		}
   103  
   104  		if context.Bool("compress-blobs") {
   105  			opts = append(opts, containerd.WithImportCompression())
   106  		}
   107  
   108  		opts = append(opts, containerd.WithAllPlatforms(context.Bool("all-platforms")))
   109  
   110  		client, ctx, cancel, err := commands.NewClient(context)
   111  		if err != nil {
   112  			return err
   113  		}
   114  		defer cancel()
   115  
   116  		var r io.ReadCloser
   117  		if in == "-" {
   118  			r = os.Stdin
   119  		} else {
   120  			r, err = os.Open(in)
   121  			if err != nil {
   122  				return err
   123  			}
   124  		}
   125  		imgs, err := client.Import(ctx, r, opts...)
   126  		closeErr := r.Close()
   127  		if err != nil {
   128  			return err
   129  		}
   130  		if closeErr != nil {
   131  			return closeErr
   132  		}
   133  
   134  		if !context.Bool("no-unpack") {
   135  			log.G(ctx).Debugf("unpacking %d images", len(imgs))
   136  
   137  			for _, img := range imgs {
   138  				// TODO: Allow configuration of the platform
   139  				image := containerd.NewImage(client, img)
   140  
   141  				// TODO: Show unpack status
   142  				fmt.Printf("unpacking %s (%s)...", img.Name, img.Target.Digest)
   143  				err = image.Unpack(ctx, context.String("snapshotter"))
   144  				if err != nil {
   145  					return err
   146  				}
   147  				fmt.Println("done")
   148  			}
   149  		}
   150  		return nil
   151  	},
   152  }