Improving Rundeck provider: scheduler (#9449)
* feat(rundeck provider): Scheduling (crontab) * fix(govendor-upgrade): Rundeck api wrapper
This commit is contained in:
parent
e558cbddf4
commit
88faa1bb7f
|
@ -2,6 +2,7 @@ package rundeck
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
|
@ -99,6 +100,11 @@ func resourceRundeckJob() *schema.Resource {
|
||||||
Optional: true,
|
Optional: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"schedule": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
|
||||||
"option": &schema.Schema{
|
"option": &schema.Schema{
|
||||||
// This is a list because order is important when preserve_options_order is
|
// This is a list because order is important when preserve_options_order is
|
||||||
// set. When it's not set the order is unimportant but preserved by Rundeck/
|
// set. When it's not set the order is unimportant but preserved by Rundeck/
|
||||||
|
@ -455,6 +461,30 @@ func jobFromResourceData(d *schema.ResourceData) (*rundeck.JobDetail, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if d.Get("schedule").(string) != "" {
|
||||||
|
schedule := strings.Split(d.Get("schedule").(string), " ")
|
||||||
|
if len(schedule) != 7 {
|
||||||
|
return nil, fmt.Errorf("Rundeck schedule must be formated like a cron expression, as defined here: http://www.quartz-scheduler.org/documentation/quartz-2.2.x/tutorials/tutorial-lesson-06.html")
|
||||||
|
}
|
||||||
|
job.Schedule = &rundeck.JobSchedule{
|
||||||
|
Time: rundeck.JobScheduleTime{
|
||||||
|
Seconds: schedule[0],
|
||||||
|
Minute: schedule[1],
|
||||||
|
Hour: schedule[2],
|
||||||
|
},
|
||||||
|
Month: rundeck.JobScheduleMonth{
|
||||||
|
Day: schedule[3],
|
||||||
|
Month: schedule[4],
|
||||||
|
},
|
||||||
|
WeekDay: &rundeck.JobScheduleWeekDay{
|
||||||
|
Day: schedule[5],
|
||||||
|
},
|
||||||
|
Year: rundeck.JobScheduleYear{
|
||||||
|
Year: schedule[6],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return job, nil
|
return job, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -562,5 +592,22 @@ func jobToResourceData(job *rundeck.JobDetail, d *schema.ResourceData) error {
|
||||||
}
|
}
|
||||||
d.Set("command", commandConfigsI)
|
d.Set("command", commandConfigsI)
|
||||||
|
|
||||||
|
if job.Schedule != nil {
|
||||||
|
schedule := []string{}
|
||||||
|
schedule = append(schedule, job.Schedule.Time.Seconds)
|
||||||
|
schedule = append(schedule, job.Schedule.Time.Minute)
|
||||||
|
schedule = append(schedule, job.Schedule.Time.Hour)
|
||||||
|
schedule = append(schedule, job.Schedule.Month.Day)
|
||||||
|
schedule = append(schedule, job.Schedule.Month.Month)
|
||||||
|
if job.Schedule.WeekDay != nil {
|
||||||
|
schedule = append(schedule, job.Schedule.WeekDay.Day)
|
||||||
|
} else {
|
||||||
|
schedule = append(schedule, "*")
|
||||||
|
}
|
||||||
|
schedule = append(schedule, job.Schedule.Year.Year)
|
||||||
|
|
||||||
|
d.Set("schedule", strings.Join(schedule, " "))
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,7 @@ resource "rundeck_job" "test" {
|
||||||
allow_concurrent_executions = 1
|
allow_concurrent_executions = 1
|
||||||
max_thread_count = 1
|
max_thread_count = 1
|
||||||
rank_order = "ascending"
|
rank_order = "ascending"
|
||||||
|
schedule = "0 0 12 * * * *"
|
||||||
option {
|
option {
|
||||||
name = "foo"
|
name = "foo"
|
||||||
default_value = "bar"
|
default_value = "bar"
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
# go-rundeck-api
|
||||||
|
|
||||||
|
This is a Go client for the Rundeck HTTP API. It was primarily developed to back the Rundeck provider in [Terraform](https://terraform.io), but can be used standalone too.
|
||||||
|
|
||||||
|
It should ``go install`` just like any other Go package:
|
||||||
|
|
||||||
|
* ``go install github.com/apparentlymart/go-rundeck-api/rundeck``
|
||||||
|
|
||||||
|
For reference documentation, see [godoc](https://godoc.org/github.com/apparentlymart/go-rundeck-api/rundeck).
|
|
@ -30,12 +30,57 @@ type JobDetail struct {
|
||||||
GroupName string `xml:"group,omitempty"`
|
GroupName string `xml:"group,omitempty"`
|
||||||
ProjectName string `xml:"context>project,omitempty"`
|
ProjectName string `xml:"context>project,omitempty"`
|
||||||
OptionsConfig *JobOptions `xml:"context>options,omitempty"`
|
OptionsConfig *JobOptions `xml:"context>options,omitempty"`
|
||||||
Description string `xml:"description,omitempty"`
|
Description string `xml:"description"`
|
||||||
LogLevel string `xml:"loglevel,omitempty"`
|
LogLevel string `xml:"loglevel,omitempty"`
|
||||||
AllowConcurrentExecutions bool `xml:"multipleExecutions"`
|
AllowConcurrentExecutions bool `xml:"multipleExecutions,omitempty"`
|
||||||
Dispatch *JobDispatch `xml:"dispatch"`
|
Dispatch *JobDispatch `xml:"dispatch,omitempty"`
|
||||||
CommandSequence *JobCommandSequence `xml:"sequence,omitempty"`
|
CommandSequence *JobCommandSequence `xml:"sequence,omitempty"`
|
||||||
|
Timeout string `xml:"timeout,omitempty"`
|
||||||
|
Retry string `xml:"retry,omitempty"`
|
||||||
NodeFilter *JobNodeFilter `xml:"nodefilters,omitempty"`
|
NodeFilter *JobNodeFilter `xml:"nodefilters,omitempty"`
|
||||||
|
|
||||||
|
/* If Dispatch is enabled, nodesSelectedByDefault is always present with true/false.
|
||||||
|
* by this reason omitempty cannot be present.
|
||||||
|
* This has to be handle by the user.
|
||||||
|
*/
|
||||||
|
NodesSelectedByDefault bool `xml:"nodesSelectedByDefault"`
|
||||||
|
Schedule *JobSchedule `xml:"schedule,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type JobSchedule struct {
|
||||||
|
XMLName xml.Name `xml:"schedule"`
|
||||||
|
DayOfMonth *JobScheduleDayOfMonth `xml:"dayofmonth,omitempty"`
|
||||||
|
Time JobScheduleTime `xml:"time"`
|
||||||
|
Month JobScheduleMonth `xml:"month"`
|
||||||
|
WeekDay *JobScheduleWeekDay `xml:"weekday,omitempty"`
|
||||||
|
Year JobScheduleYear `xml:"year"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type JobScheduleDayOfMonth struct {
|
||||||
|
XMLName xml.Name `xml:"dayofmonth"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type JobScheduleMonth struct {
|
||||||
|
XMLName xml.Name `xml:"month"`
|
||||||
|
Day string `xml:"day,attr,omitempty"`
|
||||||
|
Month string `xml:"month,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type JobScheduleYear struct {
|
||||||
|
XMLName xml.Name `xml:"year"`
|
||||||
|
Year string `xml:"year,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type JobScheduleWeekDay struct {
|
||||||
|
XMLName xml.Name `xml:"weekday"`
|
||||||
|
Day string `xml:"day,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type JobScheduleTime struct {
|
||||||
|
XMLName xml.Name `xml:"time"`
|
||||||
|
Hour string `xml:"hour,attr"`
|
||||||
|
Minute string `xml:"minute,attr"`
|
||||||
|
Seconds string `xml:"seconds,attr"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type jobDetailList struct {
|
type jobDetailList struct {
|
||||||
|
@ -53,13 +98,40 @@ type JobOptions struct {
|
||||||
type JobOption struct {
|
type JobOption struct {
|
||||||
XMLName xml.Name `xml:"option"`
|
XMLName xml.Name `xml:"option"`
|
||||||
|
|
||||||
|
// If AllowsMultipleChoices is set, the string that will be used to delimit the multiple
|
||||||
|
// chosen options.
|
||||||
|
MultiValueDelimiter string `xml:"delimiter,attr,omitempty"`
|
||||||
|
|
||||||
|
// If set, Rundeck will reject values that are not in the set of predefined choices.
|
||||||
|
RequirePredefinedChoice bool `xml:"enforcedvalues,attr,omitempty"`
|
||||||
|
|
||||||
|
// When either ValueChoices or ValueChoicesURL is set, controls whether more than one
|
||||||
|
// choice may be selected as the value.
|
||||||
|
AllowsMultipleValues bool `xml:"multivalued,attr,omitempty"`
|
||||||
|
|
||||||
// The name of the option, which can be used to interpolate its value
|
// The name of the option, which can be used to interpolate its value
|
||||||
// into job commands.
|
// into job commands.
|
||||||
Name string `xml:"name,attr,omitempty"`
|
Name string `xml:"name,attr,omitempty"`
|
||||||
|
|
||||||
|
// Regular expression to be used to validate the option value.
|
||||||
|
ValidationRegex string `xml:"regex,attr,omitempty"`
|
||||||
|
|
||||||
|
// If set, Rundeck requires a value to be set for this option.
|
||||||
|
IsRequired bool `xml:"required,attr,omitempty"`
|
||||||
|
|
||||||
|
// If set, the input for this field will be obscured in the UI. Useful for passwords
|
||||||
|
// and other secrets.
|
||||||
|
ObscureInput bool `xml:"secure,attr,omitempty"`
|
||||||
|
|
||||||
|
// If ObscureInput is set, StoragePath can be used to point out credentials.
|
||||||
|
StoragePath string `xml:"storagePath,attr,omitempty"`
|
||||||
|
|
||||||
// The default value of the option.
|
// The default value of the option.
|
||||||
DefaultValue string `xml:"value,attr,omitempty"`
|
DefaultValue string `xml:"value,attr,omitempty"`
|
||||||
|
|
||||||
|
// If set, the value can be accessed from scripts.
|
||||||
|
ValueIsExposedToScripts bool `xml:"valueExposed,attr,omitempty"`
|
||||||
|
|
||||||
// A sequence of predefined choices for this option. Mutually exclusive with ValueChoicesURL.
|
// A sequence of predefined choices for this option. Mutually exclusive with ValueChoicesURL.
|
||||||
ValueChoices JobValueChoices `xml:"values,attr"`
|
ValueChoices JobValueChoices `xml:"values,attr"`
|
||||||
|
|
||||||
|
@ -67,34 +139,11 @@ type JobOption struct {
|
||||||
// Mutually exclusive with ValueChoices
|
// Mutually exclusive with ValueChoices
|
||||||
ValueChoicesURL string `xml:"valuesUrl,attr,omitempty"`
|
ValueChoicesURL string `xml:"valuesUrl,attr,omitempty"`
|
||||||
|
|
||||||
// If set, Rundeck will reject values that are not in the set of predefined choices.
|
|
||||||
RequirePredefinedChoice bool `xml:"enforcedvalues,attr,omitempty"`
|
|
||||||
|
|
||||||
// Regular expression to be used to validate the option value.
|
|
||||||
ValidationRegex string `xml:"regex,attr,omitempty"`
|
|
||||||
|
|
||||||
// Description of the value to be shown in the Rundeck UI.
|
// Description of the value to be shown in the Rundeck UI.
|
||||||
Description string `xml:"description,omitempty"`
|
Description string `xml:"description,omitempty"`
|
||||||
|
|
||||||
// If set, Rundeck requires a value to be set for this option.
|
|
||||||
IsRequired bool `xml:"required,attr,omitempty"`
|
|
||||||
|
|
||||||
// When either ValueChoices or ValueChoicesURL is set, controls whether more than one
|
|
||||||
// choice may be selected as the value.
|
|
||||||
AllowsMultipleValues bool `xml:"multivalued,attr,omitempty"`
|
|
||||||
|
|
||||||
// If AllowsMultipleChoices is set, the string that will be used to delimit the multiple
|
|
||||||
// chosen options.
|
|
||||||
MultiValueDelimiter string `xml:"delimeter,attr,omitempty"`
|
|
||||||
|
|
||||||
// If set, the input for this field will be obscured in the UI. Useful for passwords
|
|
||||||
// and other secrets.
|
|
||||||
ObscureInput bool `xml:"secure,attr,omitempty"`
|
|
||||||
|
|
||||||
// If set, the value can be accessed from scripts.
|
|
||||||
ValueIsExposedToScripts bool `xml:"valueExposed,attr,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// JobValueChoices is a specialization of []string representing a sequence of predefined values
|
// JobValueChoices is a specialization of []string representing a sequence of predefined values
|
||||||
// for a job option.
|
// for a job option.
|
||||||
type JobValueChoices []string
|
type JobValueChoices []string
|
||||||
|
@ -112,6 +161,9 @@ type JobCommandSequence struct {
|
||||||
|
|
||||||
// Sequence of commands to run in the sequence.
|
// Sequence of commands to run in the sequence.
|
||||||
Commands []JobCommand `xml:"command"`
|
Commands []JobCommand `xml:"command"`
|
||||||
|
|
||||||
|
// Description
|
||||||
|
Description string `xml:"description,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// JobCommand describes a particular command to run within the sequence of commands on a job.
|
// JobCommand describes a particular command to run within the sequence of commands on a job.
|
||||||
|
@ -120,9 +172,21 @@ type JobCommandSequence struct {
|
||||||
type JobCommand struct {
|
type JobCommand struct {
|
||||||
XMLName xml.Name
|
XMLName xml.Name
|
||||||
|
|
||||||
|
// If the Workflow keepgoing is false, this allows the Workflow to continue when the Error Handler is successful.
|
||||||
|
ContinueOnError bool `xml:"keepgoingOnSuccess,attr,omitempty"`
|
||||||
|
|
||||||
|
// Description
|
||||||
|
Description string `xml:"description,omitempty"`
|
||||||
|
|
||||||
|
// On error:
|
||||||
|
ErrorHandler *JobCommand `xml:"errorhandler,omitempty"`
|
||||||
|
|
||||||
// A literal shell command to run.
|
// A literal shell command to run.
|
||||||
ShellCommand string `xml:"exec,omitempty"`
|
ShellCommand string `xml:"exec,omitempty"`
|
||||||
|
|
||||||
|
// Add extension to the temporary filename.
|
||||||
|
FileExtension string `xml:"fileExtension,omitempty"`
|
||||||
|
|
||||||
// An inline program to run. This will be written to disk and executed, so if it is
|
// An inline program to run. This will be written to disk and executed, so if it is
|
||||||
// a shell script it should have an appropriate #! line.
|
// a shell script it should have an appropriate #! line.
|
||||||
Script string `xml:"script,omitempty"`
|
Script string `xml:"script,omitempty"`
|
||||||
|
@ -133,6 +197,9 @@ type JobCommand struct {
|
||||||
// When ScriptFile is set, the arguments to provide to the script when executing it.
|
// When ScriptFile is set, the arguments to provide to the script when executing it.
|
||||||
ScriptFileArgs string `xml:"scriptargs,omitempty"`
|
ScriptFileArgs string `xml:"scriptargs,omitempty"`
|
||||||
|
|
||||||
|
// ScriptInterpreter is used to execute (Script)File with.
|
||||||
|
ScriptInterpreter *JobCommandScriptInterpreter `xml:"scriptinterpreter,omitempty"`
|
||||||
|
|
||||||
// A reference to another job to run as this command.
|
// A reference to another job to run as this command.
|
||||||
Job *JobCommandJobRef `xml:"jobref"`
|
Job *JobCommandJobRef `xml:"jobref"`
|
||||||
|
|
||||||
|
@ -143,12 +210,20 @@ type JobCommand struct {
|
||||||
NodeStepPlugin *JobPlugin `xml:"node-step-plugin"`
|
NodeStepPlugin *JobPlugin `xml:"node-step-plugin"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// (Inline) Script interpreter
|
||||||
|
type JobCommandScriptInterpreter struct {
|
||||||
|
XMLName xml.Name `xml:"scriptinterpreter"`
|
||||||
|
InvocationString string `xml:",chardata"`
|
||||||
|
ArgsQuoted bool `xml:"argsquoted,attr,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// JobCommandJobRef is a reference to another job that will run as one of the commands of a job.
|
// JobCommandJobRef is a reference to another job that will run as one of the commands of a job.
|
||||||
type JobCommandJobRef struct {
|
type JobCommandJobRef struct {
|
||||||
XMLName xml.Name `xml:"jobref"`
|
XMLName xml.Name `xml:"jobref"`
|
||||||
Name string `xml:"name,attr"`
|
Name string `xml:"name,attr"`
|
||||||
GroupName string `xml:"group,attr"`
|
GroupName string `xml:"group,attr"`
|
||||||
RunForEachNode bool `xml:"nodeStep,attr"`
|
RunForEachNode bool `xml:"nodeStep,attr"`
|
||||||
|
NodeFilter *JobNodeFilter `xml:"nodefilters,omitempty"`
|
||||||
Arguments JobCommandJobRefArguments `xml:"arg"`
|
Arguments JobCommandJobRefArguments `xml:"arg"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
314
vendor/github.com/apparentlymart/go-rundeck-api/rundeck/job_test.go
generated
vendored
Normal file
314
vendor/github.com/apparentlymart/go-rundeck-api/rundeck/job_test.go
generated
vendored
Normal file
|
@ -0,0 +1,314 @@
|
||||||
|
package rundeck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUnmarshalJobDetail(t *testing.T) {
|
||||||
|
testUnmarshalXML(t, []unmarshalTest{
|
||||||
|
unmarshalTest{
|
||||||
|
"with-config",
|
||||||
|
`<job><uuid>baz</uuid><dispatch><rankOrder>ascending</rankOrder></dispatch></job>`,
|
||||||
|
&JobDetail{},
|
||||||
|
func (rv interface {}) error {
|
||||||
|
v := rv.(*JobDetail)
|
||||||
|
if v.ID != "baz" {
|
||||||
|
return fmt.Errorf("got ID %s, but expecting baz", v.ID)
|
||||||
|
}
|
||||||
|
if v.Dispatch.RankOrder != "ascending" {
|
||||||
|
return fmt.Errorf("Dispatch.RankOrder = \"%v\", but expecting \"ascending\"", v.Dispatch.RankOrder)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
unmarshalTest{
|
||||||
|
"with-empty-config",
|
||||||
|
`<JobPlugin type="foo-plugin"><configuration/></JobPlugin>`,
|
||||||
|
&JobPlugin{},
|
||||||
|
func (rv interface {}) error {
|
||||||
|
v := rv.(*JobPlugin)
|
||||||
|
if v.Type != "foo-plugin" {
|
||||||
|
return fmt.Errorf("got Type %s, but expecting foo-plugin", v.Type)
|
||||||
|
}
|
||||||
|
if len(v.Config) != 0 {
|
||||||
|
return fmt.Errorf("got %i Config values, but expecting 0", len(v.Config))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalJobPlugin(t *testing.T) {
|
||||||
|
testMarshalXML(t, []marshalTest{
|
||||||
|
marshalTest{
|
||||||
|
"with-config",
|
||||||
|
JobPlugin{
|
||||||
|
Type: "foo-plugin",
|
||||||
|
Config: map[string]string{
|
||||||
|
"woo": "foo",
|
||||||
|
"bar": "baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`<JobPlugin type="foo-plugin"><configuration><entry key="bar" value="baz"></entry><entry key="woo" value="foo"></entry></configuration></JobPlugin>`,
|
||||||
|
},
|
||||||
|
marshalTest{
|
||||||
|
"with-empty-config",
|
||||||
|
JobPlugin{
|
||||||
|
Type: "foo-plugin",
|
||||||
|
Config: map[string]string{},
|
||||||
|
},
|
||||||
|
`<JobPlugin type="foo-plugin"></JobPlugin>`,
|
||||||
|
},
|
||||||
|
marshalTest{
|
||||||
|
"with-zero-value-config",
|
||||||
|
JobPlugin{
|
||||||
|
Type: "foo-plugin",
|
||||||
|
},
|
||||||
|
`<JobPlugin type="foo-plugin"></JobPlugin>`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalJobPlugin(t *testing.T) {
|
||||||
|
testUnmarshalXML(t, []unmarshalTest{
|
||||||
|
unmarshalTest{
|
||||||
|
"with-config",
|
||||||
|
`<JobPlugin type="foo-plugin"><configuration><entry key="woo" value="foo"/><entry key="bar" value="baz"/></configuration></JobPlugin>`,
|
||||||
|
&JobPlugin{},
|
||||||
|
func (rv interface {}) error {
|
||||||
|
v := rv.(*JobPlugin)
|
||||||
|
if v.Type != "foo-plugin" {
|
||||||
|
return fmt.Errorf("got Type %s, but expecting foo-plugin", v.Type)
|
||||||
|
}
|
||||||
|
if len(v.Config) != 2 {
|
||||||
|
return fmt.Errorf("got %v Config values, but expecting 2", len(v.Config))
|
||||||
|
}
|
||||||
|
if v.Config["woo"] != "foo" {
|
||||||
|
return fmt.Errorf("Config[\"woo\"] = \"%s\", but expecting \"foo\"", v.Config["woo"])
|
||||||
|
}
|
||||||
|
if v.Config["bar"] != "baz" {
|
||||||
|
return fmt.Errorf("Config[\"bar\"] = \"%s\", but expecting \"baz\"", v.Config["bar"])
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
unmarshalTest{
|
||||||
|
"with-empty-config",
|
||||||
|
`<JobPlugin type="foo-plugin"><configuration/></JobPlugin>`,
|
||||||
|
&JobPlugin{},
|
||||||
|
func (rv interface {}) error {
|
||||||
|
v := rv.(*JobPlugin)
|
||||||
|
if v.Type != "foo-plugin" {
|
||||||
|
return fmt.Errorf("got Type %s, but expecting foo-plugin", v.Type)
|
||||||
|
}
|
||||||
|
if len(v.Config) != 0 {
|
||||||
|
return fmt.Errorf("got %i Config values, but expecting 0", len(v.Config))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalJobCommand(t *testing.T) {
|
||||||
|
testMarshalXML(t, []marshalTest{
|
||||||
|
marshalTest{
|
||||||
|
"with-shell",
|
||||||
|
JobCommand{
|
||||||
|
ShellCommand: "command",
|
||||||
|
},
|
||||||
|
`<JobCommand><exec>command</exec></JobCommand>`,
|
||||||
|
},
|
||||||
|
marshalTest{
|
||||||
|
"with-script",
|
||||||
|
JobCommand{
|
||||||
|
Script: "script",
|
||||||
|
},
|
||||||
|
`<JobCommand><script>script</script></JobCommand>`,
|
||||||
|
},
|
||||||
|
marshalTest{
|
||||||
|
"with-script-interpreter",
|
||||||
|
JobCommand{
|
||||||
|
FileExtension: "sh",
|
||||||
|
Script: "Hello World!",
|
||||||
|
ScriptInterpreter: &JobCommandScriptInterpreter{
|
||||||
|
InvocationString: "sudo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`<JobCommand><fileExtension>sh</fileExtension><script>Hello World!</script><scriptinterpreter>sudo</scriptinterpreter></JobCommand>`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalJobCommand(t *testing.T) {
|
||||||
|
testUnmarshalXML(t, []unmarshalTest{
|
||||||
|
unmarshalTest{
|
||||||
|
"with-shell",
|
||||||
|
`<JobCommand><exec>command</exec></JobCommand>`,
|
||||||
|
&JobCommand{},
|
||||||
|
func (rv interface {}) error {
|
||||||
|
v := rv.(*JobCommand)
|
||||||
|
if v.ShellCommand != "command" {
|
||||||
|
return fmt.Errorf("got ShellCommand %s, but expecting command", v.ShellCommand)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
unmarshalTest{
|
||||||
|
"with-script",
|
||||||
|
`<JobCommand><script>script</script></JobCommand>`,
|
||||||
|
&JobCommand{},
|
||||||
|
func (rv interface {}) error {
|
||||||
|
v := rv.(*JobCommand)
|
||||||
|
if v.Script != "script" {
|
||||||
|
return fmt.Errorf("got Script %s, but expecting script", v.Script)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
unmarshalTest{
|
||||||
|
"with-script-interpreter",
|
||||||
|
`<JobCommand><script>Hello World!</script><fileExtension>sh</fileExtension><scriptinterpreter>sudo</scriptinterpreter></JobCommand>`,
|
||||||
|
&JobCommand{},
|
||||||
|
func (rv interface {}) error {
|
||||||
|
v := rv.(*JobCommand)
|
||||||
|
if v.FileExtension != "sh" {
|
||||||
|
return fmt.Errorf("got FileExtension %s, but expecting sh", v.FileExtension)
|
||||||
|
}
|
||||||
|
if v.Script != "Hello World!" {
|
||||||
|
return fmt.Errorf("got Script %s, but expecting Hello World!", v.Script)
|
||||||
|
}
|
||||||
|
if v.ScriptInterpreter == nil {
|
||||||
|
return fmt.Errorf("got %s, but expecting not nil", v.ScriptInterpreter)
|
||||||
|
}
|
||||||
|
if v.ScriptInterpreter.InvocationString != "sudo" {
|
||||||
|
return fmt.Errorf("got InvocationString %s, but expecting sudo", v.ScriptInterpreter.InvocationString)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalScriptInterpreter(t *testing.T) {
|
||||||
|
testMarshalXML(t, []marshalTest{
|
||||||
|
marshalTest{
|
||||||
|
"with-script-interpreter",
|
||||||
|
JobCommandScriptInterpreter{
|
||||||
|
InvocationString: "sudo",
|
||||||
|
},
|
||||||
|
`<scriptinterpreter>sudo</scriptinterpreter>`,
|
||||||
|
},
|
||||||
|
marshalTest{
|
||||||
|
"with-script-interpreter-quoted",
|
||||||
|
JobCommandScriptInterpreter{
|
||||||
|
ArgsQuoted: true,
|
||||||
|
InvocationString: "sudo",
|
||||||
|
},
|
||||||
|
`<scriptinterpreter argsquoted="true">sudo</scriptinterpreter>`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalScriptInterpreter(t *testing.T) {
|
||||||
|
testUnmarshalXML(t, []unmarshalTest{
|
||||||
|
unmarshalTest{
|
||||||
|
"with-script-interpreter",
|
||||||
|
`<scriptinterpreter>sudo</scriptinterpreter>`,
|
||||||
|
&JobCommandScriptInterpreter{},
|
||||||
|
func (rv interface {}) error {
|
||||||
|
v := rv.(*JobCommandScriptInterpreter)
|
||||||
|
if v.InvocationString != "sudo" {
|
||||||
|
return fmt.Errorf("got InvocationString %s, but expecting sudo", v.InvocationString)
|
||||||
|
}
|
||||||
|
if v.ArgsQuoted {
|
||||||
|
return fmt.Errorf("got ArgsQuoted %s, but expecting false", v.ArgsQuoted)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
unmarshalTest{
|
||||||
|
"with-script-interpreter-quoted",
|
||||||
|
`<scriptinterpreter argsquoted="true">sudo</scriptinterpreter>`,
|
||||||
|
&JobCommandScriptInterpreter{},
|
||||||
|
func (rv interface {}) error {
|
||||||
|
v := rv.(*JobCommandScriptInterpreter)
|
||||||
|
if v.InvocationString != "sudo" {
|
||||||
|
return fmt.Errorf("got InvocationString %s, but expecting sudo", v.InvocationString)
|
||||||
|
}
|
||||||
|
if ! v.ArgsQuoted {
|
||||||
|
return fmt.Errorf("got ArgsQuoted %s, but expecting true", v.ArgsQuoted)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalErrorHanlder(t *testing.T) {
|
||||||
|
testMarshalXML(t, []marshalTest{
|
||||||
|
marshalTest{
|
||||||
|
"with-errorhandler",
|
||||||
|
JobCommandSequence{
|
||||||
|
ContinueOnError: true,
|
||||||
|
OrderingStrategy: "step-first",
|
||||||
|
Commands: []JobCommand{
|
||||||
|
JobCommand{
|
||||||
|
Script: "inline_script",
|
||||||
|
ErrorHandler: &JobCommand{
|
||||||
|
ContinueOnError: true,
|
||||||
|
Script: "error_script",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`<sequence keepgoing="true" strategy="step-first"><command><errorhandler keepgoingOnSuccess="true"><script>error_script</script></errorhandler><script>inline_script</script></command></sequence>`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestMarshalJobOption(t *testing.T) {
|
||||||
|
testMarshalXML(t, []marshalTest{
|
||||||
|
marshalTest{
|
||||||
|
"with-option-basic",
|
||||||
|
JobOption{
|
||||||
|
Name: "basic",
|
||||||
|
},
|
||||||
|
`<option name="basic"></option>`,
|
||||||
|
},
|
||||||
|
marshalTest{
|
||||||
|
"with-option-multivalued",
|
||||||
|
JobOption{
|
||||||
|
Name: "Multivalued",
|
||||||
|
MultiValueDelimiter: "|",
|
||||||
|
RequirePredefinedChoice: true,
|
||||||
|
AllowsMultipleValues: true,
|
||||||
|
IsRequired: true,
|
||||||
|
ValueChoices: JobValueChoices([]string{"myValues"}),
|
||||||
|
},
|
||||||
|
`<option delimiter="|" enforcedvalues="true" multivalued="true" name="Multivalued" required="true" values="myValues"></option>`,
|
||||||
|
},
|
||||||
|
marshalTest{
|
||||||
|
"with-all-attributes",
|
||||||
|
JobOption{
|
||||||
|
Name: "advanced",
|
||||||
|
MultiValueDelimiter: "|",
|
||||||
|
RequirePredefinedChoice: true,
|
||||||
|
AllowsMultipleValues: true,
|
||||||
|
ValidationRegex: ".+",
|
||||||
|
IsRequired: true,
|
||||||
|
ObscureInput: true,
|
||||||
|
StoragePath: "myKey",
|
||||||
|
DefaultValue: "myValue",
|
||||||
|
ValueIsExposedToScripts: true,
|
||||||
|
ValueChoices: JobValueChoices([]string{"myValues"}),
|
||||||
|
ValueChoicesURL: "myValuesUrl",
|
||||||
|
},
|
||||||
|
`<option delimiter="|" enforcedvalues="true" multivalued="true" name="advanced" regex=".+" required="true" secure="true" storagePath="myKey" value="myValue" valueExposed="true" values="myValues" valuesUrl="myValuesUrl"></option>`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -365,6 +365,12 @@
|
||||||
"path": "github.com/apparentlymart/go-grafana-api",
|
"path": "github.com/apparentlymart/go-grafana-api",
|
||||||
"revision": "d49f95c81c580a4e7a15244b9b12dce8f60750f4"
|
"revision": "d49f95c81c580a4e7a15244b9b12dce8f60750f4"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "+2yCNqbcf7VcavAptooQReTGiHY=",
|
||||||
|
"path": "github.com/apparentlymart/go-rundeck-api",
|
||||||
|
"revision": "f6af74d34d1ef69a511c59173876fc1174c11f0d",
|
||||||
|
"revisionTime": "2016-08-26T14:30:32Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"comment": "v0.0.1-1-g43fcd8f",
|
"comment": "v0.0.1-1-g43fcd8f",
|
||||||
"path": "github.com/apparentlymart/go-rundeck-api/rundeck",
|
"path": "github.com/apparentlymart/go-rundeck-api/rundeck",
|
||||||
|
|
Loading…
Reference in New Issue