@@ -58,12 +58,14 @@ export function registerClusters(program: Command) {
58
58
. option ( "--json" , "Output in JSON format" )
59
59
. option ( "--token <token>" , "API token" )
60
60
. option ( "--print" , "Print the kubeconfig instead of syncing to file" )
61
+ . option ( "--vcluster" , "Use vcluster configuration instead of direct namespace access" )
61
62
. action ( async ( options ) => {
62
63
await addClusterUserAction ( {
63
64
clusterName : options . cluster ,
64
65
username : options . user ,
65
66
token : options . token ,
66
67
print : options . print ,
68
+ vcluster : options . vcluster ,
67
69
} ) ;
68
70
} ) ;
69
71
@@ -94,10 +96,12 @@ export function registerClusters(program: Command) {
94
96
. description ( "Generate or sync kubeconfig" )
95
97
. option ( "--token <token>" , "API token" )
96
98
. option ( "--print" , "Print the config instead of syncing to file" )
99
+ . option ( "--vcluster" , "Use vcluster configuration instead of direct namespace access" )
97
100
. action ( async ( options ) => {
98
101
await kubeconfigAction ( {
99
102
token : options . token ,
100
103
print : options . print ,
104
+ vcluster : options . vcluster ,
101
105
} ) ;
102
106
} ) ;
103
107
}
@@ -315,6 +319,7 @@ function UserAddedDisplay(props: {
315
319
id : string ;
316
320
username : string ;
317
321
print ?: boolean ;
322
+ vcluster ?: boolean ;
318
323
} ) {
319
324
const [ isReady , setIsReady ] = useState ( false ) ;
320
325
const { exit } = useApp ( ) ;
@@ -327,14 +332,14 @@ function UserAddedDisplay(props: {
327
332
setIsReady ( true ) ;
328
333
329
334
// Once ready, sync or print config before exiting
330
- await kubeconfigAction ( { print : props . print } ) ;
335
+ await kubeconfigAction ( { print : props . print , vcluster : props . vcluster } ) ;
331
336
setTimeout ( ( ) => {
332
337
exit ( ) ;
333
338
} , 0 ) ;
334
339
}
335
340
} , 500 ) ;
336
341
return ( ) => clearInterval ( interval ) ;
337
- } , [ props . id , props . print ] ) ;
342
+ } , [ props . id , props . print , props . vcluster ] ) ;
338
343
339
344
if ( ! isReady ) {
340
345
return (
@@ -390,11 +395,13 @@ async function addClusterUserAction({
390
395
username : rawUsername ,
391
396
token,
392
397
print,
398
+ vcluster,
393
399
} : {
394
400
clusterName : string ;
395
401
username : string ;
396
402
token ?: string ;
397
403
print ?: boolean ;
404
+ vcluster ?: boolean ;
398
405
} ) {
399
406
const api = await apiClient ( token ) ;
400
407
@@ -429,7 +436,7 @@ async function addClusterUserAction({
429
436
}
430
437
431
438
// 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 } /> ) ;
433
440
}
434
441
435
442
async function removeClusterUserAction ( {
@@ -472,9 +479,11 @@ async function removeClusterUserAction({
472
479
async function kubeconfigAction ( {
473
480
token,
474
481
print,
482
+ vcluster,
475
483
} : {
476
484
token ?: string ;
477
485
print ?: boolean ;
486
+ vcluster ?: boolean ;
478
487
} ) {
479
488
const api = await apiClient ( token ) ;
480
489
@@ -506,6 +515,7 @@ async function kubeconfigAction({
506
515
namespace ?: string ;
507
516
} > = [ ] ;
508
517
const users : Array < { name : string ; token : string } > = [ ] ;
518
+
509
519
for ( const item of data . data ) {
510
520
if ( item . object !== "k8s_credential" ) {
511
521
continue ;
@@ -544,12 +554,77 @@ async function kubeconfigAction({
544
554
} ) ;
545
555
}
546
556
557
+ // First create and sync the regular kubeconfig to access the namespace
547
558
const kubeconfig = createKubeconfig ( { clusters, users } ) ;
548
559
549
- if ( print ) {
550
- console . log ( yaml . stringify ( kubeconfig ) ) ;
551
- } else {
560
+ if ( ! print ) {
552
561
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
+ }
554
629
}
555
630
}
0 commit comments