github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/api/state.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package api 5 6 import ( 7 "net" 8 "strconv" 9 10 "github.com/juju/errors" 11 "github.com/juju/names" 12 13 "github.com/juju/juju/api/addresser" 14 "github.com/juju/juju/api/agent" 15 "github.com/juju/juju/api/base" 16 "github.com/juju/juju/api/charmrevisionupdater" 17 "github.com/juju/juju/api/cleaner" 18 "github.com/juju/juju/api/deployer" 19 "github.com/juju/juju/api/diskmanager" 20 "github.com/juju/juju/api/environment" 21 "github.com/juju/juju/api/firewaller" 22 "github.com/juju/juju/api/instancepoller" 23 "github.com/juju/juju/api/keyupdater" 24 apilogger "github.com/juju/juju/api/logger" 25 "github.com/juju/juju/api/machiner" 26 "github.com/juju/juju/api/networker" 27 "github.com/juju/juju/api/provisioner" 28 "github.com/juju/juju/api/reboot" 29 "github.com/juju/juju/api/resumer" 30 "github.com/juju/juju/api/rsyslog" 31 "github.com/juju/juju/api/storageprovisioner" 32 "github.com/juju/juju/api/uniter" 33 "github.com/juju/juju/api/upgrader" 34 "github.com/juju/juju/apiserver/params" 35 "github.com/juju/juju/network" 36 "github.com/juju/juju/version" 37 ) 38 39 // Login authenticates as the entity with the given name and password. 40 // Subsequent requests on the state will act as that entity. This 41 // method is usually called automatically by Open. The machine nonce 42 // should be empty unless logging in as a machine agent. 43 func (st *State) Login(tag, password, nonce string) error { 44 err := st.loginV2(tag, password, nonce) 45 if params.IsCodeNotImplemented(err) { 46 err = st.loginV1(tag, password, nonce) 47 if params.IsCodeNotImplemented(err) { 48 // TODO (cmars): remove fallback once we can drop v0 compatibility 49 return st.loginV0(tag, password, nonce) 50 } 51 } 52 return err 53 } 54 55 func (st *State) loginV2(tag, password, nonce string) error { 56 var result params.LoginResultV1 57 request := ¶ms.LoginRequest{ 58 AuthTag: tag, 59 Credentials: password, 60 Nonce: nonce, 61 } 62 err := st.APICall("Admin", 2, "", "Login", request, &result) 63 if err != nil { 64 // If the server complains about an empty tag it may be that we are 65 // talking to an older server version that does not understand facades and 66 // expects a params.Creds request instead of a params.LoginRequest. We 67 // return a CodNotImplemented error to force login down to V1, which 68 // supports older server logins. This may mask an actual empty tag in 69 // params.LoginRequest, but that would be picked up in loginV1. V1 will 70 // also produce a warning that we are ignoring an invalid API, so we do not 71 // need to add one here. 72 if err.Error() == `"" is not a valid tag` { 73 return ¶ms.Error{ 74 Message: err.Error(), 75 Code: params.CodeNotImplemented, 76 } 77 } 78 return errors.Trace(err) 79 } 80 servers := params.NetworkHostsPorts(result.Servers) 81 err = st.setLoginResult(tag, result.EnvironTag, result.ServerTag, servers, result.Facades) 82 if err != nil { 83 return errors.Trace(err) 84 } 85 st.serverVersion, err = version.Parse(result.ServerVersion) 86 if err != nil { 87 return errors.Trace(err) 88 } 89 return nil 90 } 91 92 func (st *State) loginV1(tag, password, nonce string) error { 93 var result struct { 94 // TODO (cmars): remove once we can drop 1.18 login compatibility 95 params.LoginResult 96 97 params.LoginResultV1 98 } 99 err := st.APICall("Admin", 1, "", "Login", ¶ms.LoginRequestCompat{ 100 LoginRequest: params.LoginRequest{ 101 AuthTag: tag, 102 Credentials: password, 103 Nonce: nonce, 104 }, 105 // TODO (cmars): remove once we can drop 1.18 login compatibility 106 Creds: params.Creds{ 107 AuthTag: tag, 108 Password: password, 109 Nonce: nonce, 110 }, 111 }, &result) 112 if err != nil { 113 return err 114 } 115 116 // We've either logged into an Admin v1 facade, or a pre-facade (1.18) API 117 // server. The JSON field names between the structures are disjoint, so only 118 // one should have an environ tag set. 119 120 var environTag string 121 var serverTag string 122 var servers [][]network.HostPort 123 var facades []params.FacadeVersions 124 // For quite old servers, it is possible that they don't send down 125 // the environTag. 126 if result.LoginResult.EnvironTag != "" { 127 environTag = result.LoginResult.EnvironTag 128 // If the server doesn't support login v1, it doesn't support 129 // multiple environments, so don't store a server tag. 130 servers = params.NetworkHostsPorts(result.LoginResult.Servers) 131 facades = result.LoginResult.Facades 132 } else if result.LoginResultV1.EnvironTag != "" { 133 environTag = result.LoginResultV1.EnvironTag 134 serverTag = result.LoginResultV1.ServerTag 135 servers = params.NetworkHostsPorts(result.LoginResultV1.Servers) 136 facades = result.LoginResultV1.Facades 137 } 138 139 err = st.setLoginResult(tag, environTag, serverTag, servers, facades) 140 if err != nil { 141 return err 142 } 143 return nil 144 } 145 146 func (st *State) setLoginResult(tag, environTag, serverTag string, servers [][]network.HostPort, facades []params.FacadeVersions) error { 147 authtag, err := names.ParseTag(tag) 148 if err != nil { 149 return err 150 } 151 st.authTag = authtag 152 st.environTag = environTag 153 st.serverTag = serverTag 154 155 hostPorts, err := addAddress(servers, st.addr) 156 if err != nil { 157 if clerr := st.Close(); clerr != nil { 158 err = errors.Annotatef(err, "error closing state: %v", clerr) 159 } 160 return err 161 } 162 st.hostPorts = hostPorts 163 164 st.facadeVersions = make(map[string][]int, len(facades)) 165 for _, facade := range facades { 166 st.facadeVersions[facade.Name] = facade.Versions 167 } 168 return nil 169 } 170 171 func (st *State) loginV0(tag, password, nonce string) error { 172 var result params.LoginResult 173 err := st.APICall("Admin", 0, "", "Login", ¶ms.Creds{ 174 AuthTag: tag, 175 Password: password, 176 Nonce: nonce, 177 }, &result) 178 if err != nil { 179 return err 180 } 181 servers := params.NetworkHostsPorts(result.Servers) 182 // Don't set a server tag. 183 if err = st.setLoginResult(tag, result.EnvironTag, "", servers, result.Facades); err != nil { 184 return err 185 } 186 return nil 187 } 188 189 // slideAddressToFront moves the address at the location (serverIndex, addrIndex) to be 190 // the first address of the first server. 191 func slideAddressToFront(servers [][]network.HostPort, serverIndex, addrIndex int) { 192 server := servers[serverIndex] 193 hostPort := server[addrIndex] 194 // Move the matching address to be the first in this server 195 for ; addrIndex > 0; addrIndex-- { 196 server[addrIndex] = server[addrIndex-1] 197 } 198 server[0] = hostPort 199 for ; serverIndex > 0; serverIndex-- { 200 servers[serverIndex] = servers[serverIndex-1] 201 } 202 servers[0] = server 203 } 204 205 // addAddress appends a new server derived from the given 206 // address to servers if the address is not already found 207 // there. 208 func addAddress(servers [][]network.HostPort, addr string) ([][]network.HostPort, error) { 209 for i, server := range servers { 210 for j, hostPort := range server { 211 if hostPort.NetAddr() == addr { 212 slideAddressToFront(servers, i, j) 213 return servers, nil 214 } 215 } 216 } 217 host, portString, err := net.SplitHostPort(addr) 218 if err != nil { 219 return nil, err 220 } 221 port, err := strconv.Atoi(portString) 222 if err != nil { 223 return nil, err 224 } 225 result := make([][]network.HostPort, 0, len(servers)+1) 226 result = append(result, network.NewHostPorts(port, host)) 227 result = append(result, servers...) 228 return result, nil 229 } 230 231 // Client returns an object that can be used 232 // to access client-specific functionality. 233 func (st *State) Client() *Client { 234 frontend, backend := base.NewClientFacade(st, "Client") 235 return &Client{ClientFacade: frontend, facade: backend, st: st} 236 } 237 238 // Machiner returns a version of the state that provides functionality 239 // required by the machiner worker. 240 func (st *State) Machiner() *machiner.State { 241 return machiner.NewState(st) 242 } 243 244 // Resumer returns a version of the state that provides functionality 245 // required by the resumer worker. 246 func (st *State) Resumer() *resumer.API { 247 return resumer.NewAPI(st) 248 } 249 250 // Networker returns a version of the state that provides functionality 251 // required by the networker worker. 252 func (st *State) Networker() networker.State { 253 return networker.NewState(st) 254 } 255 256 // Provisioner returns a version of the state that provides functionality 257 // required by the provisioner worker. 258 func (st *State) Provisioner() *provisioner.State { 259 return provisioner.NewState(st) 260 } 261 262 // Uniter returns a version of the state that provides functionality 263 // required by the uniter worker. 264 func (st *State) Uniter() (*uniter.State, error) { 265 unitTag, ok := st.authTag.(names.UnitTag) 266 if !ok { 267 return nil, errors.Errorf("expected UnitTag, got %T %v", st.authTag, st.authTag) 268 } 269 return uniter.NewState(st, unitTag), nil 270 } 271 272 // DiskManager returns a version of the state that provides functionality 273 // required by the diskmanager worker. 274 func (st *State) DiskManager() (*diskmanager.State, error) { 275 machineTag, ok := st.authTag.(names.MachineTag) 276 if !ok { 277 return nil, errors.Errorf("expected MachineTag, got %#v", st.authTag) 278 } 279 return diskmanager.NewState(st, machineTag), nil 280 } 281 282 // StorageProvisioner returns a version of the state that provides 283 // functionality required by the storageprovisioner worker. 284 // The scope tag defines the type of storage that is provisioned, either 285 // either attached directly to a specified machine (machine scoped), 286 // or provisioned on the underlying cloud for use by any machine in a 287 // specified environment (environ scoped). 288 func (st *State) StorageProvisioner(scope names.Tag) *storageprovisioner.State { 289 return storageprovisioner.NewState(st, scope) 290 } 291 292 // Firewaller returns a version of the state that provides functionality 293 // required by the firewaller worker. 294 func (st *State) Firewaller() *firewaller.State { 295 return firewaller.NewState(st) 296 } 297 298 // Agent returns a version of the state that provides 299 // functionality required by the agent code. 300 func (st *State) Agent() *agent.State { 301 return agent.NewState(st) 302 } 303 304 // Upgrader returns access to the Upgrader API 305 func (st *State) Upgrader() *upgrader.State { 306 return upgrader.NewState(st) 307 } 308 309 // Reboot returns access to the Reboot API 310 func (st *State) Reboot() (*reboot.State, error) { 311 switch tag := st.authTag.(type) { 312 case names.MachineTag: 313 return reboot.NewState(st, tag), nil 314 default: 315 return nil, errors.Errorf("expected names.MachineTag, got %T", tag) 316 } 317 } 318 319 // Deployer returns access to the Deployer API 320 func (st *State) Deployer() *deployer.State { 321 return deployer.NewState(st) 322 } 323 324 // Addresser returns access to the Addresser API. 325 func (st *State) Addresser() *addresser.API { 326 return addresser.NewAPI(st) 327 } 328 329 // Environment returns access to the Environment API 330 func (st *State) Environment() *environment.Facade { 331 return environment.NewFacade(st) 332 } 333 334 // Logger returns access to the Logger API 335 func (st *State) Logger() *apilogger.State { 336 return apilogger.NewState(st) 337 } 338 339 // KeyUpdater returns access to the KeyUpdater API 340 func (st *State) KeyUpdater() *keyupdater.State { 341 return keyupdater.NewState(st) 342 } 343 344 // InstancePoller returns access to the InstancePoller API 345 func (st *State) InstancePoller() *instancepoller.API { 346 return instancepoller.NewAPI(st) 347 } 348 349 // CharmRevisionUpdater returns access to the CharmRevisionUpdater API 350 func (st *State) CharmRevisionUpdater() *charmrevisionupdater.State { 351 return charmrevisionupdater.NewState(st) 352 } 353 354 // Cleaner returns a version of the state that provides access to the cleaner API 355 func (st *State) Cleaner() *cleaner.API { 356 return cleaner.NewAPI(st) 357 } 358 359 // Rsyslog returns access to the Rsyslog API 360 func (st *State) Rsyslog() *rsyslog.State { 361 return rsyslog.NewState(st) 362 } 363 364 // ServerVersion holds the version of the API server that we are connected to. 365 // It is possible that this version is Zero if the server does not report this 366 // during login. The second result argument indicates if the version number is 367 // set. 368 func (st *State) ServerVersion() (version.Number, bool) { 369 return st.serverVersion, st.serverVersion != version.Zero 370 }