github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/api/client/spaces/spaces_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package spaces_test
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"math/rand"
    10  
    11  	"github.com/juju/names/v5"
    12  	jc "github.com/juju/testing/checkers"
    13  	"go.uber.org/mock/gomock"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/api/base/mocks"
    17  	"github.com/juju/juju/api/client/spaces"
    18  	"github.com/juju/juju/rpc/params"
    19  )
    20  
    21  // spacesSuite are using mocks instead of the apicaller stubs
    22  type spacesSuite struct {
    23  	fCaller *mocks.MockFacadeCaller
    24  	API     *spaces.API
    25  }
    26  
    27  var _ = gc.Suite(&spacesSuite{})
    28  
    29  func (s *spacesSuite) SetUpTest(c *gc.C) {
    30  }
    31  
    32  func (s *spacesSuite) TearDownTest(c *gc.C) {
    33  	s.fCaller = nil
    34  }
    35  
    36  func (s *spacesSuite) setUpMocks(c *gc.C) *gomock.Controller {
    37  	ctrl := gomock.NewController(c)
    38  
    39  	caller := mocks.NewMockAPICallCloser(ctrl)
    40  
    41  	s.fCaller = mocks.NewMockFacadeCaller(ctrl)
    42  	s.fCaller.EXPECT().RawAPICaller().Return(caller).AnyTimes()
    43  	s.API = spaces.NewAPIFromCaller(s.fCaller)
    44  	return ctrl
    45  }
    46  
    47  func (s *spacesSuite) TestRemoveSpace(c *gc.C) {
    48  	defer s.setUpMocks(c).Finish()
    49  	name := "myspace"
    50  	resultSource := params.RemoveSpaceResults{
    51  		Results: []params.RemoveSpaceResult{},
    52  	}
    53  	args := getRemoveSpaceArgs(name, false, false)
    54  
    55  	s.fCaller.EXPECT().FacadeCall("RemoveSpace", args, gomock.Any()).SetArg(2, resultSource).Return(nil)
    56  
    57  	bounds, err := s.API.RemoveSpace(name, false, false)
    58  	c.Assert(err, gc.ErrorMatches, "0 results, expected 1")
    59  	c.Assert(bounds, gc.DeepEquals, params.RemoveSpaceResult{})
    60  }
    61  
    62  func (s *spacesSuite) TestRemoveSpaceUnexpectedError(c *gc.C) {
    63  	defer s.setUpMocks(c).Finish()
    64  	name := "myspace"
    65  	resultSource := params.RemoveSpaceResults{
    66  		Results: []params.RemoveSpaceResult{{
    67  			Constraints:        nil,
    68  			Bindings:           nil,
    69  			ControllerSettings: nil,
    70  			Error: &params.Error{
    71  				Message: "bam",
    72  				Code:    "500",
    73  			},
    74  		}},
    75  	}
    76  	args := getRemoveSpaceArgs(name, false, false)
    77  
    78  	s.fCaller.EXPECT().FacadeCall("RemoveSpace", args, gomock.Any()).SetArg(2, resultSource).Return(nil)
    79  
    80  	bounds, err := s.API.RemoveSpace(name, false, false)
    81  	c.Assert(err, gc.ErrorMatches, "bam")
    82  	c.Assert(bounds, gc.DeepEquals, params.RemoveSpaceResult{})
    83  }
    84  
    85  func (s *spacesSuite) TestRemoveSpaceUnexpectedErrorAPICall(c *gc.C) {
    86  	defer s.setUpMocks(c).Finish()
    87  	name := "myspace"
    88  	resultSource := params.RemoveSpaceResults{
    89  		Results: []params.RemoveSpaceResult{}}
    90  	args := getRemoveSpaceArgs(name, false, false)
    91  
    92  	bam := errors.New("bam")
    93  	s.fCaller.EXPECT().FacadeCall("RemoveSpace", args, gomock.Any()).SetArg(2, resultSource).Return(bam)
    94  
    95  	bounds, err := s.API.RemoveSpace(name, false, false)
    96  	c.Assert(err, gc.ErrorMatches, bam.Error())
    97  	c.Assert(bounds, gc.DeepEquals, params.RemoveSpaceResult{})
    98  }
    99  
   100  func (s *spacesSuite) TestRemoveSpaceUnexpectedErrorAPICallNotSupported(c *gc.C) {
   101  	defer s.setUpMocks(c).Finish()
   102  	name := "myspace"
   103  	resultSource := params.RemoveSpaceResults{
   104  		Results: []params.RemoveSpaceResult{}}
   105  	args := getRemoveSpaceArgs(name, false, false)
   106  
   107  	bam := params.Error{
   108  		Message: "not supported",
   109  		Code:    params.CodeNotSupported,
   110  		Info:    nil,
   111  	}
   112  	s.fCaller.EXPECT().FacadeCall("RemoveSpace", args, gomock.Any()).SetArg(2, resultSource).Return(bam)
   113  
   114  	bounds, err := s.API.RemoveSpace(name, false, false)
   115  	c.Assert(err, gc.ErrorMatches, bam.Error())
   116  	c.Assert(bounds, gc.DeepEquals, params.RemoveSpaceResult{})
   117  }
   118  
   119  func (s *spacesSuite) TestRemoveSpaceConstraintsBindings(c *gc.C) {
   120  	defer s.setUpMocks(c).Finish()
   121  	name := "myspace"
   122  	resultSource := params.RemoveSpaceResults{
   123  		Results: []params.RemoveSpaceResult{{
   124  			Constraints: []params.Entity{
   125  				{Tag: "model-42c4f770-86ed-4fcc-8e39-697063d082bc:e"},
   126  				{Tag: "application-mysql"},
   127  			},
   128  			Bindings: []params.Entity{
   129  				{Tag: "application-mysql"},
   130  				{Tag: "application-mediawiki"},
   131  			},
   132  			ControllerSettings: []string{"jujuhaspace", "juuuu-space"},
   133  			Error:              nil,
   134  		}}}
   135  	args := getRemoveSpaceArgs(name, false, false)
   136  
   137  	s.fCaller.EXPECT().FacadeCall("RemoveSpace", args, gomock.Any()).SetArg(2, resultSource).Return(nil)
   138  
   139  	bounds, err := s.API.RemoveSpace(name, false, false)
   140  
   141  	expectedBounds := params.RemoveSpaceResult{
   142  		Constraints: []params.Entity{
   143  			{Tag: "model-42c4f770-86ed-4fcc-8e39-697063d082bc:e"},
   144  			{Tag: "application-mysql"},
   145  		},
   146  		Bindings: []params.Entity{
   147  			{Tag: "application-mysql"},
   148  			{Tag: "application-mediawiki"},
   149  		},
   150  		ControllerSettings: []string{"jujuhaspace", "juuuu-space"},
   151  	}
   152  	c.Assert(err, jc.ErrorIsNil)
   153  	c.Assert(bounds, jc.DeepEquals, expectedBounds)
   154  }
   155  func (s *spacesSuite) TestRemoveSpaceConstraints(c *gc.C) {
   156  	defer s.setUpMocks(c).Finish()
   157  	name := "myspace"
   158  	resultSource := params.RemoveSpaceResults{
   159  		Results: []params.RemoveSpaceResult{{
   160  			Constraints: []params.Entity{
   161  				{Tag: "model-42c4f770-86ed-4fcc-8e39-697063d082bc:e"},
   162  				{Tag: "application-mysql"},
   163  			},
   164  			Error: nil,
   165  		}}}
   166  	args := getRemoveSpaceArgs(name, false, false)
   167  	s.fCaller.EXPECT().FacadeCall("RemoveSpace", args, gomock.Any()).SetArg(2, resultSource).Return(nil)
   168  
   169  	bounds, err := s.API.RemoveSpace(name, false, false)
   170  	expectedBounds := params.RemoveSpaceResult{
   171  		Constraints: []params.Entity{
   172  			{Tag: "model-42c4f770-86ed-4fcc-8e39-697063d082bc:e"},
   173  			{Tag: "application-mysql"},
   174  		},
   175  	}
   176  	c.Assert(err, jc.ErrorIsNil)
   177  	c.Assert(bounds, jc.DeepEquals, expectedBounds)
   178  }
   179  
   180  func (s *spacesSuite) TestRemoveSpaceForce(c *gc.C) {
   181  	defer s.setUpMocks(c).Finish()
   182  	name := "myspace"
   183  	resultSource := params.RemoveSpaceResults{
   184  		Results: []params.RemoveSpaceResult{{}}}
   185  	args := getRemoveSpaceArgs(name, true, false)
   186  	s.fCaller.EXPECT().FacadeCall("RemoveSpace", args, gomock.Any()).SetArg(2, resultSource).Return(nil)
   187  
   188  	bounds, err := s.API.RemoveSpace(name, true, false)
   189  
   190  	c.Assert(err, gc.IsNil)
   191  	c.Assert(bounds, gc.DeepEquals, params.RemoveSpaceResult{})
   192  }
   193  
   194  func getRemoveSpaceArgs(spaceName string, force, dryRun bool) params.RemoveSpaceParams {
   195  	return params.RemoveSpaceParams{SpaceParams: []params.RemoveSpaceParam{
   196  		{
   197  			Space:  params.Entity{Tag: names.NewSpaceTag(spaceName).String()},
   198  			Force:  force,
   199  			DryRun: dryRun,
   200  		},
   201  	}}
   202  }
   203  
   204  func (s *spacesSuite) TestRenameSpace(c *gc.C) {
   205  	defer s.setUpMocks(c).Finish()
   206  	from, to := "from", "to"
   207  	resultSource := params.ErrorResults{}
   208  	args := params.RenameSpacesParams{
   209  		Changes: []params.RenameSpaceParams{{
   210  			FromSpaceTag: names.NewSpaceTag(from).String(),
   211  			ToSpaceTag:   names.NewSpaceTag(to).String(),
   212  		}},
   213  	}
   214  	s.fCaller.EXPECT().FacadeCall("RenameSpace", args, gomock.Any()).SetArg(2, resultSource).Return(nil)
   215  
   216  	err := s.API.RenameSpace(from, to)
   217  	c.Assert(err, gc.IsNil)
   218  }
   219  
   220  func (s *spacesSuite) TestRenameSpaceError(c *gc.C) {
   221  	defer s.setUpMocks(c).Finish()
   222  	from, to := "from", "to"
   223  	resultSource := params.ErrorResults{Results: []params.ErrorResult{{
   224  		Error: &params.Error{
   225  			Message: "bam",
   226  			Code:    "500",
   227  		},
   228  	}}}
   229  	args := params.RenameSpacesParams{
   230  		Changes: []params.RenameSpaceParams{{
   231  			FromSpaceTag: names.NewSpaceTag(from).String(),
   232  			ToSpaceTag:   names.NewSpaceTag(to).String(),
   233  		}},
   234  	}
   235  	s.fCaller.EXPECT().FacadeCall("RenameSpace", args, gomock.Any()).SetArg(2, resultSource).Return(nil)
   236  
   237  	err := s.API.RenameSpace(from, to)
   238  	c.Assert(err, gc.ErrorMatches, "bam")
   239  }
   240  
   241  func (s *spacesSuite) TestCreateSpace(c *gc.C) {
   242  	defer s.setUpMocks(c).Finish()
   243  	name := "foo"
   244  	subnets := []string{}
   245  	r := rand.New(rand.NewSource(0xdeadbeef))
   246  	for i := 0; i < 100; i++ {
   247  		for j := 0; j < 10; j++ {
   248  			n := r.Uint32()
   249  			newSubnet := fmt.Sprintf("%d.%d.%d.0/24", uint8(n>>16), uint8(n>>8), uint8(n))
   250  			subnets = append(subnets, newSubnet)
   251  		}
   252  		args := params.CreateSpacesParams{
   253  			Spaces: []params.CreateSpaceParams{
   254  				{
   255  					SpaceTag: names.NewSpaceTag(name).String(),
   256  					CIDRs:    subnets,
   257  					Public:   true,
   258  				},
   259  			},
   260  		}
   261  		res := new(params.ErrorResults)
   262  		ress := params.ErrorResults{
   263  			Results: []params.ErrorResult{{}},
   264  		}
   265  		s.fCaller.EXPECT().FacadeCall("CreateSpaces", args, res).SetArg(2, ress).Return(nil)
   266  		err := s.API.CreateSpace(name, subnets, true)
   267  		c.Assert(err, jc.ErrorIsNil)
   268  	}
   269  }
   270  
   271  func (s *spacesSuite) TestCreateSpaceEmptyResults(c *gc.C) {
   272  	defer s.setUpMocks(c).Finish()
   273  	args := params.CreateSpacesParams{
   274  		Spaces: []params.CreateSpaceParams{
   275  			{
   276  				SpaceTag: names.NewSpaceTag("foo").String(),
   277  				CIDRs:    nil,
   278  				Public:   true,
   279  			},
   280  		},
   281  	}
   282  	res := new(params.ErrorResults)
   283  	ress := params.ErrorResults{
   284  		Results: []params.ErrorResult{{
   285  			Error: &params.Error{Message: "expected 1 result, got 0"},
   286  		}},
   287  	}
   288  
   289  	s.fCaller.EXPECT().FacadeCall("CreateSpaces", args, res).SetArg(2, ress).Return(nil)
   290  	err := s.API.CreateSpace("foo", nil, true)
   291  	c.Assert(err, gc.ErrorMatches, "expected 1 result, got 0")
   292  }
   293  
   294  func (s *spacesSuite) TestCreateSpaceFails(c *gc.C) {
   295  	defer s.setUpMocks(c).Finish()
   296  	args := params.CreateSpacesParams{
   297  		Spaces: []params.CreateSpaceParams{
   298  			{
   299  				SpaceTag: names.NewSpaceTag("foo").String(),
   300  				CIDRs:    []string{"1.1.1.0/24"},
   301  				Public:   true,
   302  			},
   303  		},
   304  	}
   305  	res := new(params.ErrorResults)
   306  	ress := params.ErrorResults{
   307  		Results: []params.ErrorResult{{
   308  			Error: &params.Error{Message: "bang"},
   309  		}},
   310  	}
   311  
   312  	s.fCaller.EXPECT().FacadeCall("CreateSpaces", args, res).SetArg(2, ress).Return(nil)
   313  	err := s.API.CreateSpace("foo", []string{"1.1.1.0/24"}, true)
   314  	c.Assert(err, gc.ErrorMatches, "bang")
   315  }
   316  
   317  func (s *spacesSuite) testShowSpaces(c *gc.C, spaceName string, results []params.ShowSpaceResult, err error, expectErr string) {
   318  	defer s.setUpMocks(c).Finish()
   319  
   320  	var expectResults params.ShowSpaceResults
   321  	if results != nil {
   322  		expectResults = params.ShowSpaceResults{
   323  			Results: results,
   324  		}
   325  	}
   326  
   327  	args := params.Entities{
   328  		Entities: []params.Entity{{Tag: names.NewSpaceTag(spaceName).String()}},
   329  	}
   330  	res := new(params.ShowSpaceResults)
   331  
   332  	s.fCaller.EXPECT().FacadeCall("ShowSpace", args, res).SetArg(2, expectResults).Return(err)
   333  	gotResults, gotErr := s.API.ShowSpace(spaceName)
   334  	if expectErr != "" {
   335  		c.Assert(gotErr, gc.ErrorMatches, expectErr)
   336  		return
   337  	} else {
   338  		c.Assert(results, gc.NotNil)
   339  		c.Assert(len(results), gc.Equals, 1)
   340  		c.Assert(gotResults, jc.DeepEquals, results[0])
   341  	}
   342  	if err != nil {
   343  		c.Assert(gotErr, jc.DeepEquals, err)
   344  	} else {
   345  		c.Assert(gotErr, jc.ErrorIsNil)
   346  	}
   347  }
   348  
   349  func (s *spacesSuite) TestShowSpaceTooManyResults(c *gc.C) {
   350  	s.testShowSpaces(c, "empty",
   351  		[]params.ShowSpaceResult{
   352  			{
   353  				Space: params.Space{},
   354  			},
   355  			{
   356  				Space: params.Space{},
   357  			},
   358  		}, nil, "expected 1 result, got 2")
   359  }
   360  
   361  func (s *spacesSuite) TestShowSpaceNoResultsResults(c *gc.C) {
   362  	s.testShowSpaces(c, "empty", nil, nil, "expected 1 result, got 0")
   363  }
   364  
   365  func (s *spacesSuite) TestShowSpaceResult(c *gc.C) {
   366  	result := []params.ShowSpaceResult{{
   367  		Space:        params.Space{Id: "1", Name: "default"},
   368  		Applications: []string{},
   369  		MachineCount: 0,
   370  	}}
   371  	s.testShowSpaces(c, "default", result, nil, "")
   372  }
   373  
   374  func (s *spacesSuite) TestShowSpaceServerError(c *gc.C) {
   375  	s.testShowSpaces(c, "nil", nil, errors.New("boom"), "boom")
   376  }
   377  
   378  func (s *spacesSuite) TestShowSpaceError(c *gc.C) {
   379  	defer s.setUpMocks(c).Finish()
   380  
   381  	arg := "space"
   382  	args := params.Entities{
   383  		Entities: []params.Entity{{Tag: names.NewSpaceTag(arg).String()}},
   384  	}
   385  	res := new(params.ShowSpaceResults)
   386  	ress := params.ShowSpaceResults{
   387  		Results: []params.ShowSpaceResult{},
   388  	}
   389  
   390  	s.fCaller.EXPECT().FacadeCall("ShowSpace", args, res).SetArg(2, ress).Return(nil)
   391  
   392  	_, err := s.API.ShowSpace(arg)
   393  	c.Assert(err, gc.ErrorMatches, "expected 1 result, got 0")
   394  }
   395  
   396  func (s *spacesSuite) testListSpaces(c *gc.C, results []params.Space, err error, expectErr string) {
   397  	defer s.setUpMocks(c).Finish()
   398  
   399  	var expectResults params.ListSpacesResults
   400  	if results != nil {
   401  		expectResults = params.ListSpacesResults{
   402  			Results: results,
   403  		}
   404  	}
   405  
   406  	res := new(params.ListSpacesResults)
   407  
   408  	s.fCaller.EXPECT().FacadeCall("ListSpaces", nil, res).SetArg(2, expectResults).Return(err)
   409  	gotResults, gotErr := s.API.ListSpaces()
   410  	c.Assert(gotResults, jc.DeepEquals, results)
   411  	if expectErr != "" {
   412  		c.Assert(gotErr, gc.ErrorMatches, expectErr)
   413  		return
   414  	}
   415  	if err != nil {
   416  		c.Assert(gotErr, jc.DeepEquals, err)
   417  	} else {
   418  		c.Assert(gotErr, jc.ErrorIsNil)
   419  	}
   420  }
   421  
   422  func (s *spacesSuite) TestListSpacesEmptyResults(c *gc.C) {
   423  	s.testListSpaces(c, []params.Space{}, nil, "")
   424  }
   425  
   426  func (s *spacesSuite) TestListSpacesManyResults(c *gc.C) {
   427  	spaces := []params.Space{{
   428  		Name: "space1",
   429  		Subnets: []params.Subnet{{
   430  			CIDR: "foo",
   431  		}, {
   432  			CIDR: "bar",
   433  		}},
   434  	}, {
   435  		Name: "space2",
   436  	}, {
   437  		Name:    "space3",
   438  		Subnets: []params.Subnet{},
   439  	}}
   440  	s.testListSpaces(c, spaces, nil, "")
   441  }
   442  
   443  func (s *spacesSuite) TestListSpacesServerError(c *gc.C) {
   444  	s.testListSpaces(c, nil, errors.New("boom"), "boom")
   445  }
   446  
   447  func (s *spacesSuite) testMoveSubnets(c *gc.C,
   448  	space names.SpaceTag,
   449  	subnets []names.SubnetTag,
   450  	results []params.MoveSubnetsResult,
   451  	err error, expectErr string,
   452  ) {
   453  	defer s.setUpMocks(c).Finish()
   454  
   455  	var expectedResults params.MoveSubnetsResults
   456  	if results != nil {
   457  		expectedResults.Results = results
   458  	}
   459  
   460  	subnetTags := make([]string, len(subnets))
   461  	for k, subnet := range subnets {
   462  		subnetTags[k] = subnet.String()
   463  	}
   464  	args := params.MoveSubnetsParams{
   465  		Args: []params.MoveSubnetsParam{{
   466  			SubnetTags: subnetTags,
   467  			SpaceTag:   space.String(),
   468  			Force:      false,
   469  		}},
   470  	}
   471  	res := new(params.MoveSubnetsResults)
   472  
   473  	s.fCaller.EXPECT().FacadeCall("MoveSubnets", args, res).SetArg(2, expectedResults).Return(err)
   474  	gotResult, gotErr := s.API.MoveSubnets(space, subnets, false)
   475  	if len(results) > 0 {
   476  		c.Assert(gotResult, jc.DeepEquals, results[0])
   477  	} else {
   478  		c.Assert(gotResult, jc.DeepEquals, params.MoveSubnetsResult{})
   479  	}
   480  
   481  	if expectErr != "" {
   482  		c.Assert(gotErr, gc.ErrorMatches, expectErr)
   483  		return
   484  	}
   485  
   486  	if err != nil {
   487  		c.Assert(gotErr, jc.DeepEquals, err)
   488  	} else {
   489  		c.Assert(gotErr, jc.ErrorIsNil)
   490  	}
   491  }
   492  
   493  func (s *spacesSuite) TestMoveSubnetsEmptyResults(c *gc.C) {
   494  	space := names.NewSpaceTag("aaabbb")
   495  	subnets := []names.SubnetTag{names.NewSubnetTag("1")}
   496  
   497  	s.testMoveSubnets(c, space, subnets, []params.MoveSubnetsResult{}, nil, "expected 1 result, got 0")
   498  }
   499  
   500  func (s *spacesSuite) TestMoveSubnets(c *gc.C) {
   501  	space := names.NewSpaceTag("aaabbb")
   502  	subnets := []names.SubnetTag{names.NewSubnetTag("1")}
   503  
   504  	s.testMoveSubnets(c, space, subnets, []params.MoveSubnetsResult{{
   505  		MovedSubnets: []params.MovedSubnet{{
   506  			SubnetTag:   "2",
   507  			OldSpaceTag: "aaabbb",
   508  		}},
   509  		NewSpaceTag: "xxxyyy",
   510  	}}, nil, "")
   511  }
   512  
   513  func (s *spacesSuite) TestMoveSubnetsServerError(c *gc.C) {
   514  	space := names.NewSpaceTag("aaabbb")
   515  	subnets := []names.SubnetTag{names.NewSubnetTag("1")}
   516  
   517  	s.testMoveSubnets(c, space, subnets, nil, errors.New("boom"), "boom")
   518  }