Optimize ContainerTop() aka docker top
| ... | ... |
@@ -229,6 +229,10 @@ func checkMounted(t *testing.T, p string, expect bool) {
|
| 229 | 229 |
} |
| 230 | 230 |
|
| 231 | 231 |
func TestRootMountCleanup(t *testing.T) {
|
| 232 |
+ if os.Getuid() != 0 {
|
|
| 233 |
+ t.Skip("root required")
|
|
| 234 |
+ } |
|
| 235 |
+ |
|
| 232 | 236 |
t.Parallel() |
| 233 | 237 |
|
| 234 | 238 |
testRoot, err := ioutil.TempDir("", t.Name())
|
| ... | ... |
@@ -153,6 +153,10 @@ func TestValidContainerNames(t *testing.T) {
|
| 153 | 153 |
} |
| 154 | 154 |
|
| 155 | 155 |
func TestContainerInitDNS(t *testing.T) {
|
| 156 |
+ if os.Getuid() != 0 {
|
|
| 157 |
+ t.Skip("root required") // for chown
|
|
| 158 |
+ } |
|
| 159 |
+ |
|
| 156 | 160 |
tmp, err := ioutil.TempDir("", "docker-container-test-")
|
| 157 | 161 |
if err != nil {
|
| 158 | 162 |
t.Fatal(err) |
| ... | ... |
@@ -1,6 +1,7 @@ |
| 1 | 1 |
package daemon // import "github.com/docker/docker/daemon" |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "os" |
|
| 4 | 5 |
"reflect" |
| 5 | 6 |
"sort" |
| 6 | 7 |
"testing" |
| ... | ... |
@@ -499,6 +500,9 @@ func TestDaemonDiscoveryReloadOnlyClusterAdvertise(t *testing.T) {
|
| 499 | 499 |
} |
| 500 | 500 |
|
| 501 | 501 |
func TestDaemonReloadNetworkDiagnosticPort(t *testing.T) {
|
| 502 |
+ if os.Getuid() != 0 {
|
|
| 503 |
+ t.Skip("root required")
|
|
| 504 |
+ } |
|
| 502 | 505 |
daemon := &Daemon{
|
| 503 | 506 |
imageService: images.NewImageService(images.ImageServiceConfig{}),
|
| 504 | 507 |
} |
| ... | ... |
@@ -3,6 +3,7 @@ |
| 3 | 3 |
package daemon // import "github.com/docker/docker/daemon" |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 |
+ "bytes" |
|
| 6 | 7 |
"context" |
| 7 | 8 |
"fmt" |
| 8 | 9 |
"os/exec" |
| ... | ... |
@@ -11,6 +12,8 @@ import ( |
| 11 | 11 |
"strings" |
| 12 | 12 |
|
| 13 | 13 |
"github.com/docker/docker/api/types/container" |
| 14 |
+ "github.com/docker/docker/errdefs" |
|
| 15 |
+ "github.com/pkg/errors" |
|
| 14 | 16 |
) |
| 15 | 17 |
|
| 16 | 18 |
func validatePSArgs(psArgs string) error {
|
| ... | ... |
@@ -70,6 +73,7 @@ func parsePSOutput(output []byte, procs []uint32) (*container.ContainerTopOKBody |
| 70 | 70 |
for i, name := range procList.Titles {
|
| 71 | 71 |
if name == "PID" {
|
| 72 | 72 |
pidIndex = i |
| 73 |
+ break |
|
| 73 | 74 |
} |
| 74 | 75 |
} |
| 75 | 76 |
if pidIndex == -1 {
|
| ... | ... |
@@ -112,6 +116,20 @@ func parsePSOutput(output []byte, procs []uint32) (*container.ContainerTopOKBody |
| 112 | 112 |
return procList, nil |
| 113 | 113 |
} |
| 114 | 114 |
|
| 115 |
+// psPidsArg converts a slice of PIDs to a string consisting |
|
| 116 |
+// of comma-separated list of PIDs prepended by "-q". |
|
| 117 |
+// For example, psPidsArg([]uint32{1,2,3}) returns "-q1,2,3".
|
|
| 118 |
+func psPidsArg(pids []uint32) string {
|
|
| 119 |
+ b := []byte{'-', 'q'}
|
|
| 120 |
+ for i, p := range pids {
|
|
| 121 |
+ b = strconv.AppendUint(b, uint64(p), 10) |
|
| 122 |
+ if i < len(pids)-1 {
|
|
| 123 |
+ b = append(b, ',') |
|
| 124 |
+ } |
|
| 125 |
+ } |
|
| 126 |
+ return string(b) |
|
| 127 |
+} |
|
| 128 |
+ |
|
| 115 | 129 |
// ContainerTop lists the processes running inside of the given |
| 116 | 130 |
// container by calling ps with the given args, or with the flags |
| 117 | 131 |
// "-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 |
| 144 | 144 |
return nil, err |
| 145 | 145 |
} |
| 146 | 146 |
|
| 147 |
- output, err := exec.Command("ps", strings.Split(psArgs, " ")...).Output()
|
|
| 147 |
+ args := strings.Split(psArgs, " ") |
|
| 148 |
+ pids := psPidsArg(procs) |
|
| 149 |
+ output, err := exec.Command("ps", append(args, pids)...).Output()
|
|
| 148 | 150 |
if err != nil {
|
| 149 |
- return nil, fmt.Errorf("Error running ps: %v", err)
|
|
| 151 |
+ // some ps options (such as f) can't be used together with q, |
|
| 152 |
+ // so retry without it |
|
| 153 |
+ output, err = exec.Command("ps", args...).Output()
|
|
| 154 |
+ if err != nil {
|
|
| 155 |
+ if ee, ok := err.(*exec.ExitError); ok {
|
|
| 156 |
+ // first line of stderr shows why ps failed |
|
| 157 |
+ line := bytes.SplitN(ee.Stderr, []byte{'\n'}, 2)
|
|
| 158 |
+ if len(line) > 0 && len(line[0]) > 0 {
|
|
| 159 |
+ err = errors.New(string(line[0])) |
|
| 160 |
+ } |
|
| 161 |
+ } |
|
| 162 |
+ return nil, errdefs.System(errors.Wrap(err, "ps")) |
|
| 163 |
+ } |
|
| 150 | 164 |
} |
| 151 | 165 |
procList, err := parsePSOutput(output, procs) |
| 152 | 166 |
if err != nil {
|