api-remove.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. /*
  2. * Minio Go Library for Amazon S3 Compatible Cloud Storage
  3. * Copyright 2015-2017 Minio, Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package minio
  18. import (
  19. "bytes"
  20. "context"
  21. "encoding/xml"
  22. "io"
  23. "net/http"
  24. "net/url"
  25. "github.com/minio/minio-go/pkg/s3utils"
  26. )
  27. // RemoveBucket deletes the bucket name.
  28. //
  29. // All objects (including all object versions and delete markers).
  30. // in the bucket must be deleted before successfully attempting this request.
  31. func (c Client) RemoveBucket(bucketName string) error {
  32. // Input validation.
  33. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  34. return err
  35. }
  36. // Execute DELETE on bucket.
  37. resp, err := c.executeMethod(context.Background(), "DELETE", requestMetadata{
  38. bucketName: bucketName,
  39. contentSHA256Hex: emptySHA256Hex,
  40. })
  41. defer closeResponse(resp)
  42. if err != nil {
  43. return err
  44. }
  45. if resp != nil {
  46. if resp.StatusCode != http.StatusNoContent {
  47. return httpRespToErrorResponse(resp, bucketName, "")
  48. }
  49. }
  50. // Remove the location from cache on a successful delete.
  51. c.bucketLocCache.Delete(bucketName)
  52. return nil
  53. }
  54. // RemoveObject remove an object from a bucket.
  55. func (c Client) RemoveObject(bucketName, objectName string) error {
  56. // Input validation.
  57. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  58. return err
  59. }
  60. if err := s3utils.CheckValidObjectName(objectName); err != nil {
  61. return err
  62. }
  63. // Execute DELETE on objectName.
  64. resp, err := c.executeMethod(context.Background(), "DELETE", requestMetadata{
  65. bucketName: bucketName,
  66. objectName: objectName,
  67. contentSHA256Hex: emptySHA256Hex,
  68. })
  69. defer closeResponse(resp)
  70. if err != nil {
  71. return err
  72. }
  73. if resp != nil {
  74. // if some unexpected error happened and max retry is reached, we want to let client know
  75. if resp.StatusCode != http.StatusNoContent {
  76. return httpRespToErrorResponse(resp, bucketName, objectName)
  77. }
  78. }
  79. // DeleteObject always responds with http '204' even for
  80. // objects which do not exist. So no need to handle them
  81. // specifically.
  82. return nil
  83. }
  84. // RemoveObjectError - container of Multi Delete S3 API error
  85. type RemoveObjectError struct {
  86. ObjectName string
  87. Err error
  88. }
  89. // generateRemoveMultiObjects - generate the XML request for remove multi objects request
  90. func generateRemoveMultiObjectsRequest(objects []string) []byte {
  91. rmObjects := []deleteObject{}
  92. for _, obj := range objects {
  93. rmObjects = append(rmObjects, deleteObject{Key: obj})
  94. }
  95. xmlBytes, _ := xml.Marshal(deleteMultiObjects{Objects: rmObjects, Quiet: true})
  96. return xmlBytes
  97. }
  98. // processRemoveMultiObjectsResponse - parse the remove multi objects web service
  99. // and return the success/failure result status for each object
  100. func processRemoveMultiObjectsResponse(body io.Reader, objects []string, errorCh chan<- RemoveObjectError) {
  101. // Parse multi delete XML response
  102. rmResult := &deleteMultiObjectsResult{}
  103. err := xmlDecoder(body, rmResult)
  104. if err != nil {
  105. errorCh <- RemoveObjectError{ObjectName: "", Err: err}
  106. return
  107. }
  108. // Fill deletion that returned an error.
  109. for _, obj := range rmResult.UnDeletedObjects {
  110. errorCh <- RemoveObjectError{
  111. ObjectName: obj.Key,
  112. Err: ErrorResponse{
  113. Code: obj.Code,
  114. Message: obj.Message,
  115. },
  116. }
  117. }
  118. }
  119. // RemoveObjects remove multiples objects from a bucket.
  120. // The list of objects to remove are received from objectsCh.
  121. // Remove failures are sent back via error channel.
  122. func (c Client) RemoveObjects(bucketName string, objectsCh <-chan string) <-chan RemoveObjectError {
  123. errorCh := make(chan RemoveObjectError, 1)
  124. // Validate if bucket name is valid.
  125. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  126. defer close(errorCh)
  127. errorCh <- RemoveObjectError{
  128. Err: err,
  129. }
  130. return errorCh
  131. }
  132. // Validate objects channel to be properly allocated.
  133. if objectsCh == nil {
  134. defer close(errorCh)
  135. errorCh <- RemoveObjectError{
  136. Err: ErrInvalidArgument("Objects channel cannot be nil"),
  137. }
  138. return errorCh
  139. }
  140. // Generate and call MultiDelete S3 requests based on entries received from objectsCh
  141. go func(errorCh chan<- RemoveObjectError) {
  142. maxEntries := 1000
  143. finish := false
  144. urlValues := make(url.Values)
  145. urlValues.Set("delete", "")
  146. // Close error channel when Multi delete finishes.
  147. defer close(errorCh)
  148. // Loop over entries by 1000 and call MultiDelete requests
  149. for {
  150. if finish {
  151. break
  152. }
  153. count := 0
  154. var batch []string
  155. // Try to gather 1000 entries
  156. for object := range objectsCh {
  157. batch = append(batch, object)
  158. if count++; count >= maxEntries {
  159. break
  160. }
  161. }
  162. if count == 0 {
  163. // Multi Objects Delete API doesn't accept empty object list, quit immediately
  164. break
  165. }
  166. if count < maxEntries {
  167. // We didn't have 1000 entries, so this is the last batch
  168. finish = true
  169. }
  170. // Generate remove multi objects XML request
  171. removeBytes := generateRemoveMultiObjectsRequest(batch)
  172. // Execute GET on bucket to list objects.
  173. resp, err := c.executeMethod(context.Background(), "POST", requestMetadata{
  174. bucketName: bucketName,
  175. queryValues: urlValues,
  176. contentBody: bytes.NewReader(removeBytes),
  177. contentLength: int64(len(removeBytes)),
  178. contentMD5Base64: sumMD5Base64(removeBytes),
  179. contentSHA256Hex: sum256Hex(removeBytes),
  180. })
  181. if err != nil {
  182. for _, b := range batch {
  183. errorCh <- RemoveObjectError{ObjectName: b, Err: err}
  184. }
  185. continue
  186. }
  187. // Process multiobjects remove xml response
  188. processRemoveMultiObjectsResponse(resp.Body, batch, errorCh)
  189. closeResponse(resp)
  190. }
  191. }(errorCh)
  192. return errorCh
  193. }
  194. // RemoveIncompleteUpload aborts an partially uploaded object.
  195. func (c Client) RemoveIncompleteUpload(bucketName, objectName string) error {
  196. // Input validation.
  197. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  198. return err
  199. }
  200. if err := s3utils.CheckValidObjectName(objectName); err != nil {
  201. return err
  202. }
  203. // Find multipart upload id of the object to be aborted.
  204. uploadID, err := c.findUploadID(bucketName, objectName)
  205. if err != nil {
  206. return err
  207. }
  208. if uploadID != "" {
  209. // Upload id found, abort the incomplete multipart upload.
  210. err := c.abortMultipartUpload(context.Background(), bucketName, objectName, uploadID)
  211. if err != nil {
  212. return err
  213. }
  214. }
  215. return nil
  216. }
  217. // abortMultipartUpload aborts a multipart upload for the given
  218. // uploadID, all previously uploaded parts are deleted.
  219. func (c Client) abortMultipartUpload(ctx context.Context, bucketName, objectName, uploadID string) error {
  220. // Input validation.
  221. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  222. return err
  223. }
  224. if err := s3utils.CheckValidObjectName(objectName); err != nil {
  225. return err
  226. }
  227. // Initialize url queries.
  228. urlValues := make(url.Values)
  229. urlValues.Set("uploadId", uploadID)
  230. // Execute DELETE on multipart upload.
  231. resp, err := c.executeMethod(ctx, "DELETE", requestMetadata{
  232. bucketName: bucketName,
  233. objectName: objectName,
  234. queryValues: urlValues,
  235. contentSHA256Hex: emptySHA256Hex,
  236. })
  237. defer closeResponse(resp)
  238. if err != nil {
  239. return err
  240. }
  241. if resp != nil {
  242. if resp.StatusCode != http.StatusNoContent {
  243. // Abort has no response body, handle it for any errors.
  244. var errorResponse ErrorResponse
  245. switch resp.StatusCode {
  246. case http.StatusNotFound:
  247. // This is needed specifically for abort and it cannot
  248. // be converged into default case.
  249. errorResponse = ErrorResponse{
  250. Code: "NoSuchUpload",
  251. Message: "The specified multipart upload does not exist.",
  252. BucketName: bucketName,
  253. Key: objectName,
  254. RequestID: resp.Header.Get("x-amz-request-id"),
  255. HostID: resp.Header.Get("x-amz-id-2"),
  256. Region: resp.Header.Get("x-amz-bucket-region"),
  257. }
  258. default:
  259. return httpRespToErrorResponse(resp, bucketName, objectName)
  260. }
  261. return errorResponse
  262. }
  263. }
  264. return nil
  265. }