Beispiel #1
0
func (s *ActionSuite) TestMergeIds(c *gc.C) {
	var tests = []struct {
		changes  string
		adds     string
		removes  string
		expected string
	}{
		{changes: "", adds: "a0,a1", removes: "", expected: "a0,a1"},
		{changes: "a0,a1", adds: "", removes: "a0", expected: "a1"},
		{changes: "a0,a1", adds: "a2", removes: "a0", expected: "a1,a2"},

		{changes: "", adds: "a0,a1,a2", removes: "a0,a2", expected: "a1"},
		{changes: "", adds: "a0,a1,a2", removes: "a0,a1,a2", expected: ""},

		{changes: "a0", adds: "a0,a1,a2", removes: "a0,a2", expected: "a1"},
		{changes: "a1", adds: "a0,a1,a2", removes: "a0,a2", expected: "a1"},
		{changes: "a2", adds: "a0,a1,a2", removes: "a0,a2", expected: "a1"},

		{changes: "a3,a4", adds: "a1,a4,a5", removes: "a1,a3", expected: "a4,a5"},
		{changes: "a0,a1,a2", adds: "a1,a4,a5", removes: "a1,a3", expected: "a0,a2,a4,a5"},
	}

	prefix := state.DocID(s.State, "")

	for ix, test := range tests {
		updates := mapify(prefix, test.adds, test.removes)
		changes := sliceify("", test.changes)
		expected := sliceify("", test.expected)

		c.Log(fmt.Sprintf("test number %d %#v", ix, test))
		err := state.WatcherMergeIds(s.State, &changes, updates, state.ActionNotificationIdToActionId)
		c.Assert(err, jc.ErrorIsNil)
		c.Assert(changes, jc.SameContents, expected)
	}
}
Beispiel #2
0
func (s *CharmSuite) TestAddCharmUpdatesPlaceholder(c *gc.C) {
	// Check that adding charms updates any existing placeholder charm
	// with the same URL.
	ch := testcharms.Repo.CharmDir("dummy")

	// Add a placeholder charm.
	curl := charm.MustParseURL("cs:quantal/dummy-1")
	err := s.State.AddStoreCharmPlaceholder(curl)
	c.Assert(err, jc.ErrorIsNil)

	// Add a deployed charm.
	info := state.CharmInfo{
		Charm:       ch,
		ID:          curl,
		StoragePath: "dummy-1",
		SHA256:      "dummy-1-sha256",
	}
	dummy, err := s.State.AddCharm(info)
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(dummy.URL().String(), gc.Equals, curl.String())

	// Charm doc has been updated.
	var docs []state.CharmDoc
	err = s.charms.FindId(state.DocID(s.State, curl.String())).All(&docs)
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(docs, gc.HasLen, 1)
	c.Assert(docs[0].URL, gc.DeepEquals, curl)
	c.Assert(docs[0].StoragePath, gc.DeepEquals, info.StoragePath)

	// No more placeholder charm.
	_, err = s.State.LatestPlaceholderCharm(curl)
	c.Assert(err, jc.Satisfies, errors.IsNotFound)
}
Beispiel #3
0
// assertMachineStorageRefs ensures that the specified machine's set of volume
// and filesystem references corresponds exactly to the volume and filesystem
// attachments that relate to the machine.
func assertMachineStorageRefs(c *gc.C, st *state.State, m names.MachineTag) {
	machines, closer := state.GetRawCollection(st, state.MachinesC)
	defer closer()

	var doc struct {
		Volumes     []string `bson:"volumes,omitempty"`
		Filesystems []string `bson:"filesystems,omitempty"`
	}
	err := machines.FindId(state.DocID(st, m.Id())).One(&doc)
	c.Assert(err, jc.ErrorIsNil)

	have := make(set.Tags)
	for _, v := range doc.Volumes {
		have.Add(names.NewVolumeTag(v))
	}
	for _, f := range doc.Filesystems {
		have.Add(names.NewFilesystemTag(f))
	}

	expect := make(set.Tags)
	volumeAttachments, err := st.MachineVolumeAttachments(m)
	c.Assert(err, jc.ErrorIsNil)
	for _, a := range volumeAttachments {
		expect.Add(a.Volume())
	}
	filesystemAttachments, err := st.MachineFilesystemAttachments(m)
	c.Assert(err, jc.ErrorIsNil)
	for _, a := range filesystemAttachments {
		expect.Add(a.Filesystem())
	}

	c.Assert(have, jc.DeepEquals, expect)
}
Beispiel #4
0
func (s *AssignSuite) TestAssignMachinePrincipalsChange(c *gc.C) {
	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
	c.Assert(err, jc.ErrorIsNil)
	unit, err := s.wordpress.AddUnit()
	c.Assert(err, jc.ErrorIsNil)
	err = unit.AssignToMachine(machine)
	c.Assert(err, jc.ErrorIsNil)
	unit, err = s.wordpress.AddUnit()
	c.Assert(err, jc.ErrorIsNil)
	err = unit.AssignToMachine(machine)
	c.Assert(err, jc.ErrorIsNil)
	subUnit := s.addSubordinate(c, unit)

	checkPrincipals := func() []string {
		docID := state.DocID(s.State, machine.Id())
		doc := make(map[string][]string)
		s.machines.FindId(docID).One(&doc)
		principals, ok := doc["principals"]
		if !ok {
			c.Errorf(`machine document does not have a "principals" field`)
		}
		return principals
	}
	c.Assert(checkPrincipals(), gc.DeepEquals, []string{"wordpress/0", "wordpress/1"})

	err = subUnit.EnsureDead()
	c.Assert(err, jc.ErrorIsNil)
	err = subUnit.Remove()
	c.Assert(err, jc.ErrorIsNil)
	err = unit.EnsureDead()
	c.Assert(err, jc.ErrorIsNil)
	err = unit.Remove()
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(checkPrincipals(), gc.DeepEquals, []string{"wordpress/0"})
}
Beispiel #5
0
func (s *EnvUserSuite) TestCaseUserNameVsId(c *gc.C) {
	env, err := s.State.Environment()
	c.Assert(err, jc.ErrorIsNil)

	user, err := s.State.AddEnvironmentUser(names.NewUserTag("Bob@RandomProvider"), env.Owner(), "")
	c.Assert(err, gc.IsNil)
	c.Assert(user.UserName(), gc.Equals, "Bob@RandomProvider")
	c.Assert(user.ID(), gc.Equals, state.DocID(s.State, "bob@randomprovider"))
}
Beispiel #6
0
func (s *ModelUserSuite) TestCaseUserNameVsId(c *gc.C) {
	model, err := s.State.Model()
	c.Assert(err, jc.ErrorIsNil)

	user, err := s.State.AddModelUser(state.ModelUserSpec{
		User:      names.NewUserTag("Bob@RandomProvider"),
		CreatedBy: model.Owner()})
	c.Assert(err, gc.IsNil)
	c.Assert(user.UserName(), gc.Equals, "Bob@RandomProvider")
	c.Assert(user.ID(), gc.Equals, state.DocID(s.State, "bob@randomprovider"))
}
Beispiel #7
0
func (s *StorageStateSuite) storageInstanceExists(c *gc.C, tag names.StorageTag) bool {
	_, err := state.TxnRevno(
		s.State,
		state.StorageInstancesC,
		state.DocID(s.State, tag.Id()),
	)
	if err != nil {
		c.Assert(err, gc.Equals, mgo.ErrNotFound)
		return false
	}
	return true
}
Beispiel #8
0
func (s *CharmSuite) TestAddCharm(c *gc.C) {
	// Check that adding charms from scratch works correctly.
	info := s.dummyCharm(c, "")
	dummy, err := s.State.AddCharm(info)
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(dummy.URL().String(), gc.Equals, info.ID.String())

	doc := state.CharmDoc{}
	err = s.charms.FindId(state.DocID(s.State, info.ID.String())).One(&doc)
	c.Assert(err, jc.ErrorIsNil)
	c.Logf("%#v", doc)
	c.Assert(doc.URL, gc.DeepEquals, info.ID)
}
Beispiel #9
0
func (s *BlockDevicesSuite) TestSetMachineBlockDevicesUnchanged(c *gc.C) {
	sda := state.BlockDeviceInfo{DeviceName: "sda"}
	err := s.machine.SetMachineBlockDevices(sda)
	c.Assert(err, jc.ErrorIsNil)
	s.assertBlockDevices(c, s.machine.MachineTag(), []state.BlockDeviceInfo{sda})

	// Setting the same should not change txn-revno.
	docID := state.DocID(s.State, s.machine.Id())
	before, err := state.TxnRevno(s.State, state.BlockDevicesC, docID)
	c.Assert(err, jc.ErrorIsNil)

	err = s.machine.SetMachineBlockDevices(sda)
	c.Assert(err, jc.ErrorIsNil)

	after, err := state.TxnRevno(s.State, state.BlockDevicesC, docID)
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(after, gc.Equals, before)
}
Beispiel #10
0
func (s *CharmSuite) assertPlaceholderCharmExists(c *gc.C, curl *charm.URL) {
	// Find charm directly and verify only the charm URL and
	// Placeholder are set.
	doc := state.CharmDoc{}
	err := s.charms.FindId(state.DocID(s.State, curl.String())).One(&doc)
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(doc.URL, gc.DeepEquals, curl)
	c.Assert(doc.PendingUpload, jc.IsFalse)
	c.Assert(doc.Placeholder, jc.IsTrue)
	c.Assert(doc.Meta, gc.IsNil)
	c.Assert(doc.Config, gc.IsNil)
	c.Assert(doc.StoragePath, gc.Equals, "")
	c.Assert(doc.BundleSha256, gc.Equals, "")

	// Make sure we can't find it with st.Charm().
	_, err = s.State.Charm(curl)
	c.Assert(err, jc.Satisfies, errors.IsNotFound)
}
Beispiel #11
0
func (s *ActionSuite) TestMakeIdFilter(c *gc.C) {
	marker := "-marker-"
	badmarker := "-bad-"
	fn := state.WatcherMakeIdFilter(s.State, marker)
	c.Assert(fn, gc.IsNil)

	ar1 := mockAR{id: "mock/1"}
	ar2 := mockAR{id: "mock/2"}
	fn = state.WatcherMakeIdFilter(s.State, marker, ar1, ar2)
	c.Assert(fn, gc.Not(gc.IsNil))

	var tests = []struct {
		id    string
		match bool
	}{
		{id: "mock/1" + marker + "", match: true},
		{id: "mock/1" + marker + "asdf", match: true},
		{id: "mock/2" + marker + "", match: true},
		{id: "mock/2" + marker + "asdf", match: true},

		{id: "mock/1" + badmarker + "", match: false},
		{id: "mock/1" + badmarker + "asdf", match: false},
		{id: "mock/2" + badmarker + "", match: false},
		{id: "mock/2" + badmarker + "asdf", match: false},

		{id: "mock/1" + marker + "0", match: true},
		{id: "mock/10" + marker + "0", match: false},
		{id: "mock/2" + marker + "0", match: true},
		{id: "mock/20" + marker + "0", match: false},
		{id: "mock" + marker + "0", match: false},

		{id: "" + marker + "0", match: false},
		{id: "mock/1-0", match: false},
		{id: "mock/1-0", match: false},
	}

	for _, test := range tests {
		c.Assert(fn(state.DocID(s.State, test.id)), gc.Equals, test.match)
	}
}
Beispiel #12
0
func (l *unitLife) id() (coll string, id interface{}) {
	return "units", state.DocID(l.st, l.unit.Name())
}
Beispiel #13
0
func (l *machineLife) id() (coll string, id interface{}) {
	return "machines", state.DocID(l.st, l.machine.Id())
}
Beispiel #14
0
func (s *CollectionsSuite) TestEnvStateCollection(c *gc.C) {
	// The machines collection requires filtering by env UUID. Set up
	// 2 environments with machines in each.
	m0 := s.Factory.MakeMachine(c, nil)
	s.Factory.MakeMachine(c, nil)
	st1 := s.Factory.MakeEnvironment(c, nil)
	defer st1.Close()
	f1 := factory.NewFactory(st1)
	otherM0 := f1.MakeMachine(c, &factory.MachineParams{Series: "trusty"})

	// Ensure that the first machine in each env have overlapping ids
	// (otherwise tests may not fail when they should)
	c.Assert(m0.Id(), gc.Equals, otherM0.Id())

	getIfaceId := func(st *state.State) bson.ObjectId {
		var doc bson.M
		coll, closer := state.GetRawCollection(st, state.NetworkInterfacesC)
		defer closer()
		err := coll.Find(bson.D{{"env-uuid", st.EnvironUUID()}}).One(&doc)
		c.Assert(err, jc.ErrorIsNil)
		return doc["_id"].(bson.ObjectId)
	}

	// Also add a network interface to test collections with ObjectId ids
	_, err := s.State.AddNetwork(state.NetworkInfo{"net1", "net1", "0.1.2.3/24", 0})
	c.Assert(err, jc.ErrorIsNil)
	_, err = m0.AddNetworkInterface(state.NetworkInterfaceInfo{
		MACAddress:    "91:de:f1:02:f6:f0",
		InterfaceName: "foo0",
		NetworkName:   "net1",
	})
	c.Assert(err, jc.ErrorIsNil)

	// Grab the document id of the just added network interface for use in tests.
	ifaceId := getIfaceId(s.State)

	// Add a network interface to the other environment to test collections that rely on the env-uuid field.
	_, err = st1.AddNetwork(state.NetworkInfo{"net2", "net2", "0.1.2.4/24", 0})
	c.Assert(err, jc.ErrorIsNil)
	_, err = otherM0.AddNetworkInterface(state.NetworkInterfaceInfo{
		MACAddress:    "91:de:f1:02:f6:f0",
		InterfaceName: "foo1",
		NetworkName:   "net2",
	})
	c.Assert(err, jc.ErrorIsNil)

	// Grab the document id of the network interface just added to the other environment for use in tests.
	otherIfaceId := getIfaceId(st1)

	machines0, closer := state.GetCollection(s.State, state.MachinesC)
	defer closer()
	machines1, closer := state.GetCollection(st1, state.MachinesC)
	defer closer()
	networkInterfaces, closer := state.GetCollection(s.State, state.NetworkInterfacesC)
	defer closer()

	machinesSnapshot := newCollectionSnapshot(c, machines0.Writeable().Underlying())
	networkInterfacesSnapshot := newCollectionSnapshot(c, networkInterfaces.Writeable().Underlying())

	c.Assert(machines0.Name(), gc.Equals, state.MachinesC)
	c.Assert(networkInterfaces.Name(), gc.Equals, state.NetworkInterfacesC)

	for i, t := range []collectionsTestCase{
		{
			label: "Count filters by env",
			test: func() (int, error) {
				return machines0.Count()
			},
			expectedCount: 2,
		},
		{
			label: "Find filters by env",
			test: func() (int, error) {
				return machines0.Find(bson.D{{"series", m0.Series()}}).Count()
			},
			expectedCount: 2,
		},
		{
			label: "Find adds env UUID when _id is provided",
			test: func() (int, error) {
				return machines0.Find(bson.D{{"_id", m0.Id()}}).Count()
			},
			expectedCount: 1,
		},
		{
			label: "Find tolerates env UUID prefix already being present",
			test: func() (int, error) {
				return machines0.Find(bson.D{
					{"_id", state.DocID(s.State, m0.Id())},
				}).Count()
			},
			expectedCount: 1,
		},
		{
			label: "Find with no selector still filters by env",
			test: func() (int, error) {
				return machines0.Find(nil).Count()
			},
			expectedCount: 2,
		},
		{
			label: "Find leaves _id alone if used with operator",
			test: func() (int, error) {
				return machines0.Find(bson.D{
					{"_id", bson.D{{"$regex", ":" + m0.Id() + "$"}}},
				}).Count()
			},
			expectedCount: 1, // not 2 because env-uuid filter is still added
		},
		{
			label: "Find works with collections with ObjectId ids",
			test: func() (int, error) {
				return networkInterfaces.Find(bson.D{{"interfacename", "foo0"}}).Count()
			},
			expectedCount: 1,
		},
		{
			label: "Find works with ObjectId ids",
			test: func() (int, error) {
				return networkInterfaces.Find(bson.D{{"_id", ifaceId}}).Count()
			},
			expectedCount: 1,
		},
		{
			label: "Find panics if env-uuid is included",
			test: func() (int, error) {
				machines0.Find(bson.D{{"env-uuid", "whatever"}})
				return 0, nil
			},
			expectedPanic: "env-uuid is added automatically and should not be provided",
		},
		{
			label: "Find panics if query type is unsupported",
			test: func() (int, error) {
				machines0.Find(map[string]string{"foo": "bar"})
				return 0, nil
			},
			expectedPanic: "query must be bson.D, bson.M, or nil",
		},
		{
			label: "FindId adds env UUID prefix",
			test: func() (int, error) {
				return machines0.FindId(m0.Id()).Count()
			},
			expectedCount: 1,
		},
		{
			label: "FindId tolerates env UUID prefix already being there",
			test: func() (int, error) {
				return machines0.FindId(state.DocID(s.State, m0.Id())).Count()
			},
			expectedCount: 1,
		},
		{
			label: "FindId works with ObjectId ids",
			test: func() (int, error) {
				return networkInterfaces.FindId(ifaceId).Count()
			},
			expectedCount: 1,
		},
		{
			label: "FindId adds env-uuid field",
			test: func() (int, error) {
				return networkInterfaces.FindId(otherIfaceId).Count()
			},
			// expect to find no networks, as we are searching with the id of
			// the network in the other environment.
			expectedCount: 0,
		},
		{
			label: "Insert works",
			test: func() (int, error) {
				err := machines0.Writeable().Insert(bson.D{
					{"_id", state.DocID(s.State, "99")},
					{"machineid", 99},
					{"env-uuid", s.State.EnvironUUID()},
				})
				c.Assert(err, jc.ErrorIsNil)
				return machines0.Count()
			},
			expectedCount: 3,
		},
		{
			label: "Remove adds env UUID prefix to _id",
			test: func() (int, error) {
				err := machines0.Writeable().Remove(bson.D{{"_id", "0"}})
				c.Assert(err, jc.ErrorIsNil)
				return s.machines.Count()
			},
			expectedCount: 2, // Expect machine-1 in first env and machine-0 in second env
		},
		{
			label: "Remove filters by env",
			test: func() (int, error) {
				// Attempt to remove the trusty machine in the second
				// env with the collection that's filtering for the
				// first env - nothing should get removed.
				err := machines0.Writeable().Remove(bson.D{{"series", "trusty"}})
				c.Assert(err, gc.ErrorMatches, "not found")
				return s.machines.Count()
			},
			expectedCount: 3, // Expect all machines to still be there.
		},
		{
			label: "Remove filters by env 2",
			test: func() (int, error) {
				err := machines0.Writeable().Remove(bson.D{{"machineid", "0"}})
				c.Assert(err, jc.ErrorIsNil)
				return s.machines.Count()
			},
			expectedCount: 2, // Expect machine 1 in first env and machine-0 in second env
		},
		{
			label: "RemoveId adds env UUID prefix",
			test: func() (int, error) {
				err := machines0.Writeable().RemoveId(m0.Id())
				c.Assert(err, jc.ErrorIsNil)
				return s.machines.Count()
			},
			expectedCount: 2, // Expect machine-1 in first env and machine-0 in second env
		},
		{
			label: "RemoveId tolerates env UUID prefix already being there",
			test: func() (int, error) {
				err := machines0.Writeable().RemoveId(state.DocID(s.State, m0.Id()))
				c.Assert(err, jc.ErrorIsNil)
				return s.machines.Count()
			},
			expectedCount: 2, // Expect machine-1 in first env and machine-0 in second env
		},
		{
			label: "RemoveId filters by env-uuid field",
			test: func() (int, error) {
				err := networkInterfaces.Writeable().RemoveId(otherIfaceId)
				c.Assert(err, gc.ErrorMatches, "not found")
				return networkInterfaces.Count()
			},
			expectedCount: 1, // ensure doc was not removed
		},
		{
			label: "RemoveAll filters by env",
			test: func() (int, error) {
				_, err := machines0.Writeable().RemoveAll(bson.D{{"series", m0.Series()}})
				c.Assert(err, jc.ErrorIsNil)
				return s.machines.Count()
			},
			expectedCount: 1, // Expect machine-1 in second env
		},
		{
			label: "RemoveAll adds env UUID when _id is provided",
			test: func() (int, error) {
				_, err := machines0.Writeable().RemoveAll(bson.D{{"_id", m0.Id()}})
				c.Assert(err, jc.ErrorIsNil)
				return s.machines.Count()
			},
			expectedCount: 2, // Expect machine-1 in first env and machine-0 in second env
		},
		{
			label: "RemoveAll tolerates env UUID prefix already being present",
			test: func() (int, error) {
				_, err := machines0.Writeable().RemoveAll(bson.D{
					{"_id", state.DocID(s.State, m0.Id())},
				})
				c.Assert(err, jc.ErrorIsNil)
				return s.machines.Count()
			},
			expectedCount: 2, // Expect machine-1 in first env and machine-0 in second env
		},
		{
			label: "RemoveAll with no selector still filters by env",
			test: func() (int, error) {
				_, err := machines0.Writeable().RemoveAll(nil)
				c.Assert(err, jc.ErrorIsNil)
				return s.machines.Count()
			},
			expectedCount: 1, // Expect machine-0 in second env
		},
		{
			label: "RemoveAll panics if env-uuid is included",
			test: func() (int, error) {
				machines0.Writeable().RemoveAll(bson.D{{"env-uuid", "whatever"}})
				return 0, nil
			},
			expectedPanic: "env-uuid is added automatically and should not be provided",
		},
		{
			label: "RemoveAll panics if query type is unsupported",
			test: func() (int, error) {
				machines0.Writeable().RemoveAll(map[string]string{"foo": "bar"})
				return 0, nil
			},
			expectedPanic: "query must be bson.D, bson.M, or nil",
		},
		{
			label: "Update",
			test: func() (int, error) {
				err := machines0.Writeable().Update(bson.D{{"_id", m0.Id()}},
					bson.D{{"$set", bson.D{{"update-field", "field value"}}}})
				c.Assert(err, jc.ErrorIsNil)
				return machines0.Find(bson.D{{"update-field", "field value"}}).Count()
			},
			expectedCount: 1,
		},
		{
			label: "UpdateId",
			test: func() (int, error) {
				err := machines0.Writeable().UpdateId(m0.Id(),
					bson.D{{"$set", bson.D{{"update-field", "field value"}}}})
				c.Assert(err, jc.ErrorIsNil)
				return machines0.Find(bson.D{{"update-field", "field value"}}).Count()
			},
			expectedCount: 1,
		},
	} {
		c.Logf("test %d: %s", i, t.label)
		machinesSnapshot.restore(c)
		networkInterfacesSnapshot.restore(c)

		if t.expectedPanic == "" {
			count, err := t.test()
			if t.expectedError != "" {
				c.Assert(err, gc.ErrorMatches, t.expectedError)
			} else {
				c.Assert(err, jc.ErrorIsNil)
			}
			c.Check(count, gc.Equals, t.expectedCount)
		} else {
			c.Check(func() { t.test() }, gc.PanicMatches, t.expectedPanic)
		}

		// Check that other environment is untouched after each test
		count, err := machines1.Count()
		c.Assert(err, jc.ErrorIsNil)
		c.Check(count, gc.Equals, 1)
	}
}
Beispiel #15
0
func (s *collectionSuite) TestModelStateCollection(c *gc.C) {
	// The machines collection requires filtering by model UUID. Set up
	// 2 models with machines in each.
	m0 := s.Factory.MakeMachine(c, nil)
	s.Factory.MakeMachine(c, nil)
	st1 := s.Factory.MakeModel(c, nil)
	defer st1.Close()
	f1 := factory.NewFactory(st1)
	otherM0 := f1.MakeMachine(c, &factory.MachineParams{Series: "trusty"})

	// Ensure that the first machine in each model have overlapping ids
	// (otherwise tests may not fail when they should)
	c.Assert(m0.Id(), gc.Equals, otherM0.Id())

	machines0, closer := state.GetCollection(s.State, state.MachinesC)
	defer closer()
	machines1, closer := state.GetCollection(st1, state.MachinesC)
	defer closer()

	machinesSnapshot := newCollectionSnapshot(c, machines0.Writeable().Underlying())

	c.Assert(machines0.Name(), gc.Equals, state.MachinesC)

	for i, t := range []collectionTestCase{
		{
			label: "Count filters by model",
			test: func() (int, error) {
				return machines0.Count()
			},
			expectedCount: 2,
		},
		{
			label: "Find filters by model",
			test: func() (int, error) {
				return machines0.Find(bson.D{{"series", m0.Series()}}).Count()
			},
			expectedCount: 2,
		},
		{
			label: "Find adds model UUID when _id is provided",
			test: func() (int, error) {
				return machines0.Find(bson.D{{"_id", m0.Id()}}).Count()
			},
			expectedCount: 1,
		},
		{
			label: "Find tolerates model UUID prefix already being present",
			test: func() (int, error) {
				return machines0.Find(bson.D{
					{"_id", state.DocID(s.State, m0.Id())},
				}).Count()
			},
			expectedCount: 1,
		},
		{
			label: "Find with no selector still filters by model",
			test: func() (int, error) {
				return machines0.Find(nil).Count()
			},
			expectedCount: 2,
		},
		{
			label: "Find leaves _id alone if used with operator",
			test: func() (int, error) {
				return machines0.Find(bson.D{
					{"_id", bson.D{{"$regex", ":" + m0.Id() + "$"}}},
				}).Count()
			},
			expectedCount: 1, // not 2 because model-uuid filter is still added
		},
		{
			label: "Find works with maps",
			test: func() (int, error) {
				return machines0.Find(map[string]string{"_id": m0.Id()}).Count()
			},
			expectedCount: 1,
		},
		{
			label: "Find panics if model-uuid is included",
			test: func() (int, error) {
				machines0.Find(bson.D{{"model-uuid", "whatever"}})
				return 0, nil
			},
			expectedPanic: "model-uuid is added automatically and should not be provided",
		},
		{
			label: "FindId adds model UUID prefix",
			test: func() (int, error) {
				return machines0.FindId(m0.Id()).Count()
			},
			expectedCount: 1,
		},
		{
			label: "FindId tolerates model UUID prefix already being there",
			test: func() (int, error) {
				return machines0.FindId(state.DocID(s.State, m0.Id())).Count()
			},
			expectedCount: 1,
		},
		{
			label: "Insert adds model-uuid",
			test: func() (int, error) {
				err := machines0.Writeable().Insert(bson.D{
					{"_id", state.DocID(s.State, "99")},
					{"machineid", 99},
				})
				c.Assert(err, jc.ErrorIsNil)
				return machines0.Count()
			},
			expectedCount: 3,
		},
		{
			label: "Insert populates model-uuid if blank",
			test: func() (int, error) {
				err := machines0.Writeable().Insert(bson.D{
					{"_id", state.DocID(s.State, "99")},
					{"machineid", 99},
					{"model-uuid", ""},
				})
				c.Assert(err, jc.ErrorIsNil)
				return machines0.Count()
			},
			expectedCount: 3,
		},
		{
			label: "Insert prefixes _id",
			test: func() (int, error) {
				err := machines0.Writeable().Insert(bson.D{
					{"_id", "99"},
					{"machineid", 99},
				})
				c.Assert(err, jc.ErrorIsNil)
				return machines0.FindId("99").Count()
			},
			expectedCount: 1,
		},
		{
			label: "Insert tolerates prefixed _id and correct model-uuid if provided",
			test: func() (int, error) {
				err := machines0.Writeable().Insert(bson.D{
					{"_id", state.DocID(s.State, "99")},
					{"machineid", 99},
					{"model-uuid", s.State.ModelUUID()},
				})
				c.Assert(err, jc.ErrorIsNil)
				return machines0.Count()
			},
			expectedCount: 3,
		},
		{
			label: "Insert fails if model-uuid doesn't match",
			test: func() (int, error) {
				err := machines0.Writeable().Insert(bson.D{
					{"_id", "99"},
					{"machineid", 99},
					{"model-uuid", "something-else"},
				})
				return 0, err
			},
			expectedError: "bad \"model-uuid\" value: .+",
		},
		{
			label: "Remove adds model UUID prefix to _id",
			test: func() (int, error) {
				err := machines0.Writeable().Remove(bson.D{{"_id", "0"}})
				c.Assert(err, jc.ErrorIsNil)
				return s.machines.Count()
			},
			expectedCount: 2, // Expect machine-1 in first model and machine-0 in second model
		},
		{
			label: "Remove filters by model",
			test: func() (int, error) {
				// Attempt to remove the trusty machine in the second
				// model with the collection that's filtering for the
				// first model - nothing should get removed.
				err := machines0.Writeable().Remove(bson.D{{"series", "trusty"}})
				c.Assert(err, gc.ErrorMatches, "not found")
				return s.machines.Count()
			},
			expectedCount: 3, // Expect all machines to still be there.
		},
		{
			label: "Remove filters by model 2",
			test: func() (int, error) {
				err := machines0.Writeable().Remove(bson.D{{"machineid", "0"}})
				c.Assert(err, jc.ErrorIsNil)
				return s.machines.Count()
			},
			expectedCount: 2, // Expect machine 1 in first model and machine-0 in second model
		},
		{
			label: "RemoveId adds model UUID prefix",
			test: func() (int, error) {
				err := machines0.Writeable().RemoveId(m0.Id())
				c.Assert(err, jc.ErrorIsNil)
				return s.machines.Count()
			},
			expectedCount: 2, // Expect machine-1 in first model and machine-0 in second model
		},
		{
			label: "RemoveId tolerates model UUID prefix already being there",
			test: func() (int, error) {
				err := machines0.Writeable().RemoveId(state.DocID(s.State, m0.Id()))
				c.Assert(err, jc.ErrorIsNil)
				return s.machines.Count()
			},
			expectedCount: 2, // Expect machine-1 in first model and machine-0 in second model
		},
		{
			label: "RemoveAll filters by model",
			test: func() (int, error) {
				_, err := machines0.Writeable().RemoveAll(bson.D{{"series", m0.Series()}})
				c.Assert(err, jc.ErrorIsNil)
				return s.machines.Count()
			},
			expectedCount: 1, // Expect machine-1 in second model
		},
		{
			label: "RemoveAll adds model UUID when _id is provided",
			test: func() (int, error) {
				_, err := machines0.Writeable().RemoveAll(bson.D{{"_id", m0.Id()}})
				c.Assert(err, jc.ErrorIsNil)
				return s.machines.Count()
			},
			expectedCount: 2, // Expect machine-1 in first model and machine-0 in second model
		},
		{
			label: "RemoveAll tolerates model UUID prefix already being present",
			test: func() (int, error) {
				_, err := machines0.Writeable().RemoveAll(bson.D{
					{"_id", state.DocID(s.State, m0.Id())},
				})
				c.Assert(err, jc.ErrorIsNil)
				return s.machines.Count()
			},
			expectedCount: 2, // Expect machine-1 in first model and machine-0 in second model
		},
		{
			label: "RemoveAll with no selector still filters by model",
			test: func() (int, error) {
				_, err := machines0.Writeable().RemoveAll(nil)
				c.Assert(err, jc.ErrorIsNil)
				return s.machines.Count()
			},
			expectedCount: 1, // Expect machine-0 in second model
		},
		{
			label: "RemoveAll panics if model-uuid is included",
			test: func() (int, error) {
				machines0.Writeable().RemoveAll(bson.D{{"model-uuid", "whatever"}})
				return 0, nil
			},
			expectedPanic: "model-uuid is added automatically and should not be provided",
		},
		{
			label: "Update",
			test: func() (int, error) {
				err := machines0.Writeable().Update(bson.D{{"_id", m0.Id()}},
					bson.D{{"$set", bson.D{{"update-field", "field value"}}}})
				c.Assert(err, jc.ErrorIsNil)
				return machines0.Find(bson.D{{"update-field", "field value"}}).Count()
			},
			expectedCount: 1,
		},
		{
			label: "UpdateId",
			test: func() (int, error) {
				err := machines0.Writeable().UpdateId(m0.Id(),
					bson.D{{"$set", bson.D{{"update-field", "field value"}}}})
				c.Assert(err, jc.ErrorIsNil)
				return machines0.Find(bson.D{{"update-field", "field value"}}).Count()
			},
			expectedCount: 1,
		},
	} {
		c.Logf("test %d: %s", i, t.label)
		machinesSnapshot.restore(c)

		if t.expectedPanic == "" {
			count, err := t.test()
			if t.expectedError != "" {
				c.Assert(err, gc.ErrorMatches, t.expectedError)
			} else {
				c.Assert(err, jc.ErrorIsNil)
			}
			c.Check(count, gc.Equals, t.expectedCount)
		} else {
			c.Check(func() { t.test() }, gc.PanicMatches, t.expectedPanic)
		}

		// Check that other model is untouched after each test
		count, err := machines1.Count()
		c.Assert(err, jc.ErrorIsNil)
		c.Check(count, gc.Equals, 1)
	}
}