github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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  		host, port, err := net.SplitHostPort(hp)
    70  		if err != nil {
    71  			return nil, errors.Annotatef(err, "cannot parse %q as address:port", hp)
    72  		}
    73  		numPort, err := strconv.Atoi(port)
    74  		if err != nil {
    75  			return nil, errors.Annotatef(err, "cannot parse %q port", hp)
    76  		}
    77  		hps[i] = HostPort{
    78  			Address: NewAddress(host),
    79  			Port:    numPort,
    80  		}
    81  	}
    82  	return hps, nil
    83  }
    84  
    85  // HostsWithoutPort strips the port from each HostPort, returning just
    86  // the addresses.
    87  func HostsWithoutPort(hps []HostPort) []Address {
    88  	addrs := make([]Address, len(hps))
    89  	for i, hp := range hps {
    90  		addrs[i] = hp.Address
    91  	}
    92  	return addrs
    93  }
    94  
    95  type hostPortsPreferringIPv4Slice []HostPort
    96  
    97  func (hp hostPortsPreferringIPv4Slice) Len() int      { return len(hp) }
    98  func (hp hostPortsPreferringIPv4Slice) Swap(i, j int) { hp[i], hp[j] = hp[j], hp[i] }
    99  func (hp hostPortsPreferringIPv4Slice) Less(i, j int) bool {
   100  	hp1 := hp[i]
   101  	hp2 := hp[j]
   102  	order1 := hp1.sortOrder(false)
   103  	order2 := hp2.sortOrder(false)
   104  	if order1 == order2 {
   105  		return hp1.Address.Value < hp2.Address.Value
   106  	}
   107  	return order1 < order2
   108  }
   109  
   110  type hostPortsPreferringIPv6Slice struct {
   111  	hostPortsPreferringIPv4Slice
   112  }
   113  
   114  func (hp hostPortsPreferringIPv6Slice) Less(i, j int) bool {
   115  	hp1 := hp.hostPortsPreferringIPv4Slice[i]
   116  	hp2 := hp.hostPortsPreferringIPv4Slice[j]
   117  	order1 := hp1.sortOrder(true)
   118  	order2 := hp2.sortOrder(true)
   119  	if order1 == order2 {
   120  		return hp1.Address.Value < hp2.Address.Value
   121  	}
   122  	return order1 < order2
   123  }
   124  
   125  // SortHostPorts sorts the given HostPort slice according to the
   126  // sortOrder of each HostPort's embedded Address and the preferIpv6
   127  // flag. See Address.sortOrder() for more info.
   128  func SortHostPorts(hps []HostPort, preferIPv6 bool) {
   129  	if preferIPv6 {
   130  		sort.Sort(hostPortsPreferringIPv6Slice{hostPortsPreferringIPv4Slice(hps)})
   131  	} else {
   132  		sort.Sort(hostPortsPreferringIPv4Slice(hps))
   133  	}
   134  }
   135  
   136  var netLookupIP = net.LookupIP
   137  
   138  // ResolveOrDropHostnames tries to resolve each address of type
   139  // HostName (except for "localhost" - it's kept unchanged) using the
   140  // local resolver. If successful, each IP address corresponding to the
   141  // hostname is inserted in the same order. If not successful, a debug
   142  // log is added and the hostname is removed from the list. Duplicated
   143  // addresses after the resolving is done are removed.
   144  func ResolveOrDropHostnames(hps []HostPort) []HostPort {
   145  	uniqueAddrs := set.NewStrings()
   146  	result := make([]HostPort, 0, len(hps))
   147  	for _, hp := range hps {
   148  		val := hp.Value
   149  		if uniqueAddrs.Contains(val) {
   150  			continue
   151  		}
   152  		// localhost is special - do not resolve it, because it can be
   153  		// used both as an IPv4 or IPv6 endpoint (e.g. in IPv6-only
   154  		// networks).
   155  		if hp.Type != HostName || hp.Value == "localhost" {
   156  			result = append(result, hp)
   157  			uniqueAddrs.Add(val)
   158  			continue
   159  		}
   160  		ips, err := netLookupIP(val)
   161  		if err != nil {
   162  			logger.Debugf("removing unresolvable address %q: %v", val, err)
   163  			continue
   164  		}
   165  		for _, ip := range ips {
   166  			if ip == nil {
   167  				continue
   168  			}
   169  			addr := NewAddress(ip.String())
   170  			if !uniqueAddrs.Contains(addr.Value) {
   171  				result = append(result, HostPort{Address: addr, Port: hp.Port})
   172  				uniqueAddrs.Add(addr.Value)
   173  			}
   174  		}
   175  	}
   176  	return result
   177  }
   178  
   179  // FilterUnusableHostPorts returns a copy of the given HostPorts after
   180  // removing any addresses unlikely to be usable (ScopeMachineLocal or
   181  // ScopeLinkLocal).
   182  func FilterUnusableHostPorts(hps []HostPort) []HostPort {
   183  	filtered := make([]HostPort, 0, len(hps))
   184  	for _, hp := range hps {
   185  		switch hp.Scope {
   186  		case ScopeMachineLocal, ScopeLinkLocal:
   187  			continue
   188  		}
   189  		filtered = append(filtered, hp)
   190  	}
   191  	return filtered
   192  }
   193  
   194  // DropDuplicatedHostPorts removes any HostPorts duplicates from the
   195  // given slice and returns the result.
   196  func DropDuplicatedHostPorts(hps []HostPort) []HostPort {
   197  	uniqueHPs := set.NewStrings()
   198  	var result []HostPort
   199  	for _, hp := range hps {
   200  		if !uniqueHPs.Contains(hp.NetAddr()) {
   201  			uniqueHPs.Add(hp.NetAddr())
   202  			result = append(result, hp)
   203  		}
   204  	}
   205  	return result
   206  }
   207  
   208  // HostPortsToStrings converts each HostPort to string calling its
   209  // NetAddr() method.
   210  func HostPortsToStrings(hps []HostPort) []string {
   211  	result := make([]string, len(hps))
   212  	for i, hp := range hps {
   213  		result[i] = hp.NetAddr()
   214  	}
   215  	return result
   216  }
   217  
   218  // CollapseHostPorts returns a flattened list of HostPorts keeping the
   219  // same order they appear in serversHostPorts.
   220  func CollapseHostPorts(serversHostPorts [][]HostPort) []HostPort {
   221  	var collapsed []HostPort
   222  	for _, hps := range serversHostPorts {
   223  		collapsed = append(collapsed, hps...)
   224  	}
   225  	return collapsed
   226  }
   227  
   228  // EnsureFirstHostPort scans the given list of HostPorts and if
   229  // "first" is found, it moved to index 0. Otherwise, if "first" is not
   230  // in the list, it's inserted at index 0.
   231  func EnsureFirstHostPort(first HostPort, hps []HostPort) []HostPort {
   232  	var result []HostPort
   233  	found := false
   234  	for _, hp := range hps {
   235  		if hp.NetAddr() == first.NetAddr() && !found {
   236  			// Found, so skip it.
   237  			found = true
   238  			continue
   239  		}
   240  		result = append(result, hp)
   241  	}
   242  	// Insert it at the top.
   243  	result = append([]HostPort{first}, result...)
   244  	return result
   245  }