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.jupiter.api.Assertions.assertNotNull;
021import static org.junit.jupiter.api.Assertions.assertNull;
022import static org.junit.jupiter.api.Assertions.assertTrue;
023
024import java.io.IOException;
025import org.apache.hadoop.conf.Configuration;
026import org.apache.hadoop.hbase.HBaseTestingUtil;
027import org.apache.hadoop.hbase.NamespaceDescriptor;
028import org.apache.hadoop.hbase.NamespaceNotFoundException;
029import org.apache.hadoop.hbase.TableName;
030import org.apache.hadoop.hbase.client.TableDescriptor;
031import org.apache.hadoop.hbase.constraint.ConstraintException;
032import org.apache.hadoop.hbase.procedure2.Procedure;
033import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
034import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
035import org.apache.hadoop.hbase.testclassification.MasterTests;
036import org.apache.hadoop.hbase.testclassification.MediumTests;
037import org.junit.jupiter.api.AfterAll;
038import org.junit.jupiter.api.AfterEach;
039import org.junit.jupiter.api.BeforeAll;
040import org.junit.jupiter.api.BeforeEach;
041import org.junit.jupiter.api.Tag;
042import org.junit.jupiter.api.Test;
043import org.junit.jupiter.api.TestInfo;
044import org.slf4j.Logger;
045import org.slf4j.LoggerFactory;
046
047@Tag(MasterTests.TAG)
048@Tag(MediumTests.TAG)
049public class TestDeleteNamespaceProcedure {
050
051  private static final Logger LOG = LoggerFactory.getLogger(TestDeleteNamespaceProcedure.class);
052
053  protected static final HBaseTestingUtil UTIL = new HBaseTestingUtil();
054  private String testMethodName;
055
056  @BeforeEach
057  public void setTestMethod(TestInfo testInfo) {
058    testMethodName = testInfo.getTestMethod().get().getName();
059  }
060
061  private static void setupConf(Configuration conf) {
062    conf.setInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS, 1);
063  }
064
065  @BeforeAll
066  public static void setupCluster() throws Exception {
067    setupConf(UTIL.getConfiguration());
068    UTIL.startMiniCluster(1);
069  }
070
071  @AfterAll
072  public static void cleanupTest() throws Exception {
073    try {
074      UTIL.shutdownMiniCluster();
075    } catch (Exception e) {
076      LOG.warn("failure shutting down cluster", e);
077    }
078  }
079
080  @BeforeEach
081  public void setup() throws Exception {
082    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false);
083  }
084
085  @AfterEach
086  public void tearDown() throws Exception {
087    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false);
088    for (TableDescriptor htd : UTIL.getAdmin().listTableDescriptors()) {
089      LOG.info("Tear down, remove table=" + htd.getTableName());
090      UTIL.deleteTable(htd.getTableName());
091    }
092  }
093
094  @Test
095  public void testDeleteNamespace() throws Exception {
096    final String namespaceName = "testDeleteNamespace";
097    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
098
099    createNamespaceForTesting(namespaceName);
100
101    long procId = procExec
102      .submitProcedure(new DeleteNamespaceProcedure(procExec.getEnvironment(), namespaceName));
103    // Wait the completion
104    ProcedureTestingUtility.waitProcedure(procExec, procId);
105    ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
106
107    validateNamespaceNotExist(namespaceName);
108  }
109
110  @Test
111  public void testDeleteNonExistNamespace() throws Exception {
112    final String namespaceName = "testDeleteNonExistNamespace";
113    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
114
115    validateNamespaceNotExist(namespaceName);
116
117    long procId = procExec
118      .submitProcedure(new DeleteNamespaceProcedure(procExec.getEnvironment(), namespaceName));
119    // Wait the completion
120    ProcedureTestingUtility.waitProcedure(procExec, procId);
121    // Expect fail with NamespaceNotFoundException
122    Procedure<?> result = procExec.getResult(procId);
123    assertTrue(result.isFailed());
124    LOG.debug("Delete namespace failed with exception: " + result.getException());
125    assertTrue(
126      ProcedureTestingUtility.getExceptionCause(result) instanceof NamespaceNotFoundException);
127  }
128
129  @Test
130  public void testDeleteSystemNamespace() throws Exception {
131    final String namespaceName = NamespaceDescriptor.SYSTEM_NAMESPACE.getName();
132    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
133
134    long procId = procExec
135      .submitProcedure(new DeleteNamespaceProcedure(procExec.getEnvironment(), namespaceName));
136    // Wait the completion
137    ProcedureTestingUtility.waitProcedure(procExec, procId);
138    Procedure<?> result = procExec.getResult(procId);
139    assertTrue(result.isFailed());
140    LOG.debug("Delete namespace failed with exception: " + result.getException());
141    assertTrue(ProcedureTestingUtility.getExceptionCause(result) instanceof ConstraintException);
142  }
143
144  @Test
145  public void testDeleteNonEmptyNamespace() throws Exception {
146    final String namespaceName = "testDeleteNonExistNamespace";
147    final TableName tableName = TableName.valueOf("testDeleteNonExistNamespace:" + testMethodName);
148    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
149    // create namespace
150    createNamespaceForTesting(namespaceName);
151    // create the table under the new namespace
152    MasterProcedureTestingUtility.createTable(procExec, tableName, null, "f1");
153
154    long procId = procExec
155      .submitProcedure(new DeleteNamespaceProcedure(procExec.getEnvironment(), namespaceName));
156    // Wait the completion
157    ProcedureTestingUtility.waitProcedure(procExec, procId);
158    Procedure<?> result = procExec.getResult(procId);
159    assertTrue(result.isFailed());
160    LOG.debug("Delete namespace failed with exception: " + result.getException());
161    assertTrue(ProcedureTestingUtility.getExceptionCause(result) instanceof ConstraintException);
162  }
163
164  @Test
165  public void testRecoveryAndDoubleExecution() throws Exception {
166    final String namespaceName = "testRecoveryAndDoubleExecution";
167    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
168
169    createNamespaceForTesting(namespaceName);
170
171    ProcedureTestingUtility.waitNoProcedureRunning(procExec);
172    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
173
174    // Start the DeleteNamespace procedure && kill the executor
175    long procId = procExec
176      .submitProcedure(new DeleteNamespaceProcedure(procExec.getEnvironment(), namespaceName));
177
178    // Restart the executor and execute the step twice
179    MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(procExec, procId);
180
181    // Validate the deletion of namespace
182    ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
183    validateNamespaceNotExist(namespaceName);
184  }
185
186  @Test
187  public void testRollbackAndDoubleExecution() throws Exception {
188    final String namespaceName = "testRollbackAndDoubleExecution";
189    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
190
191    createNamespaceForTesting(namespaceName);
192
193    ProcedureTestingUtility.waitNoProcedureRunning(procExec);
194    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
195
196    // Start the DeleteNamespace procedure && kill the executor
197    long procId = procExec
198      .submitProcedure(new DeleteNamespaceProcedure(procExec.getEnvironment(), namespaceName));
199
200    int lastStep = 2; // failing before DELETE_NAMESPACE_DELETE_FROM_NS_TABLE
201    MasterProcedureTestingUtility.testRollbackAndDoubleExecution(procExec, procId, lastStep);
202
203    // Validate the namespace still exists
204    NamespaceDescriptor createdNsDescriptor = UTIL.getAdmin().getNamespaceDescriptor(namespaceName);
205    assertNotNull(createdNsDescriptor);
206  }
207
208  private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() {
209    return UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor();
210  }
211
212  private void createNamespaceForTesting(final String namespaceName) throws Exception {
213    final NamespaceDescriptor nsd = NamespaceDescriptor.create(namespaceName).build();
214    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
215
216    long procId =
217      procExec.submitProcedure(new CreateNamespaceProcedure(procExec.getEnvironment(), nsd));
218    // Wait the completion
219    ProcedureTestingUtility.waitProcedure(procExec, procId);
220    ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
221  }
222
223  public static void validateNamespaceNotExist(final String nsName) throws IOException {
224    try {
225      NamespaceDescriptor nsDescriptor = UTIL.getAdmin().getNamespaceDescriptor(nsName);
226      assertNull(nsDescriptor);
227    } catch (NamespaceNotFoundException nsnfe) {
228      // Expected
229    }
230  }
231}