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
062  public ExpectedException thrown = ExpectedException.none();
063
064  private static final Configuration CONF = HBaseConfiguration.create();
065  static {
066    CONF.setStrings(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, SystemCoprocessor.class.getName());
067    CONF.setStrings(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY,
068      SystemCoprocessor.class.getName());
069    CONF.setStrings(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, SystemCoprocessor.class.getName());
070  }
071  private static final TableName TABLENAME = TableName.valueOf("TestCoprocessorConfiguration");
072  private static final RegionInfo REGIONINFO = RegionInfoBuilder.newBuilder(TABLENAME).build();
073  private static final TableDescriptor TABLEDESC;
074  static {
075    try {
076      TABLEDESC = TableDescriptorBuilder.newBuilder(TABLENAME)
077        .setCoprocessor(TableCoprocessor.class.getName()).build();
078    } catch (IOException e) {
079      throw new RuntimeException(e);
080    }
081  }
082
083  // use atomic types in case coprocessor loading is ever multithreaded, also
084  // so we can mutate them even though they are declared final here
085  private static final AtomicBoolean systemCoprocessorLoaded = new AtomicBoolean();
086  private static final AtomicBoolean tableCoprocessorLoaded = new AtomicBoolean();
087
088  public static class SystemCoprocessor
089    implements MasterCoprocessor, RegionCoprocessor, RegionServerCoprocessor {
090    @Override
091    public void start(CoprocessorEnvironment env) throws IOException {
092      systemCoprocessorLoaded.set(true);
093    }
094
095    @Override
096    public void stop(CoprocessorEnvironment env) throws IOException {
097    }
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
111  @Test
112  public void testRegionCoprocessorHostDefaults() throws Exception {
113    Configuration conf = new Configuration(CONF);
114    HRegion region = mock(HRegion.class);
115    when(region.getRegionInfo()).thenReturn(REGIONINFO);
116    when(region.getTableDescriptor()).thenReturn(TABLEDESC);
117    RegionServerServices rsServices = mock(RegionServerServices.class);
118    systemCoprocessorLoaded.set(false);
119    tableCoprocessorLoaded.set(false);
120    new RegionCoprocessorHost(region, rsServices, conf);
121    assertEquals("System coprocessors loading default was not honored",
122      CoprocessorHost.DEFAULT_COPROCESSORS_ENABLED, systemCoprocessorLoaded.get());
123    assertEquals("Table coprocessors loading default was not honored",
124      CoprocessorHost.DEFAULT_COPROCESSORS_ENABLED
125        && CoprocessorHost.DEFAULT_USER_COPROCESSORS_ENABLED,
126      tableCoprocessorLoaded.get());
127  }
128
129  @Test
130  public void testRegionServerCoprocessorHostDefaults() throws Exception {
131    Configuration conf = new Configuration(CONF);
132    RegionServerServices rsServices = mock(RegionServerServices.class);
133    systemCoprocessorLoaded.set(false);
134    new RegionServerCoprocessorHost(rsServices, conf);
135    assertEquals("System coprocessors loading default was not honored",
136      CoprocessorHost.DEFAULT_COPROCESSORS_ENABLED, systemCoprocessorLoaded.get());
137  }
138
139  @Test
140  public void testMasterCoprocessorHostDefaults() throws Exception {
141    Configuration conf = new Configuration(CONF);
142    MasterServices masterServices = mock(MasterServices.class);
143    systemCoprocessorLoaded.set(false);
144    new MasterCoprocessorHost(masterServices, conf);
145    assertEquals("System coprocessors loading default was not honored",
146      CoprocessorHost.DEFAULT_COPROCESSORS_ENABLED, systemCoprocessorLoaded.get());
147  }
148
149  @Test
150  public void testRegionCoprocessorHostAllDisabled() throws Exception {
151    Configuration conf = new Configuration(CONF);
152    conf.setBoolean(CoprocessorHost.COPROCESSORS_ENABLED_CONF_KEY, false);
153    HRegion region = mock(HRegion.class);
154    when(region.getRegionInfo()).thenReturn(REGIONINFO);
155    when(region.getTableDescriptor()).thenReturn(TABLEDESC);
156    RegionServerServices rsServices = mock(RegionServerServices.class);
157    systemCoprocessorLoaded.set(false);
158    tableCoprocessorLoaded.set(false);
159    new RegionCoprocessorHost(region, rsServices, conf);
160    assertFalse("System coprocessors should not have been loaded", systemCoprocessorLoaded.get());
161    assertFalse("Table coprocessors should not have been loaded", 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", systemCoprocessorLoaded.get());
177    assertFalse("Table coprocessors should not have been loaded", tableCoprocessorLoaded.get());
178  }
179
180  /**
181   * Rough test that Coprocessor Environment is Read-Only. Just check a random CP and see that it
182   * returns a read-only config.
183   */
184  @Test
185  public void testReadOnlyConfiguration() throws Exception {
186    Configuration conf = new Configuration(CONF);
187    HRegion region = mock(HRegion.class);
188    when(region.getRegionInfo()).thenReturn(REGIONINFO);
189    when(region.getTableDescriptor()).thenReturn(TABLEDESC);
190    RegionServerServices rsServices = mock(RegionServerServices.class);
191    RegionCoprocessorHost rcp = new RegionCoprocessorHost(region, rsServices, conf);
192    boolean found = false;
193    for (String cpStr : rcp.getCoprocessors()) {
194      CoprocessorEnvironment cpenv = rcp.findCoprocessorEnvironment(cpStr);
195      if (cpenv != null) {
196        found = true;
197      }
198      Configuration c = cpenv.getConfiguration();
199      thrown.expect(UnsupportedOperationException.class);
200      c.set("one.two.three", "four.five.six");
201    }
202    assertTrue("Should be at least one CP found", found);
203  }
204}