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

Add retry logic to JenkinsQueueJobV2 #15211

Merged
merged 15 commits into from
Aug 26, 2021
Merged
Show file tree
Hide file tree
Changes from 13 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 @@ -22,6 +22,10 @@
"loc.input.help.jobParameters": "Specify job parameters, one per line, in the form <b>`<parameterName>=<parameterValue>`</b><p>To set a parameter to an empty value (useful for overriding a default value), leave off the parameter value. For example, specify <b>`<parameterName>=`</b><p>Variables are supported. For example, to set a <b>`commitId`</b> parameter value to the Git commit ID of the build, use: <b>`commitId=$(Build.SourceVersion)`</b>. See the [documentation on variables](https://go.microsoft.com/fwlink/?linkid=875288) for more details.<p>Supported Jenkins parameter types are: <ul><li>`Boolean`</li><li>`Choice`</li><li>`Password`</li><li>`String`</li></ul>",
"loc.input.label.failOnUnstableResult": "Fail on unstable result",
"loc.input.help.failOnUnstableResult": "Specifies strictness of a success definition: whether to consider unstable as a failure or not. False for non-strict, and true for strict version.",
"loc.input.label.retryCount": "Number of retries for failed connection",
"loc.input.help.retryCount": "Specify number of retries on errors or failures",
"loc.input.label.delayBetweenRetries": "Time between retries",
"loc.input.help.delayBetweenRetries": "Specify time between retries. This is specified in seconds",
"loc.messages.FailedToGenerateSummary": "Failed to generate build summary.",
"loc.messages.succeeded": "succeeded",
"loc.messages.unstable": "unstable",
Expand Down
7 changes: 7 additions & 0 deletions Tasks/JenkinsQueueJobV2/jenkinsqueuejobtask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ export class TaskOptions {

pollIntervalMillis: number;

//initialize retry count and timer
retryCount: number;
delayBetweenRetries: number;

parameterizedJob: boolean;
// jobParameters are only possible if parameterizedJob is enabled
jobParameters: string[];
Expand Down Expand Up @@ -71,6 +75,9 @@ export class TaskOptions {

this.pollIntervalMillis = 5000; // five seconds is what the Jenkins Web UI uses

this.retryCount = parseInt(tl.getInput('retryCount', false));
this.delayBetweenRetries = parseInt(tl.getInput('delayBetweenRetries', false));

this.parameterizedJob = tl.getBoolInput('parameterizedJob', true);
// jobParameters are only possible if parameterizedJob is enabled
this.jobParameters = this.parameterizedJob ? tl.getDelimitedInput('jobParameters', '\n', false) : [];
Expand Down
38 changes: 31 additions & 7 deletions Tasks/JenkinsQueueJobV2/job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { unzip } from './unzip';
import {JobState, checkStateTransitions} from './states';

import * as Util from './util';
import { rawListeners } from 'process';
kuleshovilya marked this conversation as resolved.
Show resolved Hide resolved

export class Job {
public Parent: Job; // if this job is a pipelined job, its parent that started it.
Expand All @@ -35,6 +36,7 @@ export class Job {

private working: boolean = true; // initially mark it as working
private workDelay: number = 0;
private retryNumber: number;

public ParsedExecutionResult: {result: string, timestamp: number}; // set during state Finishing

Expand All @@ -48,7 +50,7 @@ export class Job {
this.Parent.Children.push(this);
}
this.queue = jobQueue;

this.retryNumber = 0;
if (this.TaskUrl.startsWith(this.queue.TaskOptions.serverEndpointUrl)) {
// simplest case (jobs run on the same server name as the endpoint)
this.Identifier = this.TaskUrl.substr(this.queue.TaskOptions.serverEndpointUrl.length);
Expand Down Expand Up @@ -131,6 +133,13 @@ export class Job {
this.working = false;
}


private RetryConnection(): void {
this.retryNumber++;
this.consoleLog(`Connection error. Retrying again in ${this.queue.TaskOptions.delayBetweenRetries} seconds. Retry ${this.retryNumber} out of ${this.queue.TaskOptions.retryCount}`);
this.stopWork(this.queue.TaskOptions.delayBetweenRetries*1000, this.State);
}

public IsActive(): boolean {
return this.State === JobState.New ||
this.State === JobState.Locating ||
Expand Down Expand Up @@ -404,9 +413,14 @@ export class Job {
request.get({ url: fullUrl, strictSSL: thisJob.queue.TaskOptions.strictSSL }, function requestCallback(err, httpResponse, body) {
tl.debug('streamConsole().requestCallback()');
if (err) {
Util.handleConnectionResetError(err); // something went bad
thisJob.stopWork(thisJob.queue.TaskOptions.pollIntervalMillis, thisJob.State);
return;
if (thisJob.retryNumber >= thisJob.queue.TaskOptions.retryCount) {
Util.handleConnectionResetError(err); // something went bad
thisJob.stopWork(thisJob.queue.TaskOptions.pollIntervalMillis, thisJob.State);
return;
}
else {
thisJob.RetryConnection();
}
} else if (httpResponse.statusCode === 404) {
// got here too fast, stream not yet available, try again in the future
thisJob.stopWork(thisJob.queue.TaskOptions.pollIntervalMillis, thisJob.State);
Expand All @@ -418,8 +432,13 @@ export class Job {
thisJob.queue.TaskOptions.failureMsg = 'Job progress tracking failed to read job progress';
thisJob.stopWork(0, JobState.Finishing);
} else if (httpResponse.statusCode !== 200) {
Util.failReturnCode(httpResponse, 'Job progress tracking failed to read job progress');
thisJob.stopWork(thisJob.queue.TaskOptions.pollIntervalMillis, thisJob.State);
if (thisJob.retryNumber >= thisJob.queue.TaskOptions.retryCount) {
Util.failReturnCode(httpResponse, 'Job progress tracking failed to read job progress');
thisJob.stopWork(thisJob.queue.TaskOptions.pollIntervalMillis, thisJob.State);
}
else {
thisJob.RetryConnection();
kuleshovilya marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
thisJob.consoleLog(body); // redirect Jenkins console to task console
const xMoreData: string = httpResponse.headers['x-more-data'];
Expand All @@ -433,7 +452,12 @@ export class Job {
}
}).auth(thisJob.queue.TaskOptions.username, thisJob.queue.TaskOptions.password, true)
.on('error', (err) => {
throw err;
if (thisJob.retryNumber >= thisJob.queue.TaskOptions.retryCount) {
throw err;
}
else{
kuleshovilya marked this conversation as resolved.
Show resolved Hide resolved
thisJob.consoleLog(err);
}
});
}

Expand Down
20 changes: 19 additions & 1 deletion Tasks/JenkinsQueueJobV2/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"version": {
"Major": 2,
"Minor": 192,
"Patch": 0
"Patch": 1
},
"groups": [
{
Expand Down Expand Up @@ -110,6 +110,24 @@
"required": false,
"helpMarkDown": "Specifies strictness of a success definition: whether to consider unstable as a failure or not. False for non-strict, and true for strict version.",
"groupName": "advanced"
},
{
"name": "retryCount",
"type": "string",
"label": "Number of retries for failed connection",
"defaultValue": "3",
"required": false,
"helpMarkDown": "Specify number of retries on errors or failures",
"groupName": "advanced"
},
{
"name": "delayBetweenRetries",
"type": "string",
"label": "Time between retries",
"defaultValue": "60",
kuleshovilya marked this conversation as resolved.
Show resolved Hide resolved
"required": false,
"helpMarkDown": "Specify time between retries. This is specified in seconds",
"groupName": "advanced"
}
],
"outputVariables": [
Expand Down
20 changes: 19 additions & 1 deletion Tasks/JenkinsQueueJobV2/task.loc.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"version": {
"Major": 2,
"Minor": 192,
"Patch": 0
"Patch": 1
},
"groups": [
{
Expand Down Expand Up @@ -110,6 +110,24 @@
"required": false,
"helpMarkDown": "ms-resource:loc.input.help.failOnUnstableResult",
"groupName": "advanced"
},
{
"name": "retryCount",
"type": "string",
"label": "ms-resource:loc.input.label.retryCount",
"defaultValue": "3",
"required": false,
"helpMarkDown": "ms-resource:loc.input.help.retryCount",
"groupName": "advanced"
},
{
"name": "delayBetweenRetries",
"type": "string",
"label": "ms-resource:loc.input.label.delayBetweenRetries",
"defaultValue": "60",
"required": false,
"helpMarkDown": "ms-resource:loc.input.help.delayBetweenRetries",
"groupName": "advanced"
}
],
"outputVariables": [
Expand Down