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