From 744f5acb3051f947b873829d113c3284602e5552 Mon Sep 17 00:00:00 2001 From: Devansh Thakur Date: Mon, 8 Dec 2025 00:05:21 +0100 Subject: [PATCH 01/44] Resolve conflicts # Conflicts: # go.mod # go.sum # stackit/internal/testutil/testutil.go # stackit/provider.go # Conflicts: # stackit/internal/testutil/testutil.go # Conflicts: # stackit/internal/testutil/testutil.go # Conflicts: # stackit/provider.go --- docs/index.md | 1 + docs/resources/intake_runner.md | 34 ++ stackit/internal/core/core.go | 1 + .../services/intake/runner/resource.go | 529 ++++++++++++++++++ .../intake/runner/resource_acc_test.go | 160 ++++++ .../services/intake/runner/resource_test.go | 254 +++++++++ .../internal/services/intake/utils/utils.go | 31 + stackit/internal/testutil/testutil.go | 1 + stackit/provider.go | 8 + 9 files changed, 1019 insertions(+) create mode 100644 docs/resources/intake_runner.md create mode 100644 stackit/internal/services/intake/runner/resource.go create mode 100644 stackit/internal/services/intake/runner/resource_acc_test.go create mode 100644 stackit/internal/services/intake/runner/resource_test.go create mode 100644 stackit/internal/services/intake/utils/utils.go diff --git a/docs/index.md b/docs/index.md index ebf478219..0b10f72ce 100644 --- a/docs/index.md +++ b/docs/index.md @@ -175,6 +175,7 @@ Note: AWS specific checks must be skipped as they do not work on STACKIT. For de - `experiments` (List of String) Enables experiments. These are unstable features without official support. More information can be found in the README. Available Experiments: iam, routing-tables, network - `git_custom_endpoint` (String) Custom endpoint for the Git service - `iaas_custom_endpoint` (String) Custom endpoint for the IaaS service +- `intake_custom_endpoint` (String) - `kms_custom_endpoint` (String) Custom endpoint for the KMS service - `loadbalancer_custom_endpoint` (String) Custom endpoint for the Load Balancer service - `logme_custom_endpoint` (String) Custom endpoint for the LogMe service diff --git a/docs/resources/intake_runner.md b/docs/resources/intake_runner.md new file mode 100644 index 000000000..65a5c3206 --- /dev/null +++ b/docs/resources/intake_runner.md @@ -0,0 +1,34 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "stackit_intake_runner Resource - stackit" +subcategory: "" +description: |- + Manages STACKIT Intake Runner. +--- + +# stackit_intake_runner (Resource) + +Manages STACKIT Intake Runner. + + + + +## Schema + +### Required + +- `max_message_size_kib` (Number) The maximum message size in KiB. +- `max_messages_per_hour` (Number) The maximum number of messages per hour. +- `name` (String) The name of the runner. +- `project_id` (String) STACKIT Project ID to which the runner is associated. + +### Optional + +- `description` (String) The description of the runner. +- `labels` (Map of String) User-defined labels. +- `region` (String) The resource region. If not defined, the provider region is used. + +### Read-Only + +- `id` (String) Terraform's internal resource identifier. It is structured as "`project_id`,`runner_id`". +- `runner_id` (String) The runner ID. diff --git a/stackit/internal/core/core.go b/stackit/internal/core/core.go index f8fa8f9f0..3ecf92de1 100644 --- a/stackit/internal/core/core.go +++ b/stackit/internal/core/core.go @@ -48,6 +48,7 @@ type ProviderData struct { EdgeCloudCustomEndpoint string GitCustomEndpoint string IaaSCustomEndpoint string + IntakeCustomEndpoint string KMSCustomEndpoint string LoadBalancerCustomEndpoint string LogMeCustomEndpoint string diff --git a/stackit/internal/services/intake/runner/resource.go b/stackit/internal/services/intake/runner/resource.go new file mode 100644 index 000000000..d9de6667a --- /dev/null +++ b/stackit/internal/services/intake/runner/resource.go @@ -0,0 +1,529 @@ +package runner + +import ( + "context" + "errors" + "fmt" + "net/http" + "strings" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier" + "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/core/oapierror" + sdkUtils "github.com/stackitcloud/stackit-sdk-go/core/utils" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" + intakeUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/intake/utils" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate" + + "github.com/stackitcloud/stackit-sdk-go/services/intake" + "github.com/stackitcloud/stackit-sdk-go/services/intake/wait" +) + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ resource.Resource = &runnerResource{} + _ resource.ResourceWithConfigure = &runnerResource{} + _ resource.ResourceWithImportState = &runnerResource{} + _ resource.ResourceWithModifyPlan = &runnerResource{} +) + +// Model is the internal model of the terraform resource +type Model struct { + Id types.String `tfsdk:"id"` // needed by TF + ProjectId types.String `tfsdk:"project_id"` + RunnerId types.String `tfsdk:"runner_id"` + Region types.String `tfsdk:"region"` + Name types.String `tfsdk:"name"` + Description types.String `tfsdk:"description"` + Labels types.Map `tfsdk:"labels"` + MaxMessageSizeKiB types.Int64 `tfsdk:"max_message_size_kib"` + MaxMessagesPerHour types.Int64 `tfsdk:"max_messages_per_hour"` +} + +// NewRunnerResource is a helper function to simplify the provider implementation. +func NewRunnerResource() resource.Resource { + return &runnerResource{} +} + +// runnerResource is the resource implementation. +type runnerResource struct { + client *intake.APIClient + providerData core.ProviderData +} + +// Metadata returns the resource type name. +func (r *runnerResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_intake_runner" +} + +// Configure adds the provider configured client to the resource. +func (r *runnerResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + providerData, ok := conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics) + if !ok { + return + } + + apiClient := intakeUtils.ConfigureClient(ctx, &providerData, &resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + r.client = apiClient + tflog.Info(ctx, "Intake runner client configured") +} + +// ModifyPlan implements resource.ResourceWithModifyPlan. +// Use the modifier to set the effective region in the current plan. +func (r *runnerResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { // nolint:gocritic // function signature required by Terraform + var configModel Model + // skip initial empty configuration to avoid follow-up errors + if req.Config.Raw.IsNull() { + return + } + resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...) + if resp.Diagnostics.HasError() { + return + } + + var planModel Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &planModel)...) + if resp.Diagnostics.HasError() { + return + } + + utils.AdaptRegion(ctx, configModel.Region, &planModel.Region, r.providerData.GetRegion(), resp) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...) + if resp.Diagnostics.HasError() { + return + } +} + +// Schema defines the schema for the resource. +func (r *runnerResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + descriptions := map[string]string{ + "main": "Manages STACKIT Intake Runner.", + "id": "Terraform's internal resource identifier. It is structured as \"`project_id`,`runner_id`\".", + "project_id": "STACKIT Project ID to which the runner is associated.", + "runner_id": "The runner ID.", + "name": "The name of the runner.", + "region": "The resource region. If not defined, the provider region is used.", + "description": "The description of the runner.", + "labels": "User-defined labels.", + "max_message_size_kib": "The maximum message size in KiB.", + "max_messages_per_hour": "The maximum number of messages per hour.", + } + + resp.Schema = schema.Schema{ + Description: descriptions["main"], + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Description: descriptions["id"], + Computed: true, + }, + "project_id": schema.StringAttribute{ + Description: descriptions["project_id"], + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + validate.UUID(), + validate.NoSeparator(), + }, + }, + "runner_id": schema.StringAttribute{ + Description: descriptions["runner_id"], + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "name": schema.StringAttribute{ + Description: descriptions["name"], + Required: true, + }, + "description": schema.StringAttribute{ + Description: descriptions["description"], + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "labels": schema.MapAttribute{ + Description: descriptions["labels"], + ElementType: types.StringType, + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Map{ + mapplanmodifier.UseStateForUnknown(), + }, + }, + "max_message_size_kib": schema.Int64Attribute{ + Description: descriptions["max_message_size_kib"], + Required: true, + }, + "max_messages_per_hour": schema.Int64Attribute{ + Description: descriptions["max_messages_per_hour"], + Required: true, + }, + "region": schema.StringAttribute{ + Optional: true, + Computed: true, + Description: descriptions["region"], + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.OneOf("eu01"), // Currently Intake supports only EU01 region + }, + }, + }, + } +} + +// Create creates the resource and sets the initial Terraform state. +func (r *runnerResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform + var model Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &model)...) + if resp.Diagnostics.HasError() { + return + } + + ctx = core.InitProviderContext(ctx) + + projectId := model.ProjectId.ValueString() + region := model.Region.ValueString() + ctx = tflog.SetField(ctx, "project_id", projectId) + ctx = tflog.SetField(ctx, "region", region) + + // prepare the payload struct for the create bar request + payload, err := toCreatePayload(&model) + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating credential", fmt.Sprintf("Creating API payload: %v", err)) + return + } + + // Create new bar + runnerResp, err := r.client.CreateIntakeRunner(ctx, projectId, region).CreateIntakeRunnerPayload(*payload).Execute() + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating runner", fmt.Sprintf("Calling API: %v", err)) + return + } + ctx = core.LogResponse(ctx) + + // Wait for creation of intake runner + _, err = wait.CreateOrUpdateIntakeRunnerWaitHandler(ctx, r.client, projectId, region, runnerResp.GetId()).WaitWithContext(ctx) + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating runner", fmt.Sprintf("Intake runner creation waiting: %v", err)) + return + } + + err = mapFields(runnerResp, &model) + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating runner", fmt.Sprintf("Processing API payload: %v", err)) + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, model)...) + if resp.Diagnostics.HasError() { + return + } + tflog.Info(ctx, "Intake runner created") +} + +// Read refreshes the Terraform state with the latest data. +func (r *runnerResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform + var model Model + resp.Diagnostics.Append(req.State.Get(ctx, &model)...) + if resp.Diagnostics.HasError() { + return + } + + ctx = core.InitProviderContext(ctx) + + projectId := model.ProjectId.ValueString() + region := r.providerData.GetRegionWithOverride(model.Region) + runnerId := model.RunnerId.ValueString() + ctx = tflog.SetField(ctx, "project_id", projectId) + ctx = tflog.SetField(ctx, "region", region) + ctx = tflog.SetField(ctx, "runner_id", runnerId) + + runnerResp, err := r.client.GetIntakeRunner(ctx, projectId, region, runnerId).Execute() + if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) { + if oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } + } + core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading runner", fmt.Sprintf("Calling API: %v", err)) + return + } + + ctx = core.LogResponse(ctx) + + // Map response body to schema + err = mapFields(runnerResp, &model) + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading runner", fmt.Sprintf("Processing API payload: %v", err)) + return + } + + // Set refreshed state + resp.Diagnostics.Append(resp.State.Set(ctx, model)...) + if resp.Diagnostics.HasError() { + return + } + tflog.Info(ctx, "Intake runner read") +} + +// Update updates the resource and sets the updated Terraform state on success. +func (r *runnerResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform + var model, state Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &model)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + ctx = core.InitProviderContext(ctx) + + projectId := model.ProjectId.ValueString() + runnerId := model.RunnerId.ValueString() + region := r.providerData.GetRegionWithOverride(model.Region) + ctx = tflog.SetField(ctx, "project_id", projectId) + ctx = tflog.SetField(ctx, "runner_id", runnerId) + ctx = tflog.SetField(ctx, "region", region) + + payload, err := toUpdatePayload(&model, &state) + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating runner", fmt.Sprintf("Creating API payload: %v", err)) + return + } + + // Update runner + runnerResp, err := r.client.UpdateIntakeRunner(ctx, projectId, region, runnerId).UpdateIntakeRunnerPayload(*payload).Execute() + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating runner", fmt.Sprintf("Calling API: %v", err)) + return + } + + // Wait for update + _, err = wait.CreateOrUpdateIntakeRunnerWaitHandler(ctx, r.client, projectId, region, runnerId).WaitWithContext(ctx) + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating runner", fmt.Sprintf("Runner update waiting: %v", err)) + return + } + + // Map response body to schema + err = mapFields(runnerResp, &model) + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating runner", fmt.Sprintf("Processing API response: %v", err)) + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, model)...) + if resp.Diagnostics.HasError() { + return + } + tflog.Info(ctx, "Intake runner updated") +} + +// Delete deletes the resource and removes the Terraform state on success. +func (r *runnerResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform + var model Model + diags := req.State.Get(ctx, &model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + ctx = core.InitProviderContext(ctx) + + projectId := model.ProjectId.ValueString() + region := model.Region.ValueString() + runnerId := model.RunnerId.ValueString() + ctx = tflog.SetField(ctx, "project_id", projectId) + ctx = tflog.SetField(ctx, "region", region) + ctx = tflog.SetField(ctx, "runner_id", runnerId) + + // Delete existing bar + err := r.client.DeleteIntakeRunner(ctx, projectId, region, runnerId).Execute() + if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + tflog.Info(ctx, "Intake runner already deleted") + return + } + core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting runner", fmt.Sprintf("Calling API: %v", err)) + return + } + + ctx = core.LogResponse(ctx) + + // Wait for the delete operation to complete + _, err = wait.DeleteIntakeRunnerWaitHandler(ctx, r.client, projectId, region, runnerId).WaitWithContext(ctx) + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting runner", fmt.Sprintf("Runner deletion waiting: %v", err)) + return + } + + tflog.Info(ctx, "Intake runner deleted") +} + +// ImportState imports a resource into the Terraform state on success. +// The expected format of the Intake runner resource import identifier is: [project_id],[region],[runner_id] +func (r *runnerResource) 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 intake runner", + fmt.Sprintf("Expected import identifier with format [project_id],[region],[runner_id], got %q", req.ID), + ) + return + } + + utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{ + "project_id": idParts[0], + "region": idParts[1], + "runner_id": idParts[2], + }) + + tflog.Info(ctx, "Intake runner state imported") +} + +// Maps runner fields to the provider internal model +func mapFields(runnerResp *intake.IntakeRunnerResponse, model *Model) error { + if runnerResp == nil { + return fmt.Errorf("response input is nil") + } + if model == nil { + return fmt.Errorf("model input is nil") + } + + var runnerId string + if runnerResp.Id != nil { + runnerId = *runnerResp.Id + } + + model.Id = utils.BuildInternalTerraformId( + model.ProjectId.ValueString(), + model.Region.ValueString(), + runnerId, + ) + + if runnerResp.Labels == nil { + model.Labels = types.MapValueMust(types.StringType, map[string]attr.Value{}) + } else { + labels, diags := types.MapValueFrom(context.Background(), types.StringType, runnerResp.Labels) + if diags.HasError() { + return fmt.Errorf("converting labels: %w", core.DiagsToError(diags)) + } + model.Labels = labels + } + + if runnerResp.Id != nil && *runnerResp.Id == "" { + model.RunnerId = types.StringNull() + } else { + model.RunnerId = types.StringPointerValue(runnerResp.Id) + } + model.Name = types.StringPointerValue(runnerResp.DisplayName) + if runnerResp.Description == nil { + model.Description = types.StringValue("") + } else { + model.Description = types.StringPointerValue(runnerResp.Description) + } + model.MaxMessageSizeKiB = types.Int64PointerValue(runnerResp.MaxMessageSizeKiB) + model.MaxMessagesPerHour = types.Int64PointerValue(runnerResp.MaxMessagesPerHour) + return nil +} + +// Build CreateBarPayload from provider's model +func toCreatePayload(model *Model) (*intake.CreateIntakeRunnerPayload, error) { + if model == nil { + return nil, fmt.Errorf("nil model") + } + + var labels map[string]string + if !model.Labels.IsNull() && !model.Labels.IsUnknown() { + diags := model.Labels.ElementsAs(context.Background(), &labels, false) + if diags.HasError() { + return nil, fmt.Errorf("converting labels: %w", core.DiagsToError(diags)) + } + } + + var labelsPtr *map[string]string + if len(labels) > 0 { + labelsPtr = &labels + } + + return &intake.CreateIntakeRunnerPayload{ + Description: conversion.StringValueToPointer(model.Description), + DisplayName: conversion.StringValueToPointer(model.Name), + Labels: labelsPtr, + MaxMessageSizeKiB: conversion.Int64ValueToPointer(model.MaxMessageSizeKiB), + MaxMessagesPerHour: conversion.Int64ValueToPointer(model.MaxMessagesPerHour), + }, nil +} + +func toUpdatePayload(model, state *Model) (*intake.UpdateIntakeRunnerPayload, error) { + if model == nil { + return nil, fmt.Errorf("model is nil") + } + if state == nil { + return nil, fmt.Errorf("state is nil") + } + + payload := &intake.UpdateIntakeRunnerPayload{} + + if !model.Name.IsUnknown() { + payload.DisplayName = conversion.StringValueToPointer(model.Name) + } + + if !model.MaxMessageSizeKiB.IsUnknown() { + payload.MaxMessageSizeKiB = conversion.Int64ValueToPointer(model.MaxMessageSizeKiB) + } + + if !model.MaxMessagesPerHour.IsUnknown() { + payload.MaxMessagesPerHour = conversion.Int64ValueToPointer(model.MaxMessagesPerHour) + } + + // Handle optional fields + if !model.Description.IsUnknown() || model.Description.IsNull() { + if model.Description.IsNull() { + payload.Description = sdkUtils.Ptr("") + } else { + payload.Description = conversion.StringValueToPointer(model.Description) + } + } + + var labels map[string]string + if !model.Labels.IsUnknown() { + if model.Labels.IsNull() { + labels = map[string]string{} + payload.Labels = &labels + } else { + diags := model.Labels.ElementsAs(context.Background(), &labels, false) + if diags.HasError() { + return nil, fmt.Errorf("failed to convert labels: %w", core.DiagsToError(diags)) + } + payload.Labels = &labels + } + } + + return payload, nil +} diff --git a/stackit/internal/services/intake/runner/resource_acc_test.go b/stackit/internal/services/intake/runner/resource_acc_test.go new file mode 100644 index 000000000..7a65bc10a --- /dev/null +++ b/stackit/internal/services/intake/runner/resource_acc_test.go @@ -0,0 +1,160 @@ +package runner_test + +import ( + "context" + "errors" + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + sdkConfig "github.com/stackitcloud/stackit-sdk-go/core/config" + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" + "github.com/stackitcloud/stackit-sdk-go/services/intake" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil" +) + +// intakeRunnerResource is the name of the test resource +const intakeRunnerResource = "stackit_intake_runner.example" + +func TestAccIntakeRunner(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories, + CheckDestroy: testAccCheckIntakeRunnerDestroy, + Steps: []resource.TestStep{ + // create the runner + { + Config: testutil.IntakeProviderConfig() + testAccIntakeRunnerConfigMinimal("example-runner-minimal"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(intakeRunnerResource, "project_id", testutil.ProjectId), + resource.TestCheckResourceAttr(intakeRunnerResource, "name", "example-runner-minimal"), + resource.TestCheckResourceAttr(intakeRunnerResource, "region", "eu01"), + resource.TestCheckResourceAttrSet(intakeRunnerResource, "runner_id"), + resource.TestCheckResourceAttr(intakeRunnerResource, "description", ""), + resource.TestCheckResourceAttr(intakeRunnerResource, "labels.%", "0"), + ), + }, + // update the runner + { + Config: testutil.IntakeProviderConfig() + testAccIntakeRunnerConfigFull("example-runner-full", "An example runner for Intake", 1024, 1100), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(intakeRunnerResource, "name", "example-runner-full"), + resource.TestCheckResourceAttr(intakeRunnerResource, "description", "An example runner for Intake"), + resource.TestCheckResourceAttr(intakeRunnerResource, "max_message_size_kib", "1024"), + resource.TestCheckResourceAttr(intakeRunnerResource, "max_messages_per_hour", "1100"), + resource.TestCheckResourceAttr(intakeRunnerResource, "labels.%", "2"), + resource.TestCheckResourceAttr(intakeRunnerResource, "labels.created_by", "terraform-provider-stackit"), + resource.TestCheckResourceAttr(intakeRunnerResource, "labels.env", "development"), + ), + }, + // importing the runner + { + ResourceName: intakeRunnerResource, + ImportState: true, + ImportStateVerify: true, + }, + // update to remove optional attributes + { + Config: testutil.IntakeProviderConfig() + testAccIntakeRunnerConfigUpdated("example-runner-updated", 1024, 1100), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(intakeRunnerResource, "name", "example-runner-updated"), + resource.TestCheckResourceAttr(intakeRunnerResource, "description", ""), + resource.TestCheckResourceAttr(intakeRunnerResource, "labels.%", "0"), + resource.TestCheckResourceAttr(intakeRunnerResource, "max_message_size_kib", "1024"), + resource.TestCheckResourceAttr(intakeRunnerResource, "max_messages_per_hour", "1100"), + ), + }, + }, + }) +} + +func testAccIntakeRunnerConfigMinimal(name string) string { + return fmt.Sprintf(` + resource "stackit_intake_runner" "example" { + project_id = "%s" + name = "%s" + region = "eu01" + max_message_size_kib = 1024 + max_messages_per_hour = 1000 + } + `, + testutil.ProjectId, + name, + ) +} + +func testAccIntakeRunnerConfigFull(name, description string, maxKib, maxPerHour int) string { + return fmt.Sprintf(` + resource "stackit_intake_runner" "example" { + project_id = "%s" + name = "%s" + description = "%s" + max_message_size_kib = %d + max_messages_per_hour = %d + labels = { + "created_by" = "terraform-provider-stackit" + "env" = "development" + } + region = "eu01" + } + `, + testutil.ProjectId, + name, + description, + maxKib, + maxPerHour, + ) +} + +func testAccIntakeRunnerConfigUpdated(name string, maxKib, maxPerHour int) string { + return fmt.Sprintf(` + resource "stackit_intake_runner" "example" { + project_id = "%s" + name = "%s" + description = "" + max_message_size_kib = %d + max_messages_per_hour = %d + labels = {} + region = "eu01" + } + `, + testutil.ProjectId, + name, + maxKib, + maxPerHour, + ) +} + +func testAccCheckIntakeRunnerDestroy(s *terraform.State) error { + ctx := context.Background() + var client *intake.APIClient + var err error + if testutil.IntakeCustomEndpoint == "" { + client, err = intake.NewAPIClient( + sdkConfig.WithRegion("eu01"), + ) + } else { + client, err = intake.NewAPIClient(sdkConfig.WithEndpoint(testutil.IntakeCustomEndpoint)) + } + if err != nil { + return fmt.Errorf("creating client: %w", err) + } + + for _, rs := range s.RootModule().Resources { + if rs.Type != "stackit_intake_runner" { + continue + } + // Try to find the runner + _, err := client.GetIntakeRunner(ctx, rs.Primary.Attributes["project_id"], rs.Primary.Attributes["region"], rs.Primary.Attributes["runner_id"]).Execute() + if err == nil { + return fmt.Errorf("intake runner with ID %s still exists", rs.Primary.ID) + } + var oapiErr *oapierror.GenericOpenAPIError + if !errors.As(err, &oapiErr) || oapiErr.StatusCode != http.StatusNotFound { + return fmt.Errorf("expected 404 not found, got error: %w", err) + } + } + + return nil +} diff --git a/stackit/internal/services/intake/runner/resource_test.go b/stackit/internal/services/intake/runner/resource_test.go new file mode 100644 index 000000000..921349c64 --- /dev/null +++ b/stackit/internal/services/intake/runner/resource_test.go @@ -0,0 +1,254 @@ +package runner + +import ( + "context" + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/stackitcloud/stackit-sdk-go/core/utils" + "github.com/stackitcloud/stackit-sdk-go/services/intake" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" +) + +func TestMapFields(t *testing.T) { + runnerId := uuid.New().String() + tests := []struct { + description string + input *intake.IntakeRunnerResponse + model *Model + expected *Model + wantErr bool + }{ + { + "success", + &intake.IntakeRunnerResponse{ + Id: utils.Ptr(runnerId), + DisplayName: utils.Ptr("name"), + Description: utils.Ptr("description"), + Labels: &map[string]string{"key": "value"}, + MaxMessageSizeKiB: utils.Ptr(int64(1024)), + MaxMessagesPerHour: utils.Ptr(int64(100)), + }, + &Model{ + ProjectId: types.StringValue("pid"), + Region: types.StringValue("eu01"), + }, + &Model{ + Id: types.StringValue(fmt.Sprintf("pid,eu01,%s", runnerId)), + ProjectId: types.StringValue("pid"), + Region: types.StringValue("eu01"), + RunnerId: types.StringValue(runnerId), + Name: types.StringValue("name"), + Description: types.StringValue("description"), + Labels: types.MapValueMust(types.StringType, map[string]attr.Value{"key": types.StringValue("value")}), + MaxMessageSizeKiB: types.Int64Value(1024), + MaxMessagesPerHour: types.Int64Value(100), + }, + false, + }, + { + "nil input", + nil, + &Model{}, + nil, + true, + }, + { + "nil model", + &intake.IntakeRunnerResponse{}, + nil, + nil, + true, + }, + { + "empty response", + &intake.IntakeRunnerResponse{ + Id: utils.Ptr(""), + Labels: &map[string]string{}, + }, + &Model{ + ProjectId: types.StringValue("pid"), + Region: types.StringValue("eu01"), + }, + &Model{ + Id: types.StringValue("pid,eu01,"), + ProjectId: types.StringValue("pid"), + Region: types.StringValue("eu01"), + RunnerId: types.StringNull(), + Name: types.StringNull(), + Description: types.StringValue(""), + Labels: types.MapValueMust(types.StringType, map[string]attr.Value{}), + MaxMessageSizeKiB: types.Int64Null(), + MaxMessagesPerHour: types.Int64Null(), + }, + false, + }, + } + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + err := mapFields(tt.input, tt.model) + if (err != nil) != tt.wantErr { + t.Errorf("mapFields error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr { + if diff := cmp.Diff(tt.expected, tt.model); diff != "" { + t.Errorf("mapFields mismatch (-want +got):\n%s", diff) + } + } + }) + } +} + +func TestToCreatePayload(t *testing.T) { + tests := []struct { + description string + model *Model + expected *intake.CreateIntakeRunnerPayload + wantErr bool + }{ + { + "success", + &Model{ + Name: types.StringValue("name"), + Description: types.StringValue("description"), + Labels: types.MapValueMust(types.StringType, map[string]attr.Value{"key": types.StringValue("value")}), + MaxMessageSizeKiB: types.Int64Value(1024), + MaxMessagesPerHour: types.Int64Value(100), + }, + &intake.CreateIntakeRunnerPayload{ + DisplayName: utils.Ptr("name"), + Description: utils.Ptr("description"), + Labels: utils.Ptr(map[string]string{"key": "value"}), + MaxMessageSizeKiB: utils.Ptr(int64(1024)), + MaxMessagesPerHour: utils.Ptr(int64(100)), + }, + false, + }, + { + "nil model", + nil, + nil, + true, + }, + { + "empty model", + &Model{}, + &intake.CreateIntakeRunnerPayload{ + DisplayName: nil, + Description: nil, + Labels: nil, + MaxMessageSizeKiB: nil, + MaxMessagesPerHour: nil, + }, + false, + }, + } + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + payload, err := toCreatePayload(tt.model) + if (err != nil) != tt.wantErr { + t.Errorf("toCreatePayload error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr { + if diff := cmp.Diff(tt.expected, payload); diff != "" { + t.Errorf("toCreatePayload mismatch (-want +got):\n%s", diff) + } + } + }) + } +} + +func TestToUpdatePayload(t *testing.T) { + tests := []struct { + description string + model *Model + state *Model + expected *intake.UpdateIntakeRunnerPayload + wantErr bool + }{ + { + "success", + &Model{ + Name: types.StringValue("name"), + Description: types.StringValue("description"), + Labels: types.MapValueMust(types.StringType, map[string]attr.Value{"key": types.StringValue("value")}), + MaxMessageSizeKiB: types.Int64Value(1024), + MaxMessagesPerHour: types.Int64Value(100), + }, + &Model{}, + &intake.UpdateIntakeRunnerPayload{ + DisplayName: conversion.StringValueToPointer(types.StringValue("name")), + Description: conversion.StringValueToPointer(types.StringValue("description")), + Labels: utils.Ptr(map[string]string{"key": "value"}), + MaxMessageSizeKiB: conversion.Int64ValueToPointer(types.Int64Value(1024)), + MaxMessagesPerHour: conversion.Int64ValueToPointer(types.Int64Value(100)), + }, + false, + }, + { + "nil model", + nil, + &Model{}, + nil, + true, + }, + { + "nil state", + &Model{}, + nil, + nil, + true, + }, + { + "empty model", + &Model{}, + &Model{}, + &intake.UpdateIntakeRunnerPayload{ + Description: utils.Ptr(""), + Labels: &map[string]string{}, + }, + false, + }, + { + "unknown values", + &Model{ + Name: types.StringUnknown(), + Description: types.StringUnknown(), + Labels: types.MapUnknown(types.StringType), + MaxMessageSizeKiB: types.Int64Unknown(), + MaxMessagesPerHour: types.Int64Unknown(), + }, + &Model{}, + &intake.UpdateIntakeRunnerPayload{}, + false, + }, + } + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + var labels map[string]string + if tt.model != nil && !tt.model.Labels.IsNull() && !tt.model.Labels.IsUnknown() { + diags := tt.model.Labels.ElementsAs(context.Background(), &labels, false) + if diags.HasError() { + t.Fatalf("error preparing test %v", diags) + } + } + + payload, err := toUpdatePayload(tt.model, tt.state) + if (err != nil) != tt.wantErr { + t.Errorf("toUpdatePayload error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr { + if diff := cmp.Diff(tt.expected, payload); diff != "" { + t.Errorf("toUpdatePayload mismatch (-want +got):\n%s", diff) + } + } + }) + } +} diff --git a/stackit/internal/services/intake/utils/utils.go b/stackit/internal/services/intake/utils/utils.go new file mode 100644 index 000000000..b6357b496 --- /dev/null +++ b/stackit/internal/services/intake/utils/utils.go @@ -0,0 +1,31 @@ +package utils + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/stackitcloud/stackit-sdk-go/core/config" + "github.com/stackitcloud/stackit-sdk-go/services/intake" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils" +) + +func ConfigureClient(ctx context.Context, providerData *core.ProviderData, diags *diag.Diagnostics) *intake.APIClient { + apiClientConfigOptions := []config.ConfigurationOption{ + config.WithCustomAuth(providerData.RoundTripper), + utils.UserAgentConfigOption(providerData.Version), + } + if providerData.IntakeCustomEndpoint != "" { + apiClientConfigOptions = append(apiClientConfigOptions, config.WithEndpoint(providerData.IntakeCustomEndpoint)) + } else { + apiClientConfigOptions = append(apiClientConfigOptions, config.WithRegion(providerData.GetRegion())) + } + apiClient, err := intake.NewAPIClient(apiClientConfigOptions...) + if err != nil { + core.LogAndAddError(ctx, diags, "Error configuring API client", fmt.Sprintf("Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration", err)) + return nil + } + + return apiClient +} diff --git a/stackit/internal/testutil/testutil.go b/stackit/internal/testutil/testutil.go index 5be2e7281..553e54774 100644 --- a/stackit/internal/testutil/testutil.go +++ b/stackit/internal/testutil/testutil.go @@ -98,6 +98,7 @@ var ( ServiceAccountCustomEndpoint = customEndpointConfig{envVarName: "TF_ACC_SERVICE_ACCOUNT_CUSTOM_ENDPOINT", providerName: "service_account_custom_endpoint"} TokenCustomEndpoint = customEndpointConfig{envVarName: "TF_ACC_TOKEN_CUSTOM_ENDPOINT", providerName: "token_custom_endpoint"} SKECustomEndpoint = customEndpointConfig{envVarName: "TF_ACC_SKE_CUSTOM_ENDPOINT", providerName: "ske_custom_endpoint"} + IntakeCustomEndpoint = customEndpointConfig{envVarName: "TF_ACC_INTAKE_CUSTOM_ENDPOINT", providerName: "intake_custom_endpoint"} allCustomEndpoints = []customEndpointConfig{ ALBCustomEndpoint, diff --git a/stackit/provider.go b/stackit/provider.go index 1dc8da94c..a1926f2a8 100644 --- a/stackit/provider.go +++ b/stackit/provider.go @@ -62,6 +62,7 @@ import ( iaasVolume "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/volume" iaasVolumeAttach "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/volumeattach" iamRoleBindingsV1 "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iam/rolebindings/v1" + intakeRunner "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/intake/runner" kmsKey "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/kms/key" kmsKeyRing "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/kms/keyring" kmsWrappingKey "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/kms/wrapping-key" @@ -173,6 +174,7 @@ type providerModel struct { EdgeCloudCustomEndpoint types.String `tfsdk:"edgecloud_custom_endpoint"` GitCustomEndpoint types.String `tfsdk:"git_custom_endpoint"` IaaSCustomEndpoint types.String `tfsdk:"iaas_custom_endpoint"` + IntakeCustomEndpoint types.String `tfsdk:"intake_custom_endpoint"` KmsCustomEndpoint types.String `tfsdk:"kms_custom_endpoint"` LoadBalancerCustomEndpoint types.String `tfsdk:"loadbalancer_custom_endpoint"` LogMeCustomEndpoint types.String `tfsdk:"logme_custom_endpoint"` @@ -362,6 +364,10 @@ func (p *Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *pro Optional: true, Description: descriptions["iaas_custom_endpoint"], }, + "intake_custom_endpoint": schema.StringAttribute{ + Optional: true, + Description: descriptions["intake_custom_endpoint"], + }, "kms_custom_endpoint": schema.StringAttribute{ Optional: true, Description: descriptions["kms_custom_endpoint"], @@ -521,6 +527,7 @@ func (p *Provider) Configure(ctx context.Context, req provider.ConfigureRequest, setStringField(providerConfig.EdgeCloudCustomEndpoint, func(v string) { providerData.EdgeCloudCustomEndpoint = v }) setStringField(providerConfig.GitCustomEndpoint, func(v string) { providerData.GitCustomEndpoint = v }) setStringField(providerConfig.IaaSCustomEndpoint, func(v string) { providerData.IaaSCustomEndpoint = v }) + setStringField(providerConfig.IntakeCustomEndpoint, func(v string) { providerData.IntakeCustomEndpoint = v }) setStringField(providerConfig.KmsCustomEndpoint, func(v string) { providerData.KMSCustomEndpoint = v }) setStringField(providerConfig.LoadBalancerCustomEndpoint, func(v string) { providerData.LoadBalancerCustomEndpoint = v }) setStringField(providerConfig.LogMeCustomEndpoint, func(v string) { providerData.LogMeCustomEndpoint = v }) @@ -743,6 +750,7 @@ func (p *Provider) Resources(_ context.Context) []func() resource.Resource { iaasSecurityGroupRule.NewSecurityGroupRuleResource, iaasRoutingTable.NewRoutingTableResource, iaasRoutingTableRoute.NewRoutingTableRouteResource, + intakeRunner.NewRunnerResource, kmsKey.NewKeyResource, kmsKeyRing.NewKeyRingResource, kmsWrappingKey.NewWrappingKeyResource, From 33dad61dcf294c5357ab3cebf020e4fbc49cc137 Mon Sep 17 00:00:00 2001 From: Devansh Thakur Date: Mon, 8 Dec 2025 09:07:58 +0100 Subject: [PATCH 02/44] added example of intake runner --- .../resources/stackit_intake_runner/resource.tf | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 examples/resources/stackit_intake_runner/resource.tf diff --git a/examples/resources/stackit_intake_runner/resource.tf b/examples/resources/stackit_intake_runner/resource.tf new file mode 100644 index 000000000..ceda583ff --- /dev/null +++ b/examples/resources/stackit_intake_runner/resource.tf @@ -0,0 +1,17 @@ +resource "stackit_intake_runner" "example" { + project_id = var.project_id + name = "example-runner-full" + description = "An example runner for STACKIT Intake" + max_message_size_kib = 2048 + max_messages_per_hour = 1500 + labels = { + "created_by" = "terraform-example" + "env" = "production" + } + region = var.region +} + +import { + to = stackit_intake_runner.example + id = "${var.project_id},${var.region},${var.runner_id}" +} \ No newline at end of file From ce2a6a7c45512f9d4c0dd917e8be50e2dd6e0740 Mon Sep 17 00:00:00 2001 From: Devansh Thakur Date: Tue, 16 Dec 2025 22:19:46 +0100 Subject: [PATCH 03/44] added datasource for intake runner --- .../services/intake/runner/data_source.go | 167 ++++++++++++++++++ .../services/intake/runner/resource.go | 2 +- stackit/provider.go | 1 + 3 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 stackit/internal/services/intake/runner/data_source.go diff --git a/stackit/internal/services/intake/runner/data_source.go b/stackit/internal/services/intake/runner/data_source.go new file mode 100644 index 000000000..84728b714 --- /dev/null +++ b/stackit/internal/services/intake/runner/data_source.go @@ -0,0 +1,167 @@ +package runner + +import ( + "context" + "errors" + "fmt" + "net/http" + + "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-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" + intakeUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/intake/utils" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate" + + "github.com/stackitcloud/stackit-sdk-go/services/intake" +) + +// Ensure the implementation satisfies the expected interfaces +var ( + _ datasource.DataSource = &runnerDataSource{} +) + +// NewRunnerDataSource is a helper function to simplify the provider implementation +func NewRunnerDataSource() datasource.DataSource { + return &runnerDataSource{} +} + +type runnerDataSource struct { + client *intake.APIClient +} + +func (r *runnerDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_intake_runner" +} + +// Configure adds the provider configured client to the data source +func (r *runnerDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + providerData, ok := conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics) + if !ok { + return + } + + apiClient := intakeUtils.ConfigureClient(ctx, &providerData, &resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + r.client = apiClient + tflog.Info(ctx, "Intake runner client configured for data source") +} + +// Schema defines the schema for the data source +func (r *runnerDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + descriptions := map[string]string{ + "main": "Datasource for STACKIT Intake Runner.", + "id": "Terraform's internal resource identifier. It is structured as \"`project_id`,`region`,`runner_id`\".", + "project_id": "STACKIT Project ID to which the runner is associated.", + "runner_id": "The runner ID.", + "name": "The name of the runner.", + "region": "The resource region.", + "description": "The description of the runner.", + "labels": "User-defined labels.", + "max_message_size_kib": "The maximum message size in KiB.", + "max_messages_per_hour": "The maximum number of messages per hour.", + } + + resp.Schema = schema.Schema{ + Description: descriptions["main"], + 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(), + }, + }, + "runner_id": schema.StringAttribute{ + Description: descriptions["runner_id"], + Required: true, + Validators: []validator.String{ + validate.UUID(), + validate.NoSeparator(), + }, + }, + "region": schema.StringAttribute{ + Description: descriptions["region"], + Required: true, + }, + "name": schema.StringAttribute{ + Description: descriptions["name"], + Computed: true, + }, + "description": schema.StringAttribute{ + Description: descriptions["description"], + Computed: true, + }, + "labels": schema.MapAttribute{ + Description: descriptions["labels"], + ElementType: types.StringType, + Computed: true, + }, + "max_message_size_kib": schema.Int64Attribute{ + Description: descriptions["max_message_size_kib"], + Computed: true, + }, + "max_messages_per_hour": schema.Int64Attribute{ + Description: descriptions["max_messages_per_hour"], + Computed: true, + }, + }, + } +} + +// Read refreshes the Terraform state with the latest data. +func (r *runnerDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform + var model Model + resp.Diagnostics.Append(req.Config.Get(ctx, &model)...) + if resp.Diagnostics.HasError() { + return + } + + ctx = core.InitProviderContext(ctx) + + projectId := model.ProjectId.ValueString() + region := model.Region.ValueString() + runnerId := model.RunnerId.ValueString() + ctx = tflog.SetField(ctx, "project_id", projectId) + ctx = tflog.SetField(ctx, "region", region) + ctx = tflog.SetField(ctx, "runner_id", runnerId) + + runnerResp, err := r.client.GetIntakeRunner(ctx, projectId, region, runnerId).Execute() + if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) { + if oapiErr.StatusCode == http.StatusNotFound { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading runner", fmt.Sprintf("Runner with ID %s not found in project %s and region %s", runnerId, projectId, region)) + return + } + } + core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading runner", fmt.Sprintf("Calling API: %v", err)) + return + } + + ctx = core.LogResponse(ctx) + + err = mapFields(runnerResp, &model) + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading runner", fmt.Sprintf("Processing API payload: %v", err)) + return + } + + // Set refreshed state + resp.Diagnostics.Append(resp.State.Set(ctx, model)...) + if resp.Diagnostics.HasError() { + return + } + tflog.Info(ctx, "Intake runner read") +} diff --git a/stackit/internal/services/intake/runner/resource.go b/stackit/internal/services/intake/runner/resource.go index d9de6667a..8c7a6b93a 100644 --- a/stackit/internal/services/intake/runner/resource.go +++ b/stackit/internal/services/intake/runner/resource.go @@ -115,7 +115,7 @@ func (r *runnerResource) ModifyPlan(ctx context.Context, req resource.ModifyPlan func (r *runnerResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { descriptions := map[string]string{ "main": "Manages STACKIT Intake Runner.", - "id": "Terraform's internal resource identifier. It is structured as \"`project_id`,`runner_id`\".", + "id": "Terraform's internal resource identifier. It is structured as \"`project_id`,`region`,`runner_id`\".", "project_id": "STACKIT Project ID to which the runner is associated.", "runner_id": "The runner ID.", "name": "The name of the runner.", diff --git a/stackit/provider.go b/stackit/provider.go index a1926f2a8..2e9754cbc 100644 --- a/stackit/provider.go +++ b/stackit/provider.go @@ -655,6 +655,7 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource iaasRoutingTables.NewRoutingTablesDataSource, iaasRoutingTableRoutes.NewRoutingTableRoutesDataSource, iaasSecurityGroupRule.NewSecurityGroupRuleDataSource, + intakeRunner.NewRunnerDataSource, kmsKey.NewKeyDataSource, kmsKeyRing.NewKeyRingDataSource, kmsWrappingKey.NewWrappingKeyDataSource, From fa616c6fe7a040144773baf936759c9c95405b4d Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 22 Jan 2026 11:33:04 +0100 Subject: [PATCH 04/44] Add docs --- docs/data-sources/intake_runner.md | 31 ++++++++++++++++++++++++++++++ docs/resources/intake_runner.md | 24 +++++++++++++++++++++-- 2 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 docs/data-sources/intake_runner.md diff --git a/docs/data-sources/intake_runner.md b/docs/data-sources/intake_runner.md new file mode 100644 index 000000000..d914e5d61 --- /dev/null +++ b/docs/data-sources/intake_runner.md @@ -0,0 +1,31 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "stackit_intake_runner Data Source - stackit" +subcategory: "" +description: |- + Datasource for STACKIT Intake Runner. +--- + +# stackit_intake_runner (Data Source) + +Datasource for STACKIT Intake Runner. + + + + +## Schema + +### Required + +- `project_id` (String) STACKIT Project ID to which the runner is associated. +- `region` (String) The resource region. +- `runner_id` (String) The runner ID. + +### Read-Only + +- `description` (String) The description of the runner. +- `id` (String) Terraform's internal resource identifier. It is structured as "`project_id`,`region`,`runner_id`". +- `labels` (Map of String) User-defined labels. +- `max_message_size_kib` (Number) The maximum message size in KiB. +- `max_messages_per_hour` (Number) The maximum number of messages per hour. +- `name` (String) The name of the runner. diff --git a/docs/resources/intake_runner.md b/docs/resources/intake_runner.md index 65a5c3206..9a246d535 100644 --- a/docs/resources/intake_runner.md +++ b/docs/resources/intake_runner.md @@ -10,7 +10,27 @@ description: |- Manages STACKIT Intake Runner. - +## Example Usage + +```terraform +resource "stackit_intake_runner" "example" { + project_id = var.project_id + name = "example-runner-full" + description = "An example runner for STACKIT Intake" + max_message_size_kib = 2048 + max_messages_per_hour = 1500 + labels = { + "created_by" = "terraform-example" + "env" = "production" + } + region = var.region +} + +import { + to = stackit_intake_runner.example + id = "${var.project_id},${var.region},${var.runner_id}" +} +``` ## Schema @@ -30,5 +50,5 @@ Manages STACKIT Intake Runner. ### Read-Only -- `id` (String) Terraform's internal resource identifier. It is structured as "`project_id`,`runner_id`". +- `id` (String) Terraform's internal resource identifier. It is structured as "`project_id`,`region`,`runner_id`". - `runner_id` (String) The runner ID. From 80ac6c788224edca667e531f7b44de0fa31783c6 Mon Sep 17 00:00:00 2001 From: BipBopBipBop Date: Thu, 22 Jan 2026 11:56:50 +0100 Subject: [PATCH 05/44] Update stackit/internal/services/intake/runner/data_source.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ruben Hönle --- stackit/internal/services/intake/runner/data_source.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stackit/internal/services/intake/runner/data_source.go b/stackit/internal/services/intake/runner/data_source.go index 84728b714..200fe0c9c 100644 --- a/stackit/internal/services/intake/runner/data_source.go +++ b/stackit/internal/services/intake/runner/data_source.go @@ -45,7 +45,7 @@ func (r *runnerDataSource) Configure(ctx context.Context, req datasource.Configu return } - apiClient := intakeUtils.ConfigureClient(ctx, &providerData, &resp.Diagnostics) + r.client := intakeUtils.ConfigureClient(ctx, &providerData, &resp.Diagnostics) if resp.Diagnostics.HasError() { return } From 2f93301d1ee9b926d1c01bc3fd4537fb85425f57 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 22 Jan 2026 17:12:32 +0100 Subject: [PATCH 06/44] Remove region field --- docs/data-sources/intake_runner.md | 1 - docs/index.md | 2 +- .../services/intake/runner/data_source.go | 7 +------ .../services/intake/runner/resource_acc_test.go | 15 +++++++-------- 4 files changed, 9 insertions(+), 16 deletions(-) diff --git a/docs/data-sources/intake_runner.md b/docs/data-sources/intake_runner.md index d914e5d61..60950b2f1 100644 --- a/docs/data-sources/intake_runner.md +++ b/docs/data-sources/intake_runner.md @@ -18,7 +18,6 @@ Datasource for STACKIT Intake Runner. ### Required - `project_id` (String) STACKIT Project ID to which the runner is associated. -- `region` (String) The resource region. - `runner_id` (String) The runner ID. ### Read-Only diff --git a/docs/index.md b/docs/index.md index 0b10f72ce..300fcbd63 100644 --- a/docs/index.md +++ b/docs/index.md @@ -175,7 +175,7 @@ Note: AWS specific checks must be skipped as they do not work on STACKIT. For de - `experiments` (List of String) Enables experiments. These are unstable features without official support. More information can be found in the README. Available Experiments: iam, routing-tables, network - `git_custom_endpoint` (String) Custom endpoint for the Git service - `iaas_custom_endpoint` (String) Custom endpoint for the IaaS service -- `intake_custom_endpoint` (String) +- `intake_custom_endpoint` (String) Custom endpoint for the Intake service - `kms_custom_endpoint` (String) Custom endpoint for the KMS service - `loadbalancer_custom_endpoint` (String) Custom endpoint for the Load Balancer service - `logme_custom_endpoint` (String) Custom endpoint for the LogMe service diff --git a/stackit/internal/services/intake/runner/data_source.go b/stackit/internal/services/intake/runner/data_source.go index 200fe0c9c..ffa1aa1d1 100644 --- a/stackit/internal/services/intake/runner/data_source.go +++ b/stackit/internal/services/intake/runner/data_source.go @@ -45,7 +45,7 @@ func (r *runnerDataSource) Configure(ctx context.Context, req datasource.Configu return } - r.client := intakeUtils.ConfigureClient(ctx, &providerData, &resp.Diagnostics) + apiClient := intakeUtils.ConfigureClient(ctx, &providerData, &resp.Diagnostics) if resp.Diagnostics.HasError() { return } @@ -61,7 +61,6 @@ func (r *runnerDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, "project_id": "STACKIT Project ID to which the runner is associated.", "runner_id": "The runner ID.", "name": "The name of the runner.", - "region": "The resource region.", "description": "The description of the runner.", "labels": "User-defined labels.", "max_message_size_kib": "The maximum message size in KiB.", @@ -91,10 +90,6 @@ func (r *runnerDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, validate.NoSeparator(), }, }, - "region": schema.StringAttribute{ - Description: descriptions["region"], - Required: true, - }, "name": schema.StringAttribute{ Description: descriptions["name"], Computed: true, diff --git a/stackit/internal/services/intake/runner/resource_acc_test.go b/stackit/internal/services/intake/runner/resource_acc_test.go index 7a65bc10a..948c879ad 100644 --- a/stackit/internal/services/intake/runner/resource_acc_test.go +++ b/stackit/internal/services/intake/runner/resource_acc_test.go @@ -29,7 +29,6 @@ func TestAccIntakeRunner(t *testing.T) { Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(intakeRunnerResource, "project_id", testutil.ProjectId), resource.TestCheckResourceAttr(intakeRunnerResource, "name", "example-runner-minimal"), - resource.TestCheckResourceAttr(intakeRunnerResource, "region", "eu01"), resource.TestCheckResourceAttrSet(intakeRunnerResource, "runner_id"), resource.TestCheckResourceAttr(intakeRunnerResource, "description", ""), resource.TestCheckResourceAttr(intakeRunnerResource, "labels.%", "0"), @@ -74,7 +73,6 @@ func testAccIntakeRunnerConfigMinimal(name string) string { resource "stackit_intake_runner" "example" { project_id = "%s" name = "%s" - region = "eu01" max_message_size_kib = 1024 max_messages_per_hour = 1000 } @@ -96,7 +94,6 @@ func testAccIntakeRunnerConfigFull(name, description string, maxKib, maxPerHour "created_by" = "terraform-provider-stackit" "env" = "development" } - region = "eu01" } `, testutil.ProjectId, @@ -116,7 +113,6 @@ func testAccIntakeRunnerConfigUpdated(name string, maxKib, maxPerHour int) strin max_message_size_kib = %d max_messages_per_hour = %d labels = {} - region = "eu01" } `, testutil.ProjectId, @@ -131,9 +127,7 @@ func testAccCheckIntakeRunnerDestroy(s *terraform.State) error { var client *intake.APIClient var err error if testutil.IntakeCustomEndpoint == "" { - client, err = intake.NewAPIClient( - sdkConfig.WithRegion("eu01"), - ) + client, err = intake.NewAPIClient() } else { client, err = intake.NewAPIClient(sdkConfig.WithEndpoint(testutil.IntakeCustomEndpoint)) } @@ -148,7 +142,12 @@ func testAccCheckIntakeRunnerDestroy(s *terraform.State) error { // Try to find the runner _, err := client.GetIntakeRunner(ctx, rs.Primary.Attributes["project_id"], rs.Primary.Attributes["region"], rs.Primary.Attributes["runner_id"]).Execute() if err == nil { - return fmt.Errorf("intake runner with ID %s still exists", rs.Primary.ID) + err = client.DeleteIntakeRunner(ctx, rs.Primary.Attributes["project_id"], rs.Primary.Attributes["region"], rs.Primary.Attributes["runner_id"]).Execute() + if err != nil { + return fmt.Errorf("intake runner with ID %s still existed, got an error removing", rs.Primary.ID, err) + } + + return fmt.Errorf("intake runner with ID %s still existed", rs.Primary.ID) } var oapiErr *oapierror.GenericOpenAPIError if !errors.As(err, &oapiErr) || oapiErr.StatusCode != http.StatusNotFound { From e8d2af3bb3c95c01ff3c6613ec356103db398723 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 22 Jan 2026 17:12:50 +0100 Subject: [PATCH 07/44] Add description to custom endpoint --- stackit/provider.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stackit/provider.go b/stackit/provider.go index 2e9754cbc..26db9c2b2 100644 --- a/stackit/provider.go +++ b/stackit/provider.go @@ -230,6 +230,7 @@ func (p *Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *pro "edgecloud_custom_endpoint": "Custom endpoint for the Edge Cloud service", "git_custom_endpoint": "Custom endpoint for the Git service", "iaas_custom_endpoint": "Custom endpoint for the IaaS service", + "intake_custom_endpoint": "Custom endpoint for the Intake service", "kms_custom_endpoint": "Custom endpoint for the KMS service", "mongodbflex_custom_endpoint": "Custom endpoint for the MongoDB Flex service", "modelserving_custom_endpoint": "Custom endpoint for the AI Model Serving service", @@ -258,6 +259,7 @@ func (p *Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *pro "enable_beta_resources": "Enable beta resources. Default is false.", "experiments": fmt.Sprintf("Enables experiments. These are unstable features without official support. More information can be found in the README. Available Experiments: %v", strings.Join(features.AvailableExperiments, ", ")), } + resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ "credentials_path": schema.StringAttribute{ From da29820243f1cda172fca78e26dac7089f2aa717 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 22 Jan 2026 17:47:36 +0100 Subject: [PATCH 08/44] Adjust fields for resource.go (still missing region) --- .../services/intake/runner/resource.go | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/stackit/internal/services/intake/runner/resource.go b/stackit/internal/services/intake/runner/resource.go index 8c7a6b93a..4d2a208b1 100644 --- a/stackit/internal/services/intake/runner/resource.go +++ b/stackit/internal/services/intake/runner/resource.go @@ -489,18 +489,8 @@ func toUpdatePayload(model, state *Model) (*intake.UpdateIntakeRunnerPayload, er } payload := &intake.UpdateIntakeRunnerPayload{} - - if !model.Name.IsUnknown() { - payload.DisplayName = conversion.StringValueToPointer(model.Name) - } - - if !model.MaxMessageSizeKiB.IsUnknown() { - payload.MaxMessageSizeKiB = conversion.Int64ValueToPointer(model.MaxMessageSizeKiB) - } - - if !model.MaxMessagesPerHour.IsUnknown() { - payload.MaxMessagesPerHour = conversion.Int64ValueToPointer(model.MaxMessagesPerHour) - } + payload.MaxMessageSizeKiB = conversion.Int64ValueToPointer(model.MaxMessageSizeKiB) + payload.MaxMessagesPerHour = conversion.Int64ValueToPointer(model.MaxMessagesPerHour) // Handle optional fields if !model.Description.IsUnknown() || model.Description.IsNull() { @@ -513,10 +503,7 @@ func toUpdatePayload(model, state *Model) (*intake.UpdateIntakeRunnerPayload, er var labels map[string]string if !model.Labels.IsUnknown() { - if model.Labels.IsNull() { - labels = map[string]string{} - payload.Labels = &labels - } else { + if !model.Labels.IsNull() { diags := model.Labels.ElementsAs(context.Background(), &labels, false) if diags.HasError() { return nil, fmt.Errorf("failed to convert labels: %w", core.DiagsToError(diags)) From 57ff91b51cb4842581cfaaf7d7edb58461963d71 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 13:33:43 +0100 Subject: [PATCH 09/44] Reintroduce region as optional field in data resource --- .../services/intake/runner/data_source.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/stackit/internal/services/intake/runner/data_source.go b/stackit/internal/services/intake/runner/data_source.go index ffa1aa1d1..f44dfaf69 100644 --- a/stackit/internal/services/intake/runner/data_source.go +++ b/stackit/internal/services/intake/runner/data_source.go @@ -31,7 +31,8 @@ func NewRunnerDataSource() datasource.DataSource { } type runnerDataSource struct { - client *intake.APIClient + client *intake.APIClient + providerData core.ProviderData } func (r *runnerDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { @@ -40,12 +41,13 @@ func (r *runnerDataSource) Metadata(_ context.Context, req datasource.MetadataRe // Configure adds the provider configured client to the data source func (r *runnerDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { - providerData, ok := conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics) + var ok bool + r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics) if !ok { return } - apiClient := intakeUtils.ConfigureClient(ctx, &providerData, &resp.Diagnostics) + apiClient := intakeUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics) if resp.Diagnostics.HasError() { return } @@ -65,6 +67,7 @@ func (r *runnerDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, "labels": "User-defined labels.", "max_message_size_kib": "The maximum message size in KiB.", "max_messages_per_hour": "The maximum number of messages per hour.", + "region": "The resource region. If not defined, the provider region is used.", } resp.Schema = schema.Schema{ @@ -111,6 +114,10 @@ func (r *runnerDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, Description: descriptions["max_messages_per_hour"], Computed: true, }, + "region": schema.StringAttribute{ + Optional: true, + Description: descriptions["region"], + }, }, } } @@ -126,7 +133,7 @@ func (r *runnerDataSource) Read(ctx context.Context, req datasource.ReadRequest, ctx = core.InitProviderContext(ctx) projectId := model.ProjectId.ValueString() - region := model.Region.ValueString() + region := r.providerData.GetRegionWithOverride(model.Region) runnerId := model.RunnerId.ValueString() ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "region", region) @@ -147,7 +154,7 @@ func (r *runnerDataSource) Read(ctx context.Context, req datasource.ReadRequest, ctx = core.LogResponse(ctx) - err = mapFields(runnerResp, &model) + err = mapFields(runnerResp, &model, region) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading runner", fmt.Sprintf("Processing API payload: %v", err)) return From ce5fcde779bffae0027bdced398a98c01a585c86 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 13:51:35 +0100 Subject: [PATCH 10/44] Adjust resource implementation and implement review comments --- .../services/intake/runner/resource.go | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/stackit/internal/services/intake/runner/resource.go b/stackit/internal/services/intake/runner/resource.go index 4d2a208b1..40b02599b 100644 --- a/stackit/internal/services/intake/runner/resource.go +++ b/stackit/internal/services/intake/runner/resource.go @@ -7,7 +7,6 @@ import ( "net/http" "strings" - "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" @@ -18,7 +17,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/stackitcloud/stackit-sdk-go/core/oapierror" - sdkUtils "github.com/stackitcloud/stackit-sdk-go/core/utils" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" intakeUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/intake/utils" @@ -42,12 +40,12 @@ type Model struct { Id types.String `tfsdk:"id"` // needed by TF ProjectId types.String `tfsdk:"project_id"` RunnerId types.String `tfsdk:"runner_id"` - Region types.String `tfsdk:"region"` Name types.String `tfsdk:"name"` Description types.String `tfsdk:"description"` Labels types.Map `tfsdk:"labels"` MaxMessageSizeKiB types.Int64 `tfsdk:"max_message_size_kib"` MaxMessagesPerHour types.Int64 `tfsdk:"max_messages_per_hour"` + Region types.String `tfsdk:"region"` } // NewRunnerResource is a helper function to simplify the provider implementation. @@ -132,12 +130,16 @@ func (r *runnerResource) Schema(_ context.Context, _ resource.SchemaRequest, res "id": schema.StringAttribute{ Description: descriptions["id"], Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, "project_id": schema.StringAttribute{ Description: descriptions["project_id"], Required: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), + stringplanmodifier.UseStateForUnknown(), }, Validators: []validator.String{ validate.UUID(), @@ -154,6 +156,9 @@ func (r *runnerResource) Schema(_ context.Context, _ resource.SchemaRequest, res "name": schema.StringAttribute{ Description: descriptions["name"], Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, "description": schema.StringAttribute{ Description: descriptions["description"], @@ -187,9 +192,6 @@ func (r *runnerResource) Schema(_ context.Context, _ resource.SchemaRequest, res PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), }, - Validators: []validator.String{ - stringvalidator.OneOf("eu01"), // Currently Intake supports only EU01 region - }, }, }, } @@ -217,7 +219,7 @@ func (r *runnerResource) Create(ctx context.Context, req resource.CreateRequest, return } - // Create new bar + // Create new runner runnerResp, err := r.client.CreateIntakeRunner(ctx, projectId, region).CreateIntakeRunnerPayload(*payload).Execute() if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating runner", fmt.Sprintf("Calling API: %v", err)) @@ -232,7 +234,7 @@ func (r *runnerResource) Create(ctx context.Context, req resource.CreateRequest, return } - err = mapFields(runnerResp, &model) + err = mapFields(runnerResp, &model, region) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating runner", fmt.Sprintf("Processing API payload: %v", err)) return @@ -277,7 +279,7 @@ func (r *runnerResource) Read(ctx context.Context, req resource.ReadRequest, res ctx = core.LogResponse(ctx) // Map response body to schema - err = mapFields(runnerResp, &model) + err = mapFields(runnerResp, &model, region) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading runner", fmt.Sprintf("Processing API payload: %v", err)) return @@ -304,7 +306,7 @@ func (r *runnerResource) Update(ctx context.Context, req resource.UpdateRequest, projectId := model.ProjectId.ValueString() runnerId := model.RunnerId.ValueString() - region := r.providerData.GetRegionWithOverride(model.Region) + region := model.Region.ValueString() ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "runner_id", runnerId) ctx = tflog.SetField(ctx, "region", region) @@ -330,7 +332,7 @@ func (r *runnerResource) Update(ctx context.Context, req resource.UpdateRequest, } // Map response body to schema - err = mapFields(runnerResp, &model) + err = mapFields(runnerResp, &model, region) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating runner", fmt.Sprintf("Processing API response: %v", err)) return @@ -361,7 +363,7 @@ func (r *runnerResource) Delete(ctx context.Context, req resource.DeleteRequest, ctx = tflog.SetField(ctx, "region", region) ctx = tflog.SetField(ctx, "runner_id", runnerId) - // Delete existing bar + // Delete existing runner err := r.client.DeleteIntakeRunner(ctx, projectId, region, runnerId).Execute() if err != nil { var oapiErr *oapierror.GenericOpenAPIError @@ -407,7 +409,7 @@ func (r *runnerResource) ImportState(ctx context.Context, req resource.ImportSta } // Maps runner fields to the provider internal model -func mapFields(runnerResp *intake.IntakeRunnerResponse, model *Model) error { +func mapFields(runnerResp *intake.IntakeRunnerResponse, model *Model, region string) error { if runnerResp == nil { return fmt.Errorf("response input is nil") } @@ -422,7 +424,7 @@ func mapFields(runnerResp *intake.IntakeRunnerResponse, model *Model) error { model.Id = utils.BuildInternalTerraformId( model.ProjectId.ValueString(), - model.Region.ValueString(), + region, runnerId, ) @@ -447,12 +449,13 @@ func mapFields(runnerResp *intake.IntakeRunnerResponse, model *Model) error { } else { model.Description = types.StringPointerValue(runnerResp.Description) } + model.Region = types.StringValue(region) model.MaxMessageSizeKiB = types.Int64PointerValue(runnerResp.MaxMessageSizeKiB) model.MaxMessagesPerHour = types.Int64PointerValue(runnerResp.MaxMessagesPerHour) return nil } -// Build CreateBarPayload from provider's model +// Build CreateIntakeRunnerPayload from provider's model func toCreatePayload(model *Model) (*intake.CreateIntakeRunnerPayload, error) { if model == nil { return nil, fmt.Errorf("nil model") @@ -480,6 +483,7 @@ func toCreatePayload(model *Model) (*intake.CreateIntakeRunnerPayload, error) { }, nil } +// Build UpdateIntakeRunnerPayload from provider's model func toUpdatePayload(model, state *Model) (*intake.UpdateIntakeRunnerPayload, error) { if model == nil { return nil, fmt.Errorf("model is nil") @@ -492,14 +496,9 @@ func toUpdatePayload(model, state *Model) (*intake.UpdateIntakeRunnerPayload, er payload.MaxMessageSizeKiB = conversion.Int64ValueToPointer(model.MaxMessageSizeKiB) payload.MaxMessagesPerHour = conversion.Int64ValueToPointer(model.MaxMessagesPerHour) - // Handle optional fields - if !model.Description.IsUnknown() || model.Description.IsNull() { - if model.Description.IsNull() { - payload.Description = sdkUtils.Ptr("") - } else { - payload.Description = conversion.StringValueToPointer(model.Description) - } - } + // Optional fields + payload.DisplayName = conversion.StringValueToPointer(model.Name) + payload.Description = conversion.StringValueToPointer(model.Description) var labels map[string]string if !model.Labels.IsUnknown() { From f802f792b2ce82bda3711ba4dfbba06a6a79947c Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 14:45:43 +0100 Subject: [PATCH 11/44] Reshape resource_acc_test --- .../services/intake/resource_acc_test.go | 199 ++++++++++++++++++ .../services/intake/runner/resource.go | 12 +- .../intake/runner/resource_acc_test.go | 159 -------------- .../services/intake/testdata/resource-max.tf | 15 ++ .../services/intake/testdata/resource-min.tf | 10 + 5 files changed, 229 insertions(+), 166 deletions(-) create mode 100644 stackit/internal/services/intake/resource_acc_test.go delete mode 100644 stackit/internal/services/intake/runner/resource_acc_test.go create mode 100644 stackit/internal/services/intake/testdata/resource-max.tf create mode 100644 stackit/internal/services/intake/testdata/resource-min.tf diff --git a/stackit/internal/services/intake/resource_acc_test.go b/stackit/internal/services/intake/resource_acc_test.go new file mode 100644 index 000000000..4c8cb9fd9 --- /dev/null +++ b/stackit/internal/services/intake/resource_acc_test.go @@ -0,0 +1,199 @@ +package intake_test + +import ( + "context" + _ "embed" + "errors" + "fmt" + "maps" + "net/http" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + sdkConfig "github.com/stackitcloud/stackit-sdk-go/core/config" + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" + "github.com/stackitcloud/stackit-sdk-go/services/intake" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil" +) + +//go:embed testdata/resource-min.tf +var resourceMin string + +//go:embed testdata/resource-max.tf +var resourceMax string + +const intakeRunnerResource = "stackit_intake_runner.example" + +const ( + intakeRunnerMinName = "intake-min-runner" + intakeRunnerMinNameUpdated = "intake-min-runner-upd" + intakeRunnerMaxName = "intake-max-runner" + intakeRunnerMaxNameUpdated = "intake-max-runner-upd" +) + +var testConfigVarsMin = config.Variables{ + "project_id": config.StringVariable(testutil.ProjectId), + "name": config.StringVariable(intakeRunnerMinName), +} + +var testConfigVarsMax = config.Variables{ + "project_id": config.StringVariable(testutil.ProjectId), + "name": config.StringVariable(intakeRunnerMaxName), +} + +func testConfigVarsMinUpdated() config.Variables { + tempConfig := make(config.Variables, len(testConfigVarsMin)) + maps.Copy(tempConfig, testConfigVarsMin) + tempConfig["name"] = config.StringVariable(intakeRunnerMinNameUpdated) + return tempConfig +} + +func testConfigVarsMaxUpdated() config.Variables { + tempConfig := make(config.Variables, len(testConfigVarsMax)) + maps.Copy(tempConfig, testConfigVarsMax) + tempConfig["name"] = config.StringVariable(intakeRunnerMaxNameUpdated) + return tempConfig +} + +func TestAccIntakeRunnerMin(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories, + CheckDestroy: testAccCheckIntakeRunnerDestroy, + Steps: []resource.TestStep{ + // Create the minimum runner from the HCL file + { + ConfigVariables: testConfigVarsMin, + Config: testutil.IntakeProviderConfig() + "\n" + resourceMin, + Check: resource.ComposeAggregateTestCheckFunc( + // Verify project_id, name and the existence of runner_id + resource.TestCheckResourceAttr(intakeRunnerResource, "project_id", testutil.ProjectId), + resource.TestCheckResourceAttr(intakeRunnerResource, "name", intakeRunnerMinName), + resource.TestCheckResourceAttrSet(intakeRunnerResource, "runner_id"), + ), + }, + // Data source check: creates config that includes resource and data source + { + ConfigVariables: testConfigVarsMin, + Config: fmt.Sprintf(` + %s + data "stackit_intake_runner" "example" { + project_id = %s.project_id + runner_id = %s.runner_id + region = %s.region + }`, testutil.IntakeProviderConfig()+"\n"+resourceMin, intakeRunnerResource, intakeRunnerResource, intakeRunnerResource), + Check: resource.ComposeAggregateTestCheckFunc( + // Make sure it's correctly found resource by comparing runner_id attribute + resource.TestCheckResourceAttrPair(intakeRunnerResource, "runner_id", "data.stackit_intake_runner.example", "runner_id"), + ), + }, + // Simulate terraform import + { + ConfigVariables: testConfigVarsMin, + Config: testutil.IntakeProviderConfig() + "\n" + resourceMin, + ResourceName: intakeRunnerResource, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: func(s *terraform.State) (string, error) { + // Construct ID string + r, ok := s.RootModule().Resources[intakeRunnerResource] + if !ok { + return "", fmt.Errorf("couldn't find resource %s", intakeRunnerResource) + } + return fmt.Sprintf("%s,%s,%s", r.Primary.Attributes["project_id"], r.Primary.Attributes["region"], r.Primary.Attributes["runner_id"]), nil + }, + }, + // Update check: verifies API updated resource name without crashing + { + ConfigVariables: testConfigVarsMinUpdated(), + Config: testutil.IntakeProviderConfig() + "\n" + resourceMin, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(intakeRunnerResource, "name", intakeRunnerMinNameUpdated), + ), + }, + }, + }) +} + +func TestAccIntakeRunnerMax(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories, + CheckDestroy: testAccCheckIntakeRunnerDestroy, + Steps: []resource.TestStep{ + // Create the max intake runner from HCL files and verify comparison + { + ConfigVariables: testConfigVarsMax, + Config: testutil.IntakeProviderConfig() + "\n" + resourceMax, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(intakeRunnerResource, "name", intakeRunnerMaxName), + resource.TestCheckResourceAttr(intakeRunnerResource, "description", "An example runner for Intake"), + resource.TestCheckResourceAttr(intakeRunnerResource, "max_message_size_kib", "1024"), + ), + }, + // Update and verify changes are reflected + { + ConfigVariables: testConfigVarsMaxUpdated(), + Config: testutil.IntakeProviderConfig() + "\n" + resourceMax, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(intakeRunnerResource, "name", intakeRunnerMaxNameUpdated), + ), + }, + }, + }) +} + +// testAccCheckIntakeRunnerDestroy act as independent auditor to verify destroy operation +func testAccCheckIntakeRunnerDestroy(s *terraform.State) error { + // Create own raw API client + ctx := context.Background() + var client *intake.APIClient + var err error + + // todo: check this again + effectiveRegion := testutil.Region + if effectiveRegion == "" { + effectiveRegion = "eu01" + } + + if testutil.IntakeCustomEndpoint == "" { + client, err = intake.NewAPIClient(sdkConfig.WithRegion(effectiveRegion)) + } else { + client, err = intake.NewAPIClient( + sdkConfig.WithEndpoint(testutil.IntakeCustomEndpoint), + sdkConfig.WithRegion(effectiveRegion), + ) + } + if err != nil { + return fmt.Errorf("creating client: %w", err) + } + + // Loop through resources that should have been deleted + for _, rs := range s.RootModule().Resources { + if rs.Type != "stackit_intake_runner" { + continue + } + + pID := rs.Primary.Attributes["project_id"] + reg := rs.Primary.Attributes["region"] + rID := rs.Primary.Attributes["runner_id"] + + // If it still exists, destroy operation was unsuccessful + _, err := client.GetIntakeRunner(ctx, pID, reg, rID).Execute() + if err == nil { + // Delete to prevent orphaned instances + errDel := client.DeleteIntakeRunner(ctx, pID, reg, rID).Execute() + if errDel != nil { + return fmt.Errorf("resource leaked and manual cleanup failed: %w", errDel) + } + + return fmt.Errorf("intake runner %s still exists in region %s", rID, reg) + } + + var oapiErr *oapierror.GenericOpenAPIError + if !errors.As(err, &oapiErr) || oapiErr.StatusCode != http.StatusNotFound { + return fmt.Errorf("unexpected error checking destruction: %w", err) + } + } + return nil +} diff --git a/stackit/internal/services/intake/runner/resource.go b/stackit/internal/services/intake/runner/resource.go index 40b02599b..ebea2b85a 100644 --- a/stackit/internal/services/intake/runner/resource.go +++ b/stackit/internal/services/intake/runner/resource.go @@ -501,14 +501,12 @@ func toUpdatePayload(model, state *Model) (*intake.UpdateIntakeRunnerPayload, er payload.Description = conversion.StringValueToPointer(model.Description) var labels map[string]string - if !model.Labels.IsUnknown() { - if !model.Labels.IsNull() { - diags := model.Labels.ElementsAs(context.Background(), &labels, false) - if diags.HasError() { - return nil, fmt.Errorf("failed to convert labels: %w", core.DiagsToError(diags)) - } - payload.Labels = &labels + if !model.Labels.IsUnknown() && !model.Labels.IsNull() { + diags := model.Labels.ElementsAs(context.Background(), &labels, false) + if diags.HasError() { + return nil, fmt.Errorf("failed to convert labels: %w", core.DiagsToError(diags)) } + payload.Labels = &labels } return payload, nil diff --git a/stackit/internal/services/intake/runner/resource_acc_test.go b/stackit/internal/services/intake/runner/resource_acc_test.go deleted file mode 100644 index 948c879ad..000000000 --- a/stackit/internal/services/intake/runner/resource_acc_test.go +++ /dev/null @@ -1,159 +0,0 @@ -package runner_test - -import ( - "context" - "errors" - "fmt" - "net/http" - "testing" - - "github.com/hashicorp/terraform-plugin-testing/helper/resource" - "github.com/hashicorp/terraform-plugin-testing/terraform" - sdkConfig "github.com/stackitcloud/stackit-sdk-go/core/config" - "github.com/stackitcloud/stackit-sdk-go/core/oapierror" - "github.com/stackitcloud/stackit-sdk-go/services/intake" - "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil" -) - -// intakeRunnerResource is the name of the test resource -const intakeRunnerResource = "stackit_intake_runner.example" - -func TestAccIntakeRunner(t *testing.T) { - resource.Test(t, resource.TestCase{ - ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories, - CheckDestroy: testAccCheckIntakeRunnerDestroy, - Steps: []resource.TestStep{ - // create the runner - { - Config: testutil.IntakeProviderConfig() + testAccIntakeRunnerConfigMinimal("example-runner-minimal"), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(intakeRunnerResource, "project_id", testutil.ProjectId), - resource.TestCheckResourceAttr(intakeRunnerResource, "name", "example-runner-minimal"), - resource.TestCheckResourceAttrSet(intakeRunnerResource, "runner_id"), - resource.TestCheckResourceAttr(intakeRunnerResource, "description", ""), - resource.TestCheckResourceAttr(intakeRunnerResource, "labels.%", "0"), - ), - }, - // update the runner - { - Config: testutil.IntakeProviderConfig() + testAccIntakeRunnerConfigFull("example-runner-full", "An example runner for Intake", 1024, 1100), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(intakeRunnerResource, "name", "example-runner-full"), - resource.TestCheckResourceAttr(intakeRunnerResource, "description", "An example runner for Intake"), - resource.TestCheckResourceAttr(intakeRunnerResource, "max_message_size_kib", "1024"), - resource.TestCheckResourceAttr(intakeRunnerResource, "max_messages_per_hour", "1100"), - resource.TestCheckResourceAttr(intakeRunnerResource, "labels.%", "2"), - resource.TestCheckResourceAttr(intakeRunnerResource, "labels.created_by", "terraform-provider-stackit"), - resource.TestCheckResourceAttr(intakeRunnerResource, "labels.env", "development"), - ), - }, - // importing the runner - { - ResourceName: intakeRunnerResource, - ImportState: true, - ImportStateVerify: true, - }, - // update to remove optional attributes - { - Config: testutil.IntakeProviderConfig() + testAccIntakeRunnerConfigUpdated("example-runner-updated", 1024, 1100), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(intakeRunnerResource, "name", "example-runner-updated"), - resource.TestCheckResourceAttr(intakeRunnerResource, "description", ""), - resource.TestCheckResourceAttr(intakeRunnerResource, "labels.%", "0"), - resource.TestCheckResourceAttr(intakeRunnerResource, "max_message_size_kib", "1024"), - resource.TestCheckResourceAttr(intakeRunnerResource, "max_messages_per_hour", "1100"), - ), - }, - }, - }) -} - -func testAccIntakeRunnerConfigMinimal(name string) string { - return fmt.Sprintf(` - resource "stackit_intake_runner" "example" { - project_id = "%s" - name = "%s" - max_message_size_kib = 1024 - max_messages_per_hour = 1000 - } - `, - testutil.ProjectId, - name, - ) -} - -func testAccIntakeRunnerConfigFull(name, description string, maxKib, maxPerHour int) string { - return fmt.Sprintf(` - resource "stackit_intake_runner" "example" { - project_id = "%s" - name = "%s" - description = "%s" - max_message_size_kib = %d - max_messages_per_hour = %d - labels = { - "created_by" = "terraform-provider-stackit" - "env" = "development" - } - } - `, - testutil.ProjectId, - name, - description, - maxKib, - maxPerHour, - ) -} - -func testAccIntakeRunnerConfigUpdated(name string, maxKib, maxPerHour int) string { - return fmt.Sprintf(` - resource "stackit_intake_runner" "example" { - project_id = "%s" - name = "%s" - description = "" - max_message_size_kib = %d - max_messages_per_hour = %d - labels = {} - } - `, - testutil.ProjectId, - name, - maxKib, - maxPerHour, - ) -} - -func testAccCheckIntakeRunnerDestroy(s *terraform.State) error { - ctx := context.Background() - var client *intake.APIClient - var err error - if testutil.IntakeCustomEndpoint == "" { - client, err = intake.NewAPIClient() - } else { - client, err = intake.NewAPIClient(sdkConfig.WithEndpoint(testutil.IntakeCustomEndpoint)) - } - if err != nil { - return fmt.Errorf("creating client: %w", err) - } - - for _, rs := range s.RootModule().Resources { - if rs.Type != "stackit_intake_runner" { - continue - } - // Try to find the runner - _, err := client.GetIntakeRunner(ctx, rs.Primary.Attributes["project_id"], rs.Primary.Attributes["region"], rs.Primary.Attributes["runner_id"]).Execute() - if err == nil { - err = client.DeleteIntakeRunner(ctx, rs.Primary.Attributes["project_id"], rs.Primary.Attributes["region"], rs.Primary.Attributes["runner_id"]).Execute() - if err != nil { - return fmt.Errorf("intake runner with ID %s still existed, got an error removing", rs.Primary.ID, err) - } - - return fmt.Errorf("intake runner with ID %s still existed", rs.Primary.ID) - } - var oapiErr *oapierror.GenericOpenAPIError - if !errors.As(err, &oapiErr) || oapiErr.StatusCode != http.StatusNotFound { - return fmt.Errorf("expected 404 not found, got error: %w", err) - } - } - - return nil -} diff --git a/stackit/internal/services/intake/testdata/resource-max.tf b/stackit/internal/services/intake/testdata/resource-max.tf new file mode 100644 index 000000000..5030bb196 --- /dev/null +++ b/stackit/internal/services/intake/testdata/resource-max.tf @@ -0,0 +1,15 @@ + +variable "project_id" {} +variable "name" {} + +resource "stackit_intake_runner" "example" { + project_id = var.project_id + name = var.name + description = "An example runner for Intake" + max_message_size_kib = 1024 + max_messages_per_hour = 1100 + labels = { + "created_by" = "terraform-provider-stackit" + "env" = "development" + } +} diff --git a/stackit/internal/services/intake/testdata/resource-min.tf b/stackit/internal/services/intake/testdata/resource-min.tf new file mode 100644 index 000000000..3760c61e8 --- /dev/null +++ b/stackit/internal/services/intake/testdata/resource-min.tf @@ -0,0 +1,10 @@ + +variable "project_id" {} +variable "name" {} + +resource "stackit_intake_runner" "example" { + project_id = var.project_id + name = var.name + max_message_size_kib = 1024 + max_messages_per_hour = 1000 +} From 3dd603fffa746dd07202fd24047e0289c6f7eb61 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 16:52:48 +0100 Subject: [PATCH 12/44] Remove replace directive during name change & complement test checks --- stackit/internal/services/intake/resource_acc_test.go | 10 ++++++++++ stackit/internal/services/intake/runner/resource.go | 3 --- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/stackit/internal/services/intake/resource_acc_test.go b/stackit/internal/services/intake/resource_acc_test.go index 4c8cb9fd9..a171c7bf3 100644 --- a/stackit/internal/services/intake/resource_acc_test.go +++ b/stackit/internal/services/intake/resource_acc_test.go @@ -71,6 +71,11 @@ func TestAccIntakeRunnerMin(t *testing.T) { resource.TestCheckResourceAttr(intakeRunnerResource, "project_id", testutil.ProjectId), resource.TestCheckResourceAttr(intakeRunnerResource, "name", intakeRunnerMinName), resource.TestCheckResourceAttrSet(intakeRunnerResource, "runner_id"), + resource.TestCheckResourceAttr(intakeRunnerResource, "max_message_size_kib", "1024"), + resource.TestCheckResourceAttr(intakeRunnerResource, "max_messages_per_hour", "1000"), + // Verify empty fields + resource.TestCheckResourceAttr(intakeRunnerResource, "description", ""), + resource.TestCheckResourceAttr(intakeRunnerResource, "labels.%", "0"), ), }, // Data source check: creates config that includes resource and data source @@ -129,6 +134,11 @@ func TestAccIntakeRunnerMax(t *testing.T) { resource.TestCheckResourceAttr(intakeRunnerResource, "name", intakeRunnerMaxName), resource.TestCheckResourceAttr(intakeRunnerResource, "description", "An example runner for Intake"), resource.TestCheckResourceAttr(intakeRunnerResource, "max_message_size_kib", "1024"), + resource.TestCheckResourceAttr(intakeRunnerResource, "max_messages_per_hour", "1100"), + // Verify map size + resource.TestCheckResourceAttr(intakeRunnerResource, "labels.%", "2"), + resource.TestCheckResourceAttr(intakeRunnerResource, "labels.created_by", "terraform-provider-stackit"), + resource.TestCheckResourceAttr(intakeRunnerResource, "labels.env", "development"), ), }, // Update and verify changes are reflected diff --git a/stackit/internal/services/intake/runner/resource.go b/stackit/internal/services/intake/runner/resource.go index ebea2b85a..fd17024bd 100644 --- a/stackit/internal/services/intake/runner/resource.go +++ b/stackit/internal/services/intake/runner/resource.go @@ -156,9 +156,6 @@ func (r *runnerResource) Schema(_ context.Context, _ resource.SchemaRequest, res "name": schema.StringAttribute{ Description: descriptions["name"], Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, }, "description": schema.StringAttribute{ Description: descriptions["description"], From b4eb22d7ef59caea4db97ff48cdff3fc977b8f25 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 17:08:46 +0100 Subject: [PATCH 13/44] docs --- docs/data-sources/intake_runner.md | 4 ++ docs/ephemeral-resources/access_token.md | 73 ------------------------ docs/resources/volume.md | 2 +- 3 files changed, 5 insertions(+), 74 deletions(-) delete mode 100644 docs/ephemeral-resources/access_token.md diff --git a/docs/data-sources/intake_runner.md b/docs/data-sources/intake_runner.md index 60950b2f1..bb995f4a7 100644 --- a/docs/data-sources/intake_runner.md +++ b/docs/data-sources/intake_runner.md @@ -20,6 +20,10 @@ Datasource for STACKIT Intake Runner. - `project_id` (String) STACKIT Project ID to which the runner is associated. - `runner_id` (String) The runner ID. +### Optional + +- `region` (String) The resource region. If not defined, the provider region is used. + ### Read-Only - `description` (String) The description of the runner. diff --git a/docs/ephemeral-resources/access_token.md b/docs/ephemeral-resources/access_token.md deleted file mode 100644 index b45fd715e..000000000 --- a/docs/ephemeral-resources/access_token.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -# generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "stackit_access_token Ephemeral Resource - stackit" -subcategory: "" -description: |- - Ephemeral resource that generates a short-lived STACKIT access token (JWT) using a service account key. A new token is generated each time the resource is evaluated, and it remains consistent for the duration of a Terraform operation. If a private key is not explicitly provided, the provider attempts to extract it from the service account key instead. Access tokens generated from service account keys expire after 60 minutes. - ~> Service account key credentials must be configured either in the STACKIT provider configuration or via environment variables (see example below). If any other authentication method is configured, this ephemeral resource will fail with an error. - ~> This ephemeral-resource is in beta and may be subject to breaking changes in the future. Use with caution. See our guide https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources for how to opt-in to use beta resources. ---- - -# stackit_access_token (Ephemeral Resource) - -Ephemeral resource that generates a short-lived STACKIT access token (JWT) using a service account key. A new token is generated each time the resource is evaluated, and it remains consistent for the duration of a Terraform operation. If a private key is not explicitly provided, the provider attempts to extract it from the service account key instead. Access tokens generated from service account keys expire after 60 minutes. - -~> Service account key credentials must be configured either in the STACKIT provider configuration or via environment variables (see example below). If any other authentication method is configured, this ephemeral resource will fail with an error. - -~> This ephemeral-resource is in beta and may be subject to breaking changes in the future. Use with caution. See our [guide](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources) for how to opt-in to use beta resources. - -## Example Usage - -```terraform -provider "stackit" { - default_region = "eu01" - service_account_key_path = "/path/to/sa_key.json" - enable_beta_resources = true -} - -ephemeral "stackit_access_token" "example" {} - -locals { - stackit_api_base_url = "https://iaas.api.stackit.cloud" - public_ip_path = "/v2/projects/${var.project_id}/regions/${var.region}/public-ips" - - public_ip_payload = { - labels = { - key = "value" - } - } -} - -# Docs: https://registry.terraform.io/providers/Mastercard/restapi/latest -provider "restapi" { - uri = local.stackit_api_base_url - write_returns_object = true - - headers = { - Authorization = "Bearer ${ephemeral.stackit_access_token.example.access_token}" - Content-Type = "application/json" - } - - create_method = "POST" - update_method = "PATCH" - destroy_method = "DELETE" -} - -resource "restapi_object" "public_ip_restapi" { - path = local.public_ip_path - data = jsonencode(local.public_ip_payload) - - id_attribute = "id" - read_method = "GET" - create_method = "POST" - update_method = "PATCH" - destroy_method = "DELETE" -} -``` - - -## Schema - -### Read-Only - -- `access_token` (String, Sensitive) JWT access token for STACKIT API authentication. diff --git a/docs/resources/volume.md b/docs/resources/volume.md index 125fed296..fb57dff6d 100644 --- a/docs/resources/volume.md +++ b/docs/resources/volume.md @@ -72,7 +72,7 @@ Required: Optional: - `key_payload_base64` (String, Sensitive) Optional predefined secret, which will be encrypted against the key-encryption-key within the STACKIT-KMS. If not defined, a random secret will be generated by the API and encrypted against the STACKIT-KMS. If a key-payload is provided here, it must be base64 encoded. -- `key_payload_base64_wo` (String, Sensitive, [Write-only](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments)) Optional predefined secret, which will be encrypted against the key-encryption-key within the STACKIT-KMS. If not defined, a random secret will be generated by the API and encrypted against the STACKIT-KMS. If a key-payload is provided here, it must be base64 encoded. +- `key_payload_base64_wo` (String, Sensitive) Optional predefined secret, which will be encrypted against the key-encryption-key within the STACKIT-KMS. If not defined, a random secret will be generated by the API and encrypted against the STACKIT-KMS. If a key-payload is provided here, it must be base64 encoded. - `key_payload_base64_wo_version` (Number) Used together with `key_payload_base64_wo` to trigger an re-create. Increment this value when an update to `key_payload_base64_wo` is required. From 22786c2290c8a896b2b72cb53f0f8cf97e0016ab Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 17:11:34 +0100 Subject: [PATCH 14/44] lint --- .../services/intake/testdata/resource-max.tf | 18 +++++++++--------- .../services/intake/testdata/resource-min.tf | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/stackit/internal/services/intake/testdata/resource-max.tf b/stackit/internal/services/intake/testdata/resource-max.tf index 5030bb196..ff8324311 100644 --- a/stackit/internal/services/intake/testdata/resource-max.tf +++ b/stackit/internal/services/intake/testdata/resource-max.tf @@ -3,13 +3,13 @@ variable "project_id" {} variable "name" {} resource "stackit_intake_runner" "example" { - project_id = var.project_id - name = var.name - description = "An example runner for Intake" - max_message_size_kib = 1024 - max_messages_per_hour = 1100 - labels = { - "created_by" = "terraform-provider-stackit" - "env" = "development" - } + project_id = var.project_id + name = var.name + description = "An example runner for Intake" + max_message_size_kib = 1024 + max_messages_per_hour = 1100 + labels = { + "created_by" = "terraform-provider-stackit" + "env" = "development" + } } diff --git a/stackit/internal/services/intake/testdata/resource-min.tf b/stackit/internal/services/intake/testdata/resource-min.tf index 3760c61e8..29673b437 100644 --- a/stackit/internal/services/intake/testdata/resource-min.tf +++ b/stackit/internal/services/intake/testdata/resource-min.tf @@ -3,8 +3,8 @@ variable "project_id" {} variable "name" {} resource "stackit_intake_runner" "example" { - project_id = var.project_id - name = var.name - max_message_size_kib = 1024 - max_messages_per_hour = 1000 + project_id = var.project_id + name = var.name + max_message_size_kib = 1024 + max_messages_per_hour = 1000 } From a56421565cb710a0576cd43b58f6bef71a79398e Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 17:12:18 +0100 Subject: [PATCH 15/44] Adjust resource_test --- .../services/intake/runner/resource_test.go | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/stackit/internal/services/intake/runner/resource_test.go b/stackit/internal/services/intake/runner/resource_test.go index 921349c64..9d721d462 100644 --- a/stackit/internal/services/intake/runner/resource_test.go +++ b/stackit/internal/services/intake/runner/resource_test.go @@ -1,7 +1,6 @@ package runner import ( - "context" "fmt" "testing" @@ -20,6 +19,7 @@ func TestMapFields(t *testing.T) { description string input *intake.IntakeRunnerResponse model *Model + region string expected *Model wantErr bool }{ @@ -35,8 +35,8 @@ func TestMapFields(t *testing.T) { }, &Model{ ProjectId: types.StringValue("pid"), - Region: types.StringValue("eu01"), }, + "eu01", &Model{ Id: types.StringValue(fmt.Sprintf("pid,eu01,%s", runnerId)), ProjectId: types.StringValue("pid"), @@ -54,6 +54,7 @@ func TestMapFields(t *testing.T) { "nil input", nil, &Model{}, + "eu01", nil, true, }, @@ -61,6 +62,7 @@ func TestMapFields(t *testing.T) { "nil model", &intake.IntakeRunnerResponse{}, nil, + "eu01", nil, true, }, @@ -72,8 +74,8 @@ func TestMapFields(t *testing.T) { }, &Model{ ProjectId: types.StringValue("pid"), - Region: types.StringValue("eu01"), }, + "eu01", &Model{ Id: types.StringValue("pid,eu01,"), ProjectId: types.StringValue("pid"), @@ -90,7 +92,7 @@ func TestMapFields(t *testing.T) { } for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { - err := mapFields(tt.input, tt.model) + err := mapFields(tt.input, tt.model, tt.region) if (err != nil) != tt.wantErr { t.Errorf("mapFields error = %v, wantErr %v", err, tt.wantErr) return @@ -209,10 +211,7 @@ func TestToUpdatePayload(t *testing.T) { "empty model", &Model{}, &Model{}, - &intake.UpdateIntakeRunnerPayload{ - Description: utils.Ptr(""), - Labels: &map[string]string{}, - }, + &intake.UpdateIntakeRunnerPayload{}, false, }, { @@ -231,14 +230,6 @@ func TestToUpdatePayload(t *testing.T) { } for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { - var labels map[string]string - if tt.model != nil && !tt.model.Labels.IsNull() && !tt.model.Labels.IsUnknown() { - diags := tt.model.Labels.ElementsAs(context.Background(), &labels, false) - if diags.HasError() { - t.Fatalf("error preparing test %v", diags) - } - } - payload, err := toUpdatePayload(tt.model, tt.state) if (err != nil) != tt.wantErr { t.Errorf("toUpdatePayload error = %v, wantErr %v", err, tt.wantErr) From 8e477f159614dc1af3d415f5b0c2d030e799014a Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 17:20:56 +0100 Subject: [PATCH 16/44] adjust last small tweaks --- stackit/internal/services/intake/runner/resource.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/stackit/internal/services/intake/runner/resource.go b/stackit/internal/services/intake/runner/resource.go index fd17024bd..2222a5790 100644 --- a/stackit/internal/services/intake/runner/resource.go +++ b/stackit/internal/services/intake/runner/resource.go @@ -435,17 +435,13 @@ func mapFields(runnerResp *intake.IntakeRunnerResponse, model *Model, region str model.Labels = labels } - if runnerResp.Id != nil && *runnerResp.Id == "" { + if runnerResp.Id != nil || *runnerResp.Id == "" { model.RunnerId = types.StringNull() } else { model.RunnerId = types.StringPointerValue(runnerResp.Id) } model.Name = types.StringPointerValue(runnerResp.DisplayName) - if runnerResp.Description == nil { - model.Description = types.StringValue("") - } else { - model.Description = types.StringPointerValue(runnerResp.Description) - } + model.Description = types.StringPointerValue(runnerResp.Description) model.Region = types.StringValue(region) model.MaxMessageSizeKiB = types.Int64PointerValue(runnerResp.MaxMessageSizeKiB) model.MaxMessagesPerHour = types.Int64PointerValue(runnerResp.MaxMessagesPerHour) @@ -498,7 +494,7 @@ func toUpdatePayload(model, state *Model) (*intake.UpdateIntakeRunnerPayload, er payload.Description = conversion.StringValueToPointer(model.Description) var labels map[string]string - if !model.Labels.IsUnknown() && !model.Labels.IsNull() { + if !model.Labels.IsNull() && !model.Labels.IsUnknown() { diags := model.Labels.ElementsAs(context.Background(), &labels, false) if diags.HasError() { return nil, fmt.Errorf("failed to convert labels: %w", core.DiagsToError(diags)) From 98d211d4e0130b2199133af8e8f5e5044ed3edba Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 17:25:13 +0100 Subject: [PATCH 17/44] lint --- docs/resources/intake_runner.md | 12 ++++++------ .../resources/stackit_intake_runner/resource.tf | 12 ++++++------ .../services/intake/testdata/resource-max.tf | 16 ++++++++-------- .../services/intake/testdata/resource-min.tf | 10 +++++----- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/docs/resources/intake_runner.md b/docs/resources/intake_runner.md index 9a246d535..215b5316f 100644 --- a/docs/resources/intake_runner.md +++ b/docs/resources/intake_runner.md @@ -14,16 +14,16 @@ Manages STACKIT Intake Runner. ```terraform resource "stackit_intake_runner" "example" { - project_id = var.project_id - name = "example-runner-full" - description = "An example runner for STACKIT Intake" - max_message_size_kib = 2048 - max_messages_per_hour = 1500 + project_id = var.project_id + name = "example-runner-full" + description = "An example runner for STACKIT Intake" + max_message_size_kib = 2048 + max_messages_per_hour = 1500 labels = { "created_by" = "terraform-example" "env" = "production" } - region = var.region + region = var.region } import { diff --git a/examples/resources/stackit_intake_runner/resource.tf b/examples/resources/stackit_intake_runner/resource.tf index ceda583ff..311a9f41f 100644 --- a/examples/resources/stackit_intake_runner/resource.tf +++ b/examples/resources/stackit_intake_runner/resource.tf @@ -1,14 +1,14 @@ resource "stackit_intake_runner" "example" { - project_id = var.project_id - name = "example-runner-full" - description = "An example runner for STACKIT Intake" - max_message_size_kib = 2048 - max_messages_per_hour = 1500 + project_id = var.project_id + name = "example-runner-full" + description = "An example runner for STACKIT Intake" + max_message_size_kib = 2048 + max_messages_per_hour = 1500 labels = { "created_by" = "terraform-example" "env" = "production" } - region = var.region + region = var.region } import { diff --git a/stackit/internal/services/intake/testdata/resource-max.tf b/stackit/internal/services/intake/testdata/resource-max.tf index ff8324311..5614426bb 100644 --- a/stackit/internal/services/intake/testdata/resource-max.tf +++ b/stackit/internal/services/intake/testdata/resource-max.tf @@ -3,13 +3,13 @@ variable "project_id" {} variable "name" {} resource "stackit_intake_runner" "example" { - project_id = var.project_id - name = var.name - description = "An example runner for Intake" - max_message_size_kib = 1024 - max_messages_per_hour = 1100 + project_id = var.project_id + name = var.name + description = "An example runner for Intake" + max_message_size_kib = 1024 + max_messages_per_hour = 1100 labels = { - "created_by" = "terraform-provider-stackit" - "env" = "development" + "created_by" = "terraform-provider-stackit" + "env" = "development" } -} +} \ No newline at end of file diff --git a/stackit/internal/services/intake/testdata/resource-min.tf b/stackit/internal/services/intake/testdata/resource-min.tf index 29673b437..e7c8d77fa 100644 --- a/stackit/internal/services/intake/testdata/resource-min.tf +++ b/stackit/internal/services/intake/testdata/resource-min.tf @@ -3,8 +3,8 @@ variable "project_id" {} variable "name" {} resource "stackit_intake_runner" "example" { - project_id = var.project_id - name = var.name - max_message_size_kib = 1024 - max_messages_per_hour = 1000 -} + project_id = var.project_id + name = var.name + max_message_size_kib = 1024 + max_messages_per_hour = 1000 +} \ No newline at end of file From 264727c889e914f277651103184065190acc9942 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 18:32:45 +0100 Subject: [PATCH 18/44] Add missing checks in tests --- .../services/intake/resource_acc_test.go | 43 +++++++++++++++---- .../services/intake/runner/resource.go | 11 ++--- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/stackit/internal/services/intake/resource_acc_test.go b/stackit/internal/services/intake/resource_acc_test.go index a171c7bf3..92d12ebb3 100644 --- a/stackit/internal/services/intake/resource_acc_test.go +++ b/stackit/internal/services/intake/resource_acc_test.go @@ -67,15 +67,13 @@ func TestAccIntakeRunnerMin(t *testing.T) { ConfigVariables: testConfigVarsMin, Config: testutil.IntakeProviderConfig() + "\n" + resourceMin, Check: resource.ComposeAggregateTestCheckFunc( - // Verify project_id, name and the existence of runner_id resource.TestCheckResourceAttr(intakeRunnerResource, "project_id", testutil.ProjectId), resource.TestCheckResourceAttr(intakeRunnerResource, "name", intakeRunnerMinName), resource.TestCheckResourceAttrSet(intakeRunnerResource, "runner_id"), resource.TestCheckResourceAttr(intakeRunnerResource, "max_message_size_kib", "1024"), resource.TestCheckResourceAttr(intakeRunnerResource, "max_messages_per_hour", "1000"), - // Verify empty fields - resource.TestCheckResourceAttr(intakeRunnerResource, "description", ""), - resource.TestCheckResourceAttr(intakeRunnerResource, "labels.%", "0"), + resource.TestCheckResourceAttrSet(intakeRunnerResource, "id"), + resource.TestCheckResourceAttrSet(intakeRunnerResource, "region"), ), }, // Data source check: creates config that includes resource and data source @@ -90,7 +88,11 @@ func TestAccIntakeRunnerMin(t *testing.T) { }`, testutil.IntakeProviderConfig()+"\n"+resourceMin, intakeRunnerResource, intakeRunnerResource, intakeRunnerResource), Check: resource.ComposeAggregateTestCheckFunc( // Make sure it's correctly found resource by comparing runner_id attribute + resource.TestCheckResourceAttrPair(intakeRunnerResource, "project_id", "data.stackit_intake_runner.example", "project_id"), resource.TestCheckResourceAttrPair(intakeRunnerResource, "runner_id", "data.stackit_intake_runner.example", "runner_id"), + resource.TestCheckResourceAttrPair(intakeRunnerResource, "name", "data.stackit_intake_runner.example", "name"), + resource.TestCheckResourceAttrPair(intakeRunnerResource, "region", "data.stackit_intake_runner.example", "region"), + resource.TestCheckResourceAttrPair(intakeRunnerResource, "max_messages_per_hour", "data.stackit_intake_runner.example", "max_messages_per_hour"), ), }, // Simulate terraform import @@ -131,14 +133,35 @@ func TestAccIntakeRunnerMax(t *testing.T) { ConfigVariables: testConfigVarsMax, Config: testutil.IntakeProviderConfig() + "\n" + resourceMax, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(intakeRunnerResource, "name", intakeRunnerMaxName), + resource.TestCheckResourceAttr(intakeRunnerResource, "project_id", testutil.ConvertConfigVariable(testConfigVarsMax["project_id"])), + resource.TestCheckResourceAttr(intakeRunnerResource, "name", testutil.ConvertConfigVariable(testConfigVarsMax["name"])), resource.TestCheckResourceAttr(intakeRunnerResource, "description", "An example runner for Intake"), resource.TestCheckResourceAttr(intakeRunnerResource, "max_message_size_kib", "1024"), resource.TestCheckResourceAttr(intakeRunnerResource, "max_messages_per_hour", "1100"), - // Verify map size resource.TestCheckResourceAttr(intakeRunnerResource, "labels.%", "2"), - resource.TestCheckResourceAttr(intakeRunnerResource, "labels.created_by", "terraform-provider-stackit"), resource.TestCheckResourceAttr(intakeRunnerResource, "labels.env", "development"), + resource.TestCheckResourceAttr(intakeRunnerResource, "labels.created_by", "terraform-provider-stackit"), + resource.TestCheckResourceAttrSet(intakeRunnerResource, "runner_id"), + resource.TestCheckResourceAttrSet(intakeRunnerResource, "id"), + resource.TestCheckResourceAttrSet(intakeRunnerResource, "region"), + ), + }, + { + ConfigVariables: testConfigVarsMax, + Config: fmt.Sprintf(` + %s + data "stackit_intake_runner" "example" { + project_id = %s.project_id + runner_id = %s.runner_id + }`, testutil.IntakeProviderConfig()+"\n"+resourceMax, intakeRunnerResource, intakeRunnerResource), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPair(intakeRunnerResource, "project_id", "data.stackit_intake_runner.example", "project_id"), + resource.TestCheckResourceAttrPair(intakeRunnerResource, "runner_id", "data.stackit_intake_runner.example", "runner_id"), + resource.TestCheckResourceAttrPair(intakeRunnerResource, "name", "data.stackit_intake_runner.example", "name"), + resource.TestCheckResourceAttrPair(intakeRunnerResource, "description", "data.stackit_intake_runner.example", "description"), + resource.TestCheckResourceAttrPair(intakeRunnerResource, "region", "data.stackit_intake_runner.example", "region"), + resource.TestCheckResourceAttrPair(intakeRunnerResource, "labels.env", "data.stackit_intake_runner.example", "labels.env"), + resource.TestCheckResourceAttrPair(intakeRunnerResource, "max_messages_per_hour", "data.stackit_intake_runner.example", "max_messages_per_hour"), ), }, // Update and verify changes are reflected @@ -146,7 +169,10 @@ func TestAccIntakeRunnerMax(t *testing.T) { ConfigVariables: testConfigVarsMaxUpdated(), Config: testutil.IntakeProviderConfig() + "\n" + resourceMax, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(intakeRunnerResource, "name", intakeRunnerMaxNameUpdated), + resource.TestCheckResourceAttr(intakeRunnerResource, "name", testutil.ConvertConfigVariable(testConfigVarsMaxUpdated()["name"])), + // Ensure optional fields survived the update (didn't get wiped by a bad Update payload) + resource.TestCheckResourceAttr(intakeRunnerResource, "description", "An example runner for Intake"), + resource.TestCheckResourceAttr(intakeRunnerResource, "labels.env", "development"), ), }, }, @@ -160,7 +186,6 @@ func testAccCheckIntakeRunnerDestroy(s *terraform.State) error { var client *intake.APIClient var err error - // todo: check this again effectiveRegion := testutil.Region if effectiveRegion == "" { effectiveRegion = "eu01" diff --git a/stackit/internal/services/intake/runner/resource.go b/stackit/internal/services/intake/runner/resource.go index 2222a5790..c73818bcf 100644 --- a/stackit/internal/services/intake/runner/resource.go +++ b/stackit/internal/services/intake/runner/resource.go @@ -425,6 +425,12 @@ func mapFields(runnerResp *intake.IntakeRunnerResponse, model *Model, region str runnerId, ) + if runnerResp.Id == nil || *runnerResp.Id == "" { + model.RunnerId = types.StringNull() + } else { + model.RunnerId = types.StringPointerValue(runnerResp.Id) + } + if runnerResp.Labels == nil { model.Labels = types.MapValueMust(types.StringType, map[string]attr.Value{}) } else { @@ -435,11 +441,6 @@ func mapFields(runnerResp *intake.IntakeRunnerResponse, model *Model, region str model.Labels = labels } - if runnerResp.Id != nil || *runnerResp.Id == "" { - model.RunnerId = types.StringNull() - } else { - model.RunnerId = types.StringPointerValue(runnerResp.Id) - } model.Name = types.StringPointerValue(runnerResp.DisplayName) model.Description = types.StringPointerValue(runnerResp.Description) model.Region = types.StringValue(region) From 66b247c722334ebf60c197209ee5e856782dfedf Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 18:46:04 +0100 Subject: [PATCH 19/44] Fix remaining test failing in resource_test.go --- stackit/internal/services/intake/runner/resource_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stackit/internal/services/intake/runner/resource_test.go b/stackit/internal/services/intake/runner/resource_test.go index 9d721d462..b6d3594a2 100644 --- a/stackit/internal/services/intake/runner/resource_test.go +++ b/stackit/internal/services/intake/runner/resource_test.go @@ -82,7 +82,7 @@ func TestMapFields(t *testing.T) { Region: types.StringValue("eu01"), RunnerId: types.StringNull(), Name: types.StringNull(), - Description: types.StringValue(""), + Description: types.StringNull(), Labels: types.MapValueMust(types.StringType, map[string]attr.Value{}), MaxMessageSizeKiB: types.Int64Null(), MaxMessagesPerHour: types.Int64Null(), From 47bcaba66b1a2a56c493801465c0a9d01cf61da7 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 27 Jan 2026 15:52:57 +0100 Subject: [PATCH 20/44] Adjust destroy function --- .../services/intake/resource_acc_test.go | 61 ++++++++++--------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/stackit/internal/services/intake/resource_acc_test.go b/stackit/internal/services/intake/resource_acc_test.go index 92d12ebb3..5adb68761 100644 --- a/stackit/internal/services/intake/resource_acc_test.go +++ b/stackit/internal/services/intake/resource_acc_test.go @@ -3,18 +3,19 @@ package intake_test import ( "context" _ "embed" - "errors" "fmt" "maps" - "net/http" + "strings" "testing" "github.com/hashicorp/terraform-plugin-testing/config" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" sdkConfig "github.com/stackitcloud/stackit-sdk-go/core/config" - "github.com/stackitcloud/stackit-sdk-go/core/oapierror" + "github.com/stackitcloud/stackit-sdk-go/core/utils" "github.com/stackitcloud/stackit-sdk-go/services/intake" + "github.com/stackitcloud/stackit-sdk-go/services/intake/wait" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil" ) @@ -181,53 +182,57 @@ func TestAccIntakeRunnerMax(t *testing.T) { // testAccCheckIntakeRunnerDestroy act as independent auditor to verify destroy operation func testAccCheckIntakeRunnerDestroy(s *terraform.State) error { - // Create own raw API client ctx := context.Background() var client *intake.APIClient var err error - effectiveRegion := testutil.Region - if effectiveRegion == "" { - effectiveRegion = "eu01" - } - if testutil.IntakeCustomEndpoint == "" { - client, err = intake.NewAPIClient(sdkConfig.WithRegion(effectiveRegion)) + client, err = intake.NewAPIClient() } else { client, err = intake.NewAPIClient( sdkConfig.WithEndpoint(testutil.IntakeCustomEndpoint), - sdkConfig.WithRegion(effectiveRegion), ) } if err != nil { return fmt.Errorf("creating client: %w", err) } - // Loop through resources that should have been deleted + instancesToDestroy := []string{} for _, rs := range s.RootModule().Resources { if rs.Type != "stackit_intake_runner" { continue } + // Intake internal ID: "[project_id],[region],[runner_id]" + runnerId := strings.Split(rs.Primary.ID, core.Separator)[2] + instancesToDestroy = append(instancesToDestroy, runnerId) + } - pID := rs.Primary.Attributes["project_id"] - reg := rs.Primary.Attributes["region"] - rID := rs.Primary.Attributes["runner_id"] - - // If it still exists, destroy operation was unsuccessful - _, err := client.GetIntakeRunner(ctx, pID, reg, rID).Execute() - if err == nil { - // Delete to prevent orphaned instances - errDel := client.DeleteIntakeRunner(ctx, pID, reg, rID).Execute() - if errDel != nil { - return fmt.Errorf("resource leaked and manual cleanup failed: %w", errDel) - } + // List all resources in the project/region to see what's left + instancesResp, err := client.ListIntakeRunners(ctx, testutil.ProjectId, testutil.Region).Execute() + if err != nil { + return fmt.Errorf("getting instancesResp: %w", err) + } - return fmt.Errorf("intake runner %s still exists in region %s", rID, reg) + // If the API returns a list of runners, check if our deleted ones are still there + items := *instancesResp.IntakeRunners + for i := range items { + if items[i].Id == nil { + continue } - var oapiErr *oapierror.GenericOpenAPIError - if !errors.As(err, &oapiErr) || oapiErr.StatusCode != http.StatusNotFound { - return fmt.Errorf("unexpected error checking destruction: %w", err) + // If a runner we thought we deleted is found in the list + if utils.Contains(instancesToDestroy, *items[i].Id) { + // Attempt a final delete and wait, just like Postgres + err := client.DeleteIntakeRunner(ctx, testutil.ProjectId, testutil.Region, *items[i].Id).Execute() + if err != nil { + return fmt.Errorf("deleting runner %s during CheckDestroy: %w", *items[i].Id, err) + } + + // Using the wait handler for destruction verification + _, err = wait.DeleteIntakeRunnerWaitHandler(ctx, client, testutil.ProjectId, testutil.Region, *items[i].Id).WaitWithContext(ctx) + if err != nil { + return fmt.Errorf("deleting runner %s during CheckDestroy: waiting for deletion %w", *items[i].Id, err) + } } } return nil From e9d6d6b4308a6318e40082b81b893c2d4704539c Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 29 Jan 2026 10:42:01 +0100 Subject: [PATCH 21/44] Reestablish tf-plugin-docs to newer version and re-generate docs properly --- docs/ephemeral-resources/access_token.md | 73 ++++++++++++++++++++++++ docs/resources/volume.md | 2 +- 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 docs/ephemeral-resources/access_token.md diff --git a/docs/ephemeral-resources/access_token.md b/docs/ephemeral-resources/access_token.md new file mode 100644 index 000000000..b45fd715e --- /dev/null +++ b/docs/ephemeral-resources/access_token.md @@ -0,0 +1,73 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "stackit_access_token Ephemeral Resource - stackit" +subcategory: "" +description: |- + Ephemeral resource that generates a short-lived STACKIT access token (JWT) using a service account key. A new token is generated each time the resource is evaluated, and it remains consistent for the duration of a Terraform operation. If a private key is not explicitly provided, the provider attempts to extract it from the service account key instead. Access tokens generated from service account keys expire after 60 minutes. + ~> Service account key credentials must be configured either in the STACKIT provider configuration or via environment variables (see example below). If any other authentication method is configured, this ephemeral resource will fail with an error. + ~> This ephemeral-resource is in beta and may be subject to breaking changes in the future. Use with caution. See our guide https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources for how to opt-in to use beta resources. +--- + +# stackit_access_token (Ephemeral Resource) + +Ephemeral resource that generates a short-lived STACKIT access token (JWT) using a service account key. A new token is generated each time the resource is evaluated, and it remains consistent for the duration of a Terraform operation. If a private key is not explicitly provided, the provider attempts to extract it from the service account key instead. Access tokens generated from service account keys expire after 60 minutes. + +~> Service account key credentials must be configured either in the STACKIT provider configuration or via environment variables (see example below). If any other authentication method is configured, this ephemeral resource will fail with an error. + +~> This ephemeral-resource is in beta and may be subject to breaking changes in the future. Use with caution. See our [guide](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources) for how to opt-in to use beta resources. + +## Example Usage + +```terraform +provider "stackit" { + default_region = "eu01" + service_account_key_path = "/path/to/sa_key.json" + enable_beta_resources = true +} + +ephemeral "stackit_access_token" "example" {} + +locals { + stackit_api_base_url = "https://iaas.api.stackit.cloud" + public_ip_path = "/v2/projects/${var.project_id}/regions/${var.region}/public-ips" + + public_ip_payload = { + labels = { + key = "value" + } + } +} + +# Docs: https://registry.terraform.io/providers/Mastercard/restapi/latest +provider "restapi" { + uri = local.stackit_api_base_url + write_returns_object = true + + headers = { + Authorization = "Bearer ${ephemeral.stackit_access_token.example.access_token}" + Content-Type = "application/json" + } + + create_method = "POST" + update_method = "PATCH" + destroy_method = "DELETE" +} + +resource "restapi_object" "public_ip_restapi" { + path = local.public_ip_path + data = jsonencode(local.public_ip_payload) + + id_attribute = "id" + read_method = "GET" + create_method = "POST" + update_method = "PATCH" + destroy_method = "DELETE" +} +``` + + +## Schema + +### Read-Only + +- `access_token` (String, Sensitive) JWT access token for STACKIT API authentication. diff --git a/docs/resources/volume.md b/docs/resources/volume.md index fb57dff6d..125fed296 100644 --- a/docs/resources/volume.md +++ b/docs/resources/volume.md @@ -72,7 +72,7 @@ Required: Optional: - `key_payload_base64` (String, Sensitive) Optional predefined secret, which will be encrypted against the key-encryption-key within the STACKIT-KMS. If not defined, a random secret will be generated by the API and encrypted against the STACKIT-KMS. If a key-payload is provided here, it must be base64 encoded. -- `key_payload_base64_wo` (String, Sensitive) Optional predefined secret, which will be encrypted against the key-encryption-key within the STACKIT-KMS. If not defined, a random secret will be generated by the API and encrypted against the STACKIT-KMS. If a key-payload is provided here, it must be base64 encoded. +- `key_payload_base64_wo` (String, Sensitive, [Write-only](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments)) Optional predefined secret, which will be encrypted against the key-encryption-key within the STACKIT-KMS. If not defined, a random secret will be generated by the API and encrypted against the STACKIT-KMS. If a key-payload is provided here, it must be base64 encoded. - `key_payload_base64_wo_version` (Number) Used together with `key_payload_base64_wo` to trigger an re-create. Increment this value when an update to `key_payload_base64_wo` is required. From fd0a687f4a2619d4f8cf3f4bb50d1231adbf50c7 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 17 Feb 2026 13:49:07 +0100 Subject: [PATCH 22/44] Address review --- .../services/intake/resource_acc_test.go | 131 +++++++++++------- 1 file changed, 79 insertions(+), 52 deletions(-) diff --git a/stackit/internal/services/intake/resource_acc_test.go b/stackit/internal/services/intake/resource_acc_test.go index 5adb68761..fd964eea1 100644 --- a/stackit/internal/services/intake/resource_acc_test.go +++ b/stackit/internal/services/intake/resource_acc_test.go @@ -20,41 +20,39 @@ import ( ) //go:embed testdata/resource-min.tf -var resourceMin string +var resourceIntakeRunnerMin string //go:embed testdata/resource-max.tf -var resourceMax string +var resourceIntakeRunnerMax string const intakeRunnerResource = "stackit_intake_runner.example" -const ( - intakeRunnerMinName = "intake-min-runner" - intakeRunnerMinNameUpdated = "intake-min-runner-upd" - intakeRunnerMaxName = "intake-max-runner" - intakeRunnerMaxNameUpdated = "intake-max-runner-upd" -) - -var testConfigVarsMin = config.Variables{ - "project_id": config.StringVariable(testutil.ProjectId), - "name": config.StringVariable(intakeRunnerMinName), +var testIntakeRunnerConfigVarsMin = config.Variables{ + "project_id": config.StringVariable(testutil.ProjectId), + "name": config.StringVariable("intake-min-runner"), + "max_message_size_kib": config.IntegerVariable(1024), + "max_messages_per_hour": config.IntegerVariable(1000), } -var testConfigVarsMax = config.Variables{ - "project_id": config.StringVariable(testutil.ProjectId), - "name": config.StringVariable(intakeRunnerMaxName), +var testIntakeRunnerConfigVarsMax = config.Variables{ + "project_id": config.StringVariable(testutil.ProjectId), + "name": config.StringVariable("intake-max-runner"), + "description": config.StringVariable("An example runner for Intake"), + "max_message_size_kib": config.IntegerVariable(1024), + "max_messages_per_hour": config.IntegerVariable(1100), } -func testConfigVarsMinUpdated() config.Variables { - tempConfig := make(config.Variables, len(testConfigVarsMin)) - maps.Copy(tempConfig, testConfigVarsMin) - tempConfig["name"] = config.StringVariable(intakeRunnerMinNameUpdated) +func testIntakeRunnerConfigVarsMinUpdated() config.Variables { + tempConfig := make(config.Variables, len(testIntakeRunnerConfigVarsMin)) + maps.Copy(tempConfig, testIntakeRunnerConfigVarsMin) + tempConfig["name"] = config.StringVariable("intake-min-runner-upd") return tempConfig } -func testConfigVarsMaxUpdated() config.Variables { - tempConfig := make(config.Variables, len(testConfigVarsMax)) - maps.Copy(tempConfig, testConfigVarsMax) - tempConfig["name"] = config.StringVariable(intakeRunnerMaxNameUpdated) +func testIntakeRunnerConfigVarsMaxUpdated() config.Variables { + tempConfig := make(config.Variables, len(testIntakeRunnerConfigVarsMax)) + maps.Copy(tempConfig, testIntakeRunnerConfigVarsMax) + tempConfig["name"] = config.StringVariable("intake-max-runner-upd") return tempConfig } @@ -65,28 +63,28 @@ func TestAccIntakeRunnerMin(t *testing.T) { Steps: []resource.TestStep{ // Create the minimum runner from the HCL file { - ConfigVariables: testConfigVarsMin, - Config: testutil.IntakeProviderConfig() + "\n" + resourceMin, + ConfigVariables: testIntakeRunnerConfigVarsMin, + Config: testutil.IntakeProviderConfig() + "\n" + resourceIntakeRunnerMin, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(intakeRunnerResource, "project_id", testutil.ProjectId), - resource.TestCheckResourceAttr(intakeRunnerResource, "name", intakeRunnerMinName), + resource.TestCheckResourceAttr(intakeRunnerResource, "project_id", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMin["project_id"])), + resource.TestCheckResourceAttr(intakeRunnerResource, "name", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMin["name"])), resource.TestCheckResourceAttrSet(intakeRunnerResource, "runner_id"), - resource.TestCheckResourceAttr(intakeRunnerResource, "max_message_size_kib", "1024"), - resource.TestCheckResourceAttr(intakeRunnerResource, "max_messages_per_hour", "1000"), + resource.TestCheckResourceAttr(intakeRunnerResource, "max_message_size_kib", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMin["max_message_size_kib"])), + resource.TestCheckResourceAttr(intakeRunnerResource, "max_messages_per_hour", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMin["max_messages_per_hour"])), resource.TestCheckResourceAttrSet(intakeRunnerResource, "id"), - resource.TestCheckResourceAttrSet(intakeRunnerResource, "region"), + resource.TestCheckResourceAttr(intakeRunnerResource, "region", testutil.Region), ), }, // Data source check: creates config that includes resource and data source { - ConfigVariables: testConfigVarsMin, + ConfigVariables: testIntakeRunnerConfigVarsMin, Config: fmt.Sprintf(` %s data "stackit_intake_runner" "example" { project_id = %s.project_id runner_id = %s.runner_id region = %s.region - }`, testutil.IntakeProviderConfig()+"\n"+resourceMin, intakeRunnerResource, intakeRunnerResource, intakeRunnerResource), + }`, testutil.IntakeProviderConfig()+"\n"+resourceIntakeRunnerMin, intakeRunnerResource, intakeRunnerResource, intakeRunnerResource), Check: resource.ComposeAggregateTestCheckFunc( // Make sure it's correctly found resource by comparing runner_id attribute resource.TestCheckResourceAttrPair(intakeRunnerResource, "project_id", "data.stackit_intake_runner.example", "project_id"), @@ -98,8 +96,8 @@ func TestAccIntakeRunnerMin(t *testing.T) { }, // Simulate terraform import { - ConfigVariables: testConfigVarsMin, - Config: testutil.IntakeProviderConfig() + "\n" + resourceMin, + ConfigVariables: testIntakeRunnerConfigVarsMin, + Config: testutil.IntakeProviderConfig() + "\n" + resourceIntakeRunnerMin, ResourceName: intakeRunnerResource, ImportState: true, ImportStateVerify: true, @@ -114,10 +112,16 @@ func TestAccIntakeRunnerMin(t *testing.T) { }, // Update check: verifies API updated resource name without crashing { - ConfigVariables: testConfigVarsMinUpdated(), - Config: testutil.IntakeProviderConfig() + "\n" + resourceMin, + ConfigVariables: testIntakeRunnerConfigVarsMinUpdated(), + Config: testutil.IntakeProviderConfig() + "\n" + resourceIntakeRunnerMin, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(intakeRunnerResource, "name", intakeRunnerMinNameUpdated), + resource.TestCheckResourceAttr(intakeRunnerResource, "project_id", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMin["project_id"])), + resource.TestCheckResourceAttr(intakeRunnerResource, "name", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMinUpdated()["name"])), + resource.TestCheckResourceAttr(intakeRunnerResource, "max_message_size_kib", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMin["max_message_size_kib"])), + resource.TestCheckResourceAttr(intakeRunnerResource, "max_messages_per_hour", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMin["max_messages_per_hour"])), + resource.TestCheckResourceAttr(intakeRunnerResource, "region", testutil.Region), + resource.TestCheckResourceAttrSet(intakeRunnerResource, "runner_id"), + resource.TestCheckResourceAttrSet(intakeRunnerResource, "id"), ), }, }, @@ -131,30 +135,30 @@ func TestAccIntakeRunnerMax(t *testing.T) { Steps: []resource.TestStep{ // Create the max intake runner from HCL files and verify comparison { - ConfigVariables: testConfigVarsMax, - Config: testutil.IntakeProviderConfig() + "\n" + resourceMax, + ConfigVariables: testIntakeRunnerConfigVarsMax, + Config: testutil.IntakeProviderConfig() + "\n" + resourceIntakeRunnerMax, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(intakeRunnerResource, "project_id", testutil.ConvertConfigVariable(testConfigVarsMax["project_id"])), - resource.TestCheckResourceAttr(intakeRunnerResource, "name", testutil.ConvertConfigVariable(testConfigVarsMax["name"])), - resource.TestCheckResourceAttr(intakeRunnerResource, "description", "An example runner for Intake"), - resource.TestCheckResourceAttr(intakeRunnerResource, "max_message_size_kib", "1024"), - resource.TestCheckResourceAttr(intakeRunnerResource, "max_messages_per_hour", "1100"), + resource.TestCheckResourceAttr(intakeRunnerResource, "project_id", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMax["project_id"])), + resource.TestCheckResourceAttr(intakeRunnerResource, "name", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMax["name"])), + resource.TestCheckResourceAttr(intakeRunnerResource, "description", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMax["description"])), + resource.TestCheckResourceAttr(intakeRunnerResource, "max_message_size_kib", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMax["max_message_size_kib"])), + resource.TestCheckResourceAttr(intakeRunnerResource, "max_messages_per_hour", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMax["max_messages_per_hour"])), resource.TestCheckResourceAttr(intakeRunnerResource, "labels.%", "2"), resource.TestCheckResourceAttr(intakeRunnerResource, "labels.env", "development"), resource.TestCheckResourceAttr(intakeRunnerResource, "labels.created_by", "terraform-provider-stackit"), resource.TestCheckResourceAttrSet(intakeRunnerResource, "runner_id"), resource.TestCheckResourceAttrSet(intakeRunnerResource, "id"), - resource.TestCheckResourceAttrSet(intakeRunnerResource, "region"), + resource.TestCheckResourceAttr(intakeRunnerResource, "region", testutil.Region), ), }, { - ConfigVariables: testConfigVarsMax, + ConfigVariables: testIntakeRunnerConfigVarsMax, Config: fmt.Sprintf(` %s data "stackit_intake_runner" "example" { project_id = %s.project_id runner_id = %s.runner_id - }`, testutil.IntakeProviderConfig()+"\n"+resourceMax, intakeRunnerResource, intakeRunnerResource), + }`, testutil.IntakeProviderConfig()+"\n"+resourceIntakeRunnerMax, intakeRunnerResource, intakeRunnerResource), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrPair(intakeRunnerResource, "project_id", "data.stackit_intake_runner.example", "project_id"), resource.TestCheckResourceAttrPair(intakeRunnerResource, "runner_id", "data.stackit_intake_runner.example", "runner_id"), @@ -165,15 +169,38 @@ func TestAccIntakeRunnerMax(t *testing.T) { resource.TestCheckResourceAttrPair(intakeRunnerResource, "max_messages_per_hour", "data.stackit_intake_runner.example", "max_messages_per_hour"), ), }, + // Simulate terraform import + { + ConfigVariables: testIntakeRunnerConfigVarsMax, + Config: testutil.IntakeProviderConfig() + "\n" + resourceIntakeRunnerMax, + ResourceName: intakeRunnerResource, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: func(s *terraform.State) (string, error) { + // Construct ID string + r, ok := s.RootModule().Resources[intakeRunnerResource] + if !ok { + return "", fmt.Errorf("couldn't find resource %s", intakeRunnerResource) + } + return fmt.Sprintf("%s,%s,%s", r.Primary.Attributes["project_id"], r.Primary.Attributes["region"], r.Primary.Attributes["runner_id"]), nil + }, + }, // Update and verify changes are reflected { - ConfigVariables: testConfigVarsMaxUpdated(), - Config: testutil.IntakeProviderConfig() + "\n" + resourceMax, + ConfigVariables: testIntakeRunnerConfigVarsMaxUpdated(), + Config: testutil.IntakeProviderConfig() + "\n" + resourceIntakeRunnerMax, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(intakeRunnerResource, "name", testutil.ConvertConfigVariable(testConfigVarsMaxUpdated()["name"])), - // Ensure optional fields survived the update (didn't get wiped by a bad Update payload) - resource.TestCheckResourceAttr(intakeRunnerResource, "description", "An example runner for Intake"), + resource.TestCheckResourceAttr(intakeRunnerResource, "project_id", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMax["project_id"])), + resource.TestCheckResourceAttr(intakeRunnerResource, "name", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMaxUpdated()["name"])), + resource.TestCheckResourceAttr(intakeRunnerResource, "description", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMax["description"])), + resource.TestCheckResourceAttr(intakeRunnerResource, "max_message_size_kib", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMax["max_message_size_kib"])), + resource.TestCheckResourceAttr(intakeRunnerResource, "max_messages_per_hour", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMax["max_messages_per_hour"])), + resource.TestCheckResourceAttr(intakeRunnerResource, "labels.%", "2"), resource.TestCheckResourceAttr(intakeRunnerResource, "labels.env", "development"), + resource.TestCheckResourceAttr(intakeRunnerResource, "labels.created_by", "terraform-provider-stackit"), + resource.TestCheckResourceAttrSet(intakeRunnerResource, "runner_id"), + resource.TestCheckResourceAttrSet(intakeRunnerResource, "id"), + resource.TestCheckResourceAttr(intakeRunnerResource, "region", testutil.Region), ), }, }, From 01b5ef7a561e93c801d96b62939a992dd3dc6659 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 17 Feb 2026 16:16:36 +0100 Subject: [PATCH 23/44] Small adjustment --- .../services/intake/resource_acc_test.go | 43 +++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/stackit/internal/services/intake/resource_acc_test.go b/stackit/internal/services/intake/resource_acc_test.go index fd964eea1..31c8fb2cc 100644 --- a/stackit/internal/services/intake/resource_acc_test.go +++ b/stackit/internal/services/intake/resource_acc_test.go @@ -8,11 +8,12 @@ import ( "strings" "testing" + "github.com/stackitcloud/stackit-sdk-go/core/utils" + "github.com/hashicorp/terraform-plugin-testing/config" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" sdkConfig "github.com/stackitcloud/stackit-sdk-go/core/config" - "github.com/stackitcloud/stackit-sdk-go/core/utils" "github.com/stackitcloud/stackit-sdk-go/services/intake" "github.com/stackitcloud/stackit-sdk-go/services/intake/wait" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" @@ -30,6 +31,7 @@ const intakeRunnerResource = "stackit_intake_runner.example" var testIntakeRunnerConfigVarsMin = config.Variables{ "project_id": config.StringVariable(testutil.ProjectId), "name": config.StringVariable("intake-min-runner"), + "region": config.StringVariable(testutil.Region), "max_message_size_kib": config.IntegerVariable(1024), "max_messages_per_hour": config.IntegerVariable(1000), } @@ -37,6 +39,7 @@ var testIntakeRunnerConfigVarsMin = config.Variables{ var testIntakeRunnerConfigVarsMax = config.Variables{ "project_id": config.StringVariable(testutil.ProjectId), "name": config.StringVariable("intake-max-runner"), + "region": config.StringVariable(testutil.Region), "description": config.StringVariable("An example runner for Intake"), "max_message_size_kib": config.IntegerVariable(1024), "max_messages_per_hour": config.IntegerVariable(1100), @@ -72,19 +75,19 @@ func TestAccIntakeRunnerMin(t *testing.T) { resource.TestCheckResourceAttr(intakeRunnerResource, "max_message_size_kib", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMin["max_message_size_kib"])), resource.TestCheckResourceAttr(intakeRunnerResource, "max_messages_per_hour", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMin["max_messages_per_hour"])), resource.TestCheckResourceAttrSet(intakeRunnerResource, "id"), - resource.TestCheckResourceAttr(intakeRunnerResource, "region", testutil.Region), + resource.TestCheckResourceAttr(intakeRunnerResource, "region", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMax["region"])), ), }, // Data source check: creates config that includes resource and data source { ConfigVariables: testIntakeRunnerConfigVarsMin, Config: fmt.Sprintf(` - %s - data "stackit_intake_runner" "example" { - project_id = %s.project_id - runner_id = %s.runner_id - region = %s.region - }`, testutil.IntakeProviderConfig()+"\n"+resourceIntakeRunnerMin, intakeRunnerResource, intakeRunnerResource, intakeRunnerResource), + %s + data "stackit_intake_runner" "example" { + project_id = %s.project_id + runner_id = %s.runner_id + region = %s.region + }`, testutil.IntakeProviderConfig()+"\n"+resourceIntakeRunnerMin, intakeRunnerResource, intakeRunnerResource, intakeRunnerResource), Check: resource.ComposeAggregateTestCheckFunc( // Make sure it's correctly found resource by comparing runner_id attribute resource.TestCheckResourceAttrPair(intakeRunnerResource, "project_id", "data.stackit_intake_runner.example", "project_id"), @@ -107,6 +110,7 @@ func TestAccIntakeRunnerMin(t *testing.T) { if !ok { return "", fmt.Errorf("couldn't find resource %s", intakeRunnerResource) } + // ID structure: project_id, region, runner_id return fmt.Sprintf("%s,%s,%s", r.Primary.Attributes["project_id"], r.Primary.Attributes["region"], r.Primary.Attributes["runner_id"]), nil }, }, @@ -115,11 +119,11 @@ func TestAccIntakeRunnerMin(t *testing.T) { ConfigVariables: testIntakeRunnerConfigVarsMinUpdated(), Config: testutil.IntakeProviderConfig() + "\n" + resourceIntakeRunnerMin, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(intakeRunnerResource, "project_id", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMin["project_id"])), + resource.TestCheckResourceAttr(intakeRunnerResource, "project_id", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMinUpdated()["project_id"])), resource.TestCheckResourceAttr(intakeRunnerResource, "name", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMinUpdated()["name"])), resource.TestCheckResourceAttr(intakeRunnerResource, "max_message_size_kib", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMin["max_message_size_kib"])), resource.TestCheckResourceAttr(intakeRunnerResource, "max_messages_per_hour", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMin["max_messages_per_hour"])), - resource.TestCheckResourceAttr(intakeRunnerResource, "region", testutil.Region), + resource.TestCheckResourceAttr(intakeRunnerResource, "region", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMax["region"])), resource.TestCheckResourceAttrSet(intakeRunnerResource, "runner_id"), resource.TestCheckResourceAttrSet(intakeRunnerResource, "id"), ), @@ -148,17 +152,17 @@ func TestAccIntakeRunnerMax(t *testing.T) { resource.TestCheckResourceAttr(intakeRunnerResource, "labels.created_by", "terraform-provider-stackit"), resource.TestCheckResourceAttrSet(intakeRunnerResource, "runner_id"), resource.TestCheckResourceAttrSet(intakeRunnerResource, "id"), - resource.TestCheckResourceAttr(intakeRunnerResource, "region", testutil.Region), + resource.TestCheckResourceAttr(intakeRunnerResource, "region", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMax["region"])), ), }, { ConfigVariables: testIntakeRunnerConfigVarsMax, Config: fmt.Sprintf(` - %s - data "stackit_intake_runner" "example" { - project_id = %s.project_id - runner_id = %s.runner_id - }`, testutil.IntakeProviderConfig()+"\n"+resourceIntakeRunnerMax, intakeRunnerResource, intakeRunnerResource), + %s + data "stackit_intake_runner" "example" { + project_id = %s.project_id + runner_id = %s.runner_id + }`, testutil.IntakeProviderConfig()+"\n"+resourceIntakeRunnerMax, intakeRunnerResource, intakeRunnerResource), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrPair(intakeRunnerResource, "project_id", "data.stackit_intake_runner.example", "project_id"), resource.TestCheckResourceAttrPair(intakeRunnerResource, "runner_id", "data.stackit_intake_runner.example", "runner_id"), @@ -182,6 +186,7 @@ func TestAccIntakeRunnerMax(t *testing.T) { if !ok { return "", fmt.Errorf("couldn't find resource %s", intakeRunnerResource) } + // ID structure: project_id, region, runner_id return fmt.Sprintf("%s,%s,%s", r.Primary.Attributes["project_id"], r.Primary.Attributes["region"], r.Primary.Attributes["runner_id"]), nil }, }, @@ -200,7 +205,7 @@ func TestAccIntakeRunnerMax(t *testing.T) { resource.TestCheckResourceAttr(intakeRunnerResource, "labels.created_by", "terraform-provider-stackit"), resource.TestCheckResourceAttrSet(intakeRunnerResource, "runner_id"), resource.TestCheckResourceAttrSet(intakeRunnerResource, "id"), - resource.TestCheckResourceAttr(intakeRunnerResource, "region", testutil.Region), + resource.TestCheckResourceAttr(intakeRunnerResource, "region", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMax["region"])), ), }, }, @@ -214,7 +219,9 @@ func testAccCheckIntakeRunnerDestroy(s *terraform.State) error { var err error if testutil.IntakeCustomEndpoint == "" { - client, err = intake.NewAPIClient() + client, err = intake.NewAPIClient( + sdkConfig.WithRegion(testutil.Region), + ) } else { client, err = intake.NewAPIClient( sdkConfig.WithEndpoint(testutil.IntakeCustomEndpoint), From 590fdc0df4bad18e7f3852d16524718e87caf3fe Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Fri, 20 Feb 2026 16:48:48 +0100 Subject: [PATCH 24/44] Small adjustment and complete docs --- docs/data-sources/intake_runner.md | 9 ++++++++- docs/resources/intake_runner.md | 18 ++++++------------ .../stackit_intake_runner/data-source.tf | 4 ++++ .../stackit_intake_runner/resource.tf | 18 ++++++------------ .../services/intake/testdata/resource-max.tf | 2 ++ .../services/intake/testdata/resource-min.tf | 2 ++ 6 files changed, 28 insertions(+), 25 deletions(-) create mode 100644 examples/data-sources/stackit_intake_runner/data-source.tf diff --git a/docs/data-sources/intake_runner.md b/docs/data-sources/intake_runner.md index bb995f4a7..52806a24a 100644 --- a/docs/data-sources/intake_runner.md +++ b/docs/data-sources/intake_runner.md @@ -10,7 +10,14 @@ description: |- Datasource for STACKIT Intake Runner. - +## Example Usage + +```terraform +data "stackit_intake_runner" "example" { + project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + runner_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +} +``` ## Schema diff --git a/docs/resources/intake_runner.md b/docs/resources/intake_runner.md index 215b5316f..4dd64b6ab 100644 --- a/docs/resources/intake_runner.md +++ b/docs/resources/intake_runner.md @@ -14,21 +14,15 @@ Manages STACKIT Intake Runner. ```terraform resource "stackit_intake_runner" "example" { - project_id = var.project_id - name = "example-runner-full" + project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + name = "example-runner" + region = "eu01" description = "An example runner for STACKIT Intake" - max_message_size_kib = 2048 - max_messages_per_hour = 1500 + max_message_size_kib = 1024 + max_messages_per_hour = 1000 labels = { - "created_by" = "terraform-example" - "env" = "production" + "env" = "development" } - region = var.region -} - -import { - to = stackit_intake_runner.example - id = "${var.project_id},${var.region},${var.runner_id}" } ``` diff --git a/examples/data-sources/stackit_intake_runner/data-source.tf b/examples/data-sources/stackit_intake_runner/data-source.tf new file mode 100644 index 000000000..0c6ea2288 --- /dev/null +++ b/examples/data-sources/stackit_intake_runner/data-source.tf @@ -0,0 +1,4 @@ +data "stackit_intake_runner" "example" { + project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + runner_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +} diff --git a/examples/resources/stackit_intake_runner/resource.tf b/examples/resources/stackit_intake_runner/resource.tf index 311a9f41f..d85980bce 100644 --- a/examples/resources/stackit_intake_runner/resource.tf +++ b/examples/resources/stackit_intake_runner/resource.tf @@ -1,17 +1,11 @@ resource "stackit_intake_runner" "example" { - project_id = var.project_id - name = "example-runner-full" + project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + name = "example-runner" + region = "eu01" description = "An example runner for STACKIT Intake" - max_message_size_kib = 2048 - max_messages_per_hour = 1500 + max_message_size_kib = 1024 + max_messages_per_hour = 1000 labels = { - "created_by" = "terraform-example" - "env" = "production" + "env" = "development" } - region = var.region } - -import { - to = stackit_intake_runner.example - id = "${var.project_id},${var.region},${var.runner_id}" -} \ No newline at end of file diff --git a/stackit/internal/services/intake/testdata/resource-max.tf b/stackit/internal/services/intake/testdata/resource-max.tf index 5614426bb..9e5ff9e9f 100644 --- a/stackit/internal/services/intake/testdata/resource-max.tf +++ b/stackit/internal/services/intake/testdata/resource-max.tf @@ -1,10 +1,12 @@ variable "project_id" {} variable "name" {} +variable "region" {} resource "stackit_intake_runner" "example" { project_id = var.project_id name = var.name + region = var.region description = "An example runner for Intake" max_message_size_kib = 1024 max_messages_per_hour = 1100 diff --git a/stackit/internal/services/intake/testdata/resource-min.tf b/stackit/internal/services/intake/testdata/resource-min.tf index e7c8d77fa..7c6f26fc0 100644 --- a/stackit/internal/services/intake/testdata/resource-min.tf +++ b/stackit/internal/services/intake/testdata/resource-min.tf @@ -1,10 +1,12 @@ variable "project_id" {} variable "name" {} +variable "region" {} resource "stackit_intake_runner" "example" { project_id = var.project_id name = var.name + region = var.region max_message_size_kib = 1024 max_messages_per_hour = 1000 } \ No newline at end of file From efc4db4f2a7ae2021f9dacd398193507026891f3 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 23 Feb 2026 10:17:32 +0100 Subject: [PATCH 25/44] lint --- stackit/provider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stackit/provider.go b/stackit/provider.go index 26db9c2b2..1e484fefb 100644 --- a/stackit/provider.go +++ b/stackit/provider.go @@ -230,7 +230,7 @@ func (p *Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *pro "edgecloud_custom_endpoint": "Custom endpoint for the Edge Cloud service", "git_custom_endpoint": "Custom endpoint for the Git service", "iaas_custom_endpoint": "Custom endpoint for the IaaS service", - "intake_custom_endpoint": "Custom endpoint for the Intake service", + "intake_custom_endpoint": "Custom endpoint for the Intake service", "kms_custom_endpoint": "Custom endpoint for the KMS service", "mongodbflex_custom_endpoint": "Custom endpoint for the MongoDB Flex service", "modelserving_custom_endpoint": "Custom endpoint for the AI Model Serving service", From d9d80f231af639da6eb10b174a296c316f276141 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 19 Mar 2026 08:34:08 +0100 Subject: [PATCH 26/44] Use new SDK with region removed from hostname --- stackit/internal/services/intake/resource_acc_test.go | 4 +--- stackit/internal/services/intake/utils/utils.go | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/stackit/internal/services/intake/resource_acc_test.go b/stackit/internal/services/intake/resource_acc_test.go index 31c8fb2cc..2706a452d 100644 --- a/stackit/internal/services/intake/resource_acc_test.go +++ b/stackit/internal/services/intake/resource_acc_test.go @@ -219,9 +219,7 @@ func testAccCheckIntakeRunnerDestroy(s *terraform.State) error { var err error if testutil.IntakeCustomEndpoint == "" { - client, err = intake.NewAPIClient( - sdkConfig.WithRegion(testutil.Region), - ) + client, err = intake.NewAPIClient() } else { client, err = intake.NewAPIClient( sdkConfig.WithEndpoint(testutil.IntakeCustomEndpoint), diff --git a/stackit/internal/services/intake/utils/utils.go b/stackit/internal/services/intake/utils/utils.go index b6357b496..4752fe79f 100644 --- a/stackit/internal/services/intake/utils/utils.go +++ b/stackit/internal/services/intake/utils/utils.go @@ -19,7 +19,7 @@ func ConfigureClient(ctx context.Context, providerData *core.ProviderData, diags if providerData.IntakeCustomEndpoint != "" { apiClientConfigOptions = append(apiClientConfigOptions, config.WithEndpoint(providerData.IntakeCustomEndpoint)) } else { - apiClientConfigOptions = append(apiClientConfigOptions, config.WithRegion(providerData.GetRegion())) + apiClientConfigOptions = append(apiClientConfigOptions) } apiClient, err := intake.NewAPIClient(apiClientConfigOptions...) if err != nil { From e57d1a4d6d5d8e14ee2c000396306f94c9f83529 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 31 Mar 2026 12:36:03 +0200 Subject: [PATCH 27/44] Remove computed field from description and labels --- stackit/internal/services/intake/runner/resource.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/stackit/internal/services/intake/runner/resource.go b/stackit/internal/services/intake/runner/resource.go index c73818bcf..eb86cce48 100644 --- a/stackit/internal/services/intake/runner/resource.go +++ b/stackit/internal/services/intake/runner/resource.go @@ -160,7 +160,6 @@ func (r *runnerResource) Schema(_ context.Context, _ resource.SchemaRequest, res "description": schema.StringAttribute{ Description: descriptions["description"], Optional: true, - Computed: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), }, @@ -169,7 +168,6 @@ func (r *runnerResource) Schema(_ context.Context, _ resource.SchemaRequest, res Description: descriptions["labels"], ElementType: types.StringType, Optional: true, - Computed: true, PlanModifiers: []planmodifier.Map{ mapplanmodifier.UseStateForUnknown(), }, From c4ff3f5f3fd8f47498d3485ce13d66c94d0a5ab3 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 31 Mar 2026 12:37:00 +0200 Subject: [PATCH 28/44] Remove region from resource-min.tf test --- stackit/internal/services/intake/testdata/resource-min.tf | 2 -- 1 file changed, 2 deletions(-) diff --git a/stackit/internal/services/intake/testdata/resource-min.tf b/stackit/internal/services/intake/testdata/resource-min.tf index 7c6f26fc0..e7c8d77fa 100644 --- a/stackit/internal/services/intake/testdata/resource-min.tf +++ b/stackit/internal/services/intake/testdata/resource-min.tf @@ -1,12 +1,10 @@ variable "project_id" {} variable "name" {} -variable "region" {} resource "stackit_intake_runner" "example" { project_id = var.project_id name = var.name - region = var.region max_message_size_kib = 1024 max_messages_per_hour = 1000 } \ No newline at end of file From 022dbd42c4709aaef9b034e8b7a90bcca7a94520 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 31 Mar 2026 12:43:10 +0200 Subject: [PATCH 29/44] Move numerical payloads to variables --- stackit/internal/services/intake/testdata/resource-max.tf | 6 ++++-- stackit/internal/services/intake/testdata/resource-min.tf | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/stackit/internal/services/intake/testdata/resource-max.tf b/stackit/internal/services/intake/testdata/resource-max.tf index 9e5ff9e9f..273d7aa7a 100644 --- a/stackit/internal/services/intake/testdata/resource-max.tf +++ b/stackit/internal/services/intake/testdata/resource-max.tf @@ -2,14 +2,16 @@ variable "project_id" {} variable "name" {} variable "region" {} +variable "max_message_size_kib" {} +variable "max_messages_per_hour" {} resource "stackit_intake_runner" "example" { project_id = var.project_id name = var.name region = var.region description = "An example runner for Intake" - max_message_size_kib = 1024 - max_messages_per_hour = 1100 + max_message_size_kib = var.max_message_size_kib + max_messages_per_hour = var.max_messages_per_hour labels = { "created_by" = "terraform-provider-stackit" "env" = "development" diff --git a/stackit/internal/services/intake/testdata/resource-min.tf b/stackit/internal/services/intake/testdata/resource-min.tf index e7c8d77fa..5b1cfd21b 100644 --- a/stackit/internal/services/intake/testdata/resource-min.tf +++ b/stackit/internal/services/intake/testdata/resource-min.tf @@ -1,10 +1,12 @@ variable "project_id" {} variable "name" {} +variable "max_message_size_kib" {} +variable "max_messages_per_hour" {} resource "stackit_intake_runner" "example" { project_id = var.project_id name = var.name - max_message_size_kib = 1024 - max_messages_per_hour = 1000 + max_message_size_kib = var.max_message_size_kib + max_messages_per_hour = var.max_messages_per_hour } \ No newline at end of file From a2f5d0c42678f62a094a2be2960b31b0765eddb8 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 31 Mar 2026 12:44:34 +0200 Subject: [PATCH 30/44] Remove region from resource-min.tf test --- stackit/internal/services/intake/resource_acc_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/stackit/internal/services/intake/resource_acc_test.go b/stackit/internal/services/intake/resource_acc_test.go index 2706a452d..cb2acc241 100644 --- a/stackit/internal/services/intake/resource_acc_test.go +++ b/stackit/internal/services/intake/resource_acc_test.go @@ -31,7 +31,6 @@ const intakeRunnerResource = "stackit_intake_runner.example" var testIntakeRunnerConfigVarsMin = config.Variables{ "project_id": config.StringVariable(testutil.ProjectId), "name": config.StringVariable("intake-min-runner"), - "region": config.StringVariable(testutil.Region), "max_message_size_kib": config.IntegerVariable(1024), "max_messages_per_hour": config.IntegerVariable(1000), } From f4019d8dda24ba7d96ae64171de6c637610e9c3e Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 31 Mar 2026 12:50:10 +0200 Subject: [PATCH 31/44] Replace unnecessary concatenation with sprintf --- stackit/internal/services/intake/resource_acc_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stackit/internal/services/intake/resource_acc_test.go b/stackit/internal/services/intake/resource_acc_test.go index cb2acc241..81d1c85a7 100644 --- a/stackit/internal/services/intake/resource_acc_test.go +++ b/stackit/internal/services/intake/resource_acc_test.go @@ -82,11 +82,12 @@ func TestAccIntakeRunnerMin(t *testing.T) { ConfigVariables: testIntakeRunnerConfigVarsMin, Config: fmt.Sprintf(` %s + %s data "stackit_intake_runner" "example" { project_id = %s.project_id runner_id = %s.runner_id region = %s.region - }`, testutil.IntakeProviderConfig()+"\n"+resourceIntakeRunnerMin, intakeRunnerResource, intakeRunnerResource, intakeRunnerResource), + }`, testutil.IntakeProviderConfig(), resourceIntakeRunnerMin, intakeRunnerResource, intakeRunnerResource, intakeRunnerResource), Check: resource.ComposeAggregateTestCheckFunc( // Make sure it's correctly found resource by comparing runner_id attribute resource.TestCheckResourceAttrPair(intakeRunnerResource, "project_id", "data.stackit_intake_runner.example", "project_id"), @@ -158,10 +159,11 @@ func TestAccIntakeRunnerMax(t *testing.T) { ConfigVariables: testIntakeRunnerConfigVarsMax, Config: fmt.Sprintf(` %s + %s data "stackit_intake_runner" "example" { project_id = %s.project_id runner_id = %s.runner_id - }`, testutil.IntakeProviderConfig()+"\n"+resourceIntakeRunnerMax, intakeRunnerResource, intakeRunnerResource), + }`, testutil.IntakeProviderConfig(), resourceIntakeRunnerMax, intakeRunnerResource, intakeRunnerResource), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrPair(intakeRunnerResource, "project_id", "data.stackit_intake_runner.example", "project_id"), resource.TestCheckResourceAttrPair(intakeRunnerResource, "runner_id", "data.stackit_intake_runner.example", "runner_id"), From b22dc563fb91105b5b823ea9c52d9baad52238cc Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 31 Mar 2026 16:51:04 +0200 Subject: [PATCH 32/44] Add state fields before starting wait handler & retrieve ctx from SetAndLogStateFields --- stackit/internal/services/intake/runner/resource.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/stackit/internal/services/intake/runner/resource.go b/stackit/internal/services/intake/runner/resource.go index eb86cce48..9773a80df 100644 --- a/stackit/internal/services/intake/runner/resource.go +++ b/stackit/internal/services/intake/runner/resource.go @@ -220,7 +220,17 @@ func (r *runnerResource) Create(ctx context.Context, req resource.CreateRequest, core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating runner", fmt.Sprintf("Calling API: %v", err)) return } + ctx = core.LogResponse(ctx) + ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]interface{}{ + "project_id": projectId, + "region": region, + "runner_id": *runnerResp.Id, + }) + + if resp.Diagnostics.HasError() { + return + } // Wait for creation of intake runner _, err = wait.CreateOrUpdateIntakeRunnerWaitHandler(ctx, r.client, projectId, region, runnerResp.GetId()).WaitWithContext(ctx) @@ -394,7 +404,7 @@ func (r *runnerResource) ImportState(ctx context.Context, req resource.ImportSta return } - utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{ + ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{ "project_id": idParts[0], "region": idParts[1], "runner_id": idParts[2], From 50deea2001744b318c120bb9bc9b686669ee4cfc Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 31 Mar 2026 16:56:44 +0200 Subject: [PATCH 33/44] Log response during update --- stackit/internal/services/intake/runner/resource.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stackit/internal/services/intake/runner/resource.go b/stackit/internal/services/intake/runner/resource.go index 9773a80df..3362a2a38 100644 --- a/stackit/internal/services/intake/runner/resource.go +++ b/stackit/internal/services/intake/runner/resource.go @@ -329,6 +329,8 @@ func (r *runnerResource) Update(ctx context.Context, req resource.UpdateRequest, return } + ctx = core.LogResponse(ctx) + // Wait for update _, err = wait.CreateOrUpdateIntakeRunnerWaitHandler(ctx, r.client, projectId, region, runnerId).WaitWithContext(ctx) if err != nil { From 3d783a31602cd334d022112421f4f439b60b6978 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 31 Mar 2026 17:20:06 +0200 Subject: [PATCH 34/44] Reestablish 'Computed: true' for labels until we agree on a solution --- stackit/internal/services/intake/runner/resource.go | 1 + 1 file changed, 1 insertion(+) diff --git a/stackit/internal/services/intake/runner/resource.go b/stackit/internal/services/intake/runner/resource.go index 3362a2a38..a56eb0f2c 100644 --- a/stackit/internal/services/intake/runner/resource.go +++ b/stackit/internal/services/intake/runner/resource.go @@ -168,6 +168,7 @@ func (r *runnerResource) Schema(_ context.Context, _ resource.SchemaRequest, res Description: descriptions["labels"], ElementType: types.StringType, Optional: true, + Computed: true, PlanModifiers: []planmodifier.Map{ mapplanmodifier.UseStateForUnknown(), }, From b189555c3d9583ea35eb34fa941f9fc71a090f1a Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Wed, 1 Apr 2026 15:00:13 +0200 Subject: [PATCH 35/44] Use new SDK API hierarchy --- .../services/intake/resource_acc_test.go | 22 ++--- .../services/intake/runner/data_source.go | 8 +- .../services/intake/runner/resource.go | 86 +++++++++++-------- .../services/intake/runner/resource_test.go | 56 ++++++------ .../internal/services/intake/utils/utils.go | 4 +- 5 files changed, 91 insertions(+), 85 deletions(-) diff --git a/stackit/internal/services/intake/resource_acc_test.go b/stackit/internal/services/intake/resource_acc_test.go index 81d1c85a7..fe2b83962 100644 --- a/stackit/internal/services/intake/resource_acc_test.go +++ b/stackit/internal/services/intake/resource_acc_test.go @@ -14,8 +14,8 @@ import ( "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" sdkConfig "github.com/stackitcloud/stackit-sdk-go/core/config" - "github.com/stackitcloud/stackit-sdk-go/services/intake" - "github.com/stackitcloud/stackit-sdk-go/services/intake/wait" + intake "github.com/stackitcloud/stackit-sdk-go/services/intake/v1betaapi" + "github.com/stackitcloud/stackit-sdk-go/services/intake/v1betaapi/wait" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil" ) @@ -241,30 +241,26 @@ func testAccCheckIntakeRunnerDestroy(s *terraform.State) error { } // List all resources in the project/region to see what's left - instancesResp, err := client.ListIntakeRunners(ctx, testutil.ProjectId, testutil.Region).Execute() + instancesResp, err := client.DefaultAPI.ListIntakeRunners(ctx, testutil.ProjectId, testutil.Region).Execute() if err != nil { return fmt.Errorf("getting instancesResp: %w", err) } // If the API returns a list of runners, check if our deleted ones are still there - items := *instancesResp.IntakeRunners + items := instancesResp.IntakeRunners for i := range items { - if items[i].Id == nil { - continue - } - // If a runner we thought we deleted is found in the list - if utils.Contains(instancesToDestroy, *items[i].Id) { + if utils.Contains(instancesToDestroy, items[i].Id) { // Attempt a final delete and wait, just like Postgres - err := client.DeleteIntakeRunner(ctx, testutil.ProjectId, testutil.Region, *items[i].Id).Execute() + err := client.DefaultAPI.DeleteIntakeRunner(ctx, testutil.ProjectId, testutil.Region, items[i].Id).Execute() if err != nil { - return fmt.Errorf("deleting runner %s during CheckDestroy: %w", *items[i].Id, err) + return fmt.Errorf("deleting runner %s during CheckDestroy: %w", items[i].Id, err) } // Using the wait handler for destruction verification - _, err = wait.DeleteIntakeRunnerWaitHandler(ctx, client, testutil.ProjectId, testutil.Region, *items[i].Id).WaitWithContext(ctx) + _, err = wait.DeleteIntakeRunnerWaitHandler(ctx, client.DefaultAPI, testutil.ProjectId, testutil.Region, items[i].Id).WaitWithContext(ctx) if err != nil { - return fmt.Errorf("deleting runner %s during CheckDestroy: waiting for deletion %w", *items[i].Id, err) + return fmt.Errorf("deleting runner %s during CheckDestroy: waiting for deletion %w", items[i].Id, err) } } } diff --git a/stackit/internal/services/intake/runner/data_source.go b/stackit/internal/services/intake/runner/data_source.go index f44dfaf69..0d075be7a 100644 --- a/stackit/internal/services/intake/runner/data_source.go +++ b/stackit/internal/services/intake/runner/data_source.go @@ -17,7 +17,7 @@ import ( intakeUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/intake/utils" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate" - "github.com/stackitcloud/stackit-sdk-go/services/intake" + intake "github.com/stackitcloud/stackit-sdk-go/services/intake/v1betaapi" ) // Ensure the implementation satisfies the expected interfaces @@ -106,11 +106,11 @@ func (r *runnerDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, ElementType: types.StringType, Computed: true, }, - "max_message_size_kib": schema.Int64Attribute{ + "max_message_size_kib": schema.Int32Attribute{ Description: descriptions["max_message_size_kib"], Computed: true, }, - "max_messages_per_hour": schema.Int64Attribute{ + "max_messages_per_hour": schema.Int32Attribute{ Description: descriptions["max_messages_per_hour"], Computed: true, }, @@ -139,7 +139,7 @@ func (r *runnerDataSource) Read(ctx context.Context, req datasource.ReadRequest, ctx = tflog.SetField(ctx, "region", region) ctx = tflog.SetField(ctx, "runner_id", runnerId) - runnerResp, err := r.client.GetIntakeRunner(ctx, projectId, region, runnerId).Execute() + runnerResp, err := r.client.DefaultAPI.GetIntakeRunner(ctx, projectId, region, runnerId).Execute() if err != nil { var oapiErr *oapierror.GenericOpenAPIError if errors.As(err, &oapiErr) { diff --git a/stackit/internal/services/intake/runner/resource.go b/stackit/internal/services/intake/runner/resource.go index a56eb0f2c..d2a8bb967 100644 --- a/stackit/internal/services/intake/runner/resource.go +++ b/stackit/internal/services/intake/runner/resource.go @@ -17,14 +17,15 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/stackitcloud/stackit-sdk-go/core/oapierror" + coreUtils "github.com/stackitcloud/stackit-sdk-go/core/utils" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" intakeUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/intake/utils" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate" - "github.com/stackitcloud/stackit-sdk-go/services/intake" - "github.com/stackitcloud/stackit-sdk-go/services/intake/wait" + intake "github.com/stackitcloud/stackit-sdk-go/services/intake/v1betaapi" + "github.com/stackitcloud/stackit-sdk-go/services/intake/v1betaapi/wait" ) // Ensure the implementation satisfies the expected interfaces. @@ -43,8 +44,8 @@ type Model struct { Name types.String `tfsdk:"name"` Description types.String `tfsdk:"description"` Labels types.Map `tfsdk:"labels"` - MaxMessageSizeKiB types.Int64 `tfsdk:"max_message_size_kib"` - MaxMessagesPerHour types.Int64 `tfsdk:"max_messages_per_hour"` + MaxMessageSizeKiB types.Int32 `tfsdk:"max_message_size_kib"` + MaxMessagesPerHour types.Int32 `tfsdk:"max_messages_per_hour"` Region types.String `tfsdk:"region"` } @@ -173,11 +174,11 @@ func (r *runnerResource) Schema(_ context.Context, _ resource.SchemaRequest, res mapplanmodifier.UseStateForUnknown(), }, }, - "max_message_size_kib": schema.Int64Attribute{ + "max_message_size_kib": schema.Int32Attribute{ Description: descriptions["max_message_size_kib"], Required: true, }, - "max_messages_per_hour": schema.Int64Attribute{ + "max_messages_per_hour": schema.Int32Attribute{ Description: descriptions["max_messages_per_hour"], Required: true, }, @@ -216,7 +217,7 @@ func (r *runnerResource) Create(ctx context.Context, req resource.CreateRequest, } // Create new runner - runnerResp, err := r.client.CreateIntakeRunner(ctx, projectId, region).CreateIntakeRunnerPayload(*payload).Execute() + runnerResp, err := r.client.DefaultAPI.CreateIntakeRunner(ctx, projectId, region).CreateIntakeRunnerPayload(*payload).Execute() if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating runner", fmt.Sprintf("Calling API: %v", err)) return @@ -226,7 +227,7 @@ func (r *runnerResource) Create(ctx context.Context, req resource.CreateRequest, ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]interface{}{ "project_id": projectId, "region": region, - "runner_id": *runnerResp.Id, + "runner_id": runnerResp.Id, }) if resp.Diagnostics.HasError() { @@ -234,7 +235,7 @@ func (r *runnerResource) Create(ctx context.Context, req resource.CreateRequest, } // Wait for creation of intake runner - _, err = wait.CreateOrUpdateIntakeRunnerWaitHandler(ctx, r.client, projectId, region, runnerResp.GetId()).WaitWithContext(ctx) + _, err = wait.CreateOrUpdateIntakeRunnerWaitHandler(ctx, r.client.DefaultAPI, projectId, region, runnerResp.GetId()).WaitWithContext(ctx) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating runner", fmt.Sprintf("Intake runner creation waiting: %v", err)) return @@ -269,7 +270,7 @@ func (r *runnerResource) Read(ctx context.Context, req resource.ReadRequest, res ctx = tflog.SetField(ctx, "region", region) ctx = tflog.SetField(ctx, "runner_id", runnerId) - runnerResp, err := r.client.GetIntakeRunner(ctx, projectId, region, runnerId).Execute() + runnerResp, err := r.client.DefaultAPI.GetIntakeRunner(ctx, projectId, region, runnerId).Execute() if err != nil { var oapiErr *oapierror.GenericOpenAPIError if errors.As(err, &oapiErr) { @@ -324,7 +325,7 @@ func (r *runnerResource) Update(ctx context.Context, req resource.UpdateRequest, } // Update runner - runnerResp, err := r.client.UpdateIntakeRunner(ctx, projectId, region, runnerId).UpdateIntakeRunnerPayload(*payload).Execute() + runnerResp, err := r.client.DefaultAPI.UpdateIntakeRunner(ctx, projectId, region, runnerId).UpdateIntakeRunnerPayload(*payload).Execute() if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating runner", fmt.Sprintf("Calling API: %v", err)) return @@ -333,7 +334,7 @@ func (r *runnerResource) Update(ctx context.Context, req resource.UpdateRequest, ctx = core.LogResponse(ctx) // Wait for update - _, err = wait.CreateOrUpdateIntakeRunnerWaitHandler(ctx, r.client, projectId, region, runnerId).WaitWithContext(ctx) + _, err = wait.CreateOrUpdateIntakeRunnerWaitHandler(ctx, r.client.DefaultAPI, projectId, region, runnerId).WaitWithContext(ctx) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating runner", fmt.Sprintf("Runner update waiting: %v", err)) return @@ -372,7 +373,7 @@ func (r *runnerResource) Delete(ctx context.Context, req resource.DeleteRequest, ctx = tflog.SetField(ctx, "runner_id", runnerId) // Delete existing runner - err := r.client.DeleteIntakeRunner(ctx, projectId, region, runnerId).Execute() + err := r.client.DefaultAPI.DeleteIntakeRunner(ctx, projectId, region, runnerId).Execute() if err != nil { var oapiErr *oapierror.GenericOpenAPIError if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { @@ -386,7 +387,7 @@ func (r *runnerResource) Delete(ctx context.Context, req resource.DeleteRequest, ctx = core.LogResponse(ctx) // Wait for the delete operation to complete - _, err = wait.DeleteIntakeRunnerWaitHandler(ctx, r.client, projectId, region, runnerId).WaitWithContext(ctx) + _, err = wait.DeleteIntakeRunnerWaitHandler(ctx, r.client.DefaultAPI, projectId, region, runnerId).WaitWithContext(ctx) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting runner", fmt.Sprintf("Runner deletion waiting: %v", err)) return @@ -425,21 +426,16 @@ func mapFields(runnerResp *intake.IntakeRunnerResponse, model *Model, region str return fmt.Errorf("model input is nil") } - var runnerId string - if runnerResp.Id != nil { - runnerId = *runnerResp.Id - } - model.Id = utils.BuildInternalTerraformId( model.ProjectId.ValueString(), region, - runnerId, + runnerResp.Id, ) - if runnerResp.Id == nil || *runnerResp.Id == "" { + if runnerResp.Id == "" { model.RunnerId = types.StringNull() } else { - model.RunnerId = types.StringPointerValue(runnerResp.Id) + model.RunnerId = types.StringValue(runnerResp.Id) } if runnerResp.Labels == nil { @@ -452,11 +448,27 @@ func mapFields(runnerResp *intake.IntakeRunnerResponse, model *Model, region str model.Labels = labels } - model.Name = types.StringPointerValue(runnerResp.DisplayName) + if runnerResp.DisplayName == "" { + model.Name = types.StringNull() + } else { + model.Name = types.StringValue(runnerResp.DisplayName) + } + model.Description = types.StringPointerValue(runnerResp.Description) model.Region = types.StringValue(region) - model.MaxMessageSizeKiB = types.Int64PointerValue(runnerResp.MaxMessageSizeKiB) - model.MaxMessagesPerHour = types.Int64PointerValue(runnerResp.MaxMessagesPerHour) + + if runnerResp.MaxMessageSizeKiB == 0 { + model.MaxMessageSizeKiB = types.Int32Null() + } else { + model.MaxMessageSizeKiB = types.Int32Value(runnerResp.MaxMessageSizeKiB) + } + + if runnerResp.MaxMessagesPerHour == 0 { + model.MaxMessagesPerHour = types.Int32Null() + } else { + model.MaxMessagesPerHour = types.Int32Value(runnerResp.MaxMessagesPerHour) + } + return nil } @@ -474,17 +486,12 @@ func toCreatePayload(model *Model) (*intake.CreateIntakeRunnerPayload, error) { } } - var labelsPtr *map[string]string - if len(labels) > 0 { - labelsPtr = &labels - } - return &intake.CreateIntakeRunnerPayload{ Description: conversion.StringValueToPointer(model.Description), - DisplayName: conversion.StringValueToPointer(model.Name), - Labels: labelsPtr, - MaxMessageSizeKiB: conversion.Int64ValueToPointer(model.MaxMessageSizeKiB), - MaxMessagesPerHour: conversion.Int64ValueToPointer(model.MaxMessagesPerHour), + DisplayName: model.Name.ValueString(), + Labels: labels, + MaxMessageSizeKiB: int32(model.MaxMessageSizeKiB.ValueInt32()), + MaxMessagesPerHour: int32(model.MaxMessagesPerHour.ValueInt32()), }, nil } @@ -498,8 +505,13 @@ func toUpdatePayload(model, state *Model) (*intake.UpdateIntakeRunnerPayload, er } payload := &intake.UpdateIntakeRunnerPayload{} - payload.MaxMessageSizeKiB = conversion.Int64ValueToPointer(model.MaxMessageSizeKiB) - payload.MaxMessagesPerHour = conversion.Int64ValueToPointer(model.MaxMessagesPerHour) + if !model.MaxMessageSizeKiB.IsNull() && !model.MaxMessageSizeKiB.IsUnknown() { + payload.MaxMessageSizeKiB = coreUtils.Ptr(model.MaxMessageSizeKiB.ValueInt32()) + } + + if !model.MaxMessagesPerHour.IsNull() && !model.MaxMessagesPerHour.IsUnknown() { + payload.MaxMessagesPerHour = coreUtils.Ptr(model.MaxMessagesPerHour.ValueInt32()) + } // Optional fields payload.DisplayName = conversion.StringValueToPointer(model.Name) @@ -511,7 +523,7 @@ func toUpdatePayload(model, state *Model) (*intake.UpdateIntakeRunnerPayload, er if diags.HasError() { return nil, fmt.Errorf("failed to convert labels: %w", core.DiagsToError(diags)) } - payload.Labels = &labels + payload.Labels = labels } return payload, nil diff --git a/stackit/internal/services/intake/runner/resource_test.go b/stackit/internal/services/intake/runner/resource_test.go index b6d3594a2..8c79a70a3 100644 --- a/stackit/internal/services/intake/runner/resource_test.go +++ b/stackit/internal/services/intake/runner/resource_test.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/stackitcloud/stackit-sdk-go/core/utils" - "github.com/stackitcloud/stackit-sdk-go/services/intake" + intake "github.com/stackitcloud/stackit-sdk-go/services/intake/v1betaapi" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" ) @@ -26,12 +26,12 @@ func TestMapFields(t *testing.T) { { "success", &intake.IntakeRunnerResponse{ - Id: utils.Ptr(runnerId), - DisplayName: utils.Ptr("name"), + Id: runnerId, + DisplayName: "name", Description: utils.Ptr("description"), - Labels: &map[string]string{"key": "value"}, - MaxMessageSizeKiB: utils.Ptr(int64(1024)), - MaxMessagesPerHour: utils.Ptr(int64(100)), + Labels: map[string]string{"key": "value"}, + MaxMessageSizeKiB: int32(1024), + MaxMessagesPerHour: int32(100), }, &Model{ ProjectId: types.StringValue("pid"), @@ -45,8 +45,8 @@ func TestMapFields(t *testing.T) { Name: types.StringValue("name"), Description: types.StringValue("description"), Labels: types.MapValueMust(types.StringType, map[string]attr.Value{"key": types.StringValue("value")}), - MaxMessageSizeKiB: types.Int64Value(1024), - MaxMessagesPerHour: types.Int64Value(100), + MaxMessageSizeKiB: types.Int32Value(1024), + MaxMessagesPerHour: types.Int32Value(100), }, false, }, @@ -69,8 +69,8 @@ func TestMapFields(t *testing.T) { { "empty response", &intake.IntakeRunnerResponse{ - Id: utils.Ptr(""), - Labels: &map[string]string{}, + Id: "", + Labels: map[string]string{}, }, &Model{ ProjectId: types.StringValue("pid"), @@ -84,8 +84,8 @@ func TestMapFields(t *testing.T) { Name: types.StringNull(), Description: types.StringNull(), Labels: types.MapValueMust(types.StringType, map[string]attr.Value{}), - MaxMessageSizeKiB: types.Int64Null(), - MaxMessagesPerHour: types.Int64Null(), + MaxMessageSizeKiB: types.Int32Null(), + MaxMessagesPerHour: types.Int32Null(), }, false, }, @@ -119,15 +119,15 @@ func TestToCreatePayload(t *testing.T) { Name: types.StringValue("name"), Description: types.StringValue("description"), Labels: types.MapValueMust(types.StringType, map[string]attr.Value{"key": types.StringValue("value")}), - MaxMessageSizeKiB: types.Int64Value(1024), - MaxMessagesPerHour: types.Int64Value(100), + MaxMessageSizeKiB: types.Int32Value(1024), + MaxMessagesPerHour: types.Int32Value(100), }, &intake.CreateIntakeRunnerPayload{ - DisplayName: utils.Ptr("name"), + DisplayName: "name", Description: utils.Ptr("description"), - Labels: utils.Ptr(map[string]string{"key": "value"}), - MaxMessageSizeKiB: utils.Ptr(int64(1024)), - MaxMessagesPerHour: utils.Ptr(int64(100)), + Labels: map[string]string{"key": "value"}, + MaxMessageSizeKiB: int32(1024), + MaxMessagesPerHour: int32(100), }, false, }, @@ -141,11 +141,11 @@ func TestToCreatePayload(t *testing.T) { "empty model", &Model{}, &intake.CreateIntakeRunnerPayload{ - DisplayName: nil, + DisplayName: "", Description: nil, Labels: nil, - MaxMessageSizeKiB: nil, - MaxMessagesPerHour: nil, + MaxMessageSizeKiB: 0, + MaxMessagesPerHour: 0, }, false, }, @@ -180,16 +180,16 @@ func TestToUpdatePayload(t *testing.T) { Name: types.StringValue("name"), Description: types.StringValue("description"), Labels: types.MapValueMust(types.StringType, map[string]attr.Value{"key": types.StringValue("value")}), - MaxMessageSizeKiB: types.Int64Value(1024), - MaxMessagesPerHour: types.Int64Value(100), + MaxMessageSizeKiB: types.Int32Value(1024), + MaxMessagesPerHour: types.Int32Value(100), }, &Model{}, &intake.UpdateIntakeRunnerPayload{ DisplayName: conversion.StringValueToPointer(types.StringValue("name")), Description: conversion.StringValueToPointer(types.StringValue("description")), - Labels: utils.Ptr(map[string]string{"key": "value"}), - MaxMessageSizeKiB: conversion.Int64ValueToPointer(types.Int64Value(1024)), - MaxMessagesPerHour: conversion.Int64ValueToPointer(types.Int64Value(100)), + Labels: map[string]string{"key": "value"}, + MaxMessageSizeKiB: utils.Ptr(int32(1024)), + MaxMessagesPerHour: utils.Ptr(int32(100)), }, false, }, @@ -220,8 +220,8 @@ func TestToUpdatePayload(t *testing.T) { Name: types.StringUnknown(), Description: types.StringUnknown(), Labels: types.MapUnknown(types.StringType), - MaxMessageSizeKiB: types.Int64Unknown(), - MaxMessagesPerHour: types.Int64Unknown(), + MaxMessageSizeKiB: types.Int32Unknown(), + MaxMessagesPerHour: types.Int32Unknown(), }, &Model{}, &intake.UpdateIntakeRunnerPayload{}, diff --git a/stackit/internal/services/intake/utils/utils.go b/stackit/internal/services/intake/utils/utils.go index 4752fe79f..15b4ff7ca 100644 --- a/stackit/internal/services/intake/utils/utils.go +++ b/stackit/internal/services/intake/utils/utils.go @@ -6,7 +6,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/stackitcloud/stackit-sdk-go/core/config" - "github.com/stackitcloud/stackit-sdk-go/services/intake" + intake "github.com/stackitcloud/stackit-sdk-go/services/intake/v1betaapi" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils" ) @@ -18,8 +18,6 @@ func ConfigureClient(ctx context.Context, providerData *core.ProviderData, diags } if providerData.IntakeCustomEndpoint != "" { apiClientConfigOptions = append(apiClientConfigOptions, config.WithEndpoint(providerData.IntakeCustomEndpoint)) - } else { - apiClientConfigOptions = append(apiClientConfigOptions) } apiClient, err := intake.NewAPIClient(apiClientConfigOptions...) if err != nil { From 4e93c2d9a9a780e9c00cfc2fa5213a046f354f08 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Wed, 1 Apr 2026 16:35:36 +0200 Subject: [PATCH 36/44] Handle empty labels --- stackit/internal/services/intake/runner/resource.go | 6 ++---- stackit/internal/services/intake/runner/resource_test.go | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/stackit/internal/services/intake/runner/resource.go b/stackit/internal/services/intake/runner/resource.go index d2a8bb967..c7480c438 100644 --- a/stackit/internal/services/intake/runner/resource.go +++ b/stackit/internal/services/intake/runner/resource.go @@ -7,7 +7,6 @@ import ( "net/http" "strings" - "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier" @@ -169,7 +168,6 @@ func (r *runnerResource) Schema(_ context.Context, _ resource.SchemaRequest, res Description: descriptions["labels"], ElementType: types.StringType, Optional: true, - Computed: true, PlanModifiers: []planmodifier.Map{ mapplanmodifier.UseStateForUnknown(), }, @@ -438,8 +436,8 @@ func mapFields(runnerResp *intake.IntakeRunnerResponse, model *Model, region str model.RunnerId = types.StringValue(runnerResp.Id) } - if runnerResp.Labels == nil { - model.Labels = types.MapValueMust(types.StringType, map[string]attr.Value{}) + if runnerResp.Labels == nil || len(runnerResp.Labels) == 0 { + model.Labels = types.MapNull(types.StringType) } else { labels, diags := types.MapValueFrom(context.Background(), types.StringType, runnerResp.Labels) if diags.HasError() { diff --git a/stackit/internal/services/intake/runner/resource_test.go b/stackit/internal/services/intake/runner/resource_test.go index 8c79a70a3..5c885ecc0 100644 --- a/stackit/internal/services/intake/runner/resource_test.go +++ b/stackit/internal/services/intake/runner/resource_test.go @@ -83,7 +83,7 @@ func TestMapFields(t *testing.T) { RunnerId: types.StringNull(), Name: types.StringNull(), Description: types.StringNull(), - Labels: types.MapValueMust(types.StringType, map[string]attr.Value{}), + Labels: types.MapNull(types.StringType), MaxMessageSizeKiB: types.Int32Null(), MaxMessagesPerHour: types.Int32Null(), }, From 3044854b925ee57dde05fdd73c4fbda3ccfebc17 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 21 Apr 2026 10:54:41 +0200 Subject: [PATCH 37/44] Address review comments --- stackit/internal/services/intake/resource_acc_test.go | 10 ++++++++-- .../internal/services/intake/testdata/resource-max.tf | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/stackit/internal/services/intake/resource_acc_test.go b/stackit/internal/services/intake/resource_acc_test.go index fe2b83962..7f4e9f854 100644 --- a/stackit/internal/services/intake/resource_acc_test.go +++ b/stackit/internal/services/intake/resource_acc_test.go @@ -71,10 +71,12 @@ func TestAccIntakeRunnerMin(t *testing.T) { resource.TestCheckResourceAttr(intakeRunnerResource, "project_id", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMin["project_id"])), resource.TestCheckResourceAttr(intakeRunnerResource, "name", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMin["name"])), resource.TestCheckResourceAttrSet(intakeRunnerResource, "runner_id"), + resource.TestCheckNoResourceAttr(intakeRunnerResource, "description"), + resource.TestCheckNoResourceAttr(intakeRunnerResource, "labels"), resource.TestCheckResourceAttr(intakeRunnerResource, "max_message_size_kib", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMin["max_message_size_kib"])), resource.TestCheckResourceAttr(intakeRunnerResource, "max_messages_per_hour", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMin["max_messages_per_hour"])), resource.TestCheckResourceAttrSet(intakeRunnerResource, "id"), - resource.TestCheckResourceAttr(intakeRunnerResource, "region", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMax["region"])), + resource.TestCheckResourceAttr(intakeRunnerResource, "region", testutil.Region), ), }, // Data source check: creates config that includes resource and data source @@ -94,6 +96,8 @@ func TestAccIntakeRunnerMin(t *testing.T) { resource.TestCheckResourceAttrPair(intakeRunnerResource, "runner_id", "data.stackit_intake_runner.example", "runner_id"), resource.TestCheckResourceAttrPair(intakeRunnerResource, "name", "data.stackit_intake_runner.example", "name"), resource.TestCheckResourceAttrPair(intakeRunnerResource, "region", "data.stackit_intake_runner.example", "region"), + resource.TestCheckNoResourceAttr(intakeRunnerResource, "description"), + resource.TestCheckNoResourceAttr(intakeRunnerResource, "labels"), resource.TestCheckResourceAttrPair(intakeRunnerResource, "max_messages_per_hour", "data.stackit_intake_runner.example", "max_messages_per_hour"), ), }, @@ -123,7 +127,9 @@ func TestAccIntakeRunnerMin(t *testing.T) { resource.TestCheckResourceAttr(intakeRunnerResource, "name", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMinUpdated()["name"])), resource.TestCheckResourceAttr(intakeRunnerResource, "max_message_size_kib", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMin["max_message_size_kib"])), resource.TestCheckResourceAttr(intakeRunnerResource, "max_messages_per_hour", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMin["max_messages_per_hour"])), - resource.TestCheckResourceAttr(intakeRunnerResource, "region", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMax["region"])), + resource.TestCheckResourceAttr(intakeRunnerResource, "region", testutil.Region), + resource.TestCheckNoResourceAttr(intakeRunnerResource, "description"), + resource.TestCheckNoResourceAttr(intakeRunnerResource, "labels"), resource.TestCheckResourceAttrSet(intakeRunnerResource, "runner_id"), resource.TestCheckResourceAttrSet(intakeRunnerResource, "id"), ), diff --git a/stackit/internal/services/intake/testdata/resource-max.tf b/stackit/internal/services/intake/testdata/resource-max.tf index 273d7aa7a..ea41988fe 100644 --- a/stackit/internal/services/intake/testdata/resource-max.tf +++ b/stackit/internal/services/intake/testdata/resource-max.tf @@ -2,6 +2,7 @@ variable "project_id" {} variable "name" {} variable "region" {} +variable "description" {} variable "max_message_size_kib" {} variable "max_messages_per_hour" {} @@ -9,7 +10,7 @@ resource "stackit_intake_runner" "example" { project_id = var.project_id name = var.name region = var.region - description = "An example runner for Intake" + description = var.description max_message_size_kib = var.max_message_size_kib max_messages_per_hour = var.max_messages_per_hour labels = { From 91fe021bbdb5c3a2189f4d2fecb81b9ae304b71d Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 23 Apr 2026 07:25:20 +0200 Subject: [PATCH 38/44] Adjust to new config builder func and new client API --- ...esource_acc_test.go => intake_acc_test.go} | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) rename stackit/internal/services/intake/{resource_acc_test.go => intake_acc_test.go} (92%) diff --git a/stackit/internal/services/intake/resource_acc_test.go b/stackit/internal/services/intake/intake_acc_test.go similarity index 92% rename from stackit/internal/services/intake/resource_acc_test.go rename to stackit/internal/services/intake/intake_acc_test.go index 7f4e9f854..950e4f0fb 100644 --- a/stackit/internal/services/intake/resource_acc_test.go +++ b/stackit/internal/services/intake/intake_acc_test.go @@ -13,7 +13,6 @@ import ( "github.com/hashicorp/terraform-plugin-testing/config" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" - sdkConfig "github.com/stackitcloud/stackit-sdk-go/core/config" intake "github.com/stackitcloud/stackit-sdk-go/services/intake/v1betaapi" "github.com/stackitcloud/stackit-sdk-go/services/intake/v1betaapi/wait" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" @@ -66,7 +65,7 @@ func TestAccIntakeRunnerMin(t *testing.T) { // Create the minimum runner from the HCL file { ConfigVariables: testIntakeRunnerConfigVarsMin, - Config: testutil.IntakeProviderConfig() + "\n" + resourceIntakeRunnerMin, + Config: testutil.NewConfigBuilder().EnableBetaResources(true).BuildProviderConfig() + resourceIntakeRunnerMin, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(intakeRunnerResource, "project_id", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMin["project_id"])), resource.TestCheckResourceAttr(intakeRunnerResource, "name", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMin["name"])), @@ -89,7 +88,7 @@ func TestAccIntakeRunnerMin(t *testing.T) { project_id = %s.project_id runner_id = %s.runner_id region = %s.region - }`, testutil.IntakeProviderConfig(), resourceIntakeRunnerMin, intakeRunnerResource, intakeRunnerResource, intakeRunnerResource), + }`, testutil.NewConfigBuilder().BuildProviderConfig(), resourceIntakeRunnerMin, intakeRunnerResource, intakeRunnerResource, intakeRunnerResource), Check: resource.ComposeAggregateTestCheckFunc( // Make sure it's correctly found resource by comparing runner_id attribute resource.TestCheckResourceAttrPair(intakeRunnerResource, "project_id", "data.stackit_intake_runner.example", "project_id"), @@ -104,7 +103,7 @@ func TestAccIntakeRunnerMin(t *testing.T) { // Simulate terraform import { ConfigVariables: testIntakeRunnerConfigVarsMin, - Config: testutil.IntakeProviderConfig() + "\n" + resourceIntakeRunnerMin, + Config: testutil.NewConfigBuilder().BuildProviderConfig() + "\n" + resourceIntakeRunnerMin, ResourceName: intakeRunnerResource, ImportState: true, ImportStateVerify: true, @@ -121,7 +120,7 @@ func TestAccIntakeRunnerMin(t *testing.T) { // Update check: verifies API updated resource name without crashing { ConfigVariables: testIntakeRunnerConfigVarsMinUpdated(), - Config: testutil.IntakeProviderConfig() + "\n" + resourceIntakeRunnerMin, + Config: testutil.NewConfigBuilder().BuildProviderConfig() + "\n" + resourceIntakeRunnerMin, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(intakeRunnerResource, "project_id", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMinUpdated()["project_id"])), resource.TestCheckResourceAttr(intakeRunnerResource, "name", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMinUpdated()["name"])), @@ -146,7 +145,7 @@ func TestAccIntakeRunnerMax(t *testing.T) { // Create the max intake runner from HCL files and verify comparison { ConfigVariables: testIntakeRunnerConfigVarsMax, - Config: testutil.IntakeProviderConfig() + "\n" + resourceIntakeRunnerMax, + Config: testutil.NewConfigBuilder().BuildProviderConfig() + "\n" + resourceIntakeRunnerMax, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(intakeRunnerResource, "project_id", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMax["project_id"])), resource.TestCheckResourceAttr(intakeRunnerResource, "name", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMax["name"])), @@ -169,7 +168,7 @@ func TestAccIntakeRunnerMax(t *testing.T) { data "stackit_intake_runner" "example" { project_id = %s.project_id runner_id = %s.runner_id - }`, testutil.IntakeProviderConfig(), resourceIntakeRunnerMax, intakeRunnerResource, intakeRunnerResource), + }`, testutil.NewConfigBuilder().BuildProviderConfig(), resourceIntakeRunnerMax, intakeRunnerResource, intakeRunnerResource), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrPair(intakeRunnerResource, "project_id", "data.stackit_intake_runner.example", "project_id"), resource.TestCheckResourceAttrPair(intakeRunnerResource, "runner_id", "data.stackit_intake_runner.example", "runner_id"), @@ -183,7 +182,7 @@ func TestAccIntakeRunnerMax(t *testing.T) { // Simulate terraform import { ConfigVariables: testIntakeRunnerConfigVarsMax, - Config: testutil.IntakeProviderConfig() + "\n" + resourceIntakeRunnerMax, + Config: testutil.NewConfigBuilder().BuildProviderConfig() + "\n" + resourceIntakeRunnerMax, ResourceName: intakeRunnerResource, ImportState: true, ImportStateVerify: true, @@ -200,7 +199,7 @@ func TestAccIntakeRunnerMax(t *testing.T) { // Update and verify changes are reflected { ConfigVariables: testIntakeRunnerConfigVarsMaxUpdated(), - Config: testutil.IntakeProviderConfig() + "\n" + resourceIntakeRunnerMax, + Config: testutil.NewConfigBuilder().BuildProviderConfig() + "\n" + resourceIntakeRunnerMax, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(intakeRunnerResource, "project_id", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMax["project_id"])), resource.TestCheckResourceAttr(intakeRunnerResource, "name", testutil.ConvertConfigVariable(testIntakeRunnerConfigVarsMaxUpdated()["name"])), @@ -222,16 +221,11 @@ func TestAccIntakeRunnerMax(t *testing.T) { // testAccCheckIntakeRunnerDestroy act as independent auditor to verify destroy operation func testAccCheckIntakeRunnerDestroy(s *terraform.State) error { ctx := context.Background() - var client *intake.APIClient - var err error - - if testutil.IntakeCustomEndpoint == "" { - client, err = intake.NewAPIClient() - } else { - client, err = intake.NewAPIClient( - sdkConfig.WithEndpoint(testutil.IntakeCustomEndpoint), - ) + client, err := intake.NewAPIClient(testutil.NewConfigBuilder().BuildClientOptions(testutil.GitCustomEndpoint, false)...) + if err != nil { + return fmt.Errorf("creating client: %w", err) } + if err != nil { return fmt.Errorf("creating client: %w", err) } From 10997d93d77e481e79dd9ccec575b59e0863449f Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 23 Apr 2026 07:43:23 +0200 Subject: [PATCH 39/44] Rebase --- go.mod | 1 + 1 file changed, 1 insertion(+) diff --git a/go.mod b/go.mod index 207f2cfd4..c03684d3a 100644 --- a/go.mod +++ b/go.mod @@ -210,6 +210,7 @@ require ( github.com/spf13/pflag v1.0.10 // indirect github.com/spf13/viper v1.12.0 // indirect github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect + github.com/stackitcloud/stackit-sdk-go/services/intake v0.8.1 // indirect github.com/stbenjam/no-sprintf-host-port v0.3.1 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.11.1 // indirect From 0f503ad655816b7b9c7b92ce7796d2d137ae3109 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Fri, 24 Apr 2026 16:02:04 +0200 Subject: [PATCH 40/44] Lint --- stackit/internal/services/intake/intake_acc_test.go | 1 + stackit/internal/services/intake/runner/data_source.go | 1 + stackit/internal/services/intake/runner/resource.go | 3 ++- stackit/internal/services/intake/runner/resource_test.go | 1 + stackit/internal/services/intake/utils/utils.go | 1 + 5 files changed, 6 insertions(+), 1 deletion(-) diff --git a/stackit/internal/services/intake/intake_acc_test.go b/stackit/internal/services/intake/intake_acc_test.go index 950e4f0fb..91d1c7a98 100644 --- a/stackit/internal/services/intake/intake_acc_test.go +++ b/stackit/internal/services/intake/intake_acc_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-testing/terraform" intake "github.com/stackitcloud/stackit-sdk-go/services/intake/v1betaapi" "github.com/stackitcloud/stackit-sdk-go/services/intake/v1betaapi/wait" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil" ) diff --git a/stackit/internal/services/intake/runner/data_source.go b/stackit/internal/services/intake/runner/data_source.go index 0d075be7a..f0f92ede9 100644 --- a/stackit/internal/services/intake/runner/data_source.go +++ b/stackit/internal/services/intake/runner/data_source.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/stackitcloud/stackit-sdk-go/core/oapierror" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" intakeUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/intake/utils" diff --git a/stackit/internal/services/intake/runner/resource.go b/stackit/internal/services/intake/runner/resource.go index c7480c438..16a8cf556 100644 --- a/stackit/internal/services/intake/runner/resource.go +++ b/stackit/internal/services/intake/runner/resource.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/stackitcloud/stackit-sdk-go/core/oapierror" coreUtils "github.com/stackitcloud/stackit-sdk-go/core/utils" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" intakeUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/intake/utils" @@ -436,7 +437,7 @@ func mapFields(runnerResp *intake.IntakeRunnerResponse, model *Model, region str model.RunnerId = types.StringValue(runnerResp.Id) } - if runnerResp.Labels == nil || len(runnerResp.Labels) == 0 { + if len(runnerResp.Labels) == 0 { model.Labels = types.MapNull(types.StringType) } else { labels, diags := types.MapValueFrom(context.Background(), types.StringType, runnerResp.Labels) diff --git a/stackit/internal/services/intake/runner/resource_test.go b/stackit/internal/services/intake/runner/resource_test.go index 5c885ecc0..f7c9f2b9b 100644 --- a/stackit/internal/services/intake/runner/resource_test.go +++ b/stackit/internal/services/intake/runner/resource_test.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" "github.com/stackitcloud/stackit-sdk-go/core/utils" intake "github.com/stackitcloud/stackit-sdk-go/services/intake/v1betaapi" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" ) diff --git a/stackit/internal/services/intake/utils/utils.go b/stackit/internal/services/intake/utils/utils.go index 15b4ff7ca..87d1fd648 100644 --- a/stackit/internal/services/intake/utils/utils.go +++ b/stackit/internal/services/intake/utils/utils.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/stackitcloud/stackit-sdk-go/core/config" intake "github.com/stackitcloud/stackit-sdk-go/services/intake/v1betaapi" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils" ) From c2918d717b75f2761255152cca95f74ff1affb8f Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 5 May 2026 13:47:51 +0200 Subject: [PATCH 41/44] rebase --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index c03684d3a..e6f6dc7d6 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/stackitcloud/stackit-sdk-go/services/edge v0.11.0 github.com/stackitcloud/stackit-sdk-go/services/git v0.12.2 github.com/stackitcloud/stackit-sdk-go/services/iaas v1.11.1 + github.com/stackitcloud/stackit-sdk-go/services/intake v0.8.1 github.com/stackitcloud/stackit-sdk-go/services/kms v1.8.0 github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.12.2 github.com/stackitcloud/stackit-sdk-go/services/logme v0.28.2 @@ -210,7 +211,6 @@ require ( github.com/spf13/pflag v1.0.10 // indirect github.com/spf13/viper v1.12.0 // indirect github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect - github.com/stackitcloud/stackit-sdk-go/services/intake v0.8.1 // indirect github.com/stbenjam/no-sprintf-host-port v0.3.1 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.11.1 // indirect diff --git a/go.sum b/go.sum index fba53fc30..b11040084 100644 --- a/go.sum +++ b/go.sum @@ -686,6 +686,8 @@ github.com/stackitcloud/stackit-sdk-go/services/git v0.12.2 h1:SOqbdC5hvjJRMUWEb github.com/stackitcloud/stackit-sdk-go/services/git v0.12.2/go.mod h1:YZEL+gaK+ELn5E9VtK8yvz5RcmCBH+JkRpf6YbNVSbM= github.com/stackitcloud/stackit-sdk-go/services/iaas v1.11.1 h1:HcKqjwIjv4OAW1aWI0U/JWjnzTwzSvdr6DLasH940EU= github.com/stackitcloud/stackit-sdk-go/services/iaas v1.11.1/go.mod h1:Ts06id0KejUlQWbpR+/rm+tKng6QkTuFV1VQTPJ4dA4= +github.com/stackitcloud/stackit-sdk-go/services/intake v0.8.1 h1:Wkr/1OS/bn6cokqHtKJsYZjeHr+ludfMnoxKu9XVTWE= +github.com/stackitcloud/stackit-sdk-go/services/intake v0.8.1/go.mod h1:A7HS+7n4ZyIwFbHoJwBOGNTBd3udE+/LumEwPu2KvRg= github.com/stackitcloud/stackit-sdk-go/services/kms v1.8.0 h1:w2wPPo87PPqYO/cvWCz6GGE/VYAvGh3yaK4UEOXY9Gw= github.com/stackitcloud/stackit-sdk-go/services/kms v1.8.0/go.mod h1:pVaCmb1ZHAPGVRlSlBlVOjThp9Tb2sX9+nRX0M+d1KU= github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.12.2 h1:3Xnt5lnMmqVWChvH8lYJwpRoRatoqXfHlZ12wgNwUD4= From 2965e736a4f0f450c60023f5df7d7b1f35fb93b6 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 5 May 2026 15:16:27 +0200 Subject: [PATCH 42/44] Remove uneeded checks --- .../services/intake/runner/resource.go | 25 +++---------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/stackit/internal/services/intake/runner/resource.go b/stackit/internal/services/intake/runner/resource.go index 16a8cf556..98459c5d1 100644 --- a/stackit/internal/services/intake/runner/resource.go +++ b/stackit/internal/services/intake/runner/resource.go @@ -431,11 +431,7 @@ func mapFields(runnerResp *intake.IntakeRunnerResponse, model *Model, region str runnerResp.Id, ) - if runnerResp.Id == "" { - model.RunnerId = types.StringNull() - } else { - model.RunnerId = types.StringValue(runnerResp.Id) - } + model.RunnerId = types.StringValue(runnerResp.Id) if len(runnerResp.Labels) == 0 { model.Labels = types.MapNull(types.StringType) @@ -447,26 +443,13 @@ func mapFields(runnerResp *intake.IntakeRunnerResponse, model *Model, region str model.Labels = labels } - if runnerResp.DisplayName == "" { - model.Name = types.StringNull() - } else { - model.Name = types.StringValue(runnerResp.DisplayName) - } + model.Name = types.StringValue(runnerResp.DisplayName) model.Description = types.StringPointerValue(runnerResp.Description) model.Region = types.StringValue(region) - if runnerResp.MaxMessageSizeKiB == 0 { - model.MaxMessageSizeKiB = types.Int32Null() - } else { - model.MaxMessageSizeKiB = types.Int32Value(runnerResp.MaxMessageSizeKiB) - } - - if runnerResp.MaxMessagesPerHour == 0 { - model.MaxMessagesPerHour = types.Int32Null() - } else { - model.MaxMessagesPerHour = types.Int32Value(runnerResp.MaxMessagesPerHour) - } + model.MaxMessageSizeKiB = types.Int32Value(runnerResp.MaxMessageSizeKiB) + model.MaxMessagesPerHour = types.Int32Value(runnerResp.MaxMessagesPerHour) return nil } From cd3022834c06ed0864ca1826122870208cedf96b Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 5 May 2026 16:48:36 +0200 Subject: [PATCH 43/44] address testing changes --- stackit/internal/services/intake/runner/resource_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stackit/internal/services/intake/runner/resource_test.go b/stackit/internal/services/intake/runner/resource_test.go index f7c9f2b9b..e8e1f869b 100644 --- a/stackit/internal/services/intake/runner/resource_test.go +++ b/stackit/internal/services/intake/runner/resource_test.go @@ -81,12 +81,12 @@ func TestMapFields(t *testing.T) { Id: types.StringValue("pid,eu01,"), ProjectId: types.StringValue("pid"), Region: types.StringValue("eu01"), - RunnerId: types.StringNull(), - Name: types.StringNull(), + RunnerId: types.StringValue(""), + Name: types.StringValue(""), Description: types.StringNull(), Labels: types.MapNull(types.StringType), - MaxMessageSizeKiB: types.Int32Null(), - MaxMessagesPerHour: types.Int32Null(), + MaxMessageSizeKiB: types.Int32Value(0), + MaxMessagesPerHour: types.Int32Value(0), }, false, }, From 37fde5f969ebbd3457235c2a6efc243f816d325c Mon Sep 17 00:00:00 2001 From: Ruben Hoenle Date: Fri, 8 May 2026 14:10:55 +0200 Subject: [PATCH 44/44] rename datasource file --- .../services/intake/runner/{data_source.go => datasource.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename stackit/internal/services/intake/runner/{data_source.go => datasource.go} (100%) diff --git a/stackit/internal/services/intake/runner/data_source.go b/stackit/internal/services/intake/runner/datasource.go similarity index 100% rename from stackit/internal/services/intake/runner/data_source.go rename to stackit/internal/services/intake/runner/datasource.go