github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/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 // skip embedding these paths in chroots. Used for avoiding embedding 43 // client.alloc_dir recursively. 44 skip map[string]struct{} 45 46 logger hclog.Logger 47 } 48 49 // newTaskDir creates a TaskDir struct with paths set. Call Build() to 50 // create paths on disk. 51 // 52 // Call AllocDir.NewTaskDir to create new TaskDirs 53 func newTaskDir(logger hclog.Logger, clientAllocDir, allocDir, taskName string) *TaskDir { 54 taskDir := filepath.Join(allocDir, taskName) 55 56 logger = logger.Named("task_dir").With("task_name", taskName) 57 58 // skip embedding client.alloc_dir in chroots 59 skip := map[string]struct{}{clientAllocDir: {}} 60 61 return &TaskDir{ 62 AllocDir: allocDir, 63 Dir: taskDir, 64 SharedAllocDir: filepath.Join(allocDir, SharedAllocName), 65 LogDir: filepath.Join(allocDir, SharedAllocName, LogDirName), 66 SharedTaskDir: filepath.Join(taskDir, SharedAllocName), 67 LocalDir: filepath.Join(taskDir, TaskLocal), 68 SecretsDir: filepath.Join(taskDir, TaskSecrets), 69 skip: skip, 70 logger: logger, 71 } 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. client.alloc_dir will be skipped. 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 if _, ok := t.skip[source]; ok { 153 // source in skip list 154 continue 155 } 156 157 // Check to see if directory exists on host. 158 s, err := os.Stat(source) 159 if os.IsNotExist(err) { 160 continue 161 } 162 163 // Embedding a single file 164 if !s.IsDir() { 165 if err := createDir(t.Dir, filepath.Dir(dest)); err != nil { 166 return fmt.Errorf("Couldn't create destination directory %v: %v", dest, err) 167 } 168 169 // Copy the file. 170 taskEntry := filepath.Join(t.Dir, dest) 171 uid, gid := getOwner(s) 172 if err := linkOrCopy(source, taskEntry, uid, gid, s.Mode().Perm()); err != nil { 173 return err 174 } 175 176 continue 177 } 178 179 // Create destination directory. 180 destDir := filepath.Join(t.Dir, dest) 181 182 if err := createDir(t.Dir, dest); err != nil { 183 return fmt.Errorf("Couldn't create destination directory %v: %v", destDir, err) 184 } 185 186 // Enumerate the files in source. 187 dirEntries, err := ioutil.ReadDir(source) 188 if err != nil { 189 return fmt.Errorf("Couldn't read directory %v: %v", source, err) 190 } 191 192 for _, entry := range dirEntries { 193 hostEntry := filepath.Join(source, entry.Name()) 194 taskEntry := filepath.Join(destDir, filepath.Base(hostEntry)) 195 if entry.IsDir() { 196 subdirs[hostEntry] = filepath.Join(dest, filepath.Base(hostEntry)) 197 continue 198 } 199 200 // Check if entry exists. This can happen if restarting a failed 201 // task. 202 if _, err := os.Lstat(taskEntry); err == nil { 203 continue 204 } 205 206 if !entry.Mode().IsRegular() { 207 // If it is a symlink we can create it, otherwise we skip it. 208 if entry.Mode()&os.ModeSymlink == 0 { 209 continue 210 } 211 212 link, err := os.Readlink(hostEntry) 213 if err != nil { 214 return fmt.Errorf("Couldn't resolve symlink for %v: %v", source, err) 215 } 216 217 if err := os.Symlink(link, taskEntry); err != nil { 218 // Symlinking twice 219 if err.(*os.LinkError).Err.Error() != "file exists" { 220 return fmt.Errorf("Couldn't create symlink: %v", err) 221 } 222 } 223 continue 224 } 225 226 uid, gid := getOwner(entry) 227 if err := linkOrCopy(hostEntry, taskEntry, uid, gid, entry.Mode().Perm()); err != nil { 228 return err 229 } 230 } 231 } 232 233 // Recurse on self to copy subdirectories. 234 if len(subdirs) != 0 { 235 return t.embedDirs(subdirs) 236 } 237 238 return nil 239 }