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.client;
019
020import static org.junit.Assert.fail;
021import static org.mockito.Mockito.mock;
022import static org.mockito.Mockito.when;
023
024import com.google.protobuf.ServiceException;
025import java.io.IOException;
026import java.util.ArrayList;
027import org.apache.hadoop.conf.Configuration;
028import org.apache.hadoop.hbase.HBaseClassTestRule;
029import org.apache.hadoop.hbase.HBaseConfiguration;
030import org.apache.hadoop.hbase.HBaseTestingUtility;
031import org.apache.hadoop.hbase.HConstants;
032import org.apache.hadoop.hbase.HTableDescriptor;
033import org.apache.hadoop.hbase.MasterNotRunningException;
034import org.apache.hadoop.hbase.PleaseHoldException;
035import org.apache.hadoop.hbase.TableName;
036import org.apache.hadoop.hbase.ZooKeeperConnectionException;
037import org.apache.hadoop.hbase.ipc.HBaseRpcController;
038import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
039import org.apache.hadoop.hbase.testclassification.ClientTests;
040import org.apache.hadoop.hbase.testclassification.SmallTests;
041import org.junit.ClassRule;
042import org.junit.Ignore;
043import org.junit.Rule;
044import org.junit.Test;
045import org.junit.experimental.categories.Category;
046import org.junit.rules.TestName;
047import org.mockito.Matchers;
048import org.mockito.Mockito;
049import org.mockito.invocation.InvocationOnMock;
050import org.mockito.stubbing.Answer;
051import org.slf4j.Logger;
052import org.slf4j.LoggerFactory;
053
054import org.apache.hbase.thirdparty.com.google.protobuf.RpcController;
055
056import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.BalanceRequest;
057import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.CreateTableRequest;
058import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.EnableCatalogJanitorRequest;
059import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.GetTableDescriptorsRequest;
060import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.GetTableNamesRequest;
061import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.IsCatalogJanitorEnabledRequest;
062import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.MoveRegionRequest;
063import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.OfflineRegionRequest;
064import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.RunCatalogScanRequest;
065import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.SetBalancerRunningRequest;
066
067@Category({ SmallTests.class, ClientTests.class })
068public class TestHBaseAdminNoCluster {
069
070  @ClassRule
071  public static final HBaseClassTestRule CLASS_RULE =
072    HBaseClassTestRule.forClass(TestHBaseAdminNoCluster.class);
073
074  private static final Logger LOG = LoggerFactory.getLogger(TestHBaseAdminNoCluster.class);
075
076  @Rule
077  public TestName name = new TestName();
078
079  /**
080   * Verify that PleaseHoldException gets retried. HBASE-8764
081   */
082  // TODO: Clean up, with Procedure V2 and nonce to prevent the same procedure to call mulitple
083  // time, this test is invalid anymore. Just keep the test around for some time before
084  // fully removing it.
085  @Ignore
086  @Test
087  public void testMasterMonitorCallableRetries()
088    throws MasterNotRunningException, ZooKeeperConnectionException, IOException,
089    org.apache.hbase.thirdparty.com.google.protobuf.ServiceException {
090    Configuration configuration = HBaseConfiguration.create();
091    // Set the pause and retry count way down.
092    configuration.setLong(HConstants.HBASE_CLIENT_PAUSE, 1);
093    final int count = 10;
094    configuration.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, count);
095    // Get mocked connection. Getting the connection will register it so when HBaseAdmin is
096    // constructed with same configuration, it will find this mocked connection.
097    ClusterConnection connection = HConnectionTestingUtility.getMockedConnection(configuration);
098    // Mock so we get back the master interface. Make it so when createTable is called, we throw
099    // the PleaseHoldException.
100    MasterKeepAliveConnection masterAdmin = Mockito.mock(MasterKeepAliveConnection.class);
101    Mockito
102      .when(
103        masterAdmin.createTable((RpcController) Mockito.any(), (CreateTableRequest) Mockito.any()))
104      .thenThrow(new ServiceException("Test fail").initCause(new PleaseHoldException("test")));
105    Mockito.when(connection.getMaster()).thenReturn(masterAdmin);
106    Admin admin = new HBaseAdmin(connection);
107    try {
108      HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name.getMethodName()));
109      // Pass any old htable descriptor; not important
110      try {
111        admin.createTable(htd, HBaseTestingUtility.KEYS_FOR_HBA_CREATE_TABLE);
112        fail();
113      } catch (RetriesExhaustedException e) {
114        LOG.info("Expected fail", e);
115      }
116      // Assert we were called 'count' times.
117      Mockito.verify(masterAdmin, Mockito.atLeast(count)).createTable((RpcController) Mockito.any(),
118        (CreateTableRequest) Mockito.any());
119    } finally {
120      admin.close();
121      if (connection != null) connection.close();
122    }
123  }
124
125  @Test
126  public void testMasterOperationsRetries() throws Exception {
127
128    // Admin.listTables()
129    testMasterOperationIsRetried(new MethodCaller() {
130      @Override
131      public void call(Admin admin) throws Exception {
132        admin.listTables();
133      }
134
135      @Override
136      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
137        Mockito.verify(masterAdmin, Mockito.atLeast(count)).getTableDescriptors(
138          (RpcController) Mockito.any(), (GetTableDescriptorsRequest) Mockito.any());
139      }
140    });
141
142    // Admin.listTableNames()
143    testMasterOperationIsRetried(new MethodCaller() {
144      @Override
145      public void call(Admin admin) throws Exception {
146        admin.listTableNames();
147      }
148
149      @Override
150      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
151        Mockito.verify(masterAdmin, Mockito.atLeast(count))
152          .getTableNames((RpcController) Mockito.any(), (GetTableNamesRequest) Mockito.any());
153      }
154    });
155
156    // Admin.getTableDescriptor()
157    testMasterOperationIsRetried(new MethodCaller() {
158      @Override
159      public void call(Admin admin) throws Exception {
160        admin.getTableDescriptor(TableName.valueOf(name.getMethodName()));
161      }
162
163      @Override
164      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
165        Mockito.verify(masterAdmin, Mockito.atLeast(count)).getTableDescriptors(
166          (RpcController) Mockito.any(), (GetTableDescriptorsRequest) Mockito.any());
167      }
168    });
169
170    // Admin.getTableDescriptorsByTableName()
171    testMasterOperationIsRetried(new MethodCaller() {
172      @Override
173      public void call(Admin admin) throws Exception {
174        admin.getTableDescriptorsByTableName(new ArrayList<>());
175      }
176
177      @Override
178      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
179        Mockito.verify(masterAdmin, Mockito.atLeast(count)).getTableDescriptors(
180          (RpcController) Mockito.any(), (GetTableDescriptorsRequest) Mockito.any());
181      }
182    });
183
184    // Admin.move()
185    testMasterOperationIsRetried(new MethodCaller() {
186      @Override
187      public void call(Admin admin) throws Exception {
188        admin.move(new byte[0]);
189      }
190
191      @Override
192      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
193        Mockito.verify(masterAdmin, Mockito.atLeast(count))
194          .moveRegion((RpcController) Mockito.any(), (MoveRegionRequest) Mockito.any());
195      }
196    });
197
198    // Admin.offline()
199    testMasterOperationIsRetried(new MethodCaller() {
200      @Override
201      public void call(Admin admin) throws Exception {
202        admin.offline(new byte[0]);
203      }
204
205      @Override
206      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
207        Mockito.verify(masterAdmin, Mockito.atLeast(count))
208          .offlineRegion((RpcController) Mockito.any(), (OfflineRegionRequest) Mockito.any());
209      }
210    });
211
212    // Admin.setBalancerRunning()
213    testMasterOperationIsRetried(new MethodCaller() {
214      @Override
215      public void call(Admin admin) throws Exception {
216        admin.setBalancerRunning(true, true);
217      }
218
219      @Override
220      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
221        Mockito.verify(masterAdmin, Mockito.atLeast(count)).setBalancerRunning(
222          (RpcController) Mockito.any(), (SetBalancerRunningRequest) Mockito.any());
223      }
224    });
225
226    // Admin.balancer()
227    testMasterOperationIsRetried(new MethodCaller() {
228      @Override
229      public void call(Admin admin) throws Exception {
230        admin.balancer();
231      }
232
233      @Override
234      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
235        Mockito.verify(masterAdmin, Mockito.atLeast(count)).balance((RpcController) Mockito.any(),
236          (BalanceRequest) Mockito.any());
237      }
238    });
239
240    // Admin.enabledCatalogJanitor()
241    testMasterOperationIsRetried(new MethodCaller() {
242      @Override
243      public void call(Admin admin) throws Exception {
244        admin.enableCatalogJanitor(true);
245      }
246
247      @Override
248      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
249        Mockito.verify(masterAdmin, Mockito.atLeast(count)).enableCatalogJanitor(
250          (RpcController) Mockito.any(), (EnableCatalogJanitorRequest) Mockito.any());
251      }
252    });
253
254    // Admin.runCatalogScan()
255    testMasterOperationIsRetried(new MethodCaller() {
256      @Override
257      public void call(Admin admin) throws Exception {
258        admin.runCatalogScan();
259      }
260
261      @Override
262      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
263        Mockito.verify(masterAdmin, Mockito.atLeast(count))
264          .runCatalogScan((RpcController) Mockito.any(), (RunCatalogScanRequest) Mockito.any());
265      }
266    });
267
268    // Admin.isCatalogJanitorEnabled()
269    testMasterOperationIsRetried(new MethodCaller() {
270      @Override
271      public void call(Admin admin) throws Exception {
272        admin.isCatalogJanitorEnabled();
273      }
274
275      @Override
276      public void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception {
277        Mockito.verify(masterAdmin, Mockito.atLeast(count)).isCatalogJanitorEnabled(
278          (RpcController) Mockito.any(), (IsCatalogJanitorEnabledRequest) Mockito.any());
279      }
280    });
281  }
282
283  private static interface MethodCaller {
284    void call(Admin admin) throws Exception;
285
286    void verify(MasterKeepAliveConnection masterAdmin, int count) throws Exception;
287  }
288
289  private void testMasterOperationIsRetried(MethodCaller caller) throws Exception {
290    Configuration configuration = HBaseConfiguration.create();
291    // Set the pause and retry count way down.
292    configuration.setLong(HConstants.HBASE_CLIENT_PAUSE, 1);
293    final int count = 10;
294    configuration.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, count);
295
296    ClusterConnection connection = mock(ClusterConnection.class);
297    when(connection.getConfiguration()).thenReturn(configuration);
298    MasterKeepAliveConnection masterAdmin =
299      Mockito.mock(MasterKeepAliveConnection.class, new Answer() {
300        @Override
301        public Object answer(InvocationOnMock invocation) throws Throwable {
302          if (invocation.getMethod().getName().equals("close")) {
303            return null;
304          }
305          throw new MasterNotRunningException(); // all methods will throw an exception
306        }
307      });
308    Mockito.when(connection.getMaster()).thenReturn(masterAdmin);
309    RpcControllerFactory rpcControllerFactory = Mockito.mock(RpcControllerFactory.class);
310    Mockito.when(connection.getRpcControllerFactory()).thenReturn(rpcControllerFactory);
311    Mockito.when(rpcControllerFactory.newController())
312      .thenReturn(Mockito.mock(HBaseRpcController.class));
313
314    // we need a real retrying caller
315    RpcRetryingCallerFactory callerFactory = new RpcRetryingCallerFactory(configuration);
316    Mockito.when(connection.getRpcRetryingCallerFactory()).thenReturn(callerFactory);
317
318    Admin admin = null;
319    try {
320      admin = Mockito.spy(new HBaseAdmin(connection));
321      // mock the call to getRegion since in the absence of a cluster (which means the meta
322      // is not assigned), getRegion can't function
323      Mockito.doReturn(null).when(((HBaseAdmin) admin)).getRegion(Matchers.<byte[]> any());
324      try {
325        caller.call(admin); // invoke the HBaseAdmin method
326        fail();
327      } catch (RetriesExhaustedException e) {
328        LOG.info("Expected fail", e);
329      }
330      // Assert we were called 'count' times.
331      caller.verify(masterAdmin, count);
332    } finally {
333      if (admin != null) {
334        admin.close();
335      }
336    }
337  }
338}