github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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  	"strings"
    10  
    11  	"github.com/juju/errors"
    12  	jc "github.com/juju/testing/checkers"
    13  	gc "gopkg.in/check.v1"
    14  
    15  	"github.com/juju/juju/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) {
    26  	s.addSubnetsForState(c, CIDRs, s.State)
    27  }
    28  
    29  func (s *SpacesSuite) addSubnetsForState(c *gc.C, CIDRs []string, st *state.State) {
    30  	if len(CIDRs) == 0 {
    31  		return
    32  	}
    33  	for _, info := range s.makeSubnetInfosForCIDRs(c, CIDRs) {
    34  		_, err := st.AddSubnet(info)
    35  		c.Assert(err, jc.ErrorIsNil)
    36  	}
    37  }
    38  
    39  func (s *SpacesSuite) makeSubnetInfosForCIDRs(c *gc.C, CIDRs []string) []state.SubnetInfo {
    40  	infos := make([]state.SubnetInfo, len(CIDRs))
    41  	for i, cidr := range CIDRs {
    42  		ip, ipNet, err := net.ParseCIDR(cidr)
    43  		c.Assert(err, jc.ErrorIsNil)
    44  
    45  		// Generate the high IP address from the CIDR
    46  		// First create a copy of the low IP address
    47  		highIp := ip
    48  
    49  		// By default we always get 16 bytes for each IP address. We want to
    50  		// reduce this to 4 if we were provided an IPv4 address.
    51  		if ip.To4() != nil {
    52  			highIp = ip.To4()
    53  			ip = ip.To4()
    54  		}
    55  
    56  		// To generate a high IP address we bitwise not each byte of the subnet
    57  		// mask and OR it to the low IP address.
    58  		for j, b := range ipNet.Mask {
    59  			if j < len(ip) {
    60  				highIp[j] |= ^b
    61  			}
    62  		}
    63  
    64  		infos[i] = state.SubnetInfo{
    65  			CIDR:              cidr,
    66  			VLANTag:           79,
    67  			AllocatableIPLow:  ip.String(),
    68  			AllocatableIPHigh: highIp.String(),
    69  			AvailabilityZone:  "AvailabilityZone",
    70  		}
    71  
    72  	}
    73  	return infos
    74  }
    75  
    76  type addSpaceArgs struct {
    77  	Name        string
    78  	ProviderId  network.Id
    79  	SubnetCIDRs []string
    80  	IsPublic    bool
    81  	ForState    *state.State
    82  }
    83  
    84  func (s *SpacesSuite) addSpaceWithSubnets(c *gc.C, args addSpaceArgs) (*state.Space, error) {
    85  	if args.ForState == nil {
    86  		args.ForState = s.State
    87  	}
    88  	s.addSubnetsForState(c, args.SubnetCIDRs, args.ForState)
    89  	return args.ForState.AddSpace(args.Name, args.ProviderId, args.SubnetCIDRs, args.IsPublic)
    90  }
    91  
    92  func (s *SpacesSuite) assertSpaceNotFound(c *gc.C, name string) {
    93  	s.assertSpaceNotFoundForState(c, name, s.State)
    94  }
    95  
    96  func (s *SpacesSuite) assertSpaceNotFoundForState(c *gc.C, name string, st *state.State) {
    97  	_, err := st.Space(name)
    98  	s.assertSpaceNotFoundError(c, err, name)
    99  }
   100  
   101  func (s *SpacesSuite) assertSpaceNotFoundError(c *gc.C, err error, name string) {
   102  	c.Assert(err, gc.ErrorMatches, fmt.Sprintf("space %q not found", name))
   103  
   104  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   105  }
   106  
   107  func (s *SpacesSuite) assertSpaceMatchesArgs(c *gc.C, space *state.Space, args addSpaceArgs) {
   108  	c.Assert(space.Name(), gc.Equals, args.Name)
   109  	c.Assert(space.ProviderId(), gc.Equals, args.ProviderId)
   110  
   111  	actualSubnets, err := space.Subnets()
   112  	c.Assert(err, jc.ErrorIsNil)
   113  	actualSubnetIds := make([]string, len(actualSubnets))
   114  	for i, subnet := range actualSubnets {
   115  		actualSubnetIds[i] = subnet.CIDR()
   116  	}
   117  	c.Assert(actualSubnetIds, jc.SameContents, args.SubnetCIDRs)
   118  	c.Assert(state.SpaceDoc(space).IsPublic, gc.Equals, args.IsPublic)
   119  
   120  	c.Assert(space.Life(), gc.Equals, state.Alive)
   121  	c.Assert(strings.HasSuffix(space.ID(), args.Name), jc.IsTrue)
   122  	c.Assert(space.String(), gc.Equals, args.Name)
   123  }
   124  
   125  func (s *SpacesSuite) TestAddSpaceWithNoSubnetsAndEmptyProviderId(c *gc.C) {
   126  	args := addSpaceArgs{
   127  		Name:        "my-space",
   128  		ProviderId:  "",
   129  		SubnetCIDRs: nil,
   130  	}
   131  	space, err := s.addSpaceWithSubnets(c, args)
   132  	c.Assert(err, jc.ErrorIsNil)
   133  	s.assertSpaceMatchesArgs(c, space, args)
   134  
   135  }
   136  
   137  func (s *SpacesSuite) TestAddSpaceWithNoSubnetsAndNonEmptyProviderId(c *gc.C) {
   138  	args := addSpaceArgs{
   139  		Name:        "my-space",
   140  		ProviderId:  network.Id("my provider id"),
   141  		SubnetCIDRs: nil,
   142  	}
   143  	space, err := s.addSpaceWithSubnets(c, args)
   144  	c.Assert(err, jc.ErrorIsNil)
   145  	s.assertSpaceMatchesArgs(c, space, args)
   146  }
   147  
   148  func (s *SpacesSuite) TestAddSpaceWithOneIPv4SubnetAndEmptyProviderId(c *gc.C) {
   149  	args := addSpaceArgs{
   150  		Name:        "my-space",
   151  		ProviderId:  "",
   152  		SubnetCIDRs: []string{"1.1.1.0/24"},
   153  	}
   154  	space, err := s.addSpaceWithSubnets(c, args)
   155  	c.Assert(err, jc.ErrorIsNil)
   156  	s.assertSpaceMatchesArgs(c, space, args)
   157  }
   158  
   159  func (s *SpacesSuite) TestAddSpaceWithOneIPv4SubnetAndNonEmptyProviderId(c *gc.C) {
   160  	args := addSpaceArgs{
   161  		Name:        "my-space",
   162  		ProviderId:  network.Id("some id"),
   163  		SubnetCIDRs: []string{"1.1.1.0/24"},
   164  	}
   165  	space, err := s.addSpaceWithSubnets(c, args)
   166  	c.Assert(err, jc.ErrorIsNil)
   167  	s.assertSpaceMatchesArgs(c, space, args)
   168  }
   169  
   170  func (s *SpacesSuite) TestAddSpaceWithOneIPv6SubnetAndEmptyProviderId(c *gc.C) {
   171  	args := addSpaceArgs{
   172  		Name:        "my-space",
   173  		ProviderId:  "",
   174  		SubnetCIDRs: []string{"fc00:123::/64"},
   175  	}
   176  	space, err := s.addSpaceWithSubnets(c, args)
   177  	c.Assert(err, jc.ErrorIsNil)
   178  	s.assertSpaceMatchesArgs(c, space, args)
   179  }
   180  
   181  func (s *SpacesSuite) TestAddSpaceWithOneIPv6SubnetAndNonEmptyProviderId(c *gc.C) {
   182  	args := addSpaceArgs{
   183  		Name:        "my-space",
   184  		ProviderId:  network.Id("provider id"),
   185  		SubnetCIDRs: []string{"fc00:123::/64"},
   186  	}
   187  	space, err := s.addSpaceWithSubnets(c, args)
   188  	c.Assert(err, jc.ErrorIsNil)
   189  	s.assertSpaceMatchesArgs(c, space, args)
   190  }
   191  
   192  func (s *SpacesSuite) TestAddSpaceWithOneIPv4AndOneIPv6SubnetAndEmptyProviderId(c *gc.C) {
   193  	args := addSpaceArgs{
   194  		Name:        "my-space",
   195  		ProviderId:  "",
   196  		SubnetCIDRs: []string{"1.1.1.0/24", "fc00::123/64"},
   197  	}
   198  	space, err := s.addSpaceWithSubnets(c, args)
   199  	c.Assert(err, jc.ErrorIsNil)
   200  	s.assertSpaceMatchesArgs(c, space, args)
   201  }
   202  
   203  func (s *SpacesSuite) TestAddSpaceWithOneIPv4AndOneIPv6SubnetAndNonEmptyProviderId(c *gc.C) {
   204  	args := addSpaceArgs{
   205  		Name:        "my-space",
   206  		ProviderId:  network.Id("foo bar"),
   207  		SubnetCIDRs: []string{"1.1.1.0/24", "fc00::123/64"},
   208  	}
   209  	space, err := s.addSpaceWithSubnets(c, args)
   210  	c.Assert(err, jc.ErrorIsNil)
   211  	s.assertSpaceMatchesArgs(c, space, args)
   212  }
   213  
   214  func (s *SpacesSuite) TestAddSpaceWithMultipleIPv4SubnetsAndEmptyProviderId(c *gc.C) {
   215  	args := addSpaceArgs{
   216  		Name:        "my-space",
   217  		ProviderId:  "",
   218  		SubnetCIDRs: []string{"1.1.1.0/24", "1.2.2.0/24"},
   219  	}
   220  	space, err := s.addSpaceWithSubnets(c, args)
   221  	c.Assert(err, jc.ErrorIsNil)
   222  	s.assertSpaceMatchesArgs(c, space, args)
   223  }
   224  
   225  func (s *SpacesSuite) TestAddSpaceWithMultipleIPv4SubnetsAndNonEmptyProviderId(c *gc.C) {
   226  	args := addSpaceArgs{
   227  		Name:        "my-space",
   228  		ProviderId:  network.Id("My Provider ID"),
   229  		SubnetCIDRs: []string{"1.1.1.0/24", "1.2.2.0/24"},
   230  	}
   231  	space, err := s.addSpaceWithSubnets(c, args)
   232  	c.Assert(err, jc.ErrorIsNil)
   233  	s.assertSpaceMatchesArgs(c, space, args)
   234  }
   235  
   236  func (s *SpacesSuite) TestAddSpaceWithMultipleIPv6SubnetsAndEmptyProviderId(c *gc.C) {
   237  	args := addSpaceArgs{
   238  		Name:        "my-space",
   239  		ProviderId:  "",
   240  		SubnetCIDRs: []string{"fc00:123::/64", "fc00:321::/64"},
   241  	}
   242  	space, err := s.addSpaceWithSubnets(c, args)
   243  	c.Assert(err, jc.ErrorIsNil)
   244  	s.assertSpaceMatchesArgs(c, space, args)
   245  }
   246  
   247  func (s *SpacesSuite) TestAddSpaceWithMultipleIPv6SubnetsAndNonEmptyProviderId(c *gc.C) {
   248  	args := addSpaceArgs{
   249  		Name:        "my-space",
   250  		ProviderId:  network.Id("My Provider ID"),
   251  		SubnetCIDRs: []string{"fc00:123::/64", "fc00:321::/64"},
   252  	}
   253  	space, err := s.addSpaceWithSubnets(c, args)
   254  
   255  	c.Assert(err, jc.ErrorIsNil)
   256  	s.assertSpaceMatchesArgs(c, space, args)
   257  }
   258  
   259  func (s *SpacesSuite) TestAddSpaceWithMultipleIPv4AndIPv6SubnetsAndEmptyProviderId(c *gc.C) {
   260  	args := addSpaceArgs{
   261  		Name:        "my-space",
   262  		ProviderId:  "",
   263  		SubnetCIDRs: []string{"fc00:123::/64", "2.2.2.0/20", "fc00:321::/64", "1.1.1.0/24"},
   264  	}
   265  	space, err := s.addSpaceWithSubnets(c, args)
   266  
   267  	c.Assert(err, jc.ErrorIsNil)
   268  	s.assertSpaceMatchesArgs(c, space, args)
   269  
   270  }
   271  
   272  func (s *SpacesSuite) TestAddSpaceWithMultipleIPv4AndIPv6SubnetsAndNonEmptyProviderId(c *gc.C) {
   273  	args := addSpaceArgs{
   274  		Name:        "my-space",
   275  		ProviderId:  network.Id("My Provider ID"),
   276  		SubnetCIDRs: []string{"fc00:123::/64", "2.2.2.0/20", "fc00:321::/64", "1.1.1.0/24"},
   277  	}
   278  	space, err := s.addSpaceWithSubnets(c, args)
   279  	c.Assert(err, jc.ErrorIsNil)
   280  	s.assertSpaceMatchesArgs(c, space, args)
   281  }
   282  
   283  func (s *SpacesSuite) addTwoSpacesReturnSecond(c *gc.C, args1, args2 addSpaceArgs) (*state.Space, error) {
   284  	space1, err := s.addSpaceWithSubnets(c, args1)
   285  
   286  	c.Assert(err, jc.ErrorIsNil)
   287  	s.assertSpaceMatchesArgs(c, space1, args1)
   288  
   289  	return s.addSpaceWithSubnets(c, args2)
   290  }
   291  
   292  func (s *SpacesSuite) TestAddTwoSpacesWithDifferentNamesButSameProviderIdFailsInSameModel(c *gc.C) {
   293  	args1 := addSpaceArgs{
   294  		Name:       "my-space",
   295  		ProviderId: network.Id("provider id"),
   296  	}
   297  	args2 := args1
   298  	args2.Name = "different"
   299  
   300  	_, err := s.addTwoSpacesReturnSecond(c, args1, args2)
   301  	s.assertProviderIdNotUniqueErrorForArgs(c, err, args2)
   302  }
   303  
   304  func (s *SpacesSuite) assertProviderIdNotUniqueErrorForArgs(c *gc.C, err error, args addSpaceArgs) {
   305  	expectedError := fmt.Sprintf("adding space %q: ProviderId %q not unique", args.Name, args.ProviderId)
   306  	c.Assert(err, gc.ErrorMatches, expectedError)
   307  }
   308  
   309  func (s *SpacesSuite) TestAddTwoSpacesWithDifferentNamesButSameProviderIdSucceedsInDifferentModels(c *gc.C) {
   310  	args1 := addSpaceArgs{
   311  		Name:       "my-space",
   312  		ProviderId: network.Id("provider id"),
   313  		ForState:   s.State,
   314  	}
   315  	args2 := args1
   316  	args2.Name = "different"
   317  	args2.ForState = s.NewStateForModelNamed(c, "other")
   318  
   319  	space2, err := s.addTwoSpacesReturnSecond(c, args1, args2)
   320  
   321  	c.Assert(err, jc.ErrorIsNil)
   322  	s.assertSpaceMatchesArgs(c, space2, args2)
   323  }
   324  
   325  func (s *SpacesSuite) TestAddTwoSpacesWithDifferentNamesAndEmptyProviderIdSucceedsInSameModel(c *gc.C) {
   326  	args1 := addSpaceArgs{
   327  		Name:       "my-space",
   328  		ProviderId: "",
   329  	}
   330  	args2 := args1
   331  	args2.Name = "different"
   332  
   333  	space2, err := s.addTwoSpacesReturnSecond(c, args1, args2)
   334  	c.Assert(err, jc.ErrorIsNil)
   335  	s.assertSpaceMatchesArgs(c, space2, args2)
   336  }
   337  
   338  func (s *SpacesSuite) TestAddTwoSpacesWithDifferentNamesAndEmptyProviderIdSucceedsInDifferentModels(c *gc.C) {
   339  	args1 := addSpaceArgs{
   340  		Name:       "my-space",
   341  		ProviderId: "",
   342  		ForState:   s.State,
   343  	}
   344  	args2 := args1
   345  	args2.Name = "different"
   346  	args2.ForState = s.NewStateForModelNamed(c, "other")
   347  
   348  	space2, err := s.addTwoSpacesReturnSecond(c, args1, args2)
   349  
   350  	c.Assert(err, jc.ErrorIsNil)
   351  	s.assertSpaceMatchesArgs(c, space2, args2)
   352  }
   353  
   354  func (s *SpacesSuite) TestAddTwoSpacesWithSameNamesAndEmptyProviderIdsFailsInSameModel(c *gc.C) {
   355  	args := addSpaceArgs{
   356  		Name:       "my-space",
   357  		ProviderId: "",
   358  	}
   359  
   360  	_, err := s.addTwoSpacesReturnSecond(c, args, args)
   361  	s.assertSpaceAlreadyExistsErrorForArgs(c, err, args)
   362  }
   363  
   364  func (s *SpacesSuite) assertSpaceAlreadyExistsErrorForArgs(c *gc.C, err error, args addSpaceArgs) {
   365  	expectedError := fmt.Sprintf("adding space %q: space %q already exists", args.Name, args.Name)
   366  	c.Assert(err, gc.ErrorMatches, expectedError)
   367  	c.Assert(err, jc.Satisfies, errors.IsAlreadyExists)
   368  }
   369  
   370  func (s *SpacesSuite) TestAddTwoSpacesWithSameNamesAndProviderIdsFailsInTheSameModel(c *gc.C) {
   371  	args := addSpaceArgs{
   372  		Name:       "my-space",
   373  		ProviderId: network.Id("does not matter if not empty"),
   374  	}
   375  
   376  	_, err := s.addTwoSpacesReturnSecond(c, args, args)
   377  	s.assertSpaceAlreadyExistsErrorForArgs(c, err, args)
   378  }
   379  
   380  func (s *SpacesSuite) TestAddTwoSpacesWithSameNamesAndEmptyProviderIdsSuccedsInDifferentModels(c *gc.C) {
   381  	args1 := addSpaceArgs{
   382  		Name:       "my-space",
   383  		ProviderId: "",
   384  		ForState:   s.State,
   385  	}
   386  	args2 := args1
   387  	args2.ForState = s.NewStateForModelNamed(c, "other")
   388  
   389  	space2, err := s.addTwoSpacesReturnSecond(c, args1, args2)
   390  	c.Assert(err, jc.ErrorIsNil)
   391  	s.assertSpaceMatchesArgs(c, space2, args2)
   392  }
   393  
   394  func (s *SpacesSuite) TestAddTwoSpacesWithSameNamesAndProviderIdsSuccedsInDifferentModels(c *gc.C) {
   395  	args1 := addSpaceArgs{
   396  		Name:       "my-space",
   397  		ProviderId: network.Id("same provider id"),
   398  		ForState:   s.State,
   399  	}
   400  	args2 := args1
   401  	args2.ForState = s.NewStateForModelNamed(c, "other")
   402  
   403  	space2, err := s.addTwoSpacesReturnSecond(c, args1, args2)
   404  	c.Assert(err, jc.ErrorIsNil)
   405  	s.assertSpaceMatchesArgs(c, space2, args2)
   406  }
   407  
   408  func (s *SpacesSuite) TestAddSpaceWhenSubnetNotFound(c *gc.C) {
   409  	name := "my-space"
   410  	subnets := []string{"1.1.1.0/24"}
   411  	isPublic := false
   412  
   413  	_, err := s.State.AddSpace(name, "", subnets, isPublic)
   414  	c.Assert(err, gc.ErrorMatches, `adding space "my-space": subnet "1.1.1.0/24" not found`)
   415  	s.assertSpaceNotFound(c, name)
   416  }
   417  
   418  func (s *SpacesSuite) TestAddSpaceWithNonEmptyProviderIdAndInvalidNameFails(c *gc.C) {
   419  	args := addSpaceArgs{
   420  		Name:       "-bad name-",
   421  		ProviderId: network.Id("My Provider ID"),
   422  	}
   423  	_, err := s.addSpaceWithSubnets(c, args)
   424  	s.assertInvalidSpaceNameErrorAndWasNotAdded(c, err, args.Name)
   425  }
   426  
   427  func (s *SpacesSuite) assertInvalidSpaceNameErrorAndWasNotAdded(c *gc.C, err error, name string) {
   428  	expectedError := fmt.Sprintf("adding space %q: invalid space name", name)
   429  	c.Assert(err, gc.ErrorMatches, expectedError)
   430  	s.assertSpaceNotFound(c, name)
   431  }
   432  
   433  func (s *SpacesSuite) TestAddSpaceWithEmptyProviderIdAndInvalidNameFails(c *gc.C) {
   434  	args := addSpaceArgs{
   435  		Name:       "-bad name-",
   436  		ProviderId: "",
   437  	}
   438  	_, err := s.addSpaceWithSubnets(c, args)
   439  	s.assertInvalidSpaceNameErrorAndWasNotAdded(c, err, args.Name)
   440  }
   441  
   442  func (s *SpacesSuite) TestAddSpaceWithEmptyNameAndProviderIdFails(c *gc.C) {
   443  	args := addSpaceArgs{
   444  		Name:       "",
   445  		ProviderId: "",
   446  	}
   447  	_, err := s.addSpaceWithSubnets(c, args)
   448  	s.assertInvalidSpaceNameErrorAndWasNotAdded(c, err, args.Name)
   449  }
   450  
   451  func (s *SpacesSuite) TestAddSpaceWithEmptyNameAndNonEmptyProviderIdFails(c *gc.C) {
   452  	args := addSpaceArgs{
   453  		Name:       "",
   454  		ProviderId: network.Id("doesn't matter"),
   455  	}
   456  	_, err := s.addSpaceWithSubnets(c, args)
   457  	s.assertInvalidSpaceNameErrorAndWasNotAdded(c, err, args.Name)
   458  }
   459  
   460  func (s *SpacesSuite) TestSubnetsReturnsExpectedSubnets(c *gc.C) {
   461  	args := addSpaceArgs{
   462  		Name:        "my-space",
   463  		SubnetCIDRs: []string{"1.1.1.0/24", "2.1.1.0/24", "3.1.1.0/24", "4.1.1.0/24", "5.1.1.0/24"},
   464  	}
   465  	space, err := s.addSpaceWithSubnets(c, args)
   466  	c.Assert(err, jc.ErrorIsNil)
   467  
   468  	expected := []*state.Subnet{}
   469  	for _, cidr := range args.SubnetCIDRs {
   470  		subnet, err := s.State.Subnet(cidr)
   471  		c.Assert(err, jc.ErrorIsNil)
   472  		expected = append(expected, subnet)
   473  	}
   474  	actual, err := space.Subnets()
   475  	c.Assert(err, jc.ErrorIsNil)
   476  	c.Assert(actual, jc.DeepEquals, expected)
   477  }
   478  
   479  func (s *SpacesSuite) TestAllSpaces(c *gc.C) {
   480  	spaces, err := s.State.AllSpaces()
   481  	c.Assert(err, jc.ErrorIsNil)
   482  	c.Assert(spaces, jc.DeepEquals, []*state.Space{})
   483  
   484  	subnets := []string{"1.1.1.0/24", "2.1.1.0/24", "3.1.1.0/24"}
   485  	isPublic := false
   486  	s.addSubnets(c, subnets)
   487  
   488  	first, err := s.State.AddSpace("first", "", []string{"1.1.1.0/24"}, isPublic)
   489  	c.Assert(err, jc.ErrorIsNil)
   490  	second, err := s.State.AddSpace("second", "", []string{"2.1.1.0/24"}, isPublic)
   491  	c.Assert(err, jc.ErrorIsNil)
   492  	third, err := s.State.AddSpace("third", "", []string{"3.1.1.0/24"}, isPublic)
   493  	c.Assert(err, jc.ErrorIsNil)
   494  
   495  	actual, err := s.State.AllSpaces()
   496  	c.Assert(err, jc.ErrorIsNil)
   497  	c.Assert(actual, jc.SameContents, []*state.Space{first, second, third})
   498  }
   499  
   500  func (s *SpacesSuite) TestEnsureDeadSetsLifeToDeadWhenAlive(c *gc.C) {
   501  	space := s.addAliveSpace(c, "alive")
   502  
   503  	s.ensureDeadAndAssertLifeIsDead(c, space)
   504  	s.refreshAndAssertSpaceLifeIs(c, space, state.Dead)
   505  }
   506  
   507  func (s *SpacesSuite) addAliveSpace(c *gc.C, name string) *state.Space {
   508  	space, err := s.State.AddSpace(name, "", nil, false)
   509  	c.Assert(err, jc.ErrorIsNil)
   510  	c.Assert(space.Life(), gc.Equals, state.Alive)
   511  	return space
   512  }
   513  
   514  func (s *SpacesSuite) ensureDeadAndAssertLifeIsDead(c *gc.C, space *state.Space) {
   515  	err := space.EnsureDead()
   516  	c.Assert(err, jc.ErrorIsNil)
   517  	c.Assert(space.Life(), gc.Equals, state.Dead)
   518  }
   519  
   520  func (s *SpacesSuite) refreshAndAssertSpaceLifeIs(c *gc.C, space *state.Space, expectedLife state.Life) {
   521  	err := space.Refresh()
   522  	c.Assert(err, jc.ErrorIsNil)
   523  	c.Assert(space.Life(), gc.Equals, expectedLife)
   524  }
   525  
   526  func (s *SpacesSuite) TestEnsureDeadSetsLifeToDeadWhenNotAlive(c *gc.C) {
   527  	space := s.addAliveSpace(c, "soon-dead")
   528  	s.ensureDeadAndAssertLifeIsDead(c, space)
   529  
   530  	s.ensureDeadAndAssertLifeIsDead(c, space)
   531  }
   532  
   533  func (s *SpacesSuite) TestRemoveFailsIfStillAlive(c *gc.C) {
   534  	space := s.addAliveSpace(c, "still-alive")
   535  
   536  	err := space.Remove()
   537  	c.Assert(err, gc.ErrorMatches, `cannot remove space "still-alive": space is not dead`)
   538  
   539  	s.refreshAndAssertSpaceLifeIs(c, space, state.Alive)
   540  }
   541  
   542  func (s *SpacesSuite) TestRemoveSucceedsWhenSpaceIsNotAlive(c *gc.C) {
   543  	space := s.addAliveSpace(c, "not-alive-soon")
   544  	s.ensureDeadAndAssertLifeIsDead(c, space)
   545  
   546  	s.removeSpaceAndAssertNotFound(c, space)
   547  }
   548  
   549  func (s *SpacesSuite) removeSpaceAndAssertNotFound(c *gc.C, space *state.Space) {
   550  	err := space.Remove()
   551  	c.Assert(err, jc.ErrorIsNil)
   552  	s.assertSpaceNotFound(c, space.Name())
   553  }
   554  
   555  func (s *SpacesSuite) TestRemoveSucceedsWhenCalledTwice(c *gc.C) {
   556  	space := s.addAliveSpace(c, "twice-deleted")
   557  	s.ensureDeadAndAssertLifeIsDead(c, space)
   558  	s.removeSpaceAndAssertNotFound(c, space)
   559  
   560  	err := space.Remove()
   561  	c.Assert(err, gc.ErrorMatches, `cannot remove space "twice-deleted": not found or not dead`)
   562  }
   563  
   564  func (s *SpacesSuite) TestRefreshUpdatesStaleDocData(c *gc.C) {
   565  	space := s.addAliveSpace(c, "original")
   566  	spaceCopy, err := s.State.Space(space.Name())
   567  	c.Assert(err, jc.ErrorIsNil)
   568  
   569  	s.ensureDeadAndAssertLifeIsDead(c, space)
   570  	c.Assert(spaceCopy.Life(), gc.Equals, state.Alive)
   571  
   572  	err = spaceCopy.Refresh()
   573  	c.Assert(err, jc.ErrorIsNil)
   574  	c.Assert(spaceCopy.Life(), gc.Equals, state.Dead)
   575  }
   576  
   577  func (s *SpacesSuite) TestRefreshFailsWithNotFoundWhenRemoved(c *gc.C) {
   578  	space := s.addAliveSpace(c, "soon-removed")
   579  	s.ensureDeadAndAssertLifeIsDead(c, space)
   580  	s.removeSpaceAndAssertNotFound(c, space)
   581  
   582  	err := space.Refresh()
   583  	s.assertSpaceNotFoundError(c, err, "soon-removed")
   584  }