Skip to content

Commit

Permalink
Adding a bunch of tests around DeploymentCompleteCheckJob
Browse files Browse the repository at this point in the history
  • Loading branch information
matt-richardson committed Feb 25, 2016
1 parent e33ceee commit 312a6eb
Show file tree
Hide file tree
Showing 16 changed files with 517 additions and 128 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ private boolean ProcessDeployment(HttpContentProvider contentProvider, Deploymen

GetTask(contentProvider, result, deployment, environmentId, createdDate);

if (result.haveAllDeploymentsFinishedSuccessfully()) {
if (result.haveAllEnvironmentsHadAtLeastOneSuccessfulDeployment()) {
LOG.debug("All deployments have finished successfully - no need to keep iterating");
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ private boolean Parse(String progressionResponse) throws java.text.ParseExceptio
return true;
}

if (deployments.haveAllDeploymentsFinishedSuccessfully()) {
if (deployments.haveAllEnvironmentsHadAtLeastOneSuccessfulDeployment()) {
LOG.debug("All deployments have finished successfully - no need to parse deployment response");
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ public boolean isLatestSuccessfulDeploymentOlderThen(OctopusDate compareDate) {
}

public boolean isSuccessful() {
OctopusDate testDate = new OctopusDate(2000, 1, 1);
return this.latestSuccessfulDeployment.compareTo(this.latestDeployment) == 0;
}

return this.latestSuccessfulDeployment.compareTo(testDate) > 0;
public boolean hasHadAtLeastOneSuccessfulDeployment() {
return this.latestSuccessfulDeployment.compareTo(new NullOctopusDate()) > 0;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ public int getPollInterval(@NotNull AsyncTriggerParameters parameters) {

@NotNull
public CheckJob<DeploymentCompleteSpec> createJob(@NotNull final AsyncTriggerParameters asyncTriggerParameters) throws CheckJobCreationException {
return new DeploymentCompleteCheckJob(asyncTriggerParameters, displayName);
return new DeploymentCompleteCheckJob(displayName,
asyncTriggerParameters.getBuildType().toString(),
asyncTriggerParameters.getCustomDataStorage(),
asyncTriggerParameters.getTriggerDescriptor().getProperties());
}

@NotNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import com.intellij.openapi.diagnostic.Logger;
import com.mjrichardson.teamCity.buildTriggers.OctopusBuildTriggerUtil;
import jetbrains.buildServer.buildTriggers.BuildTriggerDescriptor;
import jetbrains.buildServer.buildTriggers.async.AsyncTriggerParameters;
import jetbrains.buildServer.buildTriggers.async.CheckJob;
import jetbrains.buildServer.buildTriggers.async.CheckResult;
import jetbrains.buildServer.serverSide.CustomDataStorage;
Expand All @@ -35,12 +34,22 @@ class DeploymentCompleteCheckJob implements CheckJob<DeploymentCompleteSpec> {
@NotNull
private static final Logger LOG = Logger.getInstance(DeploymentCompleteBuildTrigger.class.getName());

private final AsyncTriggerParameters asyncTriggerParameters;
private final String displayName;
private final String buildType;
private final CustomDataStorage dataStorage;
private final Map<String, String> props;
private final DeploymentsProviderFactory deploymentsProviderFactory;

public DeploymentCompleteCheckJob(AsyncTriggerParameters asyncTriggerParameters, String displayName) {
this.asyncTriggerParameters = asyncTriggerParameters;
public DeploymentCompleteCheckJob(String displayName, String buildType, CustomDataStorage dataStorage, Map<String, String> properties) {
this(new DeploymentsProviderFactory(), displayName, buildType, dataStorage, properties);
}

public DeploymentCompleteCheckJob(DeploymentsProviderFactory deploymentsProviderFactory, String displayName, String buildType, CustomDataStorage dataStorage, Map<String, String> properties) {
this.deploymentsProviderFactory = deploymentsProviderFactory;
this.displayName = displayName;
this.buildType = buildType;
this.dataStorage = dataStorage;
this.props = properties;
}

@NotNull
Expand All @@ -52,9 +61,10 @@ CheckResult<DeploymentCompleteSpec> getCheckResult(String octopusUrl, String oct
try {
final String oldStoredData = dataStorage.getValue(dataStorageKey);
final Deployments oldDeployments = new Deployments(oldStoredData);

final Integer connectionTimeout = OctopusBuildTriggerUtil.DEFAULT_CONNECTION_TIMEOUT;//triggerParameters.getConnectionTimeout(); //todo:fix
DeploymentsProvider provider = deploymentsProviderFactory.getProvider(octopusUrl, octopusApiKey, connectionTimeout);

DeploymentsProvider provider = new DeploymentsProvider(octopusUrl, octopusApiKey, connectionTimeout, LOG);
final Deployments newDeployments = provider.getDeployments(octopusProject, oldDeployments);

//only store that one deployment to one environment has happened here, not multiple environment.
Expand Down Expand Up @@ -88,37 +98,34 @@ CheckResult<DeploymentCompleteSpec> getCheckResult(String octopusUrl, String oct
return DeploymentCompleteSpecCheckResult.createEmptyResult();

} catch (Exception e) {
final DeploymentCompleteSpec deploymentCompleteSpec = new DeploymentCompleteSpec(octopusUrl, octopusProject);
return DeploymentCompleteSpecCheckResult.createThrowableResult(deploymentCompleteSpec, e);
return DeploymentCompleteSpecCheckResult.createThrowableResult(e);
}
}

@NotNull
public CheckResult<DeploymentCompleteSpec> perform() {
final Map<String, String> props = asyncTriggerParameters.getTriggerDescriptor().getProperties();

final String octopusUrl = props.get(OCTOPUS_URL);
if (StringUtil.isEmptyOrSpaces(octopusUrl)) {
return DeploymentCompleteSpecCheckResult.createErrorResult(String.format("%s settings are invalid (empty url) in build configuration %s",
displayName, asyncTriggerParameters.getBuildType()));
displayName, this.buildType));
}

final String octopusApiKey = props.get(OCTOPUS_APIKEY);
if (StringUtil.isEmptyOrSpaces(octopusApiKey)) {
return DeploymentCompleteSpecCheckResult.createErrorResult(String.format("%s settings are invalid (empty api key) in build configuration %s",
displayName, asyncTriggerParameters.getBuildType()));
displayName, this.buildType));
}

final String octopusProject = props.get(OCTOPUS_PROJECT_ID);
if (StringUtil.isEmptyOrSpaces(octopusProject)) {
return DeploymentCompleteSpecCheckResult.createErrorResult(String.format("%s settings are invalid (empty project) in build configuration %s",
displayName, asyncTriggerParameters.getBuildType()));
displayName, this.buildType));
}

final Boolean triggerOnlyOnSuccessfulDeployment = Boolean.parseBoolean(props.get(OCTOPUS_TRIGGER_ONLY_ON_SUCCESSFUL_DEPLOYMENT));

return getCheckResult(octopusUrl, octopusApiKey, octopusProject,
triggerOnlyOnSuccessfulDeployment, asyncTriggerParameters.getCustomDataStorage());
return getCheckResult(octopusUrl, octopusApiKey, octopusProject, triggerOnlyOnSuccessfulDeployment, dataStorage);
}

public boolean allowSchedule(@NotNull BuildTriggerDescriptor buildTriggerDescriptor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ class DeploymentCompleteSpec {
}

public String getRequestorString() {
if (wasSuccessful == null)
if (environmentId == null || environmentId == null)
return String.format("Unsuccessful attempt to get deployments for %s on %s", project, url);
if (wasSuccessful)
return String.format("Successful deployment of %s to %s on %s", project, environmentId, url);
return String.format("Deployment of %s on %s", project, url);
return String.format("Deployment of %s to %s on %s", project, environmentId, url);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ public Deployments() throws ParseException {
this("");
}

public Deployments(Deployment deployment) throws ParseException {
this("");
addOrUpdate(deployment);
}

@Override
public String toString() {
String result = "";
Expand Down Expand Up @@ -166,10 +171,10 @@ private void addOrUpdate(String environmentId, OctopusDate latestDeployment, Oct
}
}

public boolean haveAllDeploymentsFinishedSuccessfully() {
public boolean haveAllEnvironmentsHadAtLeastOneSuccessfulDeployment() {
boolean result = true;
for (Deployment deployment: statusMap) {
result = result & deployment.isSuccessful();
result = result & deployment.hasHadAtLeastOneSuccessfulDeployment();
}
return result;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,83 +1,11 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.mjrichardson.teamCity.buildTriggers.DeploymentComplete;

import com.intellij.openapi.diagnostic.Logger;
import com.mjrichardson.teamCity.buildTriggers.*;

import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;

//todo needs tests
public final class DeploymentsProvider {

private final HttpContentProvider contentProvider;
private final Logger LOG;

public DeploymentsProvider(String octopusUrl, String apiKey, Integer connectionTimeout, Logger log) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
this(new HttpContentProviderImpl(log, octopusUrl, apiKey, connectionTimeout), log);
}

public DeploymentsProvider(HttpContentProvider contentProvider, Logger log)
{
this.contentProvider = contentProvider;
this.LOG = log;
}

public Deployments getDeployments(String projectId, Deployments oldDeployments) throws DeploymentsProviderException, ProjectNotFoundException, InvalidOctopusApiKeyException, InvalidOctopusUrlException {
//get {octopusurl}/api
//parse out project url
//parse out progression url
//call project url
//parse id for project
//call progression url for project id
//return NewDeploymentStatus(responseBody);

try {
LOG.debug("DeploymentCompleteBuildTrigger: Getting deployments from " + contentProvider.getUrl() + " for project id '" + projectId + "'");

final String apiResponse = contentProvider.getContent("/api");
final ApiRootResponse apiRootResponse = new ApiRootResponse(apiResponse);

final String progressionResponse = contentProvider.getContent(apiRootResponse.progressionApiLink + "/" + projectId);
final ApiProgressionResponse apiProgressionResponse = new ApiProgressionResponse(progressionResponse);

if (apiProgressionResponse.haveCompleteInformation)
return apiProgressionResponse.deployments;

final ApiDeploymentsResponse apiDeploymentsResponse = new ApiDeploymentsResponse(
contentProvider, apiRootResponse.deploymentsApiLink, projectId,
oldDeployments, apiProgressionResponse.deployments);

return apiDeploymentsResponse.deployments;
}
catch (InvalidOctopusApiKeyException e) {
throw e;
}
catch (InvalidOctopusUrlException e) {
throw e;
}
catch (ProjectNotFoundException e) {
throw e;
}
catch (Throwable e) {
//todo: improve error message here
throw new DeploymentsProviderException("URL " + contentProvider.getUrl() + ": " + e, e);
}
}
}
package com.mjrichardson.teamCity.buildTriggers.DeploymentComplete;

import com.mjrichardson.teamCity.buildTriggers.InvalidOctopusApiKeyException;
import com.mjrichardson.teamCity.buildTriggers.InvalidOctopusUrlException;
import com.mjrichardson.teamCity.buildTriggers.ProjectNotFoundException;

import java.text.ParseException;

public interface DeploymentsProvider {
Deployments getDeployments(String octopusProject, Deployments oldDeployments) throws DeploymentsProviderException, ProjectNotFoundException, InvalidOctopusApiKeyException, InvalidOctopusUrlException, ParseException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.mjrichardson.teamCity.buildTriggers.DeploymentComplete;

import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;


public class DeploymentsProviderFactory {
public DeploymentsProvider getProvider(String octopusUrl, String octopusApiKey, Integer connectionTimeout) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
return new DeploymentsProviderImpl(octopusUrl, octopusApiKey, connectionTimeout);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.mjrichardson.teamCity.buildTriggers.DeploymentComplete;

import com.intellij.openapi.diagnostic.Logger;
import com.mjrichardson.teamCity.buildTriggers.*;
import org.jetbrains.annotations.NotNull;

import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;

//todo needs tests
public class DeploymentsProviderImpl implements DeploymentsProvider {

private final HttpContentProvider contentProvider;
@NotNull
private static final Logger LOG = Logger.getInstance(DeploymentsProviderImpl.class.getName());

public DeploymentsProviderImpl(String octopusUrl, String apiKey, Integer connectionTimeout) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
this(new HttpContentProviderImpl(octopusUrl, apiKey, connectionTimeout));
}

public DeploymentsProviderImpl(HttpContentProvider contentProvider)
{
this.contentProvider = contentProvider;
}

public Deployments getDeployments(String projectId, Deployments oldDeployments) throws DeploymentsProviderException, ProjectNotFoundException, InvalidOctopusApiKeyException, InvalidOctopusUrlException {
//get {octopusurl}/api
//parse out project url
//parse out progression url
//call project url
//parse id for project
//call progression url for project id
//return NewDeploymentStatus(responseBody);

try {
LOG.debug("DeploymentCompleteBuildTrigger: Getting deployments from " + contentProvider.getUrl() + " for project id '" + projectId + "'");

final String apiResponse = contentProvider.getContent("/api");
final ApiRootResponse apiRootResponse = new ApiRootResponse(apiResponse);

final String progressionResponse = contentProvider.getContent(apiRootResponse.progressionApiLink + "/" + projectId);
final ApiProgressionResponse apiProgressionResponse = new ApiProgressionResponse(progressionResponse);

if (apiProgressionResponse.haveCompleteInformation)
return apiProgressionResponse.deployments;

final ApiDeploymentsResponse apiDeploymentsResponse = new ApiDeploymentsResponse(
contentProvider, apiRootResponse.deploymentsApiLink, projectId,
oldDeployments, apiProgressionResponse.deployments);

return apiDeploymentsResponse.deployments;
}
catch (InvalidOctopusApiKeyException e) {
throw e;
}
catch (InvalidOctopusUrlException e) {
throw e;
}
catch (ProjectNotFoundException e) {
throw e;
}
catch (Throwable e) {
//todo: improve error message here
throw new DeploymentsProviderException("URL " + contentProvider.getUrl() + ": " + e, e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,15 @@

//todo: needs tests
public class HttpContentProviderImpl implements HttpContentProvider {
private final Logger LOG;
@NotNull
private static final Logger LOG = Logger.getInstance(HttpContentProviderImpl.class.getName());

private final String octopusUrl;
CloseableHttpClient httpClient;
@NotNull
private String apiKey;

public HttpContentProviderImpl(@NotNull Logger log, @NotNull String octopusUrl, @NotNull String apiKey, @NotNull Integer connectionTimeout) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
this.LOG = log;
public HttpContentProviderImpl(@NotNull String octopusUrl, @NotNull String apiKey, @NotNull Integer connectionTimeout) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
this.octopusUrl = octopusUrl;
this.apiKey = apiKey;
this.init(connectionTimeout);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class OctopusConnectivityChecker {
private HttpContentProvider contentProvider;

public OctopusConnectivityChecker(String octopusUrl, String apiKey, Integer connectionTimeout, Logger log) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
this(new HttpContentProviderImpl(log, octopusUrl, apiKey, connectionTimeout), log);
this(new HttpContentProviderImpl(octopusUrl, apiKey, connectionTimeout), log);
}

OctopusConnectivityChecker(HttpContentProvider contentProvider, Logger log) {
Expand Down
Loading

0 comments on commit 312a6eb

Please sign in to comment.