github.com/hspak/nomad@v0.7.2-0.20180309000617-bc4ae22a39a5/api/jobs.go (about)

     1  package api
     2  
     3  import (
     4  	"fmt"
     5  	"net/url"
     6  	"sort"
     7  	"strconv"
     8  	"time"
     9  
    10  	"github.com/gorhill/cronexpr"
    11  	"github.com/hashicorp/nomad/helper"
    12  )
    13  
    14  const (
    15  	// JobTypeService indicates a long-running processes
    16  	JobTypeService = "service"
    17  
    18  	// JobTypeBatch indicates a short-lived process
    19  	JobTypeBatch = "batch"
    20  
    21  	// PeriodicSpecCron is used for a cron spec.
    22  	PeriodicSpecCron = "cron"
    23  
    24  	// DefaultNamespace is the default namespace.
    25  	DefaultNamespace = "default"
    26  )
    27  
    28  const (
    29  	// RegisterEnforceIndexErrPrefix is the prefix to use in errors caused by
    30  	// enforcing the job modify index during registers.
    31  	RegisterEnforceIndexErrPrefix = "Enforcing job modify index"
    32  )
    33  
    34  // Jobs is used to access the job-specific endpoints.
    35  type Jobs struct {
    36  	client *Client
    37  }
    38  
    39  // Jobs returns a handle on the jobs endpoints.
    40  func (c *Client) Jobs() *Jobs {
    41  	return &Jobs{client: c}
    42  }
    43  
    44  func (j *Jobs) Validate(job *Job, q *WriteOptions) (*JobValidateResponse, *WriteMeta, error) {
    45  	var resp JobValidateResponse
    46  	req := &JobValidateRequest{Job: job}
    47  	if q != nil {
    48  		req.WriteRequest = WriteRequest{Region: q.Region}
    49  	}
    50  	wm, err := j.client.write("/v1/validate/job", req, &resp, q)
    51  	return &resp, wm, err
    52  }
    53  
    54  // RegisterOptions is used to pass through job registration parameters
    55  type RegisterOptions struct {
    56  	EnforceIndex   bool
    57  	ModifyIndex    uint64
    58  	PolicyOverride bool
    59  }
    60  
    61  // Register is used to register a new job. It returns the ID
    62  // of the evaluation, along with any errors encountered.
    63  func (j *Jobs) Register(job *Job, q *WriteOptions) (*JobRegisterResponse, *WriteMeta, error) {
    64  	return j.RegisterOpts(job, nil, q)
    65  }
    66  
    67  // EnforceRegister is used to register a job enforcing its job modify index.
    68  func (j *Jobs) EnforceRegister(job *Job, modifyIndex uint64, q *WriteOptions) (*JobRegisterResponse, *WriteMeta, error) {
    69  	opts := RegisterOptions{EnforceIndex: true, ModifyIndex: modifyIndex}
    70  	return j.RegisterOpts(job, &opts, q)
    71  }
    72  
    73  // Register is used to register a new job. It returns the ID
    74  // of the evaluation, along with any errors encountered.
    75  func (j *Jobs) RegisterOpts(job *Job, opts *RegisterOptions, q *WriteOptions) (*JobRegisterResponse, *WriteMeta, error) {
    76  	// Format the request
    77  	req := &RegisterJobRequest{
    78  		Job: job,
    79  	}
    80  	if opts != nil {
    81  		if opts.EnforceIndex {
    82  			req.EnforceIndex = true
    83  			req.JobModifyIndex = opts.ModifyIndex
    84  		}
    85  		if opts.PolicyOverride {
    86  			req.PolicyOverride = true
    87  		}
    88  	}
    89  
    90  	var resp JobRegisterResponse
    91  	wm, err := j.client.write("/v1/jobs", req, &resp, q)
    92  	if err != nil {
    93  		return nil, nil, err
    94  	}
    95  	return &resp, wm, nil
    96  }
    97  
    98  // List is used to list all of the existing jobs.
    99  func (j *Jobs) List(q *QueryOptions) ([]*JobListStub, *QueryMeta, error) {
   100  	var resp []*JobListStub
   101  	qm, err := j.client.query("/v1/jobs", &resp, q)
   102  	if err != nil {
   103  		return nil, qm, err
   104  	}
   105  	sort.Sort(JobIDSort(resp))
   106  	return resp, qm, nil
   107  }
   108  
   109  // PrefixList is used to list all existing jobs that match the prefix.
   110  func (j *Jobs) PrefixList(prefix string) ([]*JobListStub, *QueryMeta, error) {
   111  	return j.List(&QueryOptions{Prefix: prefix})
   112  }
   113  
   114  // Info is used to retrieve information about a particular
   115  // job given its unique ID.
   116  func (j *Jobs) Info(jobID string, q *QueryOptions) (*Job, *QueryMeta, error) {
   117  	var resp Job
   118  	qm, err := j.client.query("/v1/job/"+jobID, &resp, q)
   119  	if err != nil {
   120  		return nil, nil, err
   121  	}
   122  	return &resp, qm, nil
   123  }
   124  
   125  // Versions is used to retrieve all versions of a particular job given its
   126  // unique ID.
   127  func (j *Jobs) Versions(jobID string, diffs bool, q *QueryOptions) ([]*Job, []*JobDiff, *QueryMeta, error) {
   128  	var resp JobVersionsResponse
   129  	qm, err := j.client.query(fmt.Sprintf("/v1/job/%s/versions?diffs=%v", jobID, diffs), &resp, q)
   130  	if err != nil {
   131  		return nil, nil, nil, err
   132  	}
   133  	return resp.Versions, resp.Diffs, qm, nil
   134  }
   135  
   136  // Allocations is used to return the allocs for a given job ID.
   137  func (j *Jobs) Allocations(jobID string, allAllocs bool, q *QueryOptions) ([]*AllocationListStub, *QueryMeta, error) {
   138  	var resp []*AllocationListStub
   139  	u, err := url.Parse("/v1/job/" + jobID + "/allocations")
   140  	if err != nil {
   141  		return nil, nil, err
   142  	}
   143  
   144  	v := u.Query()
   145  	v.Add("all", strconv.FormatBool(allAllocs))
   146  	u.RawQuery = v.Encode()
   147  
   148  	qm, err := j.client.query(u.String(), &resp, q)
   149  	if err != nil {
   150  		return nil, nil, err
   151  	}
   152  	sort.Sort(AllocIndexSort(resp))
   153  	return resp, qm, nil
   154  }
   155  
   156  // Deployments is used to query the deployments associated with the given job
   157  // ID.
   158  func (j *Jobs) Deployments(jobID string, q *QueryOptions) ([]*Deployment, *QueryMeta, error) {
   159  	var resp []*Deployment
   160  	qm, err := j.client.query("/v1/job/"+jobID+"/deployments", &resp, q)
   161  	if err != nil {
   162  		return nil, nil, err
   163  	}
   164  	sort.Sort(DeploymentIndexSort(resp))
   165  	return resp, qm, nil
   166  }
   167  
   168  // LatestDeployment is used to query for the latest deployment associated with
   169  // the given job ID.
   170  func (j *Jobs) LatestDeployment(jobID string, q *QueryOptions) (*Deployment, *QueryMeta, error) {
   171  	var resp *Deployment
   172  	qm, err := j.client.query("/v1/job/"+jobID+"/deployment", &resp, q)
   173  	if err != nil {
   174  		return nil, nil, err
   175  	}
   176  	return resp, qm, nil
   177  }
   178  
   179  // Evaluations is used to query the evaluations associated with the given job
   180  // ID.
   181  func (j *Jobs) Evaluations(jobID string, q *QueryOptions) ([]*Evaluation, *QueryMeta, error) {
   182  	var resp []*Evaluation
   183  	qm, err := j.client.query("/v1/job/"+jobID+"/evaluations", &resp, q)
   184  	if err != nil {
   185  		return nil, nil, err
   186  	}
   187  	sort.Sort(EvalIndexSort(resp))
   188  	return resp, qm, nil
   189  }
   190  
   191  // Deregister is used to remove an existing job. If purge is set to true, the job
   192  // is deregistered and purged from the system versus still being queryable and
   193  // eventually GC'ed from the system. Most callers should not specify purge.
   194  func (j *Jobs) Deregister(jobID string, purge bool, q *WriteOptions) (string, *WriteMeta, error) {
   195  	var resp JobDeregisterResponse
   196  	wm, err := j.client.delete(fmt.Sprintf("/v1/job/%v?purge=%t", jobID, purge), &resp, q)
   197  	if err != nil {
   198  		return "", nil, err
   199  	}
   200  	return resp.EvalID, wm, nil
   201  }
   202  
   203  // ForceEvaluate is used to force-evaluate an existing job.
   204  func (j *Jobs) ForceEvaluate(jobID string, q *WriteOptions) (string, *WriteMeta, error) {
   205  	var resp JobRegisterResponse
   206  	wm, err := j.client.write("/v1/job/"+jobID+"/evaluate", nil, &resp, q)
   207  	if err != nil {
   208  		return "", nil, err
   209  	}
   210  	return resp.EvalID, wm, nil
   211  }
   212  
   213  // PeriodicForce spawns a new instance of the periodic job and returns the eval ID
   214  func (j *Jobs) PeriodicForce(jobID string, q *WriteOptions) (string, *WriteMeta, error) {
   215  	var resp periodicForceResponse
   216  	wm, err := j.client.write("/v1/job/"+jobID+"/periodic/force", nil, &resp, q)
   217  	if err != nil {
   218  		return "", nil, err
   219  	}
   220  	return resp.EvalID, wm, nil
   221  }
   222  
   223  // PlanOptions is used to pass through job planning parameters
   224  type PlanOptions struct {
   225  	Diff           bool
   226  	PolicyOverride bool
   227  }
   228  
   229  func (j *Jobs) Plan(job *Job, diff bool, q *WriteOptions) (*JobPlanResponse, *WriteMeta, error) {
   230  	opts := PlanOptions{Diff: diff}
   231  	return j.PlanOpts(job, &opts, q)
   232  }
   233  
   234  func (j *Jobs) PlanOpts(job *Job, opts *PlanOptions, q *WriteOptions) (*JobPlanResponse, *WriteMeta, error) {
   235  	if job == nil {
   236  		return nil, nil, fmt.Errorf("must pass non-nil job")
   237  	}
   238  
   239  	// Setup the request
   240  	req := &JobPlanRequest{
   241  		Job: job,
   242  	}
   243  	if opts != nil {
   244  		req.Diff = opts.Diff
   245  		req.PolicyOverride = opts.PolicyOverride
   246  	}
   247  
   248  	var resp JobPlanResponse
   249  	wm, err := j.client.write("/v1/job/"+*job.ID+"/plan", req, &resp, q)
   250  	if err != nil {
   251  		return nil, nil, err
   252  	}
   253  	return &resp, wm, nil
   254  }
   255  
   256  func (j *Jobs) Summary(jobID string, q *QueryOptions) (*JobSummary, *QueryMeta, error) {
   257  	var resp JobSummary
   258  	qm, err := j.client.query("/v1/job/"+jobID+"/summary", &resp, q)
   259  	if err != nil {
   260  		return nil, nil, err
   261  	}
   262  	return &resp, qm, nil
   263  }
   264  
   265  func (j *Jobs) Dispatch(jobID string, meta map[string]string,
   266  	payload []byte, q *WriteOptions) (*JobDispatchResponse, *WriteMeta, error) {
   267  	var resp JobDispatchResponse
   268  	req := &JobDispatchRequest{
   269  		JobID:   jobID,
   270  		Meta:    meta,
   271  		Payload: payload,
   272  	}
   273  	wm, err := j.client.write("/v1/job/"+jobID+"/dispatch", req, &resp, q)
   274  	if err != nil {
   275  		return nil, nil, err
   276  	}
   277  	return &resp, wm, nil
   278  }
   279  
   280  // Revert is used to revert the given job to the passed version. If
   281  // enforceVersion is set, the job is only reverted if the current version is at
   282  // the passed version.
   283  func (j *Jobs) Revert(jobID string, version uint64, enforcePriorVersion *uint64,
   284  	q *WriteOptions) (*JobRegisterResponse, *WriteMeta, error) {
   285  
   286  	var resp JobRegisterResponse
   287  	req := &JobRevertRequest{
   288  		JobID:               jobID,
   289  		JobVersion:          version,
   290  		EnforcePriorVersion: enforcePriorVersion,
   291  	}
   292  	wm, err := j.client.write("/v1/job/"+jobID+"/revert", req, &resp, q)
   293  	if err != nil {
   294  		return nil, nil, err
   295  	}
   296  	return &resp, wm, nil
   297  }
   298  
   299  // Stable is used to mark a job version's stability.
   300  func (j *Jobs) Stable(jobID string, version uint64, stable bool,
   301  	q *WriteOptions) (*JobStabilityResponse, *WriteMeta, error) {
   302  
   303  	var resp JobStabilityResponse
   304  	req := &JobStabilityRequest{
   305  		JobID:      jobID,
   306  		JobVersion: version,
   307  		Stable:     stable,
   308  	}
   309  	wm, err := j.client.write("/v1/job/"+jobID+"/stable", req, &resp, q)
   310  	if err != nil {
   311  		return nil, nil, err
   312  	}
   313  	return &resp, wm, nil
   314  }
   315  
   316  // periodicForceResponse is used to deserialize a force response
   317  type periodicForceResponse struct {
   318  	EvalID string
   319  }
   320  
   321  // UpdateStrategy defines a task groups update strategy.
   322  type UpdateStrategy struct {
   323  	Stagger         *time.Duration `mapstructure:"stagger"`
   324  	MaxParallel     *int           `mapstructure:"max_parallel"`
   325  	HealthCheck     *string        `mapstructure:"health_check"`
   326  	MinHealthyTime  *time.Duration `mapstructure:"min_healthy_time"`
   327  	HealthyDeadline *time.Duration `mapstructure:"healthy_deadline"`
   328  	AutoRevert      *bool          `mapstructure:"auto_revert"`
   329  	Canary          *int           `mapstructure:"canary"`
   330  }
   331  
   332  // DefaultUpdateStrategy provides a baseline that can be used to upgrade
   333  // jobs with the old policy or for populating field defaults.
   334  func DefaultUpdateStrategy() *UpdateStrategy {
   335  	return &UpdateStrategy{
   336  		Stagger:         helper.TimeToPtr(30 * time.Second),
   337  		MaxParallel:     helper.IntToPtr(1),
   338  		HealthCheck:     helper.StringToPtr("checks"),
   339  		MinHealthyTime:  helper.TimeToPtr(10 * time.Second),
   340  		HealthyDeadline: helper.TimeToPtr(5 * time.Minute),
   341  		AutoRevert:      helper.BoolToPtr(false),
   342  		Canary:          helper.IntToPtr(0),
   343  	}
   344  }
   345  
   346  func (u *UpdateStrategy) Copy() *UpdateStrategy {
   347  	if u == nil {
   348  		return nil
   349  	}
   350  
   351  	copy := new(UpdateStrategy)
   352  
   353  	if u.Stagger != nil {
   354  		copy.Stagger = helper.TimeToPtr(*u.Stagger)
   355  	}
   356  
   357  	if u.MaxParallel != nil {
   358  		copy.MaxParallel = helper.IntToPtr(*u.MaxParallel)
   359  	}
   360  
   361  	if u.HealthCheck != nil {
   362  		copy.HealthCheck = helper.StringToPtr(*u.HealthCheck)
   363  	}
   364  
   365  	if u.MinHealthyTime != nil {
   366  		copy.MinHealthyTime = helper.TimeToPtr(*u.MinHealthyTime)
   367  	}
   368  
   369  	if u.HealthyDeadline != nil {
   370  		copy.HealthyDeadline = helper.TimeToPtr(*u.HealthyDeadline)
   371  	}
   372  
   373  	if u.AutoRevert != nil {
   374  		copy.AutoRevert = helper.BoolToPtr(*u.AutoRevert)
   375  	}
   376  
   377  	if u.Canary != nil {
   378  		copy.Canary = helper.IntToPtr(*u.Canary)
   379  	}
   380  
   381  	return copy
   382  }
   383  
   384  func (u *UpdateStrategy) Merge(o *UpdateStrategy) {
   385  	if o == nil {
   386  		return
   387  	}
   388  
   389  	if o.Stagger != nil {
   390  		u.Stagger = helper.TimeToPtr(*o.Stagger)
   391  	}
   392  
   393  	if o.MaxParallel != nil {
   394  		u.MaxParallel = helper.IntToPtr(*o.MaxParallel)
   395  	}
   396  
   397  	if o.HealthCheck != nil {
   398  		u.HealthCheck = helper.StringToPtr(*o.HealthCheck)
   399  	}
   400  
   401  	if o.MinHealthyTime != nil {
   402  		u.MinHealthyTime = helper.TimeToPtr(*o.MinHealthyTime)
   403  	}
   404  
   405  	if o.HealthyDeadline != nil {
   406  		u.HealthyDeadline = helper.TimeToPtr(*o.HealthyDeadline)
   407  	}
   408  
   409  	if o.AutoRevert != nil {
   410  		u.AutoRevert = helper.BoolToPtr(*o.AutoRevert)
   411  	}
   412  
   413  	if o.Canary != nil {
   414  		u.Canary = helper.IntToPtr(*o.Canary)
   415  	}
   416  }
   417  
   418  func (u *UpdateStrategy) Canonicalize() {
   419  	d := DefaultUpdateStrategy()
   420  
   421  	if u.MaxParallel == nil {
   422  		u.MaxParallel = d.MaxParallel
   423  	}
   424  
   425  	if u.Stagger == nil {
   426  		u.Stagger = d.Stagger
   427  	}
   428  
   429  	if u.HealthCheck == nil {
   430  		u.HealthCheck = d.HealthCheck
   431  	}
   432  
   433  	if u.HealthyDeadline == nil {
   434  		u.HealthyDeadline = d.HealthyDeadline
   435  	}
   436  
   437  	if u.MinHealthyTime == nil {
   438  		u.MinHealthyTime = d.MinHealthyTime
   439  	}
   440  
   441  	if u.AutoRevert == nil {
   442  		u.AutoRevert = d.AutoRevert
   443  	}
   444  
   445  	if u.Canary == nil {
   446  		u.Canary = d.Canary
   447  	}
   448  }
   449  
   450  // Empty returns whether the UpdateStrategy is empty or has user defined values.
   451  func (u *UpdateStrategy) Empty() bool {
   452  	if u == nil {
   453  		return true
   454  	}
   455  
   456  	if u.Stagger != nil && *u.Stagger != 0 {
   457  		return false
   458  	}
   459  
   460  	if u.MaxParallel != nil && *u.MaxParallel != 0 {
   461  		return false
   462  	}
   463  
   464  	if u.HealthCheck != nil && *u.HealthCheck != "" {
   465  		return false
   466  	}
   467  
   468  	if u.MinHealthyTime != nil && *u.MinHealthyTime != 0 {
   469  		return false
   470  	}
   471  
   472  	if u.HealthyDeadline != nil && *u.HealthyDeadline != 0 {
   473  		return false
   474  	}
   475  
   476  	if u.AutoRevert != nil && *u.AutoRevert {
   477  		return false
   478  	}
   479  
   480  	if u.Canary != nil && *u.Canary != 0 {
   481  		return false
   482  	}
   483  
   484  	return true
   485  }
   486  
   487  // PeriodicConfig is for serializing periodic config for a job.
   488  type PeriodicConfig struct {
   489  	Enabled         *bool
   490  	Spec            *string
   491  	SpecType        *string
   492  	ProhibitOverlap *bool   `mapstructure:"prohibit_overlap"`
   493  	TimeZone        *string `mapstructure:"time_zone"`
   494  }
   495  
   496  func (p *PeriodicConfig) Canonicalize() {
   497  	if p.Enabled == nil {
   498  		p.Enabled = helper.BoolToPtr(true)
   499  	}
   500  	if p.Spec == nil {
   501  		p.Spec = helper.StringToPtr("")
   502  	}
   503  	if p.SpecType == nil {
   504  		p.SpecType = helper.StringToPtr(PeriodicSpecCron)
   505  	}
   506  	if p.ProhibitOverlap == nil {
   507  		p.ProhibitOverlap = helper.BoolToPtr(false)
   508  	}
   509  	if p.TimeZone == nil || *p.TimeZone == "" {
   510  		p.TimeZone = helper.StringToPtr("UTC")
   511  	}
   512  }
   513  
   514  // Next returns the closest time instant matching the spec that is after the
   515  // passed time. If no matching instance exists, the zero value of time.Time is
   516  // returned. The `time.Location` of the returned value matches that of the
   517  // passed time.
   518  func (p *PeriodicConfig) Next(fromTime time.Time) time.Time {
   519  	if *p.SpecType == PeriodicSpecCron {
   520  		if e, err := cronexpr.Parse(*p.Spec); err == nil {
   521  			return e.Next(fromTime)
   522  		}
   523  	}
   524  
   525  	return time.Time{}
   526  }
   527  
   528  func (p *PeriodicConfig) GetLocation() (*time.Location, error) {
   529  	if p.TimeZone == nil || *p.TimeZone == "" {
   530  		return time.UTC, nil
   531  	}
   532  
   533  	return time.LoadLocation(*p.TimeZone)
   534  }
   535  
   536  // ParameterizedJobConfig is used to configure the parameterized job.
   537  type ParameterizedJobConfig struct {
   538  	Payload      string
   539  	MetaRequired []string `mapstructure:"meta_required"`
   540  	MetaOptional []string `mapstructure:"meta_optional"`
   541  }
   542  
   543  // Job is used to serialize a job.
   544  type Job struct {
   545  	Stop              *bool
   546  	Region            *string
   547  	Namespace         *string
   548  	ID                *string
   549  	ParentID          *string
   550  	Name              *string
   551  	Type              *string
   552  	Priority          *int
   553  	AllAtOnce         *bool `mapstructure:"all_at_once"`
   554  	Datacenters       []string
   555  	Constraints       []*Constraint
   556  	TaskGroups        []*TaskGroup
   557  	Update            *UpdateStrategy
   558  	Periodic          *PeriodicConfig
   559  	ParameterizedJob  *ParameterizedJobConfig
   560  	Payload           []byte
   561  	Reschedule        *ReschedulePolicy
   562  	Meta              map[string]string
   563  	VaultToken        *string `mapstructure:"vault_token"`
   564  	Status            *string
   565  	StatusDescription *string
   566  	Stable            *bool
   567  	Version           *uint64
   568  	SubmitTime        *int64
   569  	CreateIndex       *uint64
   570  	ModifyIndex       *uint64
   571  	JobModifyIndex    *uint64
   572  }
   573  
   574  // IsPeriodic returns whether a job is periodic.
   575  func (j *Job) IsPeriodic() bool {
   576  	return j.Periodic != nil
   577  }
   578  
   579  // IsParameterized returns whether a job is parameterized job.
   580  func (j *Job) IsParameterized() bool {
   581  	return j.ParameterizedJob != nil
   582  }
   583  
   584  func (j *Job) Canonicalize() {
   585  	if j.ID == nil {
   586  		j.ID = helper.StringToPtr("")
   587  	}
   588  	if j.Name == nil {
   589  		j.Name = helper.StringToPtr(*j.ID)
   590  	}
   591  	if j.ParentID == nil {
   592  		j.ParentID = helper.StringToPtr("")
   593  	}
   594  	if j.Namespace == nil {
   595  		j.Namespace = helper.StringToPtr(DefaultNamespace)
   596  	}
   597  	if j.Priority == nil {
   598  		j.Priority = helper.IntToPtr(50)
   599  	}
   600  	if j.Stop == nil {
   601  		j.Stop = helper.BoolToPtr(false)
   602  	}
   603  	if j.Region == nil {
   604  		j.Region = helper.StringToPtr("global")
   605  	}
   606  	if j.Namespace == nil {
   607  		j.Namespace = helper.StringToPtr("default")
   608  	}
   609  	if j.Type == nil {
   610  		j.Type = helper.StringToPtr("service")
   611  	}
   612  	if j.AllAtOnce == nil {
   613  		j.AllAtOnce = helper.BoolToPtr(false)
   614  	}
   615  	if j.VaultToken == nil {
   616  		j.VaultToken = helper.StringToPtr("")
   617  	}
   618  	if j.Status == nil {
   619  		j.Status = helper.StringToPtr("")
   620  	}
   621  	if j.StatusDescription == nil {
   622  		j.StatusDescription = helper.StringToPtr("")
   623  	}
   624  	if j.Stable == nil {
   625  		j.Stable = helper.BoolToPtr(false)
   626  	}
   627  	if j.Version == nil {
   628  		j.Version = helper.Uint64ToPtr(0)
   629  	}
   630  	if j.CreateIndex == nil {
   631  		j.CreateIndex = helper.Uint64ToPtr(0)
   632  	}
   633  	if j.ModifyIndex == nil {
   634  		j.ModifyIndex = helper.Uint64ToPtr(0)
   635  	}
   636  	if j.JobModifyIndex == nil {
   637  		j.JobModifyIndex = helper.Uint64ToPtr(0)
   638  	}
   639  	if j.Periodic != nil {
   640  		j.Periodic.Canonicalize()
   641  	}
   642  	if j.Update != nil {
   643  		j.Update.Canonicalize()
   644  	}
   645  
   646  	for _, tg := range j.TaskGroups {
   647  		tg.Canonicalize(j)
   648  	}
   649  }
   650  
   651  // JobSummary summarizes the state of the allocations of a job
   652  type JobSummary struct {
   653  	JobID     string
   654  	Namespace string
   655  	Summary   map[string]TaskGroupSummary
   656  	Children  *JobChildrenSummary
   657  
   658  	// Raft Indexes
   659  	CreateIndex uint64
   660  	ModifyIndex uint64
   661  }
   662  
   663  // JobChildrenSummary contains the summary of children job status
   664  type JobChildrenSummary struct {
   665  	Pending int64
   666  	Running int64
   667  	Dead    int64
   668  }
   669  
   670  func (jc *JobChildrenSummary) Sum() int {
   671  	if jc == nil {
   672  		return 0
   673  	}
   674  
   675  	return int(jc.Pending + jc.Running + jc.Dead)
   676  }
   677  
   678  // TaskGroup summarizes the state of all the allocations of a particular
   679  // TaskGroup
   680  type TaskGroupSummary struct {
   681  	Queued   int
   682  	Complete int
   683  	Failed   int
   684  	Running  int
   685  	Starting int
   686  	Lost     int
   687  }
   688  
   689  // JobListStub is used to return a subset of information about
   690  // jobs during list operations.
   691  type JobListStub struct {
   692  	ID                string
   693  	ParentID          string
   694  	Name              string
   695  	Type              string
   696  	Priority          int
   697  	Periodic          bool
   698  	ParameterizedJob  bool
   699  	Stop              bool
   700  	Status            string
   701  	StatusDescription string
   702  	JobSummary        *JobSummary
   703  	CreateIndex       uint64
   704  	ModifyIndex       uint64
   705  	JobModifyIndex    uint64
   706  	SubmitTime        int64
   707  }
   708  
   709  // JobIDSort is used to sort jobs by their job ID's.
   710  type JobIDSort []*JobListStub
   711  
   712  func (j JobIDSort) Len() int {
   713  	return len(j)
   714  }
   715  
   716  func (j JobIDSort) Less(a, b int) bool {
   717  	return j[a].ID < j[b].ID
   718  }
   719  
   720  func (j JobIDSort) Swap(a, b int) {
   721  	j[a], j[b] = j[b], j[a]
   722  }
   723  
   724  // NewServiceJob creates and returns a new service-style job
   725  // for long-lived processes using the provided name, ID, and
   726  // relative job priority.
   727  func NewServiceJob(id, name, region string, pri int) *Job {
   728  	return newJob(id, name, region, JobTypeService, pri)
   729  }
   730  
   731  // NewBatchJob creates and returns a new batch-style job for
   732  // short-lived processes using the provided name and ID along
   733  // with the relative job priority.
   734  func NewBatchJob(id, name, region string, pri int) *Job {
   735  	return newJob(id, name, region, JobTypeBatch, pri)
   736  }
   737  
   738  // newJob is used to create a new Job struct.
   739  func newJob(id, name, region, typ string, pri int) *Job {
   740  	return &Job{
   741  		Region:   &region,
   742  		ID:       &id,
   743  		Name:     &name,
   744  		Type:     &typ,
   745  		Priority: &pri,
   746  	}
   747  }
   748  
   749  // SetMeta is used to set arbitrary k/v pairs of metadata on a job.
   750  func (j *Job) SetMeta(key, val string) *Job {
   751  	if j.Meta == nil {
   752  		j.Meta = make(map[string]string)
   753  	}
   754  	j.Meta[key] = val
   755  	return j
   756  }
   757  
   758  // AddDatacenter is used to add a datacenter to a job.
   759  func (j *Job) AddDatacenter(dc string) *Job {
   760  	j.Datacenters = append(j.Datacenters, dc)
   761  	return j
   762  }
   763  
   764  // Constrain is used to add a constraint to a job.
   765  func (j *Job) Constrain(c *Constraint) *Job {
   766  	j.Constraints = append(j.Constraints, c)
   767  	return j
   768  }
   769  
   770  // AddTaskGroup adds a task group to an existing job.
   771  func (j *Job) AddTaskGroup(grp *TaskGroup) *Job {
   772  	j.TaskGroups = append(j.TaskGroups, grp)
   773  	return j
   774  }
   775  
   776  // AddPeriodicConfig adds a periodic config to an existing job.
   777  func (j *Job) AddPeriodicConfig(cfg *PeriodicConfig) *Job {
   778  	j.Periodic = cfg
   779  	return j
   780  }
   781  
   782  type WriteRequest struct {
   783  	// The target region for this write
   784  	Region string
   785  
   786  	// Namespace is the target namespace for this write
   787  	Namespace string
   788  
   789  	// SecretID is the secret ID of an ACL token
   790  	SecretID string
   791  }
   792  
   793  // JobValidateRequest is used to validate a job
   794  type JobValidateRequest struct {
   795  	Job *Job
   796  	WriteRequest
   797  }
   798  
   799  // JobValidateResponse is the response from validate request
   800  type JobValidateResponse struct {
   801  	// DriverConfigValidated indicates whether the agent validated the driver
   802  	// config
   803  	DriverConfigValidated bool
   804  
   805  	// ValidationErrors is a list of validation errors
   806  	ValidationErrors []string
   807  
   808  	// Error is a string version of any error that may have occurred
   809  	Error string
   810  
   811  	// Warnings contains any warnings about the given job. These may include
   812  	// deprecation warnings.
   813  	Warnings string
   814  }
   815  
   816  // JobRevertRequest is used to revert a job to a prior version.
   817  type JobRevertRequest struct {
   818  	// JobID is the ID of the job  being reverted
   819  	JobID string
   820  
   821  	// JobVersion the version to revert to.
   822  	JobVersion uint64
   823  
   824  	// EnforcePriorVersion if set will enforce that the job is at the given
   825  	// version before reverting.
   826  	EnforcePriorVersion *uint64
   827  
   828  	WriteRequest
   829  }
   830  
   831  // JobUpdateRequest is used to update a job
   832  type JobRegisterRequest struct {
   833  	Job *Job
   834  	// If EnforceIndex is set then the job will only be registered if the passed
   835  	// JobModifyIndex matches the current Jobs index. If the index is zero, the
   836  	// register only occurs if the job is new.
   837  	EnforceIndex   bool
   838  	JobModifyIndex uint64
   839  	PolicyOverride bool
   840  
   841  	WriteRequest
   842  }
   843  
   844  // RegisterJobRequest is used to serialize a job registration
   845  type RegisterJobRequest struct {
   846  	Job            *Job
   847  	EnforceIndex   bool   `json:",omitempty"`
   848  	JobModifyIndex uint64 `json:",omitempty"`
   849  	PolicyOverride bool   `json:",omitempty"`
   850  }
   851  
   852  // JobRegisterResponse is used to respond to a job registration
   853  type JobRegisterResponse struct {
   854  	EvalID          string
   855  	EvalCreateIndex uint64
   856  	JobModifyIndex  uint64
   857  
   858  	// Warnings contains any warnings about the given job. These may include
   859  	// deprecation warnings.
   860  	Warnings string
   861  
   862  	QueryMeta
   863  }
   864  
   865  // JobDeregisterResponse is used to respond to a job deregistration
   866  type JobDeregisterResponse struct {
   867  	EvalID          string
   868  	EvalCreateIndex uint64
   869  	JobModifyIndex  uint64
   870  	QueryMeta
   871  }
   872  
   873  type JobPlanRequest struct {
   874  	Job            *Job
   875  	Diff           bool
   876  	PolicyOverride bool
   877  	WriteRequest
   878  }
   879  
   880  type JobPlanResponse struct {
   881  	JobModifyIndex     uint64
   882  	CreatedEvals       []*Evaluation
   883  	Diff               *JobDiff
   884  	Annotations        *PlanAnnotations
   885  	FailedTGAllocs     map[string]*AllocationMetric
   886  	NextPeriodicLaunch time.Time
   887  
   888  	// Warnings contains any warnings about the given job. These may include
   889  	// deprecation warnings.
   890  	Warnings string
   891  }
   892  
   893  type JobDiff struct {
   894  	Type       string
   895  	ID         string
   896  	Fields     []*FieldDiff
   897  	Objects    []*ObjectDiff
   898  	TaskGroups []*TaskGroupDiff
   899  }
   900  
   901  type TaskGroupDiff struct {
   902  	Type    string
   903  	Name    string
   904  	Fields  []*FieldDiff
   905  	Objects []*ObjectDiff
   906  	Tasks   []*TaskDiff
   907  	Updates map[string]uint64
   908  }
   909  
   910  type TaskDiff struct {
   911  	Type        string
   912  	Name        string
   913  	Fields      []*FieldDiff
   914  	Objects     []*ObjectDiff
   915  	Annotations []string
   916  }
   917  
   918  type FieldDiff struct {
   919  	Type        string
   920  	Name        string
   921  	Old, New    string
   922  	Annotations []string
   923  }
   924  
   925  type ObjectDiff struct {
   926  	Type    string
   927  	Name    string
   928  	Fields  []*FieldDiff
   929  	Objects []*ObjectDiff
   930  }
   931  
   932  type PlanAnnotations struct {
   933  	DesiredTGUpdates map[string]*DesiredUpdates
   934  }
   935  
   936  type DesiredUpdates struct {
   937  	Ignore            uint64
   938  	Place             uint64
   939  	Migrate           uint64
   940  	Stop              uint64
   941  	InPlaceUpdate     uint64
   942  	DestructiveUpdate uint64
   943  	Canary            uint64
   944  }
   945  
   946  type JobDispatchRequest struct {
   947  	JobID   string
   948  	Payload []byte
   949  	Meta    map[string]string
   950  }
   951  
   952  type JobDispatchResponse struct {
   953  	DispatchedJobID string
   954  	EvalID          string
   955  	EvalCreateIndex uint64
   956  	JobCreateIndex  uint64
   957  	WriteMeta
   958  }
   959  
   960  // JobVersionsResponse is used for a job get versions request
   961  type JobVersionsResponse struct {
   962  	Versions []*Job
   963  	Diffs    []*JobDiff
   964  	QueryMeta
   965  }
   966  
   967  // JobStabilityRequest is used to marked a job as stable.
   968  type JobStabilityRequest struct {
   969  	// Job to set the stability on
   970  	JobID      string
   971  	JobVersion uint64
   972  
   973  	// Set the stability
   974  	Stable bool
   975  	WriteRequest
   976  }
   977  
   978  // JobStabilityResponse is the response when marking a job as stable.
   979  type JobStabilityResponse struct {
   980  	JobModifyIndex uint64
   981  	WriteMeta
   982  }