Exemple #1
0
func (s *AccountsSuite) TestUpdateAccountIgnoresEmptyAccess(c *gc.C) {
	testAccountDetails := jujuclient.AccountDetails{
		User:     "******",
		Password: "******",
	}
	err := s.store.UpdateAccount("ctrl", testAccountDetails)
	c.Assert(err, jc.ErrorIsNil)
	details, err := s.store.AccountDetails("ctrl")
	c.Assert(err, jc.ErrorIsNil)
	testAccountDetails.LastKnownAccess = ctrlAdminAccountDetails.LastKnownAccess
	c.Assert(testAccountDetails.LastKnownAccess, gc.Equals, "superuser")
	c.Assert(*details, jc.DeepEquals, testAccountDetails)
}
Exemple #2
0
// NewAPIConnection returns an api.Connection to the specified Juju controller,
// with specified account credentials, optionally scoped to the specified model
// name.
func NewAPIConnection(args NewAPIConnectionParams) (api.Connection, error) {
	apiInfo, controller, err := connectionInfo(args)
	if err != nil {
		return nil, errors.Annotatef(err, "cannot work out how to connect")
	}
	if len(apiInfo.Addrs) == 0 {
		return nil, errors.New("no API addresses")
	}
	logger.Infof("connecting to API addresses: %v", apiInfo.Addrs)
	st, err := args.OpenAPI(apiInfo, args.DialOpts)
	if err != nil {
		redirErr, ok := errors.Cause(err).(*api.RedirectError)
		if !ok {
			return nil, errors.Trace(err)
		}
		// We've been told to connect to a different API server,
		// so do so. Note that we don't copy the account details
		// because the account on the redirected server may well
		// be different - we'll use macaroon authentication
		// directly without sending account details.
		// Copy the API info because it's possible that the
		// apiConfigConnect is still using it concurrently.
		apiInfo = &api.Info{
			ModelTag: apiInfo.ModelTag,
			Addrs:    network.HostPortsToStrings(usableHostPorts(redirErr.Servers)),
			CACert:   redirErr.CACert,
		}
		st, err = args.OpenAPI(apiInfo, args.DialOpts)
		if err != nil {
			return nil, errors.Annotatef(err, "cannot connect to redirected address")
		}
		// TODO(rog) update cached model addresses.
		// TODO(rog) should we do something with the logged-in username?
		return st, nil
	}
	addrConnectedTo, err := serverAddress(st.Addr())
	if err != nil {
		return nil, errors.Trace(err)
	}
	// Update API addresses if they've changed. Error is non-fatal.
	// Note that in the redirection case, we won't update the addresses
	// of the controller we first connected to. This shouldn't be
	// a problem in practice because the intended scenario for
	// controllers that redirect involves them having well known
	// public addresses that won't change over time.
	hostPorts := st.APIHostPorts()
	agentVersion := ""
	if v, ok := st.ServerVersion(); ok {
		agentVersion = v.String()
	}
	params := UpdateControllerParams{
		AgentVersion:     agentVersion,
		AddrConnectedTo:  []network.HostPort{addrConnectedTo},
		CurrentHostPorts: hostPorts,
	}
	err = updateControllerDetailsFromLogin(args.Store, args.ControllerName, controller, params)
	if err != nil {
		logger.Errorf("cannot cache API addresses: %v", err)
	}

	// Process the account details obtained from login.
	var accountDetails *jujuclient.AccountDetails
	user, ok := st.AuthTag().(names.UserTag)
	if !apiInfo.SkipLogin {
		if ok {
			if accountDetails, err = args.Store.AccountDetails(args.ControllerName); err != nil {
				if !errors.IsNotFound(err) {
					logger.Errorf("cannot load local account information: %v", err)
				}
			} else {
				accountDetails.LastKnownAccess = st.ControllerAccess()
			}
		}
		if ok && !user.IsLocal() && apiInfo.Tag == nil {
			// We used macaroon auth to login; save the username
			// that we've logged in as.
			accountDetails = &jujuclient.AccountDetails{
				User:            user.Canonical(),
				LastKnownAccess: st.ControllerAccess(),
			}
		} else if apiInfo.Tag == nil {
			logger.Errorf("unexpected logged-in username %v", st.AuthTag())
		}
	}
	if accountDetails != nil {
		if err := args.Store.UpdateAccount(args.ControllerName, *accountDetails); err != nil {
			logger.Errorf("cannot update account information: %v", err)
		}
	}
	return st, nil
}