Merge lp:~axwalk/juju-core/lp1281071-rsyslog-tls into lp:~go-bot/juju-core/trunk

Proposed by Andrew Wilkins
Status: Merged
Merge reported by: John A Meinel
Merged at revision: not available
Proposed branch: lp:~axwalk/juju-core/lp1281071-rsyslog-tls
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 2006 lines (+660/-331)
45 files modified
agent/agent.go (+0/-1)
cmd/juju/environment_test.go (+0/-1)
cmd/jujud/machine.go (+16/-10)
cmd/jujud/machine_test.go (+27/-0)
cmd/jujud/unit.go (+4/-0)
container/testing/common.go (+0/-1)
environs/cloudinit.go (+0/-10)
environs/cloudinit/cloudinit.go (+0/-40)
environs/cloudinit/cloudinit_test.go (+13/-33)
environs/cloudinit_test.go (+0/-5)
environs/config/config.go (+20/-4)
environs/config/config_test.go (+0/-9)
log/syslog/config.go (+46/-14)
log/syslog/config_test.go (+12/-0)
log/syslog/testing/syslogconf.go (+21/-4)
provider/azure/customdata_test.go (+0/-2)
provider/local/config.go (+0/-1)
provider/local/environ.go (+9/-6)
provider/local/environprovider.go (+6/-7)
state/address.go (+0/-2)
state/api/params/params.go (+5/-2)
state/api/provisioner/provisioner_test.go (+0/-1)
state/api/rsyslog/package_test.go (+14/-0)
state/api/rsyslog/rsyslog.go (+44/-0)
state/api/rsyslog/rsyslog_test.go (+33/-0)
state/api/state.go (+6/-0)
state/apiserver/deployer/deployer.go (+0/-1)
state/apiserver/provisioner/provisioner.go (+0/-1)
state/apiserver/provisioner/provisioner_test.go (+0/-1)
state/apiserver/root.go (+12/-0)
state/apiserver/rsyslog/package_test.go (+14/-0)
state/apiserver/rsyslog/rsyslog.go (+56/-0)
state/apiserver/rsyslog/rsyslog_test.go (+48/-0)
upgrades/export_test.go (+0/-2)
upgrades/rsyslogconf.go (+0/-50)
upgrades/rsyslogconf_test.go (+0/-72)
upgrades/steps118.go (+0/-10)
upgrades/steps118_test.go (+1/-3)
worker/deployer/export_test.go (+2/-3)
worker/deployer/simple.go (+1/-18)
worker/deployer/simple_test.go (+4/-13)
worker/provisioner/kvm-broker.go (+0/-1)
worker/provisioner/lxc-broker.go (+0/-1)
worker/provisioner/lxc-broker_test.go (+1/-2)
worker/rsyslog/worker.go (+245/-0)
To merge this branch: bzr merge lp:~axwalk/juju-core/lp1281071-rsyslog-tls
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+207889@code.launchpad.net

Description of the change

Implement rsyslog TLS support

This changes the rsyslog configuration
we generate to use TLS. We install the
rsyslog-gnutls package and generate a
new CA cert, server cert and key
specifically for rsyslog.

This completely changes the way rsyslog
configuration is managed. Now, instead
of writing at bootstrap time and having
an upgrade step, we have a worker that
writes the syslog config whenever syslog
parameters change. The state server will
generate certificates and propagate the
CA cert to other agents via environ config.

There are some other miscellaneous changes:
  - we now use reliable forwarding, as
    otherwise the machine agent and unit
    agent will restart rsyslog while
    log messages are buffered causing
    message loss
  - dedicated upgrades for rsyslog are
    redundant and removed. The new worker
    upgrades machine and unit agent rsyslog
    automatically.
  - syslog-port can now be changed, and must
    be changed to work around the privilege
    drop race in rsyslog 5.x (this is the
    sole motivation)
  - the ryslog config path is no longer
    populated into agent.conf, and we
    no longer use the existing value to
    perform cleanups. The worker is now
    responsible for cleaning up the config
    file on tear-down.
  - the local provider symlinks machine-0.log
    into /var/log/juju$namespace so that we
    do not need any configuration other than
    the existing namespace to determine log
    location

TODO(axw) tests in worker/rsyslog
TODO(axw) tests in state/api/rsyslog
TODO(axw) tests in state/apiserver/rsyslog

Fixes lp:1281071

https://codereview.appspot.com/68070044/

To post a comment you must log in.
Revision history for this message
Andrew Wilkins (axwalk) wrote :

Reviewers: mp+207889_code.launchpad.net,

Message:
Please take a look.

Description:
Implement rsyslog TLS support

A new config attribute, syslog-tls, is introduced.
This is true for all new environments except for
the local provider, and false (rather, omitted) for
existing environments and any local provider
environments. As with syslog-port, this is immutable.

If syslog-tls is true, then rsyslog-gnutls is
installed and used by the rsyslog configuration that
we generate. The syslog configuration conditionally
uses the TLS transport.

We cannot automatically upgrade existing environments
without additional tooling, as existing server certs
are incompatible with GnuTLS (incorrect Key Usage).

Fixes lp:1281071

https://code.launchpad.net/~axwalk/juju-core/lp1281071-rsyslog-tls/+merge/207889

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/68070044/

Affected files (+334, -64 lines):
   A [revision details]
   M environs/cloudinit.go
   M environs/cloudinit/cloudinit.go
   M environs/cloudinit/cloudinit_test.go
   M environs/config/config.go
   M environs/config/config_test.go
   M log/syslog/config.go
   M log/syslog/config_test.go
   M log/syslog/testing/syslogconf.go
   M provider/local/environprovider.go
   M state/address.go
   M state/api/params/params.go
   M state/apiserver/deployer/deployer.go
   M state/apiserver/provisioner/provisioner.go
   M upgrades/rsyslogconf.go
   M upgrades/rsyslogconf_test.go
   M worker/deployer/export_test.go
   M worker/deployer/simple.go
   M worker/provisioner/kvm-broker.go
   M worker/provisioner/lxc-broker.go

Revision history for this message
Tim Penhey (thumper) wrote :

We already need to rewrite the syslog config files as part of the 1.18
upgrade process as they are all out of date and wrong, so this is
something we need to address anyway.

I'd like to see us move from UDP to TCP for the logging.

I'm a little surprised that I have never been hit by the inability for a
machine to listen on the current port.

https://codereview.appspot.com/68070044/diff/1/environs/cloudinit/cloudinit.go
File environs/cloudinit/cloudinit.go (right):

https://codereview.appspot.com/68070044/diff/1/environs/cloudinit/cloudinit.go#newcode434
environs/cloudinit/cloudinit.go:434: configRenderer.TLSCACertPath =
path.Join(cfg.LogDir, "ca.pem")
Do we really want to reuse the same cert? I strongly suggest that we
have different certs / keys for different jobs.

https://codereview.appspot.com/68070044/diff/1/log/syslog/config.go
File log/syslog/config.go (right):

https://codereview.appspot.com/68070044/diff/1/log/syslog/config.go#newcode38
log/syslog/config.go:38: // File="{{logDir}}/all-machines.log"
Argh... you can't just change this.

rsyslog has an apparmor profile that restricts where it can write files.
  This was a source of many hours of searching.

Needs to be /var/log/** somewhere.

https://codereview.appspot.com/68070044/diff/1/log/syslog/config.go#newcode64
log/syslog/config.go:64: $ModLoad imudp
I think we should actually be using TCP not UDP for log messages anyway.
  There is a built in store and forward mechanism.

https://codereview.appspot.com/68070044/diff/1/log/syslog/config.go#newcode65
log/syslog/config.go:65: $UDPServerRun {{portNumber}}
Why had I not been hit by this port number problem?

https://codereview.appspot.com/68070044/diff/1/log/syslog/config.go#newcode198
log/syslog/config.go:198: addressPrefix = "@@"
If we switch to TCP for all, we can avoid this.

https://codereview.appspot.com/68070044/diff/1/log/syslog/testing/syslogconf.go
File log/syslog/testing/syslogconf.go (right):

https://codereview.appspot.com/68070044/diff/1/log/syslog/testing/syslogconf.go#newcode25
log/syslog/testing/syslogconf.go:25: $DefaultNetstreamDriverCAFile
/var/log/juju/ca.pem
Oh? so this isn't the same as the mongo cert?

https://codereview.appspot.com/68070044/

Revision history for this message
Andrew Wilkins (axwalk) wrote :

I'll start on a new worker, and do the certificate stuff at the same
time.

If we don't mind installing rsyslog-gnutls on machine-0 for local, we
can remove the syslog-tls config attr and make TLS mandatory.

https://codereview.appspot.com/68070044/diff/1/environs/cloudinit/cloudinit.go
File environs/cloudinit/cloudinit.go (right):

https://codereview.appspot.com/68070044/diff/1/environs/cloudinit/cloudinit.go#newcode434
environs/cloudinit/cloudinit.go:434: configRenderer.TLSCACertPath =
path.Join(cfg.LogDir, "ca.pem")
On 2014/02/24 21:48:29, thumper wrote:
> Do we really want to reuse the same cert? I strongly suggest that we
have
> different certs / keys for different jobs.

I will update the code to generate a new CA cert/key for rsyslog, and
use that to sign a new server cert. The CA cert will be populated into
environment config, which the rsyslog worker will watch for.

https://codereview.appspot.com/68070044/diff/1/log/syslog/config.go
File log/syslog/config.go (right):

https://codereview.appspot.com/68070044/diff/1/log/syslog/config.go#newcode38
log/syslog/config.go:38: // File="{{logDir}}/all-machines.log"
On 2014/02/24 21:48:29, thumper wrote:
> Argh... you can't just change this.

I tested it and it works on both precise and trusty.

> rsyslog has an apparmor profile that restricts where it can write
files. This
> was a source of many hours of searching.

I know, and I did it knowingly. It is writing to /var/log/juju, on
account of the symlink.

> Needs to be /var/log/** somewhere.

If we hard-code it, then the SyslogConfig parameters ought to be
changed. Otherwise it looks like it's going to write to the configured
log-dir, but doesn't.

https://codereview.appspot.com/68070044/diff/1/log/syslog/config.go#newcode64
log/syslog/config.go:64: $ModLoad imudp
On 2014/02/24 21:48:29, thumper wrote:
> I think we should actually be using TCP not UDP for log messages
anyway. There
> is a built in store and forward mechanism.

Probably not a bad idea, but let's please leave that until after TLS is
sorted.

https://codereview.appspot.com/68070044/diff/1/log/syslog/config.go#newcode65
log/syslog/config.go:65: $UDPServerRun {{portNumber}}
On 2014/02/24 21:48:29, thumper wrote:
> Why had I not been hit by this port number problem?

I don't know the specifics of the bug. All I know is that it only hit me
when I was using TLS, and not UDP.

https://codereview.appspot.com/68070044/diff/1/log/syslog/testing/syslogconf.go
File log/syslog/testing/syslogconf.go (right):

https://codereview.appspot.com/68070044/diff/1/log/syslog/testing/syslogconf.go#newcode25
log/syslog/testing/syslogconf.go:25: $DefaultNetstreamDriverCAFile
/var/log/juju/ca.pem
On 2014/02/24 21:48:29, thumper wrote:
> Oh? so this isn't the same as the mongo cert?

It is, it's just copied to another place to allow for using a different
one.

https://codereview.appspot.com/68070044/

Revision history for this message
Andrew Wilkins (axwalk) wrote :
Revision history for this message
Andrew Wilkins (axwalk) wrote :

On 2014/02/25 14:22:14, axw wrote:
> Please take a look.

This CL has jumped the shark, I guess, but I wanted to get it out there
to get feedback.
If the direction is good I will try to pare it back, keeping the
existing environs/cloudinit and related code, and remove all that in a
followup.

https://codereview.appspot.com/68070044/

Revision history for this message
Andrew Wilkins (axwalk) wrote :

This is *not* merged, lying bot! I created another branch and merged this in, removing all the bits I didn't care about. Bot merged the second branch and thought this one was merged too.

Revision history for this message
John A Meinel (jameinel) wrote :

This revision is in the history, though maybe it didn't merge like it looks here?

Revision history for this message
Andrew Wilkins (axwalk) wrote :

On 2014/02/25 14:27:20, axw wrote:
> On 2014/02/25 14:22:14, axw wrote:
> > Please take a look.

> This CL has jumped the shark, I guess, but I wanted to get it out
there to get
> feedback.
> If the direction is good I will try to pare it back, keeping the
existing
> environs/cloudinit and related code, and remove all that in a
followup.

This is superseded by https://codereview.appspot.com/68930045

https://codereview.appspot.com/68070044/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'agent/agent.go'
2--- agent/agent.go 2014-02-18 05:43:06 +0000
3+++ agent/agent.go 2014-02-25 14:03:09 +0000
4@@ -30,7 +30,6 @@
5 StorageAddr = "STORAGE_ADDR"
6 AgentServiceName = "AGENT_SERVICE_NAME"
7 MongoServiceName = "MONGO_SERVICE_NAME"
8- RsyslogConfPath = "RSYSLOG_CONF_PATH"
9 BootstrapJobs = "BOOTSTRAP_JOBS"
10 )
11
12
13=== modified file 'cmd/juju/environment_test.go'
14--- cmd/juju/environment_test.go 2013-11-28 09:11:12 +0000
15+++ cmd/juju/environment_test.go 2014-02-25 14:03:09 +0000
16@@ -167,7 +167,6 @@
17 "firewall-mode": "global",
18 "state-port": "1",
19 "api-port": "666",
20- "syslog-port": "42",
21 }
22
23 func (s *SetEnvironmentSuite) TestImmutableConfigValues(c *gc.C) {
24
25=== modified file 'cmd/jujud/machine.go'
26--- cmd/jujud/machine.go 2014-02-20 08:23:40 +0000
27+++ cmd/jujud/machine.go 2014-02-25 14:03:09 +0000
28@@ -18,7 +18,6 @@
29 "launchpad.net/juju-core/cmd"
30 "launchpad.net/juju-core/container/kvm"
31 "launchpad.net/juju-core/instance"
32- "launchpad.net/juju-core/log/syslog"
33 "launchpad.net/juju-core/names"
34 "launchpad.net/juju-core/provider"
35 "launchpad.net/juju-core/state"
36@@ -42,6 +41,7 @@
37 "launchpad.net/juju-core/worker/minunitsworker"
38 "launchpad.net/juju-core/worker/provisioner"
39 "launchpad.net/juju-core/worker/resumer"
40+ "launchpad.net/juju-core/worker/rsyslog"
41 "launchpad.net/juju-core/worker/terminationworker"
42 "launchpad.net/juju-core/worker/upgrader"
43 )
44@@ -65,6 +65,8 @@
45 Conf AgentConf
46 MachineId string
47 runner worker.Runner
48+
49+ rsyslogConfPath string
50 }
51
52 // Info returns usage information for the command.
53@@ -157,6 +159,8 @@
54 return err
55 }
56
57+var newRsyslogConfigWorker = rsyslog.NewRsyslogConfigWorker
58+
59 // APIWorker returns a Worker that connects to the API and starts any
60 // workers that need an API connection.
61 //
62@@ -174,6 +178,14 @@
63 break
64 }
65 }
66+ rsyslogMode := rsyslog.RsyslogModeForwarding
67+ for _, job := range entity.Jobs() {
68+ if job == params.JobManageEnviron {
69+ rsyslogMode = rsyslog.RsyslogModeAccumulate
70+ break
71+ }
72+ }
73+
74 runner := newRunner(connectionIsFatal(st), moreImportant)
75 runner.StartWorker("machiner", func() (worker.Worker, error) {
76 return machiner.NewMachiner(st.Machiner(), agentConfig), nil
77@@ -187,6 +199,9 @@
78 runner.StartWorker("machineenvironmentworker", func() (worker.Worker, error) {
79 return machineenvironmentworker.NewMachineEnvironmentWorker(st.Environment(), agentConfig), nil
80 })
81+ runner.StartWorker("rsyslog", func() (worker.Worker, error) {
82+ return newRsyslogConfigWorker(st.Rsyslog(), agentConfig, rsyslogMode)
83+ })
84
85 // If not a local provider bootstrap machine, start the worker to manage SSH keys.
86 providerType := agentConfig.Value(agent.ProviderType)
87@@ -385,15 +400,6 @@
88 errors = append(errors, fmt.Errorf("cannot remove service %q: %v", agentServiceName, err))
89 }
90 }
91- // Remove the rsyslog conf file and restart rsyslogd.
92- if rsyslogConfPath := a.Conf.config.Value(agent.RsyslogConfPath); rsyslogConfPath != "" {
93- if err := os.Remove(rsyslogConfPath); err != nil {
94- errors = append(errors, err)
95- }
96- if err := syslog.Restart(); err != nil {
97- errors = append(errors, err)
98- }
99- }
100 // Remove the juju-run symlink.
101 if err := os.Remove(jujuRun); err != nil && !os.IsNotExist(err) {
102 errors = append(errors, err)
103
104=== modified file 'cmd/jujud/machine_test.go'
105--- cmd/jujud/machine_test.go 2014-02-19 02:25:30 +0000
106+++ cmd/jujud/machine_test.go 2014-02-25 14:03:09 +0000
107@@ -28,6 +28,7 @@
108 "launchpad.net/juju-core/state/api"
109 apideployer "launchpad.net/juju-core/state/api/deployer"
110 "launchpad.net/juju-core/state/api/params"
111+ apirsyslog "launchpad.net/juju-core/state/api/rsyslog"
112 charmtesting "launchpad.net/juju-core/state/apiserver/charmrevisionupdater/testing"
113 statetesting "launchpad.net/juju-core/state/testing"
114 "launchpad.net/juju-core/state/watcher"
115@@ -40,10 +41,12 @@
116 "launchpad.net/juju-core/utils/ssh"
117 sshtesting "launchpad.net/juju-core/utils/ssh/testing"
118 "launchpad.net/juju-core/version"
119+ "launchpad.net/juju-core/worker"
120 "launchpad.net/juju-core/worker/authenticationworker"
121 "launchpad.net/juju-core/worker/deployer"
122 "launchpad.net/juju-core/worker/instancepoller"
123 "launchpad.net/juju-core/worker/machineenvironmentworker"
124+ "launchpad.net/juju-core/worker/rsyslog"
125 "launchpad.net/juju-core/worker/upgrader"
126 )
127
128@@ -750,6 +753,30 @@
129 c.Assert(err, jc.Satisfies, os.IsNotExist)
130 }
131
132+func (s *MachineSuite) TestMachineAgentRsyslogManageEnviron(c *gc.C) {
133+ s.testMachineAgentRsyslogConfigWorker(c, state.JobManageEnviron, rsyslog.RsyslogModeAccumulate)
134+}
135+
136+func (s *MachineSuite) TestMachineAgentRsyslogHostUnits(c *gc.C) {
137+ s.testMachineAgentRsyslogConfigWorker(c, state.JobHostUnits, rsyslog.RsyslogModeForwarding)
138+}
139+
140+func (s *MachineSuite) testMachineAgentRsyslogConfigWorker(c *gc.C, job state.MachineJob, expectedMode rsyslog.RsyslogMode) {
141+ created := make(chan rsyslog.RsyslogMode, 1)
142+ s.PatchValue(&newRsyslogConfigWorker, func(_ *apirsyslog.State, _ agent.Config, mode rsyslog.RsyslogMode) (worker.Worker, error) {
143+ created <- mode
144+ return worker.NewRunner(isFatal, moreImportant), nil
145+ })
146+ s.assertJobWithAPI(c, job, func(conf agent.Config, st *api.State) {
147+ select {
148+ case <-time.After(testing.LongWait):
149+ c.Fatalf("timeout while waiting for rsyslog worker to be created")
150+ case mode := <-created:
151+ c.Assert(mode, gc.Equals, expectedMode)
152+ }
153+ })
154+}
155+
156 // MachineWithCharmsSuite provides infrastructure for tests which need to
157 // work with charms.
158 type MachineWithCharmsSuite struct {
159
160=== modified file 'cmd/jujud/unit.go'
161--- cmd/jujud/unit.go 2014-02-18 00:15:30 +0000
162+++ cmd/jujud/unit.go 2014-02-25 14:03:09 +0000
163@@ -15,6 +15,7 @@
164 "launchpad.net/juju-core/state"
165 "launchpad.net/juju-core/worker"
166 workerlogger "launchpad.net/juju-core/worker/logger"
167+ "launchpad.net/juju-core/worker/rsyslog"
168 "launchpad.net/juju-core/worker/uniter"
169 "launchpad.net/juju-core/worker/upgrader"
170 )
171@@ -94,6 +95,9 @@
172 runner.StartWorker("uniter", func() (worker.Worker, error) {
173 return uniter.NewUniter(st.Uniter(), entity.Tag(), dataDir), nil
174 })
175+ runner.StartWorker("rsyslog", func() (worker.Worker, error) {
176+ return rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), agentConfig, rsyslog.RsyslogModeForwarding)
177+ })
178 return newCloseWorker(runner, st), nil
179 }
180
181
182=== modified file 'container/testing/common.go'
183--- container/testing/common.go 2013-12-04 04:33:23 +0000
184+++ container/testing/common.go 2014-02-25 14:03:09 +0000
185@@ -21,7 +21,6 @@
186 stateInfo := jujutesting.FakeStateInfo(machineId)
187 apiInfo := jujutesting.FakeAPIInfo(machineId)
188 machineConfig := environs.NewMachineConfig(machineId, "fake-nonce", stateInfo, apiInfo)
189- machineConfig.SyslogPort = 2345
190 machineConfig.Tools = &tools.Tools{
191 Version: version.MustParseBinary("2.3.4-foo-bar"),
192 URL: "http://tools.testing.invalid/2.3.4-foo-bar.tgz",
193
194=== modified file 'environs/cloudinit.go'
195--- environs/cloudinit.go 2014-02-13 05:22:01 +0000
196+++ environs/cloudinit.go 2014-02-25 14:03:09 +0000
197@@ -31,12 +31,6 @@
198 // CloudInitOutputLog is the default cloud-init-output.log file path.
199 const CloudInitOutputLog = "/var/log/cloud-init-output.log"
200
201-// DefaultRsyslogConfPath is the default rsyslogd conf file path.
202-const DefaultRsyslogConfPath = "/etc/rsyslog.d/25-juju.conf"
203-
204-// Override for testing.
205-var RsyslogConfPath = DefaultRsyslogConfPath
206-
207 // MongoServiceName is the default Upstart service name for Mongo.
208 const MongoServiceName = "juju-db"
209
210@@ -50,7 +44,6 @@
211 DataDir: DataDir,
212 LogDir: LogDir,
213 CloudInitOutputLog: CloudInitOutputLog,
214- RsyslogConfPath: RsyslogConfPath,
215 MachineAgentServiceName: "jujud-" + names.MachineTag(machineID),
216 MongoServiceName: MongoServiceName,
217
218@@ -85,7 +78,6 @@
219 func PopulateMachineConfig(mcfg *cloudinit.MachineConfig,
220 providerType, authorizedKeys string,
221 sslHostnameVerification bool,
222- syslogPort int,
223 proxy, aptProxy osenv.ProxySettings,
224 ) error {
225 if authorizedKeys == "" {
226@@ -98,7 +90,6 @@
227 mcfg.AgentEnvironment[agent.ProviderType] = providerType
228 mcfg.AgentEnvironment[agent.ContainerType] = string(mcfg.MachineContainerType)
229 mcfg.DisableSSLHostnameVerification = !sslHostnameVerification
230- mcfg.SyslogPort = syslogPort
231 mcfg.ProxySettings = proxy
232 mcfg.AptProxySettings = aptProxy
233 return nil
234@@ -122,7 +113,6 @@
235 cfg.Type(),
236 cfg.AuthorizedKeys(),
237 cfg.SSLHostnameVerification(),
238- cfg.SyslogPort(),
239 cfg.ProxySettings(),
240 cfg.AptProxySettings(),
241 ); err != nil {
242
243=== modified file 'environs/cloudinit/cloudinit.go'
244--- environs/cloudinit/cloudinit.go 2014-02-20 15:03:08 +0000
245+++ environs/cloudinit/cloudinit.go 2014-02-25 14:03:09 +0000
246@@ -20,7 +20,6 @@
247 "launchpad.net/juju-core/environs/config"
248 "launchpad.net/juju-core/instance"
249 "launchpad.net/juju-core/juju/osenv"
250- "launchpad.net/juju-core/log/syslog"
251 "launchpad.net/juju-core/names"
252 "launchpad.net/juju-core/state"
253 "launchpad.net/juju-core/state/api"
254@@ -63,10 +62,6 @@
255 // if StateServer is true.
256 APIPort int
257
258- // SyslogPort specifies the port number that will be used when
259- // sending the log messages using rsyslog.
260- SyslogPort int
261-
262 // StateInfo holds the means for the new instance to communicate with the
263 // juju state. Unless the new machine is running a state server (StateServer is
264 // set), there must be at least one state server address supplied.
265@@ -95,10 +90,6 @@
266 // LogDir holds the directory that juju logs will be written to.
267 LogDir string
268
269- // RsyslogConfPath is the path to the rsyslogd conf file written
270- // for configuring distributed logging.
271- RsyslogConfPath string
272-
273 // CloudInitOutputLog specifies the path to the output log for cloud-init.
274 // The directory containing the log file must already exist.
275 CloudInitOutputLog string
276@@ -317,10 +308,6 @@
277 fmt.Sprintf("printf %%s %s > $bin/downloaded-tools.txt", shquote(string(toolsJson))),
278 )
279
280- if err := cfg.addLogging(c); err != nil {
281- return err
282- }
283-
284 // We add the machine agent's configuration info
285 // before running bootstrap-state so that bootstrap-state
286 // has a chance to rerwrite it to change the password.
287@@ -401,26 +388,6 @@
288 return cfg.addMachineAgentToBoot(c, machineTag, cfg.MachineId)
289 }
290
291-func (cfg *MachineConfig) addLogging(c *cloudinit.Config) error {
292- namespace := cfg.AgentEnvironment[agent.Namespace]
293- var configRenderer *syslog.SyslogConfig
294- if cfg.StateServer {
295- configRenderer = syslog.NewAccumulateConfig(
296- names.MachineTag(cfg.MachineId), cfg.SyslogPort, namespace)
297- } else {
298- configRenderer = syslog.NewForwardConfig(
299- names.MachineTag(cfg.MachineId), cfg.SyslogPort, namespace, cfg.stateHostAddrs())
300- }
301- configRenderer.LogDir = cfg.LogDir
302- content, err := configRenderer.Render()
303- if err != nil {
304- return err
305- }
306- c.AddFile(cfg.RsyslogConfPath, string(content), 0644)
307- c.AddRunCmd("restart rsyslog")
308- return nil
309-}
310-
311 func (cfg *MachineConfig) dataFile(name string) string {
312 return path.Join(cfg.DataDir, name)
313 }
314@@ -464,7 +431,6 @@
315 if err != nil {
316 return nil, err
317 }
318- acfg.SetValue(agent.RsyslogConfPath, cfg.RsyslogConfPath)
319 acfg.SetValue(agent.AgentServiceName, cfg.MachineAgentServiceName)
320 if cfg.StateServer {
321 acfg.SetValue(agent.MongoServiceName, cfg.MongoServiceName)
322@@ -656,9 +622,6 @@
323 if cfg.CloudInitOutputLog == "" {
324 return fmt.Errorf("missing cloud-init output log path")
325 }
326- if cfg.RsyslogConfPath == "" {
327- return fmt.Errorf("missing rsyslog.d conf path")
328- }
329 if cfg.Tools == nil {
330 return fmt.Errorf("missing tools")
331 }
332@@ -674,9 +637,6 @@
333 if cfg.APIInfo == nil {
334 return fmt.Errorf("missing API info")
335 }
336- if cfg.SyslogPort == 0 {
337- return fmt.Errorf("missing syslog port")
338- }
339 if len(cfg.APIInfo.CACert) == 0 {
340 return fmt.Errorf("missing API CA certificate")
341 }
342
343=== modified file 'environs/cloudinit/cloudinit_test.go'
344--- environs/cloudinit/cloudinit_test.go 2014-02-21 17:32:31 +0000
345+++ environs/cloudinit/cloudinit_test.go 2014-02-25 14:03:09 +0000
346@@ -76,7 +76,6 @@
347 StateServerKey: serverKey,
348 StatePort: 37017,
349 APIPort: 17070,
350- SyslogPort: 514,
351 MachineNonce: "FAKE_NONCE",
352 StateInfo: &state.Info{
353 Password: "arble",
354@@ -90,7 +89,6 @@
355 DataDir: environs.DataDir,
356 LogDir: environs.LogDir,
357 CloudInitOutputLog: environs.CloudInitOutputLog,
358- RsyslogConfPath: environs.RsyslogConfPath,
359 StateInfoURL: "some-url",
360 SystemPrivateSSHKey: "private rsa key",
361 MachineAgentServiceName: "jujud-machine-0",
362@@ -116,9 +114,6 @@
363 tar zxf \$bin/tools.tar.gz -C \$bin
364 rm \$bin/tools\.tar\.gz && rm \$bin/juju1\.2\.3-precise-amd64\.sha256
365 printf %s '{"version":"1\.2\.3-precise-amd64","url":"http://foo\.com/tools/releases/juju1\.2\.3-precise-amd64\.tgz","sha256":"1234","size":10}' > \$bin/downloaded-tools\.txt
366-install -D -m 644 /dev/null '/etc/rsyslog\.d/25-juju\.conf'
367-printf '%s\\n' '.*' > '/etc/rsyslog.d/25-juju.conf'
368-restart rsyslog
369 mkdir -p '/var/lib/juju/agents/machine-0'
370 install -m 644 /dev/null '/var/lib/juju/agents/machine-0/format'
371 printf '%s\\n' '.*' > '/var/lib/juju/agents/machine-0/format'
372@@ -165,7 +160,6 @@
373 StateServerKey: serverKey,
374 StatePort: 37017,
375 APIPort: 17070,
376- SyslogPort: 514,
377 MachineNonce: "FAKE_NONCE",
378 StateInfo: &state.Info{
379 Password: "arble",
380@@ -179,7 +173,6 @@
381 DataDir: environs.DataDir,
382 LogDir: environs.LogDir,
383 CloudInitOutputLog: environs.CloudInitOutputLog,
384- RsyslogConfPath: environs.RsyslogConfPath,
385 StateInfoURL: "some-url",
386 SystemPrivateSSHKey: "private rsa key",
387 MachineAgentServiceName: "jujud-machine-0",
388@@ -207,7 +200,6 @@
389 DataDir: environs.DataDir,
390 LogDir: environs.LogDir,
391 CloudInitOutputLog: environs.CloudInitOutputLog,
392- RsyslogConfPath: environs.RsyslogConfPath,
393 StateServer: false,
394 Tools: newSimpleTools("1.2.3-linux-amd64"),
395 MachineNonce: "FAKE_NONCE",
396@@ -223,7 +215,6 @@
397 Password: "bletch",
398 CACert: []byte("CA CERT\n" + testing.CACert),
399 },
400- SyslogPort: 514,
401 MachineAgentServiceName: "jujud-machine-99",
402 },
403 expectScripts: `
404@@ -244,9 +235,6 @@
405 tar zxf \$bin/tools.tar.gz -C \$bin
406 rm \$bin/tools\.tar\.gz && rm \$bin/juju1\.2\.3-linux-amd64\.sha256
407 printf %s '{"version":"1\.2\.3-linux-amd64","url":"http://foo\.com/tools/releases/juju1\.2\.3-linux-amd64\.tgz","sha256":"1234","size":10}' > \$bin/downloaded-tools\.txt
408-install -D -m 644 /dev/null '/etc/rsyslog\.d/25-juju\.conf'
409-printf '%s\\n' '.*' > '/etc/rsyslog\.d/25-juju\.conf'
410-restart rsyslog
411 mkdir -p '/var/lib/juju/agents/machine-99'
412 install -m 644 /dev/null '/var/lib/juju/agents/machine-99/format'
413 printf '%s\\n' '.*' > '/var/lib/juju/agents/machine-99/format'
414@@ -267,7 +255,6 @@
415 DataDir: environs.DataDir,
416 LogDir: environs.LogDir,
417 CloudInitOutputLog: environs.CloudInitOutputLog,
418- RsyslogConfPath: environs.RsyslogConfPath,
419 StateServer: false,
420 Tools: newSimpleTools("1.2.3-linux-amd64"),
421 MachineNonce: "FAKE_NONCE",
422@@ -283,13 +270,10 @@
423 Password: "bletch",
424 CACert: []byte("CA CERT\n" + testing.CACert),
425 },
426- SyslogPort: 514,
427 MachineAgentServiceName: "jujud-machine-2-lxc-1",
428 },
429 inexactMatch: true,
430 expectScripts: `
431-printf '%s\\n' '.*' > '/etc/rsyslog\.d/25-juju\.conf'
432-restart rsyslog
433 mkdir -p '/var/lib/juju/agents/machine-2-lxc-1'
434 install -m 644 /dev/null '/var/lib/juju/agents/machine-2-lxc-1/format'
435 printf '%s\\n' '.*' > '/var/lib/juju/agents/machine-2-lxc-1/format'
436@@ -308,7 +292,6 @@
437 DataDir: environs.DataDir,
438 LogDir: environs.LogDir,
439 CloudInitOutputLog: environs.CloudInitOutputLog,
440- RsyslogConfPath: environs.RsyslogConfPath,
441 StateServer: false,
442 Tools: newSimpleTools("1.2.3-linux-amd64"),
443 MachineNonce: "FAKE_NONCE",
444@@ -324,7 +307,6 @@
445 Password: "bletch",
446 CACert: []byte("CA CERT\n" + testing.CACert),
447 },
448- SyslogPort: 514,
449 DisableSSLHostnameVerification: true,
450 MachineAgentServiceName: "jujud-machine-99",
451 },
452@@ -345,7 +327,6 @@
453 StateServerKey: serverKey,
454 StatePort: 37017,
455 APIPort: 17070,
456- SyslogPort: 514,
457 MachineNonce: "FAKE_NONCE",
458 StateInfo: &state.Info{
459 Password: "arble",
460@@ -358,7 +339,6 @@
461 DataDir: environs.DataDir,
462 LogDir: environs.LogDir,
463 CloudInitOutputLog: environs.CloudInitOutputLog,
464- RsyslogConfPath: environs.RsyslogConfPath,
465 StateInfoURL: "some-url",
466 SystemPrivateSSHKey: "private rsa key",
467 MachineAgentServiceName: "jujud-machine-0",
468@@ -644,9 +624,6 @@
469 {"missing API info", func(cfg *cloudinit.MachineConfig) {
470 cfg.APIInfo = nil
471 }},
472- {"missing syslog port", func(cfg *cloudinit.MachineConfig) {
473- cfg.SyslogPort = 0
474- }},
475 {"missing state hosts", func(cfg *cloudinit.MachineConfig) {
476 cfg.StateServer = false
477 cfg.StateInfo = &state.Info{
478@@ -696,9 +673,6 @@
479 {"missing cloud-init output log path", func(cfg *cloudinit.MachineConfig) {
480 cfg.CloudInitOutputLog = ""
481 }},
482- {"missing rsyslog.d conf path", func(cfg *cloudinit.MachineConfig) {
483- cfg.RsyslogConfPath = ""
484- }},
485 {"missing tools", func(cfg *cloudinit.MachineConfig) {
486 cfg.Tools = nil
487 }},
488@@ -765,7 +739,6 @@
489 StateServerKey: serverKey,
490 StatePort: 1234,
491 APIPort: 1235,
492- SyslogPort: 2345,
493 MachineId: "99",
494 Tools: newSimpleTools("9.9.9-linux-arble"),
495 AuthorizedKeys: "sshkey1",
496@@ -783,7 +756,6 @@
497 DataDir: environs.DataDir,
498 LogDir: environs.LogDir,
499 CloudInitOutputLog: environs.CloudInitOutputLog,
500- RsyslogConfPath: environs.RsyslogConfPath,
501 MachineNonce: "FAKE_NONCE",
502 SystemPrivateSSHKey: "private rsa key",
503 MachineAgentServiceName: "jujud-machine-99",
504@@ -803,12 +775,20 @@
505 }
506 }
507
508-func (*cloudinitSuite) createMachineConfig(c *gc.C, environConfig *config.Config) *cloudinit.MachineConfig {
509+func (*cloudinitSuite) createMachineConfig(c *gc.C, environConfig *config.Config, stateServer bool) *cloudinit.MachineConfig {
510 machineId := "42"
511 machineNonce := "fake-nonce"
512 stateInfo := jujutesting.FakeStateInfo(machineId)
513 apiInfo := jujutesting.FakeAPIInfo(machineId)
514- machineConfig := environs.NewMachineConfig(machineId, machineNonce, stateInfo, apiInfo)
515+ var machineConfig *cloudinit.MachineConfig
516+ if stateServer {
517+ var err error
518+ environConfig, err = environConfig.Apply(map[string]interface{}{"agent-version": "2.3.4"})
519+ c.Assert(err, gc.IsNil)
520+ machineConfig = environs.NewBootstrapMachineConfig("http://testing.invalid/provider-state", "invalid ssh key")
521+ } else {
522+ machineConfig = environs.NewMachineConfig(machineId, machineNonce, stateInfo, apiInfo)
523+ }
524 machineConfig.Tools = &tools.Tools{
525 Version: version.MustParseBinary("2.3.4-foo-bar"),
526 URL: "http://tools.testing.invalid/2.3.4-foo-bar.tgz",
527@@ -820,7 +800,7 @@
528
529 func (s *cloudinitSuite) TestAptProxyNotWrittenIfNotSet(c *gc.C) {
530 environConfig := minimalConfig(c)
531- machineCfg := s.createMachineConfig(c, environConfig)
532+ machineCfg := s.createMachineConfig(c, environConfig, false)
533 cloudcfg := coreCloudinit.New()
534 err := cloudinit.Configure(machineCfg, cloudcfg)
535 c.Assert(err, gc.IsNil)
536@@ -835,7 +815,7 @@
537 "apt-http-proxy": "http://user@10.0.0.1",
538 })
539 c.Assert(err, gc.IsNil)
540- machineCfg := s.createMachineConfig(c, environConfig)
541+ machineCfg := s.createMachineConfig(c, environConfig, false)
542 cloudcfg := coreCloudinit.New()
543 err = cloudinit.Configure(machineCfg, cloudcfg)
544 c.Assert(err, gc.IsNil)
545@@ -851,7 +831,7 @@
546 "http-proxy": "http://user@10.0.0.1",
547 })
548 c.Assert(err, gc.IsNil)
549- machineCfg := s.createMachineConfig(c, environConfig)
550+ machineCfg := s.createMachineConfig(c, environConfig, false)
551 cloudcfg := coreCloudinit.New()
552 err = cloudinit.Configure(machineCfg, cloudcfg)
553 c.Assert(err, gc.IsNil)
554
555=== modified file 'environs/cloudinit_test.go'
556--- environs/cloudinit_test.go 2014-01-23 05:45:58 +0000
557+++ environs/cloudinit_test.go 2014-02-25 14:03:09 +0000
558@@ -63,7 +63,6 @@
559 StateInfo: &state.Info{Tag: "not touched"},
560 APIInfo: &api.Info{Tag: "not touched"},
561 DisableSSLHostnameVerification: false,
562- SyslogPort: 2345,
563 })
564 }
565
566@@ -71,7 +70,6 @@
567 attrs := dummySampleConfig().Merge(testing.Attrs{
568 "authorized-keys": "we-are-the-keys",
569 "ssl-hostname-verification": false,
570- "syslog-port": 8888,
571 })
572 cfg, err := config.New(config.NoDefaults, attrs)
573 c.Assert(err, gc.IsNil)
574@@ -90,7 +88,6 @@
575 StateInfo: &state.Info{Tag: "not touched"},
576 APIInfo: &api.Info{Tag: "not touched"},
577 DisableSSLHostnameVerification: true,
578- SyslogPort: 8888,
579 })
580 }
581
582@@ -178,11 +175,9 @@
583 DataDir: environs.DataDir,
584 LogDir: environs.LogDir,
585 CloudInitOutputLog: environs.CloudInitOutputLog,
586- RsyslogConfPath: environs.RsyslogConfPath,
587 Config: envConfig,
588 StatePort: envConfig.StatePort(),
589 APIPort: envConfig.APIPort(),
590- SyslogPort: envConfig.SyslogPort(),
591 StateServer: stateServer,
592 AgentEnvironment: map[string]string{agent.ProviderType: "dummy"},
593 AuthorizedKeys: "wheredidileavemykeys",
594
595=== modified file 'environs/config/config.go'
596--- environs/config/config.go 2014-02-12 04:54:19 +0000
597+++ environs/config/config.go 2014-02-25 14:03:09 +0000
598@@ -43,9 +43,9 @@
599 // DefaultApiPort is the default port the API server is listening on.
600 DefaultAPIPort int = 17070
601
602- // DefaultSyslogPort is the default port that the syslog UDP listener is
603+ // DefaultSyslogPort is the default port that the syslog UDP/TCP listener is
604 // listening on.
605- DefaultSyslogPort int = 514
606+ DefaultSyslogPort int = 6514
607
608 // DefaultBootstrapSSHTimeout is the amount of time to wait
609 // contacting a state server, in seconds.
610@@ -427,6 +427,16 @@
611 return c.mustInt("syslog-port")
612 }
613
614+// RsyslogCACert returns the certificate of the CA that signed the
615+// rsyslog certificate, in PEM format, or nil if one hasn't been
616+// generated yet.
617+func (c *Config) RsyslogCACert() []byte {
618+ if s, ok := c.defined["rsyslog-ca-cert"]; ok {
619+ return []byte(s.(string))
620+ }
621+ return nil
622+}
623+
624 // AuthorizedKeys returns the content for ssh's authorized_keys file.
625 func (c *Config) AuthorizedKeys() string {
626 return c.mustString("authorized-keys")
627@@ -668,6 +678,7 @@
628 "state-port": schema.ForceInt(),
629 "api-port": schema.ForceInt(),
630 "syslog-port": schema.ForceInt(),
631+ "rsyslog-ca-cert": schema.String(),
632 "logging-config": schema.String(),
633 "charm-store-auth": schema.String(),
634 "provisioner-safe-mode": schema.Bool(),
635@@ -711,6 +722,7 @@
636 "bootstrap-timeout": schema.Omit,
637 "bootstrap-retry-delay": schema.Omit,
638 "bootstrap-addresses-delay": schema.Omit,
639+ "rsyslog-ca-cert": schema.Omit,
640
641 // Deprecated fields, retain for backwards compatibility.
642 "tools-url": "",
643@@ -742,6 +754,9 @@
644
645 var defaults = allDefaults()
646
647+// allDefaults returns a schema.Defaults that contains
648+// defaults to be used when creating a new config with
649+// UseDefaults.
650 func allDefaults() schema.Defaults {
651 d := schema.Defaults{
652 "default-series": DefaultSeries,
653@@ -756,7 +771,9 @@
654 "bootstrap-addresses-delay": DefaultBootstrapSSHAddressesDelay,
655 }
656 for attr, val := range alwaysOptional {
657- d[attr] = val
658+ if _, ok := d[attr]; !ok {
659+ d[attr] = val
660+ }
661 }
662 return d
663 }
664@@ -786,7 +803,6 @@
665 "firewall-mode",
666 "state-port",
667 "api-port",
668- "syslog-port",
669 "bootstrap-timeout",
670 "bootstrap-retry-delay",
671 "bootstrap-addresses-delay",
672
673=== modified file 'environs/config/config_test.go'
674--- environs/config/config_test.go 2014-02-12 04:54:19 +0000
675+++ environs/config/config_test.go 2014-02-25 14:03:09 +0000
676@@ -1072,11 +1072,6 @@
677 new: testing.Attrs{"api-port": 42},
678 err: `cannot change api-port from 17070 to 42`,
679 }, {
680- about: "Cannot change the syslog-port",
681- old: testing.Attrs{"syslog-port": 345},
682- new: testing.Attrs{"syslog-port": 42},
683- err: `cannot change syslog-port from 345 to 42`,
684-}, {
685 about: "Can change the state-port from explicit-default to implicit-default",
686 old: testing.Attrs{"state-port": config.DefaultStatePort},
687 }, {
688@@ -1096,10 +1091,6 @@
689 about: "Cannot change the api-port from implicit-default to different value",
690 new: testing.Attrs{"api-port": 42},
691 err: `cannot change api-port from 17070 to 42`,
692-}, {
693- about: "Cannot change the syslog-port from implicit-default to different value",
694- new: testing.Attrs{"syslog-port": 42},
695- err: `cannot change syslog-port from 514 to 42`,
696 }}
697
698 func (*ConfigSuite) TestValidateChange(c *gc.C) {
699
700=== modified file 'log/syslog/config.go'
701--- log/syslog/config.go 2014-01-30 22:12:42 +0000
702+++ log/syslog/config.go 2014-02-25 14:03:09 +0000
703@@ -35,7 +35,7 @@
704 //
705 // if $syslogtag startswith "juju{{namespace}}-" then
706 // action(type="omfile"
707-// File="/var/log/juju{{namespace}}/all-machines.log"
708+// File="{{logDir}}/all-machines.log"
709 // Template="JujuLogFormat{{namespace}}"
710 // FileCreateMode="0644")
711 // & stop
712@@ -52,15 +52,21 @@
713 $InputFileStateFile {{logfileName}}{{namespace}}
714 $InputRunFileMonitor
715
716-$ModLoad imudp
717-$UDPServerRun {{portNumber}}
718+$ModLoad imtcp
719+$DefaultNetstreamDriver gtls
720+$DefaultNetstreamDriverCAFile {{tlsCACertPath}}
721+$DefaultNetstreamDriverCertFile {{tlsCertPath}}
722+$DefaultNetstreamDriverKeyFile {{tlsKeyPath}}
723+$InputTCPServerStreamDriverAuthMode anon
724+$InputTCPServerStreamDriverMode 1 # run driver in TLS-only mode
725+$InputTCPServerRun {{portNumber}}
726
727 # Messages received from remote rsyslog machines have messages prefixed with a space,
728 # so add one in for local messages too if needed.
729 $template JujuLogFormat{{namespace}},"%syslogtag:{{tagStart}}:$%%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n"
730
731 $FileCreateMode 0644
732-:syslogtag, startswith, "juju{{namespace}}-" /var/log/juju{{namespace}}/all-machines.log;JujuLogFormat{{namespace}}
733+:syslogtag, startswith, "juju{{namespace}}-" {{logDir}}/all-machines.log;JujuLogFormat{{namespace}}
734 & ~
735 $FileCreateMode 0640
736 `
737@@ -70,6 +76,12 @@
738 const nodeRsyslogTemplate = `
739 $ModLoad imfile
740
741+# Enable reliable forwarding.
742+$ActionQueueType LinkedList
743+$ActionQueueFileName {{logfileName}}{{namespace}}
744+$ActionResumeRetryCount -1
745+$ActionQueueSaveOnShutdown on
746+
747 $InputFilePersistStateInterval 50
748 $InputFilePollInterval 5
749 $InputFileName {{logfilePath}}
750@@ -77,12 +89,22 @@
751 $InputFileStateFile {{logfileName}}{{namespace}}
752 $InputRunFileMonitor
753
754+$DefaultNetstreamDriver gtls
755+$DefaultNetstreamDriverCAFile {{tlsCACertPath}}
756+$ActionSendStreamDriverAuthMode anon
757+$ActionSendStreamDriverMode 1 # run driver in TLS-only mode
758+
759 $template LongTagForwardFormat,"<%PRI%>%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg%"
760
761-:syslogtag, startswith, "juju{{namespace}}-" @{{bootstrapIP}}:{{portNumber}};LongTagForwardFormat
762+:syslogtag, startswith, "juju{{namespace}}-" @@{{bootstrapIP}}:{{portNumber}};LongTagForwardFormat
763 & ~
764 `
765
766+// nodeRsyslogTemplateTLSHeader is prepended to
767+// nodeRsyslogTemplate if TLS is to be used.
768+const nodeRsyslogTemplateTLSHeader = `
769+`
770+
771 const defaultConfigDir = "/etc/rsyslog.d"
772
773 // SyslogConfigRenderer instances are used to generate a rsyslog conf file.
774@@ -104,7 +126,15 @@
775 LogFileName string
776 // the addresses of the state server to which messages should be forwarded.
777 StateServerAddresses []string
778- // the port number for the udp listener
779+ // TLSCACertPath is the full path to the CA certificate that
780+ // signed the rsyslog server's certificate. If this is non-empty,
781+ // then TLS will be used.
782+ TLSCACertPath string
783+ // TLSCertPath is the full path to the server's certificate.
784+ TLSCertPath string
785+ // TLSKeyPath is the full path to the server's private key.
786+ TLSKeyPath string
787+ // the port number for the listener
788 Port int
789 // the directory for the logfiles
790 LogDir string
791@@ -153,7 +183,6 @@
792
793 // Render generates the rsyslog config.
794 func (slConfig *SyslogConfig) Render() ([]byte, error) {
795-
796 // TODO: for HA, we will want to send to all state server addresses (maybe).
797 var bootstrapIP = func() string {
798 addr := slConfig.StateServerAddresses[0]
799@@ -167,13 +196,16 @@
800
801 t := template.New("")
802 t.Funcs(template.FuncMap{
803- "logfileName": func() string { return slConfig.LogFileName },
804- "bootstrapIP": bootstrapIP,
805- "logfilePath": logFilePath,
806- "portNumber": func() int { return slConfig.Port },
807- "logDir": func() string { return slConfig.LogDir },
808- "namespace": func() string { return slConfig.Namespace },
809- "tagStart": func() int { return tagOffset + len(slConfig.Namespace) },
810+ "logfileName": func() string { return slConfig.LogFileName },
811+ "bootstrapIP": bootstrapIP,
812+ "logfilePath": logFilePath,
813+ "portNumber": func() int { return slConfig.Port },
814+ "logDir": func() string { return slConfig.LogDir },
815+ "namespace": func() string { return slConfig.Namespace },
816+ "tagStart": func() int { return tagOffset + len(slConfig.Namespace) },
817+ "tlsCACertPath": func() string { return slConfig.TLSCACertPath },
818+ "tlsCertPath": func() string { return slConfig.TLSCertPath },
819+ "tlsKeyPath": func() string { return slConfig.TLSKeyPath },
820 })
821
822 // Process the rsyslog config template and echo to the conf file.
823
824=== modified file 'log/syslog/config_test.go'
825--- log/syslog/config_test.go 2014-02-13 05:22:01 +0000
826+++ log/syslog/config_test.go 2014-02-25 14:03:09 +0000
827@@ -43,12 +43,18 @@
828
829 func (s *SyslogConfigSuite) TestAccumulateConfigRender(c *gc.C) {
830 syslogConfigRenderer := syslog.NewAccumulateConfig("some-machine", 8888, "")
831+ syslogConfigRenderer.TLSCACertPath = "/var/log/juju/ca.pem"
832+ syslogConfigRenderer.TLSCertPath = "/var/log/juju/cert.pem"
833+ syslogConfigRenderer.TLSKeyPath = "/var/log/juju/key.pem"
834 s.assertRsyslogConfigContents(
835 c, syslogConfigRenderer, syslogtesting.ExpectedAccumulateSyslogConf(c, "some-machine", "", 8888))
836 }
837
838 func (s *SyslogConfigSuite) TestAccumulateConfigWrite(c *gc.C) {
839 syslogConfigRenderer := syslog.NewAccumulateConfig("some-machine", 8888, "")
840+ syslogConfigRenderer.TLSCACertPath = "/var/log/juju/ca.pem"
841+ syslogConfigRenderer.TLSCertPath = "/var/log/juju/cert.pem"
842+ syslogConfigRenderer.TLSKeyPath = "/var/log/juju/key.pem"
843 syslogConfigRenderer.ConfigDir = s.configDir
844 syslogConfigRenderer.ConfigFileName = "rsyslog.conf"
845 s.assertRsyslogConfigPath(c, syslogConfigRenderer)
846@@ -61,24 +67,30 @@
847
848 func (s *SyslogConfigSuite) TestAccumulateConfigRenderWithNamespace(c *gc.C) {
849 syslogConfigRenderer := syslog.NewAccumulateConfig("some-machine", 8888, "namespace")
850+ syslogConfigRenderer.TLSCACertPath = "/var/log/juju/ca.pem"
851+ syslogConfigRenderer.TLSCertPath = "/var/log/juju/cert.pem"
852+ syslogConfigRenderer.TLSKeyPath = "/var/log/juju/key.pem"
853 s.assertRsyslogConfigContents(
854 c, syslogConfigRenderer, syslogtesting.ExpectedAccumulateSyslogConf(c, "some-machine", "namespace", 8888))
855 }
856
857 func (s *SyslogConfigSuite) TestForwardConfigRender(c *gc.C) {
858 syslogConfigRenderer := syslog.NewForwardConfig("some-machine", 999, "", []string{"server"})
859+ syslogConfigRenderer.TLSCACertPath = "/var/log/juju/ca.pem"
860 s.assertRsyslogConfigContents(
861 c, syslogConfigRenderer, syslogtesting.ExpectedForwardSyslogConf(c, "some-machine", "", 999))
862 }
863
864 func (s *SyslogConfigSuite) TestForwardConfigRenderWithNamespace(c *gc.C) {
865 syslogConfigRenderer := syslog.NewForwardConfig("some-machine", 999, "namespace", []string{"server"})
866+ syslogConfigRenderer.TLSCACertPath = "/var/log/juju/ca.pem"
867 s.assertRsyslogConfigContents(
868 c, syslogConfigRenderer, syslogtesting.ExpectedForwardSyslogConf(c, "some-machine", "namespace", 999))
869 }
870
871 func (s *SyslogConfigSuite) TestForwardConfigWrite(c *gc.C) {
872 syslogConfigRenderer := syslog.NewForwardConfig("some-machine", 999, "", []string{"server"})
873+ syslogConfigRenderer.TLSCACertPath = "/var/log/juju/ca.pem"
874 syslogConfigRenderer.ConfigDir = s.configDir
875 syslogConfigRenderer.ConfigFileName = "rsyslog.conf"
876 s.assertRsyslogConfigPath(c, syslogConfigRenderer)
877
878=== modified file 'log/syslog/testing/syslogconf.go'
879--- log/syslog/testing/syslogconf.go 2014-02-13 05:41:33 +0000
880+++ log/syslog/testing/syslogconf.go 2014-02-25 14:03:09 +0000
881@@ -20,15 +20,21 @@
882 $InputFileStateFile {{machine}}{{namespace}}
883 $InputRunFileMonitor
884
885-$ModLoad imudp
886-$UDPServerRun {{port}}
887+$ModLoad imtcp
888+$DefaultNetstreamDriver gtls
889+$DefaultNetstreamDriverCAFile /var/log/juju/ca.pem
890+$DefaultNetstreamDriverCertFile /var/log/juju/cert.pem
891+$DefaultNetstreamDriverKeyFile /var/log/juju/key.pem
892+$InputTCPServerStreamDriverAuthMode anon
893+$InputTCPServerStreamDriverMode 1 # run driver in TLS-only mode
894+$InputTCPServerRun {{port}}
895
896 # Messages received from remote rsyslog machines have messages prefixed with a space,
897 # so add one in for local messages too if needed.
898 $template JujuLogFormat{{namespace}},"%syslogtag:{{offset}}:$%%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n"
899
900 $FileCreateMode 0644
901-:syslogtag, startswith, "juju{{namespace}}-" /var/log/juju{{namespace}}/all-machines.log;JujuLogFormat{{namespace}}
902+:syslogtag, startswith, "juju{{namespace}}-" /var/log/juju/all-machines.log;JujuLogFormat{{namespace}}
903 & ~
904 $FileCreateMode 0640
905 `
906@@ -55,6 +61,12 @@
907 var expectedForwardSyslogConfTemplate = `
908 $ModLoad imfile
909
910+# Enable reliable forwarding.
911+$ActionQueueType LinkedList
912+$ActionQueueFileName {{machine}}{{namespace}}
913+$ActionResumeRetryCount -1
914+$ActionQueueSaveOnShutdown on
915+
916 $InputFilePersistStateInterval 50
917 $InputFilePollInterval 5
918 $InputFileName /var/log/juju/{{machine}}.log
919@@ -62,9 +74,14 @@
920 $InputFileStateFile {{machine}}{{namespace}}
921 $InputRunFileMonitor
922
923+$DefaultNetstreamDriver gtls
924+$DefaultNetstreamDriverCAFile /var/log/juju/ca.pem
925+$ActionSendStreamDriverAuthMode anon
926+$ActionSendStreamDriverMode 1 # run driver in TLS-only mode
927+
928 $template LongTagForwardFormat,"<%PRI%>%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg%"
929
930-:syslogtag, startswith, "juju{{namespace}}-" @server:{{port}};LongTagForwardFormat
931+:syslogtag, startswith, "juju{{namespace}}-" @@server:{{port}};LongTagForwardFormat
932 & ~
933 `
934
935
936=== modified file 'provider/azure/customdata_test.go'
937--- provider/azure/customdata_test.go 2014-01-23 05:21:21 +0000
938+++ provider/azure/customdata_test.go 2014-02-25 14:03:09 +0000
939@@ -33,7 +33,6 @@
940 DataDir: environs.DataDir,
941 LogDir: environs.LogDir,
942 CloudInitOutputLog: environs.CloudInitOutputLog,
943- RsyslogConfPath: environs.RsyslogConfPath,
944 Tools: &tools.Tools{URL: "file://" + c.MkDir()},
945 StateInfo: &state.Info{
946 CACert: []byte(testing.CACert),
947@@ -46,7 +45,6 @@
948 Addrs: []string{"127.0.0.1:123"},
949 Tag: names.MachineTag(machineID),
950 },
951- SyslogPort: 2345,
952 MachineAgentServiceName: "jujud-machine-0",
953 }
954 }
955
956=== modified file 'provider/local/config.go'
957--- provider/local/config.go 2014-02-12 02:56:13 +0000
958+++ provider/local/config.go 2014-02-25 14:03:09 +0000
959@@ -107,7 +107,6 @@
960 for _, dirname := range []string{
961 c.storageDir(),
962 c.mongoDir(),
963- c.logDir(),
964 } {
965 logger.Tracef("creating directory %s", dirname)
966 if err := os.MkdirAll(dirname, 0755); err != nil {
967
968=== modified file 'provider/local/environ.go'
969--- provider/local/environ.go 2014-02-20 08:23:40 +0000
970+++ provider/local/environ.go 2014-02-25 14:03:09 +0000
971@@ -138,9 +138,8 @@
972 mcfg := environs.NewBootstrapMachineConfig(stateFileURL, privateKey)
973 mcfg.Tools = selectedTools[0]
974 mcfg.DataDir = env.config.rootDir()
975- mcfg.LogDir = env.config.logDir()
976- mcfg.RsyslogConfPath = env.rsyslogConfPath()
977- mcfg.CloudInitOutputLog = filepath.Join(mcfg.LogDir, "cloud-init-output.log")
978+ mcfg.LogDir = fmt.Sprintf("/var/log/juju-%s", env.config.namespace())
979+ mcfg.CloudInitOutputLog = filepath.Join(env.config.logDir(), "cloud-init-output.log")
980 mcfg.DisablePackageCommands = true
981 mcfg.MachineAgentServiceName = env.machineAgentServiceName()
982 mcfg.MongoServiceName = env.mongoServiceName()
983@@ -162,10 +161,14 @@
984 // Also, we leave the old all-machines.log file in
985 // /var/log/juju-{{namespace}} until we start the environment again. So
986 // potentially remove it at the start of the cloud-init.
987- logfile := fmt.Sprintf("/var/log/juju-%s/all-machines.log", env.config.namespace())
988+ os.RemoveAll(env.config.logDir())
989+ os.MkdirAll(env.config.logDir(), 0755)
990 cloudcfg.AddScripts(
991- fmt.Sprintf("[ -f %s ] && rm %s", logfile, logfile),
992- fmt.Sprintf("ln -s %s %s/", logfile, env.config.logDir()))
993+ fmt.Sprintf("rm -fr %s", mcfg.LogDir),
994+ fmt.Sprintf("mkdir -p %s", mcfg.LogDir),
995+ fmt.Sprintf("rm -f /var/spool/rsyslog/machine-0-%s", env.config.namespace()),
996+ fmt.Sprintf("ln -s %s/all-machines.log %s/", mcfg.LogDir, env.config.logDir()),
997+ fmt.Sprintf("ln -s %s/machine-0.log %s/", env.config.logDir(), mcfg.LogDir))
998 if err := cloudinit.ConfigureJuju(mcfg, cloudcfg); err != nil {
999 return err
1000 }
1001
1002=== modified file 'provider/local/environprovider.go'
1003--- provider/local/environprovider.go 2014-02-18 01:29:47 +0000
1004+++ provider/local/environprovider.go 2014-02-25 14:03:09 +0000
1005@@ -101,9 +101,10 @@
1006 if err != nil {
1007 return nil, err
1008 }
1009+ attrs := make(map[string]interface{})
1010+
1011 // If the user has specified no values for any of the three normal
1012 // proxies, then look in the environment and set them.
1013- attrs := make(map[string]interface{})
1014 setIfNotBlank := func(key, value string) {
1015 if value != "" {
1016 attrs[key] = value
1017@@ -130,13 +131,11 @@
1018 setIfNotBlank("apt-https-proxy", proxy.Https)
1019 setIfNotBlank("apt-ftp-proxy", proxy.Ftp)
1020 }
1021- if len(attrs) > 0 {
1022- cfg, err = cfg.Apply(attrs)
1023- if err != nil {
1024- return nil, err
1025- }
1026- }
1027
1028+ cfg, err = cfg.Apply(attrs)
1029+ if err != nil {
1030+ return nil, err
1031+ }
1032 return p.Open(cfg)
1033 }
1034
1035
1036=== modified file 'state/address.go'
1037--- state/address.go 2014-01-22 19:28:08 +0000
1038+++ state/address.go 2014-02-25 14:03:09 +0000
1039@@ -122,7 +122,6 @@
1040 type DeployerConnectionValues struct {
1041 StateAddresses []string
1042 APIAddresses []string
1043- SyslogPort int
1044 }
1045
1046 // DeployerConnectionInfo returns the address information necessary for the deployer.
1047@@ -139,6 +138,5 @@
1048 return &DeployerConnectionValues{
1049 StateAddresses: appendPort(addrs, config.StatePort()),
1050 APIAddresses: appendPort(addrs, config.APIPort()),
1051- SyslogPort: config.SyslogPort(),
1052 }, nil
1053 }
1054
1055=== modified file 'state/api/params/params.go'
1056--- state/api/params/params.go 2014-01-31 03:36:58 +0000
1057+++ state/api/params/params.go 2014-02-25 14:03:09 +0000
1058@@ -513,7 +513,6 @@
1059 ProviderType string
1060 AuthorizedKeys string
1061 SSLHostnameVerification bool
1062- SyslogPort int
1063 Proxy osenv.ProxySettings
1064 AptProxy osenv.ProxySettings
1065 }
1066@@ -562,10 +561,14 @@
1067 type DeployerConnectionValues struct {
1068 StateAddresses []string
1069 APIAddresses []string
1070- SyslogPort int
1071 }
1072
1073 // StatusParams holds parameters for the Status call.
1074 type StatusParams struct {
1075 Patterns []string
1076 }
1077+
1078+// SetRsyslogCertParams holds parameters for the SetRsyslogCert call.
1079+type SetRsyslogCertParams struct {
1080+ CACert []byte
1081+}
1082
1083=== modified file 'state/api/provisioner/provisioner_test.go'
1084--- state/api/provisioner/provisioner_test.go 2014-02-14 11:33:57 +0000
1085+++ state/api/provisioner/provisioner_test.go 2014-02-25 14:03:09 +0000
1086@@ -358,7 +358,6 @@
1087 c.Assert(result.ProviderType, gc.Equals, "dummy")
1088 c.Assert(result.AuthorizedKeys, gc.Equals, coretesting.FakeAuthKeys)
1089 c.Assert(result.SSLHostnameVerification, jc.IsTrue)
1090- c.Assert(result.SyslogPort, gc.Equals, 2345)
1091 }
1092
1093 func (s *provisionerSuite) TestCACert(c *gc.C) {
1094
1095=== added directory 'state/api/rsyslog'
1096=== added file 'state/api/rsyslog/package_test.go'
1097--- state/api/rsyslog/package_test.go 1970-01-01 00:00:00 +0000
1098+++ state/api/rsyslog/package_test.go 2014-02-25 14:03:09 +0000
1099@@ -0,0 +1,14 @@
1100+// Copyright 2014 Canonical Ltd.
1101+// Licensed under the AGPLv3, see LICENCE file for details.
1102+
1103+package rsyslog_test
1104+
1105+import (
1106+ stdtesting "testing"
1107+
1108+ "launchpad.net/juju-core/testing"
1109+)
1110+
1111+func TestAll(t *stdtesting.T) {
1112+ testing.MgoTestPackage(t)
1113+}
1114
1115=== added file 'state/api/rsyslog/rsyslog.go'
1116--- state/api/rsyslog/rsyslog.go 1970-01-01 00:00:00 +0000
1117+++ state/api/rsyslog/rsyslog.go 2014-02-25 14:03:09 +0000
1118@@ -0,0 +1,44 @@
1119+// Copyright 2014 Canonical Ltd.
1120+// Licensed under the AGPLv3, see LICENCE file for details.
1121+
1122+package rsyslog
1123+
1124+import (
1125+ "launchpad.net/juju-core/state/api/base"
1126+ "launchpad.net/juju-core/state/api/common"
1127+ "launchpad.net/juju-core/state/api/params"
1128+)
1129+
1130+const rsyslogAPI = "Rsyslog"
1131+
1132+// State provides access to the Rsyslog API facade.
1133+type State struct {
1134+ *common.EnvironWatcher
1135+ caller base.Caller
1136+}
1137+
1138+// NewState creates a new client-side Rsyslog facade.
1139+func NewState(caller base.Caller) *State {
1140+ return &State{
1141+ EnvironWatcher: common.NewEnvironWatcher(rsyslogAPI, caller),
1142+ caller: caller,
1143+ }
1144+}
1145+
1146+// SetRsyslogCert sets the rsyslog CA certificate,
1147+// which is used by clients to verify the server's
1148+// identity and establish a TLS session.
1149+func (st *State) SetRsyslogCert(caCert []byte) error {
1150+ var result params.ErrorResult
1151+ args := params.SetRsyslogCertParams{
1152+ CACert: caCert,
1153+ }
1154+ err := st.caller.Call(rsyslogAPI, "", "SetRsyslogCert", args, &result)
1155+ if err != nil {
1156+ return err
1157+ }
1158+ if result.Error != nil {
1159+ return result.Error
1160+ }
1161+ return nil
1162+}
1163
1164=== added file 'state/api/rsyslog/rsyslog_test.go'
1165--- state/api/rsyslog/rsyslog_test.go 1970-01-01 00:00:00 +0000
1166+++ state/api/rsyslog/rsyslog_test.go 2014-02-25 14:03:09 +0000
1167@@ -0,0 +1,33 @@
1168+// Copyright 2014 Canonical Ltd.
1169+// Licensed under the AGPLv3, see LICENCE file for details.
1170+
1171+package rsyslog_test
1172+
1173+import (
1174+ gc "launchpad.net/gocheck"
1175+
1176+ jujutesting "launchpad.net/juju-core/juju/testing"
1177+ commontesting "launchpad.net/juju-core/state/api/common/testing"
1178+)
1179+
1180+type rsyslogSuite struct {
1181+ jujutesting.JujuConnSuite
1182+ *commontesting.EnvironWatcherTest
1183+}
1184+
1185+var _ = gc.Suite(&rsyslogSuite{})
1186+
1187+func (s *rsyslogSuite) SetUpTest(c *gc.C) {
1188+ s.JujuConnSuite.SetUpTest(c)
1189+
1190+ stateAPI, _ := s.OpenAPIAsNewMachine(c)
1191+ rsyslogAPI := stateAPI.Rsyslog()
1192+ c.Assert(rsyslogAPI, gc.NotNil)
1193+
1194+ s.EnvironWatcherTest = commontesting.NewEnvironWatcherTest(
1195+ rsyslogAPI,
1196+ s.State,
1197+ s.BackingState,
1198+ commontesting.NoSecrets,
1199+ )
1200+}
1201
1202=== modified file 'state/api/state.go'
1203--- state/api/state.go 2014-02-18 00:15:30 +0000
1204+++ state/api/state.go 2014-02-25 14:03:09 +0000
1205@@ -14,6 +14,7 @@
1206 "launchpad.net/juju-core/state/api/machiner"
1207 "launchpad.net/juju-core/state/api/params"
1208 "launchpad.net/juju-core/state/api/provisioner"
1209+ "launchpad.net/juju-core/state/api/rsyslog"
1210 "launchpad.net/juju-core/state/api/uniter"
1211 "launchpad.net/juju-core/state/api/upgrader"
1212 )
1213@@ -99,3 +100,8 @@
1214 func (st *State) CharmRevisionUpdater() *charmrevisionupdater.State {
1215 return charmrevisionupdater.NewState(st)
1216 }
1217+
1218+// Rsyslog returns access to the Rsyslog API
1219+func (st *State) Rsyslog() *rsyslog.State {
1220+ return rsyslog.NewState(st)
1221+}
1222
1223=== modified file 'state/apiserver/deployer/deployer.go'
1224--- state/apiserver/deployer/deployer.go 2014-01-21 15:54:31 +0000
1225+++ state/apiserver/deployer/deployer.go 2014-02-25 14:03:09 +0000
1226@@ -76,7 +76,6 @@
1227 result = params.DeployerConnectionValues{
1228 StateAddresses: info.StateAddresses,
1229 APIAddresses: info.APIAddresses,
1230- SyslogPort: info.SyslogPort,
1231 }
1232 }
1233 return result, err
1234
1235=== modified file 'state/apiserver/provisioner/provisioner.go'
1236--- state/apiserver/provisioner/provisioner.go 2014-01-31 03:36:58 +0000
1237+++ state/apiserver/provisioner/provisioner.go 2014-02-25 14:03:09 +0000
1238@@ -196,7 +196,6 @@
1239 result.ProviderType = config.Type()
1240 result.AuthorizedKeys = config.AuthorizedKeys()
1241 result.SSLHostnameVerification = config.SSLHostnameVerification()
1242- result.SyslogPort = config.SyslogPort()
1243 result.Proxy = config.ProxySettings()
1244 result.AptProxy = config.AptProxySettings()
1245 return result, nil
1246
1247=== modified file 'state/apiserver/provisioner/provisioner_test.go'
1248--- state/apiserver/provisioner/provisioner_test.go 2014-02-14 11:33:57 +0000
1249+++ state/apiserver/provisioner/provisioner_test.go 2014-02-25 14:03:09 +0000
1250@@ -700,7 +700,6 @@
1251 c.Check(results.ProviderType, gc.Equals, "dummy")
1252 c.Check(results.AuthorizedKeys, gc.Equals, coretesting.FakeAuthKeys)
1253 c.Check(results.SSLHostnameVerification, jc.IsTrue)
1254- c.Check(results.SyslogPort, gc.Equals, 2345)
1255 c.Check(results.Proxy, gc.DeepEquals, expectedProxy)
1256 c.Check(results.AptProxy, gc.DeepEquals, expectedProxy)
1257 }
1258
1259=== modified file 'state/apiserver/root.go'
1260--- state/apiserver/root.go 2014-02-20 03:12:23 +0000
1261+++ state/apiserver/root.go 2014-02-25 14:03:09 +0000
1262@@ -24,6 +24,7 @@
1263 loggerapi "launchpad.net/juju-core/state/apiserver/logger"
1264 "launchpad.net/juju-core/state/apiserver/machine"
1265 "launchpad.net/juju-core/state/apiserver/provisioner"
1266+ "launchpad.net/juju-core/state/apiserver/rsyslog"
1267 "launchpad.net/juju-core/state/apiserver/uniter"
1268 "launchpad.net/juju-core/state/apiserver/upgrader"
1269 "launchpad.net/juju-core/state/multiwatcher"
1270@@ -182,6 +183,17 @@
1271 return environment.NewEnvironmentAPI(r.srv.state, r.resources, r)
1272 }
1273
1274+// Rsyslog returns an object that provides access to the Rsyslog API
1275+// facade. The id argument is reserved for future use and currently needs to
1276+// be empty.
1277+func (r *srvRoot) Rsyslog(id string) (*rsyslog.RsyslogAPI, error) {
1278+ if id != "" {
1279+ // Safeguard id for possible future use.
1280+ return nil, common.ErrBadId
1281+ }
1282+ return rsyslog.NewRsyslogAPI(r.srv.state, r.resources, r)
1283+}
1284+
1285 // Logger returns an object that provides access to the Logger API facade.
1286 // The id argument is reserved for future use and must be empty.
1287 func (r *srvRoot) Logger(id string) (*loggerapi.LoggerAPI, error) {
1288
1289=== added directory 'state/apiserver/rsyslog'
1290=== added file 'state/apiserver/rsyslog/package_test.go'
1291--- state/apiserver/rsyslog/package_test.go 1970-01-01 00:00:00 +0000
1292+++ state/apiserver/rsyslog/package_test.go 2014-02-25 14:03:09 +0000
1293@@ -0,0 +1,14 @@
1294+// Copyright 2014 Canonical Ltd.
1295+// Licensed under the AGPLv3, see LICENCE file for details.
1296+
1297+package rsyslog_test
1298+
1299+import (
1300+ stdtesting "testing"
1301+
1302+ "launchpad.net/juju-core/testing"
1303+)
1304+
1305+func TestAll(t *stdtesting.T) {
1306+ testing.MgoTestPackage(t)
1307+}
1308
1309=== added file 'state/apiserver/rsyslog/rsyslog.go'
1310--- state/apiserver/rsyslog/rsyslog.go 1970-01-01 00:00:00 +0000
1311+++ state/apiserver/rsyslog/rsyslog.go 2014-02-25 14:03:09 +0000
1312@@ -0,0 +1,56 @@
1313+// Copyright 2014 Canonical Ltd.
1314+// Licensed under the AGPLv3, see LICENCE file for details.
1315+
1316+package rsyslog
1317+
1318+import (
1319+ "launchpad.net/juju-core/cert"
1320+ "launchpad.net/juju-core/state"
1321+ "launchpad.net/juju-core/state/api/params"
1322+ "launchpad.net/juju-core/state/apiserver/common"
1323+)
1324+
1325+// RsyslogAPI implements the API used by the rsyslog worker.
1326+type RsyslogAPI struct {
1327+ *common.EnvironWatcher
1328+ st *state.State
1329+ canModify bool
1330+}
1331+
1332+// NewRsyslogAPI creates a new instance of the Rsyslog API.
1333+func NewRsyslogAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*RsyslogAPI, error) {
1334+ // Can always watch for environ changes.
1335+ getCanWatch := common.AuthAlways(true)
1336+ // Does not get the secrets.
1337+ getCanReadSecrets := common.AuthAlways(false)
1338+ return &RsyslogAPI{
1339+ EnvironWatcher: common.NewEnvironWatcher(st, resources, getCanWatch, getCanReadSecrets),
1340+ st: st,
1341+ canModify: authorizer.AuthEnvironManager(),
1342+ }, nil
1343+}
1344+
1345+func (api *RsyslogAPI) SetRsyslogCert(args params.SetRsyslogCertParams) (params.ErrorResult, error) {
1346+ var result params.ErrorResult
1347+ if !api.canModify {
1348+ result.Error = common.ServerError(common.ErrBadCreds)
1349+ return result, nil
1350+ }
1351+ if _, err := cert.ParseCert(args.CACert); err != nil {
1352+ result.Error = common.ServerError(err)
1353+ return result, nil
1354+ }
1355+ old, err := api.st.EnvironConfig()
1356+ if err != nil {
1357+ return params.ErrorResult{}, err
1358+ }
1359+ cfg, err := old.Apply(map[string]interface{}{"rsyslog-ca-cert": string(args.CACert)})
1360+ if err != nil {
1361+ result.Error = common.ServerError(err)
1362+ } else {
1363+ if err := api.st.SetEnvironConfig(cfg, old); err != nil {
1364+ result.Error = common.ServerError(err)
1365+ }
1366+ }
1367+ return result, nil
1368+}
1369
1370=== added file 'state/apiserver/rsyslog/rsyslog_test.go'
1371--- state/apiserver/rsyslog/rsyslog_test.go 1970-01-01 00:00:00 +0000
1372+++ state/apiserver/rsyslog/rsyslog_test.go 2014-02-25 14:03:09 +0000
1373@@ -0,0 +1,48 @@
1374+// Copyright 2013 Canonical Ltd.
1375+// Licensed under the AGPLv3, see LICENCE file for details.
1376+
1377+package rsyslog_test
1378+
1379+import (
1380+ gc "launchpad.net/gocheck"
1381+
1382+ "launchpad.net/juju-core/juju/testing"
1383+ "launchpad.net/juju-core/state/apiserver/common"
1384+ commontesting "launchpad.net/juju-core/state/apiserver/common/testing"
1385+ "launchpad.net/juju-core/state/apiserver/rsyslog"
1386+ apiservertesting "launchpad.net/juju-core/state/apiserver/testing"
1387+)
1388+
1389+type rsyslogSuite struct {
1390+ testing.JujuConnSuite
1391+ *commontesting.EnvironWatcherTest
1392+ authorizer apiservertesting.FakeAuthorizer
1393+ resources *common.Resources
1394+}
1395+
1396+var _ = gc.Suite(&rsyslogSuite{})
1397+
1398+func (s *rsyslogSuite) SetUpTest(c *gc.C) {
1399+ s.JujuConnSuite.SetUpTest(c)
1400+ s.authorizer = apiservertesting.FakeAuthorizer{
1401+ LoggedIn: true,
1402+ EnvironManager: true,
1403+ }
1404+ s.resources = common.NewResources()
1405+ api, err := rsyslog.NewRsyslogAPI(s.State, s.resources, s.authorizer)
1406+ c.Assert(err, gc.IsNil)
1407+ s.EnvironWatcherTest = commontesting.NewEnvironWatcherTest(
1408+ api, s.State, s.resources, commontesting.NoSecrets)
1409+}
1410+
1411+func (s *rsyslogSuite) TestSetRsyslogCert(c *gc.C) {
1412+ st := s.APIState.Rsyslog()
1413+ err := st.SetRsyslogCert(nil)
1414+ // TODO(axw) finish me. this should fail due to being an invalid cert
1415+ c.Assert(err, gc.IsNil)
1416+}
1417+
1418+func (s *rsyslogSuite) TestSetRsyslogCertPerms(c *gc.C) {
1419+ // TODO(axw) SetRsyslogCert requires that the
1420+ // caller is a state server.
1421+}
1422
1423=== modified file 'upgrades/export_test.go'
1424--- upgrades/export_test.go 2014-02-13 05:22:01 +0000
1425+++ upgrades/export_test.go 2014-02-25 14:03:09 +0000
1426@@ -10,6 +10,4 @@
1427 // 118 upgrade functions
1428 StepsFor118 = stepsFor118
1429 EnsureLockDirExistsAndUbuntuWritable = ensureLockDirExistsAndUbuntuWritable
1430- UpgradeStateServerRsyslogConfig = upgradeStateServerRsyslogConfig
1431- UpgradeHostMachineRsyslogConfig = upgradeHostMachineRsyslogConfig
1432 )
1433
1434=== removed file 'upgrades/rsyslogconf.go'
1435--- upgrades/rsyslogconf.go 2014-02-14 00:48:23 +0000
1436+++ upgrades/rsyslogconf.go 1970-01-01 00:00:00 +0000
1437@@ -1,50 +0,0 @@
1438-// Copyright 2014 Canonical Ltd.
1439-// Licensed under the AGPLv3, see LICENCE file for details.
1440-
1441-package upgrades
1442-
1443-import (
1444- "launchpad.net/juju-core/agent"
1445- "launchpad.net/juju-core/environs"
1446- "launchpad.net/juju-core/log/syslog"
1447-)
1448-
1449-func confParams(context Context) (machineTag, namespace string, port int, err error) {
1450- namespace = context.AgentConfig().Value(agent.Namespace)
1451- machineTag = context.AgentConfig().Tag()
1452-
1453- environment := context.APIState().Environment()
1454- config, err := environment.EnvironConfig()
1455- if err != nil {
1456- return "", "", 0, err
1457- }
1458- port = config.SyslogPort()
1459- return machineTag, namespace, port, err
1460-}
1461-
1462-// upgradeStateServerRsyslogConfig upgrades a rsuslog config file on a state server.
1463-func upgradeStateServerRsyslogConfig(context Context) (err error) {
1464- machineTag, namespace, syslogPort, err := confParams(context)
1465- configRenderer := syslog.NewAccumulateConfig(machineTag, syslogPort, namespace)
1466- data, err := configRenderer.Render()
1467- if err != nil {
1468- return nil
1469- }
1470- return WriteReplacementFile(environs.RsyslogConfPath, []byte(data), 0644)
1471-}
1472-
1473-// upgradeHostMachineRsyslogConfig upgrades a rsuslog config file on a host machine.
1474-func upgradeHostMachineRsyslogConfig(context Context) (err error) {
1475- machineTag, namespace, syslogPort, err := confParams(context)
1476- addr, err := context.AgentConfig().APIAddresses()
1477- if err != nil {
1478- return err
1479- }
1480-
1481- configRenderer := syslog.NewForwardConfig(machineTag, syslogPort, namespace, addr)
1482- data, err := configRenderer.Render()
1483- if err != nil {
1484- return nil
1485- }
1486- return WriteReplacementFile(environs.RsyslogConfPath, []byte(data), 0644)
1487-}
1488
1489=== removed file 'upgrades/rsyslogconf_test.go'
1490--- upgrades/rsyslogconf_test.go 2014-02-14 11:39:37 +0000
1491+++ upgrades/rsyslogconf_test.go 1970-01-01 00:00:00 +0000
1492@@ -1,72 +0,0 @@
1493-// Copyright 2014 Canonical Ltd.
1494-// Licensed under the AGPLv3, see LICENCE file for details.
1495-
1496-package upgrades_test
1497-
1498-import (
1499- "io/ioutil"
1500- "path/filepath"
1501-
1502- gc "launchpad.net/gocheck"
1503-
1504- "launchpad.net/juju-core/environs"
1505- jujutesting "launchpad.net/juju-core/juju/testing"
1506- syslogtesting "launchpad.net/juju-core/log/syslog/testing"
1507- "launchpad.net/juju-core/state"
1508- "launchpad.net/juju-core/upgrades"
1509-)
1510-
1511-type rsyslogSuite struct {
1512- jujutesting.JujuConnSuite
1513-
1514- syslogPath string
1515- ctx upgrades.Context
1516-}
1517-
1518-var _ = gc.Suite(&rsyslogSuite{})
1519-
1520-func (s *rsyslogSuite) SetUpTest(c *gc.C) {
1521- s.JujuConnSuite.SetUpTest(c)
1522-
1523- dir := c.MkDir()
1524- s.syslogPath = filepath.Join(dir, "fakesyslog.conf")
1525- s.PatchValue(&environs.RsyslogConfPath, s.syslogPath)
1526-
1527- apiState, _ := s.OpenAPIAsNewMachine(c, state.JobManageEnviron)
1528- s.ctx = &mockContext{
1529- agentConfig: &mockAgentConfig{
1530- tag: "machine-tag",
1531- namespace: "namespace",
1532- apiAddresses: []string{"server:1234"},
1533- },
1534- apiState: apiState,
1535- }
1536-}
1537-
1538-func (s *rsyslogSuite) TestStateServerUpgrade(c *gc.C) {
1539- err := upgrades.UpgradeStateServerRsyslogConfig(s.ctx)
1540- c.Assert(err, gc.IsNil)
1541-
1542- data, err := ioutil.ReadFile(s.syslogPath)
1543- c.Assert(err, gc.IsNil)
1544- c.Assert(string(data), gc.Equals, syslogtesting.ExpectedAccumulateSyslogConf(c, "machine-tag", "namespace", 2345))
1545-}
1546-
1547-func (s *rsyslogSuite) TestStateServerUpgradeIdempotent(c *gc.C) {
1548- s.TestStateServerUpgrade(c)
1549- s.TestStateServerUpgrade(c)
1550-}
1551-
1552-func (s *rsyslogSuite) TestHostMachineUpgrade(c *gc.C) {
1553- err := upgrades.UpgradeHostMachineRsyslogConfig(s.ctx)
1554- c.Assert(err, gc.IsNil)
1555-
1556- data, err := ioutil.ReadFile(s.syslogPath)
1557- c.Assert(err, gc.IsNil)
1558- c.Assert(string(data), gc.Equals, syslogtesting.ExpectedForwardSyslogConf(c, "machine-tag", "namespace", 2345))
1559-}
1560-
1561-func (s *rsyslogSuite) TestHostServerUpgradeIdempotent(c *gc.C) {
1562- s.TestHostMachineUpgrade(c)
1563- s.TestHostMachineUpgrade(c)
1564-}
1565
1566=== modified file 'upgrades/steps118.go'
1567--- upgrades/steps118.go 2014-02-16 23:35:10 +0000
1568+++ upgrades/steps118.go 2014-02-25 14:03:09 +0000
1569@@ -11,15 +11,5 @@
1570 targets: []Target{HostMachine},
1571 run: ensureLockDirExistsAndUbuntuWritable,
1572 },
1573- &upgradeStep{
1574- description: "upgrade rsyslog config file on state server",
1575- targets: []Target{StateServer},
1576- run: upgradeStateServerRsyslogConfig,
1577- },
1578- &upgradeStep{
1579- description: "upgrade rsyslog config file on host machine",
1580- targets: []Target{HostMachine},
1581- run: upgradeHostMachineRsyslogConfig,
1582- },
1583 }
1584 }
1585
1586=== modified file 'upgrades/steps118_test.go'
1587--- upgrades/steps118_test.go 2014-02-13 05:22:01 +0000
1588+++ upgrades/steps118_test.go 2014-02-25 14:03:09 +0000
1589@@ -18,12 +18,10 @@
1590
1591 var expectedSteps = []string{
1592 "make $DATADIR/locks owned by ubuntu:ubuntu",
1593- "upgrade rsyslog config file on state server",
1594- "upgrade rsyslog config file on host machine",
1595 }
1596
1597 func (s *steps118Suite) TestUpgradeOperationsContent(c *gc.C) {
1598 upgradeSteps := upgrades.StepsFor118()
1599- c.Assert(upgradeSteps, gc.HasLen, 3)
1600+ c.Assert(upgradeSteps, gc.HasLen, 1)
1601 assertExpectedSteps(c, upgradeSteps, expectedSteps)
1602 }
1603
1604=== modified file 'worker/deployer/export_test.go'
1605--- worker/deployer/export_test.go 2013-11-25 03:52:09 +0000
1606+++ worker/deployer/export_test.go 2014-02-25 14:03:09 +0000
1607@@ -12,9 +12,8 @@
1608
1609 func (*fakeAPI) ConnectionInfo() (params.DeployerConnectionValues, error) {
1610 return params.DeployerConnectionValues{
1611- []string{"s1:123", "s2:123"},
1612- []string{"a1:123", "a2:123"},
1613- 2345,
1614+ StateAddresses: []string{"s1:123", "s2:123"},
1615+ APIAddresses: []string{"a1:123", "a2:123"},
1616 }, nil
1617 }
1618
1619
1620=== modified file 'worker/deployer/simple.go'
1621--- worker/deployer/simple.go 2014-02-12 07:24:47 +0000
1622+++ worker/deployer/simple.go 2014-02-25 14:03:09 +0000
1623@@ -54,9 +54,6 @@
1624 // will be written. It is set for testing and left empty for production, in
1625 // which case the system default is used, typically /etc/rsyslog.d
1626 syslogConfigDir string
1627-
1628- // syslogConfigPath is the full path name of the syslog conf file.
1629- syslogConfigPath string
1630 }
1631
1632 var _ Context = (*SimpleContext)(nil)
1633@@ -100,6 +97,7 @@
1634 logger.Debugf("API addresses: %q", result.APIAddresses)
1635 containerType := ctx.agentConfig.Value(agent.ContainerType)
1636 namespace := ctx.agentConfig.Value(agent.Namespace)
1637+ // TODO(axw) record rsyslog config dir.
1638 conf, err := agent.NewAgentConfig(
1639 agent.AgentConfigParams{
1640 DataDir: dataDir,
1641@@ -125,18 +123,6 @@
1642
1643 // Install an upstart job that runs the unit agent.
1644 logPath := path.Join(ctx.logDir, tag+".log")
1645- syslogConfigRenderer := syslog.NewForwardConfig(tag, result.SyslogPort, namespace, result.StateAddresses)
1646- syslogConfigRenderer.ConfigDir = ctx.syslogConfigDir
1647- syslogConfigRenderer.ConfigFileName = fmt.Sprintf("26-juju-%s.conf", tag)
1648- if err := syslogConfigRenderer.Write(); err != nil {
1649- return err
1650- }
1651- ctx.syslogConfigPath = syslogConfigRenderer.ConfigFilePath()
1652- if err := syslog.Restart(); err != nil {
1653- logger.Warningf("installer: cannot restart syslog daemon: %v", err)
1654- }
1655- defer removeOnErr(&err, ctx.syslogConfigPath)
1656-
1657 cmd := strings.Join([]string{
1658 path.Join(toolsDir, "jujud"), "unit",
1659 "--data-dir", dataDir,
1660@@ -190,9 +176,6 @@
1661 if err := os.RemoveAll(agentDir); err != nil {
1662 return err
1663 }
1664- if err := os.Remove(ctx.syslogConfigPath); err != nil && !os.IsNotExist(err) {
1665- logger.Warningf("installer: cannot remove %q: %v", ctx.syslogConfigPath, err)
1666- }
1667 // Defer this so a failure here does not impede the cleanup (as in tests).
1668 defer func() {
1669 if err := syslog.Restart(); err != nil {
1670
1671=== modified file 'worker/deployer/simple_test.go'
1672--- worker/deployer/simple_test.go 2013-11-21 03:22:49 +0000
1673+++ worker/deployer/simple_test.go 2014-02-25 14:03:09 +0000
1674@@ -198,12 +198,11 @@
1675 return deployer.NewTestSimpleContext(config, fix.initDir, fix.logDir, fix.syslogConfigDir)
1676 }
1677
1678-func (fix *SimpleToolsFixture) paths(tag string) (confPath, agentDir, toolsDir, syslogConfPath string) {
1679+func (fix *SimpleToolsFixture) paths(tag string) (confPath, agentDir, toolsDir string) {
1680 confName := fmt.Sprintf("jujud-%s.conf", tag)
1681 confPath = filepath.Join(fix.initDir, confName)
1682 agentDir = agent.Dir(fix.dataDir, tag)
1683 toolsDir = tools.ToolsDir(fix.dataDir, tag)
1684- syslogConfPath = filepath.Join(fix.syslogConfigDir, fmt.Sprintf("26-juju-%s.conf", tag))
1685 return
1686 }
1687
1688@@ -225,7 +224,7 @@
1689
1690 func (fix *SimpleToolsFixture) checkUnitInstalled(c *gc.C, name, password string) {
1691 tag := names.UnitTag(name)
1692- uconfPath, _, toolsDir, syslogConfPath := fix.paths(tag)
1693+ uconfPath, _, toolsDir := fix.paths(tag)
1694 uconfData, err := ioutil.ReadFile(uconfPath)
1695 c.Assert(err, gc.IsNil)
1696 uconf := string(uconfData)
1697@@ -261,20 +260,12 @@
1698 jujudData, err := ioutil.ReadFile(jujudPath)
1699 c.Assert(err, gc.IsNil)
1700 c.Assert(string(jujudData), gc.Equals, fakeJujud)
1701-
1702- syslogConfData, err := ioutil.ReadFile(syslogConfPath)
1703- c.Assert(err, gc.IsNil)
1704- parts := strings.SplitN(name, "/", 2)
1705- unitTag := fmt.Sprintf("unit-%s-%s", parts[0], parts[1])
1706- expectedSyslogConfReplaced := fmt.Sprintf(expectedSyslogConf, unitTag, unitTag, unitTag)
1707- c.Assert(string(syslogConfData), gc.Equals, expectedSyslogConfReplaced)
1708-
1709 }
1710
1711 func (fix *SimpleToolsFixture) checkUnitRemoved(c *gc.C, name string) {
1712 tag := names.UnitTag(name)
1713- confPath, agentDir, toolsDir, syslogConfPath := fix.paths(tag)
1714- for _, path := range []string{confPath, agentDir, toolsDir, syslogConfPath} {
1715+ confPath, agentDir, toolsDir := fix.paths(tag)
1716+ for _, path := range []string{confPath, agentDir, toolsDir} {
1717 _, err := ioutil.ReadFile(path)
1718 if err == nil {
1719 c.Log("Warning: %q not removed as expected", path)
1720
1721=== modified file 'worker/provisioner/kvm-broker.go'
1722--- worker/provisioner/kvm-broker.go 2014-01-31 03:36:58 +0000
1723+++ worker/provisioner/kvm-broker.go 2014-02-25 14:03:09 +0000
1724@@ -84,7 +84,6 @@
1725 config.ProviderType,
1726 config.AuthorizedKeys,
1727 config.SSLHostnameVerification,
1728- config.SyslogPort,
1729 config.Proxy,
1730 config.AptProxy,
1731 ); err != nil {
1732
1733=== modified file 'worker/provisioner/lxc-broker.go'
1734--- worker/provisioner/lxc-broker.go 2014-01-31 03:36:58 +0000
1735+++ worker/provisioner/lxc-broker.go 2014-02-25 14:03:09 +0000
1736@@ -74,7 +74,6 @@
1737 config.ProviderType,
1738 config.AuthorizedKeys,
1739 config.SSLHostnameVerification,
1740- config.SyslogPort,
1741 config.Proxy,
1742 config.AptProxy,
1743 ); err != nil {
1744
1745=== modified file 'worker/provisioner/lxc-broker_test.go'
1746--- worker/provisioner/lxc-broker_test.go 2014-02-14 11:33:57 +0000
1747+++ worker/provisioner/lxc-broker_test.go 2014-02-25 14:03:09 +0000
1748@@ -285,6 +285,5 @@
1749 return params.ContainerConfig{
1750 ProviderType: "fake",
1751 AuthorizedKeys: coretesting.FakeAuthKeys,
1752- SSLHostnameVerification: true,
1753- SyslogPort: 2345}, nil
1754+ SSLHostnameVerification: true}, nil
1755 }
1756
1757=== added directory 'worker/rsyslog'
1758=== added file 'worker/rsyslog/worker.go'
1759--- worker/rsyslog/worker.go 1970-01-01 00:00:00 +0000
1760+++ worker/rsyslog/worker.go 2014-02-25 14:03:09 +0000
1761@@ -0,0 +1,245 @@
1762+// Copyright 2014 Canonical Ltd.
1763+// Licensed under the AGPLv3, see LICENCE file for details.
1764+
1765+package rsyslog
1766+
1767+import (
1768+ "fmt"
1769+ "io/ioutil"
1770+ "os"
1771+ "os/user"
1772+ "path/filepath"
1773+ "strconv"
1774+ "time"
1775+
1776+ "github.com/errgo/errgo"
1777+ "github.com/loggo/loggo"
1778+
1779+ "launchpad.net/juju-core/agent"
1780+ "launchpad.net/juju-core/cert"
1781+ "launchpad.net/juju-core/log/syslog"
1782+ "launchpad.net/juju-core/names"
1783+ apirsyslog "launchpad.net/juju-core/state/api/rsyslog"
1784+ "launchpad.net/juju-core/state/api/watcher"
1785+ "launchpad.net/juju-core/utils"
1786+ "launchpad.net/juju-core/worker"
1787+)
1788+
1789+var logger = loggo.GetLogger("juju.worker.rsyslog")
1790+
1791+const rsyslogConfDir = "/etc/rsyslog.d"
1792+
1793+// RsyslogMode describes how to configure rsyslog.
1794+type RsyslogMode int
1795+
1796+const (
1797+ RsyslogModeInvalid RsyslogMode = iota
1798+ // RsyslogModeForwarding is the mode in which
1799+ // rsyslog will be configured to forward logging
1800+ // to state servers.
1801+ RsyslogModeForwarding
1802+ // RsyslogModeAccumulate is the mode in which
1803+ // rsyslog will be configured to accumulate logging
1804+ // from other machines into an "all-machines.log".
1805+ RsyslogModeAccumulate
1806+)
1807+
1808+// RsyslogConfigHandler implements worker.NotifyWatchHandler, watching
1809+// environment configuration changes and generating new rsyslog
1810+// configuration.
1811+type RsyslogConfigHandler struct {
1812+ st *apirsyslog.State
1813+ mode RsyslogMode
1814+ syslogConfig *syslog.SyslogConfig
1815+ rsyslogConfPath string
1816+ rsyslogGnutlsInstalled bool
1817+}
1818+
1819+var _ worker.NotifyWatchHandler = (*RsyslogConfigHandler)(nil)
1820+
1821+// NewRsyslogConfigWorker returns a worker.Worker that watches
1822+// for config changes and updates rsyslog configuration based
1823+// on changes.
1824+// NewRsyslogConfigWorker also returns the path to the rsyslog
1825+// configuration file that is written, so that the agent may
1826+// remove it on shutdown.
1827+func NewRsyslogConfigWorker(st *apirsyslog.State, config agent.Config, mode RsyslogMode) (worker.Worker, error) {
1828+ namespace := config.Value(agent.Namespace)
1829+ var syslogConfig *syslog.SyslogConfig
1830+ if mode == RsyslogModeAccumulate {
1831+ syslogConfig = syslog.NewAccumulateConfig(config.Tag(), 0, namespace)
1832+ } else {
1833+ addr, err := config.APIAddresses()
1834+ if err != nil {
1835+ return nil, err
1836+ }
1837+ syslogConfig = syslog.NewForwardConfig(config.Tag(), 0, namespace, addr)
1838+ }
1839+
1840+ // Historically only machine-0 includes the namespace in the log
1841+ // dir/file; for backwards compatibility we continue the tradition.
1842+ if config.Tag() != "machine-0" {
1843+ namespace = ""
1844+ }
1845+ kind, err := names.TagKind(config.Tag())
1846+ if err != nil {
1847+ return nil, err
1848+ }
1849+ if kind == names.MachineTagKind {
1850+ if namespace == "" {
1851+ syslogConfig.ConfigFileName = "25-juju.conf"
1852+ } else {
1853+ syslogConfig.ConfigFileName = fmt.Sprintf("25-juju-%s.conf", namespace)
1854+ }
1855+ } else {
1856+ syslogConfig.ConfigFileName = fmt.Sprintf("26-juju-%s.conf", config.Tag())
1857+ }
1858+
1859+ if namespace != "" {
1860+ syslogConfig.LogDir += "-" + namespace
1861+ }
1862+ worker := worker.NewNotifyWorker(&RsyslogConfigHandler{
1863+ st: st,
1864+ mode: mode,
1865+ syslogConfig: syslogConfig,
1866+ })
1867+ return worker, nil
1868+}
1869+
1870+func (h *RsyslogConfigHandler) SetUp() (watcher.NotifyWatcher, error) {
1871+ if h.mode == RsyslogModeAccumulate {
1872+ if err := h.ensureCertificates(); err != nil {
1873+ return nil, errgo.Annotate(err, "failed to write rsyslog certificates")
1874+ }
1875+ }
1876+ return h.st.WatchForEnvironConfigChanges()
1877+}
1878+
1879+func (h *RsyslogConfigHandler) TearDown() error {
1880+ if err := os.Remove(h.syslogConfig.ConfigFilePath()); err == nil {
1881+ syslog.Restart()
1882+ }
1883+ return nil
1884+}
1885+
1886+func (h *RsyslogConfigHandler) Handle() error {
1887+ if !h.rsyslogGnutlsInstalled {
1888+ if err := utils.AptGetInstall("rsyslog-gnutls"); err != nil {
1889+ // apt-get may fail if another process has the lock,
1890+ // so keep we'll just exit and try again next time.
1891+ return errgo.Annotate(err, "cannot install rsyslog-gnutls")
1892+ }
1893+ h.rsyslogGnutlsInstalled = true
1894+ }
1895+ cfg, err := h.st.EnvironConfig()
1896+ if err != nil {
1897+ return errgo.Annotate(err, "cannot get environ config")
1898+ }
1899+ h.syslogConfig.Port = cfg.SyslogPort()
1900+ rsyslogCACert := cfg.RsyslogCACert()
1901+ if rsyslogCACert == nil {
1902+ return nil
1903+ }
1904+ if h.mode == RsyslogModeForwarding {
1905+ h.syslogConfig.TLSCACertPath = h.caCertPath()
1906+ if err := writeFileAtomic(h.syslogConfig.TLSCACertPath, rsyslogCACert, 0644, 0, 0); err != nil {
1907+ return err
1908+ }
1909+ }
1910+ data, err := h.syslogConfig.Render()
1911+ if err != nil {
1912+ return errgo.Annotate(err, "failed to render rsyslog configuration file")
1913+ }
1914+ if err := writeFileAtomic(h.syslogConfig.ConfigFilePath(), []byte(data), 0644, 0, 0); err != nil {
1915+ return errgo.Annotate(err, "failed to write rsyslog configuration file")
1916+ }
1917+ logger.Debugf("Reloading rsyslog configuration")
1918+ if err := syslog.Restart(); err != nil {
1919+ logger.Errorf("failed to reload rsyslog configuration")
1920+ return err
1921+ }
1922+ return nil
1923+}
1924+
1925+func (h *RsyslogConfigHandler) caCertPath() string {
1926+ return filepath.Join(h.syslogConfig.LogDir, "ca-cert.pem")
1927+}
1928+
1929+// ensureCertificates ensures that a CA certificate,
1930+// server certificate, and private key exist in the log
1931+// directory, and writes them if not. The CA certificate
1932+// is entered into the environment configuration to be
1933+// picked up by other agents.
1934+func (h *RsyslogConfigHandler) ensureCertificates() error {
1935+ // We write ca-cert.pem last, after propagating into state.
1936+ // If it's there, then there's nothing to do. Otherwise,
1937+ // start over.
1938+ caCertPEM := h.caCertPath()
1939+ if _, err := os.Stat(caCertPEM); err == nil {
1940+ return nil
1941+ }
1942+
1943+ // Files must be chowned to syslog:adm.
1944+ syslogUser, err := user.Lookup("syslog")
1945+ if err != nil {
1946+ return err
1947+ }
1948+ syslogUid, err := strconv.Atoi(syslogUser.Uid)
1949+ if err != nil {
1950+ return err
1951+ }
1952+ syslogGid, err := strconv.Atoi(syslogUser.Gid)
1953+ if err != nil {
1954+ return err
1955+ }
1956+
1957+ // Generate a new CA and server cert/key pairs.
1958+ // The CA key will be discarded after the server
1959+ // cert has been generated.
1960+ expiry := time.Now().UTC().AddDate(10, 0, 0)
1961+ caCertPEMBytes, caKeyPEMBytes, err := cert.NewCA("rsyslog", expiry)
1962+ if err != nil {
1963+ return err
1964+ }
1965+ rsyslogCertPEMBytes, rsyslogKeyPEMBytes, err := cert.NewServer(caCertPEMBytes, caKeyPEMBytes, expiry, nil)
1966+ if err != nil {
1967+ return err
1968+ }
1969+
1970+ // Update the environment config with the CA cert,
1971+ // so clients can configure rsyslog.
1972+ if err := h.st.SetRsyslogCert(caCertPEMBytes); err != nil {
1973+ return err
1974+ }
1975+
1976+ // Write the certificates and key. The CA certificate must be written last for idempotency.
1977+ h.syslogConfig.TLSCACertPath = caCertPEM
1978+ h.syslogConfig.TLSCertPath = filepath.Join(h.syslogConfig.LogDir, "rsyslog-cert.pem")
1979+ h.syslogConfig.TLSKeyPath = filepath.Join(h.syslogConfig.LogDir, "rsyslog-key.pem")
1980+ for _, pair := range []struct {
1981+ path string
1982+ data []byte
1983+ }{
1984+ {h.syslogConfig.TLSCertPath, rsyslogCertPEMBytes},
1985+ {h.syslogConfig.TLSKeyPath, rsyslogKeyPEMBytes},
1986+ {h.syslogConfig.TLSCACertPath, caCertPEMBytes},
1987+ } {
1988+ if err := writeFileAtomic(pair.path, pair.data, 0600, syslogUid, syslogGid); err != nil {
1989+ return err
1990+ }
1991+ }
1992+ return nil
1993+}
1994+
1995+func writeFileAtomic(path string, data []byte, mode os.FileMode, uid, gid int) error {
1996+ temp := path + ".temp"
1997+ if err := ioutil.WriteFile(temp, data, mode); err != nil {
1998+ return err
1999+ }
2000+ if uid != -1 {
2001+ if err := os.Chown(temp, uid, gid); err != nil {
2002+ return err
2003+ }
2004+ }
2005+ return os.Rename(temp, path)
2006+}

Subscribers

People subscribed via source and target branches

to status/vote changes: