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