github.com/emate/nomad@v0.8.2-wo-binpacking/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  // PeriodicForce spawns a new instance of the periodic job and returns the eval ID
   237  func (j *Jobs) PeriodicForce(jobID string, q *WriteOptions) (string, *WriteMeta, error) {
   238  	var resp periodicForceResponse
   239  	wm, err := j.client.write("/v1/job/"+jobID+"/periodic/force", nil, &resp, q)
   240  	if err != nil {
   241  		return "", nil, err
   242  	}
   243  	return resp.EvalID, wm, nil
   244  }
   245  
   246  // PlanOptions is used to pass through job planning parameters
   247  type PlanOptions struct {
   248  	Diff           bool
   249  	PolicyOverride bool
   250  }
   251  
   252  func (j *Jobs) Plan(job *Job, diff bool, q *WriteOptions) (*JobPlanResponse, *WriteMeta, error) {
   253  	opts := PlanOptions{Diff: diff}
   254  	return j.PlanOpts(job, &opts, q)
   255  }
   256  
   257  func (j *Jobs) PlanOpts(job *Job, opts *PlanOptions, q *WriteOptions) (*JobPlanResponse, *WriteMeta, error) {
   258  	if job == nil {
   259  		return nil, nil, fmt.Errorf("must pass non-nil job")
   260  	}
   261  
   262  	// Setup the request
   263  	req := &JobPlanRequest{
   264  		Job: job,
   265  	}
   266  	if opts != nil {
   267  		req.Diff = opts.Diff
   268  		req.PolicyOverride = opts.PolicyOverride
   269  	}
   270  
   271  	var resp JobPlanResponse
   272  	wm, err := j.client.write("/v1/job/"+*job.ID+"/plan", req, &resp, q)
   273  	if err != nil {
   274  		return nil, nil, err
   275  	}
   276  	return &resp, wm, nil
   277  }
   278  
   279  func (j *Jobs) Summary(jobID string, q *QueryOptions) (*JobSummary, *QueryMeta, error) {
   280  	var resp JobSummary
   281  	qm, err := j.client.query("/v1/job/"+jobID+"/summary", &resp, q)
   282  	if err != nil {
   283  		return nil, nil, err
   284  	}
   285  	return &resp, qm, nil
   286  }
   287  
   288  func (j *Jobs) Dispatch(jobID string, meta map[string]string,
   289  	payload []byte, q *WriteOptions) (*JobDispatchResponse, *WriteMeta, error) {
   290  	var resp JobDispatchResponse
   291  	req := &JobDispatchRequest{
   292  		JobID:   jobID,
   293  		Meta:    meta,
   294  		Payload: payload,
   295  	}
   296  	wm, err := j.client.write("/v1/job/"+jobID+"/dispatch", req, &resp, q)
   297  	if err != nil {
   298  		return nil, nil, err
   299  	}
   300  	return &resp, wm, nil
   301  }
   302  
   303  // Revert is used to revert the given job to the passed version. If
   304  // enforceVersion is set, the job is only reverted if the current version is at
   305  // the passed version.
   306  func (j *Jobs) Revert(jobID string, version uint64, enforcePriorVersion *uint64,
   307  	q *WriteOptions) (*JobRegisterResponse, *WriteMeta, error) {
   308  
   309  	var resp JobRegisterResponse
   310  	req := &JobRevertRequest{
   311  		JobID:               jobID,
   312  		JobVersion:          version,
   313  		EnforcePriorVersion: enforcePriorVersion,
   314  	}
   315  	wm, err := j.client.write("/v1/job/"+jobID+"/revert", req, &resp, q)
   316  	if err != nil {
   317  		return nil, nil, err
   318  	}
   319  	return &resp, wm, nil
   320  }
   321  
   322  // Stable is used to mark a job version's stability.
   323  func (j *Jobs) Stable(jobID string, version uint64, stable bool,
   324  	q *WriteOptions) (*JobStabilityResponse, *WriteMeta, error) {
   325  
   326  	var resp JobStabilityResponse
   327  	req := &JobStabilityRequest{
   328  		JobID:      jobID,
   329  		JobVersion: version,
   330  		Stable:     stable,
   331  	}
   332  	wm, err := j.client.write("/v1/job/"+jobID+"/stable", req, &resp, q)
   333  	if err != nil {
   334  		return nil, nil, err
   335  	}
   336  	return &resp, wm, nil
   337  }
   338  
   339  // periodicForceResponse is used to deserialize a force response
   340  type periodicForceResponse struct {
   341  	EvalID string
   342  }
   343  
   344  // UpdateStrategy defines a task groups update strategy.
   345  type UpdateStrategy struct {
   346  	Stagger         *time.Duration `mapstructure:"stagger"`
   347  	MaxParallel     *int           `mapstructure:"max_parallel"`
   348  	HealthCheck     *string        `mapstructure:"health_check"`
   349  	MinHealthyTime  *time.Duration `mapstructure:"min_healthy_time"`
   350  	HealthyDeadline *time.Duration `mapstructure:"healthy_deadline"`
   351  	AutoRevert      *bool          `mapstructure:"auto_revert"`
   352  	Canary          *int           `mapstructure:"canary"`
   353  }
   354  
   355  // DefaultUpdateStrategy provides a baseline that can be used to upgrade
   356  // jobs with the old policy or for populating field defaults.
   357  func DefaultUpdateStrategy() *UpdateStrategy {
   358  	return &UpdateStrategy{
   359  		Stagger:         helper.TimeToPtr(30 * time.Second),
   360  		MaxParallel:     helper.IntToPtr(1),
   361  		HealthCheck:     helper.StringToPtr("checks"),
   362  		MinHealthyTime:  helper.TimeToPtr(10 * time.Second),
   363  		HealthyDeadline: helper.TimeToPtr(5 * time.Minute),
   364  		AutoRevert:      helper.BoolToPtr(false),
   365  		Canary:          helper.IntToPtr(0),
   366  	}
   367  }
   368  
   369  func (u *UpdateStrategy) Copy() *UpdateStrategy {
   370  	if u == nil {
   371  		return nil
   372  	}
   373  
   374  	copy := new(UpdateStrategy)
   375  
   376  	if u.Stagger != nil {
   377  		copy.Stagger = helper.TimeToPtr(*u.Stagger)
   378  	}
   379  
   380  	if u.MaxParallel != nil {
   381  		copy.MaxParallel = helper.IntToPtr(*u.MaxParallel)
   382  	}
   383  
   384  	if u.HealthCheck != nil {
   385  		copy.HealthCheck = helper.StringToPtr(*u.HealthCheck)
   386  	}
   387  
   388  	if u.MinHealthyTime != nil {
   389  		copy.MinHealthyTime = helper.TimeToPtr(*u.MinHealthyTime)
   390  	}
   391  
   392  	if u.HealthyDeadline != nil {
   393  		copy.HealthyDeadline = helper.TimeToPtr(*u.HealthyDeadline)
   394  	}
   395  
   396  	if u.AutoRevert != nil {
   397  		copy.AutoRevert = helper.BoolToPtr(*u.AutoRevert)
   398  	}
   399  
   400  	if u.Canary != nil {
   401  		copy.Canary = helper.IntToPtr(*u.Canary)
   402  	}
   403  
   404  	return copy
   405  }
   406  
   407  func (u *UpdateStrategy) Merge(o *UpdateStrategy) {
   408  	if o == nil {
   409  		return
   410  	}
   411  
   412  	if o.Stagger != nil {
   413  		u.Stagger = helper.TimeToPtr(*o.Stagger)
   414  	}
   415  
   416  	if o.MaxParallel != nil {
   417  		u.MaxParallel = helper.IntToPtr(*o.MaxParallel)
   418  	}
   419  
   420  	if o.HealthCheck != nil {
   421  		u.HealthCheck = helper.StringToPtr(*o.HealthCheck)
   422  	}
   423  
   424  	if o.MinHealthyTime != nil {
   425  		u.MinHealthyTime = helper.TimeToPtr(*o.MinHealthyTime)
   426  	}
   427  
   428  	if o.HealthyDeadline != nil {
   429  		u.HealthyDeadline = helper.TimeToPtr(*o.HealthyDeadline)
   430  	}
   431  
   432  	if o.AutoRevert != nil {
   433  		u.AutoRevert = helper.BoolToPtr(*o.AutoRevert)
   434  	}
   435  
   436  	if o.Canary != nil {
   437  		u.Canary = helper.IntToPtr(*o.Canary)
   438  	}
   439  }
   440  
   441  func (u *UpdateStrategy) Canonicalize() {
   442  	d := DefaultUpdateStrategy()
   443  
   444  	if u.MaxParallel == nil {
   445  		u.MaxParallel = d.MaxParallel
   446  	}
   447  
   448  	if u.Stagger == nil {
   449  		u.Stagger = d.Stagger
   450  	}
   451  
   452  	if u.HealthCheck == nil {
   453  		u.HealthCheck = d.HealthCheck
   454  	}
   455  
   456  	if u.HealthyDeadline == nil {
   457  		u.HealthyDeadline = d.HealthyDeadline
   458  	}
   459  
   460  	if u.MinHealthyTime == nil {
   461  		u.MinHealthyTime = d.MinHealthyTime
   462  	}
   463  
   464  	if u.AutoRevert == nil {
   465  		u.AutoRevert = d.AutoRevert
   466  	}
   467  
   468  	if u.Canary == nil {
   469  		u.Canary = d.Canary
   470  	}
   471  }
   472  
   473  // Empty returns whether the UpdateStrategy is empty or has user defined values.
   474  func (u *UpdateStrategy) Empty() bool {
   475  	if u == nil {
   476  		return true
   477  	}
   478  
   479  	if u.Stagger != nil && *u.Stagger != 0 {
   480  		return false
   481  	}
   482  
   483  	if u.MaxParallel != nil && *u.MaxParallel != 0 {
   484  		return false
   485  	}
   486  
   487  	if u.HealthCheck != nil && *u.HealthCheck != "" {
   488  		return false
   489  	}
   490  
   491  	if u.MinHealthyTime != nil && *u.MinHealthyTime != 0 {
   492  		return false
   493  	}
   494  
   495  	if u.HealthyDeadline != nil && *u.HealthyDeadline != 0 {
   496  		return false
   497  	}
   498  
   499  	if u.AutoRevert != nil && *u.AutoRevert {
   500  		return false
   501  	}
   502  
   503  	if u.Canary != nil && *u.Canary != 0 {
   504  		return false
   505  	}
   506  
   507  	return true
   508  }
   509  
   510  // PeriodicConfig is for serializing periodic config for a job.
   511  type PeriodicConfig struct {
   512  	Enabled         *bool
   513  	Spec            *string
   514  	SpecType        *string
   515  	ProhibitOverlap *bool   `mapstructure:"prohibit_overlap"`
   516  	TimeZone        *string `mapstructure:"time_zone"`
   517  }
   518  
   519  func (p *PeriodicConfig) Canonicalize() {
   520  	if p.Enabled == nil {
   521  		p.Enabled = helper.BoolToPtr(true)
   522  	}
   523  	if p.Spec == nil {
   524  		p.Spec = helper.StringToPtr("")
   525  	}
   526  	if p.SpecType == nil {
   527  		p.SpecType = helper.StringToPtr(PeriodicSpecCron)
   528  	}
   529  	if p.ProhibitOverlap == nil {
   530  		p.ProhibitOverlap = helper.BoolToPtr(false)
   531  	}
   532  	if p.TimeZone == nil || *p.TimeZone == "" {
   533  		p.TimeZone = helper.StringToPtr("UTC")
   534  	}
   535  }
   536  
   537  // Next returns the closest time instant matching the spec that is after the
   538  // passed time. If no matching instance exists, the zero value of time.Time is
   539  // returned. The `time.Location` of the returned value matches that of the
   540  // passed time.
   541  func (p *PeriodicConfig) Next(fromTime time.Time) (time.Time, error) {
   542  	if *p.SpecType == PeriodicSpecCron {
   543  		if e, err := cronexpr.Parse(*p.Spec); err == nil {
   544  			return structs.CronParseNext(e, fromTime, *p.Spec)
   545  		}
   546  	}
   547  
   548  	return time.Time{}, nil
   549  }
   550  
   551  func (p *PeriodicConfig) GetLocation() (*time.Location, error) {
   552  	if p.TimeZone == nil || *p.TimeZone == "" {
   553  		return time.UTC, nil
   554  	}
   555  
   556  	return time.LoadLocation(*p.TimeZone)
   557  }
   558  
   559  // ParameterizedJobConfig is used to configure the parameterized job.
   560  type ParameterizedJobConfig struct {
   561  	Payload      string
   562  	MetaRequired []string `mapstructure:"meta_required"`
   563  	MetaOptional []string `mapstructure:"meta_optional"`
   564  }
   565  
   566  // Job is used to serialize a job.
   567  type Job struct {
   568  	Stop              *bool
   569  	Region            *string
   570  	Namespace         *string
   571  	ID                *string
   572  	ParentID          *string
   573  	Name              *string
   574  	Type              *string
   575  	Priority          *int
   576  	AllAtOnce         *bool `mapstructure:"all_at_once"`
   577  	Datacenters       []string
   578  	Constraints       []*Constraint
   579  	TaskGroups        []*TaskGroup
   580  	Update            *UpdateStrategy
   581  	Periodic          *PeriodicConfig
   582  	ParameterizedJob  *ParameterizedJobConfig
   583  	Payload           []byte
   584  	Reschedule        *ReschedulePolicy
   585  	Migrate           *MigrateStrategy
   586  	Meta              map[string]string
   587  	VaultToken        *string `mapstructure:"vault_token"`
   588  	Status            *string
   589  	StatusDescription *string
   590  	Stable            *bool
   591  	Version           *uint64
   592  	SubmitTime        *int64
   593  	CreateIndex       *uint64
   594  	ModifyIndex       *uint64
   595  	JobModifyIndex    *uint64
   596  }
   597  
   598  // IsPeriodic returns whether a job is periodic.
   599  func (j *Job) IsPeriodic() bool {
   600  	return j.Periodic != nil
   601  }
   602  
   603  // IsParameterized returns whether a job is parameterized job.
   604  func (j *Job) IsParameterized() bool {
   605  	return j.ParameterizedJob != nil
   606  }
   607  
   608  func (j *Job) Canonicalize() {
   609  	if j.ID == nil {
   610  		j.ID = helper.StringToPtr("")
   611  	}
   612  	if j.Name == nil {
   613  		j.Name = helper.StringToPtr(*j.ID)
   614  	}
   615  	if j.ParentID == nil {
   616  		j.ParentID = helper.StringToPtr("")
   617  	}
   618  	if j.Namespace == nil {
   619  		j.Namespace = helper.StringToPtr(DefaultNamespace)
   620  	}
   621  	if j.Priority == nil {
   622  		j.Priority = helper.IntToPtr(50)
   623  	}
   624  	if j.Stop == nil {
   625  		j.Stop = helper.BoolToPtr(false)
   626  	}
   627  	if j.Region == nil {
   628  		j.Region = helper.StringToPtr("global")
   629  	}
   630  	if j.Namespace == nil {
   631  		j.Namespace = helper.StringToPtr("default")
   632  	}
   633  	if j.Type == nil {
   634  		j.Type = helper.StringToPtr("service")
   635  	}
   636  	if j.AllAtOnce == nil {
   637  		j.AllAtOnce = helper.BoolToPtr(false)
   638  	}
   639  	if j.VaultToken == nil {
   640  		j.VaultToken = helper.StringToPtr("")
   641  	}
   642  	if j.Status == nil {
   643  		j.Status = helper.StringToPtr("")
   644  	}
   645  	if j.StatusDescription == nil {
   646  		j.StatusDescription = helper.StringToPtr("")
   647  	}
   648  	if j.Stable == nil {
   649  		j.Stable = helper.BoolToPtr(false)
   650  	}
   651  	if j.Version == nil {
   652  		j.Version = helper.Uint64ToPtr(0)
   653  	}
   654  	if j.CreateIndex == nil {
   655  		j.CreateIndex = helper.Uint64ToPtr(0)
   656  	}
   657  	if j.ModifyIndex == nil {
   658  		j.ModifyIndex = helper.Uint64ToPtr(0)
   659  	}
   660  	if j.JobModifyIndex == nil {
   661  		j.JobModifyIndex = helper.Uint64ToPtr(0)
   662  	}
   663  	if j.Periodic != nil {
   664  		j.Periodic.Canonicalize()
   665  	}
   666  	if j.Update != nil {
   667  		j.Update.Canonicalize()
   668  	}
   669  
   670  	for _, tg := range j.TaskGroups {
   671  		tg.Canonicalize(j)
   672  	}
   673  }
   674  
   675  // LookupTaskGroup finds a task group by name
   676  func (j *Job) LookupTaskGroup(name string) *TaskGroup {
   677  	for _, tg := range j.TaskGroups {
   678  		if *tg.Name == name {
   679  			return tg
   680  		}
   681  	}
   682  	return nil
   683  }
   684  
   685  // JobSummary summarizes the state of the allocations of a job
   686  type JobSummary struct {
   687  	JobID     string
   688  	Namespace string
   689  	Summary   map[string]TaskGroupSummary
   690  	Children  *JobChildrenSummary
   691  
   692  	// Raft Indexes
   693  	CreateIndex uint64
   694  	ModifyIndex uint64
   695  }
   696  
   697  // JobChildrenSummary contains the summary of children job status
   698  type JobChildrenSummary struct {
   699  	Pending int64
   700  	Running int64
   701  	Dead    int64
   702  }
   703  
   704  func (jc *JobChildrenSummary) Sum() int {
   705  	if jc == nil {
   706  		return 0
   707  	}
   708  
   709  	return int(jc.Pending + jc.Running + jc.Dead)
   710  }
   711  
   712  // TaskGroup summarizes the state of all the allocations of a particular
   713  // TaskGroup
   714  type TaskGroupSummary struct {
   715  	Queued   int
   716  	Complete int
   717  	Failed   int
   718  	Running  int
   719  	Starting int
   720  	Lost     int
   721  }
   722  
   723  // JobListStub is used to return a subset of information about
   724  // jobs during list operations.
   725  type JobListStub struct {
   726  	ID                string
   727  	ParentID          string
   728  	Name              string
   729  	Type              string
   730  	Priority          int
   731  	Periodic          bool
   732  	ParameterizedJob  bool
   733  	Stop              bool
   734  	Status            string
   735  	StatusDescription string
   736  	JobSummary        *JobSummary
   737  	CreateIndex       uint64
   738  	ModifyIndex       uint64
   739  	JobModifyIndex    uint64
   740  	SubmitTime        int64
   741  }
   742  
   743  // JobIDSort is used to sort jobs by their job ID's.
   744  type JobIDSort []*JobListStub
   745  
   746  func (j JobIDSort) Len() int {
   747  	return len(j)
   748  }
   749  
   750  func (j JobIDSort) Less(a, b int) bool {
   751  	return j[a].ID < j[b].ID
   752  }
   753  
   754  func (j JobIDSort) Swap(a, b int) {
   755  	j[a], j[b] = j[b], j[a]
   756  }
   757  
   758  // NewServiceJob creates and returns a new service-style job
   759  // for long-lived processes using the provided name, ID, and
   760  // relative job priority.
   761  func NewServiceJob(id, name, region string, pri int) *Job {
   762  	return newJob(id, name, region, JobTypeService, pri)
   763  }
   764  
   765  // NewBatchJob creates and returns a new batch-style job for
   766  // short-lived processes using the provided name and ID along
   767  // with the relative job priority.
   768  func NewBatchJob(id, name, region string, pri int) *Job {
   769  	return newJob(id, name, region, JobTypeBatch, pri)
   770  }
   771  
   772  // newJob is used to create a new Job struct.
   773  func newJob(id, name, region, typ string, pri int) *Job {
   774  	return &Job{
   775  		Region:   &region,
   776  		ID:       &id,
   777  		Name:     &name,
   778  		Type:     &typ,
   779  		Priority: &pri,
   780  	}
   781  }
   782  
   783  // SetMeta is used to set arbitrary k/v pairs of metadata on a job.
   784  func (j *Job) SetMeta(key, val string) *Job {
   785  	if j.Meta == nil {
   786  		j.Meta = make(map[string]string)
   787  	}
   788  	j.Meta[key] = val
   789  	return j
   790  }
   791  
   792  // AddDatacenter is used to add a datacenter to a job.
   793  func (j *Job) AddDatacenter(dc string) *Job {
   794  	j.Datacenters = append(j.Datacenters, dc)
   795  	return j
   796  }
   797  
   798  // Constrain is used to add a constraint to a job.
   799  func (j *Job) Constrain(c *Constraint) *Job {
   800  	j.Constraints = append(j.Constraints, c)
   801  	return j
   802  }
   803  
   804  // AddTaskGroup adds a task group to an existing job.
   805  func (j *Job) AddTaskGroup(grp *TaskGroup) *Job {
   806  	j.TaskGroups = append(j.TaskGroups, grp)
   807  	return j
   808  }
   809  
   810  // AddPeriodicConfig adds a periodic config to an existing job.
   811  func (j *Job) AddPeriodicConfig(cfg *PeriodicConfig) *Job {
   812  	j.Periodic = cfg
   813  	return j
   814  }
   815  
   816  type WriteRequest struct {
   817  	// The target region for this write
   818  	Region string
   819  
   820  	// Namespace is the target namespace for this write
   821  	Namespace string
   822  
   823  	// SecretID is the secret ID of an ACL token
   824  	SecretID string
   825  }
   826  
   827  // JobValidateRequest is used to validate a job
   828  type JobValidateRequest struct {
   829  	Job *Job
   830  	WriteRequest
   831  }
   832  
   833  // JobValidateResponse is the response from validate request
   834  type JobValidateResponse struct {
   835  	// DriverConfigValidated indicates whether the agent validated the driver
   836  	// config
   837  	DriverConfigValidated bool
   838  
   839  	// ValidationErrors is a list of validation errors
   840  	ValidationErrors []string
   841  
   842  	// Error is a string version of any error that may have occurred
   843  	Error string
   844  
   845  	// Warnings contains any warnings about the given job. These may include
   846  	// deprecation warnings.
   847  	Warnings string
   848  }
   849  
   850  // JobRevertRequest is used to revert a job to a prior version.
   851  type JobRevertRequest struct {
   852  	// JobID is the ID of the job  being reverted
   853  	JobID string
   854  
   855  	// JobVersion the version to revert to.
   856  	JobVersion uint64
   857  
   858  	// EnforcePriorVersion if set will enforce that the job is at the given
   859  	// version before reverting.
   860  	EnforcePriorVersion *uint64
   861  
   862  	WriteRequest
   863  }
   864  
   865  // JobUpdateRequest is used to update a job
   866  type JobRegisterRequest struct {
   867  	Job *Job
   868  	// If EnforceIndex is set then the job will only be registered if the passed
   869  	// JobModifyIndex matches the current Jobs index. If the index is zero, the
   870  	// register only occurs if the job is new.
   871  	EnforceIndex   bool
   872  	JobModifyIndex uint64
   873  	PolicyOverride bool
   874  
   875  	WriteRequest
   876  }
   877  
   878  // RegisterJobRequest is used to serialize a job registration
   879  type RegisterJobRequest struct {
   880  	Job            *Job
   881  	EnforceIndex   bool   `json:",omitempty"`
   882  	JobModifyIndex uint64 `json:",omitempty"`
   883  	PolicyOverride bool   `json:",omitempty"`
   884  }
   885  
   886  // JobRegisterResponse is used to respond to a job registration
   887  type JobRegisterResponse struct {
   888  	EvalID          string
   889  	EvalCreateIndex uint64
   890  	JobModifyIndex  uint64
   891  
   892  	// Warnings contains any warnings about the given job. These may include
   893  	// deprecation warnings.
   894  	Warnings string
   895  
   896  	QueryMeta
   897  }
   898  
   899  // JobDeregisterResponse is used to respond to a job deregistration
   900  type JobDeregisterResponse struct {
   901  	EvalID          string
   902  	EvalCreateIndex uint64
   903  	JobModifyIndex  uint64
   904  	QueryMeta
   905  }
   906  
   907  type JobPlanRequest struct {
   908  	Job            *Job
   909  	Diff           bool
   910  	PolicyOverride bool
   911  	WriteRequest
   912  }
   913  
   914  type JobPlanResponse struct {
   915  	JobModifyIndex     uint64
   916  	CreatedEvals       []*Evaluation
   917  	Diff               *JobDiff
   918  	Annotations        *PlanAnnotations
   919  	FailedTGAllocs     map[string]*AllocationMetric
   920  	NextPeriodicLaunch time.Time
   921  
   922  	// Warnings contains any warnings about the given job. These may include
   923  	// deprecation warnings.
   924  	Warnings string
   925  }
   926  
   927  type JobDiff struct {
   928  	Type       string
   929  	ID         string
   930  	Fields     []*FieldDiff
   931  	Objects    []*ObjectDiff
   932  	TaskGroups []*TaskGroupDiff
   933  }
   934  
   935  type TaskGroupDiff struct {
   936  	Type    string
   937  	Name    string
   938  	Fields  []*FieldDiff
   939  	Objects []*ObjectDiff
   940  	Tasks   []*TaskDiff
   941  	Updates map[string]uint64
   942  }
   943  
   944  type TaskDiff struct {
   945  	Type        string
   946  	Name        string
   947  	Fields      []*FieldDiff
   948  	Objects     []*ObjectDiff
   949  	Annotations []string
   950  }
   951  
   952  type FieldDiff struct {
   953  	Type        string
   954  	Name        string
   955  	Old, New    string
   956  	Annotations []string
   957  }
   958  
   959  type ObjectDiff struct {
   960  	Type    string
   961  	Name    string
   962  	Fields  []*FieldDiff
   963  	Objects []*ObjectDiff
   964  }
   965  
   966  type PlanAnnotations struct {
   967  	DesiredTGUpdates map[string]*DesiredUpdates
   968  }
   969  
   970  type DesiredUpdates struct {
   971  	Ignore            uint64
   972  	Place             uint64
   973  	Migrate           uint64
   974  	Stop              uint64
   975  	InPlaceUpdate     uint64
   976  	DestructiveUpdate uint64
   977  	Canary            uint64
   978  }
   979  
   980  type JobDispatchRequest struct {
   981  	JobID   string
   982  	Payload []byte
   983  	Meta    map[string]string
   984  }
   985  
   986  type JobDispatchResponse struct {
   987  	DispatchedJobID string
   988  	EvalID          string
   989  	EvalCreateIndex uint64
   990  	JobCreateIndex  uint64
   991  	WriteMeta
   992  }
   993  
   994  // JobVersionsResponse is used for a job get versions request
   995  type JobVersionsResponse struct {
   996  	Versions []*Job
   997  	Diffs    []*JobDiff
   998  	QueryMeta
   999  }
  1000  
  1001  // JobStabilityRequest is used to marked a job as stable.
  1002  type JobStabilityRequest struct {
  1003  	// Job to set the stability on
  1004  	JobID      string
  1005  	JobVersion uint64
  1006  
  1007  	// Set the stability
  1008  	Stable bool
  1009  	WriteRequest
  1010  }
  1011  
  1012  // JobStabilityResponse is the response when marking a job as stable.
  1013  type JobStabilityResponse struct {
  1014  	JobModifyIndex uint64
  1015  	WriteMeta
  1016  }