~juju-qa/ubuntu/yakkety/juju/2.0-rc3-again

« back to all changes in this revision

Viewing changes to src/launchpad.net/juju-core/cmd/jujud/machine.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-04-24 22:34:47 UTC
  • Revision ID: package-import@ubuntu.com-20130424223447-f0qdji7ubnyo0s71
Tags: upstream-1.10.0.1
ImportĀ upstreamĀ versionĀ 1.10.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package main
 
2
 
 
3
import (
 
4
        "fmt"
 
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"
 
16
        "launchpad.net/tomb"
 
17
        "path/filepath"
 
18
        "time"
 
19
)
 
20
 
 
21
var retryDelay = 3 * time.Second
 
22
 
 
23
// MachineAgent is a cmd.Command responsible for running a machine agent.
 
24
type MachineAgent struct {
 
25
        cmd.CommandBase
 
26
        tomb      tomb.Tomb
 
27
        Conf      AgentConf
 
28
        MachineId string
 
29
}
 
30
 
 
31
// Info returns usage information for the command.
 
32
func (a *MachineAgent) Info() *cmd.Info {
 
33
        return &cmd.Info{
 
34
                Name:    "machine",
 
35
                Purpose: "run a juju machine agent",
 
36
        }
 
37
}
 
38
 
 
39
func (a *MachineAgent) SetFlags(f *gnuflag.FlagSet) {
 
40
        a.Conf.addFlags(f)
 
41
        f.StringVar(&a.MachineId, "machine-id", "", "id of the machine to run")
 
42
}
 
43
 
 
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")
 
48
        }
 
49
        return a.Conf.checkArgs(args)
 
50
}
 
51
 
 
52
// Stop stops the machine agent.
 
53
func (a *MachineAgent) Stop() error {
 
54
        a.tomb.Kill(nil)
 
55
        return a.tomb.Wait()
 
56
}
 
57
 
 
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 {
 
61
                return err
 
62
        }
 
63
        charm.CacheDir = filepath.Join(a.Conf.DataDir, "charmcache")
 
64
        defer a.tomb.Done()
 
65
 
 
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.
 
71
        conf := *a.Conf.Conf
 
72
        go func() {
 
73
                apiDone <- a.maybeRunAPIServer(&conf)
 
74
        }()
 
75
        runLoopDone := make(chan error)
 
76
        go func() {
 
77
                runLoopDone <- RunAgentLoop(a.Conf.Conf, a)
 
78
        }()
 
79
        var err error
 
80
        for apiDone != nil || runLoopDone != nil {
 
81
                var err1 error
 
82
                select {
 
83
                case err1 = <-apiDone:
 
84
                        apiDone = nil
 
85
                case err1 = <-runLoopDone:
 
86
                        runLoopDone = nil
 
87
                }
 
88
                a.tomb.Kill(err1)
 
89
                if moreImportant(err1, err) {
 
90
                        err = err1
 
91
                }
 
92
        }
 
93
        if err == worker.ErrTerminateAgent {
 
94
                err = nil
 
95
        }
 
96
        if ug, ok := err.(*UpgradeReadyError); ok {
 
97
                if err1 := ug.ChangeAgentTools(); err1 != nil {
 
98
                        err = err1
 
99
                        // Return and let upstart deal with the restart.
 
100
                }
 
101
        }
 
102
        return err
 
103
}
 
104
 
 
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())
 
108
        tasks := []task{
 
109
                NewUpgrader(st, m, a.Conf.DataDir),
 
110
                machiner.NewMachiner(st, m.Id()),
 
111
        }
 
112
        for _, j := range m.Jobs() {
 
113
                switch j {
 
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.
 
123
                        continue
 
124
                default:
 
125
                        log.Warningf("ignoring unknown job %q", j)
 
126
                }
 
127
        }
 
128
        return runTasks(a.tomb.Dying(), tasks...)
 
129
}
 
130
 
 
131
func (a *MachineAgent) Entity(st *state.State) (AgentState, error) {
 
132
        m, err := st.Machine(a.MachineId)
 
133
        if err != nil {
 
134
                return nil, err
 
135
        }
 
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
 
142
        }
 
143
        return m, nil
 
144
}
 
145
 
 
146
func (a *MachineAgent) Tag() string {
 
147
        return state.MachineTag(a.MachineId)
 
148
}
 
149
 
 
150
func (a *MachineAgent) Tomb() *tomb.Tomb {
 
151
        return &a.tomb
 
152
}
 
153
 
 
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)
 
158
        }, a.tomb.Dying())
 
159
}
 
160
 
 
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)
 
165
        if err != nil {
 
166
                return err
 
167
        }
 
168
        defer st.Close()
 
169
        m := entity.(*state.Machine)
 
170
        runAPI := false
 
171
        for _, job := range m.Jobs() {
 
172
                if job == state.JobServeAPI {
 
173
                        runAPI = true
 
174
                        break
 
175
                }
 
176
        }
 
177
        if !runAPI {
 
178
                // If we don't need to run the API, then we just hang
 
179
                // around indefinitely until asked to stop.
 
180
                <-a.tomb.Dying()
 
181
                return nil
 
182
        }
 
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"}
 
191
        }
 
192
        log.Infof("running API server job")
 
193
        srv, err := apiserver.NewServer(st, fmt.Sprintf(":%d", conf.APIPort), conf.StateServerCert, conf.StateServerKey)
 
194
        if err != nil {
 
195
                return err
 
196
        }
 
197
        select {
 
198
        case <-a.tomb.Dying():
 
199
        case <-srv.Dead():
 
200
                log.Noticef("API server has died: %v", srv.Stop())
 
201
        }
 
202
        return srv.Stop()
 
203
}