github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/worker/rsyslog/rsyslog_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package rsyslog_test 5 6 import ( 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 "strings" 11 stdtesting "testing" 12 "time" 13 14 "github.com/juju/testing" 15 jc "github.com/juju/testing/checkers" 16 gc "launchpad.net/gocheck" 17 18 "github.com/juju/juju/cert" 19 "github.com/juju/juju/instance" 20 jujutesting "github.com/juju/juju/juju/testing" 21 "github.com/juju/juju/state" 22 "github.com/juju/juju/state/api" 23 coretesting "github.com/juju/juju/testing" 24 "github.com/juju/juju/utils/syslog" 25 "github.com/juju/juju/worker/rsyslog" 26 ) 27 28 func TestPackage(t *stdtesting.T) { 29 coretesting.MgoTestPackage(t) 30 } 31 32 type RsyslogSuite struct { 33 jujutesting.JujuConnSuite 34 35 st *api.State 36 machine *state.Machine 37 } 38 39 var _ = gc.Suite(&RsyslogSuite{}) 40 41 func (s *RsyslogSuite) SetUpSuite(c *gc.C) { 42 s.JujuConnSuite.SetUpSuite(c) 43 // TODO(waigani) 2014-03-19 bug 1294462 44 // Add patch for suite functions 45 restore := testing.PatchValue(rsyslog.LookupUser, func(username string) (uid, gid int, err error) { 46 // worker will not attempt to chown files if uid/gid is 0 47 return 0, 0, nil 48 }) 49 s.AddSuiteCleanup(func(*gc.C) { restore() }) 50 } 51 52 func (s *RsyslogSuite) SetUpTest(c *gc.C) { 53 s.JujuConnSuite.SetUpTest(c) 54 s.PatchValue(rsyslog.RestartRsyslog, func() error { return nil }) 55 s.PatchValue(rsyslog.LogDir, c.MkDir()) 56 s.PatchValue(rsyslog.RsyslogConfDir, c.MkDir()) 57 58 s.st, s.machine = s.OpenAPIAsNewMachine(c, state.JobManageEnviron) 59 err := s.machine.SetAddresses(instance.NewAddress("0.1.2.3", instance.NetworkUnknown)) 60 c.Assert(err, gc.IsNil) 61 } 62 63 func waitForFile(c *gc.C, file string) { 64 timeout := time.After(coretesting.LongWait) 65 for { 66 select { 67 case <-timeout: 68 c.Fatalf("timed out waiting for %s to be written", file) 69 case <-time.After(coretesting.ShortWait): 70 if _, err := os.Stat(file); err == nil { 71 return 72 } 73 } 74 } 75 } 76 77 func waitForRestart(c *gc.C, restarted chan struct{}) { 78 timeout := time.After(coretesting.LongWait) 79 for { 80 select { 81 case <-timeout: 82 c.Fatalf("timed out waiting for rsyslog to be restarted") 83 case <-restarted: 84 return 85 } 86 } 87 } 88 89 func (s *RsyslogSuite) TestStartStop(c *gc.C) { 90 st, m := s.OpenAPIAsNewMachine(c, state.JobHostUnits) 91 worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeForwarding, m.Tag(), "", []string{"0.1.2.3"}) 92 c.Assert(err, gc.IsNil) 93 worker.Kill() 94 c.Assert(worker.Wait(), gc.IsNil) 95 } 96 97 func (s *RsyslogSuite) TestTearDown(c *gc.C) { 98 st, m := s.st, s.machine 99 worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeAccumulate, m.Tag(), "", []string{"0.1.2.3"}) 100 c.Assert(err, gc.IsNil) 101 confFile := filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf") 102 // On worker teardown, the rsyslog config file should be removed. 103 defer func() { 104 _, err := os.Stat(confFile) 105 c.Assert(err, jc.Satisfies, os.IsNotExist) 106 }() 107 defer func() { c.Assert(worker.Wait(), gc.IsNil) }() 108 defer worker.Kill() 109 waitForFile(c, confFile) 110 } 111 112 func (s *RsyslogSuite) TestModeForwarding(c *gc.C) { 113 err := s.APIState.Client().EnvironmentSet(map[string]interface{}{"rsyslog-ca-cert": coretesting.CACert}) 114 c.Assert(err, gc.IsNil) 115 st, m := s.OpenAPIAsNewMachine(c, state.JobHostUnits) 116 addrs := []string{"0.1.2.3", "0.2.4.6"} 117 worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeForwarding, m.Tag(), "", addrs) 118 c.Assert(err, gc.IsNil) 119 defer func() { c.Assert(worker.Wait(), gc.IsNil) }() 120 defer worker.Kill() 121 122 // We should get a ca-cert.pem with the contents introduced into state config. 123 waitForFile(c, filepath.Join(*rsyslog.LogDir, "ca-cert.pem")) 124 caCertPEM, err := ioutil.ReadFile(filepath.Join(*rsyslog.LogDir, "ca-cert.pem")) 125 c.Assert(err, gc.IsNil) 126 c.Assert(string(caCertPEM), gc.DeepEquals, coretesting.CACert) 127 128 // Verify rsyslog configuration. 129 waitForFile(c, filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf")) 130 rsyslogConf, err := ioutil.ReadFile(filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf")) 131 c.Assert(err, gc.IsNil) 132 133 syslogPort := s.Conn.Environ.Config().SyslogPort() 134 syslogConfig := syslog.NewForwardConfig(m.Tag(), *rsyslog.LogDir, syslogPort, "", addrs) 135 syslogConfig.ConfigDir = *rsyslog.RsyslogConfDir 136 rendered, err := syslogConfig.Render() 137 c.Assert(err, gc.IsNil) 138 c.Assert(string(rsyslogConf), gc.DeepEquals, string(rendered)) 139 } 140 141 func (s *RsyslogSuite) TestModeAccumulate(c *gc.C) { 142 st, m := s.st, s.machine 143 worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeAccumulate, m.Tag(), "", nil) 144 c.Assert(err, gc.IsNil) 145 defer func() { c.Assert(worker.Wait(), gc.IsNil) }() 146 defer worker.Kill() 147 waitForFile(c, filepath.Join(*rsyslog.LogDir, "ca-cert.pem")) 148 149 // We should have ca-cert.pem, rsyslog-cert.pem, and rsyslog-key.pem. 150 caCertPEM, err := ioutil.ReadFile(filepath.Join(*rsyslog.LogDir, "ca-cert.pem")) 151 c.Assert(err, gc.IsNil) 152 rsyslogCertPEM, err := ioutil.ReadFile(filepath.Join(*rsyslog.LogDir, "rsyslog-cert.pem")) 153 c.Assert(err, gc.IsNil) 154 rsyslogKeyPEM, err := ioutil.ReadFile(filepath.Join(*rsyslog.LogDir, "rsyslog-key.pem")) 155 c.Assert(err, gc.IsNil) 156 _, _, err = cert.ParseCertAndKey(string(rsyslogCertPEM), string(rsyslogKeyPEM)) 157 c.Assert(err, gc.IsNil) 158 err = cert.Verify(string(rsyslogCertPEM), string(caCertPEM), time.Now().UTC()) 159 c.Assert(err, gc.IsNil) 160 161 // Verify rsyslog configuration. 162 waitForFile(c, filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf")) 163 rsyslogConf, err := ioutil.ReadFile(filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf")) 164 c.Assert(err, gc.IsNil) 165 166 syslogPort := s.Conn.Environ.Config().SyslogPort() 167 syslogConfig := syslog.NewAccumulateConfig(m.Tag(), *rsyslog.LogDir, syslogPort, "", []string{}) 168 syslogConfig.ConfigDir = *rsyslog.RsyslogConfDir 169 rendered, err := syslogConfig.Render() 170 c.Assert(err, gc.IsNil) 171 172 c.Assert(string(rsyslogConf), gc.DeepEquals, string(rendered)) 173 } 174 175 func (s *RsyslogSuite) TestAccumulateHA(c *gc.C) { 176 m := s.machine 177 syslogConfig := syslog.NewAccumulateConfig(m.Tag(), *rsyslog.LogDir, 6541, "", []string{"192.168.1", "127.0.0.1"}) 178 rendered, err := syslogConfig.Render() 179 c.Assert(err, gc.IsNil) 180 181 stateServer1Config := ":syslogtag, startswith, \"juju-\" @@192.168.1:6541;LongTagForwardFormat" 182 stateServer2Config := ":syslogtag, startswith, \"juju-\" @@127.0.0.1:6541;LongTagForwardFormat" 183 184 c.Assert(strings.Contains(string(rendered), stateServer1Config), gc.Equals, true) 185 c.Assert(strings.Contains(string(rendered), stateServer2Config), gc.Equals, true) 186 } 187 188 func (s *RsyslogSuite) TestNamespace(c *gc.C) { 189 st := s.st 190 // set the rsyslog cert 191 err := s.APIState.Client().EnvironmentSet(map[string]interface{}{"rsyslog-ca-cert": coretesting.CACert}) 192 c.Assert(err, gc.IsNil) 193 194 // namespace only takes effect in filenames 195 // for machine-0; all others assume isolation. 196 s.testNamespace(c, st, "machine-0", "", "25-juju.conf", *rsyslog.LogDir) 197 s.testNamespace(c, st, "machine-0", "mynamespace", "25-juju-mynamespace.conf", *rsyslog.LogDir+"-mynamespace") 198 s.testNamespace(c, st, "machine-1", "", "25-juju.conf", *rsyslog.LogDir) 199 s.testNamespace(c, st, "machine-1", "mynamespace", "25-juju.conf", *rsyslog.LogDir) 200 s.testNamespace(c, st, "unit-myservice-0", "", "26-juju-unit-myservice-0.conf", *rsyslog.LogDir) 201 s.testNamespace(c, st, "unit-myservice-0", "mynamespace", "26-juju-unit-myservice-0.conf", *rsyslog.LogDir) 202 } 203 204 // testNamespace starts a worker and ensures that 205 // the rsyslog config file has the expected filename, 206 // and the appropriate log dir is used. 207 func (s *RsyslogSuite) testNamespace(c *gc.C, st *api.State, tag, namespace, expectedFilename, expectedLogDir string) { 208 restarted := make(chan struct{}, 2) // once for create, once for teardown 209 s.PatchValue(rsyslog.RestartRsyslog, func() error { 210 restarted <- struct{}{} 211 return nil 212 }) 213 214 err := os.MkdirAll(expectedLogDir, 0755) 215 c.Assert(err, gc.IsNil) 216 worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeForwarding, tag, namespace, []string{"0.1.2.3"}) 217 c.Assert(err, gc.IsNil) 218 defer func() { c.Assert(worker.Wait(), gc.IsNil) }() 219 defer worker.Kill() 220 221 // change the API HostPorts to trigger an rsyslog restart 222 newHostPorts := instance.AddressesWithPort(instance.NewAddresses("127.0.0.1"), 6541) 223 err = s.State.SetAPIHostPorts([][]instance.HostPort{newHostPorts}) 224 c.Assert(err, gc.IsNil) 225 226 // Wait for rsyslog to be restarted, so we can check to see 227 // what the name of the config file is. 228 waitForRestart(c, restarted) 229 230 // Ensure that ca-cert.pem gets written to the expected log dir. 231 waitForFile(c, filepath.Join(expectedLogDir, "ca-cert.pem")) 232 233 dir, err := os.Open(*rsyslog.RsyslogConfDir) 234 c.Assert(err, gc.IsNil) 235 names, err := dir.Readdirnames(-1) 236 dir.Close() 237 c.Assert(err, gc.IsNil) 238 c.Assert(names, gc.HasLen, 1) 239 c.Assert(names[0], gc.Equals, expectedFilename) 240 }