forked from shuveb/containers-the-hard-way
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ps.go
161 lines (147 loc) · 4.36 KB
/
ps.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package main
import (
"bufio"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
)
type runningContainerInfo struct {
containerId string
image string
command string
pid int
}
/*
This isn't a great implementation and can possibly be simplified
using regex. But for now, here we are. This function gets the
current mount points, figures out which image is mounted for a
given container ID, looks it up in our images database which we
maintain and returns the image and tag information.
*/
func getDistribution(containerID string) (string, error) {
var lines []string
file, err := os.Open("/proc/mounts")
if err != nil {
fmt.Println("Unable to read /proc/mounts")
return "", err
}
defer file.Close()
scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
for _, line := range lines {
if strings.Contains(line, containerID) {
parts := strings.Split(line, " ")
for _, part := range parts {
if strings.Contains(part, "lowerdir=") {
options := strings.Split(part, ",")
for _, option := range options {
if strings.Contains(option, "lowerdir=") {
imagesPath := getGockerImagesPath()
leaderString := "lowerdir=" + imagesPath + "/"
trailerString := option[len(leaderString):]
imageID := trailerString[:12]
image, tag := getImageAndTagForHash(imageID)
return fmt.Sprintf("%s:%s", image, tag), nil
}
}
}
}
}
}
return "", nil
}
func getRunningContainerInfoForId(containerID string) (runningContainerInfo, error) {
container := runningContainerInfo{}
var procs []string
basePath := "/sys/fs/cgroup/cpu/gocker"
file, err := os.Open(basePath + "/" + containerID + "/cgroup.procs")
if err != nil {
fmt.Println("Unable to read cgroup.procs")
return container, err
}
defer file.Close()
scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
procs = append(procs, scanner.Text())
}
if len(procs) > 0 {
pid, err := strconv.Atoi(procs[len(procs)-1])
if err != nil {
fmt.Println("Unable to read PID")
return container, err
}
cmd, err := os.Readlink("/proc/" + strconv.Itoa(pid) + "/exe")
containerMntPath := getGockerContainersPath() + "/" + containerID + "/fs/mnt"
realContainerMntPath, err := filepath.EvalSymlinks(containerMntPath)
if err != nil {
fmt.Println("Unable to resolve path")
return container, err
}
if err != nil {
fmt.Println("Unable to read command link.")
return container, err
}
image, _ := getDistribution(containerID)
container = runningContainerInfo{
containerId: containerID,
image: image,
command: cmd[len(realContainerMntPath):],
pid: pid,
}
}
return container, nil
}
/*
Get the list of running container IDs.
Implementation logic:
- Gocker creates multiple folders in the /sys/fs/cgroup hierarchy
- For example, for setting cpu limits, gocker uses /sys/fs/cgroup/cpu/gocker
- Inside that folder are folders one each for currently running containers
- Those folder names are the container IDs we create.
- getContainerInfoForId() does more work. It gathers more information about running
containers. See struct runningContainerInfo for details.
- Inside each of those folders is a "cgroup.procs" file that has the list
of PIDs of processes inside of that container. From the PID, we can
get the mounted path from which the process was started. From that
mounted path, we can get the image of the containers since containers
are mounted via the overlay file system.
*/
func getRunningContainers() ([]runningContainerInfo, error) {
var containers []runningContainerInfo
basePath := "/sys/fs/cgroup/cpu/gocker"
entries, err := ioutil.ReadDir(basePath)
if os.IsNotExist(err) {
return containers, nil
} else {
if err != nil {
return nil, err
} else {
for _, entry := range entries {
if entry.IsDir() {
container, _ := getRunningContainerInfoForId(entry.Name())
if container.pid > 0 {
containers = append(containers, container)
}
}
}
return containers, nil
}
}
}
func printRunningContainers() {
containers, err := getRunningContainers()
if err != nil {
os.Exit(1)
}
fmt.Println("CONTAINER ID\tIMAGE\t\tCOMMAND")
for _, container := range containers {
fmt.Printf("%s\t%s\t%s\n", container.containerId, container.image, container.command)
}
}