github.com/cloudberrydb/gpbackup@v1.0.3-0.20240118031043-5410fd45eed6/utils/agent_remote_test.go (about) 1 package utils_test 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 8 "github.com/cloudberrydb/gp-common-go-libs/cluster" 9 "github.com/cloudberrydb/gp-common-go-libs/operating" 10 "github.com/cloudberrydb/gp-common-go-libs/testhelper" 11 "github.com/cloudberrydb/gpbackup/filepath" 12 "github.com/cloudberrydb/gpbackup/utils" 13 "github.com/pkg/errors" 14 15 . "github.com/onsi/ginkgo/v2" 16 . "github.com/onsi/gomega" 17 ) 18 19 var _ = Describe("agent remote", func() { 20 var ( 21 oidList []string 22 fpInfo filepath.FilePathInfo 23 testCluster *cluster.Cluster 24 testExecutor *testhelper.TestExecutor 25 remoteOutput *cluster.RemoteOutput 26 ) 27 BeforeEach(func() { 28 oidList = []string{"1", "2", "3"} 29 operating.System.OpenFileWrite = func(name string, flag int, perm os.FileMode) (io.WriteCloser, error) { 30 return buffer, nil 31 } 32 33 // Setup test cluster 34 coordinatorSeg := cluster.SegConfig{ContentID: -1, Hostname: "localhost", DataDir: "/data/gpseg-1"} 35 localSegOne := cluster.SegConfig{ContentID: 0, Hostname: "localhost", DataDir: "/data/gpseg0"} 36 remoteSegOne := cluster.SegConfig{ContentID: 1, Hostname: "remotehost1", DataDir: "/data/gpseg1"} 37 38 testExecutor = &testhelper.TestExecutor{} 39 remoteOutput = &cluster.RemoteOutput{} 40 testExecutor.ClusterOutput = remoteOutput 41 42 testCluster = cluster.NewCluster([]cluster.SegConfig{coordinatorSeg, localSegOne, remoteSegOne}) 43 testCluster.Executor = testExecutor 44 45 fpInfo = filepath.NewFilePathInfo(testCluster, "", "11112233445566", "") 46 }) 47 // note: technically the file system is written to during the call `operating.System.TempFile` 48 // this file is not used throughout the unit tests below, and it is cleaned up with the method: `operating.System.Remove` 49 Describe("WriteOidListToSegments()", func() { 50 It("generates the correct rsync commands to copy oid file to segments", func() { 51 utils.WriteOidListToSegments(oidList, testCluster, fpInfo, "oid") 52 53 Expect(testExecutor.NumExecutions).To(Equal(1)) 54 cc := testExecutor.ClusterCommands[0] 55 Expect(len(cc)).To(Equal(2)) 56 Expect(cc[0].CommandString).To(MatchRegexp("rsync -e ssh .*/gpbackup-oids.* localhost:/data/gpseg0/gpbackup_0_11112233445566_oid_.*")) 57 Expect(cc[1].CommandString).To(MatchRegexp("rsync -e ssh .*/gpbackup-oids.* remotehost1:/data/gpseg1/gpbackup_1_11112233445566_oid_.*")) 58 }) 59 It("panics if any rsync commands fail and outputs correct err messages", func() { 60 testExecutor.ErrorOnExecNum = 1 61 remoteOutput.NumErrors = 1 62 remoteOutput.Scope = cluster.ON_LOCAL & cluster.ON_SEGMENTS 63 remoteOutput.Commands = []cluster.ShellCommand{ 64 cluster.ShellCommand{Content: -1}, 65 cluster.ShellCommand{Content: 0}, 66 cluster.ShellCommand{ 67 Content: 1, 68 CommandString: "rsync -e ssh fake_coordinator fake_host", 69 Stderr: "stderr content 1", 70 Error: errors.New("test error 1"), 71 }, 72 } 73 74 Expect(func() { utils.WriteOidListToSegments(oidList, testCluster, fpInfo, "oid") }).To(Panic()) 75 76 Expect(testExecutor.NumExecutions).To(Equal(1)) 77 Expect(string(logfile.Contents())).To(ContainSubstring(`[CRITICAL]:-Failed to rsync oid file on 1 segment. See gbytes.Buffer for a complete list of errors.`)) 78 }) 79 }) 80 Describe("WriteOidsToFile()", func() { 81 It("writes oid list, delimited by newline characters", func() { 82 utils.WriteOidsToFile("myFilename", oidList) 83 84 Expect(string(buffer.Contents())).To(Equal("1\n2\n3\n")) 85 }) 86 It("panics and prints when it cannot open local oid file", func() { 87 operating.System.OpenFileWrite = func(name string, flag int, perm os.FileMode) (io.WriteCloser, error) { 88 return nil, errors.New("cannot open local oid file") 89 } 90 91 Expect(func() { utils.WriteOidsToFile("filename", oidList) }).To(Panic()) 92 Expect(string(logfile.Contents())).To(ContainSubstring(`cannot open local oid file`)) 93 }) 94 It("panics and prints when it cannot close local oid file", func() { 95 operating.System.OpenFileWrite = func(name string, flag int, perm os.FileMode) (io.WriteCloser, error) { 96 return testWriter{CloseErr: errors.New("cannot close local oid file")}, nil 97 } 98 99 Expect(func() { utils.WriteOidsToFile("filename", oidList) }).To(Panic()) 100 Expect(string(logfile.Contents())).To(ContainSubstring(`cannot close local oid file`)) 101 }) 102 It("panics and prints when WriteOids returns an error", func() { 103 operating.System.OpenFileWrite = func(name string, flag int, perm os.FileMode) (io.WriteCloser, error) { 104 return testWriter{WriteErr: errors.New("WriteOids returned err")}, nil 105 } 106 107 Expect(func() { utils.WriteOidsToFile("filename", oidList) }).To(Panic()) 108 Expect(string(logfile.Contents())).To(ContainSubstring("WriteOids returned err")) 109 }) 110 }) 111 Describe("WriteOids()", func() { 112 It("writes oid list, delimited by newline characters", func() { 113 err := utils.WriteOids(buffer, oidList) 114 Expect(err).ToNot(HaveOccurred()) 115 116 Expect(string(buffer.Contents())).To(Equal("1\n2\n3\n")) 117 }) 118 It("returns an error if it fails to write an oid", func() { 119 tw := testWriter{} 120 tw.WriteErr = errors.New("fail oid write") 121 122 err := utils.WriteOids(tw, oidList) 123 Expect(err).To(Equal(tw.WriteErr)) 124 }) 125 }) 126 Describe("StartGpbackupHelpers()", func() { 127 It("Correctly propagates --on-error-continue flag to gpbackup_helper", func() { 128 wasTerminated := false 129 utils.StartGpbackupHelpers(testCluster, fpInfo, "operation", "/tmp/pluginConfigFile.yml", " compressStr", true, false, &wasTerminated, 1, true, false, 0, 0) 130 131 cc := testExecutor.ClusterCommands[0] 132 Expect(cc[1].CommandString).To(ContainSubstring(" --on-error-continue")) 133 }) 134 It("Correctly propagates --copy-queue-size value to gpbackup_helper", func() { 135 wasTerminated := false 136 utils.StartGpbackupHelpers(testCluster, fpInfo, "operation", "/tmp/pluginConfigFile.yml", " compressStr", false, false, &wasTerminated, 4, true, false, 0, 0) 137 138 cc := testExecutor.ClusterCommands[0] 139 Expect(cc[1].CommandString).To(ContainSubstring(" --copy-queue-size 4")) 140 }) 141 }) 142 Describe("CheckAgentErrorsOnSegments", func() { 143 It("constructs the correct ssh call to check for the existance of an error file on each segment", func() { 144 err := utils.CheckAgentErrorsOnSegments(testCluster, fpInfo) 145 Expect(err).ToNot(HaveOccurred()) 146 147 cc := testExecutor.ClusterCommands[0] 148 errorFile0 := fmt.Sprintf(`/data/gpseg0/gpbackup_0_11112233445566_pipe_%d_error`, fpInfo.PID) 149 expectedCmd0 := fmt.Sprintf(`if [[ -f %[1]s ]]; then echo 'error'; fi; rm -f %[1]s`, errorFile0) 150 Expect(cc[0].CommandString).To(ContainSubstring(expectedCmd0)) 151 152 errorFile1 := fmt.Sprintf(`/data/gpseg1/gpbackup_1_11112233445566_pipe_%d_error`, fpInfo.PID) 153 expectedCmd1 := fmt.Sprintf(`if [[ -f %[1]s ]]; then echo 'error'; fi; rm -f %[1]s`, errorFile1) 154 Expect(cc[1].CommandString).To(ContainSubstring(expectedCmd1)) 155 }) 156 157 }) 158 }) 159 160 type testWriter struct { 161 WriteErr error 162 CloseErr error 163 } 164 165 func (f testWriter) Write(p []byte) (n int, err error) { 166 _ = p 167 return 0, f.WriteErr 168 } 169 func (f testWriter) Close() error { 170 return f.CloseErr 171 }