Skip to content

Commit 4974684

Browse files
authored
Add tool elasticsearch-node unsafe-bootstrap (#37696)
elasticsearch-node tool helps to restore cluster if half or more of master eligible nodes are lost. Of course, all bets are off, regarding data consistency. There are two parts of the tool: unsafe-bootstrap to be used when there is still at least one master-eligible node alive and detach-cluster, when there are no master-eligible nodes left. This commit implements the first part. Docs for the tool will be added separately as a part of #37812.
1 parent 289106a commit 4974684

File tree

13 files changed

+552
-7
lines changed

13 files changed

+552
-7
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/bash
2+
3+
ES_MAIN_CLASS=org.elasticsearch.cluster.coordination.NodeToolCli \
4+
"`dirname "$0"`"/elasticsearch-cli \
5+
"$@"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@echo off
2+
3+
setlocal enabledelayedexpansion
4+
setlocal enableextensions
5+
6+
set ES_MAIN_CLASS=org.elasticsearch.cluster.coordination.NodeToolCli
7+
call "%~dp0elasticsearch-cli.bat" ^
8+
%%* ^
9+
|| exit /b 1
10+
11+
endlocal
12+
endlocal

qa/vagrant/src/main/java/org/elasticsearch/packaging/test/ArchiveTestCase.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ public void test90SecurityCliPackaging() {
301301
}
302302
}
303303

304-
public void test100RepairIndexCliPackaging() {
304+
public void test100ElasticsearchShardCliPackaging() {
305305
assumeThat(installation, is(notNullValue()));
306306

307307
final Installation.Executables bin = installation.executables();
@@ -318,4 +318,22 @@ public void test100RepairIndexCliPackaging() {
318318
}
319319
}
320320

321+
public void test110ElasticsearchNodeCliPackaging() {
322+
assumeThat(installation, is(notNullValue()));
323+
324+
final Installation.Executables bin = installation.executables();
325+
final Shell sh = new Shell();
326+
327+
Platforms.PlatformAction action = () -> {
328+
final Result result = sh.run(bin.elasticsearchNode + " -h");
329+
assertThat(result.stdout,
330+
containsString("A CLI tool to unsafely recover a cluster after the permanent loss of too many master-eligible nodes"));
331+
};
332+
333+
if (distribution().equals(Distribution.DEFAULT_TAR) || distribution().equals(Distribution.DEFAULT_ZIP)) {
334+
Platforms.onLinux(action);
335+
Platforms.onWindows(action);
336+
}
337+
}
338+
321339
}

