You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

307 lines
7.8 KiB

  1. /*
  2. * Minio Cloud Storage, (C) 2016 Minio, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package cmd
  17. import (
  18. "path"
  19. "sort"
  20. "sync"
  21. )
  22. /// Bucket operations
  23. // MakeBucket - make a bucket.
  24. func (xl xlObjects) MakeBucket(bucket string) error {
  25. // Verify if bucket is valid.
  26. if !IsValidBucketName(bucket) {
  27. return traceError(BucketNameInvalid{Bucket: bucket})
  28. }
  29. // get a random ID for lock instrumentation.
  30. opsID := getOpsID()
  31. nsMutex.Lock(bucket, "", opsID)
  32. defer nsMutex.Unlock(bucket, "", opsID)
  33. // Initialize sync waitgroup.
  34. var wg = &sync.WaitGroup{}
  35. // Initialize list of errors.
  36. var dErrs = make([]error, len(xl.storageDisks))
  37. // Make a volume entry on all underlying storage disks.
  38. for index, disk := range xl.storageDisks {
  39. if disk == nil {
  40. dErrs[index] = traceError(errDiskNotFound)
  41. continue
  42. }
  43. wg.Add(1)
  44. // Make a volume inside a go-routine.
  45. go func(index int, disk StorageAPI) {
  46. defer wg.Done()
  47. err := disk.MakeVol(bucket)
  48. if err != nil {
  49. dErrs[index] = traceError(err)
  50. }
  51. }(index, disk)
  52. }
  53. // Wait for all make vol to finish.
  54. wg.Wait()
  55. // Do we have write quorum?.
  56. if !isDiskQuorum(dErrs, xl.writeQuorum) {
  57. // Purge successfully created buckets if we don't have writeQuorum.
  58. xl.undoMakeBucket(bucket)
  59. return toObjectErr(traceError(errXLWriteQuorum), bucket)
  60. }
  61. // Verify we have any other errors which should undo make bucket.
  62. if reducedErr := reduceErrs(dErrs, []error{
  63. errDiskNotFound,
  64. errFaultyDisk,
  65. errDiskAccessDenied,
  66. }); reducedErr != nil {
  67. return toObjectErr(reducedErr, bucket)
  68. }
  69. return nil
  70. }
  71. func (xl xlObjects) undoDeleteBucket(bucket string) {
  72. // Initialize sync waitgroup.
  73. var wg = &sync.WaitGroup{}
  74. // Undo previous make bucket entry on all underlying storage disks.
  75. for index, disk := range xl.storageDisks {
  76. if disk == nil {
  77. continue
  78. }
  79. wg.Add(1)
  80. // Delete a bucket inside a go-routine.
  81. go func(index int, disk StorageAPI) {
  82. defer wg.Done()
  83. _ = disk.MakeVol(bucket)
  84. }(index, disk)
  85. }
  86. // Wait for all make vol to finish.
  87. wg.Wait()
  88. }
  89. // undo make bucket operation upon quorum failure.
  90. func (xl xlObjects) undoMakeBucket(bucket string) {
  91. // Initialize sync waitgroup.
  92. var wg = &sync.WaitGroup{}
  93. // Undo previous make bucket entry on all underlying storage disks.
  94. for index, disk := range xl.storageDisks {
  95. if disk == nil {
  96. continue
  97. }
  98. wg.Add(1)
  99. // Delete a bucket inside a go-routine.
  100. go func(index int, disk StorageAPI) {
  101. defer wg.Done()
  102. _ = disk.DeleteVol(bucket)
  103. }(index, disk)
  104. }
  105. // Wait for all make vol to finish.
  106. wg.Wait()
  107. }
  108. // list all errors that can be ignored in a bucket metadata operation.
  109. var bucketMetadataOpIgnoredErrs = []error{
  110. errDiskNotFound,
  111. errDiskAccessDenied,
  112. errFaultyDisk,
  113. errVolumeNotFound,
  114. }
  115. // getBucketInfo - returns the BucketInfo from one of the load balanced disks.
  116. func (xl xlObjects) getBucketInfo(bucketName string) (bucketInfo BucketInfo, err error) {
  117. for _, disk := range xl.getLoadBalancedDisks() {
  118. if disk == nil {
  119. continue
  120. }
  121. var volInfo VolInfo
  122. volInfo, err = disk.StatVol(bucketName)
  123. if err == nil {
  124. bucketInfo = BucketInfo{
  125. Name: volInfo.Name,
  126. Created: volInfo.Created,
  127. }
  128. return bucketInfo, nil
  129. }
  130. err = traceError(err)
  131. // For any reason disk went offline continue and pick the next one.
  132. if isErrIgnored(err, bucketMetadataOpIgnoredErrs) {
  133. continue
  134. }
  135. break
  136. }
  137. return BucketInfo{}, err
  138. }
  139. // Checks whether bucket exists.
  140. func (xl xlObjects) isBucketExist(bucket string) bool {
  141. // Check whether bucket exists.
  142. _, err := xl.getBucketInfo(bucket)
  143. if err != nil {
  144. if err == errVolumeNotFound {
  145. return false
  146. }
  147. return false
  148. }
  149. return true
  150. }
  151. // GetBucketInfo - returns BucketInfo for a bucket.
  152. func (xl xlObjects) GetBucketInfo(bucket string) (BucketInfo, error) {
  153. // Verify if bucket is valid.
  154. if !IsValidBucketName(bucket) {
  155. return BucketInfo{}, BucketNameInvalid{Bucket: bucket}
  156. }
  157. // get a random ID for lock instrumentation.
  158. opsID := getOpsID()
  159. nsMutex.RLock(bucket, "", opsID)
  160. defer nsMutex.RUnlock(bucket, "", opsID)
  161. bucketInfo, err := xl.getBucketInfo(bucket)
  162. if err != nil {
  163. return BucketInfo{}, toObjectErr(err, bucket)
  164. }
  165. return bucketInfo, nil
  166. }
  167. // listBuckets - returns list of all buckets from a disk picked at random.
  168. func (xl xlObjects) listBuckets() (bucketsInfo []BucketInfo, err error) {
  169. for _, disk := range xl.getLoadBalancedDisks() {
  170. if disk == nil {
  171. continue
  172. }
  173. var volsInfo []VolInfo
  174. volsInfo, err = disk.ListVols()
  175. if err == nil {
  176. // NOTE: The assumption here is that volumes across all disks in
  177. // readQuorum have consistent view i.e they all have same number
  178. // of buckets. This is essentially not verified since healing
  179. // should take care of this.
  180. var bucketsInfo []BucketInfo
  181. for _, volInfo := range volsInfo {
  182. // StorageAPI can send volume names which are incompatible
  183. // with buckets, handle it and skip them.
  184. if !IsValidBucketName(volInfo.Name) {
  185. continue
  186. }
  187. // Ignore the volume special bucket.
  188. if volInfo.Name == minioMetaBucket {
  189. continue
  190. }
  191. bucketsInfo = append(bucketsInfo, BucketInfo{
  192. Name: volInfo.Name,
  193. Created: volInfo.Created,
  194. })
  195. }
  196. // For buckets info empty, loop once again to check
  197. // if we have, can happen if disks are down.
  198. if len(bucketsInfo) == 0 {
  199. continue
  200. }
  201. return bucketsInfo, nil
  202. }
  203. // Ignore any disks not found.
  204. if isErrIgnored(err, bucketMetadataOpIgnoredErrs) {
  205. continue
  206. }
  207. break
  208. }
  209. return nil, err
  210. }
  211. // ListBuckets - lists all the buckets, sorted by its name.
  212. func (xl xlObjects) ListBuckets() ([]BucketInfo, error) {
  213. bucketInfos, err := xl.listBuckets()
  214. if err != nil {
  215. return nil, toObjectErr(err)
  216. }
  217. // Sort by bucket name before returning.
  218. sort.Sort(byBucketName(bucketInfos))
  219. return bucketInfos, nil
  220. }
  221. // DeleteBucket - deletes a bucket.
  222. func (xl xlObjects) DeleteBucket(bucket string) error {
  223. // Verify if bucket is valid.
  224. if !IsValidBucketName(bucket) {
  225. return BucketNameInvalid{Bucket: bucket}
  226. }
  227. // get a random ID for lock instrumentation.
  228. opsID := getOpsID()
  229. nsMutex.Lock(bucket, "", opsID)
  230. defer nsMutex.Unlock(bucket, "", opsID)
  231. // Collect if all disks report volume not found.
  232. var wg = &sync.WaitGroup{}
  233. var dErrs = make([]error, len(xl.storageDisks))
  234. // Remove a volume entry on all underlying storage disks.
  235. for index, disk := range xl.storageDisks {
  236. if disk == nil {
  237. dErrs[index] = traceError(errDiskNotFound)
  238. continue
  239. }
  240. wg.Add(1)
  241. // Delete volume inside a go-routine.
  242. go func(index int, disk StorageAPI) {
  243. defer wg.Done()
  244. // Attempt to delete bucket.
  245. err := disk.DeleteVol(bucket)
  246. if err != nil {
  247. dErrs[index] = traceError(err)
  248. return
  249. }
  250. // Cleanup all the previously incomplete multiparts.
  251. err = cleanupDir(disk, path.Join(minioMetaBucket, mpartMetaPrefix), bucket)
  252. if err != nil {
  253. if errorCause(err) == errVolumeNotFound {
  254. return
  255. }
  256. dErrs[index] = err
  257. }
  258. }(index, disk)
  259. }
  260. // Wait for all the delete vols to finish.
  261. wg.Wait()
  262. if !isDiskQuorum(dErrs, xl.writeQuorum) {
  263. xl.undoDeleteBucket(bucket)
  264. return toObjectErr(traceError(errXLWriteQuorum), bucket)
  265. }
  266. if reducedErr := reduceErrs(dErrs, []error{
  267. errFaultyDisk,
  268. errDiskNotFound,
  269. errDiskAccessDenied,
  270. }); reducedErr != nil {
  271. return toObjectErr(reducedErr, bucket)
  272. }
  273. // Success.
  274. return nil
  275. }