No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 

591 líneas
15 KiB

  1. package s3_test
  2. import (
  3. "bytes"
  4. "crypto/md5"
  5. "fmt"
  6. "io/ioutil"
  7. "net"
  8. "net/http"
  9. "sort"
  10. "strings"
  11. "time"
  12. "github.com/goamz/goamz/aws"
  13. "github.com/goamz/goamz/s3"
  14. "github.com/goamz/goamz/testutil"
  15. . "gopkg.in/check.v1"
  16. )
  17. // AmazonServer represents an Amazon S3 server.
  18. type AmazonServer struct {
  19. auth aws.Auth
  20. }
  21. func (s *AmazonServer) SetUp(c *C) {
  22. auth, err := aws.EnvAuth()
  23. if err != nil {
  24. c.Fatal(err.Error())
  25. }
  26. s.auth = auth
  27. }
  28. var _ = Suite(&AmazonClientSuite{Region: aws.USEast})
  29. var _ = Suite(&AmazonClientSuite{Region: aws.EUWest})
  30. var _ = Suite(&AmazonDomainClientSuite{Region: aws.USEast})
  31. // AmazonClientSuite tests the client against a live S3 server.
  32. type AmazonClientSuite struct {
  33. aws.Region
  34. srv AmazonServer
  35. ClientTests
  36. }
  37. func (s *AmazonClientSuite) SetUpSuite(c *C) {
  38. if !testutil.Amazon {
  39. c.Skip("live tests against AWS disabled (no -amazon)")
  40. }
  41. s.srv.SetUp(c)
  42. s.s3 = s3.New(s.srv.auth, s.Region)
  43. // In case tests were interrupted in the middle before.
  44. s.ClientTests.Cleanup()
  45. }
  46. func (s *AmazonClientSuite) TearDownTest(c *C) {
  47. s.ClientTests.Cleanup()
  48. }
  49. // AmazonDomainClientSuite tests the client against a live S3
  50. // server using bucket names in the endpoint domain name rather
  51. // than the request path.
  52. type AmazonDomainClientSuite struct {
  53. aws.Region
  54. srv AmazonServer
  55. ClientTests
  56. }
  57. func (s *AmazonDomainClientSuite) SetUpSuite(c *C) {
  58. if !testutil.Amazon {
  59. c.Skip("live tests against AWS disabled (no -amazon)")
  60. }
  61. s.srv.SetUp(c)
  62. region := s.Region
  63. region.S3BucketEndpoint = "https://${bucket}.s3.amazonaws.com"
  64. s.s3 = s3.New(s.srv.auth, region)
  65. s.ClientTests.Cleanup()
  66. }
  67. func (s *AmazonDomainClientSuite) TearDownTest(c *C) {
  68. s.ClientTests.Cleanup()
  69. }
  70. // ClientTests defines integration tests designed to test the client.
  71. // It is not used as a test suite in itself, but embedded within
  72. // another type.
  73. type ClientTests struct {
  74. s3 *s3.S3
  75. authIsBroken bool
  76. }
  77. func (s *ClientTests) Cleanup() {
  78. killBucket(testBucket(s.s3))
  79. }
  80. func testBucket(s *s3.S3) *s3.Bucket {
  81. // Watch out! If this function is corrupted and made to match with something
  82. // people own, killBucket will happily remove *everything* inside the bucket.
  83. key := s.Auth.AccessKey
  84. if len(key) >= 8 {
  85. key = s.Auth.AccessKey[:8]
  86. }
  87. return s.Bucket(fmt.Sprintf("goamz-%s-%s", s.Region.Name, key))
  88. }
  89. var attempts = aws.AttemptStrategy{
  90. Min: 5,
  91. Total: 20 * time.Second,
  92. Delay: 100 * time.Millisecond,
  93. }
  94. func killBucket(b *s3.Bucket) {
  95. var err error
  96. for attempt := attempts.Start(); attempt.Next(); {
  97. err = b.DelBucket()
  98. if err == nil {
  99. return
  100. }
  101. if _, ok := err.(*net.DNSError); ok {
  102. return
  103. }
  104. e, ok := err.(*s3.Error)
  105. if ok && e.Code == "NoSuchBucket" {
  106. return
  107. }
  108. if ok && e.Code == "BucketNotEmpty" {
  109. // Errors are ignored here. Just retry.
  110. resp, err := b.List("", "", "", 1000)
  111. if err == nil {
  112. for _, key := range resp.Contents {
  113. _ = b.Del(key.Key)
  114. }
  115. }
  116. multis, _, _ := b.ListMulti("", "")
  117. for _, m := range multis {
  118. _ = m.Abort()
  119. }
  120. }
  121. }
  122. message := "cannot delete test bucket"
  123. if err != nil {
  124. message += ": " + err.Error()
  125. }
  126. panic(message)
  127. }
  128. func get(url string) ([]byte, error) {
  129. for attempt := attempts.Start(); attempt.Next(); {
  130. resp, err := http.Get(url)
  131. if err != nil {
  132. if attempt.HasNext() {
  133. continue
  134. }
  135. return nil, err
  136. }
  137. data, err := ioutil.ReadAll(resp.Body)
  138. resp.Body.Close()
  139. if err != nil {
  140. if attempt.HasNext() {
  141. continue
  142. }
  143. return nil, err
  144. }
  145. return data, err
  146. }
  147. panic("unreachable")
  148. }
  149. func (s *ClientTests) TestBasicFunctionality(c *C) {
  150. b := testBucket(s.s3)
  151. err := b.PutBucket(s3.PublicRead)
  152. c.Assert(err, IsNil)
  153. err = b.Put("name", []byte("yo!"), "text/plain", s3.PublicRead, s3.Options{})
  154. c.Assert(err, IsNil)
  155. defer b.Del("name")
  156. data, err := b.Get("name")
  157. c.Assert(err, IsNil)
  158. c.Assert(string(data), Equals, "yo!")
  159. data, err = get(b.URL("name"))
  160. c.Assert(err, IsNil)
  161. c.Assert(string(data), Equals, "yo!")
  162. buf := bytes.NewBufferString("hey!")
  163. err = b.PutReader("name2", buf, int64(buf.Len()), "text/plain", s3.Private, s3.Options{})
  164. c.Assert(err, IsNil)
  165. defer b.Del("name2")
  166. rc, err := b.GetReader("name2")
  167. c.Assert(err, IsNil)
  168. data, err = ioutil.ReadAll(rc)
  169. c.Check(err, IsNil)
  170. c.Check(string(data), Equals, "hey!")
  171. rc.Close()
  172. data, err = get(b.SignedURL("name2", time.Now().Add(time.Hour)))
  173. c.Assert(err, IsNil)
  174. c.Assert(string(data), Equals, "hey!")
  175. if !s.authIsBroken {
  176. data, err = get(b.SignedURL("name2", time.Now().Add(-time.Hour)))
  177. c.Assert(err, IsNil)
  178. c.Assert(string(data), Matches, "(?s).*AccessDenied.*")
  179. }
  180. err = b.DelBucket()
  181. c.Assert(err, NotNil)
  182. s3err, ok := err.(*s3.Error)
  183. c.Assert(ok, Equals, true)
  184. c.Assert(s3err.Code, Equals, "BucketNotEmpty")
  185. c.Assert(s3err.BucketName, Equals, b.Name)
  186. c.Assert(s3err.Message, Equals, "The bucket you tried to delete is not empty")
  187. err = b.Del("name")
  188. c.Assert(err, IsNil)
  189. err = b.Del("name2")
  190. c.Assert(err, IsNil)
  191. err = b.DelBucket()
  192. c.Assert(err, IsNil)
  193. }
  194. func (s *ClientTests) TestGetNotFound(c *C) {
  195. b := s.s3.Bucket("goamz-" + s.s3.Auth.AccessKey)
  196. data, err := b.Get("non-existent")
  197. s3err, _ := err.(*s3.Error)
  198. c.Assert(s3err, NotNil)
  199. c.Assert(s3err.StatusCode, Equals, 404)
  200. c.Assert(s3err.Code, Equals, "NoSuchBucket")
  201. c.Assert(s3err.Message, Equals, "The specified bucket does not exist")
  202. c.Assert(data, IsNil)
  203. }
  204. // Communicate with all endpoints to see if they are alive.
  205. func (s *ClientTests) TestRegions(c *C) {
  206. errs := make(chan error, len(aws.Regions))
  207. for _, region := range aws.Regions {
  208. go func(r aws.Region) {
  209. s := s3.New(s.s3.Auth, r)
  210. b := s.Bucket("goamz-" + s.Auth.AccessKey)
  211. _, err := b.Get("non-existent")
  212. errs <- err
  213. }(region)
  214. }
  215. for _ = range aws.Regions {
  216. err := <-errs
  217. if err != nil {
  218. s3_err, ok := err.(*s3.Error)
  219. if ok {
  220. c.Check(s3_err.Code, Matches, "NoSuchBucket")
  221. } else if _, ok = err.(*net.DNSError); ok {
  222. // Okay as well.
  223. } else {
  224. c.Errorf("Non-S3 error: %s", err)
  225. }
  226. } else {
  227. c.Errorf("Test should have errored but it seems to have succeeded")
  228. }
  229. }
  230. }
  231. var objectNames = []string{
  232. "index.html",
  233. "index2.html",
  234. "photos/2006/February/sample2.jpg",
  235. "photos/2006/February/sample3.jpg",
  236. "photos/2006/February/sample4.jpg",
  237. "photos/2006/January/sample.jpg",
  238. "test/bar",
  239. "test/foo",
  240. }
  241. func keys(names ...string) []s3.Key {
  242. ks := make([]s3.Key, len(names))
  243. for i, name := range names {
  244. ks[i].Key = name
  245. }
  246. return ks
  247. }
  248. // As the ListResp specifies all the parameters to the
  249. // request too, we use it to specify request parameters
  250. // and expected results. The Contents field is
  251. // used only for the key names inside it.
  252. var listTests = []s3.ListResp{
  253. // normal list.
  254. {
  255. Contents: keys(objectNames...),
  256. }, {
  257. Marker: objectNames[0],
  258. Contents: keys(objectNames[1:]...),
  259. }, {
  260. Marker: objectNames[0] + "a",
  261. Contents: keys(objectNames[1:]...),
  262. }, {
  263. Marker: "z",
  264. },
  265. // limited results.
  266. {
  267. MaxKeys: 2,
  268. Contents: keys(objectNames[0:2]...),
  269. IsTruncated: true,
  270. }, {
  271. MaxKeys: 2,
  272. Marker: objectNames[0],
  273. Contents: keys(objectNames[1:3]...),
  274. IsTruncated: true,
  275. }, {
  276. MaxKeys: 2,
  277. Marker: objectNames[len(objectNames)-2],
  278. Contents: keys(objectNames[len(objectNames)-1:]...),
  279. },
  280. // with delimiter
  281. {
  282. Delimiter: "/",
  283. CommonPrefixes: []string{"photos/", "test/"},
  284. Contents: keys("index.html", "index2.html"),
  285. }, {
  286. Delimiter: "/",
  287. Prefix: "photos/2006/",
  288. CommonPrefixes: []string{"photos/2006/February/", "photos/2006/January/"},
  289. }, {
  290. Delimiter: "/",
  291. Prefix: "t",
  292. CommonPrefixes: []string{"test/"},
  293. }, {
  294. Delimiter: "/",
  295. MaxKeys: 1,
  296. Contents: keys("index.html"),
  297. IsTruncated: true,
  298. }, {
  299. Delimiter: "/",
  300. MaxKeys: 1,
  301. Marker: "index2.html",
  302. CommonPrefixes: []string{"photos/"},
  303. IsTruncated: true,
  304. }, {
  305. Delimiter: "/",
  306. MaxKeys: 1,
  307. Marker: "photos/",
  308. CommonPrefixes: []string{"test/"},
  309. IsTruncated: false,
  310. }, {
  311. Delimiter: "Feb",
  312. CommonPrefixes: []string{"photos/2006/Feb"},
  313. Contents: keys("index.html", "index2.html", "photos/2006/January/sample.jpg", "test/bar", "test/foo"),
  314. },
  315. }
  316. func (s *ClientTests) TestDoublePutBucket(c *C) {
  317. b := testBucket(s.s3)
  318. err := b.PutBucket(s3.PublicRead)
  319. c.Assert(err, IsNil)
  320. err = b.PutBucket(s3.PublicRead)
  321. if err != nil {
  322. c.Assert(err, FitsTypeOf, new(s3.Error))
  323. c.Assert(err.(*s3.Error).Code, Equals, "BucketAlreadyOwnedByYou")
  324. }
  325. }
  326. func (s *ClientTests) TestBucketList(c *C) {
  327. b := testBucket(s.s3)
  328. err := b.PutBucket(s3.Private)
  329. c.Assert(err, IsNil)
  330. objData := make(map[string][]byte)
  331. for i, path := range objectNames {
  332. data := []byte(strings.Repeat("a", i))
  333. err := b.Put(path, data, "text/plain", s3.Private, s3.Options{})
  334. c.Assert(err, IsNil)
  335. defer b.Del(path)
  336. objData[path] = data
  337. }
  338. for i, t := range listTests {
  339. c.Logf("test %d", i)
  340. resp, err := b.List(t.Prefix, t.Delimiter, t.Marker, t.MaxKeys)
  341. c.Assert(err, IsNil)
  342. c.Check(resp.Name, Equals, b.Name)
  343. c.Check(resp.Delimiter, Equals, t.Delimiter)
  344. c.Check(resp.IsTruncated, Equals, t.IsTruncated)
  345. c.Check(resp.CommonPrefixes, DeepEquals, t.CommonPrefixes)
  346. checkContents(c, resp.Contents, objData, t.Contents)
  347. }
  348. }
  349. func etag(data []byte) string {
  350. sum := md5.New()
  351. sum.Write(data)
  352. return fmt.Sprintf(`"%x"`, sum.Sum(nil))
  353. }
  354. func checkContents(c *C, contents []s3.Key, data map[string][]byte, expected []s3.Key) {
  355. c.Assert(contents, HasLen, len(expected))
  356. for i, k := range contents {
  357. c.Check(k.Key, Equals, expected[i].Key)
  358. // TODO mtime
  359. c.Check(k.Size, Equals, int64(len(data[k.Key])))
  360. c.Check(k.ETag, Equals, etag(data[k.Key]))
  361. }
  362. }
  363. func (s *ClientTests) TestMultiInitPutList(c *C) {
  364. b := testBucket(s.s3)
  365. err := b.PutBucket(s3.Private)
  366. c.Assert(err, IsNil)
  367. multi, err := b.InitMulti("multi", "text/plain", s3.Private)
  368. c.Assert(err, IsNil)
  369. c.Assert(multi.UploadId, Matches, ".+")
  370. defer multi.Abort()
  371. var sent []s3.Part
  372. for i := 0; i < 5; i++ {
  373. p, err := multi.PutPart(i+1, strings.NewReader(fmt.Sprintf("<part %d>", i+1)))
  374. c.Assert(err, IsNil)
  375. c.Assert(p.N, Equals, i+1)
  376. c.Assert(p.Size, Equals, int64(8))
  377. c.Assert(p.ETag, Matches, ".+")
  378. sent = append(sent, p)
  379. }
  380. s3.SetListPartsMax(2)
  381. parts, err := multi.ListParts()
  382. c.Assert(err, IsNil)
  383. c.Assert(parts, HasLen, len(sent))
  384. for i := range parts {
  385. c.Assert(parts[i].N, Equals, sent[i].N)
  386. c.Assert(parts[i].Size, Equals, sent[i].Size)
  387. c.Assert(parts[i].ETag, Equals, sent[i].ETag)
  388. }
  389. err = multi.Complete(parts)
  390. s3err, failed := err.(*s3.Error)
  391. c.Assert(failed, Equals, true)
  392. c.Assert(s3err.Code, Equals, "EntityTooSmall")
  393. err = multi.Abort()
  394. c.Assert(err, IsNil)
  395. _, err = multi.ListParts()
  396. s3err, ok := err.(*s3.Error)
  397. c.Assert(ok, Equals, true)
  398. c.Assert(s3err.Code, Equals, "NoSuchUpload")
  399. }
  400. // This may take a minute or more due to the minimum size accepted S3
  401. // on multipart upload parts.
  402. func (s *ClientTests) TestMultiComplete(c *C) {
  403. b := testBucket(s.s3)
  404. err := b.PutBucket(s3.Private)
  405. c.Assert(err, IsNil)
  406. multi, err := b.InitMulti("multi", "text/plain", s3.Private)
  407. c.Assert(err, IsNil)
  408. c.Assert(multi.UploadId, Matches, ".+")
  409. defer multi.Abort()
  410. // Minimum size S3 accepts for all but the last part is 5MB.
  411. data1 := make([]byte, 5*1024*1024)
  412. data2 := []byte("<part 2>")
  413. part1, err := multi.PutPart(1, bytes.NewReader(data1))
  414. c.Assert(err, IsNil)
  415. part2, err := multi.PutPart(2, bytes.NewReader(data2))
  416. c.Assert(err, IsNil)
  417. // Purposefully reversed. The order requirement must be handled.
  418. err = multi.Complete([]s3.Part{part2, part1})
  419. c.Assert(err, IsNil)
  420. data, err := b.Get("multi")
  421. c.Assert(err, IsNil)
  422. c.Assert(len(data), Equals, len(data1)+len(data2))
  423. for i := range data1 {
  424. if data[i] != data1[i] {
  425. c.Fatalf("uploaded object at byte %d: want %d, got %d", data1[i], data[i])
  426. }
  427. }
  428. c.Assert(string(data[len(data1):]), Equals, string(data2))
  429. }
  430. type multiList []*s3.Multi
  431. func (l multiList) Len() int { return len(l) }
  432. func (l multiList) Less(i, j int) bool { return l[i].Key < l[j].Key }
  433. func (l multiList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
  434. func (s *ClientTests) TestListMulti(c *C) {
  435. b := testBucket(s.s3)
  436. err := b.PutBucket(s3.Private)
  437. c.Assert(err, IsNil)
  438. // Ensure an empty state before testing its behavior.
  439. multis, _, err := b.ListMulti("", "")
  440. for _, m := range multis {
  441. err := m.Abort()
  442. c.Assert(err, IsNil)
  443. }
  444. keys := []string{
  445. "a/multi2",
  446. "a/multi3",
  447. "b/multi4",
  448. "multi1",
  449. }
  450. for _, key := range keys {
  451. m, err := b.InitMulti(key, "", s3.Private)
  452. c.Assert(err, IsNil)
  453. defer m.Abort()
  454. }
  455. // Amazon's implementation of the multiple-request listing for
  456. // multipart uploads in progress seems broken in multiple ways.
  457. // (next tokens are not provided, etc).
  458. //s3.SetListMultiMax(2)
  459. multis, prefixes, err := b.ListMulti("", "")
  460. c.Assert(err, IsNil)
  461. for attempt := attempts.Start(); attempt.Next() && len(multis) < len(keys); {
  462. multis, prefixes, err = b.ListMulti("", "")
  463. c.Assert(err, IsNil)
  464. }
  465. sort.Sort(multiList(multis))
  466. c.Assert(prefixes, IsNil)
  467. var gotKeys []string
  468. for _, m := range multis {
  469. gotKeys = append(gotKeys, m.Key)
  470. }
  471. c.Assert(gotKeys, DeepEquals, keys)
  472. for _, m := range multis {
  473. c.Assert(m.Bucket, Equals, b)
  474. c.Assert(m.UploadId, Matches, ".+")
  475. }
  476. multis, prefixes, err = b.ListMulti("", "/")
  477. for attempt := attempts.Start(); attempt.Next() && len(prefixes) < 2; {
  478. multis, prefixes, err = b.ListMulti("", "")
  479. c.Assert(err, IsNil)
  480. }
  481. c.Assert(err, IsNil)
  482. c.Assert(prefixes, DeepEquals, []string{"a/", "b/"})
  483. c.Assert(multis, HasLen, 1)
  484. c.Assert(multis[0].Bucket, Equals, b)
  485. c.Assert(multis[0].Key, Equals, "multi1")
  486. c.Assert(multis[0].UploadId, Matches, ".+")
  487. for attempt := attempts.Start(); attempt.Next() && len(multis) < 2; {
  488. multis, prefixes, err = b.ListMulti("", "")
  489. c.Assert(err, IsNil)
  490. }
  491. multis, prefixes, err = b.ListMulti("a/", "/")
  492. c.Assert(err, IsNil)
  493. c.Assert(prefixes, IsNil)
  494. c.Assert(multis, HasLen, 2)
  495. c.Assert(multis[0].Bucket, Equals, b)
  496. c.Assert(multis[0].Key, Equals, "a/multi2")
  497. c.Assert(multis[0].UploadId, Matches, ".+")
  498. c.Assert(multis[1].Bucket, Equals, b)
  499. c.Assert(multis[1].Key, Equals, "a/multi3")
  500. c.Assert(multis[1].UploadId, Matches, ".+")
  501. }
  502. func (s *ClientTests) TestMultiPutAllZeroLength(c *C) {
  503. b := testBucket(s.s3)
  504. err := b.PutBucket(s3.Private)
  505. c.Assert(err, IsNil)
  506. multi, err := b.InitMulti("multi", "text/plain", s3.Private)
  507. c.Assert(err, IsNil)
  508. defer multi.Abort()
  509. // This tests an edge case. Amazon requires at least one
  510. // part for multiprat uploads to work, even the part is empty.
  511. parts, err := multi.PutAll(strings.NewReader(""), 5*1024*1024)
  512. c.Assert(err, IsNil)
  513. c.Assert(parts, HasLen, 1)
  514. c.Assert(parts[0].Size, Equals, int64(0))
  515. c.Assert(parts[0].ETag, Equals, `"d41d8cd98f00b204e9800998ecf8427e"`)
  516. err = multi.Complete(parts)
  517. c.Assert(err, IsNil)
  518. }