diff --git a/api/v1beta2/mysqlcluster_webhook_test.go b/api/v1beta2/mysqlcluster_webhook_test.go index 489aaaff..683b3098 100644 --- a/api/v1beta2/mysqlcluster_webhook_test.go +++ b/api/v1beta2/mysqlcluster_webhook_test.go @@ -423,6 +423,7 @@ var _ = Describe("MySQLCluster Webhook", func() { EndpointURL: "https://foo.bar.svc:9000", }, }, + IncludeSchemas: []string{"db1", "db2"}, } err := k8sClient.Create(ctx, r) Expect(err).NotTo(HaveOccurred()) diff --git a/backup/backup_test.go b/backup/backup_test.go index b85b29fb..9e12eba2 100644 --- a/backup/backup_test.go +++ b/backup/backup_test.go @@ -49,7 +49,7 @@ func (o *getUUIDSetMockOp) PrepareRestore(_ context.Context) error { panic("not implemented") } -func (o *getUUIDSetMockOp) LoadDump(ctx context.Context, dir string) error { +func (o *getUUIDSetMockOp) LoadDump(ctx context.Context, dir string, includeSchemas []string) error { panic("not implemented") } diff --git a/backup/integration_test.go b/backup/integration_test.go index d113b004..add1aecb 100644 --- a/backup/integration_test.go +++ b/backup/integration_test.go @@ -151,7 +151,7 @@ var _ = Describe("Backup/Restore", func() { Expect(bs.WorkDirUsage).To(BeNumerically(">", 0)) Expect(bs.Warnings).To(BeEmpty()) - rm, err := NewRestoreManager(cfg, bc, workDir2, "test", "single", "restore", "target", "", 3, bs.Time.Time) + rm, err := NewRestoreManager(cfg, bc, workDir2, "test", "single", "restore", "target", "", 3, bs.Time.Time, []string{}) Expect(err).NotTo(HaveOccurred()) ctx2, cancel := context.WithTimeout(ctx, 3*time.Second) @@ -240,7 +240,7 @@ var _ = Describe("Backup/Restore", func() { Expect(bs.WorkDirUsage).To(BeNumerically(">", 0)) Expect(bs.Warnings).To(BeEmpty()) - rm, err := NewRestoreManager(cfg, bc, workDir2, "test", "single", "restore", "target", "", 3, restorePoint) + rm, err := NewRestoreManager(cfg, bc, workDir2, "test", "single", "restore", "target", "", 3, restorePoint, []string{}) Expect(err).NotTo(HaveOccurred()) err = rm.Restore(ctx) @@ -292,7 +292,7 @@ var _ = Describe("Backup/Restore", func() { Expect(err).NotTo(HaveOccurred()) Expect(bc.contents).To(HaveLen(3)) - rm, err := NewRestoreManager(cfg, bc, workDir2, "test", "single", "restore", "target", "", 3, bt) + rm, err := NewRestoreManager(cfg, bc, workDir2, "test", "single", "restore", "target", "", 3, bt, []string{}) Expect(err).NotTo(HaveOccurred()) err = rm.Restore(ctx) diff --git a/backup/mock_test.go b/backup/mock_test.go index c07b5c9c..75f3a064 100644 --- a/backup/mock_test.go +++ b/backup/mock_test.go @@ -97,7 +97,7 @@ func (o *mockOperator) PrepareRestore(_ context.Context) error { return nil } -func (o *mockOperator) LoadDump(ctx context.Context, dir string) error { +func (o *mockOperator) LoadDump(ctx context.Context, dir string, includeSchemas []string) error { if !o.prepared { return errors.New("not prepared") } diff --git a/controllers/mysqlcluster_controller_test.go b/controllers/mysqlcluster_controller_test.go index 1efffdfa..eeb56590 100644 --- a/controllers/mysqlcluster_controller_test.go +++ b/controllers/mysqlcluster_controller_test.go @@ -1560,6 +1560,7 @@ var _ = Describe("MySQLCluster reconciler", func() { "test", "test", now.UTC().Format(constants.BackupTimeFormat), + "", })) Expect(c.EnvFrom).To(HaveLen(1)) Expect(c.Env).To(HaveLen(2)) diff --git a/e2e/backup_test.go b/e2e/backup_test.go index aa0772e5..3721a075 100644 --- a/e2e/backup_test.go +++ b/e2e/backup_test.go @@ -23,8 +23,11 @@ var makeBucketYAML string //go:embed testdata/backup.yaml var backupYAML string -//go:embed testdata/restore.yaml -var restoreYAML string +//go:embed testdata/restore1.yaml +var restore1YAML string + +//go:embed testdata/restore2.yaml +var restore2YAML string var _ = Context("backup", func() { if doUpgrade { @@ -58,11 +61,17 @@ var _ = Context("backup", func() { }).Should(Succeed()) kubectlSafe(nil, "moco", "-n", "backup", "mysql", "-u", "moco-writable", "source", "--", - "-e", "CREATE DATABASE test") + "-e", "CREATE DATABASE test1") + kubectlSafe(nil, "moco", "-n", "backup", "mysql", "-u", "moco-writable", "source", "--", + "-D", "test1", "-e", "CREATE TABLE t (id INT NOT NULL AUTO_INCREMENT, data VARCHAR(32) NOT NULL, PRIMARY KEY (id), KEY key1 (data), KEY key2 (data, id)) ENGINE=InnoDB") + kubectlSafe(nil, "moco", "-n", "backup", "mysql", "-u", "moco-writable", "source", "--", + "-D", "test1", "--init_command=SET autocommit=1", "-e", "INSERT INTO t (data) VALUES ('aaa')") kubectlSafe(nil, "moco", "-n", "backup", "mysql", "-u", "moco-writable", "source", "--", - "-D", "test", "-e", "CREATE TABLE t (id INT NOT NULL AUTO_INCREMENT, data VARCHAR(32) NOT NULL, PRIMARY KEY (id), KEY key1 (data), KEY key2 (data, id)) ENGINE=InnoDB") + "-e", "CREATE DATABASE test2") kubectlSafe(nil, "moco", "-n", "backup", "mysql", "-u", "moco-writable", "source", "--", - "-D", "test", "--init_command=SET autocommit=1", "-e", "INSERT INTO t (data) VALUES ('aaa')") + "-D", "test2", "-e", "CREATE TABLE t (id INT NOT NULL AUTO_INCREMENT, data VARCHAR(32) NOT NULL, PRIMARY KEY (id), KEY key1 (data), KEY key2 (data, id)) ENGINE=InnoDB") + kubectlSafe(nil, "moco", "-n", "backup", "mysql", "-u", "moco-writable", "source", "--", + "-D", "test2", "--init_command=SET autocommit=1", "-e", "INSERT INTO t (data) VALUES ('aaa')") }) It("should take a full dump", func() { @@ -81,14 +90,14 @@ var _ = Context("backup", func() { It("should take an incremental backup", func() { kubectlSafe(nil, "moco", "-n", "backup", "mysql", "-u", "moco-writable", "source", "--", - "-D", "test", "--init_command=SET autocommit=1", "-e", "INSERT INTO t (data) VALUES ('bbb')") + "-D", "test1", "--init_command=SET autocommit=1", "-e", "INSERT INTO t (data) VALUES ('bbb')") time.Sleep(1100 * time.Millisecond) restorePoint = time.Now().UTC() time.Sleep(1100 * time.Millisecond) kubectlSafe(nil, "moco", "-n", "backup", "mysql", "-u", "moco-admin", "source", "--", - "-D", "test", "--init_command=SET autocommit=1", "-e", "FLUSH LOCAL BINARY LOGS") + "-D", "test1", "--init_command=SET autocommit=1", "-e", "FLUSH LOCAL BINARY LOGS") kubectlSafe(nil, "moco", "-n", "backup", "mysql", "-u", "moco-writable", "source", "--", - "-D", "test", "--init_command=SET autocommit=1", "-e", "INSERT INTO t (data) VALUES ('ccc')") + "-D", "test1", "--init_command=SET autocommit=1", "-e", "INSERT INTO t (data) VALUES ('ccc')") time.Sleep(100 * time.Millisecond) kubectlSafe(nil, "-n", "backup", "create", "job", "--from=cronjob/moco-backup-source", "backup-2") @@ -111,7 +120,42 @@ var _ = Context("backup", func() { It("should destroy the source then restore the backup data", func() { kubectlSafe(nil, "-n", "backup", "delete", "mysqlclusters", "source") - tmpl, err := template.New("").Parse(restoreYAML) + tmpl, err := template.New("").Parse(restore1YAML) + Expect(err).NotTo(HaveOccurred()) + buf := new(bytes.Buffer) + err = tmpl.Execute(buf, struct { + MySQLVersion string + RestorePoint string + }{ + mysqlVersion, + restorePoint.Format(time.RFC3339), + }) + Expect(err).NotTo(HaveOccurred()) + + kubectlSafe(buf.Bytes(), "apply", "-f", "-") + Eventually(func(g Gomega) { + cluster, err := getCluster("backup", "target1") + g.Expect(err).NotTo(HaveOccurred()) + condHealthy, err := getClusterCondition(cluster, mocov1beta2.ConditionHealthy) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(condHealthy.Status).To(Equal(metav1.ConditionTrue), "target1 is not healthy") + }).Should(Succeed()) + + out := kubectlSafe(nil, "moco", "-n", "backup", "mysql", "target1", "--", + "-N", "-D", "test1", "-e", "SELECT COUNT(*) FROM t") + count, err := strconv.Atoi(strings.TrimSpace(string(out))) + Expect(err).NotTo(HaveOccurred()) + Expect(count).To(Equal(2)) + + out = kubectlSafe(nil, "moco", "-n", "backup", "mysql", "target1", "--", + "-N", "-e", "SHOW DATABASES LIKE 'test%'") + count, err = strconv.Atoi(strings.TrimSpace(string(out))) + Expect(err).NotTo(HaveOccurred()) + Expect(count).To(Equal(2)) + }) + + It("should restore only test2 schema", func() { + tmpl, err := template.New("").Parse(restore2YAML) Expect(err).NotTo(HaveOccurred()) buf := new(bytes.Buffer) err = tmpl.Execute(buf, struct { @@ -125,18 +169,30 @@ var _ = Context("backup", func() { kubectlSafe(buf.Bytes(), "apply", "-f", "-") Eventually(func(g Gomega) { - cluster, err := getCluster("backup", "target") + cluster, err := getCluster("backup", "target2") g.Expect(err).NotTo(HaveOccurred()) condHealthy, err := getClusterCondition(cluster, mocov1beta2.ConditionHealthy) g.Expect(err).NotTo(HaveOccurred()) - g.Expect(condHealthy.Status).To(Equal(metav1.ConditionTrue), "target is not healthy") + g.Expect(condHealthy.Status).To(Equal(metav1.ConditionTrue), "target2 is not healthy") }).Should(Succeed()) - out := kubectlSafe(nil, "moco", "-n", "backup", "mysql", "target", "--", - "-N", "-D", "test", "-e", "SELECT COUNT(*) FROM t") + out := kubectlSafe(nil, "moco", "-n", "backup", "mysql", "target2", "--", + "-N", "-D", "test2", "-e", "SELECT COUNT(*) FROM t") count, err := strconv.Atoi(strings.TrimSpace(string(out))) Expect(err).NotTo(HaveOccurred()) Expect(count).To(Equal(2)) + + out = kubectlSafe(nil, "moco", "-n", "backup", "mysql", "target1", "--", + "-N", "-e", "SHOW DATABASES LIKE 'test%'") + count, err = strconv.Atoi(strings.TrimSpace(string(out))) + Expect(err).NotTo(HaveOccurred()) + Expect(count).To(Equal(1)) + + out = kubectlSafe(nil, "moco", "-n", "backup", "mysql", "target1", "--", + "-N", "-e", "SHOW DATABASES WHERE `Database` = 'test2'") + count, err = strconv.Atoi(strings.TrimSpace(string(out))) + Expect(err).NotTo(HaveOccurred()) + Expect(count).To(Equal(1)) }) It("should delete clusters", func() { diff --git a/e2e/testdata/restore.yaml b/e2e/testdata/restore1.yaml similarity index 98% rename from e2e/testdata/restore.yaml rename to e2e/testdata/restore1.yaml index 79835305..d77c0182 100644 --- a/e2e/testdata/restore.yaml +++ b/e2e/testdata/restore1.yaml @@ -2,7 +2,7 @@ apiVersion: moco.cybozu.com/v1beta2 kind: MySQLCluster metadata: namespace: backup - name: target + name: target1 spec: mysqlConfigMapName: mycnf replicas: 1 diff --git a/e2e/testdata/restore2.yaml b/e2e/testdata/restore2.yaml new file mode 100644 index 00000000..bfe1dda1 --- /dev/null +++ b/e2e/testdata/restore2.yaml @@ -0,0 +1,41 @@ +apiVersion: moco.cybozu.com/v1beta2 +kind: MySQLCluster +metadata: + namespace: backup + name: target2 +spec: + mysqlConfigMapName: mycnf + replicas: 1 + restore: + sourceName: source + sourceNamespace: backup + restorePoint: "{{ .RestorePoint }}" + IncludeSchemas: ["test2"] + jobConfig: + serviceAccountName: backup-owner + env: + - name: AWS_ACCESS_KEY_ID + value: minioadmin + - name: AWS_SECRET_ACCESS_KEY + value: minioadmin + - name: AWS_REGION + value: us-east-1 + bucketConfig: + bucketName: moco + endpointURL: http://minio.default.svc:9000 + usePathStyle: true + workVolume: + emptyDir: {} + podTemplate: + spec: + containers: + - name: mysqld + image: ghcr.io/cybozu-go/moco/mysql:{{ .MySQLVersion }} + volumeClaimTemplates: + - metadata: + name: mysql-data + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi diff --git a/pkg/bkop/operator_test.go b/pkg/bkop/operator_test.go index d173355d..71ac9d0e 100644 --- a/pkg/bkop/operator_test.go +++ b/pkg/bkop/operator_test.go @@ -168,7 +168,7 @@ var _ = Describe("Operator", func() { err = opRe.PrepareRestore(ctx) Expect(err).NotTo(HaveOccurred()) - err = opRe.LoadDump(ctx, dumpDir) + err = opRe.LoadDump(ctx, dumpDir, []string{}) Expect(err).NotTo(HaveOccurred()) var restoredGTID string