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

Auto deploy feed version #361

Merged
merged 76 commits into from
Jun 15, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
526c126
feat(auto-deploy): auto-deploy to OTP when a new feed version is proc…
landonreed Dec 18, 2020
0f45633
Merge branch 'dev' into auto-deploy
Jan 12, 2021
21919cb
refactor: Initial auto deploy feed version work
Jan 18, 2021
f7bafa6
refactor(FeedVersionTest): Removed unused imports (hangover from test…
Jan 18, 2021
82b7a6d
refactor(Added an additional test): Added a unit test to cover high s…
Jan 19, 2021
ac16806
test: add mock GTFS that expires in 2099
landonreed Jan 19, 2021
3f3e9f9
refactor(Addressed PR feedback): Updated DB call and unit tests
Jan 20, 2021
c37cae0
refactor(AutoDeployFeedJob): Added fail status if job unable to be de…
Jan 20, 2021
e734c03
Merge branch 'dev' into auto-deploy
Jan 21, 2021
6b12c36
refactor(FeedVersion.java): use select distinct for checking error types
landonreed Jan 21, 2021
ce092d6
refactor: provide access to MonitorableJob#subjobs for testing
landonreed Jan 21, 2021
bddfa11
refactor(AutoDeployFeedJobTest): Updated assertions with expected text
Jan 21, 2021
da212c7
refactor(auto-deploy): entirely skip auto deploy job on certain condi…
landonreed Jan 21, 2021
b8d6d91
refactor(Addressed PR feedback): Addressed PR comments
Jan 25, 2021
67fd142
Merge branch 'auto-deploy' of https://github.com/ibi-group/datatools-…
Jan 25, 2021
853862f
refactor(ProcessSingleFeedJob): Added the missing DataManager import
Jan 25, 2021
93400d4
refactor(AutoDeployFeedJobTest): Removed '*' use in imports
Jan 27, 2021
30dde34
refactor(AutoDeployFeedJob): Updated method name
Jan 27, 2021
67cdad4
refactor(AutoDeployFeedJob): Removed unused import
Jan 27, 2021
7e59914
Merge branch 'dev' into auto-deploy
Jan 28, 2021
f3b90b6
refactor(AutoDeployJob): check for active feed fetches before deploying
landonreed Feb 11, 2021
702a5eb
refactor(Auto deploy race conditions unit test): Additional unit test…
Feb 18, 2021
8e27a03
refactor(Addressed PR feedback): Updated how the MonitorableJob messa…
Feb 19, 2021
f172917
refactor(AutoDeployFeedJobTest): Updated base gtfs zip file used to t…
Feb 22, 2021
629ffd2
refactor(AutoDeployFeedJob): Created separate feed sources for each u…
Feb 23, 2021
30e6dcf
refactor(AutoDeployFeedJobTest): Created separate server, project and…
Feb 23, 2021
51f7eda
refactor(AutoDeployFeedJobTest): Fixed incorrect id assignment
Feb 23, 2021
70014be
refactor(AutoDeployFeedJobTest): Reduced the number of feed sources u…
Feb 23, 2021
84586e2
refactor(MonitorableJob): avoid duplicate completion in completeSucce…
landonreed Feb 23, 2021
1d21bf4
refactor(Updated approach to auto deploy): Auto deploy logic now hand…
Mar 17, 2021
135a592
refactor(Fixed merge conflicts): Fixed merge conflicts in DeployJob
Mar 17, 2021
8ba6fc5
Merge branch 'dev' into auto-deploy
Mar 22, 2021
3f9d8b5
refactor(AutoDeployFeedJobTest): Updated junit annotations
Mar 22, 2021
00e451c
Update src/main/java/com/conveyal/datatools/manager/jobs/AutoDeployFe…
Mar 23, 2021
2cb11ea
refactor(Addressed PR feedback): Updated the auto deploy job logic an…
Mar 25, 2021
238d4c6
Update src/test/resources/com/conveyal/datatools/gtfs/fake-agency-wit…
Mar 29, 2021
de1ce5c
Update src/test/java/com/conveyal/datatools/manager/jobs/AutoDeployFe…
Mar 29, 2021
6987ec9
refactor(Addressed PR feedback): Update to focus on deployment feed v…
Mar 29, 2021
801c898
refactor(AutoDeployJob): Updated to use feed sources associated with …
Mar 30, 2021
e5d3a4d
refactor(FeedVersion): Corrected comments
Mar 30, 2021
1e38a4c
refactor: change some logic in AutoDeployJob
evansiroky Mar 31, 2021
9efd934
Merge pull request #372 from ibi-group/auto-deploy-eas
Mar 31, 2021
1a282cf
Update src/main/java/com/conveyal/datatools/manager/jobs/AutoDeployJo…
Mar 31, 2021
1175cdb
Merge branch 'dev' into auto-deploy
Apr 7, 2021
24f1725
refactor(Addressed PR feedback): Addressed PR feedback
Apr 8, 2021
3c3f66b
refactor: add abstract FeedSourceJob and FeedVersionJob classes
landonreed Apr 9, 2021
afaf3bc
refactor(FeedSource.java): Streamlined job instanceof check
Apr 9, 2021
243ef63
Update src/main/java/com/conveyal/datatools/manager/jobs/ProcessSingl…
Apr 9, 2021
413a2b4
Update src/main/java/com/conveyal/datatools/common/status/FeedSourceJ…
Apr 12, 2021
fa3d584
Update src/main/java/com/conveyal/datatools/common/status/FeedVersion…
Apr 12, 2021
6fd03b5
refactor(FeedSource.java): Reverted job in progress check back to a m…
Apr 13, 2021
6cc2657
Merge branch 'auto-deploy' of https://github.com/ibi-group/datatools-…
Apr 13, 2021
38cb1c2
refactor(FeedSource.java): Removed obsolete comments/code
Apr 13, 2021
baad68a
refactor(Added pinned feed versions to Deployment): Added pinned feed…
Apr 15, 2021
cafb0e5
Update src/main/java/com/conveyal/datatools/manager/models/Deployment…
Apr 19, 2021
2aea8ae
refactor(AutoDeployJob.java): Removed notification regarding advancin…
Apr 21, 2021
e65a337
refactor(Addressed PR feedback): Addressed PR feedback
Apr 28, 2021
83d1134
refactor: consolidate AutoDeployJob initialization in ProcessSingleFe…
evansiroky Apr 28, 2021
2d3b82b
Merge pull request #375 from ibi-group/auto-deploy-eas
Apr 30, 2021
0fb54ca
test: fix GisExportJobTest
evansiroky May 2, 2021
d209aca
refactor(auto-deploy): add code to end auto-deployment in certain cases
evansiroky May 3, 2021
5818355
test: fix a failing test
evansiroky May 3, 2021
1c429e8
refactor: move post-deployment auto-deploy logic into method
evansiroky May 4, 2021
3083c5f
Merge pull request #377 from ibi-group/auto-deploy-eas
May 4, 2021
db462d0
refactor(auto-deploy): refactor a few things to improve auto-deployment
evansiroky May 7, 2021
c4d04c7
refactor: address PR review comments
evansiroky May 11, 2021
03e9d2e
Merge pull request #381 from ibi-group/auto-deploy-eas
evansiroky May 11, 2021
0f3f36f
refactor: make sure deployment is persisted in AutoDeployJob
evansiroky May 11, 2021
276aa46
Merge branch 'dev' into auto-deploy
May 21, 2021
2292e72
refactor: add FeedSource#versions
landonreed May 26, 2021
68e678c
refactor: Apply suggestions from code review
landonreed May 27, 2021
0a82db8
refactor(FeedSource): change versions to versionCount
landonreed May 28, 2021
51cca5c
Merge branch 'dev' into auto-deploy
Jun 1, 2021
e88392c
Merge branch 'dev' into auto-deploy
Jun 1, 2021
d196cc4
refactor(Fixed merge conflicts): Fixed merge conflicts
Jun 15, 2021
38c1b43
refactor(HandleCorruptGTFSFileTest.java): Updated test to include pro…
Jun 15, 2021
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 @@ -44,6 +44,7 @@ public abstract class MonitorableJob implements Runnable, Serializable {
protected List<MonitorableJob> subJobs = new ArrayList<>();

public enum JobType {
AUTO_DEPLOY_FEED_VERSION,
UNKNOWN_TYPE,
ARBITRARY_FEED_TRANSFORM,
BUILD_TRANSPORT_NETWORK,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.conveyal.datatools.manager.models.Project;
import com.conveyal.datatools.manager.persistence.Persistence;
import com.conveyal.datatools.manager.utils.json.JsonManager;
import com.mongodb.client.FindIterable;
import org.bson.Document;
import org.eclipse.jetty.http.HttpStatus;
import org.slf4j.Logger;
Expand Down Expand Up @@ -49,7 +50,7 @@
*/
public class DeploymentController {
private static final Logger LOG = LoggerFactory.getLogger(DeploymentController.class);
private static Map<String, DeployJob> deploymentJobsByServer = new HashMap<>();
private static final Map<String, DeployJob> deploymentJobsByServer = new HashMap<>();

/**
* Gets the deployment specified by the request's id parameter and ensure that user has access to the
Expand Down Expand Up @@ -376,6 +377,51 @@ private static boolean terminateEC2InstanceForDeployment(Request req, Response r
return true;
}

/**
* Queue a new {@link DeployJob} if there are no conflicting jobs assigned to the specified server.
* @param deployJob new deploy job to queue
* @return whether the deploy job was successfully queued or not
*/
public static boolean queueDeployJob(DeployJob deployJob) {
String serverId = deployJob.getServerId();
// Check that we can deploy to the specified target. (Any deploy job for the target that is presently active will
// cause a halt.)
if (deploymentJobsByServer.containsKey(serverId)) {
// There is a deploy job for the server. Check if it is active.
DeployJob conflictingDeployJob = deploymentJobsByServer.get(serverId);
if (conflictingDeployJob != null && !conflictingDeployJob.status.completed) {
// Another deploy job is actively being deployed to the server target.
LOG.error("New deploy job will not be queued due to active job in progress.");
br648 marked this conversation as resolved.
Show resolved Hide resolved
return false;
}
}

// For any previous deployments sent to the server/router combination, set deployedTo to null because
// this new one will overwrite it. NOTE: deployedTo for the current deployment will only be updated after the
// successful completion of the deploy job.
FindIterable<Deployment> deploymentsWithSameTarget = Deployment.retrieveDeploymentForServerAndRouterId(
serverId,
deployJob.getDeployment().routerId
);
for (Deployment oldDeployment : deploymentsWithSameTarget) {
LOG.info("Setting deployment target to null id={}", oldDeployment.id);
br648 marked this conversation as resolved.
Show resolved Hide resolved
Persistence.deployments.updateField(oldDeployment.id, "deployedTo", null);
}
// Finally, add deploy job to the heavy executor.
DataManager.heavyExecutor.execute(deployJob);
deploymentJobsByServer.put(serverId, deployJob);
return true;
}

public static DeployJob checkDeploymentInProgress(String deploymentId) {
for (DeployJob job : deploymentJobsByServer.values()) {
if (job.getDeploymentId().equals(deploymentId)) {
return job;
}
}
return null;
}
br648 marked this conversation as resolved.
Show resolved Hide resolved

/**
* HTTP controller to fetch information about provided EC2 machines that power ELBs running a trip planner.
*/
Expand All @@ -396,8 +442,9 @@ private static String deploy (Request req, Response res) {
String target = req.params("target");
Deployment deployment = getDeploymentWithPermissions(req, res);
Project project = Persistence.projects.getById(deployment.projectId);
if (project == null)
if (project == null) {
logMessageAndHalt(req, 400, "Internal reference error. Deployment's project ID is invalid");
}
// Get server by ID
OtpServer otpServer = Persistence.servers.getById(target);
if (otpServer == null) {
Expand All @@ -411,23 +458,6 @@ private static String deploy (Request req, Response res) {
logMessageAndHalt(req, 401, "User not authorized to deploy to admin-only target OTP server.");
}

// Check that we can deploy to the specified target. (Any deploy job for the target that is presently active will
// cause a halt.)
if (deploymentJobsByServer.containsKey(target)) {
// There is a deploy job for the server. Check if it is active.
DeployJob deployJob = deploymentJobsByServer.get(target);
if (deployJob != null && !deployJob.status.completed) {
// Job for the target is still active! Send a 202 to the requester to indicate that it is not possible
// to deploy to this target right now because someone else is deploying.
String message = String.format(
"Will not process request to deploy %s. Deployment currently in progress for target: %s",
deployment.name,
target);
LOG.warn(message);
logMessageAndHalt(req, HttpStatus.ACCEPTED_202, message);
}
}

// Get the URLs to deploy to.
List<String> targetUrls = otpServer.internalUrl;
if ((targetUrls == null || targetUrls.isEmpty()) && (otpServer.s3Bucket == null || otpServer.s3Bucket.isEmpty())) {
Expand All @@ -438,19 +468,17 @@ private static String deploy (Request req, Response res) {
);
}

// For any previous deployments sent to the server/router combination, set deployedTo to null because
// this new one will overwrite it. NOTE: deployedTo for the current deployment will only be updated after the
// successful completion of the deploy job.
for (Deployment oldDeployment : Deployment.retrieveDeploymentForServerAndRouterId(target, deployment.routerId)) {
LOG.info("Setting deployment target to null id={}", oldDeployment.id);
Persistence.deployments.updateField(oldDeployment.id, "deployedTo", null);
}

// Execute the deployment job and keep track of it in the jobs for server map.
DeployJob job = new DeployJob(deployment, userProfile, otpServer);
DataManager.heavyExecutor.execute(job);
deploymentJobsByServer.put(target, job);

if (!queueDeployJob(job)) {
// Job for the target is still active! Send a 202 to the requester to indicate that it is not possible
// to deploy to this target right now because someone else is deploying.
String message = String.format(
"Will not process request to deploy %s. Deployment currently in progress for target: %s",
br648 marked this conversation as resolved.
Show resolved Hide resolved
deployment.name,
target);
logMessageAndHalt(req, HttpStatus.ACCEPTED_202, message);
}
return SparkUtils.formatJobMessage(job.jobId, "Deployment initiating.");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.conveyal.datatools.manager.jobs;

import com.conveyal.datatools.common.status.MonitorableJob;
import com.conveyal.datatools.manager.DataManager;
br648 marked this conversation as resolved.
Show resolved Hide resolved
import com.conveyal.datatools.manager.auth.Auth0UserProfile;
import com.conveyal.datatools.manager.controllers.api.DeploymentController;
import com.conveyal.datatools.manager.models.*;
import com.conveyal.datatools.manager.persistence.Persistence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;

/**
* Auto deploy new feed version to OTP server if {@link Project#autoDeploy} is enabled and other conditions are met
* (e.g., feed version has no critical errors, active deployment is not in progress, etc.). This job must run after
* {@Link ValidateFeedJob} as it has a dependency on the outcome of {@Link FeedVersion#hasCriticalErrors}.
br648 marked this conversation as resolved.
Show resolved Hide resolved
*/
public class AutoDeployFeedJob extends MonitorableJob {
br648 marked this conversation as resolved.
Show resolved Hide resolved
public static final Logger LOG = LoggerFactory.getLogger(ValidateFeedJob.class);
br648 marked this conversation as resolved.
Show resolved Hide resolved

private FeedVersion feedVersion;
br648 marked this conversation as resolved.
Show resolved Hide resolved
private final FeedSource feedSource;

AutoDeployFeedJob(FeedVersion version, Auth0UserProfile owner, FeedSource source) {
super(owner, "Auto Deploy Feed", JobType.AUTO_DEPLOY_FEED_VERSION);
feedVersion = version;
feedSource = source;
}

@Override
public void jobLogic () {
if (!DataManager.isModuleEnabled("deployment")) {
// Do not attempt to deploy if module is disabled.
return;
}
Project project = feedSource.retrieveProject();
if (feedSource.deployable && project.pinnedDeploymentId != null && project.autoDeploy) {
// TODO: Get deployment, update feed version for feed source, and kick off deployment to server that
// deployment is currently pointed at.
Deployment deployment = Persistence.deployments.getById(project.pinnedDeploymentId);
if (deployment == null) {
LOG.error("Pinned deployment no longer exists.");
br648 marked this conversation as resolved.
Show resolved Hide resolved
// FIXME: Remove pinned deployment?
} else {
// If the deployment is already in progress, do not attempt auto-deploy.
// FIXME: Should this be placed in the DeploymentController#queueDeployJob body? Perhaps, since things
// could get jumbled when updating the deployment object in MongoDB if we're trying to simultaneously
// deploy to two servers at once.
DeployJob activeJob = DeploymentController.checkDeploymentInProgress(deployment.id);
if (activeJob != null) {
LOG.warn("Skipping deployment (deployment by {} in progress already)", activeJob.retrieveEmail());
return;
}
// As long as the feed does not have any critical errors, move forward with auto-deployment.
if (!feedVersion.hasCriticalErrors()) {
if (deployment.feedVersionIds == null) {
// FIXME: is it possible that no previous versions have been deployed?
deployment.feedVersionIds = new ArrayList<>();
}
// Remove previously defined version for this feed source.
for (FeedVersion versionToReplace : deployment.retrieveFullFeedVersions()) {
if (versionToReplace.feedSourceId.equals(feedSource.id)) {
deployment.feedVersionIds.remove(versionToReplace.id);
}
}
// Add new version ID TODO: Should we not do this if the feed source was not already applied?
deployment.feedVersionIds.add(feedVersion.id);
Persistence.deployments.replace(deployment.id, deployment);
// Send deployment (with new feed version) to most recently used server.
OtpServer server;
if (deployment.latest() != null) {
String latestServerId = deployment.latest().serverId;
server = Persistence.servers.getById(latestServerId);
if (server == null) {
LOG.error("Server with id {} no longer exists. Skipping deployment.", latestServerId);
return;
}
} else {
// FIXME: Should we deploy some other server if deployment has not previously been deployed?
LOG.error("Deployment {} has never been deployed. Skipping auto-deploy.", deployment.id);
return;
}
// Finally, queue up the new deploy job.
// FIXME: Assumption being that if this returns true the feed version has been deployed.
feedVersion.autoDeployed = DeploymentController.queueDeployJob(new DeployJob(deployment, owner, server));
landonreed marked this conversation as resolved.
Show resolved Hide resolved
if (feedVersion.autoDeployed) LOG.info("New deploy job initiated for {}", server.name);
else LOG.warn("Could not auto-deploy to {} due to conflicting active deployment.", server.name);
}
}
}
}
}
13 changes: 11 additions & 2 deletions src/main/java/com/conveyal/datatools/manager/jobs/DeployJob.java
Original file line number Diff line number Diff line change
Expand Up @@ -180,15 +180,24 @@ public OtpServer getOtpServer() {
return otpServer;
}

/**
* Primary constructor for kicking off a deployment of transit/OSM/config data to OpenTripPlanner.
*
* FIXME: It appears that DeployType#Replace is the only type used in the non-test code. Should the others be
br648 marked this conversation as resolved.
Show resolved Hide resolved
* removed?
* @param deployment the deployment (set of feed versions and configurations) to deploy
* @param owner the requesting user
* @param otpServer the server/ELB target for the deployment
*/
public DeployJob(Deployment deployment, Auth0UserProfile owner, OtpServer otpServer) {
this(deployment, owner, otpServer, null, DeployType.REPLACE);
}

public DeployJob(Deployment deployment, Auth0UserProfile owner, OtpServer otpServer, String bundlePath, DeployType deployType) {
protected DeployJob(Deployment deployment, Auth0UserProfile owner, OtpServer otpServer, String bundlePath, DeployType deployType) {
this("Deploying " + deployment.name, deployment, owner, otpServer, bundlePath, deployType);
}

public DeployJob(String jobName, Deployment deployment, Auth0UserProfile owner, OtpServer otpServer, String bundlePath, DeployType deployType) {
private DeployJob(String jobName, Deployment deployment, Auth0UserProfile owner, OtpServer otpServer, String bundlePath, DeployType deployType) {
// TODO add new job type or get rid of enum in favor of just using class names
super(owner, jobName, JobType.DEPLOY_TO_OTP);
this.deployment = deployment;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@
import com.conveyal.datatools.editor.jobs.CreateSnapshotJob;
import com.conveyal.datatools.manager.DataManager;
import com.conveyal.datatools.manager.auth.Auth0UserProfile;
import com.conveyal.datatools.manager.controllers.api.DeploymentController;
br648 marked this conversation as resolved.
Show resolved Hide resolved
import com.conveyal.datatools.manager.models.Deployment;
import com.conveyal.datatools.manager.models.FeedSource;
import com.conveyal.datatools.manager.models.FeedVersion;
import com.conveyal.datatools.manager.models.OtpServer;
import com.conveyal.datatools.manager.models.Project;
import com.conveyal.datatools.manager.models.Snapshot;
import com.conveyal.datatools.manager.models.transform.DbTransformation;
import com.conveyal.datatools.manager.models.transform.FeedTransformDbTarget;
import com.conveyal.datatools.manager.models.transform.FeedTransformRules;
import com.conveyal.datatools.manager.models.transform.FeedTransformZipTarget;
import com.conveyal.datatools.manager.models.transform.ZipTransformation;
import com.conveyal.datatools.manager.persistence.Persistence;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.slf4j.Logger;
Expand Down Expand Up @@ -171,16 +176,9 @@ public void jobLogic() {
status.fail("Could not clone version.", e);
}
}

// FIXME: Should we overwrite the input GTFS dataset if transforming in place?
br648 marked this conversation as resolved.
Show resolved Hide resolved

// TODO: Any other activities that need to be run (e.g., module-specific activities).
if (DataManager.isModuleEnabled("deployment")) {
// Project project = feedSource.retrieveProject();
// if (project.autoDeployId)
// TODO: Get deployment, update feed version for feed source, and kick off deployment to server that
// deployment is currently pointed at.
}
// Make the necessary checks and update the OTP deployment with this new dataset if required.
addNextJob(new AutoDeployFeedJob(feedVersion, owner, feedSource));
landonreed marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down
Loading