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