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  }