github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/resource/charmstore/operations.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package charmstore
     5  
     6  import (
     7  	"io"
     8  
     9  	"github.com/juju/errors"
    10  	charmresource "gopkg.in/juju/charm.v6-unstable/resource"
    11  
    12  	"github.com/juju/juju/charmstore"
    13  	"github.com/juju/juju/resource"
    14  )
    15  
    16  // StoreResourceGetter provides the functionality for getting a resource
    17  // file from the charm store.
    18  type StoreResourceGetter interface {
    19  	// GetResource returns a reader for the resource's data. That data
    20  	// is streamed from the charm store. The charm's revision, if any,
    21  	// is ignored. If the identified resource is not in the charm store
    22  	// then errors.NotFound is returned.
    23  	//
    24  	// But if you write any code that assumes a NotFound error returned
    25  	// from this methid means that the resource was not found, you fail
    26  	// basic logic.
    27  	GetResource(charmstore.ResourceRequest) (charmstore.ResourceData, error)
    28  }
    29  
    30  // GetResourceArgs holds the arguments to GetResource().
    31  type GetResourceArgs struct {
    32  	// Client is the charm store client to use.
    33  	Client StoreResourceGetter
    34  
    35  	// EntityCache is the charm store cache to use. It is optional.
    36  	Cache EntityCache
    37  
    38  	// CharmID indicates the charm for which to get the resource.
    39  	CharmID charmstore.CharmID
    40  
    41  	// Name is the name of the resource.
    42  	Name string
    43  }
    44  
    45  func (args GetResourceArgs) validate() error {
    46  	if args.Client == nil {
    47  		return errors.Errorf("missing charm store client")
    48  	}
    49  	// FYI, args.Cache may be nil.
    50  	if args.CharmID.URL == nil {
    51  		return errors.Errorf("missing charm URL")
    52  	}
    53  	if args.Name == "" {
    54  		return errors.Errorf("missing resource name")
    55  	}
    56  	return nil
    57  }
    58  
    59  // GetResource returns a reader for the resource's data. That data is
    60  // streamed from the charm store.
    61  //
    62  // If a cache is set up then the resource is read from there. If the
    63  // resource is not in the cache at all then errors.NotFound is returned.
    64  // If only the resource's details are in the cache (but not the actual
    65  // file) then the file is read from the charm store. In that case the
    66  // cache is updated to contain the file too.
    67  func GetResource(args GetResourceArgs) (resource.Resource, io.ReadCloser, error) {
    68  	if err := args.validate(); err != nil {
    69  		return resource.Resource{}, nil, errors.Trace(err)
    70  	}
    71  
    72  	cache := cacheForOperations{
    73  		EntityCache: args.Cache,
    74  	}
    75  
    76  	res, reader, err := cache.get(args.Name)
    77  	if err != nil {
    78  		return resource.Resource{}, nil, errors.Trace(err)
    79  	}
    80  	if reader != nil {
    81  		// Both the info *and* the data were found in the cache.
    82  		return res, reader, nil
    83  	}
    84  
    85  	// Otherwise, just the info was found in the cache. So we read the
    86  	// data from the charm store through a new client and set the data
    87  	// for the resource in the cache.
    88  
    89  	if res.Origin != charmresource.OriginStore {
    90  		return resource.Resource{}, nil, errors.NotFoundf("resource %q", res.Name)
    91  	}
    92  
    93  	req := charmstore.ResourceRequest{
    94  		Charm:    args.CharmID.URL,
    95  		Channel:  args.CharmID.Channel,
    96  		Name:     res.Name,
    97  		Revision: res.Revision,
    98  	}
    99  	data, err := args.Client.GetResource(req)
   100  	if errors.IsNotFound(err) {
   101  		msg := "while getting resource from the charm store"
   102  		return resource.Resource{}, nil, errors.Annotate(err, msg)
   103  	}
   104  	if err != nil {
   105  		return resource.Resource{}, nil, errors.Trace(err)
   106  	}
   107  
   108  	res, reader, err = cache.set(data.Resource, data)
   109  	if err != nil {
   110  		return resource.Resource{}, nil, errors.Trace(err)
   111  	}
   112  
   113  	return res, reader, nil
   114  }