fix: separated models for resource and data-source (#640)
This commit is contained in:
parent
4d6f860b26
commit
a2ed2b6068
2 changed files with 271 additions and 6 deletions
|
|
@ -4,11 +4,15 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"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/core/config"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||
|
|
@ -29,6 +33,25 @@ var (
|
|||
_ datasource.DataSource = &serverDataSource{}
|
||||
)
|
||||
|
||||
type DataSourceModel struct {
|
||||
Id types.String `tfsdk:"id"` // needed by TF
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
ServerId types.String `tfsdk:"server_id"`
|
||||
MachineType types.String `tfsdk:"machine_type"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
AvailabilityZone types.String `tfsdk:"availability_zone"`
|
||||
BootVolume types.Object `tfsdk:"boot_volume"`
|
||||
ImageId types.String `tfsdk:"image_id"`
|
||||
NetworkInterfaces types.List `tfsdk:"network_interfaces"`
|
||||
KeypairName types.String `tfsdk:"keypair_name"`
|
||||
Labels types.Map `tfsdk:"labels"`
|
||||
AffinityGroup types.String `tfsdk:"affinity_group"`
|
||||
UserData types.String `tfsdk:"user_data"`
|
||||
CreatedAt types.String `tfsdk:"created_at"`
|
||||
LaunchedAt types.String `tfsdk:"launched_at"`
|
||||
UpdatedAt types.String `tfsdk:"updated_at"`
|
||||
}
|
||||
|
||||
// NewServerDataSource is a helper function to simplify the provider implementation.
|
||||
func NewServerDataSource() datasource.DataSource {
|
||||
return &serverDataSource{}
|
||||
|
|
@ -185,17 +208,13 @@ func (r *serverDataSource) Schema(_ context.Context, _ datasource.SchemaRequest,
|
|||
Description: "Date-time when the server was updated",
|
||||
Computed: true,
|
||||
},
|
||||
"desired_status": schema.StringAttribute{
|
||||
Description: "The desired status of the server resource." + utils.SupportedValuesDocumentation(desiredStatusOptions),
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// // Read refreshes the Terraform state with the latest data.
|
||||
func (r *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model Model
|
||||
var model DataSourceModel
|
||||
diags := req.Config.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
|
|
@ -220,7 +239,7 @@ func (r *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest,
|
|||
}
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFields(ctx, serverResp, &model)
|
||||
err = mapDataSourceFields(ctx, serverResp, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading server", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
|
|
@ -233,3 +252,87 @@ func (r *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest,
|
|||
}
|
||||
tflog.Info(ctx, "server read")
|
||||
}
|
||||
|
||||
func mapDataSourceFields(ctx context.Context, serverResp *iaas.Server, model *DataSourceModel) error {
|
||||
if serverResp == nil {
|
||||
return fmt.Errorf("response input is nil")
|
||||
}
|
||||
if model == nil {
|
||||
return fmt.Errorf("model input is nil")
|
||||
}
|
||||
|
||||
var serverId string
|
||||
if model.ServerId.ValueString() != "" {
|
||||
serverId = model.ServerId.ValueString()
|
||||
} else if serverResp.Id != nil {
|
||||
serverId = *serverResp.Id
|
||||
} else {
|
||||
return fmt.Errorf("server id not present")
|
||||
}
|
||||
|
||||
idParts := []string{
|
||||
model.ProjectId.ValueString(),
|
||||
serverId,
|
||||
}
|
||||
model.Id = types.StringValue(
|
||||
strings.Join(idParts, core.Separator),
|
||||
)
|
||||
|
||||
labels, diags := types.MapValueFrom(ctx, types.StringType, map[string]interface{}{})
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("convert labels to StringValue map: %w", core.DiagsToError(diags))
|
||||
}
|
||||
if serverResp.Labels != nil && len(*serverResp.Labels) != 0 {
|
||||
var diags diag.Diagnostics
|
||||
labels, diags = types.MapValueFrom(ctx, types.StringType, *serverResp.Labels)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("convert labels to StringValue map: %w", core.DiagsToError(diags))
|
||||
}
|
||||
} else if model.Labels.IsNull() {
|
||||
labels = types.MapNull(types.StringType)
|
||||
}
|
||||
var createdAt basetypes.StringValue
|
||||
if serverResp.CreatedAt != nil {
|
||||
createdAtValue := *serverResp.CreatedAt
|
||||
createdAt = types.StringValue(createdAtValue.Format(time.RFC3339))
|
||||
}
|
||||
var updatedAt basetypes.StringValue
|
||||
if serverResp.UpdatedAt != nil {
|
||||
updatedAtValue := *serverResp.UpdatedAt
|
||||
updatedAt = types.StringValue(updatedAtValue.Format(time.RFC3339))
|
||||
}
|
||||
var launchedAt basetypes.StringValue
|
||||
if serverResp.LaunchedAt != nil {
|
||||
launchedAtValue := *serverResp.LaunchedAt
|
||||
launchedAt = types.StringValue(launchedAtValue.Format(time.RFC3339))
|
||||
}
|
||||
if serverResp.Nics != nil {
|
||||
var respNics []string
|
||||
for _, nic := range *serverResp.Nics {
|
||||
respNics = append(respNics, *nic.NicId)
|
||||
}
|
||||
nicTF, diags := types.ListValueFrom(ctx, types.StringType, respNics)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("failed to map networkInterfaces: %w", core.DiagsToError(diags))
|
||||
}
|
||||
|
||||
model.NetworkInterfaces = nicTF
|
||||
} else {
|
||||
model.NetworkInterfaces = types.ListNull(types.StringType)
|
||||
}
|
||||
|
||||
model.AvailabilityZone = types.StringPointerValue(serverResp.AvailabilityZone)
|
||||
model.ServerId = types.StringValue(serverId)
|
||||
model.MachineType = types.StringPointerValue(serverResp.MachineType)
|
||||
|
||||
model.Name = types.StringPointerValue(serverResp.Name)
|
||||
model.Labels = labels
|
||||
model.ImageId = types.StringPointerValue(serverResp.ImageId)
|
||||
model.KeypairName = types.StringPointerValue(serverResp.KeypairName)
|
||||
model.AffinityGroup = types.StringPointerValue(serverResp.AffinityGroup)
|
||||
model.CreatedAt = createdAt
|
||||
model.UpdatedAt = updatedAt
|
||||
model.LaunchedAt = launchedAt
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
162
stackit/internal/services/iaas/server/datasource_test.go
Normal file
162
stackit/internal/services/iaas/server/datasource_test.go
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
|
||||
)
|
||||
|
||||
func TestMapDataSourceFields(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
state DataSourceModel
|
||||
input *iaas.Server
|
||||
expected DataSourceModel
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"default_values",
|
||||
DataSourceModel{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
ServerId: types.StringValue("sid"),
|
||||
},
|
||||
&iaas.Server{
|
||||
Id: utils.Ptr("sid"),
|
||||
},
|
||||
DataSourceModel{
|
||||
Id: types.StringValue("pid,sid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
ServerId: types.StringValue("sid"),
|
||||
Name: types.StringNull(),
|
||||
AvailabilityZone: types.StringNull(),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
ImageId: types.StringNull(),
|
||||
NetworkInterfaces: types.ListNull(types.StringType),
|
||||
KeypairName: types.StringNull(),
|
||||
AffinityGroup: types.StringNull(),
|
||||
UserData: types.StringNull(),
|
||||
CreatedAt: types.StringNull(),
|
||||
UpdatedAt: types.StringNull(),
|
||||
LaunchedAt: types.StringNull(),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"simple_values",
|
||||
DataSourceModel{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
ServerId: types.StringValue("sid"),
|
||||
},
|
||||
&iaas.Server{
|
||||
Id: utils.Ptr("sid"),
|
||||
Name: utils.Ptr("name"),
|
||||
AvailabilityZone: utils.Ptr("zone"),
|
||||
Labels: &map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
ImageId: utils.Ptr("image_id"),
|
||||
Nics: &[]iaas.ServerNetwork{
|
||||
{
|
||||
NicId: utils.Ptr("nic1"),
|
||||
},
|
||||
{
|
||||
NicId: utils.Ptr("nic2"),
|
||||
},
|
||||
},
|
||||
KeypairName: utils.Ptr("keypair_name"),
|
||||
AffinityGroup: utils.Ptr("group_id"),
|
||||
CreatedAt: utils.Ptr(testTimestamp()),
|
||||
UpdatedAt: utils.Ptr(testTimestamp()),
|
||||
LaunchedAt: utils.Ptr(testTimestamp()),
|
||||
Status: utils.Ptr("active"),
|
||||
},
|
||||
DataSourceModel{
|
||||
Id: types.StringValue("pid,sid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
ServerId: types.StringValue("sid"),
|
||||
Name: types.StringValue("name"),
|
||||
AvailabilityZone: types.StringValue("zone"),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
|
||||
"key": types.StringValue("value"),
|
||||
}),
|
||||
ImageId: types.StringValue("image_id"),
|
||||
NetworkInterfaces: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("nic1"),
|
||||
types.StringValue("nic2"),
|
||||
}),
|
||||
KeypairName: types.StringValue("keypair_name"),
|
||||
AffinityGroup: types.StringValue("group_id"),
|
||||
CreatedAt: types.StringValue(testTimestampValue),
|
||||
UpdatedAt: types.StringValue(testTimestampValue),
|
||||
LaunchedAt: types.StringValue(testTimestampValue),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"empty_labels",
|
||||
DataSourceModel{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
ServerId: types.StringValue("sid"),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{}),
|
||||
},
|
||||
&iaas.Server{
|
||||
Id: utils.Ptr("sid"),
|
||||
},
|
||||
DataSourceModel{
|
||||
Id: types.StringValue("pid,sid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
ServerId: types.StringValue("sid"),
|
||||
Name: types.StringNull(),
|
||||
AvailabilityZone: types.StringNull(),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{}),
|
||||
ImageId: types.StringNull(),
|
||||
NetworkInterfaces: types.ListNull(types.StringType),
|
||||
KeypairName: types.StringNull(),
|
||||
AffinityGroup: types.StringNull(),
|
||||
UserData: types.StringNull(),
|
||||
CreatedAt: types.StringNull(),
|
||||
UpdatedAt: types.StringNull(),
|
||||
LaunchedAt: types.StringNull(),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"response_nil_fail",
|
||||
DataSourceModel{},
|
||||
nil,
|
||||
DataSourceModel{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"no_resource_id",
|
||||
DataSourceModel{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
},
|
||||
&iaas.Server{},
|
||||
DataSourceModel{},
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
err := mapDataSourceFields(context.Background(), tt.input, &tt.state)
|
||||
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(tt.state, tt.expected)
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue