Skip to content

Commit

Permalink
Merge pull request #95 from tomwilkie/docker-image-annot
Browse files Browse the repository at this point in the history
Expose Docker image IDs and Names
  • Loading branch information
tomwilkie committed May 26, 2015
2 parents c155782 + 18c38a6 commit 957d525
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 36 deletions.
4 changes: 1 addition & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,4 @@ deps:
github.com/golang/lint/golint \
github.com/fzipp/gocyclo \
github.com/mattn/goveralls \
github.com/mjibson/esc \
github.com/davecgh/go-spew/spew \
github.com/pmezard/go-difflib/difflib
github.com/mjibson/esc
2 changes: 2 additions & 0 deletions app/origin_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ func originNodeForProcess(node report.NodeMetadata) OriginNode {
for _, tuple := range []struct{ key, human string }{
{"docker_id", "Container ID"},
{"docker_name", "Container name"},
{"docker_image_id", "Container image ID"},
{"docker_image_name", "Container image name"},
{"cgroup", "cgroup"},
} {
if val, ok := node[tuple.key]; ok {
Expand Down
1 change: 1 addition & 0 deletions bin/test
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
echo "mode: count" > profile.cov
fail=0
for dir in $(find . -type f -name '*_test.go' | grep -v '^./experimental/' | xargs -n1 dirname | sort -u); do
go get -t $dir
output=$(mktemp cover.XXXXXXXXXX)
if ! go test -tags netgo -covermode=count -coverprofile=$output $dir ; then
fail=1
Expand Down
74 changes: 53 additions & 21 deletions probe/docker_process_mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ import (

type dockerMapper struct {
sync.RWMutex
d map[int]*docker.Container
procRoot string
containers map[int]*docker.Container
images map[string]*docker.APIImages
procRoot string
}

func newDockerMapper(procRoot string, interval time.Duration) *dockerMapper {
m := dockerMapper{
procRoot: procRoot,
d: map[int]*docker.Container{},
procRoot: procRoot,
containers: map[int]*docker.Container{},
}
m.update()
go m.loop(interval)
Expand All @@ -35,6 +36,7 @@ func (m *dockerMapper) loop(d time.Duration) {
type dockerClient interface {
ListContainers(docker.ListContainersOptions) ([]docker.APIContainers, error)
InspectContainer(string) (*docker.Container, error)
ListImages(docker.ListImagesOptions) ([]docker.APIImages, error)
}

func newRealDockerClient(endpoint string) (dockerClient, error) {
Expand Down Expand Up @@ -88,41 +90,71 @@ func (m *dockerMapper) update() {
}
}

imageList, err := client.ListImages(docker.ListImagesOptions{})
if err != nil {
log.Printf("docker mapper: %s", err)
return
}

imageMap := map[string]*docker.APIImages{}
for i := range imageList {
image := &imageList[i]
imageMap[image.ID] = image
}

m.Lock()
m.d = pmap
m.containers = pmap
m.images = imageMap
m.Unlock()
}

type dockerIDMapper struct {
type dockerProcessMapper struct {
*dockerMapper
key string
f func(*docker.Container) string
}

func (m dockerIDMapper) Key() string { return "docker_id" }
func (m dockerIDMapper) Map(pid uint) (string, error) {
func (m *dockerProcessMapper) Key() string { return m.key }
func (m *dockerProcessMapper) Map(pid uint) (string, error) {
m.RLock()
container, ok := m.d[int(pid)]
container, ok := m.containers[int(pid)]
m.RUnlock()

if !ok {
return "", fmt.Errorf("no container found for PID %d", pid)
}

return container.ID, nil
return m.f(container), nil
}

type dockerNameMapper struct {
*dockerMapper
func (m *dockerMapper) idMapper() processMapper {
return &dockerProcessMapper{m, "docker_id", func(c *docker.Container) string {
return c.ID
}}
}

func (m dockerNameMapper) Key() string { return "docker_name" }
func (m dockerNameMapper) Map(pid uint) (string, error) {
m.RLock()
container, ok := m.d[int(pid)]
m.RUnlock()
func (m *dockerMapper) nameMapper() processMapper {
return &dockerProcessMapper{m, "docker_name", func(c *docker.Container) string {
return c.Name
}}
}

if !ok {
return "", fmt.Errorf("no container found for PID %d", pid)
}
func (m *dockerMapper) imageIDMapper() processMapper {
return &dockerProcessMapper{m, "docker_image_id", func(c *docker.Container) string {
return c.Image
}}
}

func (m *dockerMapper) imageNameMapper() processMapper {
return &dockerProcessMapper{m, "docker_image_name", func(c *docker.Container) string {
m.RLock()
image, ok := m.images[c.Image]
m.RUnlock()

if !ok || len(image.RepoTags) == 0 {
return ""
}

return container.Name, nil
return image.RepoTags[0]
}}
}
39 changes: 28 additions & 11 deletions probe/docker_process_mapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,21 @@ import (
)

type mockDockerClient struct {
containers []docker.APIContainers
containerInfo map[string]*docker.Container
apiContainers []docker.APIContainers
containers map[string]*docker.Container
apiImages []docker.APIImages
}

func (m mockDockerClient) ListContainers(options docker.ListContainersOptions) ([]docker.APIContainers, error) {
return m.containers, nil
return m.apiContainers, nil
}

func (m mockDockerClient) InspectContainer(id string) (*docker.Container, error) {
return m.containerInfo[id], nil
return m.containers[id], nil
}

func (m mockDockerClient) ListImages(options docker.ListImagesOptions) ([]docker.APIImages, error) {
return m.apiImages, nil
}

func TestDockerProcessMapper(t *testing.T) {
Expand All @@ -41,24 +46,28 @@ func TestDockerProcessMapper(t *testing.T) {

newDockerClient = func(endpoint string) (dockerClient, error) {
return mockDockerClient{
containers: []docker.APIContainers{{ID: "foo"}},
containerInfo: map[string]*docker.Container{
apiContainers: []docker.APIContainers{{ID: "foo"}},
containers: map[string]*docker.Container{
"foo": {
ID: "foo",
Name: "bar",
Image: "baz",
State: docker.State{Pid: 1, Running: true},
},
},
apiImages: []docker.APIImages{{ID: "baz", RepoTags: []string{"tag"}}},
}, nil
}

dockerMapper := newDockerMapper("/proc", 10*time.Second)
dockerIDMapper := dockerIDMapper{dockerMapper}
dockerNameMapper := dockerNameMapper{dockerMapper}
dockerIDMapper := dockerMapper.idMapper()
dockerNameMapper := dockerMapper.nameMapper()
dockerImageIDMapper := dockerMapper.imageIDMapper()
dockerImageNameMapper := dockerMapper.imageNameMapper()

for pid, want := range map[uint]struct{ id, name string }{
1: {"foo", "bar"},
2: {"foo", "bar"},
for pid, want := range map[uint]struct{ id, name, imageID, imageName string }{
1: {"foo", "bar", "baz", "tag"},
2: {"foo", "bar", "baz", "tag"},
} {
haveID, err := dockerIDMapper.Map(pid)
if err != nil || want.id != haveID {
Expand All @@ -68,5 +77,13 @@ func TestDockerProcessMapper(t *testing.T) {
if err != nil || want.name != haveName {
t.Errorf("%d: want %q, have %q (%v)", pid, want.name, haveName, err)
}
haveImageID, err := dockerImageIDMapper.Map(pid)
if err != nil || want.imageID != haveImageID {
t.Errorf("%d: want %q, have %q (%v)", pid, want.imageID, haveImageID, err)
}
haveImageName, err := dockerImageNameMapper.Map(pid)
if err != nil || want.imageName != haveImageName {
t.Errorf("%d: want %q, have %q (%v)", pid, want.imageName, haveImageName, err)
}
}
}
7 changes: 6 additions & 1 deletion probe/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,12 @@ func main() {

if *dockerMapper {
docker := newDockerMapper(*procRoot, *dockerInterval)
pms = append(pms, &dockerIDMapper{docker}, &dockerNameMapper{docker})
pms = append(pms,
docker.idMapper(),
docker.nameMapper(),
docker.imageIDMapper(),
docker.imageNameMapper(),
)
}

log.Printf("listening on %s", *listen)
Expand Down

0 comments on commit 957d525

Please sign in to comment.