github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/unit.go (about) 1 // Copyright 2012-2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "fmt" 8 "time" 9 10 "github.com/juju/errors" 11 "github.com/juju/loggo" 12 "github.com/juju/names" 13 jujutxn "github.com/juju/txn" 14 "github.com/juju/utils" 15 "github.com/juju/utils/set" 16 "github.com/juju/version" 17 "gopkg.in/juju/charm.v6-unstable" 18 "gopkg.in/mgo.v2" 19 "gopkg.in/mgo.v2/bson" 20 "gopkg.in/mgo.v2/txn" 21 22 "github.com/juju/juju/constraints" 23 "github.com/juju/juju/core/actions" 24 "github.com/juju/juju/instance" 25 "github.com/juju/juju/network" 26 "github.com/juju/juju/state/presence" 27 "github.com/juju/juju/status" 28 "github.com/juju/juju/tools" 29 ) 30 31 var unitLogger = loggo.GetLogger("juju.state.unit") 32 33 // AssignmentPolicy controls what machine a unit will be assigned to. 34 type AssignmentPolicy string 35 36 const ( 37 // AssignLocal indicates that all service units should be assigned 38 // to machine 0. 39 AssignLocal AssignmentPolicy = "local" 40 41 // AssignClean indicates that every service unit should be assigned 42 // to a machine which never previously has hosted any units, and that 43 // new machines should be launched if required. 44 AssignClean AssignmentPolicy = "clean" 45 46 // AssignCleanEmpty indicates that every service unit should be assigned 47 // to a machine which never previously has hosted any units, and which is not 48 // currently hosting any containers, and that new machines should be launched if required. 49 AssignCleanEmpty AssignmentPolicy = "clean-empty" 50 51 // AssignNew indicates that every service unit should be assigned to a new 52 // dedicated machine. A new machine will be launched for each new unit. 53 AssignNew AssignmentPolicy = "new" 54 ) 55 56 // ResolvedMode describes the way state transition errors 57 // are resolved. 58 type ResolvedMode string 59 60 // These are available ResolvedMode values. 61 const ( 62 ResolvedNone ResolvedMode = "" 63 ResolvedRetryHooks ResolvedMode = "retry-hooks" 64 ResolvedNoHooks ResolvedMode = "no-hooks" 65 ) 66 67 // port identifies a network port number for a particular protocol. 68 // TODO(mue) Not really used anymore, se bellow. Can be removed when 69 // cleaning unitDoc. 70 type port struct { 71 Protocol string `bson:"protocol"` 72 Number int `bson:"number"` 73 } 74 75 // unitDoc represents the internal state of a unit in MongoDB. 76 // Note the correspondence with UnitInfo in apiserver/params. 77 type unitDoc struct { 78 DocID string `bson:"_id"` 79 Name string `bson:"name"` 80 ModelUUID string `bson:"model-uuid"` 81 Service string 82 Series string 83 CharmURL *charm.URL 84 Principal string 85 Subordinates []string 86 StorageAttachmentCount int `bson:"storageattachmentcount"` 87 MachineId string 88 Resolved ResolvedMode 89 Tools *tools.Tools `bson:",omitempty"` 90 Life Life 91 TxnRevno int64 `bson:"txn-revno"` 92 PasswordHash string 93 94 // TODO(mue) No longer actively used, only in upgrades.go. 95 // To be removed later. 96 Ports []port `bson:"ports"` 97 PublicAddress string `bson:"publicaddress"` 98 PrivateAddress string `bson:"privateaddress"` 99 } 100 101 // Unit represents the state of a service unit. 102 type Unit struct { 103 st *State 104 doc unitDoc 105 } 106 107 func newUnit(st *State, udoc *unitDoc) *Unit { 108 unit := &Unit{ 109 st: st, 110 doc: *udoc, 111 } 112 return unit 113 } 114 115 // Service returns the service. 116 func (u *Unit) Service() (*Service, error) { 117 return u.st.Service(u.doc.Service) 118 } 119 120 // ConfigSettings returns the complete set of service charm config settings 121 // available to the unit. Unset values will be replaced with the default 122 // value for the associated option, and may thus be nil when no default is 123 // specified. 124 func (u *Unit) ConfigSettings() (charm.Settings, error) { 125 if u.doc.CharmURL == nil { 126 return nil, fmt.Errorf("unit charm not set") 127 } 128 settings, err := readSettings(u.st, serviceSettingsKey(u.doc.Service, u.doc.CharmURL)) 129 if err != nil { 130 return nil, err 131 } 132 chrm, err := u.st.Charm(u.doc.CharmURL) 133 if err != nil { 134 return nil, err 135 } 136 result := chrm.Config().DefaultSettings() 137 for name, value := range settings.Map() { 138 result[name] = value 139 } 140 return result, nil 141 } 142 143 // ServiceName returns the service name. 144 func (u *Unit) ServiceName() string { 145 return u.doc.Service 146 } 147 148 // Series returns the deployed charm's series. 149 func (u *Unit) Series() string { 150 return u.doc.Series 151 } 152 153 // String returns the unit as string. 154 func (u *Unit) String() string { 155 return u.doc.Name 156 } 157 158 // Name returns the unit name. 159 func (u *Unit) Name() string { 160 return u.doc.Name 161 } 162 163 // unitGlobalKey returns the global database key for the named unit. 164 func unitGlobalKey(name string) string { 165 return "u#" + name + "#charm" 166 } 167 168 // globalAgentKey returns the global database key for the unit. 169 func (u *Unit) globalAgentKey() string { 170 return unitAgentGlobalKey(u.doc.Name) 171 } 172 173 // globalMeterStatusKey returns the global database key for the meter status of the unit. 174 func (u *Unit) globalMeterStatusKey() string { 175 return unitAgentGlobalKey(u.doc.Name) 176 } 177 178 // globalKey returns the global database key for the unit. 179 func (u *Unit) globalKey() string { 180 return unitGlobalKey(u.doc.Name) 181 } 182 183 // Life returns whether the unit is Alive, Dying or Dead. 184 func (u *Unit) Life() Life { 185 return u.doc.Life 186 } 187 188 // AgentTools returns the tools that the agent is currently running. 189 // It an error that satisfies errors.IsNotFound if the tools have not 190 // yet been set. 191 func (u *Unit) AgentTools() (*tools.Tools, error) { 192 if u.doc.Tools == nil { 193 return nil, errors.NotFoundf("agent tools for unit %q", u) 194 } 195 tools := *u.doc.Tools 196 return &tools, nil 197 } 198 199 // SetAgentVersion sets the version of juju that the agent is 200 // currently running. 201 func (u *Unit) SetAgentVersion(v version.Binary) (err error) { 202 defer errors.DeferredAnnotatef(&err, "cannot set agent version for unit %q", u) 203 if err = checkVersionValidity(v); err != nil { 204 return err 205 } 206 tools := &tools.Tools{Version: v} 207 ops := []txn.Op{{ 208 C: unitsC, 209 Id: u.doc.DocID, 210 Assert: notDeadDoc, 211 Update: bson.D{{"$set", bson.D{{"tools", tools}}}}, 212 }} 213 if err := u.st.runTransaction(ops); err != nil { 214 return onAbort(err, ErrDead) 215 } 216 u.doc.Tools = tools 217 return nil 218 } 219 220 // SetPassword sets the password for the machine's agent. 221 func (u *Unit) SetPassword(password string) error { 222 if len(password) < utils.MinAgentPasswordLength { 223 return fmt.Errorf("password is only %d bytes long, and is not a valid Agent password", len(password)) 224 } 225 return u.setPasswordHash(utils.AgentPasswordHash(password)) 226 } 227 228 // setPasswordHash sets the underlying password hash in the database directly 229 // to the value supplied. This is split out from SetPassword to allow direct 230 // manipulation in tests (to check for backwards compatibility). 231 func (u *Unit) setPasswordHash(passwordHash string) error { 232 ops := []txn.Op{{ 233 C: unitsC, 234 Id: u.doc.DocID, 235 Assert: notDeadDoc, 236 Update: bson.D{{"$set", bson.D{{"passwordhash", passwordHash}}}}, 237 }} 238 err := u.st.runTransaction(ops) 239 if err != nil { 240 return fmt.Errorf("cannot set password of unit %q: %v", u, onAbort(err, ErrDead)) 241 } 242 u.doc.PasswordHash = passwordHash 243 return nil 244 } 245 246 // Return the underlying PasswordHash stored in the database. Used by the test 247 // suite to check that the PasswordHash gets properly updated to new values 248 // when compatibility mode is detected. 249 func (u *Unit) getPasswordHash() string { 250 return u.doc.PasswordHash 251 } 252 253 // PasswordValid returns whether the given password is valid 254 // for the given unit. 255 func (u *Unit) PasswordValid(password string) bool { 256 agentHash := utils.AgentPasswordHash(password) 257 if agentHash == u.doc.PasswordHash { 258 return true 259 } 260 return false 261 } 262 263 // Destroy, when called on a Alive unit, advances its lifecycle as far as 264 // possible; it otherwise has no effect. In most situations, the unit's 265 // life is just set to Dying; but if a principal unit that is not assigned 266 // to a provisioned machine is Destroyed, it will be removed from state 267 // directly. 268 func (u *Unit) Destroy() (err error) { 269 defer func() { 270 if err == nil { 271 // This is a white lie; the document might actually be removed. 272 u.doc.Life = Dying 273 } 274 }() 275 unit := &Unit{st: u.st, doc: u.doc} 276 buildTxn := func(attempt int) ([]txn.Op, error) { 277 if attempt > 0 { 278 if err := unit.Refresh(); errors.IsNotFound(err) { 279 return nil, jujutxn.ErrNoOperations 280 } else if err != nil { 281 return nil, err 282 } 283 } 284 switch ops, err := unit.destroyOps(); err { 285 case errRefresh: 286 case errAlreadyDying: 287 return nil, jujutxn.ErrNoOperations 288 case nil: 289 return ops, nil 290 default: 291 return nil, err 292 } 293 return nil, jujutxn.ErrNoOperations 294 } 295 if err = unit.st.run(buildTxn); err == nil { 296 if historyErr := unit.eraseHistory(); historyErr != nil { 297 logger.Errorf("cannot delete history for unit %q: %v", unit.globalKey(), err) 298 } 299 if err = unit.Refresh(); errors.IsNotFound(err) { 300 return nil 301 } 302 } 303 return err 304 } 305 306 func (u *Unit) eraseHistory() error { 307 history, closer := u.st.getCollection(statusesHistoryC) 308 defer closer() 309 // XXX(fwereade): 2015-06-19 this is anything but safe: we must not mix 310 // txn and non-txn operations in the same collection without clear and 311 // detailed reasoning for so doing. 312 historyW := history.Writeable() 313 314 if _, err := historyW.RemoveAll(bson.D{{"statusid", u.globalKey()}}); err != nil { 315 return err 316 } 317 if _, err := historyW.RemoveAll(bson.D{{"statusid", u.globalAgentKey()}}); err != nil { 318 return err 319 } 320 return nil 321 } 322 323 // destroyOps returns the operations required to destroy the unit. If it 324 // returns errRefresh, the unit should be refreshed and the destruction 325 // operations recalculated. 326 func (u *Unit) destroyOps() ([]txn.Op, error) { 327 if u.doc.Life != Alive { 328 return nil, errAlreadyDying 329 } 330 331 // Where possible, we'd like to be able to short-circuit unit destruction 332 // such that units can be removed directly rather than waiting for their 333 // agents to start, observe Dying, set Dead, and shut down; this takes a 334 // long time and is vexing to users. This turns out to be possible if and 335 // only if the unit agent has not yet set its status; this implies that the 336 // most the unit could possibly have done is to run its install hook. 337 // 338 // There's no harm in removing a unit that's run its install hook only -- 339 // or, at least, there is no more harm than there is in removing a unit 340 // that's run its stop hook, and that's the usual condition. 341 // 342 // Principals with subordinates are never eligible for this shortcut, 343 // because the unit agent must inevitably have set a status before getting 344 // to the point where it can actually create its subordinate. 345 // 346 // Subordinates should be eligible for the shortcut but are not currently 347 // considered, on the basis that (1) they were created by active principals 348 // and can be expected to be deployed pretty soon afterwards, so we don't 349 // lose much time and (2) by maintaining this restriction, I can reduce 350 // the number of tests that have to change and defer that improvement to 351 // its own CL. 352 minUnitsOp := minUnitsTriggerOp(u.st, u.ServiceName()) 353 cleanupOp := u.st.newCleanupOp(cleanupDyingUnit, u.doc.Name) 354 setDyingOp := txn.Op{ 355 C: unitsC, 356 Id: u.doc.DocID, 357 Assert: isAliveDoc, 358 Update: bson.D{{"$set", bson.D{{"life", Dying}}}}, 359 } 360 setDyingOps := []txn.Op{setDyingOp, cleanupOp, minUnitsOp} 361 if u.doc.Principal != "" { 362 return setDyingOps, nil 363 } else if len(u.doc.Subordinates)+u.doc.StorageAttachmentCount != 0 { 364 return setDyingOps, nil 365 } 366 367 // See if the unit agent has started running. 368 // If so then we can't set directly to dead. 369 agentStatusDocId := u.globalAgentKey() 370 agentStatusInfo, agentErr := getStatus(u.st, agentStatusDocId, "agent") 371 if errors.IsNotFound(agentErr) { 372 return nil, errAlreadyDying 373 } else if agentErr != nil { 374 return nil, errors.Trace(agentErr) 375 } 376 if agentStatusInfo.Status != status.StatusAllocating { 377 return setDyingOps, nil 378 } 379 380 ops := []txn.Op{{ 381 C: statusesC, 382 Id: u.st.docID(agentStatusDocId), 383 Assert: bson.D{{"status", status.StatusAllocating}}, 384 }, minUnitsOp} 385 removeAsserts := append(isAliveDoc, bson.DocElem{ 386 "$and", []bson.D{ 387 unitHasNoSubordinates, 388 unitHasNoStorageAttachments, 389 }, 390 }) 391 removeOps, err := u.removeOps(removeAsserts) 392 if err == errAlreadyRemoved { 393 return nil, errAlreadyDying 394 } else if err != nil { 395 return nil, err 396 } 397 return append(ops, removeOps...), nil 398 } 399 400 // destroyHostOps returns all necessary operations to destroy the service unit's host machine, 401 // or ensure that the conditions preventing its destruction remain stable through the transaction. 402 func (u *Unit) destroyHostOps(s *Service) (ops []txn.Op, err error) { 403 if s.doc.Subordinate { 404 return []txn.Op{{ 405 C: unitsC, 406 Id: u.st.docID(u.doc.Principal), 407 Assert: txn.DocExists, 408 Update: bson.D{{"$pull", bson.D{{"subordinates", u.doc.Name}}}}, 409 }}, nil 410 } else if u.doc.MachineId == "" { 411 unitLogger.Errorf("unit %v unassigned", u) 412 return nil, nil 413 } 414 415 machineUpdate := bson.D{{"$pull", bson.D{{"principals", u.doc.Name}}}} 416 417 m, err := u.st.Machine(u.doc.MachineId) 418 if err != nil { 419 if errors.IsNotFound(err) { 420 return nil, nil 421 } 422 return nil, err 423 } 424 425 containerCheck := true // whether container conditions allow destroying the host machine 426 containers, err := m.Containers() 427 if err != nil { 428 return nil, err 429 } 430 if len(containers) > 0 { 431 ops = append(ops, txn.Op{ 432 C: containerRefsC, 433 Id: m.doc.DocID, 434 Assert: bson.D{{"children.0", bson.D{{"$exists", 1}}}}, 435 }) 436 containerCheck = false 437 } else { 438 ops = append(ops, txn.Op{ 439 C: containerRefsC, 440 Id: m.doc.DocID, 441 Assert: bson.D{{"$or", []bson.D{ 442 {{"children", bson.D{{"$size", 0}}}}, 443 {{"children", bson.D{{"$exists", false}}}}, 444 }}}, 445 }) 446 } 447 448 machineCheck := true // whether host machine conditions allow destroy 449 if len(m.doc.Principals) != 1 || m.doc.Principals[0] != u.doc.Name { 450 machineCheck = false 451 } else if hasJob(m.doc.Jobs, JobManageModel) { 452 // Check that the machine does not have any responsibilities that 453 // prevent a lifecycle change. 454 machineCheck = false 455 } else if m.doc.HasVote { 456 machineCheck = false 457 } 458 459 // assert that the machine conditions pertaining to host removal conditions 460 // remain the same throughout the transaction. 461 var machineAssert bson.D 462 if machineCheck { 463 machineAssert = bson.D{{"$and", []bson.D{ 464 {{"principals", []string{u.doc.Name}}}, 465 {{"jobs", bson.D{{"$nin", []MachineJob{JobManageModel}}}}}, 466 {{"hasvote", bson.D{{"$ne", true}}}}, 467 }}} 468 } else { 469 machineAssert = bson.D{{"$or", []bson.D{ 470 {{"principals", bson.D{{"$ne", []string{u.doc.Name}}}}}, 471 {{"jobs", bson.D{{"$in", []MachineJob{JobManageModel}}}}}, 472 {{"hasvote", true}}, 473 }}} 474 } 475 476 // If removal conditions satisfied by machine & container docs, we can 477 // destroy it, in addition to removing the unit principal. 478 if machineCheck && containerCheck { 479 machineUpdate = append(machineUpdate, bson.D{{"$set", bson.D{{"life", Dying}}}}...) 480 } 481 482 ops = append(ops, txn.Op{ 483 C: machinesC, 484 Id: m.doc.DocID, 485 Assert: machineAssert, 486 Update: machineUpdate, 487 }) 488 return ops, nil 489 } 490 491 var errAlreadyRemoved = errors.New("entity has already been removed") 492 493 // removeOps returns the operations necessary to remove the unit, assuming 494 // the supplied asserts apply to the unit document. 495 func (u *Unit) removeOps(asserts bson.D) ([]txn.Op, error) { 496 svc, err := u.st.Service(u.doc.Service) 497 if errors.IsNotFound(err) { 498 // If the service has been removed, the unit must already have been. 499 return nil, errAlreadyRemoved 500 } else if err != nil { 501 return nil, err 502 } 503 return svc.removeUnitOps(u, asserts) 504 } 505 506 // ErrUnitHasSubordinates is a standard error to indicate that a Unit 507 // cannot complete an operation to end its life because it still has 508 // subordinate services 509 var ErrUnitHasSubordinates = errors.New("unit has subordinates") 510 511 var unitHasNoSubordinates = bson.D{{ 512 "$or", []bson.D{ 513 {{"subordinates", bson.D{{"$size", 0}}}}, 514 {{"subordinates", bson.D{{"$exists", false}}}}, 515 }, 516 }} 517 518 // ErrUnitHasStorageAttachments is a standard error to indicate that 519 // a Unit cannot complete an operation to end its life because it still 520 // has storage attachments. 521 var ErrUnitHasStorageAttachments = errors.New("unit has storage attachments") 522 523 var unitHasNoStorageAttachments = bson.D{{ 524 "$or", []bson.D{ 525 {{"storageattachmentcount", 0}}, 526 {{"storageattachmentcount", bson.D{{"$exists", false}}}}, 527 }, 528 }} 529 530 // EnsureDead sets the unit lifecycle to Dead if it is Alive or Dying. 531 // It does nothing otherwise. If the unit has subordinates, it will 532 // return ErrUnitHasSubordinates; otherwise, if it has storage instances, 533 // it will return ErrUnitHasStorageInstances. 534 func (u *Unit) EnsureDead() (err error) { 535 if u.doc.Life == Dead { 536 return nil 537 } 538 defer func() { 539 if err == nil { 540 u.doc.Life = Dead 541 } 542 }() 543 assert := append(notDeadDoc, bson.DocElem{ 544 "$and", []bson.D{ 545 unitHasNoSubordinates, 546 unitHasNoStorageAttachments, 547 }, 548 }) 549 ops := []txn.Op{{ 550 C: unitsC, 551 Id: u.doc.DocID, 552 Assert: assert, 553 Update: bson.D{{"$set", bson.D{{"life", Dead}}}}, 554 }} 555 if err := u.st.runTransaction(ops); err != txn.ErrAborted { 556 return err 557 } 558 if notDead, err := isNotDead(u.st, unitsC, u.doc.DocID); err != nil { 559 return err 560 } else if !notDead { 561 return nil 562 } 563 if err := u.Refresh(); errors.IsNotFound(err) { 564 return nil 565 } else if err != nil { 566 return err 567 } 568 if len(u.doc.Subordinates) > 0 { 569 return ErrUnitHasSubordinates 570 } 571 return ErrUnitHasStorageAttachments 572 } 573 574 // Remove removes the unit from state, and may remove its service as well, if 575 // the service is Dying and no other references to it exist. It will fail if 576 // the unit is not Dead. 577 func (u *Unit) Remove() (err error) { 578 defer errors.DeferredAnnotatef(&err, "cannot remove unit %q", u) 579 if u.doc.Life != Dead { 580 return errors.New("unit is not dead") 581 } 582 583 // Now the unit is Dead, we can be sure that it's impossible for it to 584 // enter relation scopes (once it's Dying, we can be sure of this; but 585 // EnsureDead does not require that it already be Dying, so this is the 586 // only point at which we can safely backstop lp:1233457 and mitigate 587 // the impact of unit agent bugs that leave relation scopes occupied). 588 relations, err := serviceRelations(u.st, u.doc.Service) 589 if err != nil { 590 return err 591 } 592 for _, rel := range relations { 593 ru, err := rel.Unit(u) 594 if err != nil { 595 return err 596 } 597 if err := ru.LeaveScope(); err != nil { 598 return err 599 } 600 } 601 602 // Now we're sure we haven't left any scopes occupied by this unit, we 603 // can safely remove the document. 604 unit := &Unit{st: u.st, doc: u.doc} 605 buildTxn := func(attempt int) ([]txn.Op, error) { 606 if attempt > 0 { 607 if err := unit.Refresh(); errors.IsNotFound(err) { 608 return nil, jujutxn.ErrNoOperations 609 } else if err != nil { 610 return nil, err 611 } 612 } 613 switch ops, err := unit.removeOps(isDeadDoc); err { 614 case errRefresh: 615 case errAlreadyDying: 616 return nil, jujutxn.ErrNoOperations 617 case nil: 618 return ops, nil 619 default: 620 return nil, err 621 } 622 return nil, jujutxn.ErrNoOperations 623 } 624 return unit.st.run(buildTxn) 625 } 626 627 // Resolved returns the resolved mode for the unit. 628 func (u *Unit) Resolved() ResolvedMode { 629 return u.doc.Resolved 630 } 631 632 // IsPrincipal returns whether the unit is deployed in its own container, 633 // and can therefore have subordinate services deployed alongside it. 634 func (u *Unit) IsPrincipal() bool { 635 return u.doc.Principal == "" 636 } 637 638 // SubordinateNames returns the names of any subordinate units. 639 func (u *Unit) SubordinateNames() []string { 640 names := make([]string, len(u.doc.Subordinates)) 641 copy(names, u.doc.Subordinates) 642 return names 643 } 644 645 // RelationsJoined returns the relations for which the unit has entered scope 646 // and neither left it nor prepared to leave it 647 func (u *Unit) RelationsJoined() ([]*Relation, error) { 648 return u.relations(func(ru *RelationUnit) (bool, error) { 649 return ru.Joined() 650 }) 651 } 652 653 // RelationsInScope returns the relations for which the unit has entered scope 654 // and not left it. 655 func (u *Unit) RelationsInScope() ([]*Relation, error) { 656 return u.relations(func(ru *RelationUnit) (bool, error) { 657 return ru.InScope() 658 }) 659 } 660 661 type relationPredicate func(ru *RelationUnit) (bool, error) 662 663 // relations implements RelationsJoined and RelationsInScope. 664 func (u *Unit) relations(predicate relationPredicate) ([]*Relation, error) { 665 candidates, err := serviceRelations(u.st, u.doc.Service) 666 if err != nil { 667 return nil, err 668 } 669 var filtered []*Relation 670 for _, relation := range candidates { 671 relationUnit, err := relation.Unit(u) 672 if err != nil { 673 return nil, err 674 } 675 if include, err := predicate(relationUnit); err != nil { 676 return nil, err 677 } else if include { 678 filtered = append(filtered, relation) 679 } 680 } 681 return filtered, nil 682 } 683 684 // DeployerTag returns the tag of the agent responsible for deploying 685 // the unit. If no such entity can be determined, false is returned. 686 func (u *Unit) DeployerTag() (names.Tag, bool) { 687 if u.doc.Principal != "" { 688 return names.NewUnitTag(u.doc.Principal), true 689 } else if u.doc.MachineId != "" { 690 return names.NewMachineTag(u.doc.MachineId), true 691 } 692 return nil, false 693 } 694 695 // PrincipalName returns the name of the unit's principal. 696 // If the unit is not a subordinate, false is returned. 697 func (u *Unit) PrincipalName() (string, bool) { 698 return u.doc.Principal, u.doc.Principal != "" 699 } 700 701 // machine returns the unit's machine. 702 func (u *Unit) machine() (*Machine, error) { 703 id, err := u.AssignedMachineId() 704 if err != nil { 705 return nil, errors.Annotatef(err, "unit %v cannot get assigned machine", u) 706 } 707 m, err := u.st.Machine(id) 708 if err != nil { 709 return nil, errors.Annotatef(err, "unit %v misses machine id %v", u, id) 710 } 711 return m, nil 712 } 713 714 // PublicAddress returns the public address of the unit. 715 func (u *Unit) PublicAddress() (network.Address, error) { 716 m, err := u.machine() 717 if err != nil { 718 unitLogger.Errorf("%v", err) 719 return network.Address{}, errors.Trace(err) 720 } 721 return m.PublicAddress() 722 } 723 724 // PrivateAddress returns the private address of the unit. 725 func (u *Unit) PrivateAddress() (network.Address, error) { 726 m, err := u.machine() 727 if err != nil { 728 unitLogger.Errorf("%v", err) 729 return network.Address{}, errors.Trace(err) 730 } 731 return m.PrivateAddress() 732 } 733 734 // AvailabilityZone returns the name of the availability zone into which 735 // the unit's machine instance was provisioned. 736 func (u *Unit) AvailabilityZone() (string, error) { 737 m, err := u.machine() 738 if err != nil { 739 return "", errors.Trace(err) 740 } 741 return m.AvailabilityZone() 742 } 743 744 // Refresh refreshes the contents of the Unit from the underlying 745 // state. It an error that satisfies errors.IsNotFound if the unit has 746 // been removed. 747 func (u *Unit) Refresh() error { 748 units, closer := u.st.getCollection(unitsC) 749 defer closer() 750 751 err := units.FindId(u.doc.DocID).One(&u.doc) 752 if err == mgo.ErrNotFound { 753 return errors.NotFoundf("unit %q", u) 754 } 755 if err != nil { 756 return fmt.Errorf("cannot refresh unit %q: %v", u, err) 757 } 758 return nil 759 } 760 761 // Agent Returns an agent by its unit's name. 762 func (u *Unit) Agent() *UnitAgent { 763 return newUnitAgent(u.st, u.Tag(), u.Name()) 764 } 765 766 // AgentHistory returns an StatusHistoryGetter which can 767 //be used to query the status history of the unit's agent. 768 func (u *Unit) AgentHistory() status.StatusHistoryGetter { 769 return u.Agent() 770 } 771 772 // SetAgentStatus calls SetStatus for this unit's agent, this call 773 // is equivalent to the former call to SetStatus when Agent and Unit 774 // where not separate entities. 775 func (u *Unit) SetAgentStatus(agentStatus status.Status, info string, data map[string]interface{}) error { 776 agent := newUnitAgent(u.st, u.Tag(), u.Name()) 777 return agent.SetStatus(agentStatus, info, data) 778 } 779 780 // AgentStatus calls Status for this unit's agent, this call 781 // is equivalent to the former call to Status when Agent and Unit 782 // where not separate entities. 783 func (u *Unit) AgentStatus() (status.StatusInfo, error) { 784 agent := newUnitAgent(u.st, u.Tag(), u.Name()) 785 return agent.Status() 786 } 787 788 // StatusHistory returns a slice of at most <size> StatusInfo items 789 // representing past statuses for this unit. 790 func (u *Unit) StatusHistory(size int) ([]status.StatusInfo, error) { 791 return statusHistory(u.st, u.globalKey(), size) 792 } 793 794 // Status returns the status of the unit. 795 // This method relies on globalKey instead of globalAgentKey since it is part of 796 // the effort to separate Unit from UnitAgent. Now the Status for UnitAgent is in 797 // the UnitAgent struct. 798 func (u *Unit) Status() (status.StatusInfo, error) { 799 // The current health spec says when a hook error occurs, the workload should 800 // be in error state, but the state model more correctly records the agent 801 // itself as being in error. So we'll do that model translation here. 802 // TODO(fwereade) as on unitagent, this transformation does not belong here. 803 // For now, pretend we're always reading the unit status. 804 info, err := getStatus(u.st, u.globalAgentKey(), "unit") 805 if err != nil { 806 return status.StatusInfo{}, err 807 } 808 if info.Status != status.StatusError { 809 info, err = getStatus(u.st, u.globalKey(), "unit") 810 if err != nil { 811 return status.StatusInfo{}, err 812 } 813 } 814 return info, nil 815 } 816 817 // SetStatus sets the status of the unit agent. The optional values 818 // allow to pass additional helpful status data. 819 // This method relies on globalKey instead of globalAgentKey since it is part of 820 // the effort to separate Unit from UnitAgent. Now the SetStatus for UnitAgent is in 821 // the UnitAgent struct. 822 func (u *Unit) SetStatus(unitStatus status.Status, info string, data map[string]interface{}) error { 823 if !status.ValidWorkloadStatus(unitStatus) { 824 return errors.Errorf("cannot set invalid status %q", unitStatus) 825 } 826 return setStatus(u.st, setStatusParams{ 827 badge: "unit", 828 globalKey: u.globalKey(), 829 status: unitStatus, 830 message: info, 831 rawData: data, 832 }) 833 } 834 835 // OpenPortsOnSubnet opens the given port range and protocol for the unit on the 836 // given subnet, which can be empty. When non-empty, subnetID must refer to an 837 // existing, alive subnet, otherwise an error is returned. Returns an error if 838 // opening the requested range conflicts with another already opened range on 839 // the same subnet and and the unit's assigned machine. 840 func (u *Unit) OpenPortsOnSubnet(subnetID, protocol string, fromPort, toPort int) (err error) { 841 ports, err := NewPortRange(u.Name(), fromPort, toPort, protocol) 842 if err != nil { 843 return errors.Annotatef(err, "invalid port range %v-%v/%v", fromPort, toPort, protocol) 844 } 845 defer errors.DeferredAnnotatef(&err, "cannot open ports %v for unit %q on subnet %q", ports, u, subnetID) 846 847 machineID, err := u.AssignedMachineId() 848 if err != nil { 849 return errors.Annotatef(err, "unit %q has no assigned machine", u) 850 } 851 852 if err := u.checkSubnetAliveWhenSet(subnetID); err != nil { 853 return errors.Trace(err) 854 } 855 856 machinePorts, err := getOrCreatePorts(u.st, machineID, subnetID) 857 if err != nil { 858 return errors.Annotate(err, "cannot get or create ports") 859 } 860 861 return machinePorts.OpenPorts(ports) 862 } 863 864 func (u *Unit) checkSubnetAliveWhenSet(subnetID string) error { 865 if subnetID == "" { 866 return nil 867 } else if !names.IsValidSubnet(subnetID) { 868 return errors.Errorf("invalid subnet ID %q", subnetID) 869 } 870 871 subnet, err := u.st.Subnet(subnetID) 872 if err != nil && !errors.IsNotFound(err) { 873 return errors.Annotatef(err, "getting subnet %q", subnetID) 874 } else if errors.IsNotFound(err) || subnet.Life() != Alive { 875 return errors.Errorf("subnet %q not found or not alive", subnetID) 876 } 877 return nil 878 } 879 880 // ClosePortsOnSubnet closes the given port range and protocol for the unit on 881 // the given subnet, which can be empty. When non-empty, subnetID must refer to 882 // an existing, alive subnet, otherwise an error is returned. 883 func (u *Unit) ClosePortsOnSubnet(subnetID, protocol string, fromPort, toPort int) (err error) { 884 ports, err := NewPortRange(u.Name(), fromPort, toPort, protocol) 885 if err != nil { 886 return errors.Annotatef(err, "invalid port range %v-%v/%v", fromPort, toPort, protocol) 887 } 888 defer errors.DeferredAnnotatef(&err, "cannot close ports %v for unit %q on subnet %q", ports, u, subnetID) 889 890 machineID, err := u.AssignedMachineId() 891 if err != nil { 892 return errors.Annotatef(err, "unit %q has no assigned machine", u) 893 } 894 895 if err := u.checkSubnetAliveWhenSet(subnetID); err != nil { 896 return errors.Trace(err) 897 } 898 899 machinePorts, err := getOrCreatePorts(u.st, machineID, subnetID) 900 if err != nil { 901 return errors.Annotate(err, "cannot get or create ports") 902 } 903 904 return machinePorts.ClosePorts(ports) 905 } 906 907 // OpenPorts opens the given port range and protocol for the unit, if it does 908 // not conflict with another already opened range on the unit's assigned 909 // machine. 910 // 911 // TODO(dimitern): This should be removed once we use OpenPortsOnSubnet across 912 // the board, passing subnet IDs explicitly. 913 func (u *Unit) OpenPorts(protocol string, fromPort, toPort int) error { 914 return u.OpenPortsOnSubnet("", protocol, fromPort, toPort) 915 } 916 917 // ClosePorts closes the given port range and protocol for the unit. 918 // 919 // TODO(dimitern): This should be removed once we use ClosePortsOnSubnet across 920 // the board, passing subnet IDs explicitly. 921 func (u *Unit) ClosePorts(protocol string, fromPort, toPort int) (err error) { 922 return u.ClosePortsOnSubnet("", protocol, fromPort, toPort) 923 } 924 925 // OpenPortOnSubnet opens the given port and protocol for the unit on the given 926 // subnet, which can be empty. When non-empty, subnetID must refer to an 927 // existing, alive subnet, otherwise an error is returned. 928 func (u *Unit) OpenPortOnSubnet(subnetID, protocol string, number int) error { 929 return u.OpenPortsOnSubnet(subnetID, protocol, number, number) 930 } 931 932 // ClosePortOnSubnet closes the given port and protocol for the unit on the given 933 // subnet, which can be empty. When non-empty, subnetID must refer to an 934 // existing, alive subnet, otherwise an error is returned. 935 func (u *Unit) ClosePortOnSubnet(subnetID, protocol string, number int) error { 936 return u.ClosePortsOnSubnet(subnetID, protocol, number, number) 937 } 938 939 // OpenPort opens the given port and protocol for the unit. 940 // 941 // TODO(dimitern): This should be removed once we use OpenPort(s)OnSubnet across 942 // the board, passing subnet IDs explicitly. 943 func (u *Unit) OpenPort(protocol string, number int) error { 944 return u.OpenPortOnSubnet("", protocol, number) 945 } 946 947 // ClosePort closes the given port and protocol for the unit. 948 // 949 // TODO(dimitern): This should be removed once we use ClosePortsOnSubnet across 950 // the board, passing subnet IDs explicitly. 951 func (u *Unit) ClosePort(protocol string, number int) error { 952 return u.ClosePortOnSubnet("", protocol, number) 953 } 954 955 // OpenedPortsOnSubnet returns a slice containing the open port ranges of the 956 // unit on the given subnet ID, which can be empty. When subnetID is not empty, 957 // it must refer to an existing, alive subnet, otherwise an error is returned. 958 // Also, when no ports are yet open for the unit on that subnet, no error and 959 // empty slice is returned. 960 func (u *Unit) OpenedPortsOnSubnet(subnetID string) ([]network.PortRange, error) { 961 machineID, err := u.AssignedMachineId() 962 if err != nil { 963 return nil, errors.Annotatef(err, "unit %q has no assigned machine", u) 964 } 965 966 if err := u.checkSubnetAliveWhenSet(subnetID); err != nil { 967 return nil, errors.Trace(err) 968 } 969 970 machinePorts, err := getPorts(u.st, machineID, subnetID) 971 result := []network.PortRange{} 972 if errors.IsNotFound(err) { 973 return result, nil 974 } else if err != nil { 975 return nil, errors.Annotatef(err, "failed getting ports for unit %q, subnet %q", u, subnetID) 976 } 977 ports := machinePorts.PortsForUnit(u.Name()) 978 for _, port := range ports { 979 result = append(result, network.PortRange{ 980 Protocol: port.Protocol, 981 FromPort: port.FromPort, 982 ToPort: port.ToPort, 983 }) 984 } 985 network.SortPortRanges(result) 986 return result, nil 987 } 988 989 // OpenedPorts returns a slice containing the open port ranges of the 990 // unit. 991 // 992 // TODO(dimitern): This should be removed once we use OpenedPortsOnSubnet across 993 // the board, passing subnet IDs explicitly. 994 func (u *Unit) OpenedPorts() ([]network.PortRange, error) { 995 return u.OpenedPortsOnSubnet("") 996 } 997 998 // CharmURL returns the charm URL this unit is currently using. 999 func (u *Unit) CharmURL() (*charm.URL, bool) { 1000 if u.doc.CharmURL == nil { 1001 return nil, false 1002 } 1003 return u.doc.CharmURL, true 1004 } 1005 1006 // SetCharmURL marks the unit as currently using the supplied charm URL. 1007 // An error will be returned if the unit is dead, or the charm URL not known. 1008 func (u *Unit) SetCharmURL(curl *charm.URL) error { 1009 if curl == nil { 1010 return fmt.Errorf("cannot set nil charm url") 1011 } 1012 1013 db, closer := u.st.newDB() 1014 defer closer() 1015 units, closer := db.GetCollection(unitsC) 1016 defer closer() 1017 charms, closer := db.GetCollection(charmsC) 1018 defer closer() 1019 1020 buildTxn := func(attempt int) ([]txn.Op, error) { 1021 if attempt > 0 { 1022 // NOTE: We're explicitly allowing SetCharmURL to succeed 1023 // when the unit is Dying, because service/charm upgrades 1024 // should still be allowed to apply to dying units, so 1025 // that bugs in departed/broken hooks can be addressed at 1026 // runtime. 1027 if notDead, err := isNotDeadWithSession(units, u.doc.DocID); err != nil { 1028 return nil, errors.Trace(err) 1029 } else if !notDead { 1030 return nil, ErrDead 1031 } 1032 } 1033 sel := bson.D{{"_id", u.doc.DocID}, {"charmurl", curl}} 1034 if count, err := units.Find(sel).Count(); err != nil { 1035 return nil, errors.Trace(err) 1036 } else if count == 1 { 1037 // Already set 1038 return nil, jujutxn.ErrNoOperations 1039 } 1040 if count, err := charms.FindId(curl.String()).Count(); err != nil { 1041 return nil, errors.Trace(err) 1042 } else if count < 1 { 1043 return nil, errors.Errorf("unknown charm url %q", curl) 1044 } 1045 1046 // Add a reference to the service settings for the new charm. 1047 incOp, err := settingsIncRefOp(u.st, u.doc.Service, curl, false) 1048 if err != nil { 1049 return nil, errors.Trace(err) 1050 } 1051 1052 // Set the new charm URL. 1053 differentCharm := bson.D{{"charmurl", bson.D{{"$ne", curl}}}} 1054 ops := []txn.Op{ 1055 incOp, 1056 { 1057 C: unitsC, 1058 Id: u.doc.DocID, 1059 Assert: append(notDeadDoc, differentCharm...), 1060 Update: bson.D{{"$set", bson.D{{"charmurl", curl}}}}, 1061 }} 1062 if u.doc.CharmURL != nil { 1063 // Drop the reference to the old charm. 1064 decOps, err := settingsDecRefOps(u.st, u.doc.Service, u.doc.CharmURL) 1065 if err != nil { 1066 return nil, errors.Trace(err) 1067 } 1068 ops = append(ops, decOps...) 1069 } 1070 return ops, nil 1071 } 1072 err := u.st.run(buildTxn) 1073 if err == nil { 1074 u.doc.CharmURL = curl 1075 } 1076 return err 1077 } 1078 1079 // AgentPresence returns whether the respective remote agent is alive. 1080 func (u *Unit) AgentPresence() (bool, error) { 1081 return u.st.pwatcher.Alive(u.globalAgentKey()) 1082 } 1083 1084 // Tag returns a name identifying the unit. 1085 // The returned name will be different from other Tag values returned by any 1086 // other entities from the same state. 1087 func (u *Unit) Tag() names.Tag { 1088 return u.UnitTag() 1089 } 1090 1091 // UnitTag returns a names.UnitTag representing this Unit, unless the 1092 // unit Name is invalid, in which case it will panic 1093 func (u *Unit) UnitTag() names.UnitTag { 1094 return names.NewUnitTag(u.Name()) 1095 } 1096 1097 // WaitAgentPresence blocks until the respective agent is alive. 1098 func (u *Unit) WaitAgentPresence(timeout time.Duration) (err error) { 1099 defer errors.DeferredAnnotatef(&err, "waiting for agent of unit %q", u) 1100 ch := make(chan presence.Change) 1101 u.st.pwatcher.Watch(u.globalAgentKey(), ch) 1102 defer u.st.pwatcher.Unwatch(u.globalAgentKey(), ch) 1103 for i := 0; i < 2; i++ { 1104 select { 1105 case change := <-ch: 1106 if change.Alive { 1107 return nil 1108 } 1109 case <-time.After(timeout): 1110 // TODO(fwereade): 2016-03-17 lp:1558657 1111 return fmt.Errorf("still not alive after timeout") 1112 case <-u.st.pwatcher.Dead(): 1113 return u.st.pwatcher.Err() 1114 } 1115 } 1116 panic(fmt.Sprintf("presence reported dead status twice in a row for unit %q", u)) 1117 } 1118 1119 // SetAgentPresence signals that the agent for unit u is alive. 1120 // It returns the started pinger. 1121 func (u *Unit) SetAgentPresence() (*presence.Pinger, error) { 1122 presenceCollection := u.st.getPresence() 1123 p := presence.NewPinger(presenceCollection, u.st.ModelTag(), u.globalAgentKey()) 1124 err := p.Start() 1125 if err != nil { 1126 return nil, err 1127 } 1128 return p, nil 1129 } 1130 1131 func unitNotAssignedError(u *Unit) error { 1132 msg := fmt.Sprintf("unit %q is not assigned to a machine", u) 1133 return errors.NewNotAssigned(nil, msg) 1134 } 1135 1136 // AssignedMachineId returns the id of the assigned machine. 1137 func (u *Unit) AssignedMachineId() (id string, err error) { 1138 if u.IsPrincipal() { 1139 if u.doc.MachineId == "" { 1140 return "", unitNotAssignedError(u) 1141 } 1142 return u.doc.MachineId, nil 1143 } 1144 1145 units, closer := u.st.getCollection(unitsC) 1146 defer closer() 1147 1148 pudoc := unitDoc{} 1149 err = units.FindId(u.doc.Principal).One(&pudoc) 1150 if err == mgo.ErrNotFound { 1151 return "", errors.NotFoundf("principal unit %q of %q", u.doc.Principal, u) 1152 } else if err != nil { 1153 return "", err 1154 } 1155 if pudoc.MachineId == "" { 1156 return "", unitNotAssignedError(u) 1157 } 1158 return pudoc.MachineId, nil 1159 } 1160 1161 var ( 1162 machineNotAliveErr = errors.New("machine is not alive") 1163 machineNotCleanErr = errors.New("machine is dirty") 1164 unitNotAliveErr = errors.New("unit is not alive") 1165 alreadyAssignedErr = errors.New("unit is already assigned to a machine") 1166 inUseErr = errors.New("machine is not unused") 1167 ) 1168 1169 // assignToMachine is the internal version of AssignToMachine, 1170 // also used by AssignToUnusedMachine. It returns specific errors 1171 // in some cases: 1172 // - machineNotAliveErr when the machine is not alive. 1173 // - unitNotAliveErr when the unit is not alive. 1174 // - alreadyAssignedErr when the unit has already been assigned 1175 // - inUseErr when the machine already has a unit assigned (if unused is true) 1176 func (u *Unit) assignToMachine(m *Machine, unused bool) (err error) { 1177 originalm := m 1178 buildTxn := func(attempt int) ([]txn.Op, error) { 1179 if attempt > 0 { 1180 m, err = u.st.Machine(m.Id()) 1181 if err != nil { 1182 return nil, errors.Trace(err) 1183 } 1184 } 1185 return u.assignToMachineOps(m, unused) 1186 } 1187 if err := u.st.run(buildTxn); err != nil { 1188 // Don't wrap the error, as we want to return specific values 1189 // as described in the doc comment. 1190 return err 1191 } 1192 u.doc.MachineId = originalm.doc.Id 1193 originalm.doc.Clean = false 1194 return nil 1195 } 1196 1197 func (u *Unit) assignToMachineOps(m *Machine, unused bool) ([]txn.Op, error) { 1198 if u.Life() != Alive { 1199 return nil, unitNotAliveErr 1200 } 1201 if u.doc.MachineId != "" { 1202 if u.doc.MachineId != m.Id() { 1203 return nil, alreadyAssignedErr 1204 } 1205 return nil, jujutxn.ErrNoOperations 1206 } 1207 if unused && !m.doc.Clean { 1208 return nil, inUseErr 1209 } 1210 storageParams, err := u.machineStorageParams() 1211 if err != nil { 1212 return nil, errors.Trace(err) 1213 } 1214 storagePools, err := machineStoragePools(m.st, storageParams) 1215 if err != nil { 1216 return nil, errors.Trace(err) 1217 } 1218 if err := validateUnitMachineAssignment( 1219 m, u.doc.Series, u.doc.Principal != "", storagePools, 1220 ); err != nil { 1221 return nil, errors.Trace(err) 1222 } 1223 storageOps, volumesAttached, filesystemsAttached, err := u.st.machineStorageOps( 1224 &m.doc, storageParams, 1225 ) 1226 if err != nil { 1227 return nil, errors.Trace(err) 1228 } 1229 // addMachineStorageAttachmentsOps will add a txn.Op that ensures 1230 // that no filesystems were concurrently added to the machine if 1231 // any of the filesystems being attached specify a location. 1232 attachmentOps, err := addMachineStorageAttachmentsOps( 1233 m, volumesAttached, filesystemsAttached, 1234 ) 1235 if err != nil { 1236 return nil, errors.Trace(err) 1237 } 1238 storageOps = append(storageOps, attachmentOps...) 1239 1240 assert := append(isAliveDoc, bson.D{ 1241 {"$or", []bson.D{ 1242 {{"machineid", ""}}, 1243 {{"machineid", m.Id()}}, 1244 }}, 1245 }...) 1246 massert := isAliveDoc 1247 if unused { 1248 massert = append(massert, bson.D{{"clean", bson.D{{"$ne", false}}}}...) 1249 } 1250 ops := []txn.Op{{ 1251 C: unitsC, 1252 Id: u.doc.DocID, 1253 Assert: assert, 1254 Update: bson.D{{"$set", bson.D{{"machineid", m.doc.Id}}}}, 1255 }, { 1256 C: machinesC, 1257 Id: m.doc.DocID, 1258 Assert: massert, 1259 Update: bson.D{{"$addToSet", bson.D{{"principals", u.doc.Name}}}, {"$set", bson.D{{"clean", false}}}}, 1260 }, 1261 removeStagedAssignmentOp(u.doc.DocID), 1262 } 1263 ops = append(ops, storageOps...) 1264 return ops, nil 1265 } 1266 1267 // validateUnitMachineAssignment validates the parameters for assigning a unit 1268 // to a specified machine. 1269 func validateUnitMachineAssignment( 1270 m *Machine, 1271 series string, 1272 isSubordinate bool, 1273 storagePools set.Strings, 1274 ) (err error) { 1275 if m.Life() != Alive { 1276 return machineNotAliveErr 1277 } 1278 if isSubordinate { 1279 return fmt.Errorf("unit is a subordinate") 1280 } 1281 if series != m.doc.Series { 1282 return fmt.Errorf("series does not match") 1283 } 1284 canHost := false 1285 for _, j := range m.doc.Jobs { 1286 if j == JobHostUnits { 1287 canHost = true 1288 break 1289 } 1290 } 1291 if !canHost { 1292 return fmt.Errorf("machine %q cannot host units", m) 1293 } 1294 if err := m.st.supportsUnitPlacement(); err != nil { 1295 return errors.Trace(err) 1296 } 1297 if err := validateDynamicMachineStoragePools(m, storagePools); err != nil { 1298 return errors.Trace(err) 1299 } 1300 return nil 1301 } 1302 1303 // validateDynamicMachineStorageParams validates that the provided machine 1304 // storage parameters are compatible with the specified machine. 1305 func validateDynamicMachineStorageParams(m *Machine, params *machineStorageParams) error { 1306 pools, err := machineStoragePools(m.st, params) 1307 if err != nil { 1308 return err 1309 } 1310 return validateDynamicMachineStoragePools(m, pools) 1311 } 1312 1313 // machineStoragePools returns the names of storage pools in each of the 1314 // volume, filesystem and attachments in the machine storage parameters. 1315 func machineStoragePools(st *State, params *machineStorageParams) (set.Strings, error) { 1316 pools := make(set.Strings) 1317 for _, v := range params.volumes { 1318 v, err := st.volumeParamsWithDefaults(v.Volume) 1319 if err != nil { 1320 return nil, errors.Trace(err) 1321 } 1322 pools.Add(v.Pool) 1323 } 1324 for _, f := range params.filesystems { 1325 f, err := st.filesystemParamsWithDefaults(f.Filesystem) 1326 if err != nil { 1327 return nil, errors.Trace(err) 1328 } 1329 pools.Add(f.Pool) 1330 } 1331 for volumeTag := range params.volumeAttachments { 1332 volume, err := st.Volume(volumeTag) 1333 if err != nil { 1334 return nil, errors.Trace(err) 1335 } 1336 if params, ok := volume.Params(); ok { 1337 pools.Add(params.Pool) 1338 } else { 1339 info, err := volume.Info() 1340 if err != nil { 1341 return nil, errors.Trace(err) 1342 } 1343 pools.Add(info.Pool) 1344 } 1345 } 1346 for filesystemTag := range params.filesystemAttachments { 1347 filesystem, err := st.Filesystem(filesystemTag) 1348 if err != nil { 1349 return nil, errors.Trace(err) 1350 } 1351 if params, ok := filesystem.Params(); ok { 1352 pools.Add(params.Pool) 1353 } else { 1354 info, err := filesystem.Info() 1355 if err != nil { 1356 return nil, errors.Trace(err) 1357 } 1358 pools.Add(info.Pool) 1359 } 1360 } 1361 return pools, nil 1362 } 1363 1364 // validateDynamicMachineStoragePools validates that all of the specified 1365 // storage pools support dynamic storage provisioning. If any provider doesn't 1366 // support dynamic storage, then an IsNotSupported error is returned. 1367 func validateDynamicMachineStoragePools(m *Machine, pools set.Strings) error { 1368 if pools.IsEmpty() { 1369 return nil 1370 } 1371 if m.ContainerType() != "" { 1372 // TODO(axw) consult storage providers to check if they 1373 // support adding storage to containers. Loop is fine, 1374 // for example. 1375 // 1376 // TODO(axw) later we might allow *any* storage, and 1377 // passthrough/bindmount storage. That would imply either 1378 // container creation time only, or requiring containers 1379 // to be restarted to pick up new configuration. 1380 return errors.NotSupportedf("adding storage to %s container", m.ContainerType()) 1381 } 1382 return validateDynamicStoragePools(m.st, pools) 1383 } 1384 1385 // validateDynamicStoragePools validates that all of the specified storage 1386 // providers support dynamic storage provisioning. If any provider doesn't 1387 // support dynamic storage, then an IsNotSupported error is returned. 1388 func validateDynamicStoragePools(st *State, pools set.Strings) error { 1389 for pool := range pools { 1390 providerType, provider, err := poolStorageProvider(st, pool) 1391 if err != nil { 1392 return errors.Trace(err) 1393 } 1394 if !provider.Dynamic() { 1395 return errors.NewNotSupported(err, fmt.Sprintf( 1396 "%q storage provider does not support dynamic storage", 1397 providerType, 1398 )) 1399 } 1400 } 1401 return nil 1402 } 1403 1404 func assignContextf(err *error, unitName string, target string) { 1405 if *err != nil { 1406 *err = errors.Annotatef(*err, 1407 "cannot assign unit %q to %s", 1408 unitName, target, 1409 ) 1410 } 1411 } 1412 1413 // AssignToMachine assigns this unit to a given machine. 1414 func (u *Unit) AssignToMachine(m *Machine) (err error) { 1415 defer assignContextf(&err, u.Name(), fmt.Sprintf("machine %s", m)) 1416 return u.assignToMachine(m, false) 1417 } 1418 1419 // assignToNewMachine assigns the unit to a machine created according to 1420 // the supplied params, with the supplied constraints. 1421 func (u *Unit) assignToNewMachine(template MachineTemplate, parentId string, containerType instance.ContainerType) error { 1422 template.principals = []string{u.doc.Name} 1423 template.Dirty = true 1424 1425 var ( 1426 mdoc *machineDoc 1427 ops []txn.Op 1428 err error 1429 ) 1430 switch { 1431 case parentId == "" && containerType == "": 1432 mdoc, ops, err = u.st.addMachineOps(template) 1433 case parentId == "": 1434 if containerType == "" { 1435 return fmt.Errorf("assignToNewMachine called without container type (should never happen)") 1436 } 1437 // The new parent machine is clean and only hosts units, 1438 // regardless of its child. 1439 parentParams := template 1440 parentParams.Jobs = []MachineJob{JobHostUnits} 1441 mdoc, ops, err = u.st.addMachineInsideNewMachineOps(template, parentParams, containerType) 1442 default: 1443 // Container type is specified but no parent id. 1444 mdoc, ops, err = u.st.addMachineInsideMachineOps(template, parentId, containerType) 1445 } 1446 if err != nil { 1447 return err 1448 } 1449 // Ensure the host machine is really clean. 1450 if parentId != "" { 1451 parentDocId := u.st.docID(parentId) 1452 ops = append(ops, txn.Op{ 1453 C: machinesC, 1454 Id: parentDocId, 1455 Assert: bson.D{{"clean", true}}, 1456 }, txn.Op{ 1457 C: containerRefsC, 1458 Id: parentDocId, 1459 Assert: bson.D{hasNoContainersTerm}, 1460 }) 1461 } 1462 isUnassigned := bson.D{{"machineid", ""}} 1463 1464 asserts := append(isAliveDoc, isUnassigned...) 1465 ops = append(ops, txn.Op{ 1466 C: unitsC, 1467 Id: u.doc.DocID, 1468 Assert: asserts, 1469 Update: bson.D{{"$set", bson.D{{"machineid", mdoc.Id}}}}, 1470 }, 1471 removeStagedAssignmentOp(u.doc.DocID), 1472 ) 1473 1474 err = u.st.runTransaction(ops) 1475 if err == nil { 1476 u.doc.MachineId = mdoc.Id 1477 return nil 1478 } else if err != txn.ErrAborted { 1479 return err 1480 } 1481 1482 // If we assume that the machine ops will never give us an 1483 // operation that would fail (because the machine id(s) that it 1484 // chooses are unique), then the only reasons that the 1485 // transaction could have been aborted are: 1486 // * the unit is no longer alive 1487 // * the unit has been assigned to a different machine 1488 // * the parent machine we want to create a container on was 1489 // clean but became dirty 1490 unit, err := u.st.Unit(u.Name()) 1491 if err != nil { 1492 return err 1493 } 1494 switch { 1495 case unit.Life() != Alive: 1496 return unitNotAliveErr 1497 case unit.doc.MachineId != "": 1498 return alreadyAssignedErr 1499 } 1500 if parentId == "" { 1501 return fmt.Errorf("cannot add top level machine: transaction aborted for unknown reason") 1502 } 1503 m, err := u.st.Machine(parentId) 1504 if err != nil { 1505 return err 1506 } 1507 if !m.Clean() { 1508 return machineNotCleanErr 1509 } 1510 containers, err := m.Containers() 1511 if err != nil { 1512 return err 1513 } 1514 if len(containers) > 0 { 1515 return machineNotCleanErr 1516 } 1517 return fmt.Errorf("cannot add container within machine: transaction aborted for unknown reason") 1518 } 1519 1520 // Constraints returns the unit's deployment constraints. 1521 func (u *Unit) Constraints() (*constraints.Value, error) { 1522 cons, err := readConstraints(u.st, u.globalAgentKey()) 1523 if errors.IsNotFound(err) { 1524 // Lack of constraints indicates lack of unit. 1525 return nil, errors.NotFoundf("unit") 1526 } else if err != nil { 1527 return nil, err 1528 } 1529 return &cons, nil 1530 } 1531 1532 // AssignToNewMachineOrContainer assigns the unit to a new machine, 1533 // with constraints determined according to the service and 1534 // model constraints at the time of unit creation. If a 1535 // container is required, a clean, empty machine instance is required 1536 // on which to create the container. An existing clean, empty instance 1537 // is first searched for, and if not found, a new one is created. 1538 func (u *Unit) AssignToNewMachineOrContainer() (err error) { 1539 defer assignContextf(&err, u.Name(), "new machine or container") 1540 if u.doc.Principal != "" { 1541 return fmt.Errorf("unit is a subordinate") 1542 } 1543 cons, err := u.Constraints() 1544 if err != nil { 1545 return err 1546 } 1547 if !cons.HasContainer() { 1548 return u.AssignToNewMachine() 1549 } 1550 1551 // Find a clean, empty machine on which to create a container. 1552 hostCons := *cons 1553 noContainer := instance.NONE 1554 hostCons.Container = &noContainer 1555 query, err := u.findCleanMachineQuery(true, &hostCons) 1556 if err != nil { 1557 return err 1558 } 1559 machinesCollection, closer := u.st.getCollection(machinesC) 1560 defer closer() 1561 var host machineDoc 1562 if err := machinesCollection.Find(query).One(&host); err == mgo.ErrNotFound { 1563 // No existing clean, empty machine so create a new one. 1564 // The container constraint will be used by AssignToNewMachine to create the required container. 1565 return u.AssignToNewMachine() 1566 } else if err != nil { 1567 return err 1568 } 1569 1570 template := MachineTemplate{ 1571 Series: u.doc.Series, 1572 Constraints: *cons, 1573 Jobs: []MachineJob{JobHostUnits}, 1574 } 1575 err = u.assignToNewMachine(template, host.Id, *cons.Container) 1576 if err == machineNotCleanErr { 1577 // The clean machine was used before we got a chance to use it so just 1578 // stick the unit on a new machine. 1579 return u.AssignToNewMachine() 1580 } 1581 return err 1582 } 1583 1584 // AssignToNewMachine assigns the unit to a new machine, with constraints 1585 // determined according to the service and model constraints at the 1586 // time of unit creation. 1587 func (u *Unit) AssignToNewMachine() (err error) { 1588 defer assignContextf(&err, u.Name(), "new machine") 1589 if u.doc.Principal != "" { 1590 return fmt.Errorf("unit is a subordinate") 1591 } 1592 // Get the ops necessary to create a new machine, and the machine doc that 1593 // will be added with those operations (which includes the machine id). 1594 cons, err := u.Constraints() 1595 if err != nil { 1596 return err 1597 } 1598 var containerType instance.ContainerType 1599 // Configure to create a new container if required. 1600 if cons.HasContainer() { 1601 containerType = *cons.Container 1602 } 1603 storageParams, err := u.machineStorageParams() 1604 if err != nil { 1605 return errors.Trace(err) 1606 } 1607 template := MachineTemplate{ 1608 Series: u.doc.Series, 1609 Constraints: *cons, 1610 Jobs: []MachineJob{JobHostUnits}, 1611 Volumes: storageParams.volumes, 1612 VolumeAttachments: storageParams.volumeAttachments, 1613 Filesystems: storageParams.filesystems, 1614 FilesystemAttachments: storageParams.filesystemAttachments, 1615 } 1616 return u.assignToNewMachine(template, "", containerType) 1617 } 1618 1619 // machineStorageParams returns parameters for creating volumes/filesystems 1620 // and volume/filesystem attachments for a machine that the unit will be 1621 // assigned to. 1622 func (u *Unit) machineStorageParams() (*machineStorageParams, error) { 1623 storageAttachments, err := u.st.UnitStorageAttachments(u.UnitTag()) 1624 if err != nil { 1625 return nil, errors.Annotate(err, "getting storage attachments") 1626 } 1627 svc, err := u.Service() 1628 if err != nil { 1629 return nil, errors.Trace(err) 1630 } 1631 curl, _ := svc.CharmURL() 1632 if curl == nil { 1633 return nil, errors.Errorf("no URL set for service %q", svc.Name()) 1634 } 1635 ch, err := u.st.Charm(curl) 1636 if err != nil { 1637 return nil, errors.Annotate(err, "getting charm") 1638 } 1639 allCons, err := u.StorageConstraints() 1640 if err != nil { 1641 return nil, errors.Annotatef(err, "getting storage constraints") 1642 } 1643 1644 chMeta := ch.Meta() 1645 1646 var volumes []MachineVolumeParams 1647 var filesystems []MachineFilesystemParams 1648 volumeAttachments := make(map[names.VolumeTag]VolumeAttachmentParams) 1649 filesystemAttachments := make(map[names.FilesystemTag]FilesystemAttachmentParams) 1650 for _, storageAttachment := range storageAttachments { 1651 storage, err := u.st.StorageInstance(storageAttachment.StorageInstance()) 1652 if err != nil { 1653 return nil, errors.Annotatef(err, "getting storage instance") 1654 } 1655 machineParams, err := machineStorageParamsForStorageInstance( 1656 u.st, chMeta, u.UnitTag(), u.Series(), allCons, storage, 1657 ) 1658 if err != nil { 1659 return nil, errors.Trace(err) 1660 } 1661 1662 volumes = append(volumes, machineParams.volumes...) 1663 for k, v := range machineParams.volumeAttachments { 1664 volumeAttachments[k] = v 1665 } 1666 1667 filesystems = append(filesystems, machineParams.filesystems...) 1668 for k, v := range machineParams.filesystemAttachments { 1669 filesystemAttachments[k] = v 1670 } 1671 } 1672 result := &machineStorageParams{ 1673 volumes, 1674 volumeAttachments, 1675 filesystems, 1676 filesystemAttachments, 1677 } 1678 return result, nil 1679 } 1680 1681 // machineStorageParamsForStorageInstance returns parameters for creating 1682 // volumes/filesystems and volume/filesystem attachments for a machine that 1683 // the unit will be assigned to. These parameters are based on a given storage 1684 // instance. 1685 func machineStorageParamsForStorageInstance( 1686 st *State, 1687 charmMeta *charm.Meta, 1688 unit names.UnitTag, 1689 series string, 1690 allCons map[string]StorageConstraints, 1691 storage StorageInstance, 1692 ) (*machineStorageParams, error) { 1693 1694 charmStorage := charmMeta.Storage[storage.StorageName()] 1695 1696 var volumes []MachineVolumeParams 1697 var filesystems []MachineFilesystemParams 1698 volumeAttachments := make(map[names.VolumeTag]VolumeAttachmentParams) 1699 filesystemAttachments := make(map[names.FilesystemTag]FilesystemAttachmentParams) 1700 1701 switch storage.Kind() { 1702 case StorageKindBlock: 1703 volumeAttachmentParams := VolumeAttachmentParams{ 1704 charmStorage.ReadOnly, 1705 } 1706 if unit == storage.Owner() { 1707 // The storage instance is owned by the unit, so we'll need 1708 // to create a volume. 1709 cons := allCons[storage.StorageName()] 1710 volumeParams := VolumeParams{ 1711 storage: storage.StorageTag(), 1712 binding: storage.StorageTag(), 1713 Pool: cons.Pool, 1714 Size: cons.Size, 1715 } 1716 volumes = append(volumes, MachineVolumeParams{ 1717 volumeParams, volumeAttachmentParams, 1718 }) 1719 } else { 1720 // The storage instance is owned by the service, so there 1721 // should be a (shared) volume already, for which we will 1722 // just add an attachment. 1723 volume, err := st.StorageInstanceVolume(storage.StorageTag()) 1724 if err != nil { 1725 return nil, errors.Annotatef(err, "getting volume for storage %q", storage.Tag().Id()) 1726 } 1727 volumeAttachments[volume.VolumeTag()] = volumeAttachmentParams 1728 } 1729 case StorageKindFilesystem: 1730 location, err := filesystemMountPoint(charmStorage, storage.StorageTag(), series) 1731 if err != nil { 1732 return nil, errors.Annotatef( 1733 err, "getting filesystem mount point for storage %s", 1734 storage.StorageName(), 1735 ) 1736 } 1737 filesystemAttachmentParams := FilesystemAttachmentParams{ 1738 charmStorage.Location == "", // auto-generated location 1739 location, 1740 charmStorage.ReadOnly, 1741 } 1742 if unit == storage.Owner() { 1743 // The storage instance is owned by the unit, so we'll need 1744 // to create a filesystem. 1745 cons := allCons[storage.StorageName()] 1746 filesystemParams := FilesystemParams{ 1747 storage: storage.StorageTag(), 1748 binding: storage.StorageTag(), 1749 Pool: cons.Pool, 1750 Size: cons.Size, 1751 } 1752 filesystems = append(filesystems, MachineFilesystemParams{ 1753 filesystemParams, filesystemAttachmentParams, 1754 }) 1755 } else { 1756 // The storage instance is owned by the service, so there 1757 // should be a (shared) filesystem already, for which we will 1758 // just add an attachment. 1759 filesystem, err := st.StorageInstanceFilesystem(storage.StorageTag()) 1760 if err != nil { 1761 return nil, errors.Annotatef(err, "getting filesystem for storage %q", storage.Tag().Id()) 1762 } 1763 filesystemAttachments[filesystem.FilesystemTag()] = filesystemAttachmentParams 1764 } 1765 default: 1766 return nil, errors.Errorf("invalid storage kind %v", storage.Kind()) 1767 } 1768 result := &machineStorageParams{ 1769 volumes, 1770 volumeAttachments, 1771 filesystems, 1772 filesystemAttachments, 1773 } 1774 return result, nil 1775 } 1776 1777 var noCleanMachines = errors.New("all eligible machines in use") 1778 1779 // AssignToCleanMachine assigns u to a machine which is marked as clean. A machine 1780 // is clean if it has never had any principal units assigned to it. 1781 // If there are no clean machines besides any machine(s) running JobHostEnviron, 1782 // an error is returned. 1783 // This method does not take constraints into consideration when choosing a 1784 // machine (lp:1161919). 1785 func (u *Unit) AssignToCleanMachine() (m *Machine, err error) { 1786 return u.assignToCleanMaybeEmptyMachine(false) 1787 } 1788 1789 // AssignToCleanEmptyMachine assigns u to a machine which is marked as clean and is also 1790 // not hosting any containers. A machine is clean if it has never had any principal units 1791 // assigned to it. If there are no clean machines besides any machine(s) running JobHostEnviron, 1792 // an error is returned. 1793 // This method does not take constraints into consideration when choosing a 1794 // machine (lp:1161919). 1795 func (u *Unit) AssignToCleanEmptyMachine() (m *Machine, err error) { 1796 return u.assignToCleanMaybeEmptyMachine(true) 1797 } 1798 1799 var hasContainerTerm = bson.DocElem{ 1800 "$and", []bson.D{ 1801 {{"children", bson.D{{"$not", bson.D{{"$size", 0}}}}}}, 1802 {{"children", bson.D{{"$exists", true}}}}, 1803 }} 1804 1805 var hasNoContainersTerm = bson.DocElem{ 1806 "$or", []bson.D{ 1807 {{"children", bson.D{{"$size", 0}}}}, 1808 {{"children", bson.D{{"$exists", false}}}}, 1809 }} 1810 1811 // findCleanMachineQuery returns a Mongo query to find clean (and possibly empty) machines with 1812 // characteristics matching the specified constraints. 1813 func (u *Unit) findCleanMachineQuery(requireEmpty bool, cons *constraints.Value) (bson.D, error) { 1814 db, closer := u.st.newDB() 1815 defer closer() 1816 containerRefsCollection, closer := db.GetCollection(containerRefsC) 1817 defer closer() 1818 1819 // Select all machines that can accept principal units and are clean. 1820 var containerRefs []machineContainers 1821 // If we need empty machines, first build up a list of machine ids which have containers 1822 // so we can exclude those. 1823 if requireEmpty { 1824 err := containerRefsCollection.Find(bson.D{hasContainerTerm}).All(&containerRefs) 1825 if err != nil { 1826 return nil, err 1827 } 1828 } 1829 var machinesWithContainers = make([]string, len(containerRefs)) 1830 for i, cref := range containerRefs { 1831 machinesWithContainers[i] = cref.Id 1832 } 1833 terms := bson.D{ 1834 {"life", Alive}, 1835 {"series", u.doc.Series}, 1836 {"jobs", []MachineJob{JobHostUnits}}, 1837 {"clean", true}, 1838 {"machineid", bson.D{{"$nin", machinesWithContainers}}}, 1839 } 1840 // Add the container filter term if necessary. 1841 var containerType instance.ContainerType 1842 if cons.Container != nil { 1843 containerType = *cons.Container 1844 } 1845 if containerType == instance.NONE { 1846 terms = append(terms, bson.DocElem{"containertype", ""}) 1847 } else if containerType != "" { 1848 terms = append(terms, bson.DocElem{"containertype", string(containerType)}) 1849 } 1850 1851 // Find the ids of machines which satisfy any required hardware 1852 // constraints. If there is no instanceData for a machine, that 1853 // machine is not considered as suitable for deploying the unit. 1854 // This can happen if the machine is not yet provisioned. It may 1855 // be that when the machine is provisioned it will be found to 1856 // be suitable, but we don't know that right now and it's best 1857 // to err on the side of caution and exclude such machines. 1858 var suitableInstanceData []instanceData 1859 var suitableTerms bson.D 1860 if cons.Arch != nil && *cons.Arch != "" { 1861 suitableTerms = append(suitableTerms, bson.DocElem{"arch", *cons.Arch}) 1862 } 1863 if cons.Mem != nil && *cons.Mem > 0 { 1864 suitableTerms = append(suitableTerms, bson.DocElem{"mem", bson.D{{"$gte", *cons.Mem}}}) 1865 } 1866 if cons.RootDisk != nil && *cons.RootDisk > 0 { 1867 suitableTerms = append(suitableTerms, bson.DocElem{"rootdisk", bson.D{{"$gte", *cons.RootDisk}}}) 1868 } 1869 if cons.CpuCores != nil && *cons.CpuCores > 0 { 1870 suitableTerms = append(suitableTerms, bson.DocElem{"cpucores", bson.D{{"$gte", *cons.CpuCores}}}) 1871 } 1872 if cons.CpuPower != nil && *cons.CpuPower > 0 { 1873 suitableTerms = append(suitableTerms, bson.DocElem{"cpupower", bson.D{{"$gte", *cons.CpuPower}}}) 1874 } 1875 if cons.Tags != nil && len(*cons.Tags) > 0 { 1876 suitableTerms = append(suitableTerms, bson.DocElem{"tags", bson.D{{"$all", *cons.Tags}}}) 1877 } 1878 if len(suitableTerms) > 0 { 1879 instanceDataCollection, closer := db.GetCollection(instanceDataC) 1880 defer closer() 1881 err := instanceDataCollection.Find(suitableTerms).Select(bson.M{"_id": 1}).All(&suitableInstanceData) 1882 if err != nil { 1883 return nil, err 1884 } 1885 var suitableIds = make([]string, len(suitableInstanceData)) 1886 for i, m := range suitableInstanceData { 1887 suitableIds[i] = m.DocID 1888 } 1889 terms = append(terms, bson.DocElem{"_id", bson.D{{"$in", suitableIds}}}) 1890 } 1891 return terms, nil 1892 } 1893 1894 // assignToCleanMaybeEmptyMachine implements AssignToCleanMachine and AssignToCleanEmptyMachine. 1895 // A 'machine' may be a machine instance or container depending on the service constraints. 1896 func (u *Unit) assignToCleanMaybeEmptyMachine(requireEmpty bool) (m *Machine, err error) { 1897 context := "clean" 1898 if requireEmpty { 1899 context += ", empty" 1900 } 1901 context += " machine" 1902 1903 if u.doc.Principal != "" { 1904 err = fmt.Errorf("unit is a subordinate") 1905 assignContextf(&err, u.Name(), context) 1906 return nil, err 1907 } 1908 1909 // If required storage is not all dynamic, then assigning 1910 // to a new machine is required. 1911 storageParams, err := u.machineStorageParams() 1912 if err != nil { 1913 assignContextf(&err, u.Name(), context) 1914 return nil, err 1915 } 1916 storagePools, err := machineStoragePools(u.st, storageParams) 1917 if err != nil { 1918 assignContextf(&err, u.Name(), context) 1919 return nil, err 1920 } 1921 if err := validateDynamicStoragePools(u.st, storagePools); err != nil { 1922 if errors.IsNotSupported(err) { 1923 return nil, noCleanMachines 1924 } 1925 assignContextf(&err, u.Name(), context) 1926 return nil, err 1927 } 1928 1929 // Get the unit constraints to see what deployment requirements we have to adhere to. 1930 cons, err := u.Constraints() 1931 if err != nil { 1932 assignContextf(&err, u.Name(), context) 1933 return nil, err 1934 } 1935 query, err := u.findCleanMachineQuery(requireEmpty, cons) 1936 if err != nil { 1937 assignContextf(&err, u.Name(), context) 1938 return nil, err 1939 } 1940 1941 // Find all of the candidate machines, and associated 1942 // instances for those that are provisioned. Instances 1943 // will be distributed across in preference to 1944 // unprovisioned machines. 1945 machinesCollection, closer := u.st.getCollection(machinesC) 1946 defer closer() 1947 var mdocs []*machineDoc 1948 if err := machinesCollection.Find(query).All(&mdocs); err != nil { 1949 assignContextf(&err, u.Name(), context) 1950 return nil, err 1951 } 1952 var unprovisioned []*Machine 1953 var instances []instance.Id 1954 instanceMachines := make(map[instance.Id]*Machine) 1955 for _, mdoc := range mdocs { 1956 m := newMachine(u.st, mdoc) 1957 instance, err := m.InstanceId() 1958 if errors.IsNotProvisioned(err) { 1959 unprovisioned = append(unprovisioned, m) 1960 } else if err != nil { 1961 assignContextf(&err, u.Name(), context) 1962 return nil, err 1963 } else { 1964 instances = append(instances, instance) 1965 instanceMachines[instance] = m 1966 } 1967 } 1968 1969 // Filter the list of instances that are suitable for 1970 // distribution, and then map them back to machines. 1971 // 1972 // TODO(axw) 2014-05-30 #1324904 1973 // Shuffle machines to reduce likelihood of collisions. 1974 // The partition of provisioned/unprovisioned machines 1975 // must be maintained. 1976 if instances, err = distributeUnit(u, instances); err != nil { 1977 assignContextf(&err, u.Name(), context) 1978 return nil, err 1979 } 1980 machines := make([]*Machine, len(instances), len(instances)+len(unprovisioned)) 1981 for i, instance := range instances { 1982 m, ok := instanceMachines[instance] 1983 if !ok { 1984 err := fmt.Errorf("invalid instance returned: %v", instance) 1985 assignContextf(&err, u.Name(), context) 1986 return nil, err 1987 } 1988 machines[i] = m 1989 } 1990 machines = append(machines, unprovisioned...) 1991 1992 // TODO(axw) 2014-05-30 #1253704 1993 // We should not select a machine that is in the process 1994 // of being provisioned. There's no point asserting that 1995 // the machine hasn't been provisioned, as there'll still 1996 // be a period of time during which the machine may be 1997 // provisioned without the fact having yet been recorded 1998 // in state. 1999 for _, m := range machines { 2000 // Check that the unit storage is compatible with 2001 // the machine in question. 2002 if err := validateDynamicMachineStorageParams(m, storageParams); err != nil { 2003 if errors.IsNotSupported(err) { 2004 continue 2005 } 2006 assignContextf(&err, u.Name(), context) 2007 return nil, err 2008 } 2009 err := u.assignToMachine(m, true) 2010 if err == nil { 2011 return m, nil 2012 } 2013 switch errors.Cause(err) { 2014 case inUseErr, machineNotAliveErr: 2015 default: 2016 assignContextf(&err, u.Name(), context) 2017 return nil, err 2018 } 2019 } 2020 return nil, noCleanMachines 2021 } 2022 2023 // UnassignFromMachine removes the assignment between this unit and the 2024 // machine it's assigned to. 2025 func (u *Unit) UnassignFromMachine() (err error) { 2026 // TODO check local machine id and add an assert that the 2027 // machine id is as expected. 2028 ops := []txn.Op{{ 2029 C: unitsC, 2030 Id: u.doc.DocID, 2031 Assert: txn.DocExists, 2032 Update: bson.D{{"$set", bson.D{{"machineid", ""}}}}, 2033 }} 2034 if u.doc.MachineId != "" { 2035 ops = append(ops, txn.Op{ 2036 C: machinesC, 2037 Id: u.st.docID(u.doc.MachineId), 2038 Assert: txn.DocExists, 2039 Update: bson.D{{"$pull", bson.D{{"principals", u.doc.Name}}}}, 2040 }) 2041 } 2042 err = u.st.runTransaction(ops) 2043 if err != nil { 2044 return fmt.Errorf("cannot unassign unit %q from machine: %v", u, onAbort(err, errors.NotFoundf("machine"))) 2045 } 2046 u.doc.MachineId = "" 2047 return nil 2048 } 2049 2050 // ActionSpecsByName is a map of action names to their respective ActionSpec. 2051 type ActionSpecsByName map[string]charm.ActionSpec 2052 2053 // AddAction adds a new Action of type name and using arguments payload to 2054 // this Unit, and returns its ID. Note that the use of spec.InsertDefaults 2055 // mutates payload. 2056 func (u *Unit) AddAction(name string, payload map[string]interface{}) (Action, error) { 2057 if len(name) == 0 { 2058 return nil, errors.New("no action name given") 2059 } 2060 2061 // If the action is predefined inside juju, get spec from map 2062 spec, ok := actions.PredefinedActionsSpec[name] 2063 if !ok { 2064 specs, err := u.ActionSpecs() 2065 if err != nil { 2066 return nil, err 2067 } 2068 spec, ok = specs[name] 2069 if !ok { 2070 return nil, errors.Errorf("action %q not defined on unit %q", name, u.Name()) 2071 } 2072 } 2073 // Reject bad payloads before attempting to insert defaults. 2074 err := spec.ValidateParams(payload) 2075 if err != nil { 2076 return nil, err 2077 } 2078 payloadWithDefaults, err := spec.InsertDefaults(payload) 2079 if err != nil { 2080 return nil, err 2081 } 2082 return u.st.EnqueueAction(u.Tag(), name, payloadWithDefaults) 2083 } 2084 2085 // ActionSpecs gets the ActionSpec map for the Unit's charm. 2086 func (u *Unit) ActionSpecs() (ActionSpecsByName, error) { 2087 none := ActionSpecsByName{} 2088 curl, _ := u.CharmURL() 2089 if curl == nil { 2090 // If unit charm URL is not yet set, fall back to service 2091 svc, err := u.Service() 2092 if err != nil { 2093 return none, err 2094 } 2095 curl, _ = svc.CharmURL() 2096 if curl == nil { 2097 return none, errors.Errorf("no URL set for service %q", svc.Name()) 2098 } 2099 } 2100 ch, err := u.st.Charm(curl) 2101 if err != nil { 2102 return none, errors.Annotatef(err, "unable to get charm with URL %q", curl.String()) 2103 } 2104 chActions := ch.Actions() 2105 if chActions == nil || len(chActions.ActionSpecs) == 0 { 2106 return none, errors.Errorf("no actions defined on charm %q", ch.String()) 2107 } 2108 return chActions.ActionSpecs, nil 2109 } 2110 2111 // CancelAction removes a pending Action from the queue for this 2112 // ActionReceiver and marks it as cancelled. 2113 func (u *Unit) CancelAction(action Action) (Action, error) { 2114 return action.Finish(ActionResults{Status: ActionCancelled}) 2115 } 2116 2117 // WatchActionNotifications starts and returns a StringsWatcher that 2118 // notifies when actions with Id prefixes matching this Unit are added 2119 func (u *Unit) WatchActionNotifications() StringsWatcher { 2120 return u.st.watchEnqueuedActionsFilteredBy(u) 2121 } 2122 2123 // Actions returns a list of actions pending or completed for this unit. 2124 func (u *Unit) Actions() ([]Action, error) { 2125 return u.st.matchingActions(u) 2126 } 2127 2128 // CompletedActions returns a list of actions that have finished for 2129 // this unit. 2130 func (u *Unit) CompletedActions() ([]Action, error) { 2131 return u.st.matchingActionsCompleted(u) 2132 } 2133 2134 // PendingActions returns a list of actions pending for this unit. 2135 func (u *Unit) PendingActions() ([]Action, error) { 2136 return u.st.matchingActionsPending(u) 2137 } 2138 2139 // RunningActions returns a list of actions running on this unit. 2140 func (u *Unit) RunningActions() ([]Action, error) { 2141 return u.st.matchingActionsRunning(u) 2142 } 2143 2144 // Resolve marks the unit as having had any previous state transition 2145 // problems resolved, and informs the unit that it may attempt to 2146 // reestablish normal workflow. The retryHooks parameter informs 2147 // whether to attempt to reexecute previous failed hooks or to continue 2148 // as if they had succeeded before. 2149 func (u *Unit) Resolve(retryHooks bool) error { 2150 // We currently check agent status to see if a unit is 2151 // in error state. As the new Juju Health work is completed, 2152 // this will change to checking the unit status. 2153 statusInfo, err := u.Status() 2154 if err != nil { 2155 return err 2156 } 2157 if statusInfo.Status != status.StatusError { 2158 return errors.Errorf("unit %q is not in an error state", u) 2159 } 2160 mode := ResolvedNoHooks 2161 if retryHooks { 2162 mode = ResolvedRetryHooks 2163 } 2164 return u.SetResolved(mode) 2165 } 2166 2167 // SetResolved marks the unit as having had any previous state transition 2168 // problems resolved, and informs the unit that it may attempt to 2169 // reestablish normal workflow. The resolved mode parameter informs 2170 // whether to attempt to reexecute previous failed hooks or to continue 2171 // as if they had succeeded before. 2172 func (u *Unit) SetResolved(mode ResolvedMode) (err error) { 2173 defer errors.DeferredAnnotatef(&err, "cannot set resolved mode for unit %q", u) 2174 switch mode { 2175 case ResolvedRetryHooks, ResolvedNoHooks: 2176 default: 2177 return fmt.Errorf("invalid error resolution mode: %q", mode) 2178 } 2179 // TODO(fwereade): assert unit has error status. 2180 resolvedNotSet := bson.D{{"resolved", ResolvedNone}} 2181 ops := []txn.Op{{ 2182 C: unitsC, 2183 Id: u.doc.DocID, 2184 Assert: append(notDeadDoc, resolvedNotSet...), 2185 Update: bson.D{{"$set", bson.D{{"resolved", mode}}}}, 2186 }} 2187 if err := u.st.runTransaction(ops); err == nil { 2188 u.doc.Resolved = mode 2189 return nil 2190 } else if err != txn.ErrAborted { 2191 return err 2192 } 2193 if ok, err := isNotDead(u.st, unitsC, u.doc.DocID); err != nil { 2194 return err 2195 } else if !ok { 2196 return ErrDead 2197 } 2198 // For now, the only remaining assert is that resolved was unset. 2199 return fmt.Errorf("already resolved") 2200 } 2201 2202 // ClearResolved removes any resolved setting on the unit. 2203 func (u *Unit) ClearResolved() error { 2204 ops := []txn.Op{{ 2205 C: unitsC, 2206 Id: u.doc.DocID, 2207 Assert: txn.DocExists, 2208 Update: bson.D{{"$set", bson.D{{"resolved", ResolvedNone}}}}, 2209 }} 2210 err := u.st.runTransaction(ops) 2211 if err != nil { 2212 return fmt.Errorf("cannot clear resolved mode for unit %q: %v", u, errors.NotFoundf("unit")) 2213 } 2214 u.doc.Resolved = ResolvedNone 2215 return nil 2216 } 2217 2218 // StorageConstraints returns the unit's storage constraints. 2219 func (u *Unit) StorageConstraints() (map[string]StorageConstraints, error) { 2220 // TODO(axw) eventually we should be able to override service 2221 // storage constraints at the unit level. 2222 return readStorageConstraints(u.st, serviceGlobalKey(u.doc.Service)) 2223 } 2224 2225 type addUnitOpsArgs struct { 2226 unitDoc *unitDoc 2227 agentStatusDoc statusDoc 2228 workloadStatusDoc statusDoc 2229 meterStatusDoc *meterStatusDoc 2230 } 2231 2232 // addUnitOps returns the operations required to add a unit to the units 2233 // collection, along with all the associated expected other unit entries. This 2234 // method is used by both the *Service.addUnitOpsWithCons method and the 2235 // migration import code. 2236 func addUnitOps(st *State, args addUnitOpsArgs) []txn.Op { 2237 name := args.unitDoc.Name 2238 agentGlobalKey := unitAgentGlobalKey(name) 2239 // TODO: consider the constraints op 2240 // TODO: consider storageOps 2241 return []txn.Op{ 2242 createStatusOp(st, unitGlobalKey(name), args.workloadStatusDoc), 2243 createStatusOp(st, agentGlobalKey, args.agentStatusDoc), 2244 createMeterStatusOp(st, agentGlobalKey, args.meterStatusDoc), 2245 { 2246 C: unitsC, 2247 Id: name, 2248 Assert: txn.DocMissing, 2249 Insert: args.unitDoc, 2250 }, 2251 } 2252 }