diff --git a/linode/instanceconfig/helper.go b/linode/instanceconfig/helper.go index f14131e58..0e671f874 100644 --- a/linode/instanceconfig/helper.go +++ b/linode/instanceconfig/helper.go @@ -96,13 +96,33 @@ func createDevice(deviceMap map[string]any) linodego.InstanceConfigDevice { return device } -func expandDevicesBlock(devicesBlock any) *linodego.InstanceConfigDeviceMap { +func setDeviceMapField(target *linodego.InstanceConfigDeviceMap, deviceKey string, device linodego.InstanceConfigDevice) error { + if target == nil { + return nil + } + + structVal := reflect.ValueOf(target).Elem() + lookupKey := strings.ToUpper(strings.TrimSpace(deviceKey)) + field := structVal.FieldByName(lookupKey) + + if !field.IsValid() { + return fmt.Errorf("unknown device %q (no matching field on InstanceConfigDeviceMap)", deviceKey) + } + if !field.CanSet() { + return fmt.Errorf("cannot set device %q (field exists but is unsettable)", deviceKey) + } + + field.Set(reflect.ValueOf(&device)) + return nil +} + +func expandDevicesBlock(devicesBlock any) (*linodego.InstanceConfigDeviceMap, error) { var result linodego.InstanceConfigDeviceMap devices := devicesBlock.(*schema.Set).List() if len(devices) <= 0 { - return nil + return nil, nil } seenDevices := make(map[string]bool) @@ -118,26 +138,23 @@ func expandDevicesBlock(devicesBlock any) *linodego.InstanceConfigDeviceMap { seenDevices[deviceName.(string)] = true } - field := reflect.Indirect( - reflect.ValueOf(&result), - ).FieldByName( - strings.ToUpper(deviceName.(string)), - ) - - field.Set(reflect.ValueOf(&linodeGoDevice)) + err := setDeviceMapField(&result, deviceName.(string), linodeGoDevice) + if err != nil { + return nil, err + } } } - return &result + return &result, nil } -func expandDevicesNamedBlock(devicesNamedBlock any) *linodego.InstanceConfigDeviceMap { +func expandDevicesNamedBlock(devicesNamedBlock any) (*linodego.InstanceConfigDeviceMap, error) { var result linodego.InstanceConfigDeviceMap deviceMapSlice := devicesNamedBlock.([]any) if len(deviceMapSlice) < 1 { - return nil + return nil, nil } devices := deviceMapSlice[0].(map[string]any) @@ -151,12 +168,13 @@ func expandDevicesNamedBlock(devicesNamedBlock any) *linodego.InstanceConfigDevi currentDevice := currentDeviceSlice[0].(map[string]any) linodeGoDevice := createDevice(currentDevice) - // Get the corresponding struct field and set it to the correct device - field := reflect.Indirect(reflect.ValueOf(&result)).FieldByName(strings.ToUpper(k)) - field.Set(reflect.ValueOf(&linodeGoDevice)) + err := setDeviceMapField(&result, k, linodeGoDevice) + if err != nil { + return nil, err + } } - return &result + return &result, nil } func expandHelpers(helpersRaw any) *linodego.InstanceConfigHelpers { diff --git a/linode/instanceconfig/helper_test.go b/linode/instanceconfig/helper_test.go index bd3880397..26bdaaa89 100644 --- a/linode/instanceconfig/helper_test.go +++ b/linode/instanceconfig/helper_test.go @@ -19,7 +19,10 @@ func TestExpandDevicesNamedBlock(t *testing.T) { }, } - result := expandDevicesNamedBlock(inputValue) + result, err := expandDevicesNamedBlock(inputValue) + if err != nil { + t.Fatalf("failed to expand devices named block: %s", err) + } if result.SDA.DiskID != 12345 { t.Fatal("disk id != 12345") @@ -46,7 +49,10 @@ func TestExpandDevicesBlock(t *testing.T) { return schema.HashString(i.(map[string]any)["device_name"]) }, inputValue) - result := expandDevicesBlock(setValue) + result, err := expandDevicesBlock(setValue) + if err != nil { + t.Fatalf("failed to expand devices block: %s", err) + } if result.SDA.DiskID != 12345 { t.Fatal("disk id != 12345") diff --git a/linode/instanceconfig/resource.go b/linode/instanceconfig/resource.go index 1d9c7b14c..811d5d674 100644 --- a/linode/instanceconfig/resource.go +++ b/linode/instanceconfig/resource.go @@ -154,10 +154,17 @@ func createResource(ctx context.Context, d *schema.ResourceData, meta any) diag. } var devices *linodego.InstanceConfigDeviceMap + var err error if devicesBlock, ok := d.GetOk("device"); ok { - devices = expandDevicesBlock(devicesBlock) + devices, err = expandDevicesBlock(devicesBlock) + if err != nil { + return diag.Errorf("failed to expand devices block: %s", err) + } } else if devicesBlock, ok := d.GetOk("devices"); ok { - devices = expandDevicesNamedBlock(devicesBlock) + devices, err = expandDevicesNamedBlock(devicesBlock) + if err != nil { + return diag.Errorf("failed to expand devices named block: %s", err) + } } if devices != nil { createOpts.Devices = *devices @@ -213,14 +220,20 @@ func updateResource(ctx context.Context, d *schema.ResourceData, meta any) diag. if d.HasChange("device") { if devices, ok := d.GetOk("device"); ok { - putRequest.Devices = expandDevicesBlock(devices) + putRequest.Devices, err = expandDevicesBlock(devices) + if err != nil { + return diag.Errorf("failed to expand devices block: %s", err) + } } shouldUpdate = true } if d.HasChange("devices") { if devices, ok := d.GetOk("devices"); ok { - putRequest.Devices = expandDevicesNamedBlock(devices) + putRequest.Devices, err = expandDevicesNamedBlock(devices) + if err != nil { + return diag.Errorf("failed to expand devices named block: %s", err) + } } shouldUpdate = true }