From 0ab786dcfb692ae1dffdb53bc6b7ccc1fd59f0da Mon Sep 17 00:00:00 2001 From: Ryan Derr Date: Mon, 6 Jan 2025 13:52:30 -0600 Subject: [PATCH] Update Data and Resources For Boundary Controller Configs --- .changelog/1164.txt | 3 + docs/data-sources/boundary_cluster.md | 10 ++ docs/resources/boundary_cluster.md | 10 ++ .../hcp_boundary_cluster/resource.tf | 4 + internal/clients/boundary_cluster.go | 29 +++- .../data_source_boundary_cluster.go | 25 ++- .../resource_boundary_cluster.go | 164 +++++++++++++++++- 7 files changed, 232 insertions(+), 13 deletions(-) create mode 100644 .changelog/1164.txt diff --git a/.changelog/1164.txt b/.changelog/1164.txt new file mode 100644 index 000000000..d556e19c1 --- /dev/null +++ b/.changelog/1164.txt @@ -0,0 +1,3 @@ +```release-note:improvement +Updating the provider for HCPb to allow for controller configuration settings upon hcp_boundary_cluster resource/data-source +``` \ No newline at end of file diff --git a/docs/data-sources/boundary_cluster.md b/docs/data-sources/boundary_cluster.md index 6ce6d93d5..4ae9a6b08 100644 --- a/docs/data-sources/boundary_cluster.md +++ b/docs/data-sources/boundary_cluster.md @@ -34,6 +34,7 @@ If a project is not configured in the HCP Provider config block, the oldest proj ### Read-Only - `cluster_url` (String) A unique URL identifying the Boundary cluster. +- `controller_config` (List of Object) (see [below for nested schema](#nestedatt--controller_config)) - `created_at` (String) The time that the Boundary cluster was created. - `id` (String) The ID of this resource. - `maintenance_window_config` (List of Object) (see [below for nested schema](#nestedatt--maintenance_window_config)) @@ -49,6 +50,15 @@ Optional: - `default` (String) + +### Nested Schema for `controller_config` + +Read-Only: + +- `auth_token_time_to_live` (String) +- `auth_token_time_to_stale` (String) + + ### Nested Schema for `maintenance_window_config` diff --git a/docs/resources/boundary_cluster.md b/docs/resources/boundary_cluster.md index 0bd200c4e..91d0cfaa7 100644 --- a/docs/resources/boundary_cluster.md +++ b/docs/resources/boundary_cluster.md @@ -37,6 +37,7 @@ resource "hcp_boundary_cluster" "example" { ### Optional +- `controller_config` (Block List, Max: 1) The controller configuration for the Boundary cluster. (see [below for nested schema](#nestedblock--controller_config)) - `maintenance_window_config` (Block List, Max: 1) The maintenance window configuration for when cluster upgrades can take place. (see [below for nested schema](#nestedblock--maintenance_window_config)) - `project_id` (String) The ID of the HCP project where the Boundary cluster is located. If not specified, the project specified in the HCP Provider config block will be used, if configured. @@ -51,6 +52,15 @@ If a project is not configured in the HCP Provider config block, the oldest proj - `state` (String) The state of the Boundary cluster. - `version` (String) The version of the Boundary cluster. + +### Nested Schema for `controller_config` + +Optional: + +- `auth_token_time_to_live` (String) The time to live for the auth token in the format of golang's time.Duration string. +- `auth_token_time_to_stale` (String) The time to stale for the auth token in the format of golang's time.Duration string. + + ### Nested Schema for `maintenance_window_config` diff --git a/examples/resources/hcp_boundary_cluster/resource.tf b/examples/resources/hcp_boundary_cluster/resource.tf index cfca4e95e..bafd392ad 100644 --- a/examples/resources/hcp_boundary_cluster/resource.tf +++ b/examples/resources/hcp_boundary_cluster/resource.tf @@ -8,4 +8,8 @@ resource "hcp_boundary_cluster" "example" { end = 12 upgrade_type = "SCHEDULED" } + controller_config { + auth_token_time_to_live = "36h0m0s" + auth_token_time_to_stale = "12h0m0s" + } } diff --git a/internal/clients/boundary_cluster.go b/internal/clients/boundary_cluster.go index 53d32bfb9..8bc6fb150 100644 --- a/internal/clients/boundary_cluster.go +++ b/internal/clients/boundary_cluster.go @@ -121,7 +121,7 @@ func GetBoundaryClusterControllerConfigByID( client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, boundaryClusterID string, -) (*boundarymodels.HashicorpCloudBoundary20211221GetControllerConfigurationResponse, error) { +) (*boundarymodels.HashicorpCloudBoundary20211221ControllerConfiguration, error) { params := boundary_service.NewBoundaryServiceGetControllerConfigurationParams() params.Context = ctx @@ -134,7 +134,7 @@ func GetBoundaryClusterControllerConfigByID( return nil, err } - return resp.Payload, nil + return resp.Payload.Config, nil } // UpdateBoundaryClusterControllerConfig updates the controllers auth config for TTL and TTS on a Boundary cluster. @@ -160,4 +160,27 @@ func UpdateBoundaryClusterControllerConfig( } return nil -} \ No newline at end of file +} + +// ResetBoundaryClusterControllerConfig resets the controllers auth config for TTL and TTS on a Boundary cluster. +func ResetBoundaryClusterControllerConfig( + ctx context.Context, + client *Client, + loc *sharedmodels.HashicorpCloudLocationLocation, + boundaryClusterID string, +) error { + + params := boundary_service.NewBoundaryServiceResetControllerConfigurationParams() + params.Context = ctx + + params.LocationOrganizationID = loc.OrganizationID + params.LocationProjectID = loc.ProjectID + params.ClusterID = boundaryClusterID + + _, err := client.Boundary.BoundaryServiceResetControllerConfiguration(params, nil) + if err != nil { + return err + } + + return nil +} diff --git a/internal/providersdkv2/data_source_boundary_cluster.go b/internal/providersdkv2/data_source_boundary_cluster.go index 1c10b879b..54344428c 100644 --- a/internal/providersdkv2/data_source_boundary_cluster.go +++ b/internal/providersdkv2/data_source_boundary_cluster.go @@ -94,6 +94,24 @@ If a project is not configured in the HCP Provider config block, the oldest proj Type: schema.TypeString, Computed: true, }, + "controller_config": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "auth_token_time_to_live": { + Description: "The time to live for the auth token in time.Duration format.", + Type: schema.TypeString, + Computed: true, + }, + "auth_token_time_to_stale": { + Description: "The time to stale for the auth token in time.Duration format.", + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, }, } } @@ -135,8 +153,13 @@ func dataSourceBoundaryClusterRead(ctx context.Context, d *schema.ResourceData, return diag.Errorf("unable to fetch maintenenace window Boundary cluster (%s): %v", clusterID, err) } + controllerConfig, err := clients.GetBoundaryClusterControllerConfigByID(ctx, client, loc, clusterID) + if err != nil { + return diag.Errorf("unable to fetch controller config for Boundary cluster (%s): %v", clusterID, err) + } + // cluster found, update resource data - if err := setBoundaryClusterResourceData(d, cluster, clusterUpgradeType, clusterMW); err != nil { + if err := setBoundaryClusterResourceData(d, cluster, clusterUpgradeType, clusterMW, controllerConfig); err != nil { return diag.FromErr(err) } diff --git a/internal/providersdkv2/resource_boundary_cluster.go b/internal/providersdkv2/resource_boundary_cluster.go index a319fb0ee..35941e442 100644 --- a/internal/providersdkv2/resource_boundary_cluster.go +++ b/internal/providersdkv2/resource_boundary_cluster.go @@ -166,6 +166,53 @@ If a project is not configured in the HCP Provider config block, the oldest proj Type: schema.TypeString, Computed: true, }, + "controller_config": { + Description: "The controller configuration for the Boundary cluster.", + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "auth_token_time_to_live": { + Description: "The time to live for the auth token in the format of golang's time.Duration string.", + Type: schema.TypeString, + Optional: true, + ValidateFunc: func(i any, k string) (warnings []string, errors []error) { + _, err := time.ParseDuration(i.(string)) + if err != nil { + errors = append(errors, fmt.Errorf("expected %s to be a valid duration, got %s", k, i)) + } + return warnings, errors + }, + RequiredWith: []string{"controller_config.0.auth_token_time_to_stale"}, + DiffSuppressFunc: func(_, old, new string, _ *schema.ResourceData) bool { + oldTime, _ := time.ParseDuration(old) + newTime, _ := time.ParseDuration(new) + return newTime == oldTime + }, + }, + "auth_token_time_to_stale": { + Description: "The time to stale for the auth token in the format of golang's time.Duration string.", + Type: schema.TypeString, + Optional: true, + ValidateFunc: func(i any, k string) (warnings []string, errors []error) { + _, err := time.ParseDuration(i.(string)) + if err != nil { + errors = append(errors, fmt.Errorf("expected %s to be a valid duration, got %s", k, i)) + } + return warnings, errors + }, + RequiredWith: []string{"controller_config.0.auth_token_time_to_live"}, + DiffSuppressFunc: func(_, old, new string, _ *schema.ResourceData) bool { + oldTime, _ := time.ParseDuration(old) + newTime, _ := time.ParseDuration(new) + return newTime == oldTime + }, + }, + }, + }, + }, }, } } @@ -205,6 +252,12 @@ func resourceBoundaryClusterCreate(ctx context.Context, d *schema.ResourceData, return diagErr } + controllerConfig, diagErr := getBoundaryClusterControllerConfig(d) + + if diagErr != nil { + return diagErr + } + // check for an existing boundary cluster _, err = clients.GetBoundaryClusterByID(ctx, client, loc, clusterID) if err != nil { @@ -219,11 +272,12 @@ func resourceBoundaryClusterCreate(ctx context.Context, d *schema.ResourceData, // assemble the BoundaryClusterCreateRequest req := &boundarymodels.HashicorpCloudBoundary20211221CreateRequest{ - ClusterID: clusterID, - Username: username, - Password: password, - Location: loc, - MarketingSku: &tierPb, + ClusterID: clusterID, + Username: username, + Password: password, + Location: loc, + MarketingSku: &tierPb, + ControllerConfig: controllerConfig, } // execute the Boundary cluster creation @@ -271,8 +325,14 @@ func resourceBoundaryClusterCreate(ctx context.Context, d *schema.ResourceData, currentUpgradeType = upgradeType } + // Retrieve the controller configuration in the case it was not provided + currentControllerConfig, err := clients.GetBoundaryClusterControllerConfigByID(ctx, client, loc, clusterID) + if err != nil { + return diag.Errorf("unable to fetch controller configuration for Boundary cluster (%s): %v", clusterID, err) + } + // set Boundary cluster resource data - err = setBoundaryClusterResourceData(d, cluster, currentUpgradeType, currentMaintenanceWindow) + err = setBoundaryClusterResourceData(d, cluster, currentUpgradeType, currentMaintenanceWindow, currentControllerConfig) if err != nil { return diag.FromErr(err) } @@ -296,6 +356,11 @@ func resourceBoundaryClusterUpdate(ctx context.Context, d *schema.ResourceData, return diagErr } + controllerConfig, dialErr := getBoundaryClusterControllerConfig(d) + if dialErr != nil { + return dialErr + } + log.Printf("[INFO] Reading Boundary cluster (%s) [project_id=%s, organization_id=%s]", clusterID, loc.ProjectID, loc.OrganizationID) cluster, err := clients.GetBoundaryClusterByID(ctx, client, loc, clusterID) @@ -331,8 +396,28 @@ func resourceBoundaryClusterUpdate(ctx context.Context, d *schema.ResourceData, currentUpgradeType = upgradeType } + log.Printf("[INFO] Updated controller configuration for Boundary cluster (%s) [project_id=%s, organization_id=%s]", clusterID, loc.ProjectID, loc.OrganizationID) + + currentControllerConfig, err := clients.GetBoundaryClusterControllerConfigByID(ctx, client, loc, clusterID) + if err != nil { + return diag.Errorf("unable to fetch controller configuration for Boundary cluster (%s): %v", clusterID, err) + } + + // update the controller configuration if it was created + if controllerConfig != nil { + ctrlConfigReq := boundarymodels.HashicorpCloudBoundary20211221UpdateControllerConfigurationRequest{} + ctrlConfigReq.ClusterID = cluster.ClusterID + ctrlConfigReq.Location = cluster.Location + ctrlConfigReq.Config = controllerConfig + + if err = clients.UpdateBoundaryClusterControllerConfig(ctx, client, loc, clusterID, &ctrlConfigReq); err != nil { + return diag.Errorf("error updating controller configuration for Boundary cluster (%s): %v", clusterID, err) + } + currentControllerConfig = controllerConfig + } + // set Boundary cluster resource data - err = setBoundaryClusterResourceData(d, cluster, currentUpgradeType, currentMaintenanceWindow) + err = setBoundaryClusterResourceData(d, cluster, currentUpgradeType, currentMaintenanceWindow, currentControllerConfig) if err != nil { return diag.FromErr(err) } @@ -376,8 +461,13 @@ func resourceBoundaryClusterRead(ctx context.Context, d *schema.ResourceData, me return diag.Errorf("unable to fetch maintenenace window Boundary cluster (%s): %v", clusterID, err) } + controllerConfig, err := clients.GetBoundaryClusterControllerConfigByID(ctx, client, loc, clusterID) + if err != nil { + return diag.Errorf("unable to fetch controller configuration for Boundary cluster (%s): %v", clusterID, err) + } + // Cluster found, update resource data. - if err := setBoundaryClusterResourceData(d, cluster, clusterUpgradeType, clusterMW); err != nil { + if err := setBoundaryClusterResourceData(d, cluster, clusterUpgradeType, clusterMW, controllerConfig); err != nil { return diag.FromErr(err) } @@ -455,7 +545,7 @@ func resourceBoundaryClusterImport(ctx context.Context, d *schema.ResourceData, return []*schema.ResourceData{d}, nil } -func setBoundaryClusterResourceData(d *schema.ResourceData, cluster *boundarymodels.HashicorpCloudBoundary20211221Cluster, upgradeType *boundarymodels.HashicorpCloudBoundary20211221UpgradeType, clusterMW *boundarymodels.HashicorpCloudBoundary20211221MaintenanceWindow) error { +func setBoundaryClusterResourceData(d *schema.ResourceData, cluster *boundarymodels.HashicorpCloudBoundary20211221Cluster, upgradeType *boundarymodels.HashicorpCloudBoundary20211221UpgradeType, clusterMW *boundarymodels.HashicorpCloudBoundary20211221MaintenanceWindow, clusterCtrlConfig *boundarymodels.HashicorpCloudBoundary20211221ControllerConfiguration) error { if err := d.Set("cluster_id", cluster.ClusterID); err != nil { return err } @@ -496,6 +586,24 @@ func setBoundaryClusterResourceData(d *schema.ResourceData, cluster *boundarymod return err } + ctrlConfig := map[string]any{} + if clusterCtrlConfig != nil { + authTTL, err := time.ParseDuration(clusterCtrlConfig.AuthTokenTimeToLive) + if err != nil { + return fmt.Errorf("unable to parse auth_token_time_to_live to time: %v", err) + } + authTTS, err := time.ParseDuration(clusterCtrlConfig.AuthTokenTimeToStale) + if err != nil { + return fmt.Errorf("unable to parse auth_token_time_to_stale to time: %v", err) + } + ctrlConfig["auth_token_time_to_live"] = authTTL.String() + ctrlConfig["auth_token_time_to_stale"] = authTTS.String() + } + + if err := d.Set("controller_config", []any{ctrlConfig}); err != nil { + return err + } + if err := d.Set("version", cluster.BoundaryVersion); err != nil { return err } @@ -561,3 +669,41 @@ func getBoundaryClusterMaintainanceWindowConfig(d *schema.ResourceData) (*bounda return &upgradeType, maintenanceWindow, nil } + +func getBoundaryClusterControllerConfig(d *schema.ResourceData) (*boundarymodels.HashicorpCloudBoundary20211221ControllerConfiguration, diag.Diagnostics) { + if !d.HasChange("controller_config") { + return nil, nil + } + + // get the controller_config resources + controllerConfigParam, ok := d.GetOk("controller_config") + if !ok { + return nil, nil + } + + // convert to []any is required even though we set a MaxItems=1 + controllerConfigs, ok := controllerConfigParam.([]any) + if !ok || len(controllerConfigs) == 0 { + return nil, nil + } + + // get the elements in the config + controllerConfigElems, ok := controllerConfigs[0].(map[string]any) + if !ok || len(controllerConfigElems) == 0 { + return nil, nil + } + + controllerConfig := &boundarymodels.HashicorpCloudBoundary20211221ControllerConfiguration{} + + authTTL, _ := time.ParseDuration(controllerConfigElems["auth_token_time_to_live"].(string)) + authTTS, _ := time.ParseDuration(controllerConfigElems["auth_token_time_to_stale"].(string)) + + controllerConfig.AuthTokenTimeToLive = authTTL.String() + controllerConfig.AuthTokenTimeToStale = authTTS.String() + + if authTTL < authTTS { + return nil, diag.Errorf("controller configuration is invalid: `auth_token_time_to_live` should be greater than or equal to `auth_token_time_to_stale`") + } + + return controllerConfig, nil +}