diff --git a/samples/system-test/spanner.test.js b/samples/system-test/spanner.test.js index 43f56b7d9..3f7e0c8b0 100644 --- a/samples/system-test/spanner.test.js +++ b/samples/system-test/spanner.test.js @@ -92,6 +92,8 @@ const KEY_RING_ID = 'test-key-ring-node'; const KEY_ID = 'test-key'; const DEFAULT_LEADER = 'us-central1'; const DEFAULT_LEADER_2 = 'us-east1'; +const SKIP_BACKUPS = process.env.SKIP_BACKUPS; +const KOKORO_JOB_NAME = process.env.KOKORO_JOB_NAME; const spanner = new Spanner({ projectId: PROJECT_ID, @@ -1362,253 +1364,265 @@ describe('Autogenerated Admin Clients', () => { ); }); - it('shoud create a full backup schedule', async () => { - const output = execSync( - `node create-full-backup-schedule.js ${PROJECT_ID} ${INSTANCE_ID} ${DATABASE_ID} full-backup-schedule`, - ); - assert.match(output, new RegExp('Created full backup schedule')); - assert.match( - output, - new RegExp( - `projects/${PROJECT_ID}/instances/${INSTANCE_ID}/databases/${DATABASE_ID}/backupSchedules/full-backup-schedule`, - ), - ); - }); - - it('shoud create an incremental backup schedule', async () => { - const output = execSync( - `node create-incremental-backup-schedule.js ${PROJECT_ID} ${INSTANCE_ID} ${DATABASE_ID} incremental-backup-schedule`, - ); - assert.match(output, new RegExp('Created incremental backup schedule')); - assert.match( - output, - new RegExp( - `projects/${PROJECT_ID}/instances/${INSTANCE_ID}/databases/${DATABASE_ID}/backupSchedules/incremental-backup-schedule`, - ), - ); - }); + describe('backups', async () => { + before(async function () { + if ( + SKIP_BACKUPS === 'true' || + (KOKORO_JOB_NAME && KOKORO_JOB_NAME.includes('presubmit')) + ) { + this.skip(); + } + }); - it('shoud list backup schedules', async () => { - const output = execSync( - `node list-backup-schedules.js ${PROJECT_ID} ${INSTANCE_ID} ${DATABASE_ID}`, - ); - assert.match( - output, - new RegExp( - `projects/${PROJECT_ID}/instances/${INSTANCE_ID}/databases/${DATABASE_ID}/backupSchedules/full-backup-schedule`, - ), - ); - assert.match( - output, - new RegExp( - `projects/${PROJECT_ID}/instances/${INSTANCE_ID}/databases/${DATABASE_ID}/backupSchedules/incremental-backup-schedule`, - ), - ); - }); + it('shoud create a full backup schedule', async () => { + const output = execSync( + `node create-full-backup-schedule.js ${PROJECT_ID} ${INSTANCE_ID} ${DATABASE_ID} full-backup-schedule`, + ); + assert.match(output, new RegExp('Created full backup schedule')); + assert.match( + output, + new RegExp( + `projects/${PROJECT_ID}/instances/${INSTANCE_ID}/databases/${DATABASE_ID}/backupSchedules/full-backup-schedule`, + ), + ); + }); - it('shoud get a backup schedule', async () => { - const output = execSync( - `node get-backup-schedule.js ${PROJECT_ID} ${INSTANCE_ID} ${DATABASE_ID} full-backup-schedule`, - ); - assert.match( - output, - new RegExp( - `projects/${PROJECT_ID}/instances/${INSTANCE_ID}/databases/${DATABASE_ID}/backupSchedules/full-backup-schedule`, - ), - ); - }); + it('shoud create an incremental backup schedule', async () => { + const output = execSync( + `node create-incremental-backup-schedule.js ${PROJECT_ID} ${INSTANCE_ID} ${DATABASE_ID} incremental-backup-schedule`, + ); + assert.match(output, new RegExp('Created incremental backup schedule')); + assert.match( + output, + new RegExp( + `projects/${PROJECT_ID}/instances/${INSTANCE_ID}/databases/${DATABASE_ID}/backupSchedules/incremental-backup-schedule`, + ), + ); + }); - it('shoud update a backup schedule', async () => { - const output = execSync( - `node update-backup-schedule.js ${PROJECT_ID} ${INSTANCE_ID} ${DATABASE_ID} full-backup-schedule`, - ); - assert.match(output, new RegExp('Updated backup schedule')); - assert.match( - output, - new RegExp( - `projects/${PROJECT_ID}/instances/${INSTANCE_ID}/databases/${DATABASE_ID}/backupSchedules/full-backup-schedule`, - ), - ); - }); + it('shoud list backup schedules', async () => { + const output = execSync( + `node list-backup-schedules.js ${PROJECT_ID} ${INSTANCE_ID} ${DATABASE_ID}`, + ); + assert.match( + output, + new RegExp( + `projects/${PROJECT_ID}/instances/${INSTANCE_ID}/databases/${DATABASE_ID}/backupSchedules/full-backup-schedule`, + ), + ); + assert.match( + output, + new RegExp( + `projects/${PROJECT_ID}/instances/${INSTANCE_ID}/databases/${DATABASE_ID}/backupSchedules/incremental-backup-schedule`, + ), + ); + }); - it('shoud delete a backup schedule', async () => { - const output = execSync( - `node delete-backup-schedule.js ${PROJECT_ID} ${INSTANCE_ID} ${DATABASE_ID} full-backup-schedule`, - ); - assert.match(output, new RegExp('Deleted backup schedule')); - }); + it('shoud get a backup schedule', async () => { + const output = execSync( + `node get-backup-schedule.js ${PROJECT_ID} ${INSTANCE_ID} ${DATABASE_ID} full-backup-schedule`, + ); + assert.match( + output, + new RegExp( + `projects/${PROJECT_ID}/instances/${INSTANCE_ID}/databases/${DATABASE_ID}/backupSchedules/full-backup-schedule`, + ), + ); + }); - // create_backup - it('should create a backup of the database', async () => { - const instance = spanner.instance(INSTANCE_ID); - const database = instance.database(DATABASE_ID); - const query = { - sql: 'SELECT CURRENT_TIMESTAMP() as Timestamp', - }; - const [rows] = await database.run(query); - const versionTime = rows[0].toJSON().Timestamp.toISOString(); + it('shoud update a backup schedule', async () => { + const output = execSync( + `node update-backup-schedule.js ${PROJECT_ID} ${INSTANCE_ID} ${DATABASE_ID} full-backup-schedule`, + ); + assert.match(output, new RegExp('Updated backup schedule')); + assert.match( + output, + new RegExp( + `projects/${PROJECT_ID}/instances/${INSTANCE_ID}/databases/${DATABASE_ID}/backupSchedules/full-backup-schedule`, + ), + ); + }); - const output = execSync( - `${backupsCmd} createBackup ${INSTANCE_ID} ${DATABASE_ID} ${BACKUP_ID} ${PROJECT_ID} ${versionTime}`, - ); - assert.match(output, new RegExp(`Backup (.+)${BACKUP_ID} of size`)); - }); + it('shoud delete a backup schedule', async () => { + const output = execSync( + `node delete-backup-schedule.js ${PROJECT_ID} ${INSTANCE_ID} ${DATABASE_ID} full-backup-schedule`, + ); + assert.match(output, new RegExp('Deleted backup schedule')); + }); - // create_backup_with_encryption_key - it('should create an encrypted backup of the database', async () => { - const key = await getCryptoKey(KEY_LOCATION_ID1); + // create_backup + it('should create a backup of the database', async () => { + const instance = spanner.instance(INSTANCE_ID); + const database = instance.database(DATABASE_ID); + const query = { + sql: 'SELECT CURRENT_TIMESTAMP() as Timestamp', + }; + const [rows] = await database.run(query); + const versionTime = rows[0].toJSON().Timestamp.toISOString(); - const output = execSync( - `${backupsCmd} createBackupWithEncryptionKey ${INSTANCE_ID} ${DATABASE_ID} ${ENCRYPTED_BACKUP_ID} ${PROJECT_ID} ${key.name}`, - ); - assert.match( - output, - new RegExp(`Backup (.+)${ENCRYPTED_BACKUP_ID} of size`), - ); - assert.include(output, `using encryption key ${key.name}`); - }); + const output = execSync( + `${backupsCmd} createBackup ${INSTANCE_ID} ${DATABASE_ID} ${BACKUP_ID} ${PROJECT_ID} ${versionTime}`, + ); + assert.match(output, new RegExp(`Backup (.+)${BACKUP_ID} of size`)); + }); - // copy_backup - it('should create a copy of a backup', async () => { - const sourceBackupPath = `projects/${PROJECT_ID}/instances/${INSTANCE_ID}/backups/${BACKUP_ID}`; - const output = execSync( - `node backups-copy.js ${INSTANCE_ID} ${COPY_BACKUP_ID} ${sourceBackupPath} ${PROJECT_ID}`, - ); - assert.match( - output, - new RegExp(`(.*)Backup copy(.*)${COPY_BACKUP_ID} of size(.*)`), - ); - }); + // create_backup_with_encryption_key + it('should create an encrypted backup of the database', async () => { + const key = await getCryptoKey(KEY_LOCATION_ID1); - // cancel_backup - it('should cancel a backup of the database', async () => { - const output = execSync( - `${backupsCmd} cancelBackup ${INSTANCE_ID} ${DATABASE_ID} ${CANCELLED_BACKUP_ID} ${PROJECT_ID}`, - ); - assert.match(output, /Backup cancelled./); - }); + const output = execSync( + `${backupsCmd} createBackupWithEncryptionKey ${INSTANCE_ID} ${DATABASE_ID} ${ENCRYPTED_BACKUP_ID} ${PROJECT_ID} ${key.name}`, + ); + assert.match( + output, + new RegExp(`Backup (.+)${ENCRYPTED_BACKUP_ID} of size`), + ); + assert.include(output, `using encryption key ${key.name}`); + }); - // get_backups - it('should list backups in the instance', async () => { - const output = execSync( - `${backupsCmd} getBackups ${INSTANCE_ID} ${DATABASE_ID} ${BACKUP_ID} ${PROJECT_ID}`, - ); - assert.include(output, 'All backups:'); - assert.include(output, 'Backups matching backup name:'); - assert.include(output, 'Backups expiring within 30 days:'); - assert.include(output, 'Backups matching database name:'); - assert.include(output, 'Backups filtered by size:'); - assert.include(output, 'Ready backups filtered by create time:'); - assert.include(output, 'Get backups paginated:'); - const count = (output.match(new RegExp(`${BACKUP_ID}`, 'g')) || []).length; - assert.equal(count, 14); - }); + // copy_backup + it('should create a copy of a backup', async () => { + const sourceBackupPath = `projects/${PROJECT_ID}/instances/${INSTANCE_ID}/backups/${BACKUP_ID}`; + const output = execSync( + `node backups-copy.js ${INSTANCE_ID} ${COPY_BACKUP_ID} ${sourceBackupPath} ${PROJECT_ID}`, + ); + assert.match( + output, + new RegExp(`(.*)Backup copy(.*)${COPY_BACKUP_ID} of size(.*)`), + ); + }); - // list_backup_operations - it('should list backup operations in the instance', async () => { - const output = execSync( - `${backupsCmd} getBackupOperations ${INSTANCE_ID} ${DATABASE_ID} ${BACKUP_ID} ${PROJECT_ID}`, - ); - assert.match(output, /Create Backup Operations:/); - assert.match( - output, - new RegExp(`Backup (.+)${BACKUP_ID} (.+) is 100% complete`), - ); - assert.match(output, /Copy Backup Operations:/); - assert.match( - output, - new RegExp(`Backup (.+)${COPY_BACKUP_ID} (.+) is 100% complete`), - ); - }); + // cancel_backup + it('should cancel a backup of the database', async () => { + const output = execSync( + `${backupsCmd} cancelBackup ${INSTANCE_ID} ${DATABASE_ID} ${CANCELLED_BACKUP_ID} ${PROJECT_ID}`, + ); + assert.match(output, /Backup cancelled./); + }); - // update_backup_expire_time - it('should update the expire time of a backup', async () => { - const output = execSync( - `${backupsCmd} updateBackup ${INSTANCE_ID} ${BACKUP_ID} ${PROJECT_ID}`, - ); - assert.match(output, /Expire time updated./); - }); + // get_backups + it('should list backups in the instance', async () => { + const output = execSync( + `${backupsCmd} getBackups ${INSTANCE_ID} ${DATABASE_ID} ${BACKUP_ID} ${PROJECT_ID}`, + ); + assert.include(output, 'All backups:'); + assert.include(output, 'Backups matching backup name:'); + assert.include(output, 'Backups expiring within 30 days:'); + assert.include(output, 'Backups matching database name:'); + assert.include(output, 'Backups filtered by size:'); + assert.include(output, 'Ready backups filtered by create time:'); + assert.include(output, 'Get backups paginated:'); + const count = (output.match(new RegExp(`${BACKUP_ID}`, 'g')) || []) + .length; + assert.equal(count, 14); + }); + + // list_backup_operations + it('should list backup operations in the instance', async () => { + const output = execSync( + `${backupsCmd} getBackupOperations ${INSTANCE_ID} ${DATABASE_ID} ${BACKUP_ID} ${PROJECT_ID}`, + ); + assert.match(output, /Create Backup Operations:/); + assert.match( + output, + new RegExp(`Backup (.+)${BACKUP_ID} (.+) is 100% complete`), + ); + assert.match(output, /Copy Backup Operations:/); + assert.match( + output, + new RegExp(`Backup (.+)${COPY_BACKUP_ID} (.+) is 100% complete`), + ); + }); - // restore_backup - it('should restore database from a backup', async function () { - // Restoring a backup can be a slow operation so the test may timeout and - // we'll have to retry. - this.retries(5); - // Delay the start of the test, if this is a retry. - await delay(this.test, async () => { - await cleanupDatabase(INSTANCE_ID, RESTORE_DATABASE_ID); + // update_backup_expire_time + it('should update the expire time of a backup', async () => { + const output = execSync( + `${backupsCmd} updateBackup ${INSTANCE_ID} ${BACKUP_ID} ${PROJECT_ID}`, + ); + assert.match(output, /Expire time updated./); }); - const output = execSync( - `${backupsCmd} restoreBackup ${INSTANCE_ID} ${RESTORE_DATABASE_ID} ${BACKUP_ID} ${PROJECT_ID}`, - ); - assert.match(output, /Database restored from backup./); - assert.match( - output, - new RegExp( - `Database (.+) was restored to ${RESTORE_DATABASE_ID} from backup ` + - `(.+)${BACKUP_ID} with version time (.+)`, - ), - ); - }); + // restore_backup + it('should restore database from a backup', async function () { + // Restoring a backup can be a slow operation so the test may timeout and + // we'll have to retry. + this.retries(5); + // Delay the start of the test, if this is a retry. + await delay(this.test, async () => { + await cleanupDatabase(INSTANCE_ID, RESTORE_DATABASE_ID); + }); - // restore_backup_with_encryption_key - it('should restore database from a backup using an encryption key', async function () { - // Restoring a backup can be a slow operation so the test may timeout and - // we'll have to retry. - this.retries(5); - // Delay the start of the test, if this is a retry. - await delay(this.test, async () => { - await cleanupDatabase(INSTANCE_ID, ENCRYPTED_RESTORE_DATABASE_ID); + const output = execSync( + `${backupsCmd} restoreBackup ${INSTANCE_ID} ${RESTORE_DATABASE_ID} ${BACKUP_ID} ${PROJECT_ID}`, + ); + assert.match(output, /Database restored from backup./); + assert.match( + output, + new RegExp( + `Database (.+) was restored to ${RESTORE_DATABASE_ID} from backup ` + + `(.+)${BACKUP_ID} with version time (.+)`, + ), + ); }); - const key = await getCryptoKey(KEY_LOCATION_ID1); + // restore_backup_with_encryption_key + it('should restore database from a backup using an encryption key', async function () { + // Restoring a backup can be a slow operation so the test may timeout and + // we'll have to retry. + this.retries(5); + // Delay the start of the test, if this is a retry. + await delay(this.test, async () => { + await cleanupDatabase(INSTANCE_ID, ENCRYPTED_RESTORE_DATABASE_ID); + }); - const output = execSync( - `${backupsCmd} restoreBackupWithEncryptionKey ${INSTANCE_ID} ${ENCRYPTED_RESTORE_DATABASE_ID} ${ENCRYPTED_BACKUP_ID} ${PROJECT_ID} ${key.name}`, - ); - assert.match(output, /Database restored from backup./); - assert.match( - output, - new RegExp( - `Database (.+) was restored to ${ENCRYPTED_RESTORE_DATABASE_ID} from backup ` + - `(.+)${ENCRYPTED_BACKUP_ID} using encryption key ${key.name}`, - ), - ); - }); + const key = await getCryptoKey(KEY_LOCATION_ID1); - // list_database_operations - it('should list database operations in the instance', async () => { - const output = execSync( - `${backupsCmd} getDatabaseOperations ${INSTANCE_ID} ${PROJECT_ID}`, - ); - assert.match(output, /Optimize Database Operations:/); - assert.match( - output, - new RegExp( - `Database (.+)${RESTORE_DATABASE_ID} restored from backup is (\\d+)% ` + - 'optimized', - ), - ); - }); + const output = execSync( + `${backupsCmd} restoreBackupWithEncryptionKey ${INSTANCE_ID} ${ENCRYPTED_RESTORE_DATABASE_ID} ${ENCRYPTED_BACKUP_ID} ${PROJECT_ID} ${key.name}`, + ); + assert.match(output, /Database restored from backup./); + assert.match( + output, + new RegExp( + `Database (.+) was restored to ${ENCRYPTED_RESTORE_DATABASE_ID} from backup ` + + `(.+)${ENCRYPTED_BACKUP_ID} using encryption key ${key.name}`, + ), + ); + }); - // delete_backup - it('should delete a backup', async () => { - function sleep(timeMillis) { - return new Promise(resolve => setTimeout(resolve, timeMillis)); - } + // list_database_operations + it('should list database operations in the instance', async () => { + const output = execSync( + `${backupsCmd} getDatabaseOperations ${INSTANCE_ID} ${PROJECT_ID}`, + ); + assert.match(output, /Optimize Database Operations:/); + assert.match( + output, + new RegExp( + `Database (.+)${RESTORE_DATABASE_ID} restored from backup is (\\d+)% ` + + 'optimized', + ), + ); + }); - // Wait for database to finish optimizing - cannot delete a backup if a database restored from it - const instance = spanner.instance(INSTANCE_ID); - const database = instance.database(RESTORE_DATABASE_ID); - while ((await database.getState()) === 'READY_OPTIMIZING') { - await sleep(1000); - } + // delete_backup + it('should delete a backup', async () => { + function sleep(timeMillis) { + return new Promise(resolve => setTimeout(resolve, timeMillis)); + } - const output = execSync( - `${backupsCmd} deleteBackup ${INSTANCE_ID} ${BACKUP_ID} ${PROJECT_ID}`, - ); - assert.match(output, /Backup deleted./); + // Wait for database to finish optimizing - cannot delete a backup if a database restored from it + const instance = spanner.instance(INSTANCE_ID); + const database = instance.database(RESTORE_DATABASE_ID); + while ((await database.getState()) === 'READY_OPTIMIZING') { + await sleep(1000); + } + + const output = execSync( + `${backupsCmd} deleteBackup ${INSTANCE_ID} ${BACKUP_ID} ${PROJECT_ID}`, + ); + assert.match(output, /Backup deleted./); + }); }); // custom_timeout_and_retry @@ -2426,7 +2440,15 @@ describe('Autogenerated Admin Clients', () => { const MR_CMEK_RESTORED = `test-mr-${CURRENT_TIME}-restored`; let instance_already_exists = false; let key1, key2, key3; - before(async () => { + let shouldCleanup = false; + before(async function () { + if ( + SKIP_BACKUPS === 'true' || + (KOKORO_JOB_NAME && KOKORO_JOB_NAME.includes('presubmit')) + ) { + this.skip(); + } + shouldCleanup = true; // Create multiple KMS keys covering `nam3`. key1 = await getCryptoKey(KEY_LOCATION_ID1); key2 = await getCryptoKey(KEY_LOCATION_ID2); @@ -2456,6 +2478,9 @@ describe('Autogenerated Admin Clients', () => { }); after(async () => { + if (!shouldCleanup) { + return; + } const instance = spanner.instance(MULTI_REGION_INSTANCE_ID); const restored_db = instance.database(MR_CMEK_RESTORED); function sleep(timeMillis) {