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.Assert.assertThrows; 026 027import java.util.concurrent.CompletableFuture; 028import java.util.function.Supplier; 029import org.apache.hadoop.conf.Configuration; 030import org.apache.hadoop.hbase.ConnectionRule; 031import org.apache.hadoop.hbase.HBaseClassTestRule; 032import org.apache.hadoop.hbase.HBaseConfiguration; 033import org.apache.hadoop.hbase.HConstants; 034import org.apache.hadoop.hbase.MiniClusterRule; 035import org.apache.hadoop.hbase.ServerName; 036import org.apache.hadoop.hbase.StartMiniClusterOption; 037import org.apache.hadoop.hbase.TableName; 038import org.apache.hadoop.hbase.client.AsyncAdmin; 039import org.apache.hadoop.hbase.client.AsyncConnection; 040import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 041import org.apache.hadoop.hbase.client.Durability; 042import org.apache.hadoop.hbase.client.TableDescriptor; 043import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 044import org.apache.hadoop.hbase.master.http.api_v1.cluster_metrics.resource.ClusterMetricsResource; 045import org.apache.hadoop.hbase.testclassification.LargeTests; 046import org.apache.hadoop.hbase.testclassification.MasterTests; 047import org.apache.hadoop.hbase.util.Bytes; 048import org.junit.ClassRule; 049import org.junit.Test; 050import org.junit.experimental.categories.Category; 051import org.junit.rules.ExternalResource; 052import org.junit.rules.RuleChain; 053 054import org.apache.hbase.thirdparty.javax.ws.rs.NotAcceptableException; 055import org.apache.hbase.thirdparty.javax.ws.rs.client.Client; 056import org.apache.hbase.thirdparty.javax.ws.rs.client.ClientBuilder; 057import org.apache.hbase.thirdparty.javax.ws.rs.client.WebTarget; 058import org.apache.hbase.thirdparty.javax.ws.rs.core.MediaType; 059 060/** 061 * Tests for the master api_v1 {@link ClusterMetricsResource}. 062 */ 063@Category({ MasterTests.class, LargeTests.class }) 064public class TestApiV1ClusterMetricsResource { 065 066 @ClassRule 067 public static final HBaseClassTestRule CLASS_RULE = 068 HBaseClassTestRule.forClass(TestApiV1ClusterMetricsResource.class); 069 070 private static final MiniClusterRule miniClusterRule = MiniClusterRule.newBuilder() 071 .setMiniClusterOption( 072 StartMiniClusterOption.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 private static final ConnectionRule connectionRule = 081 ConnectionRule.createAsyncConnectionRule(miniClusterRule::createAsyncConnection); 082 private static final ClassSetup classRule = new ClassSetup(connectionRule::getAsyncConnection); 083 084 private static final class ClassSetup extends ExternalResource { 085 086 private final Supplier<AsyncConnection> connectionSupplier; 087 private final TableName tableName; 088 private AsyncAdmin admin; 089 private WebTarget target; 090 091 public ClassSetup(final Supplier<AsyncConnection> connectionSupplier) { 092 this.connectionSupplier = connectionSupplier; 093 tableName = TableName.valueOf(TestApiV1ClusterMetricsResource.class.getSimpleName()); 094 } 095 096 public WebTarget getTarget() { 097 return target; 098 } 099 100 @Override 101 protected void before() throws Throwable { 102 final AsyncConnection conn = connectionSupplier.get(); 103 admin = conn.getAdmin(); 104 final TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(tableName) 105 .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("c")).build()) 106 .setDurability(Durability.SKIP_WAL).build(); 107 admin.createTable(tableDescriptor).get(); 108 109 final String baseUrl = 110 admin.getMaster().thenApply(ServerName::getHostname).thenCombine(admin.getMasterInfoPort(), 111 (hostName, infoPort) -> "http://" + hostName + ":" + infoPort).get(); 112 final Client client = ClientBuilder.newClient(); 113 target = client.target(baseUrl).path("api/v1/admin/cluster_metrics"); 114 } 115 116 @Override 117 protected void after() { 118 final TableName tableName = TableName.valueOf("test"); 119 try { 120 admin.tableExists(tableName).thenCompose(val -> { 121 if (val) { 122 return admin.disableTable(tableName) 123 .thenCompose(ignored -> admin.deleteTable(tableName)); 124 } else { 125 return CompletableFuture.completedFuture(null); 126 } 127 }).get(); 128 } catch (Exception e) { 129 throw new RuntimeException(e); 130 } 131 } 132 } 133 134 @ClassRule 135 public static RuleChain ruleChain = 136 RuleChain.outerRule(miniClusterRule).around(connectionRule).around(classRule); 137 138 @Test 139 public void testGetRoot() { 140 final String response = classRule.getTarget().request(MediaType.APPLICATION_JSON_TYPE) 141 .header("X-Jersey-Tracing-Accept", true).get(String.class); 142 assertThat(response, 143 allOf(containsString("\"hbase_version\":"), containsString("\"cluster_id\":"), 144 containsString("\"master_name\":"), containsString("\"backup_master_names\":"))); 145 } 146 147 @Test 148 public void testGetRootHtml() { 149 assertThrows(NotAcceptableException.class, () -> classRule.getTarget() 150 .request(MediaType.TEXT_HTML_TYPE).header("X-Jersey-Tracing-Accept", true).get(String.class)); 151 } 152 153 @Test 154 public void testGetLiveServers() { 155 final String response = 156 classRule.getTarget().path("live_servers").request(MediaType.APPLICATION_JSON_TYPE) 157 .header("X-Jersey-Tracing-Accept", true).get(String.class); 158 assertThat(response, allOf(startsWith("{\"data\":["), endsWith("]}"))); 159 } 160 161 @Test 162 public void testGetLiveServersHtml() { 163 assertThrows(NotAcceptableException.class, () -> classRule.getTarget().path("live_servers") 164 .request(MediaType.TEXT_HTML_TYPE).header("X-Jersey-Tracing-Accept", true).get(String.class)); 165 } 166 167 @Test 168 public void testGetDeadServers() { 169 final String response = 170 classRule.getTarget().path("dead_servers").request(MediaType.APPLICATION_JSON_TYPE) 171 .header("X-Jersey-Tracing-Accept", true).get(String.class); 172 assertThat(response, allOf(startsWith("{\"data\":["), endsWith("]}"))); 173 } 174 175 @Test 176 public void testGetDeadServersHtml() { 177 assertThrows(NotAcceptableException.class, () -> classRule.getTarget().path("dead_servers") 178 .request(MediaType.TEXT_HTML_TYPE).header("X-Jersey-Tracing-Accept", true).get(String.class)); 179 } 180}