Skip to content

Commit

Permalink
Some bug fixes and improvements
Browse files Browse the repository at this point in the history
Fix issue #1361 - Able to restore database while server is running
Fix issue #1360 - NPE when running reset-admin-password utility
  • Loading branch information
robinshine committed May 2, 2023
1 parent 32abc05 commit 69baf83
Show file tree
Hide file tree
Showing 26 changed files with 813 additions and 758 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -614,8 +614,8 @@
</repository>
</repositories>
<properties>
<commons.version>2.7.13</commons.version>
<agent.version>1.8.6</agent.version>
<commons.version>2.7.14</commons.version>
<agent.version>1.8.7</agent.version>
<slf4j.version>1.7.36</slf4j.version>
<logback.version>1.2.11</logback.version>
<antlr.version>4.7.2</antlr.version>
Expand Down
2 changes: 1 addition & 1 deletion server-core/src/main/java/io/onedev/server/CoreModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ else if (CleanDatabase.COMMAND.equals(Bootstrap.command.getName()))
return CleanDatabase.class;
else if (ResetAdminPassword.COMMAND.equals(Bootstrap.command.getName()))
return ResetAdminPassword.class;
else
else
throw new RuntimeException("Unrecognized command: " + Bootstrap.command.getName());
} else {
return OneDev.class;
Expand Down
91 changes: 76 additions & 15 deletions server-core/src/main/java/io/onedev/server/OneDev.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,20 @@
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
import java.io.*;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.util.Date;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;

import static io.onedev.commons.utils.FileUtils.loadProperties;
import static io.onedev.k8shelper.KubernetesHelper.BEARER;
import static io.onedev.server.persistence.PersistenceUtils.callWithTransaction;
import static javax.ws.rs.core.HttpHeaders.AUTHORIZATION;

public class OneDev extends AbstractPlugin implements Serializable {
public class OneDev extends AbstractPlugin implements Serializable, Runnable {

private static final Logger logger = LoggerFactory.getLogger(OneDev.class);

Expand Down Expand Up @@ -83,6 +82,10 @@ public class OneDev extends AbstractPlugin implements Serializable {

private volatile InitStage initStage;

private Class<?> wrapperManagerClass;

private volatile Thread thread;

// Some are injected via provider as instantiation might encounter problem during upgrade
@Inject
public OneDev(Provider<JettyLauncher> jettyLauncherProvider, TaskScheduler taskScheduler,
Expand All @@ -100,19 +103,36 @@ public OneDev(Provider<JettyLauncher> jettyLauncherProvider, TaskScheduler taskS
this.clusterManager = clusterManager;
this.idManager = idManager;
this.sessionFactoryManager = sessionFactoryManager;

try {
wrapperManagerClass = Class.forName("org.tanukisoftware.wrapper.WrapperManager");
} catch (ClassNotFoundException e) {
}
thread = new Thread(this);

initStage = new InitStage("Server is Starting...");
}

@Override
public void start() {
var maintenanceFile = getMaintenanceFile(Bootstrap.installDir);
while (maintenanceFile.exists()) {
logger.info("Maintenance in progress, waiting...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}

SecurityUtils.bindAsSystem();

System.setProperty("hsqldb.reconfig_logging", "false");
System.setProperty("hsqldb.method_class_names", "java.lang.Math");

sessionFactoryManager.start();

clusterManager.start();
sessionFactoryManager.start();

try (var conn = persistenceManager.openConnection()) {
callWithTransaction(conn, () -> {
persistenceManager.populateDatabase(conn);
Expand All @@ -122,13 +142,8 @@ public void start() {
throw new RuntimeException(e);
};

clusterManager.start();
idManager.init();

// restart session factory to pick up Hazelcast 2nd level cache
sessionFactoryManager.stop();
sessionFactoryManager.start();

sessionManager.run(() -> listenerRegistry.post(new SystemStarting()));
jettyLauncherProvider.get().start();

Expand Down Expand Up @@ -184,11 +199,13 @@ public void postStart() {
initStage = null;
listenerRegistry.post(new SystemStarted());
clusterManager.postStart();
thread.start();
logger.info("Server is ready at " + guessServerUrl() + ".");
}

@Override
public void preStop() {
thread = null;
clusterManager.preStop();
SecurityUtils.bindAsSystem();
try {
Expand All @@ -207,9 +224,8 @@ public void stop() {
jettyLauncherProvider.get().stop();
sessionManager.run(() -> listenerRegistry.post(new SystemStopped()));

// stop cluster manager first as it depends on metadata of session factory
clusterManager.stop();
sessionFactoryManager.stop();
clusterManager.stop();
executorService.shutdown();
} catch (ServerNotReadyException ignore) {
}
Expand Down Expand Up @@ -319,9 +335,8 @@ public Object writeReplace() throws ObjectStreamException {
}

public static boolean isServerRunning(File installDir) {
Properties props = loadProperties(new File(installDir, "conf/server.properties"));
int sshPort = Integer.parseInt(props.get("ssh_port").toString());
try (ServerSocket ignored = new ServerSocket(sshPort)) {
var serverConfig = new ServerConfig(installDir);
try (ServerSocket ignored = new ServerSocket(serverConfig.getClusterPort())) {
return false;
} catch (IOException e) {
if (e.getMessage() != null && e.getMessage().contains("Address already in use"))
Expand All @@ -335,4 +350,50 @@ public static File getAssetsDir() {
return new File(Bootstrap.getSiteDir(), "assets");
}

public static File getMaintenanceFile(File installDir) {
return new File(installDir, "maintenance");
}

private void restart() {
if (wrapperManagerClass != null) {
try {
Method method = wrapperManagerClass.getDeclaredMethod("restartAndReturn");
method.invoke(null, new Object[0]);
} catch (Exception e) {
logger.error("Error restarting server", e);
}
} else {
logger.warn("Restart request ignored as there is no wrapper manager available");
}
}

@Override
public void run() {
var localServer = clusterManager.getLocalServerAddress();
var maintenanceFile = getMaintenanceFile(Bootstrap.installDir);
while (thread != null) {
if (maintenanceFile.exists()) {
logger.info("Maintenance requested, trying to stop all servers...");
clusterManager.submitToAllServers(() -> {
if (!localServer.equals(clusterManager.getLocalServerAddress()))
restart();
return null;
});
while (thread != null && clusterManager.getServerAddresses().size() != 1) {
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
}
restart();
break;
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
}
}
}

}
147 changes: 126 additions & 21 deletions server-core/src/main/java/io/onedev/server/ServerConfig.java
Original file line number Diff line number Diff line change
@@ -1,25 +1,130 @@
package io.onedev.server;

public interface ServerConfig {

/**
* Get http port configured for the server.
* <p>
* @return
* http port of the server, or <i>0</i> if http port is not defined
*/
int getHttpPort();

/**
* Get ssh port configured for the server.
* <p>
* @return
* ssh port of the server
*/
int getSshPort();

String getClusterIp();

int getClusterPort();
import com.google.common.base.Splitter;
import io.onedev.server.persistence.HibernateConfig;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.List;
import java.util.Properties;

import static io.onedev.commons.utils.FileUtils.loadProperties;

public class ServerConfig {

private static final Logger logger = LoggerFactory.getLogger(ServerConfig.class);

private static final String PROP_HTTP_PORT = "http_port";

private static final String PROP_SSH_PORT = "ssh_port";

private static final String PROP_CLUSTER_IP = "cluster_ip";

private static final String PROP_CLUSTER_PORT = "cluster_port";

private final int httpPort;

private final int sshPort;

private final String clusterIp;

private final int clusterPort;

public ServerConfig(File installDir) {
File file = new File(installDir, "conf/server.properties");
Properties props = loadProperties(file);

String httpPortStr = System.getenv(PROP_HTTP_PORT);
if (StringUtils.isBlank(httpPortStr))
httpPortStr = props.getProperty(PROP_HTTP_PORT);
if (StringUtils.isNotBlank(httpPortStr)) {
httpPort = Integer.parseInt(httpPortStr.trim());
} else {
logger.warn(PROP_HTTP_PORT + " not specified, default to 6610");
httpPort = 6610;
}

String sshPortStr = System.getenv(PROP_SSH_PORT);
if (StringUtils.isBlank(sshPortStr))
sshPortStr = props.getProperty(PROP_SSH_PORT);
if (StringUtils.isNotBlank(sshPortStr)) {
sshPort = Integer.parseInt(sshPortStr.trim());
} else {
logger.warn(PROP_SSH_PORT + " not specified, default to 6611");
sshPort = 6611;
}

String clusterIp = System.getenv(PROP_CLUSTER_IP);
if (StringUtils.isBlank(clusterIp))
clusterIp = props.getProperty(PROP_CLUSTER_IP);

if (StringUtils.isBlank(clusterIp)) {
HibernateConfig hibernateConfig = new HibernateConfig(installDir);
String dbUrl = StringUtils.substringAfter(hibernateConfig.getUrl(), ":");
if (dbUrl.startsWith("hsqldb")) {
clusterIp = "127.0.0.1";
} else {
String dbHost;
int dbPort;
if (dbUrl.startsWith("oracle")) {
List<String> fields = Splitter.on(":").splitToList(dbUrl);
dbHost = StringUtils.stripStart(fields.get(2), "@");
String dbPortString = fields.get(3);
if (dbPortString.contains("/"))
dbPort = Integer.parseInt(StringUtils.substringBefore(dbPortString, "/"));
else
dbPort = Integer.parseInt(dbPortString);
} else {
String tempStr = StringUtils.substringAfter(dbUrl, "//");
dbHost = StringUtils.substringBefore(tempStr, ":");
tempStr = StringUtils.substringAfter(tempStr, ":");
if (dbUrl.startsWith("sqlserver")) {
dbPort = Integer.parseInt(StringUtils.substringBefore(tempStr, ";"));
} else {
tempStr = StringUtils.substringBefore(tempStr, "/");
// Fix issue https://code.onedev.io/onedev/server/~issues/1086
tempStr = StringUtils.substringBefore(tempStr, ",");
dbPort = Integer.parseInt(tempStr);
}
}

try (Socket socket = new Socket()) {
socket.connect(new InetSocketAddress(dbHost, dbPort));
clusterIp = socket.getLocalAddress().getHostAddress();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
this.clusterIp = clusterIp;

String clusterPortStr = System.getenv(PROP_CLUSTER_PORT);
if (StringUtils.isBlank(clusterPortStr))
clusterPortStr = props.getProperty(PROP_CLUSTER_PORT);
if (StringUtils.isBlank(clusterPortStr))
clusterPort = 5710;
else
clusterPort = Integer.parseInt(clusterPortStr.trim());
}

public int getHttpPort() {
return httpPort;
}

public int getSshPort() {
return sshPort;
}

public String getClusterIp() {
return clusterIp;
}

public int getClusterPort() {
return clusterPort;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public interface ClusterManager {

boolean isLeaderServer();

@Nullable
HazelcastInstance getHazelcastInstance();

void init(IAtomicLong data, Callable<Long> initializer);
Expand Down
Loading

0 comments on commit 69baf83

Please sign in to comment.