github.com/zoomfoo/nomad@v0.8.5-0.20180907175415-f28fd3a1a056/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  	// Delay is a minimum duration to wait between reschedule attempts.
    91  	// The delay function determines how much subsequent reschedule attempts are delayed by.
    92  	Delay *time.Duration `mapstructure:"delay"`
    93  
    94  	// DelayFunction determines how the delay progressively changes on subsequent reschedule
    95  	// attempts. Valid values are "exponential", "constant", and "fibonacci".
    96  	DelayFunction *string `mapstructure:"delay_function"`
    97  
    98  	// MaxDelay is an upper bound on the delay.
    99  	MaxDelay *time.Duration `mapstructure:"max_delay"`
   100  
   101  	// Unlimited allows rescheduling attempts until they succeed
   102  	Unlimited *bool `mapstructure:"unlimited"`
   103  }
   104  
   105  func (r *ReschedulePolicy) Merge(rp *ReschedulePolicy) {
   106  	if rp == nil {
   107  		return
   108  	}
   109  	if rp.Interval != nil {
   110  		r.Interval = rp.Interval
   111  	}
   112  	if rp.Attempts != nil {
   113  		r.Attempts = rp.Attempts
   114  	}
   115  	if rp.Delay != nil {
   116  		r.Delay = rp.Delay
   117  	}
   118  	if rp.DelayFunction != nil {
   119  		r.DelayFunction = rp.DelayFunction
   120  	}
   121  	if rp.MaxDelay != nil {
   122  		r.MaxDelay = rp.MaxDelay
   123  	}
   124  	if rp.Unlimited != nil {
   125  		r.Unlimited = rp.Unlimited
   126  	}
   127  }
   128  
   129  func (r *ReschedulePolicy) Canonicalize(jobType string) {
   130  	dp := NewDefaultReschedulePolicy(jobType)
   131  	if r.Interval == nil {
   132  		r.Interval = dp.Interval
   133  	}
   134  	if r.Attempts == nil {
   135  		r.Attempts = dp.Attempts
   136  	}
   137  	if r.Delay == nil {
   138  		r.Delay = dp.Delay
   139  	}
   140  	if r.DelayFunction == nil {
   141  		r.DelayFunction = dp.DelayFunction
   142  	}
   143  	if r.MaxDelay == nil {
   144  		r.MaxDelay = dp.MaxDelay
   145  	}
   146  	if r.Unlimited == nil {
   147  		r.Unlimited = dp.Unlimited
   148  	}
   149  }
   150  
   151  // Affinity is used to serialize task group affinities
   152  type Affinity struct {
   153  	LTarget string  // Left-hand target
   154  	RTarget string  // Right-hand target
   155  	Operand string  // Constraint operand (<=, <, =, !=, >, >=), set_contains_all, set_contains_any
   156  	Weight  float64 // Weight applied to nodes that match the affinity. Can be negative
   157  }
   158  
   159  func NewAffinity(LTarget string, Operand string, RTarget string, Weight float64) *Affinity {
   160  	return &Affinity{
   161  		LTarget: LTarget,
   162  		RTarget: RTarget,
   163  		Operand: Operand,
   164  		Weight:  Weight,
   165  	}
   166  }
   167  
   168  func NewDefaultReschedulePolicy(jobType string) *ReschedulePolicy {
   169  	var dp *ReschedulePolicy
   170  	switch jobType {
   171  	case "service":
   172  		dp = &ReschedulePolicy{
   173  			Attempts:      helper.IntToPtr(structs.DefaultServiceJobReschedulePolicy.Attempts),
   174  			Interval:      helper.TimeToPtr(structs.DefaultServiceJobReschedulePolicy.Interval),
   175  			Delay:         helper.TimeToPtr(structs.DefaultServiceJobReschedulePolicy.Delay),
   176  			DelayFunction: helper.StringToPtr(structs.DefaultServiceJobReschedulePolicy.DelayFunction),
   177  			MaxDelay:      helper.TimeToPtr(structs.DefaultServiceJobReschedulePolicy.MaxDelay),
   178  			Unlimited:     helper.BoolToPtr(structs.DefaultServiceJobReschedulePolicy.Unlimited),
   179  		}
   180  	case "batch":
   181  		dp = &ReschedulePolicy{
   182  			Attempts:      helper.IntToPtr(structs.DefaultBatchJobReschedulePolicy.Attempts),
   183  			Interval:      helper.TimeToPtr(structs.DefaultBatchJobReschedulePolicy.Interval),
   184  			Delay:         helper.TimeToPtr(structs.DefaultBatchJobReschedulePolicy.Delay),
   185  			DelayFunction: helper.StringToPtr(structs.DefaultBatchJobReschedulePolicy.DelayFunction),
   186  			MaxDelay:      helper.TimeToPtr(structs.DefaultBatchJobReschedulePolicy.MaxDelay),
   187  			Unlimited:     helper.BoolToPtr(structs.DefaultBatchJobReschedulePolicy.Unlimited),
   188  		}
   189  
   190  	case "system":
   191  		dp = &ReschedulePolicy{
   192  			Attempts:      helper.IntToPtr(0),
   193  			Interval:      helper.TimeToPtr(0),
   194  			Delay:         helper.TimeToPtr(0),
   195  			DelayFunction: helper.StringToPtr(""),
   196  			MaxDelay:      helper.TimeToPtr(0),
   197  			Unlimited:     helper.BoolToPtr(false),
   198  		}
   199  	}
   200  	return dp
   201  }
   202  
   203  func (r *ReschedulePolicy) Copy() *ReschedulePolicy {
   204  	if r == nil {
   205  		return nil
   206  	}
   207  	nrp := new(ReschedulePolicy)
   208  	*nrp = *r
   209  	return nrp
   210  }
   211  
   212  func (p *ReschedulePolicy) String() string {
   213  	if p == nil {
   214  		return ""
   215  	}
   216  	if *p.Unlimited {
   217  		return fmt.Sprintf("unlimited with %v delay, max_delay = %v", *p.DelayFunction, *p.MaxDelay)
   218  	}
   219  	return fmt.Sprintf("%v in %v with %v delay, max_delay = %v", *p.Attempts, *p.Interval, *p.DelayFunction, *p.MaxDelay)
   220  }
   221  
   222  // Spread is used to serialize task group allocation spread preferences
   223  type Spread struct {
   224  	Attribute    string
   225  	Weight       int
   226  	SpreadTarget []*SpreadTarget
   227  }
   228  
   229  // SpreadTarget is used to serialize target allocation spread percentages
   230  type SpreadTarget struct {
   231  	Value   string
   232  	Percent uint32
   233  }
   234  
   235  func NewSpreadTarget(value string, percent uint32) *SpreadTarget {
   236  	return &SpreadTarget{
   237  		Value:   value,
   238  		Percent: percent,
   239  	}
   240  }
   241  
   242  func NewSpread(attribute string, weight int, spreadTargets []*SpreadTarget) *Spread {
   243  	return &Spread{
   244  		Attribute:    attribute,
   245  		Weight:       weight,
   246  		SpreadTarget: spreadTargets,
   247  	}
   248  }
   249  
   250  // CheckRestart describes if and when a task should be restarted based on
   251  // failing health checks.
   252  type CheckRestart struct {
   253  	Limit          int            `mapstructure:"limit"`
   254  	Grace          *time.Duration `mapstructure:"grace"`
   255  	IgnoreWarnings bool           `mapstructure:"ignore_warnings"`
   256  }
   257  
   258  // Canonicalize CheckRestart fields if not nil.
   259  func (c *CheckRestart) Canonicalize() {
   260  	if c == nil {
   261  		return
   262  	}
   263  
   264  	if c.Grace == nil {
   265  		c.Grace = helper.TimeToPtr(1 * time.Second)
   266  	}
   267  }
   268  
   269  // Copy returns a copy of CheckRestart or nil if unset.
   270  func (c *CheckRestart) Copy() *CheckRestart {
   271  	if c == nil {
   272  		return nil
   273  	}
   274  
   275  	nc := new(CheckRestart)
   276  	nc.Limit = c.Limit
   277  	if c.Grace != nil {
   278  		g := *c.Grace
   279  		nc.Grace = &g
   280  	}
   281  	nc.IgnoreWarnings = c.IgnoreWarnings
   282  	return nc
   283  }
   284  
   285  // Merge values from other CheckRestart over default values on this
   286  // CheckRestart and return merged copy.
   287  func (c *CheckRestart) Merge(o *CheckRestart) *CheckRestart {
   288  	if c == nil {
   289  		// Just return other
   290  		return o
   291  	}
   292  
   293  	nc := c.Copy()
   294  
   295  	if o == nil {
   296  		// Nothing to merge
   297  		return nc
   298  	}
   299  
   300  	if o.Limit > 0 {
   301  		nc.Limit = o.Limit
   302  	}
   303  
   304  	if o.Grace != nil {
   305  		nc.Grace = o.Grace
   306  	}
   307  
   308  	if o.IgnoreWarnings {
   309  		nc.IgnoreWarnings = o.IgnoreWarnings
   310  	}
   311  
   312  	return nc
   313  }
   314  
   315  // The ServiceCheck data model represents the consul health check that
   316  // Nomad registers for a Task
   317  type ServiceCheck struct {
   318  	Id            string
   319  	Name          string
   320  	Type          string
   321  	Command       string
   322  	Args          []string
   323  	Path          string
   324  	Protocol      string
   325  	PortLabel     string `mapstructure:"port"`
   326  	AddressMode   string `mapstructure:"address_mode"`
   327  	Interval      time.Duration
   328  	Timeout       time.Duration
   329  	InitialStatus string `mapstructure:"initial_status"`
   330  	TLSSkipVerify bool   `mapstructure:"tls_skip_verify"`
   331  	Header        map[string][]string
   332  	Method        string
   333  	CheckRestart  *CheckRestart `mapstructure:"check_restart"`
   334  	GRPCService   string        `mapstructure:"grpc_service"`
   335  	GRPCUseTLS    bool          `mapstructure:"grpc_use_tls"`
   336  }
   337  
   338  // The Service model represents a Consul service definition
   339  type Service struct {
   340  	Id           string
   341  	Name         string
   342  	Tags         []string
   343  	CanaryTags   []string `mapstructure:"canary_tags"`
   344  	PortLabel    string   `mapstructure:"port"`
   345  	AddressMode  string   `mapstructure:"address_mode"`
   346  	Checks       []ServiceCheck
   347  	CheckRestart *CheckRestart `mapstructure:"check_restart"`
   348  }
   349  
   350  func (s *Service) Canonicalize(t *Task, tg *TaskGroup, job *Job) {
   351  	if s.Name == "" {
   352  		s.Name = fmt.Sprintf("%s-%s-%s", *job.Name, *tg.Name, t.Name)
   353  	}
   354  
   355  	// Default to AddressModeAuto
   356  	if s.AddressMode == "" {
   357  		s.AddressMode = "auto"
   358  	}
   359  
   360  	// Canonicalize CheckRestart on Checks and merge Service.CheckRestart
   361  	// into each check.
   362  	for i, check := range s.Checks {
   363  		s.Checks[i].CheckRestart = s.CheckRestart.Merge(check.CheckRestart)
   364  		s.Checks[i].CheckRestart.Canonicalize()
   365  	}
   366  }
   367  
   368  // EphemeralDisk is an ephemeral disk object
   369  type EphemeralDisk struct {
   370  	Sticky  *bool
   371  	Migrate *bool
   372  	SizeMB  *int `mapstructure:"size"`
   373  }
   374  
   375  func DefaultEphemeralDisk() *EphemeralDisk {
   376  	return &EphemeralDisk{
   377  		Sticky:  helper.BoolToPtr(false),
   378  		Migrate: helper.BoolToPtr(false),
   379  		SizeMB:  helper.IntToPtr(300),
   380  	}
   381  }
   382  
   383  func (e *EphemeralDisk) Canonicalize() {
   384  	if e.Sticky == nil {
   385  		e.Sticky = helper.BoolToPtr(false)
   386  	}
   387  	if e.Migrate == nil {
   388  		e.Migrate = helper.BoolToPtr(false)
   389  	}
   390  	if e.SizeMB == nil {
   391  		e.SizeMB = helper.IntToPtr(300)
   392  	}
   393  }
   394  
   395  // MigrateStrategy describes how allocations for a task group should be
   396  // migrated between nodes (eg when draining).
   397  type MigrateStrategy struct {
   398  	MaxParallel     *int           `mapstructure:"max_parallel"`
   399  	HealthCheck     *string        `mapstructure:"health_check"`
   400  	MinHealthyTime  *time.Duration `mapstructure:"min_healthy_time"`
   401  	HealthyDeadline *time.Duration `mapstructure:"healthy_deadline"`
   402  }
   403  
   404  func DefaultMigrateStrategy() *MigrateStrategy {
   405  	return &MigrateStrategy{
   406  		MaxParallel:     helper.IntToPtr(1),
   407  		HealthCheck:     helper.StringToPtr("checks"),
   408  		MinHealthyTime:  helper.TimeToPtr(10 * time.Second),
   409  		HealthyDeadline: helper.TimeToPtr(5 * time.Minute),
   410  	}
   411  }
   412  
   413  func (m *MigrateStrategy) Canonicalize() {
   414  	if m == nil {
   415  		return
   416  	}
   417  	defaults := DefaultMigrateStrategy()
   418  	if m.MaxParallel == nil {
   419  		m.MaxParallel = defaults.MaxParallel
   420  	}
   421  	if m.HealthCheck == nil {
   422  		m.HealthCheck = defaults.HealthCheck
   423  	}
   424  	if m.MinHealthyTime == nil {
   425  		m.MinHealthyTime = defaults.MinHealthyTime
   426  	}
   427  	if m.HealthyDeadline == nil {
   428  		m.HealthyDeadline = defaults.HealthyDeadline
   429  	}
   430  }
   431  
   432  func (m *MigrateStrategy) Merge(o *MigrateStrategy) {
   433  	if o.MaxParallel != nil {
   434  		m.MaxParallel = o.MaxParallel
   435  	}
   436  	if o.HealthCheck != nil {
   437  		m.HealthCheck = o.HealthCheck
   438  	}
   439  	if o.MinHealthyTime != nil {
   440  		m.MinHealthyTime = o.MinHealthyTime
   441  	}
   442  	if o.HealthyDeadline != nil {
   443  		m.HealthyDeadline = o.HealthyDeadline
   444  	}
   445  }
   446  
   447  func (m *MigrateStrategy) Copy() *MigrateStrategy {
   448  	if m == nil {
   449  		return nil
   450  	}
   451  	nm := new(MigrateStrategy)
   452  	*nm = *m
   453  	return nm
   454  }
   455  
   456  // TaskGroup is the unit of scheduling.
   457  type TaskGroup struct {
   458  	Name             *string
   459  	Count            *int
   460  	Constraints      []*Constraint
   461  	Affinities       []*Affinity
   462  	Tasks            []*Task
   463  	Spreads          []*Spread
   464  	RestartPolicy    *RestartPolicy
   465  	ReschedulePolicy *ReschedulePolicy
   466  	EphemeralDisk    *EphemeralDisk
   467  	Update           *UpdateStrategy
   468  	Migrate          *MigrateStrategy
   469  	Meta             map[string]string
   470  }
   471  
   472  // NewTaskGroup creates a new TaskGroup.
   473  func NewTaskGroup(name string, count int) *TaskGroup {
   474  	return &TaskGroup{
   475  		Name:  helper.StringToPtr(name),
   476  		Count: helper.IntToPtr(count),
   477  	}
   478  }
   479  
   480  func (g *TaskGroup) Canonicalize(job *Job) {
   481  	if g.Name == nil {
   482  		g.Name = helper.StringToPtr("")
   483  	}
   484  	if g.Count == nil {
   485  		g.Count = helper.IntToPtr(1)
   486  	}
   487  	for _, t := range g.Tasks {
   488  		t.Canonicalize(g, job)
   489  	}
   490  	if g.EphemeralDisk == nil {
   491  		g.EphemeralDisk = DefaultEphemeralDisk()
   492  	} else {
   493  		g.EphemeralDisk.Canonicalize()
   494  	}
   495  
   496  	// Merge the update policy from the job
   497  	if ju, tu := job.Update != nil, g.Update != nil; ju && tu {
   498  		// Merge the jobs and task groups definition of the update strategy
   499  		jc := job.Update.Copy()
   500  		jc.Merge(g.Update)
   501  		g.Update = jc
   502  	} else if ju && !job.Update.Empty() {
   503  		// Inherit the jobs as long as it is non-empty.
   504  		jc := job.Update.Copy()
   505  		g.Update = jc
   506  	}
   507  
   508  	if g.Update != nil {
   509  		g.Update.Canonicalize()
   510  	}
   511  
   512  	// Merge the reschedule policy from the job
   513  	if jr, tr := job.Reschedule != nil, g.ReschedulePolicy != nil; jr && tr {
   514  		jobReschedule := job.Reschedule.Copy()
   515  		jobReschedule.Merge(g.ReschedulePolicy)
   516  		g.ReschedulePolicy = jobReschedule
   517  	} else if jr {
   518  		jobReschedule := job.Reschedule.Copy()
   519  		g.ReschedulePolicy = jobReschedule
   520  	}
   521  	// Only use default reschedule policy for non system jobs
   522  	if g.ReschedulePolicy == nil && *job.Type != "system" {
   523  		g.ReschedulePolicy = NewDefaultReschedulePolicy(*job.Type)
   524  	}
   525  	if g.ReschedulePolicy != nil {
   526  		g.ReschedulePolicy.Canonicalize(*job.Type)
   527  	}
   528  	// Merge the migrate strategy from the job
   529  	if jm, tm := job.Migrate != nil, g.Migrate != nil; jm && tm {
   530  		jobMigrate := job.Migrate.Copy()
   531  		jobMigrate.Merge(g.Migrate)
   532  		g.Migrate = jobMigrate
   533  	} else if jm {
   534  		jobMigrate := job.Migrate.Copy()
   535  		g.Migrate = jobMigrate
   536  	}
   537  
   538  	// Merge with default reschedule policy
   539  	if *job.Type == "service" {
   540  		defaultMigrateStrategy := &MigrateStrategy{}
   541  		defaultMigrateStrategy.Canonicalize()
   542  		if g.Migrate != nil {
   543  			defaultMigrateStrategy.Merge(g.Migrate)
   544  		}
   545  		g.Migrate = defaultMigrateStrategy
   546  	}
   547  
   548  	var defaultRestartPolicy *RestartPolicy
   549  	switch *job.Type {
   550  	case "service", "system":
   551  		defaultRestartPolicy = &RestartPolicy{
   552  			Delay:    helper.TimeToPtr(structs.DefaultServiceJobRestartPolicy.Delay),
   553  			Attempts: helper.IntToPtr(structs.DefaultServiceJobRestartPolicy.Attempts),
   554  			Interval: helper.TimeToPtr(structs.DefaultServiceJobRestartPolicy.Interval),
   555  			Mode:     helper.StringToPtr(structs.DefaultServiceJobRestartPolicy.Mode),
   556  		}
   557  	default:
   558  		defaultRestartPolicy = &RestartPolicy{
   559  			Delay:    helper.TimeToPtr(structs.DefaultBatchJobRestartPolicy.Delay),
   560  			Attempts: helper.IntToPtr(structs.DefaultBatchJobRestartPolicy.Attempts),
   561  			Interval: helper.TimeToPtr(structs.DefaultBatchJobRestartPolicy.Interval),
   562  			Mode:     helper.StringToPtr(structs.DefaultBatchJobRestartPolicy.Mode),
   563  		}
   564  	}
   565  
   566  	if g.RestartPolicy != nil {
   567  		defaultRestartPolicy.Merge(g.RestartPolicy)
   568  	}
   569  	g.RestartPolicy = defaultRestartPolicy
   570  }
   571  
   572  // Constrain is used to add a constraint to a task group.
   573  func (g *TaskGroup) Constrain(c *Constraint) *TaskGroup {
   574  	g.Constraints = append(g.Constraints, c)
   575  	return g
   576  }
   577  
   578  // AddMeta is used to add a meta k/v pair to a task group
   579  func (g *TaskGroup) SetMeta(key, val string) *TaskGroup {
   580  	if g.Meta == nil {
   581  		g.Meta = make(map[string]string)
   582  	}
   583  	g.Meta[key] = val
   584  	return g
   585  }
   586  
   587  // AddTask is used to add a new task to a task group.
   588  func (g *TaskGroup) AddTask(t *Task) *TaskGroup {
   589  	g.Tasks = append(g.Tasks, t)
   590  	return g
   591  }
   592  
   593  // AddAffinity is used to add a new affinity to a task group.
   594  func (g *TaskGroup) AddAffinity(a *Affinity) *TaskGroup {
   595  	g.Affinities = append(g.Affinities, a)
   596  	return g
   597  }
   598  
   599  // RequireDisk adds a ephemeral disk to the task group
   600  func (g *TaskGroup) RequireDisk(disk *EphemeralDisk) *TaskGroup {
   601  	g.EphemeralDisk = disk
   602  	return g
   603  }
   604  
   605  // AddSpread is used to add a new spread preference to a task group.
   606  func (g *TaskGroup) AddSpread(s *Spread) *TaskGroup {
   607  	g.Spreads = append(g.Spreads, s)
   608  	return g
   609  }
   610  
   611  // LogConfig provides configuration for log rotation
   612  type LogConfig struct {
   613  	MaxFiles      *int `mapstructure:"max_files"`
   614  	MaxFileSizeMB *int `mapstructure:"max_file_size"`
   615  }
   616  
   617  func DefaultLogConfig() *LogConfig {
   618  	return &LogConfig{
   619  		MaxFiles:      helper.IntToPtr(10),
   620  		MaxFileSizeMB: helper.IntToPtr(10),
   621  	}
   622  }
   623  
   624  func (l *LogConfig) Canonicalize() {
   625  	if l.MaxFiles == nil {
   626  		l.MaxFiles = helper.IntToPtr(10)
   627  	}
   628  	if l.MaxFileSizeMB == nil {
   629  		l.MaxFileSizeMB = helper.IntToPtr(10)
   630  	}
   631  }
   632  
   633  // DispatchPayloadConfig configures how a task gets its input from a job dispatch
   634  type DispatchPayloadConfig struct {
   635  	File string
   636  }
   637  
   638  // Task is a single process in a task group.
   639  type Task struct {
   640  	Name            string
   641  	Driver          string
   642  	User            string
   643  	Config          map[string]interface{}
   644  	Constraints     []*Constraint
   645  	Affinities      []*Affinity
   646  	Env             map[string]string
   647  	Services        []*Service
   648  	Resources       *Resources
   649  	Meta            map[string]string
   650  	KillTimeout     *time.Duration `mapstructure:"kill_timeout"`
   651  	LogConfig       *LogConfig     `mapstructure:"logs"`
   652  	Artifacts       []*TaskArtifact
   653  	Vault           *Vault
   654  	Templates       []*Template
   655  	DispatchPayload *DispatchPayloadConfig
   656  	Leader          bool
   657  	ShutdownDelay   time.Duration `mapstructure:"shutdown_delay"`
   658  	KillSignal      string        `mapstructure:"kill_signal"`
   659  }
   660  
   661  func (t *Task) Canonicalize(tg *TaskGroup, job *Job) {
   662  	if t.Resources == nil {
   663  		t.Resources = &Resources{}
   664  	}
   665  	t.Resources.Canonicalize()
   666  	if t.KillTimeout == nil {
   667  		t.KillTimeout = helper.TimeToPtr(5 * time.Second)
   668  	}
   669  	if t.LogConfig == nil {
   670  		t.LogConfig = DefaultLogConfig()
   671  	} else {
   672  		t.LogConfig.Canonicalize()
   673  	}
   674  	for _, artifact := range t.Artifacts {
   675  		artifact.Canonicalize()
   676  	}
   677  	if t.Vault != nil {
   678  		t.Vault.Canonicalize()
   679  	}
   680  	for _, tmpl := range t.Templates {
   681  		tmpl.Canonicalize()
   682  	}
   683  	for _, s := range t.Services {
   684  		s.Canonicalize(t, tg, job)
   685  	}
   686  }
   687  
   688  // TaskArtifact is used to download artifacts before running a task.
   689  type TaskArtifact struct {
   690  	GetterSource  *string           `mapstructure:"source"`
   691  	GetterOptions map[string]string `mapstructure:"options"`
   692  	GetterMode    *string           `mapstructure:"mode"`
   693  	RelativeDest  *string           `mapstructure:"destination"`
   694  }
   695  
   696  func (a *TaskArtifact) Canonicalize() {
   697  	if a.GetterMode == nil {
   698  		a.GetterMode = helper.StringToPtr("any")
   699  	}
   700  	if a.GetterSource == nil {
   701  		// Shouldn't be possible, but we don't want to panic
   702  		a.GetterSource = helper.StringToPtr("")
   703  	}
   704  	if a.RelativeDest == nil {
   705  		switch *a.GetterMode {
   706  		case "file":
   707  			// File mode should default to local/filename
   708  			dest := *a.GetterSource
   709  			dest = path.Base(dest)
   710  			dest = filepath.Join("local", dest)
   711  			a.RelativeDest = &dest
   712  		default:
   713  			// Default to a directory
   714  			a.RelativeDest = helper.StringToPtr("local/")
   715  		}
   716  	}
   717  }
   718  
   719  type Template struct {
   720  	SourcePath   *string        `mapstructure:"source"`
   721  	DestPath     *string        `mapstructure:"destination"`
   722  	EmbeddedTmpl *string        `mapstructure:"data"`
   723  	ChangeMode   *string        `mapstructure:"change_mode"`
   724  	ChangeSignal *string        `mapstructure:"change_signal"`
   725  	Splay        *time.Duration `mapstructure:"splay"`
   726  	Perms        *string        `mapstructure:"perms"`
   727  	LeftDelim    *string        `mapstructure:"left_delimiter"`
   728  	RightDelim   *string        `mapstructure:"right_delimiter"`
   729  	Envvars      *bool          `mapstructure:"env"`
   730  	VaultGrace   *time.Duration `mapstructure:"vault_grace"`
   731  }
   732  
   733  func (tmpl *Template) Canonicalize() {
   734  	if tmpl.SourcePath == nil {
   735  		tmpl.SourcePath = helper.StringToPtr("")
   736  	}
   737  	if tmpl.DestPath == nil {
   738  		tmpl.DestPath = helper.StringToPtr("")
   739  	}
   740  	if tmpl.EmbeddedTmpl == nil {
   741  		tmpl.EmbeddedTmpl = helper.StringToPtr("")
   742  	}
   743  	if tmpl.ChangeMode == nil {
   744  		tmpl.ChangeMode = helper.StringToPtr("restart")
   745  	}
   746  	if tmpl.ChangeSignal == nil {
   747  		if *tmpl.ChangeMode == "signal" {
   748  			tmpl.ChangeSignal = helper.StringToPtr("SIGHUP")
   749  		} else {
   750  			tmpl.ChangeSignal = helper.StringToPtr("")
   751  		}
   752  	} else {
   753  		sig := *tmpl.ChangeSignal
   754  		tmpl.ChangeSignal = helper.StringToPtr(strings.ToUpper(sig))
   755  	}
   756  	if tmpl.Splay == nil {
   757  		tmpl.Splay = helper.TimeToPtr(5 * time.Second)
   758  	}
   759  	if tmpl.Perms == nil {
   760  		tmpl.Perms = helper.StringToPtr("0644")
   761  	}
   762  	if tmpl.LeftDelim == nil {
   763  		tmpl.LeftDelim = helper.StringToPtr("{{")
   764  	}
   765  	if tmpl.RightDelim == nil {
   766  		tmpl.RightDelim = helper.StringToPtr("}}")
   767  	}
   768  	if tmpl.Envvars == nil {
   769  		tmpl.Envvars = helper.BoolToPtr(false)
   770  	}
   771  	if tmpl.VaultGrace == nil {
   772  		tmpl.VaultGrace = helper.TimeToPtr(15 * time.Second)
   773  	}
   774  }
   775  
   776  type Vault struct {
   777  	Policies     []string
   778  	Env          *bool
   779  	ChangeMode   *string `mapstructure:"change_mode"`
   780  	ChangeSignal *string `mapstructure:"change_signal"`
   781  }
   782  
   783  func (v *Vault) Canonicalize() {
   784  	if v.Env == nil {
   785  		v.Env = helper.BoolToPtr(true)
   786  	}
   787  	if v.ChangeMode == nil {
   788  		v.ChangeMode = helper.StringToPtr("restart")
   789  	}
   790  	if v.ChangeSignal == nil {
   791  		v.ChangeSignal = helper.StringToPtr("SIGHUP")
   792  	}
   793  }
   794  
   795  // NewTask creates and initializes a new Task.
   796  func NewTask(name, driver string) *Task {
   797  	return &Task{
   798  		Name:   name,
   799  		Driver: driver,
   800  	}
   801  }
   802  
   803  // Configure is used to configure a single k/v pair on
   804  // the task.
   805  func (t *Task) SetConfig(key string, val interface{}) *Task {
   806  	if t.Config == nil {
   807  		t.Config = make(map[string]interface{})
   808  	}
   809  	t.Config[key] = val
   810  	return t
   811  }
   812  
   813  // SetMeta is used to add metadata k/v pairs to the task.
   814  func (t *Task) SetMeta(key, val string) *Task {
   815  	if t.Meta == nil {
   816  		t.Meta = make(map[string]string)
   817  	}
   818  	t.Meta[key] = val
   819  	return t
   820  }
   821  
   822  // Require is used to add resource requirements to a task.
   823  func (t *Task) Require(r *Resources) *Task {
   824  	t.Resources = r
   825  	return t
   826  }
   827  
   828  // Constraint adds a new constraints to a single task.
   829  func (t *Task) Constrain(c *Constraint) *Task {
   830  	t.Constraints = append(t.Constraints, c)
   831  	return t
   832  }
   833  
   834  // AddAffinity adds a new affinity to a single task.
   835  func (t *Task) AddAffinity(a *Affinity) *Task {
   836  	t.Affinities = append(t.Affinities, a)
   837  	return t
   838  }
   839  
   840  // SetLogConfig sets a log config to a task
   841  func (t *Task) SetLogConfig(l *LogConfig) *Task {
   842  	t.LogConfig = l
   843  	return t
   844  }
   845  
   846  // TaskState tracks the current state of a task and events that caused state
   847  // transitions.
   848  type TaskState struct {
   849  	State       string
   850  	Failed      bool
   851  	Restarts    uint64
   852  	LastRestart time.Time
   853  	StartedAt   time.Time
   854  	FinishedAt  time.Time
   855  	Events      []*TaskEvent
   856  }
   857  
   858  const (
   859  	TaskSetup                  = "Task Setup"
   860  	TaskSetupFailure           = "Setup Failure"
   861  	TaskDriverFailure          = "Driver Failure"
   862  	TaskDriverMessage          = "Driver"
   863  	TaskReceived               = "Received"
   864  	TaskFailedValidation       = "Failed Validation"
   865  	TaskStarted                = "Started"
   866  	TaskTerminated             = "Terminated"
   867  	TaskKilling                = "Killing"
   868  	TaskKilled                 = "Killed"
   869  	TaskRestarting             = "Restarting"
   870  	TaskNotRestarting          = "Not Restarting"
   871  	TaskDownloadingArtifacts   = "Downloading Artifacts"
   872  	TaskArtifactDownloadFailed = "Failed Artifact Download"
   873  	TaskSiblingFailed          = "Sibling Task Failed"
   874  	TaskSignaling              = "Signaling"
   875  	TaskRestartSignal          = "Restart Signaled"
   876  	TaskLeaderDead             = "Leader Task Dead"
   877  	TaskBuildingTaskDir        = "Building Task Directory"
   878  )
   879  
   880  // TaskEvent is an event that effects the state of a task and contains meta-data
   881  // appropriate to the events type.
   882  type TaskEvent struct {
   883  	Type           string
   884  	Time           int64
   885  	DisplayMessage string
   886  	Details        map[string]string
   887  	// DEPRECATION NOTICE: The following fields are all deprecated. see TaskEvent struct in structs.go for details.
   888  	FailsTask        bool
   889  	RestartReason    string
   890  	SetupError       string
   891  	DriverError      string
   892  	DriverMessage    string
   893  	ExitCode         int
   894  	Signal           int
   895  	Message          string
   896  	KillReason       string
   897  	KillTimeout      time.Duration
   898  	KillError        string
   899  	StartDelay       int64
   900  	DownloadError    string
   901  	ValidationError  string
   902  	DiskLimit        int64
   903  	DiskSize         int64
   904  	FailedSibling    string
   905  	VaultError       string
   906  	TaskSignalReason string
   907  	TaskSignal       string
   908  	GenericSource    string
   909  }