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