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