View Javadoc

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