From 7260ca45d7273a7b2c6282714a354fb48996e221 Mon Sep 17 00:00:00 2001 From: Adarsh Shah Date: Fri, 13 Oct 2023 13:49:06 -0400 Subject: [PATCH] adds tembo_instance_secret data source (#35) --- docs/data-sources/instance_secret.md | 26 +++ examples/resource-creation/main.tf | 10 ++ .../provider/instance_secret_data_source.go | 150 ++++++++++++++++++ .../instance_secret_data_source_test.go | 44 +++++ .../provider/instance_secrets_data_source.go | 2 +- internal/provider/provider.go | 1 + 6 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 docs/data-sources/instance_secret.md create mode 100644 internal/provider/instance_secret_data_source.go create mode 100644 internal/provider/instance_secret_data_source_test.go diff --git a/docs/data-sources/instance_secret.md b/docs/data-sources/instance_secret.md new file mode 100644 index 0000000..dff6d2c --- /dev/null +++ b/docs/data-sources/instance_secret.md @@ -0,0 +1,26 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "tembo_instance_secret Data Source - terraform-provider-tembo" +subcategory: "" +description: |- + Data Source for Tembo Instance Secret. +--- + +# tembo_instance_secret (Data Source) + +Data Source for Tembo Instance Secret. + + + + +## Schema + +### Required + +- `instance_id` (String) Unique ID for the instance generated by Tembo +- `org_id` (String) Id of the organization in which the instance will be created +- `secret_name` (String) Secret name + +### Read-Only + +- `secrets` (Map of String) Secret Key/Values diff --git a/examples/resource-creation/main.tf b/examples/resource-creation/main.tf index 1fb1813..4fcf1b1 100644 --- a/examples/resource-creation/main.tf +++ b/examples/resource-creation/main.tf @@ -69,6 +69,12 @@ data "tembo_instance_secrets" "test" { instance_id = tembo_instance.test_db.instance_id } +data "tembo_instance_secret" "test_sec" { + org_id = "org_2UdhszNbCVhLAXkZm30nz8pL778" + instance_id = tembo_instance.test_db.instance_id + secret_name = "readonly-role" +} + output "instance" { value = tembo_instance.test_db @@ -77,3 +83,7 @@ output "instance" { output "data" { value = data.tembo_instance_secrets.test } + +output "data_secret" { + value = data.tembo_instance_secret.test_sec +} diff --git a/internal/provider/instance_secret_data_source.go b/internal/provider/instance_secret_data_source.go new file mode 100644 index 0000000..51e07f8 --- /dev/null +++ b/internal/provider/instance_secret_data_source.go @@ -0,0 +1,150 @@ +package provider + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + tembodataclient "github.com/tembo-io/terraform-provider-tembo/tembodataclient" +) + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ datasource.DataSource = &temboInstanceSecret{} + _ datasource.DataSourceWithConfigure = &temboInstanceSecret{} +) + +// NewTemboInstanceSecretDataSource is a helper function to simplify the provider implementation. +func NewTemboInstanceSecretDataSource() datasource.DataSource { + return &temboInstanceSecret{} +} + +// TemboInstanceSecret is the data source implementation. +type temboInstanceSecret struct { + temboInstanceSecretsConfig instanceSecretsConfig +} + +// Metadata returns the data source type name. +func (d *temboInstanceSecret) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_instance_secret" +} + +// Schema defines the schema for the data source. +func (d *temboInstanceSecret) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Data Source for Tembo Instance Secret.", + Attributes: map[string]schema.Attribute{ + "org_id": schema.StringAttribute{ + MarkdownDescription: "Id of the organization in which the instance will be created", + Required: true, + }, + "instance_id": schema.StringAttribute{ + MarkdownDescription: "Unique ID for the instance generated by Tembo", + Required: true, + }, + "secret_name": schema.StringAttribute{ + MarkdownDescription: "Secret name", + Required: true, + }, + "secrets": schema.MapAttribute{ + MarkdownDescription: "Secret Key/Values", + Computed: true, + ElementType: types.StringType, + }, + }, + } +} + +// Configure adds the provider configured client to the data source. +func (d *temboInstanceSecret) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + temboInstanceSecretConfig, ok := req.ProviderData.(instanceSecretsConfig) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *instanceSecretConfig, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + d.temboInstanceSecretsConfig = temboInstanceSecretConfig +} + +// temboInstanceSecretModel maps the data source schema data. +type temboInstanceSecretModel struct { + OrgId types.String `tfsdk:"org_id"` + InstanceId types.String `tfsdk:"instance_id"` + SecretName types.String `tfsdk:"secret_name"` + Secrets map[string]types.String `tfsdk:"secrets"` +} + +// Read refreshes the Terraform state with the latest data. +func (d *temboInstanceSecret) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + // Get current state + var state temboInstanceSecretModel + + ctx = context.WithValue(ctx, tembodataclient.ContextAccessToken, d.temboInstanceSecretsConfig.accessToken) + + var orgId string + resp.Diagnostics.Append(req.Config.GetAttribute(ctx, path.Root("org_id"), &orgId)...) + + var instanceId string + resp.Diagnostics.Append(req.Config.GetAttribute(ctx, path.Root("instance_id"), &instanceId)...) + + var secretName string + resp.Diagnostics.Append(req.Config.GetAttribute(ctx, path.Root("secret_name"), &secretName)...) + + if resp.Diagnostics.HasError() { + tflog.Error(ctx, fmt.Sprintf("error reading terraform plan %v", resp.Diagnostics.Errors())) + return + } + + availableSecrets, _, err := d.temboInstanceSecretsConfig.client.SecretsApi.GetSecretNamesV1(ctx, orgId, instanceId).Execute() + if err != nil { + resp.Diagnostics.AddError( + "Unable to Read Tembo Instance Available Secrets", + err.Error(), + ) + return + } + + // Get refreshed Instance value from API + secret, _, err := d.temboInstanceSecretsConfig.client.SecretsApi.GetSecretV1(ctx, orgId, instanceId, secretName).Execute() + if err != nil { + resp.Diagnostics.AddError( + "Unable to Read Tembo Instance Secret", + err.Error(), + ) + return + } + + localSecret := make(map[string]types.String) + + if len(availableSecrets) > 0 { + for _, aSecret := range availableSecrets { + if aSecret.Name == secretName { + for _, possibleKey := range aSecret.PossibleKeys { + localSecret[possibleKey] = types.StringValue(secret[possibleKey]) + } + state.Secrets = localSecret + } + } + } + + // Set refreshed state + diags := resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} diff --git a/internal/provider/instance_secret_data_source_test.go b/internal/provider/instance_secret_data_source_test.go new file mode 100644 index 0000000..abf04d1 --- /dev/null +++ b/internal/provider/instance_secret_data_source_test.go @@ -0,0 +1,44 @@ +package provider + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestInstanceSecretDataSource(t *testing.T) { + instanceName := generateInstanceName() + orgId := os.Getenv("ORG_ID") + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Read testing + { + Config: testProviderConfig() + testInstanceResourceCreateConfig(instanceName, orgId) + testInstanceSecretCreateConfig(orgId), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("data.tembo_instance_secret.test_readonly", "secrets.username"), + resource.TestCheckResourceAttrSet("data.tembo_instance_secret.test_readonly", "secrets.password"), + resource.TestCheckResourceAttrSet("data.tembo_instance_secret.test_certificate", "secrets.ca.crt"), + ), + }, + }, + }) +} + +func testInstanceSecretCreateConfig(orgId string) string { + return fmt.Sprintf(` + data "tembo_instance_secret" "test_readonly" { + org_id = "%v" + instance_id = tembo_instance.test.instance_id + secret_name = "readonly-role" + } + data "tembo_instance_secret" "test_certificate" { + org_id = "%v" + instance_id = tembo_instance.test.instance_id + secret_name = "certificate" + } + `, orgId, orgId) +} diff --git a/internal/provider/instance_secrets_data_source.go b/internal/provider/instance_secrets_data_source.go index 6b72809..71f38fc 100644 --- a/internal/provider/instance_secrets_data_source.go +++ b/internal/provider/instance_secrets_data_source.go @@ -127,7 +127,7 @@ func (d *temboInstanceSecretsDataSource) Read(ctx context.Context, req datasourc return } - // Get refreshed Instance value from API + // Get Secret value from API availableSecrets, _, err := d.temboInstanceSecretsConfig.client.SecretsApi.GetSecretNamesV1(ctx, orgId, instanceId).Execute() if err != nil { resp.Diagnostics.AddError( diff --git a/internal/provider/provider.go b/internal/provider/provider.go index d0caada..cba24a5 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -214,6 +214,7 @@ func (p *temboProvider) Configure(ctx context.Context, req provider.ConfigureReq func (p *temboProvider) DataSources(_ context.Context) []func() datasource.DataSource { return []func() datasource.DataSource{ NewTemboInstanceSecretsDataSource, + NewTemboInstanceSecretDataSource, } }