typekro 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/alchemy/deployers.d.ts +2 -2
- package/dist/alchemy/deployers.d.ts.map +1 -1
- package/dist/alchemy/deployers.js +12 -5
- package/dist/alchemy/deployers.js.map +1 -1
- package/dist/alchemy/index.d.ts +4 -2
- package/dist/alchemy/index.d.ts.map +1 -1
- package/dist/alchemy/index.js +4 -3
- package/dist/alchemy/index.js.map +1 -1
- package/dist/alchemy/resource-registration.d.ts +81 -19
- package/dist/alchemy/resource-registration.d.ts.map +1 -1
- package/dist/alchemy/resource-registration.js +296 -147
- package/dist/alchemy/resource-registration.js.map +1 -1
- package/dist/alchemy/types.d.ts +76 -8
- package/dist/alchemy/types.d.ts.map +1 -1
- package/dist/alchemy/wrapper.d.ts +0 -9
- package/dist/alchemy/wrapper.d.ts.map +1 -1
- package/dist/alchemy/wrapper.js +0 -15
- package/dist/alchemy/wrapper.js.map +1 -1
- package/dist/core/deployment/direct-factory.d.ts +14 -2
- package/dist/core/deployment/direct-factory.d.ts.map +1 -1
- package/dist/core/deployment/direct-factory.js +117 -14
- package/dist/core/deployment/direct-factory.js.map +1 -1
- package/dist/core/deployment/engine.d.ts +8 -5
- package/dist/core/deployment/engine.d.ts.map +1 -1
- package/dist/core/deployment/engine.js +36 -12
- package/dist/core/deployment/engine.js.map +1 -1
- package/dist/core/deployment/kro-factory.d.ts +35 -9
- package/dist/core/deployment/kro-factory.d.ts.map +1 -1
- package/dist/core/deployment/kro-factory.js +132 -150
- package/dist/core/deployment/kro-factory.js.map +1 -1
- package/dist/core/deployment/shared-utilities.d.ts +15 -4
- package/dist/core/deployment/shared-utilities.d.ts.map +1 -1
- package/dist/core/deployment/shared-utilities.js +47 -14
- package/dist/core/deployment/shared-utilities.js.map +1 -1
- package/dist/core/deployment/strategies/direct-strategy.d.ts.map +1 -1
- package/dist/core/deployment/strategies/direct-strategy.js +1 -2
- package/dist/core/deployment/strategies/direct-strategy.js.map +1 -1
- package/dist/core/deployment/strategies/index.d.ts +0 -1
- package/dist/core/deployment/strategies/index.d.ts.map +1 -1
- package/dist/core/deployment/strategies/index.js +0 -1
- package/dist/core/deployment/strategies/index.js.map +1 -1
- package/dist/core/types/deployment.d.ts +28 -42
- package/dist/core/types/deployment.d.ts.map +1 -1
- package/dist/core/types/resource-graph.d.ts +2 -6
- package/dist/core/types/resource-graph.d.ts.map +1 -1
- package/dist/core/types/schema.d.ts +0 -1
- package/dist/core/types/schema.d.ts.map +1 -1
- package/dist/core/types/serialization.d.ts +3 -7
- package/dist/core/types/serialization.d.ts.map +1 -1
- package/dist/factories/kubernetes/yaml/yaml-directory.d.ts.map +1 -1
- package/dist/factories/kubernetes/yaml/yaml-directory.js +2 -13
- package/dist/factories/kubernetes/yaml/yaml-directory.js.map +1 -1
- package/dist/factories/kubernetes/yaml/yaml-file.d.ts.map +1 -1
- package/dist/factories/kubernetes/yaml/yaml-file.js +2 -18
- package/dist/factories/kubernetes/yaml/yaml-file.js.map +1 -1
- package/package.json +6 -3
- package/dist/alchemy/deployment.d.ts +0 -12
- package/dist/alchemy/deployment.d.ts.map +0 -1
- package/dist/alchemy/deployment.js +0 -12
- package/dist/alchemy/deployment.js.map +0 -1
- package/dist/core/deployment/strategies/alchemy-strategy.d.ts +0 -109
- package/dist/core/deployment/strategies/alchemy-strategy.d.ts.map +0 -1
- package/dist/core/deployment/strategies/alchemy-strategy.js +0 -556
- package/dist/core/deployment/strategies/alchemy-strategy.js.map +0 -1
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* This factory handles deployment via Kro ResourceGraphDefinitions,
|
|
5
5
|
* using the Kro controller for dependency resolution and resource management.
|
|
6
6
|
*/
|
|
7
|
+
import type { AlchemyResourceDeclaration } from '../../alchemy/types.js';
|
|
7
8
|
import type { FactoryOptions, FactoryStatus, KroResourceFactory, RGDStatus } from '../types/deployment.js';
|
|
8
9
|
import type { Enhanced, KubernetesResource } from '../types/kubernetes.js';
|
|
9
10
|
import type { KroCompatibleType, MagicAssignableShape, SchemaDefinition, SchemaProxy } from '../types/serialization.js';
|
|
@@ -48,14 +49,12 @@ export declare class KroResourceFactoryImpl<TSpec extends KroCompatibleType, TSt
|
|
|
48
49
|
readonly mode: "kro";
|
|
49
50
|
readonly name: string;
|
|
50
51
|
readonly namespace: string;
|
|
51
|
-
readonly isAlchemyManaged: boolean;
|
|
52
52
|
readonly rgdName: string;
|
|
53
53
|
readonly schema: SchemaProxy<TSpec, TStatus>;
|
|
54
54
|
private readonly resources;
|
|
55
55
|
private readonly closures;
|
|
56
56
|
private readonly schemaDefinition;
|
|
57
57
|
private readonly statusMappings;
|
|
58
|
-
private readonly alchemyScope;
|
|
59
58
|
private readonly singletonDefinitions;
|
|
60
59
|
private readonly singletonOwnerStatuses;
|
|
61
60
|
private readonly logger;
|
|
@@ -84,7 +83,6 @@ export declare class KroResourceFactoryImpl<TSpec extends KroCompatibleType, TSt
|
|
|
84
83
|
private discoveredPlural;
|
|
85
84
|
private readonly kroCustomResourceProvider;
|
|
86
85
|
private readonly rgdProvider;
|
|
87
|
-
private readonly alchemyBridge;
|
|
88
86
|
constructor(name: string, resources: Record<string, KubernetesResource>, schemaDefinition: SchemaDefinition<TSpec, TStatus>, statusMappings: MagicAssignableShape<TStatus>, options?: FactoryOptions);
|
|
89
87
|
/** Extract nested composition status CEL mappings from the raw status object. */
|
|
90
88
|
private getNestedStatusCel;
|
|
@@ -142,6 +140,14 @@ export declare class KroResourceFactoryImpl<TSpec extends KroCompatibleType, TSt
|
|
|
142
140
|
instanceNameOverride?: string;
|
|
143
141
|
singletonSpecFingerprint?: string;
|
|
144
142
|
}): Promise<Enhanced<TSpec, TStatus>>;
|
|
143
|
+
/**
|
|
144
|
+
* Discover the singleton-owner definitions this composition depends on (deduped by key): from
|
|
145
|
+
* re-executing the composition fn under a discovery context, plus any injected definitions.
|
|
146
|
+
* Shared by {@link ensureSingletonOwners} (imperative deploy) and {@link toAlchemyResources}.
|
|
147
|
+
*/
|
|
148
|
+
private discoverSingletonDefinitions;
|
|
149
|
+
/** Build the KRO factory that owns a singleton's RGD + instance (its registry namespace). */
|
|
150
|
+
private singletonFactoryFor;
|
|
145
151
|
private ensureSingletonOwners;
|
|
146
152
|
private getSingletonOwnerInstancesForDriftCheck;
|
|
147
153
|
private isMissingSingletonOwnerCrdError;
|
|
@@ -154,12 +160,6 @@ export declare class KroResourceFactoryImpl<TSpec extends KroCompatibleType, TSt
|
|
|
154
160
|
* Deploy directly to Kubernetes using DirectDeploymentEngine
|
|
155
161
|
*/
|
|
156
162
|
private deployDirect;
|
|
157
|
-
/**
|
|
158
|
-
* Deploy using type-safe alchemy resource wrapping
|
|
159
|
-
*
|
|
160
|
-
* In alchemy mode, the RGD gets one typed alchemy Resource and each instance gets another
|
|
161
|
-
*/
|
|
162
|
-
private deployWithAlchemy;
|
|
163
163
|
/**
|
|
164
164
|
* Get all deployed instances
|
|
165
165
|
*/
|
|
@@ -173,7 +173,33 @@ export declare class KroResourceFactoryImpl<TSpec extends KroCompatibleType, TSt
|
|
|
173
173
|
scopes?: string[];
|
|
174
174
|
includeUnscopedResources?: boolean;
|
|
175
175
|
}): Promise<void>;
|
|
176
|
+
/**
|
|
177
|
+
* Emit this factory's KRO deployment as declarative alchemy **v2** resources — the v2
|
|
178
|
+
* analog of the removed imperative `deployWithAlchemy(scope)`. Returns, in dependency order, a
|
|
179
|
+
* declaration for each discovered **singleton owner** (its own RGD + instance — mirroring
|
|
180
|
+
* `ensureSingletonOwners` in the imperative `deploy()` path), then the **RGD** (shared, deployed
|
|
181
|
+
* once), then the **CR instance** (one per `spec`, which `dependsOn` the RGD + the singletons),
|
|
182
|
+
* matching the old integration's per-resource state granularity. The caller instantiates
|
|
183
|
+
* each with `KroResource` inside their Stack (RGD first so reverse-topo removes the
|
|
184
|
+
* instance before the shared RGD):
|
|
185
|
+
*
|
|
186
|
+
* ```ts
|
|
187
|
+
* for (const { id, props } of await factory.toAlchemyResources(spec)) {
|
|
188
|
+
* yield* KroResource(id, props);
|
|
189
|
+
* }
|
|
190
|
+
* ```
|
|
191
|
+
*
|
|
192
|
+
* Each declaration carries serialized `kubeConfigOptions` (so reconcile can reconnect to the
|
|
193
|
+
* cluster after state rehydration) and `kroDeletion` (so delete is finalizer-safe / shared-RGD
|
|
194
|
+
* aware). The actual deploy/teardown runs in `kroProvider`'s reconcile/delete.
|
|
195
|
+
*/
|
|
196
|
+
toAlchemyResources(spec: TSpec, opts?: {
|
|
197
|
+
instanceNameOverride?: string;
|
|
198
|
+
singletonSpecFingerprint?: string;
|
|
199
|
+
}): Promise<AlchemyResourceDeclaration[]>;
|
|
200
|
+
/** Serialize the factory's cluster connection so an alchemy resource can reconnect after rehydration. */
|
|
176
201
|
private extractKubeConfigOptionsForAlchemy;
|
|
202
|
+
/** Build the finalizer-safe, shared-RGD-aware deletion metadata for this factory's instances. */
|
|
177
203
|
private createAlchemyKroDeletionOptions;
|
|
178
204
|
/**
|
|
179
205
|
* Get factory status
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kro-factory.d.ts","sourceRoot":"","sources":["../../../src/core/deployment/kro-factory.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"kro-factory.d.ts","sourceRoot":"","sources":["../../../src/core/deployment/kro-factory.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,6 +18,7 @@ import { CRDInstanceError, ensureError, ResourceGraphFactoryError, TypeKroError,
|
|
|
18
18
|
import { applyAnalysisToResources } from '../expressions/composition/composition-analyzer.js';
|
|
19
19
|
import { createBunCompatibleKubernetesObjectApi } from '../kubernetes/index.js';
|
|
20
20
|
import { getComponentLogger } from '../logging/index.js';
|
|
21
|
+
import { createAlchemyResourceId } from '../../alchemy/utilities.js';
|
|
21
22
|
// Dependency inversion: kroCustomResource, resourceGraphDefinition, and
|
|
22
23
|
// alchemy bridge are injected via FactoryOptions providers (Phase 3.5)
|
|
23
24
|
// instead of dynamic import() from higher layers.
|
|
@@ -32,7 +33,7 @@ import { DirectDeploymentEngine } from './engine.js';
|
|
|
32
33
|
import { logHandleSnapshot } from './handle-tracing.js';
|
|
33
34
|
import { isNotFoundError } from './k8s-helpers.js';
|
|
34
35
|
import { waitForKroInstanceReady as waitForKroInstanceReadyShared } from './kro-readiness.js';
|
|
35
|
-
import { convertToKubernetesName, generateInstanceName, getSingletonInstanceName, pluralizeKind,
|
|
36
|
+
import { convertToKubernetesName, extractSerializableKubeConfigOptions, generateInstanceName, getSingletonInstanceName, pluralizeKind, validateSpec, } from './shared-utilities.js';
|
|
36
37
|
import { joinYamlDocuments, singletonOwnerInstanceYamls, singletonRgdYamls, } from './singleton-gitops.js';
|
|
37
38
|
import { assertNoDeployedSingletonSpecDrift, singletonSpecFingerprintAnnotationValue, } from './singleton-owner-drift.js';
|
|
38
39
|
/**
|
|
@@ -104,14 +105,12 @@ export class KroResourceFactoryImpl {
|
|
|
104
105
|
mode = 'kro';
|
|
105
106
|
name;
|
|
106
107
|
namespace;
|
|
107
|
-
isAlchemyManaged;
|
|
108
108
|
rgdName;
|
|
109
109
|
schema;
|
|
110
110
|
resources;
|
|
111
111
|
closures;
|
|
112
112
|
schemaDefinition;
|
|
113
113
|
statusMappings;
|
|
114
|
-
alchemyScope;
|
|
115
114
|
singletonDefinitions;
|
|
116
115
|
singletonOwnerStatuses = new Map();
|
|
117
116
|
logger = getComponentLogger('kro-factory');
|
|
@@ -142,12 +141,9 @@ export class KroResourceFactoryImpl {
|
|
|
142
141
|
// instead of dynamic import() from factories/ and alchemy/ layers.
|
|
143
142
|
kroCustomResourceProvider;
|
|
144
143
|
rgdProvider;
|
|
145
|
-
alchemyBridge;
|
|
146
144
|
constructor(name, resources, schemaDefinition, statusMappings, options = {}) {
|
|
147
145
|
this.name = name;
|
|
148
146
|
this.namespace = options.namespace || 'default';
|
|
149
|
-
this.alchemyScope = options.alchemyScope;
|
|
150
|
-
this.isAlchemyManaged = !!options.alchemyScope;
|
|
151
147
|
this.rgdName = convertToKubernetesName(name); // Convert to valid Kubernetes resource name
|
|
152
148
|
this.resources = resources;
|
|
153
149
|
this.closures = options.closures || {};
|
|
@@ -164,7 +160,6 @@ export class KroResourceFactoryImpl {
|
|
|
164
160
|
// Injected providers — fall back to dynamic import() for backward compatibility
|
|
165
161
|
this.kroCustomResourceProvider = options.kroCustomResourceProvider;
|
|
166
162
|
this.rgdProvider = options.rgdProvider;
|
|
167
|
-
this.alchemyBridge = options.alchemyBridge;
|
|
168
163
|
// Validate closures for Kro mode - detect KubernetesRef inputs and raise clear errors
|
|
169
164
|
this.validateClosuresForKroMode();
|
|
170
165
|
}
|
|
@@ -397,53 +392,49 @@ export class KroResourceFactoryImpl {
|
|
|
397
392
|
// Execute closures before RGD creation (Kro mode requirement)
|
|
398
393
|
await this.executeClosuresBeforeRGD(spec);
|
|
399
394
|
await this.ensureSingletonOwners(spec);
|
|
400
|
-
|
|
401
|
-
return this.deployWithAlchemy(spec, opts?.instanceNameOverride, opts?.singletonSpecFingerprint);
|
|
402
|
-
}
|
|
403
|
-
else {
|
|
404
|
-
return this.deployDirect(spec, opts?.instanceNameOverride, opts?.singletonSpecFingerprint);
|
|
405
|
-
}
|
|
395
|
+
return this.deployDirect(spec, opts?.instanceNameOverride, opts?.singletonSpecFingerprint);
|
|
406
396
|
}
|
|
407
|
-
|
|
408
|
-
|
|
397
|
+
/**
|
|
398
|
+
* Discover the singleton-owner definitions this composition depends on (deduped by key): from
|
|
399
|
+
* re-executing the composition fn under a discovery context, plus any injected definitions.
|
|
400
|
+
* Shared by {@link ensureSingletonOwners} (imperative deploy) and {@link toAlchemyResources}.
|
|
401
|
+
*/
|
|
402
|
+
discoverSingletonDefinitions(spec) {
|
|
403
|
+
const discovered = new Map();
|
|
409
404
|
if (this.factoryOptions.compositionFn) {
|
|
410
|
-
const
|
|
411
|
-
runWithCompositionContext(
|
|
405
|
+
const ctx = createCompositionContext('singleton-owner-discovery');
|
|
406
|
+
runWithCompositionContext(ctx, () => {
|
|
412
407
|
this.factoryOptions.compositionFn?.(spec);
|
|
413
408
|
});
|
|
414
|
-
for (const [key, definition] of
|
|
415
|
-
|
|
409
|
+
for (const [key, definition] of ctx.singletonDefinitions ?? []) {
|
|
410
|
+
discovered.set(key, definition);
|
|
416
411
|
}
|
|
417
412
|
}
|
|
418
413
|
for (const definition of this.singletonDefinitions) {
|
|
419
|
-
if (!
|
|
420
|
-
|
|
421
|
-
}
|
|
414
|
+
if (!discovered.has(definition.key))
|
|
415
|
+
discovered.set(definition.key, definition);
|
|
422
416
|
}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
417
|
+
return Array.from(discovered.values());
|
|
418
|
+
}
|
|
419
|
+
/** Build the KRO factory that owns a singleton's RGD + instance (its registry namespace). */
|
|
420
|
+
singletonFactoryFor(definition) {
|
|
421
|
+
return definition.composition.factory('kro', {
|
|
422
|
+
namespace: definition.registryNamespace,
|
|
423
|
+
waitForReady: true,
|
|
424
|
+
...(this.factoryOptions.timeout !== undefined ? { timeout: this.factoryOptions.timeout } : {}),
|
|
425
|
+
...(this.factoryOptions.kubeConfig !== undefined
|
|
426
|
+
? { kubeConfig: this.factoryOptions.kubeConfig }
|
|
427
|
+
: {}),
|
|
428
|
+
...(this.factoryOptions.skipTLSVerify !== undefined
|
|
429
|
+
? { skipTLSVerify: this.factoryOptions.skipTLSVerify }
|
|
430
|
+
: {}),
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
async ensureSingletonOwners(spec) {
|
|
434
|
+
for (const definition of this.discoverSingletonDefinitions(spec)) {
|
|
432
435
|
await this.ensureTargetNamespace(definition.registryNamespace);
|
|
433
436
|
const singletonInstanceName = getSingletonInstanceName(definition.id);
|
|
434
|
-
const singletonFactory =
|
|
435
|
-
namespace: definition.registryNamespace,
|
|
436
|
-
waitForReady: true,
|
|
437
|
-
...(this.factoryOptions.timeout !== undefined
|
|
438
|
-
? { timeout: this.factoryOptions.timeout }
|
|
439
|
-
: {}),
|
|
440
|
-
...(this.factoryOptions.kubeConfig !== undefined
|
|
441
|
-
? { kubeConfig: this.factoryOptions.kubeConfig }
|
|
442
|
-
: {}),
|
|
443
|
-
...(this.factoryOptions.skipTLSVerify !== undefined
|
|
444
|
-
? { skipTLSVerify: this.factoryOptions.skipTLSVerify }
|
|
445
|
-
: {}),
|
|
446
|
-
});
|
|
437
|
+
const singletonFactory = this.singletonFactoryFor(definition);
|
|
447
438
|
try {
|
|
448
439
|
this.logger.info('Ensuring singleton owner boundary', {
|
|
449
440
|
singletonId: definition.id,
|
|
@@ -542,7 +533,6 @@ export class KroResourceFactoryImpl {
|
|
|
542
533
|
const deploymentContext = {
|
|
543
534
|
kubernetesApi: createBunCompatibleKubernetesObjectApi(kubeConfig),
|
|
544
535
|
kubeConfig: kubeConfig,
|
|
545
|
-
...(this.alchemyScope && { alchemyScope: this.alchemyScope }),
|
|
546
536
|
namespace: this.namespace,
|
|
547
537
|
deployedResources: new Map(), // Empty for pre-RGD execution
|
|
548
538
|
resolveReference: async (ref) => {
|
|
@@ -653,78 +643,6 @@ export class KroResourceFactoryImpl {
|
|
|
653
643
|
await deploymentEngine.dispose();
|
|
654
644
|
}
|
|
655
645
|
}
|
|
656
|
-
/**
|
|
657
|
-
* Deploy using type-safe alchemy resource wrapping
|
|
658
|
-
*
|
|
659
|
-
* In alchemy mode, the RGD gets one typed alchemy Resource and each instance gets another
|
|
660
|
-
*/
|
|
661
|
-
async deployWithAlchemy(spec, instanceNameOverride, singletonSpecFingerprint) {
|
|
662
|
-
validateAlchemyScope(this.alchemyScope, 'KRO Alchemy deployment');
|
|
663
|
-
const alchemyScope = this.alchemyScope;
|
|
664
|
-
// Use static registration functions
|
|
665
|
-
// Create deployer instance using DirectDeploymentEngine with KRO mode
|
|
666
|
-
const kroEngine = new DirectDeploymentEngine(this.getKubeConfig(), undefined, undefined, DeploymentMode.KRO);
|
|
667
|
-
const kubeConfigOptions = this.extractKubeConfigOptionsForAlchemy();
|
|
668
|
-
const kroDeletion = this.createAlchemyKroDeletionOptions();
|
|
669
|
-
try {
|
|
670
|
-
// 1. Ensure RGD is deployed via alchemy (once per factory). Reuse the
|
|
671
|
-
// normal serializer so externalRef/forEach/includeWhen/readyWhen and
|
|
672
|
-
// singleton owner boundaries match non-Alchemy KRO deploys.
|
|
673
|
-
const rgdManifest = yaml.load(this.buildRgdYaml());
|
|
674
|
-
// Register RGD type dynamically
|
|
675
|
-
const rgdFactory = this.rgdProvider ??
|
|
676
|
-
(await import('../../factories/kro/resource-graph-definition.js')).resourceGraphDefinition;
|
|
677
|
-
const rgdEnhanced = rgdFactory(rgdManifest);
|
|
678
|
-
const bridge = this.alchemyBridge ?? (await import('../../alchemy/deployment.js'));
|
|
679
|
-
const RGDProvider = bridge.ensureResourceTypeRegistered(rgdEnhanced);
|
|
680
|
-
const rgdId = bridge.createAlchemyResourceId(rgdEnhanced, this.namespace);
|
|
681
|
-
await alchemyScope.run(async () => {
|
|
682
|
-
await RGDProvider(rgdId, {
|
|
683
|
-
resource: rgdEnhanced,
|
|
684
|
-
namespace: this.namespace,
|
|
685
|
-
deploymentStrategy: 'kro',
|
|
686
|
-
kubeConfigOptions,
|
|
687
|
-
kroDeletion,
|
|
688
|
-
options: {
|
|
689
|
-
waitForReady: true,
|
|
690
|
-
timeout: DEFAULT_RGD_TIMEOUT, // RGD should be ready quickly
|
|
691
|
-
},
|
|
692
|
-
});
|
|
693
|
-
});
|
|
694
|
-
await this.waitForCRDReadyWithEngine(kroEngine);
|
|
695
|
-
// 2. Create instance via alchemy (once per deploy call)
|
|
696
|
-
await this.ensureTargetNamespace();
|
|
697
|
-
const instanceName = instanceNameOverride ?? generateInstanceName(spec, this.name);
|
|
698
|
-
const crdInstanceManifest = this.createCustomResourceInstance(instanceName, spec, singletonSpecFingerprint);
|
|
699
|
-
// Register CRD instance type dynamically
|
|
700
|
-
// Cast required: crdInstanceManifest is a plain KubernetesResource, but alchemy functions
|
|
701
|
-
// expect Enhanced<unknown, unknown>. They only access kind/metadata.name for type inference.
|
|
702
|
-
const crdAsEnhanced = crdInstanceManifest;
|
|
703
|
-
const CRDInstanceProvider = bridge.ensureResourceTypeRegistered(crdAsEnhanced);
|
|
704
|
-
const instanceId = bridge.createAlchemyResourceId(crdAsEnhanced, this.namespace);
|
|
705
|
-
await alchemyScope.run(async () => {
|
|
706
|
-
await CRDInstanceProvider(instanceId, {
|
|
707
|
-
resource: crdAsEnhanced,
|
|
708
|
-
namespace: this.namespace,
|
|
709
|
-
deploymentStrategy: 'kro',
|
|
710
|
-
kubeConfigOptions,
|
|
711
|
-
kroDeletion,
|
|
712
|
-
options: {
|
|
713
|
-
waitForReady: false,
|
|
714
|
-
timeout: this.factoryOptions.timeout ?? DEFAULT_DEPLOYMENT_TIMEOUT,
|
|
715
|
-
},
|
|
716
|
-
});
|
|
717
|
-
});
|
|
718
|
-
if (this.factoryOptions.waitForReady ?? true) {
|
|
719
|
-
await this.waitForKroInstanceReady(instanceName, this.factoryOptions.timeout || DEFAULT_KRO_INSTANCE_TIMEOUT);
|
|
720
|
-
}
|
|
721
|
-
// Create Enhanced proxy for the deployed instance
|
|
722
|
-
return await this.createEnhancedProxy(spec, instanceName);
|
|
723
|
-
}
|
|
724
|
-
finally {
|
|
725
|
-
await kroEngine.dispose();
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
646
|
/**
|
|
729
647
|
* Get all deployed instances
|
|
730
648
|
*/
|
|
@@ -969,41 +887,106 @@ export class KroResourceFactoryImpl {
|
|
|
969
887
|
// KRO's finalizer processing handles deleting all child resources
|
|
970
888
|
// (including Namespaces) via its applyset — no manual cleanup needed.
|
|
971
889
|
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
890
|
+
/**
|
|
891
|
+
* Emit this factory's KRO deployment as declarative alchemy **v2** resources — the v2
|
|
892
|
+
* analog of the removed imperative `deployWithAlchemy(scope)`. Returns, in dependency order, a
|
|
893
|
+
* declaration for each discovered **singleton owner** (its own RGD + instance — mirroring
|
|
894
|
+
* `ensureSingletonOwners` in the imperative `deploy()` path), then the **RGD** (shared, deployed
|
|
895
|
+
* once), then the **CR instance** (one per `spec`, which `dependsOn` the RGD + the singletons),
|
|
896
|
+
* matching the old integration's per-resource state granularity. The caller instantiates
|
|
897
|
+
* each with `KroResource` inside their Stack (RGD first so reverse-topo removes the
|
|
898
|
+
* instance before the shared RGD):
|
|
899
|
+
*
|
|
900
|
+
* ```ts
|
|
901
|
+
* for (const { id, props } of await factory.toAlchemyResources(spec)) {
|
|
902
|
+
* yield* KroResource(id, props);
|
|
903
|
+
* }
|
|
904
|
+
* ```
|
|
905
|
+
*
|
|
906
|
+
* Each declaration carries serialized `kubeConfigOptions` (so reconcile can reconnect to the
|
|
907
|
+
* cluster after state rehydration) and `kroDeletion` (so delete is finalizer-safe / shared-RGD
|
|
908
|
+
* aware). The actual deploy/teardown runs in `kroProvider`'s reconcile/delete.
|
|
909
|
+
*/
|
|
910
|
+
async toAlchemyResources(spec, opts) {
|
|
911
|
+
const kubeConfigOptions = this.extractKubeConfigOptionsForAlchemy();
|
|
912
|
+
const kroDeletion = this.createAlchemyKroDeletionOptions();
|
|
913
|
+
// 0. Singleton owners. The imperative `deploy()` ensures shared singleton owners (each its own
|
|
914
|
+
// RGD + instance, in its registry namespace) via `ensureSingletonOwners` BEFORE the main
|
|
915
|
+
// instance; emit them as declarations too so the declarative path has the same boundaries.
|
|
916
|
+
// Their deterministic ids/names mean alchemy dedupes a singleton shared across compositions.
|
|
917
|
+
// (The runtime spec-drift check from `ensureSingletonOwners` is omitted here — `toAlchemyResources`
|
|
918
|
+
// stays cluster-free — but the spec-fingerprint annotation it relied on is still emitted.)
|
|
919
|
+
const singletonDeclarations = [];
|
|
920
|
+
const singletonInstanceIds = [];
|
|
921
|
+
for (const definition of this.discoverSingletonDefinitions(spec)) {
|
|
922
|
+
const singletonFactory = this.singletonFactoryFor(definition);
|
|
923
|
+
try {
|
|
924
|
+
const decls = await singletonFactory.toAlchemyResources(definition.spec, {
|
|
925
|
+
instanceNameOverride: getSingletonInstanceName(definition.id),
|
|
926
|
+
singletonSpecFingerprint: singletonSpecFingerprintAnnotationValue(definition.specFingerprint),
|
|
927
|
+
});
|
|
928
|
+
singletonDeclarations.push(...decls);
|
|
929
|
+
// `toAlchemyResources` returns the owner's OWN CR instance LAST (after any of ITS nested
|
|
930
|
+
// singletons + its RGD), so the consumer must wait on `decls[last]` — using "first decl
|
|
931
|
+
// with a dependency" would wrongly point at a nested singleton's instance.
|
|
932
|
+
const ownerInstance = decls[decls.length - 1];
|
|
933
|
+
if (ownerInstance)
|
|
934
|
+
singletonInstanceIds.push(ownerInstance.id);
|
|
935
|
+
}
|
|
936
|
+
finally {
|
|
937
|
+
await singletonFactory.dispose?.();
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
// 1. RGD declaration (deployed once per factory; shared by all instances). Reuse the normal
|
|
941
|
+
// serializer so externalRef/forEach/includeWhen/readyWhen + singleton boundaries match
|
|
942
|
+
// non-alchemy KRO deploys.
|
|
943
|
+
const rgdManifest = yaml.load(this.buildRgdYaml());
|
|
944
|
+
const rgdFactory = this.rgdProvider ??
|
|
945
|
+
(await import('../../factories/kro/resource-graph-definition.js')).resourceGraphDefinition;
|
|
946
|
+
const rgdEnhanced = rgdFactory(rgdManifest);
|
|
947
|
+
const rgdId = createAlchemyResourceId(rgdEnhanced, this.namespace);
|
|
948
|
+
const rgdDeclaration = {
|
|
949
|
+
id: rgdId,
|
|
950
|
+
dependsOn: [],
|
|
951
|
+
props: {
|
|
952
|
+
resource: rgdEnhanced,
|
|
953
|
+
namespace: this.namespace,
|
|
954
|
+
deploymentStrategy: 'kro',
|
|
955
|
+
kubeConfigOptions,
|
|
956
|
+
kroDeletion,
|
|
957
|
+
options: { waitForReady: true, timeout: DEFAULT_RGD_TIMEOUT },
|
|
958
|
+
},
|
|
959
|
+
};
|
|
960
|
+
// 2. CR instance declaration (one per deploy call). Depends on the RGD so alchemy applies the
|
|
961
|
+
// instance only after the RGD's CRD is established (else the CR apply races a missing kind).
|
|
962
|
+
const instanceName = opts?.instanceNameOverride ?? generateInstanceName(spec, this.name);
|
|
963
|
+
const crdInstanceManifest = this.createCustomResourceInstance(instanceName, spec, opts?.singletonSpecFingerprint);
|
|
964
|
+
// Cast: a plain KubernetesResource is fine — the alchemy path only reads kind/metadata.
|
|
965
|
+
const crdAsEnhanced = crdInstanceManifest;
|
|
966
|
+
const instanceDeclaration = {
|
|
967
|
+
id: createAlchemyResourceId(crdAsEnhanced, this.namespace),
|
|
968
|
+
// Wait for the RGD's CRD AND for any singleton owners (mirrors `ensureSingletonOwners` running
|
|
969
|
+
// before the main deploy), so the instance applies only once its dependencies exist.
|
|
970
|
+
dependsOn: [rgdId, ...singletonInstanceIds],
|
|
971
|
+
props: {
|
|
972
|
+
resource: crdAsEnhanced,
|
|
973
|
+
namespace: this.namespace,
|
|
974
|
+
deploymentStrategy: 'kro',
|
|
975
|
+
kubeConfigOptions,
|
|
976
|
+
kroDeletion,
|
|
977
|
+
options: {
|
|
978
|
+
waitForReady: false,
|
|
979
|
+
timeout: this.factoryOptions.timeout ?? DEFAULT_DEPLOYMENT_TIMEOUT,
|
|
1003
980
|
},
|
|
1004
|
-
}
|
|
981
|
+
},
|
|
1005
982
|
};
|
|
983
|
+
return [...singletonDeclarations, rgdDeclaration, instanceDeclaration];
|
|
984
|
+
}
|
|
985
|
+
/** Serialize the factory's cluster connection so an alchemy resource can reconnect after rehydration. */
|
|
986
|
+
extractKubeConfigOptionsForAlchemy() {
|
|
987
|
+
return extractSerializableKubeConfigOptions(this.getKubeConfig(), this.factoryOptions.skipTLSVerify === true ? true : undefined);
|
|
1006
988
|
}
|
|
989
|
+
/** Build the finalizer-safe, shared-RGD-aware deletion metadata for this factory's instances. */
|
|
1007
990
|
createAlchemyKroDeletionOptions() {
|
|
1008
991
|
return {
|
|
1009
992
|
apiVersion: this.schemaDefinition.apiVersion,
|
|
@@ -1024,7 +1007,6 @@ export class KroResourceFactoryImpl {
|
|
|
1024
1007
|
return {
|
|
1025
1008
|
name: this.name,
|
|
1026
1009
|
mode: this.mode,
|
|
1027
|
-
isAlchemyManaged: this.isAlchemyManaged,
|
|
1028
1010
|
namespace: this.namespace,
|
|
1029
1011
|
instanceCount: instances.length,
|
|
1030
1012
|
health: rgdStatus.phase === 'ready' ? 'healthy' : 'degraded',
|