github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/binarystorage/layered.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package binarystorage
     5  
     6  import (
     7  	"io"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/utils/set"
    11  )
    12  
    13  type layeredStorage []Storage
    14  
    15  // NewLayeredStorage wraps multiple Storages such all of their metadata
    16  // can be listed and fetched. The later entries in the list have lower
    17  // precedence than the earlier ones. The first entry in the list is always
    18  // used for mutating operations.
    19  func NewLayeredStorage(s ...Storage) (Storage, error) {
    20  	if len(s) <= 1 {
    21  		return nil, errors.Errorf("expected multiple stores")
    22  	}
    23  	return layeredStorage(s), nil
    24  }
    25  
    26  // Add implements Storage.Add.
    27  //
    28  // This method operates on the first Storage passed to NewLayeredStorage.
    29  func (s layeredStorage) Add(r io.Reader, m Metadata) error {
    30  	return s[0].Add(r, m)
    31  }
    32  
    33  // Open implements Storage.Open.
    34  //
    35  // This method calls Open for each Storage passed to NewLayeredStorage in
    36  // the order given, and returns the first result where the error does not
    37  // satisfy errors.IsNotFound.
    38  func (s layeredStorage) Open(v string) (Metadata, io.ReadCloser, error) {
    39  	var m Metadata
    40  	var rc io.ReadCloser
    41  	var err error
    42  	for _, s := range s {
    43  		m, rc, err = s.Open(v)
    44  		if !errors.IsNotFound(err) {
    45  			break
    46  		}
    47  	}
    48  	return m, rc, err
    49  }
    50  
    51  // Metadata implements Storage.Metadata.
    52  //
    53  // This method calls Metadata for each Storage passed to NewLayeredStorage in
    54  // the order given, and returns the first result where the error does not
    55  // satisfy errors.IsNotFound.
    56  func (s layeredStorage) Metadata(v string) (Metadata, error) {
    57  	var m Metadata
    58  	var err error
    59  	for _, s := range s {
    60  		m, err = s.Metadata(v)
    61  		if !errors.IsNotFound(err) {
    62  			break
    63  		}
    64  	}
    65  	return m, err
    66  }
    67  
    68  // AllMetadata implements Storage.AllMetadata.
    69  //
    70  // This method calls AllMetadata for each Storage passed to NewLayeredStorage
    71  // in the order given, and accumulates the results. Any results from a Storage
    72  // earlier in the list will take precedence over any others with the same
    73  // version.
    74  func (s layeredStorage) AllMetadata() ([]Metadata, error) {
    75  	seen := set.NewStrings()
    76  	var all []Metadata
    77  	for _, s := range s {
    78  		sm, err := s.AllMetadata()
    79  		if err != nil {
    80  			return nil, err
    81  		}
    82  		for _, m := range sm {
    83  			if seen.Contains(m.Version) {
    84  				continue
    85  			}
    86  			all = append(all, m)
    87  			seen.Add(m.Version)
    88  		}
    89  	}
    90  	return all, nil
    91  }