github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/spaces_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  	"net"
     9  
    10  	"github.com/juju/errors"
    11  	jc "github.com/juju/testing/checkers"
    12  	gc "gopkg.in/check.v1"
    13  
    14  	"github.com/juju/juju/core/life"
    15  	"github.com/juju/juju/core/network"
    16  	"github.com/juju/juju/state"
    17  )
    18  
    19  type SpacesSuite struct {
    20  	ConnSuite
    21  }
    22  
    23  var _ = gc.Suite(&SpacesSuite{})
    24  
    25  func (s *SpacesSuite) addSubnets(c *gc.C, CIDRs []string) []string {
    26  	return s.addSubnetsForState(c, CIDRs, s.State)
    27  }
    28  
    29  func (s *SpacesSuite) addSubnetsForState(c *gc.C, CIDRs []string, st *state.State) []string {
    30  	if len(CIDRs) == 0 {
    31  		return nil
    32  	}
    33  	subnetIDs := make([]string, len(CIDRs))
    34  	for i, info := range s.makeSubnetInfosForCIDRs(c, CIDRs) {
    35  		subnet, err := st.AddSubnet(info)
    36  		c.Assert(err, jc.ErrorIsNil)
    37  		subnetIDs[i] = subnet.ID()
    38  	}
    39  	return subnetIDs
    40  }
    41  
    42  func (s *SpacesSuite) makeSubnetInfosForCIDRs(c *gc.C, CIDRs []string) []network.SubnetInfo {
    43  	infos := make([]network.SubnetInfo, len(CIDRs))
    44  	for i, cidr := range CIDRs {
    45  		_, _, err := net.ParseCIDR(cidr)
    46  		c.Assert(err, jc.ErrorIsNil)
    47  
    48  		infos[i] = network.SubnetInfo{
    49  			CIDR:              cidr,
    50  			VLANTag:           79,
    51  			AvailabilityZones: []string{"AvailabilityZone"},
    52  		}
    53  
    54  	}
    55  	return infos
    56  }
    57  
    58  type addSpaceArgs struct {
    59  	Name        string
    60  	ProviderId  network.Id
    61  	SubnetCIDRs []string
    62  	IsPublic    bool
    63  	ForState    *state.State
    64  }
    65  
    66  func (s *SpacesSuite) addSpaceWithSubnets(c *gc.C, args addSpaceArgs) (*state.Space, error) {
    67  	if args.ForState == nil {
    68  		args.ForState = s.State
    69  	}
    70  	subnetIDs := s.addSubnetsForState(c, args.SubnetCIDRs, args.ForState)
    71  	return args.ForState.AddSpace(args.Name, args.ProviderId, subnetIDs, args.IsPublic)
    72  }
    73  
    74  func (s *SpacesSuite) assertSpaceNotFound(c *gc.C, name string) {
    75  	s.assertSpaceNotFoundForState(c, name, s.State)
    76  }
    77  
    78  func (s *SpacesSuite) assertSpaceNotFoundForState(c *gc.C, name string, st *state.State) {
    79  	_, err := st.SpaceByName(name)
    80  	s.assertSpaceNotFoundError(c, err, name)
    81  }
    82  
    83  func (s *SpacesSuite) assertSpaceNotFoundError(c *gc.C, err error, name string) {
    84  	c.Assert(err, gc.ErrorMatches, fmt.Sprintf("space %q not found", name))
    85  
    86  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
    87  }
    88  
    89  func (s *SpacesSuite) assertSpaceMatchesArgs(c *gc.C, space *state.Space, args addSpaceArgs) {
    90  	c.Assert(space.Name(), gc.Equals, args.Name)
    91  	c.Assert(space.ProviderId(), gc.Equals, args.ProviderId)
    92  
    93  	actualSpaceInfo, err := space.NetworkSpace()
    94  	c.Assert(err, jc.ErrorIsNil)
    95  	actualSubnetIds := make([]string, len(actualSpaceInfo.Subnets))
    96  	for i, subnet := range actualSpaceInfo.Subnets {
    97  		actualSubnetIds[i] = subnet.CIDR
    98  	}
    99  	c.Assert(actualSubnetIds, jc.SameContents, args.SubnetCIDRs)
   100  	c.Assert(state.SpaceDoc(space).IsPublic, gc.Equals, args.IsPublic)
   101  
   102  	c.Assert(space.Life(), gc.Equals, state.Alive)
   103  	c.Assert(space.String(), gc.Equals, args.Name)
   104  
   105  	// The space ID is not empty and not equivalent to the default space.
   106  	c.Assert(space.Id(), gc.Not(gc.Equals), "")
   107  	c.Assert(space.Id(), gc.Not(gc.Equals), "0")
   108  }
   109  
   110  func (s *SpacesSuite) TestAddSpaceWithNoSubnetsAndEmptyProviderId(c *gc.C) {
   111  	args := addSpaceArgs{
   112  		Name:        "my-space",
   113  		ProviderId:  "",
   114  		SubnetCIDRs: nil,
   115  	}
   116  	space, err := s.addSpaceWithSubnets(c, args)
   117  	c.Assert(err, jc.ErrorIsNil)
   118  	s.assertSpaceMatchesArgs(c, space, args)
   119  }
   120  
   121  func (s *SpacesSuite) TestAddSpaceWithNoSubnetsAndNonEmptyProviderId(c *gc.C) {
   122  	args := addSpaceArgs{
   123  		Name:        "my-space",
   124  		ProviderId:  network.Id("my provider id"),
   125  		SubnetCIDRs: nil,
   126  	}
   127  	space, err := s.addSpaceWithSubnets(c, args)
   128  	c.Assert(err, jc.ErrorIsNil)
   129  	s.assertSpaceMatchesArgs(c, space, args)
   130  }
   131  
   132  func (s *SpacesSuite) TestAddSpaceWithOneIPv4SubnetAndEmptyProviderId(c *gc.C) {
   133  	args := addSpaceArgs{
   134  		Name:        "my-space",
   135  		ProviderId:  "",
   136  		SubnetCIDRs: []string{"1.1.1.0/24"},
   137  	}
   138  	space, err := s.addSpaceWithSubnets(c, args)
   139  	c.Assert(err, jc.ErrorIsNil)
   140  	s.assertSpaceMatchesArgs(c, space, args)
   141  }
   142  
   143  func (s *SpacesSuite) TestAddSpaceWithOneIPv4SubnetAndNonEmptyProviderId(c *gc.C) {
   144  	args := addSpaceArgs{
   145  		Name:        "my-space",
   146  		ProviderId:  network.Id("some id"),
   147  		SubnetCIDRs: []string{"1.1.1.0/24"},
   148  	}
   149  	space, err := s.addSpaceWithSubnets(c, args)
   150  	c.Assert(err, jc.ErrorIsNil)
   151  	s.assertSpaceMatchesArgs(c, space, args)
   152  }
   153  
   154  func (s *SpacesSuite) TestAddSpaceWithOneIPv6SubnetAndEmptyProviderId(c *gc.C) {
   155  	args := addSpaceArgs{
   156  		Name:        "my-space",
   157  		ProviderId:  "",
   158  		SubnetCIDRs: []string{"fc00:123::/64"},
   159  	}
   160  	space, err := s.addSpaceWithSubnets(c, args)
   161  	c.Assert(err, jc.ErrorIsNil)
   162  	s.assertSpaceMatchesArgs(c, space, args)
   163  }
   164  
   165  func (s *SpacesSuite) TestAddSpaceWithOneIPv6SubnetAndNonEmptyProviderId(c *gc.C) {
   166  	args := addSpaceArgs{
   167  		Name:        "my-space",
   168  		ProviderId:  network.Id("provider id"),
   169  		SubnetCIDRs: []string{"fc00:123::/64"},
   170  	}
   171  	space, err := s.addSpaceWithSubnets(c, args)
   172  	c.Assert(err, jc.ErrorIsNil)
   173  	s.assertSpaceMatchesArgs(c, space, args)
   174  }
   175  
   176  func (s *SpacesSuite) TestAddSpaceWithOneIPv4AndOneIPv6SubnetAndEmptyProviderId(c *gc.C) {
   177  	args := addSpaceArgs{
   178  		Name:        "my-space",
   179  		ProviderId:  "",
   180  		SubnetCIDRs: []string{"1.1.1.0/24", "fc00::/64"},
   181  	}
   182  	space, err := s.addSpaceWithSubnets(c, args)
   183  	c.Assert(err, jc.ErrorIsNil)
   184  	s.assertSpaceMatchesArgs(c, space, args)
   185  }
   186  
   187  func (s *SpacesSuite) TestAddSpaceWithOneIPv4AndOneIPv6SubnetAndNonEmptyProviderId(c *gc.C) {
   188  	args := addSpaceArgs{
   189  		Name:        "my-space",
   190  		ProviderId:  network.Id("foo bar"),
   191  		SubnetCIDRs: []string{"1.1.1.0/24", "fc00::/64"},
   192  	}
   193  	space, err := s.addSpaceWithSubnets(c, args)
   194  	c.Assert(err, jc.ErrorIsNil)
   195  	s.assertSpaceMatchesArgs(c, space, args)
   196  }
   197  
   198  func (s *SpacesSuite) TestAddSpaceWithMultipleIPv4SubnetsAndEmptyProviderId(c *gc.C) {
   199  	args := addSpaceArgs{
   200  		Name:        "my-space",
   201  		ProviderId:  "",
   202  		SubnetCIDRs: []string{"1.1.1.0/24", "1.2.2.0/24"},
   203  	}
   204  	space, err := s.addSpaceWithSubnets(c, args)
   205  	c.Assert(err, jc.ErrorIsNil)
   206  	s.assertSpaceMatchesArgs(c, space, args)
   207  }
   208  
   209  func (s *SpacesSuite) TestAddSpaceWithMultipleIPv4SubnetsAndNonEmptyProviderId(c *gc.C) {
   210  	args := addSpaceArgs{
   211  		Name:        "my-space",
   212  		ProviderId:  network.Id("My Provider ID"),
   213  		SubnetCIDRs: []string{"1.1.1.0/24", "1.2.2.0/24"},
   214  	}
   215  	space, err := s.addSpaceWithSubnets(c, args)
   216  	c.Assert(err, jc.ErrorIsNil)
   217  	s.assertSpaceMatchesArgs(c, space, args)
   218  }
   219  
   220  func (s *SpacesSuite) TestAddSpaceWithMultipleIPv6SubnetsAndEmptyProviderId(c *gc.C) {
   221  	args := addSpaceArgs{
   222  		Name:        "my-space",
   223  		ProviderId:  "",
   224  		SubnetCIDRs: []string{"fc00:123::/64", "fc00:321::/64"},
   225  	}
   226  	space, err := s.addSpaceWithSubnets(c, args)
   227  	c.Assert(err, jc.ErrorIsNil)
   228  	s.assertSpaceMatchesArgs(c, space, args)
   229  }
   230  
   231  func (s *SpacesSuite) TestAddSpaceWithMultipleIPv6SubnetsAndNonEmptyProviderId(c *gc.C) {
   232  	args := addSpaceArgs{
   233  		Name:        "my-space",
   234  		ProviderId:  network.Id("My Provider ID"),
   235  		SubnetCIDRs: []string{"fc00:123::/64", "fc00:321::/64"},
   236  	}
   237  	space, err := s.addSpaceWithSubnets(c, args)
   238  
   239  	c.Assert(err, jc.ErrorIsNil)
   240  	s.assertSpaceMatchesArgs(c, space, args)
   241  }
   242  
   243  func (s *SpacesSuite) TestAddSpaceWithMultipleIPv4AndIPv6SubnetsAndEmptyProviderId(c *gc.C) {
   244  	args := addSpaceArgs{
   245  		Name:        "my-space",
   246  		ProviderId:  "",
   247  		SubnetCIDRs: []string{"fc00:123::/64", "2.2.0.0/20", "fc00:321::/64", "1.1.1.0/24"},
   248  	}
   249  	space, err := s.addSpaceWithSubnets(c, args)
   250  
   251  	c.Assert(err, jc.ErrorIsNil)
   252  	s.assertSpaceMatchesArgs(c, space, args)
   253  
   254  }
   255  
   256  func (s *SpacesSuite) TestAddSpaceWithMultipleIPv4AndIPv6SubnetsAndNonEmptyProviderId(c *gc.C) {
   257  	args := addSpaceArgs{
   258  		Name:        "my-space",
   259  		ProviderId:  network.Id("My Provider ID"),
   260  		SubnetCIDRs: []string{"fc00:123::/64", "2.2.0.0/20", "fc00:321::/64", "1.1.1.0/24"},
   261  	}
   262  	space, err := s.addSpaceWithSubnets(c, args)
   263  	c.Assert(err, jc.ErrorIsNil)
   264  	s.assertSpaceMatchesArgs(c, space, args)
   265  }
   266  
   267  func (s *SpacesSuite) addTwoSpacesReturnSecond(c *gc.C, args1, args2 addSpaceArgs) (*state.Space, error) {
   268  	space1, err := s.addSpaceWithSubnets(c, args1)
   269  
   270  	c.Assert(err, jc.ErrorIsNil)
   271  	s.assertSpaceMatchesArgs(c, space1, args1)
   272  
   273  	return s.addSpaceWithSubnets(c, args2)
   274  }
   275  
   276  func (s *SpacesSuite) TestAddTwoSpacesWithDifferentNamesButSameProviderIdFailsInSameModel(c *gc.C) {
   277  	args1 := addSpaceArgs{
   278  		Name:       "my-space",
   279  		ProviderId: network.Id("provider id"),
   280  	}
   281  	args2 := args1
   282  	args2.Name = "different"
   283  
   284  	_, err := s.addTwoSpacesReturnSecond(c, args1, args2)
   285  	s.assertProviderIdNotUniqueErrorForArgs(c, err, args2)
   286  }
   287  
   288  func (s *SpacesSuite) assertProviderIdNotUniqueErrorForArgs(c *gc.C, err error, args addSpaceArgs) {
   289  	expectedError := fmt.Sprintf("adding space %q: provider ID %q not unique", args.Name, args.ProviderId)
   290  	c.Assert(err, gc.ErrorMatches, expectedError)
   291  }
   292  
   293  func (s *SpacesSuite) TestAddTwoSpacesWithDifferentNamesButSameProviderIdSucceedsInDifferentModels(c *gc.C) {
   294  	args1 := addSpaceArgs{
   295  		Name:       "my-space",
   296  		ProviderId: network.Id("provider id"),
   297  		ForState:   s.State,
   298  	}
   299  	args2 := args1
   300  	args2.Name = "different"
   301  	args2.ForState = s.NewStateForModelNamed(c, "other")
   302  
   303  	space2, err := s.addTwoSpacesReturnSecond(c, args1, args2)
   304  
   305  	c.Assert(err, jc.ErrorIsNil)
   306  	s.assertSpaceMatchesArgs(c, space2, args2)
   307  }
   308  
   309  func (s *SpacesSuite) TestAddTwoSpacesWithDifferentNamesAndEmptyProviderIdSucceedsInSameModel(c *gc.C) {
   310  	args1 := addSpaceArgs{
   311  		Name:       "my-space",
   312  		ProviderId: "",
   313  	}
   314  	args2 := args1
   315  	args2.Name = "different"
   316  
   317  	space2, err := s.addTwoSpacesReturnSecond(c, args1, args2)
   318  	c.Assert(err, jc.ErrorIsNil)
   319  	s.assertSpaceMatchesArgs(c, space2, args2)
   320  }
   321  
   322  func (s *SpacesSuite) TestAddTwoSpacesWithDifferentNamesAndEmptyProviderIdSucceedsInDifferentModels(c *gc.C) {
   323  	args1 := addSpaceArgs{
   324  		Name:       "my-space",
   325  		ProviderId: "",
   326  		ForState:   s.State,
   327  	}
   328  	args2 := args1
   329  	args2.Name = "different"
   330  	args2.ForState = s.NewStateForModelNamed(c, "other")
   331  
   332  	space2, err := s.addTwoSpacesReturnSecond(c, args1, args2)
   333  
   334  	c.Assert(err, jc.ErrorIsNil)
   335  	s.assertSpaceMatchesArgs(c, space2, args2)
   336  }
   337  
   338  func (s *SpacesSuite) TestAddTwoSpacesWithSameNamesAndEmptyProviderIdsFailsInSameModel(c *gc.C) {
   339  	args := addSpaceArgs{
   340  		Name:       "my-space",
   341  		ProviderId: "",
   342  	}
   343  
   344  	_, err := s.addTwoSpacesReturnSecond(c, args, args)
   345  	s.assertSpaceAlreadyExistsErrorForArgs(c, err, args)
   346  }
   347  
   348  func (s *SpacesSuite) assertSpaceAlreadyExistsErrorForArgs(c *gc.C, err error, args addSpaceArgs) {
   349  	expectedError := fmt.Sprintf("adding space %q: space %q already exists", args.Name, args.Name)
   350  	c.Assert(err, gc.ErrorMatches, expectedError)
   351  	c.Assert(err, jc.Satisfies, errors.IsAlreadyExists)
   352  }
   353  
   354  func (s *SpacesSuite) TestAddTwoSpacesWithSameNamesAndProviderIdsFailsInTheSameModel(c *gc.C) {
   355  	args := addSpaceArgs{
   356  		Name:       "my-space",
   357  		ProviderId: network.Id("does not matter if not empty"),
   358  	}
   359  
   360  	_, err := s.addTwoSpacesReturnSecond(c, args, args)
   361  	s.assertSpaceAlreadyExistsErrorForArgs(c, err, args)
   362  }
   363  
   364  func (s *SpacesSuite) TestAddTwoSpacesWithSameNamesAndEmptyProviderIdsSuccedsInDifferentModels(c *gc.C) {
   365  	args1 := addSpaceArgs{
   366  		Name:       "my-space",
   367  		ProviderId: "",
   368  		ForState:   s.State,
   369  	}
   370  	args2 := args1
   371  	args2.ForState = s.NewStateForModelNamed(c, "other")
   372  
   373  	space2, err := s.addTwoSpacesReturnSecond(c, args1, args2)
   374  	c.Assert(err, jc.ErrorIsNil)
   375  	s.assertSpaceMatchesArgs(c, space2, args2)
   376  }
   377  
   378  func (s *SpacesSuite) TestAddTwoSpacesWithSameNamesAndProviderIdsSuccedsInDifferentModels(c *gc.C) {
   379  	args1 := addSpaceArgs{
   380  		Name:       "my-space",
   381  		ProviderId: network.Id("same provider id"),
   382  		ForState:   s.State,
   383  	}
   384  	args2 := args1
   385  	args2.ForState = s.NewStateForModelNamed(c, "other")
   386  
   387  	space2, err := s.addTwoSpacesReturnSecond(c, args1, args2)
   388  	c.Assert(err, jc.ErrorIsNil)
   389  	s.assertSpaceMatchesArgs(c, space2, args2)
   390  }
   391  
   392  func (s *SpacesSuite) TestAddSpaceWhenSubnetNotFound(c *gc.C) {
   393  	name := "my-space"
   394  	subnets := []string{"1.1.1.0/24"}
   395  	isPublic := false
   396  
   397  	_, err := s.State.AddSpace(name, "", subnets, isPublic)
   398  	c.Assert(err, gc.ErrorMatches, `adding space "my-space": subnet "1.1.1.0/24" not found`)
   399  	s.assertSpaceNotFound(c, name)
   400  }
   401  
   402  func (s *SpacesSuite) TestAddSpaceWithNonEmptyProviderIdAndInvalidNameFails(c *gc.C) {
   403  	args := addSpaceArgs{
   404  		Name:       "-bad name-",
   405  		ProviderId: network.Id("My Provider ID"),
   406  	}
   407  	_, err := s.addSpaceWithSubnets(c, args)
   408  	s.assertInvalidSpaceNameErrorAndWasNotAdded(c, err, args.Name)
   409  }
   410  
   411  func (s *SpacesSuite) assertInvalidSpaceNameErrorAndWasNotAdded(c *gc.C, err error, name string) {
   412  	expectedError := fmt.Sprintf("adding space %q: invalid space name", name)
   413  	c.Assert(err, gc.ErrorMatches, expectedError)
   414  
   415  	// The default space will be present, although we cannot add it.
   416  	// Only check non-default names.
   417  	if name != network.AlphaSpaceName {
   418  		s.assertSpaceNotFound(c, name)
   419  	}
   420  }
   421  
   422  func (s *SpacesSuite) TestAddSpaceWithEmptyProviderIdAndInvalidNameFails(c *gc.C) {
   423  	args := addSpaceArgs{
   424  		Name:       "-bad name-",
   425  		ProviderId: "",
   426  	}
   427  	_, err := s.addSpaceWithSubnets(c, args)
   428  	s.assertInvalidSpaceNameErrorAndWasNotAdded(c, err, args.Name)
   429  }
   430  
   431  func (s *SpacesSuite) TestAddSpaceWithEmptyNameAndProviderIdFails(c *gc.C) {
   432  	args := addSpaceArgs{
   433  		Name:       "",
   434  		ProviderId: "",
   435  	}
   436  	_, err := s.addSpaceWithSubnets(c, args)
   437  	s.assertInvalidSpaceNameErrorAndWasNotAdded(c, err, args.Name)
   438  }
   439  
   440  func (s *SpacesSuite) TestAddSpaceWithEmptyNameAndNonEmptyProviderIdFails(c *gc.C) {
   441  	args := addSpaceArgs{
   442  		Name:       "",
   443  		ProviderId: network.Id("doesn't matter"),
   444  	}
   445  	_, err := s.addSpaceWithSubnets(c, args)
   446  	s.assertInvalidSpaceNameErrorAndWasNotAdded(c, err, args.Name)
   447  }
   448  
   449  func (s *SpacesSuite) TestAllSpaces(c *gc.C) {
   450  	alphaSpace, err := s.State.SpaceByName(network.AlphaSpaceName)
   451  	c.Assert(err, jc.ErrorIsNil)
   452  
   453  	spaces, err := s.State.AllSpaces()
   454  	c.Assert(err, jc.ErrorIsNil)
   455  	c.Assert(spaces, jc.DeepEquals, []*state.Space{alphaSpace})
   456  
   457  	subnets := []string{"1.1.1.0/24", "2.1.1.0/24", "3.1.1.0/24"}
   458  	isPublic := false
   459  	subnetIDs := s.addSubnets(c, subnets)
   460  
   461  	first, err := s.State.AddSpace("first", "", []string{subnetIDs[0]}, isPublic)
   462  	c.Assert(err, jc.ErrorIsNil)
   463  	second, err := s.State.AddSpace("second", "", []string{subnetIDs[1]}, isPublic)
   464  	c.Assert(err, jc.ErrorIsNil)
   465  	third, err := s.State.AddSpace("third", "", []string{subnetIDs[2]}, isPublic)
   466  	c.Assert(err, jc.ErrorIsNil)
   467  
   468  	actual, err := s.State.AllSpaces()
   469  	c.Assert(err, jc.ErrorIsNil)
   470  	c.Assert(actual, jc.SameContents, []*state.Space{first, second, third, alphaSpace})
   471  }
   472  
   473  func (s *SpacesSuite) TestSpaceByID(c *gc.C) {
   474  	_, err := s.State.Space(network.AlphaSpaceId)
   475  	c.Assert(err, jc.ErrorIsNil)
   476  }
   477  
   478  func (s *SpacesSuite) TestSpaceByIDNotFound(c *gc.C) {
   479  	_, err := s.State.Space("42")
   480  	c.Assert(err, gc.ErrorMatches, "space id \"42\" not found")
   481  }
   482  
   483  func (s *SpacesSuite) TestEnsureDeadSetsLifeToDeadWhenAlive(c *gc.C) {
   484  	space := s.addAliveSpace(c, "alive")
   485  
   486  	s.ensureDeadAndAssertLifeIsDead(c, space)
   487  	s.refreshAndAssertSpaceLifeIs(c, space, state.Dead)
   488  }
   489  
   490  func (s *SpacesSuite) addAliveSpace(c *gc.C, name string) *state.Space {
   491  	space, err := s.State.AddSpace(name, "", nil, false)
   492  	c.Assert(err, jc.ErrorIsNil)
   493  	c.Assert(space.Life(), gc.Equals, state.Alive)
   494  	return space
   495  }
   496  
   497  func (s *SpacesSuite) ensureDeadAndAssertLifeIsDead(c *gc.C, space *state.Space) {
   498  	err := space.EnsureDead()
   499  	c.Assert(err, jc.ErrorIsNil)
   500  	c.Assert(space.Life(), gc.Equals, state.Dead)
   501  }
   502  
   503  func (s *SpacesSuite) refreshAndAssertSpaceLifeIs(c *gc.C, space *state.Space, expectedLife state.Life) {
   504  	err := space.Refresh()
   505  	c.Assert(err, jc.ErrorIsNil)
   506  	c.Assert(space.Life(), gc.Equals, expectedLife)
   507  }
   508  
   509  func (s *SpacesSuite) TestEnsureDeadSetsLifeToDeadWhenNotAlive(c *gc.C) {
   510  	space := s.addAliveSpace(c, "soon-dead")
   511  	s.ensureDeadAndAssertLifeIsDead(c, space)
   512  
   513  	s.ensureDeadAndAssertLifeIsDead(c, space)
   514  }
   515  
   516  func (s *SpacesSuite) TestRemoveFailsIfStillAlive(c *gc.C) {
   517  	space := s.addAliveSpace(c, "still-alive")
   518  
   519  	err := space.Remove()
   520  	c.Assert(err, gc.ErrorMatches, `cannot remove space "still-alive": space is not dead`)
   521  
   522  	s.refreshAndAssertSpaceLifeIs(c, space, state.Alive)
   523  }
   524  
   525  func (s *SpacesSuite) TestRemoveSucceedsWhenSpaceIsNotAlive(c *gc.C) {
   526  	space := s.addAliveSpace(c, "not-alive-soon")
   527  	s.ensureDeadAndAssertLifeIsDead(c, space)
   528  
   529  	s.removeSpaceAndAssertNotFound(c, space)
   530  }
   531  
   532  func (s *SpacesSuite) removeSpaceAndAssertNotFound(c *gc.C, space *state.Space) {
   533  	err := space.Remove()
   534  	c.Assert(err, jc.ErrorIsNil)
   535  	s.assertSpaceNotFound(c, space.Name())
   536  }
   537  
   538  func (s *SpacesSuite) TestRemoveSucceedsWhenCalledTwice(c *gc.C) {
   539  	space := s.addAliveSpace(c, "twice-deleted")
   540  	s.ensureDeadAndAssertLifeIsDead(c, space)
   541  	s.removeSpaceAndAssertNotFound(c, space)
   542  
   543  	err := space.Remove()
   544  	c.Assert(err, gc.ErrorMatches, `cannot remove space "twice-deleted": not found or not dead`)
   545  }
   546  
   547  func (s *SpacesSuite) TestRefreshUpdatesStaleDocData(c *gc.C) {
   548  	space := s.addAliveSpace(c, "original")
   549  	spaceCopy, err := s.State.SpaceByName(space.Name())
   550  	c.Assert(err, jc.ErrorIsNil)
   551  
   552  	s.ensureDeadAndAssertLifeIsDead(c, space)
   553  	c.Assert(spaceCopy.Life(), gc.Equals, state.Alive)
   554  
   555  	err = spaceCopy.Refresh()
   556  	c.Assert(err, jc.ErrorIsNil)
   557  	c.Assert(spaceCopy.Life(), gc.Equals, state.Dead)
   558  }
   559  
   560  func (s *SpacesSuite) TestRefreshFailsWithNotFoundWhenRemoved(c *gc.C) {
   561  	space := s.addAliveSpace(c, "soon-removed")
   562  	s.ensureDeadAndAssertLifeIsDead(c, space)
   563  	s.removeSpaceAndAssertNotFound(c, space)
   564  
   565  	err := space.Refresh()
   566  	s.assertSpaceNotFoundError(c, err, "soon-removed")
   567  }
   568  
   569  func (s *SpacesSuite) TestFanSubnetInheritsSpace(c *gc.C) {
   570  	args := addSpaceArgs{
   571  		Name:        "space1",
   572  		ProviderId:  network.Id("some id 2"),
   573  		SubnetCIDRs: []string{"1.1.1.0/24", "2001:cbd0::/32"},
   574  	}
   575  	space, err := s.addSpaceWithSubnets(c, args)
   576  	c.Assert(err, jc.ErrorIsNil)
   577  	s.assertSpaceMatchesArgs(c, space, args)
   578  	info := network.SubnetInfo{
   579  		CIDR:              "253.1.0.0/16",
   580  		VLANTag:           79,
   581  		AvailabilityZones: []string{"AvailabilityZone"},
   582  	}
   583  	info.SetFan("1.1.1.0/24", "253.0.0.0/8")
   584  	_, err = s.State.AddSubnet(info)
   585  	c.Assert(err, jc.ErrorIsNil)
   586  
   587  	err = space.Refresh()
   588  	c.Assert(err, jc.ErrorIsNil)
   589  	spaceInfo, err := space.NetworkSpace()
   590  	c.Assert(err, jc.ErrorIsNil)
   591  	subnets := spaceInfo.Subnets
   592  	var foundSubnet network.SubnetInfo
   593  	for _, subnet := range subnets {
   594  		if subnet.CIDR == "253.1.0.0/16" {
   595  			foundSubnet = subnet
   596  			break
   597  		}
   598  	}
   599  	c.Assert(foundSubnet, gc.NotNil)
   600  	c.Assert(foundSubnet.SpaceID, gc.Equals, space.Id())
   601  }
   602  
   603  func (s *SpacesSuite) TestSpaceToNetworkSpace(c *gc.C) {
   604  	args := addSpaceArgs{
   605  		Name:        "space1",
   606  		ProviderId:  network.Id("some id 2"),
   607  		SubnetCIDRs: []string{"1.1.1.0/24", "2001:cbd0::/32"},
   608  	}
   609  	space, err := s.addSpaceWithSubnets(c, args)
   610  	c.Assert(err, jc.ErrorIsNil)
   611  	s.assertSpaceMatchesArgs(c, space, args)
   612  	info := network.SubnetInfo{
   613  		CIDR:              "253.1.0.0/16",
   614  		VLANTag:           79,
   615  		AvailabilityZones: []string{"AvailabilityZone"},
   616  	}
   617  	info.SetFan("1.1.1.0/24", "253.0.0.0/8")
   618  	_, err = s.State.AddSubnet(info)
   619  	c.Assert(err, jc.ErrorIsNil)
   620  
   621  	err = space.Refresh()
   622  	c.Assert(err, jc.ErrorIsNil)
   623  
   624  	spaceInfo, err := space.NetworkSpace()
   625  	c.Assert(err, jc.ErrorIsNil)
   626  
   627  	expSpaceInfo := network.SpaceInfo{
   628  		Name:       "space1",
   629  		ID:         space.Id(),
   630  		ProviderId: args.ProviderId,
   631  		Subnets: []network.SubnetInfo{
   632  			{
   633  				ID:                "0",
   634  				SpaceID:           space.Id(),
   635  				SpaceName:         "space1",
   636  				CIDR:              "1.1.1.0/24",
   637  				VLANTag:           79,
   638  				AvailabilityZones: []string{"AvailabilityZone"},
   639  				ProviderSpaceId:   "some id 2",
   640  				Life:              life.Alive,
   641  			},
   642  			{
   643  				ID:                "2",
   644  				SpaceID:           space.Id(),
   645  				SpaceName:         "space1",
   646  				CIDR:              "253.1.0.0/16",
   647  				VLANTag:           79,
   648  				AvailabilityZones: []string{"AvailabilityZone"},
   649  				FanInfo: &network.FanCIDRs{
   650  					FanLocalUnderlay: "1.1.1.0/24",
   651  					FanOverlay:       "253.0.0.0/8",
   652  				},
   653  				ProviderSpaceId: "some id 2",
   654  				Life:            life.Alive,
   655  			},
   656  			{
   657  				ID:                "1",
   658  				SpaceID:           space.Id(),
   659  				SpaceName:         "space1",
   660  				CIDR:              "2001:cbd0::/32",
   661  				VLANTag:           79,
   662  				AvailabilityZones: []string{"AvailabilityZone"},
   663  				ProviderSpaceId:   "some id 2",
   664  				Life:              life.Alive,
   665  			},
   666  		},
   667  	}
   668  
   669  	// Sort subnets by CIDR for test consistency.
   670  	network.SortSubnetInfos(spaceInfo.Subnets)
   671  	network.SortSubnetInfos(expSpaceInfo.Subnets)
   672  
   673  	c.Assert(spaceInfo, gc.DeepEquals, expSpaceInfo)
   674  
   675  	// Test that AllSpaceInfos works the same way.
   676  	allSpaceInfos, err := s.State.AllSpaceInfos()
   677  	c.Assert(err, jc.ErrorIsNil)
   678  
   679  	space1 := allSpaceInfos.GetByName("space1")
   680  	c.Assert(space1, gc.NotNil)
   681  
   682  	network.SortSubnetInfos(space1.Subnets)
   683  	c.Assert(*space1, gc.DeepEquals, expSpaceInfo)
   684  }
   685  
   686  type SpacesDiscoverySuite struct {
   687  	ConnSuite
   688  }
   689  
   690  var _ = gc.Suite(&SpacesDiscoverySuite{})
   691  
   692  var twoSubnets = []network.SubnetInfo{
   693  	{
   694  		ProviderId:        "1",
   695  		AvailabilityZones: []string{"1", "2"},
   696  		CIDR:              "10.0.0.1/24",
   697  	},
   698  	{
   699  		ProviderId:        "2",
   700  		AvailabilityZones: []string{"3", "4"},
   701  		CIDR:              "10.100.30.1/24",
   702  	},
   703  }
   704  
   705  var twoSubnetsAndIgnored = []network.SubnetInfo{
   706  	{
   707  		ProviderId:        "1",
   708  		AvailabilityZones: []string{"1", "2"},
   709  		CIDR:              "10.0.0.1/24",
   710  	},
   711  	{
   712  		ProviderId:        "2",
   713  		AvailabilityZones: []string{"3", "4"},
   714  		CIDR:              "10.100.30.1/24",
   715  	},
   716  	// Interface-local multicast:
   717  	{
   718  		ProviderId:        "foo",
   719  		AvailabilityZones: []string{"bar", "baz"},
   720  		CIDR:              "ff51:dead:beef::/48",
   721  	},
   722  	// Link-local multicast:
   723  	{
   724  		ProviderId:        "moo",
   725  		AvailabilityZones: []string{"bar", "baz"},
   726  		CIDR:              "ff32:dead:beef::/48",
   727  	},
   728  	// IPv6 link-local unicast:
   729  	{
   730  		ProviderId:        "baa",
   731  		AvailabilityZones: []string{"bar", "baz"},
   732  		CIDR:              "fe80:dead:beef::/48",
   733  	},
   734  	// IPv4 link-local unicast:
   735  	{
   736  		ProviderId:        "maa",
   737  		AvailabilityZones: []string{"bar", "baz"},
   738  		CIDR:              "169.254.13.0/24",
   739  	},
   740  }
   741  
   742  var anotherTwoSubnets = []network.SubnetInfo{
   743  	{
   744  		ProviderId:        "3",
   745  		AvailabilityZones: []string{"5", "6"},
   746  		CIDR:              "10.101.0.1/24",
   747  	},
   748  	{
   749  		ProviderId:        "4",
   750  		AvailabilityZones: []string{"7", "8"},
   751  		CIDR:              "10.105.0.1/24",
   752  	},
   753  }
   754  
   755  var fourSubnets = []network.SubnetInfo{
   756  	{
   757  		ProviderId:        "1",
   758  		AvailabilityZones: []string{"1", "2"},
   759  		CIDR:              "10.0.0.1/24",
   760  	},
   761  	{
   762  		ProviderId:        "2",
   763  		AvailabilityZones: []string{"3", "4"},
   764  		CIDR:              "10.100.30.1/24",
   765  	},
   766  	{
   767  		ProviderId:        "3",
   768  		AvailabilityZones: []string{"5", "6"},
   769  		CIDR:              "10.101.0.1/24",
   770  	},
   771  	{
   772  		ProviderId:        "4",
   773  		AvailabilityZones: []string{"7", "8"},
   774  		CIDR:              "10.105.0.1/24",
   775  	},
   776  }
   777  
   778  var twoSubnetsAfterFAN = []network.SubnetInfo{
   779  	{
   780  		ProviderId:        "1",
   781  		AvailabilityZones: []string{"1", "2"},
   782  		CIDR:              "10.0.0.1/24",
   783  	},
   784  	{
   785  		ProviderId:        "2",
   786  		AvailabilityZones: []string{"3", "4"},
   787  		CIDR:              "10.100.30.1/24",
   788  	},
   789  	{
   790  		ProviderId:        network.Id(fmt.Sprintf("2-%s-10-100-30-0-24", network.InFan)),
   791  		AvailabilityZones: []string{"3", "4"},
   792  		CIDR:              "253.30.0.0/16",
   793  	},
   794  }
   795  
   796  func checkSubnetsEqual(c *gc.C, subnets []*state.Subnet, subnetInfos []network.SubnetInfo) {
   797  	c.Assert(len(subnetInfos), gc.Equals, len(subnets))
   798  	for i, subnetInfo := range subnetInfos {
   799  		subnet := subnets[i]
   800  		c.Check(subnetInfo.CIDR, gc.Equals, subnet.CIDR())
   801  		c.Check(subnetInfo.AvailabilityZones, gc.DeepEquals, subnet.AvailabilityZones())
   802  		c.Check(subnetInfo.ProviderId, gc.Equals, subnet.ProviderId())
   803  		c.Check(subnetInfo.ProviderNetworkId, gc.Equals, subnet.ProviderNetworkId())
   804  		c.Check(subnetInfo.VLANTag, gc.Equals, subnet.VLANTag())
   805  	}
   806  }
   807  
   808  func (s *SpacesDiscoverySuite) TestSaveProviderSubnets(c *gc.C) {
   809  	err := s.State.SaveProviderSubnets(twoSubnets, "")
   810  	c.Check(err, jc.ErrorIsNil)
   811  
   812  	subnets, err := s.State.AllSubnets()
   813  	c.Assert(err, jc.ErrorIsNil)
   814  	checkSubnetsEqual(c, subnets, twoSubnets)
   815  }
   816  
   817  // TODO(wpk) 2017-05-24 this test will have to be rewritten when we support removing spaces/subnets in discovery.
   818  func (s *SpacesDiscoverySuite) TestSaveProviderSubnetsOnlyAddsSubnets(c *gc.C) {
   819  	err := s.State.SaveProviderSubnets(twoSubnets, "")
   820  	c.Check(err, jc.ErrorIsNil)
   821  
   822  	err = s.State.SaveProviderSubnets(anotherTwoSubnets, "")
   823  	c.Check(err, jc.ErrorIsNil)
   824  
   825  	subnets, err := s.State.AllSubnets()
   826  	c.Assert(err, jc.ErrorIsNil)
   827  	checkSubnetsEqual(c, subnets, fourSubnets)
   828  }
   829  
   830  func (s *SpacesDiscoverySuite) TestSaveProviderSubnetsOnlyIdempotent(c *gc.C) {
   831  	err := s.State.SaveProviderSubnets(twoSubnets, "")
   832  	c.Check(err, jc.ErrorIsNil)
   833  
   834  	subnets1, err := s.State.AllSubnets()
   835  	c.Assert(err, jc.ErrorIsNil)
   836  
   837  	err = s.State.SaveProviderSubnets(twoSubnets, "")
   838  	c.Check(err, jc.ErrorIsNil)
   839  
   840  	subnets2, err := s.State.AllSubnets()
   841  	c.Assert(err, jc.ErrorIsNil)
   842  	c.Check(subnets1, jc.DeepEquals, subnets2)
   843  }
   844  
   845  func (s *SpacesDiscoverySuite) TestSaveProviderSubnetsWithFAN(c *gc.C) {
   846  	err := s.Model.UpdateModelConfig(map[string]interface{}{"fan-config": "10.100.0.0/16=253.0.0.0/8"}, nil)
   847  	c.Assert(err, jc.ErrorIsNil)
   848  
   849  	err = s.State.SaveProviderSubnets(twoSubnets, "")
   850  	c.Check(err, jc.ErrorIsNil)
   851  
   852  	subnets, err := s.State.AllSubnets()
   853  	c.Assert(err, jc.ErrorIsNil)
   854  
   855  	checkSubnetsEqual(c, subnets, twoSubnetsAfterFAN)
   856  }
   857  
   858  func (s *SpacesDiscoverySuite) TestSaveProviderSubnetsIgnoredWithFAN(c *gc.C) {
   859  	// This is just a test configuration. This configuration may be
   860  	// considered invalid in the future. Here we show that this
   861  	// configuration is ignored.
   862  	err := s.Model.UpdateModelConfig(
   863  		map[string]interface{}{"fan-config": "fe80:dead:beef::/48=fe80:dead:beef::/24"}, nil)
   864  	c.Assert(err, jc.ErrorIsNil)
   865  
   866  	err = s.State.SaveProviderSubnets(twoSubnetsAndIgnored, "")
   867  	c.Check(err, jc.ErrorIsNil)
   868  
   869  	subnets, err := s.State.AllSubnets()
   870  	c.Assert(err, jc.ErrorIsNil)
   871  
   872  	checkSubnetsEqual(c, subnets, twoSubnets)
   873  }
   874  
   875  func (s *SpacesDiscoverySuite) TestSaveProviderSubnetsIgnored(c *gc.C) {
   876  	err := s.State.SaveProviderSubnets(twoSubnetsAndIgnored, "")
   877  	c.Check(err, jc.ErrorIsNil)
   878  
   879  	subnets, err := s.State.AllSubnets()
   880  	c.Assert(err, jc.ErrorIsNil)
   881  
   882  	checkSubnetsEqual(c, subnets, twoSubnets)
   883  }