Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Droplet GPU information #748

Merged
merged 3 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading