Skip to content

Commit

Permalink
Parse timeouts (#215)
Browse files Browse the repository at this point in the history
  • Loading branch information
sethvargo authored Dec 8, 2021
1 parent b863bde commit 726b4f7
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 4 deletions.
1 change: 1 addition & 0 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ jobs:
build_environment_variables_file: './tests/env-var-files/test.good.yaml'
min_instances: 2
max_instances: 5
timeout: 300

# Auth as the main account for integration and cleanup
- uses: 'google-github-actions/auth@main'
Expand Down
1 change: 1 addition & 0 deletions action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ inputs:
timeout:
description: |-
The function execution timeout.
default: '60s'
required: false

min_instances:
Expand Down
2 changes: 1 addition & 1 deletion dist/index.js

Large diffs are not rendered by default.

10 changes: 8 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { CloudFunctionsClient, CloudFunction } from './client';
import {
errorMessage,
isServiceAccountKey,
parseDuration,
parseKVString,
parseKVStringAndFile,
parseServiceAccountKeyJSON,
Expand Down Expand Up @@ -54,7 +55,7 @@ async function run(): Promise<void> {
);
const ingressSettings = presence(getInput('ingress_settings'));
const serviceAccountEmail = presence(getInput('service_account_email'));
const timeout = presence(getInput('timeout'));
const timeout = parseDuration(getInput('timeout'));
const maxInstances = presence(getInput('max_instances'));
const minInstances = presence(getInput('min_instances'));
const eventTriggerType = presence(getInput('event_trigger_type'));
Expand Down Expand Up @@ -118,6 +119,11 @@ async function run(): Promise<void> {
);
}
}
if (timeout <= 0) {
throw new Error(
`The 'timeout' parameter must be > 0 seconds (got ${timeout})`,
);
}

// Create Cloud Functions client
const client = new CloudFunctionsClient({
Expand Down Expand Up @@ -151,7 +157,7 @@ async function run(): Promise<void> {
// network: network, // TODO: add support
serviceAccountEmail: serviceAccountEmail,
// sourceToken: sourceToken, // TODO: add support
timeout: timeout,
timeout: `${timeout}s`,
vpcConnector: vpcConnector,
vpcConnectorEgressSettings: vpcConnectorEgressSettings,
};
Expand Down
79 changes: 79 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ export function getGcloudIgnores(dir: string): string[] {
* removeFile removes the file at the given path. If the file does not exist, it
* does nothing.
*
* TODO(sethvargo): Candidate for centralization.
*
* @param filePath Path of the file on disk to delete.
* @returns Path of the file that was removed.
*/
Expand All @@ -114,6 +116,9 @@ export function removeFile(filePath: string): string {
/**
* fromBase64 base64 decodes the result, taking into account URL and standard
* encoding with and without padding.
*
* TODO(sethvargo): Candidate for centralization.
*
*/
export function fromBase64(s: string): string {
let str = s.replace(/-/g, '+').replace(/_/g, '/');
Expand All @@ -137,6 +142,8 @@ export type ServiceAccountKey = {
/**
* parseServiceAccountKeyJSON attempts to parse the given string as a service
* account key JSON. It handles if the string is base64-encoded.
*
* TODO(sethvargo): Candidate for centralization.
*/
export function parseServiceAccountKeyJSON(
str: string,
Expand Down Expand Up @@ -185,6 +192,8 @@ type KVPair = Record<string, string>;
/**
* Parses a string of the format `KEY1=VALUE1,KEY2=VALUE2`.
*
* TODO(sethvargo): Candidate for centralization.
*
* @param str String with key/value pairs to parse.
*/
export function parseKVString(str: string): KVPair {
Expand Down Expand Up @@ -294,6 +303,8 @@ export function parseKVStringAndFile(
* presence takes the given string and converts it to undefined iff it's null,
* undefined, or the empty string. Otherwise, it returns the trimmed string.
*
* TODO(sethvargo): Candidate for centralization.
*
* @param str The string to check
*/
export function presence(str: string | null | undefined): string | undefined {
Expand All @@ -307,6 +318,9 @@ export function presence(str: string | null | undefined): string | undefined {

/**
* errorMessage extracts the error message from the given error.
*
* TODO(sethvargo): Candidate for centralization.
*
*/
export function errorMessage(err: unknown): string {
if (!err) {
Expand All @@ -325,3 +339,68 @@ export function errorMessage(err: unknown): string {
msg = msg[0].toLowerCase() + msg.slice(1);
return msg;
}

/**
* parseDuration parses a user-supplied string duration with optional suffix and
* returns a number representing the number of seconds. It returns 0 when given
* the empty string.
*
* TODO(sethvargo): Candidate for centralization.
*
* @param str Duration string
*/
export function parseDuration(str: string): number {
const given = (str || '').trim();
if (!given) {
return 0;
}

let total = 0;
let curr = '';
for (let i = 0; i < str.length; i++) {
const ch = str[i];
switch (ch) {
case ' ':
continue;
case ',':
continue;
case 's': {
total += +curr;
curr = '';
break;
}
case 'm': {
total += +curr * 60;
curr = '';
break;
}
case 'h': {
total += +curr * 60 * 60;
curr = '';
break;
}

case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
curr += ch;
break;
default:
throw new SyntaxError(`Unsupported character "${ch}" at position ${i}`);
}
}

// Anything left over is seconds
if (curr) {
total += +curr;
}

return total;
}
65 changes: 64 additions & 1 deletion tests/util.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import 'mocha';
import * as path from 'path';
import {
errorMessage,
parseServiceAccountKeyJSON,
parseDuration,
parseKVFile,
parseKVString,
parseKVStringAndFile,
parseKVYAML,
parseServiceAccountKeyJSON,
presence,
zipDir,
} from '../src/util';
Expand Down Expand Up @@ -338,6 +339,68 @@ describe('Util', () => {
});
});
});

describe('#parseDuration', () => {
const cases = [
{
name: 'empty string',
input: '',
expected: 0,
},
{
name: 'unitless',
input: '149585',
expected: 149585,
},
{
name: 'with commas',
input: '149,585',
expected: 149585,
},
{
name: 'suffix seconds',
input: '149585s',
expected: 149585,
},
{
name: 'suffix minutes',
input: '25m',
expected: 1500,
},
{
name: 'suffix hours',
input: '12h',
expected: 43200,
},
{
name: 'suffix hours minutes seconds',
input: '12h10m55s',
expected: 43855,
},
{
name: 'commas and spaces',
input: '12h, 10m 55s',
expected: 43855,
},
{
name: 'invalid',
input: '12h blueberries',
error: 'Unsupported character "b" at position 4',
},
];

cases.forEach((tc) => {
it(tc.name, async () => {
if (tc.expected) {
expect(parseDuration(tc.input)).to.eq(tc.expected);
} else if (tc.error) {
expect(() => {
parseDuration(tc.input);
}).to.throw(tc.error);
}
});
});
});
});

describe('Zip', function () {
Expand Down

0 comments on commit 726b4f7

Please sign in to comment.