github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/caasmodeloperator/modeloperator.go (about)

     1  // Copyright 2020 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package caasmodeloperator
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/names/v5"
     9  	"github.com/juju/utils/v3"
    10  	"github.com/juju/version/v2"
    11  	"github.com/juju/worker/v3/catacomb"
    12  
    13  	"github.com/juju/juju/agent"
    14  	"github.com/juju/juju/api/controller/caasmodeloperator"
    15  	"github.com/juju/juju/caas"
    16  	"github.com/juju/juju/core/watcher"
    17  )
    18  
    19  type ModelOperatorAPI interface {
    20  	SetPassword(password string) error
    21  	ModelOperatorProvisioningInfo() (caasmodeloperator.ModelOperatorProvisioningInfo, error)
    22  	WatchModelOperatorProvisioningInfo() (watcher.NotifyWatcher, error)
    23  }
    24  
    25  // ModelOperatorBroker describes the caas broker interface needed for installing
    26  // a ModelOperator into Kubernetes
    27  type ModelOperatorBroker interface {
    28  	EnsureModelOperator(string, string, *caas.ModelOperatorConfig) error
    29  	ModelOperator() (*caas.ModelOperatorConfig, error)
    30  	ModelOperatorExists() (bool, error)
    31  }
    32  
    33  // ModelOperatorManager defines the worker used for managing model operators in
    34  // caas
    35  type ModelOperatorManager struct {
    36  	agentConfig agent.Config
    37  	api         ModelOperatorAPI
    38  	broker      ModelOperatorBroker
    39  	catacomb    catacomb.Catacomb
    40  	logger      Logger
    41  	modelUUID   string
    42  }
    43  
    44  const (
    45  	// DefaultModelOperatorPort is the default port used for the api server on
    46  	// the model operator
    47  	DefaultModelOperatorPort = 17071
    48  )
    49  
    50  // Kill implements worker kill method
    51  func (m *ModelOperatorManager) Kill() {
    52  	m.catacomb.Kill(nil)
    53  }
    54  
    55  // Wait implements worker Wait method
    56  func (m *ModelOperatorManager) Wait() error {
    57  	return m.catacomb.Wait()
    58  }
    59  
    60  func (m *ModelOperatorManager) loop() error {
    61  	watcher, err := m.api.WatchModelOperatorProvisioningInfo()
    62  	if err != nil {
    63  		return errors.Annotate(err, "cannot watch model operator provisioning info")
    64  	}
    65  	err = m.catacomb.Add(watcher)
    66  	if err != nil {
    67  		return errors.Trace(err)
    68  	}
    69  
    70  	for {
    71  		select {
    72  		case <-m.catacomb.Dying():
    73  			return m.catacomb.ErrDying()
    74  		case <-watcher.Changes():
    75  			err := m.update()
    76  			if err != nil {
    77  				return errors.Annotate(err, "failed to update model operator")
    78  			}
    79  		}
    80  	}
    81  }
    82  
    83  func (m *ModelOperatorManager) update() error {
    84  	m.logger.Debugf("gathering model operator provisioning information for model %s", m.modelUUID)
    85  	info, err := m.api.ModelOperatorProvisioningInfo()
    86  	if err != nil {
    87  		return errors.Trace(err)
    88  	}
    89  
    90  	exists, err := m.broker.ModelOperatorExists()
    91  	if err != nil {
    92  		return errors.Trace(err)
    93  	}
    94  
    95  	setPassword := true
    96  	password, err := utils.RandomPassword()
    97  	if err != nil {
    98  		return errors.Trace(err)
    99  	}
   100  	if exists {
   101  		mo, err := m.broker.ModelOperator()
   102  		if err != nil {
   103  			return errors.Trace(err)
   104  		}
   105  		prevConf, err := agent.ParseConfigData(mo.AgentConf)
   106  		if err != nil {
   107  			return errors.Annotate(err, "cannot parse model operator agent config: ")
   108  		}
   109  		// reuse old password
   110  		if prevInfo, ok := prevConf.APIInfo(); ok && prevInfo.Password != "" {
   111  			password = prevInfo.Password
   112  			setPassword = false
   113  		} else if prevConf.OldPassword() != "" {
   114  			password = prevConf.OldPassword()
   115  			setPassword = false
   116  		}
   117  	}
   118  	if setPassword {
   119  		err := m.api.SetPassword(password)
   120  		if err != nil {
   121  			return errors.Annotate(err, "failed to set model api passwords")
   122  		}
   123  	}
   124  
   125  	agentConf, err := m.updateAgentConf(info.APIAddresses, password, info.Version)
   126  	if err != nil {
   127  		return errors.Trace(err)
   128  	}
   129  	agentConfBuf, err := agentConf.Render()
   130  	if err != nil {
   131  		return errors.Trace(err)
   132  	}
   133  
   134  	m.logger.Debugf("ensuring model operator deployment in kubernetes for model %s", m.modelUUID)
   135  	err = m.broker.EnsureModelOperator(
   136  		m.modelUUID,
   137  		m.agentConfig.DataDir(),
   138  		&caas.ModelOperatorConfig{
   139  			AgentConf:    agentConfBuf,
   140  			ImageDetails: info.ImageDetails,
   141  			Port:         DefaultModelOperatorPort,
   142  		},
   143  	)
   144  	if err != nil {
   145  		return errors.Annotate(err, "deploying model operator")
   146  	}
   147  
   148  	return nil
   149  }
   150  
   151  // NewModelOperatorManager constructs a new model operator manager worker
   152  func NewModelOperatorManager(
   153  	logger Logger,
   154  	api ModelOperatorAPI,
   155  	broker ModelOperatorBroker,
   156  	modelUUID string,
   157  	agentConfig agent.Config,
   158  ) (*ModelOperatorManager, error) {
   159  	m := &ModelOperatorManager{
   160  		agentConfig: agentConfig,
   161  		api:         api,
   162  		broker:      broker,
   163  		logger:      logger,
   164  		modelUUID:   modelUUID,
   165  	}
   166  
   167  	if err := catacomb.Invoke(catacomb.Plan{
   168  		Site: &m.catacomb,
   169  		Work: m.loop,
   170  	}); err != nil {
   171  		return m, errors.Trace(err)
   172  	}
   173  
   174  	return m, nil
   175  }
   176  
   177  func (m *ModelOperatorManager) updateAgentConf(
   178  	apiAddresses []string,
   179  	password string,
   180  	ver version.Number,
   181  ) (agent.ConfigSetterWriter, error) {
   182  	modelTag := names.NewModelTag(m.modelUUID)
   183  	conf, err := agent.NewAgentConfig(
   184  		agent.AgentConfigParams{
   185  			Paths: agent.Paths{
   186  				DataDir: m.agentConfig.DataDir(),
   187  				LogDir:  m.agentConfig.LogDir(),
   188  			},
   189  			Tag:          modelTag,
   190  			Controller:   m.agentConfig.Controller(),
   191  			Model:        modelTag,
   192  			APIAddresses: apiAddresses,
   193  			CACert:       m.agentConfig.CACert(),
   194  			Password:     password,
   195  
   196  			// UpgradedToVersion is mandatory but not used by
   197  			// caas operator agents as they are not upgraded insitu.
   198  			UpgradedToVersion: ver,
   199  		},
   200  	)
   201  	if err != nil {
   202  		return nil, errors.Annotatef(err, "creating new agent config")
   203  	}
   204  
   205  	return conf, nil
   206  }