mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-08-31 01:35:56 +08:00
feat(region,host,climc): support pod host random port mapping
This commit is contained in:
+23
-9
@@ -22,8 +22,9 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
POD_METADATA_CRI_ID = "cri_id"
|
||||
POD_METADATA_CRI_CONFIG = "cri_config"
|
||||
POD_METADATA_CRI_ID = "cri_id"
|
||||
POD_METADATA_CRI_CONFIG = "cri_config"
|
||||
POD_METADATA_PORT_MAPPINGS = "port_mappings"
|
||||
)
|
||||
|
||||
type PodContainerCreateInput struct {
|
||||
@@ -35,16 +36,22 @@ type PodContainerCreateInput struct {
|
||||
type PodPortMappingProtocol string
|
||||
|
||||
const (
|
||||
PodPortMappingProtocolTCP = "tcp"
|
||||
PodPortMappingProtocolUDP = "udp"
|
||||
PodPortMappingProtocolSCTP = "sctp"
|
||||
PodPortMappingProtocolTCP = "tcp"
|
||||
PodPortMappingProtocolUDP = "udp"
|
||||
//PodPortMappingProtocolSCTP = "sctp"
|
||||
)
|
||||
|
||||
type PodPortMappingPortRange struct {
|
||||
Start int `json:"start"`
|
||||
End int `json:"end"`
|
||||
}
|
||||
|
||||
type PodPortMapping struct {
|
||||
Protocol PodPortMappingProtocol `json:"protocol"`
|
||||
ContainerPort int32 `json:"container_port"`
|
||||
HostPort int32 `json:"host_port"`
|
||||
HostIp string `json:"host_ip"`
|
||||
Protocol PodPortMappingProtocol `json:"protocol"`
|
||||
ContainerPort int `json:"container_port"`
|
||||
HostPort *int `json:"host_port,omitempty"`
|
||||
HostIp string `json:"host_ip"`
|
||||
HostPortRange *PodPortMappingPortRange `json:"host_port_range,omitempty"`
|
||||
}
|
||||
|
||||
type PodCreateInput struct {
|
||||
@@ -56,3 +63,10 @@ type PodStartResponse struct {
|
||||
CRIId string `json:"cri_id"`
|
||||
IsRunning bool `json:"is_running"`
|
||||
}
|
||||
|
||||
type PodMetadataPortMapping struct {
|
||||
Protocol PodPortMappingProtocol `json:"protocol"`
|
||||
ContainerPort int32 `json:"container_port"`
|
||||
HostPort int32 `json:"host_port,omitempty"`
|
||||
HostIp string `json:"host_ip"`
|
||||
}
|
||||
|
||||
@@ -89,8 +89,19 @@ func (p *SPodDriver) ValidateCreateData(ctx context.Context, userCred mcclient.T
|
||||
}
|
||||
|
||||
func (p *SPodDriver) validatePortMappings(input *api.PodCreateInput) error {
|
||||
usedPorts := make(map[api.PodPortMappingProtocol]sets.Int)
|
||||
for idx, pm := range input.PortMappings {
|
||||
// TODO: 判断 host port 是否重复
|
||||
ports, ok := usedPorts[pm.Protocol]
|
||||
if !ok {
|
||||
ports = sets.NewInt()
|
||||
}
|
||||
if pm.HostPort != nil {
|
||||
if ports.Has(*pm.HostPort) {
|
||||
return httperrors.NewInputParameterError("%s host_port %d is already specified", pm.Protocol, *pm.HostPort)
|
||||
}
|
||||
ports.Insert(*pm.HostPort)
|
||||
}
|
||||
usedPorts[pm.Protocol] = ports
|
||||
if err := p.validatePortMapping(pm); err != nil {
|
||||
return errors.Wrapf(err, "validate portmapping %d", idx)
|
||||
}
|
||||
@@ -98,6 +109,11 @@ func (p *SPodDriver) validatePortMappings(input *api.PodCreateInput) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *SPodDriver) validateHostPortMapping(hostId string, pm *api.PodPortMapping) error {
|
||||
// TODO:
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *SPodDriver) validateContainerData(ctx context.Context, userCred mcclient.TokenCredential, idx int, defaultNamePrefix string, ctr *api.PodContainerCreateInput, input *api.ServerCreateInput) error {
|
||||
if ctr.Name == "" {
|
||||
ctr.Name = fmt.Sprintf("%s-%d", defaultNamePrefix, idx)
|
||||
@@ -137,7 +153,22 @@ func (p *SPodDriver) validateContainerVolumeMount(ctx context.Context, userCred
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *SPodDriver) validatePortRange(port int32) error {
|
||||
func (p *SPodDriver) validatePortRange(portRange *api.PodPortMappingPortRange) error {
|
||||
if portRange != nil {
|
||||
if portRange.Start > portRange.End {
|
||||
return httperrors.NewInputParameterError("port range start %d is large than %d", portRange.Start, portRange.End)
|
||||
}
|
||||
if portRange.Start <= 0 {
|
||||
return httperrors.NewInputParameterError("port range start %d <= 0", portRange.Start)
|
||||
}
|
||||
if portRange.End > 65535 {
|
||||
return httperrors.NewInputParameterError("port range end %d > 65535", portRange.End)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *SPodDriver) validatePort(port int) error {
|
||||
if port <= 0 || port > 65535 {
|
||||
return httperrors.NewInputParameterError("port number %d isn't within 1 to 65535", port)
|
||||
}
|
||||
@@ -145,16 +176,21 @@ func (p *SPodDriver) validatePortRange(port int32) error {
|
||||
}
|
||||
|
||||
func (p *SPodDriver) validatePortMapping(pm *api.PodPortMapping) error {
|
||||
if err := p.validatePortRange(pm.HostPort); err != nil {
|
||||
return errors.Wrap(err, "validate host_port")
|
||||
if err := p.validatePortRange(pm.HostPortRange); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.validatePortRange(pm.ContainerPort); err != nil {
|
||||
if pm.HostPort != nil {
|
||||
if err := p.validatePort(*pm.HostPort); err != nil {
|
||||
return errors.Wrap(err, "validate host_port")
|
||||
}
|
||||
}
|
||||
if err := p.validatePort(pm.ContainerPort); err != nil {
|
||||
return errors.Wrap(err, "validate container_port")
|
||||
}
|
||||
if pm.Protocol == "" {
|
||||
pm.Protocol = api.PodPortMappingProtocolTCP
|
||||
}
|
||||
if !sets.NewString(api.PodPortMappingProtocolSCTP, api.PodPortMappingProtocolUDP, api.PodPortMappingProtocolTCP).Has(string(pm.Protocol)) {
|
||||
if !sets.NewString(api.PodPortMappingProtocolUDP, api.PodPortMappingProtocolTCP).Has(string(pm.Protocol)) {
|
||||
return httperrors.NewInputParameterError("unsupported protocol %s", pm.Protocol)
|
||||
}
|
||||
return nil
|
||||
|
||||
+169
-21
@@ -1,3 +1,17 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package guestman
|
||||
|
||||
import (
|
||||
@@ -13,6 +27,7 @@ import (
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/sets"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
computeapi "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
@@ -30,6 +45,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
computemod "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
|
||||
"yunion.io/x/onecloud/pkg/util/fileutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/netutils2/getport"
|
||||
"yunion.io/x/onecloud/pkg/util/pod"
|
||||
)
|
||||
|
||||
@@ -243,6 +259,111 @@ func (s *sPodGuestInstance) getPodPrivilegedMode(input *computeapi.PodCreateInpu
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *sPodGuestInstance) getPortMappings(input []*computeapi.PodPortMapping) ([]*runtimeapi.PortMapping, error) {
|
||||
result := make([]*runtimeapi.PortMapping, len(input))
|
||||
for idx := range input {
|
||||
pm, err := s.getPortMapping(input[idx])
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "get port mapping %s", jsonutils.Marshal(input[idx]))
|
||||
}
|
||||
result[idx] = pm
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *sPodGuestInstance) getOtherPods() []*sPodGuestInstance {
|
||||
man := s.manager
|
||||
otherPods := make([]*sPodGuestInstance, 0)
|
||||
man.Servers.Range(func(id, value any) bool {
|
||||
if id == s.Id {
|
||||
return true
|
||||
}
|
||||
ins := value.(GuestRuntimeInstance)
|
||||
pod, ok := ins.(*sPodGuestInstance)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
otherPods = append(otherPods, pod)
|
||||
return true
|
||||
})
|
||||
return otherPods
|
||||
}
|
||||
|
||||
func (s *sPodGuestInstance) getOtherPodsUsedPorts() (map[computeapi.PodPortMappingProtocol]sets.Int, error) {
|
||||
otherPods := s.getOtherPods()
|
||||
ret := make(map[computeapi.PodPortMappingProtocol]sets.Int)
|
||||
for _, pod := range otherPods {
|
||||
pms, err := pod.GetPodMetadataPortMappings()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "get pod %s port_mappins", pod.GetId())
|
||||
}
|
||||
for _, pm := range pms {
|
||||
ps, ok := ret[pm.Protocol]
|
||||
if !ok {
|
||||
ps = sets.NewInt()
|
||||
}
|
||||
ps.Insert(int(pm.HostPort))
|
||||
ret[pm.Protocol] = ps
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (s *sPodGuestInstance) getPortMapping(pm *computeapi.PodPortMapping) (*runtimeapi.PortMapping, error) {
|
||||
runtimePm := &runtimeapi.PortMapping{
|
||||
ContainerPort: int32(pm.ContainerPort),
|
||||
HostIp: pm.HostIp,
|
||||
}
|
||||
portProtocol := getport.TCP
|
||||
switch pm.Protocol {
|
||||
case computeapi.PodPortMappingProtocolTCP:
|
||||
runtimePm.Protocol = runtimeapi.Protocol_TCP
|
||||
portProtocol = getport.TCP
|
||||
case computeapi.PodPortMappingProtocolUDP:
|
||||
runtimePm.Protocol = runtimeapi.Protocol_UDP
|
||||
portProtocol = getport.UDP
|
||||
//case computeapi.PodPortMappingProtocolSCTP:
|
||||
// runtimePm.Protocol = runtimeapi.Protocol_SCTP
|
||||
default:
|
||||
return nil, errors.Errorf("invalid protocol: %q", pm.Protocol)
|
||||
}
|
||||
// listen random port
|
||||
otherPorts, err := s.getOtherPodsUsedPorts()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "getOtherPodsUsedPorts")
|
||||
}
|
||||
if pm.HostPort != nil {
|
||||
runtimePm.HostPort = int32(*pm.HostPort)
|
||||
if getport.IsPortUsed(portProtocol, "", *pm.HostPort) {
|
||||
return nil, httperrors.NewInputParameterError("host_port %d is used", pm.HostPort)
|
||||
}
|
||||
usedPorts, ok := otherPorts[pm.Protocol]
|
||||
if ok {
|
||||
if usedPorts.Has(*pm.HostPort) {
|
||||
return nil, errors.Wrapf(err, "%s host_port %d is already used", pm.Protocol, *pm.HostPort)
|
||||
}
|
||||
}
|
||||
return runtimePm, nil
|
||||
} else {
|
||||
start := 20000
|
||||
end := 25000
|
||||
if pm.HostPortRange != nil {
|
||||
start = pm.HostPortRange.Start
|
||||
end = pm.HostPortRange.End
|
||||
}
|
||||
otherPodPorts, ok := otherPorts[pm.Protocol]
|
||||
if !ok {
|
||||
otherPodPorts = sets.NewInt()
|
||||
}
|
||||
portResult, err := getport.GetPortByRangeBySets(portProtocol, start, end, otherPodPorts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "listen %s port inside %d and %d", pm.Protocol, start, end)
|
||||
}
|
||||
runtimePm.HostPort = int32(portResult.Port)
|
||||
return runtimePm, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sPodGuestInstance) startPod(ctx context.Context, userCred mcclient.TokenCredential) (*computeapi.PodStartResponse, error) {
|
||||
podInput, err := s.getPodCreateParams()
|
||||
if err != nil {
|
||||
@@ -284,26 +405,11 @@ func (s *sPodGuestInstance) startPod(ctx context.Context, userCred mcclient.Toke
|
||||
}
|
||||
|
||||
if len(podInput.PortMappings) != 0 {
|
||||
podCfg.PortMappings = make([]*runtimeapi.PortMapping, len(podInput.PortMappings))
|
||||
for idx := range podInput.PortMappings {
|
||||
pm := podInput.PortMappings[idx]
|
||||
runtimePm := &runtimeapi.PortMapping{
|
||||
ContainerPort: pm.ContainerPort,
|
||||
HostPort: pm.HostPort,
|
||||
HostIp: pm.HostIp,
|
||||
}
|
||||
switch pm.Protocol {
|
||||
case computeapi.PodPortMappingProtocolTCP:
|
||||
runtimePm.Protocol = runtimeapi.Protocol_TCP
|
||||
case computeapi.PodPortMappingProtocolUDP:
|
||||
runtimePm.Protocol = runtimeapi.Protocol_UDP
|
||||
case computeapi.PodPortMappingProtocolSCTP:
|
||||
runtimePm.Protocol = runtimeapi.Protocol_SCTP
|
||||
default:
|
||||
return nil, errors.Errorf("invalid protocol: %q", pm.Protocol)
|
||||
}
|
||||
podCfg.PortMappings[idx] = runtimePm
|
||||
pms, err := s.getPortMappings(podInput.PortMappings)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get port mappings")
|
||||
}
|
||||
podCfg.PortMappings = pms
|
||||
}
|
||||
|
||||
criId, err := s.getCRI().RunPod(ctx, podCfg, "")
|
||||
@@ -435,15 +541,41 @@ func (s *sPodGuestInstance) getCRIId() string {
|
||||
return s.GetSourceDesc().Metadata[computeapi.POD_METADATA_CRI_ID]
|
||||
}
|
||||
|
||||
func (s *sPodGuestInstance) convertToPodMetadataPortMappings(cfg *runtimeapi.PodSandboxConfig) []*computeapi.PodMetadataPortMapping {
|
||||
if cfg.PortMappings == nil {
|
||||
return []*computeapi.PodMetadataPortMapping{}
|
||||
}
|
||||
ret := make([]*computeapi.PodMetadataPortMapping, len(cfg.PortMappings))
|
||||
for idx := range cfg.PortMappings {
|
||||
pm := cfg.PortMappings[idx]
|
||||
var proto computeapi.PodPortMappingProtocol = computeapi.PodPortMappingProtocolTCP
|
||||
if pm.Protocol == runtimeapi.Protocol_UDP {
|
||||
proto = computeapi.PodPortMappingProtocolUDP
|
||||
}
|
||||
ret[idx] = &computeapi.PodMetadataPortMapping{
|
||||
Protocol: proto,
|
||||
ContainerPort: pm.ContainerPort,
|
||||
HostPort: pm.HostPort,
|
||||
HostIp: pm.HostIp,
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *sPodGuestInstance) setCRIInfo(ctx context.Context, userCred mcclient.TokenCredential, criId string, cfg *runtimeapi.PodSandboxConfig) error {
|
||||
s.Desc.Metadata[computeapi.POD_METADATA_CRI_ID] = criId
|
||||
cfgStr := jsonutils.Marshal(cfg).String()
|
||||
s.Desc.Metadata[computeapi.POD_METADATA_CRI_CONFIG] = cfgStr
|
||||
|
||||
pms := s.convertToPodMetadataPortMappings(cfg)
|
||||
pmStr := jsonutils.Marshal(pms).String()
|
||||
s.Desc.Metadata[computeapi.POD_METADATA_PORT_MAPPINGS] = pmStr
|
||||
|
||||
session := auth.GetSession(ctx, userCred, options.HostOptions.Region)
|
||||
if _, err := computemod.Servers.SetMetadata(session, s.GetId(), jsonutils.Marshal(map[string]string{
|
||||
computeapi.POD_METADATA_CRI_ID: criId,
|
||||
computeapi.POD_METADATA_CRI_CONFIG: cfgStr,
|
||||
computeapi.POD_METADATA_CRI_ID: criId,
|
||||
computeapi.POD_METADATA_CRI_CONFIG: cfgStr,
|
||||
computeapi.POD_METADATA_PORT_MAPPINGS: pmStr,
|
||||
})); err != nil {
|
||||
return errors.Wrapf(err, "set cri_id of pod %s", s.GetId())
|
||||
}
|
||||
@@ -473,6 +605,22 @@ func (s *sPodGuestInstance) getPodSandboxConfig() (*runtimeapi.PodSandboxConfig,
|
||||
return podCfg, nil
|
||||
}
|
||||
|
||||
func (s *sPodGuestInstance) GetPodMetadataPortMappings() ([]*computeapi.PodMetadataPortMapping, error) {
|
||||
cfgStr := s.GetSourceDesc().Metadata[computeapi.POD_METADATA_PORT_MAPPINGS]
|
||||
if cfgStr == "" {
|
||||
return nil, nil
|
||||
}
|
||||
obj, err := jsonutils.ParseString(cfgStr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "ParseString to json object: %s", cfgStr)
|
||||
}
|
||||
pms := make([]*computeapi.PodMetadataPortMapping, 0)
|
||||
if err := obj.Unmarshal(pms); err != nil {
|
||||
return nil, errors.Wrap(err, "Unmarshal to PodMetadataPortMappings")
|
||||
}
|
||||
return pms, nil
|
||||
}
|
||||
|
||||
func (s *sPodGuestInstance) saveContainer(id string, criId string) error {
|
||||
_, ok := s.containers[id]
|
||||
if ok {
|
||||
|
||||
@@ -34,50 +34,127 @@ type PodCreateOptions struct {
|
||||
MEM string `help:"Memory size MB" metavar:"MEM" json:"-"`
|
||||
VcpuCount int `help:"#CPU cores of VM server, default 1" default:"1" metavar:"<SERVER_CPU_COUNT>" json:"vcpu_count" token:"ncpu"`
|
||||
AllowDelete *bool `help:"Unlock server to allow deleting" json:"-"`
|
||||
PortMapping []string `help:"Port mapping of the pod and the format is: <host_port>:<container_port>/<tcp|udp>" short-token:"p"`
|
||||
PortMapping []string `help:"Port mapping of the pod and the format is: host_port=8080,port=80,protocol=<tcp|udp>,host_port_range=<int>-<int>" short-token:"p"`
|
||||
Arch string `help:"image arch" choices:"aarch64|x86_64"`
|
||||
|
||||
ContainerCreateCommonOptions
|
||||
}
|
||||
|
||||
func parsePodPortMapping(input string) (*computeapi.PodPortMapping, error) {
|
||||
segs := strings.Split(input, ":")
|
||||
if len(segs) != 2 {
|
||||
return nil, errors.Errorf("wrong format: %s", input)
|
||||
func parsePodPortMappingDetails(input string) (*computeapi.PodPortMapping, error) {
|
||||
pm := &computeapi.PodPortMapping{
|
||||
Protocol: computeapi.PodPortMappingProtocolTCP,
|
||||
}
|
||||
hostPortStr := segs[0]
|
||||
hostPort, err := strconv.Atoi(hostPortStr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "host_port %s isn't integer", hostPortStr)
|
||||
}
|
||||
ctrPortPart := segs[1]
|
||||
ctrPortSegs := strings.Split(ctrPortPart, "/")
|
||||
if len(ctrPortSegs) > 2 {
|
||||
return nil, errors.Wrapf(err, "wrong format: %s", ctrPortPart)
|
||||
}
|
||||
ctrPortStr := ctrPortSegs[0]
|
||||
ctrPort, err := strconv.Atoi(ctrPortStr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "container_port %s isn't integer", ctrPortStr)
|
||||
}
|
||||
var protocol computeapi.PodPortMappingProtocol = computeapi.PodPortMappingProtocolTCP
|
||||
if len(ctrPortSegs) == 2 {
|
||||
switch ctrPortSegs[1] {
|
||||
case "tcp":
|
||||
protocol = computeapi.PodPortMappingProtocolTCP
|
||||
case "udp":
|
||||
protocol = computeapi.PodPortMappingProtocolUDP
|
||||
case "sctp":
|
||||
protocol = computeapi.PodPortMappingProtocolSCTP
|
||||
default:
|
||||
return nil, errors.Wrapf(err, "wrong protocol: %s", ctrPortSegs[1])
|
||||
for _, seg := range strings.Split(input, ",") {
|
||||
info := strings.Split(seg, "=")
|
||||
if len(info) != 2 {
|
||||
return nil, errors.Errorf("invalid option %s", seg)
|
||||
}
|
||||
key := info[0]
|
||||
val := info[1]
|
||||
switch key {
|
||||
case "host_port":
|
||||
hp, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid host_port %s", val)
|
||||
}
|
||||
pm.HostPort = &hp
|
||||
case "container_port", "port":
|
||||
cp, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid container_port %s", val)
|
||||
}
|
||||
pm.ContainerPort = cp
|
||||
case "proto", "protocol":
|
||||
pm.Protocol = computeapi.PodPortMappingProtocol(val)
|
||||
case "host_port_range":
|
||||
rangeParts := strings.Split(val, "-")
|
||||
if len(rangeParts) != 2 {
|
||||
return nil, errors.Errorf("invalid range string %s", val)
|
||||
}
|
||||
start, err := strconv.Atoi(rangeParts[0])
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid host_port_range %s", rangeParts[0])
|
||||
}
|
||||
end, err := strconv.Atoi(rangeParts[1])
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid host_port_range %s", rangeParts[1])
|
||||
}
|
||||
pm.HostPortRange = &computeapi.PodPortMappingPortRange{
|
||||
Start: start,
|
||||
End: end,
|
||||
}
|
||||
}
|
||||
}
|
||||
return &computeapi.PodPortMapping{
|
||||
Protocol: protocol,
|
||||
ContainerPort: int32(ctrPort),
|
||||
HostPort: int32(hostPort),
|
||||
}, nil
|
||||
if pm.ContainerPort == 0 {
|
||||
return nil, errors.Error("container_port must specified")
|
||||
}
|
||||
return pm, nil
|
||||
}
|
||||
|
||||
func ParsePodPortMapping(input string) (*computeapi.PodPortMapping, error) {
|
||||
pm, err := parsePodPortMapping(input)
|
||||
if err != nil {
|
||||
return parsePodPortMappingDetails(input)
|
||||
}
|
||||
return pm, nil
|
||||
}
|
||||
|
||||
func parsePodPortMapping(input string) (*computeapi.PodPortMapping, error) {
|
||||
segs := strings.Split(input, ":")
|
||||
parseCtrPart := func(ctrPortPart string) (computeapi.PodPortMappingProtocol, int, error) {
|
||||
ctrPortSegs := strings.Split(ctrPortPart, "/")
|
||||
if len(ctrPortSegs) > 2 {
|
||||
return "", 0, errors.Errorf("wrong format: %s", ctrPortPart)
|
||||
}
|
||||
ctrPortStr := ctrPortSegs[0]
|
||||
ctrPort, err := strconv.Atoi(ctrPortStr)
|
||||
if err != nil {
|
||||
return "", 0, errors.Wrapf(err, "container_port %s isn't integer", ctrPortStr)
|
||||
}
|
||||
var protocol computeapi.PodPortMappingProtocol = computeapi.PodPortMappingProtocolTCP
|
||||
if len(ctrPortSegs) == 2 {
|
||||
switch ctrPortSegs[1] {
|
||||
case "tcp":
|
||||
protocol = computeapi.PodPortMappingProtocolTCP
|
||||
case "udp":
|
||||
protocol = computeapi.PodPortMappingProtocolUDP
|
||||
//case "sctp":
|
||||
// protocol = computeapi.PodPortMappingProtocolSCTP
|
||||
default:
|
||||
return "", 0, errors.Wrapf(err, "wrong protocol: %s", ctrPortSegs[1])
|
||||
}
|
||||
}
|
||||
return protocol, ctrPort, nil
|
||||
}
|
||||
|
||||
if len(segs) == 1 {
|
||||
protocol, ctrPort, err := parseCtrPart(segs[0])
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "parse %s", segs[0])
|
||||
}
|
||||
return &computeapi.PodPortMapping{
|
||||
Protocol: protocol,
|
||||
ContainerPort: ctrPort,
|
||||
}, nil
|
||||
} else if len(segs) == 2 {
|
||||
hostPortStr := segs[0]
|
||||
hostPort, err := strconv.Atoi(hostPortStr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "host_port %s isn't integer", hostPortStr)
|
||||
}
|
||||
ctrPortPart := segs[1]
|
||||
protocol, ctrPort, err := parseCtrPart(ctrPortPart)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "parse %s", ctrPortPart)
|
||||
}
|
||||
return &computeapi.PodPortMapping{
|
||||
Protocol: protocol,
|
||||
ContainerPort: ctrPort,
|
||||
HostPort: &hostPort,
|
||||
}, nil
|
||||
} else {
|
||||
return nil, errors.Errorf("wrong format: %s", input)
|
||||
}
|
||||
}
|
||||
|
||||
func parseContainerDevice(dev string) (*computeapi.ContainerDevice, error) {
|
||||
@@ -105,7 +182,7 @@ func (o *PodCreateOptions) Params() (*computeapi.ServerCreateInput, error) {
|
||||
portMappings := make([]*computeapi.PodPortMapping, 0)
|
||||
if len(o.PortMapping) != 0 {
|
||||
for _, input := range o.PortMapping {
|
||||
pm, err := parsePodPortMapping(input)
|
||||
pm, err := ParsePodPortMapping(input)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "parse port mapping: %s", input)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
)
|
||||
|
||||
func Test_parsePodPortMapping(t *testing.T) {
|
||||
var port80 int = 80
|
||||
tests := []struct {
|
||||
args string
|
||||
want *computeapi.PodPortMapping
|
||||
@@ -18,7 +19,7 @@ func Test_parsePodPortMapping(t *testing.T) {
|
||||
want: &computeapi.PodPortMapping{
|
||||
Protocol: computeapi.PodPortMappingProtocolTCP,
|
||||
ContainerPort: 8080,
|
||||
HostPort: 80,
|
||||
HostPort: &port80,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
@@ -27,7 +28,7 @@ func Test_parsePodPortMapping(t *testing.T) {
|
||||
want: &computeapi.PodPortMapping{
|
||||
Protocol: computeapi.PodPortMappingProtocolTCP,
|
||||
ContainerPort: 8080,
|
||||
HostPort: 80,
|
||||
HostPort: &port80,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
@@ -37,9 +38,11 @@ func Test_parsePodPortMapping(t *testing.T) {
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
args: "80",
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
args: "80",
|
||||
want: &computeapi.PodPortMapping{
|
||||
Protocol: computeapi.PodPortMappingProtocolTCP,
|
||||
ContainerPort: 80,
|
||||
},
|
||||
},
|
||||
{
|
||||
args: "80s:ctrP",
|
||||
@@ -60,3 +63,56 @@ func Test_parsePodPortMapping(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_parsePodPortMappingDetails(t *testing.T) {
|
||||
port8080 := 8080
|
||||
tests := []struct {
|
||||
input string
|
||||
want *computeapi.PodPortMapping
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
input: "host_port=8080,port=80",
|
||||
want: &computeapi.PodPortMapping{
|
||||
Protocol: computeapi.PodPortMappingProtocolTCP,
|
||||
ContainerPort: 80,
|
||||
HostPort: &port8080,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "host_port=8080,port=80,protocol=udp",
|
||||
want: &computeapi.PodPortMapping{
|
||||
Protocol: computeapi.PodPortMappingProtocolUDP,
|
||||
ContainerPort: 80,
|
||||
HostPort: &port8080,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "host_port=8080,protocol=udp",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
input: "container_port=80,protocol=udp,host_port_range=20000-25000",
|
||||
want: &computeapi.PodPortMapping{
|
||||
Protocol: computeapi.PodPortMappingProtocolUDP,
|
||||
ContainerPort: 80,
|
||||
HostPortRange: &computeapi.PodPortMappingPortRange{
|
||||
Start: 20000,
|
||||
End: 25000,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.input, func(t *testing.T) {
|
||||
got, err := parsePodPortMappingDetails(tt.input)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("parsePodPortMappingDetails() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("parsePodPortMappingDetails() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
package getport // import "yunion.io/x/onecloud/pkg/util/netutils2/getport"
|
||||
@@ -0,0 +1,263 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package getport
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/sets"
|
||||
)
|
||||
|
||||
// REF: https://github.com/jsumners/go-getport/blob/master/getport.go
|
||||
|
||||
// Protocol indicates the communication protocol (tcp or udp) and network
|
||||
// stack (IPv4, IPv6, or OS choice) to target when finding an available port.
|
||||
type Protocol int
|
||||
|
||||
const (
|
||||
// TCP indicates to let the OS decide between IPv4 and IPv6 when finding
|
||||
// an open TCP based port.
|
||||
TCP Protocol = iota
|
||||
|
||||
// TCP4 indicates to find an open IPv4 port.
|
||||
TCP4
|
||||
|
||||
// TCP6 indicates to find an open IPv6 port.
|
||||
TCP6
|
||||
|
||||
// UDP indicates to let the OS decide between IPv4 and IPv6 when finding
|
||||
// an open UDP based port.
|
||||
UDP
|
||||
|
||||
// UDP4 indicates to find an open IPv4 port.
|
||||
UDP4
|
||||
|
||||
// UDP6 indicates to find an open IPv6 port.
|
||||
UDP6
|
||||
)
|
||||
|
||||
// PortResult represents the result of [GetPort]. It indicates the IP address
|
||||
// and port number combination that resulted in finding an open port.
|
||||
type PortResult struct {
|
||||
// IP is either an IPv4 or IPv6 string as returned by [net.SplitHostPort].
|
||||
IP string
|
||||
|
||||
// Port is the determined available port number.
|
||||
Port int
|
||||
}
|
||||
|
||||
// GetPort finds an open port for a given [Protocol] and address and returns
|
||||
// that port number. If the [Protocol] is not recognized, or some problem is
|
||||
// encountered while verifying the port, then the returned [PortResult.Port]
|
||||
// number will be `-1` along with an error. The address parameter should be a
|
||||
// simple IP address string, e.g. `127.0.0.1` or `::1`. The [PortResult.IP] will
|
||||
// be set to the IP address that was actually used to find the open port. If
|
||||
// address is the empty string (`""`), then the returned IP address will be the
|
||||
// one determined by the OS when finding the port.
|
||||
//
|
||||
// Note: it is not guaranteed the port will remain open long enough to actually
|
||||
// be used. Errors should still be checked when attempting to use the found
|
||||
// port.
|
||||
func GetPort(protocol Protocol, address string) (PortResult, error) {
|
||||
return getPort(protocol, address, 0)
|
||||
}
|
||||
|
||||
func getPort(protocol Protocol, address string, port int) (PortResult, error) {
|
||||
stack := resolveProtocol(protocol)
|
||||
|
||||
result := PortResult{
|
||||
IP: "",
|
||||
Port: -1,
|
||||
}
|
||||
resolvedAddress, listenError := listen(
|
||||
stack,
|
||||
net.JoinHostPort(address, fmt.Sprintf("%d", port)),
|
||||
)
|
||||
if listenError != nil {
|
||||
return result, listenError
|
||||
}
|
||||
|
||||
// I do not see how it's possible to get an error from [net.SplitHostPort]
|
||||
// here given how we have already validated the stack and successfully
|
||||
// issued a [net.Listen].
|
||||
addr, portStr, _ := net.SplitHostPort(resolvedAddress.String())
|
||||
hPort, _ := strconv.Atoi(portStr)
|
||||
result.IP = addr
|
||||
result.Port = hPort
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func IsPortUsed(protocol Protocol, address string, port int) bool {
|
||||
_, err := getPort(protocol, address, port)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func GetPortByRange(proto Protocol, start int, end int) (PortResult, error) {
|
||||
return GetPortByRangeBySets(proto, start, end, sets.NewInt())
|
||||
}
|
||||
|
||||
func GetPortByRangeBySets(proto Protocol, start int, end int, usedPorts sets.Int) (PortResult, error) {
|
||||
for i := start; i <= end; i++ {
|
||||
rPort := rand.Intn(end-start) + start
|
||||
if usedPorts.Has(rPort) {
|
||||
continue
|
||||
}
|
||||
result, err := getPort(proto, "", rPort)
|
||||
if err != nil {
|
||||
usedPorts.Insert(rPort)
|
||||
log.Debugf("check random port %d: %v", rPort, err)
|
||||
} else {
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
return PortResult{
|
||||
IP: "",
|
||||
Port: -1,
|
||||
}, errors.Errorf("can't get free port in [%d, %d]", start, end)
|
||||
}
|
||||
|
||||
// GetTcpPort gets a port for some random available address using either
|
||||
// TCP4 or TCP6. See [GetPort] for more detail
|
||||
func GetTcpPort() (PortResult, error) {
|
||||
return GetPort(TCP, "")
|
||||
}
|
||||
|
||||
// GetTcp4Port gets a port for some random available address using TCP4.
|
||||
// See [GetPort] for more detail
|
||||
func GetTcp4Port() (PortResult, error) {
|
||||
return GetPort(TCP4, "")
|
||||
}
|
||||
|
||||
// GetTcp6Port gets a port for some random available address using TCP6.
|
||||
// See [GetPort] for more detail
|
||||
func GetTcp6Port() (PortResult, error) {
|
||||
return GetPort(TCP6, "")
|
||||
}
|
||||
|
||||
// GetUdpPort gets a port for some random available address using either
|
||||
// UDP4 or UDP6. See [GetPort] for more detail
|
||||
func GetUdpPort() (PortResult, error) {
|
||||
return GetPort(UDP, "")
|
||||
}
|
||||
|
||||
// GetUdp4Port gets a port for some random available address using UDP4.
|
||||
// See [GetPort] for more detail
|
||||
func GetUdp4Port() (PortResult, error) {
|
||||
return GetPort(UDP4, "")
|
||||
}
|
||||
|
||||
// GetUdp6Port gets a port for some random available address using UDP6.
|
||||
// See [GetPort] for more detail
|
||||
func GetUdp6Port() (PortResult, error) {
|
||||
return GetPort(UDP6, "")
|
||||
}
|
||||
|
||||
// GetTcpPortForAddress gets either a TCP4 or TCP6 port for the given address.
|
||||
// See [GetPort] for more detail.
|
||||
func GetTcpPortForAddress(address string) (PortResult, error) {
|
||||
return GetPort(TCP, address)
|
||||
}
|
||||
|
||||
// GetTcp4PortForAddress gets a TCP4 port for the given address.
|
||||
// See [GetPort] for more detail.
|
||||
func GetTcp4PortForAddress(address string) (PortResult, error) {
|
||||
return GetPort(TCP4, address)
|
||||
}
|
||||
|
||||
// GetTcp6PortForAddress gets a TCP6 port for the given address.
|
||||
// See [GetPort] for more detail.
|
||||
func GetTcp6PortForAddress(address string) (PortResult, error) {
|
||||
return GetPort(TCP6, address)
|
||||
}
|
||||
|
||||
// GetUdpPortForAddress gets either a UDP4 or UDP6 port for the given address.
|
||||
// See [GetPort] for more detail.
|
||||
func GetUdpPortForAddress(address string) (PortResult, error) {
|
||||
return GetPort(UDP, address)
|
||||
}
|
||||
|
||||
// GetUdp4PortForAddress gets a UDP4 port for the given address.
|
||||
// See [GetPort] for more detail.
|
||||
func GetUdp4PortForAddress(address string) (PortResult, error) {
|
||||
return GetPort(UDP4, address)
|
||||
}
|
||||
|
||||
// GetUdp6PortForAddress gets a UDP6 port for the given address.
|
||||
// See [GetPort] for more detail.
|
||||
func GetUdp6PortForAddress(address string) (PortResult, error) {
|
||||
return GetPort(UDP6, address)
|
||||
}
|
||||
|
||||
// PortResultToAddress converts a [PortResult] into a traditional host:port
|
||||
// string usable by [net.Listen] or [net.ListenPacket].
|
||||
func PortResultToAddress(portResult PortResult) string {
|
||||
return net.JoinHostPort(portResult.IP, fmt.Sprintf("%d", portResult.Port))
|
||||
}
|
||||
|
||||
// listen is an internal wrapper for [net.Listen] and [net.ListenPacket].
|
||||
func listen(stack string, addrWithPort string) (net.Addr, error) {
|
||||
if strings.HasPrefix(stack, "tcp") {
|
||||
l, err := net.Listen(stack, addrWithPort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer l.Close()
|
||||
return l.Addr(), nil
|
||||
}
|
||||
|
||||
if strings.HasPrefix(stack, "udp") {
|
||||
l, err := net.ListenPacket(stack, addrWithPort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer l.Close()
|
||||
return l.LocalAddr(), nil
|
||||
}
|
||||
|
||||
return nil, errors.Errorf("stack not recognized: %s", stack)
|
||||
}
|
||||
|
||||
// resolveProtocol maps the [Protocol] value to a network stack string
|
||||
// that is supported by [net.Listen] and [net.ListenPacket].
|
||||
func resolveProtocol(protocol Protocol) string {
|
||||
var stack string
|
||||
|
||||
switch protocol {
|
||||
case TCP:
|
||||
stack = "tcp"
|
||||
case TCP4:
|
||||
stack = "tcp4"
|
||||
case TCP6:
|
||||
stack = "tcp6"
|
||||
case UDP:
|
||||
stack = "udp"
|
||||
case UDP4:
|
||||
stack = "udp4"
|
||||
case UDP6:
|
||||
stack = "udp6"
|
||||
}
|
||||
|
||||
return stack
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
package getport
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"yunion.io/x/log"
|
||||
)
|
||||
|
||||
func TestGetPort(t *testing.T) {
|
||||
t.Run("gets empty tcp port", func(t *testing.T) {
|
||||
portResult, err := GetPort(TCP, "127.0.0.1")
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, []string{"127.0.0.1", "::1"}, portResult.IP)
|
||||
assert.Greater(t, portResult.Port, 0)
|
||||
})
|
||||
|
||||
t.Run("get by empty address", func(t *testing.T) {
|
||||
result, err := GetPort(TCP4, "")
|
||||
assert.NoError(t, err)
|
||||
log.Infof("result: %#v", result)
|
||||
})
|
||||
|
||||
t.Run("gets empty tcp4 port", func(t *testing.T) {
|
||||
portResult, err := GetPort(TCP4, "127.0.0.1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, portResult.IP, "127.0.0.1")
|
||||
assert.Greater(t, portResult.Port, 0)
|
||||
})
|
||||
|
||||
t.Run("gets empty tcp6 port", func(t *testing.T) {
|
||||
portResult, err := GetPort(TCP6, "::1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, portResult.IP, "::1")
|
||||
assert.Greater(t, portResult.Port, 0)
|
||||
})
|
||||
|
||||
t.Run("gets empty udp port", func(t *testing.T) {
|
||||
portResult, err := GetPort(UDP, "127.0.0.1")
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, []string{"127.0.0.1", "::1"}, portResult.IP)
|
||||
assert.Greater(t, portResult.Port, 0)
|
||||
})
|
||||
|
||||
t.Run("gets empty udp4 port", func(t *testing.T) {
|
||||
portResult, err := GetPort(UDP4, "127.0.0.1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, portResult.IP, "127.0.0.1")
|
||||
assert.Greater(t, portResult.Port, 0)
|
||||
})
|
||||
|
||||
t.Run("gets empty udp6 port", func(t *testing.T) {
|
||||
portResult, err := GetPort(UDP6, "::1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, portResult.IP, "::1")
|
||||
assert.Greater(t, portResult.Port, 0)
|
||||
})
|
||||
|
||||
t.Run("errors for bad protocol", func(t *testing.T) {
|
||||
portResult, err := GetPort(-1, "127.0.0.1")
|
||||
assert.Equal(t, portResult.IP, "")
|
||||
assert.Equal(t, portResult.Port, -1)
|
||||
assert.Error(t, err, "stack not recognized")
|
||||
})
|
||||
|
||||
t.Run("get port within 30000 to 31000", func(t *testing.T) {
|
||||
portResult, err := GetPortByRange(TCP4, 30000, 31000)
|
||||
assert.NoError(t, err)
|
||||
log.Infof("port result: %#v", portResult)
|
||||
assert.Greater(t, portResult.Port, 30000)
|
||||
})
|
||||
|
||||
t.Run("get port within 30000 to 30001 except 30001", func(t *testing.T) {
|
||||
var l net.Listener
|
||||
var err error
|
||||
go func() {
|
||||
l, err = net.Listen("tcp4", net.JoinHostPort("", "30001"))
|
||||
if err != nil {
|
||||
t.Fatalf("listen 30001 tcp port: %v", err)
|
||||
}
|
||||
log.Infof("listen port 30001")
|
||||
}()
|
||||
time.Sleep(1 * time.Second)
|
||||
portResult, err := GetPortByRange(TCP4, 30000, 30001)
|
||||
assert.NoError(t, err)
|
||||
log.Infof("port result: %#v", portResult)
|
||||
assert.Equal(t, portResult.Port, 30000)
|
||||
l.Close()
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetTcpPort(t *testing.T) {
|
||||
portResult, err := GetTcpPort()
|
||||
assert.NoError(t, err)
|
||||
assert.Greater(t, portResult.Port, 0)
|
||||
}
|
||||
|
||||
func TestGetTcp4Port(t *testing.T) {
|
||||
portResult, err := GetTcp4Port()
|
||||
assert.NoError(t, err)
|
||||
assert.Greater(t, portResult.Port, 0)
|
||||
}
|
||||
|
||||
func TestGetTcp6Port(t *testing.T) {
|
||||
portResult, err := GetTcp6Port()
|
||||
assert.NoError(t, err)
|
||||
assert.Greater(t, portResult.Port, 0)
|
||||
}
|
||||
|
||||
func TestGetUdpPort(t *testing.T) {
|
||||
portResult, err := GetUdpPort()
|
||||
assert.NoError(t, err)
|
||||
assert.Greater(t, portResult.Port, 0)
|
||||
}
|
||||
|
||||
func TestGetUdp4Port(t *testing.T) {
|
||||
portResult, err := GetUdp4Port()
|
||||
assert.NoError(t, err)
|
||||
assert.Greater(t, portResult.Port, 0)
|
||||
}
|
||||
|
||||
func TestGetUdp6Port(t *testing.T) {
|
||||
portResult, err := GetUdp6Port()
|
||||
assert.NoError(t, err)
|
||||
assert.Greater(t, portResult.Port, 0)
|
||||
}
|
||||
|
||||
func TestGetTcpPortForAddress(t *testing.T) {
|
||||
portResult, err := GetTcpPortForAddress("127.0.0.1")
|
||||
assert.NoError(t, err)
|
||||
assert.Greater(t, portResult.Port, 0)
|
||||
}
|
||||
|
||||
func TestGetTcp4PortForAddress(t *testing.T) {
|
||||
portResult, err := GetTcp4PortForAddress("127.0.0.1")
|
||||
assert.NoError(t, err)
|
||||
assert.Greater(t, portResult.Port, 0)
|
||||
}
|
||||
|
||||
func TestGetTcp6PortForAddress(t *testing.T) {
|
||||
portResult, err := GetTcp6PortForAddress("::1")
|
||||
assert.NoError(t, err)
|
||||
assert.Greater(t, portResult.Port, 0)
|
||||
}
|
||||
|
||||
func TestGetUdpPortForAddress(t *testing.T) {
|
||||
portResult, err := GetUdpPortForAddress("127.0.0.1")
|
||||
assert.NoError(t, err)
|
||||
assert.Greater(t, portResult.Port, 0)
|
||||
}
|
||||
|
||||
func TestGetUdp4PortForAddress(t *testing.T) {
|
||||
portResult, err := GetUdp4PortForAddress("127.0.0.1")
|
||||
assert.NoError(t, err)
|
||||
assert.Greater(t, portResult.Port, 0)
|
||||
}
|
||||
|
||||
func TestGetUdp6PortForAddress(t *testing.T) {
|
||||
portResult, err := GetUdp6PortForAddress("::1")
|
||||
assert.NoError(t, err)
|
||||
assert.Greater(t, portResult.Port, 0)
|
||||
}
|
||||
|
||||
func TestPortResultToAddress(t *testing.T) {
|
||||
portResult := PortResult{
|
||||
IP: "::1",
|
||||
Port: 3000,
|
||||
}
|
||||
address := PortResultToAddress(portResult)
|
||||
assert.Equal(t, address, "[::1]:3000")
|
||||
}
|
||||
|
||||
func TestListen(t *testing.T) {
|
||||
t.Run("errors for bad tcp stack", func(t *testing.T) {
|
||||
addr, err := listen("tcp-bad", "127.0.0.1:0")
|
||||
assert.Empty(t, addr)
|
||||
assert.Error(t, err, "listen tcp-bad: unknown network tcp-bad")
|
||||
})
|
||||
|
||||
t.Run("errors for bad udp stack", func(t *testing.T) {
|
||||
addr, err := listen("udp-bad", "127.0.0.1:0")
|
||||
assert.Empty(t, addr)
|
||||
assert.Error(t, err, "listen udp-bad: unknown network udp-bad")
|
||||
})
|
||||
|
||||
t.Run("errors for bad stack", func(t *testing.T) {
|
||||
addr, err := listen("unix", "127.0.0.1:0")
|
||||
assert.Empty(t, addr)
|
||||
assert.Error(t, err, "stack not recognized")
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user