github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/builtin/providers/rundeck/resource_job.go (about)

     1  package rundeck
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/terraform/helper/schema"
     7  
     8  	"github.com/apparentlymart/go-rundeck-api/rundeck"
     9  )
    10  
    11  func resourceRundeckJob() *schema.Resource {
    12  	return &schema.Resource{
    13  		Create: CreateJob,
    14  		Update: UpdateJob,
    15  		Delete: DeleteJob,
    16  		Exists: JobExists,
    17  		Read:   ReadJob,
    18  
    19  		Schema: map[string]*schema.Schema{
    20  			"id": &schema.Schema{
    21  				Type:     schema.TypeString,
    22  				Computed: true,
    23  			},
    24  
    25  			"name": &schema.Schema{
    26  				Type:     schema.TypeString,
    27  				Required: true,
    28  			},
    29  
    30  			"group_name": &schema.Schema{
    31  				Type:     schema.TypeString,
    32  				Optional: true,
    33  				ForceNew: true,
    34  			},
    35  
    36  			"project_name": &schema.Schema{
    37  				Type:     schema.TypeString,
    38  				Required: true,
    39  				ForceNew: true,
    40  			},
    41  
    42  			"description": &schema.Schema{
    43  				Type:     schema.TypeString,
    44  				Required: true,
    45  			},
    46  
    47  			"log_level": &schema.Schema{
    48  				Type:     schema.TypeString,
    49  				Optional: true,
    50  				Default:  "INFO",
    51  			},
    52  
    53  			"allow_concurrent_executions": &schema.Schema{
    54  				Type:     schema.TypeBool,
    55  				Optional: true,
    56  			},
    57  
    58  			"max_thread_count": &schema.Schema{
    59  				Type:     schema.TypeInt,
    60  				Optional: true,
    61  				Default:  1,
    62  			},
    63  
    64  			"continue_on_error": &schema.Schema{
    65  				Type:     schema.TypeBool,
    66  				Optional: true,
    67  			},
    68  
    69  			"rank_order": &schema.Schema{
    70  				Type:     schema.TypeString,
    71  				Optional: true,
    72  				Default:  "ascending",
    73  			},
    74  
    75  			"rank_attribute": &schema.Schema{
    76  				Type:     schema.TypeString,
    77  				Optional: true,
    78  			},
    79  
    80  			"preserve_options_order": &schema.Schema{
    81  				Type:     schema.TypeBool,
    82  				Optional: true,
    83  			},
    84  
    85  			"command_ordering_strategy": &schema.Schema{
    86  				Type:     schema.TypeString,
    87  				Optional: true,
    88  				Default:  "node-first",
    89  			},
    90  
    91  			"node_filter_query": &schema.Schema{
    92  				Type:     schema.TypeString,
    93  				Optional: true,
    94  			},
    95  
    96  			"node_filter_exclude_precedence": &schema.Schema{
    97  				Type:     schema.TypeBool,
    98  				Optional: true,
    99  			},
   100  
   101  			"option": &schema.Schema{
   102  				// This is a list because order is important when preserve_options_order is
   103  				// set. When it's not set the order is unimportant but preserved by Rundeck/
   104  				Type:     schema.TypeList,
   105  				Optional: true,
   106  				Elem: &schema.Resource{
   107  					Schema: map[string]*schema.Schema{
   108  						"name": &schema.Schema{
   109  							Type:     schema.TypeString,
   110  							Required: true,
   111  						},
   112  
   113  						"default_value": &schema.Schema{
   114  							Type:     schema.TypeString,
   115  							Optional: true,
   116  						},
   117  
   118  						"value_choices": &schema.Schema{
   119  							Type:     schema.TypeList,
   120  							Optional: true,
   121  							Elem: &schema.Schema{
   122  								Type: schema.TypeString,
   123  							},
   124  						},
   125  
   126  						"value_choices_url": &schema.Schema{
   127  							Type:     schema.TypeString,
   128  							Optional: true,
   129  						},
   130  
   131  						"require_predefined_choice": &schema.Schema{
   132  							Type:     schema.TypeBool,
   133  							Optional: true,
   134  						},
   135  
   136  						"validation_regex": &schema.Schema{
   137  							Type:     schema.TypeString,
   138  							Optional: true,
   139  						},
   140  
   141  						"description": &schema.Schema{
   142  							Type:     schema.TypeString,
   143  							Optional: true,
   144  						},
   145  
   146  						"required": &schema.Schema{
   147  							Type:     schema.TypeBool,
   148  							Optional: true,
   149  						},
   150  
   151  						"allow_multiple_values": &schema.Schema{
   152  							Type:     schema.TypeBool,
   153  							Optional: true,
   154  						},
   155  
   156  						"multi_value_delimiter": &schema.Schema{
   157  							Type:     schema.TypeString,
   158  							Optional: true,
   159  						},
   160  
   161  						"obscure_input": &schema.Schema{
   162  							Type:     schema.TypeBool,
   163  							Optional: true,
   164  						},
   165  
   166  						"exposed_to_scripts": &schema.Schema{
   167  							Type:     schema.TypeBool,
   168  							Optional: true,
   169  						},
   170  					},
   171  				},
   172  			},
   173  
   174  			"command": &schema.Schema{
   175  				Type:     schema.TypeList,
   176  				Required: true,
   177  				Elem: &schema.Resource{
   178  					Schema: map[string]*schema.Schema{
   179  						"shell_command": &schema.Schema{
   180  							Type:     schema.TypeString,
   181  							Optional: true,
   182  						},
   183  
   184  						"inline_script": &schema.Schema{
   185  							Type:     schema.TypeString,
   186  							Optional: true,
   187  						},
   188  
   189  						"script_file": &schema.Schema{
   190  							Type:     schema.TypeString,
   191  							Optional: true,
   192  						},
   193  
   194  						"script_file_args": &schema.Schema{
   195  							Type:     schema.TypeString,
   196  							Optional: true,
   197  						},
   198  
   199  						"job": &schema.Schema{
   200  							Type:     schema.TypeList,
   201  							Optional: true,
   202  							Elem: &schema.Resource{
   203  								Schema: map[string]*schema.Schema{
   204  									"name": &schema.Schema{
   205  										Type:     schema.TypeString,
   206  										Required: true,
   207  									},
   208  									"group_name": &schema.Schema{
   209  										Type:     schema.TypeString,
   210  										Optional: true,
   211  									},
   212  									"run_for_each_node": &schema.Schema{
   213  										Type:     schema.TypeBool,
   214  										Optional: true,
   215  									},
   216  									"args": &schema.Schema{
   217  										Type:     schema.TypeString,
   218  										Optional: true,
   219  									},
   220  								},
   221  							},
   222  						},
   223  
   224  						"step_plugin": &schema.Schema{
   225  							Type:     schema.TypeList,
   226  							Optional: true,
   227  							Elem:     resourceRundeckJobPluginResource(),
   228  						},
   229  
   230  						"node_step_plugin": &schema.Schema{
   231  							Type:     schema.TypeList,
   232  							Optional: true,
   233  							Elem:     resourceRundeckJobPluginResource(),
   234  						},
   235  					},
   236  				},
   237  			},
   238  		},
   239  	}
   240  }
   241  
   242  func resourceRundeckJobPluginResource() *schema.Resource {
   243  	return &schema.Resource{
   244  		Schema: map[string]*schema.Schema{
   245  			"type": &schema.Schema{
   246  				Type:     schema.TypeString,
   247  				Required: true,
   248  			},
   249  			"config": &schema.Schema{
   250  				Type:     schema.TypeMap,
   251  				Optional: true,
   252  			},
   253  		},
   254  	}
   255  }
   256  
   257  func CreateJob(d *schema.ResourceData, meta interface{}) error {
   258  	client := meta.(*rundeck.Client)
   259  
   260  	job, err := jobFromResourceData(d)
   261  	if err != nil {
   262  		return err
   263  	}
   264  
   265  	jobSummary, err := client.CreateJob(job)
   266  	if err != nil {
   267  		return err
   268  	}
   269  
   270  	d.SetId(jobSummary.ID)
   271  	d.Set("id", jobSummary.ID)
   272  
   273  	return ReadJob(d, meta)
   274  }
   275  
   276  func UpdateJob(d *schema.ResourceData, meta interface{}) error {
   277  	client := meta.(*rundeck.Client)
   278  
   279  	job, err := jobFromResourceData(d)
   280  	if err != nil {
   281  		return err
   282  	}
   283  
   284  	jobSummary, err := client.CreateOrUpdateJob(job)
   285  	if err != nil {
   286  		return err
   287  	}
   288  
   289  	d.SetId(jobSummary.ID)
   290  	d.Set("id", jobSummary.ID)
   291  
   292  	return ReadJob(d, meta)
   293  }
   294  
   295  func DeleteJob(d *schema.ResourceData, meta interface{}) error {
   296  	client := meta.(*rundeck.Client)
   297  
   298  	err := client.DeleteJob(d.Id())
   299  	if err != nil {
   300  		return err
   301  	}
   302  
   303  	d.SetId("")
   304  
   305  	return nil
   306  }
   307  
   308  func JobExists(d *schema.ResourceData, meta interface{}) (bool, error) {
   309  	client := meta.(*rundeck.Client)
   310  
   311  	_, err := client.GetJob(d.Id())
   312  	if err != nil {
   313  		if _, ok := err.(rundeck.NotFoundError); ok {
   314  			err = nil
   315  		}
   316  		return false, err
   317  	}
   318  
   319  	return true, nil
   320  }
   321  
   322  func ReadJob(d *schema.ResourceData, meta interface{}) error {
   323  	client := meta.(*rundeck.Client)
   324  
   325  	job, err := client.GetJob(d.Id())
   326  	if err != nil {
   327  		return err
   328  	}
   329  
   330  	return jobToResourceData(job, d)
   331  }
   332  
   333  func jobFromResourceData(d *schema.ResourceData) (*rundeck.JobDetail, error) {
   334  	job := &rundeck.JobDetail{
   335  		ID:                        d.Id(),
   336  		Name:                      d.Get("name").(string),
   337  		GroupName:                 d.Get("group_name").(string),
   338  		ProjectName:               d.Get("project_name").(string),
   339  		Description:               d.Get("description").(string),
   340  		LogLevel:                  d.Get("log_level").(string),
   341  		AllowConcurrentExecutions: d.Get("allow_concurrent_executions").(bool),
   342  		Dispatch: &rundeck.JobDispatch{
   343  			MaxThreadCount:  d.Get("max_thread_count").(int),
   344  			ContinueOnError: d.Get("continue_on_error").(bool),
   345  			RankAttribute:   d.Get("rank_attribute").(string),
   346  			RankOrder:       d.Get("rank_order").(string),
   347  		},
   348  	}
   349  
   350  	sequence := &rundeck.JobCommandSequence{
   351  		ContinueOnError:  d.Get("continue_on_error").(bool),
   352  		OrderingStrategy: d.Get("command_ordering_strategy").(string),
   353  		Commands:         []rundeck.JobCommand{},
   354  	}
   355  
   356  	commandConfigs := d.Get("command").([]interface{})
   357  	for _, commandI := range commandConfigs {
   358  		commandMap := commandI.(map[string]interface{})
   359  		command := rundeck.JobCommand{
   360  			ShellCommand:   commandMap["shell_command"].(string),
   361  			Script:         commandMap["inline_script"].(string),
   362  			ScriptFile:     commandMap["script_file"].(string),
   363  			ScriptFileArgs: commandMap["script_file_args"].(string),
   364  		}
   365  
   366  		jobRefsI := commandMap["job"].([]interface{})
   367  		if len(jobRefsI) > 1 {
   368  			return nil, fmt.Errorf("rundeck command may have no more than one job")
   369  		}
   370  		if len(jobRefsI) > 0 {
   371  			jobRefMap := jobRefsI[0].(map[string]interface{})
   372  			command.Job = &rundeck.JobCommandJobRef{
   373  				Name:           jobRefMap["name"].(string),
   374  				GroupName:      jobRefMap["group_name"].(string),
   375  				RunForEachNode: jobRefMap["run_for_each_node"].(bool),
   376  				Arguments:      rundeck.JobCommandJobRefArguments(jobRefMap["args"].(string)),
   377  			}
   378  		}
   379  
   380  		stepPluginsI := commandMap["step_plugin"].([]interface{})
   381  		if len(stepPluginsI) > 1 {
   382  			return nil, fmt.Errorf("rundeck command may have no more than one step plugin")
   383  		}
   384  		if len(stepPluginsI) > 0 {
   385  			stepPluginMap := stepPluginsI[0].(map[string]interface{})
   386  			configI := stepPluginMap["config"].(map[string]interface{})
   387  			config := map[string]string{}
   388  			for k, v := range configI {
   389  				config[k] = v.(string)
   390  			}
   391  			command.StepPlugin = &rundeck.JobPlugin{
   392  				Type:   stepPluginMap["type"].(string),
   393  				Config: config,
   394  			}
   395  		}
   396  
   397  		stepPluginsI = commandMap["node_step_plugin"].([]interface{})
   398  		if len(stepPluginsI) > 1 {
   399  			return nil, fmt.Errorf("rundeck command may have no more than one node step plugin")
   400  		}
   401  		if len(stepPluginsI) > 0 {
   402  			stepPluginMap := stepPluginsI[0].(map[string]interface{})
   403  			configI := stepPluginMap["config"].(map[string]interface{})
   404  			config := map[string]string{}
   405  			for k, v := range configI {
   406  				config[k] = v.(string)
   407  			}
   408  			command.NodeStepPlugin = &rundeck.JobPlugin{
   409  				Type:   stepPluginMap["type"].(string),
   410  				Config: config,
   411  			}
   412  		}
   413  
   414  		sequence.Commands = append(sequence.Commands, command)
   415  	}
   416  	job.CommandSequence = sequence
   417  
   418  	optionConfigsI := d.Get("option").([]interface{})
   419  	if len(optionConfigsI) > 0 {
   420  		optionsConfig := &rundeck.JobOptions{
   421  			PreserveOrder: d.Get("preserve_options_order").(bool),
   422  			Options:       []rundeck.JobOption{},
   423  		}
   424  		for _, optionI := range optionConfigsI {
   425  			optionMap := optionI.(map[string]interface{})
   426  			option := rundeck.JobOption{
   427  				Name:                    optionMap["name"].(string),
   428  				DefaultValue:            optionMap["default_value"].(string),
   429  				ValueChoices:            rundeck.JobValueChoices([]string{}),
   430  				ValueChoicesURL:         optionMap["value_choices_url"].(string),
   431  				RequirePredefinedChoice: optionMap["require_predefined_choice"].(bool),
   432  				ValidationRegex:         optionMap["validation_regex"].(string),
   433  				Description:             optionMap["description"].(string),
   434  				IsRequired:              optionMap["required"].(bool),
   435  				AllowsMultipleValues:    optionMap["allow_multiple_values"].(bool),
   436  				MultiValueDelimiter:     optionMap["multi_value_delimiter"].(string),
   437  				ObscureInput:            optionMap["obscure_input"].(bool),
   438  				ValueIsExposedToScripts: optionMap["exposed_to_scripts"].(bool),
   439  			}
   440  
   441  			for _, iv := range optionMap["value_choices"].([]interface{}) {
   442  				option.ValueChoices = append(option.ValueChoices, iv.(string))
   443  			}
   444  
   445  			optionsConfig.Options = append(optionsConfig.Options, option)
   446  		}
   447  		job.OptionsConfig = optionsConfig
   448  	}
   449  
   450  	if d.Get("node_filter_query").(string) != "" {
   451  		job.NodeFilter = &rundeck.JobNodeFilter{
   452  			ExcludePrecedence: d.Get("node_filter_exclude_precedence").(bool),
   453  			Query:             d.Get("node_filter_query").(string),
   454  		}
   455  	}
   456  
   457  	return job, nil
   458  }
   459  
   460  func jobToResourceData(job *rundeck.JobDetail, d *schema.ResourceData) error {
   461  
   462  	d.SetId(job.ID)
   463  	d.Set("id", job.ID)
   464  	d.Set("name", job.Name)
   465  	d.Set("group_name", job.GroupName)
   466  	d.Set("project_name", job.ProjectName)
   467  	d.Set("description", job.Description)
   468  	d.Set("log_level", job.LogLevel)
   469  	d.Set("allow_concurrent_executions", job.AllowConcurrentExecutions)
   470  	if job.Dispatch != nil {
   471  		d.Set("max_thread_count", job.Dispatch.MaxThreadCount)
   472  		d.Set("continue_on_error", job.Dispatch.ContinueOnError)
   473  		d.Set("rank_attribute", job.Dispatch.RankAttribute)
   474  		d.Set("rank_order", job.Dispatch.RankOrder)
   475  	} else {
   476  		d.Set("max_thread_count", nil)
   477  		d.Set("continue_on_error", nil)
   478  		d.Set("rank_attribute", nil)
   479  		d.Set("rank_order", nil)
   480  	}
   481  
   482  	d.Set("node_filter_query", nil)
   483  	d.Set("node_filter_exclude_precedence", nil)
   484  	if job.NodeFilter != nil {
   485  		d.Set("node_filter_query", job.NodeFilter.Query)
   486  		d.Set("node_filter_exclude_precedence", job.NodeFilter.ExcludePrecedence)
   487  	}
   488  
   489  	optionConfigsI := []interface{}{}
   490  	if job.OptionsConfig != nil {
   491  		d.Set("preserve_options_order", job.OptionsConfig.PreserveOrder)
   492  		for _, option := range job.OptionsConfig.Options {
   493  			optionConfigI := map[string]interface{}{
   494  				"name":                      option.Name,
   495  				"default_value":             option.DefaultValue,
   496  				"value_choices":             option.ValueChoices,
   497  				"value_choices_url":         option.ValueChoicesURL,
   498  				"require_predefined_choice": option.RequirePredefinedChoice,
   499  				"validation_regex":          option.ValidationRegex,
   500  				"decription":                option.Description,
   501  				"required":                  option.IsRequired,
   502  				"allow_multiple_values":     option.AllowsMultipleValues,
   503  				"multi_value_delimiter":     option.MultiValueDelimiter,
   504  				"obscure_input":             option.ObscureInput,
   505  				"exposed_to_scripts":        option.ValueIsExposedToScripts,
   506  			}
   507  			optionConfigsI = append(optionConfigsI, optionConfigI)
   508  		}
   509  	}
   510  	d.Set("option", optionConfigsI)
   511  
   512  	commandConfigsI := []interface{}{}
   513  	if job.CommandSequence != nil {
   514  		d.Set("command_ordering_strategy", job.CommandSequence.OrderingStrategy)
   515  		for _, command := range job.CommandSequence.Commands {
   516  			commandConfigI := map[string]interface{}{
   517  				"shell_command":    command.ShellCommand,
   518  				"inline_script":    command.Script,
   519  				"script_file":      command.ScriptFile,
   520  				"script_file_args": command.ScriptFileArgs,
   521  			}
   522  
   523  			if command.Job != nil {
   524  				commandConfigI["job"] = []interface{}{
   525  					map[string]interface{}{
   526  						"name":              command.Job.Name,
   527  						"group_name":        command.Job.GroupName,
   528  						"run_for_each_node": command.Job.RunForEachNode,
   529  						"args":              command.Job.Arguments,
   530  					},
   531  				}
   532  			}
   533  
   534  			if command.StepPlugin != nil {
   535  				commandConfigI["step_plugin"] = []interface{}{
   536  					map[string]interface{}{
   537  						"type":   command.StepPlugin.Type,
   538  						"config": map[string]string(command.StepPlugin.Config),
   539  					},
   540  				}
   541  			}
   542  
   543  			if command.NodeStepPlugin != nil {
   544  				commandConfigI["node_step_plugin"] = []interface{}{
   545  					map[string]interface{}{
   546  						"type":   command.NodeStepPlugin.Type,
   547  						"config": map[string]string(command.NodeStepPlugin.Config),
   548  					},
   549  				}
   550  			}
   551  
   552  			commandConfigsI = append(commandConfigsI, commandConfigI)
   553  		}
   554  	}
   555  	d.Set("command", commandConfigsI)
   556  
   557  	return nil
   558  }