From 7c34379e02e9f8fc58c1bf6609162fefa2f5e44a Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Fri, 22 Sep 2023 10:39:06 +0300 Subject: [PATCH] chore: add pino logger; review logs; Signed-off-by: Oleksii Kurinnyi --- .deps/dev.md | 4 +- .deps/prod.md | 14 +- packages/dashboard-backend/jest.config.js | 3 + packages/dashboard-backend/jest.setup.js | 13 ++ packages/dashboard-backend/package.json | 2 + .../src/__tests__/app.spec.ts | 5 +- packages/dashboard-backend/src/app.ts | 60 ++++----- .../services/__tests__/eventApi.spec.ts | 16 ++- .../services/__tests__/kubeConfigApi.spec.ts | 7 +- .../services/__tests__/podApi.spec.ts | 6 +- .../__tests__/serverConfigApi.spec.ts | 6 +- .../services/devWorkspaceApi.ts | 3 +- .../devworkspaceClient/services/eventApi.ts | 3 +- .../services/gettingStartedSamplesApi.ts | 21 +-- .../__mocks__/prepareCustomObjectWatch.ts | 3 + .../helpers/prepareCustomObjectAPI.ts | 33 ++--- .../services/helpers/retryableExec.ts | 16 ++- .../services/kubeConfigApi.ts | 19 +-- .../logsApi/__tests__/logsApi.retry1.spec.ts | 3 - .../logsApi/__tests__/logsApi.retry2.spec.ts | 3 - .../logsApi/__tests__/logsApi.spec.ts | 4 - .../services/logsApi/index.ts | 6 +- .../src/devworkspaceClient/services/podApi.ts | 3 +- .../devworkspaceClient/services/podmanApi.ts | 3 +- .../services/serverConfigApi.ts | 50 +++---- .../services/userProfileApi.ts | 1 - .../src/devworkspaceClient/types/index.ts | 127 ++++++++++++++---- .../src/helpers/__tests__/getUserName.spec.ts | 4 +- .../src/helpers/getUserName.ts | 4 +- .../dashboard-backend/src/localRun/index.ts | 3 +- .../src/localRun/proxies/cheServerApi.ts | 5 +- .../src/plugins/staticServer.ts | 4 +- .../dashboard-backend/src/plugins/swagger.ts | 4 +- .../factoryAcceptanceRedirect.spec.ts | 2 +- .../api/__tests__/clusterConfig.spec.ts | 2 +- .../routes/api/__tests__/clusterInfo.spec.ts | 2 +- .../__tests__/devworkspaceTemplates.spec.ts | 2 +- .../api/__tests__/devworkspaces.spec.ts | 2 +- .../routes/api/__tests__/dockerConfig.spec.ts | 2 +- .../src/routes/api/__tests__/events.spec.ts | 2 +- .../routes/api/__tests__/gitConfig.spec.ts | 2 +- .../routes/api/__tests__/kubeConfig.spec.ts | 2 +- .../api/__tests__/personalAccessToken.spec.ts | 2 +- .../src/routes/api/__tests__/pods.spec.ts | 2 +- .../routes/api/__tests__/serverConfig.spec.ts | 5 +- .../routes/api/__tests__/userProfile.spec.ts | 2 +- .../routes/api/__tests__/yamlResolver.spec.ts | 2 +- .../api/helpers/getServiceAccountToken.ts | 3 +- .../src/routes/api/websocket.ts | 11 +- packages/dashboard-backend/src/server.ts | 50 ++++--- .../__mocks__/kubeConfigProvider.ts | 7 +- .../logWatcher/__mock__/index.ts} | 27 ++-- .../logWatcher/__tests__/index.spec.ts | 95 +++++++++++++ .../src/services/logWatcher/index.ts | 77 +++++++++++ .../src/utils/__mocks__/logger.ts | 21 +++ .../{helpers/tests => utils}/appBuilder.ts | 0 .../dashboard-backend/src/utils/logger.ts | 50 +++++++ .../webpack.config.common.js | 1 - yarn.lock | 81 ++++++++++- 59 files changed, 671 insertions(+), 241 deletions(-) create mode 100644 packages/dashboard-backend/jest.setup.js rename packages/dashboard-backend/src/{devworkspaceClient/services/helpers/__mocks__/prepareCoreV1API.ts => services/logWatcher/__mock__/index.ts} (53%) create mode 100644 packages/dashboard-backend/src/services/logWatcher/__tests__/index.spec.ts create mode 100644 packages/dashboard-backend/src/services/logWatcher/index.ts create mode 100644 packages/dashboard-backend/src/utils/__mocks__/logger.ts rename packages/dashboard-backend/src/{helpers/tests => utils}/appBuilder.ts (100%) create mode 100644 packages/dashboard-backend/src/utils/logger.ts diff --git a/.deps/dev.md b/.deps/dev.md index b6c18637c..68774a929 100644 --- a/.deps/dev.md +++ b/.deps/dev.md @@ -171,7 +171,7 @@ | [`@types/lodash@4.14.197`](https://github.com/DefinitelyTyped/DefinitelyTyped.git) | MIT | #4131 | | [`@types/minimatch@3.0.5`](https://github.com/DefinitelyTyped/DefinitelyTyped.git) | MIT | clearlydefined | | [`@types/minimist@1.2.2`](https://github.com/DefinitelyTyped/DefinitelyTyped.git) | MIT | #10839 | -| [`@types/node-fetch@2.6.4`](https://github.com/DefinitelyTyped/DefinitelyTyped.git) | MIT | clearlydefined | +| [`@types/node-fetch@2.6.4`](https://github.com/DefinitelyTyped/DefinitelyTyped.git) | MIT | #11004 | | [`@types/normalize-package-data@2.4.1`](https://github.com/DefinitelyTyped/DefinitelyTyped.git) | MIT | #10792 | | [`@types/parse-json@4.0.0`](https://www.github.com/DefinitelyTyped/DefinitelyTyped.git) | MIT | clearlydefined | | [`@types/qs@6.9.7`](https://github.com/DefinitelyTyped/DefinitelyTyped.git) | MIT | clearlydefined | @@ -308,7 +308,6 @@ | [`color-name@1.1.4`](git@github.com:colorjs/color-name.git) | MIT | clearlydefined | | [`color-support@1.1.3`](git+https://github.com/isaacs/color-support.git) | ISC | clearlydefined | | [`colord@2.9.3`](https://github.com/omgovich/colord.git) | MIT | clearlydefined | -| [`colorette@2.0.20`](https://github.com/jorgebucaran/colorette.git) | MIT | clearlydefined | | [`columnify@1.6.0`](git://github.com/timoxley/columnify.git) | MIT | clearlydefined | | [`commander@7.2.0`](https://github.com/tj/commander.js.git) | MIT | clearlydefined | | [`common-ancestor-path@1.0.1`](git+https://github.com/isaacs/common-ancestor-path) | ISC | clearlydefined | @@ -859,7 +858,6 @@ | [`strip-bom@3.0.0`](https://github.com/sindresorhus/strip-bom.git) | MIT | clearlydefined | | [`strip-final-newline@2.0.0`](https://github.com/sindresorhus/strip-final-newline.git) | MIT | clearlydefined | | [`strip-indent@3.0.0`](https://github.com/sindresorhus/strip-indent.git) | MIT | clearlydefined | -| [`strip-json-comments@3.1.1`](https://github.com/sindresorhus/strip-json-comments.git) | MIT | clearlydefined | | [`strong-log-transformer@2.1.0`](git://github.com/strongloop/strong-log-transformer) | Apache-2.0 | #1138 | | [`style-loader@3.3.3`](https://github.com/webpack-contrib/style-loader.git) | MIT | clearlydefined | | [`style-search@0.1.0`](git+https://github.com/davidtheclark/style-search.git) | ISC | clearlydefined | diff --git a/.deps/prod.md b/.deps/prod.md index 15a0ef6ec..ecad46658 100644 --- a/.deps/prod.md +++ b/.deps/prod.md @@ -46,7 +46,7 @@ | [`@types/js-yaml@4.0.5`](https://github.com/DefinitelyTyped/DefinitelyTyped.git) | MIT | clearlydefined | | [`@types/node@20.5.0`](https://github.com/DefinitelyTyped/DefinitelyTyped.git) | MIT | clearlydefined | | [`@types/prop-types@15.7.5`](https://github.com/DefinitelyTyped/DefinitelyTyped.git) | MIT | clearlydefined | -| [`@types/react-redux@7.1.25`](https://github.com/DefinitelyTyped/DefinitelyTyped.git) | MIT | clearlydefined | +| [`@types/react-redux@7.1.25`](https://github.com/DefinitelyTyped/DefinitelyTyped.git) | MIT | #10970 | | [`@types/react@18.2.20`](https://github.com/DefinitelyTyped/DefinitelyTyped.git) | MIT | #8234 | | [`@types/request@2.48.8`](https://github.com/DefinitelyTyped/DefinitelyTyped.git) | MIT | clearlydefined | | [`@types/scheduler@0.16.3`](https://github.com/DefinitelyTyped/DefinitelyTyped.git) | MIT | #7582 | @@ -109,6 +109,7 @@ | [`codemirror@5.65.15`](https://github.com/codemirror/CodeMirror.git) | MIT | (clearlydefined)[https://clearlydefined.io/definitions/npm/npmjs/-/codemirror/5.65.15] | | [`color-convert@1.9.3`](https://github.com/Qix-/color-convert.git) | MIT | clearlydefined | | [`color-name@1.1.3`](git@github.com:dfcreative/color-name.git) | MIT | clearlydefined | +| [`colorette@2.0.20`](https://github.com/jorgebucaran/colorette.git) | MIT | clearlydefined | | [`combined-stream@1.0.8`](git://github.com/felixge/node-combined-stream.git) | MIT | clearlydefined | | [`concat-map@0.0.1`](git://github.com/substack/node-concat-map.git) | MIT | clearlydefined | | [`connected-react-router@6.9.3`](https://github.com/supasate/connected-react-router.git) | MIT | clearlydefined | @@ -125,6 +126,7 @@ | [`csstype@3.1.2`](https://github.com/frenic/csstype) | MIT | clearlydefined | | [`dashdash@1.14.1`](git://github.com/trentm/node-dashdash.git) | MIT | clearlydefined | | [`date-fns@2.30.0`](https://github.com/date-fns/date-fns) | MIT | clearlydefined | +| [`dateformat@4.6.3`](https://github.com/felixge/node-dateformat.git) | MIT | clearlydefined | | [`debug@4.3.4`](git://github.com/debug-js/debug.git) | MIT | clearlydefined | | [`deepmerge@4.3.1`](git://github.com/TehShrike/deepmerge.git) | MIT | #7032 | | [`delayed-stream@1.0.0`](git://github.com/felixge/node-delayed-stream.git) | MIT | clearlydefined | @@ -150,12 +152,14 @@ | [`extend@3.0.2`](https://github.com/justmoon/node-extend.git) | MIT | clearlydefined | | [`extsprintf@1.3.0`](git://github.com/davepacheco/node-extsprintf.git) | MIT | #1813 | | [`fast-content-type-parse@1.0.0`](https://github.com/fastify/fast-content-type-parse.git) | MIT | clearlydefined | +| [`fast-copy@3.0.1`](git+https://github.com/planttheidea/fast-copy.git) | MIT | clearlydefined | | [`fast-decode-uri-component@1.0.1`](git+https://github.com/delvedor/fast-decode-uri-component.git) | MIT | clearlydefined | | [`fast-deep-equal@3.1.3`](git+https://github.com/epoberezkin/fast-deep-equal.git) | MIT | clearlydefined | | [`fast-json-stable-stringify@2.1.0`](git://github.com/epoberezkin/fast-json-stable-stringify.git) | MIT | clearlydefined | | [`fast-json-stringify@5.8.0`](git+https://github.com/fastify/fast-json-stringify.git) | MIT | clearlydefined | | [`fast-querystring@1.1.2`](git+https://github.com/anonrig/fast-querystring.git) | MIT | clearlydefined | | [`fast-redact@3.3.0`](git+https://github.com/davidmarkclements/fast-redact.git) | MIT | clearlydefined | +| [`fast-safe-stringify@2.1.1`](git+https://github.com/davidmarkclements/fast-safe-stringify.git) | MIT | clearlydefined | | [`fast-uri@2.2.0`](https://github.com/fastify/fast-uri) | MIT | (clearlydefined)[https://clearlydefined.io/definitions/npm/npmjs/-/fast-uri/2.2.0] | | [`fastify-plugin@4.5.1`](git+https://github.com/fastify/fastify-plugin.git) | MIT | clearlydefined | | [`fastify@4.21.0`](git+https://github.com/fastify/fastify.git) | MIT | (clearlydefined)[https://clearlydefined.io/definitions/npm/npmjs/-/fastify/4.21.0] | @@ -186,6 +190,7 @@ | [`has@1.0.3`](git://github.com/tarruda/has.git) | MIT | #10930 | | [`hash-base@3.1.0`](https://github.com/crypto-browserify/hash-base.git) | MIT | clearlydefined | | [`hash.js@1.1.7`](git@github.com:indutny/hash.js) | MIT | #1044 | +| [`help-me@4.2.0`](https://github.com/mcollina/help-me.git) | MIT | clearlydefined | | [`history@4.10.1`](https://github.com/ReactTraining/history.git) | MIT | clearlydefined | | [`hmac-drbg@1.0.1`](git+ssh://git@github.com/indutny/hmac-drbg.git) | MIT | clearlydefined | | [`hoist-non-react-statics@3.3.2`](git://github.com/mridgway/hoist-non-react-statics.git) | BSD-3-Clause | clearlydefined | @@ -211,6 +216,7 @@ | [`isstream@0.1.2`](https://github.com/rvagg/isstream.git) | MIT | clearlydefined | | [`joi@17.9.2`](git://github.com/hapijs/joi) | BSD-3-Clause | #7488 | | [`jose@4.14.4`](https://github.com/panva/jose.git) | MIT | clearlydefined | +| [`joycon@3.1.1`](https://github.com/egoist/joycon.git) | MIT | clearlydefined | | [`js-tokens@4.0.0`](https://github.com/lydell/js-tokens.git) | MIT | #2401 | | [`js-yaml@4.1.0`](https://github.com/nodeca/js-yaml.git) | MIT | clearlydefined | | [`jsbn@0.1.1`](https://github.com/andyperlitch/jsbn.git) | MIT | clearlydefined | @@ -269,9 +275,10 @@ | [`pbkdf2@3.1.2`](https://github.com/crypto-browserify/pbkdf2.git) | MIT | clearlydefined | | [`performance-now@2.1.0`](git://github.com/braveg1rl/performance-now.git) | MIT | clearlydefined | | [`picocolors@1.0.0`](https://github.com/alexeyraspopov/picocolors.git) | ISC | clearlydefined | -| [`pino-abstract-transport@1.0.0`](git+https://github.com/pinojs/pino-abstract-transport.git) | MIT | clearlydefined | +| [`pino-abstract-transport@1.1.0`](git+https://github.com/pinojs/pino-abstract-transport.git) | MIT | clearlydefined | +| [`pino-pretty@10.2.0`](git+ssh://git@github.com/pinojs/pino-pretty.git) | MIT | clearlydefined | | [`pino-std-serializers@6.2.2`](git+ssh://git@github.com/pinojs/pino-std-serializers.git) | MIT | clearlydefined | -| [`pino@8.15.0`](git+https://github.com/pinojs/pino.git) | MIT | clearlydefined | +| [`pino@8.15.1`](git+https://github.com/pinojs/pino.git) | MIT | clearlydefined | | [`popper.js@1.16.1`](git+https://github.com/FezVrasta/popper.js.git) | MIT | [CQ22353](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=22353) | | [`postcss@8.4.28`](https://github.com/postcss/postcss.git) | MIT | #3545 | | [`private@0.1.8`](git://github.com/benjamn/private.git) | MIT | clearlydefined | @@ -348,6 +355,7 @@ | [`streamsearch@1.1.0`](http://github.com/mscdex/streamsearch.git) | MIT | clearlydefined | | [`string_decoder@1.3.0`](git://github.com/nodejs/string_decoder.git) | MIT | clearlydefined | | [`strip-ansi@3.0.1`](https://github.com/chalk/strip-ansi.git) | MIT | clearlydefined | +| [`strip-json-comments@3.1.1`](https://github.com/sindresorhus/strip-json-comments.git) | MIT | clearlydefined | | [`supports-color@5.5.0`](https://github.com/chalk/supports-color.git) | MIT | clearlydefined | | [`tabbable@5.3.3`](git+https://github.com/focus-trap/tabbable.git) | MIT | clearlydefined | | [`tar@6.1.15`](https://github.com/isaacs/node-tar.git) | ISC | #4566 | diff --git a/packages/dashboard-backend/jest.config.js b/packages/dashboard-backend/jest.config.js index 53ab24067..0469ae0e2 100644 --- a/packages/dashboard-backend/jest.config.js +++ b/packages/dashboard-backend/jest.config.js @@ -26,8 +26,11 @@ module.exports = { ...base.collectCoverageFrom, '!src/localRun/**', + '!src/utils/**', + '!src/server.ts', ], testEnvironment: 'node', + setupFilesAfterEnv: ['./jest.setup.js'], coverageThreshold: { global: { statements: 81, diff --git a/packages/dashboard-backend/jest.setup.js b/packages/dashboard-backend/jest.setup.js new file mode 100644 index 000000000..f081043b3 --- /dev/null +++ b/packages/dashboard-backend/jest.setup.js @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2018-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ + +jest.mock('./src/utils/logger'); diff --git a/packages/dashboard-backend/package.json b/packages/dashboard-backend/package.json index 5dc8a772b..eb953aad0 100644 --- a/packages/dashboard-backend/package.json +++ b/packages/dashboard-backend/package.json @@ -45,6 +45,8 @@ "js-yaml": "^4.0.0", "multi-ini": "^2.3.2", "node-fetch": "^2.6.7", + "pino": "^8.15.1", + "pino-pretty": "^10.2.0", "querystring": "^0.2.1", "reflect-metadata": "^0.1.13", "request": "^2.88.2", diff --git a/packages/dashboard-backend/src/__tests__/app.spec.ts b/packages/dashboard-backend/src/__tests__/app.spec.ts index 384f6d3ac..43a20c429 100644 --- a/packages/dashboard-backend/src/__tests__/app.spec.ts +++ b/packages/dashboard-backend/src/__tests__/app.spec.ts @@ -12,16 +12,13 @@ import process from 'process'; -import { setup, teardown } from '@/helpers/tests/appBuilder'; +import { setup, teardown } from '@/utils/appBuilder'; const mockProcessExit = jest.fn(); (process as any).exit = mockProcessExit.mockImplementation(code => { throw new Error('exit code ' + code); }); -// mute the output -console.error = jest.fn(); - describe('App', () => { afterEach(() => { jest.clearAllMocks(); diff --git a/packages/dashboard-backend/src/app.ts b/packages/dashboard-backend/src/app.ts index 4fcc814f3..724ff95d5 100644 --- a/packages/dashboard-backend/src/app.ts +++ b/packages/dashboard-backend/src/app.ts @@ -41,10 +41,10 @@ import { registerYamlResolverRoute } from '@/routes/api/yamlResolver'; import { registerFactoryAcceptanceRedirect } from '@/routes/factoryAcceptanceRedirect'; import { registerWorkspaceRedirect } from '@/routes/workspaceRedirect'; -export default async function buildApp(server: FastifyInstance): Promise { +export default async function buildApp(server: FastifyInstance): Promise { const cheHost = process.env.CHE_HOST as string; if (!cheHost) { - console.error('CHE_HOST environment variable is required'); + server.log.fatal('CHE_HOST environment variable is required'); process.exit(1); } @@ -59,60 +59,60 @@ export default async function buildApp(server: FastifyInstance): Promise { done(null, json); } catch (e) { const error = new Error(helpers.errors.getMessage(e)); - console.warn(`[WARN] Can't parse the JSON payload:`, body); + server.log.warn(`Can't parse the JSON payload: %s`, body); done(error, undefined); } }, ); - registerWebSocket(server); + return Promise.allSettled([ + registerWebSocket(server), - if (isLocalRun()) { - registerLocalRun(server); - } + isLocalRun() ? registerLocalRun(server) : Promise.resolve(), - registerCors(isLocalRun(), server); + registerCors(isLocalRun(), server), - registerStaticServer(publicFolder, server); + registerStaticServer(publicFolder, server), - registerFactoryAcceptanceRedirect(server); + registerFactoryAcceptanceRedirect(server), - registerWorkspaceRedirect(server); + registerWorkspaceRedirect(server), - registerWebsocket(server); + registerWebsocket(server), - // swagger and API - registerSwagger(server); + // swagger and API + registerSwagger(server), - registerClusterConfigRoute(server); + registerClusterConfigRoute(server), - registerClusterInfoRoute(server); + registerClusterInfoRoute(server), - registerDevWorkspaceTemplates(server); + registerDevWorkspaceTemplates(server), - registerDevworkspacesRoutes(server); + registerDevworkspacesRoutes(server), - registerDockerConfigRoutes(server); + registerDockerConfigRoutes(server), - registerEventsRoutes(server); + registerEventsRoutes(server), - registerPodsRoutes(server); + registerPodsRoutes(server), - registerKubeConfigRoute(server); + registerKubeConfigRoute(server), - registerPodmanLoginRoute(server); + registerPodmanLoginRoute(server), - registerServerConfigRoute(server); + registerServerConfigRoute(server), - registerUserProfileRoute(server); + registerUserProfileRoute(server), - registerYamlResolverRoute(server); + registerYamlResolverRoute(server), - registerDevworkspaceResourcesRoute(server); + registerDevworkspaceResourcesRoute(server), - registerPersonalAccessTokenRoutes(server); + registerPersonalAccessTokenRoutes(server), - registerGitConfigRoutes(server); + registerGitConfigRoutes(server), - registerGettingStartedSamplesRoutes(isLocalRun(), server); + registerGettingStartedSamplesRoutes(isLocalRun(), server), + ]); } diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/__tests__/eventApi.spec.ts b/packages/dashboard-backend/src/devworkspaceClient/services/__tests__/eventApi.spec.ts index 3dcd5dcc7..90f0842e9 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/__tests__/eventApi.spec.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/__tests__/eventApi.spec.ts @@ -17,14 +17,13 @@ import * as mockClient from '@kubernetes/client-node'; import { CoreV1Api, CoreV1Event, V1Status } from '@kubernetes/client-node'; import { EventApiService } from '@/devworkspaceClient/services/eventApi'; +import { logger } from '@/utils/logger'; jest.mock('../helpers/prepareCustomObjectWatch'); +jest.mock('../helpers/retryableExec'); const namespace = 'user-che'; -// mute console.error -console.error = jest.fn(); - describe('Event API Service', () => { let eventApiService: EventApiService; @@ -45,6 +44,7 @@ describe('Event API Service', () => { afterEach(() => { jest.clearAllMocks(); + spyListNamespacedEvent.mockClear(); }); it('should list events', async () => { @@ -57,9 +57,10 @@ describe('Event API Service', () => { const failureReason = 'failed to get events list'; spyListNamespacedEvent.mockRejectedValue(new Error(failureReason)); - const list = async () => await eventApiService.listInNamespace(namespace); + expect.assertions(1); - expect(list).rejects.toThrowError(failureReason); + const list = async () => await eventApiService.listInNamespace(namespace); + await expect(list()).rejects.toThrow(failureReason); }); it('should watch events', async () => { @@ -174,7 +175,10 @@ describe('Event API Service', () => { const error = new Error('watch error'); (eventApiService as any).handleWatchError(error, path); - expect(console.error).toHaveBeenCalledWith(`[ERROR] Stopped watching ${path}. Reason:`, error); + expect(logger.warn).toHaveBeenCalledWith( + error, + 'Stopped watching /api/v1/namespaces/user-che/events.', + ); }); }); diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/__tests__/kubeConfigApi.spec.ts b/packages/dashboard-backend/src/devworkspaceClient/services/__tests__/kubeConfigApi.spec.ts index 85cf644d9..0a4436ef5 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/__tests__/kubeConfigApi.spec.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/__tests__/kubeConfigApi.spec.ts @@ -18,6 +18,9 @@ import { CoreV1Api, V1PodList } from '@kubernetes/client-node'; import * as helper from '@/devworkspaceClient/services/helpers/exec'; import { KubeConfigApiService } from '@/devworkspaceClient/services/kubeConfigApi'; +// mute the output +console.error = jest.fn(); + const homeUserDir = '/home/user'; const kubeConfigDir = `${homeUserDir}/.kube`; const mockExecPrintenvHome = jest.fn().mockReturnValue({ @@ -79,10 +82,6 @@ describe('Kubernetes Config API Service', () => { }); test('injecting kubeconfig', async () => { - // mute output - console.error = jest.fn(); - console.warn = jest.fn(); - await kubeConfigService.injectKubeConfig(namespace, 'wksp-id'); expect(spyExec).toHaveBeenCalledTimes(4); diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/__tests__/podApi.spec.ts b/packages/dashboard-backend/src/devworkspaceClient/services/__tests__/podApi.spec.ts index 923049b89..030e8e616 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/__tests__/podApi.spec.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/__tests__/podApi.spec.ts @@ -17,14 +17,12 @@ import * as mockClient from '@kubernetes/client-node'; import { CoreV1Api, V1Pod, V1Status } from '@kubernetes/client-node'; import { PodApiService } from '@/devworkspaceClient/services/podApi'; +import { logger } from '@/utils/logger'; jest.mock('../helpers/prepareCustomObjectWatch'); const namespace = 'user-che'; -// mute console.error -console.error = jest.fn(); - describe('Pod API Service', () => { let podApiService: PodApiService; @@ -177,7 +175,7 @@ describe('Pod API Service', () => { const error = new Error('watch error'); (podApiService as any).handleWatchError(error, path); - expect(console.error).toHaveBeenCalledWith(`[ERROR] Stopped watching ${path}. Reason:`, error); + expect(logger.error).toHaveBeenCalledWith(error, `Stopped watching ${path}.`); }); }); diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/__tests__/serverConfigApi.spec.ts b/packages/dashboard-backend/src/devworkspaceClient/services/__tests__/serverConfigApi.spec.ts index 37aecbd4c..ad08bafad 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/__tests__/serverConfigApi.spec.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/__tests__/serverConfigApi.spec.ts @@ -15,7 +15,7 @@ import * as mockClient from '@kubernetes/client-node'; import { CustomObjectsApi } from '@kubernetes/client-node'; -import { CustomResourceDefinition, CustomResourceDefinitionList } from '@/devworkspaceClient'; +import { CheClusterCustomResource, CustomResourceDefinitionList } from '@/devworkspaceClient'; import { ServerConfigApiService } from '@/devworkspaceClient/services/serverConfigApi'; jest.mock('../../../helpers/getUserName.ts'); @@ -149,7 +149,7 @@ function buildCustomResourceList(): { body: CustomResourceDefinitionList } { }; } -function buildCustomResource(): CustomResourceDefinition { +function buildCustomResource(): CheClusterCustomResource { return { apiVersion: 'org.eclipse.che/v2', kind: 'CheCluster', @@ -198,5 +198,5 @@ function buildCustomResource(): CustomResourceDefinition { devfileRegistryURL: 'http://devfile-registry.eclipse-che.svc/devfile-registry', pluginRegistryURL: 'http://plugin-registry.eclipse-che.svc/v3', }, - } as CustomResourceDefinition; + } as CheClusterCustomResource; } diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/devWorkspaceApi.ts b/packages/dashboard-backend/src/devworkspaceClient/services/devWorkspaceApi.ts index c9b877673..373265e4f 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/devWorkspaceApi.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/devWorkspaceApi.ts @@ -29,6 +29,7 @@ import { import { prepareCustomObjectWatch } from '@/devworkspaceClient/services/helpers/prepareCustomObjectWatch'; import { IDevWorkspaceApi } from '@/devworkspaceClient/types'; import { MessageListener } from '@/services/types/Observer'; +import { logger } from '@/utils/logger'; const DEV_WORKSPACE_API_ERROR_LABEL = 'CUSTOM_OBJECTS_API_ERROR'; @@ -193,7 +194,7 @@ export class DevWorkspaceApiService implements IDevWorkspaceApi { } }, (error: unknown) => { - console.error(`[ERROR] Stopped watching ${path}. Reason:`, error); + logger.warn(error, `Stopped watching ${path}.`); }, ); diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/eventApi.ts b/packages/dashboard-backend/src/devworkspaceClient/services/eventApi.ts index 68c7072aa..33618ee44 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/eventApi.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/eventApi.ts @@ -23,6 +23,7 @@ import { import { prepareCustomObjectWatch } from '@/devworkspaceClient/services/helpers/prepareCustomObjectWatch'; import { IEventApi } from '@/devworkspaceClient/types'; import { MessageListener } from '@/services/types/Observer'; +import { logger } from '@/utils/logger'; const EVENT_API_ERROR_LABEL = 'CUSTOM_OBJECTS_API_ERROR'; @@ -88,7 +89,7 @@ export class EventApiService implements IEventApi { } private handleWatchError(error: unknown, path: string): void { - console.error(`[ERROR] Stopped watching ${path}. Reason:`, error); + logger.warn(error, `Stopped watching ${path}.`); } /** diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/gettingStartedSamplesApi.ts b/packages/dashboard-backend/src/devworkspaceClient/services/gettingStartedSamplesApi.ts index 07fad5bce..83f4eafcc 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/gettingStartedSamplesApi.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/gettingStartedSamplesApi.ts @@ -22,6 +22,7 @@ import { prepareCoreV1API, } from '@/devworkspaceClient/services/helpers/prepareCoreV1API'; import { IGettingStartedSampleApi } from '@/devworkspaceClient/types'; +import { logger } from '@/utils/logger'; const API_ERROR_LABEL = 'CORE_V1_API_ERROR'; const DEVFILE_METADATA_LABEL_SELECTOR = @@ -41,7 +42,7 @@ export class GettingStartedSamplesApiService implements IGettingStartedSampleApi async list(): Promise> { if (!this.env.NAMESPACE) { - console.warn('Mandatory environment variables are not defined: $CHECLUSTER_CR_NAMESPACE'); + logger.warn('Mandatory environment variables are not defined: $CHECLUSTER_CR_NAMESPACE'); return []; } @@ -63,14 +64,16 @@ export class GettingStartedSamplesApiService implements IGettingStartedSampleApi const samples: api.IGettingStartedSample[] = []; for (const cm of response.body.items) { - if (cm.data) { - for (const key in cm.data) { - try { - const sample = JSON.parse(cm.data[key]); - Array.isArray(sample) ? samples.push(...sample) : samples.push(sample); - } catch (error) { - console.error(`Failed to parse getting started samples: ${error}`); - } + if (cm.data === undefined) { + continue; + } + + for (const key in cm.data) { + try { + const sample = JSON.parse(cm.data[key]); + Array.isArray(sample) ? samples.push(...sample) : samples.push(sample); + } catch (error) { + logger.error(error, 'Failed to parse getting started sample: %s', cm.data[key]); } } } diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/helpers/__mocks__/prepareCustomObjectWatch.ts b/packages/dashboard-backend/src/devworkspaceClient/services/helpers/__mocks__/prepareCustomObjectWatch.ts index 477505482..95c79d8bc 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/helpers/__mocks__/prepareCustomObjectWatch.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/helpers/__mocks__/prepareCustomObjectWatch.ts @@ -22,6 +22,9 @@ export function prepareCustomObjectWatch(_kc: k8s.KubeConfig): k8s.Watch { destroy: () => { /* no-op */ }, + on: (_event: string, _callback: (data: unknown) => void) => { + /* no-op */ + }, } as request.Request; }; return { diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/helpers/prepareCustomObjectAPI.ts b/packages/dashboard-backend/src/devworkspaceClient/services/helpers/prepareCustomObjectAPI.ts index 58b2a6248..a3fcfdd15 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/helpers/prepareCustomObjectAPI.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/helpers/prepareCustomObjectAPI.ts @@ -16,6 +16,7 @@ import { retryableExec } from '@/devworkspaceClient/services/helpers/retryableEx export type CustomObjectAPI = Pick< k8s.CustomObjectsApi, + | 'getClusterCustomObject' | 'listClusterCustomObject' | 'listNamespacedCustomObject' | 'getNamespacedCustomObject' @@ -26,28 +27,30 @@ export type CustomObjectAPI = Pick< >; export function prepareCustomObjectAPI(kc: k8s.KubeConfig): CustomObjectAPI { - const customObjectAPI = kc.makeApiClient(k8s.CustomObjectsApi); + const customObjectsApi = kc.makeApiClient(k8s.CustomObjectsApi); return { + getClusterCustomObject: (...args: Parameters) => + retryableExec(() => customObjectsApi.getClusterCustomObject(...args)), listClusterCustomObject: ( - ...args: Parameters - ) => retryableExec(() => customObjectAPI.listClusterCustomObject(...args)), + ...args: Parameters + ) => retryableExec(() => customObjectsApi.listClusterCustomObject(...args)), listNamespacedCustomObject: ( - ...args: Parameters - ) => retryableExec(() => customObjectAPI.listNamespacedCustomObject(...args), 10), + ...args: Parameters + ) => retryableExec(() => customObjectsApi.listNamespacedCustomObject(...args)), getNamespacedCustomObject: ( - ...args: Parameters - ) => retryableExec(() => customObjectAPI.getNamespacedCustomObject(...args)), + ...args: Parameters + ) => retryableExec(() => customObjectsApi.getNamespacedCustomObject(...args)), createNamespacedCustomObject: ( - ...args: Parameters - ) => retryableExec(() => customObjectAPI.createNamespacedCustomObject(...args)), + ...args: Parameters + ) => retryableExec(() => customObjectsApi.createNamespacedCustomObject(...args)), replaceNamespacedCustomObject: ( - ...args: Parameters - ) => retryableExec(() => customObjectAPI.replaceNamespacedCustomObject(...args)), + ...args: Parameters + ) => retryableExec(() => customObjectsApi.replaceNamespacedCustomObject(...args)), deleteNamespacedCustomObject: ( - ...args: Parameters - ) => retryableExec(() => customObjectAPI.deleteNamespacedCustomObject(...args), 10), + ...args: Parameters + ) => retryableExec(() => customObjectsApi.deleteNamespacedCustomObject(...args)), patchNamespacedCustomObject: ( - ...args: Parameters - ) => retryableExec(() => customObjectAPI.patchNamespacedCustomObject(...args)), + ...args: Parameters + ) => retryableExec(() => customObjectsApi.patchNamespacedCustomObject(...args)), }; } diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/helpers/retryableExec.ts b/packages/dashboard-backend/src/devworkspaceClient/services/helpers/retryableExec.ts index f61552e16..5ba45c425 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/helpers/retryableExec.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/helpers/retryableExec.ts @@ -13,6 +13,7 @@ import { helpers } from '@eclipse-che/common'; import { delay } from '@/services/helpers'; +import { logger } from '@/utils/logger'; export async function retryableExec(callback: () => Promise, maxAttempt = 5): Promise { let error: unknown; @@ -21,12 +22,19 @@ export async function retryableExec(callback: () => Promise, maxAttempt = return await callback(); } catch (e) { error = e; - if (helpers.errors.isKubeClientError(error) && error.statusCode === 404) { - return Promise.reject(error); + + const message = helpers.errors.getMessage(e); + logger.warn(`Attempt ${attempt + 1} failed: ${message}`); + + if (helpers.errors.isKubeClientError(e) && e.statusCode === 404) { + break; } - console.error(e); + + logger.debug(e); } await delay(1000); } - return Promise.reject(error); + + logger.error(error); + throw error; } diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/kubeConfigApi.ts b/packages/dashboard-backend/src/devworkspaceClient/services/kubeConfigApi.ts index b8266c7e2..2af5636d3 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/kubeConfigApi.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/kubeConfigApi.ts @@ -19,6 +19,7 @@ import { prepareCoreV1API, } from '@/devworkspaceClient/services/helpers/prepareCoreV1API'; import { IKubeConfigApi } from '@/devworkspaceClient/types'; +import { logger } from '@/utils/logger'; const EXCLUDED_CONTAINERS = ['che-gateway', 'che-machine-exec']; @@ -59,7 +60,7 @@ export class KubeConfigApiService implements IKubeConfigApi { // find the directory where we should create the kubeconfig const kubeConfigDirectory = await this.resolveDirectory(podName, namespace, containerName); if (kubeConfigDirectory === '') { - console.log( + logger.info( `Could not find appropriate kubeconfig directory for ${namespace}/${podName}/${containerName}`, ); continue; @@ -92,7 +93,7 @@ export class KubeConfigApiService implements IKubeConfigApi { resolved = true; } } catch (e) { - console.warn(helpers.errors.getMessage(e)); + logger.warn(e); } } if (!resolved) { @@ -159,9 +160,9 @@ export class KubeConfigApiService implements IKubeConfigApi { return kubeConfigEnvResolver.stdOut.replace(new RegExp('/config$'), ''); } } catch (e) { - const message = helpers.errors.getMessage(e); - console.error( - `Failed to run command 'printenv KUBECONFIG' in '${namespace}/${name}/${containerName}' with message: '${message}'`, + logger.error( + e, + `Failed to run command "printenv KUBECONFIG" in "${namespace}/${name}/${containerName}"`, ); } @@ -183,9 +184,9 @@ export class KubeConfigApiService implements IKubeConfigApi { } } } catch (e) { - const message = helpers.errors.getMessage(e); - console.error( - `Failed to run command 'printenv HOME' in '${namespace}/${name}/${containerName}' with message: '${message}'`, + logger.error( + e, + `Failed to run command "printenv HOME" in "${namespace}/${name}/${containerName}"`, ); } return ''; @@ -199,7 +200,7 @@ export class KubeConfigApiService implements IKubeConfigApi { } return JSON.stringify(kubeConfigJson, undefined, ' '); } catch (e) { - console.error('Failed to parse kubeconfig', e); + logger.error(e, 'Failed to parse kubeconfig'); return kubeConfig; } } diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/logsApi/__tests__/logsApi.retry1.spec.ts b/packages/dashboard-backend/src/devworkspaceClient/services/logsApi/__tests__/logsApi.retry1.spec.ts index 428e55f05..e59294bb1 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/logsApi/__tests__/logsApi.retry1.spec.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/logsApi/__tests__/logsApi.retry1.spec.ts @@ -41,9 +41,6 @@ jest.mock('@kubernetes/client-node', () => { }; }); -// mute console.error -console.warn = jest.fn(); - describe('Logs API Service, retry watching logs (fake timers used)', () => { let logsApiService: LogsApiService; diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/logsApi/__tests__/logsApi.retry2.spec.ts b/packages/dashboard-backend/src/devworkspaceClient/services/logsApi/__tests__/logsApi.retry2.spec.ts index ef92bfaba..af47fde73 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/logsApi/__tests__/logsApi.retry2.spec.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/logsApi/__tests__/logsApi.retry2.spec.ts @@ -39,9 +39,6 @@ jest.mock('@kubernetes/client-node', () => { }; }); -// mute console.error -console.warn = jest.fn(); - describe('Logs API Service, retry watching logs (real timers used)', () => { let logsApiService: LogsApiService; diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/logsApi/__tests__/logsApi.spec.ts b/packages/dashboard-backend/src/devworkspaceClient/services/logsApi/__tests__/logsApi.spec.ts index 189acdfaf..7e8756836 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/logsApi/__tests__/logsApi.spec.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/logsApi/__tests__/logsApi.spec.ts @@ -43,10 +43,6 @@ jest.mock('@kubernetes/client-node', () => { }; }); -// mute console.error -console.warn = jest.fn(); -console.error = jest.fn(); - describe('Logs API Service', () => { let logsApiService: LogsApiService; diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/logsApi/index.ts b/packages/dashboard-backend/src/devworkspaceClient/services/logsApi/index.ts index aa378781d..1615938b2 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/logsApi/index.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/logsApi/index.ts @@ -24,6 +24,7 @@ import { ILogsApi } from '@/devworkspaceClient/types'; import { isHttpError, isResponse, isV1Status } from '@/helpers/typeguards'; import { delay } from '@/services/helpers'; import { MessageListener } from '@/services/types/Observer'; +import { logger } from '@/utils/logger'; type StopWatchCallback = () => void; type ContainerName = string; @@ -68,7 +69,7 @@ export class LogsApiService implements ILogsApi { params, }); - console.warn(`Unable to watch logs in pod "${params.podName}". Error:`, e); + logger.warn(e, `Unable to watch logs in pod "${params.podName}".`); } await Promise.all( @@ -183,7 +184,6 @@ export class LogsApiService implements ILogsApi { this.storeStopWatchCallback(params, containerName, () => request.destroy()); } catch (e) { const status = this.buildStatus(e); - console.warn(status.message); listener({ eventPhase: api.webSocket.EventPhase.ERROR, @@ -191,7 +191,7 @@ export class LogsApiService implements ILogsApi { params: { ...params, containerName }, }); - console.warn(`Unable to watch logs of ${containerName} in ${podName}: ${status.message}`); + logger.warn(e, `Unable to watch logs of ${containerName} in ${podName}.`); throw e; } } diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/podApi.ts b/packages/dashboard-backend/src/devworkspaceClient/services/podApi.ts index 69425e746..f6c0e3dc5 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/podApi.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/podApi.ts @@ -23,6 +23,7 @@ import { import { prepareCustomObjectWatch } from '@/devworkspaceClient/services/helpers/prepareCustomObjectWatch'; import { IPodApi } from '@/devworkspaceClient/types'; import { MessageListener } from '@/services/types/Observer'; +import { logger } from '@/utils/logger'; const EVENT_API_ERROR_LABEL = 'CUSTOM_OBJECTS_API_ERROR'; @@ -66,7 +67,7 @@ export class PodApiService implements IPodApi { } private handleWatchError(error: unknown, path: string): void { - console.error(`[ERROR] Stopped watching ${path}. Reason:`, error); + logger.error(error, `Stopped watching ${path}.`); } private handleWatchMessage( diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/podmanApi.ts b/packages/dashboard-backend/src/devworkspaceClient/services/podmanApi.ts index 40d55169f..8def39db0 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/podmanApi.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/podmanApi.ts @@ -19,6 +19,7 @@ import { prepareCoreV1API, } from '@/devworkspaceClient/services/helpers/prepareCoreV1API'; import { IPodmanApi } from '@/devworkspaceClient/types'; +import { logger } from '@/utils/logger'; const EXCLUDED_CONTAINERS = ['che-gateway', 'che-machine-exec']; @@ -84,7 +85,7 @@ export class PodmanApiService implements IPodmanApi { resolved = true; } } catch (e) { - console.warn(helpers.errors.getMessage(e)); + logger.warn(e); } } if (!resolved) { diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/serverConfigApi.ts b/packages/dashboard-backend/src/devworkspaceClient/services/serverConfigApi.ts index eb5fb1a8a..c3acb522e 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/serverConfigApi.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/serverConfigApi.ts @@ -23,12 +23,13 @@ import { prepareCustomObjectAPI, } from '@/devworkspaceClient/services/helpers/prepareCustomObjectAPI'; import { - CustomResourceDefinition, + CheClusterCustomResource, + CheClusterCustomResourceSpecDevEnvironments, CustomResourceDefinitionList, - CustomResourceDefinitionSpecDevEnvironments, IServerConfigApi, } from '@/devworkspaceClient/types'; import { isLocalRun } from '@/localRun'; +import { logger } from '@/utils/logger'; const CUSTOM_RESOURCE_DEFINITIONS_API_ERROR_LABEL = 'CUSTOM_RESOURCE_DEFINITIONS_API_ERROR'; @@ -50,7 +51,7 @@ export class ServerConfigApiService implements IServerConfigApi { }; } - async fetchCheCustomResource(): Promise { + async fetchCheCustomResource(): Promise { if (isLocalRun()) { return JSON.parse( readFileSync(path.join(__dirname, '../../../../run/.custom-resources')).toString(), @@ -69,7 +70,7 @@ export class ServerConfigApiService implements IServerConfigApi { const customResourceDefinitionsList = body as CustomResourceDefinitionList; const cheCustomResource = customResourceDefinitionsList.items?.find( - (item: CustomResourceDefinition) => + (item: CheClusterCustomResource) => item.metadata?.name === this.env.NAME && item.metadata?.namespace === this.env.NAMESPACE, ); @@ -84,9 +85,9 @@ export class ServerConfigApiService implements IServerConfigApi { } getContainerBuild( - cheCustomResource: CustomResourceDefinition, + cheCustomResource: CheClusterCustomResource, ): Pick< - CustomResourceDefinitionSpecDevEnvironments, + CheClusterCustomResourceSpecDevEnvironments, 'containerBuildConfiguration' | 'disableContainerBuildCapabilities' > { const { devEnvironments } = cheCustomResource.spec; @@ -106,11 +107,11 @@ export class ServerConfigApiService implements IServerConfigApi { }; } - getDefaultPlugins(cheCustomResource: CustomResourceDefinition): api.IWorkspacesDefaultPlugins[] { + getDefaultPlugins(cheCustomResource: CheClusterCustomResource): api.IWorkspacesDefaultPlugins[] { return cheCustomResource.spec.devEnvironments?.defaultPlugins || []; } - getDefaultDevfileRegistryUrl(cheCustomResource: CustomResourceDefinition): string { + getDefaultDevfileRegistryUrl(cheCustomResource: CheClusterCustomResource): string { let devfileRegistryURL = cheCustomResource.status?.devfileRegistryURL || ''; if (devfileRegistryURL && !devfileRegistryURL.endsWith('/')) { devfileRegistryURL += '/'; @@ -119,18 +120,18 @@ export class ServerConfigApiService implements IServerConfigApi { return devfileRegistryURL; } - getDefaultPluginRegistryUrl(cheCustomResource: CustomResourceDefinition): string { + getDefaultPluginRegistryUrl(cheCustomResource: CheClusterCustomResource): string { return cheCustomResource.status?.pluginRegistryURL || ''; } - getDefaultEditor(cheCustomResource: CustomResourceDefinition): string | undefined { + getDefaultEditor(cheCustomResource: CheClusterCustomResource): string | undefined { return ( cheCustomResource.spec.devEnvironments?.defaultEditor || process.env['CHE_DEFAULT_SPEC_DEVENVIRONMENTS_DEFAULTEDITOR'] ); } - getDefaultComponents(cheCustomResource: CustomResourceDefinition): V221DevfileComponents[] { + getDefaultComponents(cheCustomResource: CheClusterCustomResource): V221DevfileComponents[] { if (cheCustomResource.spec.devEnvironments?.defaultComponents) { return cheCustomResource.spec.devEnvironments.defaultComponents; } @@ -139,8 +140,9 @@ export class ServerConfigApiService implements IServerConfigApi { try { return JSON.parse(process.env['CHE_DEFAULT_SPEC_DEVENVIRONMENTS_DEFAULTCOMPONENTS']); } catch (e) { - console.error( - `Unable to parse default components from environment variable CHE_DEFAULT_SPEC_DEVENVIRONMENTS_DEFAULTCOMPONENTS: ${e}`, + logger.error( + e, + `Unable to parse default components from environment variable CHE_DEFAULT_SPEC_DEVENVIRONMENTS_DEFAULTCOMPONENTS.`, ); } } @@ -148,7 +150,7 @@ export class ServerConfigApiService implements IServerConfigApi { return []; } - getPluginRegistry(cheCustomResource: CustomResourceDefinition): api.IPluginRegistry { + getPluginRegistry(cheCustomResource: CheClusterCustomResource): api.IPluginRegistry { // Undefined and empty value are treated in a different ways: // - empty value forces to use embedded registry // - undefined value means that the default value should be used @@ -169,21 +171,21 @@ export class ServerConfigApiService implements IServerConfigApi { return pluginRegistry; } - getPvcStrategy(cheCustomResource: CustomResourceDefinition): string | undefined { + getPvcStrategy(cheCustomResource: CheClusterCustomResource): string | undefined { return cheCustomResource.spec.devEnvironments?.storage?.pvcStrategy; } - getInternalRegistryDisableStatus(cheCustomResource: CustomResourceDefinition): boolean { + getInternalRegistryDisableStatus(cheCustomResource: CheClusterCustomResource): boolean { return cheCustomResource.spec.components?.devfileRegistry?.disableInternalRegistry || false; } getExternalDevfileRegistries( - cheCustomResource: CustomResourceDefinition, + cheCustomResource: CheClusterCustomResource, ): api.IExternalDevfileRegistry[] { return cheCustomResource.spec.components?.devfileRegistry?.externalDevfileRegistries || []; } - getDashboardWarning(cheCustomResource: CustomResourceDefinition): string | undefined { + getDashboardWarning(cheCustomResource: CheClusterCustomResource): string | undefined { // Return the message if it is defined and the show flag is true if (cheCustomResource.spec.components?.dashboard?.headerMessage?.text) { return cheCustomResource.spec.components?.dashboard?.headerMessage?.show @@ -197,7 +199,7 @@ export class ServerConfigApiService implements IServerConfigApi { // getRunningWorkspacesLimit return the maximum number of running workspaces. // See https://github.com/eclipse-che/che-operator/pull/1585 for details. - getRunningWorkspacesLimit(cheCustomResource: CustomResourceDefinition): number { + getRunningWorkspacesLimit(cheCustomResource: CheClusterCustomResource): number { return ( cheCustomResource.spec.devEnvironments?.maxNumberOfRunningWorkspacesPerUser || cheCustomResource.spec.components?.devWorkspace?.runningLimit || @@ -205,24 +207,24 @@ export class ServerConfigApiService implements IServerConfigApi { ); } - getAllWorkspacesLimit(cheCustomResource: CustomResourceDefinition): number { + getAllWorkspacesLimit(cheCustomResource: CheClusterCustomResource): number { return cheCustomResource.spec.devEnvironments?.maxNumberOfWorkspacesPerUser || -1; } - getWorkspaceInactivityTimeout(cheCustomResource: CustomResourceDefinition): number { + getWorkspaceInactivityTimeout(cheCustomResource: CheClusterCustomResource): number { return cheCustomResource.spec.devEnvironments?.secondsOfInactivityBeforeIdling || -1; } - getWorkspaceRunTimeout(cheCustomResource: CustomResourceDefinition): number { + getWorkspaceRunTimeout(cheCustomResource: CheClusterCustomResource): number { return cheCustomResource.spec.devEnvironments?.secondsOfRunBeforeIdling || -1; } - getWorkspaceStartTimeout(cheCustomResource: CustomResourceDefinition): number { + getWorkspaceStartTimeout(cheCustomResource: CheClusterCustomResource): number { return cheCustomResource.spec.devEnvironments?.startTimeoutSeconds || startTimeoutSeconds; } getDashboardLogo( - cheCustomResource: CustomResourceDefinition, + cheCustomResource: CheClusterCustomResource, ): { base64data: string; mediatype: string } | undefined { return cheCustomResource.spec.components?.dashboard?.branding?.logo; } diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/userProfileApi.ts b/packages/dashboard-backend/src/devworkspaceClient/services/userProfileApi.ts index 20b737b2c..04b281c5a 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/userProfileApi.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/userProfileApi.ts @@ -42,7 +42,6 @@ export class UserProfileApiService implements IUserProfileApi { email: Buffer.from(data.email, 'base64').toString(), }; } catch (e) { - console.error('Unable to get user profile data:', e); throw createError(e, ERROR_LABEL, 'Unable to get user profile data'); } } diff --git a/packages/dashboard-backend/src/devworkspaceClient/types/index.ts b/packages/dashboard-backend/src/devworkspaceClient/types/index.ts index 543866f6c..2d90af164 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/types/index.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/types/index.ts @@ -112,13 +112,13 @@ export interface IDevWorkspaceTemplateApi { } export type CustomResourceDefinitionList = k8s.V1CustomResourceDefinitionList & { - items?: CustomResourceDefinition[]; + items?: CheClusterCustomResource[]; }; -export type CustomResourceDefinition = k8s.V1CustomResourceDefinition & { +export type CheClusterCustomResource = k8s.V1CustomResourceDefinition & { spec: { - devEnvironments?: CustomResourceDefinitionSpecDevEnvironments; - components?: CustomResourceDefinitionSpecComponents; + devEnvironments?: CheClusterCustomResourceSpecDevEnvironments; + components?: CheClusterCustomResourceSpecComponents; }; status: { devfileRegistryURL: string; @@ -126,7 +126,21 @@ export type CustomResourceDefinition = k8s.V1CustomResourceDefinition & { }; }; -export type CustomResourceDefinitionSpecDevEnvironments = { +export function isCheClusterCustomResource(object: unknown): object is CheClusterCustomResource { + if (typeof object !== 'object' || object === null) { + return false; + } + + const { spec, status } = object as CheClusterCustomResource; + return ( + spec !== undefined && + status !== undefined && + (isCheClusterCustomResourceSpecDevEnvironments(spec.devEnvironments) || + isCheClusterCustomResourceSpecComponent(spec.components)) + ); +} + +export type CheClusterCustomResourceSpecDevEnvironments = { containerBuildConfiguration?: { openShiftSecurityContextConstraint?: string; }; @@ -134,17 +148,53 @@ export type CustomResourceDefinitionSpecDevEnvironments = { defaultEditor?: string; defaultPlugins?: api.IWorkspacesDefaultPlugins[]; disableContainerBuildCapabilities?: boolean; - secondsOfInactivityBeforeIdling?: number; + secondsOfInactivityBeforeIdling: number; secondsOfRunBeforeIdling?: number; startTimeoutSeconds?: number; storage?: { pvcStrategy?: string; }; - maxNumberOfRunningWorkspacesPerUser: number; - maxNumberOfWorkspacesPerUser: number; + maxNumberOfRunningWorkspacesPerUser?: number; + maxNumberOfWorkspacesPerUser?: number; }; -export type CustomResourceDefinitionSpecComponents = { +export function isCheClusterCustomResourceSpecDevEnvironments( + object: unknown, +): object is CheClusterCustomResourceSpecDevEnvironments { + if (typeof object !== 'object' || object === null) { + return false; + } + + const { + containerBuildConfiguration, + defaultComponents, + defaultEditor, + defaultPlugins, + disableContainerBuildCapabilities, + secondsOfInactivityBeforeIdling, + secondsOfRunBeforeIdling, + startTimeoutSeconds, + storage, + maxNumberOfRunningWorkspacesPerUser, + maxNumberOfWorkspacesPerUser, + } = object as CheClusterCustomResourceSpecDevEnvironments; + return ( + containerBuildConfiguration !== undefined || + defaultComponents !== undefined || + defaultEditor !== undefined || + defaultPlugins !== undefined || + disableContainerBuildCapabilities !== undefined || + secondsOfInactivityBeforeIdling !== undefined || + secondsOfRunBeforeIdling !== undefined || + startTimeoutSeconds !== undefined || + storage !== undefined || + maxNumberOfRunningWorkspacesPerUser !== undefined || + maxNumberOfWorkspacesPerUser !== undefined + ); +} + +export type CheClusterCustomResourceSpecComponents = { + cheServer?: Record; dashboard?: { branding?: { logo?: { @@ -156,105 +206,124 @@ export type CustomResourceDefinitionSpecComponents = { show?: boolean; text?: string; }; + logLevel?: string; }; devWorkspace?: { runningLimit?: number; }; pluginRegistry?: api.IPluginRegistry; - devfileRegistry: { + devfileRegistry?: { disableInternalRegistry?: boolean; externalDevfileRegistries?: api.IExternalDevfileRegistry[]; }; }; +export function isCheClusterCustomResourceSpecComponent( + object: unknown, +): object is CheClusterCustomResourceSpecComponents { + if (typeof object !== 'object' || object === null) { + return false; + } + + const { cheServer, dashboard, devWorkspace, pluginRegistry, devfileRegistry } = + object as CheClusterCustomResourceSpecComponents; + return ( + cheServer !== undefined || + dashboard !== undefined || + devWorkspace !== undefined || + pluginRegistry !== undefined || + devfileRegistry !== undefined + ); +} + export interface IServerConfigApi { /** * Returns custom resource */ - fetchCheCustomResource(): Promise; + fetchCheCustomResource(): Promise; /** * Returns the container build capabilities and configuration. */ getContainerBuild( - cheCustomResource: CustomResourceDefinition, + cheCustomResource: CheClusterCustomResource, ): Pick< - CustomResourceDefinitionSpecDevEnvironments, + CheClusterCustomResourceSpecDevEnvironments, 'containerBuildConfiguration' | 'disableContainerBuildCapabilities' >; /** * Returns default plugins */ - getDefaultPlugins(cheCustomResource: CustomResourceDefinition): api.IWorkspacesDefaultPlugins[]; + getDefaultPlugins(cheCustomResource: CheClusterCustomResource): api.IWorkspacesDefaultPlugins[]; /** * Returns the default devfile registry URL. */ - getDefaultDevfileRegistryUrl(cheCustomResource: CustomResourceDefinition): string; + getDefaultDevfileRegistryUrl(cheCustomResource: CheClusterCustomResource): string; /** * Returns the plugin registry URL. */ - getDefaultPluginRegistryUrl(cheCustomResource: CustomResourceDefinition): string; + getDefaultPluginRegistryUrl(cheCustomResource: CheClusterCustomResource): string; /** * Returns the default editor to workspace create with. It could be a plugin ID or a URI. */ - getDefaultEditor(cheCustomResource: CustomResourceDefinition): string | undefined; + getDefaultEditor(cheCustomResource: CheClusterCustomResource): string | undefined; /** * Returns the default components applied to DevWorkspaces. * These default components are meant to be used when a Devfile does not contain any components. */ - getDefaultComponents(cheCustomResource: CustomResourceDefinition): V221DevfileComponents[]; + getDefaultComponents(cheCustomResource: CheClusterCustomResource): V221DevfileComponents[]; /** * Returns the plugin registry. */ - getPluginRegistry(cheCustomResource: CustomResourceDefinition): api.IPluginRegistry; + getPluginRegistry(cheCustomResource: CheClusterCustomResource): api.IPluginRegistry; /** * Returns the internal registry disable status. */ - getInternalRegistryDisableStatus(cheCustomResource: CustomResourceDefinition): boolean; + getInternalRegistryDisableStatus(cheCustomResource: CheClusterCustomResource): boolean; /** * Returns the external devfile registries. */ getExternalDevfileRegistries( - cheCustomResource: CustomResourceDefinition, + cheCustomResource: CheClusterCustomResource, ): api.IExternalDevfileRegistry[]; /** * Returns the PVC strategy if it is defined. */ - getPvcStrategy(cheCustomResource: CustomResourceDefinition): string | undefined; + getPvcStrategy(cheCustomResource: CheClusterCustomResource): string | undefined; /** * Returns a maintenance warning. */ - getDashboardWarning(cheCustomResource: CustomResourceDefinition): string | undefined; + getDashboardWarning(cheCustomResource: CheClusterCustomResource): string | undefined; /** * Returns limit of running workspaces per user. */ - getRunningWorkspacesLimit(cheCustomResource: CustomResourceDefinition): number; + getRunningWorkspacesLimit(cheCustomResource: CheClusterCustomResource): number; /** * Returns the total number of workspaces, both stopped and running, that a user can keep. */ - getAllWorkspacesLimit(cheCustomResource: CustomResourceDefinition): number; + getAllWorkspacesLimit(cheCustomResource: CheClusterCustomResource): number; /** * Returns the workspace inactivity timeout */ - getWorkspaceInactivityTimeout(cheCustomResource: CustomResourceDefinition): number; + getWorkspaceInactivityTimeout(cheCustomResource: CheClusterCustomResource): number; /** * Returns the workspace run timeout */ - getWorkspaceRunTimeout(cheCustomResource: CustomResourceDefinition): number; + getWorkspaceRunTimeout(cheCustomResource: CheClusterCustomResource): number; /** * Returns the workspace start timeout */ - getWorkspaceStartTimeout(cheCustomResource: CustomResourceDefinition): number; + getWorkspaceStartTimeout(cheCustomResource: CheClusterCustomResource): number; /** * Returns the dashboard branding logo */ getDashboardLogo( - cheCustomResource: CustomResourceDefinition, + cheCustomResource: CheClusterCustomResource, ): { base64data: string; mediatype: string } | undefined; } diff --git a/packages/dashboard-backend/src/helpers/__tests__/getUserName.spec.ts b/packages/dashboard-backend/src/helpers/__tests__/getUserName.spec.ts index 09bf3fb6f..ebf16256f 100644 --- a/packages/dashboard-backend/src/helpers/__tests__/getUserName.spec.ts +++ b/packages/dashboard-backend/src/helpers/__tests__/getUserName.spec.ts @@ -11,6 +11,7 @@ */ import { getUserName } from '@/helpers/getUserName'; +import { logger } from '@/utils/logger'; describe('helpers', () => { afterEach(() => { @@ -47,10 +48,9 @@ describe('helpers', () => { ']]]{"iss":"https://dex.myhost","aud":"eclipse-che","exp":1234567890,"iat":1234567890,"email":"user1@che","email_verified":true,"name":"user1"}', ).toString('base64') + '.signature'; - console.warn = jest.fn(); expect(() => getUserName(token)).toThrow(); - expect(console.warn).toHaveBeenCalledWith(`[WARN] Can't parse the token payload.`); + expect(logger.warn).toHaveBeenCalledWith(`Can't parse the token payload.`); }); }); }); diff --git a/packages/dashboard-backend/src/helpers/getUserName.ts b/packages/dashboard-backend/src/helpers/getUserName.ts index d55e6837f..2f36cf93b 100644 --- a/packages/dashboard-backend/src/helpers/getUserName.ts +++ b/packages/dashboard-backend/src/helpers/getUserName.ts @@ -10,6 +10,8 @@ * Red Hat, Inc. - initial API and implementation */ +import { logger } from '@/utils/logger'; + // currently, it works on minikube only (decode from Base64 format) export function getUserName(token: string): string { const tokenPayload = token.split('.')[1]; @@ -18,7 +20,7 @@ export function getUserName(token: string): string { const parsedTokenPayload = JSON.parse(decodedTokenPayload); return parsedTokenPayload.name; } catch (e) { - console.warn(`[WARN] Can't parse the token payload.`); + logger.warn(`Can't parse the token payload.`); throw e; } } diff --git a/packages/dashboard-backend/src/localRun/index.ts b/packages/dashboard-backend/src/localRun/index.ts index 801d7f637..f7886e713 100644 --- a/packages/dashboard-backend/src/localRun/index.ts +++ b/packages/dashboard-backend/src/localRun/index.ts @@ -19,6 +19,7 @@ import { registerCheApiProxy } from '@/localRun/proxies/cheServerApi'; import { registerDexProxies } from '@/localRun/proxies/dexProxies'; import { registerDexCallback } from '@/localRun/routes/dexCallback'; import { registerSignOut } from '@/localRun/routes/signOut'; +import { logger } from '@/utils/logger'; export function isLocalRun(): boolean { const isLocalRun = process.env['LOCAL_RUN'] === 'true'; @@ -26,7 +27,7 @@ export function isLocalRun(): boolean { } export function registerLocalRun(server: FastifyInstance) { - console.log('Running locally, setting up stubs'); + logger.info('Running locally, setting up stubs'); const isNativeAuth = process.env['NATIVE_AUTH'] === 'true'; const cheApiProxyUpstream = process.env['CHE_API_PROXY_UPSTREAM'] || ''; diff --git a/packages/dashboard-backend/src/localRun/proxies/cheServerApi.ts b/packages/dashboard-backend/src/localRun/proxies/cheServerApi.ts index d9e16663a..eda99ba82 100644 --- a/packages/dashboard-backend/src/localRun/proxies/cheServerApi.ts +++ b/packages/dashboard-backend/src/localRun/proxies/cheServerApi.ts @@ -12,15 +12,16 @@ import fastifyHttpProxy from '@fastify/http-proxy'; import { FastifyInstance } from 'fastify'; -// to workaround the issue with TextEncoder import { TextEncoder } from 'util'; import { stubCheServerOptionsRequests } from '@/localRun/hooks/stubCheServerOptionsRequests'; +import { logger } from '@/utils/logger'; +// to workaround the issue with TextEncoder (global as any).TextEncoder = TextEncoder; export function registerCheApiProxy(server: FastifyInstance, upstream: string, origin: string) { - console.log(`Dashboard proxies requests to Che Server API on ${upstream}/api.`); + logger.info(`Dashboard proxies requests to Che Server API on ${upstream}/api.`); // server api server.register(fastifyHttpProxy, { upstream, diff --git a/packages/dashboard-backend/src/plugins/staticServer.ts b/packages/dashboard-backend/src/plugins/staticServer.ts index 998dad70b..993fde441 100644 --- a/packages/dashboard-backend/src/plugins/staticServer.ts +++ b/packages/dashboard-backend/src/plugins/staticServer.ts @@ -14,9 +14,11 @@ import fastifyStatic from '@fastify/static'; import { DoneFuncWithErrOrRes, FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'; import path from 'path'; +import { logger } from '@/utils/logger'; + export function registerStaticServer(publicFolder: string, server: FastifyInstance) { const rootPath = path.resolve(__dirname, publicFolder); - console.log(`Static server's serving "${rootPath}" on 0.0.0.0:8080/`); + logger.info(`Static server's serving "${rootPath}" on 0.0.0.0:8080/`); server.register(fastifyStatic, { root: rootPath, diff --git a/packages/dashboard-backend/src/plugins/swagger.ts b/packages/dashboard-backend/src/plugins/swagger.ts index f9df9d771..e95834cb4 100644 --- a/packages/dashboard-backend/src/plugins/swagger.ts +++ b/packages/dashboard-backend/src/plugins/swagger.ts @@ -14,6 +14,8 @@ import fastifySwagger from '@fastify/swagger'; import fastifySwaggerUi from '@fastify/swagger-ui'; import { FastifyInstance } from 'fastify'; +import { logger } from '@/utils/logger'; + const ROUTE_PREFIX = '/dashboard/api/swagger'; type MySchema = { @@ -25,7 +27,7 @@ type MySchema = { }; export function registerSwagger(server: FastifyInstance) { - console.log(`Che Dashboard swagger is running on "${ROUTE_PREFIX}".`); + logger.info(`Che Dashboard swagger is running on "${ROUTE_PREFIX}".`); server.register(fastifySwagger, { mode: 'dynamic', diff --git a/packages/dashboard-backend/src/routes/__tests__/factoryAcceptanceRedirect.spec.ts b/packages/dashboard-backend/src/routes/__tests__/factoryAcceptanceRedirect.spec.ts index 0de6700cd..12f7f111a 100644 --- a/packages/dashboard-backend/src/routes/__tests__/factoryAcceptanceRedirect.spec.ts +++ b/packages/dashboard-backend/src/routes/__tests__/factoryAcceptanceRedirect.spec.ts @@ -12,7 +12,7 @@ import { FastifyInstance } from 'fastify'; -import { setup, teardown } from '@/helpers/tests/appBuilder'; +import { setup, teardown } from '@/utils/appBuilder'; describe('Factory Acceptance Redirect', () => { let app: FastifyInstance; diff --git a/packages/dashboard-backend/src/routes/api/__tests__/clusterConfig.spec.ts b/packages/dashboard-backend/src/routes/api/__tests__/clusterConfig.spec.ts index d0fa0db82..dfa78643d 100644 --- a/packages/dashboard-backend/src/routes/api/__tests__/clusterConfig.spec.ts +++ b/packages/dashboard-backend/src/routes/api/__tests__/clusterConfig.spec.ts @@ -13,12 +13,12 @@ import { FastifyInstance } from 'fastify'; import { baseApiPath } from '@/constants/config'; -import { setup, teardown } from '@/helpers/tests/appBuilder'; import { stubAllWorkspacesLimit, stubDashboardWarning, stubRunningWorkspacesLimit, } from '@/routes/api/helpers/__mocks__/getDevWorkspaceClient'; +import { setup, teardown } from '@/utils/appBuilder'; jest.mock('../helpers/getServiceAccountToken.ts'); jest.mock('../helpers/getDevWorkspaceClient.ts'); diff --git a/packages/dashboard-backend/src/routes/api/__tests__/clusterInfo.spec.ts b/packages/dashboard-backend/src/routes/api/__tests__/clusterInfo.spec.ts index 87db8ad3c..889f2401e 100644 --- a/packages/dashboard-backend/src/routes/api/__tests__/clusterInfo.spec.ts +++ b/packages/dashboard-backend/src/routes/api/__tests__/clusterInfo.spec.ts @@ -14,7 +14,7 @@ import { ApplicationId } from '@eclipse-che/common'; import { FastifyInstance } from 'fastify'; import { baseApiPath } from '@/constants/config'; -import { setup, teardown } from '@/helpers/tests/appBuilder'; +import { setup, teardown } from '@/utils/appBuilder'; describe('Cluster Info Route', () => { let app: FastifyInstance; diff --git a/packages/dashboard-backend/src/routes/api/__tests__/devworkspaceTemplates.spec.ts b/packages/dashboard-backend/src/routes/api/__tests__/devworkspaceTemplates.spec.ts index ed612a1eb..eff6504b1 100644 --- a/packages/dashboard-backend/src/routes/api/__tests__/devworkspaceTemplates.spec.ts +++ b/packages/dashboard-backend/src/routes/api/__tests__/devworkspaceTemplates.spec.ts @@ -14,11 +14,11 @@ import { api } from '@eclipse-che/common'; import { FastifyInstance } from 'fastify'; import { baseApiPath } from '@/constants/config'; -import { setup, teardown } from '@/helpers/tests/appBuilder'; import { stubDevWorkspaceTemplate, stubDevWorkspaceTemplatesList, } from '@/routes/api/helpers/__mocks__/getDevWorkspaceClient'; +import { setup, teardown } from '@/utils/appBuilder'; jest.mock('../helpers/getDevWorkspaceClient.ts'); jest.mock('../helpers/getToken.ts'); diff --git a/packages/dashboard-backend/src/routes/api/__tests__/devworkspaces.spec.ts b/packages/dashboard-backend/src/routes/api/__tests__/devworkspaces.spec.ts index 4a2271046..3d68bfbee 100644 --- a/packages/dashboard-backend/src/routes/api/__tests__/devworkspaces.spec.ts +++ b/packages/dashboard-backend/src/routes/api/__tests__/devworkspaces.spec.ts @@ -14,11 +14,11 @@ import { api } from '@eclipse-che/common'; import { FastifyInstance } from 'fastify'; import { baseApiPath } from '@/constants/config'; -import { setup, teardown } from '@/helpers/tests/appBuilder'; import { stubDevWorkspace, stubDevWorkspacesList, } from '@/routes/api/helpers/__mocks__/getDevWorkspaceClient'; +import { setup, teardown } from '@/utils/appBuilder'; jest.mock('../helpers/getDevWorkspaceClient.ts'); jest.mock('../helpers/getToken.ts'); diff --git a/packages/dashboard-backend/src/routes/api/__tests__/dockerConfig.spec.ts b/packages/dashboard-backend/src/routes/api/__tests__/dockerConfig.spec.ts index c169c1021..cc01a4b6e 100644 --- a/packages/dashboard-backend/src/routes/api/__tests__/dockerConfig.spec.ts +++ b/packages/dashboard-backend/src/routes/api/__tests__/dockerConfig.spec.ts @@ -13,8 +13,8 @@ import { FastifyInstance } from 'fastify'; import { baseApiPath } from '@/constants/config'; -import { setup, teardown } from '@/helpers/tests/appBuilder'; import { stubDockerConfig } from '@/routes/api/helpers/__mocks__/getDevWorkspaceClient'; +import { setup, teardown } from '@/utils/appBuilder'; jest.mock('../helpers/getDevWorkspaceClient.ts'); jest.mock('../helpers/getToken.ts'); diff --git a/packages/dashboard-backend/src/routes/api/__tests__/events.spec.ts b/packages/dashboard-backend/src/routes/api/__tests__/events.spec.ts index 0f1625063..61e3a1b44 100644 --- a/packages/dashboard-backend/src/routes/api/__tests__/events.spec.ts +++ b/packages/dashboard-backend/src/routes/api/__tests__/events.spec.ts @@ -13,8 +13,8 @@ import { FastifyInstance } from 'fastify'; import { baseApiPath } from '@/constants/config'; -import { setup, teardown } from '@/helpers/tests/appBuilder'; import { stubEventsList } from '@/routes/api/helpers/__mocks__/getDevWorkspaceClient'; +import { setup, teardown } from '@/utils/appBuilder'; jest.mock('../helpers/getToken.ts'); jest.mock('../helpers/getDevWorkspaceClient.ts'); diff --git a/packages/dashboard-backend/src/routes/api/__tests__/gitConfig.spec.ts b/packages/dashboard-backend/src/routes/api/__tests__/gitConfig.spec.ts index aa63703d0..baabfa106 100644 --- a/packages/dashboard-backend/src/routes/api/__tests__/gitConfig.spec.ts +++ b/packages/dashboard-backend/src/routes/api/__tests__/gitConfig.spec.ts @@ -14,7 +14,7 @@ import { api } from '@eclipse-che/common'; import { FastifyInstance } from 'fastify'; import { baseApiPath } from '@/constants/config'; -import { setup, teardown } from '@/helpers/tests/appBuilder'; +import { setup, teardown } from '@/utils/appBuilder'; const mockRead = jest.fn().mockResolvedValue({}); const mockPatch = jest.fn().mockResolvedValue({}); diff --git a/packages/dashboard-backend/src/routes/api/__tests__/kubeConfig.spec.ts b/packages/dashboard-backend/src/routes/api/__tests__/kubeConfig.spec.ts index c44219d34..a71c3a7be 100644 --- a/packages/dashboard-backend/src/routes/api/__tests__/kubeConfig.spec.ts +++ b/packages/dashboard-backend/src/routes/api/__tests__/kubeConfig.spec.ts @@ -13,7 +13,7 @@ import { FastifyInstance } from 'fastify'; import { baseApiPath } from '@/constants/config'; -import { setup, teardown } from '@/helpers/tests/appBuilder'; +import { setup, teardown } from '@/utils/appBuilder'; jest.mock('../helpers/getDevWorkspaceClient.ts'); jest.mock('../helpers/getToken.ts'); diff --git a/packages/dashboard-backend/src/routes/api/__tests__/personalAccessToken.spec.ts b/packages/dashboard-backend/src/routes/api/__tests__/personalAccessToken.spec.ts index ee04c70b4..5c1877a23 100644 --- a/packages/dashboard-backend/src/routes/api/__tests__/personalAccessToken.spec.ts +++ b/packages/dashboard-backend/src/routes/api/__tests__/personalAccessToken.spec.ts @@ -14,8 +14,8 @@ import { api } from '@eclipse-che/common'; import { FastifyInstance } from 'fastify'; import { baseApiPath } from '@/constants/config'; -import { setup, teardown } from '@/helpers/tests/appBuilder'; import { stubPersonalAccessTokenList } from '@/routes/api/helpers/__mocks__/getDevWorkspaceClient'; +import { setup, teardown } from '@/utils/appBuilder'; jest.mock('../helpers/getToken.ts'); jest.mock('../helpers/getDevWorkspaceClient.ts'); diff --git a/packages/dashboard-backend/src/routes/api/__tests__/pods.spec.ts b/packages/dashboard-backend/src/routes/api/__tests__/pods.spec.ts index acbf5f20f..47c8507bf 100644 --- a/packages/dashboard-backend/src/routes/api/__tests__/pods.spec.ts +++ b/packages/dashboard-backend/src/routes/api/__tests__/pods.spec.ts @@ -13,8 +13,8 @@ import { FastifyInstance } from 'fastify'; import { baseApiPath } from '@/constants/config'; -import { setup, teardown } from '@/helpers/tests/appBuilder'; import { stubPodsList } from '@/routes/api/helpers/__mocks__/getDevWorkspaceClient'; +import { setup, teardown } from '@/utils/appBuilder'; jest.mock('../helpers/getToken.ts'); jest.mock('../helpers/getDevWorkspaceClient.ts'); diff --git a/packages/dashboard-backend/src/routes/api/__tests__/serverConfig.spec.ts b/packages/dashboard-backend/src/routes/api/__tests__/serverConfig.spec.ts index 6fe1c566f..54b2cb558 100644 --- a/packages/dashboard-backend/src/routes/api/__tests__/serverConfig.spec.ts +++ b/packages/dashboard-backend/src/routes/api/__tests__/serverConfig.spec.ts @@ -13,15 +13,12 @@ import { FastifyInstance } from 'fastify'; import { baseApiPath } from '@/constants/config'; -import { setup, teardown } from '@/helpers/tests/appBuilder'; +import { setup, teardown } from '@/utils/appBuilder'; jest.mock('../helpers/getDevWorkspaceClient.ts'); jest.mock('../helpers/getToken.ts'); jest.mock('../helpers/getServiceAccountToken.ts'); -// mute the outputs -console.log = jest.fn(); - describe('Server Config Route', () => { let app: FastifyInstance; const pluginRegistryInternalURL = 'http://plugin-registry.internal'; diff --git a/packages/dashboard-backend/src/routes/api/__tests__/userProfile.spec.ts b/packages/dashboard-backend/src/routes/api/__tests__/userProfile.spec.ts index 4dfdc7e96..77636f3a1 100644 --- a/packages/dashboard-backend/src/routes/api/__tests__/userProfile.spec.ts +++ b/packages/dashboard-backend/src/routes/api/__tests__/userProfile.spec.ts @@ -13,8 +13,8 @@ import { FastifyInstance } from 'fastify'; import { baseApiPath } from '@/constants/config'; -import { setup, teardown } from '@/helpers/tests/appBuilder'; import { stubUserProfile } from '@/routes/api/helpers/__mocks__/getDevWorkspaceClient'; +import { setup, teardown } from '@/utils/appBuilder'; jest.mock('../helpers/getDevWorkspaceClient.ts'); jest.mock('../helpers/getToken.ts'); diff --git a/packages/dashboard-backend/src/routes/api/__tests__/yamlResolver.spec.ts b/packages/dashboard-backend/src/routes/api/__tests__/yamlResolver.spec.ts index 98042d1c9..1a10079c6 100644 --- a/packages/dashboard-backend/src/routes/api/__tests__/yamlResolver.spec.ts +++ b/packages/dashboard-backend/src/routes/api/__tests__/yamlResolver.spec.ts @@ -14,7 +14,7 @@ import { FastifyInstance } from 'fastify'; import * as mockNodeFetch from 'node-fetch'; import { baseApiPath } from '@/constants/config'; -import { setup, teardown } from '@/helpers/tests/appBuilder'; +import { setup, teardown } from '@/utils/appBuilder'; const { Response } = jest.requireActual('node-fetch'); diff --git a/packages/dashboard-backend/src/routes/api/helpers/getServiceAccountToken.ts b/packages/dashboard-backend/src/routes/api/helpers/getServiceAccountToken.ts index 1788c91c3..f0d228036 100644 --- a/packages/dashboard-backend/src/routes/api/helpers/getServiceAccountToken.ts +++ b/packages/dashboard-backend/src/routes/api/helpers/getServiceAccountToken.ts @@ -13,6 +13,7 @@ import { existsSync, readFileSync } from 'fs'; import { isLocalRun } from '@/localRun'; +import { logger } from '@/utils/logger'; export const SERVICE_ACCOUNT_TOKEN_PATH = '/run/secrets/kubernetes.io/serviceaccount/token'; @@ -21,7 +22,7 @@ export function getServiceAccountToken(): string { return process.env.SERVICE_ACCOUNT_TOKEN as string; } if (!existsSync(SERVICE_ACCOUNT_TOKEN_PATH)) { - console.error('SERVICE_ACCOUNT_TOKEN is required'); + logger.fatal('SERVICE_ACCOUNT_TOKEN is required'); process.exit(1); } return readFileSync(SERVICE_ACCOUNT_TOKEN_PATH).toString(); diff --git a/packages/dashboard-backend/src/routes/api/websocket.ts b/packages/dashboard-backend/src/routes/api/websocket.ts index cf987e87a..33a870af4 100644 --- a/packages/dashboard-backend/src/routes/api/websocket.ts +++ b/packages/dashboard-backend/src/routes/api/websocket.ts @@ -20,6 +20,7 @@ import { getDevWorkspaceClient } from '@/routes/api/helpers/getDevWorkspaceClien import { getToken } from '@/routes/api/helpers/getToken'; import { ObjectsWatcher } from '@/services/ObjectsWatcher'; import { SubscriptionManager } from '@/services/SubscriptionManager'; +import { logger } from '@/utils/logger'; export function registerWebsocket(instance: FastifyInstance) { instance.register(async server => { @@ -76,11 +77,11 @@ function webSocketHandler(connection: SocketStream, request: FastifyRequest): vo } ws.on('close', (code: number, reason: string) => { - console.warn(`[WARN] The WebSocket connection closed. Code: ${code}, reason: ${reason}`); + logger.warn(`The WebSocket connection closed. Code: ${code}, reason: ${reason}`); handleUnsubscribeAll(); }); ws.on('error', (error: Error) => { - console.error(`[ERROR] The WebSocket connection error:`, error); + logger.error(error, `The WebSocket connection error:`); handleUnsubscribeAll(); }); ws.on('message', async (messageStr: WebSocket.Data) => { @@ -90,15 +91,15 @@ function webSocketHandler(connection: SocketStream, request: FastifyRequest): vo if (api.webSocket.isWebSocketSubscriptionMessage(obj)) { message = obj; } else { - console.warn(`[WARN] Unexpected WS message payload:`, messageStr.toString()); + logger.warn(`Unexpected WS message payload: %s`, messageStr.toString()); return; } } catch (e) { - console.warn(`[WARN] Can't parse the WS message payload:`, messageStr.toString()); + logger.warn(`Can't parse the WS message payload: %s`, messageStr.toString()); throw e; } - console.log(`[INFO] WS message:`, message); + logger.info(`WS message: %s`, message); switch (message.method) { case 'UNSUBSCRIBE': { diff --git a/packages/dashboard-backend/src/server.ts b/packages/dashboard-backend/src/server.ts index ea6e42ab1..28160aca4 100644 --- a/packages/dashboard-backend/src/server.ts +++ b/packages/dashboard-backend/src/server.ts @@ -13,41 +13,47 @@ import 'reflect-metadata'; import fastify from 'fastify'; +import process from 'process'; import buildApp from '@/app'; import { isLocalRun } from '@/localRun'; +import { watchLogLevel } from '@/services/logWatcher'; +import { stream } from '@/utils/logger'; + +(async function startServer() { + const server = fastify({ + logger: { + stream, + }, + }); + + await buildApp(server); + + try { + const address = await server.listen({ port: 8080, host: '0.0.0.0' }); -const server = fastify({ - logger: false, -}); -buildApp(server); - -server.listen( - { - port: 8080, - host: '0.0.0.0', - }, - (err: Error | null, address: string) => { - if (err) { - console.error(err); - process.exit(1); - } if (isLocalRun()) { // when we're running against keycloak, 0.0.0.0 is not allowed // so suggesting to use whitelisted localhost instead - console.log('Server listening at http://localhost:8080/'); + server.log.info('Server listening at http://localhost:8080/'); } else { - console.log(`Server listening at ${address}`); + server.log.info(`Server listening at ${address}`); } - }, -); + } catch (e) { + server.log.fatal(e); + process.exit(1); + } + + await server.ready(); -server.ready(() => { - console.log( + server.log.info( server.printRoutes({ includeMeta: false, commonPrefix: false, includeHooks: false, }), ); -}); + + // set initial log level and watch for changes + watchLogLevel(server); +})(); diff --git a/packages/dashboard-backend/src/services/kubeclient/__mocks__/kubeConfigProvider.ts b/packages/dashboard-backend/src/services/kubeclient/__mocks__/kubeConfigProvider.ts index 63c450805..bfe716d0a 100644 --- a/packages/dashboard-backend/src/services/kubeclient/__mocks__/kubeConfigProvider.ts +++ b/packages/dashboard-backend/src/services/kubeclient/__mocks__/kubeConfigProvider.ts @@ -14,16 +14,13 @@ import * as k8s from '@kubernetes/client-node'; -const { KubeConfig } = k8s; -const config = new KubeConfig(); +export const config = {} as k8s.KubeConfig; config.makeApiClient = jest.fn(); export function KubeConfigProvider() { return { getSAKubeConfig: () => config, - getKubeConfig: (_token: string) => { - return new KubeConfig(); - }, + getKubeConfig: (_token: string) => config, inClusterKubeConfig: config, }; } diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/helpers/__mocks__/prepareCoreV1API.ts b/packages/dashboard-backend/src/services/logWatcher/__mock__/index.ts similarity index 53% rename from packages/dashboard-backend/src/devworkspaceClient/services/helpers/__mocks__/prepareCoreV1API.ts rename to packages/dashboard-backend/src/services/logWatcher/__mock__/index.ts index 73de8ba21..8a9c3aef8 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/helpers/__mocks__/prepareCoreV1API.ts +++ b/packages/dashboard-backend/src/services/logWatcher/__mock__/index.ts @@ -13,18 +13,19 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import * as k8s from '@kubernetes/client-node'; -import * as request from 'request'; +import { FastifyInstance } from 'fastify'; -export function prepareCoreV1API(_kc: k8s.KubeConfig): k8s.Watch { - const watch = async (..._args: Parameters) => { - return { - body: {}, - destroy: () => { - /* no-op */ - }, - } as request.Request; - }; - return { - watch: (...args: Parameters) => watch(...args), - } as k8s.Watch; +export async function watchLogLevel(_server: FastifyInstance): Promise { + // no-op +} + +export async function readCR(_kubeConfig: k8s.KubeConfig, _server: FastifyInstance): Promise { + // no-op +} + +export async function watchCR( + _kubeConfig: k8s.KubeConfig, + _server: FastifyInstance, +): Promise { + // no-op } diff --git a/packages/dashboard-backend/src/services/logWatcher/__tests__/index.spec.ts b/packages/dashboard-backend/src/services/logWatcher/__tests__/index.spec.ts new file mode 100644 index 000000000..45740eb50 --- /dev/null +++ b/packages/dashboard-backend/src/services/logWatcher/__tests__/index.spec.ts @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2018-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ + +import * as k8s from '@kubernetes/client-node'; +import { FastifyInstance } from 'fastify'; + +import { CheClusterCustomResource } from '@/devworkspaceClient'; + +import { watchLogLevel } from '..'; + +const name = 'user-che'; +const namespace = 'che-namespace'; + +let watchCallback: Parameters[2]; +const mockWatch = jest.fn().mockImplementation((...args: Parameters) => { + const [, , _callback] = args; + watchCallback = _callback; + return Promise.resolve({ + on: jest.fn(), + }) as ReturnType; +}); + +jest.mock('@/devworkspaceClient/services/helpers/prepareCustomObjectWatch', () => { + return { + prepareCustomObjectWatch: jest.fn().mockImplementation(() => { + return { + watch: jest.fn().mockImplementation(mockWatch), + }; + }), + }; +}); +jest.mock('@/devworkspaceClient', () => { + const requireActual = jest.requireActual('@/devworkspaceClient'); + return { + ...requireActual, + isCheClusterCustomResource: jest.fn().mockImplementation(() => true), + }; +}); +jest.mock('@/routes/api/helpers/getServiceAccountToken'); + +const mockUpdateLogLevel = jest.fn(); +jest.mock('@/utils/logger', () => { + const originalLogger = jest.requireActual('@/utils/logger'); + return { + ...originalLogger, + updateLogLevel: (...args: unknown[]) => { + mockUpdateLogLevel(...args); + }, + }; +}); + +describe('watchLogLevel', () => { + beforeEach(() => { + const env = { CHECLUSTER_CR_NAME: name, CHECLUSTER_CR_NAMESPACE: namespace }; + (process as any).env = env; + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should watch the CR and update the log level', async () => { + const server = {} as FastifyInstance; + + await watchLogLevel(server); + + expect(mockWatch).toBeCalledWith( + `/apis/org.eclipse.che/v2/namespaces/${namespace}/checlusters`, + { watch: true }, + expect.any(Function), + expect.any(Function), + ); + + watchCallback('ADDED', { + spec: { + components: { + dashboard: { + logLevel: 'DEBUG', + }, + }, + }, + } as CheClusterCustomResource); + + expect(mockUpdateLogLevel).toBeCalledWith('DEBUG', server); + }); +}); diff --git a/packages/dashboard-backend/src/services/logWatcher/index.ts b/packages/dashboard-backend/src/services/logWatcher/index.ts new file mode 100644 index 000000000..417a35723 --- /dev/null +++ b/packages/dashboard-backend/src/services/logWatcher/index.ts @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ + +import * as k8s from '@kubernetes/client-node'; +import { FastifyInstance } from 'fastify'; + +import { isCheClusterCustomResource } from '@/devworkspaceClient'; +import { prepareCustomObjectWatch } from '@/devworkspaceClient/services/helpers/prepareCustomObjectWatch'; +import { getServiceAccountToken } from '@/routes/api/helpers/getServiceAccountToken'; +import { KubeConfigProvider } from '@/services/kubeclient/kubeConfigProvider'; +import { logger, updateLogLevel } from '@/utils/logger'; + +const GROUP = 'org.eclipse.che'; +const VERSION = 'v2'; +const PLURAL = 'checlusters'; + +export async function watchLogLevel(server: FastifyInstance) { + const token = getServiceAccountToken(); + const kubeConfigProvider = new KubeConfigProvider(); + const kubeConfig = kubeConfigProvider.getKubeConfig(token); + + watchCR(kubeConfig, server); +} + +// Watch for changes to the Che Cluster Custom Resource object +// and update the log level accordingly +export async function watchCR(kubeConfig: k8s.KubeConfig, server: FastifyInstance) { + const env = getEnv(); + if (env.CHECLUSTER_CR_NAMESPACE === undefined) { + logger.error( + 'Log level watcher: Environment variable is not defined: $CHECLUSTER_CR_NAMESPACE', + ); + return; + } + + const customObjectWatch = prepareCustomObjectWatch(kubeConfig); + + // Watch for changes to the Che Cluster Custom Resource object + const stream = await customObjectWatch.watch( + `/apis/${GROUP}/${VERSION}/namespaces/${env.CHECLUSTER_CR_NAMESPACE}/${PLURAL}`, + { watch: true }, + (type, apiObj) => { + if (isCheClusterCustomResource(apiObj)) { + const logLevel = apiObj.spec.components?.dashboard?.logLevel; + if (logLevel !== undefined) { + updateLogLevel(logLevel, server); + } + } + }, + err => { + logger.error(err, 'Log level watcher: Watch failed.'); + }, + ); + + stream.on('close', () => { + logger.error('Log level watcher: Stream closed.'); + }); +} + +function getEnv(): { + CHECLUSTER_CR_NAME: string | undefined; + CHECLUSTER_CR_NAMESPACE: string | undefined; +} { + return { + CHECLUSTER_CR_NAME: process.env.CHECLUSTER_CR_NAME, + CHECLUSTER_CR_NAMESPACE: process.env.CHECLUSTER_CR_NAMESPACE, + }; +} diff --git a/packages/dashboard-backend/src/utils/__mocks__/logger.ts b/packages/dashboard-backend/src/utils/__mocks__/logger.ts new file mode 100644 index 000000000..097dbe11d --- /dev/null +++ b/packages/dashboard-backend/src/utils/__mocks__/logger.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ + +export const logger = { + trace: jest.fn(), + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + fatal: jest.fn(), + silent: jest.fn(), +}; diff --git a/packages/dashboard-backend/src/helpers/tests/appBuilder.ts b/packages/dashboard-backend/src/utils/appBuilder.ts similarity index 100% rename from packages/dashboard-backend/src/helpers/tests/appBuilder.ts rename to packages/dashboard-backend/src/utils/appBuilder.ts diff --git a/packages/dashboard-backend/src/utils/logger.ts b/packages/dashboard-backend/src/utils/logger.ts new file mode 100644 index 000000000..b3ae92717 --- /dev/null +++ b/packages/dashboard-backend/src/utils/logger.ts @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ + +import { FastifyInstance } from 'fastify'; +import { LevelWithSilent, pino } from 'pino'; +import pretty from 'pino-pretty'; + +export const stream = pretty({ + levelFirst: true, + colorize: true, + ignore: 'pid,hostname', + translateTime: 'HH:MM:ss Z', + // singleLine: true, +}); + +export const logger = pino(stream); + +const logLevels = [ + 'debug', + 'error', + 'fatal', + 'info', + 'silent', + 'trace', + 'warn', +] as LevelWithSilent[]; + +export function updateLogLevel(logLevel: LevelWithSilent, server: FastifyInstance): void; +export function updateLogLevel(logLevel: string, server: FastifyInstance): void; +export function updateLogLevel(logLevel: LevelWithSilent | string, server: FastifyInstance): void { + const level = logLevel.toLowerCase(); + if (isLevelWithSilent(level)) { + console.log('[logger] logLevel:', logLevel); + logger.level = level; + server.log.level = level; + } +} + +function isLevelWithSilent(logLevel: string): logLevel is LevelWithSilent { + return logLevels.includes(logLevel as LevelWithSilent); +} diff --git a/packages/dashboard-backend/webpack.config.common.js b/packages/dashboard-backend/webpack.config.common.js index 991c7cd34..b7f08900b 100644 --- a/packages/dashboard-backend/webpack.config.common.js +++ b/packages/dashboard-backend/webpack.config.common.js @@ -84,6 +84,5 @@ module.exports = () => { __dirname: false, }, target: 'node', - externals: ['long', 'pino-pretty'], }; }; diff --git a/yarn.lock b/yarn.lock index d08d91480..514343e70 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3571,7 +3571,7 @@ colord@^2.9.1, colord@^2.9.3: resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== -colorette@^2.0.14: +colorette@^2.0.14, colorette@^2.0.7: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== @@ -4106,6 +4106,11 @@ dateformat@^3.0.0: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== +dateformat@^4.6.3: + version "4.6.3" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.6.3.tgz#556fa6497e5217fedb78821424f8a1c22fa3f4b5" + integrity sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA== + debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" @@ -5079,6 +5084,11 @@ fast-content-type-parse@^1.0.0: resolved "https://registry.yarnpkg.com/fast-content-type-parse/-/fast-content-type-parse-1.0.0.tgz#cddce00df7d7efb3727d375a598e4904bfcb751c" integrity sha512-Xbc4XcysUXcsP5aHUU7Nq3OwvHq97C+WnbkeIefpeYLX+ryzFJlU6OStFJhs6Ol0LkUGpcK+wL0JwfM+FCU5IA== +fast-copy@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/fast-copy/-/fast-copy-3.0.1.tgz#9e89ef498b8c04c1cd76b33b8e14271658a732aa" + integrity sha512-Knr7NOtK3HWRYGtHoJrjkaWepqT8thIVGAwt0p0aUs1zqkAzXZV4vo9fFNwyb5fcqK1GKYFYxldQdIDVKhUAfA== + fast-decode-uri-component@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz#46f8b6c22b30ff7a81357d4f59abfae938202543" @@ -5150,6 +5160,11 @@ fast-redact@^3.1.1: resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.3.0.tgz#7c83ce3a7be4898241a46560d51de10f653f7634" integrity sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ== +fast-safe-stringify@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" + integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== + fast-uri@^2.0.0, fast-uri@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-2.2.0.tgz#519a0f849bef714aad10e9753d69d8f758f7445a" @@ -5676,7 +5691,7 @@ glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.0.1: +glob@^8.0.0, glob@^8.0.1: version "8.1.0" resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== @@ -5936,6 +5951,14 @@ he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +help-me@^4.0.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/help-me/-/help-me-4.2.0.tgz#50712bfd799ff1854ae1d312c36eafcea85b0563" + integrity sha512-TAOnTB8Tz5Dw8penUuzHVrKNKlCIbwwbHnXraNJxPwf8LRtE2HlM84RYuezMFcwOJmoYOCWVDyJ8TQGxn9PgxA== + dependencies: + glob "^8.0.0" + readable-stream "^3.6.0" + history@^4.10.1, history@^4.9.0: version "4.10.1" resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" @@ -7248,6 +7271,11 @@ jose@^4.14.4: resolved "https://registry.yarnpkg.com/jose/-/jose-4.14.4.tgz#59e09204e2670c3164ee24cbfe7115c6f8bff9ca" integrity sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g== +joycon@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" + integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -9188,6 +9216,14 @@ pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== +pino-abstract-transport@^1.0.0, pino-abstract-transport@v1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.1.0.tgz#083d98f966262164504afb989bccd05f665937a8" + integrity sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA== + dependencies: + readable-stream "^4.0.0" + split2 "^4.0.0" + pino-abstract-transport@v1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz#cc0d6955fffcadb91b7b49ef220a6cc111d48bb3" @@ -9196,6 +9232,26 @@ pino-abstract-transport@v1.0.0: readable-stream "^4.0.0" split2 "^4.0.0" +pino-pretty@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-10.2.0.tgz#c674a153e15c08d7032a826d0051d786feace1d9" + integrity sha512-tRvpyEmGtc2D+Lr3FulIZ+R1baggQ4S3xD2Ar93KixFEDx6SEAUP3W5aYuEw1C73d6ROrNcB2IXLteW8itlwhA== + dependencies: + colorette "^2.0.7" + dateformat "^4.6.3" + fast-copy "^3.0.0" + fast-safe-stringify "^2.1.1" + help-me "^4.0.1" + joycon "^3.1.1" + minimist "^1.2.6" + on-exit-leak-free "^2.1.0" + pino-abstract-transport "^1.0.0" + pump "^3.0.0" + readable-stream "^4.0.0" + secure-json-parse "^2.4.0" + sonic-boom "^3.0.0" + strip-json-comments "^3.1.1" + pino-std-serializers@^6.0.0: version "6.2.2" resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz#d9a9b5f2b9a402486a5fc4db0a737570a860aab3" @@ -9218,6 +9274,23 @@ pino@^8.12.0: sonic-boom "^3.1.0" thread-stream "^2.0.0" +pino@^8.15.1: + version "8.15.1" + resolved "https://registry.yarnpkg.com/pino/-/pino-8.15.1.tgz#04b815ff7aa4e46b1bbab88d8010aaa2b17eaba4" + integrity sha512-Cp4QzUQrvWCRJaQ8Lzv0mJzXVk4z2jlq8JNKMGaixC2Pz5L4l2p95TkuRvYbrEbe85NQsDKrAd4zalf7Ml6WiA== + dependencies: + atomic-sleep "^1.0.0" + fast-redact "^3.1.1" + on-exit-leak-free "^2.1.0" + pino-abstract-transport v1.1.0 + pino-std-serializers "^6.0.0" + process-warning "^2.0.0" + quick-format-unescaped "^4.0.3" + real-require "^0.2.0" + safe-stable-stringify "^2.3.1" + sonic-boom "^3.1.0" + thread-stream "^2.0.0" + pirates@^4.0.4: version "4.0.6" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" @@ -10508,7 +10581,7 @@ seamless-immutable@^7.1.3: resolved "https://registry.yarnpkg.com/seamless-immutable/-/seamless-immutable-7.1.4.tgz#6e9536def083ddc4dea0207d722e0e80d0f372f8" integrity sha512-XiUO1QP4ki4E2PHegiGAlu6r82o5A+6tRh7IkGGTVg/h+UoeX4nFBeCGPOhb4CYjvkqsfm/TUtvOMYC1xmV30A== -secure-json-parse@^2.5.0: +secure-json-parse@^2.4.0, secure-json-parse@^2.5.0: version "2.7.0" resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== @@ -10709,7 +10782,7 @@ socks@^2.6.2: ip "^2.0.0" smart-buffer "^4.2.0" -sonic-boom@^3.1.0: +sonic-boom@^3.0.0, sonic-boom@^3.1.0: version "3.3.0" resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-3.3.0.tgz#cffab6dafee3b2bcb88d08d589394198bee1838c" integrity sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==