Skip to content

Commit 007b84e

Browse files
committed
Add flag to enable log forwarding; handle config changes
1 parent 3b014b4 commit 007b84e

File tree

16 files changed

+463
-144
lines changed

16 files changed

+463
-144
lines changed

api/agent/state.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,17 @@ import (
1919
type State struct {
2020
facade base.FacadeCaller
2121
*common.ModelWatcher
22+
*common.ControllerConfigAPI
2223
}
2324

2425
// NewState returns a version of the state that provides functionality
2526
// required by agent code.
2627
func NewState(caller base.APICaller) *State {
2728
facadeCaller := base.NewFacadeCaller(caller, "Agent")
2829
return &State{
29-
facade: facadeCaller,
30-
ModelWatcher: common.NewModelWatcher(facadeCaller),
30+
facade: facadeCaller,
31+
ModelWatcher: common.NewModelWatcher(facadeCaller),
32+
ControllerConfigAPI: common.NewControllerConfig(facadeCaller),
3133
}
3234
}
3335

api/common/modelwatcher.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
apiwatcher "github.com/juju/juju/api/watcher"
99
"github.com/juju/juju/apiserver/params"
1010
"github.com/juju/juju/environs/config"
11+
"github.com/juju/juju/logfwd/syslog"
1112
"github.com/juju/juju/watcher"
1213
)
1314

@@ -47,3 +48,23 @@ func (e *ModelWatcher) ModelConfig() (*config.Config, error) {
4748
}
4849
return conf, nil
4950
}
51+
52+
// WatchForLogForwardConfigChanges return a NotifyWatcher waiting for the
53+
// log forward syslog configuration to change.
54+
func (e *ModelWatcher) WatchForLogForwardConfigChanges() (watcher.NotifyWatcher, error) {
55+
// TODO(wallyworld) - lp:1602237 - this needs to have it's own backend implementation.
56+
// For now, we'll piggyback off the ModelConfig API.
57+
return e.WatchForModelConfigChanges()
58+
}
59+
60+
// LogForwardConfig returns the current log forward syslog configuration.
61+
func (e *ModelWatcher) LogForwardConfig() (*syslog.RawConfig, bool, error) {
62+
// TODO(wallyworld) - lp:1602237 - this needs to have it's own backend implementation.
63+
// For now, we'll piggyback off the ModelConfig API.
64+
modelConfig, err := e.ModelConfig()
65+
if err != nil {
66+
return nil, false, err
67+
}
68+
cfg, ok := modelConfig.LogFwdSyslog()
69+
return cfg, ok, nil
70+
}

apiserver/agent/agent.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type AgentAPIV2 struct {
2626
*common.PasswordChanger
2727
*common.RebootFlagClearer
2828
*common.ModelWatcher
29+
*common.ControllerConfigAPI
2930

3031
st *state.State
3132
auth common.Authorizer
@@ -42,11 +43,12 @@ func NewAgentAPIV2(st *state.State, resources *common.Resources, auth common.Aut
4243
return auth.AuthOwner, nil
4344
}
4445
return &AgentAPIV2{
45-
PasswordChanger: common.NewPasswordChanger(st, getCanChange),
46-
RebootFlagClearer: common.NewRebootFlagClearer(st, getCanChange),
47-
ModelWatcher: common.NewModelWatcher(st, resources, auth),
48-
st: st,
49-
auth: auth,
46+
PasswordChanger: common.NewPasswordChanger(st, getCanChange),
47+
RebootFlagClearer: common.NewRebootFlagClearer(st, getCanChange),
48+
ModelWatcher: common.NewModelWatcher(st, resources, auth),
49+
ControllerConfigAPI: common.NewControllerConfig(st),
50+
st: st,
51+
auth: auth,
5052
}, nil
5153
}
5254

cmd/jujud/agent/machine/manifolds.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -404,9 +404,10 @@ func Manifolds(config ManifoldsConfig) dependency.Manifolds {
404404
logForwarderName: ifFullyUpgraded(logforwarder.Manifold(logforwarder.ManifoldConfig{
405405
StateName: stateName,
406406
APICallerName: apiCallerName,
407-
SinkOpeners: []logforwarder.LogSinkFn{
408-
sinks.OpenSyslog,
409-
},
407+
Sinks: []logforwarder.LogSinkSpec{{
408+
Name: "juju-log-forward",
409+
OpenFn: sinks.OpenSyslog,
410+
}},
410411
})),
411412
}
412413
}

environs/config/config.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ const (
125125
// is primarily for enabling Juju to work cleanly in a closed network.
126126
CloudImageBaseURL = "cloudimg-base-url"
127127

128+
// LogForwardEnabled determines whether the log forward functionality is enabled.
129+
LogForwardEnabled = "logforward-enabled"
130+
128131
// LogFwdSyslogHost sets the hostname:port of the syslog server.
129132
LogFwdSyslogHost = "syslog-host"
130133

@@ -650,6 +653,11 @@ func (c *Config) LogFwdSyslog() (*syslog.RawConfig, bool) {
650653
partial := false
651654
var lfCfg syslog.RawConfig
652655

656+
if s, ok := c.defined[LogForwardEnabled]; ok {
657+
partial = true
658+
lfCfg.Enabled = s.(bool)
659+
}
660+
653661
if s, ok := c.defined[LogFwdSyslogHost]; ok && s != "" {
654662
partial = true
655663
lfCfg.Host = s.(string)
@@ -952,6 +960,7 @@ var alwaysOptional = schema.Defaults{
952960
"bootstrap-timeout": schema.Omit,
953961
"bootstrap-retry-delay": schema.Omit,
954962
"bootstrap-addresses-delay": schema.Omit,
963+
LogForwardEnabled: schema.Omit,
955964
LogFwdSyslogHost: schema.Omit,
956965
LogFwdSyslogCACert: schema.Omit,
957966
LogFwdSyslogClientCert: schema.Omit,
@@ -1347,8 +1356,13 @@ global or per instance security groups.`,
13471356
Type: environschema.Tattrs,
13481357
Group: environschema.EnvironGroup,
13491358
},
1359+
LogForwardEnabled: {
1360+
Description: `Whether syslog forwarding is enabled.`,
1361+
Type: environschema.Tbool,
1362+
Group: environschema.EnvironGroup,
1363+
},
13501364
LogFwdSyslogHost: {
1351-
Description: `LogFwdSyslogHost specifies the hostname:port of the syslog server.`,
1365+
Description: `The hostname:port of the syslog server.`,
13521366
Type: environschema.Tstring,
13531367
Group: environschema.EnvironGroup,
13541368
},

environs/config/config_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,7 @@ var configTests = []configTest{
636636
attrs: minimalConfigAttrs.Merge(testing.Attrs{
637637
"type": "my-type",
638638
"name": "my-name",
639+
"logforward-enabled": true,
639640
"syslog-host": "localhost:1234",
640641
"syslog-ca-cert": "abc",
641642
"syslog-client-cert": caCert,
@@ -648,6 +649,7 @@ var configTests = []configTest{
648649
attrs: minimalConfigAttrs.Merge(testing.Attrs{
649650
"type": "my-type",
650651
"name": "my-name",
652+
"logforward-enabled": true,
651653
"syslog-host": "localhost:1234",
652654
"syslog-ca-cert": invalidCACert,
653655
"syslog-client-cert": caCert,
@@ -658,6 +660,7 @@ var configTests = []configTest{
658660
about: "invalid syslog cert",
659661
useDefaults: config.UseDefaults,
660662
attrs: minimalConfigAttrs.Merge(testing.Attrs{
663+
"logforward-enabled": true,
661664
"syslog-host": "10.0.0.1:12345",
662665
"syslog-ca-cert": caCert,
663666
"syslog-client-cert": invalidCACert,
@@ -668,6 +671,7 @@ var configTests = []configTest{
668671
about: "invalid syslog key",
669672
useDefaults: config.UseDefaults,
670673
attrs: minimalConfigAttrs.Merge(testing.Attrs{
674+
"logforward-enabled": true,
671675
"syslog-host": "10.0.0.1:12345",
672676
"syslog-ca-cert": caCert,
673677
"syslog-client-cert": caCert,
@@ -678,6 +682,7 @@ var configTests = []configTest{
678682
about: "Mismatched syslog cert and key",
679683
useDefaults: config.UseDefaults,
680684
attrs: minimalConfigAttrs.Merge(testing.Attrs{
685+
"logforward-enabled": true,
681686
"syslog-host": "10.0.0.1:12345",
682687
"syslog-ca-cert": caCert,
683688
"syslog-client-cert": caCert,
@@ -690,6 +695,7 @@ var configTests = []configTest{
690695
attrs: minimalConfigAttrs.Merge(testing.Attrs{
691696
"type": "my-type",
692697
"name": "my-name",
698+
"logforward-enabled": true,
693699
"syslog-host": "localhost:1234",
694700
"syslog-ca-cert": testing.CACert,
695701
"syslog-client-cert": testing.ServerCert,
@@ -951,6 +957,10 @@ func (test configTest) check(c *gc.C, home *gitjujutesting.FakeHome) {
951957
c.Assert(cfg.AuthorizedKeys(), gc.Equals, keys)
952958

953959
lfCfg, hasLogCfg := cfg.LogFwdSyslog()
960+
if v, ok := test.attrs["logforward-enabled"].(bool); ok {
961+
c.Assert(hasLogCfg, jc.IsTrue)
962+
c.Assert(lfCfg.Enabled, gc.Equals, v)
963+
}
954964
if v, ok := test.attrs["syslog-ca-cert"].(string); v != "" {
955965
c.Assert(hasLogCfg, jc.IsTrue)
956966
c.Assert(lfCfg.CACert, gc.Equals, v)

featuretests/syslog_test.go

Lines changed: 91 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import (
3434

3535
type syslogSuite struct {
3636
agenttest.AgentSuite
37-
server *rfc5424test.Server
3837
logsCh logsender.LogRecordCh
3938
received chan rfc5424test.Message
4039
fakeEnsureMongo *agenttest.FakeEnsureMongo
@@ -65,32 +64,14 @@ func (s *syslogSuite) SetUpSuite(c *gc.C) {
6564
})
6665
}
6766

68-
func (s *syslogSuite) SetUpTest(c *gc.C) {
69-
if runtime.GOOS != "linux" {
70-
c.Skip(fmt.Sprintf("this test requires a controller, therefore does not support %q", runtime.GOOS))
71-
}
72-
currentSeries := series.HostSeries()
73-
osFromSeries, err := series.GetOSFromSeries(currentSeries)
74-
c.Assert(err, jc.ErrorIsNil)
75-
if osFromSeries != os.Ubuntu {
76-
c.Skip(fmt.Sprintf("this test requires a controller, therefore does not support OS %q only Ubuntu", osFromSeries.String()))
77-
}
78-
s.AgentSuite.SetUpTest(c)
79-
// TODO(perrito666) 200160701:
80-
// This needs to be done to stop the test from trying to install mongo
81-
// while running, but it is a huge footprint for such little benefit.
82-
// This test should not need JujuConnSuite or AgentSuite.
83-
s.fakeEnsureMongo = agenttest.InstallFakeEnsureMongo(s)
84-
85-
done := make(chan struct{})
86-
s.received = make(chan rfc5424test.Message)
87-
s.server = rfc5424test.NewServer(rfc5424test.HandlerFunc(func(msg rfc5424test.Message) {
67+
func (s *syslogSuite) createSyslogServer(c *gc.C, received chan rfc5424test.Message, done chan struct{}) string {
68+
server := rfc5424test.NewServer(rfc5424test.HandlerFunc(func(msg rfc5424test.Message) {
8869
select {
89-
case s.received <- msg:
70+
case received <- msg:
9071
case <-done:
9172
}
9273
}))
93-
s.AddCleanup(func(*gc.C) { s.server.Close() })
74+
s.AddCleanup(func(*gc.C) { server.Close() })
9475
s.AddCleanup(func(*gc.C) { close(done) })
9576

9677
serverCert, err := tls.X509KeyPair(
@@ -102,17 +83,42 @@ func (s *syslogSuite) SetUpTest(c *gc.C) {
10283
c.Assert(err, jc.ErrorIsNil)
10384
clientCAs := x509.NewCertPool()
10485
clientCAs.AddCert(caCert)
105-
s.server.TLS = &tls.Config{
86+
server.TLS = &tls.Config{
10687
Certificates: []tls.Certificate{serverCert},
10788
ClientCAs: clientCAs,
10889
}
109-
s.server.StartTLS()
90+
server.StartTLS()
11091

11192
// We must use "localhost", as the certificate does not
11293
// have any IP SANs.
113-
port := s.server.Listener.Addr().(*net.TCPAddr).Port
94+
port := server.Listener.Addr().(*net.TCPAddr).Port
11495
addr := net.JoinHostPort("localhost", fmt.Sprint(port))
96+
return addr
97+
}
11598

99+
func (s *syslogSuite) SetUpTest(c *gc.C) {
100+
if runtime.GOOS != "linux" {
101+
c.Skip(fmt.Sprintf("this test requires a controller, therefore does not support %q", runtime.GOOS))
102+
}
103+
currentSeries := series.HostSeries()
104+
osFromSeries, err := series.GetOSFromSeries(currentSeries)
105+
c.Assert(err, jc.ErrorIsNil)
106+
if osFromSeries != os.Ubuntu {
107+
c.Skip(fmt.Sprintf("this test requires a controller, therefore does not support OS %q only Ubuntu", osFromSeries.String()))
108+
}
109+
s.AgentSuite.SetUpTest(c)
110+
// TODO(perrito666) 200160701:
111+
// This needs to be done to stop the test from trying to install mongo
112+
// while running, but it is a huge footprint for such little benefit.
113+
// This test should not need JujuConnSuite or AgentSuite.
114+
s.fakeEnsureMongo = agenttest.InstallFakeEnsureMongo(s)
115+
116+
done := make(chan struct{})
117+
s.received = make(chan rfc5424test.Message)
118+
addr := s.createSyslogServer(c, s.received, done)
119+
120+
// Leave log forwarding disabled initially, it will be enabled
121+
// via a model config update in the test.
116122
err = s.State.UpdateModelConfig(map[string]interface{}{
117123
"syslog-host": addr,
118124
"syslog-ca-cert": coretesting.CACert,
@@ -143,23 +149,23 @@ func (s *syslogSuite) sendRecord(c *gc.C, rec *logsender.LogRecord) {
143149
}
144150
}
145151

146-
func (s *syslogSuite) popMessagesUntil(c *gc.C, expected string) rfc5424test.Message {
152+
func (s *syslogSuite) popMessagesUntil(c *gc.C, expected string, received chan rfc5424test.Message) rfc5424test.Message {
147153
re, err := regexp.Compile(expected)
148154
c.Assert(err, jc.ErrorIsNil)
149155

150156
c.Logf("popping messages")
151157
for {
152-
msg := s.nextMessage(c)
158+
msg := s.nextMessage(c, received)
153159
c.Logf("message: %+v", msg)
154160
if re.MatchString(msg.Message) {
155161
return msg
156162
}
157163
}
158164
}
159165

160-
func (s *syslogSuite) nextMessage(c *gc.C) rfc5424test.Message {
166+
func (s *syslogSuite) nextMessage(c *gc.C, received chan rfc5424test.Message) rfc5424test.Message {
161167
select {
162-
case msg, ok := <-s.received:
168+
case msg, ok := <-received:
163169
c.Assert(ok, jc.IsTrue)
164170
return msg
165171
case <-time.After(coretesting.LongWait):
@@ -168,6 +174,22 @@ func (s *syslogSuite) nextMessage(c *gc.C) rfc5424test.Message {
168174
return rfc5424test.Message{}
169175
}
170176

177+
func (s *syslogSuite) assertLogRecordForwarded(c *gc.C, received chan rfc5424test.Message) {
178+
// Pop off all initial log records.
179+
s.sendRecord(c, s.newRecord("<stop here>"))
180+
s.popMessagesUntil(c, `.*<stop here>`, received)
181+
182+
// Ensure that a specific log record gets forwarded.
183+
rec := s.newRecord("something happened!")
184+
rec.Time = time.Date(2099, time.June, 1, 23, 2, 1, 23, time.UTC)
185+
s.sendRecord(c, rec)
186+
msg := s.popMessagesUntil(c, `something happened!`, received)
187+
expected := `<11>1 2099-06-01T23:02:01.000000023Z machine-0.%s jujud-machine-agent-%s - - [origin enterpriseID="28978" sofware="jujud-machine-agent" swVersion="%s"][model@28978 controller-uuid="%s" model-uuid="%s"][log@28978 module="juju.featuretests.syslog" source="syslog_test.go:99999"] something happened!`
188+
modelID := coretesting.ModelTag.Id()
189+
ctlrID := modelID
190+
c.Check(msg.Message, gc.Equals, fmt.Sprintf(expected, modelID, modelID[:28], version.Current, ctlrID, modelID))
191+
}
192+
171193
func (s *syslogSuite) TestLogRecordForwarded(c *gc.C) {
172194
// Create a machine and an agent for it.
173195
m, password := s.Factory.MakeMachineReturningPassword(c, &factory.MachineParams{
@@ -187,17 +209,44 @@ func (s *syslogSuite) TestLogRecordForwarded(c *gc.C) {
187209
go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }()
188210
defer a.Stop()
189211

190-
// Pop off all initial log records.
191-
s.sendRecord(c, s.newRecord("<stop here>"))
192-
s.popMessagesUntil(c, `.*<stop here>`)
212+
err := s.State.UpdateModelConfig(map[string]interface{}{
213+
"logforward-enabled": true,
214+
}, nil, nil)
215+
c.Assert(err, jc.ErrorIsNil)
193216

194-
// Ensure that a specific log record gets forwarded.
195-
rec := s.newRecord("something happened!")
196-
rec.Time = time.Date(2099, time.June, 1, 23, 2, 1, 23, time.UTC)
197-
s.sendRecord(c, rec)
198-
msg := s.popMessagesUntil(c, `something happened!`)
199-
expected := `<11>1 2099-06-01T23:02:01.000000023Z machine-0.%s jujud-machine-agent-%s - - [origin enterpriseID="28978" sofware="jujud-machine-agent" swVersion="%s"][model@28978 controller-uuid="%s" model-uuid="%s"][log@28978 module="juju.featuretests.syslog" source="syslog_test.go:99999"] something happened!`
200-
modelID := coretesting.ModelTag.Id()
201-
ctlrID := modelID
202-
c.Check(msg.Message, gc.Equals, fmt.Sprintf(expected, modelID, modelID[:28], version.Current, ctlrID, modelID))
217+
s.assertLogRecordForwarded(c, s.received)
218+
}
219+
220+
func (s *syslogSuite) TestConfigChange(c *gc.C) {
221+
// Create a machine and an agent for it.
222+
m, password := s.Factory.MakeMachineReturningPassword(c, &factory.MachineParams{
223+
Nonce: agent.BootstrapNonce,
224+
Jobs: []state.MachineJob{state.JobManageModel},
225+
})
226+
227+
s.PrimeAgent(c, m.Tag(), password)
228+
agentConf := agentcmd.NewAgentConf(s.DataDir())
229+
agentConf.ReadConfig(m.Tag().String())
230+
231+
machineAgentFactory := agentcmd.MachineAgentFactoryFn(agentConf, s.logsCh, c.MkDir())
232+
a := machineAgentFactory(m.Id())
233+
234+
// Ensure there's no logs to begin with.
235+
// Start the agent.
236+
go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }()
237+
defer a.Stop()
238+
239+
done := make(chan struct{})
240+
received := make(chan rfc5424test.Message)
241+
addr := s.createSyslogServer(c, received, done)
242+
243+
err := s.State.UpdateModelConfig(map[string]interface{}{
244+
"logforward-enabled": true,
245+
"syslog-host": addr,
246+
"syslog-ca-cert": coretesting.CACert,
247+
"syslog-client-cert": coretesting.ServerCert,
248+
"syslog-client-key": coretesting.ServerKey,
249+
}, nil, nil)
250+
c.Assert(err, jc.ErrorIsNil)
251+
s.assertLogRecordForwarded(c, received)
203252
}

0 commit comments

Comments
 (0)