gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

lifecycle.go (16024B)


      1 /*
      2  * MinIO Go Library for Amazon S3 Compatible Cloud Storage
      3  * Copyright 2020 MinIO, Inc.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 // Package lifecycle contains all the lifecycle related data types and marshallers.
     19 package lifecycle
     20 
     21 import (
     22 	"encoding/json"
     23 	"encoding/xml"
     24 	"errors"
     25 	"time"
     26 )
     27 
     28 var errMissingStorageClass = errors.New("storage-class cannot be empty")
     29 
     30 // AbortIncompleteMultipartUpload structure, not supported yet on MinIO
     31 type AbortIncompleteMultipartUpload struct {
     32 	XMLName             xml.Name       `xml:"AbortIncompleteMultipartUpload,omitempty"  json:"-"`
     33 	DaysAfterInitiation ExpirationDays `xml:"DaysAfterInitiation,omitempty" json:"DaysAfterInitiation,omitempty"`
     34 }
     35 
     36 // IsDaysNull returns true if days field is null
     37 func (n AbortIncompleteMultipartUpload) IsDaysNull() bool {
     38 	return n.DaysAfterInitiation == ExpirationDays(0)
     39 }
     40 
     41 // MarshalXML if days after initiation is set to non-zero value
     42 func (n AbortIncompleteMultipartUpload) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
     43 	if n.IsDaysNull() {
     44 		return nil
     45 	}
     46 	type abortIncompleteMultipartUploadWrapper AbortIncompleteMultipartUpload
     47 	return e.EncodeElement(abortIncompleteMultipartUploadWrapper(n), start)
     48 }
     49 
     50 // NoncurrentVersionExpiration - Specifies when noncurrent object versions expire.
     51 // Upon expiration, server permanently deletes the noncurrent object versions.
     52 // Set this lifecycle configuration action on a bucket that has versioning enabled
     53 // (or suspended) to request server delete noncurrent object versions at a
     54 // specific period in the object's lifetime.
     55 type NoncurrentVersionExpiration struct {
     56 	XMLName                 xml.Name       `xml:"NoncurrentVersionExpiration" json:"-"`
     57 	NoncurrentDays          ExpirationDays `xml:"NoncurrentDays,omitempty" json:"NoncurrentDays,omitempty"`
     58 	NewerNoncurrentVersions int            `xml:"NewerNoncurrentVersions,omitempty" json:"NewerNoncurrentVersions,omitempty"`
     59 }
     60 
     61 // MarshalXML if n is non-empty, i.e has a non-zero NoncurrentDays or NewerNoncurrentVersions.
     62 func (n NoncurrentVersionExpiration) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
     63 	if n.isNull() {
     64 		return nil
     65 	}
     66 	type noncurrentVersionExpirationWrapper NoncurrentVersionExpiration
     67 	return e.EncodeElement(noncurrentVersionExpirationWrapper(n), start)
     68 }
     69 
     70 // IsDaysNull returns true if days field is null
     71 func (n NoncurrentVersionExpiration) IsDaysNull() bool {
     72 	return n.NoncurrentDays == ExpirationDays(0)
     73 }
     74 
     75 func (n NoncurrentVersionExpiration) isNull() bool {
     76 	return n.IsDaysNull() && n.NewerNoncurrentVersions == 0
     77 }
     78 
     79 // NoncurrentVersionTransition structure, set this action to request server to
     80 // transition noncurrent object versions to different set storage classes
     81 // at a specific period in the object's lifetime.
     82 type NoncurrentVersionTransition struct {
     83 	XMLName                 xml.Name       `xml:"NoncurrentVersionTransition,omitempty"  json:"-"`
     84 	StorageClass            string         `xml:"StorageClass,omitempty" json:"StorageClass,omitempty"`
     85 	NoncurrentDays          ExpirationDays `xml:"NoncurrentDays" json:"NoncurrentDays"`
     86 	NewerNoncurrentVersions int            `xml:"NewerNoncurrentVersions,omitempty" json:"NewerNoncurrentVersions,omitempty"`
     87 }
     88 
     89 // IsDaysNull returns true if days field is null
     90 func (n NoncurrentVersionTransition) IsDaysNull() bool {
     91 	return n.NoncurrentDays == ExpirationDays(0)
     92 }
     93 
     94 // IsStorageClassEmpty returns true if storage class field is empty
     95 func (n NoncurrentVersionTransition) IsStorageClassEmpty() bool {
     96 	return n.StorageClass == ""
     97 }
     98 
     99 func (n NoncurrentVersionTransition) isNull() bool {
    100 	return n.StorageClass == ""
    101 }
    102 
    103 // UnmarshalJSON implements NoncurrentVersionTransition JSONify
    104 func (n *NoncurrentVersionTransition) UnmarshalJSON(b []byte) error {
    105 	type noncurrentVersionTransition NoncurrentVersionTransition
    106 	var nt noncurrentVersionTransition
    107 	err := json.Unmarshal(b, &nt)
    108 	if err != nil {
    109 		return err
    110 	}
    111 
    112 	if nt.StorageClass == "" {
    113 		return errMissingStorageClass
    114 	}
    115 	*n = NoncurrentVersionTransition(nt)
    116 	return nil
    117 }
    118 
    119 // MarshalXML is extended to leave out
    120 // <NoncurrentVersionTransition></NoncurrentVersionTransition> tags
    121 func (n NoncurrentVersionTransition) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
    122 	if n.isNull() {
    123 		return nil
    124 	}
    125 	type noncurrentVersionTransitionWrapper NoncurrentVersionTransition
    126 	return e.EncodeElement(noncurrentVersionTransitionWrapper(n), start)
    127 }
    128 
    129 // Tag structure key/value pair representing an object tag to apply lifecycle configuration
    130 type Tag struct {
    131 	XMLName xml.Name `xml:"Tag,omitempty" json:"-"`
    132 	Key     string   `xml:"Key,omitempty" json:"Key,omitempty"`
    133 	Value   string   `xml:"Value,omitempty" json:"Value,omitempty"`
    134 }
    135 
    136 // IsEmpty returns whether this tag is empty or not.
    137 func (tag Tag) IsEmpty() bool {
    138 	return tag.Key == ""
    139 }
    140 
    141 // Transition structure - transition details of lifecycle configuration
    142 type Transition struct {
    143 	XMLName      xml.Name       `xml:"Transition" json:"-"`
    144 	Date         ExpirationDate `xml:"Date,omitempty" json:"Date,omitempty"`
    145 	StorageClass string         `xml:"StorageClass,omitempty" json:"StorageClass,omitempty"`
    146 	Days         ExpirationDays `xml:"Days" json:"Days"`
    147 }
    148 
    149 // UnmarshalJSON returns an error if storage-class is empty.
    150 func (t *Transition) UnmarshalJSON(b []byte) error {
    151 	type transition Transition
    152 	var tr transition
    153 	err := json.Unmarshal(b, &tr)
    154 	if err != nil {
    155 		return err
    156 	}
    157 
    158 	if tr.StorageClass == "" {
    159 		return errMissingStorageClass
    160 	}
    161 	*t = Transition(tr)
    162 	return nil
    163 }
    164 
    165 // MarshalJSON customizes json encoding by omitting empty values
    166 func (t Transition) MarshalJSON() ([]byte, error) {
    167 	if t.IsNull() {
    168 		return nil, nil
    169 	}
    170 	type transition struct {
    171 		Date         *ExpirationDate `json:"Date,omitempty"`
    172 		StorageClass string          `json:"StorageClass,omitempty"`
    173 		Days         *ExpirationDays `json:"Days"`
    174 	}
    175 
    176 	newt := transition{
    177 		StorageClass: t.StorageClass,
    178 	}
    179 
    180 	if !t.IsDateNull() {
    181 		newt.Date = &t.Date
    182 	} else {
    183 		newt.Days = &t.Days
    184 	}
    185 	return json.Marshal(newt)
    186 }
    187 
    188 // IsDaysNull returns true if days field is null
    189 func (t Transition) IsDaysNull() bool {
    190 	return t.Days == ExpirationDays(0)
    191 }
    192 
    193 // IsDateNull returns true if date field is null
    194 func (t Transition) IsDateNull() bool {
    195 	return t.Date.Time.IsZero()
    196 }
    197 
    198 // IsNull returns true if no storage-class is set.
    199 func (t Transition) IsNull() bool {
    200 	return t.StorageClass == ""
    201 }
    202 
    203 // MarshalXML is transition is non null
    204 func (t Transition) MarshalXML(en *xml.Encoder, startElement xml.StartElement) error {
    205 	if t.IsNull() {
    206 		return nil
    207 	}
    208 	type transitionWrapper Transition
    209 	return en.EncodeElement(transitionWrapper(t), startElement)
    210 }
    211 
    212 // And And Rule for LifecycleTag, to be used in LifecycleRuleFilter
    213 type And struct {
    214 	XMLName xml.Name `xml:"And" json:"-"`
    215 	Prefix  string   `xml:"Prefix" json:"Prefix,omitempty"`
    216 	Tags    []Tag    `xml:"Tag" json:"Tags,omitempty"`
    217 }
    218 
    219 // IsEmpty returns true if Tags field is null
    220 func (a And) IsEmpty() bool {
    221 	return len(a.Tags) == 0 && a.Prefix == ""
    222 }
    223 
    224 // Filter will be used in selecting rule(s) for lifecycle configuration
    225 type Filter struct {
    226 	XMLName xml.Name `xml:"Filter" json:"-"`
    227 	And     And      `xml:"And,omitempty" json:"And,omitempty"`
    228 	Prefix  string   `xml:"Prefix,omitempty" json:"Prefix,omitempty"`
    229 	Tag     Tag      `xml:"Tag,omitempty" json:"Tag,omitempty"`
    230 }
    231 
    232 // IsNull returns true if all Filter fields are empty.
    233 func (f Filter) IsNull() bool {
    234 	return f.Tag.IsEmpty() && f.And.IsEmpty() && f.Prefix == ""
    235 }
    236 
    237 // MarshalJSON customizes json encoding by removing empty values.
    238 func (f Filter) MarshalJSON() ([]byte, error) {
    239 	type filter struct {
    240 		And    *And   `json:"And,omitempty"`
    241 		Prefix string `json:"Prefix,omitempty"`
    242 		Tag    *Tag   `json:"Tag,omitempty"`
    243 	}
    244 
    245 	newf := filter{
    246 		Prefix: f.Prefix,
    247 	}
    248 	if !f.Tag.IsEmpty() {
    249 		newf.Tag = &f.Tag
    250 	}
    251 	if !f.And.IsEmpty() {
    252 		newf.And = &f.And
    253 	}
    254 	return json.Marshal(newf)
    255 }
    256 
    257 // MarshalXML - produces the xml representation of the Filter struct
    258 // only one of Prefix, And and Tag should be present in the output.
    259 func (f Filter) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
    260 	if err := e.EncodeToken(start); err != nil {
    261 		return err
    262 	}
    263 
    264 	switch {
    265 	case !f.And.IsEmpty():
    266 		if err := e.EncodeElement(f.And, xml.StartElement{Name: xml.Name{Local: "And"}}); err != nil {
    267 			return err
    268 		}
    269 	case !f.Tag.IsEmpty():
    270 		if err := e.EncodeElement(f.Tag, xml.StartElement{Name: xml.Name{Local: "Tag"}}); err != nil {
    271 			return err
    272 		}
    273 	default:
    274 		// Always print Prefix field when both And & Tag are empty
    275 		if err := e.EncodeElement(f.Prefix, xml.StartElement{Name: xml.Name{Local: "Prefix"}}); err != nil {
    276 			return err
    277 		}
    278 	}
    279 
    280 	return e.EncodeToken(xml.EndElement{Name: start.Name})
    281 }
    282 
    283 // ExpirationDays is a type alias to unmarshal Days in Expiration
    284 type ExpirationDays int
    285 
    286 // MarshalXML encodes number of days to expire if it is non-zero and
    287 // encodes empty string otherwise
    288 func (eDays ExpirationDays) MarshalXML(e *xml.Encoder, startElement xml.StartElement) error {
    289 	if eDays == 0 {
    290 		return nil
    291 	}
    292 	return e.EncodeElement(int(eDays), startElement)
    293 }
    294 
    295 // ExpirationDate is a embedded type containing time.Time to unmarshal
    296 // Date in Expiration
    297 type ExpirationDate struct {
    298 	time.Time
    299 }
    300 
    301 // MarshalXML encodes expiration date if it is non-zero and encodes
    302 // empty string otherwise
    303 func (eDate ExpirationDate) MarshalXML(e *xml.Encoder, startElement xml.StartElement) error {
    304 	if eDate.Time.IsZero() {
    305 		return nil
    306 	}
    307 	return e.EncodeElement(eDate.Format(time.RFC3339), startElement)
    308 }
    309 
    310 // ExpireDeleteMarker represents value of ExpiredObjectDeleteMarker field in Expiration XML element.
    311 type ExpireDeleteMarker bool
    312 
    313 // MarshalXML encodes delete marker boolean into an XML form.
    314 func (b ExpireDeleteMarker) MarshalXML(e *xml.Encoder, startElement xml.StartElement) error {
    315 	if !b {
    316 		return nil
    317 	}
    318 	type expireDeleteMarkerWrapper ExpireDeleteMarker
    319 	return e.EncodeElement(expireDeleteMarkerWrapper(b), startElement)
    320 }
    321 
    322 // IsEnabled returns true if the auto delete-marker expiration is enabled
    323 func (b ExpireDeleteMarker) IsEnabled() bool {
    324 	return bool(b)
    325 }
    326 
    327 // Expiration structure - expiration details of lifecycle configuration
    328 type Expiration struct {
    329 	XMLName      xml.Name           `xml:"Expiration,omitempty" json:"-"`
    330 	Date         ExpirationDate     `xml:"Date,omitempty" json:"Date,omitempty"`
    331 	Days         ExpirationDays     `xml:"Days,omitempty" json:"Days,omitempty"`
    332 	DeleteMarker ExpireDeleteMarker `xml:"ExpiredObjectDeleteMarker,omitempty" json:"ExpiredObjectDeleteMarker,omitempty"`
    333 }
    334 
    335 // MarshalJSON customizes json encoding by removing empty day/date specification.
    336 func (e Expiration) MarshalJSON() ([]byte, error) {
    337 	type expiration struct {
    338 		Date         *ExpirationDate    `json:"Date,omitempty"`
    339 		Days         *ExpirationDays    `json:"Days,omitempty"`
    340 		DeleteMarker ExpireDeleteMarker `json:"ExpiredObjectDeleteMarker,omitempty"`
    341 	}
    342 
    343 	newexp := expiration{
    344 		DeleteMarker: e.DeleteMarker,
    345 	}
    346 	if !e.IsDaysNull() {
    347 		newexp.Days = &e.Days
    348 	}
    349 	if !e.IsDateNull() {
    350 		newexp.Date = &e.Date
    351 	}
    352 	return json.Marshal(newexp)
    353 }
    354 
    355 // IsDaysNull returns true if days field is null
    356 func (e Expiration) IsDaysNull() bool {
    357 	return e.Days == ExpirationDays(0)
    358 }
    359 
    360 // IsDateNull returns true if date field is null
    361 func (e Expiration) IsDateNull() bool {
    362 	return e.Date.Time.IsZero()
    363 }
    364 
    365 // IsDeleteMarkerExpirationEnabled returns true if the auto-expiration of delete marker is enabled
    366 func (e Expiration) IsDeleteMarkerExpirationEnabled() bool {
    367 	return e.DeleteMarker.IsEnabled()
    368 }
    369 
    370 // IsNull returns true if both date and days fields are null
    371 func (e Expiration) IsNull() bool {
    372 	return e.IsDaysNull() && e.IsDateNull() && !e.IsDeleteMarkerExpirationEnabled()
    373 }
    374 
    375 // MarshalXML is expiration is non null
    376 func (e Expiration) MarshalXML(en *xml.Encoder, startElement xml.StartElement) error {
    377 	if e.IsNull() {
    378 		return nil
    379 	}
    380 	type expirationWrapper Expiration
    381 	return en.EncodeElement(expirationWrapper(e), startElement)
    382 }
    383 
    384 // MarshalJSON customizes json encoding by omitting empty values
    385 func (r Rule) MarshalJSON() ([]byte, error) {
    386 	type rule struct {
    387 		AbortIncompleteMultipartUpload *AbortIncompleteMultipartUpload `json:"AbortIncompleteMultipartUpload,omitempty"`
    388 		Expiration                     *Expiration                     `json:"Expiration,omitempty"`
    389 		ID                             string                          `json:"ID"`
    390 		RuleFilter                     *Filter                         `json:"Filter,omitempty"`
    391 		NoncurrentVersionExpiration    *NoncurrentVersionExpiration    `json:"NoncurrentVersionExpiration,omitempty"`
    392 		NoncurrentVersionTransition    *NoncurrentVersionTransition    `json:"NoncurrentVersionTransition,omitempty"`
    393 		Prefix                         string                          `json:"Prefix,omitempty"`
    394 		Status                         string                          `json:"Status"`
    395 		Transition                     *Transition                     `json:"Transition,omitempty"`
    396 	}
    397 	newr := rule{
    398 		Prefix: r.Prefix,
    399 		Status: r.Status,
    400 		ID:     r.ID,
    401 	}
    402 
    403 	if !r.RuleFilter.IsNull() {
    404 		newr.RuleFilter = &r.RuleFilter
    405 	}
    406 	if !r.AbortIncompleteMultipartUpload.IsDaysNull() {
    407 		newr.AbortIncompleteMultipartUpload = &r.AbortIncompleteMultipartUpload
    408 	}
    409 	if !r.Expiration.IsNull() {
    410 		newr.Expiration = &r.Expiration
    411 	}
    412 	if !r.Transition.IsNull() {
    413 		newr.Transition = &r.Transition
    414 	}
    415 	if !r.NoncurrentVersionExpiration.isNull() {
    416 		newr.NoncurrentVersionExpiration = &r.NoncurrentVersionExpiration
    417 	}
    418 	if !r.NoncurrentVersionTransition.isNull() {
    419 		newr.NoncurrentVersionTransition = &r.NoncurrentVersionTransition
    420 	}
    421 
    422 	return json.Marshal(newr)
    423 }
    424 
    425 // Rule represents a single rule in lifecycle configuration
    426 type Rule struct {
    427 	XMLName                        xml.Name                       `xml:"Rule,omitempty" json:"-"`
    428 	AbortIncompleteMultipartUpload AbortIncompleteMultipartUpload `xml:"AbortIncompleteMultipartUpload,omitempty" json:"AbortIncompleteMultipartUpload,omitempty"`
    429 	Expiration                     Expiration                     `xml:"Expiration,omitempty" json:"Expiration,omitempty"`
    430 	ID                             string                         `xml:"ID" json:"ID"`
    431 	RuleFilter                     Filter                         `xml:"Filter,omitempty" json:"Filter,omitempty"`
    432 	NoncurrentVersionExpiration    NoncurrentVersionExpiration    `xml:"NoncurrentVersionExpiration,omitempty"  json:"NoncurrentVersionExpiration,omitempty"`
    433 	NoncurrentVersionTransition    NoncurrentVersionTransition    `xml:"NoncurrentVersionTransition,omitempty" json:"NoncurrentVersionTransition,omitempty"`
    434 	Prefix                         string                         `xml:"Prefix,omitempty" json:"Prefix,omitempty"`
    435 	Status                         string                         `xml:"Status" json:"Status"`
    436 	Transition                     Transition                     `xml:"Transition,omitempty" json:"Transition,omitempty"`
    437 }
    438 
    439 // Configuration is a collection of Rule objects.
    440 type Configuration struct {
    441 	XMLName xml.Name `xml:"LifecycleConfiguration,omitempty" json:"-"`
    442 	Rules   []Rule   `xml:"Rule"`
    443 }
    444 
    445 // Empty check if lifecycle configuration is empty
    446 func (c *Configuration) Empty() bool {
    447 	if c == nil {
    448 		return true
    449 	}
    450 	return len(c.Rules) == 0
    451 }
    452 
    453 // NewConfiguration initializes a fresh lifecycle configuration
    454 // for manipulation, such as setting and removing lifecycle rules
    455 // and filters.
    456 func NewConfiguration() *Configuration {
    457 	return &Configuration{}
    458 }