Skip to content

Fix: boot device selection delete error when using volumes #856

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
## 6.7.12
### Changes
- No longer add `boot_volume` in the `inline_volume_ids` list on server and vcpu_server imports as it generated errors when using `ionoscloud_server_boot_device_selection` with a server with on boot device
### Fixes
- Fix `ionoscloud_server_boot_device_selection` delete error when servers have no inital boot device and a cdrom is used.
### Documentation
- Update Kafka documentation to state that version 3.9.0 is supported in all relevant resources and data sources.

Expand Down
2 changes: 1 addition & 1 deletion docs/resources/server_boot_device_selection.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Manages the selection of a boot device for IonosCloud Servers.
## Example Usage

The boot device of a `ionoscloud_server`, `ionoscloud_vcpu_server` or `ionoscloud_cube_server` can be selected with this resource.
Deleting this resource will revert the boot device back to the default volume, which is the first inline volume created together with the server.
Deleting this resource will revert the boot device back to the default volume, which is the first inline volume created together with the server. In case in which there is no default to revert to, when the server had no boot device, the current device will remain set as a boot device when this resource is deleted.
This resource also allows switching between a `volume` and a `ionoscloud_image` CDROM. Note that CDROM images are detached after they are no longer set as boot devices.

### Select an external volume
Expand Down
4 changes: 2 additions & 2 deletions ionoscloud/import_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func TestAccServerImportBasic(t *testing.T) {
ImportStateIdFunc: testAccServerImportStateIdWithNicAndFw,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"image_password", "ssh_key_path.#", "image_name", "volume.0.user_data", "volume.0.backup_unit_id", "firewallrule_id", "primary_nic", "inline_volume_ids", "allow_replace"},
ImportStateVerifyIgnore: []string{"image_password", "ssh_key_path.#", "image_name", "volume", "firewallrule_id", "primary_nic", "inline_volume_ids", "allow_replace"},
},
},
})
Expand All @@ -50,7 +50,7 @@ func TestAccServerWithLabelsImport(t *testing.T) {
ImportStateIdFunc: testAccServerImportStateId,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"image_password", "ssh_key_path.#", "image_name", "volume.0.user_data", "volume.0.backup_unit_id", "firewallrule_id", "primary_nic", "inline_volume_ids", "primary_ip", "allow_replace"},
ImportStateVerifyIgnore: []string{"image_password", "ssh_key_path.#", "image_name", "volume", "firewallrule_id", "primary_nic", "inline_volume_ids", "primary_ip", "allow_replace"},
},
},
})
Expand Down
4 changes: 2 additions & 2 deletions ionoscloud/import_server_vcpu_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func TestAccServerVCPUImportBasic(t *testing.T) {
ImportStateIdFunc: testAccServerVCPUImportStateIdWithNicAndFw,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"image_password", "ssh_key_path.#", "image_name", "volume.0.user_data", "volume.0.backup_unit_id", "firewallrule_id", "primary_nic", "inline_volume_ids"},
ImportStateVerifyIgnore: []string{"image_password", "ssh_key_path.#", "image_name", "volume", "firewallrule_id", "primary_nic", "inline_volume_ids"},
},
},
})
Expand All @@ -50,7 +50,7 @@ func TestAccServerVCPUWithLabelsImport(t *testing.T) {
ImportStateIdFunc: testAccServerVCPUImportStateId,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"image_password", "ssh_key_path.#", "image_name", "volume.0.user_data", "volume.0.backup_unit_id", "firewallrule_id", "primary_nic", "inline_volume_ids", "primary_ip"},
ImportStateVerifyIgnore: []string{"image_password", "ssh_key_path.#", "image_name", "volume", "firewallrule_id", "primary_nic", "inline_volume_ids", "primary_ip"},
},
},
})
Expand Down
13 changes: 0 additions & 13 deletions ionoscloud/resource_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1491,19 +1491,6 @@ func setResourceServerData(ctx context.Context, client *ionoscloud.APIClient, d
return err
}

// takes care of an upgrade from a version that does not have inline_volume_ids(pre 6.4.0)
// to one that has it(>6.4.0)
if _, ok := d.GetOk("inline_volume_ids"); !ok {
if bootVolumeItf, ok := d.GetOk("boot_volume"); ok {
bootVolume := bootVolumeItf.(string)
var inlineVolumeIds []string
inlineVolumeIds = append(inlineVolumeIds, bootVolume)
if err := d.Set("inline_volume_ids", inlineVolumeIds); err != nil {
return utils.GenerateSetError("server", "inline_volume_ids", err)
}
}
}

