github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/client/modelgeneration/modelgeneration_test.go (about)

     1  // Copyright 2019 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package modelgeneration_test
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/names/v5"
     9  	jc "github.com/juju/testing/checkers"
    10  	"go.uber.org/mock/gomock"
    11  	gc "gopkg.in/check.v1"
    12  
    13  	facademocks "github.com/juju/juju/apiserver/facade/mocks"
    14  	"github.com/juju/juju/apiserver/facades/client/modelgeneration"
    15  	"github.com/juju/juju/apiserver/facades/client/modelgeneration/mocks"
    16  	"github.com/juju/juju/core/cache"
    17  	"github.com/juju/juju/core/model"
    18  	"github.com/juju/juju/core/settings"
    19  	"github.com/juju/juju/rpc/params"
    20  )
    21  
    22  type modelGenerationSuite struct {
    23  	modelUUID     string
    24  	newBranchName string
    25  	apiUser       string
    26  
    27  	api *modelgeneration.API
    28  
    29  	mockState      *mocks.MockState
    30  	mockModel      *mocks.MockModel
    31  	mockGen        *mocks.MockGeneration
    32  	mockModelCache *mocks.MockModelCache
    33  }
    34  
    35  var _ = gc.Suite(&modelGenerationSuite{})
    36  
    37  func (s *modelGenerationSuite) SetUpSuite(c *gc.C) {
    38  	s.modelUUID = "deadbeef-abcd-4fd2-967d-db9663db7bea"
    39  	s.newBranchName = "new-branch"
    40  	s.apiUser = "test-user"
    41  }
    42  
    43  func (s *modelGenerationSuite) TearDownTest(c *gc.C) {
    44  	s.api = nil
    45  }
    46  
    47  // TODO (hml) 17-jan-2019
    48  // Add more explicit permissions tests once that requirement is ironed out.
    49  
    50  func (s *modelGenerationSuite) TestAddBranchInvalidNameError(c *gc.C) {
    51  	defer s.setupModelGenerationAPI(c).Finish()
    52  
    53  	arg := params.BranchArg{BranchName: model.GenerationMaster}
    54  	result, err := s.api.AddBranch(arg)
    55  	c.Assert(err, jc.ErrorIsNil)
    56  	c.Assert(result.Error, gc.NotNil)
    57  	c.Check(result.Error.Message, gc.Matches, ".* not valid")
    58  }
    59  
    60  func (s *modelGenerationSuite) TestAddBranchSuccess(c *gc.C) {
    61  	defer s.setupModelGenerationAPI(c).Finish()
    62  	s.expectAddBranch()
    63  
    64  	result, err := s.api.AddBranch(s.newBranchArg())
    65  	c.Assert(err, jc.ErrorIsNil)
    66  	c.Assert(result.Error, gc.IsNil)
    67  }
    68  
    69  func (s *modelGenerationSuite) TestTrackBranchEntityTypeError(c *gc.C) {
    70  	defer s.setupModelGenerationAPI(c).Finish()
    71  	s.expectAssignUnits("ghost", 0)
    72  	s.expectAssignUnit("mysql/0")
    73  	s.expectBranch()
    74  
    75  	arg := params.BranchTrackArg{
    76  		BranchName: s.newBranchName,
    77  		Entities: []params.Entity{
    78  			{Tag: names.NewUnitTag("mysql/0").String()},
    79  			{Tag: names.NewApplicationTag("ghost").String()},
    80  			{Tag: names.NewMachineTag("7").String()},
    81  		},
    82  	}
    83  	result, err := s.api.TrackBranch(arg)
    84  	c.Assert(err, jc.ErrorIsNil)
    85  	c.Check(result.Results, gc.DeepEquals, []params.ErrorResult{
    86  		{Error: nil},
    87  		{Error: nil},
    88  		{Error: &params.Error{Message: "expected names.UnitTag or names.ApplicationTag, got names.MachineTag"}},
    89  	})
    90  }
    91  
    92  func (s *modelGenerationSuite) TestTrackBranchSuccess(c *gc.C) {
    93  	defer s.setupModelGenerationAPI(c).Finish()
    94  	s.expectAssignUnits("ghost", 0)
    95  	s.expectAssignUnit("mysql/0")
    96  	s.expectBranch()
    97  
    98  	arg := params.BranchTrackArg{
    99  		BranchName: s.newBranchName,
   100  		Entities: []params.Entity{
   101  			{Tag: names.NewUnitTag("mysql/0").String()},
   102  			{Tag: names.NewApplicationTag("ghost").String()},
   103  		},
   104  	}
   105  	result, err := s.api.TrackBranch(arg)
   106  	c.Assert(err, jc.ErrorIsNil)
   107  	c.Check(result.Results, gc.DeepEquals, []params.ErrorResult{
   108  		{Error: nil},
   109  		{Error: nil},
   110  	})
   111  }
   112  
   113  func (s *modelGenerationSuite) TestTrackBranchWithTooManyNumUnits(c *gc.C) {
   114  	defer s.setupModelGenerationAPI(c).Finish()
   115  
   116  	arg := params.BranchTrackArg{
   117  		BranchName: s.newBranchName,
   118  		Entities: []params.Entity{
   119  			{Tag: names.NewUnitTag("mysql/0").String()},
   120  			{Tag: names.NewApplicationTag("ghost").String()},
   121  		},
   122  		NumUnits: 1,
   123  	}
   124  	result, err := s.api.TrackBranch(arg)
   125  	c.Assert(err, gc.ErrorMatches, "number of units and unit IDs can not be specified at the same time")
   126  	c.Check(result.Results, gc.DeepEquals, []params.ErrorResult(nil))
   127  }
   128  
   129  func (s *modelGenerationSuite) TestCommitBranchSuccess(c *gc.C) {
   130  	defer s.setupModelGenerationAPI(c).Finish()
   131  	s.expectCommit()
   132  	s.expectBranch()
   133  
   134  	result, err := s.api.CommitBranch(s.newBranchArg())
   135  	c.Assert(err, jc.ErrorIsNil)
   136  	c.Assert(result, gc.DeepEquals, params.IntResult{Result: 3, Error: nil})
   137  }
   138  
   139  func (s *modelGenerationSuite) TestAbortBranchSuccess(c *gc.C) {
   140  	defer s.setupModelGenerationAPI(c).Finish()
   141  	s.expectAbort()
   142  	s.expectBranch()
   143  
   144  	result, err := s.api.AbortBranch(s.newBranchArg())
   145  	c.Assert(err, jc.ErrorIsNil)
   146  	c.Assert(result, gc.DeepEquals, params.ErrorResult{Error: nil})
   147  }
   148  
   149  func (s *modelGenerationSuite) TestHasActiveBranchTrue(c *gc.C) {
   150  	defer s.setupModelGenerationAPI(c).Finish()
   151  	s.expectHasActiveBranch(nil)
   152  
   153  	result, err := s.api.HasActiveBranch(s.newBranchArg())
   154  	c.Assert(err, jc.ErrorIsNil)
   155  	c.Assert(result.Error, gc.IsNil)
   156  	c.Check(result.Result, jc.IsTrue)
   157  }
   158  
   159  func (s *modelGenerationSuite) TestHasActiveBranchFalse(c *gc.C) {
   160  	defer s.setupModelGenerationAPI(c).Finish()
   161  	s.expectHasActiveBranch(errors.NotFoundf(s.newBranchName))
   162  
   163  	result, err := s.api.HasActiveBranch(s.newBranchArg())
   164  	c.Assert(err, jc.ErrorIsNil)
   165  	c.Assert(result.Error, gc.IsNil)
   166  	c.Check(result.Result, jc.IsFalse)
   167  }
   168  
   169  func (s *modelGenerationSuite) TestBranchInfoDetailed(c *gc.C) {
   170  	s.testBranchInfo(c, nil, true)
   171  }
   172  
   173  func (s *modelGenerationSuite) TestBranchInfoSummary(c *gc.C) {
   174  	s.testBranchInfo(c, []string{s.newBranchName}, false)
   175  }
   176  
   177  func (s *modelGenerationSuite) testBranchInfo(c *gc.C, branchNames []string, detailed bool) {
   178  	ctrl := s.setupModelGenerationAPI(c)
   179  	defer ctrl.Finish()
   180  
   181  	units := []string{"redis/0", "redis/1", "redis/2"}
   182  
   183  	s.expectConfig()
   184  	s.expectBranchName()
   185  	s.expectAssignedUnits(units[:2])
   186  	s.expectCreated()
   187  	s.expectCreatedBy()
   188  
   189  	// Flex the code path based on whether we are getting all branches
   190  	// or a sub-set.
   191  	if len(branchNames) > 0 {
   192  		s.expectBranch()
   193  	} else {
   194  		s.expectBranches()
   195  	}
   196  
   197  	s.setupMockApp(ctrl, units)
   198  
   199  	result, err := s.api.BranchInfo(params.BranchInfoArgs{
   200  		BranchNames: branchNames,
   201  		Detailed:    detailed,
   202  	})
   203  	c.Assert(err, jc.ErrorIsNil)
   204  	c.Assert(result.Error, gc.IsNil)
   205  	c.Assert(result.Generations, gc.HasLen, 1)
   206  
   207  	gen := result.Generations[0]
   208  	c.Assert(gen.BranchName, gc.Equals, s.newBranchName)
   209  	c.Assert(gen.Created, gc.Equals, int64(666))
   210  	c.Assert(gen.CreatedBy, gc.Equals, s.apiUser)
   211  	c.Assert(gen.Applications, gc.HasLen, 1)
   212  
   213  	genApp := gen.Applications[0]
   214  	c.Check(genApp.ApplicationName, gc.Equals, "redis")
   215  	c.Check(genApp.UnitProgress, gc.Equals, "2/3")
   216  	c.Check(genApp.ConfigChanges, gc.DeepEquals, map[string]interface{}{
   217  		"password":  "added-pass",
   218  		"databases": 16,
   219  		"port":      8000,
   220  	})
   221  
   222  	// Unit lists are only populated when detailed is true.
   223  	if detailed {
   224  		c.Check(genApp.UnitsTracking, jc.SameContents, units[:2])
   225  		c.Check(genApp.UnitsPending, jc.SameContents, units[2:])
   226  	} else {
   227  		c.Check(genApp.UnitsTracking, gc.IsNil)
   228  		c.Check(genApp.UnitsPending, gc.IsNil)
   229  	}
   230  }
   231  
   232  func (s *modelGenerationSuite) setupModelGenerationAPI(c *gc.C) *gomock.Controller {
   233  	ctrl := gomock.NewController(c)
   234  
   235  	s.mockGen = mocks.NewMockGeneration(ctrl)
   236  
   237  	s.mockState = mocks.NewMockState(ctrl)
   238  	s.mockState.EXPECT().ControllerTag().Return(names.NewControllerTag(s.modelUUID))
   239  
   240  	s.mockModel = mocks.NewMockModel(ctrl)
   241  
   242  	mockAuthorizer := facademocks.NewMockAuthorizer(ctrl)
   243  	aExp := mockAuthorizer.EXPECT()
   244  	aExp.HasPermission(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
   245  	aExp.GetAuthTag().Return(names.NewUserTag("test-user"))
   246  	aExp.AuthClient().Return(true)
   247  
   248  	s.mockModelCache = mocks.NewMockModelCache(ctrl)
   249  
   250  	var err error
   251  	s.api, err = modelgeneration.NewModelGenerationAPI(s.mockState, mockAuthorizer, s.mockModel, s.mockModelCache)
   252  	c.Assert(err, jc.ErrorIsNil)
   253  
   254  	return ctrl
   255  }
   256  
   257  func (s *modelGenerationSuite) newBranchArg() params.BranchArg {
   258  	return params.BranchArg{BranchName: s.newBranchName}
   259  }
   260  
   261  func (s *modelGenerationSuite) expectAddBranch() {
   262  	s.mockModel.EXPECT().AddBranch(s.newBranchName, s.apiUser).Return(nil)
   263  }
   264  
   265  func (s *modelGenerationSuite) expectBranches() {
   266  	s.mockModel.EXPECT().Branches().Return([]modelgeneration.Generation{s.mockGen}, nil)
   267  }
   268  
   269  func (s *modelGenerationSuite) expectBranch() {
   270  	s.mockModel.EXPECT().Branch(s.newBranchName).Return(s.mockGen, nil)
   271  }
   272  
   273  func (s *modelGenerationSuite) expectHasActiveBranch(err error) {
   274  	s.mockModelCache.EXPECT().Branch(s.newBranchName).Return(cache.Branch{}, err)
   275  }
   276  
   277  func (s *modelGenerationSuite) expectAssignUnits(appName string, numUnits int) {
   278  	s.mockGen.EXPECT().AssignUnits(appName, numUnits).Return(nil)
   279  }
   280  
   281  func (s *modelGenerationSuite) expectAssignUnit(unitName string) {
   282  	s.mockGen.EXPECT().AssignUnit(unitName).Return(nil)
   283  }
   284  
   285  func (s *modelGenerationSuite) expectAbort() {
   286  	s.mockGen.EXPECT().Abort(s.apiUser).Return(nil)
   287  }
   288  
   289  func (s *modelGenerationSuite) expectCommit() {
   290  	s.mockGen.EXPECT().Commit(s.apiUser).Return(3, nil)
   291  }
   292  
   293  func (s *modelGenerationSuite) expectAssignedUnits(units []string) {
   294  	s.mockGen.EXPECT().AssignedUnits().Return(map[string][]string{"redis": units})
   295  }
   296  
   297  func (s *modelGenerationSuite) expectBranchName() {
   298  	s.mockGen.EXPECT().BranchName().Return(s.newBranchName)
   299  }
   300  
   301  func (s *modelGenerationSuite) expectCreated() {
   302  	s.mockGen.EXPECT().Created().Return(int64(666))
   303  }
   304  
   305  func (s *modelGenerationSuite) expectCreatedBy() {
   306  	s.mockGen.EXPECT().CreatedBy().Return(s.apiUser)
   307  }
   308  
   309  func (s *modelGenerationSuite) expectConfig() {
   310  	s.mockGen.EXPECT().Config().Return(map[string]settings.ItemChanges{"redis": {
   311  		settings.MakeAddition("password", "added-pass"),
   312  		settings.MakeDeletion("databases", 100),
   313  		settings.MakeModification("port", 7000, 8000),
   314  	}})
   315  }
   316  
   317  func (s *modelGenerationSuite) setupMockApp(ctrl *gomock.Controller, units []string) {
   318  	mockApp := mocks.NewMockApplication(ctrl)
   319  	mockApp.EXPECT().DefaultCharmConfig().Return(map[string]interface{}{
   320  		"databases": 16,
   321  		"password":  "",
   322  	}, nil)
   323  	mockApp.EXPECT().UnitNames().Return(units, nil)
   324  
   325  	s.mockState.EXPECT().Application("redis").Return(mockApp, nil)
   326  }