Implement quiet flag for ps command

This commit is contained in:
Djordje Lukic 2020-05-06 09:37:52 +02:00
parent 28299c099d
commit 688e7e5deb
9 changed files with 174 additions and 30 deletions

View File

@ -53,7 +53,7 @@ test: ## Run unit tests
@docker build . \
--target test
cache-clear: # Clear the builder cache
cache-clear: ## Clear the builder cache
@docker builder prune --force --filter type=exec.cachemount --filter=unused-for=24h
lint: ## run linter(s)
@ -62,8 +62,8 @@ lint: ## run linter(s)
help: ## Show help
@echo Please specify a build target. The choices are:
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
@grep -E '^[0-9a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
FORCE:
.PHONY: all protos cli cross test cache-clear lint help
.PHONY: all protos cli e2e-local cross test cache-clear lint help

View File

@ -23,3 +23,14 @@ $ make
```
If you make changes to the `.proto` files, make sure to `make protos` to generate go code.
## Tests
To run unit tests:
```
make test
```
If you need to update a golden file simply do `go test ./... -test.update-golden`.

View File

@ -1,6 +1,7 @@
package cmd
import (
"context"
"fmt"
"os"
"text/tabwriter"
@ -11,29 +12,50 @@ import (
"github.com/docker/api/client"
)
// PsCommand lists containers
var PsCommand = cobra.Command{
Use: "ps",
Short: "List containers",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
c, err := client.New(ctx)
if err != nil {
return errors.Wrap(err, "cannot connect to backend")
}
containers, err := c.ContainerService().List(ctx)
if err != nil {
return errors.Wrap(err, "fetch containers")
}
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintf(w, "NAME\tIMAGE\tSTATUS\tCOMMAND\n")
format := "%s\t%s\t%s\t%s\n"
for _, c := range containers {
fmt.Fprintf(w, format, c.ID, c.Image, c.Status, c.Command)
}
return w.Flush()
},
type psOpts struct {
quiet bool
}
// PsCommand lists containers
func PsCommand() *cobra.Command {
var opts psOpts
cmd := &cobra.Command{
Use: "ps",
Short: "List containers",
RunE: func(cmd *cobra.Command, args []string) error {
return runPs(cmd.Context(), opts)
},
}
cmd.Flags().BoolVarP(&opts.quiet, "quiet", "q", false, "Only display IDs")
return cmd
}
func runPs(ctx context.Context, opts psOpts) error {
c, err := client.New(ctx)
if err != nil {
return errors.Wrap(err, "cannot connect to backend")
}
containers, err := c.ContainerService().List(ctx)
if err != nil {
return errors.Wrap(err, "fetch containers")
}
if opts.quiet {
for _, c := range containers {
fmt.Println(c.ID)
}
return nil
}
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintf(w, "NAME\tIMAGE\tSTATUS\tCOMMAND\n")
format := "%s\t%s\t%s\t%s\n"
for _, c := range containers {
fmt.Fprintf(w, format, c.ID, c.Image, c.Status, c.Command)
}
return w.Flush()
}

96
cli/cmd/ps_test.go Normal file
View File

@ -0,0 +1,96 @@
package cmd
import (
"context"
"io/ioutil"
"os"
"testing"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"gotest.tools/v3/assert"
"gotest.tools/v3/golden"
apicontext "github.com/docker/api/context"
"github.com/docker/api/context/store"
_ "github.com/docker/api/example"
)
type PsSuite struct {
suite.Suite
ctx context.Context
writer *os.File
reader *os.File
originalStdout *os.File
storeRoot string
}
func (sut *PsSuite) BeforeTest(suiteName, testName string) {
ctx := context.Background()
ctx = apicontext.WithCurrentContext(ctx, "example")
dir, err := ioutil.TempDir("", "store")
require.Nil(sut.T(), err)
s, err := store.New(
store.WithRoot(dir),
)
require.Nil(sut.T(), err)
err = s.Create("example", store.TypedContext{
Type: "example",
})
require.Nil(sut.T(), err)
sut.storeRoot = dir
ctx = store.WithContextStore(ctx, s)
sut.ctx = ctx
sut.originalStdout = os.Stdout
r, w, err := os.Pipe()
require.Nil(sut.T(), err)
os.Stdout = w
sut.writer = w
sut.reader = r
}
func (sut *PsSuite) getStdOut() string {
err := sut.writer.Close()
require.Nil(sut.T(), err)
out, _ := ioutil.ReadAll(sut.reader)
return string(out)
}
func (sut *PsSuite) AfterTest(suiteName, testName string) {
os.Stdout = sut.originalStdout
err := os.RemoveAll(sut.storeRoot)
require.Nil(sut.T(), err)
}
func (sut *PsSuite) TestPs() {
opts := psOpts{
quiet: false,
}
err := runPs(sut.ctx, opts)
assert.NilError(sut.T(), err)
golden.Assert(sut.T(), sut.getStdOut(), "ps-out.golden")
}
func (sut *PsSuite) TestPsQuiet() {
opts := psOpts{
quiet: true,
}
err := runPs(sut.ctx, opts)
assert.NilError(sut.T(), err)
golden.Assert(sut.T(), sut.getStdOut(), "ps-out-quiet.golden")
}
func TestPs(t *testing.T) {
suite.Run(t, new(PsSuite))
}

2
cli/cmd/testdata/ps-out-quiet.golden vendored Normal file
View File

@ -0,0 +1,2 @@
id
1234

3
cli/cmd/testdata/ps-out.golden vendored Normal file
View File

@ -0,0 +1,3 @@
NAME IMAGE STATUS COMMAND
id nginx
1234 alpine

View File

@ -96,7 +96,7 @@ func main() {
root.AddCommand(
cmd.ContextCommand(),
&cmd.PsCommand,
cmd.PsCommand(),
cmd.ServeCommand(),
run.Command(),
cmd.ExecCommand(),

1
go.mod
View File

@ -31,4 +31,5 @@ require (
golang.org/x/text v0.3.2 // indirect
google.golang.org/grpc v1.29.1
google.golang.org/protobuf v1.21.0
gotest.tools/v3 v3.0.2
)

View File

@ -7,8 +7,9 @@ import (
"github.com/robpike/filter"
f "github.com/docker/api/tests/framework"
g "github.com/onsi/gomega"
f "github.com/docker/api/tests/framework"
)
func main() {
@ -69,6 +70,14 @@ func main() {
g.Expect(lines[2]).To(g.ContainSubstring("1234 alpine"))
})
It("can run quiet ps command", func() {
output := f.NewDockerCommand("ps", "-q").ExecOrDie()
lines := lines(output)
g.Expect(len(lines)).To(g.Equal(2))
g.Expect(lines[0]).To(g.Equal("id"))
g.Expect(lines[1]).To(g.Equal("1234"))
})
It("can run 'run' command", func() {
output := f.NewDockerCommand("run", "nginx", "-p", "80:80").ExecOrDie()
g.Expect(output).To(g.ContainSubstring("Running container \"nginx\" with name"))