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; 019 020import static org.junit.jupiter.api.Assertions.assertNotNull; 021import static org.junit.jupiter.api.Assertions.assertTrue; 022import static org.junit.jupiter.api.Assertions.fail; 023 024import java.io.IOException; 025import javax.management.remote.JMXConnector; 026import javax.management.remote.JMXConnectorFactory; 027import javax.naming.ServiceUnavailableException; 028import org.apache.hadoop.conf.Configuration; 029import org.apache.hadoop.hbase.client.Admin; 030import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; 031import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; 032import org.apache.hadoop.hbase.coprocessor.ObserverContext; 033import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment; 034import org.apache.hadoop.hbase.security.AccessDeniedException; 035import org.apache.hadoop.hbase.security.access.AccessController; 036import org.apache.hadoop.hbase.testclassification.MediumTests; 037import org.apache.hadoop.hbase.testclassification.MiscTests; 038import org.junit.jupiter.api.AfterAll; 039import org.junit.jupiter.api.AfterEach; 040import org.junit.jupiter.api.BeforeAll; 041import org.junit.jupiter.api.BeforeEach; 042import org.junit.jupiter.api.Tag; 043import org.junit.jupiter.api.Test; 044import org.slf4j.Logger; 045import org.slf4j.LoggerFactory; 046 047/** 048 * Test case for JMX Connector Server. 049 */ 050@Tag(MiscTests.TAG) 051@Tag(MediumTests.TAG) 052public class TestJMXConnectorServer { 053 054 private static final Logger LOG = LoggerFactory.getLogger(TestJMXConnectorServer.class); 055 private static HBaseTestingUtil UTIL = new HBaseTestingUtil(); 056 057 private static Configuration conf = null; 058 private static Admin admin; 059 // RMI registry port 060 private static int rmiRegistryPort; 061 // Switch for customized Accesscontroller to throw ACD exception while executing test case 062 private volatile static boolean hasAccess; 063 064 @BeforeAll 065 public static void setUpBeforeClass() throws Exception { 066 conf = UTIL.getConfiguration(); 067 String cps = JMXListener.class.getName() + "," + MyAccessController.class.getName(); 068 conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, cps); 069 conf.set(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY, cps); 070 rmiRegistryPort = UTIL.randomFreePort(); 071 conf.setInt("master.rmi.registry.port", rmiRegistryPort); 072 conf.setInt("regionserver.rmi.registry.port", rmiRegistryPort); 073 UTIL.startMiniCluster(); 074 admin = UTIL.getConnection().getAdmin(); 075 } 076 077 @AfterAll 078 public static void tearDownAfterClass() throws Exception { 079 admin.close(); 080 UTIL.shutdownMiniCluster(); 081 } 082 083 @BeforeEach 084 public void setUp() { 085 hasAccess = false; 086 } 087 088 @AfterEach 089 public void tearDown() { 090 hasAccess = true; 091 } 092 093 /** 094 * This tests to validate the HMaster's ConnectorServer after unauthorised stopMaster call. 095 */ 096 @Test 097 public void testHMConnectorServerWhenStopMaster() throws Exception { 098 // try to stop master 099 boolean accessDenied = false; 100 try { 101 LOG.info("Stopping HMaster..."); 102 admin.stopMaster(); 103 } catch (AccessDeniedException e) { 104 LOG.info("Exception occurred while stopping HMaster. ", e); 105 accessDenied = true; 106 } 107 assertTrue(accessDenied); 108 109 checkConnector(); 110 } 111 112 /** 113 * This tests to validate the RegionServer's ConnectorServer after unauthorised stopRegionServer 114 * call. 115 */ 116 @Test 117 public void testRSConnectorServerWhenStopRegionServer() throws Exception { 118 ServerName serverName = UTIL.getHBaseCluster().getRegionServer(0).getServerName(); 119 LOG.info("Stopping Region Server..."); 120 admin.stopRegionServer(serverName.getHostname() + ":" + serverName.getPort()); 121 122 checkConnector(); 123 } 124 125 /** 126 * This tests to validate the HMaster's ConnectorServer after unauthorised shutdown call. 127 */ 128 @Test 129 public void testHMConnectorServerWhenShutdownCluster() throws Exception { 130 boolean accessDenied = false; 131 try { 132 LOG.info("Stopping HMaster..."); 133 admin.shutdown(); 134 } catch (AccessDeniedException e) { 135 LOG.error("Exception occurred while stopping HMaster. ", e); 136 accessDenied = true; 137 } 138 assertTrue(accessDenied); 139 140 checkConnector(); 141 } 142 143 private void checkConnector() throws Exception { 144 // Check whether HMaster JMX Connector server can be connected 145 JMXConnector connector = null; 146 try { 147 connector = JMXConnectorFactory 148 .connect(JMXListener.buildJMXServiceURL(rmiRegistryPort, rmiRegistryPort)); 149 } catch (IOException e) { 150 if (e.getCause() instanceof ServiceUnavailableException) { 151 fail("Can't connect to ConnectorServer."); 152 } 153 } 154 assertNotNull(connector, "JMXConnector should not be null."); 155 connector.close(); 156 } 157 158 /* 159 * Customized class for test case execution which will throw ACD exception while executing 160 * stopMaster/preStopRegionServer/preShutdown explicitly. 161 */ 162 public static class MyAccessController extends AccessController { 163 @Override 164 public void postStartMaster(ObserverContext<MasterCoprocessorEnvironment> ctx) { 165 // Do nothing. In particular, stop the creation of the hbase:acl table. It makes the 166 // shutdown take time. 167 } 168 169 @Override 170 public void preStopMaster(ObserverContext<MasterCoprocessorEnvironment> c) throws IOException { 171 if (!hasAccess) { 172 throw new AccessDeniedException("Insufficient permissions to stop master"); 173 } 174 } 175 176 @Override 177 public void preStopRegionServer(ObserverContext<RegionServerCoprocessorEnvironment> ctx) 178 throws IOException { 179 if (!hasAccess) { 180 throw new AccessDeniedException("Insufficient permissions to stop region server."); 181 } 182 } 183 184 @Override 185 public void preShutdown(ObserverContext<MasterCoprocessorEnvironment> c) throws IOException { 186 if (!hasAccess) { 187 throw new AccessDeniedException("Insufficient permissions to shut down cluster."); 188 } 189 } 190 191 @Override 192 public void preExecuteProcedures(ObserverContext<RegionServerCoprocessorEnvironment> ctx) 193 throws IOException { 194 // FIXME: ignore the procedure permission check since in our UT framework master is neither 195 // the systemuser nor the superuser so we can not call executeProcedures... 196 } 197 } 198}