Skip to content

Commit e7d6c6d

Browse files
committed
ZOOKEEPER-2623: Forbid OpCode.check outside OpCode.multi
Individual `OpCode.check` will get `UnimplementedException`. This is a squashed backport of several commits from master: 1. b31f776 (#1988) 2. dc99bd7 (#2067) 3. 66f9cc3 (#2104)
1 parent 4d68e3e commit e7d6c6d

File tree

4 files changed

+161
-1
lines changed

4 files changed

+161
-1
lines changed

zookeeper-server/src/main/java/org/apache/zookeeper/server/Request.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,8 +295,8 @@ static boolean isValid(int type) {
295295
// make sure this is always synchronized with Zoodefs!!
296296
switch (type) {
297297
case OpCode.notification:
298-
return false;
299298
case OpCode.check:
299+
return false;
300300
case OpCode.closeSession:
301301
case OpCode.create:
302302
case OpCode.create2:
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.zookeeper.test;
20+
21+
import static org.junit.jupiter.api.Assertions.assertThrows;
22+
import java.io.File;
23+
import org.apache.zookeeper.KeeperException;
24+
import org.apache.zookeeper.TestableZooKeeper;
25+
import org.apache.zookeeper.ZooDefs;
26+
import org.apache.zookeeper.client.ZKClientConfig;
27+
import org.apache.zookeeper.data.Stat;
28+
import org.apache.zookeeper.proto.CheckVersionRequest;
29+
import org.apache.zookeeper.proto.ReplyHeader;
30+
import org.apache.zookeeper.proto.RequestHeader;
31+
import org.apache.zookeeper.server.ZKDatabase;
32+
import org.apache.zookeeper.server.persistence.FileTxnSnapLog;
33+
import org.apache.zookeeper.server.quorum.QuorumPeer;
34+
import org.junit.jupiter.api.AfterEach;
35+
import org.junit.jupiter.api.BeforeEach;
36+
import org.junit.jupiter.api.Test;
37+
import org.junit.jupiter.api.TestInfo;
38+
39+
public class CheckTest extends ClientBase {
40+
41+
@BeforeEach
42+
public void setUp(TestInfo testInfo) throws Exception {
43+
System.setProperty(ZKClientConfig.ZOOKEEPER_REQUEST_TIMEOUT, "2000");
44+
if (testInfo.getDisplayName().contains("Cluster")) {
45+
return;
46+
}
47+
super.setUp();
48+
}
49+
50+
@AfterEach
51+
public void tearDown(TestInfo testInfo) throws Exception {
52+
System.clearProperty(ZKClientConfig.ZOOKEEPER_REQUEST_TIMEOUT);
53+
if (testInfo.getDisplayName().contains("Cluster")) {
54+
return;
55+
}
56+
super.tearDown();
57+
}
58+
59+
@Override
60+
public void setUp() throws Exception {
61+
}
62+
63+
@Override
64+
public void tearDown() throws Exception {
65+
}
66+
67+
private static void checkVersion(TestableZooKeeper zk, String path, int version) throws Exception {
68+
RequestHeader header = new RequestHeader();
69+
header.setType(ZooDefs.OpCode.check);
70+
CheckVersionRequest request = new CheckVersionRequest(path, version);
71+
ReplyHeader replyHeader = zk.submitRequest(header, request, null, null);
72+
if (replyHeader.getErr() != 0) {
73+
throw KeeperException.create(KeeperException.Code.get(replyHeader.getErr()), path);
74+
}
75+
}
76+
77+
private void testOperations(TestableZooKeeper zk) throws Exception {
78+
Stat stat = new Stat();
79+
zk.getData("/", false, stat);
80+
assertThrows(KeeperException.UnimplementedException.class, () -> checkVersion(zk, "/", -1));
81+
}
82+
83+
@Test
84+
public void testStandalone() throws Exception {
85+
testOperations(createClient());
86+
}
87+
88+
@Test
89+
public void testStandaloneDatabaseReloadAfterCheck() throws Exception {
90+
try {
91+
testOperations(createClient());
92+
} catch (Throwable ignored) {
93+
// Ignore to test database reload after check
94+
}
95+
stopServer();
96+
startServer();
97+
}
98+
99+
@Test
100+
public void testCluster() throws Exception {
101+
QuorumBase qb = new QuorumBase();
102+
try {
103+
qb.setUp(true, true);
104+
testOperations(qb.createClient(new CountdownWatcher(), QuorumPeer.ServerState.OBSERVING));
105+
testOperations(qb.createClient(new CountdownWatcher(), QuorumPeer.ServerState.FOLLOWING));
106+
testOperations(qb.createClient(new CountdownWatcher(), QuorumPeer.ServerState.LEADING));
107+
} finally {
108+
try {
109+
qb.tearDown();
110+
} catch (Exception ignored) {}
111+
}
112+
}
113+
114+
@Test
115+
public void testClusterDatabaseReloadAfterCheck() throws Exception {
116+
QuorumBase qb = new QuorumBase();
117+
try {
118+
qb.setUp(true, true);
119+
120+
// Get leader before possible damaging operations to
121+
// reduce chance of leader migration and log truncation.
122+
File dataDir = qb.getLeaderDataDir();
123+
QuorumPeer leader = qb.getLeaderQuorumPeer();
124+
125+
try {
126+
testOperations(qb.createClient(new CountdownWatcher(), QuorumPeer.ServerState.LEADING));
127+
} catch (Throwable ignored) {
128+
// Ignore to test database reload after check
129+
}
130+
qb.shutdown(leader);
131+
132+
FileTxnSnapLog txnSnapLog = new FileTxnSnapLog(dataDir, dataDir);
133+
ZKDatabase database = new ZKDatabase(txnSnapLog);
134+
database.loadDataBase();
135+
} finally {
136+
try {
137+
qb.tearDown();
138+
} catch (Exception ignored) {}
139+
}
140+
}
141+
}

zookeeper-server/src/test/java/org/apache/zookeeper/test/ClientBase.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,10 @@ protected TestableZooKeeper createClient(CountdownWatcher watcher) throws IOExce
197197
private List<ZooKeeper> allClients;
198198
private boolean allClientsSetup = false;
199199

200+
protected TestableZooKeeper createClient(String hp, int timeout) throws IOException, InterruptedException {
201+
return createClient(new CountdownWatcher(), hp, timeout);
202+
}
203+
200204
protected TestableZooKeeper createClient(CountdownWatcher watcher, String hp) throws IOException, InterruptedException {
201205
return createClient(watcher, hp, CONNECTION_TIMEOUT);
202206
}

zookeeper-server/src/test/java/org/apache/zookeeper/test/QuorumBase.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,21 @@ public QuorumPeer getLeaderQuorumPeer() {
342342
return null;
343343
}
344344

345+
public File getLeaderDataDir() {
346+
if (s1.getPeerState() == ServerState.LEADING) {
347+
return s1dir;
348+
} else if (s2.getPeerState() == ServerState.LEADING) {
349+
return s2dir;
350+
} else if (s3.getPeerState() == ServerState.LEADING) {
351+
return s3dir;
352+
} else if (s4.getPeerState() == ServerState.LEADING) {
353+
return s4dir;
354+
} else if (s5.getPeerState() == ServerState.LEADING) {
355+
return s5dir;
356+
}
357+
return null;
358+
}
359+
345360
public QuorumPeer getFirstObserver() {
346361
if (s1.getLearnerType() == LearnerType.OBSERVER) {
347362
return s1;

0 commit comments

Comments
 (0)