func NewAPIConnection(args NewAPIConnectionParams) (_ api.Connection, err error) {
	if args.OpenAPI == nil {
		args.OpenAPI = api.Open
	}
	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, errNoAddresses
	}

	// Copy the cache so we'll know whether it's changed so that
	// we'll update the entry correctly.
	dnsCache := dnsCacheMap(controller.DNSCache).copy()
	args.DialOpts.DNSCache = dnsCache
	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 || !redirErr.FollowRedirect {
			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:          usableHostPorts(redirErr.Servers).Strings(),
			CACert:         redirErr.CACert,
			ControllerUUID: apiInfo.ControllerUUID,
		}
		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
	}
	defer func() {
		if err != nil {
			_ = st.Close()
		}
	}()

	// 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:   st.Addr(),
		IPAddrConnectedTo: st.IPAddr(),
		CurrentHostPorts:  hostPorts,
		DNSCache:          dnsCache,
	}
	if host := st.PublicDNSName(); host != "" {
		params.PublicDNSName = &host
	}
	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.Id(),
				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
}
// NewAPIConnection returns an api.Connection to the specified Juju controller,
// with specified account credentials, optionally scoped to the specified model
// name.
NewAPIConnection returns an api.Connection to the specified Juju controller,
with specified account credentials, optionally scoped to the specified model
name.
// 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, err error) {
	if args.OpenAPI == nil {
		args.OpenAPI = api.Open
	}
	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, errNoAddresses
	}
	dnsCache := dnsCacheMap(controller.DNSCache).copy()
	args.DialOpts.DNSCache = dnsCache
	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 || !redirErr.FollowRedirect {
			return nil, errors.Trace(err)
		}
		apiInfo = &api.Info{
			ModelTag:       apiInfo.ModelTag,
			Addrs:          usableHostPorts(redirErr.Servers).Strings(),
			CACert:         redirErr.CACert,
			ControllerUUID: apiInfo.ControllerUUID,
		}
		st, err = args.OpenAPI(apiInfo, args.DialOpts)
		if err != nil {
			return nil, errors.Annotatef(err, "cannot connect to redirected address")
		}
		return st, nil
	}
	defer func() {
		if err != nil {
			_ = st.Close()
		}
	}()
	hostPorts := st.APIHostPorts()
	agentVersion := ""
	if v, ok := st.ServerVersion(); ok {
		agentVersion = v.String()
	}
	params := UpdateControllerParams{
		AgentVersion:      agentVersion,
		AddrConnectedTo:   st.Addr(),
		IPAddrConnectedTo: st.IPAddr(),
		CurrentHostPorts:  hostPorts,
		DNSCache:          dnsCache,
	}
	if host := st.PublicDNSName(); host != "" {
		params.PublicDNSName = &host
	}
	err = updateControllerDetailsFromLogin(args.Store, args.ControllerName, controller, params)
	if err != nil {
		logger.Errorf("cannot cache API addresses: %v", err)
	}
	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 {
			accountDetails = &jujuclient.AccountDetails{
				User:            user.Id(),
				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
}
func NewAPIConnection ( args NewAPIConnectionParams ) ( _ api . Connection , err error ) { if args . OpenAPI == nil { args . OpenAPI = api . Open } apiInfo , controller , err := connectionInfo ( args ) if err != nil { return nil , errors . Annotatef ( err , " " ) } if len ( apiInfo . Addrs ) == 0 { return nil , errNoAddresses } dnsCache := dnsCacheMap ( controller . DNSCache ) . copy ( ) args . DialOpts . DNSCache = dnsCache logger . Infof ( " " , apiInfo . Addrs ) st , err := args . OpenAPI ( apiInfo , args . DialOpts ) if err != nil { redirErr , ok := errors . Cause ( err ) . ( * api . RedirectError ) if ! ok || ! redirErr . FollowRedirect { return nil , errors . Trace ( err ) } apiInfo = & api . Info { ModelTag : apiInfo . ModelTag , Addrs : usableHostPorts ( redirErr . Servers ) . Strings ( ) , CACert : redirErr . CACert , ControllerUUID : apiInfo . ControllerUUID , } st , err = args . OpenAPI ( apiInfo , args . DialOpts ) if err != nil { return nil , errors . Annotatef ( err , " " ) } return st , nil } defer func ( ) { if err != nil { _ = st . Close ( ) } } ( ) hostPorts := st . APIHostPorts ( ) agentVersion := " " if v , ok := st . ServerVersion ( ) ; ok { agentVersion = v . String ( ) } params := UpdateControllerParams { AgentVersion : agentVersion , AddrConnectedTo : st . Addr ( ) , IPAddrConnectedTo : st . IPAddr ( ) , CurrentHostPorts : hostPorts , DNSCache : dnsCache , } if host := st . PublicDNSName ( ) ; host != " " { params . PublicDNSName = & host } err = updateControllerDetailsFromLogin ( args . Store , args . ControllerName , controller , params ) if err != nil { logger . Errorf ( " " , err ) } 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 ( " " , err ) } } else { accountDetails . LastKnownAccess = st . ControllerAccess ( ) } } if ok && ! user . IsLocal ( ) && apiInfo . Tag == nil { accountDetails = & jujuclient . AccountDetails { User : user . Id ( ) , LastKnownAccess : st . ControllerAccess ( ) , } } else if apiInfo . Tag == nil { logger . Errorf ( " " , st . AuthTag ( ) ) } } if accountDetails != nil { if err := args . Store . UpdateAccount ( args . ControllerName , * accountDetails ) ; err != nil { logger . Errorf ( " " , err ) } } return st , nil }
NewAPIConnection returns an api.Connection to the specified Juju controller,
with specified account credentials, optionally scoped to the specified model
name.
NewAPIConnection returns an api . Connection to the specified Juju controller with specified account credentials optionally scoped to the specified model name .
// Copy the cache so we'll know whether it's changed so that // we'll update the entry correctly. // 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. // TODO(rog) update cached model addresses. // TODO(rog) should we do something with the logged-in username? // 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. // Process the account details obtained from login. // We used macaroon auth to login; save the username // that we've logged in as.