Skip to content

Commit 7b15365

Browse files
authored
feat(BREV-2083): Split Byte Sizes into Value and Unit (#55)
* feat(BREV-2083): Capture both byte value and unit in size * implement memory * lint * include gpu memory * include gpu memory * check for overflow * lint * lint * createinstanceattrs * disk * interface * no pointers to interfaces * remove interface, move to int64 * use bytes moniker
1 parent fc8a5d1 commit 7b15365

File tree

11 files changed

+126
-37
lines changed

11 files changed

+126
-37
lines changed

v1/bytes.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package v1
2+
3+
var zeroBytes = Bytes{value: 0, unit: Byte}
4+
5+
// NewBytes creates a new Bytes with the given value and unit
6+
func NewBytes(value BytesValue, unit BytesUnit) Bytes {
7+
if value < 0 {
8+
return zeroBytes
9+
}
10+
return Bytes{
11+
value: value,
12+
unit: unit,
13+
}
14+
}
15+
16+
type (
17+
BytesValue int64
18+
BytesUnit string
19+
)
20+
21+
// Bytes represents a number of some unit of bytes
22+
type Bytes struct {
23+
value BytesValue
24+
unit BytesUnit
25+
}
26+
27+
// Value is the whole non-negative number of bytes of the specified unit
28+
func (b Bytes) Value() BytesValue {
29+
return b.value
30+
}
31+
32+
// Unit is the unit of the byte value
33+
func (b Bytes) Unit() BytesUnit {
34+
return b.unit
35+
}
36+
37+
// ByteUnit is a unit of measurement for bytes
38+
const (
39+
Byte BytesUnit = "B"
40+
41+
// Base 10
42+
Kilobyte BytesUnit = "KB"
43+
Megabyte BytesUnit = "MB"
44+
Gigabyte BytesUnit = "GB"
45+
Terabyte BytesUnit = "TB"
46+
Petabyte BytesUnit = "PB"
47+
Exabyte BytesUnit = "EB"
48+
49+
// Base 2
50+
Kibibyte BytesUnit = "KiB"
51+
Mebibyte BytesUnit = "MiB"
52+
Gibibyte BytesUnit = "GiB"
53+
Tebibyte BytesUnit = "TiB"
54+
Pebibyte BytesUnit = "PiB"
55+
Exbibyte BytesUnit = "EiB"
56+
)

v1/instance.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,8 @@ type Instance struct {
195195
Hostname string
196196
ImageID string
197197
InstanceType string
198-
DiskSize units.Base2Bytes
198+
DiskSize units.Base2Bytes // TODO: deprecate in favor of DiskSizeByteValue
199+
DiskSizeBytes Bytes
199200
VolumeType string
200201
PubKeyFingerprint string
201202
SSHUser string
@@ -274,7 +275,8 @@ type CreateInstanceAttrs struct {
274275
ImageID string
275276
InstanceType string
276277
UserDataBase64 string
277-
DiskSize units.Base2Bytes
278+
DiskSize units.Base2Bytes // TODO: deprecate in favor of DiskSizeByteValue
279+
DiskSizeBytes Bytes
278280
Tags Tags
279281
FirewallRules FirewallRules
280282
UseSpot bool

v1/instancetype.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ type InstanceType struct {
6464
SupportedStorage []Storage
6565
ElasticRootVolume bool
6666
SupportedUsageClasses []string
67-
Memory units.Base2Bytes
67+
Memory units.Base2Bytes // TODO: deprecate in favor of MemoryByteValue
68+
MemoryBytes Bytes
6869
MaximumNetworkInterfaces int32
6970
NetworkPerformance string
7071
SupportedNumCores []int32
@@ -114,7 +115,8 @@ func MakeGenericInstanceTypeIDFromInstance(instance Instance) InstanceTypeID {
114115

115116
type GPU struct {
116117
Count int32
117-
Memory units.Base2Bytes
118+
Memory units.Base2Bytes // TODO: deprecate in favor of MemoryByteValue
119+
MemoryBytes Bytes
118120
MemoryDetails string // "", "HBM", "GDDR", "DDR", etc.
119121
NetworkDetails string // "PCIe", "SXM4", "SXM5", etc.
120122
Manufacturer Manufacturer
@@ -378,7 +380,7 @@ func normalizeInstanceTypes(types []InstanceType) []InstanceType {
378380

379381
// ValidateStableInstanceTypeIDs validates that the provided stable instance type IDs are valid and stable
380382
// This function ensures that stable IDs exist in the current instance types and have required properties
381-
func ValidateStableInstanceTypeIDs(ctx context.Context, client CloudInstanceType, stableIDs []InstanceTypeID) error {
383+
func ValidateStableInstanceTypeIDs(ctx context.Context, client CloudInstanceType, stableIDs []InstanceTypeID) error { //nolint:gocyclo // test
382384
// Get all instance types
383385
allTypes, err := client.GetInstanceTypes(ctx, GetInstanceTypeArgs{})
384386
if err != nil {
@@ -427,7 +429,7 @@ func ValidateStableInstanceTypeIDs(ctx context.Context, client CloudInstanceType
427429

428430
// Check that supported storage has price information
429431
for i, storage := range instanceType.SupportedStorage {
430-
if storage.MinSize != nil {
432+
if storage.MinSize != nil || storage.MinSizeBytes != nil {
431433
if storage.PricePerGBHr == nil {
432434
return fmt.Errorf("instance type %s should have storage %d price", instanceType.ID, i)
433435
}

v1/providers/fluidstack/instancetype.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99

1010
"github.com/alecthomas/units"
1111
"github.com/bojanz/currency"
12-
"github.com/brevdev/cloud/v1"
12+
v1 "github.com/brevdev/cloud/v1"
1313
openapi "github.com/brevdev/cloud/v1/providers/fluidstack/gen/fluidstack"
1414
)
1515

@@ -82,12 +82,14 @@ func convertFluidStackInstanceTypeToV1InstanceType(location string, fsInstanceTy
8282
}
8383
}
8484

85+
var memoryBytes v1.Bytes
8586
var ram units.Base2Bytes
8687
if fsInstanceType.Memory != "" {
8788
memoryStr := strings.TrimSuffix(fsInstanceType.Memory, "GB")
8889
memoryStr = strings.TrimSpace(memoryStr)
8990
if memoryGB, err := strconv.ParseFloat(memoryStr, 64); err == nil {
9091
ram = units.Base2Bytes(memoryGB) * units.Gibibyte
92+
memoryBytes = v1.NewBytes(v1.BytesValue(memoryGB), v1.Gigabyte)
9193
}
9294
}
9395

@@ -102,6 +104,7 @@ func convertFluidStackInstanceTypeToV1InstanceType(location string, fsInstanceTy
102104
Type: fsInstanceType.Name,
103105
VCPU: vcpus,
104106
Memory: ram,
107+
MemoryBytes: memoryBytes,
105108
SupportedGPUs: gpus,
106109
BasePrice: &price,
107110
IsAvailable: isAvailable,

v1/providers/lambdalabs/instance.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,10 @@ func convertLambdaLabsInstanceToV1Instance(instance openapi.Instance) *v1.Instan
177177
Status: v1.Status{
178178
LifecycleStatus: convertLambdaLabsStatusToV1Status(instance.Status),
179179
},
180-
InstanceType: instance.InstanceType.Name,
181-
VolumeType: "ssd",
182-
DiskSize: units.GiB * units.Base2Bytes(instance.InstanceType.Specs.StorageGib),
180+
InstanceType: instance.InstanceType.Name,
181+
VolumeType: "ssd",
182+
DiskSize: units.GiB * units.Base2Bytes(instance.InstanceType.Specs.StorageGib),
183+
DiskSizeBytes: v1.NewBytes(v1.BytesValue(instance.InstanceType.Specs.StorageGib), v1.Gibibyte),
183184
FirewallRules: v1.FirewallRules{
184185
IngressRules: []v1.FirewallRule{generateFirewallRouteFromPort(22), generateFirewallRouteFromPort(2222)}, // TODO pull from api
185186
EgressRules: []v1.FirewallRule{generateFirewallRouteFromPort(22), generateFirewallRouteFromPort(2222)}, // TODO pull from api

v1/providers/lambdalabs/instancetype.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package v1
33
import (
44
"context"
55
"fmt"
6+
"math"
67
"regexp"
78
"strconv"
89
"strings"
@@ -127,7 +128,11 @@ func parseGPUFromDescription(input string) (v1.GPU, error) {
127128
}
128129
memoryStr := memoryMatch[1]
129130
memoryGiB, _ := strconv.Atoi(memoryStr)
131+
if memoryGiB > math.MaxInt32 {
132+
memoryGiB = math.MaxInt32
133+
}
130134
gpu.Memory = units.GiB * units.Base2Bytes(memoryGiB)
135+
gpu.MemoryBytes = v1.NewBytes(v1.BytesValue(memoryGiB), v1.Gibibyte)
131136

132137
// Extract the network details
133138
networkRegex := regexp.MustCompile(`(\w+\s?)+\)`)
@@ -172,19 +177,22 @@ func convertLambdaLabsInstanceTypeToV1InstanceType(location string, instType ope
172177
if err != nil {
173178
return v1.InstanceType{}, err
174179
}
180+
175181
it := v1.InstanceType{
176182
Location: location,
177183
Type: instType.Name,
178184
SupportedGPUs: gpus,
179185
SupportedStorage: []v1.Storage{
180186
{
181-
Type: "ssd",
182-
Count: 1,
183-
Size: units.GiB * units.Base2Bytes(instType.Specs.StorageGib),
187+
Type: "ssd",
188+
Count: 1,
189+
Size: units.GiB * units.Base2Bytes(instType.Specs.StorageGib),
190+
SizeBytes: v1.NewBytes(v1.BytesValue(instType.Specs.StorageGib), v1.Gibibyte),
184191
},
185192
},
186193
SupportedUsageClasses: []string{"on-demand"},
187194
Memory: units.GiB * units.Base2Bytes(instType.Specs.MemoryGib),
195+
MemoryBytes: v1.NewBytes(v1.BytesValue(instType.Specs.MemoryGib), v1.Gibibyte),
188196
MaximumNetworkInterfaces: 0,
189197
NetworkPerformance: "",
190198
SupportedNumCores: []int32{},

v1/providers/launchpad/instance_get.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,18 @@ func launchpadDeploymentToInstance(deployment *openapi.Deployment) (v1.Instance,
4444
return v1.Instance{}, errors.WrapAndTrace(err)
4545
}
4646

47-
var diskSize units.Base2Bytes
47+
var totalStorageSize int32
4848
nodes := deployment.GetCluster().Cluster.GetNodes()
4949
if len(nodes) == 0 {
50-
diskSize = 0
50+
totalStorageSize = 0
5151
} else {
5252
node := nodes[0]
5353

5454
// Calculate disk size
5555
storage := node.Node.GetStorage()
56-
size := 0
5756
for _, s := range storage {
58-
size += int(s.GetSize())
57+
totalStorageSize += s.GetSize()
5958
}
60-
diskSize = units.Base2Bytes(size) * units.GiB
6159
}
6260

6361
inst := v1.Instance{
@@ -78,10 +76,11 @@ func launchpadDeploymentToInstance(deployment *openapi.Deployment) (v1.Instance,
7876
ToPort: 2022,
7977
},
8078
},
81-
DiskSize: diskSize,
82-
Location: deployment.GetRegion(),
83-
PublicDNS: deployment.GetCluster().Cluster.GetPublicAddress(),
84-
PublicIP: deployment.GetCluster().Cluster.GetPublicAddress(),
79+
DiskSize: units.Base2Bytes(totalStorageSize) * units.GiB,
80+
DiskSizeBytes: v1.NewBytes(v1.BytesValue(totalStorageSize), v1.Gigabyte),
81+
Location: deployment.GetRegion(),
82+
PublicDNS: deployment.GetCluster().Cluster.GetPublicAddress(),
83+
PublicIP: deployment.GetCluster().Cluster.GetPublicAddress(),
8584
}
8685

8786
cluster := deployment.GetCluster().Cluster

v1/providers/launchpad/instancetype.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ func launchpadInstanceTypeToInstanceType(launchpadInstanceType openapi.InstanceT
204204
Type: typeName,
205205
VCPU: launchpadInstanceType.Cpu,
206206
Memory: gbToBytes(launchpadInstanceType.MemoryGb),
207+
MemoryBytes: v1.NewBytes(v1.BytesValue(launchpadInstanceType.MemoryGb), v1.Gigabyte),
207208
SupportedGPUs: []v1.GPU{gpu},
208209
SupportedStorage: storage,
209210
SupportedArchitectures: []v1.Architecture{launchpadArchitectureToArchitecture(launchpadInstanceType.SystemArch)},
@@ -242,9 +243,10 @@ func launchpadStorageToStorages(launchpadStorage []openapi.InstanceTypeStorage)
242243
storage := make([]v1.Storage, len(launchpadStorage))
243244
for i, s := range launchpadStorage {
244245
storage[i] = v1.Storage{
245-
Count: 1,
246-
Size: gbToBytes(s.SizeGb),
247-
Type: string(s.Type),
246+
Count: 1,
247+
Size: gbToBytes(s.SizeGb),
248+
SizeBytes: v1.NewBytes(v1.BytesValue(s.SizeGb), v1.Gigabyte),
249+
Type: string(s.Type),
248250
}
249251
}
250252
return storage
@@ -261,6 +263,7 @@ func launchpadGpusToGpus(lpGpus []openapi.InstanceTypeGpu) []v1.GPU {
261263
Manufacturer: v1.GetManufacturer(gp.Manufacturer),
262264
Count: gp.Count,
263265
Memory: gbToBytes(gp.MemoryGb),
266+
MemoryBytes: v1.NewBytes(v1.BytesValue(int64(gp.MemoryGb)), v1.Gigabyte),
264267
NetworkDetails: string(gp.InterconnectionType),
265268
Type: strings.ToUpper(gp.Model),
266269
}
@@ -307,8 +310,10 @@ func launchpadClusterToInstanceType(cluster openapi.Cluster) *v1.InstanceType {
307310
vcpu = *node.Cpu
308311
}
309312
var memory units.Base2Bytes
313+
var memoryBytes v1.Bytes
310314
if node.Memory != nil {
311315
memory = gbToBytes(*node.Memory)
316+
memoryBytes = v1.NewBytes(v1.BytesValue(*node.Memory), v1.Gigabyte)
312317
}
313318

314319
isAvailable := (cluster.ProvisioningState != nil && *cluster.ProvisioningState == openapi.ProvisioningStateReady)
@@ -328,6 +333,7 @@ func launchpadClusterToInstanceType(cluster openapi.Cluster) *v1.InstanceType {
328333
SupportedGPUs: []v1.GPU{*gpu},
329334
SupportedStorage: storage,
330335
Memory: memory,
336+
MemoryBytes: memoryBytes,
331337
VCPU: vcpu,
332338
IsAvailable: isAvailable,
333339
Location: location,
@@ -359,15 +365,18 @@ func launchpadGputoGpu(node openapi.Node) *v1.GPU {
359365
}
360366

361367
var lpGpuMemory units.Base2Bytes
368+
var lpGpuMemoryBytes v1.Bytes
362369
if lpGpu.Memory != nil {
363370
lpGpuMemory = gbToBytes(*lpGpu.Memory)
371+
lpGpuMemoryBytes = v1.NewBytes(v1.BytesValue(*lpGpu.Memory), v1.Gigabyte)
364372
}
365373

366374
gpu := &v1.GPU{
367375
Name: lpGpuModel,
368376
Count: lpGpuCount,
369377
NetworkDetails: lpGpuFormFactor,
370378
Memory: lpGpuMemory,
379+
MemoryBytes: lpGpuMemoryBytes,
371380
Manufacturer: "NVIDIA", // The only supported manufacturer for Launchpad
372381
}
373382
return gpu

v1/providers/shadeform/instance.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ func (c *ShadeformClient) convertInstanceInfoResponseToV1Instance(ctx context.Co
295295
InstanceType: instanceType,
296296
InstanceTypeID: v1.InstanceTypeID(c.getInstanceTypeID(instanceType, instanceInfo.Region)),
297297
DiskSize: diskSize,
298+
DiskSizeBytes: v1.NewBytes(v1.BytesValue(instanceInfo.Configuration.StorageInGb), v1.Gigabyte),
298299
SSHUser: instanceInfo.SshUser,
299300
SSHPort: int(instanceInfo.SshPort),
300301
Status: v1.Status{

v1/providers/shadeform/instancetype.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -225,14 +225,16 @@ func (c *ShadeformClient) convertShadeformInstanceTypeToV1InstanceType(shadeform
225225

226226
for _, region := range shadeformInstanceType.Availability {
227227
instanceTypes = append(instanceTypes, v1.InstanceType{
228-
ID: v1.InstanceTypeID(c.getInstanceTypeID(instanceType, region.Region)),
229-
Type: instanceType,
230-
VCPU: shadeformInstanceType.Configuration.Vcpus,
231-
Memory: units.Base2Bytes(shadeformInstanceType.Configuration.MemoryInGb) * units.GiB,
228+
ID: v1.InstanceTypeID(c.getInstanceTypeID(instanceType, region.Region)),
229+
Type: instanceType,
230+
VCPU: shadeformInstanceType.Configuration.Vcpus,
231+
Memory: units.Base2Bytes(shadeformInstanceType.Configuration.MemoryInGb) * units.GiB,
232+
MemoryBytes: v1.NewBytes(v1.BytesValue(shadeformInstanceType.Configuration.MemoryInGb), v1.Gigabyte),
232233
SupportedGPUs: []v1.GPU{
233234
{
234235
Count: shadeformInstanceType.Configuration.NumGpus,
235236
Memory: units.Base2Bytes(shadeformInstanceType.Configuration.VramPerGpuInGb) * units.GiB,
237+
MemoryBytes: v1.NewBytes(v1.BytesValue(shadeformInstanceType.Configuration.VramPerGpuInGb), v1.Gigabyte),
236238
MemoryDetails: "",
237239
NetworkDetails: shadeformInstanceType.Configuration.Interconnect,
238240
Manufacturer: gpuManufacturer,
@@ -242,9 +244,10 @@ func (c *ShadeformClient) convertShadeformInstanceTypeToV1InstanceType(shadeform
242244
},
243245
SupportedStorage: []v1.Storage{ // TODO: add storage (look in configuration)
244246
{
245-
Type: "ssd",
246-
Count: 1,
247-
Size: units.Base2Bytes(shadeformInstanceType.Configuration.StorageInGb) * units.GiB,
247+
Type: "ssd",
248+
Count: 1,
249+
Size: units.Base2Bytes(shadeformInstanceType.Configuration.StorageInGb) * units.GiB,
250+
SizeBytes: v1.NewBytes(v1.BytesValue(shadeformInstanceType.Configuration.StorageInGb), v1.Gigabyte),
248251
},
249252
},
250253
SupportedArchitectures: []v1.Architecture{architecture},

0 commit comments

Comments
 (0)