github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/state/unit_test.go (about)

     1  // Copyright 2012-2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"strconv"
     8  
     9  	"github.com/juju/errors"
    10  	jc "github.com/juju/testing/checkers"
    11  	jujutxn "github.com/juju/txn"
    12  	gc "gopkg.in/check.v1"
    13  	"gopkg.in/juju/charm.v4"
    14  
    15  	"github.com/juju/juju/instance"
    16  	"github.com/juju/juju/network"
    17  	"github.com/juju/juju/state"
    18  	"github.com/juju/juju/state/testing"
    19  	coretesting "github.com/juju/juju/testing"
    20  )
    21  
    22  const (
    23  	contentionErr = ".*: state changing too quickly; try again soon"
    24  )
    25  
    26  type UnitSuite struct {
    27  	ConnSuite
    28  	charm   *state.Charm
    29  	service *state.Service
    30  	unit    *state.Unit
    31  }
    32  
    33  var _ = gc.Suite(&UnitSuite{})
    34  
    35  func (s *UnitSuite) SetUpTest(c *gc.C) {
    36  	s.ConnSuite.SetUpTest(c)
    37  	s.charm = s.AddTestingCharm(c, "wordpress")
    38  	var err error
    39  	s.service = s.AddTestingService(c, "wordpress", s.charm)
    40  	c.Assert(err, jc.ErrorIsNil)
    41  	s.unit, err = s.service.AddUnit()
    42  	c.Assert(err, jc.ErrorIsNil)
    43  	c.Assert(s.unit.Series(), gc.Equals, "quantal")
    44  }
    45  
    46  func (s *UnitSuite) TestUnitNotFound(c *gc.C) {
    47  	_, err := s.State.Unit("subway/0")
    48  	c.Assert(err, gc.ErrorMatches, `unit "subway/0" not found`)
    49  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
    50  }
    51  
    52  func (s *UnitSuite) TestService(c *gc.C) {
    53  	svc, err := s.unit.Service()
    54  	c.Assert(err, jc.ErrorIsNil)
    55  	c.Assert(svc.Name(), gc.Equals, s.unit.ServiceName())
    56  }
    57  
    58  func (s *UnitSuite) TestConfigSettingsNeedCharmURLSet(c *gc.C) {
    59  	_, err := s.unit.ConfigSettings()
    60  	c.Assert(err, gc.ErrorMatches, "unit charm not set")
    61  }
    62  
    63  func (s *UnitSuite) TestConfigSettingsIncludeDefaults(c *gc.C) {
    64  	err := s.unit.SetCharmURL(s.charm.URL())
    65  	c.Assert(err, jc.ErrorIsNil)
    66  	settings, err := s.unit.ConfigSettings()
    67  	c.Assert(err, jc.ErrorIsNil)
    68  	c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "My Title"})
    69  }
    70  
    71  func (s *UnitSuite) TestConfigSettingsReflectService(c *gc.C) {
    72  	err := s.service.UpdateConfigSettings(charm.Settings{"blog-title": "no title"})
    73  	c.Assert(err, jc.ErrorIsNil)
    74  	err = s.unit.SetCharmURL(s.charm.URL())
    75  	c.Assert(err, jc.ErrorIsNil)
    76  	settings, err := s.unit.ConfigSettings()
    77  	c.Assert(err, jc.ErrorIsNil)
    78  	c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "no title"})
    79  
    80  	err = s.service.UpdateConfigSettings(charm.Settings{"blog-title": "ironic title"})
    81  	c.Assert(err, jc.ErrorIsNil)
    82  	settings, err = s.unit.ConfigSettings()
    83  	c.Assert(err, jc.ErrorIsNil)
    84  	c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "ironic title"})
    85  }
    86  
    87  func (s *UnitSuite) TestConfigSettingsReflectCharm(c *gc.C) {
    88  	err := s.unit.SetCharmURL(s.charm.URL())
    89  	c.Assert(err, jc.ErrorIsNil)
    90  	newCharm := s.AddConfigCharm(c, "wordpress", "options: {}", 123)
    91  	err = s.service.SetCharm(newCharm, false)
    92  	c.Assert(err, jc.ErrorIsNil)
    93  
    94  	// Settings still reflect charm set on unit.
    95  	settings, err := s.unit.ConfigSettings()
    96  	c.Assert(err, jc.ErrorIsNil)
    97  	c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "My Title"})
    98  
    99  	// When the unit has the new charm set, it'll see the new config.
   100  	err = s.unit.SetCharmURL(newCharm.URL())
   101  	c.Assert(err, jc.ErrorIsNil)
   102  	settings, err = s.unit.ConfigSettings()
   103  	c.Assert(err, jc.ErrorIsNil)
   104  	c.Assert(settings, gc.DeepEquals, charm.Settings{})
   105  }
   106  
   107  func (s *UnitSuite) TestWatchConfigSettingsNeedsCharmURL(c *gc.C) {
   108  	_, err := s.unit.WatchConfigSettings()
   109  	c.Assert(err, gc.ErrorMatches, "unit charm not set")
   110  }
   111  
   112  func (s *UnitSuite) TestWatchConfigSettings(c *gc.C) {
   113  	err := s.unit.SetCharmURL(s.charm.URL())
   114  	c.Assert(err, jc.ErrorIsNil)
   115  	w, err := s.unit.WatchConfigSettings()
   116  	c.Assert(err, jc.ErrorIsNil)
   117  	defer testing.AssertStop(c, w)
   118  
   119  	// Initial event.
   120  	wc := testing.NewNotifyWatcherC(c, s.State, w)
   121  	wc.AssertOneChange()
   122  
   123  	// Update config a couple of times, check a single event.
   124  	err = s.service.UpdateConfigSettings(charm.Settings{
   125  		"blog-title": "superhero paparazzi",
   126  	})
   127  	c.Assert(err, jc.ErrorIsNil)
   128  	err = s.service.UpdateConfigSettings(charm.Settings{
   129  		"blog-title": "sauceror central",
   130  	})
   131  	c.Assert(err, jc.ErrorIsNil)
   132  	wc.AssertOneChange()
   133  
   134  	// Non-change is not reported.
   135  	err = s.service.UpdateConfigSettings(charm.Settings{
   136  		"blog-title": "sauceror central",
   137  	})
   138  	c.Assert(err, jc.ErrorIsNil)
   139  	wc.AssertNoChange()
   140  
   141  	// Change service's charm; nothing detected.
   142  	newCharm := s.AddConfigCharm(c, "wordpress", floatConfig, 123)
   143  	err = s.service.SetCharm(newCharm, false)
   144  	c.Assert(err, jc.ErrorIsNil)
   145  	wc.AssertNoChange()
   146  
   147  	// Change service config for new charm; nothing detected.
   148  	err = s.service.UpdateConfigSettings(charm.Settings{
   149  		"key": 42.0,
   150  	})
   151  	c.Assert(err, jc.ErrorIsNil)
   152  	wc.AssertNoChange()
   153  
   154  	// NOTE: if we were to change the unit to use the new charm, we'd see
   155  	// another event, because the originally-watched document will become
   156  	// unreferenced and be removed. But I'm not testing that behaviour
   157  	// because it's not very helpful and subject to change.
   158  }
   159  
   160  func (s *UnitSuite) addSubordinateUnit(c *gc.C) *state.Unit {
   161  	subCharm := s.AddTestingCharm(c, "logging")
   162  	s.AddTestingService(c, "logging", subCharm)
   163  	eps, err := s.State.InferEndpoints("wordpress", "logging")
   164  	c.Assert(err, jc.ErrorIsNil)
   165  	rel, err := s.State.AddRelation(eps...)
   166  	c.Assert(err, jc.ErrorIsNil)
   167  	ru, err := rel.Unit(s.unit)
   168  	c.Assert(err, jc.ErrorIsNil)
   169  	err = ru.EnterScope(nil)
   170  	c.Assert(err, jc.ErrorIsNil)
   171  	subUnit, err := s.State.Unit("logging/0")
   172  	c.Assert(err, jc.ErrorIsNil)
   173  	return subUnit
   174  }
   175  
   176  func (s *UnitSuite) setAssignedMachineAddresses(c *gc.C, u *state.Unit) {
   177  	err := u.AssignToNewMachine()
   178  	c.Assert(err, jc.ErrorIsNil)
   179  	mid, err := u.AssignedMachineId()
   180  	c.Assert(err, jc.ErrorIsNil)
   181  	machine, err := s.State.Machine(mid)
   182  	c.Assert(err, jc.ErrorIsNil)
   183  	err = machine.SetProvisioned("i-exist", "fake_nonce", nil)
   184  	c.Assert(err, jc.ErrorIsNil)
   185  	err = machine.SetAddresses(network.Address{
   186  		Type:  network.IPv4Address,
   187  		Scope: network.ScopeCloudLocal,
   188  		Value: "private.address.example.com",
   189  	}, network.Address{
   190  		Type:  network.IPv4Address,
   191  		Scope: network.ScopePublic,
   192  		Value: "public.address.example.com",
   193  	})
   194  	c.Assert(err, jc.ErrorIsNil)
   195  }
   196  
   197  func (s *UnitSuite) TestPublicAddressSubordinate(c *gc.C) {
   198  	subUnit := s.addSubordinateUnit(c)
   199  	_, ok := subUnit.PublicAddress()
   200  	c.Assert(ok, jc.IsFalse)
   201  
   202  	s.setAssignedMachineAddresses(c, s.unit)
   203  	address, ok := subUnit.PublicAddress()
   204  	c.Assert(ok, jc.IsTrue)
   205  	c.Assert(address, gc.Equals, "public.address.example.com")
   206  }
   207  
   208  func (s *UnitSuite) TestPublicAddress(c *gc.C) {
   209  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
   210  	c.Assert(err, jc.ErrorIsNil)
   211  	err = s.unit.AssignToMachine(machine)
   212  	c.Assert(err, jc.ErrorIsNil)
   213  
   214  	address, ok := s.unit.PublicAddress()
   215  	c.Check(address, gc.Equals, "")
   216  	c.Assert(ok, jc.IsFalse)
   217  
   218  	public := network.NewAddress("8.8.8.8", network.ScopePublic)
   219  	private := network.NewAddress("127.0.0.1", network.ScopeCloudLocal)
   220  
   221  	err = machine.SetAddresses(public, private)
   222  	c.Assert(err, jc.ErrorIsNil)
   223  
   224  	address, ok = s.unit.PublicAddress()
   225  	c.Check(address, gc.Equals, "8.8.8.8")
   226  	c.Assert(ok, jc.IsTrue)
   227  }
   228  
   229  func (s *UnitSuite) TestPublicAddressMachineAddresses(c *gc.C) {
   230  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
   231  	c.Assert(err, jc.ErrorIsNil)
   232  	err = s.unit.AssignToMachine(machine)
   233  	c.Assert(err, jc.ErrorIsNil)
   234  
   235  	publicProvider := network.NewAddress("8.8.8.8", network.ScopePublic)
   236  	privateProvider := network.NewAddress("127.0.0.1", network.ScopeCloudLocal)
   237  	privateMachine := network.NewAddress("127.0.0.2", network.ScopeUnknown)
   238  
   239  	err = machine.SetAddresses(privateProvider)
   240  	c.Assert(err, jc.ErrorIsNil)
   241  	err = machine.SetMachineAddresses(privateMachine)
   242  	c.Assert(err, jc.ErrorIsNil)
   243  	address, ok := s.unit.PublicAddress()
   244  	c.Check(address, gc.Equals, "127.0.0.1")
   245  	c.Assert(ok, jc.IsTrue)
   246  
   247  	err = machine.SetAddresses(publicProvider, privateProvider)
   248  	c.Assert(err, jc.ErrorIsNil)
   249  	address, ok = s.unit.PublicAddress()
   250  	c.Check(address, gc.Equals, "8.8.8.8")
   251  	c.Assert(ok, jc.IsTrue)
   252  }
   253  
   254  func (s *UnitSuite) TestPrivateAddressSubordinate(c *gc.C) {
   255  	subUnit := s.addSubordinateUnit(c)
   256  	_, ok := subUnit.PrivateAddress()
   257  	c.Assert(ok, jc.IsFalse)
   258  
   259  	s.setAssignedMachineAddresses(c, s.unit)
   260  	address, ok := subUnit.PrivateAddress()
   261  	c.Assert(ok, jc.IsTrue)
   262  	c.Assert(address, gc.Equals, "private.address.example.com")
   263  }
   264  
   265  func (s *UnitSuite) TestPrivateAddress(c *gc.C) {
   266  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
   267  	c.Assert(err, jc.ErrorIsNil)
   268  	err = s.unit.AssignToMachine(machine)
   269  	c.Assert(err, jc.ErrorIsNil)
   270  
   271  	address, ok := s.unit.PrivateAddress()
   272  	c.Check(address, gc.Equals, "")
   273  	c.Assert(ok, jc.IsFalse)
   274  
   275  	public := network.NewAddress("8.8.8.8", network.ScopePublic)
   276  	private := network.NewAddress("127.0.0.1", network.ScopeCloudLocal)
   277  
   278  	err = machine.SetAddresses(public, private)
   279  	c.Assert(err, jc.ErrorIsNil)
   280  
   281  	address, ok = s.unit.PrivateAddress()
   282  	c.Check(address, gc.Equals, "127.0.0.1")
   283  	c.Assert(ok, jc.IsTrue)
   284  }
   285  
   286  type destroyMachineTestCase struct {
   287  	target    *state.Unit
   288  	host      *state.Machine
   289  	desc      string
   290  	flipHook  []jujutxn.TestHook
   291  	destroyed bool
   292  }
   293  
   294  func (s *UnitSuite) destroyMachineTestCases(c *gc.C) []destroyMachineTestCase {
   295  	var result []destroyMachineTestCase
   296  	var err error
   297  
   298  	{
   299  		tc := destroyMachineTestCase{desc: "standalone principal", destroyed: true}
   300  		tc.host, err = s.State.AddMachine("quantal", state.JobHostUnits)
   301  		c.Assert(err, jc.ErrorIsNil)
   302  		tc.target, err = s.service.AddUnit()
   303  		c.Assert(err, jc.ErrorIsNil)
   304  		c.Assert(tc.target.AssignToMachine(tc.host), gc.IsNil)
   305  		result = append(result, tc)
   306  	}
   307  	{
   308  		tc := destroyMachineTestCase{desc: "co-located principals", destroyed: false}
   309  		tc.host, err = s.State.AddMachine("quantal", state.JobHostUnits)
   310  		c.Assert(err, jc.ErrorIsNil)
   311  		tc.target, err = s.service.AddUnit()
   312  		c.Assert(err, jc.ErrorIsNil)
   313  		c.Assert(tc.target.AssignToMachine(tc.host), gc.IsNil)
   314  		colocated, err := s.service.AddUnit()
   315  		c.Assert(err, jc.ErrorIsNil)
   316  		c.Assert(colocated.AssignToMachine(tc.host), gc.IsNil)
   317  
   318  		result = append(result, tc)
   319  	}
   320  	{
   321  		tc := destroyMachineTestCase{desc: "host has container", destroyed: false}
   322  		tc.host, err = s.State.AddMachine("quantal", state.JobHostUnits)
   323  		c.Assert(err, jc.ErrorIsNil)
   324  		_, err := s.State.AddMachineInsideMachine(state.MachineTemplate{
   325  			Series: "quantal",
   326  			Jobs:   []state.MachineJob{state.JobHostUnits},
   327  		}, tc.host.Id(), instance.LXC)
   328  		c.Assert(err, jc.ErrorIsNil)
   329  		tc.target, err = s.service.AddUnit()
   330  		c.Assert(err, jc.ErrorIsNil)
   331  		c.Assert(tc.target.AssignToMachine(tc.host), gc.IsNil)
   332  
   333  		result = append(result, tc)
   334  	}
   335  	{
   336  		tc := destroyMachineTestCase{desc: "host has vote", destroyed: false}
   337  		tc.host, err = s.State.AddMachine("quantal", state.JobHostUnits)
   338  		c.Assert(err, jc.ErrorIsNil)
   339  		c.Assert(tc.host.SetHasVote(true), gc.IsNil)
   340  		tc.target, err = s.service.AddUnit()
   341  		c.Assert(err, jc.ErrorIsNil)
   342  		c.Assert(tc.target.AssignToMachine(tc.host), gc.IsNil)
   343  
   344  		result = append(result, tc)
   345  	}
   346  	{
   347  		tc := destroyMachineTestCase{desc: "unassigned unit", destroyed: true}
   348  		tc.host, err = s.State.AddMachine("quantal", state.JobHostUnits)
   349  		c.Assert(err, jc.ErrorIsNil)
   350  		tc.target, err = s.service.AddUnit()
   351  		c.Assert(err, jc.ErrorIsNil)
   352  		c.Assert(tc.target.AssignToMachine(tc.host), gc.IsNil)
   353  		result = append(result, tc)
   354  	}
   355  
   356  	return result
   357  }
   358  
   359  func (s *UnitSuite) TestRemoveUnitMachineFastForwardDestroy(c *gc.C) {
   360  	for _, tc := range s.destroyMachineTestCases(c) {
   361  		c.Log(tc.desc)
   362  		c.Assert(tc.target.Destroy(), gc.IsNil)
   363  		if tc.destroyed {
   364  			assertLife(c, tc.host, state.Dying)
   365  			c.Assert(tc.host.EnsureDead(), gc.IsNil)
   366  		} else {
   367  			assertLife(c, tc.host, state.Alive)
   368  			c.Assert(tc.host.Destroy(), gc.NotNil)
   369  		}
   370  	}
   371  }
   372  
   373  func (s *UnitSuite) TestRemoveUnitMachineNoFastForwardDestroy(c *gc.C) {
   374  	for _, tc := range s.destroyMachineTestCases(c) {
   375  		c.Log(tc.desc)
   376  		preventUnitDestroyRemove(c, tc.target)
   377  		c.Assert(tc.target.Destroy(), gc.IsNil)
   378  		c.Assert(tc.target.EnsureDead(), gc.IsNil)
   379  		assertLife(c, tc.host, state.Alive)
   380  		c.Assert(tc.target.Remove(), gc.IsNil)
   381  		if tc.destroyed {
   382  			assertLife(c, tc.host, state.Dying)
   383  		} else {
   384  			assertLife(c, tc.host, state.Alive)
   385  			c.Assert(tc.host.Destroy(), gc.NotNil)
   386  		}
   387  	}
   388  }
   389  
   390  func (s *UnitSuite) setMachineVote(c *gc.C, id string, hasVote bool) {
   391  	m, err := s.State.Machine(id)
   392  	c.Assert(err, jc.ErrorIsNil)
   393  	c.Assert(m.SetHasVote(hasVote), gc.IsNil)
   394  }
   395  
   396  func (s *UnitSuite) TestRemoveUnitMachineThrashed(c *gc.C) {
   397  	host, err := s.State.AddMachine("quantal", state.JobHostUnits)
   398  	c.Assert(err, jc.ErrorIsNil)
   399  	target, err := s.service.AddUnit()
   400  	c.Assert(err, jc.ErrorIsNil)
   401  	c.Assert(target.AssignToMachine(host), gc.IsNil)
   402  	flip := jujutxn.TestHook{
   403  		Before: func() {
   404  			s.setMachineVote(c, host.Id(), true)
   405  		},
   406  	}
   407  	flop := jujutxn.TestHook{
   408  		Before: func() {
   409  			s.setMachineVote(c, host.Id(), false)
   410  		},
   411  	}
   412  	// You'll need to adjust the flip-flops to match the number of transaction
   413  	// retries.
   414  	defer state.SetTestHooks(c, s.State, flip, flop, flip).Check()
   415  
   416  	c.Assert(target.Destroy(), gc.ErrorMatches, "state changing too quickly; try again soon")
   417  }
   418  
   419  func (s *UnitSuite) TestRemoveUnitMachineRetryVoter(c *gc.C) {
   420  	host, err := s.State.AddMachine("quantal", state.JobHostUnits)
   421  	c.Assert(err, jc.ErrorIsNil)
   422  	target, err := s.service.AddUnit()
   423  	c.Assert(err, jc.ErrorIsNil)
   424  	c.Assert(target.AssignToMachine(host), gc.IsNil)
   425  
   426  	defer state.SetBeforeHooks(c, s.State, func() {
   427  		s.setMachineVote(c, host.Id(), true)
   428  	}, nil).Check()
   429  
   430  	c.Assert(target.Destroy(), gc.IsNil)
   431  	assertLife(c, host, state.Alive)
   432  }
   433  
   434  func (s *UnitSuite) TestRemoveUnitMachineRetryNoVoter(c *gc.C) {
   435  	host, err := s.State.AddMachine("quantal", state.JobHostUnits)
   436  	c.Assert(err, jc.ErrorIsNil)
   437  	target, err := s.service.AddUnit()
   438  	c.Assert(err, jc.ErrorIsNil)
   439  	c.Assert(target.AssignToMachine(host), gc.IsNil)
   440  	c.Assert(host.SetHasVote(true), gc.IsNil)
   441  
   442  	defer state.SetBeforeHooks(c, s.State, func() {
   443  		s.setMachineVote(c, host.Id(), false)
   444  	}, nil).Check()
   445  
   446  	c.Assert(target.Destroy(), gc.IsNil)
   447  	assertLife(c, host, state.Dying)
   448  }
   449  
   450  func (s *UnitSuite) TestRemoveUnitMachineRetryContainer(c *gc.C) {
   451  	host, err := s.State.AddMachine("quantal", state.JobHostUnits)
   452  	c.Assert(err, jc.ErrorIsNil)
   453  	target, err := s.service.AddUnit()
   454  	c.Assert(err, jc.ErrorIsNil)
   455  	c.Assert(target.AssignToMachine(host), gc.IsNil)
   456  	defer state.SetTestHooks(c, s.State, jujutxn.TestHook{
   457  		Before: func() {
   458  			machine, err := s.State.AddMachineInsideMachine(state.MachineTemplate{
   459  				Series: "quantal",
   460  				Jobs:   []state.MachineJob{state.JobHostUnits},
   461  			}, host.Id(), instance.LXC)
   462  			c.Assert(err, jc.ErrorIsNil)
   463  			assertLife(c, machine, state.Alive)
   464  
   465  			// test-setup verification for the disqualifying machine.
   466  			hostHandle, err := s.State.Machine(host.Id())
   467  			c.Assert(err, jc.ErrorIsNil)
   468  			containers, err := hostHandle.Containers()
   469  			c.Assert(err, jc.ErrorIsNil)
   470  			c.Assert(containers, gc.HasLen, 1)
   471  		},
   472  	}).Check()
   473  
   474  	c.Assert(target.Destroy(), gc.IsNil)
   475  	assertLife(c, host, state.Alive)
   476  }
   477  
   478  func (s *UnitSuite) TestRemoveUnitMachineRetryOrCond(c *gc.C) {
   479  	host, err := s.State.AddMachine("quantal", state.JobHostUnits)
   480  	c.Assert(err, jc.ErrorIsNil)
   481  	target, err := s.service.AddUnit()
   482  	c.Assert(err, jc.ErrorIsNil)
   483  	c.Assert(target.AssignToMachine(host), gc.IsNil)
   484  
   485  	// This unit will be colocated in the transaction hook to cause a retry.
   486  	colocated, err := s.service.AddUnit()
   487  	c.Assert(err, jc.ErrorIsNil)
   488  
   489  	c.Assert(host.SetHasVote(true), gc.IsNil)
   490  
   491  	defer state.SetTestHooks(c, s.State, jujutxn.TestHook{
   492  		Before: func() {
   493  			hostHandle, err := s.State.Machine(host.Id())
   494  			c.Assert(err, jc.ErrorIsNil)
   495  
   496  			// Original assertion preventing host removal is no longer valid
   497  			c.Assert(hostHandle.SetHasVote(false), gc.IsNil)
   498  
   499  			// But now the host gets a colocated unit, a different condition preventing removal
   500  			c.Assert(colocated.AssignToMachine(hostHandle), gc.IsNil)
   501  		},
   502  	}).Check()
   503  
   504  	c.Assert(target.Destroy(), gc.IsNil)
   505  	assertLife(c, host, state.Alive)
   506  }
   507  
   508  func (s *UnitSuite) TestRefresh(c *gc.C) {
   509  	unit1, err := s.State.Unit(s.unit.Name())
   510  	c.Assert(err, jc.ErrorIsNil)
   511  
   512  	err = s.unit.SetPassword("arble-farble-dying-yarble")
   513  	c.Assert(err, jc.ErrorIsNil)
   514  	valid := unit1.PasswordValid("arble-farble-dying-yarble")
   515  	c.Assert(valid, jc.IsFalse)
   516  	err = unit1.Refresh()
   517  	c.Assert(err, jc.ErrorIsNil)
   518  	valid = unit1.PasswordValid("arble-farble-dying-yarble")
   519  	c.Assert(valid, jc.IsTrue)
   520  
   521  	err = unit1.EnsureDead()
   522  	c.Assert(err, jc.ErrorIsNil)
   523  	err = unit1.Remove()
   524  	c.Assert(err, jc.ErrorIsNil)
   525  	err = unit1.Refresh()
   526  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   527  }
   528  
   529  func (s *UnitSuite) TestGetSetStatusWhileAlive(c *gc.C) {
   530  	err := s.unit.SetStatus(state.StatusError, "", nil)
   531  	c.Assert(err, gc.ErrorMatches, `cannot set status "error" without info`)
   532  	err = s.unit.SetStatus(state.StatusAllocating, "", nil)
   533  	c.Assert(err, gc.ErrorMatches, `cannot set status "allocating"`)
   534  	err = s.unit.SetStatus(state.StatusFailed, "", nil)
   535  	c.Assert(err, gc.ErrorMatches, `cannot set status "failed"`)
   536  	err = s.unit.SetStatus(state.Status("vliegkat"), "orville", nil)
   537  	c.Assert(err, gc.ErrorMatches, `cannot set invalid status "vliegkat"`)
   538  
   539  	status, info, data, err := s.unit.Status()
   540  	c.Assert(err, jc.ErrorIsNil)
   541  	c.Assert(status, gc.Equals, state.StatusAllocating)
   542  	c.Assert(info, gc.Equals, "")
   543  	c.Assert(data, gc.HasLen, 0)
   544  
   545  	err = s.unit.SetStatus(state.StatusActive, "", nil)
   546  	c.Assert(err, jc.ErrorIsNil)
   547  	status, info, data, err = s.unit.Status()
   548  	c.Assert(err, jc.ErrorIsNil)
   549  	c.Assert(status, gc.Equals, state.StatusActive)
   550  	c.Assert(info, gc.Equals, "")
   551  	c.Assert(data, gc.HasLen, 0)
   552  
   553  	err = s.unit.SetStatus(state.StatusError, "test-hook failed", map[string]interface{}{
   554  		"foo": "bar",
   555  	})
   556  	c.Assert(err, jc.ErrorIsNil)
   557  	status, info, data, err = s.unit.Status()
   558  	c.Assert(err, jc.ErrorIsNil)
   559  	c.Assert(status, gc.Equals, state.StatusError)
   560  	c.Assert(info, gc.Equals, "test-hook failed")
   561  	c.Assert(data, gc.DeepEquals, map[string]interface{}{
   562  		"foo": "bar",
   563  	})
   564  }
   565  
   566  func (s *UnitSuite) TestGetSetStatusWhileNotAlive(c *gc.C) {
   567  	err := s.unit.Destroy()
   568  	c.Assert(err, jc.ErrorIsNil)
   569  	err = s.unit.SetStatus(state.StatusActive, "not really", nil)
   570  	c.Assert(err, gc.ErrorMatches, `cannot set status of unit "wordpress/0": not found or dead`)
   571  	_, _, _, err = s.unit.Status()
   572  	c.Assert(err, gc.ErrorMatches, "status not found")
   573  
   574  	err = s.unit.EnsureDead()
   575  	c.Assert(err, jc.ErrorIsNil)
   576  	err = s.unit.SetStatus(state.StatusActive, "not really", nil)
   577  	c.Assert(err, gc.ErrorMatches, `cannot set status of unit "wordpress/0": not found or dead`)
   578  	_, _, _, err = s.unit.Status()
   579  	c.Assert(err, gc.ErrorMatches, "status not found")
   580  }
   581  
   582  func (s *UnitSuite) TestGetSetStatusDataStandard(c *gc.C) {
   583  	err := s.unit.SetStatus(state.StatusActive, "", nil)
   584  	c.Assert(err, jc.ErrorIsNil)
   585  	_, _, _, err = s.unit.Status()
   586  	c.Assert(err, jc.ErrorIsNil)
   587  
   588  	// Regular status setting with data.
   589  	err = s.unit.SetStatus(state.StatusError, "test-hook failed", map[string]interface{}{
   590  		"1st-key": "one",
   591  		"2nd-key": 2,
   592  		"3rd-key": true,
   593  	})
   594  	c.Assert(err, jc.ErrorIsNil)
   595  
   596  	status, info, data, err := s.unit.Status()
   597  	c.Assert(err, jc.ErrorIsNil)
   598  	c.Assert(status, gc.Equals, state.StatusError)
   599  	c.Assert(info, gc.Equals, "test-hook failed")
   600  	c.Assert(data, gc.DeepEquals, map[string]interface{}{
   601  		"1st-key": "one",
   602  		"2nd-key": 2,
   603  		"3rd-key": true,
   604  	})
   605  }
   606  
   607  func (s *UnitSuite) TestGetSetStatusDataMongo(c *gc.C) {
   608  	err := s.unit.SetStatus(state.StatusActive, "", nil)
   609  	c.Assert(err, jc.ErrorIsNil)
   610  	_, _, _, err = s.unit.Status()
   611  	c.Assert(err, jc.ErrorIsNil)
   612  
   613  	// Status setting with MongoDB special values.
   614  	err = s.unit.SetStatus(state.StatusError, "mongo", map[string]interface{}{
   615  		`{name: "Joe"}`: "$where",
   616  		"eval":          `eval(function(foo) { return foo; }, "bar")`,
   617  		"mapReduce":     "mapReduce",
   618  		"group":         "group",
   619  	})
   620  	c.Assert(err, jc.ErrorIsNil)
   621  
   622  	status, info, data, err := s.unit.Status()
   623  	c.Assert(err, jc.ErrorIsNil)
   624  	c.Assert(status, gc.Equals, state.StatusError)
   625  	c.Assert(info, gc.Equals, "mongo")
   626  	c.Assert(data, gc.DeepEquals, map[string]interface{}{
   627  		`{name: "Joe"}`: "$where",
   628  		"eval":          `eval(function(foo) { return foo; }, "bar")`,
   629  		"mapReduce":     "mapReduce",
   630  		"group":         "group",
   631  	})
   632  }
   633  
   634  func (s *UnitSuite) TestGetSetStatusDataChange(c *gc.C) {
   635  	err := s.unit.SetStatus(state.StatusActive, "", nil)
   636  	c.Assert(err, jc.ErrorIsNil)
   637  	_, _, _, err = s.unit.Status()
   638  	c.Assert(err, jc.ErrorIsNil)
   639  
   640  	// Status setting and changing data afterwards.
   641  	data := map[string]interface{}{
   642  		"1st-key": "one",
   643  		"2nd-key": 2,
   644  		"3rd-key": true,
   645  	}
   646  	err = s.unit.SetStatus(state.StatusError, "test-hook failed", data)
   647  	c.Assert(err, jc.ErrorIsNil)
   648  	data["4th-key"] = 4.0
   649  
   650  	status, info, data, err := s.unit.Status()
   651  	c.Assert(err, jc.ErrorIsNil)
   652  	c.Assert(status, gc.Equals, state.StatusError)
   653  	c.Assert(info, gc.Equals, "test-hook failed")
   654  	c.Assert(data, gc.DeepEquals, map[string]interface{}{
   655  		"1st-key": "one",
   656  		"2nd-key": 2,
   657  		"3rd-key": true,
   658  	})
   659  
   660  	// Set status data to nil, so an empty map will be returned.
   661  	err = s.unit.SetStatus(state.StatusActive, "", nil)
   662  	c.Assert(err, jc.ErrorIsNil)
   663  
   664  	status, info, data, err = s.unit.Status()
   665  	c.Assert(err, jc.ErrorIsNil)
   666  	c.Assert(status, gc.Equals, state.StatusActive)
   667  	c.Assert(info, gc.Equals, "")
   668  	c.Assert(data, gc.HasLen, 0)
   669  }
   670  
   671  func (s *UnitSuite) TestSetCharmURLSuccess(c *gc.C) {
   672  	preventUnitDestroyRemove(c, s.unit)
   673  	curl, ok := s.unit.CharmURL()
   674  	c.Assert(ok, jc.IsFalse)
   675  	c.Assert(curl, gc.IsNil)
   676  
   677  	err := s.unit.SetCharmURL(s.charm.URL())
   678  	c.Assert(err, jc.ErrorIsNil)
   679  
   680  	curl, ok = s.unit.CharmURL()
   681  	c.Assert(ok, jc.IsTrue)
   682  	c.Assert(curl, gc.DeepEquals, s.charm.URL())
   683  }
   684  
   685  func (s *UnitSuite) TestSetCharmURLFailures(c *gc.C) {
   686  	preventUnitDestroyRemove(c, s.unit)
   687  	curl, ok := s.unit.CharmURL()
   688  	c.Assert(ok, jc.IsFalse)
   689  	c.Assert(curl, gc.IsNil)
   690  
   691  	err := s.unit.SetCharmURL(nil)
   692  	c.Assert(err, gc.ErrorMatches, "cannot set nil charm url")
   693  
   694  	err = s.unit.SetCharmURL(charm.MustParseURL("cs:missing/one-1"))
   695  	c.Assert(err, gc.ErrorMatches, `unknown charm url "cs:missing/one-1"`)
   696  
   697  	err = s.unit.EnsureDead()
   698  	c.Assert(err, jc.ErrorIsNil)
   699  	err = s.unit.SetCharmURL(s.charm.URL())
   700  	c.Assert(err, gc.Equals, state.ErrDead)
   701  }
   702  
   703  func (s *UnitSuite) TestSetCharmURLWithRemovedUnit(c *gc.C) {
   704  	err := s.unit.Destroy()
   705  	c.Assert(err, jc.ErrorIsNil)
   706  	assertRemoved(c, s.unit)
   707  
   708  	err = s.unit.SetCharmURL(s.charm.URL())
   709  	c.Assert(err, gc.Equals, state.ErrDead)
   710  }
   711  
   712  func (s *UnitSuite) TestSetCharmURLWithDyingUnit(c *gc.C) {
   713  	preventUnitDestroyRemove(c, s.unit)
   714  	err := s.unit.Destroy()
   715  	c.Assert(err, jc.ErrorIsNil)
   716  	assertLife(c, s.unit, state.Dying)
   717  
   718  	err = s.unit.SetCharmURL(s.charm.URL())
   719  	c.Assert(err, jc.ErrorIsNil)
   720  
   721  	curl, ok := s.unit.CharmURL()
   722  	c.Assert(ok, jc.IsTrue)
   723  	c.Assert(curl, gc.DeepEquals, s.charm.URL())
   724  }
   725  
   726  func (s *UnitSuite) TestSetCharmURLRetriesWithDeadUnit(c *gc.C) {
   727  	preventUnitDestroyRemove(c, s.unit)
   728  
   729  	defer state.SetBeforeHooks(c, s.State, func() {
   730  		err := s.unit.Destroy()
   731  		c.Assert(err, jc.ErrorIsNil)
   732  		err = s.unit.EnsureDead()
   733  		c.Assert(err, jc.ErrorIsNil)
   734  		assertLife(c, s.unit, state.Dead)
   735  	}).Check()
   736  
   737  	err := s.unit.SetCharmURL(s.charm.URL())
   738  	c.Assert(err, gc.Equals, state.ErrDead)
   739  }
   740  
   741  func (s *UnitSuite) TestSetCharmURLRetriesWithDifferentURL(c *gc.C) {
   742  	sch := s.AddConfigCharm(c, "wordpress", emptyConfig, 2)
   743  
   744  	defer state.SetTestHooks(c, s.State,
   745  		jujutxn.TestHook{
   746  			Before: func() {
   747  				// Set a different charm to force a retry: first on
   748  				// the service, so the settings are created, then on
   749  				// the unit.
   750  				err := s.service.SetCharm(sch, false)
   751  				c.Assert(err, jc.ErrorIsNil)
   752  				err = s.unit.SetCharmURL(sch.URL())
   753  				c.Assert(err, jc.ErrorIsNil)
   754  			},
   755  			After: func() {
   756  				// Set back the same charm on the service, so the
   757  				// settings refcount is correct..
   758  				err := s.service.SetCharm(s.charm, false)
   759  				c.Assert(err, jc.ErrorIsNil)
   760  			},
   761  		},
   762  		jujutxn.TestHook{
   763  			Before: nil, // Ensure there will be a retry.
   764  			After: func() {
   765  				// Verify it worked after the second attempt.
   766  				err := s.unit.Refresh()
   767  				c.Assert(err, jc.ErrorIsNil)
   768  				currentURL, hasURL := s.unit.CharmURL()
   769  				c.Assert(currentURL, jc.DeepEquals, s.charm.URL())
   770  				c.Assert(hasURL, jc.IsTrue)
   771  			},
   772  		},
   773  	).Check()
   774  
   775  	err := s.unit.SetCharmURL(s.charm.URL())
   776  	c.Assert(err, jc.ErrorIsNil)
   777  }
   778  
   779  func (s *UnitSuite) TestDestroySetStatusRetry(c *gc.C) {
   780  	defer state.SetRetryHooks(c, s.State, func() {
   781  		err := s.unit.SetStatus(state.StatusActive, "", nil)
   782  		c.Assert(err, jc.ErrorIsNil)
   783  	}, func() {
   784  		assertLife(c, s.unit, state.Dying)
   785  	}).Check()
   786  
   787  	err := s.unit.Destroy()
   788  	c.Assert(err, jc.ErrorIsNil)
   789  }
   790  
   791  func (s *UnitSuite) TestDestroySetCharmRetry(c *gc.C) {
   792  	defer state.SetRetryHooks(c, s.State, func() {
   793  		err := s.unit.SetCharmURL(s.charm.URL())
   794  		c.Assert(err, jc.ErrorIsNil)
   795  	}, func() {
   796  		assertRemoved(c, s.unit)
   797  	}).Check()
   798  
   799  	err := s.unit.Destroy()
   800  	c.Assert(err, jc.ErrorIsNil)
   801  }
   802  
   803  func (s *UnitSuite) TestDestroyChangeCharmRetry(c *gc.C) {
   804  	err := s.unit.SetCharmURL(s.charm.URL())
   805  	c.Assert(err, jc.ErrorIsNil)
   806  	newCharm := s.AddConfigCharm(c, "mysql", "options: {}", 99)
   807  	err = s.service.SetCharm(newCharm, false)
   808  	c.Assert(err, jc.ErrorIsNil)
   809  
   810  	defer state.SetRetryHooks(c, s.State, func() {
   811  		err := s.unit.SetCharmURL(newCharm.URL())
   812  		c.Assert(err, jc.ErrorIsNil)
   813  	}, func() {
   814  		assertRemoved(c, s.unit)
   815  	}).Check()
   816  
   817  	err = s.unit.Destroy()
   818  	c.Assert(err, jc.ErrorIsNil)
   819  }
   820  
   821  func (s *UnitSuite) TestDestroyAssignRetry(c *gc.C) {
   822  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
   823  	c.Assert(err, jc.ErrorIsNil)
   824  
   825  	defer state.SetRetryHooks(c, s.State, func() {
   826  		err := s.unit.AssignToMachine(machine)
   827  		c.Assert(err, jc.ErrorIsNil)
   828  	}, func() {
   829  		assertRemoved(c, s.unit)
   830  		// Also check the unit ref was properly removed from the machine doc --
   831  		// if it weren't, we wouldn't be able to make the machine Dead.
   832  		err := machine.EnsureDead()
   833  		c.Assert(err, jc.ErrorIsNil)
   834  	}).Check()
   835  
   836  	err = s.unit.Destroy()
   837  	c.Assert(err, jc.ErrorIsNil)
   838  }
   839  
   840  func (s *UnitSuite) TestDestroyUnassignRetry(c *gc.C) {
   841  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
   842  	c.Assert(err, jc.ErrorIsNil)
   843  	err = s.unit.AssignToMachine(machine)
   844  	c.Assert(err, jc.ErrorIsNil)
   845  
   846  	defer state.SetRetryHooks(c, s.State, func() {
   847  		err := s.unit.UnassignFromMachine()
   848  		c.Assert(err, jc.ErrorIsNil)
   849  	}, func() {
   850  		assertRemoved(c, s.unit)
   851  	}).Check()
   852  
   853  	err = s.unit.Destroy()
   854  	c.Assert(err, jc.ErrorIsNil)
   855  }
   856  
   857  func (s *UnitSuite) TestShortCircuitDestroyUnit(c *gc.C) {
   858  	// A unit that has not set any status is removed directly.
   859  	err := s.unit.Destroy()
   860  	c.Assert(err, jc.ErrorIsNil)
   861  	c.Assert(s.unit.Life(), gc.Equals, state.Dying)
   862  	assertRemoved(c, s.unit)
   863  }
   864  
   865  func (s *UnitSuite) TestCannotShortCircuitDestroyWithSubordinates(c *gc.C) {
   866  	// A unit with subordinates is just set to Dying.
   867  	s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging"))
   868  	eps, err := s.State.InferEndpoints("logging", "wordpress")
   869  	c.Assert(err, jc.ErrorIsNil)
   870  	rel, err := s.State.AddRelation(eps...)
   871  	c.Assert(err, jc.ErrorIsNil)
   872  	ru, err := rel.Unit(s.unit)
   873  	c.Assert(err, jc.ErrorIsNil)
   874  	err = ru.EnterScope(nil)
   875  	c.Assert(err, jc.ErrorIsNil)
   876  	err = s.unit.Destroy()
   877  	c.Assert(err, jc.ErrorIsNil)
   878  	c.Assert(s.unit.Life(), gc.Equals, state.Dying)
   879  	assertLife(c, s.unit, state.Dying)
   880  }
   881  
   882  func (s *UnitSuite) TestCannotShortCircuitDestroyWithStatus(c *gc.C) {
   883  	for i, test := range []struct {
   884  		status state.Status
   885  		info   string
   886  	}{{
   887  		state.StatusActive, "",
   888  	}, {
   889  		state.StatusError, "blah",
   890  	}, {
   891  		state.StatusStopping, "",
   892  	}} {
   893  		c.Logf("test %d: %s", i, test.status)
   894  		unit, err := s.service.AddUnit()
   895  		c.Assert(err, jc.ErrorIsNil)
   896  		err = unit.SetStatus(test.status, test.info, nil)
   897  		c.Assert(err, jc.ErrorIsNil)
   898  		err = unit.Destroy()
   899  		c.Assert(err, jc.ErrorIsNil)
   900  		c.Assert(unit.Life(), gc.Equals, state.Dying)
   901  		assertLife(c, unit, state.Dying)
   902  	}
   903  }
   904  
   905  func (s *UnitSuite) TestShortCircuitDestroyWithProvisionedMachine(c *gc.C) {
   906  	// A unit assigned to a provisioned machine is still removed directly so
   907  	// long as it has not set status.
   908  	err := s.unit.AssignToNewMachine()
   909  	c.Assert(err, jc.ErrorIsNil)
   910  	mid, err := s.unit.AssignedMachineId()
   911  	c.Assert(err, jc.ErrorIsNil)
   912  	machine, err := s.State.Machine(mid)
   913  	c.Assert(err, jc.ErrorIsNil)
   914  	err = machine.SetProvisioned("i-malive", "fake_nonce", nil)
   915  	c.Assert(err, jc.ErrorIsNil)
   916  	err = s.unit.Destroy()
   917  	c.Assert(err, jc.ErrorIsNil)
   918  	c.Assert(s.unit.Life(), gc.Equals, state.Dying)
   919  	assertRemoved(c, s.unit)
   920  }
   921  
   922  func assertLife(c *gc.C, entity state.Living, life state.Life) {
   923  	c.Assert(entity.Refresh(), gc.IsNil)
   924  	c.Assert(entity.Life(), gc.Equals, life)
   925  }
   926  
   927  func assertRemoved(c *gc.C, entity state.Living) {
   928  	err := entity.Refresh()
   929  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   930  	err = entity.Destroy()
   931  	c.Assert(err, jc.ErrorIsNil)
   932  	if entity, ok := entity.(state.AgentLiving); ok {
   933  		err = entity.EnsureDead()
   934  		c.Assert(err, jc.ErrorIsNil)
   935  		err = entity.Remove()
   936  		c.Assert(err, jc.ErrorIsNil)
   937  	}
   938  }
   939  
   940  func (s *UnitSuite) TestTag(c *gc.C) {
   941  	c.Assert(s.unit.Tag().String(), gc.Equals, "unit-wordpress-0")
   942  }
   943  
   944  func (s *UnitSuite) TestSetPassword(c *gc.C) {
   945  	preventUnitDestroyRemove(c, s.unit)
   946  	testSetPassword(c, func() (state.Authenticator, error) {
   947  		return s.State.Unit(s.unit.Name())
   948  	})
   949  }
   950  
   951  func (s *UnitSuite) TestSetAgentCompatPassword(c *gc.C) {
   952  	e, err := s.State.Unit(s.unit.Name())
   953  	c.Assert(err, jc.ErrorIsNil)
   954  	testSetAgentCompatPassword(c, e)
   955  }
   956  
   957  func (s *UnitSuite) TestUnitSetAgentPresence(c *gc.C) {
   958  	alive, err := s.unit.AgentPresence()
   959  	c.Assert(err, jc.ErrorIsNil)
   960  	c.Assert(alive, jc.IsFalse)
   961  
   962  	pinger, err := s.unit.SetAgentPresence()
   963  	c.Assert(err, jc.ErrorIsNil)
   964  	c.Assert(pinger, gc.NotNil)
   965  	defer pinger.Stop()
   966  
   967  	s.State.StartSync()
   968  	alive, err = s.unit.AgentPresence()
   969  	c.Assert(err, jc.ErrorIsNil)
   970  	c.Assert(alive, jc.IsTrue)
   971  }
   972  
   973  func (s *UnitSuite) TestUnitWaitAgentPresence(c *gc.C) {
   974  	alive, err := s.unit.AgentPresence()
   975  	c.Assert(err, jc.ErrorIsNil)
   976  	c.Assert(alive, jc.IsFalse)
   977  
   978  	err = s.unit.WaitAgentPresence(coretesting.ShortWait)
   979  	c.Assert(err, gc.ErrorMatches, `waiting for agent of unit "wordpress/0": still not alive after timeout`)
   980  
   981  	pinger, err := s.unit.SetAgentPresence()
   982  	c.Assert(err, jc.ErrorIsNil)
   983  
   984  	s.State.StartSync()
   985  	err = s.unit.WaitAgentPresence(coretesting.LongWait)
   986  	c.Assert(err, jc.ErrorIsNil)
   987  
   988  	alive, err = s.unit.AgentPresence()
   989  	c.Assert(err, jc.ErrorIsNil)
   990  	c.Assert(alive, jc.IsTrue)
   991  
   992  	err = pinger.Kill()
   993  	c.Assert(err, jc.ErrorIsNil)
   994  
   995  	s.State.StartSync()
   996  
   997  	alive, err = s.unit.AgentPresence()
   998  	c.Assert(err, jc.ErrorIsNil)
   999  	c.Assert(alive, jc.IsFalse)
  1000  }
  1001  
  1002  func (s *UnitSuite) TestResolve(c *gc.C) {
  1003  	err := s.unit.Resolve(false)
  1004  	c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" is not in an error state`)
  1005  	err = s.unit.Resolve(true)
  1006  	c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" is not in an error state`)
  1007  
  1008  	err = s.unit.SetStatus(state.StatusError, "gaaah", nil)
  1009  	c.Assert(err, jc.ErrorIsNil)
  1010  	err = s.unit.Resolve(false)
  1011  	c.Assert(err, jc.ErrorIsNil)
  1012  	err = s.unit.Resolve(true)
  1013  	c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": already resolved`)
  1014  	c.Assert(s.unit.Resolved(), gc.Equals, state.ResolvedNoHooks)
  1015  
  1016  	err = s.unit.ClearResolved()
  1017  	c.Assert(err, jc.ErrorIsNil)
  1018  	err = s.unit.Resolve(true)
  1019  	c.Assert(err, jc.ErrorIsNil)
  1020  	err = s.unit.Resolve(false)
  1021  	c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": already resolved`)
  1022  	c.Assert(s.unit.Resolved(), gc.Equals, state.ResolvedRetryHooks)
  1023  }
  1024  
  1025  func (s *UnitSuite) TestGetSetClearResolved(c *gc.C) {
  1026  	mode := s.unit.Resolved()
  1027  	c.Assert(mode, gc.Equals, state.ResolvedNone)
  1028  
  1029  	err := s.unit.SetResolved(state.ResolvedNoHooks)
  1030  	c.Assert(err, jc.ErrorIsNil)
  1031  	err = s.unit.SetResolved(state.ResolvedNoHooks)
  1032  	c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": already resolved`)
  1033  
  1034  	mode = s.unit.Resolved()
  1035  	c.Assert(mode, gc.Equals, state.ResolvedNoHooks)
  1036  	err = s.unit.Refresh()
  1037  	c.Assert(err, jc.ErrorIsNil)
  1038  	mode = s.unit.Resolved()
  1039  	c.Assert(mode, gc.Equals, state.ResolvedNoHooks)
  1040  
  1041  	err = s.unit.ClearResolved()
  1042  	c.Assert(err, jc.ErrorIsNil)
  1043  	mode = s.unit.Resolved()
  1044  	c.Assert(mode, gc.Equals, state.ResolvedNone)
  1045  	err = s.unit.Refresh()
  1046  	c.Assert(err, jc.ErrorIsNil)
  1047  	mode = s.unit.Resolved()
  1048  	c.Assert(mode, gc.Equals, state.ResolvedNone)
  1049  	err = s.unit.ClearResolved()
  1050  	c.Assert(err, jc.ErrorIsNil)
  1051  
  1052  	err = s.unit.SetResolved(state.ResolvedNone)
  1053  	c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": invalid error resolution mode: ""`)
  1054  	err = s.unit.SetResolved(state.ResolvedMode("foo"))
  1055  	c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": invalid error resolution mode: "foo"`)
  1056  }
  1057  
  1058  func (s *UnitSuite) TestOpenedPorts(c *gc.C) {
  1059  	// Verify ports can be opened and closed only when the unit has
  1060  	// assigned machine.
  1061  	err := s.unit.OpenPort("tcp", 10)
  1062  	c.Assert(errors.Cause(err), jc.Satisfies, state.IsNotAssigned)
  1063  	err = s.unit.OpenPorts("tcp", 10, 20)
  1064  	c.Assert(errors.Cause(err), jc.Satisfies, state.IsNotAssigned)
  1065  	err = s.unit.ClosePort("tcp", 10)
  1066  	c.Assert(errors.Cause(err), jc.Satisfies, state.IsNotAssigned)
  1067  	err = s.unit.ClosePorts("tcp", 10, 20)
  1068  	c.Assert(errors.Cause(err), jc.Satisfies, state.IsNotAssigned)
  1069  	open, err := s.unit.OpenedPorts()
  1070  	c.Assert(errors.Cause(err), jc.Satisfies, state.IsNotAssigned)
  1071  	c.Assert(open, gc.HasLen, 0)
  1072  
  1073  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
  1074  	c.Assert(err, jc.ErrorIsNil)
  1075  	err = s.unit.AssignToMachine(machine)
  1076  	c.Assert(err, jc.ErrorIsNil)
  1077  
  1078  	// Verify no open ports before activity.
  1079  	open, err = s.unit.OpenedPorts()
  1080  	c.Assert(err, jc.ErrorIsNil)
  1081  	c.Assert(open, gc.HasLen, 0)
  1082  
  1083  	// Now open and close ports and ranges and check.
  1084  
  1085  	err = s.unit.OpenPort("tcp", 80)
  1086  	c.Assert(err, jc.ErrorIsNil)
  1087  	err = s.unit.OpenPorts("udp", 100, 200)
  1088  	c.Assert(err, jc.ErrorIsNil)
  1089  	open, err = s.unit.OpenedPorts()
  1090  	c.Assert(err, jc.ErrorIsNil)
  1091  	c.Assert(open, gc.DeepEquals, []network.PortRange{
  1092  		{80, 80, "tcp"},
  1093  		{100, 200, "udp"},
  1094  	})
  1095  
  1096  	err = s.unit.OpenPort("udp", 53)
  1097  	c.Assert(err, jc.ErrorIsNil)
  1098  	open, err = s.unit.OpenedPorts()
  1099  	c.Assert(err, jc.ErrorIsNil)
  1100  	c.Assert(open, gc.DeepEquals, []network.PortRange{
  1101  		{80, 80, "tcp"},
  1102  		{53, 53, "udp"},
  1103  		{100, 200, "udp"},
  1104  	})
  1105  
  1106  	err = s.unit.OpenPorts("tcp", 53, 55)
  1107  	c.Assert(err, jc.ErrorIsNil)
  1108  	open, err = s.unit.OpenedPorts()
  1109  	c.Assert(err, jc.ErrorIsNil)
  1110  	c.Assert(open, gc.DeepEquals, []network.PortRange{
  1111  		{53, 55, "tcp"},
  1112  		{80, 80, "tcp"},
  1113  		{53, 53, "udp"},
  1114  		{100, 200, "udp"},
  1115  	})
  1116  
  1117  	err = s.unit.OpenPort("tcp", 443)
  1118  	c.Assert(err, jc.ErrorIsNil)
  1119  	open, err = s.unit.OpenedPorts()
  1120  	c.Assert(err, jc.ErrorIsNil)
  1121  	c.Assert(open, gc.DeepEquals, []network.PortRange{
  1122  		{53, 55, "tcp"},
  1123  		{80, 80, "tcp"},
  1124  		{443, 443, "tcp"},
  1125  		{53, 53, "udp"},
  1126  		{100, 200, "udp"},
  1127  	})
  1128  
  1129  	err = s.unit.ClosePort("tcp", 80)
  1130  	c.Assert(err, jc.ErrorIsNil)
  1131  	open, err = s.unit.OpenedPorts()
  1132  	c.Assert(err, jc.ErrorIsNil)
  1133  	c.Assert(open, gc.DeepEquals, []network.PortRange{
  1134  		{53, 55, "tcp"},
  1135  		{443, 443, "tcp"},
  1136  		{53, 53, "udp"},
  1137  		{100, 200, "udp"},
  1138  	})
  1139  
  1140  	err = s.unit.ClosePorts("udp", 100, 200)
  1141  	c.Assert(err, jc.ErrorIsNil)
  1142  	open, err = s.unit.OpenedPorts()
  1143  	c.Assert(err, jc.ErrorIsNil)
  1144  	c.Assert(open, gc.DeepEquals, []network.PortRange{
  1145  		{53, 55, "tcp"},
  1146  		{443, 443, "tcp"},
  1147  		{53, 53, "udp"},
  1148  	})
  1149  }
  1150  
  1151  func (s *UnitSuite) TestOpenClosePortWhenDying(c *gc.C) {
  1152  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
  1153  	c.Assert(err, jc.ErrorIsNil)
  1154  	err = s.unit.AssignToMachine(machine)
  1155  	c.Assert(err, jc.ErrorIsNil)
  1156  
  1157  	preventUnitDestroyRemove(c, s.unit)
  1158  	testWhenDying(c, s.unit, noErr, contentionErr, func() error {
  1159  		err := s.unit.OpenPort("tcp", 20)
  1160  		if err != nil {
  1161  			return err
  1162  		}
  1163  		err = s.unit.OpenPorts("tcp", 10, 15)
  1164  		if err != nil {
  1165  			return err
  1166  		}
  1167  		err = s.unit.Refresh()
  1168  		if err != nil {
  1169  			return err
  1170  		}
  1171  		err = s.unit.ClosePort("tcp", 20)
  1172  		if err != nil {
  1173  			return err
  1174  		}
  1175  		return s.unit.ClosePorts("tcp", 10, 15)
  1176  	})
  1177  }
  1178  
  1179  func (s *UnitSuite) TestRemoveLastUnitOnMachineRemovesAllPorts(c *gc.C) {
  1180  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
  1181  	c.Assert(err, jc.ErrorIsNil)
  1182  	err = s.unit.AssignToMachine(machine)
  1183  	c.Assert(err, jc.ErrorIsNil)
  1184  
  1185  	ports, err := machine.AllPorts()
  1186  	c.Assert(err, jc.ErrorIsNil)
  1187  	c.Assert(ports, gc.HasLen, 0)
  1188  
  1189  	err = s.unit.OpenPorts("tcp", 100, 200)
  1190  	c.Assert(err, jc.ErrorIsNil)
  1191  
  1192  	ports, err = machine.AllPorts()
  1193  	c.Assert(err, jc.ErrorIsNil)
  1194  	c.Assert(ports, gc.HasLen, 1)
  1195  	c.Assert(ports[0].PortsForUnit(s.unit.Name()), jc.DeepEquals, []state.PortRange{
  1196  		{s.unit.Name(), 100, 200, "tcp"},
  1197  	})
  1198  
  1199  	// Now remove the unit and check again.
  1200  	err = s.unit.EnsureDead()
  1201  	c.Assert(err, jc.ErrorIsNil)
  1202  	err = s.unit.Remove()
  1203  	c.Assert(err, jc.ErrorIsNil)
  1204  	err = s.unit.Refresh()
  1205  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1206  
  1207  	// Because that was the only range open, the ports doc will be
  1208  	// removed as well.
  1209  	ports, err = machine.AllPorts()
  1210  	c.Assert(err, jc.ErrorIsNil)
  1211  	c.Assert(ports, gc.HasLen, 0)
  1212  }
  1213  
  1214  func (s *UnitSuite) TestRemoveUnitRemovesItsPortsOnly(c *gc.C) {
  1215  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
  1216  	c.Assert(err, jc.ErrorIsNil)
  1217  	err = s.unit.AssignToMachine(machine)
  1218  	c.Assert(err, jc.ErrorIsNil)
  1219  
  1220  	otherUnit, err := s.service.AddUnit()
  1221  	c.Assert(err, jc.ErrorIsNil)
  1222  	err = otherUnit.AssignToMachine(machine)
  1223  	c.Assert(err, jc.ErrorIsNil)
  1224  
  1225  	ports, err := machine.AllPorts()
  1226  	c.Assert(err, jc.ErrorIsNil)
  1227  	c.Assert(ports, gc.HasLen, 0)
  1228  
  1229  	err = s.unit.OpenPorts("tcp", 100, 200)
  1230  	c.Assert(err, jc.ErrorIsNil)
  1231  	err = otherUnit.OpenPorts("udp", 300, 400)
  1232  	c.Assert(err, jc.ErrorIsNil)
  1233  
  1234  	ports, err = machine.AllPorts()
  1235  	c.Assert(err, jc.ErrorIsNil)
  1236  	c.Assert(ports, gc.HasLen, 1)
  1237  	c.Assert(ports[0].PortsForUnit(s.unit.Name()), jc.DeepEquals, []state.PortRange{
  1238  		{s.unit.Name(), 100, 200, "tcp"},
  1239  	})
  1240  	c.Assert(ports[0].PortsForUnit(otherUnit.Name()), jc.DeepEquals, []state.PortRange{
  1241  		{otherUnit.Name(), 300, 400, "udp"},
  1242  	})
  1243  
  1244  	// Now remove the first unit and check again.
  1245  	err = s.unit.EnsureDead()
  1246  	c.Assert(err, jc.ErrorIsNil)
  1247  	err = s.unit.Remove()
  1248  	c.Assert(err, jc.ErrorIsNil)
  1249  	err = s.unit.Refresh()
  1250  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1251  
  1252  	// Verify only otherUnit still has open ports.
  1253  	ports, err = machine.AllPorts()
  1254  	c.Assert(err, jc.ErrorIsNil)
  1255  	c.Assert(ports, gc.HasLen, 1)
  1256  	c.Assert(ports[0].PortsForUnit(s.unit.Name()), gc.HasLen, 0)
  1257  	c.Assert(ports[0].PortsForUnit(otherUnit.Name()), jc.DeepEquals, []state.PortRange{
  1258  		{otherUnit.Name(), 300, 400, "udp"},
  1259  	})
  1260  }
  1261  
  1262  func (s *UnitSuite) TestSetClearResolvedWhenNotAlive(c *gc.C) {
  1263  	preventUnitDestroyRemove(c, s.unit)
  1264  	err := s.unit.Destroy()
  1265  	c.Assert(err, jc.ErrorIsNil)
  1266  	err = s.unit.SetResolved(state.ResolvedNoHooks)
  1267  	c.Assert(err, jc.ErrorIsNil)
  1268  	err = s.unit.Refresh()
  1269  	c.Assert(err, jc.ErrorIsNil)
  1270  	c.Assert(s.unit.Resolved(), gc.Equals, state.ResolvedNoHooks)
  1271  	err = s.unit.ClearResolved()
  1272  	c.Assert(err, jc.ErrorIsNil)
  1273  
  1274  	err = s.unit.EnsureDead()
  1275  	c.Assert(err, jc.ErrorIsNil)
  1276  	err = s.unit.SetResolved(state.ResolvedRetryHooks)
  1277  	c.Assert(err, gc.ErrorMatches, deadErr)
  1278  	err = s.unit.ClearResolved()
  1279  	c.Assert(err, jc.ErrorIsNil)
  1280  }
  1281  
  1282  func (s *UnitSuite) TestSubordinateChangeInPrincipal(c *gc.C) {
  1283  	subCharm := s.AddTestingCharm(c, "logging")
  1284  	for i := 0; i < 2; i++ {
  1285  		// Note: subordinate units can only be created as a side effect of a
  1286  		// principal entering scope; and a given principal can only have a
  1287  		// single subordinate unit of each service.
  1288  		name := "logging" + strconv.Itoa(i)
  1289  		s.AddTestingService(c, name, subCharm)
  1290  		eps, err := s.State.InferEndpoints(name, "wordpress")
  1291  		c.Assert(err, jc.ErrorIsNil)
  1292  		rel, err := s.State.AddRelation(eps...)
  1293  		c.Assert(err, jc.ErrorIsNil)
  1294  		ru, err := rel.Unit(s.unit)
  1295  		c.Assert(err, jc.ErrorIsNil)
  1296  		err = ru.EnterScope(nil)
  1297  		c.Assert(err, jc.ErrorIsNil)
  1298  	}
  1299  
  1300  	err := s.unit.Refresh()
  1301  	c.Assert(err, jc.ErrorIsNil)
  1302  	subordinates := s.unit.SubordinateNames()
  1303  	c.Assert(subordinates, gc.DeepEquals, []string{"logging0/0", "logging1/0"})
  1304  
  1305  	su1, err := s.State.Unit("logging1/0")
  1306  	c.Assert(err, jc.ErrorIsNil)
  1307  	err = su1.EnsureDead()
  1308  	c.Assert(err, jc.ErrorIsNil)
  1309  	err = su1.Remove()
  1310  	c.Assert(err, jc.ErrorIsNil)
  1311  	err = s.unit.Refresh()
  1312  	c.Assert(err, jc.ErrorIsNil)
  1313  	subordinates = s.unit.SubordinateNames()
  1314  	c.Assert(subordinates, gc.DeepEquals, []string{"logging0/0"})
  1315  }
  1316  
  1317  func (s *UnitSuite) TestDeathWithSubordinates(c *gc.C) {
  1318  	// Check that units can become dead when they've never had subordinates.
  1319  	u, err := s.service.AddUnit()
  1320  	c.Assert(err, jc.ErrorIsNil)
  1321  	err = u.EnsureDead()
  1322  	c.Assert(err, jc.ErrorIsNil)
  1323  
  1324  	// Create a new unit and add a subordinate.
  1325  	u, err = s.service.AddUnit()
  1326  	c.Assert(err, jc.ErrorIsNil)
  1327  	s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging"))
  1328  	c.Assert(err, jc.ErrorIsNil)
  1329  	eps, err := s.State.InferEndpoints("logging", "wordpress")
  1330  	c.Assert(err, jc.ErrorIsNil)
  1331  	rel, err := s.State.AddRelation(eps...)
  1332  	c.Assert(err, jc.ErrorIsNil)
  1333  	ru, err := rel.Unit(u)
  1334  	c.Assert(err, jc.ErrorIsNil)
  1335  	err = ru.EnterScope(nil)
  1336  	c.Assert(err, jc.ErrorIsNil)
  1337  
  1338  	// Check the unit cannot become Dead, but can become Dying...
  1339  	err = u.EnsureDead()
  1340  	c.Assert(err, gc.Equals, state.ErrUnitHasSubordinates)
  1341  	err = u.Destroy()
  1342  	c.Assert(err, jc.ErrorIsNil)
  1343  
  1344  	// ...and that it still can't become Dead now it's Dying.
  1345  	err = u.EnsureDead()
  1346  	c.Assert(err, gc.Equals, state.ErrUnitHasSubordinates)
  1347  
  1348  	// Make the subordinate Dead and check the principal still cannot be removed.
  1349  	sub, err := s.State.Unit("logging/0")
  1350  	c.Assert(err, jc.ErrorIsNil)
  1351  	err = sub.EnsureDead()
  1352  	c.Assert(err, jc.ErrorIsNil)
  1353  	err = u.EnsureDead()
  1354  	c.Assert(err, gc.Equals, state.ErrUnitHasSubordinates)
  1355  
  1356  	// remove the subordinate and check the principal can finally become Dead.
  1357  	err = sub.Remove()
  1358  	c.Assert(err, jc.ErrorIsNil)
  1359  	err = u.EnsureDead()
  1360  	c.Assert(err, jc.ErrorIsNil)
  1361  }
  1362  
  1363  func (s *UnitSuite) TestPrincipalName(c *gc.C) {
  1364  	subCharm := s.AddTestingCharm(c, "logging")
  1365  	s.AddTestingService(c, "logging", subCharm)
  1366  	eps, err := s.State.InferEndpoints("logging", "wordpress")
  1367  	c.Assert(err, jc.ErrorIsNil)
  1368  	rel, err := s.State.AddRelation(eps...)
  1369  	c.Assert(err, jc.ErrorIsNil)
  1370  	ru, err := rel.Unit(s.unit)
  1371  	c.Assert(err, jc.ErrorIsNil)
  1372  	err = ru.EnterScope(nil)
  1373  	c.Assert(err, jc.ErrorIsNil)
  1374  
  1375  	err = s.unit.Refresh()
  1376  	c.Assert(err, jc.ErrorIsNil)
  1377  	subordinates := s.unit.SubordinateNames()
  1378  	c.Assert(subordinates, gc.DeepEquals, []string{"logging/0"})
  1379  
  1380  	su, err := s.State.Unit("logging/0")
  1381  	c.Assert(err, jc.ErrorIsNil)
  1382  	principal, valid := su.PrincipalName()
  1383  	c.Assert(valid, jc.IsTrue)
  1384  	c.Assert(principal, gc.Equals, s.unit.Name())
  1385  
  1386  	// Calling PrincipalName on a principal unit yields "", false.
  1387  	principal, valid = s.unit.PrincipalName()
  1388  	c.Assert(valid, jc.IsFalse)
  1389  	c.Assert(principal, gc.Equals, "")
  1390  }
  1391  
  1392  func (s *UnitSuite) TestRelations(c *gc.C) {
  1393  	wordpress0 := s.unit
  1394  	mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql"))
  1395  	mysql0, err := mysql.AddUnit()
  1396  	c.Assert(err, jc.ErrorIsNil)
  1397  	eps, err := s.State.InferEndpoints("wordpress", "mysql")
  1398  	c.Assert(err, jc.ErrorIsNil)
  1399  	rel, err := s.State.AddRelation(eps...)
  1400  	c.Assert(err, jc.ErrorIsNil)
  1401  
  1402  	assertEquals := func(actual, expect []*state.Relation) {
  1403  		c.Assert(actual, gc.HasLen, len(expect))
  1404  		for i, a := range actual {
  1405  			c.Assert(a.Id(), gc.Equals, expect[i].Id())
  1406  		}
  1407  	}
  1408  	assertRelationsJoined := func(unit *state.Unit, expect ...*state.Relation) {
  1409  		actual, err := unit.RelationsJoined()
  1410  		c.Assert(err, jc.ErrorIsNil)
  1411  		assertEquals(actual, expect)
  1412  	}
  1413  	assertRelationsInScope := func(unit *state.Unit, expect ...*state.Relation) {
  1414  		actual, err := unit.RelationsInScope()
  1415  		c.Assert(err, jc.ErrorIsNil)
  1416  		assertEquals(actual, expect)
  1417  	}
  1418  	assertRelations := func(unit *state.Unit, expect ...*state.Relation) {
  1419  		assertRelationsInScope(unit, expect...)
  1420  		assertRelationsJoined(unit, expect...)
  1421  	}
  1422  	assertRelations(wordpress0)
  1423  	assertRelations(mysql0)
  1424  
  1425  	mysql0ru, err := rel.Unit(mysql0)
  1426  	c.Assert(err, jc.ErrorIsNil)
  1427  	err = mysql0ru.EnterScope(nil)
  1428  	c.Assert(err, jc.ErrorIsNil)
  1429  	assertRelations(wordpress0)
  1430  	assertRelations(mysql0, rel)
  1431  
  1432  	wordpress0ru, err := rel.Unit(wordpress0)
  1433  	c.Assert(err, jc.ErrorIsNil)
  1434  	err = wordpress0ru.EnterScope(nil)
  1435  	c.Assert(err, jc.ErrorIsNil)
  1436  	assertRelations(wordpress0, rel)
  1437  	assertRelations(mysql0, rel)
  1438  
  1439  	err = mysql0ru.PrepareLeaveScope()
  1440  	c.Assert(err, jc.ErrorIsNil)
  1441  	assertRelations(wordpress0, rel)
  1442  	assertRelationsInScope(mysql0, rel)
  1443  	assertRelationsJoined(mysql0)
  1444  }
  1445  
  1446  func (s *UnitSuite) TestRemove(c *gc.C) {
  1447  	err := s.unit.Remove()
  1448  	c.Assert(err, gc.ErrorMatches, `cannot remove unit "wordpress/0": unit is not dead`)
  1449  	err = s.unit.EnsureDead()
  1450  	c.Assert(err, jc.ErrorIsNil)
  1451  	err = s.unit.Remove()
  1452  	c.Assert(err, jc.ErrorIsNil)
  1453  	err = s.unit.Refresh()
  1454  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1455  	units, err := s.service.AllUnits()
  1456  	c.Assert(err, jc.ErrorIsNil)
  1457  	c.Assert(units, gc.HasLen, 0)
  1458  	err = s.unit.Remove()
  1459  	c.Assert(err, jc.ErrorIsNil)
  1460  }
  1461  
  1462  func (s *UnitSuite) TestRemovePathological(c *gc.C) {
  1463  	// Add a relation between wordpress and mysql...
  1464  	wordpress := s.service
  1465  	wordpress0 := s.unit
  1466  	mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql"))
  1467  	eps, err := s.State.InferEndpoints("wordpress", "mysql")
  1468  	c.Assert(err, jc.ErrorIsNil)
  1469  	rel, err := s.State.AddRelation(eps...)
  1470  	c.Assert(err, jc.ErrorIsNil)
  1471  
  1472  	// The relation holds a reference to wordpress, but that can't keep
  1473  	// wordpress from being removed -- because the relation will be removed
  1474  	// if we destroy wordpress.
  1475  	// However, if a unit of the *other* service joins the relation, that
  1476  	// will add an additional reference and prevent the relation -- and
  1477  	// thus wordpress itself -- from being removed when its last unit is.
  1478  	mysql0, err := mysql.AddUnit()
  1479  	c.Assert(err, jc.ErrorIsNil)
  1480  	mysql0ru, err := rel.Unit(mysql0)
  1481  	c.Assert(err, jc.ErrorIsNil)
  1482  	err = mysql0ru.EnterScope(nil)
  1483  	c.Assert(err, jc.ErrorIsNil)
  1484  
  1485  	// Destroy wordpress, and remove its last unit.
  1486  	err = wordpress.Destroy()
  1487  	c.Assert(err, jc.ErrorIsNil)
  1488  	err = wordpress0.EnsureDead()
  1489  	c.Assert(err, jc.ErrorIsNil)
  1490  	err = wordpress0.Remove()
  1491  	c.Assert(err, jc.ErrorIsNil)
  1492  
  1493  	// Check this didn't kill the service or relation yet...
  1494  	err = wordpress.Refresh()
  1495  	c.Assert(err, jc.ErrorIsNil)
  1496  	err = rel.Refresh()
  1497  	c.Assert(err, jc.ErrorIsNil)
  1498  
  1499  	// ...but when the unit on the other side departs the relation, the
  1500  	// relation and the other service are cleaned up.
  1501  	err = mysql0ru.LeaveScope()
  1502  	c.Assert(err, jc.ErrorIsNil)
  1503  	err = wordpress.Refresh()
  1504  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1505  	err = rel.Refresh()
  1506  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1507  }
  1508  
  1509  func (s *UnitSuite) TestRemovePathologicalWithBuggyUniter(c *gc.C) {
  1510  	// Add a relation between wordpress and mysql...
  1511  	wordpress := s.service
  1512  	wordpress0 := s.unit
  1513  	mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql"))
  1514  	eps, err := s.State.InferEndpoints("wordpress", "mysql")
  1515  	c.Assert(err, jc.ErrorIsNil)
  1516  	rel, err := s.State.AddRelation(eps...)
  1517  	c.Assert(err, jc.ErrorIsNil)
  1518  
  1519  	// The relation holds a reference to wordpress, but that can't keep
  1520  	// wordpress from being removed -- because the relation will be removed
  1521  	// if we destroy wordpress.
  1522  	// However, if a unit of the *other* service joins the relation, that
  1523  	// will add an additional reference and prevent the relation -- and
  1524  	// thus wordpress itself -- from being removed when its last unit is.
  1525  	mysql0, err := mysql.AddUnit()
  1526  	c.Assert(err, jc.ErrorIsNil)
  1527  	mysql0ru, err := rel.Unit(mysql0)
  1528  	c.Assert(err, jc.ErrorIsNil)
  1529  	err = mysql0ru.EnterScope(nil)
  1530  	c.Assert(err, jc.ErrorIsNil)
  1531  
  1532  	// Destroy wordpress, and remove its last unit.
  1533  	err = wordpress.Destroy()
  1534  	c.Assert(err, jc.ErrorIsNil)
  1535  	err = wordpress0.EnsureDead()
  1536  	c.Assert(err, jc.ErrorIsNil)
  1537  	err = wordpress0.Remove()
  1538  	c.Assert(err, jc.ErrorIsNil)
  1539  
  1540  	// Check this didn't kill the service or relation yet...
  1541  	err = wordpress.Refresh()
  1542  	c.Assert(err, jc.ErrorIsNil)
  1543  	err = rel.Refresh()
  1544  	c.Assert(err, jc.ErrorIsNil)
  1545  
  1546  	// ...and that when the malfunctioning unit agent on the other side
  1547  	// sets itself to dead *without* departing the relation, the unit's
  1548  	// removal causes the relation and the other service to be cleaned up.
  1549  	err = mysql0.EnsureDead()
  1550  	c.Assert(err, jc.ErrorIsNil)
  1551  	err = mysql0.Remove()
  1552  	c.Assert(err, jc.ErrorIsNil)
  1553  	err = wordpress.Refresh()
  1554  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1555  	err = rel.Refresh()
  1556  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1557  }
  1558  
  1559  func (s *UnitSuite) TestWatchSubordinates(c *gc.C) {
  1560  	// TODO(mjs) - ENVUUID - test with multiple environments with
  1561  	// identically named units and ensure there's no leakage.
  1562  	w := s.unit.WatchSubordinateUnits()
  1563  	defer testing.AssertStop(c, w)
  1564  	wc := testing.NewStringsWatcherC(c, s.State, w)
  1565  	wc.AssertChange()
  1566  	wc.AssertNoChange()
  1567  
  1568  	// Add a couple of subordinates, check change.
  1569  	subCharm := s.AddTestingCharm(c, "logging")
  1570  	var subUnits []*state.Unit
  1571  	for i := 0; i < 2; i++ {
  1572  		// Note: subordinate units can only be created as a side effect of a
  1573  		// principal entering scope; and a given principal can only have a
  1574  		// single subordinate unit of each service.
  1575  		name := "logging" + strconv.Itoa(i)
  1576  		subSvc := s.AddTestingService(c, name, subCharm)
  1577  		eps, err := s.State.InferEndpoints(name, "wordpress")
  1578  		c.Assert(err, jc.ErrorIsNil)
  1579  		rel, err := s.State.AddRelation(eps...)
  1580  		c.Assert(err, jc.ErrorIsNil)
  1581  		ru, err := rel.Unit(s.unit)
  1582  		c.Assert(err, jc.ErrorIsNil)
  1583  		err = ru.EnterScope(nil)
  1584  		c.Assert(err, jc.ErrorIsNil)
  1585  		units, err := subSvc.AllUnits()
  1586  		c.Assert(err, jc.ErrorIsNil)
  1587  		c.Assert(units, gc.HasLen, 1)
  1588  		subUnits = append(subUnits, units[0])
  1589  	}
  1590  	wc.AssertChange(subUnits[0].Name(), subUnits[1].Name())
  1591  	wc.AssertNoChange()
  1592  
  1593  	// Set one to Dying, check change.
  1594  	err := subUnits[0].Destroy()
  1595  	c.Assert(err, jc.ErrorIsNil)
  1596  	wc.AssertChange(subUnits[0].Name())
  1597  	wc.AssertNoChange()
  1598  
  1599  	// Set both to Dead, and remove one; check change.
  1600  	err = subUnits[0].EnsureDead()
  1601  	c.Assert(err, jc.ErrorIsNil)
  1602  	err = subUnits[1].EnsureDead()
  1603  	c.Assert(err, jc.ErrorIsNil)
  1604  	err = subUnits[1].Remove()
  1605  	c.Assert(err, jc.ErrorIsNil)
  1606  	wc.AssertChange(subUnits[0].Name(), subUnits[1].Name())
  1607  	wc.AssertNoChange()
  1608  
  1609  	// Stop watcher, check closed.
  1610  	testing.AssertStop(c, w)
  1611  	wc.AssertClosed()
  1612  
  1613  	// Start a new watch, check Dead unit is reported.
  1614  	w = s.unit.WatchSubordinateUnits()
  1615  	defer testing.AssertStop(c, w)
  1616  	wc = testing.NewStringsWatcherC(c, s.State, w)
  1617  	wc.AssertChange(subUnits[0].Name())
  1618  	wc.AssertNoChange()
  1619  
  1620  	// Remove the leftover, check no change.
  1621  	err = subUnits[0].Remove()
  1622  	c.Assert(err, jc.ErrorIsNil)
  1623  	wc.AssertNoChange()
  1624  }
  1625  
  1626  func (s *UnitSuite) TestWatchUnit(c *gc.C) {
  1627  	preventUnitDestroyRemove(c, s.unit)
  1628  	w := s.unit.Watch()
  1629  	defer testing.AssertStop(c, w)
  1630  
  1631  	// Initial event.
  1632  	wc := testing.NewNotifyWatcherC(c, s.State, w)
  1633  	wc.AssertOneChange()
  1634  
  1635  	// Make one change (to a separate instance), check one event.
  1636  	unit, err := s.State.Unit(s.unit.Name())
  1637  	c.Assert(err, jc.ErrorIsNil)
  1638  	s.setAssignedMachineAddresses(c, unit)
  1639  	wc.AssertOneChange()
  1640  
  1641  	// Make two changes, check one event.
  1642  	err = unit.SetPassword("arble-farble-dying-yarble")
  1643  	c.Assert(err, jc.ErrorIsNil)
  1644  	err = unit.Destroy()
  1645  	c.Assert(err, jc.ErrorIsNil)
  1646  	wc.AssertOneChange()
  1647  
  1648  	// Stop, check closed.
  1649  	testing.AssertStop(c, w)
  1650  	wc.AssertClosed()
  1651  
  1652  	// Remove unit, start new watch, check single event.
  1653  	err = unit.EnsureDead()
  1654  	c.Assert(err, jc.ErrorIsNil)
  1655  	err = unit.Remove()
  1656  	c.Assert(err, jc.ErrorIsNil)
  1657  	w = s.unit.Watch()
  1658  	defer testing.AssertStop(c, w)
  1659  	testing.NewNotifyWatcherC(c, s.State, w).AssertOneChange()
  1660  }
  1661  
  1662  func (s *UnitSuite) TestUnitAgentTools(c *gc.C) {
  1663  	preventUnitDestroyRemove(c, s.unit)
  1664  	testAgentTools(c, s.unit, `unit "wordpress/0"`)
  1665  }
  1666  
  1667  func (s *UnitSuite) TestActionSpecs(c *gc.C) {
  1668  	basicActions := `
  1669  snapshot:
  1670    params:
  1671      outfile:
  1672        type: string
  1673  `[1:]
  1674  
  1675  	wordpress := s.AddTestingService(c, "wordpress-actions", s.AddActionsCharm(c, "wordpress", basicActions, 1))
  1676  	unit1, err := wordpress.AddUnit()
  1677  	c.Assert(err, jc.ErrorIsNil)
  1678  	specs, err := unit1.ActionSpecs()
  1679  	c.Assert(err, jc.ErrorIsNil)
  1680  	c.Check(specs, jc.DeepEquals, state.ActionSpecsByName{
  1681  		"snapshot": charm.ActionSpec{
  1682  			Description: "No description",
  1683  			Params: map[string]interface{}{
  1684  				"type":        "object",
  1685  				"title":       "snapshot",
  1686  				"description": "No description",
  1687  				"properties": map[string]interface{}{
  1688  					"outfile": map[string]interface{}{
  1689  						"type": "string",
  1690  					},
  1691  				},
  1692  			},
  1693  		},
  1694  	})
  1695  }
  1696  
  1697  func (s *UnitSuite) TestUnitActionsFindsRightActions(c *gc.C) {
  1698  	// An actions.yaml which permits actions by the following names
  1699  	basicActions := `
  1700  action-a-a:
  1701  action-a-b:
  1702  action-a-c:
  1703  action-b-a:
  1704  action-b-b:
  1705  `[1:]
  1706  
  1707  	// Add simple service and two units
  1708  	dummy := s.AddTestingService(c, "dummy", s.AddActionsCharm(c, "dummy", basicActions, 1))
  1709  
  1710  	unit1, err := dummy.AddUnit()
  1711  	c.Assert(err, jc.ErrorIsNil)
  1712  
  1713  	unit2, err := dummy.AddUnit()
  1714  	c.Assert(err, jc.ErrorIsNil)
  1715  
  1716  	// Add 3 actions to first unit, and 2 to the second unit
  1717  	_, err = unit1.AddAction("action-a-a", nil)
  1718  	c.Assert(err, jc.ErrorIsNil)
  1719  	_, err = unit1.AddAction("action-a-b", nil)
  1720  	c.Assert(err, jc.ErrorIsNil)
  1721  	_, err = unit1.AddAction("action-a-c", nil)
  1722  	c.Assert(err, jc.ErrorIsNil)
  1723  
  1724  	_, err = unit2.AddAction("action-b-a", nil)
  1725  	c.Assert(err, jc.ErrorIsNil)
  1726  	_, err = unit2.AddAction("action-b-b", nil)
  1727  	c.Assert(err, jc.ErrorIsNil)
  1728  
  1729  	// Verify that calling Actions on unit1 returns only
  1730  	// the three actions added to unit1
  1731  	actions1, err := unit1.Actions()
  1732  	c.Assert(err, jc.ErrorIsNil)
  1733  	c.Assert(len(actions1), gc.Equals, 3)
  1734  	for _, action := range actions1 {
  1735  		c.Assert(action.Name(), gc.Matches, "^action-a-.")
  1736  	}
  1737  
  1738  	// Verify that calling Actions on unit2 returns only
  1739  	// the two actions added to unit2
  1740  	actions2, err := unit2.Actions()
  1741  	c.Assert(err, jc.ErrorIsNil)
  1742  	c.Assert(len(actions2), gc.Equals, 2)
  1743  	for _, action := range actions2 {
  1744  		c.Assert(action.Name(), gc.Matches, "^action-b-.")
  1745  	}
  1746  }