Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added debug to connection errors, added console output #9

Merged
merged 2 commits into from
May 12, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public class RemoteBuildConfiguration extends Builder {
private final int connectionRetryLimit = 5;
private final boolean preventRemoteBuildQueue;
private final boolean blockBuildUntilComplete;
private final boolean enhancedLogging;

// "parameters" is the raw string entered by the user
private final String parameters;
Expand All @@ -83,7 +84,7 @@ public class RemoteBuildConfiguration extends Builder {

@DataBoundConstructor
public RemoteBuildConfiguration(String remoteJenkinsName, boolean shouldNotFailBuild, String job, String token,
String parameters, JSONObject overrideAuth, JSONObject loadParamsFromFile, boolean preventRemoteBuildQueue,
String parameters, boolean enhancedLogging, JSONObject overrideAuth, JSONObject loadParamsFromFile, boolean preventRemoteBuildQueue,
boolean blockBuildUntilComplete, int pollInterval) throws MalformedURLException {

this.token = token.trim();
Expand All @@ -94,6 +95,7 @@ public RemoteBuildConfiguration(String remoteJenkinsName, boolean shouldNotFailB
this.preventRemoteBuildQueue = preventRemoteBuildQueue;
this.blockBuildUntilComplete = blockBuildUntilComplete;
this.pollInterval = pollInterval;
this.enhancedLogging = enhancedLogging;

if (overrideAuth != null && overrideAuth.has("auth")) {
this.overrideAuth = true;
Expand Down Expand Up @@ -125,11 +127,12 @@ public RemoteBuildConfiguration(String remoteJenkinsName, boolean shouldNotFailB

public RemoteBuildConfiguration(String remoteJenkinsName, boolean shouldNotFailBuild,
boolean preventRemoteBuildQueue, boolean blockBuildUntilComplete, int pollInterval, String job,
String token, String parameters) throws MalformedURLException {
String token, String parameters, boolean enhancedLogging) throws MalformedURLException {

this.token = token.trim();
this.remoteJenkinsName = remoteJenkinsName;
this.parameters = parameters;
this.enhancedLogging = enhancedLogging;
this.job = job.trim();
this.shouldNotFailBuild = shouldNotFailBuild;
this.preventRemoteBuildQueue = preventRemoteBuildQueue;
Expand Down Expand Up @@ -643,6 +646,17 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListener lis
listener.getLogger().println("Remote build finished with status " + buildStatusStr + ".");
BuildInfoExporterAction.addBuildInfoExporterAction(build, jobName, nextBuildNumber, Result.fromString(buildStatusStr));

if (this.getEnhancedLogging()) {
String buildUrl = getBuildUrl(jobLocation, build, listener);
String consoleOutput = getConsoleOutput(buildUrl, "GET", build, listener);

listener.getLogger().println();
listener.getLogger().println("Console output of remote job:");
listener.getLogger().println("--------------------------------------------------------------------------------");
listener.getLogger().println(consoleOutput);
listener.getLogger().println("--------------------------------------------------------------------------------");
}

// If build did not finish with 'success' then fail build step.
if (!buildStatusStr.equals("SUCCESS")) {
// failBuild will check if the 'shouldNotFailBuild' parameter is set or not, so will decide how to
Expand Down Expand Up @@ -728,6 +742,45 @@ public String getBuildStatus(String buildUrlString, AbstractBuild build, BuildLi
return buildStatus;
}

public String getBuildUrl(String buildUrlString, AbstractBuild build, BuildListener listener) throws IOException {
String buildUrl = "";

RemoteJenkinsServer remoteServer = this.findRemoteHost(this.getRemoteJenkinsName());

if (remoteServer == null) {
this.failBuild(new Exception("No remote host is defined for this job."), listener);
return null;
}

// print out some debugging information to the console
//listener.getLogger().println("Checking Status of this job: " + buildUrlString);
if (this.getOverrideAuth()) {
listener.getLogger().println(
"Using job-level defined credentails in place of those from remote Jenkins config ["
+ this.getRemoteJenkinsName() + "]");
}

JSONObject responseObject = sendHTTPCall(buildUrlString, "GET", build, listener);

// get the next build from the location

if (responseObject != null && responseObject.getString("url") != null) {
buildUrl = responseObject.getString("url");
} else {
// Add additional else to check for unhandled conditions
listener.getLogger().println("WARNING: URL not found in JSON Response!");
return null;
}

return buildUrl;
}

public String getConsoleOutput(String urlString, String requestType, AbstractBuild build, BuildListener listener)
throws IOException {

return getConsoleOutput( urlString, requestType, build, listener, 1 );
}

/**
* Orchestrates all calls to the remote server.
* Also takes care of any credentials or failed-connection retries.
Expand All @@ -745,6 +798,117 @@ public JSONObject sendHTTPCall(String urlString, String requestType, AbstractBui
return sendHTTPCall( urlString, requestType, build, listener, 1 );
}

public String getConsoleOutput(String urlString, String requestType, AbstractBuild build, BuildListener listener, int numberOfAttempts)
throws IOException {
RemoteJenkinsServer remoteServer = this.findRemoteHost(this.getRemoteJenkinsName());
int retryLimit = this.getConnectionRetryLimit();

if (remoteServer == null) {
this.failBuild(new Exception("No remote host is defined for this job."), listener);
return null;
}

HttpURLConnection connection = null;

String consoleOutput = null;

URL buildUrl = new URL(urlString+"consoleText");
connection = (HttpURLConnection) buildUrl.openConnection();

// if there is a username + apiToken defined for this remote host, then use it
String usernameTokenConcat;

if (this.getOverrideAuth()) {
usernameTokenConcat = this.getAuth()[0].getUsername() + ":" + this.getAuth()[0].getPassword();
} else {
usernameTokenConcat = remoteServer.getAuth()[0].getUsername() + ":"
+ remoteServer.getAuth()[0].getPassword();
}

if (!usernameTokenConcat.equals(":")) {
// token-macro replacment
try {
usernameTokenConcat = TokenMacro.expandAll(build, listener, usernameTokenConcat);
} catch (MacroEvaluationException e) {
this.failBuild(e, listener);
} catch (InterruptedException e) {
this.failBuild(e, listener);
}

byte[] encodedAuthKey = Base64.encodeBase64(usernameTokenConcat.getBytes());
connection.setRequestProperty("Authorization", "Basic " + new String(encodedAuthKey));
}

try {
connection.setDoInput(true);
connection.setRequestProperty("Accept", "application/json");
connection.setRequestMethod(requestType);
// wait up to 5 seconds for the connection to be open
connection.setConnectTimeout(5000);
connection.connect();

InputStream is;
try {
is = connection.getInputStream();
} catch (FileNotFoundException e) {
// In case of a e.g. 404 status
is = connection.getErrorStream();
}

BufferedReader rd = new BufferedReader(new InputStreamReader(is));
String line;
// String response = "";
StringBuilder response = new StringBuilder();

while ((line = rd.readLine()) != null) {
response.append(line+"\n");
}
rd.close();


consoleOutput = response.toString();
} catch (IOException e) {

//If we have connectionRetryLimit set to > 0 then retry that many times.
if( numberOfAttempts <= retryLimit) {
listener.getLogger().println("Connection to remote server failed, waiting for to retry - " + this.pollInterval + " seconds until next attempt.");
e.printStackTrace();

// Sleep for 'pollInterval' seconds.
// Sleep takes miliseconds so need to convert this.pollInterval to milisecopnds (x 1000)
try {
// Could do with a better way of sleeping...
Thread.sleep(this.pollInterval * 1000);
} catch (InterruptedException ex) {
this.failBuild(ex, listener);
}


listener.getLogger().println("Retry attempt #" + numberOfAttempts + " out of " + retryLimit );
numberOfAttempts++;
consoleOutput = getConsoleOutput(urlString, requestType, build, listener, numberOfAttempts);
} else if(numberOfAttempts > retryLimit){
//reached the maximum number of retries, time to fail
this.failBuild(new Exception("Max number of connection retries have been exeeded."), listener);
} else{
//something failed with the connection and we retried the max amount of times... so throw an exception to mark the build as failed.
this.failBuild(e, listener);
}

} finally {
// always make sure we close the connection
if (connection != null) {
connection.disconnect();
}
// and always clear the query string and remove some "global" values
this.clearQueryString();
// this.build = null;
// this.listener = null;

}
return consoleOutput;
}

/**
* Same as sendHTTPCall, but keeps track of the number of failed connection attempts (aka: the number of times this
* method has been called).
Expand Down Expand Up @@ -842,6 +1006,7 @@ public JSONObject sendHTTPCall(String urlString, String requestType, AbstractBui
//If we have connectionRetryLimit set to > 0 then retry that many times.
if( numberOfAttempts <= retryLimit) {
listener.getLogger().println("Connection to remote server failed, waiting for to retry - " + this.pollInterval + " seconds until next attempt.");
e.printStackTrace();

// Sleep for 'pollInterval' seconds.
// Sleep takes miliseconds so need to convert this.pollInterval to milisecopnds (x 1000)
Expand Down Expand Up @@ -910,6 +1075,10 @@ public boolean getShouldNotFailBuild() {
return this.shouldNotFailBuild;
}

public boolean getEnhancedLogging() {
return this.enhancedLogging;
}

public boolean getPreventRemoteBuildQueue() {
return this.preventRemoteBuildQueue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@
<f:textarea />
</f:entry>

<f:entry title="Enable enhanced logging" field="enhancedLogging">
<f:checkbox />
</f:entry>

<f:optionalBlock title="Load parameters from external file (this will cause the job to ignore the text field above)" field="loadParamsFromFile">
<f:entry title="Parameter file path + name (all paths are relative to the current workspace)" field="parameterFile">
<f:textbox />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<div>
<div style="font-weight: bolder; text-decoration: underline">
Enable Enhanced Logging
</div>
If this option is enabled, the console output of the remote job is also logged.
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public void testRemoteBuild() throws Exception {
FreeStyleProject project = jenkinsRule.createFreeStyleProject();
RemoteBuildConfiguration remoteBuildConfiguration = new RemoteBuildConfiguration(
remoteJenkinsServer.getDisplayName(), false, remoteProject.getFullName(), "",
"", null, null, false, true, 1);
"", true, null, null, false, true, 1);
project.getBuildersList().add(remoteBuildConfiguration);

jenkinsRule.buildAndAssertSuccess(project);
Expand Down