github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/relationunit.go (about) 1 // Copyright 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 "strings" 10 11 "github.com/juju/errors" 12 "github.com/juju/names" 13 jujutxn "github.com/juju/txn" 14 "gopkg.in/juju/charm.v6-unstable" 15 "gopkg.in/mgo.v2" 16 "gopkg.in/mgo.v2/bson" 17 "gopkg.in/mgo.v2/txn" 18 19 "github.com/juju/juju/network" 20 ) 21 22 // RelationUnit holds information about a single unit in a relation, and 23 // allows clients to conveniently access unit-specific functionality. 24 type RelationUnit struct { 25 st *State 26 relation *Relation 27 unit *Unit 28 endpoint Endpoint 29 scope string 30 } 31 32 // Relation returns the relation associated with the unit. 33 func (ru *RelationUnit) Relation() *Relation { 34 return ru.relation 35 } 36 37 // Endpoint returns the relation endpoint that defines the unit's 38 // participation in the relation. 39 func (ru *RelationUnit) Endpoint() Endpoint { 40 return ru.endpoint 41 } 42 43 // PrivateAddress returns the private address of the unit. 44 func (ru *RelationUnit) PrivateAddress() (network.Address, error) { 45 return ru.unit.PrivateAddress() 46 } 47 48 // ErrCannotEnterScope indicates that a relation unit failed to enter its scope 49 // due to either the unit or the relation not being Alive. 50 var ErrCannotEnterScope = stderrors.New("cannot enter scope: unit or relation is not alive") 51 52 // ErrCannotEnterScopeYet indicates that a relation unit failed to enter its 53 // scope due to a required and pre-existing subordinate unit that is not Alive. 54 // Once that subordinate has been removed, a new one can be created. 55 var ErrCannotEnterScopeYet = stderrors.New("cannot enter scope yet: non-alive subordinate unit has not been removed") 56 57 // EnterScope ensures that the unit has entered its scope in the relation. 58 // When the unit has already entered its relation scope, EnterScope will report 59 // success but make no changes to state. 60 // 61 // Otherwise, assuming both the relation and the unit are alive, it will enter 62 // scope and create or overwrite the unit's settings in the relation according 63 // to the supplied map. 64 // 65 // If the unit is a principal and the relation has container scope, EnterScope 66 // will also create the required subordinate unit, if it does not already exist; 67 // this is because there's no point having a principal in scope if there is no 68 // corresponding subordinate to join it. 69 // 70 // Once a unit has entered a scope, it stays in scope without further 71 // intervention; the relation will not be able to become Dead until all units 72 // have departed its scopes. 73 func (ru *RelationUnit) EnterScope(settings map[string]interface{}) error { 74 db, closer := ru.st.newDB() 75 defer closer() 76 relationScopes, closer := db.GetCollection(relationScopesC) 77 defer closer() 78 79 // Verify that the unit is not already in scope, and abort without error 80 // if it is. 81 ruKey := ru.key() 82 if count, err := relationScopes.FindId(ruKey).Count(); err != nil { 83 return err 84 } else if count != 0 { 85 return nil 86 } 87 88 // Collect the operations necessary to enter scope, as follows: 89 // * Check unit and relation state, and incref the relation. 90 // * TODO(fwereade): check unit status == params.StatusActive (this 91 // breaks a bunch of tests in a boring but noisy-to-fix way, and is 92 // being saved for a followup). 93 unitDocID, relationDocID := ru.unit.doc.DocID, ru.relation.doc.DocID 94 ops := []txn.Op{{ 95 C: unitsC, 96 Id: unitDocID, 97 Assert: isAliveDoc, 98 }, { 99 C: relationsC, 100 Id: relationDocID, 101 Assert: isAliveDoc, 102 Update: bson.D{{"$inc", bson.D{{"unitcount", 1}}}}, 103 }} 104 105 // * Create the unit settings in this relation, if they do not already 106 // exist; or completely overwrite them if they do. This must happen 107 // before we create the scope doc, because the existence of a scope doc 108 // is considered to be a guarantee of the existence of a settings doc. 109 settingsChanged := func() (bool, error) { return false, nil } 110 settingsColl, closer := db.GetCollection(settingsC) 111 defer closer() 112 if count, err := settingsColl.FindId(ruKey).Count(); err != nil { 113 return err 114 } else if count == 0 { 115 ops = append(ops, createSettingsOp(ruKey, settings)) 116 } else { 117 var rop txn.Op 118 rop, settingsChanged, err = replaceSettingsOp(ru.st, ruKey, settings) 119 if err != nil { 120 return err 121 } 122 ops = append(ops, rop) 123 } 124 125 // * Create the scope doc. 126 ops = append(ops, txn.Op{ 127 C: relationScopesC, 128 Id: ruKey, 129 Assert: txn.DocMissing, 130 Insert: relationScopeDoc{ 131 Key: ruKey, 132 }, 133 }) 134 135 // * If the unit should have a subordinate, and does not, create it. 136 var existingSubName string 137 if subOps, subName, err := ru.subordinateOps(); err != nil { 138 return err 139 } else { 140 existingSubName = subName 141 ops = append(ops, subOps...) 142 } 143 144 // Now run the complete transaction, or figure out why we can't. 145 if err := ru.st.runTransaction(ops); err != txn.ErrAborted { 146 return err 147 } 148 if count, err := relationScopes.FindId(ruKey).Count(); err != nil { 149 return err 150 } else if count != 0 { 151 // The scope document exists, so we're actually already in scope. 152 return nil 153 } 154 155 units, closer := db.GetCollection(unitsC) 156 defer closer() 157 relations, closer := db.GetCollection(relationsC) 158 defer closer() 159 160 // The relation or unit might no longer be Alive. (Note that there is no 161 // need for additional checks if we're trying to create a subordinate 162 // unit: this could fail due to the subordinate service's not being Alive, 163 // but this case will always be caught by the check for the relation's 164 // life (because a relation cannot be Alive if its services are not).) 165 if alive, err := isAliveWithSession(units, unitDocID); err != nil { 166 return err 167 } else if !alive { 168 return ErrCannotEnterScope 169 } 170 if alive, err := isAliveWithSession(relations, relationDocID); err != nil { 171 return err 172 } else if !alive { 173 return ErrCannotEnterScope 174 } 175 176 // Maybe a subordinate used to exist, but is no longer alive. If that is 177 // case, we will be unable to enter scope until that unit is gone. 178 if existingSubName != "" { 179 if alive, err := isAliveWithSession(units, existingSubName); err != nil { 180 return err 181 } else if !alive { 182 return ErrCannotEnterScopeYet 183 } 184 } 185 186 // It's possible that there was a pre-existing settings doc whose version 187 // has changed under our feet, preventing us from clearing it properly; if 188 // that is the case, something is seriously wrong (nobody else should be 189 // touching that doc under our feet) and we should bail out. 190 prefix := fmt.Sprintf("cannot enter scope for unit %q in relation %q: ", ru.unit, ru.relation) 191 if changed, err := settingsChanged(); err != nil { 192 return err 193 } else if changed { 194 return fmt.Errorf(prefix + "concurrent settings change detected") 195 } 196 197 // Apparently, all our assertions should have passed, but the txn was 198 // aborted: something is really seriously wrong. 199 return fmt.Errorf(prefix + "inconsistent state in EnterScope") 200 } 201 202 // subordinateOps returns any txn operations necessary to ensure sane 203 // subordinate state when entering scope. If a required subordinate unit 204 // exists and is Alive, its name will be returned as well; if one exists 205 // but is not Alive, ErrCannotEnterScopeYet is returned. 206 func (ru *RelationUnit) subordinateOps() ([]txn.Op, string, error) { 207 units, closer := ru.st.getCollection(unitsC) 208 defer closer() 209 210 if !ru.unit.IsPrincipal() || ru.endpoint.Scope != charm.ScopeContainer { 211 return nil, "", nil 212 } 213 related, err := ru.relation.RelatedEndpoints(ru.endpoint.ServiceName) 214 if err != nil { 215 return nil, "", err 216 } 217 if len(related) != 1 { 218 return nil, "", fmt.Errorf("expected single related endpoint, got %v", related) 219 } 220 serviceName, unitName := related[0].ServiceName, ru.unit.doc.Name 221 selSubordinate := bson.D{{"service", serviceName}, {"principal", unitName}} 222 var lDoc lifeDoc 223 if err := units.Find(selSubordinate).One(&lDoc); err == mgo.ErrNotFound { 224 service, err := ru.st.Service(serviceName) 225 if err != nil { 226 return nil, "", err 227 } 228 _, ops, err := service.addUnitOps(unitName, nil) 229 return ops, "", err 230 } else if err != nil { 231 return nil, "", err 232 } else if lDoc.Life != Alive { 233 return nil, "", ErrCannotEnterScopeYet 234 } 235 return []txn.Op{{ 236 C: unitsC, 237 Id: lDoc.Id, 238 Assert: isAliveDoc, 239 }}, lDoc.Id, nil 240 } 241 242 // PrepareLeaveScope causes the unit to be reported as departed by watchers, 243 // but does not *actually* leave the scope, to avoid triggering relation 244 // cleanup. 245 func (ru *RelationUnit) PrepareLeaveScope() error { 246 relationScopes, closer := ru.st.getCollection(relationScopesC) 247 defer closer() 248 249 key := ru.key() 250 if count, err := relationScopes.FindId(key).Count(); err != nil { 251 return err 252 } else if count == 0 { 253 return nil 254 } 255 ops := []txn.Op{{ 256 C: relationScopesC, 257 Id: key, 258 Update: bson.D{{"$set", bson.D{{"departing", true}}}}, 259 }} 260 return ru.st.runTransaction(ops) 261 } 262 263 // LeaveScope signals that the unit has left its scope in the relation. 264 // After the unit has left its relation scope, it is no longer a member 265 // of the relation; if the relation is dying when its last member unit 266 // leaves, it is removed immediately. It is not an error to leave a scope 267 // that the unit is not, or never was, a member of. 268 func (ru *RelationUnit) LeaveScope() error { 269 relationScopes, closer := ru.st.getCollection(relationScopesC) 270 defer closer() 271 272 key := ru.key() 273 // The logic below is involved because we remove a dying relation 274 // with the last unit that leaves a scope in it. It handles three 275 // possible cases: 276 // 277 // 1. Relation is alive: just leave the scope. 278 // 279 // 2. Relation is dying, and other units remain: just leave the scope. 280 // 281 // 3. Relation is dying, and this is the last unit: leave the scope 282 // and remove the relation. 283 // 284 // In each of those cases, proper assertions are done to guarantee 285 // that the condition observed is still valid when the transaction is 286 // applied. If an abort happens, it observes the new condition and 287 // retries. In theory, a worst case will try at most all of the 288 // conditions once, because units cannot join a scope once its relation 289 // is dying. 290 // 291 // Keep in mind that in the first iteration of the loop it's possible 292 // to have a Dying relation with a smaller-than-real unit count, because 293 // Destroy changes the Life attribute in memory (units could join before 294 // the database is actually changed). 295 desc := fmt.Sprintf("unit %q in relation %q", ru.unit, ru.relation) 296 buildTxn := func(attempt int) ([]txn.Op, error) { 297 if attempt > 0 { 298 if err := ru.relation.Refresh(); errors.IsNotFound(err) { 299 return nil, jujutxn.ErrNoOperations 300 } else if err != nil { 301 return nil, err 302 } 303 } 304 count, err := relationScopes.FindId(key).Count() 305 if err != nil { 306 return nil, fmt.Errorf("cannot examine scope for %s: %v", desc, err) 307 } else if count == 0 { 308 return nil, jujutxn.ErrNoOperations 309 } 310 ops := []txn.Op{{ 311 C: relationScopesC, 312 Id: key, 313 Assert: txn.DocExists, 314 Remove: true, 315 }} 316 if ru.relation.doc.Life == Alive { 317 ops = append(ops, txn.Op{ 318 C: relationsC, 319 Id: ru.relation.doc.DocID, 320 Assert: bson.D{{"life", Alive}}, 321 Update: bson.D{{"$inc", bson.D{{"unitcount", -1}}}}, 322 }) 323 } else if ru.relation.doc.UnitCount > 1 { 324 ops = append(ops, txn.Op{ 325 C: relationsC, 326 Id: ru.relation.doc.DocID, 327 Assert: bson.D{{"unitcount", bson.D{{"$gt", 1}}}}, 328 Update: bson.D{{"$inc", bson.D{{"unitcount", -1}}}}, 329 }) 330 } else { 331 relOps, err := ru.relation.removeOps("", ru.unit) 332 if err != nil { 333 return nil, err 334 } 335 ops = append(ops, relOps...) 336 } 337 return ops, nil 338 } 339 if err := ru.st.run(buildTxn); err != nil { 340 return errors.Annotatef(err, "cannot leave scope for %s", desc) 341 } 342 return nil 343 } 344 345 // InScope returns whether the relation unit has entered scope and not left it. 346 func (ru *RelationUnit) InScope() (bool, error) { 347 return ru.inScope(nil) 348 } 349 350 // Joined returns whether the relation unit has entered scope and neither left 351 // it nor prepared to leave it. 352 func (ru *RelationUnit) Joined() (bool, error) { 353 return ru.inScope(bson.D{{"departing", bson.D{{"$ne", true}}}}) 354 } 355 356 // inScope returns whether a scope document exists satisfying the supplied 357 // selector. 358 func (ru *RelationUnit) inScope(sel bson.D) (bool, error) { 359 relationScopes, closer := ru.st.getCollection(relationScopesC) 360 defer closer() 361 362 sel = append(sel, bson.D{{"_id", ru.key()}}...) 363 count, err := relationScopes.Find(sel).Count() 364 if err != nil { 365 return false, err 366 } 367 return count > 0, nil 368 } 369 370 // WatchScope returns a watcher which notifies of counterpart units 371 // entering and leaving the unit's scope. 372 func (ru *RelationUnit) WatchScope() *RelationScopeWatcher { 373 role := counterpartRole(ru.endpoint.Role) 374 scope := ru.scope + "#" + string(role) 375 return newRelationScopeWatcher(ru.st, scope, ru.unit.Name()) 376 } 377 378 // Settings returns a Settings which allows access to the unit's settings 379 // within the relation. 380 func (ru *RelationUnit) Settings() (*Settings, error) { 381 return readSettings(ru.st, ru.key()) 382 } 383 384 // ReadSettings returns a map holding the settings of the unit with the 385 // supplied name within this relation. An error will be returned if the 386 // relation no longer exists, or if the unit's service is not part of the 387 // relation, or the settings are invalid; but mere non-existence of the 388 // unit is not grounds for an error, because the unit settings are 389 // guaranteed to persist for the lifetime of the relation, regardless 390 // of the lifetime of the unit. 391 func (ru *RelationUnit) ReadSettings(uname string) (m map[string]interface{}, err error) { 392 defer errors.DeferredAnnotatef(&err, "cannot read settings for unit %q in relation %q", uname, ru.relation) 393 if !names.IsValidUnit(uname) { 394 return nil, fmt.Errorf("%q is not a valid unit name", uname) 395 } 396 key, err := ru.unitKey(uname) 397 if err != nil { 398 return nil, err 399 } 400 node, err := readSettings(ru.st, key) 401 if err != nil { 402 return nil, err 403 } 404 return node.Map(), nil 405 } 406 407 // unitKey returns a string, based on the relation and the supplied unit name, 408 // which is used as a key for that unit within this relation in the settings, 409 // presence, and relationScopes collections. 410 func (ru *RelationUnit) unitKey(uname string) (string, error) { 411 uparts := strings.Split(uname, "/") 412 sname := uparts[0] 413 ep, err := ru.relation.Endpoint(sname) 414 if err != nil { 415 return "", err 416 } 417 return ru._key(string(ep.Role), uname), nil 418 } 419 420 // key returns a string, based on the relation and the current unit name, 421 // which is used as a key for that unit within this relation in the settings, 422 // presence, and relationScopes collections. 423 func (ru *RelationUnit) key() string { 424 return ru._key(string(ru.endpoint.Role), ru.unit.Name()) 425 } 426 427 func (ru *RelationUnit) _key(role, unitname string) string { 428 parts := []string{ru.scope, role, unitname} 429 return strings.Join(parts, "#") 430 } 431 432 // relationScopeDoc represents a unit which is in a relation scope. 433 // The relation, container, role, and unit are all encoded in the key. 434 type relationScopeDoc struct { 435 DocID string `bson:"_id"` 436 Key string `bson:"key"` 437 ModelUUID string `bson:"model-uuid"` 438 Departing bool 439 } 440 441 func (d *relationScopeDoc) unitName() string { 442 return unitNameFromScopeKey(d.Key) 443 } 444 445 func unitNameFromScopeKey(key string) string { 446 parts := strings.Split(key, "#") 447 return parts[len(parts)-1] 448 }