简体   繁体   English

如果尝试连接到本地服务 (MinIO),AmazonsS3Client 会抛出 UnknownHostException

[英]AmazonsS3Client throws UnknownHostException if attempting to connect to a local service (MinIO)

In Short: Using AmazonS3Client to connect to a local instance of MinIO results in a UnknownHostException thrown because the url is resolved to http://{bucket_name}.localhost:port .简而言之:使用AmazonS3Client连接到MinIO的本地实例会导致抛出UnknownHostException ,因为 url 被解析为http://{bucket_name}.localhost:port


Detailed description of the problem:问题详细描述:

I'm creating an integration test for a Java service that uses AmazonS3Client lib to retrieve content from S3.我正在为 Java 服务创建集成测试,该服务使用 AmazonS3Client 库从 S3 检索内容。 I'm using MinIO inside a test container to perform the role of Amazon S3, as follows:我在测试容器内使用 MinIO 来执行 Amazon S3 的角色,如下所示:

@Container
static final GenericContainer<?> minioContainer = new GenericContainer<>("minio/minio:latest")
    .withCommand("server /data")
    .withEnv(
        Map.of(
            "MINIO_ACCESS_KEY", AWS_ACCESS_KEY.getValue(),
            "MINIO_SECRET_KEY", AWS_SECRET_KEY.getValue()
        )
    )
    .withExposedPorts(MINIO_PORT)
    .waitingFor(new HttpWaitStrategy()
        .forPath("/minio/health/ready")
        .forPort(MINIO_PORT)
        .withStartupTimeout(Duration.ofSeconds(10)));

and then I export its url dynamically (because test containers are deployed at a random port) using something like this:然后我使用如下方式动态导出其 url(因为测试容器部署在随机端口):

String.format("http://%s:%s", minioContainer.getHost(), minioContainer.getFirstMappedPort())

which in turn results in a url like this:这反过来导致 url 像这样:

http://localhost:54123

The problem I encountered during the runtime of my test lies within the actual implementation of AmazonS3Client.getObject(String,String) - when creating the request it performs the following validation (class S3RequestEndpointResolver , method resolveRequestEndpoint ):我在测试运行时遇到的问题在于AmazonS3Client.getObject(String,String)的实际实现 - 在创建请求时它执行以下验证(类S3RequestEndpointResolver ,方法resolveRequestEndpoint ):

...    
if (shouldUseVirtualAddressing(endpoint)) {
        request.setEndpoint(convertToVirtualHostEndpoint(endpoint, bucketName));
        request.setResourcePath(SdkHttpUtils.urlEncode(getHostStyleResourcePath(), true));
    } else {
        request.setEndpoint(endpoint);
        request.setResourcePath(SdkHttpUtils.urlEncode(getPathStyleResourcePath(), true));
    }
}

private boolean shouldUseVirtualAddressing(final URI endpoint) {
    return !isPathStyleAccess && BucketNameUtils.isDNSBucketName(bucketName)
            && !isValidIpV4Address(endpoint.getHost());
}

This in turn returns true for the url http://localhost:54123 and as a result this method这反过来为 url http://localhost:54123返回true ,因此该方法

private static URI convertToVirtualHostEndpoint(URI endpoint, String bucketName) {
    try {
        return new URI(String.format("%s://%s.%s", endpoint.getScheme(), bucketName, endpoint.getAuthority()));
    } catch (URISyntaxException e) {
        throw new IllegalArgumentException("Invalid bucket name: " + bucketName, e);
    }
}

concatenates the name of the bucket to the host resulting in: http://mybucket.localhost:54123 and this ultimately results in a UnknownHostException to be thrown.将存储桶的名称连接到主机,结果为: http://mybucket.localhost:54123 ,这最终导致抛出UnknownHostException I can work around this by setting the host to 0.0.0.0 instead of localhost , but this is hardly a solution.我可以通过将主机设置为0.0.0.0而不是localhost来解决这个问题,但这几乎不是解决方案。

Therefore I was wondering if i) this a bug/limitation in AmazonS3Client ?;因此我想知道这是否是AmazonS3Client中的错误/限制? ii) I'm the one who is missing something, eg poor configuration? ii) 我是缺少某些东西的人,例如配置不佳?

Thank you for your time感谢您的时间

I was able to find a solution.我能够找到解决方案。 Looking at the method used by the resolver:查看解析器使用的方法:

private boolean shouldUseVirtualAddressing(final URI endpoint) {
    return !isPathStyleAccess && BucketNameUtils.isDNSBucketName(bucketName)
            && !isValidIpV4Address(endpoint.getHost());
}

which was returning true and leading the flow to the wrong concatenation I found that we can set the first variable isPathStyleAccess when building the client.返回 true 并将流程引导到错误的连接我发现我们可以在构建客户端时设置第一个变量isPathStyleAccess In my case, I created a bean in my test configuration to override the main one:在我的例子中,我在我的测试配置中创建了一个 bean 来覆盖主要的:

@Bean
@Primary
public AmazonS3 amazonS3() {

    return AmazonS3Client.builder()
        .withPathStyleAccessEnabled(true) //HERE
        .withCredentials(new AWSStaticCredentialsProvider(
            new BasicAWSCredentials(AWS_ACCESS_KEY.getValue(), AWS_SECRET_KEY.getValue())
        ))
        .withEndpointConfiguration(
            new AwsClientBuilder.EndpointConfiguration(s3Endpoint, region)
        )
        .build();
    
}

For the SDK V2, the solution was pretty similar:对于 SDK V2,解决方案非常相似:

S3AsyncClient s3 = S3AsyncClient.builder()
                .forcePathStyle(true) // adding this one
                .endpointOverride(new URI(s3Endpoint))
                .credentialsProvider(() -> AwsBasicCredentials.create(s3Properties.getAccessKey(), s3Properties.getSecretKey()))
                .build()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM