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