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