CloudObjectStoreAdapter.java
// 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.storage;
import org.apache.doris.cloud.proto.Cloud;
import org.apache.doris.cloud.proto.Cloud.CredProviderTypePB;
import org.apache.doris.cloud.proto.Cloud.ObjectStoreInfoPB.Provider;
import org.apache.doris.datasource.property.common.AwsCredentialsProviderMode;
import org.apache.doris.filesystem.properties.S3CompatibleFileSystemProperties;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import java.util.Map;
/**
* fe-core-only cloud meta-service glue: builds {@link Cloud.ObjectStoreInfoPB} from the
* persisted user-facing {@code s3.*} property map. Behaviourally identical to the legacy
* {@code S3Properties.getObjStoreInfoPB}; this is a WIRE CONTRACT with the cloud meta-service
* (master plan §2.7-③), so every builder field is locked by a golden test against the legacy
* implementation until Phase D deletes it.
*
* <p>IMPORTANT: this reads the USER key namespace ({@code s3.endpoint}, {@code s3.access_key},
* ...) — do NOT feed it a backend {@code AWS_*} map.</p>
*/
public final class CloudObjectStoreAdapter {
private static final String ENDPOINT = S3CompatibleFileSystemProperties.PROP_ENDPOINT;
private static final String REGION = S3CompatibleFileSystemProperties.PROP_REGION;
private static final String ACCESS_KEY = S3CompatibleFileSystemProperties.PROP_ACCESS_KEY;
private static final String SECRET_KEY = S3CompatibleFileSystemProperties.PROP_SECRET_KEY;
private static final String ROOT_PATH = S3CompatibleFileSystemProperties.PROP_ROOT_PATH;
private static final String BUCKET = S3CompatibleFileSystemProperties.PROP_BUCKET;
private static final String EXTERNAL_ENDPOINT = S3CompatibleFileSystemProperties.PROP_EXTERNAL_ENDPOINT;
private static final String ROLE_ARN = S3CompatibleFileSystemProperties.PROP_ROLE_ARN;
private static final String EXTERNAL_ID = S3CompatibleFileSystemProperties.PROP_EXTERNAL_ID;
private static final String CREDENTIALS_PROVIDER_TYPE =
S3CompatibleFileSystemProperties.PROP_CREDENTIALS_PROVIDER_TYPE;
private static final String ENV_CREDENTIALS_PROVIDER_TYPE = "AWS_CREDENTIALS_PROVIDER_TYPE";
private static final String USE_PATH_STYLE = S3CompatibleFileSystemProperties.PROP_USE_PATH_STYLE;
private static final String FS_PROVIDER_KEY = S3CompatibleFileSystemProperties.PROP_PROVIDER;
private CloudObjectStoreAdapter() {
}
/** Direct move of legacy {@code S3Properties.getObjStoreInfoPB}. */
public static Cloud.ObjectStoreInfoPB.Builder getObjStoreInfoPB(Map<String, String> properties) {
Cloud.ObjectStoreInfoPB.Builder builder = Cloud.ObjectStoreInfoPB.newBuilder();
if (properties.containsKey(ENDPOINT)) {
builder.setEndpoint(properties.get(ENDPOINT));
}
if (properties.containsKey(REGION)) {
builder.setRegion(properties.get(REGION));
}
if (properties.containsKey(ACCESS_KEY)) {
builder.setAk(properties.get(ACCESS_KEY));
}
if (properties.containsKey(SECRET_KEY)) {
builder.setSk(properties.get(SECRET_KEY));
}
if (properties.containsKey(ROOT_PATH)) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(properties.get(ROOT_PATH)),
"%s cannot be empty", ROOT_PATH);
builder.setPrefix(properties.get(ROOT_PATH));
}
if (properties.containsKey(BUCKET)) {
builder.setBucket(properties.get(BUCKET));
}
if (properties.containsKey(EXTERNAL_ENDPOINT)) {
builder.setExternalEndpoint(properties.get(EXTERNAL_ENDPOINT));
}
if (properties.containsKey(FS_PROVIDER_KEY)) {
// S3 Provider properties should be case insensitive.
builder.setProvider(Provider.valueOf(properties.get(FS_PROVIDER_KEY).toUpperCase()));
}
if (properties.containsKey(USE_PATH_STYLE)) {
String value = properties.get(USE_PATH_STYLE);
Preconditions.checkArgument(!Strings.isNullOrEmpty(value), "use_path_style cannot be empty");
Preconditions.checkArgument(value.equalsIgnoreCase("true")
|| value.equalsIgnoreCase("false"),
"Invalid use_path_style value: %s only 'true' or 'false' is acceptable", value);
builder.setUsePathStyle(value.equalsIgnoreCase("true"));
}
if (hasCredentialsProviderType(properties)) {
builder.setCredProviderType(getCredProviderTypePB(properties));
}
if (properties.containsKey(ROLE_ARN)) {
String roleArn = properties.get(ROLE_ARN);
if (!Strings.isNullOrEmpty(roleArn)) {
builder.setRoleArn(roleArn);
}
if (!Strings.isNullOrEmpty(roleArn) && properties.containsKey(EXTERNAL_ID)) {
builder.setExternalId(properties.get(EXTERNAL_ID));
}
if (!Strings.isNullOrEmpty(roleArn) && !builder.hasCredProviderType()) {
builder.setCredProviderType(getCredProviderTypePB(properties));
}
}
return builder;
}
private static boolean hasCredentialsProviderType(Map<String, String> properties) {
return properties.containsKey(CREDENTIALS_PROVIDER_TYPE)
|| properties.containsKey(ENV_CREDENTIALS_PROVIDER_TYPE);
}
private static CredProviderTypePB getCredProviderTypePB(Map<String, String> properties) {
AwsCredentialsProviderMode mode = S3ThriftAdapter.getCredentialsProviderMode(properties,
AwsCredentialsProviderMode.INSTANCE_PROFILE);
switch (mode) {
case DEFAULT:
return CredProviderTypePB.DEFAULT;
case ENV:
return CredProviderTypePB.ENV;
case SYSTEM_PROPERTIES:
return CredProviderTypePB.SYSTEM_PROPERTIES;
case WEB_IDENTITY:
return CredProviderTypePB.WEB_IDENTITY;
case CONTAINER:
return CredProviderTypePB.CONTAINER;
case INSTANCE_PROFILE:
return CredProviderTypePB.INSTANCE_PROFILE;
case ANONYMOUS:
return CredProviderTypePB.ANONYMOUS;
default:
throw new IllegalArgumentException("Unsupported AWS credentials provider mode: " + mode);
}
}
}