5
"launchpad.net/gnuflag"
6
"launchpad.net/juju-core/charm"
7
"launchpad.net/juju-core/cmd"
8
"launchpad.net/juju-core/environs/agent"
9
"launchpad.net/juju-core/log"
10
"launchpad.net/juju-core/state"
11
"launchpad.net/juju-core/state/apiserver"
12
"launchpad.net/juju-core/worker"
13
"launchpad.net/juju-core/worker/firewaller"
14
"launchpad.net/juju-core/worker/machiner"
15
"launchpad.net/juju-core/worker/provisioner"
21
var retryDelay = 3 * time.Second
23
// MachineAgent is a cmd.Command responsible for running a machine agent.
24
type MachineAgent struct {
31
// Info returns usage information for the command.
32
func (a *MachineAgent) Info() *cmd.Info {
35
Purpose: "run a juju machine agent",
39
func (a *MachineAgent) SetFlags(f *gnuflag.FlagSet) {
41
f.StringVar(&a.MachineId, "machine-id", "", "id of the machine to run")
44
// Init initializes the command for running.
45
func (a *MachineAgent) Init(args []string) error {
46
if !state.IsMachineId(a.MachineId) {
47
return fmt.Errorf("--machine-id option must be set, and expects a non-negative integer")
49
return a.Conf.checkArgs(args)
52
// Stop stops the machine agent.
53
func (a *MachineAgent) Stop() error {
58
// Run runs a machine agent.
59
func (a *MachineAgent) Run(_ *cmd.Context) error {
60
if err := a.Conf.read(state.MachineTag(a.MachineId)); err != nil {
63
charm.CacheDir = filepath.Join(a.Conf.DataDir, "charmcache")
66
// We run the API server worker first, because we may
67
// need to connect to it before starting the other workers.
68
apiDone := make(chan error)
69
// Pass a copy of the API configuration to maybeRunAPIServer
70
// so that it can mutate it independently.
73
apiDone <- a.maybeRunAPIServer(&conf)
75
runLoopDone := make(chan error)
77
runLoopDone <- RunAgentLoop(a.Conf.Conf, a)
80
for apiDone != nil || runLoopDone != nil {
83
case err1 = <-apiDone:
85
case err1 = <-runLoopDone:
89
if moreImportant(err1, err) {
93
if err == worker.ErrTerminateAgent {
96
if ug, ok := err.(*UpgradeReadyError); ok {
97
if err1 := ug.ChangeAgentTools(); err1 != nil {
99
// Return and let upstart deal with the restart.
105
func (a *MachineAgent) RunOnce(st *state.State, e AgentState) error {
106
m := e.(*state.Machine)
107
log.Infof("jobs for machine agent: %v", m.Jobs())
109
NewUpgrader(st, m, a.Conf.DataDir),
110
machiner.NewMachiner(st, m.Id()),
112
for _, j := range m.Jobs() {
114
case state.JobHostUnits:
115
tasks = append(tasks,
116
newDeployer(st, m.WatchPrincipalUnits(), a.Conf.DataDir))
117
case state.JobManageEnviron:
118
tasks = append(tasks,
119
provisioner.NewProvisioner(st, a.MachineId),
120
firewaller.NewFirewaller(st))
121
case state.JobServeAPI:
122
// Ignore because it's started independently.
125
log.Warningf("ignoring unknown job %q", j)
128
return runTasks(a.tomb.Dying(), tasks...)
131
func (a *MachineAgent) Entity(st *state.State) (AgentState, error) {
132
m, err := st.Machine(a.MachineId)
136
// Check the machine nonce as provisioned matches the agent.Conf value.
137
if !m.CheckProvisioned(a.Conf.MachineNonce) {
138
// The agent is running on a different machine to the one it
139
// should be according to state. It must stop immediately.
140
log.Errorf("running machine %v agent on inappropriate instance", m)
141
return nil, worker.ErrTerminateAgent
146
func (a *MachineAgent) Tag() string {
147
return state.MachineTag(a.MachineId)
150
func (a *MachineAgent) Tomb() *tomb.Tomb {
154
// maybeStartAPIServer starts the API server if necessary.
155
func (a *MachineAgent) maybeRunAPIServer(conf *agent.Conf) error {
156
return runLoop(func() error {
157
return a.maybeRunAPIServerOnce(conf)
161
// maybeRunAPIServerOnce runs the API server until it dies,
162
// but only if the machine is required to run the API server.
163
func (a *MachineAgent) maybeRunAPIServerOnce(conf *agent.Conf) error {
164
st, entity, err := openState(conf, a)
169
m := entity.(*state.Machine)
171
for _, job := range m.Jobs() {
172
if job == state.JobServeAPI {
178
// If we don't need to run the API, then we just hang
179
// around indefinitely until asked to stop.
183
// If the configuration does not have the required information,
184
// it is currently not a recoverable error, so we kill the whole
185
// agent, potentially enabling human intervention to fix
186
// the agent's configuration file. In the future, we may retrieve
187
// the state server certificate and key from the state, and
188
// this should then change.
189
if len(conf.StateServerCert) == 0 || len(conf.StateServerKey) == 0 {
190
return &fatalError{"configuration does not have state server cert/key"}
192
log.Infof("running API server job")
193
srv, err := apiserver.NewServer(st, fmt.Sprintf(":%d", conf.APIPort), conf.StateServerCert, conf.StateServerKey)
198
case <-a.tomb.Dying():
200
log.Noticef("API server has died: %v", srv.Stop())