/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hbase.master.webapp; import static org.apache.hadoop.hbase.client.hamcrest.BytesMatchers.bytesAsStringBinary; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasProperty; import static org.hamcrest.Matchers.startsWith; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.List; import java.util.concurrent.CompletableFuture; import javax.servlet.http.HttpServletRequest; import org.apache.hadoop.hbase.ClearUserNamespacesAndTablesRule; import org.apache.hadoop.hbase.ConnectionRule; import org.apache.hadoop.hbase.HBaseClassTestRule; import org.apache.hadoop.hbase.MiniClusterRule; import org.apache.hadoop.hbase.NamespaceDescriptor; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.client.AsyncAdmin; import org.apache.hadoop.hbase.client.AsyncConnection; import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; import org.apache.hadoop.hbase.client.TableDescriptor; import org.apache.hadoop.hbase.client.TableDescriptorBuilder; import org.apache.hadoop.hbase.master.RegionState; import org.apache.hadoop.hbase.testclassification.MasterTests; import org.apache.hadoop.hbase.testclassification.MediumTests; import org.apache.hadoop.hbase.util.RegionSplitter; import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.rules.RuleChain; import org.junit.rules.TestName; import org.junit.rules.TestRule; import org.apache.hbase.thirdparty.org.apache.commons.collections4.IterableUtils; /** * Cluster-backed correctness tests for the functionality provided by {@link MetaBrowser}. */ @Category({ MasterTests.class, MediumTests.class}) public class TestMetaBrowser { @ClassRule public static final HBaseClassTestRule testRule = HBaseClassTestRule.forClass(TestMetaBrowser.class); @ClassRule public static final MiniClusterRule miniClusterRule = MiniClusterRule.newBuilder().build(); private final ConnectionRule connectionRule = new ConnectionRule(miniClusterRule::createConnection); private final ClearUserNamespacesAndTablesRule clearUserNamespacesAndTablesRule = new ClearUserNamespacesAndTablesRule(connectionRule::getConnection); @Rule public TestRule rule = RuleChain.outerRule(connectionRule) .around(clearUserNamespacesAndTablesRule); @Rule public TestName testNameRule = new TestName(); private AsyncConnection connection; private AsyncAdmin admin; @Before public void before() { connection = connectionRule.getConnection(); admin = connection.getAdmin(); } @Test public void noFilters() { final String namespaceName = testNameRule.getMethodName(); final TableName a = TableName.valueOf("a"); final TableName b = TableName.valueOf(namespaceName, "b"); CompletableFuture.allOf( createTable(a), createNamespace(namespaceName).thenCompose(_void -> createTable(b, 2))) .join(); final HttpServletRequest request = new MockRequestBuilder().build(); final List<RegionReplicaInfo> rows; try (final MetaBrowser.Results results = new MetaBrowser(connection, request).getResults()) { rows = IterableUtils.toList(results); } assertThat(rows, contains( hasProperty("row", bytesAsStringBinary(startsWith(a + ",,"))), hasProperty("row", bytesAsStringBinary(startsWith(b + ",,"))), hasProperty("row", bytesAsStringBinary(startsWith(b + ",80000000"))))); } @Test public void limit() { final String tableName = testNameRule.getMethodName(); createTable(TableName.valueOf(tableName), 8).join(); final HttpServletRequest request = new MockRequestBuilder() .setLimit(5) .build(); final List<RegionReplicaInfo> rows; try (final MetaBrowser.Results results = new MetaBrowser(connection, request).getResults()) { rows = IterableUtils.toList(results); } assertThat(rows, contains( hasProperty("row", bytesAsStringBinary(startsWith(tableName + ",,"))), hasProperty("row", bytesAsStringBinary(startsWith(tableName + ",20000000"))), hasProperty("row", bytesAsStringBinary(startsWith(tableName + ",40000000"))), hasProperty("row", bytesAsStringBinary(startsWith(tableName + ",60000000"))), hasProperty("row", bytesAsStringBinary(startsWith(tableName + ",80000000"))))); } @Test public void regionStateFilter() { final String namespaceName = testNameRule.getMethodName(); final TableName foo = TableName.valueOf(namespaceName, "foo"); final TableName bar = TableName.valueOf(namespaceName, "bar"); createNamespace(namespaceName) .thenCompose(_void1 -> CompletableFuture.allOf( createTable(foo, 2).thenCompose(_void2 -> admin.disableTable(foo)), createTable(bar, 2))) .join(); final HttpServletRequest request = new MockRequestBuilder() .setLimit(10_000) .setRegionState(RegionState.State.OPEN) .setTable(namespaceName) .build(); final List<RegionReplicaInfo> rows; try (final MetaBrowser.Results results = new MetaBrowser(connection, request).getResults()) { rows = IterableUtils.toList(results); } assertThat(rows, contains( hasProperty("row", bytesAsStringBinary(startsWith(bar.toString() + ",,"))), hasProperty("row", bytesAsStringBinary(startsWith(bar.toString() + ",80000000"))))); } @Test public void scanTableFilter() { final String namespaceName = testNameRule.getMethodName(); final TableName a = TableName.valueOf("a"); final TableName b = TableName.valueOf(namespaceName, "b"); CompletableFuture.allOf( createTable(a), createNamespace(namespaceName).thenCompose(_void -> createTable(b, 2))) .join(); final HttpServletRequest request = new MockRequestBuilder() .setTable(namespaceName) .build(); final List<RegionReplicaInfo> rows; try (final MetaBrowser.Results results = new MetaBrowser(connection, request).getResults()) { rows = IterableUtils.toList(results); } assertThat(rows, contains( hasProperty("row", bytesAsStringBinary(startsWith(b + ",,"))), hasProperty("row", bytesAsStringBinary(startsWith(b + ",80000000"))))); } @Test public void paginateWithReplicas() { final String namespaceName = testNameRule.getMethodName(); final TableName a = TableName.valueOf("a"); final TableName b = TableName.valueOf(namespaceName, "b"); CompletableFuture.allOf( createTableWithReplicas(a, 2), createNamespace(namespaceName).thenCompose(_void -> createTable(b, 2))) .join(); final HttpServletRequest request1 = new MockRequestBuilder() .setLimit(2) .build(); final List<RegionReplicaInfo> rows1; try (final MetaBrowser.Results results = new MetaBrowser(connection, request1).getResults()) { rows1 = IterableUtils.toList(results); } assertThat(rows1, contains( allOf( hasProperty("regionName", bytesAsStringBinary(startsWith(a + ",,"))), hasProperty("replicaId", equalTo(0))), allOf( hasProperty("regionName", bytesAsStringBinary(startsWith(a + ",,"))), hasProperty("replicaId", equalTo(1))))); final HttpServletRequest request2 = new MockRequestBuilder() .setLimit(2) .setStart(MetaBrowser.buildStartParamFrom(rows1.get(rows1.size() - 1).getRow())) .build(); final List<RegionReplicaInfo> rows2; try (final MetaBrowser.Results results = new MetaBrowser(connection, request2).getResults()) { rows2 = IterableUtils.toList(results); } assertThat(rows2, contains( hasProperty("row", bytesAsStringBinary(startsWith(b + ",,"))), hasProperty("row", bytesAsStringBinary(startsWith(b + ",80000000"))))); } @Test public void paginateWithTableFilter() { final String namespaceName = testNameRule.getMethodName(); final TableName a = TableName.valueOf("a"); final TableName b = TableName.valueOf(namespaceName, "b"); CompletableFuture.allOf( createTable(a), createNamespace(namespaceName).thenCompose(_void -> createTable(b, 5))) .join(); final HttpServletRequest request1 = new MockRequestBuilder() .setLimit(2) .setTable(namespaceName) .build(); final List<RegionReplicaInfo> rows1; try (final MetaBrowser.Results results = new MetaBrowser(connection, request1).getResults()) { rows1 = IterableUtils.toList(results); } assertThat(rows1, contains( hasProperty("row", bytesAsStringBinary(startsWith(b + ",,"))), hasProperty("row", bytesAsStringBinary(startsWith(b + ",33333333"))))); final HttpServletRequest request2 = new MockRequestBuilder() .setLimit(2) .setTable(namespaceName) .setStart(MetaBrowser.buildStartParamFrom(rows1.get(rows1.size() - 1).getRow())) .build(); final List<RegionReplicaInfo> rows2; try (final MetaBrowser.Results results = new MetaBrowser(connection, request2).getResults()) { rows2 = IterableUtils.toList(results); } assertThat(rows2, contains( hasProperty("row", bytesAsStringBinary(startsWith(b + ",66666666"))), hasProperty("row", bytesAsStringBinary(startsWith(b + ",99999999"))))); final HttpServletRequest request3 = new MockRequestBuilder() .setLimit(2) .setTable(namespaceName) .setStart(MetaBrowser.buildStartParamFrom(rows2.get(rows2.size() - 1).getRow())) .build(); final List<RegionReplicaInfo> rows3; try (final MetaBrowser.Results results = new MetaBrowser(connection, request3).getResults()) { rows3 = IterableUtils.toList(results); } assertThat(rows3, contains( hasProperty("row", bytesAsStringBinary(startsWith(b + ",cccccccc"))))); } private ColumnFamilyDescriptor columnFamilyDescriptor() { return ColumnFamilyDescriptorBuilder.of("f1"); } private TableDescriptor tableDescriptor(final TableName tableName) { return TableDescriptorBuilder.newBuilder(tableName) .setColumnFamily(columnFamilyDescriptor()) .build(); } private TableDescriptor tableDescriptor(final TableName tableName, final int replicaCount) { return TableDescriptorBuilder.newBuilder(tableName) .setRegionReplication(replicaCount) .setColumnFamily(columnFamilyDescriptor()) .build(); } private CompletableFuture<Void> createTable(final TableName tableName) { return admin.createTable(tableDescriptor(tableName)); } private CompletableFuture<Void> createTable(final TableName tableName, final int splitCount) { return admin.createTable( tableDescriptor(tableName), new RegionSplitter.HexStringSplit().split(splitCount)); } private CompletableFuture<Void> createTableWithReplicas(final TableName tableName, final int replicaCount) { return admin.createTable(tableDescriptor(tableName, replicaCount)); } private CompletableFuture<Void> createNamespace(final String namespace) { final NamespaceDescriptor descriptor = NamespaceDescriptor.create(namespace).build(); return admin.createNamespace(descriptor); } /** * Helper for mocking an {@link HttpServletRequest} relevant to the test. */ static class MockRequestBuilder { private String limit = null; private String regionState = null; private String start = null; private String table = null; public MockRequestBuilder setLimit(final int value) { this.limit = Integer.toString(value); return this; } public MockRequestBuilder setLimit(final String value) { this.limit = value; return this; } public MockRequestBuilder setRegionState(final RegionState.State value) { this.regionState = value.toString(); return this; } public MockRequestBuilder setRegionState(final String value) { this.regionState = value; return this; } public MockRequestBuilder setStart(final String value) { this.start = value; return this; } public MockRequestBuilder setTable(final String value) { this.table = value; return this; } public HttpServletRequest build() { final HttpServletRequest request = mock(HttpServletRequest.class); when(request.getRequestURI()).thenReturn("/table.jsp"); when(request.getParameter("name")).thenReturn("hbase%3Ameta"); when(request.getParameter("scan_limit")).thenReturn(limit); when(request.getParameter("scan_region_state")).thenReturn(regionState); when(request.getParameter("scan_start")).thenReturn(start); when(request.getParameter("scan_table")).thenReturn(table); return request; } } }