github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/worker/introspection/socket_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package introspection_test
     5  
     6  import (
     7  	"bufio"
     8  	"bytes"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"net"
    12  	"os"
    13  	"regexp"
    14  	"runtime"
    15  
    16  	"github.com/juju/testing"
    17  	jc "github.com/juju/testing/checkers"
    18  	gc "gopkg.in/check.v1"
    19  
    20  	"github.com/juju/juju/worker"
    21  	"github.com/juju/juju/worker/introspection"
    22  	"github.com/juju/juju/worker/workertest"
    23  )
    24  
    25  type suite struct {
    26  	testing.IsolationSuite
    27  }
    28  
    29  var _ = gc.Suite(&suite{})
    30  
    31  func (s *suite) TestConfigValidation(c *gc.C) {
    32  	w, err := introspection.NewWorker(introspection.Config{})
    33  	c.Check(w, gc.IsNil)
    34  	c.Assert(err, gc.ErrorMatches, "empty SocketName not valid")
    35  }
    36  
    37  func (s *suite) TestStartStop(c *gc.C) {
    38  	if runtime.GOOS != "linux" {
    39  		c.Skip("introspection worker not supported on non-linux")
    40  	}
    41  
    42  	w, err := introspection.NewWorker(introspection.Config{
    43  		SocketName: "introspection-test",
    44  	})
    45  	c.Assert(err, jc.ErrorIsNil)
    46  	workertest.CheckKill(c, w)
    47  }
    48  
    49  type introspectionSuite struct {
    50  	testing.IsolationSuite
    51  
    52  	name     string
    53  	worker   worker.Worker
    54  	reporter introspection.DepEngineReporter
    55  }
    56  
    57  var _ = gc.Suite(&introspectionSuite{})
    58  
    59  func (s *introspectionSuite) SetUpTest(c *gc.C) {
    60  	if runtime.GOOS != "linux" {
    61  		c.Skip("introspection worker not supported on non-linux")
    62  	}
    63  	s.IsolationSuite.SetUpTest(c)
    64  	s.reporter = nil
    65  	s.worker = nil
    66  	s.startWorker(c)
    67  }
    68  
    69  func (s *introspectionSuite) startWorker(c *gc.C) {
    70  	s.name = fmt.Sprintf("introspection-test-%d", os.Getpid())
    71  	w, err := introspection.NewWorker(introspection.Config{
    72  		SocketName: s.name,
    73  		Reporter:   s.reporter,
    74  	})
    75  	c.Assert(err, jc.ErrorIsNil)
    76  	s.worker = w
    77  	s.AddCleanup(func(c *gc.C) {
    78  		workertest.CheckKill(c, w)
    79  	})
    80  }
    81  
    82  func (s *introspectionSuite) call(c *gc.C, url string) []byte {
    83  	path := "@" + s.name
    84  	conn, err := net.Dial("unix", path)
    85  	c.Assert(err, jc.ErrorIsNil)
    86  	defer conn.Close()
    87  
    88  	_, err = fmt.Fprintf(conn, "GET %s HTTP/1.0\r\n\r\n", url)
    89  	c.Assert(err, jc.ErrorIsNil)
    90  
    91  	buf, err := ioutil.ReadAll(conn)
    92  	c.Assert(err, jc.ErrorIsNil)
    93  	return buf
    94  }
    95  
    96  func (s *introspectionSuite) TestCmdLine(c *gc.C) {
    97  	buf := s.call(c, "/debug/pprof/cmdline")
    98  	c.Assert(buf, gc.NotNil)
    99  	matches(c, buf, ".*/introspection.test")
   100  }
   101  
   102  func (s *introspectionSuite) TestGoroutineProfile(c *gc.C) {
   103  	buf := s.call(c, "/debug/pprof/goroutine")
   104  	c.Assert(buf, gc.NotNil)
   105  	matches(c, buf, `^goroutine profile: total \d+`)
   106  }
   107  
   108  func (s *introspectionSuite) TestMissingReporter(c *gc.C) {
   109  	buf := s.call(c, "/depengine/")
   110  	matches(c, buf, "404 Not Found")
   111  	matches(c, buf, "missing reporter")
   112  }
   113  
   114  func (s *introspectionSuite) TestEngineReporter(c *gc.C) {
   115  	// We need to make sure the existing worker is shut down
   116  	// so we can connect to the socket.
   117  	workertest.CheckKill(c, s.worker)
   118  	s.reporter = &reporter{
   119  		values: map[string]interface{}{
   120  			"working": true,
   121  		},
   122  	}
   123  	s.startWorker(c)
   124  	buf := s.call(c, "/depengine/")
   125  
   126  	matches(c, buf, "200 OK")
   127  	matches(c, buf, "working: true")
   128  }
   129  
   130  // matches fails if regex is not found in the contents of b.
   131  // b is expected to be the response from the pprof http server, and will
   132  // contain some HTTP preamble that should be ignored.
   133  func matches(c *gc.C, b []byte, regex string) {
   134  	re, err := regexp.Compile(regex)
   135  	c.Assert(err, jc.ErrorIsNil)
   136  	r := bytes.NewReader(b)
   137  	sc := bufio.NewScanner(r)
   138  	for sc.Scan() {
   139  		if re.MatchString(sc.Text()) {
   140  			return
   141  		}
   142  	}
   143  	c.Fatalf("%q did not match regex %q", string(b), regex)
   144  }
   145  
   146  type reporter struct {
   147  	values map[string]interface{}
   148  }
   149  
   150  func (r *reporter) Report() map[string]interface{} {
   151  	return r.values
   152  }