github.com/mattyr/nomad@v0.3.3-0.20160919021406-3485a065154a/client/driver/mock_driver.go (about) 1 // +build nomad_test 2 3 package driver 4 5 import ( 6 "errors" 7 "log" 8 "time" 9 10 "github.com/mitchellh/mapstructure" 11 12 "github.com/hashicorp/nomad/client/config" 13 dstructs "github.com/hashicorp/nomad/client/driver/structs" 14 "github.com/hashicorp/nomad/client/fingerprint" 15 cstructs "github.com/hashicorp/nomad/client/structs" 16 "github.com/hashicorp/nomad/nomad/structs" 17 ) 18 19 // Add the mock driver to the list of builtin drivers 20 func init() { 21 BuiltinDrivers["mock_driver"] = NewMockDriver 22 } 23 24 // MockDriverConfig is the driver configuration for the MockDriver 25 type MockDriverConfig struct { 26 27 // KillAfter is the duration after which the mock driver indicates the task 28 // has exited after getting the initial SIGINT signal 29 KillAfter time.Duration `mapstructure:"kill_after"` 30 31 // RunFor is the duration for which the fake task runs for. After this 32 // period the MockDriver responds to the task running indicating that the 33 // task has terminated 34 RunFor time.Duration `mapstructure:"run_for"` 35 36 // ExitCode is the exit code with which the MockDriver indicates the task 37 // has exited 38 ExitCode int `mapstructure:"exit_code"` 39 40 // ExitSignal is the signal with which the MockDriver indicates the task has 41 // been killed 42 ExitSignal int `mapstructure:"exit_signal"` 43 44 // ExitErrMsg is the error message that the task returns while exiting 45 ExitErrMsg string `mapstructure:"exit_err_msg"` 46 } 47 48 // MockDriver is a driver which is used for testing purposes 49 type MockDriver struct { 50 DriverContext 51 fingerprint.StaticFingerprinter 52 } 53 54 // NewMockDriver is a factory method which returns a new Mock Driver 55 func NewMockDriver(ctx *DriverContext) Driver { 56 return &MockDriver{DriverContext: *ctx} 57 } 58 59 // Start starts the mock driver 60 func (m *MockDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { 61 var driverConfig MockDriverConfig 62 dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 63 DecodeHook: mapstructure.StringToTimeDurationHookFunc(), 64 WeaklyTypedInput: true, 65 Result: &driverConfig, 66 }) 67 if err != nil { 68 return nil, err 69 } 70 if err := dec.Decode(task.Config); err != nil { 71 return nil, err 72 } 73 74 h := mockDriverHandle{ 75 taskName: task.Name, 76 runFor: driverConfig.RunFor, 77 killAfter: driverConfig.KillAfter, 78 killTimeout: task.KillTimeout, 79 exitCode: driverConfig.ExitCode, 80 exitSignal: driverConfig.ExitSignal, 81 logger: m.logger, 82 doneCh: make(chan struct{}), 83 waitCh: make(chan *dstructs.WaitResult, 1), 84 } 85 if driverConfig.ExitErrMsg != "" { 86 h.exitErr = errors.New(driverConfig.ExitErrMsg) 87 } 88 m.logger.Printf("[DEBUG] driver.mock: starting task %q", task.Name) 89 go h.run() 90 return &h, nil 91 } 92 93 // TODO implement Open when we need it. 94 // Open re-connects the driver to the running task 95 func (m *MockDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) { 96 return nil, nil 97 } 98 99 // TODO implement Open when we need it. 100 // Validate validates the mock driver configuration 101 func (m *MockDriver) Validate(map[string]interface{}) error { 102 return nil 103 } 104 105 // TODO implement Open when we need it. 106 // Fingerprint fingerprints a node and returns if MockDriver is enabled 107 func (m *MockDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { 108 node.Attributes["driver.mock_driver"] = "1" 109 return true, nil 110 } 111 112 // MockDriverHandle is a driver handler which supervises a mock task 113 type mockDriverHandle struct { 114 taskName string 115 runFor time.Duration 116 killAfter time.Duration 117 killTimeout time.Duration 118 exitCode int 119 exitSignal int 120 exitErr error 121 logger *log.Logger 122 waitCh chan *dstructs.WaitResult 123 doneCh chan struct{} 124 } 125 126 // TODO Implement when we need it. 127 func (h *mockDriverHandle) ID() string { 128 return "" 129 } 130 131 // TODO Implement when we need it. 132 func (h *mockDriverHandle) WaitCh() chan *dstructs.WaitResult { 133 return h.waitCh 134 } 135 136 // TODO Implement when we need it. 137 func (h *mockDriverHandle) Update(task *structs.Task) error { 138 return nil 139 } 140 141 // Kill kills a mock task 142 func (h *mockDriverHandle) Kill() error { 143 h.logger.Printf("[DEBUG] driver.mock: killing task %q after kill timeout: %v", h.taskName, h.killTimeout) 144 select { 145 case <-h.doneCh: 146 case <-time.After(h.killAfter): 147 close(h.doneCh) 148 case <-time.After(h.killTimeout): 149 h.logger.Printf("[DEBUG] driver.mock: terminating task %q", h.taskName) 150 close(h.doneCh) 151 } 152 return nil 153 } 154 155 // TODO Implement when we need it. 156 func (h *mockDriverHandle) Stats() (*cstructs.TaskResourceUsage, error) { 157 return nil, nil 158 } 159 160 // run waits for the configured amount of time and then indicates the task has 161 // terminated 162 func (h *mockDriverHandle) run() { 163 timer := time.NewTimer(h.runFor) 164 defer timer.Stop() 165 for { 166 select { 167 case <-timer.C: 168 close(h.doneCh) 169 case <-h.doneCh: 170 h.logger.Printf("[DEBUG] driver.mock: finished running task %q", h.taskName) 171 h.waitCh <- dstructs.NewWaitResult(h.exitCode, h.exitSignal, h.exitErr) 172 return 173 } 174 } 175 }