github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/state/bakerystorage/storage.go (about)

     1  // Copyright 2014-2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package bakerystorage
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/errors"
    10  	"gopkg.in/macaroon-bakery.v1/bakery"
    11  	"gopkg.in/mgo.v2"
    12  )
    13  
    14  var expiryTimeIndex = mgo.Index{
    15  	Key:    []string{"expire-at"},
    16  	Sparse: true,
    17  
    18  	// We expire records when the clock time is one
    19  	// second older than the record's expire-at field
    20  	// value. It has to be at least one second, because
    21  	// mgo uses "omitempty" for this field.
    22  	ExpireAfter: time.Second,
    23  }
    24  
    25  type storage struct {
    26  	config   Config
    27  	expireAt time.Time
    28  }
    29  
    30  type storageDoc struct {
    31  	Location string    `bson:"_id"`
    32  	Item     string    `bson:"item"`
    33  	ExpireAt time.Time `bson:"expire-at,omitempty"`
    34  }
    35  
    36  // ExpireAt implements ExpirableStorage.ExpireAt.
    37  func (s *storage) ExpireAt(expireAt time.Time) ExpirableStorage {
    38  	return &storage{s.config, expireAt}
    39  }
    40  
    41  // Put implements bakery.Storage.Put.
    42  func (s *storage) Put(location, item string) error {
    43  	coll, closer := s.config.GetCollection()
    44  	defer closer()
    45  
    46  	doc := storageDoc{
    47  		Location: location,
    48  		Item:     item,
    49  	}
    50  	if !s.expireAt.IsZero() {
    51  		// NOTE(axw) we subtract one second from the expiry time, because
    52  		// the expireAfterSeconds index we create is 1 and not 0 due to
    53  		// a limitation in the mgo EnsureIndex API.
    54  		doc.ExpireAt = s.expireAt.Add(-1 * time.Second)
    55  	}
    56  	_, err := coll.Writeable().UpsertId(location, doc)
    57  	if err != nil {
    58  		return errors.Annotatef(err, "cannot store item for location %q", location)
    59  	}
    60  	return nil
    61  }
    62  
    63  // Get implements bakery.Storage.Get.
    64  func (s *storage) Get(location string) (string, error) {
    65  	coll, closer := s.config.GetCollection()
    66  	defer closer()
    67  
    68  	var i storageDoc
    69  	err := coll.FindId(location).One(&i)
    70  	if err != nil {
    71  		if err == mgo.ErrNotFound {
    72  			return "", bakery.ErrNotFound
    73  		}
    74  		return "", errors.Annotatef(err, "cannot get item for location %q", location)
    75  	}
    76  	return i.Item, nil
    77  }
    78  
    79  // Del implements bakery.Storage.Del.
    80  func (s *storage) Del(location string) error {
    81  	coll, closer := s.config.GetCollection()
    82  	defer closer()
    83  
    84  	err := coll.Writeable().RemoveId(location)
    85  	if err != nil {
    86  		if err == mgo.ErrNotFound {
    87  			// Not an error to remove an item that doesn't exist.
    88  			return nil
    89  		}
    90  		return errors.Annotatef(err, "cannot remove item for location %q", location)
    91  	}
    92  	return nil
    93  }