Skip to content

Commit

Permalink
Python -> Rust
Browse files Browse the repository at this point in the history
  • Loading branch information
thueske committed Oct 16, 2024
1 parent ddd112a commit bded2ce
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 51 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/docker-build-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to GitLab Container Registry
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
Expand All @@ -39,4 +39,4 @@ jobs:
platforms: linux/amd64,linux/arm64

- name: Do not automatically disable workflow execution
uses: gautamkrishnar/keepalive-workflow@v1
uses: gautamkrishnar/keepalive-workflow@v1
19 changes: 19 additions & 0 deletions build/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "cronjoblistener"
version = "0.1.0"
edition = "2021"

[profile.release]
opt-level = "z"
lto = true
strip = true

[dependencies]
bollard = "0.17"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1.0"
dotenv = "0.15"
log = "0.4"
env_logger = "0.11"
futures-util = "0.3"
25 changes: 20 additions & 5 deletions build/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
FROM python:3-slim
FROM rust:1-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY main.py .
CMD ["python", "./main.py"]

# Kopiere die Cargo.toml und den Quellcode ins Arbeitsverzeichnis
COPY Cargo.toml .
COPY src ./src

# Installiere notwendige Tools
RUN apt-get update && apt-get install -y musl-tools

# Erstelle den Build für beide Architekturen (amd64 und aarch64)
ARG TARGETARCH

RUN rustup target add ${TARGETARCH}-unknown-linux-musl && \
cargo build --release --target ${TARGETARCH}-unknown-linux-musl

# Der finale Stage, der das Ergebnis des Builds verwendet
FROM scratch
COPY --from=builder /app/target/${TARGETARCH}-unknown-linux-musl/release/cronjoblistener /app/cronjoblistener

ENTRYPOINT ["/app/cronjoblistener"]
43 changes: 0 additions & 43 deletions build/main.py

This file was deleted.

1 change: 0 additions & 1 deletion build/requirements.txt

This file was deleted.

139 changes: 139 additions & 0 deletions build/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
use bollard::Docker;
use bollard::models::EventMessage;
use bollard::errors::Error as DockerError;
use dotenv::dotenv;
use futures_util::StreamExt;
use std::env;
use std::sync::Arc;
use tokio::sync::Mutex;
use log::{info, debug, error, warn};
use tokio::time::{sleep, Duration};

#[tokio::main]
async fn main() {
// Lese Umgebungsvariablen (z.B. für LABEL_KEY und LABEL_VALUE)
dotenv().ok();

// Setze das Standard-Log-Level auf "info", wenn keine Umgebungsvariable RUST_LOG gesetzt ist
if env::var("RUST_LOG").is_err() {
env::set_var("RUST_LOG", "info");
}

// Initialisiere den Logger ohne Zeitstempel
env_logger::Builder::from_default_env()
.format_timestamp(None)
.init();

info!("Listening on container start and stop events...");

let label_key = env::var("LABEL_KEY").unwrap_or_else(|_| "ofelia.restart".to_string());
let label_value = env::var("LABEL_VALUE").unwrap_or_else(|_| "true".to_string());
let container_name_to_restart = env::var("CRON_CONTAINER").unwrap_or_else(|_| "cronjobs-cron-1".to_string());

let docker = Docker::connect_with_local_defaults().expect("Failed to connect to Docker");

// Mutex für die Steuerung des Neustart-Timers
let restart_timer = Arc::new(Mutex::new(None));

let mut events_stream = docker.events::<String>(None).fuse();

while let Some(event) = events_stream.next().await {
match event {
Ok(event_message) => {
debug!("Received event: {:?}", event_message);
let docker = docker.clone();
let restart_timer = restart_timer.clone();
let label_key = label_key.clone();
let label_value = label_value.clone();
let container_name_to_restart = container_name_to_restart.clone();

tokio::spawn(async move {
handle_event(&docker, event_message, &label_key, &label_value, &container_name_to_restart, restart_timer).await;
});
},
Err(e) => error!("Error receiving event: {:?}", e),
}
}
}

async fn handle_event(
docker: &Docker,
event: EventMessage,
label_key: &str,
label_value: &str,
container_name_to_restart: &str,
restart_timer: Arc<Mutex<Option<tokio::task::JoinHandle<()>>>>
) {
if let Some(action) = event.action {
if action == "start" || action == "stop" {
if let Some(actor) = event.actor {
if let Some(container_id) = actor.id {
// Hole den Container-Namen
let container_info = docker.inspect_container(&container_id, None).await;
if let Ok(container_info) = container_info {
if let Some(container_name) = container_info.name {
// Ignoriere Events des Cron-Containers selbst
if container_name == format!("/{}", container_name_to_restart) {
debug!("Ignoring event for the cron container itself: {}", container_name);
return;
}
}
}

match docker.inspect_container(&container_id, None).await {
Ok(container_info) => {
if let Some(labels) = container_info.config.and_then(|c| c.labels) {
if labels.get(label_key).map_or(false, |v| v == label_value) {
info!("Container {} {}ed", container_info.name.unwrap_or_default(), action);

// Setze den Timer zurück, wenn ein neuer Container startet oder stoppt
let mut timer_guard = restart_timer.lock().await;
if let Some(existing_timer) = timer_guard.take() {
existing_timer.abort(); // Abbrechen des bestehenden Timers
info!("Timer reset for restarting the cron container");
}

let docker = docker.clone();
let container_name_to_restart = container_name_to_restart.to_string();

// Starte einen neuen Timer (z.B. 60 Sekunden)
*timer_guard = Some(tokio::spawn(async move {
info!("Timer set for restarting the cron container in 60 seconds");
sleep(Duration::from_secs(60)).await;
if let Err(e) = restart_container(&docker, &container_name_to_restart).await {
error!("Error restarting container {}: {:?}", container_name_to_restart, e);
}
}));
}
}
},
Err(DockerError::DockerResponseServerError { status_code, .. }) if status_code == 404 => {
warn!("Container {} not found", container_id);
},
Err(e) => {
error!("API error when getting container {}: {:?}", container_id, e);
}
}
}
}
}
}
}

async fn restart_container(docker: &Docker, container_name: &str) -> Result<(), DockerError> {
match docker.inspect_container(container_name, None).await {
Ok(_) => {
docker.restart_container(container_name, None).await?;
info!("Container {} was restarted.", container_name);
Ok(())
},
Err(DockerError::DockerResponseServerError { status_code, .. }) if status_code == 404 => {
warn!("Container {} not found.", container_name);
Ok(())
},
Err(e) => {
error!("API error when restarting container {}: {:?}", container_name, e);
Err(e)
}
}
}

0 comments on commit bded2ce

Please sign in to comment.