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 }