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.assertNotNull;
022import static org.junit.Assert.assertNull;
023import static org.junit.Assert.assertTrue;
024import static org.junit.Assert.fail;
025
026import java.io.IOException;
027import java.util.List;
028import org.apache.hadoop.conf.Configuration;
029import org.apache.hadoop.hbase.Abortable;
030import org.apache.hadoop.hbase.HBaseClassTestRule;
031import org.apache.hadoop.hbase.HBaseZKTestingUtility;
032import org.apache.hadoop.hbase.testclassification.MediumTests;
033import org.apache.hadoop.hbase.testclassification.ZKTests;
034import org.apache.hadoop.hbase.util.Bytes;
035import org.apache.hadoop.hbase.util.Threads;
036import org.apache.hadoop.hbase.zookeeper.ZKUtil.ZKUtilOp;
037import org.apache.zookeeper.CreateMode;
038import org.apache.zookeeper.KeeperException;
039import org.apache.zookeeper.ZooDefs;
040import org.apache.zookeeper.ZooKeeper;
041import org.apache.zookeeper.data.ACL;
042import org.apache.zookeeper.data.Stat;
043import org.junit.AfterClass;
044import org.junit.BeforeClass;
045import org.junit.ClassRule;
046import org.junit.Test;
047import org.junit.experimental.categories.Category;
048import org.slf4j.Logger;
049import org.slf4j.LoggerFactory;
050
051import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableList;
052import org.apache.hbase.thirdparty.com.google.common.io.Closeables;
053
054@Category({ ZKTests.class, MediumTests.class })
055public class TestZKUtil {
056
057  @ClassRule
058  public static final HBaseClassTestRule CLASS_RULE =
059      HBaseClassTestRule.forClass(TestZKUtil.class);
060
061  private static final Logger LOG = LoggerFactory.getLogger(TestZKUtil.class);
062
063  private static HBaseZKTestingUtility UTIL = new HBaseZKTestingUtility();
064
065  private static ZKWatcher ZKW;
066
067  @BeforeClass
068  public static void setUp() throws Exception {
069    UTIL.startMiniZKCluster().getClientPort();
070    ZKW = new ZKWatcher(new Configuration(UTIL.getConfiguration()), TestZKUtil.class.getName(),
071        new WarnOnlyAbortable());
072
073  }
074
075  @AfterClass
076  public static void tearDown() throws IOException {
077    Closeables.close(ZKW, true);
078    UTIL.shutdownMiniZKCluster();
079    UTIL.cleanupTestDir();
080  }
081
082  /**
083   * Create a znode with data
084   */
085  @Test
086  public void testCreateWithParents() throws KeeperException, InterruptedException {
087    byte[] expectedData = new byte[] { 1, 2, 3 };
088    ZKUtil.createWithParents(ZKW, "/l1/l2/l3/l4/testCreateWithParents", expectedData);
089    byte[] data = ZKUtil.getData(ZKW, "/l1/l2/l3/l4/testCreateWithParents");
090    assertTrue(Bytes.equals(expectedData, data));
091    ZKUtil.deleteNodeRecursively(ZKW, "/l1");
092
093    ZKUtil.createWithParents(ZKW, "/testCreateWithParents", expectedData);
094    data = ZKUtil.getData(ZKW, "/testCreateWithParents");
095    assertTrue(Bytes.equals(expectedData, data));
096    ZKUtil.deleteNodeRecursively(ZKW, "/testCreateWithParents");
097  }
098
099  /**
100   * Create a bunch of znodes in a hierarchy, try deleting one that has childs (it will fail), then
101   * delete it recursively, then delete the last znode
102   */
103  @Test
104  public void testZNodeDeletes() throws Exception {
105    ZKUtil.createWithParents(ZKW, "/l1/l2/l3/l4");
106    try {
107      ZKUtil.deleteNode(ZKW, "/l1/l2");
108      fail("We should not be able to delete if znode has childs");
109    } catch (KeeperException ex) {
110      assertNotNull(ZKUtil.getDataNoWatch(ZKW, "/l1/l2/l3/l4", null));
111    }
112    ZKUtil.deleteNodeRecursively(ZKW, "/l1/l2");
113    // make sure it really is deleted
114    assertNull(ZKUtil.getDataNoWatch(ZKW, "/l1/l2/l3/l4", null));
115
116    // do the same delete again and make sure it doesn't crash
117    ZKUtil.deleteNodeRecursively(ZKW, "/l1/l2");
118
119    ZKUtil.deleteNode(ZKW, "/l1");
120    assertNull(ZKUtil.getDataNoWatch(ZKW, "/l1/l2", null));
121  }
122
123  private int getZNodeDataVersion(String znode) throws KeeperException {
124    Stat stat = new Stat();
125    ZKUtil.getDataNoWatch(ZKW, znode, stat);
126    return stat.getVersion();
127  }
128
129  @Test
130  public void testSetDataWithVersion() throws Exception {
131    ZKUtil.createWithParents(ZKW, "/s1/s2/s3");
132    int v0 = getZNodeDataVersion("/s1/s2/s3");
133    assertEquals(0, v0);
134
135    ZKUtil.setData(ZKW, "/s1/s2/s3", Bytes.toBytes(12L));
136    int v1 = getZNodeDataVersion("/s1/s2/s3");
137    assertEquals(1, v1);
138
139    ZKUtil.multiOrSequential(ZKW,
140      ImmutableList.of(ZKUtilOp.setData("/s1/s2/s3", Bytes.toBytes(13L), v1)), false);
141    int v2 = getZNodeDataVersion("/s1/s2/s3");
142    assertEquals(2, v2);
143  }
144
145  /**
146   * A test for HBASE-3238
147   * @throws IOException A connection attempt to zk failed
148   * @throws InterruptedException One of the non ZKUtil actions was interrupted
149   * @throws KeeperException Any of the zookeeper connections had a KeeperException
150   */
151  @Test
152  public void testCreateSilentIsReallySilent()
153      throws InterruptedException, KeeperException, IOException {
154    Configuration c = UTIL.getConfiguration();
155
156    String aclZnode = "/aclRoot";
157    String quorumServers = ZKConfig.getZKQuorumServersString(c);
158    int sessionTimeout = 5 * 1000; // 5 seconds
159    ZooKeeper zk = new ZooKeeper(quorumServers, sessionTimeout, EmptyWatcher.instance);
160    zk.addAuthInfo("digest", "hbase:rox".getBytes());
161
162    // Save the previous ACL
163    Stat s = null;
164    List<ACL> oldACL = null;
165    while (true) {
166      try {
167        s = new Stat();
168        oldACL = zk.getACL("/", s);
169        break;
170      } catch (KeeperException e) {
171        switch (e.code()) {
172          case CONNECTIONLOSS:
173          case SESSIONEXPIRED:
174          case OPERATIONTIMEOUT:
175            LOG.warn("Possibly transient ZooKeeper exception", e);
176            Threads.sleep(100);
177            break;
178          default:
179            throw e;
180        }
181      }
182    }
183
184    // I set this acl after the attempted creation of the cluster home node.
185    // Add retries in case of retryable zk exceptions.
186    while (true) {
187      try {
188        zk.setACL("/", ZooDefs.Ids.CREATOR_ALL_ACL, -1);
189        break;
190      } catch (KeeperException e) {
191        switch (e.code()) {
192          case CONNECTIONLOSS:
193          case SESSIONEXPIRED:
194          case OPERATIONTIMEOUT:
195            LOG.warn("Possibly transient ZooKeeper exception: " + e);
196            Threads.sleep(100);
197            break;
198          default:
199            throw e;
200        }
201      }
202    }
203
204    while (true) {
205      try {
206        zk.create(aclZnode, null, ZooDefs.Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT);
207        break;
208      } catch (KeeperException e) {
209        switch (e.code()) {
210          case CONNECTIONLOSS:
211          case SESSIONEXPIRED:
212          case OPERATIONTIMEOUT:
213            LOG.warn("Possibly transient ZooKeeper exception: " + e);
214            Threads.sleep(100);
215            break;
216          default:
217            throw e;
218        }
219      }
220    }
221    zk.close();
222    ZKUtil.createAndFailSilent(ZKW, aclZnode);
223
224    // Restore the ACL
225    ZooKeeper zk3 = new ZooKeeper(quorumServers, sessionTimeout, EmptyWatcher.instance);
226    zk3.addAuthInfo("digest", "hbase:rox".getBytes());
227    try {
228      zk3.setACL("/", oldACL, -1);
229    } finally {
230      zk3.close();
231    }
232  }
233
234  /**
235   * Test should not fail with NPE when getChildDataAndWatchForNewChildren invoked with wrongNode
236   */
237  @Test
238  @SuppressWarnings("deprecation")
239  public void testGetChildDataAndWatchForNewChildrenShouldNotThrowNPE() throws Exception {
240    ZKUtil.getChildDataAndWatchForNewChildren(ZKW, "/wrongNode");
241  }
242
243  private static class WarnOnlyAbortable implements Abortable {
244
245    @Override
246    public void abort(String why, Throwable e) {
247      LOG.warn("ZKWatcher received abort, ignoring.  Reason: " + why);
248      if (LOG.isDebugEnabled()) {
249        LOG.debug(e.toString(), e);
250      }
251    }
252
253    @Override
254    public boolean isAborted() {
255      return false;
256    }
257  }
258}