github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/application.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 stderrors "errors" 8 "fmt" 9 "sort" 10 "strconv" 11 "strings" 12 13 "github.com/juju/errors" 14 jujutxn "github.com/juju/txn" 15 "github.com/juju/utils/series" 16 "gopkg.in/juju/charm.v6-unstable" 17 csparams "gopkg.in/juju/charmrepo.v2-unstable/csclient/params" 18 "gopkg.in/juju/names.v2" 19 "gopkg.in/mgo.v2" 20 "gopkg.in/mgo.v2/bson" 21 "gopkg.in/mgo.v2/txn" 22 23 "github.com/juju/juju/constraints" 24 "github.com/juju/juju/core/leadership" 25 "github.com/juju/juju/status" 26 ) 27 28 // Application represents the state of an application. 29 type Application struct { 30 st *State 31 doc applicationDoc 32 } 33 34 // serviceDoc represents the internal state of an application in MongoDB. 35 // Note the correspondence with ApplicationInfo in apiserver. 36 type applicationDoc struct { 37 DocID string `bson:"_id"` 38 Name string `bson:"name"` 39 ModelUUID string `bson:"model-uuid"` 40 Series string `bson:"series"` 41 Subordinate bool `bson:"subordinate"` 42 CharmURL *charm.URL `bson:"charmurl"` 43 Channel string `bson:"cs-channel"` 44 CharmModifiedVersion int `bson:"charmmodifiedversion"` 45 ForceCharm bool `bson:"forcecharm"` 46 Life Life `bson:"life"` 47 UnitCount int `bson:"unitcount"` 48 RelationCount int `bson:"relationcount"` 49 Exposed bool `bson:"exposed"` 50 MinUnits int `bson:"minunits"` 51 TxnRevno int64 `bson:"txn-revno"` 52 MetricCredentials []byte `bson:"metric-credentials"` 53 } 54 55 func newApplication(st *State, doc *applicationDoc) *Application { 56 svc := &Application{ 57 st: st, 58 doc: *doc, 59 } 60 return svc 61 } 62 63 // Name returns the application name. 64 func (a *Application) Name() string { 65 return a.doc.Name 66 } 67 68 // Tag returns a name identifying the service. 69 // The returned name will be different from other Tag values returned by any 70 // other entities from the same state. 71 func (a *Application) Tag() names.Tag { 72 return a.ApplicationTag() 73 } 74 75 // ApplicationTag returns the more specific ApplicationTag rather than the generic 76 // Tag. 77 func (a *Application) ApplicationTag() names.ApplicationTag { 78 return names.NewApplicationTag(a.Name()) 79 } 80 81 // applicationGlobalKey returns the global database key for the application 82 // with the given name. 83 func applicationGlobalKey(svcName string) string { 84 return "a#" + svcName 85 } 86 87 // globalKey returns the global database key for the application. 88 func (a *Application) globalKey() string { 89 return applicationGlobalKey(a.doc.Name) 90 } 91 92 func applicationSettingsKey(appName string, curl *charm.URL) string { 93 return fmt.Sprintf("a#%s#%s", appName, curl) 94 } 95 96 // settingsKey returns the charm-version-specific settings collection 97 // key for the application. 98 func (a *Application) settingsKey() string { 99 return applicationSettingsKey(a.doc.Name, a.doc.CharmURL) 100 } 101 102 func applicationStorageConstraintsKey(appName string, curl *charm.URL) string { 103 return fmt.Sprintf("asc#%s#%s", appName, curl) 104 } 105 106 // storageConstraintsKey returns the charm-version-specific storage 107 // constraints collection key for the application. 108 func (a *Application) storageConstraintsKey() string { 109 return applicationStorageConstraintsKey(a.doc.Name, a.doc.CharmURL) 110 } 111 112 // Series returns the specified series for this charm. 113 func (a *Application) Series() string { 114 return a.doc.Series 115 } 116 117 // Life returns whether the application is Alive, Dying or Dead. 118 func (a *Application) Life() Life { 119 return a.doc.Life 120 } 121 122 var errRefresh = stderrors.New("state seems inconsistent, refresh and try again") 123 124 // Destroy ensures that the application and all its relations will be removed at 125 // some point; if the application has no units, and no relation involving the 126 // application has any units in scope, they are all removed immediately. 127 func (a *Application) Destroy() (err error) { 128 defer errors.DeferredAnnotatef(&err, "cannot destroy application %q", a) 129 defer func() { 130 if err == nil { 131 // This is a white lie; the document might actually be removed. 132 a.doc.Life = Dying 133 } 134 }() 135 svc := &Application{st: a.st, doc: a.doc} 136 buildTxn := func(attempt int) ([]txn.Op, error) { 137 if attempt > 0 { 138 if err := svc.Refresh(); errors.IsNotFound(err) { 139 return nil, jujutxn.ErrNoOperations 140 } else if err != nil { 141 return nil, err 142 } 143 } 144 switch ops, err := svc.destroyOps(); err { 145 case errRefresh: 146 case errAlreadyDying: 147 return nil, jujutxn.ErrNoOperations 148 case nil: 149 return ops, nil 150 default: 151 return nil, err 152 } 153 return nil, jujutxn.ErrTransientFailure 154 } 155 return a.st.run(buildTxn) 156 } 157 158 // destroyOps returns the operations required to destroy the service. If it 159 // returns errRefresh, the application should be refreshed and the destruction 160 // operations recalculated. 161 func (a *Application) destroyOps() ([]txn.Op, error) { 162 if a.doc.Life == Dying { 163 return nil, errAlreadyDying 164 } 165 rels, err := a.Relations() 166 if err != nil { 167 return nil, err 168 } 169 if len(rels) != a.doc.RelationCount { 170 // This is just an early bail out. The relations obtained may still 171 // be wrong, but that situation will be caught by a combination of 172 // asserts on relationcount and on each known relation, below. 173 return nil, errRefresh 174 } 175 ops := []txn.Op{minUnitsRemoveOp(a.st, a.doc.Name)} 176 removeCount := 0 177 for _, rel := range rels { 178 relOps, isRemove, err := rel.destroyOps(a.doc.Name) 179 if err == errAlreadyDying { 180 relOps = []txn.Op{{ 181 C: relationsC, 182 Id: rel.doc.DocID, 183 Assert: bson.D{{"life", Dying}}, 184 }} 185 } else if err != nil { 186 return nil, err 187 } 188 if isRemove { 189 removeCount++ 190 } 191 ops = append(ops, relOps...) 192 } 193 // TODO(ericsnow) Use a generic registry instead. 194 resOps, err := removeResourcesOps(a.st, a.doc.Name) 195 if err != nil { 196 return nil, errors.Trace(err) 197 } 198 ops = append(ops, resOps...) 199 // If the application has no units, and all its known relations will be 200 // removed, the application can also be removed. 201 if a.doc.UnitCount == 0 && a.doc.RelationCount == removeCount { 202 hasLastRefs := bson.D{{"life", Alive}, {"unitcount", 0}, {"relationcount", removeCount}} 203 removeOps, err := a.removeOps(hasLastRefs) 204 if err != nil { 205 return nil, errors.Trace(err) 206 } 207 return append(ops, removeOps...), nil 208 } 209 // In all other cases, application removal will be handled as a consequence 210 // of the removal of the last unit or relation referencing it. If any 211 // relations have been removed, they'll be caught by the operations 212 // collected above; but if any has been added, we need to abort and add 213 // a destroy op for that relation too. In combination, it's enough to 214 // check for count equality: an add/remove will not touch the count, but 215 // will be caught by virtue of being a remove. 216 notLastRefs := bson.D{ 217 {"life", Alive}, 218 {"relationcount", a.doc.RelationCount}, 219 } 220 // With respect to unit count, a changing value doesn't matter, so long 221 // as the count's equality with zero does not change, because all we care 222 // about is that *some* unit is, or is not, keeping the application from 223 // being removed: the difference between 1 unit and 1000 is irrelevant. 224 if a.doc.UnitCount > 0 { 225 ops = append(ops, newCleanupOp(cleanupUnitsForDyingService, a.doc.Name)) 226 notLastRefs = append(notLastRefs, bson.D{{"unitcount", bson.D{{"$gt", 0}}}}...) 227 } else { 228 notLastRefs = append(notLastRefs, bson.D{{"unitcount", 0}}...) 229 } 230 update := bson.D{{"$set", bson.D{{"life", Dying}}}} 231 if removeCount != 0 { 232 decref := bson.D{{"$inc", bson.D{{"relationcount", -removeCount}}}} 233 update = append(update, decref...) 234 } 235 return append(ops, txn.Op{ 236 C: applicationsC, 237 Id: a.doc.DocID, 238 Assert: notLastRefs, 239 Update: update, 240 }), nil 241 } 242 243 func removeResourcesOps(st *State, serviceID string) ([]txn.Op, error) { 244 persist, err := st.ResourcesPersistence() 245 if errors.IsNotSupported(err) { 246 // Nothing to see here, move along. 247 return nil, nil 248 } 249 if err != nil { 250 return nil, errors.Trace(err) 251 } 252 ops, err := persist.NewRemoveResourcesOps(serviceID) 253 if err != nil { 254 return nil, errors.Trace(err) 255 } 256 return ops, nil 257 } 258 259 // removeOps returns the operations required to remove the service. Supplied 260 // asserts will be included in the operation on the application document. 261 func (a *Application) removeOps(asserts bson.D) ([]txn.Op, error) { 262 ops := []txn.Op{ 263 { 264 C: applicationsC, 265 Id: a.doc.DocID, 266 Assert: asserts, 267 Remove: true, 268 }, { 269 C: settingsC, 270 Id: a.settingsKey(), 271 Remove: true, 272 }, 273 } 274 // Note that appCharmDecRefOps might not catch the final decref 275 // when run in a transaction that decrefs more than once. In 276 // this case, luckily, we can be sure that we unconditionally 277 // need finalAppCharmRemoveOps; and we trust that it's written 278 // such that it's safe to run multiple times. 279 name := a.doc.Name 280 curl := a.doc.CharmURL 281 charmOps, err := appCharmDecRefOps(a.st, name, curl) 282 if err != nil { 283 return nil, errors.Trace(err) 284 } 285 ops = append(ops, charmOps...) 286 ops = append(ops, finalAppCharmRemoveOps(name, curl)...) 287 288 globalKey := a.globalKey() 289 ops = append(ops, 290 removeEndpointBindingsOp(globalKey), 291 removeConstraintsOp(a.st, globalKey), 292 annotationRemoveOp(a.st, globalKey), 293 removeLeadershipSettingsOp(name), 294 removeStatusOp(a.st, globalKey), 295 removeModelServiceRefOp(a.st, name), 296 ) 297 return ops, nil 298 } 299 300 // IsExposed returns whether this application is exposed. The explicitly open 301 // ports (with open-port) for exposed services may be accessed from machines 302 // outside of the local deployment network. See SetExposed and ClearExposed. 303 func (a *Application) IsExposed() bool { 304 return a.doc.Exposed 305 } 306 307 // SetExposed marks the application as exposed. 308 // See ClearExposed and IsExposed. 309 func (a *Application) SetExposed() error { 310 return a.setExposed(true) 311 } 312 313 // ClearExposed removes the exposed flag from the service. 314 // See SetExposed and IsExposed. 315 func (a *Application) ClearExposed() error { 316 return a.setExposed(false) 317 } 318 319 func (a *Application) setExposed(exposed bool) (err error) { 320 ops := []txn.Op{{ 321 C: applicationsC, 322 Id: a.doc.DocID, 323 Assert: isAliveDoc, 324 Update: bson.D{{"$set", bson.D{{"exposed", exposed}}}}, 325 }} 326 if err := a.st.runTransaction(ops); err != nil { 327 return errors.Errorf("cannot set exposed flag for application %q to %v: %v", a, exposed, onAbort(err, errNotAlive)) 328 } 329 a.doc.Exposed = exposed 330 return nil 331 } 332 333 // Charm returns the service's charm and whether units should upgrade to that 334 // charm even if they are in an error state. 335 func (a *Application) Charm() (ch *Charm, force bool, err error) { 336 // We don't worry about the channel since we aren't interacting 337 // with the charm store here. 338 ch, err = a.st.Charm(a.doc.CharmURL) 339 if err != nil { 340 return nil, false, err 341 } 342 return ch, a.doc.ForceCharm, nil 343 } 344 345 // IsPrincipal returns whether units of the application can 346 // have subordinate units. 347 func (a *Application) IsPrincipal() bool { 348 return !a.doc.Subordinate 349 } 350 351 // CharmModifiedVersion increases whenever the service's charm is changed in any 352 // way. 353 func (a *Application) CharmModifiedVersion() int { 354 return a.doc.CharmModifiedVersion 355 } 356 357 // CharmURL returns the service's charm URL, and whether units should upgrade 358 // to the charm with that URL even if they are in an error state. 359 func (a *Application) CharmURL() (curl *charm.URL, force bool) { 360 return a.doc.CharmURL, a.doc.ForceCharm 361 } 362 363 // Channel identifies the charm store channel from which the service's 364 // charm was deployed. It is only needed when interacting with the charm 365 // store. 366 func (a *Application) Channel() csparams.Channel { 367 return csparams.Channel(a.doc.Channel) 368 } 369 370 // Endpoints returns the service's currently available relation endpoints. 371 func (a *Application) Endpoints() (eps []Endpoint, err error) { 372 ch, _, err := a.Charm() 373 if err != nil { 374 return nil, err 375 } 376 collect := func(role charm.RelationRole, rels map[string]charm.Relation) { 377 for _, rel := range rels { 378 eps = append(eps, Endpoint{ 379 ApplicationName: a.doc.Name, 380 Relation: rel, 381 }) 382 } 383 } 384 meta := ch.Meta() 385 collect(charm.RolePeer, meta.Peers) 386 collect(charm.RoleProvider, meta.Provides) 387 collect(charm.RoleRequirer, meta.Requires) 388 collect(charm.RoleProvider, map[string]charm.Relation{ 389 "juju-info": { 390 Name: "juju-info", 391 Role: charm.RoleProvider, 392 Interface: "juju-info", 393 Scope: charm.ScopeGlobal, 394 }, 395 }) 396 sort.Sort(epSlice(eps)) 397 return eps, nil 398 } 399 400 // Endpoint returns the relation endpoint with the supplied name, if it exists. 401 func (a *Application) Endpoint(relationName string) (Endpoint, error) { 402 eps, err := a.Endpoints() 403 if err != nil { 404 return Endpoint{}, err 405 } 406 for _, ep := range eps { 407 if ep.Name == relationName { 408 return ep, nil 409 } 410 } 411 return Endpoint{}, errors.Errorf("application %q has no %q relation", a, relationName) 412 } 413 414 // extraPeerRelations returns only the peer relations in newMeta not 415 // present in the service's current charm meta data. 416 func (a *Application) extraPeerRelations(newMeta *charm.Meta) map[string]charm.Relation { 417 if newMeta == nil { 418 // This should never happen, since we're checking the charm in SetCharm already. 419 panic("newMeta is nil") 420 } 421 ch, _, err := a.Charm() 422 if err != nil { 423 return nil 424 } 425 newPeers := newMeta.Peers 426 oldPeers := ch.Meta().Peers 427 extraPeers := make(map[string]charm.Relation) 428 for relName, rel := range newPeers { 429 if _, ok := oldPeers[relName]; !ok { 430 extraPeers[relName] = rel 431 } 432 } 433 return extraPeers 434 } 435 436 func (a *Application) checkRelationsOps(ch *Charm, relations []*Relation) ([]txn.Op, error) { 437 asserts := make([]txn.Op, 0, len(relations)) 438 // All relations must still exist and their endpoints are implemented by the charm. 439 for _, rel := range relations { 440 if ep, err := rel.Endpoint(a.doc.Name); err != nil { 441 return nil, err 442 } else if !ep.ImplementedBy(ch) { 443 return nil, errors.Errorf("would break relation %q", rel) 444 } 445 asserts = append(asserts, txn.Op{ 446 C: relationsC, 447 Id: rel.doc.DocID, 448 Assert: txn.DocExists, 449 }) 450 } 451 return asserts, nil 452 } 453 454 func (a *Application) checkStorageUpgrade(newMeta, oldMeta *charm.Meta, units []*Unit) (_ []txn.Op, err error) { 455 // Make sure no storage instances are added or removed. 456 var ops []txn.Op 457 for name, oldStorageMeta := range oldMeta.Storage { 458 if _, ok := newMeta.Storage[name]; ok { 459 continue 460 } 461 if oldStorageMeta.CountMin > 0 { 462 return nil, errors.Errorf("required storage %q removed", name) 463 } 464 // Optional storage has been removed. So long as there 465 // are no instances of the store, it can safely be 466 // removed. 467 if oldStorageMeta.Shared { 468 op, n, err := a.st.countEntityStorageInstances(a.Tag(), name) 469 if err != nil { 470 return nil, errors.Trace(err) 471 } 472 if n > 0 { 473 return nil, errors.Errorf("in-use storage %q removed", name) 474 } 475 ops = append(ops, op) 476 } else { 477 for _, u := range units { 478 op, n, err := a.st.countEntityStorageInstances(u.Tag(), name) 479 if err != nil { 480 return nil, errors.Trace(err) 481 } 482 if n > 0 { 483 return nil, errors.Errorf("in-use storage %q removed", name) 484 } 485 ops = append(ops, op) 486 } 487 } 488 } 489 less := func(a, b int) bool { 490 return a != -1 && (b == -1 || a < b) 491 } 492 for name, newStorageMeta := range newMeta.Storage { 493 oldStorageMeta, ok := oldMeta.Storage[name] 494 if !ok { 495 continue 496 } 497 if newStorageMeta.Type != oldStorageMeta.Type { 498 return nil, errors.Errorf( 499 "existing storage %q type changed from %q to %q", 500 name, oldStorageMeta.Type, newStorageMeta.Type, 501 ) 502 } 503 if newStorageMeta.Shared != oldStorageMeta.Shared { 504 return nil, errors.Errorf( 505 "existing storage %q shared changed from %v to %v", 506 name, oldStorageMeta.Shared, newStorageMeta.Shared, 507 ) 508 } 509 if newStorageMeta.ReadOnly != oldStorageMeta.ReadOnly { 510 return nil, errors.Errorf( 511 "existing storage %q read-only changed from %v to %v", 512 name, oldStorageMeta.ReadOnly, newStorageMeta.ReadOnly, 513 ) 514 } 515 if newStorageMeta.Location != oldStorageMeta.Location { 516 return nil, errors.Errorf( 517 "existing storage %q location changed from %q to %q", 518 name, oldStorageMeta.Location, newStorageMeta.Location, 519 ) 520 } 521 if less(newStorageMeta.CountMax, oldStorageMeta.CountMax) { 522 var oldCountMax interface{} = oldStorageMeta.CountMax 523 if oldStorageMeta.CountMax == -1 { 524 oldCountMax = "<unbounded>" 525 } 526 return nil, errors.Errorf( 527 "existing storage %q range contracted: max decreased from %v to %d", 528 name, oldCountMax, newStorageMeta.CountMax, 529 ) 530 } 531 if oldStorageMeta.Location != "" && oldStorageMeta.CountMax == 1 && newStorageMeta.CountMax != 1 { 532 // If a location is specified, the store may not go 533 // from being a singleton to multiple, since then the 534 // location has a different meaning. 535 return nil, errors.Errorf( 536 "existing storage %q with location changed from single to multiple", 537 name, 538 ) 539 } 540 } 541 return ops, nil 542 } 543 544 // changeCharmOps returns the operations necessary to set a service's 545 // charm URL to a new value. 546 func (a *Application) changeCharmOps( 547 ch *Charm, 548 channel string, 549 updatedSettings charm.Settings, 550 forceUnits bool, 551 resourceIDs map[string]string, 552 updatedStorageConstraints map[string]StorageConstraints, 553 ) ([]txn.Op, error) { 554 // Build the new application config from what can be used of the old one. 555 var newSettings charm.Settings 556 oldSettings, err := readSettings(a.st, settingsC, a.settingsKey()) 557 if err == nil { 558 // Filter the old settings through to get the new settings. 559 newSettings = ch.Config().FilterSettings(oldSettings.Map()) 560 for k, v := range updatedSettings { 561 newSettings[k] = v 562 } 563 } else if errors.IsNotFound(err) { 564 // No old settings, start with the updated settings. 565 newSettings = updatedSettings 566 } else { 567 return nil, errors.Trace(err) 568 } 569 570 // Create or replace application settings. 571 var settingsOp txn.Op 572 newSettingsKey := applicationSettingsKey(a.doc.Name, ch.URL()) 573 if _, err := readSettings(a.st, settingsC, newSettingsKey); errors.IsNotFound(err) { 574 // No settings for this key yet, create it. 575 settingsOp = createSettingsOp(settingsC, newSettingsKey, newSettings) 576 } else if err != nil { 577 return nil, errors.Trace(err) 578 } else { 579 // Settings exist, just replace them with the new ones. 580 settingsOp, _, err = replaceSettingsOp(a.st, settingsC, newSettingsKey, newSettings) 581 if err != nil { 582 return nil, errors.Trace(err) 583 } 584 } 585 586 // Make sure no units are added or removed while the upgrade 587 // transaction is being executed. This allows us to make 588 // changes to units during the upgrade, e.g. add storage 589 // to existing units, or remove optional storage so long as 590 // it is unreferenced. 591 units, err := a.AllUnits() 592 if err != nil { 593 return nil, errors.Trace(err) 594 } 595 unitOps := make([]txn.Op, len(units)) 596 for i, u := range units { 597 unitOps[i] = txn.Op{ 598 C: unitsC, 599 Id: u.doc.DocID, 600 Assert: txn.DocExists, 601 } 602 } 603 unitOps = append(unitOps, txn.Op{ 604 C: applicationsC, 605 Id: a.doc.DocID, 606 Assert: bson.D{{"unitcount", len(units)}}, 607 }) 608 609 // Check storage to ensure no referenced storage is removed, or changed 610 // in an incompatible way. We do this before computing the new storage 611 // constraints, as incompatible charm changes will otherwise yield 612 // confusing error messages that would suggest the user has supplied 613 // invalid constraints. 614 oldCharm, _, err := a.Charm() 615 if err != nil { 616 return nil, errors.Trace(err) 617 } 618 oldMeta := oldCharm.Meta() 619 checkStorageOps, err := a.checkStorageUpgrade(ch.Meta(), oldMeta, units) 620 if err != nil { 621 return nil, errors.Trace(err) 622 } 623 624 // Create or replace storage constraints. We take the existing storage 625 // constraints, remove any keys that are no longer referenced by the 626 // charm, and update the constraints that the user has specified. 627 var storageConstraintsOp txn.Op 628 oldStorageConstraints, err := a.StorageConstraints() 629 if err != nil { 630 return nil, errors.Trace(err) 631 } 632 newStorageConstraints := oldStorageConstraints 633 for name, cons := range updatedStorageConstraints { 634 newStorageConstraints[name] = cons 635 } 636 for name := range newStorageConstraints { 637 if _, ok := ch.Meta().Storage[name]; !ok { 638 delete(newStorageConstraints, name) 639 } 640 } 641 if err := addDefaultStorageConstraints(a.st, newStorageConstraints, ch.Meta()); err != nil { 642 return nil, errors.Annotate(err, "adding default storage constraints") 643 } 644 if err := validateStorageConstraints(a.st, newStorageConstraints, ch.Meta()); err != nil { 645 return nil, errors.Annotate(err, "validating storage constraints") 646 } 647 newStorageConstraintsKey := applicationStorageConstraintsKey(a.doc.Name, ch.URL()) 648 if _, err := readStorageConstraints(a.st, newStorageConstraintsKey); errors.IsNotFound(err) { 649 storageConstraintsOp = createStorageConstraintsOp( 650 newStorageConstraintsKey, newStorageConstraints, 651 ) 652 } else if err != nil { 653 return nil, errors.Trace(err) 654 } else { 655 storageConstraintsOp = replaceStorageConstraintsOp( 656 newStorageConstraintsKey, newStorageConstraints, 657 ) 658 } 659 660 // Upgrade charm storage. 661 upgradeStorageOps, err := a.upgradeStorageOps(ch.Meta(), oldMeta, units, newStorageConstraints) 662 if err != nil { 663 return nil, errors.Trace(err) 664 } 665 666 // Add or create a reference to the new charm, settings, 667 // and storage constraints docs. 668 incOps, err := appCharmIncRefOps(a.st, a.doc.Name, ch.URL(), true) 669 if err != nil { 670 return nil, errors.Trace(err) 671 } 672 var decOps []txn.Op 673 // Drop the references to the old settings, storage constraints, 674 // and charm docs (if the refs actually exist yet). 675 if oldSettings != nil { 676 decOps, err = appCharmDecRefOps(a.st, a.doc.Name, a.doc.CharmURL) // current charm 677 if err != nil { 678 return nil, errors.Trace(err) 679 } 680 } 681 682 // Build the transaction. 683 var ops []txn.Op 684 if oldSettings != nil { 685 // Old settings shouldn't change (when they exist). 686 ops = append(ops, oldSettings.assertUnchangedOp()) 687 } 688 ops = append(ops, unitOps...) 689 ops = append(ops, incOps...) 690 ops = append(ops, []txn.Op{ 691 // Create or replace new settings. 692 settingsOp, 693 // Create storage constraints. 694 storageConstraintsOp, 695 // Update the charm URL and force flag (if relevant). 696 { 697 C: applicationsC, 698 Id: a.doc.DocID, 699 Update: bson.D{{"$set", bson.D{ 700 {"charmurl", ch.URL()}, 701 {"cs-channel", channel}, 702 {"forcecharm", forceUnits}, 703 }}}, 704 }, 705 }...) 706 ops = append(ops, checkStorageOps...) 707 ops = append(ops, upgradeStorageOps...) 708 709 ops = append(ops, incCharmModifiedVersionOps(a.doc.DocID)...) 710 711 // Add any extra peer relations that need creation. 712 newPeers := a.extraPeerRelations(ch.Meta()) 713 peerOps, err := a.st.addPeerRelationsOps(a.doc.Name, newPeers) 714 if err != nil { 715 return nil, errors.Trace(err) 716 } 717 718 if len(resourceIDs) > 0 { 719 // Collect pending resource resolution operations. 720 resOps, err := a.resolveResourceOps(resourceIDs) 721 if err != nil { 722 return nil, errors.Trace(err) 723 } 724 ops = append(ops, resOps...) 725 } 726 727 // Get all relations - we need to check them later. 728 relations, err := a.Relations() 729 if err != nil { 730 return nil, errors.Trace(err) 731 } 732 // Make sure the relation count does not change. 733 sameRelCount := bson.D{{"relationcount", len(relations)}} 734 735 ops = append(ops, peerOps...) 736 // Update the relation count as well. 737 ops = append(ops, txn.Op{ 738 C: applicationsC, 739 Id: a.doc.DocID, 740 Assert: append(notDeadDoc, sameRelCount...), 741 Update: bson.D{{"$inc", bson.D{{"relationcount", len(newPeers)}}}}, 742 }) 743 // Check relations to ensure no active relations are removed. 744 relOps, err := a.checkRelationsOps(ch, relations) 745 if err != nil { 746 return nil, errors.Trace(err) 747 } 748 ops = append(ops, relOps...) 749 750 // Update any existing endpoint bindings, using defaults for new endpoints. 751 // 752 // TODO(dimitern): Once upgrade-charm accepts --bind like deploy, pass the 753 // given bindings below, instead of nil. 754 endpointBindingsOp, err := updateEndpointBindingsOp(a.st, a.globalKey(), nil, ch.Meta()) 755 if err == nil { 756 ops = append(ops, endpointBindingsOp) 757 } else if !errors.IsNotFound(err) && err != jujutxn.ErrNoOperations { 758 // If endpoint bindings do not exist this most likely means the service 759 // itself no longer exists, which will be caught soon enough anyway. 760 // ErrNoOperations on the other hand means there's nothing to update. 761 return nil, errors.Trace(err) 762 } 763 764 // And finally, decrement the old charm and settings. 765 return append(ops, decOps...), nil 766 } 767 768 func (a *Application) upgradeStorageOps( 769 meta, oldMeta *charm.Meta, 770 units []*Unit, 771 allStorageCons map[string]StorageConstraints, 772 ) (_ []txn.Op, err error) { 773 // For each store, ensure that every unit has the minimum requirements. 774 // If a unit has an existing store, but its minimum count has been 775 // increased, we only add the shortfall; we do not necessarily add as 776 // many instances as are specified in the storage constraints. 777 var ops []txn.Op 778 for name, cons := range allStorageCons { 779 for _, u := range units { 780 countMin := meta.Storage[name].CountMin 781 if _, ok := oldMeta.Storage[name]; !ok { 782 // The store did not exist previously, so we 783 // create the full amount specified in the 784 // cosntraints. 785 countMin = int(cons.Count) 786 } 787 unitOps, err := a.st.addUnitStorageOps( 788 meta, u, name, cons, countMin, 789 ) 790 if err != nil { 791 return nil, errors.Trace(err) 792 } 793 ops = append(ops, unitOps...) 794 } 795 } 796 return ops, nil 797 } 798 799 // incCharmModifiedVersionOps returns the operations necessary to increment 800 // the CharmModifiedVersion field for the given service. 801 func incCharmModifiedVersionOps(serviceID string) []txn.Op { 802 return []txn.Op{{ 803 C: applicationsC, 804 Id: serviceID, 805 Assert: txn.DocExists, 806 Update: bson.D{{"$inc", bson.D{{"charmmodifiedversion", 1}}}}, 807 }} 808 } 809 810 func (a *Application) resolveResourceOps(resourceIDs map[string]string) ([]txn.Op, error) { 811 // Collect pending resource resolution operations. 812 resources, err := a.st.Resources() 813 if err != nil { 814 return nil, errors.Trace(err) 815 } 816 return resources.NewResolvePendingResourcesOps(a.doc.Name, resourceIDs) 817 } 818 819 // SetCharmConfig contains the parameters for Application.SetCharm. 820 type SetCharmConfig struct { 821 // Charm is the new charm to use for the application. New units 822 // will be started with this charm, and existing units will be 823 // upgraded to use it. 824 Charm *Charm 825 826 // Channel is the charm store channel from which charm was pulled. 827 Channel csparams.Channel 828 829 // ConfigSettings is the charm config settings to apply when upgrading 830 // the charm. 831 ConfigSettings charm.Settings 832 833 // ForceUnits forces the upgrade on units in an error state. 834 ForceUnits bool 835 836 // ForceSeries forces the use of the charm even if it is not one of 837 // the charm's supported series. 838 ForceSeries bool 839 840 // ResourceIDs is a map of resource names to resource IDs to activate during 841 // the upgrade. 842 ResourceIDs map[string]string 843 844 // StorageConstraints contains the constraints to add or update when 845 // upgrading the charm. 846 // 847 // Any existing storage instances for the named stores will be 848 // unaffected; the storage constraints will only be used for 849 // provisioning new storage instances. 850 StorageConstraints map[string]StorageConstraints 851 } 852 853 // SetCharm changes the charm for the application. 854 func (a *Application) SetCharm(cfg SetCharmConfig) (err error) { 855 defer errors.DeferredAnnotatef( 856 &err, "cannot upgrade application %q to charm %q", a, cfg.Charm, 857 ) 858 if cfg.Charm.Meta().Subordinate != a.doc.Subordinate { 859 return errors.Errorf("cannot change an application's subordinacy") 860 } 861 // For old style charms written for only one series, we still retain 862 // this check. Newer charms written for multi-series have a URL 863 // with series = "". 864 if cfg.Charm.URL().Series != "" { 865 if cfg.Charm.URL().Series != a.doc.Series { 866 return errors.Errorf("cannot change an application's series") 867 } 868 } else if !cfg.ForceSeries { 869 supported := false 870 for _, series := range cfg.Charm.Meta().Series { 871 if series == a.doc.Series { 872 supported = true 873 break 874 } 875 } 876 if !supported { 877 supportedSeries := "no series" 878 if len(cfg.Charm.Meta().Series) > 0 { 879 supportedSeries = strings.Join(cfg.Charm.Meta().Series, ", ") 880 } 881 return errors.Errorf("only these series are supported: %v", supportedSeries) 882 } 883 } else { 884 // Even with forceSeries=true, we do not allow a charm to be used which is for 885 // a different OS. This assumes the charm declares it has supported series which 886 // we can check for OS compatibility. Otherwise, we just accept the series supplied. 887 currentOS, err := series.GetOSFromSeries(a.doc.Series) 888 if err != nil { 889 // We don't expect an error here but there's not much we can 890 // do to recover. 891 return err 892 } 893 supportedOS := false 894 supportedSeries := cfg.Charm.Meta().Series 895 for _, chSeries := range supportedSeries { 896 charmSeriesOS, err := series.GetOSFromSeries(chSeries) 897 if err != nil { 898 return nil 899 } 900 if currentOS == charmSeriesOS { 901 supportedOS = true 902 break 903 } 904 } 905 if !supportedOS && len(supportedSeries) > 0 { 906 return errors.Errorf("OS %q not supported by charm", currentOS) 907 } 908 } 909 910 updatedSettings, err := cfg.Charm.Config().ValidateSettings(cfg.ConfigSettings) 911 if err != nil { 912 return errors.Annotate(err, "validating config settings") 913 } 914 915 var newCharmModifiedVersion int 916 channel := string(cfg.Channel) 917 acopy := &Application{a.st, a.doc} 918 buildTxn := func(attempt int) ([]txn.Op, error) { 919 a := acopy 920 if attempt > 0 { 921 if err := a.Refresh(); err != nil { 922 return nil, errors.Trace(err) 923 } 924 } 925 926 // NOTE: We're explicitly allowing SetCharm to succeed 927 // when the application is Dying, because service/charm 928 // upgrades should still be allowed to apply to dying 929 // services and units, so that bugs in departed/broken 930 // hooks can be addressed at runtime. 931 if a.Life() == Dead { 932 return nil, ErrDead 933 } 934 935 // Record the current value of charmModifiedVersion, so we can 936 // set the value on the method receiver's in-memory document 937 // structure. We increment the version only when we change the 938 // charm URL. 939 newCharmModifiedVersion = a.doc.CharmModifiedVersion 940 941 ops := []txn.Op{{ 942 C: applicationsC, 943 Id: a.doc.DocID, 944 Assert: append(notDeadDoc, bson.DocElem{ 945 "charmmodifiedversion", a.doc.CharmModifiedVersion, 946 }), 947 }} 948 949 if a.doc.CharmURL.String() == cfg.Charm.URL().String() { 950 // Charm URL already set; just update the force flag and channel. 951 ops = append(ops, txn.Op{ 952 C: applicationsC, 953 Id: a.doc.DocID, 954 Update: bson.D{{"$set", bson.D{ 955 {"cs-channel", channel}, 956 {"forcecharm", cfg.ForceUnits}, 957 }}}, 958 }) 959 } else { 960 chng, err := a.changeCharmOps( 961 cfg.Charm, 962 channel, 963 updatedSettings, 964 cfg.ForceUnits, 965 cfg.ResourceIDs, 966 cfg.StorageConstraints, 967 ) 968 if err != nil { 969 return nil, errors.Trace(err) 970 } 971 ops = append(ops, chng...) 972 newCharmModifiedVersion++ 973 } 974 975 return ops, nil 976 } 977 if err := a.st.run(buildTxn); err != nil { 978 return err 979 } 980 a.doc.CharmURL = cfg.Charm.URL() 981 a.doc.Channel = channel 982 a.doc.ForceCharm = cfg.ForceUnits 983 a.doc.CharmModifiedVersion = newCharmModifiedVersion 984 return nil 985 } 986 987 // String returns the application name. 988 func (a *Application) String() string { 989 return a.doc.Name 990 } 991 992 // Refresh refreshes the contents of the Service from the underlying 993 // state. It returns an error that satisfies errors.IsNotFound if the 994 // application has been removed. 995 func (a *Application) Refresh() error { 996 services, closer := a.st.getCollection(applicationsC) 997 defer closer() 998 999 err := services.FindId(a.doc.DocID).One(&a.doc) 1000 if err == mgo.ErrNotFound { 1001 return errors.NotFoundf("application %q", a) 1002 } 1003 if err != nil { 1004 return errors.Errorf("cannot refresh application %q: %v", a, err) 1005 } 1006 return nil 1007 } 1008 1009 // newUnitName returns the next unit name. 1010 func (a *Application) newUnitName() (string, error) { 1011 unitSeq, err := a.st.sequence(a.Tag().String()) 1012 if err != nil { 1013 return "", errors.Trace(err) 1014 } 1015 name := a.doc.Name + "/" + strconv.Itoa(unitSeq) 1016 return name, nil 1017 } 1018 1019 // addUnitOps returns a unique name for a new unit, and a list of txn operations 1020 // necessary to create that unit. The principalName param must be non-empty if 1021 // and only if s is a subordinate service. Only one subordinate of a given 1022 // application will be assigned to a given principal. The asserts param can be used 1023 // to include additional assertions for the application document. This method 1024 // assumes that the application already exists in the db. 1025 func (a *Application) addUnitOps(principalName string, asserts bson.D) (string, []txn.Op, error) { 1026 var cons constraints.Value 1027 if !a.doc.Subordinate { 1028 scons, err := a.Constraints() 1029 if errors.IsNotFound(err) { 1030 return "", nil, errors.NotFoundf("application %q", a.Name()) 1031 } 1032 if err != nil { 1033 return "", nil, err 1034 } 1035 cons, err = a.st.resolveConstraints(scons) 1036 if err != nil { 1037 return "", nil, err 1038 } 1039 } 1040 storageCons, err := a.StorageConstraints() 1041 if err != nil { 1042 return "", nil, err 1043 } 1044 args := applicationAddUnitOpsArgs{ 1045 cons: cons, 1046 principalName: principalName, 1047 storageCons: storageCons, 1048 } 1049 names, ops, err := a.addUnitOpsWithCons(args) 1050 if err != nil { 1051 return names, ops, err 1052 } 1053 // we verify the application is alive 1054 asserts = append(isAliveDoc, asserts...) 1055 ops = append(ops, a.incUnitCountOp(asserts)) 1056 return names, ops, err 1057 } 1058 1059 type applicationAddUnitOpsArgs struct { 1060 principalName string 1061 cons constraints.Value 1062 storageCons map[string]StorageConstraints 1063 } 1064 1065 // addServiceUnitOps is just like addUnitOps but explicitly takes a 1066 // constraints value (this is used at application creation time). 1067 func (a *Application) addServiceUnitOps(args applicationAddUnitOpsArgs) (string, []txn.Op, error) { 1068 names, ops, err := a.addUnitOpsWithCons(args) 1069 if err == nil { 1070 ops = append(ops, a.incUnitCountOp(nil)) 1071 } 1072 return names, ops, err 1073 } 1074 1075 // addUnitOpsWithCons is a helper method for returning addUnitOps. 1076 func (a *Application) addUnitOpsWithCons(args applicationAddUnitOpsArgs) (string, []txn.Op, error) { 1077 if a.doc.Subordinate && args.principalName == "" { 1078 return "", nil, errors.New("application is a subordinate") 1079 } else if !a.doc.Subordinate && args.principalName != "" { 1080 return "", nil, errors.New("application is not a subordinate") 1081 } 1082 name, err := a.newUnitName() 1083 if err != nil { 1084 return "", nil, err 1085 } 1086 unitTag := names.NewUnitTag(name) 1087 1088 charm, _, err := a.Charm() 1089 if err != nil { 1090 return "", nil, err 1091 } 1092 1093 // Add storage instances/attachments for the unit. If the 1094 // application is subordinate, we'll add the machine storage 1095 // if the principal is assigned to a machine. Otherwise, we 1096 // will add the subordinate's storage along with the principal's 1097 // when the principal is assigned to a machine. 1098 var machineAssignable machineAssignable 1099 if a.doc.Subordinate { 1100 pu, err := a.st.Unit(args.principalName) 1101 if err != nil { 1102 return "", nil, errors.Trace(err) 1103 } 1104 machineAssignable = pu 1105 } 1106 storageOps, numStorageAttachments, err := createStorageOps( 1107 a.st, 1108 unitTag, 1109 charm.Meta(), 1110 args.storageCons, 1111 a.doc.Series, 1112 machineAssignable, 1113 ) 1114 if err != nil { 1115 return "", nil, errors.Trace(err) 1116 } 1117 1118 docID := a.st.docID(name) 1119 globalKey := unitGlobalKey(name) 1120 agentGlobalKey := unitAgentGlobalKey(name) 1121 udoc := &unitDoc{ 1122 DocID: docID, 1123 Name: name, 1124 Application: a.doc.Name, 1125 Series: a.doc.Series, 1126 Life: Alive, 1127 Principal: args.principalName, 1128 StorageAttachmentCount: numStorageAttachments, 1129 } 1130 now := a.st.clock.Now() 1131 agentStatusDoc := statusDoc{ 1132 Status: status.Allocating, 1133 Updated: now.UnixNano(), 1134 } 1135 unitStatusDoc := statusDoc{ 1136 Status: status.Waiting, 1137 StatusInfo: status.MessageWaitForMachine, 1138 Updated: now.UnixNano(), 1139 } 1140 workloadVersionDoc := statusDoc{ 1141 Status: status.Unknown, 1142 Updated: now.UnixNano(), 1143 } 1144 1145 ops, err := addUnitOps(a.st, addUnitOpsArgs{ 1146 unitDoc: udoc, 1147 agentStatusDoc: agentStatusDoc, 1148 workloadStatusDoc: unitStatusDoc, 1149 workloadVersionDoc: workloadVersionDoc, 1150 meterStatusDoc: &meterStatusDoc{Code: MeterNotSet.String()}, 1151 }) 1152 if err != nil { 1153 return "", nil, errors.Trace(err) 1154 } 1155 1156 ops = append(ops, storageOps...) 1157 1158 if a.doc.Subordinate { 1159 ops = append(ops, txn.Op{ 1160 C: unitsC, 1161 Id: a.st.docID(args.principalName), 1162 Assert: append(isAliveDoc, bson.DocElem{ 1163 "subordinates", bson.D{{"$not", bson.RegEx{Pattern: "^" + a.doc.Name + "/"}}}, 1164 }), 1165 Update: bson.D{{"$addToSet", bson.D{{"subordinates", name}}}}, 1166 }) 1167 } else { 1168 ops = append(ops, createConstraintsOp(a.st, agentGlobalKey, args.cons)) 1169 } 1170 1171 // At the last moment we still have the statusDocs in scope, set the initial 1172 // history entries. This is risky, and may lead to extra entries, but that's 1173 // an intrinsic problem with mixing txn and non-txn ops -- we can't sync 1174 // them cleanly. 1175 probablyUpdateStatusHistory(a.st, globalKey, unitStatusDoc) 1176 probablyUpdateStatusHistory(a.st, agentGlobalKey, agentStatusDoc) 1177 probablyUpdateStatusHistory(a.st, globalWorkloadVersionKey(name), workloadVersionDoc) 1178 return name, ops, nil 1179 } 1180 1181 // incUnitCountOp returns the operation to increment the service's unit count. 1182 func (a *Application) incUnitCountOp(asserts bson.D) txn.Op { 1183 op := txn.Op{ 1184 C: applicationsC, 1185 Id: a.doc.DocID, 1186 Update: bson.D{{"$inc", bson.D{{"unitcount", 1}}}}, 1187 } 1188 if len(asserts) > 0 { 1189 op.Assert = asserts 1190 } 1191 return op 1192 } 1193 1194 // AddUnit adds a new principal unit to the service. 1195 func (a *Application) AddUnit() (unit *Unit, err error) { 1196 defer errors.DeferredAnnotatef(&err, "cannot add unit to application %q", a) 1197 name, ops, err := a.addUnitOps("", nil) 1198 if err != nil { 1199 return nil, err 1200 } 1201 1202 if err := a.st.runTransaction(ops); err == txn.ErrAborted { 1203 if alive, err := isAlive(a.st, applicationsC, a.doc.DocID); err != nil { 1204 return nil, err 1205 } else if !alive { 1206 return nil, errors.New("application is not alive") 1207 } 1208 return nil, errors.New("inconsistent state") 1209 } else if err != nil { 1210 return nil, err 1211 } 1212 return a.st.Unit(name) 1213 } 1214 1215 // removeUnitOps returns the operations necessary to remove the supplied unit, 1216 // assuming the supplied asserts apply to the unit document. 1217 func (a *Application) removeUnitOps(u *Unit, asserts bson.D) ([]txn.Op, error) { 1218 ops, err := u.destroyHostOps(a) 1219 if err != nil { 1220 return nil, err 1221 } 1222 portsOps, err := removePortsForUnitOps(a.st, u) 1223 if err != nil { 1224 return nil, err 1225 } 1226 storageInstanceOps, err := removeStorageInstancesOps(a.st, u.Tag()) 1227 if err != nil { 1228 return nil, err 1229 } 1230 resOps, err := removeUnitResourcesOps(a.st, u.doc.Application, u.doc.Name) 1231 if err != nil { 1232 return nil, errors.Trace(err) 1233 } 1234 ops = append(ops, resOps...) 1235 1236 observedFieldsMatch := bson.D{ 1237 {"charmurl", u.doc.CharmURL}, 1238 {"machineid", u.doc.MachineId}, 1239 } 1240 ops = append(ops, 1241 txn.Op{ 1242 C: unitsC, 1243 Id: u.doc.DocID, 1244 Assert: append(observedFieldsMatch, asserts...), 1245 Remove: true, 1246 }, 1247 removeMeterStatusOp(a.st, u.globalMeterStatusKey()), 1248 removeStatusOp(a.st, u.globalAgentKey()), 1249 removeStatusOp(a.st, u.globalKey()), 1250 removeConstraintsOp(a.st, u.globalAgentKey()), 1251 annotationRemoveOp(a.st, u.globalKey()), 1252 newCleanupOp(cleanupRemovedUnit, u.doc.Name), 1253 ) 1254 ops = append(ops, portsOps...) 1255 ops = append(ops, storageInstanceOps...) 1256 if u.doc.CharmURL != nil { 1257 decOps, err := appCharmDecRefOps(a.st, a.doc.Name, u.doc.CharmURL) 1258 if errors.IsNotFound(err) { 1259 return nil, errRefresh 1260 } else if err != nil { 1261 return nil, err 1262 } 1263 ops = append(ops, decOps...) 1264 } 1265 if a.doc.Life == Dying && a.doc.RelationCount == 0 && a.doc.UnitCount == 1 { 1266 hasLastRef := bson.D{{"life", Dying}, {"relationcount", 0}, {"unitcount", 1}} 1267 removeOps, err := a.removeOps(hasLastRef) 1268 if err != nil { 1269 return nil, errors.Trace(err) 1270 } 1271 return append(ops, removeOps...), nil 1272 } 1273 svcOp := txn.Op{ 1274 C: applicationsC, 1275 Id: a.doc.DocID, 1276 Update: bson.D{{"$inc", bson.D{{"unitcount", -1}}}}, 1277 } 1278 if a.doc.Life == Alive { 1279 svcOp.Assert = bson.D{{"life", Alive}, {"unitcount", bson.D{{"$gt", 0}}}} 1280 } else { 1281 svcOp.Assert = bson.D{ 1282 {"life", Dying}, 1283 {"$or", []bson.D{ 1284 {{"unitcount", bson.D{{"$gt", 1}}}}, 1285 {{"relationcount", bson.D{{"$gt", 0}}}}, 1286 }}, 1287 } 1288 } 1289 ops = append(ops, svcOp) 1290 1291 return ops, nil 1292 } 1293 1294 func removeUnitResourcesOps(st *State, serviceID, unitID string) ([]txn.Op, error) { 1295 persist, err := st.ResourcesPersistence() 1296 if errors.IsNotSupported(err) { 1297 // Nothing to see here, move along. 1298 return nil, nil 1299 } 1300 if err != nil { 1301 return nil, errors.Trace(err) 1302 } 1303 ops, err := persist.NewRemoveUnitResourcesOps(unitID) 1304 if err != nil { 1305 return nil, errors.Trace(err) 1306 } 1307 return ops, nil 1308 } 1309 1310 // AllUnits returns all units of the service. 1311 func (a *Application) AllUnits() (units []*Unit, err error) { 1312 return allUnits(a.st, a.doc.Name) 1313 } 1314 1315 func allUnits(st *State, application string) (units []*Unit, err error) { 1316 unitsCollection, closer := st.getCollection(unitsC) 1317 defer closer() 1318 1319 docs := []unitDoc{} 1320 err = unitsCollection.Find(bson.D{{"application", application}}).All(&docs) 1321 if err != nil { 1322 return nil, errors.Errorf("cannot get all units from application %q: %v", application, err) 1323 } 1324 for i := range docs { 1325 units = append(units, newUnit(st, &docs[i])) 1326 } 1327 return units, nil 1328 } 1329 1330 // Relations returns a Relation for every relation the application is in. 1331 func (a *Application) Relations() (relations []*Relation, err error) { 1332 return applicationRelations(a.st, a.doc.Name) 1333 } 1334 1335 func applicationRelations(st *State, name string) (relations []*Relation, err error) { 1336 defer errors.DeferredAnnotatef(&err, "can't get relations for application %q", name) 1337 relationsCollection, closer := st.getCollection(relationsC) 1338 defer closer() 1339 1340 docs := []relationDoc{} 1341 err = relationsCollection.Find(bson.D{{"endpoints.applicationname", name}}).All(&docs) 1342 if err != nil { 1343 return nil, err 1344 } 1345 for _, v := range docs { 1346 relations = append(relations, newRelation(st, &v)) 1347 } 1348 return relations, nil 1349 } 1350 1351 // ConfigSettings returns the raw user configuration for the application's charm. 1352 // Unset values are omitted. 1353 func (a *Application) ConfigSettings() (charm.Settings, error) { 1354 settings, err := readSettings(a.st, settingsC, a.settingsKey()) 1355 if err != nil { 1356 return nil, err 1357 } 1358 return settings.Map(), nil 1359 } 1360 1361 // UpdateConfigSettings changes a service's charm config settings. Values set 1362 // to nil will be deleted; unknown and invalid values will return an error. 1363 func (a *Application) UpdateConfigSettings(changes charm.Settings) error { 1364 charm, _, err := a.Charm() 1365 if err != nil { 1366 return err 1367 } 1368 changes, err = charm.Config().ValidateSettings(changes) 1369 if err != nil { 1370 return err 1371 } 1372 // TODO(fwereade) state.Settings is itself really problematic in just 1373 // about every use case. This needs to be resolved some time; but at 1374 // least the settings docs are keyed by charm url as well as service 1375 // name, so the actual impact of a race is non-threatening. 1376 node, err := readSettings(a.st, settingsC, a.settingsKey()) 1377 if err != nil { 1378 return err 1379 } 1380 for name, value := range changes { 1381 if value == nil { 1382 node.Delete(name) 1383 } else { 1384 node.Set(name, value) 1385 } 1386 } 1387 _, err = node.Write() 1388 return err 1389 } 1390 1391 // LeaderSettings returns a service's leader settings. If nothing has been set 1392 // yet, it will return an empty map; this is not an error. 1393 func (a *Application) LeaderSettings() (map[string]string, error) { 1394 // There's no compelling reason to have these methods on Service -- and 1395 // thus require an extra db read to access them -- but it stops the State 1396 // type getting even more cluttered. 1397 1398 doc, err := readSettingsDoc(a.st, settingsC, leadershipSettingsKey(a.doc.Name)) 1399 if errors.IsNotFound(err) { 1400 return nil, errors.NotFoundf("application") 1401 } else if err != nil { 1402 return nil, errors.Trace(err) 1403 } 1404 result := make(map[string]string) 1405 for escapedKey, interfaceValue := range doc.Settings { 1406 key := unescapeReplacer.Replace(escapedKey) 1407 if value, _ := interfaceValue.(string); value != "" { 1408 // Empty strings are technically bad data -- when set, they clear. 1409 result[key] = value 1410 } else { 1411 // Some bad data isn't reason enough to obscure the good data. 1412 logger.Warningf("unexpected leader settings value for %s: %#v", key, interfaceValue) 1413 } 1414 } 1415 return result, nil 1416 } 1417 1418 // UpdateLeaderSettings updates the service's leader settings with the supplied 1419 // values, but will fail (with a suitable error) if the supplied Token loses 1420 // validity. Empty values in the supplied map will be cleared in the database. 1421 func (a *Application) UpdateLeaderSettings(token leadership.Token, updates map[string]string) error { 1422 // There's no compelling reason to have these methods on Service -- and 1423 // thus require an extra db read to access them -- but it stops the State 1424 // type getting even more cluttered. 1425 1426 // We can calculate the actual update ahead of time; it's not dependent 1427 // upon the current state of the document. (*Writing* it should depend 1428 // on document state, but that's handled below.) 1429 key := leadershipSettingsKey(a.doc.Name) 1430 sets := bson.M{} 1431 unsets := bson.M{} 1432 for unescapedKey, value := range updates { 1433 key := escapeReplacer.Replace(unescapedKey) 1434 if value == "" { 1435 unsets[key] = 1 1436 } else { 1437 sets[key] = value 1438 } 1439 } 1440 update := setUnsetUpdateSettings(sets, unsets) 1441 1442 isNullChange := func(rawMap map[string]interface{}) bool { 1443 for key := range unsets { 1444 if _, found := rawMap[key]; found { 1445 return false 1446 } 1447 } 1448 for key, value := range sets { 1449 if current := rawMap[key]; current != value { 1450 return false 1451 } 1452 } 1453 return true 1454 } 1455 1456 buildTxn := func(_ int) ([]txn.Op, error) { 1457 // Read the current document state so we can abort if there's 1458 // no actual change; and the version number so we can assert 1459 // on it and prevent these settings from landing late. 1460 doc, err := readSettingsDoc(a.st, settingsC, key) 1461 if errors.IsNotFound(err) { 1462 return nil, errors.NotFoundf("application") 1463 } else if err != nil { 1464 return nil, errors.Trace(err) 1465 } 1466 if isNullChange(doc.Settings) { 1467 return nil, jujutxn.ErrNoOperations 1468 } 1469 return []txn.Op{{ 1470 C: settingsC, 1471 Id: key, 1472 Assert: bson.D{{"version", doc.Version}}, 1473 Update: update, 1474 }}, nil 1475 } 1476 return a.st.run(buildTxnWithLeadership(buildTxn, token)) 1477 } 1478 1479 var ErrSubordinateConstraints = stderrors.New("constraints do not apply to subordinate services") 1480 1481 // Constraints returns the current application constraints. 1482 func (a *Application) Constraints() (constraints.Value, error) { 1483 if a.doc.Subordinate { 1484 return constraints.Value{}, ErrSubordinateConstraints 1485 } 1486 return readConstraints(a.st, a.globalKey()) 1487 } 1488 1489 // SetConstraints replaces the current application constraints. 1490 func (a *Application) SetConstraints(cons constraints.Value) (err error) { 1491 unsupported, err := a.st.validateConstraints(cons) 1492 if len(unsupported) > 0 { 1493 logger.Warningf( 1494 "setting constraints on application %q: unsupported constraints: %v", a.Name(), strings.Join(unsupported, ",")) 1495 } else if err != nil { 1496 return err 1497 } 1498 if a.doc.Subordinate { 1499 return ErrSubordinateConstraints 1500 } 1501 defer errors.DeferredAnnotatef(&err, "cannot set constraints") 1502 if a.doc.Life != Alive { 1503 return errNotAlive 1504 } 1505 ops := []txn.Op{{ 1506 C: applicationsC, 1507 Id: a.doc.DocID, 1508 Assert: isAliveDoc, 1509 }} 1510 ops = append(ops, setConstraintsOp(a.st, a.globalKey(), cons)) 1511 return onAbort(a.st.runTransaction(ops), errNotAlive) 1512 } 1513 1514 // EndpointBindings returns the mapping for each endpoint name and the space 1515 // name it is bound to (or empty if unspecified). When no bindings are stored 1516 // for the application, defaults are returned. 1517 func (a *Application) EndpointBindings() (map[string]string, error) { 1518 // We don't need the TxnRevno below. 1519 bindings, _, err := readEndpointBindings(a.st, a.globalKey()) 1520 if err != nil && !errors.IsNotFound(err) { 1521 return nil, errors.Trace(err) 1522 } 1523 if bindings == nil { 1524 bindings, err = a.defaultEndpointBindings() 1525 if err != nil { 1526 return nil, errors.Trace(err) 1527 } 1528 } 1529 return bindings, nil 1530 } 1531 1532 // defaultEndpointBindings returns a map with each endpoint from the current 1533 // charm metadata bound to an empty space. If no charm URL is set yet, it 1534 // returns an empty map. 1535 func (a *Application) defaultEndpointBindings() (map[string]string, error) { 1536 if a.doc.CharmURL == nil { 1537 return map[string]string{}, nil 1538 } 1539 1540 charm, _, err := a.Charm() 1541 if err != nil { 1542 return nil, errors.Trace(err) 1543 } 1544 1545 return DefaultEndpointBindingsForCharm(charm.Meta()), nil 1546 } 1547 1548 // MetricCredentials returns any metric credentials associated with this service. 1549 func (a *Application) MetricCredentials() []byte { 1550 return a.doc.MetricCredentials 1551 } 1552 1553 // SetMetricCredentials updates the metric credentials associated with this service. 1554 func (a *Application) SetMetricCredentials(b []byte) error { 1555 buildTxn := func(attempt int) ([]txn.Op, error) { 1556 if attempt > 0 { 1557 alive, err := isAlive(a.st, applicationsC, a.doc.DocID) 1558 if err != nil { 1559 return nil, errors.Trace(err) 1560 } else if !alive { 1561 return nil, errNotAlive 1562 } 1563 } 1564 ops := []txn.Op{ 1565 { 1566 C: applicationsC, 1567 Id: a.doc.DocID, 1568 Assert: isAliveDoc, 1569 Update: bson.M{"$set": bson.M{"metric-credentials": b}}, 1570 }, 1571 } 1572 return ops, nil 1573 } 1574 if err := a.st.run(buildTxn); err != nil { 1575 if err == errNotAlive { 1576 return errors.New("cannot update metric credentials: application " + err.Error()) 1577 } 1578 return errors.Annotatef(err, "cannot update metric credentials") 1579 } 1580 a.doc.MetricCredentials = b 1581 return nil 1582 } 1583 1584 // StorageConstraints returns the storage constraints for the application. 1585 func (a *Application) StorageConstraints() (map[string]StorageConstraints, error) { 1586 cons, err := readStorageConstraints(a.st, a.storageConstraintsKey()) 1587 if errors.IsNotFound(err) { 1588 return nil, nil 1589 } else if err != nil { 1590 return nil, errors.Trace(err) 1591 } 1592 return cons, nil 1593 } 1594 1595 // Status returns the status of the service. 1596 // Only unit leaders are allowed to set the status of the service. 1597 // If no status is recorded, then there are no unit leaders and the 1598 // status is derived from the unit status values. 1599 func (a *Application) Status() (status.StatusInfo, error) { 1600 statuses, closer := a.st.getCollection(statusesC) 1601 defer closer() 1602 query := statuses.Find(bson.D{{"_id", a.globalKey()}, {"neverset", true}}) 1603 if count, err := query.Count(); err != nil { 1604 return status.StatusInfo{}, errors.Trace(err) 1605 } else if count != 0 { 1606 // This indicates that SetStatus has never been called on this service. 1607 // This in turn implies the application status document is likely to be 1608 // inaccurate, so we return aggregated unit statuses instead. 1609 // 1610 // TODO(fwereade): this is completely wrong and will produce bad results 1611 // in not-very-challenging scenarios. The leader unit remains responsible 1612 // for setting the application status in a timely way, *whether or not the 1613 // charm's hooks exists or sets an application status*. This logic should be 1614 // removed as soon as possible, and the responsibilities implemented in 1615 // the right places rather than being applied at seeming random. 1616 units, err := a.AllUnits() 1617 if err != nil { 1618 return status.StatusInfo{}, err 1619 } 1620 logger.Tracef("service %q has %d units", a.Name(), len(units)) 1621 if len(units) > 0 { 1622 return a.deriveStatus(units) 1623 } 1624 } 1625 return getStatus(a.st, a.globalKey(), "application") 1626 } 1627 1628 // SetStatus sets the status for the application. 1629 func (a *Application) SetStatus(statusInfo status.StatusInfo) error { 1630 if !status.ValidWorkloadStatus(statusInfo.Status) { 1631 return errors.Errorf("cannot set invalid status %q", statusInfo.Status) 1632 } 1633 return setStatus(a.st, setStatusParams{ 1634 badge: "application", 1635 globalKey: a.globalKey(), 1636 status: statusInfo.Status, 1637 message: statusInfo.Message, 1638 rawData: statusInfo.Data, 1639 updated: statusInfo.Since, 1640 }) 1641 } 1642 1643 // StatusHistory returns a slice of at most filter.Size StatusInfo items 1644 // or items as old as filter.Date or items newer than now - filter.Delta time 1645 // representing past statuses for this service. 1646 func (a *Application) StatusHistory(filter status.StatusHistoryFilter) ([]status.StatusInfo, error) { 1647 args := &statusHistoryArgs{ 1648 st: a.st, 1649 globalKey: a.globalKey(), 1650 filter: filter, 1651 } 1652 return statusHistory(args) 1653 } 1654 1655 // ServiceAndUnitsStatus returns the status for this application and all its units. 1656 func (a *Application) ServiceAndUnitsStatus() (status.StatusInfo, map[string]status.StatusInfo, error) { 1657 serviceStatus, err := a.Status() 1658 if err != nil { 1659 return status.StatusInfo{}, nil, errors.Trace(err) 1660 } 1661 units, err := a.AllUnits() 1662 if err != nil { 1663 return status.StatusInfo{}, nil, err 1664 } 1665 results := make(map[string]status.StatusInfo, len(units)) 1666 for _, unit := range units { 1667 unitStatus, err := unit.Status() 1668 if err != nil { 1669 return status.StatusInfo{}, nil, err 1670 } 1671 results[unit.Name()] = unitStatus 1672 } 1673 return serviceStatus, results, nil 1674 1675 } 1676 1677 func (a *Application) deriveStatus(units []*Unit) (status.StatusInfo, error) { 1678 var result status.StatusInfo 1679 for _, unit := range units { 1680 currentSeverity := statusServerities[result.Status] 1681 unitStatus, err := unit.Status() 1682 if err != nil { 1683 return status.StatusInfo{}, errors.Annotatef(err, "deriving application status from %q", unit.Name()) 1684 } 1685 unitSeverity := statusServerities[unitStatus.Status] 1686 if unitSeverity > currentSeverity { 1687 result.Status = unitStatus.Status 1688 result.Message = unitStatus.Message 1689 result.Data = unitStatus.Data 1690 result.Since = unitStatus.Since 1691 } 1692 } 1693 return result, nil 1694 } 1695 1696 // statusSeverities holds status values with a severity measure. 1697 // Status values with higher severity are used in preference to others. 1698 var statusServerities = map[status.Status]int{ 1699 status.Error: 100, 1700 status.Blocked: 90, 1701 status.Waiting: 80, 1702 status.Maintenance: 70, 1703 status.Terminated: 60, 1704 status.Active: 50, 1705 status.Unknown: 40, 1706 } 1707 1708 type addApplicationOpsArgs struct { 1709 applicationDoc *applicationDoc 1710 statusDoc statusDoc 1711 constraints constraints.Value 1712 storage map[string]StorageConstraints 1713 settings map[string]interface{} 1714 // These are nil when adding a new service, and most likely 1715 // non-nil when migrating. 1716 leadershipSettings map[string]interface{} 1717 } 1718 1719 // addApplicationOps returns the operations required to add an application to the 1720 // services collection, along with all the associated expected other service 1721 // entries. This method is used by both the *State.AddService method and the 1722 // migration import code. 1723 func addApplicationOps(st *State, args addApplicationOpsArgs) ([]txn.Op, error) { 1724 svc := newApplication(st, args.applicationDoc) 1725 1726 charmRefOps, err := appCharmIncRefOps(st, args.applicationDoc.Name, args.applicationDoc.CharmURL, true) 1727 if err != nil { 1728 return nil, errors.Trace(err) 1729 } 1730 1731 globalKey := svc.globalKey() 1732 settingsKey := svc.settingsKey() 1733 storageConstraintsKey := svc.storageConstraintsKey() 1734 leadershipKey := leadershipSettingsKey(svc.Name()) 1735 1736 ops := []txn.Op{ 1737 createConstraintsOp(st, globalKey, args.constraints), 1738 createStorageConstraintsOp(storageConstraintsKey, args.storage), 1739 createSettingsOp(settingsC, settingsKey, args.settings), 1740 createSettingsOp(settingsC, leadershipKey, args.leadershipSettings), 1741 createStatusOp(st, globalKey, args.statusDoc), 1742 addModelServiceRefOp(st, svc.Name()), 1743 } 1744 ops = append(ops, charmRefOps...) 1745 ops = append(ops, txn.Op{ 1746 C: applicationsC, 1747 Id: svc.Name(), 1748 Assert: txn.DocMissing, 1749 Insert: args.applicationDoc, 1750 }) 1751 return ops, nil 1752 }