feat: upgrade to kubernetes client 1.x

This commit is contained in:
Aarnav Tale
2025-05-29 11:55:30 -04:00
parent b7009e387b
commit 59525b7b63
3 changed files with 419 additions and 347 deletions
+21 -27
View File
@@ -3,13 +3,18 @@ import { platform } from 'node:os';
import { join, resolve } from 'node:path'; import { join, resolve } from 'node:path';
import { kill } from 'node:process'; import { kill } from 'node:process';
import { setTimeout } from 'node:timers/promises'; import { setTimeout } from 'node:timers/promises';
import { Config, CoreV1Api, KubeConfig } from '@kubernetes/client-node'; import { CoreV1Api, KubeConfig } from '@kubernetes/client-node';
import { ApiClient } from '~/server/headscale/api-client'; import { ApiClient } from '~/server/headscale/api-client';
import log from '~/utils/log'; import log from '~/utils/log';
import { HeadplaneConfig } from '../schema'; import { HeadplaneConfig } from '../schema';
import { Integration } from './abstract'; import { Integration } from './abstract';
// TODO: Upgrade to the new CoreV1Api from @kubernetes/client-node // https://github.com/kubernetes-client/javascript/blob/055b83c6504dfd1b2a2d081efd974163c6cbb808/src/config.ts#L40
const svcRoot = '/var/run/secrets/kubernetes.io/serviceaccount';
const svcCaPath = `${svcRoot}/ca.crt`;
const svcTokenPath = `${svcRoot}/token`;
const svcNamespacePath = `${svcRoot}/namespace`;
type T = NonNullable<HeadplaneConfig['integration']>['kubernetes']; type T = NonNullable<HeadplaneConfig['integration']>['kubernetes'];
export default class KubernetesIntegration extends Integration<T> { export default class KubernetesIntegration extends Integration<T> {
private pid: number | undefined; private pid: number | undefined;
@@ -25,7 +30,6 @@ export default class KubernetesIntegration extends Integration<T> {
return false; return false;
} }
const svcRoot = Config.SERVICEACCOUNT_ROOT;
try { try {
log.debug('config', 'Checking Kubernetes service account at %s', svcRoot); log.debug('config', 'Checking Kubernetes service account at %s', svcRoot);
const files = await readdir(svcRoot); const files = await readdir(svcRoot);
@@ -35,11 +39,7 @@ export default class KubernetesIntegration extends Integration<T> {
} }
const mappedFiles = new Set(files.map((file) => join(svcRoot, file))); const mappedFiles = new Set(files.map((file) => join(svcRoot, file)));
const expectedFiles = [ const expectedFiles = [svcCaPath, svcTokenPath, svcNamespacePath];
Config.SERVICEACCOUNT_CA_PATH,
Config.SERVICEACCOUNT_TOKEN_PATH,
Config.SERVICEACCOUNT_NAMESPACE_PATH,
];
log.debug('config', 'Looking for %s', expectedFiles.join(', ')); log.debug('config', 'Looking for %s', expectedFiles.join(', '));
if (!expectedFiles.every((file) => mappedFiles.has(file))) { if (!expectedFiles.every((file) => mappedFiles.has(file))) {
@@ -52,10 +52,7 @@ export default class KubernetesIntegration extends Integration<T> {
} }
log.debug('config', 'Reading Kubernetes service account at %s', svcRoot); log.debug('config', 'Reading Kubernetes service account at %s', svcRoot);
const namespace = await readFile( const namespace = await readFile(svcNamespacePath, 'utf8');
Config.SERVICEACCOUNT_NAMESPACE_PATH,
'utf8',
);
// Some very ugly nesting but it's necessary // Some very ugly nesting but it's necessary
if (this.context.validate_manifest === false) { if (this.context.validate_manifest === false) {
@@ -99,36 +96,32 @@ export default class KubernetesIntegration extends Integration<T> {
const kCoreV1Api = kc.makeApiClient(CoreV1Api); const kCoreV1Api = kc.makeApiClient(CoreV1Api);
log.info( log.info('config', 'Checking pod %s in namespace %s', pod, namespace);
'config',
'Checking pod %s in namespace %s (%s)',
pod,
namespace,
kCoreV1Api.basePath,
);
log.debug('config', 'Reading pod info for %s', pod); log.debug('config', 'Reading pod info for %s', pod);
const { response, body } = await kCoreV1Api.readNamespacedPod( const body = await kCoreV1Api.readNamespacedPod({
pod, name: pod,
namespace, namespace,
); });
if (response.statusCode !== 200) { if (!body.spec) {
log.error( log.error(
'config', 'config',
'Failed to read pod info: http %d', 'Missing spec in pod info for %s/%s',
response.statusCode, pod,
namespace,
); );
return false; return false;
} }
log.debug('config', 'Got pod info: %o', body.spec); log.debug('config', 'Got pod info: %o', body.spec);
const shared = body.spec?.shareProcessNamespace; const shared = body.spec.shareProcessNamespace;
if (shared === undefined) { if (shared === undefined) {
log.error( log.error(
'config', 'config',
'Pod does not have spec.shareProcessNamespace set', 'Pod does not have spec.shareProcessNamespace set',
); );
return false; return false;
} }
@@ -137,6 +130,7 @@ export default class KubernetesIntegration extends Integration<T> {
'config', 'config',
'Pod has set but disabled spec.shareProcessNamespace', 'Pod has set but disabled spec.shareProcessNamespace',
); );
return false; return false;
} }
+1 -1
View File
@@ -17,7 +17,7 @@
"@dnd-kit/sortable": "^8.0.0", "@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2", "@dnd-kit/utilities": "^3.2.2",
"@fontsource-variable/inter": "^5.2.5", "@fontsource-variable/inter": "^5.2.5",
"@kubernetes/client-node": "^0.22.3", "@kubernetes/client-node": "^1.3.0",
"@primer/octicons-react": "^19.15.2", "@primer/octicons-react": "^19.15.2",
"@react-aria/toast": "3.0.3", "@react-aria/toast": "3.0.3",
"@react-router/node": "^7.6.1", "@react-router/node": "^7.6.1",
+397 -319
View File
File diff suppressed because it is too large Load Diff