github.com/google/cadvisor@v0.49.1/manager/container_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  	"sync"
    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  
    34  	"github.com/stretchr/testify/assert"
    35  	"github.com/stretchr/testify/require"
    36  
    37  	clock "k8s.io/utils/clock/testing"
    38  )
    39  
    40  const (
    41  	containerName        = "/container"
    42  	testLongHousekeeping = time.Second
    43  )
    44  
    45  // Create a containerData instance for a test.
    46  func setupContainerData(t *testing.T, spec info.ContainerSpec) (*containerData, *containertest.MockContainerHandler, *memory.InMemoryCache, *clock.FakeClock) {
    47  	mockHandler := containertest.NewMockContainerHandler(containerName)
    48  	mockHandler.On("GetSpec").Return(
    49  		spec,
    50  		nil,
    51  	)
    52  	memoryCache := memory.New(60, nil)
    53  	fakeClock := clock.NewFakeClock(time.Now())
    54  	ret, err := newContainerData(containerName, memoryCache, mockHandler, false, &collector.GenericCollectorManager{}, 60*time.Second, true, fakeClock)
    55  	if err != nil {
    56  		t.Fatal(err)
    57  	}
    58  	return ret, mockHandler, memoryCache, fakeClock
    59  }
    60  
    61  // Create a containerData instance for a test and add a default GetSpec mock.
    62  func newTestContainerData(t *testing.T) (*containerData, *containertest.MockContainerHandler, *memory.InMemoryCache, *clock.FakeClock) {
    63  	return setupContainerData(t, itest.GenerateRandomContainerSpec(4))
    64  }
    65  
    66  func TestUpdateSubcontainers(t *testing.T) {
    67  	subcontainers := []info.ContainerReference{
    68  		{Name: "/container/ee0103"},
    69  		{Name: "/container/abcd"},
    70  		{Name: "/container/something"},
    71  	}
    72  	cd, mockHandler, _, _ := newTestContainerData(t)
    73  	mockHandler.On("ListContainers", container.ListSelf).Return(
    74  		subcontainers,
    75  		nil,
    76  	)
    77  
    78  	err := cd.updateSubcontainers()
    79  	if err != nil {
    80  		t.Fatal(err)
    81  	}
    82  
    83  	if len(cd.info.Subcontainers) != len(subcontainers) {
    84  		t.Errorf("Received %v subcontainers, should be %v", len(cd.info.Subcontainers), len(subcontainers))
    85  	}
    86  
    87  	for _, sub := range cd.info.Subcontainers {
    88  		found := false
    89  		for _, sub2 := range subcontainers {
    90  			if sub.Name == sub2.Name {
    91  				found = true
    92  			}
    93  		}
    94  		if !found {
    95  			t.Errorf("Received unknown sub container %v", sub)
    96  		}
    97  	}
    98  
    99  	mockHandler.AssertExpectations(t)
   100  }
   101  
   102  func TestUpdateSubcontainersWithError(t *testing.T) {
   103  	cd, mockHandler, _, _ := newTestContainerData(t)
   104  	mockHandler.On("ListContainers", container.ListSelf).Return(
   105  		[]info.ContainerReference{},
   106  		fmt.Errorf("some error"),
   107  	)
   108  	mockHandler.On("Exists").Return(true)
   109  
   110  	assert.NotNil(t, cd.updateSubcontainers())
   111  	assert.Empty(t, cd.info.Subcontainers, "subcontainers should not be populated on failure")
   112  	mockHandler.AssertExpectations(t)
   113  }
   114  
   115  func TestUpdateSubcontainersWithErrorOnDeadContainer(t *testing.T) {
   116  	cd, mockHandler, _, _ := newTestContainerData(t)
   117  	mockHandler.On("ListContainers", container.ListSelf).Return(
   118  		[]info.ContainerReference{},
   119  		fmt.Errorf("some error"),
   120  	)
   121  	mockHandler.On("Exists").Return(false)
   122  
   123  	assert.Nil(t, cd.updateSubcontainers())
   124  	mockHandler.AssertExpectations(t)
   125  }
   126  
   127  func checkNumStats(t *testing.T, memoryCache *memory.InMemoryCache, numStats int) {
   128  	var empty time.Time
   129  	stats, err := memoryCache.RecentStats(containerName, empty, empty, -1)
   130  	require.Nil(t, err)
   131  	assert.Len(t, stats, numStats)
   132  }
   133  
   134  func TestUpdateStats(t *testing.T) {
   135  	statsList := itest.GenerateRandomStats(1, 4, 1*time.Second)
   136  	stats := statsList[0]
   137  
   138  	cd, mockHandler, memoryCache, _ := newTestContainerData(t)
   139  	mockHandler.On("GetStats").Return(
   140  		stats,
   141  		nil,
   142  	)
   143  
   144  	err := cd.updateStats()
   145  	if err != nil {
   146  		t.Fatal(err)
   147  	}
   148  
   149  	checkNumStats(t, memoryCache, 1)
   150  	mockHandler.AssertExpectations(t)
   151  }
   152  
   153  func TestUpdateSpec(t *testing.T) {
   154  	spec := itest.GenerateRandomContainerSpec(4)
   155  	cd, mockHandler, _, _ := newTestContainerData(t)
   156  	mockHandler.On("GetSpec").Return(
   157  		spec,
   158  		nil,
   159  	)
   160  
   161  	err := cd.updateSpec()
   162  	if err != nil {
   163  		t.Fatal(err)
   164  	}
   165  
   166  	mockHandler.AssertExpectations(t)
   167  }
   168  
   169  func TestGetInfo(t *testing.T) {
   170  	spec := itest.GenerateRandomContainerSpec(4)
   171  	subcontainers := []info.ContainerReference{
   172  		{Name: "/container/ee0103"},
   173  		{Name: "/container/abcd"},
   174  		{Name: "/container/something"},
   175  	}
   176  	cd, mockHandler, _, _ := setupContainerData(t, spec)
   177  	mockHandler.On("ListContainers", container.ListSelf).Return(
   178  		subcontainers,
   179  		nil,
   180  	)
   181  	mockHandler.Aliases = []string{"a1", "a2"}
   182  
   183  	info, err := cd.GetInfo(true)
   184  	if err != nil {
   185  		t.Fatal(err)
   186  	}
   187  
   188  	mockHandler.AssertExpectations(t)
   189  
   190  	if len(info.Subcontainers) != len(subcontainers) {
   191  		t.Errorf("Received %v subcontainers, should be %v", len(info.Subcontainers), len(subcontainers))
   192  	}
   193  
   194  	for _, sub := range info.Subcontainers {
   195  		found := false
   196  		for _, sub2 := range subcontainers {
   197  			if sub.Name == sub2.Name {
   198  				found = true
   199  			}
   200  		}
   201  		if !found {
   202  			t.Errorf("Received unknown sub container %v", sub)
   203  		}
   204  	}
   205  
   206  	if !reflect.DeepEqual(spec, info.Spec) {
   207  		t.Errorf("received wrong container spec")
   208  	}
   209  
   210  	if info.Name != mockHandler.Name {
   211  		t.Errorf("received wrong container name: received %v; should be %v", info.Name, mockHandler.Name)
   212  	}
   213  }
   214  
   215  func TestOnDemandHousekeeping(t *testing.T) {
   216  	statsList := itest.GenerateRandomStats(1, 4, 1*time.Second)
   217  	stats := statsList[0]
   218  
   219  	cd, mockHandler, memoryCache, fakeClock := newTestContainerData(t)
   220  	mockHandler.On("GetStats").Return(stats, nil)
   221  	defer func() {
   222  		err := cd.Stop()
   223  		assert.NoError(t, err)
   224  	}()
   225  
   226  	// 0 seconds should always trigger an update
   227  	go cd.OnDemandHousekeeping(0 * time.Second)
   228  	cd.housekeepingTick(fakeClock.NewTimer(time.Minute).C(), testLongHousekeeping)
   229  
   230  	fakeClock.Step(2 * time.Second)
   231  
   232  	// This should return without requiring a housekeepingTick because stats have been updated recently enough
   233  	cd.OnDemandHousekeeping(3 * time.Second)
   234  
   235  	go cd.OnDemandHousekeeping(1 * time.Second)
   236  	cd.housekeepingTick(fakeClock.NewTimer(time.Minute).C(), testLongHousekeeping)
   237  
   238  	checkNumStats(t, memoryCache, 2)
   239  	mockHandler.AssertExpectations(t)
   240  }
   241  
   242  func TestConcurrentOnDemandHousekeeping(t *testing.T) {
   243  	statsList := itest.GenerateRandomStats(1, 4, 1*time.Second)
   244  	stats := statsList[0]
   245  
   246  	cd, mockHandler, memoryCache, fakeClock := newTestContainerData(t)
   247  	mockHandler.On("GetStats").Return(stats, nil)
   248  	defer func() {
   249  		err := cd.Stop()
   250  		assert.NoError(t, err)
   251  	}()
   252  
   253  	numConcurrentCalls := 5
   254  	var waitForHousekeeping sync.WaitGroup
   255  	waitForHousekeeping.Add(numConcurrentCalls)
   256  	onDemandCache := []chan struct{}{}
   257  	for i := 0; i < numConcurrentCalls; i++ {
   258  		go func() {
   259  			cd.OnDemandHousekeeping(0 * time.Second)
   260  			waitForHousekeeping.Done()
   261  		}()
   262  		// Wait for work to be queued
   263  		onDemandCache = append(onDemandCache, <-cd.onDemandChan)
   264  	}
   265  	// Requeue work:
   266  	for _, ch := range onDemandCache {
   267  		cd.onDemandChan <- ch
   268  	}
   269  
   270  	go cd.housekeepingTick(fakeClock.NewTimer(time.Minute).C(), testLongHousekeeping)
   271  	// Ensure that all queued calls return with only a single call to housekeepingTick
   272  	waitForHousekeeping.Wait()
   273  
   274  	checkNumStats(t, memoryCache, 1)
   275  	mockHandler.AssertExpectations(t)
   276  }
   277  
   278  func TestOnDemandHousekeepingReturnsAfterStopped(t *testing.T) {
   279  	statsList := itest.GenerateRandomStats(1, 4, 1*time.Second)
   280  	stats := statsList[0]
   281  
   282  	cd, mockHandler, memoryCache, fakeClock := newTestContainerData(t)
   283  	mockHandler.On("GetStats").Return(stats, nil)
   284  
   285  	// trigger housekeeping update
   286  	go cd.OnDemandHousekeeping(0 * time.Second)
   287  	cd.housekeepingTick(fakeClock.NewTimer(time.Minute).C(), testLongHousekeeping)
   288  
   289  	checkNumStats(t, memoryCache, 1)
   290  
   291  	fakeClock.Step(2 * time.Second)
   292  
   293  	err := cd.Stop()
   294  	assert.NoError(t, err)
   295  	// housekeeping tick should detect stop and not store any more metrics
   296  	assert.False(t, cd.housekeepingTick(fakeClock.NewTimer(time.Minute).C(), testLongHousekeeping))
   297  	fakeClock.Step(1 * time.Second)
   298  	// on demand housekeeping should not block and return
   299  	cd.OnDemandHousekeeping(-1 * time.Second)
   300  
   301  	mockHandler.AssertExpectations(t)
   302  }
   303  
   304  func TestOnDemandHousekeepingRace(t *testing.T) {
   305  	statsList := itest.GenerateRandomStats(1, 4, 1*time.Second)
   306  	stats := statsList[0]
   307  
   308  	cd, mockHandler, _, _ := newTestContainerData(t)
   309  	mockHandler.On("GetStats").Return(stats, nil)
   310  
   311  	wg := sync.WaitGroup{}
   312  	wg.Add(1002)
   313  
   314  	go func() {
   315  		time.Sleep(10 * time.Millisecond)
   316  		err := cd.Start()
   317  		assert.NoError(t, err)
   318  		wg.Done()
   319  	}()
   320  
   321  	go func() {
   322  		t.Log("starting on demand goroutine")
   323  		for i := 0; i < 1000; i++ {
   324  			go func() {
   325  				time.Sleep(1 * time.Microsecond)
   326  				cd.OnDemandHousekeeping(0 * time.Millisecond)
   327  				wg.Done()
   328  			}()
   329  		}
   330  		wg.Done()
   331  	}()
   332  	wg.Wait()
   333  }
   334  
   335  var psOutput = [][]byte{
   336  	[]byte("root       15886       2 23:51  0.1  0.0     0      0 I    00:00:00 kworker/u8:3-ev   3 -\nroot       15887       2 23:51  0.0  0.0     0      0 I<   00:00:00 kworker/1:2H      1 -\nubuntu     15888    1804 23:51  0.0  0.0  2832  10176 R+   00:00:00 ps                1 8:devices:/user.slice,6:pids:/user.slice/user-1000.slice/session-3.scope,5:blkio:/user.slice,2:cpu,cpuacct:/user.slice,1:na"),
   337  	[]byte("root         104       2 21:34  0.0  0.0     0      0 I<   00:00:00 kthrotld          3 -\nroot         105       2 21:34  0.0  0.0     0      0 S    00:00:00 irq/41-aerdrv     0 -\nroot         107       2 21:34  0.0  0.0     0      0 I<   00:00:00 DWC Notificatio   3 -\nroot         109       2 21:34  0.0  0.0     0      0 S<   00:00:00 vchiq-slot/0      1 -\nroot         110       2 21:34  0.0  0.0     0      0 S<   00:00:00 vchiq-recy/0      3 -"),
   338  }
   339  
   340  func TestParseProcessList(t *testing.T) {
   341  	for i, ps := range psOutput {
   342  		t.Run(fmt.Sprintf("iteration %d", i), func(tt *testing.T) {
   343  			cd := &containerData{}
   344  			_, err := cd.parseProcessList("/", true, ps)
   345  			// checking *only* parsing errors - otherwise /proc would have to be emulated.
   346  			assert.NoError(tt, err)
   347  		})
   348  	}
   349  }
   350  
   351  var psLine = []struct {
   352  	name              string
   353  	line              string
   354  	cadvisorContainer string
   355  	isHostNamespace   bool
   356  	process           *v2.ProcessInfo
   357  	err               error
   358  	cd                *containerData
   359  }{
   360  	{
   361  		name:              "plain process with cgroup",
   362  		line:              "ubuntu     15888    1804 23:51  0.1  0.0  2832  10176 R+   00:10:00 cadvisor            1 10:cpuset:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,9:devices:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,8:pids:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,7:memory:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,6:freezer:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,5:perf_event:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,4:blkio:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,3:cpu,cpuacct:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,2:net_cls,net_prio:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,1:name=systemd:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831",
   363  		cadvisorContainer: "/docker/cadvisor",
   364  		isHostNamespace:   true,
   365  		process: &v2.ProcessInfo{
   366  			User:          "ubuntu",
   367  			Pid:           15888,
   368  			Ppid:          1804,
   369  			StartTime:     "23:51",
   370  			PercentCpu:    0.1,
   371  			PercentMemory: 0.0,
   372  			RSS:           2899968,
   373  			VirtualSize:   10420224,
   374  			Status:        "R+",
   375  			RunningTime:   "00:10:00",
   376  			CgroupPath:    "/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831",
   377  			Cmd:           "cadvisor",
   378  			Psr:           1,
   379  		},
   380  		cd: &containerData{
   381  			info: containerInfo{ContainerReference: info.ContainerReference{Name: "/"}},
   382  		},
   383  	},
   384  	{
   385  		name:              "process with space in name and no cgroup",
   386  		line:              "root         107       2 21:34  0.0  0.1     3      4 I<   00:20:00 DWC Notificatio   3 -",
   387  		cadvisorContainer: "/docker/cadvisor",
   388  		process: &v2.ProcessInfo{
   389  			User:          "root",
   390  			Pid:           107,
   391  			Ppid:          2,
   392  			StartTime:     "21:34",
   393  			PercentCpu:    0.0,
   394  			PercentMemory: 0.1,
   395  			RSS:           3072,
   396  			VirtualSize:   4096,
   397  			Status:        "I<",
   398  			RunningTime:   "00:20:00",
   399  			CgroupPath:    "/",
   400  			Cmd:           "DWC Notificatio",
   401  			Psr:           3,
   402  		},
   403  		cd: &containerData{
   404  			info: containerInfo{ContainerReference: info.ContainerReference{Name: "/"}},
   405  		},
   406  	},
   407  	{
   408  		name:              "process with highly unusual name (one 2 three 4 five 6 eleven), cgroup to be ignored",
   409  		line:              "root         107       2 21:34  0.0  0.1     3      4 I<   00:20:00 one 2 three 4 five 6 eleven   3 10:cpuset:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,9:devices:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,8:pids:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,7:memory:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,6:freezer:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,5:perf_event:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,4:blkio:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,3:cpu,cpuacct:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,2:net_cls,net_prio:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,1:name=systemd:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831",
   410  		cadvisorContainer: "/docker/cadvisor",
   411  		isHostNamespace:   true,
   412  		process: &v2.ProcessInfo{
   413  			User:          "root",
   414  			Pid:           107,
   415  			Ppid:          2,
   416  			StartTime:     "21:34",
   417  			PercentCpu:    0.0,
   418  			PercentMemory: 0.1,
   419  			RSS:           3072,
   420  			VirtualSize:   4096,
   421  			Status:        "I<",
   422  			RunningTime:   "00:20:00",
   423  			Cmd:           "one 2 three 4 five 6 eleven",
   424  			Psr:           3,
   425  			CgroupPath:    "/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831",
   426  		},
   427  		cd: &containerData{
   428  			info: containerInfo{ContainerReference: info.ContainerReference{Name: "/"}},
   429  		},
   430  	},
   431  	{
   432  		name:              "wrong field count",
   433  		line:              "ps output it is not",
   434  		cadvisorContainer: "/docker/cadvisor",
   435  		err:               fmt.Errorf("expected at least 13 fields, found 5: output: \"ps output it is not\""),
   436  		cd:                &containerData{},
   437  	},
   438  	{
   439  		name:              "ps running in cadvisor container should be ignored",
   440  		line:              "root         107       2 21:34  0.0  0.1     3      4 I<   00:20:00 ps   3 10:cpuset:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,9:devices:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,8:pids:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,7:memory:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,6:freezer:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,5:perf_event:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,4:blkio:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,3:cpu,cpuacct:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,2:net_cls,net_prio:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831,1:name=systemd:/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831",
   441  		cadvisorContainer: "/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831",
   442  		cd: &containerData{
   443  			info: containerInfo{ContainerReference: info.ContainerReference{Name: "/"}},
   444  		},
   445  	},
   446  	{
   447  		name: "non-root container but process belongs to the container",
   448  		line: "root         107       2 21:34  0.0  0.1     3      4 I<   00:20:00 sleep inf   3 10:cpuset:/docker/some-random-container,9:devices:/docker/some-random-container,8:pids:/docker/some-random-container,7:memory:/docker/some-random-container,6:freezer:/docker/some-random-container,5:perf_event:/docker/some-random-container,4:blkio:/docker/some-random-container,3:cpu,cpuacct:/docker/some-random-container,2:net_cls,net_prio:/docker/some-random-container,1:name=systemd:/docker/some-random-container",
   449  		process: &v2.ProcessInfo{
   450  			User:          "root",
   451  			Pid:           107,
   452  			Ppid:          2,
   453  			StartTime:     "21:34",
   454  			PercentCpu:    0.0,
   455  			PercentMemory: 0.1,
   456  			RSS:           3072,
   457  			VirtualSize:   4096,
   458  			Status:        "I<",
   459  			RunningTime:   "00:20:00",
   460  			Cmd:           "sleep inf",
   461  			Psr:           3,
   462  		},
   463  		cadvisorContainer: "/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831",
   464  		cd: &containerData{
   465  			info: containerInfo{ContainerReference: info.ContainerReference{Name: "/docker/some-random-container"}},
   466  		},
   467  	},
   468  	{
   469  		name:              "non-root container and process belonging to another container",
   470  		line:              "root         107       2 21:34  0.0  0.1     3      4 I<   00:20:00 sleep inf   3 10:cpuset:/docker/some-random-container,9:devices:/docker/some-random-container,8:pids:/docker/some-random-container,7:memory:/docker/some-random-container,6:freezer:/docker/some-random-container,5:perf_event:/docker/some-random-container,4:blkio:/docker/some-random-container,3:cpu,cpuacct:/docker/some-random-container,2:net_cls,net_prio:/docker/some-random-container,1:name=systemd:/docker/some-random-container",
   471  		cadvisorContainer: "/docker/dd479c33249f6c3f0f1189aa88f07dad3eeb3e6fedfc71385c27ddd699994831",
   472  		cd: &containerData{
   473  			info: containerInfo{ContainerReference: info.ContainerReference{Name: "/docker/some-other-container"}},
   474  		},
   475  	},
   476  }
   477  
   478  func TestParsePsLine(t *testing.T) {
   479  	for _, ps := range psLine {
   480  		t.Run(ps.name, func(tt *testing.T) {
   481  			process, err := ps.cd.parsePsLine(ps.line, ps.cadvisorContainer, ps.isHostNamespace)
   482  			assert.Equal(tt, ps.err, err)
   483  			assert.EqualValues(tt, ps.process, process)
   484  		})
   485  	}
   486  }
   487  
   488  var cgroupCases = []struct {
   489  	name    string
   490  	cgroups string
   491  	path    string
   492  }{
   493  	{
   494  		name:    "no cgroup",
   495  		cgroups: "-",
   496  		path:    "/",
   497  	},
   498  	{
   499  		name:    "random and meaningless string",
   500  		cgroups: "/this/is/a/path/to/some.file",
   501  		path:    "/",
   502  	},
   503  	{
   504  		name:    "0::-type cgroup",
   505  		cgroups: "0::/docker/some-cgroup",
   506  		path:    "/docker/some-cgroup",
   507  	},
   508  	{
   509  		name:    "memory cgroup",
   510  		cgroups: "4:memory:/docker/09c89cd48b3597db904ab8e6920fef2cbf93588d037d9613ce362e25188f8ec6,2:net_cls:/docker/09c89cd48b3597db904ab8e6920fef2cbf93588d037d9613ce362e25188f8ec6",
   511  		path:    "/docker/09c89cd48b3597db904ab8e6920fef2cbf93588d037d9613ce362e25188f8ec6",
   512  	},
   513  	{
   514  		name:    "cpu,cpuacct cgroup",
   515  		cgroups: "4:cpu,cpuacct:/docker/09c89cd48b3597db904ab8e6920fef2cbf93588d037d9613ce362e25188f8ec6,2:net_cls:/docker/09c89cd48b3597db904ab8e6920fef2cbf93588d037d9613ce362e25188f8ec6",
   516  		path:    "/docker/09c89cd48b3597db904ab8e6920fef2cbf93588d037d9613ce362e25188f8ec6",
   517  	},
   518  	{
   519  		name:    "cpu cgroup",
   520  		cgroups: "4:cpu:/docker/09c89cd48b3597db904ab8e6920fef2cbf93588d037d9613ce362e25188f8ec6,2:net_cls:/docker/09c89cd48b3597db904ab8e6920fef2cbf93588d037d9613ce362e25188f8ec6",
   521  		path:    "/docker/09c89cd48b3597db904ab8e6920fef2cbf93588d037d9613ce362e25188f8ec6",
   522  	},
   523  	{
   524  		name:    "cpuacct cgroup",
   525  		cgroups: "4:cpuacct:/docker/09c89cd48b3597db904ab8e6920fef2cbf93588d037d9613ce362e25188f8ec6,2:net_cls:/docker/09c89cd48b3597db904ab8e6920fef2cbf93588d037d9613ce362e25188f8ec6",
   526  		path:    "/docker/09c89cd48b3597db904ab8e6920fef2cbf93588d037d9613ce362e25188f8ec6",
   527  	},
   528  }
   529  
   530  func TestGetCgroupPath(t *testing.T) {
   531  	for _, cgroup := range cgroupCases {
   532  		t.Run(cgroup.name, func(tt *testing.T) {
   533  			cd := &containerData{}
   534  			path := cd.getCgroupPath(cgroup.cgroups)
   535  			assert.Equal(t, cgroup.path, path)
   536  		})
   537  	}
   538  }