diff --git a/ci/integration/images_test.go b/ci/integration/images_test.go new file mode 100644 index 00000000..e796fd61 --- /dev/null +++ b/ci/integration/images_test.go @@ -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")), + ) + }) +} diff --git a/coder-sdk/image.go b/coder-sdk/image.go index 78fad426..2e656ea8 100644 --- a/coder-sdk/image.go +++ b/coder-sdk/image.go @@ -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. diff --git a/coder-sdk/tags.go b/coder-sdk/tags.go index 8c7282a9..f5384dc7 100644 --- a/coder-sdk/tags.go +++ b/coder-sdk/tags.go @@ -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"` diff --git a/docs/coder.md b/docs/coder.md index e268d312..d83ee5eb 100644 --- a/docs/coder.md +++ b/docs/coder.md @@ -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 diff --git a/docs/coder_images.md b/docs/coder_images.md new file mode 100644 index 00000000..386041c7 --- /dev/null +++ b/docs/coder_images.md @@ -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 + diff --git a/docs/coder_images_ls.md b/docs/coder_images_ls.md new file mode 100644 index 00000000..bfb646d5 --- /dev/null +++ b/docs/coder_images_ls.md @@ -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 + diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go index e7b8aa63..433266f8 100644 --- a/internal/cmd/cmd.go +++ b/internal/cmd/cmd.go @@ -35,6 +35,7 @@ func Make() *cobra.Command { tokensCmd(), resourceCmd(), completionCmd(), + imgsCmd(), genDocsCmd(app), ) app.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "show verbose output") diff --git a/internal/cmd/images.go b/internal/cmd/images.go new file mode 100644 index 00000000..8e9679ce --- /dev/null +++ b/internal/cmd/images.go @@ -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") + + 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 +} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy