Skip to content

feat: Add OomKillDisabled, NetworkDisabled and MACAddress option #86

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions api/handlers/container/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,15 +172,16 @@ func (h *handler) create(w http.ResponseWriter, r *http.Request) {
GOptions: globalOpt,

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

// #region for platform flags
Expand Down Expand Up @@ -264,14 +265,19 @@ func (h *handler) create(w http.ResponseWriter, r *http.Request) {
if req.HostConfig.DNSOptions != nil {
dnsOpt = req.HostConfig.DNSOptions
}

if req.NetworkDisabled {
networkMode = "none"
}
netOpt := ncTypes.NetworkOptions{
Hostname: req.Hostname,
NetworkSlice: []string{networkMode}, // TODO: Set to none if "NetworkDisabled" is true in request
NetworkSlice: []string{networkMode},
DNSServers: req.HostConfig.DNS, // Custom DNS lookup servers.
DNSResolvConfOptions: dnsOpt, // DNS options.
DNSSearchDomains: req.HostConfig.DNSSearch, // Custom DNS search domains.
PortMappings: portMappings,
AddHost: req.HostConfig.ExtraHosts, // Extra hosts.
MACAddress: req.MacAddress,
}

ctx := namespaces.WithNamespace(r.Context(), h.Config.Namespace)
Expand Down
57 changes: 57 additions & 0 deletions api/handlers/container/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,63 @@ var _ = Describe("Container Create API ", func() {
Expect(rr.Body.String()).Should(ContainSubstring("failed to parse"))
})

It("should set specified NetworkDisabled setting", func() {
body := []byte(`{
"Image": "test-image",
"NetworkDisabled": true
}`)
req, _ := http.NewRequest(http.MethodPost, "/containers/create", bytes.NewReader(body))

// expected network options
netOpt.NetworkSlice = []string{"none"}

service.EXPECT().Create(gomock.Any(), "test-image", nil, equalTo(createOpt), equalTo(netOpt)).Return(
cid, nil)

// handler should return success message with 201 status code.
h.create(rr, req)
Expect(rr).Should(HaveHTTPStatus(http.StatusCreated))
Expect(rr.Body).Should(MatchJSON(jsonResponse))
})

It("should set the MACAddress to a user specified value", func() {
body := []byte(`{
"Image": "test-image",
"MacAddress": "12:34:56:78:9a:bc"
}`)
req, _ := http.NewRequest(http.MethodPost, "/containers/create", bytes.NewReader(body))

// expected network options
netOpt.MACAddress = "12:34:56:78:9a:bc"
service.EXPECT().Create(gomock.Any(), "test-image", nil, equalTo(createOpt), equalTo(netOpt)).Return(
cid, nil)

// handler should return success message with 201 status code.
h.create(rr, req)
Expect(rr).Should(HaveHTTPStatus(http.StatusCreated))
Expect(rr.Body).Should(MatchJSON(jsonResponse))
})

It("should set the OomKillDisable option", func() {
body := []byte(`{
"Image": "test-image",
"HostConfig": {
"OomKillDisable": true
}
}`)
req, _ := http.NewRequest(http.MethodPost, "/containers/create", bytes.NewReader(body))

// expected network options
createOpt.OomKillDisable = true
service.EXPECT().Create(gomock.Any(), "test-image", nil, equalTo(createOpt), equalTo(netOpt)).Return(
cid, nil)

// handler should return success message with 201 status code.
h.create(rr, req)
Expect(rr).Should(HaveHTTPStatus(http.StatusCreated))
Expect(rr.Body).Should(MatchJSON(jsonResponse))
})

Context("translate port mappings", func() {
It("should return empty if port mappings is nil", func() {
Expect(translatePortMappings(nil)).Should(BeEmpty())
Expand Down
14 changes: 7 additions & 7 deletions api/types/container_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ type ContainerConfig struct {
Cmd []string `json:",omitempty"` // Command to run when starting the container
// TODO Healthcheck *HealthConfig `json:",omitempty"` // Healthcheck describes how to check the container is healthy
// TODO: ArgsEscaped bool `json:",omitempty"` // True if command is already escaped (meaning treat as a command line) (Windows specific).
Image string // Name of the image as it was passed by the operator (e.g. could be symbolic)
Volumes map[string]struct{} `json:",omitempty"` // List of volumes (mounts) used for the container
WorkingDir string `json:",omitempty"` // Current directory (PWD) in the command will be launched
Entrypoint []string `json:",omitempty"` // Entrypoint to run when starting the container
// TODO: NetworkDisabled bool `json:",omitempty"` // Is network disabled
// TODO: MacAddress string `json:",omitempty"` // Mac Address of the container
Image string // Name of the image as it was passed by the operator (e.g. could be symbolic)
Volumes map[string]struct{} `json:",omitempty"` // List of volumes (mounts) used for the container
WorkingDir string `json:",omitempty"` // Current directory (PWD) in the command will be launched
Entrypoint []string `json:",omitempty"` // Entrypoint to run when starting the container
NetworkDisabled bool `json:",omitempty"` // Is network disabled
MacAddress string `json:",omitempty"` // Mac Address of the container
// TODO: OnBuild []string // ONBUILD metadata that were defined on the image Dockerfile
Labels map[string]string `json:",omitempty"` // List of labels set to this container
StopSignal string `json:",omitempty"` // Signal to stop a container
Expand Down Expand Up @@ -82,7 +82,7 @@ type ContainerHostConfig struct {
// TODO: IpcMode IpcMode // IPC namespace to use for the container
// TODO: Cgroup CgroupSpec // Cgroup to use for the container
// TODO: Links []string // List of links (in the name:alias form)
// TODO: OomKillDisable bool // specifies whether to disable OOM Killer
OomKillDisable bool // specifies whether to disable OOM Killer
// TODO: OomScoreAdj int // specifies the tune container’s OOM preferences (-1000 to 1000, rootless: 100 to 1000)
// TODO: PidMode string // PID namespace to use for the container
Privileged bool // Is the container in privileged mode
Expand Down
95 changes: 95 additions & 0 deletions e2e/tests/container_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,101 @@ func ContainerCreate(opt *option.Option) {
}
}
})
It("should create a container with OomKillDisable set to true", func() {
// Define options
options.Cmd = []string{"sleep", "Infinity"}
options.HostConfig.OomKillDisable = true

// Create container
statusCode, ctr := createContainer(uClient, url, testContainerName, options)
Expect(statusCode).Should(Equal(http.StatusCreated))
Expect(ctr.ID).ShouldNot(BeEmpty())

// Start container
command.Run(opt, "start", testContainerName)

// Inspect the container using native format to verify OomKillDisable
nativeResp := command.Stdout(opt, "inspect", "--mode=native", testContainerName)
var nativeInspect []map[string]interface{}
err := json.Unmarshal(nativeResp, &nativeInspect)
Expect(err).Should(BeNil())
Expect(nativeInspect).Should(HaveLen(1))

// Navigate to the linux resources memory section
spec, ok := nativeInspect[0]["Spec"].(map[string]interface{})
Expect(ok).Should(BeTrue())
linux, ok := spec["linux"].(map[string]interface{})
Expect(ok).Should(BeTrue())
resources, ok := linux["resources"].(map[string]interface{})
Expect(ok).Should(BeTrue())
memory, ok := resources["memory"].(map[string]interface{})
Expect(ok).Should(BeTrue())

// Verify OomKillDisable is set
oomKillDisable, ok := memory["disableOOMKiller"].(bool)
Expect(ok).Should(BeTrue())
Expect(oomKillDisable).Should(BeTrue())
})

It("should create a container with NetworkDisabled set to true", func() {
// Define options
options.Cmd = []string{"sleep", "Infinity"}
options.NetworkDisabled = true

// Create container
statusCode, ctr := createContainer(uClient, url, testContainerName, options)
Expect(statusCode).Should(Equal(http.StatusCreated))
Expect(ctr.ID).ShouldNot(BeEmpty())

// Start container
command.Run(opt, "start", testContainerName)

// Inspect using the native format to verify network mode is "none"
nativeResp := command.Stdout(opt, "inspect", "--mode=native", testContainerName)
var nativeInspect []map[string]interface{}
err := json.Unmarshal(nativeResp, &nativeInspect)
Expect(err).Should(BeNil())
Expect(nativeInspect).Should(HaveLen(1))

// Check that network is set to "none" in nerdctl/networks label
labels, ok := nativeInspect[0]["Labels"].(map[string]interface{})
Expect(ok).Should(BeTrue())
networks, ok := labels["nerdctl/networks"].(string)
Expect(ok).Should(BeTrue())
Expect(networks).Should(ContainSubstring(`"none"`))
})

It("should create a container with specified MAC address", func() {
// Define custom MAC address
macAddress := "02:42:ac:11:00:42"

// Define options
options.Cmd = []string{"sleep", "Infinity"}
options.MacAddress = macAddress

// Create container
statusCode, ctr := createContainer(uClient, url, testContainerName, options)
Expect(statusCode).Should(Equal(http.StatusCreated))
Expect(ctr.ID).ShouldNot(BeEmpty())

// Start container
command.Run(opt, "start", testContainerName)

// Inspect container using Docker-compatible format
resp := command.Stdout(opt, "inspect", testContainerName)
var inspect []*dockercompat.Container
err := json.Unmarshal(resp, &inspect)
Expect(err).Should(BeNil())
Expect(inspect).Should(HaveLen(1))

// Verify MAC address in NetworkSettings
Expect(inspect[0].NetworkSettings.MacAddress).Should(Equal(macAddress))

// Also verify MAC address in the network details
for _, netDetails := range inspect[0].NetworkSettings.Networks {
Expect(netDetails.MacAddress).Should(Equal(macAddress))
}
})
})
}

Expand Down
Loading