github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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/runner"
    18  	"github.com/juju/juju/worker/uniter/runner/jujuc"
    19  )
    20  
    21  type FlushContextSuite struct {
    22  	HookContextSuite
    23  	testing.Stub
    24  }
    25  
    26  var _ = gc.Suite(&FlushContextSuite{})
    27  
    28  // StubMetricsReader is a stub implementation of the metrics reader.
    29  type StubMetricsReader struct {
    30  	*testing.Stub
    31  	Batches []runner.MetricsBatch
    32  }
    33  
    34  // Open implements the MetricsReader interface.
    35  func (mr *StubMetricsReader) Open() ([]runner.MetricsBatch, error) {
    36  	mr.MethodCall(mr, "Open")
    37  	return mr.Batches, mr.NextErr()
    38  }
    39  
    40  // Remove implements the MetricsReader interface.
    41  func (mr *StubMetricsReader) Remove(uuid string) error {
    42  	mr.MethodCall(mr, "Remove", uuid)
    43  	return mr.NextErr()
    44  }
    45  
    46  // Close implements the MetricsReader interface.
    47  func (mr *StubMetricsReader) Close() error {
    48  	mr.MethodCall(mr, "Close")
    49  	return mr.NextErr()
    50  }
    51  
    52  func (s *FlushContextSuite) TestRunHookRelationFlushingError(c *gc.C) {
    53  	uuid, err := utils.NewUUID()
    54  	c.Assert(err, jc.ErrorIsNil)
    55  	ctx := s.getHookContext(c, uuid.String(), -1, "", noProxies)
    56  
    57  	// Mess with multiple relation settings.
    58  	relCtx0, ok := ctx.Relation(0)
    59  	c.Assert(ok, jc.IsTrue)
    60  	node0, err := relCtx0.Settings()
    61  	c.Assert(err, jc.ErrorIsNil)
    62  	node0.Set("foo", "1")
    63  	relCtx1, ok := ctx.Relation(1)
    64  	c.Assert(ok, jc.IsTrue)
    65  	node1, err := relCtx1.Settings()
    66  	c.Assert(err, jc.ErrorIsNil)
    67  	node1.Set("bar", "2")
    68  
    69  	// Flush the context with a failure.
    70  	err = ctx.FlushContext("some badge", errors.New("blam pow"))
    71  	c.Assert(err, gc.ErrorMatches, "blam pow")
    72  
    73  	// Check that the changes have not been written to state.
    74  	settings0, err := s.relunits[0].ReadSettings("u/0")
    75  	c.Assert(err, jc.ErrorIsNil)
    76  	c.Assert(settings0, gc.DeepEquals, map[string]interface{}{"relation-name": "db0"})
    77  	settings1, err := s.relunits[1].ReadSettings("u/0")
    78  	c.Assert(err, jc.ErrorIsNil)
    79  	c.Assert(settings1, gc.DeepEquals, map[string]interface{}{"relation-name": "db1"})
    80  }
    81  
    82  func (s *FlushContextSuite) TestRunHookRelationFlushingSuccess(c *gc.C) {
    83  	// Create a charm with a working hook, and mess with settings again.
    84  	uuid, err := utils.NewUUID()
    85  	c.Assert(err, jc.ErrorIsNil)
    86  	ctx := s.getHookContext(c, uuid.String(), -1, "", noProxies)
    87  
    88  	// Mess with multiple relation settings.
    89  	relCtx0, ok := ctx.Relation(0)
    90  	c.Assert(ok, jc.IsTrue)
    91  	node0, err := relCtx0.Settings()
    92  	c.Assert(err, jc.ErrorIsNil)
    93  	node0.Set("baz", "3")
    94  	relCtx1, ok := ctx.Relation(1)
    95  	c.Assert(ok, jc.IsTrue)
    96  	node1, err := relCtx1.Settings()
    97  	c.Assert(err, jc.ErrorIsNil)
    98  	node1.Set("qux", "4")
    99  
   100  	// Flush the context with a success.
   101  	err = ctx.FlushContext("some badge", nil)
   102  	c.Assert(err, jc.ErrorIsNil)
   103  
   104  	// Check that the changes have been written to state.
   105  	settings0, err := s.relunits[0].ReadSettings("u/0")
   106  	c.Assert(err, jc.ErrorIsNil)
   107  	c.Assert(settings0, gc.DeepEquals, map[string]interface{}{
   108  		"relation-name": "db0",
   109  		"baz":           "3",
   110  	})
   111  	settings1, err := s.relunits[1].ReadSettings("u/0")
   112  	c.Assert(err, jc.ErrorIsNil)
   113  	c.Assert(settings1, gc.DeepEquals, map[string]interface{}{
   114  		"relation-name": "db1",
   115  		"qux":           "4",
   116  	})
   117  }
   118  
   119  func (s *FlushContextSuite) TestRunHookMetricSendingSuccess(c *gc.C) {
   120  	uuid, err := utils.NewUUID()
   121  	c.Assert(err, jc.ErrorIsNil)
   122  	ctx := s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, true, s.metricsDefinition("pings"))
   123  
   124  	now := time.Now()
   125  	err = ctx.AddMetric("pings", "50", now)
   126  	c.Assert(err, jc.ErrorIsNil)
   127  
   128  	// Flush the context with a success.
   129  	err = ctx.FlushContext("some badge", nil)
   130  	c.Assert(err, jc.ErrorIsNil)
   131  
   132  	metricBatches, err := s.State.MetricBatches()
   133  	c.Assert(err, jc.ErrorIsNil)
   134  	c.Assert(metricBatches, gc.HasLen, 1)
   135  	metrics := metricBatches[0].Metrics()
   136  	c.Assert(metrics, gc.HasLen, 1)
   137  	c.Assert(metrics[0].Key, gc.Equals, "pings")
   138  	c.Assert(metrics[0].Value, gc.Equals, "50")
   139  }
   140  
   141  func (s *FlushContextSuite) TestRunHookMetricSendingGetDuplicate(c *gc.C) {
   142  	uuid := utils.MustNewUUID()
   143  	ctx := s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, true, s.metricsDefinition("pings"))
   144  
   145  	// Send batches once.
   146  	batches := []runner.MetricsBatch{
   147  		{
   148  			CharmURL: s.meteredCharm.URL().String(),
   149  			UUID:     utils.MustNewUUID().String(),
   150  			Created:  time.Now(),
   151  			Metrics:  []jujuc.Metric{{Key: "pings", Value: "1", Time: time.Now()}},
   152  		}, {
   153  			CharmURL: s.meteredCharm.URL().String(),
   154  			UUID:     utils.MustNewUUID().String(),
   155  			Created:  time.Now(),
   156  			Metrics:  []jujuc.Metric{{Key: "pings", Value: "1", Time: time.Now()}},
   157  		},
   158  	}
   159  
   160  	reader := &StubMetricsReader{
   161  		Stub:    &s.Stub,
   162  		Batches: batches,
   163  	}
   164  
   165  	runner.PatchMetricsReader(ctx, reader)
   166  
   167  	// Flush the context with a success.
   168  	err := ctx.FlushContext("some badge", nil)
   169  	c.Assert(err, jc.ErrorIsNil)
   170  
   171  	// Check stub calls.
   172  	s.Stub.CheckCallNames(c, "Open", "Remove", "Remove", "Close")
   173  	s.Stub.ResetCalls()
   174  	metricBatches, err := s.State.MetricBatches()
   175  	c.Assert(err, jc.ErrorIsNil)
   176  	c.Assert(metricBatches, gc.HasLen, 2)
   177  
   178  	// Create a new context with a duplicate metrics batch.
   179  	uuid = utils.MustNewUUID()
   180  	ctx = s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, true, s.metricsDefinition("pings"))
   181  	runner.PatchMetricsReader(ctx, reader)
   182  
   183  	newBatches := []runner.MetricsBatch{
   184  		batches[0],
   185  		{
   186  			CharmURL: s.meteredCharm.URL().String(),
   187  			UUID:     utils.MustNewUUID().String(),
   188  			Created:  time.Now(),
   189  			Metrics:  []jujuc.Metric{{Key: "pings", Value: "1", Time: time.Now()}},
   190  		},
   191  	}
   192  	reader.Batches = newBatches
   193  
   194  	// Flush the context with a success.
   195  	err = ctx.FlushContext("some badge", nil)
   196  	c.Assert(err, jc.ErrorIsNil)
   197  
   198  	// Check stub calls.
   199  	s.Stub.CheckCallNames(c, "Open", "Remove", "Remove", "Close")
   200  
   201  	metricBatches, err = s.State.MetricBatches()
   202  	c.Assert(err, jc.ErrorIsNil)
   203  	// Only one additional metric has been recorded.
   204  	c.Assert(metricBatches, gc.HasLen, 3)
   205  
   206  }
   207  
   208  func (s *FlushContextSuite) TestRunHookMetricSendingFailedByServer(c *gc.C) {
   209  	uuid, err := utils.NewUUID()
   210  	c.Assert(err, jc.ErrorIsNil)
   211  	ctx := s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, true, s.metricsDefinition("pings"))
   212  
   213  	// Send batches once.
   214  	batches := []runner.MetricsBatch{
   215  		{
   216  			CharmURL: s.meteredCharm.URL().String(),
   217  			UUID:     utils.MustNewUUID().String(),
   218  			Created:  time.Now(),
   219  			Metrics:  []jujuc.Metric{{Key: "pings", Value: "1", Time: time.Now()}},
   220  		}, {
   221  			CharmURL: s.meteredCharm.URL().String(),
   222  			UUID:     utils.MustNewUUID().String(),
   223  			Created:  time.Now(),
   224  			Metrics:  []jujuc.Metric{{Key: "pings", Value: "1", Time: time.Now()}},
   225  		},
   226  	}
   227  
   228  	reader := &StubMetricsReader{
   229  		Stub:    &s.Stub,
   230  		Batches: batches,
   231  	}
   232  
   233  	restoreRunner := runner.PatchMetricsReader(ctx, reader)
   234  	defer restoreRunner()
   235  
   236  	restoreSender := runner.PatchMetricsSender(ctx, func(batches []params.MetricBatch) (map[string]error, error) {
   237  		responses := make(map[string]error, len(batches))
   238  		for i := range responses {
   239  			responses[i] = errors.New("failed to store")
   240  		}
   241  		return responses, nil
   242  	})
   243  	defer restoreSender()
   244  
   245  	// Flush the context.
   246  	err = ctx.FlushContext("some badge", nil)
   247  	c.Assert(err, jc.ErrorIsNil)
   248  
   249  	// Check stub calls, metrics should not be removed.
   250  	s.Stub.CheckCallNames(c, "Open", "Close")
   251  	s.Stub.ResetCalls()
   252  }
   253  
   254  func (s *FlushContextSuite) TestRunHookNoMetricSendingOnFailure(c *gc.C) {
   255  	uuid, err := utils.NewUUID()
   256  	c.Assert(err, jc.ErrorIsNil)
   257  	ctx := s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, true, s.metricsDefinition("key"))
   258  
   259  	now := time.Now()
   260  	ctx.AddMetric("key", "50", now)
   261  
   262  	// Flush the context with an error.
   263  	err = ctx.FlushContext("some badge", errors.New("boom squelch"))
   264  	c.Assert(err, gc.ErrorMatches, "boom squelch")
   265  
   266  	metricBatches, err := s.State.MetricBatches()
   267  	c.Assert(err, jc.ErrorIsNil)
   268  	c.Assert(metricBatches, gc.HasLen, 0)
   269  }
   270  
   271  func (s *FlushContextSuite) TestRunHookMetricSendingDisabled(c *gc.C) {
   272  	uuid, err := utils.NewUUID()
   273  	c.Assert(err, jc.ErrorIsNil)
   274  	ctx := s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, false, s.metricsDefinition("key"))
   275  
   276  	now := time.Now()
   277  	err = ctx.AddMetric("key", "50", now)
   278  	c.Assert(err, gc.ErrorMatches, "metrics disabled")
   279  
   280  	// Flush the context with a success.
   281  	err = ctx.FlushContext("some badge", nil)
   282  	c.Assert(err, jc.ErrorIsNil)
   283  
   284  	metricBatches, err := s.State.MetricBatches()
   285  	c.Assert(err, jc.ErrorIsNil)
   286  	c.Assert(metricBatches, gc.HasLen, 0)
   287  }
   288  
   289  func (s *FlushContextSuite) TestRunHookMetricSendingUndeclared(c *gc.C) {
   290  	uuid, err := utils.NewUUID()
   291  	c.Assert(err, jc.ErrorIsNil)
   292  	ctx := s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, true, nil)
   293  
   294  	now := time.Now()
   295  	err = ctx.AddMetric("key", "50", now)
   296  	c.Assert(err, gc.ErrorMatches, "metrics disabled")
   297  
   298  	// Flush the context with a success.
   299  	err = ctx.FlushContext("some badge", nil)
   300  	c.Assert(err, jc.ErrorIsNil)
   301  
   302  	metricBatches, err := s.State.MetricBatches()
   303  	c.Assert(err, jc.ErrorIsNil)
   304  	c.Assert(metricBatches, gc.HasLen, 0)
   305  }
   306  
   307  func (s *FlushContextSuite) TestRunHookOpensAndClosesPendingPorts(c *gc.C) {
   308  	// Initially, no port ranges are open on the unit or its machine.
   309  	unitRanges, err := s.unit.OpenedPorts()
   310  	c.Assert(err, jc.ErrorIsNil)
   311  	c.Assert(unitRanges, gc.HasLen, 0)
   312  	machinePorts, err := s.machine.AllPorts()
   313  	c.Assert(err, jc.ErrorIsNil)
   314  	c.Assert(machinePorts, gc.HasLen, 0)
   315  
   316  	// Add another unit on the same machine.
   317  	otherUnit, err := s.service.AddUnit()
   318  	c.Assert(err, jc.ErrorIsNil)
   319  	err = otherUnit.AssignToMachine(s.machine)
   320  	c.Assert(err, jc.ErrorIsNil)
   321  
   322  	// Open some ports on both units.
   323  	err = s.unit.OpenPorts("tcp", 100, 200)
   324  	c.Assert(err, jc.ErrorIsNil)
   325  	err = otherUnit.OpenPorts("udp", 200, 300)
   326  	c.Assert(err, jc.ErrorIsNil)
   327  
   328  	unitRanges, err = s.unit.OpenedPorts()
   329  	c.Assert(err, jc.ErrorIsNil)
   330  	c.Assert(unitRanges, jc.DeepEquals, []network.PortRange{
   331  		{100, 200, "tcp"},
   332  	})
   333  
   334  	// Get the context.
   335  	uuid, err := utils.NewUUID()
   336  	c.Assert(err, jc.ErrorIsNil)
   337  	ctx := s.getHookContext(c, uuid.String(), -1, "", noProxies)
   338  
   339  	// Try opening some ports via the context.
   340  	err = ctx.OpenPorts("tcp", 100, 200)
   341  	c.Assert(err, jc.ErrorIsNil) // duplicates are ignored
   342  	err = ctx.OpenPorts("udp", 200, 300)
   343  	c.Assert(err, gc.ErrorMatches, `cannot open 200-300/udp \(unit "u/0"\): conflicts with existing 200-300/udp \(unit "u/1"\)`)
   344  	err = ctx.OpenPorts("udp", 100, 200)
   345  	c.Assert(err, gc.ErrorMatches, `cannot open 100-200/udp \(unit "u/0"\): conflicts with existing 200-300/udp \(unit "u/1"\)`)
   346  	err = ctx.OpenPorts("udp", 10, 20)
   347  	c.Assert(err, jc.ErrorIsNil)
   348  	err = ctx.OpenPorts("tcp", 50, 100)
   349  	c.Assert(err, gc.ErrorMatches, `cannot open 50-100/tcp \(unit "u/0"\): conflicts with existing 100-200/tcp \(unit "u/0"\)`)
   350  	err = ctx.OpenPorts("tcp", 50, 80)
   351  	c.Assert(err, jc.ErrorIsNil)
   352  	err = ctx.OpenPorts("tcp", 40, 90)
   353  	c.Assert(err, gc.ErrorMatches, `cannot open 40-90/tcp \(unit "u/0"\): conflicts with 50-80/tcp requested earlier`)
   354  
   355  	// Now try closing some ports as well.
   356  	err = ctx.ClosePorts("udp", 8080, 8088)
   357  	c.Assert(err, jc.ErrorIsNil) // not existing -> ignored
   358  	err = ctx.ClosePorts("tcp", 100, 200)
   359  	c.Assert(err, jc.ErrorIsNil)
   360  	err = ctx.ClosePorts("tcp", 100, 200)
   361  	c.Assert(err, jc.ErrorIsNil) // duplicates are ignored
   362  	err = ctx.ClosePorts("udp", 200, 300)
   363  	c.Assert(err, gc.ErrorMatches, `cannot close 200-300/udp \(opened by "u/1"\) from "u/0"`)
   364  	err = ctx.ClosePorts("tcp", 50, 80)
   365  	c.Assert(err, jc.ErrorIsNil) // still pending -> no longer pending
   366  
   367  	// Ensure the ports are not actually changed on the unit yet.
   368  	unitRanges, err = s.unit.OpenedPorts()
   369  	c.Assert(err, jc.ErrorIsNil)
   370  	c.Assert(unitRanges, jc.DeepEquals, []network.PortRange{
   371  		{100, 200, "tcp"},
   372  	})
   373  
   374  	// Flush the context with a success.
   375  	err = ctx.FlushContext("some badge", nil)
   376  	c.Assert(err, jc.ErrorIsNil)
   377  
   378  	// Verify the unit ranges are now open.
   379  	expectUnitRanges := []network.PortRange{
   380  		{FromPort: 10, ToPort: 20, Protocol: "udp"},
   381  	}
   382  	unitRanges, err = s.unit.OpenedPorts()
   383  	c.Assert(err, jc.ErrorIsNil)
   384  	c.Assert(unitRanges, jc.DeepEquals, expectUnitRanges)
   385  }
   386  
   387  func (s *FlushContextSuite) TestRunHookAddStorageOnFailure(c *gc.C) {
   388  	// Get the context.
   389  	uuid, err := utils.NewUUID()
   390  	c.Assert(err, jc.ErrorIsNil)
   391  	ctx := s.getHookContext(c, uuid.String(), -1, "", noProxies)
   392  	c.Assert(ctx.UnitName(), gc.Equals, "u/0")
   393  
   394  	size := uint64(1)
   395  	ctx.AddUnitStorage(
   396  		map[string]params.StorageConstraints{
   397  			"allecto": params.StorageConstraints{Size: &size},
   398  		})
   399  
   400  	// Flush the context with an error.
   401  	msg := "test fail run hook"
   402  	err = ctx.FlushContext("test fail run hook", errors.New(msg))
   403  	c.Assert(errors.Cause(err), gc.ErrorMatches, msg)
   404  
   405  	all, err := s.State.AllStorageInstances()
   406  	c.Assert(err, jc.ErrorIsNil)
   407  	c.Assert(all, gc.HasLen, 0)
   408  }
   409  
   410  func (s *FlushContextSuite) TestRunHookAddUnitStorageOnSuccess(c *gc.C) {
   411  	// Get the context.
   412  	uuid, err := utils.NewUUID()
   413  	c.Assert(err, jc.ErrorIsNil)
   414  	ctx := s.getHookContext(c, uuid.String(), -1, "", noProxies)
   415  	c.Assert(ctx.UnitName(), gc.Equals, "u/0")
   416  
   417  	size := uint64(1)
   418  	ctx.AddUnitStorage(
   419  		map[string]params.StorageConstraints{
   420  			"allecto": params.StorageConstraints{Size: &size},
   421  		})
   422  
   423  	// Flush the context with a success.
   424  	err = ctx.FlushContext("success", nil)
   425  	c.Assert(errors.Cause(err), gc.ErrorMatches, `.*storage "allecto" not found.*`)
   426  
   427  	all, err := s.State.AllStorageInstances()
   428  	c.Assert(err, jc.ErrorIsNil)
   429  	c.Assert(all, gc.HasLen, 0)
   430  }