qa/vagrant/src/main/java/org/elasticsearch/packaging/util/Archives.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,8 @@ private static void verifyOssInstallation(Installation es, Distribution distribu
186186
"elasticsearch-env",
187187
"elasticsearch-keystore",
188188
"elasticsearch-plugin",
189-
"elasticsearch-shard"
189+
"elasticsearch-shard",
190+
"elasticsearch-node"
190191
).forEach(executable -> {
191192

192193
assertThat(es.bin(executable), file(File, owner, owner, p755));

qa/vagrant/src/main/java/org/elasticsearch/packaging/util/Installation.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ public class Executables {
102102
public final Path elasticsearchKeystore = platformExecutable("elasticsearch-keystore");
103103
public final Path elasticsearchCertutil = platformExecutable("elasticsearch-certutil");
104104
public final Path elasticsearchShard = platformExecutable("elasticsearch-shard");
105+
public final Path elasticsearchNode = platformExecutable("elasticsearch-node");
105106

106107
private Path platformExecutable(String name) {
107108
final String platformExecutableName = Platforms.WINDOWS

qa/vagrant/src/main/java/org/elasticsearch/packaging/util/Packages.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,8 @@ private static void verifyOssInstallation(Installation es, Distribution distribu
196196
"elasticsearch",
197197
"elasticsearch-plugin",
198198
"elasticsearch-keystore",
199-
"elasticsearch-shard"
199+
"elasticsearch-shard",
200+
"elasticsearch-node"
200201
).forEach(executable -> assertThat(es.bin(executable), file(File, "root", "root", p755)));
201202

202203
Stream.of(

qa/vagrant/src/test/resources/packaging/utils/packages.bash

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ verify_package_installation() {
104104
assert_file "$ESHOME/bin/elasticsearch" f root root 755
105105
assert_file "$ESHOME/bin/elasticsearch-plugin" f root root 755
106106
assert_file "$ESHOME/bin/elasticsearch-shard" f root root 755
107+
assert_file "$ESHOME/bin/elasticsearch-node" f root root 755
107108
assert_file "$ESHOME/lib" d root root 755
108109
assert_file "$ESCONFIG" d root elasticsearch 2750
109110
assert_file "$ESCONFIG/elasticsearch.keystore" f root elasticsearch 660

qa/vagrant/src/test/resources/packaging/utils/tar.bash

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ verify_archive_installation() {
9595
assert_file "$ESHOME/bin/elasticsearch-keystore" f elasticsearch elasticsearch 755
9696
assert_file "$ESHOME/bin/elasticsearch-plugin" f elasticsearch elasticsearch 755
9797
assert_file "$ESHOME/bin/elasticsearch-shard" f elasticsearch elasticsearch 755
98+
assert_file "$ESHOME/bin/elasticsearch-node" f elasticsearch elasticsearch 755
9899
assert_file "$ESCONFIG" d elasticsearch elasticsearch 755
99100
assert_file "$ESCONFIG/elasticsearch.yml" f elasticsearch elasticsearch 660
100101
assert_file "$ESCONFIG/jvm.options" f elasticsearch elasticsearch 660

server/src/main/java/org/elasticsearch/cli/CommandLoggingConfigurator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@
2727
* Holder class for method to configure logging without Elasticsearch configuration files for use in CLI tools that will not read such
2828
* files.
2929
*/
30-
final class CommandLoggingConfigurator {
30+
public final class CommandLoggingConfigurator {
3131

3232
/**
3333
* Configures logging without Elasticsearch configuration files based on the system property "es.logger.level" only. As such, any
3434
* logging will be written to the console.
3535
*/
36-
static void configureLoggingWithoutConfig() {
36+
public static void configureLoggingWithoutConfig() {
3737
// initialize default for es.logger.level because we will not read the log4j2.properties
3838
final String loggerLevel = System.getProperty("es.logger.level", Level.INFO.name());
3939
final Settings settings = Settings.builder().put("logger.level", loggerLevel).build();
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* 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,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.cluster.coordination;
20+
21+
import org.elasticsearch.cli.CommandLoggingConfigurator;
22+
import org.elasticsearch.cli.MultiCommand;
23+
import org.elasticsearch.cli.Terminal;
24+
25+
// NodeToolCli does not extend LoggingAwareCommand, because LoggingAwareCommand performs logging initialization
26+
// after LoggingAwareCommand instance is constructed.
27+
// It's too late for us, because before UnsafeBootstrapMasterCommand is added to the list of subcommands
28+
// log4j2 initialization will happen, because it has static reference to Logger class.
29+
// Even if we avoid making a static reference to Logger class, there is no nice way to avoid declaring
30+
// UNSAFE_BOOTSTRAP, which depends on ClusterService, which in turn has static Logger.
31+
// TODO execute CommandLoggingConfigurator.configureLoggingWithoutConfig() in the constructor of commands, not in beforeMain
32+
public class NodeToolCli extends MultiCommand {
33+
34+
public NodeToolCli() {
35+
super("A CLI tool to unsafely recover a cluster after the permanent loss of too many master-eligible nodes", ()->{});
36+
CommandLoggingConfigurator.configureLoggingWithoutConfig();
37+
subcommands.put("unsafe-bootstrap", new UnsafeBootstrapMasterCommand());
38+
}
39+
40+
public static void main(String[] args) throws Exception {
41+
exit(new NodeToolCli().main(args, Terminal.DEFAULT));
42+
}
43+
44+
}

0 commit comments

Comments
 (0)