github.com/dkerwin/nomad@v0.3.3-0.20160525181927-74554135514b/client/driver/driver.go (about)

     1  package driver
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"path/filepath"
     7  	"sync"
     8  
     9  	"github.com/hashicorp/nomad/client/allocdir"
    10  	"github.com/hashicorp/nomad/client/config"
    11  	"github.com/hashicorp/nomad/client/driver/env"
    12  	"github.com/hashicorp/nomad/client/fingerprint"
    13  	"github.com/hashicorp/nomad/nomad/structs"
    14  
    15  	cstructs "github.com/hashicorp/nomad/client/driver/structs"
    16  )
    17  
    18  // BuiltinDrivers contains the built in registered drivers
    19  // which are available for allocation handling
    20  var BuiltinDrivers = map[string]Factory{
    21  	"docker":   NewDockerDriver,
    22  	"exec":     NewExecDriver,
    23  	"raw_exec": NewRawExecDriver,
    24  	"java":     NewJavaDriver,
    25  	"qemu":     NewQemuDriver,
    26  	"rkt":      NewRktDriver,
    27  }
    28  
    29  // NewDriver is used to instantiate and return a new driver
    30  // given the name and a logger
    31  func NewDriver(name string, ctx *DriverContext) (Driver, error) {
    32  	// Lookup the factory function
    33  	factory, ok := BuiltinDrivers[name]
    34  	if !ok {
    35  		return nil, fmt.Errorf("unknown driver '%s'", name)
    36  	}
    37  
    38  	// Instantiate the driver
    39  	f := factory(ctx)
    40  	return f, nil
    41  }
    42  
    43  // Factory is used to instantiate a new Driver
    44  type Factory func(*DriverContext) Driver
    45  
    46  // Driver is used for execution of tasks. This allows Nomad
    47  // to support many pluggable implementations of task drivers.
    48  // Examples could include LXC, Docker, Qemu, etc.
    49  type Driver interface {
    50  	// Drivers must support the fingerprint interface for detection
    51  	fingerprint.Fingerprint
    52  
    53  	// Start is used to being task execution
    54  	Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error)
    55  
    56  	// Open is used to re-open a handle to a task
    57  	Open(ctx *ExecContext, handleID string) (DriverHandle, error)
    58  
    59  	// Drivers must validate their configuration
    60  	Validate(map[string]interface{}) error
    61  }
    62  
    63  // DriverContext is a means to inject dependencies such as loggers, configs, and
    64  // node attributes into a Driver without having to change the Driver interface
    65  // each time we do it. Used in conjection with Factory, above.
    66  type DriverContext struct {
    67  	taskName string
    68  	config   *config.Config
    69  	logger   *log.Logger
    70  	node     *structs.Node
    71  	taskEnv  *env.TaskEnvironment
    72  }
    73  
    74  // NewEmptyDriverContext returns a DriverContext with all fields set to their
    75  // zero value.
    76  func NewEmptyDriverContext() *DriverContext {
    77  	return &DriverContext{
    78  		taskName: "",
    79  		config:   nil,
    80  		node:     nil,
    81  		logger:   nil,
    82  		taskEnv:  nil,
    83  	}
    84  }
    85  
    86  // NewDriverContext initializes a new DriverContext with the specified fields.
    87  // This enables other packages to create DriverContexts but keeps the fields
    88  // private to the driver. If we want to change this later we can gorename all of
    89  // the fields in DriverContext.
    90  func NewDriverContext(taskName string, config *config.Config, node *structs.Node,
    91  	logger *log.Logger, taskEnv *env.TaskEnvironment) *DriverContext {
    92  	return &DriverContext{
    93  		taskName: taskName,
    94  		config:   config,
    95  		node:     node,
    96  		logger:   logger,
    97  		taskEnv:  taskEnv,
    98  	}
    99  }
   100  
   101  // DriverHandle is an opaque handle into a driver used for task
   102  // manipulation
   103  type DriverHandle interface {
   104  	// Returns an opaque handle that can be used to re-open the handle
   105  	ID() string
   106  
   107  	// WaitCh is used to return a channel used wait for task completion
   108  	WaitCh() chan *cstructs.WaitResult
   109  
   110  	// Update is used to update the task if possible and update task related
   111  	// configurations.
   112  	Update(task *structs.Task) error
   113  
   114  	// Kill is used to stop the task
   115  	Kill() error
   116  }
   117  
   118  // ExecContext is shared between drivers within an allocation
   119  type ExecContext struct {
   120  	sync.Mutex
   121  
   122  	// AllocDir contains information about the alloc directory structure.
   123  	AllocDir *allocdir.AllocDir
   124  
   125  	// Alloc ID
   126  	AllocID string
   127  }
   128  
   129  // NewExecContext is used to create a new execution context
   130  func NewExecContext(alloc *allocdir.AllocDir, allocID string) *ExecContext {
   131  	return &ExecContext{AllocDir: alloc, AllocID: allocID}
   132  }
   133  
   134  // GetTaskEnv converts the alloc dir, the node, task and alloc into a
   135  // TaskEnvironment.
   136  func GetTaskEnv(allocDir *allocdir.AllocDir, node *structs.Node,
   137  	task *structs.Task, alloc *structs.Allocation) (*env.TaskEnvironment, error) {
   138  
   139  	tg := alloc.Job.LookupTaskGroup(alloc.TaskGroup)
   140  	env := env.NewTaskEnvironment(node).
   141  		SetTaskMeta(task.Meta).
   142  		SetTaskGroupMeta(tg.Meta).
   143  		SetJobMeta(alloc.Job.Meta).
   144  		SetEnvvars(task.Env).
   145  		SetTaskName(task.Name)
   146  
   147  	if allocDir != nil {
   148  		env.SetAllocDir(allocDir.SharedDir)
   149  		taskdir, ok := allocDir.TaskDirs[task.Name]
   150  		if !ok {
   151  			return nil, fmt.Errorf("failed to get task directory for task %q", task.Name)
   152  		}
   153  
   154  		env.SetTaskLocalDir(filepath.Join(taskdir, allocdir.TaskLocal))
   155  	}
   156  
   157  	if task.Resources != nil {
   158  		env.SetMemLimit(task.Resources.MemoryMB).
   159  			SetCpuLimit(task.Resources.CPU).
   160  			SetNetworks(task.Resources.Networks)
   161  	}
   162  
   163  	if alloc != nil {
   164  		env.SetAlloc(alloc)
   165  	}
   166  
   167  	return env.Build(), nil
   168  }
   169  
   170  func mapMergeStrInt(maps ...map[string]int) map[string]int {
   171  	out := map[string]int{}
   172  	for _, in := range maps {
   173  		for key, val := range in {
   174  			out[key] = val
   175  		}
   176  	}
   177  	return out
   178  }
   179  
   180  func mapMergeStrStr(maps ...map[string]string) map[string]string {
   181  	out := map[string]string{}
   182  	for _, in := range maps {
   183  		for key, val := range in {
   184  			out[key] = val
   185  		}
   186  	}
   187  	return out
   188  }