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

Joã*_*tos 6 java amazon-s3 minio testcontainers

简而言之:使用AmazonS3Client连接到 的本地实例MinIO会导致抛出异常UnknownHostException,因为 url 已解析为http://{bucket_name}.localhost:port


问题详细描述:

我正在为使用 AmazonS3Client lib 从 S3 检索内容的 Java 服务创建集成测试。我在测试容器中使用 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)));
Run Code Online (Sandbox Code Playgroud)

然后我使用如下方式动态导出其 url(因为测试容器部署在随机端口):

String.format("http://%s:%s", minioContainer.getHost(), minioContainer.getFirstMappedPort())
Run Code Online (Sandbox Code Playgroud)

这又会产生这样的 url:

http://localhost:54123
Run Code Online (Sandbox Code Playgroud)

我在测试运行时遇到的问题在于实际实现中AmazonS3Client.getObject(String,String)- 创建请求时,它执行以下验证( class S3RequestEndpointResolver, method 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());
}
Run Code Online (Sandbox Code Playgroud)

这又返回trueurlhttp://localhost:54123并因此返回此方法

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

将存储桶的名称连接到主机,结果是:http://mybucket.localhost:54123这最终导致UnknownHostException抛出 a 。我可以通过将主机设置为0.0.0.0而不是来解决这个问题localhost,但这不是一个解决方案。

因此我想知道我)这是否是一个错误/限制AmazonS3Client?ii)我是缺少某些东西的人,例如配置不佳?

感谢您的时间

Joã*_*tos 6

我找到了解决方案。查看解析器使用的方法:

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

返回 true 并导致流程出现错误的串联 我发现我们可以isPathStyleAccess在构建客户端时设置第一个变量。就我而言,我在测试配置中创建了一个 bean 来覆盖主 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();
    
}
Run Code Online (Sandbox Code Playgroud)