Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Feb 24, 2020. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 39 additions & 4 deletions common/apps/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,26 @@
package apps

import (
"fmt"

"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema"
"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types"
)

type App struct {
Image string // the image reference as supplied by the user on the cli
Args []string // any arguments the user supplied for this app
Asc string // signature file override for image verification (if fetching occurs)
Image string // the image reference as supplied by the user on the cli
Args []string // any arguments the user supplied for this app
Asc string // signature file override for image verification (if fetching occurs)
Mounts []schema.Mount // mounts for this app (superceding any mounts in rktApps.mounts of same MountPoint)

// TODO(jonboulle): These images are partially-populated hashes, this should be clarified.
ImageID types.Hash // resolved image identifier
}

type Apps struct {
apps []App
apps []App
Mounts []schema.Mount // global mounts applied to all apps
Volumes []types.Volume // volumes available to all apps
}

// Reset creates a new slice for al.apps, needed by tests
Expand All @@ -56,6 +62,35 @@ func (al *Apps) Last() *App {
return &al.apps[len(al.apps)-1]
}

// Validate validates al for things like referential integrity of mounts<->volumes.
func (al *Apps) Validate() error {
vs := map[types.ACName]struct{}{}
for _, v := range al.Volumes {
vs[v.Name] = struct{}{}
}

f := func(mnts []schema.Mount) error {
for _, m := range mnts {
_, ok := vs[m.Volume]
if !ok {
return fmt.Errorf("dangling mount point %q: volume %q not found", m.MountPoint, m.Volume)
}
}
return nil
}

if err := f(al.Mounts); err != nil {
return err
}

err := al.Walk(func(app *App) error {
return f(app.Mounts)
})

/* TODO(vc): in debug/verbose mode say something about unused volumes? */
return err
}

// Walk iterates on al.apps calling f for each app
// walking stops if f returns an error, the error is simply returned
func (al *Apps) Walk(f func(*App) error) error {
Expand Down
80 changes: 78 additions & 2 deletions rkt/cli_apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ package main
import (
"flag"
"fmt"
"net/url"
"strings"

"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema"
"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types"
"github.com/coreos/rkt/common/apps"
)

Expand Down Expand Up @@ -97,7 +101,7 @@ func parseApps(al *apps.Apps, args []string, flags *flag.FlagSet, allowAppArgs b
}
}

return nil
return al.Validate()
}

// Value interface implementations for the various per-app fields we provide flags for
Expand Down Expand Up @@ -126,4 +130,76 @@ func (al *appAsc) String() string {
return app.Asc
}

// TODO(vc): --mount, --set-env, etc.
// appMount is for --mount flags in the form of: --mount volume=VOLNAME,target=MNTNAME
type appMount apps.Apps

func (al *appMount) Set(s string) error {
mount := schema.Mount{}

// this is intentionally made similar to types.VolumeFromString()
m, err := url.ParseQuery(strings.Replace(s, ",", "&", -1))
if err != nil {
return err
}

for key, val := range m {
if len(val) > 1 {
return fmt.Errorf("label %s with multiple values %q", key, val)
}
switch key {
// FIXME(vc): ACName seems a bit restrictive for naming volumes and mountpoints...
case "volume":
mv, err := types.NewACName(val[0])
if err != nil {
return err
}
mount.Volume = *mv
case "target":
mp, err := types.NewACName(val[0])
if err != nil {
return err
}
mount.MountPoint = *mp
default:
return fmt.Errorf("unknown mount parameter %q", key)
}
}

if (*apps.Apps)(al).Count() == 0 {
(*apps.Apps)(al).Mounts = append((*apps.Apps)(al).Mounts, mount)
} else {
app := (*apps.Apps)(al).Last()
app.Mounts = append(app.Mounts, mount)
}

return nil
}

func (al *appMount) String() string {
var ms []string
for _, m := range ((*apps.Apps)(al)).Mounts {
ms = append(ms, m.Volume.String(), ":", m.MountPoint.String())
}
return strings.Join(ms, " ")
}

// appsVolume is for --volume flags in the form name,kind=host,source=/tmp,readOnly=true (defined by appc)
type appsVolume apps.Apps

func (al *appsVolume) Set(s string) error {
vol, err := types.VolumeFromString(s)
if err != nil {
return err
}

(*apps.Apps)(al).Volumes = append((*apps.Apps)(al).Volumes, *vol)
return nil
}

