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

SNOW-1789759: NodeJS Support GCS region specific endpoint #975

Merged
merged 10 commits into from
Dec 5, 2024
4 changes: 3 additions & 1 deletion lib/file_transfer_agent/gcs_util.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ function GCSUtil(httpclient, filestream) {
}
});

const storage = new Storage({ interceptors_: interceptors });
//TODO: hardcoded region will be replaced in the future
sfc-gh-dprzybysz marked this conversation as resolved.
Show resolved Hide resolved
const isRegionalUrlEnabled = (stageInfo.region).toLowerCase() === 'me-central2' || stageInfo.useRegionalUrl;
const storage = isRegionalUrlEnabled ? new Storage({ interceptors_: interceptors, apiEndpoint: `storage.${stageInfo.region.toLowerCase()}.rep.googleapis.com` }) : new Storage({ interceptors_: interceptors });
sfc-gh-dprzybysz marked this conversation as resolved.
Show resolved Hide resolved

client = { gcsToken: gcsToken, gcsClient: storage };
} else {
Expand Down
13 changes: 10 additions & 3 deletions lib/file_transfer_agent/s3_util.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,19 @@ function S3Util(connectionConfig, s3, filestream) {
this.createClient = function (stageInfo, useAccelerateEndpoint) {
const stageCredentials = stageInfo['creds'];
const securityToken = stageCredentials['AWS_TOKEN'];
const isRegionalUrlEnabled = stageInfo.useRegionalUrl || stageInfo.useS3RegionalUrl;

// if GS sends us an endpoint, it's likely for FIPS. Use it.
let endPoint = null;
if (stageInfo['endPoint']) {
endPoint = 'https://' + stageInfo['endPoint'];
endPoint = `https://${stageInfo['endPoint']}`;
} else {
if (stageInfo.region && isRegionalUrlEnabled) {
const domainSuffixForRegionalUrl = (stageInfo.region).toLowerCase().startsWith('cn-') ? 'amazonaws.com.cn' : 'amazonaws.com';
sfc-gh-dprzybysz marked this conversation as resolved.
Show resolved Hide resolved
endPoint = `https://s3.${stageInfo.region}.${domainSuffixForRegionalUrl}`;
}
}

const config = {
apiVersion: '2006-03-01',
region: stageInfo['region'],
Expand All @@ -80,7 +87,7 @@ function S3Util(connectionConfig, s3, filestream) {
}
}
if (proxy) {
const proxyAgent = getProxyAgent(proxy, new URL(connectionConfig.accessUrl), SNOWFLAKE_S3_DESTINATION);
const proxyAgent = getProxyAgent(proxy, new URL(connectionConfig.accessUrl), endPoint || SNOWFLAKE_S3_DESTINATION);
config.requestHandler = new NodeHttpHandler({
httpAgent: proxyAgent,
httpsAgent: proxyAgent
Expand Down
37 changes: 37 additions & 0 deletions test/unit/file_transfer_agent/gcs_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,43 @@ describe('GCS client', function () {
GCS = new SnowflakeGCSUtil(httpclient, filestream);
});

describe('GCS client endpoint testing', async function () {
sfc-gh-dprzybysz marked this conversation as resolved.
Show resolved Hide resolved
const testCases = [
{
name: 'when useRegionalURL is only enabled',
stageInfo: {
useRegionalUrl: true,
region: 'mockLocation',
},
result: 'https://storage.mocklocation.rep.googleapis.com'
},
{
name: 'when region is me-central2',
stageInfo: {
useRegionalUrl: false,
region: 'me-central2'
},
result: 'https://storage.me-central2.rep.googleapis.com'
},
{
name: 'when region is me-central2',
stageInfo: {
useRegionalUrl: false,
region: 'ME-cEntRal2'
},
result: 'https://storage.me-central2.rep.googleapis.com'
},
];

testCases.forEach(({ name, stageInfo, result }) => {
it(name, () => {
const client = GCS.createClient({ ...stageInfo, ...meta.stageInfo, creds: { GCS_ACCESS_TOKEN: 'mockToken' } });
assert.strictEqual(client.gcsClient.apiEndpoint, result);
} );

});
});

it('extract bucket name and path', async function () {
const GCS = new SnowflakeGCSUtil();

Expand Down
57 changes: 56 additions & 1 deletion test/unit/file_transfer_agent/s3_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('S3 client', function () {

before(function () {
mock('s3', {
S3: function () {
S3: function (config) {
function S3() {
this.getObject = function () {
function getObject() {
Expand All @@ -57,6 +57,8 @@ describe('S3 client', function () {

return new getObject;
};

this.config = config;
this.putObject = function () {
function putObject() {
this.then = function (callback) {
Expand All @@ -82,6 +84,59 @@ describe('S3 client', function () {
AWS = new SnowflakeS3Util(noProxyConnectionConfig, s3, filesystem);
});

describe('AWS client endpoint testing', async function () {
const originalStageInfo = meta.stageInfo;
const testCases = [
{
name: 'when useS3RegionalURL is only enabled',
stageInfo: {
...originalStageInfo,
useS3RegionalUrl: true,
endPoint: null,
},
result: null
},
{
name: 'when useS3RegionalURL and is enabled and domain starts with cn',
stageInfo: {
...originalStageInfo,
useS3RegionalUrl: true,
endPoint: null,
region: 'cn-mockLocation'
},
result: 'https://s3.cn-mockLocation.amazonaws.com.cn'
},
{
name: 'when endPoint is enabled',
stageInfo: {
...originalStageInfo,
endPoint: 's3.endpoint',
useS3RegionalUrl: false
},
result: 'https://s3.endpoint'
},
{
name: 'when both endPoint and useS3PReiongalUrl is valid',
stageInfo: {
...originalStageInfo,
endPoint: 's3.endpoint',
useS3RegionalUrl: true,

},
result: 'https://s3.endpoint'
},
];

testCases.forEach(({ name, stageInfo, result }) => {
it(name, () => {
const client = AWS.createClient(stageInfo);
assert.strictEqual(client.config.endpoint, result);
} );

});
});


it('extract bucket name and path', async function () {
let result = extractBucketNameAndPath('sfc-eng-regression/test_sub_dir/');
assert.strictEqual(result.bucketName, 'sfc-eng-regression');
Expand Down
Loading