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 })