Skip to content

Commit

Permalink
Make RemoteS3Facade a plugin
Browse files Browse the repository at this point in the history
This allows implementations to provide their custom implementation of
RemoteS3Facade, e.g. to support multiple S3 endpoints based on
bucket / path.

The existing RemoteS3Facade implementation is named "default" and
provided by default.
  • Loading branch information
Laonel committed Nov 19, 2024
1 parent 8176dff commit b2e58fb
Show file tree
Hide file tree
Showing 22 changed files with 223 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
import io.trino.aws.proxy.spi.plugin.config.AssumedRoleProviderConfig;
import io.trino.aws.proxy.spi.plugin.config.CredentialsProviderConfig;
import io.trino.aws.proxy.spi.plugin.config.PluginIdentifierConfig;
import io.trino.aws.proxy.spi.plugin.config.RemoteS3Config;
import io.trino.aws.proxy.spi.plugin.config.S3RequestRewriterConfig;
import io.trino.aws.proxy.spi.plugin.config.S3SecurityFacadeProviderConfig;
import io.trino.aws.proxy.spi.remote.RemoteS3Facade;
import io.trino.aws.proxy.spi.rest.S3RequestRewriter;
import io.trino.aws.proxy.spi.security.S3SecurityFacadeProvider;

Expand Down Expand Up @@ -57,6 +59,11 @@ static Module s3RequestRewriterModule(String identifier, Class<? extends S3Reque
return optionalPluginModule(S3RequestRewriterConfig.class, identifier, S3RequestRewriter.class, implementationClass, module);
}

static Module remoteS3Module(String identifier, Class<? extends RemoteS3Facade> implementationClass, Module module)
{
return optionalPluginModule(RemoteS3Config.class, identifier, RemoteS3Facade.class, implementationClass, module);
}

