From e2f483a119d10d526d5d26a249bc938fd2aa583c Mon Sep 17 00:00:00 2001 From: Devansh Thakur Date: Mon, 8 Dec 2025 00:05:21 +0100 Subject: [PATCH 01/43] 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 01b56db9c..dc47c13ea 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" @@ -172,6 +173,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"` @@ -361,6 +363,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"], @@ -520,6 +526,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 }) @@ -741,6 +748,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 2475933b6893ff96a59530ace9b86a6fc3fb7fdc Mon Sep 17 00:00:00 2001 From: Devansh Thakur Date: Mon, 8 Dec 2025 09:07:58 +0100 Subject: [PATCH 02/43] 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 a811cce5918abf9322d666000ae41fce333d59c6 Mon Sep 17 00:00:00 2001 From: Devansh Thakur Date: Tue, 16 Dec 2025 22:19:46 +0100 Subject: [PATCH 03/43] 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 dc47c13ea..f054c88f3 100644 --- a/stackit/provider.go +++ b/stackit/provider.go @@ -654,6 +654,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 7bbbf08e4a550a0cc0d7095e1625f38508167a83 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 22 Jan 2026 11:33:04 +0100 Subject: [PATCH 04/43] 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 50c8bd120117bb4a318d818832c38b506b8bdb75 Mon Sep 17 00:00:00 2001 From: BipBopBipBop Date: Thu, 22 Jan 2026 11:56:50 +0100 Subject: [PATCH 05/43] 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 33f6815c4e7a20e85f871dbe38ff9fc11c7bdf0d Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 22 Jan 2026 17:12:32 +0100 Subject: [PATCH 06/43] 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 a2d08e101182524ed766b23b2a6c878e0be1d976 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 22 Jan 2026 17:12:50 +0100 Subject: [PATCH 07/43] 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 f054c88f3..e90afe9a6 100644 --- a/stackit/provider.go +++ b/stackit/provider.go @@ -229,6 +229,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", @@ -257,6 +258,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 6c3a88a5e641c597b26ede5e6fc68047b331e8a0 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 22 Jan 2026 17:47:36 +0100 Subject: [PATCH 08/43] 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 c0a6685f967beea603b89a5e1c390c2767c2e16c Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 13:33:43 +0100 Subject: [PATCH 09/43] 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 0e5feb6fc5bc700bd1ee9136ac3a487a196cab6f Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 13:51:35 +0100 Subject: [PATCH 10/43] 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 d24b1efb773ec7815150fab05140e1e1ca6ffb9f Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 14:45:43 +0100 Subject: [PATCH 11/43] 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 d188eadce51557dad4b199b20b479e4c3fc15453 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 16:52:48 +0100 Subject: [PATCH 12/43] 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 7a3c2e0b222af6ee2b496f601cb92ddf649eff95 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 17:08:46 +0100 Subject: [PATCH 13/43] 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 2becb95357279aa0eb8cbdf99e6bb47d53646358 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 17:11:34 +0100 Subject: [PATCH 14/43] 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 a4549e68f761fee1a290db36fffd7b8d0dd9fbae Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 17:12:18 +0100 Subject: [PATCH 15/43] 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 58c64daecb83c241dcc0a80cac3f5236c76ac241 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 17:20:56 +0100 Subject: [PATCH 16/43] 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 d5bccb83ab5bd293b670a232050189f0ba1c90a0 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 17:25:13 +0100 Subject: [PATCH 17/43] 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 ba16d3aa2fc5a226e378bdad05f63798b5afff39 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 18:32:45 +0100 Subject: [PATCH 18/43] 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 f2ad0fa169e0c06bb522fefd578f801cb16110bd Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 26 Jan 2026 18:46:04 +0100 Subject: [PATCH 19/43] 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 2bd1e8495510dced19bf383a3d04c685a0ad670a Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 27 Jan 2026 15:52:57 +0100 Subject: [PATCH 20/43] 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 8d679841c52f28238ddf05ad3d8bb135185dae5c Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 29 Jan 2026 10:42:01 +0100 Subject: [PATCH 21/43] 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 81539a9e1ea543ec080df461246876553a787549 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 17 Feb 2026 13:49:07 +0100 Subject: [PATCH 22/43] 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 74612d344389a92bdce44211c166dc29e24a88ae Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 17 Feb 2026 16:16:36 +0100 Subject: [PATCH 23/43] 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 68d828b4473b7334ecffcd24305e4d579582666e Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Fri, 20 Feb 2026 16:48:48 +0100 Subject: [PATCH 24/43] 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 9acff526e849ace6f6ccd45eea7b6c31973665c1 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 23 Feb 2026 10:17:32 +0100 Subject: [PATCH 25/43] lint --- stackit/provider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stackit/provider.go b/stackit/provider.go index e90afe9a6..e530cdd58 100644 --- a/stackit/provider.go +++ b/stackit/provider.go @@ -229,7 +229,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 f086fb258bc8c7713a34c61deea4cade1e5d677c Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 19 Mar 2026 08:34:08 +0100 Subject: [PATCH 26/43] 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 5e0ed3e942ef6a2e0ef902d388c81eaf6f04572b Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 31 Mar 2026 12:36:03 +0200 Subject: [PATCH 27/43] 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 5bb64c28f0ab85cbf06991885a344850931da2a5 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 31 Mar 2026 12:37:00 +0200 Subject: [PATCH 28/43] 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 9c4b9076a5ff50f23c0c1a45a96d3a569f16c651 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 31 Mar 2026 12:43:10 +0200 Subject: [PATCH 29/43] 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 be1d39029c15ac18b3bbf95090baaec1707276be Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 31 Mar 2026 12:44:34 +0200 Subject: [PATCH 30/43] 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 0e95436a62997164ba8af5607e7a2636efc2418b Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 31 Mar 2026 12:50:10 +0200 Subject: [PATCH 31/43] 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 a98876eee8b34ed23613ece38f4face937b2ea2e Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 31 Mar 2026 16:51:04 +0200 Subject: [PATCH 32/43] 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 c6ff75608a73535cf4aecf9760d7830065906b9a Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 31 Mar 2026 16:56:44 +0200 Subject: [PATCH 33/43] 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 9f50efef47455935cd52a89691c03a856be4df64 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 31 Mar 2026 17:20:06 +0200 Subject: [PATCH 34/43] 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 688ad0a277a7a3d7576e177157b78a40a944720b Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Wed, 1 Apr 2026 15:00:13 +0200 Subject: [PATCH 35/43] 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 198175b3d2e7a3d3516e4f77fbaefbb0c6fa190f Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Wed, 1 Apr 2026 16:35:36 +0200 Subject: [PATCH 36/43] 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 88198309a496002cd188397e5bd2d99314d27185 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 21 Apr 2026 10:54:41 +0200 Subject: [PATCH 37/43] 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 ea3e940e80421bf438f001345883ba715cdd57b2 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 23 Apr 2026 07:25:20 +0200 Subject: [PATCH 38/43] 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 768e0051ba4b1de61b29d8322f007d164900e373 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 23 Apr 2026 07:43:23 +0200 Subject: [PATCH 39/43] Rebase --- go.mod | 1 + 1 file changed, 1 insertion(+) diff --git a/go.mod b/go.mod index 5a2e5e022..133487222 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 81b24b7c30e118b2e313ced047417804a6170194 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Fri, 24 Apr 2026 16:02:04 +0200 Subject: [PATCH 40/43] 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 4da9b972fedc6414431c94e7e1274f5f47f92547 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 5 May 2026 13:47:51 +0200 Subject: [PATCH 41/43] rebase --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 133487222..3773398a8 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/stackitcloud/stackit-sdk-go/services/edge v0.9.2 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 bd0b66677..1ac291538 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 d1ca511b1eeb78823ace046009b581c7aa74f0aa Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 5 May 2026 15:16:27 +0200 Subject: [PATCH 42/43] 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 a49df9f6fbec402210f836040dffe20b0b1ae7c8 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 5 May 2026 16:48:36 +0200 Subject: [PATCH 43/43] 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, },