Skip to content

Commit

Permalink
'gitrepo informer wip'
Browse files Browse the repository at this point in the history
  • Loading branch information
juozasg authored and Kingdon Barrett committed Jul 20, 2023
1 parent 994b128 commit ceebd88
Show file tree
Hide file tree
Showing 4 changed files with 220 additions and 2 deletions.
4 changes: 3 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import { CommandId, ContextId, GitOpsExtensionConstants } from './types/extensio
import { TelemetryEvent } from './types/telemetryEventNames';
import { promptToInstallFlux } from './ui/promptToInstallFlux';
import { statusBar } from './ui/statusBar';
import { clusterTreeViewProvider, createTreeViews, sourceTreeViewProvider, workloadTreeViewProvider } from './ui/treeviews/treeViews';
import { clusterTreeViewProvider, createTreeViews, sourceTreeViewProvider, templateTreeViewProvider, workloadTreeViewProvider } from './ui/treeviews/treeViews';
import { startFluxInformers } from 'flux/fluxInformer';

/** Disable interactive modal dialogs, useful for testing */
export let disableConfirmations = false;
Expand Down Expand Up @@ -44,6 +45,7 @@ export async function activate(context: ExtensionContext) {

// create gitops tree views
createTreeViews();
startFluxInformers(sourceTreeViewProvider, workloadTreeViewProvider, templateTreeViewProvider);

// register gitops commands
registerCommands(context);
Expand Down
194 changes: 194 additions & 0 deletions src/flux/fluxInformer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
// https://code.visualstudio.com/api/references/vscode-api#FileSystemWatcher
// make sure to test and debug switching kubeconfig from outside and from right click menu
// kubectl auth can-i watch gitrepository
// kubectl auth can-i watch kustomizations --all-namespaces
// kubectl api-resources -o wide
// experiment how the fallbacks work by default if watch is disabled

// flux v 0.42: /apis/source.toolkit.fluxcd.io/v1beta2/gitrepositories
// flux prerelease: /apis/source.toolkit.fluxcd.io/v1/gitrepositories

/*
* 1. exec kubectl proxy -p 0
* 2. parse result to get port
* 3. watch proxy for aliveness.
* 4. watch kubeconfig for changes and update proxy process
* 5. recreate informer as needed
*/
import * as k8s from '@kubernetes/client-node';
import { KubernetesListObject, KubernetesObject } from 'types/kubernetes/kubernetesTypes';

// export const fluxInformers: Record<string, FluxInformer> = {};

function startKubeProxy(): number | false {
return 57375;
}

// interface KubernetesCustomObject {
// 'apiVersion'?: string;
// 'kind'?: string;
// 'metadata'?: k8s.V1ObjectMeta;
// 'spec'?: k8s.V1PodSpec;
// 'status'?: k8s.V1PodStatus;
// }

// export class FluxInformer {
// public list(): KubernetesObject[] {
// return [];
// }
// }

function createKubeProxyConfig(port: number): k8s.KubeConfig {
const kcDefault = new k8s.KubeConfig();
kcDefault.loadFromDefault();

const cluster = {
name: kcDefault.getCurrentCluster()?.name,
server: `http://127.0.0.1:${port}`,
};

const user = kcDefault.getCurrentUser();

const context = {
name: kcDefault.getCurrentContext(),
user: user?.name,
cluster: cluster.name,
};

const kc = new k8s.KubeConfig();
kc.loadFromOptions({
clusters: [cluster],
users: [user],
contexts: [context],
currentContext: context.name,
});

return kc;
}

function getAPIPaths() {
return {
'gitrepositories': ['source.toolkit.fluxcd.io', 'v1'],
};
}


// will start a self-healing informer for each resource type and namespaces
export async function startFluxInformers(
sourceDataProvider: any,
workloadDataProvider: any,
templateDataProvider: any): Promise<boolean> {
const port = startKubeProxy();
if(!port) {
return false;
}

const kc = createKubeProxyConfig(port);

const k8sCoreApi = kc.makeApiClient(k8s.CoreV1Api);
const k8sCustomApi = kc.makeApiClient(k8s.CustomObjectsApi);

const plural = 'gitrepositories';
const [group, version] = getAPIPaths()[plural];

const listFn = async () => {
const result = await k8sCustomApi.listClusterCustomObject(group, version, plural);
const kbody = result.body as KubernetesListObject<KubernetesObject>;
return Promise.resolve({response: result.response, body: kbody});
};

// const listFn = () => {
// const x = k8sCoreApi.listNamespacedPod('default');
// return x;
// };

// listFnBad();
// const a: ListPromise<KubernetesOjbect>;

// const inf2 = k8s.make

const informer = k8s.makeInformer(
kc,
`/apis/${group}/${version}/${plural}`,
listFn,
);


informer.on('add', (obj: KubernetesObject) => {
console.log(`Added: ${obj.metadata?.name}`);
sourceDataProvider.add(obj);
});
informer.on('update', (obj: KubernetesObject) => {
console.log(`Updated: ${obj.metadata?.name}`);
sourceDataProvider.update(obj);
});
informer.on('delete', (obj: KubernetesObject) => {
console.log(`Deleted: ${obj.metadata?.name}`);
sourceDataProvider.delete(obj);
});
informer.on('error', (err: any) => {
console.error('ERRORed:', err);
// Restart informer after 5sec
setTimeout(() => {
console.log('Restarting informer...');
informer.start();
}, 2000);
});
try {
await informer.start();
} catch (err) {
console.error('flux resources informer unavailable:', err);
informer.stop();
setTimeout(() => {
console.info('Restarting informer...');
informer.start();
// this needs to be recursive
}, 2000);

return false;
}
const l = informer.list();
console.log('list:', l);

return true;
}


// const fn = k8sApi.depl

// k8sCoreApi.listPodForAllNamespaces();



// const watch = new k8s.Watch(kc);
// // watch.watch('/api/v1/namespaces', {},
// // watch.watch('/apis/apps/v1/deployments', {},
// watch.watch('/apis/source.toolkit.fluxcd.io/v1beta2/gitrepositories', {},
// (type, apiObj, watchObj) => {
// // console.log(type, apiObj, watchObj);
// if (type === 'ADDED') {
// // tslint:disable-next-line:no-console
// console.log(`NEW: ${apiObj.metadata.name}`);
// } else if (type === 'DELETED') {
// // tslint:disable-next-line:no-console
// console.log(`DELETED: ${apiObj.metadata.name}`);
// }
// },
// err => {
// console.log(err);
// },
// );



// const crApi = kc.makeApiClient(k8s.CustomObjectsApi);
// crApi.listClusterCustomObject('source.toolkit.fluxcd.io', 'v1',
// 'gitrepositories')
// .then(res => {
// console.log('GITREPO', res.body);
// }).catch(err => {
// console.log(err);
// });



1 change: 1 addition & 0 deletions src/types/kubernetes/kubernetesTypes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as k8s from '@kubernetes/client-node';

export type KubernetesObject = k8s.KubernetesObject;
export type KubernetesListObject<T extends KubernetesObject> = k8s.KubernetesListObject<T>;
export type Condition = k8s.V1Condition;

// Specify types from `@kubernetes/client-node`
Expand Down
23 changes: 22 additions & 1 deletion src/ui/treeviews/dataProviders/dataProvider.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Event, EventEmitter, TreeDataProvider, TreeItem } from 'vscode';
import { Namespace } from 'types/kubernetes/kubernetesTypes';
import { KubernetesObject, Namespace } from 'types/kubernetes/kubernetesTypes';
import { NamespaceNode } from '../nodes/namespaceNode';
import { TreeNode } from '../nodes/treeNode';

Expand Down Expand Up @@ -74,6 +74,27 @@ export class DataProvider implements TreeDataProvider<TreeItem> {
return Promise.resolve([]);
}

public add(object: KubernetesObject) {
// find or create namespace TreeItem for node
// add update TreeItem
// this.refresh(treeItem)
console.log('add', object);
}

public update(object: KubernetesObject) {
// find or create namespace TreeItem for node
// add update TreeItem
// this.refresh(treeItem)
console.log('add', object);
}

public delete(object: KubernetesObject) {
// find or create namespace TreeItem for node
// add update TreeItem
// this.refresh(treeItem)
console.log('add', object);
}

groupByNamespace(namespaces: Namespace[], nodes: TreeNode[]): NamespaceNode[] {
const namespaceNodes: NamespaceNode[] = [];

Expand Down

0 comments on commit ceebd88

Please sign in to comment.