github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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 "github.com/juju/version" 13 "gopkg.in/macaroon-bakery.v1/httpbakery" 14 "gopkg.in/macaroon.v1" 15 16 "github.com/juju/juju/api/addresser" 17 "github.com/juju/juju/api/base" 18 "github.com/juju/juju/api/charmrevisionupdater" 19 "github.com/juju/juju/api/cleaner" 20 "github.com/juju/juju/api/discoverspaces" 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 "github.com/juju/juju/api/provisioner" 26 "github.com/juju/juju/api/reboot" 27 "github.com/juju/juju/api/unitassigner" 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 // or macaroons. Subsequent requests on the state will act as that entity. 36 // This 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 names.Tag, password, nonce string, ms []macaroon.Slice) error { 39 err := st.loginV3(tag, password, nonce, ms) 40 return errors.Trace(err) 41 } 42 43 // loginV2 is retained for testing logins from older clients. 44 func (st *state) loginV2(tag names.Tag, password, nonce string, ms []macaroon.Slice) error { 45 return st.loginForVersion(tag, password, nonce, ms, 2) 46 } 47 48 func (st *state) loginV3(tag names.Tag, password, nonce string, ms []macaroon.Slice) error { 49 return st.loginForVersion(tag, password, nonce, ms, 3) 50 } 51 52 func (st *state) loginForVersion(tag names.Tag, password, nonce string, macaroons []macaroon.Slice, vers int) error { 53 var result params.LoginResultV1 54 request := ¶ms.LoginRequest{ 55 AuthTag: tagToString(tag), 56 Credentials: password, 57 Nonce: nonce, 58 Macaroons: macaroons, 59 } 60 if tag == nil { 61 // Add any macaroons from the cookie jar that might work for 62 // authenticating the login request. 63 request.Macaroons = append(request.Macaroons, 64 httpbakery.MacaroonsForURL(st.bakeryClient.Client.Jar, st.cookieURL)..., 65 ) 66 } 67 err := st.APICall("Admin", vers, "", "Login", request, &result) 68 if err != nil { 69 return errors.Trace(err) 70 } 71 if result.DischargeRequired != nil { 72 // The result contains a discharge-required 73 // macaroon. We discharge it and retry 74 // the login request with the original macaroon 75 // and its discharges. 76 if result.DischargeRequiredReason == "" { 77 result.DischargeRequiredReason = "no reason given for discharge requirement" 78 } 79 if err := st.bakeryClient.HandleError(st.cookieURL, &httpbakery.Error{ 80 Message: result.DischargeRequiredReason, 81 Code: httpbakery.ErrDischargeRequired, 82 Info: &httpbakery.ErrorInfo{ 83 Macaroon: result.DischargeRequired, 84 MacaroonPath: "/", 85 }, 86 }); err != nil { 87 return errors.Trace(err) 88 } 89 // Add the macaroons that have been saved by HandleError to our login request. 90 request.Macaroons = httpbakery.MacaroonsForURL(st.bakeryClient.Client.Jar, st.cookieURL) 91 result = params.LoginResultV1{} // zero result 92 err = st.APICall("Admin", vers, "", "Login", request, &result) 93 if err != nil { 94 return errors.Trace(err) 95 } 96 if result.DischargeRequired != nil { 97 return errors.Errorf("login with discharged macaroons failed: %s", result.DischargeRequiredReason) 98 } 99 } 100 101 servers := params.NetworkHostsPorts(result.Servers) 102 err = st.setLoginResult(tag, result.ModelTag, result.ControllerTag, servers, result.Facades) 103 if err != nil { 104 return errors.Trace(err) 105 } 106 st.serverVersion, err = version.Parse(result.ServerVersion) 107 if err != nil { 108 return errors.Trace(err) 109 } 110 return nil 111 } 112 113 func (st *state) setLoginResult(tag names.Tag, modelTag, controllerTag string, servers [][]network.HostPort, facades []params.FacadeVersions) error { 114 st.authTag = tag 115 st.modelTag = modelTag 116 st.controllerTag = controllerTag 117 118 hostPorts, err := addAddress(servers, st.addr) 119 if err != nil { 120 if clerr := st.Close(); clerr != nil { 121 err = errors.Annotatef(err, "error closing state: %v", clerr) 122 } 123 return err 124 } 125 st.hostPorts = hostPorts 126 127 st.facadeVersions = make(map[string][]int, len(facades)) 128 for _, facade := range facades { 129 st.facadeVersions[facade.Name] = facade.Versions 130 } 131 132 st.setLoggedIn() 133 return nil 134 } 135 136 // slideAddressToFront moves the address at the location (serverIndex, addrIndex) to be 137 // the first address of the first server. 138 func slideAddressToFront(servers [][]network.HostPort, serverIndex, addrIndex int) { 139 server := servers[serverIndex] 140 hostPort := server[addrIndex] 141 // Move the matching address to be the first in this server 142 for ; addrIndex > 0; addrIndex-- { 143 server[addrIndex] = server[addrIndex-1] 144 } 145 server[0] = hostPort 146 for ; serverIndex > 0; serverIndex-- { 147 servers[serverIndex] = servers[serverIndex-1] 148 } 149 servers[0] = server 150 } 151 152 // addAddress appends a new server derived from the given 153 // address to servers if the address is not already found 154 // there. 155 func addAddress(servers [][]network.HostPort, addr string) ([][]network.HostPort, error) { 156 for i, server := range servers { 157 for j, hostPort := range server { 158 if hostPort.NetAddr() == addr { 159 slideAddressToFront(servers, i, j) 160 return servers, nil 161 } 162 } 163 } 164 host, portString, err := net.SplitHostPort(addr) 165 if err != nil { 166 return nil, err 167 } 168 port, err := strconv.Atoi(portString) 169 if err != nil { 170 return nil, err 171 } 172 result := make([][]network.HostPort, 0, len(servers)+1) 173 result = append(result, network.NewHostPorts(port, host)) 174 result = append(result, servers...) 175 return result, nil 176 } 177 178 // Client returns an object that can be used 179 // to access client-specific functionality. 180 func (st *state) Client() *Client { 181 frontend, backend := base.NewClientFacade(st, "Client") 182 return &Client{ClientFacade: frontend, facade: backend, st: st} 183 } 184 185 // UnitAssigner returns a version of the state that provides functionality 186 // required by the unitassigner worker. 187 func (st *state) UnitAssigner() unitassigner.API { 188 return unitassigner.New(st) 189 } 190 191 // Provisioner returns a version of the state that provides functionality 192 // required by the provisioner worker. 193 func (st *state) Provisioner() *provisioner.State { 194 return provisioner.NewState(st) 195 } 196 197 // Uniter returns a version of the state that provides functionality 198 // required by the uniter worker. 199 func (st *state) Uniter() (*uniter.State, error) { 200 unitTag, ok := st.authTag.(names.UnitTag) 201 if !ok { 202 return nil, errors.Errorf("expected UnitTag, got %T %v", st.authTag, st.authTag) 203 } 204 return uniter.NewState(st, unitTag), nil 205 } 206 207 // Firewaller returns a version of the state that provides functionality 208 // required by the firewaller worker. 209 func (st *state) Firewaller() *firewaller.State { 210 return firewaller.NewState(st) 211 } 212 213 // Upgrader returns access to the Upgrader API 214 func (st *state) Upgrader() *upgrader.State { 215 return upgrader.NewState(st) 216 } 217 218 // Reboot returns access to the Reboot API 219 func (st *state) Reboot() (reboot.State, error) { 220 switch tag := st.authTag.(type) { 221 case names.MachineTag: 222 return reboot.NewState(st, tag), nil 223 default: 224 return nil, errors.Errorf("expected names.MachineTag, got %T", tag) 225 } 226 } 227 228 // Addresser returns access to the Addresser API. 229 func (st *state) Addresser() *addresser.API { 230 return addresser.NewAPI(st) 231 } 232 233 // DiscoverSpaces returns access to the DiscoverSpacesAPI. 234 func (st *state) DiscoverSpaces() *discoverspaces.API { 235 return discoverspaces.NewAPI(st) 236 } 237 238 // KeyUpdater returns access to the KeyUpdater API 239 func (st *state) KeyUpdater() *keyupdater.State { 240 return keyupdater.NewState(st) 241 } 242 243 // InstancePoller returns access to the InstancePoller API 244 func (st *state) InstancePoller() *instancepoller.API { 245 return instancepoller.NewAPI(st) 246 } 247 248 // CharmRevisionUpdater returns access to the CharmRevisionUpdater API 249 func (st *state) CharmRevisionUpdater() *charmrevisionupdater.State { 250 return charmrevisionupdater.NewState(st) 251 } 252 253 // Cleaner returns a version of the state that provides access to the cleaner API 254 func (st *state) Cleaner() *cleaner.API { 255 return cleaner.NewAPI(st) 256 } 257 258 // ServerVersion holds the version of the API server that we are connected to. 259 // It is possible that this version is Zero if the server does not report this 260 // during login. The second result argument indicates if the version number is 261 // set. 262 func (st *state) ServerVersion() (version.Number, bool) { 263 return st.serverVersion, st.serverVersion != version.Zero 264 } 265 266 // MetadataUpdater returns access to the imageMetadata API 267 func (st *state) MetadataUpdater() *imagemetadata.Client { 268 return imagemetadata.NewClient(st) 269 }