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

droplets: support listing GPU Droplets. #1601

Merged
merged 2 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
3 changes: 3 additions & 0 deletions args.go
Original file line number Diff line number Diff line change
Expand Up @@ -565,4 +565,7 @@ const (

// ArgTokenValidationServer is the server used to validate an OAuth token
ArgTokenValidationServer = "token-validation-server"

// ArgGPUs specifies to list GPU Droplets
ArgGPUs = "gpus"
)
14 changes: 13 additions & 1 deletion commands/droplets.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ If you do not specify a region, the Droplet is created in the default region for
aliasOpt("ls"), displayerType(&displayers.Droplet{}))
AddStringFlag(cmdRunDropletList, doctl.ArgRegionSlug, "", "", "Retrieves a list of Droplets in a specified region")
AddStringFlag(cmdRunDropletList, doctl.ArgTagName, "", "", "Retrieves a list of Droplets with the specified tag name")
AddBoolFlag(cmdRunDropletList, doctl.ArgGPUs, "", false, "List GPU Droplets only. By default, only non-GPU Droplets are returned.")
cmdRunDropletList.Example = `The following example retrieves a list of all Droplets in the ` + "`" + `nyc1` + "`" + ` region: doctl compute droplet list --region nyc1`

cmdDropletNeighbors := CmdBuilder(cmd, RunDropletNeighbors, "neighbors <droplet-id>", "List a Droplet's neighbors on your account", `Lists your Droplets that are on the same physical hardware, including the following details:`+dropletDetails, Writer,
Expand Down Expand Up @@ -656,6 +657,15 @@ func RunDropletList(c *CmdConfig) error {
return err
}

gpus, err := c.Doit.GetBool(c.NS, doctl.ArgGPUs)
if err != nil {
return err
}

if gpus && tagName != "" {
return fmt.Errorf("The --gpus and --tag-name flags are mutually exclusive.")
}

matches := make([]glob.Glob, 0, len(c.Args))
for _, globStr := range c.Args {
g, err := glob.Compile(globStr)
Expand All @@ -669,7 +679,9 @@ func RunDropletList(c *CmdConfig) error {
var matchedList do.Droplets

var list do.Droplets
if tagName == "" {
if gpus {
list, err = ds.ListWithGPUs()
} else if tagName == "" {
list, err = ds.List()
} else {
list, err = ds.ListByTag(tagName)
Expand Down
19 changes: 19 additions & 0 deletions commands/droplets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,16 @@ func TestDropletsListByTag(t *testing.T) {
})
}

func TestDropletsListGPUs(t *testing.T) {
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
tm.droplets.EXPECT().ListWithGPUs().Return(testDropletList, nil)

config.Doit.Set(config.NS, doctl.ArgGPUs, true)
err := RunDropletList(config)
assert.NoError(t, err)
})
}

func TestDropletsTag(t *testing.T) {
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
trr := &godo.TagResourcesRequest{
Expand All @@ -415,6 +425,15 @@ func TestDropletsTag(t *testing.T) {
})
}

func TestDropletsListGPUsAndTagsExclusive(t *testing.T) {
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
config.Doit.Set(config.NS, doctl.ArgGPUs, true)
config.Doit.Set(config.NS, doctl.ArgTagName, "my-tag")
err := RunDropletList(config)
assert.Error(t, err)
})
}

func TestDropletsTagMultiple(t *testing.T) {
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
trr := &godo.TagResourcesRequest{
Expand Down
30 changes: 21 additions & 9 deletions do/droplets.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type Kernels []Kernel
type DropletsService interface {
List() (Droplets, error)
ListByTag(string) (Droplets, error)
ListWithGPUs() (Droplets, error)
Get(int) (*Droplet, error)
Create(*godo.DropletCreateRequest, bool) (*Droplet, error)
CreateMultiple(*godo.DropletMultiCreateRequest) (Droplets, error)
Expand Down Expand Up @@ -93,18 +94,25 @@ func (ds *dropletsService) List() (Droplets, error) {
return si, resp, err
}

si, err := PaginateResp(f)
if err != nil {
return nil, err
}
return ds.list(f)
}

list := make(Droplets, len(si))
for i := range si {
a := si[i].(godo.Droplet)
list[i] = Droplet{Droplet: &a}
func (ds *dropletsService) ListWithGPUs() (Droplets, error) {
f := func(opt *godo.ListOptions) ([]any, *godo.Response, error) {
list, resp, err := ds.client.Droplets.ListWithGPUs(context.TODO(), opt)
if err != nil {
return nil, nil, err
}

si := make([]any, len(list))
for i := range list {
si[i] = list[i]
}

return si, resp, err
}

return list, nil
return ds.list(f)
}

func (ds *dropletsService) ListByTag(tagName string) (Droplets, error) {
Expand All @@ -122,6 +130,10 @@ func (ds *dropletsService) ListByTag(tagName string) (Droplets, error) {
return si, resp, err
}

return ds.list(f)
}

func (ds *dropletsService) list(f Generator) (Droplets, error) {
si, err := PaginateResp(f)
if err != nil {
return nil, err
Expand Down
16 changes: 16 additions & 0 deletions do/mocks/DropletsService.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.22
require (
github.com/blang/semver v3.5.1+incompatible
github.com/creack/pty v1.1.21
github.com/digitalocean/godo v1.128.0
github.com/digitalocean/godo v1.128.1-0.20241025145008-2654a9d1e887
github.com/docker/cli v24.0.5+incompatible
github.com/docker/docker v25.0.6+incompatible
github.com/docker/docker-credential-helpers v0.7.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/digitalocean/godo v1.128.0 h1:cGn/ibMSRZ9+8etbzMv2MnnCEPTTGlEnx3HHTPwdk1U=
github.com/digitalocean/godo v1.128.0/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc=
github.com/digitalocean/godo v1.128.1-0.20241025145008-2654a9d1e887 h1:kdXNbMfHEDbQilcqllKkNrJ85ftyJSvSDpsQvzrhHbg=
github.com/digitalocean/godo v1.128.1-0.20241025145008-2654a9d1e887/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v24.0.5+incompatible h1:WeBimjvS0eKdH4Ygx+ihVq1Q++xg36M/rMi4aXAvodc=
Expand Down
50 changes: 50 additions & 0 deletions integration/droplet_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ var _ = suite("compute/droplet/list", func(t *testing.T, when spec.G, it spec.S)

q := req.URL.Query()
tag := q.Get("tag_name")
dtype := q.Get("type")

if tag != "" && dtype != "" {
w.Write([]byte(`{"id": "unprocessible_entity", "message":"mutually exclusive query parameters"}`))
w.WriteHeader(http.StatusUnprocessableEntity)
return
}

if tag == "some-tag" {
w.Write([]byte(`{}`))
return
Expand All @@ -48,6 +56,10 @@ var _ = suite("compute/droplet/list", func(t *testing.T, when spec.G, it spec.S)
return
}

if dtype == "gpus" {
w.Write([]byte(dropletListGPUsResponse))
}

w.Write([]byte(dropletListResponse))
default:
dump, err := httputil.DumpRequest(req, true)
Expand Down Expand Up @@ -76,6 +88,23 @@ var _ = suite("compute/droplet/list", func(t *testing.T, when spec.G, it spec.S)
})
})

when("the gpu flag is passed", func() {
it("lists gpu droplets", func() {
cmd := exec.Command(builtBinaryPath,
"-t", "some-magic-token",
"-u", server.URL,
"compute",
"droplet",
"list",
"--gpus",
)

output, err := cmd.CombinedOutput()
expect.NoError(err, fmt.Sprintf("received error output: %s", output))
expect.Equal(strings.TrimSpace(dropletGPUListOutput), strings.TrimSpace(string(output)))
})
})

when("a region is provided", func() {
it("filters the returned droplets by region", func() {
cmd := exec.Command(builtBinaryPath,
Expand Down Expand Up @@ -163,9 +192,30 @@ const (
}]
}`

dropletListGPUsResponse = `
{
"droplets": [{
"id": 1111,
"name": "gpu-droplet",
"image": {
"distribution": "Ubuntu",
"name": "gpu-h100x8-640gb-200"
},
"region": {
"slug": "tor1"
},
"status": "active"
}]
}`

dropletListOutput = `
ID Name Public IPv4 Private IPv4 Public IPv6 Memory VCPUs Disk Region Image VPC UUID Status Tags Features Volumes
1111 some-droplet-name 0 0 0 some-region-slug some-distro some-image-name active test,yes remotes some-volume-id
`

dropletGPUListOutput = `
ID Name Public IPv4 Private IPv4 Public IPv6 Memory VCPUs Disk Region Image VPC UUID Status Tags Features Volumes
1111 gpu-droplet 0 0 0 tor1 Ubuntu gpu-h100x8-640gb-200 active
`

dropletListRegionOutput = `
Expand Down
12 changes: 12 additions & 0 deletions vendor/github.com/digitalocean/godo/droplets.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 38 additions & 10 deletions vendor/github.com/digitalocean/godo/sizes.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion vendor/modules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ github.com/creack/pty
# github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
## explicit
github.com/davecgh/go-spew/spew
# github.com/digitalocean/godo v1.128.0
# github.com/digitalocean/godo v1.128.1-0.20241025145008-2654a9d1e887
## explicit; go 1.22
github.com/digitalocean/godo
github.com/digitalocean/godo/metrics
Expand Down
Loading