|
|
|
/*
|
|
|
|
* MinIO Cloud Storage, (C) 2016, 2017 MinIO, Inc.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/xml"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
// RetentionMode - object retention mode.
|
|
|
|
type RetentionMode string
|
|
|
|
|
|
|
|
const (
|
|
|
|
// Governance - governance mode.
|
|
|
|
Governance RetentionMode = "GOVERNANCE"
|
|
|
|
|
|
|
|
// Compliance - compliance mode.
|
|
|
|
Compliance RetentionMode = "COMPLIANCE"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Retention - bucket level retention configuration.
|
|
|
|
type Retention struct {
|
|
|
|
Mode RetentionMode
|
|
|
|
Validity time.Duration
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsEmpty - returns whether retention is empty or not.
|
|
|
|
func (r Retention) IsEmpty() bool {
|
|
|
|
return r.Mode == "" || r.Validity == 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// Retain - check whether given date is retainable by validity time.
|
|
|
|
func (r Retention) Retain(created time.Time) bool {
|
|
|
|
return globalWORMEnabled || created.Add(r.Validity).After(time.Now())
|
|
|
|
}
|
|
|
|
|
|
|
|
// BucketRetentionConfig - map of bucket and retention configuration.
|
|
|
|
type BucketRetentionConfig struct {
|
|
|
|
sync.RWMutex
|
|
|
|
retentionMap map[string]Retention
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set - set retention configuration.
|
|
|
|
func (config *BucketRetentionConfig) Set(bucketName string, retention Retention) {
|
|
|
|
config.Lock()
|
|
|
|
config.retentionMap[bucketName] = retention
|
|
|
|
config.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get - Get retention configuration.
|
|
|
|
func (config *BucketRetentionConfig) Get(bucketName string) (r Retention, ok bool) {
|
|
|
|
config.RLock()
|
|
|
|
defer config.RUnlock()
|
|
|
|
|
|
|
|
r, ok = config.retentionMap[bucketName]
|
|
|
|
return r, ok
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete - delete retention configuration.
|
|
|
|
func (config *BucketRetentionConfig) Delete(bucketName string) {
|
|
|
|
config.Lock()
|
|
|
|
delete(config.retentionMap, bucketName)
|
|
|
|
config.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func newBucketRetentionConfig() *BucketRetentionConfig {
|
|
|
|
return &BucketRetentionConfig{
|
|
|
|
retentionMap: map[string]Retention{},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// DefaultRetention - default retention configuration.
|
|
|
|
type DefaultRetention struct {
|
|
|
|
XMLName xml.Name `xml:"DefaultRetention"`
|
|
|
|
Mode RetentionMode `xml:"Mode"`
|
|
|
|
Days *uint64 `xml:"Days"`
|
|
|
|
Years *uint64 `xml:"Years"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Maximum support retention days and years supported by AWS S3.
|
|
|
|
const (
|
|
|
|
// This tested by using `mc lock` command
|
|
|
|
maximumRetentionDays = 36500
|
|
|
|
maximumRetentionYears = 100
|
|
|
|
)
|
|
|
|
|
|
|
|
// UnmarshalXML - decodes XML data.
|
|
|
|
func (dr *DefaultRetention) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
|
|
|
// Make subtype to avoid recursive UnmarshalXML().
|
|
|
|
type defaultRetention DefaultRetention
|
|
|
|
retention := defaultRetention{}
|
|
|
|
|
|
|
|
if err := d.DecodeElement(&retention, &start); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch string(retention.Mode) {
|
|
|
|
case "GOVERNANCE", "COMPLIANCE":
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("unknown retention mode %v", retention.Mode)
|
|
|
|
}
|
|
|
|
|
|
|
|
if retention.Days == nil && retention.Years == nil {
|
|
|
|
return fmt.Errorf("either Days or Years must be specified")
|
|
|
|
}
|
|
|
|
|
|
|
|
if retention.Days != nil && retention.Years != nil {
|
|
|
|
return fmt.Errorf("either Days or Years must be specified, not both")
|
|
|
|
}
|
|
|
|
|
|
|
|
if retention.Days != nil {
|
|
|
|
if *retention.Days == 0 {
|
|
|
|
return fmt.Errorf("Default retention period must be a positive integer value for 'Days'")
|
|
|
|
}
|
|
|
|
if *retention.Days > maximumRetentionDays {
|
|
|
|
return fmt.Errorf("Default retention period too large for 'Days' %w", *retention.Days)
|
|
|
|
}
|
|
|
|
} else if *retention.Years == 0 {
|
|
|
|
return fmt.Errorf("Default retention period must be a positive integer value for 'Years'")
|
|
|
|
} else if *retention.Years > maximumRetentionYears {
|
|
|
|
return fmt.Errorf("Default retention period too large for 'Years' %w", *retention.Years)
|
|
|
|
}
|
|
|
|
|
|
|
|
*dr = DefaultRetention(retention)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ObjectLockConfig - object lock configuration specified in
|
|
|
|
// https://docs.aws.amazon.com/AmazonS3/latest/API/Type_API_ObjectLockConfiguration.html
|
|
|
|
type ObjectLockConfig struct {
|
|
|
|
XMLNS string `xml:"xmlns,attr,omitempty"`
|
|
|
|
XMLName xml.Name `xml:"ObjectLockConfiguration"`
|
|
|
|
ObjectLockEnabled string `xml:"ObjectLockEnabled"`
|
|
|
|
Rule *struct {
|
|
|
|
DefaultRetention DefaultRetention `xml:"DefaultRetention"`
|
|
|
|
} `xml:"Rule,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalXML - decodes XML data.
|
|
|
|
func (config *ObjectLockConfig) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
|
|
|
// Make subtype to avoid recursive UnmarshalXML().
|
|
|
|
type objectLockConfig ObjectLockConfig
|
|
|
|
parsedConfig := objectLockConfig{}
|
|
|
|
|
|
|
|
if err := d.DecodeElement(&parsedConfig, &start); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if parsedConfig.ObjectLockEnabled != "Enabled" {
|
|
|
|
return fmt.Errorf("only 'Enabled' value is allowd to ObjectLockEnabled element")
|
|
|
|
}
|
|
|
|
|
|
|
|
*config = ObjectLockConfig(parsedConfig)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToRetention - convert to Retention type.
|
|
|
|
func (config *ObjectLockConfig) ToRetention() (r Retention) {
|
|
|
|
if config.Rule != nil {
|
|
|
|
r.Mode = config.Rule.DefaultRetention.Mode
|
|
|
|
utcNow := time.Now().UTC()
|
|
|
|
if config.Rule.DefaultRetention.Days != nil {
|
|
|
|
r.Validity = utcNow.AddDate(0, 0, int(*config.Rule.DefaultRetention.Days)).Sub(utcNow)
|
|
|
|
} else {
|
|
|
|
r.Validity = utcNow.AddDate(int(*config.Rule.DefaultRetention.Years), 0, 0).Sub(utcNow)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseObjectLockConfig(reader io.Reader) (*ObjectLockConfig, error) {
|
|
|
|
config := ObjectLockConfig{}
|
|
|
|
if err := xml.NewDecoder(reader).Decode(&config); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &config, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func newObjectLockConfig() *ObjectLockConfig {
|
|
|
|
return &ObjectLockConfig{
|
|
|
|
ObjectLockEnabled: "Enabled",
|
|
|
|
}
|
|
|
|
}
|