Merge pull request #177 from GiriDandu/issue-163-uat-artifact-for-deploydb

Issue 163 uat artifact for deploydb
This commit is contained in:
R. Tyler Croy 2015-05-11 07:07:10 -07:00
commit b2e88e3304
37 changed files with 2026 additions and 841 deletions

View File

@ -39,7 +39,7 @@ dependencies {
'dropwizard-views-mustache',
'dropwizard-testing',
].each {
compile withSources("io.dropwizard:${it}:0.8.1")
compile withSources("io.dropwizard:${it}:${dropwizardVersion}")
}
/* Used instead of the default HTTP connector for the Jersey client to

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,76 @@
package dropwizardintegtest
import javax.ws.rs.core.Response
/**
* This class is used by the spock integration test suites and UAT tests. This will be used by
* clients of deploydb to verify the contract of deploydb interface
*/
class IntegrationModelHelper {
private IntegrationRestApiClient integrationRestApiClient = null
IntegrationModelHelper(IntegrationRestApiClient integTestAppHelper) {
integrationRestApiClient = integTestAppHelper
}
boolean sendCreateArtifact() {
String path = "/api/artifacts"
String messageBody = """
{
"group" : "basic.group.1",
"name" : "bg1",
"version" : "1.2.345",
"sourceUrl" : "http://example.com/cucumber.jar"
}
"""
Response response = integrationRestApiClient.postJsonToPath(path, messageBody, false)
response.close()
return response.status == 201
}
boolean sendGetApi(String path) {
Response response = integrationRestApiClient.getFromPath(path, false)
response.close()
return response.status == 200
}
boolean sendDeploymentStartedTrigger(long deploymentId) {
String path = "/api/deployments/" + String.valueOf(deploymentId)
String messageBody = """
{
"status" : "STARTED"
}
"""
Response response = integrationRestApiClient.patchJsonToPath(path, messageBody)
response.close()
return response.status == 200
}
boolean sendDeploymentCompletedTrigger(long deploymentId) {
String path = "/api/deployments/" + String.valueOf(deploymentId)
String messageBody = """
{
"status" : "COMPLETED"
}
"""
Response response = integrationRestApiClient.patchJsonToPath(path, messageBody)
response.close()
return response.status == 200
}
boolean sendPromotionCompletedTrigger(long deploymentId) {
String path = "/api/deployments/" + String.valueOf(deploymentId) + "/promotions"
String messageBody = """
{
"name" : "basicPromo",
"status" : "SUCCESS",
"infoUrl" : "http://local.lookout.com/jenkins/job-id/2/results"
}
"""
Response response = integrationRestApiClient.postJsonToPath(path, messageBody, false)
response.close()
return response.status == 201
}
}

View File

@ -0,0 +1,90 @@
package dropwizardintegtest
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider
import com.github.mustachejava.DefaultMustacheFactory
import com.github.mustachejava.Mustache
import org.glassfish.jersey.client.ClientConfig
import javax.ws.rs.client.Client
import javax.ws.rs.client.ClientBuilder
import org.glassfish.jersey.apache.connector.ApacheConnectorProvider
import org.glassfish.jersey.client.JerseyInvocation
import javax.ws.rs.core.Response
import javax.ws.rs.client.Entity
/**
* This class is used by the spock integration test suites and UAT tests. This will be used by
* clients to send REST calls to servers and return responses from the server for further
* processing in the client code
*/
class IntegrationRestApiClient {
private Client jerseyClient = null
String host = "http://localhost"
int port = 8080
String processTemplate(String buffer, Map scope) {
DefaultMustacheFactory mf = new DefaultMustacheFactory()
StringWriter writer = new StringWriter()
Mustache m = mf.compile(new StringReader(buffer),
'cuke-stash-compiler')
m.execute(writer, scope)
return writer.toString()
}
Client getClient() {
if (this.jerseyClient == null) {
ClientConfig clientConfig = new ClientConfig()
clientConfig.connectorProvider(new ApacheConnectorProvider())
clientConfig.register(JacksonJsonProvider.class);
this.jerseyClient = ClientBuilder.newClient(clientConfig)
}
return this.jerseyClient
}
/**
* Create the proper full URL for our running app with the given path.
*
* If this is an admin request, we'll hit the admin port correctly
*/
String urlWithPort(String path, Boolean isAdmin) {
return host + String.format(":%d${path}", port)
}
JerseyInvocation makeRequestToPath(String path, String method, Entity entity) {
return this.makeRequestToPath(path, method, entity, false)
}
JerseyInvocation makeRequestToPath(String path, String method, Entity entity, Boolean isAdmin) {
return client.target(urlWithPort(path, isAdmin))
.request()
.build(method, entity)
}
/**
* Execute a POST to the test server for step definitions
*/
Response postJsonToPath(String path, String requestBody, Boolean isAdmin) {
return this.makeRequestToPath(path, 'POST', Entity.json(requestBody), isAdmin).invoke()
}
/**
* Execute a PATCH to the test server for step definitions
*/
Response patchJsonToPath(String path, String requestBody) {
return this.makeRequestToPath(path, 'PATCH', Entity.json(requestBody)).invoke()
}
Response deleteFromPath(String path) {
return this.makeRequestToPath(path, 'DELETE', null).invoke()
}
/**
* Minor convenience method to make sure we're dispatching GET requests to the
* right port in our test application
*/
Response getFromPath(String path, boolean isAdmin) {
return this.makeRequestToPath(path, 'GET', null , isAdmin).invoke()
}
}

View File

@ -0,0 +1,14 @@
Feature: DeployDB cleanup APIs
As a DeployDB administrator
I should be able to delete the artifact, deployment promotion results models from deploydb
Scenario: The model cleanup shall be successful
Given there is a deployment
When I POST to "/tasks/modelCleanup?group=com.example.cucumber&name=cucumber-artifact&version=1.0.1" from the admin app
Then the response should be 200
And the response body should be:
"""
Done!
"""
And the artifact doesn't exist

View File

@ -1,3 +1,4 @@
org.gradle.daemon=false
bintrayUser=
bintrayKey=
dropwizardVersion = 0.8.1

View File

@ -1 +1 @@
include "dropwizard-integtest"
include "dropwizard-integtest", "uat"

View File

