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  
20  package org.apache.hadoop.hbase.rest;
21  
22  import java.io.ByteArrayInputStream;
23  import java.io.IOException;
24  import java.io.StringWriter;
25  import java.util.ArrayList;
26  import java.util.Collection;
27  import java.util.List;
28  
29  import javax.xml.bind.JAXBContext;
30  import javax.xml.bind.JAXBException;
31  
32  import org.apache.http.Header;
33  import org.apache.http.message.BasicHeader;
34  
35  import org.apache.hadoop.conf.Configuration;
36  import org.apache.hadoop.hbase.HBaseTestingUtility;
37  import org.apache.hadoop.hbase.testclassification.MediumTests;
38  import org.apache.hadoop.hbase.TableName;
39  import org.apache.hadoop.hbase.client.Admin;
40  import org.apache.hadoop.hbase.rest.client.Client;
41  import org.apache.hadoop.hbase.rest.client.Cluster;
42  import org.apache.hadoop.hbase.rest.client.Response;
43  import org.apache.hadoop.hbase.rest.model.ColumnSchemaModel;
44  import org.apache.hadoop.hbase.rest.model.TableSchemaModel;
45  import org.apache.hadoop.hbase.rest.model.TestTableSchemaModel;
46  import org.apache.hadoop.hbase.util.Bytes;
47  
48  import static org.junit.Assert.*;
49  
50  import org.junit.AfterClass;
51  import org.junit.BeforeClass;
52  import org.junit.Test;
53  import org.junit.experimental.categories.Category;
54  import org.junit.runner.RunWith;
55  import org.junit.runners.Parameterized;
56  
57  @Category(MediumTests.class)
58  @RunWith(Parameterized.class)
59  public class TestSchemaResource {
60    private static String TABLE1 = "TestSchemaResource1";
61    private static String TABLE2 = "TestSchemaResource2";
62  
63    private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
64    private static final HBaseRESTTestingUtility REST_TEST_UTIL =
65      new HBaseRESTTestingUtility();
66    private static Client client;
67    private static JAXBContext context;
68    private static Configuration conf;
69    private static TestTableSchemaModel testTableSchemaModel;
70    private static Header extraHdr = null;
71    
72    private static boolean csrfEnabled = true;
73  
74    @Parameterized.Parameters
75    public static Collection<Object[]> data() {
76      List<Object[]> params = new ArrayList<Object[]>();
77      params.add(new Object[] {Boolean.TRUE});
78      params.add(new Object[] {Boolean.FALSE});
79      return params;
80    }
81  
82    public TestSchemaResource(Boolean csrf) {
83      csrfEnabled = csrf;
84    }
85  
86    @BeforeClass
87    public static void setUpBeforeClass() throws Exception {
88      conf = TEST_UTIL.getConfiguration();
89      conf.setBoolean(RESTServer.REST_CSRF_ENABLED_KEY, csrfEnabled);
90      extraHdr = new BasicHeader(RESTServer.REST_CSRF_CUSTOM_HEADER_DEFAULT, "");
91      TEST_UTIL.startMiniCluster();
92      REST_TEST_UTIL.startServletContainer(conf);
93      client = new Client(new Cluster().add("localhost",
94        REST_TEST_UTIL.getServletPort()));
95      testTableSchemaModel = new TestTableSchemaModel();
96      context = JAXBContext.newInstance(
97        ColumnSchemaModel.class,
98        TableSchemaModel.class);
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(TableSchemaModel model) throws JAXBException {
108     StringWriter writer = new StringWriter();
109     context.createMarshaller().marshal(model, writer);
110     return Bytes.toBytes(writer.toString());
111   }
112 
113   private static TableSchemaModel fromXML(byte[] content)
114       throws JAXBException {
115     return (TableSchemaModel) context.createUnmarshaller()
116       .unmarshal(new ByteArrayInputStream(content));
117   }
118 
119   @Test
120   public void testTableCreateAndDeleteXML() throws IOException, JAXBException {
121     String schemaPath = "/" + TABLE1 + "/schema";
122     TableSchemaModel model;
123     Response response;
124 
125     Admin admin = TEST_UTIL.getHBaseAdmin();
126     assertFalse(admin.tableExists(TableName.valueOf(TABLE1)));
127 
128     // create the table
129     model = testTableSchemaModel.buildTestModel(TABLE1);
130     testTableSchemaModel.checkModel(model, TABLE1);
131     if (csrfEnabled) {
132       // test put operation is forbidden without custom header
133       response = client.put(schemaPath, Constants.MIMETYPE_XML, toXML(model));
134       assertEquals(response.getCode(), 400);
135     }
136 
137     response = client.put(schemaPath, Constants.MIMETYPE_XML, toXML(model), extraHdr);
138     assertEquals(response.getCode(), 201);
139 
140     // recall the same put operation but in read-only mode
141     conf.set("hbase.rest.readonly", "true");
142     response = client.put(schemaPath, Constants.MIMETYPE_XML, toXML(model), extraHdr);
143     assertEquals(response.getCode(), 403);
144 
145     // retrieve the schema and validate it
146     response = client.get(schemaPath, Constants.MIMETYPE_XML);
147     assertEquals(response.getCode(), 200);
148     assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type"));
149     model = fromXML(response.getBody());
150     testTableSchemaModel.checkModel(model, TABLE1);
151 
152     // with json retrieve the schema and validate it
153     response = client.get(schemaPath, Constants.MIMETYPE_JSON);
154     assertEquals(response.getCode(), 200);
155     assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type"));
156     model = testTableSchemaModel.fromJSON(Bytes.toString(response.getBody()));
157     testTableSchemaModel.checkModel(model, TABLE1);
158 
159     if (csrfEnabled) {
160       // test delete schema operation is forbidden without custom header
161       response = client.delete(schemaPath);
162       assertEquals(400, response.getCode());
163     }
164 
165     // test delete schema operation is forbidden in read-only mode
166     response = client.delete(schemaPath, extraHdr);
167     assertEquals(response.getCode(), 403);
168 
169     // return read-only setting back to default
170     conf.set("hbase.rest.readonly", "false");
171 
172     // delete the table and make sure HBase concurs
173     response = client.delete(schemaPath, extraHdr);
174     assertEquals(response.getCode(), 200);
175     assertFalse(admin.tableExists(TableName.valueOf(TABLE1)));
176   }
177 
178   @Test
179   public void testTableCreateAndDeletePB() throws IOException, JAXBException {
180     String schemaPath = "/" + TABLE2 + "/schema";
181     TableSchemaModel model;
182     Response response;
183 
184     Admin admin = TEST_UTIL.getHBaseAdmin();
185     assertFalse(admin.tableExists(TableName.valueOf(TABLE2)));
186 
187     // create the table
188     model = testTableSchemaModel.buildTestModel(TABLE2);
189     testTableSchemaModel.checkModel(model, TABLE2);
190 
191     if (csrfEnabled) {
192       // test put operation is forbidden without custom header
193       response = client.put(schemaPath, Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput());
194       assertEquals(response.getCode(), 400);
195     }
196     response = client.put(schemaPath, Constants.MIMETYPE_PROTOBUF,
197       model.createProtobufOutput(), extraHdr);
198     assertEquals(response.getCode(), 201);
199 
200     // recall the same put operation but in read-only mode
201     conf.set("hbase.rest.readonly", "true");
202     response = client.put(schemaPath, Constants.MIMETYPE_PROTOBUF,
203       model.createProtobufOutput(), extraHdr);
204     assertNotNull(extraHdr);
205     assertEquals(response.getCode(), 403);
206 
207     // retrieve the schema and validate it
208     response = client.get(schemaPath, Constants.MIMETYPE_PROTOBUF);
209     assertEquals(response.getCode(), 200);
210     assertEquals(Constants.MIMETYPE_PROTOBUF, response.getHeader("content-type"));
211     model = new TableSchemaModel();
212     model.getObjectFromMessage(response.getBody());
213     testTableSchemaModel.checkModel(model, TABLE2);
214 
215     // retrieve the schema and validate it with alternate pbuf type
216     response = client.get(schemaPath, Constants.MIMETYPE_PROTOBUF_IETF);
217     assertEquals(response.getCode(), 200);
218     assertEquals(Constants.MIMETYPE_PROTOBUF_IETF, response.getHeader("content-type"));
219     model = new TableSchemaModel();
220     model.getObjectFromMessage(response.getBody());
221     testTableSchemaModel.checkModel(model, TABLE2);
222 
223     if (csrfEnabled) {
224       // test delete schema operation is forbidden without custom header
225       response = client.delete(schemaPath);
226       assertEquals(400, response.getCode());
227     }
228 
229     // test delete schema operation is forbidden in read-only mode
230     response = client.delete(schemaPath, extraHdr);
231     assertEquals(response.getCode(), 403);
232 
233     // return read-only setting back to default
234     conf.set("hbase.rest.readonly", "false");
235 
236     // delete the table and make sure HBase concurs
237     response = client.delete(schemaPath, extraHdr);
238     assertEquals(response.getCode(), 200);
239     assertFalse(admin.tableExists(TableName.valueOf(TABLE2)));
240   }
241 
242 }
243