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