diff --git a/apis/server/container_bridge.go b/apis/server/container_bridge.go index 84bd4308f..fde31bec4 100644 --- a/apis/server/container_bridge.go +++ b/apis/server/container_bridge.go @@ -320,7 +320,8 @@ func (s *Server) upgradeContainer(ctx context.Context, rw http.ResponseWriter, r func (s *Server) topContainer(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { name := mux.Vars(req)["name"] - procList, err := s.ContainerMgr.Top(ctx, name, req.Form.Get("ps_args")) + query := req.URL.Query() + procList, err := s.ContainerMgr.Top(ctx, name, query.Get("ps_args")) if err != nil { return err } diff --git a/apis/swagger.yml b/apis/swagger.yml index 6b0dff6da..524d05899 100644 --- a/apis/swagger.yml +++ b/apis/swagger.yml @@ -650,7 +650,7 @@ paths: tags: ["Container"] /containers/{id}/top: - post: + get: summary: "Display the running processes of a container" operationId: "ContainerTop" parameters: diff --git a/cli/top.go b/cli/top.go index 1ac495e2f..67169ceba 100644 --- a/cli/top.go +++ b/cli/top.go @@ -33,6 +33,13 @@ func (top *TopCommand) Init(c *Cli) { }, Example: topExamples(), } + top.addFlags() +} + +// addFlags adds flags for specific command. +func (top *TopCommand) addFlags() { + flagSet := top.cmd.Flags() + flagSet.SetInterspersed(false) } // runTop is the entry of top command. diff --git a/test/api_container_top_test.go b/test/api_container_top_test.go index 111619b55..8edbeb8fd 100644 --- a/test/api_container_top_test.go +++ b/test/api_container_top_test.go @@ -1,7 +1,14 @@ package main import ( + "encoding/json" + "io" + "io/ioutil" + "net/url" + + "github.com/alibaba/pouch/apis/types" "github.com/alibaba/pouch/test/environment" + "github.com/alibaba/pouch/test/request" "github.com/go-check/check" ) @@ -17,6 +24,65 @@ func init() { func (suite *APIContainerTopSuite) SetUpTest(c *check.C) { SkipIfFalse(c, environment.IsLinux) - // TODO: missing code - helpwantedForMissingCase(c, "container api top cases") + PullImage(c, busyboxImage) +} + +// TestTopContainer is to verify the correctness of pouch top command. +func (suite *APIContainerTopSuite) TestTopContainer(c *check.C) { + cname := "TestTop" + + CreateBusyboxContainerOk(c, cname) + defer DelContainerForceMultyTime(c, cname) + + StartContainerOk(c, cname) + + resp, err := request.Get("/containers/" + cname + "/top") + c.Assert(err, check.IsNil) + + response := types.ContainerProcessList{} + err = json.NewDecoder(resp.Body).Decode(&response) + if resp != nil && resp.Body != nil { + // close body ReadCloser to make Transport reuse the connection + io.CopyN(ioutil.Discard, resp.Body, 512) + resp.Body.Close() + } + + c.Assert(err, check.IsNil) + c.Assert(response, check.NotNil) + c.Assert(response.Titles[0], check.Equals, "UID") + + if len(response.Processes) != 1 { + c.Fatalf("unexpected processes length %d expected %d", len(response.Processes), 1) + } +} + +// TestTopContainerWithOptions is to verify the correctness of pouch top command with ps options. +func (suite *APIContainerTopSuite) TestTopContainerWithOptions(c *check.C) { + cname := "TestTopWithOptions" + + CreateBusyboxContainerOk(c, cname) + defer DelContainerForceMultyTime(c, cname) + + StartContainerOk(c, cname) + + q := url.Values{} + q.Add("ps_args", "-aux") + query := request.WithQuery(q) + resp, err := request.Get("/containers/"+cname+"/top", query) + c.Assert(err, check.IsNil) + + response := types.ContainerProcessList{} + err = json.NewDecoder(resp.Body).Decode(&response) + if resp != nil && resp.Body != nil { + // close body ReadCloser to make Transport reuse the connection + io.CopyN(ioutil.Discard, resp.Body, 512) + resp.Body.Close() + } + c.Assert(err, check.IsNil) + c.Assert(response, check.NotNil) + c.Assert(response.Titles[0], check.Equals, "USER") + + if len(response.Processes) != 1 { + c.Fatalf("unexpected processes length %d expected %d", len(response.Processes), 1) + } } diff --git a/test/cli_top_test.go b/test/cli_top_test.go index d8f4a65de..b8d29f9e3 100644 --- a/test/cli_top_test.go +++ b/test/cli_top_test.go @@ -63,7 +63,32 @@ func (suite *PouchTopSuite) TestTopContainer(c *check.C) { res.Assert(c, icmd.Success) expectString := "UIDPIDPPID" - if out := util.TrimAllSpaceAndNewline(res.Combined()); !strings.Contains(out, expectString) { + if out := util.TrimAllSpaceAndNewline(res.Combined()); !strings.HasPrefix(out, expectString) { + c.Fatalf("unexpected output %s expected %s", out, expectString) + } + expectString = "top" + if out := util.TrimAllSpaceAndNewline(res.Combined()); !strings.HasSuffix(out, expectString) { + c.Fatalf("unexpected output %s expected %s", out, expectString) + } +} + +// TestTopContainerWithOptions is to verify the correctness of pouch top command with ps options. +func (suite *PouchTopSuite) TestTopContainerWithOptions(c *check.C) { + name := "TestTopContainerWithOptions" + + res := command.PouchRun("run", "-d", "-m", "300M", "--name", name, busyboxImage, "top") + defer DelContainerForceMultyTime(c, name) + res.Assert(c, icmd.Success) + + res = command.PouchRun("top", name, "-aux") + res.Assert(c, icmd.Success) + + expectString := "USERPID" + if out := util.TrimAllSpaceAndNewline(res.Combined()); !strings.HasPrefix(out, expectString) { + c.Fatalf("unexpected output %s expected %s", out, expectString) + } + expectString = "top" + if out := util.TrimAllSpaceAndNewline(res.Combined()); !strings.HasSuffix(out, expectString) { c.Fatalf("unexpected output %s expected %s", out, expectString) } }