mirror of
https://github.com/minio/mc
synced 2026-08-29 14:37:42 -04:00
182 lines
6.3 KiB
Go
182 lines
6.3 KiB
Go
/*
|
|
* MinIO Client (C) 2020 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 ilm
|
|
|
|
import (
|
|
"errors"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/minio/mc/pkg/probe"
|
|
)
|
|
|
|
// Extracts the tags provided by user. The tagfilter array will be put in lifecycleRule structure.
|
|
func extractILMTags(tagLabelVal string) []LifecycleTag {
|
|
var ilmTagKVList []LifecycleTag
|
|
for _, tag := range strings.Split(tagLabelVal, tagSeperator) {
|
|
if tag == "" {
|
|
// split returns empty for empty tagLabelVal, skip it.
|
|
continue
|
|
}
|
|
lfcTag := LifecycleTag{}
|
|
kvs := strings.SplitN(tag, keyValSeperator, 2)
|
|
if len(kvs) == 2 {
|
|
lfcTag.Key = kvs[0]
|
|
lfcTag.Value = kvs[1]
|
|
} else {
|
|
lfcTag.Key = kvs[0]
|
|
}
|
|
ilmTagKVList = append(ilmTagKVList, lfcTag)
|
|
}
|
|
return ilmTagKVList
|
|
}
|
|
|
|
// Some of these rules are enforced by Amazon S3 standards.
|
|
// For example: Transition has to happen before Expiry.
|
|
// Storage class must be specified if transition date/days is provided.
|
|
func validateTranExpDate(rule LifecycleRule) error {
|
|
expirySet := (rule.Expiration != nil)
|
|
expiryDateSet := expirySet && rule.Expiration.ExpirationDate != nil && !rule.Expiration.ExpirationDate.IsZero()
|
|
expiryDaySet := expirySet && rule.Expiration.ExpirationInDays > 0
|
|
|
|
transitionSet := (rule.Transition != nil)
|
|
transitionDateSet := transitionSet && (rule.Transition.TransitionDate != nil && !rule.Transition.TransitionDate.IsZero())
|
|
transitionDaySet := transitionSet && (rule.Transition.TransitionInDays > 0)
|
|
errMsg := "Error in Transition/Expiration Date/days compatibility. Transition should happen before Expiration"
|
|
if transitionDateSet && expiryDateSet {
|
|
if rule.Expiration.ExpirationDate.Before(*(rule.Transition.TransitionDate)) {
|
|
return errors.New(errMsg)
|
|
}
|
|
}
|
|
if transitionDaySet && expiryDaySet {
|
|
if rule.Transition.TransitionInDays >= rule.Expiration.ExpirationInDays {
|
|
return errors.New(errMsg)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateTranDays(rule LifecycleRule) error {
|
|
transitionSet := (rule.Transition != nil)
|
|
transitionDaySet := transitionSet && (rule.Transition.TransitionInDays > 0)
|
|
if transitionDaySet && rule.Transition.TransitionInDays < 30 && strings.ToLower(rule.Transition.StorageClass) == "standard_ia" {
|
|
return errors.New("Transition Date/Days are less than or equal to 30 when Storage class is STANDARD_IA")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Amazon S3 requires a minimum of one action for a rule to be added.
|
|
func validateRuleAction(rule LifecycleRule) error {
|
|
expirySet := (rule.Expiration != nil)
|
|
transitionSet := (rule.Transition != nil)
|
|
if !expirySet && !transitionSet {
|
|
errMsg := "At least one action (Expiry or Transition) needs to be specified in a rule."
|
|
return errors.New(errMsg)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Check if any date is before than cur date
|
|
func validateTranExpCurdate(rule LifecycleRule) error {
|
|
var err error
|
|
expirySet := (rule.Expiration != nil)
|
|
transitionSet := (rule.Transition != nil)
|
|
transitionDateSet := transitionSet && (rule.Transition.TransitionDate != nil && !rule.Transition.TransitionDate.IsZero())
|
|
expiryDateSet := expirySet && rule.Expiration.ExpirationDate != nil && !rule.Expiration.ExpirationDate.IsZero()
|
|
currentTime := time.Now()
|
|
curTimeStr := currentTime.Format(defaultILMDateFormat)
|
|
currentTime, err = time.Parse(defaultILMDateFormat, curTimeStr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if expirySet && expiryDateSet && rule.Expiration.ExpirationDate.Before(currentTime) {
|
|
err = errors.New("Expiry date falls before or on today's date")
|
|
} else if transitionSet && transitionDateSet && rule.Transition.TransitionDate.Before(currentTime) {
|
|
err = errors.New("Transition date falls before or on today's date")
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Check S3 compatibility for the new rule and some other basic checks.
|
|
func validateILMRule(rule LifecycleRule) *probe.Error {
|
|
if e := validateRuleAction(rule); e != nil {
|
|
return probe.NewError(e)
|
|
}
|
|
if e := validateTranExpCurdate(rule); e != nil {
|
|
return probe.NewError(e)
|
|
}
|
|
if e := validateTranExpDate(rule); e != nil {
|
|
return probe.NewError(e)
|
|
}
|
|
if e := validateTranDays(rule); e != nil {
|
|
return probe.NewError(e)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Returns valid lifecycleTransition to be included in lifecycleRule
|
|
func parseTransition(storageClass, transitionDateStr, transitionDayStr string) (transition LifecycleTransition, err *probe.Error) {
|
|
storageClass = strings.ToUpper(storageClass) // Just-in-case the user has entered lower case characters.
|
|
if storageClass != "" {
|
|
if transitionDateStr != "" {
|
|
transitionDate, e := time.Parse(defaultILMDateFormat, transitionDateStr)
|
|
if e != nil {
|
|
return LifecycleTransition{}, probe.NewError(e)
|
|
}
|
|
transition.TransitionDate = &transitionDate
|
|
} else if transitionDayStr != "" {
|
|
transitionDay, e := strconv.Atoi(transitionDayStr)
|
|
if e != nil {
|
|
return LifecycleTransition{}, probe.NewError(e)
|
|
}
|
|
transition.TransitionInDays = transitionDay
|
|
} else {
|
|
return LifecycleTransition{}, probe.NewError(errors.New("if storage class is set a valid transitionDate or transitionDay must be set"))
|
|
}
|
|
transition.StorageClass = storageClass
|
|
}
|
|
|
|
return transition, nil
|
|
}
|
|
|
|
// Returns lifecycleExpiration to be included in lifecycleRule
|
|
func parseExpiry(expiryDateStr, expiryDayStr string) (lfcExp LifecycleExpiration, err *probe.Error) {
|
|
if expiryDateStr != "" {
|
|
date, e := time.Parse(defaultILMDateFormat, expiryDateStr)
|
|
if e != nil {
|
|
return LifecycleExpiration{}, probe.NewError(e)
|
|
}
|
|
if date.IsZero() {
|
|
return LifecycleExpiration{}, probe.NewError(errors.New("expiration date cannot be set to zero"))
|
|
}
|
|
lfcExp.ExpirationDate = &date
|
|
}
|
|
|
|
if expiryDayStr != "" {
|
|
days, e := strconv.Atoi(expiryDayStr)
|
|
if e != nil {
|
|
return lfcExp, probe.NewError(e)
|
|
}
|
|
if days == 0 {
|
|
return LifecycleExpiration{}, probe.NewError(errors.New("expiration days cannot be set to zero"))
|
|
}
|
|
lfcExp.ExpirationInDays = days
|
|
}
|
|
|
|
return lfcExp, nil
|
|
}
|