Compare commits
35 Commits
v0.0.4-alp
...
v1.7.0
| Author | SHA1 | Date | |
|---|---|---|---|
| ebf42e0d92 | |||
| 9f2ada68be | |||
| 171ef6a87c | |||
|
|
74807bcfac | ||
| 6223e982d7 | |||
| 07912b3239 | |||
| 249e74eb7b | |||
| 9926c26cc3 | |||
| f12b12a82c | |||
| d405677420 | |||
| 0e979fad5f | |||
| 6754210e9b | |||
| 0d11fdd000 | |||
| b33552531a | |||
| 7f7471d47b | |||
| 6d03c88261 | |||
| a696152667 | |||
| 491127d460 | |||
| 551b77f5a1 | |||
| 7932583d9d | |||
| 79e3ec2f0d | |||
| f04d08866f | |||
| 72f3ac90cd | |||
| 4dce6e3d38 | |||
| 4e91f5ce70 | |||
| f536e38800 | |||
| e0155a17a2 | |||
| b26cc0e984 | |||
| ba39661926 | |||
| 59e9131fed | |||
| d544f75c99 | |||
| ec60e55c7f | |||
| a4fb00010e | |||
|
|
6ad58270a9 | ||
|
|
e284e3f532 |
1
.gitignore
vendored
@@ -5,3 +5,4 @@ riderModule.iml
|
|||||||
/_ReSharper.Caches/
|
/_ReSharper.Caches/
|
||||||
.idea/
|
.idea/
|
||||||
*.user
|
*.user
|
||||||
|
.nuke/temp
|
||||||
122
.nuke/build.schema.json
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"definitions": {
|
||||||
|
"Host": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"AppVeyor",
|
||||||
|
"AzurePipelines",
|
||||||
|
"Bamboo",
|
||||||
|
"Bitbucket",
|
||||||
|
"Bitrise",
|
||||||
|
"GitHubActions",
|
||||||
|
"GitLab",
|
||||||
|
"Jenkins",
|
||||||
|
"Rider",
|
||||||
|
"SpaceAutomation",
|
||||||
|
"TeamCity",
|
||||||
|
"Terminal",
|
||||||
|
"TravisCI",
|
||||||
|
"VisualStudio",
|
||||||
|
"VSCode"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ExecutableTarget": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"Clean",
|
||||||
|
"Publish",
|
||||||
|
"PublishLinux",
|
||||||
|
"PublishOsx",
|
||||||
|
"PublishWin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Verbosity": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "",
|
||||||
|
"enum": [
|
||||||
|
"Verbose",
|
||||||
|
"Normal",
|
||||||
|
"Minimal",
|
||||||
|
"Quiet"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"NukeBuild": {
|
||||||
|
"properties": {
|
||||||
|
"Continue": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Indicates to continue a previously failed build attempt"
|
||||||
|
},
|
||||||
|
"Help": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Shows the help text for this build assembly"
|
||||||
|
},
|
||||||
|
"Host": {
|
||||||
|
"description": "Host for execution. Default is 'automatic'",
|
||||||
|
"$ref": "#/definitions/Host"
|
||||||
|
},
|
||||||
|
"NoLogo": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Disables displaying the NUKE logo"
|
||||||
|
},
|
||||||
|
"Partition": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Partition to use on CI"
|
||||||
|
},
|
||||||
|
"Plan": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Shows the execution plan (HTML)"
|
||||||
|
},
|
||||||
|
"Profile": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Defines the profiles to load",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Root": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Root directory during build execution"
|
||||||
|
},
|
||||||
|
"Skip": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of targets to be skipped. Empty list skips all dependencies",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/ExecutableTarget"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Target": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of targets to be invoked. Default is '{default_target}'",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/ExecutableTarget"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Verbosity": {
|
||||||
|
"description": "Logging verbosity during build execution. Default is 'Normal'",
|
||||||
|
"$ref": "#/definitions/Verbosity"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"Configuration": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)",
|
||||||
|
"enum": [
|
||||||
|
"Debug",
|
||||||
|
"Release"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Version": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/NukeBuild"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
4
.nuke/parameters.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./build.schema.json",
|
||||||
|
"Solution": "K8sFileBrowser.sln"
|
||||||
|
}
|
||||||
BIN
Assets/.DS_Store
vendored
Normal file
BIN
Assets/favicon_io.zip
Normal file
BIN
Assets/favicon_io/.DS_Store
vendored
Normal file
6
Assets/favicon_io/about.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
This favicon was generated using the following font:
|
||||||
|
|
||||||
|
- Font Title: Genos
|
||||||
|
- Font Author: Copyright 2011 The Genos Project Authors (https://github.com/googlefonts/genos)
|
||||||
|
- Font Source: http://fonts.gstatic.com/s/genos/v12/SlGNmQqPqpUOYTYjacb0Hc91fTwVA0_orUK6K7ZsAg.ttf
|
||||||
|
- Font License: SIL Open Font License, 1.1 (http://scripts.sil.org/OFL))
|
||||||
1
Assets/favicon_io/site.webmanifest
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
||||||
@@ -2,12 +2,17 @@
|
|||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "K8sFileBrowser", "K8sFileBrowser\K8sFileBrowser.csproj", "{637A753B-3168-4C9C-8098-7A16024E1957}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "K8sFileBrowser", "K8sFileBrowser\K8sFileBrowser.csproj", "{637A753B-3168-4C9C-8098-7A16024E1957}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "build\_build.csproj", "{C0C29CAE-A5C1-43B8-BFF8-BFE718FE04E8}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
Release|Any CPU = Release|Any CPU
|
Release|Any CPU = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{C0C29CAE-A5C1-43B8-BFF8-BFE718FE04E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C0C29CAE-A5C1-43B8-BFF8-BFE718FE04E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C0C29CAE-A5C1-43B8-BFF8-BFE718FE04E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{637A753B-3168-4C9C-8098-7A16024E1957}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{637A753B-3168-4C9C-8098-7A16024E1957}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{637A753B-3168-4C9C-8098-7A16024E1957}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{637A753B-3168-4C9C-8098-7A16024E1957}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{637A753B-3168-4C9C-8098-7A16024E1957}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{637A753B-3168-4C9C-8098-7A16024E1957}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
|||||||
BIN
K8sFileBrowser/.DS_Store
vendored
Normal file
@@ -10,7 +10,7 @@
|
|||||||
</Application.DataTemplates>
|
</Application.DataTemplates>
|
||||||
|
|
||||||
<Application.Styles>
|
<Application.Styles>
|
||||||
<FluentTheme>
|
<FluentTheme DensityStyle="Compact">
|
||||||
<FluentTheme.Palettes>
|
<FluentTheme.Palettes>
|
||||||
<!-- Palette for Light theme variant -->
|
<!-- Palette for Light theme variant -->
|
||||||
<ColorPaletteResources x:Key="Light" Accent="Green" RegionColor="White" ErrorText="Red" />
|
<ColorPaletteResources x:Key="Light" Accent="Green" RegionColor="White" ErrorText="Red" />
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
Accent="#677696"
|
Accent="#677696"
|
||||||
RegionColor="#282c34"
|
RegionColor="#282c34"
|
||||||
ErrorText="Red"
|
ErrorText="Red"
|
||||||
AltHigh="#282c34"
|
AltHigh="#343a45"
|
||||||
AltMediumLow="#2c313c"
|
AltMediumLow="#2c313c"
|
||||||
ListLow="#21252b"
|
ListLow="#21252b"
|
||||||
ListMedium="#2c313c"
|
ListMedium="#2c313c"
|
||||||
@@ -29,6 +29,7 @@
|
|||||||
ChromeMediumLow="#21252b"
|
ChromeMediumLow="#21252b"
|
||||||
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- AltHigh is used for the color of header in the DataGrid -->
|
<!-- AltHigh is used for the color of header in the DataGrid -->
|
||||||
<!-- BaseHigh is used for the text color -->
|
<!-- BaseHigh is used for the text color -->
|
||||||
<!-- ListLow is used for the mouse over in lists and DataGrid -->
|
<!-- ListLow is used for the mouse over in lists and DataGrid -->
|
||||||
|
|||||||
@@ -22,6 +22,8 @@
|
|||||||
<StreamGeometry x:Key="arrow_rotate_right_regular">M8.98489 5.00002C8.90135 5.00019 8.81821 5.00178 8.7355 5.00477C8.7297 5.00498 8.7239 5.0052 8.7181 5.00542C7.55763 5.05015 6.48324 5.36959 5.58643 5.88751C4.02356 6.79009 3 8.29543 3 10C3 11.7078 4.02751 13.2157 5.59546 14.1177C6.56274 14.6741 7.73571 15 9 15H9.5C9.81933 15 10.092 14.8004 10.2002 14.5192C10.2324 14.4357 10.25 14.3449 10.25 14.25C10.25 13.8358 9.91421 13.5 9.5 13.5H9C8.03665 13.5 7.14401 13.2646 6.412 12.8636C5.25554 12.2302 4.5 11.1837 4.5 10C4.5 8.81627 5.25554 7.76979 6.41201 7.13637C7.08679 6.76678 7.89807 6.53782 8.7754 6.50428L9 6.5H15.939L14.2197 8.21967C14.2193 8.21999 14.219 8.22032 14.2187 8.22064C13.9268 8.51361 13.9271 8.98776 14.2197 9.28033C14.4859 9.5466 14.9026 9.5708 15.1962 9.35295L15.2803 9.28033C15.2804 9.2803 15.2803 9.28037 15.2803 9.28033L15.2812 9.27945L18.2803 6.28033C18.2863 6.27432 18.2922 6.26823 18.298 6.26208C18.5616 5.98033 18.5672 5.54487 18.3149 5.25657C18.3038 5.24398 18.2923 5.23167 18.2803 5.21967L15.2813 2.22064C15.2811 2.22046 15.2815 2.22083 15.2813 2.22064C15.2812 2.2205 15.2805 2.21981 15.2803 2.21967L15.1962 2.14705C14.9026 1.9292 14.4859 1.9534 14.2197 2.21967C13.9271 2.51224 13.9268 2.98639 14.2187 3.27936C14.219 3.27968 14.2193 3.28001 14.2197 3.28033L15.938 5H9C8.99496 5 8.98992 5.00001 8.98489 5.00002ZM3.61101 20.0673C3.37203 20.2056 3.29046 20.5115 3.42882 20.7505C3.51821 20.9049 3.68311 21 3.86153 21H20C20.5522 21 21 20.5523 21 20V10.8672C21 10.5911 20.7761 10.3672 20.5 10.3672C20.412 10.3672 20.3256 10.3904 20.2494 10.4345L3.61101 20.0673ZM7.58467 19.5L19.5 12.6017V19.5H7.58467Z</StreamGeometry>
|
<StreamGeometry x:Key="arrow_rotate_right_regular">M8.98489 5.00002C8.90135 5.00019 8.81821 5.00178 8.7355 5.00477C8.7297 5.00498 8.7239 5.0052 8.7181 5.00542C7.55763 5.05015 6.48324 5.36959 5.58643 5.88751C4.02356 6.79009 3 8.29543 3 10C3 11.7078 4.02751 13.2157 5.59546 14.1177C6.56274 14.6741 7.73571 15 9 15H9.5C9.81933 15 10.092 14.8004 10.2002 14.5192C10.2324 14.4357 10.25 14.3449 10.25 14.25C10.25 13.8358 9.91421 13.5 9.5 13.5H9C8.03665 13.5 7.14401 13.2646 6.412 12.8636C5.25554 12.2302 4.5 11.1837 4.5 10C4.5 8.81627 5.25554 7.76979 6.41201 7.13637C7.08679 6.76678 7.89807 6.53782 8.7754 6.50428L9 6.5H15.939L14.2197 8.21967C14.2193 8.21999 14.219 8.22032 14.2187 8.22064C13.9268 8.51361 13.9271 8.98776 14.2197 9.28033C14.4859 9.5466 14.9026 9.5708 15.1962 9.35295L15.2803 9.28033C15.2804 9.2803 15.2803 9.28037 15.2803 9.28033L15.2812 9.27945L18.2803 6.28033C18.2863 6.27432 18.2922 6.26823 18.298 6.26208C18.5616 5.98033 18.5672 5.54487 18.3149 5.25657C18.3038 5.24398 18.2923 5.23167 18.2803 5.21967L15.2813 2.22064C15.2811 2.22046 15.2815 2.22083 15.2813 2.22064C15.2812 2.2205 15.2805 2.21981 15.2803 2.21967L15.1962 2.14705C14.9026 1.9292 14.4859 1.9534 14.2197 2.21967C13.9271 2.51224 13.9268 2.98639 14.2187 3.27936C14.219 3.27968 14.2193 3.28001 14.2197 3.28033L15.938 5H9C8.99496 5 8.98992 5.00001 8.98489 5.00002ZM3.61101 20.0673C3.37203 20.2056 3.29046 20.5115 3.42882 20.7505C3.51821 20.9049 3.68311 21 3.86153 21H20C20.5522 21 21 20.5523 21 20V10.8672C21 10.5911 20.7761 10.3672 20.5 10.3672C20.412 10.3672 20.3256 10.3904 20.2494 10.4345L3.61101 20.0673ZM7.58467 19.5L19.5 12.6017V19.5H7.58467Z</StreamGeometry>
|
||||||
<StreamGeometry x:Key="arrow_rotate_clockwise_regular">M12 3C16.9706 3 21 7.02944 21 12C21 15.0777 19.4407 17.865 16.9769 19.5009L18.75 19.5C19.1642 19.5 19.5 19.8358 19.5 20.25C19.5 20.6297 19.2178 20.9435 18.8518 20.9932L18.75 21H14.75C14.3703 21 14.0565 20.7178 14.0068 20.3518L14 20.25V16.25C14 15.8358 14.3358 15.5 14.75 15.5C15.1297 15.5 15.4435 15.7822 15.4932 16.1482L15.5 16.25L15.501 18.635C17.9241 17.3557 19.5 14.8247 19.5 12C19.5 7.85786 16.1421 4.5 12 4.5C7.85786 4.5 4.5 7.85786 4.5 12C4.5 12.4142 4.16421 12.75 3.75 12.75C3.33579 12.75 3 12.4142 3 12C3 7.02944 7.02944 3 12 3ZM12 9.25C13.5188 9.25 14.75 10.4812 14.75 12C14.75 13.5188 13.5188 14.75 12 14.75C10.4812 14.75 9.25 13.5188 9.25 12C9.25 10.4812 10.4812 9.25 12 9.25ZM12 10.75C11.3096 10.75 10.75 11.3096 10.75 12C10.75 12.6904 11.3096 13.25 12 13.25C12.6904 13.25 13.25 12.6904 13.25 12C13.25 11.3096 12.6904 10.75 12 10.75Z</StreamGeometry>
|
<StreamGeometry x:Key="arrow_rotate_clockwise_regular">M12 3C16.9706 3 21 7.02944 21 12C21 15.0777 19.4407 17.865 16.9769 19.5009L18.75 19.5C19.1642 19.5 19.5 19.8358 19.5 20.25C19.5 20.6297 19.2178 20.9435 18.8518 20.9932L18.75 21H14.75C14.3703 21 14.0565 20.7178 14.0068 20.3518L14 20.25V16.25C14 15.8358 14.3358 15.5 14.75 15.5C15.1297 15.5 15.4435 15.7822 15.4932 16.1482L15.5 16.25L15.501 18.635C17.9241 17.3557 19.5 14.8247 19.5 12C19.5 7.85786 16.1421 4.5 12 4.5C7.85786 4.5 4.5 7.85786 4.5 12C4.5 12.4142 4.16421 12.75 3.75 12.75C3.33579 12.75 3 12.4142 3 12C3 7.02944 7.02944 3 12 3ZM12 9.25C13.5188 9.25 14.75 10.4812 14.75 12C14.75 13.5188 13.5188 14.75 12 14.75C10.4812 14.75 9.25 13.5188 9.25 12C9.25 10.4812 10.4812 9.25 12 9.25ZM12 10.75C11.3096 10.75 10.75 11.3096 10.75 12C10.75 12.6904 11.3096 13.25 12 13.25C12.6904 13.25 13.25 12.6904 13.25 12C13.25 11.3096 12.6904 10.75 12 10.75Z</StreamGeometry>
|
||||||
<StreamGeometry x:Key="warning_regular">M10.9093922,2.78216375 C11.9491636,2.20625071 13.2471955,2.54089334 13.8850247,3.52240345 L13.9678229,3.66023048 L21.7267791,17.6684928 C21.9115773,18.0021332 22.0085303,18.3772743 22.0085303,18.7586748 C22.0085303,19.9495388 21.0833687,20.9243197 19.9125791,21.003484 L19.7585303,21.0086748 L4.24277801,21.0086748 C3.86146742,21.0086748 3.48641186,20.9117674 3.15282824,20.7270522 C2.11298886,20.1512618 1.7079483,18.8734454 2.20150311,17.8120352 L2.27440063,17.668725 L10.0311968,3.66046274 C10.2357246,3.291099 10.5400526,2.98673515 10.9093922,2.78216375 Z M20.4146132,18.3952808 L12.6556571,4.3870185 C12.4549601,4.02467391 11.9985248,3.89363262 11.6361802,4.09432959 C11.5438453,4.14547244 11.4637001,4.21532637 11.4006367,4.29899869 L11.3434484,4.38709592 L3.58665221,18.3953582 C3.385998,18.7577265 3.51709315,19.2141464 3.87946142,19.4148006 C3.96285732,19.4609794 4.05402922,19.4906942 4.14802472,19.5026655 L4.24277801,19.5086748 L19.7585303,19.5086748 C20.1727439,19.5086748 20.5085303,19.1728883 20.5085303,18.7586748 C20.5085303,18.6633247 20.4903516,18.5691482 20.455275,18.4811011 L20.4146132,18.3952808 L12.6556571,4.3870185 L20.4146132,18.3952808 Z M12.0004478,16.0017852 C12.5519939,16.0017852 12.9991104,16.4489016 12.9991104,17.0004478 C12.9991104,17.5519939 12.5519939,17.9991104 12.0004478,17.9991104 C11.4489016,17.9991104 11.0017852,17.5519939 11.0017852,17.0004478 C11.0017852,16.4489016 11.4489016,16.0017852 12.0004478,16.0017852 Z M11.9962476,8.49954934 C12.3759432,8.49924613 12.689964,8.78114897 12.7399193,9.14718469 L12.7468472,9.24894974 L12.750448,13.7505438 C12.7507788,14.1647572 12.4152611,14.5008121 12.0010476,14.5011439 C11.621352,14.5014471 11.3073312,14.2195442 11.257376,13.8535085 L11.250448,13.7517435 L11.2468472,9.25014944 C11.2465164,8.83593601 11.5820341,8.49988112 11.9962476,8.49954934 Z</StreamGeometry>
|
<StreamGeometry x:Key="warning_regular">M10.9093922,2.78216375 C11.9491636,2.20625071 13.2471955,2.54089334 13.8850247,3.52240345 L13.9678229,3.66023048 L21.7267791,17.6684928 C21.9115773,18.0021332 22.0085303,18.3772743 22.0085303,18.7586748 C22.0085303,19.9495388 21.0833687,20.9243197 19.9125791,21.003484 L19.7585303,21.0086748 L4.24277801,21.0086748 C3.86146742,21.0086748 3.48641186,20.9117674 3.15282824,20.7270522 C2.11298886,20.1512618 1.7079483,18.8734454 2.20150311,17.8120352 L2.27440063,17.668725 L10.0311968,3.66046274 C10.2357246,3.291099 10.5400526,2.98673515 10.9093922,2.78216375 Z M20.4146132,18.3952808 L12.6556571,4.3870185 C12.4549601,4.02467391 11.9985248,3.89363262 11.6361802,4.09432959 C11.5438453,4.14547244 11.4637001,4.21532637 11.4006367,4.29899869 L11.3434484,4.38709592 L3.58665221,18.3953582 C3.385998,18.7577265 3.51709315,19.2141464 3.87946142,19.4148006 C3.96285732,19.4609794 4.05402922,19.4906942 4.14802472,19.5026655 L4.24277801,19.5086748 L19.7585303,19.5086748 C20.1727439,19.5086748 20.5085303,19.1728883 20.5085303,18.7586748 C20.5085303,18.6633247 20.4903516,18.5691482 20.455275,18.4811011 L20.4146132,18.3952808 L12.6556571,4.3870185 L20.4146132,18.3952808 Z M12.0004478,16.0017852 C12.5519939,16.0017852 12.9991104,16.4489016 12.9991104,17.0004478 C12.9991104,17.5519939 12.5519939,17.9991104 12.0004478,17.9991104 C11.4489016,17.9991104 11.0017852,17.5519939 11.0017852,17.0004478 C11.0017852,16.4489016 11.4489016,16.0017852 12.0004478,16.0017852 Z M11.9962476,8.49954934 C12.3759432,8.49924613 12.689964,8.78114897 12.7399193,9.14718469 L12.7468472,9.24894974 L12.750448,13.7505438 C12.7507788,14.1647572 12.4152611,14.5008121 12.0010476,14.5011439 C11.621352,14.5014471 11.3073312,14.2195442 11.257376,13.8535085 L11.250448,13.7517435 L11.2468472,9.25014944 C11.2465164,8.83593601 11.5820341,8.49988112 11.9962476,8.49954934 Z</StreamGeometry>
|
||||||
|
<StreamGeometry x:Key="document_page_top_right_regular">M17.7446 1.99581C18.9355 1.99581 19.9103 2.92098 19.9894 4.09176L19.9946 4.24581V19.7439C19.9946 20.9347 19.0695 21.9095 17.8987 21.9887L17.7446 21.9939H6.24475C5.05389 21.9939 4.07911 21.0687 3.99994 19.8979L3.99475 19.7439V4.24581C3.99475 3.05495 4.91991 2.08017 6.0907 2.001L6.24475 1.99581H17.7446ZM17.7446 3.49581H6.24475C5.86506 3.49581 5.55126 3.77797 5.5016 4.14404L5.49475 4.24581V19.7439C5.49475 20.1236 5.77691 20.4374 6.14298 20.487L6.24475 20.4939H17.7446C18.1243 20.4939 18.4381 20.2117 18.4878 19.8456L18.4946 19.7439V4.24581C18.4946 3.86612 18.2125 3.55232 17.8464 3.50266L17.7446 3.49581Z M15.0188 12.0189C14.6097 11.9541 14.3306 11.5699 14.3954 11.1608L14.5 10.5H13.0187L12.8769 11.3954C12.8121 11.8045 12.428 12.0836 12.0188 12.0189C11.6097 11.9541 11.3306 11.5699 11.3954 11.1608L11.5 10.5H10.75C10.3358 10.5 10 10.1642 10 9.74996C10 9.33574 10.3358 8.99996 10.75 8.99996H11.7375L11.975 7.49996H11.25C10.8358 7.49996 10.5 7.16417 10.5 6.74996C10.5 6.33574 10.8358 5.99996 11.25 5.99996H12.2125L12.4119 4.74077C12.4767 4.33166 12.8608 4.05251 13.2699 4.11729C13.6791 4.18207 13.9582 4.56624 13.8934 4.97535L13.7312 5.99996H15.2125L15.4119 4.74077C15.4767 4.33165 15.8608 4.05251 16.2699 4.11729C16.6791 4.18207 16.9582 4.56624 16.8934 4.97535L16.7312 5.99996H17.25C17.6642 5.99996 18 6.33574 18 6.74996C18 7.16417 17.6642 7.49996 17.25 7.49996H16.4937L16.2562 8.99996H16.75C17.1642 8.99996 17.5 9.33574 17.5 9.74996C17.5 10.1642 17.1642 10.5 16.75 10.5H16.0187L15.8769 11.3954C15.8121 11.8045 15.428 12.0836 15.0188 12.0189ZM13.4937 7.49996L13.2562 8.99996H14.7375L14.975 7.49996H13.4937Z</StreamGeometry>
|
||||||
|
<StreamGeometry x:Key="document_error_regular">M18.5 20C18.5 20.275 18.276 20.5 18 20.5H12.2678C11.9806 21.051 11.6168 21.5557 11.1904 22H18C19.104 22 20 21.104 20 20V9.828C20 9.298 19.789 8.789 19.414 8.414L13.585 2.586C13.57 2.57105 13.5531 2.55808 13.5363 2.5452C13.5238 2.53567 13.5115 2.5262 13.5 2.516C13.429 2.452 13.359 2.389 13.281 2.336C13.2557 2.31894 13.2281 2.30548 13.2005 2.29207C13.1845 2.28426 13.1685 2.27647 13.153 2.268C13.1363 2.25859 13.1197 2.24897 13.103 2.23933C13.0488 2.20797 12.9944 2.17648 12.937 2.152C12.74 2.07 12.528 2.029 12.313 2.014C12.2933 2.01274 12.2738 2.01008 12.2542 2.00741C12.2271 2.00371 12.1999 2 12.172 2H6C4.896 2 4 2.896 4 4V11.4982C4.47417 11.3004 4.97679 11.1572 5.5 11.0764V4C5.5 3.725 5.724 3.5 6 3.5H12V8C12 9.104 12.896 10 14 10H18.5V20ZM13.5 4.621L17.378 8.5H14C13.724 8.5 13.5 8.275 13.5 8V4.621Z M12 17.5C12 20.5376 9.53757 23 6.5 23C3.46243 23 1 20.5376 1 17.5C1 14.4624 3.46243 12 6.5 12C9.53757 12 12 14.4624 12 17.5ZM6.5 14C6.22386 14 6 14.2239 6 14.5V18.5C6 18.7761 6.22386 19 6.5 19C6.77614 19 7 18.7761 7 18.5V14.5C7 14.2239 6.77614 14 6.5 14ZM6.5 21.125C6.84518 21.125 7.125 20.8452 7.125 20.5C7.125 20.1548 6.84518 19.875 6.5 19.875C6.15482 19.875 5.875 20.1548 5.875 20.5C5.875 20.8452 6.15482 21.125 6.5 21.125Z</StreamGeometry>
|
||||||
</Style.Resources>
|
</Style.Resources>
|
||||||
|
|
||||||
<Style Selector="PathIcon.loading">
|
<Style Selector="PathIcon.loading">
|
||||||
|
|||||||
BIN
K8sFileBrowser/Assets/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
K8sFileBrowser/Assets/android-chrome-512x512.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
K8sFileBrowser/Assets/android-chrome-512x512_128x128.icns
Normal file
BIN
K8sFileBrowser/Assets/android-chrome-512x512_16x16.icns
Normal file
BIN
K8sFileBrowser/Assets/android-chrome-512x512_256x256.icns
Normal file
BIN
K8sFileBrowser/Assets/android-chrome-512x512_32x32.icns
Normal file
BIN
K8sFileBrowser/Assets/android-chrome-512x512_512x512.icns
Normal file
BIN
K8sFileBrowser/Assets/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
K8sFileBrowser/Assets/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 337 B |
BIN
K8sFileBrowser/Assets/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 708 B |
BIN
K8sFileBrowser/Assets/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
3
K8sFileBrowser/FodyWeavers.xml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||||
|
<ReactiveUI />
|
||||||
|
</Weavers>
|
||||||
26
K8sFileBrowser/FodyWeavers.xsd
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
|
||||||
|
<xs:element name="Weavers">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:all>
|
||||||
|
<xs:element name="ReactiveUI" minOccurs="0" maxOccurs="1" type="xs:anyType" />
|
||||||
|
</xs:all>
|
||||||
|
<xs:attribute name="VerifyAssembly" type="xs:boolean">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
<xs:attribute name="GenerateXsd" type="xs:boolean">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:schema>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
@@ -9,7 +9,9 @@
|
|||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Platforms>AnyCPU</Platforms>
|
<Platforms>AnyCPU</Platforms>
|
||||||
<ApplicationIcon>Assets/app.ico</ApplicationIcon>
|
<ApplicationIcon>Assets/app.ico</ApplicationIcon>
|
||||||
<Version>0.0.3</Version>
|
<Version>1.7.0</Version>
|
||||||
|
<RuntimeIdentifiers>win-x64;linux-x64;osx-arm64</RuntimeIdentifiers>
|
||||||
|
<LangVersion>default</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
@@ -21,18 +23,22 @@
|
|||||||
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Avalonia" Version="11.0.1" />
|
<PackageReference Include="Avalonia" Version="11.3.8" />
|
||||||
<PackageReference Include="Avalonia.Desktop" Version="11.0.1" />
|
<PackageReference Include="Avalonia.Desktop" Version="11.3.8" />
|
||||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.1" />
|
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.8" />
|
||||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.0.1" />
|
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.8" />
|
||||||
|
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.3.8" />
|
||||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.1" />
|
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.3.8" />
|
||||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.1" />
|
<PackageReference Include="Avalonia.Xaml.Interactions" Version="11.3.0.6" />
|
||||||
<PackageReference Include="KubernetesClient" Version="11.0.44" />
|
<PackageReference Include="Avalonia.Xaml.Interactivity" Version="11.3.0.6" />
|
||||||
<PackageReference Include="Serilog" Version="3.0.1" />
|
<PackageReference Include="KubernetesClient" Version="18.0.5" />
|
||||||
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
|
<PackageReference Include="ReactiveUI.Avalonia" Version="11.3.8" />
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
|
<PackageReference Include="ReactiveUI.Fody" Version="19.5.41" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
<PackageReference Include="Serilog" Version="4.3.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Async" Version="2.1.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Console" Version="6.1.1" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||||
<PackageReference Include="SharpZipLib" Version="1.4.2" />
|
<PackageReference Include="SharpZipLib" Version="1.4.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
23
K8sFileBrowser/Models/Container.cs
Normal file
@@ -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();
|
||||||
|
}
|
||||||
@@ -6,18 +6,31 @@ public class FileInformation
|
|||||||
{
|
{
|
||||||
public string Parent { get; set; } = string.Empty;
|
public string Parent { get; set; } = string.Empty;
|
||||||
public FileType Type { get; set; } = FileType.File;
|
public FileType Type { get; set; } = FileType.File;
|
||||||
|
public string DisplayName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (".." == Name) return "..";
|
||||||
|
return Parent.Length < 2 ? Name[Parent.Length..] : Name[(Parent.Length + 1)..];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
public string Size { get; set; } = string.Empty;
|
public string Size { get; set; } = string.Empty;
|
||||||
public DateTimeOffset Date { get; set; } = DateTimeOffset.MinValue;
|
public DateTimeOffset? Date { get; set; }
|
||||||
|
|
||||||
|
public string DateTimeOffsetString => Date?.ToString("yyyy-MM-dd HH:mm:ss") ?? string.Empty;
|
||||||
|
|
||||||
public bool IsFile => Type == FileType.File;
|
public bool IsFile => Type == FileType.File;
|
||||||
public bool IsDirectory => Type == FileType.Directory;
|
public bool IsDirectory => Type == FileType.Directory;
|
||||||
public bool IsUnknown => Type == FileType.Unknown;
|
public bool IsUnknown => Type == FileType.Unknown;
|
||||||
|
public bool IsSymbolicLink => Type == FileType.SymbolicLink;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum FileType
|
public enum FileType
|
||||||
{
|
{
|
||||||
Directory,
|
Directory,
|
||||||
File,
|
File,
|
||||||
|
SymbolicLink,
|
||||||
Unknown
|
Unknown
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.ReactiveUI;
|
|
||||||
using System;
|
using System;
|
||||||
|
using Avalonia.Logging;
|
||||||
|
using ReactiveUI.Avalonia;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace K8sFileBrowser;
|
namespace K8sFileBrowser;
|
||||||
@@ -18,6 +20,7 @@ class Program
|
|||||||
//.Filter.ByIncludingOnly(Matching.WithProperty("Area", LogArea.Control))
|
//.Filter.ByIncludingOnly(Matching.WithProperty("Area", LogArea.Control))
|
||||||
.MinimumLevel.Information()
|
.MinimumLevel.Information()
|
||||||
.WriteTo.Async(a => a.File("app.log"))
|
.WriteTo.Async(a => a.File("app.log"))
|
||||||
|
//.WriteTo.Console()
|
||||||
.CreateLogger();
|
.CreateLogger();
|
||||||
|
|
||||||
BuildAvaloniaApp()
|
BuildAvaloniaApp()
|
||||||
@@ -42,5 +45,6 @@ class Program
|
|||||||
.UsePlatformDetect()
|
.UsePlatformDetect()
|
||||||
.WithInterFont()
|
.WithInterFont()
|
||||||
.LogToTrace()
|
.LogToTrace()
|
||||||
|
//.LogToTrace(LogEventLevel.Verbose)
|
||||||
.UseReactiveUI();
|
.UseReactiveUI();
|
||||||
}
|
}
|
||||||
@@ -15,9 +15,9 @@ public interface IKubernetesService
|
|||||||
Task<IEnumerable<Namespace>> GetNamespacesAsync();
|
Task<IEnumerable<Namespace>> GetNamespacesAsync();
|
||||||
Task<IEnumerable<Pod>> GetPodsAsync(string namespaceName, CancellationToken cancellationToken = default);
|
Task<IEnumerable<Pod>> GetPodsAsync(string namespaceName, CancellationToken cancellationToken = default);
|
||||||
IList<FileInformation> GetFiles(string namespaceName, string podName, string containerName, string path);
|
IList<FileInformation> 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);
|
string? saveFileName, CancellationToken cancellationToken = default);
|
||||||
Task DownloadLog(Namespace? selectedNamespace, Pod? selectedPod,
|
Task DownloadLog(Namespace? selectedNamespace, Pod? selectedPod, Container? selectedContainer,
|
||||||
string? saveFileName, CancellationToken cancellationToken = default);
|
string? saveFileName, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -56,6 +56,8 @@ public class KubernetesFileInformationResult
|
|||||||
{
|
{
|
||||||
"directory" => FileType.Directory,
|
"directory" => FileType.Directory,
|
||||||
"regular file" => FileType.File,
|
"regular file" => FileType.File,
|
||||||
|
"regular empty file" => FileType.File,
|
||||||
|
"symbolic link" => FileType.SymbolicLink,
|
||||||
_ => FileType.Unknown
|
_ => FileType.Unknown
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public class KubernetesService : IKubernetesService
|
|||||||
|
|
||||||
public async Task<IEnumerable<Namespace>> GetNamespacesAsync()
|
public async Task<IEnumerable<Namespace>> GetNamespacesAsync()
|
||||||
{
|
{
|
||||||
var namespaces = _kubernetesClient.CoreV1.ListNamespace();
|
var namespaces = await _kubernetesClient.CoreV1.ListNamespaceAsync();
|
||||||
var namespaceList = namespaces != null
|
var namespaceList = namespaces != null
|
||||||
? namespaces.Items.Select(n => new Namespace { Name = n.Metadata.Name }).ToList()
|
? namespaces.Items.Select(n => new Namespace { Name = n.Metadata.Name }).ToList()
|
||||||
: new List<Namespace>();
|
: new List<Namespace>();
|
||||||
@@ -68,7 +68,7 @@ public class KubernetesService : IKubernetesService
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var execResult = new KubernetesFileInformationResult(path);
|
var execResult = new KubernetesFileInformationResult(path);
|
||||||
var resultCode = _kubernetesClient
|
_kubernetesClient
|
||||||
.NamespacedPodExecAsync(
|
.NamespacedPodExecAsync(
|
||||||
podName, namespaceName, containerName,
|
podName, namespaceName, containerName,
|
||||||
new[] { "find", path, "-maxdepth", "1", "-exec", "stat", "-c", "%F|%n|%s|%Y", "{}", ";" },
|
new[] { "find", path, "-maxdepth", "1", "-exec", "stat", "-c", "%F|%n|%s|%Y", "{}", ";" },
|
||||||
@@ -98,7 +98,7 @@ public class KubernetesService : IKubernetesService
|
|||||||
_kubernetesClient = kubernetesClient;
|
_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)
|
string? saveFileName, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
Log.Information("{SelectedNamespace} - {SelectedPod} - {@SelectedFile} - {SaveFileName}",
|
Log.Information("{SelectedNamespace} - {SelectedPod} - {@SelectedFile} - {SaveFileName}",
|
||||||
@@ -128,9 +128,14 @@ public class KubernetesService : IKubernetesService
|
|||||||
}
|
}
|
||||||
|
|
||||||
using var streamReader = new StreamReader(stdError);
|
using var streamReader = new StreamReader(stdError);
|
||||||
while (streamReader.EndOfStream == false)
|
var error = string.Empty;
|
||||||
|
while (await streamReader.ReadLineAsync(cancellationToken) is { } line)
|
||||||
|
{
|
||||||
|
error += line.Trim() + Environment.NewLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(error))
|
||||||
{
|
{
|
||||||
var error = await streamReader.ReadToEndAsync(cancellationToken);
|
|
||||||
Log.Error("Remote error: {Error}",error);
|
Log.Error("Remote error: {Error}",error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -139,14 +144,14 @@ public class KubernetesService : IKubernetesService
|
|||||||
await _kubernetesClient.NamespacedPodExecAsync(
|
await _kubernetesClient.NamespacedPodExecAsync(
|
||||||
selectedPod?.Name,
|
selectedPod?.Name,
|
||||||
selectedNamespace?.Name,
|
selectedNamespace?.Name,
|
||||||
selectedPod?.Containers.First(),
|
selectedContainer?.Name,
|
||||||
new[] { "sh", "-c", $"tar cf - {selectedFile.Name}" },
|
new[] { "sh", "-c", $"tar cf - {selectedFile.Name}" },
|
||||||
false,
|
false,
|
||||||
handler,
|
handler,
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DownloadLog(Namespace? selectedNamespace, Pod? selectedPod,
|
public async Task DownloadLog(Namespace? selectedNamespace, Pod? selectedPod, Container? selectedContainer,
|
||||||
string? saveFileName, CancellationToken cancellationToken = default)
|
string? saveFileName, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
Log.Information("{SelectedNamespace} - {SelectedPod} - {SaveFileName}",
|
Log.Information("{SelectedNamespace} - {SelectedPod} - {SaveFileName}",
|
||||||
@@ -155,7 +160,7 @@ public class KubernetesService : IKubernetesService
|
|||||||
var response = await _kubernetesClient.CoreV1.ReadNamespacedPodLogWithHttpMessagesAsync(
|
var response = await _kubernetesClient.CoreV1.ReadNamespacedPodLogWithHttpMessagesAsync(
|
||||||
selectedPod?.Name,
|
selectedPod?.Name,
|
||||||
selectedNamespace?.Name,
|
selectedNamespace?.Name,
|
||||||
container: selectedPod?.Containers.First(),
|
container: selectedContainer?.Name,
|
||||||
follow: false , cancellationToken: cancellationToken)
|
follow: false , cancellationToken: cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ namespace K8sFileBrowser;
|
|||||||
|
|
||||||
public class ViewLocator : IDataTemplate
|
public class ViewLocator : IDataTemplate
|
||||||
{
|
{
|
||||||
public Control Build(object data)
|
public Control Build(object? data)
|
||||||
{
|
{
|
||||||
var name = data.GetType().FullName!.Replace("ViewModel", "View");
|
var name = data?.GetType().FullName!.Replace("ViewModel", "View");
|
||||||
|
if (name == null) return new TextBlock { Text = "Not Found: " + name };
|
||||||
var type = Type.GetType(name);
|
var type = Type.GetType(name);
|
||||||
|
|
||||||
if (type != null)
|
if (type != null)
|
||||||
@@ -20,7 +21,7 @@ public class ViewLocator : IDataTemplate
|
|||||||
return new TextBlock { Text = "Not Found: " + name };
|
return new TextBlock { Text = "Not Found: " + name };
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Match(object data)
|
public bool Match(object? data)
|
||||||
{
|
{
|
||||||
return data is ViewModelBase;
|
return data is ViewModelBase;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,97 +1,89 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reactive;
|
using System.Reactive;
|
||||||
using System.Reactive.Concurrency;
|
using System.Reactive.Concurrency;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using K8sFileBrowser.Models;
|
using K8sFileBrowser.Models;
|
||||||
using K8sFileBrowser.Services;
|
using K8sFileBrowser.Services;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
using ReactiveUI.Fody.Helpers;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
namespace K8sFileBrowser.ViewModels;
|
namespace K8sFileBrowser.ViewModels;
|
||||||
|
|
||||||
public class MainWindowViewModel : ViewModelBase
|
public class MainWindowViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
private ObservableAsPropertyHelper<IEnumerable<ClusterContext>> _clusterContexts;
|
|
||||||
public IEnumerable<ClusterContext> ClusterContexts => _clusterContexts.Value;
|
|
||||||
|
|
||||||
private ClusterContext? _selectedClusterContext;
|
#region Properties
|
||||||
|
|
||||||
public ClusterContext? SelectedClusterContext
|
[Reactive]
|
||||||
{
|
public string? Version { get; set; }
|
||||||
get => _selectedClusterContext;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _selectedClusterContext, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<Namespace> _namespaces;
|
[Reactive]
|
||||||
public IEnumerable<Namespace> Namespaces
|
public IEnumerable<ClusterContext> ClusterContexts { get; set; } = null!;
|
||||||
{
|
|
||||||
get => _namespaces;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _namespaces, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Namespace? _selectedNamespace;
|
[Reactive]
|
||||||
|
public ClusterContext? SelectedClusterContext { get; set; }
|
||||||
|
|
||||||
public Namespace? SelectedNamespace
|
[Reactive]
|
||||||
{
|
public IEnumerable<Namespace> Namespaces { get; set; } = null!;
|
||||||
get => _selectedNamespace;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _selectedNamespace, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObservableAsPropertyHelper<IEnumerable<Pod>> _pods;
|
[Reactive]
|
||||||
public IEnumerable<Pod> Pods => _pods.Value;
|
public Namespace? SelectedNamespace { get; set; }
|
||||||
|
|
||||||
private Pod? _selectedPod;
|
[Reactive]
|
||||||
|
public IEnumerable<Pod> Pods { get; set; } = null!;
|
||||||
|
|
||||||
public Pod? SelectedPod
|
[Reactive]
|
||||||
{
|
public Pod? SelectedPod { get; set; }
|
||||||
get => _selectedPod;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _selectedPod, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObservableAsPropertyHelper<IEnumerable<FileInformation>> _fileInformation;
|
[Reactive]
|
||||||
public IEnumerable<FileInformation> FileInformation => _fileInformation.Value;
|
public IEnumerable<Container>? Containers { get; set; }
|
||||||
|
|
||||||
private FileInformation? _selectedFile;
|
[Reactive]
|
||||||
|
public Container? SelectedContainer { get; set; }
|
||||||
|
|
||||||
public FileInformation? SelectedFile
|
[Reactive]
|
||||||
{
|
public IEnumerable<FileInformation> FileInformation { get; set; } = null!;
|
||||||
get => _selectedFile;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _selectedFile, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string? _selectedPath;
|
[Reactive]
|
||||||
|
public FileInformation? SelectedFile { get; set; }
|
||||||
|
|
||||||
public string? SelectedPath
|
[Reactive]
|
||||||
{
|
public string? SelectedPath { get; set; }
|
||||||
get => _selectedPath;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _selectedPath, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Message _message;
|
[Reactive]
|
||||||
public Message Message
|
public Message Message { get; set; } = null!;
|
||||||
{
|
|
||||||
get => _message;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _message, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> DownloadCommand { get; private set; }
|
private string _lastDirectory = ".";
|
||||||
public ReactiveCommand<Unit, Unit> DownloadLogCommand { get; private set; }
|
|
||||||
public ReactiveCommand<Unit, Unit> ParentCommand { get; private set; }
|
|
||||||
public ReactiveCommand<Unit, Unit> OpenCommand { get; private set; }
|
|
||||||
|
|
||||||
private ReactiveCommand<Namespace, IEnumerable<Pod>> GetPodsForNamespace { get; set; }
|
#endregion Properties
|
||||||
|
|
||||||
|
#region Commands
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> DownloadLogCommand { get; private set; } = null!;
|
||||||
|
public ReactiveCommand<Unit, Unit> RefreshCommand { get; private set; } = null!;
|
||||||
|
public ReactiveCommand<Unit, Unit> ParentCommand { get; private set; } = null!;
|
||||||
|
public ReactiveCommand<FileInformation, Unit> OpenContextCommand { get; private set; } = null!;
|
||||||
|
public ReactiveCommand<FileInformation, Unit> DownloadContextCommand { get; private set; } = null!;
|
||||||
|
private ReactiveCommand<Namespace, IEnumerable<Pod>> GetPodsForNamespace { get; set; } = null!;
|
||||||
|
|
||||||
|
#endregion Commands
|
||||||
|
|
||||||
public MainWindowViewModel()
|
public MainWindowViewModel()
|
||||||
{
|
{
|
||||||
//TODO: use dependency injection to get the kubernetes service
|
|
||||||
IKubernetesService kubernetesService = new KubernetesService();
|
IKubernetesService kubernetesService = new KubernetesService();
|
||||||
|
|
||||||
|
Version = Assembly.GetExecutingAssembly().GetName().Version?.ToString();
|
||||||
|
|
||||||
// commands
|
// commands
|
||||||
ConfigureOpenDirectoryCommand();
|
ConfigureOpenDirectoryContextCommand();
|
||||||
ConfigureDownloadFileCommand(kubernetesService);
|
ConfigureDownloadFileContextCommand(kubernetesService);
|
||||||
|
ConfigureRefreshCommand(kubernetesService);
|
||||||
ConfigureDownloadLogCommand(kubernetesService);
|
ConfigureDownloadLogCommand(kubernetesService);
|
||||||
ConfigureParentDirectoryCommand();
|
ConfigureParentDirectoryCommand();
|
||||||
ConfigureGetPodsForNamespaceCommand(kubernetesService);
|
ConfigureGetPodsForNamespaceCommand(kubernetesService);
|
||||||
@@ -99,6 +91,7 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
// register the listeners
|
// register the listeners
|
||||||
RegisterReadNamespaces(kubernetesService);
|
RegisterReadNamespaces(kubernetesService);
|
||||||
RegisterReadPods();
|
RegisterReadPods();
|
||||||
|
RegisterReadContainers();
|
||||||
RegisterReadFiles(kubernetesService);
|
RegisterReadFiles(kubernetesService);
|
||||||
RegisterResetPath();
|
RegisterResetPath();
|
||||||
|
|
||||||
@@ -106,50 +99,25 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
InitiallyLoadContexts(kubernetesService);
|
InitiallyLoadContexts(kubernetesService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Property Subscriptions
|
||||||
|
|
||||||
private void InitiallyLoadContexts(IKubernetesService kubernetesService)
|
private void InitiallyLoadContexts(IKubernetesService kubernetesService)
|
||||||
{
|
{
|
||||||
// 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);
|
|
||||||
|
|
||||||
// select the current cluster context
|
|
||||||
SelectedClusterContext = ClusterContexts
|
|
||||||
.FirstOrDefault(x => x.Name == kubernetesService.GetCurrentContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RegisterResetPath()
|
|
||||||
{
|
|
||||||
// reset the path when the pod or namespace changes
|
|
||||||
this.WhenAnyValue(c => c.SelectedPod, c => c.SelectedNamespace)
|
|
||||||
.Throttle(new TimeSpan(10))
|
.Throttle(new TimeSpan(10))
|
||||||
.Subscribe(x => SelectedPath = "/");
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
.Throttle(new TimeSpan(10))
|
|
||||||
.Select(x => x.Item3 == null || x.Item2 == null
|
|
||||||
? new List<FileInformation>()
|
|
||||||
: kubernetesService.GetFiles(x.Item3!.Name, x.Item2!.Name, x.Item2!.Containers.First(),
|
|
||||||
x.Item1))
|
|
||||||
.ObserveOn(RxApp.MainThreadScheduler)
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
.ToProperty(this, x => x.FileInformation);
|
.Subscribe(x =>
|
||||||
}
|
{
|
||||||
|
ResetNamespaces();
|
||||||
|
ClusterContexts = x.OrderBy(c => c.Name);
|
||||||
|
|
||||||
private void RegisterReadPods()
|
// select the current cluster context
|
||||||
{
|
SelectedClusterContext = ClusterContexts
|
||||||
// read the pods when the namespace changes
|
.FirstOrDefault(c => c.Name == kubernetesService.GetCurrentContext());
|
||||||
_pods = this
|
});
|
||||||
.WhenAnyValue(c => c.SelectedNamespace)
|
|
||||||
.Throttle(new TimeSpan(10))
|
|
||||||
.SelectMany(ns => GetPodsForNamespace.Execute(ns))
|
|
||||||
.ObserveOn(RxApp.MainThreadScheduler)
|
|
||||||
.ToProperty(this, x => x.Pods);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RegisterReadNamespaces(IKubernetesService kubernetesService)
|
private void RegisterReadNamespaces(IKubernetesService kubernetesService)
|
||||||
@@ -160,16 +128,82 @@ 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 =>
|
||||||
|
{
|
||||||
|
ResetPods();
|
||||||
|
Namespaces = ns.OrderBy(n => n.Name);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RegisterReadPods()
|
||||||
|
{
|
||||||
|
// read the pods when the namespace changes
|
||||||
|
this
|
||||||
|
.WhenAnyValue(c => c.SelectedNamespace)
|
||||||
|
.Throttle(new TimeSpan(10))
|
||||||
|
.Where(x => x != null)
|
||||||
|
.SelectMany(ns => GetPodsForNamespace.Execute(ns!))
|
||||||
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
|
.Subscribe(x =>
|
||||||
|
{
|
||||||
|
ResetContainers();
|
||||||
|
Pods = x.OrderBy(p => p.Name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterReadContainers()
|
||||||
|
{
|
||||||
|
// read the file information when the path changes
|
||||||
|
this
|
||||||
|
.WhenAnyValue(c => c.SelectedPod)
|
||||||
|
.Throttle(new TimeSpan(10))
|
||||||
|
.Where(x => x != null)
|
||||||
|
.Select(x => x!.Containers.Select(c => new Container {Name = c}))
|
||||||
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
|
.Subscribe( x =>
|
||||||
|
{
|
||||||
|
ResetPath();
|
||||||
|
Containers = x;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.WhenAnyValue(x => x.Containers)
|
||||||
|
.Throttle(new TimeSpan(10))
|
||||||
|
.Where(x => x != null && x.Any())
|
||||||
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
|
.Subscribe(x => SelectedContainer = x?.FirstOrDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterResetPath()
|
||||||
|
{
|
||||||
|
// reset the path when the pod or namespace changes
|
||||||
|
this.WhenAnyValue(c => c.SelectedContainer)
|
||||||
|
.Throttle(new TimeSpan(10))
|
||||||
|
.Where(x => x != null)
|
||||||
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
|
.Subscribe(_ => SelectedPath = "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterReadFiles(IKubernetesService kubernetesService)
|
||||||
|
{
|
||||||
|
// read the file information when the path changes
|
||||||
|
this
|
||||||
|
.WhenAnyValue(c => c.SelectedContainer, c => c.SelectedPath)
|
||||||
|
.Throttle(new TimeSpan(10))
|
||||||
|
.Where(x => x is { Item1: not null, Item2: not null })
|
||||||
|
.Select(x => GetFileInformation(kubernetesService, x.Item2!, SelectedPod!, SelectedNamespace!, x.Item1!))
|
||||||
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
|
.Subscribe(x => FileInformation = x);
|
||||||
|
}
|
||||||
|
#endregion Property Subscriptions
|
||||||
|
|
||||||
|
#region Configure Commands
|
||||||
private void ConfigureGetPodsForNamespaceCommand(IKubernetesService kubernetesService)
|
private void ConfigureGetPodsForNamespaceCommand(IKubernetesService kubernetesService)
|
||||||
{
|
{
|
||||||
GetPodsForNamespace = ReactiveCommand.CreateFromObservable<Namespace, IEnumerable<Pod>>(ns =>
|
GetPodsForNamespace = ReactiveCommand.CreateFromObservable<Namespace, IEnumerable<Pod>>(ns =>
|
||||||
Observable.StartAsync(_ => PodsAsync(ns, kubernetesService), RxApp.TaskpoolScheduler));
|
Observable.StartAsync(_ => PodsAsync(ns, kubernetesService), RxApp.TaskpoolScheduler));
|
||||||
|
|
||||||
GetPodsForNamespace.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
GetPodsForNamespace.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
.Subscribe(ex => ShowErrorMessage(ex.Message).ConfigureAwait(false).GetAwaiter().GetResult());
|
.Subscribe(ShowErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConfigureParentDirectoryCommand()
|
private void ConfigureParentDirectoryCommand()
|
||||||
@@ -188,7 +222,7 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
}, isNotRoot, RxApp.MainThreadScheduler);
|
}, isNotRoot, RxApp.MainThreadScheduler);
|
||||||
|
|
||||||
ParentCommand.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
ParentCommand.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
.Subscribe(ex => ShowErrorMessage(ex.Message).ConfigureAwait(false).GetAwaiter().GetResult());
|
.Subscribe(ShowErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConfigureDownloadLogCommand(IKubernetesService kubernetesService)
|
private void ConfigureDownloadLogCommand(IKubernetesService kubernetesService)
|
||||||
@@ -202,59 +236,86 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
await Observable.StartAsync(async () =>
|
await Observable.StartAsync(async () =>
|
||||||
{
|
{
|
||||||
var fileName = SelectedPod?.Name + ".log";
|
var fileName = SelectedPod?.Name + ".log";
|
||||||
var saveFileName = await ApplicationHelper.SaveFile(".", fileName);
|
var saveFileName = await ApplicationHelper.SaveFile(_lastDirectory, fileName);
|
||||||
if (saveFileName != null)
|
if (saveFileName != null)
|
||||||
{
|
{
|
||||||
|
SetLastDirectory(saveFileName);
|
||||||
ShowWorkingMessage("Downloading Log...");
|
ShowWorkingMessage("Downloading Log...");
|
||||||
await kubernetesService.DownloadLog(SelectedNamespace, SelectedPod, saveFileName);
|
await kubernetesService.DownloadLog(SelectedNamespace, SelectedPod, SelectedContainer, saveFileName);
|
||||||
HideWorkingMessage();
|
HideWorkingMessage();
|
||||||
}
|
}
|
||||||
}, RxApp.TaskpoolScheduler);
|
}, RxApp.TaskpoolScheduler);
|
||||||
}, isSelectedPod, RxApp.MainThreadScheduler);
|
}, isSelectedPod, RxApp.MainThreadScheduler);
|
||||||
|
|
||||||
DownloadLogCommand.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
DownloadLogCommand.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
.Subscribe(ex => ShowErrorMessage(ex.Message).ConfigureAwait(false).GetAwaiter().GetResult());
|
.Subscribe(ShowErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConfigureDownloadFileCommand(IKubernetesService kubernetesService)
|
private void ConfigureRefreshCommand(IKubernetesService kubernetesService)
|
||||||
{
|
{
|
||||||
var isFile = this
|
var isSelectedContainer = this
|
||||||
.WhenAnyValue(x => x.SelectedFile, x => x.Message.IsVisible)
|
.WhenAnyValue(x => x.SelectedContainer)
|
||||||
.Select(x => x is { Item1.Type: FileType.File, Item2: false });
|
.Select(x => x != null);
|
||||||
|
|
||||||
DownloadCommand = ReactiveCommand.CreateFromTask(async () =>
|
RefreshCommand = ReactiveCommand.CreateFromTask(async () =>
|
||||||
|
{
|
||||||
|
await Observable.Start(() =>
|
||||||
|
{
|
||||||
|
FileInformation = GetFileInformation(kubernetesService, SelectedPath!, SelectedPod!, SelectedNamespace!, SelectedContainer!);
|
||||||
|
}, RxApp.TaskpoolScheduler);
|
||||||
|
}, isSelectedContainer, RxApp.MainThreadScheduler);
|
||||||
|
|
||||||
|
RefreshCommand.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
|
.Subscribe(ShowErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void ConfigureDownloadFileContextCommand(IKubernetesService kubernetesService)
|
||||||
|
{
|
||||||
|
|
||||||
|
DownloadContextCommand = ReactiveCommand.CreateFromTask<FileInformation>(async (file) =>
|
||||||
{
|
{
|
||||||
await Observable.StartAsync(async () =>
|
await Observable.StartAsync(async () =>
|
||||||
{
|
{
|
||||||
var fileName = SelectedFile!.Name.Substring(SelectedFile!.Name.LastIndexOf('/') + 1,
|
var fileName = file.Name.Substring(SelectedFile!.Name.LastIndexOf('/') + 1,
|
||||||
SelectedFile!.Name.Length - SelectedFile!.Name.LastIndexOf('/') - 1);
|
file.Name.Length - file.Name.LastIndexOf('/') - 1);
|
||||||
var saveFileName = await ApplicationHelper.SaveFile(".", fileName);
|
var saveFileName = await ApplicationHelper.SaveFile(_lastDirectory, fileName);
|
||||||
if (saveFileName != null)
|
if (saveFileName != null)
|
||||||
{
|
{
|
||||||
|
SetLastDirectory(saveFileName);
|
||||||
ShowWorkingMessage("Downloading File...");
|
ShowWorkingMessage("Downloading File...");
|
||||||
await kubernetesService.DownloadFile(SelectedNamespace, SelectedPod, SelectedFile, saveFileName);
|
await kubernetesService.DownloadFile(SelectedNamespace, SelectedPod, SelectedContainer, file, saveFileName);
|
||||||
HideWorkingMessage();
|
HideWorkingMessage();
|
||||||
}
|
}
|
||||||
}, RxApp.TaskpoolScheduler);
|
}, RxApp.TaskpoolScheduler);
|
||||||
}, isFile, RxApp.MainThreadScheduler);
|
}, outputScheduler: RxApp.MainThreadScheduler);
|
||||||
|
|
||||||
DownloadCommand.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
DownloadContextCommand.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
.Subscribe(ex => ShowErrorMessage(ex.Message).ConfigureAwait(false).GetAwaiter().GetResult());
|
.Subscribe(ShowErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConfigureOpenDirectoryCommand()
|
private void SetLastDirectory(string saveFileName)
|
||||||
{
|
{
|
||||||
var isDirectory = this
|
_lastDirectory = saveFileName.Substring(0, saveFileName.LastIndexOf(Path.DirectorySeparatorChar));
|
||||||
.WhenAnyValue(x => x.SelectedFile, x => x.Message.IsVisible)
|
|
||||||
.Select(x => x is { Item1.Type: FileType.Directory, Item2: false });
|
|
||||||
|
|
||||||
OpenCommand = ReactiveCommand.Create(() => { SelectedPath = SelectedFile != null ? SelectedFile!.Name : "/"; },
|
|
||||||
isDirectory, RxApp.MainThreadScheduler);
|
|
||||||
|
|
||||||
OpenCommand.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
|
||||||
.Subscribe(ex => ShowErrorMessage(ex.Message).ConfigureAwait(false).GetAwaiter().GetResult());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void ConfigureOpenDirectoryContextCommand()
|
||||||
|
{
|
||||||
|
OpenContextCommand = ReactiveCommand.Create<FileInformation>((file) =>
|
||||||
|
{
|
||||||
|
SelectedPath = ".." == file.Name ? file.Parent : file.Name;
|
||||||
|
}, outputScheduler: RxApp.MainThreadScheduler);
|
||||||
|
|
||||||
|
OpenContextCommand.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
|
.Subscribe(ShowErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Configure Commands
|
||||||
|
|
||||||
|
#region Get Data
|
||||||
|
|
||||||
private static async Task<IEnumerable<Pod>> PodsAsync(Namespace? ns, IKubernetesService kubernetesService)
|
private static async Task<IEnumerable<Pod>> PodsAsync(Namespace? ns, IKubernetesService kubernetesService)
|
||||||
{
|
{
|
||||||
if (ns == null)
|
if (ns == null)
|
||||||
@@ -271,49 +332,125 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
ShowWorkingMessage("Switching context...");
|
ShowWorkingMessage("Switching context...");
|
||||||
Namespaces = new List<Namespace>();
|
Namespaces = new List<Namespace>();
|
||||||
kubernetesService.SwitchClusterContext(context!);
|
kubernetesService.SwitchClusterContext(context);
|
||||||
var namespaces = await kubernetesService.GetNamespacesAsync();
|
var namespaces = await kubernetesService.GetNamespacesAsync();
|
||||||
HideWorkingMessage();
|
HideWorkingMessage();
|
||||||
return namespaces;
|
return namespaces;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
RxApp.MainThreadScheduler.Schedule(Action);
|
ShowErrorMessage(e);
|
||||||
return new List<Namespace>();
|
return new List<Namespace>();
|
||||||
|
|
||||||
async void Action() => await ShowErrorMessage(e.Message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ShowWorkingMessage(string message)
|
private IList<FileInformation> GetFileInformation(IKubernetesService kubernetesService,
|
||||||
|
string path, Pod pod, Namespace nameSpace, Container container)
|
||||||
{
|
{
|
||||||
Message = new Message
|
var kubernetesFileInformation = kubernetesService.GetFiles(
|
||||||
|
nameSpace.Name, pod.Name, container.Name, path);
|
||||||
|
|
||||||
|
// when the path is root, we don't want to show the parent directory
|
||||||
|
if (SelectedPath is not { Length: > 1 }) return kubernetesFileInformation;
|
||||||
|
|
||||||
|
// add the parent directory
|
||||||
|
var parent = SelectedPath[..SelectedPath.LastIndexOf('/')];
|
||||||
|
if (string.IsNullOrEmpty(parent))
|
||||||
{
|
{
|
||||||
IsVisible = true,
|
parent = "/";
|
||||||
Text = message,
|
}
|
||||||
IsError = false
|
|
||||||
};
|
return kubernetesFileInformation.Prepend(new FileInformation
|
||||||
|
{
|
||||||
|
Name = "..",
|
||||||
|
Type = FileType.Directory,
|
||||||
|
Parent = parent
|
||||||
|
}).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ShowErrorMessage(string message)
|
#endregion Get Data
|
||||||
|
|
||||||
|
#region Reset Data
|
||||||
|
|
||||||
|
private void ResetPath()
|
||||||
{
|
{
|
||||||
Message = new Message
|
FileInformation = new List<FileInformation>();
|
||||||
|
SelectedPath = null;
|
||||||
|
SelectedContainer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetContainers()
|
||||||
|
{
|
||||||
|
ResetPath();
|
||||||
|
Containers = new List<Container>();
|
||||||
|
SelectedPod = null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetPods()
|
||||||
|
{
|
||||||
|
ResetContainers();
|
||||||
|
SelectedNamespace = null;
|
||||||
|
Pods = new List<Pod>();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetNamespaces()
|
||||||
|
{
|
||||||
|
ResetPods();
|
||||||
|
Namespaces = new List<Namespace>();
|
||||||
|
SelectedClusterContext = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Reset Data
|
||||||
|
|
||||||
|
#region show messages
|
||||||
|
|
||||||
|
private void ShowWorkingMessage(string message)
|
||||||
|
{
|
||||||
|
RxApp.MainThreadScheduler.Schedule(Action);
|
||||||
|
return;
|
||||||
|
|
||||||
|
void Action()
|
||||||
{
|
{
|
||||||
IsVisible = true,
|
Message = new Message
|
||||||
Text = message,
|
{
|
||||||
IsError = true
|
IsVisible = true,
|
||||||
};
|
Text = message,
|
||||||
await Task.Delay(7000);
|
IsError = false
|
||||||
HideWorkingMessage();
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowErrorMessage(string message)
|
||||||
|
{
|
||||||
|
RxApp.MainThreadScheduler.Schedule(Action);
|
||||||
|
return;
|
||||||
|
|
||||||
|
async void Action()
|
||||||
|
{
|
||||||
|
Message = new Message { IsVisible = true, Text = message, IsError = true };
|
||||||
|
await Task.Delay(7000);
|
||||||
|
HideWorkingMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowErrorMessage(Exception exception)
|
||||||
|
{
|
||||||
|
// ReSharper disable once TemplateIsNotCompileTimeConstantProblem
|
||||||
|
Log.Error(exception, exception.Message);
|
||||||
|
ShowErrorMessage(exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HideWorkingMessage()
|
private void HideWorkingMessage()
|
||||||
{
|
{
|
||||||
Message = new Message
|
RxApp.MainThreadScheduler.Schedule(() => Message = new Message
|
||||||
{
|
{
|
||||||
IsVisible = false,
|
IsVisible = false,
|
||||||
Text = "",
|
Text = "",
|
||||||
IsError = false
|
IsError = false
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion show messages
|
||||||
}
|
}
|
||||||
@@ -15,124 +15,300 @@
|
|||||||
</Design.DataContext>
|
</Design.DataContext>
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Border ZIndex="1" IsVisible="{Binding Message.IsVisible}" Background="{Binding Message.Color}" Opacity="{Binding Message.Opacity}">
|
<Border ZIndex="1" IsVisible="{Binding Message.IsVisible}" Background="{Binding Message.Color}"
|
||||||
|
Opacity="{Binding Message.Opacity}">
|
||||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Spacing="20" MaxWidth="500">
|
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Spacing="20" MaxWidth="500">
|
||||||
<PathIcon Classes="loading" Data="{StaticResource arrow_rotate_clockwise_regular}" Width="100" Height="100" IsVisible="{Binding !Message.IsError}"></PathIcon>
|
<PathIcon Classes="loading" Data="{StaticResource arrow_rotate_clockwise_regular}" Width="100"
|
||||||
<PathIcon Data="{StaticResource warning_regular}" Width="100" Height="100" IsVisible="{Binding Message.IsError}"></PathIcon>
|
Height="100" IsVisible="{Binding !Message.IsError}">
|
||||||
|
</PathIcon>
|
||||||
|
<PathIcon Data="{StaticResource warning_regular}" Width="100" Height="100"
|
||||||
|
IsVisible="{Binding Message.IsError}">
|
||||||
|
</PathIcon>
|
||||||
<TextBlock TextWrapping="Wrap" Text="{Binding Message.Text}">.</TextBlock>
|
<TextBlock TextWrapping="Wrap" Text="{Binding Message.Text}">.</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
<Grid RowDefinitions="Auto, *">
|
<Grid RowDefinitions="Auto, *">
|
||||||
<Border Padding="10 14" Background="#21252b">
|
<Border Padding="10 14" Background="#21252b">
|
||||||
<Grid ColumnDefinitions="Auto,Auto,Auto,*">
|
<Grid ColumnDefinitions="Auto,Auto,Auto,*,Auto">
|
||||||
<Label Grid.Column="0"
|
<Label Grid.Column="0"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Margin="0 0 10 0">
|
Margin="0 0 10 0">
|
||||||
Cluster:
|
Cluster:
|
||||||
</Label>
|
</Label>
|
||||||
<ComboBox Grid.Column="1"
|
<ComboBox Grid.Column="1"
|
||||||
ItemsSource="{Binding ClusterContexts}"
|
ItemsSource="{Binding ClusterContexts}"
|
||||||
SelectedItem="{Binding SelectedClusterContext}"
|
SelectedItem="{Binding SelectedClusterContext}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
MinWidth="200"
|
MinWidth="200"
|
||||||
Margin="0 0 10 0">
|
Margin="0 0 10 0">
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
<Label Grid.Column="2"
|
<Label Grid.Column="2"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Margin="0 0 10 0">
|
Margin="0 0 10 0">
|
||||||
Namespace:
|
Namespace:
|
||||||
</Label>
|
</Label>
|
||||||
<ComboBox Grid.Column="3"
|
<ComboBox Grid.Column="3"
|
||||||
ItemsSource="{Binding Namespaces}"
|
ItemsSource="{Binding Namespaces}"
|
||||||
SelectedItem="{Binding SelectedNamespace}"
|
SelectedItem="{Binding SelectedNamespace}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
MinWidth="200"
|
MinWidth="200"
|
||||||
Margin="0 0 10 0">
|
Margin="0 0 10 0">
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
</Grid>
|
<TextBlock Grid.Column="4" HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||||
</Border>
|
Text="{Binding Version}" />
|
||||||
|
</Grid>
|
||||||
<Grid ColumnDefinitions="*, 1, 3*" Grid.Row="1">
|
</Border>
|
||||||
<ListBox
|
|
||||||
ItemsSource="{Binding Pods}"
|
<Grid ColumnDefinitions="*, 1, 3*" Grid.Row="1">
|
||||||
SelectedItem="{Binding SelectedPod}" Background="Transparent">
|
<ListBox
|
||||||
<ListBox.Styles>
|
ItemsSource="{Binding Pods}"
|
||||||
<Style Selector="ListBoxItem">
|
SelectedItem="{Binding SelectedPod}" Background="Transparent">
|
||||||
<Setter Property="Padding" Value="0" />
|
<ListBox.Styles>
|
||||||
</Style>
|
<Style Selector="ListBoxItem">
|
||||||
</ListBox.Styles>
|
<Setter Property="Padding" Value="0" />
|
||||||
<ListBox.ItemTemplate>
|
</Style>
|
||||||
<DataTemplate>
|
</ListBox.Styles>
|
||||||
<Border CornerRadius="0" Padding="0 4 0 4" BorderBrush="SlateGray"
|
<ListBox.ItemTemplate>
|
||||||
BorderThickness="0 0 0 1">
|
<DataTemplate>
|
||||||
<TextBlock Text="{Binding}" Padding="4" Margin="4" />
|
<Border CornerRadius="0" Padding="0 4 0 4" BorderBrush="SlateGray"
|
||||||
</Border>
|
BorderThickness="0 0 0 1">
|
||||||
</DataTemplate>
|
<TextBlock Text="{Binding}" Padding="4" Margin="4" />
|
||||||
</ListBox.ItemTemplate>
|
</Border>
|
||||||
</ListBox>
|
</DataTemplate>
|
||||||
<GridSplitter Grid.Column="1" ResizeDirection="Columns" />
|
</ListBox.ItemTemplate>
|
||||||
<Grid Grid.Column="2" RowDefinitions="Auto, *">
|
</ListBox>
|
||||||
<Grid ColumnDefinitions="*, Auto">
|
<GridSplitter Grid.Column="1" ResizeDirection="Columns" />
|
||||||
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
<Grid Grid.Column="2" RowDefinitions="Auto, *">
|
||||||
<TextBlock Text="Current Directory" VerticalAlignment="Center" Margin="10 0 0 0"></TextBlock>
|
<Grid ColumnDefinitions="*, Auto, Auto">
|
||||||
<TextBlock Text="{Binding SelectedPath}" VerticalAlignment="Center" Margin="10 0 0 0"></TextBlock>
|
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||||
</StackPanel>
|
<TextBlock Text="Current Directory" VerticalAlignment="Center" Margin="10 0 0 0"></TextBlock>
|
||||||
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="4" Margin="10" HorizontalAlignment="Right">
|
<TextBlock Text="{Binding SelectedPath}" VerticalAlignment="Center" Margin="10 0 0 0"></TextBlock>
|
||||||
|
</StackPanel>
|
||||||
<Button Command="{Binding ParentCommand}" VerticalAlignment="Center" ToolTip.Tip="Go To Parent Directory">
|
<StackPanel Grid.Column="1" VerticalAlignment="Center" Orientation="Horizontal">
|
||||||
<PathIcon Data="{StaticResource arrow_curve_up_left_regular}"></PathIcon>
|
<Label VerticalAlignment="Center"
|
||||||
</Button>
|
Margin="0 0 10 0">
|
||||||
<Button Command="{Binding OpenCommand}" VerticalAlignment="Center" ToolTip.Tip="Browse Directory">
|
Container:
|
||||||
<PathIcon Data="{StaticResource arrow_right_regular}"></PathIcon>
|
</Label>
|
||||||
</Button>
|
<ComboBox ItemsSource="{Binding Containers}"
|
||||||
<Button Command="{Binding DownloadCommand}" VerticalAlignment="Center" ToolTip.Tip="Download File">
|
SelectedItem="{Binding SelectedContainer}"
|
||||||
<PathIcon Data="{StaticResource arrow_download_regular}"></PathIcon>
|
VerticalAlignment="Center"
|
||||||
</Button>
|
MinWidth="200"
|
||||||
<Button Command="{Binding DownloadLogCommand}" VerticalAlignment="Center" ToolTip.Tip="Download Pod Log">
|
Margin="0 0 10 0">
|
||||||
<PathIcon Data="{StaticResource document_one_page_regular}"></PathIcon>
|
</ComboBox>
|
||||||
</Button>
|
</StackPanel>
|
||||||
</StackPanel>
|
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="4" Margin="10"
|
||||||
|
HorizontalAlignment="Right">
|
||||||
|
<Button Command="{Binding DownloadLogCommand}" VerticalAlignment="Center"
|
||||||
|
ToolTip.Tip="Download Container Log" Margin="0 0 48 0 ">
|
||||||
|
<PathIcon Data="{StaticResource document_one_page_regular}"></PathIcon>
|
||||||
|
</Button>
|
||||||
|
<Button Command="{Binding RefreshCommand}" VerticalAlignment="Center"
|
||||||
|
ToolTip.Tip="Refresh Directory">
|
||||||
|
<PathIcon Data="{StaticResource arrow_sync_circle_regular}"></PathIcon>
|
||||||
|
</Button>
|
||||||
|
<Button Command="{Binding ParentCommand}" VerticalAlignment="Center"
|
||||||
|
ToolTip.Tip="Go To Parent Directory">
|
||||||
|
<PathIcon Data="{StaticResource arrow_curve_up_left_regular}"></PathIcon>
|
||||||
|
</Button>
|
||||||
|
<Button Command="{Binding OpenContextCommand}" CommandParameter="{Binding SelectedFile}"
|
||||||
|
IsEnabled="{Binding SelectedFile.IsDirectory}" VerticalAlignment="Center"
|
||||||
|
ToolTip.Tip="Browse Directory">
|
||||||
|
<PathIcon Data="{StaticResource arrow_right_regular}"></PathIcon>
|
||||||
|
</Button>
|
||||||
|
<Button Command="{Binding DownloadContextCommand}"
|
||||||
|
CommandParameter="{Binding SelectedFile}" IsEnabled="{Binding SelectedFile.IsFile}"
|
||||||
|
VerticalAlignment="Center" ToolTip.Tip="Download File">
|
||||||
|
<PathIcon Data="{StaticResource arrow_download_regular}"></PathIcon>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
<DataGrid Grid.Row="1"
|
||||||
|
Name="FileInformationDataGrid"
|
||||||
|
Margin="2 0 2 0"
|
||||||
|
ItemsSource="{Binding FileInformation}"
|
||||||
|
IsReadOnly="True"
|
||||||
|
GridLinesVisibility="Horizontal"
|
||||||
|
BorderThickness="1"
|
||||||
|
SelectionMode="Single"
|
||||||
|
SelectedItem="{Binding SelectedFile}"
|
||||||
|
Focusable="False">
|
||||||
|
<DataGrid.Styles>
|
||||||
|
<Style Selector="DataGridColumnHeader">
|
||||||
|
<Setter Property="FontSize" Value="14"></Setter>
|
||||||
|
<Setter Property="Padding" Value="10"></Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="DataGridCell">
|
||||||
|
<Setter Property="FontSize" Value="14"></Setter>
|
||||||
|
</Style>
|
||||||
|
</DataGrid.Styles>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTemplateColumn Header="Type" CanUserSort="False">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate DataType="models:FileInformation">
|
||||||
|
<Border ToolTip.Tip="{Binding Type}" Background="Transparent">
|
||||||
|
<Border.ContextFlyout>
|
||||||
|
<MenuFlyout>
|
||||||
|
<MenuItem Header="Open"
|
||||||
|
Command="{Binding $parent[Window].((vm:MainWindowViewModel)DataContext).OpenContextCommand}"
|
||||||
|
CommandParameter="{Binding}"
|
||||||
|
IsEnabled="{Binding IsDirectory}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<PathIcon Data="{StaticResource arrow_right_regular}"></PathIcon>
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem Header="Download"
|
||||||
|
Command="{Binding $parent[Window].((vm:MainWindowViewModel)DataContext).DownloadContextCommand}"
|
||||||
|
CommandParameter="{Binding}"
|
||||||
|
IsEnabled="{Binding IsFile}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<PathIcon Data="{StaticResource arrow_download_regular}"></PathIcon>
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
</MenuFlyout>
|
||||||
|
</Border.ContextFlyout>
|
||||||
|
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||||
|
<PathIcon Data="{StaticResource folder_regular}"
|
||||||
|
IsVisible="{Binding IsDirectory}">
|
||||||
|
</PathIcon>
|
||||||
|
<PathIcon Data="{StaticResource document_regular}"
|
||||||
|
IsVisible="{Binding IsFile}">
|
||||||
|
</PathIcon>
|
||||||
|
<PathIcon Data="{StaticResource document_error_regular}"
|
||||||
|
IsVisible="{Binding IsSymbolicLink}">
|
||||||
|
</PathIcon>
|
||||||
|
<PathIcon Data="{StaticResource document_unknown_regular}"
|
||||||
|
IsVisible="{Binding IsUnknown}">
|
||||||
|
</PathIcon>
|
||||||
|
</StackPanel>
|
||||||
|
<Interaction.Behaviors>
|
||||||
|
<EventTriggerBehavior EventName="DoubleTapped">
|
||||||
|
<InvokeCommandAction
|
||||||
|
Command="{Binding $parent[Window].((vm:MainWindowViewModel)DataContext).OpenContextCommand}"
|
||||||
|
CommandParameter="{Binding}"
|
||||||
|
IsEnabled="{Binding IsDirectory}"/>
|
||||||
|
</EventTriggerBehavior>
|
||||||
|
</Interaction.Behaviors>
|
||||||
|
</Border>
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
<DataGridTemplateColumn Header="Name" Width="*" CanUserSort="False">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate DataType="models:FileInformation">
|
||||||
|
<Border Background="Transparent">
|
||||||
|
<Border.ContextFlyout>
|
||||||
|
<MenuFlyout>
|
||||||
|
<MenuItem Header="Open"
|
||||||
|
Command="{Binding $parent[Window].((vm:MainWindowViewModel)DataContext).OpenContextCommand}"
|
||||||
|
CommandParameter="{Binding}"
|
||||||
|
IsEnabled="{Binding IsDirectory}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<PathIcon Data="{StaticResource arrow_right_regular}"></PathIcon>
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem Header="Download"
|
||||||
|
Command="{Binding $parent[Window].((vm:MainWindowViewModel)DataContext).DownloadContextCommand}"
|
||||||
|
CommandParameter="{Binding}"
|
||||||
|
IsEnabled="{Binding IsFile}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<PathIcon Data="{StaticResource arrow_download_regular}"></PathIcon>
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
</MenuFlyout>
|
||||||
|
</Border.ContextFlyout>
|
||||||
|
<TextBlock Text="{Binding DisplayName}" VerticalAlignment="Center" />
|
||||||
|
<Interaction.Behaviors>
|
||||||
|
<EventTriggerBehavior EventName="DoubleTapped">
|
||||||
|
<InvokeCommandAction
|
||||||
|
Command="{Binding $parent[Window].((vm:MainWindowViewModel)DataContext).OpenContextCommand}"
|
||||||
|
CommandParameter="{Binding}"
|
||||||
|
IsEnabled="{Binding IsDirectory}"/>
|
||||||
|
</EventTriggerBehavior>
|
||||||
|
</Interaction.Behaviors>
|
||||||
|
</Border>
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
<DataGridTemplateColumn Header="Size" CanUserSort="False">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate DataType="models:FileInformation">
|
||||||
|
<Border Background="Transparent">
|
||||||
|
<Border.ContextFlyout>
|
||||||
|
<MenuFlyout>
|
||||||
|
<MenuItem Header="Open"
|
||||||
|
Command="{Binding $parent[Window].((vm:MainWindowViewModel)DataContext).OpenContextCommand}"
|
||||||
|
CommandParameter="{Binding}"
|
||||||
|
IsEnabled="{Binding IsDirectory}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<PathIcon Data="{StaticResource arrow_right_regular}"></PathIcon>
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem Header="Download"
|
||||||
|
Command="{Binding $parent[Window].((vm:MainWindowViewModel)DataContext).DownloadContextCommand}"
|
||||||
|
CommandParameter="{Binding}"
|
||||||
|
IsEnabled="{Binding IsFile}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<PathIcon Data="{StaticResource arrow_download_regular}"></PathIcon>
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
</MenuFlyout>
|
||||||
|
</Border.ContextFlyout>
|
||||||
|
<TextBlock Text="{Binding Size}" VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="Right" Margin="0 0 10 0" />
|
||||||
|
<Interaction.Behaviors>
|
||||||
|
<EventTriggerBehavior EventName="DoubleTapped">
|
||||||
|
<InvokeCommandAction
|
||||||
|
Command="{Binding $parent[Window].((vm:MainWindowViewModel)DataContext).OpenContextCommand}"
|
||||||
|
CommandParameter="{Binding}"
|
||||||
|
IsEnabled="{Binding IsDirectory}"/>
|
||||||
|
</EventTriggerBehavior>
|
||||||
|
</Interaction.Behaviors>
|
||||||
|
</Border>
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
<DataGridTemplateColumn Header="Date" CanUserSort="False">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate DataType="models:FileInformation">
|
||||||
|
<Border Background="Transparent">
|
||||||
|
<Border.ContextFlyout>
|
||||||
|
<MenuFlyout>
|
||||||
|
<MenuItem Header="Open"
|
||||||
|
Command="{Binding $parent[Window].((vm:MainWindowViewModel)DataContext).OpenContextCommand}"
|
||||||
|
CommandParameter="{Binding}"
|
||||||
|
IsEnabled="{Binding IsDirectory}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<PathIcon Data="{StaticResource arrow_right_regular}"></PathIcon>
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem Header="Download"
|
||||||
|
Command="{Binding $parent[Window].((vm:MainWindowViewModel)DataContext).DownloadContextCommand}"
|
||||||
|
CommandParameter="{Binding}"
|
||||||
|
IsEnabled="{Binding IsFile}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<PathIcon Data="{StaticResource arrow_download_regular}"></PathIcon>
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
</MenuFlyout>
|
||||||
|
</Border.ContextFlyout>
|
||||||
|
<TextBlock Text="{Binding DateTimeOffsetString}" VerticalAlignment="Center"
|
||||||
|
Margin="10 0 8 0" />
|
||||||
|
<Interaction.Behaviors>
|
||||||
|
<EventTriggerBehavior EventName="DoubleTapped">
|
||||||
|
<InvokeCommandAction
|
||||||
|
Command="{Binding $parent[Window].((vm:MainWindowViewModel)DataContext).OpenContextCommand}"
|
||||||
|
CommandParameter="{Binding}"
|
||||||
|
IsEnabled="{Binding IsDirectory}"/>
|
||||||
|
</EventTriggerBehavior>
|
||||||
|
</Interaction.Behaviors>
|
||||||
|
</Border>
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<DataGrid Grid.Row="1"
|
|
||||||
Name="FileInformationDataGrid"
|
|
||||||
Margin="2 0 2 0"
|
|
||||||
ItemsSource="{Binding FileInformation}"
|
|
||||||
IsReadOnly="True"
|
|
||||||
GridLinesVisibility="Horizontal"
|
|
||||||
BorderThickness="1"
|
|
||||||
SelectionMode="Single"
|
|
||||||
SelectedItem="{Binding SelectedFile}"
|
|
||||||
>
|
|
||||||
<DataGrid.Styles>
|
|
||||||
<Style Selector="DataGridColumnHeader">
|
|
||||||
<Setter Property="FontSize" Value="14"></Setter>
|
|
||||||
<Setter Property="Padding" Value="10"></Setter>
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridCell">
|
|
||||||
<Setter Property="FontSize" Value="12"></Setter>
|
|
||||||
</Style>
|
|
||||||
</DataGrid.Styles>
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTemplateColumn Header="Type">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate DataType="models:FileInformation">
|
|
||||||
<Border ToolTip.Tip="{Binding Type}" VerticalAlignment="Center" HorizontalAlignment="Center">
|
|
||||||
<StackPanel>
|
|
||||||
<PathIcon Data="{StaticResource folder_regular}" IsVisible="{Binding IsDirectory}"></PathIcon>
|
|
||||||
<PathIcon Data="{StaticResource document_regular}" IsVisible="{Binding IsFile}"></PathIcon>
|
|
||||||
<PathIcon Data="{StaticResource document_unknown_regular}" IsVisible="{Binding IsUnknown}"></PathIcon>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
<DataGridTextColumn Header="Name" Width="*" Binding="{Binding Name}" />
|
|
||||||
<DataGridTextColumn Header="Size" Binding="{Binding Size}" />
|
|
||||||
<DataGridTextColumn Header="Date" Binding="{Binding Date}" />
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
|
||||||
</Window>
|
</Window>
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<!-- This manifest is used on Windows only.
|
<!-- This manifest is used on Windows only.
|
||||||
Don't remove it as it might cause problems with window transparency and embeded controls.
|
Don't remove it as it might cause problems with window transparency and embeded controls.
|
||||||
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
|
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
|
||||||
<assemblyIdentity version="1.0.0.0" name="K8sFileBrowser.Desktop"/>
|
<assemblyIdentity version="1.7.0.0" name="K8sFileBrowser.Desktop"/>
|
||||||
|
|
||||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
<application>
|
<application>
|
||||||
|
|||||||
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 Andreas Billmann
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
12
README.md
@@ -2,3 +2,15 @@
|
|||||||
|
|
||||||
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`.
|
||||||
|
|
||||||
|

|
||||||
7
build.cmd
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
:; set -eo pipefail
|
||||||
|
:; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
|
||||||
|
:; ${SCRIPT_DIR}/build.sh "$@"
|
||||||
|
:; exit $?
|
||||||
|
|
||||||
|
@ECHO OFF
|
||||||
|
powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %*
|
||||||
74
build.ps1
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
Param(
|
||||||
|
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
|
||||||
|
[string[]]$BuildArguments
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Output "PowerShell $($PSVersionTable.PSEdition) version $($PSVersionTable.PSVersion)"
|
||||||
|
|
||||||
|
Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 }
|
||||||
|
$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
|
||||||
|
|
||||||
|
###########################################################################
|
||||||
|
# CONFIGURATION
|
||||||
|
###########################################################################
|
||||||
|
|
||||||
|
$BuildProjectFile = "$PSScriptRoot\build\_build.csproj"
|
||||||
|
$TempDirectory = "$PSScriptRoot\\.nuke\temp"
|
||||||
|
|
||||||
|
$DotNetGlobalFile = "$PSScriptRoot\\global.json"
|
||||||
|
$DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1"
|
||||||
|
$DotNetChannel = "STS"
|
||||||
|
|
||||||
|
$env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1
|
||||||
|
$env:DOTNET_CLI_TELEMETRY_OPTOUT = 1
|
||||||
|
$env:DOTNET_MULTILEVEL_LOOKUP = 0
|
||||||
|
|
||||||
|
###########################################################################
|
||||||
|
# EXECUTION
|
||||||
|
###########################################################################
|
||||||
|
|
||||||
|
function ExecSafe([scriptblock] $cmd) {
|
||||||
|
& $cmd
|
||||||
|
if ($LASTEXITCODE) { exit $LASTEXITCODE }
|
||||||
|
}
|
||||||
|
|
||||||
|
# If dotnet CLI is installed globally and it matches requested version, use for execution
|
||||||
|
if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and `
|
||||||
|
$(dotnet --version) -and $LASTEXITCODE -eq 0) {
|
||||||
|
$env:DOTNET_EXE = (Get-Command "dotnet").Path
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# Download install script
|
||||||
|
$DotNetInstallFile = "$TempDirectory\dotnet-install.ps1"
|
||||||
|
New-Item -ItemType Directory -Path $TempDirectory -Force | Out-Null
|
||||||
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||||
|
(New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile)
|
||||||
|
|
||||||
|
# If global.json exists, load expected version
|
||||||
|
if (Test-Path $DotNetGlobalFile) {
|
||||||
|
$DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json)
|
||||||
|
if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) {
|
||||||
|
$DotNetVersion = $DotNetGlobal.sdk.version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install by channel or version
|
||||||
|
$DotNetDirectory = "$TempDirectory\dotnet-win"
|
||||||
|
if (!(Test-Path variable:DotNetVersion)) {
|
||||||
|
ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath }
|
||||||
|
} else {
|
||||||
|
ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath }
|
||||||
|
}
|
||||||
|
$env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output "Microsoft (R) .NET SDK version $(& $env:DOTNET_EXE --version)"
|
||||||
|
|
||||||
|
if (Test-Path env:NUKE_ENTERPRISE_TOKEN) {
|
||||||
|
& $env:DOTNET_EXE nuget remove source "nuke-enterprise" > $null
|
||||||
|
& $env:DOTNET_EXE nuget add source "https://f.feedz.io/nuke/enterprise/nuget" --name "nuke-enterprise" --username "PAT" --password $env:NUKE_ENTERPRISE_TOKEN > $null
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet }
|
||||||
|
ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments }
|
||||||
67
build.sh
Executable file
@@ -0,0 +1,67 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
bash --version 2>&1 | head -n 1
|
||||||
|
|
||||||
|
set -eo pipefail
|
||||||
|
SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
|
||||||
|
|
||||||
|
###########################################################################
|
||||||
|
# CONFIGURATION
|
||||||
|
###########################################################################
|
||||||
|
|
||||||
|
BUILD_PROJECT_FILE="$SCRIPT_DIR/build/_build.csproj"
|
||||||
|
TEMP_DIRECTORY="$SCRIPT_DIR//.nuke/temp"
|
||||||
|
|
||||||
|
DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json"
|
||||||
|
DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh"
|
||||||
|
DOTNET_CHANNEL="STS"
|
||||||
|
|
||||||
|
export DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||||
|
export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
|
||||||
|
export DOTNET_MULTILEVEL_LOOKUP=0
|
||||||
|
|
||||||
|
###########################################################################
|
||||||
|
# EXECUTION
|
||||||
|
###########################################################################
|
||||||
|
|
||||||
|
function FirstJsonValue {
|
||||||
|
perl -nle 'print $1 if m{"'"$1"'": "([^"]+)",?}' <<< "${@:2}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# If dotnet CLI is installed globally and it matches requested version, use for execution
|
||||||
|
if [ -x "$(command -v dotnet)" ] && dotnet --version &>/dev/null; then
|
||||||
|
export DOTNET_EXE="$(command -v dotnet)"
|
||||||
|
else
|
||||||
|
# Download install script
|
||||||
|
DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh"
|
||||||
|
mkdir -p "$TEMP_DIRECTORY"
|
||||||
|
curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL"
|
||||||
|
chmod +x "$DOTNET_INSTALL_FILE"
|
||||||
|
|
||||||
|
# If global.json exists, load expected version
|
||||||
|
if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then
|
||||||
|
DOTNET_VERSION=$(FirstJsonValue "version" "$(cat "$DOTNET_GLOBAL_FILE")")
|
||||||
|
if [[ "$DOTNET_VERSION" == "" ]]; then
|
||||||
|
unset DOTNET_VERSION
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install by channel or version
|
||||||
|
DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix"
|
||||||
|
if [[ -z ${DOTNET_VERSION+x} ]]; then
|
||||||
|
"$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path
|
||||||
|
else
|
||||||
|
"$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path
|
||||||
|
fi
|
||||||
|
export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Microsoft (R) .NET SDK version $("$DOTNET_EXE" --version)"
|
||||||
|
|
||||||
|
if [[ ! -z ${NUKE_ENTERPRISE_TOKEN+x} && "NUKE_ENTERPRISE_TOKEN" != "" ]]; then
|
||||||
|
"$DOTNET_EXE" nuget remove source "nuke-enterprise" &>/dev/null || true
|
||||||
|
"$DOTNET_EXE" nuget add source "https://f.feedz.io/nuke/enterprise/nuget" --name "nuke-enterprise" --username "PAT" --password "$NUKE_ENTERPRISE_TOKEN" --store-password-in-clear-text &>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
"$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet
|
||||||
|
"$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@"
|
||||||
11
build/.editorconfig
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[*.cs]
|
||||||
|
dotnet_style_qualification_for_field = false:warning
|
||||||
|
dotnet_style_qualification_for_property = false:warning
|
||||||
|
dotnet_style_qualification_for_method = false:warning
|
||||||
|
dotnet_style_qualification_for_event = false:warning
|
||||||
|
dotnet_style_require_accessibility_modifiers = never:warning
|
||||||
|
|
||||||
|
csharp_style_expression_bodied_methods = true:silent
|
||||||
|
csharp_style_expression_bodied_properties = true:warning
|
||||||
|
csharp_style_expression_bodied_indexers = true:warning
|
||||||
|
csharp_style_expression_bodied_accessors = true:warning
|
||||||
120
build/Build.cs
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using Nuke.Common;
|
||||||
|
using Nuke.Common.IO;
|
||||||
|
using Nuke.Common.Tooling;
|
||||||
|
using Nuke.Common.Tools.DotNet;
|
||||||
|
using static Nuke.Common.Tools.DotNet.DotNetTasks;
|
||||||
|
|
||||||
|
class Build : NukeBuild
|
||||||
|
{
|
||||||
|
[Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")]
|
||||||
|
readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release;
|
||||||
|
|
||||||
|
[Parameter] readonly string Version = "1.7.0";
|
||||||
|
|
||||||
|
AbsolutePath SourceDirectory => RootDirectory / "K8sFileBrowser";
|
||||||
|
AbsolutePath OutputDirectory => RootDirectory / "output";
|
||||||
|
AbsolutePath WinOutputDirectory => OutputDirectory / "win";
|
||||||
|
AbsolutePath LinuxOutputDirectory => OutputDirectory / "linux";
|
||||||
|
AbsolutePath OsxOutputDirectory => OutputDirectory / "osx";
|
||||||
|
|
||||||
|
AbsolutePath WinZip => OutputDirectory / $"K8sFileBrowser_{Version}.zip";
|
||||||
|
AbsolutePath LinuxGz => OutputDirectory / $"K8sFileBrowser_{Version}.tgz";
|
||||||
|
AbsolutePath OsxGz => OutputDirectory / $"K8sFileBrowser_OSX_{Version}.tgz";
|
||||||
|
|
||||||
|
AbsolutePath ProjectFile => SourceDirectory / "K8sFileBrowser.csproj";
|
||||||
|
|
||||||
|
readonly string ExcludedExtensions = "pdb";
|
||||||
|
|
||||||
|
public static int Main () => Execute<Build>(x => x.Publish);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Target Clean => _ => _
|
||||||
|
.Executes(() =>
|
||||||
|
{
|
||||||
|
OutputDirectory.DeleteDirectory();
|
||||||
|
});
|
||||||
|
|
||||||
|
Target PublishWin => _ => _
|
||||||
|
.DependsOn(Clean)
|
||||||
|
.Executes(() =>
|
||||||
|
{
|
||||||
|
DotNetPublish(s => s
|
||||||
|
.SetProject(ProjectFile)
|
||||||
|
.SetConfiguration(Configuration)
|
||||||
|
.SetOutput(WinOutputDirectory)
|
||||||
|
.EnableSelfContained()
|
||||||
|
.SetFramework("net10.0")
|
||||||
|
.SetRuntime("win-x64")
|
||||||
|
.EnablePublishSingleFile()
|
||||||
|
.EnablePublishReadyToRun()
|
||||||
|
.SetAuthors("Andreas Billmann")
|
||||||
|
.SetCopyright("Copyright (c) 2025")
|
||||||
|
.SetVersion(Version)
|
||||||
|
.SetProcessAdditionalArguments("-p:IncludeNativeLibrariesForSelfExtract=true"));
|
||||||
|
|
||||||
|
WinOutputDirectory.ZipTo(
|
||||||
|
WinZip,
|
||||||
|
filter: x => !x.HasExtension(ExcludedExtensions),
|
||||||
|
compressionLevel: CompressionLevel.SmallestSize,
|
||||||
|
fileMode: FileMode.CreateNew);
|
||||||
|
});
|
||||||
|
|
||||||
|
Target PublishLinux => _ => _
|
||||||
|
.DependsOn(Clean)
|
||||||
|
.Executes(() =>
|
||||||
|
{
|
||||||
|
DotNetPublish(s => s
|
||||||
|
.SetProject(ProjectFile)
|
||||||
|
.SetConfiguration(Configuration)
|
||||||
|
.SetOutput(LinuxOutputDirectory)
|
||||||
|
.EnableSelfContained()
|
||||||
|
.SetFramework("net10.0")
|
||||||
|
.SetRuntime("linux-x64")
|
||||||
|
.EnablePublishSingleFile()
|
||||||
|
.EnablePublishReadyToRun()
|
||||||
|
.SetAuthors("Andreas Billmann")
|
||||||
|
.SetCopyright("Copyright (c) 2025")
|
||||||
|
.SetVersion(Version)
|
||||||
|
.SetProcessAdditionalArguments("-p:IncludeNativeLibrariesForSelfExtract=true"));
|
||||||
|
|
||||||
|
LinuxOutputDirectory.TarGZipTo(
|
||||||
|
LinuxGz,
|
||||||
|
filter: x => !x.HasExtension(ExcludedExtensions),
|
||||||
|
fileMode: FileMode.CreateNew);
|
||||||
|
});
|
||||||
|
|
||||||
|
Target PublishOsx => _ => _
|
||||||
|
.DependsOn(Clean)
|
||||||
|
.Executes(() =>
|
||||||
|
{
|
||||||
|
DotNetPublish(s => s
|
||||||
|
.SetProject(ProjectFile)
|
||||||
|
.SetConfiguration(Configuration)
|
||||||
|
.SetOutput(OsxOutputDirectory)
|
||||||
|
.EnableSelfContained()
|
||||||
|
.SetFramework("net10.0")
|
||||||
|
.SetRuntime("osx-arm64")
|
||||||
|
.EnablePublishSingleFile()
|
||||||
|
.EnablePublishReadyToRun()
|
||||||
|
.SetAuthors("Andreas Billmann")
|
||||||
|
.SetCopyright("Copyright (c) 2025")
|
||||||
|
.SetVersion(Version)
|
||||||
|
.SetProcessAdditionalArguments("-p:IncludeNativeLibrariesForSelfExtract=true"));
|
||||||
|
|
||||||
|
OsxOutputDirectory.TarGZipTo(
|
||||||
|
OsxGz,
|
||||||
|
filter: x => !x.HasExtension(ExcludedExtensions),
|
||||||
|
fileMode: FileMode.CreateNew);
|
||||||
|
});
|
||||||
|
|
||||||
|
Target Publish => _ => _
|
||||||
|
.DependsOn(PublishWin, PublishLinux, PublishOsx)
|
||||||
|
.Executes(() =>
|
||||||
|
{
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
16
build/Configuration.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using Nuke.Common.Tooling;
|
||||||
|
|
||||||
|
[TypeConverter(typeof(TypeConverter<Configuration>))]
|
||||||
|
public class Configuration : Enumeration
|
||||||
|
{
|
||||||
|
public static Configuration Debug = new Configuration { Value = nameof(Debug) };
|
||||||
|
public static Configuration Release = new Configuration { Value = nameof(Release) };
|
||||||
|
|
||||||
|
public static implicit operator string(Configuration configuration)
|
||||||
|
{
|
||||||
|
return configuration.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
8
build/Directory.Build.props
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
|
||||||
|
<!-- This file prevents unintended imports of unrelated MSBuild files -->
|
||||||
|
<!-- Uncomment to include parent Directory.Build.props file -->
|
||||||
|
<!--<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />-->
|
||||||
|
|
||||||
|
</Project>
|
||||||
8
build/Directory.Build.targets
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
|
||||||
|
<!-- This file prevents unintended imports of unrelated MSBuild files -->
|
||||||
|
<!-- Uncomment to include parent Directory.Build.targets file -->
|
||||||
|
<!--<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.targets', '$(MSBuildThisFileDirectory)../'))" />-->
|
||||||
|
|
||||||
|
</Project>
|
||||||
17
build/_build.csproj
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<RootNamespace></RootNamespace>
|
||||||
|
<NoWarn>CS0649;CS0169</NoWarn>
|
||||||
|
<NukeRootDirectory>..</NukeRootDirectory>
|
||||||
|
<NukeScriptDirectory>..</NukeScriptDirectory>
|
||||||
|
<NukeTelemetryVersion>1</NukeTelemetryVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Nuke.Common" Version="9.0.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
31
build/_build.csproj.DotSettings
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=HeapView_002EDelegateAllocation/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=VariableHidesOuterVariable/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ClassNeverInstantiated_002EGlobal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=MemberCanBeMadeStatic_002ELocal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=InterpolatedStringExpressionIsNotIFormattable/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/DEFAULT_INTERNAL_MODIFIER/@EntryValue">Implicit</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/DEFAULT_PRIVATE_MODIFIER/@EntryValue">Implicit</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/METHOD_OR_OPERATOR_BODY/@EntryValue">ExpressionBody</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/ThisQualifier/INSTANCE_MEMBERS_QUALIFY_MEMBERS/@EntryValue">0</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue">NEXT_LINE</s:String>
|
||||||
|
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_USER_LINEBREAKS/@EntryValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_AFTER_INVOCATION_LPAR/@EntryValue">False</s:Boolean>
|
||||||
|
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/MAX_ATTRIBUTE_LENGTH_FOR_SAME_LINE/@EntryValue">120</s:Int64>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FIELD_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">IF_OWNER_IS_SINGLE_LINE</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_ARGUMENTS_STYLE/@EntryValue">WRAP_IF_LONG</s:String>
|
||||||
|
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_ANONYMOUSMETHOD_ON_SINGLE_LINE/@EntryValue">False</s:Boolean>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=4a98fdf6_002D7d98_002D4f5a_002Dafeb_002Dea44ad98c70c/@EntryIndexedValue"><Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb" /></Policy></s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=f9fce829_002De6f4_002D4cb2_002D80f1_002D5497c44f51df/@EntryIndexedValue"><Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb" /></Policy></s:String>
|
||||||
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||||
7
global.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"sdk": {
|
||||||
|
"version": "10.0.0",
|
||||||
|
"rollForward": "latestFeature",
|
||||||
|
"allowPrerelease": false
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
screenshot.png
Normal file
|
After Width: | Height: | Size: 150 KiB |