浏览代码

Merge pull request #3168 from discordianfish/2464-replace-lxc-ps

Reimplement lxc-ps
Guillaume J. Charmes 11 年之前
父节点
当前提交
d9a1cc7e2b
共有 3 个文件被更改,包括 101 次插入27 次删除
  1. 5 6
      integration/api_test.go
  2. 40 21
      server.go
  3. 56 0
      utils/utils.go

+ 5 - 6
integration/api_test.go

@@ -432,7 +432,6 @@ func TestGetContainersChanges(t *testing.T) {
 }
 
 func TestGetContainersTop(t *testing.T) {
-	t.Skip("Fixme. Skipping test for now. Reported error when testing using dind: 'api_test.go:527: Expected 2 processes, found 0.'")
 	eng := NewTestEngine(t)
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 	srv := mkServerFromEngine(eng, t)
@@ -475,7 +474,7 @@ func TestGetContainersTop(t *testing.T) {
 	})
 
 	r := httptest.NewRecorder()
-	req, err := http.NewRequest("GET", "/"+containerID+"/top?ps_args=u", bytes.NewReader([]byte{}))
+	req, err := http.NewRequest("GET", "/containers/"+containerID+"/top?ps_args=aux", nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -498,11 +497,11 @@ func TestGetContainersTop(t *testing.T) {
 	if len(procs.Processes) != 2 {
 		t.Fatalf("Expected 2 processes, found %d.", len(procs.Processes))
 	}
-	if procs.Processes[0][10] != "/bin/sh" && procs.Processes[0][10] != "cat" {
-		t.Fatalf("Expected `cat` or `/bin/sh`, found %s.", procs.Processes[0][10])
+	if procs.Processes[0][10] != "/bin/sh -c cat" {
+		t.Fatalf("Expected `/bin/sh -c cat`, found %s.", procs.Processes[0][10])
 	}
-	if procs.Processes[1][10] != "/bin/sh" && procs.Processes[1][10] != "cat" {
-		t.Fatalf("Expected `cat` or `/bin/sh`, found %s.", procs.Processes[1][10])
+	if procs.Processes[1][10] != "/bin/sh -c cat" {
+		t.Fatalf("Expected `/bin/sh -c cat`, found %s.", procs.Processes[1][10])
 	}
 }
 

+ 40 - 21
server.go

@@ -1,7 +1,6 @@
 package docker
 
 import (
-	"bufio"
 	"encoding/json"
 	"errors"
 	"fmt"
@@ -690,34 +689,54 @@ func (srv *Server) ImageHistory(name string) ([]APIHistory, error) {
 
 func (srv *Server) ContainerTop(name, psArgs string) (*APITop, error) {
 	if container := srv.runtime.Get(name); container != nil {
-		output, err := exec.Command("lxc-ps", "--name", container.ID, "--", psArgs).CombinedOutput()
+		pids, err := utils.GetPidsForContainer(container.ID)
 		if err != nil {
-			return nil, fmt.Errorf("lxc-ps: %s (%s)", err, output)
+			return nil, err
+		}
+		if len(psArgs) == 0 {
+			psArgs = "-ef"
+		}
+		output, err := exec.Command("ps", psArgs).Output()
+		if err != nil {
+			return nil, fmt.Errorf("Error running ps: %s", err)
+		}
+
+		lines := strings.Split(string(output), "\n")
+		header := strings.Fields(lines[0])
+		procs := APITop{
+			Titles: header,
+		}
+
+		pidIndex := -1
+		for i, name := range header {
+			if name == "PID" {
+				pidIndex = i
+			}
 		}
-		procs := APITop{}
-		for i, line := range strings.Split(string(output), "\n") {
+		if pidIndex == -1 {
+			return nil, errors.New("Couldn't find PID field in ps output")
+		}
+
+		for _, line := range lines[1:] {
 			if len(line) == 0 {
 				continue
 			}
-			words := []string{}
-			scanner := bufio.NewScanner(strings.NewReader(line))
-			scanner.Split(bufio.ScanWords)
-			if !scanner.Scan() {
-				return nil, fmt.Errorf("Wrong output using lxc-ps")
+			fields := strings.Fields(line)
+			p, err := strconv.Atoi(fields[pidIndex])
+			if err != nil {
+				return nil, fmt.Errorf("Unexpected pid '%s': %s", fields[pidIndex], err)
 			}
-			// no scanner.Text because we skip container id
-			for scanner.Scan() {
-				if i != 0 && len(words) == len(procs.Titles) {
-					words[len(words)-1] = fmt.Sprintf("%s %s", words[len(words)-1], scanner.Text())
-				} else {
-					words = append(words, scanner.Text())
+
+			for _, pid := range pids {
+				if pid == p {
+					// Make sure number of fields equals number of header titles
+					// merging "overhanging" fields
+					processes := fields[:len(procs.Titles)-1]
+					processes = append(processes, strings.Join(fields[len(procs.Titles)-1:], " "))
+
+					procs.Processes = append(procs.Processes, processes)
 				}
 			}
-			if i == 0 {
-				procs.Titles = words
-			} else {
-				procs.Processes = append(procs.Processes, words)
-			}
 		}
 		return &procs, nil
 

+ 56 - 0
utils/utils.go

@@ -1117,3 +1117,59 @@ func CopyFile(src, dst string) (int64, error) {
 	defer df.Close()
 	return io.Copy(df, sf)
 }
+
+// Returns the relative path to the cgroup docker is running in.
+func GetThisCgroup(cgroupType string) (string, error) {
+	output, err := ioutil.ReadFile("/proc/self/cgroup")
+	if err != nil {
+		return "", err
+	}
+	for _, line := range strings.Split(string(output), "\n") {
+		parts := strings.Split(line, ":")
+		// any type used by docker should work
+		if parts[1] == cgroupType {
+			return parts[2], nil
+		}
+	}
+	return "", fmt.Errorf("cgroup '%s' not found in /proc/self/cgroup", cgroupType)
+}
+
+// Returns a list of pids for the given container.
+func GetPidsForContainer(id string) ([]int, error) {
+	pids := []int{}
+
+	// memory is chosen randomly, any cgroup used by docker works
+	cgroupType := "memory"
+
+	cgroupRoot, err := FindCgroupMountpoint(cgroupType)
+	if err != nil {
+		return pids, err
+	}
+
+	cgroupThis, err := GetThisCgroup(cgroupType)
+	if err != nil {
+		return pids, err
+	}
+
+	filename := filepath.Join(cgroupRoot, cgroupThis, id, "tasks")
+	if _, err := os.Stat(filename); os.IsNotExist(err) {
+		// With more recent lxc versions use, cgroup will be in lxc/
+		filename = filepath.Join(cgroupRoot, cgroupThis, "lxc", id, "tasks")
+	}
+
+	output, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return pids, err
+	}
+	for _, p := range strings.Split(string(output), "\n") {
+		if len(p) == 0 {
+			continue
+		}
+		pid, err := strconv.Atoi(p)
+		if err != nil {
+			return pids, fmt.Errorf("Invalid pid '%s': %s", p, err)
+		}
+		pids = append(pids, pid)
+	}
+	return pids, nil
+}