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

Adding option url parameters #152

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
31 changes: 29 additions & 2 deletions rundeck/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,13 +206,40 @@ type JobOption struct {
// Mutually exclusive with ValueChoices
ValueChoicesURL string `xml:"valuesUrl,attr,omitempty"`

// URL value choices can be configured
JobValueChoicesURL JobValueChoicesURL `xml:"configRemoteUrl,omitempty"`

// Description of the value to be shown in the Rundeck UI.
Description string `xml:"description,omitempty"`

// Option should be hidden from job run page
Hidden bool `xml:"hidden,omitempty"`
}

type JobValueChoicesURL struct {
XMLName xml.Name `xml:"configRemoteUrl"`

JsonFilter string `xml:"jsonFilter,omitempty"`

// Can be BASIC, API_KEY or BEARER_TOKEN
AuthenticationType string `xml:"authenticationType,omitempty"`

// Only used by BASIC auth
PasswordStoragePath string `xml:"passwordStoragePath,omitempty"`

// Only used by BASIC auth
Username string `xml:"username,omitempty"`

// Only used by API_KEY. Can be QUERY_PARAM or HEADER
ApiTokenReporter string `xml:"apiTokenReporter,omitempty"`

// Only used by API_KEY
KeyName string `xml:"keyName,omitempty"`

// Only used by API_KEY and BEARER_TOKEN
TokenStoragePath string `xml:"tokenStoragePath,omitempty"`
}

// JobValueChoices is a specialization of []string representing a sequence of predefined values
// for a job option.
type JobValueChoices []string
Expand Down Expand Up @@ -314,14 +341,14 @@ type JobCommandJobRef struct {
// JobCommandJobRefArguments is a string representing the arguments in a JobCommandJobRef.
type JobCommandJobRefArguments string

// Plugin is a configuration for a plugin to run within a job or notification.
// JobPlugin is a configuration for a plugin to run within a job or notification.
type JobPlugin struct {
XMLName xml.Name
Type string `xml:"type,attr"`
Config JobPluginConfig `xml:"configuration"`
}

// Plugin is a configuration for a filter plugin to run for a step
// JobPlugins is a configuration for a filter plugin to run for a step
type JobPlugins struct {
XMLName xml.Name
LogFilterPlugins []JobLogFilter `xml:"LogFilter"`
Expand Down
56 changes: 51 additions & 5 deletions rundeck/resource_job.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,12 @@ func resourceRundeckJob() *schema.Resource {
Optional: true,
},

"value_choices_url_options": {
Type: schema.TypeMap,
Optional: true,
Elem: resourceRundeckValuesUrlOptions(),
},

"require_predefined_choice": {
Type: schema.TypeBool,
Optional: true,
Expand Down Expand Up @@ -338,6 +344,41 @@ func resourceRundeckJob() *schema.Resource {
}
}

func resourceRundeckValuesUrlOptions() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
"json_filter": {
Type: schema.TypeString,
Required: false,
},
"authentication_type": {
Type: schema.TypeString,
Required: false,
},
"password_storage_path": {
Type: schema.TypeString,
Required: false,
},
"username": {
Type: schema.TypeString,
Required: false,
},
"api_token_reporter": {
Type: schema.TypeString,
Required: false,
},
"key_name": {
Type: schema.TypeString,
Required: false,
},
"token_storage_path": {
Type: schema.TypeString,
Required: false,
},
},
}
}

