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.rest; 019 020import static org.junit.Assert.*; 021 022import com.fasterxml.jackson.databind.ObjectMapper; 023import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; 024import java.io.ByteArrayInputStream; 025import java.io.IOException; 026import java.io.StringWriter; 027import java.util.ArrayList; 028import java.util.Arrays; 029import java.util.HashMap; 030import java.util.List; 031import java.util.Map; 032import javax.ws.rs.core.MediaType; 033import javax.xml.bind.JAXBContext; 034import javax.xml.bind.JAXBException; 035import org.apache.hadoop.conf.Configuration; 036import org.apache.hadoop.hbase.HBaseClassTestRule; 037import org.apache.hadoop.hbase.HBaseTestingUtility; 038import org.apache.hadoop.hbase.HColumnDescriptor; 039import org.apache.hadoop.hbase.HTableDescriptor; 040import org.apache.hadoop.hbase.NamespaceDescriptor; 041import org.apache.hadoop.hbase.TableName; 042import org.apache.hadoop.hbase.client.Admin; 043import org.apache.hadoop.hbase.rest.client.Client; 044import org.apache.hadoop.hbase.rest.client.Cluster; 045import org.apache.hadoop.hbase.rest.client.Response; 046import org.apache.hadoop.hbase.rest.model.NamespacesInstanceModel; 047import org.apache.hadoop.hbase.rest.model.TableListModel; 048import org.apache.hadoop.hbase.rest.model.TableModel; 049import org.apache.hadoop.hbase.rest.model.TestNamespacesInstanceModel; 050import org.apache.hadoop.hbase.testclassification.MediumTests; 051import org.apache.hadoop.hbase.testclassification.RestTests; 052import org.apache.hadoop.hbase.util.Bytes; 053import org.junit.AfterClass; 054import org.junit.BeforeClass; 055import org.junit.ClassRule; 056import org.junit.Ignore; 057import org.junit.Test; 058import org.junit.experimental.categories.Category; 059 060@Category({RestTests.class, MediumTests.class}) 061public class TestNamespacesInstanceResource { 062 063 @ClassRule 064 public static final HBaseClassTestRule CLASS_RULE = 065 HBaseClassTestRule.forClass(TestNamespacesInstanceResource.class); 066 067 private static String NAMESPACE1 = "TestNamespacesInstanceResource1"; 068 private static Map<String,String> NAMESPACE1_PROPS = new HashMap<>(); 069 private static String NAMESPACE2 = "TestNamespacesInstanceResource2"; 070 private static Map<String,String> NAMESPACE2_PROPS = new HashMap<>(); 071 private static String NAMESPACE3 = "TestNamespacesInstanceResource3"; 072 private static Map<String,String> NAMESPACE3_PROPS = new HashMap<>(); 073 private static String NAMESPACE4 = "TestNamespacesInstanceResource4"; 074 private static Map<String,String> NAMESPACE4_PROPS = new HashMap<>(); 075 076 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 077 private static final HBaseRESTTestingUtility REST_TEST_UTIL = 078 new HBaseRESTTestingUtility(); 079 private static Client client; 080 private static JAXBContext context; 081 private static Configuration conf; 082 private static TestNamespacesInstanceModel testNamespacesInstanceModel; 083 protected static ObjectMapper jsonMapper; 084 085 @BeforeClass 086 public static void setUpBeforeClass() throws Exception { 087 conf = TEST_UTIL.getConfiguration(); 088 TEST_UTIL.startMiniCluster(); 089 REST_TEST_UTIL.startServletContainer(conf); 090 client = new Client(new Cluster().add("localhost", 091 REST_TEST_UTIL.getServletPort())); 092 testNamespacesInstanceModel = new TestNamespacesInstanceModel(); 093 context = JAXBContext.newInstance(NamespacesInstanceModel.class, TableListModel.class); 094 jsonMapper = new JacksonJaxbJsonProvider() 095 .locateMapper(NamespacesInstanceModel.class, MediaType.APPLICATION_JSON_TYPE); 096 NAMESPACE1_PROPS.put("key1", "value1"); 097 NAMESPACE2_PROPS.put("key2a", "value2a"); 098 NAMESPACE2_PROPS.put("key2b", "value2b"); 099 NAMESPACE3_PROPS.put("key3", "value3"); 100 NAMESPACE4_PROPS.put("key4a", "value4a"); 101 NAMESPACE4_PROPS.put("key4b", "value4b"); 102 } 103 104 @AfterClass 105 public static void tearDownAfterClass() throws Exception { 106 REST_TEST_UTIL.shutdownServletContainer(); 107 TEST_UTIL.shutdownMiniCluster(); 108 } 109 110 private static byte[] toXML(NamespacesInstanceModel model) throws JAXBException { 111 StringWriter writer = new StringWriter(); 112 context.createMarshaller().marshal(model, writer); 113 return Bytes.toBytes(writer.toString()); 114 } 115 116 @SuppressWarnings("unchecked") 117 private static <T> T fromXML(byte[] content) 118 throws JAXBException { 119 return (T) context.createUnmarshaller().unmarshal(new ByteArrayInputStream(content)); 120 } 121 122 private NamespaceDescriptor findNamespace(Admin admin, String namespaceName) throws IOException{ 123 NamespaceDescriptor[] nd = admin.listNamespaceDescriptors(); 124 for(int i = 0; i < nd.length; i++){ 125 if(nd[i].getName().equals(namespaceName)){ 126 return nd[i]; 127 } 128 } 129 return null; 130 } 131 132 private void checkNamespaceProperties(NamespaceDescriptor nd, Map<String,String> testProps){ 133 checkNamespaceProperties(nd.getConfiguration(), testProps); 134 } 135 136 private void checkNamespaceProperties(Map<String,String> namespaceProps, 137 Map<String,String> testProps){ 138 assertTrue(namespaceProps.size() == testProps.size()); 139 for(String key: testProps.keySet()){ 140 assertEquals(testProps.get(key), namespaceProps.get(key)); 141 } 142 } 143 144 private void checkNamespaceTables(List<TableModel> namespaceTables, List<String> testTables){ 145 assertEquals(namespaceTables.size(), testTables.size()); 146 for(int i = 0 ; i < namespaceTables.size() ; i++){ 147 String tableName = ((TableModel) namespaceTables.get(i)).getName(); 148 assertTrue(testTables.contains(tableName)); 149 } 150 } 151 152 @Test 153 public void testCannotDeleteDefaultAndHbaseNamespaces() throws IOException { 154 String defaultPath = "/namespaces/default"; 155 String hbasePath = "/namespaces/hbase"; 156 Response response; 157 158 // Check that doesn't exist via non-REST call. 159 Admin admin = TEST_UTIL.getAdmin(); 160 assertNotNull(findNamespace(admin, "default")); 161 assertNotNull(findNamespace(admin, "hbase")); 162 163 // Try (but fail) to delete namespaces via REST. 164 response = client.delete(defaultPath); 165 assertEquals(503, response.getCode()); 166 response = client.delete(hbasePath); 167 assertEquals(503, response.getCode()); 168 169 assertNotNull(findNamespace(admin, "default")); 170 assertNotNull(findNamespace(admin, "hbase")); 171 } 172 173 @Test 174 public void testGetNamespaceTablesAndCannotDeleteNamespace() throws IOException, JAXBException { 175 Admin admin = TEST_UTIL.getAdmin(); 176 String nsName = "TestNamespacesInstanceResource5"; 177 Response response; 178 179 // Create namespace via admin. 180 NamespaceDescriptor.Builder nsBuilder = NamespaceDescriptor.create(nsName); 181 NamespaceDescriptor nsd = nsBuilder.build(); 182 nsd.setConfiguration("key1", "value1"); 183 admin.createNamespace(nsd); 184 185 // Create two tables via admin. 186 HColumnDescriptor colDesc = new HColumnDescriptor("cf1"); 187 TableName tn1 = TableName.valueOf(nsName + ":table1"); 188 HTableDescriptor table = new HTableDescriptor(tn1); 189 table.addFamily(colDesc); 190 admin.createTable(table); 191 TableName tn2 = TableName.valueOf(nsName + ":table2"); 192 table = new HTableDescriptor(tn2); 193 table.addFamily(colDesc); 194 admin.createTable(table); 195 196 Map<String, String> nsProperties = new HashMap<>(); 197 nsProperties.put("key1", "value1"); 198 List<String> nsTables = Arrays.asList("table1", "table2"); 199 200 // Check get namespace properties as XML, JSON and Protobuf. 201 String namespacePath = "/namespaces/" + nsName; 202 response = client.get(namespacePath); 203 assertEquals(200, response.getCode()); 204 205 response = client.get(namespacePath, Constants.MIMETYPE_XML); 206 assertEquals(200, response.getCode()); 207 NamespacesInstanceModel model = fromXML(response.getBody()); 208 checkNamespaceProperties(model.getProperties(), nsProperties); 209 210 response = client.get(namespacePath, Constants.MIMETYPE_JSON); 211 assertEquals(200, response.getCode()); 212 model = jsonMapper.readValue(response.getBody(), NamespacesInstanceModel.class); 213 checkNamespaceProperties(model.getProperties(), nsProperties); 214 215 response = client.get(namespacePath, Constants.MIMETYPE_PROTOBUF); 216 assertEquals(200, response.getCode()); 217 model.getObjectFromMessage(response.getBody()); 218 checkNamespaceProperties(model.getProperties(), nsProperties); 219 220 // Check get namespace tables as XML, JSON and Protobuf. 221 namespacePath = "/namespaces/" + nsName + "/tables"; 222 response = client.get(namespacePath); 223 assertEquals(200, response.getCode()); 224 225 response = client.get(namespacePath, Constants.MIMETYPE_XML); 226 assertEquals(200, response.getCode()); 227 TableListModel tablemodel = fromXML(response.getBody()); 228 checkNamespaceTables(tablemodel.getTables(), nsTables); 229 230 response = client.get(namespacePath, Constants.MIMETYPE_JSON); 231 assertEquals(200, response.getCode()); 232 tablemodel = jsonMapper.readValue(response.getBody(), TableListModel.class); 233 checkNamespaceTables(tablemodel.getTables(), nsTables); 234 235 response = client.get(namespacePath, Constants.MIMETYPE_PROTOBUF); 236 assertEquals(200, response.getCode()); 237 tablemodel.setTables(new ArrayList<>()); 238 tablemodel.getObjectFromMessage(response.getBody()); 239 checkNamespaceTables(tablemodel.getTables(), nsTables); 240 241 // Check cannot delete namespace via REST because it contains tables. 242 response = client.delete(namespacePath); 243 namespacePath = "/namespaces/" + nsName; 244 assertEquals(503, response.getCode()); 245 } 246 247 @Ignore("HBASE-19210") 248 @Test 249 public void testInvalidNamespacePostsAndPuts() throws IOException, JAXBException { 250 String namespacePath1 = "/namespaces/" + NAMESPACE1; 251 String namespacePath2 = "/namespaces/" + NAMESPACE2; 252 String namespacePath3 = "/namespaces/" + NAMESPACE3; 253 NamespacesInstanceModel model1; 254 NamespacesInstanceModel model2; 255 NamespacesInstanceModel model3; 256 Response response; 257 258 // Check that namespaces don't exist via non-REST call. 259 Admin admin = TEST_UTIL.getAdmin(); 260 assertNull(findNamespace(admin, NAMESPACE1)); 261 assertNull(findNamespace(admin, NAMESPACE2)); 262 assertNull(findNamespace(admin, NAMESPACE3)); 263 264 model1 = testNamespacesInstanceModel.buildTestModel(NAMESPACE1, NAMESPACE1_PROPS); 265 testNamespacesInstanceModel.checkModel(model1, NAMESPACE1, NAMESPACE1_PROPS); 266 model2 = testNamespacesInstanceModel.buildTestModel(NAMESPACE2, NAMESPACE2_PROPS); 267 testNamespacesInstanceModel.checkModel(model2, NAMESPACE2, NAMESPACE2_PROPS); 268 model3 = testNamespacesInstanceModel.buildTestModel(NAMESPACE3, NAMESPACE3_PROPS); 269 testNamespacesInstanceModel.checkModel(model3, NAMESPACE3, NAMESPACE3_PROPS); 270 271 // Try REST post and puts with invalid content. 272 response = client.post(namespacePath1, Constants.MIMETYPE_JSON, toXML(model1)); 273 assertEquals(500, response.getCode()); 274 String jsonString = jsonMapper.writeValueAsString(model2); 275 response = client.put(namespacePath2, Constants.MIMETYPE_XML, Bytes.toBytes(jsonString)); 276 assertEquals(400, response.getCode()); 277 response = client.post(namespacePath3, Constants.MIMETYPE_PROTOBUF, toXML(model3)); 278 assertEquals(500, response.getCode()); 279 280 NamespaceDescriptor nd1 = findNamespace(admin, NAMESPACE1); 281 NamespaceDescriptor nd2 = findNamespace(admin, NAMESPACE2); 282 NamespaceDescriptor nd3 = findNamespace(admin, NAMESPACE3); 283 assertNull(nd1); 284 assertNull(nd2); 285 assertNull(nd3); 286 } 287 288 @Test 289 public void testNamespaceCreateAndDeleteXMLAndJSON() throws IOException, JAXBException { 290 String namespacePath1 = "/namespaces/" + NAMESPACE1; 291 String namespacePath2 = "/namespaces/" + NAMESPACE2; 292 NamespacesInstanceModel model1; 293 NamespacesInstanceModel model2; 294 Response response; 295 296 // Check that namespaces don't exist via non-REST call. 297 Admin admin = TEST_UTIL.getAdmin(); 298 assertNull(findNamespace(admin, NAMESPACE1)); 299 assertNull(findNamespace(admin, NAMESPACE2)); 300 301 model1 = testNamespacesInstanceModel.buildTestModel(NAMESPACE1, NAMESPACE1_PROPS); 302 testNamespacesInstanceModel.checkModel(model1, NAMESPACE1, NAMESPACE1_PROPS); 303 model2 = testNamespacesInstanceModel.buildTestModel(NAMESPACE2, NAMESPACE2_PROPS); 304 testNamespacesInstanceModel.checkModel(model2, NAMESPACE2, NAMESPACE2_PROPS); 305 306 // Test cannot PUT (alter) non-existent namespace. 307 response = client.put(namespacePath1, Constants.MIMETYPE_XML, toXML(model1)); 308 assertEquals(403, response.getCode()); 309 String jsonString = jsonMapper.writeValueAsString(model2); 310 response = client.put(namespacePath2, Constants.MIMETYPE_JSON, Bytes.toBytes(jsonString)); 311 assertEquals(403, response.getCode()); 312 313 // Test cannot create tables when in read only mode. 314 conf.set("hbase.rest.readonly", "true"); 315 response = client.post(namespacePath1, Constants.MIMETYPE_XML, toXML(model1)); 316 assertEquals(403, response.getCode()); 317 jsonString = jsonMapper.writeValueAsString(model2); 318 response = client.post(namespacePath2, Constants.MIMETYPE_JSON, Bytes.toBytes(jsonString)); 319 assertEquals(403, response.getCode()); 320 NamespaceDescriptor nd1 = findNamespace(admin, NAMESPACE1); 321 NamespaceDescriptor nd2 = findNamespace(admin, NAMESPACE2); 322 assertNull(nd1); 323 assertNull(nd2); 324 conf.set("hbase.rest.readonly", "false"); 325 326 // Create namespace via XML and JSON. 327 response = client.post(namespacePath1, Constants.MIMETYPE_XML, toXML(model1)); 328 assertEquals(201, response.getCode()); 329 jsonString = jsonMapper.writeValueAsString(model2); 330 response = client.post(namespacePath2, Constants.MIMETYPE_JSON, Bytes.toBytes(jsonString)); 331 assertEquals(201, response.getCode()); 332 333 // Check that created namespaces correctly. 334 nd1 = findNamespace(admin, NAMESPACE1); 335 nd2 = findNamespace(admin, NAMESPACE2); 336 assertNotNull(nd1); 337 assertNotNull(nd2); 338 checkNamespaceProperties(nd1, NAMESPACE1_PROPS); 339 checkNamespaceProperties(nd1, NAMESPACE1_PROPS); 340 341 // Test cannot delete tables when in read only mode. 342 conf.set("hbase.rest.readonly", "true"); 343 response = client.delete(namespacePath1); 344 assertEquals(403, response.getCode()); 345 response = client.delete(namespacePath2); 346 assertEquals(403, response.getCode()); 347 nd1 = findNamespace(admin, NAMESPACE1); 348 nd2 = findNamespace(admin, NAMESPACE2); 349 assertNotNull(nd1); 350 assertNotNull(nd2); 351 conf.set("hbase.rest.readonly", "false"); 352 353 // Delete namespaces via XML and JSON. 354 response = client.delete(namespacePath1); 355 assertEquals(200, response.getCode()); 356 response = client.delete(namespacePath2); 357 assertEquals(200, response.getCode()); 358 nd1 = findNamespace(admin, NAMESPACE1); 359 nd2 = findNamespace(admin, NAMESPACE2); 360 assertNull(nd1); 361 assertNull(nd2); 362 } 363 364 @Test 365 public void testNamespaceCreateAndDeletePBAndNoBody() throws IOException, JAXBException { 366 String namespacePath3 = "/namespaces/" + NAMESPACE3; 367 String namespacePath4 = "/namespaces/" + NAMESPACE4; 368 NamespacesInstanceModel model3; 369 NamespacesInstanceModel model4; 370 Response response; 371 372 // Check that namespaces don't exist via non-REST call. 373 Admin admin = TEST_UTIL.getAdmin(); 374 assertNull(findNamespace(admin, NAMESPACE3)); 375 assertNull(findNamespace(admin, NAMESPACE4)); 376 377 model3 = testNamespacesInstanceModel.buildTestModel(NAMESPACE3, NAMESPACE3_PROPS); 378 testNamespacesInstanceModel.checkModel(model3, NAMESPACE3, NAMESPACE3_PROPS); 379 model4 = testNamespacesInstanceModel.buildTestModel(NAMESPACE4, NAMESPACE4_PROPS); 380 testNamespacesInstanceModel.checkModel(model4, NAMESPACE4, NAMESPACE4_PROPS); 381 382 // Test cannot PUT (alter) non-existent namespace. 383 response = client.put(namespacePath3, Constants.MIMETYPE_BINARY, new byte[]{}); 384 assertEquals(403, response.getCode()); 385 response = client.put(namespacePath4, Constants.MIMETYPE_PROTOBUF, 386 model4.createProtobufOutput()); 387 assertEquals(403, response.getCode()); 388 389 // Test cannot create tables when in read only mode. 390 conf.set("hbase.rest.readonly", "true"); 391 response = client.post(namespacePath3, Constants.MIMETYPE_BINARY, new byte[]{}); 392 assertEquals(403, response.getCode()); 393 response = client.put(namespacePath4, Constants.MIMETYPE_PROTOBUF, 394 model4.createProtobufOutput()); 395 assertEquals(403, response.getCode()); 396 NamespaceDescriptor nd3 = findNamespace(admin, NAMESPACE3); 397 NamespaceDescriptor nd4 = findNamespace(admin, NAMESPACE4); 398 assertNull(nd3); 399 assertNull(nd4); 400 conf.set("hbase.rest.readonly", "false"); 401 402 // Create namespace via no body and protobuf. 403 response = client.post(namespacePath3, Constants.MIMETYPE_BINARY, new byte[]{}); 404 assertEquals(201, response.getCode()); 405 response = client.post(namespacePath4, Constants.MIMETYPE_PROTOBUF, 406 model4.createProtobufOutput()); 407 assertEquals(201, response.getCode()); 408 409 // Check that created namespaces correctly. 410 nd3 = findNamespace(admin, NAMESPACE3); 411 nd4 = findNamespace(admin, NAMESPACE4); 412 assertNotNull(nd3); 413 assertNotNull(nd4); 414 checkNamespaceProperties(nd3, new HashMap<>()); 415 checkNamespaceProperties(nd4, NAMESPACE4_PROPS); 416 417 // Check cannot post tables that already exist. 418 response = client.post(namespacePath3, Constants.MIMETYPE_BINARY, new byte[]{}); 419 assertEquals(403, response.getCode()); 420 response = client.post(namespacePath4, Constants.MIMETYPE_PROTOBUF, 421 model4.createProtobufOutput()); 422 assertEquals(403, response.getCode()); 423 424 // Check cannot post tables when in read only mode. 425 conf.set("hbase.rest.readonly", "true"); 426 response = client.delete(namespacePath3); 427 assertEquals(403, response.getCode()); 428 response = client.delete(namespacePath4); 429 assertEquals(403, response.getCode()); 430 nd3 = findNamespace(admin, NAMESPACE3); 431 nd4 = findNamespace(admin, NAMESPACE4); 432 assertNotNull(nd3); 433 assertNotNull(nd4); 434 conf.set("hbase.rest.readonly", "false"); 435 436 // Delete namespaces via XML and JSON. 437 response = client.delete(namespacePath3); 438 assertEquals(200, response.getCode()); 439 response = client.delete(namespacePath4); 440 assertEquals(200, response.getCode()); 441 nd3 = findNamespace(admin, NAMESPACE3); 442 nd4 = findNamespace(admin, NAMESPACE4); 443 assertNull(nd3); 444 assertNull(nd4); 445 } 446}