github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/modelgeneration_test.go (about)

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/charm/v12"
    10  	"github.com/juju/clock/testclock"
    11  	"github.com/juju/errors"
    12  	jc "github.com/juju/testing/checkers"
    13  	gc "gopkg.in/check.v1"
    14  
    15  	"github.com/juju/juju/core/model"
    16  	"github.com/juju/juju/core/settings"
    17  	"github.com/juju/juju/state"
    18  	"github.com/juju/juju/testing"
    19  )
    20  
    21  const (
    22  	newBranchName    = "new-branch"
    23  	newBranchCreator = "new-branch-user"
    24  	branchCommitter  = "commit-user"
    25  )
    26  
    27  type generationSuite struct {
    28  	ConnSuite
    29  
    30  	ch *state.Charm
    31  }
    32  
    33  var _ = gc.Suite(&generationSuite{})
    34  
    35  func (s *generationSuite) TestBranchNameNotFound(c *gc.C) {
    36  	_, err := s.Model.Branch("non-extant-branch")
    37  	c.Assert(errors.IsNotFound(err), jc.IsTrue)
    38  }
    39  
    40  func (s *generationSuite) TestAddBranchSuccess(c *gc.C) {
    41  	s.setupTestingClock(c)
    42  	gen := s.addBranch(c)
    43  
    44  	c.Assert(gen, gc.NotNil)
    45  	c.Check(gen.ModelUUID(), gc.Equals, s.Model.UUID())
    46  	c.Check(gen.GenerationId(), gc.Equals, 0)
    47  	c.Check(gen.Created(), gc.Not(gc.Equals), 0)
    48  	c.Check(gen.CreatedBy(), gc.Equals, newBranchCreator)
    49  	c.Check(gen.BranchName(), gc.Equals, newBranchName)
    50  	c.Check(gen.IsCompleted(), jc.IsFalse)
    51  	c.Check(gen.CompletedBy(), gc.Equals, "")
    52  }
    53  
    54  func (s *generationSuite) TestAssignApplicationCompletedError(c *gc.C) {
    55  	s.setupTestingClock(c)
    56  	gen := s.addBranch(c)
    57  
    58  	// Absence of changes will result in an aborted generation.
    59  	_, err := gen.Commit(branchCommitter)
    60  	c.Assert(err, jc.ErrorIsNil)
    61  
    62  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
    63  	c.Assert(gen.AssignApplication("redis"), gc.ErrorMatches, "branch was already aborted")
    64  }
    65  
    66  func (s *generationSuite) TestAssignApplicationSuccess(c *gc.C) {
    67  	gen := s.addBranch(c)
    68  
    69  	c.Assert(gen.AssignApplication("redis"), jc.ErrorIsNil)
    70  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
    71  	c.Check(gen.AssignedUnits(), gc.DeepEquals, map[string][]string{"redis": {}})
    72  
    73  	// Idempotent.
    74  	c.Assert(gen.AssignApplication("redis"), jc.ErrorIsNil)
    75  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
    76  	c.Check(gen.AssignedUnits(), gc.DeepEquals, map[string][]string{"redis": {}})
    77  }
    78  
    79  func (s *generationSuite) TestAssignUnitBranchAbortedError(c *gc.C) {
    80  	s.setupTestingClock(c)
    81  	gen := s.addBranch(c)
    82  
    83  	// Absence of changes will result in an aborted generation.
    84  	_, err := gen.Commit(branchCommitter)
    85  
    86  	c.Assert(err, jc.ErrorIsNil)
    87  
    88  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
    89  	c.Assert(gen.AssignUnit("redis/0"), gc.ErrorMatches, "branch was already aborted")
    90  }
    91  
    92  func (s *generationSuite) TestAssignUnitNotExistsError(c *gc.C) {
    93  	s.setupTestingClock(c)
    94  	gen := s.addBranch(c)
    95  	c.Assert(gen.AssignUnit("redis/0"), gc.ErrorMatches, `unit "redis/0" not found`)
    96  }
    97  
    98  func (s *generationSuite) TestAssignUnitBranchCommittedError(c *gc.C) {
    99  	s.setupTestingClock(c)
   100  	gen := s.setupAssignAllUnits(c)
   101  
   102  	// Make a change so that commit is a real commit with a generation ID.
   103  	c.Assert(gen.AssignApplication("riak"), jc.ErrorIsNil)
   104  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   105  	_, err := gen.Commit(branchCommitter)
   106  
   107  	c.Assert(err, jc.ErrorIsNil)
   108  
   109  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   110  	c.Assert(gen.AssignUnit("redis/0"), gc.ErrorMatches, "branch was already committed")
   111  }
   112  
   113  func (s *generationSuite) TestAssignUnitSuccess(c *gc.C) {
   114  	s.setupTestingClock(c)
   115  	gen := s.setupAssignAllUnits(c)
   116  
   117  	c.Assert(gen.AssignUnit("riak/0"), jc.ErrorIsNil)
   118  
   119  	expected := map[string][]string{"riak": {"riak/0"}}
   120  
   121  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   122  	c.Check(gen.AssignedUnits(), gc.DeepEquals, expected)
   123  
   124  	// Idempotent.
   125  	c.Assert(gen.AssignUnit("riak/0"), jc.ErrorIsNil)
   126  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   127  	c.Check(gen.AssignedUnits(), gc.DeepEquals, expected)
   128  }
   129  
   130  func (s *generationSuite) TestAssignAllUnitsSuccessAll(c *gc.C) {
   131  	gen := s.setupAssignAllUnits(c)
   132  
   133  	c.Assert(gen.AssignAllUnits("riak"), jc.ErrorIsNil)
   134  
   135  	expected := []string{"riak/0", "riak/1", "riak/2", "riak/3"}
   136  
   137  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   138  	c.Check(gen.AssignedUnits(), gc.HasLen, 1)
   139  	c.Check(gen.AssignedUnits()["riak"], jc.SameContents, expected)
   140  
   141  	// Idempotent.
   142  	c.Assert(gen.AssignAllUnits("riak"), jc.ErrorIsNil)
   143  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   144  	c.Check(gen.AssignedUnits(), gc.HasLen, 1)
   145  	c.Check(gen.AssignedUnits()["riak"], jc.SameContents, expected)
   146  }
   147  
   148  func (s *generationSuite) TestAssignAllUnitsSuccessRemaining(c *gc.C) {
   149  	gen := s.setupAssignAllUnits(c)
   150  
   151  	c.Assert(gen.AssignUnit("riak/2"), jc.ErrorIsNil)
   152  	c.Assert(gen.AssignAllUnits("riak"), jc.ErrorIsNil)
   153  
   154  	expected := []string{"riak/2", "riak/3", "riak/1", "riak/0"}
   155  
   156  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   157  	c.Check(gen.AssignedUnits(), gc.HasLen, 1)
   158  	c.Check(gen.AssignedUnits()["riak"], jc.SameContents, expected)
   159  
   160  	// Idempotent.
   161  	c.Assert(gen.AssignAllUnits("riak"), jc.ErrorIsNil)
   162  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   163  	c.Check(gen.AssignedUnits(), gc.HasLen, 1)
   164  	c.Check(gen.AssignedUnits()["riak"], jc.SameContents, expected)
   165  }
   166  
   167  func (s *generationSuite) TestAssignNumUnitsSuccessRemaining(c *gc.C) {
   168  	gen := s.setupAssignAllUnits(c)
   169  
   170  	expected := []string{"riak/0", "riak/1", "riak/2", "riak/3"}
   171  
   172  	c.Assert(gen.AssignUnits("riak", 1), jc.ErrorIsNil)
   173  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   174  	c.Check(gen.AssignedUnits(), gc.HasLen, 1)
   175  	c.Check(gen.AssignedUnits()["riak"], jc.SameContents, expected[:1])
   176  
   177  	c.Assert(gen.AssignUnits("riak", 2), jc.ErrorIsNil)
   178  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   179  	c.Check(gen.AssignedUnits(), gc.HasLen, 1)
   180  	c.Check(gen.AssignedUnits()["riak"], jc.SameContents, expected[:3])
   181  
   182  	c.Assert(gen.AssignUnits("riak", 1), jc.ErrorIsNil)
   183  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   184  	c.Check(gen.AssignedUnits(), gc.HasLen, 1)
   185  	c.Check(gen.AssignedUnits()["riak"], jc.SameContents, expected)
   186  
   187  	// Idempotent.
   188  	c.Assert(gen.AssignAllUnits("riak"), jc.ErrorIsNil)
   189  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   190  	c.Check(gen.AssignedUnits(), gc.HasLen, 1)
   191  	c.Check(gen.AssignedUnits()["riak"], jc.SameContents, expected)
   192  }
   193  
   194  func (s *generationSuite) TestAssignUnitsNoOperations(c *gc.C) {
   195  	gen := s.setupAssignUnits(c)
   196  
   197  	c.Assert(gen.AssignUnits("riak", 1), jc.ErrorIsNil)
   198  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   199  	c.Check(gen.AssignedUnits(), gc.HasLen, 0)
   200  }
   201  
   202  func (s *generationSuite) TestAssignNumUnitsSelectAll(c *gc.C) {
   203  	gen := s.setupAssignAllUnits(c)
   204  
   205  	expected := []string{"riak/0", "riak/1", "riak/2", "riak/3"}
   206  
   207  	c.Assert(gen.AssignUnits("riak", 100), jc.ErrorIsNil)
   208  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   209  	c.Check(gen.AssignedUnits(), gc.HasLen, 1)
   210  	c.Check(gen.AssignedUnits()["riak"], jc.SameContents, expected)
   211  
   212  	// Idempotent.
   213  	c.Assert(gen.AssignAllUnits("riak"), jc.ErrorIsNil)
   214  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   215  	c.Check(gen.AssignedUnits(), gc.HasLen, 1)
   216  	c.Check(gen.AssignedUnits()["riak"], jc.SameContents, expected)
   217  }
   218  
   219  func (s *generationSuite) TestAssignAllUnitsCompletedError(c *gc.C) {
   220  	s.setupTestingClock(c)
   221  	gen := s.setupAssignAllUnits(c)
   222  
   223  	// Absence of changes will result in an aborted generation.
   224  	_, err := gen.Commit(branchCommitter)
   225  	c.Assert(err, jc.ErrorIsNil)
   226  
   227  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   228  	c.Assert(gen.AssignAllUnits("riak"), gc.ErrorMatches, "branch was already aborted")
   229  }
   230  
   231  func (s *generationSuite) TestCommitAssignsRemainingUnits(c *gc.C) {
   232  	s.setupTestingClock(c)
   233  	gen := s.setupAssignAllUnits(c)
   234  
   235  	c.Assert(gen.AssignUnit("riak/0"), jc.ErrorIsNil)
   236  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   237  
   238  	genId, err := gen.Commit(branchCommitter)
   239  	c.Assert(err, jc.ErrorIsNil)
   240  	c.Check(genId, gc.Not(gc.Equals), 0)
   241  
   242  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   243  	c.Check(gen.IsCompleted(), jc.IsTrue)
   244  	c.Check(gen.CompletedBy(), gc.Equals, branchCommitter)
   245  	c.Check(gen.AssignedUnits(), gc.HasLen, 1)
   246  	c.Check(gen.AssignedUnits()["riak"], jc.SameContents, []string{"riak/0", "riak/1", "riak/2", "riak/3"})
   247  
   248  	// Idempotent.
   249  	_, err = gen.Commit(branchCommitter)
   250  	c.Assert(err, jc.ErrorIsNil)
   251  }
   252  
   253  func (s *generationSuite) TestCommitNoChangesEffectivelyAborted(c *gc.C) {
   254  	s.setupTestingClock(c)
   255  	gen := s.addBranch(c)
   256  
   257  	genId, err := gen.Commit(branchCommitter)
   258  	c.Assert(err, jc.ErrorIsNil)
   259  	c.Check(genId, gc.Equals, 0)
   260  
   261  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   262  	c.Check(gen.IsCompleted(), jc.IsTrue)
   263  	c.Check(gen.CompletedBy(), gc.Equals, branchCommitter)
   264  }
   265  
   266  func (s *generationSuite) TestCommitAppliesConfigDeltas(c *gc.C) {
   267  	s.setupTestingClock(c)
   268  	gen := s.setupAssignAllUnits(c)
   269  
   270  	app, err := s.State.Application("riak")
   271  	c.Assert(err, jc.ErrorIsNil)
   272  
   273  	newCfg := map[string]interface{}{"http_port": int64(9999)}
   274  	c.Assert(app.UpdateCharmConfig(newBranchName, newCfg), jc.ErrorIsNil)
   275  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   276  
   277  	_, err = gen.Commit(branchCommitter)
   278  	c.Assert(err, jc.ErrorIsNil)
   279  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   280  
   281  	cfg, err := app.CharmConfig(model.GenerationMaster)
   282  	c.Assert(err, jc.ErrorIsNil)
   283  
   284  	c.Check(cfg, gc.DeepEquals, charm.Settings(newCfg))
   285  }
   286  
   287  func (s *generationSuite) TestAbortSuccess(c *gc.C) {
   288  	s.setupTestingClock(c)
   289  
   290  	gen := s.addBranch(c)
   291  
   292  	err := gen.Abort(branchCommitter)
   293  	c.Assert(err, jc.ErrorIsNil)
   294  
   295  	// Idempotent.
   296  	err = gen.Refresh()
   297  	c.Assert(err, jc.ErrorIsNil)
   298  	err = gen.Abort(branchCommitter)
   299  	c.Assert(err, jc.ErrorIsNil)
   300  }
   301  
   302  func (s *generationSuite) TestAbortSuccessApplicationNoAssignedUnits(c *gc.C) {
   303  	s.setupTestingClock(c)
   304  
   305  	gen := s.addBranch(c)
   306  	err := gen.AssignApplication("riak")
   307  	c.Assert(err, jc.ErrorIsNil)
   308  	err = gen.Refresh()
   309  	c.Assert(err, jc.ErrorIsNil)
   310  
   311  	err = gen.Abort(branchCommitter)
   312  	c.Assert(err, jc.ErrorIsNil)
   313  }
   314  
   315  func (s *generationSuite) TestAbortFailsAssignedUnits(c *gc.C) {
   316  	s.setupTestingClock(c)
   317  
   318  	gen := s.setupAssignAllUnits(c)
   319  	err := gen.AssignUnit("riak/0")
   320  	c.Assert(err, jc.ErrorIsNil)
   321  	err = gen.Refresh()
   322  	c.Assert(err, jc.ErrorIsNil)
   323  
   324  	err = gen.Abort(branchCommitter)
   325  	c.Assert(err, gc.ErrorMatches, "branch is in progress. Either reset values on tracking units and commit the branch or remove them to abort.")
   326  }
   327  
   328  func (s *generationSuite) TestAbortCommittedBranch(c *gc.C) {
   329  	s.setupTestingClock(c)
   330  
   331  	gen := s.setupAssignAllUnits(c)
   332  	err := gen.AssignUnit("riak/0")
   333  	c.Assert(err, jc.ErrorIsNil)
   334  	err = gen.Refresh()
   335  	c.Assert(err, jc.ErrorIsNil)
   336  
   337  	_, err = gen.Commit(branchCommitter)
   338  	c.Assert(err, jc.ErrorIsNil)
   339  	err = gen.Refresh()
   340  	c.Assert(err, jc.ErrorIsNil)
   341  
   342  	err = gen.Abort(branchCommitter)
   343  	c.Assert(err, gc.ErrorMatches, "branch was already committed")
   344  }
   345  
   346  func (s *generationSuite) TestBranchCharmConfigDeltas(c *gc.C) {
   347  	gen := s.setupAssignAllUnits(c)
   348  	c.Assert(gen.Config(), gc.HasLen, 0)
   349  
   350  	current := state.GetPopulatedSettings(map[string]interface{}{
   351  		"http_port":    8098,
   352  		"handoff_port": 8099,
   353  		"riak_version": "1.1.4-1",
   354  	})
   355  
   356  	// Process a modification, deletion, and addition.
   357  	changes := charm.Settings{
   358  		"http_port":    8100,
   359  		"handoff_port": nil,
   360  		"node_name":    "nodey-node",
   361  	}
   362  	c.Assert(gen.UpdateCharmConfig("riak", current, changes), jc.ErrorIsNil)
   363  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   364  	c.Check(gen.Config(), gc.DeepEquals, map[string]settings.ItemChanges{"riak": {
   365  		settings.MakeDeletion("handoff_port", 8099),
   366  		settings.MakeModification("http_port", 8098, 8100),
   367  		settings.MakeAddition("node_name", "nodey-node"),
   368  	}})
   369  
   370  	// Now simulate node_name being set on master in the meantime,
   371  	// along with changes to http_port and handoff_port.
   372  	current = state.GetPopulatedSettings(map[string]interface{}{
   373  		"http_port":    100,
   374  		"handoff_port": 100,
   375  		"riak_version": "1.1.4-1",
   376  		"node_name":    "come-lately",
   377  	})
   378  
   379  	// Re-set previously deleted handoff_port, update node_name.
   380  	changes = charm.Settings{
   381  		"handoff_port": 9000,
   382  		"node_name":    "latest-nodey-node",
   383  	}
   384  	c.Assert(gen.UpdateCharmConfig("riak", current, changes), jc.ErrorIsNil)
   385  	c.Assert(gen.Refresh(), jc.ErrorIsNil)
   386  
   387  	// handoff_port old value is the original.
   388  	// http_port unchanged.
   389  	// node_name remains as an addition.
   390  	c.Check(gen.Config(), gc.DeepEquals, map[string]settings.ItemChanges{"riak": {
   391  		settings.MakeModification("handoff_port", 8099, 9000),
   392  		settings.MakeModification("http_port", 8098, 8100),
   393  		settings.MakeAddition("node_name", "latest-nodey-node"),
   394  	}})
   395  }
   396  
   397  func (s *generationSuite) TestBranches(c *gc.C) {
   398  	s.setupTestingClock(c)
   399  
   400  	branches, err := s.State.Branches()
   401  	c.Assert(err, jc.ErrorIsNil)
   402  	c.Check(branches, gc.HasLen, 0)
   403  
   404  	_ = s.addBranch(c)
   405  	branches, err = s.State.Branches()
   406  	c.Assert(err, jc.ErrorIsNil)
   407  	c.Check(branches, gc.HasLen, 1)
   408  	c.Check(branches[0].BranchName(), gc.Equals, newBranchName)
   409  
   410  	const otherBranchName = "other-branch"
   411  	c.Assert(s.Model.AddBranch(otherBranchName, newBranchCreator), jc.ErrorIsNil)
   412  	branches, err = s.State.Branches()
   413  	c.Assert(err, jc.ErrorIsNil)
   414  	c.Check(branches, gc.HasLen, 2)
   415  
   416  	// Commit the newly added branch. Branches call should not return it.
   417  	branch, err := s.Model.Branch(otherBranchName)
   418  	c.Assert(err, jc.ErrorIsNil)
   419  	_, err = branch.Commit(newBranchCreator)
   420  	c.Assert(err, jc.ErrorIsNil)
   421  
   422  	branches, err = s.State.Branches()
   423  	c.Assert(err, jc.ErrorIsNil)
   424  	c.Check(branches, gc.HasLen, 1)
   425  	c.Check(branches[0].BranchName(), gc.Equals, newBranchName)
   426  }
   427  
   428  func (s *generationSuite) TestUnitBranch(c *gc.C) {
   429  	s.setupTestingClock(c)
   430  
   431  	branchA := s.setupAssignAllUnits(c)
   432  	c.Assert(branchA.AssignUnit("riak/0"), jc.ErrorIsNil)
   433  
   434  	c.Assert(branchA.AssignUnit("riak/2"), jc.ErrorIsNil)
   435  	c.Assert(s.Model.AddBranch("banana", newBranchCreator), jc.ErrorIsNil)
   436  	branchB, err := s.Model.Branch("banana")
   437  	c.Assert(err, jc.ErrorIsNil)
   438  
   439  	c.Assert(branchB.AssignUnit("riak/1"), jc.ErrorIsNil)
   440  
   441  	unit2Branch, err := state.UnitBranch(s.Model, "riak/2")
   442  	c.Assert(err, jc.ErrorIsNil)
   443  	c.Assert(unit2Branch.BranchName(), gc.Equals, branchA.BranchName())
   444  
   445  	unit1Branch, err := state.UnitBranch(s.Model, "riak/1")
   446  	c.Assert(err, jc.ErrorIsNil)
   447  	c.Assert(unit1Branch.BranchName(), gc.Equals, branchB.BranchName())
   448  
   449  	// Idempotent.
   450  	unit2BranchTake2, err := state.UnitBranch(s.Model, "riak/2")
   451  	c.Assert(err, jc.ErrorIsNil)
   452  	c.Assert(unit2BranchTake2.BranchName(), gc.Equals, unit2Branch.BranchName())
   453  }
   454  
   455  func (s *generationSuite) TestApplicationBranches(c *gc.C) {
   456  	s.setupTestingClock(c)
   457  
   458  	branchA := s.setupAssignAllUnits(c)
   459  	c.Assert(branchA.AssignUnit("riak/0"), jc.ErrorIsNil)
   460  
   461  	appBranchesA, err := state.ApplicationBranches(s.Model, "riak")
   462  	c.Assert(err, jc.ErrorIsNil)
   463  	c.Assert(appBranchesA, gc.HasLen, 1)
   464  	c.Assert(appBranchesA[0].BranchName(), gc.Equals, branchA.BranchName())
   465  
   466  	c.Assert(s.Model.AddBranch("banana", newBranchCreator), jc.ErrorIsNil)
   467  	branchB, err := s.Model.Branch("banana")
   468  	c.Assert(err, jc.ErrorIsNil)
   469  
   470  	appBranchesATake2, err := state.ApplicationBranches(s.Model, "riak")
   471  	c.Assert(err, jc.ErrorIsNil)
   472  	c.Assert(appBranchesATake2, gc.HasLen, 1)
   473  	c.Assert(appBranchesA[0].BranchName(), gc.Equals, appBranchesATake2[0].BranchName())
   474  
   475  	c.Assert(branchB.AssignUnit("riak/1"), jc.ErrorIsNil)
   476  
   477  	appBranchesA, err = state.ApplicationBranches(s.Model, "riak")
   478  	c.Assert(err, jc.ErrorIsNil)
   479  	c.Assert(appBranchesA, gc.HasLen, 2)
   480  
   481  	// Idempotent.
   482  	appBranchesATake2, err = state.ApplicationBranches(s.Model, "riak")
   483  	c.Assert(err, jc.ErrorIsNil)
   484  	c.Assert(appBranchesATake2, gc.DeepEquals, appBranchesA)
   485  }
   486  
   487  func (s *generationSuite) TestDestroyCleansupBranches(c *gc.C) {
   488  	s.setupTestingClock(c)
   489  
   490  	branches, err := s.State.Branches()
   491  	c.Assert(err, jc.ErrorIsNil)
   492  	c.Check(branches, gc.HasLen, 0)
   493  
   494  	_ = s.addBranch(c)
   495  
   496  	branches, err = s.State.Branches()
   497  	c.Assert(err, jc.ErrorIsNil)
   498  	c.Check(branches, gc.HasLen, 1)
   499  	c.Check(branches[0].BranchName(), gc.Equals, newBranchName)
   500  
   501  	c.Assert(s.Model.Destroy(state.DestroyModelParams{}), jc.ErrorIsNil)
   502  	c.Assert(s.Model.Refresh(), jc.ErrorIsNil)
   503  	assertNeedsCleanup(c, s.State)
   504  	assertCleanupRuns(c, s.State)
   505  
   506  	branches, err = s.State.Branches()
   507  	c.Assert(err, jc.ErrorIsNil)
   508  	c.Check(branches, gc.HasLen, 0)
   509  }
   510  
   511  func (s *generationSuite) setupAssignAllUnits(c *gc.C) *state.Generation {
   512  	var cfgYAML = `
   513  options:
   514    http_port: {default: 8089, description: HTTP Port, type: int}
   515  `
   516  	s.ch = s.AddConfigCharm(c, "riak", cfgYAML, 666)
   517  
   518  	riak := s.AddTestingApplication(c, "riak", s.ch)
   519  	for i := 0; i < 4; i++ {
   520  		_, err := riak.AddUnit(state.AddUnitParams{})
   521  		c.Assert(err, jc.ErrorIsNil)
   522  	}
   523  
   524  	return s.addBranch(c)
   525  }
   526  
   527  func (s *generationSuite) setupAssignUnits(c *gc.C) *state.Generation {
   528  	var cfgYAML = `
   529  options:
   530    http_port: {default: 8089, description: HTTP Port, type: int}
   531  `
   532  	s.ch = s.AddConfigCharm(c, "riak", cfgYAML, 666)
   533  
   534  	s.AddTestingApplication(c, "riak", s.ch)
   535  
   536  	return s.addBranch(c)
   537  }
   538  
   539  func (s *generationSuite) addBranch(c *gc.C) *state.Generation {
   540  	c.Assert(s.Model.AddBranch(newBranchName, newBranchCreator), jc.ErrorIsNil)
   541  	branch, err := s.Model.Branch(newBranchName)
   542  	c.Assert(err, jc.ErrorIsNil)
   543  	return branch
   544  }
   545  
   546  func (s *generationSuite) setupTestingClock(c *gc.C) {
   547  	clock := testclock.NewClock(testing.NonZeroTime())
   548  	clock.Advance(400000 * time.Hour)
   549  	c.Assert(s.State.SetClockForTesting(clock), jc.ErrorIsNil)
   550  }