Skip to content

Fix GetCurrentContainerID() and corresponding test #345

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 59 additions & 28 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ package dockergen

import (
"bufio"
"bytes"
"fmt"
"io"
"os"
"regexp"
"sync"
"fmt"
"github.com/fsouza/go-dockerclient"

docker "github.com/fsouza/go-dockerclient"
)

var (
Expand Down Expand Up @@ -158,57 +161,85 @@ type Docker struct {
CurrentContainerID string
}

func GetCurrentContainerID() string {
filepaths := []string{"/proc/self/cgroup", "/proc/self/mountinfo"}
// GetCurrentContainerID attempts to extract the current container ID from the provided file paths.
// If no files paths are provided, it will default to /proc/1/cpuset, /proc/self/cgroup and /proc/self/mountinfo.
// It attempts to match the HOSTNAME first then use the fallback method, and returns with the first valid match.
func GetCurrentContainerID(filepaths ...string) (id string) {
if len(filepaths) == 0 {
filepaths = []string{"/proc/1/cpuset", "/proc/self/cgroup", "/proc/self/mountinfo"}
}

var files []io.Reader

for _, filepath := range filepaths {
file, err := os.Open(filepath)
if err != nil {
continue
}
reader := bufio.NewReader(file)
scanner := bufio.NewScanner(reader)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
_, lines, err := bufio.ScanLines([]byte(scanner.Text()), true)
if err == nil {
strLines := string(lines)
if id := matchDockerCurrentContainerID(strLines); id != "" {
return id
} else if id := matchECSCurrentContainerID(strLines); id != "" {
return id
}
defer file.Close()
files = append(files, file)
}

reader := io.MultiReader(files...)
var buffer bytes.Buffer
tee := io.TeeReader(reader, &buffer)

// We try to match a 64 character hex string starting with the hostname first
scanner := bufio.NewScanner(tee)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
_, lines, err := bufio.ScanLines([]byte(scanner.Text()), true)
if err == nil {
strLines := string(lines)
if id = matchContainerIDWithHostname(strLines); len(id) == 64 {
return
}
}
}

return ""
// If we didn't get any ID that matches the hostname, fall back to matching the first 64 character hex string
scanner = bufio.NewScanner(&buffer)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
_, lines, err := bufio.ScanLines([]byte(scanner.Text()), true)
if err == nil {
strLines := string(lines)
if id = matchContainerID(strLines); len(id) == 64 {
return
}
}
}

return
}

func matchDockerCurrentContainerID(lines string) string {
func matchContainerIDWithHostname(lines string) string {
hostname := os.Getenv("HOSTNAME")
regex := fmt.Sprintf("(%s[[:alnum:]]{52})", hostname)
re := regexp.MustCompilePOSIX(regex)
re := regexp.MustCompilePOSIX("^[[:alnum:]]{12}$")

if re.MatchString(lines) {
submatches := re.FindStringSubmatch(string(lines))
containerID := submatches[1]
if re.MatchString(hostname) {
regex := fmt.Sprintf("(%s[[:alnum:]]{52})", hostname)
re := regexp.MustCompilePOSIX(regex)

return containerID
if re.MatchString(lines) {
submatches := re.FindStringSubmatch(string(lines))
containerID := submatches[1]

return containerID
}
}
return ""
}

func matchECSCurrentContainerID(lines string) string {
regex := "/ecs\\/[^\\/]+\\/(.+)$"
func matchContainerID(lines string) string {
regex := "([[:alnum:]]{64})"
re := regexp.MustCompilePOSIX(regex)

if re.MatchString(string(lines)) {
if re.MatchString(lines) {
submatches := re.FindStringSubmatch(string(lines))
containerID := submatches[1]

return containerID
}

return ""
}
120 changes: 83 additions & 37 deletions context_test.go
Original file line number Diff line number Diff line change
@@ -1,52 +1,98 @@
package dockergen

import (
"fmt"
"io/ioutil"
"log"
"os"
"testing"
)

func TestGetCurrentContainerID(t *testing.T) {
currentContainerID := GetCurrentContainerID()
hostname := os.Getenv("HOSTNAME")
defer os.Setenv("HOSTNAME", hostname)

if len(currentContainerID) != 0 && len(currentContainerID) != 64 {
t.Fail()
ids := []string{
"0fa939e22e6938e7517f663de83e79a5087a18b1b997a36e0c933a917cddb295",
"e881f8c51a72db7da515e9d5cab8ed105b869579eb9923fdcf4ee80933160802",
"eede6bd9e72f5d783a4bfb845bd71f310e974cb26987328a5d15704e23a8d6cb",
}
}

func TestGetCurrentContainerID_ECS(t *testing.T) {
cgroup :=
`9:perf_event:/ecs/628967a1-46b4-4a8a-84ff-605128f4679e/3c94e08259a6235781bb65f3dec91150c92e9d414ecc410d6245687392d3900f
8:memory:/ecs/628967a1-46b4-4a8a-84ff-605128f4679e/3c94e08259a6235781bb65f3dec91150c92e9d414ecc410d6245687392d3900f
7:hugetlb:/ecs/628967a1-46b4-4a8a-84ff-605128f4679e/3c94e08259a6235781bb65f3dec91150c92e9d414ecc410d6245687392d3900f
6:freezer:/ecs/628967a1-46b4-4a8a-84ff-605128f4679e/3c94e08259a6235781bb65f3dec91150c92e9d414ecc410d6245687392d3900f
5:devices:/ecs/628967a1-46b4-4a8a-84ff-605128f4679e/3c94e08259a6235781bb65f3dec91150c92e9d414ecc410d6245687392d3900f
4:cpuset:/ecs/628967a1-46b4-4a8a-84ff-605128f4679e/3c94e08259a6235781bb65f3dec91150c92e9d414ecc410d6245687392d3900f
3:cpuacct:/ecs/628967a1-46b4-4a8a-84ff-605128f4679e/3c94e08259a6235781bb65f3dec91150c92e9d414ecc410d6245687392d3900f
2:cpu:/ecs/628967a1-46b4-4a8a-84ff-605128f4679e/3c94e08259a6235781bb65f3dec91150c92e9d414ecc410d6245687392d3900f
1:blkio:/ecs/628967a1-46b4-4a8a-84ff-605128f4679e/3c94e08259a6235781bb65f3dec91150c92e9d414ecc410d6245687392d3900f`

if got, exp := matchECSCurrentContainerID(cgroup), "3c94e08259a6235781bb65f3dec91150c92e9d414ecc410d6245687392d3900f"; got != exp {
t.Fatalf("id mismatch: got %v, exp %v", got, exp)
contents := map[string]string{
"cpuset": fmt.Sprintf("/docker/%v", ids[0]),
"cgroup": fmt.Sprintf(`13:name=systemd:/docker-ce/docker/%[1]v
12:pids:/docker-ce/docker/%[1]v
11:hugetlb:/docker-ce/docker/%[1]v
10:net_prio:/docker-ce/docker/%[1]v
9:perf_event:/docker-ce/docker/%[1]v
8:net_cls:/docker-ce/docker/%[1]v
7:freezer:/docker-ce/docker/%[1]v
6:devices:/docker-ce/docker/%[1]v
5:memory:/docker-ce/docker/%[1]v
4:blkio:/docker-ce/docker/%[1]v
3:cpuacct:/docker-ce/docker/%[1]v
2:cpu:/docker-ce/docker/%[1]v
1:cpuset:/docker-ce/docker/%[1]v`, ids[1]),
"mountinfo": fmt.Sprintf(`705 661 0:96 / / rw,relatime master:192 - overlay overlay rw,lowerdir=/var/lib/docker/overlay2/l/CVAK3VWZFQCUGTLHRJHPEKJ4UL:/var/lib/docker/overlay2/l/XMJZ73SKVWVECU7TJCOY62F3H2:/var/lib/docker/overlay2/l/AVNBXO52GHDY3MZU3R4RCSNMCE:/var/lib/docker/overlay2/l/L4IJZ33E6NAMXJ5W3SKJSVX5TS:/var/lib/docker/overlay2/l/JXAUAD5TDJCXA34FGS6NYGUZKT:/var/lib/docker/overlay2/l/TBQDSAFKBSTFMUS3QCFWN5NRLB:/var/lib/docker/overlay2/l/MXIUXRGB7MU4Y4NUNZE2VXTXIN:/var/lib/docker/overlay2/l/HN7E4YWJG7TMG7BXLZTGICTBOA:/var/lib/docker/overlay2/l/65XQPC72Z5VRY4THGASZIQXS57:/var/lib/docker/overlay2/l/BVQKC7LU6D7MOSLBDKFHY7YSO3:/var/lib/docker/overlay2/l/R4GGX3SFPMLXTNM3WKMVOKDTOY:/var/lib/docker/overlay2/l/VHGYTU73JLTRCGX45ZF2VGW4FK,upperdir=/var/lib/docker/overlay2/e1fab975d5ffd51474b11a964c82c3bfda1c0e82aec6845a1f12c8150bf61419/diff,workdir=/var/lib/docker/overlay2/e1fab975d5ffd51474b11a964c82c3bfda1c0e82aec6845a1f12c8150bf61419/work,index=off
706 705 0:105 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw
707 705 0:106 / /dev rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755,inode64
708 707 0:107 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666
709 705 0:108 / /sys ro,nosuid,nodev,noexec,relatime - sysfs sysfs ro
710 709 0:25 / /sys/fs/cgroup ro,nosuid,nodev,noexec,relatime - cgroup2 cgroup rw,nsdelegate,memory_recursiveprot
711 707 0:104 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw
712 707 0:109 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,size=65536k,inode64
713 705 8:3 /var/lib/docker/containers/%[1]v/resolv.conf /etc/resolv.conf rw,relatime - ext4 /dev/sda3 rw
714 705 8:3 /var/lib/docker/containers/%[1]v/hostname /etc/hostname rw,relatime - ext4 /dev/sda3 rw
715 705 8:3 /var/lib/docker/containers/%[1]v/hosts /etc/hosts rw,relatime - ext4 /dev/sda3 rw
716 705 8:3 /var/lib/docker/volumes/ca8074e1a2eb12edc86c59c5108bb48c31bb7ace4b90beb0da8137a9baa45812/_data /etc/nginx/certs rw,relatime master:1 - ext4 /dev/sda3 rw
717 705 8:3 /var/lib/docker/volumes/2cf8a52c907469a56f6e2cc7d1959d74a4dd04131e7edcd53eaf909db28f770f/_data /etc/nginx/dhparam rw,relatime master:1 - ext4 /dev/sda3 rw
662 707 0:107 /0 /dev/console rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666
663 706 0:105 /bus /proc/bus ro,relatime - proc proc rw
664 706 0:105 /fs /proc/fs ro,relatime - proc proc rw
665 706 0:105 /irq /proc/irq ro,relatime - proc proc rw
666 706 0:105 /sys /proc/sys ro,relatime - proc proc rw
667 706 0:105 /sysrq-trigger /proc/sysrq-trigger ro,relatime - proc proc rw
668 706 0:110 / /proc/acpi ro,relatime - tmpfs tmpfs ro,inode64
669 706 0:106 /null /proc/kcore rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755,inode64
670 706 0:106 /null /proc/keys rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755,inode64
671 706 0:106 /null /proc/latency_stats rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755,inode64
672 706 0:106 /null /proc/timer_list rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755,inode64
673 706 0:106 /null /proc/sched_debug rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755,inode64
674 706 0:111 / /proc/scsi ro,relatime - tmpfs tmpfs ro,inode64
675 709 0:112 / /sys/firmware ro,relatime - tmpfs tmpfs ro,inode64`, ids[2]),
}

keys := []string{
"cpuset",
"cgroup",
"mountinfo",
}
}

func TestGetCurrentContainerID_DockerCE(t *testing.T) {
cgroup :=
`13:name=systemd:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb
12:pids:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb
11:hugetlb:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb
10:net_prio:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb
9:perf_event:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb
8:net_cls:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb
7:freezer:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb
6:devices:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb
5:memory:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb
4:blkio:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb
3:cpuacct:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb
2:cpu:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb
1:cpuset:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb`

if got, exp := matchDockerCurrentContainerID(cgroup), "18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb"; got != exp {
t.Fatalf("id mismatch: got %v, exp %v", got, exp)
var filepaths []string
// Create temporary files with test content
for _, key := range keys {
file, err := ioutil.TempFile("", key)
if err != nil {
log.Fatal(err)
}
defer os.Remove(file.Name())
if _, err = file.WriteString(contents[key]); err != nil {
log.Fatal(err)
}
filepaths = append(filepaths, file.Name())
}

// Each time the HOSTNAME is set to a short form ID, GetCurrentContainerID() should match and return the corresponding full ID
for _, id := range ids {
os.Setenv("HOSTNAME", id[0:12])
if got, exp := GetCurrentContainerID(filepaths...), id; got != exp {
t.Fatalf("id mismatch with HOSTNAME %v: got %v, exp %v", id[0:12], got, exp)
}
}

// If the Hostname isn't a short form ID, we should match the first valid ID (64 character hex string) instead
os.Setenv("HOSTNAME", "customhostname")
if got, exp := GetCurrentContainerID(filepaths...), ids[0]; got != exp {
t.Fatalf("id mismatch with custom HOSTNAME: got %v, exp %v", got, exp)
}
}