github.com/hspak/nomad@v0.7.2-0.20180309000617-bc4ae22a39a5/api/tasks.go (about) 1 package api 2 3 import ( 4 "fmt" 5 "path" 6 "path/filepath" 7 "strings" 8 "time" 9 10 "github.com/hashicorp/nomad/helper" 11 "github.com/hashicorp/nomad/nomad/structs" 12 ) 13 14 // MemoryStats holds memory usage related stats 15 type MemoryStats struct { 16 RSS uint64 17 Cache uint64 18 Swap uint64 19 MaxUsage uint64 20 KernelUsage uint64 21 KernelMaxUsage uint64 22 Measured []string 23 } 24 25 // CpuStats holds cpu usage related stats 26 type CpuStats struct { 27 SystemMode float64 28 UserMode float64 29 TotalTicks float64 30 ThrottledPeriods uint64 31 ThrottledTime uint64 32 Percent float64 33 Measured []string 34 } 35 36 // ResourceUsage holds information related to cpu and memory stats 37 type ResourceUsage struct { 38 MemoryStats *MemoryStats 39 CpuStats *CpuStats 40 } 41 42 // TaskResourceUsage holds aggregated resource usage of all processes in a Task 43 // and the resource usage of the individual pids 44 type TaskResourceUsage struct { 45 ResourceUsage *ResourceUsage 46 Timestamp int64 47 Pids map[string]*ResourceUsage 48 } 49 50 // AllocResourceUsage holds the aggregated task resource usage of the 51 // allocation. 52 type AllocResourceUsage struct { 53 ResourceUsage *ResourceUsage 54 Tasks map[string]*TaskResourceUsage 55 Timestamp int64 56 } 57 58 // RestartPolicy defines how the Nomad client restarts 59 // tasks in a taskgroup when they fail 60 type RestartPolicy struct { 61 Interval *time.Duration 62 Attempts *int 63 Delay *time.Duration 64 Mode *string 65 } 66 67 func (r *RestartPolicy) Merge(rp *RestartPolicy) { 68 if rp.Interval != nil { 69 r.Interval = rp.Interval 70 } 71 if rp.Attempts != nil { 72 r.Attempts = rp.Attempts 73 } 74 if rp.Delay != nil { 75 r.Delay = rp.Delay 76 } 77 if rp.Mode != nil { 78 r.Mode = rp.Mode 79 } 80 } 81 82 // Reschedule configures how Tasks are rescheduled when they crash or fail. 83 type ReschedulePolicy struct { 84 // Attempts limits the number of rescheduling attempts that can occur in an interval. 85 Attempts *int `mapstructure:"attempts"` 86 87 // Interval is a duration in which we can limit the number of reschedule attempts. 88 Interval *time.Duration `mapstructure:"interval"` 89 } 90 91 func (r *ReschedulePolicy) Merge(rp *ReschedulePolicy) { 92 if rp.Interval != nil { 93 r.Interval = rp.Interval 94 } 95 if rp.Attempts != nil { 96 r.Attempts = rp.Attempts 97 } 98 } 99 100 func (r *ReschedulePolicy) Copy() *ReschedulePolicy { 101 if r == nil { 102 return nil 103 } 104 nrp := new(ReschedulePolicy) 105 *nrp = *r 106 return nrp 107 } 108 109 // CheckRestart describes if and when a task should be restarted based on 110 // failing health checks. 111 type CheckRestart struct { 112 Limit int `mapstructure:"limit"` 113 Grace *time.Duration `mapstructure:"grace"` 114 IgnoreWarnings bool `mapstructure:"ignore_warnings"` 115 } 116 117 // Canonicalize CheckRestart fields if not nil. 118 func (c *CheckRestart) Canonicalize() { 119 if c == nil { 120 return 121 } 122 123 if c.Grace == nil { 124 c.Grace = helper.TimeToPtr(1 * time.Second) 125 } 126 } 127 128 // Copy returns a copy of CheckRestart or nil if unset. 129 func (c *CheckRestart) Copy() *CheckRestart { 130 if c == nil { 131 return nil 132 } 133 134 nc := new(CheckRestart) 135 nc.Limit = c.Limit 136 if c.Grace != nil { 137 g := *c.Grace 138 nc.Grace = &g 139 } 140 nc.IgnoreWarnings = c.IgnoreWarnings 141 return nc 142 } 143 144 // Merge values from other CheckRestart over default values on this 145 // CheckRestart and return merged copy. 146 func (c *CheckRestart) Merge(o *CheckRestart) *CheckRestart { 147 if c == nil { 148 // Just return other 149 return o 150 } 151 152 nc := c.Copy() 153 154 if o == nil { 155 // Nothing to merge 156 return nc 157 } 158 159 if o.Limit > 0 { 160 nc.Limit = o.Limit 161 } 162 163 if o.Grace != nil { 164 nc.Grace = o.Grace 165 } 166 167 if o.IgnoreWarnings { 168 nc.IgnoreWarnings = o.IgnoreWarnings 169 } 170 171 return nc 172 } 173 174 // The ServiceCheck data model represents the consul health check that 175 // Nomad registers for a Task 176 type ServiceCheck struct { 177 Id string 178 Name string 179 Type string 180 Command string 181 Args []string 182 Path string 183 Protocol string 184 PortLabel string `mapstructure:"port"` 185 AddressMode string `mapstructure:"address_mode"` 186 Interval time.Duration 187 Timeout time.Duration 188 InitialStatus string `mapstructure:"initial_status"` 189 TLSSkipVerify bool `mapstructure:"tls_skip_verify"` 190 Header map[string][]string 191 Method string 192 CheckRestart *CheckRestart `mapstructure:"check_restart"` 193 } 194 195 // The Service model represents a Consul service definition 196 type Service struct { 197 Id string 198 Name string 199 Tags []string 200 PortLabel string `mapstructure:"port"` 201 AddressMode string `mapstructure:"address_mode"` 202 Checks []ServiceCheck 203 CheckRestart *CheckRestart `mapstructure:"check_restart"` 204 } 205 206 func (s *Service) Canonicalize(t *Task, tg *TaskGroup, job *Job) { 207 if s.Name == "" { 208 s.Name = fmt.Sprintf("%s-%s-%s", *job.Name, *tg.Name, t.Name) 209 } 210 211 // Default to AddressModeAuto 212 if s.AddressMode == "" { 213 s.AddressMode = "auto" 214 } 215 216 // Canonicallize CheckRestart on Checks and merge Service.CheckRestart 217 // into each check. 218 for i, check := range s.Checks { 219 s.Checks[i].CheckRestart = s.CheckRestart.Merge(check.CheckRestart) 220 s.Checks[i].CheckRestart.Canonicalize() 221 } 222 } 223 224 // EphemeralDisk is an ephemeral disk object 225 type EphemeralDisk struct { 226 Sticky *bool 227 Migrate *bool 228 SizeMB *int `mapstructure:"size"` 229 } 230 231 func DefaultEphemeralDisk() *EphemeralDisk { 232 return &EphemeralDisk{ 233 Sticky: helper.BoolToPtr(false), 234 Migrate: helper.BoolToPtr(false), 235 SizeMB: helper.IntToPtr(300), 236 } 237 } 238 239 func (e *EphemeralDisk) Canonicalize() { 240 if e.Sticky == nil { 241 e.Sticky = helper.BoolToPtr(false) 242 } 243 if e.Migrate == nil { 244 e.Migrate = helper.BoolToPtr(false) 245 } 246 if e.SizeMB == nil { 247 e.SizeMB = helper.IntToPtr(300) 248 } 249 } 250 251 // TaskGroup is the unit of scheduling. 252 type TaskGroup struct { 253 Name *string 254 Count *int 255 Constraints []*Constraint 256 Tasks []*Task 257 RestartPolicy *RestartPolicy 258 ReschedulePolicy *ReschedulePolicy 259 EphemeralDisk *EphemeralDisk 260 Update *UpdateStrategy 261 Meta map[string]string 262 } 263 264 // NewTaskGroup creates a new TaskGroup. 265 func NewTaskGroup(name string, count int) *TaskGroup { 266 return &TaskGroup{ 267 Name: helper.StringToPtr(name), 268 Count: helper.IntToPtr(count), 269 } 270 } 271 272 func (g *TaskGroup) Canonicalize(job *Job) { 273 if g.Name == nil { 274 g.Name = helper.StringToPtr("") 275 } 276 if g.Count == nil { 277 g.Count = helper.IntToPtr(1) 278 } 279 for _, t := range g.Tasks { 280 t.Canonicalize(g, job) 281 } 282 if g.EphemeralDisk == nil { 283 g.EphemeralDisk = DefaultEphemeralDisk() 284 } else { 285 g.EphemeralDisk.Canonicalize() 286 } 287 288 // Merge the update policy from the job 289 if ju, tu := job.Update != nil, g.Update != nil; ju && tu { 290 // Merge the jobs and task groups definition of the update strategy 291 jc := job.Update.Copy() 292 jc.Merge(g.Update) 293 g.Update = jc 294 } else if ju && !job.Update.Empty() { 295 // Inherit the jobs as long as it is non-empty. 296 jc := job.Update.Copy() 297 g.Update = jc 298 } 299 300 if g.Update != nil { 301 g.Update.Canonicalize() 302 } 303 304 // Merge the reschedule policy from the job 305 if jr, tr := job.Reschedule != nil, g.ReschedulePolicy != nil; jr && tr { 306 jobReschedule := job.Reschedule.Copy() 307 jobReschedule.Merge(g.ReschedulePolicy) 308 g.ReschedulePolicy = jobReschedule 309 } else if jr { 310 jobReschedule := job.Reschedule.Copy() 311 g.ReschedulePolicy = jobReschedule 312 } 313 314 // Merge with default reschedule policy 315 var defaultReschedulePolicy *ReschedulePolicy 316 switch *job.Type { 317 case "service": 318 defaultReschedulePolicy = &ReschedulePolicy{ 319 Attempts: helper.IntToPtr(structs.DefaultServiceJobReschedulePolicy.Attempts), 320 Interval: helper.TimeToPtr(structs.DefaultServiceJobReschedulePolicy.Interval), 321 } 322 case "batch": 323 defaultReschedulePolicy = &ReschedulePolicy{ 324 Attempts: helper.IntToPtr(structs.DefaultBatchJobReschedulePolicy.Attempts), 325 Interval: helper.TimeToPtr(structs.DefaultBatchJobReschedulePolicy.Interval), 326 } 327 default: 328 defaultReschedulePolicy = &ReschedulePolicy{ 329 Attempts: helper.IntToPtr(0), 330 Interval: helper.TimeToPtr(0 * time.Second), 331 } 332 } 333 334 if g.ReschedulePolicy != nil { 335 defaultReschedulePolicy.Merge(g.ReschedulePolicy) 336 } 337 g.ReschedulePolicy = defaultReschedulePolicy 338 339 var defaultRestartPolicy *RestartPolicy 340 switch *job.Type { 341 case "service", "system": 342 defaultRestartPolicy = &RestartPolicy{ 343 Delay: helper.TimeToPtr(structs.DefaultServiceJobRestartPolicy.Delay), 344 Attempts: helper.IntToPtr(structs.DefaultServiceJobRestartPolicy.Attempts), 345 Interval: helper.TimeToPtr(structs.DefaultServiceJobRestartPolicy.Interval), 346 Mode: helper.StringToPtr(structs.DefaultServiceJobRestartPolicy.Mode), 347 } 348 default: 349 defaultRestartPolicy = &RestartPolicy{ 350 Delay: helper.TimeToPtr(structs.DefaultBatchJobRestartPolicy.Delay), 351 Attempts: helper.IntToPtr(structs.DefaultBatchJobRestartPolicy.Attempts), 352 Interval: helper.TimeToPtr(structs.DefaultBatchJobRestartPolicy.Interval), 353 Mode: helper.StringToPtr(structs.DefaultBatchJobRestartPolicy.Mode), 354 } 355 } 356 357 if g.RestartPolicy != nil { 358 defaultRestartPolicy.Merge(g.RestartPolicy) 359 } 360 g.RestartPolicy = defaultRestartPolicy 361 } 362 363 // Constrain is used to add a constraint to a task group. 364 func (g *TaskGroup) Constrain(c *Constraint) *TaskGroup { 365 g.Constraints = append(g.Constraints, c) 366 return g 367 } 368 369 // AddMeta is used to add a meta k/v pair to a task group 370 func (g *TaskGroup) SetMeta(key, val string) *TaskGroup { 371 if g.Meta == nil { 372 g.Meta = make(map[string]string) 373 } 374 g.Meta[key] = val 375 return g 376 } 377 378 // AddTask is used to add a new task to a task group. 379 func (g *TaskGroup) AddTask(t *Task) *TaskGroup { 380 g.Tasks = append(g.Tasks, t) 381 return g 382 } 383 384 // RequireDisk adds a ephemeral disk to the task group 385 func (g *TaskGroup) RequireDisk(disk *EphemeralDisk) *TaskGroup { 386 g.EphemeralDisk = disk 387 return g 388 } 389 390 // LogConfig provides configuration for log rotation 391 type LogConfig struct { 392 MaxFiles *int `mapstructure:"max_files"` 393 MaxFileSizeMB *int `mapstructure:"max_file_size"` 394 } 395 396 func DefaultLogConfig() *LogConfig { 397 return &LogConfig{ 398 MaxFiles: helper.IntToPtr(10), 399 MaxFileSizeMB: helper.IntToPtr(10), 400 } 401 } 402 403 func (l *LogConfig) Canonicalize() { 404 if l.MaxFiles == nil { 405 l.MaxFiles = helper.IntToPtr(10) 406 } 407 if l.MaxFileSizeMB == nil { 408 l.MaxFileSizeMB = helper.IntToPtr(10) 409 } 410 } 411 412 // DispatchPayloadConfig configures how a task gets its input from a job dispatch 413 type DispatchPayloadConfig struct { 414 File string 415 } 416 417 // Task is a single process in a task group. 418 type Task struct { 419 Name string 420 Driver string 421 User string 422 Config map[string]interface{} 423 Constraints []*Constraint 424 Env map[string]string 425 Services []*Service 426 Resources *Resources 427 Meta map[string]string 428 KillTimeout *time.Duration `mapstructure:"kill_timeout"` 429 LogConfig *LogConfig `mapstructure:"logs"` 430 Artifacts []*TaskArtifact 431 Vault *Vault 432 Templates []*Template 433 DispatchPayload *DispatchPayloadConfig 434 Leader bool 435 ShutdownDelay time.Duration `mapstructure:"shutdown_delay"` 436 KillSignal string `mapstructure:"kill_signal"` 437 } 438 439 func (t *Task) Canonicalize(tg *TaskGroup, job *Job) { 440 if t.Resources == nil { 441 t.Resources = &Resources{} 442 } 443 t.Resources.Canonicalize() 444 if t.KillTimeout == nil { 445 t.KillTimeout = helper.TimeToPtr(5 * time.Second) 446 } 447 if t.LogConfig == nil { 448 t.LogConfig = DefaultLogConfig() 449 } else { 450 t.LogConfig.Canonicalize() 451 } 452 for _, artifact := range t.Artifacts { 453 artifact.Canonicalize() 454 } 455 if t.Vault != nil { 456 t.Vault.Canonicalize() 457 } 458 for _, tmpl := range t.Templates { 459 tmpl.Canonicalize() 460 } 461 for _, s := range t.Services { 462 s.Canonicalize(t, tg, job) 463 } 464 } 465 466 // TaskArtifact is used to download artifacts before running a task. 467 type TaskArtifact struct { 468 GetterSource *string `mapstructure:"source"` 469 GetterOptions map[string]string `mapstructure:"options"` 470 GetterMode *string `mapstructure:"mode"` 471 RelativeDest *string `mapstructure:"destination"` 472 } 473 474 func (a *TaskArtifact) Canonicalize() { 475 if a.GetterMode == nil { 476 a.GetterMode = helper.StringToPtr("any") 477 } 478 if a.GetterSource == nil { 479 // Shouldn't be possible, but we don't want to panic 480 a.GetterSource = helper.StringToPtr("") 481 } 482 if a.RelativeDest == nil { 483 switch *a.GetterMode { 484 case "file": 485 // File mode should default to local/filename 486 dest := *a.GetterSource 487 dest = path.Base(dest) 488 dest = filepath.Join("local", dest) 489 a.RelativeDest = &dest 490 default: 491 // Default to a directory 492 a.RelativeDest = helper.StringToPtr("local/") 493 } 494 } 495 } 496 497 type Template struct { 498 SourcePath *string `mapstructure:"source"` 499 DestPath *string `mapstructure:"destination"` 500 EmbeddedTmpl *string `mapstructure:"data"` 501 ChangeMode *string `mapstructure:"change_mode"` 502 ChangeSignal *string `mapstructure:"change_signal"` 503 Splay *time.Duration `mapstructure:"splay"` 504 Perms *string `mapstructure:"perms"` 505 LeftDelim *string `mapstructure:"left_delimiter"` 506 RightDelim *string `mapstructure:"right_delimiter"` 507 Envvars *bool `mapstructure:"env"` 508 VaultGrace *time.Duration `mapstructure:"vault_grace"` 509 } 510 511 func (tmpl *Template) Canonicalize() { 512 if tmpl.SourcePath == nil { 513 tmpl.SourcePath = helper.StringToPtr("") 514 } 515 if tmpl.DestPath == nil { 516 tmpl.DestPath = helper.StringToPtr("") 517 } 518 if tmpl.EmbeddedTmpl == nil { 519 tmpl.EmbeddedTmpl = helper.StringToPtr("") 520 } 521 if tmpl.ChangeMode == nil { 522 tmpl.ChangeMode = helper.StringToPtr("restart") 523 } 524 if tmpl.ChangeSignal == nil { 525 if *tmpl.ChangeMode == "signal" { 526 tmpl.ChangeSignal = helper.StringToPtr("SIGHUP") 527 } else { 528 tmpl.ChangeSignal = helper.StringToPtr("") 529 } 530 } else { 531 sig := *tmpl.ChangeSignal 532 tmpl.ChangeSignal = helper.StringToPtr(strings.ToUpper(sig)) 533 } 534 if tmpl.Splay == nil { 535 tmpl.Splay = helper.TimeToPtr(5 * time.Second) 536 } 537 if tmpl.Perms == nil { 538 tmpl.Perms = helper.StringToPtr("0644") 539 } 540 if tmpl.LeftDelim == nil { 541 tmpl.LeftDelim = helper.StringToPtr("{{") 542 } 543 if tmpl.RightDelim == nil { 544 tmpl.RightDelim = helper.StringToPtr("}}") 545 } 546 if tmpl.Envvars == nil { 547 tmpl.Envvars = helper.BoolToPtr(false) 548 } 549 if tmpl.VaultGrace == nil { 550 tmpl.VaultGrace = helper.TimeToPtr(15 * time.Second) 551 } 552 } 553 554 type Vault struct { 555 Policies []string 556 Env *bool 557 ChangeMode *string `mapstructure:"change_mode"` 558 ChangeSignal *string `mapstructure:"change_signal"` 559 } 560 561 func (v *Vault) Canonicalize() { 562 if v.Env == nil { 563 v.Env = helper.BoolToPtr(true) 564 } 565 if v.ChangeMode == nil { 566 v.ChangeMode = helper.StringToPtr("restart") 567 } 568 if v.ChangeSignal == nil { 569 v.ChangeSignal = helper.StringToPtr("SIGHUP") 570 } 571 } 572 573 // NewTask creates and initializes a new Task. 574 func NewTask(name, driver string) *Task { 575 return &Task{ 576 Name: name, 577 Driver: driver, 578 } 579 } 580 581 // Configure is used to configure a single k/v pair on 582 // the task. 583 func (t *Task) SetConfig(key string, val interface{}) *Task { 584 if t.Config == nil { 585 t.Config = make(map[string]interface{}) 586 } 587 t.Config[key] = val 588 return t 589 } 590 591 // SetMeta is used to add metadata k/v pairs to the task. 592 func (t *Task) SetMeta(key, val string) *Task { 593 if t.Meta == nil { 594 t.Meta = make(map[string]string) 595 } 596 t.Meta[key] = val 597 return t 598 } 599 600 // Require is used to add resource requirements to a task. 601 func (t *Task) Require(r *Resources) *Task { 602 t.Resources = r 603 return t 604 } 605 606 // Constraint adds a new constraints to a single task. 607 func (t *Task) Constrain(c *Constraint) *Task { 608 t.Constraints = append(t.Constraints, c) 609 return t 610 } 611 612 // SetLogConfig sets a log config to a task 613 func (t *Task) SetLogConfig(l *LogConfig) *Task { 614 t.LogConfig = l 615 return t 616 } 617 618 // TaskState tracks the current state of a task and events that caused state 619 // transitions. 620 type TaskState struct { 621 State string 622 Failed bool 623 Restarts uint64 624 LastRestart time.Time 625 StartedAt time.Time 626 FinishedAt time.Time 627 Events []*TaskEvent 628 } 629 630 const ( 631 TaskSetup = "Task Setup" 632 TaskSetupFailure = "Setup Failure" 633 TaskDriverFailure = "Driver Failure" 634 TaskDriverMessage = "Driver" 635 TaskReceived = "Received" 636 TaskFailedValidation = "Failed Validation" 637 TaskStarted = "Started" 638 TaskTerminated = "Terminated" 639 TaskKilling = "Killing" 640 TaskKilled = "Killed" 641 TaskRestarting = "Restarting" 642 TaskNotRestarting = "Not Restarting" 643 TaskDownloadingArtifacts = "Downloading Artifacts" 644 TaskArtifactDownloadFailed = "Failed Artifact Download" 645 TaskSiblingFailed = "Sibling Task Failed" 646 TaskSignaling = "Signaling" 647 TaskRestartSignal = "Restart Signaled" 648 TaskLeaderDead = "Leader Task Dead" 649 TaskBuildingTaskDir = "Building Task Directory" 650 ) 651 652 // TaskEvent is an event that effects the state of a task and contains meta-data 653 // appropriate to the events type. 654 type TaskEvent struct { 655 Type string 656 Time int64 657 DisplayMessage string 658 Details map[string]string 659 // DEPRECATION NOTICE: The following fields are all deprecated. see TaskEvent struct in structs.go for details. 660 FailsTask bool 661 RestartReason string 662 SetupError string 663 DriverError string 664 DriverMessage string 665 ExitCode int 666 Signal int 667 Message string 668 KillReason string 669 KillTimeout time.Duration 670 KillError string 671 StartDelay int64 672 DownloadError string 673 ValidationError string 674 DiskLimit int64 675 DiskSize int64 676 FailedSibling string 677 VaultError string 678 TaskSignalReason string 679 TaskSignal string 680 GenericSource string 681 }