From cbea1481319d76e34621791a414cc0a8b976fb49 Mon Sep 17 00:00:00 2001 From: dbxbbm Date: Fri, 19 Aug 2016 11:24:53 +0200 Subject: [PATCH] provider/rundeck: Added multiple functionality. --- rundeck/job.go | 73 ++++++++++++++++++++++-- rundeck/job_test.go | 135 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+), 4 deletions(-) diff --git a/rundeck/job.go b/rundeck/job.go index 844f441..4311e00 100644 --- a/rundeck/job.go +++ b/rundeck/job.go @@ -30,13 +30,55 @@ 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"` NodeFilter *JobNodeFilter `xml:"nodefilters,omitempty"` - NodesSelectedByDefault bool `xml:"nodesSelectedByDefault,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"` +} + +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 { @@ -92,6 +134,9 @@ type JobOption struct { // 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"` + // If set, the value can be accessed from scripts. ValueIsExposedToScripts bool `xml:"valueExposed,attr,omitempty"` } @@ -113,6 +158,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. @@ -121,6 +169,9 @@ type JobCommandSequence struct { type JobCommand struct { XMLName xml.Name + // Description + Description string `xml:"description,omitempty"` + // A literal shell command to run. ShellCommand string `xml:"exec,omitempty"` @@ -128,12 +179,18 @@ type JobCommand struct { // a shell script it should have an appropriate #! line. Script string `xml:"script,omitempty"` + // Add extension to the temporary filename. + FileExtension string `xml:"fileExtension,omitempty"` + // A pre-existing file (on the target nodes) that will be executed. ScriptFile string `xml:"scriptfile,omitempty"` // 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"` @@ -144,12 +201,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"` } diff --git a/rundeck/job_test.go b/rundeck/job_test.go index 3229a68..9e02970 100644 --- a/rundeck/job_test.go +++ b/rundeck/job_test.go @@ -111,3 +111,138 @@ func TestUnmarshalJobPlugin(t *testing.T) { }, }) } + +func TestMarshalJobCommand(t *testing.T) { + testMarshalXML(t, []marshalTest{ + marshalTest{ + "with-shell", + JobCommand{ + ShellCommand: "command", + }, + `command`, + }, + marshalTest{ + "with-script", + JobCommand{ + Script: "script", + }, + ``, + }, + marshalTest{ + "with-script-interpreter", + JobCommand{ + FileExtension: "sh", + Script: "Hello World!", + ScriptInterpreter: &JobCommandScriptInterpreter{ + InvocationString: "sudo", + }, + }, + `shsudo`, + }, + }) +} + +func TestUnmarshalJobCommand(t *testing.T) { + testUnmarshalXML(t, []unmarshalTest{ + unmarshalTest{ + "with-shell", + `command`, + &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{}, + 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", + `shsudo`, + &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", + }, + `sudo`, + }, + marshalTest{ + "with-script-interpreter-quoted", + JobCommandScriptInterpreter{ + ArgsQuoted: true, + InvocationString: "sudo", + }, + `sudo`, + }, + }) +} + +func TestUnmarshalScriptInterpreter(t *testing.T) { + testUnmarshalXML(t, []unmarshalTest{ + unmarshalTest{ + "with-script-interpreter", + `sudo`, + &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", + `sudo`, + &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 + }, + }, + }) +}