This is an automated email from the ASF dual-hosted git repository.

yiguolei pushed a commit to branch branch-4.0
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.0 by this push:
     new ba1b0e39e32 branch-4.0: [feat](catalog)Support for Loading Catalog 
Credentials via AwsCredentialsProviderChain #58740 (#59055)
ba1b0e39e32 is described below

commit ba1b0e39e322dc551bbe7a9886ebb4cd1a2e9022
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Mon Dec 22 14:55:50 2025 +0800

    branch-4.0: [feat](catalog)Support for Loading Catalog Credentials via 
AwsCredentialsProviderChain #58740 (#59055)
    
    Cherry-picked from #58740
    
    Co-authored-by: Calvin Kirs <[email protected]>
---
 .../ConfigurationAWSCredentialsProvider.java       |  13 +-
 .../amazonaws/glue/catalog/util/AWSGlueConfig.java |   1 +
 .../common/AwsCredentialsProviderFactory.java      | 158 +++++++++++++++++++++
 .../common/AwsCredentialsProviderMode.java         |  74 ++++++++++
 .../metastore/AWSGlueMetaStoreBaseProperties.java  |  18 ++-
 .../metastore/HiveGlueMetaStoreProperties.java     |  11 ++
 .../metastore/IcebergGlueMetaStoreProperties.java  |   1 +
 .../datasource/property/storage/S3Properties.java  |  74 +++++-----
 .../property/storage/S3PropertiesTest.java         |  28 +++-
 fe/pom.xml                                         |   2 +-
 .../test_catalog_instance_profile.groovy           | 125 ++++++++++++++++
 11 files changed, 462 insertions(+), 43 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/com/amazonaws/glue/catalog/credentials/ConfigurationAWSCredentialsProvider.java
 
b/fe/fe-core/src/main/java/com/amazonaws/glue/catalog/credentials/ConfigurationAWSCredentialsProvider.java
index 49bea54c38e..23ba972fdfe 100644
--- 
a/fe/fe-core/src/main/java/com/amazonaws/glue/catalog/credentials/ConfigurationAWSCredentialsProvider.java
+++ 
b/fe/fe-core/src/main/java/com/amazonaws/glue/catalog/credentials/ConfigurationAWSCredentialsProvider.java
@@ -22,10 +22,12 @@ import com.amazonaws.auth.AWSCredentials;
 import com.amazonaws.auth.AWSCredentialsProvider;
 import com.amazonaws.auth.BasicAWSCredentials;
 import com.amazonaws.auth.BasicSessionCredentials;
-import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
 import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider;
 import com.amazonaws.glue.catalog.util.AWSGlueConfig;
 import com.amazonaws.util.StringUtils;
+import org.apache.doris.common.Config;
+import 
org.apache.doris.datasource.property.common.AwsCredentialsProviderFactory;
+import org.apache.doris.datasource.property.common.AwsCredentialsProviderMode;
 import org.apache.hadoop.conf.Configuration;
 
 public class ConfigurationAWSCredentialsProvider implements 
AWSCredentialsProvider {
@@ -47,9 +49,9 @@ public class ConfigurationAWSCredentialsProvider implements 
AWSCredentialsProvid
             return (StringUtils.isNullOrEmpty(sessionToken) ? new 
BasicAWSCredentials(accessKey,
                     secretKey) : new BasicSessionCredentials(accessKey, 
secretKey, sessionToken));
         }
-        
-        AWSCredentialsProvider longLivedProvider = new 
DefaultAWSCredentialsProviderChain();
-
+        String credentialsProviderModeString = 
StringUtils.lowerCase(conf.get(AWSGlueConfig.AWS_CREDENTIALS_PROVIDER_MODE));
+        AwsCredentialsProviderMode 
credentialsProviderMode=AwsCredentialsProviderMode.fromString(credentialsProviderModeString);
+        AWSCredentialsProvider longLivedProvider = 
AwsCredentialsProviderFactory.createV1(credentialsProviderMode);
         if (!StringUtils.isNullOrEmpty(roleArn)) {
             STSAssumeRoleSessionCredentialsProvider.Builder builder =
                     new 
STSAssumeRoleSessionCredentialsProvider.Builder(roleArn, "local-session")
@@ -61,6 +63,9 @@ public class ConfigurationAWSCredentialsProvider implements 
AWSCredentialsProvid
             STSAssumeRoleSessionCredentialsProvider provider = builder.build();
             return provider.getCredentials();
         }
+        if (Config.aws_credentials_provider_version.equalsIgnoreCase("v2")) {
+            return longLivedProvider.getCredentials();
+        }
         throw new SdkClientException("Unable to load AWS credentials from any 
provider in the chain");
 
     }
