feat: Add main application views and structure
Some checks failed
Build and Publish Docker Image / build-and-push (push) Has been cancelled
Some checks failed
Build and Publish Docker Image / build-and-push (push) Has been cancelled
- Implemented CreateInstanceView for creating new instances. - Added HostsView for managing SSH hosts with CRUD operations. - Created InstancesView for displaying and managing instances. - Developed LogsView for viewing operation logs. - Introduced SecretsView for managing secrets associated with hosts. - Established SettingsView for configuring application settings. - Created MainWindow as the main application window with navigation. - Added app manifest and configuration files for logging and settings.
This commit is contained in:
159
OTSSignsOrchestrator.Desktop/Services/SshDockerCliService.cs
Normal file
159
OTSSignsOrchestrator.Desktop/Services/SshDockerCliService.cs
Normal file
@@ -0,0 +1,159 @@
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using OTSSignsOrchestrator.Core.Configuration;
|
||||
using OTSSignsOrchestrator.Core.Models.DTOs;
|
||||
using OTSSignsOrchestrator.Core.Models.Entities;
|
||||
using OTSSignsOrchestrator.Core.Services;
|
||||
|
||||
namespace OTSSignsOrchestrator.Desktop.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Docker CLI service that executes docker commands on a remote host over SSH.
|
||||
/// Requires an SshHost to be set before use via SetHost().
|
||||
/// </summary>
|
||||
public class SshDockerCliService : IDockerCliService
|
||||
{
|
||||
private readonly SshConnectionService _ssh;
|
||||
private readonly DockerOptions _options;
|
||||
private readonly ILogger<SshDockerCliService> _logger;
|
||||
private SshHost? _currentHost;
|
||||
|
||||
public SshDockerCliService(
|
||||
SshConnectionService ssh,
|
||||
IOptions<DockerOptions> options,
|
||||
ILogger<SshDockerCliService> logger)
|
||||
{
|
||||
_ssh = ssh;
|
||||
_options = options.Value;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the SSH host to use for Docker commands.
|
||||
/// </summary>
|
||||
public void SetHost(SshHost host)
|
||||
{
|
||||
_currentHost = host;
|
||||
}
|
||||
|
||||
public SshHost? CurrentHost => _currentHost;
|
||||
|
||||
public async Task<DeploymentResultDto> DeployStackAsync(string stackName, string composeYaml, bool resolveImage = false)
|
||||
{
|
||||
EnsureHost();
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
var args = "docker stack deploy --compose-file -";
|
||||
if (resolveImage)
|
||||
args += " --resolve-image changed";
|
||||
args += $" {stackName}";
|
||||
|
||||
_logger.LogInformation("Deploying stack via SSH: {StackName} on {Host}", stackName, _currentHost!.Label);
|
||||
|
||||
var (exitCode, stdout, stderr) = await _ssh.RunCommandWithStdinAsync(_currentHost, args, composeYaml);
|
||||
sw.Stop();
|
||||
|
||||
var result = new DeploymentResultDto
|
||||
{
|
||||
StackName = stackName,
|
||||
Success = exitCode == 0,
|
||||
ExitCode = exitCode,
|
||||
Output = stdout,
|
||||
ErrorMessage = stderr,
|
||||
Message = exitCode == 0 ? "Success" : "Failed",
|
||||
DurationMs = sw.ElapsedMilliseconds
|
||||
};
|
||||
|
||||
if (result.Success)
|
||||
_logger.LogInformation("Stack deployed via SSH: {StackName} | duration={DurationMs}ms", stackName, result.DurationMs);
|
||||
else
|
||||
_logger.LogError("Stack deploy failed via SSH: {StackName} | error={Error}", stackName, result.ErrorMessage);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<DeploymentResultDto> RemoveStackAsync(string stackName)
|
||||
{
|
||||
EnsureHost();
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
_logger.LogInformation("Removing stack via SSH: {StackName} on {Host}", stackName, _currentHost!.Label);
|
||||
|
||||
var (exitCode, stdout, stderr) = await _ssh.RunCommandAsync(_currentHost, $"docker stack rm {stackName}");
|
||||
sw.Stop();
|
||||
|
||||
var result = new DeploymentResultDto
|
||||
{
|
||||
StackName = stackName,
|
||||
Success = exitCode == 0,
|
||||
ExitCode = exitCode,
|
||||
Output = stdout,
|
||||
ErrorMessage = stderr,
|
||||
Message = exitCode == 0 ? "Success" : "Failed",
|
||||
DurationMs = sw.ElapsedMilliseconds
|
||||
};
|
||||
|
||||
if (result.Success)
|
||||
_logger.LogInformation("Stack removed via SSH: {StackName}", stackName);
|
||||
else
|
||||
_logger.LogError("Stack remove failed via SSH: {StackName} | error={Error}", stackName, result.ErrorMessage);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<List<StackInfo>> ListStacksAsync()
|
||||
{
|
||||
EnsureHost();
|
||||
|
||||
var (exitCode, stdout, _) = await _ssh.RunCommandAsync(
|
||||
_currentHost!, "docker stack ls --format '{{.Name}}\\t{{.Services}}'");
|
||||
|
||||
if (exitCode != 0 || string.IsNullOrWhiteSpace(stdout))
|
||||
return new List<StackInfo>();
|
||||
|
||||
return stdout
|
||||
.Split('\n', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(line =>
|
||||
{
|
||||
var parts = line.Split('\t', 2);
|
||||
return new StackInfo
|
||||
{
|
||||
Name = parts[0].Trim(),
|
||||
ServiceCount = parts.Length > 1 && int.TryParse(parts[1].Trim(), out var c) ? c : 0
|
||||
};
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public async Task<List<ServiceInfo>> InspectStackServicesAsync(string stackName)
|
||||
{
|
||||
EnsureHost();
|
||||
|
||||
var (exitCode, stdout, _) = await _ssh.RunCommandAsync(
|
||||
_currentHost!, $"docker stack services {stackName} --format '{{{{.Name}}}}\\t{{{{.Image}}}}\\t{{{{.Replicas}}}}'");
|
||||
|
||||
if (exitCode != 0 || string.IsNullOrWhiteSpace(stdout))
|
||||
return new List<ServiceInfo>();
|
||||
|
||||
return stdout
|
||||
.Split('\n', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(line =>
|
||||
{
|
||||
var parts = line.Split('\t', 3);
|
||||
return new ServiceInfo
|
||||
{
|
||||
Name = parts.Length > 0 ? parts[0].Trim() : "",
|
||||
Image = parts.Length > 1 ? parts[1].Trim() : "",
|
||||
Replicas = parts.Length > 2 ? parts[2].Trim() : ""
|
||||
};
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private void EnsureHost()
|
||||
{
|
||||
if (_currentHost == null)
|
||||
throw new InvalidOperationException("No SSH host configured. Call SetHost() before using Docker commands.");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user