github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/jujud/agent/unit.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package agent 5 6 import ( 7 "time" 8 9 "github.com/juju/clock" 10 "github.com/juju/cmd" 11 "github.com/juju/errors" 12 "github.com/juju/gnuflag" 13 "github.com/juju/loggo" 14 "github.com/juju/utils/voyeur" 15 "github.com/prometheus/client_golang/prometheus" 16 "gopkg.in/juju/names.v2" 17 "gopkg.in/juju/worker.v1" 18 "gopkg.in/juju/worker.v1/dependency" 19 "gopkg.in/natefinch/lumberjack.v2" 20 21 "github.com/juju/juju/agent" 22 "github.com/juju/juju/api/base" 23 "github.com/juju/juju/api/uniter" 24 jujucmd "github.com/juju/juju/cmd" 25 "github.com/juju/juju/cmd/jujud/agent/unit" 26 cmdutil "github.com/juju/juju/cmd/jujud/util" 27 "github.com/juju/juju/core/machinelock" 28 "github.com/juju/juju/upgrades" 29 jworker "github.com/juju/juju/worker" 30 "github.com/juju/juju/worker/gate" 31 "github.com/juju/juju/worker/introspection" 32 "github.com/juju/juju/worker/logsender" 33 "github.com/juju/juju/worker/upgradesteps" 34 ) 35 36 var ( 37 // should be an explicit dependency, can't do it cleanly yet 38 unitManifolds = unit.Manifolds 39 ) 40 41 // UnitAgent is a cmd.Command responsible for running a unit agent. 42 type UnitAgent struct { 43 cmd.CommandBase 44 AgentConf 45 configChangedVal *voyeur.Value 46 UnitName string 47 runner *worker.Runner 48 bufferedLogger *logsender.BufferedLogWriter 49 setupLogging func(agent.Config) error 50 logToStdErr bool 51 ctx *cmd.Context 52 dead chan struct{} 53 errReason error 54 55 // Used to signal that the upgrade worker will not 56 // reboot the agent on startup because there are no 57 // longer any immediately pending agent upgrades. 58 initialUpgradeCheckComplete gate.Lock 59 preUpgradeSteps upgrades.PreUpgradeStepsFunc 60 upgradeComplete gate.Lock 61 62 prometheusRegistry *prometheus.Registry 63 } 64 65 // NewUnitAgent creates a new UnitAgent value properly initialized. 66 func NewUnitAgent(ctx *cmd.Context, bufferedLogger *logsender.BufferedLogWriter) (*UnitAgent, error) { 67 prometheusRegistry, err := newPrometheusRegistry() 68 if err != nil { 69 return nil, errors.Trace(err) 70 } 71 return &UnitAgent{ 72 AgentConf: NewAgentConf(""), 73 configChangedVal: voyeur.NewValue(true), 74 ctx: ctx, 75 dead: make(chan struct{}), 76 initialUpgradeCheckComplete: gate.NewLock(), 77 bufferedLogger: bufferedLogger, 78 prometheusRegistry: prometheusRegistry, 79 preUpgradeSteps: upgrades.PreUpgradeSteps, 80 }, nil 81 } 82 83 // Info returns usage information for the command. 84 func (a *UnitAgent) Info() *cmd.Info { 85 return jujucmd.Info(&cmd.Info{ 86 Name: "unit", 87 Purpose: "run a juju unit agent", 88 }) 89 } 90 91 func (a *UnitAgent) SetFlags(f *gnuflag.FlagSet) { 92 a.AgentConf.AddFlags(f) 93 f.StringVar(&a.UnitName, "unit-name", "", "name of the unit to run") 94 f.BoolVar(&a.logToStdErr, "log-to-stderr", false, "whether to log to standard error instead of log files") 95 } 96 97 // Init initializes the command for running. 98 func (a *UnitAgent) Init(args []string) error { 99 if a.UnitName == "" { 100 return cmdutil.RequiredError("unit-name") 101 } 102 if !names.IsValidUnit(a.UnitName) { 103 return errors.Errorf(`--unit-name option expects "<application>/<n>" argument`) 104 } 105 if err := a.AgentConf.CheckArgs(args); err != nil { 106 return err 107 } 108 a.runner = worker.NewRunner(worker.RunnerParams{ 109 IsFatal: cmdutil.IsFatal, 110 MoreImportant: cmdutil.MoreImportant, 111 RestartDelay: jworker.RestartDelay, 112 }) 113 114 if err := a.ReadConfig(a.Tag().String()); err != nil { 115 return err 116 } 117 agentConfig := a.CurrentConfig() 118 119 if !a.logToStdErr { 120 121 // the writer in ctx.stderr gets set as the loggo writer in github.com/juju/cmd/logging.go 122 a.ctx.Stderr = &lumberjack.Logger{ 123 Filename: agent.LogFilename(agentConfig), 124 MaxSize: 300, // megabytes 125 MaxBackups: 2, 126 Compress: true, 127 } 128 129 } 130 131 return nil 132 } 133 134 // Stop stops the unit agent. 135 func (a *UnitAgent) Stop() error { 136 a.runner.Kill() 137 return a.Wait() 138 } 139 140 // Wait waits for the unit agent to finish 141 func (a *UnitAgent) Wait() error { 142 <-a.dead 143 return a.errReason 144 } 145 146 // Done signals the unit agent is finished 147 func (a *UnitAgent) Done(err error) { 148 a.errReason = err 149 close(a.dead) 150 } 151 152 func (a *UnitAgent) isUpgradeRunning() bool { 153 return !a.upgradeComplete.IsUnlocked() 154 } 155 156 func (a *UnitAgent) isInitialUpgradeCheckPending() bool { 157 return !a.initialUpgradeCheckComplete.IsUnlocked() 158 } 159 160 // Run runs a unit agent. 161 func (a *UnitAgent) Run(ctx *cmd.Context) (err error) { 162 defer a.Done(err) 163 if err := a.ReadConfig(a.Tag().String()); err != nil { 164 return err 165 } 166 setupAgentLogging(a.CurrentConfig()) 167 168 a.runner.StartWorker("api", a.APIWorkers) 169 err = cmdutil.AgentDone(logger, a.runner.Wait()) 170 return err 171 } 172 173 // APIWorkers returns a dependency.Engine running the unit agent's responsibilities. 174 func (a *UnitAgent) APIWorkers() (worker.Worker, error) { 175 updateAgentConfLogging := func(loggingConfig string) error { 176 return a.AgentConf.ChangeConfig(func(setter agent.ConfigSetter) error { 177 setter.SetLoggingConfig(loggingConfig) 178 return nil 179 }) 180 } 181 182 agentConfig := a.AgentConf.CurrentConfig() 183 a.upgradeComplete = upgradesteps.NewLock(agentConfig) 184 machineLock, err := machinelock.New(machinelock.Config{ 185 AgentName: a.Tag().String(), 186 Clock: clock.WallClock, 187 Logger: loggo.GetLogger("juju.machinelock"), 188 LogFilename: agent.MachineLockLogFilename(agentConfig), 189 }) 190 // There will only be an error if the required configuration 191 // values are not passed in. 192 if err != nil { 193 return nil, errors.Trace(err) 194 } 195 196 manifolds := unitManifolds(unit.ManifoldsConfig{ 197 Agent: agent.APIHostPortsSetter{a}, 198 LogSource: a.bufferedLogger.Logs(), 199 LeadershipGuarantee: 30 * time.Second, 200 AgentConfigChanged: a.configChangedVal, 201 ValidateMigration: a.validateMigration, 202 PrometheusRegisterer: a.prometheusRegistry, 203 UpdateLoggerConfig: updateAgentConfLogging, 204 PreviousAgentVersion: agentConfig.UpgradedToVersion(), 205 PreUpgradeSteps: a.preUpgradeSteps, 206 UpgradeStepsLock: a.upgradeComplete, 207 UpgradeCheckLock: a.initialUpgradeCheckComplete, 208 MachineLock: machineLock, 209 }) 210 211 engine, err := dependency.NewEngine(dependencyEngineConfig()) 212 if err != nil { 213 return nil, err 214 } 215 if err := dependency.Install(engine, manifolds); err != nil { 216 if err := worker.Stop(engine); err != nil { 217 logger.Errorf("while stopping engine with bad manifolds: %v", err) 218 } 219 return nil, err 220 } 221 if err := startIntrospection(introspectionConfig{ 222 Agent: a, 223 Engine: engine, 224 NewSocketName: DefaultIntrospectionSocketName, 225 PrometheusGatherer: a.prometheusRegistry, 226 MachineLock: machineLock, 227 WorkerFunc: introspection.NewWorker, 228 }); err != nil { 229 // If the introspection worker failed to start, we just log error 230 // but continue. It is very unlikely to happen in the real world 231 // as the only issue is connecting to the abstract domain socket 232 // and the agent is controlled by by the OS to only have one. 233 logger.Errorf("failed to start introspection worker: %v", err) 234 } 235 return engine, nil 236 } 237 238 func (a *UnitAgent) Tag() names.Tag { 239 return names.NewUnitTag(a.UnitName) 240 } 241 242 func (a *UnitAgent) ChangeConfig(mutate agent.ConfigMutator) error { 243 err := a.AgentConf.ChangeConfig(mutate) 244 a.configChangedVal.Set(true) 245 return errors.Trace(err) 246 } 247 248 // validateMigration is called by the migrationminion to help check 249 // that the agent will be ok when connected to a new controller. 250 func (a *UnitAgent) validateMigration(apiCaller base.APICaller) error { 251 // TODO(mjs) - more extensive checks to come. 252 unitTag := names.NewUnitTag(a.UnitName) 253 facade := uniter.NewState(apiCaller, unitTag) 254 _, err := facade.Unit(unitTag) 255 if err != nil { 256 return errors.Trace(err) 257 } 258 model, err := facade.Model() 259 if err != nil { 260 return errors.Trace(err) 261 } 262 curModelUUID := a.CurrentConfig().Model().Id() 263 newModelUUID := model.UUID 264 if newModelUUID != curModelUUID { 265 return errors.Errorf("model mismatch when validating: got %q, expected %q", 266 newModelUUID, curModelUUID) 267 } 268 return nil 269 }