From 62b6a1b3de2c17801f2a175e498b0967f29f815c Mon Sep 17 00:00:00 2001 From: Vicente Pinto Date: Wed, 13 Dec 2023 14:17:28 +0000 Subject: [PATCH] Support project UUID identifier in resource manager project (#161) * Add projectId to resource manager project, fix value conversion error * Support both uuid and container id, update acceptance tests * Update docs * Fix unit tests * Adapt acc test names --- Makefile | 3 + docs/data-sources/resourcemanager_project.md | 11 +- docs/index.md | 2 +- docs/resources/resourcemanager_project.md | 7 +- docs/resources/ske_cluster.md | 5 +- .../resourcemanager/project/datasource.go | 46 +++++++- .../resourcemanager/project/resource.go | 35 +++++- .../resourcemanager/project/resource_test.go | 64 ++++++++-- .../resourcemanager_acc_test.go | 110 ++++++++++++------ stackit/internal/testutil/testutil.go | 4 +- stackit/provider.go | 2 +- 11 files changed, 225 insertions(+), 64 deletions(-) diff --git a/Makefile b/Makefile index 5820843d..d233cd4c 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,9 @@ generate-docs: @echo "Generating documentation with tfplugindocs" @$(SCRIPTS_BASE)/tfplugindocs.sh +build: + @go build -o bin/terraform-provider-stackit + # TEST test: @echo "Running tests for the terraform provider" diff --git a/docs/data-sources/resourcemanager_project.md b/docs/data-sources/resourcemanager_project.md index 16376f9c..40b86919 100644 --- a/docs/data-sources/resourcemanager_project.md +++ b/docs/data-sources/resourcemanager_project.md @@ -3,12 +3,12 @@ page_title: "stackit_resourcemanager_project Data Source - stackit" subcategory: "" description: |- - Resource Manager project data source schema. + Resource Manager project data source schema. To identify the project, you need to provider either projectid or containerid. If you provide both, project_id will be used. --- # stackit_resourcemanager_project (Data Source) -Resource Manager project data source schema. +Resource Manager project data source schema. To identify the project, you need to provider either project_id or container_id. If you provide both, project_id will be used. ## Example Usage @@ -22,13 +22,14 @@ data "stackit_resourcemanager_project" "example" { ## Schema -### Required +### Optional -- `container_id` (String) Project container ID. +- `container_id` (String) Project container ID. Globally unique, user-friendly identifier. +- `project_id` (String) Project UUID identifier. This is the ID that can be used in most of the other resources to identify the project. ### Read-Only - `id` (String) Terraform's internal data source. ID. It is structured as "`container_id`". - `labels` (Map of String) Labels are key-value string pairs which can be attached to a resource container. A label key must match the regex [A-ZÄÜÖa-zäüöß0-9_-]{1,64}. A label value must match the regex ^$|[A-ZÄÜÖa-zäüöß0-9_-]{1,64} - `name` (String) Project name. -- `parent_container_id` (String) Parent container ID +- `parent_container_id` (String) Parent resource identifier. Both container ID (user-friendly) and UUID are supported diff --git a/docs/index.md b/docs/index.md index 07ea1b18..af6b187f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -131,7 +131,7 @@ Using this flow is less secure since the token is long-lived. You can provide th - `region` (String) Region will be used as the default location for regional services. Not all services require a region, some are global - `resourcemanager_custom_endpoint` (String) Custom endpoint for the Resource Manager service - `secretsmanager_custom_endpoint` (String) Custom endpoint for the Secrets Manager service -- `service_account_email` (String) Service account email. It can also be set using the environment variable STACKIT_SERVICE_ACCOUNT_EMAIL +- `service_account_email` (String) Service account email. It can also be set using the environment variable STACKIT_SERVICE_ACCOUNT_EMAIL. It is required if you want to use the resource manager project resource. - `service_account_key` (String) Service account key used for authentication. If set alongside private key, the key flow will be used to authenticate all operations. - `service_account_key_path` (String) Path for the service account key used for authentication. If set alongside the private key, the key flow will be used to authenticate all operations. - `service_account_token` (String) Token used for authentication. If set, the token flow will be used to authenticate all operations. diff --git a/docs/resources/resourcemanager_project.md b/docs/resources/resourcemanager_project.md index a77e2cf2..ca3934b6 100644 --- a/docs/resources/resourcemanager_project.md +++ b/docs/resources/resourcemanager_project.md @@ -3,12 +3,12 @@ page_title: "stackit_resourcemanager_project Resource - stackit" subcategory: "" description: |- - Resource Manager project resource schema. + Resource Manager project resource schema. To use this resource, it is required that you set the service account email in the provider configuration. --- # stackit_resourcemanager_project (Resource) -Resource Manager project resource schema. +Resource Manager project resource schema. To use this resource, it is required that you set the service account email in the provider configuration. ## Example Usage @@ -31,7 +31,7 @@ resource "stackit_resourcemanager_project" "example" { - `name` (String) Project name. - `owner_email` (String) Email address of the owner of the project. This value is only considered during creation. Changing it afterwards will have no effect. -- `parent_container_id` (String) Parent container ID +- `parent_container_id` (String) Parent resource identifier. Both container ID (user-friendly) and UUID are supported ### Optional @@ -41,3 +41,4 @@ resource "stackit_resourcemanager_project" "example" { - `container_id` (String) Project container ID. Globally unique, user-friendly identifier. - `id` (String) Terraform's internal resource ID. It is structured as "`container_id`". +- `project_id` (String) Project UUID identifier. This is the ID that can be used in most of the other resources to identify the project. diff --git a/docs/resources/ske_cluster.md b/docs/resources/ske_cluster.md index 6a61fc0e..47b08a01 100644 --- a/docs/resources/ske_cluster.md +++ b/docs/resources/ske_cluster.md @@ -111,9 +111,12 @@ Optional: Required: -- `allowed_cidrs` (List of String) Specify a list of CIDRs to whitelist. - `enabled` (Boolean) Is ACL enabled? +Optional: + +- `allowed_cidrs` (List of String) Specify a list of CIDRs to whitelist. + ### Nested Schema for `extensions.argus` diff --git a/stackit/internal/services/resourcemanager/project/datasource.go b/stackit/internal/services/resourcemanager/project/datasource.go index 346cffa8..fc6de657 100644 --- a/stackit/internal/services/resourcemanager/project/datasource.go +++ b/stackit/internal/services/resourcemanager/project/datasource.go @@ -28,6 +28,7 @@ var ( type ModelData struct { Id types.String `tfsdk:"id"` // needed by TF + ProjectId types.String `tfsdk:"project_id"` ContainerId types.String `tfsdk:"container_id"` ContainerParentId types.String `tfsdk:"parent_container_id"` Name types.String `tfsdk:"name"` @@ -88,10 +89,11 @@ func (d *projectDataSource) Configure(ctx context.Context, req datasource.Config // Schema defines the schema for the data source. func (d *projectDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { descriptions := map[string]string{ - "main": "Resource Manager project data source schema.", + "main": "Resource Manager project data source schema. To identify the project, you need to provider either project_id or container_id. If you provide both, project_id will be used.", "id": "Terraform's internal data source. ID. It is structured as \"`container_id`\".", - "container_id": "Project container ID.", - "parent_container_id": "Parent container ID", + "project_id": "Project UUID identifier. This is the ID that can be used in most of the other resources to identify the project.", + "container_id": "Project container ID. Globally unique, user-friendly identifier.", + "parent_container_id": "Parent resource identifier. Both container ID (user-friendly) and UUID are supported", "name": "Project name.", "labels": `Labels are key-value string pairs which can be attached to a resource container. A label key must match the regex [A-ZÄÜÖa-zäüöß0-9_-]{1,64}. A label value must match the regex ^$|[A-ZÄÜÖa-zäüöß0-9_-]{1,64}`, } @@ -103,9 +105,16 @@ func (d *projectDataSource) Schema(_ context.Context, _ datasource.SchemaRequest Description: descriptions["id"], Computed: true, }, + "project_id": schema.StringAttribute{ + Description: descriptions["project_id"], + Optional: true, + Validators: []validator.String{ + validate.UUID(), + }, + }, "container_id": schema.StringAttribute{ Description: descriptions["container_id"], - Required: true, + Optional: true, Validators: []validator.String{ validate.NoSeparator(), }, @@ -154,10 +163,25 @@ func (d *projectDataSource) Read(ctx context.Context, req datasource.ReadRequest if resp.Diagnostics.HasError() { return } + + projectId := state.ProjectId.ValueString() + ctx = tflog.SetField(ctx, "project_id", projectId) + containerId := state.ContainerId.ValueString() - ctx = tflog.SetField(ctx, "project_id", containerId) + ctx = tflog.SetField(ctx, "container_id", containerId) - projectResp, err := d.client.GetProject(ctx, containerId).Execute() + if containerId == "" && projectId == "" { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading project", "Either container_id or project_id must be set") + return + } + + // set project identifier. If projectId is provided, it takes precedence over containerId + var identifier = containerId + if projectId != "" { + identifier = projectId + } + + projectResp, err := d.client.GetProject(ctx, identifier).Execute() if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading project", fmt.Sprintf("Calling API: %v", err)) return @@ -184,6 +208,15 @@ func mapDataFields(ctx context.Context, projectResp *resourcemanager.ProjectResp return fmt.Errorf("model input is nil") } + var projectId string + if model.ProjectId.ValueString() != "" { + projectId = model.ProjectId.ValueString() + } else if projectResp.ProjectId != nil { + projectId = *projectResp.ProjectId + } else { + return fmt.Errorf("project id not present") + } + var containerId string if model.ContainerId.ValueString() != "" { containerId = model.ContainerId.ValueString() @@ -204,6 +237,7 @@ func mapDataFields(ctx context.Context, projectResp *resourcemanager.ProjectResp } model.Id = types.StringValue(containerId) + model.ProjectId = types.StringValue(projectId) model.ContainerId = types.StringValue(containerId) model.ContainerParentId = types.StringPointerValue(projectResp.Parent.ContainerId) model.Name = types.StringPointerValue(projectResp.Name) diff --git a/stackit/internal/services/resourcemanager/project/resource.go b/stackit/internal/services/resourcemanager/project/resource.go index 21930237..b0793d60 100644 --- a/stackit/internal/services/resourcemanager/project/resource.go +++ b/stackit/internal/services/resourcemanager/project/resource.go @@ -6,6 +6,7 @@ import ( "regexp" "strings" + "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/schema/validator" @@ -39,6 +40,7 @@ const ( type Model struct { Id types.String `tfsdk:"id"` // needed by TF + ProjectId types.String `tfsdk:"project_id"` ContainerId types.String `tfsdk:"container_id"` ContainerParentId types.String `tfsdk:"parent_container_id"` Name types.String `tfsdk:"name"` @@ -102,10 +104,11 @@ func (r *projectResource) Configure(ctx context.Context, req resource.ConfigureR // Schema defines the schema for the resource. func (r *projectResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { descriptions := map[string]string{ - "main": "Resource Manager project resource schema.", + "main": "Resource Manager project resource schema. To use this resource, it is required that you set the service account email in the provider configuration.", "id": "Terraform's internal resource ID. It is structured as \"`container_id`\".", + "project_id": "Project UUID identifier. This is the ID that can be used in most of the other resources to identify the project.", "container_id": "Project container ID. Globally unique, user-friendly identifier.", - "parent_container_id": "Parent container ID", + "parent_container_id": "Parent resource identifier. Both container ID (user-friendly) and UUID are supported", "name": "Project name.", "labels": "Labels are key-value string pairs which can be attached to a resource container. A label key must match the regex [A-ZÄÜÖa-zäüöß0-9_-]{1,64}. A label value must match the regex ^$|[A-ZÄÜÖa-zäüöß0-9_-]{1,64}", "owner_email": "Email address of the owner of the project. This value is only considered during creation. Changing it afterwards will have no effect.", @@ -121,6 +124,16 @@ func (r *projectResource) Schema(_ context.Context, _ resource.SchemaRequest, re stringplanmodifier.UseStateForUnknown(), }, }, + "project_id": schema.StringAttribute{ + Description: descriptions["project_id"], + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + Validators: []validator.String{ + validate.UUID(), + }, + }, "container_id": schema.StringAttribute{ Description: descriptions["container_id"], Computed: true, @@ -357,6 +370,15 @@ func mapFields(ctx context.Context, projectResp *resourcemanager.ProjectResponse return fmt.Errorf("model input is nil") } + var projectId string + if model.ProjectId.ValueString() != "" { + projectId = model.ProjectId.ValueString() + } else if projectResp.ProjectId != nil { + projectId = *projectResp.ProjectId + } else { + return fmt.Errorf("project id not present") + } + var containerId string if model.ContainerId.ValueString() != "" { containerId = model.ContainerId.ValueString() @@ -377,9 +399,16 @@ func mapFields(ctx context.Context, projectResp *resourcemanager.ProjectResponse } model.Id = types.StringValue(containerId) + model.ProjectId = types.StringValue(projectId) model.ContainerId = types.StringValue(containerId) if projectResp.Parent != nil { - model.ContainerParentId = types.StringPointerValue(projectResp.Parent.ContainerId) + if _, err := uuid.Parse(model.ContainerParentId.ValueString()); err == nil { + // the provided containerParentId is the UUID identifier + model.ContainerParentId = types.StringPointerValue(projectResp.Parent.Id) + } else { + // the provided containerParentId is the user-friendly container id + model.ContainerParentId = types.StringPointerValue(projectResp.Parent.ContainerId) + } } else { model.ContainerParentId = types.StringNull() } diff --git a/stackit/internal/services/resourcemanager/project/resource_test.go b/stackit/internal/services/resourcemanager/project/resource_test.go index 93091288..2e7ad785 100644 --- a/stackit/internal/services/resourcemanager/project/resource_test.go +++ b/stackit/internal/services/resourcemanager/project/resource_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/stackitcloud/stackit-sdk-go/core/utils" "github.com/stackitcloud/stackit-sdk-go/services/resourcemanager" @@ -12,21 +13,26 @@ import ( ) func TestMapFields(t *testing.T) { + testUUID := uuid.New().String() tests := []struct { - description string - input *resourcemanager.ProjectResponseWithParents - expected Model - expectedLabels *map[string]string - isValid bool + description string + uuidContainerParentId bool + input *resourcemanager.ProjectResponseWithParents + expected Model + expectedLabels *map[string]string + isValid bool }{ { "default_ok", + false, &resourcemanager.ProjectResponseWithParents{ ContainerId: utils.Ptr("cid"), + ProjectId: utils.Ptr("pid"), }, Model{ Id: types.StringValue("cid"), ContainerId: types.StringValue("cid"), + ProjectId: types.StringValue("pid"), ContainerParentId: types.StringNull(), Name: types.StringNull(), }, @@ -34,22 +40,55 @@ func TestMapFields(t *testing.T) { true, }, { - "values_ok", + "container_parent_id_ok", + false, &resourcemanager.ProjectResponseWithParents{ ContainerId: utils.Ptr("cid"), + ProjectId: utils.Ptr("pid"), Labels: &map[string]string{ "label1": "ref1", "label2": "ref2", }, Parent: &resourcemanager.Parent{ - ContainerId: utils.Ptr("pid"), + ContainerId: utils.Ptr("parent_cid"), + Id: utils.Ptr("parent_pid"), }, Name: utils.Ptr("name"), }, Model{ Id: types.StringValue("cid"), ContainerId: types.StringValue("cid"), - ContainerParentId: types.StringValue("pid"), + ProjectId: types.StringValue("pid"), + ContainerParentId: types.StringValue("parent_cid"), + Name: types.StringValue("name"), + }, + &map[string]string{ + "label1": "ref1", + "label2": "ref2", + }, + true, + }, + { + "uuid_parent_id_ok", + true, + &resourcemanager.ProjectResponseWithParents{ + ContainerId: utils.Ptr("cid"), + ProjectId: utils.Ptr("pid"), + Labels: &map[string]string{ + "label1": "ref1", + "label2": "ref2", + }, + Parent: &resourcemanager.Parent{ + ContainerId: utils.Ptr("parent_cid"), + Id: utils.Ptr(testUUID), + }, + Name: utils.Ptr("name"), + }, + Model{ + Id: types.StringValue("cid"), + ContainerId: types.StringValue("cid"), + ProjectId: types.StringValue("pid"), + ContainerParentId: types.StringValue(testUUID), Name: types.StringValue("name"), }, &map[string]string{ @@ -60,6 +99,7 @@ func TestMapFields(t *testing.T) { }, { "response_nil_fail", + false, nil, Model{}, nil, @@ -67,6 +107,7 @@ func TestMapFields(t *testing.T) { }, { "no_resource_id", + false, &resourcemanager.ProjectResponseWithParents{}, Model{}, nil, @@ -84,8 +125,13 @@ func TestMapFields(t *testing.T) { } tt.expected.Labels = convertedLabels } + var containerParentId = types.StringNull() + if tt.uuidContainerParentId { + containerParentId = types.StringValue(testUUID) + } state := &Model{ - ContainerId: tt.expected.ContainerId, + ContainerId: tt.expected.ContainerId, + ContainerParentId: containerParentId, } err := mapFields(context.Background(), tt.input, state) diff --git a/stackit/internal/services/resourcemanager/resourcemanager_acc_test.go b/stackit/internal/services/resourcemanager/resourcemanager_acc_test.go index 7afee30b..edb3a782 100644 --- a/stackit/internal/services/resourcemanager/resourcemanager_acc_test.go +++ b/stackit/internal/services/resourcemanager/resourcemanager_acc_test.go @@ -19,6 +19,7 @@ import ( var projectResource = map[string]string{ "name": fmt.Sprintf("acc-pj-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)), "parent_container_id": testutil.TestProjectParentContainerID, + "parent_uuid": testutil.TestProjectParentUUID, "billing_reference": "TEST-REF", "new_label": "a-label", } @@ -29,16 +30,22 @@ func resourceConfig(name string, label *string) string { labelConfig = fmt.Sprintf("new_label = %q", *label) } return fmt.Sprintf(` - %s + %[1]s - resource "stackit_resourcemanager_project" "project" { - parent_container_id = "%s" - name = "%s" + resource "stackit_resourcemanager_project" "parent_by_container" { + parent_container_id = "%[2]s" + name = "%[3]s" labels = { - "billing_reference" = "%s" - %s + "billing_reference" = "%[4]s" + %[5]s } - owner_email = "%s" + owner_email = "%[6]s" + } + + resource "stackit_resourcemanager_project" "parent_by_uuid" { + parent_container_id = "%[7]s" + name = "%[3]s-uuid" + owner_email = "%[6]s" } `, testutil.ResourceManagerProviderConfig(), @@ -47,6 +54,7 @@ func resourceConfig(name string, label *string) string { projectResource["billing_reference"], labelConfig, testutil.TestProjectServiceAccountEmail, + projectResource["parent_uuid"], ) } @@ -59,12 +67,19 @@ func TestAccResourceManagerResource(t *testing.T) { { Config: resourceConfig(projectResource["name"], nil), Check: resource.ComposeAggregateTestCheckFunc( - // Project data - resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.project", "container_id"), - resource.TestCheckResourceAttr("stackit_resourcemanager_project.project", "name", projectResource["name"]), - resource.TestCheckResourceAttr("stackit_resourcemanager_project.project", "parent_container_id", projectResource["parent_container_id"]), - resource.TestCheckResourceAttr("stackit_resourcemanager_project.project", "labels.%", "1"), - resource.TestCheckResourceAttr("stackit_resourcemanager_project.project", "labels.billing_reference", projectResource["billing_reference"]), + // Parent container id project data + resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.parent_by_container", "container_id"), + resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.parent_by_container", "project_id"), + resource.TestCheckResourceAttr("stackit_resourcemanager_project.parent_by_container", "name", projectResource["name"]), + resource.TestCheckResourceAttr("stackit_resourcemanager_project.parent_by_container", "parent_container_id", projectResource["parent_container_id"]), + resource.TestCheckResourceAttr("stackit_resourcemanager_project.parent_by_container", "labels.%", "1"), + resource.TestCheckResourceAttr("stackit_resourcemanager_project.parent_by_container", "labels.billing_reference", projectResource["billing_reference"]), + + // Parent UUID project data + resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.parent_by_uuid", "container_id"), + resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.parent_by_uuid", "project_id"), + resource.TestCheckResourceAttr("stackit_resourcemanager_project.parent_by_uuid", "name", fmt.Sprintf("%s-uuid", projectResource["name"])), + resource.TestCheckResourceAttr("stackit_resourcemanager_project.parent_by_uuid", "parent_container_id", projectResource["parent_uuid"]), ), }, // Data source @@ -72,30 +87,57 @@ func TestAccResourceManagerResource(t *testing.T) { Config: fmt.Sprintf(` %s - data "stackit_resourcemanager_project" "project" { - container_id = stackit_resourcemanager_project.project.container_id - }`, + data "stackit_resourcemanager_project" "project_by_container" { + container_id = stackit_resourcemanager_project.parent_by_container.container_id + } + + data "stackit_resourcemanager_project" "project_by_uuid" { + project_id = stackit_resourcemanager_project.parent_by_container.project_id + } + + data "stackit_resourcemanager_project" "project_by_both" { + container_id = stackit_resourcemanager_project.parent_by_container.container_id + project_id = stackit_resourcemanager_project.parent_by_container.project_id + } + `, resourceConfig(projectResource["name"], nil), ), Check: resource.ComposeAggregateTestCheckFunc( - // Project data - resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project", "id"), - resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project", "container_id"), - resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.project", "name", projectResource["name"]), - resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project", "parent_container_id"), - resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.project", "labels.%", "1"), - resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.project", "labels.billing_reference", projectResource["billing_reference"]), - resource.TestCheckResourceAttrPair("data.stackit_resourcemanager_project.project", "project_id", - "stackit_resourcemanager_project.project", "project_id"), + // Container project data + resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project_by_container", "id"), + resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project_by_container", "container_id"), + resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project_by_container", "project_id"), + resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.project_by_container", "name", projectResource["name"]), + resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project_by_container", "parent_container_id"), + resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.project_by_container", "labels.%", "1"), + resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.project_by_container", "labels.billing_reference", projectResource["billing_reference"]), + + // UUID project data + resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project_by_uuid", "id"), + resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project_by_uuid", "container_id"), + resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project_by_uuid", "project_id"), + resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.project_by_uuid", "name", projectResource["name"]), + resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project_by_uuid", "parent_container_id"), + resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.project_by_uuid", "labels.%", "1"), + resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.project_by_uuid", "labels.billing_reference", projectResource["billing_reference"]), + + // Both project data + resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project_by_both", "id"), + resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project_by_both", "container_id"), + resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project_by_both", "project_id"), + resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.project_by_both", "name", projectResource["name"]), + resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project_by_both", "parent_container_id"), + resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.project_by_both", "labels.%", "1"), + resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.project_by_both", "labels.billing_reference", projectResource["billing_reference"]), ), }, // Import { - ResourceName: "stackit_resourcemanager_project.project", + ResourceName: "stackit_resourcemanager_project.parent_by_container", ImportStateIdFunc: func(s *terraform.State) (string, error) { - r, ok := s.RootModule().Resources["stackit_resourcemanager_project.project"] + r, ok := s.RootModule().Resources["stackit_resourcemanager_project.parent_by_container"] if !ok { - return "", fmt.Errorf("couldn't find resource stackit_resourcemanager_project.project") + return "", fmt.Errorf("couldn't find resource stackit_resourcemanager_project.parent_by_container") } containerId, ok := r.Primary.Attributes["container_id"] if !ok { @@ -115,12 +157,12 @@ func TestAccResourceManagerResource(t *testing.T) { Config: resourceConfig(fmt.Sprintf("%s-new", projectResource["name"]), utils.Ptr("a-label")), Check: resource.ComposeAggregateTestCheckFunc( // Project data - resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.project", "container_id"), - resource.TestCheckResourceAttr("stackit_resourcemanager_project.project", "name", fmt.Sprintf("%s-new", projectResource["name"])), - resource.TestCheckResourceAttr("stackit_resourcemanager_project.project", "parent_container_id", projectResource["parent_container_id"]), - resource.TestCheckResourceAttr("stackit_resourcemanager_project.project", "labels.%", "2"), - resource.TestCheckResourceAttr("stackit_resourcemanager_project.project", "labels.billing_reference", projectResource["billing_reference"]), - resource.TestCheckResourceAttr("stackit_resourcemanager_project.project", "labels.new_label", projectResource["new_label"]), + resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.parent_by_container", "container_id"), + resource.TestCheckResourceAttr("stackit_resourcemanager_project.parent_by_container", "name", fmt.Sprintf("%s-new", projectResource["name"])), + resource.TestCheckResourceAttr("stackit_resourcemanager_project.parent_by_container", "parent_container_id", projectResource["parent_container_id"]), + resource.TestCheckResourceAttr("stackit_resourcemanager_project.parent_by_container", "labels.%", "2"), + resource.TestCheckResourceAttr("stackit_resourcemanager_project.parent_by_container", "labels.billing_reference", projectResource["billing_reference"]), + resource.TestCheckResourceAttr("stackit_resourcemanager_project.parent_by_container", "labels.new_label", projectResource["new_label"]), ), }, // Deletion is done by the framework implicitly diff --git a/stackit/internal/testutil/testutil.go b/stackit/internal/testutil/testutil.go index 7364c1fe..d9ec893b 100644 --- a/stackit/internal/testutil/testutil.go +++ b/stackit/internal/testutil/testutil.go @@ -30,8 +30,10 @@ var ( // ProjectId is the id of project used for tests ProjectId = os.Getenv("TF_ACC_PROJECT_ID") - // TestProjectParentContainerID is the container id of the organization under which projects are created as part of the resource-manager acceptance tests + // TestProjectParentContainerID is the container id of the parent resource under which projects are created as part of the resource-manager acceptance tests TestProjectParentContainerID = os.Getenv("TF_ACC_TEST_PROJECT_PARENT_CONTAINER_ID") + // TestProjectParentContainerID is the uuid of the parent resource under which projects are created as part of the resource-manager acceptance tests + TestProjectParentUUID = os.Getenv("TF_ACC_TEST_PROJECT_PARENT_UUID") // TestProjectServiceAccountEmail is the e-mail of a service account with admin permissions on the organization under which projects are created as part of the resource-manager acceptance tests TestProjectServiceAccountEmail = os.Getenv("TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_EMAIL") diff --git a/stackit/provider.go b/stackit/provider.go index de1530d5..678eddbe 100644 --- a/stackit/provider.go +++ b/stackit/provider.go @@ -107,7 +107,7 @@ func (p *Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *pro "service_account_key": "Service account key used for authentication. If set alongside private key, the key flow will be used to authenticate all operations.", "private_key_path": "Path for the private RSA key used for authentication. If set alongside the service account key, the key flow will be used to authenticate all operations.", "private_key": "Private RSA key used for authentication. If set alongside the service account key, the key flow will be used to authenticate all operations.", - "service_account_email": "Service account email. It can also be set using the environment variable STACKIT_SERVICE_ACCOUNT_EMAIL", + "service_account_email": "Service account email. It can also be set using the environment variable STACKIT_SERVICE_ACCOUNT_EMAIL. It is required if you want to use the resource manager project resource.", "region": "Region will be used as the default location for regional services. Not all services require a region, some are global", "dns_custom_endpoint": "Custom endpoint for the DNS service", "postgresql_custom_endpoint": "Custom endpoint for the PostgreSQL service",