github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/api/connector/clientstoreconnector.go (about)

     1  // Copyright 2022 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package connector
     5  
     6  import (
     7  	"errors"
     8  
     9  	"github.com/go-macaroon-bakery/macaroon-bakery/v3/httpbakery"
    10  
    11  	"github.com/juju/juju/api"
    12  	"github.com/juju/juju/juju"
    13  	"github.com/juju/juju/jujuclient"
    14  )
    15  
    16  var ErrEmptyControllerName = errors.New("empty controller name")
    17  
    18  // A Connector can provie api.Connection instances based on a ClientStore
    19  type ClientStoreConnector struct {
    20  	config          ClientStoreConfig
    21  	defaultDialOpts api.DialOpts
    22  }
    23  
    24  var _ Connector = (*ClientStoreConnector)(nil)
    25  
    26  // ClientStoreConfig provides configuration for a Connector.
    27  type ClientStoreConfig struct {
    28  	// Controller to connect to.  Required
    29  	ControllerName string
    30  
    31  	// Model to connect to.  Optional
    32  	ModelUUID string
    33  
    34  	// Defaults to the file client store
    35  	ClientStore jujuclient.ClientStore
    36  
    37  	// Defaults to the user for the controller
    38  	AccountDetails *jujuclient.AccountDetails
    39  }
    40  
    41  // NewClientStore returns a new *ClientStoreConnector instance for the given config, or an error if
    42  // there was a problem setting up the connector.
    43  func NewClientStore(config ClientStoreConfig, dialOptions ...api.DialOption) (*ClientStoreConnector, error) {
    44  	if config.ControllerName == "" {
    45  		return nil, ErrEmptyControllerName
    46  	}
    47  	if config.ClientStore == nil {
    48  		config.ClientStore = jujuclient.NewFileClientStore()
    49  	}
    50  	if config.AccountDetails == nil {
    51  		d, err := config.ClientStore.AccountDetails(config.ControllerName)
    52  		if err != nil {
    53  			return nil, err
    54  		}
    55  		config.AccountDetails = d
    56  	}
    57  	conn := &ClientStoreConnector{
    58  		config:          config,
    59  		defaultDialOpts: api.DefaultDialOpts(),
    60  	}
    61  	for _, opt := range dialOptions {
    62  		opt(&conn.defaultDialOpts)
    63  	}
    64  	return conn, nil
    65  }
    66  
    67  // Connect returns an api.Connection to the controller / model specified in c's
    68  // config, or an error if there was a problem opening the connection.
    69  func (c *ClientStoreConnector) Connect(dialOptions ...api.DialOption) (api.Connection, error) {
    70  	opts := c.defaultDialOpts
    71  	for _, f := range dialOptions {
    72  		f(&opts)
    73  	}
    74  
    75  	// By default there is no bakery client in the dial options, so we reproduce
    76  	// behaviour that is scattered around the code to obtain a bakery client
    77  	// with a cookie jar from the client store.
    78  	jar, err := c.config.ClientStore.CookieJar(c.config.ControllerName)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  
    83  	bakeryClient := httpbakery.NewClient()
    84  	bakeryClient.Jar = jar
    85  	opts.BakeryClient = bakeryClient
    86  
    87  	return juju.NewAPIConnection(juju.NewAPIConnectionParams{
    88  		ControllerName: c.config.ControllerName,
    89  		Store:          c.config.ClientStore,
    90  		OpenAPI:        api.Open,
    91  		DialOpts:       opts,
    92  		AccountDetails: c.config.AccountDetails,
    93  		ModelUUID:      c.config.ModelUUID,
    94  	})
    95  }