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
3 changes: 2 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,8 @@ function GCSUtil(httpclient, filestream) {
}
});

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

client = { gcsToken: gcsToken, gcsClient: storage };
} else {
Expand Down
12 changes: 10 additions & 2 deletions lib/file_transfer_agent/s3_util.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,19 @@ function S3Util(connectionConfig, s3, filestream) {
this.createClient = function (stageInfo, useAccelerateEndpoint) {
const stageCredentials = stageInfo['creds'];
const securityToken = stageCredentials['AWS_TOKEN'];
const useS3RegionalUrl = 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 && useS3RegionalUrl) {
sfc-gh-dprzybysz marked this conversation as resolved.
Show resolved Hide resolved
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',
Expand All @@ -80,7 +88,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
29 changes: 29 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,35 @@ 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'
},
];

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