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 }