diff --git a/props/tokens.properties b/props/tokens.properties new file mode 100644 index 000000000..e69de29bb diff --git a/rundeck/job.go b/rundeck/job.go index 40fd08da6..08ae5507b 100644 --- a/rundeck/job.go +++ b/rundeck/job.go @@ -69,6 +69,7 @@ type JobDetail struct { Timeout string `xml:"timeout,omitempty"` Retry *Retry `xml:"retry,omitempty"` NodeFilter *JobNodeFilter `xml:"nodefilters,omitempty"` + Orchestrator *JobOrchestrator `xml:"orchestrator,omitempty"` /* If Dispatch is enabled, nodesSelectedByDefault is always present with true/false. * by this reason omitempty cannot be present. @@ -317,6 +318,18 @@ type JobNodeFilter struct { ExcludePrecedence bool `xml:"excludeprecedence,omitempty"` } +// JobOrchestratorConfig Contains the options for the Job Orchestrators +type JobOrchestratorConfig struct { + Count int `xml:"count,omitempty"` + Percent int `xml:"percent,omitempty"` +} + +// JobOrchestrator describes how to schedule the jobs, in what order, and on how many nodes +type JobOrchestrator struct { + Config JobOrchestratorConfig `xml:"configuration"` + Type string `xml:"type"` +} + type jobImportResults struct { Succeeded jobImportResultsCategory `xml:"succeeded"` Failed jobImportResultsCategory `xml:"failed"` @@ -511,14 +524,11 @@ func (c *JobValueChoices) UnmarshalXMLAttr(attr xml.Attr) error { func (a JobCommandJobRefArguments) MarshalXML(e *xml.Encoder, start xml.StartElement) error { start.Attr = []xml.Attr{ - {Name: xml.Name{Local: "line"}, Value: string(a)}, + xml.Attr{Name: xml.Name{Local: "line"}, Value: string(a)}, } - err := e.EncodeToken(start) - if err != nil { - return err - } - err = e.EncodeToken(xml.EndElement{Name: start.Name}) - return err + e.EncodeToken(start) + e.EncodeToken(xml.EndElement{Name: start.Name}) + return nil } func (a *JobCommandJobRefArguments) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { diff --git a/rundeck/resource_job.go b/rundeck/resource_job.go index e7a910a9c..882acfbb4 100644 --- a/rundeck/resource_job.go +++ b/rundeck/resource_job.go @@ -126,9 +126,28 @@ func resourceRundeckJob() *schema.Resource { Optional: true, }, - "timeout": { - Type: schema.TypeString, + "orchestrator": { + Type: schema.TypeList, Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + Description: "Option of `subset`, `tiered`, `percentage`", + }, + "count": { + Type: schema.TypeInt, + Optional: true, + Description: "Value for the subset orchestrator", + }, + "percent": { + Type: schema.TypeInt, + Optional: true, + Description: "Value for the maxPercentage orchestrator", + }, + }, + }, }, "schedule": { @@ -627,6 +646,26 @@ func jobFromResourceData(d *schema.ResourceData) (*JobDetail, error) { job.Dispatch.SuccessOnEmptyNodeFilter = successOnEmpty.(bool) } + orchList := d.Get("orchestrator").([]interface{}) + if len(orchList) > 1 { + return nil, fmt.Errorf("rundeck command may have no more than one orchestrator") + } + for _, orch := range orchList { + orchMap := orch.(map[string]interface{}) + job.Orchestrator = &JobOrchestrator{ + Type: orchMap["type"].(string), + Config: JobOrchestratorConfig{}, + } + orchCount := orchMap["count"] + if orchCount != nil { + job.Orchestrator.Config.Count = orchCount.(int) + } + orchPct := orchMap["percent"] + if orchPct != nil { + job.Orchestrator.Config.Percent = orchPct.(int) + } + } + sequence := &JobCommandSequence{ ContinueOnError: d.Get("continue_on_error").(bool), OrderingStrategy: d.Get("command_ordering_strategy").(string), diff --git a/rundeck/resource_job_test.go b/rundeck/resource_job_test.go index 8f910075c..984d0e0ee 100644 --- a/rundeck/resource_job_test.go +++ b/rundeck/resource_job_test.go @@ -69,6 +69,34 @@ func TestAccJob_cmd_nodefilter(t *testing.T) { }, }) } +func TestAccJob_cmd_orchestrator(t *testing.T) { + var job JobDetail + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccJobCheckDestroy(&job), + Steps: []resource.TestStep{ + { + Config: testAccJobConfig_cmd_nodefilter, + Check: resource.ComposeTestCheckFunc( + testAccJobCheckExists("rundeck_job.test", &job), + func(s *terraform.State) error { + if expected := "subset"; job.Orchestrator.Type != expected { + return fmt.Errorf("wrong subset; expected %v, got %v", expected, job.Orchestrator.Type) + } + + if expected := 1; job.Orchestrator.Config.Count != expected { + return fmt.Errorf("wrong subset count; expected %v, got %v", expected, job.Orchestrator.Config.Count) + } + + return nil + }, + ), + }, + }, + }) +} func TestAccJob_Idempotency(t *testing.T) { var job JobDetail @@ -259,6 +287,10 @@ resource "rundeck_job" "test" { option { name = "foo" default_value = "bar" + } + orchestrator { + type = "subset" + count = 1 } command { job { diff --git a/website/docs/r/job.html.md b/website/docs/r/job.html.md index ee2b96ae9..aa1ed700b 100644 --- a/website/docs/r/job.html.md +++ b/website/docs/r/job.html.md @@ -56,6 +56,8 @@ The following arguments are supported: * `schedule` - (Optional) The job's schedule in Quartz schedule cron format. Similar to unix crontab, but with seven fields instead of five: Second Minute Hour Day-of-Month Month Day-of-Week Year +* `orchestrator` - (Optional) The orchestrator for the job, described below. + * `schedule_enabled` - (Optional) Sets the job schedule to be enabled or disabled. Defaults to `true`. * `time_zone` - (Optional) A valid Time Zone, either an abbreviation such as "PST", a full name such as @@ -75,7 +77,7 @@ The following arguments are supported: references like "${option.delay}". The default is 0. * `max_thread_count` - (Optional) The maximum number of threads to use to execute this job, which - controls on how many nodes the commands can be run simulateneously. Defaults to 1, meaning that + controls on how many nodes the commands can be run simultaneously. Defaults to 1, meaning that the nodes will be visited sequentially. * `continue_on_error` - (Optional) Boolean defining whether Rundeck will continue to run