@ -10,6 +10,7 @@ Given(~/^there is an artifact$/) { ->
dao.persist(a)
}
}
Given(~/^there are artifacts$/) { ->
withSession {
ArtifactDAO dao = new ArtifactDAO(sessionFactory)
@ -41,3 +42,14 @@ And(~/^the (.*?) is over ([1-9][0-9]*) characters$/) { String var, int varSize -
}
"""
}
And(~/^the artifact doesn't exist$/) { ->
boolean artifactFound = true
withSession {
ArtifactDAO dao = new ArtifactDAO(sessionFactory)
artifactFound = dao.artifactExists("com.example.cucumber",
"cucumber-artifact",
"1.0.1")
}
artifactFound == false
}

View File

@ -163,6 +163,9 @@ class DeployDBApp extends Application<DeployDBConfiguration> {
/** Add admin task for config reload */
environment.admin().addTask(new ConfigReloadTask(workFlow))
/** Add admin task for model cleanup */
environment.admin().addTask(new ModelCleanupTask(workFlow))
/** Register Ldap Authentication */
CachingAuthenticator<BasicCredentials, auth.User> authenticator = new CachingAuthenticator<>(
environment.metrics(),

View File

@ -0,0 +1,53 @@
package deploydb
import com.codahale.metrics.annotation.Timed
import com.google.common.collect.ImmutableMultimap
import deploydb.models.Artifact
import io.dropwizard.servlets.tasks.Task
import org.slf4j.Logger
import org.slf4j.LoggerFactory
/**
* Admin task to Cleanup models in the deploydb
*/
class ModelCleanupTask extends Task {
private WorkFlow workFlow
private static final Logger logger = LoggerFactory.getLogger(ConfigReloadTask.class)
/**
* Constructor
*
* @param workFlow - store the workFlow for executing load config
*/
ModelCleanupTask(WorkFlow workFlow) {
super('modelCleanup')
this.workFlow = workFlow
}
@Timed
@Override
void execute(ImmutableMultimap<String, String> parameters, PrintWriter output) throws Exception {
String group = String.valueOf(parameters.get("group").toArray()[0])
String name = String.valueOf(parameters.get("name").toArray()[0])
String version = String.valueOf(parameters.get("version").toArray()[0])
this.workFlow.deployDBApp.withHibernateSession() {
try {
// Lets fetch the artifact using name, group and version
Artifact artifact = this.workFlow.artifactDAO.findArtifactByGroupNameVersion(
group, name,version)
if(artifact == null) {
return
}
// Now delete flow using the artifact id
this.workFlow.flowDAO.deleteFlowByArtifactId(artifact.id)
output.println("Done!")
} catch (Exception e) {
logger.error("Cleanup of the model failed with an exception: ", e)
output.println("Failed: " + e.getMessage())
throw e
}
}
}
}

View File

@ -69,9 +69,27 @@ class ArtifactDAO extends AbstractDAO<Artifact> {
return artifacts
}
boolean artifactExists(String group, String name, String version) {
/**
* Fetch the artifact using group, name, version
* @param group Artifact group
* @param name Artifact name
* @param version Artifact version
* @return Artifact returned by the search if available otherwise null
*/
Artifact findArtifactByGroupNameVersion(String group, String name, String version) {
return criteria().add(Restrictions.eq('group', group))
.add(Restrictions.eq('name', name))
.add(Restrictions.eq('version', version)).uniqueResult() == null ? false: true
.add(Restrictions.eq('name', name))
.add(Restrictions.eq('version', version)).uniqueResult()
}
/**
* Check if the artifact exists based group, name, version
* @param group Artifact group
* @param name Artifact name
* @param version Artifact version
* @return true if Artifact exists otherwise false
*/
boolean artifactExists(String group, String name, String version) {
return findArtifactByGroupNameVersion(group, name, version) == null ? false: true
}
}

View File

@ -3,11 +3,25 @@ package deploydb.dao
import deploydb.models.Flow
import groovy.transform.InheritConstructors
import io.dropwizard.hibernate.AbstractDAO
import org.hibernate.Session
import org.hibernate.criterion.Restrictions
/**
* Class to represent Data Access Object for Flow
*/
@InheritConstructors
class FlowDAO extends AbstractDAO<Flow> {
}
/**
* Delete the flow based on Artifact id. This function will delete the assocaited models -
* Artifact, Deployments and PromotionResults
* @param id Artifact id of the flow
*/
void deleteFlowByArtifactId(long id) {
Session session = currentSession()
Flow flow = criteria().add(Restrictions.eq('artifact.id', id)).uniqueResult()
// now delete the found flow
session.delete(flow)
}
}

View File

@ -24,7 +24,7 @@ import javax.persistence.Table
@Table(name='flows')
class Flow extends AbstractModel {
@OneToOne(optional=false)
@OneToOne(cascade = CascadeType.ALL, optional=false)
@JoinColumn(name='artifactId', unique=false, nullable=false, updatable=false)
@JsonProperty
Artifact artifact

View File

@ -2,11 +2,16 @@ package deploydb
import spock.lang.*
import dropwizardintegtest.IntegrationModelHelper
import dropwizardintegtest.IntegrationRestApiClient
class DeploymentCompletedNotificationsSpec extends Specification {
IntegrationTestAppHelper integAppHelper = new IntegrationTestAppHelper()
IntegrationModelHelper integModelHelper = new IntegrationModelHelper(integAppHelper)
IntegrationRestApiClient integrationRestApiClient = new IntegrationRestApiClient()
IntegrationModelHelper integModelHelper = new IntegrationModelHelper(integrationRestApiClient)
private WebhooksModelConfigHelper mcfgHelper = new WebhooksModelConfigHelper()
private long deploymentId = 1L
def setup() {
mcfgHelper.setup()
@ -26,7 +31,7 @@ class DeploymentCompletedNotificationsSpec extends Specification {
def "no webhook should be called when you receive deployment completed trigger if there is no webhook config" () {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createEnvironmentNoWebhooksConfigFile()
// load up the configuration
@ -36,7 +41,7 @@ class DeploymentCompletedNotificationsSpec extends Specification {
integModelHelper.sendCreateArtifact()
when:
boolean success = integModelHelper.sendDeploymentCompletedTrigger()
boolean success = integModelHelper.sendDeploymentCompletedTrigger(deploymentId)
then:
success == true
@ -47,7 +52,7 @@ class DeploymentCompletedNotificationsSpec extends Specification {
def "webhook should be called when you receive deployment completed trigger" () {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createDeploymentCompletedWebhookConfigFile()
mcfgHelper.createEnvironmentNoWebhooksConfigFile()
@ -64,7 +69,7 @@ class DeploymentCompletedNotificationsSpec extends Specification {
integModelHelper.sendCreateArtifact()
when:
boolean success = integModelHelper.sendDeploymentCompletedTrigger()
boolean success = integModelHelper.sendDeploymentCompletedTrigger(deploymentId)
then:
success == true
@ -75,7 +80,7 @@ class DeploymentCompletedNotificationsSpec extends Specification {
def "environment webhook should be called when you receive deployment completed trigger" () {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createDeploymentCompletedEnvironmentWebhookConfigFile()
// load up the configuration
@ -91,7 +96,7 @@ class DeploymentCompletedNotificationsSpec extends Specification {
integModelHelper.sendCreateArtifact()
when:
boolean success = integModelHelper.sendDeploymentCompletedTrigger()
boolean success = integModelHelper.sendDeploymentCompletedTrigger(deploymentId)
then:
success == true
@ -102,7 +107,7 @@ class DeploymentCompletedNotificationsSpec extends Specification {
def "both global and environment webhooks should be called when you receive deployment completed trigger" () {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createDeploymentCompletedWebhookConfigFile()
mcfgHelper.createDeploymentCompletedEnvironmentWebhookConfigFile()
@ -119,7 +124,7 @@ class DeploymentCompletedNotificationsSpec extends Specification {
integModelHelper.sendCreateArtifact()
when:
boolean success = integModelHelper.sendDeploymentCompletedTrigger()
boolean success = integModelHelper.sendDeploymentCompletedTrigger(deploymentId)
then:
success == true
@ -130,7 +135,7 @@ class DeploymentCompletedNotificationsSpec extends Specification {
def "multiple webhooks should be called when you receive deployment completed trigger" () {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createMultipleDeploymentCompletedWebhooksConfigFile()
mcfgHelper.createEnvironmentNoWebhooksConfigFile()
@ -147,7 +152,7 @@ class DeploymentCompletedNotificationsSpec extends Specification {
integModelHelper.sendCreateArtifact()
when:
boolean success = integModelHelper.sendDeploymentCompletedTrigger()
boolean success = integModelHelper.sendDeploymentCompletedTrigger(deploymentId)
then:
success == true
@ -158,7 +163,7 @@ class DeploymentCompletedNotificationsSpec extends Specification {
def "multiple environments webhook should be called when you receive deployment completed trigger" () {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createMultipleDeploymentCompletedEnvironmentWebhooksConfigFile()
// load up the configuration
@ -174,7 +179,7 @@ class DeploymentCompletedNotificationsSpec extends Specification {
integModelHelper.sendCreateArtifact()
when:
boolean success = integModelHelper.sendDeploymentCompletedTrigger()
boolean success = integModelHelper.sendDeploymentCompletedTrigger(deploymentId)
then:
success == true
@ -185,7 +190,7 @@ class DeploymentCompletedNotificationsSpec extends Specification {
def "both multiple global and environment webhooks should be called when you receive deployment completed trigger" () {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createMultipleDeploymentCompletedWebhooksConfigFile()
mcfgHelper.createMultipleDeploymentCompletedEnvironmentWebhooksConfigFile()
@ -203,7 +208,7 @@ class DeploymentCompletedNotificationsSpec extends Specification {
integModelHelper.sendCreateArtifact()
when:
boolean success = integModelHelper.sendDeploymentCompletedTrigger()
boolean success = integModelHelper.sendDeploymentCompletedTrigger(deploymentId)
then:
success == true

View File

@ -1,11 +1,14 @@
package deploydb
import spock.lang.*
import dropwizardintegtest.IntegrationModelHelper
import dropwizardintegtest.IntegrationRestApiClient
class DeploymentCreatedNotificationsSpec extends Specification {
IntegrationTestAppHelper integAppHelper = new IntegrationTestAppHelper()
IntegrationModelHelper integModelHelper = new IntegrationModelHelper(integAppHelper)
IntegrationRestApiClient integrationRestApiClient = new IntegrationRestApiClient()
IntegrationModelHelper integModelHelper = new IntegrationModelHelper(integrationRestApiClient)
private WebhooksModelConfigHelper mcfgHelper = new WebhooksModelConfigHelper()
def setup() {
@ -27,7 +30,7 @@ class DeploymentCreatedNotificationsSpec extends Specification {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createEnvironmentNoWebhooksConfigFile()
// load up the configuration
@ -47,7 +50,7 @@ class DeploymentCreatedNotificationsSpec extends Specification {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createWebhookConfigFile()
mcfgHelper.createEnvironmentNoWebhooksConfigFile()
@ -74,7 +77,7 @@ class DeploymentCreatedNotificationsSpec extends Specification {
given:
// create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createEnvironmentConfigFile()
// load up the config
@ -99,7 +102,7 @@ class DeploymentCreatedNotificationsSpec extends Specification {
given:
// create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createWebhookConfigFile()
mcfgHelper.createEnvironmentConfigFile()
@ -125,7 +128,7 @@ class DeploymentCreatedNotificationsSpec extends Specification {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createMultipleWebhooksConfigFile()
mcfgHelper.createEnvironmentNoWebhooksConfigFile()
@ -151,7 +154,7 @@ class DeploymentCreatedNotificationsSpec extends Specification {
given:
// create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createMultipleWebhooksEnvironmentConfigFile()
// load up the config
@ -176,7 +179,7 @@ class DeploymentCreatedNotificationsSpec extends Specification {
given:
// create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createMultipleWebhooksConfigFile()
mcfgHelper.createMultipleWebhooksEnvironmentConfigFile()

View File

@ -2,11 +2,16 @@ package deploydb
import spock.lang.*
import dropwizardintegtest.IntegrationModelHelper
import dropwizardintegtest.IntegrationRestApiClient
class DeploymentStartedNotificationsSpec extends Specification {
IntegrationTestAppHelper integAppHelper = new IntegrationTestAppHelper()
IntegrationModelHelper integModelHelper = new IntegrationModelHelper(integAppHelper)
IntegrationRestApiClient integrationRestApiClient = new IntegrationRestApiClient()
IntegrationModelHelper integModelHelper = new IntegrationModelHelper(integrationRestApiClient)
private WebhooksModelConfigHelper mcfgHelper = new WebhooksModelConfigHelper()
private long deploymentId = 1L
def setup() {
mcfgHelper.setup()
@ -26,7 +31,7 @@ class DeploymentStartedNotificationsSpec extends Specification {
def "no webhook should be called when you receive deployment started trigger if there is no webhook config" () {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createEnvironmentNoWebhooksConfigFile()
// load up the configuration
@ -34,7 +39,7 @@ class DeploymentStartedNotificationsSpec extends Specification {
integModelHelper.sendCreateArtifact()
when:
boolean success = integModelHelper.sendDeploymentStartedTrigger()
boolean success = integModelHelper.sendDeploymentStartedTrigger(deploymentId)
then:
success == true
@ -45,7 +50,7 @@ class DeploymentStartedNotificationsSpec extends Specification {
def "webhook should be called when you receive deployment started trigger" () {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createDeploymentStartedWebhookConfigFile()
mcfgHelper.createEnvironmentNoWebhooksConfigFile()
@ -62,7 +67,7 @@ class DeploymentStartedNotificationsSpec extends Specification {
integModelHelper.sendCreateArtifact()
when:
boolean success = integModelHelper.sendDeploymentStartedTrigger()
boolean success = integModelHelper.sendDeploymentStartedTrigger(deploymentId)
then:
success == true
@ -73,7 +78,7 @@ class DeploymentStartedNotificationsSpec extends Specification {
def "environment webhook should be called when you receive deployment started trigger" () {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createDeploymentStartedEnvironmentWebhookConfigFile()
// load up the configuration
@ -89,7 +94,7 @@ class DeploymentStartedNotificationsSpec extends Specification {
integModelHelper.sendCreateArtifact()
when:
boolean success = integModelHelper.sendDeploymentStartedTrigger()
boolean success = integModelHelper.sendDeploymentStartedTrigger(deploymentId)
then:
success == true
@ -100,7 +105,7 @@ class DeploymentStartedNotificationsSpec extends Specification {
def "both global and environment webhooks should be called when you receive deployment started trigger" () {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createDeploymentStartedWebhookConfigFile()
mcfgHelper.createDeploymentStartedEnvironmentWebhookConfigFile()
@ -117,7 +122,7 @@ class DeploymentStartedNotificationsSpec extends Specification {
integModelHelper.sendCreateArtifact()
when:
boolean success = integModelHelper.sendDeploymentStartedTrigger()
boolean success = integModelHelper.sendDeploymentStartedTrigger(deploymentId)
then:
success == true
@ -128,7 +133,7 @@ class DeploymentStartedNotificationsSpec extends Specification {
def "multiple webhooks should be called when you receive deployment started trigger" () {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createMultipleDeploymentStartedWebhooksConfigFile()
mcfgHelper.createEnvironmentNoWebhooksConfigFile()
@ -144,7 +149,7 @@ class DeploymentStartedNotificationsSpec extends Specification {
integModelHelper.sendCreateArtifact()
when:
boolean success = integModelHelper.sendDeploymentStartedTrigger()
boolean success = integModelHelper.sendDeploymentStartedTrigger(deploymentId)
then:
success == true
@ -155,7 +160,7 @@ class DeploymentStartedNotificationsSpec extends Specification {
def "multiple environments webhook should be called when you receive deployment started trigger" () {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createMultipleDeploymentStartedEnvironmentWebhooksConfigFile()
// load up the configuration
@ -172,7 +177,7 @@ class DeploymentStartedNotificationsSpec extends Specification {
integModelHelper.sendCreateArtifact()
when:
boolean success = integModelHelper.sendDeploymentStartedTrigger()
boolean success = integModelHelper.sendDeploymentStartedTrigger(deploymentId)
then:
success == true
@ -183,7 +188,7 @@ class DeploymentStartedNotificationsSpec extends Specification {
def "both multiple global and environment webhooks should be called when you receive deployment started trigger" () {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createMultipleDeploymentStartedWebhooksConfigFile()
mcfgHelper.createMultipleDeploymentStartedEnvironmentWebhooksConfigFile()
@ -201,7 +206,7 @@ class DeploymentStartedNotificationsSpec extends Specification {
integModelHelper.sendCreateArtifact()
when:
boolean success = integModelHelper.sendDeploymentStartedTrigger()
boolean success = integModelHelper.sendDeploymentStartedTrigger(deploymentId)
then:
success == true

View File

@ -1,63 +0,0 @@
package deploydb
import javax.ws.rs.core.Response
class IntegrationModelHelper {
private IntegrationTestAppHelper integrationTestAppHelper = null
IntegrationModelHelper(IntegrationTestAppHelper integTestAppHelper) {
integrationTestAppHelper = integTestAppHelper
}
boolean sendCreateArtifact() {
String path = "/api/artifacts"
String messageBody = """
{
"group" : "basic.group.1",
"name" : "bg1",
"version" : "1.2.345",
"sourceUrl" : "http://example.com/cucumber.jar"
}
"""
return (integrationTestAppHelper.postJsonToPath(path, messageBody, false)).status == 201
}
boolean sendDeploymentStartedTrigger() {
String path = "/api/deployments/1"
String messageBody = """
{
"status" : "STARTED"
}
"""
Response response = integrationTestAppHelper.patchJsonToPath(path, messageBody)
response.close()
return response.status == 200
}
boolean sendDeploymentCompletedTrigger() {
String path = "/api/deployments/1"
String messageBody = """
{
"status" : "COMPLETED"
}
"""
Response response = integrationTestAppHelper.patchJsonToPath(path, messageBody)
response.close()
return response.status == 200
}
boolean sendPromotionCompletedTrigger() {
String path = "/api/deployments/1/promotions"
String messageBody = """
{
"name" : "basicPromo",
"status" : "SUCCESS",
"infoUrl" : "http://local.lookout.com/jenkins/job-id/2/results"
}
"""
Response response = integrationTestAppHelper.postJsonToPath(path, messageBody, false)
response.close()
return response.status == 201
}
}

View File

@ -1,16 +1,6 @@
package deploydb
import deploydb.models.Webhook.Webhook
import com.github.mustachejava.DefaultMustacheFactory
import com.github.mustachejava.Mustache
import org.glassfish.jersey.client.ClientConfig
import javax.ws.rs.client.Client
import javax.ws.rs.client.ClientBuilder
import org.glassfish.jersey.apache.connector.ApacheConnectorProvider
import org.glassfish.jersey.client.JerseyInvocation
import javax.ws.rs.core.Response
import javax.ws.rs.client.Entity
import org.hibernate.Session
import org.hibernate.SessionFactory
@ -20,10 +10,8 @@ import dropwizardintegtest.TestLdapServer
import dropwizardintegtest.WebhookTestServerAppRunner
import dropwizardintegtest.webhookTestServerApp
class IntegrationTestAppHelper {
private StubAppRunner runner = null
private Client jerseyClient = null
private WebhookTestServerAppRunner webhookRunner = null
private TestLdapServer testLdapServer = null
@ -48,65 +36,6 @@ class IntegrationTestAppHelper {
}
}
String processTemplate(String buffer, Map scope) {
DefaultMustacheFactory mf = new DefaultMustacheFactory()
StringWriter writer = new StringWriter()
Mustache m = mf.compile(new StringReader(buffer),
'cuke-stash-compiler')
m.execute(writer, scope)
return writer.toString()
}
Client getClient() {
if (this.jerseyClient == null) {
ClientConfig clientConfig = new ClientConfig()
clientConfig.connectorProvider(new ApacheConnectorProvider())
this.jerseyClient = ClientBuilder.newClient(clientConfig)
}
return this.jerseyClient
}
/**
* Create the proper full URL for our running app with the given path.
*
* If this is an admin request, we'll hit the admin port correctly
*/
String urlWithPort(String path, Boolean isAdmin) {
int port = isAdmin ? runner.adminPort : runner.localPort
return String.format("http://localhost:%d${path}", port)
}
JerseyInvocation makeRequestToPath(String path, String method, Entity entity) {
return this.makeRequestToPath(path, method, entity, false)
}
JerseyInvocation makeRequestToPath(String path, String method, Entity entity, Boolean isAdmin) {
return client.target(urlWithPort(path, isAdmin))
.request()
.build(method, entity)
}
/**
* Execute a POST to the test server for step definitions
*/
Response postJsonToPath(String path, String requestBody, Boolean isAdmin) {
return this.makeRequestToPath(path, 'POST', Entity.json(requestBody), isAdmin).invoke()
}
/**
* Execute a PATCH to the test server for step definitions
*/
Response patchJsonToPath(String path, String requestBody) {
return this.makeRequestToPath(path, 'PATCH', Entity.json(requestBody)).invoke()
}
Response deleteFromPath(String path) {
return this.makeRequestToPath(path, 'DELETE', null).invoke()
}
/*
* Get url path from webhook config body
*/
@ -117,14 +46,6 @@ class IntegrationTestAppHelper {
return getUrlPathFromWebhook(webhook, configBody, eventType)
}
/**
* Minor convenience method to make sure we're dispatching GET requests to the
* right port in our test application
*/
Response getFromPath(String path, boolean isAdmin) {
return this.makeRequestToPath(path, 'GET', null , isAdmin).invoke()
}
/** Set config directory */
void setConfigDirectory(String configDirectory) {
this.runner.setConfigDirectory(configDirectory)

View File

@ -204,6 +204,21 @@ promotions:
serviceFile.write(fileContents)
}
def createServicePromotionPipelineModelsConfigFiles() {
createPromotionConfigFile()
createPipelineConfigFile()
createServiceConfigFile()
}
def createBasicProdServicePromotionPipelineModelsConfigFiles() {
createEnvironmentConfigFile()
createProdEnvironmentConfigFile()
createPromotionConfigFile()
createBasicProdPipelineConfigFile()
createServiceConfigFile()
}
}
class WebhooksModelConfigHelper extends deploydb.ModelConfigHelper {
@ -368,18 +383,4 @@ webhook:
"""
createEnvironmentWebhookConfigFile(fileContents, "basicEnv")
}
def createServicePromoitionPipelineModelsConfigFiles() {
createPromotionConfigFile()
createPipelineConfigFile()
createServiceConfigFile()
}
def createBasicProdServicePromoitionPipelineModelsConfigFiles() {
createEnvironmentConfigFile()
createProdEnvironmentConfigFile()
createPromotionConfigFile()
createBasicProdPipelineConfigFile()
createServiceConfigFile()
}
}

