github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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 "io" 9 "runtime" 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 "gopkg.in/natefinch/lumberjack.v2" 17 "launchpad.net/gnuflag" 18 "launchpad.net/tomb" 19 20 "github.com/juju/juju/agent" 21 "github.com/juju/juju/api" 22 "github.com/juju/juju/api/leadership" 23 cmdutil "github.com/juju/juju/cmd/jujud/util" 24 "github.com/juju/juju/network" 25 "github.com/juju/juju/tools" 26 "github.com/juju/juju/version" 27 "github.com/juju/juju/worker" 28 "github.com/juju/juju/worker/apiaddressupdater" 29 workerlogger "github.com/juju/juju/worker/logger" 30 "github.com/juju/juju/worker/proxyupdater" 31 "github.com/juju/juju/worker/rsyslog" 32 "github.com/juju/juju/worker/uniter" 33 "github.com/juju/juju/worker/upgrader" 34 ) 35 36 var ( 37 agentLogger = loggo.GetLogger("juju.jujud") 38 reportClosedUnitAPI = func(io.Closer) {} 39 ) 40 41 // UnitAgent is a cmd.Command responsible for running a unit agent. 42 type UnitAgent struct { 43 cmd.CommandBase 44 tomb tomb.Tomb 45 AgentConf 46 UnitName string 47 runner worker.Runner 48 setupLogging func(agent.Config) error 49 logToStdErr bool 50 ctx *cmd.Context 51 apiStateUpgrader APIStateUpgrader 52 53 // Used to signal that the upgrade worker will not 54 // reboot the agent on startup because there are no 55 // longer any immediately pending agent upgrades. 56 // Channel used as a selectable bool (closed means true). 57 initialAgentUpgradeCheckComplete chan struct{} 58 } 59 60 // NewUnitAgent creates a new UnitAgent value properly initialized. 61 func NewUnitAgent(ctx *cmd.Context) *UnitAgent { 62 return &UnitAgent{ 63 AgentConf: NewAgentConf(""), 64 ctx: ctx, 65 initialAgentUpgradeCheckComplete: make(chan struct{}), 66 } 67 } 68 69 func (a *UnitAgent) getUpgrader(st *api.State) APIStateUpgrader { 70 if a.apiStateUpgrader != nil { 71 return a.apiStateUpgrader 72 } 73 return st.Upgrader() 74 } 75 76 // Info returns usage information for the command. 77 func (a *UnitAgent) Info() *cmd.Info { 78 return &cmd.Info{ 79 Name: "unit", 80 Purpose: "run a juju unit agent", 81 } 82 } 83 84 func (a *UnitAgent) SetFlags(f *gnuflag.FlagSet) { 85 a.AgentConf.AddFlags(f) 86 f.StringVar(&a.UnitName, "unit-name", "", "name of the unit to run") 87 f.BoolVar(&a.logToStdErr, "log-to-stderr", false, "whether to log to standard error instead of log files") 88 } 89 90 // Init initializes the command for running. 91 func (a *UnitAgent) Init(args []string) error { 92 if a.UnitName == "" { 93 return cmdutil.RequiredError("unit-name") 94 } 95 if !names.IsValidUnit(a.UnitName) { 96 return fmt.Errorf(`--unit-name option expects "<service>/<n>" argument`) 97 } 98 if err := a.AgentConf.CheckArgs(args); err != nil { 99 return err 100 } 101 a.runner = worker.NewRunner(cmdutil.IsFatal, cmdutil.MoreImportant) 102 103 if !a.logToStdErr { 104 if err := a.ReadConfig(a.Tag().String()); err != nil { 105 return err 106 } 107 agentConfig := a.CurrentConfig() 108 109 // the writer in ctx.stderr gets set as the loggo writer in github.com/juju/cmd/logging.go 110 a.ctx.Stderr = &lumberjack.Logger{ 111 Filename: agent.LogFilename(agentConfig), 112 MaxSize: 300, // megabytes 113 MaxBackups: 2, 114 } 115 116 } 117 118 return nil 119 } 120 121 // Stop stops the unit agent. 122 func (a *UnitAgent) Stop() error { 123 a.runner.Kill() 124 return a.tomb.Wait() 125 } 126 127 // Run runs a unit agent. 128 func (a *UnitAgent) Run(ctx *cmd.Context) error { 129 defer a.tomb.Done() 130 if err := a.ReadConfig(a.Tag().String()); err != nil { 131 return err 132 } 133 agentConfig := a.CurrentConfig() 134 135 agentLogger.Infof("unit agent %v start (%s [%s])", a.Tag().String(), version.Current, runtime.Compiler) 136 if flags := featureflag.String(); flags != "" { 137 logger.Warningf("developer feature flags enabled: %s", flags) 138 } 139 140 network.InitializeFromConfig(agentConfig) 141 a.runner.StartWorker("api", a.APIWorkers) 142 err := cmdutil.AgentDone(logger, a.runner.Wait()) 143 a.tomb.Kill(err) 144 return err 145 } 146 147 func (a *UnitAgent) APIWorkers() (_ worker.Worker, err error) { 148 agentConfig := a.CurrentConfig() 149 dataDir := agentConfig.DataDir() 150 hookLock, err := cmdutil.HookExecutionLock(dataDir) 151 if err != nil { 152 return nil, err 153 } 154 st, entity, err := OpenAPIState(agentConfig, a) 155 if err != nil { 156 return nil, err 157 } 158 unitTag, err := names.ParseUnitTag(entity.Tag()) 159 if err != nil { 160 return nil, errors.Trace(err) 161 } 162 // Ensure that the environment uuid is stored in the agent config. 163 // Luckily the API has it recorded for us after we connect. 164 if agentConfig.Environment().Id() == "" { 165 err := a.ChangeConfig(func(setter agent.ConfigSetter) error { 166 environTag, err := st.EnvironTag() 167 if err != nil { 168 return errors.Annotate(err, "no environment uuid set on api") 169 } 170 171 return setter.Migrate(agent.MigrateParams{ 172 Environment: environTag, 173 }) 174 }) 175 if err != nil { 176 logger.Warningf("unable to save environment uuid: %v", err) 177 // Not really fatal, just annoying. 178 } 179 } 180 181 defer func() { 182 if err != nil { 183 st.Close() 184 reportClosedUnitAPI(st) 185 } 186 }() 187 188 // Before starting any workers, ensure we record the Juju version this unit 189 // agent is running. 190 currentTools := &tools.Tools{Version: version.Current} 191 apiStateUpgrader := a.getUpgrader(st) 192 if err := apiStateUpgrader.SetVersion(agentConfig.Tag().String(), currentTools.Version); err != nil { 193 return nil, errors.Annotate(err, "cannot set unit agent version") 194 } 195 196 runner := worker.NewRunner(cmdutil.ConnectionIsFatal(logger, st), cmdutil.MoreImportant) 197 // start proxyupdater first to ensure proxy settings are correct 198 runner.StartWorker("proxyupdater", func() (worker.Worker, error) { 199 return proxyupdater.New(st.Environment(), false), nil 200 }) 201 runner.StartWorker("upgrader", func() (worker.Worker, error) { 202 return upgrader.NewAgentUpgrader( 203 st.Upgrader(), 204 agentConfig, 205 agentConfig.UpgradedToVersion(), 206 func() bool { return false }, 207 a.initialAgentUpgradeCheckComplete, 208 ), nil 209 }) 210 runner.StartWorker("logger", func() (worker.Worker, error) { 211 return workerlogger.NewLogger(st.Logger(), agentConfig), nil 212 }) 213 runner.StartWorker("uniter", func() (worker.Worker, error) { 214 uniterFacade, err := st.Uniter() 215 if err != nil { 216 return nil, errors.Trace(err) 217 } 218 uniterParams := uniter.UniterParams{ 219 uniterFacade, 220 unitTag, 221 leadership.NewClient(st), 222 dataDir, 223 hookLock, 224 uniter.NewMetricsTimerChooser(), 225 uniter.NewUpdateStatusTimer(), 226 } 227 return uniter.NewUniter(&uniterParams), nil 228 }) 229 230 runner.StartWorker("apiaddressupdater", func() (worker.Worker, error) { 231 uniterFacade, err := st.Uniter() 232 if err != nil { 233 return nil, errors.Trace(err) 234 } 235 return apiaddressupdater.NewAPIAddressUpdater(uniterFacade, a), nil 236 }) 237 runner.StartWorker("rsyslog", func() (worker.Worker, error) { 238 return cmdutil.NewRsyslogConfigWorker(st.Rsyslog(), agentConfig, rsyslog.RsyslogModeForwarding) 239 }) 240 return cmdutil.NewCloseWorker(logger, runner, st), nil 241 } 242 243 func (a *UnitAgent) Tag() names.Tag { 244 return names.NewUnitTag(a.UnitName) 245 }