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