github.com/taylorchu/nomad@v0.5.3-rc1.0.20170407200202-db11e7dd7b55/client/allocdir/task_dir.go (about) 1 package allocdir 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "log" 7 "os" 8 "path/filepath" 9 10 cstructs "github.com/hashicorp/nomad/client/structs" 11 ) 12 13 // TaskDir contains all of the paths relevant to a task. All paths are on the 14 // host system so drivers should mount/link into task containers as necessary. 15 type TaskDir struct { 16 // Dir is the path to Task directory on the host 17 Dir string 18 19 // SharedAllocDir is the path to shared alloc directory on the host 20 // <alloc_dir>/alloc/ 21 SharedAllocDir string 22 23 // SharedTaskDir is the path to the shared alloc directory linked into 24 // the task directory on the host. 25 // <task_dir>/alloc/ 26 SharedTaskDir string 27 28 // LocalDir is the path to the task's local directory on the host 29 // <task_dir>/local/ 30 LocalDir string 31 32 // LogDir is the path to the task's log directory on the host 33 // <alloc_dir>/alloc/logs/ 34 LogDir string 35 36 // SecretsDir is the path to secrets/ directory on the host 37 // <task_dir>/secrets/ 38 SecretsDir string 39 40 logger *log.Logger 41 } 42 43 // newTaskDir creates a TaskDir struct with paths set. Call Build() to 44 // create paths on disk. 45 // 46 // Call AllocDir.NewTaskDir to create new TaskDirs 47 func newTaskDir(logger *log.Logger, allocDir, taskName string) *TaskDir { 48 taskDir := filepath.Join(allocDir, taskName) 49 return &TaskDir{ 50 Dir: taskDir, 51 SharedAllocDir: filepath.Join(allocDir, SharedAllocName), 52 LogDir: filepath.Join(allocDir, SharedAllocName, LogDirName), 53 SharedTaskDir: filepath.Join(taskDir, SharedAllocName), 54 LocalDir: filepath.Join(taskDir, TaskLocal), 55 SecretsDir: filepath.Join(taskDir, TaskSecrets), 56 logger: logger, 57 } 58 } 59 60 // Build default directories and permissions in a task directory. chrootCreated 61 // allows skipping chroot creation if the caller knows it has already been 62 // done. 63 func (t *TaskDir) Build(chrootCreated bool, chroot map[string]string, fsi cstructs.FSIsolation) error { 64 if err := os.MkdirAll(t.Dir, 0777); err != nil { 65 return err 66 } 67 68 // Make the task directory have non-root permissions. 69 if err := dropDirPermissions(t.Dir); err != nil { 70 return err 71 } 72 73 // Create a local directory that each task can use. 74 if err := os.MkdirAll(t.LocalDir, 0777); err != nil { 75 return err 76 } 77 78 if err := dropDirPermissions(t.LocalDir); err != nil { 79 return err 80 } 81 82 // Create the directories that should be in every task. 83 for _, dir := range TaskDirs { 84 absdir := filepath.Join(t.Dir, dir) 85 if err := os.MkdirAll(absdir, 0777); err != nil { 86 return err 87 } 88 89 if err := dropDirPermissions(absdir); err != nil { 90 return err 91 } 92 } 93 94 // Only link alloc dir into task dir for chroot fs isolation. 95 // Image based isolation will bind the shared alloc dir in the driver. 96 // If there's no isolation the task will use the host path to the 97 // shared alloc dir. 98 if fsi == cstructs.FSIsolationChroot { 99 // If the path doesn't exist OR it exists and is empty, link it 100 empty, _ := pathEmpty(t.SharedTaskDir) 101 if !pathExists(t.SharedTaskDir) || empty { 102 if err := linkDir(t.SharedAllocDir, t.SharedTaskDir); err != nil { 103 return fmt.Errorf("Failed to mount shared directory for task: %v", err) 104 } 105 } 106 } 107 108 // Create the secret directory 109 if err := createSecretDir(t.SecretsDir); err != nil { 110 return err 111 } 112 113 if err := dropDirPermissions(t.SecretsDir); err != nil { 114 return err 115 } 116 117 // Build chroot if chroot filesystem isolation is going to be used 118 if fsi == cstructs.FSIsolationChroot { 119 if err := t.buildChroot(chrootCreated, chroot); err != nil { 120 return err 121 } 122 } 123 124 return nil 125 } 126 127 // buildChroot takes a mapping of absolute directory or file paths on the host 128 // to their intended, relative location within the task directory. This 129 // attempts hardlink and then defaults to copying. If the path exists on the 130 // host and can't be embedded an error is returned. If chrootCreated is true 131 // skip expensive embedding operations and only ephemeral operations (eg 132 // mounting /dev) are done. 133 func (t *TaskDir) buildChroot(chrootCreated bool, entries map[string]string) error { 134 if !chrootCreated { 135 // Link/copy chroot entries 136 if err := t.embedDirs(entries); err != nil { 137 return err 138 } 139 } 140 141 // Mount special dirs 142 if err := t.mountSpecialDirs(); err != nil { 143 return err 144 } 145 146 return nil 147 } 148 149 func (t *TaskDir) embedDirs(entries map[string]string) error { 150 subdirs := make(map[string]string) 151 for source, dest := range entries { 152 // Check to see if directory exists on host. 153 s, err := os.Stat(source) 154 if os.IsNotExist(err) { 155 continue 156 } 157 158 // Embedding a single file 159 if !s.IsDir() { 160 if err := createDir(t.Dir, filepath.Dir(dest)); err != nil { 161 return fmt.Errorf("Couldn't create destination directory %v: %v", dest, err) 162 } 163 164 // Copy the file. 165 taskEntry := filepath.Join(t.Dir, dest) 166 if err := linkOrCopy(source, taskEntry, s.Mode().Perm()); err != nil { 167 return err 168 } 169 170 continue 171 } 172 173 // Create destination directory. 174 destDir := filepath.Join(t.Dir, dest) 175 176 if err := createDir(t.Dir, dest); err != nil { 177 return fmt.Errorf("Couldn't create destination directory %v: %v", destDir, err) 178 } 179 180 // Enumerate the files in source. 181 dirEntries, err := ioutil.ReadDir(source) 182 if err != nil { 183 return fmt.Errorf("Couldn't read directory %v: %v", source, err) 184 } 185 186 for _, entry := range dirEntries { 187 hostEntry := filepath.Join(source, entry.Name()) 188 taskEntry := filepath.Join(destDir, filepath.Base(hostEntry)) 189 if entry.IsDir() { 190 subdirs[hostEntry] = filepath.Join(dest, filepath.Base(hostEntry)) 191 continue 192 } 193 194 // Check if entry exists. This can happen if restarting a failed 195 // task. 196 if _, err := os.Lstat(taskEntry); err == nil { 197 continue 198 } 199 200 if !entry.Mode().IsRegular() { 201 // If it is a symlink we can create it, otherwise we skip it. 202 if entry.Mode()&os.ModeSymlink == 0 { 203 continue 204 } 205 206 link, err := os.Readlink(hostEntry) 207 if err != nil { 208 return fmt.Errorf("Couldn't resolve symlink for %v: %v", source, err) 209 } 210 211 if err := os.Symlink(link, taskEntry); err != nil { 212 // Symlinking twice 213 if err.(*os.LinkError).Err.Error() != "file exists" { 214 return fmt.Errorf("Couldn't create symlink: %v", err) 215 } 216 } 217 continue 218 } 219 220 if err := linkOrCopy(hostEntry, taskEntry, entry.Mode().Perm()); err != nil { 221 return err 222 } 223 } 224 } 225 226 // Recurse on self to copy subdirectories. 227 if len(subdirs) != 0 { 228 return t.embedDirs(subdirs) 229 } 230 231 return nil 232 }