github.com/kardianos/nomad@v0.1.3-0.20151022182107-b13df73ee850/client/allocdir/alloc_dir.go (about)

     1  package allocdir
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  
    10  	"github.com/hashicorp/nomad/nomad/structs"
    11  )
    12  
    13  var (
    14  	// The name of the directory that is shared across tasks in a task group.
    15  	SharedAllocName = "alloc"
    16  
    17  	// The set of directories that exist inside eache shared alloc directory.
    18  	SharedAllocDirs = []string{"logs", "tmp", "data"}
    19  
    20  	// The name of the directory that exists inside each task directory
    21  	// regardless of driver.
    22  	TaskLocal = "local"
    23  )
    24  
    25  type AllocDir struct {
    26  	// AllocDir is the directory used for storing any state
    27  	// of this allocation. It will be purged on alloc destroy.
    28  	AllocDir string
    29  
    30  	// The shared directory is available to all tasks within the same task
    31  	// group.
    32  	SharedDir string
    33  
    34  	// TaskDirs is a mapping of task names to their non-shared directory.
    35  	TaskDirs map[string]string
    36  
    37  	// A list of locations the shared alloc has been mounted to.
    38  	mounted []string
    39  }
    40  
    41  func NewAllocDir(allocDir string) *AllocDir {
    42  	d := &AllocDir{AllocDir: allocDir, TaskDirs: make(map[string]string)}
    43  	d.SharedDir = filepath.Join(d.AllocDir, SharedAllocName)
    44  	return d
    45  }
    46  
    47  // Tears down previously build directory structure.
    48  func (d *AllocDir) Destroy() error {
    49  	// Unmount all mounted shared alloc dirs.
    50  	for _, m := range d.mounted {
    51  		if err := d.unmountSharedDir(m); err != nil {
    52  			return fmt.Errorf("Failed to unmount shared directory: %v", err)
    53  		}
    54  	}
    55  
    56  	return os.RemoveAll(d.AllocDir)
    57  }
    58  
    59  // Given a list of a task build the correct alloc structure.
    60  func (d *AllocDir) Build(tasks []*structs.Task) error {
    61  	// Make the alloc directory, owned by the nomad process.
    62  	if err := os.MkdirAll(d.AllocDir, 0700); err != nil {
    63  		return fmt.Errorf("Failed to make the alloc directory %v: %v", d.AllocDir, err)
    64  	}
    65  
    66  	// Make the shared directory and make it availabe to all user/groups.
    67  	if err := os.Mkdir(d.SharedDir, 0777); err != nil {
    68  		return err
    69  	}
    70  
    71  	// Make the shared directory have non-root permissions.
    72  	if err := d.dropDirPermissions(d.SharedDir); err != nil {
    73  		return err
    74  	}
    75  
    76  	for _, dir := range SharedAllocDirs {
    77  		p := filepath.Join(d.SharedDir, dir)
    78  		if err := os.Mkdir(p, 0777); err != nil {
    79  			return err
    80  		}
    81  	}
    82  
    83  	// Make the task directories.
    84  	for _, t := range tasks {
    85  		taskDir := filepath.Join(d.AllocDir, t.Name)
    86  		if err := os.Mkdir(taskDir, 0777); err != nil {
    87  			return err
    88  		}
    89  
    90  		// Make the task directory have non-root permissions.
    91  		if err := d.dropDirPermissions(taskDir); err != nil {
    92  			return err
    93  		}
    94  
    95  		// Create a local directory that each task can use.
    96  		local := filepath.Join(taskDir, TaskLocal)
    97  		if err := os.Mkdir(local, 0777); err != nil {
    98  			return err
    99  		}
   100  
   101  		if err := d.dropDirPermissions(local); err != nil {
   102  			return err
   103  		}
   104  
   105  		d.TaskDirs[t.Name] = taskDir
   106  	}
   107  
   108  	return nil
   109  }
   110  
   111  // Embed takes a mapping of absolute directory paths on the host to their
   112  // intended, relative location within the task directory. Embed attempts
   113  // hardlink and then defaults to copying. If the path exists on the host and
   114  // can't be embeded an error is returned.
   115  func (d *AllocDir) Embed(task string, dirs map[string]string) error {
   116  	taskdir, ok := d.TaskDirs[task]
   117  	if !ok {
   118  		return fmt.Errorf("Task directory doesn't exist for task %v", task)
   119  	}
   120  
   121  	subdirs := make(map[string]string)
   122  	for source, dest := range dirs {
   123  		// Check to see if directory exists on host.
   124  		s, err := os.Stat(source)
   125  		if os.IsNotExist(err) {
   126  			continue
   127  		}
   128  
   129  		// Enumerate the files in source.
   130  		entries, err := ioutil.ReadDir(source)
   131  		if err != nil {
   132  			return fmt.Errorf("Couldn't read directory %v: %v", source, err)
   133  		}
   134  
   135  		// Create destination directory.
   136  		destDir := filepath.Join(taskdir, dest)
   137  		if err := os.MkdirAll(destDir, s.Mode().Perm()); err != nil {
   138  			return fmt.Errorf("Couldn't create destination directory %v: %v", destDir, err)
   139  		}
   140  
   141  		for _, entry := range entries {
   142  			hostEntry := filepath.Join(source, entry.Name())
   143  			taskEntry := filepath.Join(destDir, filepath.Base(hostEntry))
   144  			if entry.IsDir() {
   145  				subdirs[hostEntry] = filepath.Join(dest, filepath.Base(hostEntry))
   146  				continue
   147  			} else if !entry.Mode().IsRegular() {
   148  				// If it is a symlink we can create it, otherwise we skip it.
   149  				if entry.Mode()&os.ModeSymlink == 0 {
   150  					continue
   151  				}
   152  
   153  				link, err := os.Readlink(hostEntry)
   154  				if err != nil {
   155  					return fmt.Errorf("Couldn't resolve symlink for %v: %v", source, err)
   156  				}
   157  
   158  				if err := os.Symlink(link, taskEntry); err != nil {
   159  					return fmt.Errorf("Couldn't create symlink: %v", err)
   160  				}
   161  				continue
   162  			}
   163  
   164  			if err := d.linkOrCopy(hostEntry, taskEntry, entry.Mode().Perm()); err != nil {
   165  				return err
   166  			}
   167  		}
   168  	}
   169  
   170  	// Recurse on self to copy subdirectories.
   171  	if len(subdirs) != 0 {
   172  		return d.Embed(task, subdirs)
   173  	}
   174  
   175  	return nil
   176  }
   177  
   178  // MountSharedDir mounts the shared directory into the specified task's
   179  // directory. Mount is documented at an OS level in their respective
   180  // implementation files.
   181  func (d *AllocDir) MountSharedDir(task string) error {
   182  	taskDir, ok := d.TaskDirs[task]
   183  	if !ok {
   184  		return fmt.Errorf("No task directory exists for %v", task)
   185  	}
   186  
   187  	taskLoc := filepath.Join(taskDir, SharedAllocName)
   188  	if err := d.mountSharedDir(taskLoc); err != nil {
   189  		return fmt.Errorf("Failed to mount shared directory for task %v: %v", task, err)
   190  	}
   191  
   192  	d.mounted = append(d.mounted, taskLoc)
   193  	return nil
   194  }
   195  
   196  func fileCopy(src, dst string, perm os.FileMode) error {
   197  	// Do a simple copy.
   198  	srcFile, err := os.Open(src)
   199  	if err != nil {
   200  		return fmt.Errorf("Couldn't open src file %v: %v", src, err)
   201  	}
   202  
   203  	dstFile, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE, perm)
   204  	if err != nil {
   205  		return fmt.Errorf("Couldn't create destination file %v: %v", dst, err)
   206  	}
   207  
   208  	if _, err := io.Copy(dstFile, srcFile); err != nil {
   209  		return fmt.Errorf("Couldn't copy %v to %v: %v", src, dst, err)
   210  	}
   211  
   212  	return nil
   213  }