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

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"fmt"
     8  	"strconv"
     9  	"strings"
    10  	"time" // Only used for time types.
    11  
    12  	"github.com/juju/charm/v12"
    13  	"github.com/juju/errors"
    14  	"github.com/juju/loggo"
    15  	"github.com/juju/names/v5"
    16  	jc "github.com/juju/testing/checkers"
    17  	jujutxn "github.com/juju/txn/v3"
    18  	gc "gopkg.in/check.v1"
    19  	"gopkg.in/juju/environschema.v1"
    20  
    21  	"github.com/juju/juju/core/config"
    22  	"github.com/juju/juju/core/constraints"
    23  	"github.com/juju/juju/core/instance"
    24  	"github.com/juju/juju/core/model"
    25  	"github.com/juju/juju/core/network"
    26  	"github.com/juju/juju/core/secrets"
    27  	"github.com/juju/juju/core/status"
    28  	"github.com/juju/juju/state"
    29  	stateerrors "github.com/juju/juju/state/errors"
    30  	"github.com/juju/juju/state/testing"
    31  	statetesting "github.com/juju/juju/state/testing"
    32  	coretesting "github.com/juju/juju/testing"
    33  	"github.com/juju/juju/testing/factory"
    34  )
    35  
    36  const (
    37  	contentionErr = ".*state changing too quickly; try again soon"
    38  )
    39  
    40  type UnitSuite struct {
    41  	ConnSuite
    42  	charm       *state.Charm
    43  	application *state.Application
    44  	unit        *state.Unit
    45  }
    46  
    47  var _ = gc.Suite(&UnitSuite{})
    48  
    49  func (s *UnitSuite) SetUpTest(c *gc.C) {
    50  	s.ConnSuite.SetUpTest(c)
    51  	s.charm = s.AddTestingCharm(c, "wordpress")
    52  	s.application = s.AddTestingApplication(c, "wordpress", s.charm)
    53  	var err error
    54  	s.unit, err = s.application.AddUnit(state.AddUnitParams{})
    55  	c.Assert(err, jc.ErrorIsNil)
    56  	c.Assert(s.unit.Base(), jc.DeepEquals, state.Base{OS: "ubuntu", Channel: "12.10/stable"})
    57  	c.Assert(s.unit.ShouldBeAssigned(), jc.IsTrue)
    58  }
    59  
    60  func (s *UnitSuite) TestUnitNotFound(c *gc.C) {
    61  	_, err := s.State.Unit("subway/0")
    62  	c.Assert(err, gc.ErrorMatches, `unit "subway/0" not found`)
    63  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
    64  }
    65  
    66  func (s *UnitSuite) TestApplication(c *gc.C) {
    67  	app, err := s.unit.Application()
    68  	c.Assert(err, jc.ErrorIsNil)
    69  	c.Assert(app.Name(), gc.Equals, s.unit.ApplicationName())
    70  }
    71  
    72  func (s *UnitSuite) TestCharmStateQuotaLimitWithMissingStateDocument(c *gc.C) {
    73  	// Set initial state with a restrictive limit. Since the state document
    74  	// does not exist yet, this test checks that the insert new document
    75  	// codepath correctly enforces the quota limits.
    76  	newState := new(state.UnitState)
    77  	newState.SetCharmState(map[string]string{
    78  		"answer": "42",
    79  		"data":   "encrypted",
    80  	})
    81  
    82  	err := s.unit.SetState(newState, state.UnitStateSizeLimits{
    83  		MaxCharmStateSize: 16,
    84  	})
    85  	c.Assert(err, jc.Satisfies, errors.IsQuotaLimitExceeded)
    86  
    87  	// Try again with a more generous quota limit
    88  	err = s.unit.SetState(newState, state.UnitStateSizeLimits{
    89  		MaxCharmStateSize: 640,
    90  	})
    91  	c.Assert(err, jc.ErrorIsNil)
    92  }
    93  
    94  func (s *UnitSuite) TestCharmStateQuotaLimit(c *gc.C) {
    95  	// Set initial state with a generous limit
    96  	newState := new(state.UnitState)
    97  	newState.SetCharmState(map[string]string{
    98  		"answer": "42",
    99  		"data":   "encrypted",
   100  	})
   101  
   102  	err := s.unit.SetState(newState, state.UnitStateSizeLimits{
   103  		MaxCharmStateSize: 640000,
   104  	})
   105  	c.Assert(err, jc.ErrorIsNil)
   106  
   107  	// Try to set a new state with a tight limit
   108  	newState.SetCharmState(map[string]string{
   109  		"answer": "unknown",
   110  		"data":   "decrypted",
   111  	})
   112  	err = s.unit.SetState(newState, state.UnitStateSizeLimits{
   113  		MaxCharmStateSize: 10,
   114  	})
   115  	c.Assert(err, jc.Satisfies, errors.IsQuotaLimitExceeded)
   116  }
   117  
   118  func (s *UnitSuite) TestCombinedUnitStateQuotaLimit(c *gc.C) {
   119  	// Set initial state with a generous limit
   120  	newState := new(state.UnitState)
   121  	newState.SetUniterState("my state is legendary")
   122  	newState.SetRelationState(map[int]string{42: "some serialized blob"})
   123  	newState.SetStorageState("storage is cheap")
   124  
   125  	err := s.unit.SetState(newState, state.UnitStateSizeLimits{
   126  		MaxAgentStateSize: 640000,
   127  	})
   128  	c.Assert(err, jc.ErrorIsNil)
   129  
   130  	// Try to set a new uniter state where the combined data will trip the quota limit check
   131  	newState.SetUniterState("state")
   132  	newState.SetRelationState(map[int]string{42: "a fresh serialized blob"})
   133  	newState.SetStorageState("storage")
   134  	err = s.unit.SetState(newState, state.UnitStateSizeLimits{
   135  		MaxAgentStateSize: 42,
   136  	})
   137  	c.Assert(errors.IsQuotaLimitExceeded(err), jc.IsTrue)
   138  }
   139  
   140  func (s *UnitSuite) TestUnitStateWithDualQuotaLimits(c *gc.C) {
   141  	// Set state with dual quota limits and check that both limits are
   142  	// correctly enforced and do not interfere with each other.
   143  	newState := new(state.UnitState)
   144  	newState.SetUniterState("my state is legendary")
   145  	newState.SetRelationState(map[int]string{42: "some serialized blob"})
   146  	newState.SetStorageState("storage is cheap")
   147  	newState.SetCharmState(map[string]string{
   148  		"charm-data": strings.Repeat("lol", 1024),
   149  	})
   150  
   151  	err := s.unit.SetState(newState, state.UnitStateSizeLimits{
   152  		MaxCharmStateSize: 4096,
   153  		MaxAgentStateSize: 128,
   154  	})
   155  	c.Assert(err, jc.ErrorIsNil)
   156  }
   157  
   158  func (s *UnitSuite) TestUnitStateNotSet(c *gc.C) {
   159  	// Try fetching the state without a state doc present
   160  	uState, err := s.unit.State()
   161  	c.Assert(err, gc.IsNil)
   162  	st, found := uState.CharmState()
   163  	c.Assert(st, gc.IsNil, gc.Commentf("expected to receive a nil map when no state doc is present"))
   164  	c.Assert(found, jc.IsFalse)
   165  	ust, found := uState.UniterState()
   166  	c.Assert(ust, gc.Equals, "")
   167  	c.Assert(found, jc.IsFalse)
   168  	rst, found := uState.RelationState()
   169  	c.Assert(rst, gc.IsNil, gc.Commentf("expected to receive a nil map when no state doc is present"))
   170  	c.Assert(found, jc.IsFalse)
   171  	sst, found := uState.StorageState()
   172  	c.Assert(sst, gc.Equals, "")
   173  	c.Assert(found, jc.IsFalse)
   174  	mst, found := uState.MeterStatusState()
   175  	c.Assert(mst, gc.Equals, "")
   176  	c.Assert(found, jc.IsFalse)
   177  }
   178  
   179  func (s *UnitSuite) TestUnitStateExistingDocAddNewRelationData(c *gc.C) {
   180  	initialUniterState := "testing"
   181  	us := state.NewUnitState()
   182  	us.SetUniterState(initialUniterState)
   183  	err := s.unit.SetState(us, state.UnitStateSizeLimits{})
   184  	c.Assert(err, gc.IsNil)
   185  
   186  	newRelationState := map[int]string{3: "three"}
   187  	newUS := state.NewUnitState()
   188  	newUS.SetRelationState(newRelationState)
   189  	err = s.unit.SetState(newUS, state.UnitStateSizeLimits{})
   190  	c.Assert(err, gc.IsNil)
   191  }
   192  
   193  func (s *UnitSuite) TestUnitStateMutateState(c *gc.C) {
   194  	// Set initial state; this should create a new unitstate doc
   195  	initState := s.testUnitSuite(c)
   196  
   197  	// Mutate state again with an existing state doc
   198  	newCharmState := map[string]string{"foo": "42"}
   199  	newUS := state.NewUnitState()
   200  	newUS.SetCharmState(newCharmState)
   201  	err := s.unit.SetState(newUS, state.UnitStateSizeLimits{})
   202  	c.Assert(err, gc.IsNil)
   203  
   204  	// Ensure state changed
   205  	uState, err := s.unit.State()
   206  	c.Assert(err, gc.IsNil)
   207  	assertUnitStateCharmState(c, uState, newCharmState)
   208  
   209  	// Ensure the other state did not.
   210  	assertUnitStateUniterState(c, uState, initState.uniterState)
   211  	assertUnitStateRelationState(c, uState, initState.relationState)
   212  	assertUnitStateStorageState(c, uState, initState.storageState)
   213  	assertUnitStateMeterStatusState(c, uState, initState.meterStatusState)
   214  }
   215  
   216  func (s *UnitSuite) TestUnitStateMutateUniterState(c *gc.C) {
   217  	// Set initial state; this should create a new unitstate doc
   218  	initState := s.testUnitSuite(c)
   219  
   220  	// Mutate uniter state again with an existing state doc
   221  	newUniterState := "new"
   222  	newUS := state.NewUnitState()
   223  	newUS.SetUniterState(newUniterState)
   224  	err := s.unit.SetState(newUS, state.UnitStateSizeLimits{})
   225  	c.Assert(err, gc.IsNil)
   226  
   227  	// Ensure uniter state changed
   228  	uState, err := s.unit.State()
   229  	c.Assert(err, gc.IsNil)
   230  	assertUnitStateUniterState(c, uState, newUniterState)
   231  
   232  	// Ensure the other state did not.
   233  	assertUnitStateCharmState(c, uState, initState.charmState)
   234  	assertUnitStateRelationState(c, uState, initState.relationState)
   235  	assertUnitStateStorageState(c, uState, initState.storageState)
   236  	assertUnitStateMeterStatusState(c, uState, initState.meterStatusState)
   237  }
   238  
   239  func (s *UnitSuite) TestUnitStateMutateChangeRelationState(c *gc.C) {
   240  	// Set initial state; this should create a new unitstate doc
   241  	initState := s.testUnitSuite(c)
   242  
   243  	// Mutate relation state again with an existing state doc
   244  	// by changing a value.
   245  	expectedRelationState := initState.relationState
   246  	expectedRelationState[1] = "five"
   247  	newUS := state.NewUnitState()
   248  	newUS.SetRelationState(expectedRelationState)
   249  	err := s.unit.SetState(newUS, state.UnitStateSizeLimits{})
   250  	c.Assert(err, gc.IsNil)
   251  
   252  	// Ensure relation state changed
   253  	uState, err := s.unit.State()
   254  	c.Assert(err, gc.IsNil)
   255  	assertUnitStateRelationState(c, uState, expectedRelationState)
   256  
   257  	// Ensure the other state did not.
   258  	assertUnitStateCharmState(c, uState, initState.charmState)
   259  	assertUnitStateUniterState(c, uState, initState.uniterState)
   260  	assertUnitStateStorageState(c, uState, initState.storageState)
   261  }
   262  
   263  func (s *UnitSuite) TestUnitStateMutateDeleteRelationState(c *gc.C) {
   264  	// Set initial state; this should create a new unitstate doc
   265  	initState := s.testUnitSuite(c)
   266  
   267  	// Mutate relation state again with an existing state doc
   268  	// by deleting value.
   269  	expectedRelationState := initState.relationState
   270  	delete(expectedRelationState, 2)
   271  	newUS := state.NewUnitState()
   272  	newUS.SetRelationState(expectedRelationState)
   273  	err := s.unit.SetState(newUS, state.UnitStateSizeLimits{})
   274  	c.Assert(err, gc.IsNil)
   275  
   276  	// Ensure relation state changed
   277  	uState, err := s.unit.State()
   278  	c.Assert(err, gc.IsNil)
   279  	assertUnitStateRelationState(c, uState, expectedRelationState)
   280  
   281  	// Ensure the other state did not.
   282  	assertUnitStateCharmState(c, uState, initState.charmState)
   283  	assertUnitStateUniterState(c, uState, initState.uniterState)
   284  	assertUnitStateStorageState(c, uState, initState.storageState)
   285  	assertUnitStateMeterStatusState(c, uState, initState.meterStatusState)
   286  }
   287  
   288  func (s *UnitSuite) TestUnitStateMutateStorageState(c *gc.C) {
   289  	// Set initial state; this should create a new unitstate doc
   290  	initState := s.testUnitSuite(c)
   291  
   292  	// Mutate storage state again with an existing state doc
   293  	newStorageState := "state"
   294  	newUS := state.NewUnitState()
   295  	newUS.SetStorageState(newStorageState)
   296  	err := s.unit.SetState(newUS, state.UnitStateSizeLimits{})
   297  	c.Assert(err, gc.IsNil)
   298  
   299  	// Ensure storage state changed
   300  	uState, err := s.unit.State()
   301  	c.Assert(err, gc.IsNil)
   302  	assertUnitStateStorageState(c, uState, newStorageState)
   303  
   304  	// Ensure the other state did not.
   305  	assertUnitStateCharmState(c, uState, initState.charmState)
   306  	assertUnitStateUniterState(c, uState, initState.uniterState)
   307  	assertUnitStateRelationState(c, uState, initState.relationState)
   308  	assertUnitStateMeterStatusState(c, uState, initState.meterStatusState)
   309  }
   310  
   311  func (s *UnitSuite) TestUnitStateMutateMeterStatusState(c *gc.C) {
   312  	// Set initial state; this should create a new unitstate doc
   313  	initState := s.testUnitSuite(c)
   314  
   315  	// Mutate meter status state again with an existing state doc
   316  	newMeterStatusState := "GREEN"
   317  	newUS := state.NewUnitState()
   318  	newUS.SetMeterStatusState(newMeterStatusState)
   319  	err := s.unit.SetState(newUS, state.UnitStateSizeLimits{})
   320  	c.Assert(err, gc.IsNil)
   321  
   322  	// Ensure meter status state changed
   323  	uState, err := s.unit.State()
   324  	c.Assert(err, gc.IsNil)
   325  	assertUnitStateMeterStatusState(c, uState, newMeterStatusState)
   326  
   327  	// Ensure the other state did not.
   328  	assertUnitStateCharmState(c, uState, initState.charmState)
   329  	assertUnitStateUniterState(c, uState, initState.uniterState)
   330  	assertUnitStateRelationState(c, uState, initState.relationState)
   331  	assertUnitStateStorageState(c, uState, initState.storageState)
   332  }
   333  
   334  func (s *UnitSuite) TestUnitStateDeleteState(c *gc.C) {
   335  	// Set initial state; this should create a new unitstate doc
   336  	initState := s.testUnitSuite(c)
   337  
   338  	// Mutate state again with an existing state doc
   339  	newUS := state.NewUnitState()
   340  	newUS.SetCharmState(map[string]string{})
   341  	err := s.unit.SetState(newUS, state.UnitStateSizeLimits{})
   342  	c.Assert(err, gc.IsNil)
   343  
   344  	// Ensure state changed
   345  	uState, err := s.unit.State()
   346  	c.Assert(err, jc.ErrorIsNil)
   347  	st, _ := uState.CharmState()
   348  	c.Assert(st, gc.IsNil, gc.Commentf("expected to receive a nil map when no state doc is present"))
   349  
   350  	// Ensure the other state did not.
   351  	assertUnitStateUniterState(c, uState, initState.uniterState)
   352  	assertUnitStateRelationState(c, uState, initState.relationState)
   353  	assertUnitStateStorageState(c, uState, initState.storageState)
   354  	assertUnitStateMeterStatusState(c, uState, initState.meterStatusState)
   355  }
   356  
   357  func (s *UnitSuite) TestUnitStateDeleteRelationState(c *gc.C) {
   358  	// Set initial state; this should create a new unitstate doc
   359  	initState := s.testUnitSuite(c)
   360  
   361  	// Mutate state again with an existing state doc
   362  	newUS := state.NewUnitState()
   363  	newUS.SetRelationState(map[int]string{})
   364  	err := s.unit.SetState(newUS, state.UnitStateSizeLimits{})
   365  	c.Assert(err, gc.IsNil)
   366  
   367  	// Ensure state changed
   368  	uState, err := s.unit.State()
   369  	c.Assert(err, jc.ErrorIsNil)
   370  	st, _ := uState.RelationState()
   371  	c.Assert(st, gc.IsNil, gc.Commentf("expected to receive a nil map when no state doc is present"))
   372  
   373  	// Ensure the other state did not.
   374  	assertUnitStateCharmState(c, uState, initState.charmState)
   375  	assertUnitStateUniterState(c, uState, initState.uniterState)
   376  	assertUnitStateStorageState(c, uState, initState.storageState)
   377  	assertUnitStateMeterStatusState(c, uState, initState.meterStatusState)
   378  }
   379  
   380  type initialUnitState struct {
   381  	charmState       map[string]string
   382  	uniterState      string
   383  	relationState    map[int]string
   384  	storageState     string
   385  	meterStatusState string
   386  }
   387  
   388  func (s *UnitSuite) testUnitSuite(c *gc.C) initialUnitState {
   389  	// Set initial state; this should create a new unitstate doc
   390  	initialCharmState := map[string]string{
   391  		"foo":          "bar",
   392  		"key.with.dot": "must work",
   393  		"key.with.$":   "must work to",
   394  	}
   395  	initialUniterState := "testing"
   396  	initialRelationState := map[int]string{
   397  		1: "one",
   398  		2: "two",
   399  	}
   400  	initialStorageState := "gnitset"
   401  	initialMeterStatusState := "RED"
   402  
   403  	us := state.NewUnitState()
   404  	us.SetCharmState(initialCharmState)
   405  	us.SetUniterState(initialUniterState)
   406  	us.SetRelationState(initialRelationState)
   407  	us.SetStorageState(initialStorageState)
   408  	us.SetMeterStatusState(initialMeterStatusState)
   409  	err := s.unit.SetState(us, state.UnitStateSizeLimits{})
   410  	c.Assert(err, gc.IsNil)
   411  
   412  	// Read back initial state
   413  	uState, err := s.unit.State()
   414  	c.Assert(err, gc.IsNil)
   415  	obtainedCharmState, found := uState.CharmState()
   416  	c.Assert(found, jc.IsTrue)
   417  	c.Assert(obtainedCharmState, gc.DeepEquals, initialCharmState)
   418  	obtainedUniterState, found := uState.UniterState()
   419  	c.Assert(found, jc.IsTrue)
   420  	c.Assert(obtainedUniterState, gc.Equals, initialUniterState)
   421  	obtainedRelationState, found := uState.RelationState()
   422  	c.Assert(found, jc.IsTrue)
   423  	c.Assert(obtainedRelationState, gc.DeepEquals, initialRelationState)
   424  	obtainedStorageState, found := uState.StorageState()
   425  	c.Assert(found, jc.IsTrue)
   426  	c.Assert(obtainedStorageState, gc.Equals, initialStorageState)
   427  	obtainedMeterStatusState, found := uState.MeterStatusState()
   428  	c.Assert(found, jc.IsTrue)
   429  	c.Assert(obtainedMeterStatusState, gc.Equals, initialMeterStatusState)
   430  
   431  	return initialUnitState{
   432  		charmState:       initialCharmState,
   433  		uniterState:      initialUniterState,
   434  		relationState:    initialRelationState,
   435  		storageState:     initialStorageState,
   436  		meterStatusState: initialMeterStatusState,
   437  	}
   438  }
   439  
   440  func assertUnitStateCharmState(c *gc.C, uState *state.UnitState, expected map[string]string) {
   441  	obtained, found := uState.CharmState()
   442  	c.Assert(found, jc.IsTrue)
   443  	c.Assert(obtained, gc.DeepEquals, expected)
   444  }
   445  
   446  func assertUnitStateUniterState(c *gc.C, uState *state.UnitState, expected string) {
   447  	obtained, found := uState.UniterState()
   448  	c.Assert(found, jc.IsTrue)
   449  	c.Assert(obtained, gc.Equals, expected)
   450  }
   451  
   452  func assertUnitStateRelationState(c *gc.C, uState *state.UnitState, expected map[int]string) {
   453  	obtained, found := uState.RelationState()
   454  	c.Assert(found, jc.IsTrue)
   455  	c.Assert(obtained, gc.DeepEquals, expected)
   456  }
   457  
   458  func assertUnitStateStorageState(c *gc.C, uState *state.UnitState, expected string) {
   459  	obtained, found := uState.StorageState()
   460  	c.Assert(found, jc.IsTrue)
   461  	c.Assert(obtained, gc.Equals, expected)
   462  }
   463  
   464  func assertUnitStateMeterStatusState(c *gc.C, uState *state.UnitState, expected string) {
   465  	obtained, found := uState.MeterStatusState()
   466  	c.Assert(found, jc.IsTrue)
   467  	c.Assert(obtained, gc.Equals, expected)
   468  }
   469  
   470  func (s *UnitSuite) TestUnitStateNopMutation(c *gc.C) {
   471  	// Set initial state; this should create a new unitstate doc
   472  	initialCharmState := map[string]string{
   473  		"foo":          "bar",
   474  		"key.with.dot": "must work",
   475  		"key.with.$":   "must work to",
   476  	}
   477  	initialUniterState := "unit state"
   478  	initialRelationState := map[int]string{
   479  		1: "one",
   480  		2: "two",
   481  		3: "three",
   482  	}
   483  	initialStorageState := "storage state"
   484  	iUnitState := state.NewUnitState()
   485  	iUnitState.SetCharmState(initialCharmState)
   486  	iUnitState.SetUniterState(initialUniterState)
   487  	iUnitState.SetRelationState(initialRelationState)
   488  	iUnitState.SetStorageState(initialStorageState)
   489  	err := s.unit.SetState(iUnitState, state.UnitStateSizeLimits{})
   490  	c.Assert(err, gc.IsNil)
   491  
   492  	// Read revno
   493  	txnDoc := struct {
   494  		TxnRevno int64 `bson:"txn-revno"`
   495  	}{}
   496  	coll := s.Session.DB("juju").C("unitstates")
   497  	err = coll.Find(nil).One(&txnDoc)
   498  	c.Assert(err, gc.IsNil)
   499  	curRevNo := txnDoc.TxnRevno
   500  
   501  	// Set state using the same KV pairs; this should be a no-op
   502  	err = s.unit.SetState(iUnitState, state.UnitStateSizeLimits{})
   503  	c.Assert(err, gc.IsNil)
   504  
   505  	err = coll.Find(nil).One(&txnDoc)
   506  	c.Assert(err, gc.IsNil)
   507  	c.Assert(txnDoc.TxnRevno, gc.Equals, curRevNo, gc.Commentf("expected state doc revno to remain the same"))
   508  
   509  	// Set state using a different set of KV pairs
   510  	sUnitState := state.NewUnitState()
   511  	sUnitState.SetCharmState(map[string]string{"something": "else"})
   512  	err = s.unit.SetState(sUnitState, state.UnitStateSizeLimits{})
   513  	c.Assert(err, gc.IsNil)
   514  
   515  	err = coll.Find(nil).One(&txnDoc)
   516  	c.Assert(err, gc.IsNil)
   517  	c.Assert(txnDoc.TxnRevno, jc.GreaterThan, curRevNo, gc.Commentf("expected state doc revno to be bumped"))
   518  }
   519  
   520  func (s *UnitSuite) TestConfigSettingsNeedCharmURLSet(c *gc.C) {
   521  	_, err := s.unit.ConfigSettings()
   522  	c.Assert(err, gc.ErrorMatches, "unit's charm URL must be set before retrieving config")
   523  }
   524  
   525  func (s *UnitSuite) TestConfigSettingsIncludeDefaults(c *gc.C) {
   526  	err := s.unit.SetCharmURL(s.charm.URL())
   527  	c.Assert(err, jc.ErrorIsNil)
   528  	settings, err := s.unit.ConfigSettings()
   529  	c.Assert(err, jc.ErrorIsNil)
   530  	c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "My Title"})
   531  }
   532  
   533  func (s *UnitSuite) TestConfigSettingsReflectApplication(c *gc.C) {
   534  	err := s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{"blog-title": "no title"})
   535  	c.Assert(err, jc.ErrorIsNil)
   536  	err = s.unit.SetCharmURL(s.charm.URL())
   537  	c.Assert(err, jc.ErrorIsNil)
   538  	settings, err := s.unit.ConfigSettings()
   539  	c.Assert(err, jc.ErrorIsNil)
   540  	c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "no title"})
   541  
   542  	err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{"blog-title": "ironic title"})
   543  	c.Assert(err, jc.ErrorIsNil)
   544  	settings, err = s.unit.ConfigSettings()
   545  	c.Assert(err, jc.ErrorIsNil)
   546  	c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "ironic title"})
   547  }
   548  
   549  func (s *UnitSuite) TestConfigSettingsReflectCharm(c *gc.C) {
   550  	err := s.unit.SetCharmURL(s.charm.URL())
   551  	c.Assert(err, jc.ErrorIsNil)
   552  	newCharm := s.AddConfigCharm(c, "wordpress", "options: {}", 123)
   553  	cfg := state.SetCharmConfig{Charm: newCharm, CharmOrigin: defaultCharmOrigin(newCharm.URL())}
   554  	err = s.application.SetCharm(cfg)
   555  	c.Assert(err, jc.ErrorIsNil)
   556  
   557  	// Settings still reflect charm set on unit.
   558  	settings, err := s.unit.ConfigSettings()
   559  	c.Assert(err, jc.ErrorIsNil)
   560  	c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "My Title"})
   561  
   562  	// When the unit has the new charm set, it'll see the new config.
   563  	err = s.unit.SetCharmURL(newCharm.URL())
   564  	c.Assert(err, jc.ErrorIsNil)
   565  	settings, err = s.unit.ConfigSettings()
   566  	c.Assert(err, jc.ErrorIsNil)
   567  	c.Assert(settings, gc.DeepEquals, charm.Settings{})
   568  }
   569  
   570  func (s *UnitSuite) TestWatchConfigSettingsNeedsCharmURL(c *gc.C) {
   571  	_, err := s.unit.WatchConfigSettings()
   572  	c.Assert(err, gc.ErrorMatches, "unit's charm URL must be set before watching config")
   573  }
   574  
   575  func (s *UnitSuite) TestWatchConfigSettings(c *gc.C) {
   576  	err := s.unit.SetCharmURL(s.charm.URL())
   577  	c.Assert(err, jc.ErrorIsNil)
   578  
   579  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
   580  	w, err := s.unit.WatchConfigSettings()
   581  	c.Assert(err, jc.ErrorIsNil)
   582  	defer testing.AssertStop(c, w)
   583  
   584  	// Initial event.
   585  	wc := testing.NewNotifyWatcherC(c, w)
   586  	wc.AssertOneChange()
   587  
   588  	// Update config a couple of times, check a single event.
   589  	err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{"blog-title": "superhero paparazzi"})
   590  	c.Assert(err, jc.ErrorIsNil)
   591  	// TODO(quiescence): these two changes should be one event.
   592  	wc.AssertOneChange()
   593  	err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{"blog-title": "sauceror central"})
   594  	c.Assert(err, jc.ErrorIsNil)
   595  	wc.AssertOneChange()
   596  
   597  	// Non-change is not reported.
   598  	err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{"blog-title": "sauceror central"})
   599  	c.Assert(err, jc.ErrorIsNil)
   600  	wc.AssertNoChange()
   601  
   602  	// Change application's charm; nothing detected.
   603  	newCharm := s.AddConfigCharm(c, "wordpress", floatConfig, 123)
   604  	cfg := state.SetCharmConfig{Charm: newCharm, CharmOrigin: defaultCharmOrigin(newCharm.URL())}
   605  	err = s.application.SetCharm(cfg)
   606  	c.Assert(err, jc.ErrorIsNil)
   607  	wc.AssertNoChange()
   608  
   609  	// Change application config for new charm; nothing detected.
   610  	err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{
   611  		"key": 42.0,
   612  	})
   613  	c.Assert(err, jc.ErrorIsNil)
   614  	wc.AssertNoChange()
   615  
   616  	// NOTE: if we were to change the unit to use the new charm, we'd see
   617  	// another event, because the originally-watched document will become
   618  	// unreferenced and be removed. But I'm not testing that behaviour
   619  	// because it's not very helpful and subject to change.
   620  }
   621  
   622  func (s *UnitSuite) TestWatchConfigSettingsHash(c *gc.C) {
   623  	newCharm := s.AddConfigCharm(c, "wordpress", sortableConfig, 123)
   624  	cfg := state.SetCharmConfig{Charm: newCharm, CharmOrigin: defaultCharmOrigin(newCharm.URL())}
   625  	err := s.application.SetCharm(cfg)
   626  	c.Assert(err, jc.ErrorIsNil)
   627  	err = s.unit.SetCharmURL(newCharm.URL())
   628  	c.Assert(err, jc.ErrorIsNil)
   629  
   630  	err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{"blog-title": "sauceror central"})
   631  	c.Assert(err, jc.ErrorIsNil)
   632  
   633  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
   634  	w, err := s.unit.WatchConfigSettingsHash()
   635  	c.Assert(err, jc.ErrorIsNil)
   636  	defer testing.AssertStop(c, w)
   637  
   638  	// Initial event.
   639  	wc := testing.NewStringsWatcherC(c, w)
   640  	wc.AssertChange("606cdac123d3d8d3031fe93db3d5afd9c95f709dfbc17e5eade1332b081ec6f9")
   641  
   642  	// Non-change is not reported.
   643  	err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{
   644  		"blog-title": "sauceror central",
   645  	})
   646  	c.Assert(err, jc.ErrorIsNil)
   647  	wc.AssertNoChange()
   648  
   649  	// Add an attribute that comes before.
   650  	err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{
   651  		"alphabetic": 1,
   652  	})
   653  	c.Assert(err, jc.ErrorIsNil)
   654  	// Sanity check.
   655  	config, err := s.unit.ConfigSettings()
   656  	c.Assert(err, jc.ErrorIsNil)
   657  	c.Assert(config, gc.DeepEquals, charm.Settings{
   658  		// Ints that get round-tripped through mongo come back as int64.
   659  		"alphabetic": int64(1),
   660  		"blog-title": "sauceror central",
   661  		"zygomatic":  nil,
   662  	})
   663  	wc.AssertChange("886b9586df944be35b189e5678a85ac0b2ed4da46fcb10cecfd8c06b6d1baf0f")
   664  
   665  	// And one that comes after.
   666  	err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{"zygomatic": 23})
   667  	c.Assert(err, jc.ErrorIsNil)
   668  	wc.AssertChange("9ca0a511647f09db8fab07be3c70f49cc93f38f300ca82d6e26ec7d4a8b1b4c2")
   669  
   670  	// Setting a value to int64 instead of int has no effect on the
   671  	// hash (the information always comes back as int64).
   672  	err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{"alphabetic": int64(1)})
   673  	c.Assert(err, jc.ErrorIsNil)
   674  	wc.AssertNoChange()
   675  
   676  	// Change application's charm; nothing detected.
   677  	newCharm = s.AddConfigCharm(c, "wordpress", floatConfig, 125)
   678  	cfg = state.SetCharmConfig{Charm: newCharm, CharmOrigin: defaultCharmOrigin(newCharm.URL())}
   679  	err = s.application.SetCharm(cfg)
   680  	c.Assert(err, jc.ErrorIsNil)
   681  	wc.AssertNoChange()
   682  
   683  	// Change application config for new charm; nothing detected.
   684  	err = s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{"key": 42.0})
   685  	c.Assert(err, jc.ErrorIsNil)
   686  	wc.AssertNoChange()
   687  
   688  	// NOTE: if we were to change the unit to use the new charm, we'd see
   689  	// another event, because the originally-watched document will become
   690  	// unreferenced and be removed. But I'm not testing that behaviour
   691  	// because it's not very helpful and subject to change.
   692  
   693  	err = s.unit.Destroy()
   694  	c.Assert(err, jc.ErrorIsNil)
   695  	wc.AssertChange("")
   696  }
   697  
   698  func (s *UnitSuite) TestConfigHashesDifferentForDifferentCharms(c *gc.C) {
   699  	// Config hashes should be different if the charm url changes,
   700  	// even if the config is otherwise unchanged. This ensures that
   701  	// config-changed will be run on a unit after its charm is
   702  	// upgraded.
   703  	c.Logf("charm url %s", s.charm.URL())
   704  	err := s.application.UpdateCharmConfig(model.GenerationMaster, charm.Settings{"blog-title": "sauceror central"})
   705  	c.Assert(err, jc.ErrorIsNil)
   706  	err = s.unit.SetCharmURL(s.charm.URL())
   707  	c.Assert(err, jc.ErrorIsNil)
   708  
   709  	w1, err := s.unit.WatchConfigSettingsHash()
   710  	c.Assert(err, jc.ErrorIsNil)
   711  	defer testing.AssertStop(c, w1)
   712  
   713  	wc1 := testing.NewStringsWatcherC(c, w1)
   714  	wc1.AssertChange("2e1f49c3e8b53892b822558401af33589522094681276a98458595114e04c0c1")
   715  
   716  	newCharm := s.AddConfigCharm(c, "wordpress", wordpressConfig, 125)
   717  	cfg := state.SetCharmConfig{Charm: newCharm, CharmOrigin: defaultCharmOrigin(newCharm.URL())}
   718  	err = s.application.SetCharm(cfg)
   719  	c.Assert(err, jc.ErrorIsNil)
   720  
   721  	c.Logf("new charm url %s", newCharm.URL())
   722  	err = s.unit.SetCharmURL(newCharm.URL())
   723  	c.Assert(err, jc.ErrorIsNil)
   724  
   725  	w3, err := s.unit.WatchConfigSettingsHash()
   726  	c.Assert(err, jc.ErrorIsNil)
   727  	defer testing.AssertStop(c, w3)
   728  
   729  	wc3 := testing.NewStringsWatcherC(c, w3)
   730  	wc3.AssertChange("35412457529c9e0b64b7642ad0f76137ee13b104c94136d0c18b2fe54ddf5d36")
   731  }
   732  
   733  func (s *UnitSuite) TestWatchApplicationConfigSettingsHash(c *gc.C) {
   734  	w, err := s.unit.WatchApplicationConfigSettingsHash()
   735  	c.Assert(err, jc.ErrorIsNil)
   736  	defer testing.AssertStop(c, w)
   737  
   738  	wc := testing.NewStringsWatcherC(c, w)
   739  	wc.AssertChange("92652ce7679e295c6567a3891c562dcab727c71543f8c1c3a38c3626ce064019")
   740  
   741  	schema := environschema.Fields{
   742  		"username":    environschema.Attr{Type: environschema.Tstring},
   743  		"alive":       environschema.Attr{Type: environschema.Tbool},
   744  		"skill-level": environschema.Attr{Type: environschema.Tint},
   745  		"options":     environschema.Attr{Type: environschema.Tattrs},
   746  	}
   747  
   748  	err = s.application.UpdateApplicationConfig(config.ConfigAttributes{
   749  		"username":    "abbas",
   750  		"alive":       true,
   751  		"skill-level": 23,
   752  		"options": map[string]string{
   753  			"fortuna": "crescis",
   754  			"luna":    "velut",
   755  			"status":  "malus",
   756  		},
   757  	}, nil, schema, nil)
   758  	c.Assert(err, jc.ErrorIsNil)
   759  
   760  	wc.AssertChange("fa3b92db7c37210ed80da08c522abbe8ffffc9982ad2136342f3df8f221b5768")
   761  
   762  	// For reasons that I don't understand, application config
   763  	// converts int64s to int while charm config converts ints to
   764  	// int64 - I'm not going to try to untangle that now.
   765  	err = s.application.UpdateApplicationConfig(config.ConfigAttributes{
   766  		"username":    "bob",
   767  		"skill-level": int64(23),
   768  	}, nil, schema, nil)
   769  	c.Assert(err, jc.ErrorIsNil)
   770  
   771  	wc.AssertChange("d437a3b548fecf7f8c7748bf8d2acab0960ceb53f316a22803a6ca7442a9b8b3")
   772  }
   773  
   774  func (s *UnitSuite) addSubordinateUnit(c *gc.C) *state.Unit {
   775  	subCharm := s.AddTestingCharm(c, "logging")
   776  	s.AddTestingApplication(c, "logging", subCharm)
   777  	eps, err := s.State.InferEndpoints("wordpress", "logging")
   778  	c.Assert(err, jc.ErrorIsNil)
   779  	rel, err := s.State.AddRelation(eps...)
   780  	c.Assert(err, jc.ErrorIsNil)
   781  	ru, err := rel.Unit(s.unit)
   782  	c.Assert(err, jc.ErrorIsNil)
   783  	err = ru.EnterScope(nil)
   784  	c.Assert(err, jc.ErrorIsNil)
   785  	subUnit, err := s.State.Unit("logging/0")
   786  	c.Assert(err, jc.ErrorIsNil)
   787  	return subUnit
   788  }
   789  
   790  func (s *UnitSuite) setAssignedMachineAddresses(c *gc.C, u *state.Unit) {
   791  	mid, err := u.AssignedMachineId()
   792  	if errors.IsNotAssigned(err) {
   793  		err = u.AssignToNewMachine()
   794  		c.Assert(err, jc.ErrorIsNil)
   795  		mid, err = u.AssignedMachineId()
   796  	}
   797  	c.Assert(err, jc.ErrorIsNil)
   798  	machine, err := s.State.Machine(mid)
   799  	c.Assert(err, jc.ErrorIsNil)
   800  	err = machine.SetProvisioned("i-exist", "", "fake_nonce", nil)
   801  	c.Assert(err, jc.ErrorIsNil)
   802  	err = machine.SetProviderAddresses(
   803  		network.NewSpaceAddress("private.address.example.com", network.WithScope(network.ScopeCloudLocal)),
   804  		network.NewSpaceAddress("public.address.example.com", network.WithScope(network.ScopePublic)),
   805  	)
   806  	c.Assert(err, jc.ErrorIsNil)
   807  }
   808  
   809  func (s *UnitSuite) TestPublicAddressSubordinate(c *gc.C) {
   810  	// A subordinate unit will never be created without the principal
   811  	// being assigned to a machine.
   812  	s.setAssignedMachineAddresses(c, s.unit)
   813  	subUnit := s.addSubordinateUnit(c)
   814  	address, err := subUnit.PublicAddress()
   815  	c.Assert(err, jc.ErrorIsNil)
   816  	c.Assert(address.Value, gc.Equals, "public.address.example.com")
   817  }
   818  
   819  func (s *UnitSuite) TestAllAddresses(c *gc.C) {
   820  	// Only used for CAAS units.
   821  	all, err := s.unit.AllAddresses()
   822  	c.Assert(err, jc.ErrorIsNil)
   823  	c.Assert(all, gc.HasLen, 0)
   824  }
   825  
   826  func (s *UnitSuite) TestPublicAddress(c *gc.C) {
   827  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   828  	c.Assert(err, jc.ErrorIsNil)
   829  	err = s.unit.AssignToMachine(machine)
   830  	c.Assert(err, jc.ErrorIsNil)
   831  
   832  	_, err = s.unit.PublicAddress()
   833  	c.Assert(err, jc.Satisfies, network.IsNoAddressError)
   834  
   835  	public := network.NewSpaceAddress("8.8.8.8", network.WithScope(network.ScopePublic))
   836  	private := network.NewSpaceAddress("127.0.0.1", network.WithScope(network.ScopeCloudLocal))
   837  
   838  	err = machine.SetProviderAddresses(public, private)
   839  	c.Assert(err, jc.ErrorIsNil)
   840  
   841  	address, err := s.unit.PublicAddress()
   842  	c.Assert(err, jc.ErrorIsNil)
   843  	c.Check(address.Value, gc.Equals, "8.8.8.8")
   844  }
   845  
   846  func (s *UnitSuite) TestStablePrivateAddress(c *gc.C) {
   847  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   848  	c.Assert(err, jc.ErrorIsNil)
   849  	err = s.unit.AssignToMachine(machine)
   850  	c.Assert(err, jc.ErrorIsNil)
   851  
   852  	err = machine.SetMachineAddresses(network.NewSpaceAddress("10.0.0.2"))
   853  	c.Assert(err, jc.ErrorIsNil)
   854  
   855  	// Now add an address that would previously have sorted before the
   856  	// default.
   857  	err = machine.SetMachineAddresses(network.NewSpaceAddress("10.0.0.1"), network.NewSpaceAddress("10.0.0.2"))
   858  	c.Assert(err, jc.ErrorIsNil)
   859  
   860  	// Assert the address is unchanged.
   861  	addr, err := s.unit.PrivateAddress()
   862  	c.Assert(err, jc.ErrorIsNil)
   863  	c.Assert(addr.Value, gc.Equals, "10.0.0.2")
   864  }
   865  
   866  func (s *UnitSuite) TestStablePublicAddress(c *gc.C) {
   867  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   868  	c.Assert(err, jc.ErrorIsNil)
   869  	err = s.unit.AssignToMachine(machine)
   870  	c.Assert(err, jc.ErrorIsNil)
   871  
   872  	err = machine.SetProviderAddresses(network.NewSpaceAddress("8.8.8.8"))
   873  	c.Assert(err, jc.ErrorIsNil)
   874  
   875  	// Now add an address that would previously have sorted before the
   876  	// default.
   877  	err = machine.SetProviderAddresses(network.NewSpaceAddress("8.8.4.4"), network.NewSpaceAddress("8.8.8.8"))
   878  	c.Assert(err, jc.ErrorIsNil)
   879  
   880  	// Assert the address is unchanged.
   881  	addr, err := s.unit.PublicAddress()
   882  	c.Assert(err, jc.ErrorIsNil)
   883  	c.Assert(addr.Value, gc.Equals, "8.8.8.8")
   884  }
   885  
   886  func (s *UnitSuite) TestPublicAddressMachineAddresses(c *gc.C) {
   887  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   888  	c.Assert(err, jc.ErrorIsNil)
   889  	err = s.unit.AssignToMachine(machine)
   890  	c.Assert(err, jc.ErrorIsNil)
   891  
   892  	publicProvider := network.NewSpaceAddress("8.8.8.8", network.WithScope(network.ScopePublic))
   893  	privateProvider := network.NewSpaceAddress("127.0.0.1", network.WithScope(network.ScopeCloudLocal))
   894  	privateMachine := network.NewSpaceAddress("127.0.0.2")
   895  
   896  	err = machine.SetProviderAddresses(privateProvider)
   897  	c.Assert(err, jc.ErrorIsNil)
   898  	err = machine.SetMachineAddresses(privateMachine)
   899  	c.Assert(err, jc.ErrorIsNil)
   900  	address, err := s.unit.PublicAddress()
   901  	c.Assert(err, jc.ErrorIsNil)
   902  	c.Check(address.Value, gc.Equals, "127.0.0.1")
   903  
   904  	err = machine.SetProviderAddresses(publicProvider, privateProvider)
   905  	c.Assert(err, jc.ErrorIsNil)
   906  	address, err = s.unit.PublicAddress()
   907  	c.Assert(err, jc.ErrorIsNil)
   908  	c.Check(address.Value, gc.Equals, "8.8.8.8")
   909  }
   910  
   911  func (s *UnitSuite) TestPrivateAddressSubordinate(c *gc.C) {
   912  	// A subordinate unit will never be created without the principal
   913  	// being assigned to a machine.
   914  	s.setAssignedMachineAddresses(c, s.unit)
   915  	subUnit := s.addSubordinateUnit(c)
   916  	address, err := subUnit.PrivateAddress()
   917  	c.Assert(err, jc.ErrorIsNil)
   918  	c.Assert(address.Value, gc.Equals, "private.address.example.com")
   919  }
   920  
   921  func (s *UnitSuite) TestPrivateAddress(c *gc.C) {
   922  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   923  	c.Assert(err, jc.ErrorIsNil)
   924  	err = s.unit.AssignToMachine(machine)
   925  	c.Assert(err, jc.ErrorIsNil)
   926  
   927  	_, err = s.unit.PrivateAddress()
   928  	c.Assert(err, jc.Satisfies, network.IsNoAddressError)
   929  
   930  	public := network.NewSpaceAddress("8.8.8.8", network.WithScope(network.ScopePublic))
   931  	private := network.NewSpaceAddress("127.0.0.1", network.WithScope(network.ScopeCloudLocal))
   932  
   933  	err = machine.SetProviderAddresses(public, private)
   934  	c.Assert(err, jc.ErrorIsNil)
   935  
   936  	address, err := s.unit.PrivateAddress()
   937  	c.Assert(err, jc.ErrorIsNil)
   938  	c.Check(address.Value, gc.Equals, "127.0.0.1")
   939  }
   940  
   941  type destroyMachineTestCase struct {
   942  	target    *state.Unit
   943  	host      *state.Machine
   944  	desc      string
   945  	destroyed bool
   946  }
   947  
   948  func (s *UnitSuite) destroyMachineTestCases(c *gc.C) []destroyMachineTestCase {
   949  	var result []destroyMachineTestCase
   950  	var err error
   951  
   952  	{
   953  		tc := destroyMachineTestCase{desc: "standalone principal", destroyed: true}
   954  		tc.host, err = s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   955  		c.Assert(err, jc.ErrorIsNil)
   956  		tc.target, err = s.application.AddUnit(state.AddUnitParams{})
   957  		c.Assert(err, jc.ErrorIsNil)
   958  		c.Assert(tc.target.AssignToMachine(tc.host), gc.IsNil)
   959  		result = append(result, tc)
   960  	}
   961  	{
   962  		tc := destroyMachineTestCase{desc: "co-located principals", destroyed: false}
   963  		tc.host, err = s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   964  		c.Assert(err, jc.ErrorIsNil)
   965  		tc.target, err = s.application.AddUnit(state.AddUnitParams{})
   966  		c.Assert(err, jc.ErrorIsNil)
   967  		c.Assert(tc.target.AssignToMachine(tc.host), gc.IsNil)
   968  		colocated, err := s.application.AddUnit(state.AddUnitParams{})
   969  		c.Assert(err, jc.ErrorIsNil)
   970  		c.Assert(colocated.AssignToMachine(tc.host), gc.IsNil)
   971  
   972  		result = append(result, tc)
   973  	}
   974  	{
   975  		tc := destroyMachineTestCase{desc: "host has container", destroyed: false}
   976  		tc.host, err = s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   977  		c.Assert(err, jc.ErrorIsNil)
   978  		_, err := s.State.AddMachineInsideMachine(state.MachineTemplate{
   979  			Base: state.UbuntuBase("12.10"),
   980  			Jobs: []state.MachineJob{state.JobHostUnits},
   981  		}, tc.host.Id(), instance.LXD)
   982  		c.Assert(err, jc.ErrorIsNil)
   983  		tc.target, err = s.application.AddUnit(state.AddUnitParams{})
   984  		c.Assert(err, jc.ErrorIsNil)
   985  		c.Assert(tc.target.AssignToMachine(tc.host), gc.IsNil)
   986  
   987  		result = append(result, tc)
   988  	}
   989  	{
   990  		tc := destroyMachineTestCase{desc: "host has vote", destroyed: false}
   991  		tc.host, err = s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   992  		c.Assert(err, jc.ErrorIsNil)
   993  		_, err = s.State.EnableHA(1, constraints.Value{}, state.UbuntuBase("12.10"), []string{tc.host.Id()})
   994  		c.Assert(err, jc.ErrorIsNil)
   995  		node, err := s.State.ControllerNode(tc.host.Id())
   996  		c.Assert(err, jc.ErrorIsNil)
   997  		c.Assert(node.SetHasVote(true), gc.IsNil)
   998  		tc.target, err = s.application.AddUnit(state.AddUnitParams{})
   999  		c.Assert(err, jc.ErrorIsNil)
  1000  		c.Assert(tc.target.AssignToMachine(tc.host), gc.IsNil)
  1001  
  1002  		result = append(result, tc)
  1003  	}
  1004  	{
  1005  		tc := destroyMachineTestCase{desc: "unassigned unit", destroyed: true}
  1006  		tc.host, err = s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
  1007  		c.Assert(err, jc.ErrorIsNil)
  1008  		tc.target, err = s.application.AddUnit(state.AddUnitParams{})
  1009  		c.Assert(err, jc.ErrorIsNil)
  1010  		c.Assert(tc.target.AssignToMachine(tc.host), gc.IsNil)
  1011  		result = append(result, tc)
  1012  	}
  1013  
  1014  	return result
  1015  }
  1016  
  1017  func (s *UnitSuite) TestRemoveUnitMachineFastForwardDestroy(c *gc.C) {
  1018  	for _, tc := range s.destroyMachineTestCases(c) {
  1019  		c.Log(tc.desc)
  1020  		err := tc.host.SetProvisioned("inst-id", "", "fake_nonce", nil)
  1021  		c.Assert(err, jc.ErrorIsNil)
  1022  		c.Assert(tc.target.Destroy(), gc.IsNil)
  1023  		if tc.destroyed {
  1024  			assertLife(c, tc.host, state.Dying)
  1025  			c.Assert(tc.host.EnsureDead(), gc.IsNil)
  1026  		} else {
  1027  			assertLife(c, tc.host, state.Alive)
  1028  			c.Assert(tc.host.Destroy(), gc.NotNil)
  1029  		}
  1030  	}
  1031  }
  1032  
  1033  func (s *UnitSuite) TestRemoveUnitMachineNoFastForwardDestroy(c *gc.C) {
  1034  	for _, tc := range s.destroyMachineTestCases(c) {
  1035  		c.Log(tc.desc)
  1036  		err := tc.host.SetProvisioned("inst-id", "", "fake_nonce", nil)
  1037  		c.Assert(err, jc.ErrorIsNil)
  1038  		preventUnitDestroyRemove(c, tc.target)
  1039  		c.Assert(tc.target.Destroy(), gc.IsNil)
  1040  		c.Assert(tc.target.EnsureDead(), gc.IsNil)
  1041  		assertLife(c, tc.host, state.Alive)
  1042  		c.Assert(tc.target.Remove(), gc.IsNil)
  1043  		if tc.destroyed {
  1044  			assertLife(c, tc.host, state.Dying)
  1045  		} else {
  1046  			assertLife(c, tc.host, state.Alive)
  1047  			c.Assert(tc.host.Destroy(), gc.NotNil)
  1048  		}
  1049  	}
  1050  }
  1051  
  1052  func (s *UnitSuite) TestRemoveUnitMachineNoDestroy(c *gc.C) {
  1053  	charmWithOut := s.AddTestingCharm(c, "mysql")
  1054  	applicationWithOutProfile := s.AddTestingApplication(c, "mysql", charmWithOut)
  1055  
  1056  	host, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
  1057  	c.Assert(err, jc.ErrorIsNil)
  1058  	err = host.SetProvisioned("inst-id", "", "fake_nonce", nil)
  1059  	c.Assert(err, jc.ErrorIsNil)
  1060  
  1061  	target, err := s.application.AddUnit(state.AddUnitParams{})
  1062  	c.Assert(err, jc.ErrorIsNil)
  1063  	c.Assert(target.AssignToMachine(host), gc.IsNil)
  1064  
  1065  	colocated, err := applicationWithOutProfile.AddUnit(state.AddUnitParams{})
  1066  	c.Assert(err, jc.ErrorIsNil)
  1067  	c.Assert(colocated.AssignToMachine(host), gc.IsNil)
  1068  	c.Assert(colocated.Destroy(), gc.IsNil)
  1069  	assertLife(c, host, state.Alive)
  1070  
  1071  	c.Assert(host.Destroy(), gc.NotNil)
  1072  }
  1073  
  1074  func (s *UnitSuite) setControllerVote(c *gc.C, id string, hasVote bool) {
  1075  	node, err := s.State.ControllerNode(id)
  1076  	c.Assert(err, jc.ErrorIsNil)
  1077  	c.Assert(node.SetHasVote(hasVote), gc.IsNil)
  1078  }
  1079  
  1080  func (s *UnitSuite) demoteController(c *gc.C, m *state.Machine) {
  1081  	s.setControllerVote(c, m.Id(), false)
  1082  	c.Assert(state.SetWantsVote(s.State, m.Id(), false), jc.ErrorIsNil)
  1083  	node, err := s.State.ControllerNode(m.Id())
  1084  	c.Assert(err, jc.ErrorIsNil)
  1085  	err = s.State.RemoveControllerReference(node)
  1086  	c.Assert(err, jc.ErrorIsNil)
  1087  }
  1088  
  1089  func (s *UnitSuite) TestRemoveUnitMachineThrashed(c *gc.C) {
  1090  	host, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
  1091  	c.Assert(err, jc.ErrorIsNil)
  1092  	_, err = s.State.EnableHA(3, constraints.Value{}, state.UbuntuBase("12.10"), []string{host.Id()})
  1093  	c.Assert(err, jc.ErrorIsNil)
  1094  	err = host.SetProvisioned("inst-id", "", "fake_nonce", nil)
  1095  	c.Assert(err, jc.ErrorIsNil)
  1096  	s.setControllerVote(c, host.Id(), true)
  1097  	target, err := s.application.AddUnit(state.AddUnitParams{})
  1098  	c.Assert(err, jc.ErrorIsNil)
  1099  	c.Assert(target.AssignToMachine(host), gc.IsNil)
  1100  	flip := jujutxn.TestHook{
  1101  		Before: func() {
  1102  			s.demoteController(c, host)
  1103  		},
  1104  	}
  1105  	flop := jujutxn.TestHook{
  1106  		Before: func() {
  1107  			s.setControllerVote(c, host.Id(), true)
  1108  		},
  1109  	}
  1110  	state.SetMaxTxnAttempts(c, s.State, 3)
  1111  	defer state.SetTestHooks(c, s.State, flip, flop, flip).Check()
  1112  
  1113  	c.Assert(target.Destroy(), gc.ErrorMatches, `cannot destroy unit "wordpress/1": state changing too quickly; try again soon`)
  1114  }
  1115  
  1116  func (s *UnitSuite) TestRemoveUnitMachineRetryVoter(c *gc.C) {
  1117  	host, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
  1118  	c.Assert(err, jc.ErrorIsNil)
  1119  	_, err = s.State.EnableHA(3, constraints.Value{}, state.UbuntuBase("12.10"), []string{host.Id()})
  1120  	c.Assert(err, jc.ErrorIsNil)
  1121  	err = host.SetProvisioned("inst-id", "", "fake_nonce", nil)
  1122  	c.Assert(err, jc.ErrorIsNil)
  1123  	target, err := s.application.AddUnit(state.AddUnitParams{})
  1124  	c.Assert(err, jc.ErrorIsNil)
  1125  	c.Assert(target.AssignToMachine(host), gc.IsNil)
  1126  
  1127  	defer state.SetBeforeHooks(c, s.State, func() {
  1128  		s.setControllerVote(c, host.Id(), true)
  1129  	}).Check()
  1130  
  1131  	c.Assert(target.Destroy(), gc.IsNil)
  1132  	assertLife(c, host, state.Alive)
  1133  }
  1134  
  1135  func (s *UnitSuite) TestRemoveUnitMachineRetryNoVoter(c *gc.C) {
  1136  	host, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
  1137  	c.Assert(err, jc.ErrorIsNil)
  1138  	_, err = s.State.EnableHA(3, constraints.Value{}, state.UbuntuBase("12.10"), []string{host.Id()})
  1139  	c.Assert(err, jc.ErrorIsNil)
  1140  	err = host.SetProvisioned("inst-id", "", "fake_nonce", nil)
  1141  	c.Assert(err, jc.ErrorIsNil)
  1142  	target, err := s.application.AddUnit(state.AddUnitParams{})
  1143  	c.Assert(err, jc.ErrorIsNil)
  1144  	c.Assert(target.AssignToMachine(host), gc.IsNil)
  1145  	s.setControllerVote(c, host.Id(), true)
  1146  
  1147  	defer state.SetBeforeHooks(c, s.State, func() {
  1148  		s.demoteController(c, host)
  1149  	}, nil).Check()
  1150  
  1151  	c.Assert(target.Destroy(), gc.IsNil)
  1152  	assertLife(c, host, state.Dying)
  1153  }
  1154  
  1155  func (s *UnitSuite) TestRemoveUnitMachineRetryContainer(c *gc.C) {
  1156  	host, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
  1157  	c.Assert(err, jc.ErrorIsNil)
  1158  	target, err := s.application.AddUnit(state.AddUnitParams{})
  1159  	c.Assert(err, jc.ErrorIsNil)
  1160  	c.Assert(target.AssignToMachine(host), gc.IsNil)
  1161  	defer state.SetTestHooks(c, s.State, jujutxn.TestHook{
  1162  		Before: func() {
  1163  			machine, err := s.State.AddMachineInsideMachine(state.MachineTemplate{
  1164  				Base: state.UbuntuBase("12.10"),
  1165  				Jobs: []state.MachineJob{state.JobHostUnits},
  1166  			}, host.Id(), instance.LXD)
  1167  			c.Assert(err, jc.ErrorIsNil)
  1168  			assertLife(c, machine, state.Alive)
  1169  
  1170  			// test-setup verification for the disqualifying machine.
  1171  			hostHandle, err := s.State.Machine(host.Id())
  1172  			c.Assert(err, jc.ErrorIsNil)
  1173  			containers, err := hostHandle.Containers()
  1174  			c.Assert(err, jc.ErrorIsNil)
  1175  			c.Assert(containers, gc.HasLen, 1)
  1176  		},
  1177  	}).Check()
  1178  
  1179  	c.Assert(target.Destroy(), gc.IsNil)
  1180  	assertLife(c, host, state.Alive)
  1181  }
  1182  
  1183  func (s *UnitSuite) TestRemoveUnitMachineRetryOrCond(c *gc.C) {
  1184  	host, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
  1185  	c.Assert(err, jc.ErrorIsNil)
  1186  	_, err = s.State.EnableHA(1, constraints.Value{}, state.UbuntuBase("12.10"), []string{host.Id()})
  1187  	c.Assert(err, jc.ErrorIsNil)
  1188  	err = host.SetProvisioned("inst-id", "", "fake_nonce", nil)
  1189  	c.Assert(err, jc.ErrorIsNil)
  1190  	target, err := s.application.AddUnit(state.AddUnitParams{})
  1191  	c.Assert(err, jc.ErrorIsNil)
  1192  	c.Assert(target.AssignToMachine(host), gc.IsNil)
  1193  
  1194  	// This unit will be colocated in the transaction hook to cause a retry.
  1195  	colocated, err := s.application.AddUnit(state.AddUnitParams{})
  1196  	c.Assert(err, jc.ErrorIsNil)
  1197  
  1198  	s.setControllerVote(c, host.Id(), true)
  1199  
  1200  	defer state.SetTestHooks(c, s.State, jujutxn.TestHook{
  1201  		Before: func() {
  1202  			// Original assertion preventing host removal is no longer valid
  1203  			s.setControllerVote(c, host.Id(), false)
  1204  
  1205  			// But now the host gets a colocated unit, a different condition preventing removal
  1206  			hostHandle, err := s.State.Machine(host.Id())
  1207  			c.Assert(err, jc.ErrorIsNil)
  1208  			c.Assert(colocated.AssignToMachine(hostHandle), gc.IsNil)
  1209  		},
  1210  	}).Check()
  1211  
  1212  	c.Assert(target.Destroy(), gc.IsNil)
  1213  	assertLife(c, host, state.Alive)
  1214  }
  1215  
  1216  func (s *UnitSuite) TestRemoveUnitWRelationLastUnit(c *gc.C) {
  1217  	// This will assign it to a machine, and make it look like the machine agent is active.
  1218  	preventUnitDestroyRemove(c, s.unit)
  1219  	mysqlCharm := s.AddTestingCharm(c, "mysql")
  1220  	mysqlApp := s.AddTestingApplication(c, "mysql", mysqlCharm)
  1221  	mysqlUnit, err := mysqlApp.AddUnit(state.AddUnitParams{})
  1222  	c.Assert(err, jc.ErrorIsNil)
  1223  	c.Assert(mysqlUnit.AssignToNewMachine(), jc.ErrorIsNil)
  1224  	endpoints, err := s.State.InferEndpoints("wordpress", "mysql")
  1225  	c.Assert(err, jc.ErrorIsNil)
  1226  	rel, err := s.State.AddRelation(endpoints...)
  1227  	c.Assert(err, jc.ErrorIsNil)
  1228  	relationUnit, err := rel.Unit(s.unit)
  1229  	c.Assert(err, jc.ErrorIsNil)
  1230  	c.Assert(relationUnit.EnterScope(nil), jc.ErrorIsNil)
  1231  	c.Assert(s.application.Destroy(), jc.ErrorIsNil)
  1232  	assertLife(c, s.application, state.Dying)
  1233  	c.Assert(s.unit.Destroy(), jc.ErrorIsNil)
  1234  	assertLife(c, s.unit, state.Dying)
  1235  	assertLife(c, s.application, state.Dying)
  1236  	c.Assert(s.unit.EnsureDead(), jc.ErrorIsNil)
  1237  	assertLife(c, s.application, state.Dying)
  1238  	c.Assert(s.unit.Remove(), jc.ErrorIsNil)
  1239  	c.Assert(s.State.Cleanup(), jc.ErrorIsNil)
  1240  	// Now the application should be gone
  1241  	c.Assert(s.application.Refresh(), jc.Satisfies, errors.IsNotFound)
  1242  }
  1243  
  1244  func (s *UnitSuite) TestRefresh(c *gc.C) {
  1245  	unit1, err := s.State.Unit(s.unit.Name())
  1246  	c.Assert(err, jc.ErrorIsNil)
  1247  
  1248  	err = s.unit.SetPassword("arble-farble-dying-yarble")
  1249  	c.Assert(err, jc.ErrorIsNil)
  1250  	valid := unit1.PasswordValid("arble-farble-dying-yarble")
  1251  	c.Assert(valid, jc.IsFalse)
  1252  	err = unit1.Refresh()
  1253  	c.Assert(err, jc.ErrorIsNil)
  1254  	valid = unit1.PasswordValid("arble-farble-dying-yarble")
  1255  	c.Assert(valid, jc.IsTrue)
  1256  
  1257  	err = unit1.EnsureDead()
  1258  	c.Assert(err, jc.ErrorIsNil)
  1259  	err = unit1.Remove()
  1260  	c.Assert(err, jc.ErrorIsNil)
  1261  	err = unit1.Refresh()
  1262  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1263  }
  1264  
  1265  func (s *UnitSuite) TestSetCharmURLSuccess(c *gc.C) {
  1266  	preventUnitDestroyRemove(c, s.unit)
  1267  	curl := s.unit.CharmURL()
  1268  	c.Assert(curl, gc.IsNil)
  1269  
  1270  	err := s.unit.SetCharmURL(s.charm.URL())
  1271  	c.Assert(err, jc.ErrorIsNil)
  1272  
  1273  	curl = s.unit.CharmURL()
  1274  	c.Assert(curl, gc.NotNil)
  1275  	c.Assert(*curl, gc.Equals, s.charm.URL())
  1276  }
  1277  
  1278  func (s *UnitSuite) TestSetCharmURLFailures(c *gc.C) {
  1279  	preventUnitDestroyRemove(c, s.unit)
  1280  	curl := s.unit.CharmURL()
  1281  	c.Assert(curl, gc.IsNil)
  1282  
  1283  	err := s.unit.SetCharmURL("")
  1284  	c.Assert(err, gc.ErrorMatches, "cannot set empty charm url")
  1285  
  1286  	err = s.unit.SetCharmURL("ch:missing/one-1")
  1287  	c.Assert(err, gc.ErrorMatches, `unknown charm url "ch:missing/one-1"`)
  1288  
  1289  	err = s.unit.EnsureDead()
  1290  	c.Assert(err, jc.ErrorIsNil)
  1291  	err = s.unit.SetCharmURL(s.charm.URL())
  1292  	c.Assert(err, gc.Equals, stateerrors.ErrDead)
  1293  }
  1294  
  1295  func (s *UnitSuite) TestSetCharmURLWithRemovedUnit(c *gc.C) {
  1296  	err := s.unit.Destroy()
  1297  	c.Assert(err, jc.ErrorIsNil)
  1298  	assertRemoved(c, s.unit)
  1299  
  1300  	err = s.unit.SetCharmURL(s.charm.URL())
  1301  	c.Assert(err, gc.Equals, stateerrors.ErrDead)
  1302  }
  1303  
  1304  func (s *UnitSuite) TestSetCharmURLWithDyingUnit(c *gc.C) {
  1305  	preventUnitDestroyRemove(c, s.unit)
  1306  	err := s.unit.Destroy()
  1307  	c.Assert(err, jc.ErrorIsNil)
  1308  	assertLife(c, s.unit, state.Dying)
  1309  
  1310  	err = s.unit.SetCharmURL(s.charm.URL())
  1311  	c.Assert(err, jc.ErrorIsNil)
  1312  
  1313  	curl := s.unit.CharmURL()
  1314  	c.Assert(curl, gc.NotNil)
  1315  	c.Assert(*curl, gc.Equals, s.charm.URL())
  1316  }
  1317  
  1318  func (s *UnitSuite) TestSetCharmURLRetriesWithDeadUnit(c *gc.C) {
  1319  	preventUnitDestroyRemove(c, s.unit)
  1320  
  1321  	defer state.SetBeforeHooks(c, s.State, func() {
  1322  		err := s.unit.Destroy()
  1323  		c.Assert(err, jc.ErrorIsNil)
  1324  		err = s.unit.EnsureDead()
  1325  		c.Assert(err, jc.ErrorIsNil)
  1326  		assertLife(c, s.unit, state.Dead)
  1327  	}).Check()
  1328  
  1329  	err := s.unit.SetCharmURL(s.charm.URL())
  1330  	c.Assert(err, gc.Equals, stateerrors.ErrDead)
  1331  }
  1332  
  1333  func (s *UnitSuite) TestSetCharmURLRetriesWithDifferentURL(c *gc.C) {
  1334  	sch := s.AddConfigCharm(c, "wordpress", emptyConfig, 2)
  1335  
  1336  	defer state.SetTestHooks(c, s.State,
  1337  		jujutxn.TestHook{
  1338  			Before: func() {
  1339  				// Set a different charm to force a retry: first on
  1340  				// the application, so the settings are created, then on
  1341  				// the unit.
  1342  				cfg := state.SetCharmConfig{Charm: sch, CharmOrigin: defaultCharmOrigin(sch.URL())}
  1343  				err := s.application.SetCharm(cfg)
  1344  				c.Assert(err, jc.ErrorIsNil)
  1345  				err = s.unit.SetCharmURL(sch.URL())
  1346  				c.Assert(err, jc.ErrorIsNil)
  1347  			},
  1348  			After: func() {
  1349  				// Set back the same charm on the application, so the
  1350  				// settings refcount is correct..
  1351  				cfg := state.SetCharmConfig{Charm: s.charm, CharmOrigin: defaultCharmOrigin(s.charm.URL())}
  1352  				err := s.application.SetCharm(cfg)
  1353  				c.Assert(err, jc.ErrorIsNil)
  1354  			},
  1355  		},
  1356  		jujutxn.TestHook{
  1357  			Before: nil, // Ensure there will be a retry.
  1358  			After: func() {
  1359  				// Verify it worked after the second attempt.
  1360  				err := s.unit.Refresh()
  1361  				c.Assert(err, jc.ErrorIsNil)
  1362  				currentURL := s.unit.CharmURL()
  1363  				c.Assert(currentURL, gc.NotNil)
  1364  				c.Assert(*currentURL, gc.Equals, s.charm.URL())
  1365  			},
  1366  		},
  1367  	).Check()
  1368  
  1369  	err := s.unit.SetCharmURL(s.charm.URL())
  1370  	c.Assert(err, jc.ErrorIsNil)
  1371  }
  1372  
  1373  func (s *UnitSuite) TestDestroySetStatusRetry(c *gc.C) {
  1374  	defer state.SetRetryHooks(c, s.State, func() {
  1375  		err := s.unit.AssignToNewMachine()
  1376  		c.Assert(err, jc.ErrorIsNil)
  1377  		now := coretesting.NonZeroTime()
  1378  		sInfo := status.StatusInfo{
  1379  			Status:  status.Idle,
  1380  			Message: "",
  1381  			Since:   &now,
  1382  		}
  1383  		err = s.unit.SetAgentStatus(sInfo)
  1384  		c.Assert(err, jc.ErrorIsNil)
  1385  	}, func() {
  1386  		assertLife(c, s.unit, state.Dying)
  1387  	}).Check()
  1388  
  1389  	err := s.unit.Destroy()
  1390  	c.Assert(err, jc.ErrorIsNil)
  1391  }
  1392  
  1393  func (s *UnitSuite) TestDestroySetCharmRetry(c *gc.C) {
  1394  	defer state.SetRetryHooks(c, s.State, func() {
  1395  		err := s.unit.SetCharmURL(s.charm.URL())
  1396  		c.Assert(err, jc.ErrorIsNil)
  1397  	}, func() {
  1398  		assertRemoved(c, s.unit)
  1399  	}).Check()
  1400  
  1401  	err := s.unit.Destroy()
  1402  	c.Assert(err, jc.ErrorIsNil)
  1403  }
  1404  
  1405  func (s *UnitSuite) TestDestroyChangeCharmRetry(c *gc.C) {
  1406  	err := s.unit.SetCharmURL(s.charm.URL())
  1407  	c.Assert(err, jc.ErrorIsNil)
  1408  	newCharm := s.AddConfigCharm(c, "mysql", "options: {}", 99)
  1409  	cfg := state.SetCharmConfig{Charm: newCharm, CharmOrigin: defaultCharmOrigin(newCharm.URL())}
  1410  	err = s.application.SetCharm(cfg)
  1411  	c.Assert(err, jc.ErrorIsNil)
  1412  
  1413  	defer state.SetRetryHooks(c, s.State, func() {
  1414  		err := s.unit.SetCharmURL(newCharm.URL())
  1415  		c.Assert(err, jc.ErrorIsNil)
  1416  	}, func() {
  1417  		assertRemoved(c, s.unit)
  1418  	}).Check()
  1419  
  1420  	err = s.unit.Destroy()
  1421  	c.Assert(err, jc.ErrorIsNil)
  1422  }
  1423  
  1424  func (s *UnitSuite) TestDestroyAssignRetry(c *gc.C) {
  1425  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
  1426  	c.Assert(err, jc.ErrorIsNil)
  1427  	err = machine.SetProvisioned("inst-id", "", "fake_nonce", nil)
  1428  	c.Assert(err, jc.ErrorIsNil)
  1429  
  1430  	defer state.SetRetryHooks(c, s.State, func() {
  1431  		err := s.unit.AssignToMachine(machine)
  1432  		c.Assert(err, jc.ErrorIsNil)
  1433  	}, func() {
  1434  		assertRemoved(c, s.unit)
  1435  		// Also check the unit ref was properly removed from the machine doc --
  1436  		// if it weren't, we wouldn't be able to make the machine Dead.
  1437  		err := machine.EnsureDead()
  1438  		c.Assert(err, jc.ErrorIsNil)
  1439  	}).Check()
  1440  
  1441  	err = s.unit.Destroy()
  1442  	c.Assert(err, jc.ErrorIsNil)
  1443  }
  1444  
  1445  func (s *UnitSuite) TestDestroyUnassignRetry(c *gc.C) {
  1446  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
  1447  	c.Assert(err, jc.ErrorIsNil)
  1448  	err = s.unit.AssignToMachine(machine)
  1449  	c.Assert(err, jc.ErrorIsNil)
  1450  
  1451  	defer state.SetRetryHooks(c, s.State, func() {
  1452  		err := s.unit.UnassignFromMachine()
  1453  		c.Assert(err, jc.ErrorIsNil)
  1454  	}, func() {
  1455  		assertRemoved(c, s.unit)
  1456  	}).Check()
  1457  
  1458  	err = s.unit.Destroy()
  1459  	c.Assert(err, jc.ErrorIsNil)
  1460  }
  1461  
  1462  func (s *UnitSuite) TestDestroyAssignErrorRetry(c *gc.C) {
  1463  	now := coretesting.NonZeroTime()
  1464  	sInfo := status.StatusInfo{
  1465  		Status:  status.Error,
  1466  		Message: "failed to assign",
  1467  		Since:   &now,
  1468  	}
  1469  	err := s.unit.SetAgentStatus(sInfo)
  1470  	c.Assert(err, jc.ErrorIsNil)
  1471  	_, err = s.unit.AssignedMachineId()
  1472  	c.Assert(err, jc.Satisfies, errors.IsNotAssigned)
  1473  
  1474  	defer state.SetRetryHooks(c, s.State, func() {
  1475  		err := s.unit.AssignToNewMachine()
  1476  		c.Assert(err, jc.ErrorIsNil)
  1477  		now := coretesting.NonZeroTime()
  1478  		sInfo := status.StatusInfo{
  1479  			Status:  status.Idle,
  1480  			Message: "",
  1481  			Since:   &now,
  1482  		}
  1483  		err = s.unit.SetAgentStatus(sInfo)
  1484  		c.Assert(err, jc.ErrorIsNil)
  1485  	}, func() {
  1486  		assertLife(c, s.unit, state.Dying)
  1487  	}).Check()
  1488  	err = s.unit.Destroy()
  1489  	c.Assert(err, jc.ErrorIsNil)
  1490  }
  1491  
  1492  func (s *UnitSuite) TestShortCircuitDestroyUnit(c *gc.C) {
  1493  	// A unit that has not set any status is removed directly.
  1494  	err := s.unit.Destroy()
  1495  	c.Assert(err, jc.ErrorIsNil)
  1496  	c.Assert(s.unit.Life(), gc.Equals, state.Dying)
  1497  	assertRemoved(c, s.unit)
  1498  }
  1499  
  1500  func (s *UnitSuite) TestShortCircuitDestroyUnitNotAssigned(c *gc.C) {
  1501  	// A unit that has not been assigned is removed directly.
  1502  	now := coretesting.NonZeroTime()
  1503  	err := s.unit.SetAgentStatus(status.StatusInfo{
  1504  		Status:  status.Error,
  1505  		Message: "cannot assign",
  1506  		Since:   &now,
  1507  	})
  1508  	c.Assert(err, jc.ErrorIsNil)
  1509  	err = s.unit.Destroy()
  1510  	c.Assert(err, jc.ErrorIsNil)
  1511  	c.Assert(s.unit.Life(), gc.Equals, state.Dying)
  1512  	assertRemoved(c, s.unit)
  1513  }
  1514  
  1515  func (s *UnitSuite) TestCannotShortCircuitDestroyAssignedUnit(c *gc.C) {
  1516  	// This test is similar to TestShortCircuitDestroyUnitNotAssigned but
  1517  	// the unit is assigned to a machine.
  1518  	err := s.unit.AssignToNewMachine()
  1519  	c.Assert(err, jc.ErrorIsNil)
  1520  	now := coretesting.NonZeroTime()
  1521  	err = s.unit.SetAgentStatus(status.StatusInfo{
  1522  		Status:  status.Error,
  1523  		Message: "some error",
  1524  		Since:   &now,
  1525  	})
  1526  	c.Assert(err, jc.ErrorIsNil)
  1527  	err = s.unit.Destroy()
  1528  	c.Assert(err, jc.ErrorIsNil)
  1529  	c.Assert(s.unit.Life(), gc.Equals, state.Dying)
  1530  	assertLife(c, s.unit, state.Dying)
  1531  }
  1532  
  1533  func (s *UnitSuite) TestCannotShortCircuitDestroyWithSubordinates(c *gc.C) {
  1534  	// A unit with subordinates is just set to Dying.
  1535  	s.AddTestingApplication(c, "logging", s.AddTestingCharm(c, "logging"))
  1536  	eps, err := s.State.InferEndpoints("logging", "wordpress")
  1537  	c.Assert(err, jc.ErrorIsNil)
  1538  	err = s.unit.AssignToNewMachine()
  1539  	c.Assert(err, jc.ErrorIsNil)
  1540  	rel, err := s.State.AddRelation(eps...)
  1541  	c.Assert(err, jc.ErrorIsNil)
  1542  	ru, err := rel.Unit(s.unit)
  1543  	c.Assert(err, jc.ErrorIsNil)
  1544  	err = ru.EnterScope(nil)
  1545  	c.Assert(err, jc.ErrorIsNil)
  1546  	err = s.unit.Destroy()
  1547  	c.Assert(err, jc.ErrorIsNil)
  1548  	c.Assert(s.unit.Life(), gc.Equals, state.Dying)
  1549  	assertLife(c, s.unit, state.Dying)
  1550  }
  1551  
  1552  func (s *UnitSuite) TestCannotShortCircuitDestroyWithAgentStatus(c *gc.C) {
  1553  	for i, test := range []struct {
  1554  		status status.Status
  1555  		info   string
  1556  	}{{
  1557  		status.Executing, "blah",
  1558  	}, {
  1559  		status.Idle, "blah",
  1560  	}, {
  1561  		status.Failed, "blah",
  1562  	}, {
  1563  		status.Rebooting, "blah",
  1564  	}} {
  1565  		c.Logf("test %d: %s", i, test.status)
  1566  		unit, err := s.application.AddUnit(state.AddUnitParams{})
  1567  		c.Assert(err, jc.ErrorIsNil)
  1568  		err = unit.AssignToNewMachine()
  1569  		c.Assert(err, jc.ErrorIsNil)
  1570  		now := coretesting.NonZeroTime()
  1571  		sInfo := status.StatusInfo{
  1572  			Status:  test.status,
  1573  			Message: test.info,
  1574  			Since:   &now,
  1575  		}
  1576  		err = unit.SetAgentStatus(sInfo)
  1577  		c.Assert(err, jc.ErrorIsNil)
  1578  		err = unit.Destroy()
  1579  		c.Assert(err, jc.ErrorIsNil)
  1580  		c.Assert(unit.Life(), gc.Equals, state.Dying)
  1581  		assertLife(c, unit, state.Dying)
  1582  	}
  1583  }
  1584  
  1585  func (s *UnitSuite) TestShortCircuitDestroyWithProvisionedMachine(c *gc.C) {
  1586  	// A unit assigned to a provisioned machine is still removed directly so
  1587  	// long as it has not set status.
  1588  	err := s.unit.AssignToNewMachine()
  1589  	c.Assert(err, jc.ErrorIsNil)
  1590  	mid, err := s.unit.AssignedMachineId()
  1591  	c.Assert(err, jc.ErrorIsNil)
  1592  	machine, err := s.State.Machine(mid)
  1593  	c.Assert(err, jc.ErrorIsNil)
  1594  	err = machine.SetProvisioned("i-malive", "", "fake_nonce", nil)
  1595  	c.Assert(err, jc.ErrorIsNil)
  1596  	err = s.unit.Destroy()
  1597  	c.Assert(err, jc.ErrorIsNil)
  1598  	c.Assert(s.unit.Life(), gc.Equals, state.Dying)
  1599  	assertRemoved(c, s.unit)
  1600  }
  1601  
  1602  func (s *UnitSuite) TestDestroyRemovesStatusHistory(c *gc.C) {
  1603  	err := s.unit.AssignToNewMachine()
  1604  	c.Assert(err, jc.ErrorIsNil)
  1605  	now := coretesting.NonZeroTime()
  1606  	for i := 0; i < 10; i++ {
  1607  		info := status.StatusInfo{
  1608  			Status:  status.Executing,
  1609  			Message: fmt.Sprintf("status %d", i),
  1610  			Since:   &now,
  1611  		}
  1612  		err := s.unit.SetAgentStatus(info)
  1613  		c.Assert(err, jc.ErrorIsNil)
  1614  		info.Status = status.Active
  1615  		err = s.unit.SetStatus(info)
  1616  		c.Assert(err, jc.ErrorIsNil)
  1617  
  1618  		err = s.unit.SetWorkloadVersion(fmt.Sprintf("v.%d", i))
  1619  		c.Assert(err, jc.ErrorIsNil)
  1620  	}
  1621  
  1622  	filter := status.StatusHistoryFilter{Size: 100}
  1623  	agentInfo, err := s.unit.AgentHistory().StatusHistory(filter)
  1624  	c.Assert(err, jc.ErrorIsNil)
  1625  	c.Assert(len(agentInfo), jc.GreaterThan, 9)
  1626  
  1627  	workloadInfo, err := s.unit.StatusHistory(filter)
  1628  	c.Assert(err, jc.ErrorIsNil)
  1629  	c.Assert(len(workloadInfo), jc.GreaterThan, 9)
  1630  
  1631  	versionInfo, err := s.unit.WorkloadVersionHistory().StatusHistory(filter)
  1632  	c.Assert(err, jc.ErrorIsNil)
  1633  	c.Assert(len(versionInfo), jc.GreaterThan, 9)
  1634  
  1635  	err = s.unit.Destroy()
  1636  	c.Assert(err, jc.ErrorIsNil)
  1637  
  1638  	agentInfo, err = s.unit.AgentHistory().StatusHistory(filter)
  1639  	c.Assert(err, jc.ErrorIsNil)
  1640  	c.Assert(agentInfo, gc.HasLen, 0)
  1641  
  1642  	workloadInfo, err = s.unit.StatusHistory(filter)
  1643  	c.Assert(err, jc.ErrorIsNil)
  1644  	c.Assert(workloadInfo, gc.HasLen, 0)
  1645  
  1646  	versionInfo, err = s.unit.WorkloadVersionHistory().StatusHistory(filter)
  1647  	c.Assert(err, jc.ErrorIsNil)
  1648  	c.Assert(versionInfo, gc.HasLen, 0)
  1649  }
  1650  
  1651  func assertLife(c *gc.C, entity state.Living, life state.Life) {
  1652  	c.Assert(entity.Refresh(), gc.IsNil)
  1653  	c.Assert(entity.Life(), gc.Equals, life)
  1654  }
  1655  
  1656  func assertRemoved(c *gc.C, entity state.Living) {
  1657  	c.Assert(entity.Refresh(), jc.Satisfies, errors.IsNotFound)
  1658  	c.Assert(entity.Destroy(), jc.ErrorIsNil)
  1659  	if entity, ok := entity.(state.AgentLiving); ok {
  1660  		c.Assert(entity.EnsureDead(), jc.ErrorIsNil)
  1661  		if err := entity.Remove(); err != nil {
  1662  			c.Assert(err, gc.ErrorMatches, ".*already removed.*")
  1663  		}
  1664  		err := entity.Refresh()
  1665  		c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1666  	}
  1667  }
  1668  
  1669  func (s *UnitSuite) TestUnitsInError(c *gc.C) {
  1670  	now := coretesting.NonZeroTime()
  1671  	err := s.unit.SetAgentStatus(status.StatusInfo{
  1672  		Status:  status.Error,
  1673  		Message: "some error",
  1674  		Since:   &now,
  1675  	})
  1676  	c.Assert(err, jc.ErrorIsNil)
  1677  
  1678  	// Set a non unit error to ensure it's ignored.
  1679  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
  1680  	c.Assert(err, jc.ErrorIsNil)
  1681  	err = machine.SetStatus(status.StatusInfo{
  1682  		Status:  status.Error,
  1683  		Message: "some machine error",
  1684  		Since:   &now,
  1685  	})
  1686  	c.Assert(err, jc.ErrorIsNil)
  1687  
  1688  	// Add a unit not in error to ensure it's ignored.
  1689  	another, err := s.application.AddUnit(state.AddUnitParams{})
  1690  	c.Assert(err, jc.ErrorIsNil)
  1691  	err = another.SetAgentStatus(status.StatusInfo{
  1692  		Status: status.Allocating,
  1693  		Since:  &now,
  1694  	})
  1695  	c.Assert(err, jc.ErrorIsNil)
  1696  
  1697  	units, err := s.State.UnitsInError()
  1698  	c.Assert(err, jc.ErrorIsNil)
  1699  	c.Assert(units, gc.HasLen, 1)
  1700  	c.Assert(units[0], jc.DeepEquals, s.unit)
  1701  }
  1702  
  1703  func (s *UnitSuite) TestTag(c *gc.C) {
  1704  	c.Assert(s.unit.Tag().String(), gc.Equals, "unit-wordpress-0")
  1705  }
  1706  
  1707  func (s *UnitSuite) TestSetPassword(c *gc.C) {
  1708  	preventUnitDestroyRemove(c, s.unit)
  1709  	testSetPassword(c, func() (state.Authenticator, error) {
  1710  		return s.State.Unit(s.unit.Name())
  1711  	})
  1712  }
  1713  
  1714  func (s *UnitSuite) TestResolve(c *gc.C) {
  1715  	err := s.unit.Resolve(true)
  1716  	c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" is not in an error state`)
  1717  	err = s.unit.Resolve(false)
  1718  	c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" is not in an error state`)
  1719  
  1720  	now := coretesting.NonZeroTime()
  1721  	sInfo := status.StatusInfo{
  1722  		Status:  status.Error,
  1723  		Message: "gaaah",
  1724  		Since:   &now,
  1725  	}
  1726  	err = s.unit.SetAgentStatus(sInfo)
  1727  	c.Assert(err, jc.ErrorIsNil)
  1728  	err = s.unit.Resolve(true)
  1729  	c.Assert(err, jc.ErrorIsNil)
  1730  	err = s.unit.Resolve(false)
  1731  	c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": already resolved`)
  1732  	c.Assert(s.unit.Resolved(), gc.Equals, state.ResolvedRetryHooks)
  1733  
  1734  	err = s.unit.ClearResolved()
  1735  	c.Assert(err, jc.ErrorIsNil)
  1736  	err = s.unit.Resolve(false)
  1737  	c.Assert(err, jc.ErrorIsNil)
  1738  	err = s.unit.Resolve(true)
  1739  	c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": already resolved`)
  1740  	c.Assert(s.unit.Resolved(), gc.Equals, state.ResolvedNoHooks)
  1741  }
  1742  
  1743  func (s *UnitSuite) TestGetSetClearResolved(c *gc.C) {
  1744  	mode := s.unit.Resolved()
  1745  	c.Assert(mode, gc.Equals, state.ResolvedNone)
  1746  
  1747  	err := s.unit.SetResolved(state.ResolvedNoHooks)
  1748  	c.Assert(err, jc.ErrorIsNil)
  1749  	err = s.unit.SetResolved(state.ResolvedNoHooks)
  1750  	c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": already resolved`)
  1751  
  1752  	mode = s.unit.Resolved()
  1753  	c.Assert(mode, gc.Equals, state.ResolvedNoHooks)
  1754  	err = s.unit.Refresh()
  1755  	c.Assert(err, jc.ErrorIsNil)
  1756  	mode = s.unit.Resolved()
  1757  	c.Assert(mode, gc.Equals, state.ResolvedNoHooks)
  1758  
  1759  	err = s.unit.ClearResolved()
  1760  	c.Assert(err, jc.ErrorIsNil)
  1761  	mode = s.unit.Resolved()
  1762  	c.Assert(mode, gc.Equals, state.ResolvedNone)
  1763  	err = s.unit.Refresh()
  1764  	c.Assert(err, jc.ErrorIsNil)
  1765  	mode = s.unit.Resolved()
  1766  	c.Assert(mode, gc.Equals, state.ResolvedNone)
  1767  	err = s.unit.ClearResolved()
  1768  	c.Assert(err, jc.ErrorIsNil)
  1769  
  1770  	err = s.unit.SetResolved(state.ResolvedNone)
  1771  	c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": invalid error resolution mode: ""`)
  1772  	err = s.unit.SetResolved(state.ResolvedMode("foo"))
  1773  	c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": invalid error resolution mode: "foo"`)
  1774  }
  1775  
  1776  func (s *UnitSuite) TesOpenedPorts(c *gc.C) {
  1777  	// Accessing the port ranges for the unit should fail if it's not assigned to a machine.
  1778  	_, err := s.unit.OpenedPortRanges()
  1779  	c.Assert(errors.Cause(err), jc.Satisfies, errors.IsNotAssigned)
  1780  
  1781  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
  1782  	c.Assert(err, jc.ErrorIsNil)
  1783  	err = s.unit.AssignToMachine(machine)
  1784  	c.Assert(err, jc.ErrorIsNil)
  1785  
  1786  	// Verify no open ports before activity.
  1787  	unitPortRanges, err := s.unit.OpenedPortRanges()
  1788  	c.Assert(err, jc.ErrorIsNil)
  1789  	c.Assert(unitPortRanges.UniquePortRanges(), gc.HasLen, 0)
  1790  
  1791  	// Now open and close ports and ranges and check that they are persisted correctly
  1792  	s.assertPortRangesAfterOpenClose(c, s.unit,
  1793  		[]network.PortRange{
  1794  			network.MustParsePortRange("80/tcp"),
  1795  			network.MustParsePortRange("100-200/udp"),
  1796  		},
  1797  		nil, // close
  1798  		[]network.PortRange{
  1799  			network.MustParsePortRange("80/tcp"),
  1800  			network.MustParsePortRange("100-200/udp"),
  1801  		},
  1802  	)
  1803  
  1804  	// Open a new port (53/udp)
  1805  	s.assertPortRangesAfterOpenClose(c, s.unit,
  1806  		[]network.PortRange{
  1807  			network.MustParsePortRange("53/udp"),
  1808  		},
  1809  		nil, // close
  1810  		[]network.PortRange{
  1811  			network.MustParsePortRange("53/udp"),
  1812  			network.MustParsePortRange("80/tcp"),
  1813  			network.MustParsePortRange("100-200/udp"),
  1814  		},
  1815  	)
  1816  
  1817  	// Open same port but different protocol (53/tcp)
  1818  	s.assertPortRangesAfterOpenClose(c, s.unit,
  1819  		[]network.PortRange{
  1820  			network.MustParsePortRange("53/tcp"),
  1821  		},
  1822  		nil, // close
  1823  		[]network.PortRange{
  1824  			network.MustParsePortRange("53/tcp"),
  1825  			network.MustParsePortRange("53/udp"),
  1826  			network.MustParsePortRange("80/tcp"),
  1827  			network.MustParsePortRange("100-200/udp"),
  1828  		},
  1829  	)
  1830  
  1831  	// Close an existing port (80/tcp)
  1832  	s.assertPortRangesAfterOpenClose(c, s.unit,
  1833  		nil, // open
  1834  		[]network.PortRange{
  1835  			network.MustParsePortRange("80/tcp"),
  1836  		},
  1837  		[]network.PortRange{
  1838  			network.MustParsePortRange("53/tcp"),
  1839  			network.MustParsePortRange("53/udp"),
  1840  			network.MustParsePortRange("100-200/udp"),
  1841  		},
  1842  	)
  1843  
  1844  	// Close another existing port (100-200/udp)
  1845  	s.assertPortRangesAfterOpenClose(c, s.unit,
  1846  		nil, // open
  1847  		[]network.PortRange{
  1848  			network.MustParsePortRange("100-200/udp"),
  1849  		},
  1850  		[]network.PortRange{
  1851  			network.MustParsePortRange("53/tcp"),
  1852  			network.MustParsePortRange("53/udp"),
  1853  		},
  1854  	)
  1855  }
  1856  
  1857  func (s *UnitSuite) assertPortRangesAfterOpenClose(c *gc.C, u *state.Unit, openRanges, closeRanges, exp []network.PortRange) {
  1858  	unitPortRanges, err := u.OpenedPortRanges()
  1859  	c.Assert(err, jc.ErrorIsNil)
  1860  	for _, pr := range openRanges {
  1861  		unitPortRanges.Open(allEndpoints, pr)
  1862  	}
  1863  	for _, pr := range closeRanges {
  1864  		unitPortRanges.Close(allEndpoints, pr)
  1865  	}
  1866  	c.Assert(s.State.ApplyOperation(unitPortRanges.Changes()), jc.ErrorIsNil)
  1867  
  1868  	// Reload ranges
  1869  	unitPortRanges, err = u.OpenedPortRanges()
  1870  	c.Assert(err, jc.ErrorIsNil)
  1871  	c.Assert(unitPortRanges.UniquePortRanges(), gc.DeepEquals, exp)
  1872  }
  1873  
  1874  func (s *UnitSuite) TestOpenClosePortWhenDying(c *gc.C) {
  1875  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
  1876  	c.Assert(err, jc.ErrorIsNil)
  1877  	err = s.unit.AssignToMachine(machine)
  1878  	c.Assert(err, jc.ErrorIsNil)
  1879  
  1880  	preventUnitDestroyRemove(c, s.unit)
  1881  
  1882  	// Ensure that we use a monotonically increasing port counter to
  1883  	// avoid no-op open port operations since the same unit is used for
  1884  	// both dying and dead unit states.
  1885  	nextPort := 1337
  1886  	testWhenDying(c, s.unit, noErr, contentionErr, func() error {
  1887  		// Open a port range
  1888  		unitPortRanges, err := s.unit.OpenedPortRanges()
  1889  		if err != nil {
  1890  			return errors.Annotatef(err, "cannot open port ranges")
  1891  		}
  1892  
  1893  		toOpen := fmt.Sprintf("%d/tcp", nextPort)
  1894  		nextPort++
  1895  		unitPortRanges.Open(allEndpoints, network.MustParsePortRange(toOpen))
  1896  		if err = s.State.ApplyOperation(unitPortRanges.Changes()); err != nil {
  1897  			return errors.Annotatef(err, "cannot open port ranges")
  1898  		}
  1899  
  1900  		err = s.unit.Refresh()
  1901  		if err != nil {
  1902  			return err
  1903  		}
  1904  
  1905  		// Open another port range
  1906  		toOpen = fmt.Sprintf("%d/tcp", nextPort)
  1907  		nextPort++
  1908  		unitPortRanges, err = s.unit.OpenedPortRanges()
  1909  		if err != nil {
  1910  			return errors.Annotatef(err, "cannot open port ranges")
  1911  		}
  1912  		unitPortRanges.Open(allEndpoints, network.MustParsePortRange(toOpen))
  1913  		if err = s.State.ApplyOperation(unitPortRanges.Changes()); err != nil {
  1914  			return errors.Annotatef(err, "cannot open port ranges")
  1915  		}
  1916  		return nil
  1917  	})
  1918  }
  1919  
  1920  func (s *UnitSuite) TestRemoveLastUnitOnMachineRemovesAllPorts(c *gc.C) {
  1921  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
  1922  	c.Assert(err, jc.ErrorIsNil)
  1923  	err = s.unit.AssignToMachine(machine)
  1924  	c.Assert(err, jc.ErrorIsNil)
  1925  
  1926  	machPortRanges, err := machine.OpenedPortRanges()
  1927  	c.Assert(err, jc.ErrorIsNil)
  1928  	c.Assert(machPortRanges.UniquePortRanges(), gc.HasLen, 0)
  1929  
  1930  	state.MustOpenUnitPortRange(c, s.State, machine, s.unit.Name(), allEndpoints, network.MustParsePortRange("100-200/tcp"))
  1931  
  1932  	machPortRanges, err = machine.OpenedPortRanges()
  1933  	c.Assert(err, jc.ErrorIsNil)
  1934  	c.Assert(machPortRanges.UniquePortRanges(), gc.HasLen, 1)
  1935  
  1936  	// Now remove the unit and check again.
  1937  	err = s.unit.EnsureDead()
  1938  	c.Assert(err, jc.ErrorIsNil)
  1939  	err = s.unit.Remove()
  1940  	c.Assert(err, jc.ErrorIsNil)
  1941  	err = s.unit.Refresh()
  1942  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1943  
  1944  	// Because that was the only range open, the ports doc will be
  1945  	// removed as well.
  1946  	machPortRanges, err = machine.OpenedPortRanges()
  1947  	c.Assert(err, jc.ErrorIsNil)
  1948  	c.Assert(machPortRanges.UniquePortRanges(), gc.HasLen, 0)
  1949  }
  1950  
  1951  func (s *UnitSuite) TestRemoveUnitRemovesItsPortsOnly(c *gc.C) {
  1952  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
  1953  	c.Assert(err, jc.ErrorIsNil)
  1954  	err = machine.SetProvisioned("inst-id", "", "fake_nonce", nil)
  1955  	c.Assert(err, jc.ErrorIsNil)
  1956  	err = s.unit.AssignToMachine(machine)
  1957  	c.Assert(err, jc.ErrorIsNil)
  1958  
  1959  	otherUnit, err := s.application.AddUnit(state.AddUnitParams{})
  1960  	c.Assert(err, jc.ErrorIsNil)
  1961  	err = otherUnit.AssignToMachine(machine)
  1962  	c.Assert(err, jc.ErrorIsNil)
  1963  
  1964  	state.MustOpenUnitPortRange(c, s.State, machine, s.unit.Name(), allEndpoints, network.MustParsePortRange("100-200/tcp"))
  1965  	state.MustOpenUnitPortRange(c, s.State, machine, otherUnit.Name(), allEndpoints, network.MustParsePortRange("300-400/udp"))
  1966  
  1967  	machPortRanges, err := machine.OpenedPortRanges()
  1968  	c.Assert(err, jc.ErrorIsNil)
  1969  	c.Assert(machPortRanges.UniquePortRanges(), gc.HasLen, 2)
  1970  
  1971  	c.Assert(machPortRanges.ForUnit(s.unit.Name()).UniquePortRanges(), jc.DeepEquals, []network.PortRange{
  1972  		network.MustParsePortRange("100-200/tcp"),
  1973  	})
  1974  	c.Assert(machPortRanges.ForUnit(otherUnit.Name()).UniquePortRanges(), jc.DeepEquals, []network.PortRange{
  1975  		network.MustParsePortRange("300-400/udp"),
  1976  	})
  1977  
  1978  	// Now remove the first unit and check again.
  1979  	err = s.unit.EnsureDead()
  1980  	c.Assert(err, jc.ErrorIsNil)
  1981  	err = s.unit.Remove()
  1982  	c.Assert(err, jc.ErrorIsNil)
  1983  	err = s.unit.Refresh()
  1984  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1985  
  1986  	// Verify only otherUnit still has open ports.
  1987  	machPortRanges, err = machine.OpenedPortRanges()
  1988  	c.Assert(err, jc.ErrorIsNil)
  1989  	c.Assert(machPortRanges.UniquePortRanges(), gc.HasLen, 1)
  1990  	c.Assert(machPortRanges.ForUnit(s.unit.Name()).UniquePortRanges(), gc.HasLen, 0)
  1991  	c.Assert(machPortRanges.ForUnit(otherUnit.Name()).UniquePortRanges(), jc.DeepEquals, []network.PortRange{
  1992  		network.MustParsePortRange("300-400/udp"),
  1993  	})
  1994  }
  1995  
  1996  func (s *UnitSuite) TestRemoveUnitDeletesUnitState(c *gc.C) {
  1997  	// Create unit state document
  1998  	us := state.NewUnitState()
  1999  	us.SetCharmState(map[string]string{"speed": "ludicrous"})
  2000  	err := s.unit.SetState(us, state.UnitStateSizeLimits{})
  2001  	c.Assert(err, jc.ErrorIsNil)
  2002  
  2003  	coll := s.Session.DB("juju").C("unitstates")
  2004  	numDocs, err := coll.Count()
  2005  	c.Assert(err, jc.ErrorIsNil)
  2006  	c.Assert(numDocs, gc.Equals, 1, gc.Commentf("expected a new document for the unit state to be created"))
  2007  
  2008  	// Destroy unit; this should also purge the state doc for the unit
  2009  	err = s.unit.Destroy()
  2010  	c.Assert(err, jc.ErrorIsNil)
  2011  	err = s.unit.EnsureDead()
  2012  	c.Assert(err, jc.ErrorIsNil)
  2013  
  2014  	numDocs, err = coll.Count()
  2015  	c.Assert(err, jc.ErrorIsNil)
  2016  	c.Assert(numDocs, gc.Equals, 0, gc.Commentf("expected unit state document to be removed when the unit is destroyed"))
  2017  
  2018  	// Any attempts to read/write a unit's state when not Alive should fail
  2019  	_, err = s.unit.State()
  2020  	c.Assert(errors.IsNotFound(err), jc.IsTrue)
  2021  
  2022  	newUS := state.NewUnitState()
  2023  	newUS.SetCharmState(map[string]string{"foo": "bar"})
  2024  	err = s.unit.SetState(newUS, state.UnitStateSizeLimits{})
  2025  	c.Assert(errors.IsNotFound(err), jc.IsTrue)
  2026  }
  2027  
  2028  func (s *UnitSuite) TestDestroyAlsoDeletesSecretPermissions(c *gc.C) {
  2029  	store := state.NewSecrets(s.State)
  2030  	uri := secrets.NewURI()
  2031  	cp := state.CreateSecretParams{
  2032  		Version: 1,
  2033  		Owner:   s.application.Tag(),
  2034  		UpdateSecretParams: state.UpdateSecretParams{
  2035  			LeaderToken: &fakeToken{},
  2036  			Data:        map[string]string{"foo": "bar"},
  2037  		},
  2038  	}
  2039  	_, err := store.CreateSecret(uri, cp)
  2040  	c.Assert(err, jc.ErrorIsNil)
  2041  
  2042  	// Make a relation for the access scope.
  2043  	endpoint1, err := s.application.Endpoint("juju-info")
  2044  	c.Assert(err, jc.ErrorIsNil)
  2045  	application2 := s.Factory.MakeApplication(c, &factory.ApplicationParams{
  2046  		Charm: s.Factory.MakeCharm(c, &factory.CharmParams{
  2047  			Name: "logging",
  2048  		}),
  2049  	})
  2050  	endpoint2, err := application2.Endpoint("info")
  2051  	c.Assert(err, jc.ErrorIsNil)
  2052  	rel := s.Factory.MakeRelation(c, &factory.RelationParams{
  2053  		Endpoints: []state.Endpoint{endpoint1, endpoint2},
  2054  	})
  2055  
  2056  	unit := s.Factory.MakeUnit(c, nil)
  2057  	err = s.State.GrantSecretAccess(uri, state.SecretAccessParams{
  2058  		LeaderToken: &fakeToken{},
  2059  		Scope:       rel.Tag(),
  2060  		Subject:     unit.Tag(),
  2061  		Role:        secrets.RoleView,
  2062  	})
  2063  	c.Assert(err, jc.ErrorIsNil)
  2064  	access, err := s.State.SecretAccess(uri, unit.Tag())
  2065  	c.Assert(err, jc.ErrorIsNil)
  2066  	c.Assert(access, gc.Equals, secrets.RoleView)
  2067  
  2068  	err = unit.Destroy()
  2069  	c.Assert(err, jc.ErrorIsNil)
  2070  	access, err = s.State.SecretAccess(uri, unit.Tag())
  2071  	c.Assert(err, jc.ErrorIsNil)
  2072  	c.Assert(access, gc.Equals, secrets.RoleNone)
  2073  }
  2074  
  2075  func (s *UnitSuite) TestDestroyAlsoDeletesOwnedSecrets(c *gc.C) {
  2076  	store := state.NewSecrets(s.State)
  2077  	uri := secrets.NewURI()
  2078  	cp := state.CreateSecretParams{
  2079  		Version: 1,
  2080  		Owner:   s.unit.Tag(),
  2081  		UpdateSecretParams: state.UpdateSecretParams{
  2082  			LeaderToken: &fakeToken{},
  2083  			Label:       ptr("label"),
  2084  			Data:        map[string]string{"foo": "bar"},
  2085  		},
  2086  	}
  2087  	_, err := store.CreateSecret(uri, cp)
  2088  	c.Assert(err, jc.ErrorIsNil)
  2089  
  2090  	err = s.unit.Destroy()
  2091  	c.Assert(err, jc.ErrorIsNil)
  2092  	_, err = store.GetSecret(uri)
  2093  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  2094  
  2095  	// Create again, no label clash.
  2096  	s.unit, err = s.application.AddUnit(state.AddUnitParams{})
  2097  	c.Assert(err, jc.ErrorIsNil)
  2098  	cp.Owner = s.unit.Tag()
  2099  	_, err = store.CreateSecret(uri, cp)
  2100  	c.Assert(err, jc.ErrorIsNil)
  2101  }
  2102  
  2103  func (s *UnitSuite) TestDestroyAlsoDeletesConsumerInfo(c *gc.C) {
  2104  	mysql := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql"))
  2105  	_, err := mysql.AddUnit(state.AddUnitParams{})
  2106  	c.Assert(err, jc.ErrorIsNil)
  2107  
  2108  	store := state.NewSecrets(s.State)
  2109  	uri := secrets.NewURI()
  2110  	cp := state.CreateSecretParams{
  2111  		Version: 1,
  2112  		Owner:   names.NewUnitTag("mysql/0"),
  2113  		UpdateSecretParams: state.UpdateSecretParams{
  2114  			LeaderToken: &fakeToken{},
  2115  			Label:       ptr("label"),
  2116  			Data:        map[string]string{"foo": "bar"},
  2117  		},
  2118  	}
  2119  	_, err = store.CreateSecret(uri, cp)
  2120  	c.Assert(err, jc.ErrorIsNil)
  2121  
  2122  	err = s.State.SaveSecretConsumer(uri, s.unit.UnitTag(), &secrets.SecretConsumerMetadata{CurrentRevision: 666})
  2123  	c.Assert(err, jc.ErrorIsNil)
  2124  
  2125  	err = s.unit.Destroy()
  2126  	c.Assert(err, jc.ErrorIsNil)
  2127  	_, err = s.State.GetSecretConsumer(uri, s.unit.Tag())
  2128  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  2129  }
  2130  
  2131  func (s *UnitSuite) TestSetClearResolvedWhenNotAlive(c *gc.C) {
  2132  	preventUnitDestroyRemove(c, s.unit)
  2133  	err := s.unit.Destroy()
  2134  	c.Assert(err, jc.ErrorIsNil)
  2135  	err = s.unit.SetResolved(state.ResolvedNoHooks)
  2136  	c.Assert(err, jc.ErrorIsNil)
  2137  	err = s.unit.Refresh()
  2138  	c.Assert(err, jc.ErrorIsNil)
  2139  	c.Assert(s.unit.Resolved(), gc.Equals, state.ResolvedNoHooks)
  2140  	err = s.unit.ClearResolved()
  2141  	c.Assert(err, jc.ErrorIsNil)
  2142  
  2143  	err = s.unit.EnsureDead()
  2144  	c.Assert(err, jc.ErrorIsNil)
  2145  	err = s.unit.SetResolved(state.ResolvedRetryHooks)
  2146  	c.Assert(err, gc.ErrorMatches, deadErr)
  2147  	err = s.unit.ClearResolved()
  2148  	c.Assert(err, jc.ErrorIsNil)
  2149  }
  2150  
  2151  func (s *UnitSuite) TestSubordinateChangeInPrincipal(c *gc.C) {
  2152  	subCharm := s.AddTestingCharm(c, "logging")
  2153  	for i := 0; i < 2; i++ {
  2154  		// Note: subordinate units can only be created as a side effect of a
  2155  		// principal entering scope; and a given principal can only have a
  2156  		// single subordinate unit of each application.
  2157  		name := "logging" + strconv.Itoa(i)
  2158  		s.AddTestingApplication(c, name, subCharm)
  2159  		eps, err := s.State.InferEndpoints(name, "wordpress")
  2160  		c.Assert(err, jc.ErrorIsNil)
  2161  		rel, err := s.State.AddRelation(eps...)
  2162  		c.Assert(err, jc.ErrorIsNil)
  2163  		ru, err := rel.Unit(s.unit)
  2164  		c.Assert(err, jc.ErrorIsNil)
  2165  		err = ru.EnterScope(nil)
  2166  		c.Assert(err, jc.ErrorIsNil)
  2167  	}
  2168  
  2169  	err := s.unit.Refresh()
  2170  	c.Assert(err, jc.ErrorIsNil)
  2171  	subordinates := s.unit.SubordinateNames()
  2172  	c.Assert(subordinates, gc.DeepEquals, []string{"logging0/0", "logging1/0"})
  2173  
  2174  	su1, err := s.State.Unit("logging1/0")
  2175  	c.Assert(err, jc.ErrorIsNil)
  2176  	err = su1.EnsureDead()
  2177  	c.Assert(err, jc.ErrorIsNil)
  2178  	err = su1.Remove()
  2179  	c.Assert(err, jc.ErrorIsNil)
  2180  	err = s.unit.Refresh()
  2181  	c.Assert(err, jc.ErrorIsNil)
  2182  	subordinates = s.unit.SubordinateNames()
  2183  	c.Assert(subordinates, gc.DeepEquals, []string{"logging0/0"})
  2184  }
  2185  
  2186  func (s *UnitSuite) TestDeathWithSubordinates(c *gc.C) {
  2187  	// Check that units can become dead when they've never had subordinates.
  2188  	u, err := s.application.AddUnit(state.AddUnitParams{})
  2189  	c.Assert(err, jc.ErrorIsNil)
  2190  	err = u.EnsureDead()
  2191  	c.Assert(err, jc.ErrorIsNil)
  2192  
  2193  	// Create a new unit and add a subordinate.
  2194  	u, err = s.application.AddUnit(state.AddUnitParams{})
  2195  	c.Assert(err, jc.ErrorIsNil)
  2196  	s.AddTestingApplication(c, "logging", s.AddTestingCharm(c, "logging"))
  2197  	c.Assert(err, jc.ErrorIsNil)
  2198  	eps, err := s.State.InferEndpoints("logging", "wordpress")
  2199  	c.Assert(err, jc.ErrorIsNil)
  2200  	rel, err := s.State.AddRelation(eps...)
  2201  	c.Assert(err, jc.ErrorIsNil)
  2202  	ru, err := rel.Unit(u)
  2203  	c.Assert(err, jc.ErrorIsNil)
  2204  	err = ru.EnterScope(nil)
  2205  	c.Assert(err, jc.ErrorIsNil)
  2206  
  2207  	// Check the unit cannot become Dead, but can become Dying...
  2208  	err = u.EnsureDead()
  2209  	c.Assert(err, gc.Equals, stateerrors.ErrUnitHasSubordinates)
  2210  	err = u.Destroy()
  2211  	c.Assert(err, jc.ErrorIsNil)
  2212  
  2213  	// ...and that it still can't become Dead now it's Dying.
  2214  	err = u.EnsureDead()
  2215  	c.Assert(err, gc.Equals, stateerrors.ErrUnitHasSubordinates)
  2216  
  2217  	// Make the subordinate Dead and check the principal still cannot be removed.
  2218  	sub, err := s.State.Unit("logging/0")
  2219  	c.Assert(err, jc.ErrorIsNil)
  2220  	err = sub.EnsureDead()
  2221  	c.Assert(err, jc.ErrorIsNil)
  2222  	err = u.EnsureDead()
  2223  	c.Assert(err, gc.Equals, stateerrors.ErrUnitHasSubordinates)
  2224  
  2225  	// remove the subordinate and check the principal can finally become Dead.
  2226  	err = sub.Remove()
  2227  	c.Assert(err, jc.ErrorIsNil)
  2228  	err = u.EnsureDead()
  2229  	c.Assert(err, jc.ErrorIsNil)
  2230  }
  2231  
  2232  func (s *UnitSuite) TestPrincipalName(c *gc.C) {
  2233  	subCharm := s.AddTestingCharm(c, "logging")
  2234  	s.AddTestingApplication(c, "logging", subCharm)
  2235  	eps, err := s.State.InferEndpoints("logging", "wordpress")
  2236  	c.Assert(err, jc.ErrorIsNil)
  2237  	rel, err := s.State.AddRelation(eps...)
  2238  	c.Assert(err, jc.ErrorIsNil)
  2239  	ru, err := rel.Unit(s.unit)
  2240  	c.Assert(err, jc.ErrorIsNil)
  2241  	err = ru.EnterScope(nil)
  2242  	c.Assert(err, jc.ErrorIsNil)
  2243  
  2244  	err = s.unit.Refresh()
  2245  	c.Assert(err, jc.ErrorIsNil)
  2246  	subordinates := s.unit.SubordinateNames()
  2247  	c.Assert(subordinates, gc.DeepEquals, []string{"logging/0"})
  2248  
  2249  	su, err := s.State.Unit("logging/0")
  2250  	c.Assert(err, jc.ErrorIsNil)
  2251  	principal, valid := su.PrincipalName()
  2252  	c.Assert(valid, jc.IsTrue)
  2253  	c.Assert(principal, gc.Equals, s.unit.Name())
  2254  
  2255  	// Calling PrincipalName on a principal unit yields "", false.
  2256  	principal, valid = s.unit.PrincipalName()
  2257  	c.Assert(valid, jc.IsFalse)
  2258  	c.Assert(principal, gc.Equals, "")
  2259  }
  2260  
  2261  func (s *UnitSuite) TestConstraintsDefaultArchNotRelevant(c *gc.C) {
  2262  	app := s.Factory.MakeApplication(c, &factory.ApplicationParams{
  2263  		Name: "app",
  2264  		CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{
  2265  			Architecture: "arm64",
  2266  			OS:           "ubuntu",
  2267  			Channel:      "22.04",
  2268  		}},
  2269  	})
  2270  	err := app.SetConstraints(constraints.MustParse("instance-type=big"))
  2271  	c.Assert(err, jc.ErrorIsNil)
  2272  	unit0, err := app.AddUnit(state.AddUnitParams{})
  2273  	c.Assert(err, jc.ErrorIsNil)
  2274  	cons, err := unit0.Constraints()
  2275  	c.Assert(err, jc.ErrorIsNil)
  2276  	c.Assert(cons.String(), gc.Equals, "instance-type=big")
  2277  
  2278  	app = s.Factory.MakeApplication(c, &factory.ApplicationParams{
  2279  		Name: "app2",
  2280  		CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{
  2281  			Architecture: "arm64",
  2282  			OS:           "ubuntu",
  2283  			Channel:      "22.04",
  2284  		}},
  2285  		Constraints: constraints.MustParse("arch=s390x"),
  2286  	})
  2287  	unit1, err := app.AddUnit(state.AddUnitParams{})
  2288  	c.Assert(err, jc.ErrorIsNil)
  2289  	cons, err = unit1.Constraints()
  2290  	c.Assert(err, jc.ErrorIsNil)
  2291  	c.Assert(cons.String(), gc.Equals, "arch=s390x")
  2292  }
  2293  
  2294  func (s *UnitSuite) TestConstraintsDefaultArch(c *gc.C) {
  2295  	app := s.Factory.MakeApplication(c, &factory.ApplicationParams{
  2296  		Name: "app",
  2297  		CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{
  2298  			Architecture: "arm64",
  2299  			OS:           "ubuntu",
  2300  			Channel:      "22.04",
  2301  		}},
  2302  	})
  2303  	err := app.SetConstraints(constraints.MustParse("mem=4G"))
  2304  	c.Assert(err, jc.ErrorIsNil)
  2305  	unit0, err := app.AddUnit(state.AddUnitParams{})
  2306  	c.Assert(err, jc.ErrorIsNil)
  2307  	cons, err := unit0.Constraints()
  2308  	c.Assert(err, jc.ErrorIsNil)
  2309  	c.Assert(cons.String(), gc.Equals, "arch=arm64 mem=4096M")
  2310  }
  2311  
  2312  func (s *UnitSuite) TestRelations(c *gc.C) {
  2313  	wordpress0 := s.unit
  2314  	mysql := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql"))
  2315  	mysql0, err := mysql.AddUnit(state.AddUnitParams{})
  2316  	c.Assert(err, jc.ErrorIsNil)
  2317  	eps, err := s.State.InferEndpoints("wordpress", "mysql")
  2318  	c.Assert(err, jc.ErrorIsNil)
  2319  	rel, err := s.State.AddRelation(eps...)
  2320  	c.Assert(err, jc.ErrorIsNil)
  2321  
  2322  	assertEquals := func(actual, expect []*state.Relation) {
  2323  		c.Assert(actual, gc.HasLen, len(expect))
  2324  		for i, a := range actual {
  2325  			c.Assert(a.Id(), gc.Equals, expect[i].Id())
  2326  		}
  2327  	}
  2328  	assertRelationsJoined := func(unit *state.Unit, expect ...*state.Relation) {
  2329  		actual, err := unit.RelationsJoined()
  2330  		c.Assert(err, jc.ErrorIsNil)
  2331  		assertEquals(actual, expect)
  2332  	}
  2333  	assertRelationsInScope := func(unit *state.Unit, expect ...*state.Relation) {
  2334  		actual, err := unit.RelationsInScope()
  2335  		c.Assert(err, jc.ErrorIsNil)
  2336  		assertEquals(actual, expect)
  2337  	}
  2338  	assertRelations := func(unit *state.Unit, expect ...*state.Relation) {
  2339  		assertRelationsInScope(unit, expect...)
  2340  		assertRelationsJoined(unit, expect...)
  2341  	}
  2342  	assertRelations(wordpress0)
  2343  	assertRelations(mysql0)
  2344  
  2345  	mysql0ru, err := rel.Unit(mysql0)
  2346  	c.Assert(err, jc.ErrorIsNil)
  2347  	err = mysql0ru.EnterScope(nil)
  2348  	c.Assert(err, jc.ErrorIsNil)
  2349  	assertRelations(wordpress0)
  2350  	assertRelations(mysql0, rel)
  2351  
  2352  	wordpress0ru, err := rel.Unit(wordpress0)
  2353  	c.Assert(err, jc.ErrorIsNil)
  2354  	err = wordpress0ru.EnterScope(nil)
  2355  	c.Assert(err, jc.ErrorIsNil)
  2356  	assertRelations(wordpress0, rel)
  2357  	assertRelations(mysql0, rel)
  2358  
  2359  	err = mysql0ru.PrepareLeaveScope()
  2360  	c.Assert(err, jc.ErrorIsNil)
  2361  	assertRelations(wordpress0, rel)
  2362  	assertRelationsInScope(mysql0, rel)
  2363  	assertRelationsJoined(mysql0)
  2364  }
  2365  
  2366  func (s *UnitSuite) TestRemove(c *gc.C) {
  2367  	err := s.unit.Remove()
  2368  	c.Assert(err, gc.ErrorMatches, `cannot remove unit "wordpress/0": unit is not dead`)
  2369  	err = s.unit.EnsureDead()
  2370  	c.Assert(err, jc.ErrorIsNil)
  2371  	err = s.unit.Remove()
  2372  	c.Assert(err, jc.ErrorIsNil)
  2373  	err = s.unit.Refresh()
  2374  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  2375  	units, err := s.application.AllUnits()
  2376  	c.Assert(err, jc.ErrorIsNil)
  2377  	c.Assert(units, gc.HasLen, 0)
  2378  	err = s.unit.Remove()
  2379  	c.Assert(err, jc.ErrorIsNil)
  2380  }
  2381  
  2382  func (s *UnitSuite) TestRemoveUnassignsFromBranch(c *gc.C) {
  2383  	// Add unit to a branch
  2384  	c.Assert(s.Model.AddBranch("apple", "testuser"), jc.ErrorIsNil)
  2385  	branch, err := s.Model.Branch("apple")
  2386  	c.Assert(err, jc.ErrorIsNil)
  2387  	c.Assert(branch.AssignUnit(s.unit.Name()), jc.ErrorIsNil)
  2388  	c.Assert(branch.Refresh(), jc.ErrorIsNil)
  2389  	c.Assert(branch.AssignedUnits(), gc.DeepEquals, map[string][]string{
  2390  		s.application.Name(): {s.unit.Name()},
  2391  	})
  2392  
  2393  	// remove the unit
  2394  	c.Assert(s.unit.EnsureDead(), jc.ErrorIsNil)
  2395  	c.Assert(s.unit.Remove(), jc.ErrorIsNil)
  2396  
  2397  	// verify branch no longer tracks unit
  2398  	c.Assert(branch.Refresh(), jc.ErrorIsNil)
  2399  	c.Assert(branch.AssignedUnits(), gc.DeepEquals, map[string][]string{
  2400  		s.application.Name(): {},
  2401  	})
  2402  }
  2403  
  2404  func (s *UnitSuite) TestRemovePathological(c *gc.C) {
  2405  	// Add a relation between wordpress and mysql...
  2406  	wordpress := s.application
  2407  	wordpress0 := s.unit
  2408  	mysql := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql"))
  2409  	eps, err := s.State.InferEndpoints("wordpress", "mysql")
  2410  	c.Assert(err, jc.ErrorIsNil)
  2411  	rel, err := s.State.AddRelation(eps...)
  2412  	c.Assert(err, jc.ErrorIsNil)
  2413  
  2414  	// The relation holds a reference to wordpress, but that can't keep
  2415  	// wordpress from being removed -- because the relation will be removed
  2416  	// if we destroy wordpress.
  2417  	// However, if a unit of the *other* application joins the relation, that
  2418  	// will add an additional reference and prevent the relation -- and
  2419  	// thus wordpress itself -- from being removed when its last unit is.
  2420  	mysql0, err := mysql.AddUnit(state.AddUnitParams{})
  2421  	c.Assert(err, jc.ErrorIsNil)
  2422  	mysql0ru, err := rel.Unit(mysql0)
  2423  	c.Assert(err, jc.ErrorIsNil)
  2424  	c.Assert(mysql0ru.EnterScope(nil), jc.ErrorIsNil)
  2425  
  2426  	// Destroy wordpress, and remove its last unit.
  2427  	c.Assert(wordpress.Destroy(), jc.ErrorIsNil)
  2428  	c.Assert(wordpress0.EnsureDead(), jc.ErrorIsNil)
  2429  	c.Assert(wordpress0.Remove(), jc.ErrorIsNil)
  2430  
  2431  	// Check this didn't kill the application or relation yet...
  2432  	c.Assert(wordpress.Refresh(), jc.ErrorIsNil)
  2433  	c.Assert(rel.Refresh(), jc.ErrorIsNil)
  2434  
  2435  	// ...but when the unit on the other side departs the relation, the
  2436  	// relation and the other application are cleaned up.
  2437  	c.Assert(mysql0ru.LeaveScope(), jc.ErrorIsNil)
  2438  	c.Assert(s.State.Cleanup(), jc.ErrorIsNil)
  2439  	c.Assert(wordpress.Refresh(), jc.Satisfies, errors.IsNotFound)
  2440  	c.Assert(rel.Refresh(), jc.Satisfies, errors.IsNotFound)
  2441  }
  2442  
  2443  func (s *UnitSuite) TestRemovePathologicalWithBuggyUniter(c *gc.C) {
  2444  	// Add a relation between wordpress and mysql...
  2445  	wordpress := s.application
  2446  	wordpress0 := s.unit
  2447  	mysql := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql"))
  2448  	eps, err := s.State.InferEndpoints("wordpress", "mysql")
  2449  	c.Assert(err, jc.ErrorIsNil)
  2450  	rel, err := s.State.AddRelation(eps...)
  2451  	c.Assert(err, jc.ErrorIsNil)
  2452  
  2453  	// The relation holds a reference to wordpress, but that can't keep
  2454  	// wordpress from being removed -- because the relation will be removed
  2455  	// if we destroy wordpress.
  2456  	// However, if a unit of the *other* application joins the relation, that
  2457  	// will add an additional reference and prevent the relation -- and
  2458  	// thus wordpress itself -- from being removed when its last unit is.
  2459  	mysql0, err := mysql.AddUnit(state.AddUnitParams{})
  2460  	c.Assert(err, jc.ErrorIsNil)
  2461  	mysql0ru, err := rel.Unit(mysql0)
  2462  	c.Assert(err, jc.ErrorIsNil)
  2463  	err = mysql0ru.EnterScope(nil)
  2464  	c.Assert(err, jc.ErrorIsNil)
  2465  
  2466  	// Destroy wordpress, and remove its last unit.
  2467  	c.Assert(wordpress.Destroy(), jc.ErrorIsNil)
  2468  	c.Assert(wordpress0.EnsureDead(), jc.ErrorIsNil)
  2469  	c.Assert(wordpress0.Remove(), jc.ErrorIsNil)
  2470  
  2471  	// Check this didn't kill the application or relation yet...
  2472  	c.Assert(wordpress.Refresh(), jc.ErrorIsNil)
  2473  	c.Assert(rel.Refresh(), jc.ErrorIsNil)
  2474  
  2475  	// ...and that when the malfunctioning unit agent on the other side
  2476  	// sets itself to dead *without* departing the relation, the unit's
  2477  	// removal causes the relation and the other application to be cleaned up.
  2478  	c.Assert(mysql0.EnsureDead(), jc.ErrorIsNil)
  2479  	c.Assert(mysql0.Remove(), jc.ErrorIsNil)
  2480  	c.Assert(s.State.Cleanup(), jc.ErrorIsNil)
  2481  	c.Assert(wordpress.Refresh(), jc.Satisfies, errors.IsNotFound)
  2482  	c.Assert(rel.Refresh(), jc.Satisfies, errors.IsNotFound)
  2483  }
  2484  
  2485  func (s *UnitSuite) TestWatchSubordinates(c *gc.C) {
  2486  	// TODO(mjs) - ModelUUID - test with multiple models with
  2487  	// identically named units and ensure there's no leakage.
  2488  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
  2489  	w := s.unit.WatchSubordinateUnits()
  2490  	defer testing.AssertStop(c, w)
  2491  	wc := testing.NewStringsWatcherC(c, w)
  2492  	wc.AssertChange()
  2493  	wc.AssertNoChange()
  2494  
  2495  	// Add a couple of subordinates, check change.
  2496  	subCharm := s.AddTestingCharm(c, "logging")
  2497  	var subUnits []*state.Unit
  2498  	for i := 0; i < 2; i++ {
  2499  		// Note: subordinate units can only be created as a side effect of a
  2500  		// principal entering scope; and a given principal can only have a
  2501  		// single subordinate unit of each application.
  2502  		name := "logging" + strconv.Itoa(i)
  2503  		subApp := s.AddTestingApplication(c, name, subCharm)
  2504  		eps, err := s.State.InferEndpoints(name, "wordpress")
  2505  		c.Assert(err, jc.ErrorIsNil)
  2506  		rel, err := s.State.AddRelation(eps...)
  2507  		c.Assert(err, jc.ErrorIsNil)
  2508  		ru, err := rel.Unit(s.unit)
  2509  		c.Assert(err, jc.ErrorIsNil)
  2510  		err = ru.EnterScope(nil)
  2511  		c.Assert(err, jc.ErrorIsNil)
  2512  		units, err := subApp.AllUnits()
  2513  		c.Assert(err, jc.ErrorIsNil)
  2514  		c.Assert(units, gc.HasLen, 1)
  2515  		subUnits = append(subUnits, units[0])
  2516  	}
  2517  	// In order to ensure that both the subordinate creation events
  2518  	// have all been processed, wait for the model to be idle.
  2519  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
  2520  	wc.AssertChange(subUnits[0].Name(), subUnits[1].Name())
  2521  	wc.AssertNoChange()
  2522  
  2523  	// Set one to Dying, check change.
  2524  	err := subUnits[0].Destroy()
  2525  	c.Assert(err, jc.ErrorIsNil)
  2526  	wc.AssertChange(subUnits[0].Name())
  2527  	wc.AssertNoChange()
  2528  
  2529  	// Set both to Dead, and remove one; check change.
  2530  	err = subUnits[0].EnsureDead()
  2531  	c.Assert(err, jc.ErrorIsNil)
  2532  	err = subUnits[1].EnsureDead()
  2533  	c.Assert(err, jc.ErrorIsNil)
  2534  	err = subUnits[1].Remove()
  2535  	c.Assert(err, jc.ErrorIsNil)
  2536  	// In order to ensure that both the dead and remove operations
  2537  	// have been processed, we need to wait until the model is idle.
  2538  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
  2539  	wc.AssertChange(subUnits[0].Name(), subUnits[1].Name())
  2540  	wc.AssertNoChange()
  2541  
  2542  	// Stop watcher, check closed.
  2543  	testing.AssertStop(c, w)
  2544  	wc.AssertClosed()
  2545  
  2546  	// Start a new watch, check Dead unit is reported.
  2547  	w = s.unit.WatchSubordinateUnits()
  2548  	defer testing.AssertStop(c, w)
  2549  	wc = testing.NewStringsWatcherC(c, w)
  2550  	wc.AssertChange(subUnits[0].Name())
  2551  	wc.AssertNoChange()
  2552  
  2553  	// Remove the leftover, check no change.
  2554  	err = subUnits[0].Remove()
  2555  	c.Assert(err, jc.ErrorIsNil)
  2556  	wc.AssertNoChange()
  2557  }
  2558  
  2559  func (s *UnitSuite) TestWatchUnits(c *gc.C) {
  2560  	loggo.GetLogger("juju.state.pool.txnwatcher").SetLogLevel(loggo.TRACE)
  2561  	loggo.GetLogger("juju.state.watcher").SetLogLevel(loggo.TRACE)
  2562  
  2563  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
  2564  	w := s.State.WatchUnits()
  2565  	defer testing.AssertStop(c, w)
  2566  
  2567  	// Initial event for unit created in test setup.
  2568  	wc := testing.NewStringsWatcherC(c, w)
  2569  	wc.AssertChange("wordpress/0")
  2570  
  2571  	u, err := s.application.AddUnit(state.AddUnitParams{})
  2572  	c.Assert(err, jc.ErrorIsNil)
  2573  	wc.AssertChange(u.Name())
  2574  	err = u.AssignToNewMachine()
  2575  	c.Assert(err, jc.ErrorIsNil)
  2576  	wc.AssertChange(u.Name())
  2577  
  2578  	err = u.EnsureDead()
  2579  	c.Assert(err, jc.ErrorIsNil)
  2580  	wc.AssertChange(u.Name())
  2581  
  2582  	// Stop, check closed.
  2583  	testing.AssertStop(c, w)
  2584  	wc.AssertClosed()
  2585  }
  2586  
  2587  func (s *UnitSuite) TestWatchUnit(c *gc.C) {
  2588  	loggo.GetLogger("juju.state.pool.txnwatcher").SetLogLevel(loggo.TRACE)
  2589  	loggo.GetLogger("juju.state.watcher").SetLogLevel(loggo.TRACE)
  2590  
  2591  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
  2592  	w := s.unit.Watch()
  2593  	defer testing.AssertStop(c, w)
  2594  
  2595  	// Initial event.
  2596  	wc := testing.NewNotifyWatcherC(c, w)
  2597  	wc.AssertOneChange()
  2598  
  2599  	// Make one change (to a separate instance), check one event.
  2600  	unit, err := s.State.Unit(s.unit.Name())
  2601  	c.Assert(err, jc.ErrorIsNil)
  2602  	s.setAssignedMachineAddresses(c, unit)
  2603  	wc.AssertOneChange()
  2604  
  2605  	// Make two changes, check one event.
  2606  	err = unit.SetPassword("arble-farble-dying-yarble")
  2607  	c.Assert(err, jc.ErrorIsNil)
  2608  	// TODO(quiescence): these two changes should be one event.
  2609  	wc.AssertOneChange()
  2610  	preventUnitDestroyRemove(c, unit)
  2611  	err = unit.Destroy()
  2612  	c.Assert(err, jc.ErrorIsNil)
  2613  	wc.AssertOneChange()
  2614  
  2615  	// Stop, check closed.
  2616  	testing.AssertStop(c, w)
  2617  	wc.AssertClosed()
  2618  
  2619  	// Remove unit, start new watch, check single event.
  2620  	err = unit.EnsureDead()
  2621  	c.Assert(err, jc.ErrorIsNil)
  2622  	err = unit.Remove()
  2623  	c.Assert(err, jc.ErrorIsNil)
  2624  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
  2625  	w = s.unit.Watch()
  2626  	defer testing.AssertStop(c, w)
  2627  	testing.NewNotifyWatcherC(c, w).AssertOneChange()
  2628  }
  2629  
  2630  func (s *UnitSuite) TestUnitAgentTools(c *gc.C) {
  2631  	preventUnitDestroyRemove(c, s.unit)
  2632  	testAgentTools(c, s.unit, `unit "wordpress/0"`)
  2633  }
  2634  
  2635  func (s *UnitSuite) TestValidActionsAndSpecs(c *gc.C) {
  2636  	basicActions := `
  2637  snapshot:
  2638    params:
  2639      outfile:
  2640        type: string
  2641        default: "abcd"
  2642  `[1:]
  2643  
  2644  	wordpress := s.AddTestingApplication(c, "wordpress-actions", s.AddActionsCharm(c, "wordpress", basicActions, 1))
  2645  	unit1, err := wordpress.AddUnit(state.AddUnitParams{})
  2646  	c.Assert(err, jc.ErrorIsNil)
  2647  	specs, err := unit1.ActionSpecs()
  2648  	c.Assert(err, jc.ErrorIsNil)
  2649  	c.Check(specs, jc.DeepEquals, state.ActionSpecsByName{
  2650  		"snapshot": charm.ActionSpec{
  2651  			Description: "No description",
  2652  			Params: map[string]interface{}{
  2653  				"type":        "object",
  2654  				"title":       "snapshot",
  2655  				"description": "No description",
  2656  				"properties": map[string]interface{}{
  2657  					"outfile": map[string]interface{}{
  2658  						"type":    "string",
  2659  						"default": "abcd",
  2660  					},
  2661  				},
  2662  			},
  2663  		},
  2664  	})
  2665  
  2666  	var tests = []struct {
  2667  		actionName      string
  2668  		errString       string
  2669  		givenPayload    map[string]interface{}
  2670  		expectedPayload map[string]interface{}
  2671  	}{
  2672  		{
  2673  			actionName:      "snapshot",
  2674  			expectedPayload: map[string]interface{}{"outfile": "abcd"},
  2675  		},
  2676  		{
  2677  			actionName: "juju-exec",
  2678  			errString:  `validation failed: \(root\) : "command" property is missing and required, given \{\}; \(root\) : "timeout" property is missing and required, given \{\}`,
  2679  		},
  2680  		{
  2681  			actionName:   "juju-exec",
  2682  			givenPayload: map[string]interface{}{"command": "allyourbasearebelongtous"},
  2683  			errString:    `validation failed: \(root\) : "timeout" property is missing and required, given \{"command":"allyourbasearebelongtous"\}`,
  2684  		},
  2685  		{
  2686  			actionName:   "juju-exec",
  2687  			givenPayload: map[string]interface{}{"timeout": 5 * time.Second},
  2688  			// Note: in Go 1.8 the representation of large numbers in JSON changed
  2689  			// to use integer rather than exponential notation, hence the pattern.
  2690  			errString: `validation failed: \(root\) : "command" property is missing and required, given \{"timeout":5.*\}`,
  2691  		},
  2692  		{
  2693  			actionName:      "juju-exec",
  2694  			givenPayload:    map[string]interface{}{"command": "allyourbasearebelongtous", "timeout": 5.0},
  2695  			expectedPayload: map[string]interface{}{"command": "allyourbasearebelongtous", "timeout": 5.0},
  2696  		},
  2697  		{
  2698  			actionName: "baiku",
  2699  			errString:  `action "baiku" not defined on unit "wordpress-actions/0"`,
  2700  		},
  2701  	}
  2702  
  2703  	for i, t := range tests {
  2704  		c.Logf("running test %d", i)
  2705  		operationID, err := s.Model.EnqueueOperation("a test", 1)
  2706  		c.Assert(err, jc.ErrorIsNil)
  2707  		action, err := s.Model.AddAction(unit1, operationID, t.actionName, t.givenPayload, nil, nil)
  2708  		if t.errString != "" {
  2709  			c.Assert(err, gc.ErrorMatches, t.errString)
  2710  		} else {
  2711  			c.Assert(err, jc.ErrorIsNil)
  2712  			c.Assert(action.Parameters(), jc.DeepEquals, t.expectedPayload)
  2713  			c.Assert(state.ActionOperationId(action), gc.Equals, operationID)
  2714  		}
  2715  	}
  2716  }
  2717  
  2718  func (s *UnitSuite) TestAddActionWithError(c *gc.C) {
  2719  	operationID, err := s.Model.EnqueueOperation("a test", 1)
  2720  	c.Assert(err, jc.ErrorIsNil)
  2721  	_, err = s.Model.AddAction(s.unit, operationID, "benchmark", nil, nil, nil)
  2722  	c.Assert(err, gc.ErrorMatches, `action "benchmark" not defined on unit "wordpress/0"`)
  2723  	op, err := s.Model.Operation(operationID)
  2724  	c.Assert(err, jc.ErrorIsNil)
  2725  	c.Assert(op.Status(), gc.Equals, state.ActionError)
  2726  }
  2727  
  2728  func (s *UnitSuite) TestUnitActionsFindsRightActions(c *gc.C) {
  2729  	// An actions.yaml which permits actions by the following names
  2730  	basicActions := `
  2731  action-a-a:
  2732  action-a-b:
  2733  action-a-c:
  2734  action-b-a:
  2735  action-b-b:
  2736  `[1:]
  2737  
  2738  	// Add simple application and two units
  2739  	dummy := s.AddTestingApplication(c, "dummy", s.AddActionsCharm(c, "dummy", basicActions, 1))
  2740  
  2741  	unit1, err := dummy.AddUnit(state.AddUnitParams{})
  2742  	c.Assert(err, jc.ErrorIsNil)
  2743  
  2744  	unit2, err := dummy.AddUnit(state.AddUnitParams{})
  2745  	c.Assert(err, jc.ErrorIsNil)
  2746  
  2747  	// Add 3 actions to first unit, and 2 to the second unit
  2748  	operationID, err := s.Model.EnqueueOperation("a test", 5)
  2749  	c.Assert(err, jc.ErrorIsNil)
  2750  	_, err = s.Model.AddAction(unit1, operationID, "action-a-a", nil, nil, nil)
  2751  	c.Assert(err, jc.ErrorIsNil)
  2752  	_, err = s.Model.AddAction(unit1, operationID, "action-a-b", nil, nil, nil)
  2753  	c.Assert(err, jc.ErrorIsNil)
  2754  	_, err = s.Model.AddAction(unit1, operationID, "action-a-c", nil, nil, nil)
  2755  	c.Assert(err, jc.ErrorIsNil)
  2756  
  2757  	_, err = s.Model.AddAction(unit2, operationID, "action-b-a", nil, nil, nil)
  2758  	c.Assert(err, jc.ErrorIsNil)
  2759  	_, err = s.Model.AddAction(unit2, operationID, "action-b-b", nil, nil, nil)
  2760  	c.Assert(err, jc.ErrorIsNil)
  2761  
  2762  	// Verify that calling Actions on unit1 returns only
  2763  	// the three actions added to unit1
  2764  	actions1, err := unit1.Actions()
  2765  	c.Assert(err, jc.ErrorIsNil)
  2766  	c.Assert(len(actions1), gc.Equals, 3)
  2767  	for _, action := range actions1 {
  2768  		c.Assert(action.Name(), gc.Matches, "^action-a-.")
  2769  	}
  2770  
  2771  	// Verify that calling Actions on unit2 returns only
  2772  	// the two actions added to unit2
  2773  	actions2, err := unit2.Actions()
  2774  	c.Assert(err, jc.ErrorIsNil)
  2775  	c.Assert(len(actions2), gc.Equals, 2)
  2776  	for _, action := range actions2 {
  2777  		c.Assert(action.Name(), gc.Matches, "^action-b-.")
  2778  	}
  2779  }
  2780  
  2781  func (s *UnitSuite) TestWorkloadVersion(c *gc.C) {
  2782  	ch := state.AddTestingCharm(c, s.State, "dummy")
  2783  	app := state.AddTestingApplication(c, s.State, "alexandrite", ch)
  2784  	unit, err := app.AddUnit(state.AddUnitParams{})
  2785  	c.Assert(err, jc.ErrorIsNil)
  2786  
  2787  	version, err := unit.WorkloadVersion()
  2788  	c.Assert(err, jc.ErrorIsNil)
  2789  	c.Check(version, gc.Equals, "")
  2790  
  2791  	err = unit.SetWorkloadVersion("3.combined")
  2792  	c.Assert(err, jc.ErrorIsNil)
  2793  	version, err = unit.WorkloadVersion()
  2794  	c.Assert(err, jc.ErrorIsNil)
  2795  	c.Check(version, gc.Equals, "3.combined")
  2796  
  2797  	regotUnit, err := s.State.Unit("alexandrite/0")
  2798  	c.Assert(err, jc.ErrorIsNil)
  2799  	version, err = regotUnit.WorkloadVersion()
  2800  	c.Assert(err, jc.ErrorIsNil)
  2801  	c.Check(version, gc.Equals, "3.combined")
  2802  }
  2803  
  2804  func (s *UnitSuite) TestDestroyWithForceWorksOnDyingUnit(c *gc.C) {
  2805  	// Ensure that a cleanup is scheduled if we force destroy a unit
  2806  	// that's already dying.
  2807  	ch := state.AddTestingCharm(c, s.State, "dummy")
  2808  	app := state.AddTestingApplication(c, s.State, "alexandrite", ch)
  2809  	unit, err := app.AddUnit(state.AddUnitParams{})
  2810  	c.Assert(err, jc.ErrorIsNil)
  2811  	// Assign the unit to a machine and set the agent status so
  2812  	// removal can't be short-circuited.
  2813  	err = s.State.AssignUnit(unit, state.AssignCleanEmpty)
  2814  	c.Assert(err, jc.ErrorIsNil)
  2815  	err = unit.SetAgentStatus(status.StatusInfo{
  2816  		Status: status.Idle,
  2817  	})
  2818  	c.Assert(err, jc.ErrorIsNil)
  2819  
  2820  	err = unit.Destroy()
  2821  	c.Assert(err, jc.ErrorIsNil)
  2822  
  2823  	c.Assert(unit.Life(), gc.Equals, state.Dying)
  2824  
  2825  	needsCleanup, err := s.State.NeedsCleanup()
  2826  	c.Assert(err, jc.ErrorIsNil)
  2827  	c.Assert(needsCleanup, gc.Equals, true)
  2828  
  2829  	err = s.State.Cleanup()
  2830  	c.Assert(err, jc.ErrorIsNil)
  2831  	needsCleanup, err = s.State.NeedsCleanup()
  2832  	c.Assert(err, jc.ErrorIsNil)
  2833  	c.Assert(needsCleanup, gc.Equals, false)
  2834  
  2835  	// Force-destroying the unit should schedule a cleanup so we get a
  2836  	// chance for the fallback force-cleanup to run.
  2837  	opErrs, err := unit.DestroyWithForce(true, dontWait)
  2838  	c.Assert(err, jc.ErrorIsNil)
  2839  	c.Assert(opErrs, gc.IsNil)
  2840  
  2841  	// We scheduled the dying unit cleanup again even though the unit was dying already.
  2842  	needsCleanup, err = s.State.NeedsCleanup()
  2843  	c.Assert(err, jc.ErrorIsNil)
  2844  	c.Assert(needsCleanup, gc.Equals, true)
  2845  }
  2846  
  2847  func (s *UnitSuite) TestWatchMachineAndEndpointAddressesHash(c *gc.C) {
  2848  	// Create 2 spaces
  2849  	sn1, err := s.State.AddSubnet(network.SubnetInfo{CIDR: "10.0.0.0/24"})
  2850  	c.Assert(err, gc.IsNil)
  2851  	sn2, err := s.State.AddSubnet(network.SubnetInfo{CIDR: "10.0.254.0/24"})
  2852  	c.Assert(err, gc.IsNil)
  2853  
  2854  	_, err = s.State.AddSpace("public", "", []string{sn1.ID()}, false)
  2855  	c.Assert(err, gc.IsNil)
  2856  	_, err = s.State.AddSpace("private", "", []string{sn2.ID()}, false)
  2857  	c.Assert(err, gc.IsNil)
  2858  
  2859  	// Create machine with 2 interfaces on the above spaces
  2860  	m1, err := s.State.AddOneMachine(state.MachineTemplate{
  2861  		Base:        state.UbuntuBase("12.10"),
  2862  		Jobs:        []state.MachineJob{state.JobHostUnits},
  2863  		Constraints: constraints.MustParse("spaces=public,private"),
  2864  	})
  2865  	c.Assert(err, gc.IsNil)
  2866  	err = m1.SetLinkLayerDevices(
  2867  		state.LinkLayerDeviceArgs{Name: "enp5s0", Type: network.EthernetDevice},
  2868  		state.LinkLayerDeviceArgs{Name: "enp5s1", Type: network.EthernetDevice},
  2869  	)
  2870  	c.Assert(err, gc.IsNil)
  2871  	err = m1.SetDevicesAddresses(
  2872  		state.LinkLayerDeviceAddress{DeviceName: "enp5s0", CIDRAddress: "10.0.0.1/24", ConfigMethod: network.ConfigStatic},
  2873  		state.LinkLayerDeviceAddress{DeviceName: "enp5s1", CIDRAddress: "10.0.254.42/24", ConfigMethod: network.ConfigStatic},
  2874  	)
  2875  	c.Assert(err, gc.IsNil)
  2876  
  2877  	// Deploy unit to machine
  2878  	ch := s.AddMetaCharm(c, "mysql", metaExtraEndpoints, 1)
  2879  	app := state.AddTestingApplicationWithBindings(c, s.State, "mysql", ch, map[string]string{
  2880  		"server": "public",
  2881  		"foo":    "private",
  2882  	})
  2883  	unit, err := app.AddUnit(state.AddUnitParams{})
  2884  	c.Assert(err, gc.IsNil)
  2885  	err = unit.AssignToMachine(m1)
  2886  	c.Assert(err, gc.IsNil)
  2887  
  2888  	// Create watcher
  2889  	w, err := unit.WatchMachineAndEndpointAddressesHash()
  2890  	c.Assert(err, gc.IsNil)
  2891  	defer func() { _ = w.Stop() }()
  2892  
  2893  	// The watcher will emit the original hash.
  2894  	wc := testing.NewStringsWatcherC(c, w)
  2895  	wc.AssertChange("b1b30f7f8b818a0ef59e858ab0e409a33ebe9eefead686f7a0f1d1ef7a11cf0e")
  2896  
  2897  	// Adding a new machine address should trigger a change
  2898  	err = m1.SetDevicesAddresses(state.LinkLayerDeviceAddress{
  2899  		DeviceName:   "enp5s0",
  2900  		CIDRAddress:  "10.0.0.100/24",
  2901  		ConfigMethod: network.ConfigStatic,
  2902  	})
  2903  	c.Assert(err, gc.IsNil)
  2904  	err = m1.SetProviderAddresses(network.NewSpaceAddress("10.0.0.100"))
  2905  	c.Assert(err, gc.IsNil)
  2906  	wc.AssertChange("46ed851765a963e100161210a7b4fbb28d59b24edb580a60f86dbbaebea14d37")
  2907  
  2908  	// Changing the application bindings after an upgrade should trigger a change
  2909  	sch := s.AddMetaCharm(c, "mysql", metaExtraEndpoints, 2)
  2910  	cfg := state.SetCharmConfig{
  2911  		Charm:       sch,
  2912  		CharmOrigin: defaultCharmOrigin(sch.URL()),
  2913  		ForceUnits:  true,
  2914  		EndpointBindings: map[string]string{
  2915  			"server": "private",
  2916  		},
  2917  	}
  2918  	err = app.SetCharm(cfg)
  2919  	c.Assert(err, jc.ErrorIsNil)
  2920  	wc.AssertChange("c895e8b57123efd2194d48b74db431e4db4c3ae4fa75f55aa6f32c7f39f29abd")
  2921  }
  2922  
  2923  func unitMachine(c *gc.C, st *state.State, u *state.Unit) *state.Machine {
  2924  	machineId, err := u.AssignedMachineId()
  2925  	c.Assert(err, jc.ErrorIsNil)
  2926  	machine, err := st.Machine(machineId)
  2927  	c.Assert(err, jc.ErrorIsNil)
  2928  	return machine
  2929  }
  2930  
  2931  type CAASUnitSuite struct {
  2932  	ConnSuite
  2933  	application *state.Application
  2934  	operatorApp *state.Application
  2935  }
  2936  
  2937  var _ = gc.Suite(&CAASUnitSuite{})
  2938  
  2939  func (s *CAASUnitSuite) SetUpTest(c *gc.C) {
  2940  	s.ConnSuite.SetUpTest(c)
  2941  	st := s.Factory.MakeCAASModel(c, nil)
  2942  	s.AddCleanup(func(_ *gc.C) { st.Close() })
  2943  
  2944  	var err error
  2945  	s.Model, err = st.Model()
  2946  	c.Assert(err, jc.ErrorIsNil)
  2947  
  2948  	f := factory.NewFactory(st, s.StatePool)
  2949  	ch := f.MakeCharm(c, &factory.CharmParams{Name: "gitlab", Series: "kubernetes"})
  2950  	s.application = f.MakeApplication(c, &factory.ApplicationParams{
  2951  		Name: "gitlab", Charm: ch,
  2952  		CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{OS: "ubuntu", Channel: "20.04/stable"}},
  2953  	})
  2954  
  2955  	basicActions := `
  2956  snapshot:
  2957    params:
  2958      outfile:
  2959        type: string
  2960        default: "abcd"
  2961  `[1:]
  2962  	ch = state.AddCustomCharm(c, st, "elastic-operator", "actions.yaml", basicActions, "kubernetes", 1)
  2963  	s.operatorApp = f.MakeApplication(c, &factory.ApplicationParams{Charm: ch})
  2964  }
  2965  
  2966  func (s *CAASUnitSuite) TestShortCircuitDestroyUnit(c *gc.C) {
  2967  	// A unit that has not been allocated is removed directly.
  2968  	unit, err := s.application.AddUnit(state.AddUnitParams{})
  2969  	c.Assert(err, jc.ErrorIsNil)
  2970  	c.Assert(unit.Base(), jc.DeepEquals, state.Base{OS: "ubuntu", Channel: "20.04/stable"})
  2971  	c.Assert(unit.ShouldBeAssigned(), jc.IsFalse)
  2972  
  2973  	// A unit that has not set any status is removed directly.
  2974  	err = unit.Destroy()
  2975  	c.Assert(err, jc.ErrorIsNil)
  2976  	c.Assert(unit.Life(), gc.Equals, state.Dying)
  2977  	assertRemoved(c, unit)
  2978  }
  2979  
  2980  func (s *CAASUnitSuite) TestCannotShortCircuitDestroyAllocatedUnit(c *gc.C) {
  2981  	// This test is similar to TestShortCircuitDestroyUnit but
  2982  	// the unit has been allocated and a pod created.
  2983  	unit, err := s.application.AddUnit(state.AddUnitParams{})
  2984  	c.Assert(err, jc.ErrorIsNil)
  2985  	unitState := state.NewUnitState()
  2986  	unitState.SetUniterState("error")
  2987  	err = unit.SetState(unitState, state.UnitStateSizeLimits{})
  2988  	c.Assert(err, jc.ErrorIsNil)
  2989  	err = unit.Destroy()
  2990  	c.Assert(err, jc.ErrorIsNil)
  2991  	c.Assert(unit.Life(), gc.Equals, state.Dying)
  2992  	assertLife(c, unit, state.Dying)
  2993  }
  2994  
  2995  func (s *CAASUnitSuite) TestUpdateCAASUnitProviderId(c *gc.C) {
  2996  	existingUnit, err := s.application.AddUnit(state.AddUnitParams{
  2997  		ProviderId: strPtr("unit-uuid"),
  2998  		Address:    strPtr("192.168.1.1"),
  2999  		Ports:      &[]string{"80"},
  3000  	})
  3001  	c.Assert(err, jc.ErrorIsNil)
  3002  	var updateUnits state.UpdateUnitsOperation
  3003  	updateUnits.Updates = []*state.UpdateUnitOperation{
  3004  		existingUnit.UpdateOperation(state.UnitUpdateProperties{
  3005  			ProviderId: strPtr("another-uuid"),
  3006  		})}
  3007  	err = s.application.UpdateUnits(&updateUnits)
  3008  	c.Assert(err, jc.ErrorIsNil)
  3009  	info, err := existingUnit.ContainerInfo()
  3010  	c.Assert(err, jc.ErrorIsNil)
  3011  	c.Assert(info.Unit(), gc.Equals, existingUnit.Name())
  3012  	c.Assert(info.ProviderId(), gc.Equals, "another-uuid")
  3013  	addr := network.NewSpaceAddress("192.168.1.1", network.WithScope(network.ScopeMachineLocal))
  3014  	c.Assert(info.Address(), gc.DeepEquals, &addr)
  3015  	c.Assert(info.Ports(), jc.DeepEquals, []string{"80"})
  3016  }
  3017  
  3018  func (s *CAASUnitSuite) TestAddCAASUnitProviderId(c *gc.C) {
  3019  	existingUnit, err := s.application.AddUnit(state.AddUnitParams{
  3020  		Address: strPtr("192.168.1.1"),
  3021  		Ports:   &[]string{"80"},
  3022  	})
  3023  	c.Assert(err, jc.ErrorIsNil)
  3024  	var updateUnits state.UpdateUnitsOperation
  3025  	updateUnits.Updates = []*state.UpdateUnitOperation{
  3026  		existingUnit.UpdateOperation(state.UnitUpdateProperties{
  3027  			ProviderId: strPtr("another-uuid"),
  3028  		})}
  3029  	err = s.application.UpdateUnits(&updateUnits)
  3030  	c.Assert(err, jc.ErrorIsNil)
  3031  	info, err := existingUnit.ContainerInfo()
  3032  	c.Assert(err, jc.ErrorIsNil)
  3033  	c.Assert(info.Unit(), gc.Equals, existingUnit.Name())
  3034  	c.Assert(info.ProviderId(), gc.Equals, "another-uuid")
  3035  	c.Check(info.Address(), gc.NotNil)
  3036  	c.Check(*info.Address(), jc.DeepEquals,
  3037  		network.NewSpaceAddress("192.168.1.1", network.WithScope(network.ScopeMachineLocal)))
  3038  	c.Assert(info.Ports(), jc.DeepEquals, []string{"80"})
  3039  }
  3040  
  3041  func (s *CAASUnitSuite) TestUpdateCAASUnitAddress(c *gc.C) {
  3042  	existingUnit, err := s.application.AddUnit(state.AddUnitParams{
  3043  		ProviderId: strPtr("unit-uuid"),
  3044  		Address:    strPtr("192.168.1.1"),
  3045  		Ports:      &[]string{"80"},
  3046  	})
  3047  	c.Assert(err, jc.ErrorIsNil)
  3048  	var updateUnits state.UpdateUnitsOperation
  3049  	updateUnits.Updates = []*state.UpdateUnitOperation{
  3050  		existingUnit.UpdateOperation(state.UnitUpdateProperties{
  3051  			Address: strPtr("192.168.1.2"),
  3052  		})}
  3053  	err = s.application.UpdateUnits(&updateUnits)
  3054  	c.Assert(err, jc.ErrorIsNil)
  3055  	info, err := existingUnit.ContainerInfo()
  3056  	c.Assert(err, jc.ErrorIsNil)
  3057  	c.Assert(info.Unit(), gc.Equals, existingUnit.Name())
  3058  	c.Assert(info.ProviderId(), gc.Equals, "unit-uuid")
  3059  	addr := network.NewSpaceAddress("192.168.1.2", network.WithScope(network.ScopeMachineLocal))
  3060  	c.Assert(info.Address(), jc.DeepEquals, &addr)
  3061  	c.Assert(info.Ports(), jc.DeepEquals, []string{"80"})
  3062  }
  3063  
  3064  func (s *CAASUnitSuite) TestUpdateCAASUnitPorts(c *gc.C) {
  3065  	existingUnit, err := s.application.AddUnit(state.AddUnitParams{
  3066  		ProviderId: strPtr("unit-uuid"),
  3067  		Address:    strPtr("192.168.1.1"),
  3068  		Ports:      &[]string{"80"},
  3069  	})
  3070  	c.Assert(err, jc.ErrorIsNil)
  3071  	var updateUnits state.UpdateUnitsOperation
  3072  	updateUnits.Updates = []*state.UpdateUnitOperation{
  3073  		existingUnit.UpdateOperation(state.UnitUpdateProperties{
  3074  			Ports: &[]string{"443"},
  3075  		})}
  3076  	err = s.application.UpdateUnits(&updateUnits)
  3077  	c.Assert(err, jc.ErrorIsNil)
  3078  	info, err := existingUnit.ContainerInfo()
  3079  	c.Assert(err, jc.ErrorIsNil)
  3080  	c.Assert(info.Unit(), gc.Equals, existingUnit.Name())
  3081  	c.Assert(info.ProviderId(), gc.Equals, "unit-uuid")
  3082  	addr := network.NewSpaceAddress("192.168.1.1", network.WithScope(network.ScopeMachineLocal))
  3083  	c.Assert(info.Address(), jc.DeepEquals, &addr)
  3084  	c.Assert(info.Ports(), jc.DeepEquals, []string{"443"})
  3085  }
  3086  
  3087  func (s *CAASUnitSuite) TestRemoveUnitDeletesContainerInfo(c *gc.C) {
  3088  	existingUnit, err := s.application.AddUnit(state.AddUnitParams{
  3089  		ProviderId: strPtr("unit-uuid"),
  3090  		Address:    strPtr("192.168.1.1"),
  3091  		Ports:      &[]string{"80"},
  3092  	})
  3093  	c.Assert(err, jc.ErrorIsNil)
  3094  	err = existingUnit.Destroy()
  3095  	c.Assert(err, jc.ErrorIsNil)
  3096  	_, err = existingUnit.ContainerInfo()
  3097  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  3098  }
  3099  
  3100  func (s *CAASUnitSuite) TestPrivateAddress(c *gc.C) {
  3101  	existingUnit, err := s.application.AddUnit(state.AddUnitParams{})
  3102  	c.Assert(err, jc.ErrorIsNil)
  3103  	err = s.application.UpdateCloudService("", network.SpaceAddresses{
  3104  		network.NewSpaceAddress("192.168.1.2", network.WithScope(network.ScopeCloudLocal)),
  3105  		network.NewSpaceAddress("54.32.1.2", network.WithScope(network.ScopePublic)),
  3106  	})
  3107  	c.Assert(err, jc.ErrorIsNil)
  3108  
  3109  	addr, err := existingUnit.PrivateAddress()
  3110  	c.Assert(err, jc.ErrorIsNil)
  3111  	c.Assert(addr, jc.DeepEquals, network.NewSpaceAddress("192.168.1.2", network.WithScope(network.ScopeCloudLocal)))
  3112  }
  3113  
  3114  func (s *CAASUnitSuite) TestPublicAddress(c *gc.C) {
  3115  	existingUnit, err := s.application.AddUnit(state.AddUnitParams{})
  3116  	c.Assert(err, jc.ErrorIsNil)
  3117  	err = s.application.UpdateCloudService("", []network.SpaceAddress{
  3118  		network.NewSpaceAddress("192.168.1.2", network.WithScope(network.ScopeCloudLocal)),
  3119  		network.NewSpaceAddress("54.32.1.2", network.WithScope(network.ScopePublic)),
  3120  	})
  3121  	c.Assert(err, jc.ErrorIsNil)
  3122  
  3123  	addr, err := existingUnit.PublicAddress()
  3124  	c.Assert(err, jc.ErrorIsNil)
  3125  	c.Assert(addr, jc.DeepEquals, network.NewSpaceAddress("54.32.1.2", network.WithScope(network.ScopePublic)))
  3126  }
  3127  
  3128  func (s *CAASUnitSuite) TestAllAddresses(c *gc.C) {
  3129  	existingUnit, err := s.application.AddUnit(state.AddUnitParams{})
  3130  	c.Assert(err, jc.ErrorIsNil)
  3131  	err = s.application.UpdateCloudService("", []network.SpaceAddress{
  3132  		network.NewSpaceAddress("192.168.1.2", network.WithScope(network.ScopeCloudLocal)),
  3133  		network.NewSpaceAddress("54.32.1.2", network.WithScope(network.ScopePublic)),
  3134  	})
  3135  	c.Assert(err, jc.ErrorIsNil)
  3136  
  3137  	var updateUnits state.UpdateUnitsOperation
  3138  	local := "10.0.0.1"
  3139  	updateUnits.Updates = []*state.UpdateUnitOperation{existingUnit.UpdateOperation(state.UnitUpdateProperties{
  3140  		Address: &local,
  3141  		Ports:   &[]string{"443"},
  3142  	})}
  3143  	err = s.application.UpdateUnits(&updateUnits)
  3144  	c.Assert(err, jc.ErrorIsNil)
  3145  
  3146  	addrs, err := existingUnit.AllAddresses()
  3147  	c.Assert(err, jc.ErrorIsNil)
  3148  	c.Assert(addrs, jc.DeepEquals, network.SpaceAddresses{
  3149  		network.NewSpaceAddress("192.168.1.2", network.WithScope(network.ScopeCloudLocal)),
  3150  		network.NewSpaceAddress("54.32.1.2", network.WithScope(network.ScopePublic)),
  3151  		network.NewSpaceAddress("10.0.0.1", network.WithScope(network.ScopeMachineLocal)),
  3152  	})
  3153  }
  3154  
  3155  func (s *CAASUnitSuite) TestWatchContainerAddresses(c *gc.C) {
  3156  	unit, err := s.application.AddUnit(state.AddUnitParams{})
  3157  	c.Assert(err, jc.ErrorIsNil)
  3158  
  3159  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
  3160  	w := unit.WatchContainerAddresses()
  3161  	defer w.Stop()
  3162  	wc := statetesting.NewNotifyWatcherC(c, w)
  3163  	wc.AssertOneChange()
  3164  
  3165  	// Change the container port: not reported.
  3166  	var updateUnits state.UpdateUnitsOperation
  3167  	updateUnits.Updates = []*state.UpdateUnitOperation{unit.UpdateOperation(state.UnitUpdateProperties{
  3168  		Ports: &[]string{"443"},
  3169  	})}
  3170  	err = s.application.UpdateUnits(&updateUnits)
  3171  	c.Assert(err, jc.ErrorIsNil)
  3172  	wc.AssertNoChange()
  3173  
  3174  	// Set container addresses: reported.
  3175  	addr := "10.0.0.1"
  3176  	updateUnits.Updates = []*state.UpdateUnitOperation{unit.UpdateOperation(state.UnitUpdateProperties{
  3177  		Address: &addr,
  3178  	})}
  3179  	err = s.application.UpdateUnits(&updateUnits)
  3180  	c.Assert(err, jc.ErrorIsNil)
  3181  	c.Assert(err, jc.ErrorIsNil)
  3182  	wc.AssertOneChange()
  3183  
  3184  	// Set different machine addresses: reported.
  3185  	addr = "10.0.0.2"
  3186  	updateUnits.Updates = []*state.UpdateUnitOperation{unit.UpdateOperation(state.UnitUpdateProperties{
  3187  		Address: &addr,
  3188  	})}
  3189  	err = s.application.UpdateUnits(&updateUnits)
  3190  	c.Assert(err, jc.ErrorIsNil)
  3191  	c.Assert(err, jc.ErrorIsNil)
  3192  	wc.AssertOneChange()
  3193  
  3194  	// Ensure the following operation to set the unit as Dying
  3195  	// is not short circuited to remove the unit.
  3196  	unitState := state.NewUnitState()
  3197  	unitState.SetUniterState("idle")
  3198  	err = unit.SetState(unitState, state.UnitStateSizeLimits{})
  3199  	c.Assert(err, jc.ErrorIsNil)
  3200  	// Make it Dying: not reported.
  3201  	err = unit.Destroy()
  3202  	c.Assert(err, jc.ErrorIsNil)
  3203  	wc.AssertNoChange()
  3204  	// Double check the unit is dying and not removed.
  3205  	err = unit.Refresh()
  3206  	c.Assert(err, jc.ErrorIsNil)
  3207  	c.Assert(unit.Life(), gc.Equals, state.Dying)
  3208  
  3209  	// Make it Dead: not reported.
  3210  	err = unit.EnsureDead()
  3211  	c.Assert(err, jc.ErrorIsNil)
  3212  	wc.AssertNoChange()
  3213  
  3214  	// Remove it: watcher eventually closed and Err
  3215  	// returns an IsNotFound error.
  3216  	err = unit.Remove()
  3217  	c.Assert(err, jc.ErrorIsNil)
  3218  	select {
  3219  	case _, ok := <-w.Changes():
  3220  		c.Assert(ok, jc.IsFalse)
  3221  	case <-time.After(coretesting.LongWait):
  3222  		c.Fatalf("watcher not closed")
  3223  	}
  3224  	c.Assert(w.Err(), jc.Satisfies, errors.IsNotFound)
  3225  }
  3226  
  3227  func (s *CAASUnitSuite) TestWatchServiceAddressesHash(c *gc.C) {
  3228  	unit, err := s.application.AddUnit(state.AddUnitParams{})
  3229  	c.Assert(err, jc.ErrorIsNil)
  3230  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
  3231  	w := s.application.WatchServiceAddressesHash()
  3232  	defer w.Stop()
  3233  	wc := statetesting.NewStringsWatcherC(c, w)
  3234  	wc.AssertChange("")
  3235  
  3236  	// Set container addresses: not reported.
  3237  	var updateUnits state.UpdateUnitsOperation
  3238  	addr := "10.0.0.1"
  3239  	updateUnits.Updates = []*state.UpdateUnitOperation{unit.UpdateOperation(state.UnitUpdateProperties{
  3240  		Address: &addr,
  3241  	})}
  3242  	err = s.application.UpdateUnits(&updateUnits)
  3243  	c.Assert(err, jc.ErrorIsNil)
  3244  	wc.AssertNoChange()
  3245  
  3246  	// Set service addresses: reported.
  3247  	err = s.application.UpdateCloudService("1", network.SpaceAddresses{network.NewSpaceAddress("10.0.0.2")})
  3248  	c.Assert(err, jc.ErrorIsNil)
  3249  	wc.AssertChange("7fcdfefa54c49ed9dc8132b4a28491a02ec35b03c20b2d4cc95469fead847ff8")
  3250  
  3251  	// Set different container addresses: reported.
  3252  	err = s.application.UpdateCloudService("1", network.SpaceAddresses{network.NewSpaceAddress("10.0.0.3")})
  3253  	c.Assert(err, jc.ErrorIsNil)
  3254  	wc.AssertChange("800892c5473f38623ef4856303b3458cfa81d0da803f228db69910949a13f458")
  3255  
  3256  	// Ensure the following operation to set the unit as Dying
  3257  	// is not short circuited to remove the unit.
  3258  	unitState := state.NewUnitState()
  3259  	unitState.SetUniterState("idle")
  3260  	err = unit.SetState(unitState, state.UnitStateSizeLimits{})
  3261  	c.Assert(err, jc.ErrorIsNil)
  3262  	// Make it Dying: not reported.
  3263  	err = unit.Destroy()
  3264  	c.Assert(err, jc.ErrorIsNil)
  3265  	wc.AssertNoChange()
  3266  	// Double check the unit is dying and not removed.
  3267  	err = unit.Refresh()
  3268  	c.Assert(err, jc.ErrorIsNil)
  3269  	c.Assert(unit.Life(), gc.Equals, state.Dying)
  3270  
  3271  	// Make it Dead: not reported.
  3272  	err = unit.EnsureDead()
  3273  	c.Assert(err, jc.ErrorIsNil)
  3274  	wc.AssertNoChange()
  3275  
  3276  	// Remove it: watcher eventually closed and Err
  3277  	// returns an IsNotFound error.
  3278  	err = unit.Remove()
  3279  	c.Assert(err, jc.ErrorIsNil)
  3280  	err = s.application.Destroy()
  3281  	c.Assert(err, jc.ErrorIsNil)
  3282  
  3283  	// App removal requires cluster resources to be cleared.
  3284  	err = s.application.Refresh()
  3285  	c.Assert(err, jc.ErrorIsNil)
  3286  	err = s.application.ClearResources()
  3287  	c.Assert(err, jc.ErrorIsNil)
  3288  	assertCleanupCount(c, s.Model.State(), 2)
  3289  
  3290  	select {
  3291  	case _, ok := <-w.Changes():
  3292  		c.Assert(ok, jc.IsFalse)
  3293  	case <-time.After(coretesting.LongWait):
  3294  		c.Fatalf("watcher not closed")
  3295  	}
  3296  	c.Assert(w.Err(), jc.Satisfies, errors.IsNotFound)
  3297  }
  3298  
  3299  func (s *CAASUnitSuite) TestOperatorAddAction(c *gc.C) {
  3300  	unit, err := s.operatorApp.AddUnit(state.AddUnitParams{})
  3301  	c.Assert(err, jc.ErrorIsNil)
  3302  	operationID, err := s.Model.EnqueueOperation("a test", 1)
  3303  	c.Assert(err, jc.ErrorIsNil)
  3304  	action, err := s.Model.AddAction(unit, operationID, "snapshot", nil, nil, nil)
  3305  	c.Assert(err, jc.ErrorIsNil)
  3306  	c.Assert(action.Parameters(), jc.DeepEquals, map[string]interface{}{
  3307  		"outfile": "abcd", "workload-context": false,
  3308  	})
  3309  }