|
7 | 7 | "sort"
|
8 | 8 | "time"
|
9 | 9 |
|
| 10 | + "github.com/dustin/go-humanize" |
10 | 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
11 | 12 | "github.com/scaleway/scaleway-sdk-go/api/instance/v1"
|
12 | 13 | "github.com/scaleway/scaleway-sdk-go/scw"
|
@@ -159,3 +160,82 @@ func reachState(ctx context.Context, instanceAPI *instance.API, zone scw.Zone, s
|
159 | 160 | }
|
160 | 161 | return nil
|
161 | 162 | }
|
| 163 | + |
| 164 | +// getServerType is a util to get a instance.ServerType by its commercialType |
| 165 | +func getServerType(apiInstance *instance.API, zone scw.Zone, commercialType string) *instance.ServerType { |
| 166 | + serverType := (*instance.ServerType)(nil) |
| 167 | + |
| 168 | + serverTypesRes, err := apiInstance.ListServersTypes(&instance.ListServersTypesRequest{ |
| 169 | + Zone: zone, |
| 170 | + }) |
| 171 | + if err != nil { |
| 172 | + l.Warningf("cannot get server types: %s", err) |
| 173 | + } else { |
| 174 | + serverType = serverTypesRes.Servers[commercialType] |
| 175 | + if serverType == nil { |
| 176 | + l.Warningf("unrecognized server type: %s", commercialType) |
| 177 | + } |
| 178 | + } |
| 179 | + |
| 180 | + return serverType |
| 181 | +} |
| 182 | + |
| 183 | +// validateLocalVolumeSizes validates the total size of local volumes. |
| 184 | +func validateLocalVolumeSizes(volumes map[string]*instance.VolumeTemplate, serverType *instance.ServerType, commercialType string) error { |
| 185 | + // Calculate local volume total size. |
| 186 | + var localVolumeTotalSize scw.Size |
| 187 | + for _, volume := range volumes { |
| 188 | + if volume.VolumeType == instance.VolumeVolumeTypeLSSD { |
| 189 | + localVolumeTotalSize += volume.Size |
| 190 | + } |
| 191 | + } |
| 192 | + |
| 193 | + volumeConstraint := serverType.VolumesConstraint |
| 194 | + |
| 195 | + // If no root volume provided, count the default root volume size added by the API. |
| 196 | + if rootVolume := volumes["0"]; rootVolume == nil { |
| 197 | + localVolumeTotalSize += volumeConstraint.MinSize |
| 198 | + } |
| 199 | + |
| 200 | + if localVolumeTotalSize < volumeConstraint.MinSize || localVolumeTotalSize > volumeConstraint.MaxSize { |
| 201 | + min := humanize.Bytes(uint64(volumeConstraint.MinSize)) |
| 202 | + if volumeConstraint.MinSize == volumeConstraint.MaxSize { |
| 203 | + return fmt.Errorf("%s total local volume size must be equal to %s", commercialType, min) |
| 204 | + } |
| 205 | + |
| 206 | + max := humanize.Bytes(uint64(volumeConstraint.MaxSize)) |
| 207 | + return fmt.Errorf("%s total local volume size must be between %s and %s", commercialType, min, max) |
| 208 | + } |
| 209 | + |
| 210 | + return nil |
| 211 | +} |
| 212 | + |
| 213 | +// sanitizeVolumeMap removes extra data for API validation. |
| 214 | +// |
| 215 | +// 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 |
| 216 | +// - With an image (in that case the root volume can be skipped because it is taken from the image) |
| 217 | +// - Without an image (in that case, the root volume must be defined) |
| 218 | +func sanitizeVolumeMap(serverName string, volumes map[string]*instance.VolumeTemplate) map[string]*instance.VolumeTemplate { |
| 219 | + m := make(map[string]*instance.VolumeTemplate) |
| 220 | + |
| 221 | + for index, v := range volumes { |
| 222 | + v.Name = serverName + "-" + index |
| 223 | + |
| 224 | + // Remove extra data for API validation. |
| 225 | + switch { |
| 226 | + // If a volume already got an ID it is passed as it to the API without specifying the volume type. |
| 227 | + // TODO: Fix once instance accept volume type in the schema validation |
| 228 | + case v.ID != "": |
| 229 | + v = &instance.VolumeTemplate{ID: v.ID, Name: v.Name} |
| 230 | + // For the root volume (index 0) if the specified size is not 0 it is considered as a new volume |
| 231 | + // It does not have yet a volume ID, it is passed to the API with only the size to be dynamically created by the API |
| 232 | + case index == "0" && v.Size != 0: |
| 233 | + v = &instance.VolumeTemplate{Size: v.Size} |
| 234 | + // If none of the above conditions are met, the volume is passed as it to the API |
| 235 | + default: |
| 236 | + } |
| 237 | + m[index] = v |
| 238 | + } |
| 239 | + |
| 240 | + return m |
| 241 | +} |
0 commit comments