Compare commits

..

2 commits

Author SHA1 Message Date
Aravinth Manivannan 162e815cd5
feat: CI: build docker and bin in all circumstances but publish only on master
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-12-25 16:52:26 +05:30
Aravinth Manivannan 8f4ac4ed38
feat: add telemetery instrumentation using tracing 2022-12-25 16:44:55 +05:30
12 changed files with 118 additions and 84 deletions

View file

@ -5,22 +5,39 @@ pipeline:
# - DATABASE_URL=postgres://postgres:password@database:5432/postgres # - DATABASE_URL=postgres://postgres:password@database:5432/postgres
commands: commands:
# - make migrate # - make migrate
- make
- make test - make test
- make release - make release
publish_bins: publish_bins:
image: rust image: rust
when:
event: [push]
branch: master
commands: commands:
- apt update - apt update
- apt-get -y --no-install-recommends install gpg tar curl wget - apt-get -y --no-install-recommends install gpg tar curl wget
- echo -n "$RELEASE_BOT_GPG_SIGNING_KEY" | gpg --batch --import --pinentry-mode loopback - echo -n "$RELEASE_BOT_GPG_SIGNING_KEY" | gpg --batch --import --pinentry-mode loopback
- ./scripts/bin-publish.sh publish master latest $DUMBSERVE_PASSWORD - make release
- ./scripts/bin-publish.sh publish master latest $DUMBSERVE_PASSWORD
secrets: [ RELEASE_BOT_GPG_SIGNING_KEY, DUMBSERVE_PASSWORD, GPG_PASSWORD ] secrets: [ RELEASE_BOT_GPG_SIGNING_KEY, DUMBSERVE_PASSWORD, GPG_PASSWORD ]
build_docker_img:
image: plugins/docker
when:
branch: !master
settings:
dry_run: true
username: realaravinth
password:
from_secret: DOCKER_TOKEN
repo: realaravinth/librepages-conductor
tags: latest
publish: publish:
image: plugins/docker image: plugins/docker
when:
event: [push]
branch: master
settings: settings:
username: realaravinth username: realaravinth
password: password:

56
Cargo.lock generated
View file

@ -438,13 +438,14 @@ dependencies = [
"lazy_static", "lazy_static",
"libconductor", "libconductor",
"libconfig", "libconfig",
"log",
"mime_guess", "mime_guess",
"pretty_env_logger", "pretty_env_logger",
"rust-embed", "rust-embed",
"serde", "serde",
"serde_json", "serde_json",
"sqlx", "sqlx",
"tracing",
"tracing-actix-web",
"url", "url",
] ]
@ -1360,6 +1361,26 @@ dependencies = [
"sha1", "sha1",
] ]
[[package]]
name = "pin-project"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.9" version = "0.2.9"
@ -2058,9 +2079,33 @@ dependencies = [
"cfg-if", "cfg-if",
"log", "log",
"pin-project-lite", "pin-project-lite",
"tracing-attributes",
"tracing-core", "tracing-core",
] ]
[[package]]
name = "tracing-actix-web"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d16c2a0c52b267d46ea9a46012a28b3513ce166c28eaeaa875829ed2f8debd19"
dependencies = [
"actix-web",
"pin-project",
"tracing",
"uuid",
]
[[package]]
name = "tracing-attributes"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "tracing-core" name = "tracing-core"
version = "0.1.30" version = "0.1.30"
@ -2142,6 +2187,15 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "uuid"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c"
dependencies = [
"getrandom",
]
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.4" version = "0.9.4"

View file

@ -15,7 +15,6 @@ actix-web = "4"
actix-web-prom = "0.6.0" actix-web-prom = "0.6.0"
futures-util = { version = "0.3.17", default-features = false, features = ["std"] } futures-util = { version = "0.3.17", default-features = false, features = ["std"] }
lazy_static = "1.4.0" lazy_static = "1.4.0"
log = "0.4.17"
pretty_env_logger = "0.4.0" pretty_env_logger = "0.4.0"
serde = { version = "1", features=["derive"]} serde = { version = "1", features=["derive"]}
actix-web-codegen-const-routes = { version = "0.1.0", tag = "0.1.0", git = "https://github.com/realaravinth/actix-web-codegen-const-routes" } actix-web-codegen-const-routes = { version = "0.1.0", tag = "0.1.0", git = "https://github.com/realaravinth/actix-web-codegen-const-routes" }
@ -29,6 +28,8 @@ clap = { vesrion = "3.2.20", features = ["derive"]}
actix-web-httpauth = "0.8.0" actix-web-httpauth = "0.8.0"
mime_guess = "2.0.4" mime_guess = "2.0.4"
rust-embed = "6.4.2" rust-embed = "6.4.2"
tracing-actix-web = "0.7.1"
tracing = { version = "0.1.37", features = ["log"] }
[dependencies.libconductor] [dependencies.libconductor]
path = "./env/libconductor" path = "./env/libconductor"

