Skip to content

Commit 58eef04

Browse files
authored
feat: Add OomKillDisabled, NetworkDisabled and MACAddress option (runfinch#228)
Signed-off-by: Arjun Raja Yogidas <[email protected]>
1 parent c683e9c commit 58eef04

File tree

4 files changed

+175
-17
lines changed

4 files changed

+175
-17
lines changed

api/handlers/container/create.go

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -172,15 +172,16 @@ func (h *handler) create(w http.ResponseWriter, r *http.Request) {
172172
GOptions: globalOpt,
173173

174174
// #region for basic flags
175-
Interactive: false, // TODO: update this after attach supports STDIN
176-
TTY: false, // TODO: update this after attach supports STDIN
177-
Detach: true, // TODO: current implementation of create does not support AttachStdin, AttachStdout, and AttachStderr flags
178-
Restart: restart, // Restart policy to apply when a container exits.
179-
Rm: req.HostConfig.AutoRemove, // Automatically remove container upon exit.
180-
Pull: "missing", // nerdctl default.
181-
StopSignal: stopSignal,
182-
StopTimeout: stopTimeout,
183-
CidFile: req.HostConfig.ContainerIDFile, // CidFile write the container ID to the file
175+
Interactive: false, // TODO: update this after attach supports STDIN
176+
TTY: false, // TODO: update this after attach supports STDIN
177+
Detach: true, // TODO: current implementation of create does not support AttachStdin, AttachStdout, and AttachStderr flags
178+
Restart: restart, // Restart policy to apply when a container exits.
179+
Rm: req.HostConfig.AutoRemove, // Automatically remove container upon exit.
180+
Pull: "missing", // nerdctl default.
181+
StopSignal: stopSignal,
182+
StopTimeout: stopTimeout,
183+
CidFile: req.HostConfig.ContainerIDFile, // CidFile write the container ID to the file
184+
OomKillDisable: req.HostConfig.OomKillDisable,
184185
// #endregion
185186

186187
// #region for platform flags
@@ -264,14 +265,19 @@ func (h *handler) create(w http.ResponseWriter, r *http.Request) {
264265
if req.HostConfig.DNSOptions != nil {
265266
dnsOpt = req.HostConfig.DNSOptions
266267
}
268+
269+
if req.NetworkDisabled {
270+
networkMode = "none"
271+
}
267272
netOpt := ncTypes.NetworkOptions{
268273
Hostname: req.Hostname,
269-
NetworkSlice: []string{networkMode}, // TODO: Set to none if "NetworkDisabled" is true in request
274+
NetworkSlice: []string{networkMode},
270275
DNSServers: req.HostConfig.DNS, // Custom DNS lookup servers.
271276
DNSResolvConfOptions: dnsOpt, // DNS options.
272277
DNSSearchDomains: req.HostConfig.DNSSearch, // Custom DNS search domains.
273278
PortMappings: portMappings,
274279
AddHost: req.HostConfig.ExtraHosts, // Extra hosts.
280+
MACAddress: req.MacAddress,
275281
}
276282

277283
ctx := namespaces.WithNamespace(r.Context(), h.Config.Namespace)

api/handlers/container/create_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,63 @@ var _ = Describe("Container Create API ", func() {
591591
Expect(rr.Body.String()).Should(ContainSubstring("failed to parse"))
592592
})
593593

594+
It("should set specified NetworkDisabled setting", func() {
595+
body := []byte(`{
596+
"Image": "test-image",
597+
"NetworkDisabled": true
598+
}`)
599+
req, _ := http.NewRequest(http.MethodPost, "/containers/create", bytes.NewReader(body))
600+
601+
// expected network options
602+
netOpt.NetworkSlice = []string{"none"}
603+
604+
service.EXPECT().Create(gomock.Any(), "test-image", nil, equalTo(createOpt), equalTo(netOpt)).Return(
605+
cid, nil)
606+
607+
// handler should return success message with 201 status code.
608+
h.create(rr, req)
609+
Expect(rr).Should(HaveHTTPStatus(http.StatusCreated))
610+
Expect(rr.Body).Should(MatchJSON(jsonResponse))
611+
})
612+
613+
It("should set the MACAddress to a user specified value", func() {
614+
body := []byte(`{
615+
"Image": "test-image",
616+
"MacAddress": "12:34:56:78:9a:bc"
617+
}`)
618+
req, _ := http.NewRequest(http.MethodPost, "/containers/create", bytes.NewReader(body))
619+
620+
// expected network options
621+
netOpt.MACAddress = "12:34:56:78:9a:bc"
622+
service.EXPECT().Create(gomock.Any(), "test-image", nil, equalTo(createOpt), equalTo(netOpt)).Return(
623+
cid, nil)
624+
625+
// handler should return success message with 201 status code.
626+
h.create(rr, req)
627+
Expect(rr).Should(HaveHTTPStatus(http.StatusCreated))
628+
Expect(rr.Body).Should(MatchJSON(jsonResponse))
629+
})
630+
631+
It("should set the OomKillDisable option", func() {
632+
body := []byte(`{
633+
"Image": "test-image",
634+
"HostConfig": {
635+
"OomKillDisable": true
636+
}
637+
}`)
638+
req, _ := http.NewRequest(http.MethodPost, "/containers/create", bytes.NewReader(body))
639+
640+
// expected network options
641+
createOpt.OomKillDisable = true
642+
service.EXPECT().Create(gomock.Any(), "test-image", nil, equalTo(createOpt), equalTo(netOpt)).Return(
643+
cid, nil)
644+
645+
// handler should return success message with 201 status code.
646+
h.create(rr, req)
647+
Expect(rr).Should(HaveHTTPStatus(http.StatusCreated))
648+
Expect(rr.Body).Should(MatchJSON(jsonResponse))
649+
})
650+
594651
Context("translate port mappings", func() {
595652
It("should return empty if port mappings is nil", func() {
596653
Expect(translatePortMappings(nil)).Should(BeEmpty())

api/types/container_types.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@ type ContainerConfig struct {
4242
Cmd []string `json:",omitempty"` // Command to run when starting the container
4343
// TODO Healthcheck *HealthConfig `json:",omitempty"` // Healthcheck describes how to check the container is healthy
4444
// TODO: ArgsEscaped bool `json:",omitempty"` // True if command is already escaped (meaning treat as a command line) (Windows specific).
45-
Image string // Name of the image as it was passed by the operator (e.g. could be symbolic)
46-
Volumes map[string]struct{} `json:",omitempty"` // List of volumes (mounts) used for the container
47-
WorkingDir string `json:",omitempty"` // Current directory (PWD) in the command will be launched
48-
Entrypoint []string `json:",omitempty"` // Entrypoint to run when starting the container
49-
// TODO: NetworkDisabled bool `json:",omitempty"` // Is network disabled
50-
// TODO: MacAddress string `json:",omitempty"` // Mac Address of the container
45+
Image string // Name of the image as it was passed by the operator (e.g. could be symbolic)
46+
Volumes map[string]struct{} `json:",omitempty"` // List of volumes (mounts) used for the container
47+
WorkingDir string `json:",omitempty"` // Current directory (PWD) in the command will be launched
48+
Entrypoint []string `json:",omitempty"` // Entrypoint to run when starting the container
49+
NetworkDisabled bool `json:",omitempty"` // Is network disabled
50+
MacAddress string `json:",omitempty"` // Mac Address of the container
5151
// TODO: OnBuild []string // ONBUILD metadata that were defined on the image Dockerfile
5252
Labels map[string]string `json:",omitempty"` // List of labels set to this container
5353
StopSignal string `json:",omitempty"` // Signal to stop a container
@@ -82,7 +82,7 @@ type ContainerHostConfig struct {
8282
// TODO: IpcMode IpcMode // IPC namespace to use for the container
8383
// TODO: Cgroup CgroupSpec // Cgroup to use for the container
8484
// TODO: Links []string // List of links (in the name:alias form)
85-
// TODO: OomKillDisable bool // specifies whether to disable OOM Killer
85+
OomKillDisable bool // specifies whether to disable OOM Killer
8686
// TODO: OomScoreAdj int // specifies the tune container’s OOM preferences (-1000 to 1000, rootless: 100 to 1000)
8787
// TODO: PidMode string // PID namespace to use for the container
8888
Privileged bool // Is the container in privileged mode

e2e/tests/container_create.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,101 @@ func ContainerCreate(opt *option.Option) {
654654
}
655655
}
656656
})
657+
It("should create a container with OomKillDisable set to true", func() {
658+
// Define options
659+
options.Cmd = []string{"sleep", "Infinity"}
660+
options.HostConfig.OomKillDisable = true
661+
662+
// Create container
663+
statusCode, ctr := createContainer(uClient, url, testContainerName, options)
664+
Expect(statusCode).Should(Equal(http.StatusCreated))
665+
Expect(ctr.ID).ShouldNot(BeEmpty())
666+
667+
// Start container
668+
command.Run(opt, "start", testContainerName)
669+
670+
// Inspect the container using native format to verify OomKillDisable
671+
nativeResp := command.Stdout(opt, "inspect", "--mode=native", testContainerName)
672+
var nativeInspect []map[string]interface{}
673+
err := json.Unmarshal(nativeResp, &nativeInspect)
674+
Expect(err).Should(BeNil())
675+
Expect(nativeInspect).Should(HaveLen(1))
676+
677+
// Navigate to the linux resources memory section
678+
spec, ok := nativeInspect[0]["Spec"].(map[string]interface{})
679+
Expect(ok).Should(BeTrue())
680+
linux, ok := spec["linux"].(map[string]interface{})
681+
Expect(ok).Should(BeTrue())
682+
resources, ok := linux["resources"].(map[string]interface{})
683+
Expect(ok).Should(BeTrue())
684+
memory, ok := resources["memory"].(map[string]interface{})
685+
Expect(ok).Should(BeTrue())
686+
687+
// Verify OomKillDisable is set
688+
oomKillDisable, ok := memory["disableOOMKiller"].(bool)
689+
Expect(ok).Should(BeTrue())
690+
Expect(oomKillDisable).Should(BeTrue())
691+
})
692+
693+
It("should create a container with NetworkDisabled set to true", func() {
694+
// Define options
695+
options.Cmd = []string{"sleep", "Infinity"}
696+
options.NetworkDisabled = true
697+
698+
// Create container
699+
statusCode, ctr := createContainer(uClient, url, testContainerName, options)
700+
Expect(statusCode).Should(Equal(http.StatusCreated))
701+
Expect(ctr.ID).ShouldNot(BeEmpty())
702+
703+
// Start container
704+
command.Run(opt, "start", testContainerName)
705+
706+
// Inspect using the native format to verify network mode is "none"
707+
nativeResp := command.Stdout(opt, "inspect", "--mode=native", testContainerName)
708+
var nativeInspect []map[string]interface{}
709+
err := json.Unmarshal(nativeResp, &nativeInspect)
710+
Expect(err).Should(BeNil())
711+
Expect(nativeInspect).Should(HaveLen(1))
712+
713+
// Check that network is set to "none" in nerdctl/networks label
714+
labels, ok := nativeInspect[0]["Labels"].(map[string]interface{})
715+
Expect(ok).Should(BeTrue())
716+
networks, ok := labels["nerdctl/networks"].(string)
717+
Expect(ok).Should(BeTrue())
718+
Expect(networks).Should(ContainSubstring(`"none"`))
719+
})
720+
721+
It("should create a container with specified MAC address", func() {
722+
// Define custom MAC address
723+
macAddress := "02:42:ac:11:00:42"
724+
725+
// Define options
726+
options.Cmd = []string{"sleep", "Infinity"}
727+
options.MacAddress = macAddress
728+
729+
// Create container
730+
statusCode, ctr := createContainer(uClient, url, testContainerName, options)
731+
Expect(statusCode).Should(Equal(http.StatusCreated))
732+
Expect(ctr.ID).ShouldNot(BeEmpty())
733+
734+
// Start container
735+
command.Run(opt, "start", testContainerName)
736+
737+
// Inspect container using Docker-compatible format
738+
resp := command.Stdout(opt, "inspect", testContainerName)
739+
var inspect []*dockercompat.Container
740+
err := json.Unmarshal(resp, &inspect)
741+
Expect(err).Should(BeNil())
742+
Expect(inspect).Should(HaveLen(1))
743+
744+
// Verify MAC address in NetworkSettings
745+
Expect(inspect[0].NetworkSettings.MacAddress).Should(Equal(macAddress))
746+
747+
// Also verify MAC address in the network details
748+
for _, netDetails := range inspect[0].NetworkSettings.Networks {
749+
Expect(netDetails.MacAddress).Should(Equal(macAddress))
750+
}
751+
})
657752
})
658753
}
659754

0 commit comments

Comments
 (0)