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.master.snapshot;
019
020import static org.junit.Assert.assertFalse;
021import static org.junit.Assert.assertTrue;
022import static org.junit.Assert.fail;
023
024import java.io.IOException;
025import org.apache.hadoop.conf.Configuration;
026import org.apache.hadoop.fs.FileSystem;
027import org.apache.hadoop.fs.Path;
028import org.apache.hadoop.hbase.HBaseClassTestRule;
029import org.apache.hadoop.hbase.HBaseTestingUtility;
030import org.apache.hadoop.hbase.TableName;
031import org.apache.hadoop.hbase.executor.ExecutorService;
032import org.apache.hadoop.hbase.master.MasterFileSystem;
033import org.apache.hadoop.hbase.master.MasterServices;
034import org.apache.hadoop.hbase.master.cleaner.HFileCleaner;
035import org.apache.hadoop.hbase.master.cleaner.HFileLinkCleaner;
036import org.apache.hadoop.hbase.procedure.ProcedureCoordinator;
037import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils;
038import org.apache.hadoop.hbase.testclassification.MasterTests;
039import org.apache.hadoop.hbase.testclassification.SmallTests;
040import org.apache.zookeeper.KeeperException;
041import org.junit.ClassRule;
042import org.junit.Rule;
043import org.junit.Test;
044import org.junit.experimental.categories.Category;
045import org.junit.rules.TestName;
046import org.mockito.Mockito;
047
048/**
049 * Test basic snapshot manager functionality
050 */
051@Category({MasterTests.class, SmallTests.class})
052public class TestSnapshotManager {
053
054  @ClassRule
055  public static final HBaseClassTestRule CLASS_RULE =
056      HBaseClassTestRule.forClass(TestSnapshotManager.class);
057
058  private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
059
060  @Rule
061  public TestName name = new TestName();
062
063  MasterServices services = Mockito.mock(MasterServices.class);
064  ProcedureCoordinator coordinator = Mockito.mock(ProcedureCoordinator.class);
065  ExecutorService pool = Mockito.mock(ExecutorService.class);
066  MasterFileSystem mfs = Mockito.mock(MasterFileSystem.class);
067  FileSystem fs;
068  {
069    try {
070      fs = UTIL.getTestFileSystem();
071    } catch (IOException e) {
072      throw new RuntimeException("Couldn't get test filesystem", e);
073    }
074  }
075
076   private SnapshotManager getNewManager() throws IOException, KeeperException {
077    return getNewManager(UTIL.getConfiguration());
078  }
079
080  private SnapshotManager getNewManager(Configuration conf) throws IOException, KeeperException {
081    return getNewManager(conf, 1);
082  }
083
084  private SnapshotManager getNewManager(Configuration conf, int intervalSeconds)
085      throws IOException, KeeperException {
086    Mockito.reset(services);
087    Mockito.when(services.getConfiguration()).thenReturn(conf);
088    Mockito.when(services.getMasterFileSystem()).thenReturn(mfs);
089    Mockito.when(mfs.getFileSystem()).thenReturn(fs);
090    Mockito.when(mfs.getRootDir()).thenReturn(UTIL.getDataTestDir());
091    return new SnapshotManager(services, coordinator, pool, intervalSeconds);
092  }
093
094  @Test
095  public void testCleanFinishedHandler() throws Exception {
096    TableName tableName = TableName.valueOf(name.getMethodName());
097    Configuration conf = UTIL.getConfiguration();
098    try {
099      conf.setLong(SnapshotManager.HBASE_SNAPSHOT_SENTINELS_CLEANUP_TIMEOUT_MILLIS, 5 * 1000L);
100      SnapshotManager manager = getNewManager(conf, 1);
101      TakeSnapshotHandler handler = Mockito.mock(TakeSnapshotHandler.class);
102      assertFalse("Manager is in process when there is no current handler",
103        manager.isTakingSnapshot(tableName));
104      manager.setSnapshotHandlerForTesting(tableName, handler);
105      Mockito.when(handler.isFinished()).thenReturn(false);
106      assertTrue(manager.isTakingAnySnapshot());
107      assertTrue("Manager isn't in process when handler is running",
108        manager.isTakingSnapshot(tableName));
109      Mockito.when(handler.isFinished()).thenReturn(true);
110      assertFalse("Manager is process when handler isn't running",
111        manager.isTakingSnapshot(tableName));
112      assertTrue(manager.isTakingAnySnapshot());
113      Thread.sleep(6 * 1000);
114      assertFalse(manager.isTakingAnySnapshot());
115    } finally {
116      conf.unset(SnapshotManager.HBASE_SNAPSHOT_SENTINELS_CLEANUP_TIMEOUT_MILLIS);
117    }
118  }
119
120  @Test
121  public void testInProcess() throws KeeperException, IOException {
122    final TableName tableName = TableName.valueOf(name.getMethodName());
123    SnapshotManager manager = getNewManager();
124    TakeSnapshotHandler handler = Mockito.mock(TakeSnapshotHandler.class);
125    assertFalse("Manager is in process when there is no current handler",
126        manager.isTakingSnapshot(tableName));
127    manager.setSnapshotHandlerForTesting(tableName, handler);
128    Mockito.when(handler.isFinished()).thenReturn(false);
129    assertTrue("Manager isn't in process when handler is running",
130        manager.isTakingSnapshot(tableName));
131    Mockito.when(handler.isFinished()).thenReturn(true);
132    assertFalse("Manager is process when handler isn't running",
133        manager.isTakingSnapshot(tableName));
134  }
135
136  /**
137   * Verify the snapshot support based on the configuration.
138   */
139  @Test
140  public void testSnapshotSupportConfiguration() throws Exception {
141    // No configuration (no cleaners, not enabled): snapshot feature disabled
142    Configuration conf = new Configuration();
143    SnapshotManager manager = getNewManager(conf);
144    assertFalse("Snapshot should be disabled with no configuration", isSnapshotSupported(manager));
145
146    // force snapshot feature to be enabled
147    conf = new Configuration();
148    conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
149    manager = getNewManager(conf);
150    assertTrue("Snapshot should be enabled", isSnapshotSupported(manager));
151
152    // force snapshot feature to be disabled
153    conf = new Configuration();
154    conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, false);
155    manager = getNewManager(conf);
156    assertFalse("Snapshot should be disabled", isSnapshotSupported(manager));
157
158    // force snapshot feature to be disabled, even if cleaners are present
159    conf = new Configuration();
160    conf.setStrings(HFileCleaner.MASTER_HFILE_CLEANER_PLUGINS,
161      SnapshotHFileCleaner.class.getName(), HFileLinkCleaner.class.getName());
162    conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, false);
163    manager = getNewManager(conf);
164    assertFalse("Snapshot should be disabled", isSnapshotSupported(manager));
165
166    // cleaners are present, but missing snapshot enabled property
167    conf = new Configuration();
168    conf.setStrings(HFileCleaner.MASTER_HFILE_CLEANER_PLUGINS,
169      SnapshotHFileCleaner.class.getName(), HFileLinkCleaner.class.getName());
170    manager = getNewManager(conf);
171    assertTrue("Snapshot should be enabled, because cleaners are present",
172      isSnapshotSupported(manager));
173
174    // Create a "test snapshot"
175    Path rootDir = UTIL.getDataTestDir();
176    Path testSnapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(
177      "testSnapshotSupportConfiguration", rootDir);
178    fs.mkdirs(testSnapshotDir);
179    try {
180      // force snapshot feature to be disabled, but snapshots are present
181      conf = new Configuration();
182      conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, false);
183      manager = getNewManager(conf);
184      fail("Master should not start when snapshot is disabled, but snapshots are present");
185    } catch (UnsupportedOperationException e) {
186      // expected
187    } finally {
188      fs.delete(testSnapshotDir, true);
189    }
190  }
191
192  private boolean isSnapshotSupported(final SnapshotManager manager) {
193    try {
194      manager.checkSnapshotSupport();
195      return true;
196    } catch (UnsupportedOperationException e) {
197      return false;
198    }
199  }
200}