Left: | ||
Right: |
LEFT | RIGHT |
---|---|
1 package uniter | 1 package uniter |
2 | 2 |
3 import ( | 3 import ( |
4 "errors" | 4 "errors" |
5 "fmt" | 5 "fmt" |
6 "launchpad.net/juju-core/cmd" | 6 "launchpad.net/juju-core/cmd" |
7 "launchpad.net/juju-core/cmd/jujuc/server" | 7 "launchpad.net/juju-core/cmd/jujuc/server" |
8 "launchpad.net/juju-core/environs" | |
9 "launchpad.net/juju-core/log" | 8 "launchpad.net/juju-core/log" |
10 "launchpad.net/juju-core/state" | 9 "launchpad.net/juju-core/state" |
11 "launchpad.net/juju-core/state/presence" | 10 "launchpad.net/juju-core/state/presence" |
12 "launchpad.net/juju-core/trivial" | 11 "launchpad.net/juju-core/trivial" |
13 "launchpad.net/juju-core/worker/uniter/charm" | 12 "launchpad.net/juju-core/worker/uniter/charm" |
14 "launchpad.net/juju-core/worker/uniter/hook" | 13 "launchpad.net/juju-core/worker/uniter/hook" |
15 "launchpad.net/tomb" | 14 "launchpad.net/tomb" |
16 "math/rand" | 15 "math/rand" |
17 "path/filepath" | 16 "path/filepath" |
18 "strings" | |
19 "time" | 17 "time" |
20 ) | 18 ) |
21 | 19 |
22 // Uniter implements the capabilities of the unit agent. It is not intended to | 20 // Uniter implements the capabilities of the unit agent. It is not intended to |
23 // implement the actual *behaviour* of the unit agent; that responsibility is | 21 // implement the actual *behaviour* of the unit agent; that responsibility is |
24 // delegated to Mode values, which are expected to use the capabilities of the | 22 // delegated to Mode values, which are expected to use the capabilities of the |
25 // uniter to react appropriately to changes in the system. | 23 // uniter to react appropriately to changes in the system. |
26 type Uniter struct { | 24 type Uniter struct { |
27 tomb tomb.Tomb | 25 tomb tomb.Tomb |
28 st *state.State | 26 st *state.State |
29 unit *state.Unit | 27 unit *state.Unit |
30 service *state.Service | 28 service *state.Service |
31 pinger *presence.Pinger | 29 pinger *presence.Pinger |
32 | 30 |
33 baseDir string | 31 baseDir string |
34 charm *charm.GitDir | 32 charm *charm.GitDir |
35 bundles *charm.BundlesDir | 33 bundles *charm.BundlesDir |
36 deployer *charm.Deployer | 34 deployer *charm.Deployer |
37 » op *StateFile | 35 » sf *StateFile |
38 rand *rand.Rand | 36 rand *rand.Rand |
39 } | 37 } |
40 | 38 |
41 // NewUniter creates a new Uniter which will install, run, and upgrade a | 39 // NewUniter creates a new Uniter which will install, run, and upgrade a |
42 // charm on behalf of the named unit, by executing hooks and operations | 40 // charm on behalf of the named unit, by executing hooks and operations |
43 // provoked by changes in st. | 41 // provoked by changes in st. |
44 func NewUniter(st *state.State, name string) (u *Uniter, err error) { | 42 func NewUniter(st *state.State, name string, dataDir string) (u *Uniter, err err or) { |
45 defer trivial.ErrorContextf(&err, "failed to create uniter for unit %q", name) | 43 defer trivial.ErrorContextf(&err, "failed to create uniter for unit %q", name) |
46 baseDir, err := ensureFs(name) | |
47 if err != nil { | |
48 return nil, err | |
49 } | |
50 unit, err := st.Unit(name) | 44 unit, err := st.Unit(name) |
45 if err != nil { | |
46 return nil, err | |
47 } | |
48 baseDir, err := ensureFs(dataDir, unit) | |
51 if err != nil { | 49 if err != nil { |
52 return nil, err | 50 return nil, err |
53 } | 51 } |
54 service, err := st.Service(unit.ServiceName()) | 52 service, err := st.Service(unit.ServiceName()) |
55 if err != nil { | 53 if err != nil { |
56 return nil, err | 54 return nil, err |
57 } | 55 } |
58 pinger, err := unit.SetAgentAlive() | 56 pinger, err := unit.SetAgentAlive() |
59 if err != nil { | 57 if err != nil { |
60 return nil, err | 58 return nil, err |
61 } | 59 } |
62 u = &Uniter{ | 60 u = &Uniter{ |
63 st: st, | 61 st: st, |
64 unit: unit, | 62 unit: unit, |
65 service: service, | 63 service: service, |
66 pinger: pinger, | 64 pinger: pinger, |
67 baseDir: baseDir, | 65 baseDir: baseDir, |
68 charm: charm.NewGitDir(filepath.Join(baseDir, "charm")), | 66 charm: charm.NewGitDir(filepath.Join(baseDir, "charm")), |
69 bundles: charm.NewBundlesDir(filepath.Join(baseDir, "state", "b undles")), | 67 bundles: charm.NewBundlesDir(filepath.Join(baseDir, "state", "b undles")), |
70 deployer: charm.NewDeployer(filepath.Join(baseDir, "state", "dep loyer")), | 68 deployer: charm.NewDeployer(filepath.Join(baseDir, "state", "dep loyer")), |
71 » » op: NewStateFile(filepath.Join(baseDir, "state", "uniter") ), | 69 » » sf: NewStateFile(filepath.Join(baseDir, "state", "uniter") ), |
72 rand: rand.New(rand.NewSource(time.Now().Unix())), | 70 rand: rand.New(rand.NewSource(time.Now().Unix())), |
73 } | 71 } |
74 go u.loop() | 72 go u.loop() |
75 return u, nil | 73 return u, nil |
76 } | 74 } |
77 | 75 |
78 func (u *Uniter) loop() { | 76 func (u *Uniter) loop() { |
79 var err error | 77 var err error |
80 mode := ModeInit | 78 mode := ModeInit |
81 for mode != nil { | 79 for mode != nil { |
(...skipping 17 matching lines...) Expand all Loading... | |
99 func (u *Uniter) Wait() error { | 97 func (u *Uniter) Wait() error { |
100 return u.tomb.Wait() | 98 return u.tomb.Wait() |
101 } | 99 } |
102 | 100 |
103 // deploy deploys the supplied charm, and sets follow-up hook operation state | 101 // deploy deploys the supplied charm, and sets follow-up hook operation state |
104 // as indicated by reason. | 102 // as indicated by reason. |
105 func (u *Uniter) deploy(sch *state.Charm, reason Op) error { | 103 func (u *Uniter) deploy(sch *state.Charm, reason Op) error { |
106 if reason != Install { | 104 if reason != Install { |
107 panic(fmt.Errorf("unknown deploy operation %q", reason)) | 105 panic(fmt.Errorf("unknown deploy operation %q", reason)) |
108 } | 106 } |
109 » op, err := u.op.Read() | 107 » s, err := u.sf.Read() |
niemeyer
2012/09/11 14:52:04
Feels like these are misnamed. We have an Op, and
| |
110 if err != nil && err != ErrNoStateFile { | 108 if err != nil && err != ErrNoStateFile { |
111 return err | 109 return err |
112 } | 110 } |
113 url := sch.URL() | 111 url := sch.URL() |
114 » if op.Status != Committing { | 112 » if s == nil || s.OpStep != Done { |
115 log.Printf("fetching charm %q", url) | 113 log.Printf("fetching charm %q", url) |
116 bun, err := u.bundles.Read(sch, u.tomb.Dying()) | 114 bun, err := u.bundles.Read(sch, u.tomb.Dying()) |
117 if err != nil { | 115 if err != nil { |
118 return err | 116 return err |
119 } | 117 } |
120 » » if err = u.deployer.SetCharm(bun, url); err != nil { | 118 » » if err = u.deployer.Stage(bun, url); err != nil { |
niemeyer
2012/09/11 14:52:04
Can we talk about this live somewhere? I'd appreci
fwereade
2012/09/12 15:16:40
Discussed live, agreed OK :).
| |
121 return err | 119 return err |
122 } | 120 } |
123 log.Printf("deploying charm %q", url) | 121 log.Printf("deploying charm %q", url) |
124 » » if err = u.op.Write(reason, Pending, nil, url); err != nil { | 122 » » if err = u.sf.Write(reason, Pending, nil, url); err != nil { |
125 return err | 123 return err |
126 } | 124 } |
127 if err = u.deployer.Deploy(u.charm); err != nil { | 125 if err = u.deployer.Deploy(u.charm); err != nil { |
128 return err | 126 return err |
129 } | 127 } |
130 » » if err = u.op.Write(reason, Committing, nil, url); err != nil { | 128 » » if err = u.sf.Write(reason, Done, nil, url); err != nil { |
131 return err | 129 return err |
132 } | 130 } |
133 } | 131 } |
134 log.Printf("charm %q is deployed", url) | 132 log.Printf("charm %q is deployed", url) |
135 if err := u.unit.SetCharm(sch); err != nil { | 133 if err := u.unit.SetCharm(sch); err != nil { |
136 return err | 134 return err |
137 } | 135 } |
138 » return u.op.Write(RunHook, Queued, &hook.Info{Kind: hook.Install}, nil) | 136 » return u.sf.Write(RunHook, Queued, &hook.Info{Kind: hook.Install}, nil) |
139 } | 137 } |
140 | 138 |
141 // errHookFailed indicates that a hook failed to execute, but that the Uniter's | 139 // errHookFailed indicates that a hook failed to execute, but that the Uniter's |
142 // operation is not affected by the error. | 140 // operation is not affected by the error. |
143 var errHookFailed = errors.New("hook execution failed") | 141 var errHookFailed = errors.New("hook execution failed") |
144 | 142 |
145 // runHook executes the supplied hook.Info in an appropriate hook context. If | 143 // runHook executes the supplied hook.Info in an appropriate hook context. If |
146 // the hook itself fails to execute, it returns errHookFailed. | 144 // the hook itself fails to execute, it returns errHookFailed. |
147 func (u *Uniter) runHook(hi hook.Info) error { | 145 func (u *Uniter) runHook(hi hook.Info) error { |
148 // Prepare context. | 146 // Prepare context. |
(...skipping 21 matching lines...) Expand all Loading... | |
170 } | 168 } |
171 socketPath := filepath.Join(u.baseDir, "agent.socket") | 169 socketPath := filepath.Join(u.baseDir, "agent.socket") |
172 srv, err := server.NewServer(getCmd, socketPath) | 170 srv, err := server.NewServer(getCmd, socketPath) |
173 if err != nil { | 171 if err != nil { |
174 return err | 172 return err |
175 } | 173 } |
176 go srv.Run() | 174 go srv.Run() |
177 defer srv.Close() | 175 defer srv.Close() |
178 | 176 |
179 // Run the hook. | 177 // Run the hook. |
180 » if err := u.op.Write(RunHook, Pending, &hi, nil); err != nil { | 178 » if err := u.sf.Write(RunHook, Pending, &hi, nil); err != nil { |
181 return err | 179 return err |
182 } | 180 } |
183 log.Printf("running hook %q", hookName) | 181 log.Printf("running hook %q", hookName) |
184 if err := hctx.RunHook(hookName, u.charm.Path(), socketPath); err != nil { | 182 if err := hctx.RunHook(hookName, u.charm.Path(), socketPath); err != nil { |
185 log.Printf("hook failed: %s", err) | 183 log.Printf("hook failed: %s", err) |
186 return errHookFailed | 184 return errHookFailed |
187 } | 185 } |
188 log.Printf("hook succeeded") | 186 log.Printf("hook succeeded") |
189 return u.commitHook(hi) | 187 return u.commitHook(hi) |
190 } | 188 } |
191 | 189 |
192 // commitHook ensures that state is consistent with the supplied hook, and | 190 // commitHook ensures that state is consistent with the supplied hook, and |
193 // that the fact of the hook's completion is persisted. | 191 // that the fact of the hook's completion is persisted. |
194 func (u *Uniter) commitHook(hi hook.Info) error { | 192 func (u *Uniter) commitHook(hi hook.Info) error { |
195 » if err := u.op.Write(RunHook, Committing, &hi, nil); err != nil { | 193 » if err := u.sf.Write(RunHook, Done, &hi, nil); err != nil { |
196 return err | 194 return err |
197 } | 195 } |
198 if hi.Kind.IsRelation() { | 196 if hi.Kind.IsRelation() { |
199 panic("relation hooks are not yet supported") | 197 panic("relation hooks are not yet supported") |
200 // TODO: commit relation state changes. | 198 // TODO: commit relation state changes. |
201 } | 199 } |
202 if err := u.charm.Snapshotf("completed %q hook", hi.Kind); err != nil { | 200 if err := u.charm.Snapshotf("completed %q hook", hi.Kind); err != nil { |
niemeyer
2012/09/11 14:52:04
"Completed %q hook."
fwereade
2012/09/12 15:16:40
Fixed in followup.
| |
203 return err | 201 return err |
204 } | 202 } |
205 » if err := u.op.Write(Abide, Pending, &hi, nil); err != nil { | 203 » if err := u.sf.Write(Abide, Pending, &hi, nil); err != nil { |
niemeyer
2012/09/11 14:52:04
Abide + Pending seems like a weird pair. Abide sho
fwereade
2012/09/12 15:16:40
Uncertain... the uniter is still abiding, right?
niemeyer
2012/09/12 20:58:17
Yeah, I guess. Feels a bit like there's an action
| |
206 return err | 204 return err |
207 } | 205 } |
208 log.Printf("hook complete") | 206 log.Printf("hook complete") |
209 return nil | 207 return nil |
210 } | 208 } |
211 | 209 |
212 // ensureFs ensures that files and directories required by the named uniter | 210 // ensureFs ensures that files and directories required by the named uniter |
213 // exist. It returns the path to the directory within which the uniter must | 211 // exist inside dataDir. It returns the path to the directory within which the u niter must |
214 // store its data. | 212 // store its data. |
215 func ensureFs(name string) (string, error) { | 213 func ensureFs(dataDir string, unit *state.Unit) (string, error) { |
216 // TODO: do this OAOO at packaging time? | 214 // TODO: do this OAOO at packaging time? |
217 » if err := EnsureJujucSymlinks(name); err != nil { | 215 » if err := EnsureJujucSymlinks(dataDir, unit.PathKey()); err != nil { |
218 return "", err | 216 return "", err |
219 } | 217 } |
220 » path := filepath.Join(environs.VarDir, "units", strings.Replace(name, "/ ", "-", 1)) | 218 » path := filepath.Join(dataDir, "agents", unit.PathKey()) |
221 if err := trivial.EnsureDir(filepath.Join(path, "state")); err != nil { | 219 if err := trivial.EnsureDir(filepath.Join(path, "state")); err != nil { |
222 return "", err | 220 return "", err |
223 } | 221 } |
224 return path, nil | 222 return path, nil |
225 } | 223 } |
LEFT | RIGHT |