github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/worker/rsyslog/rsyslog_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 // +build !windows 4 5 package rsyslog_test 6 7 import ( 8 "crypto/x509" 9 "encoding/pem" 10 "io/ioutil" 11 "os" 12 "path/filepath" 13 "strings" 14 "time" 15 16 "github.com/juju/names" 17 jc "github.com/juju/testing/checkers" 18 gc "gopkg.in/check.v1" 19 20 "github.com/juju/juju/api" 21 "github.com/juju/juju/cert" 22 "github.com/juju/juju/network" 23 "github.com/juju/juju/state" 24 coretesting "github.com/juju/juju/testing" 25 "github.com/juju/juju/utils/syslog" 26 "github.com/juju/juju/worker/rsyslog" 27 ) 28 29 func waitForRestart(c *gc.C, restarted chan struct{}) { 30 timeout := time.After(coretesting.LongWait) 31 for { 32 select { 33 case <-timeout: 34 c.Fatalf("timed out waiting for rsyslog to be restarted") 35 case <-restarted: 36 return 37 } 38 } 39 } 40 41 func assertPathExists(c *gc.C, path string) { 42 _, err := os.Stat(path) 43 c.Assert(err, jc.ErrorIsNil) 44 } 45 46 func (s *RsyslogSuite) TestStartStop(c *gc.C) { 47 st, m := s.OpenAPIAsNewMachine(c, state.JobHostUnits) 48 worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeForwarding, m.Tag(), "", []string{"0.1.2.3"}, s.ConfDir()) 49 c.Assert(err, jc.ErrorIsNil) 50 worker.Kill() 51 c.Assert(worker.Wait(), gc.IsNil) 52 } 53 54 func (s *RsyslogSuite) TestTearDown(c *gc.C) { 55 st, m := s.st, s.machine 56 worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeAccumulate, m.Tag(), "", []string{"0.1.2.3"}, s.ConfDir()) 57 c.Assert(err, jc.ErrorIsNil) 58 confFile := filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf") 59 // On worker teardown, the rsyslog config file should be removed. 60 defer func() { 61 _, err := os.Stat(confFile) 62 c.Assert(err, jc.Satisfies, os.IsNotExist) 63 }() 64 defer func() { c.Assert(worker.Wait(), gc.IsNil) }() 65 defer worker.Kill() 66 waitForFile(c, confFile) 67 } 68 69 func (s *RsyslogSuite) TestRsyslogCert(c *gc.C) { 70 st, m := s.st, s.machine 71 err := s.machine.SetProviderAddresses(network.NewAddress("example.com")) 72 c.Assert(err, jc.ErrorIsNil) 73 74 worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeAccumulate, m.Tag(), "", []string{"0.1.2.3"}, s.ConfDir()) 75 c.Assert(err, jc.ErrorIsNil) 76 defer func() { c.Assert(worker.Wait(), gc.IsNil) }() 77 defer worker.Kill() 78 filename := filepath.Join(s.ConfDir(), "rsyslog", "rsyslog-cert.pem") 79 waitForFile(c, filename) 80 81 rsyslogCertPEM, err := ioutil.ReadFile(filename) 82 c.Assert(err, jc.ErrorIsNil) 83 84 cert, err := cert.ParseCert(string(rsyslogCertPEM)) 85 c.Assert(err, jc.ErrorIsNil) 86 87 c.Assert(cert.DNSNames, gc.DeepEquals, []string{"example.com", "*"}) 88 89 subject := cert.Subject 90 c.Assert(subject.CommonName, gc.Equals, "*") 91 c.Assert(subject.Organization, gc.DeepEquals, []string{"juju"}) 92 93 issuer := cert.Issuer 94 c.Assert(issuer.CommonName, gc.Equals, "juju-generated CA for environment \"rsyslog\"") 95 c.Assert(issuer.Organization, gc.DeepEquals, []string{"juju"}) 96 } 97 98 func (s *RsyslogSuite) TestModeAccumulate(c *gc.C) { 99 st, m := s.st, s.machine 100 worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeAccumulate, m.Tag(), "", nil, s.ConfDir()) 101 c.Assert(err, jc.ErrorIsNil) 102 defer func() { c.Assert(worker.Wait(), gc.IsNil) }() 103 defer worker.Kill() 104 dirname := filepath.Join(s.ConfDir(), "rsyslog") 105 waitForFile(c, filepath.Join(dirname, "ca-cert.pem")) 106 107 // We should have ca-cert.pem, rsyslog-cert.pem, and rsyslog-key.pem. 108 caCertPEM, err := ioutil.ReadFile(filepath.Join(dirname, "ca-cert.pem")) 109 c.Assert(err, jc.ErrorIsNil) 110 rsyslogCertPEM, err := ioutil.ReadFile(filepath.Join(dirname, "rsyslog-cert.pem")) 111 c.Assert(err, jc.ErrorIsNil) 112 rsyslogKeyPEM, err := ioutil.ReadFile(filepath.Join(dirname, "rsyslog-key.pem")) 113 c.Assert(err, jc.ErrorIsNil) 114 115 _, _, err = cert.ParseCertAndKey(string(rsyslogCertPEM), string(rsyslogKeyPEM)) 116 c.Assert(err, jc.ErrorIsNil) 117 err = cert.Verify(string(rsyslogCertPEM), string(caCertPEM), time.Now().UTC()) 118 c.Assert(err, jc.ErrorIsNil) 119 120 // Verify rsyslog configuration. 121 waitForFile(c, filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf")) 122 rsyslogConf, err := ioutil.ReadFile(filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf")) 123 c.Assert(err, jc.ErrorIsNil) 124 125 syslogPort := s.Environ.Config().SyslogPort() 126 127 syslogConfig := &syslog.SyslogConfig{ 128 LogFileName: m.Tag().String(), 129 LogDir: *rsyslog.LogDir, 130 Port: syslogPort, 131 Namespace: "", 132 StateServerAddresses: []string{}, 133 } 134 135 syslog.NewAccumulateConfig(syslogConfig) 136 syslogConfig.ConfigDir = *rsyslog.RsyslogConfDir 137 syslogConfig.JujuConfigDir = filepath.Join(s.ConfDir(), "rsyslog") 138 rendered, err := syslogConfig.Render() 139 c.Assert(err, jc.ErrorIsNil) 140 141 c.Assert(string(rsyslogConf), gc.DeepEquals, string(rendered)) 142 143 // Verify logrotate files 144 assertPathExists(c, filepath.Join(dirname, "logrotate.conf")) 145 assertPathExists(c, filepath.Join(dirname, "logrotate.run")) 146 147 } 148 149 func (s *RsyslogSuite) TestAccumulateHA(c *gc.C) { 150 m := s.machine 151 152 syslogConfig := &syslog.SyslogConfig{ 153 LogFileName: m.Tag().String(), 154 LogDir: *rsyslog.LogDir, 155 Port: 6541, 156 Namespace: "", 157 StateServerAddresses: []string{"192.168.1", "127.0.0.1"}, 158 } 159 160 syslog.NewAccumulateConfig(syslogConfig) 161 syslogConfig.JujuConfigDir = filepath.Join(s.ConfDir(), "rsyslog") 162 rendered, err := syslogConfig.Render() 163 c.Assert(err, jc.ErrorIsNil) 164 165 stateServer1Config := ":syslogtag, startswith, \"juju-\" @@192.168.1:6541;LongTagForwardFormat" 166 stateServer2Config := ":syslogtag, startswith, \"juju-\" @@127.0.0.1:6541;LongTagForwardFormat" 167 168 c.Assert(strings.Contains(string(rendered), stateServer1Config), jc.IsTrue) 169 c.Assert(strings.Contains(string(rendered), stateServer2Config), jc.IsTrue) 170 } 171 172 // TestModeAccumulateCertsExist is a regression test for 173 // https://bugs.launchpad.net/juju-core/+bug/1464335, 174 // where the CA certs existing (in local provider) at 175 // bootstrap caused the worker to not publish to state. 176 func (s *RsyslogSuite) TestModeAccumulateCertsExistOnDisk(c *gc.C) { 177 dirname := filepath.Join(s.ConfDir(), "rsyslog") 178 err := os.MkdirAll(dirname, 0755) 179 c.Assert(err, jc.ErrorIsNil) 180 err = ioutil.WriteFile(filepath.Join(dirname, "ca-cert.pem"), nil, 0644) 181 c.Assert(err, jc.ErrorIsNil) 182 183 st, m := s.st, s.machine 184 worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeAccumulate, m.Tag(), "", nil, s.ConfDir()) 185 c.Assert(err, jc.ErrorIsNil) 186 // The worker should create certs and publish to state during setup, 187 // so we can kill and wait and be confident that the task is done. 188 worker.Kill() 189 c.Assert(worker.Wait(), jc.ErrorIsNil) 190 191 // The CA cert and key should have been published to state. 192 cfg, err := s.State.EnvironConfig() 193 c.Assert(err, jc.ErrorIsNil) 194 c.Assert(cfg.AllAttrs()["rsyslog-ca-cert"], gc.NotNil) 195 c.Assert(cfg.AllAttrs()["rsyslog-ca-key"], gc.NotNil) 196 197 // ca-cert.pem isn't updated on disk until the worker reacts to the 198 // state change. Let's just ensure that rsyslog-ca-cert is a valid 199 // certificate, and no the zero-length string we wrote to ca-cert.pem. 200 caCertPEM := cfg.AllAttrs()["rsyslog-ca-cert"].(string) 201 c.Assert(err, jc.ErrorIsNil) 202 block, _ := pem.Decode([]byte(caCertPEM)) 203 c.Assert(block, gc.NotNil) 204 _, err = x509.ParseCertificate(block.Bytes) 205 c.Assert(err, jc.ErrorIsNil) 206 } 207 208 func (s *RsyslogSuite) TestNamespace(c *gc.C) { 209 st := s.st 210 // set the rsyslog cert 211 err := s.APIState.Client().EnvironmentSet(map[string]interface{}{"rsyslog-ca-cert": coretesting.CACert}) 212 c.Assert(err, jc.ErrorIsNil) 213 214 // namespace only takes effect in filenames 215 // for machine-0; all others assume isolation. 216 s.testNamespace(c, st, names.NewMachineTag("0"), "", "25-juju.conf", *rsyslog.LogDir) 217 s.testNamespace(c, st, names.NewMachineTag("0"), "mynamespace", "25-juju-mynamespace.conf", *rsyslog.LogDir+"-mynamespace") 218 s.testNamespace(c, st, names.NewMachineTag("1"), "", "25-juju.conf", *rsyslog.LogDir) 219 s.testNamespace(c, st, names.NewMachineTag("1"), "mynamespace", "25-juju.conf", *rsyslog.LogDir) 220 s.testNamespace(c, st, names.NewUnitTag("myservice/0"), "", "26-juju-unit-myservice-0.conf", *rsyslog.LogDir) 221 s.testNamespace(c, st, names.NewUnitTag("myservice/0"), "mynamespace", "26-juju-unit-myservice-0.conf", *rsyslog.LogDir) 222 } 223 224 // testNamespace starts a worker and ensures that 225 // the rsyslog config file has the expected filename, 226 // and the appropriate log dir is used. 227 func (s *RsyslogSuite) testNamespace(c *gc.C, st api.Connection, tag names.Tag, namespace, expectedFilename, expectedLogDir string) { 228 restarted := make(chan struct{}, 2) // once for create, once for teardown 229 s.PatchValue(rsyslog.RestartRsyslog, func() error { 230 restarted <- struct{}{} 231 return nil 232 }) 233 234 err := os.MkdirAll(expectedLogDir, 0755) 235 c.Assert(err, jc.ErrorIsNil) 236 worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), 237 rsyslog.RsyslogModeAccumulate, tag, namespace, []string{"0.1.2.3"}, s.ConfDir()) 238 c.Assert(err, jc.ErrorIsNil) 239 defer func() { c.Assert(worker.Wait(), gc.IsNil) }() 240 defer worker.Kill() 241 242 // change the API HostPorts to trigger an rsyslog restart 243 newHostPorts := network.NewHostPorts(6541, "127.0.0.1") 244 err = s.State.SetAPIHostPorts([][]network.HostPort{newHostPorts}) 245 c.Assert(err, jc.ErrorIsNil) 246 247 // Wait for rsyslog to be restarted, so we can check to see 248 // what the name of the config file is. 249 waitForRestart(c, restarted) 250 251 // Ensure that ca-cert.pem gets written to the expected log dir. 252 dirname := filepath.Join(s.ConfDir(), "rsyslog") 253 waitForFile(c, filepath.Join(dirname, "ca-cert.pem")) 254 255 dir, err := os.Open(*rsyslog.RsyslogConfDir) 256 c.Assert(err, jc.ErrorIsNil) 257 names, err := dir.Readdirnames(-1) 258 dir.Close() 259 c.Assert(err, jc.ErrorIsNil) 260 c.Assert(names, gc.HasLen, 1) 261 c.Assert(names[0], gc.Equals, expectedFilename) 262 }