github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/state/lease/schema.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  	"fmt"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/juju/errors"
    12  
    13  	"github.com/juju/juju/core/lease"
    14  )
    15  
    16  // These constants define the field names and type values used by documents in
    17  // a lease collection. They *must* remain in sync with the bson marshalling
    18  // annotations in leaseDoc.
    19  const (
    20  	// field* identify the fields in a leaseDoc.
    21  	fieldNamespace = "namespace"
    22  	fieldHolder    = "holder"
    23  	fieldStart     = "start"
    24  	fieldDuration  = "duration"
    25  	fieldWriter    = "writer"
    26  )
    27  
    28  // toInt64 converts a local time.Time into a database value that doesn't
    29  // silently lose precision.
    30  func toInt64(t time.Time) int64 {
    31  	return t.UnixNano()
    32  }
    33  
    34  // toTime converts a toInt64 result, as loaded from the db, back to a time.Time.
    35  func toTime(v int64) time.Time {
    36  	return time.Unix(0, v)
    37  }
    38  
    39  // leaseDocId returns the _id for the document holding details of the supplied
    40  // namespace and lease.
    41  func leaseDocId(namespace, lease string) string {
    42  	return fmt.Sprintf("%s#%s#", namespace, lease)
    43  }
    44  
    45  // leaseDoc is used to serialise lease entries.
    46  type leaseDoc struct {
    47  	// Id is always "<Namespace>#<Name>#", so that we can extract useful
    48  	// information from a stream of watcher events without incurring extra
    49  	// DB hits. Apart from checking validity on load, though, there's
    50  	// little reason to use Id elsewhere; Namespace and Name are the
    51  	// sources of truth.
    52  	Id        string `bson:"_id"`
    53  	Namespace string `bson:"namespace"`
    54  	Name      string `bson:"name"`
    55  
    56  	// Holder, Expiry, and Writer map directly to entry.
    57  	Holder   string        `bson:"holder"`
    58  	Start    int64         `bson:"start"`
    59  	Duration time.Duration `bson:"duration"`
    60  	Writer   string        `bson:"writer"`
    61  }
    62  
    63  // validate returns an error if any fields are invalid or inconsistent.
    64  func (doc leaseDoc) validate() error {
    65  	// state.multiModelRunner prepends environ ids in our documents, and
    66  	// state.modelStateCollection does not strip them out.
    67  	if !strings.HasSuffix(doc.Id, leaseDocId(doc.Namespace, doc.Name)) {
    68  		return errors.Errorf("inconsistent _id")
    69  	}
    70  	if err := lease.ValidateString(doc.Holder); err != nil {
    71  		return errors.Annotatef(err, "invalid holder")
    72  	}
    73  	if doc.Start < 0 {
    74  		return errors.Errorf("invalid start time")
    75  	}
    76  	if doc.Duration <= 0 {
    77  		return errors.Errorf("invalid duration")
    78  	}
    79  	if err := lease.ValidateString(doc.Writer); err != nil {
    80  		return errors.Annotatef(err, "invalid writer")
    81  	}
    82  	return nil
    83  }
    84  
    85  // entry returns the lease name and an entry corresponding to the document. If
    86  // the document cannot be validated, it returns an error.
    87  func (doc leaseDoc) entry() (string, entry, error) {
    88  	if err := doc.validate(); err != nil {
    89  		return "", entry{}, errors.Trace(err)
    90  	}
    91  	entry := entry{
    92  		holder:   doc.Holder,
    93  		start:    toTime(doc.Start),
    94  		duration: doc.Duration,
    95  		writer:   doc.Writer,
    96  	}
    97  	return doc.Name, entry, nil
    98  }
    99  
   100  // newLeaseDoc returns a valid lease document encoding the supplied lease and
   101  // entry in the supplied namespace, or an error.
   102  func newLeaseDoc(namespace, name string, entry entry) (*leaseDoc, error) {
   103  	doc := &leaseDoc{
   104  		Id:        leaseDocId(namespace, name),
   105  		Namespace: namespace,
   106  		Name:      name,
   107  		Holder:    entry.holder,
   108  		Start:     toInt64(entry.start),
   109  		Duration:  entry.duration,
   110  		Writer:    entry.writer,
   111  	}
   112  	if err := doc.validate(); err != nil {
   113  		return nil, errors.Trace(err)
   114  	}
   115  	return doc, nil
   116  }