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 }