github.com/cloudberrydb/gpbackup@v1.0.3-0.20240118031043-5410fd45eed6/restore/wrappers_test.go (about)

     1  package restore_test
     2  
     3  import (
     4  	"io/ioutil"
     5  	"os"
     6  	"path/filepath"
     7  
     8  	"github.com/DATA-DOG/go-sqlmock"
     9  	"github.com/cloudberrydb/gp-common-go-libs/cluster"
    10  	"github.com/cloudberrydb/gp-common-go-libs/testhelper"
    11  	"github.com/cloudberrydb/gpbackup/history"
    12  	"github.com/cloudberrydb/gpbackup/options"
    13  	"github.com/cloudberrydb/gpbackup/restore"
    14  	"github.com/cloudberrydb/gpbackup/testutils"
    15  	"github.com/cloudberrydb/gpbackup/toc"
    16  	"github.com/cloudberrydb/gpbackup/utils"
    17  	"github.com/pkg/errors"
    18  
    19  	. "github.com/onsi/ginkgo/v2"
    20  	. "github.com/onsi/gomega"
    21  )
    22  
    23  var _ = Describe("wrapper tests", func() {
    24  	Describe("SetMaxCsvLineLengthQuery", func() {
    25  		It("returns nothing with a connection version of at least 6.0.0", func() {
    26  			testhelper.SetDBVersion(connectionPool, "6.0.0")
    27  			result := restore.SetMaxCsvLineLengthQuery(connectionPool)
    28  			Expect(result).To(Equal(""))
    29  		})
    30  		It("sets gp_max_csv_line_length to 1GB when connection version is 4.X and at least 4.3.30.0", func() {
    31  			Skip("Test is not applicable to cloudberry 1.0+")
    32  			testhelper.SetDBVersion(connectionPool, "4.3.30")
    33  			result := restore.SetMaxCsvLineLengthQuery(connectionPool)
    34  			Expect(result).To(Equal("SET gp_max_csv_line_length = 1073741824;\n"))
    35  		})
    36  		It("sets gp_max_csv_line_length to 1GB when connection version is 5.X and at least 5.11.0", func() {
    37  			Skip("Test is not applicable to cloudberry 1.0+")
    38  			testhelper.SetDBVersion(connectionPool, "5.11.0")
    39  			result := restore.SetMaxCsvLineLengthQuery(connectionPool)
    40  			Expect(result).To(Equal("SET gp_max_csv_line_length = 1073741824;\n"))
    41  		})
    42  		It("sets gp_max_csv_line_length to 4MB when connection version is 4.X and before 4.3.30.0", func() {
    43  			Skip("Test is not applicable to cloudberry 1.0+")
    44  			testhelper.SetDBVersion(connectionPool, "4.3.29")
    45  			result := restore.SetMaxCsvLineLengthQuery(connectionPool)
    46  			Expect(result).To(Equal("SET gp_max_csv_line_length = 4194304;\n"))
    47  		})
    48  		It("sets gp_max_csv_line_length to 4MB when connection version is 5.X and before 5.11.0", func() {
    49  			Skip("Test is not applicable to cloudberry 1.0+")
    50  			testhelper.SetDBVersion(connectionPool, "5.10.999")
    51  			result := restore.SetMaxCsvLineLengthQuery(connectionPool)
    52  			Expect(result).To(Equal("SET gp_max_csv_line_length = 4194304;\n"))
    53  		})
    54  	})
    55  	Describe("RestoreSchemas", func() {
    56  		var (
    57  			ignoredProgressBar utils.ProgressBar
    58  			schemaArray        = []toc.StatementWithType{{Name: "foo", Statement: "create schema foo"}}
    59  		)
    60  		BeforeEach(func() {
    61  			ignoredProgressBar = utils.NewProgressBar(1, "", utils.PB_NONE)
    62  			ignoredProgressBar.Start()
    63  		})
    64  		AfterEach(func() {
    65  			ignoredProgressBar.Finish()
    66  		})
    67  		It("logs nothing if there are no errors", func() {
    68  			expectedResult := sqlmock.NewResult(0, 1)
    69  			mock.ExpectExec("create schema foo").WillReturnResult(expectedResult)
    70  
    71  			restore.RestoreSchemas(schemaArray, ignoredProgressBar)
    72  
    73  			testhelper.NotExpectRegexp(logfile, "Schema foo already exists")
    74  			testhelper.NotExpectRegexp(logfile, "Error encountered while creating schema foo")
    75  		})
    76  		It("logs warning if schema already exists", func() {
    77  			expectedErr := errors.New(`schema "foo" already exists`)
    78  			mock.ExpectExec("create schema foo").WillReturnError(expectedErr)
    79  
    80  			restore.RestoreSchemas(schemaArray, ignoredProgressBar)
    81  
    82  			testhelper.ExpectRegexp(logfile, "[WARNING]:-Schema foo already exists")
    83  		})
    84  		It("logs error if --on-error-continue is set", func() {
    85  			_ = cmdFlags.Set(options.ON_ERROR_CONTINUE, "true")
    86  			defer cmdFlags.Set(options.ON_ERROR_CONTINUE, "false")
    87  			expectedErr := errors.New("some other schema error")
    88  			mock.ExpectExec("create schema foo").WillReturnError(expectedErr)
    89  
    90  			restore.RestoreSchemas(schemaArray, ignoredProgressBar)
    91  
    92  			expectedDebugMsg := "[DEBUG]:-Error encountered while creating schema foo: some other schema error"
    93  			testhelper.ExpectRegexp(logfile, expectedDebugMsg)
    94  			expectedErrMsg := "[ERROR]:-Encountered 1 errors during schema restore; see log file gbytes.Buffer for a list of errors."
    95  			testhelper.ExpectRegexp(logfile, expectedErrMsg)
    96  		})
    97  		It("panics if create schema statement fails", func() {
    98  			expectedErr := errors.New("some other schema error")
    99  			mock.ExpectExec("create schema foo").WillReturnError(expectedErr)
   100  			expectedPanicMsg := "[CRITICAL]:-some other schema error: Error encountered while creating schema foo"
   101  			defer testhelper.ShouldPanicWithMessage(expectedPanicMsg)
   102  
   103  			restore.RestoreSchemas(schemaArray, ignoredProgressBar)
   104  		})
   105  	})
   106  	Describe("SetRestorePlanForLegacyBackup", func() {
   107  		legacyBackupConfig := history.BackupConfig{}
   108  		legacyBackupConfig.RestorePlan = nil
   109  		legacyBackupTOC := toc.TOC{
   110  			DataEntries: []toc.CoordinatorDataEntry{
   111  				{Schema: "schema1", Name: "table1"},
   112  				{Schema: "schema2", Name: "table2"},
   113  			},
   114  		}
   115  		legacyBackupTimestamp := "ts0"
   116  
   117  		restore.SetRestorePlanForLegacyBackup(&legacyBackupTOC, legacyBackupTimestamp, &legacyBackupConfig)
   118  
   119  		Specify("That there should be only one resultant restore plan entry", func() {
   120  			Expect(legacyBackupConfig.RestorePlan).To(HaveLen(1))
   121  		})
   122  
   123  		Specify("That the restore plan entry should have the legacy backup's timestamp", func() {
   124  			Expect(legacyBackupConfig.RestorePlan[0].Timestamp).To(Equal(legacyBackupTimestamp))
   125  		})
   126  
   127  		Specify("That the restore plan entry should have all table FQNs as in the TOC's DataEntries", func() {
   128  			Expect(legacyBackupConfig.RestorePlan[0].TableFQNs).
   129  				To(Equal([]string{"schema1.table1", "schema2.table2"}))
   130  		})
   131  
   132  	})
   133  	Describe("restore history tests", func() {
   134  		sampleConfigContents := `
   135  executablepath: /bin/echo
   136  options:
   137    hostname: "10.85.20.10"
   138    storage_unit: "GPDB"
   139    username: "gpadmin"
   140    password: "changeme"
   141    password_encryption:
   142    directory: "/blah"
   143    replication: "off"
   144    remote_hostname: "10.85.20.11"
   145    remote_storage_unit: "GPDB"
   146    remote_username: "gpadmin"
   147    remote_password: "changeme"
   148    remote_directory: "/blah"
   149    pgport: 1234
   150  `
   151  
   152  		sampleBackupHistory := `
   153  backupconfigs:
   154  - backupdir: ""
   155    backupversion: 1.11.0+dev.28.g10571fd
   156    compressed: false
   157    databasename: plugin_test_db
   158    databaseversion: 4.3.99.0+dev.18.gb29642fb22 build dev
   159    dataonly: false
   160    deleted: false
   161    excluderelations: []
   162    excludeschemafiltered: false
   163    excludeschemas: []
   164    excludetablefiltered: false
   165    includerelations: []
   166    includeschemafiltered: false
   167    includeschemas: []
   168    includetablefiltered: false
   169    incremental: false
   170    leafpartitiondata: false
   171    metadataonly: false
   172    plugin: /Users/pivotal/workspace/gp-backup-ddboost-plugin/gpbackup_ddboost_plugin
   173    restoreplan:
   174    - timestamp: "20170415154408"
   175      tablefqns:
   176      - public.test_table
   177    singledatafile: false
   178    timestamp: "20170415154408"
   179    withstatistics: false
   180  - backupdir: ""
   181    backupversion: 1.11.0+dev.28.g10571fd
   182    compressed: false
   183    databasename: plugin_test_db
   184    databaseversion: 4.3.99.0+dev.18.gb29642fb22 build dev
   185    dataonly: false
   186    deleted: false
   187    excluderelations: []
   188    excludeschemafiltered: false
   189    excludeschemas: []
   190    excludetablefiltered: false
   191    includerelations: []
   192    includeschemafiltered: false
   193    includeschemas: []
   194    includetablefiltered: false
   195    incremental: false
   196    leafpartitiondata: false
   197    metadataonly: false
   198    plugin: /Users/pivotal/workspace/gp-backup-ddboost-plugin/gpbackup_ddboost_plugin
   199    pluginversion: "99.99.9999"
   200    restoreplan:
   201    - timestamp: "20180415154238"
   202      tablefqns:
   203      - public.test_table
   204    singledatafile: true
   205    timestamp: "20180415154238"
   206    withstatistics: false
   207  `
   208  
   209  		sampleBackupConfig := `
   210  backupdir: ""
   211  backupversion: 1.11.0+dev.28.g10571fd
   212  compressed: false
   213  databasename: plugin_test_db
   214  databaseversion: 4.3.99.0+dev.18.gb29642fb22 build dev
   215  dataonly: false
   216  deleted: false
   217  excluderelations: []
   218  excludeschemafiltered: false
   219  excludeschemas: []
   220  excludetablefiltered: false
   221  includerelations: []
   222  includeschemafiltered: false
   223  includeschemas: []
   224  includetablefiltered: false
   225  incremental: false
   226  leafpartitiondata: false
   227  metadataonly: false
   228  plugin: /Users/pivotal/workspace/gp-backup-ddboost-plugin/gpbackup_ddboost_plugin
   229  pluginversion: "99.99.9999"
   230  restoreplan:
   231  - timestamp: "20180415154238"
   232  tablefqns:
   233  - public.test_table
   234  singledatafile: true
   235  timestamp: "20180415154238"
   236  withstatistics: false
   237  `
   238  		var executor testutils.TestExecutorMultiple
   239  		var testConfigPath = "/tmp/unit_test_plugin_config.yml"
   240  		var oldWd string
   241  		var mdd string
   242  		var tempDir string
   243  
   244  		BeforeEach(func() {
   245  			tempDir, _ = ioutil.TempDir("", "temp")
   246  
   247  			err := ioutil.WriteFile(testConfigPath, []byte(sampleConfigContents), 0777)
   248  			Expect(err).ToNot(HaveOccurred())
   249  			err = cmdFlags.Set(options.PLUGIN_CONFIG, testConfigPath)
   250  			Expect(err).ToNot(HaveOccurred())
   251  
   252  			executor = testutils.TestExecutorMultiple{
   253  				ClusterOutputs: make([]*cluster.RemoteOutput, 2),
   254  			}
   255  			executor.ClusterOutputs[0] = &cluster.RemoteOutput{
   256  				Commands: []cluster.ShellCommand{
   257  					cluster.ShellCommand{Stdout: utils.RequiredPluginVersion},
   258  					cluster.ShellCommand{Stdout: utils.RequiredPluginVersion},
   259  					cluster.ShellCommand{Stdout: utils.RequiredPluginVersion},
   260  				},
   261  			}
   262  			executor.ClusterOutputs[1] = &cluster.RemoteOutput{
   263  				Commands: []cluster.ShellCommand{
   264  					cluster.ShellCommand{Stdout: "myPlugin version 1.2.3"},
   265  					cluster.ShellCommand{Stdout: "myPlugin version 1.2.3"},
   266  					cluster.ShellCommand{Stdout: "myPlugin version 1.2.3"},
   267  				},
   268  			}
   269  
   270  			// write history file using test cluster directories
   271  			testCluster := testutils.SetupTestCluster()
   272  			testCluster.Executor = &executor
   273  			oldWd, err = os.Getwd()
   274  			Expect(err).ToNot(HaveOccurred())
   275  			err = os.Chdir(tempDir)
   276  			Expect(err).ToNot(HaveOccurred())
   277  			mdd = filepath.Join(tempDir, testCluster.GetDirForContent(-1))
   278  			err = os.MkdirAll(mdd, 0777)
   279  			Expect(err).ToNot(HaveOccurred())
   280  			historyPath := filepath.Join(mdd, "gpbackup_history.yaml")
   281  			_ = os.Remove(historyPath) // make sure no previous copy
   282  			err = ioutil.WriteFile(historyPath, []byte(sampleBackupHistory), 0777)
   283  			Expect(err).ToNot(HaveOccurred())
   284  
   285  			// create backup config file
   286  			configDir := filepath.Join(mdd, "backups/20170101/20170101010101/")
   287  			_ = os.MkdirAll(configDir, 0777)
   288  			configPath := filepath.Join(configDir, "gpbackup_20170101010101_config.yaml")
   289  			err = ioutil.WriteFile(configPath, []byte(sampleBackupConfig), 0777)
   290  			Expect(err).ToNot(HaveOccurred())
   291  
   292  			restore.SetVersion("1.11.0+dev.28.g10571fd")
   293  		})
   294  		AfterEach(func() {
   295  			_ = os.Chdir(oldWd)
   296  			err := os.RemoveAll(tempDir)
   297  			Expect(err).To(Not(HaveOccurred()))
   298  			_ = os.Remove(testConfigPath)
   299  			confDir := filepath.Dir(testConfigPath)
   300  			confFileName := filepath.Base(testConfigPath)
   301  			files, _ := ioutil.ReadDir(confDir)
   302  			for _, f := range files {
   303  				match, _ := filepath.Match("*"+confFileName+"*", f.Name())
   304  				if match {
   305  					_ = os.Remove(confDir + "/" + f.Name())
   306  				}
   307  			}
   308  		})
   309  		Describe("RecoverMetadataFilesUsingPlugin", func() {
   310  			It("proceed without warning when plugin version is found", func() {
   311  				_ = cmdFlags.Set(options.TIMESTAMP, "20180415154238")
   312  				restore.RecoverMetadataFilesUsingPlugin()
   313  				Expect(string(logfile.Contents())).ToNot(ContainSubstring("cannot recover plugin version"))
   314  			})
   315  			It("logs warning when plugin version not found", func() {
   316  				_ = cmdFlags.Set(options.TIMESTAMP, "20170415154408")
   317  				restore.RecoverMetadataFilesUsingPlugin()
   318  				Expect(string(logfile.Contents())).To(ContainSubstring("cannot recover plugin version"))
   319  			})
   320  		})
   321  		Describe("FindHistoricalPluginVersion", func() {
   322  			It("finds plugin version", func() {
   323  				resultPluginVersion := restore.FindHistoricalPluginVersion("20180415154238")
   324  				Expect(resultPluginVersion).To(Equal("99.99.9999"))
   325  			})
   326  		})
   327  	})
   328  })