Skip to content

Commit 4eb3aa3

Browse files
committed
feat: Wire VFS cloud storage integration for S3, GCS, Azure support
- Add StorageConfig and StorageCacheConfig structs for storage cache settings - Implement parseStorageConfig() to parse storage.cache section from YAML - Wire CachingFileProvider into getFileProvider() for remote template paths - Initialize cloud credentials from environment at startup - Configure credentials in DuckDB after database initialization - Add VFSHealthChecker startup verification for storage backends Users can now configure remote file caching in flapi.yaml: storage: cache: enabled: true ttl: 300 # seconds max_size_mb: 50 # megabytes
1 parent c4a1b0e commit 4eb3aa3

3 files changed

Lines changed: 99 additions & 1 deletion

File tree

src/config_manager.cpp

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include "endpoint_repository.hpp"
55
#include "config_validator.hpp"
66
#include "config_serializer.hpp"
7+
#include "caching_file_provider.hpp"
8+
#include "vfs_adapter.hpp"
79
#include <stdexcept>
810
#include <filesystem>
911
#include <yaml-cpp/yaml.h>
@@ -107,6 +109,7 @@ void ConfigManager::parseMainConfig() {
107109
parseDuckDBConfig();
108110
parseDuckLakeConfig();
109111
parseMCPConfig();
112+
parseStorageConfig();
110113
parseTemplateConfig();
111114
parseGlobalHeartbeatConfig();
112115

@@ -231,6 +234,38 @@ void ConfigManager::parseDuckLakeConfig() {
231234
}
232235
}
233236

