Skip to content

Commit 831e2a2

Browse files
authored
feat: Add cgroupnsmode option (#237)
Signed-off-by: Arjun Raja Yogidas <[email protected]>
1 parent 50f7220 commit 831e2a2

File tree

4 files changed

+117
-16
lines changed

4 files changed

+117
-16
lines changed

api/handlers/container/create.go

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,16 @@ func (h *handler) create(w http.ResponseWriter, r *http.Request) {
187187
if req.HostConfig.SecurityOpt != nil {
188188
securityOpt = req.HostConfig.SecurityOpt
189189
}
190+
var cgroupnsMode string
191+
if req.HostConfig.CgroupnsMode != "" {
192+
if !req.HostConfig.CgroupnsMode.Valid() {
193+
response.JSON(w, http.StatusBadRequest, response.NewErrorFromMsg("invalid cgroup namespace mode, valid values are: 'private' or 'host'"))
194+
return
195+
}
196+
cgroupnsMode = string(req.HostConfig.CgroupnsMode)
197+
} else {
198+
cgroupnsMode = defaults.CgroupnsMode()
199+
}
190200

191201
globalOpt := ncTypes.GlobalCommandOptions(*h.Config)
192202
createOpt := ncTypes.ContainerCreateOptions{
@@ -222,13 +232,13 @@ func (h *handler) create(w http.ResponseWriter, r *http.Request) {
222232
CPUShares: uint64(req.HostConfig.CPUShares), // CPU shares (relative weight)
223233
CPUQuota: CpuQuota, // CPUQuota limits the CPU CFS (Completely Fair Scheduler) quota
224234
CPUPeriod: uint64(req.HostConfig.CPUPeriod),
225-
Memory: memory, // memory limit (in bytes)
226-
MemorySwap: memorySwap, // Total memory usage (memory + swap); set `-1` to enable unlimited swap
227-
MemoryReservation: memoryReservation, // Memory soft limit (in bytes)
228-
MemorySwappiness64: memorySwappiness, // Tuning container memory swappiness behaviour
229-
Ulimit: ulimits, // List of ulimits to be set in the container
230-
PidsLimit: pidLimit, // PidsLimit specifies the tune container pids limit
231-
Cgroupns: defaults.CgroupnsMode(), // nerdctl default.
235+
Memory: memory, // memory limit (in bytes)
236+
MemorySwap: memorySwap, // Total memory usage (memory + swap); set `-1` to enable unlimited swap
237+
MemoryReservation: memoryReservation, // Memory soft limit (in bytes)
238+
MemorySwappiness64: memorySwappiness, // Tuning container memory swappiness behaviour
239+
Ulimit: ulimits, // List of ulimits to be set in the container
240+
PidsLimit: pidLimit, // PidsLimit specifies the tune container pids limit
241+
Cgroupns: cgroupnsMode, // Cgroupns specifies the cgroup namespace to use
232242
BlkioWeight: req.HostConfig.BlkioWeight,
233243
BlkioWeightDevice: weightDevicesToStrings(req.HostConfig.BlkioWeightDevice),
234244
BlkioDeviceReadBps: throttleDevicesToStrings(req.HostConfig.BlkioDeviceReadBps),

api/handlers/container/create_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,42 @@ var _ = Describe("Container Create API ", func() {
974974
Expect(rr.Body).Should(MatchJSON(jsonResponse))
975975
})
976976

977+
It("should set CgroupnsMode option", func() {
978+
body := []byte(`{
979+
"Image": "test-image",
980+
"HostConfig": {
981+
"CgroupnsMode": "host"
982+
}
983+
}`)
984+
req, _ := http.NewRequest(http.MethodPost, "/containers/create", bytes.NewReader(body))
985+
986+
// expected create options
987+
createOpt.Cgroupns = "host"
988+
989+
service.EXPECT().Create(gomock.Any(), "test-image", nil, equalTo(createOpt), equalTo(netOpt)).Return(
990+
cid, nil)
991+
992+
// handler should return success message with 201 status code.
993+
h.create(rr, req)
994+
Expect(rr).Should(HaveHTTPStatus(http.StatusCreated))
995+
Expect(rr.Body).Should(MatchJSON(jsonResponse))
996+
})
997+
998+
It("should reject invalid CgroupnsMode values", func() {
999+
body := []byte(`{
1000+
"Image": "test-image",
1001+
"HostConfig": {
1002+
"CgroupnsMode": "invalid"
1003+
}
1004+
}`)
1005+
req, _ := http.NewRequest(http.MethodPost, "/containers/create", bytes.NewReader(body))
1006+
1007+
// handler should return bad request with error message
1008+
h.create(rr, req)
1009+
Expect(rr).Should(HaveHTTPStatus(http.StatusBadRequest))
1010+
Expect(rr.Body.String()).Should(ContainSubstring("invalid cgroup namespace mode"))
1011+
})
1012+
9771013
Context("translate port mappings", func() {
9781014
It("should return empty if port mappings is nil", func() {
9791015
Expect(translatePortMappings(nil)).Should(BeEmpty())

api/types/container_types.go

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,15 @@ type ContainerHostConfig struct {
7272
Annotations map[string]string `json:",omitempty"` // Arbitrary non-identifying metadata attached to container and provided to the runtime
7373

7474
// Applicable to UNIX platforms
75-
CapAdd []string // List of kernel capabilities to add to the container
76-
CapDrop []string // List of kernel capabilities to remove from the container
77-
// TODO: CgroupnsMode CgroupnsMode // Cgroup namespace mode to use for the container
78-
DNS []string `json:"Dns"` // List of DNS server to lookup
79-
DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for
80-
DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for
81-
ExtraHosts []string // List of extra hosts
82-
GroupAdd []string // List of additional groups that the container process will run as
83-
IpcMode string // IPC namespace to use for the container
75+
CapAdd []string // List of kernel capabilities to add to the container
76+
CapDrop []string // List of kernel capabilities to remove from the container
77+
CgroupnsMode CgroupnsMode // Cgroup namespace mode to use for the container
78+
DNS []string `json:"Dns"` // List of DNS server to lookup
79+
DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for
80+
DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for
81+
ExtraHosts []string // List of extra hosts
82+
GroupAdd []string // List of additional groups that the container process will run as
83+
IpcMode string // IPC namespace to use for the container
8484
// TODO: Cgroup CgroupSpec // Cgroup to use for the container
8585
// TODO: Links []string // List of links (in the name:alias form)
8686
OomKillDisable bool // specifies whether to disable OOM Killer
@@ -271,3 +271,18 @@ type StatsJSON struct {
271271
}
272272

273273
type Ulimit = units.Ulimit
274+
275+
// CgroupnsMode represents the cgroup namespace mode of the container.
276+
type CgroupnsMode string
277+
278+
// cgroup namespace modes for containers.
279+
const (
280+
CgroupnsModeEmpty CgroupnsMode = ""
281+
CgroupnsModePrivate CgroupnsMode = "private"
282+
CgroupnsModeHost CgroupnsMode = "host"
283+
)
284+
285+
// Valid indicates whether the cgroup namespace mode is valid.
286+
func (c CgroupnsMode) Valid() bool {
287+
return c == CgroupnsModePrivate || c == CgroupnsModeHost
288+
}

e2e/tests/container_create.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,6 +1412,46 @@ func ContainerCreate(opt *option.Option) {
14121412
Expect(ok).Should(BeTrue())
14131413
Expect(annotations["com.example.key"]).Should(Equal("test-value"))
14141414
})
1415+
1416+
It("should create a container with CgroupnsMode set to host", func() {
1417+
// Define options
1418+
options.Cmd = []string{"sleep", "Infinity"}
1419+
options.HostConfig.CgroupnsMode = "host"
1420+
1421+
// Create container
1422+
statusCode, ctr := createContainer(uClient, url, testContainerName, options)
1423+
Expect(statusCode).Should(Equal(http.StatusCreated))
1424+
Expect(ctr.ID).ShouldNot(BeEmpty())
1425+
1426+
// Start container
1427+
command.Run(opt, "start", testContainerName)
1428+
1429+
// Inspect using native format to verify cgroup namespace configuration
1430+
nativeResp := command.Stdout(opt, "inspect", "--mode=native", testContainerName)
1431+
var nativeInspect []map[string]interface{}
1432+
err := json.Unmarshal(nativeResp, &nativeInspect)
1433+
Expect(err).Should(BeNil())
1434+
Expect(nativeInspect).Should(HaveLen(1))
1435+
1436+
// Navigate to the namespaces section
1437+
spec, ok := nativeInspect[0]["Spec"].(map[string]interface{})
1438+
Expect(ok).Should(BeTrue())
1439+
linux, ok := spec["linux"].(map[string]interface{})
1440+
Expect(ok).Should(BeTrue())
1441+
namespaces, ok := linux["namespaces"].([]interface{})
1442+
Expect(ok).Should(BeTrue())
1443+
1444+
// For host mode, cgroup namespace should not be present in the namespaces list
1445+
foundCgroupNS := false
1446+
for _, ns := range namespaces {
1447+
namespace := ns.(map[string]interface{})
1448+
if namespace["type"] == "cgroup" {
1449+
foundCgroupNS = true
1450+
break
1451+
}
1452+
}
1453+
Expect(foundCgroupNS).Should(BeFalse())
1454+
})
14151455
})
14161456
}
14171457

0 commit comments

Comments
 (0)