github.com/kunnos/engine@v1.13.1/opts/port.go (about)

     1  package opts
     2  
     3  import (
     4  	"encoding/csv"
     5  	"fmt"
     6  	"regexp"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/docker/docker/api/types/swarm"
    11  	"github.com/docker/go-connections/nat"
    12  )
    13  
    14  const (
    15  	portOptTargetPort    = "target"
    16  	portOptPublishedPort = "published"
    17  	portOptProtocol      = "protocol"
    18  	portOptMode          = "mode"
    19  )
    20  
    21  // PortOpt represents a port config in swarm mode.
    22  type PortOpt struct {
    23  	ports []swarm.PortConfig
    24  }
    25  
    26  // Set a new port value
    27  func (p *PortOpt) Set(value string) error {
    28  	longSyntax, err := regexp.MatchString(`\w+=\w+(,\w+=\w+)*`, value)
    29  	if err != nil {
    30  		return err
    31  	}
    32  	if longSyntax {
    33  		csvReader := csv.NewReader(strings.NewReader(value))
    34  		fields, err := csvReader.Read()
    35  		if err != nil {
    36  			return err
    37  		}
    38  
    39  		pConfig := swarm.PortConfig{}
    40  		for _, field := range fields {
    41  			parts := strings.SplitN(field, "=", 2)
    42  			if len(parts) != 2 {
    43  				return fmt.Errorf("invalid field %s", field)
    44  			}
    45  
    46  			key := strings.ToLower(parts[0])
    47  			value := strings.ToLower(parts[1])
    48  
    49  			switch key {
    50  			case portOptProtocol:
    51  				if value != string(swarm.PortConfigProtocolTCP) && value != string(swarm.PortConfigProtocolUDP) {
    52  					return fmt.Errorf("invalid protocol value %s", value)
    53  				}
    54  
    55  				pConfig.Protocol = swarm.PortConfigProtocol(value)
    56  			case portOptMode:
    57  				if value != string(swarm.PortConfigPublishModeIngress) && value != string(swarm.PortConfigPublishModeHost) {
    58  					return fmt.Errorf("invalid publish mode value %s", value)
    59  				}
    60  
    61  				pConfig.PublishMode = swarm.PortConfigPublishMode(value)
    62  			case portOptTargetPort:
    63  				tPort, err := strconv.ParseUint(value, 10, 16)
    64  				if err != nil {
    65  					return err
    66  				}
    67  
    68  				pConfig.TargetPort = uint32(tPort)
    69  			case portOptPublishedPort:
    70  				pPort, err := strconv.ParseUint(value, 10, 16)
    71  				if err != nil {
    72  					return err
    73  				}
    74  
    75  				pConfig.PublishedPort = uint32(pPort)
    76  			default:
    77  				return fmt.Errorf("invalid field key %s", key)
    78  			}
    79  		}
    80  
    81  		if pConfig.TargetPort == 0 {
    82  			return fmt.Errorf("missing mandatory field %q", portOptTargetPort)
    83  		}
    84  
    85  		if pConfig.PublishMode == "" {
    86  			pConfig.PublishMode = swarm.PortConfigPublishModeIngress
    87  		}
    88  
    89  		if pConfig.Protocol == "" {
    90  			pConfig.Protocol = swarm.PortConfigProtocolTCP
    91  		}
    92  
    93  		p.ports = append(p.ports, pConfig)
    94  	} else {
    95  		// short syntax
    96  		portConfigs := []swarm.PortConfig{}
    97  		// We can ignore errors because the format was already validated by ValidatePort
    98  		ports, portBindings, _ := nat.ParsePortSpecs([]string{value})
    99  
   100  		for port := range ports {
   101  			portConfigs = append(portConfigs, ConvertPortToPortConfig(port, portBindings)...)
   102  		}
   103  		p.ports = append(p.ports, portConfigs...)
   104  	}
   105  	return nil
   106  }
   107  
   108  // Type returns the type of this option
   109  func (p *PortOpt) Type() string {
   110  	return "port"
   111  }
   112  
   113  // String returns a string repr of this option
   114  func (p *PortOpt) String() string {
   115  	ports := []string{}
   116  	for _, port := range p.ports {
   117  		repr := fmt.Sprintf("%v:%v/%s/%s", port.PublishedPort, port.TargetPort, port.Protocol, port.PublishMode)
   118  		ports = append(ports, repr)
   119  	}
   120  	return strings.Join(ports, ", ")
   121  }
   122  
   123  // Value returns the ports
   124  func (p *PortOpt) Value() []swarm.PortConfig {
   125  	return p.ports
   126  }
   127  
   128  // ConvertPortToPortConfig converts ports to the swarm type
   129  func ConvertPortToPortConfig(
   130  	port nat.Port,
   131  	portBindings map[nat.Port][]nat.PortBinding,
   132  ) []swarm.PortConfig {
   133  	ports := []swarm.PortConfig{}
   134  
   135  	for _, binding := range portBindings[port] {
   136  		hostPort, _ := strconv.ParseUint(binding.HostPort, 10, 16)
   137  		ports = append(ports, swarm.PortConfig{
   138  			//TODO Name: ?
   139  			Protocol:      swarm.PortConfigProtocol(strings.ToLower(port.Proto())),
   140  			TargetPort:    uint32(port.Int()),
   141  			PublishedPort: uint32(hostPort),
   142  			PublishMode:   swarm.PortConfigPublishModeIngress,
   143  		})
   144  	}
   145  	return ports
   146  }