func (al *appsVolume) String() string {
var vs []string
for _, v := range (*apps.Apps)(al).Volumes {
vs = append(vs, v.String())
}
return strings.Join(vs, " ")
}
4 changes: 2 additions & 2 deletions rkt/prepare.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,14 @@ End the image arguments with a lone "---" to resume argument parsing.`,
func init() {
commands = append(commands, cmdPrepare)
prepareFlags.StringVar(&flagStage1Image, "stage1-image", defaultStage1Image, `image to use as stage1. Local paths and http/https URLs are supported. If empty, rkt will look for a file called "stage1.aci" in the same directory as rkt itself`)
prepareFlags.Var(&flagVolumes, "volume", "volumes to mount into the pod")
prepareFlags.Var(&flagPorts, "port", "ports to expose on the host (requires --private-net)")
prepareFlags.BoolVar(&flagQuiet, "quiet", false, "suppress superfluous output on stdout, print only the UUID on success")
prepareFlags.BoolVar(&flagInheritEnv, "inherit-env", false, "inherit all environment variables not set by apps")
prepareFlags.BoolVar(&flagNoOverlay, "no-overlay", false, "disable overlay filesystem")
prepareFlags.Var(&flagExplicitEnv, "set-env", "an environment variable to set for apps in the form name=value")
prepareFlags.BoolVar(&flagLocal, "local", false, "use only local images (do not discover or download from remote URLs)")
prepareFlags.Var((*appsVolume)(&rktApps), "volume", "volumes to make available in the pod")
prepareFlags.Var((*appMount)(&rktApps), "mount", "mount point binding a volume to a path within an app")
}

func runPrepare(args []string) (exit int) {
Expand Down Expand Up @@ -133,7 +134,6 @@ func runPrepare(args []string) (exit int) {
Stage1Image: *s1img,
UUID: p.uuid,
},
Volumes: []types.Volume(flagVolumes),
Ports: []types.ExposedPort(flagPorts),
InheritEnv: flagInheritEnv,
ExplicitEnv: flagExplicitEnv.Strings(),
Expand Down
35 changes: 8 additions & 27 deletions rkt/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,15 @@ var (
cmdRun = &Command{
Name: "run",
Summary: "Run image(s) in a pod in rkt",
Usage: "[--volume name,kind=host,...] IMAGE [-- image-args...[---]]...",
Usage: "[--volume VOL,kind=host,...] [--mount volume=VOL,target=PATH] IMAGE [-- image-args...[---]]...",
Description: `IMAGE should be a string referencing an image; either a hash, local file on disk, or URL.
They will be checked in that order and the first match will be used.

Volumes are made available to the container via --volume.
Mounts bind volumes into each image's root within the container via --mount.
--mount is position-sensitive; occuring before any images applies to all images,
occuring after any images applies only to the nearest preceding image.

An "--" may be used to inhibit rkt run's parsing of subsequent arguments,
which will instead be appended to the preceding image app's exec arguments.
End the image arguments with a lone "---" to resume argument parsing.`,
Expand All @@ -52,7 +57,6 @@ End the image arguments with a lone "---" to resume argument parsing.`,
}
runFlags flag.FlagSet
flagStage1Image string
flagVolumes volumeList
flagPorts portList
flagPrivateNet bool
flagInheritEnv bool
Expand All @@ -74,7 +78,6 @@ func init() {
}

runFlags.StringVar(&flagStage1Image, "stage1-image", defaultStage1Image, `image to use as stage1. Local paths and http/https URLs are supported. If empty, rkt will look for a file called "stage1.aci" in the same directory as rkt itself`)
runFlags.Var(&flagVolumes, "volume", "volumes to mount into the pod")
runFlags.Var(&flagPorts, "port", "ports to expose on the host (requires --private-net)")
runFlags.BoolVar(&flagPrivateNet, "private-net", false, "give pod a private network")
runFlags.BoolVar(&flagInheritEnv, "inherit-env", false, "inherit all environment variables not set by apps")
Expand All @@ -83,7 +86,8 @@ func init() {
runFlags.BoolVar(&flagInteractive, "interactive", false, "run pod interactively")
runFlags.Var((*appAsc)(&rktApps), "signature", "local signature file to use in validating the preceding image")
runFlags.BoolVar(&flagLocal, "local", false, "use only local images (do not discover or download from remote URLs)")
flagVolumes = volumeList{}
runFlags.Var((*appsVolume)(&rktApps), "volume", "volumes to make available in the pod")
runFlags.Var((*appMount)(&rktApps), "mount", "mount point binding a volume to a path within an app")
flagPorts = portList{}
}

Expand Down Expand Up @@ -167,7 +171,6 @@ func runRun(args []string) (exit int) {
pcfg := stage0.PrepareConfig{
CommonConfig: cfg,
Apps: &rktApps,
Volumes: []types.Volume(flagVolumes),
Ports: []types.ExposedPort(flagPorts),
InheritEnv: flagInheritEnv,
ExplicitEnv: flagExplicitEnv.Strings(),
Expand Down Expand Up @@ -204,28 +207,6 @@ func runRun(args []string) (exit int) {
return 1
}

// volumeList implements the flag.Value interface to contain a set of mappings
// from mount label --> mount path
type volumeList []types.Volume

func (vl *volumeList) Set(s string) error {
vol, err := types.VolumeFromString(s)
if err != nil {
return err
}

*vl = append(*vl, *vol)
return nil
}

func (vl *volumeList) String() string {
var vs []string
for _, v := range []types.Volume(*vl) {
vs = append(vs, v.String())
}
return strings.Join(vs, " ")
}

// portList implements the flag.Value interface to contain a set of mappings
// from port name --> host port
type portList []types.ExposedPort
Expand Down
12 changes: 10 additions & 2 deletions stage0/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ type PrepareConfig struct {
Apps *apps.Apps // apps to prepare
InheritEnv bool // inherit parent environment into apps
ExplicitEnv []string // always set these environment variables for all the apps
Volumes []types.Volume // list of volumes that rkt can provide to applications
Ports []types.ExposedPort // list of ports that rkt will expose on the host
UseOverlay bool // prepare pod with overlay fs
}
Expand Down Expand Up @@ -100,6 +99,14 @@ func MergeEnvs(appEnv *types.Environment, inheritEnv bool, setEnv []string) {
}
}

// MergeMounts combines the global and per-app mount slices
func MergeMounts(mounts []schema.Mount, appMounts []schema.Mount) []schema.Mount {
ml := mounts
ml = append(ml, appMounts...)
// TODO(vc): deduplicate mountpoint collisions? (prioritize appMounts?)
return ml
}

// Prepare sets up a pod based on the given config.
func Prepare(cfg PrepareConfig, dir string, uuid *types.UUID) error {
log.Printf("Preparing stage1")
Expand Down Expand Up @@ -138,6 +145,7 @@ func Prepare(cfg PrepareConfig, dir string, uuid *types.UUID) error {
ID: img,
},
Annotations: am.Annotations,
Mounts: MergeMounts(cfg.Apps.Mounts, app.Mounts),
}

if execAppends := app.Args; execAppends != nil {
Expand All @@ -159,7 +167,7 @@ func Prepare(cfg PrepareConfig, dir string, uuid *types.UUID) error {

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

cdoc, err := json.Marshal(cm)
Expand Down
18 changes: 11 additions & 7 deletions stage1/init/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,20 +292,24 @@ func (p *Pod) appToNspawnArgs(ra *schema.RuntimeApp, am *schema.ImageManifest) (

vols := make(map[types.ACName]types.Volume)

// TODO(philips): this is implicitly creating a mapping from MountPoint
// to volumes. This is a nice convenience for users but we will need to
// introduce a --mount flag so they can control which mountPoint maps to
// which volume.

// Here we bind the volumes to the mountpoints via runtime mounts (--mount)
for _, v := range p.Manifest.Volumes {
vols[v.Name] = v
}

mnts := make(map[types.ACName]types.ACName)
for _, m := range ra.Mounts {
mnts[m.MountPoint] = m.Volume
}

for _, mp := range app.MountPoints {
key := mp.Name
key, ok := mnts[mp.Name]
if !ok {
return nil, fmt.Errorf("no mount for mountpoint %q in app %q", mp.Name, name)
}
vol, ok := vols[key]
if !ok {
return nil, fmt.Errorf("no volume for mountpoint %q in app %q", key, name)
return nil, fmt.Errorf("no volume for mount %q:%q in app %q", mp.Name, key, name)
}
opt := make([]string, 4)

Expand Down
10 changes: 9 additions & 1 deletion stage1/init/pod_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,17 @@ func TestAppToNspawnArgsOverridesImageManifestReadOnly(t *testing.T) {
},
},
}
appManifest := &schema.RuntimeApp{
Mounts: []schema.Mount{
{
Volume: "foo-mount",
MountPoint: "foo-mount",
},
},
}

p := &Pod{Manifest: podManifest}
output, err := p.appToNspawnArgs(&schema.RuntimeApp{}, imageManifest)
output, err := p.appToNspawnArgs(appManifest, imageManifest)
if err != nil {
t.Errorf("#%d: unexpected error: `%v`", i, err)
}
Expand Down
10 changes: 5 additions & 5 deletions tests/rkt_volume_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ var volTests = []struct {
},
// Check that we can read files from a volume (both ro and rw)
{
`/bin/sh -c "export FILE=/dir1/file ; ../bin/rkt --debug --insecure-skip-verify run --inherit-env=true --volume=dir1,kind=host,source=$TMPDIR ./rkt-inspect-vol-rw-read-file.aci"`,
`/bin/sh -c "export FILE=/dir1/file ; ../bin/rkt --debug --insecure-skip-verify run --inherit-env=true --volume=dir1,kind=host,source=$TMPDIR --mount=volume=dir1,target=dir1 ./rkt-inspect-vol-rw-read-file.aci"`,
`<<<host>>>`,
},
{
`/bin/sh -c "export FILE=/dir1/file ; ../bin/rkt --debug --insecure-skip-verify run --inherit-env=true --volume=dir1,kind=host,source=$TMPDIR ./rkt-inspect-vol-ro-read-file.aci"`,
`/bin/sh -c "export FILE=/dir1/file ; ../bin/rkt --debug --insecure-skip-verify run --inherit-env=true --volume=dir1,kind=host,source=$TMPDIR --mount=volume=dir1,target=dir1 ./rkt-inspect-vol-ro-read-file.aci"`,
`<<<host>>>`,
},
// Check that we can write to files in the ACI
Expand All @@ -50,16 +50,16 @@ var volTests = []struct {
},
// Check that we can write files to a volume (both ro and rw)
{
`/bin/sh -c "export FILE=/dir1/file CONTENT=2 ; ../bin/rkt --debug --insecure-skip-verify run --inherit-env=true --volume=dir1,kind=host,source=$TMPDIR ./rkt-inspect-vol-rw-write-file.aci"`,
`/bin/sh -c "export FILE=/dir1/file CONTENT=2 ; ../bin/rkt --debug --insecure-skip-verify run --inherit-env=true --volume=dir1,kind=host,source=$TMPDIR --mount=volume=dir1,target=dir1 ./rkt-inspect-vol-rw-write-file.aci"`,
`<<<2>>>`,
},
{
`/bin/sh -c "export FILE=/dir1/file CONTENT=3 ; ../bin/rkt --debug --insecure-skip-verify run --inherit-env=true --volume=dir1,kind=host,source=$TMPDIR ./rkt-inspect-vol-ro-write-file.aci"`,
`/bin/sh -c "export FILE=/dir1/file CONTENT=3 ; ../bin/rkt --debug --insecure-skip-verify run --inherit-env=true --volume=dir1,kind=host,source=$TMPDIR --mount=volume=dir1,target=dir1 ./rkt-inspect-vol-ro-write-file.aci"`,
`Cannot write to file "/dir1/file": open /dir1/file: read-only file system`,
},
// Check that the volume still contain the file previously written
{
`/bin/sh -c "export FILE=/dir1/file ; ../bin/rkt --debug --insecure-skip-verify run --inherit-env=true --volume=dir1,kind=host,source=$TMPDIR ./rkt-inspect-vol-ro-read-file.aci"`,
`/bin/sh -c "export FILE=/dir1/file ; ../bin/rkt --debug --insecure-skip-verify run --inherit-env=true --volume=dir1,kind=host,source=$TMPDIR --mount=volume=dir1,target=dir1 ./rkt-inspect-vol-ro-read-file.aci"`,
`<<<2>>>`,
},
}
Expand Down