diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml index 6cfc387..318244b 100644 --- a/.idea/dataSources.xml +++ b/.idea/dataSources.xml @@ -1,12 +1,19 @@ - + postgresql true org.postgresql.Driver jdbc:postgresql:///acrate $ProjectFileDir$ + + postgresql + true + org.postgresql.Driver + jdbc:postgresql://localhost:5432/postgres + $ProjectFileDir$ + \ No newline at end of file diff --git a/.idea/jsonSchemas.xml b/.idea/jsonSchemas.xml new file mode 100644 index 0000000..da97271 --- /dev/null +++ b/.idea/jsonSchemas.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Check.xml b/.idea/runConfigurations/Check.xml new file mode 100644 index 0000000..048d405 --- /dev/null +++ b/.idea/runConfigurations/Check.xml @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/acrate_database/Cargo.toml b/acrate_database/Cargo.toml index ba63f83..d2afc81 100644 --- a/acrate_database/Cargo.toml +++ b/acrate_database/Cargo.toml @@ -13,7 +13,7 @@ categories = ["database"] diesel = { version = "2.2.4", features = ["postgres", "uuid"] } diesel-async = { version = "0.5.1", features = ["postgres"] } diesel_migrations = { version = "2.2.0", optional = true } -log = "0.4.22" +log = { version = "0.4.22", features = ["std", "max_level_trace", "release_max_level_debug"] } mediatype = "0.19.18" micronfig = { version = "0.3.0", optional = true } mime = "0.3.17" diff --git a/acrate_docker/Dockerfile b/acrate_docker/Dockerfile new file mode 100644 index 0000000..7af2e9e --- /dev/null +++ b/acrate_docker/Dockerfile @@ -0,0 +1,32 @@ +FROM rust AS base_builder +WORKDIR /usr/src/acrate +COPY --from=source ./acrate_database ./acrate_database +COPY --from=source ./acrate_nodeinfo ./acrate_nodeinfo +COPY --from=source ./acrate_rd ./acrate_rd +COPY --from=source ./acrate_rdserver ./acrate_rdserver +COPY --from=source ./Cargo.toml ./Cargo.toml +COPY --from=source ./Cargo.lock ./Cargo.lock + +FROM rust:slim AS base_runner +RUN apt-get update +RUN apt-get upgrade --assume-yes +RUN apt-get install --assume-yes libpq5 +WORKDIR /usr/local/bin +ENV RUST_LOG="warn" + +FROM base_builder AS migrate_build +RUN cargo build --release --package=acrate_database --features=bin --bin=acrate_database_migrate + +FROM base_runner AS migrate +COPY --from=migrate_build /usr/src/acrate/target/release/acrate_database_migrate /usr/local/bin/acrate_database_migrate +ENTRYPOINT ["acrate_database_migrate"] +ENV RUST_LOG="warn,acrate_database_migrate=info" + +FROM base_builder AS rdserver_build +RUN cargo build --release --package=acrate_rdserver --bin=acrate_rdserver + +FROM base_runner AS rdserver +COPY --from=rdserver_build /usr/src/acrate/target/release/acrate_rdserver /usr/local/bin/acrate_rdserver +ENTRYPOINT ["acrate_rdserver"] +HEALTHCHECK CMD ["curl", "http://127.0.0.1/.healthcheck"] +ENV RUST_LOG="warn,acrate_rdserver=info" diff --git a/acrate_docker/compose.yml b/acrate_docker/compose.yml new file mode 100644 index 0000000..03e4a32 --- /dev/null +++ b/acrate_docker/compose.yml @@ -0,0 +1,95 @@ +# Full acrate stack, running on a single machine for experimental purposes + +x-config: + ingress_config_dir: &ingress_config_dir "./config/caddy" + +name: "acrate" + +volumes: + ingress_data: + ingress_config: + postgres_data: + +services: + # Public ingress node + ingress: + image: "caddy" + restart: "unless-stopped" + cap_add: + - "NET_ADMIN" + ports: + - protocol: "tcp" + target: 80 + published: 80 + - protocol: "tcp" + target: 443 + published: 443 + - protocol: "udp" + target: 443 + published: 443 + volumes: + - type: "volume" + source: "ingress_data" + target: "/data" + - type: "volume" + source: "ingress_config" + target: "/config" + - type: "bind" + source: *ingress_config_dir + target: "/etc/caddy" + + # Main database + database: + image: "postgres" + restart: "unless-stopped" + environment: + # Make sure the password is the same for both client and server tools + POSTGRES_PASSWORD: &postgres_password "acrate" + PGUSER: "postgres" + PGPASS: *postgres_password + volumes: + - type: "volume" + source: "postgres_data" + target: "/var/lib/postgresql/data" + ports: + # FIXME: for development purposes only + - protocol: "tcp" + host_ip: "127.0.0.1" + target: 5432 + published: 5432 + expose: + - 5432 + healthcheck: + test: ["CMD-SHELL", "pg_isready"] + + # Migrations + migrate: + build: + dockerfile: "./Dockerfile" + additional_contexts: + - "source=.." + target: "migrate" + environment: + ACRATE_DATABASE_DATABASE_URL: &database_url "postgres:///postgres?host=database&user=postgres&password=acrate" # TODO: Split parameters off to their own envvars + depends_on: + database: + condition: "service_healthy" + + # Resource descriptor server + rdserver: + build: + dockerfile: "./Dockerfile" + additional_contexts: + - "source=.." + target: "rdserver" + restart: "unless-stopped" + environment: + ACRATE_WEBFINGER_BIND_ADDRESS: "0.0.0.0:80" + ACRATE_WEBFINGER_DATABASE_URL: *database_url + expose: + - 80 + depends_on: + database: + condition: "service_healthy" + migrate: + condition: "service_completed_successfully" diff --git a/acrate_docker/config/caddy/Caddyfile b/acrate_docker/config/caddy/Caddyfile new file mode 100644 index 0000000..6514746 --- /dev/null +++ b/acrate_docker/config/caddy/Caddyfile @@ -0,0 +1,16 @@ +# replace with your HTTPS domain +:80 { + @rdserver { + path "/.well-known/webfinger" + path "/.well-known/host-meta" + path "/.well-known/host-meta.xml" + path "/.well-known/host-meta.json" + } + + reverse_proxy @rdserver { + to "http://rdserver" + + health_uri "/.healthcheck" + health_status "204" + } +} diff --git a/acrate_nodeinfo/Cargo.toml b/acrate_nodeinfo/Cargo.toml index 5e7693e..b137bc0 100644 --- a/acrate_nodeinfo/Cargo.toml +++ b/acrate_nodeinfo/Cargo.toml @@ -11,7 +11,7 @@ categories = ["web-programming"] [dependencies] acrate_rd = { path = "../acrate_rd" } -log = "0.4.22" +log = { version = "0.4.22", features = ["std", "max_level_trace", "release_max_level_debug"] } mediatype = { version = "0.19.18", features = ["serde"] } reqwest = { version = "0.12.9", features = ["json", "stream"] } serde = { version = "1.0.214", features = ["derive"] } diff --git a/acrate_rdserver/Cargo.toml b/acrate_rdserver/Cargo.toml index 4752f69..43b0148 100644 --- a/acrate_rdserver/Cargo.toml +++ b/acrate_rdserver/Cargo.toml @@ -15,7 +15,7 @@ acrate_rd = { path = "../acrate_rd" } anyhow = "1.0.93" axum = { version = "0.7.7", features = ["macros"] } axum-extra = { version = "0.9.4", features = ["query"] } -log = "0.4.22" +log = { version = "0.4.22", features = ["std", "max_level_trace", "release_max_level_debug"] } micronfig = "0.3.0" minijinja = "2.5.0" pretty_env_logger = "0.5.0" diff --git a/acrate_rdserver/src/main.rs b/acrate_rdserver/src/main.rs index 3752cbf..60dffed 100644 --- a/acrate_rdserver/src/main.rs +++ b/acrate_rdserver/src/main.rs @@ -21,6 +21,7 @@ async fn main() -> anyhow::Result { log::trace!("Creating Axum router..."); let app = axum::Router::new() .route("/*path", axum::routing::get(route::webfinger_handler)) + .route("/.healthcheck", axum::routing::get(route::healthcheck_handler)) .layer(Extension(Arc::new(mj))); log::trace!("Axum router created successfully!"); @@ -31,7 +32,7 @@ async fn main() -> anyhow::Result { .context("failed to bind listener to address")?; log::trace!("Tokio listener bound to: {bind_address}"); - log::debug!("Starting server..."); + log::info!("Starting server..."); axum::serve(listener, app) .await .context("server exited with error")?; diff --git a/acrate_rdserver/src/route.rs b/acrate_rdserver/src/route.rs index 61ba271..9821182 100644 --- a/acrate_rdserver/src/route.rs +++ b/acrate_rdserver/src/route.rs @@ -13,6 +13,18 @@ use acrate_rd::jrd::ResourceDescriptorLinkJRD; use acrate_rd::xrd::{ResourceDescriptorLinkXRD, ResourceDescriptorPropertyXRD, ResourceDescriptorTitleXRD}; use crate::config; +pub async fn healthcheck_handler() -> Result { + log::debug!("Handling an healthcheck request!"); + + log::trace!("Making sure the database is up..."); + let _conn = AsyncPgConnection::establish(config::ACRATE_WEBFINGER_DATABASE_URL()) + .await + .map_err(|_| StatusCode::BAD_GATEWAY)?; + + log::trace!("Healthcheck successful! Everything's fine!"); + Ok(StatusCode::NO_CONTENT) +} + #[derive(Debug, Clone, Deserialize)] pub struct WebfingerQuery { pub resource: Option, @@ -27,7 +39,7 @@ pub async fn webfinger_handler( headers: HeaderMap, Extension(mj): Extension>>, ) -> Result, StatusCode> { - log::info!("Handling a WebFinger request!"); + log::debug!("Handling a WebFinger request!"); let resource = resource.unwrap_or_else(|| "".to_string()); log::debug!("Resource is: {resource:#?}");