Skip to content

Commit

Permalink
SONAR-8798 start and monitor ES script from main process
Browse files Browse the repository at this point in the history
  • Loading branch information
sns-seb authored and bartfastiel committed Aug 9, 2017
1 parent 8e7d3d3 commit 070e44f
Show file tree
Hide file tree
Showing 11 changed files with 721 additions and 28 deletions.
Expand Up @@ -31,11 +31,13 @@
import org.sonar.application.config.AppSettings; import org.sonar.application.config.AppSettings;
import org.sonar.application.config.ClusterSettings; import org.sonar.application.config.ClusterSettings;
import org.sonar.application.process.CommandFactory; import org.sonar.application.process.CommandFactory;
import org.sonar.application.process.EsCommand;
import org.sonar.application.process.JavaCommand; import org.sonar.application.process.JavaCommand;
import org.sonar.application.process.ProcessLauncher; import org.sonar.application.process.ProcessLauncher;
import org.sonar.application.process.Lifecycle; import org.sonar.application.process.Lifecycle;
import org.sonar.application.process.ProcessEventListener; import org.sonar.application.process.ProcessEventListener;
import org.sonar.application.process.ProcessLifecycleListener; import org.sonar.application.process.ProcessLifecycleListener;
import org.sonar.application.process.ProcessMonitor;
import org.sonar.application.process.SQProcess; import org.sonar.application.process.SQProcess;
import org.sonar.process.ProcessId; import org.sonar.process.ProcessId;


Expand Down Expand Up @@ -105,7 +107,7 @@ private void tryToStartAll() {
private void tryToStartEs() { private void tryToStartEs() {
SQProcess process = processesById.get(ProcessId.ELASTICSEARCH); SQProcess process = processesById.get(ProcessId.ELASTICSEARCH);
if (process != null) { if (process != null) {
tryToStartProcess(process, commandFactory::createEsCommand); tryToStartEsProcess(process, commandFactory::createEsCommand);
} }
} }


Expand All @@ -115,9 +117,9 @@ private void tryToStartWeb() {
return; return;
} }
if (appState.isOperational(ProcessId.WEB_SERVER, false)) { if (appState.isOperational(ProcessId.WEB_SERVER, false)) {
tryToStartProcess(process, () -> commandFactory.createWebCommand(false)); tryToStartJavaProcess(process, () -> commandFactory.createWebCommand(false));
} else if (appState.tryToLockWebLeader()) { } else if (appState.tryToLockWebLeader()) {
tryToStartProcess(process, () -> commandFactory.createWebCommand(true)); tryToStartJavaProcess(process, () -> commandFactory.createWebCommand(true));
} else { } else {
Optional<String> leader = appState.getLeaderHostName(); Optional<String> leader = appState.getLeaderHostName();
if (leader.isPresent()) { if (leader.isPresent()) {
Expand All @@ -131,7 +133,7 @@ private void tryToStartWeb() {
private void tryToStartCe() { private void tryToStartCe() {
SQProcess process = processesById.get(ProcessId.COMPUTE_ENGINE); SQProcess process = processesById.get(ProcessId.COMPUTE_ENGINE);
if (process != null && appState.isOperational(ProcessId.WEB_SERVER, false) && isEsClientStartable()) { if (process != null && appState.isOperational(ProcessId.WEB_SERVER, false) && isEsClientStartable()) {
tryToStartProcess(process, commandFactory::createCeCommand); tryToStartJavaProcess(process, commandFactory::createCeCommand);
} }
} }


Expand All @@ -140,12 +142,23 @@ private boolean isEsClientStartable() {
return appState.isOperational(ProcessId.ELASTICSEARCH, requireLocalEs); return appState.isOperational(ProcessId.ELASTICSEARCH, requireLocalEs);
} }


private void tryToStartProcess(SQProcess process, Supplier<JavaCommand> commandSupplier) { private void tryToStartJavaProcess(SQProcess process, Supplier<JavaCommand> commandSupplier) {
tryToStart(process, () -> {
JavaCommand command = commandSupplier.get();
return processLauncher.launch(command);
});
}

private void tryToStartEsProcess(SQProcess process, Supplier<EsCommand> commandSupplier) {
tryToStart(process, () -> {
EsCommand command = commandSupplier.get();
return processLauncher.launch(command);
});
}

private void tryToStart(SQProcess process, Supplier<ProcessMonitor> processMonitorSupplier) {
try { try {
process.start(() -> { process.start(processMonitorSupplier);
JavaCommand command = commandSupplier.get();
return processLauncher.launch(command);
});
} catch (RuntimeException e) { } catch (RuntimeException e) {
// failed to start command -> stop everything // failed to start command -> stop everything
terminate(); terminate();
Expand Down
Expand Up @@ -21,7 +21,7 @@


public interface CommandFactory { public interface CommandFactory {


JavaCommand createEsCommand(); EsCommand createEsCommand();


JavaCommand createWebCommand(boolean leader); JavaCommand createWebCommand(boolean leader);


Expand Down
Expand Up @@ -19,14 +19,17 @@
*/ */
package org.sonar.application.process; package org.sonar.application.process;


import java.io.File;
import java.util.Map;
import java.util.Optional;
import org.sonar.application.config.AppSettings; import org.sonar.application.config.AppSettings;
import org.sonar.process.ProcessId; import org.sonar.process.ProcessId;
import org.sonar.process.ProcessProperties; import org.sonar.process.ProcessProperties;


import java.io.File; import static org.sonar.process.ProcessProperties.HTTPS_PROXY_HOST;
import java.util.Optional; import static org.sonar.process.ProcessProperties.HTTPS_PROXY_PORT;

import static org.sonar.process.ProcessProperties.HTTP_PROXY_HOST;
import static org.sonar.process.ProcessProperties.*; import static org.sonar.process.ProcessProperties.HTTP_PROXY_PORT;


public class CommandFactoryImpl implements CommandFactory { public class CommandFactoryImpl implements CommandFactory {
/** /**
Expand All @@ -49,15 +52,43 @@ public CommandFactoryImpl(AppSettings settings) {
} }


@Override @Override
public JavaCommand createEsCommand() { public EsCommand createEsCommand() {
File homeDir = settings.getProps().nonNullValueAsFile(ProcessProperties.PATH_HOME); File homeDir = settings.getProps().nonNullValueAsFile(ProcessProperties.PATH_HOME);
return newJavaCommand(ProcessId.ELASTICSEARCH, homeDir) File executable = new File(homeDir, getExecutable());
.addJavaOptions("-Djava.awt.headless=true") if (!executable.exists()) {
.addJavaOptions(settings.getProps().nonNullValue(ProcessProperties.SEARCH_JAVA_OPTS)) throw new IllegalStateException("Cannot find elasticsearch binary");
.addJavaOptions(settings.getProps().nonNullValue(ProcessProperties.SEARCH_JAVA_ADDITIONAL_OPTS)) }
.setClassName("org.sonar.search.SearchServer")
.addClasspath("./lib/common/*") Map<String, String> settingsMap = new EsSettings(settings.getProps()).build();
.addClasspath("./lib/search/*");
EsCommand res = new EsCommand(ProcessId.ELASTICSEARCH)
.setWorkDir(executable.getParentFile().getParentFile())
.setExecutable(executable)
.setArguments(settings.getProps().rawProperties())
// TODO add argument to specify log4j configuration file
// TODO add argument to specify yaml configuration file
.setUrl("http://" + settingsMap.get("http.host") + ":" + settingsMap.get("http.port"));

settingsMap.entrySet().stream()
.filter(entry -> !"path.home".equals(entry.getKey()))
.forEach(entry -> res.addEsOption("-E" + entry.getKey() + "=" + entry.getValue()));

return res;

// FIXME quid of proxy settings and sonar.search.javaOpts/javaAdditionalOpts
// defaults of HTTPS are the same than HTTP defaults
// setSystemPropertyToDefaultIfNotSet(command, HTTPS_PROXY_HOST, HTTP_PROXY_HOST);
// setSystemPropertyToDefaultIfNotSet(command, HTTPS_PROXY_PORT, HTTP_PROXY_PORT);
// command
// .addJavaOptions(settings.getProps().nonNullValue(ProcessProperties.SEARCH_JAVA_OPTS))
// .addJavaOptions(settings.getProps().nonNullValue(ProcessProperties.SEARCH_JAVA_ADDITIONAL_OPTS));
}

private static String getExecutable() {
if (System.getProperty("os.name").startsWith("Windows")) {
return "elasticsearch/bin/elasticsearch.bat";
}
return "elasticsearch/bin/elasticsearch";
} }


@Override @Override
Expand Down
@@ -0,0 +1,64 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.application.process;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.sonar.process.ProcessId;

public class EsCommand extends AbstractCommand<EsCommand> {
private File executable;
private String url;
private List<String> esOptions = new ArrayList<>();

public EsCommand(ProcessId id) {
super(id);
}

public File getExecutable() {
return executable;
}

public EsCommand setExecutable(File executable) {
this.executable = executable;
return this;
}

public String getUrl() {
return url;
}

public EsCommand setUrl(String url) {
this.url = url;
return this;
}

public List<String> getEsOptions() {
return esOptions;
}

public EsCommand addEsOption(String s) {
if (!s.isEmpty()) {
esOptions.add(s);
}
return this;
}
}
@@ -0,0 +1,119 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.application.process;

import java.io.IOException;
import java.net.ConnectException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class EsProcessMonitor extends AbstractProcessMonitor {
private static final Logger LOG = LoggerFactory.getLogger(EsProcessMonitor.class);
private static final int WAIT_FOR_UP_DELAY_IN_MILLIS = 100;
private static final int WAIT_FOR_UP_TIMEOUT = 10 * 60; /* 1min */

private final AtomicBoolean nodeUp = new AtomicBoolean(false);
private final AtomicBoolean nodeOperational = new AtomicBoolean(false);
private final URL healthCheckURL;

public EsProcessMonitor(Process process, String url) throws MalformedURLException {
super(process);
this.healthCheckURL = new URL(url + "/_cluster/health?wait_for_status=yellow&timeout=30s");
}

@Override
public boolean isOperational() {
if (nodeOperational.get()) {
return true;
}

try {
boolean flag = checkOperational();
if (flag) {
nodeOperational.set(true);
}
} catch (InterruptedException e) {
LOG.trace("Interrupted while checking ES node is operational", e);
Thread.currentThread().interrupt();
}
return nodeOperational.get();
}

private boolean checkOperational() throws InterruptedException {
int i = 0;
Status status = checkStatus();
do {
if (status != Status.CONNECTION_REFUSED) {
nodeUp.set(true);
} else {
Thread.sleep(WAIT_FOR_UP_DELAY_IN_MILLIS);
i++;
status = checkStatus();
}
} while (!nodeUp.get() && i < WAIT_FOR_UP_TIMEOUT);
return status == Status.YELLOW || status == Status.GREEN;
}

private Status checkStatus() {
try {
URLConnection urlConnection = healthCheckURL.openConnection();
urlConnection.connect();
String response = IOUtils.toString(urlConnection.getInputStream());
if (response.contains("\"status\":\"green\"")) {
return Status.GREEN;
} else if (response.contains("\"status\":\"yellow\"")) {
return Status.YELLOW;
} else if (response.contains("\"status\":\"red\"")) {
return Status.RED;
}
return Status.KO;
} catch (ConnectException e) {
return Status.CONNECTION_REFUSED;
} catch (IOException e) {
LOG.error("Unexpected error occurred while checking ES node status using WebService API", e);
return Status.KO;
}
}

enum Status {
CONNECTION_REFUSED, KO, RED, YELLOW, GREEN
}

@Override
public void askForStop() {
process.destroy();
}

@Override
public boolean askedForRestart() {
// ES does not support asking for restart
return false;
}

@Override
public void acknowledgeAskForRestart() {
// nothing to do
}
}

0 comments on commit 070e44f

Please sign in to comment.