github.com/fabiokung/docker@v0.11.2-0.20170222101415-4534dcd49497/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  		ports, portBindingMap, err := nat.ParsePortSpecs([]string{value})
    98  		if err != nil {
    99  			return err
   100  		}
   101  		for _, portBindings := range portBindingMap {
   102  			for _, portBinding := range portBindings {
   103  				if portBinding.HostIP != "" {
   104  					return fmt.Errorf("HostIP is not supported.")
   105  				}
   106  			}
   107  		}
   108  
   109  		for port := range ports {
   110  			portConfig, err := ConvertPortToPortConfig(port, portBindingMap)
   111  			if err != nil {
   112  				return err
   113  			}
   114  			portConfigs = append(portConfigs, portConfig...)
   115  		}
   116  		p.ports = append(p.ports, portConfigs...)
   117  	}
   118  	return nil
   119  }
   120  
   121  // Type returns the type of this option
   122  func (p *PortOpt) Type() string {
   123  	return "port"
   124  }
   125  
   126  // String returns a string repr of this option
   127  func (p *PortOpt) String() string {
   128  	ports := []string{}
   129  	for _, port := range p.ports {
   130  		repr := fmt.Sprintf("%v:%v/%s/%s", port.PublishedPort, port.TargetPort, port.Protocol, port.PublishMode)
   131  		ports = append(ports, repr)
   132  	}
   133  	return strings.Join(ports, ", ")
   134  }
   135  
   136  // Value returns the ports
   137  func (p *PortOpt) Value() []swarm.PortConfig {
   138  	return p.ports
   139  }
   140  
   141  // ConvertPortToPortConfig converts ports to the swarm type
   142  func ConvertPortToPortConfig(
   143  	port nat.Port,
   144  	portBindings map[nat.Port][]nat.PortBinding,
   145  ) ([]swarm.PortConfig, error) {
   146  	ports := []swarm.PortConfig{}
   147  
   148  	for _, binding := range portBindings[port] {
   149  		hostPort, err := strconv.ParseUint(binding.HostPort, 10, 16)
   150  		if err != nil && binding.HostPort != "" {
   151  			return nil, fmt.Errorf("invalid hostport binding (%s) for port (%s)", binding.HostPort, port.Port())
   152  		}
   153  		ports = append(ports, swarm.PortConfig{
   154  			//TODO Name: ?
   155  			Protocol:      swarm.PortConfigProtocol(strings.ToLower(port.Proto())),
   156  			TargetPort:    uint32(port.Int()),
   157  			PublishedPort: uint32(hostPort),
   158  			PublishMode:   swarm.PortConfigPublishModeIngress,
   159  		})
   160  	}
   161  	return ports, nil
   162  }