// +build linux package fs import ( "strconv" "testing" "github.com/opencontainers/runc/libcontainer/cgroups" ) const ( memoryStatContents = `cache 512 rss 1024` memoryUsageContents = "2048\n" memoryMaxUsageContents = "4096\n" memoryFailcnt = "100\n" memoryLimitContents = "8192\n" ) func TestMemorySetMemory(t *testing.T) { helper := NewCgroupTestUtil("memory", t) defer helper.cleanup() const ( memoryBefore = 314572800 // 300M memoryAfter = 524288000 // 500M reservationBefore = 209715200 // 200M reservationAfter = 314572800 // 300M ) helper.writeFileContents(map[string]string{ "memory.limit_in_bytes": strconv.Itoa(memoryBefore), "memory.soft_limit_in_bytes": strconv.Itoa(reservationBefore), }) helper.CgroupData.config.Resources.Memory = memoryAfter helper.CgroupData.config.Resources.MemoryReservation = reservationAfter memory := &MemoryGroup{} if err := memory.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { t.Fatal(err) } value, err := getCgroupParamUint(helper.CgroupPath, "memory.limit_in_bytes") if err != nil { t.Fatalf("Failed to parse memory.limit_in_bytes - %s", err) } if value != memoryAfter { t.Fatal("Got the wrong value, set memory.limit_in_bytes failed.") } value, err = getCgroupParamUint(helper.CgroupPath, "memory.soft_limit_in_bytes") if err != nil { t.Fatalf("Failed to parse memory.soft_limit_in_bytes - %s", err) } if value != reservationAfter { t.Fatal("Got the wrong value, set memory.soft_limit_in_bytes failed.") } } func TestMemorySetMemoryswap(t *testing.T) { helper := NewCgroupTestUtil("memory", t) defer helper.cleanup() const ( memoryswapBefore = 314572800 // 300M memoryswapAfter = 524288000 // 500M ) helper.writeFileContents(map[string]string{ "memory.memsw.limit_in_bytes": strconv.Itoa(memoryswapBefore), }) helper.CgroupData.config.Resources.MemorySwap = memoryswapAfter memory := &MemoryGroup{} if err := memory.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { t.Fatal(err) } value, err := getCgroupParamUint(helper.CgroupPath, "memory.memsw.limit_in_bytes") if err != nil { t.Fatalf("Failed to parse memory.memsw.limit_in_bytes - %s", err) } if value != memoryswapAfter { t.Fatal("Got the wrong value, set memory.memsw.limit_in_bytes failed.") } } func TestMemorySetNegativeMemoryswap(t *testing.T) { helper := NewCgroupTestUtil("memory", t) defer helper.cleanup() const ( memoryBefore = 314572800 // 300M memoryAfter = 524288000 // 500M memoryswapBefore = 629145600 // 600M memoryswapAfter = 629145600 // 600M ) helper.writeFileContents(map[string]string{ "memory.limit_in_bytes": strconv.Itoa(memoryBefore), "memory.memsw.limit_in_bytes": strconv.Itoa(memoryswapBefore), }) helper.CgroupData.config.Resources.Memory = memoryAfter // Negative value means not change helper.CgroupData.config.Resources.MemorySwap = -1 memory := &MemoryGroup{} if err := memory.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { t.Fatal(err) } value, err := getCgroupParamUint(helper.CgroupPath, "memory.limit_in_bytes") if err != nil { t.Fatalf("Failed to parse memory.limit_in_bytes - %s", err) } if value != memoryAfter { t.Fatal("Got the wrong value, set memory.limit_in_bytes failed.") } value, err = getCgroupParamUint(helper.CgroupPath, "memory.memsw.limit_in_bytes") if err != nil { t.Fatalf("Failed to parse memory.memsw.limit_in_bytes - %s", err) } if value != memoryswapAfter { t.Fatal("Got the wrong value, set memory.memsw.limit_in_bytes failed.") } } func TestMemorySetMemoryLargerThanSwap(t *testing.T) { helper := NewCgroupTestUtil("memory", t) defer helper.cleanup() const ( memoryBefore = 314572800 // 300M memoryswapBefore = 524288000 // 500M memoryAfter = 629145600 // 600M memoryswapAfter = 838860800 // 800M ) helper.writeFileContents(map[string]string{ "memory.limit_in_bytes": strconv.Itoa(memoryBefore), "memory.memsw.limit_in_bytes": strconv.Itoa(memoryswapBefore), // Set will call getMemoryData when memory and swap memory are // both set, fake these fields so we don't get error. "memory.usage_in_bytes": "0", "memory.max_usage_in_bytes": "0", "memory.failcnt": "0", }) helper.CgroupData.config.Resources.Memory = memoryAfter helper.CgroupData.config.Resources.MemorySwap = memoryswapAfter memory := &MemoryGroup{} if err := memory.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { t.Fatal(err) } value, err := getCgroupParamUint(helper.CgroupPath, "memory.limit_in_bytes") if err != nil { t.Fatalf("Failed to parse memory.limit_in_bytes - %s", err) } if value != memoryAfter { t.Fatal("Got the wrong value, set memory.limit_in_bytes failed.") } value, err = getCgroupParamUint(helper.CgroupPath, "memory.memsw.limit_in_bytes") if err != nil { t.Fatalf("Failed to parse memory.memsw.limit_in_bytes - %s", err) } if value != memoryswapAfter { t.Fatal("Got the wrong value, set memory.memsw.limit_in_bytes failed.") } } func TestMemorySetSwapSmallerThanMemory(t *testing.T) { helper := NewCgroupTestUtil("memory", t) defer helper.cleanup() const ( memoryBefore = 629145600 // 600M memoryswapBefore = 838860800 // 800M memoryAfter = 314572800 // 300M memoryswapAfter = 524288000 // 500M ) helper.writeFileContents(map[string]string{ "memory.limit_in_bytes": strconv.Itoa(memoryBefore), "memory.memsw.limit_in_bytes": strconv.Itoa(memoryswapBefore), // Set will call getMemoryData when memory and swap memory are // both set, fake these fields so we don't get error. "memory.usage_in_bytes": "0", "memory.max_usage_in_bytes": "0", "memory.failcnt": "0", }) helper.CgroupData.config.Resources.Memory = memoryAfter helper.CgroupData.config.Resources.MemorySwap = memoryswapAfter memory := &MemoryGroup{} if err := memory.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { t.Fatal(err) } value, err := getCgroupParamUint(helper.CgroupPath, "memory.limit_in_bytes") if err != nil { t.Fatalf("Failed to parse memory.limit_in_bytes - %s", err) } if value != memoryAfter { t.Fatal("Got the wrong value, set memory.limit_in_bytes failed.") } value, err = getCgroupParamUint(helper.CgroupPath, "memory.memsw.limit_in_bytes") if err != nil { t.Fatalf("Failed to parse memory.memsw.limit_in_bytes - %s", err) } if value != memoryswapAfter { t.Fatal("Got the wrong value, set memory.memsw.limit_in_bytes failed.") } } func TestMemorySetKernelMemory(t *testing.T) { helper := NewCgroupTestUtil("memory", t) defer helper.cleanup() const ( kernelMemoryBefore = 314572800 // 300M kernelMemoryAfter = 524288000 // 500M ) helper.writeFileContents(map[string]string{ "memory.kmem.limit_in_bytes": strconv.Itoa(kernelMemoryBefore), }) helper.CgroupData.config.Resources.KernelMemory = kernelMemoryAfter memory := &MemoryGroup{} if err := memory.SetKernelMemory(helper.CgroupPath, helper.CgroupData.config); err != nil { t.Fatal(err) } value, err := getCgroupParamUint(helper.CgroupPath, "memory.kmem.limit_in_bytes") if err != nil { t.Fatalf("Failed to parse memory.kmem.limit_in_bytes - %s", err) } if value != kernelMemoryAfter { t.Fatal("Got the wrong value, set memory.kmem.limit_in_bytes failed.") } } func TestMemorySetKernelMemoryTCP(t *testing.T) { helper := NewCgroupTestUtil("memory", t) defer helper.cleanup() const ( kernelMemoryTCPBefore = 314572800 // 300M kernelMemoryTCPAfter = 524288000 // 500M ) helper.writeFileContents(map[string]string{ "memory.kmem.tcp.limit_in_bytes": strconv.Itoa(kernelMemoryTCPBefore), }) helper.CgroupData.config.Resources.KernelMemoryTCP = kernelMemoryTCPAfter memory := &MemoryGroup{} if err := memory.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { t.Fatal(err) } value, err := getCgroupParamUint(helper.CgroupPath, "memory.kmem.tcp.limit_in_bytes") if err != nil { t.Fatalf("Failed to parse memory.kmem.tcp.limit_in_bytes - %s", err) } if value != kernelMemoryTCPAfter { t.Fatal("Got the wrong value, set memory.kmem.tcp.limit_in_bytes failed.") } } func TestMemorySetMemorySwappinessDefault(t *testing.T) { helper := NewCgroupTestUtil("memory", t) defer helper.cleanup() swappinessBefore := 60 //default is 60 swappinessAfter := int64(0) helper.writeFileContents(map[string]string{ "memory.swappiness": strconv.Itoa(swappinessBefore), }) helper.CgroupData.config.Resources.MemorySwappiness = &swappinessAfter memory := &MemoryGroup{} if err := memory.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { t.Fatal(err) } value, err := getCgroupParamUint(helper.CgroupPath, "memory.swappiness") if err != nil { t.Fatalf("Failed to parse memory.swappiness - %s", err) } if int64(value) != swappinessAfter { t.Fatalf("Got the wrong value (%d), set memory.swappiness = %d failed.", value, swappinessAfter) } } func TestMemoryStats(t *testing.T) { helper := NewCgroupTestUtil("memory", t) defer helper.cleanup() helper.writeFileContents(map[string]string{ "memory.stat": memoryStatContents, "memory.usage_in_bytes": memoryUsageContents, "memory.limit_in_bytes": memoryLimitContents, "memory.max_usage_in_bytes": memoryMaxUsageContents, "memory.failcnt": memoryFailcnt, "memory.memsw.usage_in_bytes": memoryUsageContents, "memory.memsw.max_usage_in_bytes": memoryMaxUsageContents, "memory.memsw.failcnt": memoryFailcnt, "memory.memsw.limit_in_bytes": memoryLimitContents, "memory.kmem.usage_in_bytes": memoryUsageContents, "memory.kmem.max_usage_in_bytes": memoryMaxUsageContents, "memory.kmem.failcnt": memoryFailcnt, "memory.kmem.limit_in_bytes": memoryLimitContents, }) memory := &MemoryGroup{} actualStats := *cgroups.NewStats() err := memory.GetStats(helper.CgroupPath, &actualStats) if err != nil { t.Fatal(err) } expectedStats := cgroups.MemoryStats{Cache: 512, Usage: cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, Failcnt: 100, Limit: 8192}, SwapUsage: cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, Failcnt: 100, Limit: 8192}, KernelUsage: cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, Failcnt: 100, Limit: 8192}, Stats: map[string]uint64{"cache": 512, "rss": 1024}} expectMemoryStatEquals(t, expectedStats, actualStats.MemoryStats) } func TestMemoryStatsNoStatFile(t *testing.T) { helper := NewCgroupTestUtil("memory", t) defer helper.cleanup() helper.writeFileContents(map[string]string{ "memory.usage_in_bytes": memoryUsageContents, "memory.max_usage_in_bytes": memoryMaxUsageContents, "memory.limit_in_bytes": memoryLimitContents, }) memory := &MemoryGroup{} actualStats := *cgroups.NewStats() err := memory.GetStats(helper.CgroupPath, &actualStats) if err != nil { t.Fatal(err) } } func TestMemoryStatsNoUsageFile(t *testing.T) { helper := NewCgroupTestUtil("memory", t) defer helper.cleanup() helper.writeFileContents(map[string]string{ "memory.stat": memoryStatContents, "memory.max_usage_in_bytes": memoryMaxUsageContents, "memory.limit_in_bytes": memoryLimitContents, }) memory := &MemoryGroup{} actualStats := *cgroups.NewStats() err := memory.GetStats(helper.CgroupPath, &actualStats) if err == nil { t.Fatal("Expected failure") } } func TestMemoryStatsNoMaxUsageFile(t *testing.T) { helper := NewCgroupTestUtil("memory", t) defer helper.cleanup() helper.writeFileContents(map[string]string{ "memory.stat": memoryStatContents, "memory.usage_in_bytes": memoryUsageContents, "memory.limit_in_bytes": memoryLimitContents, }) memory := &MemoryGroup{} actualStats := *cgroups.NewStats() err := memory.GetStats(helper.CgroupPath, &actualStats) if err == nil { t.Fatal("Expected failure") } } func TestMemoryStatsNoLimitInBytesFile(t *testing.T) { helper := NewCgroupTestUtil("memory", t) defer helper.cleanup() helper.writeFileContents(map[string]string{ "memory.stat": memoryStatContents, "memory.usage_in_bytes": memoryUsageContents, "memory.max_usage_in_bytes": memoryMaxUsageContents, }) memory := &MemoryGroup{} actualStats := *cgroups.NewStats() err := memory.GetStats(helper.CgroupPath, &actualStats) if err == nil { t.Fatal("Expected failure") } } func TestMemoryStatsBadStatFile(t *testing.T) { helper := NewCgroupTestUtil("memory", t) defer helper.cleanup() helper.writeFileContents(map[string]string{ "memory.stat": "rss rss", "memory.usage_in_bytes": memoryUsageContents, "memory.max_usage_in_bytes": memoryMaxUsageContents, "memory.limit_in_bytes": memoryLimitContents, }) memory := &MemoryGroup{} actualStats := *cgroups.NewStats() err := memory.GetStats(helper.CgroupPath, &actualStats) if err == nil { t.Fatal("Expected failure") } } func TestMemoryStatsBadUsageFile(t *testing.T) { helper := NewCgroupTestUtil("memory", t) defer helper.cleanup() helper.writeFileContents(map[string]string{ "memory.stat": memoryStatContents, "memory.usage_in_bytes": "bad", "memory.max_usage_in_bytes": memoryMaxUsageContents, "memory.limit_in_bytes": memoryLimitContents, }) memory := &MemoryGroup{} actualStats := *cgroups.NewStats() err := memory.GetStats(helper.CgroupPath, &actualStats) if err == nil { t.Fatal("Expected failure") } } func TestMemoryStatsBadMaxUsageFile(t *testing.T) { helper := NewCgroupTestUtil("memory", t) defer helper.cleanup() helper.writeFileContents(map[string]string{ "memory.stat": memoryStatContents, "memory.usage_in_bytes": memoryUsageContents, "memory.max_usage_in_bytes": "bad", "memory.limit_in_bytes": memoryLimitContents, }) memory := &MemoryGroup{} actualStats := *cgroups.NewStats() err := memory.GetStats(helper.CgroupPath, &actualStats) if err == nil { t.Fatal("Expected failure") } } func TestMemoryStatsBadLimitInBytesFile(t *testing.T) { helper := NewCgroupTestUtil("memory", t) defer helper.cleanup() helper.writeFileContents(map[string]string{ "memory.stat": memoryStatContents, "memory.usage_in_bytes": memoryUsageContents, "memory.max_usage_in_bytes": memoryMaxUsageContents, "memory.limit_in_bytes": "bad", }) memory := &MemoryGroup{} actualStats := *cgroups.NewStats() err := memory.GetStats(helper.CgroupPath, &actualStats) if err == nil { t.Fatal("Expected failure") } } func TestMemorySetOomControl(t *testing.T) { helper := NewCgroupTestUtil("memory", t) defer helper.cleanup() const ( oom_kill_disable = 1 // disable oom killer, default is 0 ) helper.writeFileContents(map[string]string{ "memory.oom_control": strconv.Itoa(oom_kill_disable), }) memory := &MemoryGroup{} if err := memory.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { t.Fatal(err) } value, err := getCgroupParamUint(helper.CgroupPath, "memory.oom_control") if err != nil { t.Fatalf("Failed to parse memory.oom_control - %s", err) } if value != oom_kill_disable { t.Fatalf("Got the wrong value, set memory.oom_control failed.") } }