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