github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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 "runtime" 8 "time" 9 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/featureflag" 15 "github.com/juju/utils/voyeur" 16 "gopkg.in/juju/names.v2" 17 "gopkg.in/natefinch/lumberjack.v2" 18 "gopkg.in/tomb.v1" 19 20 "github.com/juju/juju/agent" 21 "github.com/juju/juju/api/base" 22 "github.com/juju/juju/api/uniter" 23 "github.com/juju/juju/cmd/jujud/agent/unit" 24 cmdutil "github.com/juju/juju/cmd/jujud/util" 25 jujuversion "github.com/juju/juju/version" 26 "github.com/juju/juju/worker" 27 "github.com/juju/juju/worker/dependency" 28 "github.com/juju/juju/worker/introspection" 29 "github.com/juju/juju/worker/logsender" 30 ) 31 32 var ( 33 agentLogger = loggo.GetLogger("juju.jujud") 34 35 // should be an explicit dependency, can't do it cleanly yet 36 unitManifolds = unit.Manifolds 37 ) 38 39 // UnitAgent is a cmd.Command responsible for running a unit agent. 40 type UnitAgent struct { 41 cmd.CommandBase 42 tomb tomb.Tomb 43 AgentConf 44 configChangedVal *voyeur.Value 45 UnitName string 46 runner worker.Runner 47 bufferedLogs logsender.LogRecordCh 48 setupLogging func(agent.Config) error 49 logToStdErr bool 50 ctx *cmd.Context 51 52 // Used to signal that the upgrade worker will not 53 // reboot the agent on startup because there are no 54 // longer any immediately pending agent upgrades. 55 // Channel used as a selectable bool (closed means true). 56 initialUpgradeCheckComplete chan struct{} 57 } 58 59 // NewUnitAgent creates a new UnitAgent value properly initialized. 60 func NewUnitAgent(ctx *cmd.Context, bufferedLogs logsender.LogRecordCh) *UnitAgent { 61 return &UnitAgent{ 62 AgentConf: NewAgentConf(""), 63 configChangedVal: voyeur.NewValue(true), 64 ctx: ctx, 65 initialUpgradeCheckComplete: make(chan struct{}), 66 bufferedLogs: bufferedLogs, 67 } 68 } 69 70 // Info returns usage information for the command. 71 func (a *UnitAgent) Info() *cmd.Info { 72 return &cmd.Info{ 73 Name: "unit", 74 Purpose: "run a juju unit agent", 75 } 76 } 77 78 func (a *UnitAgent) SetFlags(f *gnuflag.FlagSet) { 79 a.AgentConf.AddFlags(f) 80 f.StringVar(&a.UnitName, "unit-name", "", "name of the unit to run") 81 f.BoolVar(&a.logToStdErr, "log-to-stderr", false, "whether to log to standard error instead of log files") 82 } 83 84 // Init initializes the command for running. 85 func (a *UnitAgent) Init(args []string) error { 86 if a.UnitName == "" { 87 return cmdutil.RequiredError("unit-name") 88 } 89 if !names.IsValidUnit(a.UnitName) { 90 return errors.Errorf(`--unit-name option expects "<service>/<n>" argument`) 91 } 92 if err := a.AgentConf.CheckArgs(args); err != nil { 93 return err 94 } 95 a.runner = worker.NewRunner(cmdutil.IsFatal, cmdutil.MoreImportant, worker.RestartDelay) 96 97 if !a.logToStdErr { 98 if err := a.ReadConfig(a.Tag().String()); err != nil { 99 return err 100 } 101 agentConfig := a.CurrentConfig() 102 103 // the writer in ctx.stderr gets set as the loggo writer in github.com/juju/cmd/logging.go 104 a.ctx.Stderr = &lumberjack.Logger{ 105 Filename: agent.LogFilename(agentConfig), 106 MaxSize: 300, // megabytes 107 MaxBackups: 2, 108 } 109 110 } 111 112 return nil 113 } 114 115 // Stop stops the unit agent. 116 func (a *UnitAgent) Stop() error { 117 a.runner.Kill() 118 return a.tomb.Wait() 119 } 120 121 // Run runs a unit agent. 122 func (a *UnitAgent) Run(ctx *cmd.Context) error { 123 defer a.tomb.Done() 124 if err := a.ReadConfig(a.Tag().String()); err != nil { 125 return err 126 } 127 agentLogger.Infof("unit agent %v start (%s [%s])", a.Tag().String(), jujuversion.Current, runtime.Compiler) 128 if flags := featureflag.String(); flags != "" { 129 logger.Warningf("developer feature flags enabled: %s", flags) 130 } 131 132 a.runner.StartWorker("api", a.APIWorkers) 133 err := cmdutil.AgentDone(logger, a.runner.Wait()) 134 a.tomb.Kill(err) 135 return err 136 } 137 138 // APIWorkers returns a dependency.Engine running the unit agent's responsibilities. 139 func (a *UnitAgent) APIWorkers() (worker.Worker, error) { 140 manifolds := unitManifolds(unit.ManifoldsConfig{ 141 Agent: agent.APIHostPortsSetter{a}, 142 LogSource: a.bufferedLogs, 143 LeadershipGuarantee: 30 * time.Second, 144 AgentConfigChanged: a.configChangedVal, 145 ValidateMigration: a.validateMigration, 146 }) 147 148 config := dependency.EngineConfig{ 149 IsFatal: cmdutil.IsFatal, 150 WorstError: cmdutil.MoreImportantError, 151 ErrorDelay: 3 * time.Second, 152 BounceDelay: 10 * time.Millisecond, 153 } 154 engine, err := dependency.NewEngine(config) 155 if err != nil { 156 return nil, err 157 } 158 if err := dependency.Install(engine, manifolds); err != nil { 159 if err := worker.Stop(engine); err != nil { 160 logger.Errorf("while stopping engine with bad manifolds: %v", err) 161 } 162 return nil, err 163 } 164 if err := startIntrospection(introspectionConfig{ 165 Agent: a, 166 Engine: engine, 167 WorkerFunc: introspection.NewWorker, 168 }); err != nil { 169 // If the introspection worker failed to start, we just log error 170 // but continue. It is very unlikely to happen in the real world 171 // as the only issue is connecting to the abstract domain socket 172 // and the agent is controlled by by the OS to only have one. 173 logger.Errorf("failed to start introspection worker: %v", err) 174 } 175 return engine, nil 176 } 177 178 func (a *UnitAgent) Tag() names.Tag { 179 return names.NewUnitTag(a.UnitName) 180 } 181 182 func (a *UnitAgent) ChangeConfig(mutate agent.ConfigMutator) error { 183 err := a.AgentConf.ChangeConfig(mutate) 184 a.configChangedVal.Set(true) 185 return errors.Trace(err) 186 } 187 188 // validateMigration is called by the migrationminion to help check 189 // that the agent will be ok when connected to a new controller. 190 func (a *UnitAgent) validateMigration(apiCaller base.APICaller) error { 191 // TODO(mjs) - more extensive checks to come. 192 unitTag := names.NewUnitTag(a.UnitName) 193 facade := uniter.NewState(apiCaller, unitTag) 194 _, err := facade.Unit(unitTag) 195 if err != nil { 196 return errors.Trace(err) 197 } 198 model, err := facade.Model() 199 if err != nil { 200 return errors.Trace(err) 201 } 202 curModelUUID := a.CurrentConfig().Model().Id() 203 newModelUUID := model.UUID() 204 if newModelUUID != curModelUUID { 205 return errors.Errorf("model mismatch when validating: got %q, expected %q", 206 newModelUUID, curModelUUID) 207 } 208 return nil 209 }