typekro 0.1.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/LICENSE +185 -0
- package/README.md +1137 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/alchemy/deployers.d.ts +39 -0
- package/dist/alchemy/deployers.d.ts.map +1 -0
- package/dist/alchemy/deployers.js +194 -0
- package/dist/alchemy/deployers.js.map +1 -0
- package/dist/alchemy/deployment.d.ts +12 -0
- package/dist/alchemy/deployment.d.ts.map +1 -0
- package/dist/alchemy/deployment.js +12 -0
- package/dist/alchemy/deployment.js.map +1 -0
- package/dist/alchemy/index.d.ts +17 -0
- package/dist/alchemy/index.d.ts.map +1 -0
- package/dist/alchemy/index.js +20 -0
- package/dist/alchemy/index.js.map +1 -0
- package/dist/alchemy/resolver.d.ts +127 -0
- package/dist/alchemy/resolver.d.ts.map +1 -0
- package/dist/alchemy/resolver.js +341 -0
- package/dist/alchemy/resolver.js.map +1 -0
- package/dist/alchemy/resource-registration.d.ts +22 -0
- package/dist/alchemy/resource-registration.d.ts.map +1 -0
- package/dist/alchemy/resource-registration.js +218 -0
- package/dist/alchemy/resource-registration.js.map +1 -0
- package/dist/alchemy/type-inference.d.ts +13 -0
- package/dist/alchemy/type-inference.d.ts.map +1 -0
- package/dist/alchemy/type-inference.js +67 -0
- package/dist/alchemy/type-inference.js.map +1 -0
- package/dist/alchemy/types.d.ts +92 -0
- package/dist/alchemy/types.d.ts.map +1 -0
- package/dist/alchemy/types.js +8 -0
- package/dist/alchemy/types.js.map +1 -0
- package/dist/alchemy/utilities.d.ts +12 -0
- package/dist/alchemy/utilities.d.ts.map +1 -0
- package/dist/alchemy/utilities.js +16 -0
- package/dist/alchemy/utilities.js.map +1 -0
- package/dist/alchemy/wrapper.d.ts +16 -0
- package/dist/alchemy/wrapper.d.ts.map +1 -0
- package/dist/alchemy/wrapper.js +23 -0
- package/dist/alchemy/wrapper.js.map +1 -0
- package/dist/core/composition/composition.d.ts +94 -0
- package/dist/core/composition/composition.d.ts.map +1 -0
- package/dist/core/composition/composition.js +230 -0
- package/dist/core/composition/composition.js.map +1 -0
- package/dist/core/composition/index.d.ts +12 -0
- package/dist/core/composition/index.d.ts.map +1 -0
- package/dist/core/composition/index.js +12 -0
- package/dist/core/composition/index.js.map +1 -0
- package/dist/core/composition/typekro-runtime/index.d.ts +3 -0
- package/dist/core/composition/typekro-runtime/index.d.ts.map +1 -0
- package/dist/core/composition/typekro-runtime/index.js +2 -0
- package/dist/core/composition/typekro-runtime/index.js.map +1 -0
- package/dist/core/composition/typekro-runtime/typekro-runtime.d.ts +39 -0
- package/dist/core/composition/typekro-runtime/typekro-runtime.d.ts.map +1 -0
- package/dist/core/composition/typekro-runtime/typekro-runtime.js +100 -0
- package/dist/core/composition/typekro-runtime/typekro-runtime.js.map +1 -0
- package/dist/core/composition/typekro-runtime/types.d.ts +16 -0
- package/dist/core/composition/typekro-runtime/types.d.ts.map +1 -0
- package/dist/core/composition/typekro-runtime/types.js +12 -0
- package/dist/core/composition/typekro-runtime/types.js.map +1 -0
- package/dist/core/composition/types.d.ts +264 -0
- package/dist/core/composition/types.d.ts.map +1 -0
- package/dist/core/composition/types.js +2 -0
- package/dist/core/composition/types.js.map +1 -0
- package/dist/core/constants/brands.d.ts +42 -0
- package/dist/core/constants/brands.d.ts.map +1 -0
- package/dist/core/constants/brands.js +51 -0
- package/dist/core/constants/brands.js.map +1 -0
- package/dist/core/dependencies/graph.d.ts +67 -0
- package/dist/core/dependencies/graph.d.ts.map +1 -0
- package/dist/core/dependencies/graph.js +220 -0
- package/dist/core/dependencies/graph.js.map +1 -0
- package/dist/core/dependencies/index.d.ts +7 -0
- package/dist/core/dependencies/index.d.ts.map +1 -0
- package/dist/core/dependencies/index.js +8 -0
- package/dist/core/dependencies/index.js.map +1 -0
- package/dist/core/dependencies/resolver.d.ts +53 -0
- package/dist/core/dependencies/resolver.d.ts.map +1 -0
- package/dist/core/dependencies/resolver.js +167 -0
- package/dist/core/dependencies/resolver.js.map +1 -0
- package/dist/core/dependencies/type-guards.d.ts +17 -0
- package/dist/core/dependencies/type-guards.d.ts.map +1 -0
- package/dist/core/dependencies/type-guards.js +35 -0
- package/dist/core/dependencies/type-guards.js.map +1 -0
- package/dist/core/deployment/deployment-strategies.d.ts +12 -0
- package/dist/core/deployment/deployment-strategies.d.ts.map +1 -0
- package/dist/core/deployment/deployment-strategies.js +11 -0
- package/dist/core/deployment/deployment-strategies.js.map +1 -0
- package/dist/core/deployment/direct-factory.d.ts +111 -0
- package/dist/core/deployment/direct-factory.d.ts.map +1 -0
- package/dist/core/deployment/direct-factory.js +627 -0
- package/dist/core/deployment/direct-factory.js.map +1 -0
- package/dist/core/deployment/engine.d.ts +128 -0
- package/dist/core/deployment/engine.d.ts.map +1 -0
- package/dist/core/deployment/engine.js +1408 -0
- package/dist/core/deployment/engine.js.map +1 -0
- package/dist/core/deployment/index.d.ts +14 -0
- package/dist/core/deployment/index.d.ts.map +1 -0
- package/dist/core/deployment/index.js +14 -0
- package/dist/core/deployment/index.js.map +1 -0
- package/dist/core/deployment/kro-factory.d.ts +143 -0
- package/dist/core/deployment/kro-factory.d.ts.map +1 -0
- package/dist/core/deployment/kro-factory.js +837 -0
- package/dist/core/deployment/kro-factory.js.map +1 -0
- package/dist/core/deployment/readiness.d.ts +54 -0
- package/dist/core/deployment/readiness.d.ts.map +1 -0
- package/dist/core/deployment/readiness.js +175 -0
- package/dist/core/deployment/readiness.js.map +1 -0
- package/dist/core/deployment/rollback-manager.d.ts +63 -0
- package/dist/core/deployment/rollback-manager.d.ts.map +1 -0
- package/dist/core/deployment/rollback-manager.js +225 -0
- package/dist/core/deployment/rollback-manager.js.map +1 -0
- package/dist/core/deployment/shared-utilities.d.ts +41 -0
- package/dist/core/deployment/shared-utilities.d.ts.map +1 -0
- package/dist/core/deployment/shared-utilities.js +100 -0
- package/dist/core/deployment/shared-utilities.js.map +1 -0
- package/dist/core/deployment/status-hydrator.d.ts +86 -0
- package/dist/core/deployment/status-hydrator.d.ts.map +1 -0
- package/dist/core/deployment/status-hydrator.js +262 -0
- package/dist/core/deployment/status-hydrator.js.map +1 -0
- package/dist/core/deployment/strategies/alchemy-strategy.d.ts +107 -0
- package/dist/core/deployment/strategies/alchemy-strategy.d.ts.map +1 -0
- package/dist/core/deployment/strategies/alchemy-strategy.js +446 -0
- package/dist/core/deployment/strategies/alchemy-strategy.js.map +1 -0
- package/dist/core/deployment/strategies/base-strategy.d.ts +50 -0
- package/dist/core/deployment/strategies/base-strategy.d.ts.map +1 -0
- package/dist/core/deployment/strategies/base-strategy.js +82 -0
- package/dist/core/deployment/strategies/base-strategy.js.map +1 -0
- package/dist/core/deployment/strategies/direct-strategy.d.ts +29 -0
- package/dist/core/deployment/strategies/direct-strategy.d.ts.map +1 -0
- package/dist/core/deployment/strategies/direct-strategy.js +80 -0
- package/dist/core/deployment/strategies/direct-strategy.js.map +1 -0
- package/dist/core/deployment/strategies/index.d.ts +12 -0
- package/dist/core/deployment/strategies/index.d.ts.map +1 -0
- package/dist/core/deployment/strategies/index.js +12 -0
- package/dist/core/deployment/strategies/index.js.map +1 -0
- package/dist/core/deployment/strategies/kro-strategy.d.ts +41 -0
- package/dist/core/deployment/strategies/kro-strategy.d.ts.map +1 -0
- package/dist/core/deployment/strategies/kro-strategy.js +270 -0
- package/dist/core/deployment/strategies/kro-strategy.js.map +1 -0
- package/dist/core/direct-deployment.d.ts +8 -0
- package/dist/core/direct-deployment.d.ts.map +1 -0
- package/dist/core/direct-deployment.js +9 -0
- package/dist/core/direct-deployment.js.map +1 -0
- package/dist/core/errors.d.ts +73 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +250 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/evaluation/cel-optimizer.d.ts +48 -0
- package/dist/core/evaluation/cel-optimizer.d.ts.map +1 -0
- package/dist/core/evaluation/cel-optimizer.js +176 -0
- package/dist/core/evaluation/cel-optimizer.js.map +1 -0
- package/dist/core/factory.d.ts +12 -0
- package/dist/core/factory.d.ts.map +1 -0
- package/dist/core/factory.js +13 -0
- package/dist/core/factory.js.map +1 -0
- package/dist/core/kubernetes/api.d.ts +43 -0
- package/dist/core/kubernetes/api.d.ts.map +1 -0
- package/dist/core/kubernetes/api.js +209 -0
- package/dist/core/kubernetes/api.js.map +1 -0
- package/dist/core/kubernetes/client-provider.d.ts +204 -0
- package/dist/core/kubernetes/client-provider.d.ts.map +1 -0
- package/dist/core/kubernetes/client-provider.js +421 -0
- package/dist/core/kubernetes/client-provider.js.map +1 -0
- package/dist/core/logging/config.d.ts +14 -0
- package/dist/core/logging/config.d.ts.map +1 -0
- package/dist/core/logging/config.js +57 -0
- package/dist/core/logging/config.js.map +1 -0
- package/dist/core/logging/index.d.ts +4 -0
- package/dist/core/logging/index.d.ts.map +1 -0
- package/dist/core/logging/index.js +4 -0
- package/dist/core/logging/index.js.map +1 -0
- package/dist/core/logging/logger.d.ts +26 -0
- package/dist/core/logging/logger.d.ts.map +1 -0
- package/dist/core/logging/logger.js +114 -0
- package/dist/core/logging/logger.js.map +1 -0
- package/dist/core/logging/types.d.ts +93 -0
- package/dist/core/logging/types.d.ts.map +1 -0
- package/dist/core/logging/types.js +2 -0
- package/dist/core/logging/types.js.map +1 -0
- package/dist/core/readiness/cluster-state.d.ts +229 -0
- package/dist/core/readiness/cluster-state.d.ts.map +1 -0
- package/dist/core/readiness/cluster-state.js +360 -0
- package/dist/core/readiness/cluster-state.js.map +1 -0
- package/dist/core/readiness/index.d.ts +5 -0
- package/dist/core/readiness/index.d.ts.map +1 -0
- package/dist/core/readiness/index.js +5 -0
- package/dist/core/readiness/index.js.map +1 -0
- package/dist/core/readiness/registry.d.ts +45 -0
- package/dist/core/readiness/registry.d.ts.map +1 -0
- package/dist/core/readiness/registry.js +62 -0
- package/dist/core/readiness/registry.js.map +1 -0
- package/dist/core/references/cel-evaluator.d.ts +102 -0
- package/dist/core/references/cel-evaluator.d.ts.map +1 -0
- package/dist/core/references/cel-evaluator.js +288 -0
- package/dist/core/references/cel-evaluator.js.map +1 -0
- package/dist/core/references/cel.d.ts +75 -0
- package/dist/core/references/cel.d.ts.map +1 -0
- package/dist/core/references/cel.js +213 -0
- package/dist/core/references/cel.js.map +1 -0
- package/dist/core/references/external-refs.d.ts +37 -0
- package/dist/core/references/external-refs.d.ts.map +1 -0
- package/dist/core/references/external-refs.js +53 -0
- package/dist/core/references/external-refs.js.map +1 -0
- package/dist/core/references/index.d.ts +15 -0
- package/dist/core/references/index.d.ts.map +1 -0
- package/dist/core/references/index.js +19 -0
- package/dist/core/references/index.js.map +1 -0
- package/dist/core/references/resolver.d.ts +117 -0
- package/dist/core/references/resolver.d.ts.map +1 -0
- package/dist/core/references/resolver.js +554 -0
- package/dist/core/references/resolver.js.map +1 -0
- package/dist/core/references/schema-proxy.d.ts +38 -0
- package/dist/core/references/schema-proxy.d.ts.map +1 -0
- package/dist/core/references/schema-proxy.js +163 -0
- package/dist/core/references/schema-proxy.js.map +1 -0
- package/dist/core/serialization/core.d.ts +15 -0
- package/dist/core/serialization/core.d.ts.map +1 -0
- package/dist/core/serialization/core.js +169 -0
- package/dist/core/serialization/core.js.map +1 -0
- package/dist/core/serialization/index.d.ts +9 -0
- package/dist/core/serialization/index.d.ts.map +1 -0
- package/dist/core/serialization/index.js +12 -0
- package/dist/core/serialization/index.js.map +1 -0
- package/dist/core/serialization/schema.d.ts +14 -0
- package/dist/core/serialization/schema.d.ts.map +1 -0
- package/dist/core/serialization/schema.js +33 -0
- package/dist/core/serialization/schema.js.map +1 -0
- package/dist/core/serialization/validation.d.ts +18 -0
- package/dist/core/serialization/validation.d.ts.map +1 -0
- package/dist/core/serialization/validation.js +165 -0
- package/dist/core/serialization/validation.js.map +1 -0
- package/dist/core/serialization/yaml.d.ts +10 -0
- package/dist/core/serialization/yaml.d.ts.map +1 -0
- package/dist/core/serialization/yaml.js +63 -0
- package/dist/core/serialization/yaml.js.map +1 -0
- package/dist/core/types/common.d.ts +41 -0
- package/dist/core/types/common.d.ts.map +1 -0
- package/dist/core/types/common.js +9 -0
- package/dist/core/types/common.js.map +1 -0
- package/dist/core/types/dependencies.d.ts +14 -0
- package/dist/core/types/dependencies.d.ts.map +1 -0
- package/dist/core/types/dependencies.js +5 -0
- package/dist/core/types/dependencies.js.map +1 -0
- package/dist/core/types/deployment.d.ts +265 -0
- package/dist/core/types/deployment.d.ts.map +1 -0
- package/dist/core/types/deployment.js +20 -0
- package/dist/core/types/deployment.js.map +1 -0
- package/dist/core/types/index.d.ts +19 -0
- package/dist/core/types/index.d.ts.map +1 -0
- package/dist/core/types/index.js +15 -0
- package/dist/core/types/index.js.map +1 -0
- package/dist/core/types/kubernetes.d.ts +352 -0
- package/dist/core/types/kubernetes.d.ts.map +1 -0
- package/dist/core/types/kubernetes.js +5 -0
- package/dist/core/types/kubernetes.js.map +1 -0
- package/dist/core/types/references.d.ts +54 -0
- package/dist/core/types/references.d.ts.map +1 -0
- package/dist/core/types/references.js +14 -0
- package/dist/core/types/references.js.map +1 -0
- package/dist/core/types/resource-graph.d.ts +93 -0
- package/dist/core/types/resource-graph.d.ts.map +1 -0
- package/dist/core/types/resource-graph.js +8 -0
- package/dist/core/types/resource-graph.js.map +1 -0
- package/dist/core/types/serialization.d.ts +210 -0
- package/dist/core/types/serialization.d.ts.map +1 -0
- package/dist/core/types/serialization.js +5 -0
- package/dist/core/types/serialization.js.map +1 -0
- package/dist/core/types/yaml.d.ts +92 -0
- package/dist/core/types/yaml.d.ts.map +1 -0
- package/dist/core/types/yaml.js +2 -0
- package/dist/core/types/yaml.js.map +1 -0
- package/dist/core/types.d.ts +8 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +9 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/validation/cel-validator.d.ts +47 -0
- package/dist/core/validation/cel-validator.d.ts.map +1 -0
- package/dist/core/validation/cel-validator.js +200 -0
- package/dist/core/validation/cel-validator.js.map +1 -0
- package/dist/core/yaml/index.d.ts +5 -0
- package/dist/core/yaml/index.d.ts.map +1 -0
- package/dist/core/yaml/index.js +5 -0
- package/dist/core/yaml/index.js.map +1 -0
- package/dist/core/yaml/path-resolver.d.ts +122 -0
- package/dist/core/yaml/path-resolver.d.ts.map +1 -0
- package/dist/core/yaml/path-resolver.js +556 -0
- package/dist/core/yaml/path-resolver.js.map +1 -0
- package/dist/core.d.ts +30 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +59 -0
- package/dist/core.js.map +1 -0
- package/dist/factories/flux/git-repository.d.ts +62 -0
- package/dist/factories/flux/git-repository.d.ts.map +1 -0
- package/dist/factories/flux/git-repository.js +34 -0
- package/dist/factories/flux/git-repository.js.map +1 -0
- package/dist/factories/flux/index.d.ts +10 -0
- package/dist/factories/flux/index.d.ts.map +1 -0
- package/dist/factories/flux/index.js +11 -0
- package/dist/factories/flux/index.js.map +1 -0
- package/dist/factories/flux/kustomize/index.d.ts +4 -0
- package/dist/factories/flux/kustomize/index.d.ts.map +1 -0
- package/dist/factories/flux/kustomize/index.js +3 -0
- package/dist/factories/flux/kustomize/index.js.map +1 -0
- package/dist/factories/flux/kustomize/kustomization.d.ts +178 -0
- package/dist/factories/flux/kustomize/kustomization.d.ts.map +1 -0
- package/dist/factories/flux/kustomize/kustomization.js +108 -0
- package/dist/factories/flux/kustomize/kustomization.js.map +1 -0
- package/dist/factories/flux/kustomize/readiness-evaluators.d.ts +9 -0
- package/dist/factories/flux/kustomize/readiness-evaluators.d.ts.map +1 -0
- package/dist/factories/flux/kustomize/readiness-evaluators.js +71 -0
- package/dist/factories/flux/kustomize/readiness-evaluators.js.map +1 -0
- package/dist/factories/flux/kustomize/types.d.ts +2 -0
- package/dist/factories/flux/kustomize/types.d.ts.map +1 -0
- package/dist/factories/flux/kustomize/types.js +2 -0
- package/dist/factories/flux/kustomize/types.js.map +1 -0
- package/dist/factories/helm/helm-release.d.ts +139 -0
- package/dist/factories/helm/helm-release.d.ts.map +1 -0
- package/dist/factories/helm/helm-release.js +180 -0
- package/dist/factories/helm/helm-release.js.map +1 -0
- package/dist/factories/helm/helm-repository.d.ts +24 -0
- package/dist/factories/helm/helm-repository.d.ts.map +1 -0
- package/dist/factories/helm/helm-repository.js +57 -0
- package/dist/factories/helm/helm-repository.js.map +1 -0
- package/dist/factories/helm/index.d.ts +8 -0
- package/dist/factories/helm/index.d.ts.map +1 -0
- package/dist/factories/helm/index.js +8 -0
- package/dist/factories/helm/index.js.map +1 -0
- package/dist/factories/helm/readiness-evaluators.d.ts +55 -0
- package/dist/factories/helm/readiness-evaluators.d.ts.map +1 -0
- package/dist/factories/helm/readiness-evaluators.js +295 -0
- package/dist/factories/helm/readiness-evaluators.js.map +1 -0
- package/dist/factories/helm/types.d.ts +21 -0
- package/dist/factories/helm/types.d.ts.map +1 -0
- package/dist/factories/helm/types.js +2 -0
- package/dist/factories/helm/types.js.map +1 -0
- package/dist/factories/index.d.ts +12 -0
- package/dist/factories/index.d.ts.map +1 -0
- package/dist/factories/index.js +31 -0
- package/dist/factories/index.js.map +1 -0
- package/dist/factories/kro/index.d.ts +11 -0
- package/dist/factories/kro/index.d.ts.map +1 -0
- package/dist/factories/kro/index.js +10 -0
- package/dist/factories/kro/index.js.map +1 -0
- package/dist/factories/kro/kro-crd.d.ts +14 -0
- package/dist/factories/kro/kro-crd.d.ts.map +1 -0
- package/dist/factories/kro/kro-crd.js +51 -0
- package/dist/factories/kro/kro-crd.js.map +1 -0
- package/dist/factories/kro/kro-custom-resource.d.ts +23 -0
- package/dist/factories/kro/kro-custom-resource.d.ts.map +1 -0
- package/dist/factories/kro/kro-custom-resource.js +153 -0
- package/dist/factories/kro/kro-custom-resource.js.map +1 -0
- package/dist/factories/kro/resource-graph-definition.d.ts +12 -0
- package/dist/factories/kro/resource-graph-definition.d.ts.map +1 -0
- package/dist/factories/kro/resource-graph-definition.js +94 -0
- package/dist/factories/kro/resource-graph-definition.js.map +1 -0
- package/dist/factories/kubernetes/admission/index.d.ts +9 -0
- package/dist/factories/kubernetes/admission/index.d.ts.map +1 -0
- package/dist/factories/kubernetes/admission/index.js +9 -0
- package/dist/factories/kubernetes/admission/index.js.map +1 -0
- package/dist/factories/kubernetes/admission/mutating-webhook-configuration.d.ts +5 -0
- package/dist/factories/kubernetes/admission/mutating-webhook-configuration.d.ts.map +1 -0
- package/dist/factories/kubernetes/admission/mutating-webhook-configuration.js +10 -0
- package/dist/factories/kubernetes/admission/mutating-webhook-configuration.js.map +1 -0
- package/dist/factories/kubernetes/admission/validating-webhook-configuration.d.ts +5 -0
- package/dist/factories/kubernetes/admission/validating-webhook-configuration.d.ts.map +1 -0
- package/dist/factories/kubernetes/admission/validating-webhook-configuration.js +10 -0
- package/dist/factories/kubernetes/admission/validating-webhook-configuration.js.map +1 -0
- package/dist/factories/kubernetes/autoscaling/horizontal-pod-autoscaler-v1.d.ts +6 -0
- package/dist/factories/kubernetes/autoscaling/horizontal-pod-autoscaler-v1.d.ts.map +1 -0
- package/dist/factories/kubernetes/autoscaling/horizontal-pod-autoscaler-v1.js +35 -0
- package/dist/factories/kubernetes/autoscaling/horizontal-pod-autoscaler-v1.js.map +1 -0
- package/dist/factories/kubernetes/autoscaling/horizontal-pod-autoscaler.d.ts +6 -0
- package/dist/factories/kubernetes/autoscaling/horizontal-pod-autoscaler.d.ts.map +1 -0
- package/dist/factories/kubernetes/autoscaling/horizontal-pod-autoscaler.js +31 -0
- package/dist/factories/kubernetes/autoscaling/horizontal-pod-autoscaler.js.map +1 -0
- package/dist/factories/kubernetes/autoscaling/index.d.ts +9 -0
- package/dist/factories/kubernetes/autoscaling/index.d.ts.map +1 -0
- package/dist/factories/kubernetes/autoscaling/index.js +9 -0
- package/dist/factories/kubernetes/autoscaling/index.js.map +1 -0
- package/dist/factories/kubernetes/certificates/certificate-signing-request.d.ts +6 -0
- package/dist/factories/kubernetes/certificates/certificate-signing-request.d.ts.map +1 -0
- package/dist/factories/kubernetes/certificates/certificate-signing-request.js +48 -0
- package/dist/factories/kubernetes/certificates/certificate-signing-request.js.map +1 -0
- package/dist/factories/kubernetes/certificates/index.d.ts +8 -0
- package/dist/factories/kubernetes/certificates/index.d.ts.map +1 -0
- package/dist/factories/kubernetes/certificates/index.js +8 -0
- package/dist/factories/kubernetes/certificates/index.js.map +1 -0
- package/dist/factories/kubernetes/config/config-map.d.ts +5 -0
- package/dist/factories/kubernetes/config/config-map.d.ts.map +1 -0
- package/dist/factories/kubernetes/config/config-map.js +16 -0
- package/dist/factories/kubernetes/config/config-map.js.map +1 -0
- package/dist/factories/kubernetes/config/index.d.ts +9 -0
- package/dist/factories/kubernetes/config/index.d.ts.map +1 -0
- package/dist/factories/kubernetes/config/index.js +9 -0
- package/dist/factories/kubernetes/config/index.js.map +1 -0
- package/dist/factories/kubernetes/config/secret.d.ts +5 -0
- package/dist/factories/kubernetes/config/secret.d.ts.map +1 -0
- package/dist/factories/kubernetes/config/secret.js +16 -0
- package/dist/factories/kubernetes/config/secret.js.map +1 -0
- package/dist/factories/kubernetes/coordination/index.d.ts +8 -0
- package/dist/factories/kubernetes/coordination/index.d.ts.map +1 -0
- package/dist/factories/kubernetes/coordination/index.js +8 -0
- package/dist/factories/kubernetes/coordination/index.js.map +1 -0
- package/dist/factories/kubernetes/coordination/lease.d.ts +5 -0
- package/dist/factories/kubernetes/coordination/lease.d.ts.map +1 -0
- package/dist/factories/kubernetes/coordination/lease.js +10 -0
- package/dist/factories/kubernetes/coordination/lease.js.map +1 -0
- package/dist/factories/kubernetes/core/component-status.d.ts +4 -0
- package/dist/factories/kubernetes/core/component-status.d.ts.map +1 -0
- package/dist/factories/kubernetes/core/component-status.js +25 -0
- package/dist/factories/kubernetes/core/component-status.js.map +1 -0
- package/dist/factories/kubernetes/core/index.d.ts +11 -0
- package/dist/factories/kubernetes/core/index.d.ts.map +1 -0
- package/dist/factories/kubernetes/core/index.js +11 -0
- package/dist/factories/kubernetes/core/index.js.map +1 -0
- package/dist/factories/kubernetes/core/namespace.d.ts +9 -0
- package/dist/factories/kubernetes/core/namespace.d.ts.map +1 -0
- package/dist/factories/kubernetes/core/namespace.js +48 -0
- package/dist/factories/kubernetes/core/namespace.js.map +1 -0
- package/dist/factories/kubernetes/core/node.d.ts +6 -0
- package/dist/factories/kubernetes/core/node.d.ts.map +1 -0
- package/dist/factories/kubernetes/core/node.js +34 -0
- package/dist/factories/kubernetes/core/node.js.map +1 -0
- package/dist/factories/kubernetes/core/pod.d.ts +6 -0
- package/dist/factories/kubernetes/core/pod.d.ts.map +1 -0
- package/dist/factories/kubernetes/core/pod.js +44 -0
- package/dist/factories/kubernetes/core/pod.js.map +1 -0
- package/dist/factories/kubernetes/extensions/custom-resource-definition.d.ts +6 -0
- package/dist/factories/kubernetes/extensions/custom-resource-definition.d.ts.map +1 -0
- package/dist/factories/kubernetes/extensions/custom-resource-definition.js +45 -0
- package/dist/factories/kubernetes/extensions/custom-resource-definition.js.map +1 -0
- package/dist/factories/kubernetes/extensions/custom-resource.d.ts +12 -0
- package/dist/factories/kubernetes/extensions/custom-resource.d.ts.map +1 -0
- package/dist/factories/kubernetes/extensions/custom-resource.js +17 -0
- package/dist/factories/kubernetes/extensions/custom-resource.js.map +1 -0
- package/dist/factories/kubernetes/extensions/index.d.ts +9 -0
- package/dist/factories/kubernetes/extensions/index.d.ts.map +1 -0
- package/dist/factories/kubernetes/extensions/index.js +9 -0
- package/dist/factories/kubernetes/extensions/index.js.map +1 -0
- package/dist/factories/kubernetes/index.d.ts +24 -0
- package/dist/factories/kubernetes/index.d.ts.map +1 -0
- package/dist/factories/kubernetes/index.js +75 -0
- package/dist/factories/kubernetes/index.js.map +1 -0
- package/dist/factories/kubernetes/networking/endpoint-slice.d.ts +4 -0
- package/dist/factories/kubernetes/networking/endpoint-slice.d.ts.map +1 -0
- package/dist/factories/kubernetes/networking/endpoint-slice.js +10 -0
- package/dist/factories/kubernetes/networking/endpoint-slice.js.map +1 -0
- package/dist/factories/kubernetes/networking/endpoints.d.ts +4 -0
- package/dist/factories/kubernetes/networking/endpoints.d.ts.map +1 -0
- package/dist/factories/kubernetes/networking/endpoints.js +39 -0
- package/dist/factories/kubernetes/networking/endpoints.js.map +1 -0
- package/dist/factories/kubernetes/networking/index.d.ts +13 -0
- package/dist/factories/kubernetes/networking/index.d.ts.map +1 -0
- package/dist/factories/kubernetes/networking/index.js +13 -0
- package/dist/factories/kubernetes/networking/index.js.map +1 -0
- package/dist/factories/kubernetes/networking/ingress-class.d.ts +5 -0
- package/dist/factories/kubernetes/networking/ingress-class.d.ts.map +1 -0
- package/dist/factories/kubernetes/networking/ingress-class.js +16 -0
- package/dist/factories/kubernetes/networking/ingress-class.js.map +1 -0
- package/dist/factories/kubernetes/networking/ingress.d.ts +5 -0
- package/dist/factories/kubernetes/networking/ingress.d.ts.map +1 -0
- package/dist/factories/kubernetes/networking/ingress.js +33 -0
- package/dist/factories/kubernetes/networking/ingress.js.map +1 -0
- package/dist/factories/kubernetes/networking/network-policy.d.ts +5 -0
- package/dist/factories/kubernetes/networking/network-policy.d.ts.map +1 -0
- package/dist/factories/kubernetes/networking/network-policy.js +17 -0
- package/dist/factories/kubernetes/networking/network-policy.js.map +1 -0
- package/dist/factories/kubernetes/networking/service.d.ts +5 -0
- package/dist/factories/kubernetes/networking/service.d.ts.map +1 -0
- package/dist/factories/kubernetes/networking/service.js +64 -0
- package/dist/factories/kubernetes/networking/service.js.map +1 -0
- package/dist/factories/kubernetes/policy/index.d.ts +10 -0
- package/dist/factories/kubernetes/policy/index.d.ts.map +1 -0
- package/dist/factories/kubernetes/policy/index.js +10 -0
- package/dist/factories/kubernetes/policy/index.js.map +1 -0
- package/dist/factories/kubernetes/policy/limit-range.d.ts +5 -0
- package/dist/factories/kubernetes/policy/limit-range.d.ts.map +1 -0
- package/dist/factories/kubernetes/policy/limit-range.js +17 -0
- package/dist/factories/kubernetes/policy/limit-range.js.map +1 -0
- package/dist/factories/kubernetes/policy/pod-disruption-budget.d.ts +6 -0
- package/dist/factories/kubernetes/policy/pod-disruption-budget.d.ts.map +1 -0
- package/dist/factories/kubernetes/policy/pod-disruption-budget.js +55 -0
- package/dist/factories/kubernetes/policy/pod-disruption-budget.js.map +1 -0
- package/dist/factories/kubernetes/policy/resource-quota.d.ts +6 -0
- package/dist/factories/kubernetes/policy/resource-quota.d.ts.map +1 -0
- package/dist/factories/kubernetes/policy/resource-quota.js +43 -0
- package/dist/factories/kubernetes/policy/resource-quota.js.map +1 -0
- package/dist/factories/kubernetes/rbac/cluster-role-binding.d.ts +4 -0
- package/dist/factories/kubernetes/rbac/cluster-role-binding.d.ts.map +1 -0
- package/dist/factories/kubernetes/rbac/cluster-role-binding.js +17 -0
- package/dist/factories/kubernetes/rbac/cluster-role-binding.js.map +1 -0
- package/dist/factories/kubernetes/rbac/cluster-role.d.ts +4 -0
- package/dist/factories/kubernetes/rbac/cluster-role.d.ts.map +1 -0
- package/dist/factories/kubernetes/rbac/cluster-role.js +17 -0
- package/dist/factories/kubernetes/rbac/cluster-role.js.map +1 -0
- package/dist/factories/kubernetes/rbac/index.d.ts +12 -0
- package/dist/factories/kubernetes/rbac/index.d.ts.map +1 -0
- package/dist/factories/kubernetes/rbac/index.js +12 -0
- package/dist/factories/kubernetes/rbac/index.js.map +1 -0
- package/dist/factories/kubernetes/rbac/role-binding.d.ts +4 -0
- package/dist/factories/kubernetes/rbac/role-binding.d.ts.map +1 -0
- package/dist/factories/kubernetes/rbac/role-binding.js +17 -0
- package/dist/factories/kubernetes/rbac/role-binding.js.map +1 -0
- package/dist/factories/kubernetes/rbac/role.d.ts +4 -0
- package/dist/factories/kubernetes/rbac/role.d.ts.map +1 -0
- package/dist/factories/kubernetes/rbac/role.js +17 -0
- package/dist/factories/kubernetes/rbac/role.js.map +1 -0
- package/dist/factories/kubernetes/rbac/service-account.d.ts +4 -0
- package/dist/factories/kubernetes/rbac/service-account.d.ts.map +1 -0
- package/dist/factories/kubernetes/rbac/service-account.js +17 -0
- package/dist/factories/kubernetes/rbac/service-account.js.map +1 -0
- package/dist/factories/kubernetes/scheduling/index.d.ts +9 -0
- package/dist/factories/kubernetes/scheduling/index.d.ts.map +1 -0
- package/dist/factories/kubernetes/scheduling/index.js +9 -0
- package/dist/factories/kubernetes/scheduling/index.js.map +1 -0
- package/dist/factories/kubernetes/scheduling/priority-class.d.ts +4 -0
- package/dist/factories/kubernetes/scheduling/priority-class.d.ts.map +1 -0
- package/dist/factories/kubernetes/scheduling/priority-class.js +16 -0
- package/dist/factories/kubernetes/scheduling/priority-class.js.map +1 -0
- package/dist/factories/kubernetes/scheduling/runtime-class.d.ts +5 -0
- package/dist/factories/kubernetes/scheduling/runtime-class.d.ts.map +1 -0
- package/dist/factories/kubernetes/scheduling/runtime-class.js +10 -0
- package/dist/factories/kubernetes/scheduling/runtime-class.js.map +1 -0
- package/dist/factories/kubernetes/storage/csi-driver.d.ts +5 -0
- package/dist/factories/kubernetes/storage/csi-driver.d.ts.map +1 -0
- package/dist/factories/kubernetes/storage/csi-driver.js +16 -0
- package/dist/factories/kubernetes/storage/csi-driver.js.map +1 -0
- package/dist/factories/kubernetes/storage/csi-node.d.ts +5 -0
- package/dist/factories/kubernetes/storage/csi-node.d.ts.map +1 -0
- package/dist/factories/kubernetes/storage/csi-node.js +10 -0
- package/dist/factories/kubernetes/storage/csi-node.js.map +1 -0
- package/dist/factories/kubernetes/storage/index.d.ts +13 -0
- package/dist/factories/kubernetes/storage/index.d.ts.map +1 -0
- package/dist/factories/kubernetes/storage/index.js +13 -0
- package/dist/factories/kubernetes/storage/index.js.map +1 -0
- package/dist/factories/kubernetes/storage/persistent-volume-claim.d.ts +6 -0
- package/dist/factories/kubernetes/storage/persistent-volume-claim.d.ts.map +1 -0
- package/dist/factories/kubernetes/storage/persistent-volume-claim.js +30 -0
- package/dist/factories/kubernetes/storage/persistent-volume-claim.js.map +1 -0
- package/dist/factories/kubernetes/storage/persistent-volume.d.ts +6 -0
- package/dist/factories/kubernetes/storage/persistent-volume.d.ts.map +1 -0
- package/dist/factories/kubernetes/storage/persistent-volume.js +47 -0
- package/dist/factories/kubernetes/storage/persistent-volume.js.map +1 -0
- package/dist/factories/kubernetes/storage/storage-class.d.ts +4 -0
- package/dist/factories/kubernetes/storage/storage-class.d.ts.map +1 -0
- package/dist/factories/kubernetes/storage/storage-class.js +17 -0
- package/dist/factories/kubernetes/storage/storage-class.js.map +1 -0
- package/dist/factories/kubernetes/storage/volume-attachment.d.ts +6 -0
- package/dist/factories/kubernetes/storage/volume-attachment.d.ts.map +1 -0
- package/dist/factories/kubernetes/storage/volume-attachment.js +10 -0
- package/dist/factories/kubernetes/storage/volume-attachment.js.map +1 -0
- package/dist/factories/kubernetes/types.d.ts +8 -0
- package/dist/factories/kubernetes/types.d.ts.map +1 -0
- package/dist/factories/kubernetes/types.js +8 -0
- package/dist/factories/kubernetes/types.js.map +1 -0
- package/dist/factories/kubernetes/workloads/cron-job.d.ts +6 -0
- package/dist/factories/kubernetes/workloads/cron-job.d.ts.map +1 -0
- package/dist/factories/kubernetes/workloads/cron-job.js +53 -0
- package/dist/factories/kubernetes/workloads/cron-job.js.map +1 -0
- package/dist/factories/kubernetes/workloads/daemon-set.d.ts +6 -0
- package/dist/factories/kubernetes/workloads/daemon-set.d.ts.map +1 -0
- package/dist/factories/kubernetes/workloads/daemon-set.js +32 -0
- package/dist/factories/kubernetes/workloads/daemon-set.js.map +1 -0
- package/dist/factories/kubernetes/workloads/deployment.d.ts +5 -0
- package/dist/factories/kubernetes/workloads/deployment.d.ts.map +1 -0
- package/dist/factories/kubernetes/workloads/deployment.js +59 -0
- package/dist/factories/kubernetes/workloads/deployment.js.map +1 -0
- package/dist/factories/kubernetes/workloads/index.d.ts +14 -0
- package/dist/factories/kubernetes/workloads/index.d.ts.map +1 -0
- package/dist/factories/kubernetes/workloads/index.js +14 -0
- package/dist/factories/kubernetes/workloads/index.js.map +1 -0
- package/dist/factories/kubernetes/workloads/job.d.ts +6 -0
- package/dist/factories/kubernetes/workloads/job.d.ts.map +1 -0
- package/dist/factories/kubernetes/workloads/job.js +105 -0
- package/dist/factories/kubernetes/workloads/job.js.map +1 -0
- package/dist/factories/kubernetes/workloads/replica-set.d.ts +6 -0
- package/dist/factories/kubernetes/workloads/replica-set.d.ts.map +1 -0
- package/dist/factories/kubernetes/workloads/replica-set.js +33 -0
- package/dist/factories/kubernetes/workloads/replica-set.js.map +1 -0
- package/dist/factories/kubernetes/workloads/replication-controller.d.ts +6 -0
- package/dist/factories/kubernetes/workloads/replication-controller.d.ts.map +1 -0
- package/dist/factories/kubernetes/workloads/replication-controller.js +56 -0
- package/dist/factories/kubernetes/workloads/replication-controller.js.map +1 -0
- package/dist/factories/kubernetes/workloads/stateful-set.d.ts +6 -0
- package/dist/factories/kubernetes/workloads/stateful-set.d.ts.map +1 -0
- package/dist/factories/kubernetes/workloads/stateful-set.js +81 -0
- package/dist/factories/kubernetes/workloads/stateful-set.js.map +1 -0
- package/dist/factories/kubernetes/yaml/index.d.ts +6 -0
- package/dist/factories/kubernetes/yaml/index.d.ts.map +1 -0
- package/dist/factories/kubernetes/yaml/index.js +6 -0
- package/dist/factories/kubernetes/yaml/index.js.map +1 -0
- package/dist/factories/kubernetes/yaml/yaml-directory.d.ts +54 -0
- package/dist/factories/kubernetes/yaml/yaml-directory.d.ts.map +1 -0
- package/dist/factories/kubernetes/yaml/yaml-directory.js +178 -0
- package/dist/factories/kubernetes/yaml/yaml-directory.js.map +1 -0
- package/dist/factories/kubernetes/yaml/yaml-file.d.ts +54 -0
- package/dist/factories/kubernetes/yaml/yaml-file.d.ts.map +1 -0
- package/dist/factories/kubernetes/yaml/yaml-file.js +181 -0
- package/dist/factories/kubernetes/yaml/yaml-file.js.map +1 -0
- package/dist/factories/shared.d.ts +11 -0
- package/dist/factories/shared.d.ts.map +1 -0
- package/dist/factories/shared.js +364 -0
- package/dist/factories/shared.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/helpers.d.ts +69 -0
- package/dist/utils/helpers.d.ts.map +1 -0
- package/dist/utils/helpers.js +405 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/index.d.ts +9 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +11 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/type-guards.d.ts +34 -0
- package/dist/utils/type-guards.d.ts.map +1 -0
- package/dist/utils/type-guards.js +66 -0
- package/dist/utils/type-guards.js.map +1 -0
- package/package.json +109 -0
|
@@ -0,0 +1,1408 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Direct Deployment Engine
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates the deployment of Kubernetes resources directly to a cluster
|
|
5
|
+
* without requiring the Kro controller, using in-process dependency resolution.
|
|
6
|
+
*/
|
|
7
|
+
import * as k8s from '@kubernetes/client-node';
|
|
8
|
+
import { DependencyResolver } from '../dependencies/index.js';
|
|
9
|
+
import { CircularDependencyError } from '../errors.js';
|
|
10
|
+
import { getComponentLogger } from '../logging/index.js';
|
|
11
|
+
import { ReferenceResolver, DeploymentMode } from '../references/index.js';
|
|
12
|
+
import { ResourceDeploymentError } from '../types/deployment.js';
|
|
13
|
+
import { ResourceReadinessChecker } from './readiness.js';
|
|
14
|
+
import { StatusHydrator } from './status-hydrator.js';
|
|
15
|
+
import { ensureReadinessEvaluator } from '../../utils/helpers.js';
|
|
16
|
+
export class DirectDeploymentEngine {
|
|
17
|
+
kubeClient;
|
|
18
|
+
deploymentMode;
|
|
19
|
+
dependencyResolver;
|
|
20
|
+
referenceResolver;
|
|
21
|
+
k8sApi;
|
|
22
|
+
readinessChecker;
|
|
23
|
+
statusHydrator;
|
|
24
|
+
deploymentState = new Map();
|
|
25
|
+
readyResources = new Set(); // Track resources that are already ready
|
|
26
|
+
logger = getComponentLogger('deployment-engine');
|
|
27
|
+
constructor(kubeClient, k8sApi, referenceResolver, deploymentMode = DeploymentMode.DIRECT) {
|
|
28
|
+
this.kubeClient = kubeClient;
|
|
29
|
+
this.deploymentMode = deploymentMode;
|
|
30
|
+
this.dependencyResolver = new DependencyResolver();
|
|
31
|
+
this.referenceResolver = referenceResolver || new ReferenceResolver(kubeClient, this.deploymentMode, k8sApi);
|
|
32
|
+
this.k8sApi = k8sApi || kubeClient.makeApiClient(k8s.KubernetesObjectApi);
|
|
33
|
+
this.readinessChecker = new ResourceReadinessChecker(this.k8sApi);
|
|
34
|
+
this.statusHydrator = new StatusHydrator(this.k8sApi);
|
|
35
|
+
// Set up callback to track ready resources
|
|
36
|
+
this.readinessChecker.setOnResourceReady((resource) => {
|
|
37
|
+
const resourceKey = `${resource.kind}/${resource.name}/${resource.namespace}`;
|
|
38
|
+
this.readyResources.add(resourceKey);
|
|
39
|
+
this.logger.debug('Resource marked as ready via generic readiness checker', {
|
|
40
|
+
resourceKey,
|
|
41
|
+
totalReady: this.readyResources.size
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Get the Kubernetes API client for external integrations
|
|
47
|
+
* @returns The configured KubernetesObjectApi instance
|
|
48
|
+
*/
|
|
49
|
+
getKubernetesApi() {
|
|
50
|
+
return this.k8sApi;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Check if a deployed resource is ready using the factory-provided readiness evaluator
|
|
54
|
+
*/
|
|
55
|
+
async isDeployedResourceReady(deployedResource) {
|
|
56
|
+
try {
|
|
57
|
+
// Check if the deployed resource has a factory-provided readiness evaluator
|
|
58
|
+
const readinessEvaluator = deployedResource.manifest.readinessEvaluator;
|
|
59
|
+
if (readinessEvaluator) {
|
|
60
|
+
// Use the factory-provided readiness evaluator
|
|
61
|
+
// Create a resource reference for the API call
|
|
62
|
+
const resourceRef = {
|
|
63
|
+
apiVersion: deployedResource.manifest.apiVersion || '',
|
|
64
|
+
kind: deployedResource.kind,
|
|
65
|
+
metadata: {
|
|
66
|
+
name: deployedResource.name,
|
|
67
|
+
namespace: deployedResource.namespace,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
// Get the live resource from the cluster
|
|
71
|
+
const liveResource = await this.k8sApi.read(resourceRef);
|
|
72
|
+
// Use the factory-provided readiness evaluator
|
|
73
|
+
const result = readinessEvaluator(liveResource.body);
|
|
74
|
+
if (typeof result === 'boolean') {
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
else if (result && typeof result === 'object' && 'ready' in result) {
|
|
78
|
+
return result.ready;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
this.logger.warn('Readiness evaluator returned unexpected result', {
|
|
82
|
+
resourceId: deployedResource.id,
|
|
83
|
+
result
|
|
84
|
+
});
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
// Fallback to generic readiness checker
|
|
90
|
+
const resourceRef = {
|
|
91
|
+
apiVersion: deployedResource.manifest.apiVersion || '',
|
|
92
|
+
kind: deployedResource.kind,
|
|
93
|
+
metadata: {
|
|
94
|
+
name: deployedResource.name,
|
|
95
|
+
namespace: deployedResource.namespace,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
const liveResource = await this.k8sApi.read(resourceRef);
|
|
99
|
+
return this.readinessChecker.isResourceReady(liveResource.body);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
this.logger.debug('Failed to check resource readiness', {
|
|
104
|
+
error: error,
|
|
105
|
+
resourceId: deployedResource.id,
|
|
106
|
+
kind: deployedResource.kind,
|
|
107
|
+
name: deployedResource.name,
|
|
108
|
+
namespace: deployedResource.namespace
|
|
109
|
+
});
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Get all deployment states for health checking
|
|
115
|
+
*/
|
|
116
|
+
getAllDeploymentStates() {
|
|
117
|
+
return Array.from(this.deploymentState.values());
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Deploy a resource graph to the Kubernetes cluster
|
|
121
|
+
*/
|
|
122
|
+
async deploy(graph, options) {
|
|
123
|
+
const deploymentId = this.generateDeploymentId();
|
|
124
|
+
const startTime = Date.now();
|
|
125
|
+
const deployedResources = [];
|
|
126
|
+
const errors = [];
|
|
127
|
+
const deploymentLogger = this.logger.child({ deploymentId, resourceCount: graph.resources.length });
|
|
128
|
+
deploymentLogger.info('Starting deployment', { options });
|
|
129
|
+
try {
|
|
130
|
+
this.emitEvent(options, {
|
|
131
|
+
type: 'started',
|
|
132
|
+
message: `Starting deployment of ${graph.resources.length} resources`,
|
|
133
|
+
timestamp: new Date(),
|
|
134
|
+
});
|
|
135
|
+
// 1. Validate no cycles in dependency graph
|
|
136
|
+
deploymentLogger.debug('Validating dependency graph', { dependencyGraph: graph.dependencyGraph });
|
|
137
|
+
this.dependencyResolver.validateNoCycles(graph.dependencyGraph);
|
|
138
|
+
// 2. Analyze deployment order and identify parallel stages
|
|
139
|
+
deploymentLogger.debug('Analyzing deployment order for parallel execution');
|
|
140
|
+
const deploymentPlan = this.dependencyResolver.analyzeDeploymentOrder(graph.dependencyGraph);
|
|
141
|
+
deploymentLogger.debug('Deployment plan determined', {
|
|
142
|
+
levels: deploymentPlan.levels.length,
|
|
143
|
+
totalResources: deploymentPlan.totalResources,
|
|
144
|
+
maxParallelism: deploymentPlan.maxParallelism
|
|
145
|
+
});
|
|
146
|
+
// 3. Create resolution context
|
|
147
|
+
const context = {
|
|
148
|
+
deployedResources,
|
|
149
|
+
kubeClient: this.kubeClient,
|
|
150
|
+
...(options.namespace && { namespace: options.namespace }),
|
|
151
|
+
timeout: options.timeout || 30000,
|
|
152
|
+
};
|
|
153
|
+
// 4. Deploy resources in parallel stages
|
|
154
|
+
for (let levelIndex = 0; levelIndex < deploymentPlan.levels.length; levelIndex++) {
|
|
155
|
+
const currentLevel = deploymentPlan.levels[levelIndex];
|
|
156
|
+
if (!currentLevel) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const levelLogger = deploymentLogger.child({
|
|
160
|
+
level: levelIndex + 1,
|
|
161
|
+
resourceCount: currentLevel.length
|
|
162
|
+
});
|
|
163
|
+
levelLogger.debug(`Deploying level ${levelIndex + 1} with ${currentLevel.length} resources in parallel`);
|
|
164
|
+
// Track performance metrics for this level
|
|
165
|
+
const levelStartTime = Date.now();
|
|
166
|
+
// Deploy all resources in this level in parallel
|
|
167
|
+
const levelPromises = currentLevel.map(async (resourceId) => {
|
|
168
|
+
const resourceLogger = deploymentLogger.child({ resourceId });
|
|
169
|
+
resourceLogger.debug('Starting resource deployment');
|
|
170
|
+
const resource = graph.resources.find((r) => r.id === resourceId);
|
|
171
|
+
if (!resource) {
|
|
172
|
+
resourceLogger.error('Resource not found in graph');
|
|
173
|
+
const error = new Error(`Resource with id '${resourceId}' not found in graph`);
|
|
174
|
+
return {
|
|
175
|
+
success: false,
|
|
176
|
+
resourceId,
|
|
177
|
+
error: {
|
|
178
|
+
resourceId,
|
|
179
|
+
phase: 'validation',
|
|
180
|
+
error,
|
|
181
|
+
timestamp: new Date(),
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
resourceLogger.debug('Found resource in graph', {
|
|
186
|
+
resourceId: resource.id,
|
|
187
|
+
kind: resource.manifest?.kind,
|
|
188
|
+
name: resource.manifest?.metadata?.name
|
|
189
|
+
});
|
|
190
|
+
try {
|
|
191
|
+
resourceLogger.debug('Calling deploySingleResource');
|
|
192
|
+
// Wait for CRD establishment if this is a custom resource
|
|
193
|
+
await this.waitForCRDIfCustomResource(resource.manifest, options, resourceLogger);
|
|
194
|
+
// FIX: Unconditionally ensure the readiness evaluator is attached just before deployment.
|
|
195
|
+
const resourceWithEvaluator = ensureReadinessEvaluator(resource.manifest);
|
|
196
|
+
const deployedResource = await this.deploySingleResource(resourceWithEvaluator, context, options);
|
|
197
|
+
resourceLogger.debug('Resource deployed successfully');
|
|
198
|
+
return {
|
|
199
|
+
success: true,
|
|
200
|
+
resourceId,
|
|
201
|
+
deployedResource
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
resourceLogger.error('Resource deployment failed', error);
|
|
206
|
+
const failedResource = {
|
|
207
|
+
id: resourceId,
|
|
208
|
+
kind: resource.manifest.kind,
|
|
209
|
+
name: resource.manifest.metadata?.name || 'unknown',
|
|
210
|
+
namespace: resource.manifest.metadata?.namespace || 'default',
|
|
211
|
+
manifest: resource.manifest,
|
|
212
|
+
status: 'failed',
|
|
213
|
+
deployedAt: new Date(),
|
|
214
|
+
error: error,
|
|
215
|
+
};
|
|
216
|
+
return {
|
|
217
|
+
success: false,
|
|
218
|
+
resourceId,
|
|
219
|
+
deployedResource: failedResource,
|
|
220
|
+
error: {
|
|
221
|
+
resourceId,
|
|
222
|
+
phase: 'deployment',
|
|
223
|
+
error: error,
|
|
224
|
+
timestamp: new Date(),
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
// Wait for all resources in this level to complete
|
|
230
|
+
const levelResults = await Promise.allSettled(levelPromises);
|
|
231
|
+
// Process results and handle errors
|
|
232
|
+
let levelHasFailures = false;
|
|
233
|
+
for (const result of levelResults) {
|
|
234
|
+
if (result.status === 'fulfilled') {
|
|
235
|
+
const deploymentResult = result.value;
|
|
236
|
+
if (deploymentResult.success && deploymentResult.deployedResource) {
|
|
237
|
+
deployedResources.push(deploymentResult.deployedResource);
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
levelHasFailures = true;
|
|
241
|
+
if (deploymentResult.error) {
|
|
242
|
+
errors.push(deploymentResult.error);
|
|
243
|
+
}
|
|
244
|
+
if (deploymentResult.deployedResource) {
|
|
245
|
+
deployedResources.push(deploymentResult.deployedResource);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
// Promise was rejected
|
|
251
|
+
levelHasFailures = true;
|
|
252
|
+
levelLogger.error('Unexpected promise rejection in parallel deployment', result.reason);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// Handle rollback if there are failures and rollback is enabled
|
|
256
|
+
if (levelHasFailures && options.rollbackOnFailure) {
|
|
257
|
+
levelLogger.warn('Level deployment failed, initiating rollback');
|
|
258
|
+
await this.rollbackDeployedResources(deployedResources, options);
|
|
259
|
+
const duration = Date.now() - startTime;
|
|
260
|
+
this.emitEvent(options, {
|
|
261
|
+
type: 'rollback',
|
|
262
|
+
message: `Deployment failed and rolled back in ${duration}ms`,
|
|
263
|
+
timestamp: new Date(),
|
|
264
|
+
});
|
|
265
|
+
return {
|
|
266
|
+
deploymentId,
|
|
267
|
+
resources: deployedResources,
|
|
268
|
+
dependencyGraph: graph.dependencyGraph,
|
|
269
|
+
duration,
|
|
270
|
+
status: 'failed',
|
|
271
|
+
errors,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
// Calculate level performance metrics
|
|
275
|
+
const levelDuration = Date.now() - levelStartTime;
|
|
276
|
+
const successfulCount = levelResults.filter(r => r.status === 'fulfilled' && r.value.success).length;
|
|
277
|
+
const failedCount = levelResults.filter(r => r.status === 'rejected' || (r.status === 'fulfilled' && !r.value.success)).length;
|
|
278
|
+
const isTestEnvironment = process.env.NODE_ENV === 'test' || process.env.VITEST === 'true';
|
|
279
|
+
// Use debug level in test environments to reduce noise, info level in production
|
|
280
|
+
const logLevel = isTestEnvironment ? 'debug' : 'info';
|
|
281
|
+
levelLogger[logLevel](`Level ${levelIndex + 1} deployment completed`, {
|
|
282
|
+
successful: successfulCount,
|
|
283
|
+
failed: failedCount,
|
|
284
|
+
duration: levelDuration,
|
|
285
|
+
parallelism: currentLevel.length,
|
|
286
|
+
averageTimePerResource: Math.round(levelDuration / currentLevel.length)
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
const duration = Date.now() - startTime;
|
|
290
|
+
const successfulResources = deployedResources.filter((r) => r.status !== 'failed');
|
|
291
|
+
const status = errors.length === 0 ? 'success' : successfulResources.length > 0 ? 'partial' : 'failed';
|
|
292
|
+
// Log comprehensive performance metrics
|
|
293
|
+
const isTestEnvironment = process.env.NODE_ENV === 'test' || process.env.VITEST === 'true';
|
|
294
|
+
// Use debug level in test environments to reduce noise, info level in production
|
|
295
|
+
const logLevel = isTestEnvironment ? 'debug' : 'info';
|
|
296
|
+
deploymentLogger[logLevel]('Parallel deployment performance metrics', {
|
|
297
|
+
totalDuration: duration,
|
|
298
|
+
totalResources: deploymentPlan.totalResources,
|
|
299
|
+
parallelLevels: deploymentPlan.levels.length,
|
|
300
|
+
maxParallelism: deploymentPlan.maxParallelism,
|
|
301
|
+
averageTimePerResource: Math.round(duration / deploymentPlan.totalResources),
|
|
302
|
+
successfulResources: successfulResources.length,
|
|
303
|
+
failedResources: errors.length,
|
|
304
|
+
parallelismEfficiency: Math.round((deploymentPlan.totalResources / deploymentPlan.levels.length) / deploymentPlan.maxParallelism * 100),
|
|
305
|
+
status
|
|
306
|
+
});
|
|
307
|
+
this.emitEvent(options, {
|
|
308
|
+
type: status === 'success' ? 'completed' : 'failed',
|
|
309
|
+
message: `Deployment ${status} in ${duration}ms (${deploymentPlan.levels.length} parallel levels, max ${deploymentPlan.maxParallelism} concurrent)`,
|
|
310
|
+
timestamp: new Date(),
|
|
311
|
+
});
|
|
312
|
+
// Store deployment state for rollback
|
|
313
|
+
this.deploymentState.set(deploymentId, {
|
|
314
|
+
deploymentId,
|
|
315
|
+
resources: deployedResources,
|
|
316
|
+
dependencyGraph: graph.dependencyGraph,
|
|
317
|
+
startTime: new Date(startTime),
|
|
318
|
+
endTime: new Date(),
|
|
319
|
+
status: status === 'success' ? 'completed' : status === 'partial' ? 'completed' : 'failed',
|
|
320
|
+
options,
|
|
321
|
+
});
|
|
322
|
+
return {
|
|
323
|
+
deploymentId,
|
|
324
|
+
resources: deployedResources,
|
|
325
|
+
dependencyGraph: graph.dependencyGraph,
|
|
326
|
+
duration,
|
|
327
|
+
status,
|
|
328
|
+
errors,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
catch (error) {
|
|
332
|
+
// Re-throw circular dependency errors immediately - these are configuration errors
|
|
333
|
+
if (error instanceof CircularDependencyError) {
|
|
334
|
+
throw error;
|
|
335
|
+
}
|
|
336
|
+
const duration = Date.now() - startTime;
|
|
337
|
+
this.emitEvent(options, {
|
|
338
|
+
type: 'failed',
|
|
339
|
+
message: `Deployment failed: ${error}`,
|
|
340
|
+
timestamp: new Date(),
|
|
341
|
+
error: error,
|
|
342
|
+
});
|
|
343
|
+
// Store deployment state even for failed deployments (for rollback)
|
|
344
|
+
this.deploymentState.set(deploymentId, {
|
|
345
|
+
deploymentId,
|
|
346
|
+
resources: deployedResources,
|
|
347
|
+
dependencyGraph: graph.dependencyGraph,
|
|
348
|
+
startTime: new Date(startTime),
|
|
349
|
+
endTime: new Date(),
|
|
350
|
+
status: 'failed',
|
|
351
|
+
options,
|
|
352
|
+
});
|
|
353
|
+
return {
|
|
354
|
+
deploymentId,
|
|
355
|
+
resources: deployedResources,
|
|
356
|
+
dependencyGraph: graph.dependencyGraph,
|
|
357
|
+
duration,
|
|
358
|
+
status: 'failed',
|
|
359
|
+
errors: [
|
|
360
|
+
{
|
|
361
|
+
resourceId: 'deployment',
|
|
362
|
+
phase: 'deployment',
|
|
363
|
+
error: error,
|
|
364
|
+
timestamp: new Date(),
|
|
365
|
+
},
|
|
366
|
+
],
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Analyze closure dependencies to determine execution levels
|
|
372
|
+
*/
|
|
373
|
+
analyzeClosureDependencies(closures, spec, dependencyGraph) {
|
|
374
|
+
const closureDependencies = [];
|
|
375
|
+
for (const [name, closure] of Object.entries(closures)) {
|
|
376
|
+
// For now, analyze dependencies by examining the closure's configuration
|
|
377
|
+
// This is a simplified implementation - in practice, we would need to analyze
|
|
378
|
+
// the closure's arguments to detect resource references
|
|
379
|
+
const dependencies = this.extractClosureDependencies(closure, spec);
|
|
380
|
+
// Determine execution level based on dependencies
|
|
381
|
+
// For now, assign all closures to level -1 to ensure they run before all resources
|
|
382
|
+
// This is especially important for closures that install CRDs (like fluxSystem)
|
|
383
|
+
let level = -1;
|
|
384
|
+
if (dependencies.length > 0) {
|
|
385
|
+
// Find the maximum level of any dependency + 1
|
|
386
|
+
for (const depId of dependencies) {
|
|
387
|
+
const depLevel = this.getResourceLevel(depId, dependencyGraph);
|
|
388
|
+
level = Math.max(level, depLevel + 1);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
closureDependencies.push({
|
|
392
|
+
name,
|
|
393
|
+
closure,
|
|
394
|
+
dependencies,
|
|
395
|
+
level,
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
return closureDependencies;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Extract dependencies from a closure by analyzing its configuration
|
|
402
|
+
* This is a simplified implementation - in practice, we would need more sophisticated analysis
|
|
403
|
+
*/
|
|
404
|
+
extractClosureDependencies(closure, spec) {
|
|
405
|
+
// For now, return empty dependencies since closures typically don't depend on Enhanced<> resources
|
|
406
|
+
// In the future, this could analyze closure arguments for resource references
|
|
407
|
+
return [];
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Get the execution level of a resource in the dependency graph
|
|
411
|
+
*/
|
|
412
|
+
getResourceLevel(resourceId, dependencyGraph) {
|
|
413
|
+
// Find the level where this resource appears in the deployment plan
|
|
414
|
+
const deploymentPlan = this.dependencyResolver.analyzeDeploymentOrder(dependencyGraph);
|
|
415
|
+
for (let levelIndex = 0; levelIndex < deploymentPlan.levels.length; levelIndex++) {
|
|
416
|
+
const level = deploymentPlan.levels[levelIndex];
|
|
417
|
+
if (level && level.includes(resourceId)) {
|
|
418
|
+
return levelIndex;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return 0; // Default to level 0 if not found
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Integrate closures into the deployment plan based on their dependencies
|
|
425
|
+
*/
|
|
426
|
+
integrateClosuresIntoPlan(deploymentPlan, closureDependencies) {
|
|
427
|
+
// Create enhanced levels with both resources and closures
|
|
428
|
+
const enhancedLevels = [];
|
|
429
|
+
// Check if we have any closures at level -1 (pre-resource level)
|
|
430
|
+
const preResourceClosures = closureDependencies.filter(c => c.level === -1);
|
|
431
|
+
// If we have pre-resource closures, add them as level 0 and shift everything else
|
|
432
|
+
if (preResourceClosures.length > 0) {
|
|
433
|
+
enhancedLevels.push({
|
|
434
|
+
resources: [],
|
|
435
|
+
closures: preResourceClosures,
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
// Initialize levels with existing resources (shifted if we added a pre-resource level)
|
|
439
|
+
for (let i = 0; i < deploymentPlan.levels.length; i++) {
|
|
440
|
+
enhancedLevels.push({
|
|
441
|
+
resources: deploymentPlan.levels[i] || [],
|
|
442
|
+
closures: [],
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
// Add closures to their appropriate levels (excluding level -1 which we already handled)
|
|
446
|
+
for (const closureInfo of closureDependencies) {
|
|
447
|
+
if (closureInfo.level === -1) {
|
|
448
|
+
continue; // Already handled above
|
|
449
|
+
}
|
|
450
|
+
// Adjust level index if we added a pre-resource level
|
|
451
|
+
const adjustedLevel = preResourceClosures.length > 0 ? closureInfo.level + 1 : closureInfo.level;
|
|
452
|
+
// Ensure we have enough levels
|
|
453
|
+
while (enhancedLevels.length <= adjustedLevel) {
|
|
454
|
+
enhancedLevels.push({ resources: [], closures: [] });
|
|
455
|
+
}
|
|
456
|
+
const targetLevel = enhancedLevels[adjustedLevel];
|
|
457
|
+
if (targetLevel) {
|
|
458
|
+
targetLevel.closures.push(closureInfo);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return {
|
|
462
|
+
levels: enhancedLevels,
|
|
463
|
+
totalResources: deploymentPlan.totalResources,
|
|
464
|
+
totalClosures: closureDependencies.length,
|
|
465
|
+
maxParallelism: Math.max(deploymentPlan.maxParallelism, Math.max(...enhancedLevels.map(level => level.closures.length))),
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Deploy a resource graph with deployment closures integrated into level-based execution
|
|
470
|
+
*/
|
|
471
|
+
async deployWithClosures(graph, closures, options, spec, alchemyScope) {
|
|
472
|
+
const deploymentId = this.generateDeploymentId();
|
|
473
|
+
const startTime = Date.now();
|
|
474
|
+
const deployedResources = [];
|
|
475
|
+
const errors = [];
|
|
476
|
+
const deploymentLogger = this.logger.child({
|
|
477
|
+
deploymentId,
|
|
478
|
+
resourceCount: graph.resources.length,
|
|
479
|
+
closureCount: Object.keys(closures).length
|
|
480
|
+
});
|
|
481
|
+
deploymentLogger.info('Starting deployment with closures', {
|
|
482
|
+
options,
|
|
483
|
+
closures: Object.keys(closures)
|
|
484
|
+
});
|
|
485
|
+
try {
|
|
486
|
+
this.emitEvent(options, {
|
|
487
|
+
type: 'started',
|
|
488
|
+
message: `Starting deployment of ${graph.resources.length} resources and ${Object.keys(closures).length} closures`,
|
|
489
|
+
timestamp: new Date(),
|
|
490
|
+
});
|
|
491
|
+
// 1. Validate no cycles in dependency graph
|
|
492
|
+
deploymentLogger.debug('Validating dependency graph', { dependencyGraph: graph.dependencyGraph });
|
|
493
|
+
this.dependencyResolver.validateNoCycles(graph.dependencyGraph);
|
|
494
|
+
// 2. Analyze deployment order and identify parallel stages
|
|
495
|
+
deploymentLogger.debug('Analyzing deployment order for parallel execution');
|
|
496
|
+
const deploymentPlan = this.dependencyResolver.analyzeDeploymentOrder(graph.dependencyGraph);
|
|
497
|
+
deploymentLogger.debug('Deployment plan determined', {
|
|
498
|
+
levels: deploymentPlan.levels.length,
|
|
499
|
+
totalResources: deploymentPlan.totalResources,
|
|
500
|
+
maxParallelism: deploymentPlan.maxParallelism
|
|
501
|
+
});
|
|
502
|
+
// 3. Analyze closure dependencies and integrate into deployment plan
|
|
503
|
+
const closureDependencies = this.analyzeClosureDependencies(closures, spec, graph.dependencyGraph);
|
|
504
|
+
const enhancedPlan = this.integrateClosuresIntoPlan(deploymentPlan, closureDependencies);
|
|
505
|
+
deploymentLogger.debug('Enhanced deployment plan with closures', {
|
|
506
|
+
levels: enhancedPlan.levels.length,
|
|
507
|
+
totalResources: enhancedPlan.totalResources,
|
|
508
|
+
totalClosures: enhancedPlan.totalClosures,
|
|
509
|
+
maxParallelism: enhancedPlan.maxParallelism
|
|
510
|
+
});
|
|
511
|
+
// 4. Create resolution context
|
|
512
|
+
const context = {
|
|
513
|
+
deployedResources,
|
|
514
|
+
kubeClient: this.kubeClient,
|
|
515
|
+
...(options.namespace && { namespace: options.namespace }),
|
|
516
|
+
timeout: options.timeout || 30000,
|
|
517
|
+
};
|
|
518
|
+
// 5. Deploy resources and closures level by level with proper dependency handling
|
|
519
|
+
for (let levelIndex = 0; levelIndex < enhancedPlan.levels.length; levelIndex++) {
|
|
520
|
+
const currentLevel = enhancedPlan.levels[levelIndex];
|
|
521
|
+
if (!currentLevel) {
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
const levelLogger = deploymentLogger.child({
|
|
525
|
+
level: levelIndex + 1,
|
|
526
|
+
resourceCount: currentLevel.resources.length,
|
|
527
|
+
closureCount: currentLevel.closures.length
|
|
528
|
+
});
|
|
529
|
+
levelLogger.debug(`Deploying level ${levelIndex + 1} with ${currentLevel.resources.length} resources and ${currentLevel.closures.length} closures in parallel`);
|
|
530
|
+
const levelStartTime = Date.now();
|
|
531
|
+
// Create deployment context for closures at this level
|
|
532
|
+
const deployedResourcesMap = new Map();
|
|
533
|
+
// Populate with resources from previous levels
|
|
534
|
+
for (const resource of deployedResources) {
|
|
535
|
+
deployedResourcesMap.set(resource.id, resource);
|
|
536
|
+
}
|
|
537
|
+
const deploymentContext = {
|
|
538
|
+
kubernetesApi: this.k8sApi,
|
|
539
|
+
...(alchemyScope && { alchemyScope }),
|
|
540
|
+
...(options.namespace && { namespace: options.namespace }),
|
|
541
|
+
deployedResources: deployedResourcesMap,
|
|
542
|
+
resolveReference: async (ref) => {
|
|
543
|
+
// Enhanced reference resolution - will be improved in future tasks
|
|
544
|
+
return ref;
|
|
545
|
+
},
|
|
546
|
+
};
|
|
547
|
+
// Prepare promises for both resources and closures
|
|
548
|
+
const levelPromises = [];
|
|
549
|
+
// Add resource deployment promises
|
|
550
|
+
const resourcePromises = currentLevel.resources.map(async (resourceId) => {
|
|
551
|
+
const resourceLogger = deploymentLogger.child({ resourceId });
|
|
552
|
+
resourceLogger.debug('Starting resource deployment');
|
|
553
|
+
const resource = graph.resources.find((r) => r.id === resourceId);
|
|
554
|
+
if (!resource) {
|
|
555
|
+
resourceLogger.error('Resource not found in graph');
|
|
556
|
+
const error = new Error(`Resource with id '${resourceId}' not found in graph`);
|
|
557
|
+
return {
|
|
558
|
+
success: false,
|
|
559
|
+
resourceId,
|
|
560
|
+
error: {
|
|
561
|
+
resourceId,
|
|
562
|
+
phase: 'validation',
|
|
563
|
+
error,
|
|
564
|
+
timestamp: new Date(),
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
resourceLogger.debug('Found resource in graph', {
|
|
569
|
+
resourceId: resource.id,
|
|
570
|
+
kind: resource.manifest?.kind,
|
|
571
|
+
name: resource.manifest?.metadata?.name
|
|
572
|
+
});
|
|
573
|
+
try {
|
|
574
|
+
resourceLogger.debug('Calling deploySingleResource');
|
|
575
|
+
const resourceWithEvaluator = ensureReadinessEvaluator(resource.manifest);
|
|
576
|
+
const deployedResource = await this.deploySingleResource(resourceWithEvaluator, context, options);
|
|
577
|
+
resourceLogger.debug('Resource deployed successfully');
|
|
578
|
+
return {
|
|
579
|
+
success: true,
|
|
580
|
+
resourceId,
|
|
581
|
+
deployedResource
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
catch (error) {
|
|
585
|
+
resourceLogger.error('Resource deployment failed', error);
|
|
586
|
+
const failedResource = {
|
|
587
|
+
id: resourceId,
|
|
588
|
+
kind: resource.manifest.kind,
|
|
589
|
+
name: resource.manifest.metadata?.name || 'unknown',
|
|
590
|
+
namespace: resource.manifest.metadata?.namespace || 'default',
|
|
591
|
+
manifest: resource.manifest,
|
|
592
|
+
status: 'failed',
|
|
593
|
+
deployedAt: new Date(),
|
|
594
|
+
error: error,
|
|
595
|
+
};
|
|
596
|
+
return {
|
|
597
|
+
success: false,
|
|
598
|
+
resourceId,
|
|
599
|
+
deployedResource: failedResource,
|
|
600
|
+
error: {
|
|
601
|
+
resourceId,
|
|
602
|
+
phase: 'deployment',
|
|
603
|
+
error: error,
|
|
604
|
+
timestamp: new Date(),
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
// Add closure execution promises
|
|
610
|
+
const closurePromises = currentLevel.closures.map(async (closureInfo) => {
|
|
611
|
+
const closureLogger = levelLogger.child({ closureName: closureInfo.name });
|
|
612
|
+
closureLogger.debug('Executing closure at level', { level: levelIndex + 1 });
|
|
613
|
+
try {
|
|
614
|
+
const result = await closureInfo.closure(deploymentContext);
|
|
615
|
+
closureLogger.debug('Closure executed successfully', { resultCount: result?.length || 0 });
|
|
616
|
+
return {
|
|
617
|
+
success: true,
|
|
618
|
+
type: 'closure',
|
|
619
|
+
name: closureInfo.name,
|
|
620
|
+
result
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
catch (error) {
|
|
624
|
+
closureLogger.error('Closure execution failed', error);
|
|
625
|
+
return {
|
|
626
|
+
success: false,
|
|
627
|
+
type: 'closure',
|
|
628
|
+
name: closureInfo.name,
|
|
629
|
+
error: {
|
|
630
|
+
resourceId: `closure-${closureInfo.name}`,
|
|
631
|
+
phase: 'deployment',
|
|
632
|
+
error: error,
|
|
633
|
+
timestamp: new Date(),
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
// Combine all promises for this level
|
|
639
|
+
levelPromises.push(...resourcePromises, ...closurePromises);
|
|
640
|
+
// Wait for all resources and closures in this level to complete
|
|
641
|
+
const levelResults = await Promise.allSettled(levelPromises);
|
|
642
|
+
// Process results and handle errors
|
|
643
|
+
let levelHasFailures = false;
|
|
644
|
+
let successfulResources = 0;
|
|
645
|
+
let successfulClosures = 0;
|
|
646
|
+
let failedResources = 0;
|
|
647
|
+
let failedClosures = 0;
|
|
648
|
+
for (const result of levelResults) {
|
|
649
|
+
if (result.status === 'fulfilled') {
|
|
650
|
+
const deploymentResult = result.value;
|
|
651
|
+
if (deploymentResult.type === 'closure') {
|
|
652
|
+
// Handle closure result
|
|
653
|
+
if (deploymentResult.success) {
|
|
654
|
+
successfulClosures++;
|
|
655
|
+
}
|
|
656
|
+
else {
|
|
657
|
+
levelHasFailures = true;
|
|
658
|
+
failedClosures++;
|
|
659
|
+
if (deploymentResult.error) {
|
|
660
|
+
errors.push(deploymentResult.error);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
// Handle resource result
|
|
666
|
+
if (deploymentResult.success && deploymentResult.deployedResource) {
|
|
667
|
+
deployedResources.push(deploymentResult.deployedResource);
|
|
668
|
+
successfulResources++;
|
|
669
|
+
}
|
|
670
|
+
else {
|
|
671
|
+
levelHasFailures = true;
|
|
672
|
+
failedResources++;
|
|
673
|
+
if (deploymentResult.error) {
|
|
674
|
+
errors.push(deploymentResult.error);
|
|
675
|
+
}
|
|
676
|
+
if (deploymentResult.deployedResource) {
|
|
677
|
+
deployedResources.push(deploymentResult.deployedResource);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
else {
|
|
683
|
+
levelHasFailures = true;
|
|
684
|
+
levelLogger.error('Unexpected promise rejection in parallel deployment', result.reason);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
// Handle rollback if there are failures and rollback is enabled
|
|
688
|
+
if (levelHasFailures && options.rollbackOnFailure) {
|
|
689
|
+
levelLogger.warn('Level deployment failed, initiating rollback');
|
|
690
|
+
await this.rollbackDeployedResources(deployedResources, options);
|
|
691
|
+
const duration = Date.now() - startTime;
|
|
692
|
+
this.emitEvent(options, {
|
|
693
|
+
type: 'rollback',
|
|
694
|
+
message: `Deployment failed and rolled back in ${duration}ms`,
|
|
695
|
+
timestamp: new Date(),
|
|
696
|
+
});
|
|
697
|
+
return {
|
|
698
|
+
deploymentId,
|
|
699
|
+
resources: deployedResources,
|
|
700
|
+
dependencyGraph: graph.dependencyGraph,
|
|
701
|
+
duration,
|
|
702
|
+
status: 'failed',
|
|
703
|
+
errors,
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
// Calculate level performance metrics
|
|
707
|
+
const levelDuration = Date.now() - levelStartTime;
|
|
708
|
+
const totalOperations = currentLevel.resources.length + currentLevel.closures.length;
|
|
709
|
+
const isTestEnvironment = process.env.NODE_ENV === 'test' || process.env.VITEST === 'true';
|
|
710
|
+
const logLevel = isTestEnvironment ? 'debug' : 'info';
|
|
711
|
+
levelLogger[logLevel](`Level ${levelIndex + 1} deployment completed`, {
|
|
712
|
+
resources: { successful: successfulResources, failed: failedResources },
|
|
713
|
+
closures: { successful: successfulClosures, failed: failedClosures },
|
|
714
|
+
duration: levelDuration,
|
|
715
|
+
parallelism: totalOperations,
|
|
716
|
+
averageTimePerOperation: totalOperations > 0 ? Math.round(levelDuration / totalOperations) : 0
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
const duration = Date.now() - startTime;
|
|
720
|
+
const successfulResources = deployedResources.filter((r) => r.status !== 'failed');
|
|
721
|
+
const status = errors.length === 0 ? 'success' : successfulResources.length > 0 ? 'partial' : 'failed';
|
|
722
|
+
// Log comprehensive performance metrics
|
|
723
|
+
const isTestEnvironment = process.env.NODE_ENV === 'test' || process.env.VITEST === 'true';
|
|
724
|
+
const logLevel = isTestEnvironment ? 'debug' : 'info';
|
|
725
|
+
deploymentLogger[logLevel]('Parallel deployment with closures performance metrics', {
|
|
726
|
+
totalDuration: duration,
|
|
727
|
+
totalResources: enhancedPlan.totalResources,
|
|
728
|
+
totalClosures: enhancedPlan.totalClosures,
|
|
729
|
+
parallelLevels: enhancedPlan.levels.length,
|
|
730
|
+
maxParallelism: enhancedPlan.maxParallelism,
|
|
731
|
+
averageTimePerResource: enhancedPlan.totalResources > 0 ? Math.round(duration / enhancedPlan.totalResources) : 0,
|
|
732
|
+
successfulResources: successfulResources.length,
|
|
733
|
+
failedResources: errors.length,
|
|
734
|
+
status
|
|
735
|
+
});
|
|
736
|
+
this.emitEvent(options, {
|
|
737
|
+
type: status === 'success' ? 'completed' : 'failed',
|
|
738
|
+
message: `Deployment with closures ${status} in ${duration}ms (${enhancedPlan.totalClosures} closures + ${enhancedPlan.totalResources} resources across ${enhancedPlan.levels.length} levels)`,
|
|
739
|
+
timestamp: new Date(),
|
|
740
|
+
});
|
|
741
|
+
// Store deployment state for rollback
|
|
742
|
+
this.deploymentState.set(deploymentId, {
|
|
743
|
+
deploymentId,
|
|
744
|
+
resources: deployedResources,
|
|
745
|
+
dependencyGraph: graph.dependencyGraph,
|
|
746
|
+
startTime: new Date(startTime),
|
|
747
|
+
endTime: new Date(),
|
|
748
|
+
status: status === 'success' ? 'completed' : status === 'partial' ? 'completed' : 'failed',
|
|
749
|
+
options,
|
|
750
|
+
});
|
|
751
|
+
return {
|
|
752
|
+
deploymentId,
|
|
753
|
+
resources: deployedResources,
|
|
754
|
+
dependencyGraph: graph.dependencyGraph,
|
|
755
|
+
duration,
|
|
756
|
+
status,
|
|
757
|
+
errors,
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
catch (error) {
|
|
761
|
+
// Re-throw circular dependency errors immediately - these are configuration errors
|
|
762
|
+
if (error instanceof CircularDependencyError) {
|
|
763
|
+
throw error;
|
|
764
|
+
}
|
|
765
|
+
const duration = Date.now() - startTime;
|
|
766
|
+
this.emitEvent(options, {
|
|
767
|
+
type: 'failed',
|
|
768
|
+
message: `Deployment with closures failed: ${error}`,
|
|
769
|
+
timestamp: new Date(),
|
|
770
|
+
error: error,
|
|
771
|
+
});
|
|
772
|
+
// Store deployment state even for failed deployments (for rollback)
|
|
773
|
+
this.deploymentState.set(deploymentId, {
|
|
774
|
+
deploymentId,
|
|
775
|
+
resources: deployedResources,
|
|
776
|
+
dependencyGraph: graph.dependencyGraph,
|
|
777
|
+
startTime: new Date(startTime),
|
|
778
|
+
endTime: new Date(),
|
|
779
|
+
status: 'failed',
|
|
780
|
+
options,
|
|
781
|
+
});
|
|
782
|
+
return {
|
|
783
|
+
deploymentId,
|
|
784
|
+
resources: deployedResources,
|
|
785
|
+
dependencyGraph: graph.dependencyGraph,
|
|
786
|
+
duration,
|
|
787
|
+
status: 'failed',
|
|
788
|
+
errors: [
|
|
789
|
+
{
|
|
790
|
+
resourceId: 'deployment',
|
|
791
|
+
phase: 'deployment',
|
|
792
|
+
error: error,
|
|
793
|
+
timestamp: new Date(),
|
|
794
|
+
},
|
|
795
|
+
],
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
} /**
|
|
799
|
+
|
|
800
|
+
* Deploy a single resource
|
|
801
|
+
*/
|
|
802
|
+
async deploySingleResource(resource, context, options) {
|
|
803
|
+
const resourceId = resource.id || resource.__resourceId || resource.metadata?.name || 'unknown';
|
|
804
|
+
const resourceLogger = this.logger.child({
|
|
805
|
+
resourceId,
|
|
806
|
+
kind: resource.kind,
|
|
807
|
+
name: resource.metadata?.name
|
|
808
|
+
});
|
|
809
|
+
resourceLogger.debug('Starting single resource deployment');
|
|
810
|
+
this.emitEvent(options, {
|
|
811
|
+
type: 'progress',
|
|
812
|
+
resourceId,
|
|
813
|
+
message: `Deploying ${resource.kind}/${resource.metadata?.name}`,
|
|
814
|
+
timestamp: new Date(),
|
|
815
|
+
});
|
|
816
|
+
// 1. Resolve all references in the resource
|
|
817
|
+
let resolvedResource;
|
|
818
|
+
try {
|
|
819
|
+
resourceLogger.debug('Resolving resource references', {
|
|
820
|
+
originalMetadata: resource.metadata
|
|
821
|
+
});
|
|
822
|
+
const resolveTimeout = options.timeout || 30000;
|
|
823
|
+
resolvedResource = (await Promise.race([
|
|
824
|
+
this.referenceResolver.resolveReferences(resource, context),
|
|
825
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('Reference resolution timeout')), resolveTimeout)),
|
|
826
|
+
]));
|
|
827
|
+
resourceLogger.debug('References resolved successfully', {
|
|
828
|
+
resolvedMetadata: resolvedResource.metadata,
|
|
829
|
+
hasReadinessEvaluator: !!resolvedResource.readinessEvaluator
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
catch (error) {
|
|
833
|
+
resourceLogger.warn('Reference resolution failed, using original resource', error);
|
|
834
|
+
resolvedResource = resource;
|
|
835
|
+
}
|
|
836
|
+
// 2. Apply namespace if specified, but only if resource doesn't already have one
|
|
837
|
+
if (options.namespace && resolvedResource.metadata && typeof resolvedResource.metadata.namespace !== 'string') {
|
|
838
|
+
resourceLogger.debug('Applying namespace from deployment options', {
|
|
839
|
+
targetNamespace: options.namespace,
|
|
840
|
+
currentNamespace: resolvedResource.metadata.namespace,
|
|
841
|
+
currentNamespaceType: typeof resolvedResource.metadata.namespace
|
|
842
|
+
});
|
|
843
|
+
// Create a completely new metadata object to avoid proxy issues
|
|
844
|
+
const newMetadata = {
|
|
845
|
+
...resolvedResource.metadata,
|
|
846
|
+
namespace: options.namespace,
|
|
847
|
+
};
|
|
848
|
+
// Preserve the readiness evaluator when creating the new resource
|
|
849
|
+
const newResolvedResource = {
|
|
850
|
+
...resolvedResource,
|
|
851
|
+
metadata: newMetadata,
|
|
852
|
+
};
|
|
853
|
+
// Copy the non-enumerable readiness evaluator if it exists
|
|
854
|
+
const readinessEvaluator = resolvedResource.readinessEvaluator;
|
|
855
|
+
if (readinessEvaluator) {
|
|
856
|
+
Object.defineProperty(newResolvedResource, 'readinessEvaluator', {
|
|
857
|
+
value: readinessEvaluator,
|
|
858
|
+
enumerable: false,
|
|
859
|
+
configurable: true,
|
|
860
|
+
writable: false
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
resolvedResource = newResolvedResource;
|
|
864
|
+
}
|
|
865
|
+
// 3. Apply the resource to the cluster (or simulate for dry run)
|
|
866
|
+
let appliedResource;
|
|
867
|
+
if (options.dryRun) {
|
|
868
|
+
// In dry run mode, don't actually create the resource
|
|
869
|
+
resourceLogger.debug('Dry run mode: simulating resource creation');
|
|
870
|
+
appliedResource = {
|
|
871
|
+
...resolvedResource,
|
|
872
|
+
metadata: {
|
|
873
|
+
...resolvedResource.metadata,
|
|
874
|
+
uid: 'dry-run-uid',
|
|
875
|
+
},
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
else {
|
|
879
|
+
// Apply resource with retry logic
|
|
880
|
+
const retryPolicy = options.retryPolicy || {
|
|
881
|
+
maxRetries: 3,
|
|
882
|
+
backoffMultiplier: 2,
|
|
883
|
+
initialDelay: 1000,
|
|
884
|
+
maxDelay: 30000,
|
|
885
|
+
};
|
|
886
|
+
let lastError;
|
|
887
|
+
for (let attempt = 0; attempt <= retryPolicy.maxRetries; attempt++) {
|
|
888
|
+
try {
|
|
889
|
+
resourceLogger.debug('Applying resource to cluster', { attempt });
|
|
890
|
+
// Check if resource already exists
|
|
891
|
+
let existing;
|
|
892
|
+
try {
|
|
893
|
+
const readResult = await this.k8sApi.read({
|
|
894
|
+
apiVersion: resolvedResource.apiVersion,
|
|
895
|
+
kind: resolvedResource.kind,
|
|
896
|
+
metadata: {
|
|
897
|
+
name: resolvedResource.metadata?.name || '',
|
|
898
|
+
namespace: resolvedResource.metadata?.namespace || 'default',
|
|
899
|
+
},
|
|
900
|
+
});
|
|
901
|
+
existing = readResult.body;
|
|
902
|
+
}
|
|
903
|
+
catch (error) {
|
|
904
|
+
// If it's a 404, the resource doesn't exist, which is expected for creation
|
|
905
|
+
if (error.statusCode !== 404) {
|
|
906
|
+
resourceLogger.error('Error checking resource existence', error);
|
|
907
|
+
throw error;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
if (existing) {
|
|
911
|
+
// Resource exists, use patch for safer updates
|
|
912
|
+
resourceLogger.debug('Resource exists, patching');
|
|
913
|
+
const patchResult = await this.k8sApi.patch(resolvedResource);
|
|
914
|
+
appliedResource = patchResult.body;
|
|
915
|
+
}
|
|
916
|
+
else {
|
|
917
|
+
// Resource does not exist, create it
|
|
918
|
+
resourceLogger.debug('Resource does not exist, creating');
|
|
919
|
+
const createResult = await this.k8sApi.create(resolvedResource);
|
|
920
|
+
appliedResource = createResult.body;
|
|
921
|
+
}
|
|
922
|
+
resourceLogger.debug('Resource applied successfully', {
|
|
923
|
+
appliedName: appliedResource.metadata?.name,
|
|
924
|
+
appliedNamespace: appliedResource.metadata?.namespace,
|
|
925
|
+
operation: existing ? 'patched' : 'created',
|
|
926
|
+
attempt
|
|
927
|
+
});
|
|
928
|
+
// Success - break out of retry loop
|
|
929
|
+
break;
|
|
930
|
+
}
|
|
931
|
+
catch (error) {
|
|
932
|
+
lastError = error;
|
|
933
|
+
resourceLogger.error('Failed to apply resource to cluster', lastError, { attempt });
|
|
934
|
+
// If this was the last attempt, throw the error
|
|
935
|
+
if (attempt >= retryPolicy.maxRetries) {
|
|
936
|
+
throw new ResourceDeploymentError(resolvedResource.metadata?.name || 'unknown', resolvedResource.kind || 'Unknown', lastError);
|
|
937
|
+
}
|
|
938
|
+
// Calculate delay for next attempt
|
|
939
|
+
const delay = Math.min(retryPolicy.initialDelay * retryPolicy.backoffMultiplier ** attempt, retryPolicy.maxDelay);
|
|
940
|
+
resourceLogger.debug('Retrying resource deployment', {
|
|
941
|
+
attempt: attempt + 1,
|
|
942
|
+
maxRetries: retryPolicy.maxRetries,
|
|
943
|
+
delay
|
|
944
|
+
});
|
|
945
|
+
// Wait before retrying
|
|
946
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
// 4. Create deployed resource record
|
|
951
|
+
const deployedResource = {
|
|
952
|
+
id: resourceId,
|
|
953
|
+
kind: resolvedResource.kind || 'Unknown',
|
|
954
|
+
name: resolvedResource.metadata?.name || 'unknown',
|
|
955
|
+
namespace: resolvedResource.metadata?.namespace || 'default',
|
|
956
|
+
manifest: resolvedResource,
|
|
957
|
+
status: 'deployed',
|
|
958
|
+
deployedAt: new Date(),
|
|
959
|
+
};
|
|
960
|
+
// 5. Wait for resource to be ready if requested
|
|
961
|
+
if (options.waitForReady !== false) {
|
|
962
|
+
resourceLogger.debug('Waiting for resource to be ready');
|
|
963
|
+
await this.waitForResourceReady(deployedResource, options);
|
|
964
|
+
deployedResource.status = 'ready';
|
|
965
|
+
}
|
|
966
|
+
resourceLogger.debug('Single resource deployment completed');
|
|
967
|
+
return deployedResource;
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Wait for a resource to be ready
|
|
971
|
+
*/
|
|
972
|
+
async waitForResourceReady(deployedResource, options) {
|
|
973
|
+
const resourceKey = `${deployedResource.kind}/${deployedResource.name}/${deployedResource.namespace}`;
|
|
974
|
+
// Check if already marked as ready
|
|
975
|
+
if (deployedResource.status === 'ready' || this.readyResources.has(resourceKey)) {
|
|
976
|
+
this.logger.debug('Resource already marked as ready', { resourceKey });
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
979
|
+
// Safety-first approach: check for readiness evaluator before starting the wait loop
|
|
980
|
+
const readinessEvaluator = deployedResource.manifest.readinessEvaluator;
|
|
981
|
+
// Debug logging removed
|
|
982
|
+
if (!readinessEvaluator) {
|
|
983
|
+
const errorMessage = `Resource ${deployedResource.kind}/${deployedResource.name} does not have a factory-provided readiness evaluator`;
|
|
984
|
+
this.logger.error('Missing factory-provided readiness evaluator');
|
|
985
|
+
throw new Error(errorMessage);
|
|
986
|
+
}
|
|
987
|
+
const startTime = Date.now();
|
|
988
|
+
const timeout = options.timeout || 300000; // 5 minutes default
|
|
989
|
+
let lastStatus = null;
|
|
990
|
+
while (Date.now() - startTime < timeout) {
|
|
991
|
+
try {
|
|
992
|
+
// Use custom readiness evaluator
|
|
993
|
+
const { body: liveResource } = await this.k8sApi.read({
|
|
994
|
+
apiVersion: deployedResource.manifest.apiVersion || '',
|
|
995
|
+
kind: deployedResource.kind,
|
|
996
|
+
metadata: {
|
|
997
|
+
name: deployedResource.name,
|
|
998
|
+
namespace: deployedResource.namespace,
|
|
999
|
+
},
|
|
1000
|
+
});
|
|
1001
|
+
const result = readinessEvaluator(liveResource);
|
|
1002
|
+
if (typeof result === 'boolean') {
|
|
1003
|
+
if (result) {
|
|
1004
|
+
this.readyResources.add(resourceKey);
|
|
1005
|
+
this.emitEvent(options, {
|
|
1006
|
+
type: 'resource-ready',
|
|
1007
|
+
resourceId: deployedResource.id,
|
|
1008
|
+
message: `${deployedResource.kind}/${deployedResource.name} ready (custom evaluator)`,
|
|
1009
|
+
timestamp: new Date(),
|
|
1010
|
+
});
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
else if (result && typeof result === 'object' && 'ready' in result) {
|
|
1015
|
+
lastStatus = result;
|
|
1016
|
+
if (result.ready) {
|
|
1017
|
+
this.readyResources.add(resourceKey);
|
|
1018
|
+
this.emitEvent(options, {
|
|
1019
|
+
type: 'resource-ready',
|
|
1020
|
+
resourceId: deployedResource.id,
|
|
1021
|
+
message: result.message || `${deployedResource.kind}/${deployedResource.name} ready (custom evaluator)`,
|
|
1022
|
+
timestamp: new Date(),
|
|
1023
|
+
});
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
// Emit status update if we have status information
|
|
1028
|
+
if (lastStatus && typeof lastStatus === 'object' && 'message' in lastStatus) {
|
|
1029
|
+
this.emitEvent(options, {
|
|
1030
|
+
type: 'resource-status',
|
|
1031
|
+
resourceId: deployedResource.id,
|
|
1032
|
+
message: `${deployedResource.kind}/${deployedResource.name}: ${lastStatus.message}`,
|
|
1033
|
+
timestamp: new Date(),
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
// Wait before next check
|
|
1037
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
1038
|
+
}
|
|
1039
|
+
catch (error) {
|
|
1040
|
+
// Emit error status event
|
|
1041
|
+
this.emitEvent(options, {
|
|
1042
|
+
type: 'resource-status',
|
|
1043
|
+
resourceId: deployedResource.id,
|
|
1044
|
+
message: `Unable to read resource status: ${error instanceof Error ? error.message : String(error)}`,
|
|
1045
|
+
timestamp: new Date(),
|
|
1046
|
+
});
|
|
1047
|
+
// If we can't read the resource, it's not ready yet
|
|
1048
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
// Timeout reached
|
|
1052
|
+
const timeoutMessage = lastStatus
|
|
1053
|
+
? `Timeout waiting for ${deployedResource.kind}/${deployedResource.name}: ${lastStatus.message}`
|
|
1054
|
+
: `Timeout waiting for ${deployedResource.kind}/${deployedResource.name} to be ready`;
|
|
1055
|
+
throw new Error(timeoutMessage);
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Rollback deployed resources
|
|
1059
|
+
*/
|
|
1060
|
+
async rollbackDeployedResources(deployedResources, options) {
|
|
1061
|
+
this.emitEvent(options, {
|
|
1062
|
+
type: 'rollback',
|
|
1063
|
+
message: 'Starting rollback of deployed resources',
|
|
1064
|
+
timestamp: new Date(),
|
|
1065
|
+
});
|
|
1066
|
+
const rolledBackResources = [];
|
|
1067
|
+
const errors = [];
|
|
1068
|
+
// Rollback in reverse order
|
|
1069
|
+
const reversedResources = [...deployedResources].reverse();
|
|
1070
|
+
for (const resource of reversedResources) {
|
|
1071
|
+
// Only try to rollback resources that were actually deployed (not failed)
|
|
1072
|
+
if (resource.status === 'failed') {
|
|
1073
|
+
continue; // Skip resources that failed to deploy
|
|
1074
|
+
}
|
|
1075
|
+
try {
|
|
1076
|
+
await this.k8sApi.delete({
|
|
1077
|
+
apiVersion: resource.manifest.apiVersion || '',
|
|
1078
|
+
kind: resource.kind,
|
|
1079
|
+
metadata: {
|
|
1080
|
+
name: resource.name,
|
|
1081
|
+
namespace: resource.namespace,
|
|
1082
|
+
},
|
|
1083
|
+
});
|
|
1084
|
+
rolledBackResources.push(`${resource.kind}/${resource.name}`);
|
|
1085
|
+
}
|
|
1086
|
+
catch (error) {
|
|
1087
|
+
// Log and collect errors for individual resource deletion failures
|
|
1088
|
+
this.logger.warn('Failed to delete resource during rollback', {
|
|
1089
|
+
error: error,
|
|
1090
|
+
resourceId: resource.id,
|
|
1091
|
+
kind: resource.kind,
|
|
1092
|
+
name: resource.name
|
|
1093
|
+
});
|
|
1094
|
+
errors.push({
|
|
1095
|
+
resourceId: resource.id,
|
|
1096
|
+
phase: 'rollback',
|
|
1097
|
+
error: error,
|
|
1098
|
+
timestamp: new Date(),
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
return { rolledBackResources, errors };
|
|
1103
|
+
}
|
|
1104
|
+
/**
|
|
1105
|
+
* Generate a unique deployment ID
|
|
1106
|
+
*/
|
|
1107
|
+
generateDeploymentId() {
|
|
1108
|
+
return `deployment-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
1109
|
+
}
|
|
1110
|
+
/**
|
|
1111
|
+
* Emit deployment events
|
|
1112
|
+
*/
|
|
1113
|
+
emitEvent(options, event) {
|
|
1114
|
+
if (options.progressCallback) {
|
|
1115
|
+
options.progressCallback(event);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
/**
|
|
1119
|
+
* Deploy a single resource (legacy method for compatibility)
|
|
1120
|
+
*/
|
|
1121
|
+
async deployResource(resource, options) {
|
|
1122
|
+
const context = {
|
|
1123
|
+
deployedResources: [],
|
|
1124
|
+
kubeClient: this.kubeClient,
|
|
1125
|
+
...(options.namespace && { namespace: options.namespace }),
|
|
1126
|
+
timeout: options.timeout || 30000,
|
|
1127
|
+
};
|
|
1128
|
+
return this.deploySingleResource(resource, context, options);
|
|
1129
|
+
}
|
|
1130
|
+
/**
|
|
1131
|
+
* Delete a resource from the cluster
|
|
1132
|
+
*/
|
|
1133
|
+
async deleteResource(resource) {
|
|
1134
|
+
const deleteLogger = this.logger.child({
|
|
1135
|
+
resourceId: resource.id,
|
|
1136
|
+
kind: resource.kind,
|
|
1137
|
+
name: resource.name
|
|
1138
|
+
});
|
|
1139
|
+
try {
|
|
1140
|
+
await this.k8sApi.delete({
|
|
1141
|
+
apiVersion: resource.manifest.apiVersion || '',
|
|
1142
|
+
kind: resource.kind,
|
|
1143
|
+
metadata: {
|
|
1144
|
+
name: resource.name,
|
|
1145
|
+
namespace: resource.namespace,
|
|
1146
|
+
},
|
|
1147
|
+
});
|
|
1148
|
+
// Wait for resource to be deleted
|
|
1149
|
+
const timeout = 30000; // 30 seconds
|
|
1150
|
+
const startTime = Date.now();
|
|
1151
|
+
while (Date.now() - startTime < timeout) {
|
|
1152
|
+
try {
|
|
1153
|
+
await this.k8sApi.read({
|
|
1154
|
+
apiVersion: resource.manifest.apiVersion || '',
|
|
1155
|
+
kind: resource.kind,
|
|
1156
|
+
metadata: {
|
|
1157
|
+
name: resource.name,
|
|
1158
|
+
namespace: resource.namespace,
|
|
1159
|
+
},
|
|
1160
|
+
});
|
|
1161
|
+
// Resource still exists, wait and try again
|
|
1162
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1163
|
+
}
|
|
1164
|
+
catch (error) {
|
|
1165
|
+
// Resource not found, deletion successful
|
|
1166
|
+
if (this.isNotFoundError(error)) {
|
|
1167
|
+
deleteLogger.debug('Resource successfully deleted');
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
throw error;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
throw new Error(`Timeout waiting for resource ${resource.kind}/${resource.name} to be deleted`);
|
|
1174
|
+
}
|
|
1175
|
+
catch (error) {
|
|
1176
|
+
deleteLogger.error('Failed to delete resource', error);
|
|
1177
|
+
throw error;
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
/**
|
|
1181
|
+
* Wait for resource readiness (legacy method for compatibility)
|
|
1182
|
+
*/
|
|
1183
|
+
async waitForResourceReadiness(resource, options) {
|
|
1184
|
+
return this.waitForResourceReady(resource, options);
|
|
1185
|
+
}
|
|
1186
|
+
/**
|
|
1187
|
+
* Rollback a deployment by ID
|
|
1188
|
+
*/
|
|
1189
|
+
async rollback(deploymentId) {
|
|
1190
|
+
const startTime = Date.now();
|
|
1191
|
+
const deploymentRecord = this.deploymentState.get(deploymentId);
|
|
1192
|
+
if (!deploymentRecord) {
|
|
1193
|
+
throw new Error(`Deployment ${deploymentId} not found. Cannot rollback.`);
|
|
1194
|
+
}
|
|
1195
|
+
try {
|
|
1196
|
+
const { rolledBackResources, errors } = await this.rollbackDeployedResources(deploymentRecord.resources, deploymentRecord.options);
|
|
1197
|
+
const status = errors.length === 0 ? 'success' :
|
|
1198
|
+
rolledBackResources.length > 0 ? 'partial' : 'failed';
|
|
1199
|
+
return {
|
|
1200
|
+
deploymentId,
|
|
1201
|
+
rolledBackResources,
|
|
1202
|
+
duration: Date.now() - startTime,
|
|
1203
|
+
status,
|
|
1204
|
+
errors,
|
|
1205
|
+
};
|
|
1206
|
+
}
|
|
1207
|
+
catch (error) {
|
|
1208
|
+
// This shouldn't happen now since rollbackDeployedResources handles its own errors
|
|
1209
|
+
return {
|
|
1210
|
+
deploymentId,
|
|
1211
|
+
rolledBackResources: [],
|
|
1212
|
+
duration: Date.now() - startTime,
|
|
1213
|
+
status: 'failed',
|
|
1214
|
+
errors: [{
|
|
1215
|
+
resourceId: deploymentId,
|
|
1216
|
+
phase: 'rollback',
|
|
1217
|
+
error: error,
|
|
1218
|
+
timestamp: new Date(),
|
|
1219
|
+
}],
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
/**
|
|
1224
|
+
* Get deployment status by ID
|
|
1225
|
+
*/
|
|
1226
|
+
async getStatus(deploymentId) {
|
|
1227
|
+
const deploymentRecord = this.deploymentState.get(deploymentId);
|
|
1228
|
+
if (!deploymentRecord) {
|
|
1229
|
+
return {
|
|
1230
|
+
deploymentId,
|
|
1231
|
+
status: 'unknown',
|
|
1232
|
+
startTime: new Date(),
|
|
1233
|
+
resources: [],
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1236
|
+
const result = {
|
|
1237
|
+
deploymentId,
|
|
1238
|
+
status: deploymentRecord.status === 'completed' ? 'completed' :
|
|
1239
|
+
deploymentRecord.status === 'failed' ? 'failed' : 'running',
|
|
1240
|
+
startTime: deploymentRecord.startTime,
|
|
1241
|
+
resources: deploymentRecord.resources,
|
|
1242
|
+
};
|
|
1243
|
+
if (deploymentRecord.endTime) {
|
|
1244
|
+
result.endTime = deploymentRecord.endTime;
|
|
1245
|
+
result.duration = deploymentRecord.endTime.getTime() - deploymentRecord.startTime.getTime();
|
|
1246
|
+
}
|
|
1247
|
+
return result;
|
|
1248
|
+
}
|
|
1249
|
+
/**
|
|
1250
|
+
* Check if an error is a "not found" error
|
|
1251
|
+
*/
|
|
1252
|
+
isNotFoundError(error) {
|
|
1253
|
+
if (error && typeof error === 'object') {
|
|
1254
|
+
const k8sError = error;
|
|
1255
|
+
return k8sError.statusCode === 404 || k8sError.body?.code === 404;
|
|
1256
|
+
}
|
|
1257
|
+
return false;
|
|
1258
|
+
}
|
|
1259
|
+
/**
|
|
1260
|
+
* Wait for CRD establishment if the resource is a custom resource
|
|
1261
|
+
*/
|
|
1262
|
+
async waitForCRDIfCustomResource(resource, options, logger) {
|
|
1263
|
+
// Skip if this is not a custom resource
|
|
1264
|
+
if (!this.isCustomResource(resource)) {
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
const crdName = await this.getCRDNameForResource(resource);
|
|
1268
|
+
if (!crdName) {
|
|
1269
|
+
logger.warn('Could not determine CRD name for custom resource', {
|
|
1270
|
+
kind: resource.kind,
|
|
1271
|
+
apiVersion: resource.apiVersion
|
|
1272
|
+
});
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
logger.debug('Custom resource detected, waiting for CRD establishment', {
|
|
1276
|
+
resourceKind: resource.kind,
|
|
1277
|
+
crdName
|
|
1278
|
+
});
|
|
1279
|
+
await this.waitForCRDEstablishment({ metadata: { name: crdName } }, options, logger);
|
|
1280
|
+
logger.debug('CRD established, proceeding with custom resource deployment', {
|
|
1281
|
+
resourceKind: resource.kind,
|
|
1282
|
+
crdName
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1285
|
+
/**
|
|
1286
|
+
* Check if a resource is a CustomResourceDefinition
|
|
1287
|
+
*/
|
|
1288
|
+
isCRD(resource) {
|
|
1289
|
+
return resource.kind === 'CustomResourceDefinition' &&
|
|
1290
|
+
resource.apiVersion?.includes('apiextensions.k8s.io');
|
|
1291
|
+
}
|
|
1292
|
+
/**
|
|
1293
|
+
* Check if a resource is a custom resource (not a built-in Kubernetes resource)
|
|
1294
|
+
*/
|
|
1295
|
+
isCustomResource(resource) {
|
|
1296
|
+
if (!resource.apiVersion || !resource.kind) {
|
|
1297
|
+
return false;
|
|
1298
|
+
}
|
|
1299
|
+
// Built-in Kubernetes API groups that are NOT custom resources
|
|
1300
|
+
const builtInApiGroups = [
|
|
1301
|
+
'v1', // Core API group
|
|
1302
|
+
'apps/v1',
|
|
1303
|
+
'extensions/v1beta1',
|
|
1304
|
+
'networking.k8s.io/v1',
|
|
1305
|
+
'policy/v1',
|
|
1306
|
+
'rbac.authorization.k8s.io/v1',
|
|
1307
|
+
'storage.k8s.io/v1',
|
|
1308
|
+
'apiextensions.k8s.io/v1', // CRDs themselves
|
|
1309
|
+
'admissionregistration.k8s.io/v1',
|
|
1310
|
+
'apiregistration.k8s.io/v1',
|
|
1311
|
+
'authentication.k8s.io/v1',
|
|
1312
|
+
'authorization.k8s.io/v1',
|
|
1313
|
+
'autoscaling/v1',
|
|
1314
|
+
'autoscaling/v2',
|
|
1315
|
+
'batch/v1',
|
|
1316
|
+
'certificates.k8s.io/v1',
|
|
1317
|
+
'coordination.k8s.io/v1',
|
|
1318
|
+
'discovery.k8s.io/v1',
|
|
1319
|
+
'events.k8s.io/v1',
|
|
1320
|
+
'flowcontrol.apiserver.k8s.io/v1beta3',
|
|
1321
|
+
'node.k8s.io/v1',
|
|
1322
|
+
'scheduling.k8s.io/v1'
|
|
1323
|
+
];
|
|
1324
|
+
return !builtInApiGroups.includes(resource.apiVersion);
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Get the CRD name for a custom resource
|
|
1328
|
+
*/
|
|
1329
|
+
async getCRDNameForResource(resource) {
|
|
1330
|
+
if (!resource.apiVersion || !resource.kind) {
|
|
1331
|
+
return null;
|
|
1332
|
+
}
|
|
1333
|
+
// Only return CRD name for custom resources
|
|
1334
|
+
if (!this.isCustomResource(resource)) {
|
|
1335
|
+
return null;
|
|
1336
|
+
}
|
|
1337
|
+
// Extract group from apiVersion (e.g., "example.com/v1" -> "example.com")
|
|
1338
|
+
const apiVersionParts = resource.apiVersion.split('/');
|
|
1339
|
+
const group = apiVersionParts.length > 1 ? apiVersionParts[0] : '';
|
|
1340
|
+
if (!group) {
|
|
1341
|
+
return null; // Core API resources don't have CRDs
|
|
1342
|
+
}
|
|
1343
|
+
try {
|
|
1344
|
+
// Try to find the CRD by querying the API
|
|
1345
|
+
const crds = await this.k8sApi.list('apiextensions.k8s.io/v1', 'CustomResourceDefinition');
|
|
1346
|
+
// Look for a CRD that matches our group and kind
|
|
1347
|
+
const matchingCrd = crds.body?.items?.find((crd) => {
|
|
1348
|
+
const crdSpec = crd.spec;
|
|
1349
|
+
return crdSpec?.group === group &&
|
|
1350
|
+
crdSpec?.names?.kind === resource.kind;
|
|
1351
|
+
});
|
|
1352
|
+
if (matchingCrd) {
|
|
1353
|
+
return matchingCrd.metadata?.name;
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
catch (error) {
|
|
1357
|
+
// If we can't query CRDs, fall back to heuristic
|
|
1358
|
+
console.warn('Failed to query CRDs, using heuristic for CRD name generation:', error);
|
|
1359
|
+
}
|
|
1360
|
+
// Fallback: Convert Kind to plural lowercase (simple heuristic)
|
|
1361
|
+
const kind = resource.kind.toLowerCase();
|
|
1362
|
+
const plural = kind.endsWith('s') ? kind : `${kind}s`;
|
|
1363
|
+
return `${plural}.${group}`;
|
|
1364
|
+
}
|
|
1365
|
+
/**
|
|
1366
|
+
* Wait for a CRD to be established in the cluster
|
|
1367
|
+
*/
|
|
1368
|
+
async waitForCRDEstablishment(crd, options, logger) {
|
|
1369
|
+
const crdName = crd.metadata?.name;
|
|
1370
|
+
const timeout = options.timeout || 300000; // 5 minutes default
|
|
1371
|
+
const startTime = Date.now();
|
|
1372
|
+
const pollInterval = 2000; // 2 seconds
|
|
1373
|
+
logger.debug('Waiting for CRD to exist and be established', { crdName, timeout });
|
|
1374
|
+
while (Date.now() - startTime < timeout) {
|
|
1375
|
+
try {
|
|
1376
|
+
// Check if CRD is established by reading its status
|
|
1377
|
+
const crdStatus = await this.k8sApi.read({
|
|
1378
|
+
apiVersion: 'apiextensions.k8s.io/v1',
|
|
1379
|
+
kind: 'CustomResourceDefinition',
|
|
1380
|
+
metadata: { name: crdName } // CRDs are cluster-scoped, no namespace needed
|
|
1381
|
+
});
|
|
1382
|
+
const conditions = crdStatus.body?.status?.conditions || [];
|
|
1383
|
+
const establishedCondition = conditions.find((c) => c.type === 'Established');
|
|
1384
|
+
if (establishedCondition?.status === 'True') {
|
|
1385
|
+
logger.debug('CRD exists and is established', { crdName });
|
|
1386
|
+
return;
|
|
1387
|
+
}
|
|
1388
|
+
logger.debug('CRD exists but not yet established, waiting...', {
|
|
1389
|
+
crdName,
|
|
1390
|
+
establishedStatus: establishedCondition?.status || 'unknown'
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1393
|
+
catch (error) {
|
|
1394
|
+
// CRD might not exist yet (e.g., being installed by a closure)
|
|
1395
|
+
// This is expected in scenarios where closures install CRDs
|
|
1396
|
+
logger.debug('CRD not found yet, waiting for it to be created...', {
|
|
1397
|
+
crdName,
|
|
1398
|
+
error: error.message
|
|
1399
|
+
});
|
|
1400
|
+
}
|
|
1401
|
+
// Wait before next poll
|
|
1402
|
+
await new Promise(resolve => setTimeout(resolve, pollInterval));
|
|
1403
|
+
}
|
|
1404
|
+
// Timeout reached
|
|
1405
|
+
throw new Error(`Timeout waiting for CRD ${crdName} to be established after ${timeout}ms`);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
//# sourceMappingURL=engine.js.map
|