diff --git a/docs/data-sources/observability_cert_check.md b/docs/data-sources/observability_cert_check.md new file mode 100644 index 000000000..883e336a8 --- /dev/null +++ b/docs/data-sources/observability_cert_check.md @@ -0,0 +1,35 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "stackit_observability_cert_check Data Source - stackit" +subcategory: "" +description: |- + Datasource for managing cert-checks in STACKIT Observability. +--- + +# stackit_observability_cert_check (Data Source) + +Datasource for managing cert-checks in STACKIT Observability. + +## Example Usage + +```terraform +data "stackit_observability_cert_check" "example" { + project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + cert_check_id = "xxxxxxxxxxxxxxxxxxxxxxxx" +} +``` + + +## Schema + +### Required + +- `cert_check_id` (String) Unique ID of the cert-check. +- `instance_id` (String) STACKIT Observability instance ID. +- `project_id` (String) STACKIT project ID. + +### Read-Only + +- `id` (String) Terraform resource ID in format `project_id,instance_id,cert_check_id`. +- `source` (String) The cert source to check, e.g. tcp://stackit.de:443 Must start with `tcp://`. diff --git a/docs/data-sources/observability_http_check.md b/docs/data-sources/observability_http_check.md new file mode 100644 index 000000000..6da5c9a4e --- /dev/null +++ b/docs/data-sources/observability_http_check.md @@ -0,0 +1,35 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "stackit_observability_http_check Data Source - stackit" +subcategory: "" +description: |- + Datasource for managing HTTP-checks in STACKIT Observability. +--- + +# stackit_observability_http_check (Data Source) + +Datasource for managing HTTP-checks in STACKIT Observability. + +## Example Usage + +```terraform +data "stackit_observability_http_check" "example" { + project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + http_check_id = "xxxxxxxxxxxxxxxxxxxxxxxx" +} +``` + + +## Schema + +### Required + +- `http_check_id` (String) Unique ID of the HTTP-check. +- `instance_id` (String) STACKIT Observability instance ID. +- `project_id` (String) STACKIT project ID. + +### Read-Only + +- `id` (String) Terraform resource ID in format `project_id,instance_id,http_check_id`. +- `url` (String) The URL to check, e.g. https://www.stackit.de. Must start with `http://` or `https://`. diff --git a/docs/resources/observability_cert_check.md b/docs/resources/observability_cert_check.md new file mode 100644 index 000000000..2cda8a56a --- /dev/null +++ b/docs/resources/observability_cert_check.md @@ -0,0 +1,41 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "stackit_observability_cert_check Resource - stackit" +subcategory: "" +description: |- + Resource for managing cert-checks in STACKIT Observability. +--- + +# stackit_observability_cert_check (Resource) + +Resource for managing cert-checks in STACKIT Observability. + +## Example Usage + +```terraform +resource "stackit_observability_http_check" "example" { + project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + url = "tcp://stackit.de:443" +} + +# Only use the import statement, if you want to import an existing cert-check +import { + to = stackit_observability_http_check.example + id = "${var.project_id},${var.observability_instance_id},${var.cert_check_id}" +} +``` + + +## Schema + +### Required + +- `instance_id` (String) STACKIT Observability instance ID. +- `project_id` (String) STACKIT project ID. +- `source` (String) The cert source to check, e.g. tcp://stackit.de:443 Must start with `tcp://`. + +### Read-Only + +- `cert_check_id` (String) Unique ID of the cert-check. +- `id` (String) Terraform resource ID in format `project_id,instance_id,cert_check_id`. diff --git a/docs/resources/observability_http_check.md b/docs/resources/observability_http_check.md new file mode 100644 index 000000000..352b5f70f --- /dev/null +++ b/docs/resources/observability_http_check.md @@ -0,0 +1,41 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "stackit_observability_http_check Resource - stackit" +subcategory: "" +description: |- + Resource for managing HTTP-checks in STACKIT Observability. +--- + +# stackit_observability_http_check (Resource) + +Resource for managing HTTP-checks in STACKIT Observability. + +## Example Usage + +```terraform +resource "stackit_observability_http_check" "example" { + project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + url = "https://www.stackit.de" +} + +# Only use the import statement, if you want to import an existing http-check +import { + to = stackit_observability_http_check.example + id = "${var.project_id},${var.observability_instance_id},${var.http_check_id}" +} +``` + + +## Schema + +### Required + +- `instance_id` (String) STACKIT Observability instance ID. +- `project_id` (String) STACKIT project ID. +- `url` (String) The URL to check, e.g. https://www.stackit.de. Must start with `http://` or `https://`. + +### Read-Only + +- `http_check_id` (String) Unique ID of the HTTP-check. +- `id` (String) Terraform resource ID in format `project_id,instance_id,http_check_id`. diff --git a/examples/data-sources/stackit_observability_cert_check/data-source.tf b/examples/data-sources/stackit_observability_cert_check/data-source.tf new file mode 100644 index 000000000..6939439f2 --- /dev/null +++ b/examples/data-sources/stackit_observability_cert_check/data-source.tf @@ -0,0 +1,5 @@ +data "stackit_observability_cert_check" "example" { + project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + cert_check_id = "xxxxxxxxxxxxxxxxxxxxxxxx" +} \ No newline at end of file diff --git a/examples/data-sources/stackit_observability_http_check/data-source.tf b/examples/data-sources/stackit_observability_http_check/data-source.tf new file mode 100644 index 000000000..22e273cba --- /dev/null +++ b/examples/data-sources/stackit_observability_http_check/data-source.tf @@ -0,0 +1,5 @@ +data "stackit_observability_http_check" "example" { + project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + http_check_id = "xxxxxxxxxxxxxxxxxxxxxxxx" +} \ No newline at end of file diff --git a/examples/resources/stackit_observability_cert_check/resource.tf b/examples/resources/stackit_observability_cert_check/resource.tf new file mode 100644 index 000000000..f6defe6a9 --- /dev/null +++ b/examples/resources/stackit_observability_cert_check/resource.tf @@ -0,0 +1,11 @@ +resource "stackit_observability_http_check" "example" { + project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + url = "tcp://stackit.de:443" +} + +# Only use the import statement, if you want to import an existing cert-check +import { + to = stackit_observability_http_check.example + id = "${var.project_id},${var.observability_instance_id},${var.cert_check_id}" +} diff --git a/examples/resources/stackit_observability_http_check/resource.tf b/examples/resources/stackit_observability_http_check/resource.tf new file mode 100644 index 000000000..2d7fb4299 --- /dev/null +++ b/examples/resources/stackit_observability_http_check/resource.tf @@ -0,0 +1,11 @@ +resource "stackit_observability_http_check" "example" { + project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + url = "https://www.stackit.de" +} + +# Only use the import statement, if you want to import an existing http-check +import { + to = stackit_observability_http_check.example + id = "${var.project_id},${var.observability_instance_id},${var.http_check_id}" +} diff --git a/stackit/internal/services/observability/cert-check/datasource.go b/stackit/internal/services/observability/cert-check/datasource.go new file mode 100644 index 000000000..1250f57c4 --- /dev/null +++ b/stackit/internal/services/observability/cert-check/datasource.go @@ -0,0 +1,139 @@ +package certcheck + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/stackitcloud/stackit-sdk-go/services/observability" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features" + observabilityUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/observability/utils" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate" +) + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ datasource.DataSource = &certCheckDataSource{} +) + +// NewCertCheckDataSource creates a new instance of the certCheckDataSource. +func NewCertCheckDataSource() datasource.DataSource { + return &certCheckDataSource{} +} + +// certCheckDataSource is the datasource implementation. +type certCheckDataSource struct { + client *observability.APIClient +} + +// Configure adds the provider configured client to the resource. +func (d *certCheckDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + providerData, ok := conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics) + if !ok { + return + } + + features.CheckBetaResourcesEnabled(ctx, &providerData, &resp.Diagnostics, "stackit_observability_cert_check", "datasource") + if resp.Diagnostics.HasError() { + return + } + + apiClient := observabilityUtils.ConfigureClient(ctx, &providerData, &resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + d.client = apiClient + tflog.Info(ctx, "Observability client configured") +} + +// Metadata provides metadata for the alert group datasource. +func (d *certCheckDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_observability_cert_check" +} + +// Schema defines the schema for the alert group data source. +func (d *certCheckDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Datasource for managing cert-checks in STACKIT Observability.", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Description: descriptions["id"], + Computed: true, + }, + "project_id": schema.StringAttribute{ + Description: descriptions["project_id"], + Required: true, + Validators: []validator.String{ + validate.UUID(), + validate.NoSeparator(), + }, + }, + "instance_id": schema.StringAttribute{ + Description: descriptions["instance_id"], + Required: true, + Validators: []validator.String{ + validate.UUID(), + validate.NoSeparator(), + }, + }, + "cert_check_id": schema.StringAttribute{ + Description: descriptions["cert_check_id"], + Required: true, + }, + "source": schema.StringAttribute{ + Description: descriptions["source"], + Computed: true, + }, + }, + } +} + +func (d *certCheckDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform + var model Model + diags := req.Config.Get(ctx, &model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + projectId := model.ProjectId.ValueString() + instanceId := model.InstanceId.ValueString() + certCheckId := model.CertCheckId.ValueString() + ctx = tflog.SetField(ctx, "project_id", projectId) + ctx = tflog.SetField(ctx, "instance_id", instanceId) + ctx = tflog.SetField(ctx, "cert_check_id", certCheckId) + + listCertChecks, err := d.client.ListCertChecks(ctx, instanceId, projectId).Execute() + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error listing cert-checks", fmt.Sprintf("Listing API payload: %v", err)) + return + } + + if listCertChecks.CertChecks == nil || len(*listCertChecks.CertChecks) == 0 { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error listing cert-checks", "Response is empty") + return + } + + for _, certCheck := range *listCertChecks.CertChecks { + if certCheck.Id != nil && *certCheck.Id == certCheckId { + if err := mapFields(ctx, &certCheck, &model); err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading cert-check", "Unable to map cert-check model") + return + } + break + } + } + + // Set the state with fully populated data. + diags = resp.State.Set(ctx, model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + tflog.Info(ctx, "cert-check read") +} diff --git a/stackit/internal/services/observability/cert-check/resource.go b/stackit/internal/services/observability/cert-check/resource.go new file mode 100644 index 000000000..e383a4ee0 --- /dev/null +++ b/stackit/internal/services/observability/cert-check/resource.go @@ -0,0 +1,320 @@ +package certcheck + +import ( + "context" + "fmt" + "regexp" + "strings" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "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/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/stackitcloud/stackit-sdk-go/services/observability" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features" + observabilityUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/observability/utils" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate" +) + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ resource.Resource = &observabilityCertCheckResource{} + _ resource.ResourceWithConfigure = &observabilityCertCheckResource{} + _ resource.ResourceWithImportState = &observabilityCertCheckResource{} +) + +type Model struct { + Id types.String `tfsdk:"id"` + ProjectId types.String `tfsdk:"project_id"` + InstanceId types.String `tfsdk:"instance_id"` + CertCheckId types.String `tfsdk:"cert_check_id"` + Source types.String `tfsdk:"source"` +} + +var descriptions = map[string]string{ + "id": "Terraform resource ID in format `project_id,instance_id,cert_check_id`.", + "project_id": "STACKIT project ID.", + "instance_id": "STACKIT Observability instance ID.", + "cert_check_id": "Unique ID of the cert-check.", + "source": "The cert source to check, e.g. tcp://stackit.de:443 Must start with `tcp://`.", +} + +// NewCertCheckResource is a helper function to simplify the provider implementation. +func NewCertCheckResource() resource.Resource { + return &observabilityCertCheckResource{} +} + +// observabilityCertCheckResource is the resource implementation. +type observabilityCertCheckResource struct { + client *observability.APIClient +} + +// Metadata returns the resource type name. +func (r *observabilityCertCheckResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_observability_cert_check" +} + +// Configure adds the provider configured client to the resource. +func (r *observabilityCertCheckResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + providerData, ok := conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics) + if !ok { + return + } + + features.CheckBetaResourcesEnabled(ctx, &providerData, &resp.Diagnostics, "stackit_observability_cert_check", "resource") + if resp.Diagnostics.HasError() { + return + } + + apiClient := observabilityUtils.ConfigureClient(ctx, &providerData, &resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + r.client = apiClient + tflog.Info(ctx, "Observability client configured") +} + +// Schema defines the schema for the resource. +func (r *observabilityCertCheckResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Resource for managing cert-checks in STACKIT Observability.", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Description: descriptions["id"], + Computed: true, + }, + "project_id": schema.StringAttribute{ + Description: descriptions["project_id"], + Required: true, + Validators: []validator.String{ + validate.UUID(), + validate.NoSeparator(), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "instance_id": schema.StringAttribute{ + Description: descriptions["instance_id"], + Required: true, + Validators: []validator.String{ + validate.UUID(), + validate.NoSeparator(), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "cert_check_id": schema.StringAttribute{ + Description: descriptions["cert_check_id"], + Computed: true, + }, + "source": schema.StringAttribute{ + Description: descriptions["source"], + Required: true, + Validators: []validator.String{ + stringvalidator.LengthAtLeast(5), + stringvalidator.RegexMatches( + regexp.MustCompile(`^tcp://`), + "The source must start with tcp://.", + ), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + }, + } +} + +// Create creates the resource and sets the initial Terraform state. +func (r *observabilityCertCheckResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform + // Retrieve values from plan + var model Model + diags := req.Plan.Get(ctx, &model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + projectId := model.ProjectId.ValueString() + instanceId := model.InstanceId.ValueString() + certCheckSource := model.Source.ValueString() + ctx = tflog.SetField(ctx, "project_id", projectId) + ctx = tflog.SetField(ctx, "cert_check_source", certCheckSource) + ctx = tflog.SetField(ctx, "instance_id", instanceId) + + createCertCheck, err := r.client.CreateCertCheck( + ctx, + instanceId, + projectId, + ).CreateCertCheckPayload(observability.CreateCertCheckPayload{Source: &certCheckSource}).Execute() + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating cert-check", fmt.Sprintf("Creating API payload: %v", err)) + return + } + + // Log success message from API + if createCertCheck.Message != nil { + tflog.Info(ctx, fmt.Sprintf("Create cert-check response message: %s", *createCertCheck.Message)) + } + + if createCertCheck.CertChecks == nil || len(*createCertCheck.CertChecks) == 0 { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating cert-check", "Response is empty") + return + } + + for _, certCheck := range *createCertCheck.CertChecks { + if certCheck.Source != nil && *certCheck.Source == certCheckSource { + if err := mapFields(ctx, &certCheck, &model); err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating cert-check", "Unable to map cert-check model") + return + } + break + } + } + + // Set the state with fully populated data. + diags = resp.State.Set(ctx, model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + tflog.Info(ctx, "cert-check created") +} + +// Read refreshes the Terraform state with the latest data. +func (r *observabilityCertCheckResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform + // Retrieve values from state + var model Model + diags := req.State.Get(ctx, &model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + projectId := model.ProjectId.ValueString() + instanceId := model.InstanceId.ValueString() + certCheckSource := model.Source.ValueString() + certCheckId := model.CertCheckId.ValueString() + ctx = tflog.SetField(ctx, "project_id", projectId) + ctx = tflog.SetField(ctx, "instance_id", instanceId) + ctx = tflog.SetField(ctx, "cert_check_source", certCheckSource) + ctx = tflog.SetField(ctx, "cert_check_id", certCheckId) + + listCertCheck, err := r.client.ListCertChecks(ctx, instanceId, projectId).Execute() + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error listing cert-checks", fmt.Sprintf("List API payload: %v", err)) + return + } + + if listCertCheck.CertChecks == nil || len(*listCertCheck.CertChecks) == 0 { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error listing cert-checks", "Response is empty") + return + } + + for _, certCheck := range *listCertCheck.CertChecks { + // we also check if cert-ids are matching to support import functionality + if (certCheck.Source != nil && *certCheck.Source == certCheckSource) || (certCheck.Id != nil && *certCheck.Id == certCheckId) { + if err := mapFields(ctx, &certCheck, &model); err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading cert-check", "Unable to map cert-check model") + return + } + break + } + } + + // Set the state with fully populated data. + diags = resp.State.Set(ctx, model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + tflog.Info(ctx, "cert-check read") +} + +// Update attempts to update the resource. In this case, cert-checks cannot be updated. +// The Update function is redundant since any modifications will +// automatically trigger a resource recreation through Terraform's built-in +// lifecycle management. +func (r *observabilityCertCheckResource) Update(ctx context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform + core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating cert-check", "Observability cert-checks can't be updated") +} + +// Delete deletes the resource and removes the Terraform state on success. +func (r *observabilityCertCheckResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform + // Retrieve values from state + var model Model + diags := req.State.Get(ctx, &model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + projectId := model.ProjectId.ValueString() + instanceId := model.InstanceId.ValueString() + certCheckId := model.CertCheckId.ValueString() + ctx = tflog.SetField(ctx, "project_id", projectId) + ctx = tflog.SetField(ctx, "cert_check_id", certCheckId) + ctx = tflog.SetField(ctx, "instance_id", instanceId) + + _, err := r.client.DeleteCertCheck(ctx, instanceId, projectId, certCheckId).Execute() + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting cert-check", fmt.Sprintf("Creating API payload: %v", err)) + return + } + + tflog.Info(ctx, "cert check deleted") +} + +// ImportState imports a resource into the Terraform state on success. +// The expected format of the resource import identifier is: project_id,instance_id,cert_check_id +func (r *observabilityCertCheckResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + idParts := strings.Split(req.ID, core.Separator) + + if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" { + core.LogAndAddError(ctx, &resp.Diagnostics, + "Error importing cert-check", + fmt.Sprintf("Expected import identifier with format: [project_id],[instance_id],[cert_check_id] Got: %q", req.ID), + ) + return + } + + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[1])...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("cert_check_id"), idParts[2])...) + tflog.Info(ctx, "Observability cert-check state imported") +} + +// mapFields maps certCheck response to the model. +func mapFields(_ context.Context, certCheck *observability.CertCheckChildResponse, model *Model) error { + if certCheck == nil { + return fmt.Errorf("cert-check input is nil") + } + + if model == nil { + return fmt.Errorf("model input is nil") + } + + if certCheck.Id == nil { + return fmt.Errorf("cert-check id is nil") + } + + if certCheck.Source == nil { + return fmt.Errorf("cert-check source is nil") + } + + model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), model.InstanceId.ValueString(), *certCheck.Id) + model.CertCheckId = types.StringValue(*certCheck.Id) + model.Source = types.StringValue(*certCheck.Source) + + return nil +} diff --git a/stackit/internal/services/observability/cert-check/resource_test.go b/stackit/internal/services/observability/cert-check/resource_test.go new file mode 100644 index 000000000..6b1fc0f41 --- /dev/null +++ b/stackit/internal/services/observability/cert-check/resource_test.go @@ -0,0 +1,74 @@ +package certcheck + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/stackitcloud/stackit-sdk-go/core/utils" + "github.com/stackitcloud/stackit-sdk-go/services/observability" +) + +func TestMapFields(t *testing.T) { + tests := []struct { + name string + certCheck *observability.CertCheckChildResponse + model *Model + expectedId string + expectedCertCheckId string + expectedSource string + expectErr bool + }{ + { + name: "Nil CertCheck", + certCheck: nil, + model: &Model{}, + expectErr: true, + }, + { + name: "Nil Model", + certCheck: &observability.CertCheckChildResponse{}, + model: nil, + expectErr: true, + }, + { + name: "Complete Model and CertCheck", + certCheck: &observability.CertCheckChildResponse{ + Id: utils.Ptr("cert-check-id"), + Source: utils.Ptr("cert-check-source"), + }, + model: &Model{ + ProjectId: types.StringValue("project1"), + InstanceId: types.StringValue("instance1"), + }, + expectedId: "project1,instance1,cert-check-id", + expectedCertCheckId: "cert-check-id", + expectedSource: "cert-check-source", + expectErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + err := mapFields(ctx, tt.certCheck, tt.model) + + if (err != nil) != tt.expectErr { + t.Fatalf("expected error: %v, got: %v", tt.expectErr, err) + } + + if !tt.expectErr { + if diff := cmp.Diff(tt.model.Id.ValueString(), tt.expectedId); diff != "" { + t.Errorf("unexpected ID (-got +want):\n%s", diff) + } + if diff := cmp.Diff(tt.model.CertCheckId.ValueString(), tt.expectedCertCheckId); diff != "" { + t.Errorf("unexpected CertCheckId (-got +want):\n%s", diff) + } + if diff := cmp.Diff(tt.model.Source.ValueString(), tt.expectedSource); diff != "" { + t.Errorf("unexpected Source (-got +want):\n%s", diff) + } + } + }) + } +} diff --git a/stackit/internal/services/observability/http-check/datasource.go b/stackit/internal/services/observability/http-check/datasource.go new file mode 100644 index 000000000..67dacbeee --- /dev/null +++ b/stackit/internal/services/observability/http-check/datasource.go @@ -0,0 +1,139 @@ +package httpcheck + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/stackitcloud/stackit-sdk-go/services/observability" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features" + observabilityUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/observability/utils" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate" +) + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ datasource.DataSource = &httpCheckDataSource{} +) + +// NewHttpCheckDataSource creates a new instance of the httpCheckDataSource. +func NewHttpCheckDataSource() datasource.DataSource { + return &httpCheckDataSource{} +} + +// httpCheckDataSource is the datasource implementation. +type httpCheckDataSource struct { + client *observability.APIClient +} + +// Configure adds the provider configured client to the resource. +func (d *httpCheckDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + providerData, ok := conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics) + if !ok { + return + } + + features.CheckBetaResourcesEnabled(ctx, &providerData, &resp.Diagnostics, "stackit_observability_http_check", "datasource") + if resp.Diagnostics.HasError() { + return + } + + apiClient := observabilityUtils.ConfigureClient(ctx, &providerData, &resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + d.client = apiClient + tflog.Info(ctx, "Observability client configured") +} + +// Metadata provides metadata for the alert group datasource. +func (d *httpCheckDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_observability_http_check" +} + +// Schema defines the schema for the alert group data source. +func (d *httpCheckDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Datasource for managing HTTP-checks in STACKIT Observability.", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Description: descriptions["id"], + Computed: true, + }, + "project_id": schema.StringAttribute{ + Description: descriptions["project_id"], + Required: true, + Validators: []validator.String{ + validate.UUID(), + validate.NoSeparator(), + }, + }, + "instance_id": schema.StringAttribute{ + Description: descriptions["instance_id"], + Required: true, + Validators: []validator.String{ + validate.UUID(), + validate.NoSeparator(), + }, + }, + "http_check_id": schema.StringAttribute{ + Description: descriptions["http_check_id"], + Required: true, + }, + "url": schema.StringAttribute{ + Description: descriptions["url"], + Computed: true, + }, + }, + } +} + +func (d *httpCheckDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform + var model Model + diags := req.Config.Get(ctx, &model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + projectId := model.ProjectId.ValueString() + instanceId := model.InstanceId.ValueString() + httpCheckId := model.HttpCheckId.ValueString() + ctx = tflog.SetField(ctx, "project_id", projectId) + ctx = tflog.SetField(ctx, "instance_id", instanceId) + ctx = tflog.SetField(ctx, "http_check_id", httpCheckId) + + listHttpCheck, err := d.client.ListHttpChecks(ctx, instanceId, projectId).Execute() + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error listing http-checks", fmt.Sprintf("Listing API payload: %v", err)) + return + } + + if listHttpCheck.HttpChecks == nil || len(*listHttpCheck.HttpChecks) == 0 { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error listing http-checks", "Response is empty") + return + } + + for _, httpCheck := range *listHttpCheck.HttpChecks { + if httpCheck.Id != nil && *httpCheck.Id == httpCheckId { + if err := mapFields(ctx, &httpCheck, &model); err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading http-check", "Unable to map http-check model") + return + } + break + } + } + + // Set the state with fully populated data. + diags = resp.State.Set(ctx, model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + tflog.Info(ctx, "http-check read") +} diff --git a/stackit/internal/services/observability/http-check/resource.go b/stackit/internal/services/observability/http-check/resource.go new file mode 100644 index 000000000..da57b3ba7 --- /dev/null +++ b/stackit/internal/services/observability/http-check/resource.go @@ -0,0 +1,321 @@ +package httpcheck + +import ( + "context" + "fmt" + "regexp" + "strings" + + observabilityUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/observability/utils" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "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/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/stackitcloud/stackit-sdk-go/services/observability" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate" +) + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ resource.Resource = &observabilityHttpCheckResource{} + _ resource.ResourceWithConfigure = &observabilityHttpCheckResource{} + _ resource.ResourceWithImportState = &observabilityHttpCheckResource{} +) + +type Model struct { + Id types.String `tfsdk:"id"` + ProjectId types.String `tfsdk:"project_id"` + InstanceId types.String `tfsdk:"instance_id"` + HttpCheckId types.String `tfsdk:"http_check_id"` + Url types.String `tfsdk:"url"` +} + +var descriptions = map[string]string{ + "id": "Terraform resource ID in format `project_id,instance_id,http_check_id`.", + "project_id": "STACKIT project ID.", + "instance_id": "STACKIT Observability instance ID.", + "http_check_id": "Unique ID of the HTTP-check.", + "url": "The URL to check, e.g. https://www.stackit.de. Must start with `http://` or `https://`.", +} + +// NewHttpCheckResource is a helper function to simplify the provider implementation. +func NewHttpCheckResource() resource.Resource { + return &observabilityHttpCheckResource{} +} + +// observabilityHttpCheckResource is the resource implementation. +type observabilityHttpCheckResource struct { + client *observability.APIClient +} + +// Metadata returns the resource type name. +func (r *observabilityHttpCheckResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_observability_http_check" +} + +// Configure adds the provider configured client to the resource. +func (r *observabilityHttpCheckResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + providerData, ok := conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics) + if !ok { + return + } + + features.CheckBetaResourcesEnabled(ctx, &providerData, &resp.Diagnostics, "stackit_observability_http_check", "resource") + if resp.Diagnostics.HasError() { + return + } + + apiClient := observabilityUtils.ConfigureClient(ctx, &providerData, &resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + r.client = apiClient + tflog.Info(ctx, "Observability client configured") +} + +// Schema defines the schema for the resource. +func (r *observabilityHttpCheckResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Resource for managing HTTP-checks in STACKIT Observability.", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Description: descriptions["id"], + Computed: true, + }, + "project_id": schema.StringAttribute{ + Description: descriptions["project_id"], + Required: true, + Validators: []validator.String{ + validate.UUID(), + validate.NoSeparator(), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "instance_id": schema.StringAttribute{ + Description: descriptions["instance_id"], + Required: true, + Validators: []validator.String{ + validate.UUID(), + validate.NoSeparator(), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "http_check_id": schema.StringAttribute{ + Description: descriptions["http_check_id"], + Computed: true, + }, + "url": schema.StringAttribute{ + Description: descriptions["url"], + Required: true, + Validators: []validator.String{ + stringvalidator.LengthAtLeast(5), + stringvalidator.RegexMatches( + regexp.MustCompile(`^https?://`), + "The URL must start with http:// or https://.", + ), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + }, + } +} + +// Create creates the resource and sets the initial Terraform state. +func (r *observabilityHttpCheckResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform + // Retrieve values from plan + var model Model + diags := req.Plan.Get(ctx, &model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + projectId := model.ProjectId.ValueString() + instanceId := model.InstanceId.ValueString() + httpCheckUrl := model.Url.ValueString() + ctx = tflog.SetField(ctx, "project_id", projectId) + ctx = tflog.SetField(ctx, "http_check_url", httpCheckUrl) + ctx = tflog.SetField(ctx, "instance_id", instanceId) + + createHttpCheck, err := r.client.CreateHttpCheck( + ctx, + instanceId, + projectId, + ).CreateHttpCheckPayload(observability.CreateHttpCheckPayload{Url: &httpCheckUrl}).Execute() + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating http-check", fmt.Sprintf("Creating API payload: %v", err)) + return + } + + // Log success message from API + if createHttpCheck.Message != nil { + tflog.Info(ctx, fmt.Sprintf("Create http-check response message: %s", *createHttpCheck.Message)) + } + + if createHttpCheck.HttpChecks == nil || len(*createHttpCheck.HttpChecks) == 0 { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating http-check", "Response is empty") + return + } + + for _, httpCheck := range *createHttpCheck.HttpChecks { + if httpCheck.Url != nil && *httpCheck.Url == httpCheckUrl { + if err := mapFields(ctx, &httpCheck, &model); err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating http-check", "Unable to map http-check model") + return + } + break + } + } + + // Set the state with fully populated data. + diags = resp.State.Set(ctx, model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + tflog.Info(ctx, "http-check created") +} + +// Read refreshes the Terraform state with the latest data. +func (r *observabilityHttpCheckResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform + // Retrieve values from state + var model Model + diags := req.State.Get(ctx, &model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + projectId := model.ProjectId.ValueString() + instanceId := model.InstanceId.ValueString() + httpCheckUrl := model.Url.ValueString() + httpCheckId := model.HttpCheckId.ValueString() + ctx = tflog.SetField(ctx, "project_id", projectId) + ctx = tflog.SetField(ctx, "http_check_url", httpCheckUrl) + ctx = tflog.SetField(ctx, "instance_id", instanceId) + ctx = tflog.SetField(ctx, "http_check_id", httpCheckId) + + listHttpCheck, err := r.client.ListHttpChecks(ctx, instanceId, projectId).Execute() + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error listing http-checks", fmt.Sprintf("Listing API payload: %v", err)) + return + } + + if listHttpCheck.HttpChecks == nil || len(*listHttpCheck.HttpChecks) == 0 { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error listing http-checks", "Response is empty") + return + } + + for _, httpCheck := range *listHttpCheck.HttpChecks { + // we also check if http-check-ids are matching to support import functionality + if httpCheck.Url != nil && *httpCheck.Url == httpCheckUrl || (httpCheck.Id != nil && *httpCheck.Id == httpCheckId) { + if err := mapFields(ctx, &httpCheck, &model); err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading http-check", "Unable to map http-check model") + return + } + break + } + } + + // Set the state with fully populated data. + diags = resp.State.Set(ctx, model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + tflog.Info(ctx, "http-check read") +} + +// Update attempts to update the resource. In this case, http-checks cannot be updated. +// The Update function is redundant since any modifications will +// automatically trigger a resource recreation through Terraform's built-in +// lifecycle management. +func (r *observabilityHttpCheckResource) Update(ctx context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform + core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating http-check", "Observability http-checks can't be updated") +} + +// Delete deletes the resource and removes the Terraform state on success. +func (r *observabilityHttpCheckResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform + // Retrieve values from state + var model Model + diags := req.State.Get(ctx, &model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + projectId := model.ProjectId.ValueString() + instanceId := model.InstanceId.ValueString() + httpCheckId := model.HttpCheckId.ValueString() + ctx = tflog.SetField(ctx, "project_id", projectId) + ctx = tflog.SetField(ctx, "http_check_id", httpCheckId) + ctx = tflog.SetField(ctx, "instance_id", instanceId) + + _, err := r.client.DeleteHttpCheck(ctx, instanceId, projectId, httpCheckId).Execute() + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting http-check", fmt.Sprintf("Creating API payload: %v", err)) + return + } + + tflog.Info(ctx, "http-check deleted") +} + +// ImportState imports a resource into the Terraform state on success. +// The expected format of the resource import identifier is: project_id,instance_id,http_check_id +func (r *observabilityHttpCheckResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + idParts := strings.Split(req.ID, core.Separator) + + if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" { + core.LogAndAddError(ctx, &resp.Diagnostics, + "Error importing http-check", + fmt.Sprintf("Expected import identifier with format: [project_id],[instance_id],[http_check_id] Got: %q", req.ID), + ) + return + } + + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[1])...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("http_check_id"), idParts[2])...) + tflog.Info(ctx, "Observability http-check state imported") +} + +// mapFields maps httpCheck response to the model. +func mapFields(_ context.Context, httpCheck *observability.HttpCheckChildResponse, model *Model) error { + if httpCheck == nil { + return fmt.Errorf("http-check input is nil") + } + + if model == nil { + return fmt.Errorf("model input is nil") + } + + if httpCheck.Id == nil { + return fmt.Errorf("http-check id is nil") + } + + if httpCheck.Url == nil { + return fmt.Errorf("http-check url is nil") + } + + model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), model.InstanceId.ValueString(), *httpCheck.Id) + model.HttpCheckId = types.StringValue(*httpCheck.Id) + model.Url = types.StringValue(*httpCheck.Url) + + return nil +} diff --git a/stackit/internal/services/observability/http-check/resource_test.go b/stackit/internal/services/observability/http-check/resource_test.go new file mode 100644 index 000000000..575bd2240 --- /dev/null +++ b/stackit/internal/services/observability/http-check/resource_test.go @@ -0,0 +1,96 @@ +package httpcheck + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/stackitcloud/stackit-sdk-go/core/utils" + "github.com/stackitcloud/stackit-sdk-go/services/observability" +) + +func TestMapFields(t *testing.T) { + tests := []struct { + name string + httpCheck *observability.HttpCheckChildResponse + model *Model + expectedId string + expectedHttpCheckId string + expectedUrl string + expectErr bool + }{ + { + name: "Nil HttpCheck", + httpCheck: nil, + model: &Model{}, + expectErr: true, + }, + { + name: "Nil Model", + httpCheck: &observability.HttpCheckChildResponse{}, + model: nil, + expectErr: true, + }, + { + name: "Complete Model and HttpCheck", + httpCheck: &observability.HttpCheckChildResponse{ + Id: utils.Ptr("http-check-id"), + Url: utils.Ptr("https://example.com"), + }, + model: &Model{ + ProjectId: types.StringValue("project1"), + InstanceId: types.StringValue("instance1"), + }, + expectedId: "project1,instance1,http-check-id", + expectedHttpCheckId: "http-check-id", + expectedUrl: "https://example.com", + expectErr: false, + }, + { + name: "Nil HttpCheck Id", + httpCheck: &observability.HttpCheckChildResponse{ + Url: utils.Ptr("https://example.com"), + }, + model: &Model{ + ProjectId: types.StringValue("project1"), + InstanceId: types.StringValue("instance1"), + }, + expectErr: true, + }, + { + name: "Nil HttpCheck Url", + httpCheck: &observability.HttpCheckChildResponse{ + Id: utils.Ptr("http-check-id"), + }, + model: &Model{ + ProjectId: types.StringValue("project1"), + InstanceId: types.StringValue("instance1"), + }, + expectErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + err := mapFields(ctx, tt.httpCheck, tt.model) + + if (err != nil) != tt.expectErr { + t.Fatalf("expected error: %v, got: %v", tt.expectErr, err) + } + + if !tt.expectErr { + if diff := cmp.Diff(tt.model.Id.ValueString(), tt.expectedId); diff != "" { + t.Errorf("unexpected ID (-got +want):\n%s", diff) + } + if diff := cmp.Diff(tt.model.HttpCheckId.ValueString(), tt.expectedHttpCheckId); diff != "" { + t.Errorf("unexpected HttpCheckId (-got +want):\n%s", diff) + } + if diff := cmp.Diff(tt.model.Url.ValueString(), tt.expectedUrl); diff != "" { + t.Errorf("unexpected Url (-got +want):\n%s", diff) + } + } + }) + } +} diff --git a/stackit/internal/services/observability/observability_acc_test.go b/stackit/internal/services/observability/observability_acc_test.go index 27a703bea..242ad61cb 100644 --- a/stackit/internal/services/observability/observability_acc_test.go +++ b/stackit/internal/services/observability/observability_acc_test.go @@ -9,17 +9,15 @@ import ( "testing" "github.com/hashicorp/terraform-plugin-testing/config" - "github.com/hashicorp/terraform-plugin-testing/helper/resource" - "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" + stackitSdkConfig "github.com/stackitcloud/stackit-sdk-go/core/config" "github.com/stackitcloud/stackit-sdk-go/core/utils" "github.com/stackitcloud/stackit-sdk-go/services/observability" "github.com/stackitcloud/stackit-sdk-go/services/observability/wait" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil" - - stackitSdkConfig "github.com/stackitcloud/stackit-sdk-go/core/config" ) //go:embed testdata/resource-min.tf @@ -47,6 +45,8 @@ var testConfigVarsMin = config.Variables{ "scrapeconfig_name": config.StringVariable(fmt.Sprintf("tf-acc-sc%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))), "scrapeconfig_metrics_path": config.StringVariable("/metrics"), "scrapeconfig_targets_url": config.StringVariable("www.y97xyrrocx2gsxx.de"), + "http_check_url": config.StringVariable("https://www.stackit.de"), + "cert_check_source": config.StringVariable("tcp://stackit.de:443"), } var testConfigVarsMax = config.Variables{ @@ -121,12 +121,16 @@ var testConfigVarsMax = config.Variables{ "scrapeconfig_timeout": config.StringVariable("2m"), "scrapeconfig_auth_username": config.StringVariable("username"), "scrapeconfig_auth_password": config.StringVariable("password"), + "http_check_url": config.StringVariable("https://www.stackit.de"), + "cert_check_source": config.StringVariable("tcp://stackit.de:443"), } func configVarsMinUpdated() config.Variables { tempConfig := make(config.Variables, len(testConfigVarsMin)) maps.Copy(tempConfig, testConfigVarsMin) tempConfig["alert_rule_name"] = config.StringVariable("alert1-updated") + tempConfig["http_check_url"] = config.StringVariable("https://docs.api.stackit.cloud") + tempConfig["cert_check_source"] = config.StringVariable("tcp://docs.api.stackit.cloud:443") return tempConfig } @@ -142,6 +146,8 @@ func configVarsMaxUpdated() config.Variables { tempConfig["ms_teams"] = config.StringVariable("false") tempConfig["google_chat"] = config.StringVariable("true") tempConfig["matchers"] = config.StringVariable("instance =~ \"my.*\"") + tempConfig["http_check_url"] = config.StringVariable("https://docs.api.stackit.cloud") + tempConfig["cert_check_source"] = config.StringVariable("tcp://docs.api.stackit.cloud:443") return tempConfig } @@ -153,7 +159,7 @@ func TestAccResourceMin(t *testing.T) { // Creation { ConfigVariables: testConfigVarsMin, - Config: testutil.ObservabilityProviderConfig() + resourceMinConfig, + Config: testutil.ObservabilityProviderConfigBetaEnabled() + resourceMinConfig, Check: resource.ComposeAggregateTestCheckFunc( // Instance data resource.TestCheckResourceAttr("stackit_observability_instance.instance", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])), @@ -220,6 +226,26 @@ func TestAccResourceMin(t *testing.T) { resource.TestCheckResourceAttr("stackit_observability_logalertgroup.logalertgroup", "name", testutil.ConvertConfigVariable(testConfigVarsMin["logalertgroup_name"])), resource.TestCheckResourceAttr("stackit_observability_logalertgroup.logalertgroup", "rules.0.alert", testutil.ConvertConfigVariable(testConfigVarsMin["logalertgroup_alert"])), resource.TestCheckResourceAttr("stackit_observability_logalertgroup.logalertgroup", "rules.0.expression", logalertgroup_expression), + + // http_check + resource.TestCheckResourceAttrSet("stackit_observability_http_check.httpcheck", "http_check_id"), + resource.TestCheckResourceAttrSet("stackit_observability_http_check.httpcheck", "id"), + resource.TestCheckResourceAttr("stackit_observability_http_check.httpcheck", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])), + resource.TestCheckResourceAttrPair( + "stackit_observability_instance.instance", "instance_id", + "stackit_observability_http_check.httpcheck", "instance_id", + ), + resource.TestCheckResourceAttr("stackit_observability_http_check.httpcheck", "url", testutil.ConvertConfigVariable(testConfigVarsMin["http_check_url"])), + + // cert_check + resource.TestCheckResourceAttrSet("stackit_observability_cert_check.certcheck", "cert_check_id"), + resource.TestCheckResourceAttrSet("stackit_observability_cert_check.certcheck", "id"), + resource.TestCheckResourceAttr("stackit_observability_cert_check.certcheck", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])), + resource.TestCheckResourceAttrPair( + "stackit_observability_instance.instance", "instance_id", + "stackit_observability_cert_check.certcheck", "instance_id", + ), + resource.TestCheckResourceAttr("stackit_observability_cert_check.certcheck", "source", testutil.ConvertConfigVariable(testConfigVarsMin["cert_check_source"])), ), }, // Data source @@ -250,8 +276,20 @@ func TestAccResourceMin(t *testing.T) { instance_id = stackit_observability_logalertgroup.logalertgroup.instance_id name = stackit_observability_logalertgroup.logalertgroup.name } + + data "stackit_observability_http_check" "httpcheck" { + project_id = stackit_observability_http_check.httpcheck.project_id + instance_id = stackit_observability_http_check.httpcheck.instance_id + http_check_id = stackit_observability_http_check.httpcheck.http_check_id + } + + data "stackit_observability_cert_check" "certcheck" { + project_id = stackit_observability_cert_check.certcheck.project_id + instance_id = stackit_observability_cert_check.certcheck.instance_id + cert_check_id = stackit_observability_cert_check.certcheck.cert_check_id + } `, - testutil.ObservabilityProviderConfig()+resourceMinConfig, + testutil.ObservabilityProviderConfigBetaEnabled()+resourceMinConfig, ), Check: resource.ComposeAggregateTestCheckFunc( // Instance data @@ -306,6 +344,26 @@ func TestAccResourceMin(t *testing.T) { resource.TestCheckResourceAttr("data.stackit_observability_logalertgroup.logalertgroup", "name", testutil.ConvertConfigVariable(testConfigVarsMin["logalertgroup_name"])), resource.TestCheckResourceAttr("data.stackit_observability_logalertgroup.logalertgroup", "rules.0.alert", testutil.ConvertConfigVariable(testConfigVarsMin["logalertgroup_alert"])), resource.TestCheckResourceAttr("data.stackit_observability_logalertgroup.logalertgroup", "rules.0.expression", logalertgroup_expression), + + // http_check + resource.TestCheckResourceAttrSet("data.stackit_observability_http_check.httpcheck", "http_check_id"), + resource.TestCheckResourceAttrSet("data.stackit_observability_http_check.httpcheck", "id"), + resource.TestCheckResourceAttr("data.stackit_observability_http_check.httpcheck", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])), + resource.TestCheckResourceAttrPair( + "stackit_observability_instance.instance", "instance_id", + "data.stackit_observability_http_check.httpcheck", "instance_id", + ), + resource.TestCheckResourceAttr("data.stackit_observability_http_check.httpcheck", "url", testutil.ConvertConfigVariable(testConfigVarsMin["http_check_url"])), + + // cert_check + resource.TestCheckResourceAttrSet("data.stackit_observability_cert_check.certcheck", "cert_check_id"), + resource.TestCheckResourceAttrSet("data.stackit_observability_cert_check.certcheck", "id"), + resource.TestCheckResourceAttr("data.stackit_observability_cert_check.certcheck", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])), + resource.TestCheckResourceAttrPair( + "stackit_observability_instance.instance", "instance_id", + "data.stackit_observability_cert_check.certcheck", "instance_id", + ), + resource.TestCheckResourceAttr("data.stackit_observability_cert_check.certcheck", "source", testutil.ConvertConfigVariable(testConfigVarsMin["cert_check_source"])), ), }, // Import 1 @@ -393,10 +451,54 @@ func TestAccResourceMin(t *testing.T) { ImportState: true, ImportStateVerify: true, }, + // Import 5 + { + ConfigVariables: testConfigVarsMin, + ResourceName: "stackit_observability_http_check.httpcheck", + ImportStateIdFunc: func(s *terraform.State) (string, error) { + r, ok := s.RootModule().Resources["stackit_observability_http_check.httpcheck"] + if !ok { + return "", fmt.Errorf("couldn't find resource stackit_observability_http_check.httpcheck") + } + instanceId, ok := r.Primary.Attributes["instance_id"] + if !ok { + return "", fmt.Errorf("couldn't find attribute instance_id") + } + httpCheckId, ok := r.Primary.Attributes["http_check_id"] + if !ok { + return "", fmt.Errorf("couldn't find attribute name") + } + return fmt.Sprintf("%s,%s,%s", testutil.ProjectId, instanceId, httpCheckId), nil + }, + ImportState: true, + ImportStateVerify: true, + }, + // Import 6 + { + ConfigVariables: testConfigVarsMin, + ResourceName: "stackit_observability_cert_check.certcheck", + ImportStateIdFunc: func(s *terraform.State) (string, error) { + r, ok := s.RootModule().Resources["stackit_observability_cert_check.certcheck"] + if !ok { + return "", fmt.Errorf("couldn't find resource stackit_observability_cert_check.certcheck") + } + instanceId, ok := r.Primary.Attributes["instance_id"] + if !ok { + return "", fmt.Errorf("couldn't find attribute instance_id") + } + certCheckId, ok := r.Primary.Attributes["cert_check_id"] + if !ok { + return "", fmt.Errorf("couldn't find attribute name") + } + return fmt.Sprintf("%s,%s,%s", testutil.ProjectId, instanceId, certCheckId), nil + }, + ImportState: true, + ImportStateVerify: true, + }, // Update { ConfigVariables: configVarsMinUpdated(), - Config: testutil.ObservabilityProviderConfig() + resourceMinConfig, + Config: testutil.ObservabilityProviderConfigBetaEnabled() + resourceMinConfig, Check: resource.ComposeAggregateTestCheckFunc( // Instance data resource.TestCheckResourceAttr("stackit_observability_instance.instance", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])), @@ -463,6 +565,26 @@ func TestAccResourceMin(t *testing.T) { resource.TestCheckResourceAttr("stackit_observability_logalertgroup.logalertgroup", "name", testutil.ConvertConfigVariable(testConfigVarsMin["logalertgroup_name"])), resource.TestCheckResourceAttr("stackit_observability_logalertgroup.logalertgroup", "rules.0.alert", testutil.ConvertConfigVariable(testConfigVarsMin["logalertgroup_alert"])), resource.TestCheckResourceAttr("stackit_observability_logalertgroup.logalertgroup", "rules.0.expression", logalertgroup_expression), + + // http_check + resource.TestCheckResourceAttrSet("stackit_observability_http_check.httpcheck", "http_check_id"), + resource.TestCheckResourceAttrSet("stackit_observability_http_check.httpcheck", "id"), + resource.TestCheckResourceAttr("stackit_observability_http_check.httpcheck", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])), + resource.TestCheckResourceAttrPair( + "stackit_observability_instance.instance", "instance_id", + "stackit_observability_http_check.httpcheck", "instance_id", + ), + resource.TestCheckResourceAttr("stackit_observability_http_check.httpcheck", "url", testutil.ConvertConfigVariable(configVarsMinUpdated()["http_check_url"])), + + // cert_check + resource.TestCheckResourceAttrSet("stackit_observability_cert_check.certcheck", "cert_check_id"), + resource.TestCheckResourceAttrSet("stackit_observability_cert_check.certcheck", "id"), + resource.TestCheckResourceAttr("stackit_observability_cert_check.certcheck", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])), + resource.TestCheckResourceAttrPair( + "stackit_observability_instance.instance", "instance_id", + "stackit_observability_cert_check.certcheck", "instance_id", + ), + resource.TestCheckResourceAttr("stackit_observability_cert_check.certcheck", "source", testutil.ConvertConfigVariable(configVarsMinUpdated()["cert_check_source"])), ), }, }, @@ -477,7 +599,7 @@ func TestAccResourceMax(t *testing.T) { // Creation { ConfigVariables: testConfigVarsMax, - Config: testutil.ObservabilityProviderConfig() + resourceMaxConfig, + Config: testutil.ObservabilityProviderConfigBetaEnabled() + resourceMaxConfig, Check: resource.ComposeAggregateTestCheckFunc( // Instance data resource.TestCheckResourceAttr("stackit_observability_instance.instance", "project_id", testutil.ConvertConfigVariable(testConfigVarsMax["project_id"])), @@ -613,6 +735,26 @@ func TestAccResourceMax(t *testing.T) { resource.TestCheckResourceAttr("stackit_observability_logalertgroup.logalertgroup", "rules.0.labels.label1", testutil.ConvertConfigVariable(testConfigVarsMax["logalertgroup_label"])), resource.TestCheckResourceAttr("stackit_observability_logalertgroup.logalertgroup", "rules.0.annotations.annotation1", testutil.ConvertConfigVariable(testConfigVarsMax["logalertgroup_annotation"])), resource.TestCheckResourceAttr("stackit_observability_logalertgroup.logalertgroup", "interval", testutil.ConvertConfigVariable(testConfigVarsMax["logalertgroup_interval"])), + + // http_check + resource.TestCheckResourceAttrSet("stackit_observability_http_check.httpcheck", "http_check_id"), + resource.TestCheckResourceAttrSet("stackit_observability_http_check.httpcheck", "id"), + resource.TestCheckResourceAttr("stackit_observability_http_check.httpcheck", "project_id", testutil.ConvertConfigVariable(testConfigVarsMax["project_id"])), + resource.TestCheckResourceAttrPair( + "stackit_observability_instance.instance", "instance_id", + "stackit_observability_http_check.httpcheck", "instance_id", + ), + resource.TestCheckResourceAttr("stackit_observability_http_check.httpcheck", "url", testutil.ConvertConfigVariable(testConfigVarsMax["http_check_url"])), + + // cert_check + resource.TestCheckResourceAttrSet("stackit_observability_cert_check.certcheck", "cert_check_id"), + resource.TestCheckResourceAttrSet("stackit_observability_cert_check.certcheck", "id"), + resource.TestCheckResourceAttr("stackit_observability_cert_check.certcheck", "project_id", testutil.ConvertConfigVariable(testConfigVarsMax["project_id"])), + resource.TestCheckResourceAttrPair( + "stackit_observability_instance.instance", "instance_id", + "stackit_observability_cert_check.certcheck", "instance_id", + ), + resource.TestCheckResourceAttr("stackit_observability_cert_check.certcheck", "source", testutil.ConvertConfigVariable(testConfigVarsMax["cert_check_source"])), ), }, // Data source @@ -643,8 +785,20 @@ func TestAccResourceMax(t *testing.T) { instance_id = stackit_observability_logalertgroup.logalertgroup.instance_id name = stackit_observability_logalertgroup.logalertgroup.name } + + data "stackit_observability_http_check" "httpcheck" { + project_id = stackit_observability_http_check.httpcheck.project_id + instance_id = stackit_observability_http_check.httpcheck.instance_id + http_check_id = stackit_observability_http_check.httpcheck.http_check_id + } + + data "stackit_observability_cert_check" "certcheck" { + project_id = stackit_observability_cert_check.certcheck.project_id + instance_id = stackit_observability_cert_check.certcheck.instance_id + cert_check_id = stackit_observability_cert_check.certcheck.cert_check_id + } `, - testutil.ObservabilityProviderConfig()+resourceMaxConfig, + testutil.ObservabilityProviderConfigBetaEnabled()+resourceMaxConfig, ), Check: resource.ComposeAggregateTestCheckFunc( // Instance data @@ -777,6 +931,26 @@ func TestAccResourceMax(t *testing.T) { resource.TestCheckResourceAttr("stackit_observability_logalertgroup.logalertgroup", "rules.0.labels.label1", testutil.ConvertConfigVariable(testConfigVarsMax["logalertgroup_label"])), resource.TestCheckResourceAttr("stackit_observability_logalertgroup.logalertgroup", "rules.0.annotations.annotation1", testutil.ConvertConfigVariable(testConfigVarsMax["logalertgroup_annotation"])), resource.TestCheckResourceAttr("stackit_observability_logalertgroup.logalertgroup", "interval", testutil.ConvertConfigVariable(testConfigVarsMax["logalertgroup_interval"])), + + // http_check + resource.TestCheckResourceAttrSet("data.stackit_observability_http_check.httpcheck", "http_check_id"), + resource.TestCheckResourceAttrSet("data.stackit_observability_http_check.httpcheck", "id"), + resource.TestCheckResourceAttr("data.stackit_observability_http_check.httpcheck", "project_id", testutil.ConvertConfigVariable(testConfigVarsMax["project_id"])), + resource.TestCheckResourceAttrPair( + "stackit_observability_instance.instance", "instance_id", + "data.stackit_observability_http_check.httpcheck", "instance_id", + ), + resource.TestCheckResourceAttr("data.stackit_observability_http_check.httpcheck", "url", testutil.ConvertConfigVariable(testConfigVarsMax["http_check_url"])), + + // cert_check + resource.TestCheckResourceAttrSet("data.stackit_observability_cert_check.certcheck", "cert_check_id"), + resource.TestCheckResourceAttrSet("data.stackit_observability_cert_check.certcheck", "id"), + resource.TestCheckResourceAttr("data.stackit_observability_cert_check.certcheck", "project_id", testutil.ConvertConfigVariable(testConfigVarsMax["project_id"])), + resource.TestCheckResourceAttrPair( + "stackit_observability_instance.instance", "instance_id", + "data.stackit_observability_cert_check.certcheck", "instance_id", + ), + resource.TestCheckResourceAttr("data.stackit_observability_cert_check.certcheck", "source", testutil.ConvertConfigVariable(testConfigVarsMax["cert_check_source"])), ), }, // Import 1 @@ -868,10 +1042,54 @@ func TestAccResourceMax(t *testing.T) { ImportStateVerify: true, ImportStateVerifyIgnore: []string{"alert_config.global.smtp_auth_identity", "alert_config.global.smtp_auth_password", "alert_config.global.smtp_auth_username", "alert_config.global.smtp_smart_host"}, }, + // Import 5 + { + ConfigVariables: testConfigVarsMax, + ResourceName: "stackit_observability_http_check.httpcheck", + ImportStateIdFunc: func(s *terraform.State) (string, error) { + r, ok := s.RootModule().Resources["stackit_observability_http_check.httpcheck"] + if !ok { + return "", fmt.Errorf("couldn't find resource stackit_observability_http_check.httpcheck") + } + instanceId, ok := r.Primary.Attributes["instance_id"] + if !ok { + return "", fmt.Errorf("couldn't find attribute instance_id") + } + httpCheckId, ok := r.Primary.Attributes["http_check_id"] + if !ok { + return "", fmt.Errorf("couldn't find attribute name") + } + return fmt.Sprintf("%s,%s,%s", testutil.ProjectId, instanceId, httpCheckId), nil + }, + ImportState: true, + ImportStateVerify: true, + }, + // Import 6 + { + ConfigVariables: testConfigVarsMax, + ResourceName: "stackit_observability_cert_check.certcheck", + ImportStateIdFunc: func(s *terraform.State) (string, error) { + r, ok := s.RootModule().Resources["stackit_observability_cert_check.certcheck"] + if !ok { + return "", fmt.Errorf("couldn't find resource stackit_observability_cert_check.certcheck") + } + instanceId, ok := r.Primary.Attributes["instance_id"] + if !ok { + return "", fmt.Errorf("couldn't find attribute instance_id") + } + certCheckId, ok := r.Primary.Attributes["cert_check_id"] + if !ok { + return "", fmt.Errorf("couldn't find attribute name") + } + return fmt.Sprintf("%s,%s,%s", testutil.ProjectId, instanceId, certCheckId), nil + }, + ImportState: true, + ImportStateVerify: true, + }, // Update { ConfigVariables: configVarsMaxUpdated(), - Config: testutil.ObservabilityProviderConfig() + resourceMaxConfig, + Config: testutil.ObservabilityProviderConfigBetaEnabled() + resourceMaxConfig, Check: resource.ComposeAggregateTestCheckFunc( // Instance data resource.TestCheckResourceAttr("stackit_observability_instance.instance", "project_id", testutil.ConvertConfigVariable(testConfigVarsMax["project_id"])), @@ -1007,6 +1225,26 @@ func TestAccResourceMax(t *testing.T) { resource.TestCheckResourceAttr("stackit_observability_logalertgroup.logalertgroup", "rules.0.labels.label1", testutil.ConvertConfigVariable(testConfigVarsMax["logalertgroup_label"])), resource.TestCheckResourceAttr("stackit_observability_logalertgroup.logalertgroup", "rules.0.annotations.annotation1", testutil.ConvertConfigVariable(testConfigVarsMax["logalertgroup_annotation"])), resource.TestCheckResourceAttr("stackit_observability_logalertgroup.logalertgroup", "interval", testutil.ConvertConfigVariable(configVarsMaxUpdated()["logalertgroup_interval"])), + + // http_check + resource.TestCheckResourceAttrSet("stackit_observability_http_check.httpcheck", "http_check_id"), + resource.TestCheckResourceAttrSet("stackit_observability_http_check.httpcheck", "id"), + resource.TestCheckResourceAttr("stackit_observability_http_check.httpcheck", "project_id", testutil.ConvertConfigVariable(testConfigVarsMax["project_id"])), + resource.TestCheckResourceAttrPair( + "stackit_observability_instance.instance", "instance_id", + "stackit_observability_http_check.httpcheck", "instance_id", + ), + resource.TestCheckResourceAttr("stackit_observability_http_check.httpcheck", "url", testutil.ConvertConfigVariable(configVarsMaxUpdated()["http_check_url"])), + + // cert_check + resource.TestCheckResourceAttrSet("stackit_observability_cert_check.certcheck", "cert_check_id"), + resource.TestCheckResourceAttrSet("stackit_observability_cert_check.certcheck", "id"), + resource.TestCheckResourceAttr("stackit_observability_cert_check.certcheck", "project_id", testutil.ConvertConfigVariable(testConfigVarsMax["project_id"])), + resource.TestCheckResourceAttrPair( + "stackit_observability_instance.instance", "instance_id", + "stackit_observability_cert_check.certcheck", "instance_id", + ), + resource.TestCheckResourceAttr("stackit_observability_cert_check.certcheck", "source", testutil.ConvertConfigVariable(configVarsMaxUpdated()["cert_check_source"])), ), }, // Deletion is done by the framework implicitly diff --git a/stackit/internal/services/observability/testdata/resource-max.tf b/stackit/internal/services/observability/testdata/resource-max.tf index 871d4d1fd..9c52e1010 100644 --- a/stackit/internal/services/observability/testdata/resource-max.tf +++ b/stackit/internal/services/observability/testdata/resource-max.tf @@ -71,6 +71,9 @@ variable "scrapeconfig_timeout" {} variable "scrapeconfig_auth_username" {} variable "scrapeconfig_auth_password" {} +variable "http_check_url" {} +variable "cert_check_source" {} + resource "stackit_observability_alertgroup" "alertgroup" { project_id = var.project_id instance_id = stackit_observability_instance.instance.instance_id @@ -228,3 +231,15 @@ resource "stackit_observability_scrapeconfig" "scrapeconfig" { } } + +resource "stackit_observability_http_check" "httpcheck" { + project_id = var.project_id + instance_id = stackit_observability_instance.instance.instance_id + url = var.http_check_url +} + +resource "stackit_observability_cert_check" "certcheck" { + project_id = var.project_id + instance_id = stackit_observability_instance.instance.instance_id + source = var.cert_check_source +} diff --git a/stackit/internal/services/observability/testdata/resource-min.tf b/stackit/internal/services/observability/testdata/resource-min.tf index 68d405f0c..342d70d25 100644 --- a/stackit/internal/services/observability/testdata/resource-min.tf +++ b/stackit/internal/services/observability/testdata/resource-min.tf @@ -17,6 +17,9 @@ variable "scrapeconfig_name" {} variable "scrapeconfig_metrics_path" {} variable "scrapeconfig_targets_url" {} +variable "http_check_url" {} +variable "cert_check_source" {} + resource "stackit_observability_alertgroup" "alertgroup" { project_id = var.project_id @@ -64,6 +67,18 @@ resource "stackit_observability_scrapeconfig" "scrapeconfig" { targets = [{ urls = [var.scrapeconfig_targets_url] }] } +resource "stackit_observability_http_check" "httpcheck" { + project_id = var.project_id + instance_id = stackit_observability_instance.instance.instance_id + url = var.http_check_url +} + +resource "stackit_observability_cert_check" "certcheck" { + project_id = var.project_id + instance_id = stackit_observability_instance.instance.instance_id + source = var.cert_check_source +} + diff --git a/stackit/internal/testutil/testutil.go b/stackit/internal/testutil/testutil.go index a2651db28..99f98abe6 100644 --- a/stackit/internal/testutil/testutil.go +++ b/stackit/internal/testutil/testutil.go @@ -92,6 +92,23 @@ func ObservabilityProviderConfig() string { ObservabilityCustomEndpoint, ) } + +func ObservabilityProviderConfigBetaEnabled() string { + if ObservabilityCustomEndpoint == "" { + return `provider "stackit" { + enable_beta_resources = true + default_region = "eu01" + }` + } + return fmt.Sprintf(` + provider "stackit" { + enable_beta_resources = true + observability_custom_endpoint = "%s" + }`, + ObservabilityCustomEndpoint, + ) +} + func CdnProviderConfig() string { if CdnCustomEndpoint == "" { return ` diff --git a/stackit/provider.go b/stackit/provider.go index 5f51fc33c..20a387936 100644 --- a/stackit/provider.go +++ b/stackit/provider.go @@ -61,7 +61,9 @@ import ( objecStorageCredential "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/objectstorage/credential" objecStorageCredentialsGroup "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/objectstorage/credentialsgroup" alertGroup "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/observability/alertgroup" + observabilityCertCheck "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/observability/cert-check" observabilityCredential "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/observability/credential" + observabilityHttpCheck "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/observability/http-check" observabilityInstance "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/observability/instance" logAlertGroup "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/observability/log-alertgroup" observabilityScrapeConfig "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/observability/scrapeconfig" @@ -464,51 +466,53 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource alertGroup.NewAlertGroupDataSource, cdn.NewDistributionDataSource, cdnCustomDomain.NewCustomDomainDataSource, - dnsZone.NewZoneDataSource, dnsRecordSet.NewRecordSetDataSource, + dnsZone.NewZoneDataSource, gitInstance.NewGitDataSource, iaasAffinityGroup.NewAffinityGroupDatasource, iaasImage.NewImageDataSource, iaasImageV2.NewImageV2DataSource, + iaasKeyPair.NewKeyPairDataSource, iaasNetwork.NewNetworkDataSource, iaasNetworkArea.NewNetworkAreaDataSource, iaasNetworkAreaRoute.NewNetworkAreaRouteDataSource, iaasNetworkInterface.NewNetworkInterfaceDataSource, - iaasVolume.NewVolumeDataSource, iaasProject.NewProjectDataSource, iaasPublicIp.NewPublicIpDataSource, iaasPublicIpRanges.NewPublicIpRangesDataSource, - iaasKeyPair.NewKeyPairDataSource, - iaasServer.NewServerDataSource, iaasSecurityGroup.NewSecurityGroupDataSource, + iaasSecurityGroupRule.NewSecurityGroupRuleDataSource, + iaasServer.NewServerDataSource, iaasalphaRoutingTable.NewRoutingTableDataSource, iaasalphaRoutingTableRoute.NewRoutingTableRouteDataSource, iaasalphaRoutingTables.NewRoutingTablesDataSource, iaasalphaRoutingTableRoutes.NewRoutingTableRoutesDataSource, - iaasSecurityGroupRule.NewSecurityGroupRuleDataSource, + iaasVolume.NewVolumeDataSource, loadBalancer.NewLoadBalancerDataSource, - logMeInstance.NewInstanceDataSource, - logMeCredential.NewCredentialDataSource, logAlertGroup.NewLogAlertGroupDataSource, + logMeCredential.NewCredentialDataSource, + logMeInstance.NewInstanceDataSource, machineType.NewMachineTypeDataSource, - mariaDBInstance.NewInstanceDataSource, mariaDBCredential.NewCredentialDataSource, + mariaDBInstance.NewInstanceDataSource, mongoDBFlexInstance.NewInstanceDataSource, mongoDBFlexUser.NewUserDataSource, objectStorageBucket.NewBucketDataSource, - objecStorageCredentialsGroup.NewCredentialsGroupDataSource, objecStorageCredential.NewCredentialDataSource, + objecStorageCredentialsGroup.NewCredentialsGroupDataSource, + observabilityCertCheck.NewCertCheckDataSource, + observabilityHttpCheck.NewHttpCheckDataSource, observabilityInstance.NewInstanceDataSource, observabilityScrapeConfig.NewScrapeConfigDataSource, - openSearchInstance.NewInstanceDataSource, openSearchCredential.NewCredentialDataSource, + openSearchInstance.NewInstanceDataSource, postgresFlexDatabase.NewDatabaseDataSource, postgresFlexInstance.NewInstanceDataSource, postgresFlexUser.NewUserDataSource, - rabbitMQInstance.NewInstanceDataSource, rabbitMQCredential.NewCredentialDataSource, - redisInstance.NewInstanceDataSource, + rabbitMQInstance.NewInstanceDataSource, redisCredential.NewCredentialDataSource, + redisInstance.NewInstanceDataSource, resourceManagerProject.NewProjectDataSource, scfOrganization.NewScfOrganizationDataSource, scfOrganizationmanager.NewScfOrganizationManagerDataSource, @@ -516,14 +520,14 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource resourceManagerFolder.NewFolderDataSource, secretsManagerInstance.NewInstanceDataSource, secretsManagerUser.NewUserDataSource, - sqlServerFlexInstance.NewInstanceDataSource, - sqlServerFlexUser.NewUserDataSource, serverBackupSchedule.NewScheduleDataSource, serverBackupSchedule.NewSchedulesDataSource, serverUpdateSchedule.NewScheduleDataSource, serverUpdateSchedule.NewSchedulesDataSource, serviceAccount.NewServiceAccountDataSource, skeCluster.NewClusterDataSource, + sqlServerFlexInstance.NewInstanceDataSource, + sqlServerFlexUser.NewUserDataSource, } } @@ -533,67 +537,69 @@ func (p *Provider) Resources(_ context.Context) []func() resource.Resource { alertGroup.NewAlertGroupResource, cdn.NewDistributionResource, cdnCustomDomain.NewCustomDomainResource, - dnsZone.NewZoneResource, dnsRecordSet.NewRecordSetResource, + dnsZone.NewZoneResource, gitInstance.NewGitResource, iaasAffinityGroup.NewAffinityGroupResource, iaasImage.NewImageResource, + iaasKeyPair.NewKeyPairResource, iaasNetwork.NewNetworkResource, iaasNetworkArea.NewNetworkAreaResource, iaasNetworkAreaRoute.NewNetworkAreaRouteResource, iaasNetworkInterface.NewNetworkInterfaceResource, - iaasVolume.NewVolumeResource, - iaasPublicIp.NewPublicIpResource, - iaasKeyPair.NewKeyPairResource, - iaasVolumeAttach.NewVolumeAttachResource, iaasNetworkInterfaceAttach.NewNetworkInterfaceAttachResource, - iaasServiceAccountAttach.NewServiceAccountAttachResource, + iaasPublicIp.NewPublicIpResource, iaasPublicIpAssociate.NewPublicIpAssociateResource, - iaasServer.NewServerResource, iaasSecurityGroup.NewSecurityGroupResource, iaasSecurityGroupRule.NewSecurityGroupRuleResource, + iaasServiceAccountAttach.NewServiceAccountAttachResource, + iaasVolume.NewVolumeResource, + iaasVolumeAttach.NewVolumeAttachResource, iaasalphaRoutingTable.NewRoutingTableResource, iaasalphaRoutingTableRoute.NewRoutingTableRouteResource, + iaasServer.NewServerResource, loadBalancer.NewLoadBalancerResource, loadBalancerObservabilityCredential.NewObservabilityCredentialResource, - logMeInstance.NewInstanceResource, - logMeCredential.NewCredentialResource, logAlertGroup.NewLogAlertGroupResource, - mariaDBInstance.NewInstanceResource, + logMeCredential.NewCredentialResource, + logMeInstance.NewInstanceResource, mariaDBCredential.NewCredentialResource, + mariaDBInstance.NewInstanceResource, modelServingToken.NewTokenResource, mongoDBFlexInstance.NewInstanceResource, mongoDBFlexUser.NewUserResource, - objectStorageBucket.NewBucketResource, - objecStorageCredentialsGroup.NewCredentialsGroupResource, - objecStorageCredential.NewCredentialResource, + observabilityCertCheck.NewCertCheckResource, observabilityCredential.NewCredentialResource, + observabilityHttpCheck.NewHttpCheckResource, observabilityInstance.NewInstanceResource, observabilityScrapeConfig.NewScrapeConfigResource, - openSearchInstance.NewInstanceResource, + objectStorageBucket.NewBucketResource, + objecStorageCredential.NewCredentialResource, + objecStorageCredentialsGroup.NewCredentialsGroupResource, openSearchCredential.NewCredentialResource, + openSearchInstance.NewInstanceResource, postgresFlexDatabase.NewDatabaseResource, postgresFlexInstance.NewInstanceResource, postgresFlexUser.NewUserResource, - rabbitMQInstance.NewInstanceResource, rabbitMQCredential.NewCredentialResource, - redisInstance.NewInstanceResource, + rabbitMQInstance.NewInstanceResource, redisCredential.NewCredentialResource, + redisInstance.NewInstanceResource, resourceManagerProject.NewProjectResource, scfOrganization.NewScfOrganizationResource, scfOrganizationmanager.NewScfOrganizationManagerResource, resourceManagerFolder.NewFolderResource, secretsManagerInstance.NewInstanceResource, secretsManagerUser.NewUserResource, - sqlServerFlexInstance.NewInstanceResource, - sqlServerFlexUser.NewUserResource, - serverBackupSchedule.NewScheduleResource, - serverUpdateSchedule.NewScheduleResource, serviceAccount.NewServiceAccountResource, - serviceAccountToken.NewServiceAccountTokenResource, serviceAccountKey.NewServiceAccountKeyResource, + serviceAccountToken.NewServiceAccountTokenResource, + serverBackupSchedule.NewScheduleResource, + serverUpdateSchedule.NewScheduleResource, skeCluster.NewClusterResource, skeKubeconfig.NewKubeconfigResource, + sqlServerFlexInstance.NewInstanceResource, + sqlServerFlexUser.NewUserResource, } resources = append(resources, roleAssignements.NewRoleAssignmentResources()...)