From 59e9131feda7bcf2538877f97517ba083735c37b Mon Sep 17 00:00:00 2001 From: Andreas Billmann Date: Mon, 7 Aug 2023 22:00:27 +0200 Subject: [PATCH] Added container selection to support pods with multiple containers --- K8sFileBrowser/Models/Container.cs | 23 +++++++ K8sFileBrowser/Services/IKubernetesService.cs | 4 +- K8sFileBrowser/Services/KubernetesService.cs | 8 +-- .../ViewModels/MainWindowViewModel.cs | 65 ++++++++++++++----- K8sFileBrowser/Views/MainWindow.axaml | 16 ++++- 5 files changed, 93 insertions(+), 23 deletions(-) create mode 100644 K8sFileBrowser/Models/Container.cs diff --git a/K8sFileBrowser/Models/Container.cs b/K8sFileBrowser/Models/Container.cs new file mode 100644 index 0000000..3c5656d --- /dev/null +++ b/K8sFileBrowser/Models/Container.cs @@ -0,0 +1,23 @@ +namespace K8sFileBrowser.Models; + +public class Container +{ + public string Name { get; set; } = string.Empty; + + public override string ToString() + { + return Name; + } + + public override bool Equals(object? obj) + { + if (obj is Container container) + { + return Name == container.Name; + } + + return false; + } + + public override int GetHashCode() => Name.GetHashCode(); +} \ No newline at end of file diff --git a/K8sFileBrowser/Services/IKubernetesService.cs b/K8sFileBrowser/Services/IKubernetesService.cs index 2d4aaaf..0bca049 100644 --- a/K8sFileBrowser/Services/IKubernetesService.cs +++ b/K8sFileBrowser/Services/IKubernetesService.cs @@ -15,9 +15,9 @@ public interface IKubernetesService Task> GetNamespacesAsync(); Task> GetPodsAsync(string namespaceName, CancellationToken cancellationToken = default); IList GetFiles(string namespaceName, string podName, string containerName, string path); - Task DownloadFile(Namespace? selectedNamespace, Pod? selectedPod, FileInformation selectedFile, + Task DownloadFile(Namespace? selectedNamespace, Pod? selectedPod, Container? selectedContainer, FileInformation selectedFile, string? saveFileName, CancellationToken cancellationToken = default); - Task DownloadLog(Namespace? selectedNamespace, Pod? selectedPod, + Task DownloadLog(Namespace? selectedNamespace, Pod? selectedPod, Container? selectedContainer, string? saveFileName, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/K8sFileBrowser/Services/KubernetesService.cs b/K8sFileBrowser/Services/KubernetesService.cs index 0772039..1e93b3a 100644 --- a/K8sFileBrowser/Services/KubernetesService.cs +++ b/K8sFileBrowser/Services/KubernetesService.cs @@ -98,7 +98,7 @@ public class KubernetesService : IKubernetesService _kubernetesClient = kubernetesClient; } - public async Task DownloadFile(Namespace? selectedNamespace, Pod? selectedPod, FileInformation selectedFile, + public async Task DownloadFile(Namespace? selectedNamespace, Pod? selectedPod, Container? selectedContainer, FileInformation selectedFile, string? saveFileName, CancellationToken cancellationToken = default) { Log.Information("{SelectedNamespace} - {SelectedPod} - {@SelectedFile} - {SaveFileName}", @@ -139,14 +139,14 @@ public class KubernetesService : IKubernetesService await _kubernetesClient.NamespacedPodExecAsync( selectedPod?.Name, selectedNamespace?.Name, - selectedPod?.Containers.First(), + selectedContainer?.Name, new[] { "sh", "-c", $"tar cf - {selectedFile.Name}" }, false, handler, cancellationToken); } - public async Task DownloadLog(Namespace? selectedNamespace, Pod? selectedPod, + public async Task DownloadLog(Namespace? selectedNamespace, Pod? selectedPod, Container? selectedContainer, string? saveFileName, CancellationToken cancellationToken = default) { Log.Information("{SelectedNamespace} - {SelectedPod} - {SaveFileName}", @@ -155,7 +155,7 @@ public class KubernetesService : IKubernetesService var response = await _kubernetesClient.CoreV1.ReadNamespacedPodLogWithHttpMessagesAsync( selectedPod?.Name, selectedNamespace?.Name, - container: selectedPod?.Containers.First(), + container: selectedContainer?.Name, follow: false , cancellationToken: cancellationToken) .ConfigureAwait(false); diff --git a/K8sFileBrowser/ViewModels/MainWindowViewModel.cs b/K8sFileBrowser/ViewModels/MainWindowViewModel.cs index 96be233..2395053 100644 --- a/K8sFileBrowser/ViewModels/MainWindowViewModel.cs +++ b/K8sFileBrowser/ViewModels/MainWindowViewModel.cs @@ -13,7 +13,7 @@ namespace K8sFileBrowser.ViewModels; public class MainWindowViewModel : ViewModelBase { - private ObservableAsPropertyHelper> _clusterContexts; + private ObservableAsPropertyHelper> _clusterContexts = null!; public IEnumerable ClusterContexts => _clusterContexts.Value; private ClusterContext? _selectedClusterContext; @@ -24,7 +24,7 @@ public class MainWindowViewModel : ViewModelBase set => this.RaiseAndSetIfChanged(ref _selectedClusterContext, value); } - private IEnumerable _namespaces; + private IEnumerable _namespaces = null!; public IEnumerable Namespaces { get => _namespaces; @@ -39,7 +39,7 @@ public class MainWindowViewModel : ViewModelBase set => this.RaiseAndSetIfChanged(ref _selectedNamespace, value); } - private ObservableAsPropertyHelper> _pods; + private ObservableAsPropertyHelper> _pods = null!; public IEnumerable Pods => _pods.Value; private Pod? _selectedPod; @@ -49,8 +49,23 @@ public class MainWindowViewModel : ViewModelBase get => _selectedPod; set => this.RaiseAndSetIfChanged(ref _selectedPod, value); } + + private IEnumerable? _containers; + public IEnumerable? Containers + { + get => _containers; + set => this.RaiseAndSetIfChanged(ref _containers, value); + } - private ObservableAsPropertyHelper> _fileInformation; + private Container? _selectedContainer; + + public Container? SelectedContainer + { + get => _selectedContainer; + set => this.RaiseAndSetIfChanged(ref _selectedContainer, value); + } + + private ObservableAsPropertyHelper> _fileInformation = null!; public IEnumerable FileInformation => _fileInformation.Value; private FileInformation? _selectedFile; @@ -69,19 +84,19 @@ public class MainWindowViewModel : ViewModelBase set => this.RaiseAndSetIfChanged(ref _selectedPath, value); } - private Message _message; + private Message _message = null!; public Message Message { get => _message; set => this.RaiseAndSetIfChanged(ref _message, value); } - public ReactiveCommand DownloadCommand { get; private set; } - public ReactiveCommand DownloadLogCommand { get; private set; } - public ReactiveCommand ParentCommand { get; private set; } - public ReactiveCommand OpenCommand { get; private set; } + public ReactiveCommand DownloadCommand { get; private set; } = null!; + public ReactiveCommand DownloadLogCommand { get; private set; } = null!; + public ReactiveCommand ParentCommand { get; private set; } = null!; + public ReactiveCommand OpenCommand { get; private set; } = null!; - private ReactiveCommand> GetPodsForNamespace { get; set; } + private ReactiveCommand> GetPodsForNamespace { get; set; } = null!; public MainWindowViewModel() @@ -99,6 +114,7 @@ public class MainWindowViewModel : ViewModelBase // register the listeners RegisterReadNamespaces(kubernetesService); RegisterReadPods(); + RegisterReadContainers(); RegisterReadFiles(kubernetesService); RegisterResetPath(); @@ -124,18 +140,37 @@ public class MainWindowViewModel : ViewModelBase // reset the path when the pod or namespace changes this.WhenAnyValue(c => c.SelectedPod, c => c.SelectedNamespace) .Throttle(new TimeSpan(10)) + .ObserveOn(RxApp.TaskpoolScheduler) .Subscribe(x => SelectedPath = "/"); } + + private void RegisterReadContainers() + { + // read the file information when the path changes + this + .WhenAnyValue(c => c.SelectedPod, c => c.SelectedNamespace) + .Throttle(new TimeSpan(10)) + .Select(x => x.Item2 == null || x.Item1 == null + ? new List() + : x.Item1.Containers.Select(c => new Container {Name = c})) + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe( x => Containers = x); + + this.WhenAnyValue(x => x.Containers) + .Throttle(new TimeSpan(10)) + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(x => SelectedContainer = x?.FirstOrDefault()); +} private void RegisterReadFiles(IKubernetesService kubernetesService) { // read the file information when the path changes _fileInformation = this - .WhenAnyValue(c => c.SelectedPath, c => c.SelectedPod, c => c.SelectedNamespace) + .WhenAnyValue(c => c.SelectedPath, c => c.SelectedPod, c => c.SelectedNamespace, c => c.SelectedContainer) .Throttle(new TimeSpan(10)) - .Select(x => x.Item3 == null || x.Item2 == null + .Select(x => x.Item3 == null || x.Item2 == null || x.Item1 == null || x.Item4 == null ? new List() - : kubernetesService.GetFiles(x.Item3!.Name, x.Item2!.Name, x.Item2!.Containers.First(), + : kubernetesService.GetFiles(x.Item3!.Name, x.Item2!.Name, x.Item4!.Name, x.Item1)) .ObserveOn(RxApp.MainThreadScheduler) .ToProperty(this, x => x.FileInformation); @@ -206,7 +241,7 @@ public class MainWindowViewModel : ViewModelBase if (saveFileName != null) { ShowWorkingMessage("Downloading Log..."); - await kubernetesService.DownloadLog(SelectedNamespace, SelectedPod, saveFileName); + await kubernetesService.DownloadLog(SelectedNamespace, SelectedPod, SelectedContainer, saveFileName); HideWorkingMessage(); } }, RxApp.TaskpoolScheduler); @@ -232,7 +267,7 @@ public class MainWindowViewModel : ViewModelBase if (saveFileName != null) { ShowWorkingMessage("Downloading File..."); - await kubernetesService.DownloadFile(SelectedNamespace, SelectedPod, SelectedFile, saveFileName); + await kubernetesService.DownloadFile(SelectedNamespace, SelectedPod, SelectedContainer, SelectedFile, saveFileName); HideWorkingMessage(); } }, RxApp.TaskpoolScheduler); diff --git a/K8sFileBrowser/Views/MainWindow.axaml b/K8sFileBrowser/Views/MainWindow.axaml index 638f8fd..4b6600f 100644 --- a/K8sFileBrowser/Views/MainWindow.axaml +++ b/K8sFileBrowser/Views/MainWindow.axaml @@ -72,12 +72,24 @@ - + - + + + + + +