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

fix: retry connections to gce metadata server #284

Merged
merged 4 commits into from
Feb 16, 2018
Merged
Show file tree
Hide file tree
Changes from 3 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
3 changes: 1 addition & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"gtoken": "^2.1.0",
"jws": "^3.1.4",
"lodash.isstring": "^4.0.1",
"lru-cache": "^4.1.1"
"lru-cache": "^4.1.1",
"retry-axios": "^0.3.0"

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

},
"devDependencies": {
"@justinbeckwith/typedoc": "^0.10.1",
Expand Down
18 changes: 12 additions & 6 deletions src/auth/computeclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,18 @@
* limitations under the License.
*/

import {AxiosError, AxiosPromise, AxiosRequestConfig, AxiosResponse} from 'axios';
import axios, {AxiosError, AxiosPromise, AxiosRequestConfig, AxiosResponse} from 'axios';
import {BASE_PATH, HEADER_NAME, HOST_ADDRESS} from 'gcp-metadata';

import {RequestError} from './../transporters';
import * as rax from 'retry-axios';
import {CredentialRequest, Credentials} from './credentials';
import {GetTokenResponse, OAuth2Client, RefreshOptions} from './oauth2client';

export interface ComputeOptions extends RefreshOptions {}

// Create a scoped axios instance that will retry 3 times by default
const ax = axios.create();
rax.attach(ax);

export class Compute extends OAuth2Client {
/**
* Google Compute Engine metadata server token endpoint.
Expand Down Expand Up @@ -68,8 +71,11 @@ export class Compute extends OAuth2Client {
try {
// TODO: In 2.0, we should remove the ability to configure the tokenUrl,
// and switch this over to use the gcp-metadata package instead.
res = await this.transporter.request<CredentialRequest>(

This comment was marked as spam.

This comment was marked as spam.

{url, headers: {'Metadata-Flavor': 'Google'}});
res = await ax.request<CredentialRequest>({
url,
headers: {'Metadata-Flavor': 'Google'},

This comment was marked as spam.

This comment was marked as spam.

raxConfig: {noResponseRetries: 3, retry: 3, instance: ax}

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

} as rax.RaxConfig);
} catch (e) {
e.message = 'Could not refresh access token.';
throw e;
Expand Down Expand Up @@ -109,7 +115,7 @@ export class Compute extends OAuth2Client {
e.message = helpfulMessage;
} else {
e = new Error(helpfulMessage);
(e as RequestError).code = res.status.toString();
(e as NodeJS.ErrnoException).code = res.status.toString();
}
}
}
Expand Down
40 changes: 38 additions & 2 deletions test/test.compute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,10 @@
*/

import * as assert from 'assert';
import {AxiosRequestConfig} from 'axios';
import {BASE_PATH, HOST_ADDRESS} from 'gcp-metadata';
import * as nock from 'nock';

import {Credentials} from '../src/auth/credentials';
import {GoogleAuth} from '../src/auth/googleauth';
import {Compute} from '../src/index';

nock.disableNetConnect();
Expand Down Expand Up @@ -120,6 +118,44 @@ describe('Compute auth client', () => {
});
});

it('should retry calls to the metadata service if there are network errors',
(done) => {
const scopes = [
nock(HOST_ADDRESS).get(tokenPath).times(2).replyWithError({
code: 'ENOTFOUND'
}),
nock(HOST_ADDRESS).get(tokenPath).reply(200, {
access_token: 'abc123',
expires_in: 10000
})

This comment was marked as spam.

This comment was marked as spam.

];
compute.credentials.access_token = 'initial-access-token';
compute.credentials.expiry_date = (new Date()).getTime() - 10000;
compute.request({url: 'http://foo'}, e => {
assert.equal(compute.credentials.access_token, 'abc123');
scopes.forEach(s => s.done());
done();
});
});

it('should retry calls to the metadata service if it returns non-200 errors',
(done) => {
const scopes = [
nock(HOST_ADDRESS).get(tokenPath).times(2).reply(500),
nock(HOST_ADDRESS).get(tokenPath).reply(200, {
access_token: 'abc123',
expires_in: 10000

This comment was marked as spam.

This comment was marked as spam.

})
];
compute.credentials.access_token = 'initial-access-token';
compute.credentials.expiry_date = (new Date()).getTime() - 10000;
compute.request({url: 'http://foo'}, e => {
assert.equal(compute.credentials.access_token, 'abc123');
scopes.forEach(s => s.done());
done();
});
});

describe('.createScopedRequired', () => {
it('should return false', () => {
const c = new Compute();
Expand Down