001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.backup;
019
020import static org.apache.hadoop.hbase.backup.BackupRestoreConstants.CONF_CONTINUOUS_BACKUP_WAL_DIR;
021import static org.apache.hadoop.hbase.backup.replication.ContinuousBackupReplicationEndpoint.ONE_DAY_IN_MILLISECONDS;
022import static org.junit.jupiter.api.Assertions.assertEquals;
023
024import java.io.IOException;
025import org.apache.hadoop.fs.FileStatus;
026import org.apache.hadoop.fs.FileSystem;
027import org.apache.hadoop.fs.FileUtil;
028import org.apache.hadoop.fs.Path;
029import org.apache.hadoop.hbase.TableName;
030import org.apache.hadoop.hbase.testclassification.LargeTests;
031import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
032import org.apache.hadoop.util.ToolRunner;
033import org.junit.jupiter.api.AfterAll;
034import org.junit.jupiter.api.BeforeAll;
035import org.junit.jupiter.api.Tag;
036import org.junit.jupiter.api.Test;
037
038@Tag(LargeTests.TAG)
039public class TestPointInTimeRestoreWithCustomBackupPath extends TestBackupBase {
040
041  private static final String backupWalDirName = "TestCustomBackupWalDir";
042  private static final String customBackupDirName = "CustomBackupRoot";
043
044  private static Path backupWalDir;
045  private static Path customBackupDir;
046  private static FileSystem fs;
047
048  @BeforeAll
049  public static void setupBeforeClass() throws Exception {
050    Path root = TEST_UTIL.getDataTestDirOnTestFS();
051    backupWalDir = new Path(root, backupWalDirName);
052    customBackupDir = new Path(root, customBackupDirName);
053
054    fs = FileSystem.get(conf1);
055    fs.mkdirs(backupWalDir);
056    fs.mkdirs(customBackupDir);
057
058    conf1.set(CONF_CONTINUOUS_BACKUP_WAL_DIR, backupWalDir.toString());
059
060    createAndCopyBackupData();
061  }
062
063  private static void createAndCopyBackupData() throws Exception {
064    // Simulate time 10 days ago
065    EnvironmentEdgeManager
066      .injectEdge(() -> System.currentTimeMillis() - 10 * ONE_DAY_IN_MILLISECONDS);
067    PITRTestUtil.loadRandomData(TEST_UTIL, table1, famName, 1000);
068
069    // Perform backup with continuous backup enabled
070    String[] args =
071      PITRTestUtil.buildBackupArgs("full", new TableName[] { table1 }, true, BACKUP_ROOT_DIR);
072    int ret = ToolRunner.run(conf1, new BackupDriver(), args);
073    assertEquals(0, ret, "Backup should succeed");
074
075    PITRTestUtil.waitForReplication();
076
077    // Copy the contents of BACKUP_ROOT_DIR to the new customBackupDir
078    Path defaultBackupDir = new Path(BACKUP_ROOT_DIR);
079    for (FileStatus status : fs.listStatus(defaultBackupDir)) {
080      Path dst = new Path(customBackupDir, status.getPath().getName());
081      FileUtil.copy(fs, status, fs, dst, true, false, conf1);
082    }
083
084    EnvironmentEdgeManager.reset();
085  }
086
087  @AfterAll
088  public static void cleanupAfterClass() throws IOException {
089    if (fs.exists(backupWalDir)) {
090      fs.delete(backupWalDir, true);
091    }
092    if (fs.exists(customBackupDir)) {
093      fs.delete(customBackupDir, true);
094    }
095
096    conf1.unset(CONF_CONTINUOUS_BACKUP_WAL_DIR);
097  }
098
099  @Test
100  public void testPITR_FromCustomBackupRootDir() throws Exception {
101    TableName restoredTable = TableName.valueOf("restoredTableCustomPath");
102
103    long restoreTime = EnvironmentEdgeManager.currentTime() - 2 * ONE_DAY_IN_MILLISECONDS;
104
105    String[] args = PITRTestUtil.buildPITRArgs(new TableName[] { table1 },
106      new TableName[] { restoredTable }, restoreTime, customBackupDir.toString());
107
108    int ret = ToolRunner.run(conf1, new PointInTimeRestoreDriver(), args);
109    assertEquals(0, ret, "PITR should succeed with custom backup root dir");
110
111    // Validate that the restored table has same row count
112    assertEquals(PITRTestUtil.getRowCount(TEST_UTIL, table1),
113      PITRTestUtil.getRowCount(TEST_UTIL, restoredTable), "Restored table should match row count");
114  }
115}