github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/storageprovisioner/storageprovisioner_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package storageprovisioner_test 5 6 import ( 7 "sort" 8 9 "github.com/juju/errors" 10 jc "github.com/juju/testing/checkers" 11 gc "gopkg.in/check.v1" 12 "gopkg.in/juju/names.v2" 13 14 "github.com/juju/juju/apiserver/common" 15 "github.com/juju/juju/apiserver/facades/agent/storageprovisioner" 16 "github.com/juju/juju/apiserver/params" 17 apiservertesting "github.com/juju/juju/apiserver/testing" 18 "github.com/juju/juju/caas" 19 "github.com/juju/juju/core/instance" 20 "github.com/juju/juju/environs" 21 "github.com/juju/juju/environs/tags" 22 jujutesting "github.com/juju/juju/juju/testing" 23 "github.com/juju/juju/state" 24 "github.com/juju/juju/state/stateenvirons" 25 statetesting "github.com/juju/juju/state/testing" 26 "github.com/juju/juju/storage" 27 "github.com/juju/juju/storage/poolmanager" 28 "github.com/juju/juju/testing" 29 "github.com/juju/juju/testing/factory" 30 ) 31 32 var _ = gc.Suite(&iaasProvisionerSuite{}) 33 var _ = gc.Suite(&caasProvisionerSuite{}) 34 35 type iaasProvisionerSuite struct { 36 provisionerSuite 37 } 38 39 type caasProvisionerSuite struct { 40 provisionerSuite 41 } 42 43 type storageSetUp interface { 44 setupVolumes(c *gc.C) 45 setupFilesystems(c *gc.C) 46 } 47 48 type provisionerSuite struct { 49 // TODO(wallyworld) remove JujuConnSuite 50 jujutesting.JujuConnSuite 51 52 storageSetUp 53 54 resources *common.Resources 55 authorizer *apiservertesting.FakeAuthorizer 56 api *storageprovisioner.StorageProvisionerAPIv4 57 storageBackend storageprovisioner.StorageBackend 58 } 59 60 func (s *provisionerSuite) SetUpTest(c *gc.C) { 61 s.JujuConnSuite.SetUpTest(c) 62 } 63 64 func (s *iaasProvisionerSuite) SetUpTest(c *gc.C) { 65 s.provisionerSuite.SetUpTest(c) 66 s.provisionerSuite.storageSetUp = s 67 68 // Create the resource registry separately to track invocations to 69 // Register. 70 s.resources = common.NewResources() 71 s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() }) 72 73 env, err := stateenvirons.GetNewEnvironFunc(environs.New)(s.State) 74 c.Assert(err, jc.ErrorIsNil) 75 registry := stateenvirons.NewStorageProviderRegistry(env) 76 pm := poolmanager.New(state.NewStateSettings(s.State), registry) 77 78 s.authorizer = &apiservertesting.FakeAuthorizer{ 79 Tag: names.NewMachineTag("0"), 80 Controller: true, 81 } 82 backend, storageBackend, err := storageprovisioner.NewStateBackends(s.State) 83 c.Assert(err, jc.ErrorIsNil) 84 s.storageBackend = storageBackend 85 v3, err := storageprovisioner.NewStorageProvisionerAPIv3(backend, storageBackend, s.resources, s.authorizer, registry, pm) 86 c.Assert(err, jc.ErrorIsNil) 87 s.api = storageprovisioner.NewStorageProvisionerAPIv4(v3) 88 } 89 90 func (s *caasProvisionerSuite) SetUpTest(c *gc.C) { 91 s.provisionerSuite.SetUpTest(c) 92 s.provisionerSuite.storageSetUp = s 93 94 caasSt := s.Factory.MakeCAASModel(c, nil) 95 s.AddCleanup(func(_ *gc.C) { caasSt.Close() }) 96 s.State = caasSt 97 var err error 98 s.Model, err = caasSt.Model() 99 c.Assert(err, jc.ErrorIsNil) 100 101 s.Factory = factory.NewFactory(s.State, s.StatePool) 102 s.resources = common.NewResources() 103 s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() }) 104 105 broker, err := stateenvirons.GetNewCAASBrokerFunc(caas.New)(s.State) 106 c.Assert(err, jc.ErrorIsNil) 107 registry := stateenvirons.NewStorageProviderRegistry(broker) 108 pm := poolmanager.New(state.NewStateSettings(s.State), registry) 109 110 s.authorizer = &apiservertesting.FakeAuthorizer{ 111 Tag: names.NewMachineTag("0"), 112 Controller: true, 113 } 114 backend, storageBackend, err := storageprovisioner.NewStateBackends(s.State) 115 c.Assert(err, jc.ErrorIsNil) 116 s.storageBackend = storageBackend 117 v3, err := storageprovisioner.NewStorageProvisionerAPIv3(backend, storageBackend, s.resources, s.authorizer, registry, pm) 118 c.Assert(err, jc.ErrorIsNil) 119 s.api = storageprovisioner.NewStorageProvisionerAPIv4(v3) 120 } 121 122 func (s *provisionerSuite) TestNewStorageProvisionerAPINonMachine(c *gc.C) { 123 tag := names.NewUnitTag("mysql/0") 124 authorizer := &apiservertesting.FakeAuthorizer{Tag: tag} 125 backend, storageBackend, err := storageprovisioner.NewStateBackends(s.State) 126 c.Assert(err, jc.ErrorIsNil) 127 _, err = storageprovisioner.NewStorageProvisionerAPIv3(backend, storageBackend, common.NewResources(), authorizer, nil, nil) 128 c.Assert(err, gc.ErrorMatches, "permission denied") 129 } 130 131 func (s *iaasProvisionerSuite) setupVolumes(c *gc.C) { 132 s.Factory.MakeMachine(c, &factory.MachineParams{ 133 InstanceId: instance.Id("inst-id"), 134 Volumes: []state.HostVolumeParams{ 135 {Volume: state.VolumeParams{Pool: "machinescoped", Size: 1024}}, 136 {Volume: state.VolumeParams{Pool: "modelscoped", Size: 2048}}, 137 {Volume: state.VolumeParams{Pool: "modelscoped", Size: 4096}}, 138 { 139 Volume: state.VolumeParams{Pool: "modelscoped", Size: 4096}, 140 Attachment: state.VolumeAttachmentParams{ 141 ReadOnly: true, 142 }, 143 }, 144 }, 145 }) 146 // Only provision the first and third volumes. 147 err := s.storageBackend.SetVolumeInfo(names.NewVolumeTag("0/0"), state.VolumeInfo{ 148 HardwareId: "123", 149 VolumeId: "abc", 150 Size: 1024, 151 Persistent: true, 152 }) 153 c.Assert(err, jc.ErrorIsNil) 154 err = s.storageBackend.SetVolumeInfo(names.NewVolumeTag("2"), state.VolumeInfo{ 155 HardwareId: "456", 156 VolumeId: "def", 157 Size: 4096, 158 }) 159 c.Assert(err, jc.ErrorIsNil) 160 161 // Make a machine without storage for tests to use. 162 s.Factory.MakeMachine(c, nil) 163 164 // Make an unprovisioned machine with storage for tests to use. 165 // TODO(axw) extend testing/factory to allow creating unprovisioned 166 // machines. 167 _, err = s.State.AddOneMachine(state.MachineTemplate{ 168 Series: "quantal", 169 Jobs: []state.MachineJob{state.JobHostUnits}, 170 Volumes: []state.HostVolumeParams{ 171 {Volume: state.VolumeParams{Pool: "modelscoped", Size: 2048}}, 172 }, 173 }) 174 c.Assert(err, jc.ErrorIsNil) 175 } 176 177 func (s *iaasProvisionerSuite) setupFilesystems(c *gc.C) { 178 s.Factory.MakeMachine(c, &factory.MachineParams{ 179 InstanceId: instance.Id("inst-id"), 180 Filesystems: []state.HostFilesystemParams{{ 181 Filesystem: state.FilesystemParams{Pool: "machinescoped", Size: 1024}, 182 Attachment: state.FilesystemAttachmentParams{ 183 Location: "/srv", 184 ReadOnly: true, 185 }, 186 }, { 187 Filesystem: state.FilesystemParams{Pool: "modelscoped", Size: 2048}, 188 }, { 189 Filesystem: state.FilesystemParams{Pool: "modelscoped", Size: 4096}, 190 }}, 191 }) 192 193 // Only provision the first and third filesystems. 194 err := s.storageBackend.SetFilesystemInfo(names.NewFilesystemTag("0/0"), state.FilesystemInfo{ 195 FilesystemId: "abc", 196 Size: 1024, 197 }) 198 c.Assert(err, jc.ErrorIsNil) 199 err = s.storageBackend.SetFilesystemInfo(names.NewFilesystemTag("2"), state.FilesystemInfo{ 200 FilesystemId: "def", 201 Size: 4096, 202 }) 203 c.Assert(err, jc.ErrorIsNil) 204 205 // Make a machine without storage for tests to use. 206 s.Factory.MakeMachine(c, nil) 207 208 // Make an unprovisioned machine with storage for tests to use. 209 // TODO(axw) extend testing/factory to allow creating unprovisioned 210 // machines. 211 _, err = s.State.AddOneMachine(state.MachineTemplate{ 212 Series: "quantal", 213 Jobs: []state.MachineJob{state.JobHostUnits}, 214 Filesystems: []state.HostFilesystemParams{{ 215 Filesystem: state.FilesystemParams{Pool: "modelscoped", Size: 2048}, 216 }}, 217 }) 218 c.Assert(err, jc.ErrorIsNil) 219 } 220 221 func (s *caasProvisionerSuite) setupFilesystems(c *gc.C) { 222 ch := s.Factory.MakeCharm(c, &factory.CharmParams{ 223 Name: "storage-filesystem", 224 Series: "kubernetes", 225 }) 226 app := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 227 Charm: ch, 228 Name: "mariadb", 229 Storage: map[string]state.StorageConstraints{ 230 "data": {Count: 1, Size: 1024}, 231 "cache": {Count: 2, Size: 1024}, 232 }, 233 }) 234 s.Factory.MakeUnit(c, &factory.UnitParams{Application: app}) 235 236 // Only provision the first and third backing volumes. 237 err := s.storageBackend.SetVolumeInfo(names.NewVolumeTag("0"), state.VolumeInfo{ 238 HardwareId: "123", 239 VolumeId: "abc", 240 Size: 1024, 241 Persistent: true, 242 }) 243 c.Assert(err, jc.ErrorIsNil) 244 err = s.storageBackend.SetVolumeAttachmentInfo( 245 names.NewUnitTag("mariadb/0"), 246 names.NewVolumeTag("0"), 247 state.VolumeAttachmentInfo{ReadOnly: false}, 248 ) 249 c.Assert(err, jc.ErrorIsNil) 250 251 err = s.storageBackend.SetVolumeInfo(names.NewVolumeTag("2"), state.VolumeInfo{ 252 HardwareId: "456", 253 VolumeId: "def", 254 Size: 4096, 255 }) 256 c.Assert(err, jc.ErrorIsNil) 257 err = s.storageBackend.SetVolumeAttachmentInfo( 258 names.NewUnitTag("mariadb/0"), 259 names.NewVolumeTag("2"), 260 state.VolumeAttachmentInfo{ReadOnly: false}, 261 ) 262 c.Assert(err, jc.ErrorIsNil) 263 264 // Only provision the first and third filesystems. 265 err = s.storageBackend.SetFilesystemInfo(names.NewFilesystemTag("0"), state.FilesystemInfo{ 266 FilesystemId: "abc", 267 Size: 1024, 268 }) 269 c.Assert(err, jc.ErrorIsNil) 270 err = s.storageBackend.SetFilesystemInfo(names.NewFilesystemTag("2"), state.FilesystemInfo{ 271 FilesystemId: "def", 272 Size: 4096, 273 }) 274 c.Assert(err, jc.ErrorIsNil) 275 } 276 277 func (s *iaasProvisionerSuite) TestHostedVolumes(c *gc.C) { 278 // Only IAAS models support block storage right now. 279 s.setupVolumes(c) 280 s.authorizer.Controller = false 281 282 results, err := s.api.Volumes(params.Entities{ 283 Entities: []params.Entity{{"volume-0-0"}, {"volume-1"}, {"volume-42"}}, 284 }) 285 c.Assert(err, jc.ErrorIsNil) 286 c.Assert(results, gc.DeepEquals, params.VolumeResults{ 287 Results: []params.VolumeResult{ 288 {Result: params.Volume{ 289 VolumeTag: "volume-0-0", 290 Info: params.VolumeInfo{ 291 VolumeId: "abc", 292 HardwareId: "123", 293 Size: 1024, 294 Persistent: true, 295 Pool: "machinescoped", 296 }, 297 }}, 298 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 299 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 300 }, 301 }) 302 } 303 304 func (s *iaasProvisionerSuite) TestVolumesModel(c *gc.C) { 305 // Only IAAS models support block storage right now. 306 s.setupVolumes(c) 307 s.authorizer.Tag = names.NewMachineTag("2") // neither 0 nor 1 308 309 results, err := s.api.Volumes(params.Entities{ 310 Entities: []params.Entity{ 311 {"volume-0-0"}, 312 {"volume-1"}, 313 {"volume-2"}, 314 {"volume-42"}, 315 }, 316 }) 317 c.Assert(err, jc.ErrorIsNil) 318 c.Assert(results, gc.DeepEquals, params.VolumeResults{ 319 Results: []params.VolumeResult{ 320 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 321 {Error: common.ServerError(errors.NotProvisionedf(`volume "1"`))}, 322 {Result: params.Volume{ 323 VolumeTag: "volume-2", 324 Info: params.VolumeInfo{ 325 VolumeId: "def", 326 HardwareId: "456", 327 Size: 4096, 328 Pool: "modelscoped", 329 }, 330 }}, 331 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 332 }, 333 }) 334 } 335 336 func (s *provisionerSuite) TestVolumesEmptyArgs(c *gc.C) { 337 results, err := s.api.Volumes(params.Entities{}) 338 c.Assert(err, jc.ErrorIsNil) 339 c.Assert(results.Results, gc.HasLen, 0) 340 } 341 342 func (s *iaasProvisionerSuite) TestFilesystems(c *gc.C) { 343 s.setupFilesystems(c) 344 s.authorizer.Tag = names.NewMachineTag("2") // neither 0 nor 1 345 346 results, err := s.api.Filesystems(params.Entities{ 347 Entities: []params.Entity{ 348 {"filesystem-0-0"}, 349 {"filesystem-1"}, 350 {"filesystem-2"}, 351 {"filesystem-42"}, 352 }, 353 }) 354 c.Assert(err, jc.ErrorIsNil) 355 c.Assert(results, jc.DeepEquals, params.FilesystemResults{ 356 Results: []params.FilesystemResult{ 357 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 358 {Error: common.ServerError(errors.NotProvisionedf(`filesystem "1"`))}, 359 {Result: params.Filesystem{ 360 FilesystemTag: "filesystem-2", 361 Info: params.FilesystemInfo{ 362 FilesystemId: "def", 363 Size: 4096, 364 Pool: "modelscoped", 365 }, 366 }}, 367 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 368 }, 369 }) 370 } 371 372 func (s *iaasProvisionerSuite) TestVolumeAttachments(c *gc.C) { 373 // Only IAAS models support block storage right now. 374 s.setupVolumes(c) 375 s.authorizer.Controller = false 376 377 err := s.storageBackend.SetVolumeAttachmentInfo( 378 names.NewMachineTag("0"), 379 names.NewVolumeTag("0/0"), 380 state.VolumeAttachmentInfo{DeviceName: "xvdf1"}, 381 ) 382 c.Assert(err, jc.ErrorIsNil) 383 384 results, err := s.api.VolumeAttachments(params.MachineStorageIds{ 385 Ids: []params.MachineStorageId{{ 386 MachineTag: "machine-0", 387 AttachmentTag: "volume-0-0", 388 }, { 389 MachineTag: "machine-0", 390 AttachmentTag: "volume-2", // volume attachment not provisioned 391 }, { 392 MachineTag: "machine-0", 393 AttachmentTag: "volume-42", 394 }}, 395 }) 396 c.Assert(err, jc.ErrorIsNil) 397 c.Assert(results, jc.DeepEquals, params.VolumeAttachmentResults{ 398 Results: []params.VolumeAttachmentResult{ 399 {Result: params.VolumeAttachment{ 400 VolumeTag: "volume-0-0", 401 MachineTag: "machine-0", 402 Info: params.VolumeAttachmentInfo{ 403 DeviceName: "xvdf1", 404 }, 405 }}, 406 {Error: ¶ms.Error{ 407 Code: params.CodeNotProvisioned, 408 Message: `volume attachment "2" on "machine 0" not provisioned`, 409 }}, 410 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 411 }, 412 }) 413 } 414 415 func (s *iaasProvisionerSuite) TestFilesystemAttachments(c *gc.C) { 416 s.setupFilesystems(c) 417 s.authorizer.Controller = false 418 419 err := s.storageBackend.SetFilesystemAttachmentInfo( 420 names.NewMachineTag("0"), 421 names.NewFilesystemTag("0/0"), 422 state.FilesystemAttachmentInfo{ 423 MountPoint: "/srv", 424 ReadOnly: true, 425 }, 426 ) 427 c.Assert(err, jc.ErrorIsNil) 428 429 results, err := s.api.FilesystemAttachments(params.MachineStorageIds{ 430 Ids: []params.MachineStorageId{{ 431 MachineTag: "machine-0", 432 AttachmentTag: "filesystem-0-0", 433 }, { 434 MachineTag: "machine-0", 435 AttachmentTag: "filesystem-2", // filesystem attachment not provisioned 436 }, { 437 MachineTag: "machine-0", 438 AttachmentTag: "filesystem-42", 439 }}, 440 }) 441 c.Assert(err, jc.ErrorIsNil) 442 c.Assert(results, jc.DeepEquals, params.FilesystemAttachmentResults{ 443 Results: []params.FilesystemAttachmentResult{ 444 {Result: params.FilesystemAttachment{ 445 FilesystemTag: "filesystem-0-0", 446 MachineTag: "machine-0", 447 Info: params.FilesystemAttachmentInfo{ 448 MountPoint: "/srv", 449 ReadOnly: true, 450 }, 451 }}, 452 {Error: ¶ms.Error{ 453 Code: params.CodeNotProvisioned, 454 Message: `filesystem attachment "2" on "machine 0" not provisioned`, 455 }}, 456 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 457 }, 458 }) 459 } 460 461 func (s *iaasProvisionerSuite) TestVolumeParams(c *gc.C) { 462 // Only IAAS models support block storage right now. 463 s.setupVolumes(c) 464 results, err := s.api.VolumeParams(params.Entities{ 465 Entities: []params.Entity{ 466 {"volume-0-0"}, 467 {"volume-1"}, 468 {"volume-3"}, 469 {"volume-42"}, 470 }, 471 }) 472 c.Assert(err, jc.ErrorIsNil) 473 c.Assert(results, jc.DeepEquals, params.VolumeParamsResults{ 474 Results: []params.VolumeParamsResult{ 475 {Result: params.VolumeParams{ 476 VolumeTag: "volume-0-0", 477 Size: 1024, 478 Provider: "machinescoped", 479 Tags: map[string]string{ 480 tags.JujuController: testing.ControllerTag.Id(), 481 tags.JujuModel: testing.ModelTag.Id(), 482 }, 483 Attachment: ¶ms.VolumeAttachmentParams{ 484 MachineTag: "machine-0", 485 VolumeTag: "volume-0-0", 486 Provider: "machinescoped", 487 InstanceId: "inst-id", 488 }, 489 }}, 490 {Result: params.VolumeParams{ 491 VolumeTag: "volume-1", 492 Size: 2048, 493 Provider: "modelscoped", 494 Tags: map[string]string{ 495 tags.JujuController: testing.ControllerTag.Id(), 496 tags.JujuModel: testing.ModelTag.Id(), 497 }, 498 Attachment: ¶ms.VolumeAttachmentParams{ 499 MachineTag: "machine-0", 500 VolumeTag: "volume-1", 501 Provider: "modelscoped", 502 InstanceId: "inst-id", 503 }, 504 }}, 505 {Result: params.VolumeParams{ 506 VolumeTag: "volume-3", 507 Size: 4096, 508 Provider: "modelscoped", 509 Tags: map[string]string{ 510 tags.JujuController: testing.ControllerTag.Id(), 511 tags.JujuModel: testing.ModelTag.Id(), 512 }, 513 Attachment: ¶ms.VolumeAttachmentParams{ 514 MachineTag: "machine-0", 515 VolumeTag: "volume-3", 516 Provider: "modelscoped", 517 InstanceId: "inst-id", 518 ReadOnly: true, 519 }, 520 }}, 521 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 522 }, 523 }) 524 } 525 526 func (s *provisionerSuite) TestVolumeParamsEmptyArgs(c *gc.C) { 527 results, err := s.api.VolumeParams(params.Entities{}) 528 c.Assert(err, jc.ErrorIsNil) 529 c.Assert(results.Results, gc.HasLen, 0) 530 } 531 532 func (s *iaasProvisionerSuite) TestRemoveVolumeParams(c *gc.C) { 533 // Only IAAS models support block storage right now. 534 s.setupVolumes(c) 535 536 // Deploy an application that will create a storage instance, 537 // so we can release the storage and show the effects on the 538 // RemoveVolumeParams. 539 application := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 540 Charm: s.Factory.MakeCharm(c, &factory.CharmParams{ 541 Name: "storage-block", 542 }), 543 Storage: map[string]state.StorageConstraints{ 544 "data": { 545 Count: 1, 546 Size: 1, 547 Pool: "modelscoped", 548 }, 549 }, 550 }) 551 unit := s.Factory.MakeUnit(c, &factory.UnitParams{ 552 Application: application, 553 }) 554 storage, err := s.storageBackend.AllStorageInstances() 555 c.Assert(err, jc.ErrorIsNil) 556 c.Assert(storage, gc.HasLen, 1) 557 storageVolume, err := s.storageBackend.StorageInstanceVolume(storage[0].StorageTag()) 558 c.Assert(err, jc.ErrorIsNil) 559 err = s.storageBackend.SetVolumeInfo(storageVolume.VolumeTag(), state.VolumeInfo{ 560 VolumeId: "zing", 561 Size: 1, 562 Persistent: true, 563 }) 564 c.Assert(err, jc.ErrorIsNil) 565 566 // Make volumes 0/0 and 3 Dead. 567 for _, volumeId := range []string{"0/0", "3"} { 568 volumeTag := names.NewVolumeTag(volumeId) 569 machineTag := names.NewMachineTag("0") 570 err = s.storageBackend.DestroyVolume(volumeTag) 571 c.Assert(err, jc.ErrorIsNil) 572 err = s.storageBackend.DetachVolume(machineTag, volumeTag) 573 c.Assert(err, jc.ErrorIsNil) 574 err = s.storageBackend.RemoveVolumeAttachment(machineTag, volumeTag) 575 c.Assert(err, jc.ErrorIsNil) 576 } 577 578 // Make the "data" storage volume Dead, releasing. 579 err = unit.Destroy() 580 c.Assert(err, jc.ErrorIsNil) 581 err = s.storageBackend.ReleaseStorageInstance(storage[0].StorageTag(), true) 582 c.Assert(err, jc.ErrorIsNil) 583 err = s.storageBackend.DetachStorage(storage[0].StorageTag(), unit.UnitTag()) 584 c.Assert(err, jc.ErrorIsNil) 585 unitMachineId, err := unit.AssignedMachineId() 586 c.Assert(err, jc.ErrorIsNil) 587 unitMachineTag := names.NewMachineTag(unitMachineId) 588 err = s.storageBackend.DetachVolume(unitMachineTag, storageVolume.VolumeTag()) 589 c.Assert(err, jc.ErrorIsNil) 590 err = s.storageBackend.RemoveVolumeAttachment(unitMachineTag, storageVolume.VolumeTag()) 591 c.Assert(err, jc.ErrorIsNil) 592 593 results, err := s.api.RemoveVolumeParams(params.Entities{ 594 Entities: []params.Entity{ 595 {"volume-0-0"}, 596 {storageVolume.Tag().String()}, 597 {"volume-1"}, 598 {"volume-3"}, 599 {"volume-42"}, 600 }, 601 }) 602 c.Assert(err, jc.ErrorIsNil) 603 c.Assert(results, jc.DeepEquals, params.RemoveVolumeParamsResults{ 604 Results: []params.RemoveVolumeParamsResult{{ 605 Result: params.RemoveVolumeParams{ 606 Provider: "machinescoped", 607 VolumeId: "abc", 608 Destroy: true, 609 }, 610 }, { 611 Result: params.RemoveVolumeParams{ 612 Provider: "modelscoped", 613 VolumeId: "zing", 614 Destroy: false, 615 }, 616 }, { 617 Error: ¶ms.Error{Message: `volume 1 is not dead (alive)`}, 618 }, { 619 Error: ¶ms.Error{Message: `volume "3" not provisioned`, Code: "not provisioned"}, 620 }, { 621 Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}, 622 }}, 623 }) 624 } 625 626 func (s *iaasProvisionerSuite) TestFilesystemParams(c *gc.C) { 627 s.setupFilesystems(c) 628 results, err := s.api.FilesystemParams(params.Entities{ 629 Entities: []params.Entity{{"filesystem-0-0"}, {"filesystem-1"}, {"filesystem-42"}}, 630 }) 631 c.Assert(err, jc.ErrorIsNil) 632 c.Assert(results, jc.DeepEquals, params.FilesystemParamsResults{ 633 Results: []params.FilesystemParamsResult{ 634 {Result: params.FilesystemParams{ 635 FilesystemTag: "filesystem-0-0", 636 Size: 1024, 637 Provider: "machinescoped", 638 Tags: map[string]string{ 639 tags.JujuController: testing.ControllerTag.Id(), 640 tags.JujuModel: testing.ModelTag.Id(), 641 }, 642 }}, 643 {Result: params.FilesystemParams{ 644 FilesystemTag: "filesystem-1", 645 Size: 2048, 646 Provider: "modelscoped", 647 Tags: map[string]string{ 648 tags.JujuController: testing.ControllerTag.Id(), 649 tags.JujuModel: testing.ModelTag.Id(), 650 }, 651 }}, 652 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 653 }, 654 }) 655 } 656 657 func (s *iaasProvisionerSuite) TestRemoveFilesystemParams(c *gc.C) { 658 s.setupFilesystems(c) 659 660 // Deploy an application that will create a storage instance, 661 // so we can release the storage and show the effects on the 662 // RemoveFilesystemParams. 663 application := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 664 Charm: s.Factory.MakeCharm(c, &factory.CharmParams{ 665 Name: "storage-filesystem", 666 }), 667 Storage: map[string]state.StorageConstraints{ 668 "data": { 669 Count: 1, 670 Size: 1, 671 Pool: "modelscoped", 672 }, 673 }, 674 }) 675 unit := s.Factory.MakeUnit(c, &factory.UnitParams{ 676 Application: application, 677 }) 678 storage, err := s.storageBackend.AllStorageInstances() 679 c.Assert(err, jc.ErrorIsNil) 680 c.Assert(storage, gc.HasLen, 1) 681 storageFilesystem, err := s.storageBackend.StorageInstanceFilesystem(storage[0].StorageTag()) 682 c.Assert(err, jc.ErrorIsNil) 683 err = s.storageBackend.SetFilesystemInfo(storageFilesystem.FilesystemTag(), state.FilesystemInfo{ 684 FilesystemId: "zing", 685 Size: 1, 686 }) 687 c.Assert(err, jc.ErrorIsNil) 688 689 // Make filesystems 0/0 and 1 Dead. 690 for _, filesystemId := range []string{"0/0", "1"} { 691 filesystemTag := names.NewFilesystemTag(filesystemId) 692 machineTag := names.NewMachineTag("0") 693 err = s.storageBackend.DestroyFilesystem(filesystemTag) 694 c.Assert(err, jc.ErrorIsNil) 695 err = s.storageBackend.DetachFilesystem(machineTag, filesystemTag) 696 c.Assert(err, jc.ErrorIsNil) 697 err = s.storageBackend.RemoveFilesystemAttachment(machineTag, filesystemTag) 698 c.Assert(err, jc.ErrorIsNil) 699 } 700 701 // Make the "data" storage filesystem Dead, releasing. 702 err = unit.Destroy() 703 c.Assert(err, jc.ErrorIsNil) 704 err = s.storageBackend.ReleaseStorageInstance(storage[0].StorageTag(), true) 705 c.Assert(err, jc.ErrorIsNil) 706 err = s.storageBackend.DetachStorage(storage[0].StorageTag(), unit.UnitTag()) 707 c.Assert(err, jc.ErrorIsNil) 708 unitMachineId, err := unit.AssignedMachineId() 709 c.Assert(err, jc.ErrorIsNil) 710 unitMachineTag := names.NewMachineTag(unitMachineId) 711 err = s.storageBackend.DetachFilesystem(unitMachineTag, storageFilesystem.FilesystemTag()) 712 c.Assert(err, jc.ErrorIsNil) 713 err = s.storageBackend.RemoveFilesystemAttachment(unitMachineTag, storageFilesystem.FilesystemTag()) 714 c.Assert(err, jc.ErrorIsNil) 715 716 results, err := s.api.RemoveFilesystemParams(params.Entities{ 717 Entities: []params.Entity{ 718 {"filesystem-0-0"}, 719 {storageFilesystem.Tag().String()}, 720 {"filesystem-1"}, 721 {"filesystem-2"}, 722 {"filesystem-42"}, 723 }, 724 }) 725 c.Assert(err, jc.ErrorIsNil) 726 c.Assert(results, jc.DeepEquals, params.RemoveFilesystemParamsResults{ 727 Results: []params.RemoveFilesystemParamsResult{{ 728 Result: params.RemoveFilesystemParams{ 729 Provider: "machinescoped", 730 FilesystemId: "abc", 731 Destroy: true, 732 }, 733 }, { 734 Result: params.RemoveFilesystemParams{ 735 Provider: "modelscoped", 736 FilesystemId: "zing", 737 Destroy: false, 738 }, 739 }, { 740 Error: ¶ms.Error{Message: `filesystem "1" not provisioned`, Code: "not provisioned"}, 741 }, { 742 Error: ¶ms.Error{Message: `filesystem 2 is not dead (alive)`}, 743 }, { 744 Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}, 745 }}, 746 }) 747 } 748 749 func (s *iaasProvisionerSuite) TestVolumeAttachmentParams(c *gc.C) { 750 // Only IAAS models support block storage right now. 751 s.setupVolumes(c) 752 753 err := s.storageBackend.SetVolumeInfo(names.NewVolumeTag("3"), state.VolumeInfo{ 754 HardwareId: "123", 755 VolumeId: "xyz", 756 Size: 1024, 757 }) 758 c.Assert(err, jc.ErrorIsNil) 759 760 err = s.storageBackend.SetVolumeAttachmentInfo( 761 names.NewMachineTag("0"), 762 names.NewVolumeTag("3"), 763 state.VolumeAttachmentInfo{ 764 DeviceName: "xvdf1", 765 ReadOnly: true, 766 }, 767 ) 768 c.Assert(err, jc.ErrorIsNil) 769 770 results, err := s.api.VolumeAttachmentParams(params.MachineStorageIds{ 771 Ids: []params.MachineStorageId{{ 772 MachineTag: "machine-0", 773 AttachmentTag: "volume-0-0", 774 }, { 775 MachineTag: "machine-0", 776 AttachmentTag: "volume-1", 777 }, { 778 MachineTag: "machine-0", 779 AttachmentTag: "volume-3", 780 }, { 781 MachineTag: "machine-2", 782 AttachmentTag: "volume-4", 783 }, { 784 MachineTag: "machine-0", 785 AttachmentTag: "volume-42", 786 }}, 787 }) 788 c.Assert(err, jc.ErrorIsNil) 789 c.Assert(results, jc.DeepEquals, params.VolumeAttachmentParamsResults{ 790 Results: []params.VolumeAttachmentParamsResult{ 791 {Result: params.VolumeAttachmentParams{ 792 MachineTag: "machine-0", 793 VolumeTag: "volume-0-0", 794 InstanceId: "inst-id", 795 VolumeId: "abc", 796 Provider: "machinescoped", 797 }}, 798 {Result: params.VolumeAttachmentParams{ 799 MachineTag: "machine-0", 800 VolumeTag: "volume-1", 801 InstanceId: "inst-id", 802 Provider: "modelscoped", 803 }}, 804 {Result: params.VolumeAttachmentParams{ 805 MachineTag: "machine-0", 806 VolumeTag: "volume-3", 807 InstanceId: "inst-id", 808 VolumeId: "xyz", 809 Provider: "modelscoped", 810 ReadOnly: true, 811 }}, 812 {Result: params.VolumeAttachmentParams{ 813 MachineTag: "machine-2", 814 VolumeTag: "volume-4", 815 Provider: "modelscoped", 816 }}, 817 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 818 }, 819 }) 820 } 821 822 func (s *iaasProvisionerSuite) TestFilesystemAttachmentParams(c *gc.C) { 823 s.setupFilesystems(c) 824 825 err := s.storageBackend.SetFilesystemInfo(names.NewFilesystemTag("1"), state.FilesystemInfo{ 826 FilesystemId: "fsid", 827 Size: 1024, 828 }) 829 c.Assert(err, jc.ErrorIsNil) 830 831 err = s.storageBackend.SetFilesystemAttachmentInfo( 832 names.NewMachineTag("0"), 833 names.NewFilesystemTag("1"), 834 state.FilesystemAttachmentInfo{ 835 MountPoint: "/in/the/place", 836 }, 837 ) 838 c.Assert(err, jc.ErrorIsNil) 839 840 results, err := s.api.FilesystemAttachmentParams(params.MachineStorageIds{ 841 Ids: []params.MachineStorageId{{ 842 MachineTag: "machine-0", 843 AttachmentTag: "filesystem-0-0", 844 }, { 845 MachineTag: "machine-0", 846 AttachmentTag: "filesystem-1", 847 }, { 848 MachineTag: "machine-2", 849 AttachmentTag: "filesystem-3", 850 }, { 851 MachineTag: "machine-0", 852 AttachmentTag: "filesystem-42", 853 }}, 854 }) 855 c.Assert(err, jc.ErrorIsNil) 856 c.Assert(results, jc.DeepEquals, params.FilesystemAttachmentParamsResults{ 857 Results: []params.FilesystemAttachmentParamsResult{ 858 {Result: params.FilesystemAttachmentParams{ 859 MachineTag: "machine-0", 860 FilesystemTag: "filesystem-0-0", 861 InstanceId: "inst-id", 862 FilesystemId: "abc", 863 Provider: "machinescoped", 864 MountPoint: "/srv", 865 ReadOnly: true, 866 }}, 867 {Result: params.FilesystemAttachmentParams{ 868 MachineTag: "machine-0", 869 FilesystemTag: "filesystem-1", 870 InstanceId: "inst-id", 871 FilesystemId: "fsid", 872 Provider: "modelscoped", 873 MountPoint: "/in/the/place", 874 }}, 875 {Result: params.FilesystemAttachmentParams{ 876 MachineTag: "machine-2", 877 FilesystemTag: "filesystem-3", 878 Provider: "modelscoped", 879 }}, 880 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 881 }, 882 }) 883 } 884 885 func (s *iaasProvisionerSuite) TestSetVolumeAttachmentInfo(c *gc.C) { 886 // Only IAAS models support block storage right now. 887 s.setupVolumes(c) 888 889 err := s.storageBackend.SetVolumeInfo(names.NewVolumeTag("4"), state.VolumeInfo{ 890 VolumeId: "whatever", 891 Size: 1024, 892 }) 893 c.Assert(err, jc.ErrorIsNil) 894 895 results, err := s.api.SetVolumeAttachmentInfo(params.VolumeAttachments{ 896 VolumeAttachments: []params.VolumeAttachment{{ 897 MachineTag: "machine-0", 898 VolumeTag: "volume-0-0", 899 Info: params.VolumeAttachmentInfo{ 900 DeviceName: "sda", 901 ReadOnly: true, 902 }, 903 }, { 904 MachineTag: "machine-0", 905 VolumeTag: "volume-1", 906 Info: params.VolumeAttachmentInfo{ 907 DeviceName: "sdb", 908 }, 909 }, { 910 MachineTag: "machine-2", 911 VolumeTag: "volume-4", 912 Info: params.VolumeAttachmentInfo{ 913 DeviceName: "sdc", 914 }, 915 }, { 916 MachineTag: "machine-0", 917 VolumeTag: "volume-42", 918 Info: params.VolumeAttachmentInfo{ 919 DeviceName: "sdd", 920 }, 921 }}, 922 }) 923 c.Assert(err, jc.ErrorIsNil) 924 c.Assert(results, jc.DeepEquals, params.ErrorResults{ 925 Results: []params.ErrorResult{ 926 {}, 927 {Error: ¶ms.Error{Message: `cannot set info for volume attachment 1:0: volume "1" not provisioned`, Code: "not provisioned"}}, 928 {Error: ¶ms.Error{Message: `cannot set info for volume attachment 4:2: machine 2 not provisioned`, Code: "not provisioned"}}, 929 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 930 }, 931 }) 932 } 933 934 func (s *iaasProvisionerSuite) TestSetFilesystemAttachmentInfo(c *gc.C) { 935 s.setupFilesystems(c) 936 937 err := s.storageBackend.SetFilesystemInfo(names.NewFilesystemTag("3"), state.FilesystemInfo{ 938 FilesystemId: "whatever", 939 Size: 1024, 940 }) 941 c.Assert(err, jc.ErrorIsNil) 942 943 results, err := s.api.SetFilesystemAttachmentInfo(params.FilesystemAttachments{ 944 FilesystemAttachments: []params.FilesystemAttachment{{ 945 MachineTag: "machine-0", 946 FilesystemTag: "filesystem-0-0", 947 Info: params.FilesystemAttachmentInfo{ 948 MountPoint: "/srv/a", 949 ReadOnly: true, 950 }, 951 }, { 952 MachineTag: "machine-0", 953 FilesystemTag: "filesystem-1", 954 Info: params.FilesystemAttachmentInfo{ 955 MountPoint: "/srv/b", 956 }, 957 }, { 958 MachineTag: "machine-2", 959 FilesystemTag: "filesystem-3", 960 Info: params.FilesystemAttachmentInfo{ 961 MountPoint: "/srv/c", 962 }, 963 }, { 964 MachineTag: "machine-0", 965 FilesystemTag: "filesystem-42", 966 Info: params.FilesystemAttachmentInfo{ 967 MountPoint: "/srv/d", 968 }, 969 }}, 970 }) 971 c.Assert(err, jc.ErrorIsNil) 972 c.Assert(results, jc.DeepEquals, params.ErrorResults{ 973 Results: []params.ErrorResult{ 974 {}, 975 {Error: ¶ms.Error{Message: `cannot set info for filesystem attachment 1:0: filesystem "1" not provisioned`, Code: "not provisioned"}}, 976 {Error: ¶ms.Error{Message: `cannot set info for filesystem attachment 3:2: machine 2 not provisioned`, Code: "not provisioned"}}, 977 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 978 }, 979 }) 980 } 981 982 func (s *caasProvisionerSuite) TestWatchApplications(c *gc.C) { 983 ch := s.Factory.MakeCharm(c, &factory.CharmParams{ 984 Name: "storage-filesystem", 985 Series: "kubernetes", 986 }) 987 s.Factory.MakeApplication(c, &factory.ApplicationParams{ 988 Charm: ch, 989 Name: "mariadb", 990 Storage: map[string]state.StorageConstraints{ 991 "data": {Count: 1, Size: 1024}, 992 }, 993 }) 994 995 result, err := s.api.WatchApplications() 996 c.Assert(err, jc.ErrorIsNil) 997 c.Assert(result.StringsWatcherId, gc.Equals, "1") 998 c.Assert(result.Changes, jc.DeepEquals, []string{"mariadb"}) 999 1000 w := s.resources.Get("1").(state.StringsWatcher) 1001 s.Factory.MakeApplication(c, &factory.ApplicationParams{ 1002 Charm: ch, 1003 Name: "mysql", 1004 Storage: map[string]state.StorageConstraints{ 1005 "data": {Count: 1, Size: 1024}, 1006 }, 1007 }) 1008 wc := statetesting.NewStringsWatcherC(c, s.State, w) 1009 wc.AssertChange("mysql") 1010 } 1011 1012 func (s *iaasProvisionerSuite) TestWatchVolumes(c *gc.C) { 1013 // Only IAAS models support block storage right now. 1014 s.setupVolumes(c) 1015 s.Factory.MakeMachine(c, nil) 1016 c.Assert(s.resources.Count(), gc.Equals, 0) 1017 1018 args := params.Entities{Entities: []params.Entity{ 1019 {"machine-0"}, 1020 {s.Model.ModelTag().String()}, 1021 {"environ-adb650da-b77b-4ee8-9cbb-d57a9a592847"}, 1022 {"machine-1"}, 1023 {"machine-42"}}, 1024 } 1025 result, err := s.api.WatchVolumes(args) 1026 c.Assert(err, jc.ErrorIsNil) 1027 sort.Strings(result.Results[1].Changes) 1028 c.Assert(result, jc.DeepEquals, params.StringsWatchResults{ 1029 Results: []params.StringsWatchResult{ 1030 {StringsWatcherId: "1", Changes: []string{"0/0"}}, 1031 {StringsWatcherId: "2", Changes: []string{"1", "2", "3", "4"}}, 1032 {Error: apiservertesting.ErrUnauthorized}, 1033 {Error: apiservertesting.ErrUnauthorized}, 1034 {Error: apiservertesting.ErrUnauthorized}, 1035 }, 1036 }) 1037 1038 // Verify the resources were registered and stop them when done. 1039 c.Assert(s.resources.Count(), gc.Equals, 2) 1040 v0Watcher := s.resources.Get("1") 1041 defer statetesting.AssertStop(c, v0Watcher) 1042 v1Watcher := s.resources.Get("2") 1043 defer statetesting.AssertStop(c, v1Watcher) 1044 1045 // Check that the Watch has consumed the initial events ("returned" in 1046 // the Watch call) 1047 wc := statetesting.NewStringsWatcherC(c, s.State, v0Watcher.(state.StringsWatcher)) 1048 wc.AssertNoChange() 1049 wc = statetesting.NewStringsWatcherC(c, s.State, v1Watcher.(state.StringsWatcher)) 1050 wc.AssertNoChange() 1051 } 1052 1053 func (s *iaasProvisionerSuite) TestWatchVolumeAttachments(c *gc.C) { 1054 // Only IAAS models support block storage right now. 1055 s.setupVolumes(c) 1056 s.Factory.MakeMachine(c, nil) 1057 c.Assert(s.resources.Count(), gc.Equals, 0) 1058 1059 args := params.Entities{Entities: []params.Entity{ 1060 {"machine-0"}, 1061 {s.Model.ModelTag().String()}, 1062 {"environ-adb650da-b77b-4ee8-9cbb-d57a9a592847"}, 1063 {"machine-1"}, 1064 {"machine-42"}}, 1065 } 1066 result, err := s.api.WatchVolumeAttachments(args) 1067 c.Assert(err, jc.ErrorIsNil) 1068 sort.Sort(byMachineAndEntity(result.Results[0].Changes)) 1069 sort.Sort(byMachineAndEntity(result.Results[1].Changes)) 1070 c.Assert(result, jc.DeepEquals, params.MachineStorageIdsWatchResults{ 1071 Results: []params.MachineStorageIdsWatchResult{ 1072 { 1073 MachineStorageIdsWatcherId: "1", 1074 Changes: []params.MachineStorageId{{ 1075 MachineTag: "machine-0", 1076 AttachmentTag: "volume-0-0", 1077 }}, 1078 }, 1079 { 1080 MachineStorageIdsWatcherId: "2", 1081 Changes: []params.MachineStorageId{{ 1082 MachineTag: "machine-0", 1083 AttachmentTag: "volume-1", 1084 }, { 1085 MachineTag: "machine-0", 1086 AttachmentTag: "volume-2", 1087 }, { 1088 MachineTag: "machine-0", 1089 AttachmentTag: "volume-3", 1090 }, { 1091 MachineTag: "machine-2", 1092 AttachmentTag: "volume-4", 1093 }}, 1094 }, 1095 {Error: apiservertesting.ErrUnauthorized}, 1096 {Error: apiservertesting.ErrUnauthorized}, 1097 {Error: apiservertesting.ErrUnauthorized}, 1098 }, 1099 }) 1100 1101 // Verify the resources were registered and stop them when done. 1102 c.Assert(s.resources.Count(), gc.Equals, 2) 1103 v0Watcher := s.resources.Get("1") 1104 defer statetesting.AssertStop(c, v0Watcher) 1105 v1Watcher := s.resources.Get("2") 1106 defer statetesting.AssertStop(c, v1Watcher) 1107 1108 // Check that the Watch has consumed the initial events ("returned" in 1109 // the Watch call) 1110 wc := statetesting.NewStringsWatcherC(c, s.State, v0Watcher.(state.StringsWatcher)) 1111 wc.AssertNoChange() 1112 wc = statetesting.NewStringsWatcherC(c, s.State, v1Watcher.(state.StringsWatcher)) 1113 wc.AssertNoChange() 1114 } 1115 1116 func (s *iaasProvisionerSuite) TestWatchFilesystems(c *gc.C) { 1117 s.setupFilesystems(c) 1118 c.Assert(s.resources.Count(), gc.Equals, 0) 1119 1120 args := params.Entities{Entities: []params.Entity{ 1121 {"machine-0"}, 1122 {s.Model.ModelTag().String()}, 1123 {"environ-adb650da-b77b-4ee8-9cbb-d57a9a592847"}, 1124 {"machine-1"}, 1125 {"machine-42"}}, 1126 } 1127 result, err := s.api.WatchFilesystems(args) 1128 c.Assert(err, jc.ErrorIsNil) 1129 sort.Strings(result.Results[1].Changes) 1130 c.Assert(result, jc.DeepEquals, params.StringsWatchResults{ 1131 Results: []params.StringsWatchResult{ 1132 { 1133 StringsWatcherId: "1", 1134 Changes: []string{"0/0"}, 1135 }, 1136 { 1137 StringsWatcherId: "2", 1138 Changes: []string{"1", "2", "3"}, 1139 }, 1140 {Error: apiservertesting.ErrUnauthorized}, 1141 {Error: apiservertesting.ErrUnauthorized}, 1142 {Error: apiservertesting.ErrUnauthorized}, 1143 }, 1144 }) 1145 1146 // Verify the resources were registered and stop them when done. 1147 c.Assert(s.resources.Count(), gc.Equals, 2) 1148 v0Watcher := s.resources.Get("1") 1149 defer statetesting.AssertStop(c, v0Watcher) 1150 v1Watcher := s.resources.Get("2") 1151 defer statetesting.AssertStop(c, v1Watcher) 1152 1153 // Check that the Watch has consumed the initial events ("returned" in 1154 // the Watch call) 1155 wc := statetesting.NewStringsWatcherC(c, s.State, v0Watcher.(state.StringsWatcher)) 1156 wc.AssertNoChange() 1157 wc = statetesting.NewStringsWatcherC(c, s.State, v1Watcher.(state.StringsWatcher)) 1158 wc.AssertNoChange() 1159 } 1160 1161 func (s *iaasProvisionerSuite) TestWatchFilesystemAttachments(c *gc.C) { 1162 s.setupFilesystems(c) 1163 c.Assert(s.resources.Count(), gc.Equals, 0) 1164 1165 args := params.Entities{Entities: []params.Entity{ 1166 {"machine-0"}, 1167 {s.Model.ModelTag().String()}, 1168 {"environ-adb650da-b77b-4ee8-9cbb-d57a9a592847"}, 1169 {"machine-1"}, 1170 {"machine-42"}}, 1171 } 1172 result, err := s.api.WatchFilesystemAttachments(args) 1173 c.Assert(err, jc.ErrorIsNil) 1174 sort.Sort(byMachineAndEntity(result.Results[0].Changes)) 1175 sort.Sort(byMachineAndEntity(result.Results[1].Changes)) 1176 c.Assert(result, jc.DeepEquals, params.MachineStorageIdsWatchResults{ 1177 Results: []params.MachineStorageIdsWatchResult{ 1178 { 1179 MachineStorageIdsWatcherId: "1", 1180 Changes: []params.MachineStorageId{{ 1181 MachineTag: "machine-0", 1182 AttachmentTag: "filesystem-0-0", 1183 }}, 1184 }, 1185 { 1186 MachineStorageIdsWatcherId: "2", 1187 Changes: []params.MachineStorageId{{ 1188 MachineTag: "machine-0", 1189 AttachmentTag: "filesystem-1", 1190 }, { 1191 MachineTag: "machine-0", 1192 AttachmentTag: "filesystem-2", 1193 }, { 1194 MachineTag: "machine-2", 1195 AttachmentTag: "filesystem-3", 1196 }}, 1197 }, 1198 {Error: apiservertesting.ErrUnauthorized}, 1199 {Error: apiservertesting.ErrUnauthorized}, 1200 {Error: apiservertesting.ErrUnauthorized}, 1201 }, 1202 }) 1203 1204 // Verify the resources were registered and stop them when done. 1205 c.Assert(s.resources.Count(), gc.Equals, 2) 1206 v0Watcher := s.resources.Get("1") 1207 defer statetesting.AssertStop(c, v0Watcher) 1208 v1Watcher := s.resources.Get("2") 1209 defer statetesting.AssertStop(c, v1Watcher) 1210 1211 // Check that the Watch has consumed the initial events ("returned" in 1212 // the Watch call) 1213 wc := statetesting.NewStringsWatcherC(c, s.State, v0Watcher.(state.StringsWatcher)) 1214 wc.AssertNoChange() 1215 wc = statetesting.NewStringsWatcherC(c, s.State, v1Watcher.(state.StringsWatcher)) 1216 wc.AssertNoChange() 1217 } 1218 1219 func (s *iaasProvisionerSuite) TestWatchBlockDevices(c *gc.C) { 1220 s.Factory.MakeMachine(c, nil) 1221 c.Assert(s.resources.Count(), gc.Equals, 0) 1222 1223 args := params.Entities{Entities: []params.Entity{ 1224 {"machine-0"}, 1225 {"application-mysql"}, 1226 {"machine-1"}, 1227 {"machine-42"}}, 1228 } 1229 results, err := s.api.WatchBlockDevices(args) 1230 c.Assert(err, jc.ErrorIsNil) 1231 c.Assert(results, jc.DeepEquals, params.NotifyWatchResults{ 1232 Results: []params.NotifyWatchResult{ 1233 {NotifyWatcherId: "1"}, 1234 {Error: ¶ms.Error{Message: `"application-mysql" is not a valid machine tag`}}, 1235 {Error: apiservertesting.ErrUnauthorized}, 1236 {Error: apiservertesting.ErrUnauthorized}, 1237 }, 1238 }) 1239 1240 // Verify the resources were registered and stop them when done. 1241 c.Assert(s.resources.Count(), gc.Equals, 1) 1242 watcher := s.resources.Get("1") 1243 defer statetesting.AssertStop(c, watcher) 1244 1245 // Check that the Watch has consumed the initial event. 1246 wc := statetesting.NewNotifyWatcherC(c, s.State, watcher.(state.NotifyWatcher)) 1247 wc.AssertNoChange() 1248 1249 m, err := s.State.Machine("0") 1250 c.Assert(err, jc.ErrorIsNil) 1251 err = m.SetMachineBlockDevices(state.BlockDeviceInfo{ 1252 DeviceName: "sda", 1253 Size: 123, 1254 }) 1255 c.Assert(err, jc.ErrorIsNil) 1256 wc.AssertOneChange() 1257 } 1258 1259 func (s *iaasProvisionerSuite) TestVolumeBlockDevices(c *gc.C) { 1260 s.setupVolumes(c) 1261 s.Factory.MakeMachine(c, nil) 1262 1263 err := s.storageBackend.SetVolumeAttachmentInfo( 1264 names.NewMachineTag("0"), 1265 names.NewVolumeTag("0/0"), 1266 state.VolumeAttachmentInfo{}, 1267 ) 1268 c.Assert(err, jc.ErrorIsNil) 1269 1270 machine0, err := s.State.Machine("0") 1271 c.Assert(err, jc.ErrorIsNil) 1272 err = machine0.SetMachineBlockDevices(state.BlockDeviceInfo{ 1273 DeviceName: "sda", 1274 Size: 123, 1275 HardwareId: "123", // matches volume-0/0 1276 }) 1277 c.Assert(err, jc.ErrorIsNil) 1278 1279 args := params.MachineStorageIds{Ids: []params.MachineStorageId{ 1280 {MachineTag: "machine-0", AttachmentTag: "volume-0-0"}, 1281 {MachineTag: "machine-0", AttachmentTag: "volume-0-1"}, 1282 {MachineTag: "machine-0", AttachmentTag: "volume-0-2"}, 1283 {MachineTag: "machine-1", AttachmentTag: "volume-1"}, 1284 {MachineTag: "machine-42", AttachmentTag: "volume-42"}, 1285 {MachineTag: "application-mysql", AttachmentTag: "volume-1"}, 1286 }} 1287 results, err := s.api.VolumeBlockDevices(args) 1288 c.Assert(err, jc.ErrorIsNil) 1289 c.Assert(results, jc.DeepEquals, params.BlockDeviceResults{ 1290 Results: []params.BlockDeviceResult{ 1291 {Result: storage.BlockDevice{ 1292 DeviceName: "sda", 1293 Size: 123, 1294 HardwareId: "123", 1295 }}, 1296 {Error: apiservertesting.ErrUnauthorized}, 1297 {Error: apiservertesting.ErrUnauthorized}, 1298 {Error: apiservertesting.ErrUnauthorized}, 1299 {Error: apiservertesting.ErrUnauthorized}, 1300 {Error: ¶ms.Error{Message: `volume attachment host tag "application-mysql" not valid`}}, 1301 }, 1302 }) 1303 } 1304 1305 func (s *iaasProvisionerSuite) TestVolumeBlockDevicesPlanBlockInfoSet(c *gc.C) { 1306 s.setupVolumes(c) 1307 s.Factory.MakeMachine(c, nil) 1308 1309 err := s.storageBackend.SetVolumeAttachmentInfo( 1310 names.NewMachineTag("0"), 1311 names.NewVolumeTag("0/0"), 1312 state.VolumeAttachmentInfo{}, 1313 ) 1314 1315 deviceAttrs := map[string]string{ 1316 "iqn": "bogusIQN", 1317 "address": "192.168.1.1", 1318 "port": "9999", 1319 "chap-user": "example", 1320 "chap-secret": "supersecretpassword", 1321 } 1322 1323 attachmentPlanInfo := state.VolumeAttachmentPlanInfo{ 1324 DeviceType: storage.DeviceTypeISCSI, 1325 DeviceAttributes: deviceAttrs, 1326 } 1327 1328 err = s.storageBackend.CreateVolumeAttachmentPlan( 1329 names.NewMachineTag("0"), names.NewVolumeTag("0/0"), attachmentPlanInfo) 1330 c.Assert(err, jc.ErrorIsNil) 1331 1332 // The HardwareId set here should override the HardwareId in the volume info. 1333 blockInfo := state.BlockDeviceInfo{ 1334 WWN: "testWWN", 1335 DeviceLinks: []string{ 1336 "/dev/sda", "/dev/mapper/testDevice"}, 1337 HardwareId: "test-id", 1338 } 1339 err = s.storageBackend.SetVolumeAttachmentPlanBlockInfo( 1340 names.NewMachineTag("0"), names.NewVolumeTag("0/0"), blockInfo) 1341 c.Assert(err, jc.ErrorIsNil) 1342 1343 machine0, err := s.State.Machine("0") 1344 c.Assert(err, jc.ErrorIsNil) 1345 err = machine0.SetMachineBlockDevices(state.BlockDeviceInfo{ 1346 DeviceName: "sda", 1347 Size: 123, 1348 HardwareId: "test-id", 1349 }) 1350 c.Assert(err, jc.ErrorIsNil) 1351 1352 args := params.MachineStorageIds{Ids: []params.MachineStorageId{ 1353 {MachineTag: "machine-0", AttachmentTag: "volume-0-0"}, 1354 }} 1355 results, err := s.api.VolumeBlockDevices(args) 1356 c.Assert(err, jc.ErrorIsNil) 1357 c.Assert(results, jc.DeepEquals, params.BlockDeviceResults{ 1358 Results: []params.BlockDeviceResult{ 1359 {Result: storage.BlockDevice{ 1360 DeviceName: "sda", 1361 Size: 123, 1362 HardwareId: "test-id", 1363 }}, 1364 }, 1365 }) 1366 } 1367 1368 func (s *iaasProvisionerSuite) TestLife(c *gc.C) { 1369 // Only IAAS models support block storage right now. 1370 s.setupVolumes(c) 1371 args := params.Entities{Entities: []params.Entity{{"volume-0-0"}, {"volume-1"}, {"volume-42"}}} 1372 result, err := s.api.Life(args) 1373 c.Assert(err, jc.ErrorIsNil) 1374 c.Assert(result, gc.DeepEquals, params.LifeResults{ 1375 Results: []params.LifeResult{ 1376 {Life: params.Alive}, 1377 {Life: params.Alive}, 1378 {Error: common.ServerError(errors.NotFoundf(`volume "42"`))}, 1379 }, 1380 }) 1381 } 1382 1383 func (s *iaasProvisionerSuite) TestAttachmentLife(c *gc.C) { 1384 // Only IAAS models support block storage right now. 1385 s.setupVolumes(c) 1386 1387 // TODO(axw) test filesystem attachment life 1388 // TODO(axw) test Dying 1389 1390 results, err := s.api.AttachmentLife(params.MachineStorageIds{ 1391 Ids: []params.MachineStorageId{{ 1392 MachineTag: "machine-0", 1393 AttachmentTag: "volume-0-0", 1394 }, { 1395 MachineTag: "machine-0", 1396 AttachmentTag: "volume-1", 1397 }, { 1398 MachineTag: "machine-2", 1399 AttachmentTag: "volume-4", 1400 }, { 1401 MachineTag: "machine-0", 1402 AttachmentTag: "volume-42", 1403 }}, 1404 }) 1405 c.Assert(err, jc.ErrorIsNil) 1406 c.Assert(results, jc.DeepEquals, params.LifeResults{ 1407 Results: []params.LifeResult{ 1408 {Life: params.Alive}, 1409 {Life: params.Alive}, 1410 {Life: params.Alive}, 1411 {Error: ¶ms.Error{Message: `volume "42" on "machine 0" not found`, Code: "not found"}}, 1412 }, 1413 }) 1414 } 1415 1416 func (s *iaasProvisionerSuite) TestEnsureDead(c *gc.C) { 1417 // Only IAAS models support block storage right now. 1418 s.setupVolumes(c) 1419 args := params.Entities{Entities: []params.Entity{{"volume-0-0"}, {"volume-1"}, {"volume-42"}}} 1420 result, err := s.api.EnsureDead(args) 1421 c.Assert(err, jc.ErrorIsNil) 1422 // TODO(wallyworld) - this test will be updated when EnsureDead is supported 1423 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 1424 Results: []params.ErrorResult{ 1425 {Error: common.ServerError(common.NotSupportedError(names.NewVolumeTag("0/0"), "ensuring death"))}, 1426 {Error: common.ServerError(common.NotSupportedError(names.NewVolumeTag("1"), "ensuring death"))}, 1427 {Error: common.ServerError(errors.NotFoundf(`volume "42"`))}, 1428 }, 1429 }) 1430 } 1431 1432 func (s *iaasProvisionerSuite) TestRemoveVolumesController(c *gc.C) { 1433 // Only IAAS models support block storage right now. 1434 s.setupVolumes(c) 1435 args := params.Entities{Entities: []params.Entity{ 1436 {"volume-1-0"}, {"volume-1"}, {"volume-2"}, {"volume-42"}, 1437 {"volume-invalid"}, {"machine-0"}, 1438 }} 1439 1440 err := s.storageBackend.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("1")) 1441 c.Assert(err, jc.ErrorIsNil) 1442 err = s.storageBackend.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("1")) 1443 c.Assert(err, jc.ErrorIsNil) 1444 err = s.storageBackend.DestroyVolume(names.NewVolumeTag("1")) 1445 c.Assert(err, jc.ErrorIsNil) 1446 1447 result, err := s.api.Remove(args) 1448 c.Assert(err, jc.ErrorIsNil) 1449 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 1450 Results: []params.ErrorResult{ 1451 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 1452 {Error: nil}, 1453 {Error: ¶ms.Error{Message: "removing volume 2: volume is not dead"}}, 1454 {Error: nil}, 1455 {Error: ¶ms.Error{Message: `"volume-invalid" is not a valid volume tag`}}, 1456 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 1457 }, 1458 }) 1459 } 1460 1461 func (s *iaasProvisionerSuite) TestRemoveFilesystemsController(c *gc.C) { 1462 s.setupFilesystems(c) 1463 args := params.Entities{Entities: []params.Entity{ 1464 {"filesystem-1-0"}, {"filesystem-1"}, {"filesystem-2"}, {"filesystem-42"}, 1465 {"filesystem-invalid"}, {"machine-0"}, 1466 }} 1467 1468 err := s.storageBackend.DetachFilesystem(names.NewMachineTag("0"), names.NewFilesystemTag("1")) 1469 c.Assert(err, jc.ErrorIsNil) 1470 err = s.storageBackend.RemoveFilesystemAttachment(names.NewMachineTag("0"), names.NewFilesystemTag("1")) 1471 c.Assert(err, jc.ErrorIsNil) 1472 err = s.storageBackend.DestroyFilesystem(names.NewFilesystemTag("1")) 1473 c.Assert(err, jc.ErrorIsNil) 1474 1475 result, err := s.api.Remove(args) 1476 c.Assert(err, jc.ErrorIsNil) 1477 c.Assert(result, jc.DeepEquals, params.ErrorResults{ 1478 Results: []params.ErrorResult{ 1479 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 1480 {Error: nil}, 1481 {Error: ¶ms.Error{Message: "removing filesystem 2: filesystem is not dead"}}, 1482 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 1483 {Error: ¶ms.Error{Message: `"filesystem-invalid" is not a valid filesystem tag`}}, 1484 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 1485 }, 1486 }) 1487 } 1488 1489 func (s *iaasProvisionerSuite) TestRemoveVolumesMachineAgent(c *gc.C) { 1490 // Only IAAS models support block storage right now. 1491 s.setupVolumes(c) 1492 s.authorizer.Controller = false 1493 args := params.Entities{Entities: []params.Entity{ 1494 {"volume-0-0"}, {"volume-0-42"}, {"volume-42"}, 1495 {"volume-invalid"}, {"machine-0"}, 1496 }} 1497 1498 err := s.storageBackend.DestroyVolume(names.NewVolumeTag("0/0")) 1499 c.Assert(err, jc.ErrorIsNil) 1500 err = s.storageBackend.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0/0")) 1501 c.Assert(err, jc.ErrorIsNil) 1502 err = s.storageBackend.DestroyVolume(names.NewVolumeTag("0/0")) 1503 c.Assert(err, jc.ErrorIsNil) 1504 1505 result, err := s.api.Remove(args) 1506 c.Assert(err, jc.ErrorIsNil) 1507 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 1508 Results: []params.ErrorResult{ 1509 {Error: nil}, 1510 {Error: nil}, 1511 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 1512 {Error: ¶ms.Error{Message: `"volume-invalid" is not a valid volume tag`}}, 1513 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 1514 }, 1515 }) 1516 } 1517 1518 func (s *iaasProvisionerSuite) TestRemoveFilesystemsMachineAgent(c *gc.C) { 1519 s.setupFilesystems(c) 1520 s.authorizer.Controller = false 1521 args := params.Entities{Entities: []params.Entity{ 1522 {"filesystem-0-0"}, {"filesystem-0-42"}, {"filesystem-42"}, 1523 {"filesystem-invalid"}, {"machine-0"}, 1524 }} 1525 1526 err := s.storageBackend.DestroyFilesystem(names.NewFilesystemTag("0/0")) 1527 c.Assert(err, jc.ErrorIsNil) 1528 err = s.storageBackend.RemoveFilesystemAttachment(names.NewMachineTag("0"), names.NewFilesystemTag("0/0")) 1529 c.Assert(err, jc.ErrorIsNil) 1530 1531 result, err := s.api.Remove(args) 1532 c.Assert(err, jc.ErrorIsNil) 1533 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 1534 Results: []params.ErrorResult{ 1535 {Error: nil}, 1536 {Error: nil}, 1537 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 1538 {Error: ¶ms.Error{Message: `"filesystem-invalid" is not a valid filesystem tag`}}, 1539 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 1540 }, 1541 }) 1542 } 1543 1544 func (s *iaasProvisionerSuite) TestRemoveVolumeAttachments(c *gc.C) { 1545 // Only IAAS models support block storage right now. 1546 s.setupVolumes(c) 1547 s.authorizer.Controller = false 1548 1549 err := s.storageBackend.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("1")) 1550 c.Assert(err, jc.ErrorIsNil) 1551 1552 results, err := s.api.RemoveAttachment(params.MachineStorageIds{ 1553 Ids: []params.MachineStorageId{{ 1554 MachineTag: "machine-0", 1555 AttachmentTag: "volume-0-0", 1556 }, { 1557 MachineTag: "machine-0", 1558 AttachmentTag: "volume-1", 1559 }, { 1560 MachineTag: "machine-2", 1561 AttachmentTag: "volume-4", 1562 }, { 1563 MachineTag: "machine-0", 1564 AttachmentTag: "volume-42", 1565 }}, 1566 }) 1567 c.Assert(err, jc.ErrorIsNil) 1568 c.Assert(results, jc.DeepEquals, params.ErrorResults{ 1569 Results: []params.ErrorResult{ 1570 {Error: ¶ms.Error{Message: "removing attachment of volume 0/0 from machine 0: volume attachment is not dying"}}, 1571 {Error: nil}, 1572 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 1573 {Error: ¶ms.Error{Message: `removing attachment of volume 42 from machine 0: volume "42" on "machine 0" not found`, Code: "not found"}}, 1574 }, 1575 }) 1576 } 1577 1578 func (s *iaasProvisionerSuite) TestRemoveFilesystemAttachments(c *gc.C) { 1579 s.setupFilesystems(c) 1580 s.authorizer.Controller = false 1581 1582 err := s.storageBackend.DetachFilesystem(names.NewMachineTag("0"), names.NewFilesystemTag("1")) 1583 c.Assert(err, jc.ErrorIsNil) 1584 1585 results, err := s.api.RemoveAttachment(params.MachineStorageIds{ 1586 Ids: []params.MachineStorageId{{ 1587 MachineTag: "machine-0", 1588 AttachmentTag: "filesystem-0-0", 1589 }, { 1590 MachineTag: "machine-0", 1591 AttachmentTag: "filesystem-1", 1592 }, { 1593 MachineTag: "machine-2", 1594 AttachmentTag: "filesystem-4", 1595 }, { 1596 MachineTag: "machine-0", 1597 AttachmentTag: "filesystem-42", 1598 }}, 1599 }) 1600 c.Assert(err, jc.ErrorIsNil) 1601 c.Assert(results, jc.DeepEquals, params.ErrorResults{ 1602 Results: []params.ErrorResult{ 1603 {Error: ¶ms.Error{Message: "removing attachment of filesystem 0/0 from machine 0: filesystem attachment is not dying"}}, 1604 {Error: nil}, 1605 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 1606 {Error: ¶ms.Error{Message: `removing attachment of filesystem 42 from machine 0: filesystem "42" on "machine 0" not found`, Code: "not found"}}, 1607 }, 1608 }) 1609 } 1610 1611 type byMachineAndEntity []params.MachineStorageId 1612 1613 func (b byMachineAndEntity) Len() int { 1614 return len(b) 1615 } 1616 1617 func (b byMachineAndEntity) Less(i, j int) bool { 1618 if b[i].MachineTag == b[j].MachineTag { 1619 return b[i].AttachmentTag < b[j].AttachmentTag 1620 } 1621 return b[i].MachineTag < b[j].MachineTag 1622 } 1623 1624 func (b byMachineAndEntity) Swap(i, j int) { 1625 b[i], b[j] = b[j], b[i] 1626 } 1627 1628 func (s *caasProvisionerSuite) TestWatchFilesystemAttachments(c *gc.C) { 1629 s.setupFilesystems(c) 1630 c.Assert(s.resources.Count(), gc.Equals, 0) 1631 1632 args := params.Entities{Entities: []params.Entity{ 1633 {"application-mariadb"}, 1634 {s.Model.ModelTag().String()}, 1635 {"environ-adb650da-b77b-4ee8-9cbb-d57a9a592847"}, 1636 {"unit-mysql-0"}}, 1637 } 1638 result, err := s.api.WatchFilesystemAttachments(args) 1639 c.Assert(err, jc.ErrorIsNil) 1640 sort.Sort(byMachineAndEntity(result.Results[0].Changes)) 1641 sort.Sort(byMachineAndEntity(result.Results[1].Changes)) 1642 c.Assert(result, jc.DeepEquals, params.MachineStorageIdsWatchResults{ 1643 Results: []params.MachineStorageIdsWatchResult{ 1644 { 1645 MachineStorageIdsWatcherId: "1", 1646 Changes: []params.MachineStorageId{{ 1647 MachineTag: "unit-mariadb-0", 1648 AttachmentTag: "filesystem-0", 1649 }, { 1650 MachineTag: "unit-mariadb-0", 1651 AttachmentTag: "filesystem-1", 1652 }, { 1653 MachineTag: "unit-mariadb-0", 1654 AttachmentTag: "filesystem-2", 1655 }}, 1656 }, { 1657 MachineStorageIdsWatcherId: "2", 1658 Changes: []params.MachineStorageId{}, 1659 }, 1660 {Error: apiservertesting.ErrUnauthorized}, 1661 {Error: apiservertesting.ErrUnauthorized}, 1662 }, 1663 }) 1664 1665 // Verify the resources were registered and stop them when done. 1666 c.Assert(s.resources.Count(), gc.Equals, 2) 1667 v0Watcher := s.resources.Get("1") 1668 defer statetesting.AssertStop(c, v0Watcher) 1669 v1Watcher := s.resources.Get("2") 1670 defer statetesting.AssertStop(c, v1Watcher) 1671 1672 // Check that the Watch has consumed the initial events ("returned" in 1673 // the Watch call) 1674 wc := statetesting.NewStringsWatcherC(c, s.State, v0Watcher.(state.StringsWatcher)) 1675 wc.AssertNoChange() 1676 wc = statetesting.NewStringsWatcherC(c, s.State, v1Watcher.(state.StringsWatcher)) 1677 wc.AssertNoChange() 1678 } 1679 1680 func (s *caasProvisionerSuite) TestRemoveFilesystemAttachments(c *gc.C) { 1681 s.setupFilesystems(c) 1682 1683 err := s.storageBackend.DetachFilesystem(names.NewUnitTag("mariadb/0"), names.NewFilesystemTag("1")) 1684 c.Assert(err, jc.ErrorIsNil) 1685 1686 results, err := s.api.RemoveAttachment(params.MachineStorageIds{ 1687 Ids: []params.MachineStorageId{{ 1688 MachineTag: "unit-mariadb-0", 1689 AttachmentTag: "filesystem-0", 1690 }, { 1691 MachineTag: "unit-mariadb-0", 1692 AttachmentTag: "filesystem-1", 1693 }, { 1694 MachineTag: "unit-mysql-2", 1695 AttachmentTag: "filesystem-4", 1696 }, { 1697 MachineTag: "unit-mariadb-0", 1698 AttachmentTag: "filesystem-42", 1699 }}, 1700 }) 1701 c.Assert(err, jc.ErrorIsNil) 1702 c.Assert(results, jc.DeepEquals, params.ErrorResults{ 1703 Results: []params.ErrorResult{ 1704 {Error: ¶ms.Error{Message: "removing attachment of filesystem 0 from unit mariadb/0: filesystem attachment is not dying"}}, 1705 {Error: nil}, 1706 {Error: ¶ms.Error{Message: `removing attachment of filesystem 4 from unit mysql/2: filesystem "4" on "unit mysql/2" not found`, Code: "not found"}}, 1707 {Error: ¶ms.Error{Message: `removing attachment of filesystem 42 from unit mariadb/0: filesystem "42" on "unit mariadb/0" not found`, Code: "not found"}}, 1708 }, 1709 }) 1710 } 1711 1712 func (s *caasProvisionerSuite) TestRemoveFilesystemsApplicationAgent(c *gc.C) { 1713 s.setupFilesystems(c) 1714 s.authorizer.Controller = false 1715 args := params.Entities{Entities: []params.Entity{ 1716 {"filesystem-42"}, 1717 {"filesystem-invalid"}, {"machine-0"}, 1718 }} 1719 1720 err := s.storageBackend.DestroyFilesystem(names.NewFilesystemTag("0")) 1721 c.Assert(err, gc.ErrorMatches, "destroying filesystem 0: filesystem is assigned to storage cache/0") 1722 err = s.storageBackend.RemoveFilesystemAttachment(names.NewUnitTag("mariadb/0"), names.NewFilesystemTag("0")) 1723 c.Assert(err, gc.ErrorMatches, "removing attachment of filesystem 0 from unit mariadb/0: filesystem attachment is not dying") 1724 1725 result, err := s.api.Remove(args) 1726 c.Assert(err, jc.ErrorIsNil) 1727 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 1728 Results: []params.ErrorResult{ 1729 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 1730 {Error: ¶ms.Error{Message: `"filesystem-invalid" is not a valid filesystem tag`}}, 1731 {Error: ¶ms.Error{Message: "permission denied", Code: "unauthorized access"}}, 1732 }, 1733 }) 1734 } 1735 1736 func (s *caasProvisionerSuite) TestFilesystemLife(c *gc.C) { 1737 s.setupFilesystems(c) 1738 args := params.Entities{Entities: []params.Entity{{"filesystem-0"}, {"filesystem-1"}, {"filesystem-42"}}} 1739 result, err := s.api.Life(args) 1740 c.Assert(err, jc.ErrorIsNil) 1741 c.Assert(result, gc.DeepEquals, params.LifeResults{ 1742 Results: []params.LifeResult{ 1743 {Life: params.Alive}, 1744 {Life: params.Alive}, 1745 {Error: apiservertesting.ErrUnauthorized}, 1746 }, 1747 }) 1748 } 1749 1750 func (s caasProvisionerSuite) TestFilesystemAttachmentLife(c *gc.C) { 1751 s.setupFilesystems(c) 1752 1753 results, err := s.api.AttachmentLife(params.MachineStorageIds{ 1754 Ids: []params.MachineStorageId{{ 1755 MachineTag: "unit-mariadb-0", 1756 AttachmentTag: "filesystem-0", 1757 }, { 1758 MachineTag: "unit-mariadb-0", 1759 AttachmentTag: "filesystem-1", 1760 }, { 1761 MachineTag: "unit-mariadb-0", 1762 AttachmentTag: "filesystem-42", 1763 }}, 1764 }) 1765 c.Assert(err, jc.ErrorIsNil) 1766 c.Assert(results, jc.DeepEquals, params.LifeResults{ 1767 Results: []params.LifeResult{ 1768 {Life: params.Alive}, 1769 {Life: params.Alive}, 1770 {Error: ¶ms.Error{Message: `filesystem "42" on "unit mariadb/0" not found`, Code: "not found"}}, 1771 }, 1772 }) 1773 }