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}