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 }