1313
1414use std:: collections:: BTreeMap ;
1515
16+ use semver:: Version ;
1617use snafu:: { ResultExt , Snafu } ;
1718use stackable_operator:: {
1819 builder:: configmap:: ConfigMapBuilder ,
@@ -26,7 +27,7 @@ use crate::{
2627 controller:: {
2728 build:: {
2829 authentication:: generate_runtime_properties_config as generate_auth_runtime_properties,
29- jvm:: construct_jvm_args,
30+ jvm:: { AWS_REGION , construct_jvm_args} ,
3031 properties:: {
3132 ConfigFileName ,
3233 extensions:: get_extension_list,
@@ -51,6 +52,7 @@ const S3_ENDPOINT_URL: &str = "druid.s3.endpoint.url";
5152const S3_ACCESS_KEY : & str = "druid.s3.accessKey" ;
5253const S3_SECRET_KEY : & str = "druid.s3.secretKey" ;
5354const S3_PATH_STYLE_ACCESS : & str = "druid.s3.enablePathStyleAccess" ;
55+ const S3_USE_TRANSFER_MANAGER : & str = "druid.storage.transfer.useTransferManager" ;
5456const AUTH_AUTHORIZER_OPA_URI : & str = "druid.auth.authorizer.OpaAuthorizer.opaUri" ;
5557
5658#[ derive( Snafu , Debug ) ]
@@ -101,15 +103,30 @@ const INDEXER_JAVA_OPTS: &str = "druid.indexer.runner.javaOptsArray";
101103
102104/// The `druid.indexer.runner.javaOptsArray` entry that must be present in *every* rendered file
103105/// (runtime.properties and security.properties) for MiddleManagers.
104- fn middlemanager_indexer_java_opts ( ) -> ( String , String ) {
105- (
106- INDEXER_JAVA_OPTS . to_string ( ) ,
107- build_string_list ( & [
108- format ! ( "-Djavax.net.ssl.trustStore={STACKABLE_TRUST_STORE}" ) ,
109- format ! ( "-Djavax.net.ssl.trustStorePassword={STACKABLE_TRUST_STORE_PASSWORD}" ) ,
110- format ! ( "-Djavax.net.ssl.trustStoreType={STACKABLE_TRUST_STORE_TYPE}" ) ,
111- ] ) ,
112- )
106+ fn middlemanager_indexer_java_opts (
107+ s3_conn : Option < & s3:: v1alpha1:: ConnectionSpec > ,
108+ druid_version : & Result < Version , semver:: Error > ,
109+ ) -> ( String , String ) {
110+ let mut java_opts = vec ! [
111+ format!( "-Djavax.net.ssl.trustStore={STACKABLE_TRUST_STORE}" ) ,
112+ format!( "-Djavax.net.ssl.trustStorePassword={STACKABLE_TRUST_STORE_PASSWORD}" ) ,
113+ format!( "-Djavax.net.ssl.trustStoreType={STACKABLE_TRUST_STORE_TYPE}" ) ,
114+ ] ;
115+
116+ // TODO (@NickLarsenNZ): Remove the condition (keep the body) once we no longer support Druid
117+ // less than 37.0.0.
118+ // Druid >= 37.0.0 uses the AWS SDK v2, which requires a region to be set via the JVM system
119+ // property `aws.region` for the Peon processes spawned by the MiddleManager.
120+ if matches ! ( druid_version, Ok ( v) if * v >= Version :: new( 37 , 0 , 0 ) )
121+ && let Some ( s3) = s3_conn
122+ {
123+ java_opts. push ( format ! (
124+ "-D{AWS_REGION}={region_name}" ,
125+ region_name = s3. region. name
126+ ) ) ;
127+ }
128+
129+ ( INDEXER_JAVA_OPTS . to_string ( ) , build_string_list ( & java_opts) )
113130}
114131
115132/// Returns the user-supplied key/value overrides for the given config file from a
@@ -142,6 +159,8 @@ pub fn build_rolegroup_config_map(
142159 let opa_connstr = cluster_config. opa_connection_string . as_deref ( ) ;
143160 let s3_conn = cluster_config. s3_connection . as_ref ( ) ;
144161 let deep_storage_bucket_name = cluster_config. deep_storage_bucket_name . as_deref ( ) ;
162+ // TODO (@NickLarsenNZ): Remove this once we don't support Druid less than 37.0.0
163+ let druid_version = Version :: parse ( & cluster. image . product_version ) ;
145164
146165 let mut cm_conf_data = BTreeMap :: new ( ) ; // filename -> filecontent
147166 let metadata_database_connection_details = & cluster_config. metadata_db_connection ;
@@ -220,15 +239,39 @@ pub fn build_rolegroup_config_map(
220239 }
221240
222241 if let Some ( s3) = s3_conn {
223- if !s3. region . is_default_config ( ) {
224- // Raising this as warning instead of returning an error, better safe than sorry.
225- // It might still work out for the user.
226- tracing:: warn!(
227- region = ?s3. region,
228- "You configured a non-default region on the S3Connection.
229- The S3Connection region field is ignored because Druid uses the AWS SDK v1, which ignores the region if the endpoint is set. \
230- The host is a required field, therefore the endpoint will always be set."
231- )
242+ // TODO (@NickLarsenNZ): Remove the version condition once we no longer support Druid
243+ // less than 37.0.0.
244+ // Druid >= 37.0.0 uses the AWS SDK v2, which requires a region (set via the JVM system
245+ // property `-Daws.region` in the jvm.config section below). Older versions use the AWS
246+ // SDK v1, which ignores the region when an endpoint is set (and the endpoint is always
247+ // set because `host` is a required field on the S3Connection).
248+ match & druid_version {
249+ Ok ( v) if * v < Version :: new ( 37 , 0 , 0 ) => {
250+ if !s3. region . is_default_config ( ) {
251+ // Raising this as warning instead of returning an error, better safe than
252+ // sorry. It might still work out for the user.
253+ tracing:: warn!(
254+ region = ?s3. region,
255+ "You configured a non-default region on the S3Connection. \
256+ The S3Connection region field is ignored because this Druid version uses the AWS SDK v1, which ignores the region if the endpoint is set. \
257+ The host is a required field, therefore the endpoint will always be set."
258+ ) ;
259+ }
260+ }
261+ Err ( err) => {
262+ tracing:: warn!(
263+ %err,
264+ version = %cluster. image. product_version,
265+ "Failed to parse Druid product version, skipping S3 region configuration"
266+ ) ;
267+ }
268+ // For Druid >= 37.0.0 the region is set via the JVM system property `-Daws.region`
269+ // in the jvm.config section below.
270+ Ok ( _) => {
271+ // Disable the S3 Transfer Manager to avoid the AWS CRT async HTTP client, which
272+ // fails against non-AWS S3 endpoints.
273+ conf. insert ( S3_USE_TRANSFER_MANAGER . to_string ( ) , "false" . to_string ( ) ) ;
274+ }
232275 }
233276
234277 conf. insert (
@@ -271,7 +314,7 @@ pub fn build_rolegroup_config_map(
271314 cluster_config. opa_connection_string . is_some ( ) ,
272315 ) ) ;
273316 if * role == DruidRole :: MiddleManager {
274- let ( k, v) = middlemanager_indexer_java_opts ( ) ;
317+ let ( k, v) = middlemanager_indexer_java_opts ( s3_conn , & druid_version ) ;
275318 conf. insert ( k, v) ;
276319 }
277320 conf. extend ( runtime_properties:: defaults ( role) ) ;
@@ -300,6 +343,10 @@ pub fn build_rolegroup_config_map(
300343 & rg. product_specific_common_config . jvm_argument_overrides ,
301344 heap,
302345 direct,
346+ s3_conn,
347+ // `construct_jvm_args` takes the version by value and `semver::Error` is not `Clone`,
348+ // so re-parse here rather than cloning the `druid_version` used by reference above.
349+ Version :: parse ( & cluster. image . product_version ) ,
303350 )
304351 . context ( GetJvmConfigSnafu ) ?;
305352 cm_conf_data. insert ( ConfigFileName :: JvmConfig . to_string ( ) , jvm_config) ;
@@ -309,7 +356,7 @@ pub fn build_rolegroup_config_map(
309356 {
310357 let mut security_config: BTreeMap < String , String > = BTreeMap :: new ( ) ;
311358 if * role == DruidRole :: MiddleManager {
312- let ( k, v) = middlemanager_indexer_java_opts ( ) ;
359+ let ( k, v) = middlemanager_indexer_java_opts ( s3_conn , & druid_version ) ;
313360 security_config. insert ( k, v) ;
314361 }
315362 let security_overrides =
0 commit comments