github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/core/lease/client.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package lease
     5  
     6  import (
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/juju/errors"
    11  )
    12  
    13  // Client manipulates leases directly, and is most likely to be seen set on a
    14  // worker/lease.ManagerConfig struct (and used by the Manager). Implementations
    15  // of Client are not expected to be goroutine-safe.
    16  type Client interface {
    17  
    18  	// ClaimLease records the supplied holder's claim to the supplied lease. If
    19  	// it succeeds, the claim is guaranteed until at least the supplied duration
    20  	// after the call to ClaimLease was initiated. If it returns ErrInvalid,
    21  	// check Leases() for updated state.
    22  	ClaimLease(lease string, request Request) error
    23  
    24  	// ExtendLease records the supplied holder's continued claim to the supplied
    25  	// lease, if necessary. If it succeeds, the claim is guaranteed until at
    26  	// least the supplied duration after the call to ExtendLease was initiated.
    27  	// If it returns ErrInvalid, check Leases() for updated state.
    28  	ExtendLease(lease string, request Request) error
    29  
    30  	// ExpireLease records the vacation of the supplied lease. It will fail if
    31  	// we cannot verify that the lease's writer considers the expiry time to
    32  	// have passed. If it returns ErrInvalid, check Leases() for updated state.
    33  	ExpireLease(lease string) error
    34  
    35  	// Leases returns a recent snapshot of lease state. Expiry times are
    36  	// expressed according to the Clock the client was configured with.
    37  	Leases() map[string]Info
    38  
    39  	// Refresh reads all lease state from the database.
    40  	Refresh() error
    41  }
    42  
    43  // Info holds substrate-independent information about a lease; and a substrate-
    44  // specific trapdoor func.
    45  type Info struct {
    46  
    47  	// Holder is the name of the current leaseholder.
    48  	Holder string
    49  
    50  	// Expiry is the latest time at which it's possible the lease might still
    51  	// be valid. Attempting to expire the lease before this time will fail.
    52  	Expiry time.Time
    53  
    54  	// Trapdoor exposes the originating Client's persistence substrate, if the
    55  	// substrate exposes any such capability. It's useful specifically for
    56  	// integrating mgo/txn-based components: which thus get a mechanism for
    57  	// extracting assertion operations they can use to gate other substrate
    58  	// changes on lease state.
    59  	Trapdoor Trapdoor
    60  }
    61  
    62  // Trapdoor allows a client to use pre-agreed special knowledge to communicate
    63  // with a Client substrate by passing a key with suitable properties.
    64  type Trapdoor func(key interface{}) error
    65  
    66  // LockedTrapdoor is a Trapdoor suitable for use by substrates that don't want
    67  // or need to expose their internals.
    68  func LockedTrapdoor(key interface{}) error {
    69  	if key != nil {
    70  		return errors.New("lease substrate not accessible")
    71  	}
    72  	return nil
    73  }
    74  
    75  // Request describes a lease request.
    76  type Request struct {
    77  
    78  	// Holder identifies the lease holder.
    79  	Holder string
    80  
    81  	// Duration specifies the time for which the lease is required.
    82  	Duration time.Duration
    83  }
    84  
    85  // Validate returns an error if any fields are invalid or inconsistent.
    86  func (request Request) Validate() error {
    87  	if err := ValidateString(request.Holder); err != nil {
    88  		return errors.Annotatef(err, "invalid holder")
    89  	}
    90  	if request.Duration <= 0 {
    91  		return errors.Errorf("invalid duration")
    92  	}
    93  	return nil
    94  }
    95  
    96  // ValidateString returns an error if the string is empty, or if it contains
    97  // whitespace, or if it contains any character in `.#$`. Client implementations
    98  // are expected to always reject invalid strings, and never to produce them.
    99  func ValidateString(s string) error {
   100  	if s == "" {
   101  		return errors.New("string is empty")
   102  	}
   103  	if strings.ContainsAny(s, ".$# \t\r\n") {
   104  		return errors.New("string contains forbidden characters")
   105  	}
   106  	return nil
   107  }
   108  
   109  // ErrInvalid indicates that a Client operation failed because latest state
   110  // indicates that it's a logical impossibility. It's a short-range signal to
   111  // calling code only; that code should never pass it on, but should inspect
   112  // the Client's updated Leases() and either attempt a new operation or return
   113  // a new error at a suitable level of abstraction.
   114  var ErrInvalid = errors.New("invalid lease operation")