github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/storage.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "fmt" 8 9 "github.com/dustin/go-humanize" 10 "github.com/juju/errors" 11 "github.com/juju/names" 12 jujutxn "github.com/juju/txn" 13 "github.com/juju/utils/set" 14 "gopkg.in/juju/charm.v6-unstable" 15 "gopkg.in/mgo.v2" 16 "gopkg.in/mgo.v2/bson" 17 "gopkg.in/mgo.v2/txn" 18 19 "github.com/juju/juju/environs/config" 20 "github.com/juju/juju/storage" 21 "github.com/juju/juju/storage/poolmanager" 22 "github.com/juju/juju/storage/provider" 23 "github.com/juju/juju/storage/provider/registry" 24 ) 25 26 // StorageInstance represents the state of a unit or service-wide storage 27 // instance in the model. 28 type StorageInstance interface { 29 Entity 30 31 // StorageTag returns the tag for the storage instance. 32 StorageTag() names.StorageTag 33 34 // Kind returns the storage instance kind. 35 Kind() StorageKind 36 37 // Owner returns the tag of the service or unit that owns this storage 38 // instance. 39 Owner() names.Tag 40 41 // StorageName returns the name of the storage, as defined in the charm 42 // storage metadata. This does not uniquely identify storage instances, 43 // but identifies the group that the instances belong to. 44 StorageName() string 45 46 // Life reports whether the storage instance is Alive, Dying or Dead. 47 Life() Life 48 49 // CharmURL returns the charm URL that this storage instance was created with. 50 CharmURL() *charm.URL 51 } 52 53 // StorageAttachment represents the state of a unit's attachment to a storage 54 // instance. A non-shared storage instance will have a single attachment for 55 // the storage instance's owning unit, whereas a shared storage instance will 56 // have an attachment for each unit of the service owning the storage instance. 57 type StorageAttachment interface { 58 // StorageInstance returns the tag of the corresponding storage 59 // instance. 60 StorageInstance() names.StorageTag 61 62 // Unit returns the tag of the corresponding unit. 63 Unit() names.UnitTag 64 65 // Life reports whether the storage attachment is Alive, Dying or Dead. 66 Life() Life 67 } 68 69 // StorageKind defines the type of a store: whether it is a block device 70 // or a filesystem. 71 type StorageKind int 72 73 const ( 74 StorageKindUnknown StorageKind = iota 75 StorageKindBlock 76 StorageKindFilesystem 77 ) 78 79 type storageInstance struct { 80 st *State 81 doc storageInstanceDoc 82 } 83 84 func (s *storageInstance) Tag() names.Tag { 85 return s.StorageTag() 86 } 87 88 func (s *storageInstance) StorageTag() names.StorageTag { 89 return names.NewStorageTag(s.doc.Id) 90 } 91 92 func (s *storageInstance) Kind() StorageKind { 93 return s.doc.Kind 94 } 95 96 func (s *storageInstance) Owner() names.Tag { 97 tag, err := names.ParseTag(s.doc.Owner) 98 if err != nil { 99 // This should be impossible; we do not expose 100 // a means of modifying the owner tag. 101 panic(err) 102 } 103 return tag 104 } 105 106 func (s *storageInstance) StorageName() string { 107 return s.doc.StorageName 108 } 109 110 func (s *storageInstance) Life() Life { 111 return s.doc.Life 112 } 113 114 // CharmURL returns the charm URL that this storage instance was created with. 115 func (s *storageInstance) CharmURL() *charm.URL { 116 return s.doc.CharmURL 117 } 118 119 // storageInstanceDoc describes a charm storage instance. 120 type storageInstanceDoc struct { 121 DocID string `bson:"_id"` 122 ModelUUID string `bson:"model-uuid"` 123 124 Id string `bson:"id"` 125 Kind StorageKind `bson:"storagekind"` 126 Life Life `bson:"life"` 127 Owner string `bson:"owner"` 128 StorageName string `bson:"storagename"` 129 AttachmentCount int `bson:"attachmentcount"` 130 CharmURL *charm.URL `bson:"charmurl"` 131 } 132 133 type storageAttachment struct { 134 doc storageAttachmentDoc 135 } 136 137 func (s *storageAttachment) StorageInstance() names.StorageTag { 138 return names.NewStorageTag(s.doc.StorageInstance) 139 } 140 141 func (s *storageAttachment) Unit() names.UnitTag { 142 return names.NewUnitTag(s.doc.Unit) 143 } 144 145 func (s *storageAttachment) Life() Life { 146 return s.doc.Life 147 } 148 149 // storageAttachmentDoc describes a unit's attachment to a charm storage 150 // instance. 151 type storageAttachmentDoc struct { 152 DocID string `bson:"_id"` 153 ModelUUID string `bson:"model-uuid"` 154 155 Unit string `bson:"unitid"` 156 StorageInstance string `bson:"storageid"` 157 Life Life `bson:"life"` 158 } 159 160 // newStorageInstanceId returns a unique storage instance name. The name 161 // incorporates the storage name as defined in the charm storage metadata, 162 // and a unique sequence number. 163 func newStorageInstanceId(st *State, store string) (string, error) { 164 seq, err := st.sequence("stores") 165 if err != nil { 166 return "", errors.Trace(err) 167 } 168 return fmt.Sprintf("%s/%v", store, seq), nil 169 } 170 171 func storageAttachmentId(unit string, storageInstanceId string) string { 172 return fmt.Sprintf("%s#%s", unitGlobalKey(unit), storageInstanceId) 173 } 174 175 // StorageInstance returns the StorageInstance with the specified tag. 176 func (st *State) StorageInstance(tag names.StorageTag) (StorageInstance, error) { 177 s, err := st.storageInstance(tag) 178 return s, err 179 } 180 181 func (st *State) storageInstance(tag names.StorageTag) (*storageInstance, error) { 182 storageInstances, cleanup := st.getCollection(storageInstancesC) 183 defer cleanup() 184 185 s := storageInstance{st: st} 186 err := storageInstances.FindId(tag.Id()).One(&s.doc) 187 if err == mgo.ErrNotFound { 188 return nil, errors.NotFoundf("storage instance %q", tag.Id()) 189 } else if err != nil { 190 return nil, errors.Annotate(err, "cannot get storage instance details") 191 } 192 return &s, nil 193 } 194 195 // AllStorageInstances lists all storage instances currently in state 196 // for this Juju model. 197 func (st *State) AllStorageInstances() (storageInstances []StorageInstance, err error) { 198 storageCollection, closer := st.getCollection(storageInstancesC) 199 defer closer() 200 201 sdocs := []storageInstanceDoc{} 202 err = storageCollection.Find(nil).All(&sdocs) 203 if err != nil { 204 return nil, errors.Annotate(err, "cannot get all storage instances") 205 } 206 for _, doc := range sdocs { 207 storageInstances = append(storageInstances, &storageInstance{st, doc}) 208 } 209 return 210 } 211 212 // DestroyStorageInstance ensures that the storage instance and all its 213 // attachments will be removed at some point; if the storage instance has 214 // no attachments, it will be removed immediately. 215 func (st *State) DestroyStorageInstance(tag names.StorageTag) (err error) { 216 defer errors.DeferredAnnotatef(&err, "cannot destroy storage %q", tag.Id()) 217 buildTxn := func(attempt int) ([]txn.Op, error) { 218 s, err := st.storageInstance(tag) 219 if errors.IsNotFound(err) { 220 return nil, jujutxn.ErrNoOperations 221 } else if err != nil { 222 return nil, errors.Trace(err) 223 } 224 switch ops, err := st.destroyStorageInstanceOps(s); err { 225 case errAlreadyDying: 226 return nil, jujutxn.ErrNoOperations 227 case nil: 228 return ops, nil 229 default: 230 return nil, errors.Trace(err) 231 } 232 } 233 return st.run(buildTxn) 234 } 235 236 func (st *State) destroyStorageInstanceOps(s *storageInstance) ([]txn.Op, error) { 237 if s.doc.Life == Dying { 238 return nil, errAlreadyDying 239 } 240 if s.doc.AttachmentCount == 0 { 241 // There are no attachments remaining, so we can 242 // remove the storage instance immediately. 243 hasNoAttachments := bson.D{{"attachmentcount", 0}} 244 assert := append(hasNoAttachments, isAliveDoc...) 245 return removeStorageInstanceOps(st, s.StorageTag(), assert) 246 } 247 // There are still attachments: the storage instance will be removed 248 // when the last attachment is removed. We schedule a cleanup to destroy 249 // attachments. 250 notLastRefs := bson.D{ 251 {"life", Alive}, 252 {"attachmentcount", bson.D{{"$gt", 0}}}, 253 } 254 update := bson.D{{"$set", bson.D{{"life", Dying}}}} 255 ops := []txn.Op{ 256 st.newCleanupOp(cleanupAttachmentsForDyingStorage, s.doc.Id), 257 { 258 C: storageInstancesC, 259 Id: s.doc.Id, 260 Assert: notLastRefs, 261 Update: update, 262 }, 263 } 264 return ops, nil 265 } 266 267 // removeStorageInstanceOps removes the storage instance with the given 268 // tag from state, if the specified assertions hold true. 269 func removeStorageInstanceOps( 270 st *State, 271 tag names.StorageTag, 272 assert bson.D, 273 ) ([]txn.Op, error) { 274 ops := []txn.Op{{ 275 C: storageInstancesC, 276 Id: tag.Id(), 277 Assert: assert, 278 Remove: true, 279 }} 280 281 machineStorageOp := func(c string, id string) txn.Op { 282 return txn.Op{ 283 C: c, 284 Id: id, 285 Assert: bson.D{{"storageid", tag.Id()}}, 286 Update: bson.D{{"$set", bson.D{{"storageid", ""}}}}, 287 } 288 } 289 290 // If the storage instance has an assigned volume and/or filesystem, 291 // unassign them. Any volumes and filesystems bound to the storage 292 // will be destroyed. 293 volume, err := st.storageInstanceVolume(tag) 294 if err == nil { 295 ops = append(ops, machineStorageOp( 296 volumesC, volume.Tag().Id(), 297 )) 298 if volume.LifeBinding() == tag { 299 ops = append(ops, destroyVolumeOps(st, volume)...) 300 } 301 } else if !errors.IsNotFound(err) { 302 return nil, errors.Trace(err) 303 } 304 filesystem, err := st.storageInstanceFilesystem(tag) 305 if err == nil { 306 ops = append(ops, machineStorageOp( 307 filesystemsC, filesystem.Tag().Id(), 308 )) 309 if filesystem.LifeBinding() == tag { 310 ops = append(ops, destroyFilesystemOps(st, filesystem)...) 311 } 312 } else if !errors.IsNotFound(err) { 313 return nil, errors.Trace(err) 314 } 315 return ops, nil 316 } 317 318 // createStorageOps returns txn.Ops for creating storage instances 319 // and attachments for the newly created unit or service. 320 // 321 // The entity tag identifies the entity that owns the storage instance 322 // either a unit or a service. Shared storage instances are owned by a 323 // service, and non-shared storage instances are owned by a unit. 324 // 325 // The charm metadata corresponds to the charm that the owner (service/unit) 326 // is or will be running, and is used to extract storage constraints, 327 // default values, etc. 328 // 329 // The supplied storage constraints are constraints for the storage 330 // instances to be created, keyed on the storage name. These constraints 331 // will be correlated with the charm storage metadata for validation 332 // and supplementing. 333 func createStorageOps( 334 st *State, 335 entity names.Tag, 336 charmMeta *charm.Meta, 337 curl *charm.URL, 338 cons map[string]StorageConstraints, 339 series string, 340 machineOpsNeeded bool, 341 ) (ops []txn.Op, numStorageAttachments int, err error) { 342 343 type template struct { 344 storageName string 345 meta charm.Storage 346 cons StorageConstraints 347 } 348 349 createdShared := false 350 switch entity := entity.(type) { 351 case names.ServiceTag: 352 createdShared = true 353 case names.UnitTag: 354 default: 355 return nil, -1, errors.Errorf("expected service or unit tag, got %T", entity) 356 } 357 358 // Create storage instances in order of name, to simplify testing. 359 storageNames := set.NewStrings() 360 for name := range cons { 361 storageNames.Add(name) 362 } 363 364 templates := make([]template, 0, len(cons)) 365 for _, store := range storageNames.SortedValues() { 366 cons := cons[store] 367 charmStorage, ok := charmMeta.Storage[store] 368 if !ok { 369 return nil, -1, errors.NotFoundf("charm storage %q", store) 370 } 371 if createdShared != charmStorage.Shared { 372 // services only get shared storage instances, 373 // units only get non-shared storage instances. 374 continue 375 } 376 templates = append(templates, template{ 377 storageName: store, 378 meta: charmStorage, 379 cons: cons, 380 }) 381 } 382 383 ops = make([]txn.Op, 0, len(templates)*2) 384 for _, t := range templates { 385 owner := entity.String() 386 var kind StorageKind 387 switch t.meta.Type { 388 case charm.StorageBlock: 389 kind = StorageKindBlock 390 case charm.StorageFilesystem: 391 kind = StorageKindFilesystem 392 default: 393 return nil, -1, errors.Errorf("unknown storage type %q", t.meta.Type) 394 } 395 396 for i := uint64(0); i < t.cons.Count; i++ { 397 id, err := newStorageInstanceId(st, t.storageName) 398 if err != nil { 399 return nil, -1, errors.Annotate(err, "cannot generate storage instance name") 400 } 401 doc := &storageInstanceDoc{ 402 Id: id, 403 Kind: kind, 404 Owner: owner, 405 StorageName: t.storageName, 406 CharmURL: curl, 407 } 408 if unit, ok := entity.(names.UnitTag); ok { 409 doc.AttachmentCount = 1 410 storage := names.NewStorageTag(id) 411 ops = append(ops, createStorageAttachmentOp(storage, unit)) 412 numStorageAttachments++ 413 } 414 ops = append(ops, txn.Op{ 415 C: storageInstancesC, 416 Id: id, 417 Assert: txn.DocMissing, 418 Insert: doc, 419 }) 420 if machineOpsNeeded { 421 machineOps, err := unitAssignedMachineStorageOps( 422 st, entity, charmMeta, cons, series, 423 &storageInstance{st, *doc}, 424 ) 425 if err == nil { 426 ops = append(ops, machineOps...) 427 } else if !errors.IsNotAssigned(err) { 428 return nil, -1, errors.Annotatef( 429 err, "creating machine storage for storage %s", id, 430 ) 431 } 432 } 433 } 434 } 435 436 // TODO(axw) create storage attachments for each shared storage 437 // instance owned by the service. 438 // 439 // TODO(axw) prevent creation of shared storage after service 440 // creation, because the only sane time to add storage attachments 441 // is when units are added to said service. 442 443 return ops, numStorageAttachments, nil 444 } 445 446 // unitAssignedMachineStorageOps returns ops for creating volumes, filesystems 447 // and their attachments to the machine that the specified unit is assigned to, 448 // corresponding to the specified storage instance. 449 func unitAssignedMachineStorageOps( 450 st *State, 451 entity names.Tag, 452 charmMeta *charm.Meta, 453 cons map[string]StorageConstraints, 454 series string, 455 storage StorageInstance, 456 ) (ops []txn.Op, err error) { 457 tag, ok := entity.(names.UnitTag) 458 if !ok { 459 return nil, errors.NotSupportedf("dynamic creation of shared storage") 460 } 461 storageParams, err := machineStorageParamsForStorageInstance( 462 st, charmMeta, tag, series, cons, storage, 463 ) 464 if err != nil { 465 return nil, errors.Trace(err) 466 } 467 468 u, err := st.Unit(tag.Id()) 469 if err != nil { 470 return nil, errors.Trace(err) 471 } 472 m, err := u.machine() 473 if err != nil { 474 return nil, errors.Trace(err) 475 } 476 477 if err := validateDynamicMachineStorageParams(m, storageParams); err != nil { 478 return nil, errors.Trace(err) 479 } 480 storageOps, volumeAttachments, filesystemAttachments, err := st.machineStorageOps( 481 &m.doc, storageParams, 482 ) 483 if err != nil { 484 return nil, errors.Trace(err) 485 } 486 attachmentOps, err := addMachineStorageAttachmentsOps( 487 m, volumeAttachments, filesystemAttachments, 488 ) 489 if err != nil { 490 return nil, errors.Trace(err) 491 } 492 storageOps = append(storageOps, attachmentOps...) 493 return storageOps, nil 494 } 495 496 // createStorageAttachmentOps returns a txn.Op for creating a storage attachment. 497 // The caller is responsible for updating the attachmentcount field of the storage 498 // instance. 499 func createStorageAttachmentOp(storage names.StorageTag, unit names.UnitTag) txn.Op { 500 return txn.Op{ 501 C: storageAttachmentsC, 502 Id: storageAttachmentId(unit.Id(), storage.Id()), 503 Assert: txn.DocMissing, 504 Insert: &storageAttachmentDoc{ 505 Unit: unit.Id(), 506 StorageInstance: storage.Id(), 507 }, 508 } 509 } 510 511 // StorageAttachments returns the StorageAttachments for the specified storage 512 // instance. 513 func (st *State) StorageAttachments(storage names.StorageTag) ([]StorageAttachment, error) { 514 query := bson.D{{"storageid", storage.Id()}} 515 attachments, err := st.storageAttachments(query) 516 if err != nil { 517 return nil, errors.Annotatef(err, "cannot get storage attachments for storage %s", storage.Id()) 518 } 519 return attachments, nil 520 } 521 522 // UnitStorageAttachments returns the StorageAttachments for the specified unit. 523 func (st *State) UnitStorageAttachments(unit names.UnitTag) ([]StorageAttachment, error) { 524 query := bson.D{{"unitid", unit.Id()}} 525 attachments, err := st.storageAttachments(query) 526 if err != nil { 527 return nil, errors.Annotatef(err, "cannot get storage attachments for unit %s", unit.Id()) 528 } 529 return attachments, nil 530 } 531 532 func (st *State) storageAttachments(query bson.D) ([]StorageAttachment, error) { 533 coll, closer := st.getCollection(storageAttachmentsC) 534 defer closer() 535 536 var docs []storageAttachmentDoc 537 if err := coll.Find(query).All(&docs); err != nil { 538 return nil, err 539 } 540 storageAttachments := make([]StorageAttachment, len(docs)) 541 for i, doc := range docs { 542 storageAttachments[i] = &storageAttachment{doc} 543 } 544 return storageAttachments, nil 545 } 546 547 // StorageAttachment returns the StorageAttachment wit hthe specified tags. 548 func (st *State) StorageAttachment(storage names.StorageTag, unit names.UnitTag) (StorageAttachment, error) { 549 att, err := st.storageAttachment(storage, unit) 550 if err != nil { 551 return nil, errors.Trace(err) 552 } 553 return att, nil 554 } 555 556 func (st *State) storageAttachment(storage names.StorageTag, unit names.UnitTag) (*storageAttachment, error) { 557 coll, closer := st.getCollection(storageAttachmentsC) 558 defer closer() 559 var s storageAttachment 560 err := coll.FindId(storageAttachmentId(unit.Id(), storage.Id())).One(&s.doc) 561 if err == mgo.ErrNotFound { 562 return nil, errors.NotFoundf("storage attachment %s:%s", storage.Id(), unit.Id()) 563 } else if err != nil { 564 return nil, errors.Annotatef(err, "cannot get storage attachment %s:%s", storage.Id(), unit.Id()) 565 } 566 return &s, nil 567 } 568 569 // DestroyStorageAttachment ensures that the existing storage attachments of 570 // the specified unit are removed at some point. 571 func (st *State) DestroyUnitStorageAttachments(unit names.UnitTag) (err error) { 572 defer errors.DeferredAnnotatef(&err, "cannot destroy unit %s storage attachments", unit.Id()) 573 buildTxn := func(attempt int) ([]txn.Op, error) { 574 attachments, err := st.UnitStorageAttachments(unit) 575 if err != nil { 576 return nil, errors.Trace(err) 577 } 578 ops := make([]txn.Op, 0, len(attachments)) 579 for _, attachment := range attachments { 580 if attachment.Life() != Alive { 581 continue 582 } 583 ops = append(ops, destroyStorageAttachmentOps( 584 attachment.StorageInstance(), unit, 585 )...) 586 } 587 if len(ops) == 0 { 588 return nil, jujutxn.ErrNoOperations 589 } 590 return ops, nil 591 } 592 return st.run(buildTxn) 593 } 594 595 // DestroyStorageAttachment ensures that the storage attachment will be 596 // removed at some point. 597 func (st *State) DestroyStorageAttachment(storage names.StorageTag, unit names.UnitTag) (err error) { 598 defer errors.DeferredAnnotatef(&err, "cannot destroy storage attachment %s:%s", storage.Id(), unit.Id()) 599 buildTxn := func(attempt int) ([]txn.Op, error) { 600 s, err := st.storageAttachment(storage, unit) 601 if errors.IsNotFound(err) { 602 return nil, jujutxn.ErrNoOperations 603 } else if err != nil { 604 return nil, errors.Trace(err) 605 } 606 if s.doc.Life == Dying { 607 return nil, jujutxn.ErrNoOperations 608 } 609 return destroyStorageAttachmentOps(storage, unit), nil 610 } 611 return st.run(buildTxn) 612 } 613 614 func destroyStorageAttachmentOps(storage names.StorageTag, unit names.UnitTag) []txn.Op { 615 ops := []txn.Op{{ 616 C: storageAttachmentsC, 617 Id: storageAttachmentId(unit.Id(), storage.Id()), 618 Assert: isAliveDoc, 619 Update: bson.D{{"$set", bson.D{{"life", Dying}}}}, 620 }} 621 return ops 622 } 623 624 // Remove removes the storage attachment from state, and may remove its storage 625 // instance as well, if the storage instance is Dying and no other references to 626 // it exist. It will fail if the storage attachment is not Dying. 627 func (st *State) RemoveStorageAttachment(storage names.StorageTag, unit names.UnitTag) (err error) { 628 defer errors.DeferredAnnotatef(&err, "cannot remove storage attachment %s:%s", storage.Id(), unit.Id()) 629 buildTxn := func(attempt int) ([]txn.Op, error) { 630 s, err := st.storageAttachment(storage, unit) 631 if errors.IsNotFound(err) { 632 return nil, jujutxn.ErrNoOperations 633 } else if err != nil { 634 return nil, errors.Trace(err) 635 } 636 inst, err := st.storageInstance(storage) 637 if errors.IsNotFound(err) { 638 // This implies that the attachment was removed 639 // after the call to st.storageAttachment. 640 return nil, jujutxn.ErrNoOperations 641 } else if err != nil { 642 return nil, errors.Trace(err) 643 } 644 ops, err := removeStorageAttachmentOps(st, s, inst) 645 if err != nil { 646 return nil, errors.Trace(err) 647 } 648 return ops, nil 649 } 650 return st.run(buildTxn) 651 } 652 653 func removeStorageAttachmentOps( 654 st *State, 655 s *storageAttachment, 656 si *storageInstance, 657 ) ([]txn.Op, error) { 658 if s.doc.Life != Dying { 659 return nil, errors.New("storage attachment is not dying") 660 } 661 ops := []txn.Op{{ 662 C: storageAttachmentsC, 663 Id: storageAttachmentId(s.doc.Unit, s.doc.StorageInstance), 664 Assert: bson.D{{"life", Dying}}, 665 Remove: true, 666 }, { 667 C: unitsC, 668 Id: s.doc.Unit, 669 Assert: txn.DocExists, 670 Update: bson.D{{"$inc", bson.D{{"storageattachmentcount", -1}}}}, 671 }} 672 if si.doc.AttachmentCount == 1 { 673 var hasLastRef bson.D 674 if si.doc.Life == Dying { 675 hasLastRef = bson.D{{"life", Dying}, {"attachmentcount", 1}} 676 } else if si.doc.Owner == names.NewUnitTag(s.doc.Unit).String() { 677 hasLastRef = bson.D{{"attachmentcount", 1}} 678 } 679 if len(hasLastRef) > 0 { 680 // Either the storage instance is dying, or its owner 681 // is a unit; in either case, no more attachments can 682 // be added to the instance, so it can be removed. 683 siOps, err := removeStorageInstanceOps(st, si.StorageTag(), hasLastRef) 684 if err != nil { 685 return nil, errors.Trace(err) 686 } 687 ops = append(ops, siOps...) 688 return ops, nil 689 } 690 } 691 decrefOp := txn.Op{ 692 C: storageInstancesC, 693 Id: si.doc.Id, 694 Update: bson.D{{"$inc", bson.D{{"attachmentcount", -1}}}}, 695 } 696 if si.doc.Life == Alive { 697 // This may be the last reference, but the storage instance is 698 // still alive. The storage instance will be removed when its 699 // Destroy method is called, if it has no attachments. 700 decrefOp.Assert = bson.D{ 701 {"life", Alive}, 702 {"attachmentcount", bson.D{{"$gt", 0}}}, 703 } 704 } else { 705 // If it's not the last reference when we checked, we want to 706 // allow for concurrent attachment removals but want to ensure 707 // that we don't drop to zero without removing the storage 708 // instance. 709 decrefOp.Assert = bson.D{ 710 {"life", Dying}, 711 {"attachmentcount", bson.D{{"$gt", 1}}}, 712 } 713 } 714 ops = append(ops, decrefOp) 715 return ops, nil 716 } 717 718 // removeStorageInstancesOps returns the transaction operations to remove all 719 // storage instances owned by the specified entity. 720 func removeStorageInstancesOps(st *State, owner names.Tag) ([]txn.Op, error) { 721 coll, closer := st.getCollection(storageInstancesC) 722 defer closer() 723 724 var docs []storageInstanceDoc 725 err := coll.Find(bson.D{{"owner", owner.String()}}).Select(bson.D{{"id", true}}).All(&docs) 726 if err != nil { 727 return nil, errors.Annotatef(err, "cannot get storage instances for %s", owner) 728 } 729 ops := make([]txn.Op, len(docs)) 730 for i, doc := range docs { 731 ops[i] = txn.Op{ 732 C: storageInstancesC, 733 Id: doc.Id, 734 Remove: true, 735 } 736 } 737 return ops, nil 738 } 739 740 // storageConstraintsDoc contains storage constraints for an entity. 741 type storageConstraintsDoc struct { 742 DocID string `bson:"_id"` 743 ModelUUID string `bson:"model-uuid"` 744 Constraints map[string]StorageConstraints `bson:"constraints"` 745 } 746 747 // StorageConstraints contains the user-specified constraints for provisioning 748 // storage instances for a service unit. 749 type StorageConstraints struct { 750 // Pool is the name of the storage pool from which to provision the 751 // storage instances. 752 Pool string `bson:"pool"` 753 754 // Size is the required size of the storage instances, in MiB. 755 Size uint64 `bson:"size"` 756 757 // Count is the required number of storage instances. 758 Count uint64 `bson:"count"` 759 } 760 761 func createStorageConstraintsOp(key string, cons map[string]StorageConstraints) txn.Op { 762 return txn.Op{ 763 C: storageConstraintsC, 764 Id: key, 765 Assert: txn.DocMissing, 766 Insert: &storageConstraintsDoc{ 767 Constraints: cons, 768 }, 769 } 770 } 771 772 func removeStorageConstraintsOp(key string) txn.Op { 773 return txn.Op{ 774 C: storageConstraintsC, 775 Id: key, 776 Remove: true, 777 } 778 } 779 780 func readStorageConstraints(st *State, key string) (map[string]StorageConstraints, error) { 781 coll, closer := st.getCollection(storageConstraintsC) 782 defer closer() 783 784 var doc storageConstraintsDoc 785 err := coll.FindId(key).One(&doc) 786 if err == mgo.ErrNotFound { 787 return nil, nil 788 } 789 if err != nil { 790 return nil, errors.Annotatef(err, "cannot get storage constraints for %q", key) 791 } 792 return doc.Constraints, nil 793 } 794 795 func storageKind(storageType charm.StorageType) storage.StorageKind { 796 kind := storage.StorageKindUnknown 797 switch storageType { 798 case charm.StorageBlock: 799 kind = storage.StorageKindBlock 800 case charm.StorageFilesystem: 801 kind = storage.StorageKindFilesystem 802 } 803 return kind 804 } 805 806 func validateStorageConstraints(st *State, allCons map[string]StorageConstraints, charmMeta *charm.Meta) error { 807 err := validateStorageConstraintsAgainstCharm(st, allCons, charmMeta) 808 if err != nil { 809 return errors.Trace(err) 810 } 811 // Ensure all stores have constraints specified. Defaults should have 812 // been set by this point, if the user didn't specify constraints. 813 for name, charmStorage := range charmMeta.Storage { 814 if _, ok := allCons[name]; !ok && charmStorage.CountMin > 0 { 815 return errors.Errorf("no constraints specified for store %q", name) 816 } 817 } 818 return nil 819 } 820 821 func validateStorageConstraintsAgainstCharm( 822 st *State, 823 allCons map[string]StorageConstraints, 824 charmMeta *charm.Meta, 825 ) error { 826 for name, cons := range allCons { 827 charmStorage, ok := charmMeta.Storage[name] 828 if !ok { 829 return errors.Errorf("charm %q has no store called %q", charmMeta.Name, name) 830 } 831 if charmStorage.Shared { 832 // TODO(axw) implement shared storage support. 833 return errors.Errorf( 834 "charm %q store %q: shared storage support not implemented", 835 charmMeta.Name, name, 836 ) 837 } 838 if cons.Count < uint64(charmStorage.CountMin) { 839 return errors.Errorf( 840 "charm %q store %q: %d instances required, %d specified", 841 charmMeta.Name, name, charmStorage.CountMin, cons.Count, 842 ) 843 } 844 if charmStorage.CountMax >= 0 && cons.Count > uint64(charmStorage.CountMax) { 845 return errors.Errorf( 846 "charm %q store %q: at most %d instances supported, %d specified", 847 charmMeta.Name, name, charmStorage.CountMax, cons.Count, 848 ) 849 } 850 if charmStorage.MinimumSize > 0 && cons.Size < charmStorage.MinimumSize { 851 return errors.Errorf( 852 "charm %q store %q: minimum storage size is %s, %s specified", 853 charmMeta.Name, name, 854 humanize.Bytes(charmStorage.MinimumSize*humanize.MByte), 855 humanize.Bytes(cons.Size*humanize.MByte), 856 ) 857 } 858 kind := storageKind(charmStorage.Type) 859 if err := validateStoragePool(st, cons.Pool, kind, nil); err != nil { 860 return err 861 } 862 } 863 return nil 864 } 865 866 // validateStoragePool validates the storage pool for the model. 867 // If machineId is non-nil, the storage scope will be validated against 868 // the machineId; if the storage is not machine-scoped, then the machineId 869 // will be updated to "". 870 func validateStoragePool( 871 st *State, poolName string, kind storage.StorageKind, machineId *string, 872 ) error { 873 if poolName == "" { 874 return errors.New("pool name is required") 875 } 876 providerType, provider, err := poolStorageProvider(st, poolName) 877 if err != nil { 878 return errors.Trace(err) 879 } 880 881 // Ensure the storage provider supports the specified kind. 882 kindSupported := provider.Supports(kind) 883 if !kindSupported && kind == storage.StorageKindFilesystem { 884 // Filesystems can be created if either filesystem 885 // or block storage are supported. 886 if provider.Supports(storage.StorageKindBlock) { 887 kindSupported = true 888 // The filesystem is to be backed by a volume, 889 // so the filesystem must be managed on the 890 // machine. Skip the scope-check below by 891 // setting the pointer to nil. 892 machineId = nil 893 } 894 } 895 if !kindSupported { 896 return errors.Errorf("%q provider does not support %q storage", providerType, kind) 897 } 898 899 // Check the storage scope. 900 if machineId != nil { 901 switch provider.Scope() { 902 case storage.ScopeMachine: 903 if *machineId == "" { 904 return errors.Annotate(err, "machine unspecified for machine-scoped storage") 905 } 906 default: 907 // The storage is not machine-scoped, so we clear out 908 // the machine ID to inform the caller that the storage 909 // scope should be the model. 910 *machineId = "" 911 } 912 } 913 914 // Ensure the pool type is supported by the model. 915 conf, err := st.ModelConfig() 916 if err != nil { 917 return errors.Trace(err) 918 } 919 envType := conf.Type() 920 if !registry.IsProviderSupported(envType, providerType) { 921 return errors.Errorf( 922 "pool %q uses storage provider %q which is not supported for models of type %q", 923 poolName, 924 providerType, 925 envType, 926 ) 927 } 928 return nil 929 } 930 931 func poolStorageProvider(st *State, poolName string) (storage.ProviderType, storage.Provider, error) { 932 poolManager := poolmanager.New(NewStateSettings(st)) 933 pool, err := poolManager.Get(poolName) 934 if errors.IsNotFound(err) { 935 // If there's no pool called poolName, maybe a provider type 936 // has been specified directly. 937 providerType := storage.ProviderType(poolName) 938 provider, err1 := registry.StorageProvider(providerType) 939 if err1 != nil { 940 // The name can't be resolved as a storage provider type, 941 // so return the original "pool not found" error. 942 return "", nil, errors.Trace(err) 943 } 944 return providerType, provider, nil 945 } else if err != nil { 946 return "", nil, errors.Trace(err) 947 } 948 providerType := pool.Provider() 949 provider, err := registry.StorageProvider(providerType) 950 if err != nil { 951 return "", nil, errors.Trace(err) 952 } 953 return providerType, provider, nil 954 } 955 956 // ErrNoDefaultStoragePool is returned when a storage pool is required but none 957 // is specified nor available as a default. 958 var ErrNoDefaultStoragePool = fmt.Errorf("no storage pool specifed and no default available") 959 960 // addDefaultStorageConstraints fills in default constraint values, replacing any empty/missing values 961 // in the specified constraints. 962 func addDefaultStorageConstraints(st *State, allCons map[string]StorageConstraints, charmMeta *charm.Meta) error { 963 conf, err := st.ModelConfig() 964 if err != nil { 965 return errors.Trace(err) 966 } 967 968 for name, charmStorage := range charmMeta.Storage { 969 cons, ok := allCons[name] 970 if !ok { 971 if charmStorage.Shared { 972 // TODO(axw) get the model's default shared storage 973 // pool, and create constraints here. 974 return errors.Errorf( 975 "no constraints specified for shared charm storage %q", 976 name, 977 ) 978 } 979 } 980 cons, err := storageConstraintsWithDefaults(conf, charmStorage, name, cons) 981 if err != nil { 982 return errors.Trace(err) 983 } 984 // Replace in case pool or size were updated. 985 allCons[name] = cons 986 } 987 return nil 988 } 989 990 // storageConstraintsWithDefaults returns a constraints 991 // derived from cons, with any defaults filled in. 992 func storageConstraintsWithDefaults( 993 cfg *config.Config, 994 charmStorage charm.Storage, 995 name string, 996 cons StorageConstraints, 997 ) (StorageConstraints, error) { 998 withDefaults := cons 999 1000 // If no pool is specified, determine the pool from the env config and other constraints. 1001 if cons.Pool == "" { 1002 kind := storageKind(charmStorage.Type) 1003 poolName, err := defaultStoragePool(cfg, kind, cons) 1004 if err != nil { 1005 return withDefaults, errors.Annotatef(err, "finding default pool for %q storage", name) 1006 } 1007 withDefaults.Pool = poolName 1008 } 1009 1010 // If no size is specified, we default to the min size specified by the 1011 // charm, or 1GiB. 1012 if cons.Size == 0 { 1013 if charmStorage.MinimumSize > 0 { 1014 withDefaults.Size = charmStorage.MinimumSize 1015 } else { 1016 withDefaults.Size = 1024 1017 } 1018 } 1019 if cons.Count == 0 { 1020 withDefaults.Count = uint64(charmStorage.CountMin) 1021 } 1022 return withDefaults, nil 1023 } 1024 1025 // defaultStoragePool returns the default storage pool for the model. 1026 // The default pool is either user specified, or one that is registered by the provider itself. 1027 func defaultStoragePool(cfg *config.Config, kind storage.StorageKind, cons StorageConstraints) (string, error) { 1028 switch kind { 1029 case storage.StorageKindBlock: 1030 loopPool := string(provider.LoopProviderType) 1031 1032 emptyConstraints := StorageConstraints{} 1033 if cons == emptyConstraints { 1034 // No constraints at all: use loop. 1035 return loopPool, nil 1036 } 1037 // Either size or count specified, use env default. 1038 defaultPool, ok := cfg.StorageDefaultBlockSource() 1039 if !ok { 1040 defaultPool = loopPool 1041 } 1042 return defaultPool, nil 1043 1044 case storage.StorageKindFilesystem: 1045 rootfsPool := string(provider.RootfsProviderType) 1046 emptyConstraints := StorageConstraints{} 1047 if cons == emptyConstraints { 1048 return rootfsPool, nil 1049 } 1050 1051 // TODO(axw) add env configuration for default 1052 // filesystem source, prefer that. 1053 defaultPool, ok := cfg.StorageDefaultBlockSource() 1054 if !ok { 1055 defaultPool = rootfsPool 1056 } 1057 return defaultPool, nil 1058 } 1059 return "", ErrNoDefaultStoragePool 1060 } 1061 1062 // AddStorageForUnit adds storage instances to given unit as specified. 1063 // Missing storage constraints are populated 1064 // based on model defaults. Storage store name is used to retrieve 1065 // existing storage instances for this store. 1066 // Combination of existing storage instances and 1067 // anticipated additional storage instances is validated against storage 1068 // store as specified in the charm. 1069 func (st *State) AddStorageForUnit( 1070 tag names.UnitTag, name string, cons StorageConstraints, 1071 ) error { 1072 u, err := st.Unit(tag.Id()) 1073 if err != nil { 1074 return errors.Trace(err) 1075 } 1076 1077 s, err := u.Service() 1078 if err != nil { 1079 return errors.Annotatef(err, "getting service for unit %v", u.Tag().Id()) 1080 } 1081 ch, _, err := s.Charm() 1082 if err != nil { 1083 return errors.Annotatef(err, "getting charm for unit %q", u.Tag().Id()) 1084 } 1085 1086 return st.addStorageForUnit(ch, u, name, cons) 1087 } 1088 1089 // addStorage adds storage instances to given unit as specified. 1090 func (st *State) addStorageForUnit( 1091 ch *Charm, u *Unit, 1092 name string, cons StorageConstraints, 1093 ) error { 1094 all, err := u.StorageConstraints() 1095 if err != nil { 1096 return errors.Annotatef(err, "getting existing storage directives for %s", u.Tag().Id()) 1097 } 1098 1099 // Check storage name was declared. 1100 _, exists := all[name] 1101 if !exists { 1102 return errors.NotFoundf("charm storage %q", name) 1103 } 1104 1105 // Populate missing configuration parameters with default values. 1106 conf, err := st.ModelConfig() 1107 if err != nil { 1108 return errors.Trace(err) 1109 } 1110 completeCons, err := storageConstraintsWithDefaults( 1111 conf, 1112 ch.Meta().Storage[name], 1113 name, cons, 1114 ) 1115 if err != nil { 1116 return errors.Trace(err) 1117 } 1118 1119 // This can happen for charm stores that specify instances range from 0, 1120 // and no count was specified at deploy as storage constraints for this store, 1121 // and no count was specified to storage add as a contraint either. 1122 if cons.Count == 0 { 1123 return errors.NotValidf("adding storage where instance count is 0") 1124 } 1125 1126 buildTxn := func(attempt int) ([]txn.Op, error) { 1127 if attempt > 0 { 1128 if err := u.Refresh(); err != nil { 1129 return nil, errors.Trace(err) 1130 } 1131 } 1132 if u.Life() != Alive { 1133 return nil, unitNotAliveErr 1134 } 1135 err = st.validateUnitStorage(ch.Meta(), u, name, completeCons) 1136 if err != nil { 1137 return nil, errors.Trace(err) 1138 } 1139 ops, err := st.constructAddUnitStorageOps(ch, u, name, completeCons) 1140 if err != nil { 1141 return nil, errors.Trace(err) 1142 } 1143 return ops, nil 1144 } 1145 if err := st.run(buildTxn); err != nil { 1146 return errors.Annotatef(err, "adding storage to unit %s", u) 1147 } 1148 return nil 1149 } 1150 1151 func (st *State) validateUnitStorage( 1152 charmMeta *charm.Meta, u *Unit, name string, cons StorageConstraints, 1153 ) error { 1154 // Storage directive may provide storage instance count 1155 // which combined with existing storage instance may exceed 1156 // number of storage instances specified by charm. 1157 // We must take it into account when validating. 1158 currentCount, err := st.countEntityStorageInstancesForName(u.Tag(), name) 1159 if err != nil { 1160 return errors.Trace(err) 1161 } 1162 cons.Count = cons.Count + currentCount 1163 1164 err = validateStorageConstraintsAgainstCharm( 1165 st, 1166 map[string]StorageConstraints{name: cons}, 1167 charmMeta) 1168 if err != nil { 1169 return errors.Trace(err) 1170 } 1171 return nil 1172 } 1173 1174 func (st *State) constructAddUnitStorageOps( 1175 ch *Charm, u *Unit, name string, cons StorageConstraints, 1176 ) ([]txn.Op, error) { 1177 // Create storage db operations 1178 storageOps, _, err := createStorageOps( 1179 st, 1180 u.Tag(), 1181 ch.Meta(), 1182 ch.URL(), 1183 map[string]StorageConstraints{name: cons}, 1184 u.Series(), 1185 true, // create machine storage 1186 ) 1187 if err != nil { 1188 return nil, errors.Trace(err) 1189 } 1190 1191 // Update storage attachment count. 1192 priorCount := u.doc.StorageAttachmentCount 1193 newCount := priorCount + int(cons.Count) 1194 1195 attachmentsUnchanged := bson.D{{"storageattachmentcount", priorCount}} 1196 ops := []txn.Op{{ 1197 C: unitsC, 1198 Id: u.doc.DocID, 1199 Assert: append(attachmentsUnchanged, isAliveDoc...), 1200 Update: bson.D{{"$set", 1201 bson.D{{"storageattachmentcount", newCount}}}}, 1202 }} 1203 return append(ops, storageOps...), nil 1204 } 1205 1206 func (st *State) countEntityStorageInstancesForName( 1207 tag names.Tag, 1208 name string, 1209 ) (uint64, error) { 1210 storageCollection, closer := st.getCollection(storageInstancesC) 1211 defer closer() 1212 criteria := bson.D{{ 1213 "$and", []bson.D{ 1214 bson.D{{"owner", tag.String()}}, 1215 bson.D{{"storagename", name}}, 1216 }, 1217 }} 1218 result, err := storageCollection.Find(criteria).Count() 1219 if err != nil { 1220 return 0, err 1221 } 1222 return uint64(result), err 1223 }