github.com/kardianos/nomad@v0.1.3-0.20151022182107-b13df73ee850/client/allocdir/alloc_dir.go (about) 1 package allocdir 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 10 "github.com/hashicorp/nomad/nomad/structs" 11 ) 12 13 var ( 14 // The name of the directory that is shared across tasks in a task group. 15 SharedAllocName = "alloc" 16 17 // The set of directories that exist inside eache shared alloc directory. 18 SharedAllocDirs = []string{"logs", "tmp", "data"} 19 20 // The name of the directory that exists inside each task directory 21 // regardless of driver. 22 TaskLocal = "local" 23 ) 24 25 type AllocDir struct { 26 // AllocDir is the directory used for storing any state 27 // of this allocation. It will be purged on alloc destroy. 28 AllocDir string 29 30 // The shared directory is available to all tasks within the same task 31 // group. 32 SharedDir string 33 34 // TaskDirs is a mapping of task names to their non-shared directory. 35 TaskDirs map[string]string 36 37 // A list of locations the shared alloc has been mounted to. 38 mounted []string 39 } 40 41 func NewAllocDir(allocDir string) *AllocDir { 42 d := &AllocDir{AllocDir: allocDir, TaskDirs: make(map[string]string)} 43 d.SharedDir = filepath.Join(d.AllocDir, SharedAllocName) 44 return d 45 } 46 47 // Tears down previously build directory structure. 48 func (d *AllocDir) Destroy() error { 49 // Unmount all mounted shared alloc dirs. 50 for _, m := range d.mounted { 51 if err := d.unmountSharedDir(m); err != nil { 52 return fmt.Errorf("Failed to unmount shared directory: %v", err) 53 } 54 } 55 56 return os.RemoveAll(d.AllocDir) 57 } 58 59 // Given a list of a task build the correct alloc structure. 60 func (d *AllocDir) Build(tasks []*structs.Task) error { 61 // Make the alloc directory, owned by the nomad process. 62 if err := os.MkdirAll(d.AllocDir, 0700); err != nil { 63 return fmt.Errorf("Failed to make the alloc directory %v: %v", d.AllocDir, err) 64 } 65 66 // Make the shared directory and make it availabe to all user/groups. 67 if err := os.Mkdir(d.SharedDir, 0777); err != nil { 68 return err 69 } 70 71 // Make the shared directory have non-root permissions. 72 if err := d.dropDirPermissions(d.SharedDir); err != nil { 73 return err 74 } 75 76 for _, dir := range SharedAllocDirs { 77 p := filepath.Join(d.SharedDir, dir) 78 if err := os.Mkdir(p, 0777); err != nil { 79 return err 80 } 81 } 82 83 // Make the task directories. 84 for _, t := range tasks { 85 taskDir := filepath.Join(d.AllocDir, t.Name) 86 if err := os.Mkdir(taskDir, 0777); err != nil { 87 return err 88 } 89 90 // Make the task directory have non-root permissions. 91 if err := d.dropDirPermissions(taskDir); err != nil { 92 return err 93 } 94 95 // Create a local directory that each task can use. 96 local := filepath.Join(taskDir, TaskLocal) 97 if err := os.Mkdir(local, 0777); err != nil { 98 return err 99 } 100 101 if err := d.dropDirPermissions(local); err != nil { 102 return err 103 } 104 105 d.TaskDirs[t.Name] = taskDir 106 } 107 108 return nil 109 } 110 111 // Embed takes a mapping of absolute directory paths on the host to their 112 // intended, relative location within the task directory. Embed attempts 113 // hardlink and then defaults to copying. If the path exists on the host and 114 // can't be embeded an error is returned. 115 func (d *AllocDir) Embed(task string, dirs map[string]string) error { 116 taskdir, ok := d.TaskDirs[task] 117 if !ok { 118 return fmt.Errorf("Task directory doesn't exist for task %v", task) 119 } 120 121 subdirs := make(map[string]string) 122 for source, dest := range dirs { 123 // Check to see if directory exists on host. 124 s, err := os.Stat(source) 125 if os.IsNotExist(err) { 126 continue 127 } 128 129 // Enumerate the files in source. 130 entries, err := ioutil.ReadDir(source) 131 if err != nil { 132 return fmt.Errorf("Couldn't read directory %v: %v", source, err) 133 } 134 135 // Create destination directory. 136 destDir := filepath.Join(taskdir, dest) 137 if err := os.MkdirAll(destDir, s.Mode().Perm()); err != nil { 138 return fmt.Errorf("Couldn't create destination directory %v: %v", destDir, err) 139 } 140 141 for _, entry := range entries { 142 hostEntry := filepath.Join(source, entry.Name()) 143 taskEntry := filepath.Join(destDir, filepath.Base(hostEntry)) 144 if entry.IsDir() { 145 subdirs[hostEntry] = filepath.Join(dest, filepath.Base(hostEntry)) 146 continue 147 } else if !entry.Mode().IsRegular() { 148 // If it is a symlink we can create it, otherwise we skip it. 149 if entry.Mode()&os.ModeSymlink == 0 { 150 continue 151 } 152 153 link, err := os.Readlink(hostEntry) 154 if err != nil { 155 return fmt.Errorf("Couldn't resolve symlink for %v: %v", source, err) 156 } 157 158 if err := os.Symlink(link, taskEntry); err != nil { 159 return fmt.Errorf("Couldn't create symlink: %v", err) 160 } 161 continue 162 } 163 164 if err := d.linkOrCopy(hostEntry, taskEntry, entry.Mode().Perm()); err != nil { 165 return err 166 } 167 } 168 } 169 170 // Recurse on self to copy subdirectories. 171 if len(subdirs) != 0 { 172 return d.Embed(task, subdirs) 173 } 174 175 return nil 176 } 177 178 // MountSharedDir mounts the shared directory into the specified task's 179 // directory. Mount is documented at an OS level in their respective 180 // implementation files. 181 func (d *AllocDir) MountSharedDir(task string) error { 182 taskDir, ok := d.TaskDirs[task] 183 if !ok { 184 return fmt.Errorf("No task directory exists for %v", task) 185 } 186 187 taskLoc := filepath.Join(taskDir, SharedAllocName) 188 if err := d.mountSharedDir(taskLoc); err != nil { 189 return fmt.Errorf("Failed to mount shared directory for task %v: %v", task, err) 190 } 191 192 d.mounted = append(d.mounted, taskLoc) 193 return nil 194 } 195 196 func fileCopy(src, dst string, perm os.FileMode) error { 197 // Do a simple copy. 198 srcFile, err := os.Open(src) 199 if err != nil { 200 return fmt.Errorf("Couldn't open src file %v: %v", src, err) 201 } 202 203 dstFile, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE, perm) 204 if err != nil { 205 return fmt.Errorf("Couldn't create destination file %v: %v", dst, err) 206 } 207 208 if _, err := io.Copy(dstFile, srcFile); err != nil { 209 return fmt.Errorf("Couldn't copy %v to %v: %v", src, dst, err) 210 } 211 212 return nil 213 }