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.coprocessor;
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertTrue;
23  
24  import java.io.IOException;
25  import java.util.Iterator;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.hadoop.hbase.Cell;
30  import org.apache.hadoop.hbase.CellUtil;
31  import org.apache.hadoop.hbase.DoNotRetryIOException;
32  import org.apache.hadoop.hbase.HBaseTestingUtility;
33  import org.apache.hadoop.hbase.HColumnDescriptor;
34  import org.apache.hadoop.hbase.HTableDescriptor;
35  import org.apache.hadoop.hbase.TableName;
36  import org.apache.hadoop.hbase.Tag;
37  import org.apache.hadoop.hbase.TagType;
38  import org.apache.hadoop.hbase.client.Append;
39  import org.apache.hadoop.hbase.client.Connection;
40  import org.apache.hadoop.hbase.client.Get;
41  import org.apache.hadoop.hbase.client.Increment;
42  import org.apache.hadoop.hbase.client.Mutation;
43  import org.apache.hadoop.hbase.client.Result;
44  import org.apache.hadoop.hbase.client.Table;
45  import org.apache.hadoop.hbase.client.TestFromClientSide;
46  import org.apache.hadoop.hbase.security.access.AccessController;
47  import org.apache.hadoop.hbase.security.access.Permission;
48  import org.apache.hadoop.hbase.testclassification.CoprocessorTests;
49  import org.apache.hadoop.hbase.testclassification.MediumTests;
50  import org.apache.hadoop.hbase.util.Bytes;
51  import org.junit.AfterClass;
52  import org.junit.BeforeClass;
53  import org.junit.Rule;
54  import org.junit.Test;
55  import org.junit.experimental.categories.Category;
56  import org.junit.rules.TestName;
57  
58  /**
59   * Test coprocessor methods
60   * {@link RegionObserver#postMutationBeforeWAL(ObserverContext, RegionObserver.MutationType,
61   * Mutation, Cell, Cell)}.
62   * This method will change the cells which will be applied to
63   * memstore and WAL. So add unit test for the case which change the cell's tags .
64   */
65  @Category({ CoprocessorTests.class, MediumTests.class })
66  public class TestPostMutationBeforeWAL {
67  
68    @Rule
69    public TestName name = new TestName();
70  
71    private static final Log LOG = LogFactory.getLog(TestFromClientSide.class);
72  
73    private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
74  
75    private static Connection connection;
76  
77    private static final byte [] ROW = Bytes.toBytes("row");
78    private static final String CF1 = "cf1";
79    private static final byte[] CF1_BYTES = Bytes.toBytes(CF1);
80    private static final String CF2 = "cf2";
81    private static final byte[] CF2_BYTES = Bytes.toBytes(CF2);
82    private static final byte[] CQ1 = Bytes.toBytes("cq1");
83    private static final byte[] CQ2 = Bytes.toBytes("cq2");
84    private static final byte[] VALUE = Bytes.toBytes("value");
85    private static final byte[] VALUE2 = Bytes.toBytes("valuevalue");
86    private static final String USER = "User";
87    private static final Permission PERMS = new Permission(Permission.Action.READ);
88  
89    @BeforeClass
90    public static void setupBeforeClass() throws Exception {
91      UTIL.startMiniCluster();
92      connection = UTIL.getConnection();
93    }
94  
95    @AfterClass
96    public static void tearDownAfterClass() throws Exception {
97      connection.close();
98      UTIL.shutdownMiniCluster();
99    }
100 
101   private void createTableWithCoprocessor(TableName tableName, String coprocessor)
102     throws IOException {
103     HTableDescriptor tableDesc = new HTableDescriptor(tableName);
104     tableDesc.addFamily(new HColumnDescriptor(CF1_BYTES));
105     tableDesc.addFamily(new HColumnDescriptor(CF2_BYTES));
106     tableDesc.addCoprocessor(coprocessor);
107     connection.getAdmin().createTable(tableDesc);
108   }
109 
110   @Test
111   public void testIncrementTTLWithACLTag() throws Exception {
112     TableName tableName = TableName.valueOf(name.getMethodName());
113     createTableWithCoprocessor(tableName, ChangeCellWithACLTagObserver.class.getName());
114     try (Table table = connection.getTable(tableName)) {
115       // Increment without TTL
116       Increment firstIncrement = new Increment(ROW).addColumn(CF1_BYTES, CQ1, 1)
117         .setACL(USER, PERMS);
118       Result result = table.increment(firstIncrement);
119       assertEquals(1, result.size());
120       assertEquals(1, Bytes.toLong(result.getValue(CF1_BYTES, CQ1)));
121 
122       // Check if the new cell can be read
123       Get get = new Get(ROW).addColumn(CF1_BYTES, CQ1);
124       result = table.get(get);
125       assertEquals(1, result.size());
126       assertEquals(1, Bytes.toLong(result.getValue(CF1_BYTES, CQ1)));
127 
128       // Increment with TTL
129       Increment secondIncrement = new Increment(ROW).addColumn(CF1_BYTES, CQ1, 1).setTTL(1000)
130         .setACL(USER, PERMS);
131       result = table.increment(secondIncrement);
132 
133       // We should get value 2 here
134       assertEquals(1, result.size());
135       assertEquals(2, Bytes.toLong(result.getValue(CF1_BYTES, CQ1)));
136 
137       // Wait 4s to let the second increment expire
138       Thread.sleep(4000);
139       get = new Get(ROW).addColumn(CF1_BYTES, CQ1);
140       result = table.get(get);
141 
142       // The value should revert to 1
143       assertEquals(1, result.size());
144       assertEquals(1, Bytes.toLong(result.getValue(CF1_BYTES, CQ1)));
145     }
146   }
147 
148   @Test
149   public void testAppendTTLWithACLTag() throws Exception {
150     TableName tableName = TableName.valueOf(name.getMethodName());
151     createTableWithCoprocessor(tableName, ChangeCellWithACLTagObserver.class.getName());
152     try (Table table = connection.getTable(tableName)) {
153       // Append without TTL
154       Append firstAppend = new Append(ROW).add(CF1_BYTES, CQ2, VALUE).setACL(USER, PERMS);
155       Result result = table.append(firstAppend);
156       assertEquals(1, result.size());
157       assertTrue(Bytes.equals(VALUE, result.getValue(CF1_BYTES, CQ2)));
158 
159       // Check if the new cell can be read
160       Get get = new Get(ROW).addColumn(CF1_BYTES, CQ2);
161       result = table.get(get);
162       assertEquals(1, result.size());
163       assertTrue(Bytes.equals(VALUE, result.getValue(CF1_BYTES, CQ2)));
164 
165       // Append with TTL
166       Append secondAppend = new Append(ROW).add(CF1_BYTES, CQ2, VALUE).setTTL(1000)
167         .setACL(USER, PERMS);
168       result = table.append(secondAppend);
169 
170       // We should get "valuevalue""
171       assertEquals(1, result.size());
172       assertTrue(Bytes.equals(VALUE2, result.getValue(CF1_BYTES, CQ2)));
173 
174       // Wait 4s to let the second append expire
175       Thread.sleep(4000);
176       get = new Get(ROW).addColumn(CF1_BYTES, CQ2);
177       result = table.get(get);
178 
179       // The value should revert to "value"
180       assertEquals(1, result.size());
181       assertTrue(Bytes.equals(VALUE, result.getValue(CF1_BYTES, CQ2)));
182     }
183   }
184 
185   private static boolean checkAclTag(byte[] acl, Cell cell) {
186     Iterator<Tag> iter = CellUtil.tagsIterator(cell.getTagsArray(),
187       cell.getTagsOffset(), cell.getTagsLength());
188     while (iter.hasNext()) {
189       Tag tag = iter.next();
190       if (tag.getType() == TagType.ACL_TAG_TYPE) {
191         return Bytes.equals(acl, tag.getValue());
192       }
193     }
194     return false;
195   }
196 
197   public static class ChangeCellWithACLTagObserver extends AccessController {
198 
199     @Override
200     public Cell postMutationBeforeWAL(ObserverContext<RegionCoprocessorEnvironment> ctx,
201         MutationType mutationType, Mutation mutation, Cell oldCell, Cell newCell)
202         throws IOException {
203       Cell result = super.postMutationBeforeWAL(ctx, mutationType, mutation, oldCell, newCell);
204       if (mutation.getACL() != null && !checkAclTag(mutation.getACL(), result)) {
205         throw new DoNotRetryIOException("Unmatched ACL tag.");
206       }
207       return result;
208     }
209   }
210 }