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.coprocessor;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertTrue;
023import static org.mockito.Mockito.mock;
024import static org.mockito.Mockito.when;
025
026import java.io.IOException;
027import java.util.concurrent.atomic.AtomicBoolean;
028import org.apache.hadoop.conf.Configuration;
029import org.apache.hadoop.hbase.CoprocessorEnvironment;
030import org.apache.hadoop.hbase.HBaseClassTestRule;
031import org.apache.hadoop.hbase.HBaseConfiguration;
032import org.apache.hadoop.hbase.TableName;
033import org.apache.hadoop.hbase.client.RegionInfo;
034import org.apache.hadoop.hbase.client.RegionInfoBuilder;
035import org.apache.hadoop.hbase.client.TableDescriptor;
036import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
037import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
038import org.apache.hadoop.hbase.master.MasterServices;
039import org.apache.hadoop.hbase.regionserver.HRegion;
040import org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost;
041import org.apache.hadoop.hbase.regionserver.RegionServerCoprocessorHost;
042import org.apache.hadoop.hbase.regionserver.RegionServerServices;
043import org.apache.hadoop.hbase.testclassification.CoprocessorTests;
044import org.apache.hadoop.hbase.testclassification.SmallTests;
045import org.junit.ClassRule;
046import org.junit.Rule;
047import org.junit.Test;
048import org.junit.experimental.categories.Category;
049import org.junit.rules.ExpectedException;
050
051/**
052 * Tests for global coprocessor loading configuration
053 */
054@Category({CoprocessorTests.class, SmallTests.class})
055public class TestCoprocessorConfiguration {
056
057  @ClassRule
058  public static final HBaseClassTestRule CLASS_RULE =
059      HBaseClassTestRule.forClass(TestCoprocessorConfiguration.class);
060
061  @Rule public ExpectedException thrown = ExpectedException.none();
062
063  private static final Configuration CONF = HBaseConfiguration.create();
064  static {
065    CONF.setStrings(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
066      SystemCoprocessor.class.getName());
067    CONF.setStrings(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY,
068      SystemCoprocessor.class.getName());
069    CONF.setStrings(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
070      SystemCoprocessor.class.getName());
071  }
072  private static final TableName TABLENAME = TableName.valueOf("TestCoprocessorConfiguration");
073  private static final RegionInfo REGIONINFO = RegionInfoBuilder.newBuilder(TABLENAME).build();
074  private static final TableDescriptor TABLEDESC;
075  static {
076    try {
077      TABLEDESC = TableDescriptorBuilder.newBuilder(TABLENAME)
078        .setCoprocessor(TableCoprocessor.class.getName()).build();
079    } catch (IOException e) {
080      throw new RuntimeException(e);
081    }
082  }
083
084  // use atomic types in case coprocessor loading is ever multithreaded, also
085  // so we can mutate them even though they are declared final here
086  private static final AtomicBoolean systemCoprocessorLoaded = new AtomicBoolean();
087  private static final AtomicBoolean tableCoprocessorLoaded = new AtomicBoolean();
088
089  public static class SystemCoprocessor implements MasterCoprocessor, RegionCoprocessor,
090      RegionServerCoprocessor {
091    @Override
092    public void start(CoprocessorEnvironment env) throws IOException {
093      systemCoprocessorLoaded.set(true);
094    }
095
096    @Override
097    public void stop(CoprocessorEnvironment env) throws IOException { }
098  }
099
100  public static class TableCoprocessor implements RegionCoprocessor {
101    @Override
102    public void start(CoprocessorEnvironment env) throws IOException {
103      tableCoprocessorLoaded.set(true);
104    }
105
106    @Override
107    public void stop(CoprocessorEnvironment env) throws IOException { }
108  }
109
110  @Test
111  public void testRegionCoprocessorHostDefaults() throws Exception {
112    Configuration conf = new Configuration(CONF);
113    HRegion region = mock(HRegion.class);
114    when(region.getRegionInfo()).thenReturn(REGIONINFO);
115    when(region.getTableDescriptor()).thenReturn(TABLEDESC);
116    RegionServerServices rsServices = mock(RegionServerServices.class);
117    systemCoprocessorLoaded.set(false);
118    tableCoprocessorLoaded.set(false);
119    new RegionCoprocessorHost(region, rsServices, conf);
120    assertEquals("System coprocessors loading default was not honored",
121        CoprocessorHost.DEFAULT_COPROCESSORS_ENABLED, systemCoprocessorLoaded.get());
122    assertEquals("Table coprocessors loading default was not honored",
123        CoprocessorHost.DEFAULT_COPROCESSORS_ENABLED &&
124        CoprocessorHost.DEFAULT_USER_COPROCESSORS_ENABLED, tableCoprocessorLoaded.get());
125  }
126
127  @Test
128  public void testRegionServerCoprocessorHostDefaults() throws Exception {
129    Configuration conf = new Configuration(CONF);
130    RegionServerServices rsServices = mock(RegionServerServices.class);
131    systemCoprocessorLoaded.set(false);
132    new RegionServerCoprocessorHost(rsServices, conf);
133    assertEquals("System coprocessors loading default was not honored",
134        CoprocessorHost.DEFAULT_COPROCESSORS_ENABLED, systemCoprocessorLoaded.get());
135  }
136
137  @Test
138  public void testMasterCoprocessorHostDefaults() throws Exception {
139    Configuration conf = new Configuration(CONF);
140    MasterServices masterServices = mock(MasterServices.class);
141    systemCoprocessorLoaded.set(false);
142    new MasterCoprocessorHost(masterServices, conf);
143    assertEquals("System coprocessors loading default was not honored",
144        CoprocessorHost.DEFAULT_COPROCESSORS_ENABLED, systemCoprocessorLoaded.get());
145  }
146
147  @Test
148  public void testRegionCoprocessorHostAllDisabled() throws Exception {
149    Configuration conf = new Configuration(CONF);
150    conf.setBoolean(CoprocessorHost.COPROCESSORS_ENABLED_CONF_KEY, false);
151    HRegion region = mock(HRegion.class);
152    when(region.getRegionInfo()).thenReturn(REGIONINFO);
153    when(region.getTableDescriptor()).thenReturn(TABLEDESC);
154    RegionServerServices rsServices = mock(RegionServerServices.class);
155    systemCoprocessorLoaded.set(false);
156    tableCoprocessorLoaded.set(false);
157    new RegionCoprocessorHost(region, rsServices, conf);
158    assertFalse("System coprocessors should not have been loaded",
159      systemCoprocessorLoaded.get());
160    assertFalse("Table coprocessors should not have been loaded",
161      tableCoprocessorLoaded.get());
162  }
163
164  @Test
165  public void testRegionCoprocessorHostTableLoadingDisabled() throws Exception {
166    Configuration conf = new Configuration(CONF);
167    conf.setBoolean(CoprocessorHost.COPROCESSORS_ENABLED_CONF_KEY, true); // if defaults change
168    conf.setBoolean(CoprocessorHost.USER_COPROCESSORS_ENABLED_CONF_KEY, false);
169    HRegion region = mock(HRegion.class);
170    when(region.getRegionInfo()).thenReturn(REGIONINFO);
171    when(region.getTableDescriptor()).thenReturn(TABLEDESC);
172    RegionServerServices rsServices = mock(RegionServerServices.class);
173    systemCoprocessorLoaded.set(false);
174    tableCoprocessorLoaded.set(false);
175    new RegionCoprocessorHost(region, rsServices, conf);
176    assertTrue("System coprocessors should have been loaded",
177      systemCoprocessorLoaded.get());
178    assertFalse("Table coprocessors should not have been loaded",
179      tableCoprocessorLoaded.get());
180  }
181
182  /**
183   * Rough test that Coprocessor Environment is Read-Only.
184   * Just check a random CP and see that it returns a read-only config.
185   */
186  @Test
187  public void testReadOnlyConfiguration() throws Exception {
188    Configuration conf = new Configuration(CONF);
189    HRegion region = mock(HRegion.class);
190    when(region.getRegionInfo()).thenReturn(REGIONINFO);
191    when(region.getTableDescriptor()).thenReturn(TABLEDESC);
192    RegionServerServices rsServices = mock(RegionServerServices.class);
193    RegionCoprocessorHost rcp = new RegionCoprocessorHost(region, rsServices, conf);
194    boolean found = false;
195    for (String cpStr: rcp.getCoprocessors()) {
196      CoprocessorEnvironment cpenv = rcp.findCoprocessorEnvironment(cpStr);
197      if (cpenv != null) {
198        found = true;
199      }
200      Configuration c = cpenv.getConfiguration();
201      thrown.expect(UnsupportedOperationException.class);
202      c.set("one.two.three", "four.five.six");
203    }
204    assertTrue("Should be at least one CP found", found);
205  }
206}