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