diff --git 
a/fe/fe-core/src/main/java/com/amazonaws/glue/catalog/util/AWSGlueConfig.java 
b/fe/fe-core/src/main/java/com/amazonaws/glue/catalog/util/AWSGlueConfig.java
index 4296eee0e94..7b0bd3ef979 100644
--- 
a/fe/fe-core/src/main/java/com/amazonaws/glue/catalog/util/AWSGlueConfig.java
+++ 
b/fe/fe-core/src/main/java/com/amazonaws/glue/catalog/util/AWSGlueConfig.java
@@ -63,4 +63,5 @@ public final class AWSGlueConfig {
     public static final String AWS_GLUE_SESSION_TOKEN = 
"aws.glue.session-token";
     public static final String AWS_GLUE_ROLE_ARN = "aws.glue.role-arn";
     public static final String AWS_GLUE_EXTERNAL_ID = "aws.glue.external-id";
+    public static final String AWS_CREDENTIALS_PROVIDER_MODE = 
"aws.credentials.provider.mode";
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/common/AwsCredentialsProviderFactory.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/common/AwsCredentialsProviderFactory.java
new file mode 100644
index 00000000000..f56aed0533e
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/common/AwsCredentialsProviderFactory.java
@@ -0,0 +1,158 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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.
+//
+// Copied from
+// 
https://github.com/awslabs/aws-glue-data-catalog-client-for-apache-hive-metastore/blob/branch-3.4.0/
+//
+
+package org.apache.doris.datasource.property.common;
+
+
+import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider;
+import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
+import software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain;
+import software.amazon.awssdk.auth.credentials.ContainerCredentialsProvider;
+import 
software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
+import 
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider;
+import 
software.amazon.awssdk.auth.credentials.SystemPropertyCredentialsProvider;
+import 
software.amazon.awssdk.auth.credentials.WebIdentityTokenFileCredentialsProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class AwsCredentialsProviderFactory {
+
+    private AwsCredentialsProviderFactory() {
+    }
+
+    /* =========================
+     * AWS SDK V1
+     * ========================= */
+
+    public static com.amazonaws.auth.AWSCredentialsProvider createV1(
+            AwsCredentialsProviderMode mode) {
+
+        switch (mode) {
+            case ENV:
+                return new 
com.amazonaws.auth.EnvironmentVariableCredentialsProvider();
+            case SYSTEM_PROPERTIES:
+                return new 
com.amazonaws.auth.SystemPropertiesCredentialsProvider();
+            case WEB_IDENTITY:
+                return 
com.amazonaws.auth.WebIdentityTokenCredentialsProvider.create();
+            case CONTAINER:
+                return new 
com.amazonaws.auth.EC2ContainerCredentialsProviderWrapper();
+            case ANONYMOUS:
+                throw new UnsupportedOperationException(
+                        "AWS SDK V1 does not support anonymous credentials 
provider.");
+            case INSTANCE_PROFILE:
+                return new 
com.amazonaws.auth.InstanceProfileCredentialsProvider();
+            case DEFAULT:
+                return createDefaultV1();
+            default:
+                throw new UnsupportedOperationException(
+                        "AWS SDK V1 does not support credentials provider 
mode: " + mode);
+        }
+    }
+
+    private static com.amazonaws.auth.AWSCredentialsProvider createDefaultV1() 
{
+        List<com.amazonaws.auth.AWSCredentialsProvider> providers = new 
ArrayList<>();
+        providers.add(new 
com.amazonaws.auth.InstanceProfileCredentialsProvider());
+        //lazy + env
+        
providers.add(com.amazonaws.auth.WebIdentityTokenCredentialsProvider.create());
+        providers.add(new 
com.amazonaws.auth.EC2ContainerCredentialsProviderWrapper());
+        providers.add(new 
com.amazonaws.auth.EnvironmentVariableCredentialsProvider());
+        providers.add(new 
com.amazonaws.auth.SystemPropertiesCredentialsProvider());
+        return new com.amazonaws.auth.AWSCredentialsProviderChain(
+                providers.toArray(new 
com.amazonaws.auth.AWSCredentialsProvider[0]));
+    }
+
+    /* =========================
+     * AWS SDK V2
+     * ========================= */
+
+    public static AwsCredentialsProvider createV2(
+            AwsCredentialsProviderMode mode,
+            boolean includeAnonymousInDefault) {
+        switch (mode) {
+            case ENV:
+                return EnvironmentVariableCredentialsProvider.create();
+            case SYSTEM_PROPERTIES:
+                return SystemPropertyCredentialsProvider.create();
+            case WEB_IDENTITY:
+                return WebIdentityTokenFileCredentialsProvider.create();
+            case CONTAINER:
+                return ContainerCredentialsProvider.create();
+            case INSTANCE_PROFILE:
+                return InstanceProfileCredentialsProvider.create();
+            case ANONYMOUS:
+                return AnonymousCredentialsProvider.create();
+            case DEFAULT:
+                return createDefaultV2(includeAnonymousInDefault);
+            default:
+                throw new UnsupportedOperationException(
+                        "AWS SDK V2 does not support credentials provider 
mode: " + mode);
+        }
+    }
+
+    private static AwsCredentialsProvider createDefaultV2(
+            boolean includeAnonymous) {
+
+        List<AwsCredentialsProvider> providers = new ArrayList<>();
+        providers.add(InstanceProfileCredentialsProvider.create());
+        providers.add(WebIdentityTokenFileCredentialsProvider.create());
+        providers.add(ContainerCredentialsProvider.create());
+        providers.add(EnvironmentVariableCredentialsProvider.create());
+        providers.add(SystemPropertyCredentialsProvider.create());
+        if (includeAnonymous) {
+            providers.add(AnonymousCredentialsProvider.create());
+        }
+        return AwsCredentialsProviderChain.builder()
+                .credentialsProviders(providers)
+                .build();
+    }
+
+    public static String getV2ClassName(AwsCredentialsProviderMode mode, 
boolean includeAnonymousInDefault) {
+        switch (mode) {
+            case ENV:
+                return EnvironmentVariableCredentialsProvider.class.getName();
+            case SYSTEM_PROPERTIES:
+                return SystemPropertyCredentialsProvider.class.getName();
+            case WEB_IDENTITY:
+                return WebIdentityTokenFileCredentialsProvider.class.getName();
+            case CONTAINER:
+                return ContainerCredentialsProvider.class.getName();
+            case INSTANCE_PROFILE:
+                return InstanceProfileCredentialsProvider.class.getName();
+            case ANONYMOUS:
+                return AnonymousCredentialsProvider.class.getName();
+            case DEFAULT:
+                List<String> providers = new ArrayList<>();
+                
providers.add(EnvironmentVariableCredentialsProvider.class.getName());
+                
providers.add(SystemPropertyCredentialsProvider.class.getName());
+                
providers.add(WebIdentityTokenFileCredentialsProvider.class.getName());
+                providers.add(ContainerCredentialsProvider.class.getName());
+                
providers.add(InstanceProfileCredentialsProvider.class.getName());
+                if (includeAnonymousInDefault) {
+                    
providers.add(AnonymousCredentialsProvider.class.getName());
+                }
+                return String.join("+", providers);
+            default:
+                throw new UnsupportedOperationException(
+                        "AWS SDK V2 does not support credentials provider 
mode: " + mode);
+        }
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/common/AwsCredentialsProviderMode.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/common/AwsCredentialsProviderMode.java
new file mode 100644
index 00000000000..63481ca5a7a
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/common/AwsCredentialsProviderMode.java
@@ -0,0 +1,74 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 org.apache.doris.datasource.property.common;
+
+public enum AwsCredentialsProviderMode {
+
+    DEFAULT("DEFAULT"),
+
+    ENV("ENV"),
+
+    SYSTEM_PROPERTIES("SYSTEM_PROPERTIES"),
+
+    WEB_IDENTITY("WEB_IDENTITY"),
+
+    CONTAINER("CONTAINER"),
+
+    INSTANCE_PROFILE("INSTANCE_PROFILE"),
+
+    ANONYMOUS("ANONYMOUS");
+
+    private final String mode;
+
+    AwsCredentialsProviderMode(String mode) {
+        this.mode = mode;
+    }
+
+    public String getMode() {
+        return mode;
+    }
+
+
+    public static AwsCredentialsProviderMode fromString(String value) {
+        if (value == null || value.isEmpty()) {
+            return DEFAULT;
+        }
+
+        String normalized = value.trim().toUpperCase().replace('-', '_');
+
+        switch (normalized) {
+            case "ENV":
+                return ENV;
+            case "SYSTEM_PROPERTIES":
+                return SYSTEM_PROPERTIES;
+            case "WEB_IDENTITY":
+                return WEB_IDENTITY;
+            case "CONTAINER":
+                return CONTAINER;
+            case "INSTANCE_PROFILE":
+                return INSTANCE_PROFILE;
+            case "ANONYMOUS":
+                return ANONYMOUS;
+            case "DEFAULT":
+                return DEFAULT;
+            default:
+                throw new IllegalArgumentException(
+                        "Unsupported AWS credentials provider mode: " + value);
+        }
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/AWSGlueMetaStoreBaseProperties.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/AWSGlueMetaStoreBaseProperties.java
index 445b8311f85..b17504fbca7 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/AWSGlueMetaStoreBaseProperties.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/AWSGlueMetaStoreBaseProperties.java
@@ -114,8 +114,6 @@ public class AWSGlueMetaStoreBaseProperties {
 
         return new ParamRules().requireTogether(new String[]{glueAccessKey, 
glueSecretKey},
                         "glue.access_key and glue.secret_key must be set 
together")
-                .requireAtLeastOne(new String[]{glueAccessKey, glueIAMRole},
-                        "At least one of glue.access_key or glue.role_arn must 
be set")
                 .require(glueEndpoint, "glue.endpoint must be set")
                 .check(() -> StringUtils.isNotBlank(glueEndpoint) && 
!glueEndpoint.startsWith("https://";),
                         "glue.endpoint must use https protocol,please set 
glue.endpoint to https://...";);
@@ -137,6 +135,22 @@ public class AWSGlueMetaStoreBaseProperties {
         }
     }
 
+    /**
+     * Validate that at least one Glue credential (an access key or an IAM 
role) is explicitly provided.
+     *
+     * Purpose: Some catalog implementations (for example, Iceberg) do not 
support obtaining credentials
+     * from the default credential chain (instance metadata, environment 
variables, etc.). In addition,
+     * the configuration or UI may only expose two options: {@code 
glue.access_key} and {@code glue.role_arn}.
+     * In such cases, at least one of these must be explicitly set. If neither 
is provided, an
+     * {@link IllegalArgumentException} is thrown to prompt the user to 
complete the configuration.
+     */
+    protected void requireExplicitGlueCredentials() {
+        if (StringUtils.isNotBlank(glueAccessKey) || 
StringUtils.isNotBlank(glueIAMRole)) {
+            return;
+        }
+        throw new IllegalArgumentException("At least one of glue.access_key or 
glue.role_arn must be set");
+    }
+
     private String extractRegionFromEndpoint(Matcher matcher) {
         for (int i = 1; i <= matcher.groupCount(); i++) {
             String group = matcher.group(i);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/HiveGlueMetaStoreProperties.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/HiveGlueMetaStoreProperties.java
index 5a90a3af7dc..06cd55b431b 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/HiveGlueMetaStoreProperties.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/HiveGlueMetaStoreProperties.java
@@ -18,6 +18,7 @@
 package org.apache.doris.datasource.property.metastore;
 
 import org.apache.doris.datasource.property.ConnectorProperty;
+import org.apache.doris.datasource.property.common.AwsCredentialsProviderMode;
 
 import com.amazonaws.ClientConfiguration;
 import com.amazonaws.glue.catalog.util.AWSGlueConfig;
@@ -75,6 +76,14 @@ public class HiveGlueMetaStoreProperties extends 
AbstractHiveProperties {
             description = "Catalog separator character for AWS Glue.")
     protected String awsGlueCatalogSeparator = "";
 
+    @ConnectorProperty(names = {"glue.credentials_provider_type"},
+            required = false,
+            description = "The credentials provider type of S3. "
+                    + "Options are: DEFAULT, ASSUME_ROLE, ANONYMOUS, 
ENVIRONMENT, SYSTEM_PROPERTIES, "
+                    + "WEB_IDENTITY_TOKEN_FILE, INSTANCE_PROFILE. "
+                    + "If not set, it will use the default provider chain of 
AWS SDK.")
+    protected String awsCredentialsProviderType = 
AwsCredentialsProviderMode.DEFAULT.name();
+
     // ========== Constructor ==========
 
     /**
@@ -115,6 +124,8 @@ public class HiveGlueMetaStoreProperties extends 
AbstractHiveProperties {
         setHiveConfPropertiesIfNotNull(hiveConf, 
AWSGlueConfig.AWS_GLUE_SESSION_TOKEN, baseProperties.glueSessionToken);
         setHiveConfPropertiesIfNotNull(hiveConf, 
AWSGlueConfig.AWS_GLUE_ROLE_ARN, baseProperties.glueIAMRole);
         setHiveConfPropertiesIfNotNull(hiveConf, 
AWSGlueConfig.AWS_GLUE_EXTERNAL_ID, baseProperties.glueExternalId);
+        setHiveConfPropertiesIfNotNull(hiveConf,
+                AWSGlueConfig.AWS_CREDENTIALS_PROVIDER_MODE, 
awsCredentialsProviderType);
     }
 
     private static void setHiveConfPropertiesIfNotNull(HiveConf hiveConf, 
String key, String value) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/IcebergGlueMetaStoreProperties.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/IcebergGlueMetaStoreProperties.java
index ba40940005b..3039a96ea9d 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/IcebergGlueMetaStoreProperties.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/IcebergGlueMetaStoreProperties.java
@@ -55,6 +55,7 @@ public class IcebergGlueMetaStoreProperties extends 
AbstractIcebergProperties {
     public void initNormalizeAndCheckProps() {
         super.initNormalizeAndCheckProps();
         glueProperties = AWSGlueMetaStoreBaseProperties.of(origProps);
+        glueProperties.requireExplicitGlueCredentials();
         s3Properties = S3Properties.of(origProps);
     }
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/S3Properties.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/S3Properties.java
index ffa6e6c7450..3452d759395 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/S3Properties.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/S3Properties.java
@@ -24,6 +24,8 @@ import org.apache.doris.common.Config;
 import org.apache.doris.common.DdlException;
 import org.apache.doris.datasource.property.ConnectorPropertiesUtils;
 import org.apache.doris.datasource.property.ConnectorProperty;
+import 
org.apache.doris.datasource.property.common.AwsCredentialsProviderFactory;
+import org.apache.doris.datasource.property.common.AwsCredentialsProviderMode;
 import org.apache.doris.thrift.TCredProviderType;
 import org.apache.doris.thrift.TS3StorageParam;
 
@@ -33,15 +35,11 @@ import com.google.common.collect.ImmutableSet;
 import lombok.Getter;
 import lombok.Setter;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider;
 import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
-import software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain;
-import software.amazon.awssdk.auth.credentials.ContainerCredentialsProvider;
-import 
software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
 import 
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider;
-import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
-import 
software.amazon.awssdk.auth.credentials.SystemPropertyCredentialsProvider;
-import 
software.amazon.awssdk.auth.credentials.WebIdentityTokenFileCredentialsProvider;
 import software.amazon.awssdk.regions.Region;
 import software.amazon.awssdk.services.sts.StsClient;
 import 
software.amazon.awssdk.services.sts.auth.StsAssumeRoleCredentialsProvider;
@@ -57,6 +55,8 @@ import java.util.stream.Stream;
 
 public class S3Properties extends AbstractS3CompatibleProperties {
 
+    private static final Logger LOG = LogManager.getLogger(S3Properties.class);
+
     public static final String USE_PATH_STYLE = "use_path_style";
 
     private static final String[] ENDPOINT_NAMES_FOR_GUESSING = {
@@ -170,6 +170,16 @@ public class S3Properties extends 
AbstractS3CompatibleProperties {
             description = "The external id of S3.")
     protected String s3ExternalId = "";
 
+    @ConnectorProperty(names = {"s3.credentials_provider_type", 
"glue.credentials_provider_type"},
+            required = false,
+            description = "The credentials provider type of S3. "
+                    + "Options are: DEFAULT, ASSUME_ROLE, ENVIRONMENT, 
SYSTEM_PROPERTIES, "
+                    + "WEB_IDENTITY_TOKEN_FILE, INSTANCE_PROFILE. "
+                    + "If not set, it will use the default provider chain of 
AWS SDK.")
+    protected String awsCredentialsProviderType = 
AwsCredentialsProviderMode.DEFAULT.name();
+
+    private AwsCredentialsProviderMode awsCredentialsProviderMode;
+
     public static S3Properties of(Map<String, String> properties) {
         S3Properties propertiesObj = new S3Properties(properties);
         ConnectorPropertiesUtils.bindConnectorProperties(propertiesObj, 
properties);
@@ -215,6 +225,7 @@ public class S3Properties extends 
AbstractS3CompatibleProperties {
             throw new IllegalArgumentException("s3.external_id must be used 
with s3.role_arn");
         }
         convertGlueToS3EndpointIfNeeded();
+        awsCredentialsProviderMode = 
AwsCredentialsProviderMode.fromString(awsCredentialsProviderType);
     }
 
     /**
@@ -310,15 +321,7 @@ public class S3Properties extends 
AbstractS3CompatibleProperties {
                     }).build();
         }
         // For anonymous access (no credentials required)
-        //fixme: should return AwsCredentialsProviderChain
-        if (StringUtils.isBlank(accessKey) && StringUtils.isBlank(secretKey)) {
-            return AnonymousCredentialsProvider.create();
-        }
-        return 
AwsCredentialsProviderChain.of(SystemPropertyCredentialsProvider.create(),
-                EnvironmentVariableCredentialsProvider.create(),
-                WebIdentityTokenFileCredentialsProvider.create(),
-                ProfileCredentialsProvider.create(),
-                InstanceProfileCredentialsProvider.create());
+        return AnonymousCredentialsProvider.create();
     }
 
     private AwsCredentialsProvider getAwsCredentialsProviderV2() {
@@ -329,15 +332,10 @@ public class S3Properties extends 
AbstractS3CompatibleProperties {
         if (StringUtils.isNotBlank(s3IAMRole)) {
             StsClient stsClient = StsClient.builder()
                     .region(Region.of(region))
-                    .credentialsProvider(AwsCredentialsProviderChain.of(
-                            WebIdentityTokenFileCredentialsProvider.create(),
-                            ContainerCredentialsProvider.create(),
-                            InstanceProfileCredentialsProvider.create(),
-                            SystemPropertyCredentialsProvider.create(),
-                            EnvironmentVariableCredentialsProvider.create(),
-                            ProfileCredentialsProvider.create()))
+                    
.credentialsProvider(AwsCredentialsProviderFactory.createV2(
+                            awsCredentialsProviderMode,
+                            false))
                     .build();
-
             return StsAssumeRoleCredentialsProvider.builder()
                     .stsClient(stsClient)
                     .refreshRequest(builder -> {
@@ -347,13 +345,9 @@ public class S3Properties extends 
AbstractS3CompatibleProperties {
                         }
                     }).build();
         }
-        return AwsCredentialsProviderChain.of(
-                WebIdentityTokenFileCredentialsProvider.create(),
-                ContainerCredentialsProvider.create(),
-                InstanceProfileCredentialsProvider.create(),
-                SystemPropertyCredentialsProvider.create(),
-                EnvironmentVariableCredentialsProvider.create(),
-                ProfileCredentialsProvider.create());
+        return AwsCredentialsProviderFactory.createV2(
+                awsCredentialsProviderMode,
+                true);
     }
 
     @Override
@@ -374,12 +368,26 @@ public class S3Properties extends 
AbstractS3CompatibleProperties {
             hadoopStorageConfig.set("fs.s3a.assumed.role.arn", s3IAMRole);
             hadoopStorageConfig.set("fs.s3a.aws.credentials.provider",
                     
"org.apache.hadoop.fs.s3a.auth.AssumedRoleCredentialProvider");
-            hadoopStorageConfig.set("fs.s3a.assumed.role.credentials.provider",
-                    
"org.apache.hadoop.fs.s3a.SimpleAWSCredentialsProvider,com.amazonaws.auth.EnvironmentVar"
-                            + 
"iableCredentialsProvider,com.amazonaws.auth.InstanceProfileCredentialsProvider");
+            if 
(Config.aws_credentials_provider_version.equalsIgnoreCase("v2")) {
+                
hadoopStorageConfig.set("fs.s3a.assumed.role.credentials.provider",
+                        AwsCredentialsProviderFactory.getV2ClassName(
+                                awsCredentialsProviderMode,
+                                false));
+            } else {
+                
hadoopStorageConfig.set("fs.s3a.assumed.role.credentials.provider",
+                        InstanceProfileCredentialsProvider.class.getName());
+            }
+
             if (StringUtils.isNotBlank(s3ExternalId)) {
                 hadoopStorageConfig.set("fs.s3a.assumed.role.external.id", 
s3ExternalId);
             }
+            return;
+        }
+        if (Config.aws_credentials_provider_version.equalsIgnoreCase("v2")) {
+            hadoopStorageConfig.set("fs.s3a.aws.credentials.provider",
+                    AwsCredentialsProviderFactory.createV2(
+                            awsCredentialsProviderMode,
+                            true).getClass().getName());
         }
     }
 
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/S3PropertiesTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/S3PropertiesTest.java
index 0c279df3368..29f9a126ece 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/S3PropertiesTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/S3PropertiesTest.java
@@ -223,9 +223,10 @@ public class S3PropertiesTest {
         Assertions.assertEquals("arn:aws:iam::123456789012:role/MyTestRole", 
backendProperties.get("AWS_ROLE_ARN"));
     }
 
+
     @Test
     public void testGetAwsCredentialsProviderWithIamRoleAndExternalId(@Mocked 
StsClientBuilder mockBuilder,
-                                                                      @Mocked 
StsClient mockStsClient, @Mocked InstanceProfileCredentialsProvider 
mockInstanceCreds) {
+                                                                      @Mocked 
StsClient mockStsClient) {
 
         new Expectations() {
             {
@@ -235,8 +236,6 @@ public class S3PropertiesTest {
                 result = mockBuilder;
                 mockBuilder.build();
                 result = mockStsClient;
-                InstanceProfileCredentialsProvider.create();
-                result = mockInstanceCreds;
             }
         };
 
@@ -248,6 +247,29 @@ public class S3PropertiesTest {
         AwsCredentialsProvider provider = s3Props.getAwsCredentialsProvider();
         Assertions.assertNotNull(provider);
         Assertions.assertTrue(provider instanceof 
StsAssumeRoleCredentialsProvider);
+        origProps.put("s3.credentials_provider_type", "instance_profile");
+        s3Props = (S3Properties) StorageProperties.createPrimary(origProps);
+        provider = s3Props.getAwsCredentialsProvider();
+        
Assertions.assertEquals(StsAssumeRoleCredentialsProvider.class.getName(), 
provider.getClass().getName());
+        
Assertions.assertEquals(InstanceProfileCredentialsProvider.class.getName(), 
s3Props.getHadoopStorageConfig().get("fs.s3a.assumed.role.credentials.provider"));
+        origProps.remove("s3.external_id");
+        origProps.remove("s3.role_arn");
+        origProps.remove("s3.credentials_provider_type");
+        s3Props = (S3Properties) StorageProperties.createPrimary(origProps);
+        provider = s3Props.getAwsCredentialsProvider();
+        Assertions.assertNotNull(provider);
+        Assertions.assertTrue(provider instanceof AwsCredentialsProviderChain);
+        origProps.put("s3.credentials_provider_type", "instance_profile");
+        s3Props = (S3Properties) StorageProperties.createPrimary(origProps);
+        provider = s3Props.getAwsCredentialsProvider();
+        Assertions.assertNotNull(provider);
+        Assertions.assertTrue(provider instanceof 
InstanceProfileCredentialsProvider);
+        
Assertions.assertEquals(InstanceProfileCredentialsProvider.class.getName(), 
s3Props.getHadoopStorageConfig().get("fs.s3a.aws.credentials.provider"));
+        origProps.put("s3.credentials_provider_type", "static");
+        ExceptionChecker.expectThrowsWithMsg(IllegalArgumentException.class, 
"Unsupported AWS credentials provider mode: static", () -> 
StorageProperties.createPrimary(origProps));
+        origProps.put("s3.credentials_provider_type", "anonymous");
+        Assertions.assertDoesNotThrow(() -> 
StorageProperties.createPrimary(origProps));
+
     }
 
     @Test
diff --git a/fe/pom.xml b/fe/pom.xml
index 5cc39c501a3..278f6c39673 100644
--- a/fe/pom.xml
+++ b/fe/pom.xml
@@ -230,7 +230,7 @@ under the License.
         <gcs.version>3.1.9</gcs.version>
         <obs.dependency.scope>compile</obs.dependency.scope>
         <cos.dependency.scope>compile</cos.dependency.scope>
-        <gcs.dependency.scope>compile</gcs.dependency.scope>
+        <gcs.dependency.scope>provided</gcs.dependency.scope>
         <qcloud-java-sdk.version>2.0.1</qcloud-java-sdk.version>
         <tencentcos.version>8.2.7</tencentcos.version>
         <cos-api.version>5.6.211</cos-api.version>
diff --git 
a/regression-test/suites/aws_iam_role_p0/test_catalog_instance_profile.groovy 
b/regression-test/suites/aws_iam_role_p0/test_catalog_instance_profile.groovy
new file mode 100644
index 00000000000..f8877929b96
--- /dev/null
+++ 
b/regression-test/suites/aws_iam_role_p0/test_catalog_instance_profile.groovy
@@ -0,0 +1,125 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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.
+
+import com.google.common.base.Strings;
+
+suite("test_catalog_instance_profile_with_role") {
+
+    if 
(Strings.isNullOrEmpty(context.config.otherConfigs.get("hiveGlueInstanceProfileQueryTableName")))
 {
+        return
+    }
+    
+    String hiveGlueQueryTableName = 
context.config.otherConfigs.get("hiveGlueInstanceProfileQueryTableName")
+    String hiveGlueExpectCounts = 
context.config.otherConfigs.get("hiveGlueInstanceProfileExpectCounts")
+    String icebergFsQueryTableName = 
context.config.otherConfigs.get("icebergFsInstanceProfileQueryTableName")
+    String icebergFsExpectCounts = 
context.config.otherConfigs.get("icebergFsInstanceProfileExpectCounts")
+    String icebergFsWarehouse = 
context.config.otherConfigs.get("icebergFsInstanceProfileWarehouse")
+    String region = context.config.otherConfigs.get("awsInstanceProfileRegion")
+    //query method
+    def createCatalogAndQuery = { catalogProps, catalogName, queryTableName, 
expectCounts ->
+        sql """drop catalog if exists ${catalogName}"""
+        sql """
+            ${catalogProps}
+        """
+        def result = sql """
+         select count(1) from ${catalogName}.${queryTableName};
+        """
+        println("result: ${result}")
+        def countValue = result[0][0]
+        assertTrue(countValue == expectCounts.toInteger())
+        sql """drop catalog if exists ${catalogName}"""
+    }
+    def assertCatalogAndQueryException = { catalogProps, catalogName, errMsg ->
+        sql """drop catalog if exists ${catalogName}"""
+        sql """
+            ${catalogProps}
+        """
+        try {
+            sql """
+         switch ${catalogName};
+        """
+            sql """
+                show databases;
+               """
+            throw new Exception("Expected exception was not thrown")
+        }catch (Exception e){
+            assertTrue(e.getMessage().contains(errMsg))
+        }
+    }
+    String hiveGlueCatalogProps = """
+        create catalog hive_glue_catalog properties(
+            "type"="hms",
+            "hive.metastore.type"="glue",
+            "glue.region"="${region}",
+            "glue.endpoint" = "https://glue.${region}.amazonaws.com";
+        );
+    """
+    createCatalogAndQuery(hiveGlueCatalogProps, "hive_glue_catalog", 
hiveGlueQueryTableName, hiveGlueExpectCounts)
+    hiveGlueCatalogProps = """
+        create catalog hive_glue_catalog properties(
+            "type"="hms",
+            "hive.metastore.type"="glue",
+            "glue.credentials_provider_type"="INSTANCE_PROFILE",
+            "glue.region"="${region}",
+            "glue.endpoint" = "https://glue.${region}.amazonaws.com";
+        );
+    """
+    createCatalogAndQuery(hiveGlueCatalogProps, "hive_glue_catalog", 
hiveGlueQueryTableName, hiveGlueExpectCounts)
+    hiveGlueCatalogProps = """
+        create catalog hive_glue_catalog properties(
+            "type"="hms",
+            "hive.metastore.type"="glue",
+            "glue.credentials_provider_type"="CONTAINER",
+            "glue.region"="${region}",
+            "glue.endpoint" = "https://glue.${region}.amazonaws.com";
+        );
+    """
+    assertCatalogAndQueryException(hiveGlueCatalogProps,"hive_glue_catalog", 
"The environment variable AWS_CONTAINER_CREDENTIALS_RELATIVE_URI")
+    String icebergFsCatalogProps = """
+        create catalog iceberg_fs_catalog properties(
+            "type"="iceberg",
+            "warehouse"="${icebergFsWarehouse}",
+            "iceberg.catalog.type"="hadoop",
+            "s3.region" = "${region}",
+            "s3.endpoint" = "https://s3.${region}.amazonaws.com";
+        );
+    """
+    createCatalogAndQuery(icebergFsCatalogProps, "iceberg_fs_catalog", 
icebergFsQueryTableName, icebergFsExpectCounts)
+    icebergFsCatalogProps = """
+        create catalog iceberg_fs_catalog properties(
+            "type"="iceberg",
+            "warehouse"="${icebergFsWarehouse}",
+            "iceberg.catalog.type"="hadoop",
+            "s3.credentials_provider_type"="INSTANCE_PROFILE",
+            "s3.region" = "${region}",
+            "s3.endpoint" = "https://s3.${region}.amazonaws.com";
+        );
+    """
+    createCatalogAndQuery(icebergFsCatalogProps, "iceberg_fs_catalog", 
icebergFsQueryTableName, icebergFsExpectCounts)
+    icebergFsCatalogProps = """
+        create catalog iceberg_fs_catalog properties(
+            "type"="iceberg",
+            "warehouse"="${icebergFsWarehouse}",
+            "iceberg.catalog.type"="hadoop",
+            "s3.credentials_provider_type"="CONTAINER",
+            "s3.region" = "${region}",
+            "s3.endpoint" = "https://s3.${region}.amazonaws.com";
+        );
+    """
+    assertCatalogAndQueryException(icebergFsCatalogProps,"iceberg_fs_catalog", 
"No AWS Credentials provided by ContainerCredentialsProvider")
+    
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to