@go-to-k/cdkd 0.246.0 → 0.248.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/README.md
CHANGED
|
@@ -4,15 +4,14 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/@go-to-k/cdkd)
|
|
5
5
|
[](./LICENSE)
|
|
6
6
|
|
|
7
|
-
Drop-in CDK CLI for existing CDK apps — faster deploys via AWS SDK instead of CloudFormation
|
|
7
|
+
Drop-in CDK CLI for existing CDK apps — up to 15x faster deploys via direct AWS SDK calls instead of CloudFormation.
|
|
8
8
|
|
|
9
|
-
- **Drop-in CDK compatible
|
|
10
|
-
- **Up to 15x faster deploys
|
|
11
|
-
- **Local dev for any CDK app** — invoke Lambdas, serve API Gateway routes, run ECS tasks/services directly from your CDK code. Works against both `cdkd deploy`-managed AND `cdk deploy`-managed (CloudFormation) stacks via `--from-state` / `--from-cfn-stack` — no migration, no `cdk synth → sam local` round-trip.
|
|
9
|
+
- **Drop-in CDK compatible**: your existing CDK app code runs as-is.
|
|
10
|
+
- **Up to 15x faster deploys**: direct SDK calls, aggressive parallelization, and `--no-wait` to skip slow stabilization waits.
|
|
12
11
|
|
|
13
12
|

|
|
14
13
|
|
|
15
|
-
**cdkd complements the AWS CDK CLI rather than replacing it.** Use cdkd in dev/test for rapid iteration and
|
|
14
|
+
**cdkd complements the AWS CDK CLI rather than replacing it.** Use cdkd in dev/test for rapid iteration and local execution; use the AWS CDK CLI in production for full CloudFormation tooling. Install cdkd alongside an existing `cdk deploy` workflow: no migration needed. Bidirectional migration is also supported: [import](#importing-existing-resources) into cdkd or [export](#exporting-a-stack-back-to-cloudformation) back to CloudFormation when ready.
|
|
16
15
|
|
|
17
16
|
**A natural fit for AI-driven development.** AI coding agents iterate in tight spin-up / tear-down loops — and cdkd keeps each turn short, with fast deploys and an equally fast `cdkd destroy` that deletes via direct SDK calls instead of polling a CloudFormation stack-delete.
|
|
18
17
|
|
|
@@ -139,13 +138,14 @@ parsing → synthesis → asset publishing → per-stack deploy), see
|
|
|
139
138
|
## Prerequisites
|
|
140
139
|
|
|
141
140
|
- **Node.js** >= 20.0.0
|
|
142
|
-
- **AWS CDK Bootstrap: not required.** `cdkd bootstrap` (once per account) creates
|
|
143
|
-
everything cdkd needs, and per-region asset storage is added automatically on the
|
|
144
|
-
first `cdkd deploy` into each region. Existing setups, legacy-mode opt-outs, and
|
|
145
|
-
how this relates to `cdk bootstrap`: see
|
|
146
|
-
[Upgrading from an earlier cdkd version](#upgrading-from-an-earlier-cdkd-version).
|
|
147
141
|
- **AWS credentials with admin-equivalent permissions** for the resources being deployed. cdkd does NOT route through CloudFormation, so CDK CLI's `cdk-hnb659fds-deploy-role-*` is NOT sufficient — see [`--role-arn`](docs/cli-reference.md).
|
|
148
142
|
|
|
143
|
+
AWS CDK's `cdk bootstrap` is not required. Instead, run `cdkd bootstrap` once per
|
|
144
|
+
account: it creates everything cdkd needs, and per-region asset storage is added
|
|
145
|
+
automatically on the first `cdkd deploy` into each region. Existing setups,
|
|
146
|
+
legacy-mode opt-outs, and how this relates to `cdk bootstrap`: see
|
|
147
|
+
[Upgrading from an earlier cdkd version](#upgrading-from-an-earlier-cdkd-version).
|
|
148
|
+
|
|
149
149
|
## Installation
|
|
150
150
|
|
|
151
151
|
```bash
|
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, f as withSkipPrefix, i as runStackBuffered, n as getLogger, o as PATTERN_B_NAME_OPTIONS, p as withStackName, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-BzO-joNR.js";
|
|
3
3
|
import { A as withErrorHandling, C as StackHasActiveImportsError, S as ResourceUpdateNotSupportedError, _ as MissingCdkCliError, a as normalizeAwsTagsToCfn, b as ProvisioningError, f as LocalInvokeBuildError$1, i as matchesCdkPath, k as normalizeAwsError, l as CdkdError, m as LocalStartServiceError, n as isTerminationProtectionPropagationError, o as resolveExplicitPhysicalId, p as LocalMigrateError, r as CDK_PATH_TAG, s as assertRegionMatch, t as disableInstanceApiTermination, v as NestedStackChildDirectDestroyError, w as StackTerminationProtectionError, x as ResourceTimeoutError, y as PartialFailureError } from "./ec2-termination-protection-q-WGWgxa.js";
|
|
4
4
|
import { a as setAwsClients, i as resetAwsClients, r as getAwsClients, t as AwsClients } from "./aws-clients-B15NAPbL.js";
|
|
5
|
-
import { $ as getDockerCmd, A as DiffCalculator, B as buildAssetRedirectMap, C as findActionableSilentDrops, Ct as resolveBucketRegion, D as refStateLookupFromResource, E as cfnRefValueFromPhysicalId, F as rebuildClientForBucketRegion, G as BOOTSTRAP_MARKER_PREFIX, H as loadPublishableAssetManifest, I as shouldRetainResource, J as parseBootstrapMarker, K as ensureAssetStorage, L as AssetPublisher, M as TemplateParser, N as LockManager, O as WAFv2WebACLProvider, P as S3StateBackend, Q as formatDockerLoginError, R as stringifyValue, S as ProviderRegistry, T as IntrinsicFunctionResolver, U as rewriteTemplateAssetReferences, V as createAssetRedirectResolver, W as AssetModeResolver, X as validateContainerRepoName, Y as validateAssetBucketName, Z as buildDockerImage, _ as green, _t as CFN_TEMPLATE_URL_LIMIT, a as withRetry, at as synthesisStatusMessage, b as IAMRoleProvider, bt as uploadCfnTemplate, c as computeImplicitDeleteEdges, ct as resolveApp, d as isStatefulRecreateTargetSync, dt as resolveSkipPrefix, et as runDockerForeground, f as renderStatefulReason, ft as resolveStateBucketWithDefault, g as gray, gt as CFN_TEMPLATE_BODY_LIMIT, h as cyan, ht as warnDeprecatedNoPrefixCliFlag, i as withResourceDeadline, it as Synthesizer, j as DagBuilder, k as applyRoleArnIfSet, l as extractDeploymentEventError, lt as resolveAutoAssetStorage, m as bold, mt as resolveUseCdkBootstrapAssets, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as AssetManifestLoader, o as isRetryableTransientError, ot as getDefaultStateBucketName, p as formatResourceLine, pt as resolveStateBucketWithDefaultAndSource, q as getBootstrapMarkerKey, r as DeployEngine, rt as getDockerImageBySourceHash, s as IMPLICIT_DELETE_DEPENDENCIES, st as getLegacyStateBucketName, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as runDockerStreaming, u as MULTI_REGION_RECREATE_BLOCKED_TYPES, ut as resolveCaptureObservedState, v as red, vt as MIGRATE_TMP_PREFIX, w as CloudControlProvider, x as collectInlinePolicyNamesManagedBySiblings, xt as AssemblyReader, y as yellow, yt as findLargeInlineResources, z as WorkGraph } from "./deploy-engine-
|
|
5
|
+
import { $ as getDockerCmd, A as DiffCalculator, B as buildAssetRedirectMap, C as findActionableSilentDrops, Ct as resolveBucketRegion, D as refStateLookupFromResource, E as cfnRefValueFromPhysicalId, F as rebuildClientForBucketRegion, G as BOOTSTRAP_MARKER_PREFIX, H as loadPublishableAssetManifest, I as shouldRetainResource, J as parseBootstrapMarker, K as ensureAssetStorage, L as AssetPublisher, M as TemplateParser, N as LockManager, O as WAFv2WebACLProvider, P as S3StateBackend, Q as formatDockerLoginError, R as stringifyValue, S as ProviderRegistry, T as IntrinsicFunctionResolver, U as rewriteTemplateAssetReferences, V as createAssetRedirectResolver, W as AssetModeResolver, X as validateContainerRepoName, Y as validateAssetBucketName, Z as buildDockerImage, _ as green, _t as CFN_TEMPLATE_URL_LIMIT, a as withRetry, at as synthesisStatusMessage, b as IAMRoleProvider, bt as uploadCfnTemplate, c as computeImplicitDeleteEdges, ct as resolveApp, d as isStatefulRecreateTargetSync, dt as resolveSkipPrefix, et as runDockerForeground, f as renderStatefulReason, ft as resolveStateBucketWithDefault, g as gray, gt as CFN_TEMPLATE_BODY_LIMIT, h as cyan, ht as warnDeprecatedNoPrefixCliFlag, i as withResourceDeadline, it as Synthesizer, j as DagBuilder, k as applyRoleArnIfSet, l as extractDeploymentEventError, lt as resolveAutoAssetStorage, m as bold, mt as resolveUseCdkBootstrapAssets, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as AssetManifestLoader, o as isRetryableTransientError, ot as getDefaultStateBucketName, p as formatResourceLine, pt as resolveStateBucketWithDefaultAndSource, q as getBootstrapMarkerKey, r as DeployEngine, rt as getDockerImageBySourceHash, s as IMPLICIT_DELETE_DEPENDENCIES, st as getLegacyStateBucketName, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as runDockerStreaming, u as MULTI_REGION_RECREATE_BLOCKED_TYPES, ut as resolveCaptureObservedState, v as red, vt as MIGRATE_TMP_PREFIX, w as CloudControlProvider, x as collectInlinePolicyNamesManagedBySiblings, xt as AssemblyReader, y as yellow, yt as findLargeInlineResources, z as WorkGraph } from "./deploy-engine-D6U-Cm8B.js";
|
|
6
6
|
import { t as expectedOwnerParam } from "./expected-bucket-owner-BjEfUnkA.js";
|
|
7
7
|
import { t as ASGProvider } from "./asg-provider-Dbl1_mwT.js";
|
|
8
8
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -60,11 +60,12 @@ import { AddTagsToStreamCommand, CreateStreamCommand, DecreaseStreamRetentionPer
|
|
|
60
60
|
import { CreateScheduleCommand, DeleteScheduleCommand, GetScheduleCommand, ResourceNotFoundException as ResourceNotFoundException$9, SchedulerClient, UpdateScheduleCommand } from "@aws-sdk/client-scheduler";
|
|
61
61
|
import { AccessPointNotFound, CreateAccessPointCommand, CreateFileSystemCommand, CreateMountTargetCommand, DeleteAccessPointCommand, DeleteFileSystemCommand, DeleteMountTargetCommand, DescribeAccessPointsCommand, DescribeBackupPolicyCommand, DescribeFileSystemPolicyCommand, DescribeFileSystemsCommand, DescribeLifecycleConfigurationCommand, DescribeMountTargetSecurityGroupsCommand, DescribeMountTargetsCommand, EFSClient, FileSystemNotFound, ModifyMountTargetSecurityGroupsCommand, MountTargetNotFound, PutBackupPolicyCommand, PutFileSystemPolicyCommand, PutLifecycleConfigurationCommand, UpdateFileSystemCommand, UpdateFileSystemProtectionCommand } from "@aws-sdk/client-efs";
|
|
62
62
|
import { CreateFileSystemCommand as CreateFileSystemCommand$1, CreateFileSystemFromBackupCommand, DeleteFileSystemCommand as DeleteFileSystemCommand$1, DescribeFileSystemsCommand as DescribeFileSystemsCommand$1, FSxClient, FileSystemNotFound as FileSystemNotFound$1, TagResourceCommand as TagResourceCommand$18, UntagResourceCommand as UntagResourceCommand$18, UpdateFileSystemCommand as UpdateFileSystemCommand$1 } from "@aws-sdk/client-fsx";
|
|
63
|
-
import { AddTagsCommand as AddTagsCommand$1, DescribeClusterCommand, EMRClient, InvalidRequestException, ModifyClusterCommand, PutAutoTerminationPolicyCommand, PutManagedScalingPolicyCommand, RemoveAutoTerminationPolicyCommand, RemoveManagedScalingPolicyCommand, RemoveTagsCommand as RemoveTagsCommand$1, RunJobFlowCommand, SetTerminationProtectionCommand, SetVisibleToAllUsersCommand, TerminateJobFlowsCommand } from "@aws-sdk/client-emr";
|
|
63
|
+
import { AddTagsCommand as AddTagsCommand$1, DescribeClusterCommand, EMRClient, InvalidRequestException, ListClustersCommand as ListClustersCommand$1, ListInstanceFleetsCommand, ListInstanceGroupsCommand, ModifyClusterCommand, PutAutoTerminationPolicyCommand, PutManagedScalingPolicyCommand, RemoveAutoTerminationPolicyCommand, RemoveManagedScalingPolicyCommand, RemoveTagsCommand as RemoveTagsCommand$1, RunJobFlowCommand, SetTerminationProtectionCommand, SetVisibleToAllUsersCommand, TerminateJobFlowsCommand } from "@aws-sdk/client-emr";
|
|
64
64
|
import { CreateDeliveryStreamCommand, DeleteDeliveryStreamCommand, DescribeDeliveryStreamCommand, FirehoseClient, ListDeliveryStreamsCommand, ListTagsForDeliveryStreamCommand, ResourceNotFoundException as ResourceNotFoundException$10, TagDeliveryStreamCommand, UntagDeliveryStreamCommand, UpdateDestinationCommand } from "@aws-sdk/client-firehose";
|
|
65
65
|
import { AddTagsCommand as AddTagsCommand$2, CloudTrailClient, CreateTrailCommand, DeleteTrailCommand, GetEventSelectorsCommand, GetInsightSelectorsCommand, GetTrailCommand, GetTrailStatusCommand, ListTagsCommand as ListTagsCommand$1, ListTrailsCommand, PutEventSelectorsCommand, PutInsightSelectorsCommand, RemoveTagsCommand as RemoveTagsCommand$2, StartLoggingCommand, StopLoggingCommand, TrailNotFoundException, UpdateTrailCommand } from "@aws-sdk/client-cloudtrail";
|
|
66
66
|
import { BatchGetProjectsCommand, CodeBuildClient, CreateProjectCommand, DeleteProjectCommand, ListProjectsCommand, ResourceNotFoundException as ResourceNotFoundException$11, UpdateProjectCommand } from "@aws-sdk/client-codebuild";
|
|
67
|
-
import { CodeCommitClient, CreateRepositoryCommand as CreateRepositoryCommand$1, DeleteRepositoryCommand as DeleteRepositoryCommand$1, GetRepositoryCommand, ListRepositoriesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$19, RepositoryDoesNotExistException, TagResourceCommand as TagResourceCommand$19, UntagResourceCommand as UntagResourceCommand$19, UpdateRepositoryDescriptionCommand, UpdateRepositoryEncryptionKeyCommand, UpdateRepositoryNameCommand } from "@aws-sdk/client-codecommit";
|
|
67
|
+
import { CodeCommitClient, CreateCommitCommand, CreateRepositoryCommand as CreateRepositoryCommand$1, DeleteRepositoryCommand as DeleteRepositoryCommand$1, GetRepositoryCommand, ListRepositoriesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$19, PutRepositoryTriggersCommand, RepositoryDoesNotExistException, TagResourceCommand as TagResourceCommand$19, UntagResourceCommand as UntagResourceCommand$19, UpdateRepositoryDescriptionCommand, UpdateRepositoryEncryptionKeyCommand, UpdateRepositoryNameCommand } from "@aws-sdk/client-codecommit";
|
|
68
|
+
import AdmZip from "adm-zip";
|
|
68
69
|
import { CreateLifecyclePolicyCommand, DLMClient, DeleteLifecyclePolicyCommand as DeleteLifecyclePolicyCommand$1, GetLifecyclePoliciesCommand, GetLifecyclePolicyCommand as GetLifecyclePolicyCommand$1, ResourceNotFoundException as ResourceNotFoundException$12, TagResourceCommand as TagResourceCommand$20, UntagResourceCommand as UntagResourceCommand$20, UpdateLifecyclePolicyCommand } from "@aws-sdk/client-dlm";
|
|
69
70
|
import { CreateVectorBucketCommand, DeleteIndexCommand, DeleteVectorBucketCommand, GetVectorBucketCommand, ListIndexesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$20, ListVectorBucketsCommand, S3VectorsClient, TagResourceCommand as TagResourceCommand$21, UntagResourceCommand as UntagResourceCommand$21 } from "@aws-sdk/client-s3vectors";
|
|
70
71
|
import { CreateNamespaceCommand, CreateTableBucketCommand, CreateTableCommand as CreateTableCommand$2, DeleteNamespaceCommand as DeleteNamespaceCommand$1, DeleteTableBucketCommand, DeleteTableCommand as DeleteTableCommand$2, GetTableBucketCommand, GetTableCommand as GetTableCommand$1, ListNamespacesCommand as ListNamespacesCommand$1, ListTableBucketsCommand, ListTablesCommand as ListTablesCommand$1, ListTagsForResourceCommand as ListTagsForResourceCommand$21, NotFoundException as NotFoundException$6, S3TablesClient, TagResourceCommand as TagResourceCommand$22, UntagResourceCommand as UntagResourceCommand$22 } from "@aws-sdk/client-s3tables";
|
|
@@ -1890,7 +1891,7 @@ const FLUSH_INTERVAL_MS = 2e3;
|
|
|
1890
1891
|
const FLUSH_EVENT_THRESHOLD = 50;
|
|
1891
1892
|
/** Build-time cdkd version, with a dev fallback for non-built contexts. */
|
|
1892
1893
|
function getCdkdVersion() {
|
|
1893
|
-
return "0.
|
|
1894
|
+
return "0.248.0";
|
|
1894
1895
|
}
|
|
1895
1896
|
/**
|
|
1896
1897
|
* Generate a time-sortable unique run id, e.g.
|
|
@@ -35020,6 +35021,18 @@ const CREATE_READY_STATES = new Set(["WAITING", "RUNNING"]);
|
|
|
35020
35021
|
*/
|
|
35021
35022
|
const TERMINAL_STATES = new Set(["TERMINATED", "TERMINATED_WITH_ERRORS"]);
|
|
35022
35023
|
/**
|
|
35024
|
+
* Cluster states that mean "the cluster is alive" — the `ListClusters` filter
|
|
35025
|
+
* used by `import()` discovery so a long-gone `TERMINATED*` cluster is never
|
|
35026
|
+
* adopted. Everything except the two `TERMINAL_STATES`.
|
|
35027
|
+
*/
|
|
35028
|
+
const NON_TERMINATED_STATES = [
|
|
35029
|
+
"STARTING",
|
|
35030
|
+
"BOOTSTRAPPING",
|
|
35031
|
+
"RUNNING",
|
|
35032
|
+
"WAITING",
|
|
35033
|
+
"TERMINATING"
|
|
35034
|
+
];
|
|
35035
|
+
/**
|
|
35023
35036
|
* Top-level CFn properties that map to a MUTABLE EMR API surface. Every other
|
|
35024
35037
|
* property is either registry-createOnly (routed through DELETE+CREATE by the
|
|
35025
35038
|
* replacement-detection layer) or lives inside the `Instances` block (handled
|
|
@@ -35519,6 +35532,279 @@ var EMRClusterProvider = class {
|
|
|
35519
35532
|
default: return;
|
|
35520
35533
|
}
|
|
35521
35534
|
}
|
|
35535
|
+
/**
|
|
35536
|
+
* Adopt an existing EMR cluster into cdkd state.
|
|
35537
|
+
*
|
|
35538
|
+
* Lookup order:
|
|
35539
|
+
* 1. `--resource <logicalId>=j-XXXX` override (`knownPhysicalId`) → verify
|
|
35540
|
+
* via `DescribeCluster`. There is no template name property that equals
|
|
35541
|
+
* the physical id — a cluster's id (`j-...`) is service-generated, while
|
|
35542
|
+
* the template `Name` is only the display name — so no name fallback
|
|
35543
|
+
* applies (`resolveExplicitPhysicalId(..., null)`).
|
|
35544
|
+
* 2. Tag-based lookup: `ListClusters` filtered to the non-terminated states
|
|
35545
|
+
* (a `TERMINATED*` cluster is gone and must never be adopted), then a
|
|
35546
|
+
* `DescribeCluster` per candidate to read `Tags` (the list summaries do
|
|
35547
|
+
* NOT carry tags) and match `aws:cdk:path`.
|
|
35548
|
+
*/
|
|
35549
|
+
async import(input) {
|
|
35550
|
+
const explicit = resolveExplicitPhysicalId(input, null);
|
|
35551
|
+
if (explicit) {
|
|
35552
|
+
const cluster = await this.describeClusterOrNull(explicit);
|
|
35553
|
+
if (!cluster || cluster.Status?.State && TERMINAL_STATES.has(cluster.Status.State)) return null;
|
|
35554
|
+
return {
|
|
35555
|
+
physicalId: explicit,
|
|
35556
|
+
attributes: this.buildAttributes(cluster)
|
|
35557
|
+
};
|
|
35558
|
+
}
|
|
35559
|
+
if (!input.cdkPath) return null;
|
|
35560
|
+
let marker;
|
|
35561
|
+
do {
|
|
35562
|
+
const list = await this.getClient().send(new ListClustersCommand$1({
|
|
35563
|
+
ClusterStates: [...NON_TERMINATED_STATES],
|
|
35564
|
+
...marker && { Marker: marker }
|
|
35565
|
+
}));
|
|
35566
|
+
for (const summary of list.Clusters ?? []) {
|
|
35567
|
+
if (!summary.Id) continue;
|
|
35568
|
+
const cluster = await this.describeClusterOrNull(summary.Id);
|
|
35569
|
+
if (!cluster) continue;
|
|
35570
|
+
if (matchesCdkPath(cluster.Tags, input.cdkPath)) return {
|
|
35571
|
+
physicalId: summary.Id,
|
|
35572
|
+
attributes: this.buildAttributes(cluster)
|
|
35573
|
+
};
|
|
35574
|
+
}
|
|
35575
|
+
marker = list.Marker;
|
|
35576
|
+
} while (marker);
|
|
35577
|
+
return null;
|
|
35578
|
+
}
|
|
35579
|
+
/**
|
|
35580
|
+
* `DescribeCluster` that maps a not-found (`InvalidRequestException` — the
|
|
35581
|
+
* cluster id is unknown in this region / aged out of Describe) to `null`
|
|
35582
|
+
* instead of throwing, so `import()` can treat it as "no match" rather than
|
|
35583
|
+
* aborting the whole adoption run.
|
|
35584
|
+
*/
|
|
35585
|
+
async describeClusterOrNull(clusterId) {
|
|
35586
|
+
try {
|
|
35587
|
+
return (await this.getClient().send(new DescribeClusterCommand({ ClusterId: clusterId }))).Cluster;
|
|
35588
|
+
} catch (err) {
|
|
35589
|
+
if (err instanceof InvalidRequestException) return void 0;
|
|
35590
|
+
throw err;
|
|
35591
|
+
}
|
|
35592
|
+
}
|
|
35593
|
+
/**
|
|
35594
|
+
* Read the currently-deployed properties for `cdkd drift` and to seed the
|
|
35595
|
+
* `observedProperties` baseline right after `cdkd import`.
|
|
35596
|
+
*
|
|
35597
|
+
* The bulk of the work is reversing the flatten that `create()` applies to
|
|
35598
|
+
* the CFn `Instances` block: `DescribeCluster` reports the cluster's
|
|
35599
|
+
* `InstanceCollectionType`, and the instance groups / fleets themselves come
|
|
35600
|
+
* from `ListInstanceGroups` / `ListInstanceFleets` as FLAT arrays with a
|
|
35601
|
+
* per-entry `InstanceGroupType` / `InstanceFleetType` discriminator — the
|
|
35602
|
+
* exact inverse of the role-keyed CFn `MasterInstanceGroup` /
|
|
35603
|
+
* `CoreInstanceGroup` / `TaskInstanceGroups` (+ `*InstanceFleet(s)`) shape.
|
|
35604
|
+
* `reverseInstancesToCfn` re-buckets them and folds in the flat
|
|
35605
|
+
* `Ec2InstanceAttributes` (subnet / key name / security groups).
|
|
35606
|
+
*
|
|
35607
|
+
* Scope: the reversible property set — the reverse-mapped `Instances` block,
|
|
35608
|
+
* `Tags` (via `normalizeAwsTagsToCfn`, `aws:*` stripped), and the top-level
|
|
35609
|
+
* scalar fields `DescribeCluster` returns directly. Create-only sub-config
|
|
35610
|
+
* that AWS does not read back faithfully (`ManagedScalingPolicy` /
|
|
35611
|
+
* `AutoTerminationPolicy` — separate Get* APIs; `BootstrapActions` / `Steps`
|
|
35612
|
+
* / `KerberosAttributes` / `Configurations` nesting / `AdditionalInfo` /
|
|
35613
|
+
* `PlacementGroupConfigs`; `Instances` EBS / auto-scaling sub-specs) is
|
|
35614
|
+
* declared in `getDriftUnknownPaths` so the drift comparator skips it on the
|
|
35615
|
+
* `properties`-fallback path instead of firing a guaranteed false positive.
|
|
35616
|
+
* On the normal path the baseline is `observedProperties` (captured via this
|
|
35617
|
+
* same method), so observed == current and there is no phantom drift.
|
|
35618
|
+
*
|
|
35619
|
+
* Returns `undefined` when the cluster is gone (`InvalidRequestException`)
|
|
35620
|
+
* so the caller reports drift-unknown rather than throwing — mirrors the
|
|
35621
|
+
* optional `import` method's incremental opt-in shape.
|
|
35622
|
+
*/
|
|
35623
|
+
async readCurrentState(physicalId, _logicalId, _resourceType) {
|
|
35624
|
+
const cluster = await this.describeClusterOrNull(physicalId);
|
|
35625
|
+
if (!cluster) return void 0;
|
|
35626
|
+
let instanceGroups = [];
|
|
35627
|
+
let instanceFleets = [];
|
|
35628
|
+
if (cluster.InstanceCollectionType === "INSTANCE_FLEET") instanceFleets = await this.listInstanceFleets(physicalId);
|
|
35629
|
+
else instanceGroups = await this.listInstanceGroups(physicalId);
|
|
35630
|
+
return {
|
|
35631
|
+
Name: cluster.Name ?? "",
|
|
35632
|
+
ReleaseLabel: cluster.ReleaseLabel ?? "",
|
|
35633
|
+
ServiceRole: cluster.ServiceRole ?? "",
|
|
35634
|
+
LogUri: cluster.LogUri ?? "",
|
|
35635
|
+
LogEncryptionKmsKeyId: cluster.LogEncryptionKmsKeyId ?? "",
|
|
35636
|
+
AutoScalingRole: cluster.AutoScalingRole ?? "",
|
|
35637
|
+
ScaleDownBehavior: cluster.ScaleDownBehavior ?? "",
|
|
35638
|
+
CustomAmiId: cluster.CustomAmiId ?? "",
|
|
35639
|
+
OSReleaseLabel: cluster.OSReleaseLabel ?? "",
|
|
35640
|
+
SecurityConfiguration: cluster.SecurityConfiguration ?? "",
|
|
35641
|
+
EbsRootVolumeSize: cluster.EbsRootVolumeSize,
|
|
35642
|
+
EbsRootVolumeIops: cluster.EbsRootVolumeIops,
|
|
35643
|
+
EbsRootVolumeThroughput: cluster.EbsRootVolumeThroughput,
|
|
35644
|
+
StepConcurrencyLevel: cluster.StepConcurrencyLevel,
|
|
35645
|
+
VisibleToAllUsers: cluster.VisibleToAllUsers ?? false,
|
|
35646
|
+
JobFlowRole: cluster.Ec2InstanceAttributes?.IamInstanceProfile ?? "",
|
|
35647
|
+
Applications: cluster.Applications ?? [],
|
|
35648
|
+
Tags: normalizeAwsTagsToCfn(cluster.Tags),
|
|
35649
|
+
Instances: this.reverseInstancesToCfn(cluster, instanceGroups, instanceFleets)
|
|
35650
|
+
};
|
|
35651
|
+
}
|
|
35652
|
+
/**
|
|
35653
|
+
* State property paths this provider cannot read back from AWS faithfully,
|
|
35654
|
+
* skipped by the drift comparator to avoid a guaranteed false positive on
|
|
35655
|
+
* the `properties`-fallback path. See `readCurrentState`'s docstring.
|
|
35656
|
+
*
|
|
35657
|
+
* NOTE on the `Instances.*` entries: `reverseInstancesToCfn` deliberately
|
|
35658
|
+
* reconstructs only the primary topology per group / fleet (role, type,
|
|
35659
|
+
* count / capacity, market, bid price, name, custom AMI) — the lossy
|
|
35660
|
+
* sub-specs `create()` reads (`EbsConfiguration` / `AutoScalingPolicy` /
|
|
35661
|
+
* per-group `Configurations`; per-fleet `InstanceTypeConfigs` /
|
|
35662
|
+
* `LaunchSpecifications` / `ResizeSpecifications`) and the top-level
|
|
35663
|
+
* `Instances` fields AWS does not report back (`HadoopVersion` /
|
|
35664
|
+
* `Placement` / `KeepJobFlowAliveWhenNoSteps`) are NOT reconstructed. On the
|
|
35665
|
+
* NORMAL drift path both the `observedProperties` baseline and the current
|
|
35666
|
+
* snapshot go through this same lossy reverse, so these paths are absent
|
|
35667
|
+
* from both sides and never drift — ignoring them there is a no-op. On the
|
|
35668
|
+
* `properties`-fallback path (no `observedProperties`) the baseline is the
|
|
35669
|
+
* full template `Instances`, which DOES carry these sub-fields, so without
|
|
35670
|
+
* the skip every one would fire guaranteed false-positive drift. The two
|
|
35671
|
+
* `Task*` arrays are ignored WHOLE (the comparator compares arrays as leaves,
|
|
35672
|
+
* so an element sub-field cannot be path-targeted); Master/Core groups and
|
|
35673
|
+
* fleets are single objects, so their lossy sub-fields are targeted directly.
|
|
35674
|
+
*/
|
|
35675
|
+
getDriftUnknownPaths(_resourceType) {
|
|
35676
|
+
return [
|
|
35677
|
+
"AdditionalInfo",
|
|
35678
|
+
"Applications",
|
|
35679
|
+
"AutoTerminationPolicy",
|
|
35680
|
+
"BootstrapActions",
|
|
35681
|
+
"Configurations",
|
|
35682
|
+
"Instances.HadoopVersion",
|
|
35683
|
+
"Instances.KeepJobFlowAliveWhenNoSteps",
|
|
35684
|
+
"Instances.Placement",
|
|
35685
|
+
"Instances.CoreInstanceGroup.AutoScalingPolicy",
|
|
35686
|
+
"Instances.CoreInstanceGroup.Configurations",
|
|
35687
|
+
"Instances.CoreInstanceGroup.EbsConfiguration",
|
|
35688
|
+
"Instances.MasterInstanceGroup.AutoScalingPolicy",
|
|
35689
|
+
"Instances.MasterInstanceGroup.Configurations",
|
|
35690
|
+
"Instances.MasterInstanceGroup.EbsConfiguration",
|
|
35691
|
+
"Instances.CoreInstanceFleet.InstanceTypeConfigs",
|
|
35692
|
+
"Instances.CoreInstanceFleet.LaunchSpecifications",
|
|
35693
|
+
"Instances.CoreInstanceFleet.ResizeSpecifications",
|
|
35694
|
+
"Instances.MasterInstanceFleet.InstanceTypeConfigs",
|
|
35695
|
+
"Instances.MasterInstanceFleet.LaunchSpecifications",
|
|
35696
|
+
"Instances.MasterInstanceFleet.ResizeSpecifications",
|
|
35697
|
+
"Instances.TaskInstanceFleets",
|
|
35698
|
+
"Instances.TaskInstanceGroups",
|
|
35699
|
+
"KerberosAttributes",
|
|
35700
|
+
"ManagedScalingPolicy",
|
|
35701
|
+
"PlacementGroupConfigs",
|
|
35702
|
+
"Steps"
|
|
35703
|
+
];
|
|
35704
|
+
}
|
|
35705
|
+
async listInstanceGroups(clusterId) {
|
|
35706
|
+
const groups = [];
|
|
35707
|
+
let marker;
|
|
35708
|
+
do {
|
|
35709
|
+
const resp = await this.getClient().send(new ListInstanceGroupsCommand({
|
|
35710
|
+
ClusterId: clusterId,
|
|
35711
|
+
...marker && { Marker: marker }
|
|
35712
|
+
}));
|
|
35713
|
+
groups.push(...resp.InstanceGroups ?? []);
|
|
35714
|
+
marker = resp.Marker;
|
|
35715
|
+
} while (marker);
|
|
35716
|
+
return groups;
|
|
35717
|
+
}
|
|
35718
|
+
async listInstanceFleets(clusterId) {
|
|
35719
|
+
const fleets = [];
|
|
35720
|
+
let marker;
|
|
35721
|
+
do {
|
|
35722
|
+
const resp = await this.getClient().send(new ListInstanceFleetsCommand({
|
|
35723
|
+
ClusterId: clusterId,
|
|
35724
|
+
...marker && { Marker: marker }
|
|
35725
|
+
}));
|
|
35726
|
+
fleets.push(...resp.InstanceFleets ?? []);
|
|
35727
|
+
marker = resp.Marker;
|
|
35728
|
+
} while (marker);
|
|
35729
|
+
return fleets;
|
|
35730
|
+
}
|
|
35731
|
+
/**
|
|
35732
|
+
* Reverse of `toJobFlowInstancesConfig` — re-bucket the flat SDK
|
|
35733
|
+
* `InstanceGroup[]` / `InstanceFleet[]` (each carrying a role discriminator)
|
|
35734
|
+
* back into the role-keyed CFn `Instances` shape, and fold in the flat
|
|
35735
|
+
* `Ec2InstanceAttributes` (subnet / key name / security groups).
|
|
35736
|
+
*
|
|
35737
|
+
* Only the primary topology fields per group / fleet are reversed (role,
|
|
35738
|
+
* type, count / capacity, market, bid price, name, custom AMI). Lossy
|
|
35739
|
+
* sub-specs (EBS block-device / auto-scaling for groups; per-instance-type
|
|
35740
|
+
* specs for fleets) are intentionally NOT reconstructed — they are covered
|
|
35741
|
+
* by the drift baseline being `observedProperties` on the normal path.
|
|
35742
|
+
*/
|
|
35743
|
+
reverseInstancesToCfn(cluster, instanceGroups, instanceFleets) {
|
|
35744
|
+
const ec2 = cluster.Ec2InstanceAttributes;
|
|
35745
|
+
const instances = {};
|
|
35746
|
+
const taskGroups = [];
|
|
35747
|
+
for (const g of instanceGroups) {
|
|
35748
|
+
const cfn = this.reverseInstanceGroup(g);
|
|
35749
|
+
switch (g.InstanceGroupType) {
|
|
35750
|
+
case "MASTER":
|
|
35751
|
+
instances["MasterInstanceGroup"] = cfn;
|
|
35752
|
+
break;
|
|
35753
|
+
case "CORE":
|
|
35754
|
+
instances["CoreInstanceGroup"] = cfn;
|
|
35755
|
+
break;
|
|
35756
|
+
case "TASK":
|
|
35757
|
+
taskGroups.push(cfn);
|
|
35758
|
+
break;
|
|
35759
|
+
}
|
|
35760
|
+
}
|
|
35761
|
+
if (taskGroups.length > 0) instances["TaskInstanceGroups"] = taskGroups;
|
|
35762
|
+
const taskFleets = [];
|
|
35763
|
+
for (const f of instanceFleets) {
|
|
35764
|
+
const cfn = this.reverseInstanceFleet(f);
|
|
35765
|
+
switch (f.InstanceFleetType) {
|
|
35766
|
+
case "MASTER":
|
|
35767
|
+
instances["MasterInstanceFleet"] = cfn;
|
|
35768
|
+
break;
|
|
35769
|
+
case "CORE":
|
|
35770
|
+
instances["CoreInstanceFleet"] = cfn;
|
|
35771
|
+
break;
|
|
35772
|
+
case "TASK":
|
|
35773
|
+
taskFleets.push(cfn);
|
|
35774
|
+
break;
|
|
35775
|
+
}
|
|
35776
|
+
}
|
|
35777
|
+
if (taskFleets.length > 0) instances["TaskInstanceFleets"] = taskFleets;
|
|
35778
|
+
if (ec2?.Ec2KeyName !== void 0) instances["Ec2KeyName"] = ec2.Ec2KeyName;
|
|
35779
|
+
if (ec2?.Ec2SubnetId !== void 0) instances["Ec2SubnetId"] = ec2.Ec2SubnetId;
|
|
35780
|
+
if (ec2?.RequestedEc2SubnetIds !== void 0) instances["Ec2SubnetIds"] = ec2.RequestedEc2SubnetIds;
|
|
35781
|
+
if (ec2?.EmrManagedMasterSecurityGroup !== void 0) instances["EmrManagedMasterSecurityGroup"] = ec2.EmrManagedMasterSecurityGroup;
|
|
35782
|
+
if (ec2?.EmrManagedSlaveSecurityGroup !== void 0) instances["EmrManagedSlaveSecurityGroup"] = ec2.EmrManagedSlaveSecurityGroup;
|
|
35783
|
+
if (ec2?.ServiceAccessSecurityGroup !== void 0) instances["ServiceAccessSecurityGroup"] = ec2.ServiceAccessSecurityGroup;
|
|
35784
|
+
if (ec2?.AdditionalMasterSecurityGroups !== void 0) instances["AdditionalMasterSecurityGroups"] = ec2.AdditionalMasterSecurityGroups;
|
|
35785
|
+
if (ec2?.AdditionalSlaveSecurityGroups !== void 0) instances["AdditionalSlaveSecurityGroups"] = ec2.AdditionalSlaveSecurityGroups;
|
|
35786
|
+
if (cluster.TerminationProtected !== void 0) instances["TerminationProtected"] = cluster.TerminationProtected;
|
|
35787
|
+
if (cluster.UnhealthyNodeReplacement !== void 0) instances["UnhealthyNodeReplacement"] = cluster.UnhealthyNodeReplacement;
|
|
35788
|
+
return instances;
|
|
35789
|
+
}
|
|
35790
|
+
reverseInstanceGroup(g) {
|
|
35791
|
+
const out = {
|
|
35792
|
+
InstanceType: g.InstanceType ?? "",
|
|
35793
|
+
InstanceCount: g.RequestedInstanceCount ?? 0
|
|
35794
|
+
};
|
|
35795
|
+
if (g.Name !== void 0) out["Name"] = g.Name;
|
|
35796
|
+
if (g.Market !== void 0) out["Market"] = g.Market;
|
|
35797
|
+
if (g.BidPrice !== void 0) out["BidPrice"] = g.BidPrice;
|
|
35798
|
+
if (g.CustomAmiId !== void 0) out["CustomAmiId"] = g.CustomAmiId;
|
|
35799
|
+
return out;
|
|
35800
|
+
}
|
|
35801
|
+
reverseInstanceFleet(f) {
|
|
35802
|
+
const out = {};
|
|
35803
|
+
if (f.Name !== void 0) out["Name"] = f.Name;
|
|
35804
|
+
if (f.TargetOnDemandCapacity !== void 0) out["TargetOnDemandCapacity"] = f.TargetOnDemandCapacity;
|
|
35805
|
+
if (f.TargetSpotCapacity !== void 0) out["TargetSpotCapacity"] = f.TargetSpotCapacity;
|
|
35806
|
+
return out;
|
|
35807
|
+
}
|
|
35522
35808
|
};
|
|
35523
35809
|
|
|
35524
35810
|
//#endregion
|
|
@@ -37608,6 +37894,54 @@ function tagMapsEqual(a, b) {
|
|
|
37608
37894
|
if (aKeys.length !== Object.keys(b).length) return false;
|
|
37609
37895
|
return aKeys.every((k) => b[k] === a[k]);
|
|
37610
37896
|
}
|
|
37897
|
+
/** Default branch for the `Code` seed commit when `BranchName` is omitted. */
|
|
37898
|
+
const DEFAULT_SEED_BRANCH = "main";
|
|
37899
|
+
/**
|
|
37900
|
+
* Coerce a CFn scalar value to a string. Post-intrinsic-resolution values are
|
|
37901
|
+
* strings in practice; numbers / booleans are stringified and any other shape
|
|
37902
|
+
* (object / null / undefined) collapses to `''` — avoids stringifying an
|
|
37903
|
+
* object to the useless `'[object Object]'`.
|
|
37904
|
+
*/
|
|
37905
|
+
function scalarToString(value) {
|
|
37906
|
+
if (typeof value === "string") return value;
|
|
37907
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
37908
|
+
return "";
|
|
37909
|
+
}
|
|
37910
|
+
/**
|
|
37911
|
+
* Convert the CFn `Triggers` list shape to the CodeCommit SDK's
|
|
37912
|
+
* `RepositoryTrigger[]` (PascalCase → camelCase). `Branches` is ALWAYS
|
|
37913
|
+
* emitted (defaulting to `[]` when the template omits it) — CodeCommit's
|
|
37914
|
+
* `PutRepositoryTriggers` rejects a trigger whose `branches` is null with
|
|
37915
|
+
* "Repository trigger branch name list cannot be null", and an empty array
|
|
37916
|
+
* means "all branches" (matching CFn's default when `Branches` is absent).
|
|
37917
|
+
* `CustomData` is truly optional (emitted only when present) so a re-order /
|
|
37918
|
+
* equality comparison stays stable. `Events` / `Branches` are coerced to
|
|
37919
|
+
* string arrays.
|
|
37920
|
+
*/
|
|
37921
|
+
function toSdkTriggers(triggers) {
|
|
37922
|
+
if (!Array.isArray(triggers) || triggers.length === 0) return [];
|
|
37923
|
+
return triggers.map((t) => {
|
|
37924
|
+
const events = Array.isArray(t?.Events) ? t.Events : [];
|
|
37925
|
+
const branches = Array.isArray(t?.Branches) ? t.Branches : [];
|
|
37926
|
+
const trigger = {
|
|
37927
|
+
name: scalarToString(t?.Name),
|
|
37928
|
+
destinationArn: scalarToString(t?.DestinationArn),
|
|
37929
|
+
events: events.map((e) => scalarToString(e)),
|
|
37930
|
+
branches: branches.map((b) => scalarToString(b))
|
|
37931
|
+
};
|
|
37932
|
+
if (t?.CustomData !== void 0 && t.CustomData !== null) trigger.customData = scalarToString(t.CustomData);
|
|
37933
|
+
return trigger;
|
|
37934
|
+
});
|
|
37935
|
+
}
|
|
37936
|
+
/**
|
|
37937
|
+
* Order-sensitive structural equality for two mapped SDK trigger lists, used
|
|
37938
|
+
* to skip a redundant `PutRepositoryTriggers` when the template `Triggers`
|
|
37939
|
+
* block is unchanged. Compared as canonical JSON so `undefined` optional
|
|
37940
|
+
* fields (`customData` / `branches`) collapse identically on both sides.
|
|
37941
|
+
*/
|
|
37942
|
+
function triggersEqual(a, b) {
|
|
37943
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
37944
|
+
}
|
|
37611
37945
|
/**
|
|
37612
37946
|
* AWS CodeCommit Repository Provider
|
|
37613
37947
|
*
|
|
@@ -37626,19 +37960,25 @@ function tagMapsEqual(a, b) {
|
|
|
37626
37960
|
*/
|
|
37627
37961
|
var CodeCommitRepositoryProvider = class {
|
|
37628
37962
|
client;
|
|
37963
|
+
s3Client;
|
|
37629
37964
|
providerRegion = process.env["AWS_REGION"];
|
|
37630
37965
|
logger = getLogger().child("CodeCommitRepositoryProvider");
|
|
37631
37966
|
handledProperties = new Map([["AWS::CodeCommit::Repository", new Set([
|
|
37632
37967
|
"RepositoryName",
|
|
37633
37968
|
"RepositoryDescription",
|
|
37634
37969
|
"KmsKeyId",
|
|
37635
|
-
"Tags"
|
|
37970
|
+
"Tags",
|
|
37971
|
+
"Code",
|
|
37972
|
+
"Triggers"
|
|
37636
37973
|
])]]);
|
|
37637
|
-
unhandledByDesign = new Map([["AWS::CodeCommit::Repository", new Map([["Code", "CFn-only seed-content orchestration (S3 zip unpacked into an initial commit on a chosen branch); not wired in v1 — pre-flight rejects templates carrying it so nothing is silently dropped; a follow-up could implement it via CreateCommit/PutFile"], ["Triggers", "repository trigger management not wired in v1 — pre-flight rejects templates carrying it so nothing is silently dropped; a follow-up could implement it via PutRepositoryTriggers"]])]]);
|
|
37638
37974
|
getClient() {
|
|
37639
37975
|
if (!this.client) this.client = new CodeCommitClient(this.providerRegion ? { region: this.providerRegion } : {});
|
|
37640
37976
|
return this.client;
|
|
37641
37977
|
}
|
|
37978
|
+
getS3Client() {
|
|
37979
|
+
if (!this.s3Client) this.s3Client = new S3Client(this.providerRegion ? { region: this.providerRegion } : {});
|
|
37980
|
+
return this.s3Client;
|
|
37981
|
+
}
|
|
37642
37982
|
/**
|
|
37643
37983
|
* Build the `Fn::GetAtt` attribute map from a `RepositoryMetadata`
|
|
37644
37984
|
* response. `RepositoryId` is additionally stored so the intrinsic
|
|
@@ -37671,9 +38011,26 @@ var CodeCommitRepositoryProvider = class {
|
|
|
37671
38011
|
...tags ? { tags } : {}
|
|
37672
38012
|
}))).repositoryMetadata;
|
|
37673
38013
|
if (!metadata?.repositoryName) throw new Error("CreateRepository did not return repository metadata");
|
|
37674
|
-
|
|
38014
|
+
const createdName = metadata.repositoryName;
|
|
38015
|
+
try {
|
|
38016
|
+
const code = properties["Code"];
|
|
38017
|
+
if (code) await this.seedInitialCommit(createdName, code);
|
|
38018
|
+
const triggers = properties["Triggers"];
|
|
38019
|
+
if (Array.isArray(triggers) && triggers.length > 0) {
|
|
38020
|
+
await this.getClient().send(new PutRepositoryTriggersCommand({
|
|
38021
|
+
repositoryName: createdName,
|
|
38022
|
+
triggers: toSdkTriggers(triggers)
|
|
38023
|
+
}));
|
|
38024
|
+
this.logger.debug(`Applied ${triggers.length} trigger(s) to ${createdName}`);
|
|
38025
|
+
}
|
|
38026
|
+
} catch (postCreateError) {
|
|
38027
|
+
this.logger.warn(`Post-create step failed for CodeCommit Repository ${logicalId}; deleting the just-created repository ${createdName} to avoid an orphan`);
|
|
38028
|
+
await this.bestEffortDelete(createdName);
|
|
38029
|
+
throw postCreateError;
|
|
38030
|
+
}
|
|
38031
|
+
this.logger.debug(`Successfully created CodeCommit Repository ${logicalId}: ${createdName}`);
|
|
37675
38032
|
return {
|
|
37676
|
-
physicalId:
|
|
38033
|
+
physicalId: createdName,
|
|
37677
38034
|
attributes: this.toAttributes(metadata)
|
|
37678
38035
|
};
|
|
37679
38036
|
} catch (error) {
|
|
@@ -37691,7 +38048,11 @@ var CodeCommitRepositoryProvider = class {
|
|
|
37691
38048
|
* CFn's `Ref` value — survives the rename), RepositoryDescription
|
|
37692
38049
|
* (UpdateRepositoryDescription), KmsKeyId (UpdateRepositoryEncryptionKey),
|
|
37693
38050
|
* Tags (TagResource / UntagResource — full tag removal handled explicitly,
|
|
37694
|
-
* see the ECR Tags regression class in issue #981)
|
|
38051
|
+
* see the ECR Tags regression class in issue #981), Triggers
|
|
38052
|
+
* (PutRepositoryTriggers — a full-set replace, so a dropped or fully-removed
|
|
38053
|
+
* `Triggers` property is applied by putting the new/empty set; issue #1066).
|
|
38054
|
+
* `Code` is create-only seed content (CFn ignores it on update) and is NOT
|
|
38055
|
+
* re-applied here.
|
|
37695
38056
|
*
|
|
37696
38057
|
* A rename returns the NEW repository name as `physicalId` with
|
|
37697
38058
|
* `wasReplaced: false`; the deploy engine persists the returned physical
|
|
@@ -37753,6 +38114,14 @@ var CodeCommitRepositoryProvider = class {
|
|
|
37753
38114
|
this.logger.debug(`Updated tags for ${currentName}`);
|
|
37754
38115
|
} else this.logger.warn(`Could not resolve ARN for CodeCommit Repository ${currentName}; tag update skipped`);
|
|
37755
38116
|
}
|
|
38117
|
+
const newSdkTriggers = toSdkTriggers(properties["Triggers"]);
|
|
38118
|
+
if (!triggersEqual(newSdkTriggers, toSdkTriggers(previousProperties["Triggers"]))) {
|
|
38119
|
+
await this.getClient().send(new PutRepositoryTriggersCommand({
|
|
38120
|
+
repositoryName: currentName,
|
|
38121
|
+
triggers: newSdkTriggers
|
|
38122
|
+
}));
|
|
38123
|
+
this.logger.debug(`Updated triggers for ${currentName} (${newSdkTriggers.length} trigger(s))`);
|
|
38124
|
+
}
|
|
37756
38125
|
metadata = await this.getRepositoryMetadata(currentName);
|
|
37757
38126
|
return {
|
|
37758
38127
|
physicalId: currentName,
|
|
@@ -37882,12 +38251,15 @@ var CodeCommitRepositoryProvider = class {
|
|
|
37882
38251
|
* State property paths this provider cannot read back from AWS, skipped by
|
|
37883
38252
|
* the drift comparator to avoid a guaranteed false positive.
|
|
37884
38253
|
*
|
|
37885
|
-
* `Code`
|
|
37886
|
-
*
|
|
37887
|
-
*
|
|
37888
|
-
*
|
|
37889
|
-
*
|
|
37890
|
-
*
|
|
38254
|
+
* `Code` is create-only S3-zip seed content unpacked into the initial
|
|
38255
|
+
* commit — there is no read-back to compare against (the commit is git
|
|
38256
|
+
* history, not a repository attribute), so it can never meaningfully drift.
|
|
38257
|
+
* `Triggers` IS wired on the write side (create + update via
|
|
38258
|
+
* `PutRepositoryTriggers`), but `readCurrentState` does not yet fetch
|
|
38259
|
+
* `GetRepositoryTriggers`, so comparing a state that carries `Triggers`
|
|
38260
|
+
* against an `observedProperties` that omits it would be a guaranteed false
|
|
38261
|
+
* positive. Both are therefore excluded here; a follow-up can add
|
|
38262
|
+
* `GetRepositoryTriggers` read-back and drop `Triggers` from this list.
|
|
37891
38263
|
*/
|
|
37892
38264
|
getDriftUnknownPaths(_resourceType) {
|
|
37893
38265
|
return ["Code", "Triggers"];
|
|
@@ -37938,6 +38310,67 @@ var CodeCommitRepositoryProvider = class {
|
|
|
37938
38310
|
return null;
|
|
37939
38311
|
}
|
|
37940
38312
|
/**
|
|
38313
|
+
* Seed the repository's initial commit from the CFn `Code` property.
|
|
38314
|
+
*
|
|
38315
|
+
* CFn's `Code` orchestration downloads the S3 ZIP, unpacks it, and creates
|
|
38316
|
+
* the repository's first commit on `BranchName` (default `main`). cdkd
|
|
38317
|
+
* reproduces that here: `GetObject` the ZIP, unpack every file entry
|
|
38318
|
+
* (directories are implied by file paths — CodeCommit has no empty-dir
|
|
38319
|
+
* concept), and issue a single `CreateCommit` carrying all files as
|
|
38320
|
+
* `putFiles`. This is create-only: CFn ignores `Code` on update, and so
|
|
38321
|
+
* does cdkd (`update()` never calls this).
|
|
38322
|
+
*
|
|
38323
|
+
* A ZIP with no file entries is a no-op (warn + skip) rather than a hard
|
|
38324
|
+
* failure — CodeCommit rejects a `CreateCommit` with an empty `putFiles`.
|
|
38325
|
+
*/
|
|
38326
|
+
async seedInitialCommit(repositoryName, code) {
|
|
38327
|
+
const bucket = code.S3?.Bucket;
|
|
38328
|
+
const key = code.S3?.Key;
|
|
38329
|
+
if (typeof bucket !== "string" || typeof key !== "string" || !bucket || !key) throw new Error("Code.S3 requires string Bucket and Key");
|
|
38330
|
+
const versionId = typeof code.S3?.ObjectVersion === "string" ? code.S3.ObjectVersion : void 0;
|
|
38331
|
+
const branchName = typeof code.BranchName === "string" && code.BranchName ? code.BranchName : DEFAULT_SEED_BRANCH;
|
|
38332
|
+
const obj = await this.getS3Client().send(new GetObjectCommand({
|
|
38333
|
+
Bucket: bucket,
|
|
38334
|
+
Key: key,
|
|
38335
|
+
...versionId && { VersionId: versionId }
|
|
38336
|
+
}));
|
|
38337
|
+
if (!obj.Body) throw new Error(`Code.S3 object s3://${bucket}/${key} returned an empty body`);
|
|
38338
|
+
const zipBytes = await obj.Body.transformToByteArray();
|
|
38339
|
+
const zip = new AdmZip(Buffer.from(zipBytes));
|
|
38340
|
+
const putFiles = [];
|
|
38341
|
+
for (const entry of zip.getEntries()) {
|
|
38342
|
+
if (entry.isDirectory) continue;
|
|
38343
|
+
putFiles.push({
|
|
38344
|
+
filePath: entry.entryName,
|
|
38345
|
+
fileContent: entry.getData()
|
|
38346
|
+
});
|
|
38347
|
+
}
|
|
38348
|
+
if (putFiles.length === 0) {
|
|
38349
|
+
this.logger.warn(`Code.S3 object s3://${bucket}/${key} contained no files; skipping seed commit for ${repositoryName}`);
|
|
38350
|
+
return;
|
|
38351
|
+
}
|
|
38352
|
+
await this.getClient().send(new CreateCommitCommand({
|
|
38353
|
+
repositoryName,
|
|
38354
|
+
branchName,
|
|
38355
|
+
commitMessage: "Initial commit",
|
|
38356
|
+
putFiles
|
|
38357
|
+
}));
|
|
38358
|
+
this.logger.debug(`Seeded ${repositoryName} with ${putFiles.length} file(s) on branch ${branchName}`);
|
|
38359
|
+
}
|
|
38360
|
+
/**
|
|
38361
|
+
* Best-effort delete used to roll back a just-created repository when a
|
|
38362
|
+
* post-create step (`Code` seed / `Triggers`) fails. Never throws — the
|
|
38363
|
+
* original post-create error is what the caller re-throws; a cleanup
|
|
38364
|
+
* failure is logged so the orphan is surfaced.
|
|
38365
|
+
*/
|
|
38366
|
+
async bestEffortDelete(repositoryName) {
|
|
38367
|
+
try {
|
|
38368
|
+
await this.getClient().send(new DeleteRepositoryCommand$1({ repositoryName }));
|
|
38369
|
+
} catch (cleanupError) {
|
|
38370
|
+
this.logger.warn(`Failed to clean up CodeCommit Repository ${repositoryName} after a post-create failure: ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}`);
|
|
38371
|
+
}
|
|
38372
|
+
}
|
|
38373
|
+
/**
|
|
37941
38374
|
* Fetch the repository's metadata via `GetRepository`. Throws
|
|
37942
38375
|
* `RepositoryDoesNotExistException` through to the caller.
|
|
37943
38376
|
*/
|
|
@@ -60264,7 +60697,7 @@ function reorderArgs(argv) {
|
|
|
60264
60697
|
async function main() {
|
|
60265
60698
|
installPipeCloseHandler();
|
|
60266
60699
|
const program = new Command();
|
|
60267
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
60700
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.248.0");
|
|
60268
60701
|
program.addCommand(createBootstrapCommand());
|
|
60269
60702
|
program.addCommand(createSynthCommand());
|
|
60270
60703
|
program.addCommand(createListCommand());
|