// 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()) if useOverlay { if err := cfg.Store.RenderTreeStore(img.String(), false); err != nil { return fmt.Errorf("error rendering tree image: %v", err) } if err := cfg.Store.CheckTreeStore(img.String()); err != nil { log.Printf("Warning: tree cache is in a bad state. Rebuilding...") if err := cfg.Store.RenderTreeStore(img.String(), true); err != nil { return fmt.Errorf("error rendering tree image: %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); err != nil { return fmt.Errorf("error rendering ACI: %v", err) } } return 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 { 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 }
// Write renders the ACI with the provided key in the treestore // Write, to avoid having a rendered ACI with old stale files, requires that // the destination directory doesn't exist (usually Remove should be called // before Write) func (ts *TreeStore) Write(key string, s *Store) error { treepath := filepath.Join(ts.path, key) fi, _ := os.Stat(treepath) if fi != nil { return fmt.Errorf("treestore: path %s already exists", treepath) } imageID, err := types.NewHash(key) if err != nil { return fmt.Errorf("treestore: cannot convert key to imageID: %v", err) } if err := os.MkdirAll(treepath, 0755); err != nil { return fmt.Errorf("treestore: cannot create treestore directory %s: %v", treepath, err) } err = aci.RenderACIWithImageID(*imageID, treepath, s) if err != nil { return fmt.Errorf("treestore: cannot render aci: %v", err) } hash, err := ts.Hash(key) if err != nil { return fmt.Errorf("treestore: cannot calculate tree hash: %v", err) } err = ioutil.WriteFile(filepath.Join(treepath, hashfilename), []byte(hash), 0644) if err != nil { return fmt.Errorf("treestore: cannot write hash file: %v", err) } // before creating the "rendered" flag file we need to ensure that all data is fsynced dfd, err := syscall.Open(treepath, syscall.O_RDONLY, 0) if err != nil { return err } defer syscall.Close(dfd) if err := sys.Syncfs(dfd); err != nil { return fmt.Errorf("treestore: failed to sync data: %v", err) } // Create rendered file f, err := os.Create(filepath.Join(treepath, renderedfilename)) if err != nil { return fmt.Errorf("treestore: failed to write rendered file: %v", err) } f.Close() if err := syscall.Fsync(dfd); err != nil { return fmt.Errorf("treestore: failed to sync tree store directory: %v", err) } return nil }
// prepareStage1Image renders and verifies tree cache of the given hash // when using overlay. // When useOverlay is false, it attempts to render and expand the stage1. func prepareStage1Image(cfg PrepareConfig, img types.Hash, cdir string, useOverlay bool) error { s1 := common.Stage1ImagePath(cdir) if err := os.MkdirAll(s1, 0755); err != nil { return fmt.Errorf("error creating stage1 directory: %v", err) } if err := cfg.Store.RenderTreeStore(img.String(), false); err != nil { return fmt.Errorf("error rendering tree image: %v", err) } if err := cfg.Store.CheckTreeStore(img.String()); err != nil { log.Printf("Warning: tree cache is in a bad state. Rebuilding...") if err := cfg.Store.RenderTreeStore(img.String(), true); err != nil { return fmt.Errorf("error rendering tree image: %v", err) } } if !useOverlay { if err := aci.RenderACIWithImageID(img, s1, cfg.Store); err != nil { return fmt.Errorf("error rendering ACI: %v", err) } } return 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 }
// Write renders the ACI with the provided key in the treestore. id references // that specific tree store rendered image. // Write, to avoid having a rendered ACI with old stale files, requires that // the destination directory doesn't exist (usually Remove should be called // before Write) func (ts *TreeStore) Write(id string, key string, s *Store) (string, error) { treepath := ts.GetPath(id) fi, _ := os.Stat(treepath) if fi != nil { return "", fmt.Errorf("path %s already exists", treepath) } imageID, err := types.NewHash(key) if err != nil { return "", errwrap.Wrap(errors.New("cannot convert key to imageID"), err) } if err := os.MkdirAll(treepath, 0755); err != nil { return "", errwrap.Wrap(fmt.Errorf("cannot create treestore directory %s", treepath), err) } err = aci.RenderACIWithImageID(*imageID, treepath, s, uid.NewBlankUidRange()) if err != nil { return "", errwrap.Wrap(errors.New("cannot render aci"), err) } hash, err := ts.Hash(id) if err != nil { return "", errwrap.Wrap(errors.New("cannot calculate tree hash"), err) } err = ioutil.WriteFile(filepath.Join(treepath, hashfilename), []byte(hash), 0644) if err != nil { return "", errwrap.Wrap(errors.New("cannot write hash file"), err) } // before creating the "rendered" flag file we need to ensure that all data is fsynced dfd, err := syscall.Open(treepath, syscall.O_RDONLY, 0) if err != nil { return "", err } defer syscall.Close(dfd) if err := sys.Syncfs(dfd); err != nil { return "", errwrap.Wrap(errors.New("failed to sync data"), err) } // Create rendered file f, err := os.Create(filepath.Join(treepath, renderedfilename)) if err != nil { return "", errwrap.Wrap(errors.New("failed to write rendered file"), err) } f.Close() // Write the hash of the image that will use this tree store err = ioutil.WriteFile(filepath.Join(treepath, imagefilename), []byte(key), 0644) if err != nil { return "", errwrap.Wrap(errors.New("cannot write image file"), err) } if err := syscall.Fsync(dfd); err != nil { return "", errwrap.Wrap(errors.New("failed to sync tree store directory"), err) } treeSize, err := ts.Size(id) if err != nil { return "", err } if err := s.UpdateTreeStoreSize(key, treeSize); err != nil { return "", err } return string(hash), 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 }
// render renders the ACI with the provided key in the treestore. id references // that specific tree store rendered image. // render, to avoid having a rendered ACI with old stale files, requires that // the destination directory doesn't exist (usually remove should be called // before render) func (ts *Store) render(id string, key string) (string, error) { treepath := ts.GetPath(id) fi, _ := os.Stat(treepath) if fi != nil { return "", fmt.Errorf("path %s already exists", treepath) } imageID, err := types.NewHash(key) if err != nil { return "", errwrap.Wrap(errors.New("cannot convert key to imageID"), err) } if err := os.MkdirAll(treepath, 0755); err != nil { return "", errwrap.Wrap(fmt.Errorf("cannot create treestore directory %s", treepath), err) } err = aci.RenderACIWithImageID(*imageID, treepath, ts.store, user.NewBlankUidRange()) if err != nil { return "", errwrap.Wrap(errors.New("cannot render aci"), err) } hash, err := ts.Hash(id) if err != nil { return "", errwrap.Wrap(errors.New("cannot calculate tree hash"), err) } err = ioutil.WriteFile(filepath.Join(treepath, hashfilename), []byte(hash), 0644) if err != nil { return "", errwrap.Wrap(errors.New("cannot write hash file"), err) } // before creating the "rendered" flag file we need to ensure that all data is fsynced dfd, err := syscall.Open(treepath, syscall.O_RDONLY, 0) if err != nil { return "", err } defer syscall.Close(dfd) if err := sys.Syncfs(dfd); err != nil { return "", errwrap.Wrap(errors.New("failed to sync data"), err) } // Create rendered file f, err := os.Create(filepath.Join(treepath, renderedfilename)) if err != nil { return "", errwrap.Wrap(errors.New("failed to write rendered file"), err) } f.Close() // Write the hash of the image that will use this tree store err = ioutil.WriteFile(filepath.Join(treepath, imagefilename), []byte(key), 0644) if err != nil { return "", errwrap.Wrap(errors.New("cannot write image file"), err) } if err := syscall.Fsync(dfd); err != nil { return "", errwrap.Wrap(errors.New("failed to sync tree store directory"), err) } // TODO(sgotti) this is wrong for various reasons: // * Doesn't consider that can there can be multiple treestore per ACI // (and fixing this adding/subtracting sizes is bad since cannot be // atomic and could bring to duplicated/missing subtractions causing // wrong sizes) // * ImageStore and TreeStore are decoupled (TreeStore should just use acirenderer.ACIRegistry interface) treeSize, err := ts.Size(id) if err != nil { return "", err } if err := ts.store.UpdateTreeStoreSize(key, treeSize); err != nil { return "", err } return string(hash), nil }