github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/bucket/replication/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 replication
    19  
    20  import (
    21  	"bytes"
    22  	"encoding/xml"
    23  )
    24  
    25  // Status represents Enabled/Disabled status
    26  type Status string
    27  
    28  // Supported status types
    29  const (
    30  	Enabled  Status = "Enabled"
    31  	Disabled Status = "Disabled"
    32  )
    33  
    34  // DeleteMarkerReplication - whether delete markers are replicated - https://docs.aws.amazon.com/AmazonS3/latest/dev/replication-add-config.html
    35  type DeleteMarkerReplication struct {
    36  	Status Status `xml:"Status"` // should be set to "Disabled" by default
    37  }
    38  
    39  // IsEmpty returns true if DeleteMarkerReplication is not set
    40  func (d DeleteMarkerReplication) IsEmpty() bool {
    41  	return len(d.Status) == 0
    42  }
    43  
    44  // Validate validates whether the status is disabled.
    45  func (d DeleteMarkerReplication) Validate() error {
    46  	if d.IsEmpty() {
    47  		return errDeleteMarkerReplicationMissing
    48  	}
    49  	if d.Status != Disabled && d.Status != Enabled {
    50  		return errInvalidDeleteMarkerReplicationStatus
    51  	}
    52  	return nil
    53  }
    54  
    55  // DeleteReplication - whether versioned deletes are replicated - this is a MinIO only
    56  // extension.
    57  type DeleteReplication struct {
    58  	Status Status `xml:"Status"` // should be set to "Disabled" by default
    59  }
    60  
    61  // IsEmpty returns true if DeleteReplication is not set
    62  func (d DeleteReplication) IsEmpty() bool {
    63  	return len(d.Status) == 0
    64  }
    65  
    66  // Validate validates whether the status is disabled.
    67  func (d DeleteReplication) Validate() error {
    68  	if d.IsEmpty() {
    69  		return errDeleteReplicationMissing
    70  	}
    71  	if d.Status != Disabled && d.Status != Enabled {
    72  		return errInvalidDeleteReplicationStatus
    73  	}
    74  	return nil
    75  }
    76  
    77  // UnmarshalXML - decodes XML data.
    78  func (d *DeleteReplication) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) (err error) {
    79  	// Make subtype to avoid recursive UnmarshalXML().
    80  	type deleteReplication DeleteReplication
    81  	drep := deleteReplication{}
    82  
    83  	if err := dec.DecodeElement(&drep, &start); err != nil {
    84  		return err
    85  	}
    86  	if len(drep.Status) == 0 {
    87  		drep.Status = Disabled
    88  	}
    89  	d.Status = drep.Status
    90  	return nil
    91  }
    92  
    93  // ExistingObjectReplication - whether existing object replication is enabled
    94  type ExistingObjectReplication struct {
    95  	Status Status `xml:"Status"` // should be set to "Disabled" by default
    96  }
    97  
    98  // IsEmpty returns true if ExistingObjectReplication is not set
    99  func (e ExistingObjectReplication) IsEmpty() bool {
   100  	return len(e.Status) == 0
   101  }
   102  
   103  // Validate validates whether the status is disabled.
   104  func (e ExistingObjectReplication) Validate() error {
   105  	if e.IsEmpty() {
   106  		return nil
   107  	}
   108  	if e.Status != Disabled && e.Status != Enabled {
   109  		return errInvalidExistingObjectReplicationStatus
   110  	}
   111  	return nil
   112  }
   113  
   114  // UnmarshalXML - decodes XML data. Default to Disabled unless specified
   115  func (e *ExistingObjectReplication) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) (err error) {
   116  	// Make subtype to avoid recursive UnmarshalXML().
   117  	type existingObjectReplication ExistingObjectReplication
   118  	erep := existingObjectReplication{}
   119  
   120  	if err := dec.DecodeElement(&erep, &start); err != nil {
   121  		return err
   122  	}
   123  	if len(erep.Status) == 0 {
   124  		erep.Status = Disabled
   125  	}
   126  	e.Status = erep.Status
   127  	return nil
   128  }
   129  
   130  // Rule - a rule for replication configuration.
   131  type Rule struct {
   132  	XMLName                 xml.Name                `xml:"Rule" json:"Rule"`
   133  	ID                      string                  `xml:"ID,omitempty" json:"ID,omitempty"`
   134  	Status                  Status                  `xml:"Status" json:"Status"`
   135  	Priority                int                     `xml:"Priority" json:"Priority"`
   136  	DeleteMarkerReplication DeleteMarkerReplication `xml:"DeleteMarkerReplication" json:"DeleteMarkerReplication"`
   137  	// MinIO extension to replicate versioned deletes
   138  	DeleteReplication         DeleteReplication         `xml:"DeleteReplication" json:"DeleteReplication"`
   139  	Destination               Destination               `xml:"Destination" json:"Destination"`
   140  	SourceSelectionCriteria   SourceSelectionCriteria   `xml:"SourceSelectionCriteria" json:"SourceSelectionCriteria"`
   141  	Filter                    Filter                    `xml:"Filter" json:"Filter"`
   142  	ExistingObjectReplication ExistingObjectReplication `xml:"ExistingObjectReplication,omitempty" json:"ExistingObjectReplication,omitempty"`
   143  }
   144  
   145  var (
   146  	errInvalidRuleID                          = Errorf("ID must be less than 255 characters")
   147  	errEmptyRuleStatus                        = Errorf("Status should not be empty")
   148  	errInvalidRuleStatus                      = Errorf("Status must be set to either Enabled or Disabled")
   149  	errDeleteMarkerReplicationMissing         = Errorf("DeleteMarkerReplication must be specified")
   150  	errPriorityMissing                        = Errorf("Priority must be specified")
   151  	errInvalidDeleteMarkerReplicationStatus   = Errorf("Delete marker replication status is invalid")
   152  	errDestinationSourceIdentical             = Errorf("Destination bucket cannot be the same as the source bucket.")
   153  	errDeleteReplicationMissing               = Errorf("Delete replication must be specified")
   154  	errInvalidDeleteReplicationStatus         = Errorf("Delete replication is either enable|disable")
   155  	errInvalidExistingObjectReplicationStatus = Errorf("Existing object replication status is invalid")
   156  	errTagsDeleteMarkerReplicationDisallowed  = Errorf("Delete marker replication is not supported if any Tag filter is specified")
   157  )
   158  
   159  // validateID - checks if ID is valid or not.
   160  func (r Rule) validateID() error {
   161  	// cannot be longer than 255 characters
   162  	if len(r.ID) > 255 {
   163  		return errInvalidRuleID
   164  	}
   165  	return nil
   166  }
   167  
   168  // validateStatus - checks if status is valid or not.
   169  func (r Rule) validateStatus() error {
   170  	// Status can't be empty
   171  	if len(r.Status) == 0 {
   172  		return errEmptyRuleStatus
   173  	}
   174  
   175  	// Status must be one of Enabled or Disabled
   176  	if r.Status != Enabled && r.Status != Disabled {
   177  		return errInvalidRuleStatus
   178  	}
   179  	return nil
   180  }
   181  
   182  func (r Rule) validateFilter() error {
   183  	return r.Filter.Validate()
   184  }
   185  
   186  // Prefix - a rule can either have prefix under <filter></filter> or under
   187  // <filter><and></and></filter>. This method returns the prefix from the
   188  // location where it is available
   189  func (r Rule) Prefix() string {
   190  	if r.Filter.Prefix != "" {
   191  		return r.Filter.Prefix
   192  	}
   193  	return r.Filter.And.Prefix
   194  }
   195  
   196  // Tags - a rule can either have tag under <filter></filter> or under
   197  // <filter><and></and></filter>. This method returns all the tags from the
   198  // rule in the format tag1=value1&tag2=value2
   199  func (r Rule) Tags() string {
   200  	if !r.Filter.Tag.IsEmpty() {
   201  		return r.Filter.Tag.String()
   202  	}
   203  	if len(r.Filter.And.Tags) != 0 {
   204  		var buf bytes.Buffer
   205  		for _, t := range r.Filter.And.Tags {
   206  			if buf.Len() > 0 {
   207  				buf.WriteString("&")
   208  			}
   209  			buf.WriteString(t.String())
   210  		}
   211  		return buf.String()
   212  	}
   213  	return ""
   214  }
   215  
   216  // Validate - validates the rule element
   217  func (r Rule) Validate(bucket string, sameTarget bool) error {
   218  	if err := r.validateID(); err != nil {
   219  		return err
   220  	}
   221  	if err := r.validateStatus(); err != nil {
   222  		return err
   223  	}
   224  	if err := r.validateFilter(); err != nil {
   225  		return err
   226  	}
   227  	if err := r.DeleteMarkerReplication.Validate(); err != nil {
   228  		return err
   229  	}
   230  	if err := r.DeleteReplication.Validate(); err != nil {
   231  		return err
   232  	}
   233  	if err := r.SourceSelectionCriteria.Validate(); err != nil {
   234  		return err
   235  	}
   236  
   237  	if r.Priority < 0 {
   238  		return errPriorityMissing
   239  	}
   240  	if r.Destination.Bucket == bucket && sameTarget {
   241  		return errDestinationSourceIdentical
   242  	}
   243  	if !r.Filter.Tag.IsEmpty() && (r.DeleteMarkerReplication.Status == Enabled) {
   244  		return errTagsDeleteMarkerReplicationDisallowed
   245  	}
   246  	return r.ExistingObjectReplication.Validate()
   247  }
   248  
   249  // MetadataReplicate  returns true if object is not a replica or in the case of replicas,
   250  // replica modification sync is enabled.
   251  func (r Rule) MetadataReplicate(obj ObjectOpts) bool {
   252  	if !obj.Replica {
   253  		return true
   254  	}
   255  	return obj.Replica && r.SourceSelectionCriteria.ReplicaModifications.Status == Enabled
   256  }