api-put-bucket.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  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 s3cli
  18. import (
  19. "bytes"
  20. "context"
  21. "encoding/xml"
  22. "fmt"
  23. "io/ioutil"
  24. "net/http"
  25. "net/url"
  26. "os"
  27. "strings"
  28. "github.com/minio/minio-go/v6/pkg/s3utils"
  29. )
  30. /// Bucket operations
  31. // MakeBucket creates a new bucket with bucketName.
  32. //
  33. // Location is an optional argument, by default all buckets are
  34. // created in US Standard Region.
  35. //
  36. // For Amazon S3 for more supported regions - http://docs.aws.amazon.com/general/latest/gr/rande.html
  37. // For Google Cloud Storage for more supported regions - https://cloud.google.com/storage/docs/bucket-locations
  38. func (c Client) MakeBucket(bucketName string, location string) (err error) {
  39. defer func() {
  40. // Save the location into cache on a successful makeBucket response.
  41. if err == nil {
  42. c.bucketLocCache.Set(bucketName, location)
  43. }
  44. }()
  45. // Validate the input arguments.
  46. if err := s3utils.CheckValidBucketNameStrict(bucketName); err != nil {
  47. return err
  48. }
  49. // If location is empty, treat is a default region 'us-east-1'.
  50. if location == "" {
  51. location = "us-east-1"
  52. // For custom region clients, default
  53. // to custom region instead not 'us-east-1'.
  54. if c.region != "" {
  55. location = c.region
  56. }
  57. }
  58. // PUT bucket request metadata.
  59. reqMetadata := requestMetadata{
  60. bucketName: bucketName,
  61. bucketLocation: location,
  62. }
  63. // If location is not 'us-east-1' create bucket location config.
  64. if location != "us-east-1" && location != "" {
  65. createBucketConfig := CreateBucketConfiguration{}
  66. createBucketConfig.Location = location
  67. var createBucketConfigBytes []byte
  68. createBucketConfigBytes, err = xml.Marshal(createBucketConfig)
  69. if err != nil {
  70. return err
  71. }
  72. reqMetadata.contentMD5Base64 = sumMD5Base64(createBucketConfigBytes)
  73. reqMetadata.contentSHA256Hex = sum256Hex(createBucketConfigBytes)
  74. reqMetadata.contentBody = bytes.NewReader(createBucketConfigBytes)
  75. reqMetadata.contentLength = int64(len(createBucketConfigBytes))
  76. }
  77. // Execute PUT to create a new bucket.
  78. resp, err := c.executeMethod(context.Background(), "PUT", reqMetadata)
  79. defer closeResponse(resp)
  80. if err != nil {
  81. return err
  82. }
  83. if resp != nil {
  84. if resp.StatusCode != http.StatusOK {
  85. return httpRespToErrorResponse(resp, bucketName, "")
  86. }
  87. }
  88. // Success.
  89. return nil
  90. }
  91. // SetBucketPolicy set the access permissions on an existing bucket.
  92. func (c Client) SetBucketPolicy(bucketName, policy string) error {
  93. // Input validation.
  94. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  95. return err
  96. }
  97. // If policy is empty then delete the bucket policy.
  98. if policy == "" {
  99. return c.removeBucketPolicy(bucketName)
  100. }
  101. // Save the updated policies.
  102. return c.putBucketPolicy(bucketName, policy)
  103. }
  104. // Saves a new bucket policy.
  105. func (c Client) putBucketPolicy(bucketName, policy string) error {
  106. // Input validation.
  107. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  108. return err
  109. }
  110. // Get resources properly escaped and lined up before
  111. // using them in http request.
  112. urlValues := make(url.Values)
  113. urlValues.Set("policy", "")
  114. // Content-length is mandatory for put policy request
  115. policyReader := strings.NewReader(policy)
  116. b, err := ioutil.ReadAll(policyReader)
  117. if err != nil {
  118. return err
  119. }
  120. reqMetadata := requestMetadata{
  121. bucketName: bucketName,
  122. queryValues: urlValues,
  123. contentBody: policyReader,
  124. contentLength: int64(len(b)),
  125. }
  126. // Execute PUT to upload a new bucket policy.
  127. resp, err := c.executeMethod(context.Background(), "PUT", reqMetadata)
  128. defer closeResponse(resp)
  129. if err != nil {
  130. return err
  131. }
  132. if resp != nil {
  133. if resp.StatusCode != http.StatusNoContent {
  134. return httpRespToErrorResponse(resp, bucketName, "")
  135. }
  136. }
  137. return nil
  138. }
  139. // Removes all policies on a bucket.
  140. func (c Client) removeBucketPolicy(bucketName string) error {
  141. // Input validation.
  142. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  143. return err
  144. }
  145. // Get resources properly escaped and lined up before
  146. // using them in http request.
  147. urlValues := make(url.Values)
  148. urlValues.Set("policy", "")
  149. // Execute DELETE on objectName.
  150. resp, err := c.executeMethod(context.Background(), "DELETE", requestMetadata{
  151. bucketName: bucketName,
  152. queryValues: urlValues,
  153. contentSHA256Hex: emptySHA256Hex,
  154. })
  155. defer closeResponse(resp)
  156. if err != nil {
  157. return err
  158. }
  159. return nil
  160. }
  161. // SetBucketLifecycle set the lifecycle on an existing bucket.
  162. func (c Client) SetBucketLifecycle(bucketName, lifecycle string) error {
  163. // Input validation.
  164. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  165. return err
  166. }
  167. // If lifecycle is empty then delete it.
  168. if lifecycle == "" {
  169. return c.removeBucketLifecycle(bucketName)
  170. }
  171. // Save the updated lifecycle.
  172. return c.putBucketLifecycle(bucketName, lifecycle)
  173. }
  174. // Saves a new bucket lifecycle.
  175. func (c Client) putBucketLifecycle(bucketName, lifecycle string) error {
  176. // Input validation.
  177. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  178. return err
  179. }
  180. // Get resources properly escaped and lined up before
  181. // using them in http request.
  182. urlValues := make(url.Values)
  183. urlValues.Set("lifecycle", "")
  184. // Content-length is mandatory for put lifecycle request
  185. lifecycleReader := strings.NewReader(lifecycle)
  186. b, err := ioutil.ReadAll(lifecycleReader)
  187. if err != nil {
  188. return err
  189. }
  190. reqMetadata := requestMetadata{
  191. bucketName: bucketName,
  192. queryValues: urlValues,
  193. contentBody: lifecycleReader,
  194. contentLength: int64(len(b)),
  195. contentMD5Base64: sumMD5Base64(b),
  196. }
  197. // Execute PUT to upload a new bucket lifecycle.
  198. resp, err := c.executeMethod(context.Background(), "PUT", reqMetadata)
  199. defer closeResponse(resp)
  200. if err != nil {
  201. return err
  202. }
  203. if resp != nil {
  204. if resp.StatusCode != http.StatusOK {
  205. return httpRespToErrorResponse(resp, bucketName, "")
  206. }
  207. }
  208. return nil
  209. }
  210. // Remove lifecycle from a bucket.
  211. func (c Client) removeBucketLifecycle(bucketName string) error {
  212. // Input validation.
  213. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  214. return err
  215. }
  216. // Get resources properly escaped and lined up before
  217. // using them in http request.
  218. urlValues := make(url.Values)
  219. urlValues.Set("lifecycle", "")
  220. // Execute DELETE on objectName.
  221. resp, err := c.executeMethod(context.Background(), "DELETE", requestMetadata{
  222. bucketName: bucketName,
  223. queryValues: urlValues,
  224. contentSHA256Hex: emptySHA256Hex,
  225. })
  226. defer closeResponse(resp)
  227. if err != nil {
  228. return err
  229. }
  230. return nil
  231. }
  232. // SetBucketNotification saves a new bucket notification.
  233. func (c Client) SetBucketNotification(bucketName string, bucketNotification BucketNotification) error {
  234. // Input validation.
  235. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  236. return err
  237. }
  238. // Get resources properly escaped and lined up before
  239. // using them in http request.
  240. urlValues := make(url.Values)
  241. urlValues.Set("notification", "")
  242. notifBytes, err := xml.Marshal(bucketNotification)
  243. if err != nil {
  244. return err
  245. }
  246. notifBuffer := bytes.NewReader(notifBytes)
  247. reqMetadata := requestMetadata{
  248. bucketName: bucketName,
  249. queryValues: urlValues,
  250. contentBody: notifBuffer,
  251. contentLength: int64(len(notifBytes)),
  252. contentMD5Base64: sumMD5Base64(notifBytes),
  253. contentSHA256Hex: sum256Hex(notifBytes),
  254. }
  255. // Execute PUT to upload a new bucket notification.
  256. resp, err := c.executeMethod(context.Background(), "PUT", reqMetadata)
  257. defer closeResponse(resp)
  258. if err != nil {
  259. return err
  260. }
  261. if resp != nil {
  262. if resp.StatusCode != http.StatusOK {
  263. return httpRespToErrorResponse(resp, bucketName, "")
  264. }
  265. }
  266. return nil
  267. }
  268. // RemoveAllBucketNotification - Remove bucket notification clears all previously specified config
  269. func (c Client) RemoveAllBucketNotification(bucketName string) error {
  270. return c.SetBucketNotification(bucketName, BucketNotification{})
  271. }
  272. // SetBucketPolicy set the access permissions on an existing bucket.
  273. func (c Client) SetBucketAcl(bucketName string, policy AccessControlPolicy) error {
  274. // Input validation.
  275. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  276. return err
  277. }
  278. // Save the updated policies.
  279. return c.putBucketAcl(bucketName, "", policy)
  280. }
  281. // SetBucketPolicy set the access permissions on an existing bucket.
  282. func (c Client) SetObjectAcl(bucketName string, objectName string, policy AccessControlPolicy) error {
  283. // Input validation.
  284. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  285. return err
  286. }
  287. if err := s3utils.CheckValidObjectName(objectName); err != nil {
  288. return err
  289. }
  290. // Save the updated policies.
  291. return c.putBucketAcl(bucketName, objectName, policy)
  292. }
  293. // Saves a new bucket acl.
  294. func (c Client) putBucketAcl(bucketName string, objectName string, acl AccessControlPolicy) error {
  295. // Input validation.
  296. // if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  297. // return err
  298. // }
  299. // Get resources properly escaped and lined up before
  300. // using them in http request.
  301. urlValues := make(url.Values)
  302. urlValues.Set("acl", "")
  303. // Content-length is mandatory for put policy request
  304. aclBytes, err := xml.Marshal(acl)
  305. if err != nil {
  306. return err
  307. }
  308. aclReader := strings.NewReader(xml.Header + string(aclBytes))
  309. b, err := ioutil.ReadAll(aclReader)
  310. if err != nil {
  311. return err
  312. }
  313. reqMetadata := requestMetadata{
  314. bucketName: bucketName,
  315. objectName: objectName,
  316. queryValues: urlValues,
  317. contentBody: aclReader,
  318. contentLength: int64(len(b)),
  319. }
  320. if c.debug {
  321. fmt.Fprintf(os.Stderr, "%s", string(b))
  322. }
  323. // Execute PUT to upload a new bucket policy.
  324. resp, err := c.executeMethod(context.Background(), "PUT", reqMetadata)
  325. defer closeResponse(resp)
  326. if err != nil {
  327. return err
  328. }
  329. if resp != nil {
  330. if resp.StatusCode >= 400 {
  331. return httpRespToErrorResponse(resp, bucketName, "")
  332. }
  333. }
  334. return nil
  335. }
  336. // SetBucketLogging set the logging configuration on an existing bucket.
  337. func (c Client) SetBucketLogging(bucketName string, config BucketLoggingStatus) error {
  338. // Input validation.
  339. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  340. return err
  341. }
  342. // Save the updated policies.
  343. return c.putBucketLogging(bucketName, config)
  344. }
  345. // Saves a new bucket acl.
  346. func (c Client) putBucketLogging(bucketName string, config BucketLoggingStatus) error {
  347. // Input validation.
  348. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  349. return err
  350. }
  351. // Get resources properly escaped and lined up before
  352. // using them in http request.
  353. urlValues := make(url.Values)
  354. urlValues.Set("logging", "")
  355. // Content-length is mandatory for put policy request
  356. loggingBytes, err := xml.Marshal(config)
  357. if err != nil {
  358. return err
  359. }
  360. policyReader := strings.NewReader(xml.Header + string(loggingBytes))
  361. b, err := ioutil.ReadAll(policyReader)
  362. if err != nil {
  363. return err
  364. }
  365. if c.debug {
  366. fmt.Fprintf(os.Stderr, "%s", string(b))
  367. }
  368. reqMetadata := requestMetadata{
  369. bucketName: bucketName,
  370. queryValues: urlValues,
  371. contentBody: policyReader,
  372. contentLength: int64(len(b)),
  373. }
  374. // Execute PUT to upload a new bucket policy.
  375. resp, err := c.executeMethod(context.Background(), "PUT", reqMetadata)
  376. defer closeResponse(resp)
  377. if err != nil {
  378. return err
  379. }
  380. if resp != nil {
  381. if resp.StatusCode != http.StatusNoContent {
  382. return httpRespToErrorResponse(resp, bucketName, "")
  383. }
  384. }
  385. return nil
  386. }