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