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.zookeeper;
019
020import static org.junit.jupiter.api.Assertions.assertEquals;
021import static org.junit.jupiter.api.Assertions.assertFalse;
022import static org.junit.jupiter.api.Assertions.assertNull;
023import static org.junit.jupiter.api.Assertions.assertTrue;
024
025import java.util.List;
026import java.util.concurrent.Semaphore;
027import org.apache.hadoop.hbase.HBaseZKTestingUtil;
028import org.apache.hadoop.hbase.ServerName;
029import org.apache.hadoop.hbase.testclassification.MediumTests;
030import org.apache.hadoop.hbase.testclassification.ZKTests;
031import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
032import org.junit.jupiter.api.AfterAll;
033import org.junit.jupiter.api.AfterEach;
034import org.junit.jupiter.api.BeforeAll;
035import org.junit.jupiter.api.BeforeEach;
036import org.junit.jupiter.api.Tag;
037import org.junit.jupiter.api.Test;
038import org.junit.jupiter.api.TestInfo;
039import org.slf4j.Logger;
040import org.slf4j.LoggerFactory;
041
042@Tag(ZKTests.TAG)
043@Tag(MediumTests.TAG)
044public class TestMasterAddressTracker {
045
046  private static final Logger LOG = LoggerFactory.getLogger(TestMasterAddressTracker.class);
047
048  private final static HBaseZKTestingUtil TEST_UTIL = new HBaseZKTestingUtil();
049
050  // Cleaned up after each unit test.
051  private ZKWatcher zk;
052
053  private String methodName;
054
055  @BeforeEach
056  public void setUp(TestInfo testInfo) throws Exception {
057    methodName = testInfo.getTestMethod().get().getName();
058  }
059
060  @AfterEach
061  public void cleanUp() {
062    if (zk != null) {
063      zk.close();
064    }
065  }
066
067  @BeforeAll
068  public static void setUpBeforeClass() throws Exception {
069    TEST_UTIL.startMiniZKCluster();
070  }
071
072  @AfterAll
073  public static void tearDownAfterClass() throws Exception {
074    TEST_UTIL.shutdownMiniZKCluster();
075  }
076
077  @Test
078  public void testDeleteIfEquals() throws Exception {
079    final ServerName sn =
080      ServerName.valueOf("localhost", 1234, EnvironmentEdgeManager.currentTime());
081    final MasterAddressTracker addressTracker = setupMasterTracker(sn, 1772);
082    try {
083      assertFalse(
084        MasterAddressTracker.deleteIfEquals(addressTracker.getWatcher(), "some other string."),
085        "shouldn't have deleted wrong master server.");
086    } finally {
087      assertTrue(MasterAddressTracker.deleteIfEquals(addressTracker.getWatcher(), sn.toString()),
088        "Couldn't clean up master");
089    }
090  }
091
092  /**
093   * create an address tracker instance
094   * @param sn       if not-null set the active master
095   * @param infoPort if there is an active master, set its info port.
096   */
097  private MasterAddressTracker setupMasterTracker(final ServerName sn, final int infoPort)
098    throws Exception {
099    zk = new ZKWatcher(TEST_UTIL.getConfiguration(), methodName, null);
100    ZKUtil.createAndFailSilent(zk, zk.getZNodePaths().baseZNode);
101    ZKUtil.createAndFailSilent(zk, zk.getZNodePaths().backupMasterAddressesZNode);
102
103    // Should not have a master yet
104    MasterAddressTracker addressTracker = new MasterAddressTracker(zk, null);
105    addressTracker.start();
106    assertFalse(addressTracker.hasMaster());
107    zk.registerListener(addressTracker);
108
109    // Use a listener to capture when the node is actually created
110    NodeCreationListener listener =
111      new NodeCreationListener(zk, zk.getZNodePaths().masterAddressZNode);
112    zk.registerListener(listener);
113
114    if (sn != null) {
115      LOG.info("Creating master node");
116      MasterAddressTracker.setMasterAddress(zk, zk.getZNodePaths().masterAddressZNode, sn,
117        infoPort);
118
119      // Wait for the node to be created
120      LOG.info("Waiting for master address manager to be notified");
121      listener.waitForCreation();
122      LOG.info("Master node created");
123    }
124    return addressTracker;
125  }
126
127  /**
128   * Unit tests that uses ZooKeeper but does not use the master-side methods but rather acts
129   * directly on ZK.
130   */
131  @Test
132  public void testMasterAddressTrackerFromZK() throws Exception {
133    // Create the master node with a dummy address
134    final int infoPort = 1235;
135    final ServerName sn =
136      ServerName.valueOf("localhost", 1234, EnvironmentEdgeManager.currentTime());
137    final MasterAddressTracker addressTracker = setupMasterTracker(sn, infoPort);
138    try {
139      assertTrue(addressTracker.hasMaster());
140      ServerName pulledAddress = addressTracker.getMasterAddress();
141      assertEquals(pulledAddress, sn);
142      assertEquals(infoPort, addressTracker.getMasterInfoPort());
143    } finally {
144      assertTrue(MasterAddressTracker.deleteIfEquals(addressTracker.getWatcher(), sn.toString()),
145        "Couldn't clean up master");
146    }
147  }
148
149  @Test
150  public void testParsingNull() throws Exception {
151    assertNull(MasterAddressTracker.parse(null), "parse on null data should return null.");
152  }
153
154  @Test
155  public void testNoBackups() throws Exception {
156    final ServerName sn =
157      ServerName.valueOf("localhost", 1234, EnvironmentEdgeManager.currentTime());
158    final MasterAddressTracker addressTracker = setupMasterTracker(sn, 1772);
159    try {
160      assertEquals(0, addressTracker.getBackupMasterInfoPort(
161        ServerName.valueOf("doesnotexist.example.com", 1234, EnvironmentEdgeManager.currentTime())),
162        "Should receive 0 for backup not found.");
163    } finally {
164      assertTrue(MasterAddressTracker.deleteIfEquals(addressTracker.getWatcher(), sn.toString()),
165        "Couldn't clean up master");
166    }
167  }
168
169  @Test
170  public void testNoMaster() throws Exception {
171    final MasterAddressTracker addressTracker = setupMasterTracker(null, 1772);
172    assertFalse(addressTracker.hasMaster());
173    assertNull(addressTracker.getMasterAddress(), "should get null master when none active.");
174    assertEquals(0, addressTracker.getMasterInfoPort(), "Should receive 0 for backup not found.");
175  }
176
177  @Test
178  public void testBackupMasters() throws Exception {
179    final ServerName sn =
180      ServerName.valueOf("localhost", 5678, EnvironmentEdgeManager.currentTime());
181    final MasterAddressTracker addressTracker = setupMasterTracker(sn, 1111);
182    assertTrue(addressTracker.hasMaster());
183    ServerName activeMaster = addressTracker.getMasterAddress();
184    assertEquals(activeMaster, sn);
185    // No current backup masters
186    List<ServerName> backupMasters = addressTracker.getBackupMasters();
187    assertEquals(0, backupMasters.size());
188    ServerName backupMaster1 = ServerName.valueOf("localhost", 2222, -1);
189    ServerName backupMaster2 = ServerName.valueOf("localhost", 3333, -1);
190    String backupZNode1 =
191      ZNodePaths.joinZNode(zk.getZNodePaths().backupMasterAddressesZNode, backupMaster1.toString());
192    String backupZNode2 =
193      ZNodePaths.joinZNode(zk.getZNodePaths().backupMasterAddressesZNode, backupMaster2.toString());
194    // Add backup masters
195    MasterAddressTracker.setMasterAddress(zk, backupZNode1, backupMaster1, 2222);
196    MasterAddressTracker.setMasterAddress(zk, backupZNode2, backupMaster2, 3333);
197    TEST_UTIL.waitFor(30000, () -> addressTracker.getBackupMasters().size() == 2);
198    backupMasters = addressTracker.getBackupMasters();
199    assertEquals(2, backupMasters.size());
200    assertTrue(backupMasters.contains(backupMaster1));
201    assertTrue(backupMasters.contains(backupMaster2));
202  }
203
204  public static class NodeCreationListener extends ZKListener {
205    private static final Logger LOG = LoggerFactory.getLogger(NodeCreationListener.class);
206
207    private Semaphore lock;
208    private String node;
209
210    public NodeCreationListener(ZKWatcher watcher, String node) {
211      super(watcher);
212      lock = new Semaphore(0);
213      this.node = node;
214    }
215
216    @Override
217    public void nodeCreated(String path) {
218      if (path.equals(node)) {
219        LOG.debug("nodeCreated(" + path + ")");
220        lock.release();
221      }
222    }
223
224    public void waitForCreation() throws InterruptedException {
225      lock.acquire();
226    }
227  }
228}