8
8
"fmt"
9
9
"io"
10
10
"os"
11
+ "path"
11
12
"regexp"
12
13
"strings"
13
14
"syscall"
@@ -20,8 +21,11 @@ const (
20
21
// selfMountinfo is the path to the mountinfo path where we can find the container id in case cgroup namespace is preventing the use of /proc/self/cgroup
21
22
selfMountInfoPath = "/proc/self/mountinfo"
22
23
23
- // mountsPath is the path to the file listing all the mount points
24
- mountsPath = "/proc/mounts"
24
+ // defaultCgroupMountPath is the default path to the cgroup mount point.
25
+ defaultCgroupMountPath = "/sys/fs/cgroup"
26
+
27
+ // cgroupV1BaseController is the controller used to identify the container-id for cgroup v1
28
+ cgroupV1BaseController = "memory"
25
29
26
30
uuidSource = "[0-9a-f]{8}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{12}"
27
31
containerSource = "[0-9a-f]{64}"
51
55
expContainerID = regexp .MustCompile (fmt .Sprintf (`(%s|%s|%s)(?:.scope)?$` , uuidSource , containerSource , taskSource ))
52
56
53
57
cIDMountInfoRegexp = regexp .MustCompile (cIDRegexpStr )
58
+
59
+ // initContainerID initializes the container ID.
60
+ initContainerID = internalInitContainerID
54
61
)
55
62
56
63
// parseContainerID finds the first container ID reading from r and returns it.
@@ -109,55 +116,90 @@ func readMountinfo(path string) string {
109
116
return parseMountinfo (f )
110
117
}
111
118
112
- // isCgroupV1 checks if Cgroup V1 is used
113
- func isCgroupV1 (mountsPath string ) bool {
114
- f , err := os .Open (mountsPath )
119
+ func isHostCgroupNamespace () bool {
120
+ fi , err := os .Stat ("/proc/self/ns/cgroup" )
115
121
if err != nil {
116
122
return false
117
123
}
118
- defer f .Close ()
119
124
120
- scn := bufio .NewScanner (f )
125
+ inode := fi .Sys ().(* syscall.Stat_t ).Ino
126
+
127
+ return inode == hostCgroupNamespaceInode
128
+ }
129
+
130
+ // parseCgroupNodePath parses /proc/self/cgroup and returns a map of controller to its associated cgroup node path.
131
+ func parseCgroupNodePath (r io.Reader ) map [string ]string {
132
+ res := make (map [string ]string )
133
+ scn := bufio .NewScanner (r )
121
134
for scn .Scan () {
122
135
line := scn .Text ()
123
-
124
- tokens := strings .Fields (line )
125
- if len (tokens ) >= 3 {
126
- fsType := tokens [2 ]
127
- if fsType == "cgroup" {
128
- return true
129
- }
136
+ tokens := strings .Split (line , ":" )
137
+ if len (tokens ) != 3 {
138
+ continue
139
+ }
140
+ if tokens [1 ] == cgroupV1BaseController || tokens [1 ] == "" {
141
+ res [tokens [1 ]] = tokens [2 ]
130
142
}
131
143
}
132
-
133
- return false
144
+ return res
134
145
}
135
146
136
- func isHostCgroupNamespace () bool {
137
- fi , err := os .Stat ("/proc/self/ns/cgroup" )
147
+ // getCgroupInode returns the cgroup controller inode if it exists otherwise an empty string.
148
+ // The inode is prefixed by "in-" and is used by the agent to retrieve the container ID.
149
+ // For cgroup v1, we use the memory controller.
150
+ func getCgroupInode (cgroupMountPath , procSelfCgroupPath string ) string {
151
+ // Parse /proc/self/cgroup to retrieve the paths to the memory controller (cgroupv1) and the cgroup node (cgroupv2)
152
+ f , err := os .Open (procSelfCgroupPath )
138
153
if err != nil {
139
- return false
154
+ return ""
140
155
}
156
+ defer f .Close ()
157
+ cgroupControllersPaths := parseCgroupNodePath (f )
158
+ // Retrieve the cgroup inode from /sys/fs/cgroup+controller+cgroupNodePath
159
+ for _ , controller := range []string {cgroupV1BaseController , "" } {
160
+ cgroupNodePath , ok := cgroupControllersPaths [controller ]
161
+ if ! ok {
162
+ continue
163
+ }
164
+ inode := inodeForPath (path .Join (cgroupMountPath , controller , cgroupNodePath ))
165
+ if inode != "" {
166
+ return inode
167
+ }
168
+ }
169
+ return ""
170
+ }
141
171
142
- inode := fi .Sys ().(* syscall.Stat_t ).Ino
143
-
144
- return inode == hostCgroupNamespaceInode
172
+ // inodeForPath returns the inode for the provided path or empty on failure.
173
+ func inodeForPath (path string ) string {
174
+ fi , err := os .Stat (path )
175
+ if err != nil {
176
+ return ""
177
+ }
178
+ stats , ok := fi .Sys ().(* syscall.Stat_t )
179
+ if ! ok {
180
+ return ""
181
+ }
182
+ return fmt .Sprintf ("in-%d" , stats .Ino )
145
183
}
146
184
147
- // initContainerID initializes the container ID.
185
+ // internalInitContainerID initializes the container ID.
148
186
// It can either be provided by the user or read from cgroups.
149
- func initContainerID (userProvidedID string , cgroupFallback bool ) {
187
+ func internalInitContainerID (userProvidedID string , cgroupFallback bool ) {
150
188
initOnce .Do (func () {
151
189
if userProvidedID != "" {
152
190
containerID = userProvidedID
153
191
return
154
192
}
155
193
156
194
if cgroupFallback {
157
- if isCgroupV1 (mountsPath ) || isHostCgroupNamespace () {
195
+ isHostCgroupNs := isHostCgroupNamespace ()
196
+ if isHostCgroupNs {
158
197
containerID = readContainerID (cgroupPath )
159
- } else {
160
- containerID = readMountinfo (selfMountInfoPath )
198
+ return
199
+ }
200
+ containerID = readMountinfo (selfMountInfoPath )
201
+ if containerID != "" {
202
+ containerID = getCgroupInode (defaultCgroupMountPath , cgroupPath )
161
203
}
162
204
}
163
205
})
0 commit comments