github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/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/deployer" 17 "github.com/juju/juju/api/diskformatter" 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/keyupdater" 22 apilogger "github.com/juju/juju/api/logger" 23 "github.com/juju/juju/api/machiner" 24 "github.com/juju/juju/api/networker" 25 "github.com/juju/juju/api/provisioner" 26 "github.com/juju/juju/api/reboot" 27 "github.com/juju/juju/api/rsyslog" 28 "github.com/juju/juju/api/uniter" 29 "github.com/juju/juju/api/upgrader" 30 "github.com/juju/juju/apiserver/params" 31 "github.com/juju/juju/network" 32 ) 33 34 // Login authenticates as the entity with the given name and password. 35 // Subsequent requests on the state will act as that entity. This 36 // method is usually called automatically by Open. The machine nonce 37 // should be empty unless logging in as a machine agent. 38 func (st *State) Login(tag, password, nonce string) error { 39 err := st.loginV1(tag, password, nonce) 40 if params.IsCodeNotImplemented(err) { 41 // TODO (cmars): remove fallback once we can drop v0 compatibility 42 return st.loginV0(tag, password, nonce) 43 } 44 return err 45 } 46 47 func (st *State) loginV1(tag, password, nonce string) error { 48 var result struct { 49 // TODO (cmars): remove once we can drop 1.18 login compatibility 50 params.LoginResult 51 52 params.LoginResultV1 53 } 54 err := st.APICall("Admin", 1, "", "Login", ¶ms.LoginRequestCompat{ 55 LoginRequest: params.LoginRequest{ 56 AuthTag: tag, 57 Credentials: password, 58 Nonce: nonce, 59 }, 60 // TODO (cmars): remove once we can drop 1.18 login compatibility 61 Creds: params.Creds{ 62 AuthTag: tag, 63 Password: password, 64 Nonce: nonce, 65 }, 66 }, &result) 67 if err != nil { 68 return err 69 } 70 71 // We've either logged into an Admin v1 facade, or a pre-facade (1.18) API 72 // server. The JSON field names between the structures are disjoint, so only 73 // one should have an environ tag set. 74 75 var environTag string 76 var servers [][]network.HostPort 77 var facades []params.FacadeVersions 78 if result.LoginResult.EnvironTag != "" { 79 environTag = result.LoginResult.EnvironTag 80 servers = result.LoginResult.Servers 81 facades = result.LoginResult.Facades 82 } else if result.LoginResultV1.EnvironTag != "" { 83 environTag = result.LoginResultV1.EnvironTag 84 servers = result.LoginResultV1.Servers 85 facades = result.LoginResultV1.Facades 86 } 87 88 err = st.setLoginResult(tag, environTag, servers, facades) 89 if err != nil { 90 return err 91 } 92 return nil 93 } 94 95 func (st *State) setLoginResult(tag, environTag string, servers [][]network.HostPort, facades []params.FacadeVersions) error { 96 authtag, err := names.ParseTag(tag) 97 if err != nil { 98 return err 99 } 100 st.authTag = authtag 101 st.environTag = environTag 102 103 hostPorts, err := addAddress(servers, st.addr) 104 if err != nil { 105 if clerr := st.Close(); clerr != nil { 106 err = errors.Annotatef(err, "error closing state: %v", clerr) 107 } 108 return err 109 } 110 st.hostPorts = hostPorts 111 112 st.facadeVersions = make(map[string][]int, len(facades)) 113 for _, facade := range facades { 114 st.facadeVersions[facade.Name] = facade.Versions 115 } 116 return nil 117 } 118 119 func (st *State) loginV0(tag, password, nonce string) error { 120 var result params.LoginResult 121 err := st.APICall("Admin", 0, "", "Login", ¶ms.Creds{ 122 AuthTag: tag, 123 Password: password, 124 Nonce: nonce, 125 }, &result) 126 if err != nil { 127 return err 128 } 129 if err = st.setLoginResult(tag, result.EnvironTag, result.Servers, result.Facades); err != nil { 130 return err 131 } 132 return nil 133 } 134 135 // slideAddressToFront moves the address at the location (serverIndex, addrIndex) to be 136 // the first address of the first server. 137 func slideAddressToFront(servers [][]network.HostPort, serverIndex, addrIndex int) { 138 server := servers[serverIndex] 139 hostPort := server[addrIndex] 140 // Move the matching address to be the first in this server 141 for ; addrIndex > 0; addrIndex-- { 142 server[addrIndex] = server[addrIndex-1] 143 } 144 server[0] = hostPort 145 for ; serverIndex > 0; serverIndex-- { 146 servers[serverIndex] = servers[serverIndex-1] 147 } 148 servers[0] = server 149 } 150 151 // addAddress appends a new server derived from the given 152 // address to servers if the address is not already found 153 // there. 154 func addAddress(servers [][]network.HostPort, addr string) ([][]network.HostPort, error) { 155 for i, server := range servers { 156 for j, hostPort := range server { 157 if hostPort.NetAddr() == addr { 158 slideAddressToFront(servers, i, j) 159 return servers, nil 160 } 161 } 162 } 163 host, portString, err := net.SplitHostPort(addr) 164 if err != nil { 165 return nil, err 166 } 167 port, err := strconv.Atoi(portString) 168 if err != nil { 169 return nil, err 170 } 171 hostPort := network.HostPort{ 172 Address: network.NewAddress(host, network.ScopeUnknown), 173 Port: port, 174 } 175 result := make([][]network.HostPort, 0, len(servers)+1) 176 result = append(result, []network.HostPort{hostPort}) 177 result = append(result, servers...) 178 return result, nil 179 } 180 181 // Client returns an object that can be used 182 // to access client-specific functionality. 183 func (st *State) Client() *Client { 184 frontend, backend := base.NewClientFacade(st, "Client") 185 return &Client{ClientFacade: frontend, facade: backend, st: st} 186 } 187 188 // Machiner returns a version of the state that provides functionality 189 // required by the machiner worker. 190 func (st *State) Machiner() *machiner.State { 191 return machiner.NewState(st) 192 } 193 194 // Networker returns a version of the state that provides functionality 195 // required by the networker worker. 196 func (st *State) Networker() *networker.State { 197 return networker.NewState(st) 198 } 199 200 // Provisioner returns a version of the state that provides functionality 201 // required by the provisioner worker. 202 func (st *State) Provisioner() *provisioner.State { 203 return provisioner.NewState(st) 204 } 205 206 // Uniter returns a version of the state that provides functionality 207 // required by the uniter worker. 208 func (st *State) Uniter() (*uniter.State, error) { 209 unitTag, ok := st.authTag.(names.UnitTag) 210 if !ok { 211 return nil, errors.Errorf("expected UnitTag, got %T %v", st.authTag, st.authTag) 212 } 213 return uniter.NewState(st, unitTag), nil 214 } 215 216 // DiskManager returns a version of the state that provides functionality 217 // required by the diskmanager worker. 218 func (st *State) DiskManager() (*diskmanager.State, error) { 219 machineTag, ok := st.authTag.(names.MachineTag) 220 if !ok { 221 return nil, errors.Errorf("expected MachineTag, got %#v", st.authTag) 222 } 223 return diskmanager.NewState(st, machineTag), nil 224 } 225 226 // DiskFormatter returns a version of the state that provides functionality 227 // required by the diskformatter worker. 228 func (st *State) DiskFormatter() (*diskformatter.State, error) { 229 unitTag, ok := st.authTag.(names.UnitTag) 230 if !ok { 231 return nil, errors.Errorf("expected UnitTag, got %#v", st.authTag) 232 } 233 return diskformatter.NewState(st, unitTag), nil 234 } 235 236 // Firewaller returns a version of the state that provides functionality 237 // required by the firewaller worker. 238 func (st *State) Firewaller() *firewaller.State { 239 return firewaller.NewState(st) 240 } 241 242 // Agent returns a version of the state that provides 243 // functionality required by the agent code. 244 func (st *State) Agent() *agent.State { 245 return agent.NewState(st) 246 } 247 248 // Upgrader returns access to the Upgrader API 249 func (st *State) Upgrader() *upgrader.State { 250 return upgrader.NewState(st) 251 } 252 253 // Reboot returns access to the Reboot API 254 func (st *State) Reboot() (*reboot.State, error) { 255 switch tag := st.authTag.(type) { 256 case names.MachineTag: 257 return reboot.NewState(st, tag), nil 258 default: 259 return nil, errors.Errorf("expected names.MachineTag, got %T", tag) 260 } 261 } 262 263 // Deployer returns access to the Deployer API 264 func (st *State) Deployer() *deployer.State { 265 return deployer.NewState(st) 266 } 267 268 // Environment returns access to the Environment API 269 func (st *State) Environment() *environment.Facade { 270 return environment.NewFacade(st) 271 } 272 273 // Logger returns access to the Logger API 274 func (st *State) Logger() *apilogger.State { 275 return apilogger.NewState(st) 276 } 277 278 // KeyUpdater returns access to the KeyUpdater API 279 func (st *State) KeyUpdater() *keyupdater.State { 280 return keyupdater.NewState(st) 281 } 282 283 // CharmRevisionUpdater returns access to the CharmRevisionUpdater API 284 func (st *State) CharmRevisionUpdater() *charmrevisionupdater.State { 285 return charmrevisionupdater.NewState(st) 286 } 287 288 // Rsyslog returns access to the Rsyslog API 289 func (st *State) Rsyslog() *rsyslog.State { 290 return rsyslog.NewState(st) 291 }