// ExpireLease is part of the Client interface. func (client *client) ExpireLease(name string) error { if err := lease.ValidateString(name); err != nil { return errors.Annotatef(err, "invalid name") } // No cache updates needed, only deletes; no closure here. err := client.config.Mongo.RunTransaction(func(attempt int) ([]txn.Op, error) { client.logger.Tracef("expiring lease %q (attempt %d)", name, attempt) // On the first attempt, assume cache is good. if attempt > 0 { if err := client.Refresh(); err != nil { return nil, errors.Trace(err) } } // No special error handling here. ops, err := client.expireLeaseOps(name) if err != nil { return nil, errors.Trace(err) } return ops, nil }) if err != nil { if errors.Cause(err) == lease.ErrInvalid { return lease.ErrInvalid } return errors.Trace(err) } // Uncache this lease entry. delete(client.entries, name) return nil }
// Validate returns an error if the supplied config is not valid. func (config ClientConfig) Validate() error { if err := lease.ValidateString(config.Id); err != nil { return errors.Annotatef(err, "invalid id") } if err := lease.ValidateString(config.Namespace); err != nil { return errors.Annotatef(err, "invalid namespace") } if err := lease.ValidateString(config.Collection); err != nil { return errors.Annotatef(err, "invalid collection") } if config.Mongo == nil { return errors.New("missing mongo") } if config.Clock == nil { return errors.New("missing clock") } return nil }
// validate returns an error if any fields are invalid or inconsistent. func (doc leaseDoc) validate() error { if doc.Type != typeLease { return errors.Errorf("invalid type %q", doc.Type) } // state.multiEnvRunner prepends environ ids in our documents, and // state.envStateCollection does not strip them out. if !strings.HasSuffix(doc.Id, leaseDocId(doc.Namespace, doc.Name)) { return errors.Errorf("inconsistent _id") } if err := lease.ValidateString(doc.Holder); err != nil { return errors.Annotatef(err, "invalid holder") } if doc.Expiry == 0 { return errors.Errorf("invalid expiry") } if err := lease.ValidateString(doc.Writer); err != nil { return errors.Annotatef(err, "invalid writer") } return nil }
// request implements ClaimLease and ExtendLease. func (client *client) request(name string, request lease.Request, getOps opsFunc, verb string) error { if err := lease.ValidateString(name); err != nil { return errors.Annotatef(err, "invalid name") } if err := request.Validate(); err != nil { return errors.Annotatef(err, "invalid request") } // Close over cacheEntry to record in case of success. var cacheEntry entry err := client.config.Mongo.RunTransaction(func(attempt int) ([]txn.Op, error) { client.logger.Tracef("%s lease %q for %s (attempt %d)", verb, name, request, attempt) // On the first attempt, assume cache is good. if attempt > 0 { if err := client.Refresh(); err != nil { return nil, errors.Trace(err) } } // It's possible that the request is for an "extension" isn't an // extension at all; this isn't a problem, but does require separate // handling. ops, nextEntry, err := getOps(name, request) cacheEntry = nextEntry if errors.Cause(err) == errNoExtension { return nil, jujutxn.ErrNoOperations } if err != nil { return nil, errors.Trace(err) } return ops, nil }) if err != nil { if errors.Cause(err) == lease.ErrInvalid { return lease.ErrInvalid } return errors.Annotate(err, "cannot satisfy request") } // Update the cache for this lease only. client.entries[name] = cacheEntry return nil }