github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/agentconfigupdater/manifold.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package agentconfigupdater
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/pubsub/v2"
     9  	"github.com/juju/worker/v3"
    10  	"github.com/juju/worker/v3/dependency"
    11  
    12  	coreagent "github.com/juju/juju/agent"
    13  	apiagent "github.com/juju/juju/api/agent/agent"
    14  	"github.com/juju/juju/api/base"
    15  	"github.com/juju/juju/mongo"
    16  	jworker "github.com/juju/juju/worker"
    17  )
    18  
    19  // Logger defines the logging methods used by the worker.
    20  type Logger interface {
    21  	Criticalf(string, ...interface{})
    22  	Warningf(string, ...interface{})
    23  	Infof(string, ...interface{})
    24  	Debugf(string, ...interface{})
    25  	Tracef(string, ...interface{})
    26  }
    27  
    28  // ManifoldConfig provides the dependencies for the
    29  // agent config updater manifold.
    30  type ManifoldConfig struct {
    31  	AgentName      string
    32  	APICallerName  string
    33  	CentralHubName string
    34  	Logger         Logger
    35  }
    36  
    37  // Manifold defines a simple start function which
    38  // runs after the API connection has come up. If the machine agent is
    39  // a controller, it grabs the state serving info over the API and
    40  // records it to agent configuration, and then stops.
    41  func Manifold(config ManifoldConfig) dependency.Manifold {
    42  	return dependency.Manifold{
    43  		Inputs: []string{
    44  			config.AgentName,
    45  			config.APICallerName,
    46  			config.CentralHubName,
    47  		},
    48  		Start: func(context dependency.Context) (worker.Worker, error) {
    49  			// Get the agent.
    50  			var agent coreagent.Agent
    51  			if err := context.Get(config.AgentName, &agent); err != nil {
    52  				return nil, err
    53  			}
    54  
    55  			// Grab the tag and ensure that it's for a controller.
    56  			tag := agent.CurrentConfig().Tag()
    57  			if !apiagent.IsAllowedControllerTag(tag.Kind()) {
    58  				return nil, errors.New("agent's tag is not a machine or controller agent tag")
    59  			}
    60  
    61  			// Get API connection.
    62  			var apiCaller base.APICaller
    63  			if err := context.Get(config.APICallerName, &apiCaller); err != nil {
    64  				return nil, err
    65  			}
    66  			// If the machine needs State, grab the state serving info
    67  			// over the API and write it to the agent configuration.
    68  			if controller, err := apiagent.IsController(apiCaller, tag); err != nil {
    69  				return nil, errors.Annotate(err, "checking controller status")
    70  			} else if !controller {
    71  				// Not a controller, nothing to do.
    72  				return nil, dependency.ErrUninstall
    73  			}
    74  
    75  			// Do the initial state serving info and mongo profile checks
    76  			// before attempting to get the central hub. The central hub is only
    77  			// running when the agent is a controller. If the agent isn't a controller
    78  			// but should be, the agent config will not have any state serving info
    79  			// but the database will think that we should be. In those situations
    80  			// we need to update the local config and restart.
    81  			apiState, err := apiagent.NewState(apiCaller)
    82  			if err != nil {
    83  				return nil, errors.Trace(err)
    84  			}
    85  			controllerConfig, err := apiState.ControllerConfig()
    86  			if err != nil {
    87  				return nil, errors.Annotate(err, "getting controller config")
    88  			}
    89  
    90  			logger := config.Logger
    91  
    92  			// If the mongo memory profile from the controller config
    93  			// is different from the one in the agent config we need to
    94  			// restart the agent to apply the memory profile to the mongo
    95  			// service.
    96  			agentsMongoMemoryProfile := agent.CurrentConfig().MongoMemoryProfile()
    97  			configMongoMemoryProfile := mongo.MemoryProfile(controllerConfig.MongoMemoryProfile())
    98  			mongoProfileChanged := agentsMongoMemoryProfile != configMongoMemoryProfile
    99  
   100  			agentsJujuDBSnapChannel := agent.CurrentConfig().JujuDBSnapChannel()
   101  			configJujuDBSnapChannel := controllerConfig.JujuDBSnapChannel()
   102  			jujuDBSnapChannelChanged := agentsJujuDBSnapChannel != configJujuDBSnapChannel
   103  
   104  			agentsQueryTracingEnabled := agent.CurrentConfig().QueryTracingEnabled()
   105  			configQueryTracingEnabled := controllerConfig.QueryTracingEnabled()
   106  			queryTracingEnabledChanged := agentsQueryTracingEnabled != configQueryTracingEnabled
   107  
   108  			agentsQueryTracingThreshold := agent.CurrentConfig().QueryTracingThreshold()
   109  			configQueryTracingThreshold := controllerConfig.QueryTracingThreshold()
   110  			queryTracingThresholdChanged := agentsQueryTracingThreshold != configQueryTracingThreshold
   111  
   112  			info, err := apiState.StateServingInfo()
   113  			if err != nil {
   114  				return nil, errors.Annotate(err, "getting state serving info")
   115  			}
   116  			err = agent.ChangeConfig(func(config coreagent.ConfigSetter) error {
   117  				existing, hasInfo := config.StateServingInfo()
   118  				if hasInfo {
   119  					// Use the existing cert and key as they appear to
   120  					// have been already updated by the cert updater
   121  					// worker to have this machine's IP address as
   122  					// part of the cert. This changed cert is never
   123  					// put back into the database, so it isn't
   124  					// reflected in the copy we have got from
   125  					// apiState.
   126  					info.Cert = existing.Cert
   127  					info.PrivateKey = existing.PrivateKey
   128  				}
   129  				config.SetStateServingInfo(info)
   130  				if mongoProfileChanged {
   131  					logger.Debugf("setting agent config mongo memory profile: %q => %q", agentsMongoMemoryProfile, configMongoMemoryProfile)
   132  					config.SetMongoMemoryProfile(configMongoMemoryProfile)
   133  				}
   134  				if jujuDBSnapChannelChanged {
   135  					logger.Debugf("setting agent config mongo snap channel: %q => %q", agentsJujuDBSnapChannel, configJujuDBSnapChannel)
   136  					config.SetJujuDBSnapChannel(configJujuDBSnapChannel)
   137  				}
   138  				if queryTracingEnabledChanged {
   139  					logger.Debugf("setting agent config query tracing enabled: %t => %t", agentsQueryTracingEnabled, configQueryTracingEnabled)
   140  					config.SetQueryTracingEnabled(configQueryTracingEnabled)
   141  				}
   142  				if queryTracingThresholdChanged {
   143  					logger.Debugf("setting agent config query tracing threshold: %d => %d", agentsQueryTracingThreshold, configQueryTracingThreshold)
   144  					config.SetQueryTracingThreshold(configQueryTracingThreshold)
   145  				}
   146  
   147  				return nil
   148  			})
   149  			if err != nil {
   150  				return nil, errors.Trace(err)
   151  			}
   152  
   153  			// If we need a restart, return the fatal error.
   154  			if mongoProfileChanged {
   155  				logger.Infof("restarting agent for new mongo memory profile")
   156  				return nil, jworker.ErrRestartAgent
   157  			} else if jujuDBSnapChannelChanged {
   158  				logger.Infof("restarting agent for new mongo snap channel")
   159  				return nil, jworker.ErrRestartAgent
   160  			} else if queryTracingEnabledChanged {
   161  				logger.Infof("restarting agent for new query tracing enabled")
   162  				return nil, jworker.ErrRestartAgent
   163  			} else if queryTracingThresholdChanged {
   164  				logger.Infof("restarting agent for new query tracing threshold")
   165  				return nil, jworker.ErrRestartAgent
   166  			}
   167  
   168  			// Only get the hub if we are a controller and we haven't updated
   169  			// the memory profile.
   170  			var hub *pubsub.StructuredHub
   171  			if err := context.Get(config.CentralHubName, &hub); err != nil {
   172  				logger.Tracef("hub dependency not available")
   173  				return nil, err
   174  			}
   175  
   176  			return NewWorker(WorkerConfig{
   177  				Agent:                 agent,
   178  				Hub:                   hub,
   179  				MongoProfile:          configMongoMemoryProfile,
   180  				JujuDBSnapChannel:     configJujuDBSnapChannel,
   181  				QueryTracingEnabled:   configQueryTracingEnabled,
   182  				QueryTracingThreshold: configQueryTracingThreshold,
   183  				Logger:                config.Logger,
   184  			})
   185  		},
   186  	}
   187  }