Skip to content

Commit 1185797

Browse files
committed
Use vcluster auth when passing vcluster flag to user create
1 parent 20c51bb commit 1185797

File tree

2 files changed

+89
-9
lines changed

2 files changed

+89
-9
lines changed

src/lib/clusters/clusters.tsx

Lines changed: 82 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -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

435442
async function removeClusterUserAction({
@@ -472,9 +479,11 @@ async function removeClusterUserAction({
472479
async 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
}

src/lib/clusters/kubeconfig.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export interface Kubeconfig {
3131
kind: string;
3232
preferences: Record<string, unknown>;
3333
}
34+
3435
export function createKubeconfig(props: {
3536
clusters: Array<{
3637
name: string;
@@ -76,10 +77,14 @@ export function createKubeconfig(props: {
7677
let user = users.find((u) => u.name === cluster.name);
7778

7879
// If no matching user, default to the first user
79-
if (!user) {
80+
if (!user && users.length > 0) {
8081
user = users[0];
8182
}
8283

84+
if (!user) {
85+
return null;
86+
}
87+
8388
const contextName = `${cluster.name}@${user.name}`;
8489

8590
return {
@@ -90,7 +95,7 @@ export function createKubeconfig(props: {
9095
namespace: cluster.namespace,
9196
},
9297
};
93-
});
98+
}).filter(Boolean) as Kubeconfig["contexts"];
9499

95100
// Set current context based on provided cluster and user names
96101
if (currentContext) {

0 commit comments

Comments
 (0)