Skip to content

Commit

Permalink
Merge pull request containers#11890 from Luap99/ports
Browse files Browse the repository at this point in the history
libpod: deduplicate ports in db
  • Loading branch information
openshift-merge-robot authored Nov 6, 2021
2 parents 6805bef + 0136a66 commit abbd6c1
Show file tree
Hide file tree
Showing 26 changed files with 2,181 additions and 648 deletions.
50 changes: 29 additions & 21 deletions cmd/podman/containers/port.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"github.com/containers/podman/v3/cmd/podman/common"
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/cmd/podman/validate"
"github.com/containers/podman/v3/libpod/network/types"
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/pkg/errors"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -73,7 +72,8 @@ func port(_ *cobra.Command, args []string) error {
var (
container string
err error
userPort types.OCICNIPortMapping
userPort uint16
userProto string
)

if len(args) == 0 && !portOpts.Latest && !portOpts.All {
Expand Down Expand Up @@ -101,16 +101,12 @@ func port(_ *cobra.Command, args []string) error {
fields = append(fields, "tcp")
}

portNum, err := strconv.Atoi(fields[0])
portNum, err := strconv.ParseUint(fields[0], 10, 16)
if err != nil {
return err
}
userPort = types.OCICNIPortMapping{
HostPort: 0,
ContainerPort: int32(portNum),
Protocol: fields[1],
HostIP: "",
}
userPort = uint16(portNum)
userProto = fields[1]
}

reports, err := registry.ContainerEngine().ContainerPort(registry.GetContext(), container, portOpts)
Expand All @@ -120,24 +116,36 @@ func port(_ *cobra.Command, args []string) error {
var found bool
// Iterate mappings
for _, report := range reports {
allPrefix := ""
if portOpts.All {
allPrefix = report.Id[:12] + "\t"
}
for _, v := range report.Ports {
hostIP := v.HostIP
// Set host IP to 0.0.0.0 if blank
if hostIP == "" {
hostIP = "0.0.0.0"
}
if portOpts.All {
fmt.Printf("%s\t", report.Id[:12])
}
// If not searching by port or port/proto, then dump what we see
if port == "" {
fmt.Printf("%d/%s -> %s:%d\n", v.ContainerPort, v.Protocol, hostIP, v.HostPort)
continue
}
if v.ContainerPort == userPort.ContainerPort {
fmt.Printf("%s:%d\n", hostIP, v.HostPort)
found = true
break
protocols := strings.Split(v.Protocol, ",")
for _, protocol := range protocols {
// If not searching by port or port/proto, then dump what we see
if port == "" {
for i := uint16(0); i < v.Range; i++ {
fmt.Printf("%s%d/%s -> %s:%d\n", allPrefix, v.ContainerPort+i, protocol, hostIP, v.HostPort+i)
}
continue
}
// check if the proto matches and if the port is in the range
// this is faster than looping over the range for no reason
if v.Protocol == userProto &&
v.ContainerPort <= userPort &&
v.ContainerPort+v.Range > userPort {
// we have to add the current range to the host port
hostPort := v.HostPort + userPort - v.ContainerPort
fmt.Printf("%s%s:%d\n", allPrefix, hostIP, hostPort)
found = true
break
}
}
}
if !found && port != "" {
Expand Down
183 changes: 19 additions & 164 deletions cmd/podman/containers/ps.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package containers
import (
"fmt"
"os"
"sort"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -477,174 +475,31 @@ func (l psReporter) UTS() 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 []types.OCICNIPortMapping) string {
// The format is IP:HostPort(-Range)->ContainerPort(-Range)/Proto
func portsToString(ports []types.PortMapping) string {
if len(ports) == 0 {
return ""
}
// Sort the ports, so grouping continuous ports become easy.
sort.Slice(ports, func(i, j int) bool {
return comparePorts(ports[i], ports[j])
})

portGroups := [][]types.OCICNIPortMapping{}
currentGroup := []types.OCICNIPortMapping{}
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
}

port := v.ContainerPort

// Helper functions
addToCurrentGroup := func(x types.OCICNIPortMapping) {
currentGroup = append(currentGroup, x)
}

addToPortGroup := func(x types.OCICNIPortMapping) {
portGroups = append(portGroups, []types.OCICNIPortMapping{x})
}

finishCurrentGroup := func() {
portGroups = append(portGroups, currentGroup)
currentGroup = []types.OCICNIPortMapping{}
}

// 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)
}

continue
}

// 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)
}
}
}

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
sb := &strings.Builder{}
for _, port := range ports {
hostIP := port.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, ", ")
}

func comparePorts(i, j types.OCICNIPortMapping) bool {
if i.ContainerPort != j.ContainerPort {
return i.ContainerPort < j.ContainerPort
}

if i.HostIP != j.HostIP {
return i.HostIP < j.HostIP
}

if i.HostPort != j.HostPort {
return i.HostPort < j.HostPort
}

return i.Protocol < j.Protocol
}

