github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/shared.go (about)

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package apiserver
     5  
     6  import (
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/juju/collections/set"
    11  	"github.com/juju/errors"
    12  	"github.com/juju/loggo"
    13  
    14  	"github.com/juju/juju/apiserver/facade"
    15  	jujucontroller "github.com/juju/juju/controller"
    16  	"github.com/juju/juju/core/cache"
    17  	coredatabase "github.com/juju/juju/core/database"
    18  	"github.com/juju/juju/core/lease"
    19  	"github.com/juju/juju/core/multiwatcher"
    20  	"github.com/juju/juju/core/presence"
    21  	"github.com/juju/juju/pubsub/controller"
    22  	"github.com/juju/juju/state"
    23  )
    24  
    25  // SharedHub represents the methods of the pubsub.StructuredHub
    26  // that are used. The context uses an interface to allow mocking
    27  // of the hub.
    28  type SharedHub interface {
    29  	Publish(topic string, data interface{}) (func(), error)
    30  	Subscribe(topic string, handler interface{}) (func(), error)
    31  }
    32  
    33  // sharedServerContext contains a number of components that are unchangeable in the API server.
    34  // These components need to be exposed through the facade.Context. Instead of having the methods
    35  // of newAPIHandler and newAPIRoot take ever increasing numbers of parameters, they will instead
    36  // have a pointer to the sharedServerContext.
    37  //
    38  // All attributes in the context should be goroutine aware themselves, like the state pool, hub, and
    39  // presence, or protected and only accessed through methods on this context object.
    40  type sharedServerContext struct {
    41  	statePool           *state.StatePool
    42  	controller          *cache.Controller
    43  	multiwatcherFactory multiwatcher.Factory
    44  	centralHub          SharedHub
    45  	presence            presence.Recorder
    46  	leaseManager        lease.Manager
    47  	logger              loggo.Logger
    48  	cancel              <-chan struct{}
    49  	charmhubHTTPClient  facade.HTTPClient
    50  	dbGetter            coredatabase.DBGetter
    51  
    52  	configMutex      sync.RWMutex
    53  	controllerConfig jujucontroller.Config
    54  	features         set.Strings
    55  
    56  	unsubscribe func()
    57  }
    58  
    59  type sharedServerConfig struct {
    60  	statePool           *state.StatePool
    61  	controller          *cache.Controller
    62  	multiwatcherFactory multiwatcher.Factory
    63  	centralHub          SharedHub
    64  	presence            presence.Recorder
    65  	leaseManager        lease.Manager
    66  	controllerConfig    jujucontroller.Config
    67  	logger              loggo.Logger
    68  	charmhubHTTPClient  facade.HTTPClient
    69  	dbGetter            coredatabase.DBGetter
    70  }
    71  
    72  func (c *sharedServerConfig) validate() error {
    73  	if c.statePool == nil {
    74  		return errors.NotValidf("nil statePool")
    75  	}
    76  	if c.controller == nil {
    77  		return errors.NotValidf("nil controller")
    78  	}
    79  	if c.multiwatcherFactory == nil {
    80  		return errors.NotValidf("nil multiwatcherFactory")
    81  	}
    82  	if c.centralHub == nil {
    83  		return errors.NotValidf("nil centralHub")
    84  	}
    85  	if c.presence == nil {
    86  		return errors.NotValidf("nil presence")
    87  	}
    88  	if c.leaseManager == nil {
    89  		return errors.NotValidf("nil leaseManager")
    90  	}
    91  	if c.controllerConfig == nil {
    92  		return errors.NotValidf("nil controllerConfig")
    93  	}
    94  	if c.dbGetter == nil {
    95  		return errors.NotValidf("nil dbGetter")
    96  	}
    97  	return nil
    98  }
    99  
   100  func newSharedServerContext(config sharedServerConfig) (*sharedServerContext, error) {
   101  	if err := config.validate(); err != nil {
   102  		return nil, errors.Trace(err)
   103  	}
   104  	ctx := &sharedServerContext{
   105  		statePool:           config.statePool,
   106  		controller:          config.controller,
   107  		multiwatcherFactory: config.multiwatcherFactory,
   108  		centralHub:          config.centralHub,
   109  		presence:            config.presence,
   110  		leaseManager:        config.leaseManager,
   111  		logger:              config.logger,
   112  		controllerConfig:    config.controllerConfig,
   113  		charmhubHTTPClient:  config.charmhubHTTPClient,
   114  		dbGetter:            config.dbGetter,
   115  	}
   116  	ctx.features = config.controllerConfig.Features()
   117  	// We are able to get the current controller config before subscribing to changes
   118  	// because the changes are only ever published in response to an API call, and
   119  	// this function is called in the newServer call to create the API server,
   120  	// and we know that we can't make any API calls until the server has started.
   121  	unsubscribe, err := ctx.centralHub.Subscribe(controller.ConfigChanged, ctx.onConfigChanged)
   122  	if err != nil {
   123  		ctx.logger.Criticalf("programming error in subscribe function: %v", err)
   124  		return nil, errors.Trace(err)
   125  	}
   126  	ctx.unsubscribe = unsubscribe
   127  	return ctx, nil
   128  }
   129  
   130  func (c *sharedServerContext) Close() {
   131  	c.unsubscribe()
   132  }
   133  
   134  func (c *sharedServerContext) onConfigChanged(topic string, data controller.ConfigChangedMessage, err error) {
   135  	if err != nil {
   136  		c.logger.Criticalf("programming error in %s message data: %v", topic, err)
   137  		return
   138  	}
   139  
   140  	features := data.Config.Features()
   141  
   142  	c.configMutex.Lock()
   143  	c.controllerConfig = data.Config
   144  	removed := c.features.Difference(features)
   145  	added := features.Difference(c.features)
   146  	c.features = features
   147  	values := features.SortedValues()
   148  	c.configMutex.Unlock()
   149  
   150  	if removed.Size() != 0 || added.Size() != 0 {
   151  		c.logger.Infof("updating features to %v", values)
   152  	}
   153  }
   154  
   155  func (c *sharedServerContext) featureEnabled(flag string) bool {
   156  	c.configMutex.RLock()
   157  	defer c.configMutex.RUnlock()
   158  	return c.features.Contains(flag)
   159  }
   160  
   161  func (c *sharedServerContext) maxDebugLogDuration() time.Duration {
   162  	c.configMutex.RLock()
   163  	defer c.configMutex.RUnlock()
   164  	return c.controllerConfig.MaxDebugLogDuration()
   165  }