17
16
gc "gopkg.in/check.v1"
18
17
"gopkg.in/juju/charm.v6-unstable"
19
18
"gopkg.in/juju/charmrepo.v2-unstable/csclient/params"
20
"gopkg.in/macaroon-bakery.v1/bakery/checkers"
21
"gopkg.in/macaroon-bakery.v1/httpbakery"
22
"gopkg.in/macaroon.v1"
23
"gopkg.in/mgo.v2/bson"
25
20
"gopkg.in/juju/charmstore.v5-unstable/internal/charmstore"
26
"gopkg.in/juju/charmstore.v5-unstable/internal/mongodoc"
27
21
"gopkg.in/juju/charmstore.v5-unstable/internal/router"
28
22
"gopkg.in/juju/charmstore.v5-unstable/internal/storetesting"
29
23
"gopkg.in/juju/charmstore.v5-unstable/internal/v5"
55
49
func (s *SearchSuite) SetUpTest(c *gc.C) {
56
50
s.commonSuite.SetUpTest(c)
57
51
s.addCharmsToStore(c)
58
// hide the riak charm
59
err := s.store.DB.BaseEntities().UpdateId(
60
charm.MustParseURL("cs:~charmers/riak"),
61
bson.D{{"$set", map[string]mongodoc.ACL{
63
Read: []string{"charmers", "test-user"},
52
err := s.store.SetPerms(charm.MustParseURL("cs:~charmers/riak"), "stable.read", "charmers", "test-user")
67
53
c.Assert(err, gc.IsNil)
68
54
err = s.store.UpdateSearch(newResolvedURL("~charmers/trusty/riak-0", 0))
69
55
c.Assert(err, gc.IsNil)
74
60
func (s *SearchSuite) addCharmsToStore(c *gc.C) {
75
61
for name, id := range exportTestCharms {
76
err := s.store.AddCharmWithArchive(id, getCharm(name))
77
c.Assert(err, gc.IsNil)
78
err = s.store.SetPerms(&id.URL, "read", params.Everyone, id.URL.User)
79
c.Assert(err, gc.IsNil)
80
err = s.store.UpdateSearch(id)
81
c.Assert(err, gc.IsNil)
62
s.addPublicCharm(c, getSearchCharm(name), id)
83
64
for name, id := range exportTestBundles {
84
err := s.store.AddBundleWithArchive(id, getBundle(name))
85
c.Assert(err, gc.IsNil)
86
err = s.store.SetPerms(&id.URL, "read", params.Everyone, id.URL.User)
87
c.Assert(err, gc.IsNil)
88
err = s.store.UpdateSearch(id)
89
c.Assert(err, gc.IsNil)
65
s.addPublicBundle(c, getSearchBundle(name), id, false)
93
func getCharm(name string) *charm.CharmDir {
69
func getSearchCharm(name string) *storetesting.Charm {
94
70
ca := storetesting.Charms.CharmDir(name)
95
ca.Meta().Categories = append(strings.Split(name, "-"), "bar")
72
meta.Categories = append(strings.Split(name, "-"), "bar")
73
return storetesting.NewCharm(meta)
99
func getBundle(name string) *charm.BundleDir {
76
func getSearchBundle(name string) *storetesting.Bundle {
100
77
ba := storetesting.Charms.BundleDir(name)
101
ba.Data().Tags = append(strings.Split(name, "-"), "baz")
79
data.Tags = append(strings.Split(name, "-"), "baz")
80
return storetesting.NewBundle(data)
105
83
func (s *SearchSuite) TestParseSearchParams(c *gc.C) {
471
449
about: "archive-size",
472
450
query: "name=mysql&include=archive-size",
473
451
meta: map[string]interface{}{
474
"archive-size": params.ArchiveSizeResponse{438},
452
"archive-size": params.ArchiveSizeResponse{getSearchCharm("mysql").Size()},
477
455
about: "bundle-metadata",
478
456
query: "name=wordpress-simple&type=bundle&include=bundle-metadata",
479
457
meta: map[string]interface{}{
480
"bundle-metadata": getBundle("wordpress-simple").Data(),
458
"bundle-metadata": getSearchBundle("wordpress-simple").Data(),
483
461
about: "bundle-machine-count",
495
473
about: "charm-actions",
496
474
query: "name=wordpress&type=charm&include=charm-actions",
497
475
meta: map[string]interface{}{
498
"charm-actions": getCharm("wordpress").Actions(),
476
"charm-actions": getSearchCharm("wordpress").Actions(),
501
479
about: "charm-config",
502
480
query: "name=wordpress&type=charm&include=charm-config",
503
481
meta: map[string]interface{}{
504
"charm-config": getCharm("wordpress").Config(),
482
"charm-config": getSearchCharm("wordpress").Config(),
507
485
about: "charm-related",
508
486
query: "name=wordpress&type=charm&include=charm-related",
509
487
meta: map[string]interface{}{
510
488
"charm-related": params.RelatedResponse{
511
Provides: map[string][]params.MetaAnyResponse{
489
Provides: map[string][]params.EntityResult{
514
492
Id: exportTestCharms["mysql"].PreferredURL(),
527
505
query: "name=wordpress&type=charm&include=charm-related&include=charm-config",
528
506
meta: map[string]interface{}{
529
507
"charm-related": params.RelatedResponse{
530
Provides: map[string][]params.MetaAnyResponse{
508
Provides: map[string][]params.EntityResult{
533
511
Id: exportTestCharms["mysql"].PreferredURL(),
543
"charm-config": getCharm("wordpress").Config(),
521
"charm-config": getSearchCharm("wordpress").Config(),
546
524
for i, test := range tests {
591
569
// cs:riak will not be found because it is not visible to "everyone".
592
570
c.Assert(resp.Results, gc.HasLen, len(exportTestCharms)-1)
594
// Now remove one of the blobs. The search should still
572
// Now remove one of the blobs. The list should still
595
573
// work, but only return a single result.
596
blobName, _, err := s.store.BlobNameAndHash(newResolvedURL("~charmers/precise/wordpress-23", 23))
574
entity, err := s.store.FindEntity(newResolvedURL("~charmers/precise/wordpress-23", 23), nil)
597
576
c.Assert(err, gc.IsNil)
598
err = s.store.BlobStore.Remove(blobName)
577
err = s.store.BlobStore.Remove(entity.BlobName)
599
578
c.Assert(err, gc.IsNil)
601
580
// Now search again - we should get one result less
723
702
for n, cnt := range charmDownloads {
724
703
url := newResolvedURL("cs:~downloads-test/trusty/x-1", -1)
726
err := s.store.AddCharmWithArchive(url, getCharm(n))
727
c.Assert(err, gc.IsNil)
728
err = s.store.SetPerms(&url.URL, "read", params.Everyone, url.URL.User)
729
c.Assert(err, gc.IsNil)
730
err = s.store.UpdateSearch(url)
731
c.Assert(err, gc.IsNil)
705
s.addPublicCharm(c, getSearchCharm(n), url)
732
706
for i := 0; i < cnt; i++ {
733
707
err := s.store.IncrementDownloadCounts(url)
734
708
c.Assert(err, gc.IsNil)
755
729
doc, err := s.store.ES.GetSearchDocument(charm.MustParseURL("~openstack-charmers/trusty/mysql-7"))
756
730
c.Assert(err, gc.IsNil)
757
731
c.Assert(doc.TotalDownloads, gc.Equals, int64(0))
758
s.assertPut(c, "~openstack-charmers/trusty/mysql-7/meta/extra-info/"+params.LegacyDownloadStats, 57)
732
s.assertPutAsAdmin(c, "~openstack-charmers/trusty/mysql-7/meta/extra-info/"+params.LegacyDownloadStats, 57)
759
733
doc, err = s.store.ES.GetSearchDocument(charm.MustParseURL("~openstack-charmers/trusty/mysql-7"))
760
734
c.Assert(err, gc.IsNil)
761
735
c.Assert(doc.TotalDownloads, gc.Equals, int64(57))
764
func (s *SearchSuite) assertPut(c *gc.C, url string, val interface{}) {
765
body, err := json.Marshal(val)
766
c.Assert(err, gc.IsNil)
767
rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
772
"Content-Type": {"application/json"},
774
Username: testUsername,
775
Password: testPassword,
776
Body: bytes.NewReader(body),
778
c.Assert(rec.Code, gc.Equals, http.StatusOK, gc.Commentf("headers: %v, body: %s", rec.HeaderMap, rec.Body.String()))
779
c.Assert(rec.Body.String(), gc.HasLen, 0)
782
738
func (s *SearchSuite) TestSearchWithAdminCredentials(c *gc.C) {
783
739
rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
803
759
func (s *SearchSuite) TestSearchWithUserMacaroon(c *gc.C) {
804
m, err := s.store.Bakery.NewMacaroon("", nil, []checkers.Caveat{
805
checkers.DeclaredCaveat("username", "test-user"),
807
c.Assert(err, gc.IsNil)
808
macaroonCookie, err := httpbakery.NewCookie(macaroon.Slice{m})
809
c.Assert(err, gc.IsNil)
810
760
rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
812
762
URL: storeURL("search"),
813
Cookies: []*http.Cookie{macaroonCookie},
763
Do: s.bakeryDoAsUser(c, "test-user"),
815
765
c.Assert(rec.Code, gc.Equals, http.StatusOK)
816
766
expected := []*router.ResolvedURL{
821
771
exportTestBundles["wordpress-simple"],
823
773
var sr params.SearchResponse
824
err = json.Unmarshal(rec.Body.Bytes(), &sr)
774
err := json.Unmarshal(rec.Body.Bytes(), &sr)
825
775
c.Assert(err, gc.IsNil)
826
776
assertResultSet(c, sr, expected)
829
779
func (s *SearchSuite) TestSearchWithUserInGroups(c *gc.C) {
830
m, err := s.store.Bakery.NewMacaroon("", nil, []checkers.Caveat{
831
checkers.DeclaredCaveat(v5.UsernameAttr, "bob"),
833
c.Assert(err, gc.IsNil)
834
macaroonCookie, err := httpbakery.NewCookie(macaroon.Slice{m})
835
c.Assert(err, gc.IsNil)
836
780
s.idM.groups = map[string][]string{
837
781
"bob": {"test-user", "test-user2"},
839
783
rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
841
785
URL: storeURL("search"),
842
Cookies: []*http.Cookie{macaroonCookie},
786
Do: s.bakeryDoAsUser(c, "bob"),
844
788
c.Assert(rec.Code, gc.Equals, http.StatusOK)
845
789
expected := []*router.ResolvedURL{
850
794
exportTestBundles["wordpress-simple"],
852
796
var sr params.SearchResponse
853
err = json.Unmarshal(rec.Body.Bytes(), &sr)
797
err := json.Unmarshal(rec.Body.Bytes(), &sr)
854
798
c.Assert(err, gc.IsNil)
855
799
assertResultSet(c, sr, expected)
858
802
func (s *SearchSuite) TestSearchWithBadAdminCredentialsAndACookie(c *gc.C) {
859
m, err := s.store.Bakery.NewMacaroon("", nil, []checkers.Caveat{
860
checkers.DeclaredCaveat("username", "test-user"),
862
c.Assert(err, gc.IsNil)
863
macaroonCookie, err := httpbakery.NewCookie(macaroon.Slice{m})
864
c.Assert(err, gc.IsNil)
865
803
rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
805
Do: s.bakeryDoAsUser(c, "test-user"),
867
806
URL: storeURL("search"),
868
Cookies: []*http.Cookie{macaroonCookie},
869
807
Username: testUsername,
870
808
Password: "bad-password",
877
815
exportTestBundles["wordpress-simple"],
879
817
var sr params.SearchResponse
880
err = json.Unmarshal(rec.Body.Bytes(), &sr)
818
err := json.Unmarshal(rec.Body.Bytes(), &sr)
881
819
c.Assert(err, gc.IsNil)
882
820
assertResultSet(c, sr, expected)