// Atomic deploy by symlink // 1. rsync maindir => backupdir // 2. mv -T backuplink destdir // 3. rsync srcdir => maindir // 4. mv -T mainlink destdir func DeployWithSymlink(srcDir, destDir string) error { mainLink := destDir + ".drootmain" backupLink := destDir + ".drootbackup" mainDir := destDir + ".d/main" backupDir := destDir + ".d/backup" for _, dir := range []string{mainDir, backupDir} { if err := fileutils.CreateIfNotExists(dir, true); err != nil { // mkdir -p return fmt.Errorf("Failed to create directory %s: %s", dir, err) } } // Return error if the working directory that droot internally uses exists for _, link := range []string{mainLink, backupLink, destDir} { if !osutil.IsSymlink(link) && (osutil.ExistsFile(link) || osutil.ExistsDir(link)) { return fmt.Errorf("%s already exists. Please use another directory as --dest option or delete %s", link, link) } } if err := osutil.Symlink(mainDir, mainLink); err != nil { return fmt.Errorf("Failed to create symlink %s: %s", mainLink, err) } if err := osutil.Symlink(backupDir, backupLink); err != nil { return fmt.Errorf("Failed to create symlink %s: %s", backupLink, err) } log.Info("-->", "Syncing", "from", mainDir, "to", backupDir) if err := Rsync(mainDir, backupDir); err != nil { return fmt.Errorf("Failed to rsync: %s", err) } log.Info("-->", "Renaming", "from", backupLink, "to", destDir) if err := os.Rename(backupLink, destDir); err != nil { return fmt.Errorf("Failed to rename %s: %s", destDir, err) } log.Info("-->", "Syncing", "from", srcDir, "to", mainDir) if err := Rsync(srcDir, mainDir); err != nil { return fmt.Errorf("Failed to rsync: %s", err) } log.Info("-->", "Renaming", "from", mainLink, "to", destDir) if err := os.Rename(mainLink, destDir); err != nil { return fmt.Errorf("Failed to rename %s: %s", destDir, err) } return nil }
func doRun(c *cli.Context) error { command := c.Args() if len(command) < 1 { cli.ShowCommandHelp(c, "run") return errors.New("command required") } optRootDir := c.String("root") if optRootDir == "" { cli.ShowCommandHelp(c, "run") return errors.New("--root option required") } rootDir, err := mounter.ResolveRootDir(optRootDir) if err != nil { return err } // Check env format KEY=VALUE env := c.StringSlice("env") if len(env) > 0 { for _, e := range env { if len(strings.SplitN(e, "=", 2)) != 2 { return fmt.Errorf("Invalid env format: %s", e) } } } uid, gid := os.Getuid(), os.Getgid() if group := c.String("group"); group != "" { if gid, err = osutil.LookupGroup(group); err != nil { return fmt.Errorf("Failed to lookup group: %s", err) } } if user := c.String("user"); user != "" { if uid, err = osutil.LookupUser(user); err != nil { return fmt.Errorf("Failed to lookup user: %s", err) } } // copy files if c.Bool("copy-files") { for _, f := range copyFiles { srcFile, destFile := fp.Join("/", f), fp.Join(rootDir, f) if err := osutil.Cp(srcFile, destFile); err != nil { return fmt.Errorf("Failed to copy %s: %s", f, err) } if err := os.Lchown(destFile, uid, gid); err != nil { return fmt.Errorf("Failed to lchown %s: %s", f, err) } } } mnt := mounter.NewMounter(rootDir) if err := mnt.MountSysProc(); err != nil { return err } for _, val := range c.StringSlice("bind") { hostDir, containerDir, err := parseBindOption(val) if err != nil { return err } if err := mnt.BindMount(hostDir, containerDir); err != nil { return err } } for _, val := range c.StringSlice("robind") { hostDir, containerDir, err := parseBindOption(val) if err != nil { return err } if err := mnt.RoBindMount(hostDir, containerDir); err != nil { return fmt.Errorf("Failed to robind mount %s: %s", val, err) } } // create symlinks if err := osutil.Symlink("../run/lock", fp.Join(rootDir, "/var/lock")); err != nil { return fmt.Errorf("Failed to symlink lock file: %s", err) } if err := createDevices(rootDir, uid, gid); err != nil { return fmt.Errorf("Failed to create devices: %s", err) } if err := osutil.Chroot(rootDir); err != nil { return fmt.Errorf("Failed to chroot: %s", err) } if !c.Bool("no-dropcaps") { if err := osutil.DropCapabilities(keepCaps); err != nil { return fmt.Errorf("Failed to drop capabilities: %s", err) } } if err := osutil.Setgid(gid); err != nil { return fmt.Errorf("Failed to set group %d: %s", gid, err) } if err := osutil.Setuid(uid); err != nil { return fmt.Errorf("Failed to set user %d: %s", uid, err) } if osutil.ExistsFile(environ.DROOT_ENV_FILE_PATH) { envFromFile, err := environ.GetEnvironFromEnvFile(environ.DROOT_ENV_FILE_PATH) if err != nil { return fmt.Errorf("Failed to read environ from '%s'", environ.DROOT_ENV_FILE_PATH) } env, err = environ.MergeEnviron(envFromFile, env) if err != nil { return fmt.Errorf("Failed to merge environ: %s", err) } } return osutil.Execv(command[0], command[0:], env) }