Skip to content

Commit af62b4e

Browse files
add configurable update interval and back-off
1 parent a695f21 commit af62b4e

File tree

5 files changed

+148
-40
lines changed

5 files changed

+148
-40
lines changed

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,30 @@ The only required input is `project-name`.
4646
the one defined here replaces the one in the CodeBuild project.
4747
For a list of CodeBuild environment variables, see
4848

49+
1. **update-interval** (optional) :
50+
Update interval as seconds for how often the API is called to check on the status.
51+
52+
A higher value mitigates the chance of hitting API rate-limiting especially when
53+
running many instances of this action in parallel, but also introduces a larger
54+
potential time overhead (ranging from 0 to update interval) for the action to
55+
fetch the build result and finish.
56+
57+
Lower value limits the potential time overhead worst case but it may hit the API
58+
rate-limit more often, depending on the use-case.
59+
60+
The default value is 30.
61+
62+
1. **update-back-off** (optional) :
63+
Back-off time in seconds for the update interval.
64+
65+
When API rate-limiting is hit, the back-off time will be added to the next update
66+
interval.
67+
E.g. with update interval of 30 and back-off time of 15, upon hitting the rate-limit
68+
the next interval for the update call will be 45s and if the rate-limit is hit again
69+
the next interval will be 60s and so on.
70+
71+
The default value is 15.
72+
4973
### Outputs
5074

5175
1. **aws-build-id** : The CodeBuild build ID of the build that the action ran.

action.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ inputs:
2222
env-vars-for-codebuild:
2323
description: 'Comma separated list of environment variables to send to CodeBuild'
2424
required: false
25+
update-interval:
26+
description: 'How often the action calls the API for updates'
27+
required: false
28+
update-back-off:
29+
description: 'Back-off time for the update calls for API if rate-limiting is encountered'
30+
required: false
31+
2532
outputs:
2633
aws-build-id:
2734
description: 'The AWS CodeBuild Build ID for this build.'

code-build.js

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,34 +20,37 @@ function runBuild() {
2020
// get a codeBuild instance from the SDK
2121
const sdk = buildSdk();
2222

23+
const inputs = githubInputs();
24+
25+
const config = (({ updateInterval, updateBackOff }) => ({
26+
updateInterval,
27+
updateBackOff,
28+
}))(inputs);
29+
2330
// Get input options for startBuild
24-
const params = inputs2Parameters(githubInputs());
31+
const params = inputs2Parameters(inputs);
2532

26-
return build(sdk, params);
33+
return build(sdk, params, config);
2734
}
2835

29-
async function build(sdk, params) {
36+
async function build(sdk, params, config) {
3037
// Start the build
3138
const start = await sdk.codeBuild.startBuild(params).promise();
3239

3340
// Wait for the build to "complete"
34-
return waitForBuildEndTime(sdk, start.build);
41+
return waitForBuildEndTime(sdk, start.build, config);
3542
}
3643

3744
async function waitForBuildEndTime(
3845
sdk,
3946
{ id, logs },
47+
{ updateInterval, updateBackOff },
4048
seqEmptyLogs,
4149
totalEvents,
4250
throttleCount,
4351
nextToken
4452
) {
45-
const {
46-
codeBuild,
47-
cloudWatchLogs,
48-
wait = 1000 * 30,
49-
backOff = 1000 * 15,
50-
} = sdk;
53+
const { codeBuild, cloudWatchLogs } = sdk;
5154

5255
totalEvents = totalEvents || 0;
5356
seqEmptyLogs = seqEmptyLogs || 0;
@@ -87,16 +90,17 @@ async function waitForBuildEndTime(
8790
//We caught an error in trying to make the AWS api call, and are now checking to see if it was just a rate limiting error
8891
if (errObject.message && errObject.message.search("Rate exceeded") !== -1) {
8992
//We were rate-limited, so add `backOff` seconds to the wait time
90-
let newWait = wait + backOff;
93+
let newWait = updateInterval + updateBackOff;
9194
throttleCount++;
9295

9396
//Sleep before trying again
9497
await new Promise((resolve) => setTimeout(resolve, newWait));
9598

9699
// Try again from the same token position
97100
return waitForBuildEndTime(
98-
{ ...sdk, wait: newWait },
101+
{ ...sdk },
99102
{ id, logs },
103+
{ updateInterval: newWait, updateBackOff },
100104
seqEmptyLogs,
101105
totalEvents,
102106
throttleCount,
@@ -136,13 +140,19 @@ async function waitForBuildEndTime(
136140
// More to do: Sleep for a few seconds to avoid rate limiting
137141
// If never throttled and build is complete, halve CWL polling delay to minimize latency
138142
await new Promise((resolve) =>
139-
setTimeout(resolve, current.endTime && throttleCount == 0 ? wait / 2 : wait)
143+
setTimeout(
144+
resolve,
145+
current.endTime && throttleCount == 0
146+
? updateInterval / 2
147+
: updateInterval
148+
)
140149
);
141150

142151
// Try again
143152
return waitForBuildEndTime(
144153
sdk,
145154
current,
155+
{ updateInterval, updateBackOff },
146156
seqEmptyLogs,
147157
totalEvents,
148158
throttleCount,
@@ -173,8 +183,9 @@ function githubInputs() {
173183
core.getInput("compute-type-override", { required: false }) || undefined;
174184

175185
const environmentTypeOverride =
176-
core.getInput("environment-type-override", { required: false }) || undefined;
177-
const imageOverride =
186+
core.getInput("environment-type-override", { required: false }) ||
187+
undefined;
188+
const imageOverride =
178189
core.getInput("image-override", { required: false }) || undefined;
179190

180191
const envPassthrough = core
@@ -183,6 +194,11 @@ function githubInputs() {
183194
.map((i) => i.trim())
184195
.filter((i) => i !== "");
185196

197+
const updateInterval =
198+
core.getInput("update-interval", { required: false }) || 1000 * 30;
199+
const updateBackOff =
200+
core.getInput("update-back-off", { required: false }) || 1000 * 15;
201+
186202
return {
187203
projectName,
188204
owner,
@@ -193,6 +209,8 @@ function githubInputs() {
193209
environmentTypeOverride,
194210
imageOverride,
195211
envPassthrough,
212+
updateInterval,
213+
updateBackOff,
196214
};
197215
}
198216

local.js

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,15 @@ const cb = require("./code-build");
88
const assert = require("assert");
99
const yargs = require("yargs");
1010

11-
const { projectName, buildspecOverride, computeTypeOverride, environmentTypeOverride, imageOverride, envPassthrough, remote } = yargs
11+
const {
12+
projectName,
13+
buildspecOverride,
14+
computeTypeOverride,
15+
environmentTypeOverride,
16+
imageOverride,
17+
envPassthrough,
18+
remote,
19+
} = yargs
1220
.option("project-name", {
1321
alias: "p",
1422
describe: "AWS CodeBuild Project Name",
@@ -22,17 +30,20 @@ const { projectName, buildspecOverride, computeTypeOverride, environmentTypeOver
2230
})
2331
.option("compute-type-override", {
2432
alias: "c",
25-
describe: "The name of a compute type for this build that overrides the one specified in the build project.",
33+
describe:
34+
"The name of a compute type for this build that overrides the one specified in the build project.",
2635
type: "string",
2736
})
2837
.option("environment-type-override", {
2938
alias: "et",
30-
describe: "A container type for this build that overrides the one specified in the build project.",
39+
describe:
40+
"A container type for this build that overrides the one specified in the build project.",
3141
type: "string",
3242
})
3343
.option("image-override", {
3444
alias: "i",
35-
describe: "The name of an image for this build that overrides the one specified in the build project.",
45+
describe:
46+
"The name of an image for this build that overrides the one specified in the build project.",
3647
type: "string",
3748
})
3849
.option("env-vars-for-codebuild", {

test/code-build-test.js

Lines changed: 69 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ describe("githubInputs", () => {
5454
const sha = "1234abcd-12ab-34cd-56ef-1234567890ab";
5555
const pullRequestSha = "181600acb3cfb803f4570d0018928be5d730c00d";
5656

57+
const updateInterval = "5";
58+
const updateBackOff = "10";
59+
5760
it("build basic parameters for codeBuild.startBuild", () => {
5861
// This is how GITHUB injects its input values.
5962
// It would be nice if there was an easy way to test this...
@@ -71,8 +74,12 @@ describe("githubInputs", () => {
7174
expect(test)
7275
.to.haveOwnProperty("buildspecOverride")
7376
.and.to.equal(undefined);
74-
expect(test).to.haveOwnProperty("computeTypeOverride").and.to.equal(undefined);
75-
expect(test).to.haveOwnProperty("environmentTypeOverride").and.to.equal(undefined);
77+
expect(test)
78+
.to.haveOwnProperty("computeTypeOverride")
79+
.and.to.equal(undefined);
80+
expect(test)
81+
.to.haveOwnProperty("environmentTypeOverride")
82+
.and.to.equal(undefined);
7683
expect(test).to.haveOwnProperty("imageOverride").and.to.equal(undefined);
7784
expect(test).to.haveOwnProperty("envPassthrough").and.to.deep.equal([]);
7885
});
@@ -123,8 +130,12 @@ describe("githubInputs", () => {
123130
expect(test)
124131
.to.haveOwnProperty("buildspecOverride")
125132
.and.to.equal(undefined);
126-
expect(test).to.haveOwnProperty("computeTypeOverride").and.to.equal(undefined);
127-
expect(test).to.haveOwnProperty("environmentTypeOverride").and.to.equal(undefined);
133+
expect(test)
134+
.to.haveOwnProperty("computeTypeOverride")
135+
.and.to.equal(undefined);
136+
expect(test)
137+
.to.haveOwnProperty("environmentTypeOverride")
138+
.and.to.equal(undefined);
128139
expect(test).to.haveOwnProperty("imageOverride").and.to.equal(undefined);
129140
expect(test).to.haveOwnProperty("envPassthrough").and.to.deep.equal([]);
130141
});
@@ -144,6 +155,24 @@ describe("githubInputs", () => {
144155
"No source version could be evaluated."
145156
);
146157
});
158+
it("can handle configuring update call-rate", () => {
159+
process.env[`INPUT_PROJECT-NAME`] = projectName;
160+
process.env[`INPUT_UPDATE-INTERVAL`] = updateInterval;
161+
process.env[`INPUT_UPDATE-BACK-OFF`] = updateBackOff;
162+
process.env[`GITHUB_REPOSITORY`] = repoInfo;
163+
process.env[`GITHUB_SHA`] = sha;
164+
165+
require("@actions/github").context.payload = {};
166+
167+
const test = githubInputs();
168+
169+
expect(test)
170+
.to.haveOwnProperty("updateInterval")
171+
.and.to.equal(updateInterval);
172+
expect(test)
173+
.to.haveOwnProperty("updateBackOff")
174+
.and.to.equal(updateBackOff);
175+
});
147176
});
148177

149178
describe("inputs2Parameters", () => {
@@ -179,8 +208,12 @@ describe("inputs2Parameters", () => {
179208
expect(test)
180209
.to.haveOwnProperty("buildspecOverride")
181210
.and.to.equal(undefined);
182-
expect(test).to.haveOwnProperty("computeTypeOverride").and.to.equal(undefined);
183-
expect(test).to.haveOwnProperty("environmentTypeOverride").and.to.equal(undefined);
211+
expect(test)
212+
.to.haveOwnProperty("computeTypeOverride")
213+
.and.to.equal(undefined);
214+
expect(test)
215+
.to.haveOwnProperty("environmentTypeOverride")
216+
.and.to.equal(undefined);
184217
expect(test).to.haveOwnProperty("imageOverride").and.to.equal(undefined);
185218

186219
// I send everything that starts 'GITHUB_'
@@ -218,7 +251,8 @@ describe("inputs2Parameters", () => {
218251
repo: "repo",
219252
computeTypeOverride: "BUILD_GENERAL1_LARGE",
220253
environmentTypeOverride: "LINUX_CONTAINER",
221-
imageOverride: "111122223333.dkr.ecr.us-west-2.amazonaws.com/codebuild-docker-repo"
254+
imageOverride:
255+
"111122223333.dkr.ecr.us-west-2.amazonaws.com/codebuild-docker-repo",
222256
});
223257
expect(test).to.haveOwnProperty("projectName").and.to.equal(projectName);
224258
expect(test).to.haveOwnProperty("sourceVersion").and.to.equal(sha);
@@ -239,7 +273,9 @@ describe("inputs2Parameters", () => {
239273
.and.to.equal(`LINUX_CONTAINER`);
240274
expect(test)
241275
.to.haveOwnProperty("imageOverride")
242-
.and.to.equal(`111122223333.dkr.ecr.us-west-2.amazonaws.com/codebuild-docker-repo`);
276+
.and.to.equal(
277+
`111122223333.dkr.ecr.us-west-2.amazonaws.com/codebuild-docker-repo`
278+
);
243279

244280
// I send everything that starts 'GITHUB_'
245281
expect(test)
@@ -322,6 +358,7 @@ describe("inputs2Parameters", () => {
322358
});
323359

324360
describe("waitForBuildEndTime", () => {
361+
const defaultConfig = { updateInterval: 30, updateBackOff: 15 };
325362
it("basic usages", async () => {
326363
let count = 0;
327364
const buildID = "buildID";
@@ -341,10 +378,14 @@ describe("waitForBuildEndTime", () => {
341378
() => logReplies[0]
342379
);
343380

344-
const test = await waitForBuildEndTime(sdk, {
345-
id: buildID,
346-
logs: { cloudWatchLogsArn },
347-
});
381+
const test = await waitForBuildEndTime(
382+
sdk,
383+
{
384+
id: buildID,
385+
logs: { cloudWatchLogsArn },
386+
},
387+
defaultConfig
388+
);
348389

349390
expect(test).to.equal(buildReplies.pop().builds[0]);
350391
expect(count).to.equal(2);
@@ -388,10 +429,15 @@ describe("waitForBuildEndTime", () => {
388429
() => logReplies[count - 1]
389430
);
390431

391-
const test = await waitForBuildEndTime(sdk, {
392-
id: buildID,
393-
logs: { cloudWatchLogsArn: nullArn },
394-
});
432+
const test = await waitForBuildEndTime(
433+
sdk,
434+
{
435+
id: buildID,
436+
logs: { cloudWatchLogsArn: nullArn },
437+
},
438+
defaultConfig
439+
);
440+
395441
expect(test).to.equal(buildReplies.pop().builds[0]);
396442
expect(count).to.equal(4);
397443
});
@@ -438,11 +484,12 @@ describe("waitForBuildEndTime", () => {
438484
);
439485

440486
const test = await waitForBuildEndTime(
441-
{ ...sdk, wait: 1, backOff: 1 },
487+
{ ...sdk },
442488
{
443489
id: buildID,
444490
logs: { cloudWatchLogsArn: nullArn },
445-
}
491+
},
492+
{ updateInterval: 1, updateBackOff: 1 }
446493
);
447494

448495
expect(test.id).to.equal(buildID);
@@ -490,11 +537,12 @@ describe("waitForBuildEndTime", () => {
490537

491538
try {
492539
await waitForBuildEndTime(
493-
{ ...sdk, wait: 1, backOff: 1 },
540+
{ ...sdk },
494541
{
495542
id: buildID,
496543
logs: { cloudWatchLogsArn: nullArn },
497-
}
544+
},
545+
defaultConfig
498546
);
499547
} catch (err) {
500548
didFail = true;
@@ -526,7 +574,7 @@ function help(builds, logs) {
526574
},
527575
};
528576

529-
return { codeBuild, cloudWatchLogs, wait: 10 };
577+
return { codeBuild, cloudWatchLogs };
530578

531579
function ret(thing) {
532580
if (typeof thing === "function") return thing();

0 commit comments

Comments
 (0)