View file

@ -3,7 +3,8 @@ source_code = "https://git.batsense.net/librepages/conductor"
conductor = "dummy" conductor = "dummy"
[creds] [creds]
token="longrandomlygeneratedpassword" username = "librepages_api"
password="longrandomlygeneratedpassword"
[server] [server]
# Please set a unique value, your mCaptcha instance's security depends on this being # Please set a unique value, your mCaptcha instance's security depends on this being

View file

@ -1,24 +0,0 @@
[Unit]
Description=LibrePages Conductor: Easiest way to deploy websites. Conductor component
[Service]
Type=simple
User=root
ExecStart=/usr/bin/conductor serve
Restart=on-failure
RestartSec=1
SuccessExitStatus=3 4
RestartForceExitStatus=3 4
SystemCallArchitectures=native
MemoryDenyWriteExecute=true
NoNewPrivileges=true
Environment="RUST_LOG=info"
[Unit]
Wants=network-online.target
Wants=network-online.target
Requires=postgresql.service
After=syslog.target
[Install]
WantedBy=multi-user.target

View file

@ -38,8 +38,6 @@ DOCKER_IMG="realaravinth/$NAME:$3"
get_bin(){ get_bin(){
cp target/release/conductor $TARGET_DIR cp target/release/conductor $TARGET_DIR
cp -r config/ $TARGET_DIR
cp -r contrib/ $TARGET_DIR
} }
copy() { copy() {

View file

@ -46,6 +46,7 @@ pub mod routes {
/// emits build details of the bninary /// emits build details of the bninary
#[actix_web_codegen_const_routes::get(path = "crate::API_V1_ROUTES.meta.build_details")] #[actix_web_codegen_const_routes::get(path = "crate::API_V1_ROUTES.meta.build_details")]
#[tracing::instrument(name = "Get build details", skip(ctx))]
async fn build_details(ctx: AppCtx) -> impl Responder { async fn build_details(ctx: AppCtx) -> impl Responder {
let build = BuildDetails { let build = BuildDetails {
version: VERSION, version: VERSION,
@ -63,6 +64,7 @@ pub struct Health {
/// checks all components of the system /// checks all components of the system
#[actix_web_codegen_const_routes::get(path = "crate::API_V1_ROUTES.meta.health")] #[actix_web_codegen_const_routes::get(path = "crate::API_V1_ROUTES.meta.health")]
#[tracing::instrument(name = "Get health details", skip(ctx))]
async fn health(ctx: crate::AppCtx) -> impl Responder { async fn health(ctx: crate::AppCtx) -> impl Responder {
let mut resp_builder = HealthBuilder::default(); let mut resp_builder = HealthBuilder::default();

View file

@ -17,7 +17,7 @@
use actix_web::dev::ServiceRequest; use actix_web::dev::ServiceRequest;
use actix_web::web; use actix_web::web;
use actix_web::Error; use actix_web::Error;
use actix_web_httpauth::extractors::bearer::BearerAuth; use actix_web_httpauth::extractors::basic::BasicAuth;
use crate::errors::*; use crate::errors::*;
use crate::AppCtx; use crate::AppCtx;
@ -26,13 +26,14 @@ use crate::SETTINGS;
pub mod meta; pub mod meta;
pub mod webhook; pub mod webhook;
pub async fn bearerauth( pub async fn httpauth(
req: ServiceRequest, req: ServiceRequest,
credentials: BearerAuth, credentials: BasicAuth,
) -> Result<ServiceRequest, (Error, ServiceRequest)> { ) -> Result<ServiceRequest, (Error, ServiceRequest)> {
let _ctx: &AppCtx = req.app_data().unwrap(); let _ctx: &AppCtx = req.app_data().unwrap();
let token = credentials.token(); let username = credentials.user_id();
if SETTINGS.authenticate(token) { let password = credentials.password().unwrap();
if SETTINGS.authenticate(username, password) {
Ok(req) Ok(req)
} else { } else {
let e = Error::from(ServiceError::Unauthorized); let e = Error::from(ServiceError::Unauthorized);

View file

@ -24,7 +24,7 @@ use crate::errors::*;
use crate::AppCtx; use crate::AppCtx;
use crate::*; use crate::*;
use super::bearerauth; use super::httpauth;
pub mod routes { pub mod routes {
use super::*; use super::*;
@ -47,8 +47,9 @@ pub fn services(cfg: &mut web::ServiceConfig) {
#[actix_web_codegen_const_routes::post( #[actix_web_codegen_const_routes::post(
path = "API_V1_ROUTES.webhook.post_event", path = "API_V1_ROUTES.webhook.post_event",
wrap = "HttpAuthentication::bearer(bearerauth)" wrap = "HttpAuthentication::basic(httpauth)"
)] )]
#[tracing::instrument(name = "Post events to webhook", skip(ctx, payload))]
async fn post_event(ctx: AppCtx, payload: web::Json<EventType>) -> ServiceResult<impl Responder> { async fn post_event(ctx: AppCtx, payload: web::Json<EventType>) -> ServiceResult<impl Responder> {
ctx.conductor.process(payload.into_inner()).await; ctx.conductor.process(payload.into_inner()).await;
Ok(HttpResponse::Created()) Ok(HttpResponse::Created())
@ -71,7 +72,10 @@ pub mod tests {
.await; .await;
let creds = settings.creds.clone(); let creds = settings.creds.clone();
let auth = format!("Bearer {}", creds.token,); let auth = format!(
"Basic {}",
base64::encode(format!("{}:{}", creds.username.clone(), creds.password))
);
let msg = EventType::NewSite { let msg = EventType::NewSite {
hostname: "demo.librepages.org".into(), hostname: "demo.librepages.org".into(),

View file

@ -73,6 +73,7 @@ pub fn handle_embedded_file(path: &str) -> HttpResponse {
} }
#[actix_web_codegen_const_routes::get(path = "DOCS.assets")] #[actix_web_codegen_const_routes::get(path = "DOCS.assets")]
#[tracing::instrument(name = "Serve Open API docs", skip(path))]
async fn dist(path: web::Path<String>) -> impl Responder { async fn dist(path: web::Path<String>) -> impl Responder {
handle_embedded_file(&path) handle_embedded_file(&path)
} }

View file

@ -21,7 +21,8 @@ use actix_web::web::JsonConfig;
use actix_web::{error::InternalError, middleware, App, HttpServer}; use actix_web::{error::InternalError, middleware, App, HttpServer};
use actix_web_prom::PrometheusMetricsBuilder; use actix_web_prom::PrometheusMetricsBuilder;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use log::info; use tracing::info;
use tracing_actix_web::TracingLogger;
use lazy_static::lazy_static; use lazy_static::lazy_static;
@ -30,34 +31,17 @@ mod ctx;
mod docs; mod docs;
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
mod errors; mod errors;
//#[macro_use]
//mod pages;
//#[macro_use]
mod routes; mod routes;
mod settings; mod settings;
//mod static_assets;
//#[cfg(test)] //#[cfg(test)]
//#[macro_use] //#[macro_use]
//mod tests; //mod tests;
//
pub use crate::ctx::Ctx; pub use crate::ctx::Ctx;
//pub use crate::static_assets::static_files::assets::*;
pub use api::v1::API_V1_ROUTES; pub use api::v1::API_V1_ROUTES;
//pub use docs::DOCS;
//pub use pages::routes::ROUTES as PAGES;
pub use settings::Settings; pub use settings::Settings;
//use static_assets::FileMap;
lazy_static! { lazy_static! {
pub static ref SETTINGS: Settings= Settings::new().unwrap(); pub static ref SETTINGS: Settings = Settings::new().unwrap();
// pub static ref S: String = env::var("S").unwrap();
// pub static ref FILES: FileMap = FileMap::new();
// pub static ref JS: &'static str =
// FILES.get("./static/cache/bundle/bundle.js").unwrap();
// pub static ref CSS: &'static str =
// FILES.get("./static/cache/bundle/css/main.css").unwrap();
// pub static ref MOBILE_CSS: &'static str =
// FILES.get("./static/cache/bundle/css/mobile.css").unwrap();
} }
pub const COMPILED_DATE: &str = env!("COMPILED_DATE"); pub const COMPILED_DATE: &str = env!("COMPILED_DATE");
@ -120,7 +104,7 @@ async fn serve(settings: Settings, ctx: AppCtx) -> std::io::Result<()> {
HttpServer::new(move || { HttpServer::new(move || {
App::new() App::new()
.wrap(middleware::Logger::default()) .wrap(TracingLogger::default())
.wrap( .wrap(
middleware::DefaultHeaders::new().add(("Permissions-Policy", "interest-cohort=()")), middleware::DefaultHeaders::new().add(("Permissions-Policy", "interest-cohort=()")),
) )
@ -140,8 +124,6 @@ async fn serve(settings: Settings, ctx: AppCtx) -> std::io::Result<()> {
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
pub fn get_json_err() -> JsonConfig { pub fn get_json_err() -> JsonConfig {
JsonConfig::default().error_handler(|err, _| { JsonConfig::default()
//debug!("JSON deserialization error: {:?}", &err); .error_handler(|err, _| InternalError::new(err, StatusCode::BAD_REQUEST).into())
InternalError::new(err, StatusCode::BAD_REQUEST).into()
})
} }

View file

@ -19,10 +19,10 @@ use std::path::Path;
use config::{builder::DefaultState, Config, ConfigBuilder, ConfigError, Environment, File}; use config::{builder::DefaultState, Config, ConfigBuilder, ConfigError, Environment, File};
use derive_more::Display; use derive_more::Display;
use log::info;
use log::warn;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use tracing::info;
use tracing::warn;
use url::Url; use url::Url;
const PREFIX: &str = "LPCONDUCTOR"; const PREFIX: &str = "LPCONDUCTOR";
@ -54,7 +54,8 @@ pub enum ConductorType {
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
pub struct Creds { pub struct Creds {
pub token: String, pub username: String,
pub password: String,
} }
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
@ -68,15 +69,15 @@ pub struct Settings {
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
impl Settings { impl Settings {
pub fn authenticate(&self, token: &str) -> bool { pub fn authenticate(&self, username: &str, password: &str) -> bool {
self.creds.token == token self.creds.username == username && self.creds.password == password
} }
pub fn new() -> Result<Self, ConfigError> { pub fn new() -> Result<Self, ConfigError> {
let mut s = Config::builder(); let mut s = Config::builder();
const CURRENT_DIR: &str = "./config/config.toml"; const CURRENT_DIR: &str = "./config/config.toml";
const ETC: &str = "/etc/librepages/conductor/config.toml"; const ETC: &str = "/etc/lpconductor/config.toml";
if let Ok(path) = env::var("LPCONDUCTOR_CONFIG") { if let Ok(path) = env::var("LPCONDUCTOR_CONFIG") {
s = s.add_source(File::with_name(&path)); s = s.add_source(File::with_name(&path));
@ -137,13 +138,6 @@ fn set_separator_field(mut s: ConfigBuilder<DefaultState>) -> ConfigBuilder<Defa
&format!("{PREFIX}{SEPARATOR}SERVER{SEPARATOR}PROXY_HAS_TLS"), &format!("{PREFIX}{SEPARATOR}SERVER{SEPARATOR}PROXY_HAS_TLS"),
"server.proxy_has_tls", "server.proxy_has_tls",
); );
s = from_env(
s,
&format!("{PREFIX}{SEPARATOR}CREDS{SEPARATOR}TOKEN"),
"creds.token",
);
s s
} }
@ -154,13 +148,16 @@ mod tests {
#[test] #[test]
fn creds_works() { fn creds_works() {
let settings = Settings::new().unwrap(); let settings = Settings::new().unwrap();
let creds = settings.creds.clone(); let mut creds = settings.creds.clone();
assert!(settings.authenticate(&creds.token)); assert!(settings.authenticate(&creds.username, &creds.password));
creds.username = "noexist".into();
assert!(!settings.authenticate(&creds.username, &creds.password));
let mut creds = settings.creds.clone(); let mut creds = settings.creds.clone();
creds.token = "noexist".into(); creds.password = "noexist".into();
assert!(!settings.authenticate(&creds.token)) assert!(!settings.authenticate(&creds.username, &creds.password));
} }
} }