github.com/greenplum-db/gpbackup@v0.0.0-20240517212602-89daab1885b3/end_to_end/plugin_test.go (about)

     1  package end_to_end_test
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/exec"
     7  	path "path/filepath"
     8  
     9  	"github.com/greenplum-db/gp-common-go-libs/cluster"
    10  	"github.com/greenplum-db/gp-common-go-libs/dbconn"
    11  	"github.com/greenplum-db/gp-common-go-libs/iohelper"
    12  	"github.com/greenplum-db/gp-common-go-libs/testhelper"
    13  	"github.com/greenplum-db/gpbackup/filepath"
    14  	"github.com/greenplum-db/gpbackup/testutils"
    15  	"github.com/greenplum-db/gpbackup/utils"
    16  	. "github.com/onsi/ginkgo/v2"
    17  )
    18  
    19  func copyPluginToAllHosts(conn *dbconn.DBConn, pluginPath string) {
    20  	hostnameQuery := `SELECT DISTINCT hostname AS string FROM gp_segment_configuration WHERE content != -1`
    21  	hostnames := dbconn.MustSelectStringSlice(conn, hostnameQuery)
    22  	for _, hostname := range hostnames {
    23  		// Skip the local host
    24  		h, _ := os.Hostname()
    25  		if hostname == h {
    26  			continue
    27  		}
    28  		examplePluginTestDir, _ := path.Split(pluginPath)
    29  		command := exec.Command("ssh", hostname, fmt.Sprintf("mkdir -p %s", examplePluginTestDir))
    30  		mustRunCommand(command)
    31  		command = exec.Command("scp", pluginPath, fmt.Sprintf("%s:%s", hostname, pluginPath))
    32  		mustRunCommand(command)
    33  	}
    34  }
    35  
    36  func forceMetadataFileDownloadFromPlugin(conn *dbconn.DBConn, timestamp string) {
    37  	fpInfo := filepath.NewFilePathInfo(backupCluster, "", timestamp, "", false)
    38  	remoteOutput := backupCluster.GenerateAndExecuteCommand(
    39  		fmt.Sprintf("Removing backups on all segments for "+
    40  			"timestamp %s", timestamp),
    41  		cluster.ON_SEGMENTS|cluster.INCLUDE_COORDINATOR,
    42  		func(contentID int) string {
    43  			return fmt.Sprintf("rm -rf %s", fpInfo.GetDirForContent(contentID))
    44  		})
    45  	if remoteOutput.NumErrors != 0 {
    46  		Fail(fmt.Sprintf("Failed to remove backup directory for timestamp %s", timestamp))
    47  	}
    48  }
    49  
    50  var _ = Describe("End to End plugin tests", func() {
    51  	BeforeEach(func() {
    52  		end_to_end_setup()
    53  	})
    54  	AfterEach(func() {
    55  		end_to_end_teardown()
    56  	})
    57  
    58  	Describe("Single data file", func() {
    59  		It("runs gpbackup and gprestore with single-data-file flag", func() {
    60  			output := gpbackup(gpbackupPath, backupHelperPath,
    61  				"--single-data-file",
    62  				"--backup-dir", backupDir)
    63  			timestamp := getBackupTimestamp(string(output))
    64  
    65  			gprestore(gprestorePath, restoreHelperPath, timestamp,
    66  				"--redirect-db", "restoredb",
    67  				"--backup-dir", backupDir)
    68  
    69  			assertRelationsCreated(restoreConn, TOTAL_RELATIONS)
    70  			assertDataRestored(restoreConn, publicSchemaTupleCounts)
    71  			assertDataRestored(restoreConn, schema2TupleCounts)
    72  			assertArtifactsCleaned(timestamp)
    73  
    74  		})
    75  		It("runs gpbackup and gprestore with single-data-file flag with copy-queue-size", func() {
    76  			skipIfOldBackupVersionBefore("1.23.0")
    77  			output := gpbackup(gpbackupPath, backupHelperPath,
    78  				"--single-data-file",
    79  				"--copy-queue-size", "4",
    80  				"--backup-dir", backupDir)
    81  			timestamp := getBackupTimestamp(string(output))
    82  			gprestore(gprestorePath, restoreHelperPath, timestamp,
    83  				"--redirect-db", "restoredb",
    84  				"--copy-queue-size", "4",
    85  				"--backup-dir", backupDir)
    86  
    87  			assertRelationsCreated(restoreConn, TOTAL_RELATIONS)
    88  			assertDataRestored(restoreConn, publicSchemaTupleCounts)
    89  			assertDataRestored(restoreConn, schema2TupleCounts)
    90  			assertArtifactsCleaned(timestamp)
    91  
    92  		})
    93  		It("runs gpbackup and gprestore with single-data-file flag without compression", func() {
    94  			output := gpbackup(gpbackupPath, backupHelperPath,
    95  				"--single-data-file",
    96  				"--backup-dir", backupDir,
    97  				"--no-compression")
    98  			timestamp := getBackupTimestamp(string(output))
    99  			gprestore(gprestorePath, restoreHelperPath, timestamp,
   100  				"--redirect-db", "restoredb",
   101  				"--backup-dir", backupDir)
   102  
   103  			assertRelationsCreated(restoreConn, TOTAL_RELATIONS)
   104  			assertDataRestored(restoreConn, publicSchemaTupleCounts)
   105  			assertDataRestored(restoreConn, schema2TupleCounts)
   106  			assertArtifactsCleaned(timestamp)
   107  		})
   108  		It("runs gpbackup and gprestore with single-data-file flag without compression with copy-queue-size", func() {
   109  			skipIfOldBackupVersionBefore("1.23.0")
   110  			output := gpbackup(gpbackupPath, backupHelperPath,
   111  				"--single-data-file",
   112  				"--copy-queue-size", "4",
   113  				"--backup-dir", backupDir,
   114  				"--no-compression")
   115  			timestamp := getBackupTimestamp(string(output))
   116  			gprestore(gprestorePath, restoreHelperPath, timestamp,
   117  				"--redirect-db", "restoredb",
   118  				"--copy-queue-size", "4",
   119  				"--backup-dir", backupDir)
   120  
   121  			assertRelationsCreated(restoreConn, TOTAL_RELATIONS)
   122  			assertDataRestored(restoreConn, publicSchemaTupleCounts)
   123  			assertDataRestored(restoreConn, schema2TupleCounts)
   124  			assertArtifactsCleaned(timestamp)
   125  		})
   126  		It("runs gpbackup and gprestore on database with all objects", func() {
   127  			schemaResetStatements := "DROP SCHEMA IF EXISTS schema2 CASCADE; DROP SCHEMA public CASCADE; CREATE SCHEMA public;"
   128  			testhelper.AssertQueryRuns(backupConn, schemaResetStatements)
   129  			defer testutils.ExecuteSQLFile(backupConn,
   130  				"resources/test_tables_data.sql")
   131  			defer testutils.ExecuteSQLFile(backupConn,
   132  				"resources/test_tables_ddl.sql")
   133  			defer testhelper.AssertQueryRuns(backupConn, schemaResetStatements)
   134  			defer testhelper.AssertQueryRuns(restoreConn, schemaResetStatements)
   135  			testhelper.AssertQueryRuns(backupConn,
   136  				"CREATE ROLE testrole SUPERUSER")
   137  			defer testhelper.AssertQueryRuns(backupConn,
   138  				"DROP ROLE testrole")
   139  
   140  			// In GPDB 7+, we use plpython3u because of default python 3 support.
   141  			plpythonDropStatement := "DROP PROCEDURAL LANGUAGE IF EXISTS plpythonu;"
   142  			if backupConn.Version.AtLeast("7") {
   143  				plpythonDropStatement = "DROP PROCEDURAL LANGUAGE IF EXISTS plpython3u;"
   144  			}
   145  			testhelper.AssertQueryRuns(backupConn, plpythonDropStatement)
   146  			defer testhelper.AssertQueryRuns(backupConn, plpythonDropStatement)
   147  			defer testhelper.AssertQueryRuns(restoreConn, plpythonDropStatement)
   148  
   149  			testutils.ExecuteSQLFile(backupConn, "resources/gpdb4_objects.sql")
   150  			if backupConn.Version.Before("7") {
   151  				testutils.ExecuteSQLFile(backupConn, "resources/gpdb4_compatible_objects_before_gpdb7.sql")
   152  			} else {
   153  				testutils.ExecuteSQLFile(backupConn, "resources/gpdb4_compatible_objects_after_gpdb7.sql")
   154  			}
   155  
   156  			if backupConn.Version.AtLeast("5") {
   157  				testutils.ExecuteSQLFile(backupConn, "resources/gpdb5_objects.sql")
   158  			}
   159  			if backupConn.Version.AtLeast("6") {
   160  				testutils.ExecuteSQLFile(backupConn, "resources/gpdb6_objects.sql")
   161  				defer testhelper.AssertQueryRuns(backupConn,
   162  					"DROP FOREIGN DATA WRAPPER fdw CASCADE;")
   163  				defer testhelper.AssertQueryRuns(restoreConn,
   164  					"DROP FOREIGN DATA WRAPPER fdw CASCADE;")
   165  			}
   166  			if backupConn.Version.AtLeast("6.2") {
   167  				testhelper.AssertQueryRuns(backupConn,
   168  					"CREATE TABLE mview_table1(i int, j text);")
   169  				defer testhelper.AssertQueryRuns(restoreConn,
   170  					"DROP TABLE mview_table1;")
   171  				testhelper.AssertQueryRuns(backupConn,
   172  					"CREATE MATERIALIZED VIEW mview1 (i2) as select i from mview_table1;")
   173  				defer testhelper.AssertQueryRuns(restoreConn,
   174  					"DROP MATERIALIZED VIEW mview1;")
   175  				testhelper.AssertQueryRuns(backupConn,
   176  					"CREATE MATERIALIZED VIEW mview2 as select * from mview1;")
   177  				defer testhelper.AssertQueryRuns(restoreConn,
   178  					"DROP MATERIALIZED VIEW mview2;")
   179  			}
   180  
   181  			output := gpbackup(gpbackupPath, backupHelperPath,
   182  				"--leaf-partition-data",
   183  				"--single-data-file")
   184  			timestamp := getBackupTimestamp(string(output))
   185  			gprestore(gprestorePath, restoreHelperPath, timestamp,
   186  				"--metadata-only",
   187  				"--redirect-db", "restoredb")
   188  			assertArtifactsCleaned(timestamp)
   189  		})
   190  		It("runs gpbackup and gprestore on database with all objects with copy-queue-size", func() {
   191  			skipIfOldBackupVersionBefore("1.23.0")
   192  			testhelper.AssertQueryRuns(backupConn,
   193  				"DROP SCHEMA IF EXISTS schema2 CASCADE; DROP SCHEMA public CASCADE; CREATE SCHEMA public; DROP PROCEDURAL LANGUAGE IF EXISTS plpythonu;")
   194  			defer testutils.ExecuteSQLFile(backupConn,
   195  				"resources/test_tables_data.sql")
   196  			defer testutils.ExecuteSQLFile(backupConn,
   197  				"resources/test_tables_ddl.sql")
   198  			defer testhelper.AssertQueryRuns(backupConn,
   199  				"DROP SCHEMA IF EXISTS schema2 CASCADE; DROP SCHEMA public CASCADE; CREATE SCHEMA public; DROP PROCEDURAL LANGUAGE IF EXISTS plpythonu;")
   200  			defer testhelper.AssertQueryRuns(restoreConn,
   201  				"DROP SCHEMA IF EXISTS schema2 CASCADE; DROP SCHEMA public CASCADE; CREATE SCHEMA public; DROP PROCEDURAL LANGUAGE IF EXISTS plpythonu;")
   202  			testhelper.AssertQueryRuns(backupConn,
   203  				"CREATE ROLE testrole SUPERUSER")
   204  			defer testhelper.AssertQueryRuns(backupConn,
   205  				"DROP ROLE testrole")
   206  			testutils.ExecuteSQLFile(backupConn, "resources/gpdb4_objects.sql")
   207  			if backupConn.Version.AtLeast("5") {
   208  				testutils.ExecuteSQLFile(backupConn, "resources/gpdb5_objects.sql")
   209  			}
   210  			if backupConn.Version.AtLeast("6") {
   211  				testutils.ExecuteSQLFile(backupConn, "resources/gpdb6_objects.sql")
   212  				defer testhelper.AssertQueryRuns(backupConn,
   213  					"DROP FOREIGN DATA WRAPPER fdw CASCADE;")
   214  				defer testhelper.AssertQueryRuns(restoreConn,
   215  					"DROP FOREIGN DATA WRAPPER fdw CASCADE;")
   216  			}
   217  			if backupConn.Version.AtLeast("6.2") {
   218  				testhelper.AssertQueryRuns(backupConn,
   219  					"CREATE TABLE mview_table1(i int, j text);")
   220  				defer testhelper.AssertQueryRuns(restoreConn,
   221  					"DROP TABLE mview_table1;")
   222  				testhelper.AssertQueryRuns(backupConn,
   223  					"CREATE MATERIALIZED VIEW mview1 (i2) as select i from mview_table1;")
   224  				defer testhelper.AssertQueryRuns(restoreConn,
   225  					"DROP MATERIALIZED VIEW mview1;")
   226  				testhelper.AssertQueryRuns(backupConn,
   227  					"CREATE MATERIALIZED VIEW mview2 as select * from mview1;")
   228  				defer testhelper.AssertQueryRuns(restoreConn,
   229  					"DROP MATERIALIZED VIEW mview2;")
   230  			}
   231  
   232  			output := gpbackup(gpbackupPath, backupHelperPath,
   233  				"--leaf-partition-data",
   234  				"--single-data-file",
   235  				"--copy-queue-size", "4")
   236  			timestamp := getBackupTimestamp(string(output))
   237  			gprestore(gprestorePath, restoreHelperPath, timestamp,
   238  				"--metadata-only",
   239  				"--redirect-db", "restoredb",
   240  				"--copy-queue-size", "4")
   241  			assertArtifactsCleaned(timestamp)
   242  		})
   243  
   244  		Context("with include filtering on restore", func() {
   245  			It("runs gpbackup and gprestore with include-table-file restore flag with a single data file", func() {
   246  				includeFile := iohelper.MustOpenFileForWriting("/tmp/include-tables.txt")
   247  				utils.MustPrintln(includeFile, "public.sales\npublic.foo\npublic.myseq1\npublic.myview1")
   248  				output := gpbackup(gpbackupPath, backupHelperPath,
   249  					"--backup-dir", backupDir,
   250  					"--single-data-file")
   251  				timestamp := getBackupTimestamp(string(output))
   252  				gprestore(gprestorePath, restoreHelperPath, timestamp,
   253  					"--redirect-db", "restoredb",
   254  					"--backup-dir", backupDir,
   255  					"--include-table-file", "/tmp/include-tables.txt")
   256  				assertRelationsCreated(restoreConn, 16)
   257  				assertDataRestored(restoreConn, map[string]int{
   258  					"public.sales": 13, "public.foo": 40000})
   259  				assertArtifactsCleaned(timestamp)
   260  
   261  				_ = os.Remove("/tmp/include-tables.txt")
   262  			})
   263  			It("runs gpbackup and gprestore with include-table-file restore flag with a single data with copy-queue-size", func() {
   264  				skipIfOldBackupVersionBefore("1.23.0")
   265  				includeFile := iohelper.MustOpenFileForWriting("/tmp/include-tables.txt")
   266  				utils.MustPrintln(includeFile, "public.sales\npublic.foo\npublic.myseq1\npublic.myview1")
   267  				output := gpbackup(gpbackupPath, backupHelperPath,
   268  					"--backup-dir", backupDir,
   269  					"--single-data-file",
   270  					"--copy-queue-size", "4")
   271  				timestamp := getBackupTimestamp(string(output))
   272  				gprestore(gprestorePath, restoreHelperPath, timestamp,
   273  					"--redirect-db", "restoredb",
   274  					"--backup-dir", backupDir,
   275  					"--include-table-file", "/tmp/include-tables.txt",
   276  					"--copy-queue-size", "4")
   277  				assertRelationsCreated(restoreConn, 16)
   278  				assertDataRestored(restoreConn, map[string]int{
   279  					"public.sales": 13, "public.foo": 40000})
   280  				assertArtifactsCleaned(timestamp)
   281  
   282  				_ = os.Remove("/tmp/include-tables.txt")
   283  			})
   284  			It("runs gpbackup and gprestore with include-schema restore flag with a single data file", func() {
   285  				output := gpbackup(gpbackupPath, backupHelperPath,
   286  					"--backup-dir", backupDir,
   287  					"--single-data-file")
   288  				timestamp := getBackupTimestamp(string(output))
   289  				gprestore(gprestorePath, restoreHelperPath, timestamp,
   290  					"--redirect-db", "restoredb",
   291  					"--backup-dir", backupDir,
   292  					"--include-schema", "schema2")
   293  
   294  				assertRelationsCreated(restoreConn, 17)
   295  				assertDataRestored(restoreConn, schema2TupleCounts)
   296  				assertArtifactsCleaned(timestamp)
   297  			})
   298  			It("runs gpbackup and gprestore with include-schema restore flag with a single data file with copy-queue-size", func() {
   299  				skipIfOldBackupVersionBefore("1.23.0")
   300  				output := gpbackup(gpbackupPath, backupHelperPath,
   301  					"--backup-dir", backupDir,
   302  					"--single-data-file",
   303  					"--copy-queue-size", "4")
   304  				timestamp := getBackupTimestamp(string(output))
   305  				gprestore(gprestorePath, restoreHelperPath, timestamp,
   306  					"--redirect-db", "restoredb",
   307  					"--backup-dir", backupDir,
   308  					"--include-schema", "schema2",
   309  					"--copy-queue-size", "4")
   310  
   311  				assertRelationsCreated(restoreConn, 17)
   312  				assertDataRestored(restoreConn, schema2TupleCounts)
   313  				assertArtifactsCleaned(timestamp)
   314  			})
   315  		})
   316  
   317  		Context("with plugin", func() {
   318  			BeforeEach(func() {
   319  				skipIfOldBackupVersionBefore("1.7.0")
   320  				// FIXME: we are temporarily disabling these tests because we will be altering our backwards compatibility logic.
   321  				if useOldBackupVersion {
   322  					Skip("This test is only needed for the most recent backup versions")
   323  				}
   324  			})
   325  			It("runs gpbackup and gprestore with plugin, single-data-file, and no-compression", func() {
   326  				copyPluginToAllHosts(backupConn, examplePluginExec)
   327  
   328  				output := gpbackup(gpbackupPath, backupHelperPath,
   329  					"--single-data-file",
   330  					"--no-compression",
   331  					"--plugin-config", examplePluginTestConfig)
   332  				timestamp := getBackupTimestamp(string(output))
   333  				forceMetadataFileDownloadFromPlugin(backupConn, timestamp)
   334  
   335  				gprestore(gprestorePath, restoreHelperPath, timestamp,
   336  					"--redirect-db", "restoredb",
   337  					"--plugin-config", examplePluginTestConfig)
   338  
   339  				assertRelationsCreated(restoreConn, TOTAL_RELATIONS)
   340  				assertDataRestored(restoreConn, publicSchemaTupleCounts)
   341  				assertDataRestored(restoreConn, schema2TupleCounts)
   342  				assertArtifactsCleaned(timestamp)
   343  			})
   344  			It("runs gpbackup and gprestore with plugin, single-data-file, no-compression, and copy-queue-size", func() {
   345  				copyPluginToAllHosts(backupConn, examplePluginExec)
   346  
   347  				output := gpbackup(gpbackupPath, backupHelperPath,
   348  					"--single-data-file",
   349  					"--copy-queue-size", "4",
   350  					"--no-compression",
   351  					"--plugin-config", examplePluginTestConfig)
   352  				timestamp := getBackupTimestamp(string(output))
   353  				forceMetadataFileDownloadFromPlugin(backupConn, timestamp)
   354  
   355  				gprestore(gprestorePath, restoreHelperPath, timestamp,
   356  					"--redirect-db", "restoredb",
   357  					"--plugin-config", examplePluginTestConfig,
   358  					"--copy-queue-size", "4")
   359  
   360  				assertRelationsCreated(restoreConn, TOTAL_RELATIONS)
   361  				assertDataRestored(restoreConn, publicSchemaTupleCounts)
   362  				assertDataRestored(restoreConn, schema2TupleCounts)
   363  				assertArtifactsCleaned(timestamp)
   364  			})
   365  			It("runs gpbackup and gprestore with plugin and single-data-file", func() {
   366  				copyPluginToAllHosts(backupConn, examplePluginExec)
   367  
   368  				output := gpbackup(gpbackupPath, backupHelperPath,
   369  					"--single-data-file",
   370  					"--plugin-config", examplePluginTestConfig)
   371  				timestamp := getBackupTimestamp(string(output))
   372  				forceMetadataFileDownloadFromPlugin(backupConn, timestamp)
   373  
   374  				gprestore(gprestorePath, restoreHelperPath, timestamp,
   375  					"--redirect-db", "restoredb",
   376  					"--plugin-config", examplePluginTestConfig)
   377  
   378  				assertRelationsCreated(restoreConn, TOTAL_RELATIONS)
   379  				assertDataRestored(restoreConn, publicSchemaTupleCounts)
   380  				assertDataRestored(restoreConn, schema2TupleCounts)
   381  				assertArtifactsCleaned(timestamp)
   382  			})
   383  			It("runs gpbackup and gprestore with plugin, single-data-file, and copy-queue-size", func() {
   384  				copyPluginToAllHosts(backupConn, examplePluginExec)
   385  
   386  				output := gpbackup(gpbackupPath, backupHelperPath,
   387  					"--single-data-file",
   388  					"--copy-queue-size", "4",
   389  					"--plugin-config", examplePluginTestConfig)
   390  				timestamp := getBackupTimestamp(string(output))
   391  				forceMetadataFileDownloadFromPlugin(backupConn, timestamp)
   392  
   393  				gprestore(gprestorePath, restoreHelperPath, timestamp,
   394  					"--redirect-db", "restoredb",
   395  					"--plugin-config", examplePluginTestConfig,
   396  					"--copy-queue-size", "4")
   397  
   398  				assertRelationsCreated(restoreConn, TOTAL_RELATIONS)
   399  				assertDataRestored(restoreConn, publicSchemaTupleCounts)
   400  				assertDataRestored(restoreConn, schema2TupleCounts)
   401  				assertArtifactsCleaned(timestamp)
   402  			})
   403  			It("runs gpbackup and gprestore with plugin and metadata-only", func() {
   404  				copyPluginToAllHosts(backupConn, examplePluginExec)
   405  
   406  				output := gpbackup(gpbackupPath, backupHelperPath,
   407  					"--metadata-only",
   408  					"--plugin-config", examplePluginTestConfig)
   409  				timestamp := getBackupTimestamp(string(output))
   410  				forceMetadataFileDownloadFromPlugin(backupConn, timestamp)
   411  
   412  				gprestore(gprestorePath, restoreHelperPath, timestamp,
   413  					"--redirect-db", "restoredb",
   414  					"--plugin-config", examplePluginTestConfig)
   415  
   416  				assertRelationsCreated(restoreConn, TOTAL_RELATIONS)
   417  				assertArtifactsCleaned(timestamp)
   418  			})
   419  		})
   420  	})
   421  
   422  	Describe("Multi-file Plugin", func() {
   423  		It("runs gpbackup and gprestore with plugin and no-compression", func() {
   424  			skipIfOldBackupVersionBefore("1.7.0")
   425  			// FIXME: we are temporarily disabling these tests because we will be altering our backwards compatibility logic.
   426  			if useOldBackupVersion {
   427  				Skip("This test is only needed for the most recent backup versions")
   428  			}
   429  			copyPluginToAllHosts(backupConn, examplePluginExec)
   430  
   431  			output := gpbackup(gpbackupPath, backupHelperPath,
   432  				"--no-compression",
   433  				"--plugin-config", examplePluginTestConfig)
   434  			timestamp := getBackupTimestamp(string(output))
   435  			forceMetadataFileDownloadFromPlugin(backupConn, timestamp)
   436  
   437  			gprestore(gprestorePath, restoreHelperPath, timestamp,
   438  				"--redirect-db", "restoredb",
   439  				"--plugin-config", examplePluginTestConfig)
   440  
   441  			assertRelationsCreated(restoreConn, TOTAL_RELATIONS)
   442  			assertDataRestored(restoreConn, publicSchemaTupleCounts)
   443  			assertDataRestored(restoreConn, schema2TupleCounts)
   444  		})
   445  		It("runs gpbackup and gprestore with plugin and compression", func() {
   446  			skipIfOldBackupVersionBefore("1.7.0")
   447  			// FIXME: we are temporarily disabling these tests because we will be altering our backwards compatibility logic.
   448  			if useOldBackupVersion {
   449  				Skip("This test is only needed for the most recent backup versions")
   450  			}
   451  			copyPluginToAllHosts(backupConn, examplePluginExec)
   452  
   453  			output := gpbackup(gpbackupPath, backupHelperPath,
   454  				"--plugin-config", examplePluginTestConfig)
   455  			timestamp := getBackupTimestamp(string(output))
   456  			forceMetadataFileDownloadFromPlugin(backupConn, timestamp)
   457  
   458  			gprestore(gprestorePath, restoreHelperPath, timestamp,
   459  				"--redirect-db", "restoredb",
   460  				"--plugin-config", examplePluginTestConfig)
   461  
   462  			assertRelationsCreated(restoreConn, TOTAL_RELATIONS)
   463  			assertDataRestored(restoreConn, publicSchemaTupleCounts)
   464  			assertDataRestored(restoreConn, schema2TupleCounts)
   465  		})
   466  	})
   467  
   468  	Describe("Example Plugin", func() {
   469  		It("runs example_plugin.bash with plugin_test", func() {
   470  			if useOldBackupVersion {
   471  				Skip("This test is only needed for the latest backup version")
   472  			}
   473  			copyPluginToAllHosts(backupConn, examplePluginExec)
   474  			command := exec.Command("bash", "-c", fmt.Sprintf("%s/plugin_test.sh %s %s", examplePluginDir, examplePluginExec, examplePluginTestConfig))
   475  			mustRunCommand(command)
   476  		})
   477  	})
   478  })