feat(resourcemanager): add folder resource/datasource (#975)
* feat(resourcemanager): add folder resource/datasource * feat(resourcemanager): add created_at and updated_at attributes to resourcemanager project/folder --------- Signed-off-by: Mauritz Uphoff <mauritz.uphoff@stackit.cloud>
This commit is contained in:
parent
27e4ef0227
commit
813b8c0e81
17 changed files with 1844 additions and 171 deletions
39
docs/data-sources/resourcemanager_folder.md
Normal file
39
docs/data-sources/resourcemanager_folder.md
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "stackit_resourcemanager_folder Data Source - stackit"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
Resource Manager folder data source schema. To identify the folder, you need to provide the container_id.
|
||||
~> This datasource 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_resourcemanager_folder (Data Source)
|
||||
|
||||
Resource Manager folder data source schema. To identify the folder, you need to provide the container_id.
|
||||
|
||||
~> This datasource 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
|
||||
data "stackit_resourcemanager_folder" "example" {
|
||||
container_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `container_id` (String) Folder container ID. Globally unique, user-friendly identifier.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `creation_time` (String) Date-time at which the folder was created.
|
||||
- `folder_id` (String) Folder UUID identifier. Globally unique folder identifier
|
||||
- `id` (String) Terraform's internal resource 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) The name of the folder.
|
||||
- `parent_container_id` (String) Parent resource identifier. Both container ID (user-friendly) and UUID are supported.
|
||||
- `update_time` (String) Date-time at which the folder was last modified.
|
||||
|
|
@ -29,7 +29,9 @@ data "stackit_resourcemanager_project" "example" {
|
|||
|
||||
### Read-Only
|
||||
|
||||
- `creation_time` (String) Date-time at which the project was created.
|
||||
- `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 resource identifier. Both container ID (user-friendly) and UUID are supported
|
||||
- `update_time` (String) Date-time at which the project was last modified.
|
||||
|
|
|
|||
64
docs/resources/resourcemanager_folder.md
Normal file
64
docs/resources/resourcemanager_folder.md
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "stackit_resourcemanager_folder Resource - stackit"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
Resource Manager folder resource schema.
|
||||
~> This 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_resourcemanager_folder (Resource)
|
||||
|
||||
Resource Manager folder resource schema.
|
||||
|
||||
~> This 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
|
||||
resource "stackit_resourcemanager_folder" "example" {
|
||||
name = "example-folder"
|
||||
owner_email = "foo.bar@stackit.cloud"
|
||||
parent_container_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
|
||||
# Note:
|
||||
# You can add projects under folders.
|
||||
# However, when deleting a project, be aware:
|
||||
# - Projects may remain "invisible" for up to 7 days after deletion
|
||||
# - During this time, deleting the parent folder may fail because the project is still technically linked
|
||||
resource "stackit_resourcemanager_project" "example_project" {
|
||||
name = "example-project"
|
||||
owner_email = "foo.bar@stackit.cloud"
|
||||
parent_container_id = stackit_resourcemanager_folder.example.container_id
|
||||
}
|
||||
|
||||
# Only use the import statement, if you want to import an existing resourcemanager folder
|
||||
# Note: There will be a conflict which needs to be resolved manually.
|
||||
# Must set a configuration value for the owner_email attribute as the provider has marked it as required.
|
||||
import {
|
||||
to = stackit_resourcemanager_folder.import-example
|
||||
id = var.container_id
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `name` (String) The name of the folder.
|
||||
- `owner_email` (String) Email address of the owner of the folder. This value is only considered during creation. Changing it afterwards will have no effect.
|
||||
- `parent_container_id` (String) Parent resource identifier. Both container ID (user-friendly) and UUID are supported.
|
||||
|
||||
### Optional
|
||||
|
||||
- `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}.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `container_id` (String) Folder container ID. Globally unique, user-friendly identifier.
|
||||
- `creation_time` (String) Date-time at which the folder was created.
|
||||
- `folder_id` (String) Folder UUID identifier. Globally unique folder identifier
|
||||
- `id` (String) Terraform's internal resource ID. It is structured as "`container_id`".
|
||||
- `update_time` (String) Date-time at which the folder was last modified.
|
||||
|
|
@ -3,13 +3,13 @@
|
|||
page_title: "stackit_resourcemanager_project Resource - stackit"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
Resource Manager project resource schema. To use this resource, it is required that you set the service account email in the provider configuration.
|
||||
Resource Manager project resource schema.
|
||||
-> In case you're getting started with an empty STACKIT organization and want to use this resource to create projects in it, check out this guide https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/stackit_org_service_account for how to create a service account which you can use for authentication in the STACKIT Terraform provider.
|
||||
---
|
||||
|
||||
# stackit_resourcemanager_project (Resource)
|
||||
|
||||
Resource Manager project resource schema. To use this resource, it is required that you set the service account email in the provider configuration.
|
||||
Resource Manager project resource schema.
|
||||
|
||||
-> In case you're getting started with an empty STACKIT organization and want to use this resource to create projects in it, check out [this guide](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/stackit_org_service_account) for how to create a service account which you can use for authentication in the STACKIT Terraform provider.
|
||||
|
||||
|
|
@ -52,5 +52,7 @@ To create a project within a STACKIT Network Area, setting the label `networkAre
|
|||
### Read-Only
|
||||
|
||||
- `container_id` (String) Project container ID. Globally unique, user-friendly identifier.
|
||||
- `creation_time` (String) Date-time at which the project was created.
|
||||
- `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.
|
||||
- `update_time` (String) Date-time at which the project was last modified.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
data "stackit_resourcemanager_folder" "example" {
|
||||
container_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
resource "stackit_resourcemanager_folder" "example" {
|
||||
name = "example-folder"
|
||||
owner_email = "foo.bar@stackit.cloud"
|
||||
parent_container_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
|
||||
# Note:
|
||||
# You can add projects under folders.
|
||||
# However, when deleting a project, be aware:
|
||||
# - Projects may remain "invisible" for up to 7 days after deletion
|
||||
# - During this time, deleting the parent folder may fail because the project is still technically linked
|
||||
resource "stackit_resourcemanager_project" "example_project" {
|
||||
name = "example-project"
|
||||
owner_email = "foo.bar@stackit.cloud"
|
||||
parent_container_id = stackit_resourcemanager_folder.example.container_id
|
||||
}
|
||||
|
||||
# Only use the import statement, if you want to import an existing resourcemanager folder
|
||||
# Note: There will be a conflict which needs to be resolved manually.
|
||||
# Must set a configuration value for the owner_email attribute as the provider has marked it as required.
|
||||
import {
|
||||
to = stackit_resourcemanager_folder.import-example
|
||||
id = var.container_id
|
||||
}
|
||||
185
stackit/internal/services/resourcemanager/folder/datasource.go
Normal file
185
stackit/internal/services/resourcemanager/folder/datasource.go
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
package folder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"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/services/resourcemanager"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features"
|
||||
resourcemanagerUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/resourcemanager/utils"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
|
||||
)
|
||||
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var (
|
||||
_ datasource.DataSource = &folderDataSource{}
|
||||
_ datasource.DataSourceWithConfigure = &folderDataSource{}
|
||||
)
|
||||
|
||||
// NewFolderDataSource is a helper function to simplify the provider implementation.
|
||||
func NewFolderDataSource() datasource.DataSource {
|
||||
return &folderDataSource{}
|
||||
}
|
||||
|
||||
// folderDataSource is the data source implementation.
|
||||
type folderDataSource struct {
|
||||
client *resourcemanager.APIClient
|
||||
}
|
||||
|
||||
// Metadata returns the data source type name.
|
||||
func (d *folderDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_resourcemanager_folder"
|
||||
}
|
||||
|
||||
func (d *folderDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
providerData, ok := conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
features.CheckBetaResourcesEnabled(ctx, &providerData, &resp.Diagnostics, "stackit_resourcemanager_folder", "datasource")
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
apiClient := resourcemanagerUtils.ConfigureClient(ctx, &providerData, &resp.Diagnostics)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
d.client = apiClient
|
||||
tflog.Info(ctx, "Resource Manager client configured")
|
||||
}
|
||||
|
||||
// Schema defines the schema for the data source.
|
||||
func (d *folderDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
descriptions := map[string]string{
|
||||
"main": "Resource Manager folder data source schema. To identify the folder, you need to provide the container_id.",
|
||||
"id": "Terraform's internal resource ID. It is structured as \"`container_id`\".",
|
||||
"container_id": "Folder container ID. Globally unique, user-friendly identifier.",
|
||||
"folder_id": "Folder UUID identifier. Globally unique folder identifier",
|
||||
"parent_container_id": "Parent resource identifier. Both container ID (user-friendly) and UUID are supported.",
|
||||
"name": "The name of the folder.",
|
||||
"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 folder. This value is only considered during creation. Changing it afterwards will have no effect.",
|
||||
"creation_time": "Date-time at which the folder was created.",
|
||||
"update_time": "Date-time at which the folder was last modified.",
|
||||
}
|
||||
|
||||
resp.Schema = schema.Schema{
|
||||
Description: features.AddBetaDescription(descriptions["main"], core.Datasource),
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Description: descriptions["id"],
|
||||
Computed: true,
|
||||
},
|
||||
"container_id": schema.StringAttribute{
|
||||
Description: descriptions["container_id"],
|
||||
Validators: []validator.String{
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
Required: true,
|
||||
},
|
||||
"folder_id": schema.StringAttribute{
|
||||
Description: descriptions["folder_id"],
|
||||
Computed: true,
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
},
|
||||
},
|
||||
"parent_container_id": schema.StringAttribute{
|
||||
Description: descriptions["parent_container_id"],
|
||||
Computed: true,
|
||||
Validators: []validator.String{
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Description: descriptions["name"],
|
||||
Computed: true,
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
stringvalidator.LengthAtMost(63),
|
||||
},
|
||||
},
|
||||
"labels": schema.MapAttribute{
|
||||
Description: descriptions["labels"],
|
||||
ElementType: types.StringType,
|
||||
Computed: true,
|
||||
Validators: []validator.Map{
|
||||
mapvalidator.KeysAre(
|
||||
stringvalidator.RegexMatches(
|
||||
regexp.MustCompile(`[A-ZÄÜÖa-zäüöß0-9_-]{1,64}`),
|
||||
"must match expression"),
|
||||
),
|
||||
mapvalidator.ValueStringsAre(
|
||||
stringvalidator.RegexMatches(
|
||||
regexp.MustCompile(`[A-ZÄÜÖa-zäüöß0-9_-]{1,64}`),
|
||||
"must match expression"),
|
||||
),
|
||||
},
|
||||
},
|
||||
"creation_time": schema.StringAttribute{
|
||||
Description: descriptions["creation_time"],
|
||||
Computed: true,
|
||||
},
|
||||
"update_time": schema.StringAttribute{
|
||||
Description: descriptions["update_time"],
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Read refreshes the Terraform state with the latest data.
|
||||
func (d *folderDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model Model
|
||||
diags := req.Config.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
containerId := model.ContainerId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "container_id", containerId)
|
||||
|
||||
folderResp, err := d.client.GetFolderDetails(ctx, containerId).Execute()
|
||||
if err != nil {
|
||||
utils.LogError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
err,
|
||||
"Reading folder",
|
||||
fmt.Sprintf("folder with ID %q does not exist.", containerId),
|
||||
map[int]string{
|
||||
http.StatusForbidden: fmt.Sprintf("folder with ID %q not found or forbidden access", containerId),
|
||||
},
|
||||
)
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
err = mapFolderFields(ctx, folderResp, &model, &resp.State)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading folder", fmt.Sprintf("Processing API response: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Resource Manager folder read")
|
||||
}
|
||||
510
stackit/internal/services/resourcemanager/folder/resource.go
Normal file
510
stackit/internal/services/resourcemanager/folder/resource.go
Normal file
|
|
@ -0,0 +1,510 @@
|
|||
package folder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
"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/stackit-sdk-go/services/resourcemanager"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features"
|
||||
resourcemanagerUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/resourcemanager/utils"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
|
||||
)
|
||||
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var (
|
||||
_ resource.Resource = &folderResource{}
|
||||
_ resource.ResourceWithConfigure = &folderResource{}
|
||||
_ resource.ResourceWithImportState = &folderResource{}
|
||||
)
|
||||
|
||||
const (
|
||||
projectOwnerRole = "owner"
|
||||
)
|
||||
|
||||
type Model struct {
|
||||
Id types.String `tfsdk:"id"` // needed by TF
|
||||
FolderId types.String `tfsdk:"folder_id"`
|
||||
ContainerId types.String `tfsdk:"container_id"`
|
||||
ContainerParentId types.String `tfsdk:"parent_container_id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Labels types.Map `tfsdk:"labels"`
|
||||
CreationTime types.String `tfsdk:"creation_time"`
|
||||
UpdateTime types.String `tfsdk:"update_time"`
|
||||
}
|
||||
|
||||
type ResourceModel struct {
|
||||
Model
|
||||
OwnerEmail types.String `tfsdk:"owner_email"`
|
||||
}
|
||||
|
||||
// NewFolderResource is a helper function to simplify the provider implementation.
|
||||
func NewFolderResource() resource.Resource {
|
||||
return &folderResource{}
|
||||
}
|
||||
|
||||
// folderResource is the resource implementation.
|
||||
type folderResource struct {
|
||||
client *resourcemanager.APIClient
|
||||
}
|
||||
|
||||
// Metadata returns the resource type name.
|
||||
func (r *folderResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_resourcemanager_folder"
|
||||
}
|
||||
|
||||
// Configure adds the provider configured client to the resource.
|
||||
func (r *folderResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||
providerData, ok := conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
features.CheckBetaResourcesEnabled(ctx, &providerData, &resp.Diagnostics, "stackit_resourcemanager_folder", "resource")
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
apiClient := resourcemanagerUtils.ConfigureClient(ctx, &providerData, &resp.Diagnostics)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
r.client = apiClient
|
||||
tflog.Info(ctx, "Resource Manager client configured")
|
||||
}
|
||||
|
||||
// Schema defines the schema for the resource.
|
||||
func (r *folderResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
descriptions := map[string]string{
|
||||
"main": "Resource Manager folder resource schema.",
|
||||
"id": "Terraform's internal resource ID. It is structured as \"`container_id`\".",
|
||||
"container_id": "Folder container ID. Globally unique, user-friendly identifier.",
|
||||
"folder_id": "Folder UUID identifier. Globally unique folder identifier",
|
||||
"parent_container_id": "Parent resource identifier. Both container ID (user-friendly) and UUID are supported.",
|
||||
"name": "The name of the folder.",
|
||||
"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 folder. This value is only considered during creation. Changing it afterwards will have no effect.",
|
||||
"creation_time": "Date-time at which the folder was created.",
|
||||
"update_time": "Date-time at which the folder was last modified.",
|
||||
}
|
||||
|
||||
resp.Schema = schema.Schema{
|
||||
Description: features.AddBetaDescription(descriptions["main"], core.Resource),
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Description: descriptions["id"],
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"container_id": schema.StringAttribute{
|
||||
Description: descriptions["container_id"],
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"folder_id": schema.StringAttribute{
|
||||
Description: descriptions["folder_id"],
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
},
|
||||
},
|
||||
"parent_container_id": schema.StringAttribute{
|
||||
Description: descriptions["parent_container_id"],
|
||||
Required: true,
|
||||
Validators: []validator.String{
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Description: descriptions["name"],
|
||||
Required: true,
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
stringvalidator.LengthAtMost(63),
|
||||
},
|
||||
},
|
||||
"labels": schema.MapAttribute{
|
||||
Description: descriptions["labels"],
|
||||
ElementType: types.StringType,
|
||||
Optional: true,
|
||||
Validators: []validator.Map{
|
||||
mapvalidator.KeysAre(
|
||||
stringvalidator.RegexMatches(
|
||||
regexp.MustCompile(`[A-ZÄÜÖa-zäüöß0-9_-]{1,64}`),
|
||||
"must match expression"),
|
||||
),
|
||||
mapvalidator.ValueStringsAre(
|
||||
stringvalidator.RegexMatches(
|
||||
regexp.MustCompile(`^$|[A-ZÄÜÖa-zäüöß0-9_-]{1,64}`),
|
||||
"must match expression"),
|
||||
),
|
||||
},
|
||||
},
|
||||
"owner_email": schema.StringAttribute{
|
||||
Description: descriptions["owner_email"],
|
||||
Required: true,
|
||||
},
|
||||
"creation_time": schema.StringAttribute{
|
||||
Description: descriptions["creation_time"],
|
||||
Computed: true,
|
||||
},
|
||||
"update_time": schema.StringAttribute{
|
||||
Description: descriptions["update_time"],
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Create creates the resource and sets the initial Terraform state.
|
||||
func (r *folderResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
tflog.Info(ctx, "creating folder")
|
||||
var model ResourceModel
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
containerParentId := model.ContainerParentId.ValueString()
|
||||
folderName := model.Name.ValueString()
|
||||
ctx = tflog.SetField(ctx, "container_parent_id", containerParentId)
|
||||
ctx = tflog.SetField(ctx, "folder_name", folderName)
|
||||
|
||||
// Generate API request body from model
|
||||
payload, err := toCreatePayload(&model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating folder", fmt.Sprintf("Creating API payload: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
folderCreateResp, err := r.client.CreateFolder(ctx).CreateFolderPayload(*payload).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating folder", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
if folderCreateResp.ContainerId == nil || *folderCreateResp.ContainerId == "" {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating folder", "Container ID is missing")
|
||||
return
|
||||
}
|
||||
|
||||
// This sleep is currently needed due to the IAM Cache.
|
||||
time.Sleep(10 * time.Second)
|
||||
|
||||
folderGetResponse, err := r.client.GetFolderDetails(ctx, *folderCreateResp.ContainerId).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating folder", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
err = mapFolderFields(ctx, folderGetResponse, &model.Model, &resp.State)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "API response processing error", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, model)...)
|
||||
tflog.Info(ctx, "Folder created")
|
||||
}
|
||||
|
||||
// Read refreshes the Terraform state with the latest data.
|
||||
func (r *folderResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model ResourceModel
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
containerId := model.ContainerId.ValueString()
|
||||
folderName := model.Name.ValueString()
|
||||
ctx = tflog.SetField(ctx, "folder_name", folderName)
|
||||
ctx = tflog.SetField(ctx, "container_id", containerId)
|
||||
|
||||
folderResp, err := r.client.GetFolderDetails(ctx, containerId).Execute()
|
||||
if err != nil {
|
||||
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
|
||||
if ok && oapiErr.StatusCode == http.StatusForbidden {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading folder", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
err = mapFolderFields(ctx, folderResp, &model.Model, &resp.State)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading folder", fmt.Sprintf("Processing API response: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Set refreshed model
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Resource Manager folder read")
|
||||
}
|
||||
|
||||
// Update updates the resource and sets the updated Terraform state on success.
|
||||
func (r *folderResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
// Retrieve values from plan
|
||||
var model ResourceModel
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
containerId := model.ContainerId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "container_id", containerId)
|
||||
|
||||
// Generate API request body from model
|
||||
payload, err := toUpdatePayload(&model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating folder", fmt.Sprintf("Creating API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Update existing folder
|
||||
_, err = r.client.PartialUpdateFolder(ctx, containerId).PartialUpdateFolderPayload(*payload).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating folder", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Fetch updated folder
|
||||
folderResp, err := r.client.GetFolderDetails(ctx, containerId).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating folder", fmt.Sprintf("Calling API for updated data: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
err = mapFolderFields(ctx, folderResp, &model.Model, &resp.State)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating folder", fmt.Sprintf("Processing API response: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Resource Manager folder updated")
|
||||
}
|
||||
|
||||
// Delete deletes the resource and removes the Terraform state on success.
|
||||
func (r *folderResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
// Retrieve values from state
|
||||
var model ResourceModel
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
containerId := model.ContainerId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "container_id", containerId)
|
||||
|
||||
// Delete existing folder
|
||||
err := r.client.DeleteFolder(ctx, containerId).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Error deleting folder. Deletion may fail because associated projects remain hidden for up to 7 days after user deletion due to technical requirements.",
|
||||
fmt.Sprintf("Calling API: %v", err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
tflog.Info(ctx, "Resource Manager folder deleted")
|
||||
}
|
||||
|
||||
// ImportState imports a resource into the Terraform state on success.
|
||||
// The expected format of the resource import identifier is: container_id
|
||||
func (r *folderResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
idParts := strings.Split(req.ID, core.Separator)
|
||||
if len(idParts) != 1 || idParts[0] == "" {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics,
|
||||
"Error importing folder",
|
||||
fmt.Sprintf("Expected import identifier with format: [container_id] Got: %q", req.ID),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ctx = tflog.SetField(ctx, "container_id", req.ID)
|
||||
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("container_id"), req.ID)...)
|
||||
tflog.Info(ctx, "Resource Manager folder state imported")
|
||||
}
|
||||
|
||||
// mapFolderFields maps folder fields from a response into the Terraform model and optionally updates state.
|
||||
func mapFolderFields(
|
||||
ctx context.Context,
|
||||
folderGetResponse *resourcemanager.GetFolderDetailsResponse,
|
||||
model *Model,
|
||||
state *tfsdk.State,
|
||||
) error {
|
||||
if folderGetResponse == nil {
|
||||
return fmt.Errorf("folder get response is nil")
|
||||
}
|
||||
|
||||
var folderId string
|
||||
if model.FolderId.ValueString() != "" {
|
||||
folderId = model.FolderId.ValueString()
|
||||
} else if folderGetResponse.FolderId != nil {
|
||||
folderId = *folderGetResponse.FolderId
|
||||
} else {
|
||||
return fmt.Errorf("folder id not present")
|
||||
}
|
||||
|
||||
var containerId string
|
||||
if model.ContainerId.ValueString() != "" {
|
||||
containerId = model.ContainerId.ValueString()
|
||||
} else if folderGetResponse.ContainerId != nil {
|
||||
containerId = *folderGetResponse.ContainerId
|
||||
} else {
|
||||
return fmt.Errorf("container id not present")
|
||||
}
|
||||
|
||||
var err error
|
||||
var tfLabels basetypes.MapValue
|
||||
if folderGetResponse.Labels != nil && len(*folderGetResponse.Labels) > 0 {
|
||||
tfLabels, err = conversion.ToTerraformStringMap(ctx, *folderGetResponse.Labels)
|
||||
if err != nil {
|
||||
return fmt.Errorf("converting to StringValue map: %w", err)
|
||||
}
|
||||
} else {
|
||||
tfLabels = types.MapNull(types.StringType)
|
||||
}
|
||||
|
||||
var containerParentIdTF basetypes.StringValue
|
||||
if folderGetResponse.Parent != nil {
|
||||
if _, err := uuid.Parse(model.ContainerParentId.ValueString()); err == nil {
|
||||
// the provided containerParent is the UUID identifier
|
||||
containerParentIdTF = types.StringPointerValue(folderGetResponse.Parent.Id)
|
||||
} else {
|
||||
// the provided containerParent is the user-friendly container id
|
||||
containerParentIdTF = types.StringPointerValue(folderGetResponse.Parent.ContainerId)
|
||||
}
|
||||
} else {
|
||||
containerParentIdTF = types.StringNull()
|
||||
}
|
||||
|
||||
model.Id = types.StringValue(containerId)
|
||||
model.FolderId = types.StringValue(folderId)
|
||||
model.ContainerId = types.StringValue(containerId)
|
||||
model.ContainerParentId = containerParentIdTF
|
||||
model.Name = types.StringPointerValue(folderGetResponse.Name)
|
||||
model.Labels = tfLabels
|
||||
model.CreationTime = types.StringValue(folderGetResponse.CreationTime.Format(time.RFC3339))
|
||||
model.UpdateTime = types.StringValue(folderGetResponse.UpdateTime.Format(time.RFC3339))
|
||||
|
||||
if state != nil {
|
||||
diags := diag.Diagnostics{}
|
||||
diags.Append(state.SetAttribute(ctx, path.Root("id"), model.Id)...)
|
||||
diags.Append(state.SetAttribute(ctx, path.Root("folder_id"), model.FolderId)...)
|
||||
diags.Append(state.SetAttribute(ctx, path.Root("container_id"), model.ContainerId)...)
|
||||
diags.Append(state.SetAttribute(ctx, path.Root("parent_container_id"), model.ContainerParentId)...)
|
||||
diags.Append(state.SetAttribute(ctx, path.Root("name"), model.Name)...)
|
||||
diags.Append(state.SetAttribute(ctx, path.Root("labels"), model.Labels)...)
|
||||
diags.Append(state.SetAttribute(ctx, path.Root("creation_time"), model.CreationTime)...)
|
||||
diags.Append(state.SetAttribute(ctx, path.Root("update_time"), model.UpdateTime)...)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("update terraform state: %w", core.DiagsToError(diags))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func toMembersPayload(model *ResourceModel) (*[]resourcemanager.Member, error) {
|
||||
if model == nil {
|
||||
return nil, fmt.Errorf("nil model")
|
||||
}
|
||||
if model.OwnerEmail.IsNull() {
|
||||
return nil, fmt.Errorf("owner_email is null")
|
||||
}
|
||||
|
||||
return &[]resourcemanager.Member{
|
||||
{
|
||||
Subject: model.OwnerEmail.ValueStringPointer(),
|
||||
Role: sdkUtils.Ptr(projectOwnerRole),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func toCreatePayload(model *ResourceModel) (*resourcemanager.CreateFolderPayload, error) {
|
||||
if model == nil {
|
||||
return nil, fmt.Errorf("nil model")
|
||||
}
|
||||
|
||||
members, err := toMembersPayload(model)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("processing members: %w", err)
|
||||
}
|
||||
|
||||
modelLabels := model.Labels.Elements()
|
||||
labels, err := conversion.ToOptStringMap(modelLabels)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("converting to Go map: %w", err)
|
||||
}
|
||||
|
||||
return &resourcemanager.CreateFolderPayload{
|
||||
ContainerParentId: conversion.StringValueToPointer(model.ContainerParentId),
|
||||
Labels: labels,
|
||||
Members: members,
|
||||
Name: conversion.StringValueToPointer(model.Name),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func toUpdatePayload(model *ResourceModel) (*resourcemanager.PartialUpdateFolderPayload, error) {
|
||||
if model == nil {
|
||||
return nil, fmt.Errorf("nil model")
|
||||
}
|
||||
|
||||
modelLabels := model.Labels.Elements()
|
||||
labels, err := conversion.ToOptStringMap(modelLabels)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("converting to GO map: %w", err)
|
||||
}
|
||||
|
||||
return &resourcemanager.PartialUpdateFolderPayload{
|
||||
ContainerParentId: conversion.StringValueToPointer(model.ContainerParentId),
|
||||
Name: conversion.StringValueToPointer(model.Name),
|
||||
Labels: labels,
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,396 @@
|
|||
package folder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
|
||||
)
|
||||
|
||||
func TestMapFolderFields(t *testing.T) {
|
||||
testUUID := uuid.New().String()
|
||||
baseTime := time.Now()
|
||||
createTime := baseTime
|
||||
updateTime := baseTime.Add(1 * time.Hour)
|
||||
|
||||
tests := []struct {
|
||||
description string
|
||||
uuidContainerParentId bool
|
||||
projectResp *resourcemanager.GetFolderDetailsResponse
|
||||
expected Model
|
||||
expectedLabels *map[string]string
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
description: "default_ok",
|
||||
uuidContainerParentId: false,
|
||||
projectResp: &resourcemanager.GetFolderDetailsResponse{
|
||||
ContainerId: utils.Ptr("cid"),
|
||||
FolderId: utils.Ptr("fid"),
|
||||
CreationTime: &createTime,
|
||||
UpdateTime: &updateTime,
|
||||
},
|
||||
expected: Model{
|
||||
Id: types.StringValue("cid"),
|
||||
ContainerId: types.StringValue("cid"),
|
||||
FolderId: types.StringValue("fid"),
|
||||
ContainerParentId: types.StringNull(),
|
||||
Name: types.StringNull(),
|
||||
CreationTime: types.StringValue(createTime.Format(time.RFC3339)),
|
||||
UpdateTime: types.StringValue(updateTime.Format(time.RFC3339)),
|
||||
},
|
||||
expectedLabels: nil,
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
description: "container_parent_id_ok",
|
||||
uuidContainerParentId: false,
|
||||
projectResp: &resourcemanager.GetFolderDetailsResponse{
|
||||
ContainerId: utils.Ptr("cid"),
|
||||
FolderId: utils.Ptr("fid"),
|
||||
Labels: &map[string]string{
|
||||
"label1": "ref1",
|
||||
"label2": "ref2",
|
||||
},
|
||||
Parent: &resourcemanager.Parent{
|
||||
ContainerId: utils.Ptr("parent_cid"),
|
||||
Id: utils.Ptr(testUUID),
|
||||
},
|
||||
Name: utils.Ptr("name"),
|
||||
CreationTime: &createTime,
|
||||
UpdateTime: &updateTime,
|
||||
},
|
||||
expected: Model{
|
||||
Id: types.StringValue("cid"),
|
||||
ContainerId: types.StringValue("cid"),
|
||||
FolderId: types.StringValue("fid"),
|
||||
ContainerParentId: types.StringValue("parent_cid"),
|
||||
Name: types.StringValue("name"),
|
||||
CreationTime: types.StringValue(createTime.Format(time.RFC3339)),
|
||||
UpdateTime: types.StringValue(updateTime.Format(time.RFC3339)),
|
||||
},
|
||||
expectedLabels: &map[string]string{
|
||||
"label1": "ref1",
|
||||
"label2": "ref2",
|
||||
},
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
description: "uuid_parent_id_ok",
|
||||
uuidContainerParentId: true,
|
||||
projectResp: &resourcemanager.GetFolderDetailsResponse{
|
||||
ContainerId: utils.Ptr("cid"),
|
||||
FolderId: utils.Ptr("fid"),
|
||||
Labels: &map[string]string{
|
||||
"label1": "ref1",
|
||||
"label2": "ref2",
|
||||
},
|
||||
Parent: &resourcemanager.Parent{
|
||||
ContainerId: utils.Ptr("parent_cid"),
|
||||
Id: utils.Ptr(testUUID), // simulate UUID logic
|
||||
},
|
||||
Name: utils.Ptr("name"),
|
||||
CreationTime: &createTime,
|
||||
UpdateTime: &updateTime,
|
||||
},
|
||||
expected: Model{
|
||||
Id: types.StringValue("cid"),
|
||||
ContainerId: types.StringValue("cid"),
|
||||
FolderId: types.StringValue("fid"),
|
||||
ContainerParentId: types.StringValue(testUUID),
|
||||
Name: types.StringValue("name"),
|
||||
CreationTime: types.StringValue(createTime.Format(time.RFC3339)),
|
||||
UpdateTime: types.StringValue(updateTime.Format(time.RFC3339)),
|
||||
},
|
||||
expectedLabels: &map[string]string{
|
||||
"label1": "ref1",
|
||||
"label2": "ref2",
|
||||
},
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
description: "response_nil_fail",
|
||||
uuidContainerParentId: false,
|
||||
projectResp: nil,
|
||||
expected: Model{},
|
||||
expectedLabels: nil,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
description: "no_resource_id",
|
||||
uuidContainerParentId: false,
|
||||
projectResp: &resourcemanager.GetFolderDetailsResponse{},
|
||||
expected: Model{},
|
||||
expectedLabels: nil,
|
||||
isValid: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
if tt.expectedLabels == nil {
|
||||
tt.expected.Labels = types.MapNull(types.StringType)
|
||||
} else {
|
||||
convertedLabels, err := conversion.ToTerraformStringMap(context.Background(), *tt.expectedLabels)
|
||||
if err != nil {
|
||||
t.Fatalf("Error converting to terraform string map: %v", err)
|
||||
}
|
||||
tt.expected.Labels = convertedLabels
|
||||
}
|
||||
var containerParentId = types.StringNull()
|
||||
if tt.uuidContainerParentId {
|
||||
containerParentId = types.StringValue(testUUID)
|
||||
} else if tt.projectResp != nil && tt.projectResp.Parent != nil && tt.projectResp.Parent.ContainerId != nil {
|
||||
containerParentId = types.StringValue(*tt.projectResp.Parent.ContainerId)
|
||||
}
|
||||
|
||||
model := &Model{
|
||||
ContainerId: tt.expected.ContainerId,
|
||||
ContainerParentId: containerParentId,
|
||||
}
|
||||
|
||||
err := mapFolderFields(context.Background(), tt.projectResp, model, nil)
|
||||
|
||||
if !tt.isValid && err == nil {
|
||||
t.Fatalf("Should have failed")
|
||||
}
|
||||
if tt.isValid && err != nil {
|
||||
t.Fatalf("Should not have failed: %v", err)
|
||||
}
|
||||
if tt.isValid {
|
||||
diff := cmp.Diff(model, &tt.expected)
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToCreatePayload(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
input *ResourceModel
|
||||
inputLabels *map[string]string
|
||||
expected *resourcemanager.CreateFolderPayload
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"mapping_with_conversions",
|
||||
&ResourceModel{
|
||||
Model: Model{
|
||||
ContainerParentId: types.StringValue("pid"),
|
||||
Name: types.StringValue("name"),
|
||||
},
|
||||
OwnerEmail: types.StringValue("john.doe@stackit.cloud"),
|
||||
},
|
||||
&map[string]string{
|
||||
"label1": "1",
|
||||
"label2": "2",
|
||||
},
|
||||
&resourcemanager.CreateFolderPayload{
|
||||
ContainerParentId: utils.Ptr("pid"),
|
||||
Labels: &map[string]string{
|
||||
"label1": "1",
|
||||
"label2": "2",
|
||||
},
|
||||
Members: &[]resourcemanager.Member{
|
||||
{
|
||||
Subject: utils.Ptr("john.doe@stackit.cloud"),
|
||||
Role: utils.Ptr("owner"),
|
||||
},
|
||||
},
|
||||
Name: utils.Ptr("name"),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"no owner_email fails",
|
||||
&ResourceModel{
|
||||
Model: Model{
|
||||
ContainerParentId: types.StringValue("pid"),
|
||||
Name: types.StringValue("name"),
|
||||
},
|
||||
},
|
||||
&map[string]string{},
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"nil_model",
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
if tt.input != nil {
|
||||
if tt.inputLabels == nil {
|
||||
tt.input.Labels = types.MapNull(types.StringType)
|
||||
} else {
|
||||
convertedLabels, err := conversion.ToTerraformStringMap(context.Background(), *tt.inputLabels)
|
||||
if err != nil {
|
||||
t.Fatalf("Error converting to terraform string map: %v", err)
|
||||
}
|
||||
tt.input.Labels = convertedLabels
|
||||
}
|
||||
}
|
||||
output, err := toCreatePayload(tt.input)
|
||||
if !tt.isValid && err == nil {
|
||||
t.Fatalf("Should have failed")
|
||||
}
|
||||
if tt.isValid && err != nil {
|
||||
t.Fatalf("Should not have failed: %v", err)
|
||||
}
|
||||
if tt.isValid {
|
||||
diff := cmp.Diff(output, tt.expected)
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToUpdatePayload(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
input *ResourceModel
|
||||
inputLabels *map[string]string
|
||||
expected *resourcemanager.PartialUpdateFolderPayload
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"default_ok",
|
||||
&ResourceModel{},
|
||||
nil,
|
||||
&resourcemanager.PartialUpdateFolderPayload{
|
||||
ContainerParentId: nil,
|
||||
Labels: nil,
|
||||
Name: nil,
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"mapping_with_conversions_ok",
|
||||
&ResourceModel{
|
||||
Model: Model{
|
||||
ContainerParentId: types.StringValue("pid"),
|
||||
Name: types.StringValue("name"),
|
||||
},
|
||||
OwnerEmail: types.StringValue("owner_email"),
|
||||
},
|
||||
&map[string]string{
|
||||
"label1": "1",
|
||||
"label2": "2",
|
||||
},
|
||||
&resourcemanager.PartialUpdateFolderPayload{
|
||||
ContainerParentId: utils.Ptr("pid"),
|
||||
Labels: &map[string]string{
|
||||
"label1": "1",
|
||||
"label2": "2",
|
||||
},
|
||||
Name: utils.Ptr("name"),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"nil_model",
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
if tt.input != nil {
|
||||
if tt.inputLabels == nil {
|
||||
tt.input.Labels = types.MapNull(types.StringType)
|
||||
} else {
|
||||
convertedLabels, err := conversion.ToTerraformStringMap(context.Background(), *tt.inputLabels)
|
||||
if err != nil {
|
||||
t.Fatalf("Error converting to terraform string map: %v", err)
|
||||
}
|
||||
tt.input.Labels = convertedLabels
|
||||
}
|
||||
}
|
||||
output, err := toUpdatePayload(tt.input)
|
||||
if !tt.isValid && err == nil {
|
||||
t.Fatalf("Should have failed")
|
||||
}
|
||||
if tt.isValid && err != nil {
|
||||
t.Fatalf("Should not have failed: %v", err)
|
||||
}
|
||||
if tt.isValid {
|
||||
diff := cmp.Diff(output, tt.expected)
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToMembersPayload(t *testing.T) {
|
||||
type args struct {
|
||||
model *ResourceModel
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *[]resourcemanager.Member
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "missing model",
|
||||
args: args{},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty model",
|
||||
args: args{
|
||||
model: &ResourceModel{},
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "ok",
|
||||
args: args{
|
||||
model: &ResourceModel{
|
||||
OwnerEmail: types.StringValue("john.doe@stackit.cloud"),
|
||||
},
|
||||
},
|
||||
want: &[]resourcemanager.Member{
|
||||
{
|
||||
Subject: utils.Ptr("john.doe@stackit.cloud"),
|
||||
Role: utils.Ptr("owner"),
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := toMembersPayload(tt.args.model)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("toMembersPayload() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("toMembersPayload() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -25,7 +25,8 @@ import (
|
|||
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var (
|
||||
_ datasource.DataSource = &projectDataSource{}
|
||||
_ datasource.DataSource = &projectDataSource{}
|
||||
_ datasource.DataSourceWithConfigure = &projectDataSource{}
|
||||
)
|
||||
|
||||
// NewProjectDataSource is a helper function to simplify the provider implementation.
|
||||
|
|
@ -67,6 +68,8 @@ func (d *projectDataSource) Schema(_ context.Context, _ datasource.SchemaRequest
|
|||
"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}`,
|
||||
"creation_time": "Date-time at which the project was created.",
|
||||
"update_time": "Date-time at which the project was last modified.",
|
||||
}
|
||||
|
||||
resp.Schema = schema.Schema{
|
||||
|
|
@ -122,6 +125,14 @@ func (d *projectDataSource) Schema(_ context.Context, _ datasource.SchemaRequest
|
|||
),
|
||||
},
|
||||
},
|
||||
"creation_time": schema.StringAttribute{
|
||||
Description: descriptions["creation_time"],
|
||||
Computed: true,
|
||||
},
|
||||
"update_time": schema.StringAttribute{
|
||||
Description: descriptions["update_time"],
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
resourcemanagerUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/resourcemanager/utils"
|
||||
|
||||
|
|
@ -51,6 +52,8 @@ type Model struct {
|
|||
ContainerParentId types.String `tfsdk:"parent_container_id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Labels types.Map `tfsdk:"labels"`
|
||||
CreationTime types.String `tfsdk:"creation_time"`
|
||||
UpdateTime types.String `tfsdk:"update_time"`
|
||||
}
|
||||
|
||||
type ResourceModel struct {
|
||||
|
|
@ -92,7 +95,7 @@ func (r *projectResource) Configure(ctx context.Context, req resource.ConfigureR
|
|||
func (r *projectResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
descriptions := map[string]string{
|
||||
"main": fmt.Sprintf("%s\n\n%s",
|
||||
"Resource Manager project resource schema. To use this resource, it is required that you set the service account email in the provider configuration.",
|
||||
"Resource Manager project resource schema.",
|
||||
"-> In case you're getting started with an empty STACKIT organization and want to use this resource to create projects in it, check out [this guide](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/stackit_org_service_account) for how to create a service account which you can use for authentication in the STACKIT Terraform provider.",
|
||||
),
|
||||
"id": "Terraform's internal resource ID. It is structured as \"`container_id`\".",
|
||||
|
|
@ -102,6 +105,8 @@ func (r *projectResource) Schema(_ context.Context, _ resource.SchemaRequest, re
|
|||
"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}. \nTo create a project within a STACKIT Network Area, setting the label `networkArea=<networkAreaID>` is required. This can not be changed after project creation.",
|
||||
"owner_email": "Email address of the owner of the project. This value is only considered during creation. Changing it afterwards will have no effect.",
|
||||
"creation_time": "Date-time at which the project was created.",
|
||||
"update_time": "Date-time at which the project was last modified.",
|
||||
}
|
||||
|
||||
resp.Schema = schema.Schema{
|
||||
|
|
@ -170,6 +175,14 @@ func (r *projectResource) Schema(_ context.Context, _ resource.SchemaRequest, re
|
|||
Description: descriptions["owner_email"],
|
||||
Required: true,
|
||||
},
|
||||
"creation_time": schema.StringAttribute{
|
||||
Description: descriptions["creation_time"],
|
||||
Computed: true,
|
||||
},
|
||||
"update_time": schema.StringAttribute{
|
||||
Description: descriptions["update_time"],
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -409,6 +422,8 @@ func mapProjectFields(ctx context.Context, projectResp *resourcemanager.GetProje
|
|||
model.ContainerId = types.StringValue(containerId)
|
||||
model.Name = types.StringPointerValue(projectResp.Name)
|
||||
model.Labels = labels
|
||||
model.CreationTime = types.StringValue(projectResp.CreationTime.Format(time.RFC3339))
|
||||
model.UpdateTime = types.StringValue(projectResp.UpdateTime.Format(time.RFC3339))
|
||||
|
||||
if state != nil {
|
||||
diags := diag.Diagnostics{}
|
||||
|
|
@ -418,6 +433,8 @@ func mapProjectFields(ctx context.Context, projectResp *resourcemanager.GetProje
|
|||
diags.Append(state.SetAttribute(ctx, path.Root("container_id"), model.ContainerId)...)
|
||||
diags.Append(state.SetAttribute(ctx, path.Root("name"), model.Name)...)
|
||||
diags.Append(state.SetAttribute(ctx, path.Root("labels"), model.Labels)...)
|
||||
diags.Append(state.SetAttribute(ctx, path.Root("creation_time"), model.CreationTime)...)
|
||||
diags.Append(state.SetAttribute(ctx, path.Root("update_time"), model.UpdateTime)...)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("update terraform state: %w", core.DiagsToError(diags))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/uuid"
|
||||
|
|
@ -15,6 +16,10 @@ import (
|
|||
|
||||
func TestMapProjectFields(t *testing.T) {
|
||||
testUUID := uuid.New().String()
|
||||
baseTime := time.Now()
|
||||
createTime := baseTime
|
||||
updateTime := baseTime.Add(1 * time.Hour)
|
||||
|
||||
tests := []struct {
|
||||
description string
|
||||
uuidContainerParentId bool
|
||||
|
|
@ -24,26 +29,30 @@ func TestMapProjectFields(t *testing.T) {
|
|||
isValid bool
|
||||
}{
|
||||
{
|
||||
"default_ok",
|
||||
false,
|
||||
&resourcemanager.GetProjectResponse{
|
||||
ContainerId: utils.Ptr("cid"),
|
||||
ProjectId: utils.Ptr("pid"),
|
||||
description: "default_ok",
|
||||
uuidContainerParentId: false,
|
||||
projectResp: &resourcemanager.GetProjectResponse{
|
||||
ContainerId: utils.Ptr("cid"),
|
||||
ProjectId: utils.Ptr("pid"),
|
||||
CreationTime: &createTime,
|
||||
UpdateTime: &updateTime,
|
||||
},
|
||||
Model{
|
||||
expected: Model{
|
||||
Id: types.StringValue("cid"),
|
||||
ContainerId: types.StringValue("cid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
ContainerParentId: types.StringNull(),
|
||||
Name: types.StringNull(),
|
||||
CreationTime: types.StringValue(createTime.Format(time.RFC3339)),
|
||||
UpdateTime: types.StringValue(updateTime.Format(time.RFC3339)),
|
||||
},
|
||||
nil,
|
||||
true,
|
||||
expectedLabels: nil,
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
"container_parent_id_ok",
|
||||
false,
|
||||
&resourcemanager.GetProjectResponse{
|
||||
description: "container_parent_id_ok",
|
||||
uuidContainerParentId: false,
|
||||
projectResp: &resourcemanager.GetProjectResponse{
|
||||
ContainerId: utils.Ptr("cid"),
|
||||
ProjectId: utils.Ptr("pid"),
|
||||
Labels: &map[string]string{
|
||||
|
|
@ -54,25 +63,29 @@ func TestMapProjectFields(t *testing.T) {
|
|||
ContainerId: utils.Ptr("parent_cid"),
|
||||
Id: utils.Ptr("parent_pid"),
|
||||
},
|
||||
Name: utils.Ptr("name"),
|
||||
Name: utils.Ptr("name"),
|
||||
CreationTime: &createTime,
|
||||
UpdateTime: &updateTime,
|
||||
},
|
||||
Model{
|
||||
expected: Model{
|
||||
Id: types.StringValue("cid"),
|
||||
ContainerId: types.StringValue("cid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
ContainerParentId: types.StringValue("parent_cid"),
|
||||
Name: types.StringValue("name"),
|
||||
CreationTime: types.StringValue(createTime.Format(time.RFC3339)),
|
||||
UpdateTime: types.StringValue(updateTime.Format(time.RFC3339)),
|
||||
},
|
||||
&map[string]string{
|
||||
expectedLabels: &map[string]string{
|
||||
"label1": "ref1",
|
||||
"label2": "ref2",
|
||||
},
|
||||
true,
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
"uuid_parent_id_ok",
|
||||
true,
|
||||
&resourcemanager.GetProjectResponse{
|
||||
description: "uuid_parent_id_ok",
|
||||
uuidContainerParentId: true,
|
||||
projectResp: &resourcemanager.GetProjectResponse{
|
||||
ContainerId: utils.Ptr("cid"),
|
||||
ProjectId: utils.Ptr("pid"),
|
||||
Labels: &map[string]string{
|
||||
|
|
@ -81,40 +94,45 @@ func TestMapProjectFields(t *testing.T) {
|
|||
},
|
||||
Parent: &resourcemanager.Parent{
|
||||
ContainerId: utils.Ptr("parent_cid"),
|
||||
Id: utils.Ptr(testUUID),
|
||||
Id: utils.Ptr(testUUID), // simulate UUID logic
|
||||
},
|
||||
Name: utils.Ptr("name"),
|
||||
Name: utils.Ptr("name"),
|
||||
CreationTime: &createTime,
|
||||
UpdateTime: &updateTime,
|
||||
},
|
||||
Model{
|
||||
expected: Model{
|
||||
Id: types.StringValue("cid"),
|
||||
ContainerId: types.StringValue("cid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
ContainerParentId: types.StringValue(testUUID),
|
||||
Name: types.StringValue("name"),
|
||||
CreationTime: types.StringValue(createTime.Format(time.RFC3339)),
|
||||
UpdateTime: types.StringValue(updateTime.Format(time.RFC3339)),
|
||||
},
|
||||
&map[string]string{
|
||||
expectedLabels: &map[string]string{
|
||||
"label1": "ref1",
|
||||
"label2": "ref2",
|
||||
},
|
||||
true,
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
"response_nil_fail",
|
||||
false,
|
||||
nil,
|
||||
Model{},
|
||||
nil,
|
||||
false,
|
||||
description: "response_nil_fail",
|
||||
uuidContainerParentId: false,
|
||||
projectResp: nil,
|
||||
expected: Model{},
|
||||
expectedLabels: nil,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
"no_resource_id",
|
||||
false,
|
||||
&resourcemanager.GetProjectResponse{},
|
||||
Model{},
|
||||
nil,
|
||||
false,
|
||||
description: "no_resource_id",
|
||||
uuidContainerParentId: false,
|
||||
projectResp: &resourcemanager.GetProjectResponse{},
|
||||
expected: Model{},
|
||||
expectedLabels: nil,
|
||||
isValid: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
if tt.expectedLabels == nil {
|
||||
|
|
@ -129,13 +147,17 @@ func TestMapProjectFields(t *testing.T) {
|
|||
var containerParentId = types.StringNull()
|
||||
if tt.uuidContainerParentId {
|
||||
containerParentId = types.StringValue(testUUID)
|
||||
} else if tt.projectResp != nil && tt.projectResp.Parent != nil && tt.projectResp.Parent.ContainerId != nil {
|
||||
containerParentId = types.StringValue(*tt.projectResp.Parent.ContainerId)
|
||||
}
|
||||
|
||||
model := &Model{
|
||||
ContainerId: tt.expected.ContainerId,
|
||||
ContainerParentId: containerParentId,
|
||||
}
|
||||
|
||||
err := mapProjectFields(context.Background(), tt.projectResp, model, nil)
|
||||
|
||||
if !tt.isValid && err == nil {
|
||||
t.Fatalf("Should have failed")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,176 +2,450 @@ package resourcemanager_test
|
|||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/config"
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-testing/terraform"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||
sdkConfig "github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/resourcemanager"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/resourcemanager/wait"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil"
|
||||
)
|
||||
|
||||
// Project resource data
|
||||
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",
|
||||
//go:embed testdata/resource-project.tf
|
||||
var resourceProject string
|
||||
|
||||
//go:embed testdata/resource-folder.tf
|
||||
var resourceFolder string
|
||||
|
||||
var defaultLabels = config.ObjectVariable(
|
||||
map[string]config.Variable{
|
||||
"env": config.StringVariable("prod"),
|
||||
},
|
||||
)
|
||||
|
||||
var projectNameParentContainerId = fmt.Sprintf("tfe2e-project-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))
|
||||
var projectNameParentContainerIdUpdated = fmt.Sprintf("%s-updated", projectNameParentContainerId)
|
||||
|
||||
var projectNameParentUUID = fmt.Sprintf("tfe2e-project-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))
|
||||
var projectNameParentUUIDUpdated = fmt.Sprintf("%s-updated", projectNameParentUUID)
|
||||
|
||||
var folderNameParentContainerId = fmt.Sprintf("tfe2e-folder-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))
|
||||
var folderNameParentContainerIdUpdated = fmt.Sprintf("%s-updated", folderNameParentContainerId)
|
||||
|
||||
var folderNameParentUUID = fmt.Sprintf("tfe2e-folder-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))
|
||||
var folderNameParentUUIDUpdated = fmt.Sprintf("%s-updated", folderNameParentUUID)
|
||||
|
||||
var testConfigResourceProjectParentContainerId = config.Variables{
|
||||
"name": config.StringVariable(projectNameParentContainerId),
|
||||
"owner_email": config.StringVariable(testutil.TestProjectServiceAccountEmail),
|
||||
"parent_container_id": config.StringVariable(testutil.TestProjectParentContainerID),
|
||||
"labels": config.ObjectVariable(
|
||||
map[string]config.Variable{
|
||||
"env": config.StringVariable("prod"),
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
func resourceConfig(name string, label *string) string {
|
||||
labelConfig := ""
|
||||
if label != nil {
|
||||
labelConfig = fmt.Sprintf("new_label = %q", *label)
|
||||
}
|
||||
return fmt.Sprintf(`
|
||||
%[1]s
|
||||
|
||||
resource "stackit_resourcemanager_project" "parent_by_container" {
|
||||
parent_container_id = "%[2]s"
|
||||
name = "%[3]s"
|
||||
labels = {
|
||||
"billing_reference" = "%[4]s"
|
||||
%[5]s
|
||||
}
|
||||
owner_email = "%[7]s"
|
||||
}
|
||||
|
||||
resource "stackit_resourcemanager_project" "parent_by_uuid" {
|
||||
parent_container_id = "%[6]s"
|
||||
name = "%[3]s-uuid"
|
||||
owner_email = "%[7]s"
|
||||
}
|
||||
`,
|
||||
testutil.ResourceManagerProviderConfig(),
|
||||
projectResource["parent_container_id"],
|
||||
name,
|
||||
projectResource["billing_reference"],
|
||||
labelConfig,
|
||||
projectResource["parent_uuid"],
|
||||
testutil.TestProjectServiceAccountEmail,
|
||||
)
|
||||
var testConfigResourceProjectParentUUID = config.Variables{
|
||||
"name": config.StringVariable(projectNameParentUUID),
|
||||
"owner_email": config.StringVariable(testutil.TestProjectServiceAccountEmail),
|
||||
"parent_container_id": config.StringVariable(testutil.TestProjectParentUUID),
|
||||
"labels": defaultLabels,
|
||||
}
|
||||
|
||||
func TestAccResourceManagerResource(t *testing.T) {
|
||||
var testConfigResourceFolderParentContainerId = config.Variables{
|
||||
"name": config.StringVariable(folderNameParentContainerId),
|
||||
"owner_email": config.StringVariable(testutil.TestProjectServiceAccountEmail),
|
||||
"parent_container_id": config.StringVariable(testutil.TestProjectParentContainerID),
|
||||
"labels": defaultLabels,
|
||||
}
|
||||
|
||||
var testConfigResourceFolderParentUUID = config.Variables{
|
||||
"name": config.StringVariable(folderNameParentUUID),
|
||||
"owner_email": config.StringVariable(testutil.TestProjectServiceAccountEmail),
|
||||
"parent_container_id": config.StringVariable(testutil.TestProjectParentUUID),
|
||||
"labels": defaultLabels,
|
||||
}
|
||||
|
||||
func testConfigProjectNameParentContainerIdUpdated() config.Variables {
|
||||
tempConfig := make(config.Variables, len(testConfigResourceProjectParentContainerId))
|
||||
maps.Copy(tempConfig, testConfigResourceProjectParentContainerId)
|
||||
tempConfig["name"] = config.StringVariable(projectNameParentContainerIdUpdated)
|
||||
return tempConfig
|
||||
}
|
||||
|
||||
func testConfigProjectNameParentUUIDUpdated() config.Variables {
|
||||
tempConfig := make(config.Variables, len(testConfigResourceProjectParentUUID))
|
||||
maps.Copy(tempConfig, testConfigResourceProjectParentUUID)
|
||||
tempConfig["name"] = config.StringVariable(projectNameParentUUIDUpdated)
|
||||
return tempConfig
|
||||
}
|
||||
|
||||
func testConfigFolderNameParentContainerIdUpdated() config.Variables {
|
||||
tempConfig := make(config.Variables, len(testConfigResourceFolderParentContainerId))
|
||||
maps.Copy(tempConfig, testConfigResourceFolderParentContainerId)
|
||||
tempConfig["name"] = config.StringVariable(folderNameParentContainerIdUpdated)
|
||||
return tempConfig
|
||||
}
|
||||
|
||||
func testConfigFolderNameParentUUIDUpdated() config.Variables {
|
||||
tempConfig := make(config.Variables, len(testConfigResourceFolderParentUUID))
|
||||
maps.Copy(tempConfig, testConfigResourceFolderParentUUID)
|
||||
tempConfig["name"] = config.StringVariable(folderNameParentUUIDUpdated)
|
||||
return tempConfig
|
||||
}
|
||||
|
||||
func TestAccResourceManagerProjectContainerId(t *testing.T) {
|
||||
t.Logf("TestAccResourceManagerProjectContainerId name: %s", testutil.ConvertConfigVariable(testConfigResourceProjectParentContainerId["name"]))
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||
CheckDestroy: testAccCheckResourceManagerDestroy,
|
||||
CheckDestroy: testAccCheckDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
// Creation
|
||||
// Create
|
||||
{
|
||||
Config: resourceConfig(projectResource["name"], nil),
|
||||
ConfigVariables: testConfigResourceProjectParentContainerId,
|
||||
Config: testutil.ResourceManagerProviderConfig() + resourceProject,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// 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"]),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.example", "name", testutil.ConvertConfigVariable(testConfigResourceProjectParentContainerId["name"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.example", "parent_container_id", testutil.ConvertConfigVariable(testConfigResourceProjectParentContainerId["parent_container_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.example", "owner_email", testutil.ConvertConfigVariable(testConfigResourceProjectParentContainerId["owner_email"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.example", "labels.%", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.example", "labels.env", "prod"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "container_id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "project_id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "owner_email"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "creation_time"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "update_time"),
|
||||
),
|
||||
},
|
||||
// Data source
|
||||
// Data Source
|
||||
{
|
||||
ConfigVariables: testConfigResourceProjectParentContainerId,
|
||||
Config: fmt.Sprintf(`
|
||||
%s
|
||||
%s
|
||||
%s
|
||||
|
||||
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),
|
||||
),
|
||||
data "stackit_resourcemanager_project" "example" {
|
||||
project_id = stackit_resourcemanager_project.example.project_id
|
||||
}
|
||||
`, testutil.ResourceManagerProviderConfig(), resourceProject),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// 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"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.example", "name", testutil.ConvertConfigVariable(testConfigResourceProjectParentContainerId["name"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.example", "parent_container_id", testutil.ConvertConfigVariable(testConfigResourceProjectParentContainerId["parent_container_id"])),
|
||||
resource.TestCheckResourceAttrPair("data.stackit_resourcemanager_project.example", "container_id", "stackit_resourcemanager_project.example", "container_id"),
|
||||
resource.TestCheckResourceAttrPair("data.stackit_resourcemanager_project.example", "project_id", "stackit_resourcemanager_project.example", "project_id"),
|
||||
resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.example", "labels.%", "1"),
|
||||
resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.example", "labels.env", "prod"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.example", "id"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.example", "creation_time"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.example", "update_time"),
|
||||
),
|
||||
},
|
||||
// Import
|
||||
{
|
||||
ResourceName: "stackit_resourcemanager_project.parent_by_container",
|
||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
r, ok := s.RootModule().Resources["stackit_resourcemanager_project.parent_by_container"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find resource stackit_resourcemanager_project.parent_by_container")
|
||||
}
|
||||
containerId, ok := r.Primary.Attributes["container_id"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find attribute container_id")
|
||||
}
|
||||
|
||||
return containerId, nil
|
||||
},
|
||||
ConfigVariables: testConfigResourceProjectParentContainerId,
|
||||
ResourceName: "stackit_resourcemanager_project.example",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
// The owner_email attributes don't exist in the
|
||||
// API, therefore there is no value for it during import.
|
||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
return getImportIdFromID(s, "stackit_resourcemanager_project.example", "container_id")
|
||||
},
|
||||
ImportStateVerifyIgnore: []string{"owner_email"},
|
||||
},
|
||||
// Update
|
||||
{
|
||||
Config: resourceConfig(fmt.Sprintf("%s-new", projectResource["name"]), utils.Ptr("a-label")),
|
||||
ConfigVariables: testConfigProjectNameParentContainerIdUpdated(),
|
||||
Config: testutil.ResourceManagerProviderConfig() + resourceProject,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Project data
|
||||
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"]),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.parent_by_container", "owner_email", testutil.TestProjectServiceAccountEmail),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.example", "name", testutil.ConvertConfigVariable(testConfigProjectNameParentContainerIdUpdated()["name"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.example", "parent_container_id", testutil.ConvertConfigVariable(testConfigResourceProjectParentContainerId["parent_container_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.example", "owner_email", testutil.ConvertConfigVariable(testConfigResourceProjectParentContainerId["owner_email"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.example", "labels.%", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.example", "labels.env", "prod"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "container_id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "project_id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "owner_email"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "creation_time"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "update_time"),
|
||||
),
|
||||
},
|
||||
// Deletion is done by the framework implicitly
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckResourceManagerDestroy(s *terraform.State) error {
|
||||
func TestAccResourceManagerProjectParentUUID(t *testing.T) {
|
||||
t.Logf("TestAccResourceManagerProjectParentUUID name: %s", testutil.ConvertConfigVariable(testConfigResourceProjectParentUUID["name"]))
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||
CheckDestroy: testAccCheckDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
// Create
|
||||
{
|
||||
ConfigVariables: testConfigResourceProjectParentUUID,
|
||||
Config: testutil.ResourceManagerProviderConfig() + resourceProject,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.example", "name", testutil.ConvertConfigVariable(testConfigResourceProjectParentUUID["name"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.example", "parent_container_id", testutil.ConvertConfigVariable(testConfigResourceProjectParentUUID["parent_container_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.example", "owner_email", testutil.ConvertConfigVariable(testConfigResourceProjectParentUUID["owner_email"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.example", "labels.%", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.example", "labels.env", "prod"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "container_id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "project_id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "owner_email"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "creation_time"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "update_time"),
|
||||
),
|
||||
},
|
||||
// Data Source
|
||||
{
|
||||
ConfigVariables: testConfigResourceProjectParentUUID,
|
||||
Config: fmt.Sprintf(`
|
||||
%s
|
||||
%s
|
||||
|
||||
data "stackit_resourcemanager_project" "example" {
|
||||
project_id = stackit_resourcemanager_project.example.project_id
|
||||
}
|
||||
`, testutil.ResourceManagerProviderConfig(), resourceProject),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.example", "name", testutil.ConvertConfigVariable(testConfigResourceProjectParentUUID["name"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.example", "labels.%", "1"),
|
||||
resource.TestCheckResourceAttr("data.stackit_resourcemanager_project.example", "labels.env", "prod"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.example", "parent_container_id"),
|
||||
resource.TestCheckResourceAttrPair("data.stackit_resourcemanager_project.example", "container_id", "stackit_resourcemanager_project.example", "container_id"),
|
||||
resource.TestCheckResourceAttrPair("data.stackit_resourcemanager_project.example", "project_id", "stackit_resourcemanager_project.example", "project_id"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.example", "id"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.example", "creation_time"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_project.example", "update_time"),
|
||||
),
|
||||
},
|
||||
// Import
|
||||
{
|
||||
ConfigVariables: testConfigResourceProjectParentUUID,
|
||||
ResourceName: "stackit_resourcemanager_project.example",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
return getImportIdFromID(s, "stackit_resourcemanager_project.example", "container_id")
|
||||
},
|
||||
ImportStateVerifyIgnore: []string{"owner_email", "parent_container_id"},
|
||||
},
|
||||
// Update
|
||||
{
|
||||
ConfigVariables: testConfigProjectNameParentUUIDUpdated(),
|
||||
Config: testutil.ResourceManagerProviderConfig() + resourceProject,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.example", "name", testutil.ConvertConfigVariable(testConfigProjectNameParentUUIDUpdated()["name"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.example", "parent_container_id", testutil.ConvertConfigVariable(testConfigResourceProjectParentUUID["parent_container_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.example", "owner_email", testutil.ConvertConfigVariable(testConfigResourceProjectParentUUID["owner_email"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.example", "labels.%", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_project.example", "labels.env", "prod"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "container_id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "project_id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "owner_email"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "creation_time"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_project.example", "update_time"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccResourceManagerFolderContainerId(t *testing.T) {
|
||||
t.Logf("TestAccResourceManagerFolderContainerId name: %s", testutil.ConvertConfigVariable(testConfigResourceFolderParentContainerId["name"]))
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||
CheckDestroy: testAccCheckDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
// Create
|
||||
{
|
||||
ConfigVariables: testConfigResourceFolderParentContainerId,
|
||||
Config: testutil.ResourceManagerProviderConfigBetaEnabled() + resourceFolder,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_folder.example", "name", testutil.ConvertConfigVariable(testConfigResourceFolderParentContainerId["name"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_folder.example", "parent_container_id", testutil.ConvertConfigVariable(testConfigResourceFolderParentContainerId["parent_container_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_folder.example", "owner_email", testutil.ConvertConfigVariable(testConfigResourceFolderParentContainerId["owner_email"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_folder.example", "labels.%", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_folder.example", "labels.env", "prod"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "container_id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "folder_id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "creation_time"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "update_time"),
|
||||
),
|
||||
},
|
||||
// Data Source
|
||||
{
|
||||
ConfigVariables: testConfigResourceFolderParentContainerId,
|
||||
Config: fmt.Sprintf(`
|
||||
%s
|
||||
%s
|
||||
|
||||
data "stackit_resourcemanager_folder" "example" {
|
||||
container_id = stackit_resourcemanager_folder.example.container_id
|
||||
}
|
||||
`, testutil.ResourceManagerProviderConfigBetaEnabled(), resourceFolder),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("data.stackit_resourcemanager_folder.example", "name", testutil.ConvertConfigVariable(testConfigResourceFolderParentContainerId["name"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_resourcemanager_folder.example", "labels.%", "1"),
|
||||
resource.TestCheckResourceAttr("data.stackit_resourcemanager_folder.example", "labels.env", "prod"),
|
||||
resource.TestCheckResourceAttr("data.stackit_resourcemanager_folder.example", "parent_container_id", testutil.ConvertConfigVariable(testConfigResourceFolderParentContainerId["parent_container_id"])),
|
||||
resource.TestCheckResourceAttrPair("data.stackit_resourcemanager_folder.example", "container_id", "stackit_resourcemanager_folder.example", "container_id"),
|
||||
resource.TestCheckResourceAttrPair("data.stackit_resourcemanager_folder.example", "project_id", "stackit_resourcemanager_folder.example", "project_id"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_folder.example", "id"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_folder.example", "creation_time"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_folder.example", "update_time"),
|
||||
),
|
||||
},
|
||||
// Import
|
||||
{
|
||||
ConfigVariables: testConfigResourceFolderParentContainerId,
|
||||
ResourceName: "stackit_resourcemanager_folder.example",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
return getImportIdFromID(s, "stackit_resourcemanager_folder.example", "container_id")
|
||||
},
|
||||
ImportStateVerifyIgnore: []string{"owner_email"},
|
||||
},
|
||||
// Update
|
||||
{
|
||||
ConfigVariables: testConfigFolderNameParentContainerIdUpdated(),
|
||||
Config: testutil.ResourceManagerProviderConfigBetaEnabled() + resourceFolder,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_folder.example", "name", testutil.ConvertConfigVariable(testConfigFolderNameParentContainerIdUpdated()["name"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_folder.example", "parent_container_id", testutil.ConvertConfigVariable(testConfigFolderNameParentContainerIdUpdated()["parent_container_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_folder.example", "owner_email", testutil.ConvertConfigVariable(testConfigFolderNameParentContainerIdUpdated()["owner_email"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_folder.example", "labels.%", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_folder.example", "labels.env", "prod"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "container_id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "folder_id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "owner_email"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "creation_time"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "update_time"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccResourceManagerFolderParentUUID(t *testing.T) {
|
||||
t.Logf("TestAccResourceManagerFolderParentUUID name: %s", testutil.ConvertConfigVariable(testConfigResourceFolderParentContainerId["name"]))
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||
CheckDestroy: testAccCheckDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
// Create
|
||||
{
|
||||
ConfigVariables: testConfigResourceFolderParentUUID,
|
||||
Config: testutil.ResourceManagerProviderConfigBetaEnabled() + resourceFolder,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_folder.example", "name", testutil.ConvertConfigVariable(testConfigResourceFolderParentUUID["name"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_folder.example", "parent_container_id", testutil.ConvertConfigVariable(testConfigResourceFolderParentUUID["parent_container_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_folder.example", "owner_email", testutil.ConvertConfigVariable(testConfigResourceFolderParentUUID["owner_email"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_folder.example", "labels.%", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_folder.example", "labels.env", "prod"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "container_id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "folder_id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "creation_time"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "update_time"),
|
||||
),
|
||||
},
|
||||
// Data Source
|
||||
{
|
||||
ConfigVariables: testConfigResourceFolderParentUUID,
|
||||
Config: fmt.Sprintf(`
|
||||
%s
|
||||
%s
|
||||
|
||||
data "stackit_resourcemanager_folder" "example" {
|
||||
container_id = stackit_resourcemanager_folder.example.container_id
|
||||
}
|
||||
`, testutil.ResourceManagerProviderConfigBetaEnabled(), resourceFolder),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("data.stackit_resourcemanager_folder.example", "name", testutil.ConvertConfigVariable(testConfigResourceFolderParentUUID["name"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_resourcemanager_folder.example", "labels.%", "1"),
|
||||
resource.TestCheckResourceAttr("data.stackit_resourcemanager_folder.example", "labels.env", "prod"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_folder.example", "parent_container_id"),
|
||||
resource.TestCheckResourceAttrPair("data.stackit_resourcemanager_folder.example", "container_id", "stackit_resourcemanager_folder.example", "container_id"),
|
||||
resource.TestCheckResourceAttrPair("data.stackit_resourcemanager_folder.example", "project_id", "stackit_resourcemanager_folder.example", "project_id"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_folder.example", "id"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_folder.example", "creation_time"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_resourcemanager_folder.example", "update_time"),
|
||||
),
|
||||
},
|
||||
// Import
|
||||
{
|
||||
ConfigVariables: testConfigResourceFolderParentUUID,
|
||||
ResourceName: "stackit_resourcemanager_folder.example",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
return getImportIdFromID(s, "stackit_resourcemanager_folder.example", "container_id")
|
||||
},
|
||||
ImportStateVerifyIgnore: []string{"owner_email", "parent_container_id"},
|
||||
},
|
||||
// Update
|
||||
{
|
||||
ConfigVariables: testConfigFolderNameParentUUIDUpdated(),
|
||||
Config: testutil.ResourceManagerProviderConfigBetaEnabled() + resourceFolder,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_folder.example", "name", testutil.ConvertConfigVariable(testConfigFolderNameParentUUIDUpdated()["name"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_folder.example", "parent_container_id", testutil.ConvertConfigVariable(testConfigFolderNameParentUUIDUpdated()["parent_container_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_folder.example", "owner_email", testutil.ConvertConfigVariable(testConfigFolderNameParentUUIDUpdated()["owner_email"])),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_folder.example", "labels.%", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_resourcemanager_folder.example", "labels.env", "prod"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "container_id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "folder_id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "owner_email"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "creation_time"),
|
||||
resource.TestCheckResourceAttrSet("stackit_resourcemanager_folder.example", "update_time"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckDestroy(s *terraform.State) error {
|
||||
checkFunctions := []func(s *terraform.State) error{
|
||||
testAccCheckResourceManagerProjectsDestroy,
|
||||
testAccCheckResourceManagerFoldersDestroy,
|
||||
}
|
||||
var errs []error
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(len(checkFunctions))
|
||||
|
||||
for _, f := range checkFunctions {
|
||||
go func() {
|
||||
err := f(s)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func testAccCheckResourceManagerProjectsDestroy(s *terraform.State) error {
|
||||
ctx := context.Background()
|
||||
var client *resourcemanager.APIClient
|
||||
var err error
|
||||
|
|
@ -179,7 +453,7 @@ func testAccCheckResourceManagerDestroy(s *terraform.State) error {
|
|||
client, err = resourcemanager.NewAPIClient()
|
||||
} else {
|
||||
client, err = resourcemanager.NewAPIClient(
|
||||
config.WithEndpoint(testutil.ResourceManagerCustomEndpoint),
|
||||
sdkConfig.WithEndpoint(testutil.ResourceManagerCustomEndpoint),
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
|
|
@ -196,7 +470,17 @@ func testAccCheckResourceManagerDestroy(s *terraform.State) error {
|
|||
projectsToDestroy = append(projectsToDestroy, containerId)
|
||||
}
|
||||
|
||||
projectsResp, err := client.ListProjects(ctx).ContainerParentId(projectResource["parent_container_id"]).Execute()
|
||||
var containerParentId string
|
||||
switch {
|
||||
case testutil.TestProjectParentContainerID != "":
|
||||
containerParentId = testutil.TestProjectParentContainerID
|
||||
case testutil.TestProjectParentUUID != "":
|
||||
containerParentId = testutil.TestProjectParentUUID
|
||||
default:
|
||||
return fmt.Errorf("either TestProjectParentContainerID or TestProjectParentUUID must be set")
|
||||
}
|
||||
|
||||
projectsResp, err := client.ListProjects(ctx).ContainerParentId(containerParentId).Execute()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting projectsResp: %w", err)
|
||||
}
|
||||
|
|
@ -221,3 +505,69 @@ func testAccCheckResourceManagerDestroy(s *terraform.State) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccCheckResourceManagerFoldersDestroy(s *terraform.State) error {
|
||||
ctx := context.Background()
|
||||
var client *resourcemanager.APIClient
|
||||
var err error
|
||||
if testutil.ResourceManagerCustomEndpoint == "" {
|
||||
client, err = resourcemanager.NewAPIClient()
|
||||
} else {
|
||||
client, err = resourcemanager.NewAPIClient(
|
||||
sdkConfig.WithEndpoint(testutil.ResourceManagerCustomEndpoint),
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating client: %w", err)
|
||||
}
|
||||
|
||||
foldersToDestroy := []string{}
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "stackit_resourcemanager_folder" {
|
||||
continue
|
||||
}
|
||||
// project terraform ID: "[container_id]"
|
||||
containerId := rs.Primary.ID
|
||||
foldersToDestroy = append(foldersToDestroy, containerId)
|
||||
}
|
||||
|
||||
var containerParentId string
|
||||
switch {
|
||||
case testutil.TestProjectParentContainerID != "":
|
||||
containerParentId = testutil.TestProjectParentContainerID
|
||||
case testutil.TestProjectParentUUID != "":
|
||||
containerParentId = testutil.TestProjectParentUUID
|
||||
default:
|
||||
return fmt.Errorf("either TestProjectParentContainerID or TestProjectParentUUID must be set")
|
||||
}
|
||||
|
||||
projectsResp, err := client.ListFolders(ctx).ContainerParentId(containerParentId).Execute()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting projectsResp: %w", err)
|
||||
}
|
||||
|
||||
items := *projectsResp.Items
|
||||
for i := range items {
|
||||
if !utils.Contains(foldersToDestroy, *items[i].ContainerId) {
|
||||
continue
|
||||
}
|
||||
|
||||
err := client.DeleteFolder(ctx, *items[i].ContainerId).Execute()
|
||||
if err != nil {
|
||||
return fmt.Errorf("destroying folder %s during CheckDestroy: %w", *items[i].ContainerId, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getImportIdFromID(s *terraform.State, resourceName, keyName string) (string, error) {
|
||||
r, ok := s.RootModule().Resources[resourceName]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find resource %s", resourceName)
|
||||
}
|
||||
id, ok := r.Primary.Attributes[keyName]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find attribute %s", keyName)
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
|
|
|||
12
stackit/internal/services/resourcemanager/testdata/resource-folder.tf
vendored
Normal file
12
stackit/internal/services/resourcemanager/testdata/resource-folder.tf
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
variable "parent_container_id" {}
|
||||
variable "name" {}
|
||||
variable "labels" {}
|
||||
variable "owner_email" {}
|
||||
|
||||
resource "stackit_resourcemanager_folder" "example" {
|
||||
parent_container_id = var.parent_container_id
|
||||
name = var.name
|
||||
labels = var.labels
|
||||
owner_email = var.owner_email
|
||||
}
|
||||
12
stackit/internal/services/resourcemanager/testdata/resource-project.tf
vendored
Normal file
12
stackit/internal/services/resourcemanager/testdata/resource-project.tf
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
variable "parent_container_id" {}
|
||||
variable "name" {}
|
||||
variable "labels" {}
|
||||
variable "owner_email" {}
|
||||
|
||||
resource "stackit_resourcemanager_project" "example" {
|
||||
parent_container_id = var.parent_container_id
|
||||
name = var.name
|
||||
labels = var.labels
|
||||
owner_email = var.owner_email
|
||||
}
|
||||
|
|
@ -323,10 +323,8 @@ func ResourceManagerProviderConfig() string {
|
|||
if ResourceManagerCustomEndpoint == "" || AuthorizationCustomEndpoint == "" {
|
||||
return fmt.Sprintf(`
|
||||
provider "stackit" {
|
||||
service_account_email = "%s"
|
||||
service_account_token = "%s"
|
||||
}`,
|
||||
TestProjectServiceAccountEmail,
|
||||
token,
|
||||
)
|
||||
}
|
||||
|
|
@ -334,12 +332,35 @@ func ResourceManagerProviderConfig() string {
|
|||
provider "stackit" {
|
||||
resourcemanager_custom_endpoint = "%s"
|
||||
authorization_custom_endpoint = "%s"
|
||||
service_account_email = "%s"
|
||||
service_account_token = "%s"
|
||||
}`,
|
||||
ResourceManagerCustomEndpoint,
|
||||
AuthorizationCustomEndpoint,
|
||||
TestProjectServiceAccountEmail,
|
||||
token,
|
||||
)
|
||||
}
|
||||
|
||||
func ResourceManagerProviderConfigBetaEnabled() string {
|
||||
token := GetTestProjectServiceAccountToken("")
|
||||
if ResourceManagerCustomEndpoint == "" || AuthorizationCustomEndpoint == "" {
|
||||
return fmt.Sprintf(`
|
||||
provider "stackit" {
|
||||
service_account_token = "%s"
|
||||
enable_beta_resources = true
|
||||
}`,
|
||||
|
||||
token,
|
||||
)
|
||||
}
|
||||
return fmt.Sprintf(`
|
||||
provider "stackit" {
|
||||
resourcemanager_custom_endpoint = "%s"
|
||||
authorization_custom_endpoint = "%s"
|
||||
service_account_token = "%s"
|
||||
enable_beta_resources = true
|
||||
}`,
|
||||
ResourceManagerCustomEndpoint,
|
||||
AuthorizationCustomEndpoint,
|
||||
token,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ import (
|
|||
rabbitMQInstance "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/rabbitmq/instance"
|
||||
redisCredential "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/redis/credential"
|
||||
redisInstance "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/redis/instance"
|
||||
resourceManagerFolder "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/resourcemanager/folder"
|
||||
resourceManagerProject "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/resourcemanager/project"
|
||||
secretsManagerInstance "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/secretsmanager/instance"
|
||||
secretsManagerUser "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/secretsmanager/user"
|
||||
|
|
@ -499,6 +500,7 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource
|
|||
redisInstance.NewInstanceDataSource,
|
||||
redisCredential.NewCredentialDataSource,
|
||||
resourceManagerProject.NewProjectDataSource,
|
||||
resourceManagerFolder.NewFolderDataSource,
|
||||
secretsManagerInstance.NewInstanceDataSource,
|
||||
secretsManagerUser.NewUserDataSource,
|
||||
sqlServerFlexInstance.NewInstanceDataSource,
|
||||
|
|
@ -565,6 +567,7 @@ func (p *Provider) Resources(_ context.Context) []func() resource.Resource {
|
|||
redisInstance.NewInstanceResource,
|
||||
redisCredential.NewCredentialResource,
|
||||
resourceManagerProject.NewProjectResource,
|
||||
resourceManagerFolder.NewFolderResource,
|
||||
secretsManagerInstance.NewInstanceResource,
|
||||
secretsManagerUser.NewUserResource,
|
||||
sqlServerFlexInstance.NewInstanceResource,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue