1/*
2 * MinIO Go Library for Amazon S3 Compatible Cloud Storage
3 * Copyright 2020 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
18package minio
19
20import (
21	"bytes"
22	"context"
23	"encoding/xml"
24	"io/ioutil"
25	"net/http"
26	"net/url"
27
28	"github.com/minio/minio-go/v7/pkg/lifecycle"
29	"github.com/minio/minio-go/v7/pkg/s3utils"
30)
31
32// SetBucketLifecycle set the lifecycle on an existing bucket.
33func (c Client) SetBucketLifecycle(ctx context.Context, bucketName string, config *lifecycle.Configuration) error {
34	// Input validation.
35	if err := s3utils.CheckValidBucketName(bucketName); err != nil {
36		return err
37	}
38
39	// If lifecycle is empty then delete it.
40	if config.Empty() {
41		return c.removeBucketLifecycle(ctx, bucketName)
42	}
43
44	buf, err := xml.Marshal(config)
45	if err != nil {
46		return err
47	}
48
49	// Save the updated lifecycle.
50	return c.putBucketLifecycle(ctx, bucketName, buf)
51}
52
53// Saves a new bucket lifecycle.
54func (c Client) putBucketLifecycle(ctx context.Context, bucketName string, buf []byte) error {
55	// Get resources properly escaped and lined up before
56	// using them in http request.
57	urlValues := make(url.Values)
58	urlValues.Set("lifecycle", "")
59
60	// Content-length is mandatory for put lifecycle request
61	reqMetadata := requestMetadata{
62		bucketName:       bucketName,
63		queryValues:      urlValues,
64		contentBody:      bytes.NewReader(buf),
65		contentLength:    int64(len(buf)),
66		contentMD5Base64: sumMD5Base64(buf),
67	}
68
69	// Execute PUT to upload a new bucket lifecycle.
70	resp, err := c.executeMethod(ctx, http.MethodPut, reqMetadata)
71	defer closeResponse(resp)
72	if err != nil {
73		return err
74	}
75	if resp != nil {
76		if resp.StatusCode != http.StatusOK {
77			return httpRespToErrorResponse(resp, bucketName, "")
78		}
79	}
80	return nil
81}
82
83// Remove lifecycle from a bucket.
84func (c Client) removeBucketLifecycle(ctx context.Context, bucketName string) error {
85	// Get resources properly escaped and lined up before
86	// using them in http request.
87	urlValues := make(url.Values)
88	urlValues.Set("lifecycle", "")
89
90	// Execute DELETE on objectName.
91	resp, err := c.executeMethod(ctx, http.MethodDelete, requestMetadata{
92		bucketName:       bucketName,
93		queryValues:      urlValues,
94		contentSHA256Hex: emptySHA256Hex,
95	})
96	defer closeResponse(resp)
97	if err != nil {
98		return err
99	}
100	return nil
101}
102
103// GetBucketLifecycle fetch bucket lifecycle configuration
104func (c Client) GetBucketLifecycle(ctx context.Context, bucketName string) (*lifecycle.Configuration, error) {
105	// Input validation.
106	if err := s3utils.CheckValidBucketName(bucketName); err != nil {
107		return nil, err
108	}
109
110	bucketLifecycle, err := c.getBucketLifecycle(ctx, bucketName)
111	if err != nil {
112		return nil, err
113	}
114
115	config := lifecycle.NewConfiguration()
116	if err = xml.Unmarshal(bucketLifecycle, config); err != nil {
117		return nil, err
118	}
119	return config, nil
120}
121
122// Request server for current bucket lifecycle.
123func (c Client) getBucketLifecycle(ctx context.Context, bucketName string) ([]byte, error) {
124	// Get resources properly escaped and lined up before
125	// using them in http request.
126	urlValues := make(url.Values)
127	urlValues.Set("lifecycle", "")
128
129	// Execute GET on bucket to get lifecycle.
130	resp, err := c.executeMethod(ctx, http.MethodGet, requestMetadata{
131		bucketName:  bucketName,
132		queryValues: urlValues,
133	})
134
135	defer closeResponse(resp)
136	if err != nil {
137		return nil, err
138	}
139
140	if resp != nil {
141		if resp.StatusCode != http.StatusOK {
142			return nil, httpRespToErrorResponse(resp, bucketName, "")
143		}
144	}
145
146	return ioutil.ReadAll(resp.Body)
147}
148