using MySqlConnector; using OTSSignsOrchestrator.Core.Models.DTOs; namespace OTSSignsOrchestrator.Core.Services; // Re-export for convenience so consumers only need one using using ServiceLogEntry = OTSSignsOrchestrator.Core.Models.DTOs.ServiceLogEntry; /// /// Abstraction for Docker CLI stack operations (deploy, remove, list, inspect). /// Implementations may use local docker CLI or SSH-based remote execution. /// public interface IDockerCliService { Task DeployStackAsync(string stackName, string composeYaml, bool resolveImage = false); Task RemoveStackAsync(string stackName); Task> ListStacksAsync(); Task> InspectStackServicesAsync(string stackName); /// Ensures a directory exists on the target host (equivalent to mkdir -p). Task EnsureDirectoryAsync(string path); /// /// Ensures the required folders exist on an NFS export, creating any that are missing. /// If is non-empty, creates it first as a subfolder of the export, /// then creates the volume folders inside it. /// Temporarily mounts the NFS export on the Docker host to create the directories. /// Task EnsureNfsFoldersAsync( string nfsServer, string nfsExport, IEnumerable folderNames, string? nfsExportFolder = null); /// /// Same as but returns the error message on failure /// so callers can surface actionable diagnostics. /// Task<(bool Success, string? Error)> EnsureNfsFoldersWithErrorAsync( string nfsServer, string nfsExport, IEnumerable folderNames, string? nfsExportFolder = null); /// /// Removes all Docker volumes whose names start with _. /// Volumes currently in use by running containers will be skipped. /// Safe for NFS volumes since data lives on the remote export, not in the local volume. /// Task RemoveStackVolumesAsync(string stackName); /// /// Lists all nodes in the Docker Swarm cluster. /// Must be executed against a Swarm manager node. /// Task> ListNodesAsync(); /// /// Force-updates a service so all its tasks are restarted and pick up any changed /// secrets or config (equivalent to docker service update --force). /// Task ForceUpdateServiceAsync(string serviceName); /// /// Opens a to a remote MySQL server through the /// implementation's transport (e.g. an SSH tunnel). The caller must dispose /// both the connection and the returned tunnel handle when finished. /// /// /// A tuple of (connection, tunnel). tunnel is /// and MUST be disposed after the connection is closed. /// Task<(MySqlConnection Connection, IDisposable Tunnel)> OpenMySqlConnectionAsync( string mysqlHost, int port, string adminUser, string adminPassword); /// /// Executes ALTER USER … IDENTIFIED BY … on a remote MySQL server via /// . /// Task<(bool Success, string Error)> AlterMySqlUserPasswordAsync( string mysqlHost, int port, string adminUser, string adminPassword, string targetUser, string newPassword); /// /// Atomically swaps one secret reference on a running service: /// removes and adds , /// preserving the in-container path as (defaults to /// when null, keeping the same /run/secrets/ filename). /// Task ServiceSwapSecretAsync(string serviceName, string oldSecretName, string newSecretName, string? targetAlias = null); /// /// Fetches the last log lines from a Docker Swarm service. /// If is null, fetches logs from all services in the stack. /// Returns parsed log entries sorted by timestamp ascending. /// Task> GetServiceLogsAsync(string stackName, string? serviceName = null, int tailLines = 200); /// /// Writes a file to an NFS volume by temporarily mounting the export on the Docker host. /// Used to deploy configuration files (e.g. settings-custom.php) into CMS containers. /// /// NFS server hostname or IP. /// NFS export path (e.g. "/srv/nfs"). /// Path relative to the export root (e.g. "subfolder/abbrev/cms-custom/settings-custom.php"). /// File content to write. /// Optional subfolder within the export. Task<(bool Success, string? Error)> WriteFileToNfsAsync( string nfsServer, string nfsExport, string relativePath, string content, string? nfsExportFolder = null); } public class StackInfo { public string Name { get; set; } = string.Empty; public int ServiceCount { get; set; } } public class ServiceInfo { public string Name { get; set; } = string.Empty; public string Image { get; set; } = string.Empty; public string Replicas { get; set; } = string.Empty; }