Skip to content

[WIP] Use vcluster auth when passing vcluster flag to user create #86

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 82 additions & 7 deletions src/lib/clusters/clusters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,14 @@ export function registerClusters(program: Command) {
.option("--json", "Output in JSON format")
.option("--token <token>", "API token")
.option("--print", "Print the kubeconfig instead of syncing to file")
.option("--vcluster", "Use vcluster configuration instead of direct namespace access")
.action(async (options) => {
await addClusterUserAction({
clusterName: options.cluster,
username: options.user,
token: options.token,
print: options.print,
vcluster: options.vcluster,
});
});

Expand Down Expand Up @@ -94,10 +96,12 @@ export function registerClusters(program: Command) {
.description("Generate or sync kubeconfig")
.option("--token <token>", "API token")
.option("--print", "Print the config instead of syncing to file")
.option("--vcluster", "Use vcluster configuration instead of direct namespace access")
.action(async (options) => {
await kubeconfigAction({
token: options.token,
print: options.print,
vcluster: options.vcluster,
});
});
}
Expand Down Expand Up @@ -315,6 +319,7 @@ function UserAddedDisplay(props: {
id: string;
username: string;
print?: boolean;
vcluster?: boolean;
}) {
const [isReady, setIsReady] = useState(false);
const { exit } = useApp();
Expand All @@ -327,14 +332,14 @@ function UserAddedDisplay(props: {
setIsReady(true);

// Once ready, sync or print config before exiting
await kubeconfigAction({ print: props.print });
await kubeconfigAction({ print: props.print, vcluster: props.vcluster });
setTimeout(() => {
exit();
}, 0);
}
}, 500);
return () => clearInterval(interval);
}, [props.id, props.print]);
}, [props.id, props.print, props.vcluster]);

if (!isReady) {
return (
Expand Down Expand Up @@ -390,11 +395,13 @@ async function addClusterUserAction({
username: rawUsername,
token,
print,
vcluster,
}: {
clusterName: string;
username: string;
token?: string;
print?: boolean;
vcluster?: boolean;
}) {
const api = await apiClient(token);

Expand Down Expand Up @@ -429,7 +436,7 @@ async function addClusterUserAction({
}

// Render UI that waits for the user to become ready, then sync/print config
render(<UserAddedDisplay id={data.id} username={username} print={print} />);
render(<UserAddedDisplay id={data.id} username={username} print={print} vcluster={vcluster} />);
}

async function removeClusterUserAction({
Expand Down Expand Up @@ -472,9 +479,11 @@ async function removeClusterUserAction({
async function kubeconfigAction({
token,
print,
vcluster,
}: {
token?: string;
print?: boolean;
vcluster?: boolean;
}) {
const api = await apiClient(token);

Expand Down Expand Up @@ -506,6 +515,7 @@ async function kubeconfigAction({
namespace?: string;
}> = [];
const users: Array<{ name: string; token: string }> = [];

for (const item of data.data) {
if (item.object !== "k8s_credential") {
continue;
Expand Down Expand Up @@ -544,12 +554,77 @@ async function kubeconfigAction({
});
}

// First create and sync the regular kubeconfig to access the namespace
const kubeconfig = createKubeconfig({ clusters, users });

if (print) {
console.log(yaml.stringify(kubeconfig));
} else {
if (!print) {
await syncKubeconfig(kubeconfig);
console.log(`Config written to ${KUBECONFIG_PATH}`);
if (!vcluster) {
console.log(`Config written to ${KUBECONFIG_PATH}`);
}
} else if (!vcluster) {
console.log(yaml.stringify(kubeconfig));
return;
}

// If vcluster flag is set, get the vcluster config after we've synced the regular config
if (vcluster) {
// Find the first valid cluster and user to use for vcluster config
const clusterItem = data.data.find(item =>
item.object === "k8s_credential" &&
item.cluster &&
item.cluster.kubernetes_namespace
);

if (!clusterItem || !clusterItem.cluster) {
console.error("No valid cluster found for vcluster configuration");
return;
}

try {
// Construct the namespace name
const namespace = clusterItem.cluster.kubernetes_namespace;
const vclusterName = `vc-${namespace}`;

console.log(`Retrieving vcluster config from namespace ${namespace}...`);

// Execute the kubectl command to get the vcluster config
const { execSync } = require("node:child_process");
const vclusterConfig = execSync(
`kubectl get secret ${vclusterName} -n ${namespace} --template={{.data.config}} | base64 --decode`,
{ encoding: "utf-8" }
);

// Parse the vcluster config
const parsedConfig = yaml.parse(vclusterConfig);

if (parsedConfig && parsedConfig.clusters && parsedConfig.clusters.length > 0) {
// Replace the server URL with the original cluster URL
for (const cluster of parsedConfig.clusters) {
if (cluster.cluster && cluster.cluster.server) {
// Keep the original server URL
cluster.cluster.server = clusterItem.cluster.kubernetes_api_url || "";
}
}

// Use the modified vcluster config
if (print) {
console.log(yaml.stringify(parsedConfig));
} else {
await syncKubeconfig(parsedConfig);
console.log(`vCluster config written to ${KUBECONFIG_PATH}`);
}
} else {
console.error("Invalid vcluster config format");
if (!print) {
console.log("Regular kubeconfig has been written as a fallback");
}
}
} catch (error) {
console.error(`Failed to get vcluster config: ${error instanceof Error ? error.message : String(error)}`);
if (!print) {
console.log("Regular kubeconfig has been written as a fallback");
}
}
}
}
9 changes: 7 additions & 2 deletions src/lib/clusters/kubeconfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface Kubeconfig {
kind: string;
preferences: Record<string, unknown>;
}

export function createKubeconfig(props: {
clusters: Array<{
name: string;
Expand Down Expand Up @@ -76,10 +77,14 @@ export function createKubeconfig(props: {
let user = users.find((u) => u.name === cluster.name);

// If no matching user, default to the first user
if (!user) {
if (!user && users.length > 0) {
user = users[0];
}

if (!user) {
return null;
}

const contextName = `${cluster.name}@${user.name}`;

return {
Expand All @@ -90,7 +95,7 @@ export function createKubeconfig(props: {
namespace: cluster.namespace,
},
};
});
}).filter(Boolean) as Kubeconfig["contexts"];

// Set current context based on provided cluster and user names
if (currentContext) {
Expand Down
Loading