Skip to content

Commit

Permalink
Waypoint application input vars (#833)
Browse files Browse the repository at this point in the history
* [WAYP-2173] waypoint: Add input vars to application resource.

* [WAYP-2173] waypoint: Support input variables on template resource and data source.

* waypoint: Fix application schema.

* waypoint: Fix template schema and regen docs.

* waypoint: Add changelog.

* waypoint: Fix applications and templates issues.

* Add variable type to application input vars

* Set variable options on a template to be computed

* Enforce uniqueness in a template's variable options, if provided

* Add acceptance test for templates where a template is user editable, instead of using variable options

* waypoint: Omit special variable from app input vars.

This commit updates Waypoint application creation to omit the `waypoint_application` input variable from the variables returned to the provider from HCP Waypoint. This variable is implicitly set by the service, and is never expected to be in the config. Since it's not in the config, but is returned by the API, if it is added to the state, Terraform errors since the config and state don't match, even though that is expected.

Another change in this PR is that the type of the input variable for an application is set in the state based on what is in the config, NOT the API response. This is because the API presently does not return the type of the variable. If and when that changes, this can be undone (as noted in the comment with this change).

* waypoint: Add test case for applications w/variables.

* waypoint: Split template acc test cases.

Basic test case is updated to only test basic aspects of a template, not variables with options. That has been split into a new test case.

* waypoint: Split template data source acc test cases.

* waypoint: Change app input vars to list.

* Implement value converter interface for input vars, to use "As" for conversion from a TF value to a struct

* Refactor code to read input vars into the plan/state into readInputs function

* Set list of input vars to a null list if there are no inputs

* Update tests to use corrected variable options

* Add variable type to data source schema, though it won't be set yet

* waypoint: Add test for app data source with input vars.

* waypoint: Docs generation.

* waypoint: Add app test.

This test simulates a template which sets two variables and an application created using that template.

* waypoint: Remove unnecessary list of input vars.

The list of input vars removed by this commit is only needed during creation of an app, not a read.

* waypoint: Split app and template input vars for an app.

This commit splits up the input vars for an application that are set by a template and an app into two separate parts of the schema for the resource. The function readInputs is updated to make the distinction.

* waypoint: Change app input vars to a computed set.

* waypoint: Update app input and tpl vars to set.

Update acc test to test that template vars are set for an application that uses a template which configured a value for an input var.

* waypoint: Update app tpl input var type to optional.

The variable type for the template-configured input variables of an application is optional. This will remain true unless the API changes to always return a variable's type.

* waypoint: Fix waypoint_application data source input vars.

The waypoint_application data source was updated to no longer use the `readInputs` method created to help with reading input vars for the waypoint_application resource. Instead it's handled in a loop in the data source's read function, because it doesn't split vars between app and tpl input vars. Tests were also updated accordingly here.

* waypoint: Generate docs.

* fmt.

* Rename Waypoint app data source input vars field.

Co-authored-by: Clint <[email protected]>

* Set input vars to null set for data source.

The set of input vars for the Waypoint application data source should be null if there are not any variables.

Co-authored-by: Clint <[email protected]>

* Regen Waypoint docs.

* Update WP app to always check vars.

Doing this is necessary for drift detection.

Co-authored-by: Clint <[email protected]>

* wp: Apply changes from code review.

Co-authored-by: Clint <[email protected]>

* wp: Regen docs.

* wp: Use ElementsAs for value conversion.

This is simpler than implementing the value converter interface.

* wp: Fix docs typo.

* wp: Fix data source vars test.

Typo in new field name "input_variables" was fixed. The # of variables also has changed since `waypoint_application` is counted among the variables.

* wp: Fix app vars test.

The # of variables has changed since `waypoint_application` is counted among the variables.

---------

Co-authored-by: Clint <[email protected]>
  • Loading branch information
paladin-devops and catsby authored Jun 4, 2024
1 parent ee8b04a commit 68a655f
Show file tree
Hide file tree
Showing 12 changed files with 630 additions and 24 deletions.
4 changes: 4 additions & 0 deletions .changelog/833.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
```release-note:improvement
Add support for input variables to `hcp_waypoint_application` resource and
data source.
```
10 changes: 10 additions & 0 deletions docs/data-sources/waypoint_application.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ The Waypoint Application data source retrieves information on a given Applicatio
### Optional

- `id` (String) The ID of the Application.
- `input_variables` (Attributes Set) Input variables for the Application. (see [below for nested schema](#nestedatt--input_variables))
- `name` (String) The name of the Application.
- `project_id` (String) The ID of the HCP project where the Waypoint Application is located.

Expand All @@ -27,3 +28,12 @@ The Waypoint Application data source retrieves information on a given Applicatio
- `namespace_id` (String) Internal Namespace ID.
- `organization_id` (String) The ID of the HCP organization where the Waypoint Application is located.
- `readme_markdown` (String) Instructions for using the Application (markdown format supported).

<a id="nestedatt--input_variables"></a>
### Nested Schema for `input_variables`

Read-Only:

- `name` (String) Variable name
- `value` (String) Variable value
- `variable_type` (String) Variable type
24 changes: 24 additions & 0 deletions docs/resources/waypoint_application.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ The Waypoint Application resource managed the lifecycle of an Application that's

### Optional

- `application_input_variables` (Attributes Set) Input variables set for the application. (see [below for nested schema](#nestedatt--application_input_variables))
- `project_id` (String) The ID of the HCP project where the Waypoint Application is located.
- `readme_markdown` (String) Instructions for using the Application (markdown format supported). Note: this is a base64 encoded string, and can only be set in configuration after initial creation. The initial version of the README is generated from the README Template from source Application Template.

Expand All @@ -30,3 +31,26 @@ The Waypoint Application resource managed the lifecycle of an Application that's
- `id` (String) The ID of the Application.
- `namespace_id` (String) Internal Namespace ID.
- `organization_id` (String) The ID of the HCP organization where the Waypoint Application is located.
- `template_input_variables` (Attributes Set) Input variables set for the application. (see [below for nested schema](#nestedatt--template_input_variables))

<a id="nestedatt--application_input_variables"></a>
### Nested Schema for `application_input_variables`

Required:

- `name` (String) Variable name
- `value` (String) Variable value
- `variable_type` (String) Variable type


<a id="nestedatt--template_input_variables"></a>
### Nested Schema for `template_input_variables`

Required:

- `name` (String) Variable name
- `value` (String) Variable value

Optional:

- `variable_type` (String) Variable type
2 changes: 1 addition & 1 deletion docs/resources/waypoint_application_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ Required:
Required:

- `name` (String) Variable name
- `options` (List of String) List of options
- `variable_type` (String) Variable type

Optional:

- `options` (List of String) List of options
- `user_editable` (Boolean) Whether the variable is editable by the user creating an application
18 changes: 18 additions & 0 deletions internal/clients/waypoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,21 @@ func GetAddOnByID(ctx context.Context, client *Client, loc *sharedmodels.Hashico
}
return getResp.GetPayload().AddOn, nil
}

func GetInputVariables(ctx context.Context, client *Client, workspaceName string, loc *sharedmodels.HashicorpCloudLocationLocation) ([]*waypoint_models.HashicorpCloudWaypointInputVariable, error) {
ns, err := getNamespaceByLocation(ctx, client, loc)
if err != nil {
return nil, err
}

params := &waypoint_service.WaypointServiceGetTFRunStatusParams{
WorkspaceName: workspaceName,
NamespaceID: ns.ID,
}

getResp, err := client.Waypoint.WaypointServiceGetTFRunStatus(params, nil)
if err != nil {
return nil, err
}
return getResp.GetPayload().InputVariables, nil
}
67 changes: 66 additions & 1 deletion internal/provider/waypoint/data_source_waypoint_application.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,26 @@ func (d *DataSourceApplication) Schema(ctx context.Context, req datasource.Schem
Computed: true,
Description: "Internal Namespace ID.",
},
"input_variables": schema.SetNestedAttribute{
Optional: true,
Description: "Input variables for the Application.",
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"name": &schema.StringAttribute{
Computed: true,
Description: "Variable name",
},
"value": &schema.StringAttribute{
Computed: true,
Description: "Variable value",
},
"variable_type": &schema.StringAttribute{
Computed: true,
Description: "Variable type",
},
},
},
},
},
}
}
Expand All @@ -100,8 +120,28 @@ func (d *DataSourceApplication) Configure(ctx context.Context, req datasource.Co
d.client = client
}

// ApplicationDataSourceModel describes the data source data model
type ApplicationDataSourceModel struct {
ID types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
ProjectID types.String `tfsdk:"project_id"`
OrgID types.String `tfsdk:"organization_id"`
ReadmeMarkdown types.String `tfsdk:"readme_markdown"`
ApplicationTemplateID types.String `tfsdk:"application_template_id"`
ApplicationTemplateName types.String `tfsdk:"application_template_name"`
NamespaceID types.String `tfsdk:"namespace_id"`

// deferred for now
// Tags types.List `tfsdk:"tags"`

// deferred and probably a list or objects, but may possible be a separate
// ActionCfgs types.List `tfsdk:"action_cfgs"`

InputVars types.Set `tfsdk:"input_variables"`
}

func (d *DataSourceApplication) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data ApplicationResourceModel
var data ApplicationDataSourceModel
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)

