github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/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 := &params.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  	if result.UserInfo != nil {
   102  		// This was a macaroon based user authentication.
   103  		tag, err = names.ParseTag(result.UserInfo.Identity)
   104  		if err != nil {
   105  			return errors.Trace(err)
   106  		}
   107  	}
   108  	servers := params.NetworkHostsPorts(result.Servers)
   109  	err = st.setLoginResult(tag, result.ModelTag, result.ControllerTag, servers, result.Facades)
   110  	if err != nil {
   111  		return errors.Trace(err)
   112  	}
   113  	st.serverVersion, err = version.Parse(result.ServerVersion)
   114  	if err != nil {
   115  		return errors.Trace(err)
   116  	}
   117  	return nil
   118  }
   119  
   120  func (st *state) setLoginResult(tag names.Tag, modelTag, controllerTag string, servers [][]network.HostPort, facades []params.FacadeVersions) error {
   121  	st.authTag = tag
   122  	st.modelTag = modelTag
   123  	st.controllerTag = controllerTag
   124  
   125  	hostPorts, err := addAddress(servers, st.addr)
   126  	if err != nil {
   127  		if clerr := st.Close(); clerr != nil {
   128  			err = errors.Annotatef(err, "error closing state: %v", clerr)
   129  		}
   130  		return err
   131  	}
   132  	st.hostPorts = hostPorts
   133  
   134  	st.facadeVersions = make(map[string][]int, len(facades))
   135  	for _, facade := range facades {
   136  		st.facadeVersions[facade.Name] = facade.Versions
   137  	}
   138  
   139  	st.setLoggedIn()
   140  	return nil
   141  }
   142  
   143  // AuthTag returns the tag of the authorized user of the state API connection.
   144  func (st *state) AuthTag() names.Tag {
   145  	return st.authTag
   146  }
   147  
   148  // slideAddressToFront moves the address at the location (serverIndex, addrIndex) to be
   149  // the first address of the first server.
   150  func slideAddressToFront(servers [][]network.HostPort, serverIndex, addrIndex int) {
   151  	server := servers[serverIndex]
   152  	hostPort := server[addrIndex]
   153  	// Move the matching address to be the first in this server
   154  	for ; addrIndex > 0; addrIndex-- {
   155  		server[addrIndex] = server[addrIndex-1]
   156  	}
   157  	server[0] = hostPort
   158  	for ; serverIndex > 0; serverIndex-- {
   159  		servers[serverIndex] = servers[serverIndex-1]
   160  	}
   161  	servers[0] = server
   162  }
   163  
   164  // addAddress appends a new server derived from the given
   165  // address to servers if the address is not already found
   166  // there.
   167  func addAddress(servers [][]network.HostPort, addr string) ([][]network.HostPort, error) {
   168  	for i, server := range servers {
   169  		for j, hostPort := range server {
   170  			if hostPort.NetAddr() == addr {
   171  				slideAddressToFront(servers, i, j)
   172  				return servers, nil
   173  			}
   174  		}
   175  	}
   176  	host, portString, err := net.SplitHostPort(addr)
   177  	if err != nil {
   178  		return nil, err
   179  	}
   180  	port, err := strconv.Atoi(portString)
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  	result := make([][]network.HostPort, 0, len(servers)+1)
   185  	result = append(result, network.NewHostPorts(port, host))
   186  	result = append(result, servers...)
   187  	return result, nil
   188  }
   189  
   190  // Client returns an object that can be used
   191  // to access client-specific functionality.
   192  func (st *state) Client() *Client {
   193  	frontend, backend := base.NewClientFacade(st, "Client")
   194  	return &Client{ClientFacade: frontend, facade: backend, st: st}
   195  }
   196  
   197  // UnitAssigner returns a version of the state that provides functionality
   198  // required by the unitassigner worker.
   199  func (st *state) UnitAssigner() unitassigner.API {
   200  	return unitassigner.New(st)
   201  }
   202  
   203  // Provisioner returns a version of the state that provides functionality
   204  // required by the provisioner worker.
   205  func (st *state) Provisioner() *provisioner.State {
   206  	return provisioner.NewState(st)
   207  }
   208  
   209  // Uniter returns a version of the state that provides functionality
   210  // required by the uniter worker.
   211  func (st *state) Uniter() (*uniter.State, error) {
   212  	unitTag, ok := st.authTag.(names.UnitTag)
   213  	if !ok {
   214  		return nil, errors.Errorf("expected UnitTag, got %T %v", st.authTag, st.authTag)
   215  	}
   216  	return uniter.NewState(st, unitTag), nil
   217  }
   218  
   219  // Firewaller returns a version of the state that provides functionality
   220  // required by the firewaller worker.
   221  func (st *state) Firewaller() *firewaller.State {
   222  	return firewaller.NewState(st)
   223  }
   224  
   225  // Upgrader returns access to the Upgrader API
   226  func (st *state) Upgrader() *upgrader.State {
   227  	return upgrader.NewState(st)
   228  }
   229  
   230  // Reboot returns access to the Reboot API
   231  func (st *state) Reboot() (reboot.State, error) {
   232  	switch tag := st.authTag.(type) {
   233  	case names.MachineTag:
   234  		return reboot.NewState(st, tag), nil
   235  	default:
   236  		return nil, errors.Errorf("expected names.MachineTag, got %T", tag)
   237  	}
   238  }
   239  
   240  // Addresser returns access to the Addresser API.
   241  func (st *state) Addresser() *addresser.API {
   242  	return addresser.NewAPI(st)
   243  }
   244  
   245  // DiscoverSpaces returns access to the DiscoverSpacesAPI.
   246  func (st *state) DiscoverSpaces() *discoverspaces.API {
   247  	return discoverspaces.NewAPI(st)
   248  }
   249  
   250  // KeyUpdater returns access to the KeyUpdater API
   251  func (st *state) KeyUpdater() *keyupdater.State {
   252  	return keyupdater.NewState(st)
   253  }
   254  
   255  // InstancePoller returns access to the InstancePoller API
   256  func (st *state) InstancePoller() *instancepoller.API {
   257  	return instancepoller.NewAPI(st)
   258  }
   259  
   260  // CharmRevisionUpdater returns access to the CharmRevisionUpdater API
   261  func (st *state) CharmRevisionUpdater() *charmrevisionupdater.State {
   262  	return charmrevisionupdater.NewState(st)
   263  }
   264  
   265  // Cleaner returns a version of the state that provides access to the cleaner API
   266  func (st *state) Cleaner() *cleaner.API {
   267  	return cleaner.NewAPI(st)
   268  }
   269  
   270  // ServerVersion holds the version of the API server that we are connected to.
   271  // It is possible that this version is Zero if the server does not report this
   272  // during login. The second result argument indicates if the version number is
   273  // set.
   274  func (st *state) ServerVersion() (version.Number, bool) {
   275  	return st.serverVersion, st.serverVersion != version.Zero
   276  }
   277  
   278  // MetadataUpdater returns access to the imageMetadata API
   279  func (st *state) MetadataUpdater() *imagemetadata.Client {
   280  	return imagemetadata.NewClient(st)
   281  }