replication.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. /*
  2. * MinIO Client (C) 2020 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. package replication
  17. import (
  18. "bytes"
  19. "encoding/xml"
  20. "fmt"
  21. "strconv"
  22. "strings"
  23. "unicode/utf8"
  24. "github.com/rs/xid"
  25. )
  26. var errInvalidFilter = fmt.Errorf("invalid filter")
  27. // OptionType specifies operation to be performed on config
  28. type OptionType string
  29. const (
  30. // AddOption specifies addition of rule to config
  31. AddOption OptionType = "Add"
  32. // SetOption specifies modification of existing rule to config
  33. SetOption OptionType = "Set"
  34. // RemoveOption specifies rule options are for removing a rule
  35. RemoveOption OptionType = "Remove"
  36. // ImportOption is for getting current config
  37. ImportOption OptionType = "Import"
  38. )
  39. // Options represents options to set a replication configuration rule
  40. type Options struct {
  41. Op OptionType
  42. RoleArn string
  43. ID string
  44. Prefix string
  45. RuleStatus string
  46. Priority string
  47. TagString string
  48. StorageClass string
  49. DestBucket string
  50. IsTagSet bool
  51. IsSCSet bool
  52. ReplicateDeletes string // replicate versioned deletes
  53. ReplicateDeleteMarkers string // replicate soft deletes
  54. ReplicaSync string // replicate replica metadata modifications
  55. ExistingObjectReplicate string
  56. }
  57. // Tags returns a slice of tags for a rule
  58. func (opts Options) Tags() ([]Tag, error) {
  59. var tagList []Tag
  60. tagTokens := strings.Split(opts.TagString, "&")
  61. for _, tok := range tagTokens {
  62. if tok == "" {
  63. break
  64. }
  65. kv := strings.SplitN(tok, "=", 2)
  66. if len(kv) != 2 {
  67. return []Tag{}, fmt.Errorf("tags should be entered as comma separated k=v pairs")
  68. }
  69. tagList = append(tagList, Tag{
  70. Key: kv[0],
  71. Value: kv[1],
  72. })
  73. }
  74. return tagList, nil
  75. }
  76. // Config - replication configuration specified in
  77. // https://docs.aws.amazon.com/AmazonS3/latest/dev/replication-add-config.html
  78. type Config struct {
  79. XMLName xml.Name `xml:"ReplicationConfiguration" json:"-"`
  80. Rules []Rule `xml:"Rule" json:"Rules"`
  81. Role string `xml:"Role" json:"Role"`
  82. }
  83. // Empty returns true if config is not set
  84. func (c *Config) Empty() bool {
  85. return len(c.Rules) == 0
  86. }
  87. // AddRule adds a new rule to existing replication config. If a rule exists with the
  88. // same ID, then the rule is replaced.
  89. func (c *Config) AddRule(opts Options) error {
  90. priority, err := strconv.Atoi(opts.Priority)
  91. if err != nil {
  92. return err
  93. }
  94. if opts.RoleArn != "" {
  95. tokens := strings.Split(opts.RoleArn, ":")
  96. if len(tokens) != 6 {
  97. return fmt.Errorf("invalid format for replication Role Arn: %v", opts.RoleArn)
  98. }
  99. if !strings.HasPrefix(opts.RoleArn, "arn:aws:iam") {
  100. return fmt.Errorf("RoleArn invalid for AWS replication configuration: %v", opts.RoleArn)
  101. }
  102. c.Role = opts.RoleArn
  103. }
  104. var status Status
  105. // toggle rule status for edit option
  106. switch opts.RuleStatus {
  107. case "enable":
  108. status = Enabled
  109. case "disable":
  110. status = Disabled
  111. default:
  112. return fmt.Errorf("rule state should be either [enable|disable]")
  113. }
  114. tags, err := opts.Tags()
  115. if err != nil {
  116. return err
  117. }
  118. andVal := And{
  119. Tags: tags,
  120. }
  121. filter := Filter{Prefix: opts.Prefix}
  122. // only a single tag is set.
  123. if opts.Prefix == "" && len(tags) == 1 {
  124. filter.Tag = tags[0]
  125. }
  126. // both prefix and tag are present
  127. if len(andVal.Tags) > 1 || opts.Prefix != "" {
  128. filter.And = andVal
  129. filter.And.Prefix = opts.Prefix
  130. filter.Prefix = ""
  131. filter.Tag = Tag{}
  132. }
  133. if opts.ID == "" {
  134. opts.ID = xid.New().String()
  135. }
  136. destBucket := opts.DestBucket
  137. // ref https://docs.aws.amazon.com/AmazonS3/latest/dev/s3-arn-format.html
  138. if btokens := strings.Split(destBucket, ":"); len(btokens) != 6 {
  139. return fmt.Errorf("destination bucket needs to be in Arn format")
  140. }
  141. dmStatus := Disabled
  142. if opts.ReplicateDeleteMarkers != "" {
  143. switch opts.ReplicateDeleteMarkers {
  144. case "enable":
  145. dmStatus = Enabled
  146. case "disable":
  147. dmStatus = Disabled
  148. default:
  149. return fmt.Errorf("ReplicateDeleteMarkers should be either enable|disable")
  150. }
  151. }
  152. vDeleteStatus := Disabled
  153. if opts.ReplicateDeletes != "" {
  154. switch opts.ReplicateDeletes {
  155. case "enable":
  156. vDeleteStatus = Enabled
  157. case "disable":
  158. vDeleteStatus = Disabled
  159. default:
  160. return fmt.Errorf("ReplicateDeletes should be either enable|disable")
  161. }
  162. }
  163. var replicaSync Status
  164. // replica sync is by default Enabled, unless specified.
  165. switch opts.ReplicaSync {
  166. case "enable", "":
  167. replicaSync = Enabled
  168. case "disable":
  169. replicaSync = Disabled
  170. default:
  171. return fmt.Errorf("replica metadata sync should be either [enable|disable]")
  172. }
  173. var existingStatus Status
  174. if opts.ExistingObjectReplicate != "" {
  175. switch opts.ExistingObjectReplicate {
  176. case "enable":
  177. existingStatus = Enabled
  178. case "disable", "":
  179. existingStatus = Disabled
  180. default:
  181. return fmt.Errorf("existingObjectReplicate should be either enable|disable")
  182. }
  183. }
  184. newRule := Rule{
  185. ID: opts.ID,
  186. Priority: priority,
  187. Status: status,
  188. Filter: filter,
  189. Destination: Destination{
  190. Bucket: destBucket,
  191. StorageClass: opts.StorageClass,
  192. },
  193. DeleteMarkerReplication: DeleteMarkerReplication{Status: dmStatus},
  194. DeleteReplication: DeleteReplication{Status: vDeleteStatus},
  195. // MinIO enables replica metadata syncing by default in the case of bi-directional replication to allow
  196. // automatic failover as the expectation in this case is that replica and source should be identical.
  197. // However AWS leaves this configurable https://docs.aws.amazon.com/AmazonS3/latest/dev/replication-for-metadata-changes.html
  198. SourceSelectionCriteria: SourceSelectionCriteria{
  199. ReplicaModifications: ReplicaModifications{
  200. Status: replicaSync,
  201. },
  202. },
  203. // By default disable existing object replication unless selected
  204. ExistingObjectReplication: ExistingObjectReplication{
  205. Status: existingStatus,
  206. },
  207. }
  208. // validate rule after overlaying priority for pre-existing rule being disabled.
  209. if err := newRule.Validate(); err != nil {
  210. return err
  211. }
  212. // if replication config uses RoleArn, migrate this to the destination element as target ARN for remote bucket for MinIO configuration
  213. if c.Role != "" && !strings.HasPrefix(c.Role, "arn:aws:iam") {
  214. for i := range c.Rules {
  215. c.Rules[i].Destination.Bucket = c.Role
  216. }
  217. c.Role = ""
  218. }
  219. for _, rule := range c.Rules {
  220. if rule.Priority == newRule.Priority {
  221. return fmt.Errorf("priority must be unique. Replication configuration already has a rule with this priority")
  222. }
  223. if rule.ID == newRule.ID {
  224. return fmt.Errorf("a rule exists with this ID")
  225. }
  226. }
  227. c.Rules = append(c.Rules, newRule)
  228. return nil
  229. }
  230. // EditRule modifies an existing rule in replication config
  231. func (c *Config) EditRule(opts Options) error {
  232. if opts.ID == "" {
  233. return fmt.Errorf("rule ID missing")
  234. }
  235. // if replication config uses RoleArn, migrate this to the destination element as target ARN for remote bucket for non AWS.
  236. if c.Role != "" && !strings.HasPrefix(c.Role, "arn:aws:iam") {
  237. for i := range c.Rules {
  238. c.Rules[i].Destination.Bucket = c.Role
  239. }
  240. c.Role = ""
  241. }
  242. rIdx := -1
  243. var newRule Rule
  244. for i, rule := range c.Rules {
  245. if rule.ID == opts.ID {
  246. rIdx = i
  247. newRule = rule
  248. break
  249. }
  250. }
  251. if rIdx < 0 {
  252. return fmt.Errorf("rule with ID %s not found in replication configuration", opts.ID)
  253. }
  254. prefixChg := opts.Prefix != newRule.Prefix()
  255. if opts.IsTagSet || prefixChg {
  256. prefix := newRule.Prefix()
  257. if prefix != opts.Prefix {
  258. prefix = opts.Prefix
  259. }
  260. tags := []Tag{newRule.Filter.Tag}
  261. if len(newRule.Filter.And.Tags) != 0 {
  262. tags = newRule.Filter.And.Tags
  263. }
  264. var err error
  265. if opts.IsTagSet {
  266. tags, err = opts.Tags()
  267. if err != nil {
  268. return err
  269. }
  270. }
  271. andVal := And{
  272. Tags: tags,
  273. }
  274. filter := Filter{Prefix: prefix}
  275. // only a single tag is set.
  276. if prefix == "" && len(tags) == 1 {
  277. filter.Tag = tags[0]
  278. }
  279. // both prefix and tag are present
  280. if len(andVal.Tags) > 1 || prefix != "" {
  281. filter.And = andVal
  282. filter.And.Prefix = prefix
  283. filter.Prefix = ""
  284. filter.Tag = Tag{}
  285. }
  286. newRule.Filter = filter
  287. }
  288. // toggle rule status for edit option
  289. if opts.RuleStatus != "" {
  290. switch opts.RuleStatus {
  291. case "enable":
  292. newRule.Status = Enabled
  293. case "disable":
  294. newRule.Status = Disabled
  295. default:
  296. return fmt.Errorf("rule state should be either [enable|disable]")
  297. }
  298. }
  299. // set DeleteMarkerReplication rule status for edit option
  300. if opts.ReplicateDeleteMarkers != "" {
  301. switch opts.ReplicateDeleteMarkers {
  302. case "enable":
  303. newRule.DeleteMarkerReplication.Status = Enabled
  304. case "disable":
  305. newRule.DeleteMarkerReplication.Status = Disabled
  306. default:
  307. return fmt.Errorf("ReplicateDeleteMarkers state should be either [enable|disable]")
  308. }
  309. }
  310. // set DeleteReplication rule status for edit option. This is a MinIO specific
  311. // option to replicate versioned deletes
  312. if opts.ReplicateDeletes != "" {
  313. switch opts.ReplicateDeletes {
  314. case "enable":
  315. newRule.DeleteReplication.Status = Enabled
  316. case "disable":
  317. newRule.DeleteReplication.Status = Disabled
  318. default:
  319. return fmt.Errorf("ReplicateDeletes state should be either [enable|disable]")
  320. }
  321. }
  322. if opts.ReplicaSync != "" {
  323. switch opts.ReplicaSync {
  324. case "enable", "":
  325. newRule.SourceSelectionCriteria.ReplicaModifications.Status = Enabled
  326. case "disable":
  327. newRule.SourceSelectionCriteria.ReplicaModifications.Status = Disabled
  328. default:
  329. return fmt.Errorf("replica metadata sync should be either [enable|disable]")
  330. }
  331. }
  332. if opts.ExistingObjectReplicate != "" {
  333. switch opts.ExistingObjectReplicate {
  334. case "enable":
  335. newRule.ExistingObjectReplication.Status = Enabled
  336. case "disable":
  337. newRule.ExistingObjectReplication.Status = Disabled
  338. default:
  339. return fmt.Errorf("existingObjectsReplication state should be either [enable|disable]")
  340. }
  341. }
  342. if opts.IsSCSet {
  343. newRule.Destination.StorageClass = opts.StorageClass
  344. }
  345. if opts.Priority != "" {
  346. priority, err := strconv.Atoi(opts.Priority)
  347. if err != nil {
  348. return err
  349. }
  350. newRule.Priority = priority
  351. }
  352. if opts.DestBucket != "" {
  353. destBucket := opts.DestBucket
  354. // ref https://docs.aws.amazon.com/AmazonS3/latest/dev/s3-arn-format.html
  355. if btokens := strings.Split(opts.DestBucket, ":"); len(btokens) != 6 {
  356. return fmt.Errorf("destination bucket needs to be in Arn format")
  357. }
  358. newRule.Destination.Bucket = destBucket
  359. }
  360. // validate rule
  361. if err := newRule.Validate(); err != nil {
  362. return err
  363. }
  364. // ensure priority and destination bucket restrictions are not violated
  365. for idx, rule := range c.Rules {
  366. if rule.Priority == newRule.Priority && rIdx != idx {
  367. return fmt.Errorf("priority must be unique. Replication configuration already has a rule with this priority")
  368. }
  369. if rule.Destination.Bucket != newRule.Destination.Bucket && rule.ID == newRule.ID {
  370. return fmt.Errorf("invalid destination bucket for this rule")
  371. }
  372. }
  373. c.Rules[rIdx] = newRule
  374. return nil
  375. }
  376. // RemoveRule removes a rule from replication config.
  377. func (c *Config) RemoveRule(opts Options) error {
  378. var newRules []Rule
  379. ruleFound := false
  380. for _, rule := range c.Rules {
  381. if rule.ID != opts.ID {
  382. newRules = append(newRules, rule)
  383. continue
  384. }
  385. ruleFound = true
  386. }
  387. if !ruleFound {
  388. return fmt.Errorf("Rule with ID %s not found", opts.ID)
  389. }
  390. if len(newRules) == 0 {
  391. return fmt.Errorf("replication configuration should have at least one rule")
  392. }
  393. c.Rules = newRules
  394. return nil
  395. }
  396. // Rule - a rule for replication configuration.
  397. type Rule struct {
  398. XMLName xml.Name `xml:"Rule" json:"-"`
  399. ID string `xml:"ID,omitempty"`
  400. Status Status `xml:"Status"`
  401. Priority int `xml:"Priority"`
  402. DeleteMarkerReplication DeleteMarkerReplication `xml:"DeleteMarkerReplication"`
  403. DeleteReplication DeleteReplication `xml:"DeleteReplication"`
  404. Destination Destination `xml:"Destination"`
  405. Filter Filter `xml:"Filter" json:"Filter"`
  406. SourceSelectionCriteria SourceSelectionCriteria `xml:"SourceSelectionCriteria" json:"SourceSelectionCriteria"`
  407. ExistingObjectReplication ExistingObjectReplication `xml:"ExistingObjectReplication,omitempty" json:"ExistingObjectReplication,omitempty"`
  408. }
  409. // Validate validates the rule for correctness
  410. func (r Rule) Validate() error {
  411. if err := r.validateID(); err != nil {
  412. return err
  413. }
  414. if err := r.validateStatus(); err != nil {
  415. return err
  416. }
  417. if err := r.validateFilter(); err != nil {
  418. return err
  419. }
  420. if r.Priority < 0 && r.Status == Enabled {
  421. return fmt.Errorf("priority must be set for the rule")
  422. }
  423. if err := r.validateStatus(); err != nil {
  424. return err
  425. }
  426. return r.ExistingObjectReplication.Validate()
  427. }
  428. // validateID - checks if ID is valid or not.
  429. func (r Rule) validateID() error {
  430. // cannot be longer than 255 characters
  431. if len(r.ID) > 255 {
  432. return fmt.Errorf("ID must be less than 255 characters")
  433. }
  434. return nil
  435. }
  436. // validateStatus - checks if status is valid or not.
  437. func (r Rule) validateStatus() error {
  438. // Status can't be empty
  439. if len(r.Status) == 0 {
  440. return fmt.Errorf("status cannot be empty")
  441. }
  442. // Status must be one of Enabled or Disabled
  443. if r.Status != Enabled && r.Status != Disabled {
  444. return fmt.Errorf("status must be set to either Enabled or Disabled")
  445. }
  446. return nil
  447. }
  448. func (r Rule) validateFilter() error {
  449. if err := r.Filter.Validate(); err != nil {
  450. return err
  451. }
  452. return nil
  453. }
  454. // Prefix - a rule can either have prefix under <filter></filter> or under
  455. // <filter><and></and></filter>. This method returns the prefix from the
  456. // location where it is available
  457. func (r Rule) Prefix() string {
  458. if r.Filter.Prefix != "" {
  459. return r.Filter.Prefix
  460. }
  461. return r.Filter.And.Prefix
  462. }
  463. // Tags - a rule can either have tag under <filter></filter> or under
  464. // <filter><and></and></filter>. This method returns all the tags from the
  465. // rule in the format tag1=value1&tag2=value2
  466. func (r Rule) Tags() string {
  467. ts := []Tag{r.Filter.Tag}
  468. if len(r.Filter.And.Tags) != 0 {
  469. ts = r.Filter.And.Tags
  470. }
  471. var buf bytes.Buffer
  472. for _, t := range ts {
  473. if buf.Len() > 0 {
  474. buf.WriteString("&")
  475. }
  476. buf.WriteString(t.String())
  477. }
  478. return buf.String()
  479. }
  480. // Filter - a filter for a replication configuration Rule.
  481. type Filter struct {
  482. XMLName xml.Name `xml:"Filter" json:"-"`
  483. Prefix string `json:"Prefix,omitempty"`
  484. And And `xml:"And,omitempty" json:"And,omitempty"`
  485. Tag Tag `xml:"Tag,omitempty" json:"Tag,omitempty"`
  486. }
  487. // Validate - validates the filter element
  488. func (f Filter) Validate() error {
  489. // A Filter must have exactly one of Prefix, Tag, or And specified.
  490. if !f.And.isEmpty() {
  491. if f.Prefix != "" {
  492. return errInvalidFilter
  493. }
  494. if !f.Tag.IsEmpty() {
  495. return errInvalidFilter
  496. }
  497. }
  498. if f.Prefix != "" {
  499. if !f.Tag.IsEmpty() {
  500. return errInvalidFilter
  501. }
  502. }
  503. if !f.Tag.IsEmpty() {
  504. if err := f.Tag.Validate(); err != nil {
  505. return err
  506. }
  507. }
  508. return nil
  509. }
  510. // Tag - a tag for a replication configuration Rule filter.
  511. type Tag struct {
  512. XMLName xml.Name `json:"-"`
  513. Key string `xml:"Key,omitempty" json:"Key,omitempty"`
  514. Value string `xml:"Value,omitempty" json:"Value,omitempty"`
  515. }
  516. func (tag Tag) String() string {
  517. if tag.IsEmpty() {
  518. return ""
  519. }
  520. return tag.Key + "=" + tag.Value
  521. }
  522. // IsEmpty returns whether this tag is empty or not.
  523. func (tag Tag) IsEmpty() bool {
  524. return tag.Key == ""
  525. }
  526. // Validate checks this tag.
  527. func (tag Tag) Validate() error {
  528. if len(tag.Key) == 0 || utf8.RuneCountInString(tag.Key) > 128 {
  529. return fmt.Errorf("invalid Tag Key")
  530. }
  531. if utf8.RuneCountInString(tag.Value) > 256 {
  532. return fmt.Errorf("invalid Tag Value")
  533. }
  534. return nil
  535. }
  536. // Destination - destination in ReplicationConfiguration.
  537. type Destination struct {
  538. XMLName xml.Name `xml:"Destination" json:"-"`
  539. Bucket string `xml:"Bucket" json:"Bucket"`
  540. StorageClass string `xml:"StorageClass,omitempty" json:"StorageClass,omitempty"`
  541. }
  542. // And - a tag to combine a prefix and multiple tags for replication configuration rule.
  543. type And struct {
  544. XMLName xml.Name `xml:"And,omitempty" json:"-"`
  545. Prefix string `xml:"Prefix,omitempty" json:"Prefix,omitempty"`
  546. Tags []Tag `xml:"Tag,omitempty" json:"Tag,omitempty"`
  547. }
  548. // isEmpty returns true if Tags field is null
  549. func (a And) isEmpty() bool {
  550. return len(a.Tags) == 0 && a.Prefix == ""
  551. }
  552. // Status represents Enabled/Disabled status
  553. type Status string
  554. // Supported status types
  555. const (
  556. Enabled Status = "Enabled"
  557. Disabled Status = "Disabled"
  558. )
  559. // DeleteMarkerReplication - whether delete markers are replicated - https://docs.aws.amazon.com/AmazonS3/latest/dev/replication-add-config.html
  560. type DeleteMarkerReplication struct {
  561. Status Status `xml:"Status" json:"Status"` // should be set to "Disabled" by default
  562. }
  563. // IsEmpty returns true if DeleteMarkerReplication is not set
  564. func (d DeleteMarkerReplication) IsEmpty() bool {
  565. return len(d.Status) == 0
  566. }
  567. // DeleteReplication - whether versioned deletes are replicated - this
  568. // is a MinIO specific extension
  569. type DeleteReplication struct {
  570. Status Status `xml:"Status" json:"Status"` // should be set to "Disabled" by default
  571. }
  572. // IsEmpty returns true if DeleteReplication is not set
  573. func (d DeleteReplication) IsEmpty() bool {
  574. return len(d.Status) == 0
  575. }
  576. // ReplicaModifications specifies if replica modification sync is enabled
  577. type ReplicaModifications struct {
  578. Status Status `xml:"Status" json:"Status"` // should be set to "Enabled" by default
  579. }
  580. // SourceSelectionCriteria - specifies additional source selection criteria in ReplicationConfiguration.
  581. type SourceSelectionCriteria struct {
  582. ReplicaModifications ReplicaModifications `xml:"ReplicaModifications" json:"ReplicaModifications"`
  583. }
  584. // IsValid - checks whether SourceSelectionCriteria is valid or not.
  585. func (s SourceSelectionCriteria) IsValid() bool {
  586. return s.ReplicaModifications.Status == Enabled || s.ReplicaModifications.Status == Disabled
  587. }
  588. // Validate source selection criteria
  589. func (s SourceSelectionCriteria) Validate() error {
  590. if (s == SourceSelectionCriteria{}) {
  591. return nil
  592. }
  593. if !s.IsValid() {
  594. return fmt.Errorf("invalid ReplicaModification status")
  595. }
  596. return nil
  597. }
  598. // ExistingObjectReplication - whether existing object replication is enabled
  599. type ExistingObjectReplication struct {
  600. Status Status `xml:"Status"` // should be set to "Disabled" by default
  601. }
  602. // IsEmpty returns true if DeleteMarkerReplication is not set
  603. func (e ExistingObjectReplication) IsEmpty() bool {
  604. return len(e.Status) == 0
  605. }
  606. // Validate validates whether the status is disabled.
  607. func (e ExistingObjectReplication) Validate() error {
  608. if e.IsEmpty() {
  609. return nil
  610. }
  611. if e.Status != Disabled && e.Status != Enabled {
  612. return fmt.Errorf("invalid ExistingObjectReplication status")
  613. }
  614. return nil
  615. }
  616. // TargetMetrics represents inline replication metrics
  617. // such as pending, failed and completed bytes in total for a bucket remote target
  618. type TargetMetrics struct {
  619. // Pending size in bytes
  620. PendingSize uint64 `json:"pendingReplicationSize"`
  621. // Completed size in bytes
  622. ReplicatedSize uint64 `json:"completedReplicationSize"`
  623. // Total Replica size in bytes
  624. ReplicaSize uint64 `json:"replicaSize"`
  625. // Failed size in bytes
  626. FailedSize uint64 `json:"failedReplicationSize"`
  627. // Total number of pending operations including metadata updates
  628. PendingCount uint64 `json:"pendingReplicationCount"`
  629. // Total number of failed operations including metadata updates
  630. FailedCount uint64 `json:"failedReplicationCount"`
  631. }
  632. // Metrics represents inline replication metrics for a bucket.
  633. type Metrics struct {
  634. Stats map[string]TargetMetrics
  635. // Total Pending size in bytes across targets
  636. PendingSize uint64 `json:"pendingReplicationSize"`
  637. // Completed size in bytes across targets
  638. ReplicatedSize uint64 `json:"completedReplicationSize"`
  639. // Total Replica size in bytes across targets
  640. ReplicaSize uint64 `json:"replicaSize"`
  641. // Failed size in bytes across targets
  642. FailedSize uint64 `json:"failedReplicationSize"`
  643. // Total number of pending operations including metadata updates across targets
  644. PendingCount uint64 `json:"pendingReplicationCount"`
  645. // Total number of failed operations including metadata updates across targets
  646. FailedCount uint64 `json:"failedReplicationCount"`
  647. }
  648. type ResyncTargetsInfo struct {
  649. Targets []ResyncTarget `json:"target,omitempty"`
  650. }
  651. type ResyncTarget struct {
  652. Arn string `json:"arn"`
  653. ResetID string `json:"resetid"`
  654. }