Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Implement coder images ls #189

Merged
merged 1 commit into from
Nov 16, 2020
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
43 changes: 43 additions & 0 deletions ci/integration/images_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package integration

import (
"context"
"regexp"
"testing"

"cdr.dev/coder-cli/coder-sdk"
"cdr.dev/coder-cli/pkg/tcli"
)

func TestImagesCLI(t *testing.T) {
t.Parallel()

run(t, "coder-cli-images-tests", func(t *testing.T, ctx context.Context, c *tcli.ContainerRunner) {
headlessLogin(ctx, t, c)

// Successfully output help.
c.Run(ctx, "coder images --help").Assert(t,
tcli.Success(),
tcli.StdoutMatches(regexp.QuoteMeta("Manage existing images and/or import new ones.")),
tcli.StderrEmpty(),
)

// OK - human output
c.Run(ctx, "coder images ls").Assert(t,
tcli.Success(),
)

imgs := []coder.Image{}
// OK - json output
c.Run(ctx, "coder images ls --output json").Assert(t,
tcli.Success(),
tcli.StdoutJSONUnmarshal(&imgs),
)

// Org not found
c.Run(ctx, "coder images ls --org doesntexist").Assert(t,
tcli.Error(),
tcli.StderrMatches(regexp.QuoteMeta("org name \"doesntexist\" not found\n\n")),
)
})
}
26 changes: 13 additions & 13 deletions coder-sdk/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ import (

// Image describes a Coder Image.
type Image struct {
ID string `json:"id"`
OrganizationID string `json:"organization_id"`
Repository string `json:"repository"`
Description string `json:"description"`
URL string `json:"url"` // User-supplied URL for image.
Registry *Registry `json:"registry"`
DefaultTag *ImageTag `json:"default_tag"`
DefaultCPUCores float32 `json:"default_cpu_cores"`
DefaultMemoryGB float32 `json:"default_memory_gb"`
DefaultDiskGB int `json:"default_disk_gb"`
Deprecated bool `json:"deprecated"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
ID string `json:"id" table:"-"`
OrganizationID string `json:"organization_id" table:"-"`
Repository string `json:"repository" table:"Repository"`
Description string `json:"description" table:"-"`
URL string `json:"url" table:"-"` // User-supplied URL for image.
Registry *Registry `json:"registry" table:"-"`
DefaultTag *ImageTag `json:"default_tag" table:"DefaultTag"`
DefaultCPUCores float32 `json:"default_cpu_cores" table:"DefaultCPUCores"`
DefaultMemoryGB float32 `json:"default_memory_gb" table:"DefaultMemoryGB"`
DefaultDiskGB int `json:"default_disk_gb" table:"DefaultDiskGB"`
Deprecated bool `json:"deprecated" table:"-"`
CreatedAt time.Time `json:"created_at" table:"-"`
UpdatedAt time.Time `json:"updated_at" table:"-"`
}

// NewRegistryRequest describes a docker registry used in importing an image.
Expand Down
4 changes: 4 additions & 0 deletions coder-sdk/tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ type ImageTag struct {
CreatedAt time.Time `json:"created_at" table:"-"`
}

func (i ImageTag) String() string {
return i.Tag
}

// OSRelease is the marshalled /etc/os-release file.
type OSRelease struct {
ID string `json:"id"`
Expand Down
1 change: 1 addition & 0 deletions docs/coder.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ coder provides a CLI for working with an existing Coder Enterprise installation
* [coder completion](coder_completion.md) - Generate completion script
* [coder config-ssh](coder_config-ssh.md) - Configure SSH to access Coder environments
* [coder envs](coder_envs.md) - Interact with Coder environments
* [coder images](coder_images.md) - Manage Coder images
* [coder login](coder_login.md) - Authenticate this client for future operations
* [coder logout](coder_logout.md) - Remove local authentication credentials if any exist
* [coder sh](coder_sh.md) - Open a shell and execute commands in a Coder environment
Expand Down
26 changes: 26 additions & 0 deletions docs/coder_images.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
## coder images

Manage Coder images

### Synopsis

Manage existing images and/or import new ones.

### Options

```
-h, --help help for images
--user string Specifies the user by email (default "me")
```

### Options inherited from parent commands

```
-v, --verbose show verbose output
```

### SEE ALSO

* [coder](coder.md) - coder provides a CLI for working with an existing Coder Enterprise installation
* [coder images ls](coder_images_ls.md) - list all images available to the active user

31 changes: 31 additions & 0 deletions docs/coder_images_ls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
## coder images ls

list all images available to the active user

### Synopsis

List all Coder images available to the active user.

```
coder images ls [flags]
```

### Options

```
-h, --help help for ls
--org string organization name
--output string human | json (default "human")
```

### Options inherited from parent commands

```
--user string Specifies the user by email (default "me")
-v, --verbose show verbose output
```

### SEE ALSO

* [coder images](coder_images.md) - Manage Coder images

1 change: 1 addition & 0 deletions internal/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func Make() *cobra.Command {
tokensCmd(),
resourceCmd(),
completionCmd(),
imgsCmd(),
genDocsCmd(app),
)
app.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "show verbose output")
Expand Down
88 changes: 88 additions & 0 deletions internal/cmd/images.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package cmd

import (
"encoding/json"
"os"

"cdr.dev/coder-cli/coder-sdk"
"cdr.dev/coder-cli/pkg/clog"
"cdr.dev/coder-cli/pkg/tablewriter"
"github.com/spf13/cobra"
"golang.org/x/xerrors"
)

func imgsCmd() *cobra.Command {
var user string

cmd := &cobra.Command{
Use: "images",
Short: "Manage Coder images",
Long: "Manage existing images and/or import new ones.",
}

cmd.PersistentFlags().StringVar(&user, "user", coder.Me, "Specifies the user by email")
cmd.AddCommand(lsImgsCommand(&user))
return cmd
}

func lsImgsCommand(user *string) *cobra.Command {
var (
orgName string
outputFmt string
)

cmd := &cobra.Command{
Use: "ls",
Short: "list all images available to the active user",
Long: "List all Coder images available to the active user.",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()

client, err := newClient(ctx)
if err != nil {
return err
}

imgs, err := getImgs(ctx, client,
getImgsConf{
email: *user,
orgName: orgName,
},
)

if err != nil {
return err
}

if len(imgs) < 1 {
clog.LogInfo("no images found")
imgs = []coder.Image{} // ensures that json output still marshals
}

switch outputFmt {
case jsonOutput:
enc := json.NewEncoder(os.Stdout)
// pretty print the json
enc.SetIndent("", "\t")
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice 💯


if err := enc.Encode(imgs); err != nil {
return xerrors.Errorf("write images as JSON: %w", err)
}
return nil
case humanOutput:
err = tablewriter.WriteTable(len(imgs), func(i int) interface{} {
return imgs[i]
})
if err != nil {
return xerrors.Errorf("write table: %w", err)
}
return nil
default:
return xerrors.Errorf("%q is not a supported value for --output", outputFmt)
}
},
}
cmd.Flags().StringVar(&orgName, "org", "", "organization name")
cmd.Flags().StringVar(&outputFmt, "output", humanOutput, "human | json")
return cmd
}