package com.redhat.lightblue.migrator;
import java.util.*;
import org.junit.Test;
import org.junit.Assert;
import org.junit.Before;
import org.junit.After;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.redhat.lightblue.client.*;
import com.redhat.lightblue.client.request.*;
import com.redhat.lightblue.client.response.*;
import com.redhat.lightblue.client.http.*;
import com.redhat.lightblue.client.request.data.*;
import static com.redhat.lightblue.util.test.AbstractJsonNodeTest.loadJsonNode;
public class MigratorTest extends AbstractMigratorController {
private String versionMigrationJob;
private String versionMigrationConfiguration;
private String versionSourceCustomer;
private String versionDestinationCustomer;
public MigratorTest() throws Exception {
}
@Override
protected JsonNode[] getMetadataJsonNodes() throws Exception {
ObjectNode jsonActiveExecution = (ObjectNode) loadJsonNode("./activeExecution.json");
ObjectNode jsonMigrationJob = (ObjectNode) loadJsonNode("./migrationJob.json");
ObjectNode jsonMigrationConfiguration = (ObjectNode) loadJsonNode("./migrationConfiguration.json");
ObjectNode jsonSourceCustomer = (ObjectNode) loadJsonNode("./test/metadata/source-customer.json");
ObjectNode jsonDestinationCustomer = (ObjectNode) loadJsonNode("./test/metadata/destination-customer.json");
versionMigrationJob = parseEntityVersion(jsonMigrationJob);
versionMigrationConfiguration = parseEntityVersion(jsonMigrationConfiguration);
versionSourceCustomer = parseEntityVersion(jsonSourceCustomer);
versionDestinationCustomer = parseEntityVersion(jsonDestinationCustomer);
return new JsonNode[]{
removeHooks(grantAnyoneAccess(jsonMigrationJob)),
removeHooks(grantAnyoneAccess(jsonMigrationConfiguration)),
removeHooks(grantAnyoneAccess(jsonActiveExecution)),
jsonSourceCustomer,
jsonDestinationCustomer
};
}
public void clearData() throws Exception {
LightblueClient cli = new LightblueHttpClient();
DataDeleteRequest req = new DataDeleteRequest("activeExecution", null);
req.where(Query.withValue("objectType", Query.eq, "activeExecution"));
cli.data(req);
req = new DataDeleteRequest("migrationJob", null);
req.where(Query.withValue("objectType", Query.eq, "migrationJob"));
cli.data(req);
req = new DataDeleteRequest("migrationConfiguration", null);
req.where(Query.withValue("objectType", Query.eq, "migrationConfiguration"));
cli.data(req);
req = new DataDeleteRequest("sourceCustomer", "1.0.0");
req.where(Query.withValue("objectType", Query.eq, "sourceCustomer"));
cli.data(req);
}
@Test
public void migrateTest() throws Exception {
clearData();
Breakpoint.clearAll();
loadData("migrationConfiguration", versionMigrationConfiguration, "./test/data/load-migration-configurations.json");
loadData("migrationJob", versionMigrationJob, "./test/data/load-migration-jobs.json");
loadData("sourceCustomer", versionSourceCustomer, "./test/data/load-source-customers.json");
MainConfiguration cfg = new MainConfiguration();
cfg.setName("continuum");
cfg.setHostName("hostname");
Controller controller = new Controller(cfg);
// Stop when we retrieve source docs
Breakpoint.stop("Migrator:sourceDocs");
controller.start();
Breakpoint.waitUntil("Migrator:sourceDocs");
// We got source docs, peek
Thread[] threads = new Thread[1];
Assert.assertEquals(1, controller.
getMigrationProcesses().
get("customerMigration_0").mig.getMigratorThreads().enumerate(threads));
Migrator m = (Migrator) threads[0];
Assert.assertEquals(5, m.getSourceDocs().size());
Breakpoint.stop("Migrator:complete");
Breakpoint.resume("Migrator:sourceDocs");
System.out.println("Waiting for completion");
Breakpoint.waitUntil("Migrator:complete");
LightblueClient cli = new LightblueHttpClient();
DataFindRequest req = new DataFindRequest("destCustomer", "1.0.0");
req.select(Projection.includeFieldRecursively("*"));
req.where(Query.withValue("objectType", Query.eq, "destCustomer"));
req.sort(Sort.asc("_id"));
JsonNode[] ret = cli.data(req, JsonNode[].class);
System.out.println("Complete");
Breakpoint.resume("Migrator:complete");
Assert.assertEquals(5, ret.length);
System.out.println("Interrupt controller");
System.out.println("Interrupting "+controller.getMigrationProcesses().get("customerMigration_0").mig.getName());
controller.getMigrationProcesses().get("customerMigration_0").mig.setStopped();
controller.setStopped();
System.out.println("Test ends");
Thread.sleep(1000);
}
@Test
public void threadMonitoringTest_interrupt() throws Exception {
clearData();
Breakpoint.clearAll();
loadData("migrationConfiguration", versionMigrationConfiguration, "./test/data/load-migration-configurations-interruptiblemigrator.json");
loadData("migrationJob", versionMigrationJob, "./test/data/load-migration-jobs.json");
loadData("sourceCustomer", versionSourceCustomer, "./test/data/load-source-customers.json");
MainConfiguration cfg = new MainConfiguration();
cfg.setName("continuum");
cfg.setHostName("hostname");
// 10msec thread timeout
cfg.setThreadTimeout(10l);
Controller controller = new Controller(cfg);
// We need this here to make sure controller has a chance to
// initialize the threads before we start waiting for them
Breakpoint.stop("Controller:createconfig");
Breakpoint.stop("Migrator:interrupted");
// This will put a breakpoint to the test class
Breakpoint.stop("Migrator:getSourceDocuments");
controller.start();
Breakpoint.waitUntil("Controller:createconfig");
Breakpoint.resume("Controller:createconfig");
Breakpoint.waitUntil("Migrator:getSourceDocuments");
Breakpoint.resume("Migrator:getSourceDocuments");
Breakpoint.stop("ThreadMonitor:check");
// Wait for 100 msec, the thread monitor should interrupt the thread
try {
Thread.sleep(100);
} catch (Exception e) {
}
controller.getThreadMonitor().runNow();
System.out.println("Waiting until check wakes up");
Breakpoint.waitUntil("ThreadMonitor:check");
Breakpoint.resume("ThreadMonitor:check");
System.out.println("Thread monitor woke up");
Breakpoint.waitUntil("Migrator:interrupted");
Breakpoint.resume("Migrator:interrupted");
Assert.assertTrue(InterruptibleStuckMigrator.numInterrupted > 0);
}
@Test
public void threadMonitoringTest_abandon() throws Exception {
clearData();
Breakpoint.clearAll();
loadData("migrationConfiguration", versionMigrationConfiguration, "./test/data/load-migration-configurations-uninterruptiblemigrator.json");
loadData("migrationJob", versionMigrationJob, "./test/data/load-migration-jobs.json");
loadData("sourceCustomer", versionSourceCustomer, "./test/data/load-source-customers.json");
MainConfiguration cfg = new MainConfiguration();
cfg.setName("continuum");
cfg.setHostName("hostname");
// 10msec thread timeout
cfg.setThreadTimeout(10l);
Controller controller = new Controller(cfg);
// We need this here to make sure controller has a chance to
// initialize the threads before we start waiting for them
Breakpoint.stop("Controller:createconfig");
Breakpoint.stop("Migrator:interrupted");
// This will put a breakpoint to the test class
Breakpoint.stop("Migrator:getSourceDocuments");
controller.start();
Breakpoint.waitUntil("Controller:createconfig");
Breakpoint.resume("Controller:createconfig");
Breakpoint.waitUntil("Migrator:getSourceDocuments");
Breakpoint.resume("Migrator:getSourceDocuments");
Breakpoint.stop("ThreadMonitor:check");
// Wait for 100 msec, the thread monitor should interrupt the thread
try {
Thread.sleep(100);
} catch (Exception e) {
}
controller.getThreadMonitor().runNow();
System.out.println("Waiting until check wakes up");
Breakpoint.waitUntil("ThreadMonitor:check");
Breakpoint.resume("ThreadMonitor:check");
System.out.println("Thread monitor woke up");
try {
Thread.sleep(100);
} catch (Exception e) {
}
controller.getThreadMonitor().runNow();
try {
Thread.sleep(100);
} catch (Exception e) {
}
// Thread must be abandoned by now
Assert.assertEquals(1, controller.getThreadMonitor().getThreadCount(ThreadMonitor.Status.abandoned));
}
@Test
public void cleanupOldJobs() throws Exception {
clearData();
Breakpoint.clearAll();
loadData("migrationConfiguration", versionMigrationConfiguration, "./test/data/load-migration-configurations-uninterruptiblemigrator.json");
MainConfiguration cfg = new MainConfiguration();
cfg.setName("continuum");
cfg.setHostName("hostname");
Controller controller = new Controller(cfg);
LightblueClient cli = controller.getLightblueClient();
Date now = new Date();
Date ago = new Date(System.currentTimeMillis() - 30l * 24l * 60l * 60l * 1000l);
List<MigrationJob> jobs = new ArrayList<>();
MigrationJob j = new MigrationJob();
j.set_id("completed_not_generated_old");
j.setConfigurationName("c");
j.setScheduledDate(new Date());
j.setStatus("completed");
j.setGenerated(false);
j.setJobExecutions(new ArrayList<MigrationJob.JobExecution>());
MigrationJob.JobExecution je = new MigrationJob.JobExecution();
je.setStatus("completed");
je.setOwnerName("name");
je.setHostName("host");
je.setActualStartDate(ago);
je.setActualEndDate(ago);
j.getJobExecutions().add(je);
jobs.add(j);
j = new MigrationJob();
j.set_id("completed_generated_old");
j.setConfigurationName("c");
j.setScheduledDate(new Date());
j.setStatus("completed");
j.setGenerated(true);
j.setJobExecutions(new ArrayList<MigrationJob.JobExecution>());
je = new MigrationJob.JobExecution();
je.setStatus("completed");
je.setOwnerName("name");
je.setHostName("host");
je.setActualStartDate(ago);
je.setActualEndDate(ago);
j.getJobExecutions().add(je);
jobs.add(j);
j = new MigrationJob();
j.set_id("failed_not_generated_old");
j.setConfigurationName("c");
j.setScheduledDate(new Date());
j.setStatus("failed");
j.setGenerated(false);
j.setJobExecutions(new ArrayList<MigrationJob.JobExecution>());
je = new MigrationJob.JobExecution();
je.setStatus("failed");
je.setOwnerName("name");
je.setHostName("host");
je.setActualStartDate(now);
je.setActualEndDate(now);
j.getJobExecutions().add(je);
jobs.add(j);
j = new MigrationJob();
j.set_id("failed_generated_old");
j.setConfigurationName("c");
j.setScheduledDate(new Date());
j.setStatus("failed");
j.setGenerated(true);
j.setJobExecutions(new ArrayList<MigrationJob.JobExecution>());
je = new MigrationJob.JobExecution();
je.setStatus("failed");
je.setOwnerName("name");
je.setHostName("host");
je.setActualStartDate(now);
je.setActualEndDate(now);
j.getJobExecutions().add(je);
jobs.add(j);
j = new MigrationJob();
j.set_id("completed_not_generated_new");
j.setConfigurationName("c");
j.setScheduledDate(new Date());
j.setStatus("completed");
j.setGenerated(false);
j.setJobExecutions(new ArrayList<MigrationJob.JobExecution>());
je = new MigrationJob.JobExecution();
je.setOwnerName("name");
je.setStatus("completed");
je.setHostName("host");
je.setActualStartDate(now);
je.setActualEndDate(now);
j.getJobExecutions().add(je);
jobs.add(j);
j = new MigrationJob();
j.set_id("completed_generated_new");
j.setConfigurationName("c");
j.setScheduledDate(new Date());
j.setStatus("completed");
j.setGenerated(true);
j.setJobExecutions(new ArrayList<MigrationJob.JobExecution>());
je = new MigrationJob.JobExecution();
je.setOwnerName("name");
je.setStatus("completed");
je.setHostName("host");
je.setActualStartDate(now);
je.setActualEndDate(now);
j.getJobExecutions().add(je);
jobs.add(j);
j = new MigrationJob();
j.set_id("available_generated_new");
j.setConfigurationName("c");
j.setScheduledDate(new Date());
j.setStatus("available");
j.setGenerated(true);
j.setJobExecutions(new ArrayList<MigrationJob.JobExecution>());
je = new MigrationJob.JobExecution();
je.setOwnerName("name");
je.setStatus("available");
je.setHostName("host");
je.setActualStartDate(now);
je.setActualEndDate(now);
j.getJobExecutions().add(je);
jobs.add(j);
DataInsertRequest req = new DataInsertRequest("migrationJob", null);
req.create(jobs);
cli.data(req);
DataFindRequest f = new DataFindRequest("migrationJob", null);
f.select(Projection.includeField("*"));
MigrationJob[] all = cli.data(f, MigrationJob[].class);
CleanupThread t = new CleanupThread(controller);
t.cleanupOldJobs(cli, new Date(System.currentTimeMillis() - 60000l));
f = new DataFindRequest("migrationJob", null);
f.select(Projection.includeField("*"));
MigrationJob[] cleanedup = cli.data(f, MigrationJob[].class);
Assert.assertTrue(cleanedup.length < all.length && all.length > 0);
for (MigrationJob job : cleanedup) {
Assert.assertNotEquals("completed_generated_old", job.get_id());
}
for (MigrationJob l : jobs) {
if (!l.get_id().equals("completed_generated_old")) {
boolean found = false;
for (MigrationJob k : cleanedup) {
if (k.get_id().equals(l.get_id())) {
found = true;
break;
}
}
Assert.assertTrue(found);
}
}
}
@Test
public void enableStuckJobs() throws Exception {
clearData();
Breakpoint.clearAll();
loadData("migrationConfiguration", versionMigrationConfiguration, "./test/data/load-migration-configurations-uninterruptiblemigrator.json");
MainConfiguration cfg = new MainConfiguration();
cfg.setName("continuum");
cfg.setHostName("hostname");
Controller controller = new Controller(cfg);
LightblueClient cli = controller.getLightblueClient();
Date now = new Date();
Date ago = new Date(System.currentTimeMillis() - 5l * 60l * 60l * 1000l);
List<MigrationJob> jobs = new ArrayList<>();
MigrationJob j = new MigrationJob();
j.set_id("completed_not_generated_old");
j.setConfigurationName("c");
j.setScheduledDate(new Date());
j.setStatus("completed");
j.setGenerated(false);
j.setJobExecutions(new ArrayList<MigrationJob.JobExecution>());
MigrationJob.JobExecution je = new MigrationJob.JobExecution();
je.setStatus("completed");
je.setOwnerName("name");
je.setHostName("host");
je.setActualStartDate(ago);
je.setActualEndDate(ago);
j.getJobExecutions().add(je);
jobs.add(j);
j = new MigrationJob();
j.set_id("active_old");
j.setConfigurationName("c");
j.setScheduledDate(new Date());
j.setStatus("active");
j.setGenerated(true);
j.setJobExecutions(new ArrayList<MigrationJob.JobExecution>());
je = new MigrationJob.JobExecution();
je.setStatus("active");
je.setOwnerName("name");
je.setHostName("host");
je.setActualStartDate(ago);
je.setActualEndDate(ago);
j.getJobExecutions().add(je);
jobs.add(j);
j = new MigrationJob();
j.set_id("active_new");
j.setConfigurationName("c");
j.setScheduledDate(new Date());
j.setStatus("active");
j.setGenerated(false);
j.setJobExecutions(new ArrayList<MigrationJob.JobExecution>());
je = new MigrationJob.JobExecution();
je.setStatus("active");
je.setOwnerName("name");
je.setHostName("host");
je.setActualStartDate(now);
je.setActualEndDate(now);
j.getJobExecutions().add(je);
jobs.add(j);
DataInsertRequest req = new DataInsertRequest("migrationJob", null);
req.create(jobs);
cli.data(req);
DataFindRequest f = new DataFindRequest("migrationJob", null);
f.select(Projection.includeField("*"));
MigrationJob[] all = cli.data(f, MigrationJob[].class);
CleanupThread t = new CleanupThread(controller);
t.enableStuckJobs(cli, new Date(System.currentTimeMillis() - 60000l));
f = new DataFindRequest("migrationJob", null);
f.select(Projection.includeField("*"));
MigrationJob[] enabled = cli.data(f, MigrationJob[].class);
Assert.assertEquals(all.length, enabled.length);
for (MigrationJob job : enabled) {
if (job.get_id().equals("active_old")) {
Assert.assertEquals("available", job.getStatus());
}
}
}
@Test
public void healthcheckTest() throws Exception {
clearData();
Breakpoint.clearAll();
loadData("migrationConfiguration", versionMigrationConfiguration, "./test/data/load-migration-configurations-cctest.json");
MainConfiguration cfg = new MainConfiguration();
cfg.setName("continuum");
cfg.setHostName("hostname");
Controller controller = new Controller(cfg);
Breakpoint.stop("Controller:createconfig");
Breakpoint.stop("CCC:start");
controller.start();
Breakpoint.waitUntil("Controller:createconfig");
// Here all threads are created and running
Breakpoint.waitUntil("CCC:start");
Breakpoint.resume("CCC:start");
Assert.assertTrue(controller.getMigrationProcesses().get("customerMigration_0").ccc.isAlive());
// Kill the ccc thread
Breakpoint.stop("CCC:end");
controller.getMigrationProcesses().get("customerMigration_0").ccc.setStopped();
Breakpoint.waitUntil("CCC:end");
Thread.sleep(150);
Assert.assertFalse(controller.getMigrationProcesses().get("customerMigration_0").ccc.isAlive());
controller.healthcheck(controller.getMigrationProcesses().get("customerMigration_0").cfg);
Assert.assertTrue(controller.getMigrationProcesses().get("customerMigration_0").ccc.isAlive());
}
}