New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stage1/fly: respect runtimeApp App's MountPoints #2852

Merged
merged 4 commits into from Jul 7, 2016
Jump to file or symbol
Failed to load files and symbols.
+213 −15
Diff settings

Always

Just for now

Copy path View file
@@ -110,15 +110,24 @@ func init() {
flag.StringVar(&discardString, "local-config", common.DefaultLocalConfigDir, "Local config path")
}
func addMountPoints(namedVolumeMounts map[types.ACName]volumeMountTuple, mountpoints []types.MountPoint) error {
for _, mp := range mountpoints {
tuple, exists := namedVolumeMounts[mp.Name]
switch {

This comment has been minimized.

@tmrts

tmrts Jul 7, 2016

Contributor

Minor nit for future reference, you can use switch tuple, exists := namedVolumeMounts[mp.Name]; {

case exists && tuple.M.Path != mp.Path:
return fmt.Errorf("conflicting path information from mount and mountpoint %q", mp.Name)
case !exists:
namedVolumeMounts[mp.Name] = volumeMountTuple{M: schema.Mount{Volume: mp.Name, Path: mp.Path}}
diag.Printf("adding %+v", namedVolumeMounts[mp.Name])
}
}
return nil
}
func evaluateMounts(rfs string, app string, p *stage1commontypes.Pod) ([]flyMount, error) {
imApp := p.Images[app].App
namedVolumeMounts := map[types.ACName]volumeMountTuple{}
var manifestMPs []types.MountPoint
if imApp != nil {
manifestMPs = imApp.MountPoints
}
// Insert the PodManifest's first RuntimeApp's Mounts
for _, m := range p.Manifest.Apps[0].Mounts {
_, exists := namedVolumeMounts[m.Volume]
if exists {
@@ -129,17 +138,20 @@ func evaluateMounts(rfs string, app string, p *stage1commontypes.Pod) ([]flyMoun
}
// Merge command-line Mounts with ImageManifest's MountPoints
for _, mp := range manifestMPs {
tuple, exists := namedVolumeMounts[mp.Name]
switch {
case exists && tuple.M.Path != mp.Path:
return nil, fmt.Errorf("conflicting path information from mount and mountpoint %q", mp.Name)
case !exists:
namedVolumeMounts[mp.Name] = volumeMountTuple{M: schema.Mount{Volume: mp.Name, Path: mp.Path}}
diag.Printf("adding %+v", namedVolumeMounts[mp.Name])
var imAppManifestMPs []types.MountPoint
if imApp := p.Images[app].App; imApp != nil {
imAppManifestMPs = imApp.MountPoints
if err := addMountPoints(namedVolumeMounts, imAppManifestMPs); err != nil {
return nil, err
}
}
// Merge command-line Mounts with PodManifest's RuntimeApp's App's MountPoints
raApp := p.Manifest.Apps[0]
if err := addMountPoints(namedVolumeMounts, raApp.App.MountPoints); err != nil {
return nil, err
}
// Insert the command-line Volumes
for _, v := range p.Manifest.Volumes {
// Check if we have a mount for this volume
@@ -155,7 +167,7 @@ func evaluateMounts(rfs string, app string, p *stage1commontypes.Pod) ([]flyMoun
}
// Merge command-line Volumes with ImageManifest's MountPoints
for _, mp := range manifestMPs {
for _, mp := range imAppManifestMPs {
// Check if we have a volume for this mountpoint
tuple, exists := namedVolumeMounts[mp.Name]
if !exists || tuple.V.Name == "" {
Copy path View file
@@ -18,9 +18,15 @@ package main
import (
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"testing"
"github.com/appc/spec/schema"
"github.com/appc/spec/schema/types"
"github.com/coreos/rkt/tests/testutils"
)
@@ -52,3 +58,183 @@ func TestFlyNetns(t *testing.T) {
t.Fatalf("container left host netns")
}
}
func TestFlyMountCLI(t *testing.T) {
tmpDir := createTempDirOrPanic("rkt-mount-test-")
defer os.RemoveAll(tmpDir)
mountSrcFile := filepath.Join(tmpDir, "hello")
if err := ioutil.WriteFile(mountSrcFile, []byte("world"), 0600); err != nil {
t.Fatalf("Cannot write file: %v", err)
}
testImageArgs := []string{fmt.Sprintf("--exec=/inspect --read-file --file-name %s", mountSrcFile)}
testImage := patchTestACI("rkt-inspect-stage1-fly.aci", testImageArgs...)
defer os.Remove(testImage)
ctx := testutils.NewRktRunCtx()
defer ctx.Cleanup()
testParams := []struct {
mountParam string
expectedExit int
}{
{
fmt.Sprintf(
"--volume=test,kind=host,source=%s --mount volume=test,target=%s",
mountSrcFile, mountSrcFile,
),
0,
},
{
fmt.Sprintf(
"--volume=test1,kind=host,source=%s --mount volume=test1,target=%s --volume=test2,kind=host,source=%s --mount volume=test1,target=%s",
mountSrcFile, mountSrcFile,
mountSrcFile, mountSrcFile,
),
1, /* TODO: decide on consistency with other stage1s */
},
}
for _, testParam := range testParams {
cmd := fmt.Sprintf("%s --debug --insecure-options=image run %s %s", ctx.Cmd(), testImage, testParam.mountParam)
child := spawnOrFail(t, cmd)
ctx.RegisterChild(child)
waitOrFail(t, child, testParam.expectedExit)
}
}
// TODO: unite this with rkt_run_pod_manifest_test.go
type imagePatch struct {
name string
patches []string
}
const baseAppName = "rkt-inspect"
func verifyHostFile(t *testing.T, tmpdir, filename string, i int, expectedResult string) {
filePath := path.Join(tmpdir, filename)
defer os.Remove(filePath)
// Verify the file is written to host.
if strings.Contains(expectedResult, "host:") {
data, err := ioutil.ReadFile(filePath)
if err != nil {
t.Fatalf("%d: Cannot read the host file: %v", i, err)
}
if string(data) != expectedResult {
t.Fatalf("%d: Expecting %q in the host file, but saw %q", i, expectedResult, data)
}
}
}
func TestFlyMountPodManifest(t *testing.T) {
ctx := testutils.NewRktRunCtx()
defer ctx.Cleanup()
tmpdir := createTempDirOrPanic("rkt-tests.")
defer os.RemoveAll(tmpdir)
tests := []struct {
// [image name]:[image patches]
images []imagePatch
podManifest *schema.PodManifest
expectedExit int
expectedResult string
}{
{
// Simple read after write with volume mounted in a read-only rootfs.
[]imagePatch{
{"rkt-test-run-pod-manifest-read-only-rootfs-vol-rw.aci", []string{}},
},
&schema.PodManifest{
Apps: []schema.RuntimeApp{
{
Name: baseAppName,
App: &types.App{
Exec: []string{"/inspect", "--write-file", "--read-file"},
User: "0",
Group: "0",
Environment: []types.EnvironmentVariable{
{"FILE", "/dir1/file"},
{"CONTENT", "host:foo"},
},
MountPoints: []types.MountPoint{
{"dir1", "/dir1", false},
},
},
ReadOnlyRootFS: true,
},
},
Volumes: []types.Volume{
{"dir1", "host", tmpdir, nil, nil, nil, nil},
},
},
0,
"host:foo",
},
}
for i, tt := range tests {
var hashesToRemove []string
for j, v := range tt.images {
hash := patchImportAndFetchHash(v.name, v.patches, t, ctx)
hashesToRemove = append(hashesToRemove, hash)
imgName := types.MustACIdentifier(v.name)
imgID, err := types.NewHash(hash)
if err != nil {
t.Fatalf("Cannot generate types.Hash from %v: %v", hash, err)
}
ra := &tt.podManifest.Apps[j]
ra.Image.Name = imgName
ra.Image.ID = *imgID
}
tt.podManifest.ACKind = schema.PodManifestKind
tt.podManifest.ACVersion = schema.AppContainerVersion
manifestFile := generatePodManifestFile(t, tt.podManifest)
defer os.Remove(manifestFile)
// 1. Test 'rkt run'.
runCmd := fmt.Sprintf("%s run --mds-register=false --pod-manifest=%s", ctx.Cmd(), manifestFile)
t.Logf("Running 'run' test #%v", i)
child := spawnOrFail(t, runCmd)
ctx.RegisterChild(child)
if tt.expectedResult != "" {
if _, out, err := expectRegexWithOutput(child, tt.expectedResult); err != nil {
t.Errorf("Expected %q but not found: %v\n%s", tt.expectedResult, err, out)
continue
}
}
waitOrFail(t, child, tt.expectedExit)
verifyHostFile(t, tmpdir, "file", i, tt.expectedResult)
// 2. Test 'rkt prepare' + 'rkt run-prepared'.
rktCmd := fmt.Sprintf("%s --insecure-options=image prepare --pod-manifest=%s",
ctx.Cmd(), manifestFile)
uuid := runRktAndGetUUID(t, rktCmd)
runPreparedCmd := fmt.Sprintf("%s run-prepared --mds-register=false %s", ctx.Cmd(), uuid)
t.Logf("Running 'run-prepared' test #%v", i)
child = spawnOrFail(t, runPreparedCmd)
if tt.expectedResult != "" {
if _, out, err := expectRegexWithOutput(child, tt.expectedResult); err != nil {
t.Errorf("Expected %q but not found: %v\n%s", tt.expectedResult, err, out)
continue
}
}
waitOrFail(t, child, tt.expectedExit)
verifyHostFile(t, tmpdir, "file", i, tt.expectedResult)
// we run the garbage collector and remove the imported images to save
// space
runGC(t, ctx)
for _, h := range hashesToRemove {
removeFromCas(t, ctx, h)
}
}
}
ProTip! Use n and p to navigate between commits in a pull request.