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
This commit is contained in:
parent
dc3c3487c0
commit
62b6a1b3de
11 changed files with 226 additions and 65 deletions
3
Makefile
3
Makefile
|
|
@ -24,6 +24,9 @@ generate-docs:
|
||||||
@echo "Generating documentation with tfplugindocs"
|
@echo "Generating documentation with tfplugindocs"
|
||||||
@$(SCRIPTS_BASE)/tfplugindocs.sh
|
@$(SCRIPTS_BASE)/tfplugindocs.sh
|
||||||
|
|
||||||
|
build:
|
||||||
|
@go build -o bin/terraform-provider-stackit
|
||||||
|
|
||||||
# TEST
|
# TEST
|
||||||
test:
|
test:
|
||||||
@echo "Running tests for the terraform provider"
|
@echo "Running tests for the terraform provider"
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@
|
||||||
page_title: "stackit_resourcemanager_project Data Source - stackit"
|
page_title: "stackit_resourcemanager_project Data Source - stackit"
|
||||||
subcategory: ""
|
subcategory: ""
|
||||||
description: |-
|
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)
|
# 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
|
## Example Usage
|
||||||
|
|
||||||
|
|
@ -22,13 +22,14 @@ data "stackit_resourcemanager_project" "example" {
|
||||||
<!-- schema generated by tfplugindocs -->
|
<!-- schema generated by tfplugindocs -->
|
||||||
## Schema
|
## 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
|
### Read-Only
|
||||||
|
|
||||||
- `id` (String) Terraform's internal data source. ID. It is structured as "`container_id`".
|
- `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}
|
- `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.
|
- `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
|
||||||
|
|
|
||||||
|
|
@ -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
|
- `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
|
- `resourcemanager_custom_endpoint` (String) Custom endpoint for the Resource Manager service
|
||||||
- `secretsmanager_custom_endpoint` (String) Custom endpoint for the Secrets 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` (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_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.
|
- `service_account_token` (String) Token used for authentication. If set, the token flow will be used to authenticate all operations.
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@
|
||||||
page_title: "stackit_resourcemanager_project Resource - stackit"
|
page_title: "stackit_resourcemanager_project Resource - stackit"
|
||||||
subcategory: ""
|
subcategory: ""
|
||||||
description: |-
|
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)
|
# 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
|
## Example Usage
|
||||||
|
|
||||||
|
|
@ -31,7 +31,7 @@ resource "stackit_resourcemanager_project" "example" {
|
||||||
|
|
||||||
- `name` (String) Project name.
|
- `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.
|
- `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
|
### Optional
|
||||||
|
|
||||||
|
|
@ -41,3 +41,4 @@ resource "stackit_resourcemanager_project" "example" {
|
||||||
|
|
||||||
- `container_id` (String) Project container ID. Globally unique, user-friendly identifier.
|
- `container_id` (String) Project container ID. Globally unique, user-friendly identifier.
|
||||||
- `id` (String) Terraform's internal resource ID. It is structured as "`container_id`".
|
- `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.
|
||||||
|
|
|
||||||
|
|
@ -111,9 +111,12 @@ Optional:
|
||||||
|
|
||||||
Required:
|
Required:
|
||||||
|
|
||||||
- `allowed_cidrs` (List of String) Specify a list of CIDRs to whitelist.
|
|
||||||
- `enabled` (Boolean) Is ACL enabled?
|
- `enabled` (Boolean) Is ACL enabled?
|
||||||
|
|
||||||
|
Optional:
|
||||||
|
|
||||||
|
- `allowed_cidrs` (List of String) Specify a list of CIDRs to whitelist.
|
||||||
|
|
||||||
|
|
||||||
<a id="nestedatt--extensions--argus"></a>
|
<a id="nestedatt--extensions--argus"></a>
|
||||||
### Nested Schema for `extensions.argus`
|
### Nested Schema for `extensions.argus`
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ var (
|
||||||
|
|
||||||
type ModelData struct {
|
type ModelData struct {
|
||||||
Id types.String `tfsdk:"id"` // needed by TF
|
Id types.String `tfsdk:"id"` // needed by TF
|
||||||
|
ProjectId types.String `tfsdk:"project_id"`
|
||||||
ContainerId types.String `tfsdk:"container_id"`
|
ContainerId types.String `tfsdk:"container_id"`
|
||||||
ContainerParentId types.String `tfsdk:"parent_container_id"`
|
ContainerParentId types.String `tfsdk:"parent_container_id"`
|
||||||
Name types.String `tfsdk:"name"`
|
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.
|
// Schema defines the schema for the data source.
|
||||||
func (d *projectDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
func (d *projectDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||||
descriptions := map[string]string{
|
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`\".",
|
"id": "Terraform's internal data source. ID. It is structured as \"`container_id`\".",
|
||||||
"container_id": "Project 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.",
|
||||||
"parent_container_id": "Parent container ID",
|
"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.",
|
"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}`,
|
"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"],
|
Description: descriptions["id"],
|
||||||
Computed: true,
|
Computed: true,
|
||||||
},
|
},
|
||||||
|
"project_id": schema.StringAttribute{
|
||||||
|
Description: descriptions["project_id"],
|
||||||
|
Optional: true,
|
||||||
|
Validators: []validator.String{
|
||||||
|
validate.UUID(),
|
||||||
|
},
|
||||||
|
},
|
||||||
"container_id": schema.StringAttribute{
|
"container_id": schema.StringAttribute{
|
||||||
Description: descriptions["container_id"],
|
Description: descriptions["container_id"],
|
||||||
Required: true,
|
Optional: true,
|
||||||
Validators: []validator.String{
|
Validators: []validator.String{
|
||||||
validate.NoSeparator(),
|
validate.NoSeparator(),
|
||||||
},
|
},
|
||||||
|
|
@ -154,10 +163,25 @@ func (d *projectDataSource) Read(ctx context.Context, req datasource.ReadRequest
|
||||||
if resp.Diagnostics.HasError() {
|
if resp.Diagnostics.HasError() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
containerId := state.ContainerId.ValueString()
|
|
||||||
ctx = tflog.SetField(ctx, "project_id", containerId)
|
|
||||||
|
|
||||||
projectResp, err := d.client.GetProject(ctx, containerId).Execute()
|
projectId := state.ProjectId.ValueString()
|
||||||
|
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||||
|
|
||||||
|
containerId := state.ContainerId.ValueString()
|
||||||
|
ctx = tflog.SetField(ctx, "container_id", containerId)
|
||||||
|
|
||||||
|
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 {
|
if err != nil {
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading project", fmt.Sprintf("Calling API: %v", err))
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading project", fmt.Sprintf("Calling API: %v", err))
|
||||||
return
|
return
|
||||||
|
|
@ -184,6 +208,15 @@ func mapDataFields(ctx context.Context, projectResp *resourcemanager.ProjectResp
|
||||||
return fmt.Errorf("model input is nil")
|
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
|
var containerId string
|
||||||
if model.ContainerId.ValueString() != "" {
|
if model.ContainerId.ValueString() != "" {
|
||||||
containerId = 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.Id = types.StringValue(containerId)
|
||||||
|
model.ProjectId = types.StringValue(projectId)
|
||||||
model.ContainerId = types.StringValue(containerId)
|
model.ContainerId = types.StringValue(containerId)
|
||||||
model.ContainerParentId = types.StringPointerValue(projectResp.Parent.ContainerId)
|
model.ContainerParentId = types.StringPointerValue(projectResp.Parent.ContainerId)
|
||||||
model.Name = types.StringPointerValue(projectResp.Name)
|
model.Name = types.StringPointerValue(projectResp.Name)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator"
|
"github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator"
|
||||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||||
|
|
@ -39,6 +40,7 @@ const (
|
||||||
|
|
||||||
type Model struct {
|
type Model struct {
|
||||||
Id types.String `tfsdk:"id"` // needed by TF
|
Id types.String `tfsdk:"id"` // needed by TF
|
||||||
|
ProjectId types.String `tfsdk:"project_id"`
|
||||||
ContainerId types.String `tfsdk:"container_id"`
|
ContainerId types.String `tfsdk:"container_id"`
|
||||||
ContainerParentId types.String `tfsdk:"parent_container_id"`
|
ContainerParentId types.String `tfsdk:"parent_container_id"`
|
||||||
Name types.String `tfsdk:"name"`
|
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.
|
// Schema defines the schema for the resource.
|
||||||
func (r *projectResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
func (r *projectResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||||
descriptions := map[string]string{
|
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`\".",
|
"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.",
|
"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.",
|
"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}",
|
"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.",
|
"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(),
|
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{
|
"container_id": schema.StringAttribute{
|
||||||
Description: descriptions["container_id"],
|
Description: descriptions["container_id"],
|
||||||
Computed: true,
|
Computed: true,
|
||||||
|
|
@ -357,6 +370,15 @@ func mapFields(ctx context.Context, projectResp *resourcemanager.ProjectResponse
|
||||||
return fmt.Errorf("model input is nil")
|
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
|
var containerId string
|
||||||
if model.ContainerId.ValueString() != "" {
|
if model.ContainerId.ValueString() != "" {
|
||||||
containerId = 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.Id = types.StringValue(containerId)
|
||||||
|
model.ProjectId = types.StringValue(projectId)
|
||||||
model.ContainerId = types.StringValue(containerId)
|
model.ContainerId = types.StringValue(containerId)
|
||||||
if projectResp.Parent != nil {
|
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 {
|
} else {
|
||||||
model.ContainerParentId = types.StringNull()
|
model.ContainerParentId = types.StringNull()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/services/resourcemanager"
|
"github.com/stackitcloud/stackit-sdk-go/services/resourcemanager"
|
||||||
|
|
@ -12,21 +13,26 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMapFields(t *testing.T) {
|
func TestMapFields(t *testing.T) {
|
||||||
|
testUUID := uuid.New().String()
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
description string
|
description string
|
||||||
input *resourcemanager.ProjectResponseWithParents
|
uuidContainerParentId bool
|
||||||
expected Model
|
input *resourcemanager.ProjectResponseWithParents
|
||||||
expectedLabels *map[string]string
|
expected Model
|
||||||
isValid bool
|
expectedLabels *map[string]string
|
||||||
|
isValid bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"default_ok",
|
"default_ok",
|
||||||
|
false,
|
||||||
&resourcemanager.ProjectResponseWithParents{
|
&resourcemanager.ProjectResponseWithParents{
|
||||||
ContainerId: utils.Ptr("cid"),
|
ContainerId: utils.Ptr("cid"),
|
||||||
|
ProjectId: utils.Ptr("pid"),
|
||||||
},
|
},
|
||||||
Model{
|
Model{
|
||||||
Id: types.StringValue("cid"),
|
Id: types.StringValue("cid"),
|
||||||
ContainerId: types.StringValue("cid"),
|
ContainerId: types.StringValue("cid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
ContainerParentId: types.StringNull(),
|
ContainerParentId: types.StringNull(),
|
||||||
Name: types.StringNull(),
|
Name: types.StringNull(),
|
||||||
},
|
},
|
||||||
|
|
@ -34,22 +40,55 @@ func TestMapFields(t *testing.T) {
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"values_ok",
|
"container_parent_id_ok",
|
||||||
|
false,
|
||||||
&resourcemanager.ProjectResponseWithParents{
|
&resourcemanager.ProjectResponseWithParents{
|
||||||
ContainerId: utils.Ptr("cid"),
|
ContainerId: utils.Ptr("cid"),
|
||||||
|
ProjectId: utils.Ptr("pid"),
|
||||||
Labels: &map[string]string{
|
Labels: &map[string]string{
|
||||||
"label1": "ref1",
|
"label1": "ref1",
|
||||||
"label2": "ref2",
|
"label2": "ref2",
|
||||||
},
|
},
|
||||||
Parent: &resourcemanager.Parent{
|
Parent: &resourcemanager.Parent{
|
||||||
ContainerId: utils.Ptr("pid"),
|
ContainerId: utils.Ptr("parent_cid"),
|
||||||
|
Id: utils.Ptr("parent_pid"),
|
||||||
},
|
},
|
||||||
Name: utils.Ptr("name"),
|
Name: utils.Ptr("name"),
|
||||||
},
|
},
|
||||||
Model{
|
Model{
|
||||||
Id: types.StringValue("cid"),
|
Id: types.StringValue("cid"),
|
||||||
ContainerId: 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"),
|
Name: types.StringValue("name"),
|
||||||
},
|
},
|
||||||
&map[string]string{
|
&map[string]string{
|
||||||
|
|
@ -60,6 +99,7 @@ func TestMapFields(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"response_nil_fail",
|
"response_nil_fail",
|
||||||
|
false,
|
||||||
nil,
|
nil,
|
||||||
Model{},
|
Model{},
|
||||||
nil,
|
nil,
|
||||||
|
|
@ -67,6 +107,7 @@ func TestMapFields(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"no_resource_id",
|
"no_resource_id",
|
||||||
|
false,
|
||||||
&resourcemanager.ProjectResponseWithParents{},
|
&resourcemanager.ProjectResponseWithParents{},
|
||||||
Model{},
|
Model{},
|
||||||
nil,
|
nil,
|
||||||
|
|
@ -84,8 +125,13 @@ func TestMapFields(t *testing.T) {
|
||||||
}
|
}
|
||||||
tt.expected.Labels = convertedLabels
|
tt.expected.Labels = convertedLabels
|
||||||
}
|
}
|
||||||
|
var containerParentId = types.StringNull()
|
||||||
|
if tt.uuidContainerParentId {
|
||||||
|
containerParentId = types.StringValue(testUUID)
|
||||||
|
}
|
||||||
state := &Model{
|
state := &Model{
|
||||||
ContainerId: tt.expected.ContainerId,
|
ContainerId: tt.expected.ContainerId,
|
||||||
|
ContainerParentId: containerParentId,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := mapFields(context.Background(), tt.input, state)
|
err := mapFields(context.Background(), tt.input, state)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import (
|
||||||
var projectResource = map[string]string{
|
var projectResource = map[string]string{
|
||||||
"name": fmt.Sprintf("acc-pj-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)),
|
"name": fmt.Sprintf("acc-pj-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)),
|
||||||
"parent_container_id": testutil.TestProjectParentContainerID,
|
"parent_container_id": testutil.TestProjectParentContainerID,
|
||||||
|
"parent_uuid": testutil.TestProjectParentUUID,
|
||||||
"billing_reference": "TEST-REF",
|
"billing_reference": "TEST-REF",
|
||||||
"new_label": "a-label",
|
"new_label": "a-label",
|
||||||
}
|
}
|
||||||
|
|
@ -29,16 +30,22 @@ func resourceConfig(name string, label *string) string {
|
||||||
labelConfig = fmt.Sprintf("new_label = %q", *label)
|
labelConfig = fmt.Sprintf("new_label = %q", *label)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf(`
|
return fmt.Sprintf(`
|
||||||
%s
|
%[1]s
|
||||||
|
|
||||||
resource "stackit_resourcemanager_project" "project" {
|
resource "stackit_resourcemanager_project" "parent_by_container" {
|
||||||
parent_container_id = "%s"
|
parent_container_id = "%[2]s"
|
||||||
name = "%s"
|
name = "%[3]s"
|
||||||
labels = {
|
labels = {
|
||||||
"billing_reference" = "%s"
|
"billing_reference" = "%[4]s"
|
||||||
%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(),
|
testutil.ResourceManagerProviderConfig(),
|
||||||
|
|
@ -47,6 +54,7 @@ func resourceConfig(name string, label *string) string {
|
||||||
projectResource["billing_reference"],
|
projectResource["billing_reference"],
|
||||||
labelConfig,
|
labelConfig,
|
||||||
testutil.TestProjectServiceAccountEmail,
|
testutil.TestProjectServiceAccountEmail,
|
||||||
|
projectResource["parent_uuid"],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,12 +67,19 @@ func TestAccResourceManagerResource(t *testing.T) {
|
||||||
{
|
{
|
||||||
Config: resourceConfig(projectResource["name"], nil),
|
Config: resourceConfig(projectResource["name"], nil),
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
// Project data
|
// Parent container id project data
|
||||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.project", "container_id"),
|
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.parent_by_container", "container_id"),
|
||||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.project", "name", projectResource["name"]),
|
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.parent_by_container", "project_id"),
|
||||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.project", "parent_container_id", projectResource["parent_container_id"]),
|
resource.TestCheckResourceAttr("stackit_resourcemanager_project.parent_by_container", "name", projectResource["name"]),
|
||||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.project", "labels.%", "1"),
|
resource.TestCheckResourceAttr("stackit_resourcemanager_project.parent_by_container", "parent_container_id", projectResource["parent_container_id"]),
|
||||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.project", "labels.billing_reference", projectResource["billing_reference"]),
|
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
|
// Data source
|
||||||
|
|
@ -72,30 +87,57 @@ func TestAccResourceManagerResource(t *testing.T) {
|
||||||
Config: fmt.Sprintf(`
|
Config: fmt.Sprintf(`
|
||||||
%s
|
%s
|
||||||
|
|
||||||
data "stackit_resourcemanager_project" "project" {
|
data "stackit_resourcemanager_project" "project_by_container" {
|
||||||
container_id = stackit_resourcemanager_project.project.container_id
|
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),
|
resourceConfig(projectResource["name"], nil),
|
||||||
),
|
),
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
// Project data
|
// Container project data
|
||||||
resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project", "id"),
|
resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project_by_container", "id"),
|
||||||
resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project", "container_id"),
|
resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project_by_container", "container_id"),
|
||||||
resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.project", "name", projectResource["name"]),
|
resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project_by_container", "project_id"),
|
||||||
resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project", "parent_container_id"),
|
resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.project_by_container", "name", projectResource["name"]),
|
||||||
resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.project", "labels.%", "1"),
|
resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.project_by_container", "parent_container_id"),
|
||||||
resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.project", "labels.billing_reference", projectResource["billing_reference"]),
|
resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.project_by_container", "labels.%", "1"),
|
||||||
resource.TestCheckResourceAttrPair("data.stackit_resourcemanager_project.project", "project_id",
|
resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.project_by_container", "labels.billing_reference", projectResource["billing_reference"]),
|
||||||
"stackit_resourcemanager_project.project", "project_id"),
|
|
||||||
|
// 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
|
// Import
|
||||||
{
|
{
|
||||||
ResourceName: "stackit_resourcemanager_project.project",
|
ResourceName: "stackit_resourcemanager_project.parent_by_container",
|
||||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
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 {
|
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"]
|
containerId, ok := r.Primary.Attributes["container_id"]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
@ -115,12 +157,12 @@ func TestAccResourceManagerResource(t *testing.T) {
|
||||||
Config: resourceConfig(fmt.Sprintf("%s-new", projectResource["name"]), utils.Ptr("a-label")),
|
Config: resourceConfig(fmt.Sprintf("%s-new", projectResource["name"]), utils.Ptr("a-label")),
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
// Project data
|
// Project data
|
||||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.project", "container_id"),
|
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.parent_by_container", "container_id"),
|
||||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.project", "name", fmt.Sprintf("%s-new", projectResource["name"])),
|
resource.TestCheckResourceAttr("stackit_resourcemanager_project.parent_by_container", "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.parent_by_container", "parent_container_id", projectResource["parent_container_id"]),
|
||||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.project", "labels.%", "2"),
|
resource.TestCheckResourceAttr("stackit_resourcemanager_project.parent_by_container", "labels.%", "2"),
|
||||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.project", "labels.billing_reference", projectResource["billing_reference"]),
|
resource.TestCheckResourceAttr("stackit_resourcemanager_project.parent_by_container", "labels.billing_reference", projectResource["billing_reference"]),
|
||||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.project", "labels.new_label", projectResource["new_label"]),
|
resource.TestCheckResourceAttr("stackit_resourcemanager_project.parent_by_container", "labels.new_label", projectResource["new_label"]),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
// Deletion is done by the framework implicitly
|
// Deletion is done by the framework implicitly
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,10 @@ var (
|
||||||
|
|
||||||
// ProjectId is the id of project used for tests
|
// ProjectId is the id of project used for tests
|
||||||
ProjectId = os.Getenv("TF_ACC_PROJECT_ID")
|
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 = 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 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")
|
TestProjectServiceAccountEmail = os.Getenv("TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_EMAIL")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.",
|
"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_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.",
|
"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",
|
"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",
|
"dns_custom_endpoint": "Custom endpoint for the DNS service",
|
||||||
"postgresql_custom_endpoint": "Custom endpoint for the PostgreSQL service",
|
"postgresql_custom_endpoint": "Custom endpoint for the PostgreSQL service",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue