Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

provider/rundeck: Added multiple functionality. #9

Merged
merged 1 commit into from
Aug 20, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 69 additions & 4 deletions rundeck/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Description is manditory, even when its empty.

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 {
Expand Down Expand Up @@ -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"`
}
Expand All @@ -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.
Expand All @@ -121,19 +169,28 @@ 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"`

// 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"`

// 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"`

Expand All @@ -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"`
}

Expand Down
135 changes: 135 additions & 0 deletions rundeck/job_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,138 @@ func TestUnmarshalJobPlugin(t *testing.T) {
},
})
}

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><script>Hello World!</script><fileExtension>sh</fileExtension><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
},
},
})
}