github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/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 "fmt" 8 "runtime" 9 "time" 10 11 "github.com/juju/cmd" 12 "github.com/juju/errors" 13 "github.com/juju/loggo" 14 "github.com/juju/names" 15 "github.com/juju/utils/featureflag" 16 "github.com/juju/utils/voyeur" 17 "gopkg.in/natefinch/lumberjack.v2" 18 "launchpad.net/gnuflag" 19 "launchpad.net/tomb" 20 21 "github.com/juju/juju/agent" 22 "github.com/juju/juju/cmd/jujud/agent/unit" 23 cmdutil "github.com/juju/juju/cmd/jujud/util" 24 "github.com/juju/juju/network" 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/logsender" 29 "github.com/juju/juju/worker/uniter" 30 ) 31 32 var ( 33 agentLogger = loggo.GetLogger("juju.jujud") 34 ) 35 36 // UnitAgent is a cmd.Command responsible for running a unit agent. 37 type UnitAgent struct { 38 cmd.CommandBase 39 tomb tomb.Tomb 40 AgentConf 41 configChangedVal *voyeur.Value 42 UnitName string 43 runner worker.Runner 44 bufferedLogs logsender.LogRecordCh 45 setupLogging func(agent.Config) error 46 logToStdErr bool 47 ctx *cmd.Context 48 49 // Used to signal that the upgrade worker will not 50 // reboot the agent on startup because there are no 51 // longer any immediately pending agent upgrades. 52 // Channel used as a selectable bool (closed means true). 53 initialUpgradeCheckComplete chan struct{} 54 } 55 56 // NewUnitAgent creates a new UnitAgent value properly initialized. 57 func NewUnitAgent(ctx *cmd.Context, bufferedLogs logsender.LogRecordCh) *UnitAgent { 58 return &UnitAgent{ 59 AgentConf: NewAgentConf(""), 60 configChangedVal: voyeur.NewValue(true), 61 ctx: ctx, 62 initialUpgradeCheckComplete: make(chan struct{}), 63 bufferedLogs: bufferedLogs, 64 } 65 } 66 67 // Info returns usage information for the command. 68 func (a *UnitAgent) Info() *cmd.Info { 69 return &cmd.Info{ 70 Name: "unit", 71 Purpose: "run a juju unit agent", 72 } 73 } 74 75 func (a *UnitAgent) SetFlags(f *gnuflag.FlagSet) { 76 a.AgentConf.AddFlags(f) 77 f.StringVar(&a.UnitName, "unit-name", "", "name of the unit to run") 78 f.BoolVar(&a.logToStdErr, "log-to-stderr", false, "whether to log to standard error instead of log files") 79 } 80 81 // Init initializes the command for running. 82 func (a *UnitAgent) Init(args []string) error { 83 if a.UnitName == "" { 84 return cmdutil.RequiredError("unit-name") 85 } 86 if !names.IsValidUnit(a.UnitName) { 87 return fmt.Errorf(`--unit-name option expects "<service>/<n>" argument`) 88 } 89 if err := a.AgentConf.CheckArgs(args); err != nil { 90 return err 91 } 92 a.runner = worker.NewRunner(cmdutil.IsFatal, cmdutil.MoreImportant, worker.RestartDelay) 93 94 if !a.logToStdErr { 95 if err := a.ReadConfig(a.Tag().String()); err != nil { 96 return err 97 } 98 agentConfig := a.CurrentConfig() 99 100 // the writer in ctx.stderr gets set as the loggo writer in github.com/juju/cmd/logging.go 101 a.ctx.Stderr = &lumberjack.Logger{ 102 Filename: agent.LogFilename(agentConfig), 103 MaxSize: 300, // megabytes 104 MaxBackups: 2, 105 } 106 107 } 108 109 return nil 110 } 111 112 // Stop stops the unit agent. 113 func (a *UnitAgent) Stop() error { 114 a.runner.Kill() 115 return a.tomb.Wait() 116 } 117 118 // Run runs a unit agent. 119 func (a *UnitAgent) Run(ctx *cmd.Context) error { 120 defer a.tomb.Done() 121 if err := a.ReadConfig(a.Tag().String()); err != nil { 122 return err 123 } 124 agentConfig := a.CurrentConfig() 125 126 agentLogger.Infof("unit agent %v start (%s [%s])", a.Tag().String(), jujuversion.Current, runtime.Compiler) 127 if flags := featureflag.String(); flags != "" { 128 logger.Warningf("developer feature flags enabled: %s", flags) 129 } 130 network.SetPreferIPv6(agentConfig.PreferIPv6()) 131 132 // Sometimes there are upgrade steps that are needed for each unit. 133 // There are plans afoot to unify the unit and machine agents. When 134 // this happens, there will be a simple helper function for the upgrade 135 // steps to run something for each unit on the machine. Until then, we 136 // need to have the uniter do it, as the overhead of getting a full 137 // upgrade process in the unit agent out weights the current benefits. 138 // So.. since the upgrade steps are all idempotent, we will just call 139 // the upgrade steps when we start the uniter. To be clear, these 140 // should move back to the upgrade package when we do unify the agents. 141 runUpgrades(agentConfig.Tag(), agentConfig.DataDir()) 142 143 a.runner.StartWorker("api", a.APIWorkers) 144 err := cmdutil.AgentDone(logger, a.runner.Wait()) 145 a.tomb.Kill(err) 146 return err 147 } 148 149 // runUpgrades is a temporary fix to deal with upgrade steps that need 150 // to be run for each unit. This function cannot fail. Errors in the 151 // upgrade steps are logged, but the uniter will attempt to continue. 152 // Worst case, we are no worse off than we are today, best case, things 153 // actually work properly. Only simple upgrade steps that don't use the API 154 // are available now. If we need really complex steps using the API, there 155 // should be significant steps to unify the agents first. 156 func runUpgrades(tag names.Tag, dataDir string) { 157 unitTag, ok := tag.(names.UnitTag) 158 if !ok { 159 logger.Errorf("unit agent tag not a unit tag: %v", tag) 160 return 161 } 162 if err := uniter.AddStoppedFieldToUniterState(unitTag, dataDir); err != nil { 163 logger.Errorf("Upgrade step failed - add Stopped field to uniter state: %v", err) 164 } 165 if err := uniter.AddInstalledToUniterState(unitTag, dataDir); err != nil { 166 logger.Errorf("Upgrade step failed - installed boolean needs to be set in the uniter local state: %v", err) 167 } 168 } 169 170 // APIWorkers returns a dependency.Engine running the unit agent's responsibilities. 171 func (a *UnitAgent) APIWorkers() (worker.Worker, error) { 172 manifolds := unit.Manifolds(unit.ManifoldsConfig{ 173 Agent: agent.APIHostPortsSetter{a}, 174 LogSource: a.bufferedLogs, 175 LeadershipGuarantee: 30 * time.Second, 176 AgentConfigChanged: a.configChangedVal, 177 }) 178 179 config := dependency.EngineConfig{ 180 IsFatal: cmdutil.IsFatal, 181 WorstError: cmdutil.MoreImportantError, 182 ErrorDelay: 3 * time.Second, 183 BounceDelay: 10 * time.Millisecond, 184 } 185 engine, err := dependency.NewEngine(config) 186 if err != nil { 187 return nil, err 188 } 189 if err := dependency.Install(engine, manifolds); err != nil { 190 if err := worker.Stop(engine); err != nil { 191 logger.Errorf("while stopping engine with bad manifolds: %v", err) 192 } 193 return nil, err 194 } 195 return engine, nil 196 } 197 198 func (a *UnitAgent) Tag() names.Tag { 199 return names.NewUnitTag(a.UnitName) 200 } 201 202 func (a *UnitAgent) ChangeConfig(mutate agent.ConfigMutator) error { 203 err := a.AgentConf.ChangeConfig(mutate) 204 a.configChangedVal.Set(true) 205 return errors.Trace(err) 206 }