OLD | NEW |
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/environs" | 6 "launchpad.net/juju-core/environs" |
7 "launchpad.net/juju-core/log" | 7 "launchpad.net/juju-core/log" |
8 "launchpad.net/juju-core/state" | 8 "launchpad.net/juju-core/state" |
9 "launchpad.net/juju-core/state/watcher" | 9 "launchpad.net/juju-core/state/watcher" |
10 "launchpad.net/juju-core/worker/uniter/charm" | |
11 "launchpad.net/juju-core/worker/uniter/hook" | 10 "launchpad.net/juju-core/worker/uniter/hook" |
12 "launchpad.net/tomb" | 11 "launchpad.net/tomb" |
13 ) | 12 ) |
14 | 13 |
15 // Mode defines the signature of the functions that implement the possible | 14 // Mode defines the signature of the functions that implement the possible |
16 // states of a running Uniter. | 15 // states of a running Uniter. |
17 type Mode func(u *Uniter) (Mode, error) | 16 type Mode func(u *Uniter) (Mode, error) |
18 | 17 |
19 // ModeInit is the initial Uniter mode. | 18 // ModeInit is the initial Uniter mode. |
20 func ModeInit(u *Uniter) (next Mode, err error) { | 19 func ModeInit(u *Uniter) (next Mode, err error) { |
21 defer errorContextf(&err, "ModeInit") | 20 defer errorContextf(&err, "ModeInit") |
22 | |
23 log.Printf("updating unit addresses") | 21 log.Printf("updating unit addresses") |
24 cfg, err := u.st.EnvironConfig() | 22 cfg, err := u.st.EnvironConfig() |
25 if err != nil { | 23 if err != nil { |
26 return nil, err | 24 return nil, err |
27 } | 25 } |
28 provider, err := environs.Provider(cfg.Type()) | 26 provider, err := environs.Provider(cfg.Type()) |
29 if err != nil { | 27 if err != nil { |
30 return nil, err | 28 return nil, err |
31 } | 29 } |
32 if private, err := provider.PrivateAddress(); err != nil { | 30 if private, err := provider.PrivateAddress(); err != nil { |
33 return nil, err | 31 return nil, err |
34 } else if err = u.unit.SetPrivateAddress(private); err != nil { | 32 } else if err = u.unit.SetPrivateAddress(private); err != nil { |
35 return nil, err | 33 return nil, err |
36 } | 34 } |
37 if public, err := provider.PublicAddress(); err != nil { | 35 if public, err := provider.PublicAddress(); err != nil { |
38 return nil, err | 36 return nil, err |
39 } else if err = u.unit.SetPublicAddress(public); err != nil { | 37 } else if err = u.unit.SetPublicAddress(public); err != nil { |
40 return nil, err | 38 return nil, err |
41 } | 39 } |
| 40 return ModeContinue, nil |
| 41 } |
42 | 42 |
43 » log.Printf("examining charm state...") | 43 // ModeContinue determines what action to take based on persistent uniter state. |
44 » var sch *state.Charm | 44 func ModeContinue(u *Uniter) (next Mode, err error) { |
45 » cs, err := u.charm.ReadState() | 45 » defer errorContextf(&err, "ModeContinue") |
46 » if err == charm.ErrMissing { | 46 |
| 47 » // When no charm exists, install it. |
| 48 » log.Printf("reading uniter state from disk...") |
| 49 » s, err := u.sf.Read() |
| 50 » if err == ErrNoStateFile { |
47 log.Printf("charm is not deployed") | 51 log.Printf("charm is not deployed") |
48 » » if sch, _, err = u.service.Charm(); err != nil { | 52 » » sch, _, err := u.service.Charm() |
| 53 » » if err != nil { |
49 return nil, err | 54 return nil, err |
50 } | 55 } |
51 return ModeInstalling(sch), nil | 56 return ModeInstalling(sch), nil |
52 } else if err != nil { | 57 } else if err != nil { |
53 return nil, err | 58 return nil, err |
54 } | 59 } |
55 » if cs.Status == charm.Deployed { | 60 |
56 » » log.Printf("charm is deployed") | 61 » // Filter out states not related to charm deployment. |
57 » » return ModeContinue, nil | 62 » switch s.Op { |
58 » } else if sch, err = u.st.Charm(cs.URL); err != nil { | 63 » case Abide: |
59 » » return nil, err | 64 » » log.Printf("continuing after %q hook", s.Hook.Kind) |
| 65 » » if s.Hook.Kind == hook.Install { |
| 66 » » » return ModeStarting, nil |
| 67 » » } |
| 68 » » return ModeStarted, nil |
| 69 » case RunHook: |
| 70 » » if s.OpStep == Queued { |
| 71 » » » log.Printf("running queued %q hook", s.Hook.Kind) |
| 72 » » » if err := u.runHook(*s.Hook); err != nil { |
| 73 » » » » if err != errHookFailed { |
| 74 » » » » » return nil, err |
| 75 » » » » } |
| 76 » » » } |
| 77 » » » return ModeContinue, nil |
| 78 » » } |
| 79 » » if s.OpStep == Done { |
| 80 » » » log.Printf("recovering uncommitted %q hook", s.Hook.Kind
) |
| 81 » » » if err = u.commitHook(*s.Hook); err != nil { |
| 82 » » » » return nil, err |
| 83 » » » } |
| 84 » » » return ModeContinue, nil |
| 85 » » } |
| 86 » » log.Printf("awaiting error resolution for %q hook", s.Hook.Kind) |
| 87 » » return ModeHookError, nil |
60 } | 88 } |
61 switch cs.Status { | |
62 case charm.Installing: | |
63 log.Printf("resuming charm install") | |
64 return ModeInstalling(sch), nil | |
65 case charm.Upgrading, charm.Conflicted: | |
66 panic("not implemented") | |
67 } | |
68 panic("unreachable") | |
69 } | |
70 | 89 |
71 // ModeContinue determines what action to take based on hook status. | 90 » // Resume interrupted deployment operations. |
72 func ModeContinue(u *Uniter) (next Mode, err error) { | 91 » sch, err := u.st.Charm(s.CharmURL) |
73 » defer errorContextf(&err, "ModeContinue") | |
74 » log.Printf("examining hook state...") | |
75 » hs, err := u.hook.Read() | |
76 if err != nil { | 92 if err != nil { |
77 return nil, err | 93 return nil, err |
78 } | 94 } |
79 » switch hs.Status { | 95 » if s.Op == Install { |
80 » case hook.Pending: | 96 » » log.Printf("resuming charm install") |
81 » » log.Printf("awaiting error resolution for %q hook", hs.Info.Kind
) | 97 » » return ModeInstalling(sch), nil |
82 » » return ModeHookError, nil | |
83 » case hook.Committing: | |
84 » » log.Printf("recovering uncommitted %q hook", hs.Info.Kind) | |
85 » » if err = u.commitHook(hs.Info); err != nil { | |
86 » » » return nil, err | |
87 » » } | |
88 » » return ModeContinue, nil | |
89 » case hook.Queued: | |
90 » » log.Printf("running queued %q hook", hs.Info.Kind) | |
91 » » if err := u.runHook(hs.Info); err != nil { | |
92 » » » if err == errHookFailed { | |
93 » » » » return ModeHookError, nil | |
94 » » » } | |
95 » » » return nil, err | |
96 » » } | |
97 » » return ModeContinue, nil | |
98 » case hook.Complete: | |
99 » » log.Printf("continuing after %q hook", hs.Info.Kind) | |
100 » » if hs.Info.Kind == hook.Install { | |
101 » » » return ModeStarting, nil | |
102 » » } | |
103 » » return ModeStarted, nil | |
104 } | 98 } |
105 » panic(fmt.Errorf("unhandled hook status %q", hs.Status)) | 99 » panic(fmt.Errorf("unhandled operation %q", s.Op)) |
106 } | 100 } |
107 | 101 |
108 // ModeInstalling is responsible for creating the charm directory and running | 102 // ModeInstalling is responsible for creating the charm directory and running |
109 // the "install" hook. | 103 // the "install" hook. |
110 func ModeInstalling(sch *state.Charm) Mode { | 104 func ModeInstalling(sch *state.Charm) Mode { |
111 return func(u *Uniter) (next Mode, err error) { | 105 return func(u *Uniter) (next Mode, err error) { |
112 defer errorContextf(&err, "ModeInstalling") | 106 defer errorContextf(&err, "ModeInstalling") |
113 » » if err = u.changeCharm(sch, charm.Installing); err != nil { | 107 » » if err = u.deploy(sch, Install); err != nil { |
114 return nil, err | 108 return nil, err |
115 } | 109 } |
116 return ModeContinue, nil | 110 return ModeContinue, nil |
117 } | 111 } |
118 } | 112 } |
119 | 113 |
120 // ModeStarting is responsible for running the "start" hook. | 114 // ModeStarting is responsible for running the "start" hook. |
121 func ModeStarting(u *Uniter) (next Mode, err error) { | 115 func ModeStarting(u *Uniter) (next Mode, err error) { |
122 defer errorContextf(&err, "ModeStarting") | 116 defer errorContextf(&err, "ModeStarting") |
123 if err := u.unit.SetStatus(state.UnitInstalled, ""); err != nil { | 117 if err := u.unit.SetStatus(state.UnitInstalled, ""); err != nil { |
124 return nil, err | 118 return nil, err |
125 } | 119 } |
126 hi := hook.Info{Kind: hook.Start} | 120 hi := hook.Info{Kind: hook.Start} |
127 if err := u.runHook(hi); err != nil && err != errHookFailed { | 121 if err := u.runHook(hi); err != nil && err != errHookFailed { |
128 return nil, err | 122 return nil, err |
129 } | 123 } |
130 return ModeContinue, nil | 124 return ModeContinue, nil |
131 } | 125 } |
132 | 126 |
133 // ModeStarted is the Uniter's usual steady state. It watches for and responds t
o: | 127 // ModeStarted is the Uniter's usual steady state. It watches for and responds t
o: |
134 // * service configuration changes | 128 // * service configuration changes |
135 // * charm upgrade requests (not implemented) | 129 // * charm upgrade requests (not implemented) |
136 // * relation changes (not implemented) | 130 // * relation changes (not implemented) |
137 // * unit death (not implemented) | 131 // * unit death (not implemented) |
138 func ModeStarted(u *Uniter) (next Mode, err error) { | 132 func ModeStarted(u *Uniter) (next Mode, err error) { |
139 defer errorContextf(&err, "ModeStarted") | 133 defer errorContextf(&err, "ModeStarted") |
| 134 s, err := u.sf.Read() |
| 135 if err != nil { |
| 136 return nil, err |
| 137 } |
| 138 if s.Op != Abide { |
| 139 return nil, fmt.Errorf("insane uniter state: %#v", s) |
| 140 } |
140 if err = u.unit.SetStatus(state.UnitStarted, ""); err != nil { | 141 if err = u.unit.SetStatus(state.UnitStarted, ""); err != nil { |
141 return nil, err | 142 return nil, err |
142 } | 143 } |
143 » config := u.service.WatchConfig() | 144 » configw := u.service.WatchConfig() |
144 » defer stop(config, &next, &err) | 145 » defer stop(configw, &next, &err) |
145 for { | 146 for { |
146 select { | 147 select { |
147 case <-u.tomb.Dying(): | 148 case <-u.tomb.Dying(): |
148 return nil, tomb.ErrDying | 149 return nil, tomb.ErrDying |
149 » » case _, ok := <-config.Changes(): | 150 » » case _, ok := <-configw.Changes(): |
150 if !ok { | 151 if !ok { |
151 » » » » return nil, watcher.MustErr(config) | 152 » » » » return nil, watcher.MustErr(configw) |
152 } | 153 } |
153 hi := hook.Info{Kind: hook.ConfigChanged} | 154 hi := hook.Info{Kind: hook.ConfigChanged} |
154 if err = u.runHook(hi); err != nil { | 155 if err = u.runHook(hi); err != nil { |
155 if err == errHookFailed { | 156 if err == errHookFailed { |
156 return ModeHookError, nil | 157 return ModeHookError, nil |
157 } | 158 } |
158 return nil, err | 159 return nil, err |
159 } | 160 } |
160 // TODO once we've run the initial config-changed, start
other watches. | 161 // TODO once we've run the initial config-changed, start
other watches. |
161 } | 162 } |
162 // TODO: unit death; charm upgrades; relations. | 163 // TODO: unit death; charm upgrades; relations. |
163 } | 164 } |
164 panic("unreachable") | 165 panic("unreachable") |
165 } | 166 } |
166 | 167 |
167 // ModeHookError is responsible for watching and responding to: | 168 // ModeHookError is responsible for watching and responding to: |
168 // * user resolution of hook errors | 169 // * user resolution of hook errors |
169 // * forced charm upgrade requests (not implemented) | 170 // * forced charm upgrade requests (not implemented) |
170 // * unit death (not implemented) | 171 // * unit death (not implemented) |
171 func ModeHookError(u *Uniter) (next Mode, err error) { | 172 func ModeHookError(u *Uniter) (next Mode, err error) { |
172 defer errorContextf(&err, "ModeHookError") | 173 defer errorContextf(&err, "ModeHookError") |
173 » hs, err := u.hook.Read() | 174 » s, err := u.sf.Read() |
174 if err != nil { | 175 if err != nil { |
175 return nil, err | 176 return nil, err |
176 } | 177 } |
177 » if hs.Status != hook.Pending { | 178 » if s.Op != RunHook || s.OpStep != Pending { |
178 » » return nil, fmt.Errorf("inconsistent hook status %q", hs.Status) | 179 » » return nil, fmt.Errorf("insane uniter state: %#v", s) |
179 } | 180 } |
180 » msg := fmt.Sprintf("hook failed: %q", hs.Info.Kind) | 181 » msg := fmt.Sprintf("hook failed: %q", s.Hook.Kind) |
181 if err = u.unit.SetStatus(state.UnitError, msg); err != nil { | 182 if err = u.unit.SetStatus(state.UnitError, msg); err != nil { |
182 return nil, err | 183 return nil, err |
183 } | 184 } |
184 // Wait for shutdown, error resolution, or forced charm upgrade. | 185 // Wait for shutdown, error resolution, or forced charm upgrade. |
185 » resolved := u.unit.WatchResolved() | 186 » resolvedw := u.unit.WatchResolved() |
186 » defer stop(resolved, &next, &err) | 187 » defer stop(resolvedw, &next, &err) |
187 for { | 188 for { |
188 select { | 189 select { |
189 case <-u.tomb.Dying(): | 190 case <-u.tomb.Dying(): |
190 return nil, tomb.ErrDying | 191 return nil, tomb.ErrDying |
191 » » case rm, ok := <-resolved.Changes(): | 192 » » case rm, ok := <-resolvedw.Changes(): |
192 if !ok { | 193 if !ok { |
193 » » » » return nil, watcher.MustErr(resolved) | 194 » » » » return nil, watcher.MustErr(resolvedw) |
194 } | 195 } |
195 switch rm { | 196 switch rm { |
196 case state.ResolvedNone: | 197 case state.ResolvedNone: |
197 continue | 198 continue |
198 case state.ResolvedRetryHooks: | 199 case state.ResolvedRetryHooks: |
199 » » » » err = u.runHook(hs.Info) | 200 » » » » err = u.runHook(*s.Hook) |
200 case state.ResolvedNoHooks: | 201 case state.ResolvedNoHooks: |
201 » » » » err = u.commitHook(hs.Info) | 202 » » » » err = u.commitHook(*s.Hook) |
202 default: | 203 default: |
203 panic(fmt.Errorf("unhandled resolved mode %q", r
m)) | 204 panic(fmt.Errorf("unhandled resolved mode %q", r
m)) |
204 } | 205 } |
205 if e := u.unit.ClearResolved(); e != nil { | 206 if e := u.unit.ClearResolved(); e != nil { |
206 err = e | 207 err = e |
207 } | 208 } |
208 if err == errHookFailed { | 209 if err == errHookFailed { |
209 continue | 210 continue |
210 } else if err != nil { | 211 } else if err != nil { |
211 return nil, err | 212 return nil, err |
(...skipping 12 matching lines...) Expand all Loading... |
224 *err = e | 225 *err = e |
225 } | 226 } |
226 } | 227 } |
227 | 228 |
228 type stopper interface { | 229 type stopper interface { |
229 Stop() error | 230 Stop() error |
230 } | 231 } |
231 | 232 |
232 // errorContextf prefixes the error stored in err with text formatted | 233 // errorContextf prefixes the error stored in err with text formatted |
233 // according to the format specifier. If err does not contain an error, | 234 // according to the format specifier. If err does not contain an error, |
234 // or if err is tome.ErrDying, errorContextf does nothing. | 235 // or if err is tomb.ErrDying, errorContextf does nothing. |
235 func errorContextf(err *error, format string, args ...interface{}) { | 236 func errorContextf(err *error, format string, args ...interface{}) { |
236 if *err != nil && *err != tomb.ErrDying { | 237 if *err != nil && *err != tomb.ErrDying { |
237 *err = errors.New(fmt.Sprintf(format, args...) + ": " + (*err).E
rror()) | 238 *err = errors.New(fmt.Sprintf(format, args...) + ": " + (*err).E
rror()) |
238 } | 239 } |
239 } | 240 } |
OLD | NEW |