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.procedure;
019
020import static org.junit.Assert.assertTrue;
021
022import java.util.List;
023import org.apache.hadoop.hbase.HBaseClassTestRule;
024import org.apache.hadoop.hbase.HColumnDescriptor;
025import org.apache.hadoop.hbase.HTableDescriptor;
026import org.apache.hadoop.hbase.TableExistsException;
027import org.apache.hadoop.hbase.TableName;
028import org.apache.hadoop.hbase.client.Admin;
029import org.apache.hadoop.hbase.client.SnapshotDescription;
030import org.apache.hadoop.hbase.procedure2.Procedure;
031import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
032import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
033import org.apache.hadoop.hbase.snapshot.SnapshotTestingUtils;
034import org.apache.hadoop.hbase.testclassification.MasterTests;
035import org.apache.hadoop.hbase.testclassification.MediumTests;
036import org.apache.hadoop.hbase.util.Bytes;
037import org.junit.After;
038import org.junit.ClassRule;
039import org.junit.Test;
040import org.junit.experimental.categories.Category;
041import org.slf4j.Logger;
042import org.slf4j.LoggerFactory;
043
044import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
045import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos;
046
047@Category({MasterTests.class, MediumTests.class})
048public class TestCloneSnapshotProcedure extends TestTableDDLProcedureBase {
049
050  @ClassRule
051  public static final HBaseClassTestRule CLASS_RULE =
052      HBaseClassTestRule.forClass(TestCloneSnapshotProcedure.class);
053
054  private static final Logger LOG = LoggerFactory.getLogger(TestCloneSnapshotProcedure.class);
055
056  protected final byte[] CF = Bytes.toBytes("cf1");
057
058  private static SnapshotProtos.SnapshotDescription snapshot = null;
059
060  @After
061  @Override
062  public void tearDown() throws Exception {
063    super.tearDown();
064    SnapshotTestingUtils.deleteAllSnapshots(UTIL.getAdmin());
065    snapshot = null;
066  }
067
068  private SnapshotProtos.SnapshotDescription getSnapshot() throws Exception {
069    if (snapshot == null) {
070      final TableName snapshotTableName = TableName.valueOf("testCloneSnapshot");
071      long tid = System.currentTimeMillis();
072      final byte[] snapshotName = Bytes.toBytes("snapshot-" + tid);
073
074      Admin admin = UTIL.getAdmin();
075      // create Table
076      SnapshotTestingUtils.createTable(UTIL, snapshotTableName, getNumReplicas(), CF);
077      // Load data
078      SnapshotTestingUtils.loadData(UTIL, snapshotTableName, 500, CF);
079      admin.disableTable(snapshotTableName);
080      // take a snapshot
081      admin.snapshot(snapshotName, snapshotTableName);
082      admin.enableTable(snapshotTableName);
083
084      List<SnapshotDescription> snapshotList = admin.listSnapshots();
085      snapshot = ProtobufUtil.createHBaseProtosSnapshotDesc(snapshotList.get(0));
086    }
087    return snapshot;
088  }
089
090  private int getNumReplicas() {
091    return 1;
092  }
093
094  public static HTableDescriptor createHTableDescriptor(
095      final TableName tableName, final byte[] ... family) {
096    HTableDescriptor htd = new HTableDescriptor(tableName);
097    for (int i = 0; i < family.length; ++i) {
098      htd.addFamily(new HColumnDescriptor(family[i]));
099    }
100    return htd;
101  }
102
103  @Test
104  public void testCloneSnapshot() throws Exception {
105    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
106    final TableName clonedTableName = TableName.valueOf("testCloneSnapshot2");
107    final HTableDescriptor htd = createHTableDescriptor(clonedTableName, CF);
108
109    // take the snapshot
110    SnapshotProtos.SnapshotDescription snapshotDesc = getSnapshot();
111
112    long procId = ProcedureTestingUtility.submitAndWait(
113      procExec, new CloneSnapshotProcedure(procExec.getEnvironment(), htd, snapshotDesc));
114    ProcedureTestingUtility.assertProcNotFailed(procExec.getResult(procId));
115    MasterProcedureTestingUtility.validateTableIsEnabled(
116      UTIL.getHBaseCluster().getMaster(),
117      clonedTableName);
118  }
119
120  @Test
121  public void testCloneSnapshotToSameTable() throws Exception {
122    // take the snapshot
123    SnapshotProtos.SnapshotDescription snapshotDesc = getSnapshot();
124
125    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
126    final TableName clonedTableName = TableName.valueOf(snapshotDesc.getTable());
127    final HTableDescriptor htd = createHTableDescriptor(clonedTableName, CF);
128
129    long procId = ProcedureTestingUtility.submitAndWait(
130      procExec, new CloneSnapshotProcedure(procExec.getEnvironment(), htd, snapshotDesc));
131    Procedure<?> result = procExec.getResult(procId);
132    assertTrue(result.isFailed());
133    LOG.debug("Clone snapshot failed with exception: " + result.getException());
134    assertTrue(
135      ProcedureTestingUtility.getExceptionCause(result) instanceof TableExistsException);
136  }
137
138  @Test
139  public void testRecoveryAndDoubleExecution() throws Exception {
140    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
141    final TableName clonedTableName = TableName.valueOf("testRecoveryAndDoubleExecution");
142    final HTableDescriptor htd = createHTableDescriptor(clonedTableName, CF);
143
144    // take the snapshot
145    SnapshotProtos.SnapshotDescription snapshotDesc = getSnapshot();
146
147    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
148
149    // Start the Clone snapshot procedure && kill the executor
150    long procId = procExec.submitProcedure(
151      new CloneSnapshotProcedure(procExec.getEnvironment(), htd, snapshotDesc));
152
153    // Restart the executor and execute the step twice
154    MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(procExec, procId);
155
156    MasterProcedureTestingUtility.validateTableIsEnabled(
157      UTIL.getHBaseCluster().getMaster(),
158      clonedTableName);
159  }
160
161  @Test
162  public void testRollbackAndDoubleExecution() throws Exception {
163    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
164    final TableName clonedTableName = TableName.valueOf("testRollbackAndDoubleExecution");
165    final HTableDescriptor htd = createHTableDescriptor(clonedTableName, CF);
166
167    // take the snapshot
168    SnapshotProtos.SnapshotDescription snapshotDesc = getSnapshot();
169
170    ProcedureTestingUtility.waitNoProcedureRunning(procExec);
171    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
172
173    // Start the Clone snapshot procedure && kill the executor
174    long procId = procExec.submitProcedure(
175      new CloneSnapshotProcedure(procExec.getEnvironment(), htd, snapshotDesc));
176
177    int lastStep = 2; // failing before CLONE_SNAPSHOT_WRITE_FS_LAYOUT
178    MasterProcedureTestingUtility.testRollbackAndDoubleExecution(procExec, procId, lastStep);
179
180    MasterProcedureTestingUtility.validateTableDeletion(
181      UTIL.getHBaseCluster().getMaster(), clonedTableName);
182  }
183}