Skip to content

Commit 977d9bc

Browse files
authored
refactor(instance_server): use instance_block helpers (#2636)
* refactor(instance_server): use instance_block helpers * extract root volume creation * remove unused sanitizeVolumes function * remove update specific code * lint
1 parent 9b621e6 commit 977d9bc

File tree

4 files changed

+74
-117
lines changed

4 files changed

+74
-117
lines changed

internal/services/instance/helpers_instance.go

+42-68
Original file line numberDiff line numberDiff line change
@@ -254,51 +254,6 @@ func validateLocalVolumeSizes(volumes map[string]*instance.VolumeServerTemplate,
254254
return nil
255255
}
256256

257-
// sanitizeVolumeMap removes extra data for API validation.
258-
//
259-
// On the api side, there are two possibles validation schemas for volumes and the validator will be chosen dynamically depending on the passed JSON request
260-
// - With an image (in that case the root volume can be skipped because it is taken from the image)
261-
// - Without an image (in that case, the root volume must be defined)
262-
func sanitizeVolumeMap(volumes map[string]*instance.VolumeServerTemplate) map[string]*instance.VolumeServerTemplate {
263-
m := make(map[string]*instance.VolumeServerTemplate)
264-
265-
for index, v := range volumes {
266-
// Remove extra data for API validation.
267-
switch {
268-
// If a volume already got an ID it is passed as it to the API without specifying the volume type.
269-
// TODO: Fix once instance accept volume type in the schema validation
270-
case v.ID != nil:
271-
if strings.HasPrefix(string(v.VolumeType), "sbs") {
272-
// If volume is from SBS api, the type must be passed
273-
// This rules come from instance API and may not be documented
274-
v = &instance.VolumeServerTemplate{
275-
ID: v.ID,
276-
Boot: v.Boot,
277-
VolumeType: v.VolumeType,
278-
}
279-
} else {
280-
v = &instance.VolumeServerTemplate{
281-
ID: v.ID,
282-
Name: v.Name,
283-
Boot: v.Boot,
284-
}
285-
}
286-
// For the root volume (index 0) if the size is 0, it is considered as a volume created from an image.
287-
// The size is not passed to the API, so it's computed by the API
288-
case index == "0" && v.Size == nil:
289-
v = &instance.VolumeServerTemplate{
290-
VolumeType: v.VolumeType,
291-
Boot: v.Boot,
292-
}
293-
// If none of the above conditions are met, the volume is passed as it to the API
294-
default:
295-
}
296-
m[index] = v
297-
}
298-
299-
return m
300-
}
301-
302257
func preparePrivateNIC(
303258
ctx context.Context, data interface{},
304259
server *instance.Server, vpcAPI *vpc.API,
@@ -535,34 +490,53 @@ func instanceIPHasMigrated(d *schema.ResourceData) bool {
535490
}
536491

537492
func instanceServerAdditionalVolumeTemplate(api *BlockAndInstanceAPI, zone scw.Zone, volumeID string) (*instance.VolumeServerTemplate, error) {
538-
vol, err := api.GetVolume(&instance.GetVolumeRequest{
539-
Zone: zone,
493+
vol, err := api.GetUnknownVolume(&GetUnknownVolumeRequest{
540494
VolumeID: locality.ExpandID(volumeID),
495+
Zone: zone,
541496
})
542-
if err == nil {
543-
return &instance.VolumeServerTemplate{
544-
ID: &vol.Volume.ID,
545-
Name: &vol.Volume.Name,
546-
VolumeType: vol.Volume.VolumeType,
547-
Size: &vol.Volume.Size,
548-
}, nil
549-
}
550-
if !httperrors.Is404(err) {
497+
if err != nil {
551498
return nil, err
552499
}
500+
return vol.VolumeTemplate(), nil
501+
}
502+
503+
func prepareRootVolume(rootVolumeI map[string]any, serverType *instance.ServerType, image string) *UnknownVolume {
504+
serverTypeCanBootOnBlock := serverType.VolumesConstraint.MaxSize == 0
505+
506+
rootVolumeIsBootVolume := types.ExpandBoolPtr(types.GetMapValue[bool](rootVolumeI, "boot"))
507+
rootVolumeType := types.GetMapValue[string](rootVolumeI, "volume_type")
508+
sizeInput := types.GetMapValue[int](rootVolumeI, "size_in_gb")
509+
rootVolumeID := zonal.ExpandID(types.GetMapValue[string](rootVolumeI, "volume_id")).ID
510+
511+
// If the rootVolumeType is not defined, define it depending on the offer
512+
if rootVolumeType == "" {
513+
if serverTypeCanBootOnBlock {
514+
rootVolumeType = instance.VolumeVolumeTypeBSSD.String()
515+
} else {
516+
rootVolumeType = instance.VolumeVolumeTypeLSSD.String()
517+
}
518+
}
553519

554-
blockVol, err := api.blockAPI.GetVolume(&blockSDK.GetVolumeRequest{
555-
Zone: zone,
556-
VolumeID: locality.ExpandID(volumeID),
557-
})
558-
if err == nil {
559-
return &instance.VolumeServerTemplate{
560-
ID: &blockVol.ID,
561-
Name: &blockVol.Name,
562-
VolumeType: "sbs_volume",
563-
Size: &blockVol.Size,
564-
}, nil
520+
rootVolumeName := ""
521+
if image == "" { // When creating an instance from an image, volume should not have a name
522+
rootVolumeName = types.NewRandomName("vol")
565523
}
566524

567-
return nil, err
525+
var rootVolumeSize *scw.Size
526+
if sizeInput == 0 && rootVolumeType == instance.VolumeVolumeTypeLSSD.String() {
527+
// Compute the rootVolumeSize so it will be valid against the local volume constraints
528+
// It wouldn't be valid if another local volume is added, but in this case
529+
// the user would be informed that it does not fulfill the local volume constraints
530+
rootVolumeSize = scw.SizePtr(serverType.VolumesConstraint.MaxSize)
531+
} else if sizeInput > 0 {
532+
rootVolumeSize = scw.SizePtr(scw.Size(uint64(sizeInput) * gb))
533+
}
534+
535+
return &UnknownVolume{
536+
Name: rootVolumeName,
537+
ID: rootVolumeID,
538+
InstanceVolumeType: instance.VolumeVolumeType(rootVolumeType),
539+
Size: rootVolumeSize,
540+
Boot: rootVolumeIsBootVolume,
541+
}
568542
}

internal/services/instance/helpers_instance_block.go

+18-9
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,33 @@ type UnknownVolume struct {
2525
Zone scw.Zone
2626
ID string
2727
Name string
28-
Size scw.Size
28+
Size *scw.Size
2929
ServerID *string
30+
Boot *bool
3031

3132
// IsBlockVolume is true if volume is managed by block API
3233
IsBlockVolume bool
3334

3435
InstanceVolumeType instance.VolumeVolumeType
3536
}
3637

37-
// VolumeTemplateUpdate return a VolumeServerTemplate for an UpdateServer request
38-
func (volume *UnknownVolume) VolumeTemplateUpdate() *instance.VolumeServerTemplate {
39-
template := &instance.VolumeServerTemplate{
40-
ID: scw.StringPtr(volume.ID),
41-
Name: &volume.Name, // name is ignored by the API, any name will work here
38+
// VolumeTemplate returns a template to be used for servers requests.
39+
func (volume *UnknownVolume) VolumeTemplate() *instance.VolumeServerTemplate {
40+
template := &instance.VolumeServerTemplate{}
41+
if volume.ID != "" {
42+
template.ID = &volume.ID
43+
} else {
44+
template.Size = volume.Size
4245
}
46+
47+
if volume.Boot != nil {
48+
template.Boot = volume.Boot
49+
}
50+
4351
if volume.IsBlockVolume {
44-
template.Name = nil
4552
template.VolumeType = volume.InstanceVolumeType
53+
} else {
54+
template.Name = &volume.Name
4655
}
4756

4857
return template
@@ -73,7 +82,7 @@ func (api *BlockAndInstanceAPI) GetUnknownVolume(req *GetUnknownVolumeRequest, o
7382
Zone: getVolumeResponse.Volume.Zone,
7483
ID: getVolumeResponse.Volume.ID,
7584
Name: getVolumeResponse.Volume.Name,
76-
Size: getVolumeResponse.Volume.Size,
85+
Size: &getVolumeResponse.Volume.Size,
7786
IsBlockVolume: false,
7887
InstanceVolumeType: getVolumeResponse.Volume.VolumeType,
7988
}
@@ -96,7 +105,7 @@ func (api *BlockAndInstanceAPI) GetUnknownVolume(req *GetUnknownVolumeRequest, o
96105
Zone: blockVolume.Zone,
97106
ID: blockVolume.ID,
98107
Name: blockVolume.Name,
99-
Size: blockVolume.Size,
108+
Size: &blockVolume.Size,
100109
IsBlockVolume: true,
101110
InstanceVolumeType: instance.VolumeVolumeTypeSbsVolume,
102111
}

internal/services/instance/server.go

+3-40
Original file line numberDiff line numberDiff line change
@@ -430,43 +430,9 @@ func ResourceInstanceServerCreate(ctx context.Context, d *schema.ResourceData, m
430430
}
431431

432432
req.Volumes = make(map[string]*instanceSDK.VolumeServerTemplate)
433-
serverTypeCanBootOnBlock := serverType.VolumesConstraint.MaxSize == 0
434-
rootVolumeIsBootVolume := types.ExpandBoolPtr(d.Get("root_volume.0.boot"))
435-
rootVolumeType := d.Get("root_volume.0.volume_type").(string)
436-
sizeInput := d.Get("root_volume.0.size_in_gb").(int)
437-
rootVolumeID := zonal.ExpandID(d.Get("root_volume.0.volume_id").(string)).ID
438-
439-
// If the rootVolumeType is not defined, define it depending on the offer
440-
if rootVolumeType == "" {
441-
if serverTypeCanBootOnBlock {
442-
rootVolumeType = instanceSDK.VolumeVolumeTypeBSSD.String()
443-
} else {
444-
rootVolumeType = instanceSDK.VolumeVolumeTypeLSSD.String()
445-
}
446-
}
447-
448-
rootVolumeName := ""
449-
if req.Image == "" { // When creating an instanceSDK from an image, volume should not have a name
450-
rootVolumeName = types.NewRandomName("vol")
451-
}
433+
rootVolume := d.Get("root_volume.0").(map[string]any)
452434

453-
var rootVolumeSize *scw.Size
454-
if sizeInput == 0 && rootVolumeType == instanceSDK.VolumeVolumeTypeLSSD.String() {
455-
// Compute the rootVolumeSize so it will be valid against the local volume constraints
456-
// It wouldn't be valid if another local volume is added, but in this case
457-
// the user would be informed that it does not fulfill the local volume constraints
458-
rootVolumeSize = scw.SizePtr(serverType.VolumesConstraint.MaxSize)
459-
} else if sizeInput > 0 {
460-
rootVolumeSize = scw.SizePtr(scw.Size(uint64(sizeInput) * gb))
461-
}
462-
463-
req.Volumes["0"] = &instanceSDK.VolumeServerTemplate{
464-
Name: types.ExpandStringPtr(rootVolumeName),
465-
ID: types.ExpandStringPtr(rootVolumeID),
466-
VolumeType: instanceSDK.VolumeVolumeType(rootVolumeType),
467-
Size: rootVolumeSize,
468-
Boot: rootVolumeIsBootVolume,
469-
}
435+
req.Volumes["0"] = prepareRootVolume(rootVolume, serverType, req.Image).VolumeTemplate()
470436
if raw, ok := d.GetOk("additional_volume_ids"); ok {
471437
for i, volumeID := range raw.([]interface{}) {
472438
// We have to get the volume to know whether it is a local or a block volume
@@ -483,9 +449,6 @@ func ResourceInstanceServerCreate(ctx context.Context, d *schema.ResourceData, m
483449
return diag.FromErr(err)
484450
}
485451

486-
// Sanitize the volume map to respect API schemas
487-
req.Volumes = sanitizeVolumeMap(req.Volumes)
488-
489452
res, err := api.CreateServer(req, scw.WithContext(ctx))
490453
if err != nil {
491454
return diag.FromErr(err)
@@ -837,7 +800,7 @@ func ResourceInstanceServerUpdate(ctx context.Context, d *schema.ResourceData, m
837800
if volumeHasChange && !isStopped && volume.IsLocal() && volume.IsAttached() {
838801
return diag.FromErr(errors.New("instanceSDK must be stopped to change local volumes"))
839802
}
840-
volumes[strconv.Itoa(i+1)] = volume.VolumeTemplateUpdate()
803+
volumes[strconv.Itoa(i+1)] = volume.VolumeTemplate()
841804
}
842805

843806
serverShouldUpdate = true

internal/types/map.go

+11
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,14 @@ func ExpandMapStringString(data any) map[string]string {
5858
}
5959
return m
6060
}
61+
62+
// GetMapValue returns the value for a key from a map.
63+
// returns zero value if key does not exist in map.
64+
func GetMapValue[T any](m map[string]any, key string) T { //nolint: ireturn
65+
var val T
66+
valI, exists := m[key]
67+
if exists {
68+
val = valI.(T)
69+
}
70+
return val
71+
}

0 commit comments

Comments
 (0)