diff --git a/container/docker/factory.go b/container/docker/factory.go index edd1817b..ec5c26e0 100644 --- a/container/docker/factory.go +++ b/container/docker/factory.go @@ -134,17 +134,19 @@ func FullContainerName(dockerId string) string { } // Docker handles all containers under /docker -func (self *dockerFactory) CanHandle(name string) (bool, error) { +func (self *dockerFactory) CanHandleAndAccept(name string) (bool, bool, error) { + // docker factory accepts all containers it can handle. + canAccept := true // Check if the container is known to docker and it is active. id := ContainerNameToDockerId(name) // We assume that if Inspect fails then the container is not known to docker. ctnr, err := self.client.InspectContainer(id) if err != nil || !ctnr.State.Running { - return false, fmt.Errorf("error inspecting container: %v", err) + return false, canAccept, fmt.Errorf("error inspecting container: %v", err) } - return true, nil + return true, canAccept, nil } func parseDockerVersion(full_version_string string) ([]int, error) { diff --git a/container/factory.go b/container/factory.go index 3b9cea1a..04a26b17 100644 --- a/container/factory.go +++ b/container/factory.go @@ -22,11 +22,11 @@ import ( ) type ContainerHandlerFactory interface { - // Create a new ContainerHandler using this factory. CanHandle() must have returned true. - NewContainerHandler(name string) (ContainerHandler, error) + // Create a new ContainerHandler using this factory. CanHandleAndAccept() must have returned true. + NewContainerHandler(name string) (c ContainerHandler, err error) - // Returns whether this factory can handle the specified container. - CanHandle(name string) (bool, error) + // Returns whether this factory can handle and accept the specified container. + CanHandleAndAccept(name string) (handle bool, accept bool, err error) // Name of the factory. String() string @@ -57,25 +57,30 @@ func HasFactories() bool { } // Create a new ContainerHandler for the specified container. -func NewContainerHandler(name string) (ContainerHandler, error) { +func NewContainerHandler(name string) (ContainerHandler, bool, error) { factoriesLock.RLock() defer factoriesLock.RUnlock() // Create the ContainerHandler with the first factory that supports it. for _, factory := range factories { - canHandle, err := factory.CanHandle(name) + canHandle, canAccept, err := factory.CanHandleAndAccept(name) if err != nil { - glog.V(4).Infof("Error trying to work out if we can hande %s: %v", name, err) + glog.V(4).Infof("Error trying to work out if we can handle %s: %v", name, err) } if canHandle { + if !canAccept { + glog.V(3).Infof("Factory %q can handle container %q, but ignoring.", factory, name) + return nil, false, nil + } glog.V(3).Infof("Using factory %q for container %q", factory, name) - return factory.NewContainerHandler(name) + handle, err := factory.NewContainerHandler(name) + return handle, canAccept, err } else { glog.V(4).Infof("Factory %q was unable to handle container %q", factory, name) } } - return nil, fmt.Errorf("no known factory can handle creation of container") + return nil, false, fmt.Errorf("no known factory can handle creation of container") } // Clear the known factories. diff --git a/container/factory_test.go b/container/factory_test.go index 45b7945d..535fcb07 100644 --- a/container/factory_test.go +++ b/container/factory_test.go @@ -24,14 +24,15 @@ type mockContainerHandlerFactory struct { mock.Mock Name string CanHandleValue bool + CanAcceptValue bool } func (self *mockContainerHandlerFactory) String() string { return self.Name } -func (self *mockContainerHandlerFactory) CanHandle(name string) (bool, error) { - return self.CanHandleValue, nil +func (self *mockContainerHandlerFactory) CanHandleAndAccept(name string) (bool, bool, error) { + return self.CanHandleValue, self.CanAcceptValue, nil } func (self *mockContainerHandlerFactory) NewContainerHandler(name string) (ContainerHandler, error) { @@ -50,6 +51,7 @@ func TestNewContainerHandler_FirstMatches(t *testing.T) { allwaysYes := &mockContainerHandlerFactory{ Name: "yes", CanHandleValue: true, + CanAcceptValue: true, } RegisterContainerHandlerFactory(allwaysYes) @@ -60,7 +62,7 @@ func TestNewContainerHandler_FirstMatches(t *testing.T) { } allwaysYes.On("NewContainerHandler", testContainerName).Return(mockContainer, nil) - cont, err := NewContainerHandler(testContainerName) + cont, _, err := NewContainerHandler(testContainerName) if err != nil { t.Error(err) } @@ -76,11 +78,13 @@ func TestNewContainerHandler_SecondMatches(t *testing.T) { allwaysNo := &mockContainerHandlerFactory{ Name: "no", CanHandleValue: false, + CanAcceptValue: true, } RegisterContainerHandlerFactory(allwaysNo) allwaysYes := &mockContainerHandlerFactory{ Name: "yes", CanHandleValue: true, + CanAcceptValue: true, } RegisterContainerHandlerFactory(allwaysYes) @@ -91,7 +95,7 @@ func TestNewContainerHandler_SecondMatches(t *testing.T) { } allwaysYes.On("NewContainerHandler", testContainerName).Return(mockContainer, nil) - cont, err := NewContainerHandler(testContainerName) + cont, _, err := NewContainerHandler(testContainerName) if err != nil { t.Error(err) } @@ -107,16 +111,44 @@ func TestNewContainerHandler_NoneMatch(t *testing.T) { allwaysNo1 := &mockContainerHandlerFactory{ Name: "no", CanHandleValue: false, + CanAcceptValue: true, } RegisterContainerHandlerFactory(allwaysNo1) allwaysNo2 := &mockContainerHandlerFactory{ Name: "no", CanHandleValue: false, + CanAcceptValue: true, } RegisterContainerHandlerFactory(allwaysNo2) - _, err := NewContainerHandler(testContainerName) + _, _, err := NewContainerHandler(testContainerName) if err == nil { t.Error("Expected NewContainerHandler to fail") } } + +func TestNewContainerHandler_Accept(t *testing.T) { + ClearContainerHandlerFactories() + + // Register handler that can handle the container, but can't accept it. + cannotHandle := &mockContainerHandlerFactory{ + Name: "no", + CanHandleValue: false, + CanAcceptValue: true, + } + RegisterContainerHandlerFactory(cannotHandle) + cannotAccept := &mockContainerHandlerFactory{ + Name: "no", + CanHandleValue: true, + CanAcceptValue: false, + } + RegisterContainerHandlerFactory(cannotAccept) + + _, accept, err := NewContainerHandler(testContainerName) + if err != nil { + t.Error("Expected NewContainerHandler to succeed") + } + if accept == true { + t.Error("Expected NewContainerHandler to ignore the container.") + } +} diff --git a/container/raw/factory.go b/container/raw/factory.go index 111e422f..27020eeb 100644 --- a/container/raw/factory.go +++ b/container/raw/factory.go @@ -15,6 +15,7 @@ package raw import ( + "flag" "fmt" "github.com/golang/glog" @@ -24,6 +25,8 @@ import ( info "github.com/google/cadvisor/info/v1" ) +var dockerOnly = flag.Bool("docker_only", false, "Only report docker containers in addition to root stats") + type rawFactory struct { // Factory for machine information. machineInfoFactory info.MachineInfoFactory @@ -43,9 +46,10 @@ func (self *rawFactory) NewContainerHandler(name string) (container.ContainerHan return newRawContainerHandler(name, self.cgroupSubsystems, self.machineInfoFactory, self.fsInfo) } -// The raw factory can handle any container. -func (self *rawFactory) CanHandle(name string) (bool, error) { - return true, nil +// The raw factory can handle any container. If --docker_only is set to false, non-docker containers are ignored. +func (self *rawFactory) CanHandleAndAccept(name string) (bool, bool, error) { + accept := name == "/" || !*dockerOnly + return true, accept, nil } func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo) error { diff --git a/manager/manager.go b/manager/manager.go index da458fa9..12132e12 100644 --- a/manager/manager.go +++ b/manager/manager.go @@ -73,6 +73,9 @@ type Manager interface { // Get info for all requested containers based on the request options. GetRequestedContainersInfo(containerName string, options v2.RequestOptions) (map[string]*info.ContainerInfo, error) + // Returns true if the named container exists. + Exists(containerName string) bool + // Get information about the machine. GetMachineInfo() (*info.MachineInfo, error) @@ -619,12 +622,32 @@ func (m *manager) GetVersionInfo() (*info.VersionInfo, error) { return &m.versionInfo, nil } +func (m *manager) Exists(containerName string) bool { + m.containersLock.Lock() + defer m.containersLock.Unlock() + + namespacedName := namespacedContainerName{ + Name: containerName, + } + + _, ok := m.containers[namespacedName] + if ok { + return true + } + return false +} + // Create a container. func (m *manager) createContainer(containerName string) error { - handler, err := container.NewContainerHandler(containerName) + handler, accept, err := container.NewContainerHandler(containerName) if err != nil { return err } + if !accept { + // ignoring this container. + glog.V(4).Infof("ignoring container %q", containerName) + return nil + } logUsage := *logCadvisorUsage && containerName == m.cadvisorContainer cont, err := newContainerData(containerName, m.memoryStorage, handler, m.loadReader, logUsage) if err != nil { diff --git a/manager/manager_mock.go b/manager/manager_mock.go index a74fda5f..de18a31d 100644 --- a/manager/manager_mock.go +++ b/manager/manager_mock.go @@ -70,6 +70,11 @@ func (c *ManagerMock) GetRequestedContainersInfo(containerName string, options v return args.Get(0).(map[string]*info.ContainerInfo), args.Error(1) } +func (c *ManagerMock) Exists(name string) bool { + args := c.Called(name) + return args.Get(0).(bool) +} + func (c *ManagerMock) WatchForEvents(queryuest *events.Request, passedChannel chan *info.Event) error { args := c.Called(queryuest, passedChannel) return args.Error(0) diff --git a/pages/containers.go b/pages/containers.go index d504d56f..ad083345 100644 --- a/pages/containers.go +++ b/pages/containers.go @@ -216,6 +216,9 @@ func serveContainersPage(m manager.Manager, w http.ResponseWriter, u *url.URL) e // Build the links for the subcontainers. subcontainerLinks := make([]link, 0, len(cont.Subcontainers)) for _, sub := range cont.Subcontainers { + if !m.Exists(sub.Name) { + continue + } subcontainerLinks = append(subcontainerLinks, link{ Text: getContainerDisplayName(sub), Link: path.Join(ContainersPage, sub.Name),