github.com/dctrud/umoci@v0.4.3-0.20191016193643-05a1d37de015/cmd/umoci/raw-unpack.go (about) 1 /* 2 * umoci: Umoci Modifies Open Containers' Images 3 * Copyright (C) 2018 SUSE LLC. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package main 19 20 import ( 21 "fmt" 22 23 "github.com/apex/log" 24 "github.com/openSUSE/umoci" 25 "github.com/openSUSE/umoci/oci/cas/dir" 26 "github.com/openSUSE/umoci/oci/casext" 27 "github.com/openSUSE/umoci/oci/layer" 28 ispec "github.com/opencontainers/image-spec/specs-go/v1" 29 "github.com/pkg/errors" 30 "github.com/urfave/cli" 31 "golang.org/x/net/context" 32 ) 33 34 var rawUnpackCommand = uxRemap(cli.Command{ 35 Name: "unpack", 36 Usage: "unpacks a reference into a rootfs", 37 ArgsUsage: `--image <image-path>[:<tag>] <rootfs> 38 39 Where "<image-path>" is the path to the OCI image, "<tag>" is the name of the 40 tagged image to unpack (if not specified, defaults to "latest") and "<rootfs>" 41 is the destination to unpack the image to.`, 42 43 // unpack reads manifest information. 44 Category: "image", 45 46 Flags: []cli.Flag{ 47 cli.BoolFlag{ 48 Name: "keep-dirlinks", 49 Usage: "don't clobber underlying symlinks to directories", 50 }, 51 }, 52 53 Action: rawUnpack, 54 55 Before: func(ctx *cli.Context) error { 56 if ctx.NArg() != 1 { 57 return errors.Errorf("invalid number of positional arguments: expected <rootfs>") 58 } 59 if ctx.Args().First() == "" { 60 return errors.Errorf("rootfs path cannot be empty") 61 } 62 ctx.App.Metadata["rootfs"] = ctx.Args().First() 63 return nil 64 }, 65 }) 66 67 func rawUnpack(ctx *cli.Context) error { 68 imagePath := ctx.App.Metadata["--image-path"].(string) 69 fromName := ctx.App.Metadata["--image-tag"].(string) 70 rootfsPath := ctx.App.Metadata["rootfs"].(string) 71 72 var meta umoci.Meta 73 meta.Version = umoci.MetaVersion 74 75 // Parse map options. 76 // We need to set mappings if we're in rootless mode. 77 err := umoci.ParseIdmapOptions(&meta, ctx) 78 if err != nil { 79 return err 80 } 81 82 meta.MapOptions.KeepDirlinks = ctx.Bool("keep-dirlinks") 83 84 // Get a reference to the CAS. 85 engine, err := dir.Open(imagePath) 86 if err != nil { 87 return errors.Wrap(err, "open CAS") 88 } 89 engineExt := casext.NewEngine(engine) 90 defer engine.Close() 91 92 fromDescriptorPaths, err := engineExt.ResolveReference(context.Background(), fromName) 93 if err != nil { 94 return errors.Wrap(err, "get descriptor") 95 } 96 if len(fromDescriptorPaths) == 0 { 97 return errors.Errorf("tag is not found: %s", fromName) 98 } 99 if len(fromDescriptorPaths) != 1 { 100 // TODO: Handle this more nicely. 101 return errors.Errorf("tag is ambiguous: %s", fromName) 102 } 103 meta.From = fromDescriptorPaths[0] 104 105 manifestBlob, err := engineExt.FromDescriptor(context.Background(), meta.From.Descriptor()) 106 if err != nil { 107 return errors.Wrap(err, "get manifest") 108 } 109 defer manifestBlob.Close() 110 111 if manifestBlob.MediaType != ispec.MediaTypeImageManifest { 112 return errors.Wrap(fmt.Errorf("descriptor does not point to ispec.MediaTypeImageManifest: not implemented: %s", manifestBlob.MediaType), "invalid --image tag") 113 } 114 115 log.WithFields(log.Fields{ 116 "image": imagePath, 117 "rootfs": rootfsPath, 118 "ref": fromName, 119 }).Debugf("umoci: unpacking OCI image") 120 121 // Get the manifest. 122 manifest, ok := manifestBlob.Data.(ispec.Manifest) 123 if !ok { 124 // Should _never_ be reached. 125 return errors.Errorf("[internal error] unknown manifest blob type: %s", manifestBlob.MediaType) 126 } 127 128 // FIXME: Currently we only support OCI layouts, not tar archives. This 129 // should be fixed once the CAS engine PR is merged into 130 // image-tools. https://github.com/opencontainers/image-tools/pull/5 131 log.Warnf("unpacking rootfs ...") 132 if err := layer.UnpackRootfs(context.Background(), engineExt, rootfsPath, manifest, &meta.MapOptions); err != nil { 133 return errors.Wrap(err, "create rootfs") 134 } 135 log.Warnf("... done") 136 137 log.Warnf("unpacked image rootfs: %s", rootfsPath) 138 return nil 139 }