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

Backport of cli: Add -json and -t flags to namespace status command into release/1.5.x #16492

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 .changelog/16442.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
cli: Added `-json` and `-t` flag to `namespace status` command
```
33 changes: 31 additions & 2 deletions command/namespace_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,26 @@ Usage: nomad namespace status [options] <namespace>

General Options:

` + generalOptionsUsage(usageOptsDefault|usageOptsNoNamespace)
` + generalOptionsUsage(usageOptsDefault|usageOptsNoNamespace) + `

Status Specific Options:

-json
Output the latest namespace status information in a JSON format.

-t
Format and display namespace status information using a Go template.
`

return strings.TrimSpace(helpText)
}

func (c *NamespaceStatusCommand) AutocompleteFlags() complete.Flags {
return c.Meta.AutocompleteFlags(FlagSetClient)
return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
complete.Flags{
"-json": complete.PredictNothing,
"-t": complete.PredictAnything,
})
}

func (c *NamespaceStatusCommand) AutocompleteArgs() complete.Predictor {
Expand All @@ -44,7 +57,12 @@ func (c *NamespaceStatusCommand) Synopsis() string {
func (c *NamespaceStatusCommand) Name() string { return "namespace status" }

func (c *NamespaceStatusCommand) Run(args []string) int {
var json bool
var tmpl string

flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
flags.BoolVar(&json, "json", false, "")
flags.StringVar(&tmpl, "t", "", "")
flags.Usage = func() { c.Ui.Output(c.Help()) }

if err := flags.Parse(args); err != nil {
Expand Down Expand Up @@ -80,6 +98,17 @@ func (c *NamespaceStatusCommand) Run(args []string) int {
return 1
}

if json || len(tmpl) > 0 {
out, err := Format(json, tmpl, ns)
if err != nil {
c.Ui.Error(err.Error())
return 1
}

c.Ui.Output(out)
return 0
}

c.Ui.Output(formatNamespaceBasics(ns))

if len(ns.Meta) > 0 {
Expand Down
96 changes: 54 additions & 42 deletions command/namespace_status_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package command

import (
"encoding/json"
"strings"
"testing"

"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/ci"
"github.com/mitchellh/cli"
"github.com/posener/complete"
"github.com/shoenig/test/must"
"github.com/stretchr/testify/assert"
)

Expand All @@ -22,24 +24,21 @@ func TestNamespaceStatusCommand_Fails(t *testing.T) {
cmd := &NamespaceStatusCommand{Meta: Meta{Ui: ui}}

// Fails on misuse
if code := cmd.Run([]string{"some", "bad", "args"}); code != 1 {
t.Fatalf("expected exit code 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, commandErrorText(cmd)) {
t.Fatalf("expected help output, got: %s", out)
}
code := cmd.Run([]string{"some", "bad", "args"})
must.One(t, code)

must.StrContains(t, ui.ErrorWriter.String(), commandErrorText(cmd))

ui.ErrorWriter.Reset()

if code := cmd.Run([]string{"-address=nope", "foo"}); code != 1 {
t.Fatalf("expected exit code 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "retrieving namespace") {
t.Fatalf("connection error, got: %s", out)
}
code = cmd.Run([]string{"-address=nope", "foo"})
must.One(t, code)

must.StrContains(t, ui.ErrorWriter.String(), "retrieving namespace")
ui.ErrorWriter.Reset()
}

func TestNamespaceStatusCommand_Good(t *testing.T) {
func TestNamespaceStatusCommand_Run(t *testing.T) {
ci.Parallel(t)

// Create a server
Expand All @@ -54,21 +53,42 @@ func TestNamespaceStatusCommand_Good(t *testing.T) {
Name: "foo",
}
_, err := client.Namespaces().Register(ns, nil)
assert.Nil(t, err)
must.NoError(t, err)

// Check status on namespace
if code := cmd.Run([]string{"-address=" + url, ns.Name}); code != 0 {
t.Fatalf("expected exit 0, got: %d; %v", code, ui.ErrorWriter.String())
}
code := cmd.Run([]string{"-address=" + url, ns.Name})
must.Zero(t, code)

// Check for basic spec
out := ui.OutputWriter.String()
if !strings.Contains(out, "= foo") {
t.Fatalf("expected quota, got: %s", out)
}

ui.OutputWriter.Reset()

// List json
code = cmd.Run([]string{"-address=" + url, "-json", ns.Name})
must.Zero(t, code)

outJson := api.Namespace{}
err = json.Unmarshal(ui.OutputWriter.Bytes(), &outJson)
must.NoError(t, err)

ui.OutputWriter.Reset()

// Go template to format the output
code = cmd.Run([]string{"-address=" + url, "-t", "{{.Name}}", ns.Name})
must.Zero(t, code)

out = ui.OutputWriter.String()
must.StrContains(t, out, "foo")

ui.OutputWriter.Reset()
ui.ErrorWriter.Reset()
}

func TestNamespaceStatusCommand_Good_Quota(t *testing.T) {
func TestNamespaceStatusCommand_Run_Quota(t *testing.T) {
ci.Parallel(t)

// Create a server
Expand Down Expand Up @@ -96,25 +116,21 @@ func TestNamespaceStatusCommand_Good_Quota(t *testing.T) {
assert.Nil(t, err)

// Check status on namespace
if code := cmd.Run([]string{"-address=" + url, ns.Name}); code != 0 {
t.Fatalf("expected exit 0, got: %d; %v", code, ui.ErrorWriter.String())
}
code := cmd.Run([]string{"-address=" + url, ns.Name})
must.Zero(t, code)

// Check for basic spec
out := ui.OutputWriter.String()
if !strings.Contains(out, "= foo") {
t.Fatalf("expected quota, got: %s", out)
}

// Check for basic spec
must.StrContains(t, out, "= foo")

// Check for usage
if !strings.Contains(out, "0 / 100") {
t.Fatalf("expected quota, got: %s", out)
}
must.StrContains(t, out, "0 / 100")

}

func TestNamespaceStatusCommand_AutocompleteArgs(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)

srv, client, url := testServer(t, true, nil)
defer srv.Shutdown()
Expand All @@ -127,14 +143,14 @@ func TestNamespaceStatusCommand_AutocompleteArgs(t *testing.T) {
Name: "foo",
}
_, err := client.Namespaces().Register(ns, nil)
assert.Nil(err)
must.NoError(t, err)

args := complete.Args{Last: "f"}
predictor := cmd.AutocompleteArgs()

res := predictor.Predict(args)
assert.Equal(1, len(res))
assert.Equal(ns.Name, res[0])
must.One(t, len(res))
must.StrContains(t, ns.Name, res[0])
}

// This test should demonstrate the behavior of a namespace
Expand All @@ -154,27 +170,23 @@ func TestNamespaceStatusCommand_NamespaceMatchesPrefix(t *testing.T) {
// Create a namespace that uses foo as a prefix
ns := &api.Namespace{Name: "fooBar"}
_, err := client.Namespaces().Register(ns, nil)
assert.Nil(t, err)
must.NoError(t, err)

// Create a foo namespace
ns2 := &api.Namespace{Name: "foo"}
_, err = client.Namespaces().Register(ns2, nil)
assert.Nil(t, err)
must.NoError(t, err)

// Adding a NS after to prevent sort from creating
// false successes
ns = &api.Namespace{Name: "fooBaz"}
_, err = client.Namespaces().Register(ns, nil)
assert.Nil(t, err)
must.NoError(t, err)

// Check status on namespace
code := cmd.Run([]string{"-address=" + url, ns2.Name})
if code != 0 {
t.Fatalf("expected exit 0, got: %d; %v", code, ui.ErrorWriter.String())
}
must.Zero(t, code)

// Check to ensure we got the proper foo
out := ui.OutputWriter.String()
if !strings.Contains(out, "= foo\n") {
t.Fatalf("expected namespace foo, got: %s", out)
}
must.StrContains(t, ui.OutputWriter.String(), "= foo\n")
}
26 changes: 26 additions & 0 deletions website/content/docs/commands/namespace/status.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ that has a capability associated with the namespace.

@include 'general_options_no_namespace.mdx'

## Status Options

- `-json` : Output the namespace status in its JSON format.
- `-t` : Format and display the namespace status using a Go template.

## Examples

View the status of a namespace:
Expand All @@ -46,3 +51,24 @@ Quota Limits
Region CPU Usage Memory Usage
global 500 / 2500 256 / 2000
```

The `-json` flag can be used to get the namespace status in json format:

```shell-session
$ nomad namespace status -json default
{
"Capabilities": null,
"CreateIndex": 1,
"Description": "Default shared namespace",
"Meta": null,
"ModifyIndex": 1,
"Name": "default",
"Quota": ""
}

Or use the `-t` flag to format and display the status using a Go template:

```shell-session
$ nomad namespace status -t {{.Description}} default
Default shared namespace
```