From 0b797aef2cbfb5a822dd56ae03fb522468eb6162 Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Thu, 23 May 2024 11:40:05 -0400 Subject: [PATCH 01/20] switch some methods over --- internal/clients/client.go | 78 ++++++++++++----------- internal/clients/vault_secrets.go | 46 ------------- internal/clients/vault_secrets_preview.go | 70 ++++++++++++++++++++ 3 files changed, 111 insertions(+), 83 deletions(-) create mode 100644 internal/clients/vault_secrets_preview.go diff --git a/internal/clients/client.go b/internal/clients/client.go index 3b807d75a..caab8478c 100644 --- a/internal/clients/client.go +++ b/internal/clients/client.go @@ -46,6 +46,8 @@ import ( cloud_vault "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-service/stable/2020-11-25/client" "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-service/stable/2020-11-25/client/vault_service" + cloud_vault_secrets_preview "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/preview/2023-11-28/client" + secret_service_preview "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/preview/2023-11-28/client/secret_service" cloud_vault_secrets "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/stable/2023-06-13/client" "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/stable/2023-06-13/client/secret_service" @@ -66,24 +68,25 @@ import ( type Client struct { Config ClientConfig - Billing billing_account_service.ClientService - Boundary boundary_service.ClientService - Consul consul_service.ClientService - IAM iam_service.ClientService - Network network_service.ClientService - Operation operation_service.ClientService - Organization organization_service.ClientService - Packer packer_service.ClientService - PackerV2 packer_service_v2.ClientService - Project project_service.ClientService - ServicePrincipals service_principals_service.ClientService - Groups groups_service.ClientService - Vault vault_service.ClientService - VaultSecrets secret_service.ClientService - Waypoint waypoint_service.ClientService - Webhook webhook_service.ClientService - LogService log_service.ClientService - ResourceService resource_service.ClientService + Billing billing_account_service.ClientService + Boundary boundary_service.ClientService + Consul consul_service.ClientService + IAM iam_service.ClientService + Network network_service.ClientService + Operation operation_service.ClientService + Organization organization_service.ClientService + Packer packer_service.ClientService + PackerV2 packer_service_v2.ClientService + Project project_service.ClientService + ServicePrincipals service_principals_service.ClientService + Groups groups_service.ClientService + Vault vault_service.ClientService + VaultSecrets secret_service.ClientService + VaultSecretsPreview secret_service_preview.ClientService + Waypoint waypoint_service.ClientService + Webhook webhook_service.ClientService + LogService log_service.ClientService + ResourceService resource_service.ClientService } // ClientConfig specifies configuration for the client that interacts with HCP @@ -158,25 +161,26 @@ func NewClient(config ClientConfig) (*Client, error) { } client := &Client{ - Config: config, - Billing: cloud_billing.New(httpClient, nil).BillingAccountService, - Boundary: cloud_boundary.New(httpClient, nil).BoundaryService, - Consul: cloud_consul.New(httpClient, nil).ConsulService, - IAM: cloud_iam.New(httpClient, nil).IamService, - Network: cloud_network.New(httpClient, nil).NetworkService, - Operation: cloud_operation.New(httpClient, nil).OperationService, - Organization: cloud_resource_manager.New(httpClient, nil).OrganizationService, - Packer: cloud_packer.New(httpClient, nil).PackerService, - PackerV2: cloud_packer_v2.New(httpClient, nil).PackerService, - Project: cloud_resource_manager.New(httpClient, nil).ProjectService, - ServicePrincipals: cloud_iam.New(httpClient, nil).ServicePrincipalsService, - Groups: cloud_iam.New(httpClient, nil).GroupsService, - Vault: cloud_vault.New(httpClient, nil).VaultService, - VaultSecrets: cloud_vault_secrets.New(httpClient, nil).SecretService, - Waypoint: cloud_waypoint.New(httpClient, nil).WaypointService, - LogService: cloud_log_service.New(httpClient, nil).LogService, - Webhook: cloud_webhook.New(httpClient, nil).WebhookService, - ResourceService: cloud_resource_manager.New(httpClient, nil).ResourceService, + Config: config, + Billing: cloud_billing.New(httpClient, nil).BillingAccountService, + Boundary: cloud_boundary.New(httpClient, nil).BoundaryService, + Consul: cloud_consul.New(httpClient, nil).ConsulService, + IAM: cloud_iam.New(httpClient, nil).IamService, + Network: cloud_network.New(httpClient, nil).NetworkService, + Operation: cloud_operation.New(httpClient, nil).OperationService, + Organization: cloud_resource_manager.New(httpClient, nil).OrganizationService, + Packer: cloud_packer.New(httpClient, nil).PackerService, + PackerV2: cloud_packer_v2.New(httpClient, nil).PackerService, + Project: cloud_resource_manager.New(httpClient, nil).ProjectService, + ServicePrincipals: cloud_iam.New(httpClient, nil).ServicePrincipalsService, + Groups: cloud_iam.New(httpClient, nil).GroupsService, + Vault: cloud_vault.New(httpClient, nil).VaultService, + VaultSecrets: cloud_vault_secrets.New(httpClient, nil).SecretService, + VaultSecretsPreview: cloud_vault_secrets_preview.New(httpClient, nil).SecretService, + Waypoint: cloud_waypoint.New(httpClient, nil).WaypointService, + LogService: cloud_log_service.New(httpClient, nil).LogService, + Webhook: cloud_webhook.New(httpClient, nil).WebhookService, + ResourceService: cloud_resource_manager.New(httpClient, nil).ResourceService, } return client, nil diff --git a/internal/clients/vault_secrets.go b/internal/clients/vault_secrets.go index b9fbd2d8d..d46b2fedf 100644 --- a/internal/clients/vault_secrets.go +++ b/internal/clients/vault_secrets.go @@ -69,23 +69,6 @@ func UpdateVaultSecretsApp(ctx context.Context, client *Client, loc *sharedmodel return updateResp.Payload.App, nil } -// ListVaultSecretsAppSecrets will retrieve all app secrets metadata for a Vault Secrets application. -func ListVaultSecretsAppSecrets(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, appName string) ([]*secretmodels.Secrets20230613Secret, error) { - - listParams := secret_service.NewListAppSecretsParams() - listParams.Context = ctx - listParams.AppName = appName - listParams.LocationOrganizationID = loc.OrganizationID - listParams.LocationProjectID = loc.ProjectID - - listResp, err := client.VaultSecrets.ListAppSecrets(listParams, nil) - if err != nil { - return nil, err - } - - return listResp.Payload.Secrets, nil -} - // DeleteVaultSecretsApp will delete a Vault Secrets application. func DeleteVaultSecretsApp(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, appName string) error { @@ -122,35 +105,6 @@ func CreateVaultSecretsAppSecret(ctx context.Context, client *Client, loc *share return createResp.Payload.Secret, nil } -// OpenVaultSecretsAppSecret will retrieve the latest secret for a Vault Secrets app, including it's value. -func OpenVaultSecretsAppSecret(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, appName, secretName string) (*secretmodels.Secrets20230613OpenSecret, error) { - getParams := secret_service.NewOpenAppSecretParams() - getParams.Context = ctx - getParams.AppName = appName - getParams.SecretName = secretName - getParams.LocationOrganizationID = loc.OrganizationID - getParams.LocationProjectID = loc.ProjectID - - var getResp *secret_service.OpenAppSecretOK - var err error - for attempt := 0; attempt < retryCount; attempt++ { - getResp, err = client.VaultSecrets.OpenAppSecret(getParams, nil) - if err != nil { - serviceErr, ok := err.(*secret_service.OpenAppSecretDefault) - if !ok { - return nil, err - } - - if shouldRetryWithSleep(ctx, serviceErr, attempt, []int{http.StatusTooManyRequests}) { - continue - } - return nil, err - } - break - } - return getResp.Payload.Secret, nil -} - func OpenVaultSecretsAppSecrets(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, appName string) ([]*secretmodels.Secrets20230613OpenSecret, error) { params := secret_service.NewOpenAppSecretsParams() params.Context = ctx diff --git a/internal/clients/vault_secrets_preview.go b/internal/clients/vault_secrets_preview.go new file mode 100644 index 000000000..e6eb7f19d --- /dev/null +++ b/internal/clients/vault_secrets_preview.go @@ -0,0 +1,70 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package clients + +import ( + "context" + "errors" + "fmt" + "net/http" + "time" + + sharedmodels "github.com/hashicorp/hcp-sdk-go/clients/cloud-shared/v1/models" + "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/preview/2023-11-28/client/secret_service" + secretmodels "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/preview/2023-11-28/models" + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +// ListVaultSecretsAppSecrets will retrieve all app secrets metadata for a Vault Secrets application. +func ListVaultSecretsAppSecrets(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, appName string) ([]*secretmodels.Secrets20231128Secret, error) { + listParams := secret_service.NewListAppSecretsParams() + listParams.Context = ctx + listParams.AppName = appName + listParams.OrganizationID = loc.OrganizationID + listParams.ProjectID = loc.ProjectID + + listResp, err := client.VaultSecretsPreview.ListAppSecrets(listParams, nil) + if err != nil { + return nil, err + } + return listResp.GetPayload().Secrets, nil +} + +// OpenVaultSecretsAppSecret will retrieve the latest secret for a Vault Secrets app, including it's value. +func OpenVaultSecretsAppSecret(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, appName, secretName string) (*secretmodels.Secrets20231128OpenSecret, error) { + getParams := secret_service.NewOpenAppSecretParams() + getParams.Context = ctx + getParams.AppName = appName + getParams.SecretName = secretName + getParams.OrganizationID = loc.OrganizationID + getParams.ProjectID = loc.ProjectID + + var getResp *secret_service.OpenAppSecretOK + var err error + for attempt := 0; attempt < retryCount; attempt++ { + getResp, err = client.VaultSecretsPreview.OpenAppSecret(getParams, nil) + if err != nil { + var serviceErr *secret_service.OpenAppSecretDefault + ok := errors.As(err, &serviceErr) + if !ok { + return nil, err + } + + if shouldRetryErrorCode(serviceErr.Code(), []int{http.StatusTooManyRequests}) { + backOffDuration := getAPIBackoffDuration(serviceErr.Error()) + tflog.Debug(ctx, fmt.Sprintf("The api rate limit has been exceeded, retrying in %d seconds, attempt: %d", int64(backOffDuration.Seconds()), (attempt+1))) + time.Sleep(backOffDuration) + continue + } + return nil, err + } + break + } + + if getResp == nil { + return nil, errors.New("unable to get secret") + } + + return getResp.GetPayload().Secret, nil +} From 7aec0759246dcddbce301a199228b13fbc384809 Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Thu, 23 May 2024 12:08:45 -0400 Subject: [PATCH 02/20] update vault_secrets_secret --- internal/clients/vault_secrets_preview.go | 20 +++++++------- .../data_source_vault_secrets_secret.go | 27 ++++++++++++++++--- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/internal/clients/vault_secrets_preview.go b/internal/clients/vault_secrets_preview.go index e6eb7f19d..42b91b036 100644 --- a/internal/clients/vault_secrets_preview.go +++ b/internal/clients/vault_secrets_preview.go @@ -18,11 +18,10 @@ import ( // ListVaultSecretsAppSecrets will retrieve all app secrets metadata for a Vault Secrets application. func ListVaultSecretsAppSecrets(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, appName string) ([]*secretmodels.Secrets20231128Secret, error) { - listParams := secret_service.NewListAppSecretsParams() - listParams.Context = ctx - listParams.AppName = appName - listParams.OrganizationID = loc.OrganizationID - listParams.ProjectID = loc.ProjectID + listParams := secret_service.NewListAppSecretsParamsWithContext(ctx). + WithAppName(appName). + WithOrganizationID(loc.OrganizationID). + WithProjectID(loc.ProjectID) listResp, err := client.VaultSecretsPreview.ListAppSecrets(listParams, nil) if err != nil { @@ -33,12 +32,11 @@ func ListVaultSecretsAppSecrets(ctx context.Context, client *Client, loc *shared // OpenVaultSecretsAppSecret will retrieve the latest secret for a Vault Secrets app, including it's value. func OpenVaultSecretsAppSecret(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, appName, secretName string) (*secretmodels.Secrets20231128OpenSecret, error) { - getParams := secret_service.NewOpenAppSecretParams() - getParams.Context = ctx - getParams.AppName = appName - getParams.SecretName = secretName - getParams.OrganizationID = loc.OrganizationID - getParams.ProjectID = loc.ProjectID + getParams := secret_service.NewOpenAppSecretParamsWithContext(ctx). + WithAppName(appName). + WithSecretName(secretName). + WithOrganizationID(loc.OrganizationID). + WithProjectID(loc.ProjectID) var getResp *secret_service.OpenAppSecretOK var err error diff --git a/internal/provider/vaultsecrets/data_source_vault_secrets_secret.go b/internal/provider/vaultsecrets/data_source_vault_secrets_secret.go index adf6202e8..c162c4b32 100644 --- a/internal/provider/vaultsecrets/data_source_vault_secrets_secret.go +++ b/internal/provider/vaultsecrets/data_source_vault_secrets_secret.go @@ -5,6 +5,7 @@ package vaultsecrets import ( "context" + "encoding/json" "fmt" sharedmodels "github.com/hashicorp/hcp-sdk-go/clients/cloud-shared/v1/models" @@ -31,11 +32,11 @@ func NewVaultSecretsSecretDataSource() datasource.DataSource { return &DataSourceVaultSecretsSecret{} } -func (d *DataSourceVaultSecretsSecret) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { +func (d *DataSourceVaultSecretsSecret) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { resp.TypeName = req.ProviderTypeName + "_vault_secrets_secret" } -func (d *DataSourceVaultSecretsSecret) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { +func (d *DataSourceVaultSecretsSecret) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { resp.Schema = schema.Schema{ MarkdownDescription: "The Vault Secrets secret data source retrieves a singular secret and its latest version.", Attributes: map[string]schema.Attribute{ @@ -107,7 +108,27 @@ func (d *DataSourceVaultSecretsSecret) Read(ctx context.Context, req datasource. resp.Diagnostics.AddError(err.Error(), "Unable to open secret") return } - secretValue := openSecret.Version.Value + + // NOTE: for backwards compatibility purposes, if the secret is not a static secret (aka a string) + // encode the complex secret as a json string + var secretValue string + switch { + case openSecret.StaticVersion != nil: + secretValue = openSecret.StaticVersion.Value + case openSecret.RotatingVersion != nil: + secretData, err := json.Marshal(openSecret.RotatingVersion.Values) + if err != nil { + resp.Diagnostics.AddError(err.Error(), "could not encode rotating secret as json") + return + } + secretValue = string(secretData) + default: + resp.Diagnostics.AddError( + "Unsupported HCP Secret type", + fmt.Sprintf("HCP Secrets secret type %q is not currently supported by terraform-provider-hcp", openSecret.Type), + ) + return + } data.ID = data.AppName data.SecretValue = types.StringValue(secretValue) From 2705021c72996923e631b86cd23a43efd4f690e6 Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Thu, 23 May 2024 13:58:56 -0400 Subject: [PATCH 03/20] rotated secrets to map --- internal/clients/vault_secrets.go | 27 ----------- internal/clients/vault_secrets_preview.go | 45 +++++++++++++------ .../data_source_vault_secrets_app.go | 16 ++++++- .../data_source_vault_secrets_secret.go | 4 ++ .../resource_vault_secrets_secret.go | 4 +- 5 files changed, 52 insertions(+), 44 deletions(-) diff --git a/internal/clients/vault_secrets.go b/internal/clients/vault_secrets.go index d46b2fedf..eac704bf8 100644 --- a/internal/clients/vault_secrets.go +++ b/internal/clients/vault_secrets.go @@ -105,33 +105,6 @@ func CreateVaultSecretsAppSecret(ctx context.Context, client *Client, loc *share return createResp.Payload.Secret, nil } -func OpenVaultSecretsAppSecrets(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, appName string) ([]*secretmodels.Secrets20230613OpenSecret, error) { - params := secret_service.NewOpenAppSecretsParams() - params.Context = ctx - params.AppName = appName - params.LocationOrganizationID = loc.OrganizationID - params.LocationProjectID = loc.ProjectID - - var secrets *secret_service.OpenAppSecretsOK - var err error - for attempt := 0; attempt < retryCount; attempt++ { - secrets, err = client.VaultSecrets.OpenAppSecrets(params, nil) - if err != nil { - serviceErr, ok := err.(*secret_service.OpenAppSecretsDefault) - if !ok { - return nil, err - } - if shouldRetryWithSleep(ctx, serviceErr, attempt, []int{http.StatusTooManyRequests}) { - continue - } - return nil, err - } - break - } - - return secrets.Payload.Secrets, nil -} - // DeleteVaultSecretsAppSecret will delete a Vault Secrets application secret. func DeleteVaultSecretsAppSecret(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, appName, secretName string) error { diff --git a/internal/clients/vault_secrets_preview.go b/internal/clients/vault_secrets_preview.go index 42b91b036..8ad8adbb4 100644 --- a/internal/clients/vault_secrets_preview.go +++ b/internal/clients/vault_secrets_preview.go @@ -16,20 +16,6 @@ import ( "github.com/hashicorp/terraform-plugin-log/tflog" ) -// ListVaultSecretsAppSecrets will retrieve all app secrets metadata for a Vault Secrets application. -func ListVaultSecretsAppSecrets(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, appName string) ([]*secretmodels.Secrets20231128Secret, error) { - listParams := secret_service.NewListAppSecretsParamsWithContext(ctx). - WithAppName(appName). - WithOrganizationID(loc.OrganizationID). - WithProjectID(loc.ProjectID) - - listResp, err := client.VaultSecretsPreview.ListAppSecrets(listParams, nil) - if err != nil { - return nil, err - } - return listResp.GetPayload().Secrets, nil -} - // OpenVaultSecretsAppSecret will retrieve the latest secret for a Vault Secrets app, including it's value. func OpenVaultSecretsAppSecret(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, appName, secretName string) (*secretmodels.Secrets20231128OpenSecret, error) { getParams := secret_service.NewOpenAppSecretParamsWithContext(ctx). @@ -66,3 +52,34 @@ func OpenVaultSecretsAppSecret(ctx context.Context, client *Client, loc *sharedm return getResp.GetPayload().Secret, nil } + +func OpenVaultSecretsAppSecrets(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, appName string) ([]*secretmodels.Secrets20231128OpenSecret, error) { + params := secret_service.NewOpenAppSecretsParamsWithContext(ctx). + WithAppName(appName). + WithOrganizationID(loc.OrganizationID). + WithProjectID(loc.ProjectID) + + var secrets *secret_service.OpenAppSecretsOK + var err error + for attempt := 0; attempt < retryCount; attempt++ { + secrets, err = client.VaultSecretsPreview.OpenAppSecrets(params, nil) + if err != nil { + var serviceErr *secret_service.OpenAppSecretDefault + ok := errors.As(err, &serviceErr) + if !ok { + return nil, err + } + if shouldRetryWithSleep(ctx, serviceErr, attempt, []int{http.StatusTooManyRequests}) { + continue + } + return nil, err + } + break + } + + if secrets == nil { + return nil, errors.New("unable to get secrets") + } + + return secrets.GetPayload().Secrets, nil +} diff --git a/internal/provider/vaultsecrets/data_source_vault_secrets_app.go b/internal/provider/vaultsecrets/data_source_vault_secrets_app.go index 79540e13f..c688d36d6 100644 --- a/internal/provider/vaultsecrets/data_source_vault_secrets_app.go +++ b/internal/provider/vaultsecrets/data_source_vault_secrets_app.go @@ -106,8 +106,20 @@ func (d *DataSourceVaultSecretsApp) Read(ctx context.Context, req datasource.Rea openAppSecrets := map[string]string{} for _, appSecret := range appSecrets { - secretName := appSecret.Name - openAppSecrets[secretName] = appSecret.Version.Value + switch { + case appSecret.StaticVersion != nil: + openAppSecrets[appSecret.Name] = appSecret.StaticVersion.Value + case appSecret.RotatingVersion != nil: + for name, value := range appSecret.RotatingVersion.Values { + openAppSecrets[appSecret.Name+"_"+name] = value + } + default: + resp.Diagnostics.AddError( + "Unsupported HCP Secret type", + fmt.Sprintf("HCP Secrets secret type %q is not currently supported by terraform-provider-hcp", appSecret.Type), + ) + return + } } data.ID = data.AppName diff --git a/internal/provider/vaultsecrets/data_source_vault_secrets_secret.go b/internal/provider/vaultsecrets/data_source_vault_secrets_secret.go index c162c4b32..2d6cb6d7f 100644 --- a/internal/provider/vaultsecrets/data_source_vault_secrets_secret.go +++ b/internal/provider/vaultsecrets/data_source_vault_secrets_secret.go @@ -121,6 +121,10 @@ func (d *DataSourceVaultSecretsSecret) Read(ctx context.Context, req datasource. resp.Diagnostics.AddError(err.Error(), "could not encode rotating secret as json") return } + resp.Diagnostics.AddWarning( + "HCP Vault Secrets mismatched type", + "Attempted to get a rotating secret in a KV secret data source, encoding the secret values as JSON", + ) secretValue = string(secretData) default: resp.Diagnostics.AddError( diff --git a/internal/provider/vaultsecrets/resource_vault_secrets_secret.go b/internal/provider/vaultsecrets/resource_vault_secrets_secret.go index bdc472ec2..cea6f4736 100644 --- a/internal/provider/vaultsecrets/resource_vault_secrets_secret.go +++ b/internal/provider/vaultsecrets/resource_vault_secrets_secret.go @@ -175,7 +175,9 @@ func (r *resourceVaultsecretsSecret) Read(ctx context.Context, req resource.Read return } - state.SecretValue = types.StringValue(res.Version.Value) + // TODO: so the resource can only create a static secret, + // what happens when a user tries to import a rotating/other type of secret? + state.SecretValue = types.StringValue(res.StaticVersion.Value) resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } From f37d2deb0e744fcca2f8590f183d57c5ea4a5870 Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Tue, 28 May 2024 16:38:21 -0400 Subject: [PATCH 04/20] add changelog --- .changelog/850.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/850.txt diff --git a/.changelog/850.txt b/.changelog/850.txt new file mode 100644 index 000000000..f88c31162 --- /dev/null +++ b/.changelog/850.txt @@ -0,0 +1,3 @@ +```release-note:feature +Allows users to fetch rotating secrets using the hcp_vault_secrets_app and hcp_vault_secrets_secret data sources +``` From e2ed1cea13b4f174b42edc5eb3d0ae2d7bbbeaef Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Fri, 31 May 2024 09:01:02 -0400 Subject: [PATCH 05/20] Update internal/provider/vaultsecrets/data_source_vault_secrets_secret.go Co-authored-by: Anton Averchenkov <84287187+averche@users.noreply.github.com> --- .../provider/vaultsecrets/data_source_vault_secrets_secret.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/provider/vaultsecrets/data_source_vault_secrets_secret.go b/internal/provider/vaultsecrets/data_source_vault_secrets_secret.go index 2d6cb6d7f..cb0550850 100644 --- a/internal/provider/vaultsecrets/data_source_vault_secrets_secret.go +++ b/internal/provider/vaultsecrets/data_source_vault_secrets_secret.go @@ -109,8 +109,8 @@ func (d *DataSourceVaultSecretsSecret) Read(ctx context.Context, req datasource. return } - // NOTE: for backwards compatibility purposes, if the secret is not a static secret (aka a string) - // encode the complex secret as a json string + // NOTE: for backwards compatibility purposes, if the secret is not a static secret (a string) + // encode the complex secret as a JSON string var secretValue string switch { case openSecret.StaticVersion != nil: From 846054f77f270082e321e20648e0607a2e4aa784 Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Fri, 31 May 2024 17:48:01 -0400 Subject: [PATCH 06/20] add new data source --- internal/provider/provider.go | 1 + ...ta_source_vault_secrets_rotating_secret.go | 142 ++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret.go diff --git a/internal/provider/provider.go b/internal/provider/provider.go index d9640b01a..9e45feb90 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -170,6 +170,7 @@ func (p *ProviderFramework) DataSources(ctx context.Context) []func() datasource // Vault Secrets vaultsecrets.NewVaultSecretsAppDataSource, vaultsecrets.NewVaultSecretsSecretDataSource, + vaultsecrets.NewVaultSecretsRotatingSecretDataSource, // IAM iam.NewServicePrincipalDataSource, iam.NewGroupDataSource, diff --git a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret.go b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret.go new file mode 100644 index 000000000..c5e4780f9 --- /dev/null +++ b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret.go @@ -0,0 +1,142 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package vaultsecrets + +import ( + "context" + "fmt" + + sharedmodels "github.com/hashicorp/hcp-sdk-go/clients/cloud-shared/v1/models" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-provider-hcp/internal/clients" +) + +type DataSourceVaultSecretsRotatingSecret struct { + client *clients.Client +} + +type DataSourceVaultSecretsRotatingSecretModel struct { + ID types.String `tfsdk:"id"` + AppName types.String `tfsdk:"app_name"` + ProjectID types.String `tfsdk:"project_id"` + OrgID types.String `tfsdk:"organization_id"` + SecretName types.String `tfsdk:"secret_name"` + SecretValues types.Map `tfsdk:"secret_values"` + SecretVersion types.Int64 `tfsdk:"secret_version"` +} + +func NewVaultSecretsRotatingSecretDataSource() datasource.DataSource { + return &DataSourceVaultSecretsRotatingSecret{} +} + +func (d *DataSourceVaultSecretsRotatingSecret) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vault_secrets_rotating_secret" +} + +func (d *DataSourceVaultSecretsRotatingSecret) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "The Vault Secrets secret data source retrieves a rotating secret with its latest version.", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + Description: "The ID of this resource.", + }, + "app_name": schema.StringAttribute{ + Description: "The name of the Vault Secrets application.", + Required: true, + }, + "secret_name": schema.StringAttribute{ + Description: "The name of the Vault Secrets secret.", + Required: true, + }, + "secret_values": schema.MapAttribute{ + Description: "The secret values corresponding to the secret name input.", + Computed: true, + Sensitive: true, + ElementType: types.StringType, + }, + "secret_version": schema.Int64Attribute{ + Description: "The version of the Vault Secrets secret.", + Computed: true, + }, + "organization_id": schema.StringAttribute{ + Description: "The ID of the HCP organization where the Vault Secrets app is located.", + Computed: true, + }, + "project_id": schema.StringAttribute{ + Description: "The ID of the HCP project where the Vault Secrets app is located.", + Computed: true, + }, + }, + } +} + +func (d *DataSourceVaultSecretsRotatingSecret) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*clients.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *clients.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + return + } + d.client = client +} + +func (d *DataSourceVaultSecretsRotatingSecret) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data DataSourceVaultSecretsRotatingSecretModel + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + client := d.client + if client == nil { + resp.Diagnostics.AddError( + "Unconfigured HCP Client", + "Expected configured HCP client. Please report this issue to the provider developers.", + ) + return + } + + loc := &sharedmodels.HashicorpCloudLocationLocation{ + OrganizationID: client.Config.OrganizationID, + ProjectID: client.Config.ProjectID, + } + + openSecret, err := clients.OpenVaultSecretsAppSecret(ctx, client, loc, data.AppName.ValueString(), data.SecretName.ValueString()) + if err != nil { + resp.Diagnostics.AddError(err.Error(), "Unable to open secret") + return + } + + var secretValues map[string]string + var secretVersion int64 + switch { + case openSecret.RotatingVersion != nil: + secretValues = openSecret.RotatingVersion.Values + secretVersion = openSecret.RotatingVersion.Version + default: + resp.Diagnostics.AddError( + "Unsupported HCP Secret type", + fmt.Sprintf("HCP Secrets secret type %q is not currently supported by terraform-provider-hcp", openSecret.Type), + ) + return + } + + secretsOutput, diag := types.MapValueFrom(ctx, types.StringType, secretValues) + resp.Diagnostics.Append(diag...) + + // TODO: what is ID supposed to be? + //data.ID = ? + data.OrgID = types.StringValue(client.Config.OrganizationID) + data.ProjectID = types.StringValue(client.Config.ProjectID) + data.SecretValues = secretsOutput + data.SecretVersion = types.Int64Value(secretVersion) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} From 8a7c4adbb745dce7aaee9ea731e93d5e75691dec Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Fri, 31 May 2024 17:59:07 -0400 Subject: [PATCH 07/20] add some fields --- ...ta_source_vault_secrets_rotating_secret.go | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret.go b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret.go index c5e4780f9..53765d3d2 100644 --- a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret.go +++ b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret.go @@ -19,13 +19,14 @@ type DataSourceVaultSecretsRotatingSecret struct { } type DataSourceVaultSecretsRotatingSecretModel struct { - ID types.String `tfsdk:"id"` - AppName types.String `tfsdk:"app_name"` - ProjectID types.String `tfsdk:"project_id"` - OrgID types.String `tfsdk:"organization_id"` - SecretName types.String `tfsdk:"secret_name"` - SecretValues types.Map `tfsdk:"secret_values"` - SecretVersion types.Int64 `tfsdk:"secret_version"` + ID types.String `tfsdk:"id"` + AppName types.String `tfsdk:"app_name"` + ProjectID types.String `tfsdk:"project_id"` + OrgID types.String `tfsdk:"organization_id"` + SecretName types.String `tfsdk:"secret_name"` + SecretValues types.Map `tfsdk:"secret_values"` + SecretVersion types.Int64 `tfsdk:"secret_version"` + SecretProvider types.String `tfsdk:"secret_provider"` } func NewVaultSecretsRotatingSecretDataSource() datasource.DataSource { @@ -62,6 +63,10 @@ func (d *DataSourceVaultSecretsRotatingSecret) Schema(_ context.Context, _ datas Description: "The version of the Vault Secrets secret.", Computed: true, }, + "provider": schema.StringAttribute{ + Description: "The name of the provider this rotating secret is for", + Computed: true, + }, "organization_id": schema.StringAttribute{ Description: "The ID of the HCP organization where the Vault Secrets app is located.", Computed: true, @@ -137,6 +142,7 @@ func (d *DataSourceVaultSecretsRotatingSecret) Read(ctx context.Context, req dat data.ProjectID = types.StringValue(client.Config.ProjectID) data.SecretValues = secretsOutput data.SecretVersion = types.Int64Value(secretVersion) + data.SecretProvider = types.StringValue(openSecret.Provider) resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) } From 669fd012b2c2f8d702ab26126210b6cabb853d69 Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Fri, 31 May 2024 18:13:39 -0400 Subject: [PATCH 08/20] not really adding a test... --- ...urce_vault_secrets_rotating_secret_test.go | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go diff --git a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go new file mode 100644 index 000000000..2e3606e71 --- /dev/null +++ b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go @@ -0,0 +1,33 @@ +package vaultsecrets_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + + "github.com/hashicorp/terraform-provider-hcp/internal/provider/acctest" +) + +func TestDataSourceVaultSecretsRotatingSecret(t *testing.T) { + t.Skip("skipping the test for now, we need to be able to create a rotating secret") + testAppName := generateRandomSlug() + //dataSourceAddress := "data.hcp_vault_secrets_rotating_secret.foo" + + testSecretName := "my_test_secret" + testSecretValue := "insecurepassword" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + PreConfig: func() { + createTestApp(t, testAppName) + + createTestAppSecret(t, testAppName, testSecretName, "this shouldn't show up!") + createTestAppSecret(t, testAppName, testSecretName, testSecretValue) + }, + }, + }, + }) +} From f7c7cfd617f15a25d0583432db1efdde408c9f10 Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Mon, 3 Jun 2024 11:20:00 -0400 Subject: [PATCH 09/20] remove tests for now --- ...urce_vault_secrets_rotating_secret_test.go | 25 +------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go index 2e3606e71..49878fc56 100644 --- a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go +++ b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go @@ -2,32 +2,9 @@ package vaultsecrets_test import ( "testing" - - "github.com/hashicorp/terraform-plugin-testing/helper/resource" - - "github.com/hashicorp/terraform-provider-hcp/internal/provider/acctest" ) func TestDataSourceVaultSecretsRotatingSecret(t *testing.T) { + // TODO t.Skip("skipping the test for now, we need to be able to create a rotating secret") - testAppName := generateRandomSlug() - //dataSourceAddress := "data.hcp_vault_secrets_rotating_secret.foo" - - testSecretName := "my_test_secret" - testSecretValue := "insecurepassword" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t) }, - ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, - Steps: []resource.TestStep{ - { - PreConfig: func() { - createTestApp(t, testAppName) - - createTestAppSecret(t, testAppName, testSecretName, "this shouldn't show up!") - createTestAppSecret(t, testAppName, testSecretName, testSecretValue) - }, - }, - }, - }) } From a98fdb6152005eb2d87ad0a0eab5657f3a720e9c Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Mon, 3 Jun 2024 11:23:43 -0400 Subject: [PATCH 10/20] rename --- .../vaultsecrets/data_source_vault_secrets_rotating_secret.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret.go b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret.go index 53765d3d2..a29bd47f6 100644 --- a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret.go +++ b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret.go @@ -63,7 +63,7 @@ func (d *DataSourceVaultSecretsRotatingSecret) Schema(_ context.Context, _ datas Description: "The version of the Vault Secrets secret.", Computed: true, }, - "provider": schema.StringAttribute{ + "secret_provider": schema.StringAttribute{ Description: "The name of the provider this rotating secret is for", Computed: true, }, From d259623f824a7cf877e4d0dda73608c48cb71991 Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Mon, 3 Jun 2024 11:28:47 -0400 Subject: [PATCH 11/20] lint --- .../vaultsecrets/data_source_vault_secrets_rotating_secret.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret.go b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret.go index a29bd47f6..bc5b30e57 100644 --- a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret.go +++ b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret.go @@ -137,7 +137,7 @@ func (d *DataSourceVaultSecretsRotatingSecret) Read(ctx context.Context, req dat resp.Diagnostics.Append(diag...) // TODO: what is ID supposed to be? - //data.ID = ? + // data.ID = ? data.OrgID = types.StringValue(client.Config.OrganizationID) data.ProjectID = types.StringValue(client.Config.ProjectID) data.SecretValues = secretsOutput From 9d170a45265bffcbe9b0a3c95d32e27afa6df4e9 Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Mon, 3 Jun 2024 12:21:58 -0400 Subject: [PATCH 12/20] create test --- internal/clients/vault_secrets_preview.go | 57 ++++++++++ ...urce_vault_secrets_rotating_secret_test.go | 104 +++++++++++++++++- 2 files changed, 159 insertions(+), 2 deletions(-) diff --git a/internal/clients/vault_secrets_preview.go b/internal/clients/vault_secrets_preview.go index 8ad8adbb4..5454cce8f 100644 --- a/internal/clients/vault_secrets_preview.go +++ b/internal/clients/vault_secrets_preview.go @@ -83,3 +83,60 @@ func OpenVaultSecretsAppSecrets(ctx context.Context, client *Client, loc *shared return secrets.GetPayload().Secrets, nil } + +// CreateMongoDBAtlasRotationIntegration NOTE: currently just needed for tests +func CreateMongoDBAtlasRotationIntegration(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, integrationName, mongodbAtlasApiPublicKey, mongodbAtlasPrivateKey string) (*secretmodels.Secrets20231128MongoDBAtlasRotationIntegration, error) { + body := secret_service.CreateMongoDBAtlasRotationIntegrationBody{ + IntegrationName: integrationName, + MongodbAPIPublicKey: mongodbAtlasApiPublicKey, + MongodbAPIPrivateKey: mongodbAtlasPrivateKey, + } + params := secret_service.NewCreateMongoDBAtlasRotationIntegrationParamsWithContext(ctx). + WithOrganizationID(loc.OrganizationID). + WithProjectID(loc.ProjectID). + WithBody(body) + + resp, err := client.VaultSecretsPreview.CreateMongoDBAtlasRotationIntegration(params, nil) + if err != nil { + return nil, err + } + + return resp.GetPayload().RotationIntegration, nil +} + +// DeleteMongoDBAtlasRotationIntegration NOTE: currently just needed for tests +func DeleteMongoDBAtlasRotationIntegration(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, integrationName string) error { + params := secret_service.NewDeleteMongoDBAtlasRotationIntegrationParamsWithContext(ctx). + WithOrganizationID(loc.OrganizationID). + WithProjectID(loc.ProjectID). + WithIntegrationName(integrationName) + + _, err := client.VaultSecretsPreview.DeleteMongoDBAtlasRotationIntegration(params, nil) + if err != nil { + return err + } + + return nil +} + +// CreateMongoDBAtlasRotatingSecret NOTE: currently just needed for tests +func CreateMongoDBAtlasRotatingSecret( + ctx context.Context, + client *Client, + loc *sharedmodels.HashicorpCloudLocationLocation, + appName string, + requestBody secret_service.CreateMongoDBAtlasRotatingSecretBody, +) (*secretmodels.Secrets20231128CreateMongoDBAtlasRotatingSecretResponse, error) { + params := secret_service.NewCreateMongoDBAtlasRotatingSecretParamsWithContext(ctx). + WithOrganizationID(loc.OrganizationID). + WithProjectID(loc.ProjectID). + WithAppName(appName). + WithBody(requestBody) + + resp, err := client.VaultSecretsPreview.CreateMongoDBAtlasRotatingSecret(params, nil) + if err != nil { + return nil, err + } + + return resp.GetPayload(), nil +} diff --git a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go index 49878fc56..044ae6c47 100644 --- a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go +++ b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go @@ -1,10 +1,110 @@ package vaultsecrets_test import ( + "context" + "fmt" + "os" "testing" + + sharedmodels "github.com/hashicorp/hcp-sdk-go/clients/cloud-shared/v1/models" + "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/preview/2023-11-28/client/secret_service" + secretmodels "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/preview/2023-11-28/models" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + + "github.com/hashicorp/terraform-provider-hcp/internal/clients" + "github.com/hashicorp/terraform-provider-hcp/internal/provider/acctest" ) +func checkRequiredEnvVarOrFail(t *testing.T, varName string) string { + value, exists := os.LookupEnv(varName) + if !exists { + t.Skipf("%s must be set to execute this test", varName) + } + return value +} + func TestDataSourceVaultSecretsRotatingSecret(t *testing.T) { - // TODO - t.Skip("skipping the test for now, we need to be able to create a rotating secret") + + mongodbAtlasPublicKey := checkRequiredEnvVarOrFail(t, "MONGODB_ATLAS_API_PUBLIC_KEY") + mongodbAtlasPrivateKey := checkRequiredEnvVarOrFail(t, "MONGODB_ATLAS_API_PRIVATE_KEY") + mongodbAtlasGroupID := checkRequiredEnvVarOrFail(t, "MONGODB_ATLAS_GROUP_ID") + mongodbAtlasDbName := checkRequiredEnvVarOrFail(t, "MONGODB_ATLAS_DB_NAME") + + testAppName := generateRandomSlug() + testIntegrationName := generateRandomSlug() + dataSourceAddress := "data.hcp_vault_secrets_rotating_secret.foo" + + testSecretName := "secret_one" + + tfconfig := fmt.Sprintf(`data "hcp_vault_secrets_rotating_secret" "foo" { + app_name = %q + secret_name = %q + }`, testAppName, testSecretName) + + client := acctest.HCPClients(t) + loc := &sharedmodels.HashicorpCloudLocationLocation{ + OrganizationID: client.Config.OrganizationID, + ProjectID: client.Config.ProjectID, + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + PreConfig: func() { + createTestApp(t, testAppName) + ctx := context.Background() + + _, err := clients.CreateMongoDBAtlasRotationIntegration(ctx, client, loc, testIntegrationName, mongodbAtlasPublicKey, mongodbAtlasPrivateKey) + if err != nil { + t.Fatalf("could not create mongodb rotation integration: %v", err) + } + + reqBody := secret_service.CreateMongoDBAtlasRotatingSecretBody{ + SecretName: testSecretName, + RotationIntegrationName: testIntegrationName, + RotationPolicyName: "built-in:30-days-2-active", + MongodbGroupID: mongodbAtlasGroupID, + MongodbRoles: []*secretmodels.Secrets20231128MongoDBRole{ + { + DatabaseName: mongodbAtlasDbName, + RoleName: "read", + CollectionName: "", + }, + }, + } + _, err = clients.CreateMongoDBAtlasRotatingSecret(ctx, client, loc, testAppName, reqBody) + if err != nil { + t.Fatalf("could not create rotating mongodb atlas secret: %v", err) + } + }, + Config: tfconfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(dataSourceAddress, "organization_id"), + resource.TestCheckResourceAttrSet(dataSourceAddress, "project_id"), + resource.TestCheckResourceAttrSet(dataSourceAddress, "secret_values"), + resource.TestCheckResourceAttr(dataSourceAddress, "app_name", testAppName), + resource.TestCheckResourceAttr(dataSourceAddress, "secret_provider", "mongodb-atlas"), + ), + }, + }, + CheckDestroy: func(_ *terraform.State) error { + ctx := context.Background() + err := clients.DeleteVaultSecretsAppSecret(ctx, client, loc, testAppName, testSecretName) + if err != nil { + return fmt.Errorf("could not delete rotating secret: %v", err) + } + + err = clients.DeleteMongoDBAtlasRotationIntegration(ctx, client, loc, testIntegrationName) + if err != nil { + return fmt.Errorf("could not delete rotation integration: %v", err) + } + + deleteTestApp(t, testAppName) + + return nil + }, + }) } From 28d011a6813b821f0009dd62a15adf9e2600dee3 Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Mon, 3 Jun 2024 12:22:28 -0400 Subject: [PATCH 13/20] rename --- .../data_source_vault_secrets_rotating_secret_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go index 044ae6c47..92e258915 100644 --- a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go +++ b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go @@ -24,7 +24,7 @@ func checkRequiredEnvVarOrFail(t *testing.T, varName string) string { return value } -func TestDataSourceVaultSecretsRotatingSecret(t *testing.T) { +func TestAcc_dataSourceVaultSecretsRotatingSecret(t *testing.T) { mongodbAtlasPublicKey := checkRequiredEnvVarOrFail(t, "MONGODB_ATLAS_API_PUBLIC_KEY") mongodbAtlasPrivateKey := checkRequiredEnvVarOrFail(t, "MONGODB_ATLAS_API_PRIVATE_KEY") From 2877528bfb5a7736924693b4ff3912446399747d Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Mon, 3 Jun 2024 12:27:14 -0400 Subject: [PATCH 14/20] rename for lint --- internal/clients/vault_secrets_preview.go | 4 ++-- .../data_source_vault_secrets_rotating_secret_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/clients/vault_secrets_preview.go b/internal/clients/vault_secrets_preview.go index 5454cce8f..7c1d28f68 100644 --- a/internal/clients/vault_secrets_preview.go +++ b/internal/clients/vault_secrets_preview.go @@ -85,10 +85,10 @@ func OpenVaultSecretsAppSecrets(ctx context.Context, client *Client, loc *shared } // CreateMongoDBAtlasRotationIntegration NOTE: currently just needed for tests -func CreateMongoDBAtlasRotationIntegration(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, integrationName, mongodbAtlasApiPublicKey, mongodbAtlasPrivateKey string) (*secretmodels.Secrets20231128MongoDBAtlasRotationIntegration, error) { +func CreateMongoDBAtlasRotationIntegration(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, integrationName, mongodbAtlasPublicKey, mongodbAtlasPrivateKey string) (*secretmodels.Secrets20231128MongoDBAtlasRotationIntegration, error) { body := secret_service.CreateMongoDBAtlasRotationIntegrationBody{ IntegrationName: integrationName, - MongodbAPIPublicKey: mongodbAtlasApiPublicKey, + MongodbAPIPublicKey: mongodbAtlasPublicKey, MongodbAPIPrivateKey: mongodbAtlasPrivateKey, } params := secret_service.NewCreateMongoDBAtlasRotationIntegrationParamsWithContext(ctx). diff --git a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go index 92e258915..69be6facd 100644 --- a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go +++ b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go @@ -29,7 +29,7 @@ func TestAcc_dataSourceVaultSecretsRotatingSecret(t *testing.T) { mongodbAtlasPublicKey := checkRequiredEnvVarOrFail(t, "MONGODB_ATLAS_API_PUBLIC_KEY") mongodbAtlasPrivateKey := checkRequiredEnvVarOrFail(t, "MONGODB_ATLAS_API_PRIVATE_KEY") mongodbAtlasGroupID := checkRequiredEnvVarOrFail(t, "MONGODB_ATLAS_GROUP_ID") - mongodbAtlasDbName := checkRequiredEnvVarOrFail(t, "MONGODB_ATLAS_DB_NAME") + mongodbAtlasDBName := checkRequiredEnvVarOrFail(t, "MONGODB_ATLAS_DB_NAME") testAppName := generateRandomSlug() testIntegrationName := generateRandomSlug() @@ -69,7 +69,7 @@ func TestAcc_dataSourceVaultSecretsRotatingSecret(t *testing.T) { MongodbGroupID: mongodbAtlasGroupID, MongodbRoles: []*secretmodels.Secrets20231128MongoDBRole{ { - DatabaseName: mongodbAtlasDbName, + DatabaseName: mongodbAtlasDBName, RoleName: "read", CollectionName: "", }, From 6841583b99fce199044ce14a26c2a99eecd8d274 Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Tue, 4 Jun 2024 14:28:18 -0400 Subject: [PATCH 15/20] update api --- internal/clients/vault_secrets_preview.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/clients/vault_secrets_preview.go b/internal/clients/vault_secrets_preview.go index 7c1d28f68..e52db6277 100644 --- a/internal/clients/vault_secrets_preview.go +++ b/internal/clients/vault_secrets_preview.go @@ -85,33 +85,33 @@ func OpenVaultSecretsAppSecrets(ctx context.Context, client *Client, loc *shared } // CreateMongoDBAtlasRotationIntegration NOTE: currently just needed for tests -func CreateMongoDBAtlasRotationIntegration(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, integrationName, mongodbAtlasPublicKey, mongodbAtlasPrivateKey string) (*secretmodels.Secrets20231128MongoDBAtlasRotationIntegration, error) { - body := secret_service.CreateMongoDBAtlasRotationIntegrationBody{ +func CreateMongoDBAtlasRotationIntegration(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, integrationName, mongodbAtlasPublicKey, mongodbAtlasPrivateKey string) (*secretmodels.Secrets20231128MongoDBAtlasIntegration, error) { + body := secret_service.CreateMongoDBAtlasIntegrationBody{ IntegrationName: integrationName, MongodbAPIPublicKey: mongodbAtlasPublicKey, MongodbAPIPrivateKey: mongodbAtlasPrivateKey, } - params := secret_service.NewCreateMongoDBAtlasRotationIntegrationParamsWithContext(ctx). + params := secret_service.NewCreateMongoDBAtlasIntegrationParamsWithContext(ctx). WithOrganizationID(loc.OrganizationID). WithProjectID(loc.ProjectID). WithBody(body) - resp, err := client.VaultSecretsPreview.CreateMongoDBAtlasRotationIntegration(params, nil) + resp, err := client.VaultSecretsPreview.CreateMongoDBAtlasIntegration(params, nil) if err != nil { return nil, err } - return resp.GetPayload().RotationIntegration, nil + return resp.GetPayload().Integration, nil } // DeleteMongoDBAtlasRotationIntegration NOTE: currently just needed for tests func DeleteMongoDBAtlasRotationIntegration(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, integrationName string) error { - params := secret_service.NewDeleteMongoDBAtlasRotationIntegrationParamsWithContext(ctx). + params := secret_service.NewDeleteMongoDBAtlasIntegrationParamsWithContext(ctx). WithOrganizationID(loc.OrganizationID). WithProjectID(loc.ProjectID). WithIntegrationName(integrationName) - _, err := client.VaultSecretsPreview.DeleteMongoDBAtlasRotationIntegration(params, nil) + _, err := client.VaultSecretsPreview.DeleteMongoDBAtlasIntegration(params, nil) if err != nil { return err } From 3ff8616934571577b3b6ffc765b731b88a77d5a2 Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Tue, 4 Jun 2024 15:25:05 -0400 Subject: [PATCH 16/20] added change log --- .changelog/854.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/854.txt diff --git a/.changelog/854.txt b/.changelog/854.txt new file mode 100644 index 000000000..656f55a9b --- /dev/null +++ b/.changelog/854.txt @@ -0,0 +1,3 @@ +```release-note:feature +add vault_secrets_rotating_secret data source +``` From 30dbb73e45d0400950059bae0f676aafb835fa50 Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Wed, 5 Jun 2024 11:06:38 -0400 Subject: [PATCH 17/20] block until the secret is created --- internal/clients/vault_secrets_preview.go | 15 +++++++++++ ...urce_vault_secrets_rotating_secret_test.go | 25 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/internal/clients/vault_secrets_preview.go b/internal/clients/vault_secrets_preview.go index e52db6277..71cf5a7bf 100644 --- a/internal/clients/vault_secrets_preview.go +++ b/internal/clients/vault_secrets_preview.go @@ -84,6 +84,21 @@ func OpenVaultSecretsAppSecrets(ctx context.Context, client *Client, loc *shared return secrets.GetPayload().Secrets, nil } +func GetRotatingSecretState(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, appName, secretName string) (*secretmodels.Secrets20231128RotatingSecretState, error) { + params := secret_service.NewGetRotatingSecretStateParamsWithContext(ctx). + WithOrganizationID(loc.OrganizationID). + WithProjectID(loc.ProjectID). + WithAppName(appName). + WithSecretName(secretName) + + resp, err := client.VaultSecretsPreview.GetRotatingSecretState(params, nil) + if err != nil { + return nil, err + } + + return resp.GetPayload().State, nil +} + // CreateMongoDBAtlasRotationIntegration NOTE: currently just needed for tests func CreateMongoDBAtlasRotationIntegration(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, integrationName, mongodbAtlasPublicKey, mongodbAtlasPrivateKey string) (*secretmodels.Secrets20231128MongoDBAtlasIntegration, error) { body := secret_service.CreateMongoDBAtlasIntegrationBody{ diff --git a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go index 69be6facd..06953d4dc 100644 --- a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go +++ b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "testing" + "time" sharedmodels "github.com/hashicorp/hcp-sdk-go/clients/cloud-shared/v1/models" "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/preview/2023-11-28/client/secret_service" @@ -79,6 +80,30 @@ func TestAcc_dataSourceVaultSecretsRotatingSecret(t *testing.T) { if err != nil { t.Fatalf("could not create rotating mongodb atlas secret: %v", err) } + + // block until the secret is done + // TODO: is the time amount excessive? + timer := time.AfterFunc(20*time.Minute, func() { + t.Fatalf("timed out waiting for mongodb rotating secret to be created") + }) + + for { + state, err := clients.GetRotatingSecretState(ctx, client, loc, testAppName, testSecretName) + if err != nil { + t.Fatalf("could not get rotating secret state: %v", err) + } + switch *state.Status { + case secretmodels.Secrets20231128RotatingSecretStatusERRORED: + t.Fatalf("error rotating secret: %q", state.ErrorMessage) + case secretmodels.Secrets20231128RotatingSecretStatusWAITINGFORNEXTROTATION: + timer.Stop() + t.Log("secret successfully rotated") + break + default: + time.Sleep(time.Minute) + } + } + }, Config: tfconfig, Check: resource.ComposeTestCheckFunc( From 8288a7e1569c8dc4b813bfeb5cb44e286cfc729d Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Wed, 5 Jun 2024 11:09:35 -0400 Subject: [PATCH 18/20] break in wrong place --- ...urce_vault_secrets_rotating_secret_test.go | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go index 06953d4dc..b3e973e02 100644 --- a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go +++ b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go @@ -87,23 +87,27 @@ func TestAcc_dataSourceVaultSecretsRotatingSecret(t *testing.T) { t.Fatalf("timed out waiting for mongodb rotating secret to be created") }) - for { - state, err := clients.GetRotatingSecretState(ctx, client, loc, testAppName, testSecretName) - if err != nil { - t.Fatalf("could not get rotating secret state: %v", err) - } - switch *state.Status { - case secretmodels.Secrets20231128RotatingSecretStatusERRORED: - t.Fatalf("error rotating secret: %q", state.ErrorMessage) - case secretmodels.Secrets20231128RotatingSecretStatusWAITINGFORNEXTROTATION: - timer.Stop() - t.Log("secret successfully rotated") - break - default: - time.Sleep(time.Minute) + waitForSecret := func() { + for { + state, err := clients.GetRotatingSecretState(ctx, client, loc, testAppName, testSecretName) + if err != nil { + t.Fatalf("could not get rotating secret state: %v", err) + } + switch *state.Status { + case secretmodels.Secrets20231128RotatingSecretStatusERRORED: + t.Fatalf("error rotating secret: %q", state.ErrorMessage) + case secretmodels.Secrets20231128RotatingSecretStatusWAITINGFORNEXTROTATION: + timer.Stop() + t.Log("secret successfully rotated") + return + default: + time.Sleep(time.Minute) + } } } + waitForSecret() + }, Config: tfconfig, Check: resource.ComposeTestCheckFunc( From 88141af576eb4b038fdad587fc9da36c551b5547 Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Wed, 5 Jun 2024 11:17:09 -0400 Subject: [PATCH 19/20] test passes --- .../data_source_vault_secrets_rotating_secret_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go index b3e973e02..9ba54f046 100644 --- a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go +++ b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go @@ -82,8 +82,7 @@ func TestAcc_dataSourceVaultSecretsRotatingSecret(t *testing.T) { } // block until the secret is done - // TODO: is the time amount excessive? - timer := time.AfterFunc(20*time.Minute, func() { + timer := time.AfterFunc(10*time.Minute, func() { t.Fatalf("timed out waiting for mongodb rotating secret to be created") }) @@ -101,7 +100,8 @@ func TestAcc_dataSourceVaultSecretsRotatingSecret(t *testing.T) { t.Log("secret successfully rotated") return default: - time.Sleep(time.Minute) + t.Log("waiting to check rotating secret state") + time.Sleep(30 * time.Second) } } } @@ -113,7 +113,7 @@ func TestAcc_dataSourceVaultSecretsRotatingSecret(t *testing.T) { Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet(dataSourceAddress, "organization_id"), resource.TestCheckResourceAttrSet(dataSourceAddress, "project_id"), - resource.TestCheckResourceAttrSet(dataSourceAddress, "secret_values"), + resource.TestCheckResourceAttr(dataSourceAddress, "secret_values.%", "2"), // required: check the number of elements in the map resource.TestCheckResourceAttr(dataSourceAddress, "app_name", testAppName), resource.TestCheckResourceAttr(dataSourceAddress, "secret_provider", "mongodb-atlas"), ), From c089e2ae3a17386b6d7096c8b25c0da65f123424 Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Wed, 5 Jun 2024 11:17:37 -0400 Subject: [PATCH 20/20] rename --- .../data_source_vault_secrets_rotating_secret_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go index 9ba54f046..feadd9806 100644 --- a/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go +++ b/internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go @@ -82,7 +82,7 @@ func TestAcc_dataSourceVaultSecretsRotatingSecret(t *testing.T) { } // block until the secret is done - timer := time.AfterFunc(10*time.Minute, func() { + timeout := time.AfterFunc(10*time.Minute, func() { t.Fatalf("timed out waiting for mongodb rotating secret to be created") }) @@ -96,12 +96,12 @@ func TestAcc_dataSourceVaultSecretsRotatingSecret(t *testing.T) { case secretmodels.Secrets20231128RotatingSecretStatusERRORED: t.Fatalf("error rotating secret: %q", state.ErrorMessage) case secretmodels.Secrets20231128RotatingSecretStatusWAITINGFORNEXTROTATION: - timer.Stop() + timeout.Stop() t.Log("secret successfully rotated") return default: t.Log("waiting to check rotating secret state") - time.Sleep(30 * time.Second) + time.Sleep(10 * time.Second) } } }