github.com/stackdocker/rkt@v0.10.1-0.20151109095037-1aa827478248/rkt/run.go (about) 1 // Copyright 2014 The rkt Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //+build linux 16 17 package main 18 19 import ( 20 "fmt" 21 "strconv" 22 "strings" 23 24 "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types" 25 "github.com/coreos/rkt/Godeps/_workspace/src/github.com/spf13/cobra" 26 "github.com/coreos/rkt/common" 27 "github.com/coreos/rkt/pkg/label" 28 "github.com/coreos/rkt/pkg/lock" 29 "github.com/coreos/rkt/pkg/uid" 30 "github.com/coreos/rkt/stage0" 31 "github.com/coreos/rkt/store" 32 ) 33 34 var ( 35 cmdRun = &cobra.Command{ 36 Use: "run [--volume=name,kind=host,...] [--mount volume=VOL,target=PATH] IMAGE [-- image-args...[---]]...", 37 Short: "Run image(s) in a pod in rkt", 38 Long: `IMAGE should be a string referencing an image; either a hash, local file on disk, or URL. 39 They will be checked in that order and the first match will be used. 40 41 Volumes are made available to the container via --volume. 42 Mounts bind volumes into each image's root within the container via --mount. 43 --mount is position-sensitive; occuring before any images applies to all images, 44 occuring after any images applies only to the nearest preceding image. Per-app 45 mounts take precedence over global ones if they have the same path. 46 47 An "--" may be used to inhibit rkt run's parsing of subsequent arguments, 48 which will instead be appended to the preceding image app's exec arguments. 49 End the image arguments with a lone "---" to resume argument parsing.`, 50 Run: runWrapper(runRun), 51 } 52 flagPorts portList 53 flagNet common.NetList 54 flagPrivateUsers bool 55 flagInheritEnv bool 56 flagExplicitEnv envMap 57 flagInteractive bool 58 flagNoOverlay bool 59 flagStoreOnly bool 60 flagNoStore bool 61 flagPodManifest string 62 flagMDSRegister bool 63 flagUUIDFileSave string 64 ) 65 66 func init() { 67 cmdRkt.AddCommand(cmdRun) 68 69 addStage1ImageFlag(cmdRun.Flags()) 70 cmdRun.Flags().Var(&flagPorts, "port", "ports to expose on the host (requires --net)") 71 cmdRun.Flags().Var(&flagNet, "net", "configure the pod's networking and optionally pass a list of user-configured networks to load and arguments to pass to them. syntax: --net[=n[:args], ...]") 72 cmdRun.Flags().Lookup("net").NoOptDefVal = "default" 73 cmdRun.Flags().BoolVar(&flagInheritEnv, "inherit-env", false, "inherit all environment variables not set by apps") 74 cmdRun.Flags().BoolVar(&flagNoOverlay, "no-overlay", false, "disable overlay filesystem") 75 cmdRun.Flags().BoolVar(&flagPrivateUsers, "private-users", false, "Run within user namespaces (experimental).") 76 cmdRun.Flags().Var(&flagExplicitEnv, "set-env", "an environment variable to set for apps in the form name=value") 77 cmdRun.Flags().BoolVar(&flagInteractive, "interactive", false, "run pod interactively. If true, only one image may be supplied.") 78 cmdRun.Flags().BoolVar(&flagStoreOnly, "store-only", false, "use only available images in the store (do not discover or download from remote URLs)") 79 cmdRun.Flags().BoolVar(&flagNoStore, "no-store", false, "fetch images ignoring the local store") 80 cmdRun.Flags().StringVar(&flagPodManifest, "pod-manifest", "", "the path to the pod manifest. If it's non-empty, then only '--net', '--no-overlay' and '--interactive' will have effects") 81 cmdRun.Flags().BoolVar(&flagMDSRegister, "mds-register", false, "register pod with metadata service. needs network connectivity to the host (--net=(default|default-restricted|host)") 82 cmdRun.Flags().StringVar(&flagUUIDFileSave, "uuid-file-save", "", "write out pod UUID to specified file") 83 cmdRun.Flags().Var((*appsVolume)(&rktApps), "volume", "volumes to make available in the pod") 84 85 // per-app flags 86 cmdRun.Flags().Var((*appAsc)(&rktApps), "signature", "local signature file to use in validating the preceding image") 87 cmdRun.Flags().Var((*appExec)(&rktApps), "exec", "override the exec command for the preceding image") 88 cmdRun.Flags().Var((*appMount)(&rktApps), "mount", "mount point binding a volume to a path within an app") 89 90 flagPorts = portList{} 91 92 // Disable interspersed flags to stop parsing after the first non flag 93 // argument. All the subsequent parsing will be done by parseApps. 94 // This is needed to correctly handle image args 95 cmdRun.Flags().SetInterspersed(false) 96 } 97 98 func runRun(cmd *cobra.Command, args []string) (exit int) { 99 privateUsers := uid.NewBlankUidRange() 100 err := parseApps(&rktApps, args, cmd.Flags(), true) 101 if err != nil { 102 stderr("run: error parsing app image arguments: %v", err) 103 return 1 104 } 105 106 if flagStoreOnly && flagNoStore { 107 stderr("both --store-only and --no-store specified") 108 return 1 109 } 110 111 if flagPrivateUsers { 112 if !common.SupportsUserNS() { 113 stderr("run: --private-users is not supported, kernel compiled without user namespace support") 114 return 1 115 } 116 privateUsers.SetRandomUidRange(uid.DefaultRangeCount) 117 } 118 119 if len(flagPorts) > 0 && flagNet.None() { 120 stderr("--port flag does not work with 'none' networking") 121 return 1 122 } 123 if len(flagPorts) > 0 && flagNet.Host() { 124 stderr("--port flag does not work with 'host' networking") 125 return 1 126 } 127 128 if flagMDSRegister && flagNet.None() { 129 stderr("--mds-register flag does not work with --net=none. Please use 'host', 'default' or an equivalent network") 130 return 1 131 } 132 133 if len(flagPodManifest) > 0 && (len(flagPorts) > 0 || flagInheritEnv || !flagExplicitEnv.IsEmpty() || rktApps.Count() > 0 || flagStoreOnly || flagNoStore) { 134 stderr("conflicting flags set with --pod-manifest (see --help)") 135 return 1 136 } 137 138 if flagInteractive && rktApps.Count() > 1 { 139 stderr("run: interactive option only supports one image") 140 return 1 141 } 142 143 if rktApps.Count() < 1 && len(flagPodManifest) == 0 { 144 stderr("run: must provide at least one image or specify the pod manifest") 145 return 1 146 } 147 148 s, err := store.NewStore(globalFlags.Dir) 149 if err != nil { 150 stderr("run: cannot open store: %v", err) 151 return 1 152 } 153 154 config, err := getConfig() 155 if err != nil { 156 stderr("run: cannot get configuration: %v", err) 157 return 1 158 } 159 fn := &finder{ 160 imageActionData: imageActionData{ 161 s: s, 162 headers: config.AuthPerHost, 163 dockerAuth: config.DockerCredentialsPerRegistry, 164 insecureSkipVerify: globalFlags.InsecureSkipVerify, 165 debug: globalFlags.Debug, 166 }, 167 storeOnly: flagStoreOnly, 168 noStore: flagNoStore, 169 withDeps: false, 170 } 171 172 s1img, err := getStage1Hash(s, cmd) 173 if err != nil { 174 stderr("run: %v", err) 175 return 1 176 } 177 178 fn.ks = getKeystore() 179 fn.withDeps = true 180 if err := fn.findImages(&rktApps); err != nil { 181 stderr("run: %v", err) 182 return 1 183 } 184 185 p, err := newPod() 186 if err != nil { 187 stderr("Error creating new pod: %v", err) 188 return 1 189 } 190 191 // if requested, write out pod UUID early so "rkt rm" can 192 // clean it up even if something goes wrong 193 if flagUUIDFileSave != "" { 194 if err := writeUUIDToFile(p.uuid, flagUUIDFileSave); err != nil { 195 stderr("Error saving pod UUID to file: %v", err) 196 return 1 197 } 198 } 199 200 processLabel, mountLabel, err := label.InitLabels(nil) 201 if err != nil { 202 stderr("Error initialising SELinux: %v", err) 203 return 1 204 } 205 206 cfg := stage0.CommonConfig{ 207 MountLabel: mountLabel, 208 ProcessLabel: processLabel, 209 Store: s, 210 Stage1Image: *s1img, 211 UUID: p.uuid, 212 Debug: globalFlags.Debug, 213 } 214 215 pcfg := stage0.PrepareConfig{ 216 CommonConfig: cfg, 217 UseOverlay: !flagNoOverlay && common.SupportsOverlay(), 218 PrivateUsers: privateUsers, 219 } 220 221 if len(flagPodManifest) > 0 { 222 pcfg.PodManifest = flagPodManifest 223 } else { 224 pcfg.Ports = []types.ExposedPort(flagPorts) 225 pcfg.InheritEnv = flagInheritEnv 226 pcfg.ExplicitEnv = flagExplicitEnv.Strings() 227 pcfg.Apps = &rktApps 228 } 229 230 if globalFlags.Debug { 231 stage0.InitDebug() 232 } 233 234 keyLock, err := lock.SharedKeyLock(lockDir(), common.PrepareLock) 235 if err != nil { 236 stderr("rkt: cannot get shared prepare lock: %v", err) 237 return 1 238 } 239 err = stage0.Prepare(pcfg, p.path(), p.uuid) 240 if err != nil { 241 stderr("run: error setting up stage0: %v", err) 242 keyLock.Close() 243 return 1 244 } 245 keyLock.Close() 246 247 // get the lock fd for run 248 lfd, err := p.Fd() 249 if err != nil { 250 stderr("Error getting pod lock fd: %v", err) 251 return 1 252 } 253 254 // skip prepared by jumping directly to run, we own this pod 255 if err := p.xToRun(); err != nil { 256 stderr("run: unable to transition to run: %v", err) 257 return 1 258 } 259 260 rktgid, err := common.LookupGid(common.RktGroup) 261 if err != nil { 262 stderr("run: group %q not found, will use default gid when rendering images", common.RktGroup) 263 rktgid = -1 264 } 265 266 rcfg := stage0.RunConfig{ 267 CommonConfig: cfg, 268 Net: flagNet, 269 LockFd: lfd, 270 Interactive: flagInteractive, 271 MDSRegister: flagMDSRegister, 272 LocalConfig: globalFlags.LocalConfigDir, 273 RktGid: rktgid, 274 } 275 276 apps, err := p.getApps() 277 if err != nil { 278 stderr("run: cannot get the appList in the pod manifest: %v", err) 279 return 1 280 } 281 rcfg.Apps = apps 282 stage0.Run(rcfg, p.path(), globalFlags.Dir) // execs, never returns 283 284 return 1 285 } 286 287 // portList implements the flag.Value interface to contain a set of mappings 288 // from port name --> host port 289 type portList []types.ExposedPort 290 291 func (pl *portList) Set(s string) error { 292 parts := strings.SplitN(s, ":", 2) 293 if len(parts) != 2 { 294 return fmt.Errorf("%q is not in name:port format", s) 295 } 296 297 name, err := types.NewACName(parts[0]) 298 if err != nil { 299 return fmt.Errorf("%q is not a valid port name: %v", parts[0], err) 300 } 301 302 port, err := strconv.ParseUint(parts[1], 10, 16) 303 if err != nil { 304 return fmt.Errorf("%q is not a valid port number", parts[1]) 305 } 306 307 p := types.ExposedPort{ 308 Name: *name, 309 HostPort: uint(port), 310 } 311 312 *pl = append(*pl, p) 313 return nil 314 } 315 316 func (pl *portList) String() string { 317 var ps []string 318 for _, p := range []types.ExposedPort(*pl) { 319 ps = append(ps, fmt.Sprintf("%v:%v", p.Name, p.HostPort)) 320 } 321 return strings.Join(ps, " ") 322 } 323 324 func (pl *portList) Type() string { 325 return "portList" 326 } 327 328 // envMap implements the flag.Value interface to contain a set of name=value mappings 329 type envMap struct { 330 mapping map[string]string 331 } 332 333 func (e *envMap) Set(s string) error { 334 if e.mapping == nil { 335 e.mapping = make(map[string]string) 336 } 337 pair := strings.SplitN(s, "=", 2) 338 if len(pair) != 2 { 339 return fmt.Errorf("environment variable must be specified as name=value") 340 } 341 if _, exists := e.mapping[pair[0]]; exists { 342 return fmt.Errorf("environment variable %q already set", pair[0]) 343 } 344 e.mapping[pair[0]] = pair[1] 345 return nil 346 } 347 348 func (e *envMap) IsEmpty() bool { 349 return len(e.mapping) == 0 350 } 351 352 func (e *envMap) String() string { 353 return strings.Join(e.Strings(), "\n") 354 } 355 356 func (e *envMap) Strings() []string { 357 var env []string 358 for n, v := range e.mapping { 359 env = append(env, n+"="+v) 360 } 361 return env 362 } 363 364 func (e *envMap) Type() string { 365 return "envMap" 366 }