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

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/mgo/v3/bson"
    11  	"github.com/juju/mgo/v3/txn"
    12  	"github.com/juju/names/v5"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/core/network"
    17  	"github.com/juju/juju/state"
    18  )
    19  
    20  type SubnetSuite struct {
    21  	ConnSuite
    22  }
    23  
    24  var _ = gc.Suite(&SubnetSuite{})
    25  
    26  func (s *SubnetSuite) TestAddSubnetSucceedsWithFullyPopulatedInfo(c *gc.C) {
    27  	space, err := s.State.AddSpace("foo", "4", nil, true)
    28  	c.Assert(err, jc.ErrorIsNil)
    29  
    30  	fanOverlaySubnetInfo := network.SubnetInfo{
    31  		ProviderId: "foo2",
    32  		CIDR:       "10.0.0.0/8",
    33  		SpaceID:    space.Id(),
    34  	}
    35  	subnet, err := s.State.AddSubnet(fanOverlaySubnetInfo)
    36  	c.Assert(err, jc.ErrorIsNil)
    37  	s.assertSubnetMatchesInfo(c, subnet, fanOverlaySubnetInfo)
    38  	subnetInfo := network.SubnetInfo{
    39  		ProviderId:        "foo",
    40  		CIDR:              "192.168.1.0/24",
    41  		VLANTag:           79,
    42  		AvailabilityZones: []string{"Timbuktu"},
    43  		ProviderNetworkId: "wildbirds",
    44  		IsPublic:          true,
    45  	}
    46  	subnetInfo.SetFan("10.0.0.0/8", "172.16.0.0/16")
    47  
    48  	subnet, err = s.State.AddSubnet(subnetInfo)
    49  	c.Assert(err, jc.ErrorIsNil)
    50  
    51  	// Set the expected space after adding the subnet to state.
    52  	// When retrieved, it should inherit the space of its underlay.
    53  	subnetInfo.SpaceID = space.Id()
    54  
    55  	s.assertSubnetMatchesInfo(c, subnet, subnetInfo)
    56  
    57  	// check it's been stored in state by fetching it back again
    58  	subnetFromDB, err := s.State.SubnetByCIDR("192.168.1.0/24")
    59  	c.Assert(err, jc.ErrorIsNil)
    60  	s.assertSubnetMatchesInfo(c, subnetFromDB, subnetInfo)
    61  }
    62  
    63  func (s *SubnetSuite) assertSubnetMatchesInfo(c *gc.C, subnet *state.Subnet, info network.SubnetInfo) {
    64  	c.Assert(subnet.ProviderId(), gc.Equals, info.ProviderId)
    65  	c.Assert(subnet.CIDR(), gc.Equals, info.CIDR)
    66  	c.Assert(subnet.VLANTag(), gc.Equals, info.VLANTag)
    67  	c.Assert(subnet.AvailabilityZones(), gc.DeepEquals, info.AvailabilityZones)
    68  	c.Assert(subnet.String(), gc.Equals, info.CIDR)
    69  	c.Assert(subnet.GoString(), gc.Equals, info.CIDR)
    70  	expectedSubnetID := info.SpaceID
    71  	if expectedSubnetID == "" {
    72  		expectedSubnetID = "0"
    73  	}
    74  	c.Check(subnet.SpaceID(), gc.Equals, expectedSubnetID)
    75  	c.Assert(subnet.ProviderNetworkId(), gc.Equals, info.ProviderNetworkId)
    76  	c.Assert(subnet.FanLocalUnderlay(), gc.Equals, info.FanLocalUnderlay())
    77  	c.Assert(subnet.FanOverlay(), gc.Equals, info.FanOverlay())
    78  	c.Assert(subnet.IsPublic(), gc.Equals, info.IsPublic)
    79  }
    80  
    81  func (s *SubnetSuite) TestAddSubnetFailsWithEmptyCIDR(c *gc.C) {
    82  	subnetInfo := network.SubnetInfo{}
    83  	_ = s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, "missing CIDR")
    84  }
    85  
    86  func (s *SubnetSuite) assertAddSubnetForInfoFailsWithSuffix(c *gc.C, subnetInfo network.SubnetInfo, errorSuffix string) error {
    87  	subnet, err := s.State.AddSubnet(subnetInfo)
    88  	errorMessage := fmt.Sprintf("adding subnet %q: %s", subnetInfo.CIDR, errorSuffix)
    89  	c.Assert(err, gc.ErrorMatches, errorMessage)
    90  	c.Assert(subnet, gc.IsNil)
    91  	return err
    92  }
    93  
    94  func (s *SubnetSuite) TestAddSubnetFailsWithInvalidCIDR(c *gc.C) {
    95  	subnetInfo := network.SubnetInfo{CIDR: "foobar"}
    96  	_ = s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, "invalid CIDR address: foobar")
    97  }
    98  
    99  func (s *SubnetSuite) TestAddSubnetFailsWithOutOfRangeVLANTag(c *gc.C) {
   100  	subnetInfo := network.SubnetInfo{CIDR: "192.168.0.1/24", VLANTag: 4095}
   101  	_ = s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, "invalid VLAN tag 4095: must be between 0 and 4094")
   102  }
   103  
   104  func (s *SubnetSuite) TestAddSubnetFailsWithAlreadyExistsForDuplicateCIDRInSameModel(c *gc.C) {
   105  	subnetInfo := network.SubnetInfo{CIDR: "192.168.0.1/24"}
   106  	subnet, err := s.State.AddSubnet(subnetInfo)
   107  	c.Assert(err, jc.ErrorIsNil)
   108  	s.assertSubnetMatchesInfo(c, subnet, subnetInfo)
   109  
   110  	err = s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, `subnet "192.168.0.1/24" already exists`)
   111  	c.Assert(err, jc.Satisfies, errors.IsAlreadyExists)
   112  }
   113  
   114  func (s *SubnetSuite) TestAddSubnetSuccessForDuplicateCIDRDiffProviderIDInSameModel(c *gc.C) {
   115  	subnetInfo := network.SubnetInfo{CIDR: "192.168.0.1/24"}
   116  	subnet, err := s.State.AddSubnet(subnetInfo)
   117  	c.Assert(err, jc.ErrorIsNil)
   118  	s.assertSubnetMatchesInfo(c, subnet, subnetInfo)
   119  
   120  	subnetInfo.ProviderId = "testme"
   121  	subnet2, err := s.State.AddSubnet(subnetInfo)
   122  	c.Assert(err, jc.ErrorIsNil)
   123  	c.Assert(subnet.ID(), gc.Not(gc.Equals), subnet2.ID())
   124  	c.Assert(subnet.CIDR(), gc.Equals, subnet2.CIDR())
   125  }
   126  
   127  func (s *SubnetSuite) TestAddSubnetSucceedsForDuplicateCIDRInDifferentModels(c *gc.C) {
   128  	subnetInfo1 := network.SubnetInfo{CIDR: "192.168.0.1/24"}
   129  	subnetInfo2 := network.SubnetInfo{CIDR: "10.0.0.0/24"}
   130  	subnet1State := s.NewStateForModelNamed(c, "other-model")
   131  
   132  	subnet1, subnet2 := s.addTwoSubnetsInDifferentModelsAssertSuccessAndReturnBoth(c, subnetInfo1, subnetInfo2, subnet1State)
   133  	s.assertSubnetMatchesInfo(c, subnet1, subnetInfo1)
   134  	s.assertSubnetMatchesInfo(c, subnet2, subnetInfo2)
   135  }
   136  
   137  func (s *SubnetSuite) addTwoSubnetsInDifferentModelsAssertSuccessAndReturnBoth(c *gc.C, info1, info2 network.SubnetInfo, otherState *state.State) (*state.Subnet, *state.Subnet) {
   138  	subnet1, err := otherState.AddSubnet(info1)
   139  	c.Assert(err, jc.ErrorIsNil)
   140  	subnet2, err := s.State.AddSubnet(info2)
   141  	c.Assert(err, jc.ErrorIsNil)
   142  
   143  	return subnet1, subnet2
   144  }
   145  
   146  func (s *SubnetSuite) TestAddSubnetFailsWhenProviderIdNotUniqueInSameModel(c *gc.C) {
   147  	subnetInfo1 := network.SubnetInfo{CIDR: "192.168.0.1/24", ProviderId: "foo"}
   148  	subnetInfo2 := network.SubnetInfo{CIDR: "10.0.0.0/24", ProviderId: "foo"}
   149  
   150  	s.addTwoSubnetsAndAssertSecondFailsWithSuffix(c, subnetInfo1, subnetInfo2, `provider ID "foo" not unique`)
   151  }
   152  
   153  func (s *SubnetSuite) addTwoSubnetsAndAssertSecondFailsWithSuffix(c *gc.C, info1, info2 network.SubnetInfo, errorSuffix string) {
   154  	s.addTwoSubnetsInDifferentModelsAndAssertSecondFailsWithSuffix(c, info1, info2, s.State, errorSuffix)
   155  }
   156  
   157  func (s *SubnetSuite) addTwoSubnetsInDifferentModelsAndAssertSecondFailsWithSuffix(c *gc.C, info1, info2 network.SubnetInfo, otherState *state.State, errorSuffix string) {
   158  	_, err := otherState.AddSubnet(info1)
   159  	c.Assert(err, jc.ErrorIsNil)
   160  
   161  	_ = s.assertAddSubnetForInfoFailsWithSuffix(c, info2, errorSuffix)
   162  }
   163  
   164  func (s *SubnetSuite) TestAddSubnetSucceedsWhenProviderIdNotUniqueInDifferentModels(c *gc.C) {
   165  	subnetInfo1 := network.SubnetInfo{CIDR: "192.168.0.1/24", ProviderId: "foo"}
   166  	subnetInfo2 := network.SubnetInfo{CIDR: "10.0.0.0/24", ProviderId: "foo"}
   167  	subnet1State := s.NewStateForModelNamed(c, "other-model")
   168  
   169  	subnet1, subnet2 := s.addTwoSubnetsInDifferentModelsAssertSuccessAndReturnBoth(c, subnetInfo1, subnetInfo2, subnet1State)
   170  	s.assertSubnetMatchesInfo(c, subnet1, subnetInfo1)
   171  	s.assertSubnetMatchesInfo(c, subnet2, subnetInfo2)
   172  }
   173  
   174  func (s *SubnetSuite) TestAddSubnetSucceedsForDifferentCIDRsAndEmptyProviderIdInSameModel(c *gc.C) {
   175  	subnetInfo1 := network.SubnetInfo{CIDR: "192.168.0.1/24", ProviderId: ""}
   176  	subnetInfo2 := network.SubnetInfo{CIDR: "10.0.0.0/24", ProviderId: ""}
   177  
   178  	subnet1, subnet2 := s.addTwoSubnetsAssertSuccessAndReturnBoth(c, subnetInfo1, subnetInfo2)
   179  	s.assertSubnetMatchesInfo(c, subnet1, subnetInfo1)
   180  	s.assertSubnetMatchesInfo(c, subnet2, subnetInfo2)
   181  }
   182  
   183  func (s *SubnetSuite) addTwoSubnetsAssertSuccessAndReturnBoth(c *gc.C, info1, info2 network.SubnetInfo) (*state.Subnet, *state.Subnet) {
   184  	return s.addTwoSubnetsInDifferentModelsAssertSuccessAndReturnBoth(c, info1, info2, s.State)
   185  }
   186  
   187  func (s *SubnetSuite) TestAddSubnetSucceedsForDifferentCIDRsAndEmptyProviderIdInDifferentModels(c *gc.C) {
   188  	subnetInfo1 := network.SubnetInfo{CIDR: "192.168.0.1/24", ProviderId: ""}
   189  	subnetInfo2 := network.SubnetInfo{CIDR: "10.0.0.0/24", ProviderId: ""}
   190  	subnet1State := s.NewStateForModelNamed(c, "other-model")
   191  
   192  	subnet1, subnet2 := s.addTwoSubnetsInDifferentModelsAssertSuccessAndReturnBoth(c, subnetInfo1, subnetInfo2, subnet1State)
   193  	s.assertSubnetMatchesInfo(c, subnet1, subnetInfo1)
   194  	s.assertSubnetMatchesInfo(c, subnet2, subnetInfo2)
   195  }
   196  
   197  func (s *SubnetSuite) TestEnsureDeadSetsLifeToDeadWhenAlive(c *gc.C) {
   198  	subnet := s.addAliveSubnet(c, "192.168.0.1/24")
   199  
   200  	s.ensureDeadAndAssertLifeIsDead(c, subnet)
   201  	s.refreshAndAssertSubnetLifeIs(c, subnet, state.Dead)
   202  }
   203  
   204  func (s *SubnetSuite) addAliveSubnet(c *gc.C, cidr string) *state.Subnet {
   205  	subnetInfo := network.SubnetInfo{CIDR: cidr}
   206  	subnet, err := s.State.AddSubnet(subnetInfo)
   207  	c.Assert(err, jc.ErrorIsNil)
   208  	c.Assert(subnet.Life(), gc.Equals, state.Alive)
   209  
   210  	return subnet
   211  }
   212  
   213  func (s *SubnetSuite) ensureDeadAndAssertLifeIsDead(c *gc.C, subnet *state.Subnet) {
   214  	err := subnet.EnsureDead()
   215  	c.Assert(err, jc.ErrorIsNil)
   216  	c.Assert(subnet.Life(), gc.Equals, state.Dead)
   217  }
   218  
   219  func (s *SubnetSuite) refreshAndAssertSubnetLifeIs(c *gc.C, subnet *state.Subnet, expectedLife state.Life) {
   220  	err := subnet.Refresh()
   221  	c.Assert(err, jc.ErrorIsNil)
   222  	c.Assert(subnet.Life(), gc.Equals, expectedLife)
   223  }
   224  
   225  func (s *SubnetSuite) TestEnsureDeadSetsLifeToDeadWhenNotAlive(c *gc.C) {
   226  	subnet := s.addAliveSubnet(c, "192.168.0.1/24")
   227  	s.ensureDeadAndAssertLifeIsDead(c, subnet)
   228  
   229  	s.ensureDeadAndAssertLifeIsDead(c, subnet)
   230  }
   231  
   232  func (s *SubnetSuite) TestRemoveFailsIfStillAlive(c *gc.C) {
   233  	subnet := s.addAliveSubnet(c, "192.168.0.1/24")
   234  
   235  	err := subnet.Remove()
   236  	c.Assert(err, gc.ErrorMatches, `cannot remove subnet "192.168.0.1/24": subnet is not dead`)
   237  	s.refreshAndAssertSubnetLifeIs(c, subnet, state.Alive)
   238  }
   239  
   240  func (s *SubnetSuite) TestRemoveSucceedsWhenSubnetIsNotAlive(c *gc.C) {
   241  	subnet := s.addAliveSubnet(c, "192.168.0.1/24")
   242  	s.ensureDeadAndAssertLifeIsDead(c, subnet)
   243  
   244  	s.removeSubnetAndAssertNotFound(c, subnet)
   245  }
   246  
   247  func (s *SubnetSuite) removeSubnetAndAssertNotFound(c *gc.C, subnet *state.Subnet) {
   248  	err := subnet.Remove()
   249  	c.Assert(err, jc.ErrorIsNil)
   250  	s.assertSubnetWithCIDRNotFound(c, subnet.CIDR())
   251  }
   252  
   253  func (s *SubnetSuite) assertSubnetWithCIDRNotFound(c *gc.C, cidr string) {
   254  	_, err := s.State.SubnetByCIDR(cidr)
   255  	s.assertSubnetNotFoundError(c, err)
   256  }
   257  
   258  func (s *SubnetSuite) assertSubnetNotFoundError(c *gc.C, err error) {
   259  	c.Assert(err, gc.ErrorMatches, "subnet .* not found")
   260  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   261  }
   262  
   263  func (s *SubnetSuite) TestRemoveSucceedsWhenCalledTwice(c *gc.C) {
   264  	subnet := s.addAliveSubnet(c, "192.168.0.1/24")
   265  	s.ensureDeadAndAssertLifeIsDead(c, subnet)
   266  	s.removeSubnetAndAssertNotFound(c, subnet)
   267  
   268  	err := subnet.Remove()
   269  	c.Assert(err, gc.ErrorMatches, `cannot remove subnet "192.168.0.1/24": not found or not dead`)
   270  }
   271  
   272  func (s *SubnetSuite) TestRefreshUpdatesStaleDocData(c *gc.C) {
   273  	subnet := s.addAliveSubnet(c, "fc00::/64")
   274  	subnetCopy, err := s.State.SubnetByCIDR("fc00::/64")
   275  	c.Assert(err, jc.ErrorIsNil)
   276  
   277  	s.ensureDeadAndAssertLifeIsDead(c, subnet)
   278  	c.Assert(subnetCopy.Life(), gc.Equals, state.Alive)
   279  
   280  	err = subnetCopy.Refresh()
   281  	c.Assert(err, jc.ErrorIsNil)
   282  	c.Assert(subnetCopy.Life(), gc.Equals, state.Dead)
   283  }
   284  
   285  func (s *SubnetSuite) TestRefreshFailsWithNotFoundWhenRemoved(c *gc.C) {
   286  	subnet := s.addAliveSubnet(c, "192.168.1.0/24")
   287  	s.ensureDeadAndAssertLifeIsDead(c, subnet)
   288  	s.removeSubnetAndAssertNotFound(c, subnet)
   289  
   290  	err := subnet.Refresh()
   291  	s.assertSubnetNotFoundError(c, err)
   292  }
   293  
   294  func (s *SubnetSuite) TestAllSubnets(c *gc.C) {
   295  	space1, err := s.State.AddSpace("bar", "4", nil, true)
   296  	c.Assert(err, jc.ErrorIsNil)
   297  	space2, err := s.State.AddSpace("notreally", "5", nil, true)
   298  	c.Assert(err, jc.ErrorIsNil)
   299  	subnetInfos := []network.SubnetInfo{
   300  		{CIDR: "192.168.1.0/24"},
   301  		{CIDR: "8.8.8.0/24", SpaceID: space1.Id()},
   302  		{CIDR: "10.0.2.0/24", ProviderId: "foo"},
   303  		{CIDR: "2001:db8::/64", AvailabilityZones: []string{"zone1"}},
   304  		{CIDR: "253.0.0.0/8", SpaceID: space2.Id()},
   305  	}
   306  	subnetInfos[4].SetFan("8.8.8.0/24", "")
   307  
   308  	for _, info := range subnetInfos {
   309  		_, err := s.State.AddSubnet(info)
   310  		c.Assert(err, jc.ErrorIsNil)
   311  	}
   312  
   313  	subnets, err := s.State.AllSubnets()
   314  	c.Assert(err, jc.ErrorIsNil)
   315  	c.Assert(subnets, gc.HasLen, len(subnetInfos))
   316  
   317  	for i, subnet := range subnets {
   318  		c.Check(subnet.CIDR(), gc.Equals, subnetInfos[i].CIDR)
   319  		c.Check(subnet.ProviderId(), gc.Equals, subnetInfos[i].ProviderId)
   320  		if subnet.FanLocalUnderlay() == "" {
   321  			expectedSubnetID := subnetInfos[i].SpaceID
   322  			if expectedSubnetID == "" {
   323  				expectedSubnetID = "0"
   324  			}
   325  			c.Check(subnet.SpaceID(), gc.Equals, expectedSubnetID)
   326  		} else {
   327  			// Special case
   328  			c.Check(subnet.SpaceID(), gc.Equals, space1.Id())
   329  		}
   330  		c.Check(subnet.AvailabilityZones(), gc.DeepEquals, subnetInfos[i].AvailabilityZones)
   331  	}
   332  }
   333  
   334  func (s *SubnetSuite) TestAllSubnetInfosPopulatesOverlaySpace(c *gc.C) {
   335  	space1, err := s.State.AddSpace("bar", "4", nil, true)
   336  	c.Assert(err, jc.ErrorIsNil)
   337  
   338  	subnetInfos := []network.SubnetInfo{
   339  		{CIDR: "8.8.8.0/24", SpaceID: space1.Id()},
   340  		{CIDR: "253.0.0.0/8"},
   341  	}
   342  	subnetInfos[1].SetFan("8.8.8.0/24", "")
   343  
   344  	for _, info := range subnetInfos {
   345  		_, err := s.State.AddSubnet(info)
   346  		c.Assert(err, jc.ErrorIsNil)
   347  	}
   348  
   349  	subnets, err := s.State.AllSubnetInfos()
   350  	c.Assert(err, jc.ErrorIsNil)
   351  	c.Assert(subnets, gc.HasLen, len(subnetInfos))
   352  
   353  	for _, subnet := range subnets {
   354  		if subnet.FanLocalUnderlay() == "" {
   355  			c.Check(subnet.SpaceID, gc.Equals, space1.Id())
   356  		}
   357  	}
   358  }
   359  
   360  func (s *SubnetSuite) TestUpdateMAASUndefinedSpace(c *gc.C) {
   361  	subnetInfo := network.SubnetInfo{CIDR: "8.8.8.0/24"}
   362  	subnet, err := s.State.AddSubnet(subnetInfo)
   363  	c.Assert(err, jc.ErrorIsNil)
   364  	_, err = s.State.AddSpace(names.NewSpaceTag("undefined").Id(), "-1", []string{subnet.ID()}, false)
   365  	c.Assert(err, jc.ErrorIsNil)
   366  
   367  	subnetInfo.SpaceName = "testme"
   368  	_, err = s.State.AddSpace(names.NewSpaceTag(subnetInfo.SpaceName).Id(), "2", []string{}, false)
   369  	c.Assert(err, jc.ErrorIsNil)
   370  
   371  	err = subnet.Update(subnetInfo)
   372  	c.Assert(err, jc.ErrorIsNil)
   373  
   374  	err = subnet.Refresh()
   375  	c.Assert(err, jc.ErrorIsNil)
   376  	c.Assert(subnet.SpaceName(), gc.Equals, subnetInfo.SpaceName)
   377  }
   378  
   379  func (s *SubnetSuite) TestUpdateEmpty(c *gc.C) {
   380  	subnetInfo := network.SubnetInfo{CIDR: "8.8.8.0/24"}
   381  	subnet, err := s.State.AddSubnet(subnetInfo)
   382  	c.Assert(err, jc.ErrorIsNil)
   383  
   384  	subnetInfo.VLANTag = 76
   385  	subnetInfo.AvailabilityZones = []string{"testme-az"}
   386  	subnetInfo.SpaceName = "testme"
   387  	_, err = s.State.AddSpace(names.NewSpaceTag(subnetInfo.SpaceName).Id(), "2", []string{}, false)
   388  	c.Assert(err, jc.ErrorIsNil)
   389  
   390  	err = subnet.Update(subnetInfo)
   391  	c.Assert(err, jc.ErrorIsNil)
   392  
   393  	err = subnet.Refresh()
   394  	c.Assert(err, jc.ErrorIsNil)
   395  	c.Assert(subnet.VLANTag(), gc.Equals, subnetInfo.VLANTag)
   396  	c.Assert(subnet.AvailabilityZones(), gc.DeepEquals, subnetInfo.AvailabilityZones)
   397  	c.Assert(subnet.SpaceName(), gc.Equals, subnetInfo.SpaceName)
   398  }
   399  
   400  func (s *SubnetSuite) TestUpdateNonEmpty(c *gc.C) {
   401  	expectedSubnetInfo := network.SubnetInfo{
   402  		CIDR: "8.8.8.0/24", VLANTag: 42, AvailabilityZones: []string{"changeme-az", "testme-az"}}
   403  	subnet, err := s.State.AddSubnet(expectedSubnetInfo)
   404  	c.Assert(err, jc.ErrorIsNil)
   405  
   406  	expectedSpace, err := s.State.AddSpace("changeme", "2", []string{subnet.ID()}, false)
   407  	c.Assert(err, jc.ErrorIsNil)
   408  
   409  	newSubnetInfo := network.SubnetInfo{
   410  		CIDR:              subnet.CIDR(),
   411  		SpaceName:         "testme",
   412  		VLANTag:           76,
   413  		AvailabilityZones: []string{"testme-az"},
   414  	}
   415  	_, err = s.State.AddSpace(names.NewSpaceTag(newSubnetInfo.SpaceName).Id(), "7", []string{}, false)
   416  	c.Assert(err, jc.ErrorIsNil)
   417  
   418  	err = subnet.Update(newSubnetInfo)
   419  	c.Assert(err, jc.ErrorIsNil)
   420  
   421  	err = subnet.Refresh()
   422  	c.Assert(err, jc.ErrorIsNil)
   423  	c.Assert(subnet.SpaceID(), gc.Equals, expectedSpace.Id())
   424  	c.Assert(subnet.VLANTag(), gc.Equals, expectedSubnetInfo.VLANTag)
   425  	c.Assert(subnet.AvailabilityZones(), gc.DeepEquals, expectedSubnetInfo.AvailabilityZones)
   426  }
   427  
   428  func (s *SubnetSuite) TestUniqueAdditionAndRetrievalByCIDR(c *gc.C) {
   429  	cidr := "1.1.1.1/24"
   430  
   431  	sub1 := network.SubnetInfo{
   432  		CIDR:              cidr,
   433  		ProviderId:        "1",
   434  		ProviderNetworkId: "1",
   435  	}
   436  	_, err := s.State.AddSubnet(sub1)
   437  	c.Assert(err, jc.ErrorIsNil)
   438  
   439  	sub2 := network.SubnetInfo{
   440  		CIDR:              cidr,
   441  		ProviderId:        "2",
   442  		ProviderNetworkId: "2",
   443  	}
   444  	_, err = s.State.AddSubnet(sub2)
   445  	c.Assert(err, jc.ErrorIsNil)
   446  
   447  	subs, err := s.State.SubnetsByCIDR(cidr)
   448  	c.Assert(err, jc.ErrorIsNil)
   449  	c.Check(subs, gc.HasLen, 2)
   450  
   451  	_, err = s.State.SubnetByCIDR(cidr)
   452  	c.Check(err, gc.ErrorMatches, fmt.Sprintf("multiple subnets matching %q", cidr))
   453  }
   454  
   455  func (s *SubnetSuite) TestUpdateSubnetSpaceOps(c *gc.C) {
   456  	space, err := s.State.AddSpace("space-0", "0", []string{}, false)
   457  	c.Assert(err, jc.ErrorIsNil)
   458  
   459  	arg := network.SubnetInfo{
   460  		CIDR:              "10.10.10.0/24",
   461  		ProviderId:        "1",
   462  		ProviderNetworkId: "1",
   463  		SpaceID:           space.Id(),
   464  	}
   465  
   466  	sub, err := s.State.AddSubnet(arg)
   467  	c.Assert(err, jc.ErrorIsNil)
   468  	c.Check(s.State.UpdateSubnetSpaceOps(sub.ID(), space.Id()), gc.IsNil)
   469  
   470  	ops := s.State.UpdateSubnetSpaceOps(sub.ID(), "666")
   471  	c.Assert(ops, gc.HasLen, 2)
   472  	for _, op := range ops {
   473  		if op.C == "spaces" {
   474  			c.Check(op.Id, gc.Equals, fmt.Sprintf("%s:666", s.State.ModelUUID()))
   475  			c.Check(op.Assert, gc.DeepEquals, txn.DocExists)
   476  		} else if op.C == "subnets" {
   477  			c.Check(op.Id, gc.Equals, fmt.Sprintf("%s:%s", s.State.ModelUUID(), sub.ID()))
   478  			c.Check(op.Update, gc.DeepEquals, bson.D{{"$set", bson.D{{"space-id", "666"}}}})
   479  			c.Check(op.Assert, gc.DeepEquals, bson.D{{"life", state.Alive}})
   480  		} else {
   481  			c.Fatalf("unexpected txn.Op collection: %q", op.C)
   482  		}
   483  	}
   484  }