View File

@ -2,11 +2,16 @@ package deploydb
import spock.lang.*
import dropwizardintegtest.IntegrationModelHelper
import dropwizardintegtest.IntegrationRestApiClient
class MultipleEnvironmentsNotificationsSpec extends Specification {
IntegrationTestAppHelper integAppHelper = new IntegrationTestAppHelper()
IntegrationModelHelper integModelHelper = new IntegrationModelHelper(integAppHelper)
IntegrationRestApiClient integrationRestApiClient = new IntegrationRestApiClient()
IntegrationModelHelper integModelHelper = new IntegrationModelHelper(integrationRestApiClient)
private WebhooksModelConfigHelper mcfgHelper = new WebhooksModelConfigHelper()
private long deploymentId = 1L
def setup() {
mcfgHelper.setup()
@ -34,7 +39,7 @@ class MultipleEnvironmentsNotificationsSpec extends Specification {
given:
// Create the required config
mcfgHelper.createBasicProdServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createBasicProdServicePromotionPipelineModelsConfigFiles()
// load up the configuration
integAppHelper.runner.getApplication().loadModelConfiguration()
@ -61,7 +66,7 @@ class MultipleEnvironmentsNotificationsSpec extends Specification {
integAppHelper.webhookRunner.requestWebhookObject.configuredUriPaths =
["/job/basicEnv-deploy-created/build", "/job/basicEnv-deploy-completed/build"]
success = integModelHelper.sendDeploymentCompletedTrigger()
success = integModelHelper.sendDeploymentCompletedTrigger(deploymentId)
then:
success == true
@ -76,7 +81,7 @@ class MultipleEnvironmentsNotificationsSpec extends Specification {
["/job/basicEnv-deploy-created/build", "/job/basicEnv-deploy-completed/build",
"/job/prodEnv-deploy-created/build"]
success = integModelHelper.sendPromotionCompletedTrigger()
success = integModelHelper.sendPromotionCompletedTrigger(deploymentId)
then:
success == true

View File

@ -2,11 +2,16 @@ package deploydb
import spock.lang.*
import dropwizardintegtest.IntegrationModelHelper
import dropwizardintegtest.IntegrationRestApiClient
class PromotionCompletedNotificationsSpec extends Specification {
IntegrationTestAppHelper integAppHelper = new IntegrationTestAppHelper()
IntegrationModelHelper integModelHelper = new IntegrationModelHelper(integAppHelper)
IntegrationRestApiClient integrationRestApiClient = new IntegrationRestApiClient()
IntegrationModelHelper integModelHelper = new IntegrationModelHelper(integrationRestApiClient)
private WebhooksModelConfigHelper mcfgHelper = new WebhooksModelConfigHelper()
private long deploymentId = 1L
def setup() {
mcfgHelper.setup()
@ -30,14 +35,14 @@ class PromotionCompletedNotificationsSpec extends Specification {
integModelHelper.sendCreateArtifact()
integModelHelper.sendDeploymentStartedTrigger()
integModelHelper.sendDeploymentCompletedTrigger()
integModelHelper.sendDeploymentStartedTrigger(deploymentId)
integModelHelper.sendDeploymentCompletedTrigger(deploymentId)
}
def "no webhook should be called when you receive promotion completed trigger if there is no webhook config" () {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createEnvironmentNoWebhooksConfigFile()
// load up the configuration
@ -47,7 +52,7 @@ class PromotionCompletedNotificationsSpec extends Specification {
setupDeploymentForPromotionTrigger()
when:
boolean success = integModelHelper.sendPromotionCompletedTrigger()
boolean success = integModelHelper.sendPromotionCompletedTrigger(deploymentId)
then:
success == true
@ -58,7 +63,7 @@ class PromotionCompletedNotificationsSpec extends Specification {
def "webhook should be called when you receive promotion completed trigger" () {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createPromotionCompletedWebhookConfigFile()
mcfgHelper.createEnvironmentNoWebhooksConfigFile()
@ -74,7 +79,7 @@ class PromotionCompletedNotificationsSpec extends Specification {
setupDeploymentForPromotionTrigger()
when:
boolean success = integModelHelper.sendPromotionCompletedTrigger()
boolean success = integModelHelper.sendPromotionCompletedTrigger(deploymentId)
then:
success == true
@ -85,7 +90,7 @@ class PromotionCompletedNotificationsSpec extends Specification {
def "environment webhook should be called when you receive promotion completed trigger" () {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createPromotionCompletedEnvironmentWebhookConfigFile()
// load up the configuration
@ -100,7 +105,7 @@ class PromotionCompletedNotificationsSpec extends Specification {
setupDeploymentForPromotionTrigger()
when:
boolean success = integModelHelper.sendPromotionCompletedTrigger()
boolean success = integModelHelper.sendPromotionCompletedTrigger(deploymentId)
then:
success == true
@ -111,7 +116,7 @@ class PromotionCompletedNotificationsSpec extends Specification {
def "both global and environment webhooks should be called when you receive promotion completed trigger" () {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createPromotionCompletedWebhookConfigFile()
mcfgHelper.createPromotionCompletedEnvironmentWebhookConfigFile()
@ -127,7 +132,7 @@ class PromotionCompletedNotificationsSpec extends Specification {
setupDeploymentForPromotionTrigger()
when:
boolean success = integModelHelper.sendPromotionCompletedTrigger()
boolean success = integModelHelper.sendPromotionCompletedTrigger(deploymentId)
then:
success == true
@ -138,7 +143,7 @@ class PromotionCompletedNotificationsSpec extends Specification {
def "multiple webhooks should be called when you receive promotion completed trigger" () {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createMultiplePromotionCompletedWebhookConfigFile()
mcfgHelper.createEnvironmentNoWebhooksConfigFile()
@ -154,7 +159,7 @@ class PromotionCompletedNotificationsSpec extends Specification {
setupDeploymentForPromotionTrigger()
when:
boolean success = integModelHelper.sendPromotionCompletedTrigger()
boolean success = integModelHelper.sendPromotionCompletedTrigger(deploymentId)
then:
success == true
@ -165,7 +170,7 @@ class PromotionCompletedNotificationsSpec extends Specification {
def "multiple environment webhooks should be called when you receive promotion completed trigger" () {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createMultiplePromotionCompletedEnvironmentWebhookConfigFile()
// load up the configuration
@ -180,7 +185,7 @@ class PromotionCompletedNotificationsSpec extends Specification {
setupDeploymentForPromotionTrigger()
when:
boolean success = integModelHelper.sendPromotionCompletedTrigger()
boolean success = integModelHelper.sendPromotionCompletedTrigger(deploymentId)
then:
success == true
@ -191,7 +196,7 @@ class PromotionCompletedNotificationsSpec extends Specification {
def "both multiple global and environment webhooks should be called when you receive promotion completed trigger" () {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createMultiplePromotionCompletedEnvironmentWebhookConfigFile()
mcfgHelper.createMultiplePromotionCompletedWebhookConfigFile()
@ -208,7 +213,7 @@ class PromotionCompletedNotificationsSpec extends Specification {
setupDeploymentForPromotionTrigger()
when:
boolean success = integModelHelper.sendPromotionCompletedTrigger()
boolean success = integModelHelper.sendPromotionCompletedTrigger(deploymentId)
then:
success == true
@ -232,9 +237,8 @@ class PromotionCompletedNotificationsSpec extends Specification {
setupDeploymentForPromotionTrigger()
when:
boolean success = integModelHelper.sendPromotionCompletedTrigger()
boolean success = integModelHelper.sendPromotionCompletedTrigger(deploymentId)
models.Deployment deployment
Long deploymentId = 1
integAppHelper.withSession {
deployment = integAppHelper.runner.getApplication().workFlow.deploymentDAO
.get(deploymentId)

View File

@ -1,7 +1,6 @@
package deploydb.auth
import com.google.common.base.Optional
import deploydb.IntegrationModelHelper
import deploydb.IntegrationTestAppHelper
import deploydb.ModelConfigHelper
import io.dropwizard.auth.basic.BasicCredentials
@ -28,7 +27,6 @@ class LdapAuthenticatorSpec extends Specification {
class LdapAuthenticatorWithArgsSpec extends Specification {
private IntegrationTestAppHelper integAppHelper = new IntegrationTestAppHelper()
private IntegrationModelHelper integModelHelper = new IntegrationModelHelper(integAppHelper)
private ModelConfigHelper mcfgHelper = new ModelConfigHelper()
LdapConfiguration ldapConfiguration

View File

@ -1,6 +1,7 @@
package deploydb.dao
import deploydb.IntegrationModelHelper
import dropwizardintegtest.IntegrationModelHelper
import dropwizardintegtest.IntegrationRestApiClient
import deploydb.IntegrationTestAppHelper
import deploydb.WebhooksModelConfigHelper
import deploydb.models.Deployment
@ -10,7 +11,8 @@ import spock.lang.*
class DeploymentDAOSpec extends Specification {
IntegrationTestAppHelper integAppHelper = new IntegrationTestAppHelper()
IntegrationModelHelper integModelHelper = new IntegrationModelHelper(integAppHelper)
IntegrationRestApiClient integrationRestApiClient = new IntegrationRestApiClient()
IntegrationModelHelper integModelHelper = new IntegrationModelHelper(integrationRestApiClient)
private WebhooksModelConfigHelper mcfgHelper = new WebhooksModelConfigHelper()
def setup() {
@ -60,7 +62,7 @@ class DeploymentDAOSpec extends Specification {
def "getByEnvironmentIdent() should returns deployments for the environment ident"() {
given:
// Create the required config
mcfgHelper.createServicePromoitionPipelineModelsConfigFiles()
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createEnvironmentNoWebhooksConfigFile()
// load up the configuration

View File

@ -1,7 +1,14 @@
package deploydb.models
import deploydb.IntegrationTestAppHelper
import deploydb.Status
import deploydb.dao.PromotionResultDAO
import spock.lang.Ignore
import spock.lang.Specification
import dropwizardintegtest.IntegrationModelHelper
import dropwizardintegtest.IntegrationRestApiClient
import deploydb.ModelConfigHelper
class FlowSpec extends Specification {
@ -272,3 +279,73 @@ class FlowSpecWithArgsSpec extends Specification {
}
}
class FlowCleanupSpec extends Specification {
IntegrationTestAppHelper integAppHelper = new IntegrationTestAppHelper()
IntegrationRestApiClient integrationRestApiClient = new IntegrationRestApiClient()
IntegrationModelHelper integModelHelper = new IntegrationModelHelper(integrationRestApiClient)
private ModelConfigHelper mcfgHelper = new ModelConfigHelper()
boolean foundArtifact = true
List<Deployment> deployments
List<PromotionResult> promotionResults
def setup() {
mcfgHelper.setup()
integAppHelper.startAppWithConfiguration('deploydb.spock.yml')
integAppHelper.runner.getApplication().configuration.configDirectory = mcfgHelper.baseCfgDirName
foundArtifact = true
}
def cleanup() {
integAppHelper.stopApp()
mcfgHelper.cleanup()
}
void updateModelsExistence() {
// update the existence of flow, deployments and promotion results
PromotionResultDAO promotionResultDAO = new PromotionResultDAO(
integAppHelper.runner.getApplication().getSessionFactory())
integAppHelper.runner.getApplication().withHibernateSession {
foundArtifact = integAppHelper.runner.getApplication().workFlow.artifactDAO.
artifactExists("basic.group.1", "bg1", "1.2.345")
deployments = integAppHelper.runner.getApplication().workFlow.deploymentDAO.
getByPage(0, 10)
promotionResults = promotionResultDAO.getByPage(0, 10)
}
}
def "delete of existing flow cleans up deployments and artifact"() {
given:
// Create the required config
mcfgHelper.createServicePromotionPipelineModelsConfigFiles()
mcfgHelper.createEnvironmentNoWebhooksConfigFile()
// load up the configuration
integAppHelper.runner.getApplication().loadModelConfiguration()
// create the artifact, which will create deployments and flow
integModelHelper.sendCreateArtifact()
when:
// remove the flow
integAppHelper.runner.getApplication().withHibernateSession {
Artifact artifact = integAppHelper.runner.getApplication().workFlow.artifactDAO.
findArtifactByGroupNameVersion("basic.group.1", "bg1", "1.2.345")
integAppHelper.runner.getApplication().workFlow.flowDAO.
deleteFlowByArtifactId(artifact.id)
}
then:
updateModelsExistence()
foundArtifact == false
deployments.isEmpty()
promotionResults.isEmpty()
}
}

84
uat/build.gradle Normal file
View File

@ -0,0 +1,84 @@
apply plugin: 'eclipse'
apply plugin: 'groovy'
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'com.jfrog.bintray'
version = rootProject.version
group = 'com.github.lookout.uat'
description = 'User Acceptance Testing artifact for deploydb'
mainClassName = 'uat.UatMainApp'
dependencies {
compile ('org.codehaus.groovy:groovy-all:2.4.0+')
[
'dropwizard-core',
'dropwizard-assets',
'dropwizard-jersey',
'dropwizard-views-mustache',
'dropwizard-testing',
].each {
compile ("io.dropwizard:${it}:${dropwizardVersion}")
}
/* Used instead of the default HTTP connector for the Jersey client to
* allow us to make PATCH requests
*/
compile ('org.glassfish.jersey.connectors:jersey-apache-connector:2.+')
compile('com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:2.4.1')
compile ('org.spockframework:spock-core:0.7-groovy-2.0')
compile project(':dropwizard-integtest')
}
repositories {
jcenter()
}
shadowJar {
exclude 'META-INF/*.RSA', 'META-INF/*.DSA'
manifest {
attributes 'Main-Class' : mainClassName
}
}
assemble.dependsOn shadowJar
////////////////////////////////////////////////////////////////////////////////
// PUBLISHING TASKS
////////////////////////////////////////////////////////////////////////////////
artifacts {
archives distShadowTar, distShadowZip, shadowJar
}
/* Disabling the addition of a jar to the archives which gets in the way of our
* means to publish artifacts. It appears that the java plugin doesn't give you
* the means to remove the 'default jar' from the artifacts list by any other
* means
*/
jar.enabled = false
configurations.archives.artifacts.removeAll { it.archiveTask.is jar }
bintray {
user = project.bintrayUser
key = project.bintrayKey
publish = true
dryRun = false
configurations = ['archives']
pkg {
userOrg = 'lookout'
repo = 'systems'
name = 'DeployDB'
labels = []
version {
name = project.version
vcsTag = "v${project.version}"
desc = project.description
}
}
}
bintrayUpload.dependsOn assemble

1
uat/config.groovy Normal file
View File

@ -0,0 +1 @@
customTestReportDir="./build"

View File

@ -0,0 +1,60 @@
package uat
import spock.lang.*
import dropwizardintegtest.IntegrationModelHelper
import dropwizardintegtest.IntegrationRestApiClient
import javax.ws.rs.core.Response
class ArtifactTriggerSpec extends Specification {
IntegrationRestApiClient integrationRestApiClient = new IntegrationRestApiClient()
IntegrationModelHelper integModelHelper = new IntegrationModelHelper(integrationRestApiClient)
def setup() {
integrationRestApiClient.host = "http://" + System.getProperty("DeploydbHost")
integrationRestApiClient.port = Integer.valueOf(System.getProperty("DeploydbPort"))
}
boolean sendCreateArtifact() {
String path = "/api/artifacts"
String messageBody = """
{
"group" : "basic.group.1",
"name" : "bg1",
"version" : "1.2.345",
"sourceUrl" : "http://example.com/cucumber.jar"
}
"""
Response response = integrationRestApiClient.postJsonToPath(path, messageBody, false)
UatArtifact uatArtifact = response.readEntity(UatArtifact)
/**
* We can't hard code the id to fetch the deployment because in some environments there
* will be more artifacts than test artifacts created by UAT
* Let's save the created artifact id and use them to read artifact and deployments
*/
System.setProperty("artifactId", String.valueOf(uatArtifact.id))
response.close()
return response.status == 201
}
def "create artifact should return success"() {
when:
boolean success = sendCreateArtifact()
then:
success == true
}
def "read artifact should return success"() {
when:
String path = "/api/artifacts/" + System.getProperty("artifactId")
boolean success = integModelHelper.sendGetApi(path)
then:
success == true
}
}

View File

@ -0,0 +1,27 @@
package uat
import dropwizardintegtest.IntegrationRestApiClient
import uat.consul.ServiceHealth
import javax.ws.rs.core.Response
import javax.ws.rs.core.GenericType
class ConsulClient {
IntegrationRestApiClient restApiClient = new IntegrationRestApiClient()
def getDeploydbHosts() {
restApiClient.port = 8500
String path = "/v1/health/service/deploydb?passing"
Response response = restApiClient.getFromPath(path, false)
def hosts = [:]
List<ServiceHealth> serviceHealthList = response.readEntity(new GenericType<List<ServiceHealth>>(){})
serviceHealthList.each {
hosts[it.node.address] = it.service.port
}
hosts
}
}

View File

@ -0,0 +1,71 @@
package uat
import spock.lang.*
import dropwizardintegtest.IntegrationModelHelper
import dropwizardintegtest.IntegrationRestApiClient
import javax.ws.rs.core.GenericType
import javax.ws.rs.core.Response
class DeploymentTriggerSpec extends Specification {
IntegrationRestApiClient integrationRestApiClient = new IntegrationRestApiClient()
IntegrationModelHelper integModelHelper = new IntegrationModelHelper(integrationRestApiClient)
static List<UatDeployment> uatDeployments
def setup() {
integrationRestApiClient.host = "http://" + System.getProperty("DeploydbHost")
integrationRestApiClient.port = Integer.valueOf(System.getProperty("DeploydbPort"))
}
boolean sendGetDeployments() {
/**
* We can't hard code the id to fetch the deployment because in some environments there
* will be more artifacts than test artifacts created by UAT
* Let's use the artifact id of that was created as part of ArtifactTriggerSpec and use
* that to fectch the deployment.
*/
String path = "/api/deployments/by-artifact/" + System.getProperty("artifactId")
Response response = integrationRestApiClient.getFromPath(path, false)
uatDeployments = response.readEntity(new GenericType<List<UatDeployment>>(){})
response.close()
return response.status == 200
}
def "read deployments should return success"() {
when:
boolean success = sendGetDeployments()
then:
success == true
}
def "update the deployments to started should return success"() {
when:
boolean success = integModelHelper.sendDeploymentStartedTrigger(uatDeployments[0].id)
then:
success == true
}
def "update the deployments to completed should return success"() {
when:
boolean success = integModelHelper.sendDeploymentCompletedTrigger(uatDeployments[0].id)
then:
success == true
}
def "update the deployments promotion completed should return success"() {
when:
boolean success = integModelHelper.sendPromotionCompletedTrigger(uatDeployments[0].id)
then:
success == true
}
}

View File

@ -0,0 +1,40 @@
package uat
import spock.util.EmbeddedSpecRunner
import dropwizardintegtest.IntegrationRestApiClient
import javax.ws.rs.core.Response
class TestRunner {
private EmbeddedSpecRunner embeddedSpecRunner = new EmbeddedSpecRunner()
private List listOfClasses = new ArrayList()
TestRunner() {
/**
* list of specs to run the tests. I wish there is a better way to
* add classes to run the tests using class loader but given this
* is running in stand alone jar, its not possible
*/
listOfClasses = [ArtifactTriggerSpec, DeploymentTriggerSpec]
}
boolean runTests() {
listOfClasses.each {
embeddedSpecRunner.runClass(it)
}
}
boolean cleanupModels(String artifactGroup, String artifactName, String artifactVersion) {
IntegrationRestApiClient integrationRestApiClient = new IntegrationRestApiClient()
integrationRestApiClient.host = "http://" + System.getProperty("DeploydbHost")
// admin port in one more than the application by convention
integrationRestApiClient.port = Integer.valueOf(System.getProperty("DeploydbPort")) + 1
String path = "/tasks/modelCleanup?group="+artifactGroup+"&name="+artifactName+"&version="+artifactVersion
Response response = integrationRestApiClient.postJsonToPath(path, "", false)
response.close()
return response.status == 200
}
}

View File

@ -0,0 +1,23 @@
package uat
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
/**
* For UAT tests we need to deserialize the Artifact model.
* We can't use the deploydb models because of gradle sub projects dependencies issue.
* Ideally we should have a top level "api" directory for deploydb that has models in them.
*
* Class to access the Artifact returned by deploydb REST API
*/
@JsonIgnoreProperties(ignoreUnknown = true)
/*
* We only need id property to run UAT tests, so ignore the rest
*/
public class UatArtifact {
@JsonProperty
long id
UatArtifact() { }
}

View File

@ -0,0 +1,23 @@
package uat
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
/**
* For UAT tests we need to deserialize the Deployment model.
* We can't use the deploydb models because of gradle sub projects dependencies issue.
* Ideally we should have a top level "api" directory for deploydb that has models in them.
*
* Class to access the Deployment returned by deploydb REST API
*/
@JsonIgnoreProperties(ignoreUnknown = true)
/*
* We only need id property to run UAT tests, so ignore the rest
*/
public class UatDeployment {
@JsonProperty
long id
UatDeployment() { }
}

View File

@ -0,0 +1,29 @@
package uat
import org.codehaus.groovy.testng.TestNgRunner
class UatMainApp {
static void main(String[] args) {
TestRunner testRunner = new TestRunner()
ConsulClient consulClient = new ConsulClient()
boolean success = true
consulClient.getDeploydbHosts().each { key, value ->
System.setProperty("DeploydbHost", key)
System.setProperty("DeploydbPort", String.valueOf(value))
/*
* Delete the test artifact, deployments and promotion results if they already present.
* This will allow the UAT tests to rerun for long lived environment
*/
if(false == testRunner.cleanupModels("basic.group.1", "bg1","1.2.345")) {
System.exit(1)
}
success &= testRunner.runTests()
}
success ? System.exit(0):System.exit(1)
}
}

View File

@ -0,0 +1,37 @@
package uat.consul
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.fasterxml.jackson.annotation.JsonProperty
/**
* Consul health check model. Used in UAT's service discovery of deploydb
*/
@JsonIgnoreProperties(ignoreUnknown = true)
class HealthCheck {
@JsonProperty("Node")
private String node
@JsonProperty("CheckID")
private String checkId
@JsonProperty("Name")
private String name
@JsonProperty("Status")
private String status
@JsonProperty("Notes")
private String notes
@JsonProperty("Output")
private String output
@JsonProperty("ServiceID")
private String serviceId
@JsonProperty("ServiceName")
private String serviceName
HealthCheck() { }
}

View File

@ -0,0 +1,19 @@
package uat.consul
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.fasterxml.jackson.annotation.JsonProperty
/**
* Consul Node model. Used in UAT's service discovery of deploydb
*/
@JsonIgnoreProperties(ignoreUnknown = true)
class Node {
@JsonProperty("Node")
private String node
@JsonProperty("Address")
private String address
Node() { }
}

View File

@ -0,0 +1,25 @@
package uat.consul
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.fasterxml.jackson.annotation.JsonProperty
/**
* Consul Service check model. Used in UAT's service discovery of deploydb
*/
@JsonIgnoreProperties(ignoreUnknown = true)
class Service {
@JsonProperty("ID")
private String id
@JsonProperty("Service")
private String service
@JsonProperty("Tags")
private String[] tags
@JsonProperty("Port")
private int port
Service() { }
}

View File

@ -0,0 +1,27 @@
package uat.consul
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import java.util.ArrayList
import java.util.List
/**
* Consul ServiceHealth check model. Used in UAT's service discovery of deploydb
*/
@JsonIgnoreProperties(ignoreUnknown = true)
class ServiceHealth {
@JsonProperty("Node")
private Node node
@JsonProperty("Service")
private Service service
@JsonProperty("Checks")
@JsonDeserialize(as = ArrayList.class, contentAs = HealthCheck.class)
private List<HealthCheck> checks
ServiceHealth() { }
}