Skip to content

Commit da4354f

Browse files
authored
Merge pull request #356 from nginx-proxy/fix-355
Fix container ID extraction from /proc/self/mountinfo file
2 parents 1d0e791 + 0a3bb6e commit da4354f

File tree

2 files changed

+83
-50
lines changed

2 files changed

+83
-50
lines changed

context.go

+34-38
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ package dockergen
22

33
import (
44
"bufio"
5-
"bytes"
65
"fmt"
7-
"io"
86
"os"
97
"regexp"
108
"sync"
@@ -169,43 +167,42 @@ func GetCurrentContainerID(filepaths ...string) (id string) {
169167
filepaths = []string{"/proc/1/cpuset", "/proc/self/cgroup", "/proc/self/mountinfo"}
170168
}
171169

172-
var files []io.Reader
173-
170+
// We try to match a 64 character hex string starting with the hostname first
174171
for _, filepath := range filepaths {
175172
file, err := os.Open(filepath)
176173
if err != nil {
177174
continue
178175
}
179176
defer file.Close()
180-
files = append(files, file)
181-
}
182-
183-
reader := io.MultiReader(files...)
184-
var buffer bytes.Buffer
185-
tee := io.TeeReader(reader, &buffer)
186-
187-
// We try to match a 64 character hex string starting with the hostname first
188-
scanner := bufio.NewScanner(tee)
189-
scanner.Split(bufio.ScanLines)
190-
for scanner.Scan() {
191-
_, lines, err := bufio.ScanLines([]byte(scanner.Text()), true)
192-
if err == nil {
193-
strLines := string(lines)
194-
if id = matchContainerIDWithHostname(strLines); len(id) == 64 {
195-
return
177+
scanner := bufio.NewScanner(file)
178+
scanner.Split(bufio.ScanLines)
179+
for scanner.Scan() {
180+
_, lines, err := bufio.ScanLines([]byte(scanner.Text()), true)
181+
if err == nil {
182+
strLines := string(lines)
183+
if id = matchContainerIDWithHostname(strLines); len(id) == 64 {
184+
return
185+
}
196186
}
197187
}
198188
}
199189

200190
// If we didn't get any ID that matches the hostname, fall back to matching the first 64 character hex string
201-
scanner = bufio.NewScanner(&buffer)
202-
scanner.Split(bufio.ScanLines)
203-
for scanner.Scan() {
204-
_, lines, err := bufio.ScanLines([]byte(scanner.Text()), true)
205-
if err == nil {
206-
strLines := string(lines)
207-
if id = matchContainerID(strLines); len(id) == 64 {
208-
return
191+
for _, filepath := range filepaths {
192+
file, err := os.Open(filepath)
193+
if err != nil {
194+
continue
195+
}
196+
defer file.Close()
197+
scanner := bufio.NewScanner(file)
198+
scanner.Split(bufio.ScanLines)
199+
for scanner.Scan() {
200+
_, lines, err := bufio.ScanLines([]byte(scanner.Text()), true)
201+
if err == nil {
202+
strLines := string(lines)
203+
if id = matchContainerID("([[:alnum:]]{64})", strLines); len(id) == 64 {
204+
return
205+
}
209206
}
210207
}
211208
}
@@ -219,22 +216,21 @@ func matchContainerIDWithHostname(lines string) string {
219216

220217
if re.MatchString(hostname) {
221218
regex := fmt.Sprintf("(%s[[:alnum:]]{52})", hostname)
222-
re := regexp.MustCompilePOSIX(regex)
223219

224-
if re.MatchString(lines) {
225-
submatches := re.FindStringSubmatch(string(lines))
226-
containerID := submatches[1]
227-
228-
return containerID
229-
}
220+
return matchContainerID(regex, lines)
230221
}
231222
return ""
232223
}
233224

234-
func matchContainerID(lines string) string {
235-
regex := "([[:alnum:]]{64})"
236-
re := regexp.MustCompilePOSIX(regex)
225+
func matchContainerID(regex, lines string) string {
226+
// Attempt to detect if we're on a line from a /proc/<pid>/mountinfo file and modify the regexp accordingly
227+
// https://www.kernel.org/doc/Documentation/filesystems/proc.txt section 3.5
228+
re := regexp.MustCompilePOSIX("^[0-9]+ [0-9]+ [0-9]+:[0-9]+ /")
229+
if re.MatchString(lines) {
230+
regex = fmt.Sprintf("containers/%v", regex)
231+
}
237232

233+
re = regexp.MustCompilePOSIX(regex)
238234
if re.MatchString(lines) {
239235
submatches := re.FindStringSubmatch(string(lines))
240236
containerID := submatches[1]

context_test.go

+49-12
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,20 @@ import (
88
"testing"
99
)
1010

11-
func TestGetCurrentContainerID(t *testing.T) {
12-
hostname := os.Getenv("HOSTNAME")
13-
defer os.Setenv("HOSTNAME", hostname)
14-
15-
ids := []string{
11+
var (
12+
ids = []string{
1613
"0fa939e22e6938e7517f663de83e79a5087a18b1b997a36e0c933a917cddb295",
1714
"e881f8c51a72db7da515e9d5cab8ed105b869579eb9923fdcf4ee80933160802",
1815
"eede6bd9e72f5d783a4bfb845bd71f310e974cb26987328a5d15704e23a8d6cb",
1916
}
2017

21-
contents := map[string]string{
18+
fileKeys = []string{
19+
"cpuset",
20+
"cgroup",
21+
"mountinfo",
22+
}
23+
24+
contents = map[string]string{
2225
"cpuset": fmt.Sprintf("/docker/%v", ids[0]),
2326
"cgroup": fmt.Sprintf(`13:name=systemd:/docker-ce/docker/%[1]v
2427
12:pids:/docker-ce/docker/%[1]v
@@ -61,16 +64,15 @@ func TestGetCurrentContainerID(t *testing.T) {
6164
674 706 0:111 / /proc/scsi ro,relatime - tmpfs tmpfs ro,inode64
6265
675 709 0:112 / /sys/firmware ro,relatime - tmpfs tmpfs ro,inode64`, ids[2]),
6366
}
67+
)
6468

65-
keys := []string{
66-
"cpuset",
67-
"cgroup",
68-
"mountinfo",
69-
}
69+
func TestGetCurrentContainerID(t *testing.T) {
70+
hostname := os.Getenv("HOSTNAME")
71+
defer os.Setenv("HOSTNAME", hostname)
7072

7173
var filepaths []string
7274
// Create temporary files with test content
73-
for _, key := range keys {
75+
for _, key := range fileKeys {
7476
file, err := ioutil.TempFile("", key)
7577
if err != nil {
7678
log.Fatal(err)
@@ -96,3 +98,38 @@ func TestGetCurrentContainerID(t *testing.T) {
9698
t.Fatalf("id mismatch with custom HOSTNAME: got %v, exp %v", got, exp)
9799
}
98100
}
101+
102+
func TestGetCurrentContainerIDMountInfo(t *testing.T) {
103+
// Test specific to cases like https://github.com/nginx-proxy/docker-gen/issues/355
104+
// where only the /proc/<pid>/mountinfo file contains information
105+
hostname := os.Getenv("HOSTNAME")
106+
defer os.Setenv("HOSTNAME", hostname)
107+
os.Setenv("HOSTNAME", "customhostname")
108+
109+
id := ids[2]
110+
111+
content := map[string]string{
112+
"cpuset": "/",
113+
"cgroup": "0::/",
114+
"mountinfo": contents["mountinfo"],
115+
}
116+
117+
var filepaths []string
118+
// Create temporary files with test content
119+
for _, key := range fileKeys {
120+
file, err := ioutil.TempFile("", key)
121+
if err != nil {
122+
log.Fatal(err)
123+
}
124+
defer os.Remove(file.Name())
125+
if _, err = file.WriteString(content[key]); err != nil {
126+
log.Fatal(err)
127+
}
128+
filepaths = append(filepaths, file.Name())
129+
}
130+
131+
// We should match the correct 64 characters long ID in mountinfo, not the first encountered
132+
if got, exp := GetCurrentContainerID(filepaths...), id; got != exp {
133+
t.Fatalf("id mismatch on mountinfo: got %v, exp %v", got, exp)
134+
}
135+
}

0 commit comments

Comments
 (0)