github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/cmd/podmanV2/common/ports.go (about) 1 package common 2 3 import ( 4 "fmt" 5 "net" 6 "strconv" 7 8 "github.com/cri-o/ocicni/pkg/ocicni" 9 "github.com/docker/go-connections/nat" 10 "github.com/pkg/errors" 11 "github.com/sirupsen/logrus" 12 ) 13 14 // ExposedPorts parses user and image ports and returns binding information 15 func ExposedPorts(expose []string, publish []ocicni.PortMapping, publishAll bool, imageExposedPorts map[string]struct{}) ([]ocicni.PortMapping, error) { 16 containerPorts := make(map[string]string) 17 18 // TODO this needs to be added into a something that 19 // has access to an imageengine 20 // add expose ports from the image itself 21 //for expose := range imageExposedPorts { 22 // _, port := nat.SplitProtoPort(expose) 23 // containerPorts[port] = "" 24 //} 25 26 // add the expose ports from the user (--expose) 27 // can be single or a range 28 for _, expose := range expose { 29 //support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>] 30 _, port := nat.SplitProtoPort(expose) 31 //parse the start and end port and create a sequence of ports to expose 32 //if expose a port, the start and end port are the same 33 start, end, err := nat.ParsePortRange(port) 34 if err != nil { 35 return nil, fmt.Errorf("invalid range format for --expose: %s, error: %s", expose, err) 36 } 37 for i := start; i <= end; i++ { 38 containerPorts[strconv.Itoa(int(i))] = "" 39 } 40 } 41 42 // TODO/FIXME this is hell reencarnated 43 // parse user inputted port bindings 44 pbPorts, portBindings, err := nat.ParsePortSpecs([]string{}) 45 if err != nil { 46 return nil, err 47 } 48 49 // delete exposed container ports if being used by -p 50 for i := range pbPorts { 51 delete(containerPorts, i.Port()) 52 } 53 54 // iterate container ports and make port bindings from them 55 if publishAll { 56 for e := range containerPorts { 57 //support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>] 58 //proto, port := nat.SplitProtoPort(e) 59 p, err := nat.NewPort("tcp", e) 60 if err != nil { 61 return nil, err 62 } 63 rp, err := getRandomPort() 64 if err != nil { 65 return nil, err 66 } 67 logrus.Debug(fmt.Sprintf("Using random host port %d with container port %d", rp, p.Int())) 68 portBindings[p] = CreatePortBinding(rp, "") 69 } 70 } 71 72 // We need to see if any host ports are not populated and if so, we need to assign a 73 // random port to them. 74 for k, pb := range portBindings { 75 if pb[0].HostPort == "" { 76 hostPort, err := getRandomPort() 77 if err != nil { 78 return nil, err 79 } 80 logrus.Debug(fmt.Sprintf("Using random host port %d with container port %s", hostPort, k.Port())) 81 pb[0].HostPort = strconv.Itoa(hostPort) 82 } 83 } 84 var pms []ocicni.PortMapping 85 for k, v := range portBindings { 86 for _, pb := range v { 87 hp, err := strconv.Atoi(pb.HostPort) 88 if err != nil { 89 return nil, err 90 } 91 pms = append(pms, ocicni.PortMapping{ 92 HostPort: int32(hp), 93 ContainerPort: int32(k.Int()), 94 //Protocol: "", 95 HostIP: pb.HostIP, 96 }) 97 } 98 } 99 return pms, nil 100 } 101 102 func getRandomPort() (int, error) { 103 l, err := net.Listen("tcp", ":0") 104 if err != nil { 105 return 0, errors.Wrapf(err, "unable to get free port") 106 } 107 defer l.Close() 108 _, randomPort, err := net.SplitHostPort(l.Addr().String()) 109 if err != nil { 110 return 0, errors.Wrapf(err, "unable to determine free port") 111 } 112 rp, err := strconv.Atoi(randomPort) 113 if err != nil { 114 return 0, errors.Wrapf(err, "unable to convert random port to int") 115 } 116 return rp, nil 117 } 118 119 //CreatePortBinding takes port (int) and IP (string) and creates an array of portbinding structs 120 func CreatePortBinding(hostPort int, hostIP string) []nat.PortBinding { 121 pb := nat.PortBinding{ 122 HostPort: strconv.Itoa(hostPort), 123 } 124 pb.HostIP = hostIP 125 return []nat.PortBinding{pb} 126 }