// WildcardMapper maps k8s wildcard ports (hostPort == 0) to any available offer port func WildcardMapper(pod *api.Pod, roles []string, offer *mesos.Offer) ([]Mapping, error) { mapping, err := FixedMapper(pod, roles, offer) if err != nil { return nil, err } taken := make(map[uint64]struct{}) for _, entry := range mapping { taken[entry.OfferPort] = struct{}{} } wildports := []Mapping{} for i, container := range pod.Spec.Containers { for pi, port := range container.Ports { if port.HostPort == 0 { wildports = append(wildports, Mapping{ ContainerIdx: i, PortIdx: pi, }) } } } remaining := len(wildports) resources.ForeachPortsRange(offer.GetResources(), roles, func(bp, ep uint64, role string) { log.V(3).Infof("Searching for wildcard port in range {%d:%d}", bp, ep) for i := range wildports { if wildports[i].OfferPort != 0 { continue } for port := bp; port <= ep && remaining > 0; port++ { if _, inuse := taken[port]; inuse { continue } wildports[i].OfferPort = port wildports[i].Role = resources.CanonicalRole(role) mapping = append(mapping, wildports[i]) remaining-- taken[port] = struct{}{} break } } }) if remaining > 0 { err := &PortAllocationError{ PodID: pod.Namespace + "/" + pod.Name, } // it doesn't make sense to include a port list here because they were all zero (wildcards) return nil, err } return mapping, nil }
// FixedMapper maps k8s host ports to offered ports ignoring hostPorts == 0 (remaining pod-private) func FixedMapper(pod *api.Pod, roles []string, offer *mesos.Offer) ([]Mapping, error) { requiredPorts := make(map[uint64]Mapping) mapping := []Mapping{} for i, container := range pod.Spec.Containers { // strip all port==0 from this array; k8s already knows what to do with zero- // ports (it does not create 'port bindings' on the minion-host); we need to // remove the wildcards from this array since they don't consume host resources for pi, port := range container.Ports { if port.HostPort == 0 { continue // ignore } m := Mapping{ ContainerIdx: i, PortIdx: pi, OfferPort: uint64(port.HostPort), } if entry, inuse := requiredPorts[uint64(port.HostPort)]; inuse { return nil, &DuplicateError{entry, m} } requiredPorts[uint64(port.HostPort)] = m } } resources.ForeachPortsRange(offer.GetResources(), roles, func(bp, ep uint64, role string) { for port := range requiredPorts { log.V(3).Infof("evaluating port range {%d:%d} %d", bp, ep, port) if (bp <= port) && (port <= ep) { m := requiredPorts[port] m.Role = resources.CanonicalRole(role) mapping = append(mapping, m) delete(requiredPorts, port) } } }) unsatisfiedPorts := len(requiredPorts) if unsatisfiedPorts > 0 { err := &PortAllocationError{ PodID: pod.Namespace + "/" + pod.Name, } for p := range requiredPorts { err.Ports = append(err.Ports, p) } return nil, err } return mapping, nil }