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 }