- pool/worker.rs: single-threaded consumer that BLMPOPs from Redis queues sequentially. K8s replicas provide HA — each pod runs one worker. - pool/redis.rs: RedisConsumer with BLMOVE atomic dequeue, ACK/NAK, and retry-with-json support. - pool/types.rs: HookTask, TaskType, PoolConfig (minimal — no pool metrics). - sync/lock.rs: Redis SET NX EX per-repo lock to prevent concurrent workers from processing the same repo. Lock conflicts are handled by requeueing without incrementing retry count. - hook/mod.rs: HookService.start_worker() spawns the background worker. - ssh/mod.rs / http/mod.rs: ReceiveSyncService RPUSHes to Redis queue. Both run_http and run_ssh call start_worker() to launch the consumer. - Lock conflicts (GitError::Locked) in the worker are requeued without incrementing retry_count so another worker can pick them up.
96 lines
2.8 KiB
Rust
96 lines
2.8 KiB
Rust
use crate::ssh::ReceiveSyncService;
|
|
use crate::ssh::SshTokenService;
|
|
use crate::ssh::handle::SSHandle;
|
|
use db::cache::AppCache;
|
|
use db::database::AppDatabase;
|
|
use deadpool_redis::cluster::Pool as RedisPool;
|
|
use russh::server::Handler;
|
|
use slog::{Logger, info, warn};
|
|
use std::io;
|
|
use std::net::SocketAddr;
|
|
|
|
pub struct SSHServer {
|
|
pub db: AppDatabase,
|
|
pub cache: AppCache,
|
|
pub redis_pool: RedisPool,
|
|
pub logger: Logger,
|
|
pub token_service: SshTokenService,
|
|
}
|
|
|
|
impl SSHServer {
|
|
pub fn new(
|
|
db: AppDatabase,
|
|
cache: AppCache,
|
|
redis_pool: RedisPool,
|
|
logger: Logger,
|
|
token_service: SshTokenService,
|
|
) -> Self {
|
|
SSHServer {
|
|
db,
|
|
cache,
|
|
redis_pool,
|
|
logger,
|
|
token_service,
|
|
}
|
|
}
|
|
}
|
|
impl russh::server::Server for SSHServer {
|
|
type Handler = SSHandle;
|
|
|
|
fn new_client(&mut self, addr: Option<SocketAddr>) -> Self::Handler {
|
|
if let Some(addr) = addr {
|
|
info!(
|
|
self.logger,
|
|
"New SSH connection from {}:{}",
|
|
addr.ip(),
|
|
addr.port()
|
|
);
|
|
} else {
|
|
info!(self.logger, "New SSH connection from unknown address");
|
|
}
|
|
let sync_service = ReceiveSyncService::new(self.redis_pool.clone(), self.logger.clone());
|
|
SSHandle::new(
|
|
self.db.clone(),
|
|
self.cache.clone(),
|
|
sync_service,
|
|
self.logger.clone(),
|
|
self.token_service.clone(),
|
|
addr,
|
|
)
|
|
}
|
|
|
|
fn handle_session_error(&mut self, error: <Self::Handler as Handler>::Error) {
|
|
match error {
|
|
russh::Error::Disconnect => {
|
|
info!(self.logger, "Connection disconnected by peer");
|
|
}
|
|
russh::Error::Inconsistent => {
|
|
warn!(self.logger, "Protocol inconsistency detected");
|
|
}
|
|
russh::Error::NotAuthenticated => {
|
|
warn!(self.logger, "Authentication failed");
|
|
}
|
|
russh::Error::IO(ref io_err) => {
|
|
let error_msg = format!(
|
|
"IO error: kind={:?}, message={}, raw_os_error={:?}",
|
|
io_err.kind(),
|
|
io_err,
|
|
io_err.raw_os_error()
|
|
);
|
|
warn!(self.logger, "{}", error_msg);
|
|
|
|
if io_err.kind() == io::ErrorKind::UnexpectedEof {
|
|
warn!(
|
|
self.logger,
|
|
"Client disconnected during handshake or before authentication"
|
|
);
|
|
}
|
|
}
|
|
_ => {
|
|
let error_msg = format!("SSH session error: {}", error);
|
|
warn!(self.logger, "{}", error_msg);
|
|
}
|
|
}
|
|
}
|
|
}
|