44
44
var _ = gc.Suite(&ArchiveSuite{})
46
func (s *ArchiveSuite) SetUpSuite(c *gc.C) {
47
s.enableIdentity = true
48
s.commonSuite.SetUpSuite(c)
46
51
func (s *ArchiveSuite) TestGet(c *gc.C) {
47
52
id := newResolvedURL("cs:~charmers/precise/wordpress-0", -1)
48
wordpress := s.assertUploadCharm(c, "POST", id, "wordpress")
49
err := s.store.SetPerms(&id.URL, "read", params.Everyone, id.URL.User)
50
c.Assert(err, gc.IsNil)
52
archiveBytes, err := ioutil.ReadFile(wordpress.Path)
53
c.Assert(err, gc.IsNil)
55
archiveUrl := storeURL("~charmers/precise/wordpress-0/archive")
56
rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
60
c.Assert(rec.Code, gc.Equals, http.StatusOK)
61
c.Assert(rec.Body.Bytes(), gc.DeepEquals, archiveBytes)
62
c.Assert(rec.Header().Get(params.ContentHashHeader), gc.Equals, hashOfBytes(archiveBytes))
53
ch := storetesting.NewCharm(nil)
54
s.addPublicCharm(c, ch, id)
56
rec := s.assertArchiveDownload(
58
"~charmers/precise/wordpress-0",
63
62
c.Assert(rec.Header().Get(params.EntityIdHeader), gc.Equals, "cs:~charmers/precise/wordpress-0")
64
63
assertCacheControl(c, rec.Header(), true)
68
67
// as net/http is well-tested.
69
68
rec = httptesting.DoRequest(c, httptesting.DoRequestParams{
70
URL: storeURL("~charmers/precise/wordpress-0/archive"),
72
71
Header: http.Header{"Range": {"bytes=10-100"}},
74
73
c.Assert(rec.Code, gc.Equals, http.StatusPartialContent, gc.Commentf("body: %q", rec.Body.Bytes()))
75
74
c.Assert(rec.Body.Bytes(), gc.HasLen, 100-10+1)
76
c.Assert(rec.Body.Bytes(), gc.DeepEquals, archiveBytes[10:101])
77
c.Assert(rec.Header().Get(params.ContentHashHeader), gc.Equals, hashOfBytes(archiveBytes))
75
c.Assert(rec.Body.Bytes(), gc.DeepEquals, ch.Bytes()[10:101])
76
c.Assert(rec.Header().Get(params.ContentHashHeader), gc.Equals, hashOfBytes(ch.Bytes()))
78
77
c.Assert(rec.Header().Get(params.EntityIdHeader), gc.Equals, "cs:~charmers/precise/wordpress-0")
79
78
assertCacheControl(c, rec.Header(), true)
81
// The development version of the entity can also be retrieved.
82
err = s.store.SetPerms(id.URL.WithChannel(charm.DevelopmentChannel), "read", params.Everyone, id.URL.User)
83
c.Assert(err, gc.IsNil)
84
rec = httptesting.DoRequest(c, httptesting.DoRequestParams{
86
URL: storeURL("~charmers/development/precise/wordpress-0/archive"),
88
c.Assert(rec.Code, gc.Equals, http.StatusOK)
89
c.Assert(rec.Body.Bytes(), gc.DeepEquals, archiveBytes)
90
c.Assert(rec.Header().Get(params.ContentHashHeader), gc.Equals, hashOfBytes(archiveBytes))
91
c.Assert(rec.Header().Get(params.EntityIdHeader), gc.Equals, "cs:~charmers/development/precise/wordpress-0")
94
func (s *ArchiveSuite) TestGetDevelopment(c *gc.C) {
95
id := newResolvedURL("cs:~charmers/development/trusty/wordpress-0", -1)
96
wordpress := s.assertUploadCharm(c, "POST", id, "wordpress")
97
url := id.PreferredURL()
98
err := s.store.SetPerms(url, "read", params.Everyone, id.URL.User)
99
c.Assert(err, gc.IsNil)
101
archiveBytes, err := ioutil.ReadFile(wordpress.Path)
102
c.Assert(err, gc.IsNil)
104
rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
106
URL: storeURL("~charmers/development/trusty/wordpress-0/archive"),
108
c.Assert(rec.Code, gc.Equals, http.StatusOK)
109
c.Assert(rec.Body.Bytes(), gc.DeepEquals, archiveBytes)
110
c.Assert(rec.Header().Get(params.ContentHashHeader), gc.Equals, hashOfBytes(archiveBytes))
111
c.Assert(rec.Header().Get(params.EntityIdHeader), gc.Equals, "cs:~charmers/development/trusty/wordpress-0")
113
// It is not possible to use the published URL to retrieve the archive,
114
err = s.store.SetPerms(url.WithChannel(""), "read", params.Everyone, id.URL.User)
115
c.Assert(err, gc.IsNil)
116
httptesting.AssertJSONCall(c, httptesting.JSONCallParams{
118
URL: storeURL("~charmers/trusty/wordpress-0/archive"),
119
ExpectStatus: http.StatusNotFound,
120
ExpectBody: params.Error{
121
Code: params.ErrNotFound,
122
Message: `no matching charm or bundle for "cs:~charmers/trusty/wordpress-0"`,
127
81
func (s *ArchiveSuite) TestGetWithPartialId(c *gc.C) {
128
id := newResolvedURL("cs:~charmers/utopic/wordpress-42", -1)
129
err := s.store.AddCharmWithArchive(
131
storetesting.Charms.CharmArchive(c.MkDir(), "wordpress"))
132
c.Assert(err, gc.IsNil)
133
err = s.store.SetPerms(&id.URL, "read", params.Everyone, id.URL.User)
134
c.Assert(err, gc.IsNil)
135
rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
137
URL: storeURL("~charmers/wordpress/archive"),
139
c.Assert(rec.Code, gc.Equals, http.StatusOK)
82
id := newResolvedURL("cs:~charmers/precise/wordpress-0", -1)
83
ch := storetesting.NewCharm(nil)
84
s.addPublicCharm(c, ch, id)
86
rec := s.assertArchiveDownload(
88
"~charmers/wordpress",
140
92
// The complete entity id can be retrieved from the response header.
141
93
c.Assert(rec.Header().Get(params.EntityIdHeader), gc.Equals, id.URL.String())
144
96
func (s *ArchiveSuite) TestGetPromulgatedWithPartialId(c *gc.C) {
145
97
id := newResolvedURL("cs:~charmers/utopic/wordpress-42", 42)
146
err := s.store.AddCharmWithArchive(
148
storetesting.Charms.CharmArchive(c.MkDir(), "wordpress"))
149
c.Assert(err, gc.IsNil)
150
err = s.store.SetPerms(&id.URL, "read", params.Everyone, id.URL.User)
151
c.Assert(err, gc.IsNil)
152
rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
154
URL: storeURL("wordpress/archive"),
156
c.Assert(rec.Code, gc.Equals, http.StatusOK)
157
// The complete entity id can be retrieved from the response header.
98
ch := storetesting.NewCharm(nil)
99
s.addPublicCharm(c, ch, id)
101
rec := s.assertArchiveDownload(
158
107
c.Assert(rec.Header().Get(params.EntityIdHeader), gc.Equals, id.PromulgatedURL().String())
111
func (s *ArchiveSuite) TestGetElidesSeriesFromMultiSeriesCharmMetadata(c *gc.C) {
112
_, ch := s.addPublicCharmFromRepo(c, "multi-series", newResolvedURL("cs:~charmers/multi-series-0", -1))
113
rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
115
URL: storeURL("~charmers/multi-series/archive"),
117
c.Assert(rec.Code, gc.Equals, http.StatusOK)
119
gotCh, err := charm.ReadCharmArchiveBytes(rec.Body.Bytes())
120
c.Assert(err, gc.IsNil)
121
c.Assert(gotCh.Meta().Series, gc.HasLen, 0)
123
// Check that the metadata is elided from the metadata file when retrieved
126
rec = httptesting.DoRequest(c, httptesting.DoRequestParams{
128
URL: storeURL("~charmers/multi-series/archive/metadata.yaml"),
130
c.Assert(rec.Code, gc.Equals, http.StatusOK)
132
gotMeta, err := charm.ReadMeta(bytes.NewReader(rec.Body.Bytes()))
133
c.Assert(err, gc.IsNil)
134
c.Assert(gotMeta.Series, gc.HasLen, 0)
139
c.Assert(gotMeta, jc.DeepEquals, chMeta)
161
142
func (s *ArchiveSuite) TestGetCounters(c *gc.C) {
162
143
if !storetesting.MongoJSEnabled() {
163
144
c.Skip("MongoDB JavaScript not available")
213
191
var archivePostErrorsTests = []struct {
216
194
noContentLength bool
196
entity charmstore.ArchiverTo
218
198
expectMessage string
219
199
expectCode params.ErrorCode
221
201
about: "revision specified",
222
path: "~charmers/precise/wordpress-23/archive",
202
url: "~charmers/precise/wordpress-23",
223
203
expectStatus: http.StatusBadRequest,
224
204
expectMessage: "revision specified, but should not be specified",
225
205
expectCode: params.ErrBadRequest,
227
207
about: "no hash given",
228
path: "~charmers/precise/wordpress/archive",
208
url: "~charmers/precise/wordpress",
229
210
expectStatus: http.StatusBadRequest,
230
211
expectMessage: "hash parameter not specified",
231
212
expectCode: params.ErrBadRequest,
233
214
about: "no content length",
234
path: "~charmers/precise/wordpress/archive?hash=1234563",
215
url: "~charmers/precise/wordpress",
235
216
noContentLength: true,
236
217
expectStatus: http.StatusBadRequest,
237
218
expectMessage: "Content-Length not specified",
238
219
expectCode: params.ErrBadRequest,
240
221
about: "invalid channel",
241
path: "~charmers/bad-wolf/trusty/wordpress/archive",
222
url: "~charmers/bad-wolf/trusty/wordpress",
242
223
expectStatus: http.StatusNotFound,
243
224
expectMessage: "not found",
244
225
expectCode: params.ErrNotFound,
228
url: "~charmers/juju-gui",
229
expectStatus: http.StatusForbidden,
230
expectMessage: "series not specified in url or charm metadata",
231
expectCode: params.ErrEntityIdNotAllowed,
233
about: "url series not in metadata",
234
url: "~charmers/precise/juju-gui",
235
entity: storetesting.NewCharm(&charm.Meta{
236
Series: []string{"trusty"},
238
expectStatus: http.StatusForbidden,
239
expectMessage: `"precise" series not listed in charm metadata`,
240
expectCode: params.ErrEntityIdNotAllowed,
242
about: "bad combination of series",
243
url: "~charmers/juju-gui",
244
entity: storetesting.NewCharm(&charm.Meta{
245
Series: []string{"precise", "win10"},
247
expectStatus: http.StatusBadRequest,
248
expectMessage: `cannot mix series from ubuntu and windows in single charm`,
249
expectCode: params.ErrInvalidEntity,
251
about: "unknown series",
252
url: "~charmers/juju-gui",
253
entity: storetesting.NewCharm(&charm.Meta{
254
Series: []string{"precise", "nosuchseries"},
256
expectStatus: http.StatusBadRequest,
257
expectMessage: `unrecognized series "nosuchseries" in metadata`,
258
expectCode: params.ErrInvalidEntity,
247
261
func (s *ArchiveSuite) TestPostErrors(c *gc.C) {
390
412
func (s *ArchiveSuite) TestPostCharm(c *gc.C) {
413
s.discharge = dischargeForUser("charmers")
391
415
// A charm that did not exist before should get revision 0.
392
416
s.assertUploadCharm(c, "POST", newResolvedURL("~charmers/precise/wordpress-0", -1), "wordpress")
394
418
// Subsequent charm uploads should increment the revision by 1.
395
419
s.assertUploadCharm(c, "POST", newResolvedURL("~charmers/precise/wordpress-1", -1), "mysql")
397
// Subsequent development charm uploads should increment the revision by 1.
398
s.assertUploadCharm(c, "POST", newResolvedURL("~charmers/development/precise/wordpress-2", -1), "wordpress")
400
// Retrieving the published version returns the last non-development charm.
401
err := s.store.SetPerms(charm.MustParseURL("~charmers/wordpress"), "read", params.Everyone)
402
c.Assert(err, gc.IsNil)
421
// Retrieving the unpublished version returns the latest charm.
403
422
rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
405
URL: storeURL("~charmers/wordpress/archive"),
424
URL: storeURL("~charmers/wordpress/archive?channel=unpublished"),
407
427
c.Assert(rec.Code, gc.Equals, http.StatusOK)
408
428
c.Assert(rec.Header().Get(params.EntityIdHeader), gc.Equals, "cs:~charmers/precise/wordpress-1")
411
431
func (s *ArchiveSuite) TestPostCurrentVersion(c *gc.C) {
412
s.assertUploadCharm(c, "POST", newResolvedURL("~charmers/development/precise/wordpress-0", -1), "wordpress")
432
s.assertUploadCharm(c, "POST", newResolvedURL("~charmers/precise/wordpress-0", -1), "wordpress")
414
434
// Subsequent charm uploads should not increment the revision by 1.
415
s.assertUploadCharm(c, "POST", newResolvedURL("~charmers/development/precise/wordpress-0", -1), "wordpress")
418
func (s *ArchiveSuite) TestPostDevelopmentPromulgated(c *gc.C) {
419
s.assertUploadCharm(c, "PUT", newResolvedURL("~charmers/development/trusty/wordpress-0", 0), "wordpress")
420
s.assertUploadCharm(c, "POST", newResolvedURL("~charmers/development/trusty/wordpress-1", 1), "mysql")
421
s.assertUploadCharm(c, "POST", newResolvedURL("~charmers/development/trusty/wordpress-1", 1), "mysql")
423
// The promulgated charm can be accessed via its development URL.
424
err := s.store.SetPerms(charm.MustParseURL("~charmers/development/wordpress"), "read", params.Everyone)
425
c.Assert(err, gc.IsNil)
426
rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
428
URL: storeURL("development/wordpress/archive"),
430
c.Assert(rec.Code, gc.Equals, http.StatusOK)
431
c.Assert(rec.Header().Get(params.EntityIdHeader), gc.Equals, "cs:development/trusty/wordpress-1")
433
// The promulgated charm cannot be retrieved using the published URL.
434
err = s.store.SetPerms(charm.MustParseURL("~charmers/wordpress"), "read", params.Everyone)
435
c.Assert(err, gc.IsNil)
436
httptesting.AssertJSONCall(c, httptesting.JSONCallParams{
438
URL: storeURL("wordpress/archive"),
439
ExpectStatus: http.StatusNotFound,
440
ExpectBody: params.Error{
441
Code: params.ErrNotFound,
442
Message: `no matching charm or bundle for "cs:wordpress"`,
447
var uploadAndPublishTests = []struct {
452
expectDevelopment bool
454
about: "upload same development entity",
455
existing: "~who/development/django-0",
456
upload: "~who/development/django",
457
expectId: "~who/development/django-0",
458
expectDevelopment: true,
460
about: "upload same published entity",
461
existing: "~who/django-0",
462
upload: "~who/django",
463
expectId: "~who/django-0",
465
about: "existing development, upload published",
466
existing: "~who/development/django-0",
467
upload: "~who/django",
468
expectId: "~who/django-0",
470
about: "existing published, upload development",
471
existing: "~who/django-0",
472
upload: "~who/development/django",
473
expectId: "~who/development/django-0",
476
func (s *ArchiveSuite) TestUploadAndPublish(c *gc.C) {
477
for i, test := range uploadAndPublishTests {
478
c.Logf("%d. %s", i, test.about)
480
// Upload the pre-existing entity.
481
rurl := newResolvedURL(test.existing, -1)
482
s.assertUploadCharm(c, "POST", rurl, "multi-series")
484
// Upload the same charm again, using the upload URL.
485
body, hash, size := archiveInfo(c, "multi-series")
486
httptesting.AssertJSONCall(c, httptesting.JSONCallParams{
488
URL: storeURL(test.upload + "/archive?hash=" + hash),
491
Header: http.Header{"Content-Type": {"application/zip"}},
493
Username: testUsername,
494
Password: testPassword,
495
ExpectBody: params.ArchiveUploadResponse{
496
Id: charm.MustParseURL(test.expectId),
500
// Check the development flag of the entity.
501
entity, err := s.store.FindEntity(rurl, "development")
502
c.Assert(err, gc.IsNil)
503
c.Assert(entity.Development, gc.Equals, test.expectDevelopment)
505
// Remove all entities from the store.
506
_, err = s.store.DB.Entities().RemoveAll(nil)
507
c.Assert(err, gc.IsNil)
508
_, err = s.store.DB.BaseEntities().RemoveAll(nil)
509
c.Assert(err, gc.IsNil)
435
s.assertUploadCharm(c, "POST", newResolvedURL("~charmers/precise/wordpress-0", -1), "wordpress")
513
438
func (s *ArchiveSuite) TestPostMultiSeriesCharm(c *gc.C) {
515
440
s.assertUploadCharm(c, "POST", newResolvedURL("~charmers/juju-gui-0", -1), "multi-series")
518
func (s *ArchiveSuite) TestPostMultiSeriesDevelopmentCharm(c *gc.C) {
519
// A charm that did not exist before should get revision 0.
520
s.assertUploadCharm(c, "POST", newResolvedURL("~charmers/development/juju-gui-0", -1), "multi-series")
523
var charmPostErrorTests = []struct {
528
expectBody interface{}
531
url: charm.MustParseURL("~charmers/juju-gui-0"),
533
expectStatus: http.StatusForbidden,
534
expectBody: params.Error{
535
Message: "series not specified in url or charm metadata",
536
Code: params.ErrEntityIdNotAllowed,
539
about: "url series not in metadata",
540
url: charm.MustParseURL("~charmers/precise/juju-gui-0"),
541
charm: "multi-series",
542
expectStatus: http.StatusForbidden,
543
expectBody: params.Error{
544
Message: `"precise" series not listed in charm metadata`,
545
Code: params.ErrEntityIdNotAllowed,
548
about: "bad combination of series",
549
url: charm.MustParseURL("~charmers/juju-gui-0"),
550
charm: "multi-series-bad-combination",
551
expectStatus: http.StatusBadRequest,
552
expectBody: params.Error{
553
Message: `cannot mix series from ubuntu and windows in single charm`,
554
Code: params.ErrInvalidEntity,
557
about: "unknown series",
558
url: charm.MustParseURL("~charmers/juju-gui-0"),
559
charm: "multi-series-unknown",
560
expectStatus: http.StatusBadRequest,
561
expectBody: params.Error{
562
Message: `unrecognised series "nosuchseries" in metadata`,
563
Code: params.ErrInvalidEntity,
567
func (s *ArchiveSuite) TestCharmPostError(c *gc.C) {
568
for i, test := range charmPostErrorTests {
569
c.Logf("%d. %s", i, test.about)
570
s.assertUploadCharmError(
582
443
func (s *ArchiveSuite) TestPostMultiSeriesCharmRevisionAfterAllSingleSeriesOnes(c *gc.C) {
583
444
// Create some single series versions of the charm
584
445
s.assertUploadCharm(c, "PUT", newResolvedURL("~charmers/vivid/juju-gui-1", -1), "mysql")
910
769
stats.CheckCounterSum(c, s.store, key, false, 3)
913
func (s *ArchiveSuite) TestPostErrorReadsFully(c *gc.C) {
917
b := bytes.NewBuffer([]byte("test body"))
918
r, err := http.NewRequest("POST", "/~charmers/trusty/wordpress/archive", b)
919
c.Assert(err, gc.IsNil)
920
r.Header.Set("Content-Type", "application/zip")
921
r.SetBasicAuth(testUsername, testPassword)
922
rec := httptest.NewRecorder()
924
c.Assert(rec.Code, gc.Equals, http.StatusBadRequest)
925
c.Assert(b.Len(), gc.Equals, 0)
928
func (s *ArchiveSuite) TestPostAuthErrorReadsFully(c *gc.C) {
931
b := bytes.NewBuffer([]byte("test body"))
932
r, err := http.NewRequest("POST", "/~charmers/trusty/wordpress/archive", b)
933
c.Assert(err, gc.IsNil)
934
r.Header.Set("Content-Type", "application/zip")
935
rec := httptest.NewRecorder()
937
c.Assert(rec.Code, gc.Equals, http.StatusUnauthorized)
938
c.Assert(b.Len(), gc.Equals, 0)
941
772
func (s *ArchiveSuite) TestUploadOfCurrentCharmReadsFully(c *gc.C) {
942
773
s.assertUploadCharm(c, "POST", newResolvedURL("~charmers/precise/wordpress-0", -1), "wordpress")
1089
var entity mongodoc.Entity
1090
err = s.store.DB.Entities().FindId(expectId.WithChannel("")).One(&entity)
922
entity, err := s.store.FindEntity(url, nil)
1091
923
c.Assert(err, gc.IsNil)
1092
924
c.Assert(entity.BlobHash, gc.Equals, hashSum)
1093
925
c.Assert(entity.BlobHash256, gc.Equals, hash256Sum)
1094
c.Assert(entity.PromulgatedURL, gc.DeepEquals, expectedPromulgatedId)
1095
c.Assert(entity.Development, gc.Equals, url.Development)
1096
// Test that the expected entry has been created
1097
// in the blob store.
1098
r, _, err := s.store.BlobStore.Open(entity.BlobName)
1099
c.Assert(err, gc.IsNil)
1102
return expectId, size
926
c.Assert(entity.PreV5BlobHash256, gc.Not(gc.Equals), "")
927
c.Assert(entity.PreV5BlobHash, gc.Not(gc.Equals), "")
928
c.Assert(entity.PreV5BlobSize, gc.Not(gc.Equals), int64(0))
930
c.Assert(entity.PromulgatedURL, gc.DeepEquals, url.PromulgatedURL())
931
c.Assert(entity.Development, gc.Equals, false)
933
return expectId, entity.PreV5BlobSize
1105
936
// assertUploadCharmError attempts to upload the testing charm with the
1178
1012
expectStatus: http.StatusNotFound,
1179
1013
expectMessage: `file "no-such" not found in the archive`,
1180
1014
expectCode: params.ErrNotFound,
1016
about: "no permissions",
1017
path: "~charmers/utopic/mysql-0/archive/metadata.yaml",
1018
expectStatus: http.StatusUnauthorized,
1019
expectMessage: `unauthorized: access denied for user "bob"`,
1020
expectCode: params.ErrUnauthorized,
1183
1023
func (s *ArchiveSuite) TestArchiveFileErrors(c *gc.C) {
1184
wordpress := storetesting.Charms.CharmArchive(c.MkDir(), "wordpress")
1185
url := newResolvedURL("cs:~charmers/utopic/wordpress-0", 0)
1186
err := s.store.AddCharmWithArchive(url, wordpress)
1187
c.Assert(err, gc.IsNil)
1188
err = s.store.SetPerms(&url.URL, "read", params.Everyone, url.URL.User)
1189
c.Assert(err, gc.IsNil)
1024
s.addPublicCharmFromRepo(c, "wordpress", newResolvedURL("cs:~charmers/utopic/wordpress-0", 0))
1025
id, _ := s.addPublicCharmFromRepo(c, "mysql", newResolvedURL("cs:~charmers/utopic/mysql-0", 0))
1026
err := s.store.SetPerms(&id.URL, "stable.read", "no-one")
1027
c.Assert(err, gc.IsNil)
1028
s.discharge = dischargeForUser("bob")
1190
1029
for i, test := range archiveFileErrorsTests {
1191
1030
c.Logf("test %d: %s", i, test.about)
1192
1031
httptesting.AssertJSONCall(c, httptesting.JSONCallParams{
1193
1032
Handler: s.srv,
1194
1033
URL: storeURL(test.path),
1196
1036
ExpectStatus: test.expectStatus,
1197
1037
ExpectBody: params.Error{
1275
1131
Method: "DELETE",
1276
1132
Username: testUsername,
1277
1133
Password: testPassword,
1278
ExpectStatus: http.StatusOK,
1134
ExpectStatus: http.StatusMethodNotAllowed,
1135
ExpectBody: params.Error{
1136
Message: `DELETE not allowed`,
1137
Code: params.ErrMethodNotAllowed,
1281
// The entity has been deleted.
1282
count, err := s.store.DB.Entities().FindId(url).Count()
1283
c.Assert(err, gc.IsNil)
1284
c.Assert(count, gc.Equals, 0)
1286
// The blob has been deleted.
1287
_, _, err = s.store.BlobStore.Open(entity.BlobName)
1288
c.Assert(err, gc.ErrorMatches, "resource.*not found")
1141
// TODO(mhilton) reinstate this check when DELETE is re-enabled.
1142
// // The entity has been deleted.
1143
// count, err := s.store.DB.Entities().FindId(url).Count()
1144
// c.Assert(err, gc.IsNil)
1145
// c.Assert(count, gc.Equals, 0)
1147
// // The blob has been deleted.
1148
// _, _, err = s.store.BlobStore.Open(entity.BlobName)
1149
// c.Assert(err, gc.ErrorMatches, "resource.*not found")
1291
1152
func (s *ArchiveSuite) TestDeleteSpecificCharm(c *gc.C) {
1327
1192
Method: "DELETE",
1328
1193
Username: testUsername,
1329
1194
Password: testPassword,
1330
ExpectStatus: http.StatusNotFound,
1331
ExpectBody: params.Error{
1332
Message: `no matching charm or bundle for "cs:~charmers/utopic/no-such-0"`,
1333
Code: params.ErrNotFound,
1338
func (s *ArchiveSuite) TestDeleteError(c *gc.C) {
1339
// Add a charm to the database (not including the archive).
1340
id := "~charmers/utopic/mysql-42"
1341
url := newResolvedURL(id, -1)
1342
err := s.store.AddCharm(storetesting.Charms.CharmArchive(c.MkDir(), "mysql"),
1343
charmstore.AddParams{
1345
BlobName: "no-such-name",
1346
BlobHash: fakeBlobHash,
1347
BlobSize: fakeBlobSize,
1349
c.Assert(err, gc.IsNil)
1351
// Try to delete the charm using the API.
1352
httptesting.AssertJSONCall(c, httptesting.JSONCallParams{
1354
URL: storeURL(id + "/archive"),
1356
Username: testUsername,
1357
Password: testPassword,
1358
ExpectStatus: http.StatusInternalServerError,
1359
ExpectBody: params.Error{
1360
Message: `cannot remove blob no-such-name: resource at path "global/no-such-name" not found`,
1365
func (s *ArchiveSuite) TestDeleteCounters(c *gc.C) {
1366
if !storetesting.MongoJSEnabled() {
1367
c.Skip("MongoDB JavaScript not available")
1370
// Add a charm to the database (including the archive).
1371
id := "~charmers/utopic/mysql-42"
1372
err := s.store.AddCharmWithArchive(
1373
newResolvedURL(id, -1),
1374
storetesting.Charms.CharmArchive(c.MkDir(), "mysql"))
1375
c.Assert(err, gc.IsNil)
1377
// Delete the charm using the API.
1378
rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
1381
URL: storeURL(id + "/archive"),
1382
Username: testUsername,
1383
Password: testPassword,
1385
c.Assert(rec.Code, gc.Equals, http.StatusOK)
1387
// Check that the delete count for the entity has been updated.
1388
key := []string{params.StatsArchiveDelete, "utopic", "mysql", "charmers", "42"}
1389
stats.CheckCounterSum(c, s.store, key, false, 1)
1392
func (s *ArchiveSuite) TestPostAuthErrors(c *gc.C) {
1393
checkAuthErrors(c, s.srv, "POST", "~charmers/utopic/django/archive")
1396
func (s *ArchiveSuite) TestDeleteAuthErrors(c *gc.C) {
1397
err := s.store.AddCharmWithArchive(
1398
newResolvedURL("~charmers/utopic/django-42", 42),
1399
storetesting.Charms.CharmArchive(c.MkDir(), "wordpress"),
1401
c.Assert(err, gc.IsNil)
1402
checkAuthErrors(c, s.srv, "DELETE", "utopic/django-42/archive")
1195
ExpectStatus: http.StatusMethodNotAllowed,
1196
ExpectBody: params.Error{
1197
Message: `DELETE not allowed`,
1198
Code: params.ErrMethodNotAllowed,
1203
// TODO(mhilton) reinstate this test when DELETE is re-enabled.
1204
//func (s *ArchiveSuite) TestDeleteError(c *gc.C) {
1205
// // Add a charm to the database (not including the archive).
1206
// id := "~charmers/utopic/mysql-42"
1207
// url := newResolvedURL(id, -1)
1208
// err := s.store.AddCharmWithArchive(url, storetesting.Charms.CharmArchive(c.MkDir(), "mysql"))
1209
// c.Assert(err, gc.IsNil)
1211
// err = s.store.DB.Entities().UpdateId(&url.URL, bson.M{
1213
// "blobname": "no-such-name",
1216
// c.Assert(err, gc.IsNil)
1217
// // TODO update entity to change BlobName to "no-such-name"
1219
// // Try to delete the charm using the API.
1220
// httptesting.AssertJSONCall(c, httptesting.JSONCallParams{
1222
// URL: storeURL(id + "/archive"),
1223
// Method: "DELETE",
1224
// Username: testUsername,
1225
// Password: testPassword,
1226
// ExpectStatus: http.StatusInternalServerError,
1227
// ExpectBody: params.Error{
1228
// Message: `cannot delete "cs:~charmers/utopic/mysql-42": cannot remove blob no-such-name: resource at path "global/no-such-name" not found`,
1233
// TODO(mhilton) reinstate this test when DELETE is re-enabled
1234
//.func (s *ArchiveSuite) TestDeleteCounters(c *gc.C) {
1235
// if !storetesting.MongoJSEnabled() {
1236
// c.Skip("MongoDB JavaScript not available")
1239
// // Add a charm to the database (including the archive).
1240
// id := "~charmers/utopic/mysql-42"
1241
// err := s.store.AddCharmWithArchive(
1242
// newResolvedURL(id, -1),
1243
// storetesting.Charms.CharmArchive(c.MkDir(), "mysql"))
1244
// c.Assert(err, gc.IsNil)
1246
// // Delete the charm using the API.
1247
// rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
1249
// Method: "DELETE",
1250
// URL: storeURL(id + "/archive"),
1251
// Username: testUsername,
1252
// Password: testPassword,
1254
// c.Assert(rec.Code, gc.Equals, http.StatusOK)
1256
// // Check that the delete count for the entity has been updated.
1257
// key := []string{params.StatsArchiveDelete, "utopic", "mysql", "charmers", "42"}
1258
// stats.CheckCounterSum(c, s.store, key, false, 1)
1261
type basicAuthArchiveSuite struct {
1265
var _ = gc.Suite(&basicAuthArchiveSuite{})
1267
func (s *basicAuthArchiveSuite) TestPostAuthErrors(c *gc.C) {
1268
s.checkAuthErrors(c, "POST", "~charmers/utopic/django/archive")
1271
// TODO(mhilton) reinstate this test when DELETE is re-enabled.
1272
//func (s *basicAuthArchiveSuite) TestDeleteAuthErrors(c *gc.C) {
1273
// err := s.store.AddCharmWithArchive(
1274
// newResolvedURL("~charmers/utopic/django-42", 42),
1275
// storetesting.Charms.CharmArchive(c.MkDir(), "wordpress"),
1277
// c.Assert(err, gc.IsNil)
1278
// s.checkAuthErrors(c, "DELETE", "utopic/django-42/archive")
1281
func (s *basicAuthArchiveSuite) TestPostErrorReadsFully(c *gc.C) {
1285
b := strings.NewReader("test body")
1286
r, err := http.NewRequest("POST", "/~charmers/trusty/wordpress/archive", b)
1287
c.Assert(err, gc.IsNil)
1288
r.Header.Set("Content-Type", "application/zip")
1289
r.SetBasicAuth(testUsername, testPassword)
1290
rec := httptest.NewRecorder()
1292
c.Assert(rec.Code, gc.Equals, http.StatusBadRequest)
1293
c.Assert(b.Len(), gc.Equals, 0)
1296
func (s *basicAuthArchiveSuite) TestPostAuthErrorReadsFully(c *gc.C) {
1299
b := strings.NewReader("test body")
1300
r, err := http.NewRequest("POST", "/~charmers/trusty/wordpress/archive", b)
1301
c.Assert(err, gc.IsNil)
1302
r.Header.Set("Content-Type", "application/zip")
1303
rec := httptest.NewRecorder()
1305
c.Assert(rec.Code, gc.Equals, http.StatusUnauthorized)
1306
c.Assert(b.Len(), gc.Equals, 0)
1405
1309
var archiveAuthErrorsTests = []struct {