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  }