Skip to content

Commit

Permalink
Cleanup test results when run/job is deleted (#111)
Browse files Browse the repository at this point in the history
  • Loading branch information
timja authored Aug 5, 2021
1 parent b04cf27 commit ff44866
Show file tree
Hide file tree
Showing 6 changed files with 282 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,54 @@

import hudson.init.Initializer;
import io.jenkins.plugins.junit.storage.JunitTestResultStorageConfiguration;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.flywaydb.core.Flyway;
import org.jenkinsci.plugins.database.Database;
import org.jenkinsci.plugins.database.GlobalDatabaseConfiguration;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

import javax.sql.DataSource;
import java.util.logging.Level;
import java.util.logging.Logger;

import static hudson.init.InitMilestone.SYSTEM_CONFIG_ADAPTED;

@Restricted(NoExternalUse.class)
public class DatabaseSchemaLoader {


private static final Logger LOGGER = Logger.getLogger(DatabaseSchemaLoader.class.getName());

static boolean MIGRATED;

@Initializer(after = SYSTEM_CONFIG_ADAPTED)
public static void migrateSchema() throws SQLException {
public static void migrateSchema() {
JunitTestResultStorageConfiguration configuration = JunitTestResultStorageConfiguration.get();
if (configuration.getStorage() instanceof DatabaseTestResultStorage) {
DatabaseTestResultStorage storage = (DatabaseTestResultStorage) configuration.getStorage();
DataSource dataSource = storage.getConnectionSupplier().database().getDataSource();
try {
DatabaseTestResultStorage storage = (DatabaseTestResultStorage) configuration.getStorage();
DataSource dataSource = storage.getConnectionSupplier().database().getDataSource();

Database database = GlobalDatabaseConfiguration.get().getDatabase();
Database database = GlobalDatabaseConfiguration.get().getDatabase();

assert database != null;
String databaseDriverName = database.getClass().getName();
String schemaLocation = "postgres";
if (databaseDriverName.contains("mysql")) {
schemaLocation = "mysql";
assert database != null;
String databaseDriverName = database.getClass().getName();
String schemaLocation = "postgres";
if (databaseDriverName.contains("mysql")) {
schemaLocation = "mysql";
}
Flyway flyway = Flyway
.configure(DatabaseSchemaLoader.class.getClassLoader())
.baselineOnMigrate(true)
.table("junit_flyway_schema_history")
.dataSource(dataSource)
.locations("db/migration/" + schemaLocation)
.load();
flyway.migrate();
MIGRATED = true;
} catch (Exception e) {
// TODO add admin monitor
LOGGER.log(Level.SEVERE, "Error migrating database, correct this error before using the junit plugin", e);
}
Flyway flyway = Flyway
.configure(DatabaseSchemaLoader.class.getClassLoader())
.baselineOnMigrate(true)
.table("junit_flyway_schema_history")
.dataSource(dataSource)
.locations("db/migration/" + schemaLocation)
.load();
flyway.migrate();
MIGRATED = true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.ExtensionList;
import hudson.Util;
import hudson.model.Job;
import hudson.model.Run;
Expand Down Expand Up @@ -31,19 +32,24 @@
import java.util.Map;
import java.util.Objects;
import jenkins.model.Jenkins;
import net.sf.json.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.jenkinsci.Symbol;
import org.jenkinsci.plugins.database.Database;
import org.jenkinsci.plugins.database.GlobalDatabaseConfiguration;
import org.jenkinsci.remoting.SerializableOnlyOverRemoting;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.StaplerRequest;


@Extension
public class DatabaseTestResultStorage extends JunitTestResultStorage {

private transient ConnectionSupplier connectionSupplier;

private boolean skipCleanupRunsOnDeletion;

@DataBoundConstructor
public DatabaseTestResultStorage() {}

Expand All @@ -54,6 +60,15 @@ public ConnectionSupplier getConnectionSupplier() {
return connectionSupplier;
}

public boolean isSkipCleanupRunsOnDeletion() {
return skipCleanupRunsOnDeletion;
}

@DataBoundSetter
public void setSkipCleanupRunsOnDeletion(boolean skipCleanupRunsOnDeletion) {
this.skipCleanupRunsOnDeletion = skipCleanupRunsOnDeletion;
}

@Override public RemotePublisher createRemotePublisher(Run<?, ?> build) throws IOException {
try {
getConnectionSupplier().connection(); // make sure we start a local server and create table first
Expand Down Expand Up @@ -269,6 +284,34 @@ private List<CaseResult> retrieveCaseResult(String whereCondition) {
});
}


public Void deleteRun() {
return query(connection -> {
try (PreparedStatement statement = connection.prepareStatement(
"DELETE FROM caseResults WHERE job = ? AND build = ?"
)) {
statement.setString(1, job);
statement.setInt(2, build);

statement.execute();
}
return null;
});
}

public Void deleteJob() {
return query(connection -> {
try (PreparedStatement statement = connection.prepareStatement(
"DELETE FROM caseResults WHERE job = ?"
)) {
statement.setString(1, job);

statement.execute();
}
return null;
});
}

@Override
public List<PackageResult> getAllPackageResults() {
return query(connection -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package io.jenkins.plugins.junit.storage.database;

import hudson.Extension;
import hudson.model.Item;
import hudson.model.Run;
import hudson.model.listeners.ItemListener;
import hudson.model.listeners.RunListener;
import io.jenkins.plugins.junit.storage.FileJunitTestResultStorage;
import io.jenkins.plugins.junit.storage.JunitTestResultStorage;
import io.jenkins.plugins.junit.storage.TestResultImpl;

public class TestResultCleanupListener {

@Extension
public static class RunCleanupListener extends RunListener<Run> {
@Override
public void onDeleted(Run run) {
JunitTestResultStorage junitTestResultStorage = JunitTestResultStorage.find();
if (junitTestResultStorage instanceof FileJunitTestResultStorage) {
return;
}

if (junitTestResultStorage instanceof DatabaseTestResultStorage) {
DatabaseTestResultStorage storage = (DatabaseTestResultStorage) junitTestResultStorage;
if (storage.isSkipCleanupRunsOnDeletion()) {
return;
}
}

TestResultImpl testResult = junitTestResultStorage.load(run.getParent().getFullName(), run.getNumber());

if (testResult instanceof DatabaseTestResultStorage.TestResultStorage) {
DatabaseTestResultStorage.TestResultStorage storage = (DatabaseTestResultStorage.TestResultStorage) testResult;

storage.deleteRun();
}
}
}

@Extension
public static class JobCleanupListener extends ItemListener {
@Override
public void onDeleted(Item item) {
JunitTestResultStorage junitTestResultStorage = JunitTestResultStorage.find();
if (junitTestResultStorage instanceof FileJunitTestResultStorage) {
return;
}

if (junitTestResultStorage instanceof DatabaseTestResultStorage) {
DatabaseTestResultStorage storage = (DatabaseTestResultStorage) junitTestResultStorage;
if (storage.isSkipCleanupRunsOnDeletion()) {
return;
}
}

TestResultImpl testResult = junitTestResultStorage.load(item.getFullName(), 0);

if (testResult instanceof DatabaseTestResultStorage.TestResultStorage) {
DatabaseTestResultStorage.TestResultStorage storage = (DatabaseTestResultStorage.TestResultStorage) testResult;
storage.deleteJob();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
<f:entry field="skipCleanupRunsOnDeletion">
<f:checkbox title="${%Skip cleanup of test result on build deletion}"/>
</f:entry>
</j:jelly>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<p>If checked tests results will not be automatically removed when a job or build is deleted.</p>

<p>If you need more complex test result cleanup then please raise an issue on GitHub.</p>
Loading

0 comments on commit ff44866

Please sign in to comment.