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  }