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