Skip to content
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

feat: Update container create api [WIP - DO NOT MERGE] #115

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
184 changes: 166 additions & 18 deletions api/handlers/container/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,67 @@ func (h *handler) create(w http.ResponseWriter, r *http.Request) {
}
}

// Tmpfs:
// Tmpfs are passed in as a map of strings,
// but nerdctl expects an array of strings with format [TMPFS1:VALUE1, TMPFS2:VALUE2, ...].
tmpfs := []string{}
if req.HostConfig.Tmpfs != nil {
for key, val := range req.HostConfig.Tmpfs {
tmpfs = append(tmpfs, fmt.Sprintf("%s:%s", key, val))
}
}

// Sysctls:
// Sysctls are passed in as a map of strings,
// but nerdctl expects an array of strings with format [Sysctls1=VALUE1, Sysctls2=VALUE2, ...].
sysctls := []string{}
if req.HostConfig.Sysctls != nil {
for key, val := range req.HostConfig.Sysctls {
sysctls = append(sysctls, fmt.Sprintf("%s=%s", key, val))
}
}

// Annotations: TODO - available in nerdctl 2.0
// Annotations are passed in as a map of strings,
// but nerdctl expects an array of strings with format [annotations1=VALUE1, annotations2=VALUE2, ...].
// annotations := []string{}
// if req.HostConfig.Annotations != nil {
// for key, val := range req.HostConfig.Annotations {
// annotations = append(annotations, fmt.Sprintf("%s=%s", key, val))
// }
// }

ulimits := []string{}
if req.HostConfig.Ulimits != nil {
for _, ulimit := range req.HostConfig.Ulimits {
ulimits = append(ulimits, ulimit.String())
}
}

// devices:
// devices are passed in as a map of DeviceMapping,
// but nerdctl expects an array of strings with format [PathOnHost1:PathInContainer1:CgroupPermissions1, PathOnHost2:PathInContainer2:CgroupPermissions2, ...].
devices := []string{}
if req.HostConfig.Devices != nil {
for _, deviceMap := range req.HostConfig.Devices {
deviceString := ""
if deviceMap.PathOnHost != "" {
deviceString += deviceMap.PathOnHost
}

if deviceMap.PathInContainer != "" {
deviceString += ":"
deviceString += deviceMap.PathInContainer
}

if deviceMap.CgroupPermissions != "" {
deviceString += ":"
deviceString += deviceMap.CgroupPermissions
}
devices = append(devices, deviceString)
}
}

