View Javadoc

1   /*
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.rest;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.IOException;
23  import java.io.StringWriter;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.HashMap;
27  import java.util.List;
28  import java.util.Map;
29  
30  import javax.ws.rs.core.MediaType;
31  import javax.xml.bind.JAXBContext;
32  import javax.xml.bind.JAXBException;
33  
34  import org.apache.hadoop.conf.Configuration;
35  import org.apache.hadoop.hbase.HBaseTestingUtility;
36  import org.apache.hadoop.hbase.HColumnDescriptor;
37  import org.apache.hadoop.hbase.HTableDescriptor;
38  import org.apache.hadoop.hbase.NamespaceDescriptor;
39  import org.apache.hadoop.hbase.TableName;
40  import org.apache.hadoop.hbase.client.Admin;
41  import org.apache.hadoop.hbase.rest.client.Client;
42  import org.apache.hadoop.hbase.rest.client.Cluster;
43  import org.apache.hadoop.hbase.rest.client.Response;
44  import org.apache.hadoop.hbase.rest.model.NamespacesInstanceModel;
45  import org.apache.hadoop.hbase.rest.model.TableListModel;
46  import org.apache.hadoop.hbase.rest.model.TableModel;
47  import org.apache.hadoop.hbase.rest.model.TestNamespacesInstanceModel;
48  import org.apache.hadoop.hbase.rest.provider.JacksonProvider;
49  import org.apache.hadoop.hbase.testclassification.MediumTests;
50  import org.apache.hadoop.hbase.util.Bytes;
51  import org.codehaus.jackson.map.ObjectMapper;
52  
53  import static org.junit.Assert.*;
54  
55  import org.junit.AfterClass;
56  import org.junit.BeforeClass;
57  import org.junit.Test;
58  import org.junit.experimental.categories.Category;
59  
60  @Category(MediumTests.class)
61  public class TestNamespacesInstanceResource {
62    private static String NAMESPACE1 = "TestNamespacesInstanceResource1";
63    private static Map<String,String> NAMESPACE1_PROPS = new HashMap<String,String>();
64    private static String NAMESPACE2 = "TestNamespacesInstanceResource2";
65    private static Map<String,String> NAMESPACE2_PROPS = new HashMap<String,String>();
66    private static String NAMESPACE3 = "TestNamespacesInstanceResource3";
67    private static Map<String,String> NAMESPACE3_PROPS = new HashMap<String,String>();
68    private static String NAMESPACE4 = "TestNamespacesInstanceResource4";
69    private static Map<String,String> NAMESPACE4_PROPS = new HashMap<String,String>();
70  
71    private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
72    private static final HBaseRESTTestingUtility REST_TEST_UTIL =
73      new HBaseRESTTestingUtility();
74    private static Client client;
75    private static JAXBContext context;
76    private static Configuration conf;
77    private static TestNamespacesInstanceModel testNamespacesInstanceModel;
78    protected static ObjectMapper jsonMapper;
79  
80    @BeforeClass
81    public static void setUpBeforeClass() throws Exception {
82      conf = TEST_UTIL.getConfiguration();
83      TEST_UTIL.startMiniCluster();
84      REST_TEST_UTIL.startServletContainer(conf);
85      client = new Client(new Cluster().add("localhost",
86        REST_TEST_UTIL.getServletPort()));
87      testNamespacesInstanceModel = new TestNamespacesInstanceModel();
88      context = JAXBContext.newInstance(NamespacesInstanceModel.class, TableListModel.class);
89      jsonMapper = new JacksonProvider()
90      .locateMapper(NamespacesInstanceModel.class, MediaType.APPLICATION_JSON_TYPE);
91      NAMESPACE1_PROPS.put("key1", "value1");
92      NAMESPACE2_PROPS.put("key2a", "value2a");
93      NAMESPACE2_PROPS.put("key2b", "value2b");
94      NAMESPACE3_PROPS.put("key3", "value3");
95      NAMESPACE4_PROPS.put("key4a", "value4a");
96      NAMESPACE4_PROPS.put("key4b", "value4b");
97    }
98  
99    @AfterClass
100   public static void tearDownAfterClass() throws Exception {
101     REST_TEST_UTIL.shutdownServletContainer();
102     TEST_UTIL.shutdownMiniCluster();
103   }
104 
105   private static byte[] toXML(NamespacesInstanceModel model) throws JAXBException {
106     StringWriter writer = new StringWriter();
107     context.createMarshaller().marshal(model, writer);
108     return Bytes.toBytes(writer.toString());
109   }
110 
111   @SuppressWarnings("unchecked")
112   private static <T> T fromXML(byte[] content)
113       throws JAXBException {
114     return (T) context.createUnmarshaller().unmarshal(new ByteArrayInputStream(content));
115   }
116 
117   private NamespaceDescriptor findNamespace(Admin admin, String namespaceName) throws IOException{
118     NamespaceDescriptor[] nd = admin.listNamespaceDescriptors();
119     for(int i = 0; i < nd.length; i++){
120       if(nd[i].getName().equals(namespaceName)){
121         return nd[i];
122       }
123     }
124     return null;
125   }
126 
127   private void checkNamespaceProperties(NamespaceDescriptor nd, Map<String,String> testProps){
128     checkNamespaceProperties(nd.getConfiguration(), testProps);
129   }
130 
131   private void checkNamespaceProperties(Map<String,String> namespaceProps, 
132       Map<String,String> testProps){
133     assertTrue(namespaceProps.size() == testProps.size());
134     for(String key: testProps.keySet()){
135       assertEquals(testProps.get(key), namespaceProps.get(key));
136     }
137   }
138 
139   private void checkNamespaceTables(List<TableModel> namespaceTables, List<String> testTables){
140     assertEquals(namespaceTables.size(), testTables.size());
141     for(int i = 0 ; i < namespaceTables.size() ; i++){
142       String tableName = ((TableModel) namespaceTables.get(i)).getName();
143       assertTrue(testTables.contains(tableName));
144     }
145   }
146 
147   @Test
148   public void testCannotDeleteDefaultAndHbaseNamespaces() throws IOException {
149     String defaultPath = "/namespaces/default";
150     String hbasePath = "/namespaces/hbase";
151     Response response;
152 
153     // Check that doesn't exist via non-REST call.
154     Admin admin = TEST_UTIL.getHBaseAdmin();
155     assertNotNull(findNamespace(admin, "default"));
156     assertNotNull(findNamespace(admin, "hbase"));
157 
158     // Try (but fail) to delete namespaces via REST.
159     response = client.delete(defaultPath);
160     assertEquals(503, response.getCode());
161     response = client.delete(hbasePath);
162     assertEquals(503, response.getCode());
163 
164     assertNotNull(findNamespace(admin, "default"));
165     assertNotNull(findNamespace(admin, "hbase"));
166   }
167 
168   @Test
169   public void testGetNamespaceTablesAndCannotDeleteNamespace() throws IOException, JAXBException {
170     Admin admin = TEST_UTIL.getHBaseAdmin();
171     String nsName = "TestNamespacesInstanceResource5";
172     Response response;
173 
174     // Create namespace via admin.
175     NamespaceDescriptor.Builder nsBuilder = NamespaceDescriptor.create(nsName);
176     NamespaceDescriptor nsd = nsBuilder.build(); 
177     nsd.setConfiguration("key1", "value1");
178     admin.createNamespace(nsd);
179 
180     // Create two tables via admin.
181     HColumnDescriptor colDesc = new HColumnDescriptor("cf1");
182     TableName tn1 = TableName.valueOf(nsName + ":table1");
183     HTableDescriptor table = new HTableDescriptor(tn1);
184     table.addFamily(colDesc);
185     admin.createTable(table);
186     TableName tn2 = TableName.valueOf(nsName + ":table2");
187     table = new HTableDescriptor(tn2);
188     table.addFamily(colDesc);
189     admin.createTable(table);
190 
191     Map<String, String> nsProperties = new HashMap<String,String>();
192     nsProperties.put("key1", "value1");
193     List<String> nsTables = Arrays.asList("table1", "table2");
194 
195     // Check get namespace properties as XML, JSON and Protobuf.
196     String namespacePath = "/namespaces/" + nsName;
197     response = client.get(namespacePath);
198     assertEquals(200, response.getCode());
199 
200     response = client.get(namespacePath, Constants.MIMETYPE_XML);
201     assertEquals(200, response.getCode());
202     NamespacesInstanceModel model = fromXML(response.getBody());
203     checkNamespaceProperties(model.getProperties(), nsProperties);
204 
205     response = client.get(namespacePath, Constants.MIMETYPE_JSON);
206     assertEquals(200, response.getCode());
207     model = jsonMapper.readValue(response.getBody(), NamespacesInstanceModel.class);
208     checkNamespaceProperties(model.getProperties(), nsProperties);
209 
210     response = client.get(namespacePath, Constants.MIMETYPE_PROTOBUF);
211     assertEquals(200, response.getCode());
212     model.getObjectFromMessage(response.getBody());
213     checkNamespaceProperties(model.getProperties(), nsProperties);
214 
215     // Check get namespace tables as XML, JSON and Protobuf.
216     namespacePath = "/namespaces/" + nsName + "/tables";
217     response = client.get(namespacePath);
218     assertEquals(200, response.getCode());
219 
220     response = client.get(namespacePath, Constants.MIMETYPE_XML);
221     assertEquals(200, response.getCode());
222     TableListModel tablemodel = fromXML(response.getBody());
223     checkNamespaceTables(tablemodel.getTables(), nsTables);
224 
225     response = client.get(namespacePath, Constants.MIMETYPE_JSON);
226     assertEquals(200, response.getCode());
227     tablemodel = jsonMapper.readValue(response.getBody(), TableListModel.class);
228     checkNamespaceTables(tablemodel.getTables(), nsTables);
229 
230     response = client.get(namespacePath, Constants.MIMETYPE_PROTOBUF);
231     assertEquals(200, response.getCode());
232     tablemodel.setTables(new ArrayList<TableModel>());
233     tablemodel.getObjectFromMessage(response.getBody());
234     checkNamespaceTables(tablemodel.getTables(), nsTables);
235 
236     // Check cannot delete namespace via REST because it contains tables.
237     response = client.delete(namespacePath);
238     namespacePath = "/namespaces/" + nsName;
239     assertEquals(503, response.getCode());
240   }
241 
242   @Test
243   public void testInvalidNamespacePostsAndPuts() throws IOException, JAXBException {
244     String namespacePath1 = "/namespaces/" + NAMESPACE1;
245     String namespacePath2 = "/namespaces/" + NAMESPACE2;
246     String namespacePath3 = "/namespaces/" + NAMESPACE3;
247     NamespacesInstanceModel model1;
248     NamespacesInstanceModel model2;
249     NamespacesInstanceModel model3;
250     Response response;
251 
252     // Check that namespaces don't exist via non-REST call.
253     Admin admin = TEST_UTIL.getHBaseAdmin();
254     assertNull(findNamespace(admin, NAMESPACE1));
255     assertNull(findNamespace(admin, NAMESPACE2));
256     assertNull(findNamespace(admin, NAMESPACE3));
257 
258     model1 = testNamespacesInstanceModel.buildTestModel(NAMESPACE1, NAMESPACE1_PROPS);
259     testNamespacesInstanceModel.checkModel(model1, NAMESPACE1, NAMESPACE1_PROPS);
260     model2 = testNamespacesInstanceModel.buildTestModel(NAMESPACE2, NAMESPACE2_PROPS);
261     testNamespacesInstanceModel.checkModel(model2, NAMESPACE2, NAMESPACE2_PROPS);
262     model3 = testNamespacesInstanceModel.buildTestModel(NAMESPACE3, NAMESPACE3_PROPS);
263     testNamespacesInstanceModel.checkModel(model3, NAMESPACE3, NAMESPACE3_PROPS);
264 
265     // Try REST post and puts with invalid content.
266     response = client.post(namespacePath1, Constants.MIMETYPE_JSON, toXML(model1));
267     assertEquals(500, response.getCode());
268     String jsonString = jsonMapper.writeValueAsString(model2);
269     response = client.put(namespacePath2, Constants.MIMETYPE_XML, Bytes.toBytes(jsonString));
270     assertEquals(400, response.getCode());
271     response = client.post(namespacePath3, Constants.MIMETYPE_PROTOBUF, toXML(model1));
272     assertEquals(500, response.getCode());
273 
274     NamespaceDescriptor nd1 = findNamespace(admin, NAMESPACE1);
275     NamespaceDescriptor nd2 = findNamespace(admin, NAMESPACE2);
276     NamespaceDescriptor nd3 = findNamespace(admin, NAMESPACE3);
277     assertNull(nd1);
278     assertNull(nd2);
279     assertNull(nd3);
280   }
281 
282   @Test
283   public void testNamespaceCreateAndDeleteXMLAndJSON() throws IOException, JAXBException {
284     String namespacePath1 = "/namespaces/" + NAMESPACE1;
285     String namespacePath2 = "/namespaces/" + NAMESPACE2;
286     NamespacesInstanceModel model1;
287     NamespacesInstanceModel model2;
288     Response response;
289 
290     // Check that namespaces don't exist via non-REST call.
291     Admin admin = TEST_UTIL.getHBaseAdmin();
292     assertNull(findNamespace(admin, NAMESPACE1));
293     assertNull(findNamespace(admin, NAMESPACE2));
294 
295     model1 = testNamespacesInstanceModel.buildTestModel(NAMESPACE1, NAMESPACE1_PROPS);
296     testNamespacesInstanceModel.checkModel(model1, NAMESPACE1, NAMESPACE1_PROPS);
297     model2 = testNamespacesInstanceModel.buildTestModel(NAMESPACE2, NAMESPACE2_PROPS);
298     testNamespacesInstanceModel.checkModel(model2, NAMESPACE2, NAMESPACE2_PROPS);
299 
300     // Test cannot PUT (alter) non-existent namespace.
301     response = client.put(namespacePath1, Constants.MIMETYPE_XML, toXML(model1));
302     assertEquals(403, response.getCode());
303     String jsonString = jsonMapper.writeValueAsString(model2);
304     response = client.put(namespacePath2, Constants.MIMETYPE_JSON, Bytes.toBytes(jsonString));
305     assertEquals(403, response.getCode());
306 
307     // Test cannot create tables when in read only mode.
308     conf.set("hbase.rest.readonly", "true");
309     response = client.post(namespacePath1, Constants.MIMETYPE_XML, toXML(model1));
310     assertEquals(403, response.getCode());
311     jsonString = jsonMapper.writeValueAsString(model2);
312     response = client.post(namespacePath2, Constants.MIMETYPE_JSON, Bytes.toBytes(jsonString));
313     assertEquals(403, response.getCode());
314     NamespaceDescriptor nd1 = findNamespace(admin, NAMESPACE1);
315     NamespaceDescriptor nd2 = findNamespace(admin, NAMESPACE2);
316     assertNull(nd1);
317     assertNull(nd2);
318     conf.set("hbase.rest.readonly", "false");
319 
320     // Create namespace via XML and JSON.
321     response = client.post(namespacePath1, Constants.MIMETYPE_XML, toXML(model1));
322     assertEquals(201, response.getCode());
323     jsonString = jsonMapper.writeValueAsString(model2);
324     response = client.post(namespacePath2, Constants.MIMETYPE_JSON, Bytes.toBytes(jsonString));
325     assertEquals(201, response.getCode());
326 
327     // Check that created namespaces correctly.
328     nd1 = findNamespace(admin, NAMESPACE1);
329     nd2 = findNamespace(admin, NAMESPACE2);
330     assertNotNull(nd1);
331     assertNotNull(nd2);
332     checkNamespaceProperties(nd1, NAMESPACE1_PROPS);
333     checkNamespaceProperties(nd1, NAMESPACE1_PROPS);
334 
335     // Test cannot delete tables when in read only mode.
336     conf.set("hbase.rest.readonly", "true");
337     response = client.delete(namespacePath1);
338     assertEquals(403, response.getCode());
339     response = client.delete(namespacePath2);
340     assertEquals(403, response.getCode());
341     nd1 = findNamespace(admin, NAMESPACE1);
342     nd2 = findNamespace(admin, NAMESPACE2);
343     assertNotNull(nd1);
344     assertNotNull(nd2);
345     conf.set("hbase.rest.readonly", "false");
346 
347     // Delete namespaces via XML and JSON.
348     response = client.delete(namespacePath1);
349     assertEquals(200, response.getCode());
350     response = client.delete(namespacePath2);
351     assertEquals(200, response.getCode());
352     nd1 = findNamespace(admin, NAMESPACE1);
353     nd2 = findNamespace(admin, NAMESPACE2);
354     assertNull(nd1);
355     assertNull(nd2);
356   }
357 
358   @Test
359   public void testNamespaceCreateAndDeletePBAndNoBody() throws IOException, JAXBException {
360     String namespacePath3 = "/namespaces/" + NAMESPACE3;
361     String namespacePath4 = "/namespaces/" + NAMESPACE4;
362     NamespacesInstanceModel model3;
363     NamespacesInstanceModel model4;
364     Response response;
365 
366     // Check that namespaces don't exist via non-REST call.
367     Admin admin = TEST_UTIL.getHBaseAdmin();
368     assertNull(findNamespace(admin, NAMESPACE3));
369     assertNull(findNamespace(admin, NAMESPACE4));
370 
371     model3 = testNamespacesInstanceModel.buildTestModel(NAMESPACE3, NAMESPACE3_PROPS);
372     testNamespacesInstanceModel.checkModel(model3, NAMESPACE3, NAMESPACE3_PROPS);
373     model4 = testNamespacesInstanceModel.buildTestModel(NAMESPACE4, NAMESPACE4_PROPS);
374     testNamespacesInstanceModel.checkModel(model4, NAMESPACE4, NAMESPACE4_PROPS);
375 
376     // Test cannot PUT (alter) non-existent namespace.
377     response = client.put(namespacePath3, Constants.MIMETYPE_BINARY, new byte[]{});
378     assertEquals(403, response.getCode());
379     response = client.put(namespacePath4, Constants.MIMETYPE_PROTOBUF,
380       model4.createProtobufOutput());
381     assertEquals(403, response.getCode());
382 
383     // Test cannot create tables when in read only mode.
384     conf.set("hbase.rest.readonly", "true");
385     response = client.post(namespacePath3, Constants.MIMETYPE_BINARY, new byte[]{});
386     assertEquals(403, response.getCode());
387     response = client.put(namespacePath4, Constants.MIMETYPE_PROTOBUF,
388       model4.createProtobufOutput());
389     assertEquals(403, response.getCode());
390     NamespaceDescriptor nd3 = findNamespace(admin, NAMESPACE3);
391     NamespaceDescriptor nd4 = findNamespace(admin, NAMESPACE4);
392     assertNull(nd3);
393     assertNull(nd4);
394     conf.set("hbase.rest.readonly", "false");
395 
396     // Create namespace via no body and protobuf.
397     response = client.post(namespacePath3, Constants.MIMETYPE_BINARY, new byte[]{});
398     assertEquals(201, response.getCode());
399     response = client.post(namespacePath4, Constants.MIMETYPE_PROTOBUF,
400       model4.createProtobufOutput());
401     assertEquals(201, response.getCode());
402 
403     // Check that created namespaces correctly.
404     nd3 = findNamespace(admin, NAMESPACE3);
405     nd4 = findNamespace(admin, NAMESPACE4);
406     assertNotNull(nd3);
407     assertNotNull(nd4);
408     checkNamespaceProperties(nd3, new HashMap<String,String>());
409     checkNamespaceProperties(nd4, NAMESPACE4_PROPS);
410 
411     // Check cannot post tables that already exist.
412     response = client.post(namespacePath3, Constants.MIMETYPE_BINARY, new byte[]{});
413     assertEquals(403, response.getCode());
414     response = client.post(namespacePath4, Constants.MIMETYPE_PROTOBUF, 
415       model4.createProtobufOutput());
416     assertEquals(403, response.getCode());
417 
418     // Check cannot post tables when in read only mode.
419     conf.set("hbase.rest.readonly", "true");
420     response = client.delete(namespacePath3);
421     assertEquals(403, response.getCode());
422     response = client.delete(namespacePath4);
423     assertEquals(403, response.getCode());
424     nd3 = findNamespace(admin, NAMESPACE3);
425     nd4 = findNamespace(admin, NAMESPACE4);
426     assertNotNull(nd3);
427     assertNotNull(nd4);
428     conf.set("hbase.rest.readonly", "false");
429 
430     // Delete namespaces via XML and JSON.
431     response = client.delete(namespacePath3);
432     assertEquals(200, response.getCode());
433     response = client.delete(namespacePath4);
434     assertEquals(200, response.getCode());
435     nd3 = findNamespace(admin, NAMESPACE3);
436     nd4 = findNamespace(admin, NAMESPACE4);
437     assertNull(nd3);
438     assertNull(nd4);
439   }
440 }