github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/minimumunits_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	jc "github.com/juju/testing/checkers"
     8  	gc "gopkg.in/check.v1"
     9  	"gopkg.in/mgo.v2"
    10  
    11  	"github.com/juju/juju/state"
    12  )
    13  
    14  type MinUnitsSuite struct {
    15  	ConnSuite
    16  	service *state.Application
    17  }
    18  
    19  var _ = gc.Suite(&MinUnitsSuite{})
    20  
    21  func (s *MinUnitsSuite) SetUpTest(c *gc.C) {
    22  	s.ConnSuite.SetUpTest(c)
    23  	s.service = s.AddTestingService(c, "dummy-application", s.AddTestingCharm(c, "dummy"))
    24  }
    25  
    26  func (s *MinUnitsSuite) assertRevno(c *gc.C, expectedRevno int, expectedErr error) {
    27  	revno, err := state.MinUnitsRevno(s.State, s.service.Name())
    28  	c.Assert(err, gc.Equals, expectedErr)
    29  	c.Assert(revno, gc.Equals, expectedRevno)
    30  }
    31  
    32  func (s *MinUnitsSuite) addUnits(c *gc.C, count int) {
    33  	for i := 0; i < count; i++ {
    34  		_, err := s.service.AddUnit()
    35  		c.Assert(err, jc.ErrorIsNil)
    36  	}
    37  }
    38  
    39  func (s *MinUnitsSuite) TestSetMinUnits(c *gc.C) {
    40  	service := s.service
    41  	for i, t := range []struct {
    42  		about   string
    43  		initial int
    44  		changes []int
    45  		revno   int
    46  		err     error
    47  	}{{
    48  		// Revno is set to zero on creation.
    49  		about:   "setting minimum units",
    50  		changes: []int{42},
    51  	}, {
    52  		// Revno is increased by the update operation.
    53  		about:   "updating minimum units",
    54  		initial: 1,
    55  		changes: []int{42},
    56  		revno:   1,
    57  	}, {
    58  		// Revno does not change.
    59  		about:   "updating minimum units with the same value",
    60  		initial: 42,
    61  		changes: []int{42},
    62  	}, {
    63  		// Revno is increased by each update.
    64  		about:   "increasing minimum units multiple times",
    65  		initial: 1,
    66  		changes: []int{2, 3, 4},
    67  		revno:   3,
    68  	}, {
    69  		// Revno does not change.
    70  		about:   "decreasing minimum units multiple times",
    71  		initial: 5,
    72  		changes: []int{3, 2, 1},
    73  	}, {
    74  		// No-op.
    75  		about:   "removing not existent minimum units",
    76  		changes: []int{0},
    77  		err:     mgo.ErrNotFound,
    78  	}, {
    79  		// The document is deleted.
    80  		about:   "removing existing minimum units",
    81  		initial: 42,
    82  		changes: []int{0},
    83  		err:     mgo.ErrNotFound,
    84  	}} {
    85  		c.Logf("test %d. %s", i, t.about)
    86  		// Set up initial minimum units if required.
    87  		if t.initial > 0 {
    88  			err := service.SetMinUnits(t.initial)
    89  			c.Assert(err, jc.ErrorIsNil)
    90  		}
    91  		// Insert/update minimum units.
    92  		for _, input := range t.changes {
    93  			err := service.SetMinUnits(input)
    94  			c.Assert(err, jc.ErrorIsNil)
    95  			c.Assert(service.MinUnits(), gc.Equals, input)
    96  			c.Assert(service.Refresh(), gc.IsNil)
    97  			c.Assert(service.MinUnits(), gc.Equals, input)
    98  		}
    99  		// Check the document existence and revno.
   100  		s.assertRevno(c, t.revno, t.err)
   101  		// Clean up, if required, the minUnits document.
   102  		err := service.SetMinUnits(0)
   103  		c.Assert(err, jc.ErrorIsNil)
   104  	}
   105  }
   106  
   107  func (s *MinUnitsSuite) TestInvalidMinUnits(c *gc.C) {
   108  	err := s.service.SetMinUnits(-1)
   109  	c.Assert(err, gc.ErrorMatches, `cannot set minimum units for application "dummy-application": cannot set a negative minimum number of units`)
   110  }
   111  
   112  func (s *MinUnitsSuite) TestMinUnitsInsertRetry(c *gc.C) {
   113  	defer state.SetRetryHooks(c, s.State, func() {
   114  		err := s.service.SetMinUnits(41)
   115  		c.Assert(err, jc.ErrorIsNil)
   116  		s.assertRevno(c, 0, nil)
   117  	}, func() {
   118  		s.assertRevno(c, 1, nil)
   119  	}).Check()
   120  	err := s.service.SetMinUnits(42)
   121  	c.Assert(err, jc.ErrorIsNil)
   122  	c.Assert(s.service.MinUnits(), gc.Equals, 42)
   123  }
   124  
   125  func (s *MinUnitsSuite) TestMinUnitsUpdateRetry(c *gc.C) {
   126  	err := s.service.SetMinUnits(41)
   127  	c.Assert(err, jc.ErrorIsNil)
   128  	defer state.SetRetryHooks(c, s.State, func() {
   129  		err := s.service.SetMinUnits(0)
   130  		c.Assert(err, jc.ErrorIsNil)
   131  		s.assertRevno(c, 0, mgo.ErrNotFound)
   132  	}, func() {
   133  		s.assertRevno(c, 0, nil)
   134  	}).Check()
   135  	err = s.service.SetMinUnits(42)
   136  	c.Assert(err, jc.ErrorIsNil)
   137  	c.Assert(s.service.MinUnits(), gc.Equals, 42)
   138  }
   139  
   140  func (s *MinUnitsSuite) TestMinUnitsRemoveBefore(c *gc.C) {
   141  	err := s.service.SetMinUnits(41)
   142  	c.Assert(err, jc.ErrorIsNil)
   143  	defer state.SetBeforeHooks(c, s.State, func() {
   144  		err := s.service.SetMinUnits(0)
   145  		c.Assert(err, jc.ErrorIsNil)
   146  		s.assertRevno(c, 0, mgo.ErrNotFound)
   147  	}).Check()
   148  	err = s.service.SetMinUnits(0)
   149  	c.Assert(err, jc.ErrorIsNil)
   150  	c.Assert(s.service.MinUnits(), gc.Equals, 0)
   151  }
   152  
   153  func (s *MinUnitsSuite) testDestroyOrRemoveServiceBefore(c *gc.C, initial, input int, preventRemoval bool) {
   154  	err := s.service.SetMinUnits(initial)
   155  	c.Assert(err, jc.ErrorIsNil)
   156  	expectedErr := `cannot set minimum units for application "dummy-application": application "dummy-application" not found`
   157  	if preventRemoval {
   158  		expectedErr = `cannot set minimum units for application "dummy-application": application is no longer alive`
   159  		s.addUnits(c, 1)
   160  	}
   161  	defer state.SetBeforeHooks(c, s.State, func() {
   162  		err := s.service.Destroy()
   163  		c.Assert(err, jc.ErrorIsNil)
   164  	}).Check()
   165  	err = s.service.SetMinUnits(input)
   166  	c.Assert(err, gc.ErrorMatches, expectedErr)
   167  	s.assertRevno(c, 0, mgo.ErrNotFound)
   168  }
   169  
   170  func (s *MinUnitsSuite) TestMinUnitsInsertDestroyServiceBefore(c *gc.C) {
   171  	s.testDestroyOrRemoveServiceBefore(c, 0, 42, true)
   172  }
   173  
   174  func (s *MinUnitsSuite) TestMinUnitsUpdateDestroyServiceBefore(c *gc.C) {
   175  	s.testDestroyOrRemoveServiceBefore(c, 1, 42, true)
   176  }
   177  
   178  func (s *MinUnitsSuite) TestMinUnitsRemoveDestroyServiceBefore(c *gc.C) {
   179  	s.testDestroyOrRemoveServiceBefore(c, 1, 0, true)
   180  }
   181  
   182  func (s *MinUnitsSuite) TestMinUnitsInsertRemoveServiceBefore(c *gc.C) {
   183  	s.testDestroyOrRemoveServiceBefore(c, 0, 42, false)
   184  }
   185  
   186  func (s *MinUnitsSuite) TestMinUnitsUpdateRemoveServiceBefore(c *gc.C) {
   187  	s.testDestroyOrRemoveServiceBefore(c, 1, 42, false)
   188  }
   189  
   190  func (s *MinUnitsSuite) TestMinUnitsRemoveRemoveServiceBefore(c *gc.C) {
   191  	s.testDestroyOrRemoveServiceBefore(c, 1, 0, false)
   192  }
   193  
   194  func (s *MinUnitsSuite) TestMinUnitsSetDestroyEntities(c *gc.C) {
   195  	err := s.service.SetMinUnits(1)
   196  	c.Assert(err, jc.ErrorIsNil)
   197  	s.assertRevno(c, 0, nil)
   198  
   199  	// Add two units to the service for later use.
   200  	unit1, err := s.service.AddUnit()
   201  	c.Assert(err, jc.ErrorIsNil)
   202  	unit2, err := s.service.AddUnit()
   203  	c.Assert(err, jc.ErrorIsNil)
   204  
   205  	// Destroy a unit and ensure the revno has been increased.
   206  	preventUnitDestroyRemove(c, unit1)
   207  	err = unit1.Destroy()
   208  	c.Assert(err, jc.ErrorIsNil)
   209  	s.assertRevno(c, 1, nil)
   210  
   211  	// Remove a unit and ensure the revno has been increased..
   212  	err = unit2.Destroy()
   213  	c.Assert(err, jc.ErrorIsNil)
   214  	s.assertRevno(c, 2, nil)
   215  
   216  	// Destroy the service and ensure the minUnits document has been removed.
   217  	err = s.service.Destroy()
   218  	c.Assert(err, jc.ErrorIsNil)
   219  	s.assertRevno(c, 0, mgo.ErrNotFound)
   220  }
   221  
   222  func (s *MinUnitsSuite) TestMinUnitsNotSetDestroyEntities(c *gc.C) {
   223  	// Add two units to the service for later use.
   224  	unit1, err := s.service.AddUnit()
   225  	c.Assert(err, jc.ErrorIsNil)
   226  	unit2, err := s.service.AddUnit()
   227  	c.Assert(err, jc.ErrorIsNil)
   228  
   229  	// Destroy a unit and ensure the minUnits document has not been created.
   230  	preventUnitDestroyRemove(c, unit1)
   231  	err = unit1.Destroy()
   232  	c.Assert(err, jc.ErrorIsNil)
   233  	s.assertRevno(c, 0, mgo.ErrNotFound)
   234  
   235  	// Remove a unit and ensure the minUnits document has not been created.
   236  	err = unit2.Destroy()
   237  	c.Assert(err, jc.ErrorIsNil)
   238  	s.assertRevno(c, 0, mgo.ErrNotFound)
   239  
   240  	// Destroy the service and ensure the minUnits document is still missing.
   241  	err = s.service.Destroy()
   242  	c.Assert(err, jc.ErrorIsNil)
   243  	s.assertRevno(c, 0, mgo.ErrNotFound)
   244  }
   245  
   246  func assertAllUnits(c *gc.C, service *state.Application, expected int) {
   247  	units, err := service.AllUnits()
   248  	c.Assert(err, jc.ErrorIsNil)
   249  	c.Assert(units, gc.HasLen, expected)
   250  }
   251  
   252  func (s *MinUnitsSuite) TestEnsureMinUnits(c *gc.C) {
   253  	service := s.service
   254  	for i, t := range []struct {
   255  		about    string // Test description.
   256  		initial  int    // Initial number of units.
   257  		minimum  int    // Minimum number of units for the application.
   258  		destroy  int    // Number of units to be destroyed before calling EnsureMinUnits.
   259  		expected int    // Expected number of units after calling EnsureMinUnits.
   260  	}{{
   261  		about: "no minimum units set",
   262  	}, {
   263  		about:    "initial units > minimum units",
   264  		initial:  2,
   265  		minimum:  1,
   266  		expected: 2,
   267  	}, {
   268  		about:    "initial units == minimum units",
   269  		initial:  2,
   270  		minimum:  2,
   271  		expected: 2,
   272  	}, {
   273  		about:    "initial units < minimum units",
   274  		initial:  1,
   275  		minimum:  2,
   276  		expected: 2,
   277  	}, {
   278  		about:    "alive units < minimum units",
   279  		initial:  2,
   280  		minimum:  2,
   281  		destroy:  1,
   282  		expected: 3,
   283  	}, {
   284  		about:    "add multiple units",
   285  		initial:  6,
   286  		minimum:  5,
   287  		destroy:  4,
   288  		expected: 9,
   289  	}} {
   290  		c.Logf("test %d. %s", i, t.about)
   291  
   292  		// Set up initial units if required.
   293  		s.addUnits(c, t.initial)
   294  
   295  		// Set up minimum units if required.
   296  		err := service.SetMinUnits(t.minimum)
   297  		c.Assert(err, jc.ErrorIsNil)
   298  
   299  		// Destroy units if required.
   300  		allUnits, err := service.AllUnits()
   301  		c.Assert(err, jc.ErrorIsNil)
   302  		for i := 0; i < t.destroy; i++ {
   303  			preventUnitDestroyRemove(c, allUnits[i])
   304  			err = allUnits[i].Destroy()
   305  			c.Assert(err, jc.ErrorIsNil)
   306  		}
   307  
   308  		// Ensure the minimum number of units is correctly restored.
   309  		c.Assert(service.Refresh(), gc.IsNil)
   310  		err = service.EnsureMinUnits()
   311  		c.Assert(err, jc.ErrorIsNil)
   312  		assertAllUnits(c, service, t.expected)
   313  
   314  		// Clean up the minUnits document and the units.
   315  		err = service.SetMinUnits(0)
   316  		c.Assert(err, jc.ErrorIsNil)
   317  		removeAllUnits(c, service)
   318  	}
   319  }
   320  
   321  func (s *MinUnitsSuite) TestEnsureMinUnitsServiceNotAlive(c *gc.C) {
   322  	err := s.service.SetMinUnits(2)
   323  	c.Assert(err, jc.ErrorIsNil)
   324  	s.addUnits(c, 1)
   325  	err = s.service.Destroy()
   326  	c.Assert(err, jc.ErrorIsNil)
   327  	expectedErr := `cannot ensure minimum units for application "dummy-application": application is not alive`
   328  
   329  	// An error is returned if the application is not alive.
   330  	c.Assert(s.service.EnsureMinUnits(), gc.ErrorMatches, expectedErr)
   331  
   332  	// An error is returned if the service was removed.
   333  	err = s.State.Cleanup()
   334  	c.Assert(err, jc.ErrorIsNil)
   335  	c.Assert(s.service.EnsureMinUnits(), gc.ErrorMatches, expectedErr)
   336  }
   337  
   338  func (s *MinUnitsSuite) TestEnsureMinUnitsUpdateMinUnitsRetry(c *gc.C) {
   339  	s.addUnits(c, 1)
   340  	err := s.service.SetMinUnits(4)
   341  	c.Assert(err, jc.ErrorIsNil)
   342  	defer state.SetRetryHooks(c, s.State, func() {
   343  		err := s.service.SetMinUnits(2)
   344  		c.Assert(err, jc.ErrorIsNil)
   345  	}, func() {
   346  		assertAllUnits(c, s.service, 2)
   347  	}).Check()
   348  	err = s.service.EnsureMinUnits()
   349  	c.Assert(err, jc.ErrorIsNil)
   350  
   351  }
   352  
   353  func (s *MinUnitsSuite) TestEnsureMinUnitsAddUnitsRetry(c *gc.C) {
   354  	err := s.service.SetMinUnits(3)
   355  	c.Assert(err, jc.ErrorIsNil)
   356  	defer state.SetRetryHooks(c, s.State, func() {
   357  		s.addUnits(c, 2)
   358  	}, func() {
   359  		assertAllUnits(c, s.service, 3)
   360  	}).Check()
   361  	err = s.service.EnsureMinUnits()
   362  	c.Assert(err, jc.ErrorIsNil)
   363  }
   364  
   365  func (s *MinUnitsSuite) testEnsureMinUnitsBefore(c *gc.C, f func(), minUnits, expectedUnits int) {
   366  	service := s.service
   367  	err := service.SetMinUnits(minUnits)
   368  	c.Assert(err, jc.ErrorIsNil)
   369  	defer state.SetBeforeHooks(c, s.State, f).Check()
   370  	err = service.EnsureMinUnits()
   371  	c.Assert(err, jc.ErrorIsNil)
   372  	assertAllUnits(c, service, expectedUnits)
   373  }
   374  
   375  func (s *MinUnitsSuite) TestEnsureMinUnitsDecreaseMinUnitsBefore(c *gc.C) {
   376  	f := func() {
   377  		err := s.service.SetMinUnits(3)
   378  		c.Assert(err, jc.ErrorIsNil)
   379  	}
   380  	s.testEnsureMinUnitsBefore(c, f, 42, 3)
   381  }
   382  
   383  func (s *MinUnitsSuite) TestEnsureMinUnitsRemoveMinUnitsBefore(c *gc.C) {
   384  	f := func() {
   385  		err := s.service.SetMinUnits(0)
   386  		c.Assert(err, jc.ErrorIsNil)
   387  	}
   388  	s.testEnsureMinUnitsBefore(c, f, 2, 0)
   389  }
   390  
   391  func (s *MinUnitsSuite) TestEnsureMinUnitsAddUnitsBefore(c *gc.C) {
   392  	f := func() {
   393  		s.addUnits(c, 2)
   394  	}
   395  	s.testEnsureMinUnitsBefore(c, f, 2, 2)
   396  }
   397  
   398  func (s *MinUnitsSuite) TestEnsureMinUnitsDestroyServiceBefore(c *gc.C) {
   399  	s.addUnits(c, 1)
   400  	err := s.service.SetMinUnits(42)
   401  	c.Assert(err, jc.ErrorIsNil)
   402  	defer state.SetBeforeHooks(c, s.State, func() {
   403  		err := s.service.Destroy()
   404  		c.Assert(err, jc.ErrorIsNil)
   405  	}).Check()
   406  	c.Assert(s.service.EnsureMinUnits(), gc.ErrorMatches,
   407  		`cannot ensure minimum units for application "dummy-application": application is not alive`)
   408  }
   409  
   410  func (s *MinUnitsSuite) TestEnsureMinUnitsDecreaseMinUnitsAfter(c *gc.C) {
   411  	s.addUnits(c, 2)
   412  	service := s.service
   413  	err := service.SetMinUnits(5)
   414  	c.Assert(err, jc.ErrorIsNil)
   415  	defer state.SetAfterHooks(c, s.State, func() {
   416  		err := service.SetMinUnits(3)
   417  		c.Assert(err, jc.ErrorIsNil)
   418  	}).Check()
   419  	c.Assert(service.Refresh(), gc.IsNil)
   420  	err = service.EnsureMinUnits()
   421  	c.Assert(err, jc.ErrorIsNil)
   422  	assertAllUnits(c, service, 3)
   423  }