mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-03 18:20:27 +00:00
Expose docker image id and name
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
@@ -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) {
|
||||
@@ -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()
|
||||
|
||||
if !ok {
|
||||
return "", fmt.Errorf("no container found for PID %d", pid)
|
||||
}
|
||||
|
||||
return container.Name, nil
|
||||
func (m *dockerMapper) nameMapper() processMapper {
|
||||
return &dockerProcessMapper{m, "docker_name", func(c *docker.Container) string {
|
||||
return c.Name
|
||||
}}
|
||||
}
|
||||
|
||||
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 image.RepoTags[0]
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
@@ -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 {
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user