Example #1
0
// 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
}
Example #2
0
// 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
}
Example #3
0
// 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
}
Example #4
0
// 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
}