From 1491768baf293ee2309c757c2595b0e607b64122 Mon Sep 17 00:00:00 2001 From: Natalia Marukovich Date: Fri, 15 May 2026 12:33:02 +0400 Subject: [PATCH 1/3] K8SPXC-1683 add gtid validation --- ...cona.com_perconaxtradbclusterrestores.yaml | 8 +++++++ deploy/bundle.yaml | 8 +++++++ deploy/crd.yaml | 8 +++++++ deploy/cw-bundle.yaml | 8 +++++++ pkg/apis/pxc/v1/pxc_prestore_types.go | 3 +++ .../pxcrestore/pitr_validation_test.go | 22 +++++++++++++++++-- 6 files changed, 55 insertions(+), 2 deletions(-) diff --git a/config/crd/bases/pxc.percona.com_perconaxtradbclusterrestores.yaml b/config/crd/bases/pxc.percona.com_perconaxtradbclusterrestores.yaml index 3d2cfdec0c..706f751cb1 100644 --- a/config/crd/bases/pxc.percona.com_perconaxtradbclusterrestores.yaml +++ b/config/crd/bases/pxc.percona.com_perconaxtradbclusterrestores.yaml @@ -549,6 +549,7 @@ spec: date: type: string gtid: + maxLength: 1024 type: string type: enum: @@ -570,6 +571,13 @@ spec: - message: Date and GTID should not be set when type is 'latest' rule: self.type != 'latest' || ((!has(self.date) || size(self.date) == 0) && (!has(self.gtid) || size(self.gtid) == 0)) + - message: GTID for type 'transaction' must be a single transaction + identifier in the form 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:N' + (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42') + rule: self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+$') + - message: GTID for type 'skip' must be a valid MySQL GTID set (e.g. + 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10' or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1-5') + rule: self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+(-[0-9]+)?(:[0-9]+(-[0-9]+)?)*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+(-[0-9]+)?(:[0-9]+(-[0-9]+)?)*)*$') pxcCluster: type: string resources: diff --git a/deploy/bundle.yaml b/deploy/bundle.yaml index a8528a3579..500b40ce3c 100644 --- a/deploy/bundle.yaml +++ b/deploy/bundle.yaml @@ -938,6 +938,7 @@ spec: date: type: string gtid: + maxLength: 1024 type: string type: enum: @@ -959,6 +960,13 @@ spec: - message: Date and GTID should not be set when type is 'latest' rule: self.type != 'latest' || ((!has(self.date) || size(self.date) == 0) && (!has(self.gtid) || size(self.gtid) == 0)) + - message: GTID for type 'transaction' must be a single transaction + identifier in the form 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:N' + (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42') + rule: self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+$') + - message: GTID for type 'skip' must be a valid MySQL GTID set (e.g. + 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10' or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1-5') + rule: self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+(-[0-9]+)?(:[0-9]+(-[0-9]+)?)*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+(-[0-9]+)?(:[0-9]+(-[0-9]+)?)*)*$') pxcCluster: type: string resources: diff --git a/deploy/crd.yaml b/deploy/crd.yaml index 4771c1e6d2..ff58517001 100644 --- a/deploy/crd.yaml +++ b/deploy/crd.yaml @@ -938,6 +938,7 @@ spec: date: type: string gtid: + maxLength: 1024 type: string type: enum: @@ -959,6 +960,13 @@ spec: - message: Date and GTID should not be set when type is 'latest' rule: self.type != 'latest' || ((!has(self.date) || size(self.date) == 0) && (!has(self.gtid) || size(self.gtid) == 0)) + - message: GTID for type 'transaction' must be a single transaction + identifier in the form 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:N' + (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42') + rule: self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+$') + - message: GTID for type 'skip' must be a valid MySQL GTID set (e.g. + 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10' or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1-5') + rule: self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+(-[0-9]+)?(:[0-9]+(-[0-9]+)?)*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+(-[0-9]+)?(:[0-9]+(-[0-9]+)?)*)*$') pxcCluster: type: string resources: diff --git a/deploy/cw-bundle.yaml b/deploy/cw-bundle.yaml index 20fa576e5b..cbb4896ee8 100644 --- a/deploy/cw-bundle.yaml +++ b/deploy/cw-bundle.yaml @@ -938,6 +938,7 @@ spec: date: type: string gtid: + maxLength: 1024 type: string type: enum: @@ -959,6 +960,13 @@ spec: - message: Date and GTID should not be set when type is 'latest' rule: self.type != 'latest' || ((!has(self.date) || size(self.date) == 0) && (!has(self.gtid) || size(self.gtid) == 0)) + - message: GTID for type 'transaction' must be a single transaction + identifier in the form 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:N' + (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42') + rule: self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+$') + - message: GTID for type 'skip' must be a valid MySQL GTID set (e.g. + 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10' or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1-5') + rule: self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+(-[0-9]+)?(:[0-9]+(-[0-9]+)?)*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+(-[0-9]+)?(:[0-9]+(-[0-9]+)?)*)*$') pxcCluster: type: string resources: diff --git a/pkg/apis/pxc/v1/pxc_prestore_types.go b/pkg/apis/pxc/v1/pxc_prestore_types.go index 43e8648574..d981f212f9 100644 --- a/pkg/apis/pxc/v1/pxc_prestore_types.go +++ b/pkg/apis/pxc/v1/pxc_prestore_types.go @@ -37,11 +37,14 @@ const ( // +kubebuilder:validation:XValidation:rule="self.type != 'date' || (has(self.date) && self.date.matches('^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]) ([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$'))",message="Date is required for type 'date' and should be in format YYYY-MM-DD HH:MM:SS with valid ranges (MM: 01-12, DD: 01-31, HH: 00-23, MM/SS: 00-59)" // +kubebuilder:validation:XValidation:rule="(self.type != 'transaction' && self.type != 'skip') || (has(self.gtid) && size(self.gtid) > 0)",message="GTID is required for types 'transaction' and 'skip'" // +kubebuilder:validation:XValidation:rule="self.type != 'latest' || ((!has(self.date) || size(self.date) == 0) && (!has(self.gtid) || size(self.gtid) == 0))",message="Date and GTID should not be set when type is 'latest'" +// +kubebuilder:validation:XValidation:rule="self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+$')",message="GTID for type 'transaction' must be a single transaction identifier in the form 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:N' (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42')" +// +kubebuilder:validation:XValidation:rule="self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+(-[0-9]+)?(:[0-9]+(-[0-9]+)?)*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+(-[0-9]+)?(:[0-9]+(-[0-9]+)?)*)*$')",message="GTID for type 'skip' must be a valid MySQL GTID set (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10' or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1-5')" type PITR struct { BackupSource *PXCBackupStatus `json:"backupSource"` // +kubebuilder:validation:Enum={latest,date,transaction,skip} Type string `json:"type"` Date string `json:"date"` + // +kubebuilder:validation:MaxLength=1024 GTID string `json:"gtid"` } diff --git a/pkg/controller/pxcrestore/pitr_validation_test.go b/pkg/controller/pxcrestore/pitr_validation_test.go index 21b690baee..e83af637f3 100644 --- a/pkg/controller/pxcrestore/pitr_validation_test.go +++ b/pkg/controller/pxcrestore/pitr_validation_test.go @@ -93,7 +93,13 @@ var _ = Describe("PerconaXtraDBClusterRestore PITR CRD validation", Ordered, fun }, Entry("type latest", "valid-latest", &pxcv1.PITR{Type: "latest"}), Entry("type date with valid format", "valid-date", &pxcv1.PITR{Type: "date", Date: "2024-01-15 12:30:00"}), - Entry("type transaction with gtid", "valid-transaction", &pxcv1.PITR{Type: "transaction", GTID: "abc123:1-10"}), + // transaction: single UUID:N only (recoverer parses it as a single integer) + Entry("type transaction with single gtid", "valid-transaction", &pxcv1.PITR{Type: "transaction", GTID: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42"}), + // skip: full GTID set syntax is allowed + Entry("type skip with single gtid", "valid-skip-single", &pxcv1.PITR{Type: "skip", GTID: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42"}), + Entry("type skip with gtid range", "valid-skip-range", &pxcv1.PITR{Type: "skip", GTID: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10"}), + Entry("type skip with multiple intervals", "valid-skip-intervals", &pxcv1.PITR{Type: "skip", GTID: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-5:7-9"}), + Entry("type skip with multi-source gtid set", "valid-skip-multi-source", &pxcv1.PITR{Type: "skip", GTID: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1-5"}), ) DescribeTable("invalid PITR configurations", @@ -102,10 +108,22 @@ var _ = Describe("PerconaXtraDBClusterRestore PITR CRD validation", Ordered, fun Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring(errMsg)) }, + // date type Entry("type date with empty date", "invalid-date-empty", &pxcv1.PITR{Type: "date"}, "Date is required"), Entry("type date with wrong format", "invalid-date-format", &pxcv1.PITR{Type: "date", Date: "15-01-2024 12:30:00"}, "format YYYY-MM-DD"), + // latest type Entry("type latest with date set", "invalid-latest-date", &pxcv1.PITR{Type: "latest", Date: "2024-01-15 12:30:00"}, "Date and GTID should not be set"), - Entry("type transaction without gtid", "invalid-transaction-no-gtid", &pxcv1.PITR{Type: "transaction"}, "GTID is required"), + Entry("type latest with gtid set", "invalid-latest-gtid", &pxcv1.PITR{Type: "latest", GTID: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1"}, "Date and GTID should not be set"), + // unknown type Entry("unknown type", "invalid-unknown-type", &pxcv1.PITR{Type: "unknown"}, "Unsupported value"), + // transaction: must be UUID:N (single integer), not a range or set + Entry("type transaction without gtid", "invalid-transaction-no-gtid", &pxcv1.PITR{Type: "transaction"}, "GTID is required"), + Entry("type transaction with non-uuid gtid", "invalid-transaction-bad-uuid", &pxcv1.PITR{Type: "transaction", GTID: "notauuid:42"}, "single transaction identifier"), + Entry("type transaction with gtid range", "invalid-transaction-range", &pxcv1.PITR{Type: "transaction", GTID: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10"}, "single transaction identifier"), + Entry("type transaction with multi-source gtid set", "invalid-transaction-multi-source", &pxcv1.PITR{Type: "transaction", GTID: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1"}, "single transaction identifier"), + // skip: GTID is required and must be a valid MySQL GTID set + Entry("type skip without gtid", "invalid-skip-no-gtid", &pxcv1.PITR{Type: "skip"}, "GTID is required"), + Entry("type skip with non-uuid gtid", "invalid-skip-bad-uuid", &pxcv1.PITR{Type: "skip", GTID: "notauuid:1-10"}, "valid MySQL GTID set"), + Entry("type skip with malformed gtid", "invalid-skip-malformed", &pxcv1.PITR{Type: "skip", GTID: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"}, "valid MySQL GTID set"), ) }) From 2e3b0c9b28ba6a24ef9df9da476b54820a3ab83c Mon Sep 17 00:00:00 2001 From: Natalia Marukovich Date: Mon, 18 May 2026 16:50:29 +0200 Subject: [PATCH 2/3] fix PR comments --- ...pxc.percona.com_perconaxtradbclusterrestores.yaml | 12 +++++++----- deploy/bundle.yaml | 12 +++++++----- deploy/crd.yaml | 12 +++++++----- deploy/cw-bundle.yaml | 12 +++++++----- pkg/apis/pxc/v1/pxc_prestore_types.go | 4 ++-- pkg/controller/pxcrestore/pitr_validation_test.go | 10 +++++++--- 6 files changed, 37 insertions(+), 25 deletions(-) diff --git a/config/crd/bases/pxc.percona.com_perconaxtradbclusterrestores.yaml b/config/crd/bases/pxc.percona.com_perconaxtradbclusterrestores.yaml index 706f751cb1..37668013b3 100644 --- a/config/crd/bases/pxc.percona.com_perconaxtradbclusterrestores.yaml +++ b/config/crd/bases/pxc.percona.com_perconaxtradbclusterrestores.yaml @@ -572,12 +572,14 @@ spec: rule: self.type != 'latest' || ((!has(self.date) || size(self.date) == 0) && (!has(self.gtid) || size(self.gtid) == 0)) - message: GTID for type 'transaction' must be a single transaction - identifier in the form 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:N' - (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42') - rule: self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+$') + identifier in the form 'UUID:N' or 'UUID:tag:N' (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42' + or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:Domain_1:42') + rule: self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*$') - message: GTID for type 'skip' must be a valid MySQL GTID set (e.g. - 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10' or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1-5') - rule: self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+(-[0-9]+)?(:[0-9]+(-[0-9]+)?)*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+(-[0-9]+)?(:[0-9]+(-[0-9]+)?)*)*$') + 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10', 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:Domain_1:1-10', + or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1-5') + rule: self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?)(:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?))*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?)(:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?))* + )*$') pxcCluster: type: string resources: diff --git a/deploy/bundle.yaml b/deploy/bundle.yaml index 500b40ce3c..56faaa3d81 100644 --- a/deploy/bundle.yaml +++ b/deploy/bundle.yaml @@ -961,12 +961,14 @@ spec: rule: self.type != 'latest' || ((!has(self.date) || size(self.date) == 0) && (!has(self.gtid) || size(self.gtid) == 0)) - message: GTID for type 'transaction' must be a single transaction - identifier in the form 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:N' - (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42') - rule: self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+$') + identifier in the form 'UUID:N' or 'UUID:tag:N' (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42' + or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:Domain_1:42') + rule: self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*$') - message: GTID for type 'skip' must be a valid MySQL GTID set (e.g. - 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10' or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1-5') - rule: self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+(-[0-9]+)?(:[0-9]+(-[0-9]+)?)*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+(-[0-9]+)?(:[0-9]+(-[0-9]+)?)*)*$') + 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10', 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:Domain_1:1-10', + or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1-5') + rule: self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?)(:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?))*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?)(:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?))* + )*$') pxcCluster: type: string resources: diff --git a/deploy/crd.yaml b/deploy/crd.yaml index ff58517001..22d98842f8 100644 --- a/deploy/crd.yaml +++ b/deploy/crd.yaml @@ -961,12 +961,14 @@ spec: rule: self.type != 'latest' || ((!has(self.date) || size(self.date) == 0) && (!has(self.gtid) || size(self.gtid) == 0)) - message: GTID for type 'transaction' must be a single transaction - identifier in the form 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:N' - (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42') - rule: self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+$') + identifier in the form 'UUID:N' or 'UUID:tag:N' (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42' + or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:Domain_1:42') + rule: self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*$') - message: GTID for type 'skip' must be a valid MySQL GTID set (e.g. - 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10' or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1-5') - rule: self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+(-[0-9]+)?(:[0-9]+(-[0-9]+)?)*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+(-[0-9]+)?(:[0-9]+(-[0-9]+)?)*)*$') + 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10', 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:Domain_1:1-10', + or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1-5') + rule: self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?)(:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?))*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?)(:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?))* + )*$') pxcCluster: type: string resources: diff --git a/deploy/cw-bundle.yaml b/deploy/cw-bundle.yaml index cbb4896ee8..bedf54894b 100644 --- a/deploy/cw-bundle.yaml +++ b/deploy/cw-bundle.yaml @@ -961,12 +961,14 @@ spec: rule: self.type != 'latest' || ((!has(self.date) || size(self.date) == 0) && (!has(self.gtid) || size(self.gtid) == 0)) - message: GTID for type 'transaction' must be a single transaction - identifier in the form 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:N' - (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42') - rule: self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+$') + identifier in the form 'UUID:N' or 'UUID:tag:N' (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42' + or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:Domain_1:42') + rule: self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*$') - message: GTID for type 'skip' must be a valid MySQL GTID set (e.g. - 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10' or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1-5') - rule: self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+(-[0-9]+)?(:[0-9]+(-[0-9]+)?)*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+(-[0-9]+)?(:[0-9]+(-[0-9]+)?)*)*$') + 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10', 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:Domain_1:1-10', + or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1-5') + rule: self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?)(:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?))*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?)(:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?))* + )*$') pxcCluster: type: string resources: diff --git a/pkg/apis/pxc/v1/pxc_prestore_types.go b/pkg/apis/pxc/v1/pxc_prestore_types.go index d981f212f9..861c6a562d 100644 --- a/pkg/apis/pxc/v1/pxc_prestore_types.go +++ b/pkg/apis/pxc/v1/pxc_prestore_types.go @@ -37,8 +37,8 @@ const ( // +kubebuilder:validation:XValidation:rule="self.type != 'date' || (has(self.date) && self.date.matches('^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]) ([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$'))",message="Date is required for type 'date' and should be in format YYYY-MM-DD HH:MM:SS with valid ranges (MM: 01-12, DD: 01-31, HH: 00-23, MM/SS: 00-59)" // +kubebuilder:validation:XValidation:rule="(self.type != 'transaction' && self.type != 'skip') || (has(self.gtid) && size(self.gtid) > 0)",message="GTID is required for types 'transaction' and 'skip'" // +kubebuilder:validation:XValidation:rule="self.type != 'latest' || ((!has(self.date) || size(self.date) == 0) && (!has(self.gtid) || size(self.gtid) == 0))",message="Date and GTID should not be set when type is 'latest'" -// +kubebuilder:validation:XValidation:rule="self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+$')",message="GTID for type 'transaction' must be a single transaction identifier in the form 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:N' (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42')" -// +kubebuilder:validation:XValidation:rule="self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+(-[0-9]+)?(:[0-9]+(-[0-9]+)?)*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[0-9]+(-[0-9]+)?(:[0-9]+(-[0-9]+)?)*)*$')",message="GTID for type 'skip' must be a valid MySQL GTID set (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10' or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1-5')" +// +kubebuilder:validation:XValidation:rule="self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[1-9][0-9]*$')",message="GTID for type 'transaction' must be a single transaction identifier in the form 'UUID:N' (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42')" +// +kubebuilder:validation:XValidation:rule="self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*(-[1-9][0-9]*)?(:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*(-[1-9][0-9]*)?)*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*(-[1-9][0-9]*)?(:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*(-[1-9][0-9]*)?)*)*$')",message="GTID for type 'skip' must be a valid MySQL GTID set (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10', 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:Domain_1:1-10', or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1-5')" type PITR struct { BackupSource *PXCBackupStatus `json:"backupSource"` // +kubebuilder:validation:Enum={latest,date,transaction,skip} diff --git a/pkg/controller/pxcrestore/pitr_validation_test.go b/pkg/controller/pxcrestore/pitr_validation_test.go index 42357e9d8e..58bdf3b1ee 100644 --- a/pkg/controller/pxcrestore/pitr_validation_test.go +++ b/pkg/controller/pxcrestore/pitr_validation_test.go @@ -93,13 +93,15 @@ var _ = Describe("PerconaXtraDBClusterRestore PITR CRD validation", Ordered, fun }, Entry("type latest", "valid-latest", &pxcv1.PITR{Type: "latest"}), Entry("type date with valid format", "valid-date", &pxcv1.PITR{Type: "date", Date: "2024-01-15 12:30:00"}), - // transaction: single UUID:N only (recoverer parses it as a single integer) + // transaction: single UUID:N only (recoverer uses SplitN+ParseInt, tagged format UUID:tag:N not supported) Entry("type transaction with single gtid", "valid-transaction", &pxcv1.PITR{Type: "transaction", GTID: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42"}), - // skip: full GTID set syntax is allowed + // skip: full GTID set syntax is allowed, including tags (MySQL 8.4) Entry("type skip with single gtid", "valid-skip-single", &pxcv1.PITR{Type: "skip", GTID: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42"}), Entry("type skip with gtid range", "valid-skip-range", &pxcv1.PITR{Type: "skip", GTID: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10"}), Entry("type skip with multiple intervals", "valid-skip-intervals", &pxcv1.PITR{Type: "skip", GTID: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-5:7-9"}), Entry("type skip with multi-source gtid set", "valid-skip-multi-source", &pxcv1.PITR{Type: "skip", GTID: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1-5"}), + Entry("type skip with tagged gtid", "valid-skip-tagged", &pxcv1.PITR{Type: "skip", GTID: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:Domain_1:1-10"}), + Entry("type skip with multiple tags", "valid-skip-multi-tag", &pxcv1.PITR{Type: "skip", GTID: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:Domain_1:1-3:Domain_2:8-52"}), ) DescribeTable("invalid PITR configurations", @@ -118,11 +120,13 @@ var _ = Describe("PerconaXtraDBClusterRestore PITR CRD validation", Ordered, fun Entry("type latest with gtid set", "invalid-latest-gtid", &pxcv1.PITR{Type: "latest", GTID: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1"}, "Date and GTID should not be set"), // unknown type Entry("unknown type", "invalid-unknown-type", &pxcv1.PITR{Type: "unknown"}, "Unsupported value"), - // transaction: GTID is required and must be UUID:N (single integer only) + // transaction: GTID is required and must be UUID:N or UUID:tag:N (N >= 1) Entry("type transaction without gtid", "invalid-transaction-no-gtid", &pxcv1.PITR{Type: "transaction"}, "GTID is required"), Entry("type transaction with non-uuid gtid", "invalid-transaction-bad-uuid", &pxcv1.PITR{Type: "transaction", GTID: "notauuid:42"}, "single transaction identifier"), Entry("type transaction with gtid range", "invalid-transaction-range", &pxcv1.PITR{Type: "transaction", GTID: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10"}, "single transaction identifier"), Entry("type transaction with multi-source gtid set", "invalid-transaction-multi-source", &pxcv1.PITR{Type: "transaction", GTID: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1"}, "single transaction identifier"), + Entry("type transaction with zero transaction id", "invalid-transaction-zero", &pxcv1.PITR{Type: "transaction", GTID: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:0"}, "single transaction identifier"), + Entry("type transaction with tagged gtid", "invalid-transaction-tagged", &pxcv1.PITR{Type: "transaction", GTID: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:Domain_1:42"}, "single transaction identifier"), // skip: GTID is required and must be a valid MySQL GTID set Entry("type skip without gtid", "invalid-skip-no-gtid", &pxcv1.PITR{Type: "skip"}, "GTID is required"), Entry("type skip with non-uuid gtid", "invalid-skip-bad-uuid", &pxcv1.PITR{Type: "skip", GTID: "notauuid:1-10"}, "valid MySQL GTID set"), From 545eec4a79c7d0567f2a5caf4790b0fbd77aa23b Mon Sep 17 00:00:00 2001 From: Natalia Marukovich Date: Tue, 19 May 2026 09:33:17 +0200 Subject: [PATCH 3/3] fix tests --- .../pxc.percona.com_perconaxtradbclusterrestores.yaml | 8 +++----- deploy/bundle.yaml | 8 +++----- deploy/crd.yaml | 8 +++----- deploy/cw-bundle.yaml | 8 +++----- 4 files changed, 12 insertions(+), 20 deletions(-) diff --git a/config/crd/bases/pxc.percona.com_perconaxtradbclusterrestores.yaml b/config/crd/bases/pxc.percona.com_perconaxtradbclusterrestores.yaml index 37668013b3..15873b9027 100644 --- a/config/crd/bases/pxc.percona.com_perconaxtradbclusterrestores.yaml +++ b/config/crd/bases/pxc.percona.com_perconaxtradbclusterrestores.yaml @@ -572,14 +572,12 @@ spec: rule: self.type != 'latest' || ((!has(self.date) || size(self.date) == 0) && (!has(self.gtid) || size(self.gtid) == 0)) - message: GTID for type 'transaction' must be a single transaction - identifier in the form 'UUID:N' or 'UUID:tag:N' (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42' - or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:Domain_1:42') - rule: self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*$') + identifier in the form 'UUID:N' (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42') + rule: self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[1-9][0-9]*$') - message: GTID for type 'skip' must be a valid MySQL GTID set (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10', 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:Domain_1:1-10', or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1-5') - rule: self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?)(:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?))*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?)(:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?))* - )*$') + rule: self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*(-[1-9][0-9]*)?(:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*(-[1-9][0-9]*)?)*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*(-[1-9][0-9]*)?(:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*(-[1-9][0-9]*)?)*)*$') pxcCluster: type: string resources: diff --git a/deploy/bundle.yaml b/deploy/bundle.yaml index 56faaa3d81..7c6735a3cf 100644 --- a/deploy/bundle.yaml +++ b/deploy/bundle.yaml @@ -961,14 +961,12 @@ spec: rule: self.type != 'latest' || ((!has(self.date) || size(self.date) == 0) && (!has(self.gtid) || size(self.gtid) == 0)) - message: GTID for type 'transaction' must be a single transaction - identifier in the form 'UUID:N' or 'UUID:tag:N' (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42' - or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:Domain_1:42') - rule: self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*$') + identifier in the form 'UUID:N' (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42') + rule: self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[1-9][0-9]*$') - message: GTID for type 'skip' must be a valid MySQL GTID set (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10', 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:Domain_1:1-10', or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1-5') - rule: self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?)(:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?))*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?)(:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?))* - )*$') + rule: self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*(-[1-9][0-9]*)?(:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*(-[1-9][0-9]*)?)*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*(-[1-9][0-9]*)?(:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*(-[1-9][0-9]*)?)*)*$') pxcCluster: type: string resources: diff --git a/deploy/crd.yaml b/deploy/crd.yaml index 22d98842f8..cb551ffa79 100644 --- a/deploy/crd.yaml +++ b/deploy/crd.yaml @@ -961,14 +961,12 @@ spec: rule: self.type != 'latest' || ((!has(self.date) || size(self.date) == 0) && (!has(self.gtid) || size(self.gtid) == 0)) - message: GTID for type 'transaction' must be a single transaction - identifier in the form 'UUID:N' or 'UUID:tag:N' (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42' - or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:Domain_1:42') - rule: self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*$') + identifier in the form 'UUID:N' (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42') + rule: self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[1-9][0-9]*$') - message: GTID for type 'skip' must be a valid MySQL GTID set (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10', 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:Domain_1:1-10', or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1-5') - rule: self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?)(:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?))*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?)(:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?))* - )*$') + rule: self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*(-[1-9][0-9]*)?(:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*(-[1-9][0-9]*)?)*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*(-[1-9][0-9]*)?(:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*(-[1-9][0-9]*)?)*)*$') pxcCluster: type: string resources: diff --git a/deploy/cw-bundle.yaml b/deploy/cw-bundle.yaml index bedf54894b..98e7a0c7f7 100644 --- a/deploy/cw-bundle.yaml +++ b/deploy/cw-bundle.yaml @@ -961,14 +961,12 @@ spec: rule: self.type != 'latest' || ((!has(self.date) || size(self.date) == 0) && (!has(self.gtid) || size(self.gtid) == 0)) - message: GTID for type 'transaction' must be a single transaction - identifier in the form 'UUID:N' or 'UUID:tag:N' (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42' - or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:Domain_1:42') - rule: self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*$') + identifier in the form 'UUID:N' (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:42') + rule: self.type != 'transaction' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:[1-9][0-9]*$') - message: GTID for type 'skip' must be a valid MySQL GTID set (e.g. 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-10', 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:Domain_1:1-10', or 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1,bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee:1-5') - rule: self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?)(:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?))*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?)(:([a-zA-Z_][a-zA-Z0-9_]{0,31}|[1-9][0-9]*(-[1-9][0-9]*)?))* - )*$') + rule: self.type != 'skip' || self.gtid.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*(-[1-9][0-9]*)?(:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*(-[1-9][0-9]*)?)*(,[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*(-[1-9][0-9]*)?(:([a-zA-Z_][a-zA-Z0-9_]{0,31}:)?[1-9][0-9]*(-[1-9][0-9]*)?)*)*$') pxcCluster: type: string resources: