@milaboratories/pl-drivers 1.2.16

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.
Files changed (117) hide show
  1. package/README.md +18 -0
  2. package/dist/clients/download.d.ts +30 -0
  3. package/dist/clients/download.d.ts.map +1 -0
  4. package/dist/clients/helpers.d.ts +14 -0
  5. package/dist/clients/helpers.d.ts.map +1 -0
  6. package/dist/clients/logs.d.ts +26 -0
  7. package/dist/clients/logs.d.ts.map +1 -0
  8. package/dist/clients/ls_api.d.ts +13 -0
  9. package/dist/clients/ls_api.d.ts.map +1 -0
  10. package/dist/clients/progress.d.ts +25 -0
  11. package/dist/clients/progress.d.ts.map +1 -0
  12. package/dist/clients/upload.d.ts +38 -0
  13. package/dist/clients/upload.d.ts.map +1 -0
  14. package/dist/drivers/download_and_logs_blob.d.ts +106 -0
  15. package/dist/drivers/download_and_logs_blob.d.ts.map +1 -0
  16. package/dist/drivers/download_url.d.ts +70 -0
  17. package/dist/drivers/download_url.d.ts.map +1 -0
  18. package/dist/drivers/helpers/files_cache.d.ts +28 -0
  19. package/dist/drivers/helpers/files_cache.d.ts.map +1 -0
  20. package/dist/drivers/helpers/helpers.d.ts +34 -0
  21. package/dist/drivers/helpers/helpers.d.ts.map +1 -0
  22. package/dist/drivers/helpers/ls_list_entry.d.ts +49 -0
  23. package/dist/drivers/helpers/ls_list_entry.d.ts.map +1 -0
  24. package/dist/drivers/helpers/ls_storage_entry.d.ts +25 -0
  25. package/dist/drivers/helpers/ls_storage_entry.d.ts.map +1 -0
  26. package/dist/drivers/helpers/polling_ops.d.ts +8 -0
  27. package/dist/drivers/helpers/polling_ops.d.ts.map +1 -0
  28. package/dist/drivers/helpers/test_helpers.d.ts +2 -0
  29. package/dist/drivers/helpers/test_helpers.d.ts.map +1 -0
  30. package/dist/drivers/logs.d.ts +29 -0
  31. package/dist/drivers/logs.d.ts.map +1 -0
  32. package/dist/drivers/logs_stream.d.ts +50 -0
  33. package/dist/drivers/logs_stream.d.ts.map +1 -0
  34. package/dist/drivers/ls.d.ts +30 -0
  35. package/dist/drivers/ls.d.ts.map +1 -0
  36. package/dist/drivers/upload.d.ts +87 -0
  37. package/dist/drivers/upload.d.ts.map +1 -0
  38. package/dist/helpers/download.d.ts +15 -0
  39. package/dist/helpers/download.d.ts.map +1 -0
  40. package/dist/index.cjs +2 -0
  41. package/dist/index.cjs.map +1 -0
  42. package/dist/index.d.ts +15 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +4627 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client.d.ts +36 -0
  47. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client.d.ts.map +1 -0
  48. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.d.ts +103 -0
  49. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.d.ts.map +1 -0
  50. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.client.d.ts +42 -0
  51. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.client.d.ts.map +1 -0
  52. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.d.ts +165 -0
  53. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.d.ts.map +1 -0
  54. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client.d.ts +44 -0
  55. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client.d.ts.map +1 -0
  56. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.d.ts +171 -0
  57. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.d.ts.map +1 -0
  58. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client.d.ts +122 -0
  59. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client.d.ts.map +1 -0
  60. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.d.ts +315 -0
  61. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.d.ts.map +1 -0
  62. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.client.d.ts +98 -0
  63. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.client.d.ts.map +1 -0
  64. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.d.ts +337 -0
  65. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.d.ts.map +1 -0
  66. package/dist/proto/google/api/http.d.ts +451 -0
  67. package/dist/proto/google/api/http.d.ts.map +1 -0
  68. package/dist/proto/google/protobuf/descriptor.d.ts +1646 -0
  69. package/dist/proto/google/protobuf/descriptor.d.ts.map +1 -0
  70. package/dist/proto/google/protobuf/duration.d.ts +106 -0
  71. package/dist/proto/google/protobuf/duration.d.ts.map +1 -0
  72. package/dist/proto/google/protobuf/timestamp.d.ts +151 -0
  73. package/dist/proto/google/protobuf/timestamp.d.ts.map +1 -0
  74. package/package.json +47 -0
  75. package/src/clients/download.test.ts +45 -0
  76. package/src/clients/download.ts +106 -0
  77. package/src/clients/helpers.ts +84 -0
  78. package/src/clients/logs.ts +68 -0
  79. package/src/clients/ls_api.ts +34 -0
  80. package/src/clients/progress.ts +86 -0
  81. package/src/clients/upload.test.ts +30 -0
  82. package/src/clients/upload.ts +199 -0
  83. package/src/drivers/download_and_logs_blob.ts +801 -0
  84. package/src/drivers/download_blob.test.ts +223 -0
  85. package/src/drivers/download_url.test.ts +90 -0
  86. package/src/drivers/download_url.ts +314 -0
  87. package/src/drivers/helpers/files_cache.test.ts +79 -0
  88. package/src/drivers/helpers/files_cache.ts +74 -0
  89. package/src/drivers/helpers/helpers.ts +136 -0
  90. package/src/drivers/helpers/ls_list_entry.test.ts +57 -0
  91. package/src/drivers/helpers/ls_list_entry.ts +152 -0
  92. package/src/drivers/helpers/ls_storage_entry.ts +135 -0
  93. package/src/drivers/helpers/polling_ops.ts +7 -0
  94. package/src/drivers/helpers/test_helpers.ts +5 -0
  95. package/src/drivers/logs.test.ts +337 -0
  96. package/src/drivers/logs.ts +214 -0
  97. package/src/drivers/logs_stream.ts +399 -0
  98. package/src/drivers/ls.test.ts +90 -0
  99. package/src/drivers/ls.ts +147 -0
  100. package/src/drivers/upload.test.ts +454 -0
  101. package/src/drivers/upload.ts +499 -0
  102. package/src/helpers/download.ts +43 -0
  103. package/src/index.ts +15 -0
  104. package/src/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client.ts +60 -0
  105. package/src/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.ts +442 -0
  106. package/src/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.client.ts +63 -0
  107. package/src/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.ts +503 -0
  108. package/src/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client.ts +84 -0
  109. package/src/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.ts +697 -0
  110. package/src/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client.ts +212 -0
  111. package/src/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.ts +1036 -0
  112. package/src/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.client.ts +170 -0
  113. package/src/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.ts +1201 -0
  114. package/src/proto/google/api/http.ts +838 -0
  115. package/src/proto/google/protobuf/descriptor.ts +5173 -0
  116. package/src/proto/google/protobuf/duration.ts +272 -0
  117. package/src/proto/google/protobuf/timestamp.ts +354 -0
@@ -0,0 +1,337 @@
1
+ import {
2
+ PlClient,
3
+ PlTransaction,
4
+ ResourceType,
5
+ TestHelpers,
6
+ jsonToData,
7
+ FieldRef,
8
+ FieldId,
9
+ AnyFieldRef,
10
+ ResourceRef,
11
+ stringifyWithResourceId,
12
+ ResourceId
13
+ } from '@milaboratories/pl-client';
14
+ import {
15
+ ConsoleLoggerAdapter,
16
+ HmacSha256Signer,
17
+ notEmpty
18
+ } from '@milaboratories/ts-helpers';
19
+ import { scheduler } from 'node:timers/promises';
20
+ import { Computable } from '@milaboratories/computable';
21
+ import * as os from 'node:os';
22
+ import * as fsp from 'node:fs/promises';
23
+ import * as path from 'node:path';
24
+ import { SynchronizedTreeState } from '@milaboratories/pl-tree';
25
+ import { DownloadDriver } from './download_and_logs_blob';
26
+ import { createDownloadClient, createLogsClient } from '../clients/helpers';
27
+ import { LogsStreamDriver } from './logs_stream';
28
+ import { LogsDriver } from './logs';
29
+
30
+ test('should get all logs', async () => {
31
+ await TestHelpers.withTempRoot(async (client) => {
32
+ const logger = new ConsoleLoggerAdapter();
33
+
34
+ const tree = await SynchronizedTreeState.init(client, client.clientRoot, {
35
+ stopPollingDelay: 10,
36
+ pollingInterval: 10
37
+ });
38
+ const logsStream = new LogsStreamDriver(createLogsClient(client, logger));
39
+ const dir = await fsp.mkdtemp(path.join(os.tmpdir(), 'test-logs-1-'));
40
+ const download = new DownloadDriver(
41
+ logger,
42
+ createDownloadClient(logger, client),
43
+ createLogsClient(client, logger),
44
+ dir,
45
+ new HmacSha256Signer(HmacSha256Signer.generateSecret()),
46
+ { cacheSoftSizeBytes: 700 * 1024, nConcurrentDownloads: 10 }
47
+ );
48
+ const logs = new LogsDriver(logsStream, download);
49
+
50
+ await createRunCommandWithStdoutStream(client, 'bash', [
51
+ '-c',
52
+ 'echo 1; sleep 1; echo 2'
53
+ ]);
54
+
55
+ const c = Computable.make((ctx) => {
56
+ const streamManager = ctx
57
+ .accessor(tree.entry())
58
+ .node()
59
+ .traverse('result')
60
+ ?.persist();
61
+ if (streamManager === undefined) {
62
+ ctx.markUnstable('no stream manager');
63
+ return;
64
+ }
65
+
66
+ return logs.getLastLogs(streamManager, 100, ctx);
67
+ });
68
+
69
+ while (true) {
70
+ await c.awaitChange();
71
+ const result = await c.getFullValue();
72
+
73
+ logger.info(`got result: ${JSON.stringify(result)}`);
74
+ if (result.stable) {
75
+ expect(result.value).toStrictEqual('1\n2\n');
76
+ return;
77
+ }
78
+ }
79
+ });
80
+ });
81
+
82
+ test('should get last line with a prefix', async () => {
83
+ await TestHelpers.withTempRoot(async (client) => {
84
+ const logger = new ConsoleLoggerAdapter();
85
+
86
+ const tree = await SynchronizedTreeState.init(client, client.clientRoot, {
87
+ stopPollingDelay: 10,
88
+ pollingInterval: 10
89
+ });
90
+ const logsStream = new LogsStreamDriver(createLogsClient(client, logger));
91
+ const dir = await fsp.mkdtemp(path.join(os.tmpdir(), 'test-logs-2-'));
92
+ const download = new DownloadDriver(
93
+ logger,
94
+ createDownloadClient(logger, client),
95
+ createLogsClient(client, logger),
96
+ dir,
97
+ new HmacSha256Signer(HmacSha256Signer.generateSecret()),
98
+ { cacheSoftSizeBytes: 700 * 1024, nConcurrentDownloads: 10 }
99
+ );
100
+ const logs = new LogsDriver(logsStream, download);
101
+
102
+ const c = Computable.make((ctx) => {
103
+ const streamManager = ctx
104
+ .accessor(tree.entry())
105
+ .node()
106
+ .traverse('result')
107
+ ?.persist();
108
+ if (streamManager === undefined) {
109
+ ctx.markUnstable('no stream manager');
110
+ return;
111
+ }
112
+
113
+ return logs.getProgressLog(streamManager, 'PREFIX', ctx);
114
+ });
115
+
116
+ expect(await c.getValue()).toBeUndefined();
117
+
118
+ await createRunCommandWithStdoutStream(client, 'bash', [
119
+ '-c',
120
+ 'echo PREFIX1; echo PREFIX2; echo 3; sleep 0.1; echo PREFIX4'
121
+ ]);
122
+
123
+ while (true) {
124
+ await c.awaitChange();
125
+ const result = await c.getFullValue();
126
+
127
+ logger.info(`got result: ${JSON.stringify(result)}`);
128
+ if (result.stable) {
129
+ expect(result.value).toStrictEqual('PREFIX4\n');
130
+ return;
131
+ }
132
+ }
133
+ });
134
+ });
135
+
136
+ test('should get log smart object and get log lines from that', async () => {
137
+ await TestHelpers.withTempRoot(async (client) => {
138
+ const logger = new ConsoleLoggerAdapter();
139
+
140
+ const tree = await SynchronizedTreeState.init(client, client.clientRoot, {
141
+ stopPollingDelay: 10,
142
+ pollingInterval: 10
143
+ });
144
+ const logsStream = new LogsStreamDriver(createLogsClient(client, logger));
145
+ const dir = await fsp.mkdtemp(path.join(os.tmpdir(), 'test-logs-3-'));
146
+ const download = new DownloadDriver(
147
+ logger,
148
+ createDownloadClient(logger, client),
149
+ createLogsClient(client, logger),
150
+ dir,
151
+ new HmacSha256Signer(HmacSha256Signer.generateSecret()),
152
+ { cacheSoftSizeBytes: 700 * 1024, nConcurrentDownloads: 10 }
153
+ );
154
+ const logs = new LogsDriver(logsStream, download);
155
+
156
+ const c = Computable.make((ctx) => {
157
+ const streamManager = ctx
158
+ .accessor(tree.entry())
159
+ .node()
160
+ .traverse('result')
161
+ ?.persist();
162
+ if (streamManager === undefined) {
163
+ ctx.markUnstable('no stream manager');
164
+ return;
165
+ }
166
+
167
+ return logs.getLogHandle(streamManager, ctx);
168
+ });
169
+
170
+ await createRunCommandWithStdoutStream(client, 'bash', [
171
+ '-c',
172
+ 'echo 1; sleep 1; echo 2'
173
+ ]);
174
+
175
+ let handle = await c.getValue();
176
+
177
+ while (true) {
178
+ await c.awaitChange();
179
+ handle = await c.getValue();
180
+ if (handle != undefined) break;
181
+ }
182
+
183
+ while (true) {
184
+ const response = await logs.readText(notEmpty(handle), 100);
185
+ logger.info(`got response: ${stringifyWithResourceId(response)}`);
186
+ if (response.shouldUpdateHandle) {
187
+ await c.awaitChange();
188
+ handle = await c.getValue();
189
+ continue;
190
+ }
191
+
192
+ if (response.data.toString().length == 4) {
193
+ expect(response.data.toString()).toStrictEqual('1\n2\n');
194
+ return;
195
+ }
196
+
197
+ await scheduler.wait(200);
198
+ }
199
+ });
200
+ });
201
+
202
+ async function createRunCommandWithStdoutStream(
203
+ client: PlClient,
204
+ cmd: string,
205
+ args: string[]
206
+ ): Promise<ResourceId> {
207
+ return await client.withWriteTx(
208
+ 'CreateRunCommandWithStreaming',
209
+ async (tx: PlTransaction) => {
210
+ const wdFId: FieldRef = createWd(tx);
211
+ const workdirOut: FieldRef = createRunCommand(tx, wdFId, cmd, args);
212
+ const blobsOut: FieldRef = createWdSave(tx, workdirOut);
213
+ const downloadableFId = createDownloadableBlobFromStdout(tx, blobsOut);
214
+ const streamManagerId = createStreamManager(tx, wdFId, downloadableFId);
215
+
216
+ const dynamicId: FieldId = {
217
+ resourceId: client.clientRoot,
218
+ fieldName: 'result'
219
+ };
220
+ tx.createField(dynamicId, 'Dynamic', streamManagerId);
221
+
222
+ await tx.commit();
223
+
224
+ return await streamManagerId.globalId;
225
+ }
226
+ );
227
+ }
228
+
229
+ function createWd(tx: PlTransaction): FieldRef {
230
+ const wd = tx.createEphemeral({ name: 'WorkdirCreate', version: '1' });
231
+ return { resourceId: wd, fieldName: 'workdir' };
232
+ }
233
+
234
+ function createRunCommand(
235
+ tx: PlTransaction,
236
+ wdFId: FieldRef,
237
+ cmd: string,
238
+ args: string[]
239
+ ): FieldRef {
240
+ const refsId = tx.createStruct({ name: 'RunCommandRefs', version: '1' });
241
+ tx.lock(refsId);
242
+ const cmdData = {
243
+ type: 'string',
244
+ value: cmd
245
+ };
246
+ const argsData = args.map((arg) => {
247
+ return {
248
+ type: 'string',
249
+ value: arg
250
+ };
251
+ });
252
+ const optsData = {
253
+ queueName: 'heavy',
254
+ errorLines: 200,
255
+ redirectStdout: 'logs.txt',
256
+ redirectStderr: 'logs.txt',
257
+ envs: []
258
+ };
259
+
260
+ const runCmdId = tx.createEphemeral({ name: 'RunCommand/executor', version: '1' });
261
+
262
+ const setInputValue = (fName: string, rType: ResourceType, data: unknown) => {
263
+ const valResId = tx.createValue(rType, jsonToData(data));
264
+ tx.setField({ resourceId: runCmdId, fieldName: fName }, valResId);
265
+ };
266
+
267
+ tx.setField({ resourceId: runCmdId, fieldName: 'workdirIn' }, wdFId);
268
+ tx.setField({ resourceId: runCmdId, fieldName: 'refs' }, refsId);
269
+ setInputValue('cmd', { name: 'RunCommandCmd', version: '1' }, cmdData);
270
+ setInputValue('args', { name: 'RunCommandArgs', version: '1' }, argsData);
271
+ setInputValue(
272
+ 'options',
273
+ { name: 'run-command/options', version: '1' },
274
+ optsData
275
+ );
276
+
277
+ return { resourceId: runCmdId, fieldName: 'workdirOut' };
278
+ }
279
+
280
+ function createWdSave(tx: PlTransaction, workdirOut: FieldRef): FieldRef {
281
+ const wdSave = tx.createEphemeral({ name: 'WorkdirSave', version: '1' });
282
+ const wdSaveRules = tx.createValue(
283
+ { name: 'WorkdirSave/rules', version: '1' },
284
+ jsonToData([
285
+ {
286
+ blobKey: 'logs.txt',
287
+ type: 'file',
288
+ filePath: 'logs.txt'
289
+ }
290
+ ])
291
+ );
292
+ tx.setField({ resourceId: wdSave, fieldName: 'workdirIn' }, workdirOut);
293
+ tx.setField({ resourceId: wdSave, fieldName: 'rules' }, wdSaveRules);
294
+
295
+ return { resourceId: wdSave, fieldName: 'blobsOut' };
296
+ }
297
+
298
+ function createDownloadableBlobFromStdout(
299
+ tx: PlTransaction,
300
+ blobsOut: FieldRef
301
+ ): FieldRef {
302
+ const blobOut = tx.getFutureFieldValue(blobsOut, 'logs.txt', 'Input');
303
+ const blobDownloadId = tx.createStruct({
304
+ name: 'BlobDownload',
305
+ version: '2'
306
+ });
307
+ tx.setField({ resourceId: blobDownloadId, fieldName: 'blob' }, blobOut);
308
+
309
+ return { resourceId: blobDownloadId, fieldName: 'downloadable' };
310
+ }
311
+
312
+ function createStreamManager(
313
+ tx: PlTransaction,
314
+ wdFId: FieldRef,
315
+ downloadableFId: AnyFieldRef
316
+ ): ResourceRef {
317
+ const streamId = tx.createEphemeral({ name: 'CreateStream', version: '2' });
318
+ tx.setField({ resourceId: streamId, fieldName: 'workdir' }, wdFId);
319
+ const filePathId = tx.createValue(
320
+ { name: 'json/string', version: '1' },
321
+ jsonToData('logs.txt')
322
+ );
323
+ tx.setField({ resourceId: streamId, fieldName: 'filePath' }, filePathId);
324
+ const streamFId = { resourceId: streamId, fieldName: 'stream' };
325
+
326
+ const streamManagerId = tx.createEphemeral({
327
+ name: 'StreamManager',
328
+ version: '2'
329
+ });
330
+ tx.setField(
331
+ { resourceId: streamManagerId, fieldName: 'downloadable' },
332
+ downloadableFId
333
+ );
334
+ tx.setField({ resourceId: streamManagerId, fieldName: 'stream' }, streamFId);
335
+
336
+ return streamManagerId;
337
+ }
@@ -0,0 +1,214 @@
1
+ import { Computable, ComputableCtx } from '@milaboratories/computable';
2
+ import { PlTreeEntry, ResourceInfo } from '@milaboratories/pl-tree';
3
+ import { bigintToResourceId } from '@milaboratories/pl-client';
4
+ import { LogsStreamDriver } from './logs_stream';
5
+ import { DownloadDriver } from './download_and_logs_blob';
6
+ import * as sdk from '@milaboratories/pl-model-common';
7
+
8
+ export class LogsDriver implements sdk.LogsDriver {
9
+ constructor(
10
+ private readonly logsStreamDriver: LogsStreamDriver,
11
+ private readonly downloadDriver: DownloadDriver
12
+ ) {}
13
+
14
+ /** Returns all logs and schedules a job that reads remain logs.
15
+ * Notifies when a new portion of the log appeared. */
16
+ getLastLogs(res: PlTreeEntry, lines: number): Computable<string | undefined>;
17
+ getLastLogs(
18
+ res: PlTreeEntry,
19
+ lines: number,
20
+ ctx: ComputableCtx
21
+ ): Computable<string | undefined>;
22
+ getLastLogs(
23
+ res: PlTreeEntry,
24
+ lines: number,
25
+ ctx?: ComputableCtx
26
+ ): Computable<string | undefined> | string | undefined {
27
+ if (ctx === undefined)
28
+ return Computable.make((ctx) => this.getLastLogs(res, lines, ctx));
29
+
30
+ const stream = streamManagerGetStream(ctx, res);
31
+ if (stream === undefined) {
32
+ ctx.markUnstable('no stream in stream manager');
33
+ return undefined;
34
+ }
35
+
36
+ if (isBlob(stream))
37
+ return this.downloadDriver.getLastLogs(stream, lines, ctx);
38
+
39
+ try {
40
+ return this.logsStreamDriver.getLastLogs(stream, lines, ctx);
41
+ } catch (e: any) {
42
+ if (e.name == 'RpcError' && e.code == 'NOT_FOUND') {
43
+ ctx.markUnstable(
44
+ `NOT_FOUND in logs stream driver while getting last logs: ${e}`
45
+ );
46
+ return undefined;
47
+ }
48
+ throw e;
49
+ }
50
+ }
51
+
52
+ /** Returns a last line that has patternToSearch.
53
+ * Notifies when a new line appeared or EOF reached. */
54
+ getProgressLog(
55
+ res: PlTreeEntry,
56
+ patternToSearch: string
57
+ ): Computable<string | undefined>;
58
+ getProgressLog(
59
+ res: PlTreeEntry,
60
+ patternToSearch: string,
61
+ ctx: ComputableCtx
62
+ ): string | undefined;
63
+ getProgressLog(
64
+ res: PlTreeEntry,
65
+ patternToSearch: string,
66
+ ctx?: ComputableCtx
67
+ ): Computable<string | undefined> | string | undefined {
68
+ if (ctx === undefined)
69
+ return Computable.make((ctx) =>
70
+ this.getProgressLog(res, patternToSearch, ctx)
71
+ );
72
+
73
+ const stream = streamManagerGetStream(ctx, res);
74
+ if (stream === undefined) {
75
+ ctx.markUnstable('no stream in stream manager');
76
+ return undefined;
77
+ }
78
+
79
+ if (isBlob(stream))
80
+ return this.downloadDriver.getProgressLog(stream, patternToSearch, ctx);
81
+
82
+ try {
83
+ return this.logsStreamDriver.getProgressLog(stream, patternToSearch, ctx);
84
+ } catch (e: any) {
85
+ if (e.name == 'RpcError' && e.code == 'NOT_FOUND') {
86
+ ctx.markUnstable(
87
+ `NOT_FOUND in logs stream driver while getting a progress log: ${e}`
88
+ );
89
+ return undefined;
90
+ }
91
+ throw e;
92
+ }
93
+ }
94
+
95
+ /** Returns an Id of a smart object, that can read logs directly from
96
+ * the platform. */
97
+ getLogHandle(
98
+ res: ResourceInfo | PlTreeEntry
99
+ ): Computable<sdk.AnyLogHandle | undefined>;
100
+ getLogHandle(
101
+ res: PlTreeEntry,
102
+ ctx: ComputableCtx
103
+ ): sdk.AnyLogHandle | undefined;
104
+ getLogHandle(
105
+ res: PlTreeEntry,
106
+ ctx?: ComputableCtx
107
+ ): Computable<sdk.AnyLogHandle | undefined> | sdk.AnyLogHandle | undefined {
108
+ if (ctx === undefined)
109
+ return Computable.make((ctx) => this.getLogHandle(res, ctx));
110
+
111
+ const stream = streamManagerGetStream(ctx, res);
112
+ if (stream === undefined) {
113
+ ctx.markUnstable('no stream in stream manager');
114
+ return undefined;
115
+ }
116
+
117
+ if (isBlob(stream)) return this.downloadDriver.getLogHandle(stream, ctx);
118
+
119
+ return this.logsStreamDriver.getLogHandle(stream, ctx);
120
+ }
121
+
122
+ async lastLines(
123
+ handle: sdk.AnyLogHandle,
124
+ lineCount: number,
125
+ offsetBytes?: number,
126
+ searchStr?: string
127
+ ): Promise<sdk.StreamingApiResponse> {
128
+ if (isLiveLogHandle(handle))
129
+ return await this.logsStreamDriver.lastLines(
130
+ handle,
131
+ lineCount,
132
+ offsetBytes,
133
+ searchStr
134
+ );
135
+ return await this.downloadDriver.lastLines(
136
+ handle,
137
+ lineCount,
138
+ offsetBytes,
139
+ searchStr
140
+ );
141
+ }
142
+
143
+ async readText(
144
+ handle: sdk.AnyLogHandle,
145
+ lineCount: number,
146
+ offsetBytes?: number,
147
+ searchStr?: string
148
+ ): Promise<sdk.StreamingApiResponse> {
149
+ if (isLiveLogHandle(handle))
150
+ return await this.logsStreamDriver.readText(
151
+ handle,
152
+ lineCount,
153
+ offsetBytes,
154
+ searchStr
155
+ );
156
+ return await this.downloadDriver.readText(
157
+ handle,
158
+ lineCount,
159
+ offsetBytes,
160
+ searchStr
161
+ );
162
+ }
163
+ }
164
+
165
+ function isBlob(rInfo: ResourceInfo) {
166
+ return !rInfo.type.name.startsWith('StreamWorkdir');
167
+ }
168
+
169
+ function streamManagerGetStream(ctx: ComputableCtx, manager: PlTreeEntry) {
170
+ return ctx.accessor(manager).node().traverse('stream')?.resourceInfo;
171
+ }
172
+
173
+ export function handleToData(handle: sdk.AnyLogHandle): ResourceInfo {
174
+ let parsed: RegExpMatchArray | null;
175
+
176
+ if (isLiveLogHandle(handle)) {
177
+ parsed = handle.match(liveHandleRegex);
178
+ } else if (isReadyLogHandle(handle)) {
179
+ parsed = handle.match(readyHandleRegex);
180
+ } else throw new Error(`Log handle is malformed: ${handle}`);
181
+ if (parsed == null) throw new Error(`Log handle wasn't parsed: ${handle}`);
182
+
183
+ const { resourceType, resourceVersion, resourceId } = parsed.groups!;
184
+
185
+ return {
186
+ id: bigintToResourceId(BigInt(resourceId)),
187
+ type: { name: resourceType, version: resourceVersion }
188
+ };
189
+ }
190
+
191
+ export function dataToHandle(
192
+ live: boolean,
193
+ rInfo: ResourceInfo
194
+ ): sdk.AnyLogHandle {
195
+ if (live) {
196
+ return `log+live://log/${rInfo.type.name}/${rInfo.type.version}/${BigInt(rInfo.id)}` as sdk.LiveLogHandle;
197
+ }
198
+
199
+ return `log+ready://log/${rInfo.type.name}/${rInfo.type.version}/${BigInt(rInfo.id)}` as sdk.ReadyLogHandle;
200
+ }
201
+
202
+ const liveHandleRegex =
203
+ /^log\+live:\/\/log\/(?<resourceType>.*)\/(?<resourceVersion>.*)\/(?<resourceId>.*)$/;
204
+
205
+ export function isLiveLogHandle(handle: string): handle is sdk.LiveLogHandle {
206
+ return liveHandleRegex.test(handle);
207
+ }
208
+
209
+ const readyHandleRegex =
210
+ /^log\+ready:\/\/log\/(?<resourceType>.*)\/(?<resourceVersion>.*)\/(?<resourceId>.*)$/;
211
+
212
+ export function isReadyLogHandle(handle: string): handle is sdk.ReadyLogHandle {
213
+ return readyHandleRegex.test(handle);
214
+ }