github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/state/address.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "net" 8 "reflect" 9 "strconv" 10 11 "github.com/juju/errors" 12 "gopkg.in/mgo.v2/bson" 13 "gopkg.in/mgo.v2/txn" 14 15 "github.com/juju/juju/network" 16 ) 17 18 // controllerAddresses returns the list of internal addresses of the state 19 // server machines. 20 func (st *State) controllerAddresses() ([]string, error) { 21 ssState := st 22 model, err := st.ControllerModel() 23 if err != nil { 24 return nil, errors.Trace(err) 25 } 26 if st.ModelTag() != model.ModelTag() { 27 // We are not using the controller model, so get one. 28 logger.Debugf("getting a controller state connection, current env: %s", st.ModelTag()) 29 ssState, err = st.ForModel(model.ModelTag()) 30 if err != nil { 31 return nil, errors.Trace(err) 32 } 33 defer ssState.Close() 34 logger.Debugf("ssState env: %s", ssState.ModelTag()) 35 } 36 37 type addressMachine struct { 38 Addresses []address 39 } 40 var allAddresses []addressMachine 41 // TODO(rog) 2013/10/14 index machines on jobs. 42 machines, closer := ssState.getCollection(machinesC) 43 defer closer() 44 err = machines.Find(bson.D{{"jobs", JobManageModel}}).All(&allAddresses) 45 if err != nil { 46 return nil, err 47 } 48 if len(allAddresses) == 0 { 49 return nil, errors.New("no controller machines found") 50 } 51 apiAddrs := make([]string, 0, len(allAddresses)) 52 for _, addrs := range allAddresses { 53 naddrs := networkAddresses(addrs.Addresses) 54 addr, ok := network.SelectControllerAddress(naddrs, false) 55 if ok { 56 apiAddrs = append(apiAddrs, addr.Value) 57 } 58 } 59 if len(apiAddrs) == 0 { 60 return nil, errors.New("no controller machines with addresses found") 61 } 62 return apiAddrs, nil 63 } 64 65 func appendPort(addrs []string, port int) []string { 66 newAddrs := make([]string, len(addrs)) 67 for i, addr := range addrs { 68 newAddrs[i] = net.JoinHostPort(addr, strconv.Itoa(port)) 69 } 70 return newAddrs 71 } 72 73 // Addresses returns the list of cloud-internal addresses that 74 // can be used to connect to the state. 75 func (st *State) Addresses() ([]string, error) { 76 addrs, err := st.controllerAddresses() 77 if err != nil { 78 return nil, errors.Trace(err) 79 } 80 config, err := st.ModelConfig() 81 if err != nil { 82 return nil, errors.Trace(err) 83 } 84 return appendPort(addrs, config.StatePort()), nil 85 } 86 87 // APIAddressesFromMachines returns the list of cloud-internal addresses that 88 // can be used to connect to the state API server. 89 // This method will be deprecated when API addresses are 90 // stored independently in their own document. 91 func (st *State) APIAddressesFromMachines() ([]string, error) { 92 addrs, err := st.controllerAddresses() 93 if err != nil { 94 return nil, errors.Trace(err) 95 } 96 config, err := st.ModelConfig() 97 if err != nil { 98 return nil, errors.Trace(err) 99 } 100 return appendPort(addrs, config.APIPort()), nil 101 } 102 103 const apiHostPortsKey = "apiHostPorts" 104 105 type apiHostPortsDoc struct { 106 APIHostPorts [][]hostPort `bson:"apihostports"` 107 } 108 109 // SetAPIHostPorts sets the addresses of the API server instances. 110 // Each server is represented by one element in the top level slice. 111 func (st *State) SetAPIHostPorts(netHostsPorts [][]network.HostPort) error { 112 doc := apiHostPortsDoc{ 113 APIHostPorts: fromNetworkHostsPorts(netHostsPorts), 114 } 115 buildTxn := func(attempt int) ([]txn.Op, error) { 116 existing, err := st.APIHostPorts() 117 if err != nil { 118 return nil, err 119 } 120 op := txn.Op{ 121 C: controllersC, 122 Id: apiHostPortsKey, 123 Assert: bson.D{{ 124 "apihostports", fromNetworkHostsPorts(existing), 125 }}, 126 } 127 if !hostsPortsEqual(netHostsPorts, existing) { 128 op.Update = bson.D{{ 129 "$set", bson.D{{"apihostports", doc.APIHostPorts}}, 130 }} 131 } 132 return []txn.Op{op}, nil 133 } 134 if err := st.run(buildTxn); err != nil { 135 return errors.Annotate(err, "cannot set API addresses") 136 } 137 logger.Debugf("setting API hostPorts: %v", netHostsPorts) 138 return nil 139 } 140 141 // APIHostPorts returns the API addresses as set by SetAPIHostPorts. 142 func (st *State) APIHostPorts() ([][]network.HostPort, error) { 143 var doc apiHostPortsDoc 144 controllers, closer := st.getCollection(controllersC) 145 defer closer() 146 err := controllers.Find(bson.D{{"_id", apiHostPortsKey}}).One(&doc) 147 if err != nil { 148 return nil, err 149 } 150 return networkHostsPorts(doc.APIHostPorts), nil 151 } 152 153 // address represents the location of a machine, including metadata 154 // about what kind of location the address describes. 155 // 156 // TODO(dimitern) Make sure we integrate this with other networking 157 // stuff at some point. We want to use juju-specific network names 158 // that point to existing documents in the networks collection. 159 type address struct { 160 Value string `bson:"value"` 161 AddressType string `bson:"addresstype"` 162 Scope string `bson:"networkscope,omitempty"` 163 Origin string `bson:"origin,omitempty"` 164 SpaceName string `bson:"spacename,omitempty"` 165 } 166 167 // Origin specifies where an address comes from, whether it was reported by a 168 // provider or by a machine. 169 type Origin string 170 171 const ( 172 // Address origin unknown. 173 OriginUnknown Origin = "" 174 // Address comes from a provider. 175 OriginProvider Origin = "provider" 176 // Address comes from a machine. 177 OriginMachine Origin = "machine" 178 ) 179 180 // fromNetworkAddress is a convenience helper to create a state type 181 // out of the network type, here for Address with a given Origin. 182 func fromNetworkAddress(netAddr network.Address, origin Origin) address { 183 return address{ 184 Value: netAddr.Value, 185 AddressType: string(netAddr.Type), 186 Scope: string(netAddr.Scope), 187 Origin: string(origin), 188 SpaceName: string(netAddr.SpaceName), 189 } 190 } 191 192 // networkAddress is a convenience helper to return the state type 193 // as network type, here for Address. 194 func (addr *address) networkAddress() network.Address { 195 return network.Address{ 196 Value: addr.Value, 197 Type: network.AddressType(addr.AddressType), 198 Scope: network.Scope(addr.Scope), 199 SpaceName: network.SpaceName(addr.SpaceName), 200 } 201 } 202 203 // fromNetworkAddresses is a convenience helper to create a state type 204 // out of the network type, here for a slice of Address with a given origin. 205 func fromNetworkAddresses(netAddrs []network.Address, origin Origin) []address { 206 addrs := make([]address, len(netAddrs)) 207 for i, netAddr := range netAddrs { 208 addrs[i] = fromNetworkAddress(netAddr, origin) 209 } 210 return addrs 211 } 212 213 // networkAddresses is a convenience helper to return the state type 214 // as network type, here for a slice of Address. 215 func networkAddresses(addrs []address) []network.Address { 216 netAddrs := make([]network.Address, len(addrs)) 217 for i, addr := range addrs { 218 netAddrs[i] = addr.networkAddress() 219 } 220 return netAddrs 221 } 222 223 // hostPort associates an address with a port. See also network.HostPort, 224 // from/to which this is transformed. 225 // 226 // TODO(dimitern) Make sure we integrate this with other networking 227 // stuff at some point. We want to use juju-specific network names 228 // that point to existing documents in the networks collection. 229 type hostPort struct { 230 Value string `bson:"value"` 231 AddressType string `bson:"addresstype"` 232 Scope string `bson:"networkscope,omitempty"` 233 Port int `bson:"port"` 234 SpaceName string `bson:"spacename,omitempty"` 235 } 236 237 // fromNetworkHostPort is a convenience helper to create a state type 238 // out of the network type, here for HostPort. 239 func fromNetworkHostPort(netHostPort network.HostPort) hostPort { 240 return hostPort{ 241 Value: netHostPort.Value, 242 AddressType: string(netHostPort.Type), 243 Scope: string(netHostPort.Scope), 244 Port: netHostPort.Port, 245 SpaceName: string(netHostPort.SpaceName), 246 } 247 } 248 249 // networkHostPort is a convenience helper to return the state type 250 // as network type, here for HostPort. 251 func (hp *hostPort) networkHostPort() network.HostPort { 252 return network.HostPort{ 253 Address: network.Address{ 254 Value: hp.Value, 255 Type: network.AddressType(hp.AddressType), 256 Scope: network.Scope(hp.Scope), 257 SpaceName: network.SpaceName(hp.SpaceName), 258 }, 259 Port: hp.Port, 260 } 261 } 262 263 // fromNetworkHostsPorts is a helper to create a state type 264 // out of the network type, here for a nested slice of HostPort. 265 func fromNetworkHostsPorts(netHostsPorts [][]network.HostPort) [][]hostPort { 266 hsps := make([][]hostPort, len(netHostsPorts)) 267 for i, netHostPorts := range netHostsPorts { 268 hsps[i] = make([]hostPort, len(netHostPorts)) 269 for j, netHostPort := range netHostPorts { 270 hsps[i][j] = fromNetworkHostPort(netHostPort) 271 } 272 } 273 return hsps 274 } 275 276 // networkHostsPorts is a convenience helper to return the state type 277 // as network type, here for a nested slice of HostPort. 278 func networkHostsPorts(hsps [][]hostPort) [][]network.HostPort { 279 netHostsPorts := make([][]network.HostPort, len(hsps)) 280 for i, hps := range hsps { 281 netHostsPorts[i] = make([]network.HostPort, len(hps)) 282 for j, hp := range hps { 283 netHostsPorts[i][j] = hp.networkHostPort() 284 } 285 } 286 return netHostsPorts 287 } 288 289 // addressEqual checks that two slices of network addresses are equal. 290 func addressesEqual(a, b []network.Address) bool { 291 return reflect.DeepEqual(a, b) 292 } 293 294 // hostsPortsEqual checks that two arrays of network hostports are equal. 295 func hostsPortsEqual(a, b [][]network.HostPort) bool { 296 return reflect.DeepEqual(a, b) 297 }