storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/bucket/lifecycle/transition.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  	"encoding/xml"
    21  	"time"
    22  )
    23  
    24  var (
    25  	errTransitionInvalidDays     = Errorf("Days must be 0 or greater when used with Transition")
    26  	errTransitionInvalidDate     = Errorf("Date must be provided in ISO 8601 format")
    27  	errTransitionInvalid         = Errorf("Exactly one of Days (0 or greater) or Date (positive ISO 8601 format) should be present inside Expiration.")
    28  	errTransitionDateNotMidnight = Errorf("'Date' must be at midnight GMT")
    29  )
    30  
    31  // TransitionDate is a embedded type containing time.Time to unmarshal
    32  // Date in Transition
    33  type TransitionDate struct {
    34  	time.Time
    35  }
    36  
    37  // UnmarshalXML parses date from Transition and validates date format
    38  func (tDate *TransitionDate) UnmarshalXML(d *xml.Decoder, startElement xml.StartElement) error {
    39  	var dateStr string
    40  	err := d.DecodeElement(&dateStr, &startElement)
    41  	if err != nil {
    42  		return err
    43  	}
    44  	// While AWS documentation mentions that the date specified
    45  	// must be present in ISO 8601 format, in reality they allow
    46  	// users to provide RFC 3339 compliant dates.
    47  	trnDate, err := time.Parse(time.RFC3339, dateStr)
    48  	if err != nil {
    49  		return errTransitionInvalidDate
    50  	}
    51  	// Allow only date timestamp specifying midnight GMT
    52  	hr, min, sec := trnDate.Clock()
    53  	nsec := trnDate.Nanosecond()
    54  	loc := trnDate.Location()
    55  	if !(hr == 0 && min == 0 && sec == 0 && nsec == 0 && loc.String() == time.UTC.String()) {
    56  		return errTransitionDateNotMidnight
    57  	}
    58  
    59  	*tDate = TransitionDate{trnDate}
    60  	return nil
    61  }
    62  
    63  // MarshalXML encodes expiration date if it is non-zero and encodes
    64  // empty string otherwise
    65  func (tDate TransitionDate) MarshalXML(e *xml.Encoder, startElement xml.StartElement) error {
    66  	if tDate.Time.IsZero() {
    67  		return nil
    68  	}
    69  	return e.EncodeElement(tDate.Format(time.RFC3339), startElement)
    70  }
    71  
    72  // TransitionDays is a type alias to unmarshal Days in Transition
    73  type TransitionDays int
    74  
    75  // UnmarshalXML parses number of days from Transition and validates if
    76  // >= 0
    77  func (tDays *TransitionDays) UnmarshalXML(d *xml.Decoder, startElement xml.StartElement) error {
    78  	var numDays int
    79  	err := d.DecodeElement(&numDays, &startElement)
    80  	if err != nil {
    81  		return err
    82  	}
    83  	if numDays < 0 {
    84  		return errTransitionInvalidDays
    85  	}
    86  	*tDays = TransitionDays(numDays)
    87  	return nil
    88  }
    89  
    90  // MarshalXML encodes number of days to expire if it is non-zero and
    91  // encodes empty string otherwise
    92  func (tDays TransitionDays) MarshalXML(e *xml.Encoder, startElement xml.StartElement) error {
    93  	if tDays == 0 {
    94  		return nil
    95  	}
    96  	return e.EncodeElement(int(tDays), startElement)
    97  }
    98  
    99  // Transition - transition actions for a rule in lifecycle configuration.
   100  type Transition struct {
   101  	XMLName      xml.Name       `xml:"Transition"`
   102  	Days         TransitionDays `xml:"Days,omitempty"`
   103  	Date         TransitionDate `xml:"Date,omitempty"`
   104  	StorageClass string         `xml:"StorageClass,omitempty"`
   105  
   106  	set bool
   107  }
   108  
   109  // MarshalXML encodes transition field into an XML form.
   110  func (t Transition) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {
   111  	if !t.set {
   112  		return nil
   113  	}
   114  	type transitionWrapper Transition
   115  	return enc.EncodeElement(transitionWrapper(t), start)
   116  }
   117  
   118  // UnmarshalXML decodes transition field from the XML form.
   119  func (t *Transition) UnmarshalXML(d *xml.Decoder, startElement xml.StartElement) error {
   120  	type transitionWrapper Transition
   121  	var trw transitionWrapper
   122  	err := d.DecodeElement(&trw, &startElement)
   123  	if err != nil {
   124  		return err
   125  	}
   126  	*t = Transition(trw)
   127  	t.set = true
   128  	return nil
   129  }
   130  
   131  // Validate - validates the "Expiration" element
   132  func (t Transition) Validate() error {
   133  	if !t.set {
   134  		return nil
   135  	}
   136  
   137  	if t.IsDaysNull() && t.IsDateNull() {
   138  		return errXMLNotWellFormed
   139  	}
   140  
   141  	// Both transition days and date are specified
   142  	if !t.IsDaysNull() && !t.IsDateNull() {
   143  		return errTransitionInvalid
   144  	}
   145  	if t.StorageClass == "" {
   146  		return errXMLNotWellFormed
   147  	}
   148  	return nil
   149  }
   150  
   151  // IsDaysNull returns true if days field is null
   152  func (t Transition) IsDaysNull() bool {
   153  	return t.Days == TransitionDays(0)
   154  }
   155  
   156  // IsDateNull returns true if date field is null
   157  func (t Transition) IsDateNull() bool {
   158  	return t.Date.Time.IsZero()
   159  }
   160  
   161  // IsNull returns true if both date and days fields are null
   162  func (t Transition) IsNull() bool {
   163  	return t.IsDaysNull() && t.IsDateNull()
   164  }