github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/network/hostport.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package network
     5  
     6  import (
     7  	"net"
     8  	"sort"
     9  	"strconv"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/utils/set"
    13  )
    14  
    15  // HostPort associates an address with a port.
    16  type HostPort struct {
    17  	Address
    18  	Port int
    19  }
    20  
    21  // NetAddr returns the host-port as an address
    22  // suitable for calling net.Dial.
    23  func (hp HostPort) NetAddr() string {
    24  	return net.JoinHostPort(hp.Value, strconv.Itoa(hp.Port))
    25  }
    26  
    27  // String implements Stringer.
    28  func (hp HostPort) String() string {
    29  	return hp.NetAddr()
    30  }
    31  
    32  // GoString implements fmt.GoStringer.
    33  func (hp HostPort) GoString() string {
    34  	return hp.String()
    35  }
    36  
    37  // AddressesWithPort returns the given addresses all
    38  // associated with the given port.
    39  func AddressesWithPort(addrs []Address, port int) []HostPort {
    40  	hps := make([]HostPort, len(addrs))
    41  	for i, addr := range addrs {
    42  		hps[i] = HostPort{
    43  			Address: addr,
    44  			Port:    port,
    45  		}
    46  	}
    47  	return hps
    48  }
    49  
    50  // NewHostPorts creates a list of HostPorts from each given string
    51  // address and port.
    52  func NewHostPorts(port int, addresses ...string) []HostPort {
    53  	hps := make([]HostPort, len(addresses))
    54  	for i, addr := range addresses {
    55  		hps[i] = HostPort{
    56  			Address: NewAddress(addr),
    57  			Port:    port,
    58  		}
    59  	}
    60  	return hps
    61  }
    62  
    63  // ParseHostPorts creates a list of HostPorts parsing each given
    64  // string containing address:port. An error is returned if any string
    65  // cannot be parsed as HostPort.
    66  func ParseHostPorts(hostPorts ...string) ([]HostPort, error) {
    67  	hps := make([]HostPort, len(hostPorts))
    68  	for i, hp := range hostPorts {
    69  		hostport, err := ParseHostPort(hp)
    70  		if err != nil {
    71  			return nil, errors.Trace(err)
    72  		}
    73  		hps[i] = *hostport
    74  	}
    75  	return hps, nil
    76  }
    77  
    78  // ParseHostPort converts a string containing a single host and port
    79  // value to a HostPort.
    80  func ParseHostPort(hp string) (*HostPort, error) {
    81  	host, port, err := net.SplitHostPort(hp)
    82  	if err != nil {
    83  		return nil, errors.Annotatef(err, "cannot parse %q as address:port", hp)
    84  	}
    85  	numPort, err := strconv.Atoi(port)
    86  	if err != nil {
    87  		return nil, errors.Annotatef(err, "cannot parse %q port", hp)
    88  	}
    89  	return &HostPort{
    90  		Address: NewAddress(host),
    91  		Port:    numPort,
    92  	}, nil
    93  }
    94  
    95  // HostsWithoutPort strips the port from each HostPort, returning just
    96  // the addresses.
    97  func HostsWithoutPort(hps []HostPort) []Address {
    98  	addrs := make([]Address, len(hps))
    99  	for i, hp := range hps {
   100  		addrs[i] = hp.Address
   101  	}
   102  	return addrs
   103  }
   104  
   105  type hostPortsPreferringIPv4Slice []HostPort
   106  
   107  func (hp hostPortsPreferringIPv4Slice) Len() int      { return len(hp) }
   108  func (hp hostPortsPreferringIPv4Slice) Swap(i, j int) { hp[i], hp[j] = hp[j], hp[i] }
   109  func (hp hostPortsPreferringIPv4Slice) Less(i, j int) bool {
   110  	hp1 := hp[i]
   111  	hp2 := hp[j]
   112  	order1 := hp1.sortOrder()
   113  	order2 := hp2.sortOrder()
   114  	if order1 == order2 {
   115  		if hp1.Address.Value == hp2.Address.Value {
   116  			return hp1.Port < hp2.Port
   117  		}
   118  		return hp1.Address.Value < hp2.Address.Value
   119  	}
   120  	return order1 < order2
   121  }
   122  
   123  // SortHostPorts sorts the given HostPort slice according to the sortOrder of
   124  // each HostPort's embedded Address. See Address.sortOrder() for more info.
   125  func SortHostPorts(hps []HostPort) {
   126  	sort.Sort(hostPortsPreferringIPv4Slice(hps))
   127  }
   128  
   129  var netLookupIP = net.LookupIP
   130  
   131  // ResolveOrDropHostnames tries to resolve each address of type
   132  // HostName (except for "localhost" - it's kept unchanged) using the
   133  // local resolver. If successful, each IP address corresponding to the
   134  // hostname is inserted in the same order. If not successful, a debug
   135  // log is added and the hostname is removed from the list. Duplicated
   136  // addresses after the resolving is done are removed.
   137  func ResolveOrDropHostnames(hps []HostPort) []HostPort {
   138  	uniqueAddrs := set.NewStrings()
   139  	result := make([]HostPort, 0, len(hps))
   140  	for _, hp := range hps {
   141  		val := hp.Value
   142  		if uniqueAddrs.Contains(val) {
   143  			continue
   144  		}
   145  		// localhost is special - do not resolve it, because it can be
   146  		// used both as an IPv4 or IPv6 endpoint (e.g. in IPv6-only
   147  		// networks).
   148  		if hp.Type != HostName || hp.Value == "localhost" {
   149  			result = append(result, hp)
   150  			uniqueAddrs.Add(val)
   151  			continue
   152  		}
   153  		ips, err := netLookupIP(val)
   154  		if err != nil {
   155  			logger.Debugf("removing unresolvable address %q: %v", val, err)
   156  			continue
   157  		}
   158  		for _, ip := range ips {
   159  			if ip == nil {
   160  				continue
   161  			}
   162  			addr := NewAddress(ip.String())
   163  			if !uniqueAddrs.Contains(addr.Value) {
   164  				result = append(result, HostPort{Address: addr, Port: hp.Port})
   165  				uniqueAddrs.Add(addr.Value)
   166  			}
   167  		}
   168  	}
   169  	return result
   170  }
   171  
   172  // FilterUnusableHostPorts returns a copy of the given HostPorts after
   173  // removing any addresses unlikely to be usable (ScopeMachineLocal or
   174  // ScopeLinkLocal).
   175  func FilterUnusableHostPorts(hps []HostPort) []HostPort {
   176  	filtered := make([]HostPort, 0, len(hps))
   177  	for _, hp := range hps {
   178  		switch hp.Scope {
   179  		case ScopeMachineLocal, ScopeLinkLocal:
   180  			continue
   181  		}
   182  		filtered = append(filtered, hp)
   183  	}
   184  	return filtered
   185  }
   186  
   187  // DropDuplicatedHostPorts removes any HostPorts duplicates from the
   188  // given slice and returns the result.
   189  func DropDuplicatedHostPorts(hps []HostPort) []HostPort {
   190  	uniqueHPs := set.NewStrings()
   191  	var result []HostPort
   192  	for _, hp := range hps {
   193  		if !uniqueHPs.Contains(hp.NetAddr()) {
   194  			uniqueHPs.Add(hp.NetAddr())
   195  			result = append(result, hp)
   196  		}
   197  	}
   198  	return result
   199  }
   200  
   201  // HostPortsToStrings converts each HostPort to string calling its
   202  // NetAddr() method.
   203  func HostPortsToStrings(hps []HostPort) []string {
   204  	result := make([]string, len(hps))
   205  	for i, hp := range hps {
   206  		result[i] = hp.NetAddr()
   207  	}
   208  	return result
   209  }
   210  
   211  // CollapseHostPorts returns a flattened list of HostPorts keeping the
   212  // same order they appear in serversHostPorts.
   213  func CollapseHostPorts(serversHostPorts [][]HostPort) []HostPort {
   214  	var collapsed []HostPort
   215  	for _, hps := range serversHostPorts {
   216  		collapsed = append(collapsed, hps...)
   217  	}
   218  	return collapsed
   219  }
   220  
   221  // EnsureFirstHostPort scans the given list of HostPorts and if
   222  // "first" is found, it moved to index 0. Otherwise, if "first" is not
   223  // in the list, it's inserted at index 0.
   224  func EnsureFirstHostPort(first HostPort, hps []HostPort) []HostPort {
   225  	var result []HostPort
   226  	found := false
   227  	for _, hp := range hps {
   228  		if hp.NetAddr() == first.NetAddr() && !found {
   229  			// Found, so skip it.
   230  			found = true
   231  			continue
   232  		}
   233  		result = append(result, hp)
   234  	}
   235  	// Insert it at the top.
   236  	result = append([]HostPort{first}, result...)
   237  	return result
   238  }