// 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
if len(parts) > 1 {
ip = parts[0]
groupType = parts[1]
}

group := func(first, last int32) string {
group := strconv.Itoa(int(first))
if first != last {
group = fmt.Sprintf("%s-%d", group, last)
protocols := strings.Split(port.Protocol, ",")
for _, protocol := range protocols {
if port.Range > 1 {
fmt.Fprintf(sb, "%s:%d-%d->%d-%d/%s, ",
hostIP, port.HostPort, port.HostPort+port.Range-1,
port.ContainerPort, port.ContainerPort+port.Range-1, protocol)
} else {
fmt.Fprintf(sb, "%s:%d->%d/%s, ",
hostIP, port.HostPort,
port.ContainerPort, protocol)
}
}
return group
}
hostGroup := group(firstHost, lastHost)
ctrGroup := group(firstCtr, lastCtr)

return fmt.Sprintf("%s:%s->%s/%s", ip, hostGroup, ctrGroup, groupType)
display := sb.String()
// make sure to trim the last ", " of the string
return display[:len(display)-2]
}
42 changes: 24 additions & 18 deletions cmd/rootlessport/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"os"
"os/exec"
"path/filepath"
"strings"

"github.com/containernetworking/plugins/pkg/ns"
"github.com/containers/podman/v3/libpod/network/types"
Expand Down Expand Up @@ -289,25 +290,30 @@ func handler(ctx context.Context, conn io.Reader, pm rkport.Manager) error {
return nil
}

func exposePorts(pm rkport.Manager, portMappings []types.OCICNIPortMapping, childIP string) error {
func exposePorts(pm rkport.Manager, portMappings []types.PortMapping, childIP string) error {
ctx := context.TODO()
for _, i := range portMappings {
hostIP := i.HostIP
if hostIP == "" {
hostIP = "0.0.0.0"
}
spec := rkport.Spec{
Proto: i.Protocol,
ParentIP: hostIP,
ParentPort: int(i.HostPort),
ChildPort: int(i.ContainerPort),
ChildIP: childIP,
}
if err := rkportutil.ValidatePortSpec(spec, nil); err != nil {
return err
}
if _, err := pm.AddPort(ctx, spec); err != nil {
return err
for _, port := range portMappings {
protocols := strings.Split(port.Protocol, ",")
for _, protocol := range protocols {
hostIP := port.HostIP
if hostIP == "" {
hostIP = "0.0.0.0"
}
for i := uint16(0); i < port.Range; i++ {
spec := rkport.Spec{
Proto: protocol,
ParentIP: hostIP,
ParentPort: int(port.HostPort + i),
ChildPort: int(port.ContainerPort + i),
ChildIP: childIP,
}
if err := rkportutil.ValidatePortSpec(spec, nil); err != nil {
return err
}
if _, err := pm.AddPort(ctx, spec); err != nil {
return err
}
}
}
}
return nil
Expand Down
9 changes: 9 additions & 0 deletions libpod/boltdb_state_internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,15 @@ func (s *BoltState) getContainerConfigFromDB(id []byte, config *ContainerConfig,
return errors.Wrapf(err, "error unmarshalling container %s config", string(id))
}

// convert ports to the new format if needed
if len(config.ContainerNetworkConfig.OldPortMappings) > 0 && len(config.ContainerNetworkConfig.PortMappings) == 0 {
config.ContainerNetworkConfig.PortMappings = ocicniPortsToNetTypesPorts(config.ContainerNetworkConfig.OldPortMappings)
// keep the OldPortMappings in case an user has to downgrade podman

// indicate the the config was modified and should be written back to the db when possible
config.rewrite = true
}

return nil
}

Expand Down
4 changes: 3 additions & 1 deletion libpod/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,20 @@ func getTestContainer(id, name string, manager lock.Manager) (*Container, error)
ContainerNetworkConfig: ContainerNetworkConfig{
DNSServer: []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("192.168.2.2")},
DNSSearch: []string{"example.com", "example.example.com"},
PortMappings: []types.OCICNIPortMapping{
PortMappings: []types.PortMapping{
{
HostPort: 80,
ContainerPort: 90,
Protocol: "tcp",
HostIP: "192.168.3.3",
Range: 1,
},
{
HostPort: 100,
ContainerPort: 110,
Protocol: "udp",
HostIP: "192.168.4.4",
Range: 1,
},
},
},
Expand Down
2 changes: 1 addition & 1 deletion libpod/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ func (c *Container) NewNetNS() bool {
// PortMappings returns the ports that will be mapped into a container if
// a new network namespace is created
// If NewNetNS() is false, this value is unused
func (c *Container) PortMappings() ([]types.OCICNIPortMapping, error) {
func (c *Container) PortMappings() ([]types.PortMapping, error) {
// First check if the container belongs to a network namespace (like a pod)
if len(c.config.NetNsCtr) > 0 {
netNsCtr, err := c.runtime.GetContainer(c.config.NetNsCtr)
Expand Down
Loading

0 comments on commit abbd6c1

Please sign in to comment.