diff --git a/.ci/build b/.ci/build new file mode 100755 index 00000000..8b3c3fd6 --- /dev/null +++ b/.ci/build @@ -0,0 +1,7 @@ +#!/bin/bash -e +set -Eeuo pipefail + +cd blastmud-repo +echo Running tests +cargo test --target-dir ../target --profile release-with-debug +cargo build --target-dir ../target --profile release-with-debug diff --git a/.concourse.yml b/.concourse.yml index 6b4de703..6df7ea82 100644 --- a/.concourse.yml +++ b/.concourse.yml @@ -11,8 +11,14 @@ jobs: type: registry-image source: repository: rust + inputs: + - blastmud-repo + caches: + - target + outputs: + - target run: - path: echo + path: blastmud-repo/.ci/build args: ["Hello", "world!"] resources: - name: blastmud-repo @@ -22,4 +28,3 @@ resources: source: uri: https://git.blastmud.org/blasthavers/blastmud.git branch: main - diff --git a/Cargo.lock b/Cargo.lock index b28465a5..62a9a460 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -188,6 +188,7 @@ dependencies = [ "blastmud_interfaces", "futures", "log", + "nix", "once_cell", "rand", "serde", @@ -1038,9 +1039,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a58d1d356c6597d08cde02c2f09d785b09e28711837b1ed667dc652c08a694" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ "bitflags", "cfg-if", diff --git a/blastmud_listener/Cargo.toml b/blastmud_listener/Cargo.toml index 2a4606cb..af0115cb 100644 --- a/blastmud_listener/Cargo.toml +++ b/blastmud_listener/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" blastmud_interfaces = { path = "../blastmud_interfaces" } futures = "0.3.25" log = "0.4.17" +nix = "0.26.2" once_cell = "1.17.0" rand = "0.8.5" serde = { version = "1.0.149", features = ["derive", "serde_derive"] } diff --git a/blastmud_listener/src/main.rs b/blastmud_listener/src/main.rs index a68fae31..3dd64098 100644 --- a/blastmud_listener/src/main.rs +++ b/blastmud_listener/src/main.rs @@ -1,4 +1,5 @@ use std::vec::Vec; +use std::path::Path; use std::collections::BTreeMap; use std::error::Error; use std::net::SocketAddr; @@ -24,12 +25,14 @@ use warp::{ self, filters::ws, Filter, Reply }; use std::time::Instant; +use nix::{sys::signal::{kill, Signal}, unistd::Pid}; #[derive(Deserialize, Debug)] struct Config { listeners: Vec, ws_listener: String, gameserver: String, + pidfile: String, } type DResult = Result>; @@ -637,14 +640,51 @@ async fn start_websocket(bind: String, active_sessions: SessionMap, server_sende Ok(()) } +pub fn replace_old_listener(pidfile: &str) -> DResult<()> { + match fs::read_to_string(pidfile) { + Err(e) => + if e.kind() == std::io::ErrorKind::NotFound { + info!("pidfile not found, assuming not already running"); + Ok(()) + } else { + info!("Error reading pidfile (other than NotFound): {}", e); + Err(Box::new(e) as Box::) + } + Ok(f) => { + let pid: Pid = Pid::from_raw(f.parse().map_err(|e| Box::new(e) as Box::)?); + match fs::read_to_string(format!("/proc/{}/cmdline", pid)) { + Ok(content) => + if content.contains("blastmud_listener") { + info!("pid in pidfile references blastmud_listener; starting cutover"); + kill(pid, Signal::SIGTERM) + .map_err(|e| Box::new(e) as Box) + } else { + info!("Pid in pidfile is for process not including blastmud_listener - ignoring pidfile"); + Ok(()) + } + Err(_) => { + info!("Pid in pidfile is gone - ignoring pidfile"); + Ok(()) + } + } + } + }?; + info!("Writing new pidfile"); + fs::write(Path::new(pidfile), format!("{}", std::process::id())) + .map_err(|e| Box::new(e) as Box::) +} + + #[tokio::main] async fn main() -> Result<(), Box> { SimpleLogger::new().with_level(LevelFilter::Info).init().unwrap(); + let listener_id = Uuid::new_v4(); let mut config = read_latest_config()?; let active_sessions: SessionMap = Arc::new(Mutex::new(SessionIndexes { by_uuid: BTreeMap::new(), count_by_source: BTreeMap::new() })); + replace_old_listener(&config.pidfile)?; let server_sender = start_server_task(listener_id, config.gameserver, active_sessions.clone()); start_pinger(listener_id, server_sender.clone()); diff --git a/scripts/run-latest b/scripts/run-latest new file mode 100644 index 00000000..75cbd101 --- /dev/null +++ b/scripts/run-latest @@ -0,0 +1,18 @@ +#!/bin/bash + +# Note: This script runs /app/exe/* at startup, and then any files moved into /app/exe/ +# (note - create files elsewhere and move them to avoid race conditions). +# This allows deployments to work from a different Docker container by simply pushing +# into a volume mounted in the correct place. +# Remove the old file from /app/exe/ before moving the new one. +# It doesn't kill off the old process, instead the new one kills the old one. + +for EXE in /app/exe/*; do + "$EXE"& +done + +inotifywait --quiet --monitor -e moved_to --format "%f" /app/exe |\ + while read MOVED + do + "/app/exe/$MOVED"& + done