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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
|
||||
|
@ -99,6 +100,11 @@ func resourceRundeckJob() *schema.Resource {
|
|||
Optional: true,
|
||||
},
|
||||
|
||||
"schedule": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
"option": &schema.Schema{
|
||||
// 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/
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -562,5 +592,22 @@ func jobToResourceData(job *rundeck.JobDetail, d *schema.ResourceData) error {
|
|||
}
|
||||
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
|
||||
}
|
||||
|
|
|
@ -92,6 +92,7 @@ resource "rundeck_job" "test" {
|
|||
allow_concurrent_executions = 1
|
||||
max_thread_count = 1
|
||||
rank_order = "ascending"
|
||||
schedule = "0 0 12 * * * *"
|
||||
option {
|
||||
name = "foo"
|
||||
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"`
|
||||
ProjectName string `xml:"context>project,omitempty"`
|
||||
OptionsConfig *JobOptions `xml:"context>options,omitempty"`
|
||||
Description string `xml:"description,omitempty"`
|
||||
Description string `xml:"description"`
|
||||
LogLevel string `xml:"loglevel,omitempty"`
|
||||
AllowConcurrentExecutions bool `xml:"multipleExecutions"`
|
||||
Dispatch *JobDispatch `xml:"dispatch"`
|
||||
AllowConcurrentExecutions bool `xml:"multipleExecutions,omitempty"`
|
||||
Dispatch *JobDispatch `xml:"dispatch,omitempty"`
|
||||
CommandSequence *JobCommandSequence `xml:"sequence,omitempty"`
|
||||
Timeout string `xml:"timeout,omitempty"`
|
||||
Retry string `xml:"retry,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 {
|
||||
|
@ -53,13 +98,40 @@ type JobOptions struct {
|
|||
type JobOption struct {
|
||||
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
|
||||
// into job commands.
|
||||
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.
|
||||
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.
|
||||
ValueChoices JobValueChoices `xml:"values,attr"`
|
||||
|
||||
|
@ -67,34 +139,11 @@ type JobOption struct {
|
|||
// Mutually exclusive with ValueChoices
|
||||
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 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
|
||||
// for a job option.
|
||||
type JobValueChoices []string
|
||||
|
@ -112,6 +161,9 @@ type JobCommandSequence struct {
|
|||
|
||||
// Sequence of commands to run in the sequence.
|
||||
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.
|
||||
|
@ -120,9 +172,21 @@ type JobCommandSequence struct {
|
|||
type JobCommand struct {
|
||||
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.
|
||||
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
|
||||
// a shell script it should have an appropriate #! line.
|
||||
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.
|
||||
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.
|
||||
Job *JobCommandJobRef `xml:"jobref"`
|
||||
|
||||
|
@ -143,12 +210,20 @@ type JobCommand struct {
|
|||
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.
|
||||
type JobCommandJobRef struct {
|
||||
XMLName xml.Name `xml:"jobref"`
|
||||
Name string `xml:"name,attr"`
|
||||
GroupName string `xml:"group,attr"`
|
||||
RunForEachNode bool `xml:"nodeStep,attr"`
|
||||
NodeFilter *JobNodeFilter `xml:"nodefilters,omitempty"`
|
||||
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",
|
||||
"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",
|
||||
"path": "github.com/apparentlymart/go-rundeck-api/rundeck",
|
||||
|
|
Loading…
Reference in New Issue