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 }