github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/client/charms/services/repofactory.go (about)

     1  // Copyright 2021 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package services
     5  
     6  import (
     7  	"sync"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/loggo"
    11  
    12  	"github.com/juju/juju/charmhub"
    13  	corecharm "github.com/juju/juju/core/charm"
    14  	charmrepo "github.com/juju/juju/core/charm/repository"
    15  	corelogger "github.com/juju/juju/core/logger"
    16  )
    17  
    18  // CharmRepoFactoryConfig encapsulates the information required for creating a
    19  // new CharmRepoFactory instance.
    20  type CharmRepoFactoryConfig struct {
    21  	// The logger to use.
    22  	Logger loggo.Logger
    23  
    24  	// An HTTP client that is injected when making Charmhub API calls.
    25  	CharmhubHTTPClient charmhub.HTTPClient
    26  
    27  	StateBackend StateBackend
    28  	ModelBackend ModelBackend
    29  }
    30  
    31  // CharmRepoFactory instantitates charm repositories. It memoizes created
    32  // repositories allowing them to be reused by subsequent GetCharmRepository
    33  // calls.
    34  type CharmRepoFactory struct {
    35  	logger             loggo.Logger
    36  	charmhubHTTPClient charmhub.HTTPClient
    37  	stateBackend       StateBackend
    38  	modelBackend       ModelBackend
    39  
    40  	mu            sync.Mutex
    41  	memoizedRepos map[corecharm.Source]corecharm.Repository
    42  }
    43  
    44  // NewCharmRepoFactory returns a new factory instance with the provided configuration.
    45  func NewCharmRepoFactory(cfg CharmRepoFactoryConfig) *CharmRepoFactory {
    46  	return &CharmRepoFactory{
    47  		logger:             cfg.Logger,
    48  		charmhubHTTPClient: cfg.CharmhubHTTPClient,
    49  		stateBackend:       cfg.StateBackend,
    50  		modelBackend:       cfg.ModelBackend,
    51  		memoizedRepos:      make(map[corecharm.Source]corecharm.Repository),
    52  	}
    53  }
    54  
    55  // GetCharmRepository returns a suitable corecharm.Repository instance for the
    56  // requested source. Lookups are memoized for future requests.
    57  func (f *CharmRepoFactory) GetCharmRepository(src corecharm.Source) (corecharm.Repository, error) {
    58  	f.mu.Lock()
    59  	defer f.mu.Unlock()
    60  
    61  	if repo, isCached := f.memoizedRepos[src]; isCached {
    62  		return repo, nil
    63  	}
    64  
    65  	var repo corecharm.Repository
    66  
    67  	switch src {
    68  	case corecharm.CharmHub:
    69  		cfg, err := f.modelBackend.Config()
    70  		if err != nil {
    71  			return nil, errors.Trace(err)
    72  		}
    73  		chURL, _ := cfg.CharmHubURL()
    74  		chClient, err := charmhub.NewClient(charmhub.Config{
    75  			URL:        chURL,
    76  			HTTPClient: f.charmhubHTTPClient,
    77  			Logger:     f.logger.Child("charmhubrepo"),
    78  		})
    79  		if err != nil {
    80  			return nil, errors.Trace(err)
    81  		}
    82  
    83  		repo = charmrepo.NewCharmHubRepository(
    84  			f.logger.ChildWithLabels("charmhubrepo", corelogger.CHARMHUB),
    85  			chClient,
    86  		)
    87  	default:
    88  		return nil, errors.NotSupportedf("charm repository for source %q", src)
    89  	}
    90  
    91  	// Memoize for future lookups.
    92  	f.memoizedRepos[src] = repo
    93  	return repo, nil
    94  }