// FetchImage will take an image as either a path, a URL or a name // string and import it into the store if found. If ascPath is not "", // it must exist as a local file and will be used as the signature // file for verification, unless verification is disabled. If // f.WithDeps is true also image dependencies are fetched. func (f *Fetcher) FetchImage(d dist.Distribution, image, ascPath string) (*types.Hash, error) { ensureLogger(f.Debug) db := &distBundle{ dist: d, image: image, } a := f.getAsc(ascPath) hash, err := f.fetchSingleImage(db, a) if err != nil { return nil, err } if f.WithDeps { err = f.fetchImageDeps(hash) if err != nil { return nil, err } } // we need to be able to do a chroot and access to the tree store // directories, we need to // 1) check if the system supports OverlayFS // 2) check if we're root if common.SupportsOverlay() == nil && os.Geteuid() == 0 { if _, _, err := f.Ts.Render(hash, false); err != nil { return nil, errwrap.Wrap(errors.New("error rendering tree store"), err) } } h, err := types.NewHash(hash) if err != nil { // should never happen log.PanicE("invalid hash", err) } return h, nil }
// FetchImage will take an image as either a path, a URL or a name // string and import it into the store if found. If ascPath is not "", // it must exist as a local file and will be used as the signature // file for verification, unless verification is disabled. If // f.WithDeps is true also image dependencies are fetched. func (f *Fetcher) FetchImage(img string, ascPath string, imgType apps.AppImageType) (string, error) { ensureLogger(f.Debug) a := f.getAsc(ascPath) hash, err := f.fetchSingleImage(img, a, imgType) if err != nil { return "", err } if f.WithDeps { err = f.fetchImageDeps(hash) if err != nil { return "", err } } // we need to be able to do a chroot and access to the tree store // directories, we need to // 1) check if the system supports OverlayFS // 2) check if we're root if common.SupportsOverlay() == nil && os.Geteuid() == 0 { if _, _, err := f.Ts.Render(hash, false); err != nil { return "", errwrap.Wrap(errors.New("error rendering tree store"), err) } } return hash, nil }
func TestExport(t *testing.T) { if !common.SupportsOverlay() { t.Skip("Overlay fs not supported.") } // TODO(iaguis): we need a new function to unmount the fly pod so we can also test // overlaySimulateReboot NewTestExport(overlaySimpleTest).Execute(t) }
func TestExport(t *testing.T) { if err := common.SupportsOverlay(); err != nil { t.Skipf("Overlay fs not supported: %v", err) } ctx := testutils.NewRktRunCtx() defer ctx.Cleanup() // TODO(iaguis): we need a new function to unmount the fly pod so we can also test // overlaySimulateReboot exportTestCases["overlaySimpleTest"].Execute(t, ctx) }
func TestImageGCTreeStore(t *testing.T) { ctx := testutils.NewRktRunCtx() defer ctx.Cleanup() expectedTreeStores := 2 // If overlayfs is not supported only the stage1 image is rendered in the treeStore if !common.SupportsOverlay() { expectedTreeStores = 1 } // at this point we know that RKT_INSPECT_IMAGE env var is not empty referencedACI := os.Getenv("RKT_INSPECT_IMAGE") cmd := fmt.Sprintf("%s --insecure-options=image run --mds-register=false %s", ctx.Cmd(), referencedACI) t.Logf("Running %s: %v", referencedACI, cmd) child, err := gexpect.Spawn(cmd) if err != nil { t.Fatalf("Cannot exec: %v", err) } if err := child.Wait(); err != nil { t.Fatalf("rkt didn't terminate correctly: %v", err) } treeStoreIDs, err := getTreeStoreIDs(ctx) if err != nil { t.Fatalf("unexpected error: %v", err) } // We expect 2 treeStoreIDs for stage1 and app (only 1 if overlay is not supported/enabled) if len(treeStoreIDs) != expectedTreeStores { t.Fatalf("expected %d entries in the treestore but found %d entries", expectedTreeStores, len(treeStoreIDs)) } runImageGC(t, ctx) treeStoreIDs, err = getTreeStoreIDs(ctx) if err != nil { t.Fatalf("unexpected error: %v", err) } // We expect 1/2 treeStoreIDs again as no pod gc has been executed if len(treeStoreIDs) != expectedTreeStores { t.Fatalf("expected %d entries in the treestore but found %d entries", expectedTreeStores, len(treeStoreIDs)) } runGC(t, ctx) runImageGC(t, ctx) treeStoreIDs, err = getTreeStoreIDs(ctx) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(treeStoreIDs) != 0 { t.Fatalf("expected empty treestore but found %d entries", len(treeStoreIDs)) } }
func preparedWithOverlay(dir string) (bool, error) { _, err := os.Stat(filepath.Join(dir, common.OverlayPreparedFilename)) if os.IsNotExist(err) { return false, nil } if err != nil { return false, err } if !common.SupportsOverlay() { return false, fmt.Errorf("the pod was prepared with overlay but overlay is not supported") } return true, nil }
func TestExport(t *testing.T) { testCases := []ExportTestCase{noOverlaySimpleTest} // Need to do both checks if common.SupportsUserNS() && checkUserNS() == nil { testCases = append(testCases, userNS) } if common.SupportsOverlay() { testCases = append(testCases, overlaySimpleTest) testCases = append(testCases, overlaySimulateReboot) } NewTestExport(testCases...).Execute(t) }
func TestRenderOnFetch(t *testing.T) { // If overlayfs is not supported, we don't render images on fetch if !common.SupportsOverlay() { t.Skip("Overlay fs not supported.") } ctx := testutils.NewRktRunCtx() defer ctx.Cleanup() server := runServer(t, taas.GetDefaultServerSetup()) defer server.Close() fileSet, imageList := generateComplexDependencyTree(t, ctx) for _, img := range imageList { defer os.Remove(img.fileName) } if err := server.UpdateFileSet(fileSet); err != nil { t.Fatalf("Failed to populate a file list in test aci server: %v", err) } for i := len(imageList) - 1; i >= 0; i-- { img := imageList[i] if img.prefetch { t.Logf("Importing image %q: %q", img.imageName, img.fileName) testImageShortHash, err := importImageAndFetchHash(t, ctx, "", img.fileName) if err != nil { t.Fatalf("%v", err) } t.Logf("Imported image %q: %s", img.imageName, testImageShortHash) } } fetchCmd := fmt.Sprintf("%s --debug --insecure-options=image,tls fetch %s", ctx.Cmd(), topImage) child := spawnOrFail(t, fetchCmd) treeStoreDir := filepath.Join(ctx.DataDir(), "cas", "tree") trees, err := ioutil.ReadDir(treeStoreDir) if err != nil { panic(fmt.Sprintf("Cannot read tree store dir: %v", err)) } // We expect 2 trees: stage1 and the image if len(trees) != 2 { t.Fatalf("Expected 2 trees but found %d", len(trees)) } waitOrFail(t, child, 0) }
func TestExport(t *testing.T) { ctx := testutils.NewRktRunCtx() defer ctx.Cleanup() overlay := (common.SupportsOverlay() == nil) userns := (common.SupportsUserNS() && checkUserNS() == nil && !TestedFlavor.Kvm) for name, testCase := range exportTestCases { if testCase.NeedsOverlay && !overlay { t.Logf("TestExport/%v needs overlay, skipping", name) continue } if testCase.NeedsUserNS && !userns { t.Logf("TestExport/%v needs userns, skipping", name) continue } t.Logf("TestExport/%v", name) testCase.Execute(t, ctx) } }
// FetchImage will take an image as either a path, a URL or a name // string and import it into the store if found. If ascPath is not "", // it must exist as a local file and will be used as the signature // file for verification, unless verification is disabled. If // f.WithDeps is true also image dependencies are fetched. func (f *Fetcher) FetchImage(img string, ascPath string, imgType apps.AppImageType) (string, error) { ensureLogger(f.Debug) a := f.getAsc(ascPath) hash, err := f.fetchSingleImage(img, a, imgType) if err != nil { return "", err } if f.WithDeps { err = f.fetchImageDeps(hash) if err != nil { return "", err } } if common.SupportsOverlay() { if _, _, err := f.S.RenderTreeStore(hash, false); err != nil { return "", errwrap.Wrap(errors.New("error rendering tree store"), err) } } return hash, nil }
func TestGCAfterUnmount(t *testing.T) { if err := common.SupportsOverlay(); err != nil { t.Skipf("Overlay fs not supported: %v", err) } ctx := testutils.NewRktRunCtx() defer ctx.Cleanup() imagePath := getInspectImagePath() for _, rmNetns := range []bool{false, true} { _, err := importImageAndFetchHash(t, ctx, "", imagePath) if err != nil { t.Fatalf("%v", err) } cmd := fmt.Sprintf("%s --insecure-options=image prepare %s", ctx.Cmd(), imagePath) uuid := runRktAndGetUUID(t, cmd) cmd = fmt.Sprintf("%s run-prepared %s", ctx.Cmd(), uuid) runRktAndCheckOutput(t, cmd, "", false) unmountPod(t, ctx, uuid, rmNetns) pods := podsRemaining(t, ctx) if len(pods) == 0 { t.Fatalf("pods should still be present in rkt's data directory") } gcCmd := fmt.Sprintf("%s gc --debug --mark-only=false --expire-prepared=0 --grace-period=0", ctx.Cmd()) // check we don't get any output (an error) after "executing net-plugin..." runRktAndCheckRegexOutput(t, gcCmd, `executing net-plugin .*\n\z`) pods = podsRemaining(t, ctx) if len(pods) != 0 { t.Fatalf("no pods should exist rkt's data directory, but found: %v", pods) } } }
func runPrepare(cmd *cobra.Command, args []string) (exit int) { var err error origStdout := os.Stdout if flagQuiet { if os.Stdout, err = os.Open("/dev/null"); err != nil { stderr("prepare: unable to open /dev/null") return 1 } } if err = parseApps(&rktApps, args, cmd.Flags(), true); err != nil { stderr("prepare: error parsing app image arguments: %v", err) return 1 } if len(flagPodManifest) > 0 && (len(flagVolumes) > 0 || len(flagPorts) > 0 || flagInheritEnv || !flagExplicitEnv.IsEmpty() || flagLocal) { stderr("prepare: conflicting flags set with --pod-manifest (see --help)") return 1 } if rktApps.Count() < 1 && len(flagPodManifest) == 0 { stderr("prepare: must provide at least one image or specify the pod manifest") return 1 } if globalFlags.Dir == "" { log.Printf("dir unset - using temporary directory") globalFlags.Dir, err = ioutil.TempDir("", "rkt") if err != nil { stderr("prepare: error creating temporary directory: %v", err) return 1 } } s, err := store.NewStore(globalFlags.Dir) if err != nil { stderr("prepare: cannot open store: %v", err) return 1 } config, err := getConfig() if err != nil { stderr("prepare: cannot get configuration: %v", err) return 1 } fn := &finder{ imageActionData: imageActionData{ s: s, headers: config.AuthPerHost, dockerAuth: config.DockerCredentialsPerRegistry, insecureSkipVerify: globalFlags.InsecureSkipVerify, debug: globalFlags.Debug, }, local: flagLocal, withDeps: false, } s1img, err := getStage1Hash(s, flagStage1Image) if err != nil { stderr("prepare: %v", err) return 1 } fn.ks = getKeystore() fn.withDeps = true if err := fn.findImages(&rktApps); err != nil { stderr("%v", err) return 1 } p, err := newPod() if err != nil { stderr("prepare: error creating new pod: %v", err) return 1 } cfg := stage0.CommonConfig{ Store: s, Stage1Image: *s1img, UUID: p.uuid, Debug: globalFlags.Debug, } pcfg := stage0.PrepareConfig{ CommonConfig: cfg, UseOverlay: !flagNoOverlay && common.SupportsOverlay(), } if len(flagPodManifest) > 0 { pcfg.PodManifest = flagPodManifest } else { pcfg.Volumes = []types.Volume(flagVolumes) pcfg.Ports = []types.ExposedPort(flagPorts) pcfg.InheritEnv = flagInheritEnv pcfg.ExplicitEnv = flagExplicitEnv.Strings() pcfg.Apps = &rktApps } if err = stage0.Prepare(pcfg, p.path(), p.uuid); err != nil { stderr("prepare: error setting up stage0: %v", err) return 1 } if err := p.sync(); err != nil { stderr("prepare: error syncing pod data: %v", err) return 1 } if err := p.xToPrepared(); err != nil { stderr("prepare: error transitioning to prepared: %v", err) return 1 } os.Stdout = origStdout // restore output in case of --quiet stdout("%s", p.uuid.String()) return 0 }
func runRun(cmd *cobra.Command, args []string) (exit int) { privateUsers := user.NewBlankUidRange() err := parseApps(&rktApps, args, cmd.Flags(), true) if err != nil { stderr.PrintE("error parsing app image arguments", err) return 1 } if flagStoreOnly && flagNoStore { stderr.Print("both --store-only and --no-store specified") return 1 } if flagPrivateUsers { if !common.SupportsUserNS() { stderr.Print("--private-users is not supported, kernel compiled without user namespace support") return 1 } privateUsers.SetRandomUidRange(user.DefaultRangeCount) } if len(flagPorts) > 0 && flagNet.None() { stderr.Print("--port flag does not work with 'none' networking") return 1 } if len(flagPorts) > 0 && flagNet.Host() { stderr.Print("--port flag does not work with 'host' networking") return 1 } if flagMDSRegister && flagNet.None() { stderr.Print("--mds-register flag does not work with --net=none. Please use 'host', 'default' or an equivalent network") return 1 } if len(flagPodManifest) > 0 && (len(flagPorts) > 0 || rktApps.Count() > 0 || flagStoreOnly || flagNoStore || flagInheritEnv || !flagExplicitEnv.IsEmpty() || !flagEnvFromFile.IsEmpty() || (*appsVolume)(&rktApps).String() != "" || (*appMount)(&rktApps).String() != "" || (*appExec)(&rktApps).String() != "" || (*appUser)(&rktApps).String() != "" || (*appGroup)(&rktApps).String() != "" || (*appCapsRetain)(&rktApps).String() != "" || (*appCapsRemove)(&rktApps).String() != "") { stderr.Print("conflicting flags set with --pod-manifest (see --help)") return 1 } if flagInteractive && rktApps.Count() > 1 { stderr.Print("interactive option only supports one image") return 1 } if rktApps.Count() < 1 && len(flagPodManifest) == 0 { stderr.Print("must provide at least one image or specify the pod manifest") return 1 } s, err := imagestore.NewStore(storeDir()) if err != nil { stderr.PrintE("cannot open store", err) return 1 } ts, err := treestore.NewStore(treeStoreDir(), s) if err != nil { stderr.PrintE("cannot open treestore", err) return 1 } config, err := getConfig() if err != nil { stderr.PrintE("cannot get configuration", err) return 1 } s1img, err := getStage1Hash(s, ts, config) if err != nil { stderr.Error(err) return 1 } fn := &image.Finder{ S: s, Ts: ts, Ks: getKeystore(), Headers: config.AuthPerHost, DockerAuth: config.DockerCredentialsPerRegistry, InsecureFlags: globalFlags.InsecureFlags, Debug: globalFlags.Debug, TrustKeysFromHTTPS: globalFlags.TrustKeysFromHTTPS, StoreOnly: flagStoreOnly, NoStore: flagNoStore, WithDeps: true, } if err := fn.FindImages(&rktApps); err != nil { stderr.Error(err) return 1 } p, err := newPod() if err != nil { stderr.PrintE("error creating new pod", err) return 1 } // if requested, write out pod UUID early so "rkt rm" can // clean it up even if something goes wrong if flagUUIDFileSave != "" { if err := writeUUIDToFile(p.uuid, flagUUIDFileSave); err != nil { stderr.PrintE("error saving pod UUID to file", err) return 1 } } processLabel, mountLabel, err := label.InitLabels([]string{"mcsdir:/var/run/rkt/mcs"}) if err != nil { stderr.PrintE("error initialising SELinux", err) return 1 } p.mountLabel = mountLabel cfg := stage0.CommonConfig{ MountLabel: mountLabel, ProcessLabel: processLabel, Store: s, TreeStore: ts, Stage1Image: *s1img, UUID: p.uuid, Debug: globalFlags.Debug, } pcfg := stage0.PrepareConfig{ CommonConfig: &cfg, UseOverlay: !flagNoOverlay && common.SupportsOverlay() && common.FSSupportsOverlay(getDataDir()), PrivateUsers: privateUsers, SkipTreeStoreCheck: globalFlags.InsecureFlags.SkipOnDiskCheck(), } if len(flagPodManifest) > 0 { pcfg.PodManifest = flagPodManifest } else { pcfg.Ports = []types.ExposedPort(flagPorts) pcfg.InheritEnv = flagInheritEnv pcfg.ExplicitEnv = flagExplicitEnv.Strings() pcfg.EnvFromFile = flagEnvFromFile.Strings() pcfg.Apps = &rktApps } if globalFlags.Debug { stage0.InitDebug() } keyLock, err := lock.SharedKeyLock(lockDir(), common.PrepareLock) if err != nil { stderr.PrintE("cannot get shared prepare lock", err) return 1 } err = stage0.Prepare(pcfg, p.path(), p.uuid) if err != nil { stderr.PrintE("error setting up stage0", err) keyLock.Close() return 1 } keyLock.Close() // get the lock fd for run lfd, err := p.Fd() if err != nil { stderr.PrintE("error getting pod lock fd", err) return 1 } // skip prepared by jumping directly to run, we own this pod if err := p.xToRun(); err != nil { stderr.PrintE("unable to transition to run", err) return 1 } rktgid, err := common.LookupGid(common.RktGroup) if err != nil { stderr.Printf("group %q not found, will use default gid when rendering images", common.RktGroup) rktgid = -1 } rcfg := stage0.RunConfig{ CommonConfig: &cfg, Net: flagNet, LockFd: lfd, Interactive: flagInteractive, DNS: flagDNS, DNSSearch: flagDNSSearch, DNSOpt: flagDNSOpt, MDSRegister: flagMDSRegister, LocalConfig: globalFlags.LocalConfigDir, RktGid: rktgid, Hostname: flagHostname, } apps, err := p.getApps() if err != nil { stderr.PrintE("cannot get the appList in the pod manifest", err) return 1 } rcfg.Apps = apps stage0.Run(rcfg, p.path(), getDataDir()) // execs, never returns return 1 }
func runRun(cmd *cobra.Command, args []string) (exit int) { privateUsers := uid.NewBlankUidRange() err := parseApps(&rktApps, args, cmd.Flags(), true) if err != nil { stderr("run: error parsing app image arguments: %v", err) return 1 } if flagStoreOnly && flagNoStore { stderr("both --store-only and --no-store specified") return 1 } if flagPrivateUsers { if !common.SupportsUserNS() { stderr("run: --private-users is not supported, kernel compiled without user namespace support") return 1 } privateUsers.SetRandomUidRange(uid.DefaultRangeCount) } if len(flagPorts) > 0 && flagNet.None() { stderr("--port flag does not work with 'none' networking") return 1 } if len(flagPorts) > 0 && flagNet.Host() { stderr("--port flag does not work with 'host' networking") return 1 } if flagMDSRegister && flagNet.None() { stderr("--mds-register flag does not work with --net=none. Please use 'host', 'default' or an equivalent network") return 1 } if len(flagPodManifest) > 0 && (len(flagPorts) > 0 || flagInheritEnv || !flagExplicitEnv.IsEmpty() || rktApps.Count() > 0 || flagStoreOnly || flagNoStore) { stderr("conflicting flags set with --pod-manifest (see --help)") return 1 } if flagInteractive && rktApps.Count() > 1 { stderr("run: interactive option only supports one image") return 1 } if rktApps.Count() < 1 && len(flagPodManifest) == 0 { stderr("run: must provide at least one image or specify the pod manifest") return 1 } s, err := store.NewStore(globalFlags.Dir) if err != nil { stderr("run: cannot open store: %v", err) return 1 } config, err := getConfig() if err != nil { stderr("run: cannot get configuration: %v", err) return 1 } fn := &finder{ imageActionData: imageActionData{ s: s, headers: config.AuthPerHost, dockerAuth: config.DockerCredentialsPerRegistry, insecureSkipVerify: globalFlags.InsecureSkipVerify, debug: globalFlags.Debug, }, storeOnly: flagStoreOnly, noStore: flagNoStore, withDeps: false, } s1img, err := getStage1Hash(s, cmd) if err != nil { stderr("%v", err) return 1 } fn.ks = getKeystore() fn.withDeps = true if err := fn.findImages(&rktApps); err != nil { stderr("%v", err) return 1 } p, err := newPod() if err != nil { stderr("Error creating new pod: %v", err) return 1 } // if requested, write out pod UUID early so "rkt rm" can // clean it up even if something goes wrong if flagUUIDFileSave != "" { if err := writeUUIDToFile(p.uuid, flagUUIDFileSave); err != nil { stderr("Error saving pod UUID to file: %v", err) return 1 } } processLabel, mountLabel, err := label.InitLabels(nil) if err != nil { stderr("Error initialising SELinux: %v", err) return 1 } cfg := stage0.CommonConfig{ MountLabel: mountLabel, ProcessLabel: processLabel, Store: s, Stage1Image: *s1img, UUID: p.uuid, Debug: globalFlags.Debug, } pcfg := stage0.PrepareConfig{ CommonConfig: cfg, UseOverlay: !flagNoOverlay && common.SupportsOverlay(), PrivateUsers: privateUsers, } if len(flagPodManifest) > 0 { pcfg.PodManifest = flagPodManifest } else { pcfg.Ports = []types.ExposedPort(flagPorts) pcfg.InheritEnv = flagInheritEnv pcfg.ExplicitEnv = flagExplicitEnv.Strings() pcfg.Apps = &rktApps } if globalFlags.Debug { stage0.InitDebug() } keyLock, err := lock.SharedKeyLock(lockDir(), common.PrepareLock) if err != nil { stderr("rkt: cannot get shared prepare lock: %v", err) return 1 } err = stage0.Prepare(pcfg, p.path(), p.uuid) if err != nil { stderr("run: error setting up stage0: %v", err) keyLock.Close() return 1 } keyLock.Close() // get the lock fd for run lfd, err := p.Fd() if err != nil { stderr("Error getting pod lock fd: %v", err) return 1 } // skip prepared by jumping directly to run, we own this pod if err := p.xToRun(); err != nil { stderr("run: unable to transition to run: %v", err) return 1 } rktgid, err := common.LookupGid(common.RktGroup) if err != nil { stderr("run: group %q not found, will use default gid when rendering images", common.RktGroup) rktgid = -1 } rcfg := stage0.RunConfig{ CommonConfig: cfg, Net: flagNet, LockFd: lfd, Interactive: flagInteractive, MDSRegister: flagMDSRegister, LocalConfig: globalFlags.LocalConfigDir, RktGid: rktgid, } apps, err := p.getApps() if err != nil { stderr("run: cannot get the appList in the pod manifest: %v", err) return 1 } rcfg.Apps = apps stage0.Run(rcfg, p.path(), globalFlags.Dir) // execs, never returns return 1 }
func TestImageSize(t *testing.T) { ctx := testutils.NewRktRunCtx() defer ctx.Cleanup() image := patchTestACI("rkt-size.aci", "--no-compression", "--name=size-test") defer os.Remove(image) imageHash := "sha512-" + getHashOrPanic(image)[:64] fi, err := os.Stat(image) if err != nil { t.Fatalf("cannot stat image %q: %v", image, err) } imageSize := fi.Size() fetchCmd := fmt.Sprintf("%s --insecure-options=image fetch %s", ctx.Cmd(), image) spawnAndWaitOrFail(t, fetchCmd, 0) imageListCmd := fmt.Sprintf("%s image list --no-legend --full", ctx.Cmd()) // if we don't support overlay fs, we don't render the image on fetch if !common.SupportsOverlay() { // check that the printed size is the same as the actual image size expectedStr := fmt.Sprintf("(?s)%s.*%d.*", imageHash, imageSize) runRktAndCheckRegexOutput(t, imageListCmd, expectedStr) // run the image, so rkt renders it in the tree store runCmd := fmt.Sprintf("%s --insecure-options=image run %s", ctx.Cmd(), image) spawnAndWaitOrFail(t, runCmd, 0) } tmpDir := createTempDirOrPanic("rkt_image_list_test") defer os.RemoveAll(tmpDir) imageRenderCmd := fmt.Sprintf("%s image render --overwrite %s %s", ctx.Cmd(), imageHash, tmpDir) spawnAndWaitOrFail(t, imageRenderCmd, 0) /* recreate the tree store directory contents to get an accurate size: - hash file - image file - rendered file NOTE: if/when we add new files to the tree store directory, this test will fail and will need an update. */ if err := ioutil.WriteFile(filepath.Join(tmpDir, "hash"), []byte(imageHash), 0600); err != nil { t.Fatalf(`error writing "hash" file: %v`, err) } if err := ioutil.WriteFile(filepath.Join(tmpDir, "image"), []byte(imageHash), 0600); err != nil { t.Fatalf(`error writing "image" file: %v`, err) } if err := ioutil.WriteFile(filepath.Join(tmpDir, "rendered"), []byte{}, 0600); err != nil { t.Fatalf(`error writing "rendered" file: %v`, err) } tsSize, err := fileutil.DirSize(tmpDir) if err != nil { t.Fatalf("error calculating rendered size: %v", err) } // check the size with the rendered image expectedStr := fmt.Sprintf("(?s)%s.*%d.*", imageHash, imageSize+tsSize) runRktAndCheckRegexOutput(t, imageListCmd, expectedStr) // gc the pod gcCmd := fmt.Sprintf("%s gc --grace-period=0s", ctx.Cmd()) spawnAndWaitOrFail(t, gcCmd, 0) // image gc to remove the tree store imageGCCmd := fmt.Sprintf("%s image gc", ctx.Cmd()) spawnAndWaitOrFail(t, imageGCCmd, 0) // check that the size goes back to the original (only the image size) expectedStr = fmt.Sprintf("(?s)%s.*%d.*", imageHash, imageSize) runRktAndCheckRegexOutput(t, imageListCmd, expectedStr) }
func runRun(cmd *cobra.Command, args []string) (exit int) { err := parseApps(&rktApps, args, cmd.Flags(), true) if err != nil { stderr("run: error parsing app image arguments: %v", err) return 1 } if len(flagPorts) > 0 && !flagPrivateNet.Any() { stderr("--port flag requires --private-net") return 1 } if len(flagPodManifest) > 0 && (len(flagVolumes) > 0 || len(flagPorts) > 0 || flagInheritEnv || !flagExplicitEnv.IsEmpty() || rktApps.Count() > 0 || flagLocal) { stderr("conflicting flags set with --pod-manifest (see --help)") return 1 } if globalFlags.Dir == "" { log.Printf("dir unset - using temporary directory") var err error globalFlags.Dir, err = ioutil.TempDir("", "rkt") if err != nil { stderr("error creating temporary directory: %v", err) return 1 } } if flagInteractive && rktApps.Count() > 1 { stderr("run: interactive option only supports one image") return 1 } if rktApps.Count() < 1 && len(flagPodManifest) == 0 { stderr("run: must provide at least one image or specify the pod manifest") return 1 } s, err := store.NewStore(globalFlags.Dir) if err != nil { stderr("run: cannot open store: %v", err) return 1 } config, err := getConfig() if err != nil { stderr("run: cannot get configuration: %v", err) return 1 } fn := &finder{ imageActionData: imageActionData{ s: s, headers: config.AuthPerHost, dockerAuth: config.DockerCredentialsPerRegistry, insecureSkipVerify: globalFlags.InsecureSkipVerify, debug: globalFlags.Debug, }, local: flagLocal, withDeps: false, } s1img, err := fn.findImage(flagStage1Image, "", false) if err != nil { stderr("Error finding stage1 image %q: %v", flagStage1Image, err) return 1 } fn.ks = getKeystore() fn.withDeps = true if err := fn.findImages(&rktApps); err != nil { stderr("%v", err) return 1 } p, err := newPod() if err != nil { stderr("Error creating new pod: %v", err) return 1 } processLabel, mountLabel, err := label.InitLabels(nil) if err != nil { stderr("Error initialising SELinux: %v", err) return 1 } cfg := stage0.CommonConfig{ MountLabel: mountLabel, ProcessLabel: processLabel, Store: s, Stage1Image: *s1img, UUID: p.uuid, Debug: globalFlags.Debug, } pcfg := stage0.PrepareConfig{ CommonConfig: cfg, UseOverlay: !flagNoOverlay && common.SupportsOverlay(), } if len(flagPodManifest) > 0 { pcfg.PodManifest = flagPodManifest } else { pcfg.Volumes = []types.Volume(flagVolumes) pcfg.Ports = []types.ExposedPort(flagPorts) pcfg.InheritEnv = flagInheritEnv pcfg.ExplicitEnv = flagExplicitEnv.Strings() pcfg.Apps = &rktApps } err = stage0.Prepare(pcfg, p.path(), p.uuid) if err != nil { stderr("run: error setting up stage0: %v", err) return 1 } // get the lock fd for run lfd, err := p.Fd() if err != nil { stderr("Error getting pod lock fd: %v", err) return 1 } // skip prepared by jumping directly to run, we own this pod if err := p.xToRun(); err != nil { stderr("run: unable to transition to run: %v", err) return 1 } rcfg := stage0.RunConfig{ CommonConfig: cfg, PrivateNet: flagPrivateNet, LockFd: lfd, Interactive: flagInteractive, MDSRegister: flagMDSRegister, LocalConfig: globalFlags.LocalConfigDir, } imgs, err := p.getApps() if err != nil { stderr("run: cannot get the image hashes in the pod manifest: %v", err) return 1 } rcfg.Images = imgs stage0.Run(rcfg, p.path()) // execs, never returns return 1 }
func runPrepare(cmd *cobra.Command, args []string) (exit int) { var err error origStdout := os.Stdout privateUsers := uid.NewBlankUidRange() if flagQuiet { if os.Stdout, err = os.Open("/dev/null"); err != nil { stderr("prepare: unable to open /dev/null: %v", err) return 1 } } if flagStoreOnly && flagNoStore { stderr("both --store-only and --no-store specified") return 1 } if flagPrivateUsers { if !common.SupportsUserNS() { stderr("prepare: --private-users is not supported, kernel compiled without user namespace support") return 1 } privateUsers.SetRandomUidRange(uid.DefaultRangeCount) } if err = parseApps(&rktApps, args, cmd.Flags(), true); err != nil { stderr("prepare: error parsing app image arguments: %v", err) return 1 } if len(flagPodManifest) > 0 && (len(flagPorts) > 0 || flagInheritEnv || !flagExplicitEnv.IsEmpty() || flagStoreOnly || flagNoStore) { stderr("prepare: conflicting flags set with --pod-manifest (see --help)") return 1 } if rktApps.Count() < 1 && len(flagPodManifest) == 0 { stderr("prepare: must provide at least one image or specify the pod manifest") return 1 } s, err := store.NewStore(getDataDir()) if err != nil { stderr("prepare: cannot open store: %v", err) return 1 } config, err := getConfig() if err != nil { stderr("prepare: cannot get configuration: %v", err) return 1 } s1img, err := getStage1Hash(s, cmd) if err != nil { stderr("prepare: %v", err) return 1 } fn := &image.Finder{ S: s, Ks: getKeystore(), Headers: config.AuthPerHost, DockerAuth: config.DockerCredentialsPerRegistry, InsecureFlags: globalFlags.InsecureFlags, Debug: globalFlags.Debug, TrustKeysFromHttps: globalFlags.TrustKeysFromHttps, StoreOnly: flagStoreOnly, NoStore: flagNoStore, WithDeps: true, } if err := fn.FindImages(&rktApps); err != nil { stderr("prepare: %v", err) return 1 } p, err := newPod() if err != nil { stderr("prepare: error creating new pod: %v", err) return 1 } cfg := stage0.CommonConfig{ Store: s, Stage1Image: *s1img, UUID: p.uuid, Debug: globalFlags.Debug, } pcfg := stage0.PrepareConfig{ CommonConfig: &cfg, UseOverlay: !flagNoOverlay && common.SupportsOverlay(), PrivateUsers: privateUsers, } if len(flagPodManifest) > 0 { pcfg.PodManifest = flagPodManifest } else { pcfg.Ports = []types.ExposedPort(flagPorts) pcfg.InheritEnv = flagInheritEnv pcfg.ExplicitEnv = flagExplicitEnv.Strings() pcfg.Apps = &rktApps } if globalFlags.Debug { stage0.InitDebug() } keyLock, err := lock.SharedKeyLock(lockDir(), common.PrepareLock) if err != nil { stderr("rkt: cannot get shared prepare lock: %v", err) return 1 } if err = stage0.Prepare(pcfg, p.path(), p.uuid); err != nil { stderr("prepare: error setting up stage0: %v", err) keyLock.Close() return 1 } keyLock.Close() if err := p.sync(); err != nil { stderr("prepare: error syncing pod data: %v", err) return 1 } if err := p.xToPrepared(); err != nil { stderr("prepare: error transitioning to prepared: %v", err) return 1 } os.Stdout = origStdout // restore output in case of --quiet stdout("%s", p.uuid.String()) return 0 }