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}