github.com/hhrutter/nomad@v0.6.0-rc2.0.20170723054333-80c4b03f0705/client/driver/driver.go (about) 1 package driver 2 3 import ( 4 "context" 5 "crypto/md5" 6 "errors" 7 "fmt" 8 "io" 9 "log" 10 "os" 11 12 "github.com/hashicorp/nomad/client/allocdir" 13 "github.com/hashicorp/nomad/client/config" 14 "github.com/hashicorp/nomad/client/driver/env" 15 "github.com/hashicorp/nomad/client/fingerprint" 16 "github.com/hashicorp/nomad/nomad/structs" 17 18 dstructs "github.com/hashicorp/nomad/client/driver/structs" 19 cstructs "github.com/hashicorp/nomad/client/structs" 20 ) 21 22 var ( 23 // BuiltinDrivers contains the built in registered drivers 24 // which are available for allocation handling 25 BuiltinDrivers = map[string]Factory{ 26 "docker": NewDockerDriver, 27 "exec": NewExecDriver, 28 "raw_exec": NewRawExecDriver, 29 "java": NewJavaDriver, 30 "qemu": NewQemuDriver, 31 "rkt": NewRktDriver, 32 } 33 34 // DriverStatsNotImplemented is the error to be returned if a driver doesn't 35 // implement stats. 36 DriverStatsNotImplemented = errors.New("stats not implemented for driver") 37 ) 38 39 // NewDriver is used to instantiate and return a new driver 40 // given the name and a logger 41 func NewDriver(name string, ctx *DriverContext) (Driver, error) { 42 // Lookup the factory function 43 factory, ok := BuiltinDrivers[name] 44 if !ok { 45 return nil, fmt.Errorf("unknown driver '%s'", name) 46 } 47 48 // Instantiate the driver 49 d := factory(ctx) 50 return d, nil 51 } 52 53 // Factory is used to instantiate a new Driver 54 type Factory func(*DriverContext) Driver 55 56 // PrestartResponse is driver state returned by Driver.Prestart. 57 type PrestartResponse struct { 58 // CreatedResources by the driver. 59 CreatedResources *CreatedResources 60 61 // Network contains driver-specific network parameters such as the port 62 // map between the host and a container. 63 // 64 // Since the network configuration may not be fully populated by 65 // Prestart, it will only be used for creating an environment for 66 // Start. It will be overridden by the DriverNetwork returned by Start. 67 Network *cstructs.DriverNetwork 68 } 69 70 // NewPrestartResponse creates a new PrestartResponse with CreatedResources 71 // initialized. 72 func NewPrestartResponse() *PrestartResponse { 73 return &PrestartResponse{ 74 CreatedResources: NewCreatedResources(), 75 } 76 } 77 78 // CreatedResources is a map of resources (eg downloaded images) created by a driver 79 // that must be cleaned up. 80 type CreatedResources struct { 81 Resources map[string][]string 82 } 83 84 func NewCreatedResources() *CreatedResources { 85 return &CreatedResources{Resources: make(map[string][]string)} 86 } 87 88 // Add a new resource if it doesn't already exist. 89 func (r *CreatedResources) Add(k, v string) { 90 if r.Resources == nil { 91 r.Resources = map[string][]string{k: []string{v}} 92 return 93 } 94 existing, ok := r.Resources[k] 95 if !ok { 96 // Key doesn't exist, create it 97 r.Resources[k] = []string{v} 98 return 99 } 100 for _, item := range existing { 101 if item == v { 102 // resource exists, return 103 return 104 } 105 } 106 107 // Resource type exists but value did not, append it 108 r.Resources[k] = append(existing, v) 109 return 110 } 111 112 // Remove a resource. Return true if removed, otherwise false. 113 // 114 // Removes the entire key if the needle is the last value in the list. 115 func (r *CreatedResources) Remove(k, needle string) bool { 116 haystack := r.Resources[k] 117 for i, item := range haystack { 118 if item == needle { 119 r.Resources[k] = append(haystack[:i], haystack[i+1:]...) 120 if len(r.Resources[k]) == 0 { 121 delete(r.Resources, k) 122 } 123 return true 124 } 125 } 126 return false 127 } 128 129 // Copy returns a new deep copy of CreatedResrouces. 130 func (r *CreatedResources) Copy() *CreatedResources { 131 if r == nil { 132 return nil 133 } 134 135 newr := CreatedResources{ 136 Resources: make(map[string][]string, len(r.Resources)), 137 } 138 for k, v := range r.Resources { 139 newv := make([]string, len(v)) 140 copy(newv, v) 141 newr.Resources[k] = newv 142 } 143 return &newr 144 } 145 146 // Merge another CreatedResources into this one. If the other CreatedResources 147 // is nil this method is a noop. 148 func (r *CreatedResources) Merge(o *CreatedResources) { 149 if o == nil { 150 return 151 } 152 153 for k, v := range o.Resources { 154 // New key 155 if len(r.Resources[k]) == 0 { 156 r.Resources[k] = v 157 continue 158 } 159 160 // Existing key 161 OUTER: 162 for _, item := range v { 163 for _, existing := range r.Resources[k] { 164 if item == existing { 165 // Found it, move on 166 continue OUTER 167 } 168 } 169 170 // New item, append it 171 r.Resources[k] = append(r.Resources[k], item) 172 } 173 } 174 } 175 176 func (r *CreatedResources) Hash() []byte { 177 h := md5.New() 178 179 for k, values := range r.Resources { 180 io.WriteString(h, k) 181 io.WriteString(h, "values") 182 for i, v := range values { 183 io.WriteString(h, fmt.Sprintf("%d-%v", i, v)) 184 } 185 } 186 187 return h.Sum(nil) 188 } 189 190 // StartResponse is returned by Driver.Start. 191 type StartResponse struct { 192 // Handle to the driver's task executor for controlling the lifecycle 193 // of the task. 194 Handle DriverHandle 195 196 // Network contains driver-specific network parameters such as the port 197 // map between the host and a container. 198 // 199 // Network may be nil as not all drivers or configurations create 200 // networks. 201 Network *cstructs.DriverNetwork 202 } 203 204 // Driver is used for execution of tasks. This allows Nomad 205 // to support many pluggable implementations of task drivers. 206 // Examples could include LXC, Docker, Qemu, etc. 207 type Driver interface { 208 // Drivers must support the fingerprint interface for detection 209 fingerprint.Fingerprint 210 211 // Prestart prepares the task environment and performs expensive 212 // intialization steps like downloading images. 213 // 214 // CreatedResources may be non-nil even when an error occurs. 215 Prestart(*ExecContext, *structs.Task) (*PrestartResponse, error) 216 217 // Start is used to begin task execution. If error is nil, 218 // StartResponse.Handle will be the handle to the task's executor. 219 // StartResponse.Network may be nil if the task doesn't configure a 220 // network. 221 Start(ctx *ExecContext, task *structs.Task) (*StartResponse, error) 222 223 // Open is used to re-open a handle to a task 224 Open(ctx *ExecContext, handleID string) (DriverHandle, error) 225 226 // Cleanup is called to remove resources which were created for a task 227 // and no longer needed. Cleanup is not called if CreatedResources is 228 // nil. 229 // 230 // If Cleanup returns a recoverable error it may be retried. On retry 231 // it will be passed the same CreatedResources, so all successfully 232 // cleaned up resources should be removed or handled idempotently. 233 Cleanup(*ExecContext, *CreatedResources) error 234 235 // Drivers must validate their configuration 236 Validate(map[string]interface{}) error 237 238 // Abilities returns the abilities of the driver 239 Abilities() DriverAbilities 240 241 // FSIsolation returns the method of filesystem isolation used 242 FSIsolation() cstructs.FSIsolation 243 } 244 245 // DriverAbilities marks the abilities the driver has. 246 type DriverAbilities struct { 247 // SendSignals marks the driver as being able to send signals 248 SendSignals bool 249 250 // Exec marks the driver as being able to execute arbitrary commands 251 // such as health checks. Used by the ScriptExecutor interface. 252 Exec bool 253 } 254 255 // LogEventFn is a callback which allows Drivers to emit task events. 256 type LogEventFn func(message string, args ...interface{}) 257 258 // DriverContext is a means to inject dependencies such as loggers, configs, and 259 // node attributes into a Driver without having to change the Driver interface 260 // each time we do it. Used in conjection with Factory, above. 261 type DriverContext struct { 262 taskName string 263 allocID string 264 config *config.Config 265 logger *log.Logger 266 node *structs.Node 267 268 emitEvent LogEventFn 269 } 270 271 // NewEmptyDriverContext returns a DriverContext with all fields set to their 272 // zero value. 273 func NewEmptyDriverContext() *DriverContext { 274 return &DriverContext{} 275 } 276 277 // NewDriverContext initializes a new DriverContext with the specified fields. 278 // This enables other packages to create DriverContexts but keeps the fields 279 // private to the driver. If we want to change this later we can gorename all of 280 // the fields in DriverContext. 281 func NewDriverContext(taskName, allocID string, config *config.Config, node *structs.Node, 282 logger *log.Logger, eventEmitter LogEventFn) *DriverContext { 283 return &DriverContext{ 284 taskName: taskName, 285 allocID: allocID, 286 config: config, 287 node: node, 288 logger: logger, 289 emitEvent: eventEmitter, 290 } 291 } 292 293 // DriverHandle is an opaque handle into a driver used for task 294 // manipulation 295 type DriverHandle interface { 296 // Returns an opaque handle that can be used to re-open the handle 297 ID() string 298 299 // WaitCh is used to return a channel used wait for task completion 300 WaitCh() chan *dstructs.WaitResult 301 302 // Update is used to update the task if possible and update task related 303 // configurations. 304 Update(task *structs.Task) error 305 306 // Kill is used to stop the task 307 Kill() error 308 309 // Stats returns aggregated stats of the driver 310 Stats() (*cstructs.TaskResourceUsage, error) 311 312 // Signal is used to send a signal to the task 313 Signal(s os.Signal) error 314 315 // ScriptExecutor is an interface used to execute commands such as 316 // health check scripts in the a DriverHandle's context. 317 ScriptExecutor 318 } 319 320 // ScriptExecutor is an interface that supports Exec()ing commands in the 321 // driver's context. Split out of DriverHandle to ease testing. 322 type ScriptExecutor interface { 323 Exec(ctx context.Context, cmd string, args []string) ([]byte, int, error) 324 } 325 326 // ExecContext is a task's execution context 327 type ExecContext struct { 328 // TaskDir contains information about the task directory structure. 329 TaskDir *allocdir.TaskDir 330 331 // TaskEnv contains the task's environment variables. 332 TaskEnv *env.TaskEnv 333 } 334 335 // NewExecContext is used to create a new execution context 336 func NewExecContext(td *allocdir.TaskDir, te *env.TaskEnv) *ExecContext { 337 return &ExecContext{ 338 TaskDir: td, 339 TaskEnv: te, 340 } 341 } 342 343 func mapMergeStrInt(maps ...map[string]int) map[string]int { 344 out := map[string]int{} 345 for _, in := range maps { 346 for key, val := range in { 347 out[key] = val 348 } 349 } 350 return out 351 } 352 353 func mapMergeStrStr(maps ...map[string]string) map[string]string { 354 out := map[string]string{} 355 for _, in := range maps { 356 for key, val := range in { 357 out[key] = val 358 } 359 } 360 return out 361 }