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 := &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  	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  }