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  }