datacenterId := d.Get("datacenter_id").(string)
if server.Properties != nil {
if server.Properties.Name != nil {
Expand Down
64 changes: 36 additions & 28 deletions services/cloudapi/cloudapiserver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,42 +217,47 @@ func (ss *Service) UpdateBootDevice(ctx context.Context, datacenterID, serverID,
}

newBdType := constant.BootDeviceTypeCDROM
_, apiResponse, err := ss.Client.ImagesApi.ImagesFindById(ctx, newBootDeviceID).Execute()
if err != nil {
if !apiResponse.HttpNotFound() {
return err

if newBootDeviceID != "" {
_, apiResponse, err := ss.Client.ImagesApi.ImagesFindById(ctx, newBootDeviceID).Execute()
if err != nil {
if !apiResponse.HttpNotFound() {
return err
}
log.Printf("[DEBUG] no bootable image found with id : %s", newBootDeviceID)
newBdType = constant.BootDeviceTypeVolume
}
log.Printf("[DEBUG] no bootable image found with id : %s", newBootDeviceID)
newBdType = constant.BootDeviceTypeVolume
}

switch oldBdType {
case constant.BootDeviceTypeCDROM:
if strings.EqualFold(newBdType, constant.BootDeviceTypeVolume) {
// update to new boot volume
sp := ionoscloud.ServerProperties{BootVolume: ionoscloud.NewResourceReference(newBootDeviceID)}
if _, _, err = ss.Update(ctx, datacenterID, serverID, sp); err != nil {
return err
}
} else {
// attach new cdrom
img := ionoscloud.Image{Id: &newBootDeviceID}
_, apiResponse, err := ss.Client.ServersApi.DatacentersServersCdromsPost(ctx, datacenterID, serverID).Cdrom(img).Execute()
if err != nil {
return err
}
if errState := bundleclient.WaitForStateChange(ctx, ss.Meta, ss.D, apiResponse, schema.TimeoutUpdate); errState != nil {
return errState
}
log.Printf("[DEBUG] attached CDROM image to server: serverId: %s, imageId: %s", serverID, newBootDeviceID)
// update to new boot cdrom
sp := ionoscloud.ServerProperties{BootCdrom: ionoscloud.NewResourceReference(newBootDeviceID)}
if _, _, err = ss.Update(ctx, datacenterID, serverID, sp); err != nil {
return err
if newBootDeviceID != "" {
if strings.EqualFold(newBdType, constant.BootDeviceTypeVolume) {
// update to new boot volume
sp := ionoscloud.ServerProperties{BootVolume: ionoscloud.NewResourceReference(newBootDeviceID)}
if _, _, err = ss.Update(ctx, datacenterID, serverID, sp); err != nil {
return err
}
} else {
// attach new cdrom
img := ionoscloud.Image{Id: &newBootDeviceID}
_, apiResponse, err := ss.Client.ServersApi.DatacentersServersCdromsPost(ctx, datacenterID, serverID).Cdrom(img).Execute()
if err != nil {
return err
}
if errState := bundleclient.WaitForStateChange(ctx, ss.Meta, ss.D, apiResponse, schema.TimeoutUpdate); errState != nil {
return errState
}
log.Printf("[DEBUG] attached CDROM image to server: serverId: %s, imageId: %s", serverID, newBootDeviceID)
// update to new boot cdrom
sp := ionoscloud.ServerProperties{BootCdrom: ionoscloud.NewResourceReference(newBootDeviceID)}
if _, _, err = ss.Update(ctx, datacenterID, serverID, sp); err != nil {
return err
}
}
}
// detach old cdrom
apiResponse, err = ss.Client.ServersApi.DatacentersServersCdromsDelete(ctx, datacenterID, serverID, oldBootDeviceID).Execute()
apiResponse, err := ss.Client.ServersApi.DatacentersServersCdromsDelete(ctx, datacenterID, serverID, oldBootDeviceID).Execute()
if err != nil {
return err
}
Expand All @@ -263,6 +268,9 @@ func (ss *Service) UpdateBootDevice(ctx context.Context, datacenterID, serverID,

case constant.BootDeviceTypeVolume:
// no cdrom is detached, only update to the new boot device, regardless of type
if newBootDeviceID == "" {
return nil
}
sp := ionoscloud.ServerProperties{BootVolume: ionoscloud.NewResourceReference(newBootDeviceID)}
if strings.EqualFold(newBdType, constant.BootDeviceTypeCDROM) {
sp = ionoscloud.ServerProperties{BootCdrom: ionoscloud.NewResourceReference(newBootDeviceID)}
Expand Down
Loading