Gravitino Virtual File System
The Gravitino Virtual File System (GVFS) reads and writes the files in a fileset through a virtual path, so code never names the storage behind it. The server resolves each path to the fileset's real location in HDFS or object storage and applies the fileset's permissions on the way.
GVFS comes in two forms with the same paths and the same behavior:
- Java, a Hadoop Compatible File System. Anything that reads Hadoop paths uses it: the Hadoop shell, Java code, Spark, and TensorFlow.
- Python, an fsspec file system. Anything that accepts an fsspec file system uses it: Pandas, PyArrow, Ray, and LlamaIndex.
Each section below has a Java and a Python tab. Choosing one switches every section on the page.
Quick start
The example lists a fileset, writes a file into it, and reads the file back. It assumes the fileset
sales.raw.events exists in the metalake example, and signs in with an OAuth client credential;
see Authentication for the identity provider side.
- Java
- Python
-
Add the GVFS jar to Hadoop. Datastrato Enterprise 1.3.0 is built on Apache Gravitino 1.3.1, so use version 1.3.1 of the jar.
Download the GVFS runtime jarcd ${HADOOP_HOME}/share/hadoop/common/lib/
curl -sSfLO https://repo1.maven.org/maven2/org/apache/gravitino/gravitino-filesystem-hadoop3-runtime/1.3.1/gravitino-filesystem-hadoop3-runtime-1.3.1.jar -
Point Hadoop at the server. Add these properties to
${HADOOP_HOME}/etc/hadoop/core-site.xml.core-site.xml<property>
<name>fs.AbstractFileSystem.gvfs.impl</name>
<value>org.apache.gravitino.filesystem.hadoop.Gvfs</value>
</property>
<property>
<name>fs.gvfs.impl</name>
<value>org.apache.gravitino.filesystem.hadoop.GravitinoVirtualFileSystem</value>
</property>
<property>
<name>fs.gravitino.server.uri</name>
<value>https://{gravitino_host}</value>
</property>
<property>
<name>fs.gravitino.client.metalake</name>
<value>example</value>
</property>
<property>
<name>fs.gravitino.client.authType</name>
<value>oauth2</value>
</property>
<property>
<name>fs.gravitino.client.oauth2.serverUri</name>
<value>https://{idp_host}</value>
</property>
<property>
<name>fs.gravitino.client.oauth2.path</name>
<value>{token_path}</value>
</property>
<property>
<name>fs.gravitino.client.oauth2.credential</name>
<value>{client_id}:{client_secret}</value>
</property>
<property>
<name>fs.gravitino.client.oauth2.scope</name>
<value>{scope}</value>
</property>{token_path}is the token endpoint's path without its leading slash, for examplerealms/{realm}/protocol/openid-connect/tokenfor Keycloak. -
Use the fileset.
List, write, and read a filesethadoop fs -ls gvfs://fileset/sales/raw/events/
echo "hello world" > hello.txt
hadoop fs -put hello.txt gvfs://fileset/sales/raw/events/hello.txt
hadoop fs -cat gvfs://fileset/sales/raw/events/hello.txt
-
Install the client. Datastrato Enterprise 1.3.0 is built on Apache Gravitino 1.3.1, so use a 1.3 release from 1.3.1 on. It needs Python 3.10 or later.
Install the Gravitino Python clientpip install "apache-gravitino>=1.3.1,<1.4" -
Use the fileset.
List, write, and read a filesetfrom gravitino import gvfs
from gravitino.filesystem.gvfs_config import GVFSConfig
fs = gvfs.GravitinoVirtualFileSystem(
server_uri="https://{gravitino_host}",
metalake_name="example",
options={
GVFSConfig.AUTH_TYPE: GVFSConfig.OAUTH2_AUTH_TYPE,
GVFSConfig.OAUTH2_SERVER_URI: "https://{idp_host}",
GVFSConfig.OAUTH2_PATH: "{token_path}",
GVFSConfig.OAUTH2_CREDENTIAL: "{client_id}:{client_secret}",
GVFSConfig.OAUTH2_SCOPE: "{scope}",
},
)
print(fs.ls("gvfs://fileset/sales/raw/events/"))
with fs.open("gvfs://fileset/sales/raw/events/hello.txt", mode="wb") as f:
f.write(b"hello world")
with fs.open("gvfs://fileset/sales/raw/events/hello.txt", mode="rb") as f:
print(f.read()){token_path}is the token endpoint's path without its leading slash, for examplerealms/{realm}/protocol/openid-connect/tokenfor Keycloak.
Paths
A GVFS path names the catalog, schema, and fileset, followed by any path inside the fileset.
gvfs://fileset/{catalog}/{schema}/{fileset}/{sub_path}
gvfs is the scheme and fileset is a fixed root. Python also accepts the path without the scheme,
as fileset/{catalog}/{schema}/{fileset}/{sub_path}.
Choose a location
A fileset can have several storage locations. GVFS picks one in this order:
- The location named in the client configuration:
fs.gravitino.current.location.namein Java,current_location_namein Python. - The location named in the environment variable
CURRENT_LOCATION_NAME. The variable's name can be changed withfs.gravitino.current.location.name.env.varin Java orcurrent_location_name_env_varin Python. - The fileset's
default-location-nameproperty.
Configuration
- Java
- Python
GVFS for Java needs Hadoop 3.3.1 or later. Properties go in core-site.xml, on a Hadoop
Configuration in code, or as spark.hadoop. options in Spark.
| Configuration item | Description | Default value | Required |
|---|---|---|---|
fs.AbstractFileSystem.gvfs.impl | The Gravitino Virtual File System abstract class, set it to org.apache.gravitino.filesystem.hadoop.Gvfs. | (none) | Yes |
fs.gvfs.impl | The Gravitino Virtual File System implementation class, set it to org.apache.gravitino.filesystem.hadoop.GravitinoVirtualFileSystem. | (none) | Yes |
fs.gvfs.impl.disable.cache | Disable the Gravitino Virtual File System cache in the Hadoop environment. If you need to proxy multi-user operations, set this value to true and create a separate File System for each user. | false | No |
fs.gravitino.server.uri | The Gravitino server URI which GVFS needs to load the fileset metadata. | (none) | Yes |
fs.gravitino.client.metalake | The metalake to which the fileset belongs. | (none) | Yes |
fs.gravitino.client.authType | The auth type the Gravitino client uses with the Gravitino Virtual File System: oauth2. See Authentication. | simple | Yes |
fs.gravitino.client.oauth2.serverUri | The auth server URI for the Gravitino client when using oauth2 auth type with the Gravitino Virtual File System. | (none) | Yes if you use oauth2 auth type |
fs.gravitino.client.oauth2.credential | The auth credential for the Gravitino client when using oauth2 auth type in the Gravitino Virtual File System. | (none) | Yes if you use oauth2 auth type |
fs.gravitino.client.oauth2.path | The auth server path for the Gravitino client when using oauth2 auth type with the Gravitino Virtual File System. Remove the first slash / from the path, for example oauth/token. | (none) | Yes if you use oauth2 auth type |
fs.gravitino.client.oauth2.scope | The auth scope for the Gravitino client when using oauth2 auth type with the Gravitino Virtual File System. | (none) | Yes if you use oauth2 auth type |
fs.gravitino.fileset.cache.maxCapacity | The cache capacity of the Gravitino Virtual File System. | 20 | No |
fs.gravitino.fileset.cache.evictionMillsAfterAccess | The value of time that the cache expires after accessing in the Gravitino Virtual File System. The value is in milliseconds. | 3600000 | No |
fs.gravitino.fileset.cache.closeOnEviction | Whether to close the underlying FileSystem immediately on cache eviction. Default is false: close is deferred to GVFS shutdown to avoid breaking long-lived streams. Set to true to close immediately on eviction. | false | No |
fs.gravitino.current.location.name | The location of the fileset to use; see Choose a location. | the value of fileset property default-location-name | No |
fs.gravitino.current.location.name.env.var | The environment variable name to get the current location name. | CURRENT_LOCATION_NAME | No |
fs.gravitino.operations.class | The operations class to provide the FS operations for the Gravitino Virtual File System. Users can extends BaseGVFSOperations to implement their own operations and configure the class name in this conf to use custom FS operations. | org.apache.gravitino.filesystem.hadoop.DefaultGVFSOperations | No |
fs.gravitino.hook.class | The hook class to inject into the Gravitino Virtual File System. Users can implement their own GravitinoVirtualFileSystemHook and configure the class name in this conf to inject custom code. | org.apache.gravitino.filesystem.hadoop.NoOpHook | No |
fs.gravitino.client.request.header. | The configuration key prefix for the Gravitino client request header. You can set the request header for the Gravitino client. | (none) | No |
fs.gravitino.enableCredentialVending | Whether to enable credential vending for the Gravitino Virtual File System. | false | No |
fs.gravitino.client. | The configuration key prefix for the Gravitino client config. | (none) | No |
fs.gravitino.filesetMetadataCache.enable | Whether to cache the fileset, fileset schema or fileset catalog metadata in the Gravitino Virtual File System. Note that this cache causes a side effect: if you modify the fileset or fileset catalog metadata, the client cannot see the latest changes. | false | No |
fs.gravitino.autoCreateLocation | The configuration key for whether to enable auto-creation of fileset location when the server-side filesystem ops are disabled and the location does not exist. | true | No |
fs.path.config.{name} | Defines a logical location entry. Set fs.path.config.{name} to the real base URI (for example, hdfs://cluster1/). Any key that starts with the same prefix (such as fs.path.config.{name}.config.resource) is treated as a location-scoped property and will be forwarded to the underlying filesystem client. Note: location names must not contain (., _). | (none) | No |
Properties prefixed with fs.gravitino.client. configure the underlying Gravitino client, with the
fs. removed: fs.gravitino.client.socketTimeoutMs sets gravitino.client.socketTimeoutMs. An
unknown client property raises an exception; see
Java client configurations for the full list.
Filesets spread across clusters. When a fileset catalog spans several HDFS clusters, give each
cluster's base path its own client settings with fs.path.config.{name}.
fs.path.config.cluster1 = hdfs://cluster1/
fs.path.config.cluster1.config.resource = /etc/core-site.xml,/etc/hdfs-site.xml
fs.path.config.cluster2 = hdfs://cluster2/
fs.path.config.cluster2.config.resource = /etc/fs2/core-site.xml,/etc/fs2/hdfs-site.xml
Object storage. Filesets on S3, GCS, OSS, or Azure need storage properties as well; see Amazon S3, Google Cloud Storage, Alibaba Cloud OSS, and Azure Data Lake Storage.
Custom file systems. A fileset on a
custom file system provider takes
whatever properties that provider's getFileSystem reads, set the same way as the properties above.
GVFS for Python needs Python 3.10 or later. Options are passed to GravitinoVirtualFileSystem as
keyword arguments and in its options dictionary, or as storage_options in libraries such as
Pandas. Filesets on HDFS also need a Hadoop 3.3.1 or later client environment, described under
Examples.
| Configuration item | Description | Default value | Required |
|---|---|---|---|
server_uri | The Gravitino server URI, for example https://{gravitino_host}. | (none) | Yes |
metalake_name | The metalake name which the fileset belongs to. | (none) | Yes |
cache_size | The cache capacity of the Gravitino Virtual File System. | 20 | No |
cache_expired_time | The value of time that the cache expires after accessing in the Gravitino Virtual File System. The value is in seconds. | 3600 | No |
auth_type | The auth type the Gravitino client uses with the Gravitino Virtual File System: basic or oauth2. See Authentication. | simple | Yes |
basic_username | The username for the Gravitino client when using basic auth type with the local user store. | (none) | Yes if you use basic auth type |
basic_password | The password for the Gravitino client when using basic auth type with the local user store. | (none) | Yes if you use basic auth type |
oauth2_server_uri | The auth server URI for the Gravitino client when using oauth2 auth type. | (none) | Yes if you use oauth2 auth type |
oauth2_credential | The auth credential for the Gravitino client when using oauth2 auth type. | (none) | Yes if you use oauth2 auth type |
oauth2_path | The auth server path for the Gravitino client when using oauth2 auth type. Remove the first slash / from the path, for example oauth/token. | (none) | Yes if you use oauth2 auth type |
oauth2_scope | The auth scope for the Gravitino client when using oauth2 auth type with the Gravitino Virtual File System. | (none) | Yes if you use oauth2 auth type |
credential_expiration_ratio | The fraction of a vended credential's lifetime after which GVFS refreshes it, used when the fileset catalog vends credentials. At the default, a credential that expires in one hour is refreshed after 30 minutes. | 0.5 | No |
current_location_name | The location of the fileset to use; see Choose a location. | the value of fileset property default-location-name | No |
current_location_name_env_var | The environment variable name to get the current location name. | CURRENT_LOCATION_NAME | No |
operations_class | The operations class to provide the FS operations for the Gravitino Virtual File System. Users can extends BaseGVFSOperations to implement their own operations and configure the class name in this conf to use custom FS operations. | gravitino.filesystem.gvfs_default_operations.DefaultGVFSOperations | No |
hook_class | The hook class to inject into the Gravitino Virtual File System. Users can implement their own GravitinoVirtualFileSystemHook and configure the class name in this conf to inject custom code. | gravitino.filesystem.gvfs_hook.NoOpHook | No |
client_request_header_ | The configuration key prefix for the Gravitino client request header. You can set the request header for the Gravitino client. | (none) | No |
enable_credential_vending | Whether to enable credential vending for the Gravitino Virtual File System. | false | No |
gvfs_gravitino_client_ | The configuration key prefix for the Gravitino client. You can set the config for the Gravitino client. | (none) | No |
enable_fileset_metadata_cache | Whether to cache the fileset or fileset catalog metadata in the Gravitino Virtual File System. Note that this cache causes a side effect: if you modify the fileset or fileset catalog metadata, the client cannot see the latest changes. | false | No |
auto_create_location | The configuration key for whether to enable auto-creation of fileset location when the server-side filesystem ops are disabled and the location does not exist. | true | No |
fs_path_config_{name} | Defines a logical location entry. Set fs_path_config_{name} to the real base URI (for example, hdfs://cluster1/). Any key that starts with the same prefix (such as fs_path_config_{name}_config.resource) is treated as a location-scoped property and will be forwarded to the underlying filesystem client. Note: location names must not contain (., _). | (none) | No |
Options prefixed with gvfs_gravitino_client_ configure the underlying Gravitino client, with the
gvfs_ removed: gvfs_gravitino_client_request_timeout sets gravitino_client_request_timeout. An
unknown client option raises an exception; see
Python client configurations for the full list.
Filesets spread across clusters. When a fileset catalog spans several HDFS clusters, give each
cluster's base path its own client settings with fs_path_config_{name}.
options = {
"fs_path_config_cluster1": "hdfs://cluster1/",
"fs_path_config_cluster1_config.resource": "/etc/core-site.xml,/etc/hdfs-site.xml",
"fs_path_config_cluster2": "hdfs://cluster2/",
"fs_path_config_cluster2_config.resource": "/etc/fs2/core-site.xml,/etc/fs2/hdfs-site.xml",
}
Object storage. Filesets on S3, GCS, OSS, or Azure need storage options as well; see Amazon S3, Google Cloud Storage, Alibaba Cloud OSS, and Azure Data Lake Storage.
Custom file systems. GVFS for Python cannot reach filesets on a custom file system provider, a limit of the fsspec library.
Authentication
Datastrato Enterprise does not accept the simple auth type, which is the client default, so every
client sets an auth type. Configure the server side first; see
Authentication.
- Java
- Python
GVFS for Java signs in with OAuth.
Configuration conf = new Configuration();
conf.set("fs.AbstractFileSystem.gvfs.impl", "org.apache.gravitino.filesystem.hadoop.Gvfs");
conf.set("fs.gvfs.impl", "org.apache.gravitino.filesystem.hadoop.GravitinoVirtualFileSystem");
conf.set("fs.gravitino.server.uri", "https://{gravitino_host}");
conf.set("fs.gravitino.client.metalake", "example");
conf.set("fs.gravitino.client.authType", "oauth2");
conf.set("fs.gravitino.client.oauth2.serverUri", "https://{idp_host}");
conf.set("fs.gravitino.client.oauth2.path", "{token_path}");
conf.set("fs.gravitino.client.oauth2.credential", "{client_id}:{client_secret}");
conf.set("fs.gravitino.client.oauth2.scope", "{scope}");
Path filesetPath = new Path("gvfs://fileset/sales/raw/events");
FileSystem fs = filesetPath.getFileSystem(conf);
GVFS for Python signs in with OAuth or with a local user's password (basic).
- OAuth
- Local user
from gravitino import gvfs
from gravitino.filesystem.gvfs_config import GVFSConfig
options = {
GVFSConfig.AUTH_TYPE: GVFSConfig.OAUTH2_AUTH_TYPE,
GVFSConfig.OAUTH2_SERVER_URI: "https://{idp_host}",
GVFSConfig.OAUTH2_PATH: "{token_path}",
GVFSConfig.OAUTH2_CREDENTIAL: "{client_id}:{client_secret}",
GVFSConfig.OAUTH2_SCOPE: "{scope}",
}
fs = gvfs.GravitinoVirtualFileSystem(
server_uri="https://{gravitino_host}", metalake_name="example", options=options
)
print(fs.ls("gvfs://fileset/sales/raw/events"))
The server must have basic authentication enabled; see
How to authenticate.
from gravitino import gvfs
from gravitino.filesystem.gvfs_config import GVFSConfig
options = {
GVFSConfig.AUTH_TYPE: GVFSConfig.BASIC_AUTH_TYPE,
GVFSConfig.BASIC_USERNAME: "{username}",
GVFSConfig.BASIC_PASSWORD: "{password}",
}
fs = gvfs.GravitinoVirtualFileSystem(
server_uri="https://{gravitino_host}", metalake_name="example", options=options
)
print(fs.ls("gvfs://fileset/sales/raw/events"))
Examples
Each example assumes GVFS is configured and signed in as in the Quick start. The
Java examples leave the authentication properties out, and the Python examples refer to the
quick start's options dictionary as auth_options.
- Java
- Python
- Hadoop shell
- Java code
- Spark
- TensorFlow
With the jar and core-site.xml from the quick start in place, the Hadoop shell works on any
fileset path. On a Kerberos-secured Hadoop cluster, get a ticket first with kinit.
hadoop fs -ls gvfs://fileset/sales/raw/events/
hadoop fs -mkdir gvfs://fileset/sales/raw/events/2026
hadoop fs -get gvfs://fileset/sales/raw/events/hello.txt ./hello.txt
The application needs gravitino-filesystem-hadoop3-runtime-1.3.1.jar on its classpath.
Configuration conf = new Configuration();
conf.set("fs.AbstractFileSystem.gvfs.impl", "org.apache.gravitino.filesystem.hadoop.Gvfs");
conf.set("fs.gvfs.impl", "org.apache.gravitino.filesystem.hadoop.GravitinoVirtualFileSystem");
conf.set("fs.gravitino.server.uri", "https://{gravitino_host}");
conf.set("fs.gravitino.client.metalake", "example");
// authentication settings from the quick start
Path filesetPath = new Path("gvfs://fileset/sales/raw/events");
FileSystem fs = filesetPath.getFileSystem(conf);
for (FileStatus status : fs.listStatus(filesetPath)) {
System.out.println(status.getPath());
}
Spark takes the jar with --packages and the GVFS properties as spark.hadoop. options. To bundle
the jar with Spark instead, copy it into ${SPARK_HOME}/jars/.
${SPARK_HOME}/bin/spark-submit \
--packages org.apache.gravitino:gravitino-filesystem-hadoop3-runtime:1.3.1 \
--conf spark.hadoop.fs.AbstractFileSystem.gvfs.impl=org.apache.gravitino.filesystem.hadoop.Gvfs \
--conf spark.hadoop.fs.gvfs.impl=org.apache.gravitino.filesystem.hadoop.GravitinoVirtualFileSystem \
--conf spark.hadoop.fs.gravitino.server.uri=https://{gravitino_host} \
--conf spark.hadoop.fs.gravitino.client.metalake=example \
--conf spark.hadoop.fs.gravitino.client.authType=oauth2 \
--conf spark.hadoop.fs.gravitino.client.oauth2.serverUri=https://{idp_host} \
--conf spark.hadoop.fs.gravitino.client.oauth2.path={token_path} \
--conf spark.hadoop.fs.gravitino.client.oauth2.credential={client_id}:{client_secret} \
--conf spark.hadoop.fs.gravitino.client.oauth2.scope={scope} \
{application}
val spark = SparkSession.builder().appName("GVFS example").getOrCreate()
val lines = spark.read.textFile("gvfs://fileset/sales/raw/events/")
lines.show()
TensorFlow reaches GVFS through Hadoop, so it needs a
tensorflow-io build that includes
this patch, compiled as described in its
development guide. Hadoop needs
the jar and core-site.xml from the quick start.
export HADOOP_HOME={hadoop_home}
export HADOOP_CONF_DIR=${HADOOP_HOME}/etc/hadoop
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$JAVA_HOME/jre/lib/amd64/server
export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
export CLASSPATH="$(hadoop classpath --glob)"
import tensorflow as tf
import tensorflow_io as tfio
print(tf.io.read_file("gvfs://fileset/sales/raw/events/hello.txt"))
print(tf.io.gfile.listdir("gvfs://fileset/sales/raw/events/"))
Filesets on HDFS also need a Hadoop client environment on the Python host, because GVFS reaches HDFS
through libhdfs. Set HADOOP_HOME, HADOOP_CONF_DIR, and
CLASSPATH=$($HADOOP_HOME/bin/hdfs classpath --glob), and on a Kerberos-secured cluster get a
ticket with kinit. On macOS and Windows, rebuild Hadoop's native libraries for your Hadoop version
as described in its building instructions.
Filesets on object storage need none of this.
- fsspec
- Pandas
- PyArrow
- Ray
- LlamaIndex
GVFS implements the fsspec interface, so the usual file system calls work on fileset paths.
from gravitino import gvfs
fs = gvfs.GravitinoVirtualFileSystem(
server_uri="https://{gravitino_host}",
metalake_name="example",
options=auth_options, # the options dictionary from the quick start
)
base = "gvfs://fileset/sales/raw/events"
fs.ls(f"{base}/") # list
fs.info(f"{base}/hello.txt") # file details
fs.exists(f"{base}/hello.txt") # existence check
with fs.open(f"{base}/notes.txt", mode="wb") as f:
f.write(b"first line\n")
with fs.open(f"{base}/notes.txt", mode="ab") as f:
f.write(b"second line\n")
print(fs.cat_file(f"{base}/notes.txt"))
fs.cp_file(f"{base}/notes.txt", f"{base}/notes-copy.txt")
fs.mv(f"{base}/notes-copy.txt", f"{base}/archive/notes.txt")
fs.get_file(f"{base}/notes.txt", "/tmp/notes.txt") # download
fs.makedirs(f"{base}/2026/10")
fs.rm(f"{base}/2026", recursive=True)
fs.rm_file(f"{base}/archive/notes.txt")
Pandas takes the GVFS settings as storage_options.
import pandas as pd
storage_options = {
"server_uri": "https://{gravitino_host}",
"metalake_name": "example",
# authentication settings from the quick start
}
data = pd.DataFrame({"name": ["A", "B", "C"], "id": [20, 21, 19]})
data.to_parquet("gvfs://fileset/sales/raw/events/people.parquet", storage_options=storage_options)
print(pd.read_parquet("gvfs://fileset/sales/raw/events/people.parquet", storage_options=storage_options))
data.to_csv("gvfs://fileset/sales/raw/events/people.csv", storage_options=storage_options)
print(pd.read_csv("gvfs://fileset/sales/raw/events/people.csv", storage_options=storage_options))
import pyarrow.dataset as ds
import pyarrow.parquet as pq
from gravitino import gvfs
fs = gvfs.GravitinoVirtualFileSystem(
server_uri="https://{gravitino_host}",
metalake_name="example",
options=auth_options, # the options dictionary from the quick start
)
dataset = ds.dataset("gvfs://fileset/sales/raw/events/people.parquet", filesystem=fs)
table = pq.read_table("gvfs://fileset/sales/raw/events/people.parquet", filesystem=fs)
import ray
from gravitino import gvfs
fs = gvfs.GravitinoVirtualFileSystem(
server_uri="https://{gravitino_host}",
metalake_name="example",
options=auth_options, # the options dictionary from the quick start
)
dataset = ray.data.read_parquet("gvfs://fileset/sales/raw/events/people.parquet", filesystem=fs)
LlamaIndex takes the path without the gvfs:// scheme.
from gravitino import gvfs
from llama_index.core import SimpleDirectoryReader
fs = gvfs.GravitinoVirtualFileSystem(
server_uri="https://{gravitino_host}",
metalake_name="example",
options=auth_options, # the options dictionary from the quick start
)
reader = SimpleDirectoryReader(
input_dir="fileset/sales/raw/events/docs",
fs=fs,
recursive=True,
)
documents = reader.load_data()