@milaboratories/pl-middle-layer 1.55.1 → 1.55.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_virtual/{_@oxc-project_runtime@0.114.0 → _@oxc-project_runtime@0.123.0}/helpers/usingCtx.cjs +2 -4
- package/dist/_virtual/{_@oxc-project_runtime@0.114.0 → _@oxc-project_runtime@0.123.0}/helpers/usingCtx.js +2 -3
- package/dist/_virtual/_rolldown/runtime.cjs +7 -13
- package/dist/block_registry/index.cjs +3 -3
- package/dist/block_registry/index.js +3 -3
- package/dist/block_registry/registry-v2-provider.cjs +2 -3
- package/dist/block_registry/registry-v2-provider.cjs.map +1 -1
- package/dist/block_registry/registry-v2-provider.d.ts.map +1 -0
- package/dist/block_registry/registry-v2-provider.js +1 -2
- package/dist/block_registry/registry-v2-provider.js.map +1 -1
- package/dist/block_registry/registry.cjs +3 -4
- package/dist/block_registry/registry.cjs.map +1 -1
- package/dist/block_registry/registry.d.ts.map +1 -0
- package/dist/block_registry/registry.js +1 -2
- package/dist/block_registry/registry.js.map +1 -1
- package/dist/block_registry/watcher.cjs +3 -4
- package/dist/block_registry/watcher.cjs.map +1 -1
- package/dist/block_registry/watcher.d.ts.map +1 -0
- package/dist/block_registry/watcher.js +1 -2
- package/dist/block_registry/watcher.js.map +1 -1
- package/dist/block_registry/well_known_registries.cjs +1 -2
- package/dist/block_registry/well_known_registries.cjs.map +1 -1
- package/dist/block_registry/well_known_registries.d.ts.map +1 -0
- package/dist/block_registry/well_known_registries.js +1 -1
- package/dist/cfg_render/executor.cjs +4 -5
- package/dist/cfg_render/executor.cjs.map +1 -1
- package/dist/cfg_render/executor.js +1 -2
- package/dist/cfg_render/executor.js.map +1 -1
- package/dist/cfg_render/renderer.cjs +4 -5
- package/dist/cfg_render/renderer.cjs.map +1 -1
- package/dist/cfg_render/renderer.js +1 -2
- package/dist/cfg_render/renderer.js.map +1 -1
- package/dist/cfg_render/traverse.cjs +2 -3
- package/dist/cfg_render/traverse.cjs.map +1 -1
- package/dist/cfg_render/traverse.js +1 -2
- package/dist/cfg_render/traverse.js.map +1 -1
- package/dist/cfg_render/util.cjs +2 -3
- package/dist/cfg_render/util.cjs.map +1 -1
- package/dist/cfg_render/util.js +1 -2
- package/dist/cfg_render/util.js.map +1 -1
- package/dist/debug/index.cjs +1 -2
- package/dist/debug/index.cjs.map +1 -1
- package/dist/debug/index.js +1 -1
- package/dist/debug/index.js.map +1 -1
- package/dist/dev_env/index.cjs +3 -4
- package/dist/dev_env/index.cjs.map +1 -1
- package/dist/dev_env/index.js +1 -2
- package/dist/dev_env/index.js.map +1 -1
- package/dist/dev_env/util.cjs +2 -3
- package/dist/dev_env/util.cjs.map +1 -1
- package/dist/dev_env/util.js +1 -2
- package/dist/dev_env/util.js.map +1 -1
- package/dist/index.cjs +48 -44
- package/dist/index.d.ts +0 -3
- package/dist/index.js +5 -10
- package/dist/js_render/computable_context.cjs +5 -6
- package/dist/js_render/computable_context.cjs.map +1 -1
- package/dist/js_render/computable_context.js +2 -3
- package/dist/js_render/computable_context.js.map +1 -1
- package/dist/js_render/context.cjs +3 -4
- package/dist/js_render/context.cjs.map +1 -1
- package/dist/js_render/context.js +1 -2
- package/dist/js_render/context.js.map +1 -1
- package/dist/js_render/index.cjs +4 -5
- package/dist/js_render/index.cjs.map +1 -1
- package/dist/js_render/index.js +1 -2
- package/dist/js_render/index.js.map +1 -1
- package/dist/js_render/service_injectors.cjs +3 -4
- package/dist/js_render/service_injectors.cjs.map +1 -1
- package/dist/js_render/service_injectors.js +2 -3
- package/dist/js_render/service_injectors.js.map +1 -1
- package/dist/middle_layer/active_cfg.cjs +7 -8
- package/dist/middle_layer/active_cfg.cjs.map +1 -1
- package/dist/middle_layer/active_cfg.js +1 -2
- package/dist/middle_layer/active_cfg.js.map +1 -1
- package/dist/middle_layer/block.cjs +8 -9
- package/dist/middle_layer/block.cjs.map +1 -1
- package/dist/middle_layer/block.js +1 -2
- package/dist/middle_layer/block.js.map +1 -1
- package/dist/middle_layer/block_ctx.cjs +5 -6
- package/dist/middle_layer/block_ctx.cjs.map +1 -1
- package/dist/middle_layer/block_ctx.js +1 -2
- package/dist/middle_layer/block_ctx.js.map +1 -1
- package/dist/middle_layer/block_ctx_unsafe.cjs +1 -2
- package/dist/middle_layer/block_ctx_unsafe.cjs.map +1 -1
- package/dist/middle_layer/block_ctx_unsafe.js +1 -1
- package/dist/middle_layer/block_ctx_unsafe.js.map +1 -1
- package/dist/middle_layer/driver_kit.cjs +5 -6
- package/dist/middle_layer/driver_kit.cjs.map +1 -1
- package/dist/middle_layer/driver_kit.d.ts +0 -1
- package/dist/middle_layer/driver_kit.d.ts.map +1 -0
- package/dist/middle_layer/driver_kit.js +1 -2
- package/dist/middle_layer/driver_kit.js.map +1 -1
- package/dist/middle_layer/frontend_path.cjs +6 -7
- package/dist/middle_layer/frontend_path.cjs.map +1 -1
- package/dist/middle_layer/frontend_path.js +1 -2
- package/dist/middle_layer/frontend_path.js.map +1 -1
- package/dist/middle_layer/index.cjs +5 -5
- package/dist/middle_layer/index.js +5 -5
- package/dist/middle_layer/middle_layer.cjs +15 -16
- package/dist/middle_layer/middle_layer.cjs.map +1 -1
- package/dist/middle_layer/middle_layer.d.ts +0 -2
- package/dist/middle_layer/middle_layer.d.ts.map +1 -0
- package/dist/middle_layer/middle_layer.js +1 -2
- package/dist/middle_layer/middle_layer.js.map +1 -1
- package/dist/middle_layer/navigation_states.cjs +2 -3
- package/dist/middle_layer/navigation_states.cjs.map +1 -1
- package/dist/middle_layer/navigation_states.js +1 -2
- package/dist/middle_layer/navigation_states.js.map +1 -1
- package/dist/middle_layer/ops.cjs +5 -7
- package/dist/middle_layer/ops.cjs.map +1 -1
- package/dist/middle_layer/ops.d.ts +0 -1
- package/dist/middle_layer/ops.d.ts.map +1 -0
- package/dist/middle_layer/ops.js +1 -2
- package/dist/middle_layer/ops.js.map +1 -1
- package/dist/middle_layer/project.cjs +12 -13
- package/dist/middle_layer/project.cjs.map +1 -1
- package/dist/middle_layer/project.d.ts +0 -1
- package/dist/middle_layer/project.d.ts.map +1 -0
- package/dist/middle_layer/project.js +1 -2
- package/dist/middle_layer/project.js.map +1 -1
- package/dist/middle_layer/project_list.cjs +3 -4
- package/dist/middle_layer/project_list.cjs.map +1 -1
- package/dist/middle_layer/project_list.d.ts +0 -4
- package/dist/middle_layer/project_list.d.ts.map +1 -0
- package/dist/middle_layer/project_list.js +1 -2
- package/dist/middle_layer/project_list.js.map +1 -1
- package/dist/middle_layer/project_overview.cjs +9 -10
- package/dist/middle_layer/project_overview.cjs.map +1 -1
- package/dist/middle_layer/project_overview.js +1 -2
- package/dist/middle_layer/project_overview.js.map +1 -1
- package/dist/middle_layer/project_overview_light.cjs +4 -5
- package/dist/middle_layer/project_overview_light.cjs.map +1 -1
- package/dist/middle_layer/project_overview_light.js +1 -2
- package/dist/middle_layer/project_overview_light.js.map +1 -1
- package/dist/middle_layer/render.cjs +5 -6
- package/dist/middle_layer/render.cjs.map +1 -1
- package/dist/middle_layer/render.js +1 -2
- package/dist/middle_layer/render.js.map +1 -1
- package/dist/middle_layer/types.d.ts.map +1 -0
- package/dist/middle_layer/util.cjs +4 -5
- package/dist/middle_layer/util.cjs.map +1 -1
- package/dist/middle_layer/util.js +1 -2
- package/dist/middle_layer/util.js.map +1 -1
- package/dist/model/args.cjs +2 -3
- package/dist/model/args.cjs.map +1 -1
- package/dist/model/args.js +1 -2
- package/dist/model/args.js.map +1 -1
- package/dist/model/block_pack_spec.cjs +1 -2
- package/dist/model/block_pack_spec.cjs.map +1 -1
- package/dist/model/block_pack_spec.d.ts.map +1 -0
- package/dist/model/block_pack_spec.js +1 -1
- package/dist/model/frontend.d.ts.map +1 -0
- package/dist/model/project_helper.cjs +3 -4
- package/dist/model/project_helper.cjs.map +1 -1
- package/dist/model/project_helper.d.ts.map +1 -0
- package/dist/model/project_helper.js +1 -2
- package/dist/model/project_helper.js.map +1 -1
- package/dist/model/project_model.cjs +1 -6
- package/dist/model/project_model.cjs.map +1 -1
- package/dist/model/project_model.d.ts +0 -1
- package/dist/model/project_model.d.ts.map +1 -0
- package/dist/model/project_model.js +2 -4
- package/dist/model/project_model.js.map +1 -1
- package/dist/model/project_model_util.cjs +2 -3
- package/dist/model/project_model_util.cjs.map +1 -1
- package/dist/model/project_model_util.js +1 -2
- package/dist/model/project_model_util.js.map +1 -1
- package/dist/model/project_model_v1.cjs +1 -5
- package/dist/model/project_model_v1.cjs.map +1 -1
- package/dist/model/project_model_v1.js +2 -4
- package/dist/model/project_model_v1.js.map +1 -1
- package/dist/model/template_spec.d.ts.map +1 -0
- package/dist/mutator/block-pack/block_pack.cjs +9 -10
- package/dist/mutator/block-pack/block_pack.cjs.map +1 -1
- package/dist/mutator/block-pack/block_pack.d.ts +0 -1
- package/dist/mutator/block-pack/block_pack.d.ts.map +1 -0
- package/dist/mutator/block-pack/block_pack.js +2 -3
- package/dist/mutator/block-pack/block_pack.js.map +1 -1
- package/dist/mutator/block-pack/frontend.cjs +3 -4
- package/dist/mutator/block-pack/frontend.cjs.map +1 -1
- package/dist/mutator/block-pack/frontend.js +1 -2
- package/dist/mutator/block-pack/frontend.js.map +1 -1
- package/dist/mutator/context_export.cjs +4 -5
- package/dist/mutator/context_export.cjs.map +1 -1
- package/dist/mutator/context_export.js +1 -2
- package/dist/mutator/context_export.js.map +1 -1
- package/dist/mutator/migration.cjs +13 -14
- package/dist/mutator/migration.cjs.map +1 -1
- package/dist/mutator/migration.js +11 -12
- package/dist/mutator/migration.js.map +1 -1
- package/dist/mutator/project.cjs +13 -14
- package/dist/mutator/project.cjs.map +1 -1
- package/dist/mutator/project.d.ts +0 -3
- package/dist/mutator/project.d.ts.map +1 -0
- package/dist/mutator/project.js +6 -7
- package/dist/mutator/project.js.map +1 -1
- package/dist/mutator/template/direct_template_loader.cjs +3 -4
- package/dist/mutator/template/direct_template_loader.cjs.map +1 -1
- package/dist/mutator/template/direct_template_loader.js +1 -2
- package/dist/mutator/template/direct_template_loader.js.map +1 -1
- package/dist/mutator/template/direct_template_loader_v3.cjs +2 -3
- package/dist/mutator/template/direct_template_loader_v3.cjs.map +1 -1
- package/dist/mutator/template/direct_template_loader_v3.js +1 -2
- package/dist/mutator/template/direct_template_loader_v3.js.map +1 -1
- package/dist/mutator/template/render_block.cjs +4 -6
- package/dist/mutator/template/render_block.cjs.map +1 -1
- package/dist/mutator/template/render_block.js +2 -4
- package/dist/mutator/template/render_block.js.map +1 -1
- package/dist/mutator/template/render_template.cjs +2 -3
- package/dist/mutator/template/render_template.cjs.map +1 -1
- package/dist/mutator/template/render_template.d.ts.map +1 -0
- package/dist/mutator/template/render_template.js +1 -2
- package/dist/mutator/template/render_template.js.map +1 -1
- package/dist/mutator/template/template_cache.cjs +6 -10
- package/dist/mutator/template/template_cache.cjs.map +1 -1
- package/dist/mutator/template/template_cache.d.ts +2 -3
- package/dist/mutator/template/template_cache.d.ts.map +1 -0
- package/dist/mutator/template/template_cache.js +4 -8
- package/dist/mutator/template/template_cache.js.map +1 -1
- package/dist/mutator/template/template_loading.cjs +3 -4
- package/dist/mutator/template/template_loading.cjs.map +1 -1
- package/dist/mutator/template/template_loading.d.ts.map +1 -0
- package/dist/mutator/template/template_loading.js +1 -2
- package/dist/mutator/template/template_loading.js.map +1 -1
- package/dist/network_check/network_check.cjs +4 -5
- package/dist/network_check/network_check.cjs.map +1 -1
- package/dist/network_check/network_check.d.ts.map +1 -0
- package/dist/network_check/network_check.js +1 -2
- package/dist/network_check/network_check.js.map +1 -1
- package/dist/network_check/pings.cjs +2 -3
- package/dist/network_check/pings.cjs.map +1 -1
- package/dist/network_check/pings.js +1 -2
- package/dist/network_check/pings.js.map +1 -1
- package/dist/network_check/template.cjs +4 -5
- package/dist/network_check/template.cjs.map +1 -1
- package/dist/network_check/template.js +1 -2
- package/dist/network_check/template.js.map +1 -1
- package/dist/pool/data.cjs +2 -3
- package/dist/pool/data.cjs.map +1 -1
- package/dist/pool/data.d.ts +0 -1
- package/dist/pool/data.d.ts.map +1 -0
- package/dist/pool/data.js +1 -2
- package/dist/pool/data.js.map +1 -1
- package/dist/pool/driver.cjs +3 -4
- package/dist/pool/driver.cjs.map +1 -1
- package/dist/pool/driver.d.ts +0 -1
- package/dist/pool/driver.d.ts.map +1 -0
- package/dist/pool/driver.js +1 -2
- package/dist/pool/driver.js.map +1 -1
- package/dist/pool/index.cjs +2 -2
- package/dist/pool/index.d.ts +1 -2
- package/dist/pool/index.js +2 -2
- package/dist/pool/p_object_collection.cjs +3 -4
- package/dist/pool/p_object_collection.cjs.map +1 -1
- package/dist/pool/p_object_collection.d.ts.map +1 -0
- package/dist/pool/p_object_collection.js +1 -2
- package/dist/pool/p_object_collection.js.map +1 -1
- package/dist/pool/result_pool.cjs +7 -8
- package/dist/pool/result_pool.cjs.map +1 -1
- package/dist/pool/result_pool.d.ts +1 -3
- package/dist/pool/result_pool.js +1 -2
- package/dist/pool/result_pool.js.map +1 -1
- package/dist/service_factories.cjs +2 -3
- package/dist/service_factories.cjs.map +1 -1
- package/dist/service_factories.js +1 -2
- package/dist/service_factories.js.map +1 -1
- package/dist/worker/WorkerManager.cjs +2 -3
- package/dist/worker/WorkerManager.cjs.map +1 -1
- package/dist/worker/WorkerManager.js +1 -2
- package/dist/worker/WorkerManager.js.map +1 -1
- package/dist/worker/worker.cjs +3 -4
- package/dist/worker/worker.cjs.map +1 -1
- package/dist/worker/worker.js +1 -2
- package/dist/worker/worker.js.map +1 -1
- package/dist/worker/workerApi.cjs +2 -3
- package/dist/worker/workerApi.cjs.map +1 -1
- package/dist/worker/workerApi.js +1 -2
- package/dist/worker/workerApi.js.map +1 -1
- package/package.json +15 -15
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"network_check.cjs","names":["createTempFile","createBigTempFile","backendPings","blockRegistryOverviewPings","blockGARegistryOverviewPings","blockRegistryUiPings","blockGARegistryUiPings","autoUpdateCdnPings","uploadTemplate","uploadFile","downloadFile","softwareCheck","pythonSoftware","downloadFromEveryStorage","UnauthenticatedPlClient","PlClient","ConsoleLoggerAdapter","HmacSha256Signer","LsDriver","reportToString"],"sources":["../../src/network_check/network_check.ts"],"sourcesContent":["/** A utility to check network problems and gather statistics.\n * It's useful when we cannot connect to the server of a company\n * because of security reasons,\n * but they can send us and their DevOps team this report.\n *\n * What we check:\n * - pings to backend\n * - block registry for block overview and ui.\n * - autoupdate CDN.\n * - upload workflow to backend (workflow part via our API).\n * - the desktop could do multipart upload.\n * - the desktop could download files from S3.\n * - backend could download software and run it.\n * - backend could run python software.\n * - try to get something from every storage to work storage.\n *\n * We don't check backend access to S3 storage, it is checked on the start of backend.\n */\n\nimport type { AuthInformation, PlClientConfig } from \"@milaboratories/pl-client\";\nimport { PlClient, UnauthenticatedPlClient, plAddressToConfig } from \"@milaboratories/pl-client\";\nimport type { MiLogger, Signer } from \"@milaboratories/ts-helpers\";\nimport { ConsoleLoggerAdapter, HmacSha256Signer } from \"@milaboratories/ts-helpers\";\nimport { channel } from \"node:diagnostics_channel\";\nimport type { ClientDownload, ClientUpload } from \"@milaboratories/pl-drivers\";\nimport { LsDriver, createDownloadClient, createUploadBlobClient } from \"@milaboratories/pl-drivers\";\nimport type { HttpNetworkReport, NetworkReport } from \"./pings\";\nimport {\n autoUpdateCdnPings,\n backendPings,\n blockGARegistryOverviewPings,\n blockGARegistryUiPings,\n blockRegistryOverviewPings,\n blockRegistryUiPings,\n reportToString,\n} from \"./pings\";\nimport type { Dispatcher } from \"undici\";\nimport type { TemplateReport } from \"./template\";\nimport {\n uploadTemplate,\n uploadFile,\n downloadFile,\n createTempFile,\n pythonSoftware,\n softwareCheck,\n createBigTempFile,\n downloadFromEveryStorage,\n} from \"./template\";\nimport { randomUUID } from \"node:crypto\";\n\n/** All reports we need to collect. */\ninterface NetworkReports {\n plPings: NetworkReport<string>[];\n\n blockRegistryOverviewChecks: HttpNetworkReport[];\n blockGARegistryOverviewChecks: HttpNetworkReport[];\n blockRegistryUiChecks: HttpNetworkReport[];\n blockGARegistryUiChecks: HttpNetworkReport[];\n\n autoUpdateCdnChecks: HttpNetworkReport[];\n\n uploadTemplateCheck: TemplateReport;\n uploadFileCheck: TemplateReport;\n downloadFileCheck: TemplateReport;\n softwareCheck: TemplateReport;\n pythonSoftwareCheck: TemplateReport;\n storageToDownloadReport: Record<string, TemplateReport>;\n}\n\nexport interface CheckNetworkOpts {\n /** Signal to abort all network checks. */\n signal?: AbortSignal;\n\n /** Platforma Backend pings options. */\n pingCheckDurationMs: number;\n pingTimeoutMs: number;\n maxPingsPerSecond: number;\n\n /** An options for CDN and block registry. */\n httpTimeoutMs: number;\n\n /** Block registry pings options. */\n blockRegistryDurationMs: number;\n maxRegistryChecksPerSecond: number;\n blockRegistryUrl: string;\n blockGARegistryUrl: string;\n blockOverviewPath: string;\n blockUiPath: string;\n\n /** CDN for auto-update pings options. */\n autoUpdateCdnDurationMs: number;\n maxAutoUpdateCdnChecksPerSecond: number;\n autoUpdateCdnUrl: string;\n\n /** Body limit for requests. */\n bodyLimit: number;\n\n /** Limit for the size of files to download from every storage. */\n everyStorageBytesLimit: number;\n /** Minimal size of files to create a directory from for every storage. */\n everyStorageMinFileSize: number;\n /** Maximal size of files to create a directory from for every storage. */\n everyStorageMaxFileSize: number;\n /** How many files to check from every storage. */\n everyStorageNFilesToCheck: number;\n /** Minimal number of ls requests for every storage. */\n everyStorageMinLsRequests: number;\n}\n\n/** Checks connectivity to Platforma Backend, to block registry\n * and to auto-update CDN,\n * and generates a string report. */\nexport async function checkNetwork(\n plCredentials: string,\n plUser: string | undefined,\n plPassword: string | undefined,\n optsOverrides: Partial<CheckNetworkOpts> = {},\n): Promise<string> {\n const undiciLogs: any[] = [];\n // Subscribe to all Undici diagnostic events\n undiciEvents.forEach((event) => {\n const diagnosticChannel = channel(event);\n diagnosticChannel.subscribe((message: any) => {\n const timestamp = new Date().toISOString();\n const data = { ...message };\n if (data?.response?.headers) {\n data.response = { ...data.response };\n data.response.headers = data.response.headers.slice();\n data.response.headers = data.response.headers.map((h: any) => h.toString());\n }\n\n // we try to upload big files, don't include the buffer in the report.\n if (data?.request?.body) {\n data.request = { ...data.request };\n data.request.body = `too big`;\n }\n\n undiciLogs.push(\n JSON.stringify({\n timestamp,\n event,\n data,\n }),\n );\n });\n });\n\n try {\n const {\n logger,\n plConfig,\n client,\n signer,\n downloadClient,\n uploadBlobClient,\n lsDriver,\n httpClient,\n ops,\n } = await initNetworkCheck(plCredentials, plUser, plPassword, optsOverrides);\n\n const { filePath: filePathToDownload, fileContent: fileContentToDownload } =\n await createTempFile();\n const { filePath: filePathToUpload } = await createBigTempFile();\n\n const report: NetworkReports = {\n plPings: await backendPings(ops, plConfig),\n blockRegistryOverviewChecks: await blockRegistryOverviewPings(ops, httpClient),\n blockGARegistryOverviewChecks: await blockGARegistryOverviewPings(ops, httpClient),\n blockRegistryUiChecks: await blockRegistryUiPings(ops, httpClient),\n blockGARegistryUiChecks: await blockGARegistryUiPings(ops, httpClient),\n\n autoUpdateCdnChecks: await autoUpdateCdnPings(ops, httpClient),\n\n uploadTemplateCheck: await uploadTemplate(logger, client, \"Jack\"),\n uploadFileCheck: await uploadFile(\n logger,\n signer,\n lsDriver,\n uploadBlobClient,\n client,\n filePathToUpload,\n ),\n downloadFileCheck: await downloadFile(\n logger,\n client,\n lsDriver,\n uploadBlobClient,\n downloadClient,\n filePathToDownload,\n fileContentToDownload,\n ),\n softwareCheck: await softwareCheck(client),\n pythonSoftwareCheck: await pythonSoftware(client, \"Jack\"),\n storageToDownloadReport: await downloadFromEveryStorage(logger, client, lsDriver, {\n minLsRequests: ops.everyStorageMinLsRequests,\n bytesLimit: ops.everyStorageBytesLimit,\n minFileSize: ops.everyStorageMinFileSize,\n maxFileSize: ops.everyStorageMaxFileSize,\n nFilesToCheck: ops.everyStorageNFilesToCheck,\n }),\n };\n\n return reportsToString(report, plCredentials, ops, undiciLogs);\n } catch (e) {\n return `Unhandled error while checking the network: ${e}`;\n }\n}\n\nexport async function initNetworkCheck(\n plCredentials: string,\n plUser: string | undefined,\n plPassword: string | undefined,\n optsOverrides: Partial<CheckNetworkOpts> = {},\n): Promise<{\n logger: MiLogger;\n plConfig: PlClientConfig;\n signer: Signer;\n client: PlClient;\n downloadClient: ClientDownload;\n uploadBlobClient: ClientUpload;\n lsDriver: LsDriver;\n httpClient: Dispatcher;\n ops: CheckNetworkOpts;\n terminate: () => Promise<void>;\n}> {\n const ops: CheckNetworkOpts = {\n pingCheckDurationMs: 10000,\n pingTimeoutMs: 3000,\n maxPingsPerSecond: 50,\n\n httpTimeoutMs: 3000,\n\n blockRegistryDurationMs: 3000,\n maxRegistryChecksPerSecond: 1,\n\n blockRegistryUrl: \"https://blocks.pl-open.science\",\n blockGARegistryUrl: \"https://blocks-ga.pl-open.science\",\n blockOverviewPath: \"v2/overview.json\",\n blockUiPath: \"v2/milaboratories/samples-and-data/1.7.0/ui.tgz\",\n\n autoUpdateCdnDurationMs: 5000,\n maxAutoUpdateCdnChecksPerSecond: 1,\n autoUpdateCdnUrl:\n \"https://cdn.platforma.bio/software/platforma-desktop-v2/windows/amd64/latest.yml\",\n\n bodyLimit: 300,\n\n everyStorageBytesLimit: 1024,\n everyStorageMinFileSize: 1024,\n everyStorageMaxFileSize: 200 * 1024 * 1024, // 200 MB\n everyStorageNFilesToCheck: 300,\n everyStorageMinLsRequests: 50,\n ...optsOverrides,\n };\n\n const plConfig = plAddressToConfig(plCredentials, {\n defaultRequestTimeout: ops.pingTimeoutMs,\n });\n\n // exposing alternative root for fields not to interfere with\n // projects of the user.\n plConfig.alternativeRoot = `check_network_${randomUUID()}`;\n\n const uaClient = await UnauthenticatedPlClient.build(plConfig);\n\n let auth: AuthInformation = {};\n if (plUser && plPassword) {\n auth = await uaClient.login(plUser, plPassword);\n }\n\n const client = await PlClient.init(plCredentials, { authInformation: auth });\n\n const httpClient = uaClient.ll.httpDispatcher;\n const logger = new ConsoleLoggerAdapter();\n\n // FIXME: do we need to get an actual secret?\n const signer = new HmacSha256Signer(\"localSecret\");\n\n // We could initialize middle-layer here, but for now it seems like an overkill.\n // Here's the code to do it:\n //\n // const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'platforma-network-check-'));\n // const ml = await MiddleLayer.init(client, tmpDir, {\n // logger,\n // localSecret: '',\n // localProjections: [],\n // openFileDialogCallback: () => Promise.resolve([]),\n // preferredUpdateChannel: 'stable',\n // });\n\n const downloadClient = createDownloadClient(logger, client, []);\n const uploadBlobClient = createUploadBlobClient(client, logger);\n\n const lsDriver = await LsDriver.init(logger, client, signer, [], () => Promise.resolve([]), []);\n\n const terminate = async () => {\n downloadClient.close();\n uploadBlobClient.close();\n await httpClient.close();\n await client.close();\n };\n\n return {\n logger,\n plConfig,\n client,\n signer,\n downloadClient,\n uploadBlobClient,\n lsDriver,\n httpClient,\n ops,\n terminate,\n };\n}\n\nfunction reportsToString(\n report: NetworkReports,\n plEndpoint: string,\n opts: CheckNetworkOpts,\n undiciLogs: any[],\n): string {\n const successPings = report.plPings.filter((p) => p.response.ok);\n const failedPings = report.plPings.filter((p) => !p.response.ok);\n const successPingsBodies = [\n ...new Set(successPings.map((p) => JSON.stringify((p.response as any).value))),\n ];\n\n const summary = (ok: boolean) => (ok ? \"OK\" : \"FAILED\");\n const templateSummary = (report: TemplateReport) =>\n report.status === \"ok\" ? \"OK\" : report.status === \"warn\" ? \"WARN\" : \"FAILED\";\n\n const pings = reportToString(report.plPings);\n const blockRegistryOverview = reportToString(report.blockRegistryOverviewChecks);\n const blockGARegistryOverview = reportToString(report.blockGARegistryOverviewChecks);\n const blockRegistryUi = reportToString(report.blockRegistryUiChecks);\n const blockGARegistryUi = reportToString(report.blockGARegistryUiChecks);\n const autoUpdateCdn = reportToString(report.autoUpdateCdnChecks);\n\n const storagesSummary = Object.entries(report.storageToDownloadReport)\n .map(([storage, report]) => `${templateSummary(report)} ${storage} storage check`)\n .join(\"\\n\");\n\n return `\n${summary(pings.ok)} pings to Platforma Backend\n${summary(blockRegistryOverview.ok)} block registry overview\n${summary(blockGARegistryOverview.ok)} block ga registry overview\n${summary(blockRegistryUi.ok)} block registry ui\n${summary(blockGARegistryUi.ok)} block ga registry ui\n${summary(autoUpdateCdn.ok)} auto-update CDN\n${templateSummary(report.uploadTemplateCheck)} upload template\n${templateSummary(report.uploadFileCheck)} upload file\n${templateSummary(report.downloadFileCheck)} download file\n${templateSummary(report.softwareCheck)} software check\n${templateSummary(report.pythonSoftwareCheck)} python software check\n${storagesSummary}\n\ndetails:\npl endpoint: ${plEndpoint};\noptions: ${JSON.stringify(opts, null, 2)}.\n\nUpload template response: ${report.uploadTemplateCheck.message}\n\nUpload file response: ${report.uploadFileCheck.message}\n\nDownload file response: ${report.downloadFileCheck.message}\n\nSoftware check response: ${report.softwareCheck.message}\nPython software check response: ${report.pythonSoftwareCheck.message}\nStorage to download responses: ${JSON.stringify(report.storageToDownloadReport, null, 2)}\n\nPlatforma pings: ${pings.details}\n\nBlock registry overview responses: ${blockRegistryOverview.details}\n\nBlock ga registry overview responses: ${blockGARegistryOverview.details}\n\nBlock registry ui responses: ${blockRegistryUi.details}\n\nBlock ga registry ui responses: ${blockGARegistryUi.details}\n\nAuto-update CDN responses: ${autoUpdateCdn.details}\n\ndumps:\nBlock registry overview dumps:\n${JSON.stringify(report.blockRegistryOverviewChecks, null, 2)}\n\nBlock ga registry overview dumps:\n${JSON.stringify(report.blockGARegistryOverviewChecks, null, 2)}\n\nBlock registry ui dumps:\n${JSON.stringify(report.blockRegistryUiChecks, null, 2)}\n\nBlock ga registry ui dumps:\n${JSON.stringify(report.blockGARegistryUiChecks, null, 2)}\n\nAuto-update CDN dumps:\n${JSON.stringify(report.autoUpdateCdnChecks, null, 2)}\n\nPlatforma pings error dumps:\n${JSON.stringify(failedPings, null, 2)}\n\nPlatforma pings success dump examples:\n${JSON.stringify(successPingsBodies, null, 2)}\n\nUndici logs:\n${undiciLogs.join(\"\\n\")}\n`;\n}\n\n// List of Undici diagnostic channels\nconst undiciEvents: string[] = [\n \"undici:request:create\", // When a new request is created\n \"undici:request:bodySent\", // When the request body is sent\n \"undici:request:headers\", // When request headers are sent\n \"undici:request:error\", // When a request encounters an error\n \"undici:request:trailers\", // When a response completes.\n\n \"undici:client:sendHeaders\",\n \"undici:client:beforeConnect\",\n \"undici:client:connected\",\n \"undici:client:connectError\",\n\n \"undici:socket:close\", // When a socket is closed\n \"undici:socket:connect\", // When a socket connects\n \"undici:socket:error\", // When a socket encounters an error\n\n \"undici:pool:request\", // When a request is added to the pool\n \"undici:pool:connect\", // When a pool creates a new connection\n \"undici:pool:disconnect\", // When a pool connection is closed\n \"undici:pool:destroy\", // When a pool is destroyed\n \"undici:dispatcher:request\", // When a dispatcher processes a request\n \"undici:dispatcher:connect\", // When a dispatcher connects\n \"undici:dispatcher:disconnect\", // When a dispatcher disconnects\n \"undici:dispatcher:retry\", // When a dispatcher retries a request\n];\n"],"mappings":";;;;;;;;;;;;;AAgHA,eAAsB,aACpB,eACA,QACA,YACA,gBAA2C,EAAE,EAC5B;CACjB,MAAM,aAAoB,EAAE;AAE5B,cAAa,SAAS,UAAU;AAE9B,wCADkC,MAAM,CACtB,WAAW,YAAiB;GAC5C,MAAM,6BAAY,IAAI,MAAM,EAAC,aAAa;GAC1C,MAAM,OAAO,EAAE,GAAG,SAAS;AAC3B,OAAI,MAAM,UAAU,SAAS;AAC3B,SAAK,WAAW,EAAE,GAAG,KAAK,UAAU;AACpC,SAAK,SAAS,UAAU,KAAK,SAAS,QAAQ,OAAO;AACrD,SAAK,SAAS,UAAU,KAAK,SAAS,QAAQ,KAAK,MAAW,EAAE,UAAU,CAAC;;AAI7E,OAAI,MAAM,SAAS,MAAM;AACvB,SAAK,UAAU,EAAE,GAAG,KAAK,SAAS;AAClC,SAAK,QAAQ,OAAO;;AAGtB,cAAW,KACT,KAAK,UAAU;IACb;IACA;IACA;IACD,CAAC,CACH;IACD;GACF;AAEF,KAAI;EACF,MAAM,EACJ,QACA,UACA,QACA,QACA,gBACA,kBACA,UACA,YACA,QACE,MAAM,iBAAiB,eAAe,QAAQ,YAAY,cAAc;EAE5E,MAAM,EAAE,UAAU,oBAAoB,aAAa,0BACjD,MAAMA,iCAAgB;EACxB,MAAM,EAAE,UAAU,qBAAqB,MAAMC,oCAAmB;AAwChE,SAAO,gBAtCwB;GAC7B,SAAS,MAAMC,2BAAa,KAAK,SAAS;GAC1C,6BAA6B,MAAMC,yCAA2B,KAAK,WAAW;GAC9E,+BAA+B,MAAMC,2CAA6B,KAAK,WAAW;GAClF,uBAAuB,MAAMC,mCAAqB,KAAK,WAAW;GAClE,yBAAyB,MAAMC,qCAAuB,KAAK,WAAW;GAEtE,qBAAqB,MAAMC,iCAAmB,KAAK,WAAW;GAE9D,qBAAqB,MAAMC,gCAAe,QAAQ,QAAQ,OAAO;GACjE,iBAAiB,MAAMC,4BACrB,QACA,QACA,UACA,kBACA,QACA,iBACD;GACD,mBAAmB,MAAMC,8BACvB,QACA,QACA,UACA,kBACA,gBACA,oBACA,sBACD;GACD,eAAe,MAAMC,+BAAc,OAAO;GAC1C,qBAAqB,MAAMC,gCAAe,QAAQ,OAAO;GACzD,yBAAyB,MAAMC,0CAAyB,QAAQ,QAAQ,UAAU;IAChF,eAAe,IAAI;IACnB,YAAY,IAAI;IAChB,aAAa,IAAI;IACjB,aAAa,IAAI;IACjB,eAAe,IAAI;IACpB,CAAC;GACH,EAE8B,eAAe,KAAK,WAAW;UACvD,GAAG;AACV,SAAO,+CAA+C;;;AAI1D,eAAsB,iBACpB,eACA,QACA,YACA,gBAA2C,EAAE,EAY5C;CACD,MAAM,MAAwB;EAC5B,qBAAqB;EACrB,eAAe;EACf,mBAAmB;EAEnB,eAAe;EAEf,yBAAyB;EACzB,4BAA4B;EAE5B,kBAAkB;EAClB,oBAAoB;EACpB,mBAAmB;EACnB,aAAa;EAEb,yBAAyB;EACzB,iCAAiC;EACjC,kBACE;EAEF,WAAW;EAEX,wBAAwB;EACxB,yBAAyB;EACzB,yBAAyB,MAAM,OAAO;EACtC,2BAA2B;EAC3B,2BAA2B;EAC3B,GAAG;EACJ;CAED,MAAM,4DAA6B,eAAe,EAChD,uBAAuB,IAAI,eAC5B,CAAC;AAIF,UAAS,kBAAkB,8CAA6B;CAExD,MAAM,WAAW,MAAMC,kDAAwB,MAAM,SAAS;CAE9D,IAAI,OAAwB,EAAE;AAC9B,KAAI,UAAU,WACZ,QAAO,MAAM,SAAS,MAAM,QAAQ,WAAW;CAGjD,MAAM,SAAS,MAAMC,mCAAS,KAAK,eAAe,EAAE,iBAAiB,MAAM,CAAC;CAE5E,MAAM,aAAa,SAAS,GAAG;CAC/B,MAAM,SAAS,IAAIC,iDAAsB;CAGzC,MAAM,SAAS,IAAIC,4CAAiB,cAAc;CAclD,MAAM,sEAAsC,QAAQ,QAAQ,EAAE,CAAC;CAC/D,MAAM,0EAA0C,QAAQ,OAAO;CAE/D,MAAM,WAAW,MAAMC,oCAAS,KAAK,QAAQ,QAAQ,QAAQ,EAAE,QAAQ,QAAQ,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC;CAE/F,MAAM,YAAY,YAAY;AAC5B,iBAAe,OAAO;AACtB,mBAAiB,OAAO;AACxB,QAAM,WAAW,OAAO;AACxB,QAAM,OAAO,OAAO;;AAGtB,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;AAGH,SAAS,gBACP,QACA,YACA,MACA,YACQ;CACR,MAAM,eAAe,OAAO,QAAQ,QAAQ,MAAM,EAAE,SAAS,GAAG;CAChE,MAAM,cAAc,OAAO,QAAQ,QAAQ,MAAM,CAAC,EAAE,SAAS,GAAG;CAChE,MAAM,qBAAqB,CACzB,GAAG,IAAI,IAAI,aAAa,KAAK,MAAM,KAAK,UAAW,EAAE,SAAiB,MAAM,CAAC,CAAC,CAC/E;CAED,MAAM,WAAW,OAAiB,KAAK,OAAO;CAC9C,MAAM,mBAAmB,WACvB,OAAO,WAAW,OAAO,OAAO,OAAO,WAAW,SAAS,SAAS;CAEtE,MAAM,QAAQC,6BAAe,OAAO,QAAQ;CAC5C,MAAM,wBAAwBA,6BAAe,OAAO,4BAA4B;CAChF,MAAM,0BAA0BA,6BAAe,OAAO,8BAA8B;CACpF,MAAM,kBAAkBA,6BAAe,OAAO,sBAAsB;CACpE,MAAM,oBAAoBA,6BAAe,OAAO,wBAAwB;CACxE,MAAM,gBAAgBA,6BAAe,OAAO,oBAAoB;CAEhE,MAAM,kBAAkB,OAAO,QAAQ,OAAO,wBAAwB,CACnE,KAAK,CAAC,SAAS,YAAY,GAAG,gBAAgB,OAAO,CAAC,GAAG,QAAQ,gBAAgB,CACjF,KAAK,KAAK;AAEb,QAAO;EACP,QAAQ,MAAM,GAAG,CAAC;EAClB,QAAQ,sBAAsB,GAAG,CAAC;EAClC,QAAQ,wBAAwB,GAAG,CAAC;EACpC,QAAQ,gBAAgB,GAAG,CAAC;EAC5B,QAAQ,kBAAkB,GAAG,CAAC;EAC9B,QAAQ,cAAc,GAAG,CAAC;EAC1B,gBAAgB,OAAO,oBAAoB,CAAC;EAC5C,gBAAgB,OAAO,gBAAgB,CAAC;EACxC,gBAAgB,OAAO,kBAAkB,CAAC;EAC1C,gBAAgB,OAAO,cAAc,CAAC;EACtC,gBAAgB,OAAO,oBAAoB,CAAC;EAC5C,gBAAgB;;;eAGH,WAAW;WACf,KAAK,UAAU,MAAM,MAAM,EAAE,CAAC;;4BAEb,OAAO,oBAAoB,QAAQ;;wBAEvC,OAAO,gBAAgB,QAAQ;;0BAE7B,OAAO,kBAAkB,QAAQ;;2BAEhC,OAAO,cAAc,QAAQ;kCACtB,OAAO,oBAAoB,QAAQ;iCACpC,KAAK,UAAU,OAAO,yBAAyB,MAAM,EAAE,CAAC;;mBAEtE,MAAM,QAAQ;;qCAEI,sBAAsB,QAAQ;;wCAE3B,wBAAwB,QAAQ;;+BAEzC,gBAAgB,QAAQ;;kCAErB,kBAAkB,QAAQ;;6BAE/B,cAAc,QAAQ;;;;EAIjD,KAAK,UAAU,OAAO,6BAA6B,MAAM,EAAE,CAAC;;;EAG5D,KAAK,UAAU,OAAO,+BAA+B,MAAM,EAAE,CAAC;;;EAG9D,KAAK,UAAU,OAAO,uBAAuB,MAAM,EAAE,CAAC;;;EAGtD,KAAK,UAAU,OAAO,yBAAyB,MAAM,EAAE,CAAC;;;EAGxD,KAAK,UAAU,OAAO,qBAAqB,MAAM,EAAE,CAAC;;;EAGpD,KAAK,UAAU,aAAa,MAAM,EAAE,CAAC;;;EAGrC,KAAK,UAAU,oBAAoB,MAAM,EAAE,CAAC;;;EAG5C,WAAW,KAAK,KAAK,CAAC;;;AAKxB,MAAM,eAAyB;CAC7B;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CAEA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD"}
|
|
1
|
+
{"version":3,"file":"network_check.cjs","names":["createTempFile","createBigTempFile","backendPings","blockRegistryOverviewPings","blockGARegistryOverviewPings","blockRegistryUiPings","blockGARegistryUiPings","autoUpdateCdnPings","uploadTemplate","uploadFile","downloadFile","softwareCheck","pythonSoftware","downloadFromEveryStorage","UnauthenticatedPlClient","PlClient","ConsoleLoggerAdapter","HmacSha256Signer","LsDriver","reportToString"],"sources":["../../src/network_check/network_check.ts"],"sourcesContent":["/** A utility to check network problems and gather statistics.\n * It's useful when we cannot connect to the server of a company\n * because of security reasons,\n * but they can send us and their DevOps team this report.\n *\n * What we check:\n * - pings to backend\n * - block registry for block overview and ui.\n * - autoupdate CDN.\n * - upload workflow to backend (workflow part via our API).\n * - the desktop could do multipart upload.\n * - the desktop could download files from S3.\n * - backend could download software and run it.\n * - backend could run python software.\n * - try to get something from every storage to work storage.\n *\n * We don't check backend access to S3 storage, it is checked on the start of backend.\n */\n\nimport type { AuthInformation, PlClientConfig } from \"@milaboratories/pl-client\";\nimport { PlClient, UnauthenticatedPlClient, plAddressToConfig } from \"@milaboratories/pl-client\";\nimport type { MiLogger, Signer } from \"@milaboratories/ts-helpers\";\nimport { ConsoleLoggerAdapter, HmacSha256Signer } from \"@milaboratories/ts-helpers\";\nimport { channel } from \"node:diagnostics_channel\";\nimport type { ClientDownload, ClientUpload } from \"@milaboratories/pl-drivers\";\nimport { LsDriver, createDownloadClient, createUploadBlobClient } from \"@milaboratories/pl-drivers\";\nimport type { HttpNetworkReport, NetworkReport } from \"./pings\";\nimport {\n autoUpdateCdnPings,\n backendPings,\n blockGARegistryOverviewPings,\n blockGARegistryUiPings,\n blockRegistryOverviewPings,\n blockRegistryUiPings,\n reportToString,\n} from \"./pings\";\nimport type { Dispatcher } from \"undici\";\nimport type { TemplateReport } from \"./template\";\nimport {\n uploadTemplate,\n uploadFile,\n downloadFile,\n createTempFile,\n pythonSoftware,\n softwareCheck,\n createBigTempFile,\n downloadFromEveryStorage,\n} from \"./template\";\nimport { randomUUID } from \"node:crypto\";\n\n/** All reports we need to collect. */\ninterface NetworkReports {\n plPings: NetworkReport<string>[];\n\n blockRegistryOverviewChecks: HttpNetworkReport[];\n blockGARegistryOverviewChecks: HttpNetworkReport[];\n blockRegistryUiChecks: HttpNetworkReport[];\n blockGARegistryUiChecks: HttpNetworkReport[];\n\n autoUpdateCdnChecks: HttpNetworkReport[];\n\n uploadTemplateCheck: TemplateReport;\n uploadFileCheck: TemplateReport;\n downloadFileCheck: TemplateReport;\n softwareCheck: TemplateReport;\n pythonSoftwareCheck: TemplateReport;\n storageToDownloadReport: Record<string, TemplateReport>;\n}\n\nexport interface CheckNetworkOpts {\n /** Signal to abort all network checks. */\n signal?: AbortSignal;\n\n /** Platforma Backend pings options. */\n pingCheckDurationMs: number;\n pingTimeoutMs: number;\n maxPingsPerSecond: number;\n\n /** An options for CDN and block registry. */\n httpTimeoutMs: number;\n\n /** Block registry pings options. */\n blockRegistryDurationMs: number;\n maxRegistryChecksPerSecond: number;\n blockRegistryUrl: string;\n blockGARegistryUrl: string;\n blockOverviewPath: string;\n blockUiPath: string;\n\n /** CDN for auto-update pings options. */\n autoUpdateCdnDurationMs: number;\n maxAutoUpdateCdnChecksPerSecond: number;\n autoUpdateCdnUrl: string;\n\n /** Body limit for requests. */\n bodyLimit: number;\n\n /** Limit for the size of files to download from every storage. */\n everyStorageBytesLimit: number;\n /** Minimal size of files to create a directory from for every storage. */\n everyStorageMinFileSize: number;\n /** Maximal size of files to create a directory from for every storage. */\n everyStorageMaxFileSize: number;\n /** How many files to check from every storage. */\n everyStorageNFilesToCheck: number;\n /** Minimal number of ls requests for every storage. */\n everyStorageMinLsRequests: number;\n}\n\n/** Checks connectivity to Platforma Backend, to block registry\n * and to auto-update CDN,\n * and generates a string report. */\nexport async function checkNetwork(\n plCredentials: string,\n plUser: string | undefined,\n plPassword: string | undefined,\n optsOverrides: Partial<CheckNetworkOpts> = {},\n): Promise<string> {\n const undiciLogs: any[] = [];\n // Subscribe to all Undici diagnostic events\n undiciEvents.forEach((event) => {\n const diagnosticChannel = channel(event);\n diagnosticChannel.subscribe((message: any) => {\n const timestamp = new Date().toISOString();\n const data = { ...message };\n if (data?.response?.headers) {\n data.response = { ...data.response };\n data.response.headers = data.response.headers.slice();\n data.response.headers = data.response.headers.map((h: any) => h.toString());\n }\n\n // we try to upload big files, don't include the buffer in the report.\n if (data?.request?.body) {\n data.request = { ...data.request };\n data.request.body = `too big`;\n }\n\n undiciLogs.push(\n JSON.stringify({\n timestamp,\n event,\n data,\n }),\n );\n });\n });\n\n try {\n const {\n logger,\n plConfig,\n client,\n signer,\n downloadClient,\n uploadBlobClient,\n lsDriver,\n httpClient,\n ops,\n } = await initNetworkCheck(plCredentials, plUser, plPassword, optsOverrides);\n\n const { filePath: filePathToDownload, fileContent: fileContentToDownload } =\n await createTempFile();\n const { filePath: filePathToUpload } = await createBigTempFile();\n\n const report: NetworkReports = {\n plPings: await backendPings(ops, plConfig),\n blockRegistryOverviewChecks: await blockRegistryOverviewPings(ops, httpClient),\n blockGARegistryOverviewChecks: await blockGARegistryOverviewPings(ops, httpClient),\n blockRegistryUiChecks: await blockRegistryUiPings(ops, httpClient),\n blockGARegistryUiChecks: await blockGARegistryUiPings(ops, httpClient),\n\n autoUpdateCdnChecks: await autoUpdateCdnPings(ops, httpClient),\n\n uploadTemplateCheck: await uploadTemplate(logger, client, \"Jack\"),\n uploadFileCheck: await uploadFile(\n logger,\n signer,\n lsDriver,\n uploadBlobClient,\n client,\n filePathToUpload,\n ),\n downloadFileCheck: await downloadFile(\n logger,\n client,\n lsDriver,\n uploadBlobClient,\n downloadClient,\n filePathToDownload,\n fileContentToDownload,\n ),\n softwareCheck: await softwareCheck(client),\n pythonSoftwareCheck: await pythonSoftware(client, \"Jack\"),\n storageToDownloadReport: await downloadFromEveryStorage(logger, client, lsDriver, {\n minLsRequests: ops.everyStorageMinLsRequests,\n bytesLimit: ops.everyStorageBytesLimit,\n minFileSize: ops.everyStorageMinFileSize,\n maxFileSize: ops.everyStorageMaxFileSize,\n nFilesToCheck: ops.everyStorageNFilesToCheck,\n }),\n };\n\n return reportsToString(report, plCredentials, ops, undiciLogs);\n } catch (e) {\n return `Unhandled error while checking the network: ${e}`;\n }\n}\n\nexport async function initNetworkCheck(\n plCredentials: string,\n plUser: string | undefined,\n plPassword: string | undefined,\n optsOverrides: Partial<CheckNetworkOpts> = {},\n): Promise<{\n logger: MiLogger;\n plConfig: PlClientConfig;\n signer: Signer;\n client: PlClient;\n downloadClient: ClientDownload;\n uploadBlobClient: ClientUpload;\n lsDriver: LsDriver;\n httpClient: Dispatcher;\n ops: CheckNetworkOpts;\n terminate: () => Promise<void>;\n}> {\n const ops: CheckNetworkOpts = {\n pingCheckDurationMs: 10000,\n pingTimeoutMs: 3000,\n maxPingsPerSecond: 50,\n\n httpTimeoutMs: 3000,\n\n blockRegistryDurationMs: 3000,\n maxRegistryChecksPerSecond: 1,\n\n blockRegistryUrl: \"https://blocks.pl-open.science\",\n blockGARegistryUrl: \"https://blocks-ga.pl-open.science\",\n blockOverviewPath: \"v2/overview.json\",\n blockUiPath: \"v2/milaboratories/samples-and-data/1.7.0/ui.tgz\",\n\n autoUpdateCdnDurationMs: 5000,\n maxAutoUpdateCdnChecksPerSecond: 1,\n autoUpdateCdnUrl:\n \"https://cdn.platforma.bio/software/platforma-desktop-v2/windows/amd64/latest.yml\",\n\n bodyLimit: 300,\n\n everyStorageBytesLimit: 1024,\n everyStorageMinFileSize: 1024,\n everyStorageMaxFileSize: 200 * 1024 * 1024, // 200 MB\n everyStorageNFilesToCheck: 300,\n everyStorageMinLsRequests: 50,\n ...optsOverrides,\n };\n\n const plConfig = plAddressToConfig(plCredentials, {\n defaultRequestTimeout: ops.pingTimeoutMs,\n });\n\n // exposing alternative root for fields not to interfere with\n // projects of the user.\n plConfig.alternativeRoot = `check_network_${randomUUID()}`;\n\n const uaClient = await UnauthenticatedPlClient.build(plConfig);\n\n let auth: AuthInformation = {};\n if (plUser && plPassword) {\n auth = await uaClient.login(plUser, plPassword);\n }\n\n const client = await PlClient.init(plCredentials, { authInformation: auth });\n\n const httpClient = uaClient.ll.httpDispatcher;\n const logger = new ConsoleLoggerAdapter();\n\n // FIXME: do we need to get an actual secret?\n const signer = new HmacSha256Signer(\"localSecret\");\n\n // We could initialize middle-layer here, but for now it seems like an overkill.\n // Here's the code to do it:\n //\n // const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'platforma-network-check-'));\n // const ml = await MiddleLayer.init(client, tmpDir, {\n // logger,\n // localSecret: '',\n // localProjections: [],\n // openFileDialogCallback: () => Promise.resolve([]),\n // preferredUpdateChannel: 'stable',\n // });\n\n const downloadClient = createDownloadClient(logger, client, []);\n const uploadBlobClient = createUploadBlobClient(client, logger);\n\n const lsDriver = await LsDriver.init(logger, client, signer, [], () => Promise.resolve([]), []);\n\n const terminate = async () => {\n downloadClient.close();\n uploadBlobClient.close();\n await httpClient.close();\n await client.close();\n };\n\n return {\n logger,\n plConfig,\n client,\n signer,\n downloadClient,\n uploadBlobClient,\n lsDriver,\n httpClient,\n ops,\n terminate,\n };\n}\n\nfunction reportsToString(\n report: NetworkReports,\n plEndpoint: string,\n opts: CheckNetworkOpts,\n undiciLogs: any[],\n): string {\n const successPings = report.plPings.filter((p) => p.response.ok);\n const failedPings = report.plPings.filter((p) => !p.response.ok);\n const successPingsBodies = [\n ...new Set(successPings.map((p) => JSON.stringify((p.response as any).value))),\n ];\n\n const summary = (ok: boolean) => (ok ? \"OK\" : \"FAILED\");\n const templateSummary = (report: TemplateReport) =>\n report.status === \"ok\" ? \"OK\" : report.status === \"warn\" ? \"WARN\" : \"FAILED\";\n\n const pings = reportToString(report.plPings);\n const blockRegistryOverview = reportToString(report.blockRegistryOverviewChecks);\n const blockGARegistryOverview = reportToString(report.blockGARegistryOverviewChecks);\n const blockRegistryUi = reportToString(report.blockRegistryUiChecks);\n const blockGARegistryUi = reportToString(report.blockGARegistryUiChecks);\n const autoUpdateCdn = reportToString(report.autoUpdateCdnChecks);\n\n const storagesSummary = Object.entries(report.storageToDownloadReport)\n .map(([storage, report]) => `${templateSummary(report)} ${storage} storage check`)\n .join(\"\\n\");\n\n return `\n${summary(pings.ok)} pings to Platforma Backend\n${summary(blockRegistryOverview.ok)} block registry overview\n${summary(blockGARegistryOverview.ok)} block ga registry overview\n${summary(blockRegistryUi.ok)} block registry ui\n${summary(blockGARegistryUi.ok)} block ga registry ui\n${summary(autoUpdateCdn.ok)} auto-update CDN\n${templateSummary(report.uploadTemplateCheck)} upload template\n${templateSummary(report.uploadFileCheck)} upload file\n${templateSummary(report.downloadFileCheck)} download file\n${templateSummary(report.softwareCheck)} software check\n${templateSummary(report.pythonSoftwareCheck)} python software check\n${storagesSummary}\n\ndetails:\npl endpoint: ${plEndpoint};\noptions: ${JSON.stringify(opts, null, 2)}.\n\nUpload template response: ${report.uploadTemplateCheck.message}\n\nUpload file response: ${report.uploadFileCheck.message}\n\nDownload file response: ${report.downloadFileCheck.message}\n\nSoftware check response: ${report.softwareCheck.message}\nPython software check response: ${report.pythonSoftwareCheck.message}\nStorage to download responses: ${JSON.stringify(report.storageToDownloadReport, null, 2)}\n\nPlatforma pings: ${pings.details}\n\nBlock registry overview responses: ${blockRegistryOverview.details}\n\nBlock ga registry overview responses: ${blockGARegistryOverview.details}\n\nBlock registry ui responses: ${blockRegistryUi.details}\n\nBlock ga registry ui responses: ${blockGARegistryUi.details}\n\nAuto-update CDN responses: ${autoUpdateCdn.details}\n\ndumps:\nBlock registry overview dumps:\n${JSON.stringify(report.blockRegistryOverviewChecks, null, 2)}\n\nBlock ga registry overview dumps:\n${JSON.stringify(report.blockGARegistryOverviewChecks, null, 2)}\n\nBlock registry ui dumps:\n${JSON.stringify(report.blockRegistryUiChecks, null, 2)}\n\nBlock ga registry ui dumps:\n${JSON.stringify(report.blockGARegistryUiChecks, null, 2)}\n\nAuto-update CDN dumps:\n${JSON.stringify(report.autoUpdateCdnChecks, null, 2)}\n\nPlatforma pings error dumps:\n${JSON.stringify(failedPings, null, 2)}\n\nPlatforma pings success dump examples:\n${JSON.stringify(successPingsBodies, null, 2)}\n\nUndici logs:\n${undiciLogs.join(\"\\n\")}\n`;\n}\n\n// List of Undici diagnostic channels\nconst undiciEvents: string[] = [\n \"undici:request:create\", // When a new request is created\n \"undici:request:bodySent\", // When the request body is sent\n \"undici:request:headers\", // When request headers are sent\n \"undici:request:error\", // When a request encounters an error\n \"undici:request:trailers\", // When a response completes.\n\n \"undici:client:sendHeaders\",\n \"undici:client:beforeConnect\",\n \"undici:client:connected\",\n \"undici:client:connectError\",\n\n \"undici:socket:close\", // When a socket is closed\n \"undici:socket:connect\", // When a socket connects\n \"undici:socket:error\", // When a socket encounters an error\n\n \"undici:pool:request\", // When a request is added to the pool\n \"undici:pool:connect\", // When a pool creates a new connection\n \"undici:pool:disconnect\", // When a pool connection is closed\n \"undici:pool:destroy\", // When a pool is destroyed\n \"undici:dispatcher:request\", // When a dispatcher processes a request\n \"undici:dispatcher:connect\", // When a dispatcher connects\n \"undici:dispatcher:disconnect\", // When a dispatcher disconnects\n \"undici:dispatcher:retry\", // When a dispatcher retries a request\n];\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network_check.d.ts","names":[],"sources":["../../src/network_check/network_check.ts"],"mappings":";;;;;;UAqEiB,gBAAA;EA0Bf;EAxBA,MAAA,GAAS,WAAA;EA6BT;EA1BA,mBAAA;EACA,aAAA;EACA,iBAAA;EA8ByB;EA3BzB,aAAA;EAiCoB;EA9BpB,uBAAA;EACA,0BAAA;EACA,gBAAA;EACA,kBAAA;EACA,iBAAA;EACA,WAAA;EA8BQ;EA3BR,uBAAA;EACA,+BAAA;EACA,gBAAA;EAwBe;EArBf,SAAA;EAqBA;EAlBA,sBAAA;EAmBQ;EAjBR,uBAAA;EA4GoB;EA1GpB,uBAAA;;EAEA,yBAAA;EA4Ge;EA1Gf,yBAAA;AAAA;;;;iBAMoB,YAAA,CACpB,aAAA,UACA,MAAA,sBACA,UAAA,sBACA,aAAA,GAAe,OAAA,CAAQ,gBAAA,IACtB,OAAA;AAAA,iBA2FmB,gBAAA,CACpB,aAAA,UACA,MAAA,sBACA,UAAA,sBACA,aAAA,GAAe,OAAA,CAAQ,gBAAA,IACtB,OAAA;EACD,MAAA,EAAQ,QAAA;EACR,QAAA,EAAU,cAAA;EACV,MAAA,EAAQ,MAAA;EACR,MAAA,EAAQ,QAAA;EACR,cAAA,EAAgB,cAAA;EAChB,gBAAA,EAAkB,YAAA;EAClB,QAAA,EAAU,QAAA;EACV,UAAA,EAAY,UAAA;EACZ,GAAA,EAAK,gBAAA;EACL,SAAA,QAAiB,OAAA;AAAA"}
|
|
@@ -5,7 +5,6 @@ import { PlClient, UnauthenticatedPlClient, plAddressToConfig } from "@milaborat
|
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
6
6
|
import { LsDriver, createDownloadClient, createUploadBlobClient } from "@milaboratories/pl-drivers";
|
|
7
7
|
import { channel } from "node:diagnostics_channel";
|
|
8
|
-
|
|
9
8
|
//#region src/network_check/network_check.ts
|
|
10
9
|
/** Checks connectivity to Platforma Backend, to block registry
|
|
11
10
|
* and to auto-update CDN,
|
|
@@ -215,7 +214,7 @@ const undiciEvents = [
|
|
|
215
214
|
"undici:dispatcher:disconnect",
|
|
216
215
|
"undici:dispatcher:retry"
|
|
217
216
|
];
|
|
218
|
-
|
|
219
217
|
//#endregion
|
|
220
218
|
export { checkNetwork, initNetworkCheck };
|
|
219
|
+
|
|
221
220
|
//# sourceMappingURL=network_check.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"network_check.js","names":[],"sources":["../../src/network_check/network_check.ts"],"sourcesContent":["/** A utility to check network problems and gather statistics.\n * It's useful when we cannot connect to the server of a company\n * because of security reasons,\n * but they can send us and their DevOps team this report.\n *\n * What we check:\n * - pings to backend\n * - block registry for block overview and ui.\n * - autoupdate CDN.\n * - upload workflow to backend (workflow part via our API).\n * - the desktop could do multipart upload.\n * - the desktop could download files from S3.\n * - backend could download software and run it.\n * - backend could run python software.\n * - try to get something from every storage to work storage.\n *\n * We don't check backend access to S3 storage, it is checked on the start of backend.\n */\n\nimport type { AuthInformation, PlClientConfig } from \"@milaboratories/pl-client\";\nimport { PlClient, UnauthenticatedPlClient, plAddressToConfig } from \"@milaboratories/pl-client\";\nimport type { MiLogger, Signer } from \"@milaboratories/ts-helpers\";\nimport { ConsoleLoggerAdapter, HmacSha256Signer } from \"@milaboratories/ts-helpers\";\nimport { channel } from \"node:diagnostics_channel\";\nimport type { ClientDownload, ClientUpload } from \"@milaboratories/pl-drivers\";\nimport { LsDriver, createDownloadClient, createUploadBlobClient } from \"@milaboratories/pl-drivers\";\nimport type { HttpNetworkReport, NetworkReport } from \"./pings\";\nimport {\n autoUpdateCdnPings,\n backendPings,\n blockGARegistryOverviewPings,\n blockGARegistryUiPings,\n blockRegistryOverviewPings,\n blockRegistryUiPings,\n reportToString,\n} from \"./pings\";\nimport type { Dispatcher } from \"undici\";\nimport type { TemplateReport } from \"./template\";\nimport {\n uploadTemplate,\n uploadFile,\n downloadFile,\n createTempFile,\n pythonSoftware,\n softwareCheck,\n createBigTempFile,\n downloadFromEveryStorage,\n} from \"./template\";\nimport { randomUUID } from \"node:crypto\";\n\n/** All reports we need to collect. */\ninterface NetworkReports {\n plPings: NetworkReport<string>[];\n\n blockRegistryOverviewChecks: HttpNetworkReport[];\n blockGARegistryOverviewChecks: HttpNetworkReport[];\n blockRegistryUiChecks: HttpNetworkReport[];\n blockGARegistryUiChecks: HttpNetworkReport[];\n\n autoUpdateCdnChecks: HttpNetworkReport[];\n\n uploadTemplateCheck: TemplateReport;\n uploadFileCheck: TemplateReport;\n downloadFileCheck: TemplateReport;\n softwareCheck: TemplateReport;\n pythonSoftwareCheck: TemplateReport;\n storageToDownloadReport: Record<string, TemplateReport>;\n}\n\nexport interface CheckNetworkOpts {\n /** Signal to abort all network checks. */\n signal?: AbortSignal;\n\n /** Platforma Backend pings options. */\n pingCheckDurationMs: number;\n pingTimeoutMs: number;\n maxPingsPerSecond: number;\n\n /** An options for CDN and block registry. */\n httpTimeoutMs: number;\n\n /** Block registry pings options. */\n blockRegistryDurationMs: number;\n maxRegistryChecksPerSecond: number;\n blockRegistryUrl: string;\n blockGARegistryUrl: string;\n blockOverviewPath: string;\n blockUiPath: string;\n\n /** CDN for auto-update pings options. */\n autoUpdateCdnDurationMs: number;\n maxAutoUpdateCdnChecksPerSecond: number;\n autoUpdateCdnUrl: string;\n\n /** Body limit for requests. */\n bodyLimit: number;\n\n /** Limit for the size of files to download from every storage. */\n everyStorageBytesLimit: number;\n /** Minimal size of files to create a directory from for every storage. */\n everyStorageMinFileSize: number;\n /** Maximal size of files to create a directory from for every storage. */\n everyStorageMaxFileSize: number;\n /** How many files to check from every storage. */\n everyStorageNFilesToCheck: number;\n /** Minimal number of ls requests for every storage. */\n everyStorageMinLsRequests: number;\n}\n\n/** Checks connectivity to Platforma Backend, to block registry\n * and to auto-update CDN,\n * and generates a string report. */\nexport async function checkNetwork(\n plCredentials: string,\n plUser: string | undefined,\n plPassword: string | undefined,\n optsOverrides: Partial<CheckNetworkOpts> = {},\n): Promise<string> {\n const undiciLogs: any[] = [];\n // Subscribe to all Undici diagnostic events\n undiciEvents.forEach((event) => {\n const diagnosticChannel = channel(event);\n diagnosticChannel.subscribe((message: any) => {\n const timestamp = new Date().toISOString();\n const data = { ...message };\n if (data?.response?.headers) {\n data.response = { ...data.response };\n data.response.headers = data.response.headers.slice();\n data.response.headers = data.response.headers.map((h: any) => h.toString());\n }\n\n // we try to upload big files, don't include the buffer in the report.\n if (data?.request?.body) {\n data.request = { ...data.request };\n data.request.body = `too big`;\n }\n\n undiciLogs.push(\n JSON.stringify({\n timestamp,\n event,\n data,\n }),\n );\n });\n });\n\n try {\n const {\n logger,\n plConfig,\n client,\n signer,\n downloadClient,\n uploadBlobClient,\n lsDriver,\n httpClient,\n ops,\n } = await initNetworkCheck(plCredentials, plUser, plPassword, optsOverrides);\n\n const { filePath: filePathToDownload, fileContent: fileContentToDownload } =\n await createTempFile();\n const { filePath: filePathToUpload } = await createBigTempFile();\n\n const report: NetworkReports = {\n plPings: await backendPings(ops, plConfig),\n blockRegistryOverviewChecks: await blockRegistryOverviewPings(ops, httpClient),\n blockGARegistryOverviewChecks: await blockGARegistryOverviewPings(ops, httpClient),\n blockRegistryUiChecks: await blockRegistryUiPings(ops, httpClient),\n blockGARegistryUiChecks: await blockGARegistryUiPings(ops, httpClient),\n\n autoUpdateCdnChecks: await autoUpdateCdnPings(ops, httpClient),\n\n uploadTemplateCheck: await uploadTemplate(logger, client, \"Jack\"),\n uploadFileCheck: await uploadFile(\n logger,\n signer,\n lsDriver,\n uploadBlobClient,\n client,\n filePathToUpload,\n ),\n downloadFileCheck: await downloadFile(\n logger,\n client,\n lsDriver,\n uploadBlobClient,\n downloadClient,\n filePathToDownload,\n fileContentToDownload,\n ),\n softwareCheck: await softwareCheck(client),\n pythonSoftwareCheck: await pythonSoftware(client, \"Jack\"),\n storageToDownloadReport: await downloadFromEveryStorage(logger, client, lsDriver, {\n minLsRequests: ops.everyStorageMinLsRequests,\n bytesLimit: ops.everyStorageBytesLimit,\n minFileSize: ops.everyStorageMinFileSize,\n maxFileSize: ops.everyStorageMaxFileSize,\n nFilesToCheck: ops.everyStorageNFilesToCheck,\n }),\n };\n\n return reportsToString(report, plCredentials, ops, undiciLogs);\n } catch (e) {\n return `Unhandled error while checking the network: ${e}`;\n }\n}\n\nexport async function initNetworkCheck(\n plCredentials: string,\n plUser: string | undefined,\n plPassword: string | undefined,\n optsOverrides: Partial<CheckNetworkOpts> = {},\n): Promise<{\n logger: MiLogger;\n plConfig: PlClientConfig;\n signer: Signer;\n client: PlClient;\n downloadClient: ClientDownload;\n uploadBlobClient: ClientUpload;\n lsDriver: LsDriver;\n httpClient: Dispatcher;\n ops: CheckNetworkOpts;\n terminate: () => Promise<void>;\n}> {\n const ops: CheckNetworkOpts = {\n pingCheckDurationMs: 10000,\n pingTimeoutMs: 3000,\n maxPingsPerSecond: 50,\n\n httpTimeoutMs: 3000,\n\n blockRegistryDurationMs: 3000,\n maxRegistryChecksPerSecond: 1,\n\n blockRegistryUrl: \"https://blocks.pl-open.science\",\n blockGARegistryUrl: \"https://blocks-ga.pl-open.science\",\n blockOverviewPath: \"v2/overview.json\",\n blockUiPath: \"v2/milaboratories/samples-and-data/1.7.0/ui.tgz\",\n\n autoUpdateCdnDurationMs: 5000,\n maxAutoUpdateCdnChecksPerSecond: 1,\n autoUpdateCdnUrl:\n \"https://cdn.platforma.bio/software/platforma-desktop-v2/windows/amd64/latest.yml\",\n\n bodyLimit: 300,\n\n everyStorageBytesLimit: 1024,\n everyStorageMinFileSize: 1024,\n everyStorageMaxFileSize: 200 * 1024 * 1024, // 200 MB\n everyStorageNFilesToCheck: 300,\n everyStorageMinLsRequests: 50,\n ...optsOverrides,\n };\n\n const plConfig = plAddressToConfig(plCredentials, {\n defaultRequestTimeout: ops.pingTimeoutMs,\n });\n\n // exposing alternative root for fields not to interfere with\n // projects of the user.\n plConfig.alternativeRoot = `check_network_${randomUUID()}`;\n\n const uaClient = await UnauthenticatedPlClient.build(plConfig);\n\n let auth: AuthInformation = {};\n if (plUser && plPassword) {\n auth = await uaClient.login(plUser, plPassword);\n }\n\n const client = await PlClient.init(plCredentials, { authInformation: auth });\n\n const httpClient = uaClient.ll.httpDispatcher;\n const logger = new ConsoleLoggerAdapter();\n\n // FIXME: do we need to get an actual secret?\n const signer = new HmacSha256Signer(\"localSecret\");\n\n // We could initialize middle-layer here, but for now it seems like an overkill.\n // Here's the code to do it:\n //\n // const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'platforma-network-check-'));\n // const ml = await MiddleLayer.init(client, tmpDir, {\n // logger,\n // localSecret: '',\n // localProjections: [],\n // openFileDialogCallback: () => Promise.resolve([]),\n // preferredUpdateChannel: 'stable',\n // });\n\n const downloadClient = createDownloadClient(logger, client, []);\n const uploadBlobClient = createUploadBlobClient(client, logger);\n\n const lsDriver = await LsDriver.init(logger, client, signer, [], () => Promise.resolve([]), []);\n\n const terminate = async () => {\n downloadClient.close();\n uploadBlobClient.close();\n await httpClient.close();\n await client.close();\n };\n\n return {\n logger,\n plConfig,\n client,\n signer,\n downloadClient,\n uploadBlobClient,\n lsDriver,\n httpClient,\n ops,\n terminate,\n };\n}\n\nfunction reportsToString(\n report: NetworkReports,\n plEndpoint: string,\n opts: CheckNetworkOpts,\n undiciLogs: any[],\n): string {\n const successPings = report.plPings.filter((p) => p.response.ok);\n const failedPings = report.plPings.filter((p) => !p.response.ok);\n const successPingsBodies = [\n ...new Set(successPings.map((p) => JSON.stringify((p.response as any).value))),\n ];\n\n const summary = (ok: boolean) => (ok ? \"OK\" : \"FAILED\");\n const templateSummary = (report: TemplateReport) =>\n report.status === \"ok\" ? \"OK\" : report.status === \"warn\" ? \"WARN\" : \"FAILED\";\n\n const pings = reportToString(report.plPings);\n const blockRegistryOverview = reportToString(report.blockRegistryOverviewChecks);\n const blockGARegistryOverview = reportToString(report.blockGARegistryOverviewChecks);\n const blockRegistryUi = reportToString(report.blockRegistryUiChecks);\n const blockGARegistryUi = reportToString(report.blockGARegistryUiChecks);\n const autoUpdateCdn = reportToString(report.autoUpdateCdnChecks);\n\n const storagesSummary = Object.entries(report.storageToDownloadReport)\n .map(([storage, report]) => `${templateSummary(report)} ${storage} storage check`)\n .join(\"\\n\");\n\n return `\n${summary(pings.ok)} pings to Platforma Backend\n${summary(blockRegistryOverview.ok)} block registry overview\n${summary(blockGARegistryOverview.ok)} block ga registry overview\n${summary(blockRegistryUi.ok)} block registry ui\n${summary(blockGARegistryUi.ok)} block ga registry ui\n${summary(autoUpdateCdn.ok)} auto-update CDN\n${templateSummary(report.uploadTemplateCheck)} upload template\n${templateSummary(report.uploadFileCheck)} upload file\n${templateSummary(report.downloadFileCheck)} download file\n${templateSummary(report.softwareCheck)} software check\n${templateSummary(report.pythonSoftwareCheck)} python software check\n${storagesSummary}\n\ndetails:\npl endpoint: ${plEndpoint};\noptions: ${JSON.stringify(opts, null, 2)}.\n\nUpload template response: ${report.uploadTemplateCheck.message}\n\nUpload file response: ${report.uploadFileCheck.message}\n\nDownload file response: ${report.downloadFileCheck.message}\n\nSoftware check response: ${report.softwareCheck.message}\nPython software check response: ${report.pythonSoftwareCheck.message}\nStorage to download responses: ${JSON.stringify(report.storageToDownloadReport, null, 2)}\n\nPlatforma pings: ${pings.details}\n\nBlock registry overview responses: ${blockRegistryOverview.details}\n\nBlock ga registry overview responses: ${blockGARegistryOverview.details}\n\nBlock registry ui responses: ${blockRegistryUi.details}\n\nBlock ga registry ui responses: ${blockGARegistryUi.details}\n\nAuto-update CDN responses: ${autoUpdateCdn.details}\n\ndumps:\nBlock registry overview dumps:\n${JSON.stringify(report.blockRegistryOverviewChecks, null, 2)}\n\nBlock ga registry overview dumps:\n${JSON.stringify(report.blockGARegistryOverviewChecks, null, 2)}\n\nBlock registry ui dumps:\n${JSON.stringify(report.blockRegistryUiChecks, null, 2)}\n\nBlock ga registry ui dumps:\n${JSON.stringify(report.blockGARegistryUiChecks, null, 2)}\n\nAuto-update CDN dumps:\n${JSON.stringify(report.autoUpdateCdnChecks, null, 2)}\n\nPlatforma pings error dumps:\n${JSON.stringify(failedPings, null, 2)}\n\nPlatforma pings success dump examples:\n${JSON.stringify(successPingsBodies, null, 2)}\n\nUndici logs:\n${undiciLogs.join(\"\\n\")}\n`;\n}\n\n// List of Undici diagnostic channels\nconst undiciEvents: string[] = [\n \"undici:request:create\", // When a new request is created\n \"undici:request:bodySent\", // When the request body is sent\n \"undici:request:headers\", // When request headers are sent\n \"undici:request:error\", // When a request encounters an error\n \"undici:request:trailers\", // When a response completes.\n\n \"undici:client:sendHeaders\",\n \"undici:client:beforeConnect\",\n \"undici:client:connected\",\n \"undici:client:connectError\",\n\n \"undici:socket:close\", // When a socket is closed\n \"undici:socket:connect\", // When a socket connects\n \"undici:socket:error\", // When a socket encounters an error\n\n \"undici:pool:request\", // When a request is added to the pool\n \"undici:pool:connect\", // When a pool creates a new connection\n \"undici:pool:disconnect\", // When a pool connection is closed\n \"undici:pool:destroy\", // When a pool is destroyed\n \"undici:dispatcher:request\", // When a dispatcher processes a request\n \"undici:dispatcher:connect\", // When a dispatcher connects\n \"undici:dispatcher:disconnect\", // When a dispatcher disconnects\n \"undici:dispatcher:retry\", // When a dispatcher retries a request\n];\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"network_check.js","names":[],"sources":["../../src/network_check/network_check.ts"],"sourcesContent":["/** A utility to check network problems and gather statistics.\n * It's useful when we cannot connect to the server of a company\n * because of security reasons,\n * but they can send us and their DevOps team this report.\n *\n * What we check:\n * - pings to backend\n * - block registry for block overview and ui.\n * - autoupdate CDN.\n * - upload workflow to backend (workflow part via our API).\n * - the desktop could do multipart upload.\n * - the desktop could download files from S3.\n * - backend could download software and run it.\n * - backend could run python software.\n * - try to get something from every storage to work storage.\n *\n * We don't check backend access to S3 storage, it is checked on the start of backend.\n */\n\nimport type { AuthInformation, PlClientConfig } from \"@milaboratories/pl-client\";\nimport { PlClient, UnauthenticatedPlClient, plAddressToConfig } from \"@milaboratories/pl-client\";\nimport type { MiLogger, Signer } from \"@milaboratories/ts-helpers\";\nimport { ConsoleLoggerAdapter, HmacSha256Signer } from \"@milaboratories/ts-helpers\";\nimport { channel } from \"node:diagnostics_channel\";\nimport type { ClientDownload, ClientUpload } from \"@milaboratories/pl-drivers\";\nimport { LsDriver, createDownloadClient, createUploadBlobClient } from \"@milaboratories/pl-drivers\";\nimport type { HttpNetworkReport, NetworkReport } from \"./pings\";\nimport {\n autoUpdateCdnPings,\n backendPings,\n blockGARegistryOverviewPings,\n blockGARegistryUiPings,\n blockRegistryOverviewPings,\n blockRegistryUiPings,\n reportToString,\n} from \"./pings\";\nimport type { Dispatcher } from \"undici\";\nimport type { TemplateReport } from \"./template\";\nimport {\n uploadTemplate,\n uploadFile,\n downloadFile,\n createTempFile,\n pythonSoftware,\n softwareCheck,\n createBigTempFile,\n downloadFromEveryStorage,\n} from \"./template\";\nimport { randomUUID } from \"node:crypto\";\n\n/** All reports we need to collect. */\ninterface NetworkReports {\n plPings: NetworkReport<string>[];\n\n blockRegistryOverviewChecks: HttpNetworkReport[];\n blockGARegistryOverviewChecks: HttpNetworkReport[];\n blockRegistryUiChecks: HttpNetworkReport[];\n blockGARegistryUiChecks: HttpNetworkReport[];\n\n autoUpdateCdnChecks: HttpNetworkReport[];\n\n uploadTemplateCheck: TemplateReport;\n uploadFileCheck: TemplateReport;\n downloadFileCheck: TemplateReport;\n softwareCheck: TemplateReport;\n pythonSoftwareCheck: TemplateReport;\n storageToDownloadReport: Record<string, TemplateReport>;\n}\n\nexport interface CheckNetworkOpts {\n /** Signal to abort all network checks. */\n signal?: AbortSignal;\n\n /** Platforma Backend pings options. */\n pingCheckDurationMs: number;\n pingTimeoutMs: number;\n maxPingsPerSecond: number;\n\n /** An options for CDN and block registry. */\n httpTimeoutMs: number;\n\n /** Block registry pings options. */\n blockRegistryDurationMs: number;\n maxRegistryChecksPerSecond: number;\n blockRegistryUrl: string;\n blockGARegistryUrl: string;\n blockOverviewPath: string;\n blockUiPath: string;\n\n /** CDN for auto-update pings options. */\n autoUpdateCdnDurationMs: number;\n maxAutoUpdateCdnChecksPerSecond: number;\n autoUpdateCdnUrl: string;\n\n /** Body limit for requests. */\n bodyLimit: number;\n\n /** Limit for the size of files to download from every storage. */\n everyStorageBytesLimit: number;\n /** Minimal size of files to create a directory from for every storage. */\n everyStorageMinFileSize: number;\n /** Maximal size of files to create a directory from for every storage. */\n everyStorageMaxFileSize: number;\n /** How many files to check from every storage. */\n everyStorageNFilesToCheck: number;\n /** Minimal number of ls requests for every storage. */\n everyStorageMinLsRequests: number;\n}\n\n/** Checks connectivity to Platforma Backend, to block registry\n * and to auto-update CDN,\n * and generates a string report. */\nexport async function checkNetwork(\n plCredentials: string,\n plUser: string | undefined,\n plPassword: string | undefined,\n optsOverrides: Partial<CheckNetworkOpts> = {},\n): Promise<string> {\n const undiciLogs: any[] = [];\n // Subscribe to all Undici diagnostic events\n undiciEvents.forEach((event) => {\n const diagnosticChannel = channel(event);\n diagnosticChannel.subscribe((message: any) => {\n const timestamp = new Date().toISOString();\n const data = { ...message };\n if (data?.response?.headers) {\n data.response = { ...data.response };\n data.response.headers = data.response.headers.slice();\n data.response.headers = data.response.headers.map((h: any) => h.toString());\n }\n\n // we try to upload big files, don't include the buffer in the report.\n if (data?.request?.body) {\n data.request = { ...data.request };\n data.request.body = `too big`;\n }\n\n undiciLogs.push(\n JSON.stringify({\n timestamp,\n event,\n data,\n }),\n );\n });\n });\n\n try {\n const {\n logger,\n plConfig,\n client,\n signer,\n downloadClient,\n uploadBlobClient,\n lsDriver,\n httpClient,\n ops,\n } = await initNetworkCheck(plCredentials, plUser, plPassword, optsOverrides);\n\n const { filePath: filePathToDownload, fileContent: fileContentToDownload } =\n await createTempFile();\n const { filePath: filePathToUpload } = await createBigTempFile();\n\n const report: NetworkReports = {\n plPings: await backendPings(ops, plConfig),\n blockRegistryOverviewChecks: await blockRegistryOverviewPings(ops, httpClient),\n blockGARegistryOverviewChecks: await blockGARegistryOverviewPings(ops, httpClient),\n blockRegistryUiChecks: await blockRegistryUiPings(ops, httpClient),\n blockGARegistryUiChecks: await blockGARegistryUiPings(ops, httpClient),\n\n autoUpdateCdnChecks: await autoUpdateCdnPings(ops, httpClient),\n\n uploadTemplateCheck: await uploadTemplate(logger, client, \"Jack\"),\n uploadFileCheck: await uploadFile(\n logger,\n signer,\n lsDriver,\n uploadBlobClient,\n client,\n filePathToUpload,\n ),\n downloadFileCheck: await downloadFile(\n logger,\n client,\n lsDriver,\n uploadBlobClient,\n downloadClient,\n filePathToDownload,\n fileContentToDownload,\n ),\n softwareCheck: await softwareCheck(client),\n pythonSoftwareCheck: await pythonSoftware(client, \"Jack\"),\n storageToDownloadReport: await downloadFromEveryStorage(logger, client, lsDriver, {\n minLsRequests: ops.everyStorageMinLsRequests,\n bytesLimit: ops.everyStorageBytesLimit,\n minFileSize: ops.everyStorageMinFileSize,\n maxFileSize: ops.everyStorageMaxFileSize,\n nFilesToCheck: ops.everyStorageNFilesToCheck,\n }),\n };\n\n return reportsToString(report, plCredentials, ops, undiciLogs);\n } catch (e) {\n return `Unhandled error while checking the network: ${e}`;\n }\n}\n\nexport async function initNetworkCheck(\n plCredentials: string,\n plUser: string | undefined,\n plPassword: string | undefined,\n optsOverrides: Partial<CheckNetworkOpts> = {},\n): Promise<{\n logger: MiLogger;\n plConfig: PlClientConfig;\n signer: Signer;\n client: PlClient;\n downloadClient: ClientDownload;\n uploadBlobClient: ClientUpload;\n lsDriver: LsDriver;\n httpClient: Dispatcher;\n ops: CheckNetworkOpts;\n terminate: () => Promise<void>;\n}> {\n const ops: CheckNetworkOpts = {\n pingCheckDurationMs: 10000,\n pingTimeoutMs: 3000,\n maxPingsPerSecond: 50,\n\n httpTimeoutMs: 3000,\n\n blockRegistryDurationMs: 3000,\n maxRegistryChecksPerSecond: 1,\n\n blockRegistryUrl: \"https://blocks.pl-open.science\",\n blockGARegistryUrl: \"https://blocks-ga.pl-open.science\",\n blockOverviewPath: \"v2/overview.json\",\n blockUiPath: \"v2/milaboratories/samples-and-data/1.7.0/ui.tgz\",\n\n autoUpdateCdnDurationMs: 5000,\n maxAutoUpdateCdnChecksPerSecond: 1,\n autoUpdateCdnUrl:\n \"https://cdn.platforma.bio/software/platforma-desktop-v2/windows/amd64/latest.yml\",\n\n bodyLimit: 300,\n\n everyStorageBytesLimit: 1024,\n everyStorageMinFileSize: 1024,\n everyStorageMaxFileSize: 200 * 1024 * 1024, // 200 MB\n everyStorageNFilesToCheck: 300,\n everyStorageMinLsRequests: 50,\n ...optsOverrides,\n };\n\n const plConfig = plAddressToConfig(plCredentials, {\n defaultRequestTimeout: ops.pingTimeoutMs,\n });\n\n // exposing alternative root for fields not to interfere with\n // projects of the user.\n plConfig.alternativeRoot = `check_network_${randomUUID()}`;\n\n const uaClient = await UnauthenticatedPlClient.build(plConfig);\n\n let auth: AuthInformation = {};\n if (plUser && plPassword) {\n auth = await uaClient.login(plUser, plPassword);\n }\n\n const client = await PlClient.init(plCredentials, { authInformation: auth });\n\n const httpClient = uaClient.ll.httpDispatcher;\n const logger = new ConsoleLoggerAdapter();\n\n // FIXME: do we need to get an actual secret?\n const signer = new HmacSha256Signer(\"localSecret\");\n\n // We could initialize middle-layer here, but for now it seems like an overkill.\n // Here's the code to do it:\n //\n // const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'platforma-network-check-'));\n // const ml = await MiddleLayer.init(client, tmpDir, {\n // logger,\n // localSecret: '',\n // localProjections: [],\n // openFileDialogCallback: () => Promise.resolve([]),\n // preferredUpdateChannel: 'stable',\n // });\n\n const downloadClient = createDownloadClient(logger, client, []);\n const uploadBlobClient = createUploadBlobClient(client, logger);\n\n const lsDriver = await LsDriver.init(logger, client, signer, [], () => Promise.resolve([]), []);\n\n const terminate = async () => {\n downloadClient.close();\n uploadBlobClient.close();\n await httpClient.close();\n await client.close();\n };\n\n return {\n logger,\n plConfig,\n client,\n signer,\n downloadClient,\n uploadBlobClient,\n lsDriver,\n httpClient,\n ops,\n terminate,\n };\n}\n\nfunction reportsToString(\n report: NetworkReports,\n plEndpoint: string,\n opts: CheckNetworkOpts,\n undiciLogs: any[],\n): string {\n const successPings = report.plPings.filter((p) => p.response.ok);\n const failedPings = report.plPings.filter((p) => !p.response.ok);\n const successPingsBodies = [\n ...new Set(successPings.map((p) => JSON.stringify((p.response as any).value))),\n ];\n\n const summary = (ok: boolean) => (ok ? \"OK\" : \"FAILED\");\n const templateSummary = (report: TemplateReport) =>\n report.status === \"ok\" ? \"OK\" : report.status === \"warn\" ? \"WARN\" : \"FAILED\";\n\n const pings = reportToString(report.plPings);\n const blockRegistryOverview = reportToString(report.blockRegistryOverviewChecks);\n const blockGARegistryOverview = reportToString(report.blockGARegistryOverviewChecks);\n const blockRegistryUi = reportToString(report.blockRegistryUiChecks);\n const blockGARegistryUi = reportToString(report.blockGARegistryUiChecks);\n const autoUpdateCdn = reportToString(report.autoUpdateCdnChecks);\n\n const storagesSummary = Object.entries(report.storageToDownloadReport)\n .map(([storage, report]) => `${templateSummary(report)} ${storage} storage check`)\n .join(\"\\n\");\n\n return `\n${summary(pings.ok)} pings to Platforma Backend\n${summary(blockRegistryOverview.ok)} block registry overview\n${summary(blockGARegistryOverview.ok)} block ga registry overview\n${summary(blockRegistryUi.ok)} block registry ui\n${summary(blockGARegistryUi.ok)} block ga registry ui\n${summary(autoUpdateCdn.ok)} auto-update CDN\n${templateSummary(report.uploadTemplateCheck)} upload template\n${templateSummary(report.uploadFileCheck)} upload file\n${templateSummary(report.downloadFileCheck)} download file\n${templateSummary(report.softwareCheck)} software check\n${templateSummary(report.pythonSoftwareCheck)} python software check\n${storagesSummary}\n\ndetails:\npl endpoint: ${plEndpoint};\noptions: ${JSON.stringify(opts, null, 2)}.\n\nUpload template response: ${report.uploadTemplateCheck.message}\n\nUpload file response: ${report.uploadFileCheck.message}\n\nDownload file response: ${report.downloadFileCheck.message}\n\nSoftware check response: ${report.softwareCheck.message}\nPython software check response: ${report.pythonSoftwareCheck.message}\nStorage to download responses: ${JSON.stringify(report.storageToDownloadReport, null, 2)}\n\nPlatforma pings: ${pings.details}\n\nBlock registry overview responses: ${blockRegistryOverview.details}\n\nBlock ga registry overview responses: ${blockGARegistryOverview.details}\n\nBlock registry ui responses: ${blockRegistryUi.details}\n\nBlock ga registry ui responses: ${blockGARegistryUi.details}\n\nAuto-update CDN responses: ${autoUpdateCdn.details}\n\ndumps:\nBlock registry overview dumps:\n${JSON.stringify(report.blockRegistryOverviewChecks, null, 2)}\n\nBlock ga registry overview dumps:\n${JSON.stringify(report.blockGARegistryOverviewChecks, null, 2)}\n\nBlock registry ui dumps:\n${JSON.stringify(report.blockRegistryUiChecks, null, 2)}\n\nBlock ga registry ui dumps:\n${JSON.stringify(report.blockGARegistryUiChecks, null, 2)}\n\nAuto-update CDN dumps:\n${JSON.stringify(report.autoUpdateCdnChecks, null, 2)}\n\nPlatforma pings error dumps:\n${JSON.stringify(failedPings, null, 2)}\n\nPlatforma pings success dump examples:\n${JSON.stringify(successPingsBodies, null, 2)}\n\nUndici logs:\n${undiciLogs.join(\"\\n\")}\n`;\n}\n\n// List of Undici diagnostic channels\nconst undiciEvents: string[] = [\n \"undici:request:create\", // When a new request is created\n \"undici:request:bodySent\", // When the request body is sent\n \"undici:request:headers\", // When request headers are sent\n \"undici:request:error\", // When a request encounters an error\n \"undici:request:trailers\", // When a response completes.\n\n \"undici:client:sendHeaders\",\n \"undici:client:beforeConnect\",\n \"undici:client:connected\",\n \"undici:client:connectError\",\n\n \"undici:socket:close\", // When a socket is closed\n \"undici:socket:connect\", // When a socket connects\n \"undici:socket:error\", // When a socket encounters an error\n\n \"undici:pool:request\", // When a request is added to the pool\n \"undici:pool:connect\", // When a pool creates a new connection\n \"undici:pool:disconnect\", // When a pool connection is closed\n \"undici:pool:destroy\", // When a pool is destroyed\n \"undici:dispatcher:request\", // When a dispatcher processes a request\n \"undici:dispatcher:connect\", // When a dispatcher connects\n \"undici:dispatcher:disconnect\", // When a dispatcher disconnects\n \"undici:dispatcher:retry\", // When a dispatcher retries a request\n];\n"],"mappings":"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"}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
require("../_virtual/_rolldown/runtime.cjs");
|
|
2
2
|
let undici = require("undici");
|
|
3
3
|
let _milaboratories_pl_client = require("@milaboratories/pl-client");
|
|
4
4
|
let node_timers_promises = require("node:timers/promises");
|
|
5
|
-
|
|
6
5
|
//#region src/network_check/pings.ts
|
|
7
6
|
async function backendPings(ops, plConfig) {
|
|
8
7
|
return await recordPings(ops.pingCheckDurationMs, ops.maxPingsPerSecond, async () => {
|
|
@@ -106,7 +105,6 @@ function elapsedStat(reports) {
|
|
|
106
105
|
median
|
|
107
106
|
};
|
|
108
107
|
}
|
|
109
|
-
|
|
110
108
|
//#endregion
|
|
111
109
|
exports.autoUpdateCdnPings = autoUpdateCdnPings;
|
|
112
110
|
exports.backendPings = backendPings;
|
|
@@ -115,4 +113,5 @@ exports.blockGARegistryUiPings = blockGARegistryUiPings;
|
|
|
115
113
|
exports.blockRegistryOverviewPings = blockRegistryOverviewPings;
|
|
116
114
|
exports.blockRegistryUiPings = blockRegistryUiPings;
|
|
117
115
|
exports.reportToString = reportToString;
|
|
116
|
+
|
|
118
117
|
//# sourceMappingURL=pings.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pings.cjs","names":["UnauthenticatedPlClient"],"sources":["../../src/network_check/pings.ts"],"sourcesContent":["import type { ValueOrError } from \"@milaboratories/ts-helpers\";\nimport { setTimeout } from \"node:timers/promises\";\nimport { request } from \"undici\";\nimport type { Dispatcher } from \"undici\";\nimport type { CheckNetworkOpts } from \"./network_check\";\nimport { UnauthenticatedPlClient, type PlClientConfig } from \"@milaboratories/pl-client\";\n\n/** A report about one concrete ping to the service. */\nexport interface NetworkReport<T> {\n elapsedMs: number;\n response: ValueOrError<T>;\n}\n\nexport type HttpNetworkReport = NetworkReport<{\n statusCode: number;\n beginningOfBody: string;\n}>;\n\nexport async function backendPings(\n ops: CheckNetworkOpts,\n plConfig: PlClientConfig,\n): Promise<NetworkReport<string>[]> {\n return await recordPings(\n ops.pingCheckDurationMs,\n ops.maxPingsPerSecond,\n async () => {\n const uaClient = await UnauthenticatedPlClient.build(plConfig);\n const response = await uaClient.ping();\n return JSON.stringify(response).slice(0, ops.bodyLimit) + \"...\";\n },\n ops.signal,\n );\n}\n\nexport async function blockRegistryOverviewPings(\n ops: CheckNetworkOpts,\n httpClient: Dispatcher,\n): Promise<HttpNetworkReport[]> {\n return await recordPings(\n ops.blockRegistryDurationMs,\n ops.maxRegistryChecksPerSecond,\n async () =>\n await requestUrl(new URL(ops.blockOverviewPath, ops.blockRegistryUrl), ops, httpClient),\n ops.signal,\n );\n}\n\nexport async function blockGARegistryOverviewPings(\n ops: CheckNetworkOpts,\n httpClient: Dispatcher,\n): Promise<HttpNetworkReport[]> {\n return await recordPings(\n ops.blockRegistryDurationMs,\n ops.maxRegistryChecksPerSecond,\n async () =>\n await requestUrl(new URL(ops.blockOverviewPath, ops.blockGARegistryUrl), ops, httpClient),\n ops.signal,\n );\n}\n\nexport async function blockRegistryUiPings(\n ops: CheckNetworkOpts,\n httpClient: Dispatcher,\n): Promise<HttpNetworkReport[]> {\n return await recordPings(\n ops.blockRegistryDurationMs,\n ops.maxRegistryChecksPerSecond,\n async () => await requestUrl(new URL(ops.blockUiPath, ops.blockRegistryUrl), ops, httpClient),\n ops.signal,\n );\n}\n\nexport async function blockGARegistryUiPings(\n ops: CheckNetworkOpts,\n httpClient: Dispatcher,\n): Promise<HttpNetworkReport[]> {\n return await recordPings(\n ops.blockRegistryDurationMs,\n ops.maxRegistryChecksPerSecond,\n async () => await requestUrl(new URL(ops.blockUiPath, ops.blockGARegistryUrl), ops, httpClient),\n ops.signal,\n );\n}\n\nexport async function autoUpdateCdnPings(\n ops: CheckNetworkOpts,\n httpClient: Dispatcher,\n): Promise<HttpNetworkReport[]> {\n return await recordPings(\n ops.autoUpdateCdnDurationMs,\n ops.maxAutoUpdateCdnChecksPerSecond,\n async () => await requestUrl(ops.autoUpdateCdnUrl, ops, httpClient),\n ops.signal,\n );\n}\n\n/** Executes a body several times per second up to the given duration,\n * and returns results and elapsed time for every result. */\nexport async function recordPings<T>(\n pingCheckDurationMs: number,\n maxPingsPerSecond: number,\n body: () => Promise<T>,\n signal?: AbortSignal,\n): Promise<NetworkReport<T>[]> {\n const startPings = Date.now();\n const reports: NetworkReport<T>[] = [];\n\n while (elapsed(startPings) < pingCheckDurationMs && !signal?.aborted) {\n const startPing = Date.now();\n let response: ValueOrError<T>;\n try {\n response = { ok: true, value: await body() };\n } catch (e) {\n response = { ok: false, error: e };\n }\n const elapsedPing = elapsed(startPing);\n\n reports.push({\n elapsedMs: elapsedPing,\n response,\n });\n\n const sleepBetweenPings = 1000 / maxPingsPerSecond - elapsedPing;\n\n if (sleepBetweenPings > 0) {\n try {\n await setTimeout(sleepBetweenPings, undefined, signal ? { signal } : undefined);\n } catch (e: unknown) {\n if (e instanceof Error && e.name === \"AbortError\") break;\n throw e;\n }\n }\n }\n\n return reports;\n}\n\nexport async function requestUrl(url: string | URL, ops: CheckNetworkOpts, httpClient: Dispatcher) {\n const { body: rawBody, statusCode } = await request(url, {\n dispatcher: httpClient,\n headersTimeout: ops.httpTimeoutMs,\n bodyTimeout: ops.httpTimeoutMs,\n });\n const body = await rawBody.text();\n\n return {\n statusCode: statusCode,\n beginningOfBody: body.slice(0, ops.bodyLimit) + \"...\",\n };\n}\n\nexport function elapsed(startMs: number): number {\n return Date.now() - startMs;\n}\n\nexport function reportToString<T>(report: NetworkReport<T>[]): {\n ok: boolean;\n details: string;\n} {\n const successes = report.filter((r) => r.response.ok);\n const errorsLen = report.length - successes.length;\n const { mean, median } = elapsedStat(report);\n\n const details = `\n total: ${report.length};\n successes: ${successes.length};\n errors: ${errorsLen};\n mean in ms: ${mean};\n median in ms: ${median};\n `;\n\n return {\n ok: errorsLen === 0,\n details,\n };\n}\n\nfunction elapsedStat(reports: { elapsedMs: number }[]) {\n if (reports.length === 0) return { mean: 0, median: undefined };\n const checks = reports.map((p) => p.elapsedMs).sort();\n const mean = checks.reduce((sum, p) => sum + p) / checks.length;\n\n let median = undefined;\n if (checks.length > 0) {\n const mid = Math.floor(checks.length / 2);\n median = checks.length % 2 ? checks[mid] : (checks[mid - 1] + checks[mid]) / 2;\n }\n\n return { mean, median };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"pings.cjs","names":["UnauthenticatedPlClient"],"sources":["../../src/network_check/pings.ts"],"sourcesContent":["import type { ValueOrError } from \"@milaboratories/ts-helpers\";\nimport { setTimeout } from \"node:timers/promises\";\nimport { request } from \"undici\";\nimport type { Dispatcher } from \"undici\";\nimport type { CheckNetworkOpts } from \"./network_check\";\nimport { UnauthenticatedPlClient, type PlClientConfig } from \"@milaboratories/pl-client\";\n\n/** A report about one concrete ping to the service. */\nexport interface NetworkReport<T> {\n elapsedMs: number;\n response: ValueOrError<T>;\n}\n\nexport type HttpNetworkReport = NetworkReport<{\n statusCode: number;\n beginningOfBody: string;\n}>;\n\nexport async function backendPings(\n ops: CheckNetworkOpts,\n plConfig: PlClientConfig,\n): Promise<NetworkReport<string>[]> {\n return await recordPings(\n ops.pingCheckDurationMs,\n ops.maxPingsPerSecond,\n async () => {\n const uaClient = await UnauthenticatedPlClient.build(plConfig);\n const response = await uaClient.ping();\n return JSON.stringify(response).slice(0, ops.bodyLimit) + \"...\";\n },\n ops.signal,\n );\n}\n\nexport async function blockRegistryOverviewPings(\n ops: CheckNetworkOpts,\n httpClient: Dispatcher,\n): Promise<HttpNetworkReport[]> {\n return await recordPings(\n ops.blockRegistryDurationMs,\n ops.maxRegistryChecksPerSecond,\n async () =>\n await requestUrl(new URL(ops.blockOverviewPath, ops.blockRegistryUrl), ops, httpClient),\n ops.signal,\n );\n}\n\nexport async function blockGARegistryOverviewPings(\n ops: CheckNetworkOpts,\n httpClient: Dispatcher,\n): Promise<HttpNetworkReport[]> {\n return await recordPings(\n ops.blockRegistryDurationMs,\n ops.maxRegistryChecksPerSecond,\n async () =>\n await requestUrl(new URL(ops.blockOverviewPath, ops.blockGARegistryUrl), ops, httpClient),\n ops.signal,\n );\n}\n\nexport async function blockRegistryUiPings(\n ops: CheckNetworkOpts,\n httpClient: Dispatcher,\n): Promise<HttpNetworkReport[]> {\n return await recordPings(\n ops.blockRegistryDurationMs,\n ops.maxRegistryChecksPerSecond,\n async () => await requestUrl(new URL(ops.blockUiPath, ops.blockRegistryUrl), ops, httpClient),\n ops.signal,\n );\n}\n\nexport async function blockGARegistryUiPings(\n ops: CheckNetworkOpts,\n httpClient: Dispatcher,\n): Promise<HttpNetworkReport[]> {\n return await recordPings(\n ops.blockRegistryDurationMs,\n ops.maxRegistryChecksPerSecond,\n async () => await requestUrl(new URL(ops.blockUiPath, ops.blockGARegistryUrl), ops, httpClient),\n ops.signal,\n );\n}\n\nexport async function autoUpdateCdnPings(\n ops: CheckNetworkOpts,\n httpClient: Dispatcher,\n): Promise<HttpNetworkReport[]> {\n return await recordPings(\n ops.autoUpdateCdnDurationMs,\n ops.maxAutoUpdateCdnChecksPerSecond,\n async () => await requestUrl(ops.autoUpdateCdnUrl, ops, httpClient),\n ops.signal,\n );\n}\n\n/** Executes a body several times per second up to the given duration,\n * and returns results and elapsed time for every result. */\nexport async function recordPings<T>(\n pingCheckDurationMs: number,\n maxPingsPerSecond: number,\n body: () => Promise<T>,\n signal?: AbortSignal,\n): Promise<NetworkReport<T>[]> {\n const startPings = Date.now();\n const reports: NetworkReport<T>[] = [];\n\n while (elapsed(startPings) < pingCheckDurationMs && !signal?.aborted) {\n const startPing = Date.now();\n let response: ValueOrError<T>;\n try {\n response = { ok: true, value: await body() };\n } catch (e) {\n response = { ok: false, error: e };\n }\n const elapsedPing = elapsed(startPing);\n\n reports.push({\n elapsedMs: elapsedPing,\n response,\n });\n\n const sleepBetweenPings = 1000 / maxPingsPerSecond - elapsedPing;\n\n if (sleepBetweenPings > 0) {\n try {\n await setTimeout(sleepBetweenPings, undefined, signal ? { signal } : undefined);\n } catch (e: unknown) {\n if (e instanceof Error && e.name === \"AbortError\") break;\n throw e;\n }\n }\n }\n\n return reports;\n}\n\nexport async function requestUrl(url: string | URL, ops: CheckNetworkOpts, httpClient: Dispatcher) {\n const { body: rawBody, statusCode } = await request(url, {\n dispatcher: httpClient,\n headersTimeout: ops.httpTimeoutMs,\n bodyTimeout: ops.httpTimeoutMs,\n });\n const body = await rawBody.text();\n\n return {\n statusCode: statusCode,\n beginningOfBody: body.slice(0, ops.bodyLimit) + \"...\",\n };\n}\n\nexport function elapsed(startMs: number): number {\n return Date.now() - startMs;\n}\n\nexport function reportToString<T>(report: NetworkReport<T>[]): {\n ok: boolean;\n details: string;\n} {\n const successes = report.filter((r) => r.response.ok);\n const errorsLen = report.length - successes.length;\n const { mean, median } = elapsedStat(report);\n\n const details = `\n total: ${report.length};\n successes: ${successes.length};\n errors: ${errorsLen};\n mean in ms: ${mean};\n median in ms: ${median};\n `;\n\n return {\n ok: errorsLen === 0,\n details,\n };\n}\n\nfunction elapsedStat(reports: { elapsedMs: number }[]) {\n if (reports.length === 0) return { mean: 0, median: undefined };\n const checks = reports.map((p) => p.elapsedMs).sort();\n const mean = checks.reduce((sum, p) => sum + p) / checks.length;\n\n let median = undefined;\n if (checks.length > 0) {\n const mid = Math.floor(checks.length / 2);\n median = checks.length % 2 ? checks[mid] : (checks[mid - 1] + checks[mid]) / 2;\n }\n\n return { mean, median };\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { request } from "undici";
|
|
2
2
|
import { UnauthenticatedPlClient } from "@milaboratories/pl-client";
|
|
3
3
|
import { setTimeout } from "node:timers/promises";
|
|
4
|
-
|
|
5
4
|
//#region src/network_check/pings.ts
|
|
6
5
|
async function backendPings(ops, plConfig) {
|
|
7
6
|
return await recordPings(ops.pingCheckDurationMs, ops.maxPingsPerSecond, async () => {
|
|
@@ -105,7 +104,7 @@ function elapsedStat(reports) {
|
|
|
105
104
|
median
|
|
106
105
|
};
|
|
107
106
|
}
|
|
108
|
-
|
|
109
107
|
//#endregion
|
|
110
108
|
export { autoUpdateCdnPings, backendPings, blockGARegistryOverviewPings, blockGARegistryUiPings, blockRegistryOverviewPings, blockRegistryUiPings, reportToString };
|
|
109
|
+
|
|
111
110
|
//# sourceMappingURL=pings.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pings.js","names":[],"sources":["../../src/network_check/pings.ts"],"sourcesContent":["import type { ValueOrError } from \"@milaboratories/ts-helpers\";\nimport { setTimeout } from \"node:timers/promises\";\nimport { request } from \"undici\";\nimport type { Dispatcher } from \"undici\";\nimport type { CheckNetworkOpts } from \"./network_check\";\nimport { UnauthenticatedPlClient, type PlClientConfig } from \"@milaboratories/pl-client\";\n\n/** A report about one concrete ping to the service. */\nexport interface NetworkReport<T> {\n elapsedMs: number;\n response: ValueOrError<T>;\n}\n\nexport type HttpNetworkReport = NetworkReport<{\n statusCode: number;\n beginningOfBody: string;\n}>;\n\nexport async function backendPings(\n ops: CheckNetworkOpts,\n plConfig: PlClientConfig,\n): Promise<NetworkReport<string>[]> {\n return await recordPings(\n ops.pingCheckDurationMs,\n ops.maxPingsPerSecond,\n async () => {\n const uaClient = await UnauthenticatedPlClient.build(plConfig);\n const response = await uaClient.ping();\n return JSON.stringify(response).slice(0, ops.bodyLimit) + \"...\";\n },\n ops.signal,\n );\n}\n\nexport async function blockRegistryOverviewPings(\n ops: CheckNetworkOpts,\n httpClient: Dispatcher,\n): Promise<HttpNetworkReport[]> {\n return await recordPings(\n ops.blockRegistryDurationMs,\n ops.maxRegistryChecksPerSecond,\n async () =>\n await requestUrl(new URL(ops.blockOverviewPath, ops.blockRegistryUrl), ops, httpClient),\n ops.signal,\n );\n}\n\nexport async function blockGARegistryOverviewPings(\n ops: CheckNetworkOpts,\n httpClient: Dispatcher,\n): Promise<HttpNetworkReport[]> {\n return await recordPings(\n ops.blockRegistryDurationMs,\n ops.maxRegistryChecksPerSecond,\n async () =>\n await requestUrl(new URL(ops.blockOverviewPath, ops.blockGARegistryUrl), ops, httpClient),\n ops.signal,\n );\n}\n\nexport async function blockRegistryUiPings(\n ops: CheckNetworkOpts,\n httpClient: Dispatcher,\n): Promise<HttpNetworkReport[]> {\n return await recordPings(\n ops.blockRegistryDurationMs,\n ops.maxRegistryChecksPerSecond,\n async () => await requestUrl(new URL(ops.blockUiPath, ops.blockRegistryUrl), ops, httpClient),\n ops.signal,\n );\n}\n\nexport async function blockGARegistryUiPings(\n ops: CheckNetworkOpts,\n httpClient: Dispatcher,\n): Promise<HttpNetworkReport[]> {\n return await recordPings(\n ops.blockRegistryDurationMs,\n ops.maxRegistryChecksPerSecond,\n async () => await requestUrl(new URL(ops.blockUiPath, ops.blockGARegistryUrl), ops, httpClient),\n ops.signal,\n );\n}\n\nexport async function autoUpdateCdnPings(\n ops: CheckNetworkOpts,\n httpClient: Dispatcher,\n): Promise<HttpNetworkReport[]> {\n return await recordPings(\n ops.autoUpdateCdnDurationMs,\n ops.maxAutoUpdateCdnChecksPerSecond,\n async () => await requestUrl(ops.autoUpdateCdnUrl, ops, httpClient),\n ops.signal,\n );\n}\n\n/** Executes a body several times per second up to the given duration,\n * and returns results and elapsed time for every result. */\nexport async function recordPings<T>(\n pingCheckDurationMs: number,\n maxPingsPerSecond: number,\n body: () => Promise<T>,\n signal?: AbortSignal,\n): Promise<NetworkReport<T>[]> {\n const startPings = Date.now();\n const reports: NetworkReport<T>[] = [];\n\n while (elapsed(startPings) < pingCheckDurationMs && !signal?.aborted) {\n const startPing = Date.now();\n let response: ValueOrError<T>;\n try {\n response = { ok: true, value: await body() };\n } catch (e) {\n response = { ok: false, error: e };\n }\n const elapsedPing = elapsed(startPing);\n\n reports.push({\n elapsedMs: elapsedPing,\n response,\n });\n\n const sleepBetweenPings = 1000 / maxPingsPerSecond - elapsedPing;\n\n if (sleepBetweenPings > 0) {\n try {\n await setTimeout(sleepBetweenPings, undefined, signal ? { signal } : undefined);\n } catch (e: unknown) {\n if (e instanceof Error && e.name === \"AbortError\") break;\n throw e;\n }\n }\n }\n\n return reports;\n}\n\nexport async function requestUrl(url: string | URL, ops: CheckNetworkOpts, httpClient: Dispatcher) {\n const { body: rawBody, statusCode } = await request(url, {\n dispatcher: httpClient,\n headersTimeout: ops.httpTimeoutMs,\n bodyTimeout: ops.httpTimeoutMs,\n });\n const body = await rawBody.text();\n\n return {\n statusCode: statusCode,\n beginningOfBody: body.slice(0, ops.bodyLimit) + \"...\",\n };\n}\n\nexport function elapsed(startMs: number): number {\n return Date.now() - startMs;\n}\n\nexport function reportToString<T>(report: NetworkReport<T>[]): {\n ok: boolean;\n details: string;\n} {\n const successes = report.filter((r) => r.response.ok);\n const errorsLen = report.length - successes.length;\n const { mean, median } = elapsedStat(report);\n\n const details = `\n total: ${report.length};\n successes: ${successes.length};\n errors: ${errorsLen};\n mean in ms: ${mean};\n median in ms: ${median};\n `;\n\n return {\n ok: errorsLen === 0,\n details,\n };\n}\n\nfunction elapsedStat(reports: { elapsedMs: number }[]) {\n if (reports.length === 0) return { mean: 0, median: undefined };\n const checks = reports.map((p) => p.elapsedMs).sort();\n const mean = checks.reduce((sum, p) => sum + p) / checks.length;\n\n let median = undefined;\n if (checks.length > 0) {\n const mid = Math.floor(checks.length / 2);\n median = checks.length % 2 ? checks[mid] : (checks[mid - 1] + checks[mid]) / 2;\n }\n\n return { mean, median };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"pings.js","names":[],"sources":["../../src/network_check/pings.ts"],"sourcesContent":["import type { ValueOrError } from \"@milaboratories/ts-helpers\";\nimport { setTimeout } from \"node:timers/promises\";\nimport { request } from \"undici\";\nimport type { Dispatcher } from \"undici\";\nimport type { CheckNetworkOpts } from \"./network_check\";\nimport { UnauthenticatedPlClient, type PlClientConfig } from \"@milaboratories/pl-client\";\n\n/** A report about one concrete ping to the service. */\nexport interface NetworkReport<T> {\n elapsedMs: number;\n response: ValueOrError<T>;\n}\n\nexport type HttpNetworkReport = NetworkReport<{\n statusCode: number;\n beginningOfBody: string;\n}>;\n\nexport async function backendPings(\n ops: CheckNetworkOpts,\n plConfig: PlClientConfig,\n): Promise<NetworkReport<string>[]> {\n return await recordPings(\n ops.pingCheckDurationMs,\n ops.maxPingsPerSecond,\n async () => {\n const uaClient = await UnauthenticatedPlClient.build(plConfig);\n const response = await uaClient.ping();\n return JSON.stringify(response).slice(0, ops.bodyLimit) + \"...\";\n },\n ops.signal,\n );\n}\n\nexport async function blockRegistryOverviewPings(\n ops: CheckNetworkOpts,\n httpClient: Dispatcher,\n): Promise<HttpNetworkReport[]> {\n return await recordPings(\n ops.blockRegistryDurationMs,\n ops.maxRegistryChecksPerSecond,\n async () =>\n await requestUrl(new URL(ops.blockOverviewPath, ops.blockRegistryUrl), ops, httpClient),\n ops.signal,\n );\n}\n\nexport async function blockGARegistryOverviewPings(\n ops: CheckNetworkOpts,\n httpClient: Dispatcher,\n): Promise<HttpNetworkReport[]> {\n return await recordPings(\n ops.blockRegistryDurationMs,\n ops.maxRegistryChecksPerSecond,\n async () =>\n await requestUrl(new URL(ops.blockOverviewPath, ops.blockGARegistryUrl), ops, httpClient),\n ops.signal,\n );\n}\n\nexport async function blockRegistryUiPings(\n ops: CheckNetworkOpts,\n httpClient: Dispatcher,\n): Promise<HttpNetworkReport[]> {\n return await recordPings(\n ops.blockRegistryDurationMs,\n ops.maxRegistryChecksPerSecond,\n async () => await requestUrl(new URL(ops.blockUiPath, ops.blockRegistryUrl), ops, httpClient),\n ops.signal,\n );\n}\n\nexport async function blockGARegistryUiPings(\n ops: CheckNetworkOpts,\n httpClient: Dispatcher,\n): Promise<HttpNetworkReport[]> {\n return await recordPings(\n ops.blockRegistryDurationMs,\n ops.maxRegistryChecksPerSecond,\n async () => await requestUrl(new URL(ops.blockUiPath, ops.blockGARegistryUrl), ops, httpClient),\n ops.signal,\n );\n}\n\nexport async function autoUpdateCdnPings(\n ops: CheckNetworkOpts,\n httpClient: Dispatcher,\n): Promise<HttpNetworkReport[]> {\n return await recordPings(\n ops.autoUpdateCdnDurationMs,\n ops.maxAutoUpdateCdnChecksPerSecond,\n async () => await requestUrl(ops.autoUpdateCdnUrl, ops, httpClient),\n ops.signal,\n );\n}\n\n/** Executes a body several times per second up to the given duration,\n * and returns results and elapsed time for every result. */\nexport async function recordPings<T>(\n pingCheckDurationMs: number,\n maxPingsPerSecond: number,\n body: () => Promise<T>,\n signal?: AbortSignal,\n): Promise<NetworkReport<T>[]> {\n const startPings = Date.now();\n const reports: NetworkReport<T>[] = [];\n\n while (elapsed(startPings) < pingCheckDurationMs && !signal?.aborted) {\n const startPing = Date.now();\n let response: ValueOrError<T>;\n try {\n response = { ok: true, value: await body() };\n } catch (e) {\n response = { ok: false, error: e };\n }\n const elapsedPing = elapsed(startPing);\n\n reports.push({\n elapsedMs: elapsedPing,\n response,\n });\n\n const sleepBetweenPings = 1000 / maxPingsPerSecond - elapsedPing;\n\n if (sleepBetweenPings > 0) {\n try {\n await setTimeout(sleepBetweenPings, undefined, signal ? { signal } : undefined);\n } catch (e: unknown) {\n if (e instanceof Error && e.name === \"AbortError\") break;\n throw e;\n }\n }\n }\n\n return reports;\n}\n\nexport async function requestUrl(url: string | URL, ops: CheckNetworkOpts, httpClient: Dispatcher) {\n const { body: rawBody, statusCode } = await request(url, {\n dispatcher: httpClient,\n headersTimeout: ops.httpTimeoutMs,\n bodyTimeout: ops.httpTimeoutMs,\n });\n const body = await rawBody.text();\n\n return {\n statusCode: statusCode,\n beginningOfBody: body.slice(0, ops.bodyLimit) + \"...\",\n };\n}\n\nexport function elapsed(startMs: number): number {\n return Date.now() - startMs;\n}\n\nexport function reportToString<T>(report: NetworkReport<T>[]): {\n ok: boolean;\n details: string;\n} {\n const successes = report.filter((r) => r.response.ok);\n const errorsLen = report.length - successes.length;\n const { mean, median } = elapsedStat(report);\n\n const details = `\n total: ${report.length};\n successes: ${successes.length};\n errors: ${errorsLen};\n mean in ms: ${mean};\n median in ms: ${median};\n `;\n\n return {\n ok: errorsLen === 0,\n details,\n };\n}\n\nfunction elapsedStat(reports: { elapsedMs: number }[]) {\n if (reports.length === 0) return { mean: 0, median: undefined };\n const checks = reports.map((p) => p.elapsedMs).sort();\n const mean = checks.reduce((sum, p) => sum + p) / checks.length;\n\n let median = undefined;\n if (checks.length > 0) {\n const mid = Math.floor(checks.length / 2);\n median = checks.length % 2 ? checks[mid] : (checks[mid - 1] + checks[mid]) / 2;\n }\n\n return { mean, median };\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
const require_runtime = require(
|
|
2
|
-
const require_render_template = require(
|
|
3
|
-
const require_template_loading = require(
|
|
1
|
+
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_render_template = require("../mutator/template/render_template.cjs");
|
|
3
|
+
const require_template_loading = require("../mutator/template/template_loading.cjs");
|
|
4
4
|
let node_path = require("node:path");
|
|
5
5
|
node_path = require_runtime.__toESM(node_path);
|
|
6
6
|
let _milaboratories_ts_helpers = require("@milaboratories/ts-helpers");
|
|
@@ -13,7 +13,6 @@ let _milaboratories_pl_drivers = require("@milaboratories/pl-drivers");
|
|
|
13
13
|
let node_stream_consumers = require("node:stream/consumers");
|
|
14
14
|
let node_os = require("node:os");
|
|
15
15
|
node_os = require_runtime.__toESM(node_os);
|
|
16
|
-
|
|
17
16
|
//#region src/network_check/template.ts
|
|
18
17
|
/** Uploads `hello-world` template and checks the output is correct. */
|
|
19
18
|
async function uploadTemplate(logger, pl, name) {
|
|
@@ -301,7 +300,6 @@ async function deleteFields(client, fieldIds) {
|
|
|
301
300
|
await tx.commit();
|
|
302
301
|
});
|
|
303
302
|
}
|
|
304
|
-
|
|
305
303
|
//#endregion
|
|
306
304
|
exports.createBigTempFile = createBigTempFile;
|
|
307
305
|
exports.createTempFile = createTempFile;
|
|
@@ -311,4 +309,5 @@ exports.pythonSoftware = pythonSoftware;
|
|
|
311
309
|
exports.softwareCheck = softwareCheck;
|
|
312
310
|
exports.uploadFile = uploadFile;
|
|
313
311
|
exports.uploadTemplate = uploadTemplate;
|
|
312
|
+
|
|
314
313
|
//# sourceMappingURL=template.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template.cjs","names":["SdkTemplates","Pl","ImportFileHandleUploadData","path","os","fs","createRenderTemplate","loadTemplate","prepareTemplateSpec","ContinuePolling"],"sources":["../../src/network_check/template.ts"],"sourcesContent":["import type { FieldId, FieldRef, PlClient, ResourceData } from \"@milaboratories/pl-client\";\nimport {\n type PlTransaction,\n ContinuePolling,\n field,\n isNotNullResourceId,\n isNullResourceId,\n Pl,\n poll,\n toGlobalFieldId,\n} from \"@milaboratories/pl-client\";\nimport { createRenderTemplate } from \"../mutator/template/render_template\";\nimport { Templates as SdkTemplates } from \"@platforma-sdk/workflow-tengo\";\nimport type { TemplateSpecAny } from \"../model/template_spec\";\nimport { loadTemplate, prepareTemplateSpec } from \"../mutator/template/template_loading\";\nimport type { ClientDownload, LsDriver } from \"@milaboratories/pl-drivers\";\nimport {\n ImportFileHandleUploadData,\n isSignMatch,\n isUpload,\n uploadBlob,\n type ClientUpload,\n type LsEntryWithAdditionalInfo,\n} from \"@milaboratories/pl-drivers\";\nimport type { Signer } from \"@milaboratories/ts-helpers\";\nimport { notEmpty, type MiLogger } from \"@milaboratories/ts-helpers\";\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\nimport { text } from \"node:stream/consumers\";\nimport path from \"node:path\";\nimport fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport { randomBytes } from \"node:crypto\";\nimport type { StorageEntry } from \"@milaboratories/pl-model-common\";\n\nexport interface TemplateReport {\n status: \"ok\" | \"warn\" | \"failed\";\n message: string;\n}\n\n/** Uploads `hello-world` template and checks the output is correct. */\nexport async function uploadTemplate(\n logger: MiLogger,\n pl: PlClient,\n name: string,\n): Promise<TemplateReport> {\n try {\n const gotGreeting = await runUploadTemplate(logger, pl, name);\n if (gotGreeting !== `Hello, ${name}`) {\n return {\n status: \"failed\",\n message: `Template uploading failed: expected: ${name}, got: ${gotGreeting}`,\n };\n }\n\n return { status: \"ok\", message: `Template uploading succeeded: ${gotGreeting}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `Template uploading failed: error occurred: ${e}` };\n }\n}\n\nexport async function runUploadTemplate(\n logger: MiLogger,\n pl: PlClient,\n name: string,\n): Promise<string> {\n const outputs = await runTemplate(\n pl,\n SdkTemplates[\"check_network.upload_template\"],\n true,\n (tx) => ({\n name: tx.createValue(Pl.JsonObject, JSON.stringify(name)),\n }),\n [\"greeting\"],\n );\n\n try {\n return JSON.parse(notEmpty((await getFieldValue(pl, outputs.greeting)).data?.toString()));\n } finally {\n if (outputs != undefined) {\n await deleteFields(pl, Object.values(outputs));\n }\n }\n}\n\n/** Uploads a file to the backend and checks the output is a Blob resource. */\nexport async function uploadFile(\n logger: MiLogger,\n signer: Signer,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n pl: PlClient,\n filePath: string,\n): Promise<TemplateReport> {\n try {\n const gotBlob = await runUploadFile(logger, signer, lsDriver, uploadClient, pl, filePath);\n\n if (gotBlob.type.name !== \"Blob\") {\n return { status: \"failed\", message: `File uploading failed: ${gotBlob.type.name}` };\n }\n\n return { status: \"ok\", message: `File uploading succeeded: ${gotBlob.type.name}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `File uploading failed: error occurred: ${e}` };\n }\n}\n\nexport async function runUploadFile(\n logger: MiLogger,\n signer: Signer,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n pl: PlClient,\n filePath: string,\n): Promise<ResourceInfo> {\n const handle = await lsDriver.getLocalFileHandle(filePath);\n const result = await runTemplate(\n pl,\n SdkTemplates[\"check_network.upload_blob\"],\n true,\n (tx) => ({\n file: tx.createValue(Pl.JsonObject, JSON.stringify(handle)),\n }),\n [\"progress\", \"file\"],\n );\n\n try {\n const progress = await getFieldValue(pl, result.progress);\n\n if (isUpload(progress)) {\n const uploadData = ImportFileHandleUploadData.parse(\n JSON.parse(notEmpty(progress.data?.toString())),\n );\n const isUploadSignMatch = isSignMatch(signer, uploadData.localPath, uploadData.pathSignature);\n\n if (isUploadSignMatch) {\n await uploadBlob(logger, uploadClient, progress, uploadData, () => false, {\n nPartsWithThisUploadSpeed: 10,\n nPartsToIncreaseUpload: 10,\n currentSpeed: 10,\n maxSpeed: 10,\n });\n }\n }\n\n return await getFieldValue(pl, result.file);\n } finally {\n await deleteFields(pl, Object.values(result));\n }\n}\n\n/** Uploads a file to the backend and then tries to download it back. */\nexport async function downloadFile(\n logger: MiLogger,\n pl: PlClient,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n downloadClient: ClientDownload,\n filePath: string,\n fileContent: string,\n): Promise<TemplateReport> {\n try {\n const gotFileContent = await runDownloadFile(\n logger,\n pl,\n lsDriver,\n uploadClient,\n downloadClient,\n filePath,\n );\n\n if (gotFileContent !== fileContent) {\n return {\n status: \"failed\",\n message: `File downloading failed: expected: ${fileContent}, got: ${gotFileContent}`,\n };\n }\n return { status: \"ok\", message: `File downloading succeeded: ${gotFileContent}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `File downloading failed: error occurred: ${e}` };\n }\n}\n\nexport async function runDownloadFile(\n logger: MiLogger,\n pl: PlClient,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n downloadClient: ClientDownload,\n filePath: string,\n) {\n const handle = await lsDriver.getLocalFileHandle(filePath);\n\n const outputs = await runTemplate(\n pl,\n SdkTemplates[\"check_network.download_blob\"],\n true,\n (tx) => ({ file: tx.createValue(Pl.JsonObject, JSON.stringify(handle)) }),\n [\"progress\", \"file\"],\n );\n\n try {\n const progress = await getFieldValue(pl, outputs.progress);\n\n await uploadBlob(\n logger,\n uploadClient,\n progress,\n ImportFileHandleUploadData.parse(JSON.parse(notEmpty(progress.data?.toString()))),\n () => false,\n {\n nPartsWithThisUploadSpeed: 1,\n nPartsToIncreaseUpload: 1,\n currentSpeed: 1,\n maxSpeed: 1,\n },\n );\n\n const fileInfo = await getFieldValue(pl, outputs.file);\n\n return await downloadClient.withBlobContent(\n fileInfo,\n {},\n {},\n async (content) => await text(content),\n );\n } finally {\n await deleteFields(pl, Object.values(outputs));\n }\n}\n\n/** Runs Go's hello-world binary. */\nexport async function softwareCheck(pl: PlClient): Promise<TemplateReport> {\n try {\n const gotGreeting = await runSoftware(pl);\n\n if (gotGreeting !== \"Hello from go binary\\n\") {\n return { status: \"failed\", message: `Software check failed: got: ${gotGreeting}` };\n }\n return { status: \"ok\", message: `Software check succeeded: ${gotGreeting}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `Software check failed: error occurred: ${e}` };\n }\n}\n\nexport async function runSoftware(pl: PlClient): Promise<string> {\n const result = await runTemplate(\n pl,\n SdkTemplates[\"check_network.run_hello_world\"],\n true,\n (_: PlTransaction) => ({}),\n [\"greeting\"],\n );\n\n try {\n return notEmpty((await getFieldValue(pl, result.greeting)).data?.toString());\n } finally {\n await deleteFields(pl, Object.values(result));\n }\n}\n\n/** Runs Python hello-world. */\nexport async function pythonSoftware(pl: PlClient, name: string): Promise<TemplateReport> {\n try {\n const gotGreeting = await runPythonSoftware(pl, name);\n\n if (gotGreeting !== `Hello, ${name}!\\n`) {\n return { status: \"failed\", message: `Python software check failed: got: ${gotGreeting}` };\n }\n return { status: \"ok\", message: `Python software check succeeded: ${gotGreeting}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `Python software check failed: error occurred: ${e}` };\n }\n}\n\nexport async function runPythonSoftware(pl: PlClient, name: string): Promise<string> {\n const result = await runTemplate(\n pl,\n SdkTemplates[\"check_network.run_hello_world_py\"],\n true,\n (tx) => ({ name: tx.createValue(Pl.JsonObject, JSON.stringify(name)) }),\n [\"greeting\"],\n );\n\n try {\n return notEmpty((await getFieldValue(pl, result.greeting)).data?.toString());\n } finally {\n await deleteFields(pl, Object.values(result));\n }\n}\n\n/** Tries to download a file from every storage. */\nexport async function downloadFromEveryStorage(\n logger: MiLogger,\n pl: PlClient,\n lsDriver: LsDriver,\n ops: {\n minLsRequests: number;\n bytesLimit: number;\n minFileSize: number;\n maxFileSize: number;\n nFilesToCheck: number;\n },\n): Promise<Record<string, TemplateReport>> {\n try {\n const storages = await lsDriver.getStorageList();\n const results: Record<string, TemplateReport> = {};\n\n for (const storage of storages) {\n const result = await chooseFile(\n lsDriver,\n storage,\n ops.nFilesToCheck,\n ops.minFileSize,\n ops.maxFileSize,\n ops.minLsRequests,\n );\n if (result.file === undefined) {\n results[storage.name] = {\n status: \"warn\",\n message:\n `No file between ${ops.minFileSize} and ${ops.maxFileSize} bytes ` +\n `found in storage ${storage.name}, checked ${result.nCheckedFiles} files, ` +\n `did ${result.nLsRequests} ls requests`,\n };\n continue;\n }\n\n logger.info(`Downloading file ${JSON.stringify(result)} from storage ${storage.name}`);\n const outputs = await runTemplate(\n pl,\n SdkTemplates[\"check_network.create_workdir_from_storage\"],\n true,\n (tx) => ({\n file: tx.createValue(\n Pl.JsonObject,\n JSON.stringify((result.file as { handle: string }).handle),\n ),\n }),\n [\"workdirTypeName\"],\n );\n\n try {\n const workdirTypeName = JSON.parse(\n Buffer.from((await getFieldValue(pl, outputs.workdirTypeName)).data!).toString(),\n ) as string;\n\n if (workdirTypeName?.startsWith(\"WorkingDirectory\")) {\n results[storage.name] = {\n status: \"ok\",\n message:\n `Workdir creation succeeded, size of file: ${result.file?.size}, ` +\n `checked ${result.nCheckedFiles} files, did ${result.nLsRequests} ls requests`,\n };\n } else {\n results[storage.name] = {\n status: \"failed\",\n message:\n `Workdir creation failed: ${workdirTypeName}, size of file: ${result.file?.size}, ` +\n `checked ${result.nCheckedFiles} files, did ${result.nLsRequests} ls requests`,\n };\n }\n } finally {\n await deleteFields(pl, Object.values(outputs));\n }\n }\n\n return results;\n } catch (e: unknown) {\n return {\n unknown: {\n status: \"failed\",\n message: `Download from every storage failed: error occurred: ${e}`,\n },\n };\n }\n}\n\n/** Chooses a random file from the storage in a size range.\n * If we couldn't find a normal-sized file, we'll return a small file to check at least something.\n */\nexport async function chooseFile(\n lsDriver: LsDriver,\n storage: StorageEntry,\n limit: number,\n minSize: number,\n maxSize: number,\n minLsRequests: number,\n): Promise<{\n file: LsEntryWithAdditionalInfo | undefined;\n nLsRequests: number;\n nCheckedFiles: number;\n}> {\n const files = listFilesSequence(lsDriver, storage, \"\", 0);\n\n // return small file in case we don't have many normal-sized files.\n // While we'll download only a small range of bytes from the file,\n // we don't want to return a big file in case the underlying S3 doesn't support range requests.\n let smallFile: LsEntryWithAdditionalInfo | undefined;\n let nCheckedFiles = 0;\n let maxNLsRequests = 0;\n\n for await (const { file, nLsRequests } of files) {\n maxNLsRequests = Math.max(maxNLsRequests, nLsRequests);\n\n if (nCheckedFiles >= limit && maxNLsRequests > minLsRequests) {\n // we reached a limit on both the number of files and the number of ls requests.\n return { file: smallFile, nLsRequests: maxNLsRequests, nCheckedFiles };\n }\n nCheckedFiles++;\n if (minSize <= file.size && file.size <= maxSize) {\n return { file, nLsRequests: maxNLsRequests, nCheckedFiles };\n } else if (file.size < minSize) {\n smallFile = file;\n }\n }\n\n return { file: smallFile, nLsRequests: maxNLsRequests, nCheckedFiles };\n}\n\n/** Deep-first search for files in the storage. */\nexport async function* listFilesSequence(\n lsDriver: LsDriver,\n storage: StorageEntry,\n parent: string,\n nLsRequests: number,\n): AsyncGenerator<{ file: LsEntryWithAdditionalInfo; nLsRequests: number }, void, unknown> {\n nLsRequests++;\n const files = await lsDriver.listRemoteFilesWithAdditionalInfo(storage.handle, parent);\n\n for (const file of files.entries) {\n if (file.type === \"file\" && file.fullPath.startsWith(parent)) {\n yield {\n file,\n nLsRequests,\n };\n } else if (file.type === \"dir\") {\n for await (const nestedFile of listFilesSequence(\n lsDriver,\n storage,\n file.fullPath,\n nLsRequests,\n )) {\n nLsRequests = Math.max(nestedFile.nLsRequests, nLsRequests);\n yield nestedFile;\n }\n }\n }\n}\n\n/** Creates a big temporary file with random content. */\nexport async function createBigTempFile(): Promise<{ filePath: string }> {\n const filePath = path.join(os.tmpdir(), `check-network-big-temp-${Date.now()}.bin`);\n const fileSize = 20 * 1024 * 1024; // 20 MiB\n\n const fileContent = randomBytes(fileSize);\n\n await fs.appendFile(filePath, fileContent);\n\n return { filePath };\n}\n\n/** Creates a temporarly file we could use for uploading and downloading. */\nexport async function createTempFile(): Promise<{ filePath: string; fileContent: string }> {\n const filePath = path.join(os.tmpdir(), `check-network-temp-${Date.now()}.txt`);\n\n const fileContent = \"Hello, world! \" + new Date().toISOString();\n await fs.writeFile(filePath, fileContent);\n\n return { filePath, fileContent };\n}\n\n/** Creates a template and RenderTemplate resources, gets all resources from outputs.\n * Throws a error if any of the outputs failed.\n */\nasync function runTemplate(\n client: PlClient,\n tpl: TemplateSpecAny,\n ephemeral: boolean,\n inputs: (tx: PlTransaction) => Pl.PlRecord,\n outputs: string[],\n): Promise<Record<string, FieldId>> {\n return await client.withWriteTx(\"TemplateRender\", async (tx) => {\n const preparedTemplate = await prepareTemplateSpec(tpl);\n const tplResource = loadTemplate(tx, preparedTemplate);\n\n const outputFields: Record<string, FieldRef> = createRenderTemplate(\n tx,\n tplResource,\n ephemeral,\n inputs(tx),\n outputs,\n );\n\n const outputsIds: Record<string, FieldId> = {};\n\n for (const output of outputs) {\n const fieldRef = field(client.clientRoot, output);\n tx.createField(fieldRef, \"Dynamic\", outputFields[output]);\n outputsIds[output] = await toGlobalFieldId(fieldRef);\n }\n\n await tx.commit();\n\n return outputsIds;\n });\n}\n\n/** Gets a resource from field's value or throws a error. */\nasync function getFieldValue(client: PlClient, fieldId: FieldId): Promise<ResourceData> {\n // We could also do polling with pl-tree, but it seemed like an overkill,\n // that's why we have a simple polling here.\n\n return await poll(client, async (tx) => {\n const field = await tx.tx.getField(fieldId);\n if (isNotNullResourceId(field.error)) {\n const err = await tx.tx.getResourceData(field.error, true);\n throw new Error(`getFieldValue of \"${fieldId.fieldName}\" field failed: ${err.data}`);\n }\n\n if (isNullResourceId(field.value)) {\n throw new ContinuePolling();\n }\n\n return await tx.tx.getResourceData(field.value, true);\n });\n}\n\nasync function deleteFields(client: PlClient, fieldIds: FieldId[]) {\n await client.withWriteTx(\"DeleteFields\", async (tx) => {\n for (const fieldId of fieldIds) {\n tx.resetField(fieldId);\n }\n await tx.commit();\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"template.cjs","names":["SdkTemplates","Pl","ImportFileHandleUploadData","path","os","fs","createRenderTemplate","loadTemplate","prepareTemplateSpec","ContinuePolling"],"sources":["../../src/network_check/template.ts"],"sourcesContent":["import type { FieldId, FieldRef, PlClient, ResourceData } from \"@milaboratories/pl-client\";\nimport {\n type PlTransaction,\n ContinuePolling,\n field,\n isNotNullResourceId,\n isNullResourceId,\n Pl,\n poll,\n toGlobalFieldId,\n} from \"@milaboratories/pl-client\";\nimport { createRenderTemplate } from \"../mutator/template/render_template\";\nimport { Templates as SdkTemplates } from \"@platforma-sdk/workflow-tengo\";\nimport type { TemplateSpecAny } from \"../model/template_spec\";\nimport { loadTemplate, prepareTemplateSpec } from \"../mutator/template/template_loading\";\nimport type { ClientDownload, LsDriver } from \"@milaboratories/pl-drivers\";\nimport {\n ImportFileHandleUploadData,\n isSignMatch,\n isUpload,\n uploadBlob,\n type ClientUpload,\n type LsEntryWithAdditionalInfo,\n} from \"@milaboratories/pl-drivers\";\nimport type { Signer } from \"@milaboratories/ts-helpers\";\nimport { notEmpty, type MiLogger } from \"@milaboratories/ts-helpers\";\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\nimport { text } from \"node:stream/consumers\";\nimport path from \"node:path\";\nimport fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport { randomBytes } from \"node:crypto\";\nimport type { StorageEntry } from \"@milaboratories/pl-model-common\";\n\nexport interface TemplateReport {\n status: \"ok\" | \"warn\" | \"failed\";\n message: string;\n}\n\n/** Uploads `hello-world` template and checks the output is correct. */\nexport async function uploadTemplate(\n logger: MiLogger,\n pl: PlClient,\n name: string,\n): Promise<TemplateReport> {\n try {\n const gotGreeting = await runUploadTemplate(logger, pl, name);\n if (gotGreeting !== `Hello, ${name}`) {\n return {\n status: \"failed\",\n message: `Template uploading failed: expected: ${name}, got: ${gotGreeting}`,\n };\n }\n\n return { status: \"ok\", message: `Template uploading succeeded: ${gotGreeting}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `Template uploading failed: error occurred: ${e}` };\n }\n}\n\nexport async function runUploadTemplate(\n logger: MiLogger,\n pl: PlClient,\n name: string,\n): Promise<string> {\n const outputs = await runTemplate(\n pl,\n SdkTemplates[\"check_network.upload_template\"],\n true,\n (tx) => ({\n name: tx.createValue(Pl.JsonObject, JSON.stringify(name)),\n }),\n [\"greeting\"],\n );\n\n try {\n return JSON.parse(notEmpty((await getFieldValue(pl, outputs.greeting)).data?.toString()));\n } finally {\n if (outputs != undefined) {\n await deleteFields(pl, Object.values(outputs));\n }\n }\n}\n\n/** Uploads a file to the backend and checks the output is a Blob resource. */\nexport async function uploadFile(\n logger: MiLogger,\n signer: Signer,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n pl: PlClient,\n filePath: string,\n): Promise<TemplateReport> {\n try {\n const gotBlob = await runUploadFile(logger, signer, lsDriver, uploadClient, pl, filePath);\n\n if (gotBlob.type.name !== \"Blob\") {\n return { status: \"failed\", message: `File uploading failed: ${gotBlob.type.name}` };\n }\n\n return { status: \"ok\", message: `File uploading succeeded: ${gotBlob.type.name}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `File uploading failed: error occurred: ${e}` };\n }\n}\n\nexport async function runUploadFile(\n logger: MiLogger,\n signer: Signer,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n pl: PlClient,\n filePath: string,\n): Promise<ResourceInfo> {\n const handle = await lsDriver.getLocalFileHandle(filePath);\n const result = await runTemplate(\n pl,\n SdkTemplates[\"check_network.upload_blob\"],\n true,\n (tx) => ({\n file: tx.createValue(Pl.JsonObject, JSON.stringify(handle)),\n }),\n [\"progress\", \"file\"],\n );\n\n try {\n const progress = await getFieldValue(pl, result.progress);\n\n if (isUpload(progress)) {\n const uploadData = ImportFileHandleUploadData.parse(\n JSON.parse(notEmpty(progress.data?.toString())),\n );\n const isUploadSignMatch = isSignMatch(signer, uploadData.localPath, uploadData.pathSignature);\n\n if (isUploadSignMatch) {\n await uploadBlob(logger, uploadClient, progress, uploadData, () => false, {\n nPartsWithThisUploadSpeed: 10,\n nPartsToIncreaseUpload: 10,\n currentSpeed: 10,\n maxSpeed: 10,\n });\n }\n }\n\n return await getFieldValue(pl, result.file);\n } finally {\n await deleteFields(pl, Object.values(result));\n }\n}\n\n/** Uploads a file to the backend and then tries to download it back. */\nexport async function downloadFile(\n logger: MiLogger,\n pl: PlClient,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n downloadClient: ClientDownload,\n filePath: string,\n fileContent: string,\n): Promise<TemplateReport> {\n try {\n const gotFileContent = await runDownloadFile(\n logger,\n pl,\n lsDriver,\n uploadClient,\n downloadClient,\n filePath,\n );\n\n if (gotFileContent !== fileContent) {\n return {\n status: \"failed\",\n message: `File downloading failed: expected: ${fileContent}, got: ${gotFileContent}`,\n };\n }\n return { status: \"ok\", message: `File downloading succeeded: ${gotFileContent}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `File downloading failed: error occurred: ${e}` };\n }\n}\n\nexport async function runDownloadFile(\n logger: MiLogger,\n pl: PlClient,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n downloadClient: ClientDownload,\n filePath: string,\n) {\n const handle = await lsDriver.getLocalFileHandle(filePath);\n\n const outputs = await runTemplate(\n pl,\n SdkTemplates[\"check_network.download_blob\"],\n true,\n (tx) => ({ file: tx.createValue(Pl.JsonObject, JSON.stringify(handle)) }),\n [\"progress\", \"file\"],\n );\n\n try {\n const progress = await getFieldValue(pl, outputs.progress);\n\n await uploadBlob(\n logger,\n uploadClient,\n progress,\n ImportFileHandleUploadData.parse(JSON.parse(notEmpty(progress.data?.toString()))),\n () => false,\n {\n nPartsWithThisUploadSpeed: 1,\n nPartsToIncreaseUpload: 1,\n currentSpeed: 1,\n maxSpeed: 1,\n },\n );\n\n const fileInfo = await getFieldValue(pl, outputs.file);\n\n return await downloadClient.withBlobContent(\n fileInfo,\n {},\n {},\n async (content) => await text(content),\n );\n } finally {\n await deleteFields(pl, Object.values(outputs));\n }\n}\n\n/** Runs Go's hello-world binary. */\nexport async function softwareCheck(pl: PlClient): Promise<TemplateReport> {\n try {\n const gotGreeting = await runSoftware(pl);\n\n if (gotGreeting !== \"Hello from go binary\\n\") {\n return { status: \"failed\", message: `Software check failed: got: ${gotGreeting}` };\n }\n return { status: \"ok\", message: `Software check succeeded: ${gotGreeting}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `Software check failed: error occurred: ${e}` };\n }\n}\n\nexport async function runSoftware(pl: PlClient): Promise<string> {\n const result = await runTemplate(\n pl,\n SdkTemplates[\"check_network.run_hello_world\"],\n true,\n (_: PlTransaction) => ({}),\n [\"greeting\"],\n );\n\n try {\n return notEmpty((await getFieldValue(pl, result.greeting)).data?.toString());\n } finally {\n await deleteFields(pl, Object.values(result));\n }\n}\n\n/** Runs Python hello-world. */\nexport async function pythonSoftware(pl: PlClient, name: string): Promise<TemplateReport> {\n try {\n const gotGreeting = await runPythonSoftware(pl, name);\n\n if (gotGreeting !== `Hello, ${name}!\\n`) {\n return { status: \"failed\", message: `Python software check failed: got: ${gotGreeting}` };\n }\n return { status: \"ok\", message: `Python software check succeeded: ${gotGreeting}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `Python software check failed: error occurred: ${e}` };\n }\n}\n\nexport async function runPythonSoftware(pl: PlClient, name: string): Promise<string> {\n const result = await runTemplate(\n pl,\n SdkTemplates[\"check_network.run_hello_world_py\"],\n true,\n (tx) => ({ name: tx.createValue(Pl.JsonObject, JSON.stringify(name)) }),\n [\"greeting\"],\n );\n\n try {\n return notEmpty((await getFieldValue(pl, result.greeting)).data?.toString());\n } finally {\n await deleteFields(pl, Object.values(result));\n }\n}\n\n/** Tries to download a file from every storage. */\nexport async function downloadFromEveryStorage(\n logger: MiLogger,\n pl: PlClient,\n lsDriver: LsDriver,\n ops: {\n minLsRequests: number;\n bytesLimit: number;\n minFileSize: number;\n maxFileSize: number;\n nFilesToCheck: number;\n },\n): Promise<Record<string, TemplateReport>> {\n try {\n const storages = await lsDriver.getStorageList();\n const results: Record<string, TemplateReport> = {};\n\n for (const storage of storages) {\n const result = await chooseFile(\n lsDriver,\n storage,\n ops.nFilesToCheck,\n ops.minFileSize,\n ops.maxFileSize,\n ops.minLsRequests,\n );\n if (result.file === undefined) {\n results[storage.name] = {\n status: \"warn\",\n message:\n `No file between ${ops.minFileSize} and ${ops.maxFileSize} bytes ` +\n `found in storage ${storage.name}, checked ${result.nCheckedFiles} files, ` +\n `did ${result.nLsRequests} ls requests`,\n };\n continue;\n }\n\n logger.info(`Downloading file ${JSON.stringify(result)} from storage ${storage.name}`);\n const outputs = await runTemplate(\n pl,\n SdkTemplates[\"check_network.create_workdir_from_storage\"],\n true,\n (tx) => ({\n file: tx.createValue(\n Pl.JsonObject,\n JSON.stringify((result.file as { handle: string }).handle),\n ),\n }),\n [\"workdirTypeName\"],\n );\n\n try {\n const workdirTypeName = JSON.parse(\n Buffer.from((await getFieldValue(pl, outputs.workdirTypeName)).data!).toString(),\n ) as string;\n\n if (workdirTypeName?.startsWith(\"WorkingDirectory\")) {\n results[storage.name] = {\n status: \"ok\",\n message:\n `Workdir creation succeeded, size of file: ${result.file?.size}, ` +\n `checked ${result.nCheckedFiles} files, did ${result.nLsRequests} ls requests`,\n };\n } else {\n results[storage.name] = {\n status: \"failed\",\n message:\n `Workdir creation failed: ${workdirTypeName}, size of file: ${result.file?.size}, ` +\n `checked ${result.nCheckedFiles} files, did ${result.nLsRequests} ls requests`,\n };\n }\n } finally {\n await deleteFields(pl, Object.values(outputs));\n }\n }\n\n return results;\n } catch (e: unknown) {\n return {\n unknown: {\n status: \"failed\",\n message: `Download from every storage failed: error occurred: ${e}`,\n },\n };\n }\n}\n\n/** Chooses a random file from the storage in a size range.\n * If we couldn't find a normal-sized file, we'll return a small file to check at least something.\n */\nexport async function chooseFile(\n lsDriver: LsDriver,\n storage: StorageEntry,\n limit: number,\n minSize: number,\n maxSize: number,\n minLsRequests: number,\n): Promise<{\n file: LsEntryWithAdditionalInfo | undefined;\n nLsRequests: number;\n nCheckedFiles: number;\n}> {\n const files = listFilesSequence(lsDriver, storage, \"\", 0);\n\n // return small file in case we don't have many normal-sized files.\n // While we'll download only a small range of bytes from the file,\n // we don't want to return a big file in case the underlying S3 doesn't support range requests.\n let smallFile: LsEntryWithAdditionalInfo | undefined;\n let nCheckedFiles = 0;\n let maxNLsRequests = 0;\n\n for await (const { file, nLsRequests } of files) {\n maxNLsRequests = Math.max(maxNLsRequests, nLsRequests);\n\n if (nCheckedFiles >= limit && maxNLsRequests > minLsRequests) {\n // we reached a limit on both the number of files and the number of ls requests.\n return { file: smallFile, nLsRequests: maxNLsRequests, nCheckedFiles };\n }\n nCheckedFiles++;\n if (minSize <= file.size && file.size <= maxSize) {\n return { file, nLsRequests: maxNLsRequests, nCheckedFiles };\n } else if (file.size < minSize) {\n smallFile = file;\n }\n }\n\n return { file: smallFile, nLsRequests: maxNLsRequests, nCheckedFiles };\n}\n\n/** Deep-first search for files in the storage. */\nexport async function* listFilesSequence(\n lsDriver: LsDriver,\n storage: StorageEntry,\n parent: string,\n nLsRequests: number,\n): AsyncGenerator<{ file: LsEntryWithAdditionalInfo; nLsRequests: number }, void, unknown> {\n nLsRequests++;\n const files = await lsDriver.listRemoteFilesWithAdditionalInfo(storage.handle, parent);\n\n for (const file of files.entries) {\n if (file.type === \"file\" && file.fullPath.startsWith(parent)) {\n yield {\n file,\n nLsRequests,\n };\n } else if (file.type === \"dir\") {\n for await (const nestedFile of listFilesSequence(\n lsDriver,\n storage,\n file.fullPath,\n nLsRequests,\n )) {\n nLsRequests = Math.max(nestedFile.nLsRequests, nLsRequests);\n yield nestedFile;\n }\n }\n }\n}\n\n/** Creates a big temporary file with random content. */\nexport async function createBigTempFile(): Promise<{ filePath: string }> {\n const filePath = path.join(os.tmpdir(), `check-network-big-temp-${Date.now()}.bin`);\n const fileSize = 20 * 1024 * 1024; // 20 MiB\n\n const fileContent = randomBytes(fileSize);\n\n await fs.appendFile(filePath, fileContent);\n\n return { filePath };\n}\n\n/** Creates a temporarly file we could use for uploading and downloading. */\nexport async function createTempFile(): Promise<{ filePath: string; fileContent: string }> {\n const filePath = path.join(os.tmpdir(), `check-network-temp-${Date.now()}.txt`);\n\n const fileContent = \"Hello, world! \" + new Date().toISOString();\n await fs.writeFile(filePath, fileContent);\n\n return { filePath, fileContent };\n}\n\n/** Creates a template and RenderTemplate resources, gets all resources from outputs.\n * Throws a error if any of the outputs failed.\n */\nasync function runTemplate(\n client: PlClient,\n tpl: TemplateSpecAny,\n ephemeral: boolean,\n inputs: (tx: PlTransaction) => Pl.PlRecord,\n outputs: string[],\n): Promise<Record<string, FieldId>> {\n return await client.withWriteTx(\"TemplateRender\", async (tx) => {\n const preparedTemplate = await prepareTemplateSpec(tpl);\n const tplResource = loadTemplate(tx, preparedTemplate);\n\n const outputFields: Record<string, FieldRef> = createRenderTemplate(\n tx,\n tplResource,\n ephemeral,\n inputs(tx),\n outputs,\n );\n\n const outputsIds: Record<string, FieldId> = {};\n\n for (const output of outputs) {\n const fieldRef = field(client.clientRoot, output);\n tx.createField(fieldRef, \"Dynamic\", outputFields[output]);\n outputsIds[output] = await toGlobalFieldId(fieldRef);\n }\n\n await tx.commit();\n\n return outputsIds;\n });\n}\n\n/** Gets a resource from field's value or throws a error. */\nasync function getFieldValue(client: PlClient, fieldId: FieldId): Promise<ResourceData> {\n // We could also do polling with pl-tree, but it seemed like an overkill,\n // that's why we have a simple polling here.\n\n return await poll(client, async (tx) => {\n const field = await tx.tx.getField(fieldId);\n if (isNotNullResourceId(field.error)) {\n const err = await tx.tx.getResourceData(field.error, true);\n throw new Error(`getFieldValue of \"${fieldId.fieldName}\" field failed: ${err.data}`);\n }\n\n if (isNullResourceId(field.value)) {\n throw new ContinuePolling();\n }\n\n return await tx.tx.getResourceData(field.value, true);\n });\n}\n\nasync function deleteFields(client: PlClient, fieldIds: FieldId[]) {\n await client.withWriteTx(\"DeleteFields\", async (tx) => {\n for (const fieldId of fieldIds) {\n tx.resetField(fieldId);\n }\n await tx.commit();\n });\n}\n"],"mappings":"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"}
|
|
@@ -9,7 +9,6 @@ import { Templates } from "@platforma-sdk/workflow-tengo";
|
|
|
9
9
|
import { ImportFileHandleUploadData, isSignMatch, isUpload, uploadBlob } from "@milaboratories/pl-drivers";
|
|
10
10
|
import { text } from "node:stream/consumers";
|
|
11
11
|
import os from "node:os";
|
|
12
|
-
|
|
13
12
|
//#region src/network_check/template.ts
|
|
14
13
|
/** Uploads `hello-world` template and checks the output is correct. */
|
|
15
14
|
async function uploadTemplate(logger, pl, name) {
|
|
@@ -297,7 +296,7 @@ async function deleteFields(client, fieldIds) {
|
|
|
297
296
|
await tx.commit();
|
|
298
297
|
});
|
|
299
298
|
}
|
|
300
|
-
|
|
301
299
|
//#endregion
|
|
302
300
|
export { createBigTempFile, createTempFile, downloadFile, downloadFromEveryStorage, pythonSoftware, softwareCheck, uploadFile, uploadTemplate };
|
|
301
|
+
|
|
303
302
|
//# sourceMappingURL=template.js.map
|