001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.thrift2;
019
020import static java.nio.ByteBuffer.wrap;
021import static org.junit.jupiter.api.Assertions.assertEquals;
022import static org.junit.jupiter.api.Assertions.assertFalse;
023import static org.junit.jupiter.api.Assertions.assertTrue;
024
025import java.io.IOException;
026import java.nio.ByteBuffer;
027import java.util.ArrayList;
028import java.util.Arrays;
029import java.util.List;
030import org.apache.hadoop.conf.Configuration;
031import org.apache.hadoop.hbase.DoNotRetryIOException;
032import org.apache.hadoop.hbase.HBaseTestingUtil;
033import org.apache.hadoop.hbase.TableName;
034import org.apache.hadoop.hbase.client.Admin;
035import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
036import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
037import org.apache.hadoop.hbase.client.TableDescriptor;
038import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
039import org.apache.hadoop.hbase.security.UserProvider;
040import org.apache.hadoop.hbase.testclassification.ClientTests;
041import org.apache.hadoop.hbase.testclassification.MediumTests;
042import org.apache.hadoop.hbase.thrift2.generated.TAppend;
043import org.apache.hadoop.hbase.thrift2.generated.TColumnIncrement;
044import org.apache.hadoop.hbase.thrift2.generated.TColumnValue;
045import org.apache.hadoop.hbase.thrift2.generated.TCompareOperator;
046import org.apache.hadoop.hbase.thrift2.generated.TDelete;
047import org.apache.hadoop.hbase.thrift2.generated.TGet;
048import org.apache.hadoop.hbase.thrift2.generated.TIOError;
049import org.apache.hadoop.hbase.thrift2.generated.TIncrement;
050import org.apache.hadoop.hbase.thrift2.generated.TMutation;
051import org.apache.hadoop.hbase.thrift2.generated.TPut;
052import org.apache.hadoop.hbase.thrift2.generated.TRowMutations;
053import org.apache.hadoop.hbase.thrift2.generated.TScan;
054import org.apache.hadoop.hbase.util.Bytes;
055import org.junit.jupiter.api.AfterAll;
056import org.junit.jupiter.api.BeforeAll;
057import org.junit.jupiter.api.BeforeEach;
058import org.junit.jupiter.api.Tag;
059import org.junit.jupiter.api.Test;
060
061import org.apache.hbase.thirdparty.org.apache.thrift.TException;
062
063@Tag(ClientTests.TAG)
064@Tag(MediumTests.TAG)
065public class TestThriftHBaseServiceHandlerWithReadOnly {
066
067  private static final HBaseTestingUtil UTIL = new HBaseTestingUtil();
068
069  // Static names for tables, columns, rows, and values
070  private static byte[] tableAname = Bytes.toBytes("tableA");
071  private static byte[] familyAname = Bytes.toBytes("familyA");
072  private static byte[] familyBname = Bytes.toBytes("familyB");
073  private static byte[] qualifierAname = Bytes.toBytes("qualifierA");
074  private static byte[] qualifierBname = Bytes.toBytes("qualifierB");
075  private static byte[] valueAname = Bytes.toBytes("valueA");
076  private static byte[] valueBname = Bytes.toBytes("valueB");
077  private static ColumnFamilyDescriptor[] families = new ColumnFamilyDescriptor[] {
078    ColumnFamilyDescriptorBuilder.newBuilder(familyAname).setMaxVersions(3).build(),
079    ColumnFamilyDescriptorBuilder.newBuilder(familyBname).setMaxVersions(2).build() };
080
081  @BeforeAll
082  public static void beforeClass() throws Exception {
083    UTIL.getConfiguration().setBoolean("hbase.thrift.readonly", true);
084    UTIL.getConfiguration().set("hbase.client.retries.number", "3");
085    UTIL.startMiniCluster();
086    Admin admin = UTIL.getAdmin();
087    TableDescriptor tableDescriptor = TableDescriptorBuilder
088      .newBuilder(TableName.valueOf(tableAname)).setColumnFamilies(Arrays.asList(families)).build();
089    admin.createTable(tableDescriptor);
090    admin.close();
091  }
092
093  @AfterAll
094  public static void afterClass() throws Exception {
095    UTIL.shutdownMiniCluster();
096  }
097
098  @BeforeEach
099  public void setup() throws Exception {
100
101  }
102
103  private ThriftHBaseServiceHandler createHandler() throws TException {
104    try {
105      Configuration conf = UTIL.getConfiguration();
106      return new ThriftHBaseServiceHandler(conf, UserProvider.instantiate(conf));
107    } catch (IOException ie) {
108      throw new TException(ie);
109    }
110  }
111
112  @Test
113  public void testExistsWithReadOnly() throws TException {
114
115    ThriftHBaseServiceHandler handler = createHandler();
116    byte[] rowName = Bytes.toBytes("testExists");
117    ByteBuffer table = wrap(tableAname);
118    TGet get = new TGet(wrap(rowName));
119
120    boolean exceptionCaught = false;
121    try {
122      handler.exists(table, get);
123    } catch (TIOError e) {
124      exceptionCaught = true;
125    } finally {
126      assertFalse(exceptionCaught);
127    }
128  }
129
130  @Test
131  public void testExistsAllWithReadOnly() throws TException {
132    ThriftHBaseServiceHandler handler = createHandler();
133    byte[] rowName1 = Bytes.toBytes("testExistsAll1");
134    byte[] rowName2 = Bytes.toBytes("testExistsAll2");
135    ByteBuffer table = wrap(tableAname);
136
137    List<TGet> gets = new ArrayList<>();
138    gets.add(new TGet(wrap(rowName1)));
139    gets.add(new TGet(wrap(rowName2)));
140
141    boolean exceptionCaught = false;
142    try {
143      handler.existsAll(table, gets);
144    } catch (TIOError e) {
145      exceptionCaught = true;
146    } finally {
147      assertFalse(exceptionCaught);
148    }
149  }
150
151  @Test
152  public void testGetWithReadOnly() throws Exception {
153    ThriftHBaseServiceHandler handler = createHandler();
154    byte[] rowName = Bytes.toBytes("testGet");
155    ByteBuffer table = wrap(tableAname);
156
157    TGet get = new TGet(wrap(rowName));
158
159    boolean exceptionCaught = false;
160    try {
161      handler.get(table, get);
162    } catch (TIOError e) {
163      exceptionCaught = true;
164    } finally {
165      assertFalse(exceptionCaught);
166    }
167  }
168
169  @Test
170  public void testGetMultipleWithReadOnly() throws Exception {
171    ThriftHBaseServiceHandler handler = createHandler();
172    ByteBuffer table = wrap(tableAname);
173    byte[] rowName1 = Bytes.toBytes("testGetMultiple1");
174    byte[] rowName2 = Bytes.toBytes("testGetMultiple2");
175
176    List<TGet> gets = new ArrayList<>(2);
177    gets.add(new TGet(wrap(rowName1)));
178    gets.add(new TGet(wrap(rowName2)));
179
180    boolean exceptionCaught = false;
181    try {
182      handler.getMultiple(table, gets);
183    } catch (TIOError e) {
184      exceptionCaught = true;
185    } finally {
186      assertFalse(exceptionCaught);
187    }
188  }
189
190  @Test
191  public void testPutWithReadOnly() throws Exception {
192    ThriftHBaseServiceHandler handler = createHandler();
193    ByteBuffer table = wrap(tableAname);
194    byte[] rowName = Bytes.toBytes("testPut");
195
196    List<TColumnValue> columnValues = new ArrayList<>(2);
197    columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname)));
198    columnValues.add(new TColumnValue(wrap(familyBname), wrap(qualifierBname), wrap(valueBname)));
199    TPut put = new TPut(wrap(rowName), columnValues);
200
201    boolean exceptionCaught = false;
202    try {
203      handler.put(table, put);
204    } catch (TIOError e) {
205      exceptionCaught = true;
206      assertTrue(e.getCause() instanceof DoNotRetryIOException);
207      assertEquals("Thrift Server is in Read-only mode.", e.getMessage());
208    } finally {
209      assertTrue(exceptionCaught);
210    }
211  }
212
213  @Test
214  public void testCheckAndPutWithReadOnly() throws Exception {
215    ThriftHBaseServiceHandler handler = createHandler();
216    byte[] rowName = Bytes.toBytes("testCheckAndPut");
217    ByteBuffer table = wrap(tableAname);
218
219    List<TColumnValue> columnValuesA = new ArrayList<>(1);
220    TColumnValue columnValueA =
221      new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname));
222    columnValuesA.add(columnValueA);
223    TPut putA = new TPut(wrap(rowName), columnValuesA);
224    putA.setColumnValues(columnValuesA);
225
226    List<TColumnValue> columnValuesB = new ArrayList<>(1);
227    TColumnValue columnValueB =
228      new TColumnValue(wrap(familyBname), wrap(qualifierBname), wrap(valueBname));
229    columnValuesB.add(columnValueB);
230    TPut putB = new TPut(wrap(rowName), columnValuesB);
231    putB.setColumnValues(columnValuesB);
232
233    boolean exceptionCaught = false;
234    try {
235      handler.checkAndPut(table, wrap(rowName), wrap(familyAname), wrap(qualifierAname),
236        wrap(valueAname), putB);
237    } catch (TIOError e) {
238      exceptionCaught = true;
239      assertTrue(e.getCause() instanceof DoNotRetryIOException);
240      assertEquals("Thrift Server is in Read-only mode.", e.getMessage());
241    } finally {
242      assertTrue(exceptionCaught);
243    }
244  }
245
246  @Test
247  public void testPutMultipleWithReadOnly() throws Exception {
248    ThriftHBaseServiceHandler handler = createHandler();
249    ByteBuffer table = wrap(tableAname);
250    byte[] rowName1 = Bytes.toBytes("testPutMultiple1");
251    byte[] rowName2 = Bytes.toBytes("testPutMultiple2");
252
253    List<TColumnValue> columnValues = new ArrayList<>(2);
254    columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname)));
255    columnValues.add(new TColumnValue(wrap(familyBname), wrap(qualifierBname), wrap(valueBname)));
256    List<TPut> puts = new ArrayList<>(2);
257    puts.add(new TPut(wrap(rowName1), columnValues));
258    puts.add(new TPut(wrap(rowName2), columnValues));
259
260    boolean exceptionCaught = false;
261    try {
262      handler.putMultiple(table, puts);
263    } catch (TIOError e) {
264      exceptionCaught = true;
265      assertTrue(e.getCause() instanceof DoNotRetryIOException);
266      assertEquals("Thrift Server is in Read-only mode.", e.getMessage());
267    } finally {
268      assertTrue(exceptionCaught);
269    }
270  }
271
272  @Test
273  public void testDeleteWithReadOnly() throws Exception {
274    ThriftHBaseServiceHandler handler = createHandler();
275    byte[] rowName = Bytes.toBytes("testDelete");
276    ByteBuffer table = wrap(tableAname);
277
278    TDelete delete = new TDelete(wrap(rowName));
279
280    boolean exceptionCaught = false;
281    try {
282      handler.deleteSingle(table, delete);
283    } catch (TIOError e) {
284      exceptionCaught = true;
285      assertTrue(e.getCause() instanceof DoNotRetryIOException);
286      assertEquals("Thrift Server is in Read-only mode.", e.getMessage());
287    } finally {
288      assertTrue(exceptionCaught);
289    }
290  }
291
292  @Test
293  public void testDeleteMultipleWithReadOnly() throws Exception {
294    ThriftHBaseServiceHandler handler = createHandler();
295    ByteBuffer table = wrap(tableAname);
296    byte[] rowName1 = Bytes.toBytes("testDeleteMultiple1");
297    byte[] rowName2 = Bytes.toBytes("testDeleteMultiple2");
298
299    List<TDelete> deletes = new ArrayList<>(2);
300    deletes.add(new TDelete(wrap(rowName1)));
301    deletes.add(new TDelete(wrap(rowName2)));
302
303    boolean exceptionCaught = false;
304    try {
305      handler.deleteMultiple(table, deletes);
306    } catch (TIOError e) {
307      exceptionCaught = true;
308      assertTrue(e.getCause() instanceof DoNotRetryIOException);
309      assertEquals("Thrift Server is in Read-only mode.", e.getMessage());
310    } finally {
311      assertTrue(exceptionCaught);
312    }
313  }
314
315  @Test
316  public void testCheckAndMutateWithReadOnly() throws Exception {
317    ThriftHBaseServiceHandler handler = createHandler();
318    ByteBuffer table = wrap(tableAname);
319    ByteBuffer row = wrap(Bytes.toBytes("row"));
320    ByteBuffer family = wrap(familyAname);
321    ByteBuffer qualifier = wrap(qualifierAname);
322    ByteBuffer value = wrap(valueAname);
323
324    List<TColumnValue> columnValuesB = new ArrayList<>(1);
325    TColumnValue columnValueB = new TColumnValue(family, wrap(qualifierBname), wrap(valueBname));
326    columnValuesB.add(columnValueB);
327    TPut putB = new TPut(row, columnValuesB);
328    putB.setColumnValues(columnValuesB);
329
330    TRowMutations tRowMutations =
331      new TRowMutations(row, Arrays.<TMutation> asList(TMutation.put(putB)));
332
333    boolean exceptionCaught = false;
334    try {
335      handler.checkAndMutate(table, row, family, qualifier, TCompareOperator.EQUAL, value,
336        tRowMutations);
337    } catch (TIOError e) {
338      exceptionCaught = true;
339      assertTrue(e.getCause() instanceof DoNotRetryIOException);
340      assertEquals("Thrift Server is in Read-only mode.", e.getMessage());
341    } finally {
342      assertTrue(exceptionCaught);
343    }
344  }
345
346  @Test
347  public void testCheckAndDeleteWithReadOnly() throws Exception {
348    ThriftHBaseServiceHandler handler = createHandler();
349    byte[] rowName = Bytes.toBytes("testCheckAndDelete");
350    ByteBuffer table = wrap(tableAname);
351
352    TDelete delete = new TDelete(wrap(rowName));
353
354    boolean exceptionCaught = false;
355    try {
356      handler.checkAndDelete(table, wrap(rowName), wrap(familyAname), wrap(qualifierAname),
357        wrap(valueAname), delete);
358    } catch (TIOError e) {
359      exceptionCaught = true;
360      assertTrue(e.getCause() instanceof DoNotRetryIOException);
361      assertEquals("Thrift Server is in Read-only mode.", e.getMessage());
362    } finally {
363      assertTrue(exceptionCaught);
364    }
365  }
366
367  @Test
368  public void testIncrementWithReadOnly() throws Exception {
369    ThriftHBaseServiceHandler handler = createHandler();
370    byte[] rowName = Bytes.toBytes("testIncrement");
371    ByteBuffer table = wrap(tableAname);
372
373    List<TColumnIncrement> incrementColumns = new ArrayList<>(1);
374    incrementColumns.add(new TColumnIncrement(wrap(familyAname), wrap(qualifierAname)));
375    TIncrement increment = new TIncrement(wrap(rowName), incrementColumns);
376
377    boolean exceptionCaught = false;
378    try {
379      handler.increment(table, increment);
380    } catch (TIOError e) {
381      exceptionCaught = true;
382      assertTrue(e.getCause() instanceof DoNotRetryIOException);
383      assertEquals("Thrift Server is in Read-only mode.", e.getMessage());
384    } finally {
385      assertTrue(exceptionCaught);
386    }
387  }
388
389  @Test
390  public void testAppendWithReadOnly() throws Exception {
391    ThriftHBaseServiceHandler handler = createHandler();
392    byte[] rowName = Bytes.toBytes("testAppend");
393    ByteBuffer table = wrap(tableAname);
394    byte[] v1 = Bytes.toBytes("42");
395
396    List<TColumnValue> appendColumns = new ArrayList<>(1);
397    appendColumns.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(v1)));
398    TAppend append = new TAppend(wrap(rowName), appendColumns);
399
400    boolean exceptionCaught = false;
401    try {
402      handler.append(table, append);
403    } catch (TIOError e) {
404      exceptionCaught = true;
405      assertTrue(e.getCause() instanceof DoNotRetryIOException);
406      assertEquals("Thrift Server is in Read-only mode.", e.getMessage());
407    } finally {
408      assertTrue(exceptionCaught);
409    }
410  }
411
412  @Test
413  public void testMutateRowWithReadOnly() throws Exception {
414    ThriftHBaseServiceHandler handler = createHandler();
415    byte[] rowName = Bytes.toBytes("testMutateRow");
416    ByteBuffer table = wrap(tableAname);
417
418    List<TColumnValue> columnValuesA = new ArrayList<>(1);
419    TColumnValue columnValueA =
420      new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname));
421    columnValuesA.add(columnValueA);
422    TPut putA = new TPut(wrap(rowName), columnValuesA);
423    putA.setColumnValues(columnValuesA);
424
425    TDelete delete = new TDelete(wrap(rowName));
426
427    List<TMutation> mutations = new ArrayList<>(2);
428    TMutation mutationA = TMutation.put(putA);
429    mutations.add(mutationA);
430    TMutation mutationB = TMutation.deleteSingle(delete);
431    mutations.add(mutationB);
432    TRowMutations tRowMutations = new TRowMutations(wrap(rowName), mutations);
433
434    boolean exceptionCaught = false;
435    try {
436      handler.mutateRow(table, tRowMutations);
437    } catch (TIOError e) {
438      exceptionCaught = true;
439      assertTrue(e.getCause() instanceof DoNotRetryIOException);
440      assertEquals("Thrift Server is in Read-only mode.", e.getMessage());
441    } finally {
442      assertTrue(exceptionCaught);
443    }
444  }
445
446  @Test
447  public void testScanWithReadOnly() throws Exception {
448    ThriftHBaseServiceHandler handler = createHandler();
449    ByteBuffer table = wrap(tableAname);
450
451    TScan scan = new TScan();
452    boolean exceptionCaught = false;
453    try {
454      int scanId = handler.openScanner(table, scan);
455      handler.getScannerRows(scanId, 10);
456      handler.closeScanner(scanId);
457    } catch (TIOError e) {
458      exceptionCaught = true;
459    } finally {
460      assertFalse(exceptionCaught);
461    }
462  }
463}