// Attention - Changes made to this function should be repeated in resourceRundeckJobCommandErrorHandler below!
func resourceRundeckJobCommand() *schema.Resource {
return &schema.Resource{
Expand Down Expand Up @@ -805,11 +846,15 @@ func jobFromResourceData(d *schema.ResourceData) (*JobDetail, error) {
for _, optionI := range optionConfigsI {
optionMap := optionI.(map[string]interface{})
option := JobOption{
Name: optionMap["name"].(string),
Label: optionMap["label"].(string),
DefaultValue: optionMap["default_value"].(string),
ValueChoices: JobValueChoices([]string{}),
ValueChoicesURL: optionMap["value_choices_url"].(string),
Name: optionMap["name"].(string),
Label: optionMap["label"].(string),
DefaultValue: optionMap["default_value"].(string),
ValueChoices: JobValueChoices([]string{}),
ValueChoicesURL: optionMap["value_choices_url"].(string),
// Mark: This should be a map[string]string of sorts to be able to parse the subset of URL options
// and should reference. Right now this compiles, but fails on runtime because the schema
// expects this to be a map, not an object (I think)
JobValueChoicesURL: JobValueChoicesURL{},
RequirePredefinedChoice: optionMap["require_predefined_choice"].(bool),
ValidationRegex: optionMap["validation_regex"].(string),
Description: optionMap["description"].(string),
Expand Down Expand Up @@ -1055,6 +1100,7 @@ func jobToResourceData(job *JobDetail, d *schema.ResourceData) error {
"default_value": option.DefaultValue,
"value_choices": option.ValueChoices,
"value_choices_url": option.ValueChoicesURL,
"value_choices_url_options": option.JobValueChoicesURL,
"require_predefined_choice": option.RequirePredefinedChoice,
"validation_regex": option.ValidationRegex,
"description": option.Description,
Expand Down
106 changes: 106 additions & 0 deletions rundeck/resource_job_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,54 @@ func TestAccJob_cmd_referred_job(t *testing.T) {
})
}

func TestAccJob_optionURLParameters(t *testing.T) {
var job JobDetail

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccJobCheckDestroy(&job),
Steps: []resource.TestStep{
{
Config: testAccJobConfig_optionURLParameters,
Check: resource.ComposeTestCheckFunc(
testAccJobCheckExists("rundeck_job.test", &job),
func(s *terraform.State) error {
if expected := "url-parameters-test"; job.Name != expected {
return fmt.Errorf("wrong name; expected %v, got %v", expected, job.Name)
}
if expected := "BASIC"; job.OptionsConfig.Options[0].JobValueChoicesURL.AuthenticationType != expected {
return fmt.Errorf("failed to set option value; expected %v, got %v", expected, job.OptionsConfig.Options[0].JobValueChoicesURL.AuthenticationType)
}
if expected := "admin"; job.OptionsConfig.Options[0].JobValueChoicesURL.Username != expected {
return fmt.Errorf("failed to set option value; expected %v, got %v", expected, job.OptionsConfig.Options[0].JobValueChoicesURL.Username)
}
if expected := "/keys/test/path"; job.OptionsConfig.Options[0].JobValueChoicesURL.PasswordStoragePath != expected {
return fmt.Errorf("failed to set option value; expected %v, got %v", expected, job.OptionsConfig.Options[0].JobValueChoicesURL.PasswordStoragePath)
}
if expected := "API_KEY"; job.OptionsConfig.Options[1].JobValueChoicesURL.AuthenticationType != expected {
return fmt.Errorf("failed to set option value; expected %v, got %v", expected, job.OptionsConfig.Options[1].JobValueChoicesURL.AuthenticationType)
}
if expected := "My-RunDeck-Auth-Token"; job.OptionsConfig.Options[1].JobValueChoicesURL.KeyName != expected {
return fmt.Errorf("failed to set option value; expected %v, got %v", expected, job.OptionsConfig.Options[1].JobValueChoicesURL.KeyName)
}
if expected := "HEADER"; job.OptionsConfig.Options[1].JobValueChoicesURL.ApiTokenReporter != expected {
return fmt.Errorf("failed to set option value; expected %v, got %v", expected, job.OptionsConfig.Options[1].JobValueChoicesURL.ApiTokenReporter)
}
if expected := "/keys/test/path"; job.OptionsConfig.Options[1].JobValueChoicesURL.TokenStoragePath != expected {
return fmt.Errorf("failed to set option value; expected %v, got %v", expected, job.OptionsConfig.Options[1].JobValueChoicesURL.TokenStoragePath)
}
if expected := "BEARER_TOKEN"; job.OptionsConfig.Options[2].JobValueChoicesURL.AuthenticationType != expected {
return fmt.Errorf("failed to set option value; expected %v, got %v", expected, job.OptionsConfig.Options[2].JobValueChoicesURL.AuthenticationType)
}
return nil
},
),
},
},
})
}

func TestOchestrator_high_low(t *testing.T) {
var job JobDetail

Expand Down Expand Up @@ -554,6 +602,64 @@ resource "rundeck_job" "test" {
}
`

const testAccJobConfig_optionURLParameters = `
resource "rundeck_project" "test" {
name = "terraform-acc-test-option-url-parameters"
description = "parent project for job acceptance tests"
resource_model_source {
type = "file"
config = {
format = "resourcexml"
file = "/tmp/terraform-acc-tests.xml"
}
}
}
resource "rundeck_job" "test" {
name = "url-parameters-test"
project_name = "${rundeck_project.test.name}"
description = "Testing idempotency"
execution_enabled = false
allow_concurrent_executions = false

option {
name = "url_source_basic"
required = "true"
value = "http://localhost:8080"
value_choices_url_options {
authentication_type = "BASIC"
username = "admin"
password_storage_path = "/keys/test/path"
}
}

option {
name = "url_source_api_key"
required = "true"
value = "http://localhost:8080"
value_choices_url_options {
authentication_type = "API_KEY"
api_token_reporter = "HEADER"
key_name = "My-RunDeck-Auth-Token"
token_storage_path = "/keys/test/path"
}
}

option {
name = "url_source_bearer"
required = "true"
value = "http://localhost:8080"
value_choices_url_options {
authentication_type = "BEARER_TOKEN"
token_storage_path = "/keys/test/path"
}
}

command {
shell_command = "echo hello"
}
}
`

const testAccJobNotification_wrong_type = `
resource "rundeck_project" "test" {
name = "terraform-acc-test-job-notification-wrong"
Expand Down
19 changes: 19 additions & 0 deletions website/docs/r/job.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ The following arguments are supported:
* `value_choices_url`: (Optional) Can be used instead of `value_choices` to cause Rundeck to
obtain a list of choices dynamically by fetching this URL.

* `value_choices_url_options` : (Optional) A map of options to pass to the URL when fetching the
list of choices. The map keys are the option names and the values are the option values.

* `require_predefined_choice`: (Optional) Boolean controlling whether the user is allowed to
enter values not included in the predefined set of choices (`false`, the default) or whether
a predefined choice is required (`true`).
Expand Down Expand Up @@ -228,6 +231,22 @@ The following arguments are supported:
* `hidden`: (Optional) Boolean controlling whether this option should be hidden from the UI on the job run page.
Defaults to `false`.

The `value_choices_url_options` block to parameterize using a URL to fill a list of options has the following structure:

* `json_filter`: (Optional) A [JSON path](https://en.wikipedia.org/wiki/JSONPath) filter to apply to the response from the URL. The filter should be an expression that selects a list of keys or key-values from the response.

* `authentication_type`: (Optional) The type of authentication to use when fetching the URL. Can be `BASIC`, `API_KEY` or `BEARER_TOKEN`.

* `username`: (Optional) The username to use for basic authentication. Only used if the authentication type is `BASIC`.

* `password_storage_path`: (Optional) The path to the password in the Rundeck key storage. Only used if the authentication type is `BASIC`.

* `api_token_reporter`: (Optional) Only used if the authentication type is `API_KEY`. The location where the API key can be located. This should be either `QUERY_PARAM` or `HEADER`.

* `key_name`: (Optional) The name of the key in the query parameter or header. Only used if the authentication type is `API_KEY`.

* `token_storage_path`: (Optional) The path to the API key in the Rundeck key storage. This is required if the authentication type is `API_KEY` or `BEARER_TOKEN`.

`command` blocks must have any one of the following combinations of arguments as contents:

* `description`: (Optional) gives a description to the command block.
Expand Down