github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/client/machinemanager/machinemanager_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package machinemanager_test 5 6 import ( 7 "sort" 8 "strings" 9 10 "github.com/juju/errors" 11 "github.com/juju/os/series" 12 jtesting "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 gc "gopkg.in/check.v1" 15 "gopkg.in/juju/names.v2" 16 17 "github.com/juju/juju/apiserver/common" 18 "github.com/juju/juju/apiserver/common/storagecommon" 19 "github.com/juju/juju/apiserver/facades/client/machinemanager" 20 "github.com/juju/juju/apiserver/params" 21 apiservertesting "github.com/juju/juju/apiserver/testing" 22 "github.com/juju/juju/cloud" 23 "github.com/juju/juju/core/status" 24 "github.com/juju/juju/environs/context" 25 "github.com/juju/juju/state" 26 "github.com/juju/juju/state/multiwatcher" 27 "github.com/juju/juju/storage" 28 coretesting "github.com/juju/juju/testing" 29 ) 30 31 var _ = gc.Suite(&MachineManagerSuite{}) 32 33 type MachineManagerSuite struct { 34 coretesting.BaseSuite 35 authorizer *apiservertesting.FakeAuthorizer 36 st *mockState 37 pool *mockPool 38 api *machinemanager.MachineManagerAPI 39 40 callContext context.ProviderCallContext 41 } 42 43 func (s *MachineManagerSuite) setAPIUser(c *gc.C, user names.UserTag) { 44 s.authorizer.Tag = user 45 mm, err := machinemanager.NewMachineManagerAPI(s.st, s.st, s.pool, s.authorizer, s.st.ModelTag(), s.callContext, common.NewResources()) 46 c.Assert(err, jc.ErrorIsNil) 47 s.api = mm 48 } 49 50 func (s *MachineManagerSuite) SetUpTest(c *gc.C) { 51 s.BaseSuite.SetUpTest(c) 52 s.st = &mockState{machines: make(map[string]*mockMachine)} 53 s.pool = &mockPool{} 54 s.authorizer = &apiservertesting.FakeAuthorizer{Tag: names.NewUserTag("admin")} 55 s.callContext = context.NewCloudCallContext() 56 var err error 57 s.api, err = machinemanager.NewMachineManagerAPI(s.st, s.st, s.pool, s.authorizer, s.st.ModelTag(), s.callContext, common.NewResources()) 58 c.Assert(err, jc.ErrorIsNil) 59 } 60 61 func (s *MachineManagerSuite) TestAddMachines(c *gc.C) { 62 apiParams := make([]params.AddMachineParams, 2) 63 for i := range apiParams { 64 apiParams[i] = params.AddMachineParams{ 65 Series: "trusty", 66 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 67 } 68 } 69 apiParams[0].Disks = []storage.Constraints{{Size: 1, Count: 2}, {Size: 2, Count: 1}} 70 apiParams[1].Disks = []storage.Constraints{{Size: 1, Count: 2, Pool: "three"}} 71 machines, err := s.api.AddMachines(params.AddMachines{MachineParams: apiParams}) 72 c.Assert(err, jc.ErrorIsNil) 73 c.Assert(machines.Machines, gc.HasLen, 2) 74 c.Assert(s.st.calls, gc.Equals, 2) 75 c.Assert(s.st.machineTemplates, jc.DeepEquals, []state.MachineTemplate{ 76 { 77 Series: "trusty", 78 Jobs: []state.MachineJob{state.JobHostUnits}, 79 Volumes: []state.HostVolumeParams{ 80 { 81 Volume: state.VolumeParams{Pool: "", Size: 1}, 82 Attachment: state.VolumeAttachmentParams{ReadOnly: false}, 83 }, 84 { 85 Volume: state.VolumeParams{Pool: "", Size: 1}, 86 Attachment: state.VolumeAttachmentParams{ReadOnly: false}, 87 }, 88 { 89 Volume: state.VolumeParams{Pool: "", Size: 2}, 90 Attachment: state.VolumeAttachmentParams{ReadOnly: false}, 91 }, 92 }, 93 }, 94 { 95 Series: "trusty", 96 Jobs: []state.MachineJob{state.JobHostUnits}, 97 Volumes: []state.HostVolumeParams{ 98 { 99 Volume: state.VolumeParams{Pool: "three", Size: 1}, 100 Attachment: state.VolumeAttachmentParams{ReadOnly: false}, 101 }, 102 { 103 Volume: state.VolumeParams{Pool: "three", Size: 1}, 104 Attachment: state.VolumeAttachmentParams{ReadOnly: false}, 105 }, 106 }, 107 }, 108 }) 109 } 110 111 func (s *MachineManagerSuite) TestNewMachineManagerAPINonClient(c *gc.C) { 112 tag := names.NewUnitTag("mysql/0") 113 s.authorizer = &apiservertesting.FakeAuthorizer{Tag: tag} 114 _, err := machinemanager.NewMachineManagerAPI(nil, nil, nil, s.authorizer, names.ModelTag{}, s.callContext, common.NewResources()) 115 c.Assert(err, gc.ErrorMatches, "permission denied") 116 } 117 118 func (s *MachineManagerSuite) TestAddMachinesStateError(c *gc.C) { 119 s.st.err = errors.New("boom") 120 results, err := s.api.AddMachines(params.AddMachines{ 121 MachineParams: []params.AddMachineParams{{ 122 Series: "trusty", 123 }}, 124 }) 125 c.Assert(err, jc.ErrorIsNil) 126 c.Assert(results, gc.DeepEquals, params.AddMachinesResults{ 127 Machines: []params.AddMachinesResult{{ 128 Error: ¶ms.Error{Message: "boom", Code: ""}, 129 }}, 130 }) 131 c.Assert(s.st.calls, gc.Equals, 1) 132 } 133 134 func (s *MachineManagerSuite) TestDestroyMachine(c *gc.C) { 135 s.st.machines["0"] = &mockMachine{} 136 results, err := s.api.DestroyMachine(params.Entities{ 137 Entities: []params.Entity{{Tag: "machine-0"}}, 138 }) 139 c.Assert(err, jc.ErrorIsNil) 140 c.Assert(results, jc.DeepEquals, params.DestroyMachineResults{ 141 Results: []params.DestroyMachineResult{{ 142 Info: ¶ms.DestroyMachineInfo{ 143 DestroyedUnits: []params.Entity{ 144 {"unit-foo-0"}, 145 {"unit-foo-1"}, 146 {"unit-foo-2"}, 147 }, 148 DetachedStorage: []params.Entity{ 149 {"storage-disks-0"}, 150 }, 151 DestroyedStorage: []params.Entity{ 152 {"storage-disks-1"}, 153 }, 154 }, 155 }}, 156 }) 157 } 158 159 func (s *MachineManagerSuite) TestDestroyMachineWithParams(c *gc.C) { 160 apiV4 := s.machineManagerAPIV4() 161 s.st.machines["0"] = &mockMachine{} 162 results, err := apiV4.DestroyMachineWithParams(params.DestroyMachinesParams{ 163 Keep: true, 164 Force: true, 165 MachineTags: []string{"machine-0"}, 166 }) 167 c.Assert(err, jc.ErrorIsNil) 168 m, err := s.st.Machine("0") 169 c.Assert(err, jc.ErrorIsNil) 170 c.Assert(m.(*mockMachine).keep, jc.IsTrue) 171 c.Assert(results, jc.DeepEquals, params.DestroyMachineResults{ 172 Results: []params.DestroyMachineResult{{ 173 Info: ¶ms.DestroyMachineInfo{ 174 DestroyedUnits: []params.Entity{ 175 {"unit-foo-0"}, 176 {"unit-foo-1"}, 177 {"unit-foo-2"}, 178 }, 179 DetachedStorage: []params.Entity{ 180 {"storage-disks-0"}, 181 }, 182 DestroyedStorage: []params.Entity{ 183 {"storage-disks-1"}, 184 }, 185 }, 186 }}, 187 }) 188 } 189 190 func (s *MachineManagerSuite) setupUpgradeSeries(c *gc.C) { 191 s.st.machines = map[string]*mockMachine{ 192 "0": {series: "trusty", units: []string{"foo/0", "test/0"}}, 193 "1": {series: "trusty", units: []string{"foo/1", "test/1"}}, 194 "2": {series: "centos7", units: []string{"foo/1", "test/1"}}, 195 "3": {series: "bionic", isManager: true}, 196 } 197 } 198 199 func (s *MachineManagerSuite) TestUpgradeSeriesValidateOK(c *gc.C) { 200 s.setupUpgradeSeries(c) 201 s.st.machines["0"].unitAgentState = status.Idle 202 203 apiV5 := machinemanager.MachineManagerAPIV5{MachineManagerAPI: s.api} 204 args := params.UpdateSeriesArgs{ 205 Args: []params.UpdateSeriesArg{{ 206 Entity: params.Entity{Tag: names.NewMachineTag("0").String()}, 207 Series: "xenial", 208 }}, 209 } 210 results, err := apiV5.UpgradeSeriesValidate(args) 211 c.Assert(err, jc.ErrorIsNil) 212 213 result := results.Results[0] 214 c.Assert(result.Error, gc.IsNil) 215 216 var expectedUnitNames []string 217 for _, unit := range s.st.machines["0"].Principals() { 218 expectedUnitNames = append(expectedUnitNames, unit) 219 } 220 c.Assert(result.UnitNames, gc.DeepEquals, expectedUnitNames) 221 } 222 223 func (s *MachineManagerSuite) TestUpgradeSeriesValidateIsControllerError(c *gc.C) { 224 s.setupUpgradeSeries(c) 225 apiV5 := machinemanager.MachineManagerAPIV5{MachineManagerAPI: s.api} 226 args := params.UpdateSeriesArgs{ 227 Args: []params.UpdateSeriesArg{{ 228 Entity: params.Entity{Tag: names.NewMachineTag("3").String()}, 229 }}, 230 } 231 results, err := apiV5.UpgradeSeriesValidate(args) 232 c.Assert(err, jc.ErrorIsNil) 233 234 c.Assert(results.Results[0].Error, gc.ErrorMatches, 235 "machine-3 is a controller and cannot be targeted for series upgrade") 236 } 237 238 func (s *MachineManagerSuite) TestUpgradeSeriesValidateNoSeriesError(c *gc.C) { 239 s.setupUpgradeSeries(c) 240 apiV5 := machinemanager.MachineManagerAPIV5{MachineManagerAPI: s.api} 241 args := params.UpdateSeriesArgs{ 242 Args: []params.UpdateSeriesArg{{ 243 Entity: params.Entity{Tag: names.NewMachineTag("1").String()}, 244 }}, 245 } 246 results, err := apiV5.UpgradeSeriesValidate(args) 247 c.Assert(err, jc.ErrorIsNil) 248 249 c.Assert(results.Results[0].Error, gc.ErrorMatches, "series missing from args") 250 } 251 252 func (s *MachineManagerSuite) TestUpgradeSeriesValidateNotFromUbuntuError(c *gc.C) { 253 s.setupUpgradeSeries(c) 254 apiV5 := machinemanager.MachineManagerAPIV5{MachineManagerAPI: s.api} 255 args := params.UpdateSeriesArgs{ 256 Args: []params.UpdateSeriesArg{{ 257 Entity: params.Entity{Tag: names.NewMachineTag("2").String()}, 258 Series: "bionic", 259 }}, 260 } 261 262 results, err := apiV5.UpgradeSeriesValidate(args) 263 c.Assert(err, jc.ErrorIsNil) 264 c.Assert(results.Results[0].Error, gc.ErrorMatches, 265 "machine-2 is running CentOS and is not valid for Ubuntu series upgrade") 266 } 267 268 func (s *MachineManagerSuite) TestUpgradeSeriesValidateNotToUbuntuError(c *gc.C) { 269 s.setupUpgradeSeries(c) 270 apiV5 := machinemanager.MachineManagerAPIV5{MachineManagerAPI: s.api} 271 args := params.UpdateSeriesArgs{ 272 Args: []params.UpdateSeriesArg{{ 273 Entity: params.Entity{Tag: names.NewMachineTag("1").String()}, 274 Series: "centos7", 275 }}, 276 } 277 278 results, err := apiV5.UpgradeSeriesValidate(args) 279 c.Assert(err, jc.ErrorIsNil) 280 c.Assert(results.Results[0].Error, gc.ErrorMatches, 281 `series "centos7" is from OS "CentOS" and is not a valid upgrade target`) 282 } 283 284 func (s *MachineManagerSuite) TestUpgradeSeriesValidateAlreadyRunningSeriesError(c *gc.C) { 285 s.setupUpgradeSeries(c) 286 apiV5 := machinemanager.MachineManagerAPIV5{MachineManagerAPI: s.api} 287 args := params.UpdateSeriesArgs{ 288 Args: []params.UpdateSeriesArg{{ 289 Entity: params.Entity{Tag: names.NewMachineTag("1").String()}, 290 Series: "trusty", 291 }}, 292 } 293 294 results, err := apiV5.UpgradeSeriesValidate(args) 295 c.Assert(err, jc.ErrorIsNil) 296 c.Assert(results.Results[0].Error, gc.ErrorMatches, "machine-1 is already running series trusty") 297 } 298 299 func (s *MachineManagerSuite) TestUpgradeSeriesValidateOlderSeriesError(c *gc.C) { 300 s.setupUpgradeSeries(c) 301 apiV5 := machinemanager.MachineManagerAPIV5{MachineManagerAPI: s.api} 302 args := params.UpdateSeriesArgs{ 303 Args: []params.UpdateSeriesArg{{ 304 Entity: params.Entity{Tag: names.NewMachineTag("1").String()}, 305 Series: "precise", 306 }}, 307 } 308 309 results, err := apiV5.UpgradeSeriesValidate(args) 310 c.Assert(err, jc.ErrorIsNil) 311 c.Assert(results.Results[0].Error, gc.ErrorMatches, 312 "machine machine-1 is running trusty which is a newer series than precise.") 313 } 314 315 func (s *MachineManagerSuite) TestUpgradeSeriesValidateUnitNotIdleError(c *gc.C) { 316 s.setupUpgradeSeries(c) 317 s.st.machines["0"].unitAgentState = status.Executing 318 s.st.machines["0"].unitState = status.Active 319 320 apiV5 := machinemanager.MachineManagerAPIV5{MachineManagerAPI: s.api} 321 args := params.UpdateSeriesArgs{ 322 Args: []params.UpdateSeriesArg{{ 323 Entity: params.Entity{Tag: names.NewMachineTag("0").String()}, 324 Series: "xenial", 325 }}, 326 } 327 results, err := apiV5.UpgradeSeriesValidate(args) 328 c.Assert(err, jc.ErrorIsNil) 329 c.Assert(results.Results[0].Error, gc.ErrorMatches, 330 "unit unit-foo-[0-2] is not ready to start a series upgrade; its agent status is: \"executing\" ") 331 } 332 333 func (s *MachineManagerSuite) TestUpgradeSeriesValidateUnitStatusError(c *gc.C) { 334 s.setupUpgradeSeries(c) 335 s.st.machines["0"].unitAgentState = status.Idle 336 s.st.machines["0"].unitState = status.Error 337 338 apiV5 := machinemanager.MachineManagerAPIV5{MachineManagerAPI: s.api} 339 args := params.UpdateSeriesArgs{ 340 Args: []params.UpdateSeriesArg{{ 341 Entity: params.Entity{Tag: names.NewMachineTag("0").String()}, 342 Series: "xenial", 343 }}, 344 } 345 results, err := apiV5.UpgradeSeriesValidate(args) 346 c.Assert(err, jc.ErrorIsNil) 347 c.Assert(results.Results[0].Error, gc.ErrorMatches, 348 "unit unit-foo-[0-2] is not ready to start a series upgrade; its status is: \"error\" ") 349 } 350 351 func (s *MachineManagerSuite) TestUpgradeSeriesPrepare(c *gc.C) { 352 s.setupUpgradeSeries(c) 353 s.st.machines["0"].unitAgentState = status.Idle 354 355 apiV5 := machinemanager.MachineManagerAPIV5{MachineManagerAPI: s.api} 356 machineTag := names.NewMachineTag("0") 357 result, err := apiV5.UpgradeSeriesPrepare( 358 params.UpdateSeriesArg{ 359 Entity: params.Entity{ 360 Tag: machineTag.String()}, 361 Series: "xenial", 362 }, 363 ) 364 c.Assert(err, jc.ErrorIsNil) 365 c.Assert(result.Error, gc.IsNil) 366 367 mach := s.st.machines["0"] 368 c.Assert(len(mach.Calls()), gc.Equals, 3) 369 mach.CheckCallNames(c, "Principals", "VerifyUnitsSeries", "CreateUpgradeSeriesLock") 370 mach.CheckCall(c, 2, "CreateUpgradeSeriesLock", []string{"foo/0", "test/0"}, "xenial") 371 } 372 373 func (s *MachineManagerSuite) TestUpgradeSeriesPrepareMachineNotFound(c *gc.C) { 374 apiV5 := machinemanager.MachineManagerAPIV5{MachineManagerAPI: s.api} 375 machineTag := names.NewMachineTag("76") 376 result, err := apiV5.UpgradeSeriesPrepare( 377 params.UpdateSeriesArg{ 378 Entity: params.Entity{ 379 Tag: machineTag.String()}, 380 Series: "trusty", 381 }, 382 ) 383 c.Assert(err, jc.ErrorIsNil) 384 c.Assert(result.Error, gc.ErrorMatches, "machine 76 not found") 385 } 386 387 func (s *MachineManagerSuite) TestUpgradeSeriesPrepareNotMachineTag(c *gc.C) { 388 apiV5 := machinemanager.MachineManagerAPIV5{MachineManagerAPI: s.api} 389 unitTag := names.NewUnitTag("mysql/0") 390 result, err := apiV5.UpgradeSeriesPrepare( 391 params.UpdateSeriesArg{ 392 Entity: params.Entity{ 393 Tag: unitTag.String()}, 394 Series: "trusty", 395 }, 396 ) 397 c.Assert(err, jc.ErrorIsNil) 398 c.Assert(result.Error, gc.ErrorMatches, "\"unit-mysql-0\" is not a valid machine tag") 399 } 400 401 func (s *MachineManagerSuite) TestUpgradeSeriesPreparePermissionDenied(c *gc.C) { 402 user := names.NewUserTag("fred") 403 s.setAPIUser(c, user) 404 apiV5 := machinemanager.MachineManagerAPIV5{MachineManagerAPI: s.api} 405 machineTag := names.NewMachineTag("0") 406 _, err := apiV5.UpgradeSeriesPrepare( 407 params.UpdateSeriesArg{ 408 Entity: params.Entity{ 409 Tag: machineTag.String()}, 410 Series: "xenial", 411 }, 412 ) 413 c.Assert(err, gc.ErrorMatches, "permission denied") 414 } 415 416 func (s *MachineManagerSuite) TestUpgradeSeriesPrepareBlockedChanges(c *gc.C) { 417 apiV5 := machinemanager.MachineManagerAPIV5{MachineManagerAPI: s.api} 418 s.st.blockMsg = "TestUpgradeSeriesPrepareBlockedChanges" 419 s.st.block = state.ChangeBlock 420 _, err := apiV5.UpgradeSeriesPrepare( 421 params.UpdateSeriesArg{ 422 Entity: params.Entity{ 423 Tag: names.NewMachineTag("0").String()}, 424 Series: "xenial", 425 }, 426 ) 427 c.Assert(params.IsCodeOperationBlocked(err), jc.IsTrue, gc.Commentf("error: %#v", err)) 428 c.Assert(errors.Cause(err), jc.DeepEquals, ¶ms.Error{ 429 Message: "TestUpgradeSeriesPrepareBlockedChanges", 430 Code: "operation is blocked", 431 }) 432 } 433 434 func (s *MachineManagerSuite) TestUpgradeSeriesPrepareNoSeries(c *gc.C) { 435 apiV5 := machinemanager.MachineManagerAPIV5{MachineManagerAPI: s.api} 436 result, err := apiV5.UpgradeSeriesPrepare( 437 params.UpdateSeriesArg{ 438 Entity: params.Entity{Tag: names.NewMachineTag("0").String()}, 439 }, 440 ) 441 c.Assert(err, jc.ErrorIsNil) 442 c.Assert(result, jc.DeepEquals, params.ErrorResult{ 443 Error: ¶ms.Error{ 444 Code: params.CodeBadRequest, 445 Message: `series missing from args`, 446 }, 447 }) 448 } 449 450 func (s *MachineManagerSuite) TestUpgradeSeriesPrepareIncompatibleSeries(c *gc.C) { 451 s.setupUpgradeSeries(c) 452 s.st.machines["0"].SetErrors(&state.ErrIncompatibleSeries{ 453 SeriesList: []string{"yakkety", "zesty"}, 454 Series: "xenial", 455 CharmName: "TestCharm", 456 }) 457 apiV5 := machinemanager.MachineManagerAPIV5{MachineManagerAPI: s.api} 458 result, err := apiV5.UpgradeSeriesPrepare( 459 params.UpdateSeriesArg{ 460 Entity: params.Entity{Tag: names.NewMachineTag("0").String()}, 461 Series: "xenial", 462 Force: false, 463 }, 464 ) 465 c.Assert(err, jc.ErrorIsNil) 466 c.Assert(result, jc.DeepEquals, params.ErrorResult{ 467 Error: ¶ms.Error{ 468 Code: params.CodeIncompatibleSeries, 469 Message: "series \"xenial\" not supported by charm \"TestCharm\", supported series are: yakkety, zesty", 470 }, 471 }) 472 } 473 474 func (s *MachineManagerSuite) TestUpgradeSeriesPrepareRemoveLockAfterFail(c *gc.C) { 475 // TODO managed upgrade series 476 } 477 478 func (s *MachineManagerSuite) TestUpgradeSeriesComplete(c *gc.C) { 479 s.setupUpgradeSeries(c) 480 apiV5 := machinemanager.MachineManagerAPIV5{MachineManagerAPI: s.api} 481 _, err := apiV5.UpgradeSeriesComplete( 482 params.UpdateSeriesArg{ 483 Entity: params.Entity{Tag: names.NewMachineTag("0").String()}, 484 }, 485 ) 486 c.Assert(err, jc.ErrorIsNil) 487 } 488 489 // TestIsSeriesLessThan tests a validation method which is not very complicated 490 // but complex enough to warrant being exported from an export test package for 491 // testing. 492 func (s *MachineManagerSuite) TestIsSeriesLessThan(c *gc.C) { 493 ss := series.SupportedSeries() 494 495 // get the series versions 496 vs := make([]string, 0, len(ss)) 497 for _, ser := range ss { 498 ver, err := series.SeriesVersion(ser) 499 c.Assert(err, jc.ErrorIsNil) 500 vs = append(vs, ver) 501 } 502 503 // sort the values, so the lexicographical order is determined 504 sort.Strings(vs) 505 506 // check that the IsSeriesLessThan works for all supported series 507 for i := range vs { 508 509 // We need both the series and the next series in the list. So 510 // we provide a check here to prevent going out of bounds. 511 if i+1 > len(vs)-1 { 512 break 513 } 514 515 // get the series for the specified version 516 s1, err := series.VersionSeries(vs[i]) 517 c.Assert(err, jc.ErrorIsNil) 518 s2, err := series.VersionSeries(vs[i+1]) 519 c.Assert(err, jc.ErrorIsNil) 520 521 isLessThan, err := machinemanager.IsSeriesLessThan(s1, s2) 522 c.Assert(err, jc.ErrorIsNil) 523 c.Assert(isLessThan, jc.IsTrue) 524 } 525 } 526 527 type mockState struct { 528 machinemanager.Backend 529 calls int 530 machineTemplates []state.MachineTemplate 531 machines map[string]*mockMachine 532 err error 533 blockMsg string 534 block state.BlockType 535 } 536 537 type mockVolumeAccess struct { 538 storagecommon.VolumeAccess 539 *mockState 540 } 541 542 func (st *mockVolumeAccess) StorageInstanceVolume(tag names.StorageTag) (state.Volume, error) { 543 return &mockVolume{ 544 detachable: tag.Id() == "disks/0", 545 }, nil 546 } 547 548 type mockFilesystemAccess struct { 549 storagecommon.FilesystemAccess 550 *mockState 551 } 552 553 func (st *mockState) VolumeAccess() storagecommon.VolumeAccess { 554 return &mockVolumeAccess{mockState: st} 555 } 556 557 func (st *mockState) FilesystemAccess() storagecommon.FilesystemAccess { 558 return &mockFilesystemAccess{mockState: st} 559 } 560 561 func (st *mockState) AddOneMachine(template state.MachineTemplate) (*state.Machine, error) { 562 st.calls++ 563 st.machineTemplates = append(st.machineTemplates, template) 564 m := state.Machine{} 565 return &m, st.err 566 } 567 568 func (st *mockState) GetBlockForType(t state.BlockType) (state.Block, bool, error) { 569 if st.block == t { 570 return &mockBlock{t: t, m: st.blockMsg}, true, nil 571 } else { 572 return nil, false, nil 573 } 574 } 575 576 func (st *mockState) ModelTag() names.ModelTag { 577 return names.NewModelTag("deadbeef-2f18-4fd2-967d-db9663db7bea") 578 } 579 580 func (st *mockState) Model() (machinemanager.Model, error) { 581 return &mockModel{}, nil 582 } 583 584 func (st *mockState) CloudCredential(tag names.CloudCredentialTag) (state.Credential, error) { 585 return state.Credential{}, nil 586 } 587 588 func (st *mockState) Cloud(string) (cloud.Cloud, error) { 589 return cloud.Cloud{}, nil 590 } 591 592 func (st *mockState) Machine(id string) (machinemanager.Machine, error) { 593 if m, ok := st.machines[id]; !ok { 594 return nil, errors.NotFoundf("machine %v", id) 595 } else { 596 return m, nil 597 } 598 } 599 600 func (st *mockState) StorageInstance(tag names.StorageTag) (state.StorageInstance, error) { 601 return &mockStorage{ 602 tag: tag, 603 kind: state.StorageKindBlock, 604 }, nil 605 } 606 607 func (st *mockState) UnitStorageAttachments(tag names.UnitTag) ([]state.StorageAttachment, error) { 608 if tag.Id() == "foo/0" { 609 return []state.StorageAttachment{ 610 &mockStorageAttachment{unit: tag, storage: names.NewStorageTag("disks/0")}, 611 &mockStorageAttachment{unit: tag, storage: names.NewStorageTag("disks/1")}, 612 }, nil 613 } 614 return nil, nil 615 } 616 617 type mockBlock struct { 618 state.Block 619 t state.BlockType 620 m string 621 } 622 623 func (st *mockBlock) Id() string { 624 return "id" 625 } 626 627 func (st *mockBlock) Tag() (names.Tag, error) { 628 return names.ParseTag("machine-1") 629 } 630 631 func (st *mockBlock) Type() state.BlockType { 632 return state.ChangeBlock 633 } 634 635 func (st *mockBlock) Message() string { 636 return st.m 637 } 638 639 func (st *mockBlock) ModelUUID() string { 640 return "uuid" 641 } 642 643 type mockMachine struct { 644 jtesting.Stub 645 machinemanager.Machine 646 647 keep bool 648 series string 649 units []string 650 unitAgentState status.Status 651 unitState status.Status 652 isManager bool 653 } 654 655 func (m *mockMachine) Destroy() error { 656 return nil 657 } 658 659 func (m *mockMachine) ForceDestroy() error { 660 return nil 661 } 662 663 func (m *mockMachine) Principals() []string { 664 m.MethodCall(m, "Principals") 665 return m.units 666 } 667 668 func (m *mockMachine) SetKeepInstance(keep bool) error { 669 m.keep = keep 670 return nil 671 } 672 673 func (m *mockMachine) Series() string { 674 m.MethodCall(m, "Series") 675 return m.series 676 } 677 678 func (m *mockMachine) Units() ([]machinemanager.Unit, error) { 679 return []machinemanager.Unit{ 680 &mockUnit{tag: names.NewUnitTag("foo/0")}, 681 &mockUnit{tag: names.NewUnitTag("foo/1")}, 682 &mockUnit{tag: names.NewUnitTag("foo/2")}, 683 }, nil 684 } 685 686 func (m *mockMachine) VerifyUnitsSeries(units []string, series string, force bool) ([]machinemanager.Unit, error) { 687 m.MethodCall(m, "VerifyUnitsSeries", units, series, force) 688 out := make([]machinemanager.Unit, len(m.units)) 689 for i, name := range m.units { 690 out[i] = &mockUnit{ 691 tag: names.NewUnitTag(name), 692 agentStatus: m.unitAgentState, 693 unitStatus: m.unitState, 694 } 695 } 696 return out, m.NextErr() 697 } 698 699 func (m *mockMachine) CreateUpgradeSeriesLock(unitTags []string, series string) error { 700 m.MethodCall(m, "CreateUpgradeSeriesLock", unitTags, series) 701 return m.NextErr() 702 } 703 704 func (m *mockMachine) RemoveUpgradeSeriesLock() error { 705 m.MethodCall(m, "RemoveUpgradeSeriesLock") 706 return m.NextErr() 707 } 708 709 func (m *mockMachine) CompleteUpgradeSeries() error { 710 m.MethodCall(m, "CompleteUpgradeSeries") 711 return m.NextErr() 712 } 713 714 func (m *mockMachine) IsManager() bool { 715 return m.isManager 716 } 717 718 type mockUnit struct { 719 tag names.UnitTag 720 agentStatus status.Status 721 unitStatus status.Status 722 } 723 724 func (u *mockUnit) UnitTag() names.UnitTag { 725 return u.tag 726 } 727 728 func (u *mockUnit) Name() string { 729 return u.tag.String() 730 } 731 732 func (u *mockUnit) AgentStatus() (status.StatusInfo, error) { 733 return status.StatusInfo{Status: u.agentStatus}, nil 734 } 735 736 func (u *mockUnit) Status() (status.StatusInfo, error) { 737 return status.StatusInfo{Status: u.unitStatus}, nil 738 } 739 740 func (u *mockUnit) ApplicationName() string { 741 return strings.Split(u.tag.String(), "-")[1] 742 } 743 744 type mockStorage struct { 745 state.StorageInstance 746 tag names.StorageTag 747 kind state.StorageKind 748 } 749 750 func (a *mockStorage) StorageTag() names.StorageTag { 751 return a.tag 752 } 753 754 func (a *mockStorage) Kind() state.StorageKind { 755 return a.kind 756 } 757 758 type mockStorageAttachment struct { 759 state.StorageAttachment 760 unit names.UnitTag 761 storage names.StorageTag 762 } 763 764 func (a *mockStorageAttachment) Unit() names.UnitTag { 765 return a.unit 766 } 767 768 func (a *mockStorageAttachment) StorageInstance() names.StorageTag { 769 return a.storage 770 } 771 772 type mockVolume struct { 773 state.Volume 774 detachable bool 775 } 776 777 func (v *mockVolume) Detachable() bool { 778 return v.detachable 779 } 780 781 func (s *MachineManagerSuite) machineManagerAPIV4() machinemanager.MachineManagerAPIV4 { 782 managerV5 := &machinemanager.MachineManagerAPIV5{s.api} 783 return machinemanager.MachineManagerAPIV4{managerV5} 784 }