generated from hashicorp/terraform-provider-scaffolding
-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
vault secrets | rotated secrets data source (#854)
* switch some methods over * update vault_secrets_secret * rotated secrets to map * add changelog * Update internal/provider/vaultsecrets/data_source_vault_secrets_secret.go Co-authored-by: Anton Averchenkov <[email protected]> * add new data source * add some fields * not really adding a test... * remove tests for now * rename * lint * create test * rename * rename for lint * update api * added change log * block until the secret is created * break in wrong place * test passes * rename --------- Co-authored-by: Anton Averchenkov <[email protected]>
- Loading branch information
Showing
5 changed files
with
363 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
```release-note:feature | ||
add vault_secrets_rotating_secret data source | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
148 changes: 148 additions & 0 deletions
148
internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
// 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"` | ||
SecretProvider types.String `tfsdk:"secret_provider"` | ||
} | ||
|
||
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, | ||
}, | ||
"secret_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, | ||
}, | ||
"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) | ||
data.SecretProvider = types.StringValue(openSecret.Provider) | ||
|
||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) | ||
} |
139 changes: 139 additions & 0 deletions
139
internal/provider/vaultsecrets/data_source_vault_secrets_rotating_secret_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
package vaultsecrets_test | ||
|
||
import ( | ||
"context" | ||
"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" | ||
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 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") | ||
|
||
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) | ||
} | ||
|
||
// block until the secret is done | ||
timeout := time.AfterFunc(10*time.Minute, func() { | ||
t.Fatalf("timed out waiting for mongodb rotating secret to be created") | ||
}) | ||
|
||
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: | ||
timeout.Stop() | ||
t.Log("secret successfully rotated") | ||
return | ||
default: | ||
t.Log("waiting to check rotating secret state") | ||
time.Sleep(10 * time.Second) | ||
} | ||
} | ||
} | ||
|
||
waitForSecret() | ||
|
||
}, | ||
Config: tfconfig, | ||
Check: resource.ComposeTestCheckFunc( | ||
resource.TestCheckResourceAttrSet(dataSourceAddress, "organization_id"), | ||
resource.TestCheckResourceAttrSet(dataSourceAddress, "project_id"), | ||
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"), | ||
), | ||
}, | ||
}, | ||
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 | ||
}, | ||
}) | ||
} |