github.com/containerd/Containerd@v1.4.13/cmd/ctr/commands/images/mount.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 "time" 22 23 "github.com/containerd/containerd" 24 "github.com/containerd/containerd/cmd/ctr/commands" 25 "github.com/containerd/containerd/errdefs" 26 "github.com/containerd/containerd/leases" 27 "github.com/containerd/containerd/mount" 28 "github.com/containerd/containerd/platforms" 29 "github.com/opencontainers/image-spec/identity" 30 "github.com/pkg/errors" 31 "github.com/urfave/cli" 32 ) 33 34 var mountCommand = cli.Command{ 35 Name: "mount", 36 Usage: "mount an image to a target path", 37 ArgsUsage: "[flags] <ref> <target>", 38 Description: `Mount an image rootfs to a specified path. 39 40 When you are done, use the unmount command. 41 `, 42 Flags: append(append(commands.RegistryFlags, append(commands.SnapshotterFlags, commands.LabelFlag)...), 43 cli.BoolFlag{ 44 Name: "rw", 45 Usage: "Enable write support on the mount", 46 }, 47 cli.StringFlag{ 48 Name: "platform", 49 Usage: "Mount the image for the specified platform", 50 Value: platforms.DefaultString(), 51 }, 52 ), 53 Action: func(context *cli.Context) (retErr error) { 54 var ( 55 ref = context.Args().First() 56 target = context.Args().Get(1) 57 ) 58 if ref == "" { 59 return fmt.Errorf("please provide an image reference to mount") 60 } 61 if target == "" { 62 return fmt.Errorf("please provide a target path to mount to") 63 } 64 65 client, ctx, cancel, err := commands.NewClient(context) 66 if err != nil { 67 return err 68 } 69 defer cancel() 70 71 snapshotter := context.GlobalString("snapshotter") 72 if snapshotter == "" { 73 snapshotter = containerd.DefaultSnapshotter 74 } 75 76 ctx, done, err := client.WithLease(ctx, 77 leases.WithID(target), 78 leases.WithExpiration(24*time.Hour), 79 leases.WithLabels(map[string]string{ 80 "containerd.io/gc.ref.snapshot." + snapshotter: target, 81 }), 82 ) 83 if err != nil && !errdefs.IsAlreadyExists(err) { 84 return err 85 } 86 87 defer func() { 88 if retErr != nil && done != nil { 89 done(ctx) 90 } 91 }() 92 93 ps := context.String("platform") 94 p, err := platforms.Parse(ps) 95 if err != nil { 96 return errors.Wrapf(err, "unable to parse platform %s", ps) 97 } 98 99 img, err := client.ImageService().Get(ctx, ref) 100 if err != nil { 101 return err 102 } 103 104 i := containerd.NewImageWithPlatform(client, img, platforms.Only(p)) 105 if err := i.Unpack(ctx, snapshotter); err != nil { 106 return errors.Wrap(err, "error unpacking image") 107 } 108 109 diffIDs, err := i.RootFS(ctx) 110 if err != nil { 111 return err 112 } 113 chainID := identity.ChainID(diffIDs).String() 114 fmt.Println(chainID) 115 116 s := client.SnapshotService(snapshotter) 117 118 var mounts []mount.Mount 119 if context.Bool("rw") { 120 mounts, err = s.Prepare(ctx, target, chainID) 121 } else { 122 mounts, err = s.View(ctx, target, chainID) 123 } 124 if err != nil { 125 if errdefs.IsAlreadyExists(err) { 126 mounts, err = s.Mounts(ctx, target) 127 } 128 if err != nil { 129 return err 130 } 131 } 132 133 if err := mount.All(mounts, target); err != nil { 134 if err := s.Remove(ctx, target); err != nil && !errdefs.IsNotFound(err) { 135 fmt.Fprintln(context.App.ErrWriter, "Error cleaning up snapshot after mount error:", err) 136 } 137 return err 138 } 139 140 fmt.Fprintln(context.App.Writer, target) 141 return nil 142 }, 143 }