func handlePodVerify(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() uuid, err := types.NewUUID(r.FormValue("uuid")) if err != nil { w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "uuid field missing or malformed: %v", err) return } content := r.FormValue("content") if content == "" { w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "content field missing") return } sig, err := base64.StdEncoding.DecodeString(r.FormValue("signature")) if err != nil { w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "signature field missing or corrupt: %v", err) return } h := hmac.New(sha512.New, hmacKey[:]) h.Write((*uuid)[:]) h.Write([]byte(content)) if hmac.Equal(sig, h.Sum(nil)) { w.WriteHeader(http.StatusOK) } else { w.WriteHeader(http.StatusForbidden) } }
func handleRegisterApp(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() uuid, err := types.NewUUID(mux.Vars(r)["uuid"]) if err != nil { w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "UUID is missing or malformed: %v", err) return } an := mux.Vars(r)["app"] if an == "" { w.WriteHeader(http.StatusBadRequest) fmt.Fprint(w, "app missing") return } im := &schema.ImageManifest{} if err := json.NewDecoder(r.Body).Decode(im); err != nil { w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "JSON-decoding failed: %v", err) return } err = pods.addApp(uuid, an, im) if err != nil { w.WriteHeader(http.StatusNotFound) fmt.Fprint(w, "Pod with given UUID not found") return } w.WriteHeader(http.StatusOK) }
func handleRegisterPod(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() uuid, err := types.NewUUID(mux.Vars(r)["uuid"]) if err != nil { w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "UUID is missing or malformed: %v", err) return } token := queryValue(r.URL, "token") if token == "" { w.WriteHeader(http.StatusBadRequest) fmt.Fprint(w, "token missing") return } pm := &schema.PodManifest{} if err := json.NewDecoder(r.Body).Decode(pm); err != nil { w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "JSON-decoding failed: %v", err) return } pods.addPod(uuid, token, pm) w.WriteHeader(http.StatusOK) }
func (s *v1AlphaAPIServer) InspectPod(ctx context.Context, request *v1alpha.InspectPodRequest) (*v1alpha.InspectPodResponse, error) { uuid, err := types.NewUUID(request.Id) if err != nil { log.Printf("Invalid pod id %q: %v", request.Id, err) return nil, err } p, err := getPod(uuid) if err != nil { log.Printf("Failed to get pod %q: %v", request.Id, err) return nil, err } defer p.Close() pod, _, err := getBasicPod(p) if err != nil { return nil, err } // Fill the extra pod info that is not available in ListPods(). if err := fillAppInfo(s.store, p, pod); err != nil { return nil, err } return &v1alpha.InspectPodResponse{Pod: pod}, nil }
func readUUIDFromFile(path string) (*types.UUID, error) { uuid, err := ioutil.ReadFile(path) if err != nil { return nil, err } uuid = bytes.TrimSpace(uuid) return types.NewUUID(string(uuid)) }
func TestImagePrepareRmIDRun(t *testing.T) { imageFile := patchTestACI(unreferencedACI, fmt.Sprintf("--name=%s", unreferencedApp)) defer os.Remove(imageFile) ctx := testutils.NewRktRunCtx() defer ctx.Cleanup() cmd := fmt.Sprintf("%s --insecure-options=image fetch %s", ctx.Cmd(), imageFile) t.Logf("Fetching %s", imageFile) spawnAndWaitOrFail(t, cmd, true) // at this point we know that RKT_INSPECT_IMAGE env var is not empty referencedACI := os.Getenv("RKT_INSPECT_IMAGE") cmds := strings.Fields(ctx.Cmd()) prepareCmd := exec.Command(cmds[0], cmds[1:]...) prepareCmd.Args = append(prepareCmd.Args, "--insecure-options=image", "prepare", referencedACI) output, err := prepareCmd.Output() if err != nil { t.Fatalf("Cannot read the output: %v", err) } podIDStr := strings.TrimSpace(string(output)) podID, err := types.NewUUID(podIDStr) if err != nil { t.Fatalf("%q is not a valid UUID: %v", podIDStr, err) } t.Logf("Retrieving stage1 imageID") stage1ImageID, err := getImageID(ctx, stage1App) if err != nil { t.Fatalf("rkt didn't terminate correctly: %v", err) } t.Logf("Retrieving %s image ID", referencedApp) referencedImageID, err := getImageID(ctx, referencedApp) if err != nil { t.Fatalf("rkt didn't terminate correctly: %v", err) } t.Logf("Retrieving %s image ID", unreferencedApp) unreferencedImageID, err := getImageID(ctx, unreferencedApp) if err != nil { t.Fatalf("rkt didn't terminate correctly: %v", err) } t.Logf("Removing stage1 image (should work)") removeImage(t, ctx, stage1ImageID) t.Logf("Removing image for app %s (should work)", referencedApp) removeImage(t, ctx, referencedImageID) t.Logf("Removing image for app %s (should work)", unreferencedApp) removeImage(t, ctx, unreferencedImageID) cmd = fmt.Sprintf("%s run-prepared --mds-register=false %s", ctx.Cmd(), podID.String()) t.Logf("Running %s", referencedACI) spawnAndWaitOrFail(t, cmd, true) }
func TestPodStoreAddApp(t *testing.T) { ps, _, _, app := setupPodStoreTest(t) uuid2, err := types.NewUUID("fe305d54-75b4-431b-adb2-eb6b9e546013") if err != nil { panic("bad uuid literal") } im := &schema.ImageManifest{} if err = ps.addApp(uuid2, app, im); err != errPodNotFound { t.Errorf("addApp with unknown pod returned: %v", err) } }
func validatePodMetadata(metadataURL string, pm *schema.PodManifest) results { r := results{} uuid, err := metadataGet(metadataURL, "/pod/uuid") if err != nil { return append(r, err) } _, err = types.NewUUID(string(uuid)) if err != nil { return append(r, fmt.Errorf("malformed UUID returned (%v): %v", string(uuid), err)) } return append(r, validatePodAnnotations(metadataURL, pm)...) }
func runRktAndGetUUID(t *testing.T, rktCmd string) string { child := spawnOrFail(t, rktCmd) defer waitOrFail(t, child, true) result, out, err := expectRegexWithOutput(child, "\n[0-9a-f-]{36}") if err != nil || len(result) != 1 { t.Fatalf("Error: %v\nOutput: %v", err, out) } podIDStr := strings.TrimSpace(result[0]) podID, err := types.NewUUID(podIDStr) if err != nil { t.Fatalf("%q is not a valid UUID: %v", podIDStr, err) } return podID.String() }
func handleUnregisterPod(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() uuid, err := types.NewUUID(mux.Vars(r)["uuid"]) if err != nil { w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "UUID is missing or malformed: %v", err) return } if err := pods.remove(uuid); err != nil { w.WriteHeader(http.StatusNotFound) fmt.Fprint(w, err) return } w.WriteHeader(http.StatusOK) }
func main() { flag.Parse() if !debug { log.SetOutput(ioutil.Discard) } podID, err := types.NewUUID(flag.Arg(0)) if err != nil { fmt.Fprintln(os.Stderr, "UUID is missing or malformed") os.Exit(1) } if err := gcNetworking(podID); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } }
// walkPods iterates over the included directories calling function f for every pod found. func walkPods(include includeMask, f func(*pod)) error { if err := initPods(); err != nil { return err } ls, err := listPods(include) if err != nil { return fmt.Errorf("failed to get pods: %v", err) } sort.Strings(ls) for _, uuid := range ls { u, err := types.NewUUID(uuid) if err != nil { stderr("Skipping %q: %v", uuid, err) continue } p, err := getPod(u) if err != nil { stderr("Skipping %q: %v", uuid, err) continue } // omit pods found in unrequested states // this is to cover a race between listPods finding the uuids and pod states changing // it's preferable to keep these operations lock-free, for example a `rkt gc` shouldn't block `rkt run`. if p.isEmbryo && include&includeEmbryoDir == 0 || p.isExitedGarbage && include&includeExitedGarbageDir == 0 || p.isGarbage && include&includeGarbageDir == 0 || p.isPrepared && include&includePreparedDir == 0 || ((p.isPreparing || p.isAbortedPrepare) && include&includePrepareDir == 0) || p.isRunning() && include&includeRunDir == 0 { p.Close() continue } f(p) p.Close() } return nil }
func setupPodStoreTest(t *testing.T) (*podStore, *types.UUID, string, string) { ps := newPodStore() uuid, err := types.NewUUID("de305d54-75b4-431b-adb2-eb6b9e546013") if err != nil { panic("bad uuid literal") } ip := "1.2.3.4" app := "myapp" pm := &schema.PodManifest{} ps.addPod(uuid, ip, pm) im := &schema.ImageManifest{} err = ps.addApp(uuid, app, im) if err != nil { t.Fatalf("addApp failed with %v", err) } return ps, uuid, ip, app }
// resolveUUID attempts to resolve the uuid specified as uuid against all pods present. // An unambiguously matched uuid or nil is returned. func resolveUUID(uuid string) (*types.UUID, error) { uuid = strings.ToLower(uuid) m, err := matchUUID(uuid) if err != nil { return nil, err } if len(m) == 0 { return nil, fmt.Errorf("no matches found for %q", uuid) } if len(m) > 1 { return nil, fmt.Errorf("ambiguous uuid, %d matches", len(m)) } u, err := types.NewUUID(m[0]) if err != nil { return nil, fmt.Errorf("invalid UUID: %v", err) } return u, nil }
// newPod creates a new pod directory in the "preparing" state, allocating a unique uuid for it in the process. // The returned pod is always left in an exclusively locked state (preparing is locked in the prepared directory) // The pod must be closed using pod.Close() func newPod() (*pod, error) { if err := initPods(); err != nil { return nil, err } p := &pod{ createdByMe: true, isEmbryo: true, // starts as an embryo, then xToPreparing locks, renames, and sets isPreparing // rest start false. } var err error p.uuid, err = types.NewUUID(uuid.New()) if err != nil { return nil, fmt.Errorf("error creating UUID: %v", err) } err = os.Mkdir(p.embryoPath(), 0750) if err != nil { return nil, err } p.FileLock, err = lock.NewLock(p.embryoPath(), lock.Dir) if err != nil { os.Remove(p.embryoPath()) return nil, err } err = p.xToPreparing() if err != nil { return nil, err } // At this point we we have: // /var/lib/rkt/pods/prepare/$uuid << exclusively locked to indicate "preparing" return p, nil }
func runRktAndGetUUID(t *testing.T, rktCmd string) string { child, err := gexpect.Spawn(rktCmd) if err != nil { t.Fatalf("cannot exec rkt: %v", err) } result, out, err := expectRegexWithOutput(child, "\n[0-9a-f-]{36}") if err != nil || len(result) != 1 { t.Fatalf("Error: %v\nOutput: %v", err, out) } if err = child.Wait(); err != nil { t.Fatalf("rkt didn't terminate correctly: %v", err) } podIDStr := strings.TrimSpace(result[0]) podID, err := types.NewUUID(podIDStr) if err != nil { t.Fatalf("%q is not a valid UUID: %v", podIDStr, err) } return podID.String() }
// Test running pod manifests that contains just one app. // TODO(yifan): Add more tests for port mappings. and multiple apps. func TestPodManifest(t *testing.T) { ctx := newRktRunCtx() defer ctx.cleanup() tmpdir, err := ioutil.TempDir("", "rkt-tests.") if err != nil { t.Fatalf("Cannot create temporary directory: %v", err) } defer os.RemoveAll(tmpdir) boolFalse, boolTrue := false, true tests := []struct { imageName string imagePatches []string podManifest *schema.PodManifest shouldSuccess bool expectedResult string }{ { // Simple read. "rkt-test-run-pod-manifest-read.aci", []string{}, &schema.PodManifest{ Apps: []schema.RuntimeApp{ { Name: baseAppName, App: &types.App{ Exec: []string{"/inspect", "--read-file"}, User: "******", Group: "0", Environment: []types.EnvironmentVariable{ {"FILE", "/dir1/file"}, }, }, }, }, }, true, "dir1", }, { // Simple read after write with volume mounted. "rkt-test-run-pod-manifest-vol-rw.aci", []string{}, &schema.PodManifest{ Apps: []schema.RuntimeApp{ { Name: baseAppName, App: &types.App{ Exec: []string{"/inspect", "--write-file", "--read-file"}, User: "******", Group: "0", Environment: []types.EnvironmentVariable{ {"FILE", "/dir1/file"}, {"CONTENT", "host:foo"}, }, MountPoints: []types.MountPoint{ {"dir1", "/dir1", false}, }, }, }, }, Volumes: []types.Volume{ {"dir1", "host", tmpdir, nil}, }, }, true, "host:foo", }, { // Simple read after write with read-only mount point, should fail. "rkt-test-run-pod-manifest-vol-ro.aci", []string{}, &schema.PodManifest{ Apps: []schema.RuntimeApp{ { Name: baseAppName, App: &types.App{ Exec: []string{"/inspect", "--write-file", "--read-file"}, User: "******", Group: "0", Environment: []types.EnvironmentVariable{ {"FILE", "/dir1/file"}, {"CONTENT", "bar"}, }, MountPoints: []types.MountPoint{ {"dir1", "/dir1", true}, }, }, }, }, Volumes: []types.Volume{ {"dir1", "host", tmpdir, nil}, }, }, false, `Cannot write to file "/dir1/file": open /dir1/file: read-only file system`, }, { // Simple read after write with volume mounted. // Override the image's mount point spec. This should fail as the volume is // read-only in pod manifest, (which will override the mount point in both image/pod manifest). "rkt-test-run-pod-manifest-vol-rw-override.aci", []string{ "--mounts=dir1,path=/dir1,readOnly=false", }, &schema.PodManifest{ Apps: []schema.RuntimeApp{ { Name: baseAppName, App: &types.App{ Exec: []string{"/inspect", "--write-file", "--read-file"}, User: "******", Group: "0", Environment: []types.EnvironmentVariable{ {"FILE", "/dir1/file"}, {"CONTENT", "bar"}, }, MountPoints: []types.MountPoint{ {"dir1", "/dir1", false}, }, }, }, }, Volumes: []types.Volume{ {"dir1", "host", tmpdir, &boolTrue}, }, }, false, `Cannot write to file "/dir1/file": open /dir1/file: read-only file system`, }, { // Simple read after write with volume mounted. // Override the image's mount point spec. "rkt-test-run-pod-manifest-vol-rw-override.aci", []string{ "--mounts=dir1,path=/dir1,readOnly=true", }, &schema.PodManifest{ Apps: []schema.RuntimeApp{ { Name: baseAppName, App: &types.App{ Exec: []string{"/inspect", "--write-file", "--read-file"}, User: "******", Group: "0", Environment: []types.EnvironmentVariable{ {"FILE", "/dir2/file"}, {"CONTENT", "host:bar"}, }, MountPoints: []types.MountPoint{ {"dir1", "/dir2", false}, }, }, }, }, Volumes: []types.Volume{ {"dir1", "host", tmpdir, nil}, }, }, true, "host:bar", }, { // Simple read after write with volume mounted, no apps in pod manifest. "rkt-test-run-pod-manifest-vol-rw-no-app.aci", []string{ "--exec=/inspect --write-file --read-file --file-name=/dir1/file --content=host:baz", "--mounts=dir1,path=/dir1,readOnly=false", }, &schema.PodManifest{ Apps: []schema.RuntimeApp{ {Name: baseAppName}, }, Volumes: []types.Volume{ {"dir1", "host", tmpdir, nil}, }, }, true, "host:baz", }, { // Simple read after write with volume mounted, no apps in pod manifest. // This should succeed even the mount point in image manifest is readOnly, // because it is overrided by the volume's readOnly. "rkt-test-run-pod-manifest-vol-ro-no-app.aci", []string{ "--exec=/inspect --write-file --read-file --file-name=/dir1/file --content=host:zaz", "--mounts=dir1,path=/dir1,readOnly=true", }, &schema.PodManifest{ Apps: []schema.RuntimeApp{ {Name: baseAppName}, }, Volumes: []types.Volume{ {"dir1", "host", tmpdir, &boolFalse}, }, }, true, "host:zaz", }, { // Simple read after write with read-only volume mounted, no apps in pod manifest. // This should fail as the volume is read-only. "rkt-test-run-pod-manifest-vol-ro-no-app.aci", []string{ "--exec=/inspect --write-file --read-file --file-name=/dir1/file --content=baz", "--mounts=dir1,path=/dir1,readOnly=false", }, &schema.PodManifest{ Apps: []schema.RuntimeApp{ {Name: baseAppName}, }, Volumes: []types.Volume{ {"dir1", "host", tmpdir, &boolTrue}, }, }, false, `Cannot write to file "/dir1/file": open /dir1/file: read-only file system`, }, } for i, tt := range tests { patchTestACI(tt.imageName, tt.imagePatches...) hash := importImageAndFetchHash(t, ctx, tt.imageName) defer os.Remove(tt.imageName) tt.podManifest.ACKind = schema.PodManifestKind tt.podManifest.ACVersion = schema.AppContainerVersion imgName := types.MustACIdentifier(tt.imageName) imgID, err := types.NewHash(hash) if err != nil { t.Fatalf("Cannot generate types.Hash from %v: %v", hash, err) } for i := range tt.podManifest.Apps { ra := &tt.podManifest.Apps[i] ra.Image.Name = imgName ra.Image.ID = *imgID } manifestFile := generatePodManifestFile(t, tt.podManifest) defer os.Remove(manifestFile) // 1. Test 'rkt run'. runCmd := fmt.Sprintf("%s run --pod-manifest=%s", ctx.cmd(), manifestFile) t.Logf("Running 'run' test #%v: %v", i, runCmd) child, err := gexpect.Spawn(runCmd) if err != nil { t.Fatalf("Cannot exec rkt #%v: %v", i, err) } if tt.expectedResult != "" { if err := child.Expect(tt.expectedResult); err != nil { t.Fatalf("Expected %q but not found: %v", tt.expectedResult, err) } } if err := child.Wait(); err != nil { if tt.shouldSuccess { t.Fatalf("rkt didn't terminate correctly: %v", err) } } verifyHostFile(t, tmpdir, "file", i, tt.expectedResult) // 2. Test 'rkt prepare' + 'rkt run-prepared'. cmds := strings.Fields(ctx.cmd()) prepareCmd := exec.Command(cmds[0], cmds[1:]...) prepareArg := fmt.Sprintf("--pod-manifest=%s", manifestFile) prepareCmd.Args = append(prepareCmd.Args, "--insecure-skip-verify", "prepare", prepareArg) output, err := prepareCmd.Output() if err != nil { t.Fatalf("Cannot read the output: %v", err) } podIDStr := strings.TrimSpace(string(output)) podID, err := types.NewUUID(podIDStr) if err != nil { t.Fatalf("%q is not a valid UUID: %v", podIDStr, err) } runPreparedCmd := fmt.Sprintf("%s run-prepared %s", ctx.cmd(), podID.String()) t.Logf("Running 'run' test #%v: %v", i, runPreparedCmd) child, err = gexpect.Spawn(runPreparedCmd) if err != nil { t.Fatalf("Cannot exec rkt #%v: %v", i, err) } if tt.expectedResult != "" { if err := child.Expect(tt.expectedResult); err != nil { t.Fatalf("Expected %q but not found: %v", tt.expectedResult, err) } } if err := child.Wait(); err != nil { if tt.shouldSuccess { t.Fatalf("rkt didn't terminate correctly: %v", err) } } verifyHostFile(t, tmpdir, "file", i, tt.expectedResult) } }
func stage1() int { uuid, err := types.NewUUID(flag.Arg(0)) if err != nil { fmt.Fprintln(os.Stderr, "UUID is missing or malformed") return 1 } root := "." p, err := LoadPod(root, uuid) if err != nil { fmt.Fprintf(os.Stderr, "Failed to load pod: %v\n", err) return 1 } // set close-on-exec flag on RKT_LOCK_FD so it gets correctly closed when invoking // network plugins lfd, err := common.GetRktLockFD() if err != nil { fmt.Fprintf(os.Stderr, "Failed to get rkt lock fd: %v\n", err) return 1 } if err := sys.CloseOnExec(lfd, true); err != nil { fmt.Fprintf(os.Stderr, "Failed to set FD_CLOEXEC on rkt lock: %v\n", err) return 1 } mirrorLocalZoneInfo(p.Root) if privNet.Any() { fps, err := forwardedPorts(p) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) return 6 } n, err := networking.Setup(root, p.UUID, fps, privNet) if err != nil { fmt.Fprintf(os.Stderr, "Failed to setup network: %v\n", err) return 6 } if err = n.Save(); err != nil { fmt.Fprintf(os.Stderr, "Failed to save networking state %v\n", err) n.Teardown() return 6 } if len(mdsToken) > 0 { hostIP, err := n.GetDefaultHostIP() if err != nil { fmt.Fprintf(os.Stderr, "Failed to get default Host IP: %v\n", err) return 6 } p.MetadataServiceURL = common.MetadataServicePublicURL(hostIP, mdsToken) } } else { if len(mdsToken) > 0 { p.MetadataServiceURL = common.MetadataServicePublicURL(localhostIP, mdsToken) } } flavor, systemdStage1Version, err := p.getFlavor() if err != nil { fmt.Fprintf(os.Stderr, "Failed to get stage1 flavor: %v\n", err) return 3 } if err = p.WritePrepareAppTemplate(systemdStage1Version); err != nil { fmt.Fprintf(os.Stderr, "Failed to write prepare-app service template: %v\n", err) return 2 } if err = p.PodToSystemd(interactive); err != nil { fmt.Fprintf(os.Stderr, "Failed to configure systemd: %v\n", err) return 2 } args, env, err := getArgsEnv(p, flavor, systemdStage1Version, debug) if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) return 3 } // create a separate mount namespace so the cgroup filesystems // are unmounted when exiting the pod if err := syscall.Unshare(syscall.CLONE_NEWNS); err != nil { log.Fatalf("error unsharing: %v", err) } // we recursively make / a "shared and slave" so mount events from the // new namespace don't propagate to the host namespace but mount events // from the host propagate to the new namespace and are forwarded to // its peer group // See https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt if err := syscall.Mount("", "/", "none", syscall.MS_REC|syscall.MS_SLAVE, ""); err != nil { log.Fatalf("error making / a slave mount: %v", err) } if err := syscall.Mount("", "/", "none", syscall.MS_REC|syscall.MS_SHARED, ""); err != nil { log.Fatalf("error making / a shared and slave mount: %v", err) } appHashes := p.GetAppHashes() s1Root := common.Stage1RootfsPath(p.Root) machineID := p.GetMachineID() subcgroup, err := getContainerSubCgroup(machineID) if err == nil { if err := cgroup.CreateCgroups(s1Root, subcgroup, appHashes); err != nil { fmt.Fprintf(os.Stderr, "Error creating cgroups: %v\n", err) return 5 } } else { fmt.Fprintf(os.Stderr, "Continuing with per-app isolators disabled: %v\n", err) } if err = writePpid(os.Getpid()); err != nil { fmt.Fprintln(os.Stderr, err.Error()) return 4 } err = withClearedCloExec(lfd, func() error { return syscall.Exec(args[0], args, env) }) if err != nil { fmt.Fprintf(os.Stderr, "Failed to execute nspawn: %v\n", err) return 7 } return 0 }
func stage1() int { uuid, err := types.NewUUID(flag.Arg(0)) if err != nil { fmt.Fprintln(os.Stderr, "UUID is missing or malformed") return 1 } root := "." p, err := stage1commontypes.LoadPod(root, uuid) if err != nil { fmt.Fprintf(os.Stderr, "Failed to load pod: %v\n", err) return 1 } // set close-on-exec flag on RKT_LOCK_FD so it gets correctly closed when invoking // network plugins lfd, err := common.GetRktLockFD() if err != nil { fmt.Fprintf(os.Stderr, "Failed to get rkt lock fd: %v\n", err) return 1 } if err := sys.CloseOnExec(lfd, true); err != nil { fmt.Fprintf(os.Stderr, "Failed to set FD_CLOEXEC on rkt lock: %v\n", err) return 1 } mirrorLocalZoneInfo(p.Root) flavor, _, err := stage1initcommon.GetFlavor(p) if err != nil { fmt.Fprintf(os.Stderr, "Failed to get stage1 flavor: %v\n", err) return 3 } var n *networking.Networking if netList.Contained() { fps, err := forwardedPorts(p) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) return 6 } n, err = networking.Setup(root, p.UUID, fps, netList, localConfig, flavor) if err != nil { fmt.Fprintf(os.Stderr, "Failed to setup network: %v\n", err) return 6 } if err = n.Save(); err != nil { fmt.Fprintf(os.Stderr, "Failed to save networking state %v\n", err) n.Teardown(flavor) return 6 } if len(mdsToken) > 0 { hostIP, err := n.GetDefaultHostIP() if err != nil { fmt.Fprintf(os.Stderr, "Failed to get default Host IP: %v\n", err) return 6 } p.MetadataServiceURL = common.MetadataServicePublicURL(hostIP, mdsToken) } } else { if flavor == "kvm" { fmt.Fprintf(os.Stderr, "Flavor kvm requires private network configuration (try --net).\n") return 6 } if len(mdsToken) > 0 { p.MetadataServiceURL = common.MetadataServicePublicURL(localhostIP, mdsToken) } } if err = stage1initcommon.WriteDefaultTarget(p); err != nil { fmt.Fprintf(os.Stderr, "Failed to write default.target: %v\n", err) return 2 } if err = stage1initcommon.WritePrepareAppTemplate(p); err != nil { fmt.Fprintf(os.Stderr, "Failed to write prepare-app service template: %v\n", err) return 2 } if err := stage1initcommon.SetJournalPermissions(p); err != nil { fmt.Fprintf(os.Stderr, "Warning: error setting journal ACLs, you'll need root to read the pod journal: %v", err) } if flavor == "kvm" { if err := KvmPodToSystemd(p, n); err != nil { fmt.Fprintf(os.Stderr, "Failed to configure systemd for kvm: %v\n", err) return 2 } } if err = stage1initcommon.PodToSystemd(p, interactive, flavor, privateUsers); err != nil { fmt.Fprintf(os.Stderr, "Failed to configure systemd: %v\n", err) return 2 } args, env, err := getArgsEnv(p, flavor, debug, n) if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) return 3 } // create a separate mount namespace so the cgroup filesystems // are unmounted when exiting the pod if err := syscall.Unshare(syscall.CLONE_NEWNS); err != nil { log.Fatalf("Error unsharing: %v", err) } // we recursively make / a "shared and slave" so mount events from the // new namespace don't propagate to the host namespace but mount events // from the host propagate to the new namespace and are forwarded to // its peer group // See https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt if err := syscall.Mount("", "/", "none", syscall.MS_REC|syscall.MS_SLAVE, ""); err != nil { log.Fatalf("Error making / a slave mount: %v", err) } if err := syscall.Mount("", "/", "none", syscall.MS_REC|syscall.MS_SHARED, ""); err != nil { log.Fatalf("Error making / a shared and slave mount: %v", err) } enabledCgroups, err := cgroup.GetEnabledCgroups() if err != nil { fmt.Fprintf(os.Stderr, "Error getting cgroups: %v", err) return 5 } // mount host cgroups in the rkt mount namespace if err := mountHostCgroups(enabledCgroups); err != nil { log.Fatalf("Couldn't mount the host cgroups: %v\n", err) return 5 } var serviceNames []string for _, app := range p.Manifest.Apps { serviceNames = append(serviceNames, stage1initcommon.ServiceUnitName(app.Name)) } s1Root := common.Stage1RootfsPath(p.Root) machineID := stage1initcommon.GetMachineID(p) subcgroup, err := getContainerSubCgroup(machineID) if err == nil { if err := mountContainerCgroups(s1Root, enabledCgroups, subcgroup, serviceNames); err != nil { fmt.Fprintf(os.Stderr, "Couldn't mount the container cgroups: %v\n", err) return 5 } } else { fmt.Fprintf(os.Stderr, "Continuing with per-app isolators disabled: %v\n", err) } if err = stage1common.WritePpid(os.Getpid()); err != nil { fmt.Fprintln(os.Stderr, err.Error()) return 4 } err = stage1common.WithClearedCloExec(lfd, func() error { return syscall.Exec(args[0], args, env) }) if err != nil { fmt.Fprintf(os.Stderr, "Failed to execute %q: %v\n", args[0], err) return 7 } return 0 }
func stage1() int { uuid, err := types.NewUUID(flag.Arg(0)) if err != nil { fmt.Fprintln(os.Stderr, "UUID is missing or malformed") return 1 } root := "." p, err := LoadPod(root, uuid) if err != nil { fmt.Fprintf(os.Stderr, "Failed to load pod: %v\n", err) return 1 } // set close-on-exec flag on RKT_LOCK_FD so it gets correctly closed when invoking // network plugins lfd, err := common.GetRktLockFD() if err != nil { fmt.Fprintf(os.Stderr, "Failed to get rkt lock fd: %v\n", err) return 1 } if err := sys.CloseOnExec(lfd, true); err != nil { fmt.Fprintf(os.Stderr, "Failed to set FD_CLOEXEC on rkt lock: %v\n", err) return 1 } mirrorLocalZoneInfo(p.Root) if privNet { fps, err := forwardedPorts(p) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) return 6 } n, err := networking.Setup(root, p.UUID, fps) if err != nil { fmt.Fprintf(os.Stderr, "Failed to setup network: %v\n", err) return 6 } defer n.Teardown() if err = n.Save(); err != nil { fmt.Fprintf(os.Stderr, "Failed to save networking state %v\n", err) return 6 } p.MetadataServiceURL = common.MetadataServicePublicURL(n.GetDefaultHostIP()) if err = registerPod(p, n.GetDefaultIP()); err != nil { fmt.Fprintf(os.Stderr, "Failed to register pod: %v\n", err) return 6 } defer unregisterPod(p) } if err = p.PodToSystemd(interactive); err != nil { fmt.Fprintf(os.Stderr, "Failed to configure systemd: %v\n", err) return 2 } args, env, err := getArgsEnv(p, debug) if err != nil { fmt.Fprintf(os.Stderr, "Failed to get execution parameters: %v\n", err) return 3 } var execFn func() error if privNet { cmd := exec.Cmd{ Path: args[0], Args: args, Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, Env: env, } execFn = cmd.Run } else { execFn = func() error { return syscall.Exec(args[0], args, env) } } err = withClearedCloExec(lfd, execFn) if err != nil { fmt.Fprintf(os.Stderr, "Failed to execute nspawn: %v\n", err) return 5 } return 0 }
func stage1() int { uuid, err := types.NewUUID(flag.Arg(0)) if err != nil { fmt.Fprintf(os.Stderr, "UUID is missing or malformed\n") return 1 } root := "." p, err := stage1commontypes.LoadPod(root, uuid) if err != nil { fmt.Fprintf(os.Stderr, "can't load pod: %v\n", err) return 1 } if len(p.Manifest.Apps) != 1 { fmt.Fprintf(os.Stderr, "flavor %q only supports 1 application per Pod for now.\n", flavor) return 1 } lfd, err := common.GetRktLockFD() if err != nil { fmt.Fprintf(os.Stderr, "can't get rkt lock fd: %v\n", err) return 1 } // set close-on-exec flag on RKT_LOCK_FD so it gets correctly closed after execution is finished if err := sys.CloseOnExec(lfd, true); err != nil { fmt.Fprintf(os.Stderr, "can't set FD_CLOEXEC on rkt lock: %v\n", err) return 1 } // TODO: insert environment from manifest env := []string{"PATH=/bin:/sbin:/usr/bin:/usr/local/bin"} args := p.Manifest.Apps[0].App.Exec rfs := filepath.Join(common.AppPath(p.Root, p.Manifest.Apps[0].Name), "rootfs") argFlyMounts, err := evaluateMounts(rfs, string(p.Manifest.Apps[0].Name), p) if err != nil { fmt.Fprintf(os.Stderr, "can't evaluate mounts: %v\n", err) return 1 } effectiveMounts := append( []flyMount{ {"", "", "/dev", "none", syscall.MS_REC | syscall.MS_SHARED}, {"/dev", rfs, "/dev", "none", syscall.MS_BIND | syscall.MS_REC}, {"", "", "/proc", "none", syscall.MS_REC | syscall.MS_SHARED}, {"/proc", rfs, "/proc", "none", syscall.MS_BIND | syscall.MS_REC}, {"", "", "/sys", "none", syscall.MS_REC | syscall.MS_SHARED}, {"/sys", rfs, "/sys", "none", syscall.MS_BIND | syscall.MS_REC}, {"tmpfs", rfs, "/tmp", "tmpfs", 0}, }, argFlyMounts..., ) for _, mount := range effectiveMounts { var ( err error hostPathInfo os.FileInfo targetPathInfo os.FileInfo ) if strings.HasPrefix(mount.HostPath, "/") { if hostPathInfo, err = os.Stat(mount.HostPath); err != nil { fmt.Fprintf(os.Stderr, "stat of host directory %s: \n%v", mount.HostPath, err) return 1 } } else { hostPathInfo = nil } absTargetPath := filepath.Join(mount.TargetPrefixPath, mount.RelTargetPath) if targetPathInfo, err = os.Stat(absTargetPath); err != nil && !os.IsNotExist(err) { fmt.Fprintf(os.Stderr, "stat of target directory %s: \n%v\n", absTargetPath, err) return 1 } switch { case targetPathInfo == nil: absTargetPathParent, _ := filepath.Split(absTargetPath) if err := os.MkdirAll(absTargetPathParent, 0700); err != nil { fmt.Fprintf(os.Stderr, "can't create directory %q: \n%v", absTargetPath, err) return 1 } switch { case hostPathInfo == nil || hostPathInfo.IsDir(): if err := os.Mkdir(absTargetPath, 0700); err != nil { fmt.Fprintf(os.Stderr, "can't create directory %q: \n%v", absTargetPath, err) return 1 } case !hostPathInfo.IsDir(): file, err := os.OpenFile(absTargetPath, os.O_CREATE, 0700) if err != nil { fmt.Fprintf(os.Stderr, "can't create file %q: \n%v\n", absTargetPath, err) return 1 } file.Close() } case hostPathInfo != nil: switch { case hostPathInfo.IsDir() && !targetPathInfo.IsDir(): fmt.Fprintf(os.Stderr, "can't mount: %q is a directory while %q is not\n", mount.HostPath, absTargetPath) return 1 case !hostPathInfo.IsDir() && targetPathInfo.IsDir(): fmt.Fprintf(os.Stderr, "can't mount: %q is not a directory while %q is\n", mount.HostPath, absTargetPath) return 1 } } if err := syscall.Mount(mount.HostPath, absTargetPath, mount.Fs, mount.Flags, ""); err != nil { fmt.Fprintf(os.Stderr, "can't mount %q on %q with flags %v: %v\n", mount.HostPath, absTargetPath, mount.Flags, err) return 1 } } if err = stage1common.WritePpid(os.Getpid()); err != nil { fmt.Fprintln(os.Stderr, err.Error()) return 4 } log.Printf("Chroot to %q", rfs) if err := syscall.Chroot(rfs); err != nil { fmt.Fprintf(os.Stderr, "can't chroot: %v\n", err) return 1 } if err := os.Chdir("/"); err != nil { fmt.Fprintf(os.Stderr, "can't change to root new directory: %v\n", err) return 1 } log.Printf("Execing %q in %q", args, rfs) err = stage1common.WithClearedCloExec(lfd, func() error { return syscall.Exec(args[0], args, env) }) if err != nil { fmt.Fprintf(os.Stderr, "can't execute %q: %v\n", args[0], err) return 7 } return 0 }
func stage1() int { uuid, err := types.NewUUID(flag.Arg(0)) if err != nil { fmt.Fprintln(os.Stderr, "UUID is missing or malformed") return 1 } root := "." p, err := LoadPod(root, uuid) if err != nil { fmt.Fprintf(os.Stderr, "Failed to load pod: %v\n", err) return 1 } // set close-on-exec flag on RKT_LOCK_FD so it gets correctly closed when invoking // network plugins lfd, err := common.GetRktLockFD() if err != nil { fmt.Fprintf(os.Stderr, "Failed to get rkt lock fd: %v\n", err) return 1 } if err := sys.CloseOnExec(lfd, true); err != nil { fmt.Fprintf(os.Stderr, "Failed to set FD_CLOEXEC on rkt lock: %v\n", err) return 1 } mirrorLocalZoneInfo(p.Root) if privNet.Any() { fps, err := forwardedPorts(p) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) return 6 } n, err := networking.Setup(root, p.UUID, fps, privNet) if err != nil { fmt.Fprintf(os.Stderr, "Failed to setup network: %v\n", err) return 6 } defer n.Teardown() if err = n.Save(); err != nil { fmt.Fprintf(os.Stderr, "Failed to save networking state %v\n", err) return 6 } hostIP, err := n.GetDefaultHostIP() if err != nil { fmt.Fprintf(os.Stderr, "Failed to get default Host IP: %v\n", err) return 6 } mdsToken, err := generateMDSToken() if err != nil { fmt.Fprintf(os.Stderr, "Failed to generate MDS token: %v", err) return 8 } p.MetadataServiceURL = common.MetadataServicePublicURL(hostIP, mdsToken) if err = registerPod(p, mdsToken); err != nil { fmt.Fprintf(os.Stderr, "Failed to register pod: %v\n", err) return 8 } defer unregisterPod(p) } flavor, systemdStage1Version, err := p.getFlavor() if err != nil { fmt.Fprintf(os.Stderr, "Failed to get stage1 flavor: %v\n", err) return 3 } if err = p.WritePrepareAppTemplate(systemdStage1Version); err != nil { fmt.Fprintf(os.Stderr, "Failed to write prepare-app service template: %v\n", err) return 2 } if err = p.PodToSystemd(interactive); err != nil { fmt.Fprintf(os.Stderr, "Failed to configure systemd: %v\n", err) return 2 } args, env, err := getArgsEnv(p, flavor, systemdStage1Version, debug) if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) return 3 } appHashes := p.GetAppHashes() s1Root := common.Stage1RootfsPath(p.Root) machineID := p.GetMachineID() subcgroup, err := getContainerSubCgroup(machineID) if err == nil { if err := cgroup.CreateCgroups(s1Root, subcgroup, appHashes); err != nil { fmt.Fprintf(os.Stderr, "Error creating cgroups: %v\n", err) return 5 } } else { fmt.Fprintf(os.Stderr, "Continuing with per-app isolators disabled: %v\n", err) } var execFn func() error if privNet.Any() { cmd := exec.Cmd{ Path: args[0], Args: args, Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, Env: env, } execFn = func() error { err = cmd.Start() if err != nil { return fmt.Errorf("Failed to start nspawn: %v\n", err) } if err = writePpid(cmd.Process.Pid); err != nil { return err } return cmd.Wait() } } else { if err = writePpid(os.Getpid()); err != nil { fmt.Fprintln(os.Stderr, err.Error()) return 4 } execFn = func() error { return syscall.Exec(args[0], args, env) } } err = withClearedCloExec(lfd, execFn) if err != nil { fmt.Fprintf(os.Stderr, "Failed to execute nspawn: %v\n", err) return 7 } return 0 }
// Test running pod manifests that contains just one app. // TODO(yifan): Figure out a way to test port mapping on single host. func TestPodManifest(t *testing.T) { ctx := newRktRunCtx() defer ctx.cleanup() tmpdir, err := ioutil.TempDir("", "rkt-tests.") if err != nil { t.Fatalf("Cannot create temporary directory: %v", err) } defer os.RemoveAll(tmpdir) boolFalse, boolTrue := false, true tests := []struct { // [image name]:[image patches] images []imagePatch podManifest *schema.PodManifest shouldSuccess bool expectedResult string cgroup string }{ { // Simple read. []imagePatch{ {"rkt-test-run-pod-manifest-read.aci", []string{}}, }, &schema.PodManifest{ Apps: []schema.RuntimeApp{ { Name: baseAppName, App: &types.App{ Exec: []string{"/inspect", "--read-file"}, User: "******", Group: "0", Environment: []types.EnvironmentVariable{ {"FILE", "/dir1/file"}, }, }, }, }, }, true, "dir1", "", }, { // Simple read after write with volume mounted. []imagePatch{ {"rkt-test-run-pod-manifest-vol-rw.aci", []string{}}, }, &schema.PodManifest{ Apps: []schema.RuntimeApp{ { Name: baseAppName, App: &types.App{ Exec: []string{"/inspect", "--write-file", "--read-file"}, User: "******", Group: "0", Environment: []types.EnvironmentVariable{ {"FILE", "/dir1/file"}, {"CONTENT", "host:foo"}, }, MountPoints: []types.MountPoint{ {"dir1", "/dir1", false}, }, }, }, }, Volumes: []types.Volume{ {"dir1", "host", tmpdir, nil}, }, }, true, "host:foo", "", }, { // Simple read after write with read-only mount point, should fail. []imagePatch{ {"rkt-test-run-pod-manifest-vol-ro.aci", []string{}}, }, &schema.PodManifest{ Apps: []schema.RuntimeApp{ { Name: baseAppName, App: &types.App{ Exec: []string{"/inspect", "--write-file", "--read-file"}, User: "******", Group: "0", Environment: []types.EnvironmentVariable{ {"FILE", "/dir1/file"}, {"CONTENT", "bar"}, }, MountPoints: []types.MountPoint{ {"dir1", "/dir1", true}, }, }, }, }, Volumes: []types.Volume{ {"dir1", "host", tmpdir, nil}, }, }, false, `Cannot write to file "/dir1/file": open /dir1/file: read-only file system`, "", }, { // Simple read after write with volume mounted. // Override the image's mount point spec. This should fail as the volume is // read-only in pod manifest, (which will override the mount point in both image/pod manifest). []imagePatch{ {"rkt-test-run-pod-manifest-vol-rw-override.aci", []string{"--mounts=dir1,path=/dir1,readOnly=false"}}, }, &schema.PodManifest{ Apps: []schema.RuntimeApp{ { Name: baseAppName, App: &types.App{ Exec: []string{"/inspect", "--write-file", "--read-file"}, User: "******", Group: "0", Environment: []types.EnvironmentVariable{ {"FILE", "/dir1/file"}, {"CONTENT", "bar"}, }, MountPoints: []types.MountPoint{ {"dir1", "/dir1", false}, }, }, }, }, Volumes: []types.Volume{ {"dir1", "host", tmpdir, &boolTrue}, }, }, false, `Cannot write to file "/dir1/file": open /dir1/file: read-only file system`, "", }, { // Simple read after write with volume mounted. // Override the image's mount point spec. []imagePatch{ {"rkt-test-run-pod-manifest-vol-rw-override.aci", []string{"--mounts=dir1,path=/dir1,readOnly=true"}}, }, &schema.PodManifest{ Apps: []schema.RuntimeApp{ { Name: baseAppName, App: &types.App{ Exec: []string{"/inspect", "--write-file", "--read-file"}, User: "******", Group: "0", Environment: []types.EnvironmentVariable{ {"FILE", "/dir2/file"}, {"CONTENT", "host:bar"}, }, MountPoints: []types.MountPoint{ {"dir1", "/dir2", false}, }, }, }, }, Volumes: []types.Volume{ {"dir1", "host", tmpdir, nil}, }, }, true, "host:bar", "", }, { // Simple read after write with volume mounted, no apps in pod manifest. []imagePatch{ { "rkt-test-run-pod-manifest-vol-rw-no-app.aci", []string{ "--exec=/inspect --write-file --read-file --file-name=/dir1/file --content=host:baz", "--mounts=dir1,path=/dir1,readOnly=false", }, }, }, &schema.PodManifest{ Apps: []schema.RuntimeApp{ {Name: baseAppName}, }, Volumes: []types.Volume{ {"dir1", "host", tmpdir, nil}, }, }, true, "host:baz", "", }, { // Simple read after write with volume mounted, no apps in pod manifest. // This should succeed even the mount point in image manifest is readOnly, // because it is overrided by the volume's readOnly. []imagePatch{ { "rkt-test-run-pod-manifest-vol-ro-no-app.aci", []string{ "--exec=/inspect --write-file --read-file --file-name=/dir1/file --content=host:zaz", "--mounts=dir1,path=/dir1,readOnly=true", }, }, }, &schema.PodManifest{ Apps: []schema.RuntimeApp{ {Name: baseAppName}, }, Volumes: []types.Volume{ {"dir1", "host", tmpdir, &boolFalse}, }, }, true, "host:zaz", "", }, { // Simple read after write with read-only volume mounted, no apps in pod manifest. // This should fail as the volume is read-only. []imagePatch{ { "rkt-test-run-pod-manifest-vol-ro-no-app.aci", []string{ "--exec=/inspect --write-file --read-file --file-name=/dir1/file --content=baz", "--mounts=dir1,path=/dir1,readOnly=false", }, }, }, &schema.PodManifest{ Apps: []schema.RuntimeApp{ {Name: baseAppName}, }, Volumes: []types.Volume{ {"dir1", "host", tmpdir, &boolTrue}, }, }, false, `Cannot write to file "/dir1/file": open /dir1/file: read-only file system`, "", }, { // Print CPU quota, which should be overwritten by the pod manifest. []imagePatch{ {"rkt-test-run-pod-manifest-cpu-isolator.aci", []string{}}, }, &schema.PodManifest{ Apps: []schema.RuntimeApp{ { Name: baseAppName, App: &types.App{ Exec: []string{"/inspect", "--print-cpuquota"}, User: "******", Group: "0", Isolators: []types.Isolator{ { Name: "resource/cpu", ValueRaw: rawRequestLimit("100", "100"), }, }, }, }, }, }, true, `CPU Quota: 100`, "cpu", }, { // Print memory limit, which should be overwritten by the pod manifest. []imagePatch{ {"rkt-test-run-pod-manifest-memory-isolator.aci", []string{}}, }, &schema.PodManifest{ Apps: []schema.RuntimeApp{ { Name: baseAppName, App: &types.App{ Exec: []string{"/inspect", "--print-memorylimit"}, User: "******", Group: "0", Isolators: []types.Isolator{ { Name: "resource/memory", // 4MB. ValueRaw: rawRequestLimit("4194304", "4194304"), }, }, }, }, }, }, true, `Memory Limit: 4194304`, "memory", }, { // Multiple apps (with same images) in the pod. The first app will read out the content // written by the second app. []imagePatch{ {"rkt-test-run-pod-manifest-app.aci", []string{}}, {"rkt-test-run-pod-manifest-app.aci", []string{}}, }, &schema.PodManifest{ Apps: []schema.RuntimeApp{ { Name: "rkt-inspect-readapp", App: &types.App{ Exec: []string{"/inspect", "--pre-sleep=10", "--read-file"}, User: "******", Group: "0", Environment: []types.EnvironmentVariable{ {"FILE", "/dir/file"}, }, MountPoints: []types.MountPoint{ {"dir", "/dir", false}, }, }, }, { Name: "rkt-inspect-writeapp", App: &types.App{ Exec: []string{"/inspect", "--write-file"}, User: "******", Group: "0", Environment: []types.EnvironmentVariable{ {"FILE", "/dir/file"}, {"CONTENT", "host:foo"}, }, MountPoints: []types.MountPoint{ {"dir", "/dir", false}, }, }, }, }, Volumes: []types.Volume{ {"dir", "host", tmpdir, nil}, }, }, true, "host:foo", "", }, { // Pod manifest overwrites the image's capability. []imagePatch{ {"rkt-test-run-pod-manifest-cap.aci", []string{"--capability=CAP_NET_ADMIN"}}, }, &schema.PodManifest{ Apps: []schema.RuntimeApp{ { Name: baseAppName, App: &types.App{ Exec: []string{"/inspect", "--print-caps-pid=0"}, User: "******", Group: "0", Environment: []types.EnvironmentVariable{ {"CAPABILITY", strconv.Itoa(int(capability.CAP_NET_ADMIN))}, }, }, }, }, }, true, fmt.Sprintf("%v=disabled", capability.CAP_NET_ADMIN.String()), "", }, { // Pod manifest overwrites the image's capability. []imagePatch{ {"rkt-test-run-pod-manifest-cap.aci", []string{"--capability=CAP_NET_BIND_SERVICE"}}, }, &schema.PodManifest{ Apps: []schema.RuntimeApp{ { Name: baseAppName, App: &types.App{ Exec: []string{"/inspect", "--print-caps-pid=0"}, User: "******", Group: "0", Environment: []types.EnvironmentVariable{ {"CAPABILITY", strconv.Itoa(int(capability.CAP_NET_ADMIN))}, }, Isolators: []types.Isolator{ { Name: "os/linux/capabilities-retain-set", ValueRaw: rawValue(fmt.Sprintf(`{"set":["CAP_NET_ADMIN"]}`)), }, }, }, }, }, }, true, fmt.Sprintf("%v=enabled", capability.CAP_NET_ADMIN.String()), "", }, } for i, tt := range tests { if tt.cgroup != "" && !cgroup.IsIsolatorSupported(tt.cgroup) { t.Logf("Skip test #%v: cgroup %s not supported", i, tt.cgroup) continue } for j, v := range tt.images { imageFile := patchTestACI(v.name, v.patches...) hash := importImageAndFetchHash(t, ctx, imageFile) defer os.Remove(imageFile) 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: %v", i, runCmd) child, err := gexpect.Spawn(runCmd) if err != nil { t.Fatalf("Cannot exec rkt #%v: %v", i, err) } if tt.expectedResult != "" { if err := expectWithOutput(child, tt.expectedResult); err != nil { t.Fatalf("Expected %q but not found: %v", tt.expectedResult, err) } } if err := child.Wait(); err != nil { if tt.shouldSuccess { t.Fatalf("rkt didn't terminate correctly: %v", err) } } verifyHostFile(t, tmpdir, "file", i, tt.expectedResult) // 2. Test 'rkt prepare' + 'rkt run-prepared'. cmds := strings.Fields(ctx.cmd()) prepareCmd := exec.Command(cmds[0], cmds[1:]...) prepareArg := fmt.Sprintf("--pod-manifest=%s", manifestFile) prepareCmd.Args = append(prepareCmd.Args, "--insecure-skip-verify", "prepare", prepareArg) output, err := prepareCmd.Output() if err != nil { t.Fatalf("Cannot read the output: %v", err) } podIDStr := strings.TrimSpace(string(output)) podID, err := types.NewUUID(podIDStr) if err != nil { t.Fatalf("%q is not a valid UUID: %v", podIDStr, err) } runPreparedCmd := fmt.Sprintf("%s run-prepared --mds-register=false %s", ctx.cmd(), podID.String()) t.Logf("Running 'run' test #%v: %v", i, runPreparedCmd) child, err = gexpect.Spawn(runPreparedCmd) if err != nil { t.Fatalf("Cannot exec rkt #%v: %v", i, err) } if tt.expectedResult != "" { if err := expectWithOutput(child, tt.expectedResult); err != nil { t.Fatalf("Expected %q but not found: %v", tt.expectedResult, err) } } if err := child.Wait(); err != nil { if tt.shouldSuccess { t.Fatalf("rkt didn't terminate correctly: %v", err) } } verifyHostFile(t, tmpdir, "file", i, tt.expectedResult) } }
// getPod returns a pod struct representing the given pod. // The returned lock is always left in an open but unlocked state. // The pod must be closed using pod.Close() func getPod(uuid string) (*pod, error) { if err := initPods(); err != nil { return nil, err } p := &pod{} u, err := types.NewUUID(uuid) if err != nil { return nil, err } p.uuid = u // we try open the pod in all possible directories, in the same order the states occur l, err := lock.NewLock(p.embryoPath(), lock.Dir) if err == nil { p.isEmbryo = true } else if err == lock.ErrNotExist { l, err = lock.NewLock(p.preparePath(), lock.Dir) if err == nil { // treat as aborted prepare until lock is tested p.isAbortedPrepare = true } else if err == lock.ErrNotExist { l, err = lock.NewLock(p.preparedPath(), lock.Dir) if err == nil { p.isPrepared = true } else if err == lock.ErrNotExist { l, err = lock.NewLock(p.runPath(), lock.Dir) if err == nil { // treat as exited until lock is tested p.isExited = true } else if err == lock.ErrNotExist { l, err = lock.NewLock(p.exitedGarbagePath(), lock.Dir) if err == lock.ErrNotExist { l, err = lock.NewLock(p.garbagePath(), lock.Dir) if err == nil { p.isGarbage = true } else { return nil, fmt.Errorf("pod %q not found", uuid) } } else if err == nil { p.isExitedGarbage = true p.isExited = true // ExitedGarbage is _always_ implicitly exited } } } } } if err != nil && err != lock.ErrNotExist { return nil, fmt.Errorf("error opening pod %q: %v", uuid, err) } if !p.isPrepared && !p.isEmbryo { // preparing, run, exitedGarbage, and garbage dirs use exclusive locks to indicate preparing/aborted, running/exited, and deleting/marked if err = l.TrySharedLock(); err != nil { if err != lock.ErrLocked { l.Close() return nil, fmt.Errorf("unexpected lock error: %v", err) } if p.isExitedGarbage { // locked exitedGarbage is also being deleted p.isExitedDeleting = true } else if p.isExited { // locked exited and !exitedGarbage is not exited (default in the run dir) p.isExited = false } else if p.isAbortedPrepare { // locked in preparing is preparing, not aborted (default in the preparing dir) p.isAbortedPrepare = false p.isPreparing = true } else if p.isGarbage { // locked in non-exited garbage is deleting p.isDeleting = true } err = nil } else { l.Unlock() } } p.FileLock = l if p.isRunning() { cfd, err := p.Fd() if err != nil { return nil, fmt.Errorf("error acquiring pod %v dir fd: %v", uuid, err) } p.nets, err = netinfo.LoadAt(cfd) // ENOENT is ok -- assume running without --private-net if err != nil && !os.IsNotExist(err) { return nil, fmt.Errorf("error opening pod %v netinfo: %v", uuid, err) } } return p, nil }