From 502fbaaf54d58acd3095bbbbe5888325d187e905 Mon Sep 17 00:00:00 2001 From: youngmn Date: Sat, 28 Sep 2024 00:52:28 +0900 Subject: [PATCH] review(vserver): Modify based on reviews --- docs/data-sources/server_image_numbers.md | 18 +-- docs/data-sources/server_specs.md | 18 +-- internal/common/convert_types.go | 20 --- internal/common/json.go | 10 +- internal/service/server/block_storage.go | 11 +- internal/service/server/server.go | 114 ++++++++++++------ .../service/server/server_data_source_test.go | 9 +- .../server_image_numbers_data_source.go | 98 +++++++++------ .../server_image_numbers_data_source_test.go | 23 ++-- .../server/server_specs_data_source.go | 53 ++++---- internal/service/server/server_test.go | 10 +- 11 files changed, 226 insertions(+), 158 deletions(-) diff --git a/docs/data-sources/server_image_numbers.md b/docs/data-sources/server_image_numbers.md index e502d185d..ab5fa99b4 100644 --- a/docs/data-sources/server_image_numbers.md +++ b/docs/data-sources/server_image_numbers.md @@ -26,15 +26,6 @@ output "image_list" { } ``` -```terraform -data "ncloud_server_image_numbers" "example" { - filter { - name = "name" - values = ["rocky-8.10-base"] - } -} -``` - Outputs: ```terraform image_list = { @@ -69,6 +60,15 @@ image_list = { } ``` +```terraform +data "ncloud_server_image_numbers" "example" { + filter { + name = "name" + values = ["rocky-8.10-base"] + } +} +``` + ## Argument Reference The following arguments are supported: diff --git a/docs/data-sources/server_specs.md b/docs/data-sources/server_specs.md index b1981234e..375c151ee 100644 --- a/docs/data-sources/server_specs.md +++ b/docs/data-sources/server_specs.md @@ -26,15 +26,6 @@ output "spec_list" { } ``` -```terraform -data "ncloud_server_specs" "example" { - filter { - name = "server_spec_code" - values = ["c2-g3"] - } -} -``` - Outputs: ```terraform spec_list = { @@ -53,6 +44,15 @@ spec_list = { } ``` +```terraform +data "ncloud_server_specs" "example" { + filter { + name = "server_spec_code" + values = ["c2-g3"] + } +} +``` + ## Argument Reference The following arguments are supported: diff --git a/internal/common/convert_types.go b/internal/common/convert_types.go index 57d423207..2a9317360 100644 --- a/internal/common/convert_types.go +++ b/internal/common/convert_types.go @@ -257,26 +257,6 @@ func Int64FromInt32OrDefault(value *int32) basetypes.Int64Value { return basetypes.NewInt64Value(int64(*value)) } -// Int64FromInt64OrDefault converts an int64 pointer to a Framework Int64 value. -// A nil int64 pointer is converted to a zero Int64. -// Used when the optional and computed attribute have no response value -func Int64FromInt64OrDefault(value *int64) basetypes.Int64Value { - if value == nil { - return basetypes.NewInt64Value(0) - } - return basetypes.NewInt64Value(int64(*value)) -} - -// Int32FromInt32OrDefault converts an int32 pointer to a Framework Int32 value. -// A nil int32 pointer is converted to a zero Int32. -// Used when the optional and computed attribute have no response value -func Int32FromInt32OrDefault(value *int32) basetypes.Int32Value { - if value == nil { - return basetypes.NewInt32Value(0) - } - return basetypes.NewInt32Value(int32(*value)) -} - // StringFrameworkOrDefault converts a Framework StringValue struct to a same Framework StringValue. // A null or unknown state is converted to a default(not aloocated) string. // Used when the optional and computed attribute have no response value diff --git a/internal/common/json.go b/internal/common/json.go index 746b01b21..f94b66ef5 100644 --- a/internal/common/json.go +++ b/internal/common/json.go @@ -1,6 +1,9 @@ package common -import "encoding/json" +import ( + "encoding/json" + "regexp" +) // MarshalUnchecked return the JSON encoding of value // It does not check for errors about marshaling failing, so be careful to use @@ -12,3 +15,8 @@ func MarshalUnchecked(value interface{}) []byte { func MarshalUncheckedString(value interface{}) string { return string(MarshalUnchecked(value)) } + +func ReplaceNull(s string) string { + re := regexp.MustCompile(`:`) + return re.ReplaceAllString(s, ":null") +} diff --git a/internal/service/server/block_storage.go b/internal/service/server/block_storage.go index bdad687a3..dbbb63162 100644 --- a/internal/service/server/block_storage.go +++ b/internal/service/server/block_storage.go @@ -19,10 +19,13 @@ import ( ) const ( - BlockStorageStatusCodeCreate = "CREAT" - BlockStorageStatusCodeInit = "INIT" - BlockStorageStatusCodeAttach = "ATTAC" - BlockStorageStatusNameAttach = "attached" + BlockStorageStatusCodeCreate = "CREAT" + BlockStorageStatusCodeInit = "INIT" + BlockStorageStatusCodeAttach = "ATTAC" + BlockStorageStatusNameInit = "initialized" + BlockStorageStatusNameOptimizing = "optimizing" + BlockStorageStatusNameAttach = "attached" + BlockStorageStatusNameDetach = "detached" ) func ResourceNcloudBlockStorage() *schema.Resource { diff --git a/internal/service/server/server.go b/internal/service/server/server.go index d1eb63d1f..f0c225c10 100644 --- a/internal/service/server/server.go +++ b/internal/service/server/server.go @@ -12,6 +12,7 @@ import ( "github.com/NaverCloudPlatform/ncloud-sdk-go-v2/ncloud" "github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/server" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -88,11 +89,6 @@ func ResourceNcloudServer() *schema.Resource { Computed: true, ForceNew: true, }, - "associate_with_public_ip": { - Type: schema.TypeBool, - Optional: true, - ForceNew: true, - }, "is_protect_server_termination": { Type: schema.TypeBool, Optional: true, @@ -342,7 +338,7 @@ func resourceNcloudServerDelete(d *schema.ResourceData, meta interface{}) error return err } - if err := waitForDisconnectBlockStorage(config, d, blockStorage); err != nil { + if err := waitForDisconnectBlockStorage(config, *blockStorage.BlockStorageInstanceNo); err != nil { return err } } @@ -477,7 +473,6 @@ func createVpcServerInstance(d *schema.ResourceData, config *conn.ProviderConfig ServerDescription: StringPtrOrNil(d.GetOk("description")), LoginKeyName: StringPtrOrNil(d.GetOk("login_key_name")), IsProtectServerTermination: BoolPtrOrNil(d.GetOk("is_protect_server_termination")), - AssociateWithPublicIp: BoolPtrOrNil(d.GetOk("associate_with_public_ip")), FeeSystemTypeCode: StringPtrOrNil(d.GetOk("fee_system_type_code")), InitScriptNo: StringPtrOrNil(d.GetOk("init_script_no")), VpcNo: subnet.VpcNo, @@ -543,7 +538,7 @@ func createVpcServerInstance(d *schema.ResourceData, config *conn.ProviderConfig if len(blockStorageList) > 0 { for _, blockStorage := range blockStorageList { - if err := waitForAttachedBlockStorage(config, d, blockStorage); err != nil { + if err := waitForAttachedBlockStorage(config, *blockStorage.BlockStorageInstanceNo); err != nil { return nil, err } } @@ -553,7 +548,7 @@ func createVpcServerInstance(d *schema.ResourceData, config *conn.ProviderConfig } func waitStateNcloudServerForCreation(config *conn.ProviderConfig, id string) error { - stateConf := &resource.StateChangeConf{ + stateConf := &retry.StateChangeConf{ Pending: []string{"INIT", "CREAT"}, Target: []string{"RUN"}, Refresh: func() (interface{}, string, error) { @@ -613,7 +608,7 @@ func changeServerInstanceSpec(d *schema.ResourceData, config *conn.ProviderConfi return err } - stateConf := &resource.StateChangeConf{ + stateConf := &retry.StateChangeConf{ Pending: []string{"CHNG"}, Target: []string{"NULL"}, Refresh: func() (interface{}, string, error) { @@ -734,7 +729,7 @@ func startThenWaitServerInstance(config *conn.ProviderConfig, id string) error { return err } - stateConf := &resource.StateChangeConf{ + stateConf := &retry.StateChangeConf{ Pending: []string{"NSTOP"}, Target: []string{"RUN"}, Refresh: func() (interface{}, string, error) { @@ -957,7 +952,7 @@ func buildNetworkInterfaceList(config *conn.ProviderConfig, r *ServerInstance) e func stopThenWaitServerInstance(config *conn.ProviderConfig, id string) error { var err error - stateConf := &resource.StateChangeConf{ + stateConf := &retry.StateChangeConf{ Pending: []string{"SETUP"}, Target: []string{"NULL"}, Refresh: func() (interface{}, string, error) { @@ -988,7 +983,7 @@ func stopThenWaitServerInstance(config *conn.ProviderConfig, id string) error { return err } - stateConf = &resource.StateChangeConf{ + stateConf = &retry.StateChangeConf{ Pending: []string{"RUN"}, Target: []string{"NSTOP"}, Refresh: func() (interface{}, string, error) { @@ -1013,7 +1008,7 @@ func stopThenWaitServerInstance(config *conn.ProviderConfig, id string) error { } func detachThenWaitServerInstance(config *conn.ProviderConfig, id string) error { - stateConf := &resource.StateChangeConf{ + stateConf := &retry.StateChangeConf{ Pending: []string{"SETUP"}, Target: []string{"NULL"}, Refresh: func() (interface{}, string, error) { @@ -1080,7 +1075,7 @@ func terminateThenWaitServerInstance(config *conn.ProviderConfig, id string) err return err } - stateConf := &resource.StateChangeConf{ + stateConf := &retry.StateChangeConf{ Pending: []string{"NSTOP"}, Target: []string{"TERMINATED"}, Refresh: func() (interface{}, string, error) { @@ -1307,30 +1302,74 @@ func disconnectClassicBlockStorage(config *conn.ProviderConfig, storage *BlockSt return nil } -func waitForDisconnectBlockStorage(config *conn.ProviderConfig, d *schema.ResourceData, storage *BlockStorage) error { - return resource.Retry(d.Timeout(schema.TimeoutDelete), func() *resource.RetryError { - blockStorage, err := GetBlockStorage(config, *storage.BlockStorageInstanceNo) - if err != nil { - return resource.RetryableError(err) - } - if *blockStorage.Status != BlockStorageStatusCodeCreate { - return resource.RetryableError(fmt.Errorf("sill connected block storage(%s)", *blockStorage.BlockStorageInstanceNo)) - } - return nil - }) +func waitForDisconnectBlockStorage(config *conn.ProviderConfig, no string) error { + stateConf := &retry.StateChangeConf{ + Pending: []string{BlockStorageStatusNameAttach}, + Target: []string{BlockStorageStatusNameDetach}, + Refresh: func() (interface{}, string, error) { + resp, err := GetBlockStorage(config, no) + if err != nil { + return 0, "", err + } + + if resp == nil { + return 0, "", fmt.Errorf("GetBlockStorage is nil") + } + + if *resp.StatusName == BlockStorageStatusNameAttach { + return resp, BlockStorageStatusNameAttach, nil + } else if *resp.StatusName == BlockStorageStatusNameDetach { + return resp, BlockStorageStatusNameDetach, nil + } + + return 0, "", fmt.Errorf("error occurred while waiting to detached") + }, + Timeout: 6 * conn.DefaultTimeout, + Delay: 2 * time.Second, + MinTimeout: 3 * time.Second, + } + + if _, err := stateConf.WaitForState(); err != nil { + return fmt.Errorf("Error waiting for BlockStorage (%s) to become available: %s", no, err) + } + + return nil } -func waitForAttachedBlockStorage(config *conn.ProviderConfig, d *schema.ResourceData, storage *BlockStorage) error { - return resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError { - blockStorage, err := GetBlockStorage(config, *storage.BlockStorageInstanceNo) - if err != nil { - return resource.RetryableError(err) - } - if *blockStorage.StatusName != BlockStorageStatusNameAttach { - return resource.RetryableError(fmt.Errorf("sill optimizing block storage(%s)", *blockStorage.BlockStorageInstanceNo)) - } - return nil - }) +func waitForAttachedBlockStorage(config *conn.ProviderConfig, no string) error { + stateConf := &retry.StateChangeConf{ + Pending: []string{BlockStorageStatusNameInit, BlockStorageStatusNameOptimizing}, + Target: []string{BlockStorageStatusNameAttach}, + Refresh: func() (interface{}, string, error) { + resp, err := GetBlockStorage(config, no) + if err != nil { + return 0, "", err + } + + if resp == nil { + return 0, "", fmt.Errorf("GetBlockStorage is nil") + } + + if *resp.StatusName == BlockStorageStatusNameInit { + return resp, BlockStorageStatusNameInit, nil + } else if *resp.StatusName == BlockStorageStatusNameOptimizing { + return resp, BlockStorageStatusNameOptimizing, nil + } else if *resp.StatusName == BlockStorageStatusNameAttach { + return resp, BlockStorageStatusNameAttach, nil + } + + return 0, "", fmt.Errorf("error occurred while waiting to attached") + }, + Timeout: 6 * conn.DefaultTimeout, + Delay: 2 * time.Second, + MinTimeout: 3 * time.Second, + } + + if _, err := stateConf.WaitForState(); err != nil { + return fmt.Errorf("Error waiting for BlockStorage (%s) to become available: %s", no, err) + } + + return nil } func getServerZoneNo(config *conn.ProviderConfig, serverInstanceNo string) (string, error) { @@ -1352,7 +1391,6 @@ type ServerInstance struct { ServerDescription *string `json:"description,omitempty"` LoginKeyName *string `json:"login_key_name,omitempty"` IsProtectServerTermination *bool `json:"is_protect_server_termination,omitempty"` - AssociateWithPublicIp *bool `json:"associate_with_public_ip,omitempty"` FeeSystemTypeCode *string `json:"fee_system_type_code,omitempty"` UserData *string `json:"user_data,omitempty"` RaidTypeName *string `json:"raid_type_name,omitempty"` diff --git a/internal/service/server/server_data_source_test.go b/internal/service/server/server_data_source_test.go index af24bf704..c7bc22009 100644 --- a/internal/service/server/server_data_source_test.go +++ b/internal/service/server/server_data_source_test.go @@ -107,10 +107,17 @@ resource "ncloud_subnet" "test" { usage_type = "GEN" } +data "ncloud_server_image_numbers" "server_images" { + filter { + name = "name" + values = ["ubuntu-22.04-base"] + } +} + resource "ncloud_server" "server" { subnet_no = ncloud_subnet.test.id name = "%[1]s" - server_image_number = "25495367" + server_image_number = data.ncloud_server_image_numbers.server_images.image_number_list.0.server_image_number server_spec_code = "s2-g3" login_key_name = ncloud_login_key.loginkey.key_name } diff --git a/internal/service/server/server_image_numbers_data_source.go b/internal/service/server/server_image_numbers_data_source.go index 5b54b1ac2..af486d22f 100644 --- a/internal/service/server/server_image_numbers_data_source.go +++ b/internal/service/server/server_image_numbers_data_source.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/attr" "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/types" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/terraform-providers/terraform-provider-ncloud/internal/common" @@ -167,9 +168,17 @@ func (d *serverImageNumbersDataSource) Read(ctx context.Context, req datasource. return } - imagesNoList := flattenServerImageList(ctx, imageNoResp.ServerImageList) + imagesNoList, diags := flattenServerImageList(ctx, imageNoResp.ServerImageList) + if diags.HasError() { + resp.Diagnostics.AddError("READING ERROR", "refreshFromOutput error") + return + } fillteredList := common.FilterModels(ctx, data.Filters, imagesNoList) - data.refreshFromOutput(ctx, fillteredList) + diags = data.refreshFromOutput(ctx, fillteredList) + if diags.HasError() { + resp.Diagnostics.AddError("READING ERROR", "refreshFromOutput error") + return + } if !data.OutputFile.IsNull() && data.OutputFile.String() != "" { outputPath := data.OutputFile.ValueString() @@ -191,7 +200,7 @@ func convertImagesToJsonStruct(images []attr.Value) ([]serverImageNoToJsonConver for _, image := range images { imageJasn := serverImageNoToJsonConvert{} - if err := json.Unmarshal([]byte(image.String()), &imageJasn); err != nil { + if err := json.Unmarshal([]byte(common.ReplaceNull(image.String())), &imageJasn); err != nil { return nil, err } serverImagesToConvert = append(serverImagesToConvert, imageJasn) @@ -200,16 +209,20 @@ func convertImagesToJsonStruct(images []attr.Value) ([]serverImageNoToJsonConver return serverImagesToConvert, nil } -func flattenServerImageList(ctx context.Context, list []*vserver.ServerImage) []*serverImageNo { +func flattenServerImageList(ctx context.Context, list []*vserver.ServerImage) ([]*serverImageNo, diag.Diagnostics) { var outputs []*serverImageNo + var diags diag.Diagnostics for _, v := range list { var output serverImageNo - output.refreshFromOutput(ctx, v) + diags = output.refreshFromOutput(ctx, v) + if diags.HasError() { + return nil, diags + } outputs = append(outputs, &output) } - return outputs + return outputs, diags } type serverImageNumbersDataSourceModel struct { @@ -233,15 +246,15 @@ type serverImageNo struct { } type blockStorageMap struct { - Order types.Int32 `tfsdk:"order"` - BlockStorageSnapshotInstNo types.Int32 `tfsdk:"block_storage_snapshot_instance_no"` - BlockStorageSnapshotName types.String `tfsdk:"block_storage_snapshot_name"` - BlockStorageSize types.Int64 `tfsdk:"block_storage_size"` - BlockStorageName types.String `tfsdk:"block_storage_name"` - BlockStorageVolumeType types.String `tfsdk:"block_storage_volume_type"` - Iops types.Int32 `tfsdk:"iops"` - Throughput types.Int64 `tfsdk:"throughput"` - IsEncryptedVolume types.Bool `tfsdk:"is_encrypted_volume"` + Order types.Int32 `tfsdk:"order"` + BlockStorageSnapshotInstanceNo types.Int32 `tfsdk:"block_storage_snapshot_instance_no"` + BlockStorageSnapshotName types.String `tfsdk:"block_storage_snapshot_name"` + BlockStorageSize types.Int64 `tfsdk:"block_storage_size"` + BlockStorageName types.String `tfsdk:"block_storage_name"` + BlockStorageVolumeType types.String `tfsdk:"block_storage_volume_type"` + Iops types.Int32 `tfsdk:"iops"` + Throughput types.Int64 `tfsdk:"throughput"` + IsEncryptedVolume types.Bool `tfsdk:"is_encrypted_volume"` } type serverImageNoToJsonConvert struct { @@ -258,15 +271,15 @@ type serverImageNoToJsonConvert struct { } type blockStorageMapToJsonConvert struct { - Order int32 `json:"order"` - BlockStorageSnapshotInstNo int32 `json:"block_storage_snapshot_instance_no"` - BlockStorageSnapshotName string `json:"block_storage_snapshot_name"` - BlockStorageSize int64 `json:"block_storage_size"` - BlockStorageName string `json:"block_storage_name"` - BlockStorageVolumeType string `json:"block_storage_volume_type"` - Iops int32 `json:"iops"` - Throughput int64 `json:"throughput"` - IsEncryptedVolume bool `json:"is_encrypted_volume"` + Order int32 `json:"order"` + BlockStorageSnapshotInstanceNo int32 `json:"block_storage_snapshot_instance_no,omitempty"` + BlockStorageSnapshotName string `json:"block_storage_snapshot_name,omitempty"` + BlockStorageSize int64 `json:"block_storage_size"` + BlockStorageName string `json:"block_storage_name,omitempty"` + BlockStorageVolumeType string `json:"block_storage_volume_type"` + Iops int32 `json:"iops,omitempty"` + Throughput int64 `json:"throughput,omitempty"` + IsEncryptedVolume bool `json:"is_encrypted_volume"` } func (d serverImageNo) attrTypes() map[string]attr.Type { @@ -298,13 +311,19 @@ func (d blockStorageMap) attrTypes() map[string]attr.Type { } } -func (d *serverImageNumbersDataSourceModel) refreshFromOutput(ctx context.Context, list []*serverImageNo) { - imageNoListValue, _ := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: serverImageNo{}.attrTypes()}, list) +func (d *serverImageNumbersDataSourceModel) refreshFromOutput(ctx context.Context, list []*serverImageNo) diag.Diagnostics { + imageNoListValue, diags := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: serverImageNo{}.attrTypes()}, list) + if diags.HasError() { + return diags + } + d.ImageNumberList = imageNoListValue d.ID = types.StringValue("") + + return diags } -func (d *serverImageNo) refreshFromOutput(ctx context.Context, output *vserver.ServerImage) { +func (d *serverImageNo) refreshFromOutput(ctx context.Context, output *vserver.ServerImage) diag.Diagnostics { d.Number = types.StringPointerValue(output.ServerImageNo) d.Name = types.StringPointerValue(output.ServerImageName) d.Description = types.StringPointerValue(output.ServerImageDescription) @@ -318,18 +337,23 @@ func (d *serverImageNo) refreshFromOutput(ctx context.Context, output *vserver.S var blockStorageList []blockStorageMap for _, block := range output.BlockStorageMappingList { blockStorage := blockStorageMap{ - Order: types.Int32PointerValue(block.Order), - BlockStorageSnapshotInstNo: common.Int32FromInt32OrDefault(block.BlockStorageSnapshotInstanceNo), - BlockStorageSnapshotName: types.StringValue(common.StringOrEmpty(block.BlockStorageSnapshotName)), - BlockStorageSize: types.Int64PointerValue(block.BlockStorageSize), - BlockStorageName: types.StringValue(common.StringOrEmpty(block.BlockStorageName)), - BlockStorageVolumeType: types.StringPointerValue(block.BlockStorageVolumeType.Code), - Iops: common.Int32FromInt32OrDefault(block.Iops), - Throughput: common.Int64FromInt64OrDefault(block.Throughput), - IsEncryptedVolume: types.BoolPointerValue(block.IsEncryptedVolume), + Order: types.Int32PointerValue(block.Order), + BlockStorageSnapshotInstanceNo: types.Int32PointerValue(block.BlockStorageSnapshotInstanceNo), + BlockStorageSnapshotName: types.StringPointerValue(block.BlockStorageSnapshotName), + BlockStorageSize: types.Int64PointerValue(block.BlockStorageSize), + BlockStorageName: types.StringPointerValue(block.BlockStorageName), + BlockStorageVolumeType: types.StringPointerValue(block.BlockStorageVolumeType.Code), + Iops: types.Int32PointerValue(block.Iops), + Throughput: types.Int64PointerValue(block.Throughput), + IsEncryptedVolume: types.BoolPointerValue(block.IsEncryptedVolume), } blockStorageList = append(blockStorageList, blockStorage) } - blockStorageMaps, _ := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: blockStorageMap{}.attrTypes()}, blockStorageList) + blockStorageMaps, diags := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: blockStorageMap{}.attrTypes()}, blockStorageList) + if diags.HasError() { + return diags + } d.BlockStorageMapList = blockStorageMaps + + return diags } diff --git a/internal/service/server/server_image_numbers_data_source_test.go b/internal/service/server/server_image_numbers_data_source_test.go index 67d77f86e..6f70122d9 100644 --- a/internal/service/server/server_image_numbers_data_source_test.go +++ b/internal/service/server/server_image_numbers_data_source_test.go @@ -1,7 +1,7 @@ package server_test import ( - "fmt" + "regexp" "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -10,30 +10,23 @@ import ( func TestAccDataSourceNcloudServerImageNumbers_basic(t *testing.T) { dataName := "data.ncloud_server_image_numbers.images" - imageName := "rocky-8.10-gpu" resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.TestAccPreCheck(t) }, ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, Steps: []resource.TestStep{ { - Config: testAccDataSourceServerImageNumbersConfig(imageName), + Config: testAccDataSourceServerImageNumbersConfig, Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(dataName, "image_number_list.0.name", "rocky-8.10-gpu"), - resource.TestCheckResourceAttr(dataName, "image_number_list.0.number", "25623982"), + resource.TestMatchResourceAttr(dataName, "image_number_list.0.server_image_number", regexp.MustCompile(`^\d+$`)), + resource.TestMatchResourceAttr(dataName, "image_number_list.1.server_image_number", regexp.MustCompile(`^\d+$`)), + resource.TestMatchResourceAttr(dataName, "image_number_list.2.server_image_number", regexp.MustCompile(`^\d+$`)), ), }, }, }) } -func testAccDataSourceServerImageNumbersConfig(imageName string) string { - return fmt.Sprintf(` -data "ncloud_server_image_numbers" "images" { - filter { - name = "name" - values = ["%s"] - } -} -`, imageName) -} +var testAccDataSourceServerImageNumbersConfig = ` +data "ncloud_server_image_numbers" "images" { } +` diff --git a/internal/service/server/server_specs_data_source.go b/internal/service/server/server_specs_data_source.go index f28ab732c..eee3571fa 100644 --- a/internal/service/server/server_specs_data_source.go +++ b/internal/service/server/server_specs_data_source.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/attr" "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/types" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/terraform-providers/terraform-provider-ncloud/internal/common" @@ -150,7 +151,11 @@ func (d *serverSpecsDataSource) Read(ctx context.Context, req datasource.ReadReq specList := flattenServerSpecList(specResp.ServerSpecList) fillteredList := common.FilterModels(ctx, data.Filters, specList) - data.refreshFromOutput(ctx, fillteredList) + diags := data.refreshFromOutput(ctx, fillteredList) + if diags.HasError() { + resp.Diagnostics.AddError("READING ERROR", "refreshFromOutput error") + return + } if !data.OutputFile.IsNull() && data.OutputFile.String() != "" { outputPath := data.OutputFile.ValueString() @@ -172,7 +177,7 @@ func convertSpecToJsonStruct(specs []attr.Value) ([]serverSpecToJsonConvert, err for _, spec := range specs { specJson := serverSpecToJsonConvert{} - if err := json.Unmarshal([]byte(spec.String()), &specJson); err != nil { + if err := json.Unmarshal([]byte(common.ReplaceNull(spec.String())), &specJson); err != nil { return nil, err } serverSpecToConvert = append(serverSpecToConvert, specJson) @@ -222,14 +227,14 @@ type serverSpecToJsonConvert struct { HypervisorType string `json:"hypervisor_type"` GenerationCode string `json:"generation_code"` CpuArchitectureType string `json:"cpu_architecture_type"` - CpuCount int `json:"cpu_count"` - MemorySize int64 `json:"memory_size"` - BlockStorageMaxCount int `json:"block_storage_max_count"` - BlockStorageMaxIops int `json:"block_storage_max_iops"` - BlockStorageMaxThroughput int `json:"block_storage_max_throughput"` - NetworkPerformance int64 `json:"network_performance"` - NetworkInterfaceMaxCount int `json:"network_interface_max_count"` - GpuCount int `json:"gpu_count"` + CpuCount int `json:"cpu_count,omitempty"` + MemorySize int64 `json:"memory_size,omitempty"` + BlockStorageMaxCount int `json:"block_storage_max_count,omitempty"` + BlockStorageMaxIops int `json:"block_storage_max_iops,omitempty"` + BlockStorageMaxThroughput int `json:"block_storage_max_throughput,omitempty"` + NetworkPerformance int64 `json:"network_performance,omitempty"` + NetworkInterfaceMaxCount int `json:"network_interface_max_count,omitempty"` + GpuCount int `json:"gpu_count,omitempty"` Description string `json:"description"` ProductCode string `json:"product_code"` } @@ -253,30 +258,34 @@ func (d serverSpec) attrTypes() map[string]attr.Type { } } -func (d *serverSpecsDataSourceModel) refreshFromOutput(ctx context.Context, list []*serverSpec) { - specListValue, _ := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: serverSpec{}.attrTypes()}, list) +func (d *serverSpecsDataSourceModel) refreshFromOutput(ctx context.Context, list []*serverSpec) diag.Diagnostics { + specListValue, diags := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: serverSpec{}.attrTypes()}, list) + if diags.HasError() { + return diags + } + d.ServerSpecList = specListValue d.ID = types.StringValue("") + + return diags } func (d *serverSpec) refreshFromOutput(output *vserver.ServerSpec) { d.ServerSpecCode = types.StringPointerValue(output.ServerSpecCode) d.GenerationCode = types.StringPointerValue(output.GenerationCode) d.CpuArchitectureType = types.StringPointerValue(output.CpuArchitectureType.Code) - d.CpuCount = common.Int32FromInt32OrDefault(output.CpuCount) - d.MemorySize = common.Int64FromInt64OrDefault(output.MemorySize) - d.BlockStorageMaxCount = common.Int32FromInt32OrDefault(output.BlockStorageMaxCount) - d.BlockStorageMaxIops = common.Int32FromInt32OrDefault(output.BlockStorageMaxIops) - d.BlockStorageMaxThroughput = common.Int32FromInt32OrDefault(output.BlockStorageMaxThroughput) - d.NetworkPerformance = common.Int64FromInt64OrDefault(output.NetworkPerformance) - d.NetworkInterfaceMaxCount = common.Int32FromInt32OrDefault(output.NetworkInterfaceMaxCount) - d.GpuCount = common.Int32FromInt32OrDefault(output.GpuCount) + d.CpuCount = types.Int32PointerValue(output.CpuCount) + d.MemorySize = types.Int64PointerValue(output.MemorySize) + d.BlockStorageMaxCount = types.Int32PointerValue(output.BlockStorageMaxCount) + d.BlockStorageMaxIops = types.Int32PointerValue(output.BlockStorageMaxIops) + d.BlockStorageMaxThroughput = types.Int32PointerValue(output.BlockStorageMaxThroughput) + d.NetworkPerformance = types.Int64PointerValue(output.NetworkPerformance) + d.NetworkInterfaceMaxCount = types.Int32PointerValue(output.NetworkInterfaceMaxCount) + d.GpuCount = types.Int32PointerValue(output.GpuCount) d.Description = types.StringPointerValue(output.ServerSpecDescription) d.ProductCode = types.StringPointerValue(output.ServerProductCode) if output.HypervisorType != nil { d.HypervisorType = types.StringPointerValue(output.HypervisorType.Code) - } else { - d.HypervisorType = types.StringValue("") } } diff --git a/internal/service/server/server_test.go b/internal/service/server/server_test.go index 978ea90ef..ba4fd0d29 100644 --- a/internal/service/server/server_test.go +++ b/internal/service/server/server_test.go @@ -142,7 +142,6 @@ func TestAccResourceNcloudServerImageNumber_vpc_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "description", ""), resource.TestCheckResourceAttr(resourceName, "cpu_count", "2"), resource.TestMatchResourceAttr(resourceName, "instance_no", regexp.MustCompile(`^\d+$`)), - resource.TestCheckResourceAttr(resourceName, "platform_type", "LNX64"), resource.TestCheckResourceAttr(resourceName, "is_protect_server_termination", "false"), resource.TestCheckResourceAttr(resourceName, "login_key_name", fmt.Sprintf("%s-key", testServerName)), // VPC only @@ -425,10 +424,17 @@ resource "ncloud_subnet" "test" { usage_type = "GEN" } +data "ncloud_server_image_numbers" "server_images" { + filter { + name = "name" + values = ["ubuntu-22.04-base"] + } +} + resource "ncloud_server" "server" { subnet_no = ncloud_subnet.test.id name = "%[1]s" - server_image_number = "25495367" + server_image_number = data.ncloud_server_image_numbers.server_images.image_number_list.0.server_image_number server_spec_code = "%[2]s" login_key_name = ncloud_login_key.loginkey.key_name }