pipelines/frontend/server/aws-helper.test.ts

137 lines
5.2 KiB
TypeScript

// Copyright 2019 The Kubeflow Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import fetch from 'node-fetch';
import { awsInstanceProfileCredentials, isAWSS3Endpoint } from './aws-helper';
// mock node-fetch module
jest.mock('node-fetch');
function sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
beforeEach(() => {
awsInstanceProfileCredentials.reset();
jest.clearAllMocks();
});
describe('awsInstanceProfileCredentials', () => {
const mockedFetch: jest.Mock = fetch as any;
describe('getCredentials', () => {
it('retrieves, caches, and refreshes the AWS EC2 instance profile and session credentials everytime it is called.', async () => {
let count = 0;
const expectedCredentials = [
{
AccessKeyId: 'AccessKeyId',
Code: 'Success',
Expiration: new Date(Date.now() + 1000).toISOString(), // expires 1 sec later
LastUpdated: '2019-12-17T10:55:38Z',
SecretAccessKey: 'SecretAccessKey',
Token: 'SessionToken',
Type: 'AWS-HMAC',
},
{
AccessKeyId: 'AccessKeyId2',
Code: 'Success',
Expiration: new Date(Date.now() + 10000).toISOString(), // expires 10 sec later
LastUpdated: '2019-12-17T10:55:38Z',
SecretAccessKey: 'SecretAccessKey2',
Token: 'SessionToken2',
Type: 'AWS-HMAC',
},
];
const mockFetch = (url: string) => {
if (url === 'http://169.254.169.254/latest/meta-data/iam/security-credentials/') {
return Promise.resolve({ text: () => Promise.resolve('some_iam_role') });
}
return Promise.resolve({
json: () => Promise.resolve(expectedCredentials[count++]),
});
};
mockedFetch.mockImplementation(mockFetch);
// expect to get cred from ec2 instance metadata store
expect(await awsInstanceProfileCredentials.getCredentials()).toBe(expectedCredentials[0]);
// expect to call once for profile name, and once for credential
expect(mockedFetch.mock.calls.length).toBe(2);
// expect to get same credential as it has not expire
expect(await awsInstanceProfileCredentials.getCredentials()).toBe(expectedCredentials[0]);
// expect to not to have any more calls
expect(mockedFetch.mock.calls.length).toBe(2);
// let credential expire
await sleep(1500);
// expect to get new cred as old one expire
expect(await awsInstanceProfileCredentials.getCredentials()).toBe(expectedCredentials[1]);
// expect to get same cred as it has not expire
expect(await awsInstanceProfileCredentials.getCredentials()).toBe(expectedCredentials[1]);
});
it('fails gracefully if there is no instance profile.', async () => {
const mockFetch = (url: string) => {
if (url === 'http://169.254.169.254/latest/meta-data/iam/security-credentials/') {
return Promise.resolve({ text: () => Promise.resolve('') });
}
return Promise.reject('Unknown error');
};
mockedFetch.mockImplementation(mockFetch);
expect(await awsInstanceProfileCredentials.ok()).toBeFalsy();
expect(async () => await awsInstanceProfileCredentials.getCredentials()).not.toThrow();
expect(await awsInstanceProfileCredentials.getCredentials()).toBeUndefined();
});
it('fails gracefully if there is no metadata store.', async () => {
const mockFetch = (_: string) => {
return Promise.reject('Unknown error');
};
mockedFetch.mockImplementation(mockFetch);
expect(await awsInstanceProfileCredentials.ok()).toBeFalsy();
expect(async () => await awsInstanceProfileCredentials.getCredentials()).not.toThrow();
expect(await awsInstanceProfileCredentials.getCredentials()).toBeUndefined();
});
});
});
describe('isS3Endpoint', () => {
it('checks a valid s3 endpoint', () => {
expect(isAWSS3Endpoint('s3.amazonaws.com')).toBe(true);
});
it('checks a valid s3 regional endpoint', () => {
expect(isAWSS3Endpoint('s3.dualstack.us-east-1.amazonaws.com')).toBe(true);
});
it('checks a valid s3 cn endpoint', () => {
expect(isAWSS3Endpoint('s3.cn-north-1.amazonaws.com.cn')).toBe(true);
});
it('checks a valid s3 fips GovCloud endpoint', () => {
expect(isAWSS3Endpoint('s3-fips.us-gov-west-1.amazonaws.com')).toBe(true);
});
it('checks a valid s3 PrivateLink endpoint', () => {
expect(isAWSS3Endpoint('vpce-1a2b3c4d-5e6f.s3.us-east-1.vpce.amazonaws.com')).toBe(true);
});
it('checks an invalid s3 endpoint', () => {
expect(isAWSS3Endpoint('amazonaws.com')).toBe(false);
});
it('checks non-s3 endpoint', () => {
expect(isAWSS3Endpoint('minio.kubeflow')).toBe(false);
});
});