mirror of
https://github.com/frosch95/K8sFileBrowser.git
synced 2026-04-11 21:08:22 +02:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 551b77f5a1 | |||
| 7932583d9d | |||
| 79e3ec2f0d |
@@ -14,20 +14,20 @@ namespace K8sFileBrowser.ViewModels;
|
|||||||
|
|
||||||
public class MainWindowViewModel : ViewModelBase
|
public class MainWindowViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
private ObservableAsPropertyHelper<IEnumerable<ClusterContext>> _clusterContexts = null!;
|
[Reactive]
|
||||||
public IEnumerable<ClusterContext> ClusterContexts => _clusterContexts.Value;
|
public IEnumerable<ClusterContext> ClusterContexts { get; set; } = null!;
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public ClusterContext? SelectedClusterContext { get; set; }
|
public ClusterContext? SelectedClusterContext { get; set; }
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public IEnumerable<Namespace> Namespaces { get; set; }
|
public IEnumerable<Namespace> Namespaces { get; set; } = null!;
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public Namespace? SelectedNamespace { get; set; }
|
public Namespace? SelectedNamespace { get; set; }
|
||||||
|
|
||||||
private ObservableAsPropertyHelper<IEnumerable<Pod>> _pods = null!;
|
[Reactive]
|
||||||
public IEnumerable<Pod> Pods => _pods.Value;
|
public IEnumerable<Pod> Pods { get; set; } = null!;
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public Pod? SelectedPod { get; set; }
|
public Pod? SelectedPod { get; set; }
|
||||||
@@ -38,8 +38,8 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
[Reactive]
|
[Reactive]
|
||||||
public Container? SelectedContainer { get; set; }
|
public Container? SelectedContainer { get; set; }
|
||||||
|
|
||||||
private ObservableAsPropertyHelper<IEnumerable<FileInformation>> _fileInformation = null!;
|
[Reactive]
|
||||||
public IEnumerable<FileInformation> FileInformation => _fileInformation.Value;
|
public IEnumerable<FileInformation> FileInformation { get; set; } = null!;
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public FileInformation? SelectedFile { get; set; }
|
public FileInformation? SelectedFile { get; set; }
|
||||||
@@ -48,7 +48,7 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
public string? SelectedPath { get; set; }
|
public string? SelectedPath { get; set; }
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public Message Message { get; set; }
|
public Message Message { get; set; } = null!;
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> DownloadCommand { get; private set; } = null!;
|
public ReactiveCommand<Unit, Unit> DownloadCommand { get; private set; } = null!;
|
||||||
public ReactiveCommand<Unit, Unit> DownloadLogCommand { get; private set; } = null!;
|
public ReactiveCommand<Unit, Unit> DownloadLogCommand { get; private set; } = null!;
|
||||||
@@ -86,18 +86,22 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
// load the cluster contexts when the view model is created
|
// load the cluster contexts when the view model is created
|
||||||
var loadContexts = ReactiveCommand
|
var loadContexts = ReactiveCommand
|
||||||
.Create<Unit, IEnumerable<ClusterContext>>(_ => kubernetesService.GetClusterContexts());
|
.Create<Unit, IEnumerable<ClusterContext>>(_ => kubernetesService.GetClusterContexts());
|
||||||
_clusterContexts = loadContexts.Execute().ToProperty(
|
loadContexts.Execute()
|
||||||
this, x => x.ClusterContexts, scheduler: RxApp.MainThreadScheduler);
|
.Throttle(new TimeSpan(10))
|
||||||
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
// select the current cluster context
|
.Subscribe(x =>
|
||||||
SelectedClusterContext = ClusterContexts
|
{
|
||||||
.FirstOrDefault(x => x.Name == kubernetesService.GetCurrentContext());
|
ClusterContexts = x;
|
||||||
|
// select the current cluster context
|
||||||
|
SelectedClusterContext = ClusterContexts
|
||||||
|
.FirstOrDefault(x => x.Name == kubernetesService.GetCurrentContext());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RegisterResetPath()
|
private void RegisterResetPath()
|
||||||
{
|
{
|
||||||
// reset the path when the pod or namespace changes
|
// reset the path when the pod or namespace changes
|
||||||
this.WhenAnyValue(c => c.SelectedPod, c => c.SelectedNamespace)
|
this.WhenAnyValue(c => c.SelectedContainer)
|
||||||
.Throttle(new TimeSpan(10))
|
.Throttle(new TimeSpan(10))
|
||||||
.ObserveOn(RxApp.TaskpoolScheduler)
|
.ObserveOn(RxApp.TaskpoolScheduler)
|
||||||
.Subscribe(_ => SelectedPath = "/");
|
.Subscribe(_ => SelectedPath = "/");
|
||||||
@@ -107,13 +111,17 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
// read the file information when the path changes
|
// read the file information when the path changes
|
||||||
this
|
this
|
||||||
.WhenAnyValue(c => c.SelectedPod, c => c.SelectedNamespace)
|
.WhenAnyValue(c => c.SelectedPod)
|
||||||
.Throttle(new TimeSpan(10))
|
.Throttle(new TimeSpan(10))
|
||||||
.Select(x => x.Item2 == null || x.Item1 == null
|
.Select(x => x == null
|
||||||
? new List<Container>()
|
? new List<Container>()
|
||||||
: x.Item1.Containers.Select(c => new Container {Name = c}))
|
: x.Containers.Select(c => new Container {Name = c}))
|
||||||
.ObserveOn(RxApp.MainThreadScheduler)
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
.Subscribe( x => Containers = x);
|
.Subscribe( x =>
|
||||||
|
{
|
||||||
|
Containers = x;
|
||||||
|
FileInformation = new List<FileInformation>();
|
||||||
|
});
|
||||||
|
|
||||||
this.WhenAnyValue(x => x.Containers)
|
this.WhenAnyValue(x => x.Containers)
|
||||||
.Throttle(new TimeSpan(10))
|
.Throttle(new TimeSpan(10))
|
||||||
@@ -124,25 +132,30 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
private void RegisterReadFiles(IKubernetesService kubernetesService)
|
private void RegisterReadFiles(IKubernetesService kubernetesService)
|
||||||
{
|
{
|
||||||
// read the file information when the path changes
|
// read the file information when the path changes
|
||||||
_fileInformation = this
|
this
|
||||||
.WhenAnyValue(c => c.SelectedPath, c => c.SelectedPod, c => c.SelectedNamespace, c => c.SelectedContainer)
|
.WhenAnyValue(c => c.SelectedContainer)
|
||||||
.Throttle(new TimeSpan(10))
|
.Throttle(new TimeSpan(10))
|
||||||
.Select(x => x.Item3 == null || x.Item2 == null || x.Item1 == null || x.Item4 == null
|
.Select(x => x == null
|
||||||
? new List<FileInformation>()
|
? new List<FileInformation>()
|
||||||
: GetFileInformation(kubernetesService, x.Item1, x.Item2, x.Item3, x.Item4))
|
: GetFileInformation(kubernetesService, SelectedPath!, SelectedPod!, SelectedNamespace!, x))
|
||||||
.ObserveOn(RxApp.MainThreadScheduler)
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
.ToProperty(this, x => x.FileInformation);
|
.Subscribe(x => FileInformation = x);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RegisterReadPods()
|
private void RegisterReadPods()
|
||||||
{
|
{
|
||||||
// read the pods when the namespace changes
|
// read the pods when the namespace changes
|
||||||
_pods = this
|
this
|
||||||
.WhenAnyValue(c => c.SelectedNamespace)
|
.WhenAnyValue(c => c.SelectedNamespace)
|
||||||
.Throttle(new TimeSpan(10))
|
.Throttle(new TimeSpan(10))
|
||||||
.SelectMany(ns => GetPodsForNamespace.Execute(ns!))
|
.SelectMany(ns => GetPodsForNamespace.Execute(ns!))
|
||||||
.ObserveOn(RxApp.MainThreadScheduler)
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
.ToProperty(this, x => x.Pods);
|
.Subscribe(x =>
|
||||||
|
{
|
||||||
|
Pods = x;
|
||||||
|
Containers = null;
|
||||||
|
FileInformation = new List<FileInformation>();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RegisterReadNamespaces(IKubernetesService kubernetesService)
|
private void RegisterReadNamespaces(IKubernetesService kubernetesService)
|
||||||
@@ -153,7 +166,13 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
.Throttle(new TimeSpan(10))
|
.Throttle(new TimeSpan(10))
|
||||||
.SelectMany(context => GetClusterContextAsync(context, kubernetesService))
|
.SelectMany(context => GetClusterContextAsync(context, kubernetesService))
|
||||||
.ObserveOn(RxApp.MainThreadScheduler)
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
.Subscribe(ns => Namespaces = ns);
|
.Subscribe(ns =>
|
||||||
|
{
|
||||||
|
Namespaces = ns;
|
||||||
|
Pods = new List<Pod>();
|
||||||
|
Containers = null;
|
||||||
|
FileInformation = new List<FileInformation>();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConfigureGetPodsForNamespaceCommand(IKubernetesService kubernetesService)
|
private void ConfigureGetPodsForNamespaceCommand(IKubernetesService kubernetesService)
|
||||||
|
|||||||
@@ -161,7 +161,7 @@
|
|||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate DataType="models:FileInformation">
|
<DataTemplate DataType="models:FileInformation">
|
||||||
<Border Background="Transparent">
|
<Border Background="Transparent">
|
||||||
<TextBlock Text="{Binding Size}" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0 0 8 0"/>
|
<TextBlock Text="{Binding Size}" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0 0 10 0"/>
|
||||||
<Interaction.Behaviors>
|
<Interaction.Behaviors>
|
||||||
<EventTriggerBehavior EventName="DoubleTapped">
|
<EventTriggerBehavior EventName="DoubleTapped">
|
||||||
<InvokeCommandAction Command="{Binding ((vm:MainWindowViewModel)DataContext).OpenCommand, RelativeSource={RelativeSource AncestorType=Window }}" />
|
<InvokeCommandAction Command="{Binding ((vm:MainWindowViewModel)DataContext).OpenCommand, RelativeSource={RelativeSource AncestorType=Window }}" />
|
||||||
@@ -175,7 +175,7 @@
|
|||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate DataType="models:FileInformation">
|
<DataTemplate DataType="models:FileInformation">
|
||||||
<Border Background="Transparent">
|
<Border Background="Transparent">
|
||||||
<TextBlock Text="{Binding DateTimeOffsetString}" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0 0 8 0"/>
|
<TextBlock Text="{Binding DateTimeOffsetString}" VerticalAlignment="Center" Margin="10 0 8 0"/>
|
||||||
<Interaction.Behaviors>
|
<Interaction.Behaviors>
|
||||||
<EventTriggerBehavior EventName="DoubleTapped">
|
<EventTriggerBehavior EventName="DoubleTapped">
|
||||||
<InvokeCommandAction Command="{Binding ((vm:MainWindowViewModel)DataContext).OpenCommand, RelativeSource={RelativeSource AncestorType=Window}}" />
|
<InvokeCommandAction Command="{Binding ((vm:MainWindowViewModel)DataContext).OpenCommand, RelativeSource={RelativeSource AncestorType=Window}}" />
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -3,4 +3,14 @@
|
|||||||
A UI tool for downloading files from a Pod.
|
A UI tool for downloading files from a Pod.
|
||||||
The application is also the first Avalonia UI and C# UI app I have written.
|
The application is also the first Avalonia UI and C# UI app I have written.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Just start the executable and select the cluster, namespace and pod you want to download files from.
|
||||||
|
Then select the files you want to download and click the download button.
|
||||||
|
|
||||||
|
The available clusters are read from the `~/.kube/config` file.
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
It only works on linux containers and the container must support `find` and `tar`.
|
||||||
|
|
||||||

|

|
||||||
@@ -1,32 +1,40 @@
|
|||||||
|
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
using Nuke.Common;
|
using Nuke.Common;
|
||||||
using Nuke.Common.IO;
|
using Nuke.Common.IO;
|
||||||
using Nuke.Common.ProjectModel;
|
|
||||||
using Nuke.Common.Tooling;
|
using Nuke.Common.Tooling;
|
||||||
using Nuke.Common.Tools.DotNet;
|
using Nuke.Common.Tools.DotNet;
|
||||||
using static Nuke.Common.Tools.DotNet.DotNetTasks;
|
using static Nuke.Common.Tools.DotNet.DotNetTasks;
|
||||||
|
|
||||||
class Build : NukeBuild
|
class Build : NukeBuild
|
||||||
{
|
{
|
||||||
AbsolutePath SourceDirectory => RootDirectory / "K8sFileBrowser";
|
|
||||||
AbsolutePath OutputDirectory => RootDirectory / "output";
|
|
||||||
AbsolutePath WinOutputDirectory => OutputDirectory / "win";
|
|
||||||
AbsolutePath LinuxOutputDirectory => OutputDirectory / "linux";
|
|
||||||
|
|
||||||
AbsolutePath ProjectFile => SourceDirectory / "K8sFileBrowser.csproj";
|
|
||||||
|
|
||||||
public static int Main () => Execute<Build>(x => x.Publish);
|
|
||||||
|
|
||||||
[Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")]
|
[Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")]
|
||||||
readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release;
|
readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release;
|
||||||
|
|
||||||
[Parameter] readonly string Version = "1.0.0";
|
[Parameter] readonly string Version = "1.0.0";
|
||||||
|
|
||||||
|
AbsolutePath SourceDirectory => RootDirectory / "K8sFileBrowser";
|
||||||
|
AbsolutePath OutputDirectory => RootDirectory / "output";
|
||||||
|
AbsolutePath WinOutputDirectory => OutputDirectory / "win";
|
||||||
|
AbsolutePath LinuxOutputDirectory => OutputDirectory / "linux";
|
||||||
|
|
||||||
|
AbsolutePath WinZip => OutputDirectory / $"K8sFileBrowser_{Version}.zip";
|
||||||
|
AbsolutePath LinuxGz => OutputDirectory / $"K8sFileBrowser_{Version}.tgz";
|
||||||
|
|
||||||
|
AbsolutePath ProjectFile => SourceDirectory / "K8sFileBrowser.csproj";
|
||||||
|
|
||||||
|
readonly string ExcludedExtensions = "pdb";
|
||||||
|
|
||||||
|
public static int Main () => Execute<Build>(x => x.Publish);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Target Clean => _ => _
|
Target Clean => _ => _
|
||||||
.Before(Restore)
|
.Before(Restore)
|
||||||
.Executes(() =>
|
.Executes(() =>
|
||||||
{
|
{
|
||||||
DotNetClean(s => s
|
OutputDirectory.DeleteDirectory();
|
||||||
.SetOutput(OutputDirectory));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Target Restore => _ => _
|
Target Restore => _ => _
|
||||||
@@ -56,6 +64,12 @@ class Build : NukeBuild
|
|||||||
.SetProcessArgumentConfigurator(_ => _
|
.SetProcessArgumentConfigurator(_ => _
|
||||||
.Add("-p:IncludeNativeLibrariesForSelfExtract=true"))
|
.Add("-p:IncludeNativeLibrariesForSelfExtract=true"))
|
||||||
.EnableNoRestore());
|
.EnableNoRestore());
|
||||||
|
|
||||||
|
WinOutputDirectory.ZipTo(
|
||||||
|
WinZip,
|
||||||
|
filter: x => !x.HasExtension(ExcludedExtensions),
|
||||||
|
compressionLevel: CompressionLevel.SmallestSize,
|
||||||
|
fileMode: FileMode.CreateNew);
|
||||||
});
|
});
|
||||||
|
|
||||||
Target PublishLinux => _ => _
|
Target PublishLinux => _ => _
|
||||||
@@ -77,6 +91,11 @@ class Build : NukeBuild
|
|||||||
.SetProcessArgumentConfigurator(_ => _
|
.SetProcessArgumentConfigurator(_ => _
|
||||||
.Add("-p:IncludeNativeLibrariesForSelfExtract=true"))
|
.Add("-p:IncludeNativeLibrariesForSelfExtract=true"))
|
||||||
.EnableNoRestore());
|
.EnableNoRestore());
|
||||||
|
|
||||||
|
LinuxOutputDirectory.TarGZipTo(
|
||||||
|
LinuxGz,
|
||||||
|
filter: x => !x.HasExtension(ExcludedExtensions),
|
||||||
|
fileMode: FileMode.CreateNew);
|
||||||
});
|
});
|
||||||
|
|
||||||
Target Publish => _ => _
|
Target Publish => _ => _
|
||||||
|
|||||||
BIN
screenshot.png
BIN
screenshot.png
Binary file not shown.
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 150 KiB |
Reference in New Issue
Block a user