237+
// Storage configuration methods
238+
void ConfigManager::parseStorageConfig() {
239+
CROW_LOG_INFO << "Parsing storage configuration";
240+
storage_config = StorageConfig{}; // Reset to defaults
241+
242+
if (!config["storage"]) {
243+
CROW_LOG_DEBUG << "Storage configuration not found, using defaults (cache.enabled=true, cache.ttl=300s, cache.max_size_mb=50)";
244+
return;
245+
}
246+
247+
auto storage_node = config["storage"];
248+
249+
if (storage_node["cache"]) {
250+
auto cache_node = storage_node["cache"];
251+
storage_config.cache.enabled = safeGet<bool>(cache_node, "enabled", "storage.cache.enabled", true);
252+
253+
if (cache_node["ttl"]) {
254+
int ttl_seconds = safeGet<int>(cache_node, "ttl", "storage.cache.ttl", 300);
255+
storage_config.cache.ttl = std::chrono::seconds(ttl_seconds);
256+
CROW_LOG_DEBUG << "Storage cache TTL: " << ttl_seconds << " seconds";
257+
}
258+
259+
if (cache_node["max_size_mb"]) {
260+
int max_mb = safeGet<int>(cache_node, "max_size_mb", "storage.cache.max_size_mb", 50);
261+
storage_config.cache.max_size_bytes = static_cast<size_t>(max_mb) * 1024UL * 1024UL;
262+
CROW_LOG_DEBUG << "Storage cache max size: " << max_mb << " MB";
263+
}
264+
265+
CROW_LOG_DEBUG << "Storage cache enabled: " << (storage_config.cache.enabled ? "true" : "false");
266+
}
267+
}
268+
234269
// MCP configuration methods
235270
void ConfigManager::parseMCPConfig() {
236271
CROW_LOG_INFO << "Parsing MCP configuration";
@@ -1035,7 +1070,20 @@ int ConfigManager::getHttpPort() const { return http_port; }
10351070
void ConfigManager::setHttpPort(int port) { http_port = port; }
10361071
std::string ConfigManager::getTemplatePath() const { return template_config.path; }
10371072
std::filesystem::path ConfigManager::getFullTemplatePath() const { return std::filesystem::path(base_path) / template_config.path; }
1038-
std::shared_ptr<IFileProvider> ConfigManager::getFileProvider() const { return config_loader->getFileProvider(); }
1073+
std::shared_ptr<IFileProvider> ConfigManager::getFileProvider() const {
1074+
// For remote template paths with caching enabled, wrap with CachingFileProvider
1075+
if (storage_config.cache.enabled && PathSchemeUtils::IsRemotePath(template_config.path)) {
1076+
FileCacheConfig cache_config;
1077+
cache_config.enabled = true;
1078+
cache_config.ttl = storage_config.cache.ttl;
1079+
cache_config.max_size_bytes = storage_config.cache.max_size_bytes;
1080+
1081+
auto base_provider = FileProviderFactory::CreateDuckDBProvider();
1082+
return std::make_shared<CachingFileProvider>(base_provider, cache_config);
1083+
}
1084+
1085+
return config_loader->getFileProvider();
1086+
}
10391087
std::string ConfigManager::getCacheSchema() const { return cache_schema; }
10401088
const std::unordered_map<std::string, ConnectionConfig>& ConfigManager::getConnections() const { return connections; }
10411089
const RateLimitConfig& ConfigManager::getRateLimitConfig() const { return rate_limit_config; }

src/include/config_manager.hpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,16 @@ struct DuckLakeConfig {
442442
std::optional<std::size_t> data_inlining_row_limit;
443443
};
444444

445+
struct StorageCacheConfig {
446+
bool enabled = true;
447+
std::chrono::seconds ttl{300}; // 5 minutes default
448+
size_t max_size_bytes = 50UL * 1024UL * 1024UL; // 50 MB default
449+
};
450+
451+
struct StorageConfig {
452+
StorageCacheConfig cache;
453+
};
454+
445455
// Forward declarations
446456
class EndpointConfigParser;
447457
class ConfigLoader;
@@ -501,6 +511,7 @@ class ConfigManager {
501511
const GlobalHeartbeatConfig& getGlobalHeartbeatConfig() const { return global_heartbeat_config; }
502512
const DuckLakeConfig& getDuckLakeConfig() const { return ducklake_config; }
503513
const MCPConfig& getMCPConfig() const { return mcp_config; }
514+
const StorageConfig& getStorageConfig() const { return storage_config; }
504515

505516
// Load MCP server instructions (inline or from file)
506517
std::string loadMCPInstructions() const;
@@ -559,6 +570,7 @@ class ConfigManager {
559570
GlobalHeartbeatConfig global_heartbeat_config;
560571
DuckLakeConfig ducklake_config;
561572
MCPConfig mcp_config;
573+
StorageConfig storage_config;
562574
ExtendedYamlParser yaml_parser;
563575

564576
// Extracted classes for delegation (Facade pattern)
@@ -580,6 +592,7 @@ class ConfigManager {
580592
void parseTemplateConfig();
581593
void parseDuckLakeConfig();
582594
void parseMCPConfig();
595+
void parseStorageConfig();
583596
void parseEndpointConfig(const std::filesystem::path& config_file);
584597
void parseEndpointRequestFields(const YAML::Node& endpoint_config, EndpointConfig& endpoint);
585598
void parseEndpointValidators(const YAML::Node& req, RequestFieldConfig& field);

src/main.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include "database_manager.hpp"
2323
#include "rate_limit_middleware.hpp"
2424
#include "config_token_utils.hpp"
25+
#include "credential_manager.hpp"
26+
#include "vfs_health_checker.hpp"
2527

2628
using namespace flapi;
2729

@@ -137,6 +139,32 @@ void initializeDatabase(std::shared_ptr<ConfigManager> config_manager) {
137139
}
138140
}
139141

142+
void initializeCloudCredentials() {
143+
CROW_LOG_INFO << "Initializing cloud storage credentials...";
144+
auto& cred_manager = flapi::getGlobalCredentialManager();
145+
cred_manager.loadFromEnvironment();
146+
cred_manager.logCredentialStatus();
147+
}
148+
149+
void configureCloudCredentialsInDuckDB() {
150+
auto& cred_manager = flapi::getGlobalCredentialManager();
151+
if (cred_manager.hasS3Credentials() || cred_manager.hasGCSCredentials() || cred_manager.hasAzureCredentials()) {
152+
CROW_LOG_INFO << "Configuring cloud credentials in DuckDB...";
153+
if (cred_manager.configureDuckDB()) {
154+
CROW_LOG_INFO << "Cloud credentials configured successfully";
155+
} else {
156+
CROW_LOG_WARNING << "Failed to configure some cloud credentials in DuckDB";
157+
}
158+
}
159+
}
160+
161+
void verifyStorageHealth(std::shared_ptr<ConfigManager> config_manager) {
162+
flapi::VFSHealthChecker health_checker;
163+
std::string config_path = config_manager->getBasePath();
164+
std::string templates_path = config_manager->getTemplatePath();
165+
health_checker.verifyStartupHealth(config_path, templates_path);
166+
}
167+
140168
void terminateHandler() {
141169
CROW_LOG_ERROR << "Unhandled exception caught! flapi is giving up :-(";
142170

@@ -282,12 +310,21 @@ int main(int argc, char* argv[])
282310
return validateConfiguration(config_manager, config_file);
283311
}
284312

313+
// Initialize cloud storage credentials (reads environment variables)
314+
initializeCloudCredentials();
315+
285316
if (cmd_port != -1) {
286317
config_manager->setHttpPort(cmd_port);
287318
}
288319

289320
initializeDatabase(config_manager);
290321

322+
// Configure cloud credentials in DuckDB after database is initialized
323+
configureCloudCredentialsInDuckDB();
324+
325+
// Verify storage health at startup
326+
verifyStorageHealth(config_manager);
327+
291328
// Create unified API server with MCP support (always enabled in unified configuration)
292329
api_server = std::make_shared<APIServer>(
293330
config_manager,

0 commit comments

Comments
 (0)