github.com/google/cadvisor@v0.49.1/manager/manager_test.go (about)

     1  // Copyright 2014 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Per-container manager.
    16  
    17  package manager
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"strings"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/google/cadvisor/cache/memory"
    27  	"github.com/google/cadvisor/collector"
    28  	"github.com/google/cadvisor/container"
    29  	containertest "github.com/google/cadvisor/container/testing"
    30  	info "github.com/google/cadvisor/info/v1"
    31  	itest "github.com/google/cadvisor/info/v1/test"
    32  	v2 "github.com/google/cadvisor/info/v2"
    33  	"github.com/google/cadvisor/utils/sysfs/fakesysfs"
    34  
    35  	"github.com/stretchr/testify/assert"
    36  	clock "k8s.io/utils/clock/testing"
    37  
    38  	// install all the container runtimes included in the library version for testing.
    39  	// as these are moved to cmd/internal/container, remove them from here.
    40  	_ "github.com/google/cadvisor/container/containerd/install"
    41  	_ "github.com/google/cadvisor/container/crio/install"
    42  	_ "github.com/google/cadvisor/container/docker/install"
    43  	_ "github.com/google/cadvisor/container/systemd/install"
    44  )
    45  
    46  // TODO(vmarmol): Refactor these tests.
    47  
    48  func createManagerAndAddContainers(
    49  	memoryCache *memory.InMemoryCache,
    50  	sysfs *fakesysfs.FakeSysFs,
    51  	containers []string,
    52  	f func(*containertest.MockContainerHandler),
    53  	t *testing.T,
    54  ) *manager {
    55  	container.ClearContainerHandlerFactories()
    56  	mif := &manager{
    57  		containers:   make(map[namespacedContainerName]*containerData),
    58  		quitChannels: make([]chan error, 0, 2),
    59  		memoryCache:  memoryCache,
    60  	}
    61  	for _, name := range containers {
    62  		mockHandler := containertest.NewMockContainerHandler(name)
    63  		spec := itest.GenerateRandomContainerSpec(4)
    64  		mockHandler.On("GetSpec").Return(
    65  			spec,
    66  			nil,
    67  		).Once()
    68  		cont, err := newContainerData(name, memoryCache, mockHandler, false, &collector.GenericCollectorManager{}, 60*time.Second, true, clock.NewFakeClock(time.Now()))
    69  		if err != nil {
    70  			t.Fatal(err)
    71  		}
    72  		mif.containers[namespacedContainerName{
    73  			Name: name,
    74  		}] = cont
    75  		// Add Docker containers under their namespace.
    76  		if strings.HasPrefix(name, "/docker") {
    77  			mif.containers[namespacedContainerName{
    78  				Namespace: DockerNamespace,
    79  				Name:      strings.TrimPrefix(name, "/docker/"),
    80  			}] = cont
    81  		}
    82  		f(mockHandler)
    83  	}
    84  	return mif
    85  }
    86  
    87  func createManagerAndAddSubContainers(
    88  	memoryCache *memory.InMemoryCache,
    89  	sysfs *fakesysfs.FakeSysFs,
    90  	containers []string,
    91  	f func(*containertest.MockContainerHandler),
    92  	t *testing.T,
    93  ) *manager {
    94  	container.ClearContainerHandlerFactories()
    95  	mif := &manager{
    96  		containers:   make(map[namespacedContainerName]*containerData),
    97  		quitChannels: make([]chan error, 0, 2),
    98  		memoryCache:  memoryCache,
    99  	}
   100  
   101  	subcontainers1 := []info.ContainerReference{
   102  		{Name: "/kubepods/besteffort"},
   103  		{Name: "/kubepods/burstable"},
   104  	}
   105  	subcontainers2 := []info.ContainerReference(nil)
   106  	subcontainers3 := []info.ContainerReference{
   107  		{Name: "/kubepods/burstable/pod01042b28-179d-446a-954a-7266557e12cd"},
   108  		{Name: "/kubepods/burstable/pod01042b28-179d-446a-954a-7266557e12ce"},
   109  	}
   110  	subcontainers4 := []info.ContainerReference{
   111  		{Name: "/kubepods/burstable/pod01042b28-179d-446a-954a-7266557e12cd/22f44d2a517778590e2d8bcafafe501f79e8a509e5b6de70b7700c4d37722bce"},
   112  		{Name: "/kubepods/burstable/pod01042b28-179d-446a-954a-7266557e12cd/ae9465f98d275998e148b6fc12f5f92e5d4a64fca0d255f6dc3a13cc6f93a10f"},
   113  	}
   114  
   115  	subcontainers5 := []info.ContainerReference(nil)
   116  	subcontainers6 := []info.ContainerReference(nil)
   117  
   118  	subcontainerList := [][]info.ContainerReference{subcontainers1, subcontainers2, subcontainers3, subcontainers4, subcontainers5, subcontainers6}
   119  
   120  	for idx, name := range containers {
   121  		mockHandler := containertest.NewMockContainerHandler(name)
   122  		spec := itest.GenerateRandomContainerSpec(4)
   123  		mockHandler.On("GetSpec").Return(
   124  			spec,
   125  			nil,
   126  		).Once()
   127  		mockHandler.On("ListContainers", container.ListSelf).Return(
   128  			subcontainerList[idx],
   129  			nil,
   130  		)
   131  		cont, err := newContainerData(name, memoryCache, mockHandler, false, &collector.GenericCollectorManager{}, 60*time.Second, true, clock.NewFakeClock(time.Now()))
   132  		if err != nil {
   133  			t.Fatal(err)
   134  		}
   135  		mif.containers[namespacedContainerName{
   136  			Name: name,
   137  		}] = cont
   138  		// Add Docker containers under their namespace.
   139  		if strings.HasPrefix(name, "/docker") {
   140  			mif.containers[namespacedContainerName{
   141  				Namespace: DockerNamespace,
   142  				Name:      strings.TrimPrefix(name, "/docker/"),
   143  			}] = cont
   144  		}
   145  		f(mockHandler)
   146  	}
   147  	return mif
   148  }
   149  
   150  // Expect a manager with the specified containers and query. Returns the manager, map of ContainerInfo objects,
   151  // and map of MockContainerHandler objects.}
   152  func expectManagerWithContainers(containers []string, query *info.ContainerInfoRequest, t *testing.T) (*manager, map[string]*info.ContainerInfo, map[string]*containertest.MockContainerHandler) {
   153  	infosMap := make(map[string]*info.ContainerInfo, len(containers))
   154  	handlerMap := make(map[string]*containertest.MockContainerHandler, len(containers))
   155  
   156  	for _, container := range containers {
   157  		infosMap[container] = itest.GenerateRandomContainerInfo(container, 4, query, 1*time.Second)
   158  	}
   159  
   160  	memoryCache := memory.New(time.Duration(query.NumStats)*time.Second, nil)
   161  	sysfs := &fakesysfs.FakeSysFs{}
   162  	m := createManagerAndAddContainers(
   163  		memoryCache,
   164  		sysfs,
   165  		containers,
   166  		func(h *containertest.MockContainerHandler) {
   167  			cinfo := infosMap[h.Name]
   168  			ref, err := h.ContainerReference()
   169  			if err != nil {
   170  				t.Error(err)
   171  			}
   172  
   173  			cInfo := info.ContainerInfo{
   174  				ContainerReference: ref,
   175  			}
   176  			for _, stat := range cinfo.Stats {
   177  				err = memoryCache.AddStats(&cInfo, stat)
   178  				if err != nil {
   179  					t.Error(err)
   180  				}
   181  			}
   182  			spec := cinfo.Spec
   183  
   184  			h.On("ListContainers", container.ListSelf).Return(
   185  				[]info.ContainerReference(nil),
   186  				nil,
   187  			)
   188  			h.On("GetSpec").Return(
   189  				spec,
   190  				nil,
   191  			).Once()
   192  			handlerMap[h.Name] = h
   193  		},
   194  		t,
   195  	)
   196  
   197  	return m, infosMap, handlerMap
   198  }
   199  
   200  // Expect a manager with the specified containers and query. Returns the manager, map of ContainerInfo objects,
   201  // and map of MockContainerHandler objects.}
   202  func expectManagerWithSubContainers(containers []string, query *info.ContainerInfoRequest, t *testing.T) (*manager, map[string]*info.ContainerInfo, map[string]*containertest.MockContainerHandler) {
   203  	infosMap := make(map[string]*info.ContainerInfo, len(containers))
   204  	handlerMap := make(map[string]*containertest.MockContainerHandler, len(containers))
   205  
   206  	for _, container := range containers {
   207  		infosMap[container] = itest.GenerateRandomContainerInfo(container, 4, query, 1*time.Second)
   208  	}
   209  
   210  	memoryCache := memory.New(time.Duration(query.NumStats)*time.Second, nil)
   211  	sysfs := &fakesysfs.FakeSysFs{}
   212  	m := createManagerAndAddSubContainers(
   213  		memoryCache,
   214  		sysfs,
   215  		containers,
   216  		func(h *containertest.MockContainerHandler) {
   217  			cinfo := infosMap[h.Name]
   218  			ref, err := h.ContainerReference()
   219  			if err != nil {
   220  				t.Error(err)
   221  			}
   222  
   223  			cInfo := info.ContainerInfo{
   224  				ContainerReference: ref,
   225  			}
   226  			for _, stat := range cinfo.Stats {
   227  				err = memoryCache.AddStats(&cInfo, stat)
   228  				if err != nil {
   229  					t.Error(err)
   230  				}
   231  			}
   232  			spec := cinfo.Spec
   233  			h.On("GetSpec").Return(
   234  				spec,
   235  				nil,
   236  			).Once()
   237  			handlerMap[h.Name] = h
   238  		},
   239  		t,
   240  	)
   241  
   242  	return m, infosMap, handlerMap
   243  }
   244  
   245  // Expect a manager with the specified containers and query. Returns the manager, map of ContainerInfo objects,
   246  // and map of MockContainerHandler objects.}
   247  func expectManagerWithContainersV2(containers []string, query *info.ContainerInfoRequest, t *testing.T) (*manager, map[string]*info.ContainerInfo, map[string]*containertest.MockContainerHandler) {
   248  	infosMap := make(map[string]*info.ContainerInfo, len(containers))
   249  	handlerMap := make(map[string]*containertest.MockContainerHandler, len(containers))
   250  
   251  	for _, container := range containers {
   252  		infosMap[container] = itest.GenerateRandomContainerInfo(container, 4, query, 1*time.Second)
   253  	}
   254  
   255  	memoryCache := memory.New(time.Duration(query.NumStats)*time.Second, nil)
   256  	sysfs := &fakesysfs.FakeSysFs{}
   257  	m := createManagerAndAddContainers(
   258  		memoryCache,
   259  		sysfs,
   260  		containers,
   261  		func(h *containertest.MockContainerHandler) {
   262  			cinfo := infosMap[h.Name]
   263  			ref, err := h.ContainerReference()
   264  			if err != nil {
   265  				t.Error(err)
   266  			}
   267  
   268  			cInfo := info.ContainerInfo{
   269  				ContainerReference: ref,
   270  			}
   271  
   272  			for _, stat := range cinfo.Stats {
   273  				err = memoryCache.AddStats(&cInfo, stat)
   274  				if err != nil {
   275  					t.Error(err)
   276  				}
   277  			}
   278  			spec := cinfo.Spec
   279  
   280  			h.On("GetSpec").Return(
   281  				spec,
   282  				nil,
   283  			).Once()
   284  			handlerMap[h.Name] = h
   285  		},
   286  		t,
   287  	)
   288  
   289  	return m, infosMap, handlerMap
   290  }
   291  
   292  func TestGetContainerInfo(t *testing.T) {
   293  	containers := []string{
   294  		"/c1",
   295  		"/c2",
   296  	}
   297  
   298  	query := &info.ContainerInfoRequest{
   299  		NumStats: 256,
   300  	}
   301  
   302  	m, infosMap, handlerMap := expectManagerWithContainers(containers, query, t)
   303  
   304  	returnedInfos := make(map[string]*info.ContainerInfo, len(containers))
   305  
   306  	for _, container := range containers {
   307  		cinfo, err := m.GetContainerInfo(container, query)
   308  		if err != nil {
   309  			t.Fatalf("Unable to get info for container %v: %v", container, err)
   310  		}
   311  		returnedInfos[container] = cinfo
   312  	}
   313  
   314  	for container, handler := range handlerMap {
   315  		handler.AssertExpectations(t)
   316  		returned := returnedInfos[container]
   317  		expected := infosMap[container]
   318  		if !reflect.DeepEqual(returned, expected) {
   319  			t.Errorf("returned unexpected info for container %v; returned %+v; expected %+v", container, returned, expected)
   320  		}
   321  	}
   322  }
   323  
   324  func TestGetContainerInfoV2(t *testing.T) {
   325  	containers := []string{
   326  		"/",
   327  		"/c1",
   328  		"/c2",
   329  	}
   330  
   331  	options := v2.RequestOptions{
   332  		IdType:    v2.TypeName,
   333  		Count:     1,
   334  		Recursive: true,
   335  	}
   336  	query := &info.ContainerInfoRequest{
   337  		NumStats: 2,
   338  	}
   339  
   340  	m, _, handlerMap := expectManagerWithContainersV2(containers, query, t)
   341  
   342  	infos, err := m.GetContainerInfoV2("/", options)
   343  	if err != nil {
   344  		t.Fatalf("GetContainerInfoV2 failed: %v", err)
   345  	}
   346  
   347  	for container, handler := range handlerMap {
   348  		handler.AssertExpectations(t)
   349  		info, ok := infos[container]
   350  		assert.True(t, ok, "Missing info for container %q", container)
   351  		assert.NotEqual(t, v2.ContainerSpec{}, info.Spec, "Empty spec for container %q", container)
   352  		assert.NotEmpty(t, info.Stats, "Missing stats for container %q", container)
   353  	}
   354  }
   355  
   356  func TestGetContainerInfoV2Failure(t *testing.T) {
   357  	successful := "/"
   358  	statless := "/c1"
   359  	failing := "/c2"
   360  	containers := []string{
   361  		successful, statless, failing,
   362  	}
   363  
   364  	options := v2.RequestOptions{
   365  		IdType:    v2.TypeName,
   366  		Count:     1,
   367  		Recursive: true,
   368  	}
   369  	query := &info.ContainerInfoRequest{
   370  		NumStats: 2,
   371  	}
   372  
   373  	m, _, handlerMap := expectManagerWithContainers(containers, query, t)
   374  
   375  	// Remove /c1 stats
   376  	err := m.memoryCache.RemoveContainer(statless)
   377  	if err != nil {
   378  		t.Fatalf("RemoveContainer failed: %v", err)
   379  	}
   380  
   381  	// Make GetSpec fail on /c2
   382  	mockErr := fmt.Errorf("intentional GetSpec failure")
   383  	_, err = handlerMap[failing].GetSpec()
   384  	assert.NoError(t, err) // Use up default GetSpec call, and replace below
   385  	handlerMap[failing].On("GetSpec").Return(info.ContainerSpec{}, mockErr)
   386  	handlerMap[failing].On("Exists").Return(true)
   387  	m.containers[namespacedContainerName{Name: failing}].infoLastUpdatedTime = time.Time{} // Force GetSpec.
   388  
   389  	infos, err := m.GetContainerInfoV2("/", options)
   390  	if err == nil {
   391  		t.Error("Expected error calling GetContainerInfoV2")
   392  	}
   393  
   394  	// Successful containers still successful.
   395  	info, ok := infos[successful]
   396  	assert.True(t, ok, "Missing info for container %q", successful)
   397  	assert.NotEqual(t, v2.ContainerSpec{}, info.Spec, "Empty spec for container %q", successful)
   398  	assert.NotEmpty(t, info.Stats, "Missing stats for container %q", successful)
   399  
   400  	// "/c1" present with spec.
   401  	info, ok = infos[statless]
   402  	assert.True(t, ok, "Missing info for container %q", statless)
   403  	assert.NotEqual(t, v2.ContainerSpec{}, info.Spec, "Empty spec for container %q", statless)
   404  	assert.Empty(t, info.Stats, "Missing stats for container %q", successful)
   405  
   406  	// "/c2" should be present but empty.
   407  	info, ok = infos[failing]
   408  	assert.True(t, ok, "Missing info for failed container")
   409  	assert.Equal(t, v2.ContainerInfo{}, info, "Empty spec for failed container")
   410  	assert.Empty(t, info.Stats, "Missing stats for failed container")
   411  }
   412  
   413  func TestSubcontainersInfo(t *testing.T) {
   414  	containers := []string{
   415  		"/c1",
   416  		"/c2",
   417  	}
   418  
   419  	query := &info.ContainerInfoRequest{
   420  		NumStats: 64,
   421  	}
   422  
   423  	m, _, _ := expectManagerWithContainers(containers, query, t)
   424  
   425  	result, err := m.SubcontainersInfo("/", query)
   426  	if err != nil {
   427  		t.Fatalf("expected to succeed: %s", err)
   428  	}
   429  	if len(result) != len(containers) {
   430  		t.Errorf("expected to received containers: %v, but received: %v", containers, result)
   431  	}
   432  	for _, res := range result {
   433  		found := false
   434  		for _, name := range containers {
   435  			if res.Name == name {
   436  				found = true
   437  				break
   438  			}
   439  		}
   440  		if !found {
   441  			t.Errorf("unexpected container %q in result, expected one of %v", res.Name, containers)
   442  		}
   443  	}
   444  }
   445  
   446  func TestSubcontainersInfoError(t *testing.T) {
   447  	containers := []string{
   448  		"/kubepods",
   449  		"/kubepods/besteffort",
   450  		"/kubepods/burstable",
   451  		"/kubepods/burstable/pod01042b28-179d-446a-954a-7266557e12cd",
   452  		"/kubepods/burstable/pod01042b28-179d-446a-954a-7266557e12cd/22f44d2a517778590e2d8bcafafe501f79e8a509e5b6de70b7700c4d37722bce",
   453  		"/kubepods/burstable/pod01042b28-179d-446a-954a-7266557e12cd/ae9465f98d275998e148b6fc12f5f92e5d4a64fca0d255f6dc3a13cc6f93a10f",
   454  	}
   455  	query := &info.ContainerInfoRequest{
   456  		NumStats: 1,
   457  	}
   458  
   459  	m, _, _ := expectManagerWithSubContainers(containers, query, t)
   460  	result, err := m.SubcontainersInfo("/kubepods", query)
   461  	if err != nil {
   462  		t.Fatalf("expected to succeed: %s", err)
   463  	}
   464  
   465  	if len(result) != len(containers) {
   466  		t.Errorf("expected to received containers: %v, but received: %v", containers, result)
   467  	}
   468  
   469  	totalBurstable := 0
   470  	burstableCount := 0
   471  	totalBesteffort := 0
   472  	besteffortCount := 0
   473  
   474  	for _, res := range result {
   475  		found := false
   476  		if res.Name == "/kubepods/burstable" {
   477  			totalBurstable = len(res.Subcontainers)
   478  		} else if res.Name == "/kubepods/besteffort" {
   479  			totalBesteffort = len(res.Subcontainers)
   480  		} else if strings.HasPrefix(res.Name, "/kubepods/burstable") && len(res.Name) == len("/kubepods/burstable/pod01042b28-179d-446a-954a-7266557e12cd") {
   481  			burstableCount++
   482  		} else if strings.HasPrefix(res.Name, "/kubepods/besteffort") && len(res.Name) == len("/kubepods/besteffort/pod01042b28-179d-446a-954a-7266557e12cd") {
   483  			besteffortCount++
   484  		}
   485  		for _, name := range containers {
   486  			if res.Name == name {
   487  				found = true
   488  				break
   489  			}
   490  		}
   491  		if !found {
   492  			t.Errorf("unexpected container %q in result, expected one of %v", res.Name, containers)
   493  		}
   494  	}
   495  
   496  	assert.NotEqual(t, totalBurstable, burstableCount)
   497  	assert.Equal(t, totalBesteffort, besteffortCount)
   498  }
   499  
   500  func TestDockerContainersInfo(t *testing.T) {
   501  	containers := []string{
   502  		"/docker/c1a",
   503  		"/docker/c2a",
   504  	}
   505  
   506  	query := &info.ContainerInfoRequest{
   507  		NumStats: 2,
   508  	}
   509  
   510  	m, _, _ := expectManagerWithContainers(containers, query, t)
   511  
   512  	result, err := m.DockerContainer("c1a", query)
   513  	if err != nil {
   514  		t.Fatalf("expected to succeed: %s", err)
   515  	}
   516  	if result.Name != containers[0] {
   517  		t.Errorf("Unexpected container %q in result. Expected container %q", result.Name, containers[0])
   518  	}
   519  
   520  	result, err = m.DockerContainer("c2", query)
   521  	if err != nil {
   522  		t.Fatalf("expected to succeed: %s", err)
   523  	}
   524  	if result.Name != containers[1] {
   525  		t.Errorf("Unexpected container %q in result. Expected container %q", result.Name, containers[1])
   526  	}
   527  
   528  	result, err = m.DockerContainer("c", query)
   529  	expectedError := "unable to find container. Container \"c\" is not unique"
   530  	if err == nil {
   531  		t.Errorf("expected error %q but received %q", expectedError, err)
   532  	}
   533  }