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

yuxia pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fluss.git


The following commit(s) were added to refs/heads/main by this push:
     new 2b1cce5a [filesystem] Add HuaweiCloud OBS integration (#1274)
2b1cce5a is described below

commit 2b1cce5aea6fe21907860cc8768fa43bd446a3a2
Author: andybj0228 <[email protected]>
AuthorDate: Wed Jul 9 14:04:56 2025 +0800

    [filesystem] Add HuaweiCloud OBS integration (#1274)
---
 fluss-dist/pom.xml                                 |   7 +
 fluss-dist/src/main/assemblies/plugins.xml         |   6 +
 fluss-filesystems/fluss-fs-obs/pom.xml             | 259 +++++++++++++++++++++
 .../com/alibaba/fluss/fs/obs/OBSFileSystem.java    |  64 +++++
 .../alibaba/fluss/fs/obs/OBSFileSystemPlugin.java  | 133 +++++++++++
 .../DynamicTemporaryOBSCredentialsProvider.java    |  59 +++++
 .../fs/obs/token/OBSSecurityTokenProvider.java     | 111 +++++++++
 .../fs/obs/token/OBSSecurityTokenReceiver.java     | 108 +++++++++
 .../src/main/resources/META-INF/NOTICE             |  73 ++++++
 .../resources/META-INF/licenses/LICENSE.jacoco     |  14 ++
 .../main/resources/META-INF/licenses/LICENSE.jaxb  | 135 +++++++++++
 .../main/resources/META-INF/licenses/LICENSE.jdom  |  51 ++++
 .../services/com.alibaba.fluss.fs.FileSystemPlugin |  17 ++
 ...om.alibaba.fluss.fs.token.SecurityTokenReceiver |  17 ++
 .../fluss/fs/obs/OBSFileSystemBehaviorITCase.java  |  64 +++++
 .../alibaba/fluss/fs/obs/OBSTestCredentials.java   | 118 ++++++++++
 ...redentialsProviderFileSystemBehaviorITCase.java |  65 ++++++
 .../OBSWithTokenFileSystemBehaviorBaseITCase.java  |  38 +++
 .../obs/OBSWithTokenFileSystemBehaviorITCase.java  |  72 ++++++
 .../org.junit.jupiter.api.extension.Extension      |  19 ++
 fluss-filesystems/pom.xml                          |   1 +
 fluss-test-coverage/pom.xml                        |   1 +
 website/docs/maintenance/filesystems/obs.md        |  62 +++++
 website/docs/maintenance/filesystems/overview.md   |   2 +
 24 files changed, 1496 insertions(+)

diff --git a/fluss-dist/pom.xml b/fluss-dist/pom.xml
index 6a040abb..c9a710a0 100644
--- a/fluss-dist/pom.xml
+++ b/fluss-dist/pom.xml
@@ -68,6 +68,13 @@
             <scope>provided</scope>
         </dependency>
 
+        <dependency>
+            <groupId>com.alibaba.fluss</groupId>
+            <artifactId>fluss-fs-obs</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
         <!-- metrics plugin -->
         <dependency>
             <groupId>com.alibaba.fluss</groupId>
diff --git a/fluss-dist/src/main/assemblies/plugins.xml 
b/fluss-dist/src/main/assemblies/plugins.xml
index eee9b4f7..b610e5e2 100644
--- a/fluss-dist/src/main/assemblies/plugins.xml
+++ b/fluss-dist/src/main/assemblies/plugins.xml
@@ -45,6 +45,12 @@
     <files>
         <!-- filesystem -->
         <!-- output directory should correspond to the file system *schema* 
name, i.e., plugins/<schema>/  -->
+        <file>
+            
<source>../fluss-filesystems/fluss-fs-obs/target/fluss-fs-obs-${project.version}.jar</source>
+            <outputDirectory>plugins/obs/</outputDirectory>
+            <destName>fluss-fs-obs-${project.version}.jar</destName>
+            <fileMode>0644</fileMode>
+        </file>
         <file>
             
<source>../fluss-filesystems/fluss-fs-gs/target/fluss-fs-gs-${project.version}.jar</source>
             <outputDirectory>plugins/gs/</outputDirectory>
diff --git a/fluss-filesystems/fluss-fs-obs/pom.xml 
b/fluss-filesystems/fluss-fs-obs/pom.xml
new file mode 100644
index 00000000..b184532e
--- /dev/null
+++ b/fluss-filesystems/fluss-fs-obs/pom.xml
@@ -0,0 +1,259 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.alibaba.fluss</groupId>
+        <artifactId>fluss-filesystems</artifactId>
+        <version>0.8-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>fluss-fs-obs</artifactId>
+    <name>Fluss : FileSystems : OBS FS</name>
+
+    <properties>
+        <fs.obs.sdk.version>3.24.12</fs.obs.sdk.version>
+        <huaweicloud.sdk.iam.version>3.1.87</huaweicloud.sdk.iam.version>
+        <hadoop.huaweicloud.version>3.4.0</hadoop.huaweicloud.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.alibaba.fluss</groupId>
+            <artifactId>fluss-common</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.fluss</groupId>
+            <artifactId>fluss-fs-hadoop-shaded</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.fluss</groupId>
+            <artifactId>fluss-fs-hadoop</artifactId>
+            <version>${project.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.hadoop</groupId>
+                    <artifactId>hadoop-hdfs-client</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.hadoop</groupId>
+            <artifactId>hadoop-huaweicloud</artifactId>
+            <version>${hadoop.huaweicloud.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.huaweicloud</groupId>
+                    <artifactId>esdk-obs-java-bundle</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.hadoop</groupId>
+                    <artifactId>hadoop-common</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>ch.qos.reload4j</groupId>
+                    <artifactId>reload4j</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-reload4j</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>com.huaweicloud</groupId>
+            <artifactId>esdk-obs-java</artifactId>
+            <version>${fs.obs.sdk.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>javax.xml.bind</groupId>
+                    <artifactId>jaxb-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>com.huaweicloud.sdk</groupId>
+            <artifactId>huaweicloud-sdk-iam</artifactId>
+            <version>${huaweicloud.sdk.iam.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.bouncycastle</groupId>
+                    <artifactId>bcprov-jdk18on</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.openeuler</groupId>
+                    <artifactId>bgmprovider</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <!-- Hadoop requires jaxb-api for javax.xml.bind.JAXBException -->
+            <groupId>javax.xml.bind</groupId>
+            <artifactId>jaxb-api</artifactId>
+            <version>${jaxb.api.version}</version>
+            <!-- packaged as an optional dependency that is only accessible on 
Java 11+ -->
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.fluss</groupId>
+            <artifactId>fluss-test-utils</artifactId>
+        </dependency>
+        <!-- for the behavior test suite -->
+        <dependency>
+            <groupId>com.alibaba.fluss</groupId>
+            <artifactId>fluss-common</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <archive>
+                        <manifestEntries>
+                            <!-- jaxb-api is packaged as an optional 
dependency that is only accessible on Java 11 -->
+                            <Multi-Release>true</Multi-Release>
+                        </manifestEntries>
+                    </archive>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>copy-javax-jars</id>
+                        <phase>process-resources</phase>
+                        <goals>
+                            <goal>copy</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <artifactItems>
+                        <artifactItem>
+                            <groupId>javax.xml.bind</groupId>
+                            <artifactId>jaxb-api</artifactId>
+                            <version>${jaxb.api.version}</version>
+                            <type>jar</type>
+                            <overWrite>true</overWrite>
+                        </artifactItem>
+                    </artifactItems>
+                    
<outputDirectory>${project.build.directory}/temporary</outputDirectory>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-antrun-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>unpack-javax-libraries</id>
+                        <phase>process-resources</phase>
+                        <goals>
+                            <goal>run</goal>
+                        </goals>
+                        <configuration>
+                            <target>
+                                <echo message="unpacking javax jars"/>
+                                <unzip 
dest="${project.build.directory}/classes/META-INF/versions/11">
+                                    <fileset 
dir="${project.build.directory}/temporary">
+                                        <include name="*"/>
+                                    </fileset>
+                                </unzip>
+                            </target>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>shade-fluss</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <artifactSet>
+                                <includes>
+                                    <include>*:*</include>
+                                </includes>
+                                <excludes>
+                                    
<exclude>javax.servlet:servlet-api</exclude>
+                                    <exclude>xmlenc:xmlenc</exclude>
+                                </excludes>
+                            </artifactSet>
+                            <filters>
+                                <filter>
+                                    <artifact>*</artifact>
+                                    <excludes>
+                                        <exclude>.gitkeep</exclude>
+                                        <exclude>mime.types</exclude>
+                                        <exclude>mozilla/**</exclude>
+                                        <exclude>LICENSE.txt</exclude>
+                                        <exclude>license/LICENSE*</exclude>
+                                        
<exclude>okhttp3/internal/publicsuffix/NOTICE</exclude>
+                                        <exclude>NOTICE</exclude>
+                                    </excludes>
+                                </filter>
+                            </filters>
+                            <relocations>
+                                <relocation>
+                                    <pattern>org.apache.commons</pattern>
+                                    
<shadedPattern>com.alibaba.fluss.shaded.org.apache.commons</shadedPattern>
+                                </relocation>
+                            </relocations>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project> 
\ No newline at end of file
diff --git 
a/fluss-filesystems/fluss-fs-obs/src/main/java/com/alibaba/fluss/fs/obs/OBSFileSystem.java
 
b/fluss-filesystems/fluss-fs-obs/src/main/java/com/alibaba/fluss/fs/obs/OBSFileSystem.java
new file mode 100644
index 00000000..c6d4f1b8
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-obs/src/main/java/com/alibaba/fluss/fs/obs/OBSFileSystem.java
@@ -0,0 +1,64 @@
+/*
+ * 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 com.alibaba.fluss.fs.obs;
+
+import com.alibaba.fluss.fs.hdfs.HadoopFileSystem;
+import com.alibaba.fluss.fs.obs.token.OBSSecurityTokenProvider;
+import com.alibaba.fluss.fs.token.ObtainedSecurityToken;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileSystem;
+
+import java.io.IOException;
+
+/**
+ * A {@link FileSystem} for HuaweiCloud OBS that wraps an {@link 
HadoopFileSystem}, but overwrite
+ * method to generate access security token.
+ */
+class OBSFileSystem extends HadoopFileSystem {
+
+    private final Configuration conf;
+    private volatile OBSSecurityTokenProvider obsSecurityTokenProvider;
+    private final String scheme;
+
+    OBSFileSystem(FileSystem hadoopFileSystem, String scheme, Configuration 
conf) {
+        super(hadoopFileSystem);
+        this.scheme = scheme;
+        this.conf = conf;
+    }
+
+    @Override
+    public ObtainedSecurityToken obtainSecurityToken() throws IOException {
+        try {
+            mayCreateSecurityTokenProvider();
+            return obsSecurityTokenProvider.obtainSecurityToken(scheme);
+        } catch (Exception e) {
+            throw new IOException(e);
+        }
+    }
+
+    private void mayCreateSecurityTokenProvider() throws IOException {
+        if (obsSecurityTokenProvider == null) {
+            synchronized (this) {
+                if (obsSecurityTokenProvider == null) {
+                    obsSecurityTokenProvider = new 
OBSSecurityTokenProvider(conf);
+                }
+            }
+        }
+    }
+}
diff --git 
a/fluss-filesystems/fluss-fs-obs/src/main/java/com/alibaba/fluss/fs/obs/OBSFileSystemPlugin.java
 
b/fluss-filesystems/fluss-fs-obs/src/main/java/com/alibaba/fluss/fs/obs/OBSFileSystemPlugin.java
new file mode 100644
index 00000000..c23e12e9
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-obs/src/main/java/com/alibaba/fluss/fs/obs/OBSFileSystemPlugin.java
@@ -0,0 +1,133 @@
+/*
+ * 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 com.alibaba.fluss.fs.obs;
+
+import com.alibaba.fluss.annotation.VisibleForTesting;
+import com.alibaba.fluss.config.ConfigBuilder;
+import com.alibaba.fluss.config.Configuration;
+import com.alibaba.fluss.fs.FileSystem;
+import com.alibaba.fluss.fs.FileSystemPlugin;
+import com.alibaba.fluss.fs.obs.token.OBSSecurityTokenReceiver;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.URI;
+
+/** Simple factory for the HuaweiCloud OBS file system. */
+public class OBSFileSystemPlugin implements FileSystemPlugin {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(OBSFileSystemPlugin.class);
+
+    public static final String SCHEME = "obs";
+
+    /**
+     * In order to simplify, we make fluss obs configuration keys same with 
hadoop obs module. So,
+     * we add all configuration key with prefix `fs.obs` in fluss conf to 
hadoop conf
+     */
+    private static final String[] FLUSS_CONFIG_PREFIXES = {"fs.obs."};
+
+    private static final String ACCESS_KEY_ID = "fs.obs.access.key";
+    public static final String CREDENTIALS_PROVIDER = 
"fs.obs.security.provider";
+
+    public static final String REGION_KEY = "fs.obs.region";
+
+    @Override
+    public String getScheme() {
+        return SCHEME;
+    }
+
+    @Override
+    public FileSystem create(URI fsUri, Configuration flussConfig) throws 
IOException {
+        org.apache.hadoop.conf.Configuration hadoopConfig = 
getHadoopConfiguration(flussConfig);
+
+        // set credential provider
+        if (hadoopConfig.get(ACCESS_KEY_ID) == null) {
+            String credentialsProvider = 
hadoopConfig.get(CREDENTIALS_PROVIDER);
+            if (credentialsProvider != null) {
+                LOG.info(
+                        "{} is not set, but {} is set, using credential 
provider {}.",
+                        ACCESS_KEY_ID,
+                        CREDENTIALS_PROVIDER,
+                        credentialsProvider);
+            } else {
+                // no ak, no credentialsProvider,
+                // set default credential provider which will get token from
+                // OBSSecurityTokenReceiver
+                setDefaultCredentialProvider(hadoopConfig);
+            }
+        } else {
+            LOG.info("{} is set, using provided access key id and secret.", 
ACCESS_KEY_ID);
+        }
+
+        final String scheme = fsUri.getScheme();
+        final String authority = fsUri.getAuthority();
+
+        if (scheme == null && authority == null) {
+            fsUri = 
org.apache.hadoop.fs.FileSystem.getDefaultUri(hadoopConfig);
+        } else if (scheme != null && authority == null) {
+            URI defaultUri = 
org.apache.hadoop.fs.FileSystem.getDefaultUri(hadoopConfig);
+            if (scheme.equals(defaultUri.getScheme()) && 
defaultUri.getAuthority() != null) {
+                fsUri = defaultUri;
+            }
+        }
+
+        org.apache.hadoop.fs.FileSystem fileSystem = initFileSystem(fsUri, 
hadoopConfig);
+        return new OBSFileSystem(fileSystem, getScheme(), hadoopConfig);
+    }
+
+    protected org.apache.hadoop.fs.FileSystem initFileSystem(
+            URI fsUri, org.apache.hadoop.conf.Configuration hadoopConfig) 
throws IOException {
+        org.apache.hadoop.fs.obs.OBSFileSystem fileSystem =
+                new org.apache.hadoop.fs.obs.OBSFileSystem();
+        fileSystem.initialize(fsUri, hadoopConfig);
+        return fileSystem;
+    }
+
+    protected void 
setDefaultCredentialProvider(org.apache.hadoop.conf.Configuration hadoopConfig) 
{
+        // use OBSSecurityTokenReceiver to update hadoop config to set 
credentialsProvider
+        OBSSecurityTokenReceiver.updateHadoopConfig(hadoopConfig);
+    }
+
+    @VisibleForTesting
+    org.apache.hadoop.conf.Configuration getHadoopConfiguration(Configuration 
flussConfig) {
+        org.apache.hadoop.conf.Configuration conf = new 
org.apache.hadoop.conf.Configuration();
+        if (flussConfig == null) {
+            return conf;
+        }
+
+        // read all configuration with prefix 'FLUSS_CONFIG_PREFIXES'
+        for (String key : flussConfig.keySet()) {
+            for (String prefix : FLUSS_CONFIG_PREFIXES) {
+                if (key.startsWith(prefix)) {
+                    String value =
+                            flussConfig.getString(
+                                    
ConfigBuilder.key(key).stringType().noDefaultValue(), null);
+                    conf.set(key, value);
+
+                    LOG.debug(
+                            "Adding Fluss config entry for {} as {} to Hadoop 
config",
+                            key,
+                            conf.get(key));
+                }
+            }
+        }
+        return conf;
+    }
+}
diff --git 
a/fluss-filesystems/fluss-fs-obs/src/main/java/com/alibaba/fluss/fs/obs/token/DynamicTemporaryOBSCredentialsProvider.java
 
b/fluss-filesystems/fluss-fs-obs/src/main/java/com/alibaba/fluss/fs/obs/token/DynamicTemporaryOBSCredentialsProvider.java
new file mode 100644
index 00000000..f690c384
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-obs/src/main/java/com/alibaba/fluss/fs/obs/token/DynamicTemporaryOBSCredentialsProvider.java
@@ -0,0 +1,59 @@
+/*
+ * 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 com.alibaba.fluss.fs.obs.token;
+
+import com.alibaba.fluss.annotation.Internal;
+
+import com.obs.services.IObsCredentialsProvider;
+import com.obs.services.internal.security.BasicSecurityKey;
+import com.obs.services.model.ISecurityKey;
+import org.apache.hadoop.fs.obs.OBSFileSystem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Support dynamic session credentials for authenticating with HuaweiCloud 
OBS. It'll get
+ * credentials from {@link OBSSecurityTokenReceiver}. It implements obs native 
{@link
+ * IObsCredentialsProvider} to work with {@link OBSFileSystem}.
+ */
+@Internal
+public class DynamicTemporaryOBSCredentialsProvider implements 
IObsCredentialsProvider {
+
+    private static final Logger LOG =
+            
LoggerFactory.getLogger(DynamicTemporaryOBSCredentialsProvider.class);
+
+    public static final String NAME = 
DynamicTemporaryOBSCredentialsProvider.class.getName();
+
+    @Override
+    public void setSecurityKey(ISecurityKey iSecurityKey) {
+        // do nothing
+    }
+
+    @Override
+    public ISecurityKey getSecurityKey() {
+        ISecurityKey credentials = OBSSecurityTokenReceiver.getCredentials();
+        if (credentials == null) {
+            throw new RuntimeException("Credentials is not ready.");
+        }
+        LOG.debug("Providing session credentials");
+        return new BasicSecurityKey(
+                credentials.getAccessKey(),
+                credentials.getSecretKey(),
+                credentials.getSecurityToken());
+    }
+}
diff --git 
a/fluss-filesystems/fluss-fs-obs/src/main/java/com/alibaba/fluss/fs/obs/token/OBSSecurityTokenProvider.java
 
b/fluss-filesystems/fluss-fs-obs/src/main/java/com/alibaba/fluss/fs/obs/token/OBSSecurityTokenProvider.java
new file mode 100644
index 00000000..48b2724d
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-obs/src/main/java/com/alibaba/fluss/fs/obs/token/OBSSecurityTokenProvider.java
@@ -0,0 +1,111 @@
+/*
+ * 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 com.alibaba.fluss.fs.obs.token;
+
+import com.alibaba.fluss.fs.token.Credentials;
+import com.alibaba.fluss.fs.token.CredentialsJsonSerde;
+import com.alibaba.fluss.fs.token.ObtainedSecurityToken;
+
+import com.huaweicloud.sdk.core.auth.GlobalCredentials;
+import com.huaweicloud.sdk.core.auth.ICredential;
+import com.huaweicloud.sdk.iam.v3.IamClient;
+import com.huaweicloud.sdk.iam.v3.model.CreateTemporaryAccessKeyByTokenRequest;
+import 
com.huaweicloud.sdk.iam.v3.model.CreateTemporaryAccessKeyByTokenRequestBody;
+import 
com.huaweicloud.sdk.iam.v3.model.CreateTemporaryAccessKeyByTokenResponse;
+import com.huaweicloud.sdk.iam.v3.model.Credential;
+import com.huaweicloud.sdk.iam.v3.model.IdentityToken;
+import com.huaweicloud.sdk.iam.v3.model.TokenAuth;
+import com.huaweicloud.sdk.iam.v3.model.TokenAuthIdentity;
+import com.huaweicloud.sdk.iam.v3.region.IamRegion;
+import org.apache.hadoop.conf.Configuration;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.alibaba.fluss.fs.obs.OBSFileSystemPlugin.REGION_KEY;
+
+/** A provider to provide HuaweiCloud obs security token. */
+public class OBSSecurityTokenProvider {
+
+    private static final String ACCESS_KEY_ID = "fs.obs.access.key";
+    private static final String ACCESS_KEY_SECRET = "fs.obs.secret.key";
+    private static final String ENDPOINT_KEY = "fs.obs.endpoint";
+
+    private final String endpoint;
+    private final String region;
+    private final IamClient iamClient;
+
+    public OBSSecurityTokenProvider(Configuration conf) {
+        endpoint = conf.get(ENDPOINT_KEY);
+        String accessKeyId = conf.get(ACCESS_KEY_ID);
+        String accessKeySecret = conf.get(ACCESS_KEY_SECRET);
+        region = conf.get(REGION_KEY);
+        // Create IAM client
+        ICredential credentials =
+                new 
GlobalCredentials().withAk(accessKeyId).withSk(accessKeySecret);
+
+        iamClient =
+                IamClient.newBuilder()
+                        .withCredential(credentials)
+                        .withRegion(IamRegion.valueOf(region))
+                        .build();
+    }
+
+    public ObtainedSecurityToken obtainSecurityToken(String scheme) {
+        final CreateTemporaryAccessKeyByTokenRequest request =
+                new CreateTemporaryAccessKeyByTokenRequest();
+        CreateTemporaryAccessKeyByTokenRequestBody body =
+                new CreateTemporaryAccessKeyByTokenRequestBody();
+        // todo: may consider make token duration time configurable, we don't 
set it now
+        // token duration time is 1 hour by default
+        IdentityToken tokenIdentity = new IdentityToken();
+        tokenIdentity.withDurationSeconds(3600);
+        List<TokenAuthIdentity.MethodsEnum> listIdentityMethods = new 
ArrayList<>();
+        
listIdentityMethods.add(TokenAuthIdentity.MethodsEnum.fromValue("token"));
+        TokenAuthIdentity identityAuth = new TokenAuthIdentity();
+        identityAuth.withMethods(listIdentityMethods).withToken(tokenIdentity);
+        TokenAuth authbody = new TokenAuth();
+        authbody.withIdentity(identityAuth);
+        body.withAuth(authbody);
+        request.withBody(body);
+        final CreateTemporaryAccessKeyByTokenResponse response =
+                iamClient.createTemporaryAccessKeyByToken(request);
+        Credential credential = response.getCredential();
+        Map<String, String> additionInfo = new HashMap<>();
+        // we need to put endpoint as addition info
+        additionInfo.put(ENDPOINT_KEY, endpoint);
+        additionInfo.put(REGION_KEY, region);
+        return new ObtainedSecurityToken(
+                scheme,
+                toJson(credential),
+                Instant.parse(credential.getExpiresAt()).toEpochMilli(),
+                additionInfo);
+    }
+
+    private byte[] toJson(Credential credential) {
+        Credentials credentials =
+                new Credentials(
+                        credential.getAccess(),
+                        credential.getSecret(),
+                        credential.getSecuritytoken());
+        return CredentialsJsonSerde.toJson(credentials);
+    }
+}
diff --git 
a/fluss-filesystems/fluss-fs-obs/src/main/java/com/alibaba/fluss/fs/obs/token/OBSSecurityTokenReceiver.java
 
b/fluss-filesystems/fluss-fs-obs/src/main/java/com/alibaba/fluss/fs/obs/token/OBSSecurityTokenReceiver.java
new file mode 100644
index 00000000..8a56e50e
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-obs/src/main/java/com/alibaba/fluss/fs/obs/token/OBSSecurityTokenReceiver.java
@@ -0,0 +1,108 @@
+/*
+ * 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 com.alibaba.fluss.fs.obs.token;
+
+import com.alibaba.fluss.fs.obs.OBSFileSystemPlugin;
+import com.alibaba.fluss.fs.token.CredentialsJsonSerde;
+import com.alibaba.fluss.fs.token.ObtainedSecurityToken;
+import com.alibaba.fluss.fs.token.SecurityTokenReceiver;
+
+import com.obs.services.internal.security.BasicSecurityKey;
+import com.obs.services.model.ISecurityKey;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+
+import static 
com.alibaba.fluss.fs.obs.OBSFileSystemPlugin.CREDENTIALS_PROVIDER;
+
+/** Security token receiver for HuaweiCloud OBS filesystem. */
+public class OBSSecurityTokenReceiver implements SecurityTokenReceiver {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(OBSSecurityTokenReceiver.class);
+
+    static volatile ISecurityKey credentials;
+    static volatile Map<String, String> additionInfos;
+
+    public static void updateHadoopConfig(org.apache.hadoop.conf.Configuration 
hadoopConfig) {
+        updateHadoopConfig(hadoopConfig, 
DynamicTemporaryOBSCredentialsProvider.NAME);
+    }
+
+    protected static void updateHadoopConfig(
+            org.apache.hadoop.conf.Configuration hadoopConfig, String 
credentialsProviderName) {
+        LOG.info("Updating Hadoop configuration");
+
+        String providers = hadoopConfig.get(CREDENTIALS_PROVIDER, "");
+
+        if (!providers.contains(credentialsProviderName)) {
+            if (providers.isEmpty()) {
+                LOG.debug("Setting provider");
+                providers = credentialsProviderName;
+            } else {
+                providers = credentialsProviderName + "," + providers;
+                LOG.debug("Prepending provider, new providers value: {}", 
providers);
+            }
+            hadoopConfig.set(CREDENTIALS_PROVIDER, providers);
+        } else {
+            LOG.debug("Provider already exists");
+        }
+
+        // then, set addition info
+        if (additionInfos == null) {
+            // if addition info is null, it also means we have not received 
any token,
+            throw new RuntimeException("Credentials is not ready.");
+        } else {
+            for (Map.Entry<String, String> entry : additionInfos.entrySet()) {
+                hadoopConfig.set(entry.getKey(), entry.getValue());
+            }
+        }
+
+        LOG.info("Updated Hadoop configuration successfully");
+    }
+
+    @Override
+    public String scheme() {
+        return OBSFileSystemPlugin.SCHEME;
+    }
+
+    @Override
+    public void onNewTokensObtained(ObtainedSecurityToken token) {
+        LOG.info("Updating session credentials");
+
+        byte[] tokenBytes = token.getToken();
+
+        com.alibaba.fluss.fs.token.Credentials flussCredentials =
+                CredentialsJsonSerde.fromJson(tokenBytes);
+
+        // Create Credential from fluss credentials
+        credentials =
+                new BasicSecurityKey(
+                        flussCredentials.getAccessKeyId(),
+                        flussCredentials.getSecretAccessKey(),
+                        flussCredentials.getSecurityToken());
+        additionInfos = token.getAdditionInfos();
+
+        LOG.info(
+                "Session credentials updated successfully with access key: 
{}.",
+                credentials.getAccessKey());
+    }
+
+    public static ISecurityKey getCredentials() {
+        return credentials;
+    }
+}
diff --git a/fluss-filesystems/fluss-fs-obs/src/main/resources/META-INF/NOTICE 
b/fluss-filesystems/fluss-fs-obs/src/main/resources/META-INF/NOTICE
new file mode 100644
index 00000000..767907e9
--- /dev/null
+++ b/fluss-filesystems/fluss-fs-obs/src/main/resources/META-INF/NOTICE
@@ -0,0 +1,73 @@
+fluss-fs-obs
+Copyright 2025 The Apache Software Foundation
+
+This project includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+This project bundles the following dependencies under the Apache Software 
License 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
+
+- com.huaweicloud:esdk-obs-java:3.24.12
+- com.huaweicloud.sdk:huaweicloud-sdk-iam:3.1.87
+- com.fasterxml.woodstox:woodstox-core:5.3.0
+- com.google.guava:guava:11.0.2
+- com.jamesmurty.utils:java-xmlbuilder:0.4
+- commons-beanutils:commons-beanutils:1.9.4
+- commons-collections:commons-collections:3.2.2
+- commons-configuration:commons-configuration:1.6
+- commons-digester:commons-digester:1.8
+- commons-io:commons-io:2.5
+- commons-lang:commons-lang:2.6
+- commons-logging:commons-logging:1.1.3
+- org.apache.hadoop:hadoop-huaweicloud:3.4.0
+- org.apache.httpcomponents:httpclient:4.5.13
+- org.apache.httpcomponents:httpcore:4.4.13
+- org.codehaus.jettison:jettison:1.5.4
+- org.ini4j:ini4j:0.5.4
+- net.java.dev.jets3t:jets3t:0.9.0
+- org.apache.commons:commons-compress:1.21
+- org.apache.commons:commons-lang3:3.12.0
+- org.apache.directory.api:api-asn1-api:1.0.0-M20
+- org.apache.directory.api:api-util:1.0.0-M20
+- org.apache.directory.server:apacheds-i18n:2.0.0-M15
+- org.apache.directory.server:apacheds-kerberos-codec:2.0.0-M15
+- org.apache.hadoop:hadoop-annotations:2.10.2
+- org.apache.hadoop:hadoop-auth:2.10.2
+- org.apache.hadoop:hadoop-common:2.10.2
+- org.apache.htrace:htrace-core4:4.1.0-incubating
+- org.codehaus.jackson:jackson-core-asl:1.9.13
+- org.codehaus.jackson:jackson-mapper-asl:1.9.13
+- org.codehaus.woodstox:stax2-api:4.2.1
+- org.mortbay.jetty:jetty-sslengine:6.1.26
+- org.mortbay.jetty:jetty-util:6.1.26
+- org.mortbay.jetty:jetty:6.1.26
+- com.fasterxml.jackson.core:jackson-annotations:2.15.3
+- com.fasterxml.jackson.core:jackson-core:2.15.3
+- com.fasterxml.jackson.core:jackson-databind:2.15.3
+- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.15.3
+- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.3
+- com.huaweicloud.sdk:huaweicloud-sdk-core:3.1.87
+- com.squareup.okhttp3:okhttp:4.12.0
+- com.squareup.okio:okio-jvm:3.8.0
+- com.squareup.okio:okio:3.8.0
+- org.apache.httpcomponents:httpcore:4.1.2
+- org.jetbrains.kotlin:kotlin-stdlib-common:1.8.21
+- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.21
+- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.21
+- org.jetbrains.kotlin:kotlin-stdlib:1.8.21
+- org.jetbrains:annotations:13.0
+- org.yaml:snakeyaml:2.0
+
+The binary distribution of this product bundles these dependencies under the 
Eclipse Public License - v 2.0 
(https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt)
+See bundled license files for details.
+
+- org.jacoco:org.jacoco.agent:runtime:0.8.5
+
+This project bundles the following dependencies under the JDOM license.
+See bundled license files for details.
+
+- org.jdom:jdom2:2.0.6.1
+
+This project bundles the following dependencies under the CDDL 1.1 license.
+See bundled license files for details.
+
+- javax.xml.bind:jaxb-api:2.3.1
diff --git 
a/fluss-filesystems/fluss-fs-obs/src/main/resources/META-INF/licenses/LICENSE.jacoco
 
b/fluss-filesystems/fluss-fs-obs/src/main/resources/META-INF/licenses/LICENSE.jacoco
new file mode 100644
index 00000000..47ff775b
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-obs/src/main/resources/META-INF/licenses/LICENSE.jacoco
@@ -0,0 +1,14 @@
+License
+=======
+
+Copyright (c) 2009, 2019 Mountainminds GmbH & Co. KG and Contributors
+
+The JaCoCo Java Code Coverage Library and all included documentation is made
+available by Mountainminds GmbH & Co. KG, Munich. Except indicated below, the
+Content is provided to you under the terms and conditions of the Eclipse Public
+License Version 2.0 ("EPL"). A copy of the EPL is available at
+[https://www.eclipse.org/legal/epl-2.0/](https://www.eclipse.org/legal/epl-2.0/).
+
+Please visit
+[http://www.jacoco.org/jacoco/trunk/doc/license.html](http://www.jacoco.org/jacoco/trunk/doc/license.html)
+for the complete license information including third party licenses and 
trademarks.
\ No newline at end of file
diff --git 
a/fluss-filesystems/fluss-fs-obs/src/main/resources/META-INF/licenses/LICENSE.jaxb
 
b/fluss-filesystems/fluss-fs-obs/src/main/resources/META-INF/licenses/LICENSE.jaxb
new file mode 100644
index 00000000..fd16ea95
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-obs/src/main/resources/META-INF/licenses/LICENSE.jaxb
@@ -0,0 +1,135 @@
+COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL)Version 1.1
+
+1. Definitions.
+
+     1.1. "Contributor" means each individual or entity that creates or 
contributes to the creation of Modifications.
+
+     1.2. "Contributor Version" means the combination of the Original 
Software, prior Modifications used by a Contributor (if any), and the 
Modifications made by that particular Contributor.
+
+     1.3. "Covered Software" means (a) the Original Software, or (b) 
Modifications, or (c) the combination of files containing Original Software 
with files containing Modifications, in each case including portions thereof.
+
+     1.4. "Executable" means the Covered Software in any form other than 
Source Code.
+
+     1.5. "Initial Developer" means the individual or entity that first makes 
Original Software available under this License.
+
+     1.6. "Larger Work" means a work which combines Covered Software or 
portions thereof with code not governed by the terms of this License.
+
+     1.7. "License" means this document.
+
+     1.8. "Licensable" means having the right to grant, to the maximum extent 
possible, whether at the time of the initial grant or subsequently acquired, 
any and all of the rights conveyed herein.
+
+     1.9. "Modifications" means the Source Code and Executable form of any of 
the following:
+
+     A. Any file that results from an addition to, deletion from or 
modification of the contents of a file containing Original Software or previous 
Modifications;
+
+     B. Any new file that contains any part of the Original Software or 
previous Modification; or
+
+     C. Any new file that is contributed or otherwise made available under the 
terms of this License.
+
+     1.10. "Original Software" means the Source Code and Executable form of 
computer software code that is originally released under this License.
+
+     1.11. "Patent Claims" means any patent claim(s), now owned or hereafter 
acquired, including without limitation, method, process, and apparatus claims, 
in any patent Licensable by grantor.
+
+     1.12. "Source Code" means (a) the common form of computer software code 
in which modifications are made and (b) associated documentation included in or 
with such code.
+
+     1.13. "You" (or "Your") means an individual or a legal entity exercising 
rights under, and complying with all of the terms of, this License. For legal 
entities, "You" includes any entity which controls, is controlled by, or is 
under common control with You. For purposes of this definition, "control" means 
(a) the power, direct or indirect, to cause the direction or management of such 
entity, whether by contract or otherwise, or (b) ownership of more than fifty 
percent (50%) of the o [...]
+
+2. License Grants.
+
+     2.1. The Initial Developer Grant.
+
+     Conditioned upon Your compliance with Section 3.1 below and subject to 
third party intellectual property claims, the Initial Developer hereby grants 
You a world-wide, royalty-free, non-exclusive license:
+
+     (a) under intellectual property rights (other than patent or trademark) 
Licensable by Initial Developer, to use, reproduce, modify, display, perform, 
sublicense and distribute the Original Software (or portions thereof), with or 
without Modifications, and/or as part of a Larger Work; and
+
+     (b) under Patent Claims infringed by the making, using or selling of 
Original Software, to make, have made, use, practice, sell, and offer for sale, 
and/or otherwise dispose of the Original Software (or portions thereof).
+
+     (c) The licenses granted in Sections 2.1(a) and (b) are effective on the 
date Initial Developer first distributes or otherwise makes the Original 
Software available to a third party under the terms of this License.
+
+     (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 
(1) for code that You delete from the Original Software, or (2) for 
infringements caused by: (i) the modification of the Original Software, or (ii) 
the combination of the Original Software with other software or devices.
+
+     2.2. Contributor Grant.
+
+     Conditioned upon Your compliance with Section 3.1 below and subject to 
third party intellectual property claims, each Contributor hereby grants You a 
world-wide, royalty-free, non-exclusive license:
+
+     (a) under intellectual property rights (other than patent or trademark) 
Licensable by Contributor to use, reproduce, modify, display, perform, 
sublicense and distribute the Modifications created by such Contributor (or 
portions thereof), either on an unmodified basis, with other Modifications, as 
Covered Software and/or as part of a Larger Work; and
+
+     (b) under Patent Claims infringed by the making, using, or selling of 
Modifications made by that Contributor either alone and/or in combination with 
its Contributor Version (or portions of such combination), to make, use, sell, 
offer for sale, have made, and/or otherwise dispose of: (1) Modifications made 
by that Contributor (or portions thereof); and (2) the combination of 
Modifications made by that Contributor with its Contributor Version (or 
portions of such combination).
+
+     (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on 
the date Contributor first distributes or otherwise makes the Modifications 
available to a third party.
+
+     (d) Notwithstanding Section 2.2(b) above, no patent license is granted: 
(1) for any code that Contributor has deleted from the Contributor Version; (2) 
for infringements caused by: (i) third party modifications of Contributor 
Version, or (ii) the combination of Modifications made by that Contributor with 
other software (except as part of the Contributor Version) or other devices; or 
(3) under Patent Claims infringed by Covered Software in the absence of 
Modifications made by that Co [...]
+
+3. Distribution Obligations.
+
+     3.1. Availability of Source Code.
+
+     Any Covered Software that You distribute or otherwise make available in 
Executable form must also be made available in Source Code form and that Source 
Code form must be distributed only under the terms of this License. You must 
include a copy of this License with every copy of the Source Code form of the 
Covered Software You distribute or otherwise make available. You must inform 
recipients of any such Covered Software in Executable form as to how they can 
obtain such Covered Softw [...]
+
+     3.2. Modifications.
+
+     The Modifications that You create or to which You contribute are governed 
by the terms of this License. You represent that You believe Your Modifications 
are Your original creation(s) and/or You have sufficient rights to grant the 
rights conveyed by this License.
+
+     3.3. Required Notices.
+
+     You must include a notice in each of Your Modifications that identifies 
You as the Contributor of the Modification. You may not remove or alter any 
copyright, patent or trademark notices contained within the Covered Software, 
or any notices of licensing or any descriptive text giving attribution to any 
Contributor or the Initial Developer.
+
+     3.4. Application of Additional Terms.
+
+     You may not offer or impose any terms on any Covered Software in Source 
Code form that alters or restricts the applicable version of this License or 
the recipients' rights hereunder. You may choose to offer, and to charge a fee 
for, warranty, support, indemnity or liability obligations to one or more 
recipients of Covered Software. However, you may do so only on Your own behalf, 
and not on behalf of the Initial Developer or any Contributor. You must make it 
absolutely clear that any [...]
+
+     3.5. Distribution of Executable Versions.
+
+     You may distribute the Executable form of the Covered Software under the 
terms of this License or under the terms of a license of Your choice, which may 
contain terms different from this License, provided that You are in compliance 
with the terms of this License and that the license for the Executable form 
does not attempt to limit or alter the recipient's rights in the Source Code 
form from the rights set forth in this License. If You distribute the Covered 
Software in Executable f [...]
+
+     3.6. Larger Works.
+
+     You may create a Larger Work by combining Covered Software with other 
code not governed by the terms of this License and distribute the Larger Work 
as a single product. In such a case, You must make sure the requirements of 
this License are fulfilled for the Covered Software.
+
+4. Versions of the License.
+
+     4.1. New Versions.
+
+     Oracle is the initial license steward and may publish revised and/or new 
versions of this License from time to time. Each version will be given a 
distinguishing version number. Except as provided in Section 4.3, no one other 
than the license steward has the right to modify this License.
+
+     4.2. Effect of New Versions.
+
+     You may always continue to use, distribute or otherwise make the Covered 
Software available under the terms of the version of the License under which 
You originally received the Covered Software. If the Initial Developer includes 
a notice in the Original Software prohibiting it from being distributed or 
otherwise made available under any subsequent version of the License, You must 
distribute and make the Covered Software available under the terms of the 
version of the License under  [...]
+
+     4.3. Modified Versions.
+
+     When You are an Initial Developer and You want to create a new license 
for Your Original Software, You may create and use a modified version of this 
License if You: (a) rename the license and remove any references to the name of 
the license steward (except to note that the license differs from this 
License); and (b) otherwise make it clear that the license contains terms which 
differ from this License.
+
+5. DISCLAIMER OF WARRANTY.
+
+     COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, 
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT 
LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, 
MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK 
AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD 
ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL 
DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUM [...]
+
+6. TERMINATION.
+
+     6.1. This License and the rights granted hereunder will terminate 
automatically if You fail to comply with terms herein and fail to cure such 
breach within 30 days of becoming aware of the breach. Provisions which, by 
their nature, must remain in effect beyond the termination of this License 
shall survive.
+
+     6.2. If You assert a patent infringement claim (excluding declaratory 
judgment actions) against Initial Developer or a Contributor (the Initial 
Developer or Contributor against whom You assert such claim is referred to as 
"Participant") alleging that the Participant Software (meaning the Contributor 
Version where the Participant is a Contributor or the Original Software where 
the Participant is the Initial Developer) directly or indirectly infringes any 
patent, then any and all righ [...]
+
+     6.3. If You assert a patent infringement claim against Participant 
alleging that the Participant Software directly or indirectly infringes any 
patent where such claim is resolved (such as by license or settlement) prior to 
the initiation of patent infringement litigation, then the reasonable value of 
the licenses granted by such Participant under Sections 2.1 or 2.2 shall be 
taken into account in determining the amount or value of any payment or license.
+
+     6.4. In the event of termination under Sections 6.1 or 6.2 above, all end 
user licenses that have been validly granted by You or any distributor 
hereunder prior to termination (excluding licenses granted to You by any 
distributor) shall survive termination.
+
+7. LIMITATION OF LIABILITY.
+
+     UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING 
NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY 
OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF 
ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, 
INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT 
LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR 
MALFUNCTION, OR ANY AND ALL OTHER COMM [...]
+
+8. U.S. GOVERNMENT END USERS.
+
+     The Covered Software is a "commercial item," as that term is defined in 
48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" (as 
that term is defined at 48 C.F.R. ? 252.227-7014(a)(1)) and "commercial 
computer software documentation" as such terms are used in 48 C.F.R. 12.212 
(Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 
227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Software 
with only those rights set for [...]
+
+9. MISCELLANEOUS.
+
+     This License represents the complete agreement concerning subject matter 
hereof. If any provision of this License is held to be unenforceable, such 
provision shall be reformed only to the extent necessary to make it 
enforceable. This License shall be governed by the law of the jurisdiction 
specified in a notice contained within the Original Software (except to the 
extent applicable law, if any, provides otherwise), excluding such 
jurisdiction's conflict-of-law provisions. Any litiga [...]
+
+10. RESPONSIBILITY FOR CLAIMS.
+
+     As between Initial Developer and the Contributors, each party is 
responsible for claims and damages arising, directly or indirectly, out of its 
utilization of rights under this License and You agree to work with Initial 
Developer and Contributors to distribute such responsibility on an equitable 
basis. Nothing herein is intended or shall be deemed to constitute any 
admission of liability.
+
+----------
+NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION 
LICENSE (CDDL)
+The code released under the CDDL shall be governed by the laws of the State of 
California (excluding conflict-of-law provisions). Any litigation relating to 
this License shall be subject to the jurisdiction of the Federal Courts of the 
Northern District of California and the state courts of the State of 
California, with venue lying in Santa Clara County, California.
diff --git 
a/fluss-filesystems/fluss-fs-obs/src/main/resources/META-INF/licenses/LICENSE.jdom
 
b/fluss-filesystems/fluss-fs-obs/src/main/resources/META-INF/licenses/LICENSE.jdom
new file mode 100644
index 00000000..b420aeb0
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-obs/src/main/resources/META-INF/licenses/LICENSE.jdom
@@ -0,0 +1,51 @@
+ Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin.
+ All rights reserved.
+
+This project bundles the following dependencies under the following license.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions, and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions, and the disclaimer that follows
+    these conditions in the documentation and/or other materials
+    provided with the distribution.
+
+ 3. The name "JDOM" must not be used to endorse or promote products
+    derived from this software without prior written permission.  For
+    written permission, please contact <request_AT_jdom_DOT_org>.
+
+ 4. Products derived from this software may not be called "JDOM", nor
+    may "JDOM" appear in their name, without prior written permission
+    from the JDOM Project Management <request_AT_jdom_DOT_org>.
+
+ In addition, we request (but do not require) that you include in the
+ end-user documentation provided with the redistribution and/or in the
+ software itself an acknowledgement equivalent to the following:
+     "This product includes software developed by the
+      JDOM Project (http://www.jdom.org/)."
+ Alternatively, the acknowledgment may be graphical using the logos
+ available at http://www.jdom.org/images/logos.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED.  IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+ This software consists of voluntary contributions made by many
+ individuals on behalf of the JDOM Project and was originally
+ created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
+ Brett McLaughlin <brett_AT_jdom_DOT_org>.  For more information
+ on the JDOM Project, please see <http://www.jdom.org/>.
diff --git 
a/fluss-filesystems/fluss-fs-obs/src/main/resources/META-INF/services/com.alibaba.fluss.fs.FileSystemPlugin
 
b/fluss-filesystems/fluss-fs-obs/src/main/resources/META-INF/services/com.alibaba.fluss.fs.FileSystemPlugin
new file mode 100644
index 00000000..b7911eea
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-obs/src/main/resources/META-INF/services/com.alibaba.fluss.fs.FileSystemPlugin
@@ -0,0 +1,17 @@
+# 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.
+
+com.alibaba.fluss.fs.obs.OBSFileSystemPlugin
diff --git 
a/fluss-filesystems/fluss-fs-obs/src/main/resources/META-INF/services/com.alibaba.fluss.fs.token.SecurityTokenReceiver
 
b/fluss-filesystems/fluss-fs-obs/src/main/resources/META-INF/services/com.alibaba.fluss.fs.token.SecurityTokenReceiver
new file mode 100644
index 00000000..90f6d232
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-obs/src/main/resources/META-INF/services/com.alibaba.fluss.fs.token.SecurityTokenReceiver
@@ -0,0 +1,17 @@
+# 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.
+
+com.alibaba.fluss.fs.obs.token.OBSSecurityTokenReceiver
diff --git 
a/fluss-filesystems/fluss-fs-obs/src/test/java/com/alibaba/fluss/fs/obs/OBSFileSystemBehaviorITCase.java
 
b/fluss-filesystems/fluss-fs-obs/src/test/java/com/alibaba/fluss/fs/obs/OBSFileSystemBehaviorITCase.java
new file mode 100644
index 00000000..85b439f9
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-obs/src/test/java/com/alibaba/fluss/fs/obs/OBSFileSystemBehaviorITCase.java
@@ -0,0 +1,64 @@
+/*
+ * 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 com.alibaba.fluss.fs.obs;
+
+import com.alibaba.fluss.config.Configuration;
+import com.alibaba.fluss.fs.FileSystem;
+import com.alibaba.fluss.fs.FileSystemBehaviorTestSuite;
+import com.alibaba.fluss.fs.FsPath;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+
+import java.util.UUID;
+
+/**
+ * An implementation of the {@link FileSystemBehaviorTestSuite} for the OBS 
file system with Hadoop
+ * obs sdk.
+ */
+class OBSFileSystemBehaviorITCase extends FileSystemBehaviorTestSuite {
+
+    private static final String TEST_DATA_DIR = "tests-" + UUID.randomUUID();
+
+    @BeforeAll
+    static void setup() {
+        OBSTestCredentials.assumeCredentialsAvailable();
+
+        final Configuration conf = new Configuration();
+        conf.setString("fs.obs.endpoint", OBSTestCredentials.getOBSEndpoint());
+        conf.setString("fs.obs.region", OBSTestCredentials.getOBSRegion());
+        conf.setString("fs.obs.access.key", 
OBSTestCredentials.getOBSAccessKey());
+        conf.setString("fs.obs.secret.key", 
OBSTestCredentials.getOBSSecretKey());
+        FileSystem.initialize(conf, null);
+    }
+
+    @Override
+    protected FileSystem getFileSystem() throws Exception {
+        return getBasePath().getFileSystem();
+    }
+
+    @Override
+    protected FsPath getBasePath() {
+        return new FsPath(OBSTestCredentials.getTestBucketUri() + 
TEST_DATA_DIR);
+    }
+
+    @AfterAll
+    static void clearFsConfig() {
+        FileSystem.initialize(new Configuration(), null);
+    }
+}
diff --git 
a/fluss-filesystems/fluss-fs-obs/src/test/java/com/alibaba/fluss/fs/obs/OBSTestCredentials.java
 
b/fluss-filesystems/fluss-fs-obs/src/test/java/com/alibaba/fluss/fs/obs/OBSTestCredentials.java
new file mode 100644
index 00000000..3193eb64
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-obs/src/test/java/com/alibaba/fluss/fs/obs/OBSTestCredentials.java
@@ -0,0 +1,118 @@
+/*
+ * 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 com.alibaba.fluss.fs.obs;
+
+import javax.annotation.Nullable;
+
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
+/** Access to credentials to access OBS buckets during integration tests. */
+public class OBSTestCredentials {
+    @Nullable private static final String ENDPOINT = 
System.getenv("ARTIFACTS_OBS_ENDPOINT");
+
+    @Nullable private static final String REGION = 
System.getenv("ARTIFACTS_OBS_REGION");
+
+    @Nullable private static final String BUCKET = 
System.getenv("ARTIFACTS_OBS_BUCKET");
+
+    @Nullable private static final String ACCESS_KEY = 
System.getenv("ARTIFACTS_OBS_ACCESS_KEY");
+
+    @Nullable private static final String SECRET_KEY = 
System.getenv("ARTIFACTS_OBS_SECRET_KEY");
+
+    // ------------------------------------------------------------------------
+
+    public static boolean credentialsAvailable() {
+        return isNotEmpty(ENDPOINT)
+                && isNotEmpty(BUCKET)
+                && isNotEmpty(ACCESS_KEY)
+                && isNotEmpty(SECRET_KEY);
+    }
+
+    /** Checks if a String is not null and not empty. */
+    private static boolean isNotEmpty(@Nullable String str) {
+        return str != null && !str.isEmpty();
+    }
+
+    public static void assumeCredentialsAvailable() {
+        assumeTrue(
+                credentialsAvailable(), "No OBS credentials available in this 
test's environment");
+    }
+
+    /**
+     * Get OBS endpoint used to connect.
+     *
+     * @return OBS endpoint
+     */
+    public static String getOBSEndpoint() {
+        if (ENDPOINT != null) {
+            return ENDPOINT;
+        } else {
+            throw new IllegalStateException("OBS endpoint is not available");
+        }
+    }
+
+    /**
+     * Get the region for the OBS.
+     *
+     * @return OBS region
+     */
+    public static String getOBSRegion() {
+        if (REGION != null) {
+            return REGION;
+        } else {
+            throw new IllegalStateException("OBS region is not available");
+        }
+    }
+
+    /**
+     * Get OBS access key.
+     *
+     * @return OBS access key
+     */
+    public static String getOBSAccessKey() {
+        if (ACCESS_KEY != null) {
+            return ACCESS_KEY;
+        } else {
+            throw new IllegalStateException("OBS access key is not available");
+        }
+    }
+
+    /**
+     * Get OBS secret key.
+     *
+     * @return OBS secret key
+     */
+    public static String getOBSSecretKey() {
+        if (SECRET_KEY != null) {
+            return SECRET_KEY;
+        } else {
+            throw new IllegalStateException("OBS secret key is not available");
+        }
+    }
+
+    public static String getTestBucketUri() {
+        return getTestBucketUriWithScheme("obs");
+    }
+
+    public static String getTestBucketUriWithScheme(String scheme) {
+        if (BUCKET != null) {
+            return scheme + "://" + BUCKET + "/";
+        } else {
+            throw new IllegalStateException("OBS test bucket is not 
available");
+        }
+    }
+}
diff --git 
a/fluss-filesystems/fluss-fs-obs/src/test/java/com/alibaba/fluss/fs/obs/OBSWithCredentialsProviderFileSystemBehaviorITCase.java
 
b/fluss-filesystems/fluss-fs-obs/src/test/java/com/alibaba/fluss/fs/obs/OBSWithCredentialsProviderFileSystemBehaviorITCase.java
new file mode 100644
index 00000000..411e8532
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-obs/src/test/java/com/alibaba/fluss/fs/obs/OBSWithCredentialsProviderFileSystemBehaviorITCase.java
@@ -0,0 +1,65 @@
+/*
+ * 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 com.alibaba.fluss.fs.obs;
+
+import com.alibaba.fluss.config.Configuration;
+import com.alibaba.fluss.fs.FileSystem;
+import com.alibaba.fluss.fs.FileSystemBehaviorTestSuite;
+import com.alibaba.fluss.fs.FsPath;
+
+import com.obs.services.EnvironmentVariableObsCredentialsProvider;
+import com.obs.services.IObsCredentialsProvider;
+import org.junit.jupiter.api.BeforeAll;
+
+import java.util.UUID;
+
+import static 
com.alibaba.fluss.fs.obs.OBSFileSystemPlugin.CREDENTIALS_PROVIDER;
+
+/** IT case for access obs via set {@link IObsCredentialsProvider}. */
+class OBSWithCredentialsProviderFileSystemBehaviorITCase extends 
FileSystemBehaviorTestSuite {
+
+    private static final String TEST_DATA_DIR = "tests-" + UUID.randomUUID();
+
+    @BeforeAll
+    static void setup() {
+        OBSTestCredentials.assumeCredentialsAvailable();
+
+        // use EnvironmentVariableObsCredentialsProvider
+        final Configuration conf = new Configuration();
+        conf.setString(
+                CREDENTIALS_PROVIDER,
+                
EnvironmentVariableObsCredentialsProvider.class.getCanonicalName());
+        conf.setString("fs.obs.endpoint", OBSTestCredentials.getOBSEndpoint());
+        conf.setString("fs.obs.region", OBSTestCredentials.getOBSRegion());
+
+        // now, we need to set oss config to system properties
+        System.setProperty("obs.access.key", 
OBSTestCredentials.getOBSAccessKey());
+        System.setProperty("obs.secret.key", 
OBSTestCredentials.getOBSSecretKey());
+        FileSystem.initialize(conf, null);
+    }
+
+    @Override
+    protected FileSystem getFileSystem() throws Exception {
+        return getBasePath().getFileSystem();
+    }
+
+    @Override
+    protected FsPath getBasePath() {
+        return new FsPath(OBSTestCredentials.getTestBucketUri() + 
TEST_DATA_DIR);
+    }
+}
diff --git 
a/fluss-filesystems/fluss-fs-obs/src/test/java/com/alibaba/fluss/fs/obs/OBSWithTokenFileSystemBehaviorBaseITCase.java
 
b/fluss-filesystems/fluss-fs-obs/src/test/java/com/alibaba/fluss/fs/obs/OBSWithTokenFileSystemBehaviorBaseITCase.java
new file mode 100644
index 00000000..a2d8705d
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-obs/src/test/java/com/alibaba/fluss/fs/obs/OBSWithTokenFileSystemBehaviorBaseITCase.java
@@ -0,0 +1,38 @@
+/*
+ * 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 com.alibaba.fluss.fs.obs;
+
+import com.alibaba.fluss.config.Configuration;
+import com.alibaba.fluss.fs.FileSystem;
+import com.alibaba.fluss.fs.FileSystemBehaviorTestSuite;
+
+/** Base IT case for access obs with temporary credentials in hadoop sdk as 
OBS FileSystem. */
+abstract class OBSWithTokenFileSystemBehaviorBaseITCase extends 
FileSystemBehaviorTestSuite {
+
+    static void initFileSystemWithSecretKey() {
+        OBSTestCredentials.assumeCredentialsAvailable();
+
+        // first init filesystem with ak/sk
+        final Configuration conf = new Configuration();
+        conf.setString("fs.obs.endpoint", OBSTestCredentials.getOBSEndpoint());
+        conf.setString("fs.obs.region", OBSTestCredentials.getOBSRegion());
+        conf.setString("fs.obs.access.key", 
OBSTestCredentials.getOBSAccessKey());
+        conf.setString("fs.obs.secret.key", 
OBSTestCredentials.getOBSSecretKey());
+        FileSystem.initialize(conf, null);
+    }
+}
diff --git 
a/fluss-filesystems/fluss-fs-obs/src/test/java/com/alibaba/fluss/fs/obs/OBSWithTokenFileSystemBehaviorITCase.java
 
b/fluss-filesystems/fluss-fs-obs/src/test/java/com/alibaba/fluss/fs/obs/OBSWithTokenFileSystemBehaviorITCase.java
new file mode 100644
index 00000000..ae45379b
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-obs/src/test/java/com/alibaba/fluss/fs/obs/OBSWithTokenFileSystemBehaviorITCase.java
@@ -0,0 +1,72 @@
+/*
+ * 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 com.alibaba.fluss.fs.obs;
+
+import com.alibaba.fluss.config.Configuration;
+import com.alibaba.fluss.fs.FileSystem;
+import com.alibaba.fluss.fs.FsPath;
+import com.alibaba.fluss.fs.obs.token.OBSSecurityTokenReceiver;
+import com.alibaba.fluss.fs.token.ObtainedSecurityToken;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+
+import java.util.UUID;
+
+/** IT case for access obs with iam token in hadoop sdk as FileSystem. */
+class OBSWithTokenFileSystemBehaviorITCase extends 
OBSWithTokenFileSystemBehaviorBaseITCase {
+
+    private static final String TEST_DATA_DIR = "tests-" + UUID.randomUUID();
+
+    @BeforeAll
+    static void setup() throws Exception {
+        // init a filesystem with ak/sk so that it can generate iam token
+        initFileSystemWithSecretKey();
+        // now, we can init with iam token
+        initFileSystemWithToken(getFsPath());
+    }
+
+    @Override
+    protected FileSystem getFileSystem() throws Exception {
+        return getFsPath().getFileSystem();
+    }
+
+    @Override
+    protected FsPath getBasePath() {
+        return getFsPath();
+    }
+
+    protected static FsPath getFsPath() {
+        return new FsPath(OBSTestCredentials.getTestBucketUri() + 
TEST_DATA_DIR);
+    }
+
+    @AfterAll
+    static void clearFsConfig() {
+        FileSystem.initialize(new Configuration(), null);
+    }
+
+    private static void initFileSystemWithToken(FsPath fsPath) throws 
Exception {
+        Configuration configuration = new Configuration();
+        // obtain a security token and call onNewTokensObtained
+        ObtainedSecurityToken obtainedSecurityToken = 
fsPath.getFileSystem().obtainSecurityToken();
+        OBSSecurityTokenReceiver obsSecurityTokenReceiver = new 
OBSSecurityTokenReceiver();
+        obsSecurityTokenReceiver.onNewTokensObtained(obtainedSecurityToken);
+
+        FileSystem.initialize(configuration, null);
+    }
+}
diff --git 
a/fluss-filesystems/fluss-fs-obs/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension
 
b/fluss-filesystems/fluss-fs-obs/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension
new file mode 100644
index 00000000..590243ea
--- /dev/null
+++ 
b/fluss-filesystems/fluss-fs-obs/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+
+com.alibaba.fluss.testutils.common.TestLoggerExtension
\ No newline at end of file
diff --git a/fluss-filesystems/pom.xml b/fluss-filesystems/pom.xml
index 18f52640..6b6194c4 100644
--- a/fluss-filesystems/pom.xml
+++ b/fluss-filesystems/pom.xml
@@ -34,6 +34,7 @@
         <module>fluss-fs-oss</module>
         <module>fluss-fs-s3</module>
         <module>fluss-fs-gs</module>
+        <module>fluss-fs-obs</module>
     </modules>
     <packaging>pom</packaging>
 
diff --git a/fluss-test-coverage/pom.xml b/fluss-test-coverage/pom.xml
index 54d00d30..20d8d2c8 100644
--- a/fluss-test-coverage/pom.xml
+++ b/fluss-test-coverage/pom.xml
@@ -306,6 +306,7 @@
                                         
<exclude>com.alibaba.fluss.fs.hdfs.HadoopSecurityTokenReceiver</exclude>
                                         
<exclude>com.alibaba.fluss.fs.oss.*</exclude>
                                         
<exclude>com.alibaba.fluss.fs.s3.*</exclude>
+                                        
<exclude>com.alibaba.fluss.fs.obs.*</exclude>
                                         
<exclude>com.amazonaws.services.s3.model.transform.XmlResponsesSaxParser*</exclude>
                                         
<exclude>com.alibaba.fluss.rocksdb.RocksIteratorWrapper
                                         </exclude>
diff --git a/website/docs/maintenance/filesystems/obs.md 
b/website/docs/maintenance/filesystems/obs.md
new file mode 100644
index 00000000..74d65b90
--- /dev/null
+++ b/website/docs/maintenance/filesystems/obs.md
@@ -0,0 +1,62 @@
+---
+title: HuaweiCloud OBS
+sidebar_position: 6
+---
+
+<!--
+ 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.
+-->
+
+# HuaweiCloud OBS
+
+[HuaweiCloud Object Storage 
Service](https://www.huaweicloud.com/product/obs.html) (HuaweiCloud OBS) is an 
enterprise-grade object storage solution delivering industry-leading 
scalability, data durability, security, and cost-efficiency. Trusted by 
organizations across finance, healthcare, manufacturing, and media, OBS enables 
you to securely store, manage, analyze, and protect unlimited data volumes for 
diverse scenarios like AI training, data lakes, multi-cloud backup, and 
real-time med [...]
+
+## Configurations setup
+
+To enabled HuaweiCloud OBS as remote storage, there are some required 
configurations that must be added to Fluss' `server.yaml`:
+
+```yaml
+# The dir that used to be as the remote storage of Fluss
+remote.data.dir: obs://<your-bucket>/path/to/remote/storage
+# obs endpoint, such as: https://obs.cn-north-4.myhuaweicloud.com
+fs.obs.endpoint: <obs-endpoint-hostname>
+# OBS region, such as: cn-north-4
+fs.obs.region: <your-obs-region>
+
+# Authentication (choose one option below)
+
+# Option 1: Direct credentials
+# HuaweiCloud access key
+fs.obs.access.key: <your-access-key>
+# HuaweiCloud secret key
+fs.obs.secret.key: <your-secret-key>
+
+# Option 2: Secure credential provider
+fs.obs.security.provider: <your-credentials-provider>
+```
+To avoid exposing sensitive access key information directly in the 
`server.yaml`, you can choose option2 to use a credential provider by setting 
the `fs.obs.security.provider` property.
+
+For example, to use environment variables for credential management:
+```yaml
+fs.obs.security.provider: 
com.obs.services.EnvironmentVariableObsCredentialsProvider
+```
+Then, set the following environment variables before starting the Fluss 
service:
+```bash
+export OBS_ACCESS_KEY_ID=<your-access-key>
+export OBS_SECRET_ACCESS_KEY=<your-secret-key>
+```
+This approach enhances security by keeping sensitive credentials out of 
configuration files.
\ No newline at end of file
diff --git a/website/docs/maintenance/filesystems/overview.md 
b/website/docs/maintenance/filesystems/overview.md
index 18cf09e9..f14a7afc 100644
--- a/website/docs/maintenance/filesystems/overview.md
+++ b/website/docs/maintenance/filesystems/overview.md
@@ -54,4 +54,6 @@ The Fluss project supports the following file system:
 
 - **[AWS S3](s3.md)** is supported by `fluss-fs-s3` and registered under the 
`s3://` URI scheme.
 
+- **[HuaweiCloud OBS](obs.md)** is supported by `fluss-fs-obs` and registered 
under the `obs://` URI scheme.
+
 The implementation is based on [Hadoop Project](https://hadoop.apache.org/) 
but is self-contained with no dependency footprint.
\ No newline at end of file

Reply via email to