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

Environment subcommands #89

Closed
wants to merge 14 commits into from
Prev Previous commit
Next Next commit
Add table output to envs ls
  • Loading branch information
cmoog committed Aug 2, 2020
commit 2cc7d2d7411f7b55d8adf7058fa365414b714f9e
2 changes: 1 addition & 1 deletion ci/integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func TestCoderCLI(t *testing.T) {
headlessLogin(ctx, t, c)

c.Run(ctx, "coder envs").Assert(t,
tcli.Success(),
tcli.Error(),
)

c.Run(ctx, "coder envs ls").Assert(t,
Expand Down
12 changes: 11 additions & 1 deletion cmd/coder/envs.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"fmt"

"cdr.dev/coder-cli/internal/x/xtabwriter"
"github.com/urfave/cli"
)

Expand All @@ -11,6 +12,7 @@ func makeEnvsCommand() cli.Command {
Name: "envs",
Usage: "Interact with Coder environments",
Description: "Perform operations on the Coder environments owned by the active user.",
Action: exitHelp,
Subcommands: []cli.Command{
{
Name: "ls",
Expand All @@ -21,9 +23,17 @@ func makeEnvsCommand() cli.Command {
entClient := requireAuth()
envs := getEnvs(entClient)

w := xtabwriter.NewWriter()
if len(envs) > 0 {
_, err := fmt.Fprintln(w, xtabwriter.StructFieldNames(envs[0]))
requireSuccess(err, "failed to write header: %v", err)
}
for _, env := range envs {
fmt.Println(env.Name)
_, err := fmt.Fprintln(w, xtabwriter.StructValues(env))
requireSuccess(err, "failed to write row: %v", err)
}
err := w.Flush()
requireSuccess(err, "failed to flush tab writer: %v", err)
},
Flags: nil,
},
Expand Down
5 changes: 5 additions & 0 deletions cmd/coder/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func main() {
flog.Fatal("command %q not found", s)
}
app.Email = "support@coder.com"
app.Action = exitHelp

app.Commands = []cli.Command{
makeLoginCmd(),
Expand All @@ -63,3 +64,7 @@ func requireSuccess(err error, msg string, args ...interface{}) {
flog.Fatal(msg, args...)
}
}

func exitHelp(c *cli.Context) {
cli.ShowCommandHelpAndExit(c, c.Command.FullName(), 1)
}
1 change: 1 addition & 0 deletions cmd/coder/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func makeSecretsCmd() cli.Command {
Name: "secrets",
Usage: "Interact with Coder Secrets",
Description: "Interact with secrets objects owned by the active user.",
Action: exitHelp,
Subcommands: []cli.Command{
{
Name: "ls",
Expand Down
5 changes: 3 additions & 2 deletions cmd/coder/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ import (
func makeUsersCmd() cli.Command {
var output string
return cli.Command{
Name: "users",
Usage: "Interact with Coder user accounts",
Name: "users",
Usage: "Interact with Coder user accounts",
Action: exitHelp,
Subcommands: []cli.Command{
{
Name: "ls",
Expand Down
24 changes: 22 additions & 2 deletions internal/entclient/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,33 @@ import (
"context"
"time"

"cdr.dev/coder-cli/internal/x/xjson"
"nhooyr.io/websocket"
)

// Environment describes a Coder environment
type Environment struct {
Name string `json:"name"`
ID string `json:"id"`
ID string `json:"id" tab:"-"`
Name string `json:"name"`
ImageID string `json:"image_id" tab:"-"`
ImageTag string `json:"image_tag"`
OrganizationID string `json:"organization_id" tab:"-"`
UserID string `json:"user_id" tab:"-"`
LastBuiltAt time.Time `json:"last_built_at" tab:"-"`
CPUCores float32 `json:"cpu_cores"`
MemoryGB int `json:"memory_gb"`
DiskGB int `json:"disk_gb"`
GPUs int `json:"gpus"`
Updating bool `json:"updating"`
RebuildMessages []struct {
Text string `json:"text"`
Required bool `json:"required"`
} `json:"rebuild_messages" tab:"-"`
CreatedAt time.Time `json:"created_at" tab:"-"`
UpdatedAt time.Time `json:"updated_at" tab:"-"`
LastOpenedAt time.Time `json:"last_opened_at" tab:"-"`
LastConnectionAt time.Time `json:"last_connection_at" tab:"-"`
AutoOffThreshold xjson.Duration `json:"auto_off_threshold" tab:"-"`
}

// Envs gets the list of environments owned by the authenticated user
Expand Down
33 changes: 33 additions & 0 deletions internal/x/xjson/duration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package xjson

import (
"encoding/json"
"strconv"
"time"
)

// Duration is a time.Duration that marshals to millisecond precision.
// Most javascript applications expect durations to be in milliseconds.
type Duration time.Duration

// MarshalJSON marshals the duration to millisecond precision.
func (d Duration) MarshalJSON() ([]byte, error) {
du := time.Duration(d)
return json.Marshal(du.Milliseconds())
}

// UnmarshalJSON unmarshals a millisecond-precision integer to
// a time.Duration.
func (d *Duration) UnmarshalJSON(b []byte) error {
i, err := strconv.ParseInt(string(b), 10, 64)
if err != nil {
return err
}

*d = Duration(time.Duration(i) * time.Millisecond)
return nil
}

func (d Duration) String() string {
return time.Duration(d).String()
}
2 changes: 1 addition & 1 deletion internal/x/xtabwriter/tabwriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func StructValues(data interface{}) string {
if shouldHideField(v.Type().Field(i)) {
continue
}
s.WriteString(fmt.Sprintf("%s\t", v.Field(i).Interface()))
s.WriteString(fmt.Sprintf("%v\t", v.Field(i).Interface()))
}
return s.String()
}
Expand Down