@@ -58,12 +58,14 @@ export function registerClusters(program: Command) {
5858 . option ( "--json" , "Output in JSON format" )
5959 . option ( "--token <token>" , "API token" )
6060 . option ( "--print" , "Print the kubeconfig instead of syncing to file" )
61+ . option ( "--vcluster" , "Use vcluster configuration instead of direct namespace access" )
6162 . action ( async ( options ) => {
6263 await addClusterUserAction ( {
6364 clusterName : options . cluster ,
6465 username : options . user ,
6566 token : options . token ,
6667 print : options . print ,
68+ vcluster : options . vcluster ,
6769 } ) ;
6870 } ) ;
6971
@@ -94,10 +96,12 @@ export function registerClusters(program: Command) {
9496 . description ( "Generate or sync kubeconfig" )
9597 . option ( "--token <token>" , "API token" )
9698 . option ( "--print" , "Print the config instead of syncing to file" )
99+ . option ( "--vcluster" , "Use vcluster configuration instead of direct namespace access" )
97100 . action ( async ( options ) => {
98101 await kubeconfigAction ( {
99102 token : options . token ,
100103 print : options . print ,
104+ vcluster : options . vcluster ,
101105 } ) ;
102106 } ) ;
103107}
@@ -315,6 +319,7 @@ function UserAddedDisplay(props: {
315319 id : string ;
316320 username : string ;
317321 print ?: boolean ;
322+ vcluster ?: boolean ;
318323} ) {
319324 const [ isReady , setIsReady ] = useState ( false ) ;
320325 const { exit } = useApp ( ) ;
@@ -327,14 +332,14 @@ function UserAddedDisplay(props: {
327332 setIsReady ( true ) ;
328333
329334 // Once ready, sync or print config before exiting
330- await kubeconfigAction ( { print : props . print } ) ;
335+ await kubeconfigAction ( { print : props . print , vcluster : props . vcluster } ) ;
331336 setTimeout ( ( ) => {
332337 exit ( ) ;
333338 } , 0 ) ;
334339 }
335340 } , 500 ) ;
336341 return ( ) => clearInterval ( interval ) ;
337- } , [ props . id , props . print ] ) ;
342+ } , [ props . id , props . print , props . vcluster ] ) ;
338343
339344 if ( ! isReady ) {
340345 return (
@@ -390,11 +395,13 @@ async function addClusterUserAction({
390395 username : rawUsername ,
391396 token,
392397 print,
398+ vcluster,
393399} : {
394400 clusterName : string ;
395401 username : string ;
396402 token ?: string ;
397403 print ?: boolean ;
404+ vcluster ?: boolean ;
398405} ) {
399406 const api = await apiClient ( token ) ;
400407
@@ -429,7 +436,7 @@ async function addClusterUserAction({
429436 }
430437
431438 // Render UI that waits for the user to become ready, then sync/print config
432- render ( < UserAddedDisplay id = { data . id } username = { username } print = { print } /> ) ;
439+ render ( < UserAddedDisplay id = { data . id } username = { username } print = { print } vcluster = { vcluster } /> ) ;
433440}
434441
435442async function removeClusterUserAction ( {
@@ -472,9 +479,11 @@ async function removeClusterUserAction({
472479async function kubeconfigAction ( {
473480 token,
474481 print,
482+ vcluster,
475483} : {
476484 token ?: string ;
477485 print ?: boolean ;
486+ vcluster ?: boolean ;
478487} ) {
479488 const api = await apiClient ( token ) ;
480489
@@ -506,6 +515,7 @@ async function kubeconfigAction({
506515 namespace ?: string ;
507516 } > = [ ] ;
508517 const users : Array < { name : string ; token : string } > = [ ] ;
518+
509519 for ( const item of data . data ) {
510520 if ( item . object !== "k8s_credential" ) {
511521 continue ;
@@ -544,12 +554,77 @@ async function kubeconfigAction({
544554 } ) ;
545555 }
546556
557+ // First create and sync the regular kubeconfig to access the namespace
547558 const kubeconfig = createKubeconfig ( { clusters, users } ) ;
548559
549- if ( print ) {
550- console . log ( yaml . stringify ( kubeconfig ) ) ;
551- } else {
560+ if ( ! print ) {
552561 await syncKubeconfig ( kubeconfig ) ;
553- console . log ( `Config written to ${ KUBECONFIG_PATH } ` ) ;
562+ if ( ! vcluster ) {
563+ console . log ( `Config written to ${ KUBECONFIG_PATH } ` ) ;
564+ }
565+ } else if ( ! vcluster ) {
566+ console . log ( yaml . stringify ( kubeconfig ) ) ;
567+ return ;
568+ }
569+
570+ // If vcluster flag is set, get the vcluster config after we've synced the regular config
571+ if ( vcluster ) {
572+ // Find the first valid cluster and user to use for vcluster config
573+ const clusterItem = data . data . find ( item =>
574+ item . object === "k8s_credential" &&
575+ item . cluster &&
576+ item . cluster . kubernetes_namespace
577+ ) ;
578+
579+ if ( ! clusterItem || ! clusterItem . cluster ) {
580+ console . error ( "No valid cluster found for vcluster configuration" ) ;
581+ return ;
582+ }
583+
584+ try {
585+ // Construct the namespace name
586+ const namespace = clusterItem . cluster . kubernetes_namespace ;
587+ const vclusterName = `vc-${ namespace } ` ;
588+
589+ console . log ( `Retrieving vcluster config from namespace ${ namespace } ...` ) ;
590+
591+ // Execute the kubectl command to get the vcluster config
592+ const { execSync } = require ( "node:child_process" ) ;
593+ const vclusterConfig = execSync (
594+ `kubectl get secret ${ vclusterName } -n ${ namespace } --template={{.data.config}} | base64 --decode` ,
595+ { encoding : "utf-8" }
596+ ) ;
597+
598+ // Parse the vcluster config
599+ const parsedConfig = yaml . parse ( vclusterConfig ) ;
600+
601+ if ( parsedConfig && parsedConfig . clusters && parsedConfig . clusters . length > 0 ) {
602+ // Replace the server URL with the original cluster URL
603+ for ( const cluster of parsedConfig . clusters ) {
604+ if ( cluster . cluster && cluster . cluster . server ) {
605+ // Keep the original server URL
606+ cluster . cluster . server = clusterItem . cluster . kubernetes_api_url || "" ;
607+ }
608+ }
609+
610+ // Use the modified vcluster config
611+ if ( print ) {
612+ console . log ( yaml . stringify ( parsedConfig ) ) ;
613+ } else {
614+ await syncKubeconfig ( parsedConfig ) ;
615+ console . log ( `vCluster config written to ${ KUBECONFIG_PATH } ` ) ;
616+ }
617+ } else {
618+ console . error ( "Invalid vcluster config format" ) ;
619+ if ( ! print ) {
620+ console . log ( "Regular kubeconfig has been written as a fallback" ) ;
621+ }
622+ }
623+ } catch ( error ) {
624+ console . error ( `Failed to get vcluster config: ${ error instanceof Error ? error . message : String ( error ) } ` ) ;
625+ if ( ! print ) {
626+ console . log ( "Regular kubeconfig has been written as a fallback" ) ;
627+ }
628+ }
554629 }
555630}
0 commit comments