func TestGetStoreKeyFromApp(t *testing.T) { dir, err := ioutil.TempDir("", "fetch-image") if err != nil { t.Fatalf("error creating tempdir: %v", err) } defer os.RemoveAll(dir) s, err := imagestore.NewStore(dir) if err != nil { t.Fatalf("unexpected error %v", err) } defer s.Dump(false) // Test an aci without os and arch labels a, err := aci.NewBasicACI(dir, "example.com/app") defer a.Close() if err != nil { t.Fatalf("unexpected error %v", err) } // Rewind the ACI if _, err := a.Seek(0, 0); err != nil { t.Fatalf("unexpected error %v", err) } _, err = s.WriteACI(a, imagestore.ACIFetchInfo{Latest: false}) if err != nil { t.Fatalf("unexpected error %v", err) } _, err = getStoreKeyFromApp(s, "example.com/app") if err != nil { t.Fatalf("unexpected error: %v", err) } }
func runGCImage(cmd *cobra.Command, args []string) (exit int) { 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 store", err) return } if err := gcTreeStore(ts); err != nil { stderr.PrintE("failed to remove unreferenced treestores", err) return 1 } if err := gcStore(s, flagImageGracePeriod); err != nil { stderr.Error(err) return 1 } return 0 }
func runImageExport(cmd *cobra.Command, args []string) (exit int) { if len(args) != 2 { cmd.Usage() return 1 } s, err := imagestore.NewStore(storeDir()) if err != nil { stderr.PrintE("cannot open store", err) return 1 } key, err := getStoreKeyFromAppOrHash(s, args[0]) if err != nil { stderr.Error(err) return 1 } aci, err := s.ReadStream(key) if err != nil { stderr.PrintE("error reading image", err) return 1 } defer aci.Close() mode := os.O_CREATE | os.O_WRONLY if flagOverwriteACI { mode |= os.O_TRUNC } else { mode |= os.O_EXCL } f, err := os.OpenFile(args[1], mode, 0644) if err != nil { if os.IsExist(err) { stderr.Print("output ACI file exists (try --overwrite)") } else { stderr.PrintE(fmt.Sprintf("unable to open output ACI file %s", args[1]), err) } return 1 } defer func() { err := f.Close() if err != nil { stderr.PrintE("error closing output ACI file", err) exit = 1 } }() _, err = io.Copy(f, aci) if err != nil { stderr.PrintE("error writing to output ACI file", err) return 1 } return 0 }
// deletePod cleans up files and resource associated with the pod // pod must be under exclusive lock and be in either ExitedGarbage // or Garbage state func deletePod(p *pkgPod.Pod) { podState := p.State() if podState != pkgPod.ExitedGarbage && podState != pkgPod.Garbage { stderr.Panicf("logic error: deletePod called with non-garbage pod %q (status %q)", p.UUID, p.State()) } if podState == pkgPod.ExitedGarbage { s, err := imagestore.NewStore(storeDir()) if err != nil { stderr.PrintE("cannot open store", err) return } defer s.Close() ts, err := treestore.NewStore(treeStoreDir(), s) if err != nil { stderr.PrintE("cannot open store", err) return } if globalFlags.Debug { stage0.InitDebug() } if err := mountPodStage1(ts, p); err == nil { if err = stage0.GC(p.Path(), p.UUID); err != nil { stderr.PrintE(fmt.Sprintf("problem performing stage1 GC on %q", p.UUID), err) } // an overlay fs can be mounted over itself, let's unmount it here // if we mounted it before to avoid problems when running // stage0.MountGC if p.UsesOverlay() { stage1Mnt := common.Stage1RootfsPath(p.Path()) if err := syscall.Unmount(stage1Mnt, 0); err != nil { stderr.PrintE("error unmounting stage1", err) } } } else { stderr.PrintE("skipping stage1 GC", err) } // unmount all leftover mounts if err := stage0.MountGC(p.Path(), p.UUID.String()); err != nil { stderr.PrintE(fmt.Sprintf("GC of leftover mounts for pod %q failed", p.UUID), err) return } } if err := os.RemoveAll(p.Path()); err != nil { stderr.PrintE(fmt.Sprintf("unable to remove pod %q", p.UUID), err) os.Exit(254) } }
func newV1AlphaAPIServer() (*v1AlphaAPIServer, error) { s, err := imagestore.NewStore(storeDir()) if err != nil { return nil, err } cache, err := lru.New(defaultCacheSize) if err != nil { return nil, err } return &v1AlphaAPIServer{ store: s, podCache: cache, }, nil }
func TestTreeStoreRemove(t *testing.T) { if !sys.HasChrootCapability() { t.Skipf("chroot capability not available. Disabling test.") } dir, err := ioutil.TempDir("", tstprefix) if err != nil { t.Fatalf("error creating tempdir: %v", err) } defer os.RemoveAll(dir) s, err := imagestore.NewStore(dir) if err != nil { t.Fatalf("unexpected error: %v", err) } ts, err := NewStore(dir, s) if err != nil { t.Fatalf("unexpected error: %v", err) } key, err := testStoreWriteACI(dir, s) if err != nil { t.Fatalf("unexpected error: %v", err) } id := "treestoreid01" // Test non existent dir err = ts.remove(id) if err != nil { t.Fatalf("unexpected error: %v", err) } // Test rendered tree _, err = ts.render(id, key) if err != nil { t.Fatalf("unexpected error: %v", err) } err = ts.remove(id) if err != nil { t.Fatalf("unexpected error: %v", err) } }
func runRmImage(cmd *cobra.Command, args []string) (exit int) { if len(args) < 1 { stderr.Print("must provide at least one image ID") return 254 } s, err := imagestore.NewStore(storeDir()) if err != nil { stderr.PrintE("cannot open store", err) return 254 } if err := rmImages(s, args); err != nil { stderr.Error(err) return 254 } return 0 }
// mountOverlay mounts the app from the overlay-rendered pod to the destination directory. func mountOverlay(pod *pod, app *schema.RuntimeApp, dest string) error { if _, err := os.Stat(dest); err != nil { return err } s, err := imagestore.NewStore(getDataDir()) if err != nil { return errwrap.Wrap(errors.New("cannot open store"), err) } ts, err := treestore.NewStore(treeStoreDir(), s) if err != nil { return errwrap.Wrap(errors.New("cannot open treestore"), err) } treeStoreID, err := pod.getAppTreeStoreID(app.Name) if err != nil { return err } lower := ts.GetRootFS(treeStoreID) imgDir := filepath.Join(filepath.Join(pod.path(), "overlay"), treeStoreID) if _, err := os.Stat(imgDir); err != nil { return err } upper := filepath.Join(imgDir, "upper", app.Name.String()) if _, err := os.Stat(upper); err != nil { return err } work := filepath.Join(imgDir, "work", app.Name.String()) if _, err := os.Stat(work); err != nil { return err } if err := overlay.Mount(&overlay.MountCfg{lower, upper, work, dest, ""}); err != nil { return errwrap.Wrap(errors.New("problem mounting overlayfs directory"), err) } return nil }
func runImageCatManifest(cmd *cobra.Command, args []string) (exit int) { if len(args) != 1 { cmd.Usage() return 1 } s, err := imagestore.NewStore(storeDir()) if err != nil { stderr.PrintE("cannot open store", err) return 1 } key, err := getStoreKeyFromAppOrHash(s, args[0]) if err != nil { stderr.Error(err) return 1 } manifest, err := s.GetImageManifest(key) if err != nil { stderr.PrintE("cannot get image manifest", err) return 1 } var b []byte if flagPrettyPrint { b, err = json.MarshalIndent(manifest, "", "\t") } else { b, err = json.Marshal(manifest) } if err != nil { stderr.PrintE("cannot read the image manifest", err) return 1 } stdout.Print(string(b)) return 0 }
func runImages(cmd *cobra.Command, args []string) int { var errors []error tabBuffer := new(bytes.Buffer) tabOut := getTabOutWithWriter(tabBuffer) if !flagNoLegend { var headerFields []string for _, f := range flagImagesFields.Options { headerFields = append(headerFields, ImagesFieldHeaderMap[f]) } fmt.Fprintf(tabOut, "%s\n", strings.Join(headerFields, "\t")) } s, err := imagestore.NewStore(storeDir()) if err != nil { stderr.PrintE("cannot open store", err) return 1 } remotes, err := s.GetAllRemotes() if err != nil { stderr.PrintE("unable to get remotes", err) return 1 } remoteMap := make(map[string]*imagestore.Remote) for _, r := range remotes { remoteMap[r.BlobKey] = r } var sortAciinfoFields []string for _, f := range flagImagesSortFields.Options { sortAciinfoFields = append(sortAciinfoFields, ImagesFieldAciInfoMap[f]) } aciInfos, err := s.GetAllACIInfos(sortAciinfoFields, bool(flagImagesSortAsc)) if err != nil { stderr.PrintE("unable to get aci infos", err) return 1 } for _, aciInfo := range aciInfos { imj, err := s.GetImageManifestJSON(aciInfo.BlobKey) if err != nil { // ignore aciInfo with missing image manifest as it can be deleted in the meantime continue } var im *schema.ImageManifest if err = json.Unmarshal(imj, &im); err != nil { errors = append(errors, newImgListLoadError(err, imj, aciInfo.BlobKey)) continue } version, ok := im.Labels.Get("version") var fieldValues []string for _, f := range flagImagesFields.Options { fieldValue := "" switch f { case l(id): hashKey := aciInfo.BlobKey if !flagFullOutput { // The short hash form is [HASH_ALGO]-[FIRST 12 CHAR] // For example, sha512-123456789012 pos := strings.Index(hashKey, "-") trimLength := pos + 13 if pos > 0 && trimLength < len(hashKey) { hashKey = hashKey[:trimLength] } } fieldValue = hashKey case l(name): fieldValue = aciInfo.Name if ok { fieldValue = fmt.Sprintf("%s:%s", fieldValue, version) } case l(importTime): if flagFullOutput { fieldValue = aciInfo.ImportTime.Format(defaultTimeLayout) } else { fieldValue = humanize.Time(aciInfo.ImportTime) } case l(lastUsed): if flagFullOutput { fieldValue = aciInfo.LastUsed.Format(defaultTimeLayout) } else { fieldValue = humanize.Time(aciInfo.LastUsed) } case l(size): totalSize := aciInfo.Size + aciInfo.TreeStoreSize if flagFullOutput { fieldValue = fmt.Sprintf("%d", totalSize) } else { fieldValue = humanize.IBytes(uint64(totalSize)) } case l(latest): fieldValue = fmt.Sprintf("%t", aciInfo.Latest) case l(insecureOptions): f, err := rktflag.NewSecFlagsFromValue(int(aciInfo.InsecureOptions)) if err != nil { stderr.Error(err) return 1 } // remove ondisk, it's a runtime insecure option sp := strings.Split(f.String(), ",") for i, o := range sp { if o == "ondisk" { sp = append(sp[:i], sp[i+1:]...) } } fieldValue = fmt.Sprintf("%s", strings.Join(sp, ",")) case l(aciURL): if imageRemote := remoteMap[aciInfo.BlobKey]; imageRemote == nil { fieldValue = "" } else { fieldValue = fmt.Sprintf("%s", imageRemote.ACIURL) } case l(signatureURL): if imageRemote := remoteMap[aciInfo.BlobKey]; imageRemote == nil { fieldValue = "" } else { fieldValue = fmt.Sprintf("%s", imageRemote.SigURL) } } fieldValues = append(fieldValues, fieldValue) } fmt.Fprintf(tabOut, "%s\n", strings.Join(fieldValues, "\t")) } if len(errors) > 0 { sep := "----------------------------------------" stderr.Printf("%d error(s) encountered when listing images:", len(errors)) stderr.Print(sep) for _, err := range errors { stderr.Error(err) stderr.Print(sep) } stderr.Print("misc:") stderr.Printf(" rkt's appc version: %s", schema.AppContainerVersion) // make a visible break between errors and the listing stderr.Print("") } tabOut.Flush() stdout.Print(tabBuffer.String()) return 0 }
func TestTreeStore(t *testing.T) { if !sys.HasChrootCapability() { t.Skipf("chroot capability not available. Disabling test.") } dir, err := ioutil.TempDir("", tstprefix) if err != nil { t.Fatalf("error creating tempdir: %v", err) } defer os.RemoveAll(dir) s, err := imagestore.NewStore(dir) if err != nil { t.Fatalf("unexpected error: %v", err) } ts, err := NewStore(dir, s) if err != nil { t.Fatalf("unexpected error: %v", err) } imj := ` { "acKind": "ImageManifest", "acVersion": "0.8.8", "name": "example.com/test01" } ` entries := []*aci.ACIEntry{ // An empty dir { Header: &tar.Header{ Name: "rootfs/a", Typeflag: tar.TypeDir, }, }, { Contents: "hello", Header: &tar.Header{ Name: "hello.txt", Size: 5, }, }, { Header: &tar.Header{ Name: "rootfs/link.txt", Linkname: "rootfs/hello.txt", Typeflag: tar.TypeSymlink, }, }, // dangling symlink { Header: &tar.Header{ Name: "rootfs/link2.txt", Linkname: "rootfs/missingfile.txt", Typeflag: tar.TypeSymlink, }, }, { Header: &tar.Header{ Name: "rootfs/fifo", Typeflag: tar.TypeFifo, }, }, } aci, err := aci.NewACI(dir, imj, entries) if err != nil { t.Fatalf("error creating test tar: %v", err) } defer aci.Close() // Rewind the ACI if _, err := aci.Seek(0, 0); err != nil { t.Fatalf("unexpected error: %v", err) } // Import the new ACI key, err := s.WriteACI(aci, imagestore.ACIFetchInfo{Latest: false}) if err != nil { t.Fatalf("unexpected error: %v", err) } // Ask the store to render the treestore id, _, err := ts.Render(key, false) if err != nil { t.Fatalf("unexpected error: %v", err) } // Verify image Hash. Should be the same. _, err = ts.Check(id) if err != nil { t.Fatalf("unexpected error: %v", err) } // Change a file permission rootfs := ts.GetRootFS(id) err = os.Chmod(filepath.Join(rootfs, "a"), 0600) if err != nil { t.Fatalf("unexpected error: %v", err) } // Verify image Hash. Should be different _, err = ts.Check(id) if err == nil { t.Errorf("expected non-nil error!") } // rebuild the tree prevID := id id, _, err = ts.Render(key, true) if err != nil { t.Fatalf("unexpected error: %v", err) } if id != prevID { t.Fatalf("unexpected different IDs. prevID: %s, id: %s", prevID, id) } // Add a file rootfs = ts.GetRootFS(id) err = ioutil.WriteFile(filepath.Join(rootfs, "newfile"), []byte("newfile"), 0644) if err != nil { t.Fatalf("unexpected error: %v", err) } // Verify image Hash. Should be different _, err = ts.Check(id) if err == nil { t.Errorf("expected non-nil error!") } }
func runAppAdd(cmd *cobra.Command, args []string) (exit int) { if len(args) < 2 { stderr.Print("must provide the pod UUID and an IMAGEID") return 254 } err := parseApps(&rktApps, args[1:], cmd.Flags(), true) if err != nil { stderr.PrintE("error parsing app image arguments", err) return 254 } if rktApps.Count() > 1 { stderr.Print("must give only one app") return 254 } p, err := pkgPod.PodFromUUIDString(getDataDir(), args[0]) if err != nil { stderr.PrintE("problem retrieving pod", err) return 254 } defer p.Close() if p.State() != pkgPod.Running { stderr.Printf("pod %q isn't currently running", p.UUID) return 254 } if !p.IsSupervisorReady() { stderr.Printf("supervisor for pod %q is not yet ready", p.UUID) return 254 } s, err := imagestore.NewStore(storeDir()) if err != nil { stderr.PrintE("cannot open store", err) return 254 } ts, err := treestore.NewStore(treeStoreDir(), s) if err != nil { stderr.PrintE("cannot open treestore", err) return 254 } fn := &image.Finder{ S: s, Ts: ts, Ks: getKeystore(), StoreOnly: true, NoStore: false, } img, err := fn.FindImage(args[1], "") if err != nil { stderr.PrintE("error finding images", err) return 254 } podPID, err := p.ContainerPid1() if err != nil { stderr.PrintE(fmt.Sprintf("unable to determine the pid for pod %q", p.UUID), err) return 254 } ccfg := stage0.CommonConfig{ DataDir: getDataDir(), Store: s, TreeStore: ts, UUID: p.UUID, Debug: globalFlags.Debug, } 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 } cfg := stage0.AddConfig{ CommonConfig: &ccfg, Image: *img, Apps: &rktApps, RktGid: rktgid, UsesOverlay: p.UsesOverlay(), PodPath: p.Path(), PodPID: podPID, } if globalFlags.Debug { stage0.InitDebug() } err = stage0.AddApp(cfg) if err != nil { stderr.PrintE("error adding app to pod", err) return 254 } return 0 }
func runImages(cmd *cobra.Command, args []string) int { var errors []error tabBuffer := new(bytes.Buffer) tabOut := getTabOutWithWriter(tabBuffer) if !flagNoLegend { var headerFields []string for _, f := range flagImagesFields.Options { headerFields = append(headerFields, ImagesFieldHeaderMap[f]) } fmt.Fprintf(tabOut, "%s\n", strings.Join(headerFields, "\t")) } s, err := imagestore.NewStore(storeDir()) if err != nil { stderr.PrintE("cannot open store", err) return 254 } remotes, err := s.GetAllRemotes() if err != nil { stderr.PrintE("unable to get remotes", err) return 254 } remoteMap := make(map[string]*imagestore.Remote) for _, r := range remotes { remoteMap[r.BlobKey] = r } var sortAciinfoFields []string for _, f := range flagImagesSortFields.Options { sortAciinfoFields = append(sortAciinfoFields, ImagesFieldAciInfoMap[f]) } aciInfos, err := s.GetAllACIInfos(sortAciinfoFields, bool(flagImagesSortAsc)) if err != nil { stderr.PrintE("unable to get aci infos", err) return 254 } for _, aciInfo := range aciInfos { imj, err := s.GetImageManifestJSON(aciInfo.BlobKey) if err != nil { // ignore aciInfo with missing image manifest as it can be deleted in the meantime continue } var im *schema.ImageManifest if err = json.Unmarshal(imj, &im); err != nil { errors = append(errors, newImgListLoadError(err, imj, aciInfo.BlobKey)) continue } version, ok := im.Labels.Get("version") var fieldValues []string for _, f := range flagImagesFields.Options { fieldValue := "" switch f { case l(id): hashKey := aciInfo.BlobKey if !flagFullOutput { // The short hash form is [HASH_ALGO]-[FIRST 12 CHAR] // For example, sha512-123456789012 pos := strings.Index(hashKey, "-") trimLength := pos + 13 if pos > 0 && trimLength < len(hashKey) { hashKey = hashKey[:trimLength] } } fieldValue = hashKey case l(name): fieldValue = aciInfo.Name if ok { fieldValue = fmt.Sprintf("%s:%s", fieldValue, version) } case l(importTime): if flagFullOutput { fieldValue = aciInfo.ImportTime.Format(defaultTimeLayout) } else { fieldValue = humanize.Time(aciInfo.ImportTime) } case l(lastUsed): if flagFullOutput { fieldValue = aciInfo.LastUsed.Format(defaultTimeLayout) } else { fieldValue = humanize.Time(aciInfo.LastUsed) } case l(size): totalSize := aciInfo.Size + aciInfo.TreeStoreSize if flagFullOutput { fieldValue = fmt.Sprintf("%d", totalSize) } else { fieldValue = humanize.IBytes(uint64(totalSize)) } case l(latest): fieldValue = fmt.Sprintf("%t", aciInfo.Latest) } fieldValues = append(fieldValues, fieldValue) } fmt.Fprintf(tabOut, "%s\n", strings.Join(fieldValues, "\t")) } if len(errors) > 0 { printErrors(errors, "listing images") } tabOut.Flush() stdout.Print(tabBuffer.String()) return 0 }
func runRunPrepared(cmd *cobra.Command, args []string) (exit int) { if len(args) != 1 { cmd.Usage() return 1 } p, err := getPodFromUUIDString(args[0]) if err != nil { stderr.PrintE("problem retrieving pod", err) return 1 } defer p.Close() 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 } if !p.isPrepared { stderr.Printf("pod %q is not prepared", p.uuid) return 1 } if flagInteractive { ac, err := p.getAppCount() if err != nil { stderr.PrintE("cannot get pod's app count", err) return 1 } if ac > 1 { stderr.Print("interactive option only supports pods with one app") return 1 } } // Make sure we have a metadata service available before we move to // run state so that the user can rerun the command without needing // to prepare the image again. if flagMDSRegister { if err := stage0.CheckMdsAvailability(); err != nil { stderr.Error(err) return 1 } } if err := p.xToRun(); err != nil { stderr.PrintE("cannot transition to run", err) return 1 } lfd, err := p.Fd() if err != nil { stderr.PrintE("unable to get lock fd", err) return 1 } apps, err := p.getApps() if err != nil { stderr.PrintE("unable to get app list", 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: &stage0.CommonConfig{ Store: s, TreeStore: ts, UUID: p.uuid, Debug: globalFlags.Debug, }, Net: flagNet, LockFd: lfd, Interactive: flagInteractive, DNS: flagDNS, DNSSearch: flagDNSSearch, DNSOpt: flagDNSOpt, MDSRegister: flagMDSRegister, Apps: apps, RktGid: rktgid, Hostname: flagHostname, } if globalFlags.Debug { stage0.InitDebug() } stage0.Run(rcfg, p.path(), getDataDir()) // execs, never returns return 1 }
func runPrepare(cmd *cobra.Command, args []string) (exit int) { var err error origStdout := os.Stdout privateUsers := user.NewBlankUidRange() if flagQuiet { if os.Stdout, err = os.Open("/dev/null"); err != nil { stderr.PrintE("unable to open /dev/null", err) return 254 } } if flagStoreOnly && flagNoStore { stderr.Print("both --store-only and --no-store specified") return 254 } if flagPrivateUsers { if !common.SupportsUserNS() { stderr.Print("--private-users is not supported, kernel compiled without user namespace support") return 254 } privateUsers.SetRandomUidRange(user.DefaultRangeCount) } if err = parseApps(&rktApps, args, cmd.Flags(), true); err != nil { stderr.PrintE("error parsing app image arguments", err) return 254 } if len(flagPodManifest) > 0 && (rktApps.Count() > 0 || (*appsVolume)(&rktApps).String() != "" || (*appMount)(&rktApps).String() != "" || len(flagPorts) > 0 || flagStoreOnly || flagNoStore || flagInheritEnv || !flagExplicitEnv.IsEmpty() || !flagEnvFromFile.IsEmpty()) { stderr.Print("conflicting flags set with --pod-manifest (see --help)") return 254 } if rktApps.Count() < 1 && len(flagPodManifest) == 0 { stderr.Print("must provide at least one image or specify the pod manifest") return 254 } s, err := imagestore.NewStore(storeDir()) if err != nil { stderr.PrintE("cannot open store", err) return 254 } ts, err := treestore.NewStore(treeStoreDir(), s) if err != nil { stderr.PrintE("cannot open treestore", err) return 254 } config, err := getConfig() if err != nil { stderr.PrintE("cannot get configuration", err) return 254 } s1img, err := getStage1Hash(s, ts, config) if err != nil { stderr.Error(err) return 254 } 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.PrintE("error finding images", err) return 254 } p, err := pkgPod.NewPod(getDataDir()) if err != nil { stderr.PrintE("error creating new pod", err) return 254 } cfg := stage0.CommonConfig{ DataDir: getDataDir(), Store: s, TreeStore: ts, Stage1Image: *s1img, UUID: p.UUID, Debug: globalFlags.Debug, } ovlOk := true if err := common.PathSupportsOverlay(getDataDir()); err != nil { if oerr, ok := err.(common.ErrOverlayUnsupported); ok { stderr.Printf("disabling overlay support: %q", oerr.Error()) ovlOk = false } else { stderr.PrintE("error determining overlay support", err) return 254 } } pcfg := stage0.PrepareConfig{ CommonConfig: &cfg, UseOverlay: !flagNoOverlay && ovlOk, 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 254 } if err = stage0.Prepare(pcfg, p.Path(), p.UUID); err != nil { stderr.PrintE("error setting up stage0", err) keyLock.Close() return 254 } keyLock.Close() if err := p.Sync(); err != nil { stderr.PrintE("error syncing pod data", err) return 254 } if err := p.ToPrepared(); err != nil { stderr.PrintE("error transitioning to prepared", err) return 254 } os.Stdout = origStdout // restore output in case of --quiet stdout.Printf("%s", p.UUID.String()) return 0 }
func TestDownloading(t *testing.T) { dir, err := ioutil.TempDir("", "download-image") if err != nil { t.Fatalf("error creating tempdir: %v", err) } defer os.RemoveAll(dir) imj := `{ "acKind": "ImageManifest", "acVersion": "0.7.4", "name": "example.com/test01" }` entries := []*aci.ACIEntry{ // An empty file { Contents: "hello", Header: &tar.Header{ Name: "rootfs/file01.txt", Size: 5, }, }, } aci, err := aci.NewACI(dir, imj, entries) if err != nil { t.Fatalf("error creating test tar: %v", err) } // Rewind the ACI if _, err := aci.Seek(0, 0); err != nil { t.Fatalf("unexpected error: %v", err) } body, err := ioutil.ReadAll(aci) if err != nil { t.Fatalf("unexpected error: %v", err) } noauthServer := &serverHandler{ body: body, t: t, auth: "none", } basicServer := &serverHandler{ body: body, t: t, auth: "basic", } oauthServer := &serverHandler{ body: body, t: t, auth: "bearer", } denyServer := &serverHandler{ body: body, t: t, auth: "deny", } noAuthTS := httptest.NewTLSServer(noauthServer) defer noAuthTS.Close() basicTS := httptest.NewTLSServer(basicServer) defer basicTS.Close() oauthTS := httptest.NewTLSServer(oauthServer) defer oauthTS.Close() denyAuthTS := httptest.NewServer(denyServer) noAuth := http.Header{} // YmFyOmJheg== is base64(bar:baz) basicAuth := http.Header{"Authorization": {"Basic YmFyOmJheg=="}} bearerAuth := http.Header{"Authorization": {"Bearer sometoken"}} urlToName := map[string]string{ noAuthTS.URL: "no auth", basicTS.URL: "basic", oauthTS.URL: "oauth", denyAuthTS.URL: "deny auth", } tests := []struct { ACIURL string hit bool options http.Header authFail bool }{ {noAuthTS.URL, false, noAuth, false}, {noAuthTS.URL, true, noAuth, false}, {noAuthTS.URL, true, bearerAuth, false}, {noAuthTS.URL, true, basicAuth, false}, {basicTS.URL, false, noAuth, true}, {basicTS.URL, false, bearerAuth, true}, {basicTS.URL, false, basicAuth, false}, {oauthTS.URL, false, noAuth, true}, {oauthTS.URL, false, basicAuth, true}, {oauthTS.URL, false, bearerAuth, false}, {denyAuthTS.URL, false, basicAuth, false}, {denyAuthTS.URL, true, bearerAuth, false}, {denyAuthTS.URL, true, noAuth, false}, } s, err := imagestore.NewStore(dir) if err != nil { t.Fatalf("unexpected error %v", err) } for _, tt := range tests { _, ok, err := s.GetRemote(tt.ACIURL) if err != nil { t.Fatalf("unexpected err: %v", err) } if tt.hit == false && ok { t.Fatalf("expected miss got a hit") } if tt.hit == true && !ok { t.Fatalf("expected a hit got a miss") } parsed, err := url.Parse(tt.ACIURL) if err != nil { panic(fmt.Sprintf("Invalid url from test server: %s", tt.ACIURL)) } headers := map[string]config.Headerer{ parsed.Host: &testHeaderer{tt.options}, } ft := &image.Fetcher{ S: s, Headers: headers, InsecureFlags: insecureFlags, } _, err = ft.FetchImage(tt.ACIURL, "", apps.AppImageURL) if err != nil && !tt.authFail { t.Fatalf("expected download to succeed, it failed: %v (server: %q, headers: `%v`)", err, urlToName[tt.ACIURL], tt.options) } if err == nil && tt.authFail { t.Fatalf("expected download to fail, it succeeded (server: %q, headers: `%v`)", urlToName[tt.ACIURL], tt.options) } } s.Dump(false) }
func runImageExtract(cmd *cobra.Command, args []string) (exit int) { if len(args) != 2 { cmd.Usage() return 254 } outputDir := args[1] s, err := imagestore.NewStore(storeDir()) if err != nil { stderr.PrintE("cannot open store", err) return 254 } key, err := getStoreKeyFromAppOrHash(s, args[0]) if err != nil { stderr.Error(err) return 254 } aci, err := s.ReadStream(key) if err != nil { stderr.PrintE("error reading ACI from the store", err) return 254 } // ExtractTar needs an absolute path absOutputDir, err := filepath.Abs(outputDir) if err != nil { stderr.PrintE("error converting output to an absolute path", err) return 254 } if _, err := os.Stat(absOutputDir); err == nil { if !flagExtractOverwrite { stderr.Print("output directory exists (try --overwrite)") return 254 } // don't allow the user to delete the root filesystem by mistake if absOutputDir == "/" { stderr.Print("this would delete your root filesystem. Refusing.") return 254 } if err := os.RemoveAll(absOutputDir); err != nil { stderr.PrintE("error removing existing output dir", err) return 254 } } // if the user only asks for the rootfs we extract the image to a temporary // directory and then move/copy the rootfs to the output directory, if not // we just extract the image to the output directory extractDir := absOutputDir if flagExtractRootfsOnly { rktTmpDir, err := s.TmpDir() if err != nil { stderr.PrintE("error creating rkt temporary directory", err) return 254 } tmpDir, err := ioutil.TempDir(rktTmpDir, "rkt-image-extract-") if err != nil { stderr.PrintE("error creating temporary directory", err) return 254 } defer os.RemoveAll(tmpDir) extractDir = tmpDir } else { if err := os.MkdirAll(absOutputDir, 0755); err != nil { stderr.PrintE("error creating output directory", err) return 254 } } if err := tar.ExtractTar(aci, extractDir, false, user.NewBlankUidRange(), nil); err != nil { stderr.PrintE("error extracting ACI", err) return 254 } if flagExtractRootfsOnly { rootfsDir := filepath.Join(extractDir, "rootfs") if err := os.Rename(rootfsDir, absOutputDir); err != nil { if e, ok := err.(*os.LinkError); ok && e.Err == syscall.EXDEV { // it's on a different device, fall back to copying if err := fileutil.CopyTree(rootfsDir, absOutputDir, user.NewBlankUidRange()); err != nil { stderr.PrintE("error copying ACI rootfs", err) return 254 } } else { stderr.PrintE("error moving ACI rootfs", err) return 254 } } } return 0 }
func runAppSandbox(cmd *cobra.Command, args []string) int { 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 } p, err := pod.NewPod(getDataDir()) if err != nil { stderr.PrintE("error creating new pod", err) return 1 } if flagUUIDFileSave != "" { if err := pod.WriteUUIDToFile(p.UUID, flagUUIDFileSave); err != nil { stderr.PrintE("error saving pod UUID to file", err) return 1 } } processLabel, mountLabel, err := label.InitLabels("/var/run/rkt/mcs", []string{}) if err != nil { stderr.PrintE("error initialising SELinux", err) return 1 } p.MountLabel = mountLabel cfg := stage0.CommonConfig{ DataDir: getDataDir(), MountLabel: mountLabel, ProcessLabel: processLabel, Store: s, TreeStore: ts, Stage1Image: *s1img, UUID: p.UUID, Debug: globalFlags.Debug, Mutable: true, } ovlOk := true if err := common.PathSupportsOverlay(getDataDir()); err != nil { if oerr, ok := err.(common.ErrOverlayUnsupported); ok { stderr.Printf("disabling overlay support: %q", oerr.Error()) ovlOk = false } else { stderr.PrintE("error determining overlay support", err) return 1 } } useOverlay := !flagNoOverlay && ovlOk pcfg := stage0.PrepareConfig{ CommonConfig: &cfg, UseOverlay: useOverlay, PrivateUsers: user.NewBlankUidRange(), SkipTreeStoreCheck: globalFlags.InsecureFlags.SkipOnDiskCheck(), Apps: &rktApps, Ports: []types.ExposedPort(flagAppPorts), UserAnnotations: parseAnnotations(&flagAnnotations), UserLabels: parseLabels(&flagLabels), } 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.ToRun(); 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 } DNSConfMode, DNSConfig, HostsEntries, err := parseDNSFlags(flagHostsEntries, flagDNS, flagDNSSearch, flagDNSOpt, flagDNSDomain) if err != nil { stderr.PrintE("error with dns flags", err) return 1 } rcfg := stage0.RunConfig{ CommonConfig: &cfg, Net: flagNet, LockFd: lfd, Interactive: true, DNSConfMode: DNSConfMode, DNSConfig: DNSConfig, MDSRegister: false, LocalConfig: globalFlags.LocalConfigDir, RktGid: rktgid, Hostname: flagHostname, InsecureCapabilities: globalFlags.InsecureFlags.SkipCapabilities(), InsecurePaths: globalFlags.InsecureFlags.SkipPaths(), InsecureSeccomp: globalFlags.InsecureFlags.SkipSeccomp(), UseOverlay: useOverlay, HostsEntries: *HostsEntries, } _, manifest, err := p.PodManifest() if err != nil { stderr.PrintE("cannot get the pod manifest", err) return 1 } rcfg.Apps = manifest.Apps stage0.Run(rcfg, p.Path(), getDataDir()) // execs, never returns return 1 }
func runImageRender(cmd *cobra.Command, args []string) (exit int) { if len(args) != 2 { cmd.Usage() return 254 } outputDir := args[1] s, err := imagestore.NewStore(storeDir()) if err != nil { stderr.PrintE("cannot open store", err) return 254 } ts, err := treestore.NewStore(treeStoreDir(), s) if err != nil { stderr.PrintE("cannot open store", err) return } key, err := getStoreKeyFromAppOrHash(s, args[0]) if err != nil { stderr.Error(err) return 254 } id, _, err := ts.Render(key, false) if err != nil { stderr.PrintE("error rendering ACI", err) return 254 } if _, err := ts.Check(id); err != nil { stderr.Print("warning: tree cache is in a bad state. Rebuilding...") var err error if id, _, err = ts.Render(key, true); err != nil { stderr.PrintE("error rendering ACI", err) return 254 } } if _, err := os.Stat(outputDir); err == nil { if !flagRenderOverwrite { stderr.Print("output directory exists (try --overwrite)") return 254 } // don't allow the user to delete the root filesystem by mistake if outputDir == "/" { stderr.Print("this would delete your root filesystem. Refusing.") return 254 } if err := os.RemoveAll(outputDir); err != nil { stderr.PrintE("error removing existing output dir", err) return 254 } } rootfsOutDir := outputDir if !flagRenderRootfsOnly { if err := os.MkdirAll(outputDir, 0755); err != nil { stderr.PrintE("error creating output directory", err) return 254 } rootfsOutDir = filepath.Join(rootfsOutDir, "rootfs") manifest, err := s.GetImageManifest(key) if err != nil { stderr.PrintE("error getting manifest", err) return 254 } mb, err := json.Marshal(manifest) if err != nil { stderr.PrintE("error marshalling image manifest", err) return 254 } if err := ioutil.WriteFile(filepath.Join(outputDir, "manifest"), mb, 0700); err != nil { stderr.PrintE("error writing image manifest", err) return 254 } } cachedTreePath := ts.GetRootFS(id) if err := fileutil.CopyTree(cachedTreePath, rootfsOutDir, user.NewBlankUidRange()); err != nil { stderr.PrintE("error copying ACI rootfs", err) return 254 } return 0 }
func runEnter(cmd *cobra.Command, args []string) (exit int) { if len(args) < 1 { cmd.Usage() return 254 } p, err := pkgPod.PodFromUUIDString(getDataDir(), args[0]) if err != nil { stderr.PrintE("problem retrieving pod", err) return 254 } defer p.Close() if p.State() != pkgPod.Running { stderr.Printf("pod %q isn't currently running", p.UUID) return 254 } podPID, err := p.ContainerPid1() if err != nil { stderr.PrintE(fmt.Sprintf("unable to determine the pid for pod %q", p.UUID), err) return 254 } appName, err := getAppName(p) if err != nil { stderr.PrintE("unable to determine app name", err) return 254 } argv, err := getEnterArgv(p, args) if err != nil { stderr.PrintE("enter failed", err) return 254 } s, err := imagestore.NewStore(storeDir()) if err != nil { stderr.PrintE("cannot open store", err) return 254 } ts, err := treestore.NewStore(treeStoreDir(), s) if err != nil { stderr.PrintE("cannot open store", err) return 254 } stage1TreeStoreID, err := p.GetStage1TreeStoreID() if err != nil { stderr.PrintE("error getting stage1 treeStoreID", err) return 254 } stage1RootFS := ts.GetRootFS(stage1TreeStoreID) if err = stage0.Enter(p.Path(), podPID, *appName, stage1RootFS, argv); err != nil { stderr.PrintE("enter failed", err) return 254 } // not reached when stage0.Enter execs /enter 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 runFetch(cmd *cobra.Command, args []string) (exit int) { if err := parseApps(&rktApps, args, cmd.Flags(), false); err != nil { stderr.PrintE("unable to parse arguments", err) return 254 } if rktApps.Count() < 1 { stderr.Print("must provide at least one image") return 254 } if flagStoreOnly && flagNoStore { stderr.Print("both --store-only and --no-store specified") return 254 } s, err := imagestore.NewStore(storeDir()) if err != nil { stderr.PrintE("cannot open store", err) return 254 } ts, err := treestore.NewStore(treeStoreDir(), s) if err != nil { stderr.PrintE("cannot open treestore", err) return 254 } ks := getKeystore() config, err := getConfig() if err != nil { stderr.PrintE("cannot get configuration", err) return 254 } ft := &image.Fetcher{ S: s, Ts: ts, Ks: ks, Headers: config.AuthPerHost, DockerAuth: config.DockerCredentialsPerRegistry, InsecureFlags: globalFlags.InsecureFlags, Debug: globalFlags.Debug, TrustKeysFromHTTPS: globalFlags.TrustKeysFromHTTPS, StoreOnly: flagStoreOnly, NoStore: flagNoStore, WithDeps: true, } err = rktApps.Walk(func(app *apps.App) error { hash, err := ft.FetchImage(app.Image, app.Asc, app.ImType) if err != nil { return err } if !flagFullHash { hash = types.ShortHash(hash) } stdout.Print(hash) return nil }) if err != nil { stderr.Error(err) return 254 } return }
func TestFetchImage(t *testing.T) { dir, err := ioutil.TempDir("", "fetch-image") if err != nil { t.Fatalf("error creating tempdir: %v", err) } defer os.RemoveAll(dir) s, err := imagestore.NewStore(dir) if err != nil { t.Fatalf("unexpected error %v", err) } defer s.Dump(false) ks, ksPath, err := keystore.NewTestKeystore() if err != nil { t.Errorf("unexpected error %v", err) } defer os.RemoveAll(ksPath) key := keystoretest.KeyMap["example.com/app"] if _, err := ks.StoreTrustedKeyPrefix("example.com/app", bytes.NewBufferString(key.ArmoredPublicKey)); err != nil { t.Fatalf("unexpected error %v", err) } a, err := aci.NewBasicACI(dir, "example.com/app") defer a.Close() if err != nil { t.Fatalf("unexpected error %v", err) } // Rewind the ACI if _, err := a.Seek(0, 0); err != nil { t.Fatalf("unexpected error %v", err) } asc, err := aci.NewDetachedSignature(key.ArmoredPrivateKey, a) if err != nil { t.Fatalf("unexpected error %v", err) } // Rewind the ACI. if _, err := a.Seek(0, 0); err != nil { t.Fatalf("unexpected error %v", err) } ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch filepath.Ext(r.URL.Path) { case ".aci": io.Copy(w, a) return case ".asc": io.Copy(w, asc) return default: t.Fatalf("unknown extension %v", r.URL.Path) } })) defer ts.Close() ft := &image.Fetcher{ S: s, Ks: ks, InsecureFlags: secureFlags, } _, err = ft.FetchImage(fmt.Sprintf("%s/app.aci", ts.URL), "", apps.AppImageURL) if err != nil { t.Fatalf("unexpected error: %v", err) } }
func runRunPrepared(cmd *cobra.Command, args []string) (exit int) { if len(args) != 1 { cmd.Usage() return 254 } p, err := pkgPod.PodFromUUIDString(getDataDir(), args[0]) if err != nil { stderr.PrintE("problem retrieving pod", err) return 254 } defer p.Close() s, err := imagestore.NewStore(storeDir()) if err != nil { stderr.PrintE("cannot open store", err) return 254 } ts, err := treestore.NewStore(treeStoreDir(), s) if err != nil { stderr.PrintE("cannot open treestore", err) return 254 } if p.State() != pkgPod.Prepared { stderr.Printf("pod %q is not prepared", p.UUID) return 254 } _, manifest, err := p.PodManifest() if err != nil { stderr.PrintE("cannot read pod manifest", err) return 254 } if flagInteractive { if len(manifest.Apps) > 1 { stderr.Print("interactive option only supports pods with one app") return 254 } } // Make sure we have a metadata service available before we move to // run state so that the user can rerun the command without needing // to prepare the image again. if flagMDSRegister { if err := stage0.CheckMdsAvailability(); err != nil { stderr.Error(err) return 254 } } if err := p.ToRun(); err != nil { stderr.PrintE("cannot transition to run", err) return 254 } lfd, err := p.Fd() if err != nil { stderr.PrintE("unable to get lock fd", err) return 254 } 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 } ovlOk := true if err := common.PathSupportsOverlay(getDataDir()); err != nil { if oerr, ok := err.(common.ErrOverlayUnsupported); ok { stderr.Printf("disabling overlay support: %q", oerr.Error()) ovlOk = false } else { stderr.PrintE("error determining overlay support", err) return 254 } } ovlPrep := p.UsesOverlay() // should not happen, maybe the data directory moved from an overlay-enabled fs to another location // between prepare and run-prepared if ovlPrep && !ovlOk { stderr.Print("unable to run prepared overlay-enabled pod: overlay not supported") return 254 } DNSConfMode, DNSConfig, HostsEntries, err := parseDNSFlags(flagHostsEntries, flagDNS, flagDNSSearch, flagDNSOpt, flagDNSDomain) if err != nil { stderr.PrintE("error with dns flags", err) return 254 } rcfg := stage0.RunConfig{ CommonConfig: &stage0.CommonConfig{ DataDir: getDataDir(), Store: s, TreeStore: ts, UUID: p.UUID, Debug: globalFlags.Debug, }, Net: flagNet, LockFd: lfd, Interactive: flagInteractive, DNSConfMode: DNSConfMode, DNSConfig: DNSConfig, HostsEntries: *HostsEntries, MDSRegister: flagMDSRegister, Apps: manifest.Apps, RktGid: rktgid, Hostname: flagHostname, InsecureCapabilities: globalFlags.InsecureFlags.SkipCapabilities(), InsecurePaths: globalFlags.InsecureFlags.SkipPaths(), InsecureSeccomp: globalFlags.InsecureFlags.SkipSeccomp(), UseOverlay: ovlPrep && ovlOk, } if globalFlags.Debug { stage0.InitDebug() } stage0.Run(rcfg, p.Path(), getDataDir()) // execs, never returns return 254 }
func TestFetchImageCache(t *testing.T) { dir, err := ioutil.TempDir("", "fetch-image-cache") if err != nil { t.Fatalf("error creating tempdir: %v", err) } defer os.RemoveAll(dir) s, err := imagestore.NewStore(dir) if err != nil { t.Fatalf("unexpected error %v", err) } defer s.Dump(false) ks, ksPath, err := keystore.NewTestKeystore() if err != nil { t.Errorf("unexpected error %v", err) } defer os.RemoveAll(ksPath) key := keystoretest.KeyMap["example.com/app"] if _, err := ks.StoreTrustedKeyPrefix("example.com/app", bytes.NewBufferString(key.ArmoredPublicKey)); err != nil { t.Fatalf("unexpected error %v", err) } a, err := aci.NewBasicACI(dir, "example.com/app") defer a.Close() if err != nil { t.Fatalf("unexpected error %v", err) } // Rewind the ACI if _, err := a.Seek(0, 0); err != nil { t.Fatalf("unexpected error %v", err) } asc, err := aci.NewDetachedSignature(key.ArmoredPrivateKey, a) if err != nil { t.Fatalf("unexpected error %v", err) } // Rewind the ACI if _, err := a.Seek(0, 0); err != nil { t.Fatalf("unexpected error %v", err) } aciBody, err := ioutil.ReadAll(a) if err != nil { t.Fatalf("unexpected error: %v", err) } ascBody, err := ioutil.ReadAll(asc) if err != nil { t.Fatalf("unexpected error: %v", err) } nocacheServer := &cachingServerHandler{ aciBody: aciBody, ascBody: ascBody, etag: "", maxAge: 0, t: t, } etagServer := &cachingServerHandler{ aciBody: aciBody, ascBody: ascBody, etag: "123456789", maxAge: 0, t: t, } maxAgeServer := &cachingServerHandler{ aciBody: aciBody, ascBody: ascBody, etag: "", maxAge: 10, t: t, } etagMaxAgeServer := &cachingServerHandler{ aciBody: aciBody, ascBody: ascBody, etag: "123456789", maxAge: 10, t: t, } nocacheTS := httptest.NewServer(nocacheServer) defer nocacheTS.Close() etagTS := httptest.NewServer(etagServer) defer etagTS.Close() maxAgeTS := httptest.NewServer(maxAgeServer) defer maxAgeTS.Close() etagMaxAgeTS := httptest.NewServer(etagMaxAgeServer) defer etagMaxAgeTS.Close() type testData struct { URL string etag string cacheMaxAge int shouldUseCached bool } tests := []testData{ {nocacheTS.URL, "", 0, false}, {etagTS.URL, "123456789", 0, true}, {maxAgeTS.URL, "", 10, true}, {etagMaxAgeTS.URL, "123456789", 10, true}, } testFn := func(tt testData, useRedirect bool) { aciURL := fmt.Sprintf("%s/app.aci", tt.URL) if useRedirect { redirectingTS := httptest.NewServer(&redirectingServerHandler{destServer: tt.URL}) defer redirectingTS.Close() aciURL = fmt.Sprintf("%s/app.aci", redirectingTS.URL) } ft := &image.Fetcher{ S: s, Ks: ks, InsecureFlags: secureFlags, // Skip local store NoStore: true, } _, err = ft.FetchImage(aciURL, "", apps.AppImageURL) if err != nil { t.Fatalf("unexpected error: %v", err) } rem, _, err := s.GetRemote(aciURL) if err != nil { t.Fatalf("Error getting remote info: %v\n", err) } if rem.ETag != tt.etag { t.Errorf("expected remote to have a ETag header argument") } if rem.CacheMaxAge != tt.cacheMaxAge { t.Errorf("expected max-age header argument to be %q", tt.cacheMaxAge) } downloadTime := rem.DownloadTime _, err = ft.FetchImage(aciURL, "", apps.AppImageURL) if err != nil { t.Fatalf("unexpected error: %v", err) } rem, _, err = s.GetRemote(aciURL) if err != nil { t.Fatalf("Error getting remote info: %v\n", err) } if rem.ETag != tt.etag { t.Errorf("expected remote to have a ETag header argument") } if rem.CacheMaxAge != tt.cacheMaxAge { t.Errorf("expected max-age header argument to be %q", tt.cacheMaxAge) } if tt.shouldUseCached { if downloadTime != rem.DownloadTime { t.Errorf("expected current download time to be the same as the previous one (no download) but they differ") } } else { if downloadTime == rem.DownloadTime { t.Errorf("expected current download time to be different from the previous one (new image download) but they are the same") } } if err := s.RemoveACI(rem.BlobKey); err != nil { t.Fatalf("unexpected error: %v", err) } } // repeat the tests with and without a redirecting server for i := 0; i <= 1; i++ { useRedirect := false if i == 1 { useRedirect = true } for _, tt := range tests { testFn(tt, useRedirect) } } }
func runImages(cmd *cobra.Command, args []string) int { var errors []error tabBuffer := new(bytes.Buffer) tabOut := getTabOutWithWriter(tabBuffer) s, err := imagestore.NewStore(storeDir()) if err != nil { stderr.PrintE("cannot open store", err) return 254 } remotes, err := s.GetAllRemotes() if err != nil { stderr.PrintE("unable to get remotes", err) return 254 } remoteMap := make(map[string]*imagestore.Remote) for _, r := range remotes { remoteMap[r.BlobKey] = r } var sortAciinfoFields []string for _, f := range flagImagesSortFields.Options { sortAciinfoFields = append(sortAciinfoFields, ImagesFieldAciInfoMap[f]) } aciInfos, err := s.GetAllACIInfos(sortAciinfoFields, bool(flagImagesSortAsc)) if err != nil { stderr.PrintE("unable to get aci infos", err) return 254 } var imagesToPrint []printableImage for _, aciInfo := range aciInfos { imj, err := s.GetImageManifestJSON(aciInfo.BlobKey) if err != nil { // ignore aciInfo with missing image manifest as it can be deleted in the meantime continue } var im *schema.ImageManifest if err = json.Unmarshal(imj, &im); err != nil { errors = append(errors, newImgListLoadError(err, imj, aciInfo.BlobKey)) continue } version, ok := im.Labels.Get("version") imageID := aciInfo.BlobKey imageName := aciInfo.Name if ok { imageName = fmt.Sprintf("%s:%s", imageName, version) } totalSize := aciInfo.Size + aciInfo.TreeStoreSize if !flagFullOutput && flagImageFormat == outputFormatTabbed { imageID = trimImageID(imageID) } imagesToPrint = append(imagesToPrint, printableImage{ ImageListEntry: rktlib.ImageListEntry{ ID: imageID, Name: imageName, ImportTime: aciInfo.ImportTime.UnixNano(), LastUsedTime: aciInfo.LastUsed.UnixNano(), Size: totalSize, }, format: flagImageFormat, full: flagFullOutput, }) } switch flagImageFormat { case outputFormatTabbed: if !flagNoLegend { var headerFields []string for _, f := range flagImagesFields.Options { headerFields = append(headerFields, ImagesFieldHeaderMap[f]) } fmt.Fprintf(tabOut, "%s\n", strings.Join(headerFields, "\t")) } for _, image := range imagesToPrint { fmt.Fprintf(tabOut, "%s\n", image.printableString(flagImagesFields)) } case outputFormatJSON: result, err := json.Marshal(imagesToPrint) if err != nil { errors = append(errors, err) } else { fmt.Fprintf(tabOut, "%s\n", result) } case outputFormatPrettyJSON: result, err := json.MarshalIndent(imagesToPrint, "", "\t") if err != nil { errors = append(errors, err) } else { fmt.Fprintf(tabOut, "%s\n", result) } } if len(errors) > 0 { printErrors(errors, "listing images") } tabOut.Flush() stdout.Print(tabBuffer.String()) return 0 }