1
+ using Flow . Launcher . Plugin ;
2
+ using System . Diagnostics ;
3
+ using System . Text . Json ;
4
+
5
+ namespace Flow . Launcher . Plugin . KubernetesContextSwitcher ;
6
+
7
+ public class Main : IPlugin
8
+ {
9
+ private PluginInitContext _context = null ! ;
10
+ private string _kubectlPath = "kubectl" ;
11
+
12
+ public void Init ( PluginInitContext context )
13
+ {
14
+ _context = context ;
15
+
16
+ // Try to find kubectl in PATH or common locations
17
+ var kubectlPaths = new [ ]
18
+ {
19
+ "kubectl" ,
20
+ @"C:\Program Files\Docker\Docker\resources\bin\kubectl.exe" ,
21
+ @"C:\Users\%USERNAME%\AppData\Local\Microsoft\WinGet\Packages\Kubernetes.kubectl_Microsoft.Winget.Source_8wekyb3d8bbwe\kubectl.exe"
22
+ } ;
23
+
24
+ foreach ( var path in kubectlPaths )
25
+ {
26
+ if ( File . Exists ( path ) || IsCommandAvailable ( path ) )
27
+ {
28
+ _kubectlPath = path ;
29
+ break ;
30
+ }
31
+ }
32
+ }
33
+
34
+ public List < Result > Query ( Query query )
35
+ {
36
+ var results = new List < Result > ( ) ;
37
+ var searchTerm = query . Search . Trim ( ) ;
38
+
39
+ try
40
+ {
41
+ if ( string . IsNullOrEmpty ( searchTerm ) )
42
+ {
43
+ // Show current context and available contexts
44
+ var currentContext = GetCurrentContext ( ) ;
45
+ var contexts = GetAvailableContexts ( ) ;
46
+
47
+ results . Add ( new Result
48
+ {
49
+ Title = $ "Current: { currentContext } ",
50
+ SubTitle = "Current Kubernetes context" ,
51
+ IcoPath = "Images/k8s.png" ,
52
+ Score = 100
53
+ } ) ;
54
+
55
+ foreach ( var context in contexts . Where ( c => c != currentContext ) )
56
+ {
57
+ results . Add ( new Result
58
+ {
59
+ Title = context ,
60
+ SubTitle = $ "Switch to { context } ",
61
+ IcoPath = "Images/k8s.png" ,
62
+ Score = 90 ,
63
+ Action = e =>
64
+ {
65
+ SwitchContext ( context ) ;
66
+ _context . API . ShowMsg ( $ "Switched to context: { context } ") ;
67
+ return true ;
68
+ }
69
+ } ) ;
70
+ }
71
+ }
72
+ else
73
+ {
74
+ // Filter contexts based on search term
75
+ var contexts = GetAvailableContexts ( )
76
+ . Where ( c => c . Contains ( searchTerm , StringComparison . OrdinalIgnoreCase ) )
77
+ . ToList ( ) ;
78
+
79
+ var currentContext = GetCurrentContext ( ) ;
80
+
81
+ foreach ( var context in contexts )
82
+ {
83
+ var isCurrent = context == currentContext ;
84
+ results . Add ( new Result
85
+ {
86
+ Title = context + ( isCurrent ? " (current)" : "" ) ,
87
+ SubTitle = isCurrent ? "Current context" : $ "Switch to { context } ",
88
+ IcoPath = "Images/k8s.png" ,
89
+ Score = isCurrent ? 80 : 90 ,
90
+ Action = isCurrent ? null : e =>
91
+ {
92
+ SwitchContext ( context ) ;
93
+ _context . API . ShowMsg ( $ "Switched to context: { context } ") ;
94
+ return true ;
95
+ }
96
+ } ) ;
97
+ }
98
+ }
99
+ }
100
+ catch ( Exception ex )
101
+ {
102
+ results . Add ( new Result
103
+ {
104
+ Title = "Error" ,
105
+ SubTitle = ex . Message ,
106
+ IcoPath = "Images/error.png" ,
107
+ Score = 0
108
+ } ) ;
109
+ }
110
+
111
+ return results ;
112
+ }
113
+
114
+ private string GetCurrentContext ( )
115
+ {
116
+ var startInfo = new ProcessStartInfo
117
+ {
118
+ FileName = _kubectlPath ,
119
+ Arguments = "config current-context" ,
120
+ RedirectStandardOutput = true ,
121
+ RedirectStandardError = true ,
122
+ UseShellExecute = false ,
123
+ CreateNoWindow = true
124
+ } ;
125
+
126
+ using var process = Process . Start ( startInfo ) ;
127
+ if ( process == null )
128
+ throw new Exception ( "Failed to start kubectl process" ) ;
129
+
130
+ var output = process . StandardOutput . ReadToEnd ( ) ;
131
+ var error = process . StandardError . ReadToEnd ( ) ;
132
+ process . WaitForExit ( ) ;
133
+
134
+ if ( process . ExitCode != 0 )
135
+ throw new Exception ( $ "kubectl error: { error } ") ;
136
+
137
+ return output . Trim ( ) ;
138
+ }
139
+
140
+ private List < string > GetAvailableContexts ( )
141
+ {
142
+ var startInfo = new ProcessStartInfo
143
+ {
144
+ FileName = _kubectlPath ,
145
+ Arguments = "config get-contexts -o name" ,
146
+ RedirectStandardOutput = true ,
147
+ RedirectStandardError = true ,
148
+ UseShellExecute = false ,
149
+ CreateNoWindow = true
150
+ } ;
151
+
152
+ using var process = Process . Start ( startInfo ) ;
153
+ if ( process == null )
154
+ throw new Exception ( "Failed to start kubectl process" ) ;
155
+
156
+ var output = process . StandardOutput . ReadToEnd ( ) ;
157
+ var error = process . StandardError . ReadToEnd ( ) ;
158
+ process . WaitForExit ( ) ;
159
+
160
+ if ( process . ExitCode != 0 )
161
+ throw new Exception ( $ "kubectl error: { error } ") ;
162
+
163
+ return output . Split ( '\n ' , StringSplitOptions . RemoveEmptyEntries )
164
+ . Select ( line => line . Trim ( ) )
165
+ . Where ( line => ! string . IsNullOrEmpty ( line ) )
166
+ . ToList ( ) ;
167
+ }
168
+
169
+ private void SwitchContext ( string contextName )
170
+ {
171
+ var startInfo = new ProcessStartInfo
172
+ {
173
+ FileName = _kubectlPath ,
174
+ Arguments = $ "config use-context { contextName } ",
175
+ RedirectStandardOutput = true ,
176
+ RedirectStandardError = true ,
177
+ UseShellExecute = false ,
178
+ CreateNoWindow = true
179
+ } ;
180
+
181
+ using var process = Process . Start ( startInfo ) ;
182
+ if ( process == null )
183
+ throw new Exception ( "Failed to start kubectl process" ) ;
184
+
185
+ var output = process . StandardOutput . ReadToEnd ( ) ;
186
+ var error = process . StandardError . ReadToEnd ( ) ;
187
+ process . WaitForExit ( ) ;
188
+
189
+ if ( process . ExitCode != 0 )
190
+ throw new Exception ( $ "Failed to switch context: { error } ") ;
191
+ }
192
+
193
+ private bool IsCommandAvailable ( string command )
194
+ {
195
+ try
196
+ {
197
+ var startInfo = new ProcessStartInfo
198
+ {
199
+ FileName = "where" ,
200
+ Arguments = command ,
201
+ RedirectStandardOutput = true ,
202
+ UseShellExecute = false ,
203
+ CreateNoWindow = true
204
+ } ;
205
+
206
+ using var process = Process . Start ( startInfo ) ;
207
+ if ( process == null ) return false ;
208
+
209
+ var output = process . StandardOutput . ReadToEnd ( ) ;
210
+ process . WaitForExit ( ) ;
211
+
212
+ return process . ExitCode == 0 && ! string . IsNullOrEmpty ( output . Trim ( ) ) ;
213
+ }
214
+ catch
215
+ {
216
+ return false ;
217
+ }
218
+ }
219
+ }
0 commit comments