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.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertNull;
023import static org.junit.Assert.assertTrue;
024
025import java.util.List;
026import java.util.concurrent.Semaphore;
027import org.apache.hadoop.hbase.HBaseClassTestRule;
028import org.apache.hadoop.hbase.HBaseZKTestingUtility;
029import org.apache.hadoop.hbase.ServerName;
030import org.apache.hadoop.hbase.testclassification.MediumTests;
031import org.apache.hadoop.hbase.testclassification.ZKTests;
032import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
033import org.junit.After;
034import org.junit.AfterClass;
035import org.junit.BeforeClass;
036import org.junit.ClassRule;
037import org.junit.Rule;
038import org.junit.Test;
039import org.junit.experimental.categories.Category;
040import org.junit.rules.TestName;
041import org.slf4j.Logger;
042import org.slf4j.LoggerFactory;
043
044@Category({ ZKTests.class, MediumTests.class })
045public class TestMasterAddressTracker {
046
047  @ClassRule
048  public static final HBaseClassTestRule CLASS_RULE =
049    HBaseClassTestRule.forClass(TestMasterAddressTracker.class);
050
051  private static final Logger LOG = LoggerFactory.getLogger(TestMasterAddressTracker.class);
052
053  private final static HBaseZKTestingUtility TEST_UTIL = new HBaseZKTestingUtility();
054
055  // Cleaned up after each unit test.
056  private ZKWatcher zk;
057
058  @Rule
059  public TestName name = new TestName();
060
061  @After
062  public void cleanUp() {
063    if (zk != null) {
064      zk.close();
065    }
066  }
067
068  @BeforeClass
069  public static void setUpBeforeClass() throws Exception {
070    TEST_UTIL.startMiniZKCluster();
071  }
072
073  @AfterClass
074  public static void tearDownAfterClass() throws Exception {
075    TEST_UTIL.shutdownMiniZKCluster();
076  }
077
078  @Test
079  public void testDeleteIfEquals() throws Exception {
080    final ServerName sn =
081      ServerName.valueOf("localhost", 1234, EnvironmentEdgeManager.currentTime());
082    final MasterAddressTracker addressTracker = setupMasterTracker(sn, 1772);
083    try {
084      assertFalse("shouldn't have deleted wrong master server.",
085        MasterAddressTracker.deleteIfEquals(addressTracker.getWatcher(), "some other string."));
086    } finally {
087      assertTrue("Couldn't clean up master",
088        MasterAddressTracker.deleteIfEquals(addressTracker.getWatcher(), sn.toString()));
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(), name.getMethodName(), 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      assertTrue(pulledAddress.equals(sn));
142      assertEquals(infoPort, addressTracker.getMasterInfoPort());
143    } finally {
144      assertTrue("Couldn't clean up master",
145        MasterAddressTracker.deleteIfEquals(addressTracker.getWatcher(), sn.toString()));
146    }
147  }
148
149  @Test
150  public void testParsingNull() throws Exception {
151    assertNull("parse on null data should return null.", MasterAddressTracker.parse(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("Should receive 0 for backup not found.", 0,
161        addressTracker.getBackupMasterInfoPort(ServerName.valueOf("doesnotexist.example.com", 1234,
162          EnvironmentEdgeManager.currentTime())));
163    } finally {
164      assertTrue("Couldn't clean up master",
165        MasterAddressTracker.deleteIfEquals(addressTracker.getWatcher(), sn.toString()));
166    }
167  }
168
169  @Test
170  public void testNoMaster() throws Exception {
171    final MasterAddressTracker addressTracker = setupMasterTracker(null, 1772);
172    assertFalse(addressTracker.hasMaster());
173    assertNull("should get null master when none active.", addressTracker.getMasterAddress());
174    assertEquals("Should receive 0 for backup not found.", 0, addressTracker.getMasterInfoPort());
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}