// Environment vars:
env := []string{}
if req.Env != nil {
Expand All @@ -119,21 +180,85 @@ func (h *handler) create(w http.ResponseWriter, r *http.Request) {
capAdd = req.HostConfig.CapAdd
}

capDrop := []string{}
if req.HostConfig.CapDrop != nil {
capDrop = req.HostConfig.CapDrop
}

CpuQuota := int64(-1)
if req.HostConfig.CPUQuota != 0 {
CpuQuota = req.HostConfig.CPUQuota
}

memoryReservation := ""
if req.HostConfig.MemoryReservation != 0 {
memoryReservation = fmt.Sprint(req.HostConfig.MemoryReservation)
}

memorySwap := ""
if req.HostConfig.MemorySwap != 0 {
memorySwap = fmt.Sprint(req.HostConfig.MemorySwap)
}

memorySwappiness := int64(-1)
if req.HostConfig.MemorySwappiness > 0 {
memorySwappiness = req.HostConfig.MemorySwappiness
}

shmSize := ""
if req.HostConfig.ShmSize > 0 {
shmSize = fmt.Sprint(req.HostConfig.ShmSize)
}

runtime := defaults.Runtime
if req.HostConfig.Runtime != "" {
runtime = req.HostConfig.Runtime
}

volumesFrom := []string{}
if req.HostConfig.VolumesFrom != nil {
volumesFrom = req.HostConfig.VolumesFrom
}

groupAdd := []string{}
if req.HostConfig.GroupAdd != nil {
groupAdd = req.HostConfig.GroupAdd
}

securityOpt := []string{}
if req.HostConfig.SecurityOpt != nil {
securityOpt = req.HostConfig.SecurityOpt
}

pidLimit := int64(-1)
if req.HostConfig.PidsLimit > 0 {
pidLimit = req.HostConfig.PidsLimit
}

cgroupnsMode := defaults.CgroupnsMode()
if req.HostConfig.CgroupnsMode.Valid() {
cgroupnsMode = string(req.HostConfig.CgroupnsMode)
}

globalOpt := ncTypes.GlobalCommandOptions(*h.Config)
createOpt := ncTypes.ContainerCreateOptions{
Stdout: nil,
Stderr: nil,
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,
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,
OomKillDisable: req.HostConfig.OomKillDisable,
CidFile: req.HostConfig.ContainerIDFile, // CidFile write the container ID to the file
OomScoreAdj: req.HostConfig.OomScoreAdj,
Pid: req.HostConfig.PidMode, // Pid namespace to use
// #endregion

// #region for platform flags
Expand All @@ -147,29 +272,43 @@ func (h *handler) create(w http.ResponseWriter, r *http.Request) {
// #region for resource flags
CPUShares: uint64(req.HostConfig.CPUShares), // CPU shares (relative weight)
Memory: memory, // memory limit (in bytes)
CPUQuota: -1, // nerdctl default.
MemorySwappiness64: -1, // nerdctl default.
PidsLimit: -1, // nerdctl default.
Cgroupns: defaults.CgroupnsMode(), // nerdctl default.
CPUQuota: CpuQuota, // nerdctl default.
MemorySwappiness64: memorySwappiness, // Tuning container memory swappiness behaviour
PidsLimit: pidLimit, // PidsLimit specifies the tune container pids limit
Cgroupns: cgroupnsMode, // Cgroupns specifies the cgroup namespace to use
BlkioWeight: req.HostConfig.BlkioWeight, // block IO weight (relative)
CPUPeriod: uint64(req.HostConfig.CPUPeriod), // CPU CFS (Completely Fair Scheduler) period
CPUSetCPUs: req.HostConfig.CPUSetCPUs, // CpusetCpus 0-2, 0,1
CPUSetMems: req.HostConfig.CPUSetMems, // CpusetMems 0-2, 0,1
MemoryReservation: memoryReservation, // Memory soft limit (in bytes)
MemorySwap: memorySwap, // Total memory usage (memory + swap); set `-1` to enable unlimited swap
IPC: req.HostConfig.IpcMode, // IPC namespace to use
ShmSize: shmSize, // ShmSize set the size of /dev/shm
Ulimit: ulimits, // List of ulimits to be set in the container
Device: devices, // Device specifies add a host device to the container
// #endregion

// #region for user flags
User: req.User,
GroupAdd: []string{}, // nerdctl default.
GroupAdd: groupAdd,
// #endregion

// #region for security flags
SecurityOpt: []string{}, // nerdctl default.
SecurityOpt: securityOpt, // nerdctl default.
CapAdd: capAdd,
CapDrop: []string{}, // nerdctl default.
CapDrop: capDrop,
Privileged: req.HostConfig.Privileged,
// #endregion

// #region for runtime flags
Runtime: defaults.Runtime, // nerdctl default.
Runtime: runtime, // Runtime to use for this container, e.g. "crun", or "io.containerd.runc.v2".
Sysctl: sysctls, // Sysctl set sysctl options, e.g "net.ipv4.ip_forward=1"
// #endregion

// #region for volume flags
Volume: volumes,
Volume: volumes,
VolumesFrom: volumesFrom,
Tmpfs: tmpfs,
// #endregion

// #region for env flags
Expand Down Expand Up @@ -198,6 +337,10 @@ func (h *handler) create(w http.ResponseWriter, r *http.Request) {
Stderr: nil,
},
// #endregion

// #region for rootfs flags
ReadOnly: req.HostConfig.ReadonlyRootfs, // Is the container root filesystem in read-only
// #endregion
}

portMappings, err := translatePortMappings(req.HostConfig.PortBindings)
Expand All @@ -210,18 +353,23 @@ func (h *handler) create(w http.ResponseWriter, r *http.Request) {
if networkMode == "" || networkMode == "default" {
networkMode = "bridge"
}
if req.NetworkDisabled {
networkMode = "none"
}
dnsOpt := []string{}
if req.HostConfig.DNSOptions != nil {
dnsOpt = req.HostConfig.DNSOptions
}
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,
UTSNamespace: req.HostConfig.UTSMode,
}

ctx := namespaces.WithNamespace(r.Context(), h.Config.Namespace)
Expand Down
Loading
Loading