@backstage/backend-defaults 0.17.0 → 0.17.1-next.1
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/CHANGELOG.md +33 -0
- package/dist/CreateBackend.cjs.js.map +1 -1
- package/dist/entrypoints/auth/plugin/keys/DatabasePluginKeySource.cjs.js +2 -2
- package/dist/entrypoints/auth/plugin/keys/DatabasePluginKeySource.cjs.js.map +1 -1
- package/dist/entrypoints/scheduler/lib/TaskWorker.cjs.js +2 -2
- package/dist/entrypoints/scheduler/lib/TaskWorker.cjs.js.map +1 -1
- package/dist/entrypoints/scheduler/lib/types.cjs.js +0 -12
- package/dist/entrypoints/scheduler/lib/types.cjs.js.map +1 -1
- package/dist/entrypoints/scheduler/lib/util.cjs.js +19 -5
- package/dist/entrypoints/scheduler/lib/util.cjs.js.map +1 -1
- package/dist/index.cjs.js +1 -0
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/package.json.cjs.js +1 -1
- package/package.json +17 -19
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @backstage/backend-defaults
|
|
2
2
|
|
|
3
|
+
## 0.17.1-next.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- e9b78e9: Removed the `uuid` dependency and replaced usage with the built-in `crypto.randomUUID()`.
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @backstage/cli-node@0.3.2-next.1
|
|
10
|
+
- @backstage/plugin-auth-node@0.7.1-next.1
|
|
11
|
+
- @backstage/plugin-permission-common@0.9.9-next.1
|
|
12
|
+
|
|
13
|
+
## 0.17.1-next.0
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- 3595c97: Exported `defaultServiceFactories` to allow use with `createSpecializedBackend` for advanced configuration like `extensionPointFactoryMiddleware`.
|
|
18
|
+
- 89d3248: Fixed scheduler `sleep` firing immediately for durations longer than ~24.8 days, caused by Node.js `setTimeout` overflowing its 32-bit millisecond limit.
|
|
19
|
+
- 744fa1f: Removed duplicated entries that appeared in both `dependencies` and `devDependencies`.
|
|
20
|
+
- Updated dependencies
|
|
21
|
+
- @backstage/errors@1.3.1-next.0
|
|
22
|
+
- @backstage/backend-app-api@1.7.0-next.0
|
|
23
|
+
- @backstage/integration@2.0.2-next.0
|
|
24
|
+
- @backstage/plugin-auth-node@0.7.1-next.0
|
|
25
|
+
- @backstage/backend-plugin-api@1.9.1-next.0
|
|
26
|
+
- @backstage/cli-node@0.3.2-next.0
|
|
27
|
+
- @backstage/config@1.3.8-next.0
|
|
28
|
+
- @backstage/config-loader@1.10.11-next.0
|
|
29
|
+
- @backstage/integration-aws-node@0.1.22-next.0
|
|
30
|
+
- @backstage/plugin-events-node@0.4.22-next.0
|
|
31
|
+
- @backstage/plugin-permission-common@0.9.9-next.0
|
|
32
|
+
- @backstage/plugin-permission-node@0.10.13-next.0
|
|
33
|
+
- @backstage/backend-dev-utils@0.1.7
|
|
34
|
+
- @backstage/types@1.2.2
|
|
35
|
+
|
|
3
36
|
## 0.17.0
|
|
4
37
|
|
|
5
38
|
### Minor Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreateBackend.cjs.js","sources":["../src/CreateBackend.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Backend, createSpecializedBackend } from '@backstage/backend-app-api';\nimport { auditorServiceFactory } from '@backstage/backend-defaults/auditor';\nimport { authServiceFactory } from '@backstage/backend-defaults/auth';\nimport { cacheServiceFactory } from '@backstage/backend-defaults/cache';\nimport { databaseServiceFactory } from '@backstage/backend-defaults/database';\nimport { discoveryServiceFactory } from '@backstage/backend-defaults/discovery';\nimport { httpAuthServiceFactory } from '@backstage/backend-defaults/httpAuth';\nimport { httpRouterServiceFactory } from '@backstage/backend-defaults/httpRouter';\nimport { lifecycleServiceFactory } from '@backstage/backend-defaults/lifecycle';\nimport { loggerServiceFactory } from '@backstage/backend-defaults/logger';\nimport { permissionsServiceFactory } from '@backstage/backend-defaults/permissions';\nimport { permissionsRegistryServiceFactory } from '@backstage/backend-defaults/permissionsRegistry';\nimport { rootConfigServiceFactory } from '@backstage/backend-defaults/rootConfig';\nimport { rootHealthServiceFactory } from '@backstage/backend-defaults/rootHealth';\nimport { rootHttpRouterServiceFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport { rootLifecycleServiceFactory } from '@backstage/backend-defaults/rootLifecycle';\nimport { rootLoggerServiceFactory } from '@backstage/backend-defaults/rootLogger';\nimport { schedulerServiceFactory } from '@backstage/backend-defaults/scheduler';\nimport { urlReaderServiceFactory } from '@backstage/backend-defaults/urlReader';\nimport { userInfoServiceFactory } from '@backstage/backend-defaults/userInfo';\nimport { eventsServiceFactory } from '@backstage/plugin-events-node';\nimport {\n actionsRegistryServiceFactory,\n actionsServiceFactory,\n metricsServiceFactory,\n} from '@backstage/backend-defaults/alpha';\nimport { instanceMetadataServiceFactory } from './alpha/entrypoints/instanceMetadata/instanceMetadataServiceFactory';\n\nexport const defaultServiceFactories = [\n auditorServiceFactory,\n authServiceFactory,\n cacheServiceFactory,\n rootConfigServiceFactory,\n databaseServiceFactory,\n discoveryServiceFactory,\n httpAuthServiceFactory,\n httpRouterServiceFactory,\n lifecycleServiceFactory,\n loggerServiceFactory,\n permissionsServiceFactory,\n permissionsRegistryServiceFactory,\n rootHealthServiceFactory,\n rootHttpRouterServiceFactory,\n rootLifecycleServiceFactory,\n rootLoggerServiceFactory,\n schedulerServiceFactory,\n userInfoServiceFactory,\n urlReaderServiceFactory,\n eventsServiceFactory,\n\n // alpha services\n actionsRegistryServiceFactory,\n actionsServiceFactory,\n metricsServiceFactory,\n\n // Unexported alpha services kept around for compatibility reasons\n instanceMetadataServiceFactory,\n];\n\n/**\n * @public\n */\nexport function createBackend(): Backend {\n return createSpecializedBackend({ defaultServiceFactories });\n}\n"],"names":["auditorServiceFactory","authServiceFactory","cacheServiceFactory","rootConfigServiceFactory","databaseServiceFactory","discoveryServiceFactory","httpAuthServiceFactory","httpRouterServiceFactory","lifecycleServiceFactory","loggerServiceFactory","permissionsServiceFactory","permissionsRegistryServiceFactory","rootHealthServiceFactory","rootHttpRouterServiceFactory","rootLifecycleServiceFactory","rootLoggerServiceFactory","schedulerServiceFactory","userInfoServiceFactory","urlReaderServiceFactory","eventsServiceFactory","actionsRegistryServiceFactory","actionsServiceFactory","metricsServiceFactory","instanceMetadataServiceFactory","createSpecializedBackend"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"CreateBackend.cjs.js","sources":["../src/CreateBackend.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Backend, createSpecializedBackend } from '@backstage/backend-app-api';\nimport { ServiceFactory } from '@backstage/backend-plugin-api';\nimport { auditorServiceFactory } from '@backstage/backend-defaults/auditor';\nimport { authServiceFactory } from '@backstage/backend-defaults/auth';\nimport { cacheServiceFactory } from '@backstage/backend-defaults/cache';\nimport { databaseServiceFactory } from '@backstage/backend-defaults/database';\nimport { discoveryServiceFactory } from '@backstage/backend-defaults/discovery';\nimport { httpAuthServiceFactory } from '@backstage/backend-defaults/httpAuth';\nimport { httpRouterServiceFactory } from '@backstage/backend-defaults/httpRouter';\nimport { lifecycleServiceFactory } from '@backstage/backend-defaults/lifecycle';\nimport { loggerServiceFactory } from '@backstage/backend-defaults/logger';\nimport { permissionsServiceFactory } from '@backstage/backend-defaults/permissions';\nimport { permissionsRegistryServiceFactory } from '@backstage/backend-defaults/permissionsRegistry';\nimport { rootConfigServiceFactory } from '@backstage/backend-defaults/rootConfig';\nimport { rootHealthServiceFactory } from '@backstage/backend-defaults/rootHealth';\nimport { rootHttpRouterServiceFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport { rootLifecycleServiceFactory } from '@backstage/backend-defaults/rootLifecycle';\nimport { rootLoggerServiceFactory } from '@backstage/backend-defaults/rootLogger';\nimport { schedulerServiceFactory } from '@backstage/backend-defaults/scheduler';\nimport { urlReaderServiceFactory } from '@backstage/backend-defaults/urlReader';\nimport { userInfoServiceFactory } from '@backstage/backend-defaults/userInfo';\nimport { eventsServiceFactory } from '@backstage/plugin-events-node';\nimport {\n actionsRegistryServiceFactory,\n actionsServiceFactory,\n metricsServiceFactory,\n} from '@backstage/backend-defaults/alpha';\nimport { instanceMetadataServiceFactory } from './alpha/entrypoints/instanceMetadata/instanceMetadataServiceFactory';\n\n/** @public */\nexport const defaultServiceFactories: ServiceFactory[] = [\n auditorServiceFactory,\n authServiceFactory,\n cacheServiceFactory,\n rootConfigServiceFactory,\n databaseServiceFactory,\n discoveryServiceFactory,\n httpAuthServiceFactory,\n httpRouterServiceFactory,\n lifecycleServiceFactory,\n loggerServiceFactory,\n permissionsServiceFactory,\n permissionsRegistryServiceFactory,\n rootHealthServiceFactory,\n rootHttpRouterServiceFactory,\n rootLifecycleServiceFactory,\n rootLoggerServiceFactory,\n schedulerServiceFactory,\n userInfoServiceFactory,\n urlReaderServiceFactory,\n eventsServiceFactory,\n\n // alpha services\n actionsRegistryServiceFactory,\n actionsServiceFactory,\n metricsServiceFactory,\n\n // Unexported alpha services kept around for compatibility reasons\n instanceMetadataServiceFactory,\n];\n\n/**\n * @public\n */\nexport function createBackend(): Backend {\n return createSpecializedBackend({ defaultServiceFactories });\n}\n"],"names":["auditorServiceFactory","authServiceFactory","cacheServiceFactory","rootConfigServiceFactory","databaseServiceFactory","discoveryServiceFactory","httpAuthServiceFactory","httpRouterServiceFactory","lifecycleServiceFactory","loggerServiceFactory","permissionsServiceFactory","permissionsRegistryServiceFactory","rootHealthServiceFactory","rootHttpRouterServiceFactory","rootLifecycleServiceFactory","rootLoggerServiceFactory","schedulerServiceFactory","userInfoServiceFactory","urlReaderServiceFactory","eventsServiceFactory","actionsRegistryServiceFactory","actionsServiceFactory","metricsServiceFactory","instanceMetadataServiceFactory","createSpecializedBackend"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA8CO,MAAM,uBAAA,GAA4C;AAAA,EACvDA,6BAAA;AAAA,EACAC,uBAAA;AAAA,EACAC,yBAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,+BAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,+BAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,2BAAA;AAAA,EACAC,qCAAA;AAAA,EACAC,qDAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,2CAAA;AAAA,EACAC,yCAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,+BAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,qCAAA;AAAA;AAAA,EAGAC,mCAAA;AAAA,EACAC,2BAAA;AAAA,EACAC,2BAAA;AAAA;AAAA,EAGAC;AACF;AAKO,SAAS,aAAA,GAAyB;AACvC,EAAA,OAAOC,sCAAA,CAAyB,EAAE,uBAAA,EAAyB,CAAA;AAC7D;;;;;"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var types = require('@backstage/types');
|
|
4
4
|
var jose = require('jose');
|
|
5
|
-
var
|
|
5
|
+
var node_crypto = require('node:crypto');
|
|
6
6
|
var DatabaseKeyStore = require('./DatabaseKeyStore.cjs.js');
|
|
7
7
|
|
|
8
8
|
const SECONDS_IN_MS = 1e3;
|
|
@@ -44,7 +44,7 @@ class DatabasePluginKeySource {
|
|
|
44
44
|
Date.now() + this.keyDurationSeconds * SECONDS_IN_MS
|
|
45
45
|
);
|
|
46
46
|
const promise = (async () => {
|
|
47
|
-
const kid =
|
|
47
|
+
const kid = node_crypto.randomUUID();
|
|
48
48
|
const key = await jose.generateKeyPair(this.algorithm);
|
|
49
49
|
const publicKey = await jose.exportJWK(key.publicKey);
|
|
50
50
|
const privateKey = await jose.exportJWK(key.privateKey);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatabasePluginKeySource.cjs.js","sources":["../../../../../src/entrypoints/auth/plugin/keys/DatabasePluginKeySource.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DatabaseService, LoggerService } from '@backstage/backend-plugin-api';\nimport { HumanDuration, durationToMilliseconds } from '@backstage/types';\nimport { JWK, exportJWK, generateKeyPair } from 'jose';\nimport {
|
|
1
|
+
{"version":3,"file":"DatabasePluginKeySource.cjs.js","sources":["../../../../../src/entrypoints/auth/plugin/keys/DatabasePluginKeySource.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DatabaseService, LoggerService } from '@backstage/backend-plugin-api';\nimport { HumanDuration, durationToMilliseconds } from '@backstage/types';\nimport { JWK, exportJWK, generateKeyPair } from 'jose';\nimport { randomUUID as uuid } from 'node:crypto';\nimport { DatabaseKeyStore } from './DatabaseKeyStore';\nimport { InternalKey, KeyPayload, KeyStore } from './types';\nimport { PluginKeySource } from './types';\n\nconst SECONDS_IN_MS = 1000;\n\n/**\n * The margin for how many times longer we make the public key available\n * compared to how long we use the private key to sign new tokens.\n */\nconst KEY_EXPIRATION_MARGIN_FACTOR = 3;\n\nexport class DatabasePluginKeySource implements PluginKeySource {\n private privateKeyPromise?: Promise<JWK>;\n private keyExpiry?: Date;\n private readonly keyStore: KeyStore;\n private readonly logger: LoggerService;\n private readonly keyDurationSeconds: number;\n private readonly algorithm: string;\n\n constructor(\n keyStore: KeyStore,\n logger: LoggerService,\n keyDurationSeconds: number,\n algorithm: string,\n ) {\n this.keyStore = keyStore;\n this.logger = logger;\n this.keyDurationSeconds = keyDurationSeconds;\n this.algorithm = algorithm;\n }\n\n public static async create(options: {\n logger: LoggerService;\n database: DatabaseService;\n keyDuration: HumanDuration;\n algorithm?: string;\n }): Promise<PluginKeySource> {\n const keyStore = await DatabaseKeyStore.create({\n database: options.database,\n logger: options.logger,\n });\n\n return new DatabasePluginKeySource(\n keyStore,\n options.logger,\n Math.round(durationToMilliseconds(options.keyDuration) / 1000),\n options.algorithm ?? 'ES256',\n );\n }\n\n async getPrivateSigningKey(): Promise<JWK> {\n // Make sure that we only generate one key at a time\n if (this.privateKeyPromise) {\n if (this.keyExpiry && this.keyExpiry.getTime() > Date.now()) {\n return this.privateKeyPromise;\n }\n this.logger.info(`Signing key has expired, generating new key`);\n delete this.privateKeyPromise;\n }\n\n this.keyExpiry = new Date(\n Date.now() + this.keyDurationSeconds * SECONDS_IN_MS,\n );\n\n const promise = (async () => {\n // This generates a new signing key to be used to sign tokens until the next key rotation\n const kid = uuid();\n const key = await generateKeyPair(this.algorithm);\n const publicKey = await exportJWK(key.publicKey);\n const privateKey = await exportJWK(key.privateKey);\n publicKey.kid = privateKey.kid = kid;\n publicKey.alg = privateKey.alg = this.algorithm;\n\n // We're not allowed to use the key until it has been successfully stored\n // TODO: some token verification implementations aggressively cache the list of keys, and\n // don't attempt to fetch new ones even if they encounter an unknown kid. Therefore we\n // may want to keep using the existing key for some period of time until we switch to\n // the new one. This also needs to be implemented cross-service though, meaning new services\n // that boot up need to be able to grab an existing key to use for signing.\n this.logger.info(`Created new signing key ${kid}`);\n\n await this.keyStore.addKey({\n id: kid,\n key: publicKey as unknown as InternalKey,\n expiresAt: new Date(\n Date.now() +\n this.keyDurationSeconds *\n SECONDS_IN_MS *\n KEY_EXPIRATION_MARGIN_FACTOR,\n ),\n });\n\n // At this point we are allowed to start using the new key\n return privateKey;\n })();\n\n this.privateKeyPromise = promise;\n\n try {\n // If we fail to generate a new key, we need to clear the state so that\n // the next caller will try to generate another key.\n await promise;\n } catch (error) {\n this.logger.error(`Failed to generate new signing key, ${error}`);\n delete this.keyExpiry;\n delete this.privateKeyPromise;\n }\n\n return promise;\n }\n\n listKeys(): Promise<{ keys: KeyPayload[] }> {\n return this.keyStore.listKeys();\n }\n}\n"],"names":["DatabaseKeyStore","durationToMilliseconds","uuid","generateKeyPair","exportJWK"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
var errors = require('@backstage/errors');
|
|
4
4
|
var cron = require('cron');
|
|
5
5
|
var luxon = require('luxon');
|
|
6
|
-
var
|
|
6
|
+
var node_crypto = require('node:crypto');
|
|
7
7
|
var tables = require('../database/tables.cjs.js');
|
|
8
8
|
var types = require('./types.cjs.js');
|
|
9
9
|
var util = require('./util.cjs.js');
|
|
@@ -156,7 +156,7 @@ class TaskWorker {
|
|
|
156
156
|
return findResult;
|
|
157
157
|
}
|
|
158
158
|
const taskSettings = findResult.settings;
|
|
159
|
-
const ticket =
|
|
159
|
+
const ticket = node_crypto.randomUUID();
|
|
160
160
|
const claimed = await this.tryClaimTask(ticket, taskSettings);
|
|
161
161
|
if (!claimed) {
|
|
162
162
|
return { result: "not-ready-yet" };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TaskWorker.cjs.js","sources":["../../../../src/entrypoints/scheduler/lib/TaskWorker.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { ConflictError, NotFoundError } from '@backstage/errors';\nimport { CronTime } from 'cron';\nimport { Knex } from 'knex';\nimport { DateTime, Duration } from 'luxon';\nimport { v4 as uuid } from 'uuid';\nimport { DB_TASKS_TABLE, DbTasksRow } from '../database/tables';\nimport {\n TaskSettingsV2,\n taskSettingsV2Schema,\n TaskApiTasksResponse,\n} from './types';\nimport {\n delegateAbortController,\n nowPlus,\n sleep,\n dbTime,\n serializeError,\n} from './util';\nimport { SchedulerServiceTaskFunction } from '@backstage/backend-plugin-api';\n\nconst DEFAULT_WORK_CHECK_FREQUENCY = Duration.fromObject({ seconds: 5 });\n\n/**\n * Implements tasks that run across worker hosts, with collaborative locking.\n *\n * @private\n */\nexport class TaskWorker {\n #workerState: TaskApiTasksResponse['workerState'] = {\n status: 'idle',\n };\n private readonly taskId: string;\n private readonly fn: SchedulerServiceTaskFunction;\n private readonly knex: Knex;\n private readonly logger: LoggerService;\n private readonly workCheckFrequency: Duration;\n\n constructor(\n taskId: string,\n fn: SchedulerServiceTaskFunction,\n knex: Knex,\n logger: LoggerService,\n workCheckFrequency: Duration = DEFAULT_WORK_CHECK_FREQUENCY,\n ) {\n this.taskId = taskId;\n this.fn = fn;\n this.knex = knex;\n this.logger = logger;\n this.workCheckFrequency = workCheckFrequency;\n }\n\n async start(settings: TaskSettingsV2, options: { signal: AbortSignal }) {\n try {\n await this.persistTask(settings);\n } catch (e) {\n throw new Error(`Failed to persist task, ${e}`);\n }\n\n this.logger.info(\n `Registered scheduled task: ${this.taskId}, ${JSON.stringify(settings)}`,\n );\n\n let workCheckFrequency = this.workCheckFrequency;\n const isDuration = settings?.cadence.startsWith('P');\n if (isDuration) {\n const cadence = Duration.fromISO(settings.cadence);\n if (cadence < workCheckFrequency) {\n workCheckFrequency = cadence;\n }\n }\n\n (async () => {\n let attemptNum = 1;\n for (;;) {\n try {\n await this.performInitialWait(settings, options.signal);\n\n while (!options.signal.aborted) {\n const runResult = await this.runOnce(options.signal);\n if (runResult.result === 'abort') {\n break;\n }\n await sleep(workCheckFrequency, options.signal);\n }\n\n this.logger.info(`Task worker finished: ${this.taskId}`);\n attemptNum = 0;\n break;\n } catch (e) {\n attemptNum += 1;\n this.logger.warn(\n `Task worker failed unexpectedly, attempt number ${attemptNum}, ${e}`,\n );\n await sleep(Duration.fromObject({ seconds: 1 }));\n }\n }\n })();\n }\n\n /**\n * Does the once-at-startup initial wait, if configured.\n */\n private async performInitialWait(\n settings: TaskSettingsV2,\n signal: AbortSignal,\n ): Promise<void> {\n if (settings.initialDelayDuration) {\n this.#workerState = {\n status: 'initial-wait',\n };\n await sleep(Duration.fromISO(settings.initialDelayDuration), signal);\n }\n this.#workerState = {\n status: 'idle',\n };\n }\n\n static async trigger(knex: Knex, taskId: string): Promise<void> {\n // check if task exists\n const rows = await knex<DbTasksRow>(DB_TASKS_TABLE)\n .select(knex.raw(1))\n .where('id', '=', taskId);\n if (rows.length !== 1) {\n throw new NotFoundError(`Task ${taskId} does not exist`);\n }\n\n const updatedRows = await knex<DbTasksRow>(DB_TASKS_TABLE)\n .where('id', '=', taskId)\n .whereNull('current_run_ticket')\n .update({\n next_run_start_at: knex.fn.now(),\n });\n if (updatedRows < 1) {\n throw new ConflictError(`Task ${taskId} is currently running`);\n }\n }\n\n static async cancel(knex: Knex, taskId: string): Promise<void> {\n const [row] = await knex<DbTasksRow>(DB_TASKS_TABLE)\n .where('id', '=', taskId)\n .select('settings_json', 'current_run_ticket');\n if (!row) {\n throw new NotFoundError(`Task ${taskId} does not exist`);\n }\n if (!row.current_run_ticket) {\n throw new ConflictError(`Task ${taskId} is not running`);\n }\n\n const settings = taskSettingsV2Schema.parse(JSON.parse(row.settings_json));\n const nextRun = TaskWorker.computeNextRunStartAt(knex, settings);\n\n const updatedRows = await knex<DbTasksRow>(DB_TASKS_TABLE)\n .where('id', '=', taskId)\n .where('current_run_ticket', '=', row.current_run_ticket)\n .update({\n next_run_start_at: nextRun,\n current_run_ticket: knex.raw('null'),\n current_run_started_at: knex.raw('null'),\n current_run_expires_at: knex.raw('null'),\n last_run_ended_at: knex.fn.now(),\n last_run_error_json: serializeError(new Error('Task was cancelled')),\n });\n if (updatedRows < 1) {\n throw new ConflictError(`Task ${taskId} is not running`);\n }\n }\n\n static async taskStates(\n knex: Knex,\n ): Promise<Map<string, TaskApiTasksResponse['taskState']>> {\n const rows = await knex<DbTasksRow>(DB_TASKS_TABLE);\n return new Map(\n rows.map(row => {\n const startedAt = row.current_run_started_at\n ? dbTime(row.current_run_started_at).toISO()!\n : undefined;\n const timesOutAt = row.current_run_expires_at\n ? dbTime(row.current_run_expires_at).toISO()!\n : undefined;\n const startsAt = row.next_run_start_at\n ? dbTime(row.next_run_start_at).toISO()!\n : undefined;\n const lastRunEndedAt = row.last_run_ended_at\n ? dbTime(row.last_run_ended_at).toISO()!\n : undefined;\n const lastRunError = row.last_run_error_json || undefined;\n\n return [\n row.id,\n startedAt\n ? {\n status: 'running',\n startedAt,\n timesOutAt,\n lastRunEndedAt,\n lastRunError,\n }\n : {\n status: 'idle',\n startsAt,\n lastRunEndedAt,\n lastRunError,\n },\n ];\n }),\n );\n }\n\n workerState(): TaskApiTasksResponse['workerState'] {\n return this.#workerState;\n }\n\n /**\n * Makes a single attempt at running the task to completion, if ready.\n *\n * @returns The outcome of the attempt\n */\n private async runOnce(\n signal: AbortSignal,\n ): Promise<\n | { result: 'not-ready-yet' }\n | { result: 'abort' }\n | { result: 'failed' }\n | { result: 'completed' }\n > {\n const findResult = await this.findReadyTask();\n if (\n findResult.result === 'not-ready-yet' ||\n findResult.result === 'abort'\n ) {\n return findResult;\n }\n\n const taskSettings = findResult.settings;\n const ticket = uuid();\n\n const claimed = await this.tryClaimTask(ticket, taskSettings);\n if (!claimed) {\n return { result: 'not-ready-yet' };\n }\n\n // Abort the task execution either if the worker is stopped, or if the\n // task timeout is hit, or if the task ticket was lost (e.g. due to\n // cancellation from another host)\n const taskAbortController = delegateAbortController(signal);\n const timeoutHandle = setTimeout(() => {\n taskAbortController.abort();\n }, Duration.fromISO(taskSettings.timeoutAfterDuration).as('milliseconds'));\n let livenessHandle: ReturnType<typeof setTimeout> | undefined;\n const scheduleLivenessCheck = () => {\n livenessHandle = setTimeout(async () => {\n await this.checkLiveness(ticket, taskAbortController);\n if (!taskAbortController.signal.aborted) {\n scheduleLivenessCheck();\n }\n }, this.workCheckFrequency.as('milliseconds'));\n };\n scheduleLivenessCheck();\n\n try {\n this.#workerState = {\n status: 'running',\n };\n await this.fn(taskAbortController.signal);\n taskAbortController.abort(); // releases resources\n } catch (e) {\n this.logger.error(e);\n await this.tryReleaseTask(ticket, taskSettings, e);\n return { result: 'failed' };\n } finally {\n this.#workerState = {\n status: 'idle',\n };\n clearTimeout(timeoutHandle);\n clearTimeout(livenessHandle);\n }\n\n await this.tryReleaseTask(ticket, taskSettings);\n return { result: 'completed' };\n }\n\n /**\n * Perform the initial store of the task info\n */\n async persistTask(settings: TaskSettingsV2) {\n // Perform an initial parse to ensure that we will definitely be able to\n // read it back again.\n taskSettingsV2Schema.parse(settings);\n\n const isManual = settings?.cadence === 'manual';\n const isDuration = settings?.cadence.startsWith('P');\n const isCron = !isManual && !isDuration;\n\n let startAt: Knex.Raw | undefined;\n let nextStartAt: Knex.Raw | undefined;\n if (settings.initialDelayDuration) {\n startAt = nowPlus(\n Duration.fromISO(settings.initialDelayDuration),\n this.knex,\n );\n }\n\n if (isCron) {\n const time = new CronTime(settings.cadence)\n .sendAt()\n .minus({ seconds: 1 }) // immediately, if \"* * * * * *\"\n .toUTC();\n // We make a conversion here to make typescript happy, because the luxon versions of the cron library and here may not be the same\n const timeConverted = DateTime.fromJSDate(time.toJSDate());\n\n nextStartAt = TaskWorker.nextRunAtRaw(this.knex, timeConverted);\n startAt ||= nextStartAt;\n } else if (isManual) {\n nextStartAt = this.knex.raw('null');\n startAt ||= nextStartAt;\n } else {\n startAt ||= this.knex.fn.now();\n nextStartAt = nowPlus(Duration.fromISO(settings.cadence), this.knex);\n }\n\n this.logger.debug(`task: ${this.taskId} configured to run at: ${startAt}`);\n\n // It's OK if the task already exists; if it does, just replace its\n // settings with the new value and start the loop as usual.\n const settingsJson = JSON.stringify(settings);\n await this.knex<DbTasksRow>(DB_TASKS_TABLE)\n .insert({\n id: this.taskId,\n settings_json: settingsJson,\n next_run_start_at: startAt,\n })\n .onConflict('id')\n .merge(\n this.knex.client.config.client.includes('mysql')\n ? {\n settings_json: settingsJson,\n next_run_start_at: this.knex.raw(\n `CASE WHEN ?? < ?? THEN ?? ELSE ?? END`,\n [\n nextStartAt,\n 'next_run_start_at',\n nextStartAt,\n 'next_run_start_at',\n ],\n ),\n }\n : {\n settings_json: this.knex.ref('excluded.settings_json'),\n next_run_start_at: this.knex.raw(\n `CASE WHEN ?? < ?? THEN ?? ELSE ?? END`,\n [\n nextStartAt,\n `${DB_TASKS_TABLE}.next_run_start_at`,\n nextStartAt,\n `${DB_TASKS_TABLE}.next_run_start_at`,\n ],\n ),\n },\n );\n }\n\n /**\n * Checks whether the current task ticket is still valid in the database.\n * If the ticket has been cleared (e.g. by cancellation or janitor cleanup),\n * aborts the task execution.\n */\n private async checkLiveness(\n ticket: string,\n taskAbortController: AbortController,\n ): Promise<void> {\n try {\n const [row] = await this.knex<DbTasksRow>(DB_TASKS_TABLE)\n .where('id', '=', this.taskId)\n .select('current_run_ticket');\n\n if (!row || row.current_run_ticket !== ticket) {\n this.logger.info(\n `Task ticket for \"${this.taskId}\" is no longer valid; aborting execution`,\n );\n taskAbortController.abort();\n }\n } catch (e) {\n this.logger.warn(\n `Failed to check liveness for task \"${this.taskId}\", ${e}`,\n );\n }\n }\n\n /**\n * Check if the task is ready to run\n */\n async findReadyTask(): Promise<\n | { result: 'not-ready-yet' }\n | { result: 'abort' }\n | { result: 'ready'; settings: TaskSettingsV2 }\n > {\n const [row] = await this.knex<DbTasksRow>(DB_TASKS_TABLE)\n .where('id', '=', this.taskId)\n .select({\n settingsJson: 'settings_json',\n ready: this.knex.raw(\n `CASE\n WHEN next_run_start_at <= ? AND current_run_ticket IS NULL THEN TRUE\n ELSE FALSE\n END`,\n [this.knex.fn.now()],\n ),\n });\n\n if (!row) {\n this.logger.info(\n 'No longer able to find task; aborting and assuming that it has been unregistered or expired',\n );\n return { result: 'abort' };\n } else if (!row.ready) {\n return { result: 'not-ready-yet' };\n }\n\n try {\n const obj = JSON.parse(row.settingsJson);\n const settings = taskSettingsV2Schema.parse(obj);\n return { result: 'ready', settings };\n } catch (e) {\n this.logger.info(\n `Task \"${this.taskId}\" is no longer able to parse task settings; aborting and assuming that a ` +\n `newer version of the task has been issued and being handled by other workers, ${e}`,\n );\n return { result: 'abort' };\n }\n }\n\n /**\n * Attempts to claim a task that's ready for execution, on this worker's\n * behalf. We should not attempt to perform the work unless the claim really\n * goes through.\n *\n * @param ticket - A globally unique string that changes for each invocation\n * @param settings - The settings of the task to claim\n * @returns True if it was successfully claimed\n */\n async tryClaimTask(\n ticket: string,\n settings: TaskSettingsV2,\n ): Promise<boolean> {\n const startedAt = this.knex.fn.now();\n const expiresAt = settings.timeoutAfterDuration\n ? nowPlus(Duration.fromISO(settings.timeoutAfterDuration), this.knex)\n : this.knex.raw('null');\n\n const rows = await this.knex<DbTasksRow>(DB_TASKS_TABLE)\n .where('id', '=', this.taskId)\n .whereNull('current_run_ticket')\n .update({\n current_run_ticket: ticket,\n current_run_started_at: startedAt,\n current_run_expires_at: expiresAt,\n });\n\n return rows === 1;\n }\n\n private static computeNextRunStartAt(\n knex: Knex,\n settings: TaskSettingsV2,\n ): Knex.Raw {\n const isManual = settings?.cadence === 'manual';\n const isDuration = settings?.cadence.startsWith('P');\n const isCron = !isManual && !isDuration;\n\n if (isCron) {\n const time = new CronTime(settings.cadence).sendAt().toUTC();\n const timeConverted = DateTime.fromJSDate(time.toJSDate());\n return TaskWorker.nextRunAtRaw(knex, timeConverted);\n }\n\n if (isManual) {\n return knex.raw('null');\n }\n\n const dt = Duration.fromISO(settings.cadence).as('seconds');\n\n if (knex.client.config.client.includes('sqlite3')) {\n return knex.raw(`max(datetime(next_run_start_at, ?), datetime('now'))`, [\n `+${dt} seconds`,\n ]);\n }\n\n if (knex.client.config.client.includes('mysql')) {\n return knex.raw(\n `greatest(next_run_start_at + interval ${dt} second, now())`,\n );\n }\n\n return knex.raw(\n `greatest(next_run_start_at + interval '${dt} seconds', now())`,\n );\n }\n\n async tryReleaseTask(\n ticket: string,\n settings: TaskSettingsV2,\n error?: Error,\n ): Promise<boolean> {\n const nextRun = TaskWorker.computeNextRunStartAt(this.knex, settings);\n\n const rows = await this.knex<DbTasksRow>(DB_TASKS_TABLE)\n .where('id', '=', this.taskId)\n .where('current_run_ticket', '=', ticket)\n .update({\n next_run_start_at: nextRun,\n current_run_ticket: this.knex.raw('null'),\n current_run_started_at: this.knex.raw('null'),\n current_run_expires_at: this.knex.raw('null'),\n last_run_ended_at: this.knex.fn.now(),\n last_run_error_json: error\n ? serializeError(error)\n : this.knex.raw('null'),\n });\n\n return rows === 1;\n }\n\n private static nextRunAtRaw(knex: Knex, time: DateTime): Knex.Raw {\n if (knex.client.config.client.includes('sqlite3')) {\n return knex.raw('datetime(?)', [time.toISO()]);\n }\n if (knex.client.config.client.includes('mysql')) {\n return knex.raw(`?`, [time.toSQL({ includeOffset: false })]);\n }\n return knex.raw(`?`, [time.toISO()]);\n }\n}\n"],"names":["Duration","sleep","DB_TASKS_TABLE","NotFoundError","ConflictError","taskSettingsV2Schema","serializeError","dbTime","uuid","delegateAbortController","nowPlus","CronTime","DateTime"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TaskWorker.cjs.js","sources":["../../../../src/entrypoints/scheduler/lib/TaskWorker.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { ConflictError, NotFoundError } from '@backstage/errors';\nimport { CronTime } from 'cron';\nimport { Knex } from 'knex';\nimport { DateTime, Duration } from 'luxon';\nimport { randomUUID as uuid } from 'node:crypto';\nimport { DB_TASKS_TABLE, DbTasksRow } from '../database/tables';\nimport {\n TaskSettingsV2,\n taskSettingsV2Schema,\n TaskApiTasksResponse,\n} from './types';\nimport {\n delegateAbortController,\n nowPlus,\n sleep,\n dbTime,\n serializeError,\n} from './util';\nimport { SchedulerServiceTaskFunction } from '@backstage/backend-plugin-api';\n\nconst DEFAULT_WORK_CHECK_FREQUENCY = Duration.fromObject({ seconds: 5 });\n\n/**\n * Implements tasks that run across worker hosts, with collaborative locking.\n *\n * @private\n */\nexport class TaskWorker {\n #workerState: TaskApiTasksResponse['workerState'] = {\n status: 'idle',\n };\n private readonly taskId: string;\n private readonly fn: SchedulerServiceTaskFunction;\n private readonly knex: Knex;\n private readonly logger: LoggerService;\n private readonly workCheckFrequency: Duration;\n\n constructor(\n taskId: string,\n fn: SchedulerServiceTaskFunction,\n knex: Knex,\n logger: LoggerService,\n workCheckFrequency: Duration = DEFAULT_WORK_CHECK_FREQUENCY,\n ) {\n this.taskId = taskId;\n this.fn = fn;\n this.knex = knex;\n this.logger = logger;\n this.workCheckFrequency = workCheckFrequency;\n }\n\n async start(settings: TaskSettingsV2, options: { signal: AbortSignal }) {\n try {\n await this.persistTask(settings);\n } catch (e) {\n throw new Error(`Failed to persist task, ${e}`);\n }\n\n this.logger.info(\n `Registered scheduled task: ${this.taskId}, ${JSON.stringify(settings)}`,\n );\n\n let workCheckFrequency = this.workCheckFrequency;\n const isDuration = settings?.cadence.startsWith('P');\n if (isDuration) {\n const cadence = Duration.fromISO(settings.cadence);\n if (cadence < workCheckFrequency) {\n workCheckFrequency = cadence;\n }\n }\n\n (async () => {\n let attemptNum = 1;\n for (;;) {\n try {\n await this.performInitialWait(settings, options.signal);\n\n while (!options.signal.aborted) {\n const runResult = await this.runOnce(options.signal);\n if (runResult.result === 'abort') {\n break;\n }\n await sleep(workCheckFrequency, options.signal);\n }\n\n this.logger.info(`Task worker finished: ${this.taskId}`);\n attemptNum = 0;\n break;\n } catch (e) {\n attemptNum += 1;\n this.logger.warn(\n `Task worker failed unexpectedly, attempt number ${attemptNum}, ${e}`,\n );\n await sleep(Duration.fromObject({ seconds: 1 }));\n }\n }\n })();\n }\n\n /**\n * Does the once-at-startup initial wait, if configured.\n */\n private async performInitialWait(\n settings: TaskSettingsV2,\n signal: AbortSignal,\n ): Promise<void> {\n if (settings.initialDelayDuration) {\n this.#workerState = {\n status: 'initial-wait',\n };\n await sleep(Duration.fromISO(settings.initialDelayDuration), signal);\n }\n this.#workerState = {\n status: 'idle',\n };\n }\n\n static async trigger(knex: Knex, taskId: string): Promise<void> {\n // check if task exists\n const rows = await knex<DbTasksRow>(DB_TASKS_TABLE)\n .select(knex.raw(1))\n .where('id', '=', taskId);\n if (rows.length !== 1) {\n throw new NotFoundError(`Task ${taskId} does not exist`);\n }\n\n const updatedRows = await knex<DbTasksRow>(DB_TASKS_TABLE)\n .where('id', '=', taskId)\n .whereNull('current_run_ticket')\n .update({\n next_run_start_at: knex.fn.now(),\n });\n if (updatedRows < 1) {\n throw new ConflictError(`Task ${taskId} is currently running`);\n }\n }\n\n static async cancel(knex: Knex, taskId: string): Promise<void> {\n const [row] = await knex<DbTasksRow>(DB_TASKS_TABLE)\n .where('id', '=', taskId)\n .select('settings_json', 'current_run_ticket');\n if (!row) {\n throw new NotFoundError(`Task ${taskId} does not exist`);\n }\n if (!row.current_run_ticket) {\n throw new ConflictError(`Task ${taskId} is not running`);\n }\n\n const settings = taskSettingsV2Schema.parse(JSON.parse(row.settings_json));\n const nextRun = TaskWorker.computeNextRunStartAt(knex, settings);\n\n const updatedRows = await knex<DbTasksRow>(DB_TASKS_TABLE)\n .where('id', '=', taskId)\n .where('current_run_ticket', '=', row.current_run_ticket)\n .update({\n next_run_start_at: nextRun,\n current_run_ticket: knex.raw('null'),\n current_run_started_at: knex.raw('null'),\n current_run_expires_at: knex.raw('null'),\n last_run_ended_at: knex.fn.now(),\n last_run_error_json: serializeError(new Error('Task was cancelled')),\n });\n if (updatedRows < 1) {\n throw new ConflictError(`Task ${taskId} is not running`);\n }\n }\n\n static async taskStates(\n knex: Knex,\n ): Promise<Map<string, TaskApiTasksResponse['taskState']>> {\n const rows = await knex<DbTasksRow>(DB_TASKS_TABLE);\n return new Map(\n rows.map(row => {\n const startedAt = row.current_run_started_at\n ? dbTime(row.current_run_started_at).toISO()!\n : undefined;\n const timesOutAt = row.current_run_expires_at\n ? dbTime(row.current_run_expires_at).toISO()!\n : undefined;\n const startsAt = row.next_run_start_at\n ? dbTime(row.next_run_start_at).toISO()!\n : undefined;\n const lastRunEndedAt = row.last_run_ended_at\n ? dbTime(row.last_run_ended_at).toISO()!\n : undefined;\n const lastRunError = row.last_run_error_json || undefined;\n\n return [\n row.id,\n startedAt\n ? {\n status: 'running',\n startedAt,\n timesOutAt,\n lastRunEndedAt,\n lastRunError,\n }\n : {\n status: 'idle',\n startsAt,\n lastRunEndedAt,\n lastRunError,\n },\n ];\n }),\n );\n }\n\n workerState(): TaskApiTasksResponse['workerState'] {\n return this.#workerState;\n }\n\n /**\n * Makes a single attempt at running the task to completion, if ready.\n *\n * @returns The outcome of the attempt\n */\n private async runOnce(\n signal: AbortSignal,\n ): Promise<\n | { result: 'not-ready-yet' }\n | { result: 'abort' }\n | { result: 'failed' }\n | { result: 'completed' }\n > {\n const findResult = await this.findReadyTask();\n if (\n findResult.result === 'not-ready-yet' ||\n findResult.result === 'abort'\n ) {\n return findResult;\n }\n\n const taskSettings = findResult.settings;\n const ticket = uuid();\n\n const claimed = await this.tryClaimTask(ticket, taskSettings);\n if (!claimed) {\n return { result: 'not-ready-yet' };\n }\n\n // Abort the task execution either if the worker is stopped, or if the\n // task timeout is hit, or if the task ticket was lost (e.g. due to\n // cancellation from another host)\n const taskAbortController = delegateAbortController(signal);\n const timeoutHandle = setTimeout(() => {\n taskAbortController.abort();\n }, Duration.fromISO(taskSettings.timeoutAfterDuration).as('milliseconds'));\n let livenessHandle: ReturnType<typeof setTimeout> | undefined;\n const scheduleLivenessCheck = () => {\n livenessHandle = setTimeout(async () => {\n await this.checkLiveness(ticket, taskAbortController);\n if (!taskAbortController.signal.aborted) {\n scheduleLivenessCheck();\n }\n }, this.workCheckFrequency.as('milliseconds'));\n };\n scheduleLivenessCheck();\n\n try {\n this.#workerState = {\n status: 'running',\n };\n await this.fn(taskAbortController.signal);\n taskAbortController.abort(); // releases resources\n } catch (e) {\n this.logger.error(e);\n await this.tryReleaseTask(ticket, taskSettings, e);\n return { result: 'failed' };\n } finally {\n this.#workerState = {\n status: 'idle',\n };\n clearTimeout(timeoutHandle);\n clearTimeout(livenessHandle);\n }\n\n await this.tryReleaseTask(ticket, taskSettings);\n return { result: 'completed' };\n }\n\n /**\n * Perform the initial store of the task info\n */\n async persistTask(settings: TaskSettingsV2) {\n // Perform an initial parse to ensure that we will definitely be able to\n // read it back again.\n taskSettingsV2Schema.parse(settings);\n\n const isManual = settings?.cadence === 'manual';\n const isDuration = settings?.cadence.startsWith('P');\n const isCron = !isManual && !isDuration;\n\n let startAt: Knex.Raw | undefined;\n let nextStartAt: Knex.Raw | undefined;\n if (settings.initialDelayDuration) {\n startAt = nowPlus(\n Duration.fromISO(settings.initialDelayDuration),\n this.knex,\n );\n }\n\n if (isCron) {\n const time = new CronTime(settings.cadence)\n .sendAt()\n .minus({ seconds: 1 }) // immediately, if \"* * * * * *\"\n .toUTC();\n // We make a conversion here to make typescript happy, because the luxon versions of the cron library and here may not be the same\n const timeConverted = DateTime.fromJSDate(time.toJSDate());\n\n nextStartAt = TaskWorker.nextRunAtRaw(this.knex, timeConverted);\n startAt ||= nextStartAt;\n } else if (isManual) {\n nextStartAt = this.knex.raw('null');\n startAt ||= nextStartAt;\n } else {\n startAt ||= this.knex.fn.now();\n nextStartAt = nowPlus(Duration.fromISO(settings.cadence), this.knex);\n }\n\n this.logger.debug(`task: ${this.taskId} configured to run at: ${startAt}`);\n\n // It's OK if the task already exists; if it does, just replace its\n // settings with the new value and start the loop as usual.\n const settingsJson = JSON.stringify(settings);\n await this.knex<DbTasksRow>(DB_TASKS_TABLE)\n .insert({\n id: this.taskId,\n settings_json: settingsJson,\n next_run_start_at: startAt,\n })\n .onConflict('id')\n .merge(\n this.knex.client.config.client.includes('mysql')\n ? {\n settings_json: settingsJson,\n next_run_start_at: this.knex.raw(\n `CASE WHEN ?? < ?? THEN ?? ELSE ?? END`,\n [\n nextStartAt,\n 'next_run_start_at',\n nextStartAt,\n 'next_run_start_at',\n ],\n ),\n }\n : {\n settings_json: this.knex.ref('excluded.settings_json'),\n next_run_start_at: this.knex.raw(\n `CASE WHEN ?? < ?? THEN ?? ELSE ?? END`,\n [\n nextStartAt,\n `${DB_TASKS_TABLE}.next_run_start_at`,\n nextStartAt,\n `${DB_TASKS_TABLE}.next_run_start_at`,\n ],\n ),\n },\n );\n }\n\n /**\n * Checks whether the current task ticket is still valid in the database.\n * If the ticket has been cleared (e.g. by cancellation or janitor cleanup),\n * aborts the task execution.\n */\n private async checkLiveness(\n ticket: string,\n taskAbortController: AbortController,\n ): Promise<void> {\n try {\n const [row] = await this.knex<DbTasksRow>(DB_TASKS_TABLE)\n .where('id', '=', this.taskId)\n .select('current_run_ticket');\n\n if (!row || row.current_run_ticket !== ticket) {\n this.logger.info(\n `Task ticket for \"${this.taskId}\" is no longer valid; aborting execution`,\n );\n taskAbortController.abort();\n }\n } catch (e) {\n this.logger.warn(\n `Failed to check liveness for task \"${this.taskId}\", ${e}`,\n );\n }\n }\n\n /**\n * Check if the task is ready to run\n */\n async findReadyTask(): Promise<\n | { result: 'not-ready-yet' }\n | { result: 'abort' }\n | { result: 'ready'; settings: TaskSettingsV2 }\n > {\n const [row] = await this.knex<DbTasksRow>(DB_TASKS_TABLE)\n .where('id', '=', this.taskId)\n .select({\n settingsJson: 'settings_json',\n ready: this.knex.raw(\n `CASE\n WHEN next_run_start_at <= ? AND current_run_ticket IS NULL THEN TRUE\n ELSE FALSE\n END`,\n [this.knex.fn.now()],\n ),\n });\n\n if (!row) {\n this.logger.info(\n 'No longer able to find task; aborting and assuming that it has been unregistered or expired',\n );\n return { result: 'abort' };\n } else if (!row.ready) {\n return { result: 'not-ready-yet' };\n }\n\n try {\n const obj = JSON.parse(row.settingsJson);\n const settings = taskSettingsV2Schema.parse(obj);\n return { result: 'ready', settings };\n } catch (e) {\n this.logger.info(\n `Task \"${this.taskId}\" is no longer able to parse task settings; aborting and assuming that a ` +\n `newer version of the task has been issued and being handled by other workers, ${e}`,\n );\n return { result: 'abort' };\n }\n }\n\n /**\n * Attempts to claim a task that's ready for execution, on this worker's\n * behalf. We should not attempt to perform the work unless the claim really\n * goes through.\n *\n * @param ticket - A globally unique string that changes for each invocation\n * @param settings - The settings of the task to claim\n * @returns True if it was successfully claimed\n */\n async tryClaimTask(\n ticket: string,\n settings: TaskSettingsV2,\n ): Promise<boolean> {\n const startedAt = this.knex.fn.now();\n const expiresAt = settings.timeoutAfterDuration\n ? nowPlus(Duration.fromISO(settings.timeoutAfterDuration), this.knex)\n : this.knex.raw('null');\n\n const rows = await this.knex<DbTasksRow>(DB_TASKS_TABLE)\n .where('id', '=', this.taskId)\n .whereNull('current_run_ticket')\n .update({\n current_run_ticket: ticket,\n current_run_started_at: startedAt,\n current_run_expires_at: expiresAt,\n });\n\n return rows === 1;\n }\n\n private static computeNextRunStartAt(\n knex: Knex,\n settings: TaskSettingsV2,\n ): Knex.Raw {\n const isManual = settings?.cadence === 'manual';\n const isDuration = settings?.cadence.startsWith('P');\n const isCron = !isManual && !isDuration;\n\n if (isCron) {\n const time = new CronTime(settings.cadence).sendAt().toUTC();\n const timeConverted = DateTime.fromJSDate(time.toJSDate());\n return TaskWorker.nextRunAtRaw(knex, timeConverted);\n }\n\n if (isManual) {\n return knex.raw('null');\n }\n\n const dt = Duration.fromISO(settings.cadence).as('seconds');\n\n if (knex.client.config.client.includes('sqlite3')) {\n return knex.raw(`max(datetime(next_run_start_at, ?), datetime('now'))`, [\n `+${dt} seconds`,\n ]);\n }\n\n if (knex.client.config.client.includes('mysql')) {\n return knex.raw(\n `greatest(next_run_start_at + interval ${dt} second, now())`,\n );\n }\n\n return knex.raw(\n `greatest(next_run_start_at + interval '${dt} seconds', now())`,\n );\n }\n\n async tryReleaseTask(\n ticket: string,\n settings: TaskSettingsV2,\n error?: Error,\n ): Promise<boolean> {\n const nextRun = TaskWorker.computeNextRunStartAt(this.knex, settings);\n\n const rows = await this.knex<DbTasksRow>(DB_TASKS_TABLE)\n .where('id', '=', this.taskId)\n .where('current_run_ticket', '=', ticket)\n .update({\n next_run_start_at: nextRun,\n current_run_ticket: this.knex.raw('null'),\n current_run_started_at: this.knex.raw('null'),\n current_run_expires_at: this.knex.raw('null'),\n last_run_ended_at: this.knex.fn.now(),\n last_run_error_json: error\n ? serializeError(error)\n : this.knex.raw('null'),\n });\n\n return rows === 1;\n }\n\n private static nextRunAtRaw(knex: Knex, time: DateTime): Knex.Raw {\n if (knex.client.config.client.includes('sqlite3')) {\n return knex.raw('datetime(?)', [time.toISO()]);\n }\n if (knex.client.config.client.includes('mysql')) {\n return knex.raw(`?`, [time.toSQL({ includeOffset: false })]);\n }\n return knex.raw(`?`, [time.toISO()]);\n }\n}\n"],"names":["Duration","sleep","DB_TASKS_TABLE","NotFoundError","ConflictError","taskSettingsV2Schema","serializeError","dbTime","uuid","delegateAbortController","nowPlus","CronTime","DateTime"],"mappings":"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"}
|
|
@@ -25,18 +25,6 @@ function isValidCronFormat(c) {
|
|
|
25
25
|
function isValidTrigger(t) {
|
|
26
26
|
return t === "manual";
|
|
27
27
|
}
|
|
28
|
-
v4.z.object({
|
|
29
|
-
version: v4.z.literal(1),
|
|
30
|
-
initialDelayDuration: v4.z.string().optional().refine(isValidOptionalDurationString, {
|
|
31
|
-
message: "Invalid duration, expecting ISO Period"
|
|
32
|
-
}),
|
|
33
|
-
recurringAtMostEveryDuration: v4.z.string().refine(isValidOptionalDurationString, {
|
|
34
|
-
message: "Invalid duration, expecting ISO Period"
|
|
35
|
-
}),
|
|
36
|
-
timeoutAfterDuration: v4.z.string().refine(isValidOptionalDurationString, {
|
|
37
|
-
message: "Invalid duration, expecting ISO Period"
|
|
38
|
-
})
|
|
39
|
-
});
|
|
40
28
|
const taskSettingsV2Schema = v4.z.object({
|
|
41
29
|
version: v4.z.literal(2),
|
|
42
30
|
cadence: v4.z.string().refine(isValidCronFormat, { message: "Invalid cron" }).or(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.cjs.js","sources":["../../../../src/entrypoints/scheduler/lib/types.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { JsonObject } from '@backstage/types';\nimport { CronTime } from 'cron';\nimport { Duration } from 'luxon';\nimport { z } from 'zod/v4';\n\nfunction isValidOptionalDurationString(d: string | undefined): boolean {\n try {\n return !d || Duration.fromISO(d).isValid;\n } catch {\n return false;\n }\n}\n\nfunction isValidCronFormat(c: string | undefined): boolean {\n try {\n if (!c) {\n return false;\n }\n // parse cron format to ensure it's a valid format.\n // eslint-disable-next-line no-new\n new CronTime(c);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction isValidTrigger(t: string): boolean {\n return t === 'manual';\n}\n\nexport const
|
|
1
|
+
{"version":3,"file":"types.cjs.js","sources":["../../../../src/entrypoints/scheduler/lib/types.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { JsonObject } from '@backstage/types';\nimport { CronTime } from 'cron';\nimport { Duration } from 'luxon';\nimport { z } from 'zod/v4';\n\nfunction isValidOptionalDurationString(d: string | undefined): boolean {\n try {\n return !d || Duration.fromISO(d).isValid;\n } catch {\n return false;\n }\n}\n\nfunction isValidCronFormat(c: string | undefined): boolean {\n try {\n if (!c) {\n return false;\n }\n // parse cron format to ensure it's a valid format.\n // eslint-disable-next-line no-new\n new CronTime(c);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction isValidTrigger(t: string): boolean {\n return t === 'manual';\n}\n\nexport const taskSettingsV2Schema = z.object({\n version: z.literal(2),\n cadence: z\n .string()\n .refine(isValidCronFormat, { message: 'Invalid cron' })\n .or(\n z.string().refine(isValidTrigger, {\n message: \"Invalid trigger, expecting 'manual'\",\n }),\n )\n .or(\n z.string().refine(isValidOptionalDurationString, {\n message: 'Invalid duration, expecting ISO Period',\n }),\n ),\n timeoutAfterDuration: z.string().refine(isValidOptionalDurationString, {\n message: 'Invalid duration, expecting ISO Period',\n }),\n initialDelayDuration: z\n .string()\n .optional()\n .refine(isValidOptionalDurationString, {\n message: 'Invalid duration, expecting ISO Period',\n }),\n});\n\n/**\n * The properties that control a scheduled task (version 2).\n */\nexport type TaskSettingsV2 = z.infer<typeof taskSettingsV2Schema>;\n\n/**\n * The shape of a task definition as returned by the service's REST API.\n */\nexport interface TaskApiTasksResponse {\n taskId: string;\n pluginId: string;\n scope: 'global' | 'local';\n settings: { version: number } & JsonObject;\n taskState:\n | {\n status: 'running';\n startedAt: string;\n timesOutAt?: string;\n lastRunError?: string;\n lastRunEndedAt?: string;\n }\n | {\n status: 'idle';\n startsAt?: string;\n lastRunError?: string;\n lastRunEndedAt?: string;\n }\n | null;\n workerState:\n | {\n status: 'initial-wait';\n }\n | {\n status: 'idle';\n }\n | {\n status: 'running';\n }\n | null;\n}\n"],"names":["Duration","CronTime","z"],"mappings":";;;;;;AAqBA,SAAS,8BAA8B,CAAA,EAAgC;AACrE,EAAA,IAAI;AACF,IAAA,OAAO,CAAC,CAAA,IAAKA,cAAA,CAAS,OAAA,CAAQ,CAAC,CAAA,CAAE,OAAA;AAAA,EACnC,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAEA,SAAS,kBAAkB,CAAA,EAAgC;AACzD,EAAA,IAAI;AACF,IAAA,IAAI,CAAC,CAAA,EAAG;AACN,MAAA,OAAO,KAAA;AAAA,IACT;AAGA,IAAA,IAAIC,cAAS,CAAC,CAAA;AACd,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAEA,SAAS,eAAe,CAAA,EAAoB;AAC1C,EAAA,OAAO,CAAA,KAAM,QAAA;AACf;AAEO,MAAM,oBAAA,GAAuBC,KAAE,MAAA,CAAO;AAAA,EAC3C,OAAA,EAASA,IAAA,CAAE,OAAA,CAAQ,CAAC,CAAA;AAAA,EACpB,OAAA,EAASA,IAAA,CACN,MAAA,EAAO,CACP,MAAA,CAAO,mBAAmB,EAAE,OAAA,EAAS,cAAA,EAAgB,CAAA,CACrD,EAAA;AAAA,IACCA,IAAA,CAAE,MAAA,EAAO,CAAE,MAAA,CAAO,cAAA,EAAgB;AAAA,MAChC,OAAA,EAAS;AAAA,KACV;AAAA,GACH,CACC,EAAA;AAAA,IACCA,IAAA,CAAE,MAAA,EAAO,CAAE,MAAA,CAAO,6BAAA,EAA+B;AAAA,MAC/C,OAAA,EAAS;AAAA,KACV;AAAA,GACH;AAAA,EACF,oBAAA,EAAsBA,IAAA,CAAE,MAAA,EAAO,CAAE,OAAO,6BAAA,EAA+B;AAAA,IACrE,OAAA,EAAS;AAAA,GACV,CAAA;AAAA,EACD,sBAAsBA,IAAA,CACnB,MAAA,GACA,QAAA,EAAS,CACT,OAAO,6BAAA,EAA+B;AAAA,IACrC,OAAA,EAAS;AAAA,GACV;AACL,CAAC;;;;"}
|
|
@@ -28,21 +28,35 @@ function nowPlus(duration, knex) {
|
|
|
28
28
|
}
|
|
29
29
|
return knex.raw(`now() + interval '${seconds} seconds'`);
|
|
30
30
|
}
|
|
31
|
+
const MAX_TIMEOUT_MS = 2 ** 30;
|
|
31
32
|
async function sleep(duration, abortSignal) {
|
|
32
33
|
if (abortSignal?.aborted) {
|
|
33
34
|
return;
|
|
34
35
|
}
|
|
36
|
+
let remaining = duration.as("milliseconds");
|
|
37
|
+
if (!Number.isFinite(remaining) || remaining <= 0) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
35
40
|
await new Promise((resolve) => {
|
|
36
|
-
let timeoutHandle
|
|
37
|
-
const
|
|
41
|
+
let timeoutHandle;
|
|
42
|
+
const finish = () => {
|
|
38
43
|
if (timeoutHandle) {
|
|
39
44
|
clearTimeout(timeoutHandle);
|
|
40
45
|
}
|
|
41
|
-
abortSignal?.removeEventListener("abort",
|
|
46
|
+
abortSignal?.removeEventListener("abort", finish);
|
|
42
47
|
resolve();
|
|
43
48
|
};
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
const tick = () => {
|
|
50
|
+
if (remaining <= 0) {
|
|
51
|
+
finish();
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const chunk = Math.min(remaining, MAX_TIMEOUT_MS);
|
|
55
|
+
remaining -= chunk;
|
|
56
|
+
timeoutHandle = setTimeout(tick, chunk);
|
|
57
|
+
};
|
|
58
|
+
abortSignal?.addEventListener("abort", finish);
|
|
59
|
+
tick();
|
|
46
60
|
});
|
|
47
61
|
}
|
|
48
62
|
function delegateAbortController(parent) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.cjs.js","sources":["../../../../src/entrypoints/scheduler/lib/util.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n InputError,\n serializeError as internalSerializeError,\n} from '@backstage/errors';\nimport { Knex } from 'knex';\nimport { DateTime, Duration } from 'luxon';\n\nexport const TRACER_ID = 'backstage-service-scheduler';\n\n// Keep the IDs compatible with e.g. Prometheus labels\nexport function validateId(id: string) {\n if (typeof id !== 'string' || !id.trim()) {\n throw new InputError(`${id} is not a valid ID, expected non-empty string`);\n }\n}\n\nexport function dbTime(t: Date | string): DateTime {\n if (typeof t === 'string') {\n return DateTime.fromSQL(t);\n }\n return DateTime.fromJSDate(t);\n}\n\nexport function nowPlus(duration: Duration | undefined, knex: Knex) {\n const seconds = duration?.as('seconds') ?? 0;\n if (!seconds) {\n return knex.fn.now();\n }\n\n if (knex.client.config.client.includes('sqlite3')) {\n return knex.raw(`datetime('now', ?)`, [`${seconds} seconds`]);\n }\n\n if (knex.client.config.client.includes('mysql')) {\n return knex.raw(`now() + interval ${seconds} second`);\n }\n\n return knex.raw(`now() + interval '${seconds} seconds'`);\n}\n\n/**\n * Sleep for the given duration, but return sooner if the abort signal\n * triggers.\n *\n * @param duration - The amount of time to sleep, at most\n * @param abortSignal - An optional abort signal that short circuits the wait\n */\nexport async function sleep(\n duration: Duration,\n abortSignal?: AbortSignal,\n): Promise<void> {\n if (abortSignal?.aborted) {\n return;\n }\n\n await new Promise<void>(resolve => {\n let timeoutHandle: NodeJS.Timeout | undefined
|
|
1
|
+
{"version":3,"file":"util.cjs.js","sources":["../../../../src/entrypoints/scheduler/lib/util.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n InputError,\n serializeError as internalSerializeError,\n} from '@backstage/errors';\nimport { Knex } from 'knex';\nimport { DateTime, Duration } from 'luxon';\n\nexport const TRACER_ID = 'backstage-service-scheduler';\n\n// Keep the IDs compatible with e.g. Prometheus labels\nexport function validateId(id: string) {\n if (typeof id !== 'string' || !id.trim()) {\n throw new InputError(`${id} is not a valid ID, expected non-empty string`);\n }\n}\n\nexport function dbTime(t: Date | string): DateTime {\n if (typeof t === 'string') {\n return DateTime.fromSQL(t);\n }\n return DateTime.fromJSDate(t);\n}\n\nexport function nowPlus(duration: Duration | undefined, knex: Knex) {\n const seconds = duration?.as('seconds') ?? 0;\n if (!seconds) {\n return knex.fn.now();\n }\n\n if (knex.client.config.client.includes('sqlite3')) {\n return knex.raw(`datetime('now', ?)`, [`${seconds} seconds`]);\n }\n\n if (knex.client.config.client.includes('mysql')) {\n return knex.raw(`now() + interval ${seconds} second`);\n }\n\n return knex.raw(`now() + interval '${seconds} seconds'`);\n}\n\n// Node.js setTimeout uses a 32-bit signed integer internally, so timeouts\n// longer than 2^31-1 ms (~24.8 days) fire immediately. We cap each individual\n// wait at 2^30 ms (~12.4 days) and loop until the full duration has elapsed.\nconst MAX_TIMEOUT_MS = 2 ** 30;\n\n/**\n * Sleep for the given duration, but return sooner if the abort signal\n * triggers.\n *\n * @param duration - The amount of time to sleep, at most\n * @param abortSignal - An optional abort signal that short circuits the wait\n */\nexport async function sleep(\n duration: Duration,\n abortSignal?: AbortSignal,\n): Promise<void> {\n if (abortSignal?.aborted) {\n return;\n }\n\n let remaining = duration.as('milliseconds');\n if (!Number.isFinite(remaining) || remaining <= 0) {\n return;\n }\n\n await new Promise<void>(resolve => {\n let timeoutHandle: NodeJS.Timeout | undefined;\n\n const finish = () => {\n if (timeoutHandle) {\n clearTimeout(timeoutHandle);\n }\n abortSignal?.removeEventListener('abort', finish);\n resolve();\n };\n\n const tick = () => {\n if (remaining <= 0) {\n finish();\n return;\n }\n const chunk = Math.min(remaining, MAX_TIMEOUT_MS);\n remaining -= chunk;\n timeoutHandle = setTimeout(tick, chunk);\n };\n\n abortSignal?.addEventListener('abort', finish);\n tick();\n });\n}\n\n/**\n * Creates a new AbortController that, in addition to working as a regular\n * standalone controller, also gets aborted if the given parent signal\n * reaches aborted state.\n *\n * @param parent - The \"parent\" signal that can trigger the delegate\n */\nexport function delegateAbortController(parent?: AbortSignal): AbortController {\n const delegate = new AbortController();\n\n if (parent) {\n if (parent.aborted) {\n delegate.abort();\n } else {\n const onParentAborted = () => {\n delegate.abort();\n };\n\n const onChildAborted = () => {\n parent.removeEventListener('abort', onParentAborted);\n };\n\n parent.addEventListener('abort', onParentAborted, { once: true });\n delegate.signal.addEventListener('abort', onChildAborted, { once: true });\n }\n }\n\n return delegate;\n}\n\nexport function serializeError(error: Error): string {\n return JSON.stringify(\n internalSerializeError(error, {\n includeStack: process.env.NODE_ENV === 'development',\n }),\n );\n}\n"],"names":["InputError","DateTime","internalSerializeError"],"mappings":"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"}
|
package/dist/index.cjs.js
CHANGED
|
@@ -6,5 +6,6 @@ var discoveryFeatureLoader = require('./discoveryFeatureLoader.cjs.js');
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
exports.createBackend = CreateBackend.createBackend;
|
|
9
|
+
exports.defaultServiceFactories = CreateBackend.defaultServiceFactories;
|
|
9
10
|
exports.discoveryFeatureLoader = discoveryFeatureLoader.discoveryFeatureLoader;
|
|
10
11
|
//# sourceMappingURL=index.cjs.js.map
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { Backend } from '@backstage/backend-app-api';
|
|
2
2
|
import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
|
|
3
|
+
import { ServiceFactory } from '@backstage/backend-plugin-api';
|
|
3
4
|
|
|
5
|
+
/** @public */
|
|
6
|
+
declare const defaultServiceFactories: ServiceFactory[];
|
|
4
7
|
/**
|
|
5
8
|
* @public
|
|
6
9
|
*/
|
|
@@ -26,4 +29,4 @@ declare function createBackend(): Backend;
|
|
|
26
29
|
*/
|
|
27
30
|
declare const discoveryFeatureLoader: _backstage_backend_plugin_api.BackendFeature;
|
|
28
31
|
|
|
29
|
-
export { createBackend, discoveryFeatureLoader };
|
|
32
|
+
export { createBackend, defaultServiceFactories, discoveryFeatureLoader };
|
package/dist/package.json.cjs.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/backend-defaults",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.1-next.1",
|
|
4
4
|
"description": "Backend defaults used by Backstage backend apps",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "node-library"
|
|
@@ -218,20 +218,20 @@
|
|
|
218
218
|
"@aws-sdk/types": "^3.347.0",
|
|
219
219
|
"@azure/identity": "^4.0.0",
|
|
220
220
|
"@azure/storage-blob": "^12.5.0",
|
|
221
|
-
"@backstage/backend-app-api": "
|
|
222
|
-
"@backstage/backend-dev-utils": "
|
|
223
|
-
"@backstage/backend-plugin-api": "
|
|
224
|
-
"@backstage/cli-node": "
|
|
225
|
-
"@backstage/config": "
|
|
226
|
-
"@backstage/config-loader": "
|
|
227
|
-
"@backstage/errors": "
|
|
228
|
-
"@backstage/integration": "
|
|
229
|
-
"@backstage/integration-aws-node": "
|
|
230
|
-
"@backstage/plugin-auth-node": "
|
|
231
|
-
"@backstage/plugin-events-node": "
|
|
232
|
-
"@backstage/plugin-permission-common": "
|
|
233
|
-
"@backstage/plugin-permission-node": "
|
|
234
|
-
"@backstage/types": "
|
|
221
|
+
"@backstage/backend-app-api": "1.7.0-next.0",
|
|
222
|
+
"@backstage/backend-dev-utils": "0.1.7",
|
|
223
|
+
"@backstage/backend-plugin-api": "1.9.1-next.0",
|
|
224
|
+
"@backstage/cli-node": "0.3.2-next.1",
|
|
225
|
+
"@backstage/config": "1.3.8-next.0",
|
|
226
|
+
"@backstage/config-loader": "1.10.11-next.0",
|
|
227
|
+
"@backstage/errors": "1.3.1-next.0",
|
|
228
|
+
"@backstage/integration": "2.0.2-next.0",
|
|
229
|
+
"@backstage/integration-aws-node": "0.1.22-next.0",
|
|
230
|
+
"@backstage/plugin-auth-node": "0.7.1-next.1",
|
|
231
|
+
"@backstage/plugin-events-node": "0.4.22-next.0",
|
|
232
|
+
"@backstage/plugin-permission-common": "0.9.9-next.1",
|
|
233
|
+
"@backstage/plugin-permission-node": "0.10.13-next.0",
|
|
234
|
+
"@backstage/types": "1.2.2",
|
|
235
235
|
"@google-cloud/storage": "^7.0.0",
|
|
236
236
|
"@keyv/memcache": "^2.0.1",
|
|
237
237
|
"@keyv/redis": "^4.0.1",
|
|
@@ -276,7 +276,6 @@
|
|
|
276
276
|
"selfsigned": "^2.0.0",
|
|
277
277
|
"tar": "^7.5.6",
|
|
278
278
|
"triple-beam": "^1.4.1",
|
|
279
|
-
"uuid": "^11.0.0",
|
|
280
279
|
"winston": "^3.2.1",
|
|
281
280
|
"winston-transport": "^4.5.0",
|
|
282
281
|
"yauzl": "^3.2.1",
|
|
@@ -286,9 +285,8 @@
|
|
|
286
285
|
},
|
|
287
286
|
"devDependencies": {
|
|
288
287
|
"@aws-sdk/util-stream-node": "^3.350.0",
|
|
289
|
-
"@backstage/backend-
|
|
290
|
-
"@backstage/
|
|
291
|
-
"@backstage/cli": "^0.36.1",
|
|
288
|
+
"@backstage/backend-test-utils": "1.11.3-next.1",
|
|
289
|
+
"@backstage/cli": "0.36.2-next.1",
|
|
292
290
|
"@google-cloud/cloud-sql-connector": "^1.4.0",
|
|
293
291
|
"@types/archiver": "^7.0.0",
|
|
294
292
|
"@types/base64-stream": "^1.0.2",
|