Skip to content

Commit

Permalink
Support Droplet GPU information (#748)
Browse files Browse the repository at this point in the history
* sizes: support disk_info

* sizes: support gpu_info

* droplets: support listing GPU Droplets.
  • Loading branch information
andrewsomething authored Oct 25, 2024
1 parent 1c20d59 commit 2654a9d
Show file tree
Hide file tree
Showing 4 changed files with 257 additions and 14 deletions.
12 changes: 12 additions & 0 deletions droplets.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ var errNoNetworks = errors.New("no networks have been defined")
// See: https://docs.digitalocean.com/reference/api/api-reference/#tag/Droplets
type DropletsService interface {
List(context.Context, *ListOptions) ([]Droplet, *Response, error)
ListWithGPUs(context.Context, *ListOptions) ([]Droplet, *Response, error)
ListByName(context.Context, string, *ListOptions) ([]Droplet, *Response, error)
ListByTag(context.Context, string, *ListOptions) ([]Droplet, *Response, error)
Get(context.Context, int) (*Droplet, *Response, error)
Expand Down Expand Up @@ -321,6 +322,17 @@ func (s *DropletsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Dropl
return s.list(ctx, path)
}

// ListWithGPUs lists all Droplets with GPUs.
func (s *DropletsServiceOp) ListWithGPUs(ctx context.Context, opt *ListOptions) ([]Droplet, *Response, error) {
path := fmt.Sprintf("%s?type=gpus", dropletBasePath)
path, err := addOptions(path, opt)
if err != nil {
return nil, nil, err
}

return s.list(ctx, path)
}

// ListByName lists all Droplets filtered by name returning only exact matches.
// It is case-insensitive
func (s *DropletsServiceOp) ListByName(ctx context.Context, name string, opt *ListOptions) ([]Droplet, *Response, error) {
Expand Down
92 changes: 92 additions & 0 deletions droplets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,98 @@ func TestDroplets_ListDroplets(t *testing.T) {
}
}

func TestDroplets_ListDropletsWithGPUs(t *testing.T) {
setup()
defer teardown()

mux.HandleFunc("/v2/droplets", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, http.MethodGet)
if r.URL.Query().Get("type") != "gpus" {
t.Errorf("Droplets.ListWithGPUs did not request with a type parameter")
}
fmt.Fprint(w, `{
"droplets": [
{
"id": 1,
"size": {
"gpu_info": {
"count": 1,
"vram": {
"amount": 8,
"unit": "gib"
},
"model": "nvidia_tesla_v100"
},
"disk_info": [
{
"type": "local",
"size": {
"amount": 200,
"unit": "gib"
}
},
{
"type": "scratch",
"size": {
"amount": 40960,
"unit": "gib"
}
}
]
}
}
],
"meta": {
"total": 1
}
}`)
})

droplets, resp, err := client.Droplets.ListWithGPUs(ctx, nil)
if err != nil {
t.Errorf("Droplets.List returned error: %v", err)
}

expectedDroplets := []Droplet{
{
ID: 1,
Size: &Size{
GPUInfo: &GPUInfo{
Count: 1,
VRAM: &VRAM{
Amount: 8,
Unit: "gib",
},
Model: "nvidia_tesla_v100",
},
DiskInfo: []DiskInfo{
{
Type: "local",
Size: &DiskSize{
Amount: 200,
Unit: "gib",
},
},
{
Type: "scratch",
Size: &DiskSize{
Amount: 40960,
Unit: "gib",
},
},
},
},
},
}
if !reflect.DeepEqual(droplets, expectedDroplets) {
t.Errorf("Droplets.List\nDroplets: got=%#v\nwant=%#v", droplets, expectedDroplets)
}
expectedMeta := &Meta{Total: 1}
if !reflect.DeepEqual(resp.Meta, expectedMeta) {
t.Errorf("Droplets.List\nMeta: got=%#v\nwant=%#v", resp.Meta, expectedMeta)
}
}

func TestDroplets_ListDropletsByTag(t *testing.T) {
setup()
defer teardown()
Expand Down
48 changes: 38 additions & 10 deletions sizes.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,44 @@ var _ SizesService = &SizesServiceOp{}

// Size represents a DigitalOcean Size
type Size struct {
Slug string `json:"slug,omitempty"`
Memory int `json:"memory,omitempty"`
Vcpus int `json:"vcpus,omitempty"`
Disk int `json:"disk,omitempty"`
PriceMonthly float64 `json:"price_monthly,omitempty"`
PriceHourly float64 `json:"price_hourly,omitempty"`
Regions []string `json:"regions,omitempty"`
Available bool `json:"available,omitempty"`
Transfer float64 `json:"transfer,omitempty"`
Description string `json:"description,omitempty"`
Slug string `json:"slug,omitempty"`
Memory int `json:"memory,omitempty"`
Vcpus int `json:"vcpus,omitempty"`
Disk int `json:"disk,omitempty"`
PriceMonthly float64 `json:"price_monthly,omitempty"`
PriceHourly float64 `json:"price_hourly,omitempty"`
Regions []string `json:"regions,omitempty"`
Available bool `json:"available,omitempty"`
Transfer float64 `json:"transfer,omitempty"`
Description string `json:"description,omitempty"`
GPUInfo *GPUInfo `json:"gpu_info,omitempty"`
DiskInfo []DiskInfo `json:"disk_info,omitempty"`
}

// DiskInfo containing information about the disks available to Droplets created
// with this size.
type DiskInfo struct {
Type string `json:"type,omitempty"`
Size *DiskSize `json:"size,omitempty"`
}

// DiskSize provides information about the size of a disk.
type DiskSize struct {
Amount int `json:"amount,omitempty"`
Unit string `json:"unit,omitempty"`
}

// GPUInfo provides information about the GPU available to Droplets created with this size.
type GPUInfo struct {
Count int `json:"count,omitempty"`
VRAM *VRAM `json:"vram,omitempty"`
Model string `json:"model,omitempty"`
}

// VRAM provides information about the amount of VRAM available to the GPU.
type VRAM struct {
Amount int `json:"amount,omitempty"`
Unit string `json:"unit,omitempty"`
}

func (s Size) String() string {
Expand Down
119 changes: 115 additions & 4 deletions sizes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ func TestSizes_List(t *testing.T) {
Available: true,
Transfer: 1,
Description: "Basic",
DiskInfo: []DiskInfo{
{
Type: "local",
Size: &DiskSize{
Amount: 25,
Unit: "gib",
},
},
},
},
{
Slug: "512mb",
Expand All @@ -35,6 +44,51 @@ func TestSizes_List(t *testing.T) {
Available: true,
Transfer: 1,
Description: "Legacy Basic",
DiskInfo: []DiskInfo{
{
Type: "local",
Size: &DiskSize{
Amount: 20,
Unit: "gib",
},
},
},
},
{
Slug: "gpu-h100x8-640gb-200",
Memory: 1966080,
Vcpus: 160,
Disk: 200,
PriceMonthly: 35414.4,
PriceHourly: 52.7,
Regions: []string{"tor1"},
Available: true,
Transfer: 60,
Description: "H100 GPU - 8X (small disk)",
GPUInfo: &GPUInfo{
Count: 8,
VRAM: &VRAM{
Amount: 640,
Unit: "gib",
},
Model: "nvidia_h100",
},
DiskInfo: []DiskInfo{
{
Type: "local",
Size: &DiskSize{
Amount: 200,
Unit: "gib",
},
},
{
Type: "scratch",
Size: &DiskSize{
Amount: 40960,
Unit: "gib",
},
},
},
},
}

Expand All @@ -55,7 +109,16 @@ func TestSizes_List(t *testing.T) {
"nyc2"
],
"available": true,
"description": "Basic"
"description": "Basic",
"disk_info": [
{
"type": "local",
"size": {
"amount": 25,
"unit": "gib"
}
}
]
},
{
"slug": "512mb",
Expand All @@ -70,11 +133,59 @@ func TestSizes_List(t *testing.T) {
"nyc2"
],
"available": true,
"description": "Legacy Basic"
"description": "Legacy Basic",
"disk_info": [
{
"type": "local",
"size": {
"amount": 20,
"unit": "gib"
}
}
]
},
{
"slug": "gpu-h100x8-640gb-200",
"memory": 1966080,
"vcpus": 160,
"disk": 200,
"transfer": 60,
"price_monthly": 35414.4,
"price_hourly": 52.7,
"regions": [
"tor1"
],
"available": true,
"description": "H100 GPU - 8X (small disk)",
"networking_throughput": 10000,
"gpu_info": {
"count": 8,
"vram": {
"amount": 640,
"unit": "gib"
},
"model": "nvidia_h100"
},
"disk_info": [
{
"type": "local",
"size": {
"amount": 200,
"unit": "gib"
}
},
{
"type": "scratch",
"size": {
"amount": 40960,
"unit": "gib"
}
}
]
}
],
"meta": {
"total": 2
"total": 3
}
}`)
})
Expand All @@ -88,7 +199,7 @@ func TestSizes_List(t *testing.T) {
t.Errorf("Sizes.List returned sizes %+v, expected %+v", sizes, expectedSizes)
}

expectedMeta := &Meta{Total: 2}
expectedMeta := &Meta{Total: 3}
if !reflect.DeepEqual(resp.Meta, expectedMeta) {
t.Errorf("Sizes.List returned meta %+v, expected %+v", resp.Meta, expectedMeta)
}
Expand Down

0 comments on commit 2654a9d

Please sign in to comment.