From 34c6010896251443fd59baede4b42b03e21c9462 Mon Sep 17 00:00:00 2001 From: Clinton Medbery Date: Thu, 4 May 2023 16:15:09 -0400 Subject: [PATCH] chore: update examples for node fetch Also fixed a bug with ListPromise still returning body and response. --- FETCH_MIGRATION.md | 36 ++++-- examples/cache-example.js | 7 +- examples/example.js | 12 +- examples/follow-logs.js | 31 +++-- examples/in-cluster.js | 11 +- examples/ingress.js | 64 ++++++---- examples/top_pods.js | 39 +++--- src/cache.ts | 3 +- src/cache_test.ts | 244 ++++++++++++-------------------------- src/informer.ts | 7 +- 10 files changed, 200 insertions(+), 254 deletions(-) diff --git a/FETCH_MIGRATION.md b/FETCH_MIGRATION.md index 3b72954e211..36be2b8c9b1 100644 --- a/FETCH_MIGRATION.md +++ b/FETCH_MIGRATION.md @@ -52,29 +52,41 @@ Code will be on the `master` branch. - [x] Fix errors in /src folder (due to new api) - [x] migrate src/auth.ts, the dependent implementations (ex: azure_auth, gcp_auth etc) and tests to fetch api from request - [x] migrate src/log.ts and its tests to fetch api from request - - [x] major remaining work is fixing up async signatures and return piping + - [x] major remaining work is fixing up async signatures and return piping - [x] migrate src/watch.ts and its tests to fetch api from request - - [x] remove decprecated requestImpl and RequestInterface - - [x] implement queryParams parameter in watch method by injecting them into the fetch call - - [x] update tests in src/watch_test.ts + - [x] remove decprecated requestImpl and RequestInterface + - [x] implement queryParams parameter in watch method by injecting them into the fetch call + - [x] update tests in src/watch_test.ts - [x] Fix errors in test (due to new api) - [ ] Test all features -- [ ] Fix examples and validate their param signatures (due to new api) +- [ ] Fix JavaScript examples and validate their param signatures (due to new api) - - [ ] cache-example - - [ ] example - - [ ] follow-logs + - [x] cache-example + - [x] example + - [x] follow-logs - [ ] in-cluster-create-job-from-cronjob - - [ ] in-cluster - - [ ] ingress + - [x] in-cluster + - [x] ingress - [ ] namespace - [ ] patch-example - [ ] raw-example (note: uses request lib directly, will require full fetch migration not just client param swap) - [ ] scale-deployment - - [ ] top_pods - - [ ] top + - [x] top_pods + - [x] top - [ ] yaml-example +- [ ] Fix TypeScript examples and validate their param signatures (due to new api) + + - [ ] apply-example + - [ ] attach-example + - [ ] cp-example + - [ ] exec-example + - [ ] informer-with-label-selector + - [ ] informer + - [ ] port-forward + - [ ] example + - [ ] watch-example + - [ ] Update docs - [ ] Update README examples - [ ] Document breaking changes for users diff --git a/examples/cache-example.js b/examples/cache-example.js index e489633edec..3094f80a348 100644 --- a/examples/cache-example.js +++ b/examples/cache-example.js @@ -1,4 +1,5 @@ -const k8s = require('@kubernetes/client-node'); +// in a real program use require('@kubernetes/client-node') +const k8s = require('../dist/index'); const kc = new k8s.KubeConfig(); kc.loadFromDefault(); @@ -8,7 +9,7 @@ const k8sApi = kc.makeApiClient(k8s.CoreV1Api); const path = '/api/v1/pods'; const watch = new k8s.Watch(kc); -const listFn = () => k8sApi.listPodForAllNamespaces() +const listFn = () => k8sApi.listPodForAllNamespaces(); const cache = new k8s.ListWatch(path, watch, listFn); @@ -22,6 +23,6 @@ const looper = () => { console.log(names.join(',')); } setTimeout(looper, 2000); -} +}; looper(); diff --git a/examples/example.js b/examples/example.js index 414922cf274..849c0d2ecb2 100644 --- a/examples/example.js +++ b/examples/example.js @@ -1,12 +1,16 @@ -const k8s = require('@kubernetes/client-node'); +// in a real program use require('@kubernetes/client-node') +const k8s = require('../dist/index'); const kc = new k8s.KubeConfig(); kc.loadFromDefault(); const k8sApi = kc.makeApiClient(k8s.CoreV1Api); -k8sApi.listNamespacedPod('default') +k8sApi + .listNamespacedPod({ namespace: 'default' }) .then((res) => { - console.log(res.body); + console.log(res); + }) + .catch((err) => { + console.error(err); }); - diff --git a/examples/follow-logs.js b/examples/follow-logs.js index caa7e982af9..40fabbdc668 100644 --- a/examples/follow-logs.js +++ b/examples/follow-logs.js @@ -1,5 +1,6 @@ const stream = require('stream'); -const k8s = require('@kubernetes/client-node'); +// in a real program use require('@kubernetes/client-node') +const k8s = require('../dist/index'); const kc = new k8s.KubeConfig(); kc.loadFromDefault(); @@ -9,15 +10,23 @@ const log = new k8s.Log(kc); const logStream = new stream.PassThrough(); logStream.on('data', (chunk) => { - // use write rather than console.log to prevent double line feed - process.stdout.write(chunk); + // use write rather than console.log to prevent double line feed + process.stdout.write(chunk); }); -log.log('default', 'pod1', 'container1', logStream, {follow: true, tailLines: 50, pretty: false, timestamps: false}) -.catch(err => {console.log(err)}) -.then(req => { - // disconnects after 5 seconds - setTimeout(function(){ - req.abort(); - }, 5000); -}); +log.log('default', 'pod1', 'container1', logStream, { + follow: true, + tailLines: 50, + pretty: false, + timestamps: false, +}) + .catch((err) => { + console.error(err); + }) + .then((req) => { + // disconnects after 5 seconds + setTimeout(function () { + //Note: You might have to install AbortController if you are using node version < 15.0.0 + req.abort(); + }, 5000); + }); diff --git a/examples/in-cluster.js b/examples/in-cluster.js index db221e53755..5e99afbf2b3 100644 --- a/examples/in-cluster.js +++ b/examples/in-cluster.js @@ -1,15 +1,16 @@ -const k8s = require('@kubernetes/client-node'); +// in a real program use require('@kubernetes/client-node') +const k8s = require('../dist/index'); const kc = new k8s.KubeConfig(); kc.loadFromCluster(); const k8sApi = kc.makeApiClient(k8s.CoreV1Api); -k8sApi.listNamespacedPod('default') +k8sApi + .listNamespacedPod({ namespace: 'default' }) .then((res) => { - console.log(res.body); + console.log(res); }) .catch((err) => { - console.log(err); + console.error(err); }); - diff --git a/examples/ingress.js b/examples/ingress.js index 566511d74da..0b382161382 100644 --- a/examples/ingress.js +++ b/examples/ingress.js @@ -1,27 +1,41 @@ -const k8s = require('@kubernetes/client-node') -const kc = new k8s.KubeConfig() -kc.loadFromDefault() +// in a real program use require('@kubernetes/client-node') +const k8s = require('../dist/index'); -const k8sApi = kc.makeApiClient(k8s.NetworkingV1beta1Api) // before 1.14 use extensions/v1beta1 -const clientIdentifier = 'my-subdomain' +const kc = new k8s.KubeConfig(); +kc.loadFromDefault(); -k8sApi.createNamespacedIngress('default', { - apiVersions: 'networking.k8s.io/v1beta1', - kind: 'Ingress', - metadata: { name: `production-custom-${clientIdentifier}` }, - spec: { - rules: [{ - host: `${clientIdentifier}.example.com`, - http: { - paths: [{ - backend: { - serviceName: 'production-auto-deploy', - servicePort: 5000 - }, - path: '/' - }] - } - }], - tls: [{ hosts: [`${clientIdentifier}.example.com`] }] - } -}).catch(e => console.log(e)) +const k8sApi = kc.makeApiClient(k8s.NetworkingV1Api); +const clientIdentifier = 'my-subdomain'; + +k8sApi + .createNamespacedIngress({ + namespace: 'default', + body: { + apiVersion: 'networking.k8s.io/v1', + kind: 'Ingress', + metadata: { name: `production-custom-${clientIdentifier}` }, + spec: { + rules: [ + { + host: `${clientIdentifier}.example.com`, + http: { + paths: [ + { + backend: { + service: { + name: 'production-auto-deploy', + port: { number: 5000 }, + }, + }, + path: '/', + pathType: 'ImplementationSpecific', + }, + ], + }, + }, + ], + tls: [{ hosts: [`${clientIdentifier}.example.com`] }], + }, + }, + }) + .catch((e) => console.error(e)); diff --git a/examples/top_pods.js b/examples/top_pods.js index 1f3bd5b1203..37e94c74981 100644 --- a/examples/top_pods.js +++ b/examples/top_pods.js @@ -1,3 +1,4 @@ +// in a real program use require('@kubernetes/client-node') const k8s = require('../dist/index'); const kc = new k8s.KubeConfig(); @@ -6,34 +7,30 @@ kc.loadFromDefault(); const k8sApi = kc.makeApiClient(k8s.CoreV1Api); const metricsClient = new k8s.Metrics(kc); -k8s.topPods(k8sApi, metricsClient, "kube-system") -.then((pods) => { - +k8s.topPods(k8sApi, metricsClient, 'kube-system').then((pods) => { const podsColumns = pods.map((pod) => { return { - "POD": pod.Pod.metadata.name, - "CPU(cores)": pod.CPU.CurrentUsage, - "MEMORY(bytes)": pod.Memory.CurrentUsage, - } + POD: pod.Pod.metadata?.name, + 'CPU(cores)': pod.CPU.CurrentUsage, + 'MEMORY(bytes)': pod.Memory.CurrentUsage, + }; }); - console.log("TOP PODS") - console.table(podsColumns) + console.log('TOP PODS'); + console.table(podsColumns); }); -k8s.topPods(k8sApi, metricsClient, "kube-system") -.then((pods) => { - +k8s.topPods(k8sApi, metricsClient, 'kube-system').then((pods) => { const podsAndContainersColumns = pods.flatMap((pod) => { - return pod.Containers.map(containerUsage => { + return pod.Containers.map((containerUsage) => { return { - "POD": pod.Pod.metadata.name, - "NAME": containerUsage.Container, - "CPU(cores)": containerUsage.CPUUsage.CurrentUsage, - "MEMORY(bytes)": containerUsage.MemoryUsage.CurrentUsage, + POD: pod.Pod.metadata?.name, + NAME: containerUsage.Container, + 'CPU(cores)': containerUsage.CPUUsage.CurrentUsage, + 'MEMORY(bytes)': containerUsage.MemoryUsage.CurrentUsage, }; - }) + }); }); - console.log("TOP CONTAINERS") - console.table(podsAndContainersColumns) -}); \ No newline at end of file + console.log('TOP CONTAINERS'); + console.table(podsAndContainersColumns); +}); diff --git a/src/cache.ts b/src/cache.ts index 169a41395dd..d5233a73d39 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -132,8 +132,7 @@ export class ListWatch implements ObjectCache, In this.callbackCache[CONNECT].forEach((elt: ErrorCallback) => elt(undefined)); if (!this.resourceVersion) { const promise = this.listFn(); - const result = await promise; - const list = result.body; + const list = await promise; this.objects = deleteItems(this.objects, list.items, this.callbackCache[DELETE].slice()); Object.keys(this.indexCache).forEach((key) => { const updateObjects = deleteItems(this.indexCache[key], list.items); diff --git a/src/cache_test.ts b/src/cache_test.ts index 49c13dd055d..559440d42d5 100644 --- a/src/cache_test.ts +++ b/src/cache_test.ts @@ -46,23 +46,15 @@ const fakeConfig: { describe('ListWatchCache', () => { it('should throw on unknown update', () => { const fake = mock.mock(Watch); - const listFn: ListPromise = function (): Promise<{ - response: http.IncomingMessage; - body: V1NamespaceList; - }> { - return new Promise<{ response: http.IncomingMessage; body: V1NamespaceList }>( - (resolve, reject) => { - resolve({ - response: {} as http.IncomingMessage, - body: { - metadata: { - resourceVersion: '12345', - } as V1ListMeta, - items: [], - } as V1NamespaceList, - }); - }, - ); + const listFn: ListPromise = function (): Promise { + return new Promise((resolve, reject) => { + resolve({ + metadata: { + resourceVersion: '12345', + } as V1ListMeta, + items: [], + } as V1NamespaceList); + }); }; const lw = new ListWatch('/some/path', fake, listFn); const verb = 'FOOBAR'; @@ -109,19 +101,14 @@ describe('ListWatchCache', () => { } as V1NamespaceList; let calls = 0; - const listFn: ListPromise = function (): Promise<{ - response: http.IncomingMessage; - body: V1NamespaceList; - }> { - return new Promise<{ response: http.IncomingMessage; body: V1NamespaceList }>( - (resolve, reject) => { - if (calls++ === 0) { - resolve({ response: {} as http.IncomingMessage, body: listObj }); - } else { - resolve({ response: {} as http.IncomingMessage, body: emptyObj }); - } - }, - ); + const listFn: ListPromise = function (): Promise { + return new Promise((resolve, reject) => { + if (calls++ === 0) { + resolve(listObj); + } else { + resolve(emptyObj); + } + }); }; const promise = new Promise((resolve) => { mock.when( @@ -219,15 +206,10 @@ describe('ListWatchCache', () => { items: list, } as V1NamespaceList; - const listFn: ListPromise = function (): Promise<{ - response: http.IncomingMessage; - body: V1NamespaceList; - }> { - return new Promise<{ response: http.IncomingMessage; body: V1NamespaceList }>( - (resolve, reject) => { - resolve({ response: {} as http.IncomingMessage, body: listObj }); - }, - ); + const listFn: ListPromise = function (): Promise { + return new Promise((resolve, reject) => { + resolve(listObj); + }); }; const promise = new Promise((resolve) => { mock.when( @@ -308,15 +290,10 @@ describe('ListWatchCache', () => { items: list, } as V1NamespaceList; - const listFn: ListPromise = function (): Promise<{ - response: http.IncomingMessage; - body: V1NamespaceList; - }> { - return new Promise<{ response: http.IncomingMessage; body: V1NamespaceList }>( - (resolve, reject) => { - resolve({ response: {} as http.IncomingMessage, body: listObj }); - }, - ); + const listFn: ListPromise = function (): Promise { + return new Promise((resolve, reject) => { + resolve(listObj); + }); }; const promise = new Promise((resolve) => { mock.when( @@ -371,15 +348,10 @@ describe('ListWatchCache', () => { items: [], } as V1NamespaceList; - const listFn: ListPromise = function (): Promise<{ - response: http.IncomingMessage; - body: V1NamespaceList; - }> { - return new Promise<{ response: http.IncomingMessage; body: V1NamespaceList }>( - (resolve, reject) => { - resolve({ response: {} as http.IncomingMessage, body: listObj }); - }, - ); + const listFn: ListPromise = function (): Promise { + return new Promise((resolve, reject) => { + resolve(listObj); + }); }; const promise = new Promise((resolve) => { mock.when( @@ -440,15 +412,10 @@ describe('ListWatchCache', () => { items: list, } as V1NamespaceList; - const listFn: ListPromise = function (): Promise<{ - response: http.IncomingMessage; - body: V1NamespaceList; - }> { - return new Promise<{ response: http.IncomingMessage; body: V1NamespaceList }>( - (resolve, reject) => { - resolve({ response: {} as http.IncomingMessage, body: listObj }); - }, - ); + const listFn: ListPromise = function (): Promise { + return new Promise((resolve, reject) => { + resolve(listObj); + }); }; let promise = new Promise((resolve) => { mock.when( @@ -516,15 +483,10 @@ describe('ListWatchCache', () => { items: list, } as V1NamespaceList; - const listFn: ListPromise = function (): Promise<{ - response: http.IncomingMessage; - body: V1NamespaceList; - }> { - return new Promise<{ response: http.IncomingMessage; body: V1NamespaceList }>( - (resolve, reject) => { - resolve({ response: {} as http.IncomingMessage, body: listObj }); - }, - ); + const listFn: ListPromise = function (): Promise { + return new Promise((resolve, reject) => { + resolve(listObj); + }); }; let promise = new Promise((resolve) => { mock.when( @@ -596,15 +558,10 @@ describe('ListWatchCache', () => { items: list, } as V1NamespaceList; - const listFn: ListPromise = function (): Promise<{ - response: http.IncomingMessage; - body: V1NamespaceList; - }> { - return new Promise<{ response: http.IncomingMessage; body: V1NamespaceList }>( - (resolve, reject) => { - resolve({ response: {} as http.IncomingMessage, body: listObj }); - }, - ); + const listFn: ListPromise = function (): Promise { + return new Promise((resolve, reject) => { + resolve(listObj); + }); }; const promise = new Promise((resolve) => { mock.when( @@ -720,12 +677,9 @@ describe('ListWatchCache', () => { } as V1ListMeta, items: list, } as V1NamespaceList; - const listFn: ListPromise = function (): Promise<{ - response: http.IncomingMessage; - body: V1NamespaceList; - }> { - return new Promise<{ response: http.IncomingMessage; body: V1NamespaceList }>((resolve) => { - resolve({ response: {} as http.IncomingMessage, body: listObj }); + const listFn: ListPromise = function (): Promise { + return new Promise((resolve) => { + resolve(listObj); }); }; const watchCalled = new Promise((resolve) => { @@ -779,12 +733,9 @@ describe('ListWatchCache', () => { } as V1ListMeta, items: list, } as V1NamespaceList; - const listFn: ListPromise = function (): Promise<{ - response: http.IncomingMessage; - body: V1NamespaceList; - }> { - return new Promise<{ response: http.IncomingMessage; body: V1NamespaceList }>((resolve) => { - resolve({ response: {} as http.IncomingMessage, body: listObj }); + const listFn: ListPromise = function (): Promise { + return new Promise((resolve) => { + resolve(listObj); }); }; const watchCalled = new Promise((resolve) => { @@ -840,14 +791,11 @@ describe('ListWatchCache', () => { items: list, } as V1NamespaceList; - const listFn: ListPromise = function (): Promise<{ - response: http.IncomingMessage; - body: V1NamespaceList; - }> { - return new Promise<{ response: http.IncomingMessage; body: V1NamespaceList }>((resolve) => { + const listFn: ListPromise = function (): Promise { + return new Promise((resolve) => { // setImmediate will defer the resolve to the next message loop to keep the list from being immediately available setImmediate(() => { - resolve({ response: {} as http.IncomingMessage, body: listObj }); + resolve(listObj); }); }); }; @@ -867,12 +815,9 @@ describe('ListWatchCache', () => { } as V1ListMeta, items: list, } as V1NamespaceList; - const listFn: ListPromise = function (): Promise<{ - response: http.IncomingMessage; - body: V1NamespaceList; - }> { - return new Promise<{ response: http.IncomingMessage; body: V1NamespaceList }>((resolve) => { - resolve({ response: {} as http.IncomingMessage, body: listObj }); + const listFn: ListPromise = function (): Promise { + return new Promise((resolve) => { + resolve(listObj); }); }; const watchCalled = new Promise((resolve) => { @@ -945,15 +890,10 @@ describe('ListWatchCache', () => { items: list, } as V1NamespaceList; - const listFn: ListPromise = function (): Promise<{ - response: http.IncomingMessage; - body: V1NamespaceList; - }> { - return new Promise<{ response: http.IncomingMessage; body: V1NamespaceList }>( - (resolve, reject) => { - resolve({ response: {} as http.IncomingMessage, body: listObj }); - }, - ); + const listFn: ListPromise = function (): Promise { + return new Promise((resolve, reject) => { + resolve(listObj); + }); }; const promise = new Promise((resolve) => { mock.when( @@ -1008,15 +948,10 @@ describe('ListWatchCache', () => { items: list, } as V1NamespaceList; - const listFn: ListPromise = function (): Promise<{ - response: http.IncomingMessage; - body: V1NamespaceList; - }> { - return new Promise<{ response: http.IncomingMessage; body: V1NamespaceList }>( - (resolve, reject) => { - resolve({ response: {} as http.IncomingMessage, body: listObj }); - }, - ); + const listFn: ListPromise = function (): Promise { + return new Promise((resolve, reject) => { + resolve(listObj); + }); }; const promise = new Promise((resolve) => { mock.when( @@ -1054,13 +989,10 @@ describe('ListWatchCache', () => { items: list, } as V1NamespaceList; - const listFn: ListPromise = function (): Promise<{ - response: http.IncomingMessage; - body: V1NamespaceList; - }> { - return new Promise<{ response: http.IncomingMessage; body: V1NamespaceList }>((resolve) => { + const listFn: ListPromise = function (): Promise { + return new Promise((resolve) => { listCalls++; - resolve({ response: {} as http.IncomingMessage, body: listObj }); + resolve(listObj); }); }; let promise = new Promise((resolve) => { @@ -1110,13 +1042,10 @@ describe('ListWatchCache', () => { } as V1NamespaceList; let listCalls = 0; - const listFn: ListPromise = function (): Promise<{ - response: http.IncomingMessage; - body: V1NamespaceList; - }> { - return new Promise<{ response: http.IncomingMessage; body: V1NamespaceList }>((resolve) => { + const listFn: ListPromise = function (): Promise { + return new Promise((resolve) => { listCalls++; - resolve({ response: {} as http.IncomingMessage, body: listObj }); + resolve(listObj); }); }; let promise = new Promise((resolve) => { @@ -1200,15 +1129,10 @@ describe('ListWatchCache', () => { items: list, } as V1NamespaceList; - const listFn: ListPromise = function (): Promise<{ - response: http.IncomingMessage; - body: V1NamespaceList; - }> { - return new Promise<{ response: http.IncomingMessage; body: V1NamespaceList }>( - (resolve, reject) => { - resolve({ response: {} as http.IncomingMessage, body: listObj }); - }, - ); + const listFn: ListPromise = function (): Promise { + return new Promise((resolve, reject) => { + resolve(listObj); + }); }; const kc = new KubeConfig(); @@ -1320,15 +1244,10 @@ describe('delete items', () => { items: [], } as V1NamespaceList; - const listFn: ListPromise = function (): Promise<{ - response: http.IncomingMessage; - body: V1NamespaceList; - }> { - return new Promise<{ response: http.IncomingMessage; body: V1NamespaceList }>( - (resolve, reject) => { - resolve({ response: {} as http.IncomingMessage, body: listObj }); - }, - ); + const listFn: ListPromise = function (): Promise { + return new Promise((resolve, reject) => { + resolve(listObj); + }); }; const informer = new ListWatch('/some/path', mock.instance(fakeWatch), listFn, false); const connectPromise = new Promise((resolve: (boolean) => void) => { @@ -1364,15 +1283,10 @@ describe('delete items', () => { items: list, } as V1NamespaceList; - const listFn: ListPromise = function (): Promise<{ - response: http.IncomingMessage; - body: V1NamespaceList; - }> { - return new Promise<{ response: http.IncomingMessage; body: V1NamespaceList }>( - (resolve, reject) => { - resolve({ response: {} as http.IncomingMessage, body: listObj }); - }, - ); + const listFn: ListPromise = function (): Promise { + return new Promise((resolve, reject) => { + resolve(listObj); + }); }; const promise = new Promise((resolve) => { mock.when( diff --git a/src/informer.ts b/src/informer.ts index bde49b829e3..c652dabc854 100644 --- a/src/informer.ts +++ b/src/informer.ts @@ -3,15 +3,10 @@ import { KubeConfig } from './config'; import { KubernetesListObject, KubernetesObject } from './types'; import { Watch } from './watch'; -import http = require('http'); - export type ObjectCallback = (obj: T) => void; export type ErrorCallback = (err?: any) => void; export type ListCallback = (list: T[], ResourceVersion: string) => void; -export type ListPromise = () => Promise<{ - response: http.IncomingMessage; - body: KubernetesListObject; -}>; +export type ListPromise = () => Promise>; // These are issued per object export const ADD: string = 'add';