Merge lp:~mfoord/juju-core/ha-rsyslog-api into lp:~go-bot/juju-core/trunk
- ha-rsyslog-api
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~mfoord/juju-core/ha-rsyslog-api |
Merge into: | lp:~go-bot/juju-core/trunk |
Diff against target: |
845 lines (+346/-102) 11 files modified
cmd/jujud/agent.go (+1/-5) instance/address.go (+7/-0) state/api/params/params.go (+7/-0) state/api/rsyslog/rsyslog.go (+51/-6) state/api/rsyslog/rsyslog_test.go (+57/-13) state/apiserver/rsyslog/config.go (+33/-0) state/apiserver/rsyslog/rsyslog.go (+60/-2) state/apiserver/rsyslog/rsyslog_test.go (+48/-11) utils/syslog/config.go (+40/-39) worker/rsyslog/rsyslog_test.go (+24/-8) worker/rsyslog/worker.go (+18/-18) |
To merge this branch: | bzr merge lp:~mfoord/juju-core/ha-rsyslog-api |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Engineering | Pending | ||
Review via email: mp+219482@code.launchpad.net |
This proposal has been superseded by a proposal from 2014-05-19.
Commit message
Description of the change
utils/syslog: update rsyslog conf
Update rsyslog configuration (and restart)
when the api servers change.
- 2731. By Michael Foord
-
Merge
- 2732. By Michael Foord
-
Test fixes - yay
- 2733. By Michael Foord
-
Merge plus fixes
- 2734. By Michael Foord
-
Expanded test for GetRsyslogConfig
- 2735. By Michael Foord
-
Comment fix
- 2736. By Michael Foord
-
Test WatchForRsyslog
Changes - 2737. By Michael Foord
-
Switch to watching APIHostPorts for RsyslogChanges API
- 2738. By Michael Foord
-
Test fixes
- 2739. By Michael Foord
-
Change to checking for MachineAgent authorization in the new API
- 2740. By Michael Foord
-
Fix build
- 2741. By Michael Foord
-
Merge
- 2742. By Michael Foord
-
Test fixes
- 2743. By Michael Foord
-
Test improvement and comment
- 2744. By Michael Foord
-
Merge
- 2745. By Michael Foord
-
Merge
- 2746. By Michael Foord
-
Remove debugging print
- 2747. By Michael Foord
-
Remove testing comments
- 2748. By Michael Foord
-
Add back in comments
- 2749. By Michael Foord
-
Merge
- 2750. By Michael Foord
-
Start using HostPorts in rsyslog template rendering
- 2751. By Michael Foord
-
Extract convenience function for building HostPorts
- 2752. By Michael Foord
-
Initial test tweaks
- 2753. By Michael Foord
-
Merge
Unmerged revisions
- 2753. By Michael Foord
-
Merge
- 2752. By Michael Foord
-
Initial test tweaks
- 2751. By Michael Foord
-
Extract convenience function for building HostPorts
- 2750. By Michael Foord
-
Start using HostPorts in rsyslog template rendering
- 2749. By Michael Foord
-
Merge
- 2748. By Michael Foord
-
Add back in comments
- 2747. By Michael Foord
-
Remove testing comments
- 2746. By Michael Foord
-
Remove debugging print
- 2745. By Michael Foord
-
Merge
- 2744. By Michael Foord
-
Merge
Preview Diff
1 | === modified file 'cmd/jujud/agent.go' |
2 | --- cmd/jujud/agent.go 2014-05-14 21:13:17 +0000 |
3 | +++ cmd/jujud/agent.go 2014-05-19 14:05:26 +0000 |
4 | @@ -322,11 +322,7 @@ |
5 | var newRsyslogConfigWorker = func(st *apirsyslog.State, agentConfig agent.Config, mode rsyslog.RsyslogMode) (worker.Worker, error) { |
6 | tag := agentConfig.Tag() |
7 | namespace := agentConfig.Value(agent.Namespace) |
8 | - addrs, err := agentConfig.APIAddresses() |
9 | - if err != nil { |
10 | - return nil, err |
11 | - } |
12 | - return rsyslog.NewRsyslogConfigWorker(st, mode, tag, namespace, addrs) |
13 | + return rsyslog.NewRsyslogConfigWorker(st, mode, tag, namespace, []instance.HostPort{}) |
14 | } |
15 | |
16 | // hookExecutionLock returns an *fslock.Lock suitable for use as a unit |
17 | |
18 | === modified file 'instance/address.go' |
19 | --- instance/address.go 2014-05-01 00:54:26 +0000 |
20 | +++ instance/address.go 2014-05-19 14:05:26 +0000 |
21 | @@ -114,6 +114,13 @@ |
22 | return outAddresses |
23 | } |
24 | |
25 | +//NewHostPorts is a convenience function to create HostPorts from a string |
26 | +//slice and port |
27 | +func NewHostPorts(inAddresses []string, port int) []HostPort { |
28 | + addresses := NewAddresses(inAddresses...) |
29 | + return AddressesWithPort(addresses, port) |
30 | +} |
31 | + |
32 | func DeriveAddressType(value string) AddressType { |
33 | ip := net.ParseIP(value) |
34 | switch { |
35 | |
36 | === modified file 'state/api/params/params.go' |
37 | --- state/api/params/params.go 2014-05-09 13:24:50 +0000 |
38 | +++ state/api/params/params.go 2014-05-19 14:05:26 +0000 |
39 | @@ -681,6 +681,13 @@ |
40 | CACert []byte |
41 | } |
42 | |
43 | +// RsyslogConfigResult holds the result of a GetRsyslogConfig call. |
44 | +type RsyslogConfigResult struct { |
45 | + CACert string |
46 | + Port int |
47 | + HostPorts []instance.HostPort |
48 | +} |
49 | + |
50 | // DistributionGroupResult contains the result of |
51 | // the DistributionGroup provisioner API call. |
52 | type DistributionGroupResult struct { |
53 | |
54 | === modified file 'state/api/rsyslog/rsyslog.go' |
55 | --- state/api/rsyslog/rsyslog.go 2014-04-11 17:51:58 +0000 |
56 | +++ state/api/rsyslog/rsyslog.go 2014-05-19 14:05:26 +0000 |
57 | @@ -4,25 +4,31 @@ |
58 | package rsyslog |
59 | |
60 | import ( |
61 | + "fmt" |
62 | + |
63 | + "launchpad.net/juju-core/instance" |
64 | "launchpad.net/juju-core/state/api/base" |
65 | - "launchpad.net/juju-core/state/api/common" |
66 | "launchpad.net/juju-core/state/api/params" |
67 | + "launchpad.net/juju-core/state/api/watcher" |
68 | ) |
69 | |
70 | const rsyslogAPI = "Rsyslog" |
71 | |
72 | +// RsyslogConfig holds the values needed for the rsyslog worker |
73 | +type RsyslogConfig struct { |
74 | + CACert string |
75 | + Port int |
76 | + HostPorts []instance.HostPort |
77 | +} |
78 | + |
79 | // State provides access to the Rsyslog API facade. |
80 | type State struct { |
81 | - *common.EnvironWatcher |
82 | caller base.Caller |
83 | } |
84 | |
85 | // NewState creates a new client-side Rsyslog facade. |
86 | func NewState(caller base.Caller) *State { |
87 | - return &State{ |
88 | - EnvironWatcher: common.NewEnvironWatcher(rsyslogAPI, caller), |
89 | - caller: caller, |
90 | - } |
91 | + return &State{caller: caller} |
92 | } |
93 | |
94 | // SetRsyslogCert sets the rsyslog CA certificate, |
95 | @@ -42,3 +48,42 @@ |
96 | } |
97 | return nil |
98 | } |
99 | + |
100 | +// WatchForRsyslogChanges returns a new NotifyWatcher. |
101 | +func (st *State) WatchForRsyslogChanges(agentTag string) (watcher.NotifyWatcher, error) { |
102 | + var results params.NotifyWatchResults |
103 | + args := params.Entities{ |
104 | + Entities: []params.Entity{{Tag: agentTag}}, |
105 | + } |
106 | + |
107 | + err := st.caller.Call(rsyslogAPI, "", "WatchForRsyslogChanges", args, &results) |
108 | + if err != nil { |
109 | + // TODO: Not directly tested |
110 | + return nil, err |
111 | + } |
112 | + if len(results.Results) != 1 { |
113 | + // TODO: Not directly tested |
114 | + return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) |
115 | + } |
116 | + result := results.Results[0] |
117 | + if result.Error != nil { |
118 | + // TODO: Not directly tested |
119 | + return nil, result.Error |
120 | + } |
121 | + w := watcher.NewNotifyWatcher(st.caller, result) |
122 | + return w, nil |
123 | +} |
124 | + |
125 | +// GetRsyslogConfig returns a RsyslogConfig. |
126 | +func (st *State) GetRsyslogConfig() (*RsyslogConfig, error) { |
127 | + var result params.RsyslogConfigResult |
128 | + err := st.caller.Call(rsyslogAPI, "", "GetRsyslogConfig", nil, &result) |
129 | + if err != nil { |
130 | + return nil, err |
131 | + } |
132 | + return &RsyslogConfig{ |
133 | + CACert: result.CACert, |
134 | + Port: result.Port, |
135 | + HostPorts: result.HostPorts, |
136 | + }, nil |
137 | +} |
138 | |
139 | === modified file 'state/api/rsyslog/rsyslog_test.go' |
140 | --- state/api/rsyslog/rsyslog_test.go 2014-03-21 18:02:23 +0000 |
141 | +++ state/api/rsyslog/rsyslog_test.go 2014-05-19 14:05:26 +0000 |
142 | @@ -5,14 +5,22 @@ |
143 | |
144 | import ( |
145 | gc "launchpad.net/gocheck" |
146 | + "launchpad.net/juju-core/instance" |
147 | + "launchpad.net/juju-core/juju/testing" |
148 | + "launchpad.net/juju-core/state" |
149 | + "launchpad.net/juju-core/state/api" |
150 | + "launchpad.net/juju-core/state/api/rsyslog" |
151 | |
152 | - jujutesting "launchpad.net/juju-core/juju/testing" |
153 | - apitesting "launchpad.net/juju-core/state/api/testing" |
154 | + statetesting "launchpad.net/juju-core/state/testing" |
155 | + coretesting "launchpad.net/juju-core/testing" |
156 | ) |
157 | |
158 | type rsyslogSuite struct { |
159 | - jujutesting.JujuConnSuite |
160 | - *apitesting.EnvironWatcherTests |
161 | + testing.JujuConnSuite |
162 | + |
163 | + st *api.State |
164 | + machine *state.Machine |
165 | + rsyslog *rsyslog.State |
166 | } |
167 | |
168 | var _ = gc.Suite(&rsyslogSuite{}) |
169 | @@ -20,15 +28,51 @@ |
170 | func (s *rsyslogSuite) SetUpTest(c *gc.C) { |
171 | s.JujuConnSuite.SetUpTest(c) |
172 | |
173 | - stateAPI, _ := s.OpenAPIAsNewMachine(c) |
174 | - rsyslogAPI := stateAPI.Rsyslog() |
175 | - c.Assert(rsyslogAPI, gc.NotNil) |
176 | - |
177 | - s.EnvironWatcherTests = apitesting.NewEnvironWatcherTests( |
178 | - rsyslogAPI, |
179 | - s.BackingState, |
180 | - apitesting.NoSecrets, |
181 | - ) |
182 | + s.st, s.machine = s.OpenAPIAsNewMachine(c, state.JobManageEnviron) |
183 | + err := s.machine.SetAddresses(instance.NewAddress("0.1.2.3", instance.NetworkUnknown)) |
184 | + c.Assert(err, gc.IsNil) |
185 | + |
186 | + // Create the rsyslog API facade |
187 | + s.rsyslog = s.st.Rsyslog() |
188 | + c.Assert(s.rsyslog, gc.NotNil) |
189 | +} |
190 | + |
191 | +func (s *rsyslogSuite) TestGetRsyslogConfig(c *gc.C) { |
192 | + err := s.APIState.Client().EnvironmentSet(map[string]interface{}{"rsyslog-ca-cert": coretesting.CACert}) |
193 | + c.Assert(err, gc.IsNil) |
194 | + |
195 | + cfg, err := s.rsyslog.GetRsyslogConfig() |
196 | + c.Assert(err, gc.IsNil) |
197 | + c.Assert(cfg, gc.NotNil) |
198 | + |
199 | + c.Assert(cfg.CACert, gc.Equals, coretesting.CACert) |
200 | + c.Assert(cfg.HostPorts, gc.HasLen, 1) |
201 | + hostPort := cfg.HostPorts[0] |
202 | + c.Assert(hostPort.Address.Value, gc.Equals, "0.1.2.3") |
203 | + |
204 | + // the rsyslog port is set by the provider/dummy/environs.go |
205 | + c.Assert(hostPort.Port, gc.Equals, 2345) |
206 | +} |
207 | + |
208 | +func (s *rsyslogSuite) TestWatchForRsyslogChanges(c *gc.C) { |
209 | + w, err := s.rsyslog.WatchForRsyslogChanges(s.machine.Tag()) |
210 | + c.Assert(err, gc.IsNil) |
211 | + defer statetesting.AssertStop(c, w) |
212 | + |
213 | + wc := statetesting.NewNotifyWatcherC(c, s.BackingState, w) |
214 | + // Initial event |
215 | + wc.AssertOneChange() |
216 | + |
217 | + // change the API HostPorts |
218 | + newHostPorts := instance.AddressesWithPort(instance.NewAddresses("127.0.0.1"), 6541) |
219 | + err = s.State.SetAPIHostPorts([][]instance.HostPort{newHostPorts}) |
220 | + c.Assert(err, gc.IsNil) |
221 | + |
222 | + // assert we get notified |
223 | + wc.AssertOneChange() |
224 | + |
225 | + statetesting.AssertStop(c, w) |
226 | + wc.AssertClosed() |
227 | } |
228 | |
229 | // SetRsyslogCACert is tested in state/apiserver/rsyslog |
230 | |
231 | === added file 'state/apiserver/rsyslog/config.go' |
232 | --- state/apiserver/rsyslog/config.go 1970-01-01 00:00:00 +0000 |
233 | +++ state/apiserver/rsyslog/config.go 2014-05-19 14:05:26 +0000 |
234 | @@ -0,0 +1,33 @@ |
235 | +package rsyslog |
236 | + |
237 | +import ( |
238 | + "net" |
239 | + |
240 | + "launchpad.net/juju-core/environs/config" |
241 | + "launchpad.net/juju-core/instance" |
242 | + apirsyslog "launchpad.net/juju-core/state/api/rsyslog" |
243 | +) |
244 | + |
245 | +// newRsyslogConfig creates a new instance of the RsyslogConfig. |
246 | +func newRsyslogConfig(envCfg *config.Config, api *RsyslogAPI) (*apirsyslog.RsyslogConfig, error) { |
247 | + stateAddrsResult, err := api.StateAddresser.StateAddresses() |
248 | + if err != nil { |
249 | + return nil, err |
250 | + } |
251 | + port := envCfg.SyslogPort() |
252 | + |
253 | + var bareAddrs []string |
254 | + for _, addr := range stateAddrsResult.Result { |
255 | + hostOnly, _, err := net.SplitHostPort(addr) |
256 | + if err != nil { |
257 | + return nil, err |
258 | + } |
259 | + bareAddrs = append(bareAddrs, hostOnly) |
260 | + } |
261 | + |
262 | + return &apirsyslog.RsyslogConfig{ |
263 | + CACert: envCfg.RsyslogCACert(), |
264 | + Port: port, |
265 | + HostPorts: instance.NewHostPorts(bareAddrs, port), |
266 | + }, nil |
267 | +} |
268 | |
269 | === modified file 'state/apiserver/rsyslog/rsyslog.go' |
270 | --- state/apiserver/rsyslog/rsyslog.go 2014-04-11 17:51:58 +0000 |
271 | +++ state/apiserver/rsyslog/rsyslog.go 2014-05-19 14:05:26 +0000 |
272 | @@ -8,17 +8,25 @@ |
273 | "launchpad.net/juju-core/state" |
274 | "launchpad.net/juju-core/state/api/params" |
275 | "launchpad.net/juju-core/state/apiserver/common" |
276 | + "launchpad.net/juju-core/state/watcher" |
277 | ) |
278 | |
279 | // RsyslogAPI implements the API used by the rsyslog worker. |
280 | type RsyslogAPI struct { |
281 | *common.EnvironWatcher |
282 | - st *state.State |
283 | - canModify bool |
284 | + |
285 | + st *state.State |
286 | + resources *common.Resources |
287 | + authorizer common.Authorizer |
288 | + StateAddresser *common.StateAddresser |
289 | + canModify bool |
290 | } |
291 | |
292 | // NewRsyslogAPI creates a new instance of the Rsyslog API. |
293 | func NewRsyslogAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*RsyslogAPI, error) { |
294 | + if !authorizer.AuthMachineAgent() && !authorizer.AuthUnitAgent() { |
295 | + return nil, common.ErrPerm |
296 | + } |
297 | // Can always watch for environ changes. |
298 | getCanWatch := common.AuthAlways(true) |
299 | // Does not get the secrets. |
300 | @@ -26,10 +34,14 @@ |
301 | return &RsyslogAPI{ |
302 | EnvironWatcher: common.NewEnvironWatcher(st, resources, getCanWatch, getCanReadSecrets), |
303 | st: st, |
304 | + authorizer: authorizer, |
305 | + resources: resources, |
306 | canModify: authorizer.AuthEnvironManager(), |
307 | + StateAddresser: common.NewStateAddresser(st), |
308 | }, nil |
309 | } |
310 | |
311 | +// SetRsyslogCert sets the rsyslog CACert. |
312 | func (api *RsyslogAPI) SetRsyslogCert(args params.SetRsyslogCertParams) (params.ErrorResult, error) { |
313 | var result params.ErrorResult |
314 | if !api.canModify { |
315 | @@ -46,3 +58,49 @@ |
316 | } |
317 | return result, nil |
318 | } |
319 | + |
320 | +// GetRsyslogConfig returns a RsyslogConfigResult. |
321 | +func (api *RsyslogAPI) GetRsyslogConfig() (params.RsyslogConfigResult, error) { |
322 | + cfg, err := api.st.EnvironConfig() |
323 | + if err != nil { |
324 | + return params.RsyslogConfigResult{}, err |
325 | + } |
326 | + |
327 | + rsyslogCfg, err := newRsyslogConfig(cfg, api) |
328 | + if err != nil { |
329 | + return params.RsyslogConfigResult{}, err |
330 | + } |
331 | + |
332 | + return params.RsyslogConfigResult{ |
333 | + CACert: rsyslogCfg.CACert, |
334 | + Port: rsyslogCfg.Port, |
335 | + HostPorts: rsyslogCfg.HostPorts, |
336 | + }, nil |
337 | +} |
338 | + |
339 | +// WatchForRsyslogChanges starts a watcher to track if there are changes |
340 | +// that require we update the rsyslog.d configurations for a machine and/or unit. |
341 | +func (api *RsyslogAPI) WatchForRsyslogChanges(args params.Entities) (params.NotifyWatchResults, error) { |
342 | + result := params.NotifyWatchResults{ |
343 | + Results: make([]params.NotifyWatchResult, len(args.Entities)), |
344 | + } |
345 | + for i := range args.Entities { |
346 | + err := common.ErrPerm |
347 | + if api.authorizer.AuthMachineAgent() || api.authorizer.AuthUnitAgent() { |
348 | + watch := api.st.WatchAPIHostPorts() |
349 | + // Consume the initial event. Technically, API |
350 | + // calls to Watch 'transmit' the initial event |
351 | + // in the Watch response. But NotifyWatchers |
352 | + // have no state to transmit. |
353 | + if _, ok := <-watch.Changes(); ok { |
354 | + result.Results[i].NotifyWatcherId = api.resources.Register(watch) |
355 | + err = nil |
356 | + } else { |
357 | + err = watcher.MustErr(watch) |
358 | + } |
359 | + } |
360 | + result.Results[i].Error = common.ServerError(err) |
361 | + } |
362 | + return result, nil |
363 | + |
364 | +} |
365 | |
366 | === modified file 'state/apiserver/rsyslog/rsyslog_test.go' |
367 | --- state/apiserver/rsyslog/rsyslog_test.go 2014-04-11 17:51:58 +0000 |
368 | +++ state/apiserver/rsyslog/rsyslog_test.go 2014-05-19 14:05:26 +0000 |
369 | @@ -9,6 +9,7 @@ |
370 | jc "github.com/juju/testing/checkers" |
371 | gc "launchpad.net/gocheck" |
372 | |
373 | + "launchpad.net/juju-core/instance" |
374 | "launchpad.net/juju-core/juju/testing" |
375 | "launchpad.net/juju-core/state" |
376 | "launchpad.net/juju-core/state/api/params" |
377 | @@ -25,6 +26,7 @@ |
378 | *commontesting.EnvironWatcherTest |
379 | authorizer apiservertesting.FakeAuthorizer |
380 | resources *common.Resources |
381 | + rsyslog *rsyslog.RsyslogAPI |
382 | } |
383 | |
384 | var _ = gc.Suite(&rsyslogSuite{}) |
385 | @@ -34,6 +36,7 @@ |
386 | s.authorizer = apiservertesting.FakeAuthorizer{ |
387 | LoggedIn: true, |
388 | EnvironManager: true, |
389 | + MachineAgent: true, |
390 | } |
391 | s.resources = common.NewResources() |
392 | api, err := rsyslog.NewRsyslogAPI(s.State, s.resources, s.authorizer) |
393 | @@ -43,28 +46,37 @@ |
394 | } |
395 | |
396 | func verifyRsyslogCACert(c *gc.C, st *apirsyslog.State, expected string) { |
397 | - cfg, err := st.EnvironConfig() |
398 | + cfg, err := st.GetRsyslogConfig() |
399 | c.Assert(err, gc.IsNil) |
400 | - c.Assert(cfg.RsyslogCACert(), gc.DeepEquals, expected) |
401 | + c.Assert(cfg.CACert, gc.DeepEquals, expected) |
402 | } |
403 | |
404 | func (s *rsyslogSuite) TestSetRsyslogCert(c *gc.C) { |
405 | - st, _ := s.OpenAPIAsNewMachine(c, state.JobManageEnviron) |
406 | - err := st.Rsyslog().SetRsyslogCert(coretesting.CACert) |
407 | + st, m := s.OpenAPIAsNewMachine(c, state.JobManageEnviron) |
408 | + err := m.SetAddresses(instance.NewAddress("0.1.2.3", instance.NetworkUnknown)) |
409 | + c.Assert(err, gc.IsNil) |
410 | + |
411 | + err = st.Rsyslog().SetRsyslogCert(coretesting.CACert) |
412 | c.Assert(err, gc.IsNil) |
413 | verifyRsyslogCACert(c, st.Rsyslog(), coretesting.CACert) |
414 | } |
415 | |
416 | func (s *rsyslogSuite) TestSetRsyslogCertNil(c *gc.C) { |
417 | - st, _ := s.OpenAPIAsNewMachine(c, state.JobManageEnviron) |
418 | - err := st.Rsyslog().SetRsyslogCert("") |
419 | + st, m := s.OpenAPIAsNewMachine(c, state.JobManageEnviron) |
420 | + err := m.SetAddresses(instance.NewAddress("0.1.2.3", instance.NetworkUnknown)) |
421 | + c.Assert(err, gc.IsNil) |
422 | + |
423 | + err = st.Rsyslog().SetRsyslogCert("") |
424 | c.Assert(err, gc.ErrorMatches, "no certificates found") |
425 | verifyRsyslogCACert(c, st.Rsyslog(), "") |
426 | } |
427 | |
428 | func (s *rsyslogSuite) TestSetRsyslogCertInvalid(c *gc.C) { |
429 | - st, _ := s.OpenAPIAsNewMachine(c, state.JobManageEnviron) |
430 | - err := st.Rsyslog().SetRsyslogCert(string(pem.EncodeToMemory(&pem.Block{ |
431 | + st, m := s.OpenAPIAsNewMachine(c, state.JobManageEnviron) |
432 | + err := m.SetAddresses(instance.NewAddress("0.1.2.3", instance.NetworkUnknown)) |
433 | + c.Assert(err, gc.IsNil) |
434 | + |
435 | + err = st.Rsyslog().SetRsyslogCert(string(pem.EncodeToMemory(&pem.Block{ |
436 | Type: "CERTIFICATE", |
437 | Bytes: []byte("not a valid certificate"), |
438 | }))) |
439 | @@ -73,10 +85,35 @@ |
440 | } |
441 | |
442 | func (s *rsyslogSuite) TestSetRsyslogCertPerms(c *gc.C) { |
443 | - st, _ := s.OpenAPIAsNewMachine(c, state.JobHostUnits) |
444 | - err := st.Rsyslog().SetRsyslogCert(coretesting.CACert) |
445 | + // create a machine-0 so we have an addresss to log to |
446 | + m, err := s.State.AddMachine("trusty", state.JobManageEnviron) |
447 | + c.Assert(err, gc.IsNil) |
448 | + err = m.SetAddresses(instance.NewAddress("0.1.2.3", instance.NetworkUnknown)) |
449 | + c.Assert(err, gc.IsNil) |
450 | + |
451 | + unitState, _ := s.OpenAPIAsNewMachine(c, state.JobHostUnits) |
452 | + err = unitState.Rsyslog().SetRsyslogCert(coretesting.CACert) |
453 | c.Assert(err, gc.ErrorMatches, "invalid entity name or password") |
454 | c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) |
455 | // Verify no change was effected. |
456 | - verifyRsyslogCACert(c, st.Rsyslog(), "") |
457 | + verifyRsyslogCACert(c, unitState.Rsyslog(), "") |
458 | +} |
459 | + |
460 | +func (s *rsyslogSuite) TestUpgraderAPIAllowsUnitAgent(c *gc.C) { |
461 | + anAuthorizer := s.authorizer |
462 | + anAuthorizer.UnitAgent = true |
463 | + anAuthorizer.MachineAgent = false |
464 | + anUpgrader, err := rsyslog.NewRsyslogAPI(s.State, s.resources, anAuthorizer) |
465 | + c.Check(err, gc.IsNil) |
466 | + c.Check(anUpgrader, gc.NotNil) |
467 | +} |
468 | + |
469 | +func (s *rsyslogSuite) TestUpgraderAPIRefusesNonUnitNonMachineAgent(c *gc.C) { |
470 | + anAuthorizer := s.authorizer |
471 | + anAuthorizer.UnitAgent = false |
472 | + anAuthorizer.MachineAgent = false |
473 | + anUpgrader, err := rsyslog.NewRsyslogAPI(s.State, s.resources, anAuthorizer) |
474 | + c.Check(err, gc.NotNil) |
475 | + c.Check(anUpgrader, gc.IsNil) |
476 | + c.Assert(err, gc.ErrorMatches, "permission denied") |
477 | } |
478 | |
479 | === modified file 'utils/syslog/config.go' |
480 | --- utils/syslog/config.go 2014-05-12 10:28:29 +0000 |
481 | +++ utils/syslog/config.go 2014-05-19 14:05:26 +0000 |
482 | @@ -8,8 +8,9 @@ |
483 | "fmt" |
484 | "io/ioutil" |
485 | "path/filepath" |
486 | - "strings" |
487 | "text/template" |
488 | + |
489 | + "launchpad.net/juju-core/instance" |
490 | ) |
491 | |
492 | // tagOffset represents the substring start value for the tag to return |
493 | @@ -53,8 +54,8 @@ |
494 | $template LongTagForwardFormat,"<%PRI%>%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg%" |
495 | |
496 | $RuleSet local |
497 | -{{range $i, $stateServerIP := stateServerHosts}} |
498 | -# start: Forwarding rule for {{$stateServerIP}} |
499 | +{{range $i, $rsyslogServer := rsyslogServers}} |
500 | +# start: Forwarding rule for {{$rsyslogServer}} |
501 | $ActionQueueType LinkedList |
502 | $ActionQueueFileName {{logfileName}}{{namespace}}_{{$i}} |
503 | $ActionResumeRetryCount -1 |
504 | @@ -64,8 +65,8 @@ |
505 | $ActionSendStreamDriverAuthMode anon |
506 | $ActionSendStreamDriverMode 1 # run driver in TLS-only mode |
507 | |
508 | -:syslogtag, startswith, "juju{{namespace}}-" @@{{$stateServerIP}}:{{portNumber}};LongTagForwardFormat |
509 | -# end: Forwarding rule for {{$stateServerIP}} |
510 | +:syslogtag, startswith, "juju{{namespace}}-" @@{{$rsyslogServer}};LongTagForwardFormat |
511 | +# end: Forwarding rule for {{rsyslogServer}} |
512 | {{end}} |
513 | & ~ |
514 | $FileCreateMode 0640 |
515 | @@ -112,8 +113,8 @@ |
516 | $InputFileTag juju{{namespace}}-{{logfileName}}: |
517 | $InputFileStateFile {{logfileName}}{{namespace}} |
518 | $InputRunFileMonitor |
519 | -{{range $i, $stateServerIP := stateServerHosts}} |
520 | -# start: Forwarding rule for {{$stateServerIP}} |
521 | +{{range $i, $rsyslogServer := rsyslogServers}} |
522 | +# start: Forwarding rule for {{$rsyslogServer}} |
523 | $ActionQueueType LinkedList |
524 | $ActionQueueFileName {{logfileName}}{{namespace}}_{{$i}} |
525 | $ActionResumeRetryCount -1 |
526 | @@ -124,8 +125,8 @@ |
527 | $ActionSendStreamDriverMode 1 # run driver in TLS-only mode |
528 | |
529 | $template LongTagForwardFormat,"<%PRI%>%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg%" |
530 | -:syslogtag, startswith, "juju{{namespace}}-" @@{{$stateServerIP}}:{{portNumber}};LongTagForwardFormat |
531 | -# end: Forwarding rule for {{$stateServerIP}} |
532 | +:syslogtag, startswith, "juju{{namespace}}-" @@{{$rsyslogServer}};LongTagForwardFormat |
533 | +# end: Forwarding rule for {{$rsyslogServer}} |
534 | {{end}} |
535 | & ~ |
536 | ` |
537 | @@ -159,8 +160,8 @@ |
538 | ConfigFileName string |
539 | // the name of the log file to tail. |
540 | LogFileName string |
541 | - // the addresses of the state server to which messages should be forwarded. |
542 | - StateServerAddresses []string |
543 | + // the addresses of the rsyslog servers to which messages should be forwarded. |
544 | + RsyslogHostPorts []instance.HostPort |
545 | // CA certificate file name. |
546 | CACertFileName string |
547 | // Server certificate file name. |
548 | @@ -177,13 +178,13 @@ |
549 | |
550 | // NewForwardConfig creates a SyslogConfig instance used on unit nodes to forward log entries |
551 | // to the state server nodes. |
552 | -func NewForwardConfig(logFile, logDir string, port int, namespace string, stateServerAddresses []string) *SyslogConfig { |
553 | +func NewForwardConfig(logFile, logDir string, port int, namespace string, rsyslogHostPorts []instance.HostPort) *SyslogConfig { |
554 | conf := &SyslogConfig{ |
555 | - configTemplate: nodeRsyslogTemplate, |
556 | - StateServerAddresses: stateServerAddresses, |
557 | - LogFileName: logFile, |
558 | - Port: port, |
559 | - LogDir: logDir, |
560 | + configTemplate: nodeRsyslogTemplate, |
561 | + RsyslogHostPorts: rsyslogHostPorts, |
562 | + LogFileName: logFile, |
563 | + Port: port, |
564 | + LogDir: logDir, |
565 | } |
566 | if namespace != "" { |
567 | conf.Namespace = "-" + namespace |
568 | @@ -193,13 +194,13 @@ |
569 | |
570 | // NewAccumulateConfig creates a SyslogConfig instance used to accumulate log entries from the |
571 | // various unit nodes. |
572 | -func NewAccumulateConfig(logFile, logDir string, port int, namespace string, stateServerAddresses []string) *SyslogConfig { |
573 | +func NewAccumulateConfig(logFile, logDir string, port int, namespace string, rsyslogHostPorts []instance.HostPort) *SyslogConfig { |
574 | conf := &SyslogConfig{ |
575 | - configTemplate: stateServerRsyslogTemplate, |
576 | - LogFileName: logFile, |
577 | - Port: port, |
578 | - LogDir: logDir, |
579 | - StateServerAddresses: stateServerAddresses, |
580 | + configTemplate: stateServerRsyslogTemplate, |
581 | + LogFileName: logFile, |
582 | + Port: port, |
583 | + LogDir: logDir, |
584 | + RsyslogHostPorts: rsyslogHostPorts, |
585 | } |
586 | if namespace != "" { |
587 | conf.Namespace = "-" + namespace |
588 | @@ -236,13 +237,13 @@ |
589 | |
590 | // Render generates the rsyslog config. |
591 | func (slConfig *SyslogConfig) Render() ([]byte, error) { |
592 | - var stateServerHosts = func() []string { |
593 | - var hosts []string |
594 | - for _, addr := range slConfig.StateServerAddresses { |
595 | - parts := strings.Split(addr, ":") |
596 | - hosts = append(hosts, parts[0]) |
597 | + var rsyslogServers = func() []string { |
598 | + var servers []string |
599 | + for _, hostPort := range slConfig.RsyslogHostPorts { |
600 | + server := hostPort.Address.Value + ":" + string(hostPort.Port) |
601 | + servers = append(servers, server) |
602 | } |
603 | - return hosts |
604 | + return servers |
605 | } |
606 | |
607 | var logFilePath = func() string { |
608 | @@ -251,16 +252,16 @@ |
609 | |
610 | t := template.New("") |
611 | t.Funcs(template.FuncMap{ |
612 | - "logfileName": func() string { return slConfig.LogFileName }, |
613 | - "stateServerHosts": stateServerHosts, |
614 | - "logfilePath": logFilePath, |
615 | - "portNumber": func() int { return slConfig.Port }, |
616 | - "logDir": func() string { return slConfig.LogDir }, |
617 | - "namespace": func() string { return slConfig.Namespace }, |
618 | - "tagStart": func() int { return tagOffset + len(slConfig.Namespace) }, |
619 | - "tlsCACertPath": slConfig.CACertPath, |
620 | - "tlsCertPath": slConfig.ServerCertPath, |
621 | - "tlsKeyPath": slConfig.ServerKeyPath, |
622 | + "logfileName": func() string { return slConfig.LogFileName }, |
623 | + "rsyslogServers": rsyslogServers, |
624 | + "logfilePath": logFilePath, |
625 | + "portNumber": func() int { return slConfig.Port }, |
626 | + "logDir": func() string { return slConfig.LogDir }, |
627 | + "namespace": func() string { return slConfig.Namespace }, |
628 | + "tagStart": func() int { return tagOffset + len(slConfig.Namespace) }, |
629 | + "tlsCACertPath": slConfig.CACertPath, |
630 | + "tlsCertPath": slConfig.ServerCertPath, |
631 | + "tlsKeyPath": slConfig.ServerKeyPath, |
632 | }) |
633 | |
634 | // Process the rsyslog config template and echo to the conf file. |
635 | |
636 | === modified file 'worker/rsyslog/rsyslog_test.go' |
637 | --- worker/rsyslog/rsyslog_test.go 2014-05-12 15:57:30 +0000 |
638 | +++ worker/rsyslog/rsyslog_test.go 2014-05-19 14:05:26 +0000 |
639 | @@ -16,6 +16,7 @@ |
640 | gc "launchpad.net/gocheck" |
641 | |
642 | "launchpad.net/juju-core/cert" |
643 | + "launchpad.net/juju-core/instance" |
644 | jujutesting "launchpad.net/juju-core/juju/testing" |
645 | "launchpad.net/juju-core/state" |
646 | "launchpad.net/juju-core/state/api" |
647 | @@ -30,6 +31,9 @@ |
648 | |
649 | type RsyslogSuite struct { |
650 | jujutesting.JujuConnSuite |
651 | + |
652 | + st *api.State |
653 | + machine *state.Machine |
654 | } |
655 | |
656 | var _ = gc.Suite(&RsyslogSuite{}) |
657 | @@ -50,6 +54,10 @@ |
658 | s.PatchValue(rsyslog.RestartRsyslog, func() error { return nil }) |
659 | s.PatchValue(rsyslog.LogDir, c.MkDir()) |
660 | s.PatchValue(rsyslog.RsyslogConfDir, c.MkDir()) |
661 | + |
662 | + s.st, s.machine = s.OpenAPIAsNewMachine(c, state.JobManageEnviron) |
663 | + err := s.machine.SetAddresses(instance.NewAddress("0.1.2.3", instance.NetworkUnknown)) |
664 | + c.Assert(err, gc.IsNil) |
665 | } |
666 | |
667 | func waitForFile(c *gc.C, file string) { |
668 | @@ -87,7 +95,7 @@ |
669 | } |
670 | |
671 | func (s *RsyslogSuite) TestTearDown(c *gc.C) { |
672 | - st, m := s.OpenAPIAsNewMachine(c, state.JobManageEnviron) |
673 | + st, m := s.st, s.machine |
674 | worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeAccumulate, m.Tag(), "", []string{"0.1.2.3"}) |
675 | c.Assert(err, gc.IsNil) |
676 | confFile := filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf") |
677 | @@ -131,7 +139,7 @@ |
678 | } |
679 | |
680 | func (s *RsyslogSuite) TestModeAccumulate(c *gc.C) { |
681 | - st, m := s.OpenAPIAsNewMachine(c, state.JobManageEnviron) |
682 | + st, m := s.st, s.machine |
683 | worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeAccumulate, m.Tag(), "", nil) |
684 | c.Assert(err, gc.IsNil) |
685 | defer func() { c.Assert(worker.Wait(), gc.IsNil) }() |
686 | @@ -165,7 +173,7 @@ |
687 | } |
688 | |
689 | func (s *RsyslogSuite) TestAccumulateHA(c *gc.C) { |
690 | - _, m := s.OpenAPIAsNewMachine(c, state.JobManageEnviron) |
691 | + m := s.machine |
692 | syslogConfig := syslog.NewAccumulateConfig(m.Tag(), *rsyslog.LogDir, 6541, "", []string{"192.168.1", "127.0.0.1"}) |
693 | rendered, err := syslogConfig.Render() |
694 | c.Assert(err, gc.IsNil) |
695 | @@ -178,7 +186,11 @@ |
696 | } |
697 | |
698 | func (s *RsyslogSuite) TestNamespace(c *gc.C) { |
699 | - st, _ := s.OpenAPIAsNewMachine(c, state.JobManageEnviron) |
700 | + st := s.st |
701 | + // set the rsyslog cert |
702 | + err := s.APIState.Client().EnvironmentSet(map[string]interface{}{"rsyslog-ca-cert": coretesting.CACert}) |
703 | + c.Assert(err, gc.IsNil) |
704 | + |
705 | // namespace only takes effect in filenames |
706 | // for machine-0; all others assume isolation. |
707 | s.testNamespace(c, st, "machine-0", "", "25-juju.conf", *rsyslog.LogDir) |
708 | @@ -201,19 +213,23 @@ |
709 | |
710 | err := os.MkdirAll(expectedLogDir, 0755) |
711 | c.Assert(err, gc.IsNil) |
712 | - err = s.APIState.Client().EnvironmentSet(map[string]interface{}{"rsyslog-ca-cert": coretesting.CACert}) |
713 | - c.Assert(err, gc.IsNil) |
714 | worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeForwarding, tag, namespace, []string{"0.1.2.3"}) |
715 | c.Assert(err, gc.IsNil) |
716 | defer func() { c.Assert(worker.Wait(), gc.IsNil) }() |
717 | defer worker.Kill() |
718 | |
719 | - // Ensure that ca-cert.pem gets written to the expected log dir. |
720 | - waitForFile(c, filepath.Join(expectedLogDir, "ca-cert.pem")) |
721 | + // change the API HostPorts to trigger an rsyslog restart |
722 | + newHostPorts := instance.AddressesWithPort(instance.NewAddresses("127.0.0.1"), 6541) |
723 | + err = s.State.SetAPIHostPorts([][]instance.HostPort{newHostPorts}) |
724 | + c.Assert(err, gc.IsNil) |
725 | |
726 | // Wait for rsyslog to be restarted, so we can check to see |
727 | // what the name of the config file is. |
728 | waitForRestart(c, restarted) |
729 | + |
730 | + // Ensure that ca-cert.pem gets written to the expected log dir. |
731 | + waitForFile(c, filepath.Join(expectedLogDir, "ca-cert.pem")) |
732 | + |
733 | dir, err := os.Open(*rsyslog.RsyslogConfDir) |
734 | c.Assert(err, gc.IsNil) |
735 | names, err := dir.Readdirnames(-1) |
736 | |
737 | === modified file 'worker/rsyslog/worker.go' |
738 | --- worker/rsyslog/worker.go 2014-05-14 21:13:17 +0000 |
739 | +++ worker/rsyslog/worker.go 2014-05-19 14:05:26 +0000 |
740 | @@ -15,6 +15,7 @@ |
741 | |
742 | "launchpad.net/juju-core/agent" |
743 | "launchpad.net/juju-core/cert" |
744 | + "launchpad.net/juju-core/instance" |
745 | "launchpad.net/juju-core/names" |
746 | apirsyslog "launchpad.net/juju-core/state/api/rsyslog" |
747 | "launchpad.net/juju-core/state/api/watcher" |
748 | @@ -53,7 +54,7 @@ |
749 | mode RsyslogMode |
750 | syslogConfig *syslog.SyslogConfig |
751 | rsyslogConfPath string |
752 | - |
753 | + tag string |
754 | // We store the syslog-port and rsyslog-ca-cert |
755 | // values after writing the rsyslog configuration, |
756 | // so we can decide whether a change has occurred. |
757 | @@ -63,12 +64,12 @@ |
758 | |
759 | var _ worker.NotifyWatchHandler = (*RsyslogConfigHandler)(nil) |
760 | |
761 | -// NewRsyslogConfigWorker returns a worker.Worker that watches |
762 | -// for config changes and updates rsyslog configuration based |
763 | +// NewRsyslogConfigWorker returns a worker.Worker that uses |
764 | +// WatchForRsyslogChanges and updates rsyslog configuration based |
765 | // on changes. The worker will remove the configuration file |
766 | // on teardown. |
767 | -func NewRsyslogConfigWorker(st *apirsyslog.State, mode RsyslogMode, tag, namespace string, stateServerAddrs []string) (worker.Worker, error) { |
768 | - handler, err := newRsyslogConfigHandler(st, mode, tag, namespace, stateServerAddrs) |
769 | +func NewRsyslogConfigWorker(st *apirsyslog.State, mode RsyslogMode, tag, namespace string, rsyslogHostPorts []instance.HostPort) (worker.Worker, error) { |
770 | + handler, err := newRsyslogConfigHandler(st, mode, tag, namespace, rsyslogHostPorts) |
771 | if err != nil { |
772 | return nil, err |
773 | } |
774 | @@ -76,15 +77,15 @@ |
775 | return worker.NewNotifyWorker(handler), nil |
776 | } |
777 | |
778 | -func newRsyslogConfigHandler(st *apirsyslog.State, mode RsyslogMode, tag, namespace string, stateServerAddrs []string) (*RsyslogConfigHandler, error) { |
779 | +func newRsyslogConfigHandler(st *apirsyslog.State, mode RsyslogMode, tag, namespace string, rsyslogHostPorts []instance.HostPort) (*RsyslogConfigHandler, error) { |
780 | var syslogConfig *syslog.SyslogConfig |
781 | if mode == RsyslogModeAccumulate { |
782 | syslogConfig = syslog.NewAccumulateConfig( |
783 | - tag, logDir, 0, namespace, stateServerAddrs, |
784 | + tag, logDir, 0, namespace, rsyslogHostPorts, |
785 | ) |
786 | } else { |
787 | syslogConfig = syslog.NewForwardConfig( |
788 | - tag, logDir, 0, namespace, stateServerAddrs, |
789 | + tag, logDir, 0, namespace, rsyslogHostPorts, |
790 | ) |
791 | } |
792 | |
793 | @@ -116,6 +117,7 @@ |
794 | st: st, |
795 | mode: mode, |
796 | syslogConfig: syslogConfig, |
797 | + tag: tag, |
798 | }, nil |
799 | } |
800 | |
801 | @@ -125,7 +127,7 @@ |
802 | return nil, errors.Annotate(err, "failed to write rsyslog certificates") |
803 | } |
804 | } |
805 | - return h.st.WatchForEnvironConfigChanges() |
806 | + return h.st.WatchForRsyslogChanges(h.tag) |
807 | } |
808 | |
809 | var restartRsyslog = syslog.Restart |
810 | @@ -138,20 +140,18 @@ |
811 | } |
812 | |
813 | func (h *RsyslogConfigHandler) Handle() error { |
814 | - cfg, err := h.st.EnvironConfig() |
815 | + cfg, err := h.st.GetRsyslogConfig() |
816 | if err != nil { |
817 | return errors.Annotate(err, "cannot get environ config") |
818 | } |
819 | - rsyslogCACert := cfg.RsyslogCACert() |
820 | + rsyslogCACert := cfg.CACert |
821 | if rsyslogCACert == "" { |
822 | return nil |
823 | } |
824 | - // If neither syslog-port nor rsyslog-ca-cert |
825 | - // have changed, we can drop out now. |
826 | - if cfg.SyslogPort() == h.syslogPort && rsyslogCACert == h.rsyslogCACert { |
827 | - return nil |
828 | - } |
829 | - h.syslogConfig.Port = cfg.SyslogPort() |
830 | + |
831 | + h.syslogConfig.Port = cfg.Port |
832 | + h.syslogConfig.RsyslogHostPorts = cfg.HostPorts |
833 | + |
834 | if h.mode == RsyslogModeForwarding { |
835 | if err := writeFileAtomic(h.syslogConfig.CACertPath(), []byte(rsyslogCACert), 0644, 0, 0); err != nil { |
836 | return errors.Annotate(err, "cannot write CA certificate") |
837 | @@ -172,7 +172,7 @@ |
838 | // Record config values so we don't try again. |
839 | // Do this last so we recover from intermittent |
840 | // failures. |
841 | - h.syslogPort = cfg.SyslogPort() |
842 | + h.syslogPort = cfg.Port |
843 | h.rsyslogCACert = rsyslogCACert |
844 | return nil |
845 | } |