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.procedure;
019
020import static org.apache.hadoop.hbase.coprocessor.CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY;
021import static org.junit.Assert.fail;
022
023import java.io.IOException;
024import java.util.List;
025import java.util.Optional;
026import org.apache.hadoop.conf.Configuration;
027import org.apache.hadoop.hbase.HBaseClassTestRule;
028import org.apache.hadoop.hbase.HBaseTestingUtility;
029import org.apache.hadoop.hbase.TableName;
030import org.apache.hadoop.hbase.client.RegionInfo;
031import org.apache.hadoop.hbase.client.TableDescriptor;
032import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor;
033import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
034import org.apache.hadoop.hbase.coprocessor.MasterObserver;
035import org.apache.hadoop.hbase.coprocessor.ObserverContext;
036import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv;
037import org.apache.hadoop.hbase.procedure2.Procedure;
038import org.apache.hadoop.hbase.security.AccessDeniedException;
039import org.apache.hadoop.hbase.testclassification.MediumTests;
040import org.apache.hadoop.hbase.util.Bytes;
041import org.junit.After;
042import org.junit.BeforeClass;
043import org.junit.ClassRule;
044import org.junit.Test;
045import org.junit.experimental.categories.Category;
046
047import org.slf4j.Logger;
048import org.slf4j.LoggerFactory;
049
050import org.apache.hadoop.hbase.shaded.protobuf.generated.ProcedureProtos;
051
052/**
053 * Check if CompletedProcedureCleaner cleans up failed nonce procedures.
054 */
055@Category(MediumTests.class)
056public class TestFailedProcCleanup {
057
058  @ClassRule
059  public static final HBaseClassTestRule CLASS_RULE =
060      HBaseClassTestRule.forClass(TestFailedProcCleanup.class);
061
062  private static final Logger LOG = LoggerFactory.getLogger(TestFailedProcCleanup.class);
063
064  protected static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
065  private static Configuration conf;
066  private static final TableName TABLE = TableName.valueOf("test");
067  private static final byte[] FAMILY = Bytes.toBytesBinary("f");
068  private static final int evictionDelay = 10 * 1000;
069
070  @BeforeClass
071  public static void setUpBeforeClass() {
072    conf = TEST_UTIL.getConfiguration();
073    conf.setInt("hbase.procedure.cleaner.evict.ttl", evictionDelay);
074    conf.setInt("hbase.procedure.cleaner.evict.batch.size", 1);
075  }
076
077  @After
078  public void tearDown() throws Exception {
079    TEST_UTIL.shutdownMiniCluster();
080  }
081
082  @Test
083  public void testFailCreateTable() throws Exception {
084    conf.set(MASTER_COPROCESSOR_CONF_KEY, CreateFailObserver.class.getName());
085    TEST_UTIL.startMiniCluster(3);
086    try {
087      TEST_UTIL.createTable(TABLE, FAMILY);
088    } catch (AccessDeniedException e) {
089      LOG.debug("Ignoring exception: ", e);
090      Thread.sleep(evictionDelay * 3);
091    }
092    List<Procedure<MasterProcedureEnv>> procedureInfos =
093        TEST_UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor().getProcedures();
094    for (Procedure procedureInfo : procedureInfos) {
095      if (procedureInfo.getProcName().equals("CreateTableProcedure")
096          && procedureInfo.getState() == ProcedureProtos.ProcedureState.ROLLEDBACK) {
097        fail("Found procedure " + procedureInfo + " that hasn't been cleaned up");
098      }
099    }
100  }
101
102  @Test
103  public void testFailCreateTableAction() throws Exception {
104    conf.set(MASTER_COPROCESSOR_CONF_KEY, CreateFailObserverHandler.class.getName());
105    TEST_UTIL.startMiniCluster(3);
106    try {
107      TEST_UTIL.createTable(TABLE, FAMILY);
108      fail("Table shouldn't be created");
109    } catch (AccessDeniedException e) {
110      LOG.debug("Ignoring exception: ", e);
111      Thread.sleep(evictionDelay * 3);
112    }
113    List<Procedure<MasterProcedureEnv>> procedureInfos =
114        TEST_UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor().getProcedures();
115    for (Procedure procedureInfo : procedureInfos) {
116      if (procedureInfo.getProcName().equals("CreateTableProcedure")
117          && procedureInfo.getState() == ProcedureProtos.ProcedureState.ROLLEDBACK) {
118        fail("Found procedure " + procedureInfo + " that hasn't been cleaned up");
119      }
120    }
121  }
122
123  public static class CreateFailObserver implements MasterCoprocessor, MasterObserver {
124
125    @Override
126    public void preCreateTable(ObserverContext<MasterCoprocessorEnvironment> env,
127        TableDescriptor desc, RegionInfo[] regions) throws IOException {
128
129      if (desc.getTableName().equals(TABLE)) {
130        throw new AccessDeniedException("Don't allow creation of table");
131      }
132    }
133
134    @Override
135    public Optional<MasterObserver> getMasterObserver() {
136      return Optional.of(this);
137    }
138  }
139
140  public static class CreateFailObserverHandler implements MasterCoprocessor, MasterObserver {
141
142    @Override
143    public void preCreateTableAction(
144        final ObserverContext<MasterCoprocessorEnvironment> ctx, final TableDescriptor desc,
145        final RegionInfo[] regions) throws IOException {
146
147      if (desc.getTableName().equals(TABLE)) {
148        throw new AccessDeniedException("Don't allow creation of table");
149      }
150    }
151
152    @Override
153    public Optional<MasterObserver> getMasterObserver() {
154      return Optional.of(this);
155    }
156  }
157}