github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/worker/uniter/runner/flush_test.go (about)

     1  // Copyright 2012-2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package runner_test
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/testing"
    11  	jc "github.com/juju/testing/checkers"
    12  	"github.com/juju/utils"
    13  	gc "gopkg.in/check.v1"
    14  
    15  	"github.com/juju/juju/apiserver/params"
    16  	"github.com/juju/juju/network"
    17  	"github.com/juju/juju/worker/uniter/metrics"
    18  	"github.com/juju/juju/worker/uniter/runner"
    19  )
    20  
    21  type FlushContextSuite struct {
    22  	HookContextSuite
    23  	stub testing.Stub
    24  }
    25  
    26  var _ = gc.Suite(&FlushContextSuite{})
    27  
    28  func (s *FlushContextSuite) SetUpTest(c *gc.C) {
    29  	s.HookContextSuite.SetUpTest(c)
    30  	s.stub.ResetCalls()
    31  }
    32  
    33  func (s *FlushContextSuite) TestRunHookRelationFlushingError(c *gc.C) {
    34  	ctx := s.context(c)
    35  
    36  	// Mess with multiple relation settings.
    37  	relCtx0, ok := ctx.Relation(0)
    38  	c.Assert(ok, jc.IsTrue)
    39  	node0, err := relCtx0.Settings()
    40  	c.Assert(err, jc.ErrorIsNil)
    41  	node0.Set("foo", "1")
    42  	relCtx1, ok := ctx.Relation(1)
    43  	c.Assert(ok, jc.IsTrue)
    44  	node1, err := relCtx1.Settings()
    45  	c.Assert(err, jc.ErrorIsNil)
    46  	node1.Set("bar", "2")
    47  
    48  	// Flush the context with a failure.
    49  	err = ctx.Flush("some badge", errors.New("blam pow"))
    50  	c.Assert(err, gc.ErrorMatches, "blam pow")
    51  
    52  	// Check that the changes have not been written to state.
    53  	settings0, err := s.relunits[0].ReadSettings("u/0")
    54  	c.Assert(err, jc.ErrorIsNil)
    55  	c.Assert(settings0, gc.DeepEquals, map[string]interface{}{"relation-name": "db0"})
    56  	settings1, err := s.relunits[1].ReadSettings("u/0")
    57  	c.Assert(err, jc.ErrorIsNil)
    58  	c.Assert(settings1, gc.DeepEquals, map[string]interface{}{"relation-name": "db1"})
    59  }
    60  
    61  func (s *FlushContextSuite) TestRunHookRelationFlushingSuccess(c *gc.C) {
    62  	ctx := s.context(c)
    63  
    64  	// Mess with multiple relation settings.
    65  	relCtx0, ok := ctx.Relation(0)
    66  	c.Assert(ok, jc.IsTrue)
    67  	node0, err := relCtx0.Settings()
    68  	c.Assert(err, jc.ErrorIsNil)
    69  	node0.Set("baz", "3")
    70  	relCtx1, ok := ctx.Relation(1)
    71  	c.Assert(ok, jc.IsTrue)
    72  	node1, err := relCtx1.Settings()
    73  	c.Assert(err, jc.ErrorIsNil)
    74  	node1.Set("qux", "4")
    75  
    76  	// Flush the context with a success.
    77  	err = ctx.Flush("some badge", nil)
    78  	c.Assert(err, jc.ErrorIsNil)
    79  
    80  	// Check that the changes have been written to state.
    81  	settings0, err := s.relunits[0].ReadSettings("u/0")
    82  	c.Assert(err, jc.ErrorIsNil)
    83  	c.Assert(settings0, gc.DeepEquals, map[string]interface{}{
    84  		"relation-name": "db0",
    85  		"baz":           "3",
    86  	})
    87  	settings1, err := s.relunits[1].ReadSettings("u/0")
    88  	c.Assert(err, jc.ErrorIsNil)
    89  	c.Assert(settings1, gc.DeepEquals, map[string]interface{}{
    90  		"relation-name": "db1",
    91  		"qux":           "4",
    92  	})
    93  }
    94  
    95  func (s *FlushContextSuite) TestRunHookOpensAndClosesPendingPorts(c *gc.C) {
    96  	// Initially, no port ranges are open on the unit or its machine.
    97  	unitRanges, err := s.unit.OpenedPorts()
    98  	c.Assert(err, jc.ErrorIsNil)
    99  	c.Assert(unitRanges, gc.HasLen, 0)
   100  	machinePorts, err := s.machine.AllPorts()
   101  	c.Assert(err, jc.ErrorIsNil)
   102  	c.Assert(machinePorts, gc.HasLen, 0)
   103  
   104  	// Add another unit on the same machine.
   105  	otherUnit, err := s.service.AddUnit()
   106  	c.Assert(err, jc.ErrorIsNil)
   107  	err = otherUnit.AssignToMachine(s.machine)
   108  	c.Assert(err, jc.ErrorIsNil)
   109  
   110  	// Open some ports on both units.
   111  	err = s.unit.OpenPorts("tcp", 100, 200)
   112  	c.Assert(err, jc.ErrorIsNil)
   113  	err = otherUnit.OpenPorts("udp", 200, 300)
   114  	c.Assert(err, jc.ErrorIsNil)
   115  
   116  	unitRanges, err = s.unit.OpenedPorts()
   117  	c.Assert(err, jc.ErrorIsNil)
   118  	c.Assert(unitRanges, jc.DeepEquals, []network.PortRange{
   119  		{100, 200, "tcp"},
   120  	})
   121  
   122  	ctx := s.context(c)
   123  
   124  	// Try opening some ports via the context.
   125  	err = ctx.OpenPorts("tcp", 100, 200)
   126  	c.Assert(err, jc.ErrorIsNil) // duplicates are ignored
   127  	err = ctx.OpenPorts("udp", 200, 300)
   128  	c.Assert(err, gc.ErrorMatches, `cannot open 200-300/udp \(unit "u/0"\): conflicts with existing 200-300/udp \(unit "u/1"\)`)
   129  	err = ctx.OpenPorts("udp", 100, 200)
   130  	c.Assert(err, gc.ErrorMatches, `cannot open 100-200/udp \(unit "u/0"\): conflicts with existing 200-300/udp \(unit "u/1"\)`)
   131  	err = ctx.OpenPorts("udp", 10, 20)
   132  	c.Assert(err, jc.ErrorIsNil)
   133  	err = ctx.OpenPorts("tcp", 50, 100)
   134  	c.Assert(err, gc.ErrorMatches, `cannot open 50-100/tcp \(unit "u/0"\): conflicts with existing 100-200/tcp \(unit "u/0"\)`)
   135  	err = ctx.OpenPorts("tcp", 50, 80)
   136  	c.Assert(err, jc.ErrorIsNil)
   137  	err = ctx.OpenPorts("tcp", 40, 90)
   138  	c.Assert(err, gc.ErrorMatches, `cannot open 40-90/tcp \(unit "u/0"\): conflicts with 50-80/tcp requested earlier`)
   139  
   140  	// Now try closing some ports as well.
   141  	err = ctx.ClosePorts("udp", 8080, 8088)
   142  	c.Assert(err, jc.ErrorIsNil) // not existing -> ignored
   143  	err = ctx.ClosePorts("tcp", 100, 200)
   144  	c.Assert(err, jc.ErrorIsNil)
   145  	err = ctx.ClosePorts("tcp", 100, 200)
   146  	c.Assert(err, jc.ErrorIsNil) // duplicates are ignored
   147  	err = ctx.ClosePorts("udp", 200, 300)
   148  	c.Assert(err, gc.ErrorMatches, `cannot close 200-300/udp \(opened by "u/1"\) from "u/0"`)
   149  	err = ctx.ClosePorts("tcp", 50, 80)
   150  	c.Assert(err, jc.ErrorIsNil) // still pending -> no longer pending
   151  
   152  	// Ensure the ports are not actually changed on the unit yet.
   153  	unitRanges, err = s.unit.OpenedPorts()
   154  	c.Assert(err, jc.ErrorIsNil)
   155  	c.Assert(unitRanges, jc.DeepEquals, []network.PortRange{
   156  		{100, 200, "tcp"},
   157  	})
   158  
   159  	// Flush the context with a success.
   160  	err = ctx.Flush("some badge", nil)
   161  	c.Assert(err, jc.ErrorIsNil)
   162  
   163  	// Verify the unit ranges are now open.
   164  	expectUnitRanges := []network.PortRange{
   165  		{FromPort: 10, ToPort: 20, Protocol: "udp"},
   166  	}
   167  	unitRanges, err = s.unit.OpenedPorts()
   168  	c.Assert(err, jc.ErrorIsNil)
   169  	c.Assert(unitRanges, jc.DeepEquals, expectUnitRanges)
   170  }
   171  
   172  func (s *FlushContextSuite) TestRunHookAddStorageOnFailure(c *gc.C) {
   173  	ctx := s.context(c)
   174  	c.Assert(ctx.UnitName(), gc.Equals, "u/0")
   175  
   176  	size := uint64(1)
   177  	ctx.AddUnitStorage(
   178  		map[string]params.StorageConstraints{
   179  			"allecto": params.StorageConstraints{Size: &size},
   180  		})
   181  
   182  	// Flush the context with an error.
   183  	msg := "test fail run hook"
   184  	err := ctx.Flush("test fail run hook", errors.New(msg))
   185  	c.Assert(errors.Cause(err), gc.ErrorMatches, msg)
   186  
   187  	all, err := s.State.AllStorageInstances()
   188  	c.Assert(err, jc.ErrorIsNil)
   189  	c.Assert(all, gc.HasLen, 0)
   190  }
   191  
   192  func (s *FlushContextSuite) TestRunHookAddUnitStorageOnSuccess(c *gc.C) {
   193  	ctx := s.context(c)
   194  	c.Assert(ctx.UnitName(), gc.Equals, "u/0")
   195  
   196  	size := uint64(1)
   197  	ctx.AddUnitStorage(
   198  		map[string]params.StorageConstraints{
   199  			"allecto": params.StorageConstraints{Size: &size},
   200  		})
   201  
   202  	// Flush the context with a success.
   203  	err := ctx.Flush("success", nil)
   204  	c.Assert(errors.Cause(err), gc.ErrorMatches, `.*storage "allecto" not found.*`)
   205  
   206  	all, err := s.State.AllStorageInstances()
   207  	c.Assert(err, jc.ErrorIsNil)
   208  	c.Assert(all, gc.HasLen, 0)
   209  }
   210  
   211  func (s *FlushContextSuite) TestFlushClosesMetricsRecorder(c *gc.C) {
   212  	uuid := utils.MustNewUUID()
   213  	ctx := s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, true, s.metricsDefinition("key"), NewRealPaths(c))
   214  
   215  	runner.PatchMetricsRecorder(ctx, &StubMetricsRecorder{&s.stub})
   216  
   217  	err := ctx.AddMetric("key", "value", time.Now())
   218  
   219  	// Flush the context with a success.
   220  	err = ctx.Flush("success", nil)
   221  	c.Assert(err, jc.ErrorIsNil)
   222  
   223  	s.stub.CheckCallNames(c, "IsDeclaredMetric", "AddMetric", "Close")
   224  }
   225  
   226  func (s *HookContextSuite) context(c *gc.C) *runner.HookContext {
   227  	uuid, err := utils.NewUUID()
   228  	c.Assert(err, jc.ErrorIsNil)
   229  	return s.getHookContext(c, uuid.String(), -1, "", noProxies)
   230  }
   231  
   232  func (s *FlushContextSuite) TestBuiltinMetric(c *gc.C) {
   233  	uuid := utils.MustNewUUID()
   234  	paths := NewRealPaths(c)
   235  	ctx := s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, true, s.metricsDefinition("juju-units"), paths)
   236  	reader, err := metrics.NewJSONMetricReader(
   237  		paths.GetMetricsSpoolDir(),
   238  	)
   239  
   240  	err = ctx.Flush("some badge", nil)
   241  	c.Assert(err, jc.ErrorIsNil)
   242  	batches, err := reader.Read()
   243  	c.Assert(err, jc.ErrorIsNil)
   244  	c.Assert(batches, gc.HasLen, 1)
   245  	c.Assert(batches[0].Metrics, gc.HasLen, 1)
   246  	c.Assert(batches[0].Metrics[0].Key, gc.Equals, "juju-units")
   247  	c.Assert(batches[0].Metrics[0].Value, gc.Equals, "1")
   248  }
   249  
   250  func (s *FlushContextSuite) TestBuiltinMetricNotGeneratedIfNotDefined(c *gc.C) {
   251  	uuid := utils.MustNewUUID()
   252  	paths := NewRealPaths(c)
   253  	ctx := s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, true, s.metricsDefinition("pings"), paths)
   254  	reader, err := metrics.NewJSONMetricReader(
   255  		paths.GetMetricsSpoolDir(),
   256  	)
   257  
   258  	err = ctx.Flush("some badge", nil)
   259  	c.Assert(err, jc.ErrorIsNil)
   260  	batches, err := reader.Read()
   261  	c.Assert(err, jc.ErrorIsNil)
   262  	c.Assert(batches, gc.HasLen, 0)
   263  }
   264  
   265  func (s *FlushContextSuite) TestRecorderIsClosedAfterBuiltIn(c *gc.C) {
   266  	uuid := utils.MustNewUUID()
   267  	paths := NewRealPaths(c)
   268  	ctx := s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, true, s.metricsDefinition("juju-units"), paths)
   269  	runner.PatchMetricsRecorder(ctx, &StubMetricsRecorder{&s.stub})
   270  
   271  	err := ctx.Flush("some badge", nil)
   272  	c.Assert(err, jc.ErrorIsNil)
   273  	s.stub.CheckCallNames(c, "IsDeclaredMetric", "AddMetric", "Close")
   274  }