Merge remote-tracking branch 'upstream/master' into issue-108-code-cleanup

Conflicts:
	src/cucumber/groovy/deploydb/cucumber/StubAppRunner.groovy
	src/cucumber/groovy/step_definitions/FlowSteps.groovy
	src/cucumber/groovy/step_definitions/WebhookSteps.groovy
	src/main/groovy/deploydb/WorkFlow.groovy
This commit is contained in:
Giri Dandu 2015-04-06 14:26:20 -04:00
commit a22015d6e8
22 changed files with 1874 additions and 1187 deletions

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@ Feature: DeployDB config APIs
Scenario: When the system is idle, reloading the shall succeed
Given DeployDb configuration directory path is "./example/modelConfig"
Given Models configuration directory path is "./example/modelConfig"
When I POST to "/tasks/configReload" from the admin app
Then the response should be 200
And the response body should be:
@ -15,15 +15,82 @@ Feature: DeployDB config APIs
"""
Scenario: If deployments are NOT verified i.e. are in progress, then attempt to reload
config should fail. A deployment is considered as in progress, if the status
is NOT SUCCESS or FAILED
Scenario: If deployments are in progress, then attempt to reload config should succeed.
A deployment is considered as in progress (aka NOT verified), if the status
does not have value of SUCCESS or FAILED
Given there is a deployment
And Models configuration directory path is "./example/modelConfig"
When I POST to "/tasks/configReload" from the admin app
Then the response should be 200
And the response body should be:
"""
Failed: Configuration reload is not allowed while deployments are in progress
Done!
"""
@freezetime
Scenario: After config reload, transitions of in-progress deployments should use old config.
If a deployment is in CREATED state and configuration is reloaded, then
a deployment update of STARTED state should invoke set of global webhook
and environment webhook, as specified by the older config
Given a deployment webhook "started" configuration:
"""
deployment:
started:
- http://localhost:10000/job/old-notify-deployment-started/build
"""
And an deployment environment webhook "started" configuration named "pre-prod":
"""
description: "DeployDB Primary Integration"
webhook:
deployment:
started:
- http://localhost:10000/job/another-old-notify-deployment-started/build
"""
And there is a deployment in "CREATED" state
And Models configuration is reloaded from directory path "./example/modelConfig"
When I PATCH "/api/deployments/1" with:
"""
{
"status" : "STARTED"
}
"""
Then the webhook 1 should be invoked with the JSON:
"""
{
"id" : 1,
"artifact" : {
"id" : 1,
"group" : "com.example.cucumber",
"name" : "cucumber-artifact",
"version" : "1.0.1",
"sourceUrl" : "http://example.com/maven/com.example.cucumber/cucumber-artifact/1.0.1/cucumber-artifact-1.0.1.jar",
"createdAt" : "{{created_timestamp}}"
},
"status" : "STARTED",
"service" : "faas",
"environment" : "pre-prod",
"createdAt" : "{{created_timestamp}}"
}
"""
And the webhook 2 should be invoked with the JSON:
"""
{
"id" : 1,
"artifact" : {
"id" : 1,
"group" : "com.example.cucumber",
"name" : "cucumber-artifact",
"version" : "1.0.1",
"sourceUrl" : "http://example.com/maven/com.example.cucumber/cucumber-artifact/1.0.1/cucumber-artifact-1.0.1.jar",
"createdAt" : "{{created_timestamp}}"
},
"status" : "STARTED",
"service" : "faas",
"environment" : "pre-prod",
"createdAt" : "{{created_timestamp}}"
}
"""

View File

@ -52,7 +52,7 @@ class AppHelper {
* @param c (required) Closure to execute
*/
void withServiceRegistry(Closure c) {
c.call(this.runner.serviceRegistry)
c.call(this.runner.workFlow.serviceRegistry)
}
/**
@ -61,7 +61,7 @@ class AppHelper {
* @param c (required) Closure to execute
*/
void withEnvironmentRegistry(Closure c) {
c.call(this.runner.environmentRegistry)
c.call(this.runner.workFlow.environmentRegistry)
}
/**
@ -70,7 +70,7 @@ class AppHelper {
* @param c (required) Closure to execute
*/
void withPromotionRegistry(Closure c) {
c.call(this.runner.promotionRegistry)
c.call(this.runner.workFlow.promotionRegistry)
}
/**
@ -79,7 +79,16 @@ class AppHelper {
* @param c (required) Closure to execute
*/
void withPipelineRegistry(Closure c) {
c.call(this.runner.pipelineRegistry)
c.call(this.runner.workFlow.pipelineRegistry)
}
/**
* Execute the {@link Closure} with a proper WorkFlow object
*
* @param c (required) Closure to execute
*/
void withWorkFlow(Closure c) {
c.call(this.runner.workFlow)
}
/**
@ -87,9 +96,8 @@ class AppHelper {
*
* @param c (required) Closure to execute
*/
void withWebhookManager(Closure c) {
c.call(this.runner.webhookManager,
this.webhookRunner.getApplication().requestWebhookObject)
void withTestWebhookServer(Closure c) {
c.call(this.webhookRunner.getApplication().requestWebhookObject)
}
/**

View File

@ -1,27 +1,23 @@
package deploydb.cucumber
import com.google.common.base.Strings
import com.google.common.collect.ImmutableMap
import deploydb.WebhookManager
import deploydb.WorkFlow
import io.dropwizard.Application
import io.dropwizard.Configuration
import io.dropwizard.cli.ServerCommand
import io.dropwizard.lifecycle.ServerLifecycleListener
import io.dropwizard.setup.Bootstrap
import io.dropwizard.setup.Environment
import io.dropwizard.testing.ConfigOverride
import com.google.common.base.Strings
import com.google.common.collect.ImmutableMap
import javax.annotation.Nullable
import net.sourceforge.argparse4j.inf.Namespace
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.server.ServerConnector
import org.flywaydb.core.Flyway
import org.hibernate.SessionFactory
import javax.annotation.Nullable
import deploydb.registry.ModelRegistry
/**
* Class for running the Dropwizard app
*
@ -37,10 +33,7 @@ public class StubAppRunner<C extends Configuration> {
private Environment environment
private Server jettyServer
private SessionFactory sessionFactory
private ModelRegistry<deploydb.models.Service> serviceRegistry
private ModelRegistry<deploydb.models.Environment> environmentRegistry
private ModelRegistry<deploydb.models.Promotion> promotionRegistry
private ModelRegistry<deploydb.models.pipeline.Pipeline> pipelineRegistry
private WorkFlow workFlow
private WebhookManager webhookManager
public StubAppRunner(Class<? extends Application<C>> applicationClass,
@ -88,12 +81,14 @@ public class StubAppRunner<C extends Configuration> {
webhookManager = application.webhooksManager
/**
* Get a ModelRegistry(s) from the application once it's up and running
* Save workflow object
*/
serviceRegistry = application.workFlow.serviceRegistry
environmentRegistry = application.workFlow.environmentRegistry
promotionRegistry = application.workFlow.promotionRegistry
pipelineRegistry = application.workFlow.pipelineRegistry
workFlow = application.workFlow
/**
* Setup config checksum for tests
*/
application.configChecksum = "0xdead"
/* We're running the DB migrations here to make sure we're running
* them in the same classloader environment as the DeployDB
@ -101,7 +96,7 @@ public class StubAppRunner<C extends Configuration> {
* DeployDB won't be able to "see" the in-memory DB
*/
Flyway flyway = configuration.flyway.build(
configuration.database.build(metricRegistry, "Flyway"));
configuration.database.build(metricRegistry, "Flyway"))
flyway.clean()
flyway.migrate()
}

View File

@ -33,7 +33,7 @@ Given(~/^there is a deployment$/) { ->
d1.addPromotionResult(p1)
/* Create a flow */
Flow f = new Flow(a1, "faas")
Flow f = new Flow(a1, "faas", "0xdead")
f.addDeployment(d1)
/**
@ -130,7 +130,7 @@ And(~/there is a deployment in "(.*?)" state$/) { String deploymentState ->
d1.addPromotionResult(p1)
/* Create a flow */
Flow f = new Flow(a1, "faas")
Flow f = new Flow(a1, "faas", "0xdead")
f.addDeployment(d1)
/**
@ -139,4 +139,4 @@ And(~/there is a deployment in "(.*?)" state$/) { String deploymentState ->
FlowDAO fdao = new FlowDAO(sessionFactory)
fdao.persist(f)
}
}
}

View File

@ -18,4 +18,4 @@ Given(~/^an environment is configured$/) { ->
Environment a = sampleEnvironment1()
environmentRegistry.put(a.ident, a)
}
}
}

View File

@ -20,9 +20,7 @@ Given(~/^there is a flow$/) { ->
Deployment d = sampleDeployment(adao.persist(a), "pre-production", Status.STARTED)
Deployment d1 = sampleDeployment(adao.persist(a), "production", Status.STARTED)
Flow f = new Flow()
f.setArtifact(a)
f.setService("faas")
Flow f = new Flow(a, "faas", "0xdead")
d.setFlow(f)
d1.setFlow(f)
@ -32,7 +30,6 @@ Given(~/^there is a flow$/) { ->
f.setDeployments(hashSet)
fdao.persist(f)
}
}

View File

@ -2,6 +2,7 @@
import cucumber.api.DataTable
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import deploydb.WorkFlow
import org.glassfish.jersey.client.JerseyInvocation
import org.joda.time.DateTime
@ -93,6 +94,15 @@ Then(~/^the body should be JSON:$/) { String expectedBody ->
assert bodyNode == expectedNode
}
Given(~/^DeployDb configuration directory path is "(.*?)"$/) { String configDir ->
Given(~/^Models configuration directory path is "(.*?)"$/) { String configDir ->
setConfigDirectory(configDir)
}
Given(~/^Models configuration is reloaded from directory path "(.*?)"$/) { String configDir ->
setConfigDirectory(configDir)
withWorkFlow { WorkFlow workFlow ->
withSession {
workFlow.loadConfigModels()
}
}
}

View File

@ -4,34 +4,50 @@ this.metaClass.mixin(cucumber.api.groovy.EN)
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import deploydb.ModelLoader
import deploydb.ModelType
import deploydb.WorkFlow
import deploydb.dao.ModelConfigDAO
import deploydb.models.Environment
import deploydb.models.ModelConfig
import deploydb.models.Webhook.Webhook
import deploydb.registry.ModelRegistry
import org.joda.time.DateTime
import cucumber.api.DataTable
import deploydb.WebhookManager
import webhookTestServer.models.RequestWebhookObject
Given(~/^a (.*?) webhook "(.*?)" configuration:$/) { String webhookType,
String eventType, String configBody ->
List<String> paths = getUrlPathFromWebhookConfigBody(configBody, eventType)
withWorkFlow { WorkFlow workFlow ->
/*
* Instantiate the webhook object from configuration
*/
ModelLoader<Webhook> webhookLoader = new ModelLoader<>(Webhook.class)
workFlow.globalWebhook = webhookLoader.loadFromString(configBody)
withWebhookManager { WebhookManager webhookManager, RequestWebhookObject requestWebhookObject ->
/**
* Create ModelConfig. This feature allows us to remember the config in
* case of configReload
*/
ModelConfig modelConfig = new ModelConfig(
workFlow.deployDBApp.configChecksum, configBody,
workFlow.defaultIdent, ModelType.WEBHOOK)
withSession {
ModelConfigDAO modelConfigDAO = new ModelConfigDAO(sessionFactory)
modelConfigDAO.persist(modelConfig)
}
}
withTestWebhookServer { RequestWebhookObject requestWebhookObject ->
/*
* Save the configured webhook uri(s) in requestWebhookObject. These paths will be compared
* when deploydb invokes webhooks.
*/
requestWebhookObject.addConfiguredUriPaths(paths)
/*
* Load the webhook configuration in webhookManager
*/
ModelLoader<Webhook> webhookLoader = new ModelLoader<>(Webhook.class)
webhookManager.webhook = webhookLoader.loadFromString(configBody)
/*
* Set the content type from the webhook and the event type. The content type will be
* checked when deploydb invokes webhooks
@ -55,7 +71,7 @@ Given(~/^an (.*?) environment webhook "(.*?)" configuration named "(.*?)":$/) {S
* Save the configured webhook uri(s) in requestWebhookObject. These paths will be compared
* when deploydb invokes webhooks.
*/
withWebhookManager { WebhookManager webhookManager, RequestWebhookObject requestWebhookObject ->
withTestWebhookServer { RequestWebhookObject requestWebhookObject ->
requestWebhookObject.addConfiguredUriPaths(paths)
/*
* Set the content type from the webhook and the event type. The content type will be
@ -72,6 +88,22 @@ Given(~/^an (.*?) environment webhook "(.*?)" configuration named "(.*?)":$/) {S
a.ident = envIdent
environmentRegistry.put(envIdent, a)
}
/**
* Create ModelConfig. This feature allows us to remember the config in
* case of configReload
*/
withWorkFlow { WorkFlow workFlow ->
ModelConfig modelConfig = new ModelConfig(
workFlow.deployDBApp.configChecksum, configBody,
envIdent, ModelType.ENVIRONMENT)
withSession {
ModelConfigDAO modelConfigDAO = new ModelConfigDAO(sessionFactory)
modelConfigDAO.persist(modelConfig)
}
}
}
When (~/^I POST to "(.*?)" with an artifact/) { String path ->
@ -123,8 +155,6 @@ Then(~/^the webhook ([1-9][0-9]*) should be invoked with the JSON:$/) { int webh
}
}
And (~/the webhook should have the headers:$/){ DataTable headers ->
withRequestWebhookObject { RequestWebhookObject requestWebhookObject ->

View File

@ -0,0 +1,53 @@
package db.migration
import java.sql.DatabaseMetaData
/**
* Example of a Java-based migration.
*/
class V8__create_configs_table extends DeployDBMigration {
/** Return migration number to differentiate from other versions */
@Override
Integer getChecksum() {
return 8
}
/**
* Gather sql commands for this migration
*
* @param metadata
* @return List of sql commands
*/
List<String> prepareCommands(DatabaseMetaData metadata) {
/* Sql commands */
List<String> commands = []
/*
* Add modelConfigs table
*/
commands += """
CREATE TABLE modelConfigs (
id BIGINT AUTO_INCREMENT,
checksum VARCHAR(64) NOT NULL,
contents TEXT NOT NULL,
ident VARCHAR(8192),
modelType INT NOT NULL,
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP(),
deletedAt TIMESTAMP NULL,
PRIMARY KEY (id)
);
"""
/*
* Add checksum column to flows table
*/
commands += """
ALTER TABLE flows ADD COLUMN checksum VARCHAR(64);
"""
return commands
}
}

View File

@ -3,9 +3,6 @@ package deploydb
import com.codahale.metrics.annotation.Timed
import com.google.common.collect.ImmutableMultimap
import io.dropwizard.servlets.tasks.Task
import org.hibernate.Session
import org.hibernate.Transaction
import org.hibernate.context.internal.ManagedSessionContext
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@ -31,23 +28,15 @@ class ConfigReloadTask extends Task {
@Override
void execute(ImmutableMultimap<String, String> parameters, PrintWriter output) throws Exception {
Session session = this.workFlow.deployDBApp.getSessionFactory().openSession()
try {
ManagedSessionContext.bind(session)
Transaction transaction = session.beginTransaction()
this.workFlow.deployDBApp.withHibernateSession() {
try {
this.workFlow.loadConfigModels(true)
transaction.commit()
this.workFlow.loadConfigModels()
output.println("Done!")
} catch (Exception e) {
logger.error("failed to reload the config with an exception: ", e)
output.println("Failed: " + e.getMessage())
throw e
}
catch (Exception e) {
transaction.rollback()
logger.error("failed to reload the config: " + e.getMessage())
output.println("Failed: Configuration reload is not allowed while deployments are in progress")
}
} finally {
session.close()
ManagedSessionContext.unbind(this.workFlow.deployDBApp.getSessionFactory())
}
}
}

View File

@ -16,7 +16,10 @@ import io.dropwizard.setup.Bootstrap
import io.dropwizard.setup.Environment
import io.dropwizard.util.JarLocation
import io.dropwizard.views.ViewBundle
import org.hibernate.Session
import org.hibernate.SessionFactory
import org.hibernate.Transaction
import org.hibernate.context.internal.ManagedSessionContext
import org.joda.time.DateTimeZone
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@ -25,12 +28,13 @@ import org.slf4j.LoggerFactory
class DeployDBApp extends Application<DeployDBConfiguration> {
private final ImmutableList models = ImmutableList.of(
models.Artifact, models.Deployment,
models.PromotionResult, models.Flow)
models.PromotionResult, models.Flow, models.ModelConfig)
private static final Logger logger = LoggerFactory.getLogger(DeployDBApp.class)
private WebhookManager webhooksManager
private WorkFlow workFlow
private provider.V1TypeProvider typeProvider
private String configDirectory
private String configChecksum
static void main(String[] args) throws Exception {
new DeployDBApp().run(args)
@ -61,7 +65,7 @@ class DeployDBApp extends Application<DeployDBConfiguration> {
@Override
public void initialize(Bootstrap<DeployDBConfiguration> bootstrap) {
void initialize(Bootstrap<DeployDBConfiguration> bootstrap) {
bootstrap.addBundle(new AssetsBundle())
bootstrap.addBundle(hibernate)
workFlow = new WorkFlow(this)
@ -96,6 +100,7 @@ class DeployDBApp extends Application<DeployDBConfiguration> {
bootstrap.validatorFactory.validator)
}
/** Validate the arguments */
@Override
public void run(String... arguments) throws Exception {
try {
@ -118,6 +123,8 @@ class DeployDBApp extends Application<DeployDBConfiguration> {
System.exit(1);
}
}
/** DeployDB is up and running */
@Override
public void run(DeployDBConfiguration configuration,
Environment environment) {
@ -136,11 +143,14 @@ class DeployDBApp extends Application<DeployDBConfiguration> {
* Load configuration models
*/
this.configDirectory = configuration.configDirectory
try {
workFlow.loadConfigModels(false)
} catch (Exception e) {
logger.error("failed to read config from directory: " +
"${configDirectory}, error: ${e.toString()}")
withHibernateSession() {
try {
workFlow.loadConfigModels()
} catch (Exception e) {
logger.error("failed to read config from directory: " +
"${configDirectory} with an exception: ", e)
throw e
}
}
/**
@ -155,7 +165,7 @@ class DeployDBApp extends Application<DeployDBConfiguration> {
environment.healthChecks().register('webhook', new health.WebhookHealthCheck(webhooksManager))
/** Add admin task for config reload */
environment.admin().addTask(new ConfigReloadTask(workFlow));
environment.admin().addTask(new ConfigReloadTask(workFlow))
/**
* Instantiate Resources classes for Jersey handlers
@ -170,4 +180,21 @@ class DeployDBApp extends Application<DeployDBConfiguration> {
environment.jersey().register(new resources.PipelineResource(workFlow.pipelineRegistry))
environment.jersey().register(new resources.ServiceResource(workFlow.serviceRegistry))
}
/** Execute DB operations wiht a session */
void withHibernateSession(Closure c) {
Session session = this.getSessionFactory().openSession()
Transaction transaction
try {
ManagedSessionContext.bind(session)
transaction = session.beginTransaction()
c.call()
transaction.commit()
} catch (Exception e) {
transaction.rollback()
} finally {
session.close()
ManagedSessionContext.unbind(this.getSessionFactory())
}
}
}

View File

@ -0,0 +1,18 @@
package deploydb
/**
* ModelType
*
* Enumeration to track type of the Model
*
* ALERT - All the Models store the "status" in DB as an integer; hence
* please DO NOT REMOVE OR CHANGE ORDER. You CAN ONLY ADD TO THIS LIST
*/
enum ModelType {
SERVICE, /* 0 */
PIPELINE, /* 1 */
ENVIRONMENT,
PROMOTION,
WEBHOOK
}

View File

@ -17,7 +17,7 @@ class WebhookManager implements Managed {
private AbstractHookRunner runner = null
private AbstractHookQueue queue = null
private final Logger logger = LoggerFactory.getLogger(WebhookManager.class)
private Webhook webhook = null
/*
* We should we move this to application and make sure through out the code we use this variable
*/
@ -44,7 +44,6 @@ class WebhookManager implements Managed {
* @param deployDBConfiguration
*/
WebhookManager(DeployDBConfiguration deployDBConfiguration ) {
queue = deployDBConfiguration.whoasFactory.buildQueue()
runner = deployDBConfiguration.whoasFactory.buildRunner(queue)
@ -76,9 +75,12 @@ class WebhookManager implements Managed {
* @param webhookModelMapper The mapper class to translate from model to webhook
* @return Fail if push of hook request fails
*/
boolean sendDeploymentWebhook(String eventType, Webhook environmentWebhook,
boolean sendDeploymentWebhook(String eventType,
Webhook globalWebhook,
Webhook environmentWebhook,
WebhookModelMapper webhookModelMapper) {
return sendDeployDbWebhook(eventType, "deployment", environmentWebhook, webhookModelMapper)
return sendDeployDbWebhook(eventType, "deployment", globalWebhook,
environmentWebhook, webhookModelMapper)
}
/**
@ -88,9 +90,12 @@ class WebhookManager implements Managed {
* @param webhookModelMapper The mapper class to translate from model to webhook
* @return Fail if push of hook request fails
*/
boolean sendPromotionWebhook(String eventType, Webhook environmentWebhook,
WebhookModelMapper webhookModelMapper) {
return sendDeployDbWebhook(eventType, "promotion", environmentWebhook, webhookModelMapper)
boolean sendPromotionWebhook(String eventType,
Webhook globalWebhook,
Webhook environmentWebhook,
WebhookModelMapper webhookModelMapper) {
return sendDeployDbWebhook(eventType, "promotion", globalWebhook,
environmentWebhook, webhookModelMapper)
}
/**
@ -101,8 +106,10 @@ class WebhookManager implements Managed {
* @param webhookModelMapper The mapper class to translate from model to webhook
* @return Fail if push of hook request fails
*/
boolean sendDeployDbWebhook( String eventType, String webhookType, Webhook environmentWebhook,
WebhookModelMapper webhookModelMapper) {
boolean sendDeployDbWebhook(String eventType, String webhookType,
Webhook globalWebhook,
Webhook environmentWebhook,
WebhookModelMapper webhookModelMapper) {
/*
* Initialize the list for URL's configured in webhooks
*/
@ -120,10 +127,10 @@ class WebhookManager implements Managed {
getMemberOfObject(getMemberOfObject(environmentWebhook, webhookType), eventType) != null) {
eventUrlList = getMemberOfObject(getMemberOfObject(environmentWebhook, webhookType), eventType)
}
if (webhook != null &&
getMemberOfObject(webhook, webhookType) != null &&
getMemberOfObject( getMemberOfObject(webhook, webhookType), eventType) != null) {
eventUrlList += getMemberOfObject( getMemberOfObject(webhook, webhookType), eventType)
if (globalWebhook != null &&
getMemberOfObject(globalWebhook, webhookType) != null &&
getMemberOfObject(getMemberOfObject(globalWebhook, webhookType), eventType) != null) {
eventUrlList += getMemberOfObject(getMemberOfObject(globalWebhook, webhookType), eventType)
}
/*
@ -148,6 +155,7 @@ class WebhookManager implements Managed {
}
return pushReturn
}
/**
* Return true if the webhook thread is running
*/

View File

@ -3,6 +3,7 @@ package deploydb
import groovy.io.FileType
import javax.ws.rs.WebApplicationException
import javax.ws.rs.core.Response
import org.apache.commons.codec.digest.DigestUtils
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@ -10,12 +11,13 @@ import org.slf4j.LoggerFactory
/* Define a new exception to break out of loop */
class BreakLoopException extends Exception{}
public class WorkFlow {
class WorkFlow {
private final DeployDBApp deployDBApp
private registry.ModelRegistry<models.Promotion> promotionRegistry
private registry.ModelRegistry<models.Environment> environmentRegistry
private registry.ModelRegistry<models.pipeline.Pipeline> pipelineRegistry
private registry.ModelRegistry<models.Service> serviceRegistry
private models.Webhook.Webhook globalWebhook
private ModelLoader<models.Promotion> promotionLoader
private ModelLoader<models.Environment> environmentLoader
private ModelLoader<models.pipeline.Pipeline> pipelineLoader
@ -24,7 +26,9 @@ public class WorkFlow {
private dao.ArtifactDAO artifactDAO
private dao.DeploymentDAO deploymentDAO
private dao.FlowDAO flowDAO
private dao.ModelConfigDAO modelConfigDAO
private static final Logger logger = LoggerFactory.getLogger(WorkFlow.class)
private static final String defaultIdent = "default"
WorkFlow(DeployDBApp app) {
this.deployDBApp = app
@ -37,6 +41,7 @@ public class WorkFlow {
artifactDAO = new dao.ArtifactDAO(this.deployDBApp.getSessionFactory())
deploymentDAO = new dao.DeploymentDAO(this.deployDBApp.getSessionFactory())
flowDAO = new dao.FlowDAO(this.deployDBApp.getSessionFactory())
modelConfigDAO = new dao.ModelConfigDAO(this.deployDBApp.getSessionFactory())
}
void initializeRegistry() {
@ -59,21 +64,61 @@ public class WorkFlow {
webhookLoader = new ModelLoader<>(models.Webhook.Webhook.class)
}
private void loadConfigModelsCommon(String modelDirName, Closure c) {
File modelDirectory = new File(modelDirName);
/** Helper for config loader */
private void loadConfigModelsCommon(String modelDirName,
ModelType modelType,
registry.ModelRegistry registry,
ModelLoader loader,
Vector<InputStream> inputStreams,
List<models.ModelConfig> modelConfigList,
Closure c) {
File modelDirectory = new File(modelDirName)
if (modelDirectory.exists() && modelDirectory.isDirectory()) {
logger.debug("Loading model from directory: ${modelDirectory.getCanonicalPath()}");
logger.info("Loading models from directory: ${modelDirectory.getCanonicalPath()}")
/* Skip everything but yaml file */
modelDirectory.eachFileMatch(FileType.FILES, ~/^.*?\.yml/) { File modelFile ->
/**
* When walking thr the files from the directory:
* - Skip everything but yaml file.
* - Sort these files in order to ensure that checksum remains same regardless
* the order in which files are read from the directory
*/
List<String> filenames = []
modelDirectory.eachFileMatch(FileType.FILES, ~/^.*?\.yml/) { it -> filenames << it.name }
filenames.sort()
filenames.each() { String filename ->
File modelFile = new File(modelDirectory, filename)
try {
c.call(modelFile)
/** Read YAML file into model object */
def model = loader.load(modelFile)
/**
* If registry is available, then insert model object into registry. In case
* of singular models (webhook), it can be null, assume a "deafult" ident.
*/
String ident = defaultIdent
if (registry) {
ident = model.ident = loader.getIdent(modelFile.name)
registry.put(model.ident, model)
}
/* Add file stream to table for checksum calculations */
FileInputStream fileInputStream = new FileInputStream(modelFile)
inputStreams.add(fileInputStream)
/* Create ModelConfig */
models.ModelConfig modelConfig = new models.ModelConfig(null, modelFile.text,
ident, modelType)
modelConfigList.add(modelConfig)
/** Execute the closure */
c.call(model)
} catch (BreakLoopException e) {
throw e
} catch (IllegalArgumentException e) {
throw e /* Throw the exception again */
} catch (all) {
logger.info("Failed to load model from ${modelFile.name}")
logger.error("Failed to load model from ${modelFile.name}")
}
}
}
@ -86,18 +131,10 @@ public class WorkFlow {
*
* @param baseConfigDirName
*/
void loadConfigModels(Boolean reloadConfig) {
/**
* Abort reloading the configuration (trigerred by REST API) in case we
* have active flows.
*/
if (reloadConfig && this.flowDAO.getActiveFlowsCount() != 0) {
throw new Exception("Configuration reload is not allowed while deployments are in progress")
}
void loadConfigModels() {
/** Validate base config directory */
File baseConfigDirectory = new File(this.deployDBApp.configDirectory);
File baseConfigDirectory = new File(this.deployDBApp.configDirectory)
if (!baseConfigDirectory.exists() || !baseConfigDirectory.isDirectory()) {
throw new Exception("No DeployDB configuration found. DeployDB would not function properly")
}
@ -118,30 +155,30 @@ public class WorkFlow {
registry.ModelRegistry<models.Service> tmpServiceRegistry =
new registry.ModelRegistry<models.Service>()
models.Webhook.Webhook tmpWebhook = null
Vector<InputStream> inputStreams = new Vector<>()
List<models.ModelConfig> modelConfigList = []
/* Load promotions */
String promotionsDirName = this.deployDBApp.configDirectory + "/promotions"
loadConfigModelsCommon(promotionsDirName) { File modelFile ->
models.Promotion promotion = this.promotionLoader.load(modelFile)
promotion.ident = this.promotionLoader.getIdent(modelFile.name)
tmpPromotionRegistry.put(promotion.ident, promotion)
loadConfigModelsCommon(promotionsDirName, ModelType.PROMOTION,
tmpPromotionRegistry, this.promotionLoader,
inputStreams, modelConfigList) { models.Promotion promotion ->
logger.debug("Loaded promotions model: ${promotion.ident}")
}
/* Load environments */
String environmentsDirName = this.deployDBApp.configDirectory + "/environments"
loadConfigModelsCommon(environmentsDirName) { File modelFile ->
models.Environment environment = this.environmentLoader.load(modelFile)
environment.ident = this.environmentLoader.getIdent(modelFile.name)
tmpEnvironmentRegistry.put(environment.ident, environment)
loadConfigModelsCommon(environmentsDirName, ModelType.ENVIRONMENT,
tmpEnvironmentRegistry, this.environmentLoader,
inputStreams, modelConfigList) { models.Environment environment ->
logger.debug("Loaded environments model: ${environment.ident}")
}
/* Load pipelines */
String pipelinesDirName = this.deployDBApp.configDirectory + "/pipelines"
loadConfigModelsCommon(pipelinesDirName) { File modelFile ->
models.pipeline.Pipeline pipeline = this.pipelineLoader.load(modelFile)
pipeline.ident = this.pipelineLoader.getIdent(modelFile.name)
loadConfigModelsCommon(pipelinesDirName, ModelType.PIPELINE,
tmpPipelineRegistry, this.pipelineLoader,
inputStreams, modelConfigList) { models.pipeline.Pipeline pipeline ->
/* Validate */
pipeline.environments.each() {
@ -162,16 +199,14 @@ public class WorkFlow {
}
}
/* Add to registry */
tmpPipelineRegistry.put(pipeline.ident, pipeline)
logger.debug("Loaded pipelines model: ${pipeline.ident}")
}
/* Load services */
String servicesDirName = this.deployDBApp.configDirectory + "/services"
loadConfigModelsCommon(servicesDirName) { File modelFile ->
models.Service service = this.serviceLoader.load(modelFile)
service.ident = this.serviceLoader.getIdent(modelFile.name)
loadConfigModelsCommon(servicesDirName, ModelType.SERVICE,
tmpServiceRegistry, this.serviceLoader,
inputStreams, modelConfigList) { models.Service service ->
/* Validate */
service.pipelines.each() { String pipelineIdent ->
@ -189,30 +224,52 @@ public class WorkFlow {
}
}
/* Add to registry */
tmpServiceRegistry.put(service.ident, service)
logger.debug("Loaded services model: ${service.ident}")
}
/* Load webhook */
String webhookDirName = this.deployDBApp.configDirectory + "/webhook"
try {
loadConfigModelsCommon(webhookDirName, ModelType.WEBHOOK,
null, this.webhookLoader,
inputStreams, modelConfigList) { models.Webhook.Webhook webhook ->
/* Store webhook */
tmpWebhook = webhook
logger.debug("Loaded webhook model")
/* Now that we have found a valid global webhook, we are done */
throw new BreakLoopException()
}
} catch (BreakLoopException e) {
/* Neeed the log to make codenarc happy */
logger.debug("Done with webhook load")
}
/* Compute a checksum for this iteration of Models configuration */
SequenceInputStream sequenceInputStream = new SequenceInputStream(inputStreams.elements())
String newConfigChecksum
try {
newConfigChecksum = DigestUtils.md5Hex(sequenceInputStream)
} finally {
sequenceInputStream.close()
}
if (this.deployDBApp.configChecksum == newConfigChecksum) {
logger.info("Ignoring as no change in DeployDB Model Configuration detected")
return
}
this.deployDBApp.configChecksum = newConfigChecksum
/* At least one service MUST be configured for deployDb to function properly */
if (tmpServiceRegistry.getAll().isEmpty()) {
logger.info("NO SERVICES ARE CONFIGURED. DeployDB would not function properly")
}
/* Load webhook */
String webhookDirName = this.deployDBApp.configDirectory + "/webhook"
try {
loadConfigModelsCommon(webhookDirName) { File modelFile ->
tmpWebhook = this.webhookLoader.load(modelFile)
logger.debug("Loaded webhooks model from: ${modelFile.name}")
/* Now that we have found a valid webhook, we are done */
throw new BreakLoopException()
}
} catch (BreakLoopException e) {
/* Neeed the log to make codenarc happy */
logger.debug("Done with webhook load")
/* Persist all ModelConfigs */
modelConfigList.each() { models.ModelConfig modelConfig ->
modelConfig.checksum = newConfigChecksum
this.modelConfigDAO.persist(modelConfig)
}
/**
@ -222,7 +279,72 @@ public class WorkFlow {
environmentRegistry = tmpEnvironmentRegistry
pipelineRegistry = tmpPipelineRegistry
serviceRegistry = tmpServiceRegistry
deployDBApp.webhooksManager.webhook = tmpWebhook
globalWebhook = tmpWebhook
}
/**
* Retrieve webhook either from memory or DB (if config has changed)
*
* @param deployment
* @return Webhook object
*/
models.Webhook.Webhook retrieveWebhook(models.Deployment deployment) {
/**
* If configuration has changed since the flow creation, then retrieve and
* rebuild objects from DB
*/
if (deployment.flow.configChecksum != this.deployDBApp.configChecksum) {
/* Load webhook from config */
models.ModelConfig webhookConfig =
this.modelConfigDAO.findModelConfig(ModelType.WEBHOOK,
defaultIdent, deployment.flow.configChecksum)
if (webhookConfig) {
models.Webhook.Webhook fetchedWebhook =
this.webhookLoader.loadFromString(webhookConfig.contents)
return fetchedWebhook
} else {
logger.error("Failed to find webhook configuration for " +
"deployment: ${deployment.id}, config-checksum: ${deployment.flow.configChecksum}")
return null
}
} else {
/* Load extant global webhook object */
return this.globalWebhook
}
}
/**
* Retrieve Environment either from memory or DB (if config has changed)
*
* @param deployment
* @return Environment
*/
models.Environment retrieveEnvironment(models.Deployment deployment) {
/**
* If configuration is changed since the flow creation, then retrieve and
* rebuild objects from DB
*/
if (deployment.flow.configChecksum != this.deployDBApp.configChecksum) {
/* Load environment from config */
models.ModelConfig environmentConfig =
this.modelConfigDAO.findModelConfig(ModelType.ENVIRONMENT,
deployment.environmentIdent, deployment.flow.configChecksum)
if (environmentConfig) {
models.Environment environment =
this.environmentLoader.loadFromString(environmentConfig.contents)
return environment
} else {
logger.error("Failed to find Environment configuration for " +
"deployment: ${deployment.id}, Environment: ${deployment.environmentIdent}, " +
"config-checksum: ${deployment.flow.configChecksum}")
return null
}
} else {
/* Load from environment */
return this.environmentRegistry.get(deployment.environmentIdent)
}
}
/**
@ -253,7 +375,8 @@ public class WorkFlow {
services.flatten().each() { models.Service service ->
/* Create a flow */
models.Flow flow = new models.Flow(artifact, service.ident)
models.Flow flow = new models.Flow(artifact, service.ident,
this.deployDBApp.configChecksum)
/* Get all pipelines */
List<models.pipeline.Pipeline> pipelines = service.getPipelines().collect() { String pipelineIdent ->
@ -373,16 +496,18 @@ public class WorkFlow {
new mappers.DeploymentWebhookMapper(deployment)
/*
* Get the environment based webhooks for this deployment
* Get the global and environment based webhooks for this deployment
*/
models.Webhook.Webhook webhook = retrieveWebhook(deployment)
models.Environment environment = retrieveEnvironment(deployment)
models.Webhook.Webhook environmentWebhook =
this.environmentRegistry.get(deployment.environmentIdent).webhook
environment ? environment.webhook : null
/*
* Use webhook manager to send the webhook
*/
if (deployDBApp.webhooksManager.sendDeploymentWebhook("created", environmentWebhook,
deploymentWebhookMapper) == false) {
if (deployDBApp.webhooksManager.sendDeploymentWebhook("created", webhook,
environmentWebhook, deploymentWebhookMapper) == false) {
logger.info("Failed to send deployment started ${deployment.id}")
throw new WebApplicationException(Response.Status.BAD_REQUEST)
}
@ -408,17 +533,18 @@ public class WorkFlow {
new mappers.DeploymentWebhookMapper(deployment)
/*
* Get the environment based webhooks for this deployment
* Get the global and environment based webhooks for this deployment
*/
models.Webhook.Webhook webhook = retrieveWebhook(deployment)
models.Environment environment = retrieveEnvironment(deployment)
models.Webhook.Webhook environmentWebhook =
this.environmentRegistry.get(deployment.environmentIdent)?
this.environmentRegistry.get(deployment.environmentIdent).webhook : null
environment ? environment.webhook : null
/*
* Use webhook manager to send the webhook
*/
if (deployDBApp.webhooksManager.sendDeploymentWebhook("started", environmentWebhook,
deploymentWebhookMapper) == false) {
if (deployDBApp.webhooksManager.sendDeploymentWebhook("started", webhook,
environmentWebhook, deploymentWebhookMapper) == false) {
logger.info("Failed to send deployment started ${deployment.id}")
throw new WebApplicationException(Response.Status.BAD_REQUEST)
}
@ -447,17 +573,18 @@ public class WorkFlow {
new mappers.DeploymentWebhookMapper(deployment)
/*
* Get the environment based webhooks for this deployment
* Get the global and environment based webhooks for this deployment
*/
models.Webhook.Webhook webhook = retrieveWebhook(deployment)
models.Environment environment = retrieveEnvironment(deployment)
models.Webhook.Webhook environmentWebhook =
this.environmentRegistry.get(deployment.environmentIdent)?
this.environmentRegistry.get(deployment.environmentIdent).webhook : null
environment ? environment.webhook : null
/*
* Use webhook manager to send the webhook
*/
if (deployDBApp.webhooksManager.sendDeploymentWebhook("completed", environmentWebhook,
deploymentWebhookMapper) == false) {
if (deployDBApp.webhooksManager.sendDeploymentWebhook("completed", webhook,
environmentWebhook, deploymentWebhookMapper) == false) {
logger.info("Failed to send deployment completed ${deployment.id}")
throw new WebApplicationException(Response.Status.BAD_REQUEST)
}
@ -499,17 +626,18 @@ public class WorkFlow {
new mappers.PromotionWebhookMapper(deployment, promotionResult)
/*
* Get the environment based webhooks for this deployment
* Get the global and environment based webhooks for this deployment
*/
models.Webhook.Webhook webhook = retrieveWebhook(deployment)
models.Environment environment = retrieveEnvironment(deployment)
models.Webhook.Webhook environmentWebhook =
this.environmentRegistry.get(deployment.environmentIdent)?
this.environmentRegistry.get(deployment.environmentIdent).webhook : null
environment ? environment.webhook : null
/*
* Use webhook manager to send the webhook
*/
if (deployDBApp.webhooksManager.sendPromotionWebhook("completed", environmentWebhook,
promotionWebhookMapper) == false) {
if (deployDBApp.webhooksManager.sendPromotionWebhook("completed", webhook,
environmentWebhook, promotionWebhookMapper) == false) {
logger.info("Failed to send promotion success webhook for ${promotionResult.promotion}")
throw new WebApplicationException(Response.Status.BAD_REQUEST)
}
@ -551,17 +679,18 @@ public class WorkFlow {
new mappers.PromotionWebhookMapper(deployment, promotionResult)
/*
* Get the environment based webhooks for this deployment
* Get the global and environment based webhooks for this deployment
*/
models.Webhook.Webhook webhook = retrieveWebhook(deployment)
models.Environment environment = retrieveEnvironment(deployment)
models.Webhook.Webhook environmentWebhook =
this.environmentRegistry.get(deployment.environmentIdent)?
this.environmentRegistry.get(deployment.environmentIdent).webhook : null
environment ? environment.webhook : null
/*
* Use webhook manager to send the webhook
*/
if (deployDBApp.webhooksManager.sendPromotionWebhook("completed", environmentWebhook,
promotionWebhookMapper) == false) {
if (deployDBApp.webhooksManager.sendPromotionWebhook("completed", webhook,
environmentWebhook, promotionWebhookMapper) == false) {
logger.info("Failed to send promotion failed webhook for ${promotionResult.promotion}")
throw new WebApplicationException(Response.Status.BAD_REQUEST)
}

View File

@ -1,11 +1,8 @@
package deploydb.dao
import deploydb.models.Flow
import deploydb.Status
import groovy.transform.InheritConstructors
import io.dropwizard.hibernate.AbstractDAO
import org.hibernate.criterion.Projections
import org.hibernate.criterion.Restrictions
/**
@ -13,14 +10,4 @@ import org.hibernate.criterion.Restrictions
*/
@InheritConstructors
class FlowDAO extends AbstractDAO<Flow> {
/**
* Fetch active flows count
*/
Long getActiveFlowsCount() {
return criteria()
.add(Restrictions.and(
Restrictions.ne("status", Status.SUCCESS),
Restrictions.ne("status", Status.FAILED)))
.setProjection(Projections.rowCount()).uniqueResult()
}
}

View File

@ -0,0 +1,35 @@
package deploydb.dao
import deploydb.ModelType
import deploydb.models.ModelConfig
import groovy.transform.InheritConstructors
import io.dropwizard.hibernate.AbstractDAO
import org.hibernate.criterion.Restrictions
import org.hibernate.criterion.Order
/**
* modelConfig Data access object
*/
@InheritConstructors
class ModelConfigDAO extends AbstractDAO<ModelConfig> {
/**
* Locate ModelConfig matching the modelType, identifier and checksum of config iteration
*
* @param modelType A Enum of Model type (Service, Pipeline, etc.)
* @param ident Identifier of the Model
* @param configChecksum Checksum of the configuration iteration
* @return null or ModelConfig if found
*/
ModelConfig findModelConfig(
ModelType modelType,
String ident,
String configChecksum) {
return criteria()
.add(Restrictions.eq('modelType', modelType))
.add(Restrictions.eq('ident', ident))
.add(Restrictions.eq('checksum', configChecksum))
.addOrder(Order.desc('createdAt')).uniqueResult()
}
}

View File

@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonProperty
import deploydb.Status
import javax.persistence.Column
import javax.persistence.CascadeType;
import javax.persistence.CascadeType
import javax.persistence.Entity
import javax.persistence.EnumType
import javax.persistence.Enumerated
@ -34,8 +34,13 @@ class Flow extends AbstractModel {
Set<Deployment> deployments = new HashSet<Deployment>()
@Column(name="service")
@JsonProperty
String service
@JsonProperty(value = "service")
String serviceIdent
/** Checksum of configuration of all the Models */
@Column(name="checksum")
@JsonIgnore
String configChecksum
@Column(name="status")
@Enumerated(EnumType.ORDINAL)
@ -51,9 +56,10 @@ class Flow extends AbstractModel {
* Default constructor to create a valid Flow object to save in
* the database
*/
Flow(Artifact deployedArtifact, String service) {
Flow(Artifact deployedArtifact, String serviceIdent, String configChecksum) {
this.artifact = deployedArtifact
this.service = service
this.serviceIdent = serviceIdent
this.configChecksum = configChecksum
}
/**
@ -71,7 +77,7 @@ class Flow extends AbstractModel {
* @return True if the objects are equal otherwise false
*/
@Override
public boolean equals(Object o) {
boolean equals(Object o) {
/* First object identity */
if (this.is(o)) {
return true
@ -86,7 +92,8 @@ class Flow extends AbstractModel {
return Objects.equals(this.id, that.id) &&
Objects.equals(this.artifact, that.artifact) &&
Objects.equals(this.deployments, that.deployments) &&
Objects.equals(this.service, that.service)
Objects.equals(this.serviceIdent, that.serviceIdent) &&
Objects.equals(this.configChecksum, that.configChecksum)
}
/**
@ -96,7 +103,8 @@ class Flow extends AbstractModel {
*/
@Override
int hashCode() {
return Objects.hash(this.id, this.artifact, this.deployments, this.service)
return Objects.hash(this.id, this.artifact, this.deployments,
this.serviceIdent, this.configChecksum)
}
/**
@ -106,7 +114,8 @@ class Flow extends AbstractModel {
*/
@Override
String toString() {
return "id = ${id}, artifact: ${artifact}, deployments: ${deployments}, service: ${service}"
return "id = ${id}, artifact: ${artifact}, deployments: ${deployments}, " +
"serviceIdent: ${serviceIdent}, configChecksum: ${configChecksum}"
}
}

View File

@ -0,0 +1,90 @@
package deploydb.models
import deploydb.ModelType
import com.fasterxml.jackson.annotation.JsonProperty
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.EnumType
import javax.persistence.Enumerated
import javax.persistence.Table
import org.hibernate.validator.constraints.NotEmpty
/**
* Representation class for the concept of an ModelConfig
*/
@Entity
@Table(name='modelConfigs')
class ModelConfig extends AbstractModel {
@NotEmpty
@Column(name="checksum", nullable=false)
@JsonProperty
String checksum
@NotEmpty
@Column(name="contents", nullable=false)
@JsonProperty
String contents
@NotEmpty
@Column(name='ident', nullable=false)
@JsonProperty
String ident
@Column(name='modelType', nullable=false)
@Enumerated(EnumType.ORDINAL)
@JsonProperty
ModelType modelType
/**
* Empty constructor used by Jackson for object deserialization
*/
ModelConfig() { }
/**
* Default constructor to be used by DeployDB internally. It accepts all
* of the required parameters for the database
*/
ModelConfig(String checksum,
String contents,
String ident,
ModelType modelType) {
this.checksum = checksum
this.contents = contents
this.ident = ident
this.modelType = modelType
}
@Override
boolean equals(Object o) {
/* First object identity */
if (this.is(o)) {
return true
}
if (!(o instanceof ModelConfig)) {
return false
}
final ModelConfig that = (ModelConfig)o
return Objects.equals(this.id, that.id) &&
Objects.equals(this.checksum, that.checksum) &&
Objects.equals(this.contents, that.contents) &&
Objects.equals(this.ident, that.ident) &&
Objects.equals(this.modelType, that.modelType)
}
@Override
int hashCode() {
return Objects.hash(this.id, this.checksum, this.contents,
this.ident, this.modelType)
}
@Override
String toString() {
return "id: ${id}, checksum: ${checksum}, contents: \"${contents}\", " +
"ident: ${ident}, modelType: ${modelType}"
}
}

View File

@ -31,7 +31,7 @@ class WebhookManagerSpec extends Specification {
WebhookManager m = new WebhookManager()
expect:
m.sendDeploymentWebhook("created", null, null) == true
m.sendDeploymentWebhook("created", null, null, null) == true
}
def "sendDeploymentWebhook() should push correct hook requests when global webhooks are configured"() {
@ -43,7 +43,7 @@ class WebhookManagerSpec extends Specification {
[], [])
WebhookManager m = new WebhookManager()
m.queue = new InMemoryQueue()
m.webhook = new deploydb.models.Webhook.Webhook(webhookDeployment, null)
models.Webhook.Webhook gwh = new models.Webhook.Webhook(webhookDeployment, null)
// mock the queue push
int counter = 0
@ -53,7 +53,7 @@ class WebhookManagerSpec extends Specification {
}
when:
m.sendDeploymentWebhook("created", null, new DeploymentWebhookMapper())
m.sendDeploymentWebhook("created", gwh, null, new DeploymentWebhookMapper())
then:
counter == 2
@ -68,7 +68,7 @@ class WebhookManagerSpec extends Specification {
null, null)
WebhookManager m = new WebhookManager()
m.queue = new InMemoryQueue()
deploydb.models.Webhook.Webhook ewh = new deploydb.models.Webhook.Webhook(webhookDeployment, null)
models.Webhook.Webhook ewh = new models.Webhook.Webhook(webhookDeployment, null)
// mock the queue push
int counter = 0
@ -78,7 +78,7 @@ class WebhookManagerSpec extends Specification {
}
when:
m.sendDeploymentWebhook("created", ewh, new DeploymentWebhookMapper())
m.sendDeploymentWebhook("created", null, ewh, new DeploymentWebhookMapper())
then:
counter == 2
@ -93,8 +93,8 @@ class WebhookManagerSpec extends Specification {
null, null)
WebhookManager m = new WebhookManager()
m.queue = new InMemoryQueue()
m.webhook = new deploydb.models.Webhook.Webhook(webhookDeployment, null)
deploydb.models.Webhook.Webhook ewh = new deploydb.models.Webhook.Webhook(webhookDeployment, null)
models.Webhook.Webhook gwh = new models.Webhook.Webhook(webhookDeployment, null)
models.Webhook.Webhook ewh = new models.Webhook.Webhook(webhookDeployment, null)
// mock the queue push
int counter = 0
@ -104,7 +104,7 @@ class WebhookManagerSpec extends Specification {
}
when:
m.sendDeploymentWebhook("created", ewh, new DeploymentWebhookMapper())
m.sendDeploymentWebhook("created", gwh, ewh, new DeploymentWebhookMapper())
then:
counter == 4
@ -126,8 +126,8 @@ class WebhookManagerSpec extends Specification {
["http://www.localhost.com/test-build", "http://www.localhost.com/test1-build"],
null,
null, null)
m.webhook = new deploydb.models.Webhook.Webhook(webhookDeployment, null)
deploydb.models.Webhook.Webhook ewh = new deploydb.models.Webhook.Webhook(environWebhookDeployment, null)
models.Webhook.Webhook gwh = new models.Webhook.Webhook(webhookDeployment, null)
models.Webhook.Webhook ewh = new models.Webhook.Webhook(environWebhookDeployment, null)
// mock the queue push
int counter = 0
@ -137,7 +137,7 @@ class WebhookManagerSpec extends Specification {
}
when:
m.sendDeploymentWebhook("created", ewh, new DeploymentWebhookMapper())
m.sendDeploymentWebhook("created", gwh, ewh, new DeploymentWebhookMapper())
then:
counter == 2
@ -160,8 +160,8 @@ class WebhookManagerSpec extends Specification {
["http://www.localhost.com/test-build", "http://www.localhost.com/test1-build"],
null, null)
m.webhook = new deploydb.models.Webhook.Webhook(webhookDeployment, null)
deploydb.models.Webhook.Webhook ewh = new deploydb.models.Webhook.Webhook(environWebhookDeployment, null)
models.Webhook.Webhook gwh = new models.Webhook.Webhook(webhookDeployment, null)
models.Webhook.Webhook ewh = new models.Webhook.Webhook(environWebhookDeployment, null)
// mock the queue push
int counter = 0
@ -171,7 +171,7 @@ class WebhookManagerSpec extends Specification {
}
when:
m.sendDeploymentWebhook("created", ewh, new DeploymentWebhookMapper())
m.sendDeploymentWebhook("created", gwh, ewh, new DeploymentWebhookMapper())
then:
counter == 2
@ -182,7 +182,7 @@ class WebhookManagerSpec extends Specification {
WebhookManager m = new WebhookManager()
expect:
m.sendPromotionWebhook("completed", null, null) == true
m.sendPromotionWebhook("completed", null, null, null) == true
}
def "sendPromotiontWebhook() should push correct hook requests when global webhooks are configured"() {
@ -193,7 +193,7 @@ class WebhookManagerSpec extends Specification {
["http://www.localhost.com/test-build", "http://www.localhost.com/test1-build"])
WebhookManager m = new WebhookManager()
m.queue = new InMemoryQueue()
m.webhook = new deploydb.models.Webhook.Webhook(null, webhookPromotion)
models.Webhook.Webhook gwh = new models.Webhook.Webhook(null, webhookPromotion)
// mock the queue push
int counter = 0
@ -203,7 +203,7 @@ class WebhookManagerSpec extends Specification {
}
when:
m.sendPromotionWebhook("completed", null, new DeploymentWebhookMapper())
m.sendPromotionWebhook("completed", gwh, null, new DeploymentWebhookMapper())
then:
counter == 2
@ -218,7 +218,7 @@ class WebhookManagerSpec extends Specification {
WebhookManager m = new WebhookManager()
m.queue = new InMemoryQueue()
deploydb.models.Webhook.Webhook ewh = new deploydb.models.Webhook.Webhook(null, webhookPromotion)
models.Webhook.Webhook ewh = new models.Webhook.Webhook(null, webhookPromotion)
// mock the queue push
int counter = 0
@ -228,7 +228,7 @@ class WebhookManagerSpec extends Specification {
}
when:
m.sendPromotionWebhook("completed", ewh, new DeploymentWebhookMapper())
m.sendPromotionWebhook("completed", null, ewh, new DeploymentWebhookMapper())
then:
counter == 2
@ -243,8 +243,8 @@ class WebhookManagerSpec extends Specification {
WebhookManager m = new WebhookManager()
m.queue = new InMemoryQueue()
m.webhook = new deploydb.models.Webhook.Webhook(null, webhookPromotion)
deploydb.models.Webhook.Webhook ewh = new deploydb.models.Webhook.Webhook(null, webhookPromotion)
models.Webhook.Webhook gwh = new models.Webhook.Webhook(null, webhookPromotion)
models.Webhook.Webhook ewh = new models.Webhook.Webhook(null, webhookPromotion)
// mock the queue push
int counter = 0
@ -254,7 +254,7 @@ class WebhookManagerSpec extends Specification {
}
when:
m.sendPromotionWebhook("completed", ewh, new DeploymentWebhookMapper())
m.sendPromotionWebhook("completed", gwh, ewh, new DeploymentWebhookMapper())
then:
counter == 4

View File

@ -1,6 +1,7 @@
package deploydb
import deploydb.dao.FlowDAO
import deploydb.dao.ModelConfigDAO
import deploydb.models.Flow
import spock.lang.*
@ -20,14 +21,16 @@ class workFlowWithArgsSpec extends Specification {
private String baseCfgDirName = "./build/tmp/config"
private DeployDBApp app = new DeployDBApp()
private WorkFlow workFlow
private FlowDAO dao = Mock(FlowDAO)
private FlowDAO fdao = Mock(FlowDAO)
private ModelConfigDAO mdao = Mock(ModelConfigDAO)
def setup() {
app.webhooksManager = new WebhookManager()
app.configDirectory = baseCfgDirName
app.configChecksum = null
workFlow = new WorkFlow(app)
workFlow.initializeRegistry()
workFlow.flowDAO = dao
workFlow.flowDAO = fdao
workFlow.modelConfigDAO = mdao
}
def cleanup() {
@ -52,6 +55,21 @@ description: "Basic Smoke test for the Basic Service"
promotionFile.write(fileContents)
}
def createAnotherPromotionConfigFile() {
String fileContents = """
type: AdvancedPromotion
description: "Advanced Smoke test for the Basic Service"
"""
/* Create temp file */
File promotionsDir = new File("${baseCfgDirName}/promotions")
if (!promotionsDir.exists()) {
promotionsDir.mkdirs()
}
File promotionFile = new File(promotionsDir, "advancedPromo.yml")
promotionFile.write(fileContents)
}
def createEnvironmentConfigFile() {
String fileContents = """
description: "Basic Environment"
@ -142,7 +160,8 @@ promotion:
createPipelineConfigFile()
createServiceConfigFile()
createWebhookConfigFile()
workFlow.loadConfigModels(false)
workFlow.loadConfigModels()
mdao.persist(_) >> _
expect:
workFlow.promotionRegistry.getAll().size() == 1
@ -153,14 +172,15 @@ promotion:
workFlow.pipelineRegistry.get("basicPipe").ident == "basicPipe"
workFlow.serviceRegistry.getAll().size() == 1
workFlow.serviceRegistry.get("basicServ").ident == "basicServ"
workFlow.deployDBApp.webhooksManager != null
workFlow.globalWebhook != null
}
def "If promotion is missing, then loading pipeline in loadConfigModels throws an exception"() {
when:
createEnvironmentConfigFile()
createPipelineConfigFile()
workFlow.loadConfigModels(false)
workFlow.loadConfigModels()
mdao.persist(_) >> _
then:
thrown(IllegalArgumentException)
@ -170,7 +190,8 @@ promotion:
when:
createPromotionConfigFile()
createPipelineConfigFile()
workFlow.loadConfigModels(false)
workFlow.loadConfigModels()
mdao.persist(_) >> _
then:
thrown(IllegalArgumentException)
@ -181,47 +202,50 @@ promotion:
createPromotionConfigFile()
createEnvironmentConfigFile()
createServiceConfigFile()
workFlow.loadConfigModels(false)
workFlow.loadConfigModels()
mdao.persist(_) >> _
then:
thrown(IllegalArgumentException)
}
def "Reload entire config from a directory and make sure it passes"() {
def "Reload unchanged config from a directory and make sure its ignored"() {
given:
createPromotionConfigFile()
createEnvironmentConfigFile()
createPipelineConfigFile()
createServiceConfigFile()
createWebhookConfigFile()
workFlow.loadConfigModels(false)
1 * dao.getActiveFlowsCount() >> 0
workFlow.loadConfigModels()
String oldChecksum = app.configChecksum
mdao.persist(_) >> _
when:
workFlow.loadConfigModels(true)
workFlow.loadConfigModels()
then:
workFlow.promotionRegistry.getAll().size() == 1
workFlow.promotionRegistry.get("basicPromo").ident == "basicPromo"
workFlow.environmentRegistry.getAll().size() == 1
workFlow.environmentRegistry.get("basicEnv").ident == "basicEnv"
workFlow.pipelineRegistry.getAll().size() == 1
workFlow.pipelineRegistry.get("basicPipe").ident == "basicPipe"
workFlow.serviceRegistry.getAll().size() == 1
workFlow.serviceRegistry.get("basicServ").ident == "basicServ"
workFlow.deployDBApp.webhooksManager != null
app.configChecksum != null
oldChecksum == app.configChecksum
}
def "Attempt to reload config with active flows throws an exception"() {
def "Reload changed config from a directory and make sure it passes"() {
given:
//List<models.Flow> flows = [new models.Flow()]
1 * dao.getActiveFlowsCount() >> 1
createPromotionConfigFile()
createEnvironmentConfigFile()
createPipelineConfigFile()
createServiceConfigFile()
createWebhookConfigFile()
workFlow.loadConfigModels()
String oldChecksum = app.configChecksum
createAnotherPromotionConfigFile()
mdao.persist(_) >> _
when:
workFlow.loadConfigModels(true)
workFlow.loadConfigModels()
then:
thrown(Exception)
app.configChecksum != null
oldChecksum != app.configChecksum
}
}

View File

@ -37,13 +37,13 @@ class FlowSpecWithArgsSpec extends Specification {
flow.artifact = artifact
flow.deployments = deployments
flow.service = service
flow.serviceIdent = service
expect:
flow.artifact == artifact
flow.deployments == deployments
flow.service == service
flow.serviceIdent == service
}
def "equality passes for same deployments"() {
@ -139,8 +139,8 @@ class FlowSpecWithArgsSpec extends Specification {
Flow firstFlow = new Flow()
Flow secondFlow = new Flow()
firstFlow.service = service
secondFlow.service = service
firstFlow.serviceIdent = service
secondFlow.serviceIdent = service
expect:
firstFlow == secondFlow
@ -152,8 +152,8 @@ class FlowSpecWithArgsSpec extends Specification {
Flow firstFlow = new Flow()
Flow secondFlow = new Flow()
firstFlow.service = firstService
secondFlow.service = secondService
firstFlow.serviceIdent = firstService
secondFlow.serviceIdent = secondService
expect:
firstFlow != secondFlow
@ -251,8 +251,8 @@ class FlowSpecWithArgsSpec extends Specification {
Flow firstFlow = new Flow()
Flow secondFlow = new Flow()
firstFlow.service = service
secondFlow.service = service
firstFlow.serviceIdent = service
secondFlow.serviceIdent = service
expect:
firstFlow.hashCode() == secondFlow.hashCode()
@ -264,8 +264,8 @@ class FlowSpecWithArgsSpec extends Specification {
Flow firstFlow = new Flow()
Flow secondFlow = new Flow()
firstFlow.service = firstService
secondFlow.service = secondService
firstFlow.serviceIdent = firstService
secondFlow.serviceIdent = secondService
expect:
firstFlow.hashCode() != secondFlow.hashCode()