Explorar o código

Reimplement lxc-ps

Instead of calling lxc-ps in top endpoint, we reimplement it by
calling ps and filter for pids running in a given container.
Johannes 'fish' Ziemke %!s(int64=11) %!d(string=hai) anos
pai
achega
4faba4fae7
Modificáronse 2 ficheiros con 96 adicións e 21 borrados
  1. 40 21
      server.go
  2. 56 0
      utils/utils.go

+ 40 - 21
server.go

@@ -1,7 +1,6 @@
 package docker
 package docker
 
 
 import (
 import (
-	"bufio"
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
@@ -690,34 +689,54 @@ func (srv *Server) ImageHistory(name string) ([]APIHistory, error) {
 
 
 func (srv *Server) ContainerTop(name, psArgs string) (*APITop, error) {
 func (srv *Server) ContainerTop(name, psArgs string) (*APITop, error) {
 	if container := srv.runtime.Get(name); container != nil {
 	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 {
 		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 {
 			if len(line) == 0 {
 				continue
 				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
 		return &procs, nil
 
 

+ 56 - 0
utils/utils.go

@@ -1117,3 +1117,59 @@ func CopyFile(src, dst string) (int64, error) {
 	defer df.Close()
 	defer df.Close()
 	return io.Copy(df, sf)
 	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
+}