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