feat(server): allow specifying id address and port to bind to
Some checks failed
ci/woodpecker/tag/push-next Pipeline was successful
ci/woodpecker/tag/cron-docker-builder Pipeline was successful
ci/woodpecker/tag/tag-created Pipeline was successful
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
ci/woodpecker/push/tag-created Pipeline is pending
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline failed

Closes kemitix/git-next#44
This commit is contained in:
Paul Campbell 2024-05-10 22:01:47 +01:00
parent daa40e7621
commit 1cd56d953e
6 changed files with 57 additions and 8 deletions

View file

@ -1,5 +1,9 @@
[http]
addr = "0.0.0.0"
port = 8080
[webhook] [webhook]
url = "https://localhost:8080/webhook" url = "https://localhost:8080"
[storage] [storage]
path = "./data" path = "./data"

View file

@ -3,7 +3,7 @@ use std::path::PathBuf;
use actix::prelude::*; use actix::prelude::*;
use kxio::{fs::FileSystem, network::Network}; use kxio::{fs::FileSystem, network::Network};
use tracing::{error, info}; use tracing::{error, info, warn};
use crate::server::{ use crate::server::{
actors::{ actors::{
@ -62,6 +62,10 @@ impl Handler<ServerConfig> for Server {
#[allow(clippy::cognitive_complexity)] // TODO: (#75) reduce complexity #[allow(clippy::cognitive_complexity)] // TODO: (#75) reduce complexity
fn handle(&mut self, msg: ServerConfig, _ctx: &mut Self::Context) -> Self::Result { fn handle(&mut self, msg: ServerConfig, _ctx: &mut Self::Context) -> Self::Result {
let Ok(socket_addr) = msg.http() else {
warn!("Unable to parse http.addr");
return;
};
if let Some(webhook) = self.webhook.take() { if let Some(webhook) = self.webhook.take() {
webhook.do_send(ShutdownWebhook); webhook.do_send(ShutdownWebhook);
} }
@ -103,7 +107,7 @@ impl Handler<ServerConfig> for Server {
.for_each(|msg| webhook_router.do_send(msg)); .for_each(|msg| webhook_router.do_send(msg));
} }
let webhook = WebhookActor::new(webhook_router.recipient()).start(); let webhook = WebhookActor::new(socket_addr, webhook_router.recipient()).start();
self.webhook.replace(webhook); self.webhook.replace(webhook);
} }
} }

View file

@ -4,6 +4,8 @@ mod message;
mod router; mod router;
mod server; mod server;
use std::net::SocketAddr;
use actix::prelude::*; use actix::prelude::*;
pub use message::WebhookMessage; pub use message::WebhookMessage;
@ -13,14 +15,16 @@ use tracing::Instrument;
#[derive(Debug)] #[derive(Debug)]
pub struct WebhookActor { pub struct WebhookActor {
socket_addr: SocketAddr,
span: tracing::Span, span: tracing::Span,
spawn_handle: Option<actix::SpawnHandle>, spawn_handle: Option<actix::SpawnHandle>,
message_receiver: Recipient<WebhookMessage>, message_receiver: Recipient<WebhookMessage>,
} }
impl WebhookActor { impl WebhookActor {
pub fn new(message_receiver: Recipient<WebhookMessage>) -> Self { pub fn new(socket_addr: SocketAddr, message_receiver: Recipient<WebhookMessage>) -> Self {
let span = tracing::info_span!("WebhookActor"); let span = tracing::info_span!("WebhookActor");
Self { Self {
socket_addr,
span, span,
message_receiver, message_receiver,
spawn_handle: None, spawn_handle: None,
@ -32,7 +36,7 @@ impl Actor for WebhookActor {
fn started(&mut self, ctx: &mut Self::Context) { fn started(&mut self, ctx: &mut Self::Context) {
let _gaurd = self.span.enter(); let _gaurd = self.span.enter();
let address: Recipient<WebhookMessage> = self.message_receiver.clone(); let address: Recipient<WebhookMessage> = self.message_receiver.clone();
let server = server::start(address); let server = server::start(self.socket_addr, address);
let spawn_handle = ctx.spawn(server.in_current_span().into_actor(self)); let spawn_handle = ctx.spawn(server.in_current_span().into_actor(self));
self.spawn_handle.replace(spawn_handle); self.spawn_handle.replace(spawn_handle);
} }

View file

@ -1,10 +1,15 @@
use std::net::SocketAddr;
use actix::prelude::*; use actix::prelude::*;
use tracing::{debug, info}; use tracing::{debug, info};
use crate::server::actors::webhook::message::WebhookMessage; use crate::server::actors::webhook::message::WebhookMessage;
pub async fn start(address: actix::prelude::Recipient<super::message::WebhookMessage>) { pub async fn start(
socket_addr: SocketAddr,
address: actix::prelude::Recipient<super::message::WebhookMessage>,
) {
// start webhook server // start webhook server
use warp::Filter; use warp::Filter;
// Define the Warp route to handle incoming HTTP requests // Define the Warp route to handle incoming HTTP requests
@ -49,6 +54,6 @@ pub async fn start(address: actix::prelude::Recipient<super::message::WebhookMes
); );
// Start the server // Start the server
info!("Starting webhook server: http://0.0.0.0:8080/"); info!("Starting webhook server: {}", socket_addr);
warp::serve(route).run(([0, 0, 0, 0], 8080)).await; warp::serve(route).run(socket_addr).await;
} }

View file

@ -5,8 +5,10 @@ pub mod load;
use std::{ use std::{
collections::HashMap, collections::HashMap,
fmt::{Display, Formatter}, fmt::{Display, Formatter},
net::SocketAddr,
ops::Deref, ops::Deref,
path::{Path, PathBuf}, path::{Path, PathBuf},
str::FromStr,
}; };
use serde::Deserialize; use serde::Deserialize;
@ -21,6 +23,7 @@ pub enum Error {
Io(std::io::Error), Io(std::io::Error),
KxIoFs(kxio::fs::Error), KxIoFs(kxio::fs::Error),
TomlDe(toml::de::Error), TomlDe(toml::de::Error),
AddressParse(std::net::AddrParseError),
} }
impl std::error::Error for Error {} impl std::error::Error for Error {}
@ -30,6 +33,7 @@ type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, PartialEq, Eq, Deserialize, Message)] #[derive(Debug, PartialEq, Eq, Deserialize, Message)]
#[rtype(result = "()")] #[rtype(result = "()")]
pub struct ServerConfig { pub struct ServerConfig {
http: Http,
webhook: Webhook, webhook: Webhook,
storage: ServerStorage, storage: ServerStorage,
forge: HashMap<String, ForgeConfig>, forge: HashMap<String, ForgeConfig>,
@ -56,9 +60,29 @@ impl ServerConfig {
pub const fn webhook(&self) -> &Webhook { pub const fn webhook(&self) -> &Webhook {
&self.webhook &self.webhook
} }
pub fn http(&self) -> Result<SocketAddr> {
self.http.socket_addr()
}
}
/// Defines the port the server will listen to for incoming webhooks messages
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
pub struct Http {
addr: String,
port: u16,
}
impl Http {
fn socket_addr(&self) -> Result<SocketAddr> {
Ok(SocketAddr::from_str(&format!(
"{}:{}",
self.addr, self.port
))?)
}
} }
/// Defines the Webhook Forges should send updates to /// Defines the Webhook Forges should send updates to
/// Must be an address that is accessible from the remote forge
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
pub struct Webhook { pub struct Webhook {
url: String, url: String,

View file

@ -16,6 +16,10 @@ fn load_should_parse_server_config() -> Result<()> {
fs.file_write( fs.file_write(
&fs.base().join("git-next-server.toml"), &fs.base().join("git-next-server.toml"),
r#" r#"
[http]
addr = "0.0.0.0"
port = 8080
[webhook] [webhook]
url = "http://localhost:9909/webhook" url = "http://localhost:9909/webhook"
@ -43,6 +47,10 @@ fn load_should_parse_server_config() -> Result<()> {
?; ?;
let_assert!(Ok(config) = ServerConfig::load(&fs)); let_assert!(Ok(config) = ServerConfig::load(&fs));
let expected = ServerConfig { let expected = ServerConfig {
http: Http {
addr: "0.0.0.0".to_string(),
port: 8080,
},
webhook: Webhook { webhook: Webhook {
url: "http://localhost:9909/webhook".to_string(), url: "http://localhost:9909/webhook".to_string(),
}, },