Beispiel #1
0
// getAppName returns the app name to enter
// If one was supplied in the flags then it's simply returned
// If the PM contains a single app, that app's name is returned
// If the PM has multiple apps, the names are printed and an error is returned
func getAppName(p *pod) (*types.ACName, error) {
	if flagAppName != "" {
		return types.NewACName(flagAppName)
	}

	// figure out the app name, or show a list if multiple are present
	b, err := ioutil.ReadFile(common.PodManifestPath(p.path()))
	if err != nil {
		return nil, errwrap.Wrap(errors.New("error reading pod manifest"), err)
	}

	m := schema.PodManifest{}
	if err = m.UnmarshalJSON(b); err != nil {
		return nil, errwrap.Wrap(errors.New("invalid pod manifest"), err)
	}

	switch len(m.Apps) {
	case 0:
		return nil, fmt.Errorf("pod contains zero apps")
	case 1:
		return &m.Apps[0].Name, nil
	default:
	}

	stderr.Print("pod contains multiple apps:")
	for _, ra := range m.Apps {
		stderr.Printf("\t%v", ra.Name)
	}

	return nil, fmt.Errorf("specify app using \"rkt enter --app= ...\"")
}
Beispiel #2
0
func WritePodManifest(im *schema.PodManifest, targetFile string) {
	buff, err := im.MarshalJSON()
	if err != nil {
		log.Get().Panic(err)
	}
	err = ioutil.WriteFile(targetFile, []byte(buff), 0644)
	if err != nil {
		log.Get().Panic(err)
	}
}
Beispiel #3
0
Datei: app.go Projekt: nhlfr/rkt
func removeAppFromPodManifest(pm *schema.PodManifest, appName *types.ACName) {
	for i, app := range pm.Apps {
		if app.Name == *appName {
			pm.Apps = append(pm.Apps[:i], pm.Apps[i+1:]...)
		}
	}
}
Beispiel #4
0
func ParseApps(pm *schema.PodManifest, args []string) error {
	for len(args) > 0 {
		if rest, rtapp, err := parseApp(args); err != nil {
			return err
		} else {
			pm.Apps = append(pm.Apps, *rtapp)
			args = rest
		}
	}
	return nil
}
Beispiel #5
0
func (h *Host) ReifyPodManifest(pm *schema.PodManifest) (*schema.PodManifest, error) {
	for i, rtapp := range pm.Apps {
		img, err := h.getRuntimeImage(rtapp.Image)
		if err != nil {
			return nil, err
		}

		pm.Apps[i].Image.ID = *img.Hash

		app := rtapp.App
		if app == nil {
			app = img.Manifest.App
		}
		if app == nil {
			if len(rtapp.Mounts) > 0 {
				return nil, errors.New("No app (is it valid at all?), yet mounts given")
			}
			continue
		}

	mntpnts:
		for _, mntpnt := range app.MountPoints {
			var mnt *schema.Mount
			for _, mntc := range rtapp.Mounts {
				if mntc.Path == mntpnt.Path || mntc.Path == mntpnt.Name.String() {
					if mnt != nil {
						fmt.Printf("WARNING: multiple mounts for %v:%v, using first one")
					} else {
						mnt = &mntc
					}
				}
			}
			if mnt == nil {
				fmt.Printf("INFO: mount for %v:%v not found, inserting mount for volume %v\n", rtapp.Name, mntpnt.Name, mntpnt.Name)
				mnt = &schema.Mount{Path: mntpnt.Name.String(), Volume: mntpnt.Name}
				pm.Apps[i].Mounts = append(pm.Apps[i].Mounts, *mnt)
			}
			for _, vol := range pm.Volumes {
				if vol.Name == mnt.Volume {
					continue mntpnts
				}
			}
			fmt.Printf("INFO: volume %v not found, inserting empty volume\n", mnt.Volume)
			_mode := "0755"
			_ugid := 0
			pm.Volumes = append(pm.Volumes, types.Volume{Name: mnt.Volume, Kind: "empty", Mode: &_mode, UID: &_ugid, GID: &_ugid})
		}
	}

	return pm, nil
}
Beispiel #6
0
func runList(cmd *cobra.Command, args []string) int {
	var errors []error
	tabBuffer := new(bytes.Buffer)
	tabOut := getTabOutWithWriter(tabBuffer)

	if !flagNoLegend {
		if flagFullOutput {
			fmt.Fprintf(tabOut, "UUID\tAPP\tIMAGE NAME\tIMAGE ID\tSTATE\tCREATED\tSTARTED\tNETWORKS\n")
		} else {
			fmt.Fprintf(tabOut, "UUID\tAPP\tIMAGE NAME\tSTATE\tCREATED\tSTARTED\tNETWORKS\n")
		}
	}

	if err := walkPods(includeMostDirs, func(p *pod) {
		pm := schema.PodManifest{}

		if !p.isPreparing && !p.isAbortedPrepare && !p.isExitedDeleting {
			// TODO(vc): we should really hold a shared lock here to prevent gc of the pod
			pmf, err := p.readFile(common.PodManifestPath(""))
			if err != nil {
				errors = append(errors, newPodListReadError(p, err))
				return
			}

			if err := pm.UnmarshalJSON(pmf); err != nil {
				errors = append(errors, newPodListLoadError(p, err, pmf))
				return
			}

			if len(pm.Apps) == 0 {
				errors = append(errors, newPodListZeroAppsError(p))
				return
			}
		}

		type printedApp struct {
			uuid    string
			appName string
			imgName string
			imgID   string
			state   string
			nets    string
			created string
			started string
		}

		var appsToPrint []printedApp
		uuid := p.uuid.String()
		state := p.getState()
		nets := fmtNets(p.nets)

		created, err := p.getCreationTime()
		if err != nil {
			errors = append(errors, errwrap.Wrap(fmt.Errorf("unable to get creation time for pod %q", uuid), err))
		}
		var createdStr string
		if flagFullOutput {
			createdStr = created.Format(defaultTimeLayout)
		} else {
			createdStr = humanize.Time(created)
		}

		started, err := p.getStartTime()
		if err != nil {
			errors = append(errors, errwrap.Wrap(fmt.Errorf("unable to get start time for pod %q", uuid), err))
		}
		var startedStr string
		if !started.IsZero() {
			if flagFullOutput {
				startedStr = started.Format(defaultTimeLayout)
			} else {
				startedStr = humanize.Time(started)
			}
		}

		if !flagFullOutput {
			uuid = uuid[:8]
		}
		for _, app := range pm.Apps {
			imageName, err := getImageName(p, app.Name)
			if err != nil {
				errors = append(errors, newPodListLoadImageManifestError(p, err))
				imageName = "--"
			}

			var imageID string
			if flagFullOutput {
				imageID = app.Image.ID.String()[:19]
			}

			appsToPrint = append(appsToPrint, printedApp{
				uuid:    uuid,
				appName: app.Name.String(),
				imgName: imageName,
				imgID:   imageID,
				state:   state,
				nets:    nets,
				created: createdStr,
				started: startedStr,
			})
			// clear those variables so they won't be
			// printed for another apps in the pod as they
			// are actually describing a pod, not an app
			uuid = ""
			state = ""
			nets = ""
			createdStr = ""
			startedStr = ""
		}
		// if we reached that point, then it means that the
		// pod and all its apps are valid, so they can be
		// printed
		for _, app := range appsToPrint {
			if flagFullOutput {
				fmt.Fprintf(tabOut, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", app.uuid, app.appName, app.imgName, app.imgID, app.state, app.created, app.started, app.nets)
			} else {
				fmt.Fprintf(tabOut, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", app.uuid, app.appName, app.imgName, app.state, app.created, app.started, app.nets)
			}
		}

	}); err != nil {
		stderr.PrintE("failed to get pod handles", err)
		return 1
	}

	if len(errors) > 0 {
		sep := "----------------------------------------"
		stderr.Printf("%d error(s) encountered when listing pods:", len(errors))
		stderr.Print(sep)
		for _, err := range errors {
			stderr.Error(err)
			stderr.Print(sep)
		}
		stderr.Print("misc:")
		stderr.Printf("  rkt's appc version: %s", schema.AppContainerVersion)
		stderr.Print(sep)
		// make a visible break between errors and the listing
		stderr.Print("")
	}

	tabOut.Flush()
	stdout.Print(tabBuffer)
	return 0
}
Beispiel #7
0
func (r *Runtime) newAppcRuntimeApp(pod *api.Pod, c api.Container, pullSecrets []api.Secret, manifest *appcschema.PodManifest) error {
	if err, _ := r.imagePuller.PullImage(pod, &c, pullSecrets); err != nil {
		return nil
	}
	imgManifest, err := r.getImageManifest(c.Image)
	if err != nil {
		return err
	}

	if imgManifest.App == nil {
		imgManifest.App = new(appctypes.App)
	}

	imageID, err := r.getImageID(c.Image)
	if err != nil {
		return err
	}
	hash, err := appctypes.NewHash(imageID)
	if err != nil {
		return err
	}

	opts, err := r.runtimeHelper.GenerateRunContainerOptions(pod, &c)
	if err != nil {
		return err
	}

	// create the container log file and make a mount pair.
	mnt, err := makeContainerLogMount(opts, &c)
	if err != nil {
		return err
	}

	ctx := securitycontext.DetermineEffectiveSecurityContext(pod, &c)
	if err := setApp(imgManifest.App, &c, opts, ctx, pod.Spec.SecurityContext); err != nil {
		return err
	}

	ra := appcschema.RuntimeApp{
		Name:  convertToACName(c.Name),
		Image: appcschema.RuntimeImage{ID: *hash},
		App:   imgManifest.App,
		Annotations: []appctypes.Annotation{
			{
				Name:  *appctypes.MustACIdentifier(k8sRktContainerHashAnno),
				Value: strconv.FormatUint(kubecontainer.HashContainer(&c), 10),
			},
		},
	}

	if mnt != nil {
		ra.Annotations = append(ra.Annotations, appctypes.Annotation{
			Name:  *appctypes.MustACIdentifier(k8sRktTerminationMessagePathAnno),
			Value: mnt.HostPath,
		})

		manifest.Volumes = append(manifest.Volumes, appctypes.Volume{
			Name:   convertToACName(mnt.Name),
			Kind:   "host",
			Source: mnt.HostPath,
		})
	}

	manifest.Apps = append(manifest.Apps, ra)

	// Set global ports.
	for _, port := range opts.PortMappings {
		manifest.Ports = append(manifest.Ports, appctypes.ExposedPort{
			Name:     convertToACName(port.Name),
			HostPort: uint(port.HostPort),
		})
	}

	return nil
}
Beispiel #8
0
// generatePodManifest creates the pod manifest from the command line input.
// It returns the pod manifest as []byte on success.
// This is invoked if no pod manifest is specified at the command line.
func generatePodManifest(cfg PrepareConfig, dir string) ([]byte, error) {
	pm := schema.PodManifest{
		ACKind: "PodManifest",
		Apps:   make(schema.AppList, 0),
	}

	v, err := types.NewSemVer(version.Version)
	if err != nil {
		return nil, errwrap.Wrap(errors.New("error creating version"), err)
	}
	pm.ACVersion = *v

	if err := cfg.Apps.Walk(func(app *apps.App) error {
		img := app.ImageID

		am, err := cfg.Store.GetImageManifest(img.String())
		if err != nil {
			return errwrap.Wrap(errors.New("error getting the manifest"), err)
		}
		appName, err := imageNameToAppName(am.Name)
		if err != nil {
			return errwrap.Wrap(errors.New("error converting image name to app name"), err)
		}
		if err := prepareAppImage(cfg, *appName, img, dir, cfg.UseOverlay); err != nil {
			return errwrap.Wrap(fmt.Errorf("error setting up image %s", img), err)
		}
		if pm.Apps.Get(*appName) != nil {
			return fmt.Errorf("error: multiple apps with name %s", am.Name)
		}
		if am.App == nil && app.Exec == "" {
			return fmt.Errorf("error: image %s has no app section and --exec argument is not provided", img)
		}
		ra := schema.RuntimeApp{
			// TODO(vc): leverage RuntimeApp.Name for disambiguating the apps
			Name: *appName,
			App:  am.App,
			Image: schema.RuntimeImage{
				Name:   &am.Name,
				ID:     img,
				Labels: am.Labels,
			},
			Annotations: am.Annotations,
			Mounts:      MergeMounts(cfg.Apps.Mounts, app.Mounts),
		}

		if execOverride := app.Exec; execOverride != "" {
			// Create a minimal App section if not present
			if am.App == nil {
				ra.App = &types.App{
					User:  strconv.Itoa(os.Getuid()),
					Group: strconv.Itoa(os.Getgid()),
				}
			}
			ra.App.Exec = []string{execOverride}
		}

		if execAppends := app.Args; execAppends != nil {
			ra.App.Exec = append(ra.App.Exec, execAppends...)
		}

		if memoryOverride := app.MemoryLimit; memoryOverride != nil {
			isolator := memoryOverride.AsIsolator()
			ra.App.Isolators = append(ra.App.Isolators, isolator)
		}

		if cpuOverride := app.CPULimit; cpuOverride != nil {
			isolator := cpuOverride.AsIsolator()
			ra.App.Isolators = append(ra.App.Isolators, isolator)
		}

		if cfg.InheritEnv || len(cfg.ExplicitEnv) > 0 {
			MergeEnvs(&ra.App.Environment, cfg.InheritEnv, cfg.ExplicitEnv)
		}
		pm.Apps = append(pm.Apps, ra)
		return nil
	}); err != nil {
		return nil, err
	}

	// TODO(jonboulle): check that app mountpoint expectations are
	// satisfied here, rather than waiting for stage1
	pm.Volumes = cfg.Apps.Volumes
	pm.Ports = cfg.Ports

	pmb, err := json.Marshal(pm)
	if err != nil {
		return nil, errwrap.Wrap(errors.New("error marshalling pod manifest"), err)
	}
	return pmb, nil
}
Beispiel #9
0
func BasicPodManifest() *schema.PodManifest {
	im := new(schema.PodManifest)
	im.UnmarshalJSON([]byte(POD_MANIFEST))
	return im
}
Beispiel #10
0
// generatePodManifest creates the pod manifest from the command line input.
// It returns the pod manifest as []byte on success.
// This is invoked if no pod manifest is specified at the command line.
func generatePodManifest(cfg PrepareConfig, dir string) ([]byte, error) {
	pm := schema.PodManifest{
		ACKind: "PodManifest",
		Apps:   make(schema.AppList, 0),
	}

	v, err := types.NewSemVer(version.Version)
	if err != nil {
		return nil, errwrap.Wrap(errors.New("error creating version"), err)
	}
	pm.ACVersion = *v

	if err := cfg.Apps.Walk(func(app *apps.App) error {
		img := app.ImageID

		am, err := cfg.Store.GetImageManifest(img.String())
		if err != nil {
			return errwrap.Wrap(errors.New("error getting the manifest"), err)
		}
		appName, err := imageNameToAppName(am.Name)
		if err != nil {
			return errwrap.Wrap(errors.New("error converting image name to app name"), err)
		}

		if err := prepareAppImage(cfg, *appName, img, dir, cfg.UseOverlay); err != nil {
			return errwrap.Wrap(fmt.Errorf("error setting up image %s", img), err)
		}
		if pm.Apps.Get(*appName) != nil {
			return fmt.Errorf("error: multiple apps with name %s", am.Name)
		}
		if am.App == nil && app.Exec == "" {
			return fmt.Errorf("error: image %s has no app section and --exec argument is not provided", img)
		}
		ra := schema.RuntimeApp{
			// TODO(vc): leverage RuntimeApp.Name for disambiguating the apps
			Name: *appName,
			App:  am.App,
			Image: schema.RuntimeImage{
				Name:   &am.Name,
				ID:     img,
				Labels: am.Labels,
			},
			Annotations: am.Annotations,
			Mounts:      MergeMounts(cfg.Apps.Mounts, app.Mounts),
		}

		if execOverride := app.Exec; execOverride != "" {
			// Create a minimal App section if not present
			if am.App == nil {
				ra.App = &types.App{
					User:  strconv.Itoa(os.Getuid()),
					Group: strconv.Itoa(os.Getgid()),
				}
			}
			ra.App.Exec = []string{execOverride}
		}

		if execAppends := app.Args; execAppends != nil {
			ra.App.Exec = append(ra.App.Exec, execAppends...)
		}

		if memoryOverride := app.MemoryLimit; memoryOverride != nil {
			isolator := memoryOverride.AsIsolator()
			ra.App.Isolators = append(ra.App.Isolators, isolator)
		}

		if cpuOverride := app.CPULimit; cpuOverride != nil {
			isolator := cpuOverride.AsIsolator()
			ra.App.Isolators = append(ra.App.Isolators, isolator)
		}

		if app.CapsRetain != nil && app.CapsRemove != nil {
			return fmt.Errorf("error: cannot use both --cap-retain and --cap-remove on the same image")
		}

		// Delete existing caps isolators if the user wants to override
		// them with either --cap-retain or --cap-remove
		if app.CapsRetain != nil || app.CapsRemove != nil {
			for i := len(ra.App.Isolators) - 1; i >= 0; i-- {
				isolator := ra.App.Isolators[i]
				if _, ok := isolator.Value().(types.LinuxCapabilitiesSet); ok {
					ra.App.Isolators = append(ra.App.Isolators[:i],
						ra.App.Isolators[i+1:]...)
				}
			}
		}

		if capsRetain := app.CapsRetain; capsRetain != nil {
			isolator, err := capsRetain.AsIsolator()
			if err != nil {
				return err
			}
			ra.App.Isolators = append(ra.App.Isolators, *isolator)
		} else if capsRemove := app.CapsRemove; capsRemove != nil {
			isolator, err := capsRemove.AsIsolator()
			if err != nil {
				return err
			}
			ra.App.Isolators = append(ra.App.Isolators, *isolator)
		}

		if user := app.User; user != "" {
			ra.App.User = user
		}

		if group := app.Group; group != "" {
			ra.App.Group = group
		}

		// loading the environment from the lowest priority to highest
		if cfg.InheritEnv {
			// Inherit environment does not override app image environment
			mergeEnvs(&ra.App.Environment, os.Environ(), false)
		}

		mergeEnvs(&ra.App.Environment, cfg.EnvFromFile, true)
		mergeEnvs(&ra.App.Environment, cfg.ExplicitEnv, true)
		pm.Apps = append(pm.Apps, ra)
		return nil
	}); err != nil {
		return nil, err
	}

	// TODO(jonboulle): check that app mountpoint expectations are
	// satisfied here, rather than waiting for stage1
	pm.Volumes = cfg.Apps.Volumes
	pm.Ports = cfg.Ports

	pmb, err := json.Marshal(pm)
	if err != nil {
		return nil, errwrap.Wrap(errors.New("error marshalling pod manifest"), err)
	}
	return pmb, nil
}
Beispiel #11
0
// generatePodManifest creates the pod manifest from the command line input.
// It returns the pod manifest as []byte on success.
// This is invoked if no pod manifest is specified at the command line.
func generatePodManifest(cfg PrepareConfig, dir string) ([]byte, error) {
	pm := schema.PodManifest{
		ACKind: "PodManifest",
		Apps:   make(schema.AppList, 0),
	}

	v, err := types.NewSemVer(version.Version)
	if err != nil {
		return nil, errwrap.Wrap(errors.New("error creating version"), err)
	}
	pm.ACVersion = *v

	if err := cfg.Apps.Walk(func(app *apps.App) error {
		img := app.ImageID

		am, err := cfg.Store.GetImageManifest(img.String())
		if err != nil {
			return errwrap.Wrap(errors.New("error getting the manifest"), err)
		}

		var appName *types.ACName
		if app.Name != "" {
			appName, err = types.NewACName(app.Name)
			if err != nil {
				return errwrap.Wrap(errors.New("invalid app name format"), err)
			}
		} else {
			appName, err = imageNameToAppName(am.Name)
			if err != nil {
				return errwrap.Wrap(errors.New("error converting image name to app name"), err)
			}
		}

		if _, err := prepareAppImage(cfg, *appName, img, dir, cfg.UseOverlay); err != nil {
			return errwrap.Wrap(fmt.Errorf("error preparing image %s", img), err)
		}
		if pm.Apps.Get(*appName) != nil {
			return fmt.Errorf("error: multiple apps with name %s", am.Name)
		}
		if am.App == nil && app.Exec == "" {
			return fmt.Errorf("error: image %s has no app section and --exec argument is not provided", img)
		}
		ra := schema.RuntimeApp{
			// TODO(vc): leverage RuntimeApp.Name for disambiguating the apps
			Name: *appName,
			App:  am.App,
			Image: schema.RuntimeImage{
				Name:   &am.Name,
				ID:     img,
				Labels: am.Labels,
			},
			Mounts:         MergeMounts(cfg.Apps.Mounts, app.Mounts),
			ReadOnlyRootFS: app.ReadOnlyRootFS,
		}

		if app.Exec != "" {
			// Create a minimal App section if not present
			if am.App == nil {
				ra.App = &types.App{
					User:  strconv.Itoa(os.Getuid()),
					Group: strconv.Itoa(os.Getgid()),
				}
			}
			ra.App.Exec = []string{app.Exec}
		}

		if app.Args != nil {
			ra.App.Exec = append(ra.App.Exec, app.Args...)
		}

		if app.WorkingDir != "" {
			ra.App.WorkingDirectory = app.WorkingDir
		}

		if err := prepareIsolators(app, ra.App); err != nil {
			return err
		}

		if app.User != "" {
			ra.App.User = app.User
		}

		if app.Group != "" {
			ra.App.Group = app.Group
		}

		if app.SupplementaryGIDs != nil {
			ra.App.SupplementaryGIDs = app.SupplementaryGIDs
		}

		if app.UserAnnotations != nil {
			ra.App.UserAnnotations = app.UserAnnotations
		}

		if app.UserLabels != nil {
			ra.App.UserLabels = app.UserLabels
		}

		// loading the environment from the lowest priority to highest
		if cfg.InheritEnv {
			// Inherit environment does not override app image environment
			mergeEnvs(&ra.App.Environment, os.Environ(), false)
		}

		mergeEnvs(&ra.App.Environment, cfg.EnvFromFile, true)
		mergeEnvs(&ra.App.Environment, cfg.ExplicitEnv, true)

		if app.Environments != nil {
			envs := make([]string, 0, len(app.Environments))
			for name, value := range app.Environments {
				envs = append(envs, fmt.Sprintf("%s=%s", name, value))
			}
			mergeEnvs(&ra.App.Environment, envs, true)
		}

		pm.Apps = append(pm.Apps, ra)
		return nil
	}); err != nil {
		return nil, err
	}

	// TODO(jonboulle): check that app mountpoint expectations are
	// satisfied here, rather than waiting for stage1
	pm.Volumes = cfg.Apps.Volumes

	// Check to see if ports have any errors
	pm.Ports = cfg.Ports
	if _, err := commonnet.ForwardedPorts(&pm); err != nil {
		return nil, err
	}

	pm.Annotations = append(pm.Annotations, types.Annotation{
		Name:  "coreos.com/rkt/stage1/mutable",
		Value: strconv.FormatBool(cfg.Mutable),
	})

	pm.UserAnnotations = cfg.UserAnnotations
	pm.UserLabels = cfg.UserLabels

	pmb, err := json.Marshal(pm)
	if err != nil {
		return nil, errwrap.Wrap(errors.New("error marshalling pod manifest"), err)
	}
	return pmb, nil
}
Beispiel #12
0
Datei: run.go Projekt: nhlfr/rkt
// generatePodManifest creates the pod manifest from the command line input.
// It returns the pod manifest as []byte on success.
// This is invoked if no pod manifest is specified at the command line.
func generatePodManifest(cfg PrepareConfig, dir string) ([]byte, error) {
	pm := schema.PodManifest{
		ACKind: "PodManifest",
		Apps:   make(schema.AppList, 0),
	}

	v, err := types.NewSemVer(version.Version)
	if err != nil {
		return nil, errwrap.Wrap(errors.New("error creating version"), err)
	}
	pm.ACVersion = *v

	if err := cfg.Apps.Walk(func(app *apps.App) error {
		img := app.ImageID

		am, err := cfg.Store.GetImageManifest(img.String())
		if err != nil {
			return errwrap.Wrap(errors.New("error getting the manifest"), err)
		}
		appName, err := imageNameToAppName(am.Name)
		if err != nil {
			return errwrap.Wrap(errors.New("error converting image name to app name"), err)
		}

		if err := prepareAppImage(cfg, *appName, img, dir, cfg.UseOverlay); err != nil {
			return errwrap.Wrap(fmt.Errorf("error setting up image %s", img), err)
		}
		if pm.Apps.Get(*appName) != nil {
			return fmt.Errorf("error: multiple apps with name %s", am.Name)
		}
		if am.App == nil && app.Exec == "" {
			return fmt.Errorf("error: image %s has no app section and --exec argument is not provided", img)
		}
		ra := schema.RuntimeApp{
			// TODO(vc): leverage RuntimeApp.Name for disambiguating the apps
			Name: *appName,
			App:  am.App,
			Image: schema.RuntimeImage{
				Name:   &am.Name,
				ID:     img,
				Labels: am.Labels,
			},
			Mounts: MergeMounts(cfg.Apps.Mounts, app.Mounts),
		}

		if execOverride := app.Exec; execOverride != "" {
			// Create a minimal App section if not present
			if am.App == nil {
				ra.App = &types.App{
					User:  strconv.Itoa(os.Getuid()),
					Group: strconv.Itoa(os.Getgid()),
				}
			}
			ra.App.Exec = []string{execOverride}
		}

		if execAppends := app.Args; execAppends != nil {
			ra.App.Exec = append(ra.App.Exec, execAppends...)
		}

		if err := prepareIsolators(app, ra.App); err != nil {
			return err
		}

		if user := app.User; user != "" {
			ra.App.User = user
		}

		if group := app.Group; group != "" {
			ra.App.Group = group
		}

		// loading the environment from the lowest priority to highest
		if cfg.InheritEnv {
			// Inherit environment does not override app image environment
			mergeEnvs(&ra.App.Environment, os.Environ(), false)
		}

		mergeEnvs(&ra.App.Environment, cfg.EnvFromFile, true)
		mergeEnvs(&ra.App.Environment, cfg.ExplicitEnv, true)
		pm.Apps = append(pm.Apps, ra)
		return nil
	}); err != nil {
		return nil, err
	}

	// TODO(jonboulle): check that app mountpoint expectations are
	// satisfied here, rather than waiting for stage1
	pm.Volumes = cfg.Apps.Volumes
	pm.Ports = cfg.Ports

	// TODO(sur): add to stage1-implementors-guide and to the spec
	pm.Annotations = append(pm.Annotations, types.Annotation{
		Name:  "coreos.com/rkt/stage1/mutable",
		Value: strconv.FormatBool(cfg.Mutable),
	})

	pmb, err := json.Marshal(pm)
	if err != nil {
		return nil, errwrap.Wrap(errors.New("error marshalling pod manifest"), err)
	}
	return pmb, nil
}