Skip to content

Commit 3d73836

Browse files
authored
IGNITE-27010 Implement control.sh status command for rolling upgrade (#12521)
1 parent 4f58392 commit 3d73836

File tree

8 files changed

+433
-1
lines changed

8 files changed

+433
-1
lines changed

modules/control-utility/src/test/java/org/apache/ignite/util/RollingUpgradeCommandTest.java

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,21 @@
1717

1818
package org.apache.ignite.util;
1919

20+
import java.util.ArrayList;
21+
import java.util.List;
22+
import java.util.Map;
23+
import java.util.function.Consumer;
24+
import org.apache.ignite.configuration.IgniteConfiguration;
25+
import org.apache.ignite.internal.IgniteEx;
2026
import org.apache.ignite.internal.management.rollingupgrade.RollingUpgradeCommand;
27+
import org.apache.ignite.internal.management.rollingupgrade.RollingUpgradeStatusCommand;
28+
import org.apache.ignite.internal.management.rollingupgrade.RollingUpgradeStatusNode;
2129
import org.apache.ignite.internal.management.rollingupgrade.RollingUpgradeTaskResult;
2230
import org.apache.ignite.lang.IgniteProductVersion;
31+
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
2332
import org.junit.Test;
2433

34+
import static java.util.stream.Collectors.toList;
2535
import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_BUILD_VER;
2636
import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_OK;
2737

@@ -39,6 +49,9 @@ public class RollingUpgradeCommandTest extends GridCommandHandlerClusterByClassA
3949
/** */
4050
public static final String ROLLING_UPGRADE = "--rolling-upgrade";
4151

52+
/** */
53+
public static final String STATUS = "status";
54+
4255
/** {@inheritDoc} */
4356
@Override protected void beforeTestsStarted() throws Exception {
4457
super.beforeTestsStarted();
@@ -177,4 +190,109 @@ public void testForceEnable() {
177190

178191
assertTrue(crd.context().rollingUpgrade().enabled());
179192
}
193+
194+
/** */
195+
@Test
196+
public void testStatusWhenDisabled() {
197+
int res = execute(ROLLING_UPGRADE, STATUS);
198+
199+
assertEquals(EXIT_CODE_OK, res);
200+
201+
RollingUpgradeTaskResult taskRes = (RollingUpgradeTaskResult)lastOperationResult;
202+
203+
assertNull(taskRes.errorMessage());
204+
assertNull(taskRes.currentVersion());
205+
assertNull(taskRes.targetVersion());
206+
207+
assertNull(taskRes.nodes());
208+
209+
RollingUpgradeStatusCommand statusCmd = new RollingUpgradeStatusCommand();
210+
211+
List<String> lines = new ArrayList<>();
212+
213+
statusCmd.printResult(null, taskRes, lines::add);
214+
215+
List<String> expectedLines = new ArrayList<>();
216+
217+
expectedLines.add("Rolling upgrade status: disabled");
218+
219+
assertEquals(expectedLines, lines);
220+
}
221+
222+
/** */
223+
@Test
224+
public void testStatusWhenEnabled() throws Exception {
225+
IgniteProductVersion curVer = IgniteProductVersion.fromString(crd.localNode().attribute(ATTR_BUILD_VER));
226+
227+
String targetVerStr = curVer.major() + "." + (curVer.minor() + 1) + "." + curVer.maintenance();
228+
IgniteProductVersion targetVer = IgniteProductVersion.fromString(targetVerStr);
229+
230+
execute(ROLLING_UPGRADE, ENABLE, targetVerStr);
231+
232+
Consumer<IgniteConfiguration> cfgC = cfg -> {
233+
TcpDiscoverySpi discoSpi = new TcpDiscoverySpi() {
234+
@Override public void setNodeAttributes(Map<String, Object> attrs, IgniteProductVersion ver) {
235+
super.setNodeAttributes(attrs, ver);
236+
attrs.put(ATTR_BUILD_VER, targetVerStr);
237+
}
238+
};
239+
240+
discoSpi.setIpFinder(((TcpDiscoverySpi)cfg.getDiscoverySpi()).getIpFinder());
241+
cfg.setDiscoverySpi(discoSpi);
242+
};
243+
244+
try (IgniteEx ignored = startGrid(SERVER_NODE_CNT + 1, cfgC)) {
245+
int res = execute(ROLLING_UPGRADE, STATUS);
246+
247+
assertEquals(EXIT_CODE_OK, res);
248+
}
249+
250+
RollingUpgradeTaskResult taskRes = (RollingUpgradeTaskResult)lastOperationResult;
251+
252+
assertNull(taskRes.errorMessage());
253+
assertEquals(curVer, taskRes.currentVersion());
254+
assertEquals(targetVer, taskRes.targetVersion());
255+
256+
List<RollingUpgradeStatusNode> nodes = taskRes.nodes();
257+
258+
assertNotNull(nodes);
259+
assertEquals(SERVER_NODE_CNT + 2, nodes.size());
260+
261+
List<RollingUpgradeStatusNode> oldNodes = nodes.stream().filter(node -> node.version().equals(curVer)).collect(toList());
262+
263+
List<RollingUpgradeStatusNode> newNodes = nodes.stream().filter(node -> node.version().equals(targetVer)).collect(toList());
264+
265+
assertEquals(SERVER_NODE_CNT + 1, oldNodes.size());
266+
assertEquals(1, newNodes.size());
267+
268+
RollingUpgradeStatusCommand statusCmd = new RollingUpgradeStatusCommand();
269+
270+
List<String> lines = new ArrayList<>();
271+
272+
statusCmd.printResult(null, taskRes, lines::add);
273+
274+
List<String> expectedLines = new ArrayList<>();
275+
276+
expectedLines.add("Rolling upgrade status: enabled");
277+
expectedLines.add("Current version: " + curVer);
278+
expectedLines.add("Target version: " + targetVer);
279+
280+
expectedLines.add("Version " + curVer + ":");
281+
oldNodes.forEach(node -> expectedLines.add(" Node[id=" + node.uuid() +
282+
", consistentId=" + node.consistentId() +
283+
", addrs=" + node.addresses() +
284+
", order=" + node.order() +
285+
", isClient=" + node.client() +
286+
"]"));
287+
288+
expectedLines.add("Version " + targetVer + ":");
289+
newNodes.forEach(node -> expectedLines.add(" Node[id=" + node.uuid() +
290+
", consistentId=" + node.consistentId() +
291+
", addrs=" + node.addresses() +
292+
", order=" + node.order() +
293+
", isClient=" + node.client() +
294+
"]"));
295+
296+
assertEquals(expectedLines, lines);
297+
}
180298
}

modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeCommand.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ public class RollingUpgradeCommand extends CommandRegistryImpl {
2525
public RollingUpgradeCommand() {
2626
super(
2727
new RollingUpgradeEnableCommand(),
28-
new RollingUpgradeDisableCommand()
28+
new RollingUpgradeDisableCommand(),
29+
new RollingUpgradeStatusCommand()
2930
);
3031
}
3132
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.ignite.internal.management.rollingupgrade;
19+
20+
import java.util.Collection;
21+
import java.util.TreeMap;
22+
import java.util.function.Consumer;
23+
import java.util.stream.Collectors;
24+
import org.apache.ignite.IgniteException;
25+
import org.apache.ignite.cluster.ClusterNode;
26+
import org.apache.ignite.internal.management.api.ComputeCommand;
27+
import org.apache.ignite.internal.management.api.NoArg;
28+
import org.apache.ignite.lang.IgniteExperimental;
29+
30+
import static org.apache.ignite.internal.management.api.CommandUtils.coordinatorOrNull;
31+
32+
/** Command to get status of rolling upgrade mode. */
33+
@IgniteExperimental
34+
public class RollingUpgradeStatusCommand implements ComputeCommand<NoArg, RollingUpgradeTaskResult> {
35+
/** {@inheritDoc} */
36+
@Override public String description() {
37+
return "Get status of rolling upgrade mode";
38+
}
39+
40+
/** {@inheritDoc} */
41+
@Override public Class<NoArg> argClass() {
42+
return NoArg.class;
43+
}
44+
45+
/** {@inheritDoc} */
46+
@Override public Class<RollingUpgradeStatusTask> taskClass() {
47+
return RollingUpgradeStatusTask.class;
48+
}
49+
50+
/** {@inheritDoc} */
51+
@Override public void printResult(NoArg arg, RollingUpgradeTaskResult res, Consumer<String> printer) {
52+
printer.accept("Rolling upgrade status: " + (res.targetVersion() != null ? "enabled" : "disabled"));
53+
54+
if (res.targetVersion() == null)
55+
return;
56+
57+
printer.accept("Current version: " + res.currentVersion());
58+
printer.accept("Target version: " + res.targetVersion());
59+
60+
if (res.nodes() == null || res.nodes().isEmpty()) {
61+
printer.accept("No nodes information available");
62+
return;
63+
}
64+
65+
res.nodes().stream()
66+
.collect(Collectors.groupingBy(RollingUpgradeStatusNode::version, TreeMap::new, Collectors.toList()))
67+
.forEach((ver, nodes) -> {
68+
printer.accept("Version " + ver + ":");
69+
nodes.forEach(node -> printer.accept(" Node[id=" + node.uuid() +
70+
", consistentId=" + node.consistentId() +
71+
", addrs=" + node.addresses() +
72+
", order=" + node.order() +
73+
", isClient=" + node.client() +
74+
"]"));
75+
});
76+
}
77+
78+
/** {@inheritDoc} */
79+
@Override public Collection<ClusterNode> nodes(Collection<ClusterNode> nodes, NoArg arg) {
80+
Collection<ClusterNode> coordinator = coordinatorOrNull(nodes);
81+
82+
if (coordinator == null)
83+
throw new IgniteException("Could not find coordinator among nodes: " + nodes);
84+
85+
return coordinator;
86+
}
87+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.ignite.internal.management.rollingupgrade;
19+
20+
import java.io.IOException;
21+
import java.io.ObjectInput;
22+
import java.io.ObjectOutput;
23+
import java.util.Collection;
24+
import java.util.UUID;
25+
import org.apache.ignite.cluster.ClusterNode;
26+
import org.apache.ignite.internal.dto.IgniteDataTransferObject;
27+
import org.apache.ignite.internal.util.typedef.internal.U;
28+
import org.apache.ignite.lang.IgniteProductVersion;
29+
30+
import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_BUILD_VER;
31+
32+
/** Node status information for rolling upgrade. */
33+
public class RollingUpgradeStatusNode extends IgniteDataTransferObject {
34+
/** */
35+
private static final long serialVersionUID = 0L;
36+
37+
/** */
38+
private UUID uuid;
39+
40+
/** */
41+
private Object consistentId;
42+
43+
/** */
44+
private Collection<String> addresses;
45+
46+
/** */
47+
private IgniteProductVersion ver;
48+
49+
/** */
50+
private long order;
51+
52+
/** */
53+
private boolean client;
54+
55+
/** */
56+
public RollingUpgradeStatusNode() {
57+
// No-op.
58+
}
59+
60+
/** */
61+
public RollingUpgradeStatusNode(ClusterNode node) {
62+
ver = IgniteProductVersion.fromString(node.attribute(ATTR_BUILD_VER));
63+
uuid = node.id();
64+
consistentId = node.consistentId();
65+
addresses = node.addresses();
66+
order = node.order();
67+
client = node.isClient();
68+
}
69+
70+
/** {@inheritDoc} */
71+
@Override protected void writeExternalData(ObjectOutput out) throws IOException {
72+
out.writeObject(ver);
73+
U.writeUuid(out, uuid);
74+
out.writeObject(consistentId);
75+
U.writeCollection(out, addresses);
76+
out.writeLong(order);
77+
out.writeBoolean(client);
78+
}
79+
80+
/** {@inheritDoc} */
81+
@Override protected void readExternalData(ObjectInput in) throws IOException, ClassNotFoundException {
82+
ver = (IgniteProductVersion)in.readObject();
83+
uuid = U.readUuid(in);
84+
consistentId = in.readObject();
85+
addresses = U.readCollection(in);
86+
order = in.readLong();
87+
client = in.readBoolean();
88+
}
89+
90+
/** */
91+
public IgniteProductVersion version() {
92+
return ver;
93+
}
94+
95+
/** */
96+
public Collection<String> addresses() {
97+
return addresses;
98+
}
99+
100+
/** */
101+
public UUID uuid() {
102+
return uuid;
103+
}
104+
105+
/** */
106+
public Object consistentId() {
107+
return consistentId;
108+
}
109+
110+
/** */
111+
public long order() {
112+
return order;
113+
}
114+
115+
/** */
116+
public boolean client() {
117+
return client;
118+
}
119+
}

0 commit comments

Comments
 (0)