| package filepath_test |
| |
| import ( |
| "os" |
| path "path/filepath" |
| "testing" |
| |
| "github.com/apache/cloudberry-go-libs/cluster" |
| "github.com/apache/cloudberry-go-libs/operating" |
| "github.com/apache/cloudberry-go-libs/testhelper" |
| |
| . "github.com/apache/cloudberry-backup/filepath" |
| . "github.com/onsi/ginkgo/v2" |
| . "github.com/onsi/gomega" |
| ) |
| |
| func TestBackupFilepath(t *testing.T) { |
| RegisterFailHandler(Fail) |
| RunSpecs(t, "Filepath Suite") |
| } |
| |
| var _ = BeforeSuite(func() { |
| _, _, _ = testhelper.SetupTestLogger() |
| operating.System = operating.InitializeSystemFunctions() |
| }) |
| |
| var _ = Describe("filepath tests", func() { |
| coordinatorDir := "/data/gpseg-1" |
| standbyDir := "/data/gpseg_mirror-1" |
| segDirOne := "/data/gpseg0" |
| segDirTwo := "/data/gpseg1" |
| mirrorDirOne := "/data/gpseg_mirror0" |
| mirrorDirTwo := "/data/gpseg_mirror1" |
| |
| var c *cluster.Cluster |
| BeforeEach(func() { |
| c = cluster.NewCluster([]cluster.SegConfig{ |
| {ContentID: -1, DataDir: coordinatorDir}, |
| }) |
| }) |
| Describe("Backup Filepath setup and accessors", func() { |
| It("returns content dir for a single-host, single-segment nodes", func() { |
| c = cluster.NewCluster([]cluster.SegConfig{ |
| {ContentID: -1, DataDir: coordinatorDir}, |
| {ContentID: 0, DataDir: segDirOne}, |
| }) |
| fpInfo := NewFilePathInfo(c, "", "20170101010101", "gpseg", false) |
| Expect(fpInfo.SegDirMap).To(HaveLen(2)) |
| Expect(fpInfo.GetDirForContent(-1)).To(Equal("/data/gpseg-1/backups/20170101/20170101010101")) |
| Expect(fpInfo.GetDirForContent(0)).To(Equal("/data/gpseg0/backups/20170101/20170101010101")) |
| }) |
| It("sets up the configuration for a single-host, multi-segment fpInfo", func() { |
| c = cluster.NewCluster([]cluster.SegConfig{ |
| {ContentID: -1, DataDir: coordinatorDir}, |
| {ContentID: 0, DataDir: segDirOne}, |
| {ContentID: 1, DataDir: segDirTwo}, |
| }) |
| fpInfo := NewFilePathInfo(c, "", "20170101010101", "gpseg", false) |
| Expect(fpInfo.SegDirMap).To(HaveLen(3)) |
| Expect(fpInfo.GetDirForContent(-1)).To(Equal("/data/gpseg-1/backups/20170101/20170101010101")) |
| Expect(fpInfo.GetDirForContent(0)).To(Equal("/data/gpseg0/backups/20170101/20170101010101")) |
| Expect(fpInfo.GetDirForContent(1)).To(Equal("/data/gpseg1/backups/20170101/20170101010101")) |
| }) |
| It("sets up the configuration for a single-host, multi-segment fpInfo using primaries when the cluster has mirrors", func() { |
| c = cluster.NewCluster([]cluster.SegConfig{ |
| {ContentID: -1, Role: "p", DataDir: coordinatorDir}, |
| {ContentID: 0, Role: "p", DataDir: segDirOne}, |
| {ContentID: 1, Role: "p", DataDir: segDirTwo}, |
| {ContentID: 0, Role: "m", DataDir: mirrorDirOne}, |
| {ContentID: 1, Role: "m", DataDir: mirrorDirTwo}, |
| {ContentID: -1, Role: "m", DataDir: standbyDir}, |
| }) |
| fpInfo := NewFilePathInfo(c, "", "20170101010101", "gpseg", false) |
| Expect(fpInfo.SegDirMap).To(HaveLen(3)) |
| Expect(fpInfo.GetDirForContent(-1)).To(Equal("/data/gpseg-1/backups/20170101/20170101010101")) |
| Expect(fpInfo.GetDirForContent(0)).To(Equal("/data/gpseg0/backups/20170101/20170101010101")) |
| Expect(fpInfo.GetDirForContent(1)).To(Equal("/data/gpseg1/backups/20170101/20170101010101")) |
| }) |
| It("sets up the configuration for a single-host, multi-segment fpInfo using mirrors", func() { |
| c = cluster.NewCluster([]cluster.SegConfig{ |
| {ContentID: -1, Role: "p", DataDir: coordinatorDir}, |
| {ContentID: 0, Role: "p", DataDir: segDirOne}, |
| {ContentID: 1, Role: "p", DataDir: segDirTwo}, |
| {ContentID: 0, Role: "m", DataDir: mirrorDirOne}, |
| {ContentID: 1, Role: "m", DataDir: mirrorDirTwo}, |
| {ContentID: -1, Role: "m", DataDir: standbyDir}, |
| }) |
| fpInfo := NewFilePathInfo(c, "", "20170101010101", "gpseg", false, true) |
| Expect(fpInfo.SegDirMap).To(HaveLen(3)) |
| Expect(fpInfo.GetDirForContent(-1)).To(Equal("/data/gpseg_mirror-1/backups/20170101/20170101010101")) |
| Expect(fpInfo.GetDirForContent(0)).To(Equal("/data/gpseg_mirror0/backups/20170101/20170101010101")) |
| Expect(fpInfo.GetDirForContent(1)).To(Equal("/data/gpseg_mirror1/backups/20170101/20170101010101")) |
| }) |
| It("returns the content directory based on the user specified path", func() { |
| fpInfo := NewFilePathInfo(c, "/foo/bar", "20170101010101", "gpseg", false) |
| Expect(fpInfo.SegDirMap).To(HaveLen(1)) |
| Expect(fpInfo.GetDirForContent(-1)).To(Equal("/foo/bar/gpseg-1/backups/20170101/20170101010101")) |
| }) |
| }) |
| Describe("GetTableBackupFilePathForCopyCommand()", func() { |
| It("returns table file path for copy command", func() { |
| fpInfo := NewFilePathInfo(c, "", "20170101010101", "gpseg", false) |
| Expect(fpInfo.GetTableBackupFilePathForCopyCommand(1234, "", false)).To(Equal("<SEG_DATA_DIR>/backups/20170101/20170101010101/gpbackup_<SEGID>_20170101010101_1234")) |
| }) |
| It("returns table file path for copy command based on user specified path", func() { |
| fpInfo := NewFilePathInfo(c, "/foo/bar", "20170101010101", "gpseg", false) |
| Expect(fpInfo.GetTableBackupFilePathForCopyCommand(1234, "", false)).To(Equal("/foo/bar/gpseg<SEGID>/backups/20170101/20170101010101/gpbackup_<SEGID>_20170101010101_1234")) |
| }) |
| It("returns table file path for copy command in single-file mode", func() { |
| fpInfo := NewFilePathInfo(c, "", "20170101010101", "gpseg", false) |
| Expect(fpInfo.GetTableBackupFilePathForCopyCommand(1234, "", true)).To(Equal("<SEG_DATA_DIR>/backups/20170101/20170101010101/gpbackup_<SEGID>_20170101010101")) |
| }) |
| It("returns table file path for copy command based on user specified path in single-file mode", func() { |
| fpInfo := NewFilePathInfo(c, "/foo/bar", "20170101010101", "gpseg", false) |
| Expect(fpInfo.GetTableBackupFilePathForCopyCommand(1234, "", true)).To(Equal("/foo/bar/gpseg<SEGID>/backups/20170101/20170101010101/gpbackup_<SEGID>_20170101010101")) |
| }) |
| It("returns table file path for copy command with extension", func() { |
| fpInfo := NewFilePathInfo(c, "", "20170101010101", "gpseg", false) |
| Expect(fpInfo.GetTableBackupFilePathForCopyCommand(1234, ".gzip", false)).To(Equal("<SEG_DATA_DIR>/backups/20170101/20170101010101/gpbackup_<SEGID>_20170101010101_1234.gzip")) |
| }) |
| It("returns table file path for copy command based on user specified path with extension", func() { |
| fpInfo := NewFilePathInfo(c, "/foo/bar", "20170101010101", "gpseg", false) |
| Expect(fpInfo.GetTableBackupFilePathForCopyCommand(1234, ".gzip", false)).To(Equal("/foo/bar/gpseg<SEGID>/backups/20170101/20170101010101/gpbackup_<SEGID>_20170101010101_1234.gzip")) |
| }) |
| It("returns table file path for copy command in single-file mode with extension", func() { |
| fpInfo := NewFilePathInfo(c, "", "20170101010101", "gpseg", false) |
| Expect(fpInfo.GetTableBackupFilePathForCopyCommand(1234, ".gzip", true)).To(Equal("<SEG_DATA_DIR>/backups/20170101/20170101010101/gpbackup_<SEGID>_20170101010101.gzip")) |
| }) |
| It("returns table file path for copy command based on user specified path in single-file mode with extension", func() { |
| fpInfo := NewFilePathInfo(c, "/foo/bar", "20170101010101", "gpseg", false) |
| Expect(fpInfo.GetTableBackupFilePathForCopyCommand(1234, ".gzip", true)).To(Equal("/foo/bar/gpseg<SEGID>/backups/20170101/20170101010101/gpbackup_<SEGID>_20170101010101.gzip")) |
| }) |
| }) |
| Describe("GetReportFilePath", func() { |
| It("returns report file path", func() { |
| fpInfo := NewFilePathInfo(c, "", "20170101010101", "gpseg", false) |
| Expect(fpInfo.GetBackupReportFilePath()).To(Equal("/data/gpseg-1/backups/20170101/20170101010101/gpbackup_20170101010101_report")) |
| }) |
| It("returns report file path based on user specified path", func() { |
| fpInfo := NewFilePathInfo(c, "/foo/bar", "20170101010101", "gpseg", false) |
| Expect(fpInfo.GetBackupReportFilePath()).To(Equal("/foo/bar/gpseg-1/backups/20170101/20170101010101/gpbackup_20170101010101_report")) |
| }) |
| It("returns report file path for restore command", func() { |
| fpInfo := NewFilePathInfo(c, "", "20170101010101", "gpseg", false) |
| Expect(fpInfo.GetRestoreReportFilePath("20200101010101")).To(Equal("/data/gpseg-1/backups/20170101/20170101010101/gprestore_20170101010101_20200101010101_report")) |
| }) |
| It("returns report file path based on user specified path for restore command", func() { |
| fpInfo := NewFilePathInfo(c, "/foo/bar", "20170101010101", "gpseg", false) |
| Expect(fpInfo.GetRestoreReportFilePath("20200101010101")).To(Equal("/foo/bar/gpseg-1/backups/20170101/20170101010101/gprestore_20170101010101_20200101010101_report")) |
| }) |
| It("returns different report file paths based on user specified report path for backup and restore command", func() { |
| fpInfo := NewFilePathInfo(c, "/foo/bar", "20170101010101", "gpseg", false) |
| fpInfo.SingleBackupDir = true |
| fpInfo.UserSpecifiedReportDir = "/bar/foo" |
| Expect(fpInfo.GetBackupReportFilePath()).To(Equal("/foo/bar/gpseg-1/backups/20170101/20170101010101/gpbackup_20170101010101_report")) |
| Expect(fpInfo.GetRestoreReportFilePath("20200101010101")).To(Equal("/bar/foo/gprestore_20170101010101_20200101010101_report")) |
| fpInfo.SingleBackupDir = false |
| Expect(fpInfo.GetRestoreReportFilePath("20200101010101")).To(Equal("/bar/foo/gpseg-1/backups/20170101/20170101010101/gprestore_20170101010101_20200101010101_report")) |
| }) |
| }) |
| Describe("GetTableBackupFilePath", func() { |
| It("returns table file path", func() { |
| fpInfo := NewFilePathInfo(c, "", "20170101010101", "gpseg", false) |
| Expect(fpInfo.GetTableBackupFilePath(-1, 1234, "", false)).To(Equal("/data/gpseg-1/backups/20170101/20170101010101/gpbackup_-1_20170101010101_1234")) |
| }) |
| It("returns table file path based on user specified path", func() { |
| fpInfo := NewFilePathInfo(c, "/foo/bar", "20170101010101", "gpseg", false) |
| Expect(fpInfo.GetTableBackupFilePath(-1, 1234, "", false)).To(Equal("/foo/bar/gpseg-1/backups/20170101/20170101010101/gpbackup_-1_20170101010101_1234")) |
| }) |
| It("returns single data file path", func() { |
| fpInfo := NewFilePathInfo(c, "", "20170101010101", "gpseg", false) |
| Expect(fpInfo.GetTableBackupFilePath(-1, 1234, "", true)).To(Equal("/data/gpseg-1/backups/20170101/20170101010101/gpbackup_-1_20170101010101")) |
| }) |
| It("returns single data file path based on user specified path", func() { |
| fpInfo := NewFilePathInfo(c, "/foo/bar", "20170101010101", "gpseg", false) |
| Expect(fpInfo.GetTableBackupFilePath(-1, 1234, "", true)).To(Equal("/foo/bar/gpseg-1/backups/20170101/20170101010101/gpbackup_-1_20170101010101")) |
| }) |
| }) |
| Describe("ParseSegPrefix", func() { |
| AfterEach(func() { |
| operating.System.Glob = path.Glob |
| }) |
| It("returns segment prefix from directory path if coordinator backup directory exists", func() { |
| operating.System.Glob = func(pattern string) (matches []string, err error) { |
| return []string{"/tmp/foo/gpseg-1/backups"}, nil |
| } |
| res, _, err := ParseSegPrefix("/tmp/foo", "00000000000000") |
| Expect(err).ToNot(HaveOccurred()) |
| Expect(res).To(Equal("gpseg")) |
| }) |
| It("returns empty string if backup directory is empty", func() { |
| res, _, err := ParseSegPrefix("", "00000000000000") |
| Expect(err).ToNot(HaveOccurred()) |
| Expect(res).To(Equal("")) |
| }) |
| It("returns an error when coordinator backup directory does not exist", func() { |
| operating.System.Glob = func(pattern string) (matches []string, err error) { return []string{}, nil } |
| _, _, err := ParseSegPrefix("/tmp/foo", "00000000000000") |
| Expect(err.Error()).To(Equal("Backup directory in /tmp/foo missing")) |
| }) |
| It("returns an error when there is an error accessing coordinator backup directory", func() { |
| operating.System.Glob = func(pattern string) (matches []string, err error) { |
| return []string{""}, os.ErrPermission |
| } |
| _, _, err := ParseSegPrefix("/tmp/foo", "00000000000000") |
| Expect(err.Error()).To(Equal("Failure while trying to locate backup directory in /tmp/foo. Error: permission denied")) |
| }) |
| It("returns an error when multiple coordinator backup directories", func() { |
| operating.System.Glob = func(pattern string) (matches []string, err error) { |
| if pattern == "/tmp/foo/*-1/backups" { |
| return []string{"/tmp/foo/foo-1/backups", "/tmp/foo/foo-1/backups"}, nil |
| } else { |
| return []string{}, nil |
| } |
| } |
| _, _, err := ParseSegPrefix("/tmp/foo", "00000000000000") |
| Expect(err.Error()).To(Equal("Multiple backup directories in /tmp/foo")) |
| }) |
| Describe("IsValidTimestamp", func() { |
| It("allows a valid timestamp", func() { |
| timestamp := "20170101010101" |
| isValid := IsValidTimestamp(timestamp) |
| Expect(isValid).To(BeTrue()) |
| }) |
| It("invalidates a non-numeric timestamp", func() { |
| timestamp := "2017ababababab" |
| isValid := IsValidTimestamp(timestamp) |
| Expect(isValid).To(BeFalse()) |
| }) |
| It("invalidates a timestamp that is too short", func() { |
| timestamp := "201701010101" |
| isValid := IsValidTimestamp(timestamp) |
| Expect(isValid).To(BeFalse()) |
| }) |
| It("invalidates a timestamp that is too long", func() { |
| timestamp := "2017010101010101" |
| isValid := IsValidTimestamp(timestamp) |
| Expect(isValid).To(BeFalse()) |
| }) |
| }) |
| }) |
| }) |