com.slickqa.executioner.executable.DeployVerticle Maven / Gradle / Ivy
package com.slickqa.executioner.executable;
import com.slickqa.executioner.base.Addresses;
import com.slickqa.executioner.cmdlineagent.CommandLineAgentVerticle;
import com.slickqa.executioner.dummyagent.DummyAgentVerticle;
import com.slickqa.executioner.web.ExecutionerWebVerticle;
import com.slickqa.executioner.workqueue.ExecutionerWorkQueueVerticle;
import io.vertx.core.*;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.eventbus.Message;
import io.vertx.core.file.FileSystem;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* This verticle deploys all the others.
*/
public class DeployVerticle extends AbstractVerticle {
private boolean[] webVerticleStarted;
private FileSystem fs;
private EventBus eventBus;
private Logger log;
private String locationOfAgents;
private String agentImagesDirectory;
private Map redeploy;
private Map agents;
private int dummyAgentCounter;
protected boolean allWebVerticleStarted() {
for(boolean individual : webVerticleStarted) {
if(!individual) {
return false;
}
}
return true;
}
public boolean deployAgent(JsonObject config) {
String name = config.getString("name");
if(name == null) {
log.error("Trying to deploy an agent without a name, must be a bug. JSON={0}", config.encodePrettily());
} else {
String type = config.getString("type", "DummyAgent");
DeploymentOptions options = new DeploymentOptions();
options.setConfig(config);
Verticle agent = null;
if("DummyAgent".equalsIgnoreCase(type)) {
if(!config.containsKey("agentNumber")) {
config = config.put("agentNumber", ++dummyAgentCounter);
}
agent = new DummyAgentVerticle();
} else if("CommandLineAgent".equalsIgnoreCase(type)) {
agent = new CommandLineAgentVerticle();
} else if(config.getString("className") != null) {
try {
Class agentClass = Class.forName(config.getString("className"));
if(!Verticle.class.isAssignableFrom(agentClass)) {
log.error("Class {0} for agent {1} is not a Verticle! Config: {2}",
config.getString("className"), name, config.encodePrettily());
return false;
}
agent = (Verticle) agentClass.newInstance();
} catch (ClassNotFoundException e) {
log.error("Problem when trying to deploy an agent with class " +
config.getString("className") + " from config: " + config.encodePrettily(), e);
return false;
} catch (InstantiationException | IllegalAccessException e) {
log.error("Problem when trying to instantiate agent " + name + "'s class " +
config.getString("className") + ": ", e);
e.printStackTrace();
}
}
if(agent != null) {
vertx.deployVerticle(agent, options);
agents.put(name, config);
return true;
}
}
return false;
}
public boolean deployConnector(JsonObject config) {
String connectorClassName = config.getString("className", null);
if(connectorClassName == null) {
log.error("Connector config className missing from connector config: {0}", config.encodePrettily());
return false;
}
Class connector = null;
try {
connector = this.getClass().getClassLoader().loadClass(connectorClassName);
} catch (ClassNotFoundException e) {
log.error("Unable to load class '" + connectorClassName + "': ", e);
return false;
}
if(!AbstractVerticle.class.isAssignableFrom(connector)) {
log.error("Connector class {0} is not a Verticle, we can't deploy it!", connectorClassName);
return false;
}
AbstractVerticle instance = null;
try {
instance = (AbstractVerticle)connector.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
log.error("Error creating instance of connector class '" + connectorClassName + "': ", e);
return false;
}
DeploymentOptions options = new DeploymentOptions().setConfig(config);
vertx.deployVerticle(instance, options);
return true;
}
public void loadAgents(Long id) {
Set loaded = new HashSet<>();
Set pathsLoaded = new HashSet<>();
fs.readDir(locationOfAgents, readDirResult -> {
if(readDirResult.succeeded()) {
log.info("Starting load of agents from {0}.", locationOfAgents);
for(String path : readDirResult.result()) {
if(path.endsWith(".json")) {
fs.readFile(path, readFileResult -> {
pathsLoaded.add(path);
if(readFileResult.succeeded()) {
JsonObject config = new JsonObject(readFileResult.result().toString());
if(!config.containsKey("name")) {
// name can come from file name
String name = path.substring(path.lastIndexOf('/') + 1);
name = name.substring(0, name.length() - 5); // take off .json
config = config.put("name", name);
}
String agentName = config.getString("name");
if(!agents.containsKey(agentName)) {
log.info("Attempting to deploy {0}", agentName);
if(deployAgent(config)) {
loaded.add(agentName);
} else {
log.error("Unable to deploy agent {0} with json: {1}", agentName, config.encodePrettily());
}
} else {
// already deployed, let's see if it's different
if(!config.equals(agents.get(agentName))) {
log.info("config {0} does not equal {1}", config.encodePrettily(), agents.get(agentName).encodePrettily());
// even though we are unloading and reloading, we'll record this as "loaded"
loaded.add(agentName);
// it's different, reload
redeploy.put(agentName, config);
eventBus.send(Addresses.AgentStopBaseAddress + agentName, null);
} else {
loaded.add(agentName);
}
}
} else {
log.error("Unable to read file {0}");
}
if(pathsLoaded.containsAll(readDirResult.result())) {
// look for agents to "unload"
Set toUnload = new HashSet<>(agents.keySet());
toUnload.removeAll(loaded);
for(String agentNameToUnload : toUnload) {
log.info("Unloading agent {0}", agentNameToUnload);
eventBus.send(Addresses.AgentStopBaseAddress + agentNameToUnload, null);
}
}
});
}
}
} else {
log.error("Unable to load agents from directory [" + locationOfAgents + "] : ", readDirResult.cause());
}
});
}
public void cleanupImages(Long id) {
log.info("Cleanup of agent-images started.");
fs.readDir(agentImagesDirectory, topLevelDirResponse -> {
for(String dir : topLevelDirResponse.result()) {
fs.readDir(dir, agentDirResponse -> {
if(agentDirResponse.succeeded()) {
for(String imagePath : agentDirResponse.result()) {
fs.props(imagePath, imagePropertyResponse -> {
if(imagePropertyResponse.succeeded()) {
if(LocalDateTime.now().minusSeconds(10).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() > imagePropertyResponse.result().lastModifiedTime()) {
log.info("Cleaning up {0}", imagePath);
fs.delete(imagePath, deleteResult -> {
if(deleteResult.failed()) {
log.warn("Unable to delete {0}", imagePath);
}
});
}
} else {
log.warn("Could not get the properties of " + imagePath + ": ", imagePropertyResponse.cause());
}
});
}
}
});
}
});
}
@Override
public void start(Future startFuture) {
dummyAgentCounter = 0;
redeploy = new HashMap<>();
agents = new HashMap<>();
log = LoggerFactory.getLogger(DeployVerticle.class);
fs = vertx.fileSystem();
eventBus = vertx.eventBus();
log.info("Starting Work Queue.");
// configuration
JsonObject config = vertx.getOrCreateContext().config();
int numberOfWebVerticles = config.getInteger("webVerticles", 4);
locationOfAgents = config.getString("agentsDir", "conf.d");
agentImagesDirectory = config.getString("agentImagesDir", "agent-images");
int cleanupImagesEvery = config.getInteger("cleanupImagesEvery", 10);
int checkAgentsEvery = config.getInteger("checkAgentsEvery", 60);
webVerticleStarted = new boolean[numberOfWebVerticles];
for(int i = 0; i < numberOfWebVerticles; i++) {
webVerticleStarted[i] = false;
}
// undeploy an agent as soon as it sends out a stop message
eventBus.consumer(Addresses.AgentDeleteAnnounce, message -> {
Object body = message.body();
if(body instanceof JsonObject) {
JsonObject agent = (JsonObject) body;
log.info("Undeploying agent {0} with deployment id {1}", agent.getString("name"), agent.getString("deploymentId"));
vertx.undeploy(agent.getString("deploymentId"), whenFinished -> {
if(whenFinished.succeeded()) {
// remove it as a deployed agent
agents.remove(agent.getString("name"));
// if it's scheduled to be redeployed, do that
if(redeploy.containsKey(agent.getString("name"))) {
log.info("Redeploying agent {0}", agent.getString("name"));
JsonObject agentConfig = redeploy.remove(agent.getString("name"));
deployAgent(agentConfig);
} else {
log.info("Undeployment of agent {0} finished, have a nice day!", agent.getString("name"));
}
} else {
log.error("Undeployment of agent " + agent.getString("name") + "failed: ", whenFinished.cause());
}
});
} else {
log.error("Recieved unknown type ({0}) for message delete announce: ", body.getClass().getName(), body.toString());
}
});
DeploymentOptions options = new DeploymentOptions();
options.setConfig(config);
vertx.deployVerticle(new ExecutionerWorkQueueVerticle(), options, onFinished -> {
if(onFinished.succeeded()) {
for(int i = 0; i < numberOfWebVerticles; i++) {
final int verticleNum = i;
vertx.deployVerticle(new ExecutionerWebVerticle(), options, result -> {
webVerticleStarted[verticleNum] = true;
if(allWebVerticleStarted()) {
log.info("All {0} web verticles started.", numberOfWebVerticles);
startFuture.complete();
}
});
}
} else {
log.error("Starting WorkQueue Failed: ", onFinished.cause());
startFuture.fail(onFinished.cause());
}
});
loadAgents(0L);
// ----- Deploy connectors -----
for(Object connectorConfig : config.getJsonArray("connectors", new JsonArray())) {
if(connectorConfig instanceof JsonObject) {
deployConnector((JsonObject)connectorConfig);
}
}
vertx.setPeriodic(cleanupImagesEvery * 1000, this::cleanupImages);
vertx.setPeriodic(checkAgentsEvery * 1000, this::loadAgents);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy