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.rsgroup;
019
020import static org.junit.jupiter.api.Assertions.assertTrue;
021
022import java.io.IOException;
023import java.util.ArrayList;
024import java.util.EnumSet;
025import java.util.HashSet;
026import java.util.LinkedList;
027import java.util.List;
028import java.util.Map;
029import java.util.Optional;
030import java.util.Set;
031import java.util.TreeMap;
032import java.util.concurrent.ThreadLocalRandom;
033import java.util.regex.Pattern;
034import org.apache.hadoop.conf.Configuration;
035import org.apache.hadoop.hbase.ClusterMetrics;
036import org.apache.hadoop.hbase.ClusterMetrics.Option;
037import org.apache.hadoop.hbase.HBaseClusterInterface;
038import org.apache.hadoop.hbase.HBaseTestingUtil;
039import org.apache.hadoop.hbase.NamespaceDescriptor;
040import org.apache.hadoop.hbase.ServerName;
041import org.apache.hadoop.hbase.SingleProcessHBaseCluster;
042import org.apache.hadoop.hbase.TableName;
043import org.apache.hadoop.hbase.Waiter;
044import org.apache.hadoop.hbase.client.AbstractTestUpdateConfiguration;
045import org.apache.hadoop.hbase.client.Admin;
046import org.apache.hadoop.hbase.client.BalanceRequest;
047import org.apache.hadoop.hbase.client.BalanceResponse;
048import org.apache.hadoop.hbase.client.RegionInfo;
049import org.apache.hadoop.hbase.client.TableDescriptor;
050import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
051import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor;
052import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
053import org.apache.hadoop.hbase.coprocessor.MasterObserver;
054import org.apache.hadoop.hbase.coprocessor.ObserverContext;
055import org.apache.hadoop.hbase.master.HMaster;
056import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
057import org.apache.hadoop.hbase.master.ServerManager;
058import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
059import org.apache.hadoop.hbase.net.Address;
060import org.apache.hadoop.hbase.quotas.QuotaUtil;
061import org.junit.jupiter.api.TestInfo;
062import org.slf4j.Logger;
063import org.slf4j.LoggerFactory;
064
065import org.apache.hbase.thirdparty.com.google.common.collect.Maps;
066import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
067
068public abstract class TestRSGroupsBase extends AbstractTestUpdateConfiguration {
069  protected static final Logger LOG = LoggerFactory.getLogger(TestRSGroupsBase.class);
070
071  // shared
072  protected static final String GROUP_PREFIX = "Group";
073  protected static final String TABLE_PREFIX = "Group";
074
075  // shared, cluster type specific
076  protected static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
077  protected static Admin ADMIN;
078  protected static HBaseClusterInterface CLUSTER;
079  protected static HMaster MASTER;
080  protected boolean INIT = false;
081  protected static CPMasterObserver OBSERVER;
082
083  public final static long WAIT_TIMEOUT = 60000;
084  public final static int NUM_SLAVES_BASE = 4; // number of slaves for the smallest cluster
085  public static int NUM_DEAD_SERVERS = 0;
086
087  protected static final class MethodName {
088    private String methodName;
089
090    public String getMethodName() {
091      return methodName;
092    }
093
094    void setMethodName(String methodName) {
095      this.methodName = methodName;
096    }
097  }
098
099  // Per test variables
100  protected final MethodName name = new MethodName();
101  protected TableName tableName;
102
103  public static String getNameWithoutIndex(String name) {
104    return name.split("\\[")[0];
105  }
106
107  public static void setUpTestBeforeClass() throws Exception {
108    Configuration conf = TEST_UTIL.getConfiguration();
109    conf.setFloat("hbase.master.balancer.stochastic.tableSkewCost", 6000);
110    if (conf.get(RSGroupUtil.RS_GROUP_ENABLED) == null) {
111      RSGroupUtil.enableRSGroup(conf);
112    }
113    if (conf.get(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY) != null) {
114      conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
115        conf.get(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY) + ","
116          + CPMasterObserver.class.getName());
117    } else {
118      conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, CPMasterObserver.class.getName());
119    }
120
121    conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, NUM_SLAVES_BASE);
122    conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
123    conf.setInt("hbase.rpc.timeout", 100000);
124
125    TEST_UTIL.startMiniCluster(NUM_SLAVES_BASE);
126    initialize();
127  }
128
129  protected static void initialize() throws Exception {
130    ADMIN = new VerifyingRSGroupAdmin(TEST_UTIL.getConfiguration());
131    CLUSTER = TEST_UTIL.getHBaseCluster();
132    MASTER = TEST_UTIL.getMiniHBaseCluster().getMaster();
133
134    // wait for balancer to come online
135    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
136      @Override
137      public boolean evaluate() throws Exception {
138        return MASTER.isInitialized()
139          && ((RSGroupBasedLoadBalancer) MASTER.getLoadBalancer()).isOnline();
140      }
141    });
142    ADMIN.balancerSwitch(false, true);
143    MasterCoprocessorHost host = MASTER.getMasterCoprocessorHost();
144    OBSERVER = (CPMasterObserver) host.findCoprocessor(CPMasterObserver.class.getName());
145  }
146
147  public static void tearDownAfterClass() throws Exception {
148    TEST_UTIL.shutdownMiniCluster();
149  }
150
151  public void setUpBeforeMethod(TestInfo testInfo) throws Exception {
152    name.setMethodName(testInfo.getTestMethod().get().getName());
153    LOG.info(name.getMethodName());
154    tableName = TableName.valueOf(TABLE_PREFIX + "_" + name.getMethodName().split("\\[")[0]);
155    if (!INIT) {
156      INIT = true;
157      tearDownAfterMethod();
158    }
159    OBSERVER.resetFlags();
160  }
161
162  public void tearDownAfterMethod() throws Exception {
163    deleteTableIfNecessary();
164    deleteNamespaceIfNecessary();
165    deleteGroups();
166
167    for (ServerName sn : ADMIN.listDecommissionedRegionServers()) {
168      ADMIN.recommissionRegionServer(sn, null);
169    }
170    assertTrue(ADMIN.listDecommissionedRegionServers().isEmpty());
171
172    int missing = NUM_SLAVES_BASE - getNumServers();
173    LOG.info("Restoring servers: " + missing);
174    for (int i = 0; i < missing; i++) {
175      ((SingleProcessHBaseCluster) CLUSTER).startRegionServer();
176    }
177    ADMIN.addRSGroup("master");
178    ServerName masterServerName = ((SingleProcessHBaseCluster) CLUSTER).getMaster().getServerName();
179    try {
180      ADMIN.moveServersToRSGroup(Sets.newHashSet(masterServerName.getAddress()), "master");
181    } catch (Exception ex) {
182      LOG.warn("Got this on setup, FYI", ex);
183    }
184    assertTrue(OBSERVER.preMoveServersCalled);
185    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
186      @Override
187      public boolean evaluate() throws Exception {
188        LOG.info("Waiting for cleanup to finish " + ADMIN.listRSGroups());
189        // Might be greater since moving servers back to default
190        // is after starting a server
191
192        return ADMIN.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getServers().size() == NUM_SLAVES_BASE;
193      }
194    });
195  }
196
197  protected final RSGroupInfo addGroup(String groupName, int serverCount)
198    throws IOException, InterruptedException {
199    RSGroupInfo defaultInfo = ADMIN.getRSGroup(RSGroupInfo.DEFAULT_GROUP);
200    ADMIN.addRSGroup(groupName);
201    Set<Address> set = new HashSet<>();
202    for (Address server : defaultInfo.getServers()) {
203      if (set.size() == serverCount) {
204        break;
205      }
206      set.add(server);
207    }
208    ADMIN.moveServersToRSGroup(set, groupName);
209    RSGroupInfo result = ADMIN.getRSGroup(groupName);
210    return result;
211  }
212
213  protected final void removeGroup(String groupName) throws IOException {
214    Set<TableName> tables = new HashSet<>();
215    for (TableDescriptor td : ADMIN.listTableDescriptors(true)) {
216      RSGroupInfo groupInfo = ADMIN.getRSGroup(td.getTableName());
217      if (groupInfo != null && groupInfo.getName().equals(groupName)) {
218        tables.add(td.getTableName());
219      }
220    }
221    ADMIN.setRSGroup(tables, RSGroupInfo.DEFAULT_GROUP);
222    for (NamespaceDescriptor nd : ADMIN.listNamespaceDescriptors()) {
223      if (groupName.equals(nd.getConfigurationValue(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP))) {
224        nd.removeConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP);
225        ADMIN.modifyNamespace(nd);
226      }
227    }
228    RSGroupInfo groupInfo = ADMIN.getRSGroup(groupName);
229    ADMIN.moveServersToRSGroup(groupInfo.getServers(), RSGroupInfo.DEFAULT_GROUP);
230    ADMIN.removeRSGroup(groupName);
231  }
232
233  protected final void deleteTableIfNecessary() throws IOException {
234    for (TableDescriptor desc : TEST_UTIL.getAdmin()
235      .listTableDescriptors(Pattern.compile(TABLE_PREFIX + ".*"))) {
236      TEST_UTIL.deleteTable(desc.getTableName());
237    }
238  }
239
240  protected final void deleteNamespaceIfNecessary() throws IOException {
241    for (NamespaceDescriptor desc : TEST_UTIL.getAdmin().listNamespaceDescriptors()) {
242      if (desc.getName().startsWith(TABLE_PREFIX)) {
243        ADMIN.deleteNamespace(desc.getName());
244      }
245    }
246  }
247
248  protected final void deleteGroups() throws IOException {
249    for (RSGroupInfo groupInfo : ADMIN.listRSGroups()) {
250      if (!groupInfo.getName().equals(RSGroupInfo.DEFAULT_GROUP)) {
251        removeGroup(groupInfo.getName());
252      }
253    }
254  }
255
256  protected Map<TableName, List<String>> getTableRegionMap() throws IOException {
257    Map<TableName, List<String>> map = Maps.newTreeMap();
258    Map<TableName, Map<ServerName, List<String>>> tableServerRegionMap = getTableServerRegionMap();
259    for (TableName tableName : tableServerRegionMap.keySet()) {
260      if (!map.containsKey(tableName)) {
261        map.put(tableName, new LinkedList<>());
262      }
263      for (List<String> subset : tableServerRegionMap.get(tableName).values()) {
264        map.get(tableName).addAll(subset);
265      }
266    }
267    return map;
268  }
269
270  protected Map<TableName, Map<ServerName, List<String>>> getTableServerRegionMap()
271    throws IOException {
272    Map<TableName, Map<ServerName, List<String>>> map = Maps.newTreeMap();
273    Admin admin = TEST_UTIL.getAdmin();
274    ClusterMetrics metrics =
275      admin.getClusterMetrics(EnumSet.of(ClusterMetrics.Option.SERVERS_NAME));
276    for (ServerName serverName : metrics.getServersName()) {
277      for (RegionInfo region : admin.getRegions(serverName)) {
278        TableName tableName = region.getTable();
279        map.computeIfAbsent(tableName, k -> new TreeMap<>())
280          .computeIfAbsent(serverName, k -> new ArrayList<>()).add(region.getRegionNameAsString());
281      }
282    }
283    return map;
284  }
285
286  // return the real number of region servers, excluding the master embedded region server in 2.0+
287  protected int getNumServers() throws IOException {
288    ClusterMetrics status = ADMIN.getClusterMetrics(EnumSet.of(Option.MASTER, Option.LIVE_SERVERS));
289    ServerName masterName = status.getMasterName();
290    int count = 0;
291    for (ServerName sn : status.getLiveServerMetrics().keySet()) {
292      if (!sn.equals(masterName)) {
293        count++;
294      }
295    }
296    return count;
297  }
298
299  protected final String getGroupName(String baseName) {
300    return GROUP_PREFIX + "_" + getNameWithoutIndex(baseName) + "_"
301      + ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE);
302  }
303
304  /**
305   * The server name in group does not contain the start code, this method will find out the start
306   * code and construct the ServerName object.
307   */
308  protected final ServerName getServerName(Address addr) {
309    return TEST_UTIL.getMiniHBaseCluster().getRegionServerThreads().stream()
310      .map(t -> t.getRegionServer().getServerName()).filter(sn -> sn.getAddress().equals(addr))
311      .findFirst().get();
312  }
313
314  protected final void toggleQuotaCheckAndRestartMiniCluster(boolean enable) throws Exception {
315    TEST_UTIL.shutdownMiniCluster();
316    TEST_UTIL.getConfiguration().setBoolean(QuotaUtil.QUOTA_CONF_KEY, enable);
317    TEST_UTIL.startMiniCluster(NUM_SLAVES_BASE);
318    TEST_UTIL.getConfiguration().setInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART,
319      NUM_SLAVES_BASE);
320    TEST_UTIL.getConfiguration().setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
321    initialize();
322  }
323
324  public static class CPMasterObserver implements MasterCoprocessor, MasterObserver {
325    boolean preBalanceRSGroupCalled = false;
326    boolean postBalanceRSGroupCalled = false;
327    boolean preMoveServersCalled = false;
328    boolean postMoveServersCalled = false;
329    boolean preMoveTablesCalled = false;
330    boolean postMoveTablesCalled = false;
331    boolean preAddRSGroupCalled = false;
332    boolean postAddRSGroupCalled = false;
333    boolean preRemoveRSGroupCalled = false;
334    boolean postRemoveRSGroupCalled = false;
335    boolean preRemoveServersCalled = false;
336    boolean postRemoveServersCalled = false;
337    boolean preMoveServersAndTables = false;
338    boolean postMoveServersAndTables = false;
339    boolean preGetRSGroupInfoCalled = false;
340    boolean postGetRSGroupInfoCalled = false;
341    boolean preGetRSGroupInfoOfTableCalled = false;
342    boolean postGetRSGroupInfoOfTableCalled = false;
343    boolean preListRSGroupsCalled = false;
344    boolean postListRSGroupsCalled = false;
345    boolean preGetRSGroupInfoOfServerCalled = false;
346    boolean postGetRSGroupInfoOfServerCalled = false;
347    boolean preSetRSGroupForTablesCalled = false;
348    boolean postSetRSGroupForTablesCalled = false;
349    boolean preListTablesInRSGroupCalled = false;
350    boolean postListTablesInRSGroupCalled = false;
351    boolean preGetConfiguredNamespacesAndTablesInRSGroupCalled = false;
352    boolean postGetConfiguredNamespacesAndTablesInRSGroupCalled = false;
353    boolean preRenameRSGroup = false;
354    boolean postRenameRSGroup = false;
355    boolean preUpdateRSGroupConfig = false;
356    boolean postUpdateRSGroupConfig = false;
357
358    public void resetFlags() {
359      preBalanceRSGroupCalled = false;
360      postBalanceRSGroupCalled = false;
361      preMoveServersCalled = false;
362      postMoveServersCalled = false;
363      preMoveTablesCalled = false;
364      postMoveTablesCalled = false;
365      preAddRSGroupCalled = false;
366      postAddRSGroupCalled = false;
367      preRemoveRSGroupCalled = false;
368      postRemoveRSGroupCalled = false;
369      preRemoveServersCalled = false;
370      postRemoveServersCalled = false;
371      preMoveServersAndTables = false;
372      postMoveServersAndTables = false;
373      preGetRSGroupInfoCalled = false;
374      postGetRSGroupInfoCalled = false;
375      preGetRSGroupInfoOfTableCalled = false;
376      postGetRSGroupInfoOfTableCalled = false;
377      preListRSGroupsCalled = false;
378      postListRSGroupsCalled = false;
379      preGetRSGroupInfoOfServerCalled = false;
380      postGetRSGroupInfoOfServerCalled = false;
381      preSetRSGroupForTablesCalled = false;
382      postSetRSGroupForTablesCalled = false;
383      preListTablesInRSGroupCalled = false;
384      postListTablesInRSGroupCalled = false;
385      preGetConfiguredNamespacesAndTablesInRSGroupCalled = false;
386      postGetConfiguredNamespacesAndTablesInRSGroupCalled = false;
387      preRenameRSGroup = false;
388      postRenameRSGroup = false;
389      preUpdateRSGroupConfig = false;
390      postUpdateRSGroupConfig = false;
391    }
392
393    @Override
394    public Optional<MasterObserver> getMasterObserver() {
395      return Optional.of(this);
396    }
397
398    @Override
399    public void preMoveServersAndTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
400      Set<Address> servers, Set<TableName> tables, String targetGroup) throws IOException {
401      preMoveServersAndTables = true;
402    }
403
404    @Override
405    public void postMoveServersAndTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
406      Set<Address> servers, Set<TableName> tables, String targetGroup) throws IOException {
407      postMoveServersAndTables = true;
408    }
409
410    @Override
411    public void preRemoveServers(final ObserverContext<MasterCoprocessorEnvironment> ctx,
412      Set<Address> servers) throws IOException {
413      preRemoveServersCalled = true;
414    }
415
416    @Override
417    public void postRemoveServers(final ObserverContext<MasterCoprocessorEnvironment> ctx,
418      Set<Address> servers) throws IOException {
419      postRemoveServersCalled = true;
420    }
421
422    @Override
423    public void preRemoveRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
424      String name) throws IOException {
425      preRemoveRSGroupCalled = true;
426    }
427
428    @Override
429    public void postRemoveRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
430      String name) throws IOException {
431      postRemoveRSGroupCalled = true;
432    }
433
434    @Override
435    public void preAddRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx, String name)
436      throws IOException {
437      preAddRSGroupCalled = true;
438    }
439
440    @Override
441    public void postAddRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx, String name)
442      throws IOException {
443      postAddRSGroupCalled = true;
444    }
445
446    @Override
447    public void preMoveTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
448      Set<TableName> tables, String targetGroup) throws IOException {
449      preMoveTablesCalled = true;
450    }
451
452    @Override
453    public void postMoveTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
454      Set<TableName> tables, String targetGroup) throws IOException {
455      postMoveTablesCalled = true;
456    }
457
458    @Override
459    public void preMoveServers(final ObserverContext<MasterCoprocessorEnvironment> ctx,
460      Set<Address> servers, String targetGroup) throws IOException {
461      preMoveServersCalled = true;
462    }
463
464    @Override
465    public void postMoveServers(final ObserverContext<MasterCoprocessorEnvironment> ctx,
466      Set<Address> servers, String targetGroup) throws IOException {
467      postMoveServersCalled = true;
468    }
469
470    @Override
471    public void preBalanceRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
472      String groupName, BalanceRequest request) throws IOException {
473      preBalanceRSGroupCalled = true;
474    }
475
476    @Override
477    public void postBalanceRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
478      String groupName, BalanceRequest request, BalanceResponse response) throws IOException {
479      postBalanceRSGroupCalled = true;
480    }
481
482    @Override
483    public void preGetRSGroupInfo(final ObserverContext<MasterCoprocessorEnvironment> ctx,
484      final String groupName) throws IOException {
485      preGetRSGroupInfoCalled = true;
486    }
487
488    @Override
489    public void postGetRSGroupInfo(final ObserverContext<MasterCoprocessorEnvironment> ctx,
490      final String groupName) throws IOException {
491      postGetRSGroupInfoCalled = true;
492    }
493
494    @Override
495    public void preGetRSGroupInfoOfTable(final ObserverContext<MasterCoprocessorEnvironment> ctx,
496      final TableName tableName) throws IOException {
497      preGetRSGroupInfoOfTableCalled = true;
498    }
499
500    @Override
501    public void postGetRSGroupInfoOfTable(final ObserverContext<MasterCoprocessorEnvironment> ctx,
502      final TableName tableName) throws IOException {
503      postGetRSGroupInfoOfTableCalled = true;
504    }
505
506    @Override
507    public void preListRSGroups(final ObserverContext<MasterCoprocessorEnvironment> ctx)
508      throws IOException {
509      preListRSGroupsCalled = true;
510    }
511
512    @Override
513    public void postListRSGroups(final ObserverContext<MasterCoprocessorEnvironment> ctx)
514      throws IOException {
515      postListRSGroupsCalled = true;
516    }
517
518    @Override
519    public void preGetRSGroupInfoOfServer(final ObserverContext<MasterCoprocessorEnvironment> ctx,
520      final Address server) throws IOException {
521      preGetRSGroupInfoOfServerCalled = true;
522    }
523
524    @Override
525    public void postGetRSGroupInfoOfServer(final ObserverContext<MasterCoprocessorEnvironment> ctx,
526      final Address server) throws IOException {
527      postGetRSGroupInfoOfServerCalled = true;
528    }
529
530    @Override
531    public void preListTablesInRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx,
532      String groupName) throws IOException {
533      preListTablesInRSGroupCalled = true;
534    }
535
536    @Override
537    public void postListTablesInRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx,
538      String groupName) throws IOException {
539      postListTablesInRSGroupCalled = true;
540    }
541
542    @Override
543    public void preGetConfiguredNamespacesAndTablesInRSGroup(
544      ObserverContext<MasterCoprocessorEnvironment> ctx, String groupName) throws IOException {
545      preGetConfiguredNamespacesAndTablesInRSGroupCalled = true;
546    }
547
548    @Override
549    public void postGetConfiguredNamespacesAndTablesInRSGroup(
550      ObserverContext<MasterCoprocessorEnvironment> ctx, String groupName) throws IOException {
551      postGetConfiguredNamespacesAndTablesInRSGroupCalled = true;
552    }
553
554    @Override
555    public void preRenameRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx, String oldName,
556      String newName) throws IOException {
557      preRenameRSGroup = true;
558    }
559
560    @Override
561    public void postRenameRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx, String oldName,
562      String newName) throws IOException {
563      postRenameRSGroup = true;
564    }
565
566    @Override
567    public void preUpdateRSGroupConfig(final ObserverContext<MasterCoprocessorEnvironment> ctx,
568      final String groupName, final Map<String, String> configuration) throws IOException {
569      preUpdateRSGroupConfig = true;
570    }
571
572    @Override
573    public void postUpdateRSGroupConfig(final ObserverContext<MasterCoprocessorEnvironment> ctx,
574      final String groupName, final Map<String, String> configuration) throws IOException {
575      postUpdateRSGroupConfig = true;
576    }
577  }
578}