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

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package uniter
     5  
     6  import (
     7  	"github.com/juju/clock"
     8  	"github.com/juju/errors"
     9  	"github.com/juju/loggo"
    10  	"github.com/juju/names/v5"
    11  	"github.com/juju/worker/v3"
    12  	"github.com/juju/worker/v3/dependency"
    13  
    14  	"github.com/juju/juju/agent"
    15  	"github.com/juju/juju/api"
    16  	"github.com/juju/juju/api/agent/secretsmanager"
    17  	"github.com/juju/juju/api/agent/uniter"
    18  	"github.com/juju/juju/api/client/charms"
    19  	"github.com/juju/juju/core/leadership"
    20  	"github.com/juju/juju/core/machinelock"
    21  	"github.com/juju/juju/core/model"
    22  	"github.com/juju/juju/internal/s3client"
    23  	"github.com/juju/juju/observability/probe"
    24  	"github.com/juju/juju/rpc/params"
    25  	"github.com/juju/juju/secrets"
    26  	"github.com/juju/juju/worker/common/reboot"
    27  	"github.com/juju/juju/worker/fortress"
    28  	"github.com/juju/juju/worker/secretexpire"
    29  	"github.com/juju/juju/worker/secretrotate"
    30  	"github.com/juju/juju/worker/uniter/charm"
    31  	"github.com/juju/juju/worker/uniter/operation"
    32  	"github.com/juju/juju/worker/uniter/resolver"
    33  	"github.com/juju/juju/worker/uniter/runner"
    34  )
    35  
    36  // Logger represents the methods used for logging messages.
    37  type Logger interface {
    38  	Errorf(string, ...interface{})
    39  	Warningf(string, ...interface{})
    40  	Infof(string, ...interface{})
    41  	Debugf(string, ...interface{})
    42  	Tracef(string, ...interface{})
    43  	IsTraceEnabled() bool
    44  
    45  	Child(string) loggo.Logger
    46  	ChildWithLabels(name string, labels ...string) loggo.Logger
    47  }
    48  
    49  // ManifoldConfig defines the names of the manifolds on which a
    50  // Manifold will depend.
    51  type ManifoldConfig struct {
    52  	AgentName                    string
    53  	ModelType                    model.ModelType
    54  	APICallerName                string
    55  	S3CallerName                 string
    56  	MachineLock                  machinelock.Lock
    57  	Clock                        clock.Clock
    58  	LeadershipTrackerName        string
    59  	CharmDirName                 string
    60  	HookRetryStrategyName        string
    61  	TranslateResolverErr         func(error) error
    62  	Logger                       Logger
    63  	Sidecar                      bool
    64  	EnforcedCharmModifiedVersion int
    65  	ContainerNames               []string
    66  }
    67  
    68  // Validate ensures all the required values for the config are set.
    69  func (config *ManifoldConfig) Validate() error {
    70  	if config.Clock == nil {
    71  		return errors.NotValidf("missing Clock")
    72  	}
    73  	if len(config.ModelType) == 0 {
    74  		return errors.NotValidf("missing model type")
    75  	}
    76  	if config.MachineLock == nil {
    77  		return errors.NotValidf("missing MachineLock")
    78  	}
    79  	if config.Logger == nil {
    80  		return errors.NotValidf("missing Logger")
    81  	}
    82  	return nil
    83  }
    84  
    85  // Manifold returns a dependency manifold that runs a uniter worker,
    86  // using the resource names defined in the supplied config.
    87  func Manifold(config ManifoldConfig) dependency.Manifold {
    88  	return dependency.Manifold{
    89  		Inputs: []string{
    90  			config.AgentName,
    91  			config.APICallerName,
    92  			config.S3CallerName,
    93  			config.LeadershipTrackerName,
    94  			config.CharmDirName,
    95  			config.HookRetryStrategyName,
    96  		},
    97  		Start: func(ctx dependency.Context) (worker.Worker, error) {
    98  			if err := config.Validate(); err != nil {
    99  				return nil, errors.Trace(err)
   100  			}
   101  			// Collect all required resources.
   102  			var agent agent.Agent
   103  			if err := ctx.Get(config.AgentName, &agent); err != nil {
   104  				return nil, errors.Trace(err)
   105  			}
   106  			var apiConn api.Connection
   107  			if err := ctx.Get(config.APICallerName, &apiConn); err != nil {
   108  				// TODO(fwereade): absence of an APICaller shouldn't be the end of
   109  				// the world -- we ought to return a type that can at least run the
   110  				// leader-deposed hook -- but that's not done yet.
   111  				return nil, errors.Trace(err)
   112  			}
   113  			var leadershipTracker leadership.TrackerWorker
   114  			if err := ctx.Get(config.LeadershipTrackerName, &leadershipTracker); err != nil {
   115  				return nil, errors.Trace(err)
   116  			}
   117  			leadershipTrackerFunc := func(_ names.UnitTag) leadership.TrackerWorker {
   118  				return leadershipTracker
   119  			}
   120  			var charmDirGuard fortress.Guard
   121  			if err := ctx.Get(config.CharmDirName, &charmDirGuard); err != nil {
   122  				return nil, errors.Trace(err)
   123  			}
   124  
   125  			var hookRetryStrategy params.RetryStrategy
   126  			if err := ctx.Get(config.HookRetryStrategyName, &hookRetryStrategy); err != nil {
   127  				return nil, errors.Trace(err)
   128  			}
   129  
   130  			var s3Caller s3client.Session
   131  			if err := ctx.Get(config.S3CallerName, &s3Caller); err != nil {
   132  				return nil, errors.Trace(err)
   133  			}
   134  
   135  			s3Downloader := charms.NewS3CharmDownloader(s3client.NewCharmsS3Client(s3Caller), apiConn)
   136  
   137  			jujuSecretsAPI := secretsmanager.NewClient(apiConn)
   138  			secretRotateWatcherFunc := func(unitTag names.UnitTag, isLeader bool, rotateSecrets chan []string) (worker.Worker, error) {
   139  				owners := []names.Tag{unitTag}
   140  				if isLeader {
   141  					appName, _ := names.UnitApplication(unitTag.Id())
   142  					owners = append(owners, names.NewApplicationTag(appName))
   143  				}
   144  				return secretrotate.New(secretrotate.Config{
   145  					SecretManagerFacade: jujuSecretsAPI,
   146  					Clock:               config.Clock,
   147  					Logger:              config.Logger.Child("secretsrotate"),
   148  					SecretOwners:        owners,
   149  					RotateSecrets:       rotateSecrets,
   150  				})
   151  			}
   152  			secretExpiryWatcherFunc := func(unitTag names.UnitTag, isLeader bool, expireRevisions chan []string) (worker.Worker, error) {
   153  				owners := []names.Tag{unitTag}
   154  				if isLeader {
   155  					appName, _ := names.UnitApplication(unitTag.Id())
   156  					owners = append(owners, names.NewApplicationTag(appName))
   157  				}
   158  				return secretexpire.New(secretexpire.Config{
   159  					SecretManagerFacade: jujuSecretsAPI,
   160  					Clock:               config.Clock,
   161  					Logger:              config.Logger.Child("secretrevisionsexpire"),
   162  					SecretOwners:        owners,
   163  					ExpireRevisions:     expireRevisions,
   164  				})
   165  			}
   166  
   167  			manifoldConfig := config
   168  			// Configure and start the uniter.
   169  			agentConfig := agent.CurrentConfig()
   170  			tag := agentConfig.Tag()
   171  			unitTag, ok := tag.(names.UnitTag)
   172  			if !ok {
   173  				return nil, errors.Errorf("expected a unit tag, got %v", tag)
   174  			}
   175  			resourcesFacade, err := uniter.NewResourcesFacadeClient(apiConn, unitTag)
   176  			if err != nil {
   177  				return nil, err
   178  			}
   179  			payloadFacade := uniter.NewPayloadFacadeClient(apiConn)
   180  
   181  			secretsBackendGetter := func() (secrets.BackendsClient, error) {
   182  				return secrets.NewClient(jujuSecretsAPI)
   183  			}
   184  			uniter, err := NewUniter(&UniterParams{
   185  				UniterFacade:                 uniter.NewState(apiConn, unitTag),
   186  				ResourcesFacade:              resourcesFacade,
   187  				PayloadFacade:                payloadFacade,
   188  				SecretsClient:                jujuSecretsAPI,
   189  				SecretsBackendGetter:         secretsBackendGetter,
   190  				UnitTag:                      unitTag,
   191  				ModelType:                    config.ModelType,
   192  				LeadershipTrackerFunc:        leadershipTrackerFunc,
   193  				SecretRotateWatcherFunc:      secretRotateWatcherFunc,
   194  				SecretExpiryWatcherFunc:      secretExpiryWatcherFunc,
   195  				DataDir:                      agentConfig.DataDir(),
   196  				Downloader:                   s3Downloader,
   197  				MachineLock:                  manifoldConfig.MachineLock,
   198  				CharmDirGuard:                charmDirGuard,
   199  				UpdateStatusSignal:           NewUpdateStatusTimer(),
   200  				HookRetryStrategy:            hookRetryStrategy,
   201  				NewOperationExecutor:         operation.NewExecutor,
   202  				NewDeployer:                  charm.NewDeployer,
   203  				NewProcessRunner:             runner.NewRunner,
   204  				TranslateResolverErr:         config.TranslateResolverErr,
   205  				Clock:                        manifoldConfig.Clock,
   206  				RebootQuerier:                reboot.NewMonitor(agentConfig.TransientDataDir()),
   207  				Logger:                       config.Logger,
   208  				Sidecar:                      config.Sidecar,
   209  				EnforcedCharmModifiedVersion: config.EnforcedCharmModifiedVersion,
   210  				ContainerNames:               config.ContainerNames,
   211  			})
   212  			if err != nil {
   213  				return nil, errors.Trace(err)
   214  			}
   215  			return uniter, nil
   216  		},
   217  		Output: func(in worker.Worker, out interface{}) error {
   218  			uniter, _ := in.(*Uniter)
   219  			if uniter == nil {
   220  				return errors.Errorf("expected Uniter in")
   221  			}
   222  
   223  			switch outPtr := out.(type) {
   224  			case *probe.ProbeProvider:
   225  				*outPtr = &uniter.Probe
   226  			case **Uniter:
   227  				*outPtr = uniter
   228  			default:
   229  				return errors.Errorf("unknown out type")
   230  			}
   231  			return nil
   232  		},
   233  	}
   234  }
   235  
   236  // TranslateFortressErrors turns errors returned by dependent
   237  // manifolds due to fortress lockdown (i.e. model migration) into an
   238  // error which causes the resolver loop to be restarted. When this
   239  // happens the uniter is about to be shut down anyway.
   240  func TranslateFortressErrors(err error) error {
   241  	if fortress.IsFortressError(err) {
   242  		return resolver.ErrRestart
   243  	}
   244  	return err
   245  }