diff --git a/CHANGELOG.md b/CHANGELOG.md index e2a135d2..4773a6ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.7.0] - 2024-05-17 + +## Added + +- New `cockroach_metric_export_prometheus_config` resource allows user to configure prometheus metric + export integration in AWS and GCP cloud providers. + ## [1.6.0] - 2024-05-02 ## Added diff --git a/docs/resources/metric_export_prometheus_config.md b/docs/resources/metric_export_prometheus_config.md new file mode 100644 index 00000000..8ad18d73 --- /dev/null +++ b/docs/resources/metric_export_prometheus_config.md @@ -0,0 +1,35 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "cockroach_metric_export_prometheus_config Resource - terraform-provider-cockroach" +subcategory: "" +description: |- + Prometheus metric export configuration for a cluster. This is only available for dedicated clusters with AWS and GCP cloud providers. +--- + +# cockroach_metric_export_prometheus_config (Resource) + +Prometheus metric export configuration for a cluster. This is only available for dedicated clusters with AWS and GCP cloud providers. + +## Example Usage + +```terraform +variable "cluster_id" { + type = string +} + +resource "cockroach_metric_export_prometheus_config" "example" { + id = var.cluster_id +} +``` + + +## Schema + +### Required + +- `id` (String) Cluster ID. + +### Read-Only + +- `status` (String) The current state of the metric export configuration. Possible values are: [`NOT_DEPLOYED` `DISABLING` `ENABLING` `ENABLED` `ERROR`] +- `targets` (Map of String) Represents prometheus scrape endpoint for each region. You can fetch endpoints either by executing `terraform state show {resource}` or by enabling terraform logs. diff --git a/examples/resources/cockroach_metric_export_prometheus_config/resource.tf b/examples/resources/cockroach_metric_export_prometheus_config/resource.tf new file mode 100644 index 00000000..8516aedb --- /dev/null +++ b/examples/resources/cockroach_metric_export_prometheus_config/resource.tf @@ -0,0 +1,7 @@ +variable "cluster_id" { + type = string +} + +resource "cockroach_metric_export_prometheus_config" "example" { + id = var.cluster_id +} \ No newline at end of file diff --git a/examples/workflows/cockroach_metric_export/aws/main.tf b/examples/workflows/cockroach_metric_export/aws/main.tf index c085dfec..6018b497 100644 --- a/examples/workflows/cockroach_metric_export/aws/main.tf +++ b/examples/workflows/cockroach_metric_export/aws/main.tf @@ -169,4 +169,8 @@ resource "cockroach_metric_export_datadog_config" "example" { id = cockroach_cluster.example.id site = var.datadog_site api_key = var.datadog_api_key +} + +resource "cockroach_metric_export_prometheus_config" "example" { + id = cockroach_cluster.example.id } \ No newline at end of file diff --git a/examples/workflows/cockroach_metric_export/gcp/main.tf b/examples/workflows/cockroach_metric_export/gcp/main.tf index 384c852e..3b54fbf6 100644 --- a/examples/workflows/cockroach_metric_export/gcp/main.tf +++ b/examples/workflows/cockroach_metric_export/gcp/main.tf @@ -70,3 +70,7 @@ resource "cockroach_metric_export_datadog_config" "example" { site = var.datadog_site api_key = var.datadog_api_key } + +resource "cockroach_metric_export_prometheus_config" "example" { + id = cockroach_cluster.example.id +} diff --git a/internal/provider/metric_export_prometheus_config_resource.go b/internal/provider/metric_export_prometheus_config_resource.go new file mode 100644 index 00000000..4b2e6b22 --- /dev/null +++ b/internal/provider/metric_export_prometheus_config_resource.go @@ -0,0 +1,391 @@ +package provider + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "strings" + "time" + + "github.com/cockroachdb/cockroach-cloud-sdk-go/pkg/client" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" +) + +const ( + metricExportEnableTimeout = time.Minute // This request just triggers an async job that rolls out the integration + metricExportDisableTimeout = time.Minute // Same as above. Different job, but the HTTP request behaves the same. + metricExportStableTimeout = 10 * time.Minute // This might take a while. Stability here means the integration is up and running +) + +var metricExportPrometheusConfigAttributes = map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Required: true, + MarkdownDescription: "Cluster ID.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + + "status": schema.StringAttribute{ + Computed: true, + Description: "The current state of the metric export configuration. Possible values are: " + fmt.Sprintf("%#q", client.AllowedMetricExportStatusTypeEnumValues), + }, + "targets": schema.MapAttribute{ + Computed: true, + ElementType: types.StringType, + Description: "Represents prometheus scrape endpoint for each region. You can fetch endpoints either by executing" + + " `terraform state show {resource}` or by enabling terraform logs.", + }, +} + +type metricExportPrometheusConfigResource struct { + provider *provider +} + +func (r *metricExportPrometheusConfigResource) Schema( + _ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse, +) { + resp.Schema = schema.Schema{ + Description: "Prometheus metric export configuration for a cluster. This is only available for dedicated clusters with AWS and GCP cloud providers.", + Attributes: metricExportPrometheusConfigAttributes, + } +} + +func (r *metricExportPrometheusConfigResource) Metadata( + _ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse, +) { + resp.TypeName = req.ProviderTypeName + "_metric_export_prometheus_config" +} + +func (r *metricExportPrometheusConfigResource) Configure( + _ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse, +) { + if req.ProviderData == nil { + return + } + var ok bool + if r.provider, ok = req.ProviderData.(*provider); !ok { + resp.Diagnostics.AddError("Internal provider error", + fmt.Sprintf("Error in Configuration: expected %T but got %T", provider{}, req.ProviderData)) + } +} + +func (r *metricExportPrometheusConfigResource) Create( + ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse, +) { + if r.provider == nil || !r.provider.configured { + addConfigureProviderErr(&resp.Diagnostics) + return + } + + var plan ClusterPrometheusMetricExportConfig + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + clusterID := plan.ID.ValueString() + // Check cluster + cluster, _, err := r.provider.service.GetCluster(ctx, clusterID) + if err != nil { + resp.Diagnostics.AddError( + "Error getting cluster", + fmt.Sprintf("Could not retrieve cluster info: %s", formatAPIErrorMessage(err)), + ) + return + } + + if cluster.Config.Serverless != nil { + resp.Diagnostics.AddError( + "Incompatible cluster type", + "Prometheus metric export services are only available for dedicated clusters with AWS and GCP cloud providers", + ) + return + } + + apiObj := &client.PrometheusMetricExportInfo{} + err = retry.RetryContext(ctx, metricExportEnableTimeout, retryEnablePrometheusMetricExport( + ctx, r.provider.service, clusterID, cluster, + apiObj, + )) + if err != nil { + resp.Diagnostics.AddError( + "Error enabling Prometheus metric export", err.Error(), + ) + return + } + + err = retry.RetryContext(ctx, metricExportStableTimeout, + waitForPrometheusMetricExportStableFunc(ctx, clusterID, r.provider.service, apiObj)) + if err != nil { + resp.Diagnostics.AddError( + "Error enabling Prometheus metric export", + fmt.Sprintf("Could not enable Prometheus metric export: %s", formatAPIErrorMessage(err)), + ) + return + } + + // print enable prometheus metric export as part of tflogs to fetch scrape endpoints. + // we are skipping error handling here as we are using it for logging & relying on standard libraries. + rawResponse, _ := json.MarshalIndent(apiObj, "", " ") + tflog.Info(ctx, fmt.Sprintf("enable prometheus metric export response: %s", string(rawResponse))) + + var state ClusterPrometheusMetricExportConfig + var targets types.Map + targets, diags = types.MapValueFrom(ctx, types.StringType, apiObj.GetTargets()) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + resp.Diagnostics.AddError( + "Error in parsing enable prometheus metric export", + fmt.Sprintf( + "There is an error in parsing enable prometheus metric export. response: %v", + apiObj, + )) + return + } + loadPrometheusMetricExportIntoTerraformState(types.StringValue(apiObj.GetClusterId()), types.StringValue(string(apiObj.GetStatus())), targets, &state) + diags = resp.State.Set(ctx, state) + resp.Diagnostics.Append(diags...) +} + +func retryEnablePrometheusMetricExport( + ctx context.Context, + cl client.Service, + clusterID string, + cluster *client.Cluster, + apiObj *client.PrometheusMetricExportInfo, +) retry.RetryFunc { + return func() *retry.RetryError { + body := &map[string]interface{}{} + apiResp, httpResp, err := cl.EnablePrometheusMetricExport(ctx, clusterID, body) + if err != nil { + apiErrMsg := formatAPIErrorMessage(err) + if (httpResp != nil && httpResp.StatusCode == http.StatusServiceUnavailable) || + strings.Contains(apiErrMsg, "lock") { + // Wait for cluster to be ready. + clusterErr := retry.RetryContext(ctx, clusterUpdateTimeout, + waitForClusterReadyFunc(ctx, clusterID, cl, cluster)) + if clusterErr != nil { + return retry.NonRetryableError( + fmt.Errorf("error checking cluster availability: %s", clusterErr.Error())) + } + return retry.RetryableError(fmt.Errorf("cluster was not ready - trying again")) + } + + return retry.NonRetryableError( + fmt.Errorf("could not enable Prometheus metric export: %v", apiErrMsg), + ) + } + *apiObj = *apiResp + return nil + } +} + +func loadPrometheusMetricExportIntoTerraformState( + ID types.String, + status types.String, + targets types.Map, + state *ClusterPrometheusMetricExportConfig, +) { + state.ID = ID + state.Status = status + state.Targets = targets +} + +func waitForPrometheusMetricExportStableFunc( + ctx context.Context, + clusterID string, + cl client.Service, + prometheusMetricExportInfo *client.PrometheusMetricExportInfo, +) retry.RetryFunc { + return func() *retry.RetryError { + apiObj, httpResp, err := cl.GetPrometheusMetricExportInfo(ctx, clusterID) + if err != nil { + if httpResp != nil && httpResp.StatusCode < http.StatusInternalServerError { + return retry.NonRetryableError(fmt.Errorf( + "error getting Prometheus metric export info: %s", formatAPIErrorMessage(err))) + } else { + // if this is a server error, we should retry + tflog.Error(ctx, "encountered a server error while refreshing Prometheus metric export status. Retrying...") + return retry.RetryableError(fmt.Errorf( + "encountered a server error while refreshing Prometheus metric export status", + )) + } + } + + *prometheusMetricExportInfo = *apiObj + switch prometheusMetricExportInfo.GetStatus() { + case client.METRICEXPORTSTATUSTYPE_ERROR: + errMsg := "an error occurred during Prometheus metric export config update" + if prometheusMetricExportInfo.GetUserMessage() != "" { + errMsg = fmt.Sprintf("%s: %s", errMsg, prometheusMetricExportInfo.GetUserMessage()) + } + return retry.NonRetryableError(fmt.Errorf(errMsg)) + case client.METRICEXPORTSTATUSTYPE_ENABLING, client.METRICEXPORTSTATUSTYPE_DISABLING: + return retry.RetryableError(fmt.Errorf("the Prometheus metric export is not ready yet")) + default: + return nil + } + } +} + +func (r *metricExportPrometheusConfigResource) Read( + ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse, +) { + if r.provider == nil || !r.provider.configured { + addConfigureProviderErr(&resp.Diagnostics) + return + } + + var state ClusterPrometheusMetricExportConfig + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() || state.ID.IsNull() { + return + } + + clusterID := state.ID.ValueString() + apiObj, httpResp, err := r.provider.service.GetPrometheusMetricExportInfo(ctx, clusterID) + if err != nil { + if httpResp != nil && httpResp.StatusCode == http.StatusNotFound { + resp.Diagnostics.AddWarning( + "Prometheus metric export config not found", + fmt.Sprintf( + "The Prometheus metric export config's cluster with ID %s is not found. Removing from state.", + clusterID, + )) + resp.State.RemoveResource(ctx) + return + } else { + resp.Diagnostics.AddError( + "Error getting Prometheus metric export info", + fmt.Sprintf("Unexpected error retrieving Prometheus metric export info: %s", formatAPIErrorMessage(err))) + return + + } + } + if apiObj.GetStatus() == client.METRICEXPORTSTATUSTYPE_NOT_DEPLOYED { + resp.Diagnostics.AddWarning( + "Prometheus metric export config not found", + fmt.Sprintf( + "The Prometheus metric export config's cluster with ID %s is not found. Removing from state.", + clusterID, + )) + resp.State.RemoveResource(ctx) + return + } + + var targets types.Map + targets, diags = types.MapValueFrom(ctx, types.StringType, apiObj.GetTargets()) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + resp.Diagnostics.AddError( + "Error in parsing get prometheus metric export info", + fmt.Sprintf( + "There is an error in parsing get prometheus metric export details. response: %v", + apiObj, + )) + return + } + loadPrometheusMetricExportIntoTerraformState(types.StringValue(apiObj.GetClusterId()), + types.StringValue(string(apiObj.GetStatus())), targets, &state) + diags = resp.State.Set(ctx, state) + resp.Diagnostics.Append(diags...) +} + +// Update resource is immutable so no action is necessary. +func (r *metricExportPrometheusConfigResource) Update( + ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse, +) { +} + +func (r *metricExportPrometheusConfigResource) Delete( + ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse, +) { + var state ClusterPrometheusMetricExportConfig + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + clusterID := state.ID.ValueString() + cluster := &client.Cluster{} + deletePromMetricExport := func() retry.RetryFunc { + return func() *retry.RetryError { + apiObj, httpResp, err := r.provider.service.DeletePrometheusMetricExport(ctx, clusterID) + if err != nil { + // This code block will handle below error scenarios and perform retries accordingly: + // 1. cluster does not exist. + // 2. control plane services are unavailable. + // 3. there is an issue in client in request preparation. + + apiErrMsg := formatAPIErrorMessage(err) + if httpResp != nil { + if httpResp.StatusCode == http.StatusNotFound { + // Prometheus metric export config or cluster is already gone. Swallow the error. + return nil + } + + if httpResp.StatusCode == http.StatusServiceUnavailable || + strings.Contains(apiErrMsg, "lock") { + // Wait for cluster to be ready. + clusterErr := retry.RetryContext(ctx, clusterUpdateTimeout, + waitForClusterReadyFunc(ctx, clusterID, r.provider.service, cluster)) + if clusterErr != nil { + return retry.NonRetryableError( + fmt.Errorf("Error checking cluster availability: %s", clusterErr.Error())) + } + return retry.RetryableError(fmt.Errorf("cluster was not ready - trying again")) + } + } + + return retry.NonRetryableError( + fmt.Errorf("could not delete Prometheus metric export config: %v", apiErrMsg)) + } + + if apiObj.GetStatus() == client.METRICEXPORTSTATUSTYPE_DISABLING { + // We are passing empty PrometheusMetricExportInfo object as we don't need it in stage management. + err = retry.RetryContext(ctx, metricExportStableTimeout, + waitForPrometheusMetricExportStableFunc(ctx, clusterID, r.provider.service, client.NewPrometheusMetricExportInfoWithDefaults())) + if err != nil { + resp.Diagnostics.AddError( + "Error in delete Prometheus metric export", + fmt.Sprintf("Could not delete Prometheus metric export: %s", formatAPIErrorMessage(err)), + ) + } + } + + return nil + } + } + + err := retry.RetryContext(ctx, metricExportDisableTimeout, deletePromMetricExport()) + if err != nil { + resp.Diagnostics.AddError( + "Error deleting Prometheus metric export config", err.Error(), + ) + return + } + + resp.State.RemoveResource(ctx) +} + +func (r *metricExportPrometheusConfigResource) ImportState( + ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse, +) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func NewMetricExportPrometheusConfigResource() resource.Resource { + return &metricExportPrometheusConfigResource{} +} diff --git a/internal/provider/metric_export_prometheus_config_resource_test.go b/internal/provider/metric_export_prometheus_config_resource_test.go new file mode 100644 index 00000000..c8d51a5e --- /dev/null +++ b/internal/provider/metric_export_prometheus_config_resource_test.go @@ -0,0 +1,211 @@ +package provider + +import ( + "context" + "fmt" + "net/http" + "os" + "testing" + + "github.com/cockroachdb/cockroach-cloud-sdk-go/pkg/client" + mock_client "github.com/cockroachdb/terraform-provider-cockroach/mock" + "github.com/golang/mock/gomock" + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" +) + +func TestAccMetricExportPrometheusConfigResource(t *testing.T) { + t.Parallel() + clusterName := fmt.Sprintf("%s-prometheus-%s", tfTestPrefix, GenerateRandomString(4)) + testMetricExportPrometheusConfigResource(t, clusterName, false) +} + +// TestIntegrationMetricExportPrometheusConfigResource attempts to create, check, +// and destroy a cluster, but uses a mocked API service. +func TestIntegrationMetricExportPrometheusConfigResource(t *testing.T) { + clusterName := fmt.Sprintf("%s-prometheus-%s", tfTestPrefix, GenerateRandomString(4)) + clusterID := uuid.Nil.String() + if os.Getenv(CockroachAPIKey) == "" { + os.Setenv(CockroachAPIKey, "fake") + } + + ctrl := gomock.NewController(t) + s := mock_client.NewMockService(ctrl) + defer HookGlobal(&NewService, func(c *client.Client) client.Service { + return s + })() + + cluster := &client.Cluster{ + Id: clusterID, + Name: clusterName, + CockroachVersion: "v22.2.0", + Plan: "DEDICATED", + CloudProvider: "GCP", + State: "CREATED", + Config: client.ClusterConfig{ + Dedicated: &client.DedicatedHardwareConfig{ + MachineType: "m5.xlarge", + NumVirtualCpus: 4, + StorageGib: 35, + MemoryGib: 8, + }, + }, + Regions: []client.Region{ + { + Name: "us-east-1", + NodeCount: 3, + }, + }, + } + + enabledStatus := client.METRICEXPORTSTATUSTYPE_ENABLED + + createdPrometheusClusterInfo := &client.PrometheusMetricExportInfo{ + ClusterId: clusterID, + Status: &enabledStatus, + } + + updatedPrometheusClusterInfo := &client.PrometheusMetricExportInfo{ + ClusterId: clusterID, + Status: &enabledStatus, + } + + // Create + s.EXPECT().CreateCluster(gomock.Any(), gomock.Any()). + Return(cluster, nil, nil) + s.EXPECT().GetCluster(gomock.Any(), clusterID). + Return(cluster, &http.Response{Status: http.StatusText(http.StatusOK)}, nil). + Times(3) + s.EXPECT().EnablePrometheusMetricExport(gomock.Any(), clusterID, gomock.Any()). + Return(createdPrometheusClusterInfo, nil, nil) + s.EXPECT().GetPrometheusMetricExportInfo(gomock.Any(), clusterID). + Return(createdPrometheusClusterInfo, nil, nil). + Times(3) + + // Update + s.EXPECT().GetCluster(gomock.Any(), clusterID). + Return(cluster, nil, nil). + Times(2) + s.EXPECT().GetPrometheusMetricExportInfo(gomock.Any(), clusterID). + Return(createdPrometheusClusterInfo, nil, nil) + // It should not invoke EnablePrometheusMetricExport as request contains clusterID + // which will not get changed. + s.EXPECT().EnablePrometheusMetricExport(gomock.Any(), clusterID, gomock.Any()). + Return(updatedPrometheusClusterInfo, nil, nil).Times(0) + s.EXPECT().GetPrometheusMetricExportInfo(gomock.Any(), clusterID). + Return(updatedPrometheusClusterInfo, nil, nil). + Times(3) + + // Delete + s.EXPECT().DeleteCluster(gomock.Any(), clusterID) + s.EXPECT().DeletePrometheusMetricExport(gomock.Any(), clusterID) + + testMetricExportPrometheusConfigResource(t, clusterName, true) +} + +func testMetricExportPrometheusConfigResource(t *testing.T, clusterName string, useMock bool) { + var ( + clusterResourceName = "cockroach_cluster.test" + metricExportPrometheusConfigResourceName = "cockroach_metric_export_prometheus_config.test" + ) + + resource.Test(t, resource.TestCase{ + IsUnitTest: useMock, + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: getTestMetricExportPrometheusConfigResourceCreateConfig(clusterName), + Check: resource.ComposeTestCheckFunc( + testMetricExportPrometheusConfigExists(metricExportPrometheusConfigResourceName, clusterResourceName), + resource.TestCheckResourceAttr(metricExportPrometheusConfigResourceName, "status", "ENABLED"), + ), + }, + { + Config: getTestMetricExportPrometheusConfigResourceUpdateConfig(clusterName), + Check: resource.ComposeTestCheckFunc( + testMetricExportPrometheusConfigExists(metricExportPrometheusConfigResourceName, clusterResourceName), + resource.TestCheckResourceAttr(metricExportPrometheusConfigResourceName, "status", "ENABLED"), + ), + }, + { + ResourceName: metricExportPrometheusConfigResourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testMetricExportPrometheusConfigExists( + resourceName, clusterResourceName string, +) resource.TestCheckFunc { + return func(s *terraform.State) error { + p := testAccProvider.(*provider) + p.service = NewService(cl) + + _, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("not found: %s", resourceName) + } + clusterRs, ok := s.RootModule().Resources[clusterResourceName] + if !ok { + return fmt.Errorf("not found: %s", clusterResourceName) + } + + clusterID := clusterRs.Primary.Attributes["id"] + config, _, err := p.service.GetPrometheusMetricExportInfo(context.TODO(), clusterID) + if err != nil { + return fmt.Errorf("metric export Prometheus config does not exist") + } + + if config.GetStatus() != client.METRICEXPORTSTATUSTYPE_ENABLED { + return fmt.Errorf("metric export Prometheus config is not enabled") + } + + return nil + } +} + +func getTestMetricExportPrometheusConfigResourceCreateConfig(name string) string { + return fmt.Sprintf(` +resource "cockroach_cluster" "test" { + name = "%s" + cloud_provider = "GCP" + dedicated = { + storage_gib = 35 + num_virtual_cpus = 4 + } + regions = [{ + name = "us-east-1" + node_count: 3 + }] +} + +resource "cockroach_metric_export_prometheus_config" "test" { + id = cockroach_cluster.test.id + } +`, name) +} + +func getTestMetricExportPrometheusConfigResourceUpdateConfig(name string) string { + return fmt.Sprintf(` +resource "cockroach_cluster" "test" { + name = "%s" + cloud_provider = "GCP" + dedicated = { + storage_gib = 35 + num_virtual_cpus = 4 + } + regions = [{ + name = "us-east-1" + node_count: 3 + }] +} + +resource "cockroach_metric_export_prometheus_config" "test" { + id = cockroach_cluster.test.id + } +`, name) +} diff --git a/internal/provider/models.go b/internal/provider/models.go index fe31e7bb..199e92a6 100644 --- a/internal/provider/models.go +++ b/internal/provider/models.go @@ -249,6 +249,12 @@ type ClusterDatadogMetricExportConfig struct { Status types.String `tfsdk:"status"` } +type ClusterPrometheusMetricExportConfig struct { + ID types.String `tfsdk:"id"` + Status types.String `tfsdk:"status"` + Targets types.Map `tfsdk:"targets"` +} + type ClusterMaintenanceWindow struct { ID types.String `tfsdk:"id"` OffsetDuration types.Int64 `tfsdk:"offset_duration"` diff --git a/internal/provider/provider.go b/internal/provider/provider.go index e182ce8b..184814bf 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -137,6 +137,7 @@ func (p *provider) Resources(_ context.Context) []func() resource.Resource { NewLogExportConfigResource, NewMetricExportDatadogConfigResource, NewMetricExportCloudWatchConfigResource, + NewMetricExportPrometheusConfigResource, NewClientCACertResource, NewUserRoleGrantsResource, NewUserRoleGrantResource,