github.com/demonoid81/containerd@v1.3.4/cmd/ctr/commands/run/run_unix.go (about) 1 // +build !windows 2 3 /* 4 Copyright The containerd Authors. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 */ 18 19 package run 20 21 import ( 22 gocontext "context" 23 "path/filepath" 24 "strings" 25 26 "github.com/containerd/containerd" 27 "github.com/containerd/containerd/cmd/ctr/commands" 28 "github.com/containerd/containerd/contrib/nvidia" 29 "github.com/containerd/containerd/contrib/seccomp" 30 "github.com/containerd/containerd/oci" 31 "github.com/containerd/containerd/platforms" 32 "github.com/opencontainers/runtime-spec/specs-go" 33 "github.com/pkg/errors" 34 "github.com/urfave/cli" 35 ) 36 37 var platformRunFlags []cli.Flag 38 39 // NewContainer creates a new container 40 func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) { 41 var ( 42 id string 43 config = context.IsSet("config") 44 ) 45 if config { 46 id = context.Args().First() 47 } else { 48 id = context.Args().Get(1) 49 } 50 51 var ( 52 opts []oci.SpecOpts 53 cOpts []containerd.NewContainerOpts 54 spec containerd.NewContainerOpts 55 ) 56 57 cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label")))) 58 if config { 59 opts = append(opts, oci.WithSpecFromFile(context.String("config"))) 60 } else { 61 var ( 62 ref = context.Args().First() 63 //for container's id is Args[1] 64 args = context.Args()[2:] 65 ) 66 opts = append(opts, oci.WithDefaultSpec(), oci.WithDefaultUnixDevices) 67 if ef := context.String("env-file"); ef != "" { 68 opts = append(opts, oci.WithEnvFile(ef)) 69 } 70 opts = append(opts, oci.WithEnv(context.StringSlice("env"))) 71 opts = append(opts, withMounts(context)) 72 73 if context.Bool("rootfs") { 74 rootfs, err := filepath.Abs(ref) 75 if err != nil { 76 return nil, err 77 } 78 opts = append(opts, oci.WithRootFSPath(rootfs)) 79 } else { 80 snapshotter := context.String("snapshotter") 81 var image containerd.Image 82 i, err := client.ImageService().Get(ctx, ref) 83 if err != nil { 84 return nil, err 85 } 86 if ps := context.String("platform"); ps != "" { 87 platform, err := platforms.Parse(ps) 88 if err != nil { 89 return nil, err 90 } 91 image = containerd.NewImageWithPlatform(client, i, platforms.Only(platform)) 92 } else { 93 image = containerd.NewImage(client, i) 94 } 95 96 unpacked, err := image.IsUnpacked(ctx, snapshotter) 97 if err != nil { 98 return nil, err 99 } 100 if !unpacked { 101 if err := image.Unpack(ctx, snapshotter); err != nil { 102 return nil, err 103 } 104 } 105 opts = append(opts, oci.WithImageConfig(image)) 106 cOpts = append(cOpts, 107 containerd.WithImage(image), 108 containerd.WithSnapshotter(snapshotter), 109 // Even when "readonly" is set, we don't use KindView snapshot here. (#1495) 110 // We pass writable snapshot to the OCI runtime, and the runtime remounts it as read-only, 111 // after creating some mount points on demand. 112 containerd.WithNewSnapshot(id, image), 113 containerd.WithImageStopSignal(image, "SIGTERM")) 114 } 115 if context.Bool("readonly") { 116 opts = append(opts, oci.WithRootFSReadonly()) 117 } 118 if len(args) > 0 { 119 opts = append(opts, oci.WithProcessArgs(args...)) 120 } 121 if cwd := context.String("cwd"); cwd != "" { 122 opts = append(opts, oci.WithProcessCwd(cwd)) 123 } 124 if context.Bool("tty") { 125 opts = append(opts, oci.WithTTY) 126 } 127 if context.Bool("privileged") { 128 opts = append(opts, oci.WithPrivileged) 129 } 130 if context.Bool("net-host") { 131 opts = append(opts, oci.WithHostNamespace(specs.NetworkNamespace), oci.WithHostHostsFile, oci.WithHostResolvconf) 132 } 133 if context.Bool("seccomp") { 134 opts = append(opts, seccomp.WithDefaultProfile()) 135 } 136 137 joinNs := context.StringSlice("with-ns") 138 for _, ns := range joinNs { 139 parts := strings.Split(ns, ":") 140 if len(parts) != 2 { 141 return nil, errors.New("joining a Linux namespace using --with-ns requires the format 'nstype:path'") 142 } 143 if !validNamespace(parts[0]) { 144 return nil, errors.New("the Linux namespace type specified in --with-ns is not valid: " + parts[0]) 145 } 146 opts = append(opts, oci.WithLinuxNamespace(specs.LinuxNamespace{ 147 Type: specs.LinuxNamespaceType(parts[0]), 148 Path: parts[1], 149 })) 150 } 151 if context.IsSet("gpus") { 152 opts = append(opts, nvidia.WithGPUs(nvidia.WithDevices(context.Int("gpus")), nvidia.WithAllCapabilities)) 153 } 154 if context.IsSet("allow-new-privs") { 155 opts = append(opts, oci.WithNewPrivileges) 156 } 157 if context.IsSet("cgroup") { 158 // NOTE: can be set to "" explicitly for disabling cgroup. 159 opts = append(opts, oci.WithCgroup(context.String("cgroup"))) 160 } 161 limit := context.Uint64("memory-limit") 162 if limit != 0 { 163 opts = append(opts, oci.WithMemoryLimit(limit)) 164 } 165 for _, dev := range context.StringSlice("device") { 166 opts = append(opts, oci.WithLinuxDevice(dev, "rwm")) 167 } 168 } 169 170 cOpts = append(cOpts, containerd.WithRuntime(context.String("runtime"), nil)) 171 172 opts = append(opts, oci.WithAnnotations(commands.LabelArgs(context.StringSlice("label")))) 173 var s specs.Spec 174 spec = containerd.WithSpec(&s, opts...) 175 176 cOpts = append(cOpts, spec) 177 178 // oci.WithImageConfig (WithUsername, WithUserID) depends on access to rootfs for resolving via 179 // the /etc/{passwd,group} files. So cOpts needs to have precedence over opts. 180 return client.NewContainer(ctx, id, cOpts...) 181 } 182 183 func getNewTaskOpts(context *cli.Context) []containerd.NewTaskOpts { 184 if context.Bool("no-pivot") { 185 return []containerd.NewTaskOpts{containerd.WithNoPivotRoot} 186 } 187 return nil 188 } 189 190 func validNamespace(ns string) bool { 191 linuxNs := specs.LinuxNamespaceType(ns) 192 switch linuxNs { 193 case specs.PIDNamespace, 194 specs.NetworkNamespace, 195 specs.UTSNamespace, 196 specs.MountNamespace, 197 specs.UserNamespace, 198 specs.IPCNamespace, 199 specs.CgroupNamespace: 200 return true 201 default: 202 return false 203 } 204 }