فهرست منبع

Implement quiet flag for ps command

Djordje Lukic 5 سال پیش
والد
کامیت
688e7e5deb
9فایلهای تغییر یافته به همراه169 افزوده شده و 25 حذف شده
  1. 3 3
      Makefile
  2. 11 0
      README.md
  3. 42 20
      cli/cmd/ps.go
  4. 96 0
      cli/cmd/ps_test.go
  5. 2 0
      cli/cmd/testdata/ps-out-quiet.golden
  6. 3 0
      cli/cmd/testdata/ps-out.golden
  7. 1 1
      cli/main.go
  8. 1 0
      go.mod
  9. 10 1
      tests/e2e/e2e.go

+ 3 - 3
Makefile

@@ -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

+ 11 - 0
README.md

@@ -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`.

+ 42 - 20
cli/cmd/ps.go

@@ -1,6 +1,7 @@
 package cmd
 
 import (
+	"context"
 	"fmt"
 	"os"
 	"text/tabwriter"
@@ -11,29 +12,50 @@ import (
 	"github.com/docker/api/client"
 )
 
+type psOpts struct {
+	quiet bool
+}
+
 // 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")
-		}
+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)
+		},
+	}
 
-		containers, err := c.ContainerService().List(ctx)
-		if err != nil {
-			return errors.Wrap(err, "fetch containers")
-		}
+	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")
+	}
 
-		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"
+	if opts.quiet {
 		for _, c := range containers {
-			fmt.Fprintf(w, format, c.ID, c.Image, c.Status, c.Command)
+			fmt.Println(c.ID)
 		}
-		return w.Flush()
-	},
+		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 - 0
cli/cmd/ps_test.go

@@ -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 - 0
cli/cmd/testdata/ps-out-quiet.golden

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

+ 3 - 0
cli/cmd/testdata/ps-out.golden

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

+ 1 - 1
cli/main.go

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

+ 1 - 0
go.mod

@@ -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
 )

+ 10 - 1
tests/e2e/e2e.go

@@ -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"))