Add container updater
This commit is contained in:
parent
093c035fb2
commit
520cfad72d
3 changed files with 271 additions and 0 deletions
|
|
@ -10,6 +10,7 @@
|
||||||
../../../modules/samba.nix
|
../../../modules/samba.nix
|
||||||
../../../modules/borg-client.nix
|
../../../modules/borg-client.nix
|
||||||
../../../modules/webdav.nix
|
../../../modules/webdav.nix
|
||||||
|
../../../modules/container-updater.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
# GRUB bootloader with ZFS support
|
# GRUB bootloader with ZFS support
|
||||||
|
|
@ -123,6 +124,16 @@
|
||||||
# Enable sudo for wheel group
|
# Enable sudo for wheel group
|
||||||
security.sudo.wheelNeedsPassword = false;
|
security.sudo.wheelNeedsPassword = false;
|
||||||
|
|
||||||
|
# Container auto-updater configuration
|
||||||
|
services.containerUpdater = {
|
||||||
|
enable = true;
|
||||||
|
schedule = "*-*-* 03:00:00"; # Daily at 3 AM
|
||||||
|
excludeContainers = []; # Update all containers
|
||||||
|
enableNotifications = true;
|
||||||
|
gotifyUrl = "https://notify.yanlincs.com";
|
||||||
|
gotifyToken = "Ac9qKFH5cA.7Yly"; # Same token as borg backups
|
||||||
|
};
|
||||||
|
|
||||||
# List packages installed in system profile
|
# List packages installed in system profile
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
vim
|
vim
|
||||||
|
|
|
||||||
101
modules/container-updater.nix
Normal file
101
modules/container-updater.nix
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.containerUpdater;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.services.containerUpdater = {
|
||||||
|
enable = mkEnableOption "automatic container updates";
|
||||||
|
|
||||||
|
schedule = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "*-*-* 03:00:00";
|
||||||
|
example = "daily";
|
||||||
|
description = ''
|
||||||
|
Systemd timer schedule for container updates.
|
||||||
|
Can be a systemd time specification or alias like "daily", "weekly".
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
excludeContainers = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
example = [ "traefik" "wireguard" ];
|
||||||
|
description = ''
|
||||||
|
List of container names to exclude from automatic updates.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
enableNotifications = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Enable Gotify notifications for update status";
|
||||||
|
};
|
||||||
|
|
||||||
|
gotifyUrl = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "";
|
||||||
|
example = "https://notify.yanlincs.com";
|
||||||
|
description = "Gotify server URL for notifications";
|
||||||
|
};
|
||||||
|
|
||||||
|
gotifyToken = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "";
|
||||||
|
example = "Ac9qKFH5cA.7Yly";
|
||||||
|
description = "Gotify API token for notifications";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
# Ensure the update script exists and is executable
|
||||||
|
system.activationScripts.container-updater = ''
|
||||||
|
chmod +x /home/yanlin/.config/nix/scripts/container-update.sh
|
||||||
|
chmod +x /home/yanlin/.config/nix/scripts/gotify-notify.sh
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Systemd service for container updates
|
||||||
|
systemd.services.container-updater = {
|
||||||
|
description = "Update podman containers to latest images";
|
||||||
|
after = [ "network-online.target" ];
|
||||||
|
wants = [ "network-online.target" ];
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
GOTIFY_URL = mkIf cfg.enableNotifications cfg.gotifyUrl;
|
||||||
|
GOTIFY_TOKEN = mkIf cfg.enableNotifications cfg.gotifyToken;
|
||||||
|
EXCLUDE_CONTAINERS = concatStringsSep "," cfg.excludeContainers;
|
||||||
|
};
|
||||||
|
|
||||||
|
path = [ pkgs.podman pkgs.curl pkgs.coreutils pkgs.nettools ];
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = "${pkgs.bash}/bin/bash /home/yanlin/.config/nix/scripts/container-update.sh";
|
||||||
|
User = "root";
|
||||||
|
StandardOutput = "journal";
|
||||||
|
StandardError = "journal";
|
||||||
|
|
||||||
|
# Restart policy
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = "5min";
|
||||||
|
|
||||||
|
# Timeout for the update process (30 minutes should be enough)
|
||||||
|
TimeoutStartSec = "30min";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Systemd timer for scheduled updates
|
||||||
|
systemd.timers.container-updater = {
|
||||||
|
description = "Timer for automatic container updates";
|
||||||
|
wantedBy = [ "timers.target" ];
|
||||||
|
|
||||||
|
timerConfig = {
|
||||||
|
OnCalendar = cfg.schedule;
|
||||||
|
Persistent = true;
|
||||||
|
RandomizedDelaySec = "5min";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
159
scripts/container-update.sh
Executable file
159
scripts/container-update.sh
Executable file
|
|
@ -0,0 +1,159 @@
|
||||||
|
# Container update script with Gotify notifications
|
||||||
|
# Updates all podman containers to latest images
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Configuration from environment (set by systemd service)
|
||||||
|
GOTIFY_URL="${GOTIFY_URL:-}"
|
||||||
|
GOTIFY_TOKEN="${GOTIFY_TOKEN:-}"
|
||||||
|
EXCLUDE_CONTAINERS="${EXCLUDE_CONTAINERS:-}"
|
||||||
|
|
||||||
|
# Convert excluded containers to array
|
||||||
|
IFS=',' read -ra EXCLUDED <<< "$EXCLUDE_CONTAINERS"
|
||||||
|
|
||||||
|
# Function to send Gotify notification
|
||||||
|
send_notification() {
|
||||||
|
local priority="$1"
|
||||||
|
local title="$2"
|
||||||
|
local message="$3"
|
||||||
|
|
||||||
|
if [[ -n "$GOTIFY_URL" ]] && [[ -n "$GOTIFY_TOKEN" ]]; then
|
||||||
|
/home/yanlin/.config/nix/scripts/gotify-notify.sh \
|
||||||
|
"$GOTIFY_URL" \
|
||||||
|
"$GOTIFY_TOKEN" \
|
||||||
|
"$priority" \
|
||||||
|
"$title" \
|
||||||
|
"$message" 2>&1 || echo "Failed to send notification"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get all running containers
|
||||||
|
echo "Getting list of running containers..."
|
||||||
|
containers=$(podman ps --format "{{.Names}}")
|
||||||
|
|
||||||
|
if [[ -z "$containers" ]]; then
|
||||||
|
echo "No running containers found"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Arrays to track updates
|
||||||
|
updated_containers=()
|
||||||
|
failed_containers=()
|
||||||
|
skipped_containers=()
|
||||||
|
|
||||||
|
# Update each container
|
||||||
|
for container in $containers; do
|
||||||
|
# Check if container is in exclude list
|
||||||
|
skip=false
|
||||||
|
for excluded in "${EXCLUDED[@]}"; do
|
||||||
|
if [[ "$container" == "$excluded" ]]; then
|
||||||
|
echo "Skipping excluded container: $container"
|
||||||
|
skipped_containers+=("$container")
|
||||||
|
skip=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$skip" == true ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Processing container: $container"
|
||||||
|
|
||||||
|
# Get current image
|
||||||
|
image=$(podman inspect "$container" --format '{{.ImageName}}')
|
||||||
|
|
||||||
|
if [[ -z "$image" ]]; then
|
||||||
|
echo "ERROR: Could not get image for container $container"
|
||||||
|
failed_containers+=("$container (no image)")
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " Current image: $image"
|
||||||
|
|
||||||
|
# Get current image ID before pull
|
||||||
|
old_image_id=$(podman inspect "$container" --format '{{.Image}}')
|
||||||
|
|
||||||
|
# Pull latest image
|
||||||
|
echo " Pulling latest image..."
|
||||||
|
if podman pull "$image" 2>&1; then
|
||||||
|
echo " Image pulled successfully"
|
||||||
|
|
||||||
|
# Get new image ID after pull
|
||||||
|
new_image_id=$(podman inspect "$image" --format '{{.Id}}')
|
||||||
|
|
||||||
|
# Check if image actually changed
|
||||||
|
if [[ "$old_image_id" != "$new_image_id" ]]; then
|
||||||
|
echo " New image detected, restarting container..."
|
||||||
|
|
||||||
|
# Restart container
|
||||||
|
if podman restart "$container" 2>&1; then
|
||||||
|
echo " Container updated successfully"
|
||||||
|
updated_containers+=("$container")
|
||||||
|
else
|
||||||
|
echo " ERROR: Failed to restart container"
|
||||||
|
failed_containers+=("$container (restart failed)")
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " Image unchanged, skipping restart"
|
||||||
|
skipped_containers+=("$container (no update)")
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " ERROR: Failed to pull image"
|
||||||
|
failed_containers+=("$container (pull failed)")
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
# Prepare notification message
|
||||||
|
notification_lines=()
|
||||||
|
notification_priority="normal"
|
||||||
|
|
||||||
|
if [[ ${#updated_containers[@]} -gt 0 ]]; then
|
||||||
|
notification_lines+=("✅ Updated (${#updated_containers[@]}):")
|
||||||
|
for container in "${updated_containers[@]}"; do
|
||||||
|
notification_lines+=(" • $container")
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${#failed_containers[@]} -gt 0 ]]; then
|
||||||
|
notification_priority="high"
|
||||||
|
notification_lines+=("")
|
||||||
|
notification_lines+=("❌ Failed (${#failed_containers[@]}):")
|
||||||
|
for container in "${failed_containers[@]}"; do
|
||||||
|
notification_lines+=(" • $container")
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${#skipped_containers[@]} -gt 0 ]]; then
|
||||||
|
notification_lines+=("")
|
||||||
|
notification_lines+=("⏭️ No updates (${#skipped_containers[@]}):")
|
||||||
|
for container in "${skipped_containers[@]}"; do
|
||||||
|
notification_lines+=(" • $container")
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Send notification if there were any updates or failures
|
||||||
|
if [[ ${#notification_lines[@]} -gt 0 ]]; then
|
||||||
|
# Build multi-line message similar to borg-client
|
||||||
|
message=""
|
||||||
|
for line in "${notification_lines[@]}"; do
|
||||||
|
if [[ -n "$message" ]]; then
|
||||||
|
message="${message}\n${line}"
|
||||||
|
else
|
||||||
|
message="$line"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
hostname_val=$(hostname 2>/dev/null || echo "hs")
|
||||||
|
send_notification "$notification_priority" "Container Update - $hostname_val" "$message"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Exit with error if any containers failed
|
||||||
|
if [[ ${#failed_containers[@]} -gt 0 ]]; then
|
||||||
|
echo "ERROR: Some containers failed to update"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Container update completed successfully"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue