func (s *InternalClientSuite) TestListResources(c *gc.C) { fp, err := resource.GenerateFingerprint(strings.NewReader("data")) c.Assert(err, jc.ErrorIsNil) stable := params.Resource{ Name: "name", Type: "file", Path: "foo.zip", Description: "something", Origin: "store", Revision: 5, Fingerprint: fp.Bytes(), Size: 4, } dev := params.Resource{ Name: "name2", Type: "file", Path: "bar.zip", Description: "something", Origin: "store", Revision: 7, Fingerprint: fp.Bytes(), Size: 4, } s.lowLevel.ReturnListResourcesStable = map[string][]params.Resource{ "cs:quantal/foo-1": []params.Resource{stable}, } s.lowLevel.ReturnListResourcesDev = map[string][]params.Resource{ "cs:quantal/bar-1": []params.Resource{dev}, } client := Client{lowLevel: s.lowLevel} foo := charm.MustParseURL("cs:quantal/foo-1") bar := charm.MustParseURL("cs:quantal/bar-1") ret, err := client.ListResources([]CharmID{{ URL: foo, Channel: "stable", }, { URL: bar, Channel: "development", }}) c.Assert(err, jc.ErrorIsNil) stableOut, err := params.API2Resource(stable) c.Assert(err, jc.ErrorIsNil) devOut, err := params.API2Resource(dev) c.Assert(err, jc.ErrorIsNil) c.Assert(ret, gc.DeepEquals, [][]resource.Resource{ {stableOut}, {devOut}, }) s.lowLevel.stableStub.CheckCall(c, 0, "ListResources", params.StableChannel, []*charm.URL{foo}) s.lowLevel.devStub.CheckCall(c, 0, "ListResources", params.DevelopmentChannel, []*charm.URL{bar}) }
// GetResource returns the data (bytes) and metadata for a resource from the charmstore. func (c Client) GetResource(req ResourceRequest) (data ResourceData, err error) { if err := c.jar.Activate(req.Charm); err != nil { return ResourceData{}, errors.Trace(err) } defer c.jar.Deactivate() meta, err := c.csWrapper.ResourceMeta(req.Channel, req.Charm, req.Name, req.Revision) if err != nil { return ResourceData{}, errors.Trace(err) } data.Resource, err = csparams.API2Resource(meta) if err != nil { return ResourceData{}, errors.Trace(err) } resData, err := c.csWrapper.GetResource(req.Channel, req.Charm, req.Name, req.Revision) if err != nil { return ResourceData{}, errors.Trace(err) } defer func() { if err != nil { resData.Close() } }() data.ReadCloser = resData.ReadCloser fpHash := data.Resource.Fingerprint.String() if resData.Hash != fpHash { return ResourceData{}, errors.Errorf("fingerprint for data (%s) does not match fingerprint in metadata (%s)", resData.Hash, fpHash) } if resData.Size != data.Resource.Size { return ResourceData{}, errors.Errorf("size for data (%d) does not match size in metadata (%d)", resData.Size, data.Resource.Size) } return data, nil }
func (s *ClientSuite) TestResourceInfo(c *gc.C) { fp, err := resource.GenerateFingerprint(strings.NewReader("data")) c.Assert(err, jc.ErrorIsNil) apiRes := params.Resource{ Name: "name", Type: "file", Path: "foo.zip", Description: "something", Origin: "store", Revision: 5, Fingerprint: fp.Bytes(), Size: 4, } s.wrapper.ReturnResourceMeta = apiRes client, err := newCachingClient(s.cache, nil, s.wrapper.makeWrapper) c.Assert(err, jc.ErrorIsNil) req := ResourceRequest{ Charm: charm.MustParseURL("cs:mysql"), Channel: params.StableChannel, Name: "name", Revision: 5, } res, err := client.ResourceInfo(req) c.Assert(err, jc.ErrorIsNil) expected, err := params.API2Resource(apiRes) c.Assert(err, jc.ErrorIsNil) c.Check(res, gc.DeepEquals, expected) // call #0 is a call to makeWrapper s.wrapper.stub.CheckCall(c, 1, "ResourceMeta", params.StableChannel, req.Charm, req.Name, req.Revision) }
// GetResource returns the data (bytes) and metadata for a resource from the charmstore. func (c Client) GetResource(id CharmID, resourceName string, revision int) (res charmresource.Resource, rc io.ReadCloser, err error) { defer func() { if err != nil && rc != nil { rc.Close() } }() meta, err := c.lowLevel.ResourceInfo(id.Channel, id.URL, resourceName, revision) if err != nil { return charmresource.Resource{}, nil, errors.Trace(err) } res, err = csparams.API2Resource(meta) if err != nil { return charmresource.Resource{}, nil, errors.Trace(err) } data, err := c.lowLevel.GetResource(id.Channel, id.URL, resourceName, revision) if err != nil { return charmresource.Resource{}, nil, errors.Trace(err) } fpHash := res.Fingerprint.String() if data.Hash != fpHash { return charmresource.Resource{}, nil, errors.Errorf("fingerprint for data (%s) does not match fingerprint in metadata (%s)", data.Hash, fpHash) } if data.Size != res.Size { return charmresource.Resource{}, nil, errors.Errorf("size for data (%d) does not match size in metadata (%d)", data.Size, res.Size) } return res, data.ReadCloser, nil }
// ListResources implements BaseClient by calling csclient.Client's ListResources function. func (c Client) ListResources(charms []CharmID) ([][]charmresource.Resource, error) { requests := collate(charms) result := make([][]charmresource.Resource, len(charms)) for channel, request := range requests { // we have to make one bulk call per channel, unfortunately resmap, err := c.lowLevel.ListResources(channel, request.ids) if err != nil { return nil, errors.Trace(err) } for i, id := range request.ids { resources, ok := resmap[id.String()] if !ok { continue } list := make([]charmresource.Resource, len(resources)) for j, res := range resources { resource, err := csparams.API2Resource(res) if err != nil { return nil, errors.Annotatef(err, "got bad data from server for resource %q", res.Name) } list[j] = resource } idx := request.indices[i] result[idx] = list } } return result, nil }
func api2resources(res []csparams.Resource) ([]charmresource.Resource, error) { result := make([]charmresource.Resource, len(res)) for i, r := range res { var err error result[i], err = csparams.API2Resource(r) if err != nil { return nil, errors.Trace(err) } } return result, nil }
// ResourceInfo returns the metadata info for the given resource from the charmstore. func (c Client) ResourceInfo(id CharmID, resourceName string, revision int) (charmresource.Resource, error) { meta, err := c.lowLevel.ResourceInfo(id.Channel, id.URL, resourceName, revision) if err != nil { return charmresource.Resource{}, errors.Trace(err) } res, err := csparams.API2Resource(meta) if err != nil { return charmresource.Resource{}, errors.Trace(err) } return res, nil }
// ResourceInfo returns the metadata for the given resource from the charmstore. func (c Client) ResourceInfo(req ResourceRequest) (charmresource.Resource, error) { if err := c.jar.Activate(req.Charm); err != nil { return charmresource.Resource{}, errors.Trace(err) } defer c.jar.Deactivate() meta, err := c.csWrapper.ResourceMeta(req.Channel, req.Charm, req.Name, req.Revision) if err != nil { return charmresource.Resource{}, errors.Trace(err) } res, err := csparams.API2Resource(meta) if err != nil { return charmresource.Resource{}, errors.Trace(err) } return res, nil }
func (s *ClientSuite) TestGetResource(c *gc.C) { fp, err := resource.GenerateFingerprint(strings.NewReader("data")) c.Assert(err, jc.ErrorIsNil) rc := ioutil.NopCloser(strings.NewReader("data")) s.wrapper.ReturnGetResource = csclient.ResourceData{ ReadCloser: rc, Hash: fp.String(), Size: 4, } apiRes := params.Resource{ Name: "name", Type: "file", Path: "foo.zip", Description: "something", Origin: "store", Revision: 5, Fingerprint: fp.Bytes(), Size: 4, } s.wrapper.ReturnResourceMeta = apiRes client, err := newCachingClient(s.cache, nil, s.wrapper.makeWrapper) c.Assert(err, jc.ErrorIsNil) req := ResourceRequest{ Charm: charm.MustParseURL("cs:mysql"), Channel: params.DevelopmentChannel, Name: "name", Revision: 5, } data, err := client.GetResource(req) c.Assert(err, jc.ErrorIsNil) expected, err := params.API2Resource(apiRes) c.Assert(err, jc.ErrorIsNil) c.Check(data.Resource, gc.DeepEquals, expected) c.Check(data.ReadCloser, gc.DeepEquals, rc) // call #0 is a call to makeWrapper s.wrapper.stub.CheckCall(c, 1, "ResourceMeta", params.DevelopmentChannel, req.Charm, req.Name, req.Revision) s.wrapper.stub.CheckCall(c, 2, "GetResource", params.DevelopmentChannel, req.Charm, req.Name, req.Revision) }
func (s *LatestCharmInfoSuite) TestSuccess(c *gc.C) { spam := charm.MustParseURL("cs:quantal/spam-17") eggs := charm.MustParseURL("cs:quantal/eggs-2") ham := charm.MustParseURL("cs:quantal/ham-1") charms := []CharmID{ {spam, "stable"}, {eggs, "stable"}, {ham, "stable"}, } notFound := errors.New("not found") s.lowLevel.ReturnLatestStable = []charmrepo.CharmRevision{{ Revision: 17, }, { Revision: 3, }, { Err: notFound, }} fakeRes := fakeParamsResource("foo", nil) s.lowLevel.ReturnListResourcesStable = map[string][]params.Resource{ "cs:quantal/spam-17": []params.Resource{fakeRes}, } uuid := "foobar" client := Client{lowLevel: s.lowLevel} results, err := LatestCharmInfo(client, charms, uuid) c.Assert(err, jc.ErrorIsNil) expectedIds := []*charm.URL{spam, eggs, ham} s.lowLevel.stableStub.CheckCallNames(c, "Latest", "ListResources") s.lowLevel.stableStub.CheckCall(c, 0, "Latest", params.StableChannel, expectedIds, map[string]string{"environment_uuid": uuid}) s.lowLevel.stableStub.CheckCall(c, 1, "ListResources", params.StableChannel, expectedIds) expectedRes, err := params.API2Resource(fakeRes) c.Assert(err, jc.ErrorIsNil) timestamp := results[0].Timestamp results[2].Error = errors.Cause(results[2].Error) c.Check(results, jc.DeepEquals, []CharmInfoResult{{ CharmInfo: CharmInfo{ OriginalURL: charm.MustParseURL("cs:quantal/spam-17"), Timestamp: timestamp, LatestRevision: 17, LatestResources: []charmresource.Resource{ expectedRes, }, }, }, { CharmInfo: CharmInfo{ OriginalURL: charm.MustParseURL("cs:quantal/eggs-2"), Timestamp: timestamp, LatestRevision: 3, }, }, { CharmInfo: CharmInfo{ OriginalURL: charm.MustParseURL("cs:quantal/ham-1"), Timestamp: timestamp, }, Error: notFound, }}) }
func (s *ClientSuite) TestListResources(c *gc.C) { fp, err := resource.GenerateFingerprint(strings.NewReader("data")) c.Assert(err, jc.ErrorIsNil) stable := params.Resource{ Name: "name", Type: "file", Path: "foo.zip", Description: "something", Origin: "store", Revision: 5, Fingerprint: fp.Bytes(), Size: 4, } dev := params.Resource{ Name: "name2", Type: "file", Path: "bar.zip", Description: "something", Origin: "store", Revision: 7, Fingerprint: fp.Bytes(), Size: 4, } dev2 := params.Resource{ Name: "name3", Type: "file", Path: "bar.zip", Description: "something", Origin: "store", Revision: 8, Fingerprint: fp.Bytes(), Size: 4, } s.wrapper.ReturnListResourcesStable = []resourceResult{oneResourceResult(stable), resourceResult{err: params.ErrNotFound}} s.wrapper.ReturnListResourcesDev = []resourceResult{oneResourceResult(dev), oneResourceResult(dev2)} client, err := newCachingClient(s.cache, nil, s.wrapper.makeWrapper) c.Assert(err, jc.ErrorIsNil) foo := charm.MustParseURL("cs:quantal/foo-1") bar := charm.MustParseURL("cs:quantal/bar-1") baz := charm.MustParseURL("cs:quantal/baz-1") ret, err := client.ListResources([]CharmID{{ URL: foo, Channel: params.StableChannel, }, { URL: bar, Channel: params.DevelopmentChannel, }, { URL: baz, Channel: params.DevelopmentChannel, }}) c.Assert(err, jc.ErrorIsNil) stableOut, err := params.API2Resource(stable) c.Assert(err, jc.ErrorIsNil) devOut, err := params.API2Resource(dev) c.Assert(err, jc.ErrorIsNil) dev2Out, err := params.API2Resource(dev2) c.Assert(err, jc.ErrorIsNil) c.Assert(ret, gc.DeepEquals, [][]resource.Resource{ {stableOut}, {devOut}, {dev2Out}, }) s.wrapper.stableStub.CheckCall(c, 0, "ListResources", params.StableChannel, foo) s.wrapper.devStub.CheckCall(c, 0, "ListResources", params.DevelopmentChannel, bar) s.wrapper.devStub.CheckCall(c, 1, "ListResources", params.DevelopmentChannel, baz) }
func (s *LatestCharmInfoSuite) TestSuccess(c *gc.C) { spam := charm.MustParseURL("cs:quantal/spam-17") eggs := charm.MustParseURL("cs:quantal/eggs-2") ham := charm.MustParseURL("cs:quantal/ham-1") charms := []CharmID{ {URL: spam, Channel: "stable"}, {URL: eggs, Channel: "stable"}, {URL: ham, Channel: "stable"}, } notFound := errors.New("not found") s.lowLevel.ReturnLatestStable = [][]params.CharmRevision{{{ Revision: 17, }}, {{ Revision: 3, }}, {{ Err: notFound, }}} fakeRes := fakeParamsResource("foo", nil) s.lowLevel.ReturnListResourcesStable = []resourceResult{ oneResourceResult(fakeRes), resourceResult{err: params.ErrNotFound}, resourceResult{err: params.ErrUnauthorized}, } client, err := newCachingClient(s.cache, nil, s.lowLevel.makeWrapper) c.Assert(err, jc.ErrorIsNil) metadata := map[string]string{ "environment_uuid": "foouuid", "cloud": "foocloud", "cloud_region": "fooregion", "provider": "fooprovider", } results, err := LatestCharmInfo(client, charms, metadata) c.Assert(err, jc.ErrorIsNil) header := []string{"environment_uuid=foouuid", "cloud=foocloud", "cloud_region=fooregion", "provider=fooprovider", "controller_version=" + version.Current.String()} s.lowLevel.stableStub.CheckCall(c, 0, "Latest", params.StableChannel, []*charm.URL{spam}, map[string][]string{"Juju-Metadata": header}) s.lowLevel.stableStub.CheckCall(c, 1, "Latest", params.StableChannel, []*charm.URL{eggs}, map[string][]string{"Juju-Metadata": header}) s.lowLevel.stableStub.CheckCall(c, 2, "Latest", params.StableChannel, []*charm.URL{ham}, map[string][]string{"Juju-Metadata": header}) s.lowLevel.stableStub.CheckCall(c, 3, "ListResources", params.StableChannel, spam) s.lowLevel.stableStub.CheckCall(c, 4, "ListResources", params.StableChannel, eggs) s.lowLevel.stableStub.CheckCall(c, 5, "ListResources", params.StableChannel, ham) expectedRes, err := params.API2Resource(fakeRes) c.Assert(err, jc.ErrorIsNil) timestamp := results[0].Timestamp results[2].Error = errors.Cause(results[2].Error) expected := []CharmInfoResult{{ CharmInfo: CharmInfo{ OriginalURL: charm.MustParseURL("cs:quantal/spam-17"), Timestamp: timestamp, LatestRevision: 17, LatestResources: []charmresource.Resource{ expectedRes, }, }, }, { CharmInfo: CharmInfo{ OriginalURL: charm.MustParseURL("cs:quantal/eggs-2"), Timestamp: timestamp, LatestRevision: 3, }, }, { CharmInfo: CharmInfo{ OriginalURL: charm.MustParseURL("cs:quantal/ham-1"), Timestamp: timestamp, }, Error: notFound, }} sort.Sort(byURL(results)) sort.Sort(byURL(expected)) c.Check(results, jc.DeepEquals, expected) }