storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/bucket/lifecycle/rule.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2019 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  
    17  package lifecycle
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/xml"
    22  
    23  	"github.com/google/uuid"
    24  )
    25  
    26  // Status represents lifecycle configuration status
    27  type Status string
    28  
    29  // Supported status types
    30  const (
    31  	Enabled  Status = "Enabled"
    32  	Disabled Status = "Disabled"
    33  )
    34  
    35  // Rule - a rule for lifecycle configuration.
    36  type Rule struct {
    37  	XMLName    xml.Name   `xml:"Rule"`
    38  	ID         string     `xml:"ID,omitempty"`
    39  	Status     Status     `xml:"Status"`
    40  	Filter     Filter     `xml:"Filter,omitempty"`
    41  	Prefix     Prefix     `xml:"Prefix,omitempty"`
    42  	Expiration Expiration `xml:"Expiration,omitempty"`
    43  	Transition Transition `xml:"Transition,omitempty"`
    44  	// FIXME: add a type to catch unsupported AbortIncompleteMultipartUpload AbortIncompleteMultipartUpload `xml:"AbortIncompleteMultipartUpload,omitempty"`
    45  	NoncurrentVersionExpiration NoncurrentVersionExpiration `xml:"NoncurrentVersionExpiration,omitempty"`
    46  	NoncurrentVersionTransition NoncurrentVersionTransition `xml:"NoncurrentVersionTransition,omitempty"`
    47  }
    48  
    49  var (
    50  	errInvalidRuleID     = Errorf("ID length is limited to 255 characters")
    51  	errEmptyRuleStatus   = Errorf("Status should not be empty")
    52  	errInvalidRuleStatus = Errorf("Status must be set to either Enabled or Disabled")
    53  )
    54  
    55  // generates random UUID
    56  func getNewUUID() (string, error) {
    57  	u, err := uuid.NewRandom()
    58  	if err != nil {
    59  		return "", err
    60  	}
    61  
    62  	return u.String(), nil
    63  }
    64  
    65  // validateID - checks if ID is valid or not.
    66  func (r Rule) validateID() error {
    67  	IDLen := len(string(r.ID))
    68  	// generate new ID when not provided
    69  	// cannot be longer than 255 characters
    70  	if IDLen == 0 {
    71  		if newID, err := getNewUUID(); err == nil {
    72  			r.ID = newID
    73  		} else {
    74  			return err
    75  		}
    76  	} else if IDLen > 255 {
    77  		return errInvalidRuleID
    78  	}
    79  	return nil
    80  }
    81  
    82  // validateStatus - checks if status is valid or not.
    83  func (r Rule) validateStatus() error {
    84  	// Status can't be empty
    85  	if len(r.Status) == 0 {
    86  		return errEmptyRuleStatus
    87  	}
    88  
    89  	// Status must be one of Enabled or Disabled
    90  	if r.Status != Enabled && r.Status != Disabled {
    91  		return errInvalidRuleStatus
    92  	}
    93  	return nil
    94  }
    95  
    96  func (r Rule) validateExpiration() error {
    97  	return r.Expiration.Validate()
    98  }
    99  
   100  func (r Rule) validateNoncurrentExpiration() error {
   101  	return r.NoncurrentVersionExpiration.Validate()
   102  }
   103  
   104  func (r Rule) validatePrefixAndFilter() error {
   105  	if !r.Prefix.set && r.Filter.IsEmpty() || r.Prefix.set && !r.Filter.IsEmpty() {
   106  		return errXMLNotWellFormed
   107  	}
   108  	if !r.Prefix.set {
   109  		return r.Filter.Validate()
   110  	}
   111  	return nil
   112  }
   113  
   114  func (r Rule) validateTransition() error {
   115  	return r.Transition.Validate()
   116  }
   117  
   118  func (r Rule) validateNoncurrentTransition() error {
   119  	return r.NoncurrentVersionTransition.Validate()
   120  }
   121  
   122  // GetPrefix - a rule can either have prefix under <rule></rule>, <filter></filter>
   123  // or under <filter><and></and></filter>. This method returns the prefix from the
   124  // location where it is available.
   125  func (r Rule) GetPrefix() string {
   126  	if p := r.Prefix.String(); p != "" {
   127  		return p
   128  	}
   129  	if p := r.Filter.Prefix.String(); p != "" {
   130  		return p
   131  	}
   132  	if p := r.Filter.And.Prefix.String(); p != "" {
   133  		return p
   134  	}
   135  	return ""
   136  }
   137  
   138  // Tags - a rule can either have tag under <filter></filter> or under
   139  // <filter><and></and></filter>. This method returns all the tags from the
   140  // rule in the format tag1=value1&tag2=value2
   141  func (r Rule) Tags() string {
   142  	if !r.Filter.Tag.IsEmpty() {
   143  		return r.Filter.Tag.String()
   144  	}
   145  	if len(r.Filter.And.Tags) != 0 {
   146  		var buf bytes.Buffer
   147  		for _, t := range r.Filter.And.Tags {
   148  			if buf.Len() > 0 {
   149  				buf.WriteString("&")
   150  			}
   151  			buf.WriteString(t.String())
   152  		}
   153  		return buf.String()
   154  	}
   155  	return ""
   156  }
   157  
   158  // Validate - validates the rule element
   159  func (r Rule) Validate() error {
   160  	if err := r.validateID(); err != nil {
   161  		return err
   162  	}
   163  	if err := r.validateStatus(); err != nil {
   164  		return err
   165  	}
   166  	if err := r.validateExpiration(); err != nil {
   167  		return err
   168  	}
   169  	if err := r.validateNoncurrentExpiration(); err != nil {
   170  		return err
   171  	}
   172  	if err := r.validatePrefixAndFilter(); err != nil {
   173  		return err
   174  	}
   175  	if err := r.validateTransition(); err != nil {
   176  		return err
   177  	}
   178  	if err := r.validateNoncurrentTransition(); err != nil {
   179  		return err
   180  	}
   181  	if !r.Expiration.set && !r.Transition.set && !r.NoncurrentVersionExpiration.set && !r.NoncurrentVersionTransition.set {
   182  		return errXMLNotWellFormed
   183  	}
   184  	return nil
   185  }