aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/podman/common/create_opts.go291
-rw-r--r--cmd/podman/containers/ps.go160
2 files changed, 411 insertions, 40 deletions
diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go
index 83a25f4ab..f4fecf4b7 100644
--- a/cmd/podman/common/create_opts.go
+++ b/cmd/podman/common/create_opts.go
@@ -1,6 +1,15 @@
package common
-import "github.com/containers/podman/v2/pkg/domain/entities"
+import (
+ "fmt"
+ "net"
+ "strconv"
+ "strings"
+
+ "github.com/containers/podman/v2/pkg/api/handlers"
+ "github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/containers/podman/v2/pkg/specgen"
+)
type ContainerCLIOpts struct {
Annotation []string
@@ -111,3 +120,283 @@ type ContainerCLIOpts struct {
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) (*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
+ }
+ entrypoint = string(b)
+ }
+ }
+
+ // 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)
+ }
+ }
+
+ // network names
+ endpointsConfig := cc.NetworkingConfig.EndpointsConfig
+ cniNetworks := make([]string, 0, len(endpointsConfig))
+ for netName := range endpointsConfig {
+ cniNetworks = append(cniNetworks, netName)
+ }
+
+ // 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,
+ CNINetworks: cniNetworks,
+ DNSOptions: cc.HostConfig.DNSOptions,
+ DNSSearch: cc.HostConfig.DNSSearch,
+ DNSServers: dns,
+ Network: netNS,
+ PublishPorts: specPorts,
+ }
+
+ // static IP and MAC
+ if len(endpointsConfig) == 1 {
+ for _, ep := range endpointsConfig {
+ // 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
+ }
+ }
+
+ // 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: "",
+ BlkIOWeight: strconv.Itoa(int(cc.HostConfig.BlkioWeight)),
+ BlkIOWeightDevice: nil, // TODO
+ 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),
+ Memory: strconv.Itoa(int(cc.HostConfig.Memory)),
+ MemoryReservation: strconv.Itoa(int(cc.HostConfig.MemoryReservation)),
+ MemorySwap: strconv.Itoa(int(cc.HostConfig.MemorySwap)),
+ 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,
+ ShmSize: strconv.Itoa(int(cc.HostConfig.ShmSize)),
+ 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,
+ //Ulimit: cc.HostConfig.Ulimits, // ask dan, no documented format
+ 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 cc.Config.StopTimeout != nil {
+ cliOpts.StopTimeout = uint(*cc.Config.StopTimeout)
+ }
+
+ 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 {
+ cliOpts.MemorySwappiness = *cc.HostConfig.MemorySwappiness
+ }
+ 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.Image}
+ cmd = append(cmd, cc.Config.Cmd...)
+ return &cliOpts, cmd, nil
+}
diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go
index 41d309f51..446b46471 100644
--- a/cmd/podman/containers/ps.go
+++ b/cmd/podman/containers/ps.go
@@ -371,12 +371,6 @@ func (l psReporter) CreatedHuman() string {
// portsToString converts the ports used to a string of the from "port1, port2"
// and also groups a continuous list of ports into a readable format.
func portsToString(ports []ocicni.PortMapping) string {
- type portGroup struct {
- first int32
- last int32
- }
- portDisplay := []string{}
-
if len(ports) == 0 {
return ""
}
@@ -385,41 +379,124 @@ func portsToString(ports []ocicni.PortMapping) string {
return comparePorts(ports[i], ports[j])
})
- // portGroupMap is used for grouping continuous ports.
- portGroupMap := make(map[string]*portGroup)
- var groupKeyList []string
+ portGroups := [][]ocicni.PortMapping{}
+ currentGroup := []ocicni.PortMapping{}
+ for i, v := range ports {
+ var prevPort, nextPort *int32
+ if i > 0 {
+ prevPort = &ports[i-1].ContainerPort
+ }
+ if i+1 < len(ports) {
+ nextPort = &ports[i+1].ContainerPort
+ }
- for _, v := range ports {
+ port := v.ContainerPort
- hostIP := v.HostIP
- if hostIP == "" {
- hostIP = "0.0.0.0"
+ // Helper functions
+ addToCurrentGroup := func(x ocicni.PortMapping) {
+ currentGroup = append(currentGroup, x)
}
- // If hostPort and containerPort are not same, consider as individual port.
- if v.ContainerPort != v.HostPort {
- portDisplay = append(portDisplay, fmt.Sprintf("%s:%d->%d/%s", hostIP, v.HostPort, v.ContainerPort, v.Protocol))
- continue
+
+ addToPortGroup := func(x ocicni.PortMapping) {
+ portGroups = append(portGroups, []ocicni.PortMapping{x})
+ }
+
+ finishCurrentGroup := func() {
+ portGroups = append(portGroups, currentGroup)
+ currentGroup = []ocicni.PortMapping{}
}
- portMapKey := fmt.Sprintf("%s/%s", hostIP, v.Protocol)
+ // Single entry slice
+ if prevPort == nil && nextPort == nil {
+ addToPortGroup(v)
+ }
+
+ // Start of the slice with len > 0
+ if prevPort == nil && nextPort != nil {
+ isGroup := *nextPort-1 == port
+
+ if isGroup {
+ // Start with a group
+ addToCurrentGroup(v)
+ } else {
+ // Start with single item
+ addToPortGroup(v)
+ }
- portgroup, ok := portGroupMap[portMapKey]
- if !ok {
- portGroupMap[portMapKey] = &portGroup{first: v.ContainerPort, last: v.ContainerPort}
- // This list is required to traverse portGroupMap.
- groupKeyList = append(groupKeyList, portMapKey)
continue
}
- if portgroup.last == (v.ContainerPort - 1) {
- portgroup.last = v.ContainerPort
+ // Middle of the slice with len > 0
+ if prevPort != nil && nextPort != nil {
+ currentIsGroup := *prevPort+1 == port
+ nextIsGroup := *nextPort-1 == port
+
+ if currentIsGroup {
+ // Maybe in the middle of a group
+ addToCurrentGroup(v)
+
+ if !nextIsGroup {
+ // End of a group
+ finishCurrentGroup()
+ }
+ } else if nextIsGroup {
+ // Start of a new group
+ addToCurrentGroup(v)
+ } else {
+ // No group at all
+ addToPortGroup(v)
+ }
+
continue
}
+
+ // End of the slice with len > 0
+ if prevPort != nil && nextPort == nil {
+ isGroup := *prevPort+1 == port
+
+ if isGroup {
+ // End group
+ addToCurrentGroup(v)
+ finishCurrentGroup()
+ } else {
+ // End single item
+ addToPortGroup(v)
+ }
+ }
}
- // For each portMapKey, format group list and append to output string.
- for _, portKey := range groupKeyList {
- group := portGroupMap[portKey]
- portDisplay = append(portDisplay, formatGroup(portKey, group.first, group.last))
+
+ portDisplay := []string{}
+ for _, group := range portGroups {
+ if len(group) == 0 {
+ // Usually should not happen, but better do not crash.
+ continue
+ }
+
+ first := group[0]
+
+ hostIP := first.HostIP
+ if hostIP == "" {
+ hostIP = "0.0.0.0"
+ }
+
+ // Single mappings
+ if len(group) == 1 {
+ portDisplay = append(portDisplay,
+ fmt.Sprintf(
+ "%s:%d->%d/%s",
+ hostIP, first.HostPort, first.ContainerPort, first.Protocol,
+ ),
+ )
+ continue
+ }
+
+ // Group mappings
+ last := group[len(group)-1]
+ portDisplay = append(portDisplay, formatGroup(
+ fmt.Sprintf("%s/%s", hostIP, first.Protocol),
+ first.HostPort, last.HostPort,
+ first.ContainerPort, last.ContainerPort,
+ ))
}
return strings.Join(portDisplay, ", ")
}
@@ -440,9 +517,10 @@ func comparePorts(i, j ocicni.PortMapping) bool {
return i.Protocol < j.Protocol
}
-// formatGroup returns the group as <IP:startPort:lastPort->startPort:lastPort/Proto>
-// e.g 0.0.0.0:1000-1006->1000-1006/tcp.
-func formatGroup(key string, start, last int32) string {
+// formatGroup returns the group in the format:
+// <IP:firstHost:lastHost->firstCtr:lastCtr/Proto>
+// e.g 0.0.0.0:1000-1006->2000-2006/tcp.
+func formatGroup(key string, firstHost, lastHost, firstCtr, lastCtr int32) string {
parts := strings.Split(key, "/")
groupType := parts[0]
var ip string
@@ -450,12 +528,16 @@ func formatGroup(key string, start, last int32) string {
ip = parts[0]
groupType = parts[1]
}
- group := strconv.Itoa(int(start))
- if start != last {
- group = fmt.Sprintf("%s-%d", group, last)
- }
- if ip != "" {
- group = fmt.Sprintf("%s:%s->%s", ip, group, group)
+
+ group := func(first, last int32) string {
+ group := strconv.Itoa(int(first))
+ if first != last {
+ group = fmt.Sprintf("%s-%d", group, last)
+ }
+ return group
}
- return fmt.Sprintf("%s/%s", group, groupType)
+ hostGroup := group(firstHost, lastHost)
+ ctrGroup := group(firstCtr, lastCtr)
+
+ return fmt.Sprintf("%s:%s->%s/%s", ip, hostGroup, ctrGroup, groupType)
}