github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/bucket/lifecycle/rule.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package lifecycle 19 20 import ( 21 "bytes" 22 "encoding/xml" 23 ) 24 25 // Status represents lifecycle configuration status 26 type Status string 27 28 // Supported status types 29 const ( 30 Enabled Status = "Enabled" 31 Disabled Status = "Disabled" 32 ) 33 34 // Rule - a rule for lifecycle configuration. 35 type Rule struct { 36 XMLName xml.Name `xml:"Rule"` 37 ID string `xml:"ID,omitempty"` 38 Status Status `xml:"Status"` 39 Filter Filter `xml:"Filter,omitempty"` 40 Prefix Prefix `xml:"Prefix,omitempty"` 41 Expiration Expiration `xml:"Expiration,omitempty"` 42 Transition Transition `xml:"Transition,omitempty"` 43 // FIXME: add a type to catch unsupported AbortIncompleteMultipartUpload AbortIncompleteMultipartUpload `xml:"AbortIncompleteMultipartUpload,omitempty"` 44 NoncurrentVersionExpiration NoncurrentVersionExpiration `xml:"NoncurrentVersionExpiration,omitempty"` 45 NoncurrentVersionTransition NoncurrentVersionTransition `xml:"NoncurrentVersionTransition,omitempty"` 46 } 47 48 var ( 49 errInvalidRuleID = Errorf("ID length is limited to 255 characters") 50 errEmptyRuleStatus = Errorf("Status should not be empty") 51 errInvalidRuleStatus = Errorf("Status must be set to either Enabled or Disabled") 52 ) 53 54 // validateID - checks if ID is valid or not. 55 func (r Rule) validateID() error { 56 if len(r.ID) > 255 { 57 return errInvalidRuleID 58 } 59 return nil 60 } 61 62 // validateStatus - checks if status is valid or not. 63 func (r Rule) validateStatus() error { 64 // Status can't be empty 65 if len(r.Status) == 0 { 66 return errEmptyRuleStatus 67 } 68 69 // Status must be one of Enabled or Disabled 70 if r.Status != Enabled && r.Status != Disabled { 71 return errInvalidRuleStatus 72 } 73 return nil 74 } 75 76 func (r Rule) validateExpiration() error { 77 return r.Expiration.Validate() 78 } 79 80 func (r Rule) validateNoncurrentExpiration() error { 81 return r.NoncurrentVersionExpiration.Validate() 82 } 83 84 func (r Rule) validatePrefixAndFilter() error { 85 if !r.Prefix.set && r.Filter.IsEmpty() || r.Prefix.set && !r.Filter.IsEmpty() { 86 return errXMLNotWellFormed 87 } 88 if !r.Prefix.set { 89 return r.Filter.Validate() 90 } 91 return nil 92 } 93 94 func (r Rule) validateTransition() error { 95 return r.Transition.Validate() 96 } 97 98 func (r Rule) validateNoncurrentTransition() error { 99 return r.NoncurrentVersionTransition.Validate() 100 } 101 102 // GetPrefix - a rule can either have prefix under <rule></rule>, <filter></filter> 103 // or under <filter><and></and></filter>. This method returns the prefix from the 104 // location where it is available. 105 func (r Rule) GetPrefix() string { 106 if p := r.Prefix.String(); p != "" { 107 return p 108 } 109 if p := r.Filter.Prefix.String(); p != "" { 110 return p 111 } 112 if p := r.Filter.And.Prefix.String(); p != "" { 113 return p 114 } 115 return "" 116 } 117 118 // Tags - a rule can either have tag under <filter></filter> or under 119 // <filter><and></and></filter>. This method returns all the tags from the 120 // rule in the format tag1=value1&tag2=value2 121 func (r Rule) Tags() string { 122 if !r.Filter.Tag.IsEmpty() { 123 return r.Filter.Tag.String() 124 } 125 if len(r.Filter.And.Tags) != 0 { 126 var buf bytes.Buffer 127 for _, t := range r.Filter.And.Tags { 128 if buf.Len() > 0 { 129 buf.WriteString("&") 130 } 131 buf.WriteString(t.String()) 132 } 133 return buf.String() 134 } 135 return "" 136 } 137 138 // Validate - validates the rule element 139 func (r Rule) Validate() error { 140 if err := r.validateID(); err != nil { 141 return err 142 } 143 if err := r.validateStatus(); err != nil { 144 return err 145 } 146 if err := r.validateExpiration(); err != nil { 147 return err 148 } 149 if err := r.validateNoncurrentExpiration(); err != nil { 150 return err 151 } 152 if err := r.validatePrefixAndFilter(); err != nil { 153 return err 154 } 155 if err := r.validateTransition(); err != nil { 156 return err 157 } 158 if err := r.validateNoncurrentTransition(); err != nil { 159 return err 160 } 161 if !r.Expiration.set && !r.Transition.set && !r.NoncurrentVersionExpiration.set && !r.NoncurrentVersionTransition.set { 162 return errXMLNotWellFormed 163 } 164 return nil 165 } 166 167 // CloneNonTransition - returns a clone of the object containing non transition rules 168 func (r Rule) CloneNonTransition() Rule { 169 return Rule{ 170 XMLName: r.XMLName, 171 ID: r.ID, 172 Status: r.Status, 173 Filter: r.Filter, 174 Prefix: r.Prefix, 175 Expiration: r.Expiration, 176 NoncurrentVersionExpiration: r.NoncurrentVersionExpiration, 177 } 178 }