feat(iaas): onboard iaas project datasource (#955)
* feat: onboard iaas project datasource
This commit is contained in:
parent
2558c02584
commit
5f1e4ff192
8 changed files with 399 additions and 3 deletions
35
docs/data-sources/iaas_project.md
Normal file
35
docs/data-sources/iaas_project.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "stackit_iaas_project Data Source - stackit"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
Project details. Must have a region specified in the provider configuration.
|
||||
---
|
||||
|
||||
# stackit_iaas_project (Data Source)
|
||||
|
||||
Project details. Must have a `region` specified in the provider configuration.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
data "stackit_iaas_project" "example" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `project_id` (String) STACKIT project ID.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `area_id` (String) The area ID to which the project belongs to.
|
||||
- `created_at` (String) Date-time when the project was created.
|
||||
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`".
|
||||
- `internet_access` (Boolean) Specifies if the project has internet_access
|
||||
- `state` (String) Specifies the state of the project.
|
||||
- `updated_at` (String) Date-time when the project was last updated.
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
data "stackit_iaas_project" "example" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
2
go.mod
2
go.mod
|
|
@ -15,7 +15,7 @@ require (
|
|||
github.com/stackitcloud/stackit-sdk-go/services/cdn v1.4.0
|
||||
github.com/stackitcloud/stackit-sdk-go/services/dns v0.17.1
|
||||
github.com/stackitcloud/stackit-sdk-go/services/git v0.7.1
|
||||
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.29.0
|
||||
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.29.1
|
||||
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.21-alpha
|
||||
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.5.1
|
||||
github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.1
|
||||
|
|
|
|||
4
go.sum
4
go.sum
|
|
@ -160,8 +160,8 @@ github.com/stackitcloud/stackit-sdk-go/services/dns v0.17.1 h1:CnhAMLql0MNmAeq4r
|
|||
github.com/stackitcloud/stackit-sdk-go/services/dns v0.17.1/go.mod h1:7Bx85knfNSBxulPdJUFuBePXNee3cO+sOTYnUG6M+iQ=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/git v0.7.1 h1:hkFixFnBcQzU4BSIZFITc8N0gK0pUYk7mk0wdUu5Ki8=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/git v0.7.1/go.mod h1:Ng1EzrRndG3iGXGH90AZJz//wfK+2YOyDwTnTLwX3a4=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.29.0 h1:j4FKFOVkcTot8xNxpvDsPzIFyjADE4GxXF0rFE6/Uo4=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.29.0/go.mod h1:b/jgJf7QHdRzU2fmZeJJtu5j0TAevDRghzcn5MyRmOI=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.29.1 h1:GfE+FaeIKSVaKvgzh8Eacum+bQVyRS6ngltkh0qNGtM=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.29.1/go.mod h1:b/jgJf7QHdRzU2fmZeJJtu5j0TAevDRghzcn5MyRmOI=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.21-alpha h1:m1jq6a8dbUe+suFuUNdHmM/cSehpGLUtDbK1CqLqydg=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.21-alpha/go.mod h1:Nu1b5Phsv8plgZ51+fkxPVsU91ZJ5Ayz+cthilxdmQ8=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.5.1 h1:OdJEs8eOfrzn9tCBDLxIyP8hX50zPfcXNYnRoQX+chs=
|
||||
|
|
|
|||
|
|
@ -4022,6 +4022,38 @@ func TestAccImageMax(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccProject(t *testing.T) {
|
||||
projectId := testutil.ProjectId
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||
Steps: []resource.TestStep{
|
||||
// Data source
|
||||
{
|
||||
ConfigVariables: testConfigKeyPairMin,
|
||||
Config: fmt.Sprintf(`
|
||||
%s
|
||||
|
||||
data "stackit_iaas_project" "project" {
|
||||
project_id = %q
|
||||
}
|
||||
`,
|
||||
testutil.IaaSProviderConfig(), testutil.ProjectId,
|
||||
),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Instance
|
||||
resource.TestCheckResourceAttr("data.stackit_iaas_project.project", "project_id", projectId),
|
||||
resource.TestCheckResourceAttr("data.stackit_iaas_project.project", "id", projectId),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_iaas_project.project", "area_id"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_iaas_project.project", "internet_access"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_iaas_project.project", "state"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_iaas_project.project", "created_at"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_iaas_project.project", "updated_at"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckDestroy(s *terraform.State) error {
|
||||
checkFunctions := []func(s *terraform.State) error{
|
||||
testAccCheckNetworkV1Destroy,
|
||||
|
|
|
|||
204
stackit/internal/services/iaas/project/datasource.go
Normal file
204
stackit/internal/services/iaas/project/datasource.go
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
package project
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"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-framework/types/basetypes"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
iaasUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/utils"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
|
||||
)
|
||||
|
||||
var (
|
||||
_ datasource.DataSourceWithConfigure = &projectDataSource{}
|
||||
)
|
||||
|
||||
type DatasourceModel struct {
|
||||
Id types.String `tfsdk:"id"` // needed by TF
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
AreaId types.String `tfsdk:"area_id"`
|
||||
InternetAccess types.Bool `tfsdk:"internet_access"`
|
||||
State types.String `tfsdk:"state"`
|
||||
CreatedAt types.String `tfsdk:"created_at"`
|
||||
UpdatedAt types.String `tfsdk:"updated_at"`
|
||||
}
|
||||
|
||||
// NewProjectDataSource is a helper function to simplify the provider implementation.
|
||||
func NewProjectDataSource() datasource.DataSource {
|
||||
return &projectDataSource{}
|
||||
}
|
||||
|
||||
// projectDatasource is the data source implementation.
|
||||
type projectDataSource struct {
|
||||
client *iaas.APIClient
|
||||
}
|
||||
|
||||
func (d *projectDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
providerData, ok := conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
apiClient := iaasUtils.ConfigureClient(ctx, &providerData, &resp.Diagnostics)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
d.client = apiClient
|
||||
tflog.Info(ctx, "iaas client configured")
|
||||
}
|
||||
|
||||
// Metadata returns the data source type name.
|
||||
func (d *projectDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_iaas_project"
|
||||
}
|
||||
|
||||
// Schema defines the schema for the datasource.
|
||||
func (d *projectDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
descriptions := map[string]string{
|
||||
"main": "Project details. Must have a `region` specified in the provider configuration.",
|
||||
"id": "Terraform's internal resource ID. It is structured as \"`project_id`\".",
|
||||
"project_id": "STACKIT project ID.",
|
||||
"area_id": "The area ID to which the project belongs to.",
|
||||
"internet_access": "Specifies if the project has internet_access",
|
||||
"state": "Specifies the state of the project.",
|
||||
"created_at": "Date-time when the project was created.",
|
||||
"updated_at": "Date-time when the project was last updated.",
|
||||
}
|
||||
resp.Schema = schema.Schema{
|
||||
MarkdownDescription: descriptions["main"],
|
||||
Description: descriptions["main"],
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Description: descriptions["id"],
|
||||
Computed: true,
|
||||
},
|
||||
"project_id": schema.StringAttribute{
|
||||
Description: descriptions["project_id"],
|
||||
Required: true,
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"area_id": schema.StringAttribute{
|
||||
Description: descriptions["area_id"],
|
||||
Computed: true,
|
||||
},
|
||||
"internet_access": schema.BoolAttribute{
|
||||
Description: descriptions["internet_access"],
|
||||
Computed: true,
|
||||
},
|
||||
"state": schema.StringAttribute{
|
||||
Description: descriptions["state"],
|
||||
Computed: true,
|
||||
},
|
||||
"created_at": schema.StringAttribute{
|
||||
Description: descriptions["created_at"],
|
||||
Computed: true,
|
||||
},
|
||||
"updated_at": schema.StringAttribute{
|
||||
Description: descriptions["updated_at"],
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Read refreshes the Terraform state with the latest data.
|
||||
func (d *projectDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model DatasourceModel
|
||||
diags := req.Config.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
projectId := model.ProjectId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
|
||||
projectResp, err := d.client.GetProjectDetailsExecute(ctx, projectId)
|
||||
if err != nil {
|
||||
utils.LogError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
err,
|
||||
"Reading project",
|
||||
fmt.Sprintf("Project with ID %q does not exists.", projectId),
|
||||
nil,
|
||||
)
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
// Map response body to schema
|
||||
err = mapDataSourceFields(projectResp, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading project", fmt.Sprintf("Process API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Set refreshed state
|
||||
diags = resp.State.Set(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "project read")
|
||||
}
|
||||
|
||||
func mapDataSourceFields(projectResp *iaas.Project, model *DatasourceModel) error {
|
||||
if projectResp == nil {
|
||||
return fmt.Errorf("response input is nil")
|
||||
}
|
||||
if model == nil {
|
||||
return fmt.Errorf("model input is nil")
|
||||
}
|
||||
|
||||
var projectId string
|
||||
if model.ProjectId.ValueString() != "" {
|
||||
projectId = model.ProjectId.ValueString()
|
||||
} else if projectResp.ProjectId != nil {
|
||||
projectId = *projectResp.ProjectId
|
||||
} else {
|
||||
return fmt.Errorf("project id is not present")
|
||||
}
|
||||
|
||||
model.Id = utils.BuildInternalTerraformId(projectId)
|
||||
model.ProjectId = types.StringValue(projectId)
|
||||
|
||||
var areaId basetypes.StringValue
|
||||
if projectResp.AreaId != nil {
|
||||
if projectResp.AreaId.String != nil {
|
||||
areaId = types.StringPointerValue(projectResp.AreaId.String)
|
||||
} else if projectResp.AreaId.StaticAreaID != nil {
|
||||
areaId = types.StringValue(string(*projectResp.AreaId.StaticAreaID))
|
||||
}
|
||||
}
|
||||
|
||||
var createdAt basetypes.StringValue
|
||||
if projectResp.CreatedAt != nil {
|
||||
createdAtValue := *projectResp.CreatedAt
|
||||
createdAt = types.StringValue(createdAtValue.Format(time.RFC3339))
|
||||
}
|
||||
|
||||
var updatedAt basetypes.StringValue
|
||||
if projectResp.UpdatedAt != nil {
|
||||
updatedAtValue := *projectResp.UpdatedAt
|
||||
updatedAt = types.StringValue(updatedAtValue.Format(time.RFC3339))
|
||||
}
|
||||
|
||||
model.AreaId = areaId
|
||||
model.InternetAccess = types.BoolPointerValue(projectResp.InternetAccess)
|
||||
model.State = types.StringPointerValue(projectResp.State)
|
||||
model.CreatedAt = createdAt
|
||||
model.UpdatedAt = updatedAt
|
||||
return nil
|
||||
}
|
||||
120
stackit/internal/services/iaas/project/datasource_test.go
Normal file
120
stackit/internal/services/iaas/project/datasource_test.go
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
package project
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
|
||||
)
|
||||
|
||||
const (
|
||||
testTimestampValue = "2006-01-02T15:04:05Z"
|
||||
)
|
||||
|
||||
func testTimestamp() time.Time {
|
||||
timestamp, _ := time.Parse(time.RFC3339, testTimestampValue)
|
||||
return timestamp
|
||||
}
|
||||
|
||||
func TestMapDataSourceFields(t *testing.T) {
|
||||
const projectId = "pid"
|
||||
tests := []struct {
|
||||
description string
|
||||
state *DatasourceModel
|
||||
input *iaas.Project
|
||||
expected *DatasourceModel
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
description: "default_values",
|
||||
state: &DatasourceModel{
|
||||
ProjectId: types.StringValue(projectId),
|
||||
},
|
||||
input: &iaas.Project{
|
||||
ProjectId: utils.Ptr(projectId),
|
||||
},
|
||||
expected: &DatasourceModel{
|
||||
Id: types.StringValue(projectId),
|
||||
ProjectId: types.StringValue(projectId),
|
||||
},
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
description: "simple_values",
|
||||
state: &DatasourceModel{
|
||||
ProjectId: types.StringValue(projectId),
|
||||
},
|
||||
input: &iaas.Project{
|
||||
AreaId: utils.Ptr(iaas.AreaId{String: utils.Ptr("aid")}),
|
||||
CreatedAt: utils.Ptr(testTimestamp()),
|
||||
InternetAccess: utils.Ptr(true),
|
||||
OpenstackProjectId: utils.Ptr("oid"),
|
||||
ProjectId: utils.Ptr(projectId),
|
||||
State: utils.Ptr("CREATED"),
|
||||
UpdatedAt: utils.Ptr(testTimestamp()),
|
||||
},
|
||||
expected: &DatasourceModel{
|
||||
Id: types.StringValue(projectId),
|
||||
ProjectId: types.StringValue(projectId),
|
||||
AreaId: types.StringValue("aid"),
|
||||
InternetAccess: types.BoolValue(true),
|
||||
State: types.StringValue("CREATED"),
|
||||
CreatedAt: types.StringValue(testTimestampValue),
|
||||
UpdatedAt: types.StringValue(testTimestampValue),
|
||||
},
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
description: "static_area_id",
|
||||
state: &DatasourceModel{
|
||||
ProjectId: types.StringValue(projectId),
|
||||
},
|
||||
input: &iaas.Project{
|
||||
AreaId: utils.Ptr(iaas.AreaId{
|
||||
StaticAreaID: iaas.STATICAREAID_PUBLIC.Ptr(),
|
||||
}),
|
||||
ProjectId: utils.Ptr(projectId),
|
||||
},
|
||||
expected: &DatasourceModel{
|
||||
Id: types.StringValue(projectId),
|
||||
ProjectId: types.StringValue(projectId),
|
||||
AreaId: types.StringValue("PUBLIC"),
|
||||
},
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
description: "response_nil_fail",
|
||||
state: &DatasourceModel{},
|
||||
input: nil,
|
||||
expected: &DatasourceModel{},
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
description: "no_project_id_fail",
|
||||
state: &DatasourceModel{},
|
||||
input: &iaas.Project{},
|
||||
expected: &DatasourceModel{},
|
||||
isValid: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
err := mapDataSourceFields(tt.input, tt.state)
|
||||
if !tt.isValid && err == nil {
|
||||
t.Fatal("should have failed")
|
||||
}
|
||||
if tt.isValid && err != nil {
|
||||
t.Fatalf("Should not have failed: %v", err)
|
||||
}
|
||||
if tt.isValid {
|
||||
diff := cmp.Diff(tt.expected, tt.state)
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -32,6 +32,7 @@ import (
|
|||
iaasNetworkAreaRoute "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/networkarearoute"
|
||||
iaasNetworkInterface "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/networkinterface"
|
||||
iaasNetworkInterfaceAttach "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/networkinterfaceattach"
|
||||
iaasProject "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/project"
|
||||
iaasPublicIp "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/publicip"
|
||||
iaasPublicIpAssociate "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/publicipassociate"
|
||||
iaasPublicIpRanges "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/publicipranges"
|
||||
|
|
@ -460,6 +461,7 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource
|
|||
iaasNetworkAreaRoute.NewNetworkAreaRouteDataSource,
|
||||
iaasNetworkInterface.NewNetworkInterfaceDataSource,
|
||||
iaasVolume.NewVolumeDataSource,
|
||||
iaasProject.NewProjectDataSource,
|
||||
iaasPublicIp.NewPublicIpDataSource,
|
||||
iaasPublicIpRanges.NewPublicIpRangesDataSource,
|
||||
iaasKeyPair.NewKeyPairDataSource,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue