Преглед на файлове

Merge pull request #37131 from kolyshkin/top-opt

Optimize ContainerTop() aka docker top
Sebastiaan van Stijn преди 7 години
родител
ревизия
65bd038fc5
променени са 4 файла, в които са добавени 46 реда и са изтрити 2 реда
  1. 4 0
      daemon/daemon_linux_test.go
  2. 4 0
      daemon/daemon_test.go
  3. 4 0
      daemon/reload_test.go
  4. 34 2
      daemon/top_unix.go

+ 4 - 0
daemon/daemon_linux_test.go

@@ -229,6 +229,10 @@ func checkMounted(t *testing.T, p string, expect bool) {
 }
 }
 
 
 func TestRootMountCleanup(t *testing.T) {
 func TestRootMountCleanup(t *testing.T) {
+	if os.Getuid() != 0 {
+		t.Skip("root required")
+	}
+
 	t.Parallel()
 	t.Parallel()
 
 
 	testRoot, err := ioutil.TempDir("", t.Name())
 	testRoot, err := ioutil.TempDir("", t.Name())

+ 4 - 0
daemon/daemon_test.go

@@ -153,6 +153,10 @@ func TestValidContainerNames(t *testing.T) {
 }
 }
 
 
 func TestContainerInitDNS(t *testing.T) {
 func TestContainerInitDNS(t *testing.T) {
+	if os.Getuid() != 0 {
+		t.Skip("root required") // for chown
+	}
+
 	tmp, err := ioutil.TempDir("", "docker-container-test-")
 	tmp, err := ioutil.TempDir("", "docker-container-test-")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)

+ 4 - 0
daemon/reload_test.go

@@ -1,6 +1,7 @@
 package daemon // import "github.com/docker/docker/daemon"
 package daemon // import "github.com/docker/docker/daemon"
 
 
 import (
 import (
+	"os"
 	"reflect"
 	"reflect"
 	"sort"
 	"sort"
 	"testing"
 	"testing"
@@ -499,6 +500,9 @@ func TestDaemonDiscoveryReloadOnlyClusterAdvertise(t *testing.T) {
 }
 }
 
 
 func TestDaemonReloadNetworkDiagnosticPort(t *testing.T) {
 func TestDaemonReloadNetworkDiagnosticPort(t *testing.T) {
+	if os.Getuid() != 0 {
+		t.Skip("root required")
+	}
 	daemon := &Daemon{
 	daemon := &Daemon{
 		imageService: images.NewImageService(images.ImageServiceConfig{}),
 		imageService: images.NewImageService(images.ImageServiceConfig{}),
 	}
 	}

+ 34 - 2
daemon/top_unix.go

@@ -3,6 +3,7 @@
 package daemon // import "github.com/docker/docker/daemon"
 package daemon // import "github.com/docker/docker/daemon"
 
 
 import (
 import (
+	"bytes"
 	"context"
 	"context"
 	"fmt"
 	"fmt"
 	"os/exec"
 	"os/exec"
@@ -11,6 +12,8 @@ import (
 	"strings"
 	"strings"
 
 
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/errdefs"
+	"github.com/pkg/errors"
 )
 )
 
 
 func validatePSArgs(psArgs string) error {
 func validatePSArgs(psArgs string) error {
@@ -70,6 +73,7 @@ func parsePSOutput(output []byte, procs []uint32) (*container.ContainerTopOKBody
 	for i, name := range procList.Titles {
 	for i, name := range procList.Titles {
 		if name == "PID" {
 		if name == "PID" {
 			pidIndex = i
 			pidIndex = i
+			break
 		}
 		}
 	}
 	}
 	if pidIndex == -1 {
 	if pidIndex == -1 {
@@ -112,6 +116,20 @@ func parsePSOutput(output []byte, procs []uint32) (*container.ContainerTopOKBody
 	return procList, nil
 	return procList, nil
 }
 }
 
 
+// psPidsArg converts a slice of PIDs to a string consisting
+// of comma-separated list of PIDs prepended by "-q".
+// For example, psPidsArg([]uint32{1,2,3}) returns "-q1,2,3".
+func psPidsArg(pids []uint32) string {
+	b := []byte{'-', 'q'}
+	for i, p := range pids {
+		b = strconv.AppendUint(b, uint64(p), 10)
+		if i < len(pids)-1 {
+			b = append(b, ',')
+		}
+	}
+	return string(b)
+}
+
 // ContainerTop lists the processes running inside of the given
 // ContainerTop lists the processes running inside of the given
 // container by calling ps with the given args, or with the flags
 // container by calling ps with the given args, or with the flags
 // "-ef" if no args are given.  An error is returned if the container
 // "-ef" if no args are given.  An error is returned if the container
@@ -144,9 +162,23 @@ func (daemon *Daemon) ContainerTop(name string, psArgs string) (*container.Conta
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	output, err := exec.Command("ps", strings.Split(psArgs, " ")...).Output()
+	args := strings.Split(psArgs, " ")
+	pids := psPidsArg(procs)
+	output, err := exec.Command("ps", append(args, pids)...).Output()
 	if err != nil {
 	if err != nil {
-		return nil, fmt.Errorf("Error running ps: %v", err)
+		// some ps options (such as f) can't be used together with q,
+		// so retry without it
+		output, err = exec.Command("ps", args...).Output()
+		if err != nil {
+			if ee, ok := err.(*exec.ExitError); ok {
+				// first line of stderr shows why ps failed
+				line := bytes.SplitN(ee.Stderr, []byte{'\n'}, 2)
+				if len(line) > 0 && len(line[0]) > 0 {
+					err = errors.New(string(line[0]))
+				}
+			}
+			return nil, errdefs.System(errors.Wrap(err, "ps"))
+		}
 	}
 	}
 	procList, err := parsePSOutput(output, procs)
 	procList, err := parsePSOutput(output, procs)
 	if err != nil {
 	if err != nil {