open-nomad/client/allocdir/alloc_dir.go
2015-09-25 16:46:41 -07:00

163 lines
4.3 KiB
Go

package allocdir
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"github.com/hashicorp/nomad/nomad/structs"
)
var (
// The name of the directory that is shared across tasks in a task group.
SharedAllocName = "alloc"
// The set of directories that exist inside eache shared alloc directory.
SharedAllocDirs = []string{"logs", "tmp", "data"}
// The name of the directory that exists inside each task directory
// regardless of driver.
TaskLocal = "local"
)
type AllocDir struct {
// AllocDir is the directory used for storing any state
// of this allocation. It will be purged on alloc destroy.
AllocDir string
// The shared directory is available to all tasks within the same task
// group.
SharedDir string
// TaskDirs is a mapping of task names to their non-shared directory.
TaskDirs map[string]string
}
func NewAllocDir(allocDir string) *AllocDir {
d := &AllocDir{AllocDir: allocDir, TaskDirs: make(map[string]string)}
d.SharedDir = filepath.Join(d.AllocDir, SharedAllocName)
return d
}
// Tears down previously build directory structure.
func (d *AllocDir) Destroy() error {
return os.RemoveAll(d.AllocDir)
}
// Given a list of a task build the correct alloc structure.
func (d *AllocDir) Build(tasks []*structs.Task) error {
// Make the alloc directory, owned by the nomad process.
if err := os.MkdirAll(d.AllocDir, 0700); err != nil {
return fmt.Errorf("Failed to make the alloc directory %v: %v", d.AllocDir, err)
}
// Make the shared directory and make it availabe to all user/groups.
if err := os.Mkdir(d.SharedDir, 0777); err != nil {
return err
}
for _, dir := range SharedAllocDirs {
p := filepath.Join(d.SharedDir, dir)
if err := os.Mkdir(p, 0777); err != nil {
return err
}
}
// Make the task directories.
for _, t := range tasks {
taskDir := filepath.Join(d.AllocDir, t.Name)
if err := os.Mkdir(taskDir, 0777); err != nil {
return err
}
// Create a local directory that each task can use.
local := filepath.Join(taskDir, TaskLocal)
if err := os.Mkdir(local, 0777); err != nil {
return err
}
d.TaskDirs[t.Name] = taskDir
}
return nil
}
// Embed takes a mapping of absolute directory paths on the host to their
// intended, relative location within the task directory. Embed attempts
// hardlink and then defaults to copying.
func (d *AllocDir) Embed(task string, dirs map[string]string) error {
taskdir, ok := d.TaskDirs[task]
if !ok {
return fmt.Errorf("Task directory doesn't exist for task %v", task)
}
subdirs := make(map[string]string)
for source, dest := range dirs {
// Enumerate the files in source.
entries, err := ioutil.ReadDir(source)
if err != nil {
return fmt.Errorf("Couldn't read directory %v: %v", source, err)
}
// Create destination directory.
destDir := filepath.Join(taskdir, dest)
if err := os.MkdirAll(destDir, 0777); err != nil {
return fmt.Errorf("Couldn't create destination directory %v: %v", destDir, err)
}
for _, entry := range entries {
hostEntry := filepath.Join(source, entry.Name())
if entry.IsDir() {
subdirs[hostEntry] = filepath.Join(dest, filepath.Base(hostEntry))
continue
} else if !entry.Mode().IsRegular() {
return fmt.Errorf("Can't embed non-regular file: %v", hostEntry)
}
taskEntry := filepath.Join(destDir, filepath.Base(hostEntry))
if err := d.linkOrCopy(hostEntry, taskEntry); err != nil {
return err
}
}
}
// Recurse on self to copy subdirectories.
if len(subdirs) != 0 {
return d.Embed(task, subdirs)
}
return nil
}
// MountSharedDir mounts the shared directory into the specified task's
// directory. Mount is documented at an OS level in their respective
// implementation files.
func (d *AllocDir) MountSharedDir(task string) error {
taskDir, ok := d.TaskDirs[task]
if !ok {
return fmt.Errorf("No task directory exists for %v", task)
}
return d.mountSharedDir(taskDir)
}
func fileCopy(src, dst string) error {
// Do a simple copy.
srcFile, err := os.Open(src)
if err != nil {
return fmt.Errorf("Couldn't open src file %v: %v", src, err)
}
dstFile, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE, 0777)
if err != nil {
return fmt.Errorf("Couldn't create destination file %v: %v", dst, err)
}
if _, err := io.Copy(dstFile, srcFile); err != nil {
return fmt.Errorf("Couldn't copy %v to %v: %v", src, dst, err)
}
return nil
}