2015-06-26 03:00:23 +02:00
package rundeck
import (
"fmt"
2016-12-13 13:00:53 +01:00
"strings"
2015-06-26 03:00:23 +02:00
"github.com/hashicorp/terraform/helper/schema"
"github.com/apparentlymart/go-rundeck-api/rundeck"
)
func resourceRundeckJob ( ) * schema . Resource {
return & schema . Resource {
Create : CreateJob ,
Update : UpdateJob ,
Delete : DeleteJob ,
Exists : JobExists ,
Read : ReadJob ,
Schema : map [ string ] * schema . Schema {
"id" : & schema . Schema {
Type : schema . TypeString ,
Computed : true ,
} ,
"name" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
} ,
"group_name" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
ForceNew : true ,
} ,
"project_name" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
ForceNew : true ,
} ,
"description" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
} ,
"log_level" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
Default : "INFO" ,
} ,
"allow_concurrent_executions" : & schema . Schema {
Type : schema . TypeBool ,
Optional : true ,
} ,
"max_thread_count" : & schema . Schema {
Type : schema . TypeInt ,
Optional : true ,
Default : 1 ,
} ,
"continue_on_error" : & schema . Schema {
Type : schema . TypeBool ,
Optional : true ,
} ,
"rank_order" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
Default : "ascending" ,
} ,
"rank_attribute" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
} ,
"preserve_options_order" : & schema . Schema {
Type : schema . TypeBool ,
Optional : true ,
2016-12-13 12:42:26 +01:00
Computed : true ,
2015-06-26 03:00:23 +02:00
} ,
"command_ordering_strategy" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
Default : "node-first" ,
} ,
"node_filter_query" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
} ,
"node_filter_exclude_precedence" : & schema . Schema {
Type : schema . TypeBool ,
Optional : true ,
} ,
2016-12-13 13:00:53 +01:00
"schedule" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
} ,
2015-06-26 03:00:23 +02:00
"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/
Type : schema . TypeList ,
Optional : true ,
Elem : & schema . Resource {
Schema : map [ string ] * schema . Schema {
"name" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
} ,
"default_value" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
} ,
"value_choices" : & schema . Schema {
Type : schema . TypeList ,
Optional : true ,
Elem : & schema . Schema {
Type : schema . TypeString ,
} ,
} ,
"value_choices_url" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
} ,
"require_predefined_choice" : & schema . Schema {
Type : schema . TypeBool ,
Optional : true ,
} ,
"validation_regex" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
} ,
"description" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
} ,
"required" : & schema . Schema {
Type : schema . TypeBool ,
Optional : true ,
} ,
"allow_multiple_values" : & schema . Schema {
Type : schema . TypeBool ,
Optional : true ,
} ,
"multi_value_delimiter" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
} ,
"obscure_input" : & schema . Schema {
Type : schema . TypeBool ,
Optional : true ,
} ,
"exposed_to_scripts" : & schema . Schema {
Type : schema . TypeBool ,
Optional : true ,
} ,
} ,
} ,
} ,
"command" : & schema . Schema {
Type : schema . TypeList ,
Required : true ,
Elem : & schema . Resource {
Schema : map [ string ] * schema . Schema {
"shell_command" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
} ,
"inline_script" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
} ,
"script_file" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
} ,
"script_file_args" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
} ,
"job" : & schema . Schema {
Type : schema . TypeList ,
Optional : true ,
Elem : & schema . Resource {
Schema : map [ string ] * schema . Schema {
"name" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
} ,
"group_name" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
} ,
"run_for_each_node" : & schema . Schema {
Type : schema . TypeBool ,
Optional : true ,
} ,
"args" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
} ,
} ,
} ,
} ,
"step_plugin" : & schema . Schema {
Type : schema . TypeList ,
Optional : true ,
Elem : resourceRundeckJobPluginResource ( ) ,
} ,
"node_step_plugin" : & schema . Schema {
Type : schema . TypeList ,
Optional : true ,
Elem : resourceRundeckJobPluginResource ( ) ,
} ,
} ,
} ,
} ,
} ,
}
}
func resourceRundeckJobPluginResource ( ) * schema . Resource {
return & schema . Resource {
Schema : map [ string ] * schema . Schema {
"type" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
} ,
"config" : & schema . Schema {
Type : schema . TypeMap ,
Optional : true ,
} ,
} ,
}
}
func CreateJob ( d * schema . ResourceData , meta interface { } ) error {
client := meta . ( * rundeck . Client )
job , err := jobFromResourceData ( d )
if err != nil {
return err
}
jobSummary , err := client . CreateJob ( job )
if err != nil {
return err
}
d . SetId ( jobSummary . ID )
d . Set ( "id" , jobSummary . ID )
return ReadJob ( d , meta )
}
func UpdateJob ( d * schema . ResourceData , meta interface { } ) error {
client := meta . ( * rundeck . Client )
job , err := jobFromResourceData ( d )
if err != nil {
return err
}
jobSummary , err := client . CreateOrUpdateJob ( job )
if err != nil {
return err
}
d . SetId ( jobSummary . ID )
d . Set ( "id" , jobSummary . ID )
return ReadJob ( d , meta )
}
func DeleteJob ( d * schema . ResourceData , meta interface { } ) error {
client := meta . ( * rundeck . Client )
err := client . DeleteJob ( d . Id ( ) )
if err != nil {
return err
}
d . SetId ( "" )
return nil
}
func JobExists ( d * schema . ResourceData , meta interface { } ) ( bool , error ) {
client := meta . ( * rundeck . Client )
_ , err := client . GetJob ( d . Id ( ) )
if err != nil {
if _ , ok := err . ( rundeck . NotFoundError ) ; ok {
err = nil
}
return false , err
}
return true , nil
}
func ReadJob ( d * schema . ResourceData , meta interface { } ) error {
client := meta . ( * rundeck . Client )
job , err := client . GetJob ( d . Id ( ) )
if err != nil {
return err
}
return jobToResourceData ( job , d )
}
func jobFromResourceData ( d * schema . ResourceData ) ( * rundeck . JobDetail , error ) {
job := & rundeck . JobDetail {
ID : d . Id ( ) ,
Name : d . Get ( "name" ) . ( string ) ,
GroupName : d . Get ( "group_name" ) . ( string ) ,
ProjectName : d . Get ( "project_name" ) . ( string ) ,
Description : d . Get ( "description" ) . ( string ) ,
LogLevel : d . Get ( "log_level" ) . ( string ) ,
AllowConcurrentExecutions : d . Get ( "allow_concurrent_executions" ) . ( bool ) ,
Dispatch : & rundeck . JobDispatch {
2015-10-07 22:35:06 +02:00
MaxThreadCount : d . Get ( "max_thread_count" ) . ( int ) ,
ContinueOnError : d . Get ( "continue_on_error" ) . ( bool ) ,
RankAttribute : d . Get ( "rank_attribute" ) . ( string ) ,
RankOrder : d . Get ( "rank_order" ) . ( string ) ,
2015-06-26 03:00:23 +02:00
} ,
}
sequence := & rundeck . JobCommandSequence {
ContinueOnError : d . Get ( "continue_on_error" ) . ( bool ) ,
OrderingStrategy : d . Get ( "command_ordering_strategy" ) . ( string ) ,
Commands : [ ] rundeck . JobCommand { } ,
}
commandConfigs := d . Get ( "command" ) . ( [ ] interface { } )
for _ , commandI := range commandConfigs {
commandMap := commandI . ( map [ string ] interface { } )
command := rundeck . JobCommand {
ShellCommand : commandMap [ "shell_command" ] . ( string ) ,
Script : commandMap [ "inline_script" ] . ( string ) ,
ScriptFile : commandMap [ "script_file" ] . ( string ) ,
ScriptFileArgs : commandMap [ "script_file_args" ] . ( string ) ,
}
jobRefsI := commandMap [ "job" ] . ( [ ] interface { } )
if len ( jobRefsI ) > 1 {
return nil , fmt . Errorf ( "rundeck command may have no more than one job" )
}
if len ( jobRefsI ) > 0 {
jobRefMap := jobRefsI [ 0 ] . ( map [ string ] interface { } )
command . Job = & rundeck . JobCommandJobRef {
Name : jobRefMap [ "name" ] . ( string ) ,
GroupName : jobRefMap [ "group_name" ] . ( string ) ,
RunForEachNode : jobRefMap [ "run_for_each_node" ] . ( bool ) ,
Arguments : rundeck . JobCommandJobRefArguments ( jobRefMap [ "args" ] . ( string ) ) ,
}
}
stepPluginsI := commandMap [ "step_plugin" ] . ( [ ] interface { } )
if len ( stepPluginsI ) > 1 {
return nil , fmt . Errorf ( "rundeck command may have no more than one step plugin" )
}
if len ( stepPluginsI ) > 0 {
stepPluginMap := stepPluginsI [ 0 ] . ( map [ string ] interface { } )
configI := stepPluginMap [ "config" ] . ( map [ string ] interface { } )
config := map [ string ] string { }
for k , v := range configI {
config [ k ] = v . ( string )
}
command . StepPlugin = & rundeck . JobPlugin {
Type : stepPluginMap [ "type" ] . ( string ) ,
Config : config ,
}
}
stepPluginsI = commandMap [ "node_step_plugin" ] . ( [ ] interface { } )
if len ( stepPluginsI ) > 1 {
return nil , fmt . Errorf ( "rundeck command may have no more than one node step plugin" )
}
if len ( stepPluginsI ) > 0 {
stepPluginMap := stepPluginsI [ 0 ] . ( map [ string ] interface { } )
configI := stepPluginMap [ "config" ] . ( map [ string ] interface { } )
config := map [ string ] string { }
for k , v := range configI {
config [ k ] = v . ( string )
}
command . NodeStepPlugin = & rundeck . JobPlugin {
Type : stepPluginMap [ "type" ] . ( string ) ,
Config : config ,
}
}
sequence . Commands = append ( sequence . Commands , command )
}
job . CommandSequence = sequence
optionConfigsI := d . Get ( "option" ) . ( [ ] interface { } )
if len ( optionConfigsI ) > 0 {
optionsConfig := & rundeck . JobOptions {
PreserveOrder : d . Get ( "preserve_options_order" ) . ( bool ) ,
Options : [ ] rundeck . JobOption { } ,
}
for _ , optionI := range optionConfigsI {
optionMap := optionI . ( map [ string ] interface { } )
option := rundeck . JobOption {
Name : optionMap [ "name" ] . ( string ) ,
DefaultValue : optionMap [ "default_value" ] . ( string ) ,
ValueChoices : rundeck . JobValueChoices ( [ ] string { } ) ,
ValueChoicesURL : optionMap [ "value_choices_url" ] . ( string ) ,
RequirePredefinedChoice : optionMap [ "require_predefined_choice" ] . ( bool ) ,
ValidationRegex : optionMap [ "validation_regex" ] . ( string ) ,
Description : optionMap [ "description" ] . ( string ) ,
IsRequired : optionMap [ "required" ] . ( bool ) ,
AllowsMultipleValues : optionMap [ "allow_multiple_values" ] . ( bool ) ,
MultiValueDelimiter : optionMap [ "multi_value_delimiter" ] . ( string ) ,
ObscureInput : optionMap [ "obscure_input" ] . ( bool ) ,
ValueIsExposedToScripts : optionMap [ "exposed_to_scripts" ] . ( bool ) ,
}
for _ , iv := range optionMap [ "value_choices" ] . ( [ ] interface { } ) {
option . ValueChoices = append ( option . ValueChoices , iv . ( string ) )
}
optionsConfig . Options = append ( optionsConfig . Options , option )
}
job . OptionsConfig = optionsConfig
}
if d . Get ( "node_filter_query" ) . ( string ) != "" {
job . NodeFilter = & rundeck . JobNodeFilter {
ExcludePrecedence : d . Get ( "node_filter_exclude_precedence" ) . ( bool ) ,
Query : d . Get ( "node_filter_query" ) . ( string ) ,
}
}
2016-12-13 13:00:53 +01:00
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 ] ,
} ,
}
}
2015-06-26 03:00:23 +02:00
return job , nil
}
func jobToResourceData ( job * rundeck . JobDetail , d * schema . ResourceData ) error {
d . SetId ( job . ID )
d . Set ( "id" , job . ID )
d . Set ( "name" , job . Name )
d . Set ( "group_name" , job . GroupName )
2015-12-14 02:38:08 +01:00
// The project name is not consistently returned in all rundeck versions,
// so we'll only update it if it's set. Jobs can't move between projects
// anyway, so this is harmless.
if job . ProjectName != "" {
d . Set ( "project_name" , job . ProjectName )
}
2015-06-26 03:00:23 +02:00
d . Set ( "description" , job . Description )
d . Set ( "log_level" , job . LogLevel )
d . Set ( "allow_concurrent_executions" , job . AllowConcurrentExecutions )
if job . Dispatch != nil {
d . Set ( "max_thread_count" , job . Dispatch . MaxThreadCount )
d . Set ( "continue_on_error" , job . Dispatch . ContinueOnError )
d . Set ( "rank_attribute" , job . Dispatch . RankAttribute )
d . Set ( "rank_order" , job . Dispatch . RankOrder )
} else {
d . Set ( "max_thread_count" , nil )
d . Set ( "continue_on_error" , nil )
d . Set ( "rank_attribute" , nil )
d . Set ( "rank_order" , nil )
}
d . Set ( "node_filter_query" , nil )
d . Set ( "node_filter_exclude_precedence" , nil )
if job . NodeFilter != nil {
d . Set ( "node_filter_query" , job . NodeFilter . Query )
d . Set ( "node_filter_exclude_precedence" , job . NodeFilter . ExcludePrecedence )
}
optionConfigsI := [ ] interface { } { }
if job . OptionsConfig != nil {
d . Set ( "preserve_options_order" , job . OptionsConfig . PreserveOrder )
for _ , option := range job . OptionsConfig . Options {
optionConfigI := map [ string ] interface { } {
"name" : option . Name ,
"default_value" : option . DefaultValue ,
"value_choices" : option . ValueChoices ,
"value_choices_url" : option . ValueChoicesURL ,
"require_predefined_choice" : option . RequirePredefinedChoice ,
"validation_regex" : option . ValidationRegex ,
"decription" : option . Description ,
"required" : option . IsRequired ,
"allow_multiple_values" : option . AllowsMultipleValues ,
2015-09-11 20:56:20 +02:00
"multi_value_delimiter" : option . MultiValueDelimiter ,
2015-06-26 03:00:23 +02:00
"obscure_input" : option . ObscureInput ,
"exposed_to_scripts" : option . ValueIsExposedToScripts ,
}
optionConfigsI = append ( optionConfigsI , optionConfigI )
}
}
d . Set ( "option" , optionConfigsI )
commandConfigsI := [ ] interface { } { }
if job . CommandSequence != nil {
d . Set ( "command_ordering_strategy" , job . CommandSequence . OrderingStrategy )
for _ , command := range job . CommandSequence . Commands {
commandConfigI := map [ string ] interface { } {
"shell_command" : command . ShellCommand ,
"inline_script" : command . Script ,
"script_file" : command . ScriptFile ,
"script_file_args" : command . ScriptFileArgs ,
}
if command . Job != nil {
commandConfigI [ "job" ] = [ ] interface { } {
map [ string ] interface { } {
"name" : command . Job . Name ,
"group_name" : command . Job . GroupName ,
"run_for_each_node" : command . Job . RunForEachNode ,
"args" : command . Job . Arguments ,
} ,
}
}
if command . StepPlugin != nil {
commandConfigI [ "step_plugin" ] = [ ] interface { } {
map [ string ] interface { } {
"type" : command . StepPlugin . Type ,
"config" : map [ string ] string ( command . StepPlugin . Config ) ,
} ,
}
}
if command . NodeStepPlugin != nil {
commandConfigI [ "node_step_plugin" ] = [ ] interface { } {
map [ string ] interface { } {
"type" : command . NodeStepPlugin . Type ,
"config" : map [ string ] string ( command . NodeStepPlugin . Config ) ,
} ,
}
}
commandConfigsI = append ( commandConfigsI , commandConfigI )
}
}
d . Set ( "command" , commandConfigsI )
2016-12-13 13:00:53 +01:00
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 , " " ) )
}
2015-06-26 03:00:23 +02:00
return nil
}