github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/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/deployer"
    17  	"github.com/juju/juju/api/diskformatter"
    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/keyupdater"
    22  	apilogger "github.com/juju/juju/api/logger"
    23  	"github.com/juju/juju/api/machiner"
    24  	"github.com/juju/juju/api/networker"
    25  	"github.com/juju/juju/api/provisioner"
    26  	"github.com/juju/juju/api/reboot"
    27  	"github.com/juju/juju/api/rsyslog"
    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  // Subsequent requests on the state will act as that entity.  This
    36  // 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, password, nonce string) error {
    39  	err := st.loginV1(tag, password, nonce)
    40  	if params.IsCodeNotImplemented(err) {
    41  		// TODO (cmars): remove fallback once we can drop v0 compatibility
    42  		return st.loginV0(tag, password, nonce)
    43  	}
    44  	return err
    45  }
    46  
    47  func (st *State) loginV1(tag, password, nonce string) error {
    48  	var result struct {
    49  		// TODO (cmars): remove once we can drop 1.18 login compatibility
    50  		params.LoginResult
    51  
    52  		params.LoginResultV1
    53  	}
    54  	err := st.APICall("Admin", 1, "", "Login", &params.LoginRequestCompat{
    55  		LoginRequest: params.LoginRequest{
    56  			AuthTag:     tag,
    57  			Credentials: password,
    58  			Nonce:       nonce,
    59  		},
    60  		// TODO (cmars): remove once we can drop 1.18 login compatibility
    61  		Creds: params.Creds{
    62  			AuthTag:  tag,
    63  			Password: password,
    64  			Nonce:    nonce,
    65  		},
    66  	}, &result)
    67  	if err != nil {
    68  		return err
    69  	}
    70  
    71  	// We've either logged into an Admin v1 facade, or a pre-facade (1.18) API
    72  	// server.  The JSON field names between the structures are disjoint, so only
    73  	// one should have an environ tag set.
    74  
    75  	var environTag string
    76  	var servers [][]network.HostPort
    77  	var facades []params.FacadeVersions
    78  	if result.LoginResult.EnvironTag != "" {
    79  		environTag = result.LoginResult.EnvironTag
    80  		servers = result.LoginResult.Servers
    81  		facades = result.LoginResult.Facades
    82  	} else if result.LoginResultV1.EnvironTag != "" {
    83  		environTag = result.LoginResultV1.EnvironTag
    84  		servers = result.LoginResultV1.Servers
    85  		facades = result.LoginResultV1.Facades
    86  	}
    87  
    88  	err = st.setLoginResult(tag, environTag, servers, facades)
    89  	if err != nil {
    90  		return err
    91  	}
    92  	return nil
    93  }
    94  
    95  func (st *State) setLoginResult(tag, environTag string, servers [][]network.HostPort, facades []params.FacadeVersions) error {
    96  	authtag, err := names.ParseTag(tag)
    97  	if err != nil {
    98  		return err
    99  	}
   100  	st.authTag = authtag
   101  	st.environTag = environTag
   102  
   103  	hostPorts, err := addAddress(servers, st.addr)
   104  	if err != nil {
   105  		if clerr := st.Close(); clerr != nil {
   106  			err = errors.Annotatef(err, "error closing state: %v", clerr)
   107  		}
   108  		return err
   109  	}
   110  	st.hostPorts = hostPorts
   111  
   112  	st.facadeVersions = make(map[string][]int, len(facades))
   113  	for _, facade := range facades {
   114  		st.facadeVersions[facade.Name] = facade.Versions
   115  	}
   116  	return nil
   117  }
   118  
   119  func (st *State) loginV0(tag, password, nonce string) error {
   120  	var result params.LoginResult
   121  	err := st.APICall("Admin", 0, "", "Login", &params.Creds{
   122  		AuthTag:  tag,
   123  		Password: password,
   124  		Nonce:    nonce,
   125  	}, &result)
   126  	if err != nil {
   127  		return err
   128  	}
   129  	if err = st.setLoginResult(tag, result.EnvironTag, result.Servers, result.Facades); err != nil {
   130  		return err
   131  	}
   132  	return nil
   133  }
   134  
   135  // slideAddressToFront moves the address at the location (serverIndex, addrIndex) to be
   136  // the first address of the first server.
   137  func slideAddressToFront(servers [][]network.HostPort, serverIndex, addrIndex int) {
   138  	server := servers[serverIndex]
   139  	hostPort := server[addrIndex]
   140  	// Move the matching address to be the first in this server
   141  	for ; addrIndex > 0; addrIndex-- {
   142  		server[addrIndex] = server[addrIndex-1]
   143  	}
   144  	server[0] = hostPort
   145  	for ; serverIndex > 0; serverIndex-- {
   146  		servers[serverIndex] = servers[serverIndex-1]
   147  	}
   148  	servers[0] = server
   149  }
   150  
   151  // addAddress appends a new server derived from the given
   152  // address to servers if the address is not already found
   153  // there.
   154  func addAddress(servers [][]network.HostPort, addr string) ([][]network.HostPort, error) {
   155  	for i, server := range servers {
   156  		for j, hostPort := range server {
   157  			if hostPort.NetAddr() == addr {
   158  				slideAddressToFront(servers, i, j)
   159  				return servers, nil
   160  			}
   161  		}
   162  	}
   163  	host, portString, err := net.SplitHostPort(addr)
   164  	if err != nil {
   165  		return nil, err
   166  	}
   167  	port, err := strconv.Atoi(portString)
   168  	if err != nil {
   169  		return nil, err
   170  	}
   171  	hostPort := network.HostPort{
   172  		Address: network.NewAddress(host, network.ScopeUnknown),
   173  		Port:    port,
   174  	}
   175  	result := make([][]network.HostPort, 0, len(servers)+1)
   176  	result = append(result, []network.HostPort{hostPort})
   177  	result = append(result, servers...)
   178  	return result, nil
   179  }
   180  
   181  // Client returns an object that can be used
   182  // to access client-specific functionality.
   183  func (st *State) Client() *Client {
   184  	frontend, backend := base.NewClientFacade(st, "Client")
   185  	return &Client{ClientFacade: frontend, facade: backend, st: st}
   186  }
   187  
   188  // Machiner returns a version of the state that provides functionality
   189  // required by the machiner worker.
   190  func (st *State) Machiner() *machiner.State {
   191  	return machiner.NewState(st)
   192  }
   193  
   194  // Networker returns a version of the state that provides functionality
   195  // required by the networker worker.
   196  func (st *State) Networker() *networker.State {
   197  	return networker.NewState(st)
   198  }
   199  
   200  // Provisioner returns a version of the state that provides functionality
   201  // required by the provisioner worker.
   202  func (st *State) Provisioner() *provisioner.State {
   203  	return provisioner.NewState(st)
   204  }
   205  
   206  // Uniter returns a version of the state that provides functionality
   207  // required by the uniter worker.
   208  func (st *State) Uniter() (*uniter.State, error) {
   209  	unitTag, ok := st.authTag.(names.UnitTag)
   210  	if !ok {
   211  		return nil, errors.Errorf("expected UnitTag, got %T %v", st.authTag, st.authTag)
   212  	}
   213  	return uniter.NewState(st, unitTag), nil
   214  }
   215  
   216  // DiskManager returns a version of the state that provides functionality
   217  // required by the diskmanager worker.
   218  func (st *State) DiskManager() (*diskmanager.State, error) {
   219  	machineTag, ok := st.authTag.(names.MachineTag)
   220  	if !ok {
   221  		return nil, errors.Errorf("expected MachineTag, got %#v", st.authTag)
   222  	}
   223  	return diskmanager.NewState(st, machineTag), nil
   224  }
   225  
   226  // DiskFormatter returns a version of the state that provides functionality
   227  // required by the diskformatter worker.
   228  func (st *State) DiskFormatter() (*diskformatter.State, error) {
   229  	unitTag, ok := st.authTag.(names.UnitTag)
   230  	if !ok {
   231  		return nil, errors.Errorf("expected UnitTag, got %#v", st.authTag)
   232  	}
   233  	return diskformatter.NewState(st, unitTag), nil
   234  }
   235  
   236  // Firewaller returns a version of the state that provides functionality
   237  // required by the firewaller worker.
   238  func (st *State) Firewaller() *firewaller.State {
   239  	return firewaller.NewState(st)
   240  }
   241  
   242  // Agent returns a version of the state that provides
   243  // functionality required by the agent code.
   244  func (st *State) Agent() *agent.State {
   245  	return agent.NewState(st)
   246  }
   247  
   248  // Upgrader returns access to the Upgrader API
   249  func (st *State) Upgrader() *upgrader.State {
   250  	return upgrader.NewState(st)
   251  }
   252  
   253  // Reboot returns access to the Reboot API
   254  func (st *State) Reboot() (*reboot.State, error) {
   255  	switch tag := st.authTag.(type) {
   256  	case names.MachineTag:
   257  		return reboot.NewState(st, tag), nil
   258  	default:
   259  		return nil, errors.Errorf("expected names.MachineTag, got %T", tag)
   260  	}
   261  }
   262  
   263  // Deployer returns access to the Deployer API
   264  func (st *State) Deployer() *deployer.State {
   265  	return deployer.NewState(st)
   266  }
   267  
   268  // Environment returns access to the Environment API
   269  func (st *State) Environment() *environment.Facade {
   270  	return environment.NewFacade(st)
   271  }
   272  
   273  // Logger returns access to the Logger API
   274  func (st *State) Logger() *apilogger.State {
   275  	return apilogger.NewState(st)
   276  }
   277  
   278  // KeyUpdater returns access to the KeyUpdater API
   279  func (st *State) KeyUpdater() *keyupdater.State {
   280  	return keyupdater.NewState(st)
   281  }
   282  
   283  // CharmRevisionUpdater returns access to the CharmRevisionUpdater API
   284  func (st *State) CharmRevisionUpdater() *charmrevisionupdater.State {
   285  	return charmrevisionupdater.NewState(st)
   286  }
   287  
   288  // Rsyslog returns access to the Rsyslog API
   289  func (st *State) Rsyslog() *rsyslog.State {
   290  	return rsyslog.NewState(st)
   291  }