From 92916fc1a491dd70c103a6709c46ac360793dec4 Mon Sep 17 00:00:00 2001 From: Stephen Rufle Date: Thu, 14 Jul 2016 08:05:59 -0700 Subject: [PATCH 1/5] changed to unescape carrige returns in error text --- .../com/octopusdeploy/api/ErrorParser.java | 181 +++++++++--------- 1 file changed, 92 insertions(+), 89 deletions(-) diff --git a/src/main/java/com/octopusdeploy/api/ErrorParser.java b/src/main/java/com/octopusdeploy/api/ErrorParser.java index fee7956..970e8b1 100644 --- a/src/main/java/com/octopusdeploy/api/ErrorParser.java +++ b/src/main/java/com/octopusdeploy/api/ErrorParser.java @@ -1,89 +1,92 @@ -package com.octopusdeploy.api; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Parses errors from Octopus html/javascript responses - * @author jlabroad - */ -public class ErrorParser { - - /** Find a group of messages in the format: "Errors":["error message 1", "error message 2", "error message 3"] */ - protected static final String errDetailsOutsideString = "(?:\\\"Errors\\\")(?:[^\\[]\\[)(?[^\\]]+)"; - protected static Pattern errDetailsOutsidePattern = Pattern.compile(errDetailsOutsideString); - - /** Parse each individual message from "error message 1", "error message 2", "error message 3" */ - protected static final String errDetailsInsideString = "(?:\\\")(?[^\\\"]+)*(?:\\\")"; - protected static Pattern errDetailsInsidePattern = Pattern.compile(errDetailsInsideString); - - /** - * Parse any errors from the returned HTML/javascript from Octopus - * @param response The Octopus html response that may include error data - * @return A list of error strings - */ - public static String getErrorsFromResponse(String response) { - List errorStrings = new ArrayList(); - - //Get the error title and main message - String errorTitle = getErrorDataByFieldName("title", response); - if (!errorTitle.isEmpty()) { - errorStrings.add(String.format("%s", errorTitle)); - } - - //Get the error details - String errorDetailMessage = getErrorDataByFieldName("ErrorMessage", response); - if (!errorDetailMessage.isEmpty()) { - errorStrings.add("\t" + errorDetailMessage); - } - errorStrings.addAll(getErrorDetails(response)); - - String errorMsg = ""; - for (String err : errorStrings) { - errorMsg += String.format("%s\n", err); - } - - return errorMsg; - } - - /** - * Grabs a single error data field from an Octopus html response - * @param fieldName The field name of the error string - * @param response The field data - * @return The error data - */ - protected static String getErrorDataByFieldName(String fieldName, String response) { - //Get the next string in script parameter list: "var errorData = {:"Field value", ... - final String patternString = String.format("(?:errorData.+)(?:\"%s\")(?:[:\\[\"]+)(?[^\"]+)", fieldName); - - Pattern pattern = Pattern.compile(patternString); - Matcher matcher = pattern.matcher(response); - String errData = ""; - if (matcher.find() && matcher.groupCount() > 0) { - errData = matcher.group("fieldValue"); - } - return errData; - } - - /** - * Returns a list of "Errors" values from Octopus html response - * @param response The full Octopus html response - * @return - */ - protected static List getErrorDetails(String response) { - List errorList = new ArrayList(); - - Matcher m = errDetailsOutsidePattern.matcher(response); - if (m.find() && m.groupCount() > 0) { - //Split up the list of error messages into individual messages - String errors = m.group("fullDetailString"); - m = errDetailsInsidePattern.matcher(errors); - while (m.find() && m.groupCount() > 0) { - errorList.add("\t" + m.group("singleError")); - } - } - return errorList; - } -} +package com.octopusdeploy.api; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang.StringEscapeUtils; + +/** + * Parses errors from Octopus html/javascript responses + * @author jlabroad + */ +public class ErrorParser { + + /** Find a group of messages in the format: "Errors":["error message 1", "error message 2", "error message 3"] */ + protected static final String errDetailsOutsideString = "(?:\\\"Errors\\\")(?:[^\\[]\\[)(?[^\\]]+)"; + protected static Pattern errDetailsOutsidePattern = Pattern.compile(errDetailsOutsideString); + + /** Parse each individual message from "error message 1", "error message 2", "error message 3" */ + protected static final String errDetailsInsideString = "(?:\\\")(?[^\\\"]+)*(?:\\\")"; + protected static Pattern errDetailsInsidePattern = Pattern.compile(errDetailsInsideString); + + /** + * Parse any errors from the returned HTML/javascript from Octopus + * @param response The Octopus html response that may include error data + * @return A list of error strings + */ + public static String getErrorsFromResponse(String response) { + List errorStrings = new ArrayList(); + + //Get the error title and main message + String errorTitle = getErrorDataByFieldName("title", response); + if (!errorTitle.isEmpty()) { + errorStrings.add(String.format("%s", errorTitle)); + } + + //Get the error details + String errorDetailMessage = getErrorDataByFieldName("ErrorMessage", response); + if (!errorDetailMessage.isEmpty()) { + errorStrings.add("\t" + errorDetailMessage); + } + errorStrings.addAll(getErrorDetails(response)); + + String errorMsg = ""; + for (String err : errorStrings) { + errorMsg += String.format("%s\n", err); + } + + return errorMsg; + } + + /** + * Grabs a single error data field from an Octopus html response + * @param fieldName The field name of the error string + * @param response The field data + * @return The error data + */ + protected static String getErrorDataByFieldName(String fieldName, String response) { + //Get the next string in script parameter list: "var errorData = {:"Field value", ... + final String patternString = String.format("(?:errorData.+)(?:\"%s\")(?:[:\\[\"]+)(?[^\"]+)", fieldName); + + Pattern pattern = Pattern.compile(patternString); + Matcher matcher = pattern.matcher(response); + String errData = ""; + if (matcher.find() && matcher.groupCount() > 0) { + errData = matcher.group("fieldValue"); + } + return errData; + } + + /** + * Returns a list of "Errors" values from Octopus html response + * @param response The full Octopus html response + * @return + */ + protected static List getErrorDetails(String response) { + List errorList = new ArrayList(); + + Matcher m = errDetailsOutsidePattern.matcher(response); + if (m.find() && m.groupCount() > 0) { + //Split up the list of error messages into individual messages + String errors = m.group("fullDetailString"); + m = errDetailsInsidePattern.matcher(errors); + while (m.find() && m.groupCount() > 0) { + String singleError = StringEscapeUtils.unescapeJava(m.group("singleError")); + errorList.add("\t" + singleError); + } + } + return errorList; + } +} From 5b5e33ae1d74d69a8475aa42ee6597ae5be18550 Mon Sep 17 00:00:00 2001 From: Stephen Rufle Date: Thu, 14 Jul 2016 10:11:52 -0700 Subject: [PATCH 2/5] Added getVariablesByReleaseAndEnvironment to OctopusApi --- .../com/octopusdeploy/api/OctopusApi.java | 30 ++++++++++++++++ .../java/com/octopusdeploy/api/Variable.java | 34 +++++++++++++++++++ .../OctopusDeployDeploymentRecorder.java | 12 +++++++ 3 files changed, 76 insertions(+) create mode 100644 src/main/java/com/octopusdeploy/api/Variable.java diff --git a/src/main/java/com/octopusdeploy/api/OctopusApi.java b/src/main/java/com/octopusdeploy/api/OctopusApi.java index 034df47..f7ad25e 100644 --- a/src/main/java/com/octopusdeploy/api/OctopusApi.java +++ b/src/main/java/com/octopusdeploy/api/OctopusApi.java @@ -198,6 +198,36 @@ public Environment getEnvironmentByName(String name, boolean ignoreCase) throws return null; } + /** + * Get the variables for a combination of release and environment, return null otherwise. + * @param releaseId The id of the Release. + * @param environmentId The id of the Environment. + * @return A set of all variables for a given Release and Environment combination. + * @throws IllegalArgumentException + * @throws IOException + */ + public Set getVariablesByReleaseAndEnvironment(String releaseId, String environmentId) throws IllegalArgumentException, IOException { + Set variables = new HashSet(); + + AuthenticatedWebClient.WebResponse response = webClient.get("api/releases/" + releaseId + "/deployments/preview/" + environmentId); + if (response.isErrorCode()) { + throw new IOException(String.format("Code %s - %n%s", response.getCode(), response.getContent())); + } + JSONObject json = (JSONObject)JSONSerializer.toJSON(response.getContent()); + JSONObject form = json.getJSONObject("Form"); + if (form != null){ + for (Object obj : form.getJSONArray("Elements")) { + JSONObject jsonObj = (JSONObject)obj; + String id = jsonObj.getString("Name"); + String name = jsonObj.getJSONObject("Control").getString("Name"); + String description = jsonObj.getJSONObject("Control").getString("Description"); + variables.add(new Variable(id, name, "", description)); + } + } + + return variables; + } + /** * Get all releases for a given project from the Octopus server; * @param projectId diff --git a/src/main/java/com/octopusdeploy/api/Variable.java b/src/main/java/com/octopusdeploy/api/Variable.java new file mode 100644 index 0000000..94fb07a --- /dev/null +++ b/src/main/java/com/octopusdeploy/api/Variable.java @@ -0,0 +1,34 @@ +package com.octopusdeploy.api; + +/** + * Represents a variable that will be passed to a deployment. + */ +public class Variable { + private final String name; + public String getName() { + return name; + } + + private final String value; + public String getValue() { + return value; + } + + private final String id; + public String getId() { + return id; + } + + private final String description; + public String getDescription() { + return description; + } + + public Variable(String id, String name, String value, String description) + { + this.id = id; + this.name = name; + this.value = value; + this.description = description; + } +} diff --git a/src/main/java/hudson/plugins/octopusdeploy/OctopusDeployDeploymentRecorder.java b/src/main/java/hudson/plugins/octopusdeploy/OctopusDeployDeploymentRecorder.java index f60c3ea..8cf46a5 100644 --- a/src/main/java/hudson/plugins/octopusdeploy/OctopusDeployDeploymentRecorder.java +++ b/src/main/java/hudson/plugins/octopusdeploy/OctopusDeployDeploymentRecorder.java @@ -146,6 +146,18 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListener lis log.fatal(String.format("Unable to find release version %s for project %s", releaseVersion, project)); return false; } + + // TODO: Can we tell if we need to call? For now I will always try and get variable and use if I find them + Set variables = null; + try { + String releaseId = releaseToDeploy.getId(); + String environmentId = env.getId(); + variables = api.getVariablesByReleaseAndEnvironment(releaseId, environmentId); + } catch (Exception ex) { + log.fatal(String.format("Retrieving variables for release '%s' to environment '%s' failed with message '%s'", + releaseToDeploy.getId(), env.getName(), ex.getMessage())); + success = false; + } try { String results = api.executeDeployment(releaseToDeploy.getId(), env.getId()); if (isTaskJson(results)) { From 04bb2371066fcb94b743480fb266f50d91940bad Mon Sep 17 00:00:00 2001 From: Stephen Rufle Date: Thu, 14 Jul 2016 13:31:05 -0700 Subject: [PATCH 3/5] Added passing variables through the interface while performing a deployment --- .../com/octopusdeploy/api/OctopusApi.java | 42 +++++++++++++++++-- .../OctopusDeployDeploymentRecorder.java | 31 +++++++++++--- .../OctopusDeployReleaseRecorder.java | 2 +- .../config.jelly | 3 ++ .../help-variables.html | 5 +++ 5 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 src/main/resources/hudson/plugins/octopusdeploy/OctopusDeployDeploymentRecorder/help-variables.html diff --git a/src/main/java/com/octopusdeploy/api/OctopusApi.java b/src/main/java/com/octopusdeploy/api/OctopusApi.java index f7ad25e..bd5009c 100644 --- a/src/main/java/com/octopusdeploy/api/OctopusApi.java +++ b/src/main/java/com/octopusdeploy/api/OctopusApi.java @@ -80,7 +80,34 @@ public String createRelease(String project, String releaseVersion, String releas * @throws IOException */ public String executeDeployment(String releaseId, String environmentId) throws IOException { - String json = String.format("{EnvironmentId:\"%s\",ReleaseId:\"%s\"}", environmentId, releaseId); + return executeDeployment( releaseId, environmentId, null); + } + + /** + * Deploys a given release to provided environment. + * @param releaseId Release Id from Octopus to deploy. + * @param environmentId Environment Id from Octopus to deploy to. + * @param variables Variables used during deployment. + * @return the content of the web response. + * @throws IOException + */ + public String executeDeployment(String releaseId, String environmentId, Set variables) throws IOException { + + StringBuilder jsonBuilder = new StringBuilder(); + jsonBuilder.append(String.format("{EnvironmentId:\"%s\",ReleaseId:\"%s\"", environmentId, releaseId)); + + if (variables != null && !variables.isEmpty()) { + jsonBuilder.append(",FormValues:{"); + Set variablesStrings = new HashSet(); + for (Variable v : variables) { + variablesStrings.add(String.format("\"%s\":\"%s\"", v.getId(), v.getValue())); + } + jsonBuilder.append(StringUtils.join(variablesStrings, ",")); + jsonBuilder.append("}"); + } + jsonBuilder.append("}"); + String json = jsonBuilder.toString(); + byte[] data = json.getBytes(Charset.forName(UTF8)); AuthenticatedWebClient.WebResponse response = webClient.post("api/deployments", data); if (response.isErrorCode()) { @@ -206,7 +233,7 @@ public Environment getEnvironmentByName(String name, boolean ignoreCase) throws * @throws IllegalArgumentException * @throws IOException */ - public Set getVariablesByReleaseAndEnvironment(String releaseId, String environmentId) throws IllegalArgumentException, IOException { + public Set getVariablesByReleaseAndEnvironment(String releaseId, String environmentId, Properties entryProperties) throws IllegalArgumentException, IOException { Set variables = new HashSet(); AuthenticatedWebClient.WebResponse response = webClient.get("api/releases/" + releaseId + "/deployments/preview/" + environmentId); @@ -216,12 +243,19 @@ public Set getVariablesByReleaseAndEnvironment(String releaseId, Strin JSONObject json = (JSONObject)JSONSerializer.toJSON(response.getContent()); JSONObject form = json.getJSONObject("Form"); if (form != null){ + JSONObject formValues = form.getJSONObject("Values"); for (Object obj : form.getJSONArray("Elements")) { - JSONObject jsonObj = (JSONObject)obj; + JSONObject jsonObj = (JSONObject) obj; String id = jsonObj.getString("Name"); String name = jsonObj.getJSONObject("Control").getString("Name"); + String value = formValues.getString(id); + + String entryValue = entryProperties.getProperty(name); + if (StringUtils.isNotEmpty(entryValue)) { + value = entryValue; + } String description = jsonObj.getJSONObject("Control").getString("Description"); - variables.add(new Variable(id, name, "", description)); + variables.add(new Variable(id, name, value, description)); } } diff --git a/src/main/java/hudson/plugins/octopusdeploy/OctopusDeployDeploymentRecorder.java b/src/main/java/hudson/plugins/octopusdeploy/OctopusDeployDeploymentRecorder.java index 8cf46a5..a0e889d 100644 --- a/src/main/java/hudson/plugins/octopusdeploy/OctopusDeployDeploymentRecorder.java +++ b/src/main/java/hudson/plugins/octopusdeploy/OctopusDeployDeploymentRecorder.java @@ -11,6 +11,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import net.sf.json.*; +import org.apache.commons.lang.StringUtils; import org.kohsuke.stapler.*; /** @@ -42,6 +43,15 @@ public String getEnvironment() { return environment; } + /** + * The variables to use for a deploy to in Octopus. + */ + private final String variables; + public String getVariables() { + return variables; + } + + /** * Whether or not perform will return control immediately, or wait until the Deployment * task is completed. @@ -52,10 +62,11 @@ public boolean getWaitForDeployment() { } @DataBoundConstructor - public OctopusDeployDeploymentRecorder(String project, String releaseVersion, String environment, boolean waitForDeployment) { + public OctopusDeployDeploymentRecorder(String project, String releaseVersion, String environment, String variables, boolean waitForDeployment) { this.project = project.trim(); this.releaseVersion = releaseVersion.trim(); this.environment = environment.trim(); + this.variables = variables.trim(); this.waitForDeployment = waitForDeployment; } @@ -92,6 +103,7 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListener lis String project = envInjector.injectEnvironmentVariableValues(this.project); String releaseVersion = envInjector.injectEnvironmentVariableValues(this.releaseVersion); String environment = envInjector.injectEnvironmentVariableValues(this.environment); + String variables = envInjector.injectEnvironmentVariableValues(this.variables); com.octopusdeploy.api.Project p = null; try { @@ -146,20 +158,29 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListener lis log.fatal(String.format("Unable to find release version %s for project %s", releaseVersion, project)); return false; } - + Properties properties = new Properties(); + try { + properties.load(new StringReader(variables)); + } catch (Exception ex) { + log.fatal(String.format("Unable to load entry variables failed with message '%s'", + ex.getMessage())); + success = false; + } + // TODO: Can we tell if we need to call? For now I will always try and get variable and use if I find them - Set variables = null; + Set variablesForDeploy = null; + try { String releaseId = releaseToDeploy.getId(); String environmentId = env.getId(); - variables = api.getVariablesByReleaseAndEnvironment(releaseId, environmentId); + variablesForDeploy = api.getVariablesByReleaseAndEnvironment(releaseId, environmentId, properties); } catch (Exception ex) { log.fatal(String.format("Retrieving variables for release '%s' to environment '%s' failed with message '%s'", releaseToDeploy.getId(), env.getName(), ex.getMessage())); success = false; } try { - String results = api.executeDeployment(releaseToDeploy.getId(), env.getId()); + String results = api.executeDeployment(releaseToDeploy.getId(), env.getId(), variablesForDeploy); if (isTaskJson(results)) { JSON resultJson = JSONSerializer.toJSON(results); String urlSuffix = ((JSONObject)resultJson).getJSONObject("Links").getString("Web"); diff --git a/src/main/java/hudson/plugins/octopusdeploy/OctopusDeployReleaseRecorder.java b/src/main/java/hudson/plugins/octopusdeploy/OctopusDeployReleaseRecorder.java index 92d7d30..a0ec856 100644 --- a/src/main/java/hudson/plugins/octopusdeploy/OctopusDeployReleaseRecorder.java +++ b/src/main/java/hudson/plugins/octopusdeploy/OctopusDeployReleaseRecorder.java @@ -260,7 +260,7 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListener lis } if (success && deployThisRelease) { - OctopusDeployDeploymentRecorder deployment = new OctopusDeployDeploymentRecorder(project, releaseVersion, environment, waitForDeployment); + OctopusDeployDeploymentRecorder deployment = new OctopusDeployDeploymentRecorder(project, releaseVersion, environment, "", waitForDeployment); success = deployment.perform(build, launcher, listener); } diff --git a/src/main/resources/hudson/plugins/octopusdeploy/OctopusDeployDeploymentRecorder/config.jelly b/src/main/resources/hudson/plugins/octopusdeploy/OctopusDeployDeploymentRecorder/config.jelly index 3d9560d..fbf5d5b 100644 --- a/src/main/resources/hudson/plugins/octopusdeploy/OctopusDeployDeploymentRecorder/config.jelly +++ b/src/main/resources/hudson/plugins/octopusdeploy/OctopusDeployDeploymentRecorder/config.jelly @@ -8,6 +8,9 @@ + + + diff --git a/src/main/resources/hudson/plugins/octopusdeploy/OctopusDeployDeploymentRecorder/help-variables.html b/src/main/resources/hudson/plugins/octopusdeploy/OctopusDeployDeploymentRecorder/help-variables.html new file mode 100644 index 0000000..e54c9fe --- /dev/null +++ b/src/main/resources/hudson/plugins/octopusdeploy/OctopusDeployDeploymentRecorder/help-variables.html @@ -0,0 +1,5 @@ +
+ List of variable to pass to the Depoyment process +
+ Use Java properties notation +
From 2a03887559839d095e2dd8f7fdcd8230687b6a0b Mon Sep 17 00:00:00 2001 From: Stephen Rufle Date: Thu, 14 Jul 2016 13:32:19 -0700 Subject: [PATCH 4/5] Added toStrings to API objects --- src/main/java/com/octopusdeploy/api/DeploymentProcess.java | 5 +++++ .../java/com/octopusdeploy/api/DeploymentProcessStep.java | 5 +++++ .../com/octopusdeploy/api/DeploymentProcessStepAction.java | 5 +++++ .../com/octopusdeploy/api/DeploymentProcessTemplate.java | 5 +++++ src/main/java/com/octopusdeploy/api/Environment.java | 5 +++++ src/main/java/com/octopusdeploy/api/Project.java | 6 +++++- src/main/java/com/octopusdeploy/api/Release.java | 6 ++++++ src/main/java/com/octopusdeploy/api/SelectedPackage.java | 6 ++++++ src/main/java/com/octopusdeploy/api/Task.java | 6 ++++++ src/main/java/com/octopusdeploy/api/Variable.java | 6 ++++++ 10 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/octopusdeploy/api/DeploymentProcess.java b/src/main/java/com/octopusdeploy/api/DeploymentProcess.java index a6f8a4e..58f2124 100644 --- a/src/main/java/com/octopusdeploy/api/DeploymentProcess.java +++ b/src/main/java/com/octopusdeploy/api/DeploymentProcess.java @@ -26,4 +26,9 @@ public DeploymentProcess(String id, String projectId, Set this.projectId = projectId; this.steps = steps; } + + @Override + public String toString() { + return "DeploymentProcess [id=" + id + ", projectId=" + projectId + ", steps=" + steps + "]"; + } } diff --git a/src/main/java/com/octopusdeploy/api/DeploymentProcessStep.java b/src/main/java/com/octopusdeploy/api/DeploymentProcessStep.java index 4cb0e10..2eb0a2b 100644 --- a/src/main/java/com/octopusdeploy/api/DeploymentProcessStep.java +++ b/src/main/java/com/octopusdeploy/api/DeploymentProcessStep.java @@ -26,4 +26,9 @@ public DeploymentProcessStep(String id, String name, Set Date: Thu, 14 Jul 2016 13:34:04 -0700 Subject: [PATCH 5/5] Removed unneeded import --- .../plugins/octopusdeploy/OctopusDeployDeploymentRecorder.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/hudson/plugins/octopusdeploy/OctopusDeployDeploymentRecorder.java b/src/main/java/hudson/plugins/octopusdeploy/OctopusDeployDeploymentRecorder.java index a0e889d..cc541c1 100644 --- a/src/main/java/hudson/plugins/octopusdeploy/OctopusDeployDeploymentRecorder.java +++ b/src/main/java/hudson/plugins/octopusdeploy/OctopusDeployDeploymentRecorder.java @@ -11,7 +11,6 @@ import java.util.logging.Level; import java.util.logging.Logger; import net.sf.json.*; -import org.apache.commons.lang.StringUtils; import org.kohsuke.stapler.*; /**