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: ¶ms.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 }