Skip to content

add seed executable #881

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ WEB_ASSETS_SOURCE_PATH = { value = "ui/dist", relative = true }
WEB_ASSETS_TARBALL_PATH = { value = "ui/dist.tar", relative = true }

[alias]
dev = "run -- --backend=memory --cors-allow-origin=http://127.0.0.1:8080 --cors-enabled=true"
auth_demo = "run -- --jwt-secret=test --backend=memory --cors-allow-origin=http://127.0.0.1:8080 --cors-enabled=true"
dev = "run -- --backend=memory --cors-allow-origin=http://localhost:8080 --cors-enabled=true"
auth_demo = "run -- --jwt-secret=test --backend=memory --cors-allow-origin=http://localhost:8080 --cors-enabled=true"
lint = "clippy --all-targets --workspace"
embucket-seed = "run -p embucket-seed -- --server-address 127.0.0.1:3000 --auth-user embucket --auth-password embucket --seed-variant typical"
41 changes: 41 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
default-members = ["crates/embucketd"]
members = [
"crates/embucketd",
"crates/embucket-seed",
"crates/api-iceberg-rest",
"crates/api-internal-rest",
"crates/api-snowflake-rest",
Expand Down
3 changes: 3 additions & 0 deletions crates/api-ui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ version = "0.1.0"
edition = "2024"
license-file.workspace = true

[features]
client = []

[dependencies]
api-ui-static-assets = { path = "../api-ui-static-assets" }
api-sessions = { path = "../api-sessions" }
Expand Down
6 changes: 3 additions & 3 deletions crates/api-ui/src/auth/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use utoipa::ToSchema;

#[derive(Clone, PartialEq, Eq, Serialize, ToSchema)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, derive(Debug, Deserialize))]
#[cfg_attr(any(test, feature = "client"), derive(Debug, Deserialize))]
pub struct AuthResponse {
pub access_token: String,
pub token_type: String,
Expand All @@ -23,13 +23,13 @@ impl AuthResponse {

#[derive(Serialize, Clone, PartialEq, Eq, ToSchema)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, derive(Debug, Deserialize))]
#[cfg_attr(any(test, feature = "client"), derive(Debug, Deserialize))]
pub struct RefreshTokenResponse {
pub access_token: String,
}

#[derive(Deserialize, Clone, PartialEq, Eq, ToSchema)]
#[cfg_attr(test, derive(Serialize))]
#[cfg_attr(any(test, feature = "client"), derive(Debug, Serialize))]
pub struct LoginPayload {
pub username: String,
pub password: String,
Expand Down
1 change: 1 addition & 0 deletions crates/api-ui/src/layers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub async fn add_request_metadata(
request_id,
auth_details: AuthDetails::Unauthenticated,
});
tracing::debug!("Request headers: {:#?}", request.headers());
let mut response = next.run(request).await;
response
.headers_mut()
Expand Down
2 changes: 2 additions & 0 deletions crates/api-ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ pub mod router;
pub mod schemas;
pub mod state;
pub mod tables;
pub mod test_server;

#[cfg(test)]
pub mod tests;
pub mod volumes;
Expand Down
106 changes: 106 additions & 0 deletions crates/api-ui/src/test_server.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use crate::auth::layer::require_auth;
use crate::auth::router as auth_router;
use crate::layers::make_cors_middleware;
use crate::router;
use crate::state;
use crate::{config::AuthConfig, config::WebConfig};
use api_sessions::{RequestSessionMemory, RequestSessionStore};
use axum::Router;
use axum::middleware;
use core_executor::service::CoreExecutionService;
use core_executor::utils::DataSerializationFormat;
use core_history::RecordingExecutionService;
use core_history::store::SlateDBWorksheetsStore;
use core_metastore::SlateDBMetastore;
use core_utils::Db;
use std::net::SocketAddr;
use std::sync::Arc;
use time::Duration;
use tower_sessions::{Expiry, SessionManagerLayer};

#[allow(clippy::unwrap_used)]
pub async fn run_test_server_with_demo_auth(
jwt_secret: String,
demo_user: String,
demo_password: String,
) -> SocketAddr {
let listener = tokio::net::TcpListener::bind("0.0.0.0:0").await.unwrap();
let addr = listener.local_addr().unwrap();

let db = Db::memory().await;
let metastore = Arc::new(SlateDBMetastore::new(db.clone()));
let history = Arc::new(SlateDBWorksheetsStore::new(db));
let mut auth_config = AuthConfig::new(jwt_secret);
auth_config.with_demo_credentials(demo_user, demo_password);

let app = make_app(
metastore,
history,
&WebConfig {
port: 3000,
host: "0.0.0.0".to_string(),
allow_origin: None,
},
auth_config,
)
.unwrap();

tokio::spawn(async move {
axum::serve(listener, app).await.unwrap();
});

addr
}

#[allow(clippy::unwrap_used)]
pub async fn run_test_server() -> SocketAddr {
run_test_server_with_demo_auth(String::new(), String::new(), String::new()).await
}

#[allow(clippy::needless_pass_by_value)]
pub fn make_app(
metastore: Arc<SlateDBMetastore>,
history_store: Arc<SlateDBWorksheetsStore>,
config: &WebConfig,
auth_config: AuthConfig,
) -> Result<Router, Box<dyn std::error::Error>> {
let execution_svc = Arc::new(CoreExecutionService::new(metastore.clone()));
let execution_svc = Arc::new(RecordingExecutionService::new(
execution_svc,
history_store.clone(),
DataSerializationFormat::Json,
));
let session_memory = RequestSessionMemory::default();
let session_store = RequestSessionStore::new(session_memory, execution_svc.clone());
let session_layer = SessionManagerLayer::new(session_store)
.with_secure(false)
.with_expiry(Expiry::OnInactivity(Duration::seconds(5 * 60)));

// Create the application state
let app_state = state::AppState::new(
metastore,
history_store,
execution_svc,
Arc::new(config.clone()),
Arc::new(auth_config),
);

let ui_router = router::create_router().with_state(app_state.clone());
let ui_router = ui_router.layer(middleware::from_fn_with_state(
app_state.clone(),
require_auth,
));
let mut router = Router::new()
.nest("/ui", ui_router)
.nest(
"/ui/auth",
auth_router::create_router().with_state(app_state),
)
.layer(session_layer);

if let Some(allow_origin) = config.allow_origin.as_ref() {
router = router.layer(make_cors_middleware(allow_origin));
}

Ok(router)
}
107 changes: 1 addition & 106 deletions crates/api-ui/src/tests/server.rs
Original file line number Diff line number Diff line change
@@ -1,106 +1 @@
use crate::auth::layer::require_auth;
use crate::auth::router as auth_router;
use crate::layers::make_cors_middleware;
use crate::router;
use crate::state;
use crate::{config::AuthConfig, config::WebConfig};
use api_sessions::{RequestSessionMemory, RequestSessionStore};
use axum::Router;
use axum::middleware;
use core_executor::service::CoreExecutionService;
use core_executor::utils::DataSerializationFormat;
use core_history::RecordingExecutionService;
use core_history::store::SlateDBWorksheetsStore;
use core_metastore::SlateDBMetastore;
use core_utils::Db;
use std::net::SocketAddr;
use std::sync::Arc;
use time::Duration;
use tower_sessions::{Expiry, SessionManagerLayer};

#[allow(clippy::unwrap_used)]
pub async fn run_test_server_with_demo_auth(
jwt_secret: String,
demo_user: String,
demo_password: String,
) -> SocketAddr {
let listener = tokio::net::TcpListener::bind("0.0.0.0:0").await.unwrap();
let addr = listener.local_addr().unwrap();

let db = Db::memory().await;
let metastore = Arc::new(SlateDBMetastore::new(db.clone()));
let history = Arc::new(SlateDBWorksheetsStore::new(db));
let mut auth_config = AuthConfig::new(jwt_secret);
auth_config.with_demo_credentials(demo_user, demo_password);

let app = make_app(
metastore,
history,
&WebConfig {
port: 3000,
host: "0.0.0.0".to_string(),
allow_origin: None,
},
auth_config,
)
.unwrap();

tokio::spawn(async move {
axum::serve(listener, app).await.unwrap();
});

addr
}

#[allow(clippy::unwrap_used)]
pub async fn run_test_server() -> SocketAddr {
run_test_server_with_demo_auth(String::new(), String::new(), String::new()).await
}

#[allow(clippy::needless_pass_by_value)]
pub fn make_app(
metastore: Arc<SlateDBMetastore>,
history_store: Arc<SlateDBWorksheetsStore>,
config: &WebConfig,
auth_config: AuthConfig,
) -> Result<Router, Box<dyn std::error::Error>> {
let execution_svc = Arc::new(CoreExecutionService::new(metastore.clone()));
let execution_svc = Arc::new(RecordingExecutionService::new(
execution_svc,
history_store.clone(),
DataSerializationFormat::Json,
));
let session_memory = RequestSessionMemory::default();
let session_store = RequestSessionStore::new(session_memory, execution_svc.clone());
let session_layer = SessionManagerLayer::new(session_store)
.with_secure(false)
.with_expiry(Expiry::OnInactivity(Duration::seconds(5 * 60)));

// Create the application state
let app_state = state::AppState::new(
metastore,
history_store,
execution_svc,
Arc::new(config.clone()),
Arc::new(auth_config),
);

let ui_router = router::create_router().with_state(app_state.clone());
let ui_router = ui_router.layer(middleware::from_fn_with_state(
app_state.clone(),
require_auth,
));
let mut router = Router::new()
.nest("/ui", ui_router)
.nest(
"/ui/auth",
auth_router::create_router().with_state(app_state),
)
.layer(session_layer);

if let Some(allow_origin) = config.allow_origin.as_ref() {
router = router.layer(make_cors_middleware(allow_origin));
}

Ok(router)
}
pub use crate::test_server::*;
2 changes: 1 addition & 1 deletion crates/api-ui/src/volumes/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub struct S3TablesVolume {

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, Eq, PartialEq)]
pub struct FileVolume {
path: String,
pub path: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, Eq, PartialEq)]
Expand Down
36 changes: 36 additions & 0 deletions crates/embucket-seed/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[package]
name = "embucket-seed"
version = "0.1.0"
edition = "2024"
license-file.workspace = true

[lib]
path = "src/lib.rs"

[[bin]]
name = "embucket-seed"
path = "src/bin/main.rs"

[dependencies]
core-metastore = { path = "../core-metastore" }
api-ui = { path = "../api-ui", features = ["client"] }

async-trait = { workspace = true }
cookie = "0.18.1"
http = { workspace = true }
serde_yaml = { workspace = true }
serde = { workspace = true }
snafu = { workspace = true }
fake = { version = "4.3.0", features = ["chrono"] }
reqwest = "0.12.14"
rand = "0.9.1"
serde_json = { workspace = true }
chrono = { workspace = true }
tokio = { workspace = true }
clap = { version = "4.5.27", features = ["env", "derive"] }
tracing = { workspace = true }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
uuid = { workspace = true}

[lints]
workspace = true
Loading