Skip to content
56 changes: 56 additions & 0 deletions v1/bytes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package v1

var zeroBytes = Bytes{value: 0, unit: Byte}

// NewBytes creates a new Bytes with the given value and unit
func NewBytes(value BytesValue, unit BytesUnit) Bytes {
if value < 0 {
return zeroBytes
}
return Bytes{
value: value,
unit: unit,
}
}

type (
BytesValue int64
BytesUnit string
)

// Bytes represents a number of some unit of bytes
type Bytes struct {
value BytesValue
unit BytesUnit
}

// Value is the whole non-negative number of bytes of the specified unit
func (b Bytes) Value() BytesValue {
return b.value
}

// Unit is the unit of the byte value
func (b Bytes) Unit() BytesUnit {
return b.unit
}

// ByteUnit is a unit of measurement for bytes
const (
Byte BytesUnit = "B"

// Base 10
Kilobyte BytesUnit = "KB"
Megabyte BytesUnit = "MB"
Gigabyte BytesUnit = "GB"
Terabyte BytesUnit = "TB"
Petabyte BytesUnit = "PB"
Exabyte BytesUnit = "EB"

// Base 2
Kibibyte BytesUnit = "KiB"
Mebibyte BytesUnit = "MiB"
Gibibyte BytesUnit = "GiB"
Tebibyte BytesUnit = "TiB"
Pebibyte BytesUnit = "PiB"
Exbibyte BytesUnit = "EiB"
)
6 changes: 4 additions & 2 deletions v1/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,8 @@ type Instance struct {
Hostname string
ImageID string
InstanceType string
DiskSize units.Base2Bytes
DiskSize units.Base2Bytes // TODO: deprecate in favor of DiskSizeByteValue
Copy link
Contributor

@patelspratik patelspratik Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the internet says that changing this to
// Deprecated: TODO deprecate in favor of DiskSizeByteValue will actually mark this as deprecated. Though do you actually want to do that, or defer that change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a little worried it will blow up our linter but could give it a shot...

DiskSizeBytes Bytes
VolumeType string
PubKeyFingerprint string
SSHUser string
Expand Down Expand Up @@ -274,7 +275,8 @@ type CreateInstanceAttrs struct {
ImageID string
InstanceType string
UserDataBase64 string
DiskSize units.Base2Bytes
DiskSize units.Base2Bytes // TODO: deprecate in favor of DiskSizeByteValue
DiskSizeBytes Bytes
Tags Tags
FirewallRules FirewallRules
UseSpot bool
Expand Down
10 changes: 6 additions & 4 deletions v1/instancetype.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ type InstanceType struct {
SupportedStorage []Storage
ElasticRootVolume bool
SupportedUsageClasses []string
Memory units.Base2Bytes
Memory units.Base2Bytes // TODO: deprecate in favor of MemoryByteValue
MemoryBytes Bytes
MaximumNetworkInterfaces int32
NetworkPerformance string
SupportedNumCores []int32
Expand Down Expand Up @@ -114,7 +115,8 @@ func MakeGenericInstanceTypeIDFromInstance(instance Instance) InstanceTypeID {

type GPU struct {
Count int32
Memory units.Base2Bytes
Memory units.Base2Bytes // TODO: deprecate in favor of MemoryByteValue
MemoryBytes Bytes
MemoryDetails string // "", "HBM", "GDDR", "DDR", etc.
NetworkDetails string // "PCIe", "SXM4", "SXM5", etc.
Manufacturer Manufacturer
Expand Down Expand Up @@ -378,7 +380,7 @@ func normalizeInstanceTypes(types []InstanceType) []InstanceType {

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

// Check that supported storage has price information
for i, storage := range instanceType.SupportedStorage {
if storage.MinSize != nil {
if storage.MinSize != nil || storage.MinSizeBytes != nil {
if storage.PricePerGBHr == nil {
return fmt.Errorf("instance type %s should have storage %d price", instanceType.ID, i)
}
Expand Down
5 changes: 4 additions & 1 deletion v1/providers/fluidstack/instancetype.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

"github.com/alecthomas/units"
"github.com/bojanz/currency"
"github.com/brevdev/cloud/v1"
v1 "github.com/brevdev/cloud/v1"
openapi "github.com/brevdev/cloud/v1/providers/fluidstack/gen/fluidstack"
)

Expand Down Expand Up @@ -82,12 +82,14 @@ func convertFluidStackInstanceTypeToV1InstanceType(location string, fsInstanceTy
}
}

var memoryBytes v1.Bytes
var ram units.Base2Bytes
if fsInstanceType.Memory != "" {
memoryStr := strings.TrimSuffix(fsInstanceType.Memory, "GB")
memoryStr = strings.TrimSpace(memoryStr)
if memoryGB, err := strconv.ParseFloat(memoryStr, 64); err == nil {
ram = units.Base2Bytes(memoryGB) * units.Gibibyte
memoryBytes = v1.NewBytes(v1.BytesValue(memoryGB), v1.Gigabyte)
}
}

Expand All @@ -102,6 +104,7 @@ func convertFluidStackInstanceTypeToV1InstanceType(location string, fsInstanceTy
Type: fsInstanceType.Name,
VCPU: vcpus,
Memory: ram,
MemoryBytes: memoryBytes,
SupportedGPUs: gpus,
BasePrice: &price,
IsAvailable: isAvailable,
Expand Down
7 changes: 4 additions & 3 deletions v1/providers/lambdalabs/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,10 @@ func convertLambdaLabsInstanceToV1Instance(instance openapi.Instance) *v1.Instan
Status: v1.Status{
LifecycleStatus: convertLambdaLabsStatusToV1Status(instance.Status),
},
InstanceType: instance.InstanceType.Name,
VolumeType: "ssd",
DiskSize: units.GiB * units.Base2Bytes(instance.InstanceType.Specs.StorageGib),
InstanceType: instance.InstanceType.Name,
VolumeType: "ssd",
DiskSize: units.GiB * units.Base2Bytes(instance.InstanceType.Specs.StorageGib),
DiskSizeBytes: v1.NewBytes(v1.BytesValue(instance.InstanceType.Specs.StorageGib), v1.Gibibyte),
FirewallRules: v1.FirewallRules{
IngressRules: []v1.FirewallRule{generateFirewallRouteFromPort(22), generateFirewallRouteFromPort(2222)}, // TODO pull from api
EgressRules: []v1.FirewallRule{generateFirewallRouteFromPort(22), generateFirewallRouteFromPort(2222)}, // TODO pull from api
Expand Down
14 changes: 11 additions & 3 deletions v1/providers/lambdalabs/instancetype.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package v1
import (
"context"
"fmt"
"math"
"regexp"
"strconv"
"strings"
Expand Down Expand Up @@ -127,7 +128,11 @@ func parseGPUFromDescription(input string) (v1.GPU, error) {
}
memoryStr := memoryMatch[1]
memoryGiB, _ := strconv.Atoi(memoryStr)
if memoryGiB > math.MaxInt32 {
memoryGiB = math.MaxInt32
}
gpu.Memory = units.GiB * units.Base2Bytes(memoryGiB)
gpu.MemoryBytes = v1.NewBytes(v1.BytesValue(memoryGiB), v1.Gibibyte)

// Extract the network details
networkRegex := regexp.MustCompile(`(\w+\s?)+\)`)
Expand Down Expand Up @@ -172,19 +177,22 @@ func convertLambdaLabsInstanceTypeToV1InstanceType(location string, instType ope
if err != nil {
return v1.InstanceType{}, err
}

it := v1.InstanceType{
Location: location,
Type: instType.Name,
SupportedGPUs: gpus,
SupportedStorage: []v1.Storage{
{
Type: "ssd",
Count: 1,
Size: units.GiB * units.Base2Bytes(instType.Specs.StorageGib),
Type: "ssd",
Count: 1,
Size: units.GiB * units.Base2Bytes(instType.Specs.StorageGib),
SizeBytes: v1.NewBytes(v1.BytesValue(instType.Specs.StorageGib), v1.Gibibyte),
},
},
SupportedUsageClasses: []string{"on-demand"},
Memory: units.GiB * units.Base2Bytes(instType.Specs.MemoryGib),
MemoryBytes: v1.NewBytes(v1.BytesValue(instType.Specs.MemoryGib), v1.Gibibyte),
MaximumNetworkInterfaces: 0,
NetworkPerformance: "",
SupportedNumCores: []int32{},
Expand Down
17 changes: 8 additions & 9 deletions v1/providers/launchpad/instance_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,18 @@ func launchpadDeploymentToInstance(deployment *openapi.Deployment) (v1.Instance,
return v1.Instance{}, errors.WrapAndTrace(err)
}

var diskSize units.Base2Bytes
var totalStorageSize int32
nodes := deployment.GetCluster().Cluster.GetNodes()
if len(nodes) == 0 {
diskSize = 0
totalStorageSize = 0
} else {
node := nodes[0]

// Calculate disk size
storage := node.Node.GetStorage()
size := 0
for _, s := range storage {
size += int(s.GetSize())
totalStorageSize += s.GetSize()
}
diskSize = units.Base2Bytes(size) * units.GiB
}

inst := v1.Instance{
Expand All @@ -78,10 +76,11 @@ func launchpadDeploymentToInstance(deployment *openapi.Deployment) (v1.Instance,
ToPort: 2022,
},
},
DiskSize: diskSize,
Location: deployment.GetRegion(),
PublicDNS: deployment.GetCluster().Cluster.GetPublicAddress(),
PublicIP: deployment.GetCluster().Cluster.GetPublicAddress(),
DiskSize: units.Base2Bytes(totalStorageSize) * units.GiB,
DiskSizeBytes: v1.NewBytes(v1.BytesValue(totalStorageSize), v1.Gigabyte),
Location: deployment.GetRegion(),
PublicDNS: deployment.GetCluster().Cluster.GetPublicAddress(),
PublicIP: deployment.GetCluster().Cluster.GetPublicAddress(),
}

cluster := deployment.GetCluster().Cluster
Expand Down
15 changes: 12 additions & 3 deletions v1/providers/launchpad/instancetype.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ func launchpadInstanceTypeToInstanceType(launchpadInstanceType openapi.InstanceT
Type: typeName,
VCPU: launchpadInstanceType.Cpu,
Memory: gbToBytes(launchpadInstanceType.MemoryGb),
MemoryBytes: v1.NewBytes(v1.BytesValue(launchpadInstanceType.MemoryGb), v1.Gigabyte),
SupportedGPUs: []v1.GPU{gpu},
SupportedStorage: storage,
SupportedArchitectures: []v1.Architecture{launchpadArchitectureToArchitecture(launchpadInstanceType.SystemArch)},
Expand Down Expand Up @@ -242,9 +243,10 @@ func launchpadStorageToStorages(launchpadStorage []openapi.InstanceTypeStorage)
storage := make([]v1.Storage, len(launchpadStorage))
for i, s := range launchpadStorage {
storage[i] = v1.Storage{
Count: 1,
Size: gbToBytes(s.SizeGb),
Type: string(s.Type),
Count: 1,
Size: gbToBytes(s.SizeGb),
SizeBytes: v1.NewBytes(v1.BytesValue(s.SizeGb), v1.Gigabyte),
Type: string(s.Type),
}
}
return storage
Expand All @@ -261,6 +263,7 @@ func launchpadGpusToGpus(lpGpus []openapi.InstanceTypeGpu) []v1.GPU {
Manufacturer: v1.GetManufacturer(gp.Manufacturer),
Count: gp.Count,
Memory: gbToBytes(gp.MemoryGb),
MemoryBytes: v1.NewBytes(v1.BytesValue(int64(gp.MemoryGb)), v1.Gigabyte),
NetworkDetails: string(gp.InterconnectionType),
Type: strings.ToUpper(gp.Model),
}
Expand Down Expand Up @@ -307,8 +310,10 @@ func launchpadClusterToInstanceType(cluster openapi.Cluster) *v1.InstanceType {
vcpu = *node.Cpu
}
var memory units.Base2Bytes
var memoryBytes v1.Bytes
if node.Memory != nil {
memory = gbToBytes(*node.Memory)
memoryBytes = v1.NewBytes(v1.BytesValue(*node.Memory), v1.Gigabyte)
}

isAvailable := (cluster.ProvisioningState != nil && *cluster.ProvisioningState == openapi.ProvisioningStateReady)
Expand All @@ -328,6 +333,7 @@ func launchpadClusterToInstanceType(cluster openapi.Cluster) *v1.InstanceType {
SupportedGPUs: []v1.GPU{*gpu},
SupportedStorage: storage,
Memory: memory,
MemoryBytes: memoryBytes,
VCPU: vcpu,
IsAvailable: isAvailable,
Location: location,
Expand Down Expand Up @@ -359,15 +365,18 @@ func launchpadGputoGpu(node openapi.Node) *v1.GPU {
}

var lpGpuMemory units.Base2Bytes
var lpGpuMemoryBytes v1.Bytes
if lpGpu.Memory != nil {
lpGpuMemory = gbToBytes(*lpGpu.Memory)
lpGpuMemoryBytes = v1.NewBytes(v1.BytesValue(*lpGpu.Memory), v1.Gigabyte)
}

gpu := &v1.GPU{
Name: lpGpuModel,
Count: lpGpuCount,
NetworkDetails: lpGpuFormFactor,
Memory: lpGpuMemory,
MemoryBytes: lpGpuMemoryBytes,
Manufacturer: "NVIDIA", // The only supported manufacturer for Launchpad
}
return gpu
Expand Down
1 change: 1 addition & 0 deletions v1/providers/shadeform/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ func (c *ShadeformClient) convertInstanceInfoResponseToV1Instance(ctx context.Co
InstanceType: instanceType,
InstanceTypeID: v1.InstanceTypeID(c.getInstanceTypeID(instanceType, instanceInfo.Region)),
DiskSize: diskSize,
DiskSizeBytes: v1.NewBytes(v1.BytesValue(instanceInfo.Configuration.StorageInGb), v1.Gigabyte),
SSHUser: instanceInfo.SshUser,
SSHPort: int(instanceInfo.SshPort),
Status: v1.Status{
Expand Down
17 changes: 10 additions & 7 deletions v1/providers/shadeform/instancetype.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,14 +225,16 @@ func (c *ShadeformClient) convertShadeformInstanceTypeToV1InstanceType(shadeform

for _, region := range shadeformInstanceType.Availability {
instanceTypes = append(instanceTypes, v1.InstanceType{
ID: v1.InstanceTypeID(c.getInstanceTypeID(instanceType, region.Region)),
Type: instanceType,
VCPU: shadeformInstanceType.Configuration.Vcpus,
Memory: units.Base2Bytes(shadeformInstanceType.Configuration.MemoryInGb) * units.GiB,
ID: v1.InstanceTypeID(c.getInstanceTypeID(instanceType, region.Region)),
Type: instanceType,
VCPU: shadeformInstanceType.Configuration.Vcpus,
Memory: units.Base2Bytes(shadeformInstanceType.Configuration.MemoryInGb) * units.GiB,
MemoryBytes: v1.NewBytes(v1.BytesValue(shadeformInstanceType.Configuration.MemoryInGb), v1.Gigabyte),
SupportedGPUs: []v1.GPU{
{
Count: shadeformInstanceType.Configuration.NumGpus,
Memory: units.Base2Bytes(shadeformInstanceType.Configuration.VramPerGpuInGb) * units.GiB,
MemoryBytes: v1.NewBytes(v1.BytesValue(shadeformInstanceType.Configuration.VramPerGpuInGb), v1.Gigabyte),
MemoryDetails: "",
NetworkDetails: shadeformInstanceType.Configuration.Interconnect,
Manufacturer: gpuManufacturer,
Expand All @@ -242,9 +244,10 @@ func (c *ShadeformClient) convertShadeformInstanceTypeToV1InstanceType(shadeform
},
SupportedStorage: []v1.Storage{ // TODO: add storage (look in configuration)
{
Type: "ssd",
Count: 1,
Size: units.Base2Bytes(shadeformInstanceType.Configuration.StorageInGb) * units.GiB,
Type: "ssd",
Count: 1,
Size: units.Base2Bytes(shadeformInstanceType.Configuration.StorageInGb) * units.GiB,
SizeBytes: v1.NewBytes(v1.BytesValue(shadeformInstanceType.Configuration.StorageInGb), v1.Gigabyte),
},
},
SupportedArchitectures: []v1.Architecture{architecture},
Expand Down
15 changes: 10 additions & 5 deletions v1/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ import (

type Storage struct {
Count int32
Size units.Base2Bytes
Size units.Base2Bytes // TODO: deprecate in favor of SizeByteValue
SizeBytes Bytes
Type string
MinSize *units.Base2Bytes
MaxSize *units.Base2Bytes
MinSize *units.Base2Bytes // TODO: deprecate in favor of MinSizeByteValue
MinSizeBytes *Bytes
MaxSize *units.Base2Bytes // TODO: deprecate in favor of MaxSizeByteValue
MaxSizeBytes *Bytes
PricePerGBHr *currency.Amount
IsEphemeral bool
IsAdditionalDisk bool
Expand All @@ -21,7 +24,8 @@ type Storage struct {
}

type Disk struct {
Size units.Base2Bytes
Size units.Base2Bytes // TODO: deprecate in favor of SizeByteValue
SizeBytes Bytes
Type string
MountPath string
}
Expand All @@ -32,6 +36,7 @@ type CloudResizeInstanceVolume interface {

type ResizeInstanceVolumeArgs struct {
InstanceID CloudProviderInstanceID
Size units.Base2Bytes
Size units.Base2Bytes // TODO: deprecate in favor of SizeByteValue
SizeBytes Bytes
WaitForOptimizing bool
}
Loading