github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/bucket/replication/destination.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 "encoding/xml" 22 "fmt" 23 "strings" 24 25 "github.com/minio/pkg/v2/wildcard" 26 ) 27 28 // DestinationARNPrefix - destination ARN prefix as per AWS S3 specification. 29 const DestinationARNPrefix = "arn:aws:s3:::" 30 31 // DestinationARNMinIOPrefix - destination ARN prefix for MinIO. 32 const DestinationARNMinIOPrefix = "arn:minio:replication:" 33 34 // Destination - destination in ReplicationConfiguration. 35 type Destination struct { 36 XMLName xml.Name `xml:"Destination" json:"Destination"` 37 Bucket string `xml:"Bucket" json:"Bucket"` 38 StorageClass string `xml:"StorageClass" json:"StorageClass"` 39 ARN string 40 // EncryptionConfiguration TODO: not needed for MinIO 41 } 42 43 func (d Destination) isValidStorageClass() bool { 44 if d.StorageClass == "" { 45 return true 46 } 47 return d.StorageClass == "STANDARD" || d.StorageClass == "REDUCED_REDUNDANCY" 48 } 49 50 // IsValid - checks whether Destination is valid or not. 51 func (d Destination) IsValid() bool { 52 return d.Bucket != "" || !d.isValidStorageClass() 53 } 54 55 func (d Destination) String() string { 56 return d.ARN 57 } 58 59 // LegacyArn returns true if arn format has prefix "arn:aws:s3:::" which was 60 // used prior to multi-destination 61 func (d Destination) LegacyArn() bool { 62 return strings.HasPrefix(d.ARN, DestinationARNPrefix) 63 } 64 65 // TargetArn returns true if arn format has prefix "arn:minio:replication:::" 66 // used for multi-destination targets 67 func (d Destination) TargetArn() bool { 68 return strings.HasPrefix(d.ARN, DestinationARNMinIOPrefix) 69 } 70 71 // MarshalXML - encodes to XML data. 72 func (d Destination) MarshalXML(e *xml.Encoder, start xml.StartElement) error { 73 if err := e.EncodeToken(start); err != nil { 74 return err 75 } 76 if err := e.EncodeElement(d.String(), xml.StartElement{Name: xml.Name{Local: "Bucket"}}); err != nil { 77 return err 78 } 79 if d.StorageClass != "" { 80 if err := e.EncodeElement(d.StorageClass, xml.StartElement{Name: xml.Name{Local: "StorageClass"}}); err != nil { 81 return err 82 } 83 } 84 return e.EncodeToken(xml.EndElement{Name: start.Name}) 85 } 86 87 // UnmarshalXML - decodes XML data. 88 func (d *Destination) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) (err error) { 89 // Make subtype to avoid recursive UnmarshalXML(). 90 type destination Destination 91 dest := destination{} 92 93 if err := dec.DecodeElement(&dest, &start); err != nil { 94 return err 95 } 96 parsedDest, err := parseDestination(dest.Bucket) 97 if err != nil { 98 return err 99 } 100 if dest.StorageClass != "" { 101 switch dest.StorageClass { 102 case "STANDARD", "REDUCED_REDUNDANCY": 103 default: 104 return fmt.Errorf("unknown storage class %s", dest.StorageClass) 105 } 106 } 107 parsedDest.StorageClass = dest.StorageClass 108 *d = parsedDest 109 return nil 110 } 111 112 // Validate - validates Resource is for given bucket or not. 113 func (d Destination) Validate(bucketName string) error { 114 if !d.IsValid() { 115 return Errorf("invalid destination") 116 } 117 118 if !wildcard.Match(d.Bucket, bucketName) { 119 return Errorf("bucket name does not match") 120 } 121 return nil 122 } 123 124 // parseDestination - parses string to Destination. 125 func parseDestination(s string) (Destination, error) { 126 if !strings.HasPrefix(s, DestinationARNPrefix) && !strings.HasPrefix(s, DestinationARNMinIOPrefix) { 127 return Destination{}, Errorf("invalid destination '%s'", s) 128 } 129 130 bucketName := strings.TrimPrefix(s, DestinationARNPrefix) 131 132 return Destination{ 133 Bucket: bucketName, 134 ARN: s, 135 }, nil 136 }