github.com/ferranbt/nomad@v0.9.3-0.20190607002617-85c449b7667c/plugins/drivers/driver.go (about) 1 package drivers 2 3 import ( 4 "context" 5 "crypto/md5" 6 "fmt" 7 "io" 8 "path/filepath" 9 "sort" 10 "strconv" 11 "time" 12 13 "github.com/hashicorp/nomad/client/allocdir" 14 cstructs "github.com/hashicorp/nomad/client/structs" 15 "github.com/hashicorp/nomad/helper" 16 "github.com/hashicorp/nomad/nomad/structs" 17 "github.com/hashicorp/nomad/plugins/base" 18 "github.com/hashicorp/nomad/plugins/drivers/proto" 19 "github.com/hashicorp/nomad/plugins/shared/hclspec" 20 pstructs "github.com/hashicorp/nomad/plugins/shared/structs" 21 "github.com/zclconf/go-cty/cty" 22 "github.com/zclconf/go-cty/cty/msgpack" 23 ) 24 25 const ( 26 // DriverHealthy is the default health description that should be used 27 // if the driver is nominal 28 DriverHealthy = "Healthy" 29 30 // Pre09TaskHandleVersion is the version used to identify that the task 31 // handle is from a driver that existed before driver plugins (v0.9). The 32 // driver should take appropriate action to handle the old driver state. 33 Pre09TaskHandleVersion = 0 34 ) 35 36 // DriverPlugin is the interface with drivers will implement. It is also 37 // implemented by a plugin client which proxies the calls to go-plugin. See 38 // the proto/driver.proto file for detailed information about each RPC and 39 // message structure. 40 type DriverPlugin interface { 41 base.BasePlugin 42 43 TaskConfigSchema() (*hclspec.Spec, error) 44 Capabilities() (*Capabilities, error) 45 Fingerprint(context.Context) (<-chan *Fingerprint, error) 46 47 RecoverTask(*TaskHandle) error 48 StartTask(*TaskConfig) (*TaskHandle, *DriverNetwork, error) 49 WaitTask(ctx context.Context, taskID string) (<-chan *ExitResult, error) 50 StopTask(taskID string, timeout time.Duration, signal string) error 51 DestroyTask(taskID string, force bool) error 52 InspectTask(taskID string) (*TaskStatus, error) 53 TaskStats(ctx context.Context, taskID string, interval time.Duration) (<-chan *cstructs.TaskResourceUsage, error) 54 TaskEvents(context.Context) (<-chan *TaskEvent, error) 55 56 SignalTask(taskID string, signal string) error 57 ExecTask(taskID string, cmd []string, timeout time.Duration) (*ExecTaskResult, error) 58 } 59 60 // ExecTaskStreamingDriver marks that a driver supports streaming exec task. This represents a user friendly 61 // interface to implement, as an alternative to the ExecTaskStreamingRawDriver, the low level interface. 62 type ExecTaskStreamingDriver interface { 63 ExecTaskStreaming(ctx context.Context, taskID string, execOptions *ExecOptions) (*ExitResult, error) 64 } 65 66 type ExecOptions struct { 67 // Command is command to run 68 Command []string 69 70 // Tty indicates whether pseudo-terminal is to be allocated 71 Tty bool 72 73 // streams 74 Stdin io.ReadCloser 75 Stdout io.WriteCloser 76 Stderr io.WriteCloser 77 78 // terminal size channel 79 ResizeCh <-chan TerminalSize 80 } 81 82 // InternalDriverPlugin is an interface that exposes functions that are only 83 // implemented by internal driver plugins. 84 type InternalDriverPlugin interface { 85 // Shutdown allows the plugin to cleanup any running state to avoid leaking 86 // resources. It should not block. 87 Shutdown() 88 } 89 90 // DriverSignalTaskNotSupported can be embedded by drivers which don't support 91 // the SignalTask RPC. This satisfies the SignalTask func requirement for the 92 // DriverPlugin interface. 93 type DriverSignalTaskNotSupported struct{} 94 95 func (DriverSignalTaskNotSupported) SignalTask(taskID, signal string) error { 96 return fmt.Errorf("SignalTask is not supported by this driver") 97 } 98 99 // DriverExecTaskNotSupported can be embedded by drivers which don't support 100 // the ExecTask RPC. This satisfies the ExecTask func requirement of the 101 // DriverPlugin interface. 102 type DriverExecTaskNotSupported struct{} 103 104 func (_ DriverExecTaskNotSupported) ExecTask(taskID, signal string) error { 105 return fmt.Errorf("ExecTask is not supported by this driver") 106 } 107 108 type HealthState string 109 110 var ( 111 HealthStateUndetected = HealthState("undetected") 112 HealthStateUnhealthy = HealthState("unhealthy") 113 HealthStateHealthy = HealthState("healthy") 114 ) 115 116 type Fingerprint struct { 117 Attributes map[string]*pstructs.Attribute 118 Health HealthState 119 HealthDescription string 120 121 // Err is set by the plugin if an error occurred during fingerprinting 122 Err error 123 } 124 125 // FSIsolation is an enumeration to describe what kind of filesystem isolation 126 // a driver supports. 127 type FSIsolation string 128 129 var ( 130 // FSIsolationNone means no isolation. The host filesystem is used. 131 FSIsolationNone = FSIsolation("none") 132 133 // FSIsolationChroot means the driver will use a chroot on the host 134 // filesystem. 135 FSIsolationChroot = FSIsolation("chroot") 136 137 // FSIsolationImage means the driver uses an image. 138 FSIsolationImage = FSIsolation("image") 139 ) 140 141 type Capabilities struct { 142 // SendSignals marks the driver as being able to send signals 143 SendSignals bool 144 145 // Exec marks the driver as being able to execute arbitrary commands 146 // such as health checks. Used by the ScriptExecutor interface. 147 Exec bool 148 149 //FSIsolation indicates what kind of filesystem isolation the driver supports. 150 FSIsolation FSIsolation 151 } 152 153 type TerminalSize struct { 154 Height int 155 Width int 156 } 157 158 type TaskConfig struct { 159 ID string 160 JobName string 161 TaskGroupName string 162 Name string 163 Env map[string]string 164 DeviceEnv map[string]string 165 Resources *Resources 166 Devices []*DeviceConfig 167 Mounts []*MountConfig 168 User string 169 AllocDir string 170 rawDriverConfig []byte 171 StdoutPath string 172 StderrPath string 173 AllocID string 174 } 175 176 func (tc *TaskConfig) Copy() *TaskConfig { 177 if tc == nil { 178 return nil 179 } 180 c := new(TaskConfig) 181 *c = *tc 182 c.Env = helper.CopyMapStringString(c.Env) 183 c.DeviceEnv = helper.CopyMapStringString(c.DeviceEnv) 184 c.Resources = tc.Resources.Copy() 185 186 if c.Devices != nil { 187 dc := make([]*DeviceConfig, len(c.Devices)) 188 for i, c := range c.Devices { 189 dc[i] = c.Copy() 190 } 191 c.Devices = dc 192 } 193 194 if c.Mounts != nil { 195 mc := make([]*MountConfig, len(c.Mounts)) 196 for i, m := range c.Mounts { 197 mc[i] = m.Copy() 198 } 199 c.Mounts = mc 200 } 201 202 return c 203 } 204 205 func (tc *TaskConfig) EnvList() []string { 206 l := make([]string, 0, len(tc.Env)) 207 for k, v := range tc.Env { 208 l = append(l, k+"="+v) 209 } 210 211 sort.Strings(l) 212 return l 213 } 214 215 func (tc *TaskConfig) TaskDir() *allocdir.TaskDir { 216 taskDir := filepath.Join(tc.AllocDir, tc.Name) 217 return &allocdir.TaskDir{ 218 Dir: taskDir, 219 SharedAllocDir: filepath.Join(tc.AllocDir, allocdir.SharedAllocName), 220 LogDir: filepath.Join(tc.AllocDir, allocdir.SharedAllocName, allocdir.LogDirName), 221 SharedTaskDir: filepath.Join(taskDir, allocdir.SharedAllocName), 222 LocalDir: filepath.Join(taskDir, allocdir.TaskLocal), 223 SecretsDir: filepath.Join(taskDir, allocdir.TaskSecrets), 224 } 225 } 226 227 func (tc *TaskConfig) DecodeDriverConfig(t interface{}) error { 228 return base.MsgPackDecode(tc.rawDriverConfig, t) 229 } 230 231 func (tc *TaskConfig) EncodeDriverConfig(val cty.Value) error { 232 data, err := msgpack.Marshal(val, val.Type()) 233 if err != nil { 234 return err 235 } 236 237 tc.rawDriverConfig = data 238 return nil 239 } 240 241 func (tc *TaskConfig) EncodeConcreteDriverConfig(t interface{}) error { 242 data := []byte{} 243 err := base.MsgPackEncode(&data, t) 244 if err != nil { 245 return err 246 } 247 248 tc.rawDriverConfig = data 249 return nil 250 } 251 252 type Resources struct { 253 NomadResources *structs.AllocatedTaskResources 254 LinuxResources *LinuxResources 255 } 256 257 func (r *Resources) Copy() *Resources { 258 if r == nil { 259 return nil 260 } 261 res := new(Resources) 262 if r.NomadResources != nil { 263 res.NomadResources = r.NomadResources.Copy() 264 } 265 if r.LinuxResources != nil { 266 res.LinuxResources = r.LinuxResources.Copy() 267 } 268 return res 269 } 270 271 type LinuxResources struct { 272 CPUPeriod int64 273 CPUQuota int64 274 CPUShares int64 275 MemoryLimitBytes int64 276 OOMScoreAdj int64 277 CpusetCPUs string 278 CpusetMems string 279 280 // PrecentTicks is used to calculate the CPUQuota, currently the docker 281 // driver exposes cpu period and quota through the driver configuration 282 // and thus the calculation for CPUQuota cannot be done on the client. 283 // This is a capatability and should only be used by docker until the docker 284 // specific options are deprecated in favor of exposes CPUPeriod and 285 // CPUQuota at the task resource stanza. 286 PercentTicks float64 287 } 288 289 func (r *LinuxResources) Copy() *LinuxResources { 290 res := new(LinuxResources) 291 *res = *r 292 return res 293 } 294 295 type DeviceConfig struct { 296 TaskPath string 297 HostPath string 298 Permissions string 299 } 300 301 func (d *DeviceConfig) Copy() *DeviceConfig { 302 if d == nil { 303 return nil 304 } 305 306 dc := new(DeviceConfig) 307 *dc = *d 308 return dc 309 } 310 311 type MountConfig struct { 312 TaskPath string 313 HostPath string 314 Readonly bool 315 } 316 317 func (m *MountConfig) Copy() *MountConfig { 318 if m == nil { 319 return nil 320 } 321 322 mc := new(MountConfig) 323 *mc = *m 324 return mc 325 } 326 327 const ( 328 TaskStateUnknown TaskState = "unknown" 329 TaskStateRunning TaskState = "running" 330 TaskStateExited TaskState = "exited" 331 ) 332 333 type TaskState string 334 335 type ExitResult struct { 336 ExitCode int 337 Signal int 338 OOMKilled bool 339 Err error 340 } 341 342 func (r *ExitResult) Successful() bool { 343 return r.ExitCode == 0 && r.Signal == 0 && r.Err == nil 344 } 345 346 func (r *ExitResult) Copy() *ExitResult { 347 if r == nil { 348 return nil 349 } 350 res := new(ExitResult) 351 *res = *r 352 return res 353 } 354 355 type TaskStatus struct { 356 ID string 357 Name string 358 State TaskState 359 StartedAt time.Time 360 CompletedAt time.Time 361 ExitResult *ExitResult 362 DriverAttributes map[string]string 363 NetworkOverride *DriverNetwork 364 } 365 366 type TaskEvent struct { 367 TaskID string 368 TaskName string 369 AllocID string 370 Timestamp time.Time 371 Message string 372 Annotations map[string]string 373 374 // Err is only used if an error occurred while consuming the RPC stream 375 Err error 376 } 377 378 type ExecTaskResult struct { 379 Stdout []byte 380 Stderr []byte 381 ExitResult *ExitResult 382 } 383 384 // DriverNetwork is the network created by driver's (eg Docker's bridge 385 // network) during Prestart. 386 type DriverNetwork struct { 387 // PortMap can be set by drivers to replace ports in environment 388 // variables with driver-specific mappings. 389 PortMap map[string]int 390 391 // IP is the IP address for the task created by the driver. 392 IP string 393 394 // AutoAdvertise indicates whether the driver thinks services that 395 // choose to auto-advertise-addresses should use this IP instead of the 396 // host's. eg If a Docker network plugin is used 397 AutoAdvertise bool 398 } 399 400 // Advertise returns true if the driver suggests using the IP set. May be 401 // called on a nil Network in which case it returns false. 402 func (d *DriverNetwork) Advertise() bool { 403 return d != nil && d.AutoAdvertise 404 } 405 406 // Copy a DriverNetwork struct. If it is nil, nil is returned. 407 func (d *DriverNetwork) Copy() *DriverNetwork { 408 if d == nil { 409 return nil 410 } 411 pm := make(map[string]int, len(d.PortMap)) 412 for k, v := range d.PortMap { 413 pm[k] = v 414 } 415 return &DriverNetwork{ 416 PortMap: pm, 417 IP: d.IP, 418 AutoAdvertise: d.AutoAdvertise, 419 } 420 } 421 422 // Hash the contents of a DriverNetwork struct to detect changes. If it is nil, 423 // an empty slice is returned. 424 func (d *DriverNetwork) Hash() []byte { 425 if d == nil { 426 return []byte{} 427 } 428 h := md5.New() 429 io.WriteString(h, d.IP) 430 io.WriteString(h, strconv.FormatBool(d.AutoAdvertise)) 431 for k, v := range d.PortMap { 432 io.WriteString(h, k) 433 io.WriteString(h, strconv.Itoa(v)) 434 } 435 return h.Sum(nil) 436 } 437 438 //// helper types for operating on raw exec operation 439 // we alias proto instances as much as possible to avoid conversion overhead 440 441 // ExecTaskStreamingRawDriver represents a low-level interface for executing a streaming exec 442 // call, and is intended to be used when driver instance is to delegate exec handling to another 443 // backend, e.g. to a executor or a driver behind a grpc/rpc protocol 444 // 445 // Nomad client would prefer this interface method over `ExecTaskStreaming` if driver implements it. 446 type ExecTaskStreamingRawDriver interface { 447 ExecTaskStreamingRaw( 448 ctx context.Context, 449 taskID string, 450 command []string, 451 tty bool, 452 stream ExecTaskStream) error 453 } 454 455 // ExecTaskStream represents a stream of exec streaming messages, 456 // and is a handle to get stdin and tty size and send back 457 // stdout/stderr and exit operations. 458 // 459 // The methods are not concurrent safe; callers must ensure that methods are called 460 // from at most one goroutine. 461 type ExecTaskStream interface { 462 // Send relays response message back to API. 463 // 464 // The call is synchronous and no references to message is held: once 465 // method call completes, the message reference can be reused or freed. 466 Send(*ExecTaskStreamingResponseMsg) error 467 468 // Receive exec streaming messages from API. Returns `io.EOF` on completion of stream. 469 Recv() (*ExecTaskStreamingRequestMsg, error) 470 } 471 472 type ExecTaskStreamingRequestMsg = proto.ExecTaskStreamingRequest 473 type ExecTaskStreamingResponseMsg = proto.ExecTaskStreamingResponse