automation-tests/cmd/podman/common/create_opts.go

522 lines
14 KiB
Go

package common
import (
"fmt"
"net"
"strconv"
"strings"
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/pkg/api/handlers"
"github.com/containers/podman/v2/pkg/cgroups"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/rootless"
"github.com/containers/podman/v2/pkg/specgen"
)
type ContainerCLIOpts struct {
Annotation []string
Attach []string
Authfile string
BlkIOWeight string
BlkIOWeightDevice []string
CapAdd []string
CapDrop []string
CgroupNS string
CGroupsMode string
CGroupParent string
CIDFile string
ConmonPIDFile string
CPUPeriod uint64
CPUQuota int64
CPURTPeriod uint64
CPURTRuntime int64
CPUShares uint64
CPUS float64
CPUSetCPUs string
CPUSetMems string
Devices []string
DeviceCGroupRule []string
DeviceReadBPs []string
DeviceReadIOPs []string
DeviceWriteBPs []string
DeviceWriteIOPs []string
Entrypoint *string
Env []string
EnvHost bool
EnvFile []string
Expose []string
GIDMap []string
GroupAdd []string
HealthCmd string
HealthInterval string
HealthRetries uint
HealthStartPeriod string
HealthTimeout string
Hostname string
HTTPProxy bool
ImageVolume string
Init bool
InitPath string
Interactive bool
IPC string
KernelMemory string
Label []string
LabelFile []string
LogDriver string
LogOptions []string
Memory string
MemoryReservation string
MemorySwap string
MemorySwappiness int64
Name string
NoHealthCheck bool
OOMKillDisable bool
OOMScoreAdj int
OverrideArch string
OverrideOS string
OverrideVariant string
PID string
PIDsLimit *int64
Pod string
PodIDFile string
PreserveFDs uint
Privileged bool
PublishAll bool
Pull string
Quiet bool
ReadOnly bool
ReadOnlyTmpFS bool
Restart string
Replace bool
Rm bool
RootFS bool
SecurityOpt []string
SdNotifyMode string
ShmSize string
SignaturePolicy string
StopSignal string
StopTimeout uint
StoreageOpt []string
SubUIDName string
SubGIDName string
Sysctl []string
Systemd string
TmpFS []string
TTY bool
Timezone string
Umask string
UIDMap []string
Ulimit []string
User string
UserNS string
UTS string
Mount []string
Volume []string
VolumesFrom []string
Workdir string
SeccompPolicy string
Net *entities.NetOptions
CgroupConf []string
}
func stringMaptoArray(m map[string]string) []string {
a := make([]string, 0, len(m))
for k, v := range m {
a = append(a, fmt.Sprintf("%s=%s", k, v))
}
return a
}
// ContainerCreateToContainerCLIOpts converts a compat input struct to cliopts so it can be converted to
// a specgen spec.
func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroupsManager string) (*ContainerCLIOpts, []string, error) {
var (
capAdd []string
cappDrop []string
entrypoint *string
init bool
specPorts []specgen.PortMapping
)
if cc.HostConfig.Init != nil {
init = *cc.HostConfig.Init
}
// Iterate devices and convert back to string
devices := make([]string, 0, len(cc.HostConfig.Devices))
for _, dev := range cc.HostConfig.Devices {
devices = append(devices, fmt.Sprintf("%s:%s:%s", dev.PathOnHost, dev.PathInContainer, dev.CgroupPermissions))
}
// iterate blkreaddevicebps
readBps := make([]string, 0, len(cc.HostConfig.BlkioDeviceReadBps))
for _, dev := range cc.HostConfig.BlkioDeviceReadBps {
readBps = append(readBps, dev.String())
}
// iterate blkreaddeviceiops
readIops := make([]string, 0, len(cc.HostConfig.BlkioDeviceReadIOps))
for _, dev := range cc.HostConfig.BlkioDeviceReadIOps {
readIops = append(readIops, dev.String())
}
// iterate blkwritedevicebps
writeBps := make([]string, 0, len(cc.HostConfig.BlkioDeviceWriteBps))
for _, dev := range cc.HostConfig.BlkioDeviceWriteBps {
writeBps = append(writeBps, dev.String())
}
// iterate blkwritedeviceiops
writeIops := make([]string, 0, len(cc.HostConfig.BlkioDeviceWriteIOps))
for _, dev := range cc.HostConfig.BlkioDeviceWriteIOps {
writeIops = append(writeIops, dev.String())
}
// entrypoint
// can be a string or slice. if it is a slice, we need to
// marshall it to json; otherwise it should just be the string
// value
if len(cc.Config.Entrypoint) > 0 {
entrypoint = &cc.Config.Entrypoint[0]
if len(cc.Config.Entrypoint) > 1 {
b, err := json.Marshal(cc.Config.Entrypoint)
if err != nil {
return nil, nil, err
}
var jsonString = string(b)
entrypoint = &jsonString
}
}
// expose ports
expose := make([]string, 0, len(cc.Config.ExposedPorts))
for p := range cc.Config.ExposedPorts {
expose = append(expose, fmt.Sprintf("%s/%s", p.Port(), p.Proto()))
}
// mounts type=tmpfs/bind,source=,dest=,opt=val
// TODO options
mounts := make([]string, 0, len(cc.HostConfig.Mounts))
for _, m := range cc.HostConfig.Mounts {
mount := fmt.Sprintf("type=%s", m.Type)
if len(m.Source) > 0 {
mount += fmt.Sprintf("source=%s", m.Source)
}
if len(m.Target) > 0 {
mount += fmt.Sprintf("dest=%s", m.Target)
}
mounts = append(mounts, mount)
}
// volumes
volumes := make([]string, 0, len(cc.Config.Volumes))
for v := range cc.Config.Volumes {
volumes = append(volumes, v)
}
// dns
dns := make([]net.IP, 0, len(cc.HostConfig.DNS))
for _, d := range cc.HostConfig.DNS {
dns = append(dns, net.ParseIP(d))
}
// publish
for port, pbs := range cc.HostConfig.PortBindings {
for _, pb := range pbs {
hostport, err := strconv.Atoi(pb.HostPort)
if err != nil {
return nil, nil, err
}
tmpPort := specgen.PortMapping{
HostIP: pb.HostIP,
ContainerPort: uint16(port.Int()),
HostPort: uint16(hostport),
Range: 0,
Protocol: port.Proto(),
}
specPorts = append(specPorts, tmpPort)
}
}
// netMode
nsmode, _, err := specgen.ParseNetworkNamespace(cc.HostConfig.NetworkMode.NetworkName())
if err != nil {
return nil, nil, err
}
netNS := specgen.Namespace{
NSMode: nsmode.NSMode,
Value: nsmode.Value,
}
// network
// Note: we cannot emulate compat exactly here. we only allow specifics of networks to be
// defined when there is only one network.
netInfo := entities.NetOptions{
AddHosts: cc.HostConfig.ExtraHosts,
DNSOptions: cc.HostConfig.DNSOptions,
DNSSearch: cc.HostConfig.DNSSearch,
DNSServers: dns,
Network: netNS,
PublishPorts: specPorts,
}
// network names
switch {
case len(cc.NetworkingConfig.EndpointsConfig) > 0:
var aliases []string
endpointsConfig := cc.NetworkingConfig.EndpointsConfig
cniNetworks := make([]string, 0, len(endpointsConfig))
for netName, endpoint := range endpointsConfig {
cniNetworks = append(cniNetworks, netName)
if endpoint == nil {
continue
}
if len(endpoint.Aliases) > 0 {
aliases = append(aliases, endpoint.Aliases...)
}
}
// static IP and MAC
if len(endpointsConfig) == 1 {
for _, ep := range endpointsConfig {
if ep == nil {
continue
}
// if IP address is provided
if len(ep.IPAddress) > 0 {
staticIP := net.ParseIP(ep.IPAddress)
netInfo.StaticIP = &staticIP
}
// If MAC address is provided
if len(ep.MacAddress) > 0 {
staticMac, err := net.ParseMAC(ep.MacAddress)
if err != nil {
return nil, nil, err
}
netInfo.StaticMAC = &staticMac
}
break
}
}
netInfo.Aliases = aliases
netInfo.CNINetworks = cniNetworks
case len(cc.HostConfig.NetworkMode) > 0:
netInfo.CNINetworks = []string{string(cc.HostConfig.NetworkMode)}
}
// Note: several options here are marked as "don't need". this is based
// on speculation by Matt and I. We think that these come into play later
// like with start. We believe this is just a difference in podman/compat
cliOpts := ContainerCLIOpts{
// Attach: nil, // dont need?
Authfile: "",
CapAdd: append(capAdd, cc.HostConfig.CapAdd...),
CapDrop: append(cappDrop, cc.HostConfig.CapDrop...),
CGroupParent: cc.HostConfig.CgroupParent,
CIDFile: cc.HostConfig.ContainerIDFile,
CPUPeriod: uint64(cc.HostConfig.CPUPeriod),
CPUQuota: cc.HostConfig.CPUQuota,
CPURTPeriod: uint64(cc.HostConfig.CPURealtimePeriod),
CPURTRuntime: cc.HostConfig.CPURealtimeRuntime,
CPUShares: uint64(cc.HostConfig.CPUShares),
// CPUS: 0, // dont need?
CPUSetCPUs: cc.HostConfig.CpusetCpus,
CPUSetMems: cc.HostConfig.CpusetMems,
// Detach: false, // dont need
// DetachKeys: "", // dont need
Devices: devices,
DeviceCGroupRule: nil,
DeviceReadBPs: readBps,
DeviceReadIOPs: readIops,
DeviceWriteBPs: writeBps,
DeviceWriteIOPs: writeIops,
Entrypoint: entrypoint,
Env: cc.Config.Env,
Expose: expose,
GroupAdd: cc.HostConfig.GroupAdd,
Hostname: cc.Config.Hostname,
ImageVolume: "bind",
Init: init,
Interactive: cc.Config.OpenStdin,
IPC: string(cc.HostConfig.IpcMode),
Label: stringMaptoArray(cc.Config.Labels),
LogDriver: cc.HostConfig.LogConfig.Type,
LogOptions: stringMaptoArray(cc.HostConfig.LogConfig.Config),
Name: cc.Name,
OOMScoreAdj: cc.HostConfig.OomScoreAdj,
OverrideArch: "",
OverrideOS: "",
OverrideVariant: "",
PID: string(cc.HostConfig.PidMode),
PIDsLimit: cc.HostConfig.PidsLimit,
Privileged: cc.HostConfig.Privileged,
PublishAll: cc.HostConfig.PublishAllPorts,
Quiet: false,
ReadOnly: cc.HostConfig.ReadonlyRootfs,
ReadOnlyTmpFS: true, // podman default
Rm: cc.HostConfig.AutoRemove,
SecurityOpt: cc.HostConfig.SecurityOpt,
StopSignal: cc.Config.StopSignal,
StoreageOpt: stringMaptoArray(cc.HostConfig.StorageOpt),
Sysctl: stringMaptoArray(cc.HostConfig.Sysctls),
Systemd: "true", // podman default
TmpFS: stringMaptoArray(cc.HostConfig.Tmpfs),
TTY: cc.Config.Tty,
User: cc.Config.User,
UserNS: string(cc.HostConfig.UsernsMode),
UTS: string(cc.HostConfig.UTSMode),
Mount: mounts,
Volume: volumes,
VolumesFrom: cc.HostConfig.VolumesFrom,
Workdir: cc.Config.WorkingDir,
Net: &netInfo,
}
if !rootless.IsRootless() {
var ulimits []string
if len(cc.HostConfig.Ulimits) > 0 {
for _, ul := range cc.HostConfig.Ulimits {
ulimits = append(ulimits, ul.String())
}
cliOpts.Ulimit = ulimits
}
}
if len(cc.HostConfig.BlkioWeightDevice) > 0 {
devices := make([]string, 0, len(cc.HostConfig.BlkioWeightDevice))
for _, d := range cc.HostConfig.BlkioWeightDevice {
devices = append(devices, d.String())
}
cliOpts.BlkIOWeightDevice = devices
}
if cc.HostConfig.BlkioWeight > 0 {
cliOpts.BlkIOWeight = strconv.Itoa(int(cc.HostConfig.BlkioWeight))
}
if cc.HostConfig.Memory > 0 {
cliOpts.Memory = strconv.Itoa(int(cc.HostConfig.Memory))
}
if cc.HostConfig.MemoryReservation > 0 {
cliOpts.MemoryReservation = strconv.Itoa(int(cc.HostConfig.MemoryReservation))
}
cgroupsv2, err := cgroups.IsCgroup2UnifiedMode()
if err != nil {
return nil, nil, err
}
if cc.HostConfig.MemorySwap > 0 && (!rootless.IsRootless() || (rootless.IsRootless() && cgroupsv2)) {
cliOpts.MemorySwap = strconv.Itoa(int(cc.HostConfig.MemorySwap))
}
if cc.Config.StopTimeout != nil {
cliOpts.StopTimeout = uint(*cc.Config.StopTimeout)
}
if cc.HostConfig.ShmSize > 0 {
cliOpts.ShmSize = strconv.Itoa(int(cc.HostConfig.ShmSize))
}
if cc.HostConfig.KernelMemory > 0 {
cliOpts.KernelMemory = strconv.Itoa(int(cc.HostConfig.KernelMemory))
}
if len(cc.HostConfig.RestartPolicy.Name) > 0 {
policy := cc.HostConfig.RestartPolicy.Name
// only add restart count on failure
if cc.HostConfig.RestartPolicy.IsOnFailure() {
policy += fmt.Sprintf(":%d", cc.HostConfig.RestartPolicy.MaximumRetryCount)
}
cliOpts.Restart = policy
}
if cc.HostConfig.MemorySwappiness != nil && (!rootless.IsRootless() || rootless.IsRootless() && cgroupsv2 && cgroupsManager == "systemd") {
cliOpts.MemorySwappiness = *cc.HostConfig.MemorySwappiness
} else {
cliOpts.MemorySwappiness = -1
}
if cc.HostConfig.OomKillDisable != nil {
cliOpts.OOMKillDisable = *cc.HostConfig.OomKillDisable
}
if cc.Config.Healthcheck != nil {
cliOpts.HealthCmd = strings.Join(cc.Config.Healthcheck.Test, " ")
cliOpts.HealthInterval = cc.Config.Healthcheck.Interval.String()
cliOpts.HealthRetries = uint(cc.Config.Healthcheck.Retries)
cliOpts.HealthStartPeriod = cc.Config.Healthcheck.StartPeriod.String()
cliOpts.HealthTimeout = cc.Config.Healthcheck.Timeout.String()
}
// specgen assumes the image name is arg[0]
cmd := []string{cc.Config.Image}
cmd = append(cmd, cc.Config.Cmd...)
return &cliOpts, cmd, nil
}
func ulimits() []string {
if !registry.IsRemote() {
return containerConfig.Ulimits()
}
return nil
}
func cgroupConfig() string {
if !registry.IsRemote() {
return containerConfig.Cgroups()
}
return ""
}
func devices() []string {
if !registry.IsRemote() {
return containerConfig.Devices()
}
return nil
}
func env() []string {
if !registry.IsRemote() {
return containerConfig.Env()
}
return nil
}
func initPath() string {
if !registry.IsRemote() {
return containerConfig.InitPath()
}
return ""
}
func pidsLimit() int64 {
if !registry.IsRemote() {
return containerConfig.PidsLimit()
}
return -1
}
func policy() string {
if !registry.IsRemote() {
return containerConfig.Engine.PullPolicy
}
return ""
}
func shmSize() string {
if !registry.IsRemote() {
return containerConfig.ShmSize()
}
return ""
}
func volumes() []string {
if !registry.IsRemote() {
return containerConfig.Volumes()
}
return nil
}