Skip to content

Commit 406ddc0

Browse files
authored
Support custom prefixes for S3 and STS (#75)
Closes #57
1 parent 6cf3a7a commit 406ddc0

21 files changed

+261
-115
lines changed

Diff for: trino-s3-proxy/src/main/java/io/trino/s3/proxy/server/TrinoS3ProxyServerModule.java

+11-2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import io.trino.s3.proxy.server.security.SecurityResponse;
3636
import io.trino.s3.proxy.server.signing.SigningController;
3737
import io.trino.s3.proxy.server.signing.SigningControllerConfig;
38+
import org.glassfish.jersey.server.model.Resource;
3839

3940
import java.util.Optional;
4041
import java.util.ServiceLoader;
@@ -52,11 +53,14 @@ public class TrinoS3ProxyServerModule
5253
@Override
5354
protected void setup(Binder binder)
5455
{
56+
configBinder(binder).bindConfig(SigningControllerConfig.class);
57+
TrinoS3ProxyConfig builtConfig = buildConfigObject(TrinoS3ProxyConfig.class);
58+
5559
jaxrsBinder(binder).bind(TrinoS3ProxyResource.class);
60+
jaxrsBinder(binder).bindInstance(buildResourceAtPath(TrinoS3ProxyResource.class, builtConfig.getS3Path()));
5661
jaxrsBinder(binder).bind(TrinoStsResource.class);
62+
jaxrsBinder(binder).bindInstance(buildResourceAtPath(TrinoStsResource.class, builtConfig.getStsPath()));
5763

58-
configBinder(binder).bindConfig(SigningControllerConfig.class);
59-
configBinder(binder).bindConfig(TrinoS3ProxyConfig.class);
6064
binder.bind(SigningController.class).in(Scopes.SINGLETON);
6165
binder.bind(CredentialsController.class).in(Scopes.SINGLETON);
6266
binder.bind(SecurityController.class).in(Scopes.SINGLETON);
@@ -96,4 +100,9 @@ private void installPlugins()
96100
install(plugin.module());
97101
});
98102
}
103+
104+
private static Resource buildResourceAtPath(Class<?> resourceClass, String resourcePathPrefix)
105+
{
106+
return Resource.builder(resourceClass).path(resourcePathPrefix).build();
107+
}
99108
}

Diff for: trino-s3-proxy/src/main/java/io/trino/s3/proxy/server/rest/TrinoS3ProxyConfig.java

+37-7
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,49 @@
2121

2222
public class TrinoS3ProxyConfig
2323
{
24-
private Optional<String> hostName = Optional.empty();
24+
private Optional<String> s3HostName = Optional.empty();
25+
private String s3Path = "/api/v1/s3Proxy/s3";
26+
private String stsPath = "/api/v1/s3Proxy/sts";
2527

26-
@Config("s3proxy.hostname")
27-
@ConfigDescription("Hostname to use for REST operations, virtual-host style addressing is only supported if this is set")
28-
public TrinoS3ProxyConfig setHostName(String hostName)
28+
@Config("s3proxy.s3.hostname")
29+
@ConfigDescription("Hostname to use for S3 REST operations, virtual-host style addressing is only supported if this is set")
30+
public TrinoS3ProxyConfig setS3HostName(String s3HostName)
2931
{
30-
this.hostName = Optional.ofNullable(hostName);
32+
this.s3HostName = Optional.ofNullable(s3HostName);
3133
return this;
3234
}
3335

3436
@NotNull
35-
public Optional<String> getHostName()
37+
public Optional<String> getS3HostName()
3638
{
37-
return hostName;
39+
return s3HostName;
40+
}
41+
42+
@Config("s3proxy.s3.path")
43+
@ConfigDescription("URL Path for S3 operations, optional")
44+
public TrinoS3ProxyConfig setS3Path(String s3Path)
45+
{
46+
this.s3Path = s3Path;
47+
return this;
48+
}
49+
50+
@NotNull
51+
public String getS3Path()
52+
{
53+
return s3Path;
54+
}
55+
56+
@Config("s3proxy.sts.path")
57+
@ConfigDescription("URL Path for STS operations, optional")
58+
public TrinoS3ProxyConfig setStsPath(String stsPath)
59+
{
60+
this.stsPath = stsPath;
61+
return this;
62+
}
63+
64+
@NotNull
65+
public String getStsPath()
66+
{
67+
return stsPath;
3868
}
3969
}

Diff for: trino-s3-proxy/src/main/java/io/trino/s3/proxy/server/rest/TrinoS3ProxyResource.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import static io.trino.s3.proxy.server.rest.RequestBuilder.fromRequest;
3434
import static java.util.Objects.requireNonNull;
3535

36-
@Path(TrinoS3ProxyRestConstants.S3_PATH)
3736
public class TrinoS3ProxyResource
3837
{
3938
private final SigningController signingController;
@@ -45,7 +44,7 @@ public TrinoS3ProxyResource(SigningController signingController, TrinoS3ProxyCli
4544
{
4645
this.signingController = requireNonNull(signingController, "signingController is null");
4746
this.proxyClient = requireNonNull(proxyClient, "proxyClient is null");
48-
this.serverHostName = requireNonNull(trinoS3ProxyConfig, "restConfig is null").getHostName();
47+
this.serverHostName = trinoS3ProxyConfig.getS3HostName();
4948
}
5049

5150
@GET

Diff for: trino-s3-proxy/src/main/java/io/trino/s3/proxy/server/rest/TrinoS3ProxyRestConstants.java

-23
This file was deleted.

Diff for: trino-s3-proxy/src/main/java/io/trino/s3/proxy/server/rest/TrinoStsResource.java

-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import io.trino.s3.proxy.server.signing.SigningMetadata;
2626
import io.trino.s3.proxy.server.signing.SigningServiceType;
2727
import jakarta.ws.rs.POST;
28-
import jakarta.ws.rs.Path;
2928
import jakarta.ws.rs.WebApplicationException;
3029
import jakarta.ws.rs.core.Context;
3130
import jakarta.ws.rs.core.MediaType;
@@ -43,7 +42,6 @@
4342
import static io.trino.s3.proxy.server.signing.SigningController.formatResponseInstant;
4443
import static java.util.Objects.requireNonNull;
4544

46-
@Path(TrinoS3ProxyRestConstants.STS_PATH)
4745
public class TrinoStsResource
4846
{
4947
private static final Logger log = Logger.get(TrinoStsResource.class);

Diff for: trino-s3-proxy/src/test/java/io/trino/s3/proxy/server/TestStsRequests.java renamed to trino-s3-proxy/src/test/java/io/trino/s3/proxy/server/AbstractTestStsRequests.java

+4-9
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,11 @@
1313
*/
1414
package io.trino.s3.proxy.server;
1515

16-
import com.google.inject.Inject;
1716
import io.airlift.http.server.testing.TestingHttpServer;
1817
import io.trino.s3.proxy.server.credentials.Credential;
1918
import io.trino.s3.proxy.server.credentials.Credentials;
2019
import io.trino.s3.proxy.server.credentials.CredentialsProvider;
21-
import io.trino.s3.proxy.server.rest.TrinoS3ProxyRestConstants;
22-
import io.trino.s3.proxy.server.testing.TestingUtil.ForTesting;
23-
import io.trino.s3.proxy.server.testing.harness.TrinoS3ProxyTest;
20+
import io.trino.s3.proxy.server.rest.TrinoS3ProxyConfig;
2421
import org.junit.jupiter.api.Test;
2522
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
2623
import software.amazon.awssdk.endpoints.Endpoint;
@@ -36,18 +33,16 @@
3633
import static java.util.concurrent.CompletableFuture.completedFuture;
3734
import static org.assertj.core.api.Assertions.assertThat;
3835

39-
@TrinoS3ProxyTest
40-
public class TestStsRequests
36+
public abstract class AbstractTestStsRequests
4137
{
4238
private final StsClient stsClient;
4339
private final CredentialsProvider credentialsProvider;
4440

45-
@Inject
46-
public TestStsRequests(@ForTesting Credentials testingCredentials, TestingHttpServer httpServer, CredentialsProvider credentialsProvider)
41+
public AbstractTestStsRequests(Credentials testingCredentials, TestingHttpServer httpServer, CredentialsProvider credentialsProvider, TrinoS3ProxyConfig s3ProxyConfig)
4742
{
4843
this.credentialsProvider = requireNonNull(credentialsProvider, "credentialsProvider is null");
4944

50-
URI localProxyServerUri = httpServer.getBaseUrl().resolve(TrinoS3ProxyRestConstants.STS_PATH);
45+
URI localProxyServerUri = httpServer.getBaseUrl().resolve(s3ProxyConfig.getStsPath());
5146
Endpoint endpoint = Endpoint.builder().url(localProxyServerUri).build();
5247

5348
stsClient = StsClient.builder()

Diff for: trino-s3-proxy/src/test/java/io/trino/s3/proxy/server/LocalServer.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import io.airlift.http.server.testing.TestingHttpServer;
1818
import io.airlift.log.Logger;
1919
import io.trino.s3.proxy.server.credentials.Credentials;
20-
import io.trino.s3.proxy.server.rest.TrinoS3ProxyRestConstants;
20+
import io.trino.s3.proxy.server.rest.TrinoS3ProxyConfig;
2121
import io.trino.s3.proxy.server.testing.TestingTrinoS3ProxyServer;
2222
import io.trino.s3.proxy.server.testing.TestingUtil.ForTesting;
2323

@@ -38,9 +38,10 @@ public static void main(String[] args)
3838

3939
TestingHttpServer httpServer = trinoS3ProxyServer.getInjector().getInstance(TestingHttpServer.class);
4040
Credentials testingCredentials = trinoS3ProxyServer.getInjector().getInstance(Key.get(Credentials.class, ForTesting.class));
41+
TrinoS3ProxyConfig s3ProxyConfig = trinoS3ProxyServer.getInjector().getInstance(TrinoS3ProxyConfig.class);
4142

4243
log.info("");
43-
log.info("Endpoint: %s", httpServer.getBaseUrl().resolve(TrinoS3ProxyRestConstants.S3_PATH));
44+
log.info("Endpoint: %s", httpServer.getBaseUrl().resolve(s3ProxyConfig.getS3Path()));
4445
log.info("Access Key: %s", testingCredentials.emulated().accessKey());
4546
log.info("Secret Key: %s", testingCredentials.emulated().secretKey());
4647
log.info("");

Diff for: trino-s3-proxy/src/test/java/io/trino/s3/proxy/server/TestGenericRestRequests.java

+4-5
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import io.airlift.units.Duration;
2222
import io.trino.s3.proxy.server.credentials.Credential;
2323
import io.trino.s3.proxy.server.credentials.Credentials;
24-
import io.trino.s3.proxy.server.rest.TrinoS3ProxyRestConstants;
24+
import io.trino.s3.proxy.server.rest.TrinoS3ProxyConfig;
2525
import io.trino.s3.proxy.server.testing.ManagedS3MockContainer.ForS3MockContainer;
2626
import io.trino.s3.proxy.server.testing.TestingCredentialsRolesProvider;
2727
import io.trino.s3.proxy.server.testing.TestingTrinoS3ProxyServer;
@@ -100,9 +100,10 @@ public TestGenericRestRequests(
100100
TestingCredentialsRolesProvider credentialsRolesProvider,
101101
@ForTesting HttpClient httpClient,
102102
@ForTesting Credentials testingCredentials,
103-
@ForS3MockContainer S3Client storageClient)
103+
@ForS3MockContainer S3Client storageClient,
104+
TrinoS3ProxyConfig trinoS3ProxyConfig)
104105
{
105-
baseUri = httpServer.getBaseUrl();
106+
baseUri = httpServer.getBaseUrl().resolve(trinoS3ProxyConfig.getS3Path());
106107
this.credentialsRolesProvider = requireNonNull(credentialsRolesProvider, "credentialsRolesProvider is null");
107108
this.httpClient = requireNonNull(httpClient, "httpClient is null");
108109
this.testingCredentials = requireNonNull(testingCredentials, "testingCredentials is null");
@@ -141,7 +142,6 @@ public void testPutObject()
141142
private StatusResponse doPutObject(String content, String sha256)
142143
{
143144
URI uri = UriBuilder.fromUri(baseUri)
144-
.replacePath(TrinoS3ProxyRestConstants.S3_PATH)
145145
.path("foo")
146146
.path("bar")
147147
.build();
@@ -167,7 +167,6 @@ private StatusResponse doPutObject(String content, String sha256)
167167
private StatusResponse doAwsChunkedUpload(String content)
168168
{
169169
URI uri = UriBuilder.fromUri(baseUri)
170-
.replacePath(TrinoS3ProxyRestConstants.S3_PATH)
171170
.path("two")
172171
.path("test")
173172
.build();

Diff for: trino-s3-proxy/src/test/java/io/trino/s3/proxy/server/TestProxiedAssumedRoleRequests.java

+7-6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import com.google.inject.Inject;
1717
import io.airlift.http.server.testing.TestingHttpServer;
1818
import io.trino.s3.proxy.server.credentials.Credentials;
19-
import io.trino.s3.proxy.server.rest.TrinoS3ProxyRestConstants;
19+
import io.trino.s3.proxy.server.rest.TrinoS3ProxyConfig;
2020
import io.trino.s3.proxy.server.testing.ManagedS3MockContainer.ForS3MockContainer;
2121
import io.trino.s3.proxy.server.testing.TestingCredentialsRolesProvider;
2222
import io.trino.s3.proxy.server.testing.TestingUtil.ForTesting;
@@ -51,9 +51,10 @@ public TestProxiedAssumedRoleRequests(
5151
@ForTesting Credentials testingCredentials,
5252
TestingCredentialsRolesProvider credentialsController,
5353
@ForS3MockContainer S3Client storageClient,
54-
@ForS3MockContainer List<String> configuredBuckets)
54+
@ForS3MockContainer List<String> configuredBuckets,
55+
TrinoS3ProxyConfig trinoS3ProxyConfig)
5556
{
56-
this(buildClient(httpServer, testingCredentials), testingCredentials, credentialsController, storageClient, configuredBuckets);
57+
this(buildClient(httpServer, testingCredentials, trinoS3ProxyConfig.getS3Path(), trinoS3ProxyConfig.getStsPath()), testingCredentials, credentialsController, storageClient, configuredBuckets);
5758
}
5859

5960
protected TestProxiedAssumedRoleRequests(
@@ -75,11 +76,11 @@ public void validateCount()
7576
credentialsController.resetAssumedRoles();
7677
}
7778

78-
protected static S3Client buildClient(TestingHttpServer httpServer, Credentials credentials)
79+
protected static S3Client buildClient(TestingHttpServer httpServer, Credentials credentials, String s3Path, String stsPath)
7980
{
8081
URI baseUrl = httpServer.getBaseUrl();
81-
URI localProxyServerUri = baseUrl.resolve(TrinoS3ProxyRestConstants.S3_PATH);
82-
URI localStsServerUri = baseUrl.resolve(TrinoS3ProxyRestConstants.STS_PATH);
82+
URI localProxyServerUri = baseUrl.resolve(s3Path);
83+
URI localStsServerUri = baseUrl.resolve(stsPath);
8384

8485
AwsBasicCredentials awsBasicCredentials = AwsBasicCredentials.create(credentials.emulated().accessKey(), credentials.emulated().secretKey());
8586

Diff for: trino-s3-proxy/src/test/java/io/trino/s3/proxy/server/TestProxiedEmulatedAndRemoteAssumedRoleRequests.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import com.google.inject.Inject;
1717
import io.airlift.http.server.testing.TestingHttpServer;
1818
import io.trino.s3.proxy.server.credentials.Credentials;
19+
import io.trino.s3.proxy.server.rest.TrinoS3ProxyConfig;
1920
import io.trino.s3.proxy.server.testing.ManagedS3MockContainer.ForS3MockContainer;
2021
import io.trino.s3.proxy.server.testing.TestingCredentialsRolesProvider;
2122
import io.trino.s3.proxy.server.testing.TestingTrinoS3ProxyServerModule.ForTestingRemoteCredentials;
@@ -34,8 +35,9 @@ public TestProxiedEmulatedAndRemoteAssumedRoleRequests(
3435
TestingCredentialsRolesProvider credentialsController,
3536
@ForS3MockContainer S3Client storageClient,
3637
@ForS3MockContainer List<String> configuredBuckets,
37-
@ForTestingRemoteCredentials Credentials remoteCredentials)
38+
@ForTestingRemoteCredentials Credentials remoteCredentials,
39+
TrinoS3ProxyConfig trinoS3ProxyConfig)
3840
{
39-
super(buildClient(httpServer, remoteCredentials), testingCredentials, credentialsController, storageClient, configuredBuckets);
41+
super(buildClient(httpServer, remoteCredentials, trinoS3ProxyConfig.getS3Path(), trinoS3ProxyConfig.getStsPath()), testingCredentials, credentialsController, storageClient, configuredBuckets);
4042
}
4143
}

Diff for: trino-s3-proxy/src/test/java/io/trino/s3/proxy/server/TestProxiedRequests.java

+13-1
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,28 @@
1515

1616
import com.google.inject.Inject;
1717
import io.trino.s3.proxy.server.testing.ManagedS3MockContainer.ForS3MockContainer;
18+
import io.trino.s3.proxy.server.testing.TestingTrinoS3ProxyServer;
19+
import io.trino.s3.proxy.server.testing.harness.BuilderFilter;
1820
import io.trino.s3.proxy.server.testing.harness.TrinoS3ProxyTest;
1921
import io.trino.s3.proxy.server.testing.harness.TrinoS3ProxyTestCommonModules.WithConfiguredBuckets;
2022
import software.amazon.awssdk.services.s3.S3Client;
2123

2224
import java.util.List;
2325

24-
@TrinoS3ProxyTest(filters = WithConfiguredBuckets.class)
26+
@TrinoS3ProxyTest(filters = {WithConfiguredBuckets.class, TestProxiedRequests.Filter.class})
2527
public class TestProxiedRequests
2628
extends AbstractTestProxiedRequests
2729
{
30+
public static class Filter
31+
implements BuilderFilter
32+
{
33+
@Override
34+
public TestingTrinoS3ProxyServer.Builder filter(TestingTrinoS3ProxyServer.Builder builder)
35+
{
36+
return builder.withProperty("s3proxy.s3.path", "/api/some/s3/path");
37+
}
38+
}
39+
2840
@Inject
2941
public TestProxiedRequests(S3Client s3Client, @ForS3MockContainer S3Client storageClient, @ForS3MockContainer List<String> configuredBuckets)
3042
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package io.trino.s3.proxy.server;
15+
16+
import com.google.inject.Inject;
17+
import io.trino.s3.proxy.server.testing.ManagedS3MockContainer.ForS3MockContainer;
18+
import io.trino.s3.proxy.server.testing.TestingTrinoS3ProxyServer;
19+
import io.trino.s3.proxy.server.testing.harness.BuilderFilter;
20+
import io.trino.s3.proxy.server.testing.harness.TrinoS3ProxyTest;
21+
import io.trino.s3.proxy.server.testing.harness.TrinoS3ProxyTestCommonModules.WithConfiguredBuckets;
22+
import software.amazon.awssdk.services.s3.S3Client;
23+
24+
import java.util.List;
25+
26+
@TrinoS3ProxyTest(filters = {WithConfiguredBuckets.class, TestProxiedRequestsWithEmptyPath.Filter.class})
27+
public class TestProxiedRequestsWithEmptyPath
28+
extends AbstractTestProxiedRequests
29+
{
30+
public static class Filter
31+
implements BuilderFilter
32+
{
33+
@Override
34+
public TestingTrinoS3ProxyServer.Builder filter(TestingTrinoS3ProxyServer.Builder builder)
35+
{
36+
return builder.withProperty("s3proxy.s3.path", "");
37+
}
38+
}
39+
40+
@Inject
41+
public TestProxiedRequestsWithEmptyPath(S3Client s3Client, @ForS3MockContainer S3Client storageClient, @ForS3MockContainer List<String> configuredBuckets)
42+
{
43+
super(s3Client, storageClient, configuredBuckets);
44+
}
45+
}

0 commit comments

Comments
 (0)