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.http; 019 020import static org.hamcrest.MatcherAssert.assertThat; 021import static org.hamcrest.Matchers.allOf; 022import static org.hamcrest.Matchers.containsString; 023import static org.hamcrest.Matchers.endsWith; 024import static org.hamcrest.Matchers.startsWith; 025import static org.junit.jupiter.api.Assertions.assertThrows; 026 027import java.util.concurrent.CompletableFuture; 028import java.util.function.Supplier; 029import org.apache.hadoop.conf.Configuration; 030import org.apache.hadoop.hbase.ConnectionExtension; 031import org.apache.hadoop.hbase.HBaseConfiguration; 032import org.apache.hadoop.hbase.HConstants; 033import org.apache.hadoop.hbase.MiniClusterExtension; 034import org.apache.hadoop.hbase.ServerName; 035import org.apache.hadoop.hbase.StartTestingClusterOption; 036import org.apache.hadoop.hbase.TableName; 037import org.apache.hadoop.hbase.client.AsyncAdmin; 038import org.apache.hadoop.hbase.client.AsyncConnection; 039import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 040import org.apache.hadoop.hbase.client.Durability; 041import org.apache.hadoop.hbase.client.TableDescriptor; 042import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 043import org.apache.hadoop.hbase.master.http.api_v1.cluster_metrics.resource.ClusterMetricsResource; 044import org.apache.hadoop.hbase.testclassification.LargeTests; 045import org.apache.hadoop.hbase.testclassification.MasterTests; 046import org.apache.hadoop.hbase.util.Bytes; 047import org.junit.jupiter.api.Order; 048import org.junit.jupiter.api.Tag; 049import org.junit.jupiter.api.Test; 050import org.junit.jupiter.api.extension.AfterAllCallback; 051import org.junit.jupiter.api.extension.BeforeAllCallback; 052import org.junit.jupiter.api.extension.ExtensionContext; 053import org.junit.jupiter.api.extension.RegisterExtension; 054 055import org.apache.hbase.thirdparty.javax.ws.rs.NotAcceptableException; 056import org.apache.hbase.thirdparty.javax.ws.rs.client.Client; 057import org.apache.hbase.thirdparty.javax.ws.rs.client.ClientBuilder; 058import org.apache.hbase.thirdparty.javax.ws.rs.client.WebTarget; 059import org.apache.hbase.thirdparty.javax.ws.rs.core.MediaType; 060 061/** 062 * Tests for the master api_v1 {@link ClusterMetricsResource}. 063 */ 064@Tag(MasterTests.TAG) 065@Tag(LargeTests.TAG) 066public class TestApiV1ClusterMetricsResource { 067 068 @Order(1) 069 @RegisterExtension 070 private static final MiniClusterExtension miniClusterExtension = MiniClusterExtension.newBuilder() 071 .setMiniClusterOption( 072 StartTestingClusterOption.builder().numZkServers(3).numMasters(3).numDataNodes(3).build()) 073 .setConfiguration(() -> { 074 // enable Master InfoServer and random port selection 075 final Configuration conf = HBaseConfiguration.create(); 076 conf.setInt(HConstants.MASTER_INFO_PORT, 0); 077 conf.set("hbase.http.jersey.tracing.type", "ON_DEMAND"); 078 return conf; 079 }).build(); 080 081 @Order(2) 082 @RegisterExtension 083 private static final ConnectionExtension connectionExtension = 084 ConnectionExtension.createAsyncConnectionExtension(miniClusterExtension::createAsyncConnection); 085 086 @Order(3) 087 @RegisterExtension 088 private static final ClassSetupExtension classRule = 089 new ClassSetupExtension(connectionExtension::getAsyncConnection); 090 091 private static final class ClassSetupExtension implements BeforeAllCallback, AfterAllCallback { 092 093 private final TableName tableName; 094 private AsyncAdmin admin; 095 private WebTarget target; 096 private final Supplier<AsyncConnection> connectionSupplier; 097 098 public ClassSetupExtension(final Supplier<AsyncConnection> connectionSupplier) { 099 this.connectionSupplier = connectionSupplier; 100 tableName = TableName.valueOf(TestApiV1ClusterMetricsResource.class.getSimpleName()); 101 } 102 103 public WebTarget getTarget() { 104 return target; 105 } 106 107 @Override 108 public void beforeAll(ExtensionContext context) throws Exception { 109 final AsyncConnection conn = connectionSupplier.get(); 110 admin = conn.getAdmin(); 111 final ColumnFamilyDescriptorBuilder cfBuilder = 112 ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("c")); 113 final TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(tableName) 114 .setColumnFamily(cfBuilder.build()).setDurability(Durability.SKIP_WAL).build(); 115 admin.createTable(tableDescriptor).get(); 116 117 final String baseUrl = 118 admin.getMaster().thenApply(ServerName::getHostname).thenCombine(admin.getMasterInfoPort(), 119 (hostName, infoPort) -> "http://" + hostName + ":" + infoPort).get(); 120 final Client client = ClientBuilder.newClient(); 121 target = client.target(baseUrl).path("api/v1/admin/cluster_metrics"); 122 } 123 124 @Override 125 public void afterAll(ExtensionContext context) { 126 final TableName tableName = TableName.valueOf("test"); 127 try { 128 admin.tableExists(tableName).thenCompose(val -> { 129 if (val) { 130 return admin.disableTable(tableName) 131 .thenCompose(ignored -> admin.deleteTable(tableName)); 132 } else { 133 return CompletableFuture.completedFuture(null); 134 } 135 }).get(); 136 } catch (Exception e) { 137 throw new RuntimeException(e); 138 } 139 } 140 } 141 142 @Test 143 public void testGetRoot() { 144 final String response = classRule.getTarget().request(MediaType.APPLICATION_JSON_TYPE) 145 .header("X-Jersey-Tracing-Accept", true).get(String.class); 146 assertThat(response, 147 allOf(containsString("\"hbase_version\":"), containsString("\"cluster_id\":"), 148 containsString("\"master_name\":"), containsString("\"backup_master_names\":"))); 149 } 150 151 @Test 152 public void testGetRootHtml() { 153 assertThrows(NotAcceptableException.class, () -> classRule.getTarget() 154 .request(MediaType.TEXT_HTML_TYPE).header("X-Jersey-Tracing-Accept", true).get(String.class)); 155 } 156 157 @Test 158 public void testGetLiveServers() { 159 final String response = 160 classRule.getTarget().path("live_servers").request(MediaType.APPLICATION_JSON_TYPE) 161 .header("X-Jersey-Tracing-Accept", true).get(String.class); 162 assertThat(response, allOf(startsWith("{\"data\":["), endsWith("]}"))); 163 } 164 165 @Test 166 public void testGetLiveServersHtml() { 167 assertThrows(NotAcceptableException.class, () -> classRule.getTarget().path("live_servers") 168 .request(MediaType.TEXT_HTML_TYPE).header("X-Jersey-Tracing-Accept", true).get(String.class)); 169 } 170 171 @Test 172 public void testGetDeadServers() { 173 final String response = 174 classRule.getTarget().path("dead_servers").request(MediaType.APPLICATION_JSON_TYPE) 175 .header("X-Jersey-Tracing-Accept", true).get(String.class); 176 assertThat(response, allOf(startsWith("{\"data\":["), endsWith("]}"))); 177 } 178 179 @Test 180 public void testGetDeadServersHtml() { 181 assertThrows(NotAcceptableException.class, () -> classRule.getTarget().path("dead_servers") 182 .request(MediaType.TEXT_HTML_TYPE).header("X-Jersey-Tracing-Accept", true).get(String.class)); 183 } 184}