github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/state/payloads_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  	"sort"
     9  
    10  	"github.com/juju/errors"
    11  	jc "github.com/juju/testing/checkers"
    12  	gc "gopkg.in/check.v1"
    13  	"gopkg.in/juju/charm.v6-unstable"
    14  
    15  	"github.com/juju/juju/payload"
    16  	"github.com/juju/juju/state"
    17  	"github.com/juju/juju/testing/factory"
    18  )
    19  
    20  type PayloadsSuite struct {
    21  	ConnSuite
    22  }
    23  
    24  var _ = gc.Suite(&PayloadsSuite{})
    25  
    26  func (s *PayloadsSuite) TestLookUp(c *gc.C) {
    27  	fix := s.newFixture(c)
    28  
    29  	result, err := fix.UnitPayloads.LookUp("returned", "ignored")
    30  	c.Check(result, gc.Equals, "returned")
    31  	c.Check(err, jc.ErrorIsNil)
    32  }
    33  
    34  func (s *PayloadsSuite) TestListPartial(c *gc.C) {
    35  	// Note: List and ListAll are extensively tested via the Check
    36  	// methods on payloadFixture, used throughout the suite. But
    37  	// they don't cover this feature...
    38  	fix, initial := s.newPayloadFixture(c)
    39  	results, err := fix.UnitPayloads.List("whatever", initial.Name)
    40  	c.Assert(err, jc.ErrorIsNil)
    41  	c.Assert(results, gc.HasLen, 2)
    42  
    43  	missing := results[0]
    44  	c.Check(missing.ID, gc.Equals, "whatever")
    45  	c.Check(missing.Payload, gc.IsNil)
    46  	c.Check(missing.NotFound, jc.IsTrue)
    47  	c.Check(missing.Error, jc.Satisfies, errors.IsNotFound)
    48  	c.Check(missing.Error, gc.ErrorMatches, "whatever not found")
    49  
    50  	found := results[1]
    51  	c.Check(found.ID, gc.Equals, initial.Name)
    52  	c.Assert(found.Payload, gc.NotNil)
    53  	c.Assert(*found.Payload, jc.DeepEquals, fix.FullPayload(initial))
    54  	c.Check(found.NotFound, jc.IsFalse)
    55  	c.Check(found.Error, jc.ErrorIsNil)
    56  }
    57  
    58  func (s *PayloadsSuite) TestNoPayloads(c *gc.C) {
    59  	fix := s.newFixture(c)
    60  
    61  	fix.CheckNoPayload(c)
    62  }
    63  
    64  func (s *PayloadsSuite) TestTrackInvalidPayload(c *gc.C) {
    65  	// not an exhaustive test, just an indication we do Validate()
    66  	fix := s.newFixture(c)
    67  	pl := fix.SamplePayload("")
    68  
    69  	err := fix.UnitPayloads.Track(pl)
    70  	c.Check(err, jc.Satisfies, errors.IsNotValid)
    71  	c.Check(err, gc.ErrorMatches, `missing ID not valid`)
    72  	fix.CheckNoPayload(c)
    73  }
    74  
    75  func (s *PayloadsSuite) TestTrackInvalidUnit(c *gc.C) {
    76  
    77  	// Note: this is STUPID, but none of the unit-specific contexts
    78  	// between `api/context/register.go` and here ever check that
    79  	// the track request is correctly targeted. So we overwrite it
    80  	// unconditionally... because register is unconditionally
    81  	// sending a garbage unit name for some reason.
    82  
    83  	fix := s.newFixture(c)
    84  	expect := fix.SamplePayload("some-docker-id")
    85  	track := expect
    86  	track.Unit = "different/0"
    87  
    88  	err := fix.UnitPayloads.Track(track)
    89  	// In a sensible implementation, this would be:
    90  	//
    91  	//    c.Check(err, jc.Satisfies, errors.IsNotValid)
    92  	//    c.Check(err, gc.ErrorMatches, `unexpected Unit "different/0" not valid`)
    93  	//
    94  	//    fix.CheckUnitPayloads(c)
    95  	//    fix.CheckModelPayloads(c)
    96  	//
    97  	// ...but instead we have:
    98  	c.Assert(err, jc.ErrorIsNil)
    99  	fix.CheckOnePayload(c, expect)
   100  }
   101  
   102  func (s *PayloadsSuite) TestTrackInsertPayload(c *gc.C) {
   103  	fix := s.newFixture(c)
   104  	desired := fix.SamplePayload("some-docker-id")
   105  
   106  	err := fix.UnitPayloads.Track(desired)
   107  	c.Assert(err, jc.ErrorIsNil)
   108  	fix.CheckOnePayload(c, desired)
   109  }
   110  
   111  func (s *PayloadsSuite) TestTrackUpdatePayload(c *gc.C) {
   112  	fix, initial := s.newPayloadFixture(c)
   113  	replacement := initial
   114  	replacement.ID = "new-exciting-different"
   115  
   116  	err := fix.UnitPayloads.Track(replacement)
   117  	c.Assert(err, jc.ErrorIsNil)
   118  	fix.CheckOnePayload(c, replacement)
   119  }
   120  
   121  func (s *PayloadsSuite) TestTrackMultiplePayloads(c *gc.C) {
   122  	fix, initial := s.newPayloadFixture(c)
   123  	additional := fix.SamplePayload("another-docker-id")
   124  	additional.Name = "app"
   125  
   126  	err := fix.UnitPayloads.Track(additional)
   127  	c.Assert(err, jc.ErrorIsNil)
   128  
   129  	full1 := fix.FullPayload(initial)
   130  	full2 := fix.FullPayload(additional)
   131  	fix.CheckUnitPayloads(c, full1, full2)
   132  	fix.CheckModelPayloads(c, full1, full2)
   133  }
   134  
   135  func (s *PayloadsSuite) TestTrackMultipleUnits(c *gc.C) {
   136  	fix, initial := s.newPayloadFixture(c)
   137  
   138  	// Create a new unit to add another payload to.
   139  	applicationName := fix.Unit.ApplicationName()
   140  	application, err := s.State.Application(applicationName)
   141  	c.Assert(err, jc.ErrorIsNil)
   142  	machine2 := s.Factory.MakeMachine(c, nil)
   143  	unit2 := s.Factory.MakeUnit(c, &factory.UnitParams{
   144  		Application: application,
   145  		Machine:     machine2,
   146  	})
   147  
   148  	// Add a payload which should be independent of the
   149  	// UnitPayloads in the fixture.
   150  	unit2Payloads, err := s.State.UnitPayloads(unit2)
   151  	c.Assert(err, jc.ErrorIsNil)
   152  	additional := initial
   153  	additional.Unit = unit2.Name()
   154  	err = unit2Payloads.Track(additional)
   155  	c.Assert(err, jc.ErrorIsNil)
   156  
   157  	// Check the independent payload only shows up in
   158  	// the fixture's ModelPayloads, not its UnitPayloads.
   159  	full1 := fix.FullPayload(initial)
   160  	full2 := payload.FullPayloadInfo{
   161  		Payload: additional,
   162  		Machine: machine2.Id(),
   163  	}
   164  	fix.CheckUnitPayloads(c, full1)
   165  	fix.CheckModelPayloads(c, full1, full2)
   166  }
   167  
   168  func (s *PayloadsSuite) TestSetStatusInvalid(c *gc.C) {
   169  	fix, initial := s.newPayloadFixture(c)
   170  
   171  	err := fix.UnitPayloads.SetStatus(initial.Name, "twirling")
   172  	c.Check(err, jc.Satisfies, errors.IsNotValid)
   173  	c.Check(err.Error(), gc.Equals, `status "twirling" not supported; expected one of ["running", "starting", "stopped", "stopping"]`)
   174  
   175  	fix.CheckOnePayload(c, initial)
   176  }
   177  
   178  func (s *PayloadsSuite) TestSetStatus(c *gc.C) {
   179  	fix, initial := s.newPayloadFixture(c)
   180  	expect := initial
   181  	expect.Status = "stopping"
   182  
   183  	err := fix.UnitPayloads.SetStatus(initial.Name, "stopping")
   184  	c.Assert(err, jc.ErrorIsNil)
   185  
   186  	fix.CheckOnePayload(c, expect)
   187  }
   188  
   189  func (s *PayloadsSuite) TestUntrackMissing(c *gc.C) {
   190  	fix := s.newFixture(c)
   191  
   192  	err := fix.UnitPayloads.Untrack("whatever")
   193  	c.Assert(err, jc.ErrorIsNil)
   194  	fix.CheckNoPayload(c)
   195  }
   196  
   197  func (s *PayloadsSuite) TestUntrack(c *gc.C) {
   198  	fix, initial := s.newPayloadFixture(c)
   199  
   200  	err := fix.UnitPayloads.Untrack(initial.Name)
   201  	c.Assert(err, jc.ErrorIsNil)
   202  	fix.CheckNoPayload(c)
   203  }
   204  
   205  func (s *PayloadsSuite) TestRemoveUnitUntracksPayloads(c *gc.C) {
   206  	fix, _ := s.newPayloadFixture(c)
   207  	additional := fix.SamplePayload("another-docker-id")
   208  	additional.Name = "app"
   209  	err := fix.UnitPayloads.Track(additional)
   210  	c.Assert(err, jc.ErrorIsNil)
   211  
   212  	err = fix.Unit.Destroy()
   213  	c.Assert(err, jc.ErrorIsNil)
   214  	err = s.State.Cleanup()
   215  	c.Assert(err, jc.ErrorIsNil)
   216  	fix.CheckNoPayload(c)
   217  }
   218  
   219  func (s *PayloadsSuite) TestTrackRaceDyingUnit(c *gc.C) {
   220  	fix := s.newFixture(c)
   221  	preventUnitDestroyRemove(c, fix.Unit)
   222  
   223  	defer state.SetBeforeHooks(c, s.State, func() {
   224  		err := fix.Unit.Destroy()
   225  		c.Assert(err, jc.ErrorIsNil)
   226  	}).Check()
   227  
   228  	desired := fix.SamplePayload("this-is-fine")
   229  	err := fix.UnitPayloads.Track(desired)
   230  	c.Assert(err, jc.ErrorIsNil)
   231  	fix.CheckOnePayload(c, desired)
   232  }
   233  
   234  func (s *PayloadsSuite) TestTrackRaceDeadUnit(c *gc.C) {
   235  	fix := s.newFixture(c)
   236  	preventUnitDestroyRemove(c, fix.Unit)
   237  
   238  	defer state.SetBeforeHooks(c, s.State, func() {
   239  		err := fix.Unit.Destroy()
   240  		c.Assert(err, jc.ErrorIsNil)
   241  		err = fix.Unit.EnsureDead()
   242  		c.Assert(err, jc.ErrorIsNil)
   243  	}).Check()
   244  
   245  	desired := fix.SamplePayload("sorry-too-late")
   246  	err := fix.UnitPayloads.Track(desired)
   247  	c.Check(err, gc.ErrorMatches, fix.DeadUnitMessage())
   248  	fix.CheckNoPayload(c)
   249  }
   250  
   251  func (s *PayloadsSuite) TestTrackRaceRemovedUnit(c *gc.C) {
   252  	fix := s.newFixture(c)
   253  
   254  	defer state.SetBeforeHooks(c, s.State, func() {
   255  		err := fix.Unit.Destroy()
   256  		c.Assert(err, jc.ErrorIsNil)
   257  	}).Check()
   258  
   259  	desired := fix.SamplePayload("sorry-too-late")
   260  	err := fix.UnitPayloads.Track(desired)
   261  	c.Check(err, gc.ErrorMatches, fix.DeadUnitMessage())
   262  	fix.CheckNoPayload(c)
   263  }
   264  
   265  func (s *PayloadsSuite) TestTrackRaceTrack(c *gc.C) {
   266  	fix := s.newFixture(c)
   267  	desired := fix.SamplePayload("wanted")
   268  	interloper := fix.SamplePayload("not-wanted")
   269  
   270  	defer state.SetBeforeHooks(c, s.State, func() {
   271  		err := fix.UnitPayloads.Track(interloper)
   272  		c.Assert(err, jc.ErrorIsNil)
   273  	}).Check()
   274  
   275  	err := fix.UnitPayloads.Track(desired)
   276  	c.Assert(err, jc.ErrorIsNil)
   277  	fix.CheckOnePayload(c, desired)
   278  }
   279  
   280  func (s *PayloadsSuite) TestTrackRaceSetStatus(c *gc.C) {
   281  	fix, initial := s.newPayloadFixture(c)
   282  	desired := initial
   283  	desired.Status = "starting"
   284  
   285  	defer state.SetBeforeHooks(c, s.State, func() {
   286  		err := fix.UnitPayloads.SetStatus(initial.Name, "stopping")
   287  		c.Assert(err, jc.ErrorIsNil)
   288  	}).Check()
   289  
   290  	err := fix.UnitPayloads.Track(desired)
   291  	c.Assert(err, jc.ErrorIsNil)
   292  	fix.CheckOnePayload(c, desired)
   293  }
   294  
   295  func (s *PayloadsSuite) TestTrackRaceUntrack(c *gc.C) {
   296  	fix, initial := s.newPayloadFixture(c)
   297  
   298  	defer state.SetBeforeHooks(c, s.State, func() {
   299  		err := fix.UnitPayloads.Untrack(initial.Name)
   300  		c.Assert(err, jc.ErrorIsNil)
   301  	}).Check()
   302  
   303  	err := fix.UnitPayloads.Track(initial)
   304  	c.Assert(err, jc.ErrorIsNil)
   305  	fix.CheckOnePayload(c, initial)
   306  }
   307  
   308  func (s *PayloadsSuite) TestSetStatusRaceTrack(c *gc.C) {
   309  	fix, initial := s.newPayloadFixture(c)
   310  	expect := initial
   311  	expect.Status = "stopped"
   312  
   313  	defer state.SetBeforeHooks(c, s.State, func() {
   314  		err := fix.UnitPayloads.Track(initial)
   315  		c.Assert(err, jc.ErrorIsNil)
   316  	}).Check()
   317  
   318  	err := fix.UnitPayloads.SetStatus(initial.Name, "stopped")
   319  	c.Assert(err, jc.ErrorIsNil)
   320  	fix.CheckOnePayload(c, expect)
   321  }
   322  
   323  func (s *PayloadsSuite) TestSetStatusRaceUntrack(c *gc.C) {
   324  	fix, initial := s.newPayloadFixture(c)
   325  
   326  	defer state.SetBeforeHooks(c, s.State, func() {
   327  		err := fix.UnitPayloads.Untrack(initial.Name)
   328  		c.Assert(err, jc.ErrorIsNil)
   329  	}).Check()
   330  
   331  	err := fix.UnitPayloads.SetStatus(initial.Name, "stopped")
   332  	c.Check(errors.Cause(err), gc.Equals, payload.ErrNotFound)
   333  	c.Check(err, gc.ErrorMatches, "payload not found")
   334  	fix.CheckNoPayload(c)
   335  }
   336  
   337  func (s *PayloadsSuite) TestUntrackRaceTrack(c *gc.C) {
   338  	fix, initial := s.newPayloadFixture(c)
   339  
   340  	defer state.SetBeforeHooks(c, s.State, func() {
   341  		err := fix.UnitPayloads.Track(initial)
   342  		c.Assert(err, jc.ErrorIsNil)
   343  	}).Check()
   344  
   345  	err := fix.UnitPayloads.Untrack(initial.Name)
   346  	c.Assert(err, jc.ErrorIsNil)
   347  	fix.CheckNoPayload(c)
   348  }
   349  
   350  func (s *PayloadsSuite) TestUntrackRaceSetStatus(c *gc.C) {
   351  	fix, initial := s.newPayloadFixture(c)
   352  
   353  	defer state.SetBeforeHooks(c, s.State, func() {
   354  		err := fix.UnitPayloads.SetStatus(initial.Name, "stopping")
   355  		c.Assert(err, jc.ErrorIsNil)
   356  	}).Check()
   357  
   358  	err := fix.UnitPayloads.Untrack(initial.Name)
   359  	c.Assert(err, jc.ErrorIsNil)
   360  	fix.CheckNoPayload(c)
   361  }
   362  
   363  func (s *PayloadsSuite) TestUntrackRaceUntrack(c *gc.C) {
   364  	fix, initial := s.newPayloadFixture(c)
   365  
   366  	defer state.SetBeforeHooks(c, s.State, func() {
   367  		err := fix.UnitPayloads.Untrack(initial.Name)
   368  		c.Assert(err, jc.ErrorIsNil)
   369  	}).Check()
   370  
   371  	err := fix.UnitPayloads.Untrack(initial.Name)
   372  	c.Assert(err, jc.ErrorIsNil)
   373  	fix.CheckNoPayload(c)
   374  }
   375  
   376  // -------------------------
   377  // test helpers
   378  
   379  type payloadsFixture struct {
   380  	ModelPayloads state.ModelPayloads
   381  	UnitPayloads  state.UnitPayloads
   382  	Machine       *state.Machine
   383  	Unit          *state.Unit
   384  }
   385  
   386  func (s *PayloadsSuite) newFixture(c *gc.C) payloadsFixture {
   387  	machine := s.Factory.MakeMachine(c, nil)
   388  	unit := s.Factory.MakeUnit(c, &factory.UnitParams{Machine: machine})
   389  	modelPayloads, err := s.State.ModelPayloads()
   390  	c.Assert(err, jc.ErrorIsNil)
   391  	unitPayloads, err := s.State.UnitPayloads(unit)
   392  	c.Assert(err, jc.ErrorIsNil)
   393  	return payloadsFixture{
   394  		ModelPayloads: modelPayloads,
   395  		UnitPayloads:  unitPayloads,
   396  		Machine:       machine,
   397  		Unit:          unit,
   398  	}
   399  }
   400  
   401  func (s *PayloadsSuite) newPayloadFixture(c *gc.C) (payloadsFixture, payload.Payload) {
   402  	fix := s.newFixture(c)
   403  	initial := fix.SamplePayload("some-docker-id")
   404  	err := fix.UnitPayloads.Track(initial)
   405  	c.Assert(err, jc.ErrorIsNil)
   406  	return fix, initial
   407  }
   408  
   409  func (fix payloadsFixture) SamplePayload(id string) payload.Payload {
   410  	return payload.Payload{
   411  		PayloadClass: charm.PayloadClass{
   412  			Name: "database",
   413  			Type: "docker",
   414  		},
   415  		Status: payload.StateRunning,
   416  		ID:     id,
   417  		Unit:   fix.Unit.Name(),
   418  	}
   419  }
   420  
   421  func (fix payloadsFixture) DeadUnitMessage() string {
   422  	return fmt.Sprintf("unit %q no longer available", fix.Unit.Name())
   423  }
   424  
   425  func (fix payloadsFixture) FullPayload(pl payload.Payload) payload.FullPayloadInfo {
   426  	return payload.FullPayloadInfo{
   427  		Payload: pl,
   428  		Machine: fix.Machine.Id(),
   429  	}
   430  }
   431  
   432  func (fix payloadsFixture) CheckNoPayload(c *gc.C) {
   433  	fix.CheckModelPayloads(c)
   434  	fix.CheckUnitPayloads(c)
   435  }
   436  
   437  func (fix payloadsFixture) CheckOnePayload(c *gc.C, expect payload.Payload) {
   438  	full := fix.FullPayload(expect)
   439  	fix.CheckModelPayloads(c, full)
   440  	fix.CheckUnitPayloads(c, full)
   441  }
   442  
   443  func (fix payloadsFixture) CheckModelPayloads(c *gc.C, expect ...payload.FullPayloadInfo) {
   444  	actual, err := fix.ModelPayloads.ListAll()
   445  	c.Check(err, jc.ErrorIsNil)
   446  	sort.Sort(byPayloadInfo(actual))
   447  	sort.Sort(byPayloadInfo(expect))
   448  	c.Check(actual, jc.DeepEquals, expect)
   449  }
   450  
   451  func (fix payloadsFixture) CheckUnitPayloads(c *gc.C, expect ...payload.FullPayloadInfo) {
   452  	actual, err := fix.UnitPayloads.List()
   453  	c.Check(err, jc.ErrorIsNil)
   454  	extracted := fix.extractInfos(c, actual)
   455  	sort.Sort(byPayloadInfo(extracted))
   456  	sort.Sort(byPayloadInfo(expect))
   457  	c.Check(extracted, jc.DeepEquals, expect)
   458  }
   459  
   460  func (payloadsFixture) extractInfos(c *gc.C, results []payload.Result) []payload.FullPayloadInfo {
   461  	fulls := make([]payload.FullPayloadInfo, 0, len(results))
   462  	for _, result := range results {
   463  		c.Assert(result.ID, gc.Equals, result.Payload.Name)
   464  		c.Assert(result.Payload, gc.NotNil)
   465  		c.Assert(result.NotFound, jc.IsFalse)
   466  		c.Assert(result.Error, jc.ErrorIsNil)
   467  		fulls = append(fulls, *result.Payload)
   468  	}
   469  	return fulls
   470  }
   471  
   472  type byPayloadInfo []payload.FullPayloadInfo
   473  
   474  func (s byPayloadInfo) Len() int      { return len(s) }
   475  func (s byPayloadInfo) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
   476  func (s byPayloadInfo) Less(i, j int) bool {
   477  	if s[i].Machine != s[j].Machine {
   478  		return s[i].Machine < s[j].Machine
   479  	}
   480  	if s[i].Payload.Unit != s[j].Payload.Unit {
   481  		return s[i].Payload.Unit < s[j].Payload.Unit
   482  	}
   483  	return s[i].Payload.Name < s[j].Payload.Name
   484  }
   485  
   486  // ----------------------------------------------------------
   487  // original functional tests
   488  
   489  type PayloadsFunctionalSuite struct {
   490  	ConnSuite
   491  }
   492  
   493  var _ = gc.Suite(&PayloadsFunctionalSuite{})
   494  
   495  func (s *PayloadsFunctionalSuite) TestModelPayloads(c *gc.C) {
   496  	machine := "0"
   497  	unit := addUnit(c, s.ConnSuite, unitArgs{
   498  		charm:    "dummy",
   499  		service:  "a-application",
   500  		metadata: payloadsMetaYAML,
   501  		machine:  machine,
   502  	})
   503  
   504  	ust, err := s.State.UnitPayloads(unit)
   505  	c.Assert(err, jc.ErrorIsNil)
   506  
   507  	st, err := s.State.ModelPayloads()
   508  	c.Assert(err, jc.ErrorIsNil)
   509  
   510  	payloads, err := st.ListAll()
   511  	c.Assert(err, jc.ErrorIsNil)
   512  	c.Check(payloads, gc.HasLen, 0)
   513  
   514  	err = ust.Track(payload.Payload{
   515  		PayloadClass: charm.PayloadClass{
   516  			Name: "payloadA",
   517  			Type: "docker",
   518  		},
   519  		Status: payload.StateRunning,
   520  		ID:     "xyz",
   521  		Unit:   "a-application/0",
   522  	})
   523  	c.Assert(err, jc.ErrorIsNil)
   524  
   525  	unitPayloads, err := ust.List()
   526  	c.Assert(err, jc.ErrorIsNil)
   527  	c.Assert(unitPayloads, gc.HasLen, 1)
   528  
   529  	payloads, err = st.ListAll()
   530  	c.Assert(err, jc.ErrorIsNil)
   531  	c.Check(payloads, jc.DeepEquals, []payload.FullPayloadInfo{{
   532  		Payload: payload.Payload{
   533  			PayloadClass: charm.PayloadClass{
   534  				Name: "payloadA",
   535  				Type: "docker",
   536  			},
   537  			ID:     "xyz",
   538  			Status: payload.StateRunning,
   539  			Labels: []string{},
   540  			Unit:   "a-application/0",
   541  		},
   542  		Machine: machine,
   543  	}})
   544  
   545  	id, err := ust.LookUp("payloadA", "xyz")
   546  	c.Assert(err, jc.ErrorIsNil)
   547  
   548  	err = ust.Untrack(id)
   549  	c.Assert(err, jc.ErrorIsNil)
   550  
   551  	payloads, err = st.ListAll()
   552  	c.Assert(err, jc.ErrorIsNil)
   553  	c.Check(payloads, gc.HasLen, 0)
   554  }
   555  
   556  func (s *PayloadsFunctionalSuite) TestUnitPayloads(c *gc.C) {
   557  	machine := "0"
   558  	unit := addUnit(c, s.ConnSuite, unitArgs{
   559  		charm:    "dummy",
   560  		service:  "a-application",
   561  		metadata: payloadsMetaYAML,
   562  		machine:  machine,
   563  	})
   564  
   565  	st, err := s.State.UnitPayloads(unit)
   566  	c.Assert(err, jc.ErrorIsNil)
   567  
   568  	results, err := st.List()
   569  	c.Assert(err, jc.ErrorIsNil)
   570  	c.Check(results, gc.HasLen, 0)
   571  
   572  	pl := payload.Payload{
   573  		PayloadClass: charm.PayloadClass{
   574  			Name: "payloadA",
   575  			Type: "docker",
   576  		},
   577  		ID:     "xyz",
   578  		Status: payload.StateRunning,
   579  		Unit:   "a-application/0",
   580  	}
   581  	err = st.Track(pl)
   582  	c.Assert(err, jc.ErrorIsNil)
   583  
   584  	results, err = st.List()
   585  	c.Assert(err, jc.ErrorIsNil)
   586  	// TODO(ericsnow) Once Track returns the new ID we can drop
   587  	// the following two lines.
   588  	c.Assert(results, gc.HasLen, 1)
   589  	id := results[0].ID
   590  	c.Check(results, jc.DeepEquals, []payload.Result{{
   591  		ID: id,
   592  		Payload: &payload.FullPayloadInfo{
   593  			Payload: pl,
   594  			Machine: machine,
   595  		},
   596  	}})
   597  
   598  	lookedUpID, err := st.LookUp("payloadA", "xyz")
   599  	c.Assert(err, jc.ErrorIsNil)
   600  	c.Check(lookedUpID, gc.Equals, id)
   601  
   602  	c.Logf("using ID %q", id)
   603  	results, err = st.List(id)
   604  	c.Assert(err, jc.ErrorIsNil)
   605  	c.Check(results, jc.DeepEquals, []payload.Result{{
   606  		ID: id,
   607  		Payload: &payload.FullPayloadInfo{
   608  			Payload: pl,
   609  			Machine: machine,
   610  		},
   611  	}})
   612  
   613  	err = st.SetStatus(id, "running")
   614  	c.Assert(err, jc.ErrorIsNil)
   615  
   616  	results, err = st.List(id)
   617  	c.Assert(err, jc.ErrorIsNil)
   618  	c.Check(results, jc.DeepEquals, []payload.Result{{
   619  		ID: id,
   620  		Payload: &payload.FullPayloadInfo{
   621  			Payload: pl,
   622  			Machine: machine,
   623  		},
   624  	}})
   625  
   626  	// Ensure existing ones are replaced.
   627  	update := pl
   628  	update.ID = "abc"
   629  	err = st.Track(update)
   630  	c.Check(err, jc.ErrorIsNil)
   631  	results, err = st.List()
   632  	c.Assert(err, jc.ErrorIsNil)
   633  	c.Check(results, jc.DeepEquals, []payload.Result{{
   634  		ID: id,
   635  		Payload: &payload.FullPayloadInfo{
   636  			Payload: update,
   637  			Machine: machine,
   638  		},
   639  	}})
   640  
   641  	err = st.Untrack(id)
   642  	c.Assert(err, jc.ErrorIsNil)
   643  
   644  	results, err = st.List()
   645  	c.Assert(err, jc.ErrorIsNil)
   646  	c.Check(results, gc.HasLen, 0)
   647  }
   648  
   649  const payloadsMetaYAML = `
   650  name: a-charm
   651  summary: a charm...
   652  description: a charm...
   653  payloads:
   654    payloadA:
   655      type: docker
   656  `
   657  
   658  type unitArgs struct {
   659  	charm    string
   660  	service  string
   661  	metadata string
   662  	machine  string
   663  }
   664  
   665  func addUnit(c *gc.C, s ConnSuite, args unitArgs) *state.Unit {
   666  	ch := s.AddTestingCharm(c, args.charm)
   667  	ch = s.AddMetaCharm(c, args.charm, args.metadata, 2)
   668  
   669  	svc := s.AddTestingService(c, args.service, ch)
   670  	unit, err := svc.AddUnit()
   671  	c.Assert(err, jc.ErrorIsNil)
   672  
   673  	// TODO(ericsnow) Explicitly: call unit.AssignToMachine(m)?
   674  	c.Assert(args.machine, gc.Equals, "0")
   675  	err = unit.AssignToNewMachine() // machine "0"
   676  	c.Assert(err, jc.ErrorIsNil)
   677  
   678  	return unit
   679  }