github.com/anuvu/nomad@v0.8.7-atom1/nomad/structs/structs.go (about)

     1  package structs
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/md5"
     6  	"crypto/sha1"
     7  	"crypto/sha256"
     8  	"crypto/sha512"
     9  	"encoding/base32"
    10  	"encoding/hex"
    11  	"errors"
    12  	"fmt"
    13  	"io"
    14  	"net"
    15  	"net/url"
    16  	"os"
    17  	"path/filepath"
    18  	"reflect"
    19  	"regexp"
    20  	"sort"
    21  	"strconv"
    22  	"strings"
    23  	"time"
    24  
    25  	"golang.org/x/crypto/blake2b"
    26  
    27  	"github.com/gorhill/cronexpr"
    28  	"github.com/hashicorp/consul/api"
    29  	multierror "github.com/hashicorp/go-multierror"
    30  	"github.com/hashicorp/go-version"
    31  	"github.com/hashicorp/nomad/acl"
    32  	"github.com/hashicorp/nomad/helper"
    33  	"github.com/hashicorp/nomad/helper/args"
    34  	"github.com/hashicorp/nomad/helper/uuid"
    35  	"github.com/mitchellh/copystructure"
    36  	"github.com/ugorji/go/codec"
    37  
    38  	"math"
    39  
    40  	hcodec "github.com/hashicorp/go-msgpack/codec"
    41  )
    42  
    43  var (
    44  	// validPolicyName is used to validate a policy name
    45  	validPolicyName = regexp.MustCompile("^[a-zA-Z0-9-]{1,128}$")
    46  
    47  	// b32 is a lowercase base32 encoding for use in URL friendly service hashes
    48  	b32 = base32.NewEncoding(strings.ToLower("abcdefghijklmnopqrstuvwxyz234567"))
    49  )
    50  
    51  type MessageType uint8
    52  
    53  const (
    54  	NodeRegisterRequestType MessageType = iota
    55  	NodeDeregisterRequestType
    56  	NodeUpdateStatusRequestType
    57  	NodeUpdateDrainRequestType
    58  	JobRegisterRequestType
    59  	JobDeregisterRequestType
    60  	EvalUpdateRequestType
    61  	EvalDeleteRequestType
    62  	AllocUpdateRequestType
    63  	AllocClientUpdateRequestType
    64  	ReconcileJobSummariesRequestType
    65  	VaultAccessorRegisterRequestType
    66  	VaultAccessorDeregisterRequestType
    67  	ApplyPlanResultsRequestType
    68  	DeploymentStatusUpdateRequestType
    69  	DeploymentPromoteRequestType
    70  	DeploymentAllocHealthRequestType
    71  	DeploymentDeleteRequestType
    72  	JobStabilityRequestType
    73  	ACLPolicyUpsertRequestType
    74  	ACLPolicyDeleteRequestType
    75  	ACLTokenUpsertRequestType
    76  	ACLTokenDeleteRequestType
    77  	ACLTokenBootstrapRequestType
    78  	AutopilotRequestType
    79  	UpsertNodeEventsType
    80  	JobBatchDeregisterRequestType
    81  	AllocUpdateDesiredTransitionRequestType
    82  	NodeUpdateEligibilityRequestType
    83  	BatchNodeUpdateDrainRequestType
    84  )
    85  
    86  const (
    87  	// IgnoreUnknownTypeFlag is set along with a MessageType
    88  	// to indicate that the message type can be safely ignored
    89  	// if it is not recognized. This is for future proofing, so
    90  	// that new commands can be added in a way that won't cause
    91  	// old servers to crash when the FSM attempts to process them.
    92  	IgnoreUnknownTypeFlag MessageType = 128
    93  
    94  	// ApiMajorVersion is returned as part of the Status.Version request.
    95  	// It should be incremented anytime the APIs are changed in a way
    96  	// that would break clients for sane client versioning.
    97  	ApiMajorVersion = 1
    98  
    99  	// ApiMinorVersion is returned as part of the Status.Version request.
   100  	// It should be incremented anytime the APIs are changed to allow
   101  	// for sane client versioning. Minor changes should be compatible
   102  	// within the major version.
   103  	ApiMinorVersion = 1
   104  
   105  	ProtocolVersion = "protocol"
   106  	APIMajorVersion = "api.major"
   107  	APIMinorVersion = "api.minor"
   108  
   109  	GetterModeAny  = "any"
   110  	GetterModeFile = "file"
   111  	GetterModeDir  = "dir"
   112  
   113  	// maxPolicyDescriptionLength limits a policy description length
   114  	maxPolicyDescriptionLength = 256
   115  
   116  	// maxTokenNameLength limits a ACL token name length
   117  	maxTokenNameLength = 256
   118  
   119  	// ACLClientToken and ACLManagementToken are the only types of tokens
   120  	ACLClientToken     = "client"
   121  	ACLManagementToken = "management"
   122  
   123  	// DefaultNamespace is the default namespace.
   124  	DefaultNamespace            = "default"
   125  	DefaultNamespaceDescription = "Default shared namespace"
   126  
   127  	// JitterFraction is a the limit to the amount of jitter we apply
   128  	// to a user specified MaxQueryTime. We divide the specified time by
   129  	// the fraction. So 16 == 6.25% limit of jitter. This jitter is also
   130  	// applied to RPCHoldTimeout.
   131  	JitterFraction = 16
   132  
   133  	// MaxRetainedNodeEvents is the maximum number of node events that will be
   134  	// retained for a single node
   135  	MaxRetainedNodeEvents = 10
   136  )
   137  
   138  // Context defines the scope in which a search for Nomad object operates, and
   139  // is also used to query the matching index value for this context
   140  type Context string
   141  
   142  const (
   143  	Allocs      Context = "allocs"
   144  	Deployments Context = "deployment"
   145  	Evals       Context = "evals"
   146  	Jobs        Context = "jobs"
   147  	Nodes       Context = "nodes"
   148  	Namespaces  Context = "namespaces"
   149  	Quotas      Context = "quotas"
   150  	All         Context = "all"
   151  )
   152  
   153  // NamespacedID is a tuple of an ID and a namespace
   154  type NamespacedID struct {
   155  	ID        string
   156  	Namespace string
   157  }
   158  
   159  // NewNamespacedID returns a new namespaced ID given the ID and namespace
   160  func NewNamespacedID(id, ns string) NamespacedID {
   161  	return NamespacedID{
   162  		ID:        id,
   163  		Namespace: ns,
   164  	}
   165  }
   166  
   167  func (n NamespacedID) String() string {
   168  	return fmt.Sprintf("<ns: %q, id: %q>", n.Namespace, n.ID)
   169  }
   170  
   171  // RPCInfo is used to describe common information about query
   172  type RPCInfo interface {
   173  	RequestRegion() string
   174  	IsRead() bool
   175  	AllowStaleRead() bool
   176  	IsForwarded() bool
   177  	SetForwarded()
   178  }
   179  
   180  // InternalRpcInfo allows adding internal RPC metadata to an RPC. This struct
   181  // should NOT be replicated in the API package as it is internal only.
   182  type InternalRpcInfo struct {
   183  	// Forwarded marks whether the RPC has been forwarded.
   184  	Forwarded bool
   185  }
   186  
   187  // IsForwarded returns whether the RPC is forwarded from another server.
   188  func (i *InternalRpcInfo) IsForwarded() bool {
   189  	return i.Forwarded
   190  }
   191  
   192  // SetForwarded marks that the RPC is being forwarded from another server.
   193  func (i *InternalRpcInfo) SetForwarded() {
   194  	i.Forwarded = true
   195  }
   196  
   197  // QueryOptions is used to specify various flags for read queries
   198  type QueryOptions struct {
   199  	// The target region for this query
   200  	Region string
   201  
   202  	// Namespace is the target namespace for the query.
   203  	Namespace string
   204  
   205  	// If set, wait until query exceeds given index. Must be provided
   206  	// with MaxQueryTime.
   207  	MinQueryIndex uint64
   208  
   209  	// Provided with MinQueryIndex to wait for change.
   210  	MaxQueryTime time.Duration
   211  
   212  	// If set, any follower can service the request. Results
   213  	// may be arbitrarily stale.
   214  	AllowStale bool
   215  
   216  	// If set, used as prefix for resource list searches
   217  	Prefix string
   218  
   219  	// AuthToken is secret portion of the ACL token used for the request
   220  	AuthToken string
   221  
   222  	InternalRpcInfo
   223  }
   224  
   225  func (q QueryOptions) RequestRegion() string {
   226  	return q.Region
   227  }
   228  
   229  func (q QueryOptions) RequestNamespace() string {
   230  	if q.Namespace == "" {
   231  		return DefaultNamespace
   232  	}
   233  	return q.Namespace
   234  }
   235  
   236  // QueryOption only applies to reads, so always true
   237  func (q QueryOptions) IsRead() bool {
   238  	return true
   239  }
   240  
   241  func (q QueryOptions) AllowStaleRead() bool {
   242  	return q.AllowStale
   243  }
   244  
   245  type WriteRequest struct {
   246  	// The target region for this write
   247  	Region string
   248  
   249  	// Namespace is the target namespace for the write.
   250  	Namespace string
   251  
   252  	// AuthToken is secret portion of the ACL token used for the request
   253  	AuthToken string
   254  
   255  	InternalRpcInfo
   256  }
   257  
   258  func (w WriteRequest) RequestRegion() string {
   259  	// The target region for this request
   260  	return w.Region
   261  }
   262  
   263  func (w WriteRequest) RequestNamespace() string {
   264  	if w.Namespace == "" {
   265  		return DefaultNamespace
   266  	}
   267  	return w.Namespace
   268  }
   269  
   270  // WriteRequest only applies to writes, always false
   271  func (w WriteRequest) IsRead() bool {
   272  	return false
   273  }
   274  
   275  func (w WriteRequest) AllowStaleRead() bool {
   276  	return false
   277  }
   278  
   279  // QueryMeta allows a query response to include potentially
   280  // useful metadata about a query
   281  type QueryMeta struct {
   282  	// This is the index associated with the read
   283  	Index uint64
   284  
   285  	// If AllowStale is used, this is time elapsed since
   286  	// last contact between the follower and leader. This
   287  	// can be used to gauge staleness.
   288  	LastContact time.Duration
   289  
   290  	// Used to indicate if there is a known leader node
   291  	KnownLeader bool
   292  }
   293  
   294  // WriteMeta allows a write response to include potentially
   295  // useful metadata about the write
   296  type WriteMeta struct {
   297  	// This is the index associated with the write
   298  	Index uint64
   299  }
   300  
   301  // NodeRegisterRequest is used for Node.Register endpoint
   302  // to register a node as being a schedulable entity.
   303  type NodeRegisterRequest struct {
   304  	Node      *Node
   305  	NodeEvent *NodeEvent
   306  	WriteRequest
   307  }
   308  
   309  // NodeDeregisterRequest is used for Node.Deregister endpoint
   310  // to deregister a node as being a schedulable entity.
   311  type NodeDeregisterRequest struct {
   312  	NodeID string
   313  	WriteRequest
   314  }
   315  
   316  // NodeServerInfo is used to in NodeUpdateResponse to return Nomad server
   317  // information used in RPC server lists.
   318  type NodeServerInfo struct {
   319  	// RPCAdvertiseAddr is the IP endpoint that a Nomad Server wishes to
   320  	// be contacted at for RPCs.
   321  	RPCAdvertiseAddr string
   322  
   323  	// RpcMajorVersion is the major version number the Nomad Server
   324  	// supports
   325  	RPCMajorVersion int32
   326  
   327  	// RpcMinorVersion is the minor version number the Nomad Server
   328  	// supports
   329  	RPCMinorVersion int32
   330  
   331  	// Datacenter is the datacenter that a Nomad server belongs to
   332  	Datacenter string
   333  }
   334  
   335  // NodeUpdateStatusRequest is used for Node.UpdateStatus endpoint
   336  // to update the status of a node.
   337  type NodeUpdateStatusRequest struct {
   338  	NodeID    string
   339  	Status    string
   340  	NodeEvent *NodeEvent
   341  	WriteRequest
   342  }
   343  
   344  // NodeUpdateDrainRequest is used for updating the drain strategy
   345  type NodeUpdateDrainRequest struct {
   346  	NodeID        string
   347  	DrainStrategy *DrainStrategy
   348  
   349  	// COMPAT Remove in version 0.10
   350  	// As part of Nomad 0.8 we have deprecated the drain boolean in favor of a
   351  	// drain strategy but we need to handle the upgrade path where the Raft log
   352  	// contains drain updates with just the drain boolean being manipulated.
   353  	Drain bool
   354  
   355  	// MarkEligible marks the node as eligible if removing the drain strategy.
   356  	MarkEligible bool
   357  
   358  	// NodeEvent is the event added to the node
   359  	NodeEvent *NodeEvent
   360  
   361  	WriteRequest
   362  }
   363  
   364  // BatchNodeUpdateDrainRequest is used for updating the drain strategy for a
   365  // batch of nodes
   366  type BatchNodeUpdateDrainRequest struct {
   367  	// Updates is a mapping of nodes to their updated drain strategy
   368  	Updates map[string]*DrainUpdate
   369  
   370  	// NodeEvents is a mapping of the node to the event to add to the node
   371  	NodeEvents map[string]*NodeEvent
   372  
   373  	WriteRequest
   374  }
   375  
   376  // DrainUpdate is used to update the drain of a node
   377  type DrainUpdate struct {
   378  	// DrainStrategy is the new strategy for the node
   379  	DrainStrategy *DrainStrategy
   380  
   381  	// MarkEligible marks the node as eligible if removing the drain strategy.
   382  	MarkEligible bool
   383  }
   384  
   385  // NodeUpdateEligibilityRequest is used for updating the scheduling	eligibility
   386  type NodeUpdateEligibilityRequest struct {
   387  	NodeID      string
   388  	Eligibility string
   389  
   390  	// NodeEvent is the event added to the node
   391  	NodeEvent *NodeEvent
   392  
   393  	WriteRequest
   394  }
   395  
   396  // NodeEvaluateRequest is used to re-evaluate the node
   397  type NodeEvaluateRequest struct {
   398  	NodeID string
   399  	WriteRequest
   400  }
   401  
   402  // NodeSpecificRequest is used when we just need to specify a target node
   403  type NodeSpecificRequest struct {
   404  	NodeID   string
   405  	SecretID string
   406  	QueryOptions
   407  }
   408  
   409  // SearchResponse is used to return matches and information about whether
   410  // the match list is truncated specific to each type of context.
   411  type SearchResponse struct {
   412  	// Map of context types to ids which match a specified prefix
   413  	Matches map[Context][]string
   414  
   415  	// Truncations indicates whether the matches for a particular context have
   416  	// been truncated
   417  	Truncations map[Context]bool
   418  
   419  	QueryMeta
   420  }
   421  
   422  // SearchRequest is used to parameterize a request, and returns a
   423  // list of matches made up of jobs, allocations, evaluations, and/or nodes,
   424  // along with whether or not the information returned is truncated.
   425  type SearchRequest struct {
   426  	// Prefix is what ids are matched to. I.e, if the given prefix were
   427  	// "a", potential matches might be "abcd" or "aabb"
   428  	Prefix string
   429  
   430  	// Context is the type that can be matched against. A context can be a job,
   431  	// node, evaluation, allocation, or empty (indicated every context should be
   432  	// matched)
   433  	Context Context
   434  
   435  	QueryOptions
   436  }
   437  
   438  // JobRegisterRequest is used for Job.Register endpoint
   439  // to register a job as being a schedulable entity.
   440  type JobRegisterRequest struct {
   441  	Job *Job
   442  
   443  	// If EnforceIndex is set then the job will only be registered if the passed
   444  	// JobModifyIndex matches the current Jobs index. If the index is zero, the
   445  	// register only occurs if the job is new.
   446  	EnforceIndex   bool
   447  	JobModifyIndex uint64
   448  
   449  	// PolicyOverride is set when the user is attempting to override any policies
   450  	PolicyOverride bool
   451  
   452  	WriteRequest
   453  }
   454  
   455  // JobDeregisterRequest is used for Job.Deregister endpoint
   456  // to deregister a job as being a schedulable entity.
   457  type JobDeregisterRequest struct {
   458  	JobID string
   459  
   460  	// Purge controls whether the deregister purges the job from the system or
   461  	// whether the job is just marked as stopped and will be removed by the
   462  	// garbage collector
   463  	Purge bool
   464  
   465  	WriteRequest
   466  }
   467  
   468  // JobBatchDeregisterRequest is used to batch deregister jobs and upsert
   469  // evaluations.
   470  type JobBatchDeregisterRequest struct {
   471  	// Jobs is the set of jobs to deregister
   472  	Jobs map[NamespacedID]*JobDeregisterOptions
   473  
   474  	// Evals is the set of evaluations to create.
   475  	Evals []*Evaluation
   476  
   477  	WriteRequest
   478  }
   479  
   480  // JobDeregisterOptions configures how a job is deregistered.
   481  type JobDeregisterOptions struct {
   482  	// Purge controls whether the deregister purges the job from the system or
   483  	// whether the job is just marked as stopped and will be removed by the
   484  	// garbage collector
   485  	Purge bool
   486  }
   487  
   488  // JobEvaluateRequest is used when we just need to re-evaluate a target job
   489  type JobEvaluateRequest struct {
   490  	JobID       string
   491  	EvalOptions EvalOptions
   492  	WriteRequest
   493  }
   494  
   495  // EvalOptions is used to encapsulate options when forcing a job evaluation
   496  type EvalOptions struct {
   497  	ForceReschedule bool
   498  }
   499  
   500  // JobSpecificRequest is used when we just need to specify a target job
   501  type JobSpecificRequest struct {
   502  	JobID     string
   503  	AllAllocs bool
   504  	QueryOptions
   505  }
   506  
   507  // JobListRequest is used to parameterize a list request
   508  type JobListRequest struct {
   509  	QueryOptions
   510  }
   511  
   512  // JobPlanRequest is used for the Job.Plan endpoint to trigger a dry-run
   513  // evaluation of the Job.
   514  type JobPlanRequest struct {
   515  	Job  *Job
   516  	Diff bool // Toggles an annotated diff
   517  	// PolicyOverride is set when the user is attempting to override any policies
   518  	PolicyOverride bool
   519  	WriteRequest
   520  }
   521  
   522  // JobSummaryRequest is used when we just need to get a specific job summary
   523  type JobSummaryRequest struct {
   524  	JobID string
   525  	QueryOptions
   526  }
   527  
   528  // JobDispatchRequest is used to dispatch a job based on a parameterized job
   529  type JobDispatchRequest struct {
   530  	JobID   string
   531  	Payload []byte
   532  	Meta    map[string]string
   533  	WriteRequest
   534  }
   535  
   536  // JobValidateRequest is used to validate a job
   537  type JobValidateRequest struct {
   538  	Job *Job
   539  	WriteRequest
   540  }
   541  
   542  // JobRevertRequest is used to revert a job to a prior version.
   543  type JobRevertRequest struct {
   544  	// JobID is the ID of the job  being reverted
   545  	JobID string
   546  
   547  	// JobVersion the version to revert to.
   548  	JobVersion uint64
   549  
   550  	// EnforcePriorVersion if set will enforce that the job is at the given
   551  	// version before reverting.
   552  	EnforcePriorVersion *uint64
   553  
   554  	WriteRequest
   555  }
   556  
   557  // JobStabilityRequest is used to marked a job as stable.
   558  type JobStabilityRequest struct {
   559  	// Job to set the stability on
   560  	JobID      string
   561  	JobVersion uint64
   562  
   563  	// Set the stability
   564  	Stable bool
   565  	WriteRequest
   566  }
   567  
   568  // JobStabilityResponse is the response when marking a job as stable.
   569  type JobStabilityResponse struct {
   570  	WriteMeta
   571  }
   572  
   573  // NodeListRequest is used to parameterize a list request
   574  type NodeListRequest struct {
   575  	QueryOptions
   576  }
   577  
   578  // EvalUpdateRequest is used for upserting evaluations.
   579  type EvalUpdateRequest struct {
   580  	Evals     []*Evaluation
   581  	EvalToken string
   582  	WriteRequest
   583  }
   584  
   585  // EvalDeleteRequest is used for deleting an evaluation.
   586  type EvalDeleteRequest struct {
   587  	Evals  []string
   588  	Allocs []string
   589  	WriteRequest
   590  }
   591  
   592  // EvalSpecificRequest is used when we just need to specify a target evaluation
   593  type EvalSpecificRequest struct {
   594  	EvalID string
   595  	QueryOptions
   596  }
   597  
   598  // EvalAckRequest is used to Ack/Nack a specific evaluation
   599  type EvalAckRequest struct {
   600  	EvalID string
   601  	Token  string
   602  	WriteRequest
   603  }
   604  
   605  // EvalDequeueRequest is used when we want to dequeue an evaluation
   606  type EvalDequeueRequest struct {
   607  	Schedulers       []string
   608  	Timeout          time.Duration
   609  	SchedulerVersion uint16
   610  	WriteRequest
   611  }
   612  
   613  // EvalListRequest is used to list the evaluations
   614  type EvalListRequest struct {
   615  	QueryOptions
   616  }
   617  
   618  // PlanRequest is used to submit an allocation plan to the leader
   619  type PlanRequest struct {
   620  	Plan *Plan
   621  	WriteRequest
   622  }
   623  
   624  // ApplyPlanResultsRequest is used by the planner to apply a Raft transaction
   625  // committing the result of a plan.
   626  type ApplyPlanResultsRequest struct {
   627  	// AllocUpdateRequest holds the allocation updates to be made by the
   628  	// scheduler.
   629  	AllocUpdateRequest
   630  
   631  	// Deployment is the deployment created or updated as a result of a
   632  	// scheduling event.
   633  	Deployment *Deployment
   634  
   635  	// DeploymentUpdates is a set of status updates to apply to the given
   636  	// deployments. This allows the scheduler to cancel any unneeded deployment
   637  	// because the job is stopped or the update block is removed.
   638  	DeploymentUpdates []*DeploymentStatusUpdate
   639  
   640  	// EvalID is the eval ID of the plan being applied. The modify index of the
   641  	// evaluation is updated as part of applying the plan to ensure that subsequent
   642  	// scheduling events for the same job will wait for the index that last produced
   643  	// state changes. This is necessary for blocked evaluations since they can be
   644  	// processed many times, potentially making state updates, without the state of
   645  	// the evaluation itself being updated.
   646  	EvalID string
   647  }
   648  
   649  // AllocUpdateRequest is used to submit changes to allocations, either
   650  // to cause evictions or to assign new allocations. Both can be done
   651  // within a single transaction
   652  type AllocUpdateRequest struct {
   653  	// Alloc is the list of new allocations to assign
   654  	Alloc []*Allocation
   655  
   656  	// Evals is the list of new evaluations to create
   657  	// Evals are valid only when used in the Raft RPC
   658  	Evals []*Evaluation
   659  
   660  	// Job is the shared parent job of the allocations.
   661  	// It is pulled out since it is common to reduce payload size.
   662  	Job *Job
   663  
   664  	WriteRequest
   665  }
   666  
   667  // AllocUpdateDesiredTransitionRequest is used to submit changes to allocations
   668  // desired transition state.
   669  type AllocUpdateDesiredTransitionRequest struct {
   670  	// Allocs is the mapping of allocation ids to their desired state
   671  	// transition
   672  	Allocs map[string]*DesiredTransition
   673  
   674  	// Evals is the set of evaluations to create
   675  	Evals []*Evaluation
   676  
   677  	WriteRequest
   678  }
   679  
   680  // AllocListRequest is used to request a list of allocations
   681  type AllocListRequest struct {
   682  	QueryOptions
   683  }
   684  
   685  // AllocSpecificRequest is used to query a specific allocation
   686  type AllocSpecificRequest struct {
   687  	AllocID string
   688  	QueryOptions
   689  }
   690  
   691  // AllocsGetRequest is used to query a set of allocations
   692  type AllocsGetRequest struct {
   693  	AllocIDs []string
   694  	QueryOptions
   695  }
   696  
   697  // PeriodicForceRequest is used to force a specific periodic job.
   698  type PeriodicForceRequest struct {
   699  	JobID string
   700  	WriteRequest
   701  }
   702  
   703  // ServerMembersResponse has the list of servers in a cluster
   704  type ServerMembersResponse struct {
   705  	ServerName   string
   706  	ServerRegion string
   707  	ServerDC     string
   708  	Members      []*ServerMember
   709  }
   710  
   711  // ServerMember holds information about a Nomad server agent in a cluster
   712  type ServerMember struct {
   713  	Name        string
   714  	Addr        net.IP
   715  	Port        uint16
   716  	Tags        map[string]string
   717  	Status      string
   718  	ProtocolMin uint8
   719  	ProtocolMax uint8
   720  	ProtocolCur uint8
   721  	DelegateMin uint8
   722  	DelegateMax uint8
   723  	DelegateCur uint8
   724  }
   725  
   726  // DeriveVaultTokenRequest is used to request wrapped Vault tokens for the
   727  // following tasks in the given allocation
   728  type DeriveVaultTokenRequest struct {
   729  	NodeID   string
   730  	SecretID string
   731  	AllocID  string
   732  	Tasks    []string
   733  	QueryOptions
   734  }
   735  
   736  // VaultAccessorsRequest is used to operate on a set of Vault accessors
   737  type VaultAccessorsRequest struct {
   738  	Accessors []*VaultAccessor
   739  }
   740  
   741  // VaultAccessor is a reference to a created Vault token on behalf of
   742  // an allocation's task.
   743  type VaultAccessor struct {
   744  	AllocID     string
   745  	Task        string
   746  	NodeID      string
   747  	Accessor    string
   748  	CreationTTL int
   749  
   750  	// Raft Indexes
   751  	CreateIndex uint64
   752  }
   753  
   754  // DeriveVaultTokenResponse returns the wrapped tokens for each requested task
   755  type DeriveVaultTokenResponse struct {
   756  	// Tasks is a mapping between the task name and the wrapped token
   757  	Tasks map[string]string
   758  
   759  	// Error stores any error that occurred. Errors are stored here so we can
   760  	// communicate whether it is retriable
   761  	Error *RecoverableError
   762  
   763  	QueryMeta
   764  }
   765  
   766  // GenericRequest is used to request where no
   767  // specific information is needed.
   768  type GenericRequest struct {
   769  	QueryOptions
   770  }
   771  
   772  // DeploymentListRequest is used to list the deployments
   773  type DeploymentListRequest struct {
   774  	QueryOptions
   775  }
   776  
   777  // DeploymentDeleteRequest is used for deleting deployments.
   778  type DeploymentDeleteRequest struct {
   779  	Deployments []string
   780  	WriteRequest
   781  }
   782  
   783  // DeploymentStatusUpdateRequest is used to update the status of a deployment as
   784  // well as optionally creating an evaluation atomically.
   785  type DeploymentStatusUpdateRequest struct {
   786  	// Eval, if set, is used to create an evaluation at the same time as
   787  	// updating the status of a deployment.
   788  	Eval *Evaluation
   789  
   790  	// DeploymentUpdate is a status update to apply to the given
   791  	// deployment.
   792  	DeploymentUpdate *DeploymentStatusUpdate
   793  
   794  	// Job is used to optionally upsert a job. This is used when setting the
   795  	// allocation health results in a deployment failure and the deployment
   796  	// auto-reverts to the latest stable job.
   797  	Job *Job
   798  }
   799  
   800  // DeploymentAllocHealthRequest is used to set the health of a set of
   801  // allocations as part of a deployment.
   802  type DeploymentAllocHealthRequest struct {
   803  	DeploymentID string
   804  
   805  	// Marks these allocations as healthy, allow further allocations
   806  	// to be rolled.
   807  	HealthyAllocationIDs []string
   808  
   809  	// Any unhealthy allocations fail the deployment
   810  	UnhealthyAllocationIDs []string
   811  
   812  	WriteRequest
   813  }
   814  
   815  // ApplyDeploymentAllocHealthRequest is used to apply an alloc health request via Raft
   816  type ApplyDeploymentAllocHealthRequest struct {
   817  	DeploymentAllocHealthRequest
   818  
   819  	// Timestamp is the timestamp to use when setting the allocations health.
   820  	Timestamp time.Time
   821  
   822  	// An optional field to update the status of a deployment
   823  	DeploymentUpdate *DeploymentStatusUpdate
   824  
   825  	// Job is used to optionally upsert a job. This is used when setting the
   826  	// allocation health results in a deployment failure and the deployment
   827  	// auto-reverts to the latest stable job.
   828  	Job *Job
   829  
   830  	// An optional evaluation to create after promoting the canaries
   831  	Eval *Evaluation
   832  }
   833  
   834  // DeploymentPromoteRequest is used to promote task groups in a deployment
   835  type DeploymentPromoteRequest struct {
   836  	DeploymentID string
   837  
   838  	// All is to promote all task groups
   839  	All bool
   840  
   841  	// Groups is used to set the promotion status per task group
   842  	Groups []string
   843  
   844  	WriteRequest
   845  }
   846  
   847  // ApplyDeploymentPromoteRequest is used to apply a promotion request via Raft
   848  type ApplyDeploymentPromoteRequest struct {
   849  	DeploymentPromoteRequest
   850  
   851  	// An optional evaluation to create after promoting the canaries
   852  	Eval *Evaluation
   853  }
   854  
   855  // DeploymentPauseRequest is used to pause a deployment
   856  type DeploymentPauseRequest struct {
   857  	DeploymentID string
   858  
   859  	// Pause sets the pause status
   860  	Pause bool
   861  
   862  	WriteRequest
   863  }
   864  
   865  // DeploymentSpecificRequest is used to make a request specific to a particular
   866  // deployment
   867  type DeploymentSpecificRequest struct {
   868  	DeploymentID string
   869  	QueryOptions
   870  }
   871  
   872  // DeploymentFailRequest is used to fail a particular deployment
   873  type DeploymentFailRequest struct {
   874  	DeploymentID string
   875  	WriteRequest
   876  }
   877  
   878  // SingleDeploymentResponse is used to respond with a single deployment
   879  type SingleDeploymentResponse struct {
   880  	Deployment *Deployment
   881  	QueryMeta
   882  }
   883  
   884  // GenericResponse is used to respond to a request where no
   885  // specific response information is needed.
   886  type GenericResponse struct {
   887  	WriteMeta
   888  }
   889  
   890  // VersionResponse is used for the Status.Version response
   891  type VersionResponse struct {
   892  	Build    string
   893  	Versions map[string]int
   894  	QueryMeta
   895  }
   896  
   897  // JobRegisterResponse is used to respond to a job registration
   898  type JobRegisterResponse struct {
   899  	EvalID          string
   900  	EvalCreateIndex uint64
   901  	JobModifyIndex  uint64
   902  
   903  	// Warnings contains any warnings about the given job. These may include
   904  	// deprecation warnings.
   905  	Warnings string
   906  
   907  	QueryMeta
   908  }
   909  
   910  // JobDeregisterResponse is used to respond to a job deregistration
   911  type JobDeregisterResponse struct {
   912  	EvalID          string
   913  	EvalCreateIndex uint64
   914  	JobModifyIndex  uint64
   915  	QueryMeta
   916  }
   917  
   918  // JobBatchDeregisterResponse is used to respond to a batch job deregistration
   919  type JobBatchDeregisterResponse struct {
   920  	// JobEvals maps the job to its created evaluation
   921  	JobEvals map[NamespacedID]string
   922  	QueryMeta
   923  }
   924  
   925  // JobValidateResponse is the response from validate request
   926  type JobValidateResponse struct {
   927  	// DriverConfigValidated indicates whether the agent validated the driver
   928  	// config
   929  	DriverConfigValidated bool
   930  
   931  	// ValidationErrors is a list of validation errors
   932  	ValidationErrors []string
   933  
   934  	// Error is a string version of any error that may have occurred
   935  	Error string
   936  
   937  	// Warnings contains any warnings about the given job. These may include
   938  	// deprecation warnings.
   939  	Warnings string
   940  }
   941  
   942  // NodeUpdateResponse is used to respond to a node update
   943  type NodeUpdateResponse struct {
   944  	HeartbeatTTL    time.Duration
   945  	EvalIDs         []string
   946  	EvalCreateIndex uint64
   947  	NodeModifyIndex uint64
   948  
   949  	// LeaderRPCAddr is the RPC address of the current Raft Leader.  If
   950  	// empty, the current Nomad Server is in the minority of a partition.
   951  	LeaderRPCAddr string
   952  
   953  	// NumNodes is the number of Nomad nodes attached to this quorum of
   954  	// Nomad Servers at the time of the response.  This value can
   955  	// fluctuate based on the health of the cluster between heartbeats.
   956  	NumNodes int32
   957  
   958  	// Servers is the full list of known Nomad servers in the local
   959  	// region.
   960  	Servers []*NodeServerInfo
   961  
   962  	QueryMeta
   963  }
   964  
   965  // NodeDrainUpdateResponse is used to respond to a node drain update
   966  type NodeDrainUpdateResponse struct {
   967  	NodeModifyIndex uint64
   968  	EvalIDs         []string
   969  	EvalCreateIndex uint64
   970  	WriteMeta
   971  }
   972  
   973  // NodeEligibilityUpdateResponse is used to respond to a node eligibility update
   974  type NodeEligibilityUpdateResponse struct {
   975  	NodeModifyIndex uint64
   976  	EvalIDs         []string
   977  	EvalCreateIndex uint64
   978  	WriteMeta
   979  }
   980  
   981  // NodeAllocsResponse is used to return allocs for a single node
   982  type NodeAllocsResponse struct {
   983  	Allocs []*Allocation
   984  	QueryMeta
   985  }
   986  
   987  // NodeClientAllocsResponse is used to return allocs meta data for a single node
   988  type NodeClientAllocsResponse struct {
   989  	Allocs map[string]uint64
   990  
   991  	// MigrateTokens are used when ACLs are enabled to allow cross node,
   992  	// authenticated access to sticky volumes
   993  	MigrateTokens map[string]string
   994  
   995  	QueryMeta
   996  }
   997  
   998  // SingleNodeResponse is used to return a single node
   999  type SingleNodeResponse struct {
  1000  	Node *Node
  1001  	QueryMeta
  1002  }
  1003  
  1004  // NodeListResponse is used for a list request
  1005  type NodeListResponse struct {
  1006  	Nodes []*NodeListStub
  1007  	QueryMeta
  1008  }
  1009  
  1010  // SingleJobResponse is used to return a single job
  1011  type SingleJobResponse struct {
  1012  	Job *Job
  1013  	QueryMeta
  1014  }
  1015  
  1016  // JobSummaryResponse is used to return a single job summary
  1017  type JobSummaryResponse struct {
  1018  	JobSummary *JobSummary
  1019  	QueryMeta
  1020  }
  1021  
  1022  type JobDispatchResponse struct {
  1023  	DispatchedJobID string
  1024  	EvalID          string
  1025  	EvalCreateIndex uint64
  1026  	JobCreateIndex  uint64
  1027  	WriteMeta
  1028  }
  1029  
  1030  // JobListResponse is used for a list request
  1031  type JobListResponse struct {
  1032  	Jobs []*JobListStub
  1033  	QueryMeta
  1034  }
  1035  
  1036  // JobVersionsRequest is used to get a jobs versions
  1037  type JobVersionsRequest struct {
  1038  	JobID string
  1039  	Diffs bool
  1040  	QueryOptions
  1041  }
  1042  
  1043  // JobVersionsResponse is used for a job get versions request
  1044  type JobVersionsResponse struct {
  1045  	Versions []*Job
  1046  	Diffs    []*JobDiff
  1047  	QueryMeta
  1048  }
  1049  
  1050  // JobPlanResponse is used to respond to a job plan request
  1051  type JobPlanResponse struct {
  1052  	// Annotations stores annotations explaining decisions the scheduler made.
  1053  	Annotations *PlanAnnotations
  1054  
  1055  	// FailedTGAllocs is the placement failures per task group.
  1056  	FailedTGAllocs map[string]*AllocMetric
  1057  
  1058  	// JobModifyIndex is the modification index of the job. The value can be
  1059  	// used when running `nomad run` to ensure that the Job wasn’t modified
  1060  	// since the last plan. If the job is being created, the value is zero.
  1061  	JobModifyIndex uint64
  1062  
  1063  	// CreatedEvals is the set of evaluations created by the scheduler. The
  1064  	// reasons for this can be rolling-updates or blocked evals.
  1065  	CreatedEvals []*Evaluation
  1066  
  1067  	// Diff contains the diff of the job and annotations on whether the change
  1068  	// causes an in-place update or create/destroy
  1069  	Diff *JobDiff
  1070  
  1071  	// NextPeriodicLaunch is the time duration till the job would be launched if
  1072  	// submitted.
  1073  	NextPeriodicLaunch time.Time
  1074  
  1075  	// Warnings contains any warnings about the given job. These may include
  1076  	// deprecation warnings.
  1077  	Warnings string
  1078  
  1079  	WriteMeta
  1080  }
  1081  
  1082  // SingleAllocResponse is used to return a single allocation
  1083  type SingleAllocResponse struct {
  1084  	Alloc *Allocation
  1085  	QueryMeta
  1086  }
  1087  
  1088  // AllocsGetResponse is used to return a set of allocations
  1089  type AllocsGetResponse struct {
  1090  	Allocs []*Allocation
  1091  	QueryMeta
  1092  }
  1093  
  1094  // JobAllocationsResponse is used to return the allocations for a job
  1095  type JobAllocationsResponse struct {
  1096  	Allocations []*AllocListStub
  1097  	QueryMeta
  1098  }
  1099  
  1100  // JobEvaluationsResponse is used to return the evaluations for a job
  1101  type JobEvaluationsResponse struct {
  1102  	Evaluations []*Evaluation
  1103  	QueryMeta
  1104  }
  1105  
  1106  // SingleEvalResponse is used to return a single evaluation
  1107  type SingleEvalResponse struct {
  1108  	Eval *Evaluation
  1109  	QueryMeta
  1110  }
  1111  
  1112  // EvalDequeueResponse is used to return from a dequeue
  1113  type EvalDequeueResponse struct {
  1114  	Eval  *Evaluation
  1115  	Token string
  1116  
  1117  	// WaitIndex is the Raft index the worker should wait until invoking the
  1118  	// scheduler.
  1119  	WaitIndex uint64
  1120  
  1121  	QueryMeta
  1122  }
  1123  
  1124  // GetWaitIndex is used to retrieve the Raft index in which state should be at
  1125  // or beyond before invoking the scheduler.
  1126  func (e *EvalDequeueResponse) GetWaitIndex() uint64 {
  1127  	// Prefer the wait index sent. This will be populated on all responses from
  1128  	// 0.7.0 and above
  1129  	if e.WaitIndex != 0 {
  1130  		return e.WaitIndex
  1131  	} else if e.Eval != nil {
  1132  		return e.Eval.ModifyIndex
  1133  	}
  1134  
  1135  	// This should never happen
  1136  	return 1
  1137  }
  1138  
  1139  // PlanResponse is used to return from a PlanRequest
  1140  type PlanResponse struct {
  1141  	Result *PlanResult
  1142  	WriteMeta
  1143  }
  1144  
  1145  // AllocListResponse is used for a list request
  1146  type AllocListResponse struct {
  1147  	Allocations []*AllocListStub
  1148  	QueryMeta
  1149  }
  1150  
  1151  // DeploymentListResponse is used for a list request
  1152  type DeploymentListResponse struct {
  1153  	Deployments []*Deployment
  1154  	QueryMeta
  1155  }
  1156  
  1157  // EvalListResponse is used for a list request
  1158  type EvalListResponse struct {
  1159  	Evaluations []*Evaluation
  1160  	QueryMeta
  1161  }
  1162  
  1163  // EvalAllocationsResponse is used to return the allocations for an evaluation
  1164  type EvalAllocationsResponse struct {
  1165  	Allocations []*AllocListStub
  1166  	QueryMeta
  1167  }
  1168  
  1169  // PeriodicForceResponse is used to respond to a periodic job force launch
  1170  type PeriodicForceResponse struct {
  1171  	EvalID          string
  1172  	EvalCreateIndex uint64
  1173  	WriteMeta
  1174  }
  1175  
  1176  // DeploymentUpdateResponse is used to respond to a deployment change. The
  1177  // response will include the modify index of the deployment as well as details
  1178  // of any triggered evaluation.
  1179  type DeploymentUpdateResponse struct {
  1180  	EvalID                string
  1181  	EvalCreateIndex       uint64
  1182  	DeploymentModifyIndex uint64
  1183  
  1184  	// RevertedJobVersion is the version the job was reverted to. If unset, the
  1185  	// job wasn't reverted
  1186  	RevertedJobVersion *uint64
  1187  
  1188  	WriteMeta
  1189  }
  1190  
  1191  // NodeConnQueryResponse is used to respond to a query of whether a server has
  1192  // a connection to a specific Node
  1193  type NodeConnQueryResponse struct {
  1194  	// Connected indicates whether a connection to the Client exists
  1195  	Connected bool
  1196  
  1197  	// Established marks the time at which the connection was established
  1198  	Established time.Time
  1199  
  1200  	QueryMeta
  1201  }
  1202  
  1203  // EmitNodeEventsRequest is a request to update the node events source
  1204  // with a new client-side event
  1205  type EmitNodeEventsRequest struct {
  1206  	// NodeEvents are a map where the key is a node id, and value is a list of
  1207  	// events for that node
  1208  	NodeEvents map[string][]*NodeEvent
  1209  
  1210  	WriteRequest
  1211  }
  1212  
  1213  // EmitNodeEventsResponse is a response to the client about the status of
  1214  // the node event source update.
  1215  type EmitNodeEventsResponse struct {
  1216  	Index uint64
  1217  	WriteMeta
  1218  }
  1219  
  1220  const (
  1221  	NodeEventSubsystemDrain     = "Drain"
  1222  	NodeEventSubsystemDriver    = "Driver"
  1223  	NodeEventSubsystemHeartbeat = "Heartbeat"
  1224  	NodeEventSubsystemCluster   = "Cluster"
  1225  )
  1226  
  1227  // NodeEvent is a single unit representing a node’s state change
  1228  type NodeEvent struct {
  1229  	Message     string
  1230  	Subsystem   string
  1231  	Details     map[string]string
  1232  	Timestamp   time.Time
  1233  	CreateIndex uint64
  1234  }
  1235  
  1236  func (ne *NodeEvent) String() string {
  1237  	var details []string
  1238  	for k, v := range ne.Details {
  1239  		details = append(details, fmt.Sprintf("%s: %s", k, v))
  1240  	}
  1241  
  1242  	return fmt.Sprintf("Message: %s, Subsystem: %s, Details: %s, Timestamp: %s", ne.Message, ne.Subsystem, strings.Join(details, ","), ne.Timestamp.String())
  1243  }
  1244  
  1245  func (ne *NodeEvent) Copy() *NodeEvent {
  1246  	c := new(NodeEvent)
  1247  	*c = *ne
  1248  	c.Details = helper.CopyMapStringString(ne.Details)
  1249  	return c
  1250  }
  1251  
  1252  // NewNodeEvent generates a new node event storing the current time as the
  1253  // timestamp
  1254  func NewNodeEvent() *NodeEvent {
  1255  	return &NodeEvent{Timestamp: time.Now()}
  1256  }
  1257  
  1258  // SetMessage is used to set the message on the node event
  1259  func (ne *NodeEvent) SetMessage(msg string) *NodeEvent {
  1260  	ne.Message = msg
  1261  	return ne
  1262  }
  1263  
  1264  // SetSubsystem is used to set the subsystem on the node event
  1265  func (ne *NodeEvent) SetSubsystem(sys string) *NodeEvent {
  1266  	ne.Subsystem = sys
  1267  	return ne
  1268  }
  1269  
  1270  // SetTimestamp is used to set the timestamp on the node event
  1271  func (ne *NodeEvent) SetTimestamp(ts time.Time) *NodeEvent {
  1272  	ne.Timestamp = ts
  1273  	return ne
  1274  }
  1275  
  1276  // AddDetail is used to add a detail to the node event
  1277  func (ne *NodeEvent) AddDetail(k, v string) *NodeEvent {
  1278  	if ne.Details == nil {
  1279  		ne.Details = make(map[string]string, 1)
  1280  	}
  1281  	ne.Details[k] = v
  1282  	return ne
  1283  }
  1284  
  1285  const (
  1286  	NodeStatusInit  = "initializing"
  1287  	NodeStatusReady = "ready"
  1288  	NodeStatusDown  = "down"
  1289  )
  1290  
  1291  // ShouldDrainNode checks if a given node status should trigger an
  1292  // evaluation. Some states don't require any further action.
  1293  func ShouldDrainNode(status string) bool {
  1294  	switch status {
  1295  	case NodeStatusInit, NodeStatusReady:
  1296  		return false
  1297  	case NodeStatusDown:
  1298  		return true
  1299  	default:
  1300  		panic(fmt.Sprintf("unhandled node status %s", status))
  1301  	}
  1302  }
  1303  
  1304  // ValidNodeStatus is used to check if a node status is valid
  1305  func ValidNodeStatus(status string) bool {
  1306  	switch status {
  1307  	case NodeStatusInit, NodeStatusReady, NodeStatusDown:
  1308  		return true
  1309  	default:
  1310  		return false
  1311  	}
  1312  }
  1313  
  1314  const (
  1315  	// NodeSchedulingEligible and Ineligible marks the node as eligible or not,
  1316  	// respectively, for receiving allocations. This is orthoginal to the node
  1317  	// status being ready.
  1318  	NodeSchedulingEligible   = "eligible"
  1319  	NodeSchedulingIneligible = "ineligible"
  1320  )
  1321  
  1322  // DrainSpec describes a Node's desired drain behavior.
  1323  type DrainSpec struct {
  1324  	// Deadline is the duration after StartTime when the remaining
  1325  	// allocations on a draining Node should be told to stop.
  1326  	Deadline time.Duration
  1327  
  1328  	// IgnoreSystemJobs allows systems jobs to remain on the node even though it
  1329  	// has been marked for draining.
  1330  	IgnoreSystemJobs bool
  1331  }
  1332  
  1333  // DrainStrategy describes a Node's drain behavior.
  1334  type DrainStrategy struct {
  1335  	// DrainSpec is the user declared drain specification
  1336  	DrainSpec
  1337  
  1338  	// ForceDeadline is the deadline time for the drain after which drains will
  1339  	// be forced
  1340  	ForceDeadline time.Time
  1341  }
  1342  
  1343  func (d *DrainStrategy) Copy() *DrainStrategy {
  1344  	if d == nil {
  1345  		return nil
  1346  	}
  1347  
  1348  	nd := new(DrainStrategy)
  1349  	*nd = *d
  1350  	return nd
  1351  }
  1352  
  1353  // DeadlineTime returns a boolean whether the drain strategy allows an infinite
  1354  // duration or otherwise the deadline time. The force drain is captured by the
  1355  // deadline time being in the past.
  1356  func (d *DrainStrategy) DeadlineTime() (infinite bool, deadline time.Time) {
  1357  	// Treat the nil case as a force drain so during an upgrade where a node may
  1358  	// not have a drain strategy but has Drain set to true, it is treated as a
  1359  	// force to mimick old behavior.
  1360  	if d == nil {
  1361  		return false, time.Time{}
  1362  	}
  1363  
  1364  	ns := d.Deadline.Nanoseconds()
  1365  	switch {
  1366  	case ns < 0: // Force
  1367  		return false, time.Time{}
  1368  	case ns == 0: // Infinite
  1369  		return true, time.Time{}
  1370  	default:
  1371  		return false, d.ForceDeadline
  1372  	}
  1373  }
  1374  
  1375  func (d *DrainStrategy) Equal(o *DrainStrategy) bool {
  1376  	if d == nil && o == nil {
  1377  		return true
  1378  	} else if o != nil && d == nil {
  1379  		return false
  1380  	} else if d != nil && o == nil {
  1381  		return false
  1382  	}
  1383  
  1384  	// Compare values
  1385  	if d.ForceDeadline != o.ForceDeadline {
  1386  		return false
  1387  	} else if d.Deadline != o.Deadline {
  1388  		return false
  1389  	} else if d.IgnoreSystemJobs != o.IgnoreSystemJobs {
  1390  		return false
  1391  	}
  1392  
  1393  	return true
  1394  }
  1395  
  1396  // Node is a representation of a schedulable client node
  1397  type Node struct {
  1398  	// ID is a unique identifier for the node. It can be constructed
  1399  	// by doing a concatenation of the Name and Datacenter as a simple
  1400  	// approach. Alternatively a UUID may be used.
  1401  	ID string
  1402  
  1403  	// SecretID is an ID that is only known by the Node and the set of Servers.
  1404  	// It is not accessible via the API and is used to authenticate nodes
  1405  	// conducting privileged activities.
  1406  	SecretID string
  1407  
  1408  	// Datacenter for this node
  1409  	Datacenter string
  1410  
  1411  	// Node name
  1412  	Name string
  1413  
  1414  	// HTTPAddr is the address on which the Nomad client is listening for http
  1415  	// requests
  1416  	HTTPAddr string
  1417  
  1418  	// TLSEnabled indicates if the Agent has TLS enabled for the HTTP API
  1419  	TLSEnabled bool
  1420  
  1421  	// Attributes is an arbitrary set of key/value
  1422  	// data that can be used for constraints. Examples
  1423  	// include "kernel.name=linux", "arch=386", "driver.docker=1",
  1424  	// "docker.runtime=1.8.3"
  1425  	Attributes map[string]string
  1426  
  1427  	// Resources is the available resources on the client.
  1428  	// For example 'cpu=2' 'memory=2048'
  1429  	Resources *Resources
  1430  
  1431  	// Reserved is the set of resources that are reserved,
  1432  	// and should be subtracted from the total resources for
  1433  	// the purposes of scheduling. This may be provide certain
  1434  	// high-watermark tolerances or because of external schedulers
  1435  	// consuming resources.
  1436  	Reserved *Resources
  1437  
  1438  	// Links are used to 'link' this client to external
  1439  	// systems. For example 'consul=foo.dc1' 'aws=i-83212'
  1440  	// 'ami=ami-123'
  1441  	Links map[string]string
  1442  
  1443  	// Meta is used to associate arbitrary metadata with this
  1444  	// client. This is opaque to Nomad.
  1445  	Meta map[string]string
  1446  
  1447  	// NodeClass is an opaque identifier used to group nodes
  1448  	// together for the purpose of determining scheduling pressure.
  1449  	NodeClass string
  1450  
  1451  	// ComputedClass is a unique id that identifies nodes with a common set of
  1452  	// attributes and capabilities.
  1453  	ComputedClass string
  1454  
  1455  	// COMPAT: Remove in Nomad 0.9
  1456  	// Drain is controlled by the servers, and not the client.
  1457  	// If true, no jobs will be scheduled to this node, and existing
  1458  	// allocations will be drained. Superceded by DrainStrategy in Nomad
  1459  	// 0.8 but kept for backward compat.
  1460  	Drain bool
  1461  
  1462  	// DrainStrategy determines the node's draining behavior. Will be nil
  1463  	// when Drain=false.
  1464  	DrainStrategy *DrainStrategy
  1465  
  1466  	// SchedulingEligibility determines whether this node will receive new
  1467  	// placements.
  1468  	SchedulingEligibility string
  1469  
  1470  	// Status of this node
  1471  	Status string
  1472  
  1473  	// StatusDescription is meant to provide more human useful information
  1474  	StatusDescription string
  1475  
  1476  	// StatusUpdatedAt is the time stamp at which the state of the node was
  1477  	// updated
  1478  	StatusUpdatedAt int64
  1479  
  1480  	// Events is the most recent set of events generated for the node,
  1481  	// retaining only MaxRetainedNodeEvents number at a time
  1482  	Events []*NodeEvent
  1483  
  1484  	// Drivers is a map of driver names to current driver information
  1485  	Drivers map[string]*DriverInfo
  1486  
  1487  	// Raft Indexes
  1488  	CreateIndex uint64
  1489  	ModifyIndex uint64
  1490  }
  1491  
  1492  // Ready returns true if the node is ready for running allocations
  1493  func (n *Node) Ready() bool {
  1494  	// Drain is checked directly to support pre-0.8 Node data
  1495  	return n.Status == NodeStatusReady && !n.Drain && n.SchedulingEligibility == NodeSchedulingEligible
  1496  }
  1497  
  1498  func (n *Node) Canonicalize() {
  1499  	if n == nil {
  1500  		return
  1501  	}
  1502  
  1503  	// COMPAT Remove in 0.10
  1504  	// In v0.8.0 we introduced scheduling eligibility, so we need to set it for
  1505  	// upgrading nodes
  1506  	if n.SchedulingEligibility == "" {
  1507  		if n.Drain {
  1508  			n.SchedulingEligibility = NodeSchedulingIneligible
  1509  		} else {
  1510  			n.SchedulingEligibility = NodeSchedulingEligible
  1511  		}
  1512  	}
  1513  }
  1514  
  1515  func (n *Node) Copy() *Node {
  1516  	if n == nil {
  1517  		return nil
  1518  	}
  1519  	nn := new(Node)
  1520  	*nn = *n
  1521  	nn.Attributes = helper.CopyMapStringString(nn.Attributes)
  1522  	nn.Resources = nn.Resources.Copy()
  1523  	nn.Reserved = nn.Reserved.Copy()
  1524  	nn.Links = helper.CopyMapStringString(nn.Links)
  1525  	nn.Meta = helper.CopyMapStringString(nn.Meta)
  1526  	nn.Events = copyNodeEvents(n.Events)
  1527  	nn.DrainStrategy = nn.DrainStrategy.Copy()
  1528  	nn.Drivers = copyNodeDrivers(n.Drivers)
  1529  	return nn
  1530  }
  1531  
  1532  // copyNodeEvents is a helper to copy a list of NodeEvent's
  1533  func copyNodeEvents(events []*NodeEvent) []*NodeEvent {
  1534  	l := len(events)
  1535  	if l == 0 {
  1536  		return nil
  1537  	}
  1538  
  1539  	c := make([]*NodeEvent, l)
  1540  	for i, event := range events {
  1541  		c[i] = event.Copy()
  1542  	}
  1543  	return c
  1544  }
  1545  
  1546  // copyNodeDrivers is a helper to copy a map of DriverInfo
  1547  func copyNodeDrivers(drivers map[string]*DriverInfo) map[string]*DriverInfo {
  1548  	l := len(drivers)
  1549  	if l == 0 {
  1550  		return nil
  1551  	}
  1552  
  1553  	c := make(map[string]*DriverInfo, l)
  1554  	for driver, info := range drivers {
  1555  		c[driver] = info.Copy()
  1556  	}
  1557  	return c
  1558  }
  1559  
  1560  // TerminalStatus returns if the current status is terminal and
  1561  // will no longer transition.
  1562  func (n *Node) TerminalStatus() bool {
  1563  	switch n.Status {
  1564  	case NodeStatusDown:
  1565  		return true
  1566  	default:
  1567  		return false
  1568  	}
  1569  }
  1570  
  1571  // Stub returns a summarized version of the node
  1572  func (n *Node) Stub() *NodeListStub {
  1573  
  1574  	addr, _, _ := net.SplitHostPort(n.HTTPAddr)
  1575  
  1576  	return &NodeListStub{
  1577  		Address:    addr,
  1578  		ID:         n.ID,
  1579  		Datacenter: n.Datacenter,
  1580  		Name:       n.Name,
  1581  		NodeClass:  n.NodeClass,
  1582  		Version:    n.Attributes["nomad.version"],
  1583  		Drain:      n.Drain,
  1584  		SchedulingEligibility: n.SchedulingEligibility,
  1585  		Status:                n.Status,
  1586  		StatusDescription:     n.StatusDescription,
  1587  		Drivers:               n.Drivers,
  1588  		CreateIndex:           n.CreateIndex,
  1589  		ModifyIndex:           n.ModifyIndex,
  1590  	}
  1591  }
  1592  
  1593  // NodeListStub is used to return a subset of job information
  1594  // for the job list
  1595  type NodeListStub struct {
  1596  	Address               string
  1597  	ID                    string
  1598  	Datacenter            string
  1599  	Name                  string
  1600  	NodeClass             string
  1601  	Version               string
  1602  	Drain                 bool
  1603  	SchedulingEligibility string
  1604  	Status                string
  1605  	StatusDescription     string
  1606  	Drivers               map[string]*DriverInfo
  1607  	CreateIndex           uint64
  1608  	ModifyIndex           uint64
  1609  }
  1610  
  1611  // Networks defined for a task on the Resources struct.
  1612  type Networks []*NetworkResource
  1613  
  1614  // Port assignment and IP for the given label or empty values.
  1615  func (ns Networks) Port(label string) (string, int) {
  1616  	for _, n := range ns {
  1617  		for _, p := range n.ReservedPorts {
  1618  			if p.Label == label {
  1619  				return n.IP, p.Value
  1620  			}
  1621  		}
  1622  		for _, p := range n.DynamicPorts {
  1623  			if p.Label == label {
  1624  				return n.IP, p.Value
  1625  			}
  1626  		}
  1627  	}
  1628  	return "", 0
  1629  }
  1630  
  1631  // Resources is used to define the resources available
  1632  // on a client
  1633  type Resources struct {
  1634  	CPU      int
  1635  	MemoryMB int
  1636  	DiskMB   int
  1637  	IOPS     int
  1638  	Networks Networks
  1639  }
  1640  
  1641  const (
  1642  	BytesInMegabyte = 1024 * 1024
  1643  )
  1644  
  1645  // DefaultResources is a small resources object that contains the
  1646  // default resources requests that we will provide to an object.
  1647  // ---  THIS FUNCTION IS REPLICATED IN api/resources.go and should
  1648  // be kept in sync.
  1649  func DefaultResources() *Resources {
  1650  	return &Resources{
  1651  		CPU:      100,
  1652  		MemoryMB: 300,
  1653  		IOPS:     0,
  1654  	}
  1655  }
  1656  
  1657  // MinResources is a small resources object that contains the
  1658  // absolute minimum resources that we will provide to an object.
  1659  // This should not be confused with the defaults which are
  1660  // provided in Canonicalize() ---  THIS FUNCTION IS REPLICATED IN
  1661  // api/resources.go and should be kept in sync.
  1662  func MinResources() *Resources {
  1663  	return &Resources{
  1664  		CPU:      20,
  1665  		MemoryMB: 10,
  1666  		IOPS:     0,
  1667  	}
  1668  }
  1669  
  1670  // DiskInBytes returns the amount of disk resources in bytes.
  1671  func (r *Resources) DiskInBytes() int64 {
  1672  	return int64(r.DiskMB * BytesInMegabyte)
  1673  }
  1674  
  1675  // Merge merges this resource with another resource.
  1676  func (r *Resources) Merge(other *Resources) {
  1677  	if other.CPU != 0 {
  1678  		r.CPU = other.CPU
  1679  	}
  1680  	if other.MemoryMB != 0 {
  1681  		r.MemoryMB = other.MemoryMB
  1682  	}
  1683  	if other.DiskMB != 0 {
  1684  		r.DiskMB = other.DiskMB
  1685  	}
  1686  	if other.IOPS != 0 {
  1687  		r.IOPS = other.IOPS
  1688  	}
  1689  	if len(other.Networks) != 0 {
  1690  		r.Networks = other.Networks
  1691  	}
  1692  }
  1693  
  1694  func (r *Resources) Canonicalize() {
  1695  	// Ensure that an empty and nil slices are treated the same to avoid scheduling
  1696  	// problems since we use reflect DeepEquals.
  1697  	if len(r.Networks) == 0 {
  1698  		r.Networks = nil
  1699  	}
  1700  
  1701  	for _, n := range r.Networks {
  1702  		n.Canonicalize()
  1703  	}
  1704  }
  1705  
  1706  // MeetsMinResources returns an error if the resources specified are less than
  1707  // the minimum allowed.
  1708  // This is based on the minimums defined in the Resources type
  1709  func (r *Resources) MeetsMinResources() error {
  1710  	var mErr multierror.Error
  1711  	minResources := MinResources()
  1712  	if r.CPU < minResources.CPU {
  1713  		mErr.Errors = append(mErr.Errors, fmt.Errorf("minimum CPU value is %d; got %d", minResources.CPU, r.CPU))
  1714  	}
  1715  	if r.MemoryMB < minResources.MemoryMB {
  1716  		mErr.Errors = append(mErr.Errors, fmt.Errorf("minimum MemoryMB value is %d; got %d", minResources.MemoryMB, r.MemoryMB))
  1717  	}
  1718  	if r.IOPS < minResources.IOPS {
  1719  		mErr.Errors = append(mErr.Errors, fmt.Errorf("minimum IOPS value is %d; got %d", minResources.IOPS, r.IOPS))
  1720  	}
  1721  	for i, n := range r.Networks {
  1722  		if err := n.MeetsMinResources(); err != nil {
  1723  			mErr.Errors = append(mErr.Errors, fmt.Errorf("network resource at index %d failed: %v", i, err))
  1724  		}
  1725  	}
  1726  
  1727  	return mErr.ErrorOrNil()
  1728  }
  1729  
  1730  // Copy returns a deep copy of the resources
  1731  func (r *Resources) Copy() *Resources {
  1732  	if r == nil {
  1733  		return nil
  1734  	}
  1735  	newR := new(Resources)
  1736  	*newR = *r
  1737  	if r.Networks != nil {
  1738  		n := len(r.Networks)
  1739  		newR.Networks = make([]*NetworkResource, n)
  1740  		for i := 0; i < n; i++ {
  1741  			newR.Networks[i] = r.Networks[i].Copy()
  1742  		}
  1743  	}
  1744  	return newR
  1745  }
  1746  
  1747  // NetIndex finds the matching net index using device name
  1748  func (r *Resources) NetIndex(n *NetworkResource) int {
  1749  	for idx, net := range r.Networks {
  1750  		if net.Device == n.Device {
  1751  			return idx
  1752  		}
  1753  	}
  1754  	return -1
  1755  }
  1756  
  1757  // Superset checks if one set of resources is a superset
  1758  // of another. This ignores network resources, and the NetworkIndex
  1759  // should be used for that.
  1760  func (r *Resources) Superset(other *Resources) (bool, string) {
  1761  	if r.CPU < other.CPU {
  1762  		return false, "cpu"
  1763  	}
  1764  	if r.MemoryMB < other.MemoryMB {
  1765  		return false, "memory"
  1766  	}
  1767  	if r.DiskMB < other.DiskMB {
  1768  		return false, "disk"
  1769  	}
  1770  	if r.IOPS < other.IOPS {
  1771  		return false, "iops"
  1772  	}
  1773  	return true, ""
  1774  }
  1775  
  1776  // Add adds the resources of the delta to this, potentially
  1777  // returning an error if not possible.
  1778  func (r *Resources) Add(delta *Resources) error {
  1779  	if delta == nil {
  1780  		return nil
  1781  	}
  1782  	r.CPU += delta.CPU
  1783  	r.MemoryMB += delta.MemoryMB
  1784  	r.DiskMB += delta.DiskMB
  1785  	r.IOPS += delta.IOPS
  1786  
  1787  	for _, n := range delta.Networks {
  1788  		// Find the matching interface by IP or CIDR
  1789  		idx := r.NetIndex(n)
  1790  		if idx == -1 {
  1791  			r.Networks = append(r.Networks, n.Copy())
  1792  		} else {
  1793  			r.Networks[idx].Add(n)
  1794  		}
  1795  	}
  1796  	return nil
  1797  }
  1798  
  1799  func (r *Resources) GoString() string {
  1800  	return fmt.Sprintf("*%#v", *r)
  1801  }
  1802  
  1803  type Port struct {
  1804  	Label string
  1805  	Value int
  1806  }
  1807  
  1808  // NetworkResource is used to represent available network
  1809  // resources
  1810  type NetworkResource struct {
  1811  	Device        string // Name of the device
  1812  	CIDR          string // CIDR block of addresses
  1813  	IP            string // Host IP address
  1814  	MBits         int    // Throughput
  1815  	ReservedPorts []Port // Host Reserved ports
  1816  	DynamicPorts  []Port // Host Dynamically assigned ports
  1817  }
  1818  
  1819  func (nr *NetworkResource) Equals(other *NetworkResource) bool {
  1820  	if nr.Device != other.Device {
  1821  		return false
  1822  	}
  1823  
  1824  	if nr.CIDR != other.CIDR {
  1825  		return false
  1826  	}
  1827  
  1828  	if nr.IP != other.IP {
  1829  		return false
  1830  	}
  1831  
  1832  	if nr.MBits != other.MBits {
  1833  		return false
  1834  	}
  1835  
  1836  	if len(nr.ReservedPorts) != len(other.ReservedPorts) {
  1837  		return false
  1838  	}
  1839  
  1840  	for i, port := range nr.ReservedPorts {
  1841  		if len(other.ReservedPorts) <= i {
  1842  			return false
  1843  		}
  1844  		if port != other.ReservedPorts[i] {
  1845  			return false
  1846  		}
  1847  	}
  1848  
  1849  	if len(nr.DynamicPorts) != len(other.DynamicPorts) {
  1850  		return false
  1851  	}
  1852  	for i, port := range nr.DynamicPorts {
  1853  		if len(other.DynamicPorts) <= i {
  1854  			return false
  1855  		}
  1856  		if port != other.DynamicPorts[i] {
  1857  			return false
  1858  		}
  1859  	}
  1860  	return true
  1861  }
  1862  
  1863  func (n *NetworkResource) Canonicalize() {
  1864  	// Ensure that an empty and nil slices are treated the same to avoid scheduling
  1865  	// problems since we use reflect DeepEquals.
  1866  	if len(n.ReservedPorts) == 0 {
  1867  		n.ReservedPorts = nil
  1868  	}
  1869  	if len(n.DynamicPorts) == 0 {
  1870  		n.DynamicPorts = nil
  1871  	}
  1872  }
  1873  
  1874  // MeetsMinResources returns an error if the resources specified are less than
  1875  // the minimum allowed.
  1876  func (n *NetworkResource) MeetsMinResources() error {
  1877  	var mErr multierror.Error
  1878  	if n.MBits < 1 {
  1879  		mErr.Errors = append(mErr.Errors, fmt.Errorf("minimum MBits value is 1; got %d", n.MBits))
  1880  	}
  1881  	return mErr.ErrorOrNil()
  1882  }
  1883  
  1884  // Copy returns a deep copy of the network resource
  1885  func (n *NetworkResource) Copy() *NetworkResource {
  1886  	if n == nil {
  1887  		return nil
  1888  	}
  1889  	newR := new(NetworkResource)
  1890  	*newR = *n
  1891  	if n.ReservedPorts != nil {
  1892  		newR.ReservedPorts = make([]Port, len(n.ReservedPorts))
  1893  		copy(newR.ReservedPorts, n.ReservedPorts)
  1894  	}
  1895  	if n.DynamicPorts != nil {
  1896  		newR.DynamicPorts = make([]Port, len(n.DynamicPorts))
  1897  		copy(newR.DynamicPorts, n.DynamicPorts)
  1898  	}
  1899  	return newR
  1900  }
  1901  
  1902  // Add adds the resources of the delta to this, potentially
  1903  // returning an error if not possible.
  1904  func (n *NetworkResource) Add(delta *NetworkResource) {
  1905  	if len(delta.ReservedPorts) > 0 {
  1906  		n.ReservedPorts = append(n.ReservedPorts, delta.ReservedPorts...)
  1907  	}
  1908  	n.MBits += delta.MBits
  1909  	n.DynamicPorts = append(n.DynamicPorts, delta.DynamicPorts...)
  1910  }
  1911  
  1912  func (n *NetworkResource) GoString() string {
  1913  	return fmt.Sprintf("*%#v", *n)
  1914  }
  1915  
  1916  // PortLabels returns a map of port labels to their assigned host ports.
  1917  func (n *NetworkResource) PortLabels() map[string]int {
  1918  	num := len(n.ReservedPorts) + len(n.DynamicPorts)
  1919  	labelValues := make(map[string]int, num)
  1920  	for _, port := range n.ReservedPorts {
  1921  		labelValues[port.Label] = port.Value
  1922  	}
  1923  	for _, port := range n.DynamicPorts {
  1924  		labelValues[port.Label] = port.Value
  1925  	}
  1926  	return labelValues
  1927  }
  1928  
  1929  const (
  1930  	// JobTypeNomad is reserved for internal system tasks and is
  1931  	// always handled by the CoreScheduler.
  1932  	JobTypeCore    = "_core"
  1933  	JobTypeService = "service"
  1934  	JobTypeBatch   = "batch"
  1935  	JobTypeSystem  = "system"
  1936  )
  1937  
  1938  const (
  1939  	JobStatusPending = "pending" // Pending means the job is waiting on scheduling
  1940  	JobStatusRunning = "running" // Running means the job has non-terminal allocations
  1941  	JobStatusDead    = "dead"    // Dead means all evaluation's and allocations are terminal
  1942  )
  1943  
  1944  const (
  1945  	// JobMinPriority is the minimum allowed priority
  1946  	JobMinPriority = 1
  1947  
  1948  	// JobDefaultPriority is the default priority if not
  1949  	// not specified.
  1950  	JobDefaultPriority = 50
  1951  
  1952  	// JobMaxPriority is the maximum allowed priority
  1953  	JobMaxPriority = 100
  1954  
  1955  	// Ensure CoreJobPriority is higher than any user
  1956  	// specified job so that it gets priority. This is important
  1957  	// for the system to remain healthy.
  1958  	CoreJobPriority = JobMaxPriority * 2
  1959  
  1960  	// JobTrackedVersions is the number of historic job versions that are
  1961  	// kept.
  1962  	JobTrackedVersions = 6
  1963  )
  1964  
  1965  // Job is the scope of a scheduling request to Nomad. It is the largest
  1966  // scoped object, and is a named collection of task groups. Each task group
  1967  // is further composed of tasks. A task group (TG) is the unit of scheduling
  1968  // however.
  1969  type Job struct {
  1970  	// Stop marks whether the user has stopped the job. A stopped job will
  1971  	// have all created allocations stopped and acts as a way to stop a job
  1972  	// without purging it from the system. This allows existing allocs to be
  1973  	// queried and the job to be inspected as it is being killed.
  1974  	Stop bool
  1975  
  1976  	// Region is the Nomad region that handles scheduling this job
  1977  	Region string
  1978  
  1979  	// Namespace is the namespace the job is submitted into.
  1980  	Namespace string
  1981  
  1982  	// ID is a unique identifier for the job per region. It can be
  1983  	// specified hierarchically like LineOfBiz/OrgName/Team/Project
  1984  	ID string
  1985  
  1986  	// ParentID is the unique identifier of the job that spawned this job.
  1987  	ParentID string
  1988  
  1989  	// Name is the logical name of the job used to refer to it. This is unique
  1990  	// per region, but not unique globally.
  1991  	Name string
  1992  
  1993  	// Type is used to control various behaviors about the job. Most jobs
  1994  	// are service jobs, meaning they are expected to be long lived.
  1995  	// Some jobs are batch oriented meaning they run and then terminate.
  1996  	// This can be extended in the future to support custom schedulers.
  1997  	Type string
  1998  
  1999  	// Priority is used to control scheduling importance and if this job
  2000  	// can preempt other jobs.
  2001  	Priority int
  2002  
  2003  	// AllAtOnce is used to control if incremental scheduling of task groups
  2004  	// is allowed or if we must do a gang scheduling of the entire job. This
  2005  	// can slow down larger jobs if resources are not available.
  2006  	AllAtOnce bool
  2007  
  2008  	// Datacenters contains all the datacenters this job is allowed to span
  2009  	Datacenters []string
  2010  
  2011  	// Constraints can be specified at a job level and apply to
  2012  	// all the task groups and tasks.
  2013  	Constraints []*Constraint
  2014  
  2015  	// TaskGroups are the collections of task groups that this job needs
  2016  	// to run. Each task group is an atomic unit of scheduling and placement.
  2017  	TaskGroups []*TaskGroup
  2018  
  2019  	// COMPAT: Remove in 0.7.0. Stagger is deprecated in 0.6.0.
  2020  	Update UpdateStrategy
  2021  
  2022  	// Periodic is used to define the interval the job is run at.
  2023  	Periodic *PeriodicConfig
  2024  
  2025  	// ParameterizedJob is used to specify the job as a parameterized job
  2026  	// for dispatching.
  2027  	ParameterizedJob *ParameterizedJobConfig
  2028  
  2029  	// Dispatched is used to identify if the Job has been dispatched from a
  2030  	// parameterized job.
  2031  	Dispatched bool
  2032  
  2033  	// Payload is the payload supplied when the job was dispatched.
  2034  	Payload []byte
  2035  
  2036  	// Meta is used to associate arbitrary metadata with this
  2037  	// job. This is opaque to Nomad.
  2038  	Meta map[string]string
  2039  
  2040  	// VaultToken is the Vault token that proves the submitter of the job has
  2041  	// access to the specified Vault policies. This field is only used to
  2042  	// transfer the token and is not stored after Job submission.
  2043  	VaultToken string
  2044  
  2045  	// Job status
  2046  	Status string
  2047  
  2048  	// StatusDescription is meant to provide more human useful information
  2049  	StatusDescription string
  2050  
  2051  	// Stable marks a job as stable. Stability is only defined on "service" and
  2052  	// "system" jobs. The stability of a job will be set automatically as part
  2053  	// of a deployment and can be manually set via APIs.
  2054  	Stable bool
  2055  
  2056  	// Version is a monotonically increasing version number that is incremented
  2057  	// on each job register.
  2058  	Version uint64
  2059  
  2060  	// SubmitTime is the time at which the job was submitted as a UnixNano in
  2061  	// UTC
  2062  	SubmitTime int64
  2063  
  2064  	// Raft Indexes
  2065  	CreateIndex    uint64
  2066  	ModifyIndex    uint64
  2067  	JobModifyIndex uint64
  2068  }
  2069  
  2070  // NamespacedID returns the namespaced id useful for logging
  2071  func (j *Job) NamespacedID() *NamespacedID {
  2072  	return &NamespacedID{
  2073  		ID:        j.ID,
  2074  		Namespace: j.Namespace,
  2075  	}
  2076  }
  2077  
  2078  // Canonicalize is used to canonicalize fields in the Job. This should be called
  2079  // when registering a Job. A set of warnings are returned if the job was changed
  2080  // in anyway that the user should be made aware of.
  2081  func (j *Job) Canonicalize() (warnings error) {
  2082  	if j == nil {
  2083  		return nil
  2084  	}
  2085  
  2086  	var mErr multierror.Error
  2087  	// Ensure that an empty and nil map are treated the same to avoid scheduling
  2088  	// problems since we use reflect DeepEquals.
  2089  	if len(j.Meta) == 0 {
  2090  		j.Meta = nil
  2091  	}
  2092  
  2093  	// Ensure the job is in a namespace.
  2094  	if j.Namespace == "" {
  2095  		j.Namespace = DefaultNamespace
  2096  	}
  2097  
  2098  	for _, tg := range j.TaskGroups {
  2099  		tg.Canonicalize(j)
  2100  	}
  2101  
  2102  	if j.ParameterizedJob != nil {
  2103  		j.ParameterizedJob.Canonicalize()
  2104  	}
  2105  
  2106  	if j.Periodic != nil {
  2107  		j.Periodic.Canonicalize()
  2108  	}
  2109  
  2110  	return mErr.ErrorOrNil()
  2111  }
  2112  
  2113  // Copy returns a deep copy of the Job. It is expected that callers use recover.
  2114  // This job can panic if the deep copy failed as it uses reflection.
  2115  func (j *Job) Copy() *Job {
  2116  	if j == nil {
  2117  		return nil
  2118  	}
  2119  	nj := new(Job)
  2120  	*nj = *j
  2121  	nj.Datacenters = helper.CopySliceString(nj.Datacenters)
  2122  	nj.Constraints = CopySliceConstraints(nj.Constraints)
  2123  
  2124  	if j.TaskGroups != nil {
  2125  		tgs := make([]*TaskGroup, len(nj.TaskGroups))
  2126  		for i, tg := range nj.TaskGroups {
  2127  			tgs[i] = tg.Copy()
  2128  		}
  2129  		nj.TaskGroups = tgs
  2130  	}
  2131  
  2132  	nj.Periodic = nj.Periodic.Copy()
  2133  	nj.Meta = helper.CopyMapStringString(nj.Meta)
  2134  	nj.ParameterizedJob = nj.ParameterizedJob.Copy()
  2135  	return nj
  2136  }
  2137  
  2138  // Validate is used to sanity check a job input
  2139  func (j *Job) Validate() error {
  2140  	var mErr multierror.Error
  2141  
  2142  	if j.Region == "" {
  2143  		mErr.Errors = append(mErr.Errors, errors.New("Missing job region"))
  2144  	}
  2145  	if j.ID == "" {
  2146  		mErr.Errors = append(mErr.Errors, errors.New("Missing job ID"))
  2147  	} else if strings.Contains(j.ID, " ") {
  2148  		mErr.Errors = append(mErr.Errors, errors.New("Job ID contains a space"))
  2149  	}
  2150  	if j.Name == "" {
  2151  		mErr.Errors = append(mErr.Errors, errors.New("Missing job name"))
  2152  	}
  2153  	if j.Namespace == "" {
  2154  		mErr.Errors = append(mErr.Errors, errors.New("Job must be in a namespace"))
  2155  	}
  2156  	switch j.Type {
  2157  	case JobTypeCore, JobTypeService, JobTypeBatch, JobTypeSystem:
  2158  	case "":
  2159  		mErr.Errors = append(mErr.Errors, errors.New("Missing job type"))
  2160  	default:
  2161  		mErr.Errors = append(mErr.Errors, fmt.Errorf("Invalid job type: %q", j.Type))
  2162  	}
  2163  	if j.Priority < JobMinPriority || j.Priority > JobMaxPriority {
  2164  		mErr.Errors = append(mErr.Errors, fmt.Errorf("Job priority must be between [%d, %d]", JobMinPriority, JobMaxPriority))
  2165  	}
  2166  	if len(j.Datacenters) == 0 {
  2167  		mErr.Errors = append(mErr.Errors, errors.New("Missing job datacenters"))
  2168  	}
  2169  	if len(j.TaskGroups) == 0 {
  2170  		mErr.Errors = append(mErr.Errors, errors.New("Missing job task groups"))
  2171  	}
  2172  	for idx, constr := range j.Constraints {
  2173  		if err := constr.Validate(); err != nil {
  2174  			outer := fmt.Errorf("Constraint %d validation failed: %s", idx+1, err)
  2175  			mErr.Errors = append(mErr.Errors, outer)
  2176  		}
  2177  	}
  2178  
  2179  	// Check for duplicate task groups
  2180  	taskGroups := make(map[string]int)
  2181  	for idx, tg := range j.TaskGroups {
  2182  		if tg.Name == "" {
  2183  			mErr.Errors = append(mErr.Errors, fmt.Errorf("Job task group %d missing name", idx+1))
  2184  		} else if existing, ok := taskGroups[tg.Name]; ok {
  2185  			mErr.Errors = append(mErr.Errors, fmt.Errorf("Job task group %d redefines '%s' from group %d", idx+1, tg.Name, existing+1))
  2186  		} else {
  2187  			taskGroups[tg.Name] = idx
  2188  		}
  2189  
  2190  		if j.Type == "system" && tg.Count > 1 {
  2191  			mErr.Errors = append(mErr.Errors,
  2192  				fmt.Errorf("Job task group %s has count %d. Count cannot exceed 1 with system scheduler",
  2193  					tg.Name, tg.Count))
  2194  		}
  2195  	}
  2196  
  2197  	// Validate the task group
  2198  	for _, tg := range j.TaskGroups {
  2199  		if err := tg.Validate(j); err != nil {
  2200  			outer := fmt.Errorf("Task group %s validation failed: %v", tg.Name, err)
  2201  			mErr.Errors = append(mErr.Errors, outer)
  2202  		}
  2203  	}
  2204  
  2205  	// Validate periodic is only used with batch jobs.
  2206  	if j.IsPeriodic() && j.Periodic.Enabled {
  2207  		if j.Type != JobTypeBatch {
  2208  			mErr.Errors = append(mErr.Errors,
  2209  				fmt.Errorf("Periodic can only be used with %q scheduler", JobTypeBatch))
  2210  		}
  2211  
  2212  		if err := j.Periodic.Validate(); err != nil {
  2213  			mErr.Errors = append(mErr.Errors, err)
  2214  		}
  2215  	}
  2216  
  2217  	if j.IsParameterized() {
  2218  		if j.Type != JobTypeBatch {
  2219  			mErr.Errors = append(mErr.Errors,
  2220  				fmt.Errorf("Parameterized job can only be used with %q scheduler", JobTypeBatch))
  2221  		}
  2222  
  2223  		if err := j.ParameterizedJob.Validate(); err != nil {
  2224  			mErr.Errors = append(mErr.Errors, err)
  2225  		}
  2226  	}
  2227  
  2228  	return mErr.ErrorOrNil()
  2229  }
  2230  
  2231  // Warnings returns a list of warnings that may be from dubious settings or
  2232  // deprecation warnings.
  2233  func (j *Job) Warnings() error {
  2234  	var mErr multierror.Error
  2235  
  2236  	// Check the groups
  2237  	for _, tg := range j.TaskGroups {
  2238  		if err := tg.Warnings(j); err != nil {
  2239  			outer := fmt.Errorf("Group %q has warnings: %v", tg.Name, err)
  2240  			mErr.Errors = append(mErr.Errors, outer)
  2241  		}
  2242  	}
  2243  
  2244  	return mErr.ErrorOrNil()
  2245  }
  2246  
  2247  // LookupTaskGroup finds a task group by name
  2248  func (j *Job) LookupTaskGroup(name string) *TaskGroup {
  2249  	for _, tg := range j.TaskGroups {
  2250  		if tg.Name == name {
  2251  			return tg
  2252  		}
  2253  	}
  2254  	return nil
  2255  }
  2256  
  2257  // CombinedTaskMeta takes a TaskGroup and Task name and returns the combined
  2258  // meta data for the task. When joining Job, Group and Task Meta, the precedence
  2259  // is by deepest scope (Task > Group > Job).
  2260  func (j *Job) CombinedTaskMeta(groupName, taskName string) map[string]string {
  2261  	group := j.LookupTaskGroup(groupName)
  2262  	if group == nil {
  2263  		return nil
  2264  	}
  2265  
  2266  	task := group.LookupTask(taskName)
  2267  	if task == nil {
  2268  		return nil
  2269  	}
  2270  
  2271  	meta := helper.CopyMapStringString(task.Meta)
  2272  	if meta == nil {
  2273  		meta = make(map[string]string, len(group.Meta)+len(j.Meta))
  2274  	}
  2275  
  2276  	// Add the group specific meta
  2277  	for k, v := range group.Meta {
  2278  		if _, ok := meta[k]; !ok {
  2279  			meta[k] = v
  2280  		}
  2281  	}
  2282  
  2283  	// Add the job specific meta
  2284  	for k, v := range j.Meta {
  2285  		if _, ok := meta[k]; !ok {
  2286  			meta[k] = v
  2287  		}
  2288  	}
  2289  
  2290  	return meta
  2291  }
  2292  
  2293  // Stopped returns if a job is stopped.
  2294  func (j *Job) Stopped() bool {
  2295  	return j == nil || j.Stop
  2296  }
  2297  
  2298  // HasUpdateStrategy returns if any task group in the job has an update strategy
  2299  func (j *Job) HasUpdateStrategy() bool {
  2300  	for _, tg := range j.TaskGroups {
  2301  		if tg.Update != nil {
  2302  			return true
  2303  		}
  2304  	}
  2305  
  2306  	return false
  2307  }
  2308  
  2309  // Stub is used to return a summary of the job
  2310  func (j *Job) Stub(summary *JobSummary) *JobListStub {
  2311  	return &JobListStub{
  2312  		ID:                j.ID,
  2313  		ParentID:          j.ParentID,
  2314  		Name:              j.Name,
  2315  		Type:              j.Type,
  2316  		Priority:          j.Priority,
  2317  		Periodic:          j.IsPeriodic(),
  2318  		ParameterizedJob:  j.IsParameterized(),
  2319  		Stop:              j.Stop,
  2320  		Status:            j.Status,
  2321  		StatusDescription: j.StatusDescription,
  2322  		CreateIndex:       j.CreateIndex,
  2323  		ModifyIndex:       j.ModifyIndex,
  2324  		JobModifyIndex:    j.JobModifyIndex,
  2325  		SubmitTime:        j.SubmitTime,
  2326  		JobSummary:        summary,
  2327  	}
  2328  }
  2329  
  2330  // IsPeriodic returns whether a job is periodic.
  2331  func (j *Job) IsPeriodic() bool {
  2332  	return j.Periodic != nil
  2333  }
  2334  
  2335  // IsPeriodicActive returns whether the job is an active periodic job that will
  2336  // create child jobs
  2337  func (j *Job) IsPeriodicActive() bool {
  2338  	return j.IsPeriodic() && j.Periodic.Enabled && !j.Stopped() && !j.IsParameterized()
  2339  }
  2340  
  2341  // IsParameterized returns whether a job is parameterized job.
  2342  func (j *Job) IsParameterized() bool {
  2343  	return j.ParameterizedJob != nil && !j.Dispatched
  2344  }
  2345  
  2346  // VaultPolicies returns the set of Vault policies per task group, per task
  2347  func (j *Job) VaultPolicies() map[string]map[string]*Vault {
  2348  	policies := make(map[string]map[string]*Vault, len(j.TaskGroups))
  2349  
  2350  	for _, tg := range j.TaskGroups {
  2351  		tgPolicies := make(map[string]*Vault, len(tg.Tasks))
  2352  
  2353  		for _, task := range tg.Tasks {
  2354  			if task.Vault == nil {
  2355  				continue
  2356  			}
  2357  
  2358  			tgPolicies[task.Name] = task.Vault
  2359  		}
  2360  
  2361  		if len(tgPolicies) != 0 {
  2362  			policies[tg.Name] = tgPolicies
  2363  		}
  2364  	}
  2365  
  2366  	return policies
  2367  }
  2368  
  2369  // RequiredSignals returns a mapping of task groups to tasks to their required
  2370  // set of signals
  2371  func (j *Job) RequiredSignals() map[string]map[string][]string {
  2372  	signals := make(map[string]map[string][]string)
  2373  
  2374  	for _, tg := range j.TaskGroups {
  2375  		for _, task := range tg.Tasks {
  2376  			// Use this local one as a set
  2377  			taskSignals := make(map[string]struct{})
  2378  
  2379  			// Check if the Vault change mode uses signals
  2380  			if task.Vault != nil && task.Vault.ChangeMode == VaultChangeModeSignal {
  2381  				taskSignals[task.Vault.ChangeSignal] = struct{}{}
  2382  			}
  2383  
  2384  			// If a user has specified a KillSignal, add it to required signals
  2385  			if task.KillSignal != "" {
  2386  				taskSignals[task.KillSignal] = struct{}{}
  2387  			}
  2388  
  2389  			// Check if any template change mode uses signals
  2390  			for _, t := range task.Templates {
  2391  				if t.ChangeMode != TemplateChangeModeSignal {
  2392  					continue
  2393  				}
  2394  
  2395  				taskSignals[t.ChangeSignal] = struct{}{}
  2396  			}
  2397  
  2398  			// Flatten and sort the signals
  2399  			l := len(taskSignals)
  2400  			if l == 0 {
  2401  				continue
  2402  			}
  2403  
  2404  			flat := make([]string, 0, l)
  2405  			for sig := range taskSignals {
  2406  				flat = append(flat, sig)
  2407  			}
  2408  
  2409  			sort.Strings(flat)
  2410  			tgSignals, ok := signals[tg.Name]
  2411  			if !ok {
  2412  				tgSignals = make(map[string][]string)
  2413  				signals[tg.Name] = tgSignals
  2414  			}
  2415  			tgSignals[task.Name] = flat
  2416  		}
  2417  
  2418  	}
  2419  
  2420  	return signals
  2421  }
  2422  
  2423  // SpecChanged determines if the functional specification has changed between
  2424  // two job versions.
  2425  func (j *Job) SpecChanged(new *Job) bool {
  2426  	if j == nil {
  2427  		return new != nil
  2428  	}
  2429  
  2430  	// Create a copy of the new job
  2431  	c := new.Copy()
  2432  
  2433  	// Update the new job so we can do a reflect
  2434  	c.Status = j.Status
  2435  	c.StatusDescription = j.StatusDescription
  2436  	c.Stable = j.Stable
  2437  	c.Version = j.Version
  2438  	c.CreateIndex = j.CreateIndex
  2439  	c.ModifyIndex = j.ModifyIndex
  2440  	c.JobModifyIndex = j.JobModifyIndex
  2441  	c.SubmitTime = j.SubmitTime
  2442  
  2443  	// Deep equals the jobs
  2444  	return !reflect.DeepEqual(j, c)
  2445  }
  2446  
  2447  func (j *Job) SetSubmitTime() {
  2448  	j.SubmitTime = time.Now().UTC().UnixNano()
  2449  }
  2450  
  2451  // JobListStub is used to return a subset of job information
  2452  // for the job list
  2453  type JobListStub struct {
  2454  	ID                string
  2455  	ParentID          string
  2456  	Name              string
  2457  	Type              string
  2458  	Priority          int
  2459  	Periodic          bool
  2460  	ParameterizedJob  bool
  2461  	Stop              bool
  2462  	Status            string
  2463  	StatusDescription string
  2464  	JobSummary        *JobSummary
  2465  	CreateIndex       uint64
  2466  	ModifyIndex       uint64
  2467  	JobModifyIndex    uint64
  2468  	SubmitTime        int64
  2469  }
  2470  
  2471  // JobSummary summarizes the state of the allocations of a job
  2472  type JobSummary struct {
  2473  	// JobID is the ID of the job the summary is for
  2474  	JobID string
  2475  
  2476  	// Namespace is the namespace of the job and its summary
  2477  	Namespace string
  2478  
  2479  	// Summary contains the summary per task group for the Job
  2480  	Summary map[string]TaskGroupSummary
  2481  
  2482  	// Children contains a summary for the children of this job.
  2483  	Children *JobChildrenSummary
  2484  
  2485  	// Raft Indexes
  2486  	CreateIndex uint64
  2487  	ModifyIndex uint64
  2488  }
  2489  
  2490  // Copy returns a new copy of JobSummary
  2491  func (js *JobSummary) Copy() *JobSummary {
  2492  	newJobSummary := new(JobSummary)
  2493  	*newJobSummary = *js
  2494  	newTGSummary := make(map[string]TaskGroupSummary, len(js.Summary))
  2495  	for k, v := range js.Summary {
  2496  		newTGSummary[k] = v
  2497  	}
  2498  	newJobSummary.Summary = newTGSummary
  2499  	newJobSummary.Children = newJobSummary.Children.Copy()
  2500  	return newJobSummary
  2501  }
  2502  
  2503  // JobChildrenSummary contains the summary of children job statuses
  2504  type JobChildrenSummary struct {
  2505  	Pending int64
  2506  	Running int64
  2507  	Dead    int64
  2508  }
  2509  
  2510  // Copy returns a new copy of a JobChildrenSummary
  2511  func (jc *JobChildrenSummary) Copy() *JobChildrenSummary {
  2512  	if jc == nil {
  2513  		return nil
  2514  	}
  2515  
  2516  	njc := new(JobChildrenSummary)
  2517  	*njc = *jc
  2518  	return njc
  2519  }
  2520  
  2521  // TaskGroup summarizes the state of all the allocations of a particular
  2522  // TaskGroup
  2523  type TaskGroupSummary struct {
  2524  	Queued   int
  2525  	Complete int
  2526  	Failed   int
  2527  	Running  int
  2528  	Starting int
  2529  	Lost     int
  2530  }
  2531  
  2532  const (
  2533  	// Checks uses any registered health check state in combination with task
  2534  	// states to determine if a allocation is healthy.
  2535  	UpdateStrategyHealthCheck_Checks = "checks"
  2536  
  2537  	// TaskStates uses the task states of an allocation to determine if the
  2538  	// allocation is healthy.
  2539  	UpdateStrategyHealthCheck_TaskStates = "task_states"
  2540  
  2541  	// Manual allows the operator to manually signal to Nomad when an
  2542  	// allocations is healthy. This allows more advanced health checking that is
  2543  	// outside of the scope of Nomad.
  2544  	UpdateStrategyHealthCheck_Manual = "manual"
  2545  )
  2546  
  2547  var (
  2548  	// DefaultUpdateStrategy provides a baseline that can be used to upgrade
  2549  	// jobs with the old policy or for populating field defaults.
  2550  	DefaultUpdateStrategy = &UpdateStrategy{
  2551  		Stagger:          30 * time.Second,
  2552  		MaxParallel:      1,
  2553  		HealthCheck:      UpdateStrategyHealthCheck_Checks,
  2554  		MinHealthyTime:   10 * time.Second,
  2555  		HealthyDeadline:  5 * time.Minute,
  2556  		ProgressDeadline: 10 * time.Minute,
  2557  		AutoRevert:       false,
  2558  		Canary:           0,
  2559  	}
  2560  )
  2561  
  2562  // UpdateStrategy is used to modify how updates are done
  2563  type UpdateStrategy struct {
  2564  	// Stagger is used to determine the rate at which allocations are migrated
  2565  	// due to down or draining nodes.
  2566  	Stagger time.Duration
  2567  
  2568  	// MaxParallel is how many updates can be done in parallel
  2569  	MaxParallel int
  2570  
  2571  	// HealthCheck specifies the mechanism in which allocations are marked
  2572  	// healthy or unhealthy as part of a deployment.
  2573  	HealthCheck string
  2574  
  2575  	// MinHealthyTime is the minimum time an allocation must be in the healthy
  2576  	// state before it is marked as healthy, unblocking more allocations to be
  2577  	// rolled.
  2578  	MinHealthyTime time.Duration
  2579  
  2580  	// HealthyDeadline is the time in which an allocation must be marked as
  2581  	// healthy before it is automatically transitioned to unhealthy. This time
  2582  	// period doesn't count against the MinHealthyTime.
  2583  	HealthyDeadline time.Duration
  2584  
  2585  	// ProgressDeadline is the time in which an allocation as part of the
  2586  	// deployment must transition to healthy. If no allocation becomes healthy
  2587  	// after the deadline, the deployment is marked as failed. If the deadline
  2588  	// is zero, the first failure causes the deployment to fail.
  2589  	ProgressDeadline time.Duration
  2590  
  2591  	// AutoRevert declares that if a deployment fails because of unhealthy
  2592  	// allocations, there should be an attempt to auto-revert the job to a
  2593  	// stable version.
  2594  	AutoRevert bool
  2595  
  2596  	// Canary is the number of canaries to deploy when a change to the task
  2597  	// group is detected.
  2598  	Canary int
  2599  }
  2600  
  2601  func (u *UpdateStrategy) Copy() *UpdateStrategy {
  2602  	if u == nil {
  2603  		return nil
  2604  	}
  2605  
  2606  	copy := new(UpdateStrategy)
  2607  	*copy = *u
  2608  	return copy
  2609  }
  2610  
  2611  func (u *UpdateStrategy) Validate() error {
  2612  	if u == nil {
  2613  		return nil
  2614  	}
  2615  
  2616  	var mErr multierror.Error
  2617  	switch u.HealthCheck {
  2618  	case UpdateStrategyHealthCheck_Checks, UpdateStrategyHealthCheck_TaskStates, UpdateStrategyHealthCheck_Manual:
  2619  	default:
  2620  		multierror.Append(&mErr, fmt.Errorf("Invalid health check given: %q", u.HealthCheck))
  2621  	}
  2622  
  2623  	if u.MaxParallel < 1 {
  2624  		multierror.Append(&mErr, fmt.Errorf("Max parallel can not be less than one: %d < 1", u.MaxParallel))
  2625  	}
  2626  	if u.Canary < 0 {
  2627  		multierror.Append(&mErr, fmt.Errorf("Canary count can not be less than zero: %d < 0", u.Canary))
  2628  	}
  2629  	if u.MinHealthyTime < 0 {
  2630  		multierror.Append(&mErr, fmt.Errorf("Minimum healthy time may not be less than zero: %v", u.MinHealthyTime))
  2631  	}
  2632  	if u.HealthyDeadline <= 0 {
  2633  		multierror.Append(&mErr, fmt.Errorf("Healthy deadline must be greater than zero: %v", u.HealthyDeadline))
  2634  	}
  2635  	if u.ProgressDeadline < 0 {
  2636  		multierror.Append(&mErr, fmt.Errorf("Progress deadline must be zero or greater: %v", u.ProgressDeadline))
  2637  	}
  2638  	if u.MinHealthyTime >= u.HealthyDeadline {
  2639  		multierror.Append(&mErr, fmt.Errorf("Minimum healthy time must be less than healthy deadline: %v > %v", u.MinHealthyTime, u.HealthyDeadline))
  2640  	}
  2641  	if u.ProgressDeadline != 0 && u.HealthyDeadline >= u.ProgressDeadline {
  2642  		multierror.Append(&mErr, fmt.Errorf("Healthy deadline must be less than progress deadline: %v > %v", u.HealthyDeadline, u.ProgressDeadline))
  2643  	}
  2644  	if u.Stagger <= 0 {
  2645  		multierror.Append(&mErr, fmt.Errorf("Stagger must be greater than zero: %v", u.Stagger))
  2646  	}
  2647  
  2648  	return mErr.ErrorOrNil()
  2649  }
  2650  
  2651  // TODO(alexdadgar): Remove once no longer used by the scheduler.
  2652  // Rolling returns if a rolling strategy should be used
  2653  func (u *UpdateStrategy) Rolling() bool {
  2654  	return u.Stagger > 0 && u.MaxParallel > 0
  2655  }
  2656  
  2657  const (
  2658  	// PeriodicSpecCron is used for a cron spec.
  2659  	PeriodicSpecCron = "cron"
  2660  
  2661  	// PeriodicSpecTest is only used by unit tests. It is a sorted, comma
  2662  	// separated list of unix timestamps at which to launch.
  2663  	PeriodicSpecTest = "_internal_test"
  2664  )
  2665  
  2666  // Periodic defines the interval a job should be run at.
  2667  type PeriodicConfig struct {
  2668  	// Enabled determines if the job should be run periodically.
  2669  	Enabled bool
  2670  
  2671  	// Spec specifies the interval the job should be run as. It is parsed based
  2672  	// on the SpecType.
  2673  	Spec string
  2674  
  2675  	// SpecType defines the format of the spec.
  2676  	SpecType string
  2677  
  2678  	// ProhibitOverlap enforces that spawned jobs do not run in parallel.
  2679  	ProhibitOverlap bool
  2680  
  2681  	// TimeZone is the user specified string that determines the time zone to
  2682  	// launch against. The time zones must be specified from IANA Time Zone
  2683  	// database, such as "America/New_York".
  2684  	// Reference: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
  2685  	// Reference: https://www.iana.org/time-zones
  2686  	TimeZone string
  2687  
  2688  	// location is the time zone to evaluate the launch time against
  2689  	location *time.Location
  2690  }
  2691  
  2692  func (p *PeriodicConfig) Copy() *PeriodicConfig {
  2693  	if p == nil {
  2694  		return nil
  2695  	}
  2696  	np := new(PeriodicConfig)
  2697  	*np = *p
  2698  	return np
  2699  }
  2700  
  2701  func (p *PeriodicConfig) Validate() error {
  2702  	if !p.Enabled {
  2703  		return nil
  2704  	}
  2705  
  2706  	var mErr multierror.Error
  2707  	if p.Spec == "" {
  2708  		multierror.Append(&mErr, fmt.Errorf("Must specify a spec"))
  2709  	}
  2710  
  2711  	// Check if we got a valid time zone
  2712  	if p.TimeZone != "" {
  2713  		if _, err := time.LoadLocation(p.TimeZone); err != nil {
  2714  			multierror.Append(&mErr, fmt.Errorf("Invalid time zone %q: %v", p.TimeZone, err))
  2715  		}
  2716  	}
  2717  
  2718  	switch p.SpecType {
  2719  	case PeriodicSpecCron:
  2720  		// Validate the cron spec
  2721  		if _, err := cronexpr.Parse(p.Spec); err != nil {
  2722  			multierror.Append(&mErr, fmt.Errorf("Invalid cron spec %q: %v", p.Spec, err))
  2723  		}
  2724  	case PeriodicSpecTest:
  2725  		// No-op
  2726  	default:
  2727  		multierror.Append(&mErr, fmt.Errorf("Unknown periodic specification type %q", p.SpecType))
  2728  	}
  2729  
  2730  	return mErr.ErrorOrNil()
  2731  }
  2732  
  2733  func (p *PeriodicConfig) Canonicalize() {
  2734  	// Load the location
  2735  	l, err := time.LoadLocation(p.TimeZone)
  2736  	if err != nil {
  2737  		p.location = time.UTC
  2738  	}
  2739  
  2740  	p.location = l
  2741  }
  2742  
  2743  // CronParseNext is a helper that parses the next time for the given expression
  2744  // but captures any panic that may occur in the underlying library.
  2745  func CronParseNext(e *cronexpr.Expression, fromTime time.Time, spec string) (t time.Time, err error) {
  2746  	defer func() {
  2747  		if recover() != nil {
  2748  			t = time.Time{}
  2749  			err = fmt.Errorf("failed parsing cron expression: %q", spec)
  2750  		}
  2751  	}()
  2752  
  2753  	return e.Next(fromTime), nil
  2754  }
  2755  
  2756  // Next returns the closest time instant matching the spec that is after the
  2757  // passed time. If no matching instance exists, the zero value of time.Time is
  2758  // returned. The `time.Location` of the returned value matches that of the
  2759  // passed time.
  2760  func (p *PeriodicConfig) Next(fromTime time.Time) (time.Time, error) {
  2761  	switch p.SpecType {
  2762  	case PeriodicSpecCron:
  2763  		if e, err := cronexpr.Parse(p.Spec); err == nil {
  2764  			return CronParseNext(e, fromTime, p.Spec)
  2765  		}
  2766  	case PeriodicSpecTest:
  2767  		split := strings.Split(p.Spec, ",")
  2768  		if len(split) == 1 && split[0] == "" {
  2769  			return time.Time{}, nil
  2770  		}
  2771  
  2772  		// Parse the times
  2773  		times := make([]time.Time, len(split))
  2774  		for i, s := range split {
  2775  			unix, err := strconv.Atoi(s)
  2776  			if err != nil {
  2777  				return time.Time{}, nil
  2778  			}
  2779  
  2780  			times[i] = time.Unix(int64(unix), 0)
  2781  		}
  2782  
  2783  		// Find the next match
  2784  		for _, next := range times {
  2785  			if fromTime.Before(next) {
  2786  				return next, nil
  2787  			}
  2788  		}
  2789  	}
  2790  
  2791  	return time.Time{}, nil
  2792  }
  2793  
  2794  // GetLocation returns the location to use for determining the time zone to run
  2795  // the periodic job against.
  2796  func (p *PeriodicConfig) GetLocation() *time.Location {
  2797  	// Jobs pre 0.5.5 will not have this
  2798  	if p.location != nil {
  2799  		return p.location
  2800  	}
  2801  
  2802  	return time.UTC
  2803  }
  2804  
  2805  const (
  2806  	// PeriodicLaunchSuffix is the string appended to the periodic jobs ID
  2807  	// when launching derived instances of it.
  2808  	PeriodicLaunchSuffix = "/periodic-"
  2809  )
  2810  
  2811  // PeriodicLaunch tracks the last launch time of a periodic job.
  2812  type PeriodicLaunch struct {
  2813  	ID        string    // ID of the periodic job.
  2814  	Namespace string    // Namespace of the periodic job
  2815  	Launch    time.Time // The last launch time.
  2816  
  2817  	// Raft Indexes
  2818  	CreateIndex uint64
  2819  	ModifyIndex uint64
  2820  }
  2821  
  2822  const (
  2823  	DispatchPayloadForbidden = "forbidden"
  2824  	DispatchPayloadOptional  = "optional"
  2825  	DispatchPayloadRequired  = "required"
  2826  
  2827  	// DispatchLaunchSuffix is the string appended to the parameterized job's ID
  2828  	// when dispatching instances of it.
  2829  	DispatchLaunchSuffix = "/dispatch-"
  2830  )
  2831  
  2832  // ParameterizedJobConfig is used to configure the parameterized job
  2833  type ParameterizedJobConfig struct {
  2834  	// Payload configure the payload requirements
  2835  	Payload string
  2836  
  2837  	// MetaRequired is metadata keys that must be specified by the dispatcher
  2838  	MetaRequired []string
  2839  
  2840  	// MetaOptional is metadata keys that may be specified by the dispatcher
  2841  	MetaOptional []string
  2842  }
  2843  
  2844  func (d *ParameterizedJobConfig) Validate() error {
  2845  	var mErr multierror.Error
  2846  	switch d.Payload {
  2847  	case DispatchPayloadOptional, DispatchPayloadRequired, DispatchPayloadForbidden:
  2848  	default:
  2849  		multierror.Append(&mErr, fmt.Errorf("Unknown payload requirement: %q", d.Payload))
  2850  	}
  2851  
  2852  	// Check that the meta configurations are disjoint sets
  2853  	disjoint, offending := helper.SliceSetDisjoint(d.MetaRequired, d.MetaOptional)
  2854  	if !disjoint {
  2855  		multierror.Append(&mErr, fmt.Errorf("Required and optional meta keys should be disjoint. Following keys exist in both: %v", offending))
  2856  	}
  2857  
  2858  	return mErr.ErrorOrNil()
  2859  }
  2860  
  2861  func (d *ParameterizedJobConfig) Canonicalize() {
  2862  	if d.Payload == "" {
  2863  		d.Payload = DispatchPayloadOptional
  2864  	}
  2865  }
  2866  
  2867  func (d *ParameterizedJobConfig) Copy() *ParameterizedJobConfig {
  2868  	if d == nil {
  2869  		return nil
  2870  	}
  2871  	nd := new(ParameterizedJobConfig)
  2872  	*nd = *d
  2873  	nd.MetaOptional = helper.CopySliceString(nd.MetaOptional)
  2874  	nd.MetaRequired = helper.CopySliceString(nd.MetaRequired)
  2875  	return nd
  2876  }
  2877  
  2878  // DispatchedID returns an ID appropriate for a job dispatched against a
  2879  // particular parameterized job
  2880  func DispatchedID(templateID string, t time.Time) string {
  2881  	u := uuid.Generate()[:8]
  2882  	return fmt.Sprintf("%s%s%d-%s", templateID, DispatchLaunchSuffix, t.Unix(), u)
  2883  }
  2884  
  2885  // DispatchPayloadConfig configures how a task gets its input from a job dispatch
  2886  type DispatchPayloadConfig struct {
  2887  	// File specifies a relative path to where the input data should be written
  2888  	File string
  2889  }
  2890  
  2891  func (d *DispatchPayloadConfig) Copy() *DispatchPayloadConfig {
  2892  	if d == nil {
  2893  		return nil
  2894  	}
  2895  	nd := new(DispatchPayloadConfig)
  2896  	*nd = *d
  2897  	return nd
  2898  }
  2899  
  2900  func (d *DispatchPayloadConfig) Validate() error {
  2901  	// Verify the destination doesn't escape
  2902  	escaped, err := PathEscapesAllocDir("task/local/", d.File)
  2903  	if err != nil {
  2904  		return fmt.Errorf("invalid destination path: %v", err)
  2905  	} else if escaped {
  2906  		return fmt.Errorf("destination escapes allocation directory")
  2907  	}
  2908  
  2909  	return nil
  2910  }
  2911  
  2912  var (
  2913  	DefaultServiceJobRestartPolicy = RestartPolicy{
  2914  		Delay:    15 * time.Second,
  2915  		Attempts: 2,
  2916  		Interval: 30 * time.Minute,
  2917  		Mode:     RestartPolicyModeFail,
  2918  	}
  2919  	DefaultBatchJobRestartPolicy = RestartPolicy{
  2920  		Delay:    15 * time.Second,
  2921  		Attempts: 3,
  2922  		Interval: 24 * time.Hour,
  2923  		Mode:     RestartPolicyModeFail,
  2924  	}
  2925  )
  2926  
  2927  var (
  2928  	DefaultServiceJobReschedulePolicy = ReschedulePolicy{
  2929  		Delay:         30 * time.Second,
  2930  		DelayFunction: "exponential",
  2931  		MaxDelay:      1 * time.Hour,
  2932  		Unlimited:     true,
  2933  	}
  2934  	DefaultBatchJobReschedulePolicy = ReschedulePolicy{
  2935  		Attempts:      1,
  2936  		Interval:      24 * time.Hour,
  2937  		Delay:         5 * time.Second,
  2938  		DelayFunction: "constant",
  2939  	}
  2940  )
  2941  
  2942  const (
  2943  	// RestartPolicyModeDelay causes an artificial delay till the next interval is
  2944  	// reached when the specified attempts have been reached in the interval.
  2945  	RestartPolicyModeDelay = "delay"
  2946  
  2947  	// RestartPolicyModeFail causes a job to fail if the specified number of
  2948  	// attempts are reached within an interval.
  2949  	RestartPolicyModeFail = "fail"
  2950  
  2951  	// RestartPolicyMinInterval is the minimum interval that is accepted for a
  2952  	// restart policy.
  2953  	RestartPolicyMinInterval = 5 * time.Second
  2954  
  2955  	// ReasonWithinPolicy describes restart events that are within policy
  2956  	ReasonWithinPolicy = "Restart within policy"
  2957  )
  2958  
  2959  // RestartPolicy configures how Tasks are restarted when they crash or fail.
  2960  type RestartPolicy struct {
  2961  	// Attempts is the number of restart that will occur in an interval.
  2962  	Attempts int
  2963  
  2964  	// Interval is a duration in which we can limit the number of restarts
  2965  	// within.
  2966  	Interval time.Duration
  2967  
  2968  	// Delay is the time between a failure and a restart.
  2969  	Delay time.Duration
  2970  
  2971  	// Mode controls what happens when the task restarts more than attempt times
  2972  	// in an interval.
  2973  	Mode string
  2974  }
  2975  
  2976  func (r *RestartPolicy) Copy() *RestartPolicy {
  2977  	if r == nil {
  2978  		return nil
  2979  	}
  2980  	nrp := new(RestartPolicy)
  2981  	*nrp = *r
  2982  	return nrp
  2983  }
  2984  
  2985  func (r *RestartPolicy) Validate() error {
  2986  	var mErr multierror.Error
  2987  	switch r.Mode {
  2988  	case RestartPolicyModeDelay, RestartPolicyModeFail:
  2989  	default:
  2990  		multierror.Append(&mErr, fmt.Errorf("Unsupported restart mode: %q", r.Mode))
  2991  	}
  2992  
  2993  	// Check for ambiguous/confusing settings
  2994  	if r.Attempts == 0 && r.Mode != RestartPolicyModeFail {
  2995  		multierror.Append(&mErr, fmt.Errorf("Restart policy %q with %d attempts is ambiguous", r.Mode, r.Attempts))
  2996  	}
  2997  
  2998  	if r.Interval.Nanoseconds() < RestartPolicyMinInterval.Nanoseconds() {
  2999  		multierror.Append(&mErr, fmt.Errorf("Interval can not be less than %v (got %v)", RestartPolicyMinInterval, r.Interval))
  3000  	}
  3001  	if time.Duration(r.Attempts)*r.Delay > r.Interval {
  3002  		multierror.Append(&mErr,
  3003  			fmt.Errorf("Nomad can't restart the TaskGroup %v times in an interval of %v with a delay of %v", r.Attempts, r.Interval, r.Delay))
  3004  	}
  3005  	return mErr.ErrorOrNil()
  3006  }
  3007  
  3008  func NewRestartPolicy(jobType string) *RestartPolicy {
  3009  	switch jobType {
  3010  	case JobTypeService, JobTypeSystem:
  3011  		rp := DefaultServiceJobRestartPolicy
  3012  		return &rp
  3013  	case JobTypeBatch:
  3014  		rp := DefaultBatchJobRestartPolicy
  3015  		return &rp
  3016  	}
  3017  	return nil
  3018  }
  3019  
  3020  const ReschedulePolicyMinInterval = 15 * time.Second
  3021  const ReschedulePolicyMinDelay = 5 * time.Second
  3022  
  3023  var RescheduleDelayFunctions = [...]string{"constant", "exponential", "fibonacci"}
  3024  
  3025  // ReschedulePolicy configures how Tasks are rescheduled  when they crash or fail.
  3026  type ReschedulePolicy struct {
  3027  	// Attempts limits the number of rescheduling attempts that can occur in an interval.
  3028  	Attempts int
  3029  
  3030  	// Interval is a duration in which we can limit the number of reschedule attempts.
  3031  	Interval time.Duration
  3032  
  3033  	// Delay is a minimum duration to wait between reschedule attempts.
  3034  	// The delay function determines how much subsequent reschedule attempts are delayed by.
  3035  	Delay time.Duration
  3036  
  3037  	// DelayFunction determines how the delay progressively changes on subsequent reschedule
  3038  	// attempts. Valid values are "exponential", "constant", and "fibonacci".
  3039  	DelayFunction string
  3040  
  3041  	// MaxDelay is an upper bound on the delay.
  3042  	MaxDelay time.Duration
  3043  
  3044  	// Unlimited allows infinite rescheduling attempts. Only allowed when delay is set
  3045  	// between reschedule attempts.
  3046  	Unlimited bool
  3047  }
  3048  
  3049  func (r *ReschedulePolicy) Copy() *ReschedulePolicy {
  3050  	if r == nil {
  3051  		return nil
  3052  	}
  3053  	nrp := new(ReschedulePolicy)
  3054  	*nrp = *r
  3055  	return nrp
  3056  }
  3057  
  3058  func (r *ReschedulePolicy) Enabled() bool {
  3059  	enabled := r != nil && (r.Attempts > 0 || r.Unlimited)
  3060  	return enabled
  3061  }
  3062  
  3063  // Validate uses different criteria to validate the reschedule policy
  3064  // Delay must be a minimum of 5 seconds
  3065  // Delay Ceiling is ignored if Delay Function is "constant"
  3066  // Number of possible attempts is validated, given the interval, delay and delay function
  3067  func (r *ReschedulePolicy) Validate() error {
  3068  	if !r.Enabled() {
  3069  		return nil
  3070  	}
  3071  	var mErr multierror.Error
  3072  	// Check for ambiguous/confusing settings
  3073  	if r.Attempts > 0 {
  3074  		if r.Interval <= 0 {
  3075  			multierror.Append(&mErr, fmt.Errorf("Interval must be a non zero value if Attempts > 0"))
  3076  		}
  3077  		if r.Unlimited {
  3078  			multierror.Append(&mErr, fmt.Errorf("Reschedule Policy with Attempts = %v, Interval = %v, "+
  3079  				"and Unlimited = %v is ambiguous", r.Attempts, r.Interval, r.Unlimited))
  3080  			multierror.Append(&mErr, errors.New("If Attempts >0, Unlimited cannot also be set to true"))
  3081  		}
  3082  	}
  3083  
  3084  	delayPreCheck := true
  3085  	// Delay should be bigger than the default
  3086  	if r.Delay.Nanoseconds() < ReschedulePolicyMinDelay.Nanoseconds() {
  3087  		multierror.Append(&mErr, fmt.Errorf("Delay cannot be less than %v (got %v)", ReschedulePolicyMinDelay, r.Delay))
  3088  		delayPreCheck = false
  3089  	}
  3090  
  3091  	// Must use a valid delay function
  3092  	if !isValidDelayFunction(r.DelayFunction) {
  3093  		multierror.Append(&mErr, fmt.Errorf("Invalid delay function %q, must be one of %q", r.DelayFunction, RescheduleDelayFunctions))
  3094  		delayPreCheck = false
  3095  	}
  3096  
  3097  	// Validate MaxDelay if not using linear delay progression
  3098  	if r.DelayFunction != "constant" {
  3099  		if r.MaxDelay.Nanoseconds() < ReschedulePolicyMinDelay.Nanoseconds() {
  3100  			multierror.Append(&mErr, fmt.Errorf("Max Delay cannot be less than %v (got %v)", ReschedulePolicyMinDelay, r.Delay))
  3101  			delayPreCheck = false
  3102  		}
  3103  		if r.MaxDelay < r.Delay {
  3104  			multierror.Append(&mErr, fmt.Errorf("Max Delay cannot be less than Delay %v (got %v)", r.Delay, r.MaxDelay))
  3105  			delayPreCheck = false
  3106  		}
  3107  
  3108  	}
  3109  
  3110  	// Validate Interval and other delay parameters if attempts are limited
  3111  	if !r.Unlimited {
  3112  		if r.Interval.Nanoseconds() < ReschedulePolicyMinInterval.Nanoseconds() {
  3113  			multierror.Append(&mErr, fmt.Errorf("Interval cannot be less than %v (got %v)", ReschedulePolicyMinInterval, r.Interval))
  3114  		}
  3115  		if !delayPreCheck {
  3116  			// We can't cross validate the rest of the delay params if delayPreCheck fails, so return early
  3117  			return mErr.ErrorOrNil()
  3118  		}
  3119  		crossValidationErr := r.validateDelayParams()
  3120  		if crossValidationErr != nil {
  3121  			multierror.Append(&mErr, crossValidationErr)
  3122  		}
  3123  	}
  3124  	return mErr.ErrorOrNil()
  3125  }
  3126  
  3127  func isValidDelayFunction(delayFunc string) bool {
  3128  	for _, value := range RescheduleDelayFunctions {
  3129  		if value == delayFunc {
  3130  			return true
  3131  		}
  3132  	}
  3133  	return false
  3134  }
  3135  
  3136  func (r *ReschedulePolicy) validateDelayParams() error {
  3137  	ok, possibleAttempts, recommendedInterval := r.viableAttempts()
  3138  	if ok {
  3139  		return nil
  3140  	}
  3141  	var mErr multierror.Error
  3142  	if r.DelayFunction == "constant" {
  3143  		multierror.Append(&mErr, fmt.Errorf("Nomad can only make %v attempts in %v with initial delay %v and "+
  3144  			"delay function %q", possibleAttempts, r.Interval, r.Delay, r.DelayFunction))
  3145  	} else {
  3146  		multierror.Append(&mErr, fmt.Errorf("Nomad can only make %v attempts in %v with initial delay %v, "+
  3147  			"delay function %q, and delay ceiling %v", possibleAttempts, r.Interval, r.Delay, r.DelayFunction, r.MaxDelay))
  3148  	}
  3149  	multierror.Append(&mErr, fmt.Errorf("Set the interval to at least %v to accommodate %v attempts", recommendedInterval.Round(time.Second), r.Attempts))
  3150  	return mErr.ErrorOrNil()
  3151  }
  3152  
  3153  func (r *ReschedulePolicy) viableAttempts() (bool, int, time.Duration) {
  3154  	var possibleAttempts int
  3155  	var recommendedInterval time.Duration
  3156  	valid := true
  3157  	switch r.DelayFunction {
  3158  	case "constant":
  3159  		recommendedInterval = time.Duration(r.Attempts) * r.Delay
  3160  		if r.Interval < recommendedInterval {
  3161  			possibleAttempts = int(r.Interval / r.Delay)
  3162  			valid = false
  3163  		}
  3164  	case "exponential":
  3165  		for i := 0; i < r.Attempts; i++ {
  3166  			nextDelay := time.Duration(math.Pow(2, float64(i))) * r.Delay
  3167  			if nextDelay > r.MaxDelay {
  3168  				nextDelay = r.MaxDelay
  3169  				recommendedInterval += nextDelay
  3170  			} else {
  3171  				recommendedInterval = nextDelay
  3172  			}
  3173  			if recommendedInterval < r.Interval {
  3174  				possibleAttempts++
  3175  			}
  3176  		}
  3177  		if possibleAttempts < r.Attempts {
  3178  			valid = false
  3179  		}
  3180  	case "fibonacci":
  3181  		var slots []time.Duration
  3182  		slots = append(slots, r.Delay)
  3183  		slots = append(slots, r.Delay)
  3184  		reachedCeiling := false
  3185  		for i := 2; i < r.Attempts; i++ {
  3186  			var nextDelay time.Duration
  3187  			if reachedCeiling {
  3188  				//switch to linear
  3189  				nextDelay = slots[i-1] + r.MaxDelay
  3190  			} else {
  3191  				nextDelay = slots[i-1] + slots[i-2]
  3192  				if nextDelay > r.MaxDelay {
  3193  					nextDelay = r.MaxDelay
  3194  					reachedCeiling = true
  3195  				}
  3196  			}
  3197  			slots = append(slots, nextDelay)
  3198  		}
  3199  		recommendedInterval = slots[len(slots)-1]
  3200  		if r.Interval < recommendedInterval {
  3201  			valid = false
  3202  			// calculate possible attempts
  3203  			for i := 0; i < len(slots); i++ {
  3204  				if slots[i] > r.Interval {
  3205  					possibleAttempts = i
  3206  					break
  3207  				}
  3208  			}
  3209  		}
  3210  	default:
  3211  		return false, 0, 0
  3212  	}
  3213  	if possibleAttempts < 0 { // can happen if delay is bigger than interval
  3214  		possibleAttempts = 0
  3215  	}
  3216  	return valid, possibleAttempts, recommendedInterval
  3217  }
  3218  
  3219  func NewReschedulePolicy(jobType string) *ReschedulePolicy {
  3220  	switch jobType {
  3221  	case JobTypeService:
  3222  		rp := DefaultServiceJobReschedulePolicy
  3223  		return &rp
  3224  	case JobTypeBatch:
  3225  		rp := DefaultBatchJobReschedulePolicy
  3226  		return &rp
  3227  	}
  3228  	return nil
  3229  }
  3230  
  3231  const (
  3232  	MigrateStrategyHealthChecks = "checks"
  3233  	MigrateStrategyHealthStates = "task_states"
  3234  )
  3235  
  3236  type MigrateStrategy struct {
  3237  	MaxParallel     int
  3238  	HealthCheck     string
  3239  	MinHealthyTime  time.Duration
  3240  	HealthyDeadline time.Duration
  3241  }
  3242  
  3243  // DefaultMigrateStrategy is used for backwards compat with pre-0.8 Allocations
  3244  // that lack an update strategy.
  3245  //
  3246  // This function should match its counterpart in api/tasks.go
  3247  func DefaultMigrateStrategy() *MigrateStrategy {
  3248  	return &MigrateStrategy{
  3249  		MaxParallel:     1,
  3250  		HealthCheck:     MigrateStrategyHealthChecks,
  3251  		MinHealthyTime:  10 * time.Second,
  3252  		HealthyDeadline: 5 * time.Minute,
  3253  	}
  3254  }
  3255  
  3256  func (m *MigrateStrategy) Validate() error {
  3257  	var mErr multierror.Error
  3258  
  3259  	if m.MaxParallel < 0 {
  3260  		multierror.Append(&mErr, fmt.Errorf("MaxParallel must be >= 0 but found %d", m.MaxParallel))
  3261  	}
  3262  
  3263  	switch m.HealthCheck {
  3264  	case MigrateStrategyHealthChecks, MigrateStrategyHealthStates:
  3265  		// ok
  3266  	case "":
  3267  		if m.MaxParallel > 0 {
  3268  			multierror.Append(&mErr, fmt.Errorf("Missing HealthCheck"))
  3269  		}
  3270  	default:
  3271  		multierror.Append(&mErr, fmt.Errorf("Invalid HealthCheck: %q", m.HealthCheck))
  3272  	}
  3273  
  3274  	if m.MinHealthyTime < 0 {
  3275  		multierror.Append(&mErr, fmt.Errorf("MinHealthyTime is %s and must be >= 0", m.MinHealthyTime))
  3276  	}
  3277  
  3278  	if m.HealthyDeadline < 0 {
  3279  		multierror.Append(&mErr, fmt.Errorf("HealthyDeadline is %s and must be >= 0", m.HealthyDeadline))
  3280  	}
  3281  
  3282  	if m.MinHealthyTime > m.HealthyDeadline {
  3283  		multierror.Append(&mErr, fmt.Errorf("MinHealthyTime must be less than HealthyDeadline"))
  3284  	}
  3285  
  3286  	return mErr.ErrorOrNil()
  3287  }
  3288  
  3289  // TaskGroup is an atomic unit of placement. Each task group belongs to
  3290  // a job and may contain any number of tasks. A task group support running
  3291  // in many replicas using the same configuration..
  3292  type TaskGroup struct {
  3293  	// Name of the task group
  3294  	Name string
  3295  
  3296  	// Count is the number of replicas of this task group that should
  3297  	// be scheduled.
  3298  	Count int
  3299  
  3300  	// Update is used to control the update strategy for this task group
  3301  	Update *UpdateStrategy
  3302  
  3303  	// Migrate is used to control the migration strategy for this task group
  3304  	Migrate *MigrateStrategy
  3305  
  3306  	// Constraints can be specified at a task group level and apply to
  3307  	// all the tasks contained.
  3308  	Constraints []*Constraint
  3309  
  3310  	//RestartPolicy of a TaskGroup
  3311  	RestartPolicy *RestartPolicy
  3312  
  3313  	// Tasks are the collection of tasks that this task group needs to run
  3314  	Tasks []*Task
  3315  
  3316  	// EphemeralDisk is the disk resources that the task group requests
  3317  	EphemeralDisk *EphemeralDisk
  3318  
  3319  	// Meta is used to associate arbitrary metadata with this
  3320  	// task group. This is opaque to Nomad.
  3321  	Meta map[string]string
  3322  
  3323  	// ReschedulePolicy is used to configure how the scheduler should
  3324  	// retry failed allocations.
  3325  	ReschedulePolicy *ReschedulePolicy
  3326  }
  3327  
  3328  func (tg *TaskGroup) Copy() *TaskGroup {
  3329  	if tg == nil {
  3330  		return nil
  3331  	}
  3332  	ntg := new(TaskGroup)
  3333  	*ntg = *tg
  3334  	ntg.Update = ntg.Update.Copy()
  3335  	ntg.Constraints = CopySliceConstraints(ntg.Constraints)
  3336  	ntg.RestartPolicy = ntg.RestartPolicy.Copy()
  3337  	ntg.ReschedulePolicy = ntg.ReschedulePolicy.Copy()
  3338  
  3339  	if tg.Tasks != nil {
  3340  		tasks := make([]*Task, len(ntg.Tasks))
  3341  		for i, t := range ntg.Tasks {
  3342  			tasks[i] = t.Copy()
  3343  		}
  3344  		ntg.Tasks = tasks
  3345  	}
  3346  
  3347  	ntg.Meta = helper.CopyMapStringString(ntg.Meta)
  3348  
  3349  	if tg.EphemeralDisk != nil {
  3350  		ntg.EphemeralDisk = tg.EphemeralDisk.Copy()
  3351  	}
  3352  	return ntg
  3353  }
  3354  
  3355  // Canonicalize is used to canonicalize fields in the TaskGroup.
  3356  func (tg *TaskGroup) Canonicalize(job *Job) {
  3357  	// Ensure that an empty and nil map are treated the same to avoid scheduling
  3358  	// problems since we use reflect DeepEquals.
  3359  	if len(tg.Meta) == 0 {
  3360  		tg.Meta = nil
  3361  	}
  3362  
  3363  	// Set the default restart policy.
  3364  	if tg.RestartPolicy == nil {
  3365  		tg.RestartPolicy = NewRestartPolicy(job.Type)
  3366  	}
  3367  
  3368  	if tg.ReschedulePolicy == nil {
  3369  		tg.ReschedulePolicy = NewReschedulePolicy(job.Type)
  3370  	}
  3371  
  3372  	// Canonicalize Migrate for service jobs
  3373  	if job.Type == JobTypeService && tg.Migrate == nil {
  3374  		tg.Migrate = DefaultMigrateStrategy()
  3375  	}
  3376  
  3377  	// Set a default ephemeral disk object if the user has not requested for one
  3378  	if tg.EphemeralDisk == nil {
  3379  		tg.EphemeralDisk = DefaultEphemeralDisk()
  3380  	}
  3381  
  3382  	for _, task := range tg.Tasks {
  3383  		task.Canonicalize(job, tg)
  3384  	}
  3385  
  3386  	// Add up the disk resources to EphemeralDisk. This is done so that users
  3387  	// are not required to move their disk attribute from resources to
  3388  	// EphemeralDisk section of the job spec in Nomad 0.5
  3389  	// COMPAT 0.4.1 -> 0.5
  3390  	// Remove in 0.6
  3391  	var diskMB int
  3392  	for _, task := range tg.Tasks {
  3393  		diskMB += task.Resources.DiskMB
  3394  	}
  3395  	if diskMB > 0 {
  3396  		tg.EphemeralDisk.SizeMB = diskMB
  3397  	}
  3398  }
  3399  
  3400  // Validate is used to sanity check a task group
  3401  func (tg *TaskGroup) Validate(j *Job) error {
  3402  	var mErr multierror.Error
  3403  	if tg.Name == "" {
  3404  		mErr.Errors = append(mErr.Errors, errors.New("Missing task group name"))
  3405  	}
  3406  	if tg.Count < 0 {
  3407  		mErr.Errors = append(mErr.Errors, errors.New("Task group count can't be negative"))
  3408  	}
  3409  	if len(tg.Tasks) == 0 {
  3410  		mErr.Errors = append(mErr.Errors, errors.New("Missing tasks for task group"))
  3411  	}
  3412  	for idx, constr := range tg.Constraints {
  3413  		if err := constr.Validate(); err != nil {
  3414  			outer := fmt.Errorf("Constraint %d validation failed: %s", idx+1, err)
  3415  			mErr.Errors = append(mErr.Errors, outer)
  3416  		}
  3417  	}
  3418  
  3419  	if tg.RestartPolicy != nil {
  3420  		if err := tg.RestartPolicy.Validate(); err != nil {
  3421  			mErr.Errors = append(mErr.Errors, err)
  3422  		}
  3423  	} else {
  3424  		mErr.Errors = append(mErr.Errors, fmt.Errorf("Task Group %v should have a restart policy", tg.Name))
  3425  	}
  3426  
  3427  	if j.Type == JobTypeSystem {
  3428  		if tg.ReschedulePolicy != nil {
  3429  			mErr.Errors = append(mErr.Errors, fmt.Errorf("System jobs should not have a reschedule policy"))
  3430  		}
  3431  	} else {
  3432  		if tg.ReschedulePolicy != nil {
  3433  			if err := tg.ReschedulePolicy.Validate(); err != nil {
  3434  				mErr.Errors = append(mErr.Errors, err)
  3435  			}
  3436  		} else {
  3437  			mErr.Errors = append(mErr.Errors, fmt.Errorf("Task Group %v should have a reschedule policy", tg.Name))
  3438  		}
  3439  	}
  3440  
  3441  	if tg.EphemeralDisk != nil {
  3442  		if err := tg.EphemeralDisk.Validate(); err != nil {
  3443  			mErr.Errors = append(mErr.Errors, err)
  3444  		}
  3445  	} else {
  3446  		mErr.Errors = append(mErr.Errors, fmt.Errorf("Task Group %v should have an ephemeral disk object", tg.Name))
  3447  	}
  3448  
  3449  	// Validate the update strategy
  3450  	if u := tg.Update; u != nil {
  3451  		switch j.Type {
  3452  		case JobTypeService, JobTypeSystem:
  3453  		default:
  3454  			mErr.Errors = append(mErr.Errors, fmt.Errorf("Job type %q does not allow update block", j.Type))
  3455  		}
  3456  		if err := u.Validate(); err != nil {
  3457  			mErr.Errors = append(mErr.Errors, err)
  3458  		}
  3459  	}
  3460  
  3461  	// Validate the migration strategy
  3462  	switch j.Type {
  3463  	case JobTypeService:
  3464  		if tg.Migrate != nil {
  3465  			if err := tg.Migrate.Validate(); err != nil {
  3466  				mErr.Errors = append(mErr.Errors, err)
  3467  			}
  3468  		}
  3469  	default:
  3470  		if tg.Migrate != nil {
  3471  			mErr.Errors = append(mErr.Errors, fmt.Errorf("Job type %q does not allow migrate block", j.Type))
  3472  		}
  3473  	}
  3474  
  3475  	// Check for duplicate tasks, that there is only leader task if any,
  3476  	// and no duplicated static ports
  3477  	tasks := make(map[string]int)
  3478  	staticPorts := make(map[int]string)
  3479  	leaderTasks := 0
  3480  	for idx, task := range tg.Tasks {
  3481  		if task.Name == "" {
  3482  			mErr.Errors = append(mErr.Errors, fmt.Errorf("Task %d missing name", idx+1))
  3483  		} else if existing, ok := tasks[task.Name]; ok {
  3484  			mErr.Errors = append(mErr.Errors, fmt.Errorf("Task %d redefines '%s' from task %d", idx+1, task.Name, existing+1))
  3485  		} else {
  3486  			tasks[task.Name] = idx
  3487  		}
  3488  
  3489  		if task.Leader {
  3490  			leaderTasks++
  3491  		}
  3492  
  3493  		if task.Resources == nil {
  3494  			continue
  3495  		}
  3496  
  3497  		for _, net := range task.Resources.Networks {
  3498  			for _, port := range net.ReservedPorts {
  3499  				if other, ok := staticPorts[port.Value]; ok {
  3500  					err := fmt.Errorf("Static port %d already reserved by %s", port.Value, other)
  3501  					mErr.Errors = append(mErr.Errors, err)
  3502  				} else {
  3503  					staticPorts[port.Value] = fmt.Sprintf("%s:%s", task.Name, port.Label)
  3504  				}
  3505  			}
  3506  		}
  3507  	}
  3508  
  3509  	if leaderTasks > 1 {
  3510  		mErr.Errors = append(mErr.Errors, fmt.Errorf("Only one task may be marked as leader"))
  3511  	}
  3512  
  3513  	// Validate the tasks
  3514  	for _, task := range tg.Tasks {
  3515  		if err := task.Validate(tg.EphemeralDisk); err != nil {
  3516  			outer := fmt.Errorf("Task %s validation failed: %v", task.Name, err)
  3517  			mErr.Errors = append(mErr.Errors, outer)
  3518  		}
  3519  	}
  3520  	return mErr.ErrorOrNil()
  3521  }
  3522  
  3523  // Warnings returns a list of warnings that may be from dubious settings or
  3524  // deprecation warnings.
  3525  func (tg *TaskGroup) Warnings(j *Job) error {
  3526  	var mErr multierror.Error
  3527  
  3528  	// Validate the update strategy
  3529  	if u := tg.Update; u != nil {
  3530  		// Check the counts are appropriate
  3531  		if u.MaxParallel > tg.Count {
  3532  			mErr.Errors = append(mErr.Errors,
  3533  				fmt.Errorf("Update max parallel count is greater than task group count (%d > %d). "+
  3534  					"A destructive change would result in the simultaneous replacement of all allocations.", u.MaxParallel, tg.Count))
  3535  		}
  3536  	}
  3537  
  3538  	return mErr.ErrorOrNil()
  3539  }
  3540  
  3541  // LookupTask finds a task by name
  3542  func (tg *TaskGroup) LookupTask(name string) *Task {
  3543  	for _, t := range tg.Tasks {
  3544  		if t.Name == name {
  3545  			return t
  3546  		}
  3547  	}
  3548  	return nil
  3549  }
  3550  
  3551  func (tg *TaskGroup) GoString() string {
  3552  	return fmt.Sprintf("*%#v", *tg)
  3553  }
  3554  
  3555  // CombinedResources returns the combined resources for the task group
  3556  func (tg *TaskGroup) CombinedResources() *Resources {
  3557  	r := &Resources{
  3558  		DiskMB: tg.EphemeralDisk.SizeMB,
  3559  	}
  3560  	for _, task := range tg.Tasks {
  3561  		r.Add(task.Resources)
  3562  	}
  3563  	return r
  3564  }
  3565  
  3566  // CheckRestart describes if and when a task should be restarted based on
  3567  // failing health checks.
  3568  type CheckRestart struct {
  3569  	Limit          int           // Restart task after this many unhealthy intervals
  3570  	Grace          time.Duration // Grace time to give tasks after starting to get healthy
  3571  	IgnoreWarnings bool          // If true treat checks in `warning` as passing
  3572  }
  3573  
  3574  func (c *CheckRestart) Copy() *CheckRestart {
  3575  	if c == nil {
  3576  		return nil
  3577  	}
  3578  
  3579  	nc := new(CheckRestart)
  3580  	*nc = *c
  3581  	return nc
  3582  }
  3583  
  3584  func (c *CheckRestart) Validate() error {
  3585  	if c == nil {
  3586  		return nil
  3587  	}
  3588  
  3589  	var mErr multierror.Error
  3590  	if c.Limit < 0 {
  3591  		mErr.Errors = append(mErr.Errors, fmt.Errorf("limit must be greater than or equal to 0 but found %d", c.Limit))
  3592  	}
  3593  
  3594  	if c.Grace < 0 {
  3595  		mErr.Errors = append(mErr.Errors, fmt.Errorf("grace period must be greater than or equal to 0 but found %d", c.Grace))
  3596  	}
  3597  
  3598  	return mErr.ErrorOrNil()
  3599  }
  3600  
  3601  const (
  3602  	ServiceCheckHTTP   = "http"
  3603  	ServiceCheckTCP    = "tcp"
  3604  	ServiceCheckScript = "script"
  3605  	ServiceCheckGRPC   = "grpc"
  3606  
  3607  	// minCheckInterval is the minimum check interval permitted.  Consul
  3608  	// currently has its MinInterval set to 1s.  Mirror that here for
  3609  	// consistency.
  3610  	minCheckInterval = 1 * time.Second
  3611  
  3612  	// minCheckTimeout is the minimum check timeout permitted for Consul
  3613  	// script TTL checks.
  3614  	minCheckTimeout = 1 * time.Second
  3615  )
  3616  
  3617  // The ServiceCheck data model represents the consul health check that
  3618  // Nomad registers for a Task
  3619  type ServiceCheck struct {
  3620  	Name          string              // Name of the check, defaults to id
  3621  	Type          string              // Type of the check - tcp, http, docker and script
  3622  	Command       string              // Command is the command to run for script checks
  3623  	Args          []string            // Args is a list of arguments for script checks
  3624  	Path          string              // path of the health check url for http type check
  3625  	Protocol      string              // Protocol to use if check is http, defaults to http
  3626  	PortLabel     string              // The port to use for tcp/http checks
  3627  	AddressMode   string              // 'host' to use host ip:port or 'driver' to use driver's
  3628  	Interval      time.Duration       // Interval of the check
  3629  	Timeout       time.Duration       // Timeout of the response from the check before consul fails the check
  3630  	InitialStatus string              // Initial status of the check
  3631  	TLSSkipVerify bool                // Skip TLS verification when Protocol=https
  3632  	Method        string              // HTTP Method to use (GET by default)
  3633  	Header        map[string][]string // HTTP Headers for Consul to set when making HTTP checks
  3634  	CheckRestart  *CheckRestart       // If and when a task should be restarted based on checks
  3635  	GRPCService   string              // Service for GRPC checks
  3636  	GRPCUseTLS    bool                // Whether or not to use TLS for GRPC checks
  3637  }
  3638  
  3639  func (sc *ServiceCheck) Copy() *ServiceCheck {
  3640  	if sc == nil {
  3641  		return nil
  3642  	}
  3643  	nsc := new(ServiceCheck)
  3644  	*nsc = *sc
  3645  	nsc.Args = helper.CopySliceString(sc.Args)
  3646  	nsc.Header = helper.CopyMapStringSliceString(sc.Header)
  3647  	nsc.CheckRestart = sc.CheckRestart.Copy()
  3648  	return nsc
  3649  }
  3650  
  3651  func (sc *ServiceCheck) Canonicalize(serviceName string) {
  3652  	// Ensure empty maps/slices are treated as null to avoid scheduling
  3653  	// issues when using DeepEquals.
  3654  	if len(sc.Args) == 0 {
  3655  		sc.Args = nil
  3656  	}
  3657  
  3658  	if len(sc.Header) == 0 {
  3659  		sc.Header = nil
  3660  	} else {
  3661  		for k, v := range sc.Header {
  3662  			if len(v) == 0 {
  3663  				sc.Header[k] = nil
  3664  			}
  3665  		}
  3666  	}
  3667  
  3668  	if sc.Name == "" {
  3669  		sc.Name = fmt.Sprintf("service: %q check", serviceName)
  3670  	}
  3671  }
  3672  
  3673  // validate a Service's ServiceCheck
  3674  func (sc *ServiceCheck) validate() error {
  3675  	// Validate Type
  3676  	switch strings.ToLower(sc.Type) {
  3677  	case ServiceCheckGRPC:
  3678  	case ServiceCheckTCP:
  3679  	case ServiceCheckHTTP:
  3680  		if sc.Path == "" {
  3681  			return fmt.Errorf("http type must have a valid http path")
  3682  		}
  3683  		url, err := url.Parse(sc.Path)
  3684  		if err != nil {
  3685  			return fmt.Errorf("http type must have a valid http path")
  3686  		}
  3687  		if url.IsAbs() {
  3688  			return fmt.Errorf("http type must have a relative http path")
  3689  		}
  3690  
  3691  	case ServiceCheckScript:
  3692  		if sc.Command == "" {
  3693  			return fmt.Errorf("script type must have a valid script path")
  3694  		}
  3695  
  3696  	default:
  3697  		return fmt.Errorf(`invalid type (%+q), must be one of "http", "tcp", or "script" type`, sc.Type)
  3698  	}
  3699  
  3700  	// Validate interval and timeout
  3701  	if sc.Interval == 0 {
  3702  		return fmt.Errorf("missing required value interval. Interval cannot be less than %v", minCheckInterval)
  3703  	} else if sc.Interval < minCheckInterval {
  3704  		return fmt.Errorf("interval (%v) cannot be lower than %v", sc.Interval, minCheckInterval)
  3705  	}
  3706  
  3707  	if sc.Timeout == 0 {
  3708  		return fmt.Errorf("missing required value timeout. Timeout cannot be less than %v", minCheckInterval)
  3709  	} else if sc.Timeout < minCheckTimeout {
  3710  		return fmt.Errorf("timeout (%v) is lower than required minimum timeout %v", sc.Timeout, minCheckInterval)
  3711  	}
  3712  
  3713  	// Validate InitialStatus
  3714  	switch sc.InitialStatus {
  3715  	case "":
  3716  	case api.HealthPassing:
  3717  	case api.HealthWarning:
  3718  	case api.HealthCritical:
  3719  	default:
  3720  		return fmt.Errorf(`invalid initial check state (%s), must be one of %q, %q, %q or empty`, sc.InitialStatus, api.HealthPassing, api.HealthWarning, api.HealthCritical)
  3721  
  3722  	}
  3723  
  3724  	// Validate AddressMode
  3725  	switch sc.AddressMode {
  3726  	case "", AddressModeHost, AddressModeDriver:
  3727  		// Ok
  3728  	case AddressModeAuto:
  3729  		return fmt.Errorf("invalid address_mode %q - %s only valid for services", sc.AddressMode, AddressModeAuto)
  3730  	default:
  3731  		return fmt.Errorf("invalid address_mode %q", sc.AddressMode)
  3732  	}
  3733  
  3734  	return sc.CheckRestart.Validate()
  3735  }
  3736  
  3737  // RequiresPort returns whether the service check requires the task has a port.
  3738  func (sc *ServiceCheck) RequiresPort() bool {
  3739  	switch sc.Type {
  3740  	case ServiceCheckGRPC, ServiceCheckHTTP, ServiceCheckTCP:
  3741  		return true
  3742  	default:
  3743  		return false
  3744  	}
  3745  }
  3746  
  3747  // TriggersRestarts returns true if this check should be watched and trigger a restart
  3748  // on failure.
  3749  func (sc *ServiceCheck) TriggersRestarts() bool {
  3750  	return sc.CheckRestart != nil && sc.CheckRestart.Limit > 0
  3751  }
  3752  
  3753  // Hash all ServiceCheck fields and the check's corresponding service ID to
  3754  // create an identifier. The identifier is not guaranteed to be unique as if
  3755  // the PortLabel is blank, the Service's PortLabel will be used after Hash is
  3756  // called.
  3757  func (sc *ServiceCheck) Hash(serviceID string) string {
  3758  	h := sha1.New()
  3759  	io.WriteString(h, serviceID)
  3760  	io.WriteString(h, sc.Name)
  3761  	io.WriteString(h, sc.Type)
  3762  	io.WriteString(h, sc.Command)
  3763  	io.WriteString(h, strings.Join(sc.Args, ""))
  3764  	io.WriteString(h, sc.Path)
  3765  	io.WriteString(h, sc.Protocol)
  3766  	io.WriteString(h, sc.PortLabel)
  3767  	io.WriteString(h, sc.Interval.String())
  3768  	io.WriteString(h, sc.Timeout.String())
  3769  	io.WriteString(h, sc.Method)
  3770  	// Only include TLSSkipVerify if set to maintain ID stability with Nomad <0.6
  3771  	if sc.TLSSkipVerify {
  3772  		io.WriteString(h, "true")
  3773  	}
  3774  
  3775  	// Since map iteration order isn't stable we need to write k/v pairs to
  3776  	// a slice and sort it before hashing.
  3777  	if len(sc.Header) > 0 {
  3778  		headers := make([]string, 0, len(sc.Header))
  3779  		for k, v := range sc.Header {
  3780  			headers = append(headers, k+strings.Join(v, ""))
  3781  		}
  3782  		sort.Strings(headers)
  3783  		io.WriteString(h, strings.Join(headers, ""))
  3784  	}
  3785  
  3786  	// Only include AddressMode if set to maintain ID stability with Nomad <0.7.1
  3787  	if len(sc.AddressMode) > 0 {
  3788  		io.WriteString(h, sc.AddressMode)
  3789  	}
  3790  
  3791  	// Only include GRPC if set to maintain ID stability with Nomad <0.8.4
  3792  	if sc.GRPCService != "" {
  3793  		io.WriteString(h, sc.GRPCService)
  3794  	}
  3795  	if sc.GRPCUseTLS {
  3796  		io.WriteString(h, "true")
  3797  	}
  3798  
  3799  	return fmt.Sprintf("%x", h.Sum(nil))
  3800  }
  3801  
  3802  const (
  3803  	AddressModeAuto   = "auto"
  3804  	AddressModeHost   = "host"
  3805  	AddressModeDriver = "driver"
  3806  )
  3807  
  3808  // Service represents a Consul service definition in Nomad
  3809  type Service struct {
  3810  	// Name of the service registered with Consul. Consul defaults the
  3811  	// Name to ServiceID if not specified.  The Name if specified is used
  3812  	// as one of the seed values when generating a Consul ServiceID.
  3813  	Name string
  3814  
  3815  	// PortLabel is either the numeric port number or the `host:port`.
  3816  	// To specify the port number using the host's Consul Advertise
  3817  	// address, specify an empty host in the PortLabel (e.g. `:port`).
  3818  	PortLabel string
  3819  
  3820  	// AddressMode specifies whether or not to use the host ip:port for
  3821  	// this service.
  3822  	AddressMode string
  3823  
  3824  	Tags       []string        // List of tags for the service
  3825  	CanaryTags []string        // List of tags for the service when it is a canary
  3826  	Checks     []*ServiceCheck // List of checks associated with the service
  3827  }
  3828  
  3829  func (s *Service) Copy() *Service {
  3830  	if s == nil {
  3831  		return nil
  3832  	}
  3833  	ns := new(Service)
  3834  	*ns = *s
  3835  	ns.Tags = helper.CopySliceString(ns.Tags)
  3836  	ns.CanaryTags = helper.CopySliceString(ns.CanaryTags)
  3837  
  3838  	if s.Checks != nil {
  3839  		checks := make([]*ServiceCheck, len(ns.Checks))
  3840  		for i, c := range ns.Checks {
  3841  			checks[i] = c.Copy()
  3842  		}
  3843  		ns.Checks = checks
  3844  	}
  3845  
  3846  	return ns
  3847  }
  3848  
  3849  // Canonicalize interpolates values of Job, Task Group and Task in the Service
  3850  // Name. This also generates check names, service id and check ids.
  3851  func (s *Service) Canonicalize(job string, taskGroup string, task string) {
  3852  	// Ensure empty lists are treated as null to avoid scheduler issues when
  3853  	// using DeepEquals
  3854  	if len(s.Tags) == 0 {
  3855  		s.Tags = nil
  3856  	}
  3857  	if len(s.CanaryTags) == 0 {
  3858  		s.CanaryTags = nil
  3859  	}
  3860  	if len(s.Checks) == 0 {
  3861  		s.Checks = nil
  3862  	}
  3863  
  3864  	s.Name = args.ReplaceEnv(s.Name, map[string]string{
  3865  		"JOB":       job,
  3866  		"TASKGROUP": taskGroup,
  3867  		"TASK":      task,
  3868  		"BASE":      fmt.Sprintf("%s-%s-%s", job, taskGroup, task),
  3869  	},
  3870  	)
  3871  
  3872  	for _, check := range s.Checks {
  3873  		check.Canonicalize(s.Name)
  3874  	}
  3875  }
  3876  
  3877  // Validate checks if the Check definition is valid
  3878  func (s *Service) Validate() error {
  3879  	var mErr multierror.Error
  3880  
  3881  	// Ensure the service name is valid per the below RFCs but make an exception
  3882  	// for our interpolation syntax by first stripping any environment variables from the name
  3883  
  3884  	serviceNameStripped := args.ReplaceEnvWithPlaceHolder(s.Name, "ENV-VAR")
  3885  
  3886  	if err := s.ValidateName(serviceNameStripped); err != nil {
  3887  		mErr.Errors = append(mErr.Errors, fmt.Errorf("service name must be valid per RFC 1123 and can contain only alphanumeric characters or dashes: %q", s.Name))
  3888  	}
  3889  
  3890  	switch s.AddressMode {
  3891  	case "", AddressModeAuto, AddressModeHost, AddressModeDriver:
  3892  		// OK
  3893  	default:
  3894  		mErr.Errors = append(mErr.Errors, fmt.Errorf("service address_mode must be %q, %q, or %q; not %q", AddressModeAuto, AddressModeHost, AddressModeDriver, s.AddressMode))
  3895  	}
  3896  
  3897  	for _, c := range s.Checks {
  3898  		if s.PortLabel == "" && c.PortLabel == "" && c.RequiresPort() {
  3899  			mErr.Errors = append(mErr.Errors, fmt.Errorf("check %s invalid: check requires a port but neither check nor service %+q have a port", c.Name, s.Name))
  3900  			continue
  3901  		}
  3902  
  3903  		if err := c.validate(); err != nil {
  3904  			mErr.Errors = append(mErr.Errors, fmt.Errorf("check %s invalid: %v", c.Name, err))
  3905  		}
  3906  	}
  3907  
  3908  	return mErr.ErrorOrNil()
  3909  }
  3910  
  3911  // ValidateName checks if the services Name is valid and should be called after
  3912  // the name has been interpolated
  3913  func (s *Service) ValidateName(name string) error {
  3914  	// Ensure the service name is valid per RFC-952 §1
  3915  	// (https://tools.ietf.org/html/rfc952), RFC-1123 §2.1
  3916  	// (https://tools.ietf.org/html/rfc1123), and RFC-2782
  3917  	// (https://tools.ietf.org/html/rfc2782).
  3918  	re := regexp.MustCompile(`^(?i:[a-z0-9]|[a-z0-9][a-z0-9\-]{0,61}[a-z0-9])$`)
  3919  	if !re.MatchString(name) {
  3920  		return fmt.Errorf("service name must be valid per RFC 1123 and can contain only alphanumeric characters or dashes and must be no longer than 63 characters: %q", name)
  3921  	}
  3922  	return nil
  3923  }
  3924  
  3925  // Hash returns a base32 encoded hash of a Service's contents excluding checks
  3926  // as they're hashed independently.
  3927  func (s *Service) Hash(allocID, taskName string, canary bool) string {
  3928  	h := sha1.New()
  3929  	io.WriteString(h, allocID)
  3930  	io.WriteString(h, taskName)
  3931  	io.WriteString(h, s.Name)
  3932  	io.WriteString(h, s.PortLabel)
  3933  	io.WriteString(h, s.AddressMode)
  3934  	for _, tag := range s.Tags {
  3935  		io.WriteString(h, tag)
  3936  	}
  3937  	for _, tag := range s.CanaryTags {
  3938  		io.WriteString(h, tag)
  3939  	}
  3940  
  3941  	// Vary ID on whether or not CanaryTags will be used
  3942  	if canary {
  3943  		h.Write([]byte("Canary"))
  3944  	}
  3945  
  3946  	// Base32 is used for encoding the hash as sha1 hashes can always be
  3947  	// encoded without padding, only 4 bytes larger than base64, and saves
  3948  	// 8 bytes vs hex. Since these hashes are used in Consul URLs it's nice
  3949  	// to have a reasonably compact URL-safe representation.
  3950  	return b32.EncodeToString(h.Sum(nil))
  3951  }
  3952  
  3953  const (
  3954  	// DefaultKillTimeout is the default timeout between signaling a task it
  3955  	// will be killed and killing it.
  3956  	DefaultKillTimeout = 5 * time.Second
  3957  )
  3958  
  3959  // LogConfig provides configuration for log rotation
  3960  type LogConfig struct {
  3961  	MaxFiles      int
  3962  	MaxFileSizeMB int
  3963  }
  3964  
  3965  // DefaultLogConfig returns the default LogConfig values.
  3966  func DefaultLogConfig() *LogConfig {
  3967  	return &LogConfig{
  3968  		MaxFiles:      10,
  3969  		MaxFileSizeMB: 10,
  3970  	}
  3971  }
  3972  
  3973  // Validate returns an error if the log config specified are less than
  3974  // the minimum allowed.
  3975  func (l *LogConfig) Validate() error {
  3976  	var mErr multierror.Error
  3977  	if l.MaxFiles < 1 {
  3978  		mErr.Errors = append(mErr.Errors, fmt.Errorf("minimum number of files is 1; got %d", l.MaxFiles))
  3979  	}
  3980  	if l.MaxFileSizeMB < 1 {
  3981  		mErr.Errors = append(mErr.Errors, fmt.Errorf("minimum file size is 1MB; got %d", l.MaxFileSizeMB))
  3982  	}
  3983  	return mErr.ErrorOrNil()
  3984  }
  3985  
  3986  // Task is a single process typically that is executed as part of a task group.
  3987  type Task struct {
  3988  	// Name of the task
  3989  	Name string
  3990  
  3991  	// Driver is used to control which driver is used
  3992  	Driver string
  3993  
  3994  	// User is used to determine which user will run the task. It defaults to
  3995  	// the same user the Nomad client is being run as.
  3996  	User string
  3997  
  3998  	// Config is provided to the driver to initialize
  3999  	Config map[string]interface{}
  4000  
  4001  	// Map of environment variables to be used by the driver
  4002  	Env map[string]string
  4003  
  4004  	// List of service definitions exposed by the Task
  4005  	Services []*Service
  4006  
  4007  	// Vault is used to define the set of Vault policies that this task should
  4008  	// have access to.
  4009  	Vault *Vault
  4010  
  4011  	// Templates are the set of templates to be rendered for the task.
  4012  	Templates []*Template
  4013  
  4014  	// Constraints can be specified at a task level and apply only to
  4015  	// the particular task.
  4016  	Constraints []*Constraint
  4017  
  4018  	// Resources is the resources needed by this task
  4019  	Resources *Resources
  4020  
  4021  	// DispatchPayload configures how the task retrieves its input from a dispatch
  4022  	DispatchPayload *DispatchPayloadConfig
  4023  
  4024  	// Meta is used to associate arbitrary metadata with this
  4025  	// task. This is opaque to Nomad.
  4026  	Meta map[string]string
  4027  
  4028  	// KillTimeout is the time between signaling a task that it will be
  4029  	// killed and killing it.
  4030  	KillTimeout time.Duration
  4031  
  4032  	// LogConfig provides configuration for log rotation
  4033  	LogConfig *LogConfig
  4034  
  4035  	// Artifacts is a list of artifacts to download and extract before running
  4036  	// the task.
  4037  	Artifacts []*TaskArtifact
  4038  
  4039  	// Leader marks the task as the leader within the group. When the leader
  4040  	// task exits, other tasks will be gracefully terminated.
  4041  	Leader bool
  4042  
  4043  	// ShutdownDelay is the duration of the delay between deregistering a
  4044  	// task from Consul and sending it a signal to shutdown. See #2441
  4045  	ShutdownDelay time.Duration
  4046  
  4047  	// The kill signal to use for the task. This is an optional specification,
  4048  
  4049  	// KillSignal is the kill signal to use for the task. This is an optional
  4050  	// specification and defaults to SIGINT
  4051  	KillSignal string
  4052  }
  4053  
  4054  func (t *Task) Copy() *Task {
  4055  	if t == nil {
  4056  		return nil
  4057  	}
  4058  	nt := new(Task)
  4059  	*nt = *t
  4060  	nt.Env = helper.CopyMapStringString(nt.Env)
  4061  
  4062  	if t.Services != nil {
  4063  		services := make([]*Service, len(nt.Services))
  4064  		for i, s := range nt.Services {
  4065  			services[i] = s.Copy()
  4066  		}
  4067  		nt.Services = services
  4068  	}
  4069  
  4070  	nt.Constraints = CopySliceConstraints(nt.Constraints)
  4071  
  4072  	nt.Vault = nt.Vault.Copy()
  4073  	nt.Resources = nt.Resources.Copy()
  4074  	nt.Meta = helper.CopyMapStringString(nt.Meta)
  4075  	nt.DispatchPayload = nt.DispatchPayload.Copy()
  4076  
  4077  	if t.Artifacts != nil {
  4078  		artifacts := make([]*TaskArtifact, 0, len(t.Artifacts))
  4079  		for _, a := range nt.Artifacts {
  4080  			artifacts = append(artifacts, a.Copy())
  4081  		}
  4082  		nt.Artifacts = artifacts
  4083  	}
  4084  
  4085  	if i, err := copystructure.Copy(nt.Config); err != nil {
  4086  		panic(err.Error())
  4087  	} else {
  4088  		nt.Config = i.(map[string]interface{})
  4089  	}
  4090  
  4091  	if t.Templates != nil {
  4092  		templates := make([]*Template, len(t.Templates))
  4093  		for i, tmpl := range nt.Templates {
  4094  			templates[i] = tmpl.Copy()
  4095  		}
  4096  		nt.Templates = templates
  4097  	}
  4098  
  4099  	return nt
  4100  }
  4101  
  4102  // Canonicalize canonicalizes fields in the task.
  4103  func (t *Task) Canonicalize(job *Job, tg *TaskGroup) {
  4104  	// Ensure that an empty and nil map are treated the same to avoid scheduling
  4105  	// problems since we use reflect DeepEquals.
  4106  	if len(t.Meta) == 0 {
  4107  		t.Meta = nil
  4108  	}
  4109  	if len(t.Config) == 0 {
  4110  		t.Config = nil
  4111  	}
  4112  	if len(t.Env) == 0 {
  4113  		t.Env = nil
  4114  	}
  4115  
  4116  	for _, service := range t.Services {
  4117  		service.Canonicalize(job.Name, tg.Name, t.Name)
  4118  	}
  4119  
  4120  	// If Resources are nil initialize them to defaults, otherwise canonicalize
  4121  	if t.Resources == nil {
  4122  		t.Resources = DefaultResources()
  4123  	} else {
  4124  		t.Resources.Canonicalize()
  4125  	}
  4126  
  4127  	// Set the default timeout if it is not specified.
  4128  	if t.KillTimeout == 0 {
  4129  		t.KillTimeout = DefaultKillTimeout
  4130  	}
  4131  
  4132  	if t.Vault != nil {
  4133  		t.Vault.Canonicalize()
  4134  	}
  4135  
  4136  	for _, template := range t.Templates {
  4137  		template.Canonicalize()
  4138  	}
  4139  }
  4140  
  4141  func (t *Task) GoString() string {
  4142  	return fmt.Sprintf("*%#v", *t)
  4143  }
  4144  
  4145  // Validate is used to sanity check a task
  4146  func (t *Task) Validate(ephemeralDisk *EphemeralDisk) error {
  4147  	var mErr multierror.Error
  4148  	if t.Name == "" {
  4149  		mErr.Errors = append(mErr.Errors, errors.New("Missing task name"))
  4150  	}
  4151  	if strings.ContainsAny(t.Name, `/\`) {
  4152  		// We enforce this so that when creating the directory on disk it will
  4153  		// not have any slashes.
  4154  		mErr.Errors = append(mErr.Errors, errors.New("Task name cannot include slashes"))
  4155  	}
  4156  	if t.Driver == "" {
  4157  		mErr.Errors = append(mErr.Errors, errors.New("Missing task driver"))
  4158  	}
  4159  	if t.KillTimeout < 0 {
  4160  		mErr.Errors = append(mErr.Errors, errors.New("KillTimeout must be a positive value"))
  4161  	}
  4162  	if t.ShutdownDelay < 0 {
  4163  		mErr.Errors = append(mErr.Errors, errors.New("ShutdownDelay must be a positive value"))
  4164  	}
  4165  
  4166  	// Validate the resources.
  4167  	if t.Resources == nil {
  4168  		mErr.Errors = append(mErr.Errors, errors.New("Missing task resources"))
  4169  	} else {
  4170  		if err := t.Resources.MeetsMinResources(); err != nil {
  4171  			mErr.Errors = append(mErr.Errors, err)
  4172  		}
  4173  
  4174  		// Ensure the task isn't asking for disk resources
  4175  		if t.Resources.DiskMB > 0 {
  4176  			mErr.Errors = append(mErr.Errors, errors.New("Task can't ask for disk resources, they have to be specified at the task group level."))
  4177  		}
  4178  	}
  4179  
  4180  	// Validate the log config
  4181  	if t.LogConfig == nil {
  4182  		mErr.Errors = append(mErr.Errors, errors.New("Missing Log Config"))
  4183  	} else if err := t.LogConfig.Validate(); err != nil {
  4184  		mErr.Errors = append(mErr.Errors, err)
  4185  	}
  4186  
  4187  	for idx, constr := range t.Constraints {
  4188  		if err := constr.Validate(); err != nil {
  4189  			outer := fmt.Errorf("Constraint %d validation failed: %s", idx+1, err)
  4190  			mErr.Errors = append(mErr.Errors, outer)
  4191  		}
  4192  
  4193  		switch constr.Operand {
  4194  		case ConstraintDistinctHosts, ConstraintDistinctProperty:
  4195  			outer := fmt.Errorf("Constraint %d has disallowed Operand at task level: %s", idx+1, constr.Operand)
  4196  			mErr.Errors = append(mErr.Errors, outer)
  4197  		}
  4198  	}
  4199  
  4200  	// Validate Services
  4201  	if err := validateServices(t); err != nil {
  4202  		mErr.Errors = append(mErr.Errors, err)
  4203  	}
  4204  
  4205  	if t.LogConfig != nil && ephemeralDisk != nil {
  4206  		logUsage := (t.LogConfig.MaxFiles * t.LogConfig.MaxFileSizeMB)
  4207  		if ephemeralDisk.SizeMB <= logUsage {
  4208  			mErr.Errors = append(mErr.Errors,
  4209  				fmt.Errorf("log storage (%d MB) must be less than requested disk capacity (%d MB)",
  4210  					logUsage, ephemeralDisk.SizeMB))
  4211  		}
  4212  	}
  4213  
  4214  	for idx, artifact := range t.Artifacts {
  4215  		if err := artifact.Validate(); err != nil {
  4216  			outer := fmt.Errorf("Artifact %d validation failed: %v", idx+1, err)
  4217  			mErr.Errors = append(mErr.Errors, outer)
  4218  		}
  4219  	}
  4220  
  4221  	if t.Vault != nil {
  4222  		if err := t.Vault.Validate(); err != nil {
  4223  			mErr.Errors = append(mErr.Errors, fmt.Errorf("Vault validation failed: %v", err))
  4224  		}
  4225  	}
  4226  
  4227  	destinations := make(map[string]int, len(t.Templates))
  4228  	for idx, tmpl := range t.Templates {
  4229  		if err := tmpl.Validate(); err != nil {
  4230  			outer := fmt.Errorf("Template %d validation failed: %s", idx+1, err)
  4231  			mErr.Errors = append(mErr.Errors, outer)
  4232  		}
  4233  
  4234  		if other, ok := destinations[tmpl.DestPath]; ok {
  4235  			outer := fmt.Errorf("Template %d has same destination as %d", idx+1, other)
  4236  			mErr.Errors = append(mErr.Errors, outer)
  4237  		} else {
  4238  			destinations[tmpl.DestPath] = idx + 1
  4239  		}
  4240  	}
  4241  
  4242  	// Validate the dispatch payload block if there
  4243  	if t.DispatchPayload != nil {
  4244  		if err := t.DispatchPayload.Validate(); err != nil {
  4245  			mErr.Errors = append(mErr.Errors, fmt.Errorf("Dispatch Payload validation failed: %v", err))
  4246  		}
  4247  	}
  4248  
  4249  	return mErr.ErrorOrNil()
  4250  }
  4251  
  4252  // validateServices takes a task and validates the services within it are valid
  4253  // and reference ports that exist.
  4254  func validateServices(t *Task) error {
  4255  	var mErr multierror.Error
  4256  
  4257  	// Ensure that services don't ask for nonexistent ports and their names are
  4258  	// unique.
  4259  	servicePorts := make(map[string]map[string]struct{})
  4260  	addServicePort := func(label, service string) {
  4261  		if _, ok := servicePorts[label]; !ok {
  4262  			servicePorts[label] = map[string]struct{}{}
  4263  		}
  4264  		servicePorts[label][service] = struct{}{}
  4265  	}
  4266  	knownServices := make(map[string]struct{})
  4267  	for i, service := range t.Services {
  4268  		if err := service.Validate(); err != nil {
  4269  			outer := fmt.Errorf("service[%d] %+q validation failed: %s", i, service.Name, err)
  4270  			mErr.Errors = append(mErr.Errors, outer)
  4271  		}
  4272  
  4273  		// Ensure that services with the same name are not being registered for
  4274  		// the same port
  4275  		if _, ok := knownServices[service.Name+service.PortLabel]; ok {
  4276  			mErr.Errors = append(mErr.Errors, fmt.Errorf("service %q is duplicate", service.Name))
  4277  		}
  4278  		knownServices[service.Name+service.PortLabel] = struct{}{}
  4279  
  4280  		if service.PortLabel != "" {
  4281  			if service.AddressMode == "driver" {
  4282  				// Numeric port labels are valid for address_mode=driver
  4283  				_, err := strconv.Atoi(service.PortLabel)
  4284  				if err != nil {
  4285  					// Not a numeric port label, add it to list to check
  4286  					addServicePort(service.PortLabel, service.Name)
  4287  				}
  4288  			} else {
  4289  				addServicePort(service.PortLabel, service.Name)
  4290  			}
  4291  		}
  4292  
  4293  		// Ensure that check names are unique and have valid ports
  4294  		knownChecks := make(map[string]struct{})
  4295  		for _, check := range service.Checks {
  4296  			if _, ok := knownChecks[check.Name]; ok {
  4297  				mErr.Errors = append(mErr.Errors, fmt.Errorf("check %q is duplicate", check.Name))
  4298  			}
  4299  			knownChecks[check.Name] = struct{}{}
  4300  
  4301  			if !check.RequiresPort() {
  4302  				// No need to continue validating check if it doesn't need a port
  4303  				continue
  4304  			}
  4305  
  4306  			effectivePort := check.PortLabel
  4307  			if effectivePort == "" {
  4308  				// Inherits from service
  4309  				effectivePort = service.PortLabel
  4310  			}
  4311  
  4312  			if effectivePort == "" {
  4313  				mErr.Errors = append(mErr.Errors, fmt.Errorf("check %q is missing a port", check.Name))
  4314  				continue
  4315  			}
  4316  
  4317  			isNumeric := false
  4318  			portNumber, err := strconv.Atoi(effectivePort)
  4319  			if err == nil {
  4320  				isNumeric = true
  4321  			}
  4322  
  4323  			// Numeric ports are fine for address_mode = "driver"
  4324  			if check.AddressMode == "driver" && isNumeric {
  4325  				if portNumber <= 0 {
  4326  					mErr.Errors = append(mErr.Errors, fmt.Errorf("check %q has invalid numeric port %d", check.Name, portNumber))
  4327  				}
  4328  				continue
  4329  			}
  4330  
  4331  			if isNumeric {
  4332  				mErr.Errors = append(mErr.Errors, fmt.Errorf(`check %q cannot use a numeric port %d without setting address_mode="driver"`, check.Name, portNumber))
  4333  				continue
  4334  			}
  4335  
  4336  			// PortLabel must exist, report errors by its parent service
  4337  			addServicePort(effectivePort, service.Name)
  4338  		}
  4339  	}
  4340  
  4341  	// Get the set of port labels.
  4342  	portLabels := make(map[string]struct{})
  4343  	if t.Resources != nil {
  4344  		for _, network := range t.Resources.Networks {
  4345  			ports := network.PortLabels()
  4346  			for portLabel := range ports {
  4347  				portLabels[portLabel] = struct{}{}
  4348  			}
  4349  		}
  4350  	}
  4351  
  4352  	// Iterate over a sorted list of keys to make error listings stable
  4353  	keys := make([]string, 0, len(servicePorts))
  4354  	for p := range servicePorts {
  4355  		keys = append(keys, p)
  4356  	}
  4357  	sort.Strings(keys)
  4358  
  4359  	// Ensure all ports referenced in services exist.
  4360  	for _, servicePort := range keys {
  4361  		services := servicePorts[servicePort]
  4362  		_, ok := portLabels[servicePort]
  4363  		if !ok {
  4364  			names := make([]string, 0, len(services))
  4365  			for name := range services {
  4366  				names = append(names, name)
  4367  			}
  4368  
  4369  			// Keep order deterministic
  4370  			sort.Strings(names)
  4371  			joined := strings.Join(names, ", ")
  4372  			err := fmt.Errorf("port label %q referenced by services %v does not exist", servicePort, joined)
  4373  			mErr.Errors = append(mErr.Errors, err)
  4374  		}
  4375  	}
  4376  
  4377  	// Ensure address mode is valid
  4378  	return mErr.ErrorOrNil()
  4379  }
  4380  
  4381  const (
  4382  	// TemplateChangeModeNoop marks that no action should be taken if the
  4383  	// template is re-rendered
  4384  	TemplateChangeModeNoop = "noop"
  4385  
  4386  	// TemplateChangeModeSignal marks that the task should be signaled if the
  4387  	// template is re-rendered
  4388  	TemplateChangeModeSignal = "signal"
  4389  
  4390  	// TemplateChangeModeRestart marks that the task should be restarted if the
  4391  	// template is re-rendered
  4392  	TemplateChangeModeRestart = "restart"
  4393  )
  4394  
  4395  var (
  4396  	// TemplateChangeModeInvalidError is the error for when an invalid change
  4397  	// mode is given
  4398  	TemplateChangeModeInvalidError = errors.New("Invalid change mode. Must be one of the following: noop, signal, restart")
  4399  )
  4400  
  4401  // Template represents a template configuration to be rendered for a given task
  4402  type Template struct {
  4403  	// SourcePath is the path to the template to be rendered
  4404  	SourcePath string
  4405  
  4406  	// DestPath is the path to where the template should be rendered
  4407  	DestPath string
  4408  
  4409  	// EmbeddedTmpl store the raw template. This is useful for smaller templates
  4410  	// where they are embedded in the job file rather than sent as an artifact
  4411  	EmbeddedTmpl string
  4412  
  4413  	// ChangeMode indicates what should be done if the template is re-rendered
  4414  	ChangeMode string
  4415  
  4416  	// ChangeSignal is the signal that should be sent if the change mode
  4417  	// requires it.
  4418  	ChangeSignal string
  4419  
  4420  	// Splay is used to avoid coordinated restarts of processes by applying a
  4421  	// random wait between 0 and the given splay value before signalling the
  4422  	// application of a change
  4423  	Splay time.Duration
  4424  
  4425  	// Perms is the permission the file should be written out with.
  4426  	Perms string
  4427  
  4428  	// LeftDelim and RightDelim are optional configurations to control what
  4429  	// delimiter is utilized when parsing the template.
  4430  	LeftDelim  string
  4431  	RightDelim string
  4432  
  4433  	// Envvars enables exposing the template as environment variables
  4434  	// instead of as a file. The template must be of the form:
  4435  	//
  4436  	//	VAR_NAME_1={{ key service/my-key }}
  4437  	//	VAR_NAME_2=raw string and {{ env "attr.kernel.name" }}
  4438  	//
  4439  	// Lines will be split on the initial "=" with the first part being the
  4440  	// key name and the second part the value.
  4441  	// Empty lines and lines starting with # will be ignored, but to avoid
  4442  	// escaping issues #s within lines will not be treated as comments.
  4443  	Envvars bool
  4444  
  4445  	// VaultGrace is the grace duration between lease renewal and reacquiring a
  4446  	// secret. If the lease of a secret is less than the grace, a new secret is
  4447  	// acquired.
  4448  	VaultGrace time.Duration
  4449  }
  4450  
  4451  // DefaultTemplate returns a default template.
  4452  func DefaultTemplate() *Template {
  4453  	return &Template{
  4454  		ChangeMode: TemplateChangeModeRestart,
  4455  		Splay:      5 * time.Second,
  4456  		Perms:      "0644",
  4457  	}
  4458  }
  4459  
  4460  func (t *Template) Copy() *Template {
  4461  	if t == nil {
  4462  		return nil
  4463  	}
  4464  	copy := new(Template)
  4465  	*copy = *t
  4466  	return copy
  4467  }
  4468  
  4469  func (t *Template) Canonicalize() {
  4470  	if t.ChangeSignal != "" {
  4471  		t.ChangeSignal = strings.ToUpper(t.ChangeSignal)
  4472  	}
  4473  }
  4474  
  4475  func (t *Template) Validate() error {
  4476  	var mErr multierror.Error
  4477  
  4478  	// Verify we have something to render
  4479  	if t.SourcePath == "" && t.EmbeddedTmpl == "" {
  4480  		multierror.Append(&mErr, fmt.Errorf("Must specify a source path or have an embedded template"))
  4481  	}
  4482  
  4483  	// Verify we can render somewhere
  4484  	if t.DestPath == "" {
  4485  		multierror.Append(&mErr, fmt.Errorf("Must specify a destination for the template"))
  4486  	}
  4487  
  4488  	// Verify the destination doesn't escape
  4489  	escaped, err := PathEscapesAllocDir("task", t.DestPath)
  4490  	if err != nil {
  4491  		mErr.Errors = append(mErr.Errors, fmt.Errorf("invalid destination path: %v", err))
  4492  	} else if escaped {
  4493  		mErr.Errors = append(mErr.Errors, fmt.Errorf("destination escapes allocation directory"))
  4494  	}
  4495  
  4496  	// Verify a proper change mode
  4497  	switch t.ChangeMode {
  4498  	case TemplateChangeModeNoop, TemplateChangeModeRestart:
  4499  	case TemplateChangeModeSignal:
  4500  		if t.ChangeSignal == "" {
  4501  			multierror.Append(&mErr, fmt.Errorf("Must specify signal value when change mode is signal"))
  4502  		}
  4503  		if t.Envvars {
  4504  			multierror.Append(&mErr, fmt.Errorf("cannot use signals with env var templates"))
  4505  		}
  4506  	default:
  4507  		multierror.Append(&mErr, TemplateChangeModeInvalidError)
  4508  	}
  4509  
  4510  	// Verify the splay is positive
  4511  	if t.Splay < 0 {
  4512  		multierror.Append(&mErr, fmt.Errorf("Must specify positive splay value"))
  4513  	}
  4514  
  4515  	// Verify the permissions
  4516  	if t.Perms != "" {
  4517  		if _, err := strconv.ParseUint(t.Perms, 8, 12); err != nil {
  4518  			multierror.Append(&mErr, fmt.Errorf("Failed to parse %q as octal: %v", t.Perms, err))
  4519  		}
  4520  	}
  4521  
  4522  	if t.VaultGrace.Nanoseconds() < 0 {
  4523  		multierror.Append(&mErr, fmt.Errorf("Vault grace must be greater than zero: %v < 0", t.VaultGrace))
  4524  	}
  4525  
  4526  	return mErr.ErrorOrNil()
  4527  }
  4528  
  4529  // Set of possible states for a task.
  4530  const (
  4531  	TaskStatePending = "pending" // The task is waiting to be run.
  4532  	TaskStateRunning = "running" // The task is currently running.
  4533  	TaskStateDead    = "dead"    // Terminal state of task.
  4534  )
  4535  
  4536  // TaskState tracks the current state of a task and events that caused state
  4537  // transitions.
  4538  type TaskState struct {
  4539  	// The current state of the task.
  4540  	State string
  4541  
  4542  	// Failed marks a task as having failed
  4543  	Failed bool
  4544  
  4545  	// Restarts is the number of times the task has restarted
  4546  	Restarts uint64
  4547  
  4548  	// LastRestart is the time the task last restarted. It is updated each time the
  4549  	// task restarts
  4550  	LastRestart time.Time
  4551  
  4552  	// StartedAt is the time the task is started. It is updated each time the
  4553  	// task starts
  4554  	StartedAt time.Time
  4555  
  4556  	// FinishedAt is the time at which the task transitioned to dead and will
  4557  	// not be started again.
  4558  	FinishedAt time.Time
  4559  
  4560  	// Series of task events that transition the state of the task.
  4561  	Events []*TaskEvent
  4562  }
  4563  
  4564  func (ts *TaskState) Copy() *TaskState {
  4565  	if ts == nil {
  4566  		return nil
  4567  	}
  4568  	copy := new(TaskState)
  4569  	*copy = *ts
  4570  
  4571  	if ts.Events != nil {
  4572  		copy.Events = make([]*TaskEvent, len(ts.Events))
  4573  		for i, e := range ts.Events {
  4574  			copy.Events[i] = e.Copy()
  4575  		}
  4576  	}
  4577  	return copy
  4578  }
  4579  
  4580  // Successful returns whether a task finished successfully. This doesn't really
  4581  // have meaning on a non-batch allocation because a service and system
  4582  // allocation should not finish.
  4583  func (ts *TaskState) Successful() bool {
  4584  	l := len(ts.Events)
  4585  	if ts.State != TaskStateDead || l == 0 {
  4586  		return false
  4587  	}
  4588  
  4589  	e := ts.Events[l-1]
  4590  	if e.Type != TaskTerminated {
  4591  		return false
  4592  	}
  4593  
  4594  	return e.ExitCode == 0
  4595  }
  4596  
  4597  const (
  4598  	// TaskSetupFailure indicates that the task could not be started due to a
  4599  	// a setup failure.
  4600  	TaskSetupFailure = "Setup Failure"
  4601  
  4602  	// TaskDriveFailure indicates that the task could not be started due to a
  4603  	// failure in the driver.
  4604  	TaskDriverFailure = "Driver Failure"
  4605  
  4606  	// TaskReceived signals that the task has been pulled by the client at the
  4607  	// given timestamp.
  4608  	TaskReceived = "Received"
  4609  
  4610  	// TaskFailedValidation indicates the task was invalid and as such was not
  4611  	// run.
  4612  	TaskFailedValidation = "Failed Validation"
  4613  
  4614  	// TaskStarted signals that the task was started and its timestamp can be
  4615  	// used to determine the running length of the task.
  4616  	TaskStarted = "Started"
  4617  
  4618  	// TaskTerminated indicates that the task was started and exited.
  4619  	TaskTerminated = "Terminated"
  4620  
  4621  	// TaskKilling indicates a kill signal has been sent to the task.
  4622  	TaskKilling = "Killing"
  4623  
  4624  	// TaskKilled indicates a user has killed the task.
  4625  	TaskKilled = "Killed"
  4626  
  4627  	// TaskRestarting indicates that task terminated and is being restarted.
  4628  	TaskRestarting = "Restarting"
  4629  
  4630  	// TaskNotRestarting indicates that the task has failed and is not being
  4631  	// restarted because it has exceeded its restart policy.
  4632  	TaskNotRestarting = "Not Restarting"
  4633  
  4634  	// TaskRestartSignal indicates that the task has been signalled to be
  4635  	// restarted
  4636  	TaskRestartSignal = "Restart Signaled"
  4637  
  4638  	// TaskSignaling indicates that the task is being signalled.
  4639  	TaskSignaling = "Signaling"
  4640  
  4641  	// TaskDownloadingArtifacts means the task is downloading the artifacts
  4642  	// specified in the task.
  4643  	TaskDownloadingArtifacts = "Downloading Artifacts"
  4644  
  4645  	// TaskArtifactDownloadFailed indicates that downloading the artifacts
  4646  	// failed.
  4647  	TaskArtifactDownloadFailed = "Failed Artifact Download"
  4648  
  4649  	// TaskBuildingTaskDir indicates that the task directory/chroot is being
  4650  	// built.
  4651  	TaskBuildingTaskDir = "Building Task Directory"
  4652  
  4653  	// TaskSetup indicates the task runner is setting up the task environment
  4654  	TaskSetup = "Task Setup"
  4655  
  4656  	// TaskDiskExceeded indicates that one of the tasks in a taskgroup has
  4657  	// exceeded the requested disk resources.
  4658  	TaskDiskExceeded = "Disk Resources Exceeded"
  4659  
  4660  	// TaskSiblingFailed indicates that a sibling task in the task group has
  4661  	// failed.
  4662  	TaskSiblingFailed = "Sibling Task Failed"
  4663  
  4664  	// TaskDriverMessage is an informational event message emitted by
  4665  	// drivers such as when they're performing a long running action like
  4666  	// downloading an image.
  4667  	TaskDriverMessage = "Driver"
  4668  
  4669  	// TaskLeaderDead indicates that the leader task within the has finished.
  4670  	TaskLeaderDead = "Leader Task Dead"
  4671  )
  4672  
  4673  // TaskEvent is an event that effects the state of a task and contains meta-data
  4674  // appropriate to the events type.
  4675  type TaskEvent struct {
  4676  	Type string
  4677  	Time int64 // Unix Nanosecond timestamp
  4678  
  4679  	Message string // A possible message explaining the termination of the task.
  4680  
  4681  	// DisplayMessage is a human friendly message about the event
  4682  	DisplayMessage string
  4683  
  4684  	// Details is a map with annotated info about the event
  4685  	Details map[string]string
  4686  
  4687  	// DEPRECATION NOTICE: The following fields are deprecated and will be removed
  4688  	// in a future release. Field values are available in the Details map.
  4689  
  4690  	// FailsTask marks whether this event fails the task.
  4691  	// Deprecated, use Details["fails_task"] to access this.
  4692  	FailsTask bool
  4693  
  4694  	// Restart fields.
  4695  	// Deprecated, use Details["restart_reason"] to access this.
  4696  	RestartReason string
  4697  
  4698  	// Setup Failure fields.
  4699  	// Deprecated, use Details["setup_error"] to access this.
  4700  	SetupError string
  4701  
  4702  	// Driver Failure fields.
  4703  	// Deprecated, use Details["driver_error"] to access this.
  4704  	DriverError string // A driver error occurred while starting the task.
  4705  
  4706  	// Task Terminated Fields.
  4707  
  4708  	// Deprecated, use Details["exit_code"] to access this.
  4709  	ExitCode int // The exit code of the task.
  4710  
  4711  	// Deprecated, use Details["signal"] to access this.
  4712  	Signal int // The signal that terminated the task.
  4713  
  4714  	// Killing fields
  4715  	// Deprecated, use Details["kill_timeout"] to access this.
  4716  	KillTimeout time.Duration
  4717  
  4718  	// Task Killed Fields.
  4719  	// Deprecated, use Details["kill_error"] to access this.
  4720  	KillError string // Error killing the task.
  4721  
  4722  	// KillReason is the reason the task was killed
  4723  	// Deprecated, use Details["kill_reason"] to access this.
  4724  	KillReason string
  4725  
  4726  	// TaskRestarting fields.
  4727  	// Deprecated, use Details["start_delay"] to access this.
  4728  	StartDelay int64 // The sleep period before restarting the task in unix nanoseconds.
  4729  
  4730  	// Artifact Download fields
  4731  	// Deprecated, use Details["download_error"] to access this.
  4732  	DownloadError string // Error downloading artifacts
  4733  
  4734  	// Validation fields
  4735  	// Deprecated, use Details["validation_error"] to access this.
  4736  	ValidationError string // Validation error
  4737  
  4738  	// The maximum allowed task disk size.
  4739  	// Deprecated, use Details["disk_limit"] to access this.
  4740  	DiskLimit int64
  4741  
  4742  	// Name of the sibling task that caused termination of the task that
  4743  	// the TaskEvent refers to.
  4744  	// Deprecated, use Details["failed_sibling"] to access this.
  4745  	FailedSibling string
  4746  
  4747  	// VaultError is the error from token renewal
  4748  	// Deprecated, use Details["vault_renewal_error"] to access this.
  4749  	VaultError string
  4750  
  4751  	// TaskSignalReason indicates the reason the task is being signalled.
  4752  	// Deprecated, use Details["task_signal_reason"] to access this.
  4753  	TaskSignalReason string
  4754  
  4755  	// TaskSignal is the signal that was sent to the task
  4756  	// Deprecated, use Details["task_signal"] to access this.
  4757  	TaskSignal string
  4758  
  4759  	// DriverMessage indicates a driver action being taken.
  4760  	// Deprecated, use Details["driver_message"] to access this.
  4761  	DriverMessage string
  4762  
  4763  	// GenericSource is the source of a message.
  4764  	// Deprecated, is redundant with event type.
  4765  	GenericSource string
  4766  }
  4767  
  4768  func (event *TaskEvent) PopulateEventDisplayMessage() {
  4769  	// Build up the description based on the event type.
  4770  	if event == nil { //TODO(preetha) needs investigation alloc_runner's Run method sends a nil event when sigterming nomad. Why?
  4771  		return
  4772  	}
  4773  
  4774  	if event.DisplayMessage != "" {
  4775  		return
  4776  	}
  4777  
  4778  	var desc string
  4779  	switch event.Type {
  4780  	case TaskSetup:
  4781  		desc = event.Message
  4782  	case TaskStarted:
  4783  		desc = "Task started by client"
  4784  	case TaskReceived:
  4785  		desc = "Task received by client"
  4786  	case TaskFailedValidation:
  4787  		if event.ValidationError != "" {
  4788  			desc = event.ValidationError
  4789  		} else {
  4790  			desc = "Validation of task failed"
  4791  		}
  4792  	case TaskSetupFailure:
  4793  		if event.SetupError != "" {
  4794  			desc = event.SetupError
  4795  		} else {
  4796  			desc = "Task setup failed"
  4797  		}
  4798  	case TaskDriverFailure:
  4799  		if event.DriverError != "" {
  4800  			desc = event.DriverError
  4801  		} else {
  4802  			desc = "Failed to start task"
  4803  		}
  4804  	case TaskDownloadingArtifacts:
  4805  		desc = "Client is downloading artifacts"
  4806  	case TaskArtifactDownloadFailed:
  4807  		if event.DownloadError != "" {
  4808  			desc = event.DownloadError
  4809  		} else {
  4810  			desc = "Failed to download artifacts"
  4811  		}
  4812  	case TaskKilling:
  4813  		if event.KillReason != "" {
  4814  			desc = event.KillReason
  4815  		} else if event.KillTimeout != 0 {
  4816  			desc = fmt.Sprintf("Sent interrupt. Waiting %v before force killing", event.KillTimeout)
  4817  		} else {
  4818  			desc = "Sent interrupt"
  4819  		}
  4820  	case TaskKilled:
  4821  		if event.KillError != "" {
  4822  			desc = event.KillError
  4823  		} else {
  4824  			desc = "Task successfully killed"
  4825  		}
  4826  	case TaskTerminated:
  4827  		var parts []string
  4828  		parts = append(parts, fmt.Sprintf("Exit Code: %d", event.ExitCode))
  4829  
  4830  		if event.Signal != 0 {
  4831  			parts = append(parts, fmt.Sprintf("Signal: %d", event.Signal))
  4832  		}
  4833  
  4834  		if event.Message != "" {
  4835  			parts = append(parts, fmt.Sprintf("Exit Message: %q", event.Message))
  4836  		}
  4837  		desc = strings.Join(parts, ", ")
  4838  	case TaskRestarting:
  4839  		in := fmt.Sprintf("Task restarting in %v", time.Duration(event.StartDelay))
  4840  		if event.RestartReason != "" && event.RestartReason != ReasonWithinPolicy {
  4841  			desc = fmt.Sprintf("%s - %s", event.RestartReason, in)
  4842  		} else {
  4843  			desc = in
  4844  		}
  4845  	case TaskNotRestarting:
  4846  		if event.RestartReason != "" {
  4847  			desc = event.RestartReason
  4848  		} else {
  4849  			desc = "Task exceeded restart policy"
  4850  		}
  4851  	case TaskSiblingFailed:
  4852  		if event.FailedSibling != "" {
  4853  			desc = fmt.Sprintf("Task's sibling %q failed", event.FailedSibling)
  4854  		} else {
  4855  			desc = "Task's sibling failed"
  4856  		}
  4857  	case TaskSignaling:
  4858  		sig := event.TaskSignal
  4859  		reason := event.TaskSignalReason
  4860  
  4861  		if sig == "" && reason == "" {
  4862  			desc = "Task being sent a signal"
  4863  		} else if sig == "" {
  4864  			desc = reason
  4865  		} else if reason == "" {
  4866  			desc = fmt.Sprintf("Task being sent signal %v", sig)
  4867  		} else {
  4868  			desc = fmt.Sprintf("Task being sent signal %v: %v", sig, reason)
  4869  		}
  4870  	case TaskRestartSignal:
  4871  		if event.RestartReason != "" {
  4872  			desc = event.RestartReason
  4873  		} else {
  4874  			desc = "Task signaled to restart"
  4875  		}
  4876  	case TaskDriverMessage:
  4877  		desc = event.DriverMessage
  4878  	case TaskLeaderDead:
  4879  		desc = "Leader Task in Group dead"
  4880  	default:
  4881  		desc = event.Message
  4882  	}
  4883  
  4884  	event.DisplayMessage = desc
  4885  }
  4886  
  4887  func (te *TaskEvent) GoString() string {
  4888  	return fmt.Sprintf("%v - %v", te.Time, te.Type)
  4889  }
  4890  
  4891  // SetMessage sets the message of TaskEvent
  4892  func (te *TaskEvent) SetMessage(msg string) *TaskEvent {
  4893  	te.Message = msg
  4894  	te.Details["message"] = msg
  4895  	return te
  4896  }
  4897  
  4898  func (te *TaskEvent) Copy() *TaskEvent {
  4899  	if te == nil {
  4900  		return nil
  4901  	}
  4902  	copy := new(TaskEvent)
  4903  	*copy = *te
  4904  	return copy
  4905  }
  4906  
  4907  func NewTaskEvent(event string) *TaskEvent {
  4908  	return &TaskEvent{
  4909  		Type:    event,
  4910  		Time:    time.Now().UnixNano(),
  4911  		Details: make(map[string]string),
  4912  	}
  4913  }
  4914  
  4915  // SetSetupError is used to store an error that occurred while setting up the
  4916  // task
  4917  func (e *TaskEvent) SetSetupError(err error) *TaskEvent {
  4918  	if err != nil {
  4919  		e.SetupError = err.Error()
  4920  		e.Details["setup_error"] = err.Error()
  4921  	}
  4922  	return e
  4923  }
  4924  
  4925  func (e *TaskEvent) SetFailsTask() *TaskEvent {
  4926  	e.FailsTask = true
  4927  	e.Details["fails_task"] = "true"
  4928  	return e
  4929  }
  4930  
  4931  func (e *TaskEvent) SetDriverError(err error) *TaskEvent {
  4932  	if err != nil {
  4933  		e.DriverError = err.Error()
  4934  		e.Details["driver_error"] = err.Error()
  4935  	}
  4936  	return e
  4937  }
  4938  
  4939  func (e *TaskEvent) SetExitCode(c int) *TaskEvent {
  4940  	e.ExitCode = c
  4941  	e.Details["exit_code"] = fmt.Sprintf("%d", c)
  4942  	return e
  4943  }
  4944  
  4945  func (e *TaskEvent) SetSignal(s int) *TaskEvent {
  4946  	e.Signal = s
  4947  	e.Details["signal"] = fmt.Sprintf("%d", s)
  4948  	return e
  4949  }
  4950  
  4951  func (e *TaskEvent) SetExitMessage(err error) *TaskEvent {
  4952  	if err != nil {
  4953  		e.Message = err.Error()
  4954  		e.Details["exit_message"] = err.Error()
  4955  	}
  4956  	return e
  4957  }
  4958  
  4959  func (e *TaskEvent) SetKillError(err error) *TaskEvent {
  4960  	if err != nil {
  4961  		e.KillError = err.Error()
  4962  		e.Details["kill_error"] = err.Error()
  4963  	}
  4964  	return e
  4965  }
  4966  
  4967  func (e *TaskEvent) SetKillReason(r string) *TaskEvent {
  4968  	e.KillReason = r
  4969  	e.Details["kill_reason"] = r
  4970  	return e
  4971  }
  4972  
  4973  func (e *TaskEvent) SetRestartDelay(delay time.Duration) *TaskEvent {
  4974  	e.StartDelay = int64(delay)
  4975  	e.Details["start_delay"] = fmt.Sprintf("%d", delay)
  4976  	return e
  4977  }
  4978  
  4979  func (e *TaskEvent) SetRestartReason(reason string) *TaskEvent {
  4980  	e.RestartReason = reason
  4981  	e.Details["restart_reason"] = reason
  4982  	return e
  4983  }
  4984  
  4985  func (e *TaskEvent) SetTaskSignalReason(r string) *TaskEvent {
  4986  	e.TaskSignalReason = r
  4987  	e.Details["task_signal_reason"] = r
  4988  	return e
  4989  }
  4990  
  4991  func (e *TaskEvent) SetTaskSignal(s os.Signal) *TaskEvent {
  4992  	e.TaskSignal = s.String()
  4993  	e.Details["task_signal"] = s.String()
  4994  	return e
  4995  }
  4996  
  4997  func (e *TaskEvent) SetDownloadError(err error) *TaskEvent {
  4998  	if err != nil {
  4999  		e.DownloadError = err.Error()
  5000  		e.Details["download_error"] = err.Error()
  5001  	}
  5002  	return e
  5003  }
  5004  
  5005  func (e *TaskEvent) SetValidationError(err error) *TaskEvent {
  5006  	if err != nil {
  5007  		e.ValidationError = err.Error()
  5008  		e.Details["validation_error"] = err.Error()
  5009  	}
  5010  	return e
  5011  }
  5012  
  5013  func (e *TaskEvent) SetKillTimeout(timeout time.Duration) *TaskEvent {
  5014  	e.KillTimeout = timeout
  5015  	e.Details["kill_timeout"] = timeout.String()
  5016  	return e
  5017  }
  5018  
  5019  func (e *TaskEvent) SetDiskLimit(limit int64) *TaskEvent {
  5020  	e.DiskLimit = limit
  5021  	e.Details["disk_limit"] = fmt.Sprintf("%d", limit)
  5022  	return e
  5023  }
  5024  
  5025  func (e *TaskEvent) SetFailedSibling(sibling string) *TaskEvent {
  5026  	e.FailedSibling = sibling
  5027  	e.Details["failed_sibling"] = sibling
  5028  	return e
  5029  }
  5030  
  5031  func (e *TaskEvent) SetVaultRenewalError(err error) *TaskEvent {
  5032  	if err != nil {
  5033  		e.VaultError = err.Error()
  5034  		e.Details["vault_renewal_error"] = err.Error()
  5035  	}
  5036  	return e
  5037  }
  5038  
  5039  func (e *TaskEvent) SetDriverMessage(m string) *TaskEvent {
  5040  	e.DriverMessage = m
  5041  	e.Details["driver_message"] = m
  5042  	return e
  5043  }
  5044  
  5045  // TaskArtifact is an artifact to download before running the task.
  5046  type TaskArtifact struct {
  5047  	// GetterSource is the source to download an artifact using go-getter
  5048  	GetterSource string
  5049  
  5050  	// GetterOptions are options to use when downloading the artifact using
  5051  	// go-getter.
  5052  	GetterOptions map[string]string
  5053  
  5054  	// GetterMode is the go-getter.ClientMode for fetching resources.
  5055  	// Defaults to "any" but can be set to "file" or "dir".
  5056  	GetterMode string
  5057  
  5058  	// RelativeDest is the download destination given relative to the task's
  5059  	// directory.
  5060  	RelativeDest string
  5061  }
  5062  
  5063  func (ta *TaskArtifact) Copy() *TaskArtifact {
  5064  	if ta == nil {
  5065  		return nil
  5066  	}
  5067  	nta := new(TaskArtifact)
  5068  	*nta = *ta
  5069  	nta.GetterOptions = helper.CopyMapStringString(ta.GetterOptions)
  5070  	return nta
  5071  }
  5072  
  5073  func (ta *TaskArtifact) GoString() string {
  5074  	return fmt.Sprintf("%+v", ta)
  5075  }
  5076  
  5077  // PathEscapesAllocDir returns if the given path escapes the allocation
  5078  // directory. The prefix allows adding a prefix if the path will be joined, for
  5079  // example a "task/local" prefix may be provided if the path will be joined
  5080  // against that prefix.
  5081  func PathEscapesAllocDir(prefix, path string) (bool, error) {
  5082  	// Verify the destination doesn't escape the tasks directory
  5083  	alloc, err := filepath.Abs(filepath.Join("/", "alloc-dir/", "alloc-id/"))
  5084  	if err != nil {
  5085  		return false, err
  5086  	}
  5087  	abs, err := filepath.Abs(filepath.Join(alloc, prefix, path))
  5088  	if err != nil {
  5089  		return false, err
  5090  	}
  5091  	rel, err := filepath.Rel(alloc, abs)
  5092  	if err != nil {
  5093  		return false, err
  5094  	}
  5095  
  5096  	return strings.HasPrefix(rel, ".."), nil
  5097  }
  5098  
  5099  func (ta *TaskArtifact) Validate() error {
  5100  	// Verify the source
  5101  	var mErr multierror.Error
  5102  	if ta.GetterSource == "" {
  5103  		mErr.Errors = append(mErr.Errors, fmt.Errorf("source must be specified"))
  5104  	}
  5105  
  5106  	switch ta.GetterMode {
  5107  	case "":
  5108  		// Default to any
  5109  		ta.GetterMode = GetterModeAny
  5110  	case GetterModeAny, GetterModeFile, GetterModeDir:
  5111  		// Ok
  5112  	default:
  5113  		mErr.Errors = append(mErr.Errors, fmt.Errorf("invalid artifact mode %q; must be one of: %s, %s, %s",
  5114  			ta.GetterMode, GetterModeAny, GetterModeFile, GetterModeDir))
  5115  	}
  5116  
  5117  	escaped, err := PathEscapesAllocDir("task", ta.RelativeDest)
  5118  	if err != nil {
  5119  		mErr.Errors = append(mErr.Errors, fmt.Errorf("invalid destination path: %v", err))
  5120  	} else if escaped {
  5121  		mErr.Errors = append(mErr.Errors, fmt.Errorf("destination escapes allocation directory"))
  5122  	}
  5123  
  5124  	// Verify the checksum
  5125  	if check, ok := ta.GetterOptions["checksum"]; ok {
  5126  		check = strings.TrimSpace(check)
  5127  		if check == "" {
  5128  			mErr.Errors = append(mErr.Errors, fmt.Errorf("checksum value cannot be empty"))
  5129  			return mErr.ErrorOrNil()
  5130  		}
  5131  
  5132  		parts := strings.Split(check, ":")
  5133  		if l := len(parts); l != 2 {
  5134  			mErr.Errors = append(mErr.Errors, fmt.Errorf(`checksum must be given as "type:value"; got %q`, check))
  5135  			return mErr.ErrorOrNil()
  5136  		}
  5137  
  5138  		checksumVal := parts[1]
  5139  		checksumBytes, err := hex.DecodeString(checksumVal)
  5140  		if err != nil {
  5141  			mErr.Errors = append(mErr.Errors, fmt.Errorf("invalid checksum: %v", err))
  5142  			return mErr.ErrorOrNil()
  5143  		}
  5144  
  5145  		checksumType := parts[0]
  5146  		expectedLength := 0
  5147  		switch checksumType {
  5148  		case "md5":
  5149  			expectedLength = md5.Size
  5150  		case "sha1":
  5151  			expectedLength = sha1.Size
  5152  		case "sha256":
  5153  			expectedLength = sha256.Size
  5154  		case "sha512":
  5155  			expectedLength = sha512.Size
  5156  		default:
  5157  			mErr.Errors = append(mErr.Errors, fmt.Errorf("unsupported checksum type: %s", checksumType))
  5158  			return mErr.ErrorOrNil()
  5159  		}
  5160  
  5161  		if len(checksumBytes) != expectedLength {
  5162  			mErr.Errors = append(mErr.Errors, fmt.Errorf("invalid %s checksum: %v", checksumType, checksumVal))
  5163  			return mErr.ErrorOrNil()
  5164  		}
  5165  	}
  5166  
  5167  	return mErr.ErrorOrNil()
  5168  }
  5169  
  5170  const (
  5171  	ConstraintDistinctProperty = "distinct_property"
  5172  	ConstraintDistinctHosts    = "distinct_hosts"
  5173  	ConstraintRegex            = "regexp"
  5174  	ConstraintVersion          = "version"
  5175  	ConstraintSetContains      = "set_contains"
  5176  )
  5177  
  5178  // Constraints are used to restrict placement options.
  5179  type Constraint struct {
  5180  	LTarget string // Left-hand target
  5181  	RTarget string // Right-hand target
  5182  	Operand string // Constraint operand (<=, <, =, !=, >, >=), contains, near
  5183  	str     string // Memoized string
  5184  }
  5185  
  5186  // Equal checks if two constraints are equal
  5187  func (c *Constraint) Equal(o *Constraint) bool {
  5188  	return c.LTarget == o.LTarget &&
  5189  		c.RTarget == o.RTarget &&
  5190  		c.Operand == o.Operand
  5191  }
  5192  
  5193  func (c *Constraint) Copy() *Constraint {
  5194  	if c == nil {
  5195  		return nil
  5196  	}
  5197  	nc := new(Constraint)
  5198  	*nc = *c
  5199  	return nc
  5200  }
  5201  
  5202  func (c *Constraint) String() string {
  5203  	if c.str != "" {
  5204  		return c.str
  5205  	}
  5206  	c.str = fmt.Sprintf("%s %s %s", c.LTarget, c.Operand, c.RTarget)
  5207  	return c.str
  5208  }
  5209  
  5210  func (c *Constraint) Validate() error {
  5211  	var mErr multierror.Error
  5212  	if c.Operand == "" {
  5213  		mErr.Errors = append(mErr.Errors, errors.New("Missing constraint operand"))
  5214  	}
  5215  
  5216  	// requireLtarget specifies whether the constraint requires an LTarget to be
  5217  	// provided.
  5218  	requireLtarget := true
  5219  
  5220  	// Perform additional validation based on operand
  5221  	switch c.Operand {
  5222  	case ConstraintDistinctHosts:
  5223  		requireLtarget = false
  5224  	case ConstraintSetContains:
  5225  		if c.RTarget == "" {
  5226  			mErr.Errors = append(mErr.Errors, fmt.Errorf("Set contains constraint requires an RTarget"))
  5227  		}
  5228  	case ConstraintRegex:
  5229  		if _, err := regexp.Compile(c.RTarget); err != nil {
  5230  			mErr.Errors = append(mErr.Errors, fmt.Errorf("Regular expression failed to compile: %v", err))
  5231  		}
  5232  	case ConstraintVersion:
  5233  		if _, err := version.NewConstraint(c.RTarget); err != nil {
  5234  			mErr.Errors = append(mErr.Errors, fmt.Errorf("Version constraint is invalid: %v", err))
  5235  		}
  5236  	case ConstraintDistinctProperty:
  5237  		// If a count is set, make sure it is convertible to a uint64
  5238  		if c.RTarget != "" {
  5239  			count, err := strconv.ParseUint(c.RTarget, 10, 64)
  5240  			if err != nil {
  5241  				mErr.Errors = append(mErr.Errors, fmt.Errorf("Failed to convert RTarget %q to uint64: %v", c.RTarget, err))
  5242  			} else if count < 1 {
  5243  				mErr.Errors = append(mErr.Errors, fmt.Errorf("Distinct Property must have an allowed count of 1 or greater: %d < 1", count))
  5244  			}
  5245  		}
  5246  	case "=", "==", "is", "!=", "not", "<", "<=", ">", ">=":
  5247  		if c.RTarget == "" {
  5248  			mErr.Errors = append(mErr.Errors, fmt.Errorf("Operator %q requires an RTarget", c.Operand))
  5249  		}
  5250  	default:
  5251  		mErr.Errors = append(mErr.Errors, fmt.Errorf("Unknown constraint type %q", c.Operand))
  5252  	}
  5253  
  5254  	// Ensure we have an LTarget for the constraints that need one
  5255  	if requireLtarget && c.LTarget == "" {
  5256  		mErr.Errors = append(mErr.Errors, fmt.Errorf("No LTarget provided but is required by constraint"))
  5257  	}
  5258  
  5259  	return mErr.ErrorOrNil()
  5260  }
  5261  
  5262  // EphemeralDisk is an ephemeral disk object
  5263  type EphemeralDisk struct {
  5264  	// Sticky indicates whether the allocation is sticky to a node
  5265  	Sticky bool
  5266  
  5267  	// SizeMB is the size of the local disk
  5268  	SizeMB int
  5269  
  5270  	// Migrate determines if Nomad client should migrate the allocation dir for
  5271  	// sticky allocations
  5272  	Migrate bool
  5273  }
  5274  
  5275  // DefaultEphemeralDisk returns a EphemeralDisk with default configurations
  5276  func DefaultEphemeralDisk() *EphemeralDisk {
  5277  	return &EphemeralDisk{
  5278  		SizeMB: 300,
  5279  	}
  5280  }
  5281  
  5282  // Validate validates EphemeralDisk
  5283  func (d *EphemeralDisk) Validate() error {
  5284  	if d.SizeMB < 10 {
  5285  		return fmt.Errorf("minimum DiskMB value is 10; got %d", d.SizeMB)
  5286  	}
  5287  	return nil
  5288  }
  5289  
  5290  // Copy copies the EphemeralDisk struct and returns a new one
  5291  func (d *EphemeralDisk) Copy() *EphemeralDisk {
  5292  	ld := new(EphemeralDisk)
  5293  	*ld = *d
  5294  	return ld
  5295  }
  5296  
  5297  var (
  5298  	// VaultUnrecoverableError matches unrecoverable errors returned by a Vault
  5299  	// server
  5300  	VaultUnrecoverableError = regexp.MustCompile(`Code:\s+40(0|3|4)`)
  5301  )
  5302  
  5303  const (
  5304  	// VaultChangeModeNoop takes no action when a new token is retrieved.
  5305  	VaultChangeModeNoop = "noop"
  5306  
  5307  	// VaultChangeModeSignal signals the task when a new token is retrieved.
  5308  	VaultChangeModeSignal = "signal"
  5309  
  5310  	// VaultChangeModeRestart restarts the task when a new token is retrieved.
  5311  	VaultChangeModeRestart = "restart"
  5312  )
  5313  
  5314  // Vault stores the set of permissions a task needs access to from Vault.
  5315  type Vault struct {
  5316  	// Policies is the set of policies that the task needs access to
  5317  	Policies []string
  5318  
  5319  	// Env marks whether the Vault Token should be exposed as an environment
  5320  	// variable
  5321  	Env bool
  5322  
  5323  	// ChangeMode is used to configure the task's behavior when the Vault
  5324  	// token changes because the original token could not be renewed in time.
  5325  	ChangeMode string
  5326  
  5327  	// ChangeSignal is the signal sent to the task when a new token is
  5328  	// retrieved. This is only valid when using the signal change mode.
  5329  	ChangeSignal string
  5330  }
  5331  
  5332  func DefaultVaultBlock() *Vault {
  5333  	return &Vault{
  5334  		Env:        true,
  5335  		ChangeMode: VaultChangeModeRestart,
  5336  	}
  5337  }
  5338  
  5339  // Copy returns a copy of this Vault block.
  5340  func (v *Vault) Copy() *Vault {
  5341  	if v == nil {
  5342  		return nil
  5343  	}
  5344  
  5345  	nv := new(Vault)
  5346  	*nv = *v
  5347  	return nv
  5348  }
  5349  
  5350  func (v *Vault) Canonicalize() {
  5351  	if v.ChangeSignal != "" {
  5352  		v.ChangeSignal = strings.ToUpper(v.ChangeSignal)
  5353  	}
  5354  }
  5355  
  5356  // Validate returns if the Vault block is valid.
  5357  func (v *Vault) Validate() error {
  5358  	if v == nil {
  5359  		return nil
  5360  	}
  5361  
  5362  	var mErr multierror.Error
  5363  	if len(v.Policies) == 0 {
  5364  		multierror.Append(&mErr, fmt.Errorf("Policy list cannot be empty"))
  5365  	}
  5366  
  5367  	for _, p := range v.Policies {
  5368  		if p == "root" {
  5369  			multierror.Append(&mErr, fmt.Errorf("Can not specify \"root\" policy"))
  5370  		}
  5371  	}
  5372  
  5373  	switch v.ChangeMode {
  5374  	case VaultChangeModeSignal:
  5375  		if v.ChangeSignal == "" {
  5376  			multierror.Append(&mErr, fmt.Errorf("Signal must be specified when using change mode %q", VaultChangeModeSignal))
  5377  		}
  5378  	case VaultChangeModeNoop, VaultChangeModeRestart:
  5379  	default:
  5380  		multierror.Append(&mErr, fmt.Errorf("Unknown change mode %q", v.ChangeMode))
  5381  	}
  5382  
  5383  	return mErr.ErrorOrNil()
  5384  }
  5385  
  5386  const (
  5387  	// DeploymentStatuses are the various states a deployment can be be in
  5388  	DeploymentStatusRunning    = "running"
  5389  	DeploymentStatusPaused     = "paused"
  5390  	DeploymentStatusFailed     = "failed"
  5391  	DeploymentStatusSuccessful = "successful"
  5392  	DeploymentStatusCancelled  = "cancelled"
  5393  
  5394  	// DeploymentStatusDescriptions are the various descriptions of the states a
  5395  	// deployment can be in.
  5396  	DeploymentStatusDescriptionRunning               = "Deployment is running"
  5397  	DeploymentStatusDescriptionRunningNeedsPromotion = "Deployment is running but requires promotion"
  5398  	DeploymentStatusDescriptionPaused                = "Deployment is paused"
  5399  	DeploymentStatusDescriptionSuccessful            = "Deployment completed successfully"
  5400  	DeploymentStatusDescriptionStoppedJob            = "Cancelled because job is stopped"
  5401  	DeploymentStatusDescriptionNewerJob              = "Cancelled due to newer version of job"
  5402  	DeploymentStatusDescriptionFailedAllocations     = "Failed due to unhealthy allocations"
  5403  	DeploymentStatusDescriptionProgressDeadline      = "Failed due to progress deadline"
  5404  	DeploymentStatusDescriptionFailedByUser          = "Deployment marked as failed"
  5405  )
  5406  
  5407  // DeploymentStatusDescriptionRollback is used to get the status description of
  5408  // a deployment when rolling back to an older job.
  5409  func DeploymentStatusDescriptionRollback(baseDescription string, jobVersion uint64) string {
  5410  	return fmt.Sprintf("%s - rolling back to job version %d", baseDescription, jobVersion)
  5411  }
  5412  
  5413  // DeploymentStatusDescriptionRollbackNoop is used to get the status description of
  5414  // a deployment when rolling back is not possible because it has the same specification
  5415  func DeploymentStatusDescriptionRollbackNoop(baseDescription string, jobVersion uint64) string {
  5416  	return fmt.Sprintf("%s - not rolling back to stable job version %d as current job has same specification", baseDescription, jobVersion)
  5417  }
  5418  
  5419  // DeploymentStatusDescriptionNoRollbackTarget is used to get the status description of
  5420  // a deployment when there is no target to rollback to but autorevert is desired.
  5421  func DeploymentStatusDescriptionNoRollbackTarget(baseDescription string) string {
  5422  	return fmt.Sprintf("%s - no stable job version to auto revert to", baseDescription)
  5423  }
  5424  
  5425  // Deployment is the object that represents a job deployment which is used to
  5426  // transition a job between versions.
  5427  type Deployment struct {
  5428  	// ID is a generated UUID for the deployment
  5429  	ID string
  5430  
  5431  	// Namespace is the namespace the deployment is created in
  5432  	Namespace string
  5433  
  5434  	// JobID is the job the deployment is created for
  5435  	JobID string
  5436  
  5437  	// JobVersion is the version of the job at which the deployment is tracking
  5438  	JobVersion uint64
  5439  
  5440  	// JobModifyIndex is the ModifyIndex of the job which the deployment is
  5441  	// tracking.
  5442  	JobModifyIndex uint64
  5443  
  5444  	// JobSpecModifyIndex is the JobModifyIndex of the job which the
  5445  	// deployment is tracking.
  5446  	JobSpecModifyIndex uint64
  5447  
  5448  	// JobCreateIndex is the create index of the job which the deployment is
  5449  	// tracking. It is needed so that if the job gets stopped and reran we can
  5450  	// present the correct list of deployments for the job and not old ones.
  5451  	JobCreateIndex uint64
  5452  
  5453  	// TaskGroups is the set of task groups effected by the deployment and their
  5454  	// current deployment status.
  5455  	TaskGroups map[string]*DeploymentState
  5456  
  5457  	// The status of the deployment
  5458  	Status string
  5459  
  5460  	// StatusDescription allows a human readable description of the deployment
  5461  	// status.
  5462  	StatusDescription string
  5463  
  5464  	CreateIndex uint64
  5465  	ModifyIndex uint64
  5466  }
  5467  
  5468  // NewDeployment creates a new deployment given the job.
  5469  func NewDeployment(job *Job) *Deployment {
  5470  	return &Deployment{
  5471  		ID:                 uuid.Generate(),
  5472  		Namespace:          job.Namespace,
  5473  		JobID:              job.ID,
  5474  		JobVersion:         job.Version,
  5475  		JobModifyIndex:     job.ModifyIndex,
  5476  		JobSpecModifyIndex: job.JobModifyIndex,
  5477  		JobCreateIndex:     job.CreateIndex,
  5478  		Status:             DeploymentStatusRunning,
  5479  		StatusDescription:  DeploymentStatusDescriptionRunning,
  5480  		TaskGroups:         make(map[string]*DeploymentState, len(job.TaskGroups)),
  5481  	}
  5482  }
  5483  
  5484  func (d *Deployment) Copy() *Deployment {
  5485  	if d == nil {
  5486  		return nil
  5487  	}
  5488  
  5489  	c := &Deployment{}
  5490  	*c = *d
  5491  
  5492  	c.TaskGroups = nil
  5493  	if l := len(d.TaskGroups); d.TaskGroups != nil {
  5494  		c.TaskGroups = make(map[string]*DeploymentState, l)
  5495  		for tg, s := range d.TaskGroups {
  5496  			c.TaskGroups[tg] = s.Copy()
  5497  		}
  5498  	}
  5499  
  5500  	return c
  5501  }
  5502  
  5503  // Active returns whether the deployment is active or terminal.
  5504  func (d *Deployment) Active() bool {
  5505  	switch d.Status {
  5506  	case DeploymentStatusRunning, DeploymentStatusPaused:
  5507  		return true
  5508  	default:
  5509  		return false
  5510  	}
  5511  }
  5512  
  5513  // GetID is a helper for getting the ID when the object may be nil
  5514  func (d *Deployment) GetID() string {
  5515  	if d == nil {
  5516  		return ""
  5517  	}
  5518  	return d.ID
  5519  }
  5520  
  5521  // HasPlacedCanaries returns whether the deployment has placed canaries
  5522  func (d *Deployment) HasPlacedCanaries() bool {
  5523  	if d == nil || len(d.TaskGroups) == 0 {
  5524  		return false
  5525  	}
  5526  	for _, group := range d.TaskGroups {
  5527  		if len(group.PlacedCanaries) != 0 {
  5528  			return true
  5529  		}
  5530  	}
  5531  	return false
  5532  }
  5533  
  5534  // RequiresPromotion returns whether the deployment requires promotion to
  5535  // continue
  5536  func (d *Deployment) RequiresPromotion() bool {
  5537  	if d == nil || len(d.TaskGroups) == 0 || d.Status != DeploymentStatusRunning {
  5538  		return false
  5539  	}
  5540  	for _, group := range d.TaskGroups {
  5541  		if group.DesiredCanaries > 0 && !group.Promoted {
  5542  			return true
  5543  		}
  5544  	}
  5545  	return false
  5546  }
  5547  
  5548  func (d *Deployment) GoString() string {
  5549  	base := fmt.Sprintf("Deployment ID %q for job %q has status %q (%v):", d.ID, d.JobID, d.Status, d.StatusDescription)
  5550  	for group, state := range d.TaskGroups {
  5551  		base += fmt.Sprintf("\nTask Group %q has state:\n%#v", group, state)
  5552  	}
  5553  	return base
  5554  }
  5555  
  5556  // DeploymentState tracks the state of a deployment for a given task group.
  5557  type DeploymentState struct {
  5558  	// AutoRevert marks whether the task group has indicated the job should be
  5559  	// reverted on failure
  5560  	AutoRevert bool
  5561  
  5562  	// ProgressDeadline is the deadline by which an allocation must transition
  5563  	// to healthy before the deployment is considered failed.
  5564  	ProgressDeadline time.Duration
  5565  
  5566  	// RequireProgressBy is the time by which an allocation must transition
  5567  	// to healthy before the deployment is considered failed.
  5568  	RequireProgressBy time.Time
  5569  
  5570  	// Promoted marks whether the canaries have been promoted
  5571  	Promoted bool
  5572  
  5573  	// PlacedCanaries is the set of placed canary allocations
  5574  	PlacedCanaries []string
  5575  
  5576  	// DesiredCanaries is the number of canaries that should be created.
  5577  	DesiredCanaries int
  5578  
  5579  	// DesiredTotal is the total number of allocations that should be created as
  5580  	// part of the deployment.
  5581  	DesiredTotal int
  5582  
  5583  	// PlacedAllocs is the number of allocations that have been placed
  5584  	PlacedAllocs int
  5585  
  5586  	// HealthyAllocs is the number of allocations that have been marked healthy.
  5587  	HealthyAllocs int
  5588  
  5589  	// UnhealthyAllocs are allocations that have been marked as unhealthy.
  5590  	UnhealthyAllocs int
  5591  }
  5592  
  5593  func (d *DeploymentState) GoString() string {
  5594  	base := fmt.Sprintf("\tDesired Total: %d", d.DesiredTotal)
  5595  	base += fmt.Sprintf("\n\tDesired Canaries: %d", d.DesiredCanaries)
  5596  	base += fmt.Sprintf("\n\tPlaced Canaries: %#v", d.PlacedCanaries)
  5597  	base += fmt.Sprintf("\n\tPromoted: %v", d.Promoted)
  5598  	base += fmt.Sprintf("\n\tPlaced: %d", d.PlacedAllocs)
  5599  	base += fmt.Sprintf("\n\tHealthy: %d", d.HealthyAllocs)
  5600  	base += fmt.Sprintf("\n\tUnhealthy: %d", d.UnhealthyAllocs)
  5601  	base += fmt.Sprintf("\n\tAutoRevert: %v", d.AutoRevert)
  5602  	return base
  5603  }
  5604  
  5605  func (d *DeploymentState) Copy() *DeploymentState {
  5606  	c := &DeploymentState{}
  5607  	*c = *d
  5608  	c.PlacedCanaries = helper.CopySliceString(d.PlacedCanaries)
  5609  	return c
  5610  }
  5611  
  5612  // DeploymentStatusUpdate is used to update the status of a given deployment
  5613  type DeploymentStatusUpdate struct {
  5614  	// DeploymentID is the ID of the deployment to update
  5615  	DeploymentID string
  5616  
  5617  	// Status is the new status of the deployment.
  5618  	Status string
  5619  
  5620  	// StatusDescription is the new status description of the deployment.
  5621  	StatusDescription string
  5622  }
  5623  
  5624  // RescheduleTracker encapsulates previous reschedule events
  5625  type RescheduleTracker struct {
  5626  	Events []*RescheduleEvent
  5627  }
  5628  
  5629  func (rt *RescheduleTracker) Copy() *RescheduleTracker {
  5630  	if rt == nil {
  5631  		return nil
  5632  	}
  5633  	nt := &RescheduleTracker{}
  5634  	*nt = *rt
  5635  	rescheduleEvents := make([]*RescheduleEvent, 0, len(rt.Events))
  5636  	for _, tracker := range rt.Events {
  5637  		rescheduleEvents = append(rescheduleEvents, tracker.Copy())
  5638  	}
  5639  	nt.Events = rescheduleEvents
  5640  	return nt
  5641  }
  5642  
  5643  // RescheduleEvent is used to keep track of previous attempts at rescheduling an allocation
  5644  type RescheduleEvent struct {
  5645  	// RescheduleTime is the timestamp of a reschedule attempt
  5646  	RescheduleTime int64
  5647  
  5648  	// PrevAllocID is the ID of the previous allocation being restarted
  5649  	PrevAllocID string
  5650  
  5651  	// PrevNodeID is the node ID of the previous allocation
  5652  	PrevNodeID string
  5653  
  5654  	// Delay is the reschedule delay associated with the attempt
  5655  	Delay time.Duration
  5656  }
  5657  
  5658  func NewRescheduleEvent(rescheduleTime int64, prevAllocID string, prevNodeID string, delay time.Duration) *RescheduleEvent {
  5659  	return &RescheduleEvent{RescheduleTime: rescheduleTime,
  5660  		PrevAllocID: prevAllocID,
  5661  		PrevNodeID:  prevNodeID,
  5662  		Delay:       delay}
  5663  }
  5664  
  5665  func (re *RescheduleEvent) Copy() *RescheduleEvent {
  5666  	if re == nil {
  5667  		return nil
  5668  	}
  5669  	copy := new(RescheduleEvent)
  5670  	*copy = *re
  5671  	return copy
  5672  }
  5673  
  5674  // DesiredTransition is used to mark an allocation as having a desired state
  5675  // transition. This information can be used by the scheduler to make the
  5676  // correct decision.
  5677  type DesiredTransition struct {
  5678  	// Migrate is used to indicate that this allocation should be stopped and
  5679  	// migrated to another node.
  5680  	Migrate *bool
  5681  
  5682  	// Reschedule is used to indicate that this allocation is eligible to be
  5683  	// rescheduled. Most allocations are automatically eligible for
  5684  	// rescheduling, so this field is only required when an allocation is not
  5685  	// automatically eligible. An example is an allocation that is part of a
  5686  	// deployment.
  5687  	Reschedule *bool
  5688  
  5689  	// ForceReschedule is used to indicate that this allocation must be rescheduled.
  5690  	// This field is only used when operators want to force a placement even if
  5691  	// a failed allocation is not eligible to be rescheduled
  5692  	ForceReschedule *bool
  5693  }
  5694  
  5695  // Merge merges the two desired transitions, preferring the values from the
  5696  // passed in object.
  5697  func (d *DesiredTransition) Merge(o *DesiredTransition) {
  5698  	if o.Migrate != nil {
  5699  		d.Migrate = o.Migrate
  5700  	}
  5701  
  5702  	if o.Reschedule != nil {
  5703  		d.Reschedule = o.Reschedule
  5704  	}
  5705  
  5706  	if o.ForceReschedule != nil {
  5707  		d.ForceReschedule = o.ForceReschedule
  5708  	}
  5709  }
  5710  
  5711  // ShouldMigrate returns whether the transition object dictates a migration.
  5712  func (d *DesiredTransition) ShouldMigrate() bool {
  5713  	return d.Migrate != nil && *d.Migrate
  5714  }
  5715  
  5716  // ShouldReschedule returns whether the transition object dictates a
  5717  // rescheduling.
  5718  func (d *DesiredTransition) ShouldReschedule() bool {
  5719  	return d.Reschedule != nil && *d.Reschedule
  5720  }
  5721  
  5722  // ShouldForceReschedule returns whether the transition object dictates a
  5723  // forced rescheduling.
  5724  func (d *DesiredTransition) ShouldForceReschedule() bool {
  5725  	if d == nil {
  5726  		return false
  5727  	}
  5728  	return d.ForceReschedule != nil && *d.ForceReschedule
  5729  }
  5730  
  5731  const (
  5732  	AllocDesiredStatusRun   = "run"   // Allocation should run
  5733  	AllocDesiredStatusStop  = "stop"  // Allocation should stop
  5734  	AllocDesiredStatusEvict = "evict" // Allocation should stop, and was evicted
  5735  )
  5736  
  5737  const (
  5738  	AllocClientStatusPending  = "pending"
  5739  	AllocClientStatusRunning  = "running"
  5740  	AllocClientStatusComplete = "complete"
  5741  	AllocClientStatusFailed   = "failed"
  5742  	AllocClientStatusLost     = "lost"
  5743  )
  5744  
  5745  // Allocation is used to allocate the placement of a task group to a node.
  5746  type Allocation struct {
  5747  	// ID of the allocation (UUID)
  5748  	ID string
  5749  
  5750  	// Namespace is the namespace the allocation is created in
  5751  	Namespace string
  5752  
  5753  	// ID of the evaluation that generated this allocation
  5754  	EvalID string
  5755  
  5756  	// Name is a logical name of the allocation.
  5757  	Name string
  5758  
  5759  	// NodeID is the node this is being placed on
  5760  	NodeID string
  5761  
  5762  	// Job is the parent job of the task group being allocated.
  5763  	// This is copied at allocation time to avoid issues if the job
  5764  	// definition is updated.
  5765  	JobID string
  5766  	Job   *Job
  5767  
  5768  	// TaskGroup is the name of the task group that should be run
  5769  	TaskGroup string
  5770  
  5771  	// Resources is the total set of resources allocated as part
  5772  	// of this allocation of the task group.
  5773  	Resources *Resources
  5774  
  5775  	// SharedResources are the resources that are shared by all the tasks in an
  5776  	// allocation
  5777  	SharedResources *Resources
  5778  
  5779  	// TaskResources is the set of resources allocated to each
  5780  	// task. These should sum to the total Resources.
  5781  	TaskResources map[string]*Resources
  5782  
  5783  	// Metrics associated with this allocation
  5784  	Metrics *AllocMetric
  5785  
  5786  	// Desired Status of the allocation on the client
  5787  	DesiredStatus string
  5788  
  5789  	// DesiredStatusDescription is meant to provide more human useful information
  5790  	DesiredDescription string
  5791  
  5792  	// DesiredTransition is used to indicate that a state transition
  5793  	// is desired for a given reason.
  5794  	DesiredTransition DesiredTransition
  5795  
  5796  	// Status of the allocation on the client
  5797  	ClientStatus string
  5798  
  5799  	// ClientStatusDescription is meant to provide more human useful information
  5800  	ClientDescription string
  5801  
  5802  	// TaskStates stores the state of each task,
  5803  	TaskStates map[string]*TaskState
  5804  
  5805  	// PreviousAllocation is the allocation that this allocation is replacing
  5806  	PreviousAllocation string
  5807  
  5808  	// NextAllocation is the allocation that this allocation is being replaced by
  5809  	NextAllocation string
  5810  
  5811  	// DeploymentID identifies an allocation as being created from a
  5812  	// particular deployment
  5813  	DeploymentID string
  5814  
  5815  	// DeploymentStatus captures the status of the allocation as part of the
  5816  	// given deployment
  5817  	DeploymentStatus *AllocDeploymentStatus
  5818  
  5819  	// RescheduleTrackers captures details of previous reschedule attempts of the allocation
  5820  	RescheduleTracker *RescheduleTracker
  5821  
  5822  	// FollowupEvalID captures a follow up evaluation created to handle a failed allocation
  5823  	// that can be rescheduled in the future
  5824  	FollowupEvalID string
  5825  
  5826  	// Raft Indexes
  5827  	CreateIndex uint64
  5828  	ModifyIndex uint64
  5829  
  5830  	// AllocModifyIndex is not updated when the client updates allocations. This
  5831  	// lets the client pull only the allocs updated by the server.
  5832  	AllocModifyIndex uint64
  5833  
  5834  	// CreateTime is the time the allocation has finished scheduling and been
  5835  	// verified by the plan applier.
  5836  	CreateTime int64
  5837  
  5838  	// ModifyTime is the time the allocation was last updated.
  5839  	ModifyTime int64
  5840  }
  5841  
  5842  // Index returns the index of the allocation. If the allocation is from a task
  5843  // group with count greater than 1, there will be multiple allocations for it.
  5844  func (a *Allocation) Index() uint {
  5845  	l := len(a.Name)
  5846  	prefix := len(a.JobID) + len(a.TaskGroup) + 2
  5847  	if l <= 3 || l <= prefix {
  5848  		return uint(0)
  5849  	}
  5850  
  5851  	strNum := a.Name[prefix : len(a.Name)-1]
  5852  	num, _ := strconv.Atoi(strNum)
  5853  	return uint(num)
  5854  }
  5855  
  5856  func (a *Allocation) Copy() *Allocation {
  5857  	return a.copyImpl(true)
  5858  }
  5859  
  5860  // Copy provides a copy of the allocation but doesn't deep copy the job
  5861  func (a *Allocation) CopySkipJob() *Allocation {
  5862  	return a.copyImpl(false)
  5863  }
  5864  
  5865  func (a *Allocation) copyImpl(job bool) *Allocation {
  5866  	if a == nil {
  5867  		return nil
  5868  	}
  5869  	na := new(Allocation)
  5870  	*na = *a
  5871  
  5872  	if job {
  5873  		na.Job = na.Job.Copy()
  5874  	}
  5875  
  5876  	na.Resources = na.Resources.Copy()
  5877  	na.SharedResources = na.SharedResources.Copy()
  5878  
  5879  	if a.TaskResources != nil {
  5880  		tr := make(map[string]*Resources, len(na.TaskResources))
  5881  		for task, resource := range na.TaskResources {
  5882  			tr[task] = resource.Copy()
  5883  		}
  5884  		na.TaskResources = tr
  5885  	}
  5886  
  5887  	na.Metrics = na.Metrics.Copy()
  5888  	na.DeploymentStatus = na.DeploymentStatus.Copy()
  5889  
  5890  	if a.TaskStates != nil {
  5891  		ts := make(map[string]*TaskState, len(na.TaskStates))
  5892  		for task, state := range na.TaskStates {
  5893  			ts[task] = state.Copy()
  5894  		}
  5895  		na.TaskStates = ts
  5896  	}
  5897  
  5898  	na.RescheduleTracker = a.RescheduleTracker.Copy()
  5899  	return na
  5900  }
  5901  
  5902  // TerminalStatus returns if the desired or actual status is terminal and
  5903  // will no longer transition.
  5904  func (a *Allocation) TerminalStatus() bool {
  5905  	// First check the desired state and if that isn't terminal, check client
  5906  	// state.
  5907  	switch a.DesiredStatus {
  5908  	case AllocDesiredStatusStop, AllocDesiredStatusEvict:
  5909  		return true
  5910  	default:
  5911  	}
  5912  
  5913  	return a.ClientTerminalStatus()
  5914  }
  5915  
  5916  // ClientTerminalStatus returns if the client status is terminal and will no longer transition
  5917  func (a *Allocation) ClientTerminalStatus() bool {
  5918  	switch a.ClientStatus {
  5919  	case AllocClientStatusComplete, AllocClientStatusFailed, AllocClientStatusLost:
  5920  		return true
  5921  	default:
  5922  		return false
  5923  	}
  5924  }
  5925  
  5926  // ShouldReschedule returns if the allocation is eligible to be rescheduled according
  5927  // to its status and ReschedulePolicy given its failure time
  5928  func (a *Allocation) ShouldReschedule(reschedulePolicy *ReschedulePolicy, failTime time.Time) bool {
  5929  	// First check the desired state
  5930  	switch a.DesiredStatus {
  5931  	case AllocDesiredStatusStop, AllocDesiredStatusEvict:
  5932  		return false
  5933  	default:
  5934  	}
  5935  	switch a.ClientStatus {
  5936  	case AllocClientStatusFailed:
  5937  		return a.RescheduleEligible(reschedulePolicy, failTime)
  5938  	default:
  5939  		return false
  5940  	}
  5941  }
  5942  
  5943  // RescheduleEligible returns if the allocation is eligible to be rescheduled according
  5944  // to its ReschedulePolicy and the current state of its reschedule trackers
  5945  func (a *Allocation) RescheduleEligible(reschedulePolicy *ReschedulePolicy, failTime time.Time) bool {
  5946  	if reschedulePolicy == nil {
  5947  		return false
  5948  	}
  5949  	attempts := reschedulePolicy.Attempts
  5950  	interval := reschedulePolicy.Interval
  5951  	enabled := attempts > 0 || reschedulePolicy.Unlimited
  5952  	if !enabled {
  5953  		return false
  5954  	}
  5955  	if reschedulePolicy.Unlimited {
  5956  		return true
  5957  	}
  5958  	// Early return true if there are no attempts yet and the number of allowed attempts is > 0
  5959  	if (a.RescheduleTracker == nil || len(a.RescheduleTracker.Events) == 0) && attempts > 0 {
  5960  		return true
  5961  	}
  5962  	attempted := 0
  5963  	for j := len(a.RescheduleTracker.Events) - 1; j >= 0; j-- {
  5964  		lastAttempt := a.RescheduleTracker.Events[j].RescheduleTime
  5965  		timeDiff := failTime.UTC().UnixNano() - lastAttempt
  5966  		if timeDiff < interval.Nanoseconds() {
  5967  			attempted += 1
  5968  		}
  5969  	}
  5970  	return attempted < attempts
  5971  }
  5972  
  5973  // LastEventTime is the time of the last task event in the allocation.
  5974  // It is used to determine allocation failure time. If the FinishedAt field
  5975  // is not set, the alloc's modify time is used
  5976  func (a *Allocation) LastEventTime() time.Time {
  5977  	var lastEventTime time.Time
  5978  	if a.TaskStates != nil {
  5979  		for _, s := range a.TaskStates {
  5980  			if lastEventTime.IsZero() || s.FinishedAt.After(lastEventTime) {
  5981  				lastEventTime = s.FinishedAt
  5982  			}
  5983  		}
  5984  	}
  5985  
  5986  	if lastEventTime.IsZero() {
  5987  		return time.Unix(0, a.ModifyTime).UTC()
  5988  	}
  5989  	return lastEventTime
  5990  }
  5991  
  5992  // ReschedulePolicy returns the reschedule policy based on the task group
  5993  func (a *Allocation) ReschedulePolicy() *ReschedulePolicy {
  5994  	tg := a.Job.LookupTaskGroup(a.TaskGroup)
  5995  	if tg == nil {
  5996  		return nil
  5997  	}
  5998  	return tg.ReschedulePolicy
  5999  }
  6000  
  6001  // NextRescheduleTime returns a time on or after which the allocation is eligible to be rescheduled,
  6002  // and whether the next reschedule time is within policy's interval if the policy doesn't allow unlimited reschedules
  6003  func (a *Allocation) NextRescheduleTime() (time.Time, bool) {
  6004  	failTime := a.LastEventTime()
  6005  	reschedulePolicy := a.ReschedulePolicy()
  6006  	if a.DesiredStatus == AllocDesiredStatusStop || a.ClientStatus != AllocClientStatusFailed || failTime.IsZero() || reschedulePolicy == nil {
  6007  		return time.Time{}, false
  6008  	}
  6009  
  6010  	nextDelay := a.NextDelay()
  6011  	nextRescheduleTime := failTime.Add(nextDelay)
  6012  	rescheduleEligible := reschedulePolicy.Unlimited || (reschedulePolicy.Attempts > 0 && a.RescheduleTracker == nil)
  6013  	if reschedulePolicy.Attempts > 0 && a.RescheduleTracker != nil && a.RescheduleTracker.Events != nil {
  6014  		// Check for eligibility based on the interval if max attempts is set
  6015  		attempted := 0
  6016  		for j := len(a.RescheduleTracker.Events) - 1; j >= 0; j-- {
  6017  			lastAttempt := a.RescheduleTracker.Events[j].RescheduleTime
  6018  			timeDiff := failTime.UTC().UnixNano() - lastAttempt
  6019  			if timeDiff < reschedulePolicy.Interval.Nanoseconds() {
  6020  				attempted += 1
  6021  			}
  6022  		}
  6023  		rescheduleEligible = attempted < reschedulePolicy.Attempts && nextDelay < reschedulePolicy.Interval
  6024  	}
  6025  	return nextRescheduleTime, rescheduleEligible
  6026  }
  6027  
  6028  // NextDelay returns a duration after which the allocation can be rescheduled.
  6029  // It is calculated according to the delay function and previous reschedule attempts.
  6030  func (a *Allocation) NextDelay() time.Duration {
  6031  	policy := a.ReschedulePolicy()
  6032  	// Can be nil if the task group was updated to remove its reschedule policy
  6033  	if policy == nil {
  6034  		return 0
  6035  	}
  6036  	delayDur := policy.Delay
  6037  	if a.RescheduleTracker == nil || a.RescheduleTracker.Events == nil || len(a.RescheduleTracker.Events) == 0 {
  6038  		return delayDur
  6039  	}
  6040  	events := a.RescheduleTracker.Events
  6041  	switch policy.DelayFunction {
  6042  	case "exponential":
  6043  		delayDur = a.RescheduleTracker.Events[len(a.RescheduleTracker.Events)-1].Delay * 2
  6044  	case "fibonacci":
  6045  		if len(events) >= 2 {
  6046  			fibN1Delay := events[len(events)-1].Delay
  6047  			fibN2Delay := events[len(events)-2].Delay
  6048  			// Handle reset of delay ceiling which should cause
  6049  			// a new series to start
  6050  			if fibN2Delay == policy.MaxDelay && fibN1Delay == policy.Delay {
  6051  				delayDur = fibN1Delay
  6052  			} else {
  6053  				delayDur = fibN1Delay + fibN2Delay
  6054  			}
  6055  		}
  6056  	default:
  6057  		return delayDur
  6058  	}
  6059  	if policy.MaxDelay > 0 && delayDur > policy.MaxDelay {
  6060  		delayDur = policy.MaxDelay
  6061  		// check if delay needs to be reset
  6062  
  6063  		lastRescheduleEvent := a.RescheduleTracker.Events[len(a.RescheduleTracker.Events)-1]
  6064  		timeDiff := a.LastEventTime().UTC().UnixNano() - lastRescheduleEvent.RescheduleTime
  6065  		if timeDiff > delayDur.Nanoseconds() {
  6066  			delayDur = policy.Delay
  6067  		}
  6068  
  6069  	}
  6070  
  6071  	return delayDur
  6072  }
  6073  
  6074  // Terminated returns if the allocation is in a terminal state on a client.
  6075  func (a *Allocation) Terminated() bool {
  6076  	if a.ClientStatus == AllocClientStatusFailed ||
  6077  		a.ClientStatus == AllocClientStatusComplete ||
  6078  		a.ClientStatus == AllocClientStatusLost {
  6079  		return true
  6080  	}
  6081  	return false
  6082  }
  6083  
  6084  // RanSuccessfully returns whether the client has ran the allocation and all
  6085  // tasks finished successfully. Critically this function returns whether the
  6086  // allocation has ran to completion and not just that the alloc has converged to
  6087  // its desired state. That is to say that a batch allocation must have finished
  6088  // with exit code 0 on all task groups. This doesn't really have meaning on a
  6089  // non-batch allocation because a service and system allocation should not
  6090  // finish.
  6091  func (a *Allocation) RanSuccessfully() bool {
  6092  	// Handle the case the client hasn't started the allocation.
  6093  	if len(a.TaskStates) == 0 {
  6094  		return false
  6095  	}
  6096  
  6097  	// Check to see if all the tasks finished successfully in the allocation
  6098  	allSuccess := true
  6099  	for _, state := range a.TaskStates {
  6100  		allSuccess = allSuccess && state.Successful()
  6101  	}
  6102  
  6103  	return allSuccess
  6104  }
  6105  
  6106  // ShouldMigrate returns if the allocation needs data migration
  6107  func (a *Allocation) ShouldMigrate() bool {
  6108  	if a.PreviousAllocation == "" {
  6109  		return false
  6110  	}
  6111  
  6112  	if a.DesiredStatus == AllocDesiredStatusStop || a.DesiredStatus == AllocDesiredStatusEvict {
  6113  		return false
  6114  	}
  6115  
  6116  	tg := a.Job.LookupTaskGroup(a.TaskGroup)
  6117  
  6118  	// if the task group is nil or the ephemeral disk block isn't present then
  6119  	// we won't migrate
  6120  	if tg == nil || tg.EphemeralDisk == nil {
  6121  		return false
  6122  	}
  6123  
  6124  	// We won't migrate any data is the user hasn't enabled migration or the
  6125  	// disk is not marked as sticky
  6126  	if !tg.EphemeralDisk.Migrate || !tg.EphemeralDisk.Sticky {
  6127  		return false
  6128  	}
  6129  
  6130  	return true
  6131  }
  6132  
  6133  // SetEventDisplayMessage populates the display message if its not already set,
  6134  // a temporary fix to handle old allocations that don't have it.
  6135  // This method will be removed in a future release.
  6136  func (a *Allocation) SetEventDisplayMessages() {
  6137  	setDisplayMsg(a.TaskStates)
  6138  }
  6139  
  6140  // Stub returns a list stub for the allocation
  6141  func (a *Allocation) Stub() *AllocListStub {
  6142  	return &AllocListStub{
  6143  		ID:                 a.ID,
  6144  		EvalID:             a.EvalID,
  6145  		Name:               a.Name,
  6146  		NodeID:             a.NodeID,
  6147  		JobID:              a.JobID,
  6148  		JobVersion:         a.Job.Version,
  6149  		TaskGroup:          a.TaskGroup,
  6150  		DesiredStatus:      a.DesiredStatus,
  6151  		DesiredDescription: a.DesiredDescription,
  6152  		ClientStatus:       a.ClientStatus,
  6153  		ClientDescription:  a.ClientDescription,
  6154  		DesiredTransition:  a.DesiredTransition,
  6155  		TaskStates:         a.TaskStates,
  6156  		DeploymentStatus:   a.DeploymentStatus,
  6157  		FollowupEvalID:     a.FollowupEvalID,
  6158  		RescheduleTracker:  a.RescheduleTracker,
  6159  		CreateIndex:        a.CreateIndex,
  6160  		ModifyIndex:        a.ModifyIndex,
  6161  		CreateTime:         a.CreateTime,
  6162  		ModifyTime:         a.ModifyTime,
  6163  	}
  6164  }
  6165  
  6166  // AllocListStub is used to return a subset of alloc information
  6167  type AllocListStub struct {
  6168  	ID                 string
  6169  	EvalID             string
  6170  	Name               string
  6171  	NodeID             string
  6172  	JobID              string
  6173  	JobVersion         uint64
  6174  	TaskGroup          string
  6175  	DesiredStatus      string
  6176  	DesiredDescription string
  6177  	ClientStatus       string
  6178  	ClientDescription  string
  6179  	DesiredTransition  DesiredTransition
  6180  	TaskStates         map[string]*TaskState
  6181  	DeploymentStatus   *AllocDeploymentStatus
  6182  	FollowupEvalID     string
  6183  	RescheduleTracker  *RescheduleTracker
  6184  	CreateIndex        uint64
  6185  	ModifyIndex        uint64
  6186  	CreateTime         int64
  6187  	ModifyTime         int64
  6188  }
  6189  
  6190  // SetEventDisplayMessage populates the display message if its not already set,
  6191  // a temporary fix to handle old allocations that don't have it.
  6192  // This method will be removed in a future release.
  6193  func (a *AllocListStub) SetEventDisplayMessages() {
  6194  	setDisplayMsg(a.TaskStates)
  6195  }
  6196  
  6197  func setDisplayMsg(taskStates map[string]*TaskState) {
  6198  	if taskStates != nil {
  6199  		for _, taskState := range taskStates {
  6200  			for _, event := range taskState.Events {
  6201  				event.PopulateEventDisplayMessage()
  6202  			}
  6203  		}
  6204  	}
  6205  }
  6206  
  6207  // AllocMetric is used to track various metrics while attempting
  6208  // to make an allocation. These are used to debug a job, or to better
  6209  // understand the pressure within the system.
  6210  type AllocMetric struct {
  6211  	// NodesEvaluated is the number of nodes that were evaluated
  6212  	NodesEvaluated int
  6213  
  6214  	// NodesFiltered is the number of nodes filtered due to a constraint
  6215  	NodesFiltered int
  6216  
  6217  	// NodesAvailable is the number of nodes available for evaluation per DC.
  6218  	NodesAvailable map[string]int
  6219  
  6220  	// ClassFiltered is the number of nodes filtered by class
  6221  	ClassFiltered map[string]int
  6222  
  6223  	// ConstraintFiltered is the number of failures caused by constraint
  6224  	ConstraintFiltered map[string]int
  6225  
  6226  	// NodesExhausted is the number of nodes skipped due to being
  6227  	// exhausted of at least one resource
  6228  	NodesExhausted int
  6229  
  6230  	// ClassExhausted is the number of nodes exhausted by class
  6231  	ClassExhausted map[string]int
  6232  
  6233  	// DimensionExhausted provides the count by dimension or reason
  6234  	DimensionExhausted map[string]int
  6235  
  6236  	// QuotaExhausted provides the exhausted dimensions
  6237  	QuotaExhausted []string
  6238  
  6239  	// Scores is the scores of the final few nodes remaining
  6240  	// for placement. The top score is typically selected.
  6241  	Scores map[string]float64
  6242  
  6243  	// AllocationTime is a measure of how long the allocation
  6244  	// attempt took. This can affect performance and SLAs.
  6245  	AllocationTime time.Duration
  6246  
  6247  	// CoalescedFailures indicates the number of other
  6248  	// allocations that were coalesced into this failed allocation.
  6249  	// This is to prevent creating many failed allocations for a
  6250  	// single task group.
  6251  	CoalescedFailures int
  6252  }
  6253  
  6254  func (a *AllocMetric) Copy() *AllocMetric {
  6255  	if a == nil {
  6256  		return nil
  6257  	}
  6258  	na := new(AllocMetric)
  6259  	*na = *a
  6260  	na.NodesAvailable = helper.CopyMapStringInt(na.NodesAvailable)
  6261  	na.ClassFiltered = helper.CopyMapStringInt(na.ClassFiltered)
  6262  	na.ConstraintFiltered = helper.CopyMapStringInt(na.ConstraintFiltered)
  6263  	na.ClassExhausted = helper.CopyMapStringInt(na.ClassExhausted)
  6264  	na.DimensionExhausted = helper.CopyMapStringInt(na.DimensionExhausted)
  6265  	na.QuotaExhausted = helper.CopySliceString(na.QuotaExhausted)
  6266  	na.Scores = helper.CopyMapStringFloat64(na.Scores)
  6267  	return na
  6268  }
  6269  
  6270  func (a *AllocMetric) EvaluateNode() {
  6271  	a.NodesEvaluated += 1
  6272  }
  6273  
  6274  func (a *AllocMetric) FilterNode(node *Node, constraint string) {
  6275  	a.NodesFiltered += 1
  6276  	if node != nil && node.NodeClass != "" {
  6277  		if a.ClassFiltered == nil {
  6278  			a.ClassFiltered = make(map[string]int)
  6279  		}
  6280  		a.ClassFiltered[node.NodeClass] += 1
  6281  	}
  6282  	if constraint != "" {
  6283  		if a.ConstraintFiltered == nil {
  6284  			a.ConstraintFiltered = make(map[string]int)
  6285  		}
  6286  		a.ConstraintFiltered[constraint] += 1
  6287  	}
  6288  }
  6289  
  6290  func (a *AllocMetric) ExhaustedNode(node *Node, dimension string) {
  6291  	a.NodesExhausted += 1
  6292  	if node != nil && node.NodeClass != "" {
  6293  		if a.ClassExhausted == nil {
  6294  			a.ClassExhausted = make(map[string]int)
  6295  		}
  6296  		a.ClassExhausted[node.NodeClass] += 1
  6297  	}
  6298  	if dimension != "" {
  6299  		if a.DimensionExhausted == nil {
  6300  			a.DimensionExhausted = make(map[string]int)
  6301  		}
  6302  		a.DimensionExhausted[dimension] += 1
  6303  	}
  6304  }
  6305  
  6306  func (a *AllocMetric) ExhaustQuota(dimensions []string) {
  6307  	if a.QuotaExhausted == nil {
  6308  		a.QuotaExhausted = make([]string, 0, len(dimensions))
  6309  	}
  6310  
  6311  	a.QuotaExhausted = append(a.QuotaExhausted, dimensions...)
  6312  }
  6313  
  6314  func (a *AllocMetric) ScoreNode(node *Node, name string, score float64) {
  6315  	if a.Scores == nil {
  6316  		a.Scores = make(map[string]float64)
  6317  	}
  6318  	key := fmt.Sprintf("%s.%s", node.ID, name)
  6319  	a.Scores[key] = score
  6320  }
  6321  
  6322  // AllocDeploymentStatus captures the status of the allocation as part of the
  6323  // deployment. This can include things like if the allocation has been marked as
  6324  // healthy.
  6325  type AllocDeploymentStatus struct {
  6326  	// Healthy marks whether the allocation has been marked healthy or unhealthy
  6327  	// as part of a deployment. It can be unset if it has neither been marked
  6328  	// healthy or unhealthy.
  6329  	Healthy *bool
  6330  
  6331  	// Timestamp is the time at which the health status was set.
  6332  	Timestamp time.Time
  6333  
  6334  	// Canary marks whether the allocation is a canary or not. A canary that has
  6335  	// been promoted will have this field set to false.
  6336  	Canary bool
  6337  
  6338  	// ModifyIndex is the raft index in which the deployment status was last
  6339  	// changed.
  6340  	ModifyIndex uint64
  6341  }
  6342  
  6343  // HasHealth returns true if the allocation has its health set.
  6344  func (a *AllocDeploymentStatus) HasHealth() bool {
  6345  	return a != nil && a.Healthy != nil
  6346  }
  6347  
  6348  // IsHealthy returns if the allocation is marked as healthy as part of a
  6349  // deployment
  6350  func (a *AllocDeploymentStatus) IsHealthy() bool {
  6351  	if a == nil {
  6352  		return false
  6353  	}
  6354  
  6355  	return a.Healthy != nil && *a.Healthy
  6356  }
  6357  
  6358  // IsUnhealthy returns if the allocation is marked as unhealthy as part of a
  6359  // deployment
  6360  func (a *AllocDeploymentStatus) IsUnhealthy() bool {
  6361  	if a == nil {
  6362  		return false
  6363  	}
  6364  
  6365  	return a.Healthy != nil && !*a.Healthy
  6366  }
  6367  
  6368  // IsCanary returns if the allocation is marked as a canary
  6369  func (a *AllocDeploymentStatus) IsCanary() bool {
  6370  	if a == nil {
  6371  		return false
  6372  	}
  6373  
  6374  	return a.Canary
  6375  }
  6376  
  6377  func (a *AllocDeploymentStatus) Copy() *AllocDeploymentStatus {
  6378  	if a == nil {
  6379  		return nil
  6380  	}
  6381  
  6382  	c := new(AllocDeploymentStatus)
  6383  	*c = *a
  6384  
  6385  	if a.Healthy != nil {
  6386  		c.Healthy = helper.BoolToPtr(*a.Healthy)
  6387  	}
  6388  
  6389  	return c
  6390  }
  6391  
  6392  const (
  6393  	EvalStatusBlocked   = "blocked"
  6394  	EvalStatusPending   = "pending"
  6395  	EvalStatusComplete  = "complete"
  6396  	EvalStatusFailed    = "failed"
  6397  	EvalStatusCancelled = "canceled"
  6398  )
  6399  
  6400  const (
  6401  	EvalTriggerJobRegister       = "job-register"
  6402  	EvalTriggerJobDeregister     = "job-deregister"
  6403  	EvalTriggerPeriodicJob       = "periodic-job"
  6404  	EvalTriggerNodeDrain         = "node-drain"
  6405  	EvalTriggerNodeUpdate        = "node-update"
  6406  	EvalTriggerScheduled         = "scheduled"
  6407  	EvalTriggerRollingUpdate     = "rolling-update"
  6408  	EvalTriggerDeploymentWatcher = "deployment-watcher"
  6409  	EvalTriggerFailedFollowUp    = "failed-follow-up"
  6410  	EvalTriggerMaxPlans          = "max-plan-attempts"
  6411  	EvalTriggerRetryFailedAlloc  = "alloc-failure"
  6412  	EvalTriggerQueuedAllocs      = "queued-allocs"
  6413  )
  6414  
  6415  const (
  6416  	// CoreJobEvalGC is used for the garbage collection of evaluations
  6417  	// and allocations. We periodically scan evaluations in a terminal state,
  6418  	// in which all the corresponding allocations are also terminal. We
  6419  	// delete these out of the system to bound the state.
  6420  	CoreJobEvalGC = "eval-gc"
  6421  
  6422  	// CoreJobNodeGC is used for the garbage collection of failed nodes.
  6423  	// We periodically scan nodes in a terminal state, and if they have no
  6424  	// corresponding allocations we delete these out of the system.
  6425  	CoreJobNodeGC = "node-gc"
  6426  
  6427  	// CoreJobJobGC is used for the garbage collection of eligible jobs. We
  6428  	// periodically scan garbage collectible jobs and check if both their
  6429  	// evaluations and allocations are terminal. If so, we delete these out of
  6430  	// the system.
  6431  	CoreJobJobGC = "job-gc"
  6432  
  6433  	// CoreJobDeploymentGC is used for the garbage collection of eligible
  6434  	// deployments. We periodically scan garbage collectible deployments and
  6435  	// check if they are terminal. If so, we delete these out of the system.
  6436  	CoreJobDeploymentGC = "deployment-gc"
  6437  
  6438  	// CoreJobForceGC is used to force garbage collection of all GCable objects.
  6439  	CoreJobForceGC = "force-gc"
  6440  )
  6441  
  6442  // Evaluation is used anytime we need to apply business logic as a result
  6443  // of a change to our desired state (job specification) or the emergent state
  6444  // (registered nodes). When the inputs change, we need to "evaluate" them,
  6445  // potentially taking action (allocation of work) or doing nothing if the state
  6446  // of the world does not require it.
  6447  type Evaluation struct {
  6448  	// ID is a randomly generated UUID used for this evaluation. This
  6449  	// is assigned upon the creation of the evaluation.
  6450  	ID string
  6451  
  6452  	// Namespace is the namespace the evaluation is created in
  6453  	Namespace string
  6454  
  6455  	// Priority is used to control scheduling importance and if this job
  6456  	// can preempt other jobs.
  6457  	Priority int
  6458  
  6459  	// Type is used to control which schedulers are available to handle
  6460  	// this evaluation.
  6461  	Type string
  6462  
  6463  	// TriggeredBy is used to give some insight into why this Eval
  6464  	// was created. (Job change, node failure, alloc failure, etc).
  6465  	TriggeredBy string
  6466  
  6467  	// JobID is the job this evaluation is scoped to. Evaluations cannot
  6468  	// be run in parallel for a given JobID, so we serialize on this.
  6469  	JobID string
  6470  
  6471  	// JobModifyIndex is the modify index of the job at the time
  6472  	// the evaluation was created
  6473  	JobModifyIndex uint64
  6474  
  6475  	// NodeID is the node that was affected triggering the evaluation.
  6476  	NodeID string
  6477  
  6478  	// NodeModifyIndex is the modify index of the node at the time
  6479  	// the evaluation was created
  6480  	NodeModifyIndex uint64
  6481  
  6482  	// DeploymentID is the ID of the deployment that triggered the evaluation.
  6483  	DeploymentID string
  6484  
  6485  	// Status of the evaluation
  6486  	Status string
  6487  
  6488  	// StatusDescription is meant to provide more human useful information
  6489  	StatusDescription string
  6490  
  6491  	// Wait is a minimum wait time for running the eval. This is used to
  6492  	// support a rolling upgrade in versions prior to 0.7.0
  6493  	// Deprecated
  6494  	Wait time.Duration
  6495  
  6496  	// WaitUntil is the time when this eval should be run. This is used to
  6497  	// supported delayed rescheduling of failed allocations
  6498  	WaitUntil time.Time
  6499  
  6500  	// NextEval is the evaluation ID for the eval created to do a followup.
  6501  	// This is used to support rolling upgrades, where we need a chain of evaluations.
  6502  	NextEval string
  6503  
  6504  	// PreviousEval is the evaluation ID for the eval creating this one to do a followup.
  6505  	// This is used to support rolling upgrades, where we need a chain of evaluations.
  6506  	PreviousEval string
  6507  
  6508  	// BlockedEval is the evaluation ID for a created blocked eval. A
  6509  	// blocked eval will be created if all allocations could not be placed due
  6510  	// to constraints or lacking resources.
  6511  	BlockedEval string
  6512  
  6513  	// FailedTGAllocs are task groups which have allocations that could not be
  6514  	// made, but the metrics are persisted so that the user can use the feedback
  6515  	// to determine the cause.
  6516  	FailedTGAllocs map[string]*AllocMetric
  6517  
  6518  	// ClassEligibility tracks computed node classes that have been explicitly
  6519  	// marked as eligible or ineligible.
  6520  	ClassEligibility map[string]bool
  6521  
  6522  	// QuotaLimitReached marks whether a quota limit was reached for the
  6523  	// evaluation.
  6524  	QuotaLimitReached string
  6525  
  6526  	// EscapedComputedClass marks whether the job has constraints that are not
  6527  	// captured by computed node classes.
  6528  	EscapedComputedClass bool
  6529  
  6530  	// AnnotatePlan triggers the scheduler to provide additional annotations
  6531  	// during the evaluation. This should not be set during normal operations.
  6532  	AnnotatePlan bool
  6533  
  6534  	// QueuedAllocations is the number of unplaced allocations at the time the
  6535  	// evaluation was processed. The map is keyed by Task Group names.
  6536  	QueuedAllocations map[string]int
  6537  
  6538  	// LeaderACL provides the ACL token to when issuing RPCs back to the
  6539  	// leader. This will be a valid management token as long as the leader is
  6540  	// active. This should not ever be exposed via the API.
  6541  	LeaderACL string
  6542  
  6543  	// SnapshotIndex is the Raft index of the snapshot used to process the
  6544  	// evaluation. The index will either be set when it has gone through the
  6545  	// scheduler or if a blocked evaluation is being created. The index is set
  6546  	// in this case so we can determine if an early unblocking is required since
  6547  	// capacity has changed since the evaluation was created. This can result in
  6548  	// the SnapshotIndex being less than the CreateIndex.
  6549  	SnapshotIndex uint64
  6550  
  6551  	// Raft Indexes
  6552  	CreateIndex uint64
  6553  	ModifyIndex uint64
  6554  }
  6555  
  6556  // TerminalStatus returns if the current status is terminal and
  6557  // will no longer transition.
  6558  func (e *Evaluation) TerminalStatus() bool {
  6559  	switch e.Status {
  6560  	case EvalStatusComplete, EvalStatusFailed, EvalStatusCancelled:
  6561  		return true
  6562  	default:
  6563  		return false
  6564  	}
  6565  }
  6566  
  6567  func (e *Evaluation) GoString() string {
  6568  	return fmt.Sprintf("<Eval %q JobID: %q Namespace: %q>", e.ID, e.JobID, e.Namespace)
  6569  }
  6570  
  6571  func (e *Evaluation) Copy() *Evaluation {
  6572  	if e == nil {
  6573  		return nil
  6574  	}
  6575  	ne := new(Evaluation)
  6576  	*ne = *e
  6577  
  6578  	// Copy ClassEligibility
  6579  	if e.ClassEligibility != nil {
  6580  		classes := make(map[string]bool, len(e.ClassEligibility))
  6581  		for class, elig := range e.ClassEligibility {
  6582  			classes[class] = elig
  6583  		}
  6584  		ne.ClassEligibility = classes
  6585  	}
  6586  
  6587  	// Copy FailedTGAllocs
  6588  	if e.FailedTGAllocs != nil {
  6589  		failedTGs := make(map[string]*AllocMetric, len(e.FailedTGAllocs))
  6590  		for tg, metric := range e.FailedTGAllocs {
  6591  			failedTGs[tg] = metric.Copy()
  6592  		}
  6593  		ne.FailedTGAllocs = failedTGs
  6594  	}
  6595  
  6596  	// Copy queued allocations
  6597  	if e.QueuedAllocations != nil {
  6598  		queuedAllocations := make(map[string]int, len(e.QueuedAllocations))
  6599  		for tg, num := range e.QueuedAllocations {
  6600  			queuedAllocations[tg] = num
  6601  		}
  6602  		ne.QueuedAllocations = queuedAllocations
  6603  	}
  6604  
  6605  	return ne
  6606  }
  6607  
  6608  // ShouldEnqueue checks if a given evaluation should be enqueued into the
  6609  // eval_broker
  6610  func (e *Evaluation) ShouldEnqueue() bool {
  6611  	switch e.Status {
  6612  	case EvalStatusPending:
  6613  		return true
  6614  	case EvalStatusComplete, EvalStatusFailed, EvalStatusBlocked, EvalStatusCancelled:
  6615  		return false
  6616  	default:
  6617  		panic(fmt.Sprintf("unhandled evaluation (%s) status %s", e.ID, e.Status))
  6618  	}
  6619  }
  6620  
  6621  // ShouldBlock checks if a given evaluation should be entered into the blocked
  6622  // eval tracker.
  6623  func (e *Evaluation) ShouldBlock() bool {
  6624  	switch e.Status {
  6625  	case EvalStatusBlocked:
  6626  		return true
  6627  	case EvalStatusComplete, EvalStatusFailed, EvalStatusPending, EvalStatusCancelled:
  6628  		return false
  6629  	default:
  6630  		panic(fmt.Sprintf("unhandled evaluation (%s) status %s", e.ID, e.Status))
  6631  	}
  6632  }
  6633  
  6634  // MakePlan is used to make a plan from the given evaluation
  6635  // for a given Job
  6636  func (e *Evaluation) MakePlan(j *Job) *Plan {
  6637  	p := &Plan{
  6638  		EvalID:         e.ID,
  6639  		Priority:       e.Priority,
  6640  		Job:            j,
  6641  		NodeUpdate:     make(map[string][]*Allocation),
  6642  		NodeAllocation: make(map[string][]*Allocation),
  6643  	}
  6644  	if j != nil {
  6645  		p.AllAtOnce = j.AllAtOnce
  6646  	}
  6647  	return p
  6648  }
  6649  
  6650  // NextRollingEval creates an evaluation to followup this eval for rolling updates
  6651  func (e *Evaluation) NextRollingEval(wait time.Duration) *Evaluation {
  6652  	return &Evaluation{
  6653  		ID:             uuid.Generate(),
  6654  		Namespace:      e.Namespace,
  6655  		Priority:       e.Priority,
  6656  		Type:           e.Type,
  6657  		TriggeredBy:    EvalTriggerRollingUpdate,
  6658  		JobID:          e.JobID,
  6659  		JobModifyIndex: e.JobModifyIndex,
  6660  		Status:         EvalStatusPending,
  6661  		Wait:           wait,
  6662  		PreviousEval:   e.ID,
  6663  	}
  6664  }
  6665  
  6666  // CreateBlockedEval creates a blocked evaluation to followup this eval to place any
  6667  // failed allocations. It takes the classes marked explicitly eligible or
  6668  // ineligible, whether the job has escaped computed node classes and whether the
  6669  // quota limit was reached.
  6670  func (e *Evaluation) CreateBlockedEval(classEligibility map[string]bool,
  6671  	escaped bool, quotaReached string) *Evaluation {
  6672  
  6673  	return &Evaluation{
  6674  		ID:                   uuid.Generate(),
  6675  		Namespace:            e.Namespace,
  6676  		Priority:             e.Priority,
  6677  		Type:                 e.Type,
  6678  		TriggeredBy:          EvalTriggerQueuedAllocs,
  6679  		JobID:                e.JobID,
  6680  		JobModifyIndex:       e.JobModifyIndex,
  6681  		Status:               EvalStatusBlocked,
  6682  		PreviousEval:         e.ID,
  6683  		ClassEligibility:     classEligibility,
  6684  		EscapedComputedClass: escaped,
  6685  		QuotaLimitReached:    quotaReached,
  6686  	}
  6687  }
  6688  
  6689  // CreateFailedFollowUpEval creates a follow up evaluation when the current one
  6690  // has been marked as failed because it has hit the delivery limit and will not
  6691  // be retried by the eval_broker.
  6692  func (e *Evaluation) CreateFailedFollowUpEval(wait time.Duration) *Evaluation {
  6693  	return &Evaluation{
  6694  		ID:             uuid.Generate(),
  6695  		Namespace:      e.Namespace,
  6696  		Priority:       e.Priority,
  6697  		Type:           e.Type,
  6698  		TriggeredBy:    EvalTriggerFailedFollowUp,
  6699  		JobID:          e.JobID,
  6700  		JobModifyIndex: e.JobModifyIndex,
  6701  		Status:         EvalStatusPending,
  6702  		Wait:           wait,
  6703  		PreviousEval:   e.ID,
  6704  	}
  6705  }
  6706  
  6707  // Plan is used to submit a commit plan for task allocations. These
  6708  // are submitted to the leader which verifies that resources have
  6709  // not been overcommitted before admitting the plan.
  6710  type Plan struct {
  6711  	// EvalID is the evaluation ID this plan is associated with
  6712  	EvalID string
  6713  
  6714  	// EvalToken is used to prevent a split-brain processing of
  6715  	// an evaluation. There should only be a single scheduler running
  6716  	// an Eval at a time, but this could be violated after a leadership
  6717  	// transition. This unique token is used to reject plans that are
  6718  	// being submitted from a different leader.
  6719  	EvalToken string
  6720  
  6721  	// Priority is the priority of the upstream job
  6722  	Priority int
  6723  
  6724  	// AllAtOnce is used to control if incremental scheduling of task groups
  6725  	// is allowed or if we must do a gang scheduling of the entire job.
  6726  	// If this is false, a plan may be partially applied. Otherwise, the
  6727  	// entire plan must be able to make progress.
  6728  	AllAtOnce bool
  6729  
  6730  	// Job is the parent job of all the allocations in the Plan.
  6731  	// Since a Plan only involves a single Job, we can reduce the size
  6732  	// of the plan by only including it once.
  6733  	Job *Job
  6734  
  6735  	// NodeUpdate contains all the allocations for each node. For each node,
  6736  	// this is a list of the allocations to update to either stop or evict.
  6737  	NodeUpdate map[string][]*Allocation
  6738  
  6739  	// NodeAllocation contains all the allocations for each node.
  6740  	// The evicts must be considered prior to the allocations.
  6741  	NodeAllocation map[string][]*Allocation
  6742  
  6743  	// Annotations contains annotations by the scheduler to be used by operators
  6744  	// to understand the decisions made by the scheduler.
  6745  	Annotations *PlanAnnotations
  6746  
  6747  	// Deployment is the deployment created or updated by the scheduler that
  6748  	// should be applied by the planner.
  6749  	Deployment *Deployment
  6750  
  6751  	// DeploymentUpdates is a set of status updates to apply to the given
  6752  	// deployments. This allows the scheduler to cancel any unneeded deployment
  6753  	// because the job is stopped or the update block is removed.
  6754  	DeploymentUpdates []*DeploymentStatusUpdate
  6755  }
  6756  
  6757  // AppendUpdate marks the allocation for eviction. The clientStatus of the
  6758  // allocation may be optionally set by passing in a non-empty value.
  6759  func (p *Plan) AppendUpdate(alloc *Allocation, desiredStatus, desiredDesc, clientStatus string) {
  6760  	newAlloc := new(Allocation)
  6761  	*newAlloc = *alloc
  6762  
  6763  	// If the job is not set in the plan we are deregistering a job so we
  6764  	// extract the job from the allocation.
  6765  	if p.Job == nil && newAlloc.Job != nil {
  6766  		p.Job = newAlloc.Job
  6767  	}
  6768  
  6769  	// Normalize the job
  6770  	newAlloc.Job = nil
  6771  
  6772  	// Strip the resources as it can be rebuilt.
  6773  	newAlloc.Resources = nil
  6774  
  6775  	newAlloc.DesiredStatus = desiredStatus
  6776  	newAlloc.DesiredDescription = desiredDesc
  6777  
  6778  	if clientStatus != "" {
  6779  		newAlloc.ClientStatus = clientStatus
  6780  	}
  6781  
  6782  	node := alloc.NodeID
  6783  	existing := p.NodeUpdate[node]
  6784  	p.NodeUpdate[node] = append(existing, newAlloc)
  6785  }
  6786  
  6787  func (p *Plan) PopUpdate(alloc *Allocation) {
  6788  	existing := p.NodeUpdate[alloc.NodeID]
  6789  	n := len(existing)
  6790  	if n > 0 && existing[n-1].ID == alloc.ID {
  6791  		existing = existing[:n-1]
  6792  		if len(existing) > 0 {
  6793  			p.NodeUpdate[alloc.NodeID] = existing
  6794  		} else {
  6795  			delete(p.NodeUpdate, alloc.NodeID)
  6796  		}
  6797  	}
  6798  }
  6799  
  6800  func (p *Plan) AppendAlloc(alloc *Allocation) {
  6801  	node := alloc.NodeID
  6802  	existing := p.NodeAllocation[node]
  6803  
  6804  	// Normalize the job
  6805  	alloc.Job = nil
  6806  
  6807  	p.NodeAllocation[node] = append(existing, alloc)
  6808  }
  6809  
  6810  // IsNoOp checks if this plan would do nothing
  6811  func (p *Plan) IsNoOp() bool {
  6812  	return len(p.NodeUpdate) == 0 &&
  6813  		len(p.NodeAllocation) == 0 &&
  6814  		p.Deployment == nil &&
  6815  		len(p.DeploymentUpdates) == 0
  6816  }
  6817  
  6818  // PlanResult is the result of a plan submitted to the leader.
  6819  type PlanResult struct {
  6820  	// NodeUpdate contains all the updates that were committed.
  6821  	NodeUpdate map[string][]*Allocation
  6822  
  6823  	// NodeAllocation contains all the allocations that were committed.
  6824  	NodeAllocation map[string][]*Allocation
  6825  
  6826  	// Deployment is the deployment that was committed.
  6827  	Deployment *Deployment
  6828  
  6829  	// DeploymentUpdates is the set of deployment updates that were committed.
  6830  	DeploymentUpdates []*DeploymentStatusUpdate
  6831  
  6832  	// RefreshIndex is the index the worker should refresh state up to.
  6833  	// This allows all evictions and allocations to be materialized.
  6834  	// If any allocations were rejected due to stale data (node state,
  6835  	// over committed) this can be used to force a worker refresh.
  6836  	RefreshIndex uint64
  6837  
  6838  	// AllocIndex is the Raft index in which the evictions and
  6839  	// allocations took place. This is used for the write index.
  6840  	AllocIndex uint64
  6841  }
  6842  
  6843  // IsNoOp checks if this plan result would do nothing
  6844  func (p *PlanResult) IsNoOp() bool {
  6845  	return len(p.NodeUpdate) == 0 && len(p.NodeAllocation) == 0 &&
  6846  		len(p.DeploymentUpdates) == 0 && p.Deployment == nil
  6847  }
  6848  
  6849  // FullCommit is used to check if all the allocations in a plan
  6850  // were committed as part of the result. Returns if there was
  6851  // a match, and the number of expected and actual allocations.
  6852  func (p *PlanResult) FullCommit(plan *Plan) (bool, int, int) {
  6853  	expected := 0
  6854  	actual := 0
  6855  	for name, allocList := range plan.NodeAllocation {
  6856  		didAlloc, _ := p.NodeAllocation[name]
  6857  		expected += len(allocList)
  6858  		actual += len(didAlloc)
  6859  	}
  6860  	return actual == expected, expected, actual
  6861  }
  6862  
  6863  // PlanAnnotations holds annotations made by the scheduler to give further debug
  6864  // information to operators.
  6865  type PlanAnnotations struct {
  6866  	// DesiredTGUpdates is the set of desired updates per task group.
  6867  	DesiredTGUpdates map[string]*DesiredUpdates
  6868  }
  6869  
  6870  // DesiredUpdates is the set of changes the scheduler would like to make given
  6871  // sufficient resources and cluster capacity.
  6872  type DesiredUpdates struct {
  6873  	Ignore            uint64
  6874  	Place             uint64
  6875  	Migrate           uint64
  6876  	Stop              uint64
  6877  	InPlaceUpdate     uint64
  6878  	DestructiveUpdate uint64
  6879  	Canary            uint64
  6880  }
  6881  
  6882  func (d *DesiredUpdates) GoString() string {
  6883  	return fmt.Sprintf("(place %d) (inplace %d) (destructive %d) (stop %d) (migrate %d) (ignore %d) (canary %d)",
  6884  		d.Place, d.InPlaceUpdate, d.DestructiveUpdate, d.Stop, d.Migrate, d.Ignore, d.Canary)
  6885  }
  6886  
  6887  // msgpackHandle is a shared handle for encoding/decoding of structs
  6888  var MsgpackHandle = func() *codec.MsgpackHandle {
  6889  	h := &codec.MsgpackHandle{RawToString: true}
  6890  
  6891  	// Sets the default type for decoding a map into a nil interface{}.
  6892  	// This is necessary in particular because we store the driver configs as a
  6893  	// nil interface{}.
  6894  	h.MapType = reflect.TypeOf(map[string]interface{}(nil))
  6895  	return h
  6896  }()
  6897  
  6898  var (
  6899  	// JsonHandle and JsonHandlePretty are the codec handles to JSON encode
  6900  	// structs. The pretty handle will add indents for easier human consumption.
  6901  	JsonHandle = &codec.JsonHandle{
  6902  		HTMLCharsAsIs: true,
  6903  	}
  6904  	JsonHandlePretty = &codec.JsonHandle{
  6905  		HTMLCharsAsIs: true,
  6906  		Indent:        4,
  6907  	}
  6908  )
  6909  
  6910  // TODO Figure out if we can remove this. This is our fork that is just way
  6911  // behind. I feel like its original purpose was to pin at a stable version but
  6912  // now we can accomplish this with vendoring.
  6913  var HashiMsgpackHandle = func() *hcodec.MsgpackHandle {
  6914  	h := &hcodec.MsgpackHandle{RawToString: true}
  6915  
  6916  	// Sets the default type for decoding a map into a nil interface{}.
  6917  	// This is necessary in particular because we store the driver configs as a
  6918  	// nil interface{}.
  6919  	h.MapType = reflect.TypeOf(map[string]interface{}(nil))
  6920  	return h
  6921  }()
  6922  
  6923  // Decode is used to decode a MsgPack encoded object
  6924  func Decode(buf []byte, out interface{}) error {
  6925  	return codec.NewDecoder(bytes.NewReader(buf), MsgpackHandle).Decode(out)
  6926  }
  6927  
  6928  // Encode is used to encode a MsgPack object with type prefix
  6929  func Encode(t MessageType, msg interface{}) ([]byte, error) {
  6930  	var buf bytes.Buffer
  6931  	buf.WriteByte(uint8(t))
  6932  	err := codec.NewEncoder(&buf, MsgpackHandle).Encode(msg)
  6933  	return buf.Bytes(), err
  6934  }
  6935  
  6936  // KeyringResponse is a unified key response and can be used for install,
  6937  // remove, use, as well as listing key queries.
  6938  type KeyringResponse struct {
  6939  	Messages map[string]string
  6940  	Keys     map[string]int
  6941  	NumNodes int
  6942  }
  6943  
  6944  // KeyringRequest is request objects for serf key operations.
  6945  type KeyringRequest struct {
  6946  	Key string
  6947  }
  6948  
  6949  // RecoverableError wraps an error and marks whether it is recoverable and could
  6950  // be retried or it is fatal.
  6951  type RecoverableError struct {
  6952  	Err         string
  6953  	Recoverable bool
  6954  }
  6955  
  6956  // NewRecoverableError is used to wrap an error and mark it as recoverable or
  6957  // not.
  6958  func NewRecoverableError(e error, recoverable bool) error {
  6959  	if e == nil {
  6960  		return nil
  6961  	}
  6962  
  6963  	return &RecoverableError{
  6964  		Err:         e.Error(),
  6965  		Recoverable: recoverable,
  6966  	}
  6967  }
  6968  
  6969  // WrapRecoverable wraps an existing error in a new RecoverableError with a new
  6970  // message. If the error was recoverable before the returned error is as well;
  6971  // otherwise it is unrecoverable.
  6972  func WrapRecoverable(msg string, err error) error {
  6973  	return &RecoverableError{Err: msg, Recoverable: IsRecoverable(err)}
  6974  }
  6975  
  6976  func (r *RecoverableError) Error() string {
  6977  	return r.Err
  6978  }
  6979  
  6980  func (r *RecoverableError) IsRecoverable() bool {
  6981  	return r.Recoverable
  6982  }
  6983  
  6984  // Recoverable is an interface for errors to implement to indicate whether or
  6985  // not they are fatal or recoverable.
  6986  type Recoverable interface {
  6987  	error
  6988  	IsRecoverable() bool
  6989  }
  6990  
  6991  // IsRecoverable returns true if error is a RecoverableError with
  6992  // Recoverable=true. Otherwise false is returned.
  6993  func IsRecoverable(e error) bool {
  6994  	if re, ok := e.(Recoverable); ok {
  6995  		return re.IsRecoverable()
  6996  	}
  6997  	return false
  6998  }
  6999  
  7000  // WrappedServerError wraps an error and satisfies
  7001  // both the Recoverable and the ServerSideError interfaces
  7002  type WrappedServerError struct {
  7003  	Err error
  7004  }
  7005  
  7006  // NewWrappedServerError is used to create a wrapped server side error
  7007  func NewWrappedServerError(e error) error {
  7008  	return &WrappedServerError{
  7009  		Err: e,
  7010  	}
  7011  }
  7012  
  7013  func (r *WrappedServerError) IsRecoverable() bool {
  7014  	return IsRecoverable(r.Err)
  7015  }
  7016  
  7017  func (r *WrappedServerError) Error() string {
  7018  	return r.Err.Error()
  7019  }
  7020  
  7021  func (r *WrappedServerError) IsServerSide() bool {
  7022  	return true
  7023  }
  7024  
  7025  // ServerSideError is an interface for errors to implement to indicate
  7026  // errors occurring after the request makes it to a server
  7027  type ServerSideError interface {
  7028  	error
  7029  	IsServerSide() bool
  7030  }
  7031  
  7032  // IsServerSide returns true if error is a wrapped
  7033  // server side error
  7034  func IsServerSide(e error) bool {
  7035  	if se, ok := e.(ServerSideError); ok {
  7036  		return se.IsServerSide()
  7037  	}
  7038  	return false
  7039  }
  7040  
  7041  // ACLPolicy is used to represent an ACL policy
  7042  type ACLPolicy struct {
  7043  	Name        string // Unique name
  7044  	Description string // Human readable
  7045  	Rules       string // HCL or JSON format
  7046  	Hash        []byte
  7047  	CreateIndex uint64
  7048  	ModifyIndex uint64
  7049  }
  7050  
  7051  // SetHash is used to compute and set the hash of the ACL policy
  7052  func (c *ACLPolicy) SetHash() []byte {
  7053  	// Initialize a 256bit Blake2 hash (32 bytes)
  7054  	hash, err := blake2b.New256(nil)
  7055  	if err != nil {
  7056  		panic(err)
  7057  	}
  7058  
  7059  	// Write all the user set fields
  7060  	hash.Write([]byte(c.Name))
  7061  	hash.Write([]byte(c.Description))
  7062  	hash.Write([]byte(c.Rules))
  7063  
  7064  	// Finalize the hash
  7065  	hashVal := hash.Sum(nil)
  7066  
  7067  	// Set and return the hash
  7068  	c.Hash = hashVal
  7069  	return hashVal
  7070  }
  7071  
  7072  func (a *ACLPolicy) Stub() *ACLPolicyListStub {
  7073  	return &ACLPolicyListStub{
  7074  		Name:        a.Name,
  7075  		Description: a.Description,
  7076  		Hash:        a.Hash,
  7077  		CreateIndex: a.CreateIndex,
  7078  		ModifyIndex: a.ModifyIndex,
  7079  	}
  7080  }
  7081  
  7082  func (a *ACLPolicy) Validate() error {
  7083  	var mErr multierror.Error
  7084  	if !validPolicyName.MatchString(a.Name) {
  7085  		err := fmt.Errorf("invalid name '%s'", a.Name)
  7086  		mErr.Errors = append(mErr.Errors, err)
  7087  	}
  7088  	if _, err := acl.Parse(a.Rules); err != nil {
  7089  		err = fmt.Errorf("failed to parse rules: %v", err)
  7090  		mErr.Errors = append(mErr.Errors, err)
  7091  	}
  7092  	if len(a.Description) > maxPolicyDescriptionLength {
  7093  		err := fmt.Errorf("description longer than %d", maxPolicyDescriptionLength)
  7094  		mErr.Errors = append(mErr.Errors, err)
  7095  	}
  7096  	return mErr.ErrorOrNil()
  7097  }
  7098  
  7099  // ACLPolicyListStub is used to for listing ACL policies
  7100  type ACLPolicyListStub struct {
  7101  	Name        string
  7102  	Description string
  7103  	Hash        []byte
  7104  	CreateIndex uint64
  7105  	ModifyIndex uint64
  7106  }
  7107  
  7108  // ACLPolicyListRequest is used to request a list of policies
  7109  type ACLPolicyListRequest struct {
  7110  	QueryOptions
  7111  }
  7112  
  7113  // ACLPolicySpecificRequest is used to query a specific policy
  7114  type ACLPolicySpecificRequest struct {
  7115  	Name string
  7116  	QueryOptions
  7117  }
  7118  
  7119  // ACLPolicySetRequest is used to query a set of policies
  7120  type ACLPolicySetRequest struct {
  7121  	Names []string
  7122  	QueryOptions
  7123  }
  7124  
  7125  // ACLPolicyListResponse is used for a list request
  7126  type ACLPolicyListResponse struct {
  7127  	Policies []*ACLPolicyListStub
  7128  	QueryMeta
  7129  }
  7130  
  7131  // SingleACLPolicyResponse is used to return a single policy
  7132  type SingleACLPolicyResponse struct {
  7133  	Policy *ACLPolicy
  7134  	QueryMeta
  7135  }
  7136  
  7137  // ACLPolicySetResponse is used to return a set of policies
  7138  type ACLPolicySetResponse struct {
  7139  	Policies map[string]*ACLPolicy
  7140  	QueryMeta
  7141  }
  7142  
  7143  // ACLPolicyDeleteRequest is used to delete a set of policies
  7144  type ACLPolicyDeleteRequest struct {
  7145  	Names []string
  7146  	WriteRequest
  7147  }
  7148  
  7149  // ACLPolicyUpsertRequest is used to upsert a set of policies
  7150  type ACLPolicyUpsertRequest struct {
  7151  	Policies []*ACLPolicy
  7152  	WriteRequest
  7153  }
  7154  
  7155  // ACLToken represents a client token which is used to Authenticate
  7156  type ACLToken struct {
  7157  	AccessorID  string   // Public Accessor ID (UUID)
  7158  	SecretID    string   // Secret ID, private (UUID)
  7159  	Name        string   // Human friendly name
  7160  	Type        string   // Client or Management
  7161  	Policies    []string // Policies this token ties to
  7162  	Global      bool     // Global or Region local
  7163  	Hash        []byte
  7164  	CreateTime  time.Time // Time of creation
  7165  	CreateIndex uint64
  7166  	ModifyIndex uint64
  7167  }
  7168  
  7169  var (
  7170  	// AnonymousACLToken is used no SecretID is provided, and the
  7171  	// request is made anonymously.
  7172  	AnonymousACLToken = &ACLToken{
  7173  		AccessorID: "anonymous",
  7174  		Name:       "Anonymous Token",
  7175  		Type:       ACLClientToken,
  7176  		Policies:   []string{"anonymous"},
  7177  		Global:     false,
  7178  	}
  7179  )
  7180  
  7181  type ACLTokenListStub struct {
  7182  	AccessorID  string
  7183  	Name        string
  7184  	Type        string
  7185  	Policies    []string
  7186  	Global      bool
  7187  	Hash        []byte
  7188  	CreateTime  time.Time
  7189  	CreateIndex uint64
  7190  	ModifyIndex uint64
  7191  }
  7192  
  7193  // SetHash is used to compute and set the hash of the ACL token
  7194  func (a *ACLToken) SetHash() []byte {
  7195  	// Initialize a 256bit Blake2 hash (32 bytes)
  7196  	hash, err := blake2b.New256(nil)
  7197  	if err != nil {
  7198  		panic(err)
  7199  	}
  7200  
  7201  	// Write all the user set fields
  7202  	hash.Write([]byte(a.Name))
  7203  	hash.Write([]byte(a.Type))
  7204  	for _, policyName := range a.Policies {
  7205  		hash.Write([]byte(policyName))
  7206  	}
  7207  	if a.Global {
  7208  		hash.Write([]byte("global"))
  7209  	} else {
  7210  		hash.Write([]byte("local"))
  7211  	}
  7212  
  7213  	// Finalize the hash
  7214  	hashVal := hash.Sum(nil)
  7215  
  7216  	// Set and return the hash
  7217  	a.Hash = hashVal
  7218  	return hashVal
  7219  }
  7220  
  7221  func (a *ACLToken) Stub() *ACLTokenListStub {
  7222  	return &ACLTokenListStub{
  7223  		AccessorID:  a.AccessorID,
  7224  		Name:        a.Name,
  7225  		Type:        a.Type,
  7226  		Policies:    a.Policies,
  7227  		Global:      a.Global,
  7228  		Hash:        a.Hash,
  7229  		CreateTime:  a.CreateTime,
  7230  		CreateIndex: a.CreateIndex,
  7231  		ModifyIndex: a.ModifyIndex,
  7232  	}
  7233  }
  7234  
  7235  // Validate is used to sanity check a token
  7236  func (a *ACLToken) Validate() error {
  7237  	var mErr multierror.Error
  7238  	if len(a.Name) > maxTokenNameLength {
  7239  		mErr.Errors = append(mErr.Errors, fmt.Errorf("token name too long"))
  7240  	}
  7241  	switch a.Type {
  7242  	case ACLClientToken:
  7243  		if len(a.Policies) == 0 {
  7244  			mErr.Errors = append(mErr.Errors, fmt.Errorf("client token missing policies"))
  7245  		}
  7246  	case ACLManagementToken:
  7247  		if len(a.Policies) != 0 {
  7248  			mErr.Errors = append(mErr.Errors, fmt.Errorf("management token cannot be associated with policies"))
  7249  		}
  7250  	default:
  7251  		mErr.Errors = append(mErr.Errors, fmt.Errorf("token type must be client or management"))
  7252  	}
  7253  	return mErr.ErrorOrNil()
  7254  }
  7255  
  7256  // PolicySubset checks if a given set of policies is a subset of the token
  7257  func (a *ACLToken) PolicySubset(policies []string) bool {
  7258  	// Hot-path the management tokens, superset of all policies.
  7259  	if a.Type == ACLManagementToken {
  7260  		return true
  7261  	}
  7262  	associatedPolicies := make(map[string]struct{}, len(a.Policies))
  7263  	for _, policy := range a.Policies {
  7264  		associatedPolicies[policy] = struct{}{}
  7265  	}
  7266  	for _, policy := range policies {
  7267  		if _, ok := associatedPolicies[policy]; !ok {
  7268  			return false
  7269  		}
  7270  	}
  7271  	return true
  7272  }
  7273  
  7274  // ACLTokenListRequest is used to request a list of tokens
  7275  type ACLTokenListRequest struct {
  7276  	GlobalOnly bool
  7277  	QueryOptions
  7278  }
  7279  
  7280  // ACLTokenSpecificRequest is used to query a specific token
  7281  type ACLTokenSpecificRequest struct {
  7282  	AccessorID string
  7283  	QueryOptions
  7284  }
  7285  
  7286  // ACLTokenSetRequest is used to query a set of tokens
  7287  type ACLTokenSetRequest struct {
  7288  	AccessorIDS []string
  7289  	QueryOptions
  7290  }
  7291  
  7292  // ACLTokenListResponse is used for a list request
  7293  type ACLTokenListResponse struct {
  7294  	Tokens []*ACLTokenListStub
  7295  	QueryMeta
  7296  }
  7297  
  7298  // SingleACLTokenResponse is used to return a single token
  7299  type SingleACLTokenResponse struct {
  7300  	Token *ACLToken
  7301  	QueryMeta
  7302  }
  7303  
  7304  // ACLTokenSetResponse is used to return a set of token
  7305  type ACLTokenSetResponse struct {
  7306  	Tokens map[string]*ACLToken // Keyed by Accessor ID
  7307  	QueryMeta
  7308  }
  7309  
  7310  // ResolveACLTokenRequest is used to resolve a specific token
  7311  type ResolveACLTokenRequest struct {
  7312  	SecretID string
  7313  	QueryOptions
  7314  }
  7315  
  7316  // ResolveACLTokenResponse is used to resolve a single token
  7317  type ResolveACLTokenResponse struct {
  7318  	Token *ACLToken
  7319  	QueryMeta
  7320  }
  7321  
  7322  // ACLTokenDeleteRequest is used to delete a set of tokens
  7323  type ACLTokenDeleteRequest struct {
  7324  	AccessorIDs []string
  7325  	WriteRequest
  7326  }
  7327  
  7328  // ACLTokenBootstrapRequest is used to bootstrap ACLs
  7329  type ACLTokenBootstrapRequest struct {
  7330  	Token      *ACLToken // Not client specifiable
  7331  	ResetIndex uint64    // Reset index is used to clear the bootstrap token
  7332  	WriteRequest
  7333  }
  7334  
  7335  // ACLTokenUpsertRequest is used to upsert a set of tokens
  7336  type ACLTokenUpsertRequest struct {
  7337  	Tokens []*ACLToken
  7338  	WriteRequest
  7339  }
  7340  
  7341  // ACLTokenUpsertResponse is used to return from an ACLTokenUpsertRequest
  7342  type ACLTokenUpsertResponse struct {
  7343  	Tokens []*ACLToken
  7344  	WriteMeta
  7345  }