2020package integration
2121
2222import (
23+ "bytes"
24+ "io/ioutil"
25+ "strings"
2326 "testing"
2427
2528 "github.com/containerd/cgroups"
29+ cgroupsv2 "github.com/containerd/cgroups/v2"
30+ "github.com/containerd/containerd"
2631 runtimespec "github.com/opencontainers/runtime-spec/specs-go"
2732 "github.com/stretchr/testify/assert"
2833 "github.com/stretchr/testify/require"
@@ -39,7 +44,164 @@ func checkMemoryLimit(t *testing.T, spec *runtimespec.Spec, memLimit int64) {
3944 assert .Equal (t , memLimit , * spec .Linux .Resources .Memory .Limit )
4045}
4146
42- func TestUpdateContainerResources (t * testing.T ) {
47+ func checkMemorySwapLimit (t * testing.T , spec * runtimespec.Spec , memLimit * int64 ) {
48+ require .NotNil (t , spec )
49+ require .NotNil (t , spec .Linux )
50+ require .NotNil (t , spec .Linux .Resources )
51+ require .NotNil (t , spec .Linux .Resources .Memory )
52+ if memLimit == nil {
53+ require .Nil (t , spec .Linux .Resources .Memory .Swap )
54+ } else {
55+ require .NotNil (t , spec .Linux .Resources .Memory .Swap )
56+ assert .Equal (t , * memLimit , * spec .Linux .Resources .Memory .Swap )
57+ }
58+ }
59+
60+ func getCgroupSwapLimitForTask (t * testing.T , task containerd.Task ) uint64 {
61+ if cgroups .Mode () == cgroups .Unified {
62+ groupPath , err := cgroupsv2 .PidGroupPath (int (task .Pid ()))
63+ if err != nil {
64+ t .Fatal (err )
65+ }
66+ cgroup2 , err := cgroupsv2 .LoadManager ("/sys/fs/cgroup" , groupPath )
67+ if err != nil {
68+ t .Fatal (err )
69+ }
70+ stat , err := cgroup2 .Stat ()
71+ if err != nil {
72+ t .Fatal (err )
73+ }
74+ return stat .Memory .SwapLimit
75+ }
76+ cgroup , err := cgroups .Load (cgroups .V1 , cgroups .PidPath (int (task .Pid ())))
77+ if err != nil {
78+ t .Fatal (err )
79+ }
80+ stat , err := cgroup .Stat (cgroups .IgnoreNotExist )
81+ if err != nil {
82+ t .Fatal (err )
83+ }
84+ return stat .Memory .HierarchicalSwapLimit
85+ }
86+
87+ func getCgroupMemoryLimitForTask (t * testing.T , task containerd.Task ) uint64 {
88+ if cgroups .Mode () == cgroups .Unified {
89+ groupPath , err := cgroupsv2 .PidGroupPath (int (task .Pid ()))
90+ if err != nil {
91+ t .Fatal (err )
92+ }
93+ cgroup2 , err := cgroupsv2 .LoadManager ("/sys/fs/cgroup" , groupPath )
94+ if err != nil {
95+ t .Fatal (err )
96+ }
97+ stat , err := cgroup2 .Stat ()
98+ if err != nil {
99+ t .Fatal (err )
100+ }
101+ return stat .Memory .UsageLimit
102+ }
103+
104+ cgroup , err := cgroups .Load (cgroups .V1 , cgroups .PidPath (int (task .Pid ())))
105+ if err != nil {
106+ t .Fatal (err )
107+ }
108+ stat , err := cgroup .Stat (cgroups .IgnoreNotExist )
109+ if err != nil {
110+ t .Fatal (err )
111+ }
112+ return stat .Memory .Usage .Limit
113+ }
114+
115+ func isSwapLikelyEnabled () bool {
116+ // Check whether swap is enabled.
117+ swapFile := "/proc/swaps"
118+ swapData , err := ioutil .ReadFile (swapFile )
119+ if err != nil {
120+ // We can't read the file or it doesn't exist, assume we don't have swap.
121+ return false
122+ }
123+
124+ swapData = bytes .TrimSpace (swapData ) // extra trailing \n
125+ swapLines := strings .Split (string (swapData ), "\n " )
126+ // If there is more than one line (table headers) in /proc/swaps, swap is enabled
127+ return len (swapLines ) > 1
128+ }
129+
130+ func TestUpdateContainerResources_MemorySwap (t * testing.T ) {
131+ if ! isSwapLikelyEnabled () {
132+ t .Skipf ("Swap is not enabled, or /proc/swaps is not readable. Swap is required for this test" )
133+ return
134+ }
135+
136+ t .Log ("Create a sandbox" )
137+ sb , sbConfig := PodSandboxConfigWithCleanup (t , "sandbox" , "update-container-swap-resources" )
138+
139+ EnsureImageExists (t , pauseImage )
140+
141+ memoryLimit := int64 (128 * 1024 * 1024 )
142+ baseSwapLimit := int64 (200 * 1024 * 1024 )
143+ increasedSwapLimit := int64 (256 * 1024 * 1024 )
144+
145+ t .Log ("Create a container with memory limit but no swap" )
146+ cnConfig := ContainerConfig (
147+ "container" ,
148+ pauseImage ,
149+ WithResources (& runtime.LinuxContainerResources {
150+ MemoryLimitInBytes : memoryLimit ,
151+ MemorySwapLimitInBytes : baseSwapLimit ,
152+ }),
153+ )
154+ cn , err := runtimeService .CreateContainer (sb , cnConfig , sbConfig )
155+ require .NoError (t , err )
156+
157+ t .Log ("Check memory limit in container OCI spec" )
158+ container , err := containerdClient .LoadContainer (context .Background (), cn )
159+ require .NoError (t , err )
160+ spec , err := container .Spec (context .Background ())
161+ require .NoError (t , err )
162+ checkMemoryLimit (t , spec , memoryLimit )
163+ checkMemorySwapLimit (t , spec , & baseSwapLimit )
164+
165+ t .Log ("Update container swap limit after created" )
166+ err = runtimeService .UpdateContainerResources (cn , & runtime.LinuxContainerResources {
167+ MemorySwapLimitInBytes : baseSwapLimit ,
168+ }, nil )
169+ require .NoError (t , err )
170+
171+ t .Log ("Check memory limit in container OCI spec" )
172+ spec , err = container .Spec (context .Background ())
173+ require .NoError (t , err )
174+ sw1 := baseSwapLimit
175+ checkMemorySwapLimit (t , spec , & sw1 )
176+
177+ t .Log ("Start the container" )
178+ require .NoError (t , runtimeService .StartContainer (cn ))
179+ task , err := container .Task (context .Background (), nil )
180+ require .NoError (t , err )
181+
182+ t .Log ("Check memory limit in cgroup" )
183+ memLimit := getCgroupMemoryLimitForTask (t , task )
184+ swapLimit := getCgroupSwapLimitForTask (t , task )
185+ assert .Equal (t , uint64 (memoryLimit ), memLimit )
186+ assert .Equal (t , uint64 (baseSwapLimit - memoryLimit ), swapLimit )
187+
188+ t .Log ("Update container memory limit after started" )
189+ err = runtimeService .UpdateContainerResources (cn , & runtime.LinuxContainerResources {
190+ MemorySwapLimitInBytes : increasedSwapLimit ,
191+ }, nil )
192+ require .NoError (t , err )
193+
194+ t .Log ("Check memory limit in container OCI spec" )
195+ spec , err = container .Spec (context .Background ())
196+ require .NoError (t , err )
197+ checkMemorySwapLimit (t , spec , & increasedSwapLimit )
198+
199+ t .Log ("Check memory limit in cgroup" )
200+ swapLimit = getCgroupSwapLimitForTask (t , task )
201+ assert .Equal (t , uint64 (increasedSwapLimit - memoryLimit ), swapLimit )
202+ }
203+
204+ func TestUpdateContainerResources_MemoryLimit (t * testing.T ) {
43205 // TODO(claudiub): Make this test work once https://github.com/microsoft/hcsshim/pull/931 merges.
44206 t .Log ("Create a sandbox" )
45207 sb , sbConfig := PodSandboxConfigWithCleanup (t , "sandbox" , "update-container-resources" )
0 commit comments