Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup test results when run/job is deleted #111

Merged
merged 1 commit into from
Aug 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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