// prepareAppImage renders and verifies the tree cache of the app image that // corresponds to the given app name. // When useOverlay is false, it attempts to render and expand the app image func prepareAppImage(cfg PrepareConfig, appName types.ACName, img types.Hash, cdir string, useOverlay bool) error { log.Println("Loading image", img.String()) am, err := cfg.Store.GetImageManifest(img.String()) if err != nil { return fmt.Errorf("error getting the manifest: %v", err) } if _, hasOS := am.Labels.Get("os"); !hasOS { return fmt.Errorf("missing os label in the image manifest") } if _, hasArch := am.Labels.Get("arch"); !hasArch { return fmt.Errorf("missing arch label in the image manifest") } if err := types.IsValidOSArch(am.Labels.ToMap(), ValidOSArch); err != nil { return err } appInfoDir := common.AppInfoPath(cdir, appName) if err := os.MkdirAll(appInfoDir, 0755); err != nil { return fmt.Errorf("error creating apps info directory: %v", err) } if useOverlay { if cfg.PrivateUsers.Shift > 0 { return fmt.Errorf("cannot use both overlay and user namespace: not implemented yet. (Try --no-overlay)") } treeStoreID, err := cfg.Store.RenderTreeStore(img.String(), false) if err != nil { return fmt.Errorf("error rendering tree image: %v", err) } if err := cfg.Store.CheckTreeStore(treeStoreID); err != nil { log.Printf("Warning: tree cache is in a bad state: %v. Rebuilding...", err) var err error if treeStoreID, err = cfg.Store.RenderTreeStore(img.String(), true); err != nil { return fmt.Errorf("error rendering tree image: %v", err) } } if err := ioutil.WriteFile(common.AppTreeStoreIDPath(cdir, appName), []byte(treeStoreID), 0700); err != nil { return fmt.Errorf("error writing app treeStoreID: %v", err) } } else { ad := common.AppPath(cdir, appName) err := os.MkdirAll(ad, 0755) if err != nil { return fmt.Errorf("error creating image directory: %v", err) } if err := aci.RenderACIWithImageID(img, ad, cfg.Store, cfg.PrivateUsers); err != nil { return fmt.Errorf("error rendering ACI: %v", err) } } if err := writeManifest(cfg.CommonConfig, img, appInfoDir); err != nil { return err } return nil }
func (b *Builder) upperTreeStoreId() (string, error) { if b.upperId == "" { treeStoreIDFilePath := rktcommon.AppTreeStoreIDPath(b.pod.Root, manifestApp(b.pod).Name) treeStoreID, err := ioutil.ReadFile(treeStoreIDFilePath) if err != nil { return "", errs.WithEF(err, b.fields.WithField("path", treeStoreIDFilePath), "Failed to read treeStoreID from file") } b.upperId = string(treeStoreID) } return b.upperId, nil }
// getAppTreeStoreID returns the treeStoreID of the provided app. func (p *pod) getAppTreeStoreID(app types.ACName) (string, error) { path, err := filepath.Rel("/", common.AppTreeStoreIDPath("", app)) if err != nil { return "", err } treeStoreID, err := p.readFile(path) if err != nil { // When not using overlayfs, apps don't have a treeStoreID file. In // other cases we've got a problem. if !(os.IsNotExist(err) && !p.usesOverlay()) { return "", errwrap.Wrap(fmt.Errorf("no treeStoreID found for app %s", app), err) } } return string(treeStoreID), nil }
// setupAppImage mounts the overlay filesystem for the app image that // corresponds to the given hash if useOverlay is true. // It also creates an mtab file in the application's rootfs if one is not // present. func setupAppImage(cfg RunConfig, appName types.ACName, img types.Hash, cdir string, useOverlay bool) error { ad := common.AppPath(cdir, appName) if useOverlay { err := os.MkdirAll(ad, common.DefaultRegularDirPerm) if err != nil { return errwrap.Wrap(errors.New("error creating image directory"), err) } treeStoreID, err := ioutil.ReadFile(common.AppTreeStoreIDPath(cdir, appName)) if err != nil { return err } if err := copyAppManifest(cdir, appName, ad); err != nil { return err } if err := overlayRender(cfg, string(treeStoreID), cdir, ad, appName.String()); err != nil { return errwrap.Wrap(errors.New("error rendering overlay filesystem"), err) } } return ensureMtabExists(filepath.Join(ad, "rootfs")) }
// setupAppImage mounts the overlay filesystem for the app image that // corresponds to the given hash. Then, it creates the tmp directory. // When useOverlay is false it just creates the tmp directory for this app. func setupAppImage(cfg RunConfig, appName types.ACName, img types.Hash, cdir string, useOverlay bool) error { ad := common.AppPath(cdir, appName) if useOverlay { err := os.MkdirAll(ad, defaultRegularDirPerm) if err != nil { return fmt.Errorf("error creating image directory: %v", err) } treeStoreID, err := ioutil.ReadFile(common.AppTreeStoreIDPath(cdir, appName)) if err != nil { return err } if err := copyAppManifest(cdir, appName, ad); err != nil { return err } if err := overlayRender(cfg, string(treeStoreID), cdir, ad, appName.String()); err != nil { return fmt.Errorf("error rendering overlay filesystem: %v", err) } } return nil }
// getAppTreeStoreIDs returns the treeStoreIDs of the apps images used in // this pod func (p *pod) getAppsTreeStoreIDs() ([]string, error) { var treeStoreIDs []string apps, err := p.getApps() if err != nil { return nil, err } for _, a := range apps { path, err := filepath.Rel("/", common.AppTreeStoreIDPath("", a.Name)) if err != nil { return nil, err } treeStoreID, err := p.readFile(path) if err != nil { // When not using overlayfs, apps don't have a treeStoreID file if os.IsNotExist(err) { continue } return nil, err } treeStoreIDs = append(treeStoreIDs, string(treeStoreID)) } return treeStoreIDs, nil }
// prepareAppImage renders and verifies the tree cache of the app image that // corresponds to the given app name. // When useOverlay is false, it attempts to render and expand the app image func prepareAppImage(cfg PrepareConfig, appName types.ACName, img types.Hash, cdir string, useOverlay bool) error { debug("Loading image %s", img.String()) am, err := cfg.Store.GetImageManifest(img.String()) if err != nil { return fmt.Errorf("error getting the manifest: %v", err) } if _, hasOS := am.Labels.Get("os"); !hasOS { return fmt.Errorf("missing os label in the image manifest") } if _, hasArch := am.Labels.Get("arch"); !hasArch { return fmt.Errorf("missing arch label in the image manifest") } if err := types.IsValidOSArch(am.Labels.ToMap(), ValidOSArch); err != nil { return err } appInfoDir := common.AppInfoPath(cdir, appName) if err := os.MkdirAll(appInfoDir, defaultRegularDirPerm); err != nil { return fmt.Errorf("error creating apps info directory: %v", err) } if useOverlay { if cfg.PrivateUsers.Shift > 0 { return fmt.Errorf("cannot use both overlay and user namespace: not implemented yet. (Try --no-overlay)") } treeStoreID, _, err := cfg.Store.RenderTreeStore(img.String(), false) if err != nil { return fmt.Errorf("error rendering tree image: %v", err) } if !cfg.SkipTreeStoreCheck { hash, err := cfg.Store.CheckTreeStore(treeStoreID) if err != nil { log.Printf("Warning: tree cache is in a bad state: %v. Rebuilding...", err) var err error treeStoreID, hash, err = cfg.Store.RenderTreeStore(img.String(), true) if err != nil { return fmt.Errorf("error rendering tree image: %v", err) } } cfg.CommonConfig.RootHash = hash } if err := ioutil.WriteFile(common.AppTreeStoreIDPath(cdir, appName), []byte(treeStoreID), defaultRegularFilePerm); err != nil { return fmt.Errorf("error writing app treeStoreID: %v", err) } } else { ad := common.AppPath(cdir, appName) err := os.MkdirAll(ad, defaultRegularDirPerm) if err != nil { return fmt.Errorf("error creating image directory: %v", err) } shiftedUid, shiftedGid, err := cfg.PrivateUsers.ShiftRange(uint32(os.Getuid()), uint32(os.Getgid())) if err != nil { return fmt.Errorf("error getting uid, gid: %v", err) } if err := os.Chown(ad, int(shiftedUid), int(shiftedGid)); err != nil { return fmt.Errorf("error shifting app %q's stage2 dir: %v", appName, err) } if err := aci.RenderACIWithImageID(img, ad, cfg.Store, cfg.PrivateUsers); err != nil { return fmt.Errorf("error rendering ACI: %v", err) } } if err := writeManifest(*cfg.CommonConfig, img, appInfoDir); err != nil { return err } return nil }
// TODO(iaguis): add override options for Exec, Environment (à la patch-manifest) func AddApp(cfg RunConfig, dir string, img *types.Hash) error { im, err := cfg.Store.GetImageManifest(img.String()) if err != nil { return err } appName, err := imageNameToAppName(im.Name) if err != nil { return err } p, err := stage1types.LoadPod(dir, cfg.UUID) if err != nil { return errwrap.Wrap(errors.New("error loading pod manifest"), err) } pm := p.Manifest var mutable bool ms, ok := pm.Annotations.Get("coreos.com/rkt/stage1/mutable") if ok { mutable, err = strconv.ParseBool(ms) if err != nil { return errwrap.Wrap(errors.New("error parsing mutable annotation"), err) } } if !mutable { return errors.New("immutable pod: cannot add application") } if pm.Apps.Get(*appName) != nil { return fmt.Errorf("error: multiple apps with name %s", *appName) } if im.App == nil { return fmt.Errorf("error: image %s has no app section)", img) } appInfoDir := common.AppInfoPath(dir, *appName) if err := os.MkdirAll(appInfoDir, common.DefaultRegularDirPerm); err != nil { return errwrap.Wrap(errors.New("error creating apps info directory"), err) } uidRange := user.NewBlankUidRange() // TODO(iaguis): DRY: refactor this var treeStoreID string if cfg.UseOverlay { treeStoreID, _, err := cfg.TreeStore.Render(img.String(), false) if err != nil { return errwrap.Wrap(errors.New("error rendering tree image"), err) } hash, err := cfg.TreeStore.Check(treeStoreID) if err != nil { log.PrintE("warning: tree cache is in a bad state. Rebuilding...", err) var err error treeStoreID, hash, err = cfg.TreeStore.Render(img.String(), true) if err != nil { return errwrap.Wrap(errors.New("error rendering tree image"), err) } } cfg.RootHash = hash if err := ioutil.WriteFile(common.AppTreeStoreIDPath(dir, *appName), []byte(treeStoreID), common.DefaultRegularFilePerm); err != nil { return errwrap.Wrap(errors.New("error writing app treeStoreID"), err) } } else { ad := common.AppPath(dir, *appName) err := os.MkdirAll(ad, common.DefaultRegularDirPerm) if err != nil { return errwrap.Wrap(errors.New("error creating image directory"), err) } privateUsers, err := preparedWithPrivateUsers(dir) if err != nil { log.FatalE("error reading user namespace information", err) } if err := uidRange.Deserialize([]byte(privateUsers)); err != nil { return err } shiftedUid, shiftedGid, err := uidRange.ShiftRange(uint32(os.Getuid()), uint32(os.Getgid())) if err != nil { return errwrap.Wrap(errors.New("error getting uid, gid"), err) } if err := os.Chown(ad, int(shiftedUid), int(shiftedGid)); err != nil { return errwrap.Wrap(fmt.Errorf("error shifting app %q's stage2 dir", *appName), err) } if err := aci.RenderACIWithImageID(*img, ad, cfg.Store, uidRange); err != nil { return errwrap.Wrap(errors.New("error rendering ACI"), err) } } if err := writeManifest(*cfg.CommonConfig, *img, appInfoDir); err != nil { return errwrap.Wrap(errors.New("error writing manifest"), err) } if err := setupAppImage(cfg, *appName, *img, dir, cfg.UseOverlay); err != nil { return fmt.Errorf("error setting up app image: %v", err) } if cfg.UseOverlay { imgDir := filepath.Join(dir, "overlay", treeStoreID) if err := os.Chown(imgDir, -1, cfg.RktGid); err != nil { return err } } ra := schema.RuntimeApp{ Name: *appName, App: im.App, Image: schema.RuntimeImage{ Name: &im.Name, ID: *img, Labels: im.Labels, }, // TODO(iaguis): default isolators } env := ra.App.Environment env.Set("AC_APP_NAME", appName.String()) envFilePath := filepath.Join(common.Stage1RootfsPath(dir), "rkt", "env", appName.String()) if err := common.WriteEnvFile(env, uidRange, envFilePath); err != nil { return err } apps := append(p.Manifest.Apps, ra) p.Manifest.Apps = apps if err := updatePodManifest(dir, p.Manifest); err != nil { return err } if _, err := os.Create(common.AppCreatedPath(p.Root, appName.String())); err != nil { return err } return nil }
// TODO(iaguis): RmConfig? func RmApp(dir string, uuid *types.UUID, usesOverlay bool, appName *types.ACName, podPID int) error { p, err := stage1types.LoadPod(dir, uuid) if err != nil { return errwrap.Wrap(errors.New("error loading pod manifest"), err) } pm := p.Manifest var mutable bool ms, ok := pm.Annotations.Get("coreos.com/rkt/stage1/mutable") if ok { mutable, err = strconv.ParseBool(ms) if err != nil { return errwrap.Wrap(errors.New("error parsing mutable annotation"), err) } } if !mutable { return errors.New("immutable pod: cannot remove application") } app := pm.Apps.Get(*appName) if app == nil { return fmt.Errorf("error: nonexistent app %q", *appName) } treeStoreID, err := ioutil.ReadFile(common.AppTreeStoreIDPath(dir, *appName)) if err != nil { return err } eep, err := getStage1Entrypoint(dir, enterEntrypoint) if err != nil { return errwrap.Wrap(errors.New("error determining 'enter' entrypoint"), err) } args := []string{ uuid.String(), appName.String(), filepath.Join(common.Stage1RootfsPath(dir), eep), strconv.Itoa(podPID), } if err := callEntrypoint(dir, appStopEntrypoint, args); err != nil { return err } if err := callEntrypoint(dir, appRmEntrypoint, args); err != nil { return err } appInfoDir := common.AppInfoPath(dir, *appName) if err := os.RemoveAll(appInfoDir); err != nil { return errwrap.Wrap(errors.New("error removing app info directory"), err) } if usesOverlay { appRootfs := common.AppRootfsPath(dir, *appName) if err := syscall.Unmount(appRootfs, 0); err != nil { return err } ts := filepath.Join(dir, "overlay", string(treeStoreID)) if err := os.RemoveAll(ts); err != nil { return errwrap.Wrap(errors.New("error removing app info directory"), err) } } if err := os.RemoveAll(common.AppPath(dir, *appName)); err != nil { return err } appStatusPath := filepath.Join(common.Stage1RootfsPath(dir), "rkt", "status", appName.String()) if err := os.Remove(appStatusPath); err != nil && !os.IsNotExist(err) { return err } envPath := filepath.Join(common.Stage1RootfsPath(dir), "rkt", "env", appName.String()) if err := os.Remove(envPath); err != nil && !os.IsNotExist(err) { return err } removeAppFromPodManifest(pm, appName) if err := updatePodManifest(dir, pm); err != nil { return err } return nil }
func RmApp(cfg RmConfig) error { pod, err := pkgPod.PodFromUUIDString(cfg.DataDir, cfg.UUID.String()) if err != nil { return errwrap.Wrap(errors.New("error loading pod"), err) } defer pod.Close() debug("locking sandbox manifest") if err := pod.ExclusiveLockManifest(); err != nil { return errwrap.Wrap(errors.New("failed to lock sandbox manifest"), err) } defer pod.UnlockManifest() pm, err := pod.SandboxManifest() if err != nil { return errwrap.Wrap(errors.New("cannot remove application, sandbox validation failed"), err) } app := pm.Apps.Get(*cfg.AppName) if app == nil { return fmt.Errorf("error: nonexistent app %q", *cfg.AppName) } if cfg.PodPID > 0 { // Call app-stop and app-rm entrypoint only if the pod is still running. // Otherwise, there's not much we can do about it except unmounting/removing // the file system. args := []string{ fmt.Sprintf("--debug=%t", cfg.Debug), fmt.Sprintf("--app=%s", cfg.AppName), } ce := CrossingEntrypoint{ PodPath: cfg.PodPath, PodPID: cfg.PodPID, AppName: cfg.AppName.String(), EntrypointName: appStopEntrypoint, EntrypointArgs: args, Interactive: false, } if err := ce.Run(); err != nil { status, err := common.GetExitStatus(err) // ignore nonexistent units failing to stop. Exit status 5 // comes from systemctl and means the unit doesn't exist if err != nil { return err } else if status != 5 { return fmt.Errorf("exit status %d", status) } } ce.EntrypointName = appRmEntrypoint if err := ce.Run(); err != nil { return err } } if cfg.UsesOverlay { treeStoreID, err := ioutil.ReadFile(common.AppTreeStoreIDPath(cfg.PodPath, *cfg.AppName)) if err != nil { return err } appRootfs := common.AppRootfsPath(cfg.PodPath, *cfg.AppName) if err := syscall.Unmount(appRootfs, 0); err != nil { return err } ts := filepath.Join(cfg.PodPath, "overlay", string(treeStoreID)) if err := os.RemoveAll(ts); err != nil { return errwrap.Wrap(errors.New("error removing app info directory"), err) } } appInfoDir := common.AppInfoPath(cfg.PodPath, *cfg.AppName) if err := os.RemoveAll(appInfoDir); err != nil { return errwrap.Wrap(errors.New("error removing app info directory"), err) } if err := os.RemoveAll(common.AppPath(cfg.PodPath, *cfg.AppName)); err != nil { return err } appStatusPath := filepath.Join(common.Stage1RootfsPath(cfg.PodPath), "rkt", "status", cfg.AppName.String()) if err := os.Remove(appStatusPath); err != nil && !os.IsNotExist(err) { return err } envPath := filepath.Join(common.Stage1RootfsPath(cfg.PodPath), "rkt", "env", cfg.AppName.String()) if err := os.Remove(envPath); err != nil && !os.IsNotExist(err) { return err } for i, app := range pm.Apps { if app.Name == *cfg.AppName { pm.Apps = append(pm.Apps[:i], pm.Apps[i+1:]...) break } } return pod.UpdateManifest(pm, cfg.PodPath) }
func RmApp(cfg RmConfig) error { pod, err := pkgPod.PodFromUUIDString(cfg.DataDir, cfg.UUID.String()) if err != nil { return errwrap.Wrap(errors.New("error loading pod"), err) } defer pod.Close() debug("locking pod manifest") if err := pod.ExclusiveManifestLock(); err != nil { return errwrap.Wrap(errors.New("failed to lock pod manifest"), err) } defer pod.ManifestUnlock() _, pm, err := pod.PodManifest() if err != nil { return errwrap.Wrap(errors.New("error loading pod manifest"), err) } var mutable bool ms, ok := pm.Annotations.Get("coreos.com/rkt/stage1/mutable") if ok { mutable, err = strconv.ParseBool(ms) if err != nil { return errwrap.Wrap(errors.New("error parsing mutable annotation"), err) } } if !mutable { return errors.New("immutable pod: cannot remove application") } app := pm.Apps.Get(*cfg.AppName) if app == nil { return fmt.Errorf("error: nonexistent app %q", *cfg.AppName) } if cfg.PodPID > 0 { // Call app-stop and app-rm entrypoint only if the pod is still running. // Otherwise, there's not much we can do about it except unmounting/removing // the file system. args := []string{ fmt.Sprintf("--app=%s", cfg.AppName), } ce := CrossingEntrypoint{ PodPath: cfg.PodPath, PodPID: cfg.PodPID, AppName: cfg.AppName.String(), EntrypointName: appStopEntrypoint, EntrypointArgs: args, Interactive: false, } if err := ce.Run(); err != nil { status, err := common.GetExitStatus(err) // ignore nonexistent units failing to stop. Exit status 5 // comes from systemctl and means the unit doesn't exist if err != nil { return err } else if status != 5 { return fmt.Errorf("exit status %d", status) } } ce.EntrypointName = appRmEntrypoint if err := ce.Run(); err != nil { return err } } if cfg.UsesOverlay { treeStoreID, err := ioutil.ReadFile(common.AppTreeStoreIDPath(cfg.PodPath, *cfg.AppName)) if err != nil { return err } appRootfs := common.AppRootfsPath(cfg.PodPath, *cfg.AppName) if err := syscall.Unmount(appRootfs, 0); err != nil { return err } ts := filepath.Join(cfg.PodPath, "overlay", string(treeStoreID)) if err := os.RemoveAll(ts); err != nil { return errwrap.Wrap(errors.New("error removing app info directory"), err) } } appInfoDir := common.AppInfoPath(cfg.PodPath, *cfg.AppName) if err := os.RemoveAll(appInfoDir); err != nil { return errwrap.Wrap(errors.New("error removing app info directory"), err) } if err := os.RemoveAll(common.AppPath(cfg.PodPath, *cfg.AppName)); err != nil { return err } appStatusPath := filepath.Join(common.Stage1RootfsPath(cfg.PodPath), "rkt", "status", cfg.AppName.String()) if err := os.Remove(appStatusPath); err != nil && !os.IsNotExist(err) { return err } envPath := filepath.Join(common.Stage1RootfsPath(cfg.PodPath), "rkt", "env", cfg.AppName.String()) if err := os.Remove(envPath); err != nil && !os.IsNotExist(err) { return err } removeAppFromPodManifest(pm, cfg.AppName) if err := updatePodManifest(cfg.PodPath, pm); err != nil { return err } return nil }