2222import static org .junit .Assert .assertNull ;
2323import static org .junit .Assert .assertTrue ;
2424import static org .junit .Assert .fail ;
25+ import static org .mockito .ArgumentMatchers .any ;
26+ import static org .mockito .ArgumentMatchers .anyBoolean ;
27+ import static org .mockito .ArgumentMatchers .anyList ;
28+ import static org .mockito .ArgumentMatchers .anyString ;
29+ import static org .mockito .Mockito .doAnswer ;
30+ import static org .mockito .Mockito .mock ;
31+ import static org .mockito .Mockito .spy ;
32+ import static org .mockito .Mockito .times ;
33+ import static org .mockito .Mockito .verify ;
34+ import static org .mockito .Mockito .when ;
2535
2636import java .io .IOException ;
37+ import java .util .Arrays ;
2738import java .util .List ;
39+ import java .util .concurrent .Callable ;
40+ import java .util .concurrent .atomic .AtomicBoolean ;
2841import org .apache .hadoop .conf .Configuration ;
2942import org .apache .hadoop .hbase .Abortable ;
3043import org .apache .hadoop .hbase .HBaseClassTestRule ;
4558import org .junit .ClassRule ;
4659import org .junit .Test ;
4760import org .junit .experimental .categories .Category ;
61+ import org .mockito .AdditionalAnswers ;
4862import org .slf4j .Logger ;
4963import org .slf4j .LoggerFactory ;
5064
@@ -139,31 +153,10 @@ public void testSetDataWithVersion() throws Exception {
139153 assertEquals (2 , v2 );
140154 }
141155
142- /**
143- * A test for HBASE-3238
144- * @throws IOException A connection attempt to zk failed
145- * @throws InterruptedException One of the non ZKUtil actions was interrupted
146- * @throws KeeperException Any of the zookeeper connections had a KeeperException
147- */
148- @ Test
149- public void testCreateSilentIsReallySilent ()
150- throws InterruptedException , KeeperException , IOException {
151- Configuration c = UTIL .getConfiguration ();
152-
153- String aclZnode = "/aclRoot" ;
154- String quorumServers = ZKConfig .getZKQuorumServersString (c );
155- int sessionTimeout = 5 * 1000 ; // 5 seconds
156- ZooKeeper zk = new ZooKeeper (quorumServers , sessionTimeout , EmptyWatcher .instance );
157- zk .addAuthInfo ("digest" , Bytes .toBytes ("hbase:rox" ));
158-
159- // Save the previous ACL
160- Stat s ;
161- List <ACL > oldACL ;
162- while (true ) {
156+ private <V > V callAndIgnoreTransientError (Callable <V > action ) throws Exception {
157+ for (;;) {
163158 try {
164- s = new Stat ();
165- oldACL = zk .getACL ("/" , s );
166- break ;
159+ return action .call ();
167160 } catch (KeeperException e ) {
168161 switch (e .code ()) {
169162 case CONNECTIONLOSS :
@@ -177,54 +170,54 @@ public void testCreateSilentIsReallySilent()
177170 }
178171 }
179172 }
173+ }
180174
181- // I set this acl after the attempted creation of the cluster home node.
182- // Add retries in case of retryable zk exceptions.
183- while (true ) {
184- try {
185- zk .setACL ("/" , ZooDefs .Ids .CREATOR_ALL_ACL , -1 );
186- break ;
187- } catch (KeeperException e ) {
188- switch (e .code ()) {
189- case CONNECTIONLOSS :
190- case SESSIONEXPIRED :
191- case OPERATIONTIMEOUT :
192- LOG .warn ("Possibly transient ZooKeeper exception: " + e );
193- Threads .sleep (100 );
194- break ;
195- default :
196- throw e ;
197- }
198- }
199- }
175+ /**
176+ * A test for HBASE-3238
177+ */
178+ @ Test
179+ public void testCreateSilentIsReallySilent () throws Exception {
180+ Configuration c = UTIL .getConfiguration ();
200181
201- while (true ) {
202- try {
203- zk .create (aclZnode , null , ZooDefs .Ids .CREATOR_ALL_ACL , CreateMode .PERSISTENT );
204- break ;
205- } catch (KeeperException e ) {
206- switch (e .code ()) {
207- case CONNECTIONLOSS :
208- case SESSIONEXPIRED :
209- case OPERATIONTIMEOUT :
210- LOG .warn ("Possibly transient ZooKeeper exception: " + e );
211- Threads .sleep (100 );
212- break ;
213- default :
214- throw e ;
182+ String aclZnode = "/aclRoot" ;
183+ String quorumServers = ZKConfig .getZKQuorumServersString (c );
184+ int sessionTimeout = 5 * 1000 ; // 5 seconds
185+ try (ZooKeeper zk = new ZooKeeper (quorumServers , sessionTimeout , EmptyWatcher .instance )) {
186+ zk .addAuthInfo ("digest" , Bytes .toBytes ("hbase:rox" ));
187+
188+ // Save the previous ACL
189+ List <ACL > oldACL = callAndIgnoreTransientError (() -> zk .getACL ("/" , new Stat ()));
190+
191+ // I set this acl after the attempted creation of the cluster home node.
192+ // Add retries in case of retryable zk exceptions.
193+ callAndIgnoreTransientError (() -> zk .setACL ("/" , ZooDefs .Ids .CREATOR_ALL_ACL , -1 ));
194+
195+ ZKWatcher watcher = spy (ZKW );
196+ RecoverableZooKeeper rzk = mock (RecoverableZooKeeper .class ,
197+ AdditionalAnswers .delegatesTo (ZKW .getRecoverableZooKeeper ()));
198+ when (watcher .getRecoverableZooKeeper ()).thenReturn (rzk );
199+ AtomicBoolean firstExists = new AtomicBoolean (true );
200+ doAnswer (inv -> {
201+ String path = inv .getArgument (0 );
202+ boolean watch = inv .getArgument (1 );
203+ Stat stat = ZKW .getRecoverableZooKeeper ().exists (path , watch );
204+ // create the znode after first exists check, this is to simulate that we enter the create
205+ // branch but we have no permission for creation, but the znode has been created by others
206+ if (firstExists .compareAndSet (true , false )) {
207+ callAndIgnoreTransientError (() -> zk .create (aclZnode , null ,
208+ Arrays .asList (new ACL (ZooDefs .Perms .READ , ZooDefs .Ids .ANYONE_ID_UNSAFE ),
209+ new ACL (ZooDefs .Perms .ALL , ZooDefs .Ids .AUTH_IDS )),
210+ CreateMode .PERSISTENT ));
215211 }
216- }
217- }
218- zk .close ();
219- ZKUtil .createAndFailSilent (ZKW , aclZnode );
220-
221- // Restore the ACL
222- ZooKeeper zk3 = new ZooKeeper (quorumServers , sessionTimeout , EmptyWatcher .instance );
223- zk3 .addAuthInfo ("digest" , Bytes .toBytes ("hbase:rox" ));
224- try {
225- zk3 .setACL ("/" , oldACL , -1 );
226- } finally {
227- zk3 .close ();
212+ return stat ;
213+ }).when (rzk ).exists (any (), anyBoolean ());
214+ ZKUtil .createAndFailSilent (watcher , aclZnode );
215+ // make sure we call the exists method twice and create once
216+ verify (rzk , times (2 )).exists (any (), anyBoolean ());
217+ verify (rzk ).create (anyString (), any (), anyList (), any ());
218+ // Restore the ACL
219+ zk .addAuthInfo ("digest" , Bytes .toBytes ("hbase:rox" ));
220+ zk .setACL ("/" , oldACL , -1 );
228221 }
229222 }
230223
0 commit comments