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