client := d.client
Expand Down Expand Up @@ -146,5 +186,30 @@ func (d *DataSourceApplication) Read(ctx context.Context, req datasource.ReadReq
data.ReadmeMarkdown = types.StringNull()
}

// A second API call is made to get the input vars set on the application
inputVars, err := clients.GetInputVariables(ctx, client, data.Name.ValueString(), loc)
if err != nil {
resp.Diagnostics.AddError(err.Error(), "Failed to fetch application's input variables.")
return
}

inputVariables := make([]*InputVar, 0)
for _, iv := range inputVars {
inputVariables = append(inputVariables, &InputVar{
Name: types.StringValue(iv.Name),
Value: types.StringValue(iv.Value),
})
}
if len(inputVariables) > 0 {
aivs, diags := types.SetValueFrom(ctx, types.ObjectType{AttrTypes: InputVar{}.attrTypes()}, inputVariables)
resp.Diagnostics.Append(diags...)
if diags.HasError() {
return
}
data.InputVars = aivs
} else {
data.InputVars = types.SetNull(types.ObjectType{AttrTypes: InputVar{}.attrTypes()})
}

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,6 @@ func TestAccWaypointData_Application_Template_basic(t *testing.T) {
Config: testDataAppTemplateConfig(name),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(dataSourceName, "name", name),
resource.TestCheckResourceAttr(dataSourceName, "variable_options.0.name", "string_variable"),
resource.TestCheckResourceAttr(dataSourceName, "variable_options.0.variable_type", "string"),
resource.TestCheckResourceAttr(dataSourceName, "variable_options.0.options.#", "1"),
resource.TestCheckResourceAttr(dataSourceName, "variable_options.0.options.0", "a"),
),
},
{
Expand All @@ -52,19 +48,61 @@ func TestAccWaypointData_Application_Template_basic(t *testing.T) {
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "name", updatedName),
resource.TestCheckResourceAttr(dataSourceName, "name", updatedName),
resource.TestCheckResourceAttr(dataSourceName, "variable_options.0.name", "string_variable"),
resource.TestCheckResourceAttr(dataSourceName, "variable_options.0.variable_type", "string"),
resource.TestCheckResourceAttr(dataSourceName, "variable_options.0.options.#", "1"),
resource.TestCheckResourceAttr(dataSourceName, "variable_options.0.options.0", "a"),
),
},
},
})
}

func TestAccWaypointData_Application_template_with_variable_options(t *testing.T) {
// this is only used to verify the app template gets cleaned up in the end
// of the test, and not used for any other purpose at this time
var appTemplateModel waypoint.ApplicationTemplateResourceModel
resourceName := "hcp_waypoint_application_template.var_opts_test"
dataSourceName := "data." + resourceName
name := generateRandomName()

resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories,
CheckDestroy: testAccCheckWaypointAppTemplateDestroy(t, &appTemplateModel),
Steps: []resource.TestStep{
{
// establish the base app template
// note this reuses the config method from the app template
// resource test
Config: testAppTemplateConfigWithVarOpts(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckWaypointAppTemplateExists(t, resourceName, &appTemplateModel),
),
},
{
// add a data source config to read the app template
Config: testDataAppTemplateWithVariablesWithOptionsConfig(name),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(dataSourceName, "name", name),
resource.TestCheckResourceAttr(dataSourceName, "variable_options.#", "2"),
resource.TestCheckResourceAttr(dataSourceName, "variable_options.0.name", "faction"),
resource.TestCheckResourceAttr(dataSourceName, "variable_options.0.type", "string"),
resource.TestCheckResourceAttr(dataSourceName, "variable_options.1.name", "vault_dweller_name"),
resource.TestCheckResourceAttr(dataSourceName, "variable_options.1.type", "string"),
),
},
},
})

}

func testDataAppTemplateConfig(name string) string {
return fmt.Sprintf(`%s
data "hcp_waypoint_application_template" "test" {
name = hcp_waypoint_application_template.test.name
}`, testAppTemplateConfig(name))
}

func testDataAppTemplateWithVariablesWithOptionsConfig(name string) string {
return fmt.Sprintf(`%s
data "hcp_waypoint_application_template" "var_opts_test" {
name = hcp_waypoint_application_template.var_opts_test.name
}`, testAppTemplateConfigWithVarOpts(name))
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,52 @@ func TestAccWaypoint_Application_DataSource_basic(t *testing.T) {
})
}

func TestAccWaypoint_Application_DataSource_WithInputVars(t *testing.T) {
// this is only used to verify the app template gets cleaned up in the end
// of the test, and not used for any other purpose at this time
var applicationModel waypoint.ApplicationResourceModel
resourceName := "hcp_waypoint_application.test_var_opts"
dataSourceName := "data." + resourceName
templateName := generateRandomName()
applicationName := generateRandomName()

resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories,
CheckDestroy: testAccCheckWaypointApplicationDestroy(t, &applicationModel),
Steps: []resource.TestStep{
{
// establish the base app template and application
Config: testApplicationWithInputVarsConfig(templateName, applicationName),
Check: resource.ComposeTestCheckFunc(
testAccCheckWaypointApplicationExists(t, resourceName, &applicationModel),
),
},
{
// add a data source config to read the app template
Config: testDataApplicationWithInputVarsConfig(templateName, applicationName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(dataSourceName, "name", applicationName),
resource.TestCheckResourceAttr(dataSourceName, "input_variables.#", "3"),
),
},
},
})

}

func testDataApplicationConfig(templateName, applicationName string) string {
return fmt.Sprintf(`%s
data "hcp_waypoint_application" "test" {
name = hcp_waypoint_application.test.name
}`, testApplicationConfig(templateName, applicationName))
}

func testDataApplicationWithInputVarsConfig(templateName, applicationName string) string {
return fmt.Sprintf(`%s
data "hcp_waypoint_application" "test_var_opts" {
name = hcp_waypoint_application.test_var_opts.name
}`, testApplicationWithInputVarsConfig(templateName, applicationName))
}
Loading

0 comments on commit 68a655f

Please sign in to comment.