static <T extends Identity> void bindIdentityType(Binder binder, Class<T> type)
{
newOptionalBinder(binder, new TypeLiteral<Class<? extends Identity>>() {}).setBinding().toProvider(() -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Licensed 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 io.trino.aws.proxy.spi.plugin.config;

import io.airlift.configuration.Config;
import jakarta.validation.constraints.NotNull;

import java.util.Optional;

public class RemoteS3Config
implements PluginIdentifierConfig
{
private Optional<String> identifier = Optional.of("default");

@NotNull
@Override
public Optional<String> getPluginIdentifier()
{
return identifier;
}

@Config("remote-s3.type")
public void setPluginIdentifier(String identifier)
{
this.identifier = Optional.of(identifier);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.trino.aws.proxy.server.remote;
package io.trino.aws.proxy.spi.remote;

import jakarta.ws.rs.core.UriBuilder;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
import io.trino.aws.proxy.server.credentials.JsonIdentityProvider;
import io.trino.aws.proxy.server.credentials.file.FileBasedCredentialsModule;
import io.trino.aws.proxy.server.credentials.http.HttpCredentialsModule;
import io.trino.aws.proxy.server.remote.RemoteS3Module;
import io.trino.aws.proxy.server.remote.DefaultRemoteS3Facade;
import io.trino.aws.proxy.server.remote.DefaultRemoteS3Module;
import io.trino.aws.proxy.server.rest.LimitStreamController;
import io.trino.aws.proxy.server.rest.ParamProvider;
import io.trino.aws.proxy.server.rest.RequestLoggerController;
Expand All @@ -57,6 +58,7 @@
import io.trino.aws.proxy.spi.plugin.TrinoAwsProxyServerPlugin;
import io.trino.aws.proxy.spi.plugin.config.AssumedRoleProviderConfig;
import io.trino.aws.proxy.spi.plugin.config.CredentialsProviderConfig;
import io.trino.aws.proxy.spi.plugin.config.RemoteS3Config;
import io.trino.aws.proxy.spi.plugin.config.S3RequestRewriterConfig;
import io.trino.aws.proxy.spi.plugin.config.S3SecurityFacadeProviderConfig;
import io.trino.aws.proxy.spi.rest.S3RequestRewriter;
Expand All @@ -74,6 +76,8 @@
import static io.airlift.http.client.HttpClientBinder.httpClientBinder;
import static io.airlift.http.server.HttpServerBinder.httpServerBinder;
import static io.airlift.jaxrs.JaxrsBinder.jaxrsBinder;
import static io.trino.aws.proxy.server.remote.DefaultRemoteS3Module.DEFAULT_REMOTE_S3_IDENTIFIER;
import static io.trino.aws.proxy.spi.plugin.TrinoAwsProxyServerBinding.remoteS3Module;

public class TrinoAwsProxyServerModule
extends AbstractConfigurationAwareModule
Expand Down Expand Up @@ -147,8 +151,13 @@ protected void setup(Binder binder)
return AssumedRoleProvider.NOOP;
});

// RemoteS3 binder
configBinder(binder).bindConfig(RemoteS3Config.class);

// RemoteS3 provided implementations
install(remoteS3Module(DEFAULT_REMOTE_S3_IDENTIFIER, DefaultRemoteS3Facade.class, new DefaultRemoteS3Module()));

installSigningController(binder);
installRemoteS3Facade(binder);
installS3SecurityController(binder);

installPlugins();
Expand All @@ -167,12 +176,6 @@ public XmlMapper newXmlMapper()
return xmlMapper;
}

@VisibleForTesting
protected void installRemoteS3Facade(Binder binder)
{
install(new RemoteS3Module());
}

@VisibleForTesting
protected void installS3SecurityController(Binder binder)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@

import com.google.inject.Inject;
import io.airlift.log.Logger;
import io.trino.aws.proxy.server.remote.RemoteS3Facade;
import io.trino.aws.proxy.spi.credentials.Credential;
import io.trino.aws.proxy.spi.credentials.Credentials;
import io.trino.aws.proxy.spi.credentials.CredentialsProvider;
import io.trino.aws.proxy.spi.remote.RemoteS3Facade;
import io.trino.aws.proxy.spi.remote.RemoteSessionRole;
import jakarta.annotation.PreDestroy;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

import java.util.Optional;

public class RemoteS3Config
public class DefaultRemoteS3Config
{
private boolean https = true;
private String domain = "amazonaws.com";
Expand All @@ -29,35 +29,35 @@ public class RemoteS3Config
private String hostnameTemplate = "${bucket}.s3.${region}.${domain}";

@Config("remoteS3.https")
public RemoteS3Config setHttps(boolean https)
public DefaultRemoteS3Config setHttps(boolean https)
{
this.https = https;
return this;
}

@Config("remoteS3.domain")
public RemoteS3Config setDomain(String s3Domain)
public DefaultRemoteS3Config setDomain(String s3Domain)
{
this.domain = s3Domain;
return this;
}

@Config("remoteS3.port")
public RemoteS3Config setPort(Integer port)
public DefaultRemoteS3Config setPort(Integer port)
{
this.port = Optional.ofNullable(port);
return this;
}

@Config("remoteS3.virtual-host-style")
public RemoteS3Config setVirtualHostStyle(boolean virtualHostStyle)
public DefaultRemoteS3Config setVirtualHostStyle(boolean virtualHostStyle)
{
this.virtualHostStyle = virtualHostStyle;
return this;
}

@Config("remoteS3.hostname.template")
public RemoteS3Config setHostnameTemplate(String hostnameTemplate)
public DefaultRemoteS3Config setHostnameTemplate(String hostnameTemplate)
{
this.hostnameTemplate = hostnameTemplate;
return this;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Licensed 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 io.trino.aws.proxy.server.remote;

import com.google.inject.Inject;
import io.trino.aws.proxy.server.remote.DefaultRemoteS3Module.ForDefaultRemoteS3Facade;
import io.trino.aws.proxy.spi.remote.RemoteS3Facade;
import jakarta.ws.rs.core.UriBuilder;

import java.net.URI;

import static java.util.Objects.requireNonNull;

public class DefaultRemoteS3Facade
implements RemoteS3Facade
{
private final RemoteS3Facade delegate;

@Inject
public DefaultRemoteS3Facade(@ForDefaultRemoteS3Facade RemoteS3Facade delegate)
{
this.delegate = requireNonNull(delegate, "delegate is null");
}

@Override
public URI buildEndpoint(UriBuilder uriBuilder, String path, String bucket, String region)
{
return delegate.buildEndpoint(uriBuilder, path, bucket, region);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Licensed 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 io.trino.aws.proxy.server.remote;

import com.google.inject.Binder;
import com.google.inject.BindingAnnotation;
import com.google.inject.Key;
import com.google.inject.Scopes;
import io.airlift.configuration.AbstractConfigurationAwareModule;
import io.trino.aws.proxy.spi.remote.RemoteS3Facade;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static io.airlift.configuration.ConditionalModule.conditionalModule;
import static io.airlift.configuration.ConfigBinder.configBinder;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

public class DefaultRemoteS3Module
extends AbstractConfigurationAwareModule
{
public static final String DEFAULT_REMOTE_S3_IDENTIFIER = "default";

@Override
protected void setup(Binder binder)
{
configBinder(binder).bindConfig(DefaultRemoteS3Config.class);
install(conditionalModule(
DefaultRemoteS3Config.class,
DefaultRemoteS3Config::getVirtualHostStyle,
innerBinder -> innerBinder.bind(Key.get(RemoteS3Facade.class, ForDefaultRemoteS3Facade.class))
.to(VirtualHostStyleDefaultRemoteS3Facade.class).in(Scopes.SINGLETON),
innerBinder -> innerBinder.bind(Key.get(RemoteS3Facade.class, ForDefaultRemoteS3Facade.class))
.to(PathStyleDefaultRemoteS3Facade.class).in(Scopes.SINGLETON)));
}

@Retention(RUNTIME)
@Target({FIELD, PARAMETER, METHOD})
@BindingAnnotation
public @interface ForDefaultRemoteS3Facade {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,28 @@
package io.trino.aws.proxy.server.remote;

import com.google.inject.Inject;
import io.trino.aws.proxy.spi.remote.RemoteS3Facade;
import jakarta.ws.rs.core.UriBuilder;

import java.net.URI;
import java.util.Optional;

import static java.util.Objects.requireNonNull;

public class PathStyleRemoteS3Facade
public class PathStyleDefaultRemoteS3Facade
implements RemoteS3Facade
{
private final RemoteS3HostBuilder hostBuilder;
private final boolean https;
private final Optional<Integer> port;

@Inject
public PathStyleRemoteS3Facade(RemoteS3Config remoteS3Config)
public PathStyleDefaultRemoteS3Facade(DefaultRemoteS3Config remoteS3Config)
{
this((bucket, region) -> RemoteS3HostBuilder.getHostName(bucket, region, remoteS3Config.getDomain(), remoteS3Config.getHostnameTemplate()), remoteS3Config.getHttps(), remoteS3Config.getPort());
}

public PathStyleRemoteS3Facade(RemoteS3HostBuilder hostBuilder, boolean https, Optional<Integer> port)
public PathStyleDefaultRemoteS3Facade(RemoteS3HostBuilder hostBuilder, boolean https, Optional<Integer> port)
{
this.hostBuilder = requireNonNull(hostBuilder, "hostBuilder is null");
this.port = requireNonNull(port, "port is null");
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,28 @@
package io.trino.aws.proxy.server.remote;

import com.google.inject.Inject;
import io.trino.aws.proxy.spi.remote.RemoteS3Facade;
import jakarta.ws.rs.core.UriBuilder;

import java.net.URI;
import java.util.Optional;

import static java.util.Objects.requireNonNull;

public class VirtualHostStyleRemoteS3Facade
public class VirtualHostStyleDefaultRemoteS3Facade
implements RemoteS3Facade
{
private final RemoteS3HostBuilder hostBuilder;
private final boolean https;
private final Optional<Integer> port;

@Inject
public VirtualHostStyleRemoteS3Facade(RemoteS3Config remoteS3Config)
public VirtualHostStyleDefaultRemoteS3Facade(DefaultRemoteS3Config remoteS3Config)
{
this((bucket, region) -> RemoteS3HostBuilder.getHostName(bucket, region, remoteS3Config.getDomain(), remoteS3Config.getHostnameTemplate()), remoteS3Config.getHttps(), remoteS3Config.getPort());
}

public VirtualHostStyleRemoteS3Facade(RemoteS3HostBuilder hostBuilder, boolean https, Optional<Integer> port)
public VirtualHostStyleDefaultRemoteS3Facade(RemoteS3HostBuilder hostBuilder, boolean https, Optional<Integer> port)
{
this.hostBuilder = requireNonNull(hostBuilder, "hostBuilder is null");
this.port = requireNonNull(port, "port is null");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
import io.airlift.http.client.Request;
import io.airlift.log.Logger;
import io.trino.aws.proxy.server.TrinoAwsProxyConfig;
import io.trino.aws.proxy.server.remote.RemoteS3Facade;
import io.trino.aws.proxy.server.security.S3SecurityController;
import io.trino.aws.proxy.spi.credentials.Credentials;
import io.trino.aws.proxy.spi.remote.RemoteS3Facade;
import io.trino.aws.proxy.spi.rest.ParsedS3Request;
import io.trino.aws.proxy.spi.rest.RequestContent;
import io.trino.aws.proxy.spi.rest.S3RequestRewriter;
Expand Down
Loading

0 comments on commit b2e58fb

Please sign in to comment.