github.com/hhrutter/nomad@v0.6.0-rc2.0.20170723054333-80c4b03f0705/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 // Copy a TaskDir. Panics if TaskDir is nil as TaskDirs should never be nil. 61 func (t *TaskDir) Copy() *TaskDir { 62 // No nested structures other than the logger which is safe to share, 63 // so just copy the struct 64 tcopy := *t 65 return &tcopy 66 } 67 68 // Build default directories and permissions in a task directory. chrootCreated 69 // allows skipping chroot creation if the caller knows it has already been 70 // done. 71 func (t *TaskDir) Build(chrootCreated bool, chroot map[string]string, fsi cstructs.FSIsolation) error { 72 if err := os.MkdirAll(t.Dir, 0777); err != nil { 73 return err 74 } 75 76 // Make the task directory have non-root permissions. 77 if err := dropDirPermissions(t.Dir, os.ModePerm); err != nil { 78 return err 79 } 80 81 // Create a local directory that each task can use. 82 if err := os.MkdirAll(t.LocalDir, 0777); err != nil { 83 return err 84 } 85 86 if err := dropDirPermissions(t.LocalDir, os.ModePerm); err != nil { 87 return err 88 } 89 90 // Create the directories that should be in every task. 91 for dir, perms := range TaskDirs { 92 absdir := filepath.Join(t.Dir, dir) 93 if err := os.MkdirAll(absdir, perms); err != nil { 94 return err 95 } 96 97 if err := dropDirPermissions(absdir, perms); err != nil { 98 return err 99 } 100 } 101 102 // Only link alloc dir into task dir for chroot fs isolation. 103 // Image based isolation will bind the shared alloc dir in the driver. 104 // If there's no isolation the task will use the host path to the 105 // shared alloc dir. 106 if fsi == cstructs.FSIsolationChroot { 107 // If the path doesn't exist OR it exists and is empty, link it 108 empty, _ := pathEmpty(t.SharedTaskDir) 109 if !pathExists(t.SharedTaskDir) || empty { 110 if err := linkDir(t.SharedAllocDir, t.SharedTaskDir); err != nil { 111 return fmt.Errorf("Failed to mount shared directory for task: %v", err) 112 } 113 } 114 } 115 116 // Create the secret directory 117 if err := createSecretDir(t.SecretsDir); err != nil { 118 return err 119 } 120 121 if err := dropDirPermissions(t.SecretsDir, os.ModePerm); err != nil { 122 return err 123 } 124 125 // Build chroot if chroot filesystem isolation is going to be used 126 if fsi == cstructs.FSIsolationChroot { 127 if err := t.buildChroot(chrootCreated, chroot); err != nil { 128 return err 129 } 130 } 131 132 return nil 133 } 134 135 // buildChroot takes a mapping of absolute directory or file paths on the host 136 // to their intended, relative location within the task directory. This 137 // attempts hardlink and then defaults to copying. If the path exists on the 138 // host and can't be embedded an error is returned. If chrootCreated is true 139 // skip expensive embedding operations and only ephemeral operations (eg 140 // mounting /dev) are done. 141 func (t *TaskDir) buildChroot(chrootCreated bool, entries map[string]string) error { 142 if !chrootCreated { 143 // Link/copy chroot entries 144 if err := t.embedDirs(entries); err != nil { 145 return err 146 } 147 } 148 149 // Mount special dirs 150 if err := t.mountSpecialDirs(); err != nil { 151 return err 152 } 153 154 return nil 155 } 156 157 func (t *TaskDir) embedDirs(entries map[string]string) error { 158 subdirs := make(map[string]string) 159 for source, dest := range entries { 160 // Check to see if directory exists on host. 161 s, err := os.Stat(source) 162 if os.IsNotExist(err) { 163 continue 164 } 165 166 // Embedding a single file 167 if !s.IsDir() { 168 if err := createDir(t.Dir, filepath.Dir(dest)); err != nil { 169 return fmt.Errorf("Couldn't create destination directory %v: %v", dest, err) 170 } 171 172 // Copy the file. 173 taskEntry := filepath.Join(t.Dir, dest) 174 uid, gid := getOwner(s) 175 if err := linkOrCopy(source, taskEntry, uid, gid, s.Mode().Perm()); err != nil { 176 return err 177 } 178 179 continue 180 } 181 182 // Create destination directory. 183 destDir := filepath.Join(t.Dir, dest) 184 185 if err := createDir(t.Dir, dest); err != nil { 186 return fmt.Errorf("Couldn't create destination directory %v: %v", destDir, err) 187 } 188 189 // Enumerate the files in source. 190 dirEntries, err := ioutil.ReadDir(source) 191 if err != nil { 192 return fmt.Errorf("Couldn't read directory %v: %v", source, err) 193 } 194 195 for _, entry := range dirEntries { 196 hostEntry := filepath.Join(source, entry.Name()) 197 taskEntry := filepath.Join(destDir, filepath.Base(hostEntry)) 198 if entry.IsDir() { 199 subdirs[hostEntry] = filepath.Join(dest, filepath.Base(hostEntry)) 200 continue 201 } 202 203 // Check if entry exists. This can happen if restarting a failed 204 // task. 205 if _, err := os.Lstat(taskEntry); err == nil { 206 continue 207 } 208 209 if !entry.Mode().IsRegular() { 210 // If it is a symlink we can create it, otherwise we skip it. 211 if entry.Mode()&os.ModeSymlink == 0 { 212 continue 213 } 214 215 link, err := os.Readlink(hostEntry) 216 if err != nil { 217 return fmt.Errorf("Couldn't resolve symlink for %v: %v", source, err) 218 } 219 220 if err := os.Symlink(link, taskEntry); err != nil { 221 // Symlinking twice 222 if err.(*os.LinkError).Err.Error() != "file exists" { 223 return fmt.Errorf("Couldn't create symlink: %v", err) 224 } 225 } 226 continue 227 } 228 229 uid, gid := getOwner(entry) 230 if err := linkOrCopy(hostEntry, taskEntry, uid, gid, entry.Mode().Perm()); err != nil { 231 return err 232 } 233 } 234 } 235 236 // Recurse on self to copy subdirectories. 237 if len(subdirs) != 0 { 238 return t.embedDirs(subdirs) 239 } 240 241 return nil 242 }