Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,8 @@ protected ClientOverrideConfiguration.Builder createClientOverrideConfiguration(
*/
private <BuilderT extends S3BaseClientBuilder<BuilderT, ClientT>, ClientT> void configureEndpointAndRegion(
BuilderT builder, S3ClientCreationParameters parameters, Configuration conf) {
URI endpoint = getS3Endpoint(parameters.getEndpoint(), conf);
final String endpointStr = parameters.getEndpoint();
URI endpoint = getS3Endpoint(endpointStr, conf);

String configuredRegion = parameters.getRegion();
Region region = null;
Expand All @@ -294,9 +295,14 @@ private <BuilderT extends S3BaseClientBuilder<BuilderT, ClientT>, ClientT> void
builder.endpointOverride(endpoint);
// No region was configured, try to determine it from the endpoint.
if (region == null) {
region = getS3RegionFromEndpoint(parameters.getEndpoint());
boolean endpointEndsWithCentral = endpointStr.endsWith(CENTRAL_ENDPOINT);
region = getS3RegionFromEndpoint(endpointStr, endpointEndsWithCentral);
if (region != null) {
origin = "endpoint";
if (endpointEndsWithCentral) {
builder.crossRegionAccessEnabled(true);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add more detail to origin, e,g 'origin with cross-region access"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

LOG.debug("Enabling cross region access for endpoint {}", endpointStr);
Comment thread
mukund-thakur marked this conversation as resolved.
Outdated
}
}
}
LOG.debug("Setting endpoint to {}", endpoint);
Expand Down Expand Up @@ -354,20 +360,21 @@ private static URI getS3Endpoint(String endpoint, final Configuration conf) {

/**
* Parses the endpoint to get the region.
* If endpoint is the central one, use US_EAST_1.
* If endpoint is the central one, use US_EAST_2.
*
* @param endpoint the configure endpoint.
* @param endpointEndsWithCentral true if the endpoint is configured as central.
* @return the S3 region, null if unable to resolve from endpoint.
*/
private static Region getS3RegionFromEndpoint(String endpoint) {
private static Region getS3RegionFromEndpoint(String endpoint, boolean endpointEndsWithCentral) {

if(!endpoint.endsWith(CENTRAL_ENDPOINT)) {
if (!endpointEndsWithCentral) {
LOG.debug("Endpoint {} is not the default; parsing", endpoint);
return AwsHostNameUtils.parseSigningRegion(endpoint, S3_SERVICE_NAME).orElse(null);
}

// endpoint is for US_EAST_1;
return Region.US_EAST_1;
// endpoint is for US_EAST_2;
return Region.US_EAST_2;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changing this causes confusion. Maybe its better to the use the variable present in Constants.

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/

package org.apache.hadoop.fs.s3a;

import org.junit.Test;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.contract.ContractTestUtils;

import static org.apache.hadoop.fs.s3a.Constants.AWS_REGION;
import static org.apache.hadoop.fs.s3a.Constants.CENTRAL_ENDPOINT;
import static org.apache.hadoop.fs.s3a.Constants.ENDPOINT;
import static org.apache.hadoop.fs.s3a.S3ATestUtils.removeBaseAndBucketOverrides;

/**
* Test to verify cross region bucket access.
*/
public class ITestS3ACrossRegionAccess extends AbstractS3ATestBase {

@Test
public void testCentralEndpointCrossRegionAccess() throws Throwable {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be better to move ITestsS3AEndpoint instead of creating a new test class?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that test, we are not trying to create/write/read file using fs, so i thought of keeping this separate. This test fails during mkdir with 400 without the source change.

describe("Create bucket on different region and access it using central endpoint");
Configuration conf = getConfiguration();
removeBaseAndBucketOverrides(conf, ENDPOINT, AWS_REGION);

Configuration newConf = new Configuration(conf);

newConf.set(ENDPOINT, CENTRAL_ENDPOINT);

try (S3AFileSystem newFs = new S3AFileSystem()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you look at the tests in ITestS3AEndpointRegion you can see how we do it there, instead of creating anything, we just intercept the request and check if things are being set correctly.

For this what we want to see is that if the central point is configured, and no region is configured .. region gets set to US_EAST_2 for cross region.

But if central endpoint is configured, and region is configured to US_EAST_1 , then region is US_EAST_1. that is region config takes precedence. See testCentralEndpoint in that class, does something similar.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked that test but since there was no real fs operation involved, i thought of keeping this as separate test. Does that work?
I can keep this test class as is and maybe write one more test in ITestS3AEndpointRegion as well without much of file operation?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to do a mdkir to verify behaviour though? won't just doing a headBucket also work? I think that would also fail without your change?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we have our custom RegionInterceptor, we will anyways not be able to make real request right? So regardless of whether we enable cross region endpoint, ITestS3AEndpointRegion is not going to help make real head request, that's why i thought of creating new class where regardless of the endpoint/region used to create bucket, new fs with central endpoint is able to perform file operations on the bucket.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping the stacktrace here for reference:

org.apache.hadoop.fs.s3a.AWSBadRequestException: getFileStatus on s3a://${bucket}/user/vjasani/basePath-testCentralEndpointCrossRegionAccess/srcdir: software.amazon.awssdk.services.s3.model.S3Exception: The authorization header is malformed; the region 'us-east-2' is wrong; expecting 'us-west-2' (Service: S3, Status Code: 400, Request ID: G85CNFC579T4MJ76, Extended Request ID: xrYGGqXdYtr72cYyFN3v4yemDxBCYkdt8mYd8cGItNhdx1EmZMLxMhwJTwzmWZT6ershid/WT4w=):AuthorizationHeaderMalformed: The authorization header is malformed; the region 'us-east-2' is wrong; expecting 'us-west-2' (Service: S3, Status Code: 400, Request ID: G85CNFC579T4MJ76, Extended Request ID: xrYGGqXdYtr72cYyFN3v4yemDxBCYkdt8mYd8cGItNhdx1EmZMLxMhwJTwzmWZT6ershid/WT4w=)

	at org.apache.hadoop.fs.s3a.S3AUtils.translateException(S3AUtils.java:259)
	at org.apache.hadoop.fs.s3a.S3AUtils.translateException(S3AUtils.java:154)
	at org.apache.hadoop.fs.s3a.S3AFileSystem.s3GetFileStatus(S3AFileSystem.java:4075)
	at org.apache.hadoop.fs.s3a.S3AFileSystem.innerGetFileStatus(S3AFileSystem.java:3934)
	at org.apache.hadoop.fs.s3a.S3AFileSystem$MkdirOperationCallbacksImpl.probePathStatus(S3AFileSystem.java:3806)
	at org.apache.hadoop.fs.s3a.impl.MkdirOperation.probePathStatusOrNull(MkdirOperation.java:173)
	at org.apache.hadoop.fs.s3a.impl.MkdirOperation.getPathStatusExpectingDir(MkdirOperation.java:194)
	at org.apache.hadoop.fs.s3a.impl.MkdirOperation.execute(MkdirOperation.java:108)
	at org.apache.hadoop.fs.s3a.impl.MkdirOperation.execute(MkdirOperation.java:57)
	at org.apache.hadoop.fs.s3a.impl.ExecutingStoreOperation.apply(ExecutingStoreOperation.java:76)
	at org.apache.hadoop.fs.statistics.impl.IOStatisticsBinding.invokeTrackingDuration(IOStatisticsBinding.java:547)
	at org.apache.hadoop.fs.statistics.impl.IOStatisticsBinding.lambda$trackDurationOfOperation$5(IOStatisticsBinding.java:528)
	at org.apache.hadoop.fs.statistics.impl.IOStatisticsBinding.trackDuration(IOStatisticsBinding.java:449)
	at org.apache.hadoop.fs.s3a.S3AFileSystem.trackDurationAndSpan(S3AFileSystem.java:2719)
	at org.apache.hadoop.fs.s3a.S3AFileSystem.trackDurationAndSpan(S3AFileSystem.java:2738)
	at org.apache.hadoop.fs.s3a.S3AFileSystem.mkdirs(S3AFileSystem.java:3778)
	at org.apache.hadoop.fs.FileSystem.mkdirs(FileSystem.java:2494)
	at org.apache.hadoop.fs.s3a.ITestS3ACrossRegionAccess.testCentralEndpointCrossRegionAccess(ITestS3ACrossRegionAccess.java:54)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to nitpick here! but in ITestS3AEndpointRegion, there is also a test currently that uses the FS, see testWithoutRegionConfig.

You can add another test there that does something like:

    Configuration conf = getConfiguration();
    removeBaseAndBucketOverrides(conf, ENDPOINT, AWS_REGION);
    conf.set(ENDPOINT, CENTRAL_ENDPOINT);
    
    newFS = new S3AFileSystem();
    newFS.initialize(getFileSystem().getUri(), conf);

    newFS.create(methodPath()).close();

This will fail without your source changes, but passes with them.

newFs.initialize(getFileSystem().getUri(), newConf);

final String file = getMethodName();
Path basePath = new Path("basePath-" + getMethodName());
final Path srcDir = new Path(basePath, "srcdir");
newFs.mkdirs(srcDir);
Path src = new Path(srcDir, file);

try (FSDataOutputStream out = newFs.create(src)) {
out.write(new byte[] {1, 2, 3, 4, 5});
}
ContractTestUtils.assertIsFile(getFileSystem(), new Path(srcDir, file));
newFs.delete(srcDir, true);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,17 @@ public void testCentralEndpoint() throws Throwable {
describe("Create a client with the central endpoint");
Configuration conf = getConfiguration();

S3Client client = createS3Client(conf, CENTRAL_ENDPOINT, null, US_EAST_1, false);
S3Client client = createS3Client(conf, CENTRAL_ENDPOINT, null, US_EAST_2, false);

expectInterceptorException(client);
}

@Test
public void testCentralEndpointWithRegion() throws Throwable {
describe("Create a client with the central endpoint but also specify region");
Configuration conf = getConfiguration();

S3Client client = createS3Client(conf, CENTRAL_ENDPOINT, US_WEST_2, US_WEST_2, false);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not clear to me what these different test cases are doing. looks like they call check if you set endpoint to central and also configure a region, it's always the configured region that gets set. do we really need all of them?


expectInterceptorException(client);
}
Expand Down