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