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  }