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.HBaseAdmin;
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(HBaseAdmin admin, String namespaceName)
118       throws IOException {
119     NamespaceDescriptor[] nd = admin.listNamespaceDescriptors();
120     for(int i = 0; i < nd.length; i++){
121       if(nd[i].getName().equals(namespaceName)){
122         return nd[i];
123       }
124     }
125     return null;
126   }
127 
128   private void checkNamespaceProperties(NamespaceDescriptor nd, Map<String,String> testProps){
129     checkNamespaceProperties(nd.getConfiguration(), testProps);
130   }
131 
132   private void checkNamespaceProperties(Map<String,String> namespaceProps, 
133       Map<String,String> testProps){
134     assertTrue(namespaceProps.size() == testProps.size());
135     for(String key: testProps.keySet()){
136       assertEquals(testProps.get(key), namespaceProps.get(key));
137     }
138   }
139 
140   private void checkNamespaceTables(List<TableModel> namespaceTables, List<String> testTables){
141     assertEquals(namespaceTables.size(), testTables.size());
142     for(int i = 0 ; i < namespaceTables.size() ; i++){
143       String tableName = ((TableModel) namespaceTables.get(i)).getName();
144       assertTrue(testTables.contains(tableName));
145     }
146   }
147 
148   @Test
149   public void testCannotDeleteDefaultAndHbaseNamespaces() throws IOException {
150     String defaultPath = "/namespaces/default";
151     String hbasePath = "/namespaces/hbase";
152     Response response;
153 
154     // Check that doesn't exist via non-REST call.
155     HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
156     assertNotNull(findNamespace(admin, "default"));
157     assertNotNull(findNamespace(admin, "hbase"));
158 
159     // Try (but fail) to delete namespaces via REST.
160     response = client.delete(defaultPath);
161     assertEquals(503, response.getCode());
162     response = client.delete(hbasePath);
163     assertEquals(503, response.getCode());
164 
165     assertNotNull(findNamespace(admin, "default"));
166     assertNotNull(findNamespace(admin, "hbase"));
167   }
168 
169   @Test
170   public void testGetNamespaceTablesAndCannotDeleteNamespace() throws IOException, JAXBException {
171     HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
172     String nsName = "TestNamespacesInstanceResource5";
173     Response response;
174 
175     // Create namespace via admin.
176     NamespaceDescriptor.Builder nsBuilder = NamespaceDescriptor.create(nsName);
177     NamespaceDescriptor nsd = nsBuilder.build(); 
178     nsd.setConfiguration("key1", "value1");
179     admin.createNamespace(nsd);
180 
181     // Create two tables via admin.
182     HColumnDescriptor colDesc = new HColumnDescriptor("cf1");
183     TableName tn1 = TableName.valueOf(nsName + ":table1");
184     HTableDescriptor table = new HTableDescriptor(tn1);
185     table.addFamily(colDesc);
186     admin.createTable(table);
187     TableName tn2 = TableName.valueOf(nsName + ":table2");
188     table = new HTableDescriptor(tn2);
189     table.addFamily(colDesc);
190     admin.createTable(table);
191 
192     Map<String, String> nsProperties = new HashMap<String,String>();
193     nsProperties.put("key1", "value1");
194     List<String> nsTables = Arrays.asList("table1", "table2");
195 
196     // Check get namespace properties as XML, JSON and Protobuf.
197     String namespacePath = "/namespaces/" + nsName;
198     response = client.get(namespacePath);
199     assertEquals(200, response.getCode());
200 
201     response = client.get(namespacePath, Constants.MIMETYPE_XML);
202     assertEquals(200, response.getCode());
203     NamespacesInstanceModel model = fromXML(response.getBody());
204     checkNamespaceProperties(model.getProperties(), nsProperties);
205 
206     response = client.get(namespacePath, Constants.MIMETYPE_JSON);
207     assertEquals(200, response.getCode());
208     model = jsonMapper.readValue(response.getBody(), NamespacesInstanceModel.class);
209     checkNamespaceProperties(model.getProperties(), nsProperties);
210 
211     response = client.get(namespacePath, Constants.MIMETYPE_PROTOBUF);
212     assertEquals(200, response.getCode());
213     model.getObjectFromMessage(response.getBody());
214     checkNamespaceProperties(model.getProperties(), nsProperties);
215 
216     // Check get namespace tables as XML, JSON and Protobuf.
217     namespacePath = "/namespaces/" + nsName + "/tables";
218     response = client.get(namespacePath);
219     assertEquals(200, response.getCode());
220 
221     response = client.get(namespacePath, Constants.MIMETYPE_XML);
222     assertEquals(200, response.getCode());
223     TableListModel tablemodel = fromXML(response.getBody());
224     checkNamespaceTables(tablemodel.getTables(), nsTables);
225 
226     response = client.get(namespacePath, Constants.MIMETYPE_JSON);
227     assertEquals(200, response.getCode());
228     tablemodel = jsonMapper.readValue(response.getBody(), TableListModel.class);
229     checkNamespaceTables(tablemodel.getTables(), nsTables);
230 
231     response = client.get(namespacePath, Constants.MIMETYPE_PROTOBUF);
232     assertEquals(200, response.getCode());
233     tablemodel.setTables(new ArrayList<TableModel>());
234     tablemodel.getObjectFromMessage(response.getBody());
235     checkNamespaceTables(tablemodel.getTables(), nsTables);
236 
237     // Check cannot delete namespace via REST because it contains tables.
238     response = client.delete(namespacePath);
239     namespacePath = "/namespaces/" + nsName;
240     assertEquals(503, response.getCode());
241   }
242 
243   @Test
244   public void testInvalidNamespacePostsAndPuts() throws IOException, JAXBException {
245     String namespacePath1 = "/namespaces/" + NAMESPACE1;
246     String namespacePath2 = "/namespaces/" + NAMESPACE2;
247     String namespacePath3 = "/namespaces/" + NAMESPACE3;
248     NamespacesInstanceModel model1;
249     NamespacesInstanceModel model2;
250     NamespacesInstanceModel model3;
251     Response response;
252 
253     // Check that namespaces don't exist via non-REST call.
254     HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
255     assertNull(findNamespace(admin, NAMESPACE1));
256     assertNull(findNamespace(admin, NAMESPACE2));
257     assertNull(findNamespace(admin, NAMESPACE3));
258 
259     model1 = testNamespacesInstanceModel.buildTestModel(NAMESPACE1, NAMESPACE1_PROPS);
260     testNamespacesInstanceModel.checkModel(model1, NAMESPACE1, NAMESPACE1_PROPS);
261     model2 = testNamespacesInstanceModel.buildTestModel(NAMESPACE2, NAMESPACE2_PROPS);
262     testNamespacesInstanceModel.checkModel(model2, NAMESPACE2, NAMESPACE2_PROPS);
263     model3 = testNamespacesInstanceModel.buildTestModel(NAMESPACE3, NAMESPACE3_PROPS);
264     testNamespacesInstanceModel.checkModel(model3, NAMESPACE3, NAMESPACE3_PROPS);
265 
266     // Try REST post and puts with invalid content.
267     response = client.post(namespacePath1, Constants.MIMETYPE_JSON, toXML(model1));
268     assertEquals(500, response.getCode());
269     String jsonString = jsonMapper.writeValueAsString(model2);
270     response = client.put(namespacePath2, Constants.MIMETYPE_XML, Bytes.toBytes(jsonString));
271     assertEquals(400, response.getCode());
272     response = client.post(namespacePath3, Constants.MIMETYPE_PROTOBUF, toXML(model1));
273     assertEquals(500, response.getCode());
274 
275     NamespaceDescriptor nd1 = findNamespace(admin, NAMESPACE1);
276     NamespaceDescriptor nd2 = findNamespace(admin, NAMESPACE2);
277     NamespaceDescriptor nd3 = findNamespace(admin, NAMESPACE3);
278     assertNull(nd1);
279     assertNull(nd2);
280     assertNull(nd3);
281   }
282 
283   @Test
284   public void testNamespaceCreateAndDeleteXMLAndJSON() throws IOException, JAXBException {
285     String namespacePath1 = "/namespaces/" + NAMESPACE1;
286     String namespacePath2 = "/namespaces/" + NAMESPACE2;
287     NamespacesInstanceModel model1;
288     NamespacesInstanceModel model2;
289     Response response;
290 
291     // Check that namespaces don't exist via non-REST call.
292     HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
293     assertNull(findNamespace(admin, NAMESPACE1));
294     assertNull(findNamespace(admin, NAMESPACE2));
295 
296     model1 = testNamespacesInstanceModel.buildTestModel(NAMESPACE1, NAMESPACE1_PROPS);
297     testNamespacesInstanceModel.checkModel(model1, NAMESPACE1, NAMESPACE1_PROPS);
298     model2 = testNamespacesInstanceModel.buildTestModel(NAMESPACE2, NAMESPACE2_PROPS);
299     testNamespacesInstanceModel.checkModel(model2, NAMESPACE2, NAMESPACE2_PROPS);
300 
301     // Test cannot PUT (alter) non-existent namespace.
302     response = client.put(namespacePath1, Constants.MIMETYPE_XML, toXML(model1));
303     assertEquals(403, response.getCode());
304     String jsonString = jsonMapper.writeValueAsString(model2);
305     response = client.put(namespacePath2, Constants.MIMETYPE_JSON, Bytes.toBytes(jsonString));
306     assertEquals(403, response.getCode());
307 
308     // Test cannot create tables when in read only mode.
309     conf.set("hbase.rest.readonly", "true");
310     response = client.post(namespacePath1, Constants.MIMETYPE_XML, toXML(model1));
311     assertEquals(403, response.getCode());
312     jsonString = jsonMapper.writeValueAsString(model2);
313     response = client.post(namespacePath2, Constants.MIMETYPE_JSON, Bytes.toBytes(jsonString));
314     assertEquals(403, response.getCode());
315     NamespaceDescriptor nd1 = findNamespace(admin, NAMESPACE1);
316     NamespaceDescriptor nd2 = findNamespace(admin, NAMESPACE2);
317     assertNull(nd1);
318     assertNull(nd2);
319     conf.set("hbase.rest.readonly", "false");
320 
321     // Create namespace via XML and JSON.
322     response = client.post(namespacePath1, Constants.MIMETYPE_XML, toXML(model1));
323     assertEquals(201, response.getCode());
324     jsonString = jsonMapper.writeValueAsString(model2);
325     response = client.post(namespacePath2, Constants.MIMETYPE_JSON, Bytes.toBytes(jsonString));
326     assertEquals(201, response.getCode());
327 
328     // Check that created namespaces correctly.
329     nd1 = findNamespace(admin, NAMESPACE1);
330     nd2 = findNamespace(admin, NAMESPACE2);
331     assertNotNull(nd1);
332     assertNotNull(nd2);
333     checkNamespaceProperties(nd1, NAMESPACE1_PROPS);
334     checkNamespaceProperties(nd1, NAMESPACE1_PROPS);
335 
336     // Test cannot delete tables when in read only mode.
337     conf.set("hbase.rest.readonly", "true");
338     response = client.delete(namespacePath1);
339     assertEquals(403, response.getCode());
340     response = client.delete(namespacePath2);
341     assertEquals(403, response.getCode());
342     nd1 = findNamespace(admin, NAMESPACE1);
343     nd2 = findNamespace(admin, NAMESPACE2);
344     assertNotNull(nd1);
345     assertNotNull(nd2);
346     conf.set("hbase.rest.readonly", "false");
347 
348     // Delete namespaces via XML and JSON.
349     response = client.delete(namespacePath1);
350     assertEquals(200, response.getCode());
351     response = client.delete(namespacePath2);
352     assertEquals(200, response.getCode());
353     nd1 = findNamespace(admin, NAMESPACE1);
354     nd2 = findNamespace(admin, NAMESPACE2);
355     assertNull(nd1);
356     assertNull(nd2);
357   }
358 
359   @Test
360   public void testNamespaceCreateAndDeletePBAndNoBody() throws IOException, JAXBException {
361     String namespacePath3 = "/namespaces/" + NAMESPACE3;
362     String namespacePath4 = "/namespaces/" + NAMESPACE4;
363     NamespacesInstanceModel model3;
364     NamespacesInstanceModel model4;
365     Response response;
366 
367     // Check that namespaces don't exist via non-REST call.
368     HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
369     assertNull(findNamespace(admin, NAMESPACE3));
370     assertNull(findNamespace(admin, NAMESPACE4));
371 
372     model3 = testNamespacesInstanceModel.buildTestModel(NAMESPACE3, NAMESPACE3_PROPS);
373     testNamespacesInstanceModel.checkModel(model3, NAMESPACE3, NAMESPACE3_PROPS);
374     model4 = testNamespacesInstanceModel.buildTestModel(NAMESPACE4, NAMESPACE4_PROPS);
375     testNamespacesInstanceModel.checkModel(model4, NAMESPACE4, NAMESPACE4_PROPS);
376 
377     // Test cannot PUT (alter) non-existent namespace.
378     response = client.put(namespacePath3, Constants.MIMETYPE_BINARY, new byte[]{});
379     assertEquals(403, response.getCode());
380     response = client.put(namespacePath4, Constants.MIMETYPE_PROTOBUF,
381       model4.createProtobufOutput());
382     assertEquals(403, response.getCode());
383 
384     // Test cannot create tables when in read only mode.
385     conf.set("hbase.rest.readonly", "true");
386     response = client.post(namespacePath3, Constants.MIMETYPE_BINARY, new byte[]{});
387     assertEquals(403, response.getCode());
388     response = client.put(namespacePath4, Constants.MIMETYPE_PROTOBUF,
389       model4.createProtobufOutput());
390     assertEquals(403, response.getCode());
391     NamespaceDescriptor nd3 = findNamespace(admin, NAMESPACE3);
392     NamespaceDescriptor nd4 = findNamespace(admin, NAMESPACE4);
393     assertNull(nd3);
394     assertNull(nd4);
395     conf.set("hbase.rest.readonly", "false");
396 
397     // Create namespace via no body and protobuf.
398     response = client.post(namespacePath3, Constants.MIMETYPE_BINARY, new byte[]{});
399     assertEquals(201, response.getCode());
400     response = client.post(namespacePath4, Constants.MIMETYPE_PROTOBUF,
401       model4.createProtobufOutput());
402     assertEquals(201, response.getCode());
403 
404     // Check that created namespaces correctly.
405     nd3 = findNamespace(admin, NAMESPACE3);
406     nd4 = findNamespace(admin, NAMESPACE4);
407     assertNotNull(nd3);
408     assertNotNull(nd4);
409     checkNamespaceProperties(nd3, new HashMap<String,String>());
410     checkNamespaceProperties(nd4, NAMESPACE4_PROPS);
411 
412     // Check cannot post tables that already exist.
413     response = client.post(namespacePath3, Constants.MIMETYPE_BINARY, new byte[]{});
414     assertEquals(403, response.getCode());
415     response = client.post(namespacePath4, Constants.MIMETYPE_PROTOBUF, 
416       model4.createProtobufOutput());
417     assertEquals(403, response.getCode());
418 
419     // Check cannot post tables when in read only mode.
420     conf.set("hbase.rest.readonly", "true");
421     response = client.delete(namespacePath3);
422     assertEquals(403, response.getCode());
423     response = client.delete(namespacePath4);
424     assertEquals(403, response.getCode());
425     nd3 = findNamespace(admin, NAMESPACE3);
426     nd4 = findNamespace(admin, NAMESPACE4);
427     assertNotNull(nd3);
428     assertNotNull(nd4);
429     conf.set("hbase.rest.readonly", "false");
430 
431     // Delete namespaces via XML and JSON.
432     response = client.delete(namespacePath3);
433     assertEquals(200, response.getCode());
434     response = client.delete(namespacePath4);
435     assertEquals(200, response.getCode());
436     nd3 = findNamespace(admin, NAMESPACE3);
437     nd4 = findNamespace(admin, NAMESPACE4);
438     assertNull(nd3);
439     assertNull(nd4);
440   }
441 }