@go-to-k/cdkd 0.239.0 → 0.241.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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-BxXIBZoc.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";
|
|
@@ -55,15 +55,17 @@ import { CreatePrivateDnsNamespaceCommand, CreateServiceCommand as CreateService
|
|
|
55
55
|
import { AppSyncClient, CreateApiKeyCommand, CreateDataSourceCommand, CreateGraphqlApiCommand, CreateResolverCommand, DeleteApiKeyCommand, DeleteDataSourceCommand, DeleteGraphqlApiCommand, DeleteResolverCommand, GetDataSourceCommand, GetGraphqlApiCommand, GetIntrospectionSchemaCommand, GetResolverCommand, ListApiKeysCommand, ListGraphqlApisCommand, NotFoundException as NotFoundException$4, StartSchemaCreationCommand, TagResourceCommand as TagResourceCommand$13, UntagResourceCommand as UntagResourceCommand$13, UpdateApiKeyCommand, UpdateDataSourceCommand, UpdateGraphqlApiCommand, UpdateResolverCommand } from "@aws-sdk/client-appsync";
|
|
56
56
|
import { parse, print } from "graphql";
|
|
57
57
|
import { CrawlerRunningException, CreateConnectionCommand, CreateCrawlerCommand, CreateDatabaseCommand, CreateJobCommand, CreateSecurityConfigurationCommand, CreateTableCommand as CreateTableCommand$1, CreateTriggerCommand, CreateWorkflowCommand, DeleteConnectionCommand, DeleteCrawlerCommand, DeleteDatabaseCommand, DeleteJobCommand, DeleteSecurityConfigurationCommand, DeleteTableCommand as DeleteTableCommand$1, DeleteTriggerCommand, DeleteWorkflowCommand, EntityNotFoundException, GetConnectionCommand, GetCrawlerCommand, GetDatabaseCommand, GetDatabasesCommand, GetJobCommand, GetSecurityConfigurationCommand, GetSecurityConfigurationsCommand, GetTableCommand, GetTablesCommand, GetTagsCommand, GetTriggerCommand, GetWorkflowCommand, GlueClient, ListWorkflowsCommand, StartCrawlerScheduleCommand, StartTriggerCommand, StopCrawlerCommand, StopCrawlerScheduleCommand, StopTriggerCommand, UpdateConnectionCommand, UpdateCrawlerCommand, UpdateDatabaseCommand, UpdateJobCommand, UpdateTableCommand as UpdateTableCommand$1, UpdateTriggerCommand, UpdateWorkflowCommand } from "@aws-sdk/client-glue";
|
|
58
|
-
import {
|
|
58
|
+
import { BudgetsClient, CreateBudgetCommand, CreateNotificationCommand, CreateSubscriberCommand, DeleteBudgetCommand, DeleteNotificationCommand, DeleteSubscriberCommand, DescribeBudgetCommand, DescribeBudgetsCommand, DuplicateRecordException, ListTagsForResourceCommand as ListTagsForResourceCommand$16, NotFoundException as NotFoundException$5, TagResourceCommand as TagResourceCommand$14, UntagResourceCommand as UntagResourceCommand$14, UpdateBudgetCommand } from "@aws-sdk/client-budgets";
|
|
59
|
+
import { AddTagsToStreamCommand, CreateStreamCommand, DecreaseStreamRetentionPeriodCommand, DeleteStreamCommand, DeregisterStreamConsumerCommand, DescribeStreamCommand, DescribeStreamConsumerCommand, DescribeStreamSummaryCommand, IncreaseStreamRetentionPeriodCommand, KinesisClient, ListStreamsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$17, ListTagsForStreamCommand, RegisterStreamConsumerCommand, RemoveTagsFromStreamCommand, ResourceNotFoundException as ResourceNotFoundException$8, StartStreamEncryptionCommand, StopStreamEncryptionCommand, TagResourceCommand as TagResourceCommand$15, UntagResourceCommand as UntagResourceCommand$15, UpdateShardCountCommand, UpdateStreamModeCommand } from "@aws-sdk/client-kinesis";
|
|
59
60
|
import { CreateScheduleCommand, DeleteScheduleCommand, GetScheduleCommand, ResourceNotFoundException as ResourceNotFoundException$9, SchedulerClient, UpdateScheduleCommand } from "@aws-sdk/client-scheduler";
|
|
60
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";
|
|
61
62
|
import { CreateDeliveryStreamCommand, DeleteDeliveryStreamCommand, DescribeDeliveryStreamCommand, FirehoseClient, ListDeliveryStreamsCommand, ListTagsForDeliveryStreamCommand, ResourceNotFoundException as ResourceNotFoundException$10, TagDeliveryStreamCommand, UntagDeliveryStreamCommand, UpdateDestinationCommand } from "@aws-sdk/client-firehose";
|
|
62
63
|
import { AddTagsCommand as AddTagsCommand$1, CloudTrailClient, CreateTrailCommand, DeleteTrailCommand, GetEventSelectorsCommand, GetInsightSelectorsCommand, GetTrailCommand, GetTrailStatusCommand, ListTagsCommand as ListTagsCommand$1, ListTrailsCommand, PutEventSelectorsCommand, PutInsightSelectorsCommand, RemoveTagsCommand as RemoveTagsCommand$1, StartLoggingCommand, StopLoggingCommand, TrailNotFoundException, UpdateTrailCommand } from "@aws-sdk/client-cloudtrail";
|
|
63
64
|
import { BatchGetProjectsCommand, CodeBuildClient, CreateProjectCommand, DeleteProjectCommand, ListProjectsCommand, ResourceNotFoundException as ResourceNotFoundException$11, UpdateProjectCommand } from "@aws-sdk/client-codebuild";
|
|
64
|
-
import {
|
|
65
|
-
import {
|
|
66
|
-
import {
|
|
65
|
+
import { CodeCommitClient, CreateRepositoryCommand as CreateRepositoryCommand$1, DeleteRepositoryCommand as DeleteRepositoryCommand$1, GetRepositoryCommand, ListRepositoriesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$18, RepositoryDoesNotExistException, TagResourceCommand as TagResourceCommand$16, UntagResourceCommand as UntagResourceCommand$16, UpdateRepositoryDescriptionCommand, UpdateRepositoryEncryptionKeyCommand, UpdateRepositoryNameCommand } from "@aws-sdk/client-codecommit";
|
|
66
|
+
import { CreateLifecyclePolicyCommand, DLMClient, DeleteLifecyclePolicyCommand as DeleteLifecyclePolicyCommand$1, GetLifecyclePoliciesCommand, GetLifecyclePolicyCommand as GetLifecyclePolicyCommand$1, ResourceNotFoundException as ResourceNotFoundException$12, TagResourceCommand as TagResourceCommand$17, UntagResourceCommand as UntagResourceCommand$17, UpdateLifecyclePolicyCommand } from "@aws-sdk/client-dlm";
|
|
67
|
+
import { CreateVectorBucketCommand, DeleteIndexCommand, DeleteVectorBucketCommand, GetVectorBucketCommand, ListIndexesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$19, ListVectorBucketsCommand, S3VectorsClient, TagResourceCommand as TagResourceCommand$18, UntagResourceCommand as UntagResourceCommand$18 } from "@aws-sdk/client-s3vectors";
|
|
68
|
+
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$20, NotFoundException as NotFoundException$6, S3TablesClient, TagResourceCommand as TagResourceCommand$19, UntagResourceCommand as UntagResourceCommand$19 } from "@aws-sdk/client-s3tables";
|
|
67
69
|
import { Document, Pair, Scalar, YAMLMap, YAMLSeq, parse as parse$1, stringify } from "yaml";
|
|
68
70
|
import { createLocalStartAgentCoreCommand, createLocalStartCloudFrontCommand, createLocalStateProvider, getEmbedConfig, isCfnFlagPresent, listTargets, rejectExplicitCfnStackWithMultipleStacks, resolveCfnFallbackRegion, setEmbedConfig, substituteAgainstState, substituteAgainstStateAsync, substituteEnvVarsFromState, substituteEnvVarsFromStateAsync } from "cdk-local";
|
|
69
71
|
import { A2A_CONTAINER_PORT, A2A_PATH, AGENTCORE_A2A_PROTOCOL, AGENTCORE_AGUI_PROTOCOL, AGENTCORE_MCP_PROTOCOL, ConnectionRegistry, EcsTaskResolutionError, HOST_GATEWAY_MIN_VERSION, LocalInvokeBuildError, MCP_CONTAINER_PORT, MCP_PATH, a2aInvokeOnce, addAlbSpecificOptions, addCommonEcsServiceOptions, addStartServiceSpecificOptions, albStrategy, architectureToPlatform, attachAuthorizers, attachStageContext, availableApiIdentifiers, bufferToBody, buildAgentCoreCodeImage, buildCognitoJwksUrl, buildConnectEvent, buildContainerImage, buildCorsConfigByApiId, buildCorsConfigFromCloudFrontChain, buildDisconnectEvent, buildJwksUrlFromIssuer, buildMessageEvent, buildMgmtEndpointEnvUrl, buildStageMap, classifySourceChange, createAuthorizerCache, createFileWatcher, createFileWatcher as createFileWatcher$1, createJwksCache, createWatchPredicates, defaultCredentialsLoader, derivePseudoParametersFromRegion, discoverRoutes, discoverWebSocketApis, downloadAndExtractS3Bundle, filterRoutesByApiIdentifier, groupRoutesByServer, handleConnectionsRequest, invokeAgentCore, invokeAgentCoreWs, materializeLayerFromArn, mcpInvokeOnce, parseConnectionsPath, parseSelectionExpressionPath, pickAgentCoreCandidateStack, pickAgentCoreCandidateStack as pickAgentCoreCandidateStack$1, probeHostGatewaySupport, readMtlsMaterialsFromDisk, resolveAgentCoreTarget, resolveEnvVars, resolveHostGatewayExtraHosts, resolveRuntimeCodeMountPath, resolveRuntimeFileExtension, resolveRuntimeImage, resolveSingleTarget, resolveWatchConfig, runEcsServiceEmulator, signAgentCoreInvocation, startApiServer, substituteImagePlaceholders, tryResolveImageFnJoin, verifyJwtViaDiscovery, waitForAgentCorePing } from "cdk-local/internal";
|
|
@@ -1886,7 +1888,7 @@ const FLUSH_INTERVAL_MS = 2e3;
|
|
|
1886
1888
|
const FLUSH_EVENT_THRESHOLD = 50;
|
|
1887
1889
|
/** Build-time cdkd version, with a dev fallback for non-built contexts. */
|
|
1888
1890
|
function getCdkdVersion() {
|
|
1889
|
-
return "0.
|
|
1891
|
+
return "0.241.0";
|
|
1890
1892
|
}
|
|
1891
1893
|
/**
|
|
1892
1894
|
* Generate a time-sortable unique run id, e.g.
|
|
@@ -31579,6 +31581,492 @@ var KMSProvider = class {
|
|
|
31579
31581
|
}
|
|
31580
31582
|
};
|
|
31581
31583
|
|
|
31584
|
+
//#endregion
|
|
31585
|
+
//#region src/provisioning/providers/budgets-budget-provider.ts
|
|
31586
|
+
/**
|
|
31587
|
+
* SDK Provider for AWS::Budgets::Budget (issue #1041).
|
|
31588
|
+
*
|
|
31589
|
+
* The type is `ProvisioningType: NON_PROVISIONABLE`, so the Cloud Control
|
|
31590
|
+
* fallback cannot handle it — without this provider cdkd's pre-flight
|
|
31591
|
+
* rejects the type outright.
|
|
31592
|
+
*
|
|
31593
|
+
* **Global endpoint / region semantics**: the Budgets API is a global,
|
|
31594
|
+
* per-account service served from `us-east-1`. The `@aws-sdk/client-budgets`
|
|
31595
|
+
* endpoint ruleset resolves EVERY aws-partition region to the single global
|
|
31596
|
+
* endpoint (`budgets.amazonaws.com`, SigV4 scope `us-east-1`), so the client
|
|
31597
|
+
* below is deliberately created with the deploy region like every other
|
|
31598
|
+
* provider — the SDK itself pins the endpoint. This also keeps
|
|
31599
|
+
* `client.config.region()` equal to the deploy region, so the standard
|
|
31600
|
+
* `assertRegionMatch` idempotent-delete guard behaves exactly like it does
|
|
31601
|
+
* for regional services: a destroy run with a mismatched `--region` still
|
|
31602
|
+
* refuses to trust a NotFound. (For a global namespace a NotFound would
|
|
31603
|
+
* actually be trustworthy from any region, but keeping the guard uniform is
|
|
31604
|
+
* strictly safer and costs nothing.)
|
|
31605
|
+
*
|
|
31606
|
+
* **AccountId**: every Budgets API call requires the account id. It is
|
|
31607
|
+
* resolved once per provider instance via STS `GetCallerIdentity` (shared
|
|
31608
|
+
* `AwsClients.sts`) and cached as a single-flight promise for the deploy
|
|
31609
|
+
* lifetime; failed resolutions are not cached so a transient STS throttle
|
|
31610
|
+
* cannot poison the rest of the run (mirrors
|
|
31611
|
+
* `src/utils/expected-bucket-owner.ts`).
|
|
31612
|
+
*
|
|
31613
|
+
* **Physical id**: the budget NAME. `Budget.BudgetName` is createOnly —
|
|
31614
|
+
* a rename is classified as replacement by the `AWS::Budgets::Budget`
|
|
31615
|
+
* conditional rule in `src/analyzer/replacement-rules.ts`.
|
|
31616
|
+
*
|
|
31617
|
+
* **NotificationsWithSubscribers**: CloudFormation treats the whole property
|
|
31618
|
+
* as createOnly (replacement on any change). cdkd does better — `update()`
|
|
31619
|
+
* reconciles in place via CreateNotification / DeleteNotification /
|
|
31620
|
+
* CreateSubscriber / DeleteSubscriber, so alert history and the budget
|
|
31621
|
+
* itself survive a notification edit.
|
|
31622
|
+
*/
|
|
31623
|
+
var BudgetsBudgetProvider = class {
|
|
31624
|
+
client;
|
|
31625
|
+
providerRegion = process.env["AWS_REGION"];
|
|
31626
|
+
logger = getLogger().child("BudgetsBudgetProvider");
|
|
31627
|
+
accountIdPromise;
|
|
31628
|
+
handledProperties = new Map([["AWS::Budgets::Budget", new Set([
|
|
31629
|
+
"Budget",
|
|
31630
|
+
"NotificationsWithSubscribers",
|
|
31631
|
+
"ResourceTags"
|
|
31632
|
+
])]]);
|
|
31633
|
+
getClient() {
|
|
31634
|
+
if (!this.client) this.client = new BudgetsClient(this.providerRegion ? { region: this.providerRegion } : {});
|
|
31635
|
+
return this.client;
|
|
31636
|
+
}
|
|
31637
|
+
/**
|
|
31638
|
+
* Resolve the caller's AWS account id (required on every Budgets call).
|
|
31639
|
+
* Cached per provider instance as a single-flight promise; a failed
|
|
31640
|
+
* resolution is evicted so the next call retries instead of replaying a
|
|
31641
|
+
* transient error for the rest of the deploy.
|
|
31642
|
+
*/
|
|
31643
|
+
resolveAccountId() {
|
|
31644
|
+
if (!this.accountIdPromise) this.accountIdPromise = (async () => {
|
|
31645
|
+
try {
|
|
31646
|
+
const identity = await getAwsClients().sts.send(new GetCallerIdentityCommand({}));
|
|
31647
|
+
if (!identity.Account) throw new Error("STS GetCallerIdentity returned no Account");
|
|
31648
|
+
return identity.Account;
|
|
31649
|
+
} catch (error) {
|
|
31650
|
+
this.accountIdPromise = void 0;
|
|
31651
|
+
throw error;
|
|
31652
|
+
}
|
|
31653
|
+
})();
|
|
31654
|
+
return this.accountIdPromise;
|
|
31655
|
+
}
|
|
31656
|
+
/**
|
|
31657
|
+
* Budget ARN for the tagging APIs (`ListTagsForResource` / `TagResource` /
|
|
31658
|
+
* `UntagResource`). Budgets ARNs carry no region component. The `aws`
|
|
31659
|
+
* partition is assumed — consistent with other providers that construct
|
|
31660
|
+
* ARNs; non-`aws` partitions are not supported by cdkd today.
|
|
31661
|
+
*/
|
|
31662
|
+
budgetArn(accountId, budgetName) {
|
|
31663
|
+
return `arn:aws:budgets::${accountId}:budget/${budgetName}`;
|
|
31664
|
+
}
|
|
31665
|
+
/**
|
|
31666
|
+
* Convert a CFn `Spend` shape to the SDK shape. CFn templates carry
|
|
31667
|
+
* `Amount` as a number (CDK synthesizes `budgetLimit: { amount: 10 }` to a
|
|
31668
|
+
* numeric JSON value); the SDK wants a numeric string.
|
|
31669
|
+
*/
|
|
31670
|
+
toSdkSpend(raw) {
|
|
31671
|
+
if (raw === void 0 || raw === null || typeof raw !== "object") return void 0;
|
|
31672
|
+
const spend = raw;
|
|
31673
|
+
const amount = spend["Amount"];
|
|
31674
|
+
return {
|
|
31675
|
+
Amount: typeof amount === "string" ? amount : typeof amount === "number" ? String(amount) : void 0,
|
|
31676
|
+
Unit: spend["Unit"]
|
|
31677
|
+
};
|
|
31678
|
+
}
|
|
31679
|
+
/**
|
|
31680
|
+
* Convert a CFn `TimePeriod` `Start` / `End` value to a `Date`. CFn accepts
|
|
31681
|
+
* a UTC date string (`2026-07-01T00:00:00Z`) or an epoch timestamp in
|
|
31682
|
+
* seconds (possibly as a numeric string).
|
|
31683
|
+
*/
|
|
31684
|
+
toSdkDate(raw, field, logicalId, resourceType) {
|
|
31685
|
+
let date;
|
|
31686
|
+
if (typeof raw === "number") date = new Date(raw < 0xe8d4a51000 ? raw * 1e3 : raw);
|
|
31687
|
+
else if (typeof raw === "string" && /^\d+$/.test(raw)) {
|
|
31688
|
+
const num = Number(raw);
|
|
31689
|
+
date = new Date(num < 0xe8d4a51000 ? num * 1e3 : num);
|
|
31690
|
+
} else if (typeof raw === "string") date = new Date(raw);
|
|
31691
|
+
else throw new ProvisioningError(`Invalid TimePeriod.${field} for budget ${logicalId}: expected a date string or epoch timestamp, got ${JSON.stringify(raw)}`, resourceType, logicalId);
|
|
31692
|
+
if (Number.isNaN(date.getTime())) throw new ProvisioningError(`Invalid TimePeriod.${field} for budget ${logicalId}: ${JSON.stringify(raw)} is not a parseable date`, resourceType, logicalId);
|
|
31693
|
+
return date;
|
|
31694
|
+
}
|
|
31695
|
+
/**
|
|
31696
|
+
* Map the CFn `Budget` (BudgetData) property to the SDK `Budget` shape.
|
|
31697
|
+
* The two are PascalCase-identical except: `BudgetLimit` /
|
|
31698
|
+
* `PlannedBudgetLimits` amounts are numeric strings on the wire, and
|
|
31699
|
+
* `TimePeriod.Start` / `.End` are `Date`s. `FilterExpression` / `Metrics` /
|
|
31700
|
+
* `CostFilters` / `CostTypes` / `AutoAdjustData` pass through verbatim.
|
|
31701
|
+
*/
|
|
31702
|
+
toSdkBudget(raw, budgetName, logicalId, resourceType) {
|
|
31703
|
+
if (raw === void 0 || raw === null || typeof raw !== "object") throw new ProvisioningError(`The Budget property is required for ${logicalId}`, resourceType, logicalId);
|
|
31704
|
+
const src = raw;
|
|
31705
|
+
const budget = {
|
|
31706
|
+
...src,
|
|
31707
|
+
BudgetName: budgetName
|
|
31708
|
+
};
|
|
31709
|
+
if (src["BudgetLimit"] !== void 0) budget["BudgetLimit"] = this.toSdkSpend(src["BudgetLimit"]);
|
|
31710
|
+
if (src["PlannedBudgetLimits"] !== void 0 && typeof src["PlannedBudgetLimits"] === "object") {
|
|
31711
|
+
const limits = {};
|
|
31712
|
+
for (const [key, value] of Object.entries(src["PlannedBudgetLimits"])) limits[key] = this.toSdkSpend(value);
|
|
31713
|
+
budget["PlannedBudgetLimits"] = limits;
|
|
31714
|
+
}
|
|
31715
|
+
if (src["TimePeriod"] !== void 0 && typeof src["TimePeriod"] === "object") {
|
|
31716
|
+
const rawPeriod = src["TimePeriod"];
|
|
31717
|
+
const period = {};
|
|
31718
|
+
if (rawPeriod["Start"] !== void 0) period.Start = this.toSdkDate(rawPeriod["Start"], "Start", logicalId, resourceType);
|
|
31719
|
+
if (rawPeriod["End"] !== void 0) period.End = this.toSdkDate(rawPeriod["End"], "End", logicalId, resourceType);
|
|
31720
|
+
budget["TimePeriod"] = period;
|
|
31721
|
+
}
|
|
31722
|
+
return budget;
|
|
31723
|
+
}
|
|
31724
|
+
/** Normalize a CFn notification block to the SDK `Notification` shape. */
|
|
31725
|
+
toSdkNotification(raw) {
|
|
31726
|
+
return {
|
|
31727
|
+
NotificationType: raw["NotificationType"],
|
|
31728
|
+
ComparisonOperator: raw["ComparisonOperator"],
|
|
31729
|
+
Threshold: raw["Threshold"] !== void 0 ? Number(raw["Threshold"]) : void 0,
|
|
31730
|
+
...raw["ThresholdType"] !== void 0 && { ThresholdType: raw["ThresholdType"] }
|
|
31731
|
+
};
|
|
31732
|
+
}
|
|
31733
|
+
/** Normalize a CFn subscriber block to the SDK `Subscriber` shape. */
|
|
31734
|
+
toSdkSubscriber(raw) {
|
|
31735
|
+
return {
|
|
31736
|
+
SubscriptionType: raw["SubscriptionType"],
|
|
31737
|
+
Address: raw["Address"]
|
|
31738
|
+
};
|
|
31739
|
+
}
|
|
31740
|
+
/**
|
|
31741
|
+
* Parse the CFn `NotificationsWithSubscribers` property into SDK shape.
|
|
31742
|
+
* Tolerates absent / non-array values (returns `[]`).
|
|
31743
|
+
*/
|
|
31744
|
+
toSdkNotificationsWithSubscribers(raw) {
|
|
31745
|
+
if (!Array.isArray(raw)) return [];
|
|
31746
|
+
const out = [];
|
|
31747
|
+
for (const item of raw) {
|
|
31748
|
+
if (item === null || typeof item !== "object") continue;
|
|
31749
|
+
const entry = item;
|
|
31750
|
+
const notification = entry["Notification"];
|
|
31751
|
+
const subscribers = entry["Subscribers"];
|
|
31752
|
+
out.push({
|
|
31753
|
+
Notification: notification && typeof notification === "object" ? this.toSdkNotification(notification) : void 0,
|
|
31754
|
+
Subscribers: Array.isArray(subscribers) ? subscribers.filter((s) => s !== null && typeof s === "object").map((s) => this.toSdkSubscriber(s)) : void 0
|
|
31755
|
+
});
|
|
31756
|
+
}
|
|
31757
|
+
return out;
|
|
31758
|
+
}
|
|
31759
|
+
/**
|
|
31760
|
+
* Identity key for a notification. The Budgets API addresses notifications
|
|
31761
|
+
* by their full value (there is no notification id), so the reconciler
|
|
31762
|
+
* treats a change to any of these fields as delete-old + create-new.
|
|
31763
|
+
* `ThresholdType` defaults to `PERCENTAGE` service-side — normalize the
|
|
31764
|
+
* absent case so an explicit `PERCENTAGE` and an omitted one compare equal.
|
|
31765
|
+
*/
|
|
31766
|
+
notificationKey(n) {
|
|
31767
|
+
return JSON.stringify([
|
|
31768
|
+
n.NotificationType,
|
|
31769
|
+
n.ComparisonOperator,
|
|
31770
|
+
n.Threshold,
|
|
31771
|
+
n.ThresholdType ?? "PERCENTAGE"
|
|
31772
|
+
]);
|
|
31773
|
+
}
|
|
31774
|
+
/** Identity key for a subscriber. */
|
|
31775
|
+
subscriberKey(s) {
|
|
31776
|
+
return JSON.stringify([s.SubscriptionType, s.Address]);
|
|
31777
|
+
}
|
|
31778
|
+
/** Normalize the CFn `ResourceTags` property (`{Key,Value}[]`). */
|
|
31779
|
+
toSdkResourceTags(raw) {
|
|
31780
|
+
if (!Array.isArray(raw)) return [];
|
|
31781
|
+
const out = [];
|
|
31782
|
+
for (const item of raw) {
|
|
31783
|
+
if (item === null || typeof item !== "object") continue;
|
|
31784
|
+
const tag = item;
|
|
31785
|
+
if (typeof tag["Key"] !== "string") continue;
|
|
31786
|
+
const value = tag["Value"];
|
|
31787
|
+
out.push({
|
|
31788
|
+
Key: tag["Key"],
|
|
31789
|
+
Value: typeof value === "string" ? value : typeof value === "number" || typeof value === "boolean" ? String(value) : ""
|
|
31790
|
+
});
|
|
31791
|
+
}
|
|
31792
|
+
return out;
|
|
31793
|
+
}
|
|
31794
|
+
/**
|
|
31795
|
+
* Create a budget via `CreateBudget` (a single call carries the budget,
|
|
31796
|
+
* its notifications with subscribers, and resource tags — no post-create
|
|
31797
|
+
* wiring to clean up on partial failure).
|
|
31798
|
+
*/
|
|
31799
|
+
async create(logicalId, resourceType, properties) {
|
|
31800
|
+
this.logger.debug(`Creating budget ${logicalId}`);
|
|
31801
|
+
const rawName = (properties["Budget"] ?? {})["BudgetName"];
|
|
31802
|
+
const name = typeof rawName === "string" && rawName.length > 0 ? rawName : generateResourceName(logicalId, {
|
|
31803
|
+
maxLength: 100,
|
|
31804
|
+
allowedPattern: /[^a-zA-Z0-9\-_.]/g
|
|
31805
|
+
});
|
|
31806
|
+
try {
|
|
31807
|
+
const accountId = await this.resolveAccountId();
|
|
31808
|
+
const notifications = this.toSdkNotificationsWithSubscribers(properties["NotificationsWithSubscribers"]);
|
|
31809
|
+
const resourceTags = this.toSdkResourceTags(properties["ResourceTags"]);
|
|
31810
|
+
await this.getClient().send(new CreateBudgetCommand({
|
|
31811
|
+
AccountId: accountId,
|
|
31812
|
+
Budget: this.toSdkBudget(properties["Budget"], name, logicalId, resourceType),
|
|
31813
|
+
...notifications.length > 0 && { NotificationsWithSubscribers: notifications },
|
|
31814
|
+
...resourceTags.length > 0 && { ResourceTags: resourceTags }
|
|
31815
|
+
}));
|
|
31816
|
+
this.logger.debug(`Successfully created budget ${logicalId}: ${name}`);
|
|
31817
|
+
return {
|
|
31818
|
+
physicalId: name,
|
|
31819
|
+
attributes: { Arn: this.budgetArn(accountId, name) }
|
|
31820
|
+
};
|
|
31821
|
+
} catch (error) {
|
|
31822
|
+
if (error instanceof ProvisioningError) throw error;
|
|
31823
|
+
const cause = error instanceof Error ? error : void 0;
|
|
31824
|
+
throw new ProvisioningError(`Failed to create budget ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
|
|
31825
|
+
}
|
|
31826
|
+
}
|
|
31827
|
+
/**
|
|
31828
|
+
* Update a budget: `UpdateBudget` for the budget definition, then
|
|
31829
|
+
* reconcile `NotificationsWithSubscribers` in place (CloudFormation would
|
|
31830
|
+
* replace the whole budget instead) and diff `ResourceTags`.
|
|
31831
|
+
*/
|
|
31832
|
+
async update(logicalId, physicalId, resourceType, properties, previousProperties) {
|
|
31833
|
+
this.logger.debug(`Updating budget ${logicalId}: ${physicalId}`);
|
|
31834
|
+
try {
|
|
31835
|
+
const accountId = await this.resolveAccountId();
|
|
31836
|
+
await this.getClient().send(new UpdateBudgetCommand({
|
|
31837
|
+
AccountId: accountId,
|
|
31838
|
+
NewBudget: this.toSdkBudget(properties["Budget"], physicalId, logicalId, resourceType)
|
|
31839
|
+
}));
|
|
31840
|
+
await this.reconcileNotifications(accountId, physicalId, this.toSdkNotificationsWithSubscribers(previousProperties["NotificationsWithSubscribers"]), this.toSdkNotificationsWithSubscribers(properties["NotificationsWithSubscribers"]));
|
|
31841
|
+
await this.reconcileResourceTags(accountId, physicalId, this.toSdkResourceTags(previousProperties["ResourceTags"]), this.toSdkResourceTags(properties["ResourceTags"]));
|
|
31842
|
+
this.logger.debug(`Successfully updated budget ${logicalId}`);
|
|
31843
|
+
return {
|
|
31844
|
+
physicalId,
|
|
31845
|
+
wasReplaced: false,
|
|
31846
|
+
attributes: { Arn: this.budgetArn(accountId, physicalId) }
|
|
31847
|
+
};
|
|
31848
|
+
} catch (error) {
|
|
31849
|
+
if (error instanceof ProvisioningError) throw error;
|
|
31850
|
+
const cause = error instanceof Error ? error : void 0;
|
|
31851
|
+
throw new ProvisioningError(`Failed to update budget ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
|
|
31852
|
+
}
|
|
31853
|
+
}
|
|
31854
|
+
/**
|
|
31855
|
+
* Send a reconciler DELETE call, treating `NotFoundException` as success.
|
|
31856
|
+
* The reconciler must be idempotent: after a partial failure the deploy
|
|
31857
|
+
* engine retries the update forward (re-deleting an already-deleted
|
|
31858
|
+
* notification/subscriber) or rolls it back with the reversed diff
|
|
31859
|
+
* (deleting a notification the forward pass never created) — both land
|
|
31860
|
+
* here as NotFound and must not fail the recovery.
|
|
31861
|
+
*/
|
|
31862
|
+
async sendDeleteIdempotent(command, what) {
|
|
31863
|
+
try {
|
|
31864
|
+
await this.getClient().send(command);
|
|
31865
|
+
} catch (error) {
|
|
31866
|
+
if (error instanceof NotFoundException$5) {
|
|
31867
|
+
this.logger.debug(`${what} already absent, skipping delete`);
|
|
31868
|
+
return;
|
|
31869
|
+
}
|
|
31870
|
+
throw error;
|
|
31871
|
+
}
|
|
31872
|
+
}
|
|
31873
|
+
/**
|
|
31874
|
+
* Send a reconciler CREATE call, treating `DuplicateRecordException` as
|
|
31875
|
+
* success — the retry/rollback twin of {@link sendDeleteIdempotent} for
|
|
31876
|
+
* re-creating a notification/subscriber a previous partial pass already
|
|
31877
|
+
* created.
|
|
31878
|
+
*/
|
|
31879
|
+
async sendCreateIdempotent(command, what) {
|
|
31880
|
+
try {
|
|
31881
|
+
await this.getClient().send(command);
|
|
31882
|
+
} catch (error) {
|
|
31883
|
+
if (error instanceof DuplicateRecordException) {
|
|
31884
|
+
this.logger.debug(`${what} already exists, skipping create`);
|
|
31885
|
+
return;
|
|
31886
|
+
}
|
|
31887
|
+
throw error;
|
|
31888
|
+
}
|
|
31889
|
+
}
|
|
31890
|
+
/**
|
|
31891
|
+
* Reconcile the notification set: delete removed notifications first
|
|
31892
|
+
* (frees the 10-notifications-per-budget cap before additions), create
|
|
31893
|
+
* added ones, then diff subscribers on retained notifications. Every
|
|
31894
|
+
* step is idempotent (NotFound on delete / DuplicateRecord on create are
|
|
31895
|
+
* success) so a partial failure can be retried forward or rolled back.
|
|
31896
|
+
*/
|
|
31897
|
+
async reconcileNotifications(accountId, budgetName, oldList, newList) {
|
|
31898
|
+
const buildKeyed = (list, side) => {
|
|
31899
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
31900
|
+
for (const nws of list) {
|
|
31901
|
+
if (!nws.Notification) continue;
|
|
31902
|
+
const key = this.notificationKey(nws.Notification);
|
|
31903
|
+
if (byKey.has(key)) this.logger.warn(`Duplicate notification key ${key} in ${side} NotificationsWithSubscribers for budget ${budgetName}; the last entry's subscribers win`);
|
|
31904
|
+
byKey.set(key, nws);
|
|
31905
|
+
}
|
|
31906
|
+
return byKey;
|
|
31907
|
+
};
|
|
31908
|
+
const oldByKey = buildKeyed(oldList, "previous");
|
|
31909
|
+
const newByKey = buildKeyed(newList, "desired");
|
|
31910
|
+
for (const [key, nws] of oldByKey) {
|
|
31911
|
+
if (newByKey.has(key)) continue;
|
|
31912
|
+
await this.sendDeleteIdempotent(new DeleteNotificationCommand({
|
|
31913
|
+
AccountId: accountId,
|
|
31914
|
+
BudgetName: budgetName,
|
|
31915
|
+
Notification: nws.Notification
|
|
31916
|
+
}), `Notification ${key} on budget ${budgetName}`);
|
|
31917
|
+
this.logger.debug(`Deleted notification ${key} from budget ${budgetName}`);
|
|
31918
|
+
}
|
|
31919
|
+
for (const [key, nws] of newByKey) {
|
|
31920
|
+
if (oldByKey.has(key)) continue;
|
|
31921
|
+
await this.sendCreateIdempotent(new CreateNotificationCommand({
|
|
31922
|
+
AccountId: accountId,
|
|
31923
|
+
BudgetName: budgetName,
|
|
31924
|
+
Notification: nws.Notification,
|
|
31925
|
+
Subscribers: nws.Subscribers
|
|
31926
|
+
}), `Notification ${key} on budget ${budgetName}`);
|
|
31927
|
+
this.logger.debug(`Created notification ${key} on budget ${budgetName}`);
|
|
31928
|
+
}
|
|
31929
|
+
for (const [key, newNws] of newByKey) {
|
|
31930
|
+
const oldNws = oldByKey.get(key);
|
|
31931
|
+
if (!oldNws) continue;
|
|
31932
|
+
const oldSubs = /* @__PURE__ */ new Map();
|
|
31933
|
+
for (const s of oldNws.Subscribers ?? []) oldSubs.set(this.subscriberKey(s), s);
|
|
31934
|
+
const newSubs = /* @__PURE__ */ new Map();
|
|
31935
|
+
for (const s of newNws.Subscribers ?? []) newSubs.set(this.subscriberKey(s), s);
|
|
31936
|
+
for (const [subKey, subscriber] of newSubs) {
|
|
31937
|
+
if (oldSubs.has(subKey)) continue;
|
|
31938
|
+
await this.sendCreateIdempotent(new CreateSubscriberCommand({
|
|
31939
|
+
AccountId: accountId,
|
|
31940
|
+
BudgetName: budgetName,
|
|
31941
|
+
Notification: newNws.Notification,
|
|
31942
|
+
Subscriber: subscriber
|
|
31943
|
+
}), `Subscriber ${subKey} on notification ${key}`);
|
|
31944
|
+
this.logger.debug(`Created subscriber ${subKey} on notification ${key}`);
|
|
31945
|
+
}
|
|
31946
|
+
for (const [subKey, subscriber] of oldSubs) {
|
|
31947
|
+
if (newSubs.has(subKey)) continue;
|
|
31948
|
+
await this.sendDeleteIdempotent(new DeleteSubscriberCommand({
|
|
31949
|
+
AccountId: accountId,
|
|
31950
|
+
BudgetName: budgetName,
|
|
31951
|
+
Notification: newNws.Notification,
|
|
31952
|
+
Subscriber: subscriber
|
|
31953
|
+
}), `Subscriber ${subKey} on notification ${key}`);
|
|
31954
|
+
this.logger.debug(`Deleted subscriber ${subKey} from notification ${key}`);
|
|
31955
|
+
}
|
|
31956
|
+
}
|
|
31957
|
+
}
|
|
31958
|
+
/** Diff `ResourceTags` via `UntagResource` (removed keys) + `TagResource` (upsert). */
|
|
31959
|
+
async reconcileResourceTags(accountId, budgetName, oldTags, newTags) {
|
|
31960
|
+
const sortedJson = (tags) => JSON.stringify([...tags].sort((a, b) => (a.Key ?? "").localeCompare(b.Key ?? "")));
|
|
31961
|
+
if (sortedJson(oldTags) === sortedJson(newTags)) return;
|
|
31962
|
+
const arn = this.budgetArn(accountId, budgetName);
|
|
31963
|
+
const newKeys = new Set(newTags.map((t) => t.Key));
|
|
31964
|
+
const removedKeys = oldTags.map((t) => t.Key).filter((k) => {
|
|
31965
|
+
return typeof k === "string" && !newKeys.has(k);
|
|
31966
|
+
});
|
|
31967
|
+
if (removedKeys.length > 0) await this.getClient().send(new UntagResourceCommand$14({
|
|
31968
|
+
ResourceARN: arn,
|
|
31969
|
+
ResourceTagKeys: removedKeys
|
|
31970
|
+
}));
|
|
31971
|
+
if (newTags.length > 0) await this.getClient().send(new TagResourceCommand$14({
|
|
31972
|
+
ResourceARN: arn,
|
|
31973
|
+
ResourceTags: newTags
|
|
31974
|
+
}));
|
|
31975
|
+
this.logger.debug(`Updated resource tags for budget ${budgetName}`);
|
|
31976
|
+
}
|
|
31977
|
+
/**
|
|
31978
|
+
* Delete a budget (`DeleteBudget` also deletes all of its notifications
|
|
31979
|
+
* and subscribers). NotFound is idempotent success — after the standard
|
|
31980
|
+
* region assertion (see the class doc for why the guard stays uniform
|
|
31981
|
+
* even though the Budgets namespace is global).
|
|
31982
|
+
*/
|
|
31983
|
+
async delete(logicalId, physicalId, resourceType, _properties, context) {
|
|
31984
|
+
this.logger.debug(`Deleting budget ${logicalId}: ${physicalId}`);
|
|
31985
|
+
try {
|
|
31986
|
+
const accountId = await this.resolveAccountId();
|
|
31987
|
+
await this.getClient().send(new DeleteBudgetCommand({
|
|
31988
|
+
AccountId: accountId,
|
|
31989
|
+
BudgetName: physicalId
|
|
31990
|
+
}));
|
|
31991
|
+
this.logger.debug(`Successfully deleted budget ${logicalId}`);
|
|
31992
|
+
} catch (error) {
|
|
31993
|
+
if (error instanceof NotFoundException$5) {
|
|
31994
|
+
assertRegionMatch(await this.getClient().config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
|
|
31995
|
+
this.logger.debug(`Budget ${physicalId} does not exist, skipping deletion`);
|
|
31996
|
+
return;
|
|
31997
|
+
}
|
|
31998
|
+
const cause = error instanceof Error ? error : void 0;
|
|
31999
|
+
throw new ProvisioningError(`Failed to delete budget ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
|
|
32000
|
+
}
|
|
32001
|
+
}
|
|
32002
|
+
/**
|
|
32003
|
+
* `Fn::GetAtt` support. CloudFormation documents no attributes for
|
|
32004
|
+
* `AWS::Budgets::Budget`; `Arn` is served as a cdkd convenience (computed
|
|
32005
|
+
* — Budgets ARNs are `arn:aws:budgets::{account}:budget/{name}`, verified
|
|
32006
|
+
* to exist via `DescribeBudget` first).
|
|
32007
|
+
*/
|
|
32008
|
+
async getAttribute(physicalId, resourceType, attributeName) {
|
|
32009
|
+
if (attributeName !== "Arn") throw new ProvisioningError(`Unknown attribute ${attributeName} for ${resourceType}`, resourceType, physicalId);
|
|
32010
|
+
try {
|
|
32011
|
+
const accountId = await this.resolveAccountId();
|
|
32012
|
+
await this.getClient().send(new DescribeBudgetCommand({
|
|
32013
|
+
AccountId: accountId,
|
|
32014
|
+
BudgetName: physicalId
|
|
32015
|
+
}));
|
|
32016
|
+
return this.budgetArn(accountId, physicalId);
|
|
32017
|
+
} catch (error) {
|
|
32018
|
+
const cause = error instanceof Error ? error : void 0;
|
|
32019
|
+
throw new ProvisioningError(`Failed to resolve Arn for budget ${physicalId}: ${cause?.message ?? String(error)}`, resourceType, physicalId, physicalId, cause);
|
|
32020
|
+
}
|
|
32021
|
+
}
|
|
32022
|
+
/**
|
|
32023
|
+
* Adopt an existing budget into cdkd state.
|
|
32024
|
+
*
|
|
32025
|
+
* Lookup order:
|
|
32026
|
+
* 1. `--resource` override or `Properties.Budget.BudgetName` → verify via
|
|
32027
|
+
* `DescribeBudget`.
|
|
32028
|
+
* 2. `aws:cdk:path` tag match: walk `DescribeBudgets` and check each
|
|
32029
|
+
* budget's tags via `ListTagsForResource` (budget ARNs are
|
|
32030
|
+
* region-free, so the lookup works from any deploy region).
|
|
32031
|
+
*/
|
|
32032
|
+
async import(input) {
|
|
32033
|
+
const rawBudget = input.properties["Budget"] ?? {};
|
|
32034
|
+
const explicit = input.knownPhysicalId ?? (typeof rawBudget["BudgetName"] === "string" && rawBudget["BudgetName"].length > 0 ? rawBudget["BudgetName"] : void 0);
|
|
32035
|
+
const accountId = await this.resolveAccountId();
|
|
32036
|
+
const client = this.getClient();
|
|
32037
|
+
if (explicit) try {
|
|
32038
|
+
await client.send(new DescribeBudgetCommand({
|
|
32039
|
+
AccountId: accountId,
|
|
32040
|
+
BudgetName: explicit
|
|
32041
|
+
}));
|
|
32042
|
+
return {
|
|
32043
|
+
physicalId: explicit,
|
|
32044
|
+
attributes: { Arn: this.budgetArn(accountId, explicit) }
|
|
32045
|
+
};
|
|
32046
|
+
} catch (error) {
|
|
32047
|
+
if (error instanceof NotFoundException$5) return null;
|
|
32048
|
+
throw error;
|
|
32049
|
+
}
|
|
32050
|
+
if (!input.cdkPath) return null;
|
|
32051
|
+
let nextToken;
|
|
32052
|
+
do {
|
|
32053
|
+
const list = await client.send(new DescribeBudgetsCommand({
|
|
32054
|
+
AccountId: accountId,
|
|
32055
|
+
...nextToken && { NextToken: nextToken }
|
|
32056
|
+
}));
|
|
32057
|
+
for (const budget of list.Budgets ?? []) {
|
|
32058
|
+
if (!budget.BudgetName) continue;
|
|
32059
|
+
if (matchesCdkPath((await client.send(new ListTagsForResourceCommand$16({ ResourceARN: this.budgetArn(accountId, budget.BudgetName) }))).ResourceTags, input.cdkPath)) return {
|
|
32060
|
+
physicalId: budget.BudgetName,
|
|
32061
|
+
attributes: { Arn: this.budgetArn(accountId, budget.BudgetName) }
|
|
32062
|
+
};
|
|
32063
|
+
}
|
|
32064
|
+
nextToken = list.NextToken;
|
|
32065
|
+
} while (nextToken);
|
|
32066
|
+
return null;
|
|
32067
|
+
}
|
|
32068
|
+
};
|
|
32069
|
+
|
|
31582
32070
|
//#endregion
|
|
31583
32071
|
//#region src/provisioning/providers/kinesis-provider.ts
|
|
31584
32072
|
/**
|
|
@@ -32139,7 +32627,7 @@ var KinesisStreamConsumerProvider = class {
|
|
|
32139
32627
|
if (desc.ConsumerName !== void 0) result["ConsumerName"] = desc.ConsumerName;
|
|
32140
32628
|
if (desc.StreamARN !== void 0) result["StreamARN"] = desc.StreamARN;
|
|
32141
32629
|
try {
|
|
32142
|
-
result["Tags"] = normalizeAwsTagsToCfn((await this.getClient().send(new ListTagsForResourceCommand$
|
|
32630
|
+
result["Tags"] = normalizeAwsTagsToCfn((await this.getClient().send(new ListTagsForResourceCommand$17({ ResourceARN: physicalId }))).Tags);
|
|
32143
32631
|
} catch (err) {
|
|
32144
32632
|
if (err instanceof ResourceNotFoundException$8) return void 0;
|
|
32145
32633
|
this.logger.debug(`ListTagsForResource(${physicalId}) failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -32163,14 +32651,14 @@ var KinesisStreamConsumerProvider = class {
|
|
|
32163
32651
|
const tagsToRemove = [];
|
|
32164
32652
|
for (const k of Object.keys(oldMap)) if (!(k in newMap)) tagsToRemove.push(k);
|
|
32165
32653
|
if (tagsToRemove.length > 0) {
|
|
32166
|
-
await this.getClient().send(new UntagResourceCommand$
|
|
32654
|
+
await this.getClient().send(new UntagResourceCommand$15({
|
|
32167
32655
|
ResourceARN: consumerArn,
|
|
32168
32656
|
TagKeys: tagsToRemove
|
|
32169
32657
|
}));
|
|
32170
32658
|
this.logger.debug(`Removed ${tagsToRemove.length} tag(s) from Kinesis stream consumer ${consumerArn}`);
|
|
32171
32659
|
}
|
|
32172
32660
|
if (Object.keys(tagsToAdd).length > 0) {
|
|
32173
|
-
await this.getClient().send(new TagResourceCommand$
|
|
32661
|
+
await this.getClient().send(new TagResourceCommand$15({
|
|
32174
32662
|
ResourceARN: consumerArn,
|
|
32175
32663
|
Tags: tagsToAdd
|
|
32176
32664
|
}));
|
|
@@ -35163,6 +35651,293 @@ var CodeBuildProvider = class {
|
|
|
35163
35651
|
}
|
|
35164
35652
|
};
|
|
35165
35653
|
|
|
35654
|
+
//#endregion
|
|
35655
|
+
//#region src/provisioning/providers/codecommit-repository-provider.ts
|
|
35656
|
+
/**
|
|
35657
|
+
* Convert the CFn `Tags` list shape to CodeCommit's `Record<string, string>`
|
|
35658
|
+
* map. Entries without a string `Key` are skipped; non-string values are
|
|
35659
|
+
* stringified (post-intrinsic-resolution values can be numbers/booleans).
|
|
35660
|
+
* Returns `undefined` for an absent/empty list so callers can omit the field.
|
|
35661
|
+
*/
|
|
35662
|
+
function toSdkTagMap(tags) {
|
|
35663
|
+
if (!tags || tags.length === 0) return void 0;
|
|
35664
|
+
const out = {};
|
|
35665
|
+
for (const tag of tags) {
|
|
35666
|
+
if (typeof tag?.Key !== "string" || tag.Key.length === 0) continue;
|
|
35667
|
+
const value = tag.Value;
|
|
35668
|
+
out[tag.Key] = typeof value === "string" ? value : typeof value === "number" || typeof value === "boolean" ? String(value) : "";
|
|
35669
|
+
}
|
|
35670
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
35671
|
+
}
|
|
35672
|
+
/** Order-independent equality for two SDK tag maps. */
|
|
35673
|
+
function tagMapsEqual(a, b) {
|
|
35674
|
+
const aKeys = Object.keys(a);
|
|
35675
|
+
if (aKeys.length !== Object.keys(b).length) return false;
|
|
35676
|
+
return aKeys.every((k) => b[k] === a[k]);
|
|
35677
|
+
}
|
|
35678
|
+
/**
|
|
35679
|
+
* AWS CodeCommit Repository Provider
|
|
35680
|
+
*
|
|
35681
|
+
* Implements resource provisioning for AWS::CodeCommit::Repository using the
|
|
35682
|
+
* CodeCommit SDK. The type is `ProvisioningType: NON_PROVISIONABLE`, so the
|
|
35683
|
+
* Cloud Control fallback cannot handle it — without this SDK provider cdkd's
|
|
35684
|
+
* pre-flight rejects the type outright (issue #1045). CodeCommit returned to
|
|
35685
|
+
* full General Availability on 2025-11-24, so the service is fully usable for
|
|
35686
|
+
* new sign-ups again.
|
|
35687
|
+
*
|
|
35688
|
+
* Physical id: the repository NAME (every CodeCommit API is name-based;
|
|
35689
|
+
* there is no lookup-by-id API). CloudFormation's `Ref` returns the
|
|
35690
|
+
* repository ID (a GUID), so `create()` stores `RepositoryId` in attributes
|
|
35691
|
+
* and the intrinsic resolver's `cfnRefValueFromPhysicalId` recovers it via
|
|
35692
|
+
* `stateLookup` for CFn `Ref` parity.
|
|
35693
|
+
*/
|
|
35694
|
+
var CodeCommitRepositoryProvider = class {
|
|
35695
|
+
client;
|
|
35696
|
+
providerRegion = process.env["AWS_REGION"];
|
|
35697
|
+
logger = getLogger().child("CodeCommitRepositoryProvider");
|
|
35698
|
+
handledProperties = new Map([["AWS::CodeCommit::Repository", new Set([
|
|
35699
|
+
"RepositoryName",
|
|
35700
|
+
"RepositoryDescription",
|
|
35701
|
+
"KmsKeyId",
|
|
35702
|
+
"Tags"
|
|
35703
|
+
])]]);
|
|
35704
|
+
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"]])]]);
|
|
35705
|
+
getClient() {
|
|
35706
|
+
if (!this.client) this.client = new CodeCommitClient(this.providerRegion ? { region: this.providerRegion } : {});
|
|
35707
|
+
return this.client;
|
|
35708
|
+
}
|
|
35709
|
+
/**
|
|
35710
|
+
* Build the `Fn::GetAtt` attribute map from a `RepositoryMetadata`
|
|
35711
|
+
* response. `RepositoryId` is additionally stored so the intrinsic
|
|
35712
|
+
* resolver can recover CFn's `Ref` value (the repository ID) from state.
|
|
35713
|
+
*/
|
|
35714
|
+
toAttributes(metadata) {
|
|
35715
|
+
return {
|
|
35716
|
+
Arn: metadata?.Arn ?? "",
|
|
35717
|
+
CloneUrlHttp: metadata?.cloneUrlHttp ?? "",
|
|
35718
|
+
CloneUrlSsh: metadata?.cloneUrlSsh ?? "",
|
|
35719
|
+
Name: metadata?.repositoryName ?? "",
|
|
35720
|
+
KmsKeyId: metadata?.kmsKeyId ?? "",
|
|
35721
|
+
RepositoryId: metadata?.repositoryId ?? ""
|
|
35722
|
+
};
|
|
35723
|
+
}
|
|
35724
|
+
/**
|
|
35725
|
+
* Create a CodeCommit Repository
|
|
35726
|
+
*/
|
|
35727
|
+
async create(logicalId, resourceType, properties) {
|
|
35728
|
+
this.logger.debug(`Creating CodeCommit Repository ${logicalId}`);
|
|
35729
|
+
const repositoryName = properties["RepositoryName"] || generateResourceName(logicalId, { maxLength: 100 });
|
|
35730
|
+
try {
|
|
35731
|
+
const tags = toSdkTagMap(properties["Tags"]);
|
|
35732
|
+
const description = properties["RepositoryDescription"];
|
|
35733
|
+
const kmsKeyId = properties["KmsKeyId"];
|
|
35734
|
+
const metadata = (await this.getClient().send(new CreateRepositoryCommand$1({
|
|
35735
|
+
repositoryName,
|
|
35736
|
+
...description !== void 0 ? { repositoryDescription: description } : {},
|
|
35737
|
+
...kmsKeyId !== void 0 ? { kmsKeyId } : {},
|
|
35738
|
+
...tags ? { tags } : {}
|
|
35739
|
+
}))).repositoryMetadata;
|
|
35740
|
+
if (!metadata?.repositoryName) throw new Error("CreateRepository did not return repository metadata");
|
|
35741
|
+
this.logger.debug(`Successfully created CodeCommit Repository ${logicalId}: ${metadata.repositoryName}`);
|
|
35742
|
+
return {
|
|
35743
|
+
physicalId: metadata.repositoryName,
|
|
35744
|
+
attributes: this.toAttributes(metadata)
|
|
35745
|
+
};
|
|
35746
|
+
} catch (error) {
|
|
35747
|
+
const cause = error instanceof Error ? error : void 0;
|
|
35748
|
+
throw new ProvisioningError(`Failed to create CodeCommit Repository ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, repositoryName, cause);
|
|
35749
|
+
}
|
|
35750
|
+
}
|
|
35751
|
+
/**
|
|
35752
|
+
* Update a CodeCommit Repository
|
|
35753
|
+
*
|
|
35754
|
+
* Mutable properties: RepositoryName (UpdateRepositoryName — CFn's docs
|
|
35755
|
+
* mark the property "Update requires: No interruption" and the registry
|
|
35756
|
+
* schema's createOnlyProperties is empty, so CFn parity is an IN-PLACE
|
|
35757
|
+
* rename that preserves the repository's git history; the repository ID —
|
|
35758
|
+
* CFn's `Ref` value — survives the rename), RepositoryDescription
|
|
35759
|
+
* (UpdateRepositoryDescription), KmsKeyId (UpdateRepositoryEncryptionKey),
|
|
35760
|
+
* Tags (TagResource / UntagResource — full tag removal handled explicitly,
|
|
35761
|
+
* see the ECR Tags regression class in issue #981).
|
|
35762
|
+
*
|
|
35763
|
+
* A rename returns the NEW repository name as `physicalId` with
|
|
35764
|
+
* `wasReplaced: false`; the deploy engine persists the returned physical
|
|
35765
|
+
* id into state unconditionally.
|
|
35766
|
+
*/
|
|
35767
|
+
async update(logicalId, physicalId, _resourceType, properties, previousProperties) {
|
|
35768
|
+
this.logger.debug(`Updating CodeCommit Repository ${logicalId} (${physicalId})`);
|
|
35769
|
+
let currentName = physicalId;
|
|
35770
|
+
try {
|
|
35771
|
+
const newName = properties["RepositoryName"];
|
|
35772
|
+
if (newName && newName !== physicalId) {
|
|
35773
|
+
try {
|
|
35774
|
+
await this.getClient().send(new UpdateRepositoryNameCommand({
|
|
35775
|
+
oldName: physicalId,
|
|
35776
|
+
newName
|
|
35777
|
+
}));
|
|
35778
|
+
} catch (err) {
|
|
35779
|
+
if (!(err instanceof RepositoryDoesNotExistException)) throw err;
|
|
35780
|
+
await this.getRepositoryMetadata(newName);
|
|
35781
|
+
this.logger.debug(`Rename ${physicalId} -> ${newName} already applied by a previous attempt`);
|
|
35782
|
+
}
|
|
35783
|
+
currentName = newName;
|
|
35784
|
+
this.logger.debug(`Renamed CodeCommit Repository ${physicalId} -> ${newName}`);
|
|
35785
|
+
}
|
|
35786
|
+
const newDescription = properties["RepositoryDescription"];
|
|
35787
|
+
if (newDescription !== previousProperties["RepositoryDescription"]) {
|
|
35788
|
+
await this.getClient().send(new UpdateRepositoryDescriptionCommand({
|
|
35789
|
+
repositoryName: currentName,
|
|
35790
|
+
repositoryDescription: newDescription ?? ""
|
|
35791
|
+
}));
|
|
35792
|
+
this.logger.debug(`Updated description for ${currentName}`);
|
|
35793
|
+
}
|
|
35794
|
+
const newKmsKeyId = properties["KmsKeyId"];
|
|
35795
|
+
if (newKmsKeyId !== previousProperties["KmsKeyId"]) {
|
|
35796
|
+
await this.getClient().send(new UpdateRepositoryEncryptionKeyCommand({
|
|
35797
|
+
repositoryName: currentName,
|
|
35798
|
+
kmsKeyId: newKmsKeyId ?? "alias/aws/codecommit"
|
|
35799
|
+
}));
|
|
35800
|
+
this.logger.debug(`Updated encryption key for ${currentName}`);
|
|
35801
|
+
}
|
|
35802
|
+
const newTags = properties["Tags"];
|
|
35803
|
+
const oldTags = previousProperties["Tags"];
|
|
35804
|
+
const newTagMap = toSdkTagMap(newTags) ?? {};
|
|
35805
|
+
const oldTagMap = toSdkTagMap(oldTags) ?? {};
|
|
35806
|
+
let metadata;
|
|
35807
|
+
if (!tagMapsEqual(newTagMap, oldTagMap)) {
|
|
35808
|
+
metadata = await this.getRepositoryMetadata(currentName);
|
|
35809
|
+
const repoArn = metadata?.Arn;
|
|
35810
|
+
if (repoArn) {
|
|
35811
|
+
const removedKeys = Object.keys(oldTagMap).filter((k) => !(k in newTagMap));
|
|
35812
|
+
if (removedKeys.length > 0) await this.getClient().send(new UntagResourceCommand$16({
|
|
35813
|
+
resourceArn: repoArn,
|
|
35814
|
+
tagKeys: removedKeys
|
|
35815
|
+
}));
|
|
35816
|
+
if (Object.keys(newTagMap).length > 0) await this.getClient().send(new TagResourceCommand$16({
|
|
35817
|
+
resourceArn: repoArn,
|
|
35818
|
+
tags: newTagMap
|
|
35819
|
+
}));
|
|
35820
|
+
this.logger.debug(`Updated tags for ${currentName}`);
|
|
35821
|
+
} else this.logger.warn(`Could not resolve ARN for CodeCommit Repository ${currentName}; tag update skipped`);
|
|
35822
|
+
}
|
|
35823
|
+
metadata = await this.getRepositoryMetadata(currentName);
|
|
35824
|
+
return {
|
|
35825
|
+
physicalId: currentName,
|
|
35826
|
+
wasReplaced: false,
|
|
35827
|
+
attributes: this.toAttributes(metadata)
|
|
35828
|
+
};
|
|
35829
|
+
} catch (error) {
|
|
35830
|
+
const cause = error instanceof Error ? error : void 0;
|
|
35831
|
+
throw new ProvisioningError(`Failed to update CodeCommit Repository ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, _resourceType, logicalId, physicalId, cause);
|
|
35832
|
+
}
|
|
35833
|
+
}
|
|
35834
|
+
/**
|
|
35835
|
+
* Delete a CodeCommit Repository
|
|
35836
|
+
*
|
|
35837
|
+
* `DeleteRepository` is idempotent on the AWS side: for an
|
|
35838
|
+
* already-deleted repository it does NOT throw — it returns a null
|
|
35839
|
+
* `repositoryId`. That silent-success shape would bypass the shared
|
|
35840
|
+
* region check entirely (the exact scenario `assertRegionMatch` exists
|
|
35841
|
+
* for: a client pointed at region B while the state says the repo lives
|
|
35842
|
+
* in region A), so a null `repositoryId` runs the region check before
|
|
35843
|
+
* being treated as idempotent success. A
|
|
35844
|
+
* `RepositoryDoesNotExistException` is additionally handled (same
|
|
35845
|
+
* check) for defense in depth.
|
|
35846
|
+
*/
|
|
35847
|
+
async delete(logicalId, physicalId, resourceType, _properties, context) {
|
|
35848
|
+
this.logger.debug(`Deleting CodeCommit Repository ${logicalId}: ${physicalId}`);
|
|
35849
|
+
let deletedRepositoryId;
|
|
35850
|
+
try {
|
|
35851
|
+
deletedRepositoryId = (await this.getClient().send(new DeleteRepositoryCommand$1({ repositoryName: physicalId }))).repositoryId;
|
|
35852
|
+
} catch (error) {
|
|
35853
|
+
if (error instanceof RepositoryDoesNotExistException) {
|
|
35854
|
+
assertRegionMatch(await this.getClient().config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
|
|
35855
|
+
this.logger.debug(`CodeCommit Repository ${physicalId} does not exist, skipping deletion`);
|
|
35856
|
+
return;
|
|
35857
|
+
}
|
|
35858
|
+
const cause = error instanceof Error ? error : void 0;
|
|
35859
|
+
throw new ProvisioningError(`Failed to delete CodeCommit Repository ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
|
|
35860
|
+
}
|
|
35861
|
+
if (!deletedRepositoryId) {
|
|
35862
|
+
assertRegionMatch(await this.getClient().config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
|
|
35863
|
+
this.logger.debug(`CodeCommit Repository ${physicalId} does not exist, skipping deletion`);
|
|
35864
|
+
return;
|
|
35865
|
+
}
|
|
35866
|
+
this.logger.debug(`Successfully deleted CodeCommit Repository ${logicalId}`);
|
|
35867
|
+
}
|
|
35868
|
+
/**
|
|
35869
|
+
* Get repository attributes for Fn::GetAtt resolution.
|
|
35870
|
+
*
|
|
35871
|
+
* Supported: `Arn`, `CloneUrlHttp`, `CloneUrlSsh`, `Name`, `KmsKeyId`
|
|
35872
|
+
* (the CFn-documented attribute set).
|
|
35873
|
+
*/
|
|
35874
|
+
async getAttribute(physicalId, _resourceType, attributeName) {
|
|
35875
|
+
switch (attributeName) {
|
|
35876
|
+
case "Name": return physicalId;
|
|
35877
|
+
case "Arn":
|
|
35878
|
+
case "CloneUrlHttp":
|
|
35879
|
+
case "CloneUrlSsh":
|
|
35880
|
+
case "KmsKeyId": {
|
|
35881
|
+
const metadata = await this.getRepositoryMetadata(physicalId);
|
|
35882
|
+
return this.toAttributes(metadata)[attributeName];
|
|
35883
|
+
}
|
|
35884
|
+
default: return;
|
|
35885
|
+
}
|
|
35886
|
+
}
|
|
35887
|
+
/**
|
|
35888
|
+
* Adopt an existing CodeCommit repository into cdkd state.
|
|
35889
|
+
*
|
|
35890
|
+
* Lookup order:
|
|
35891
|
+
* 1. `--resource` override or `Properties.RepositoryName` → verify via
|
|
35892
|
+
* `GetRepository`.
|
|
35893
|
+
* 2. `ListRepositories` paginated, then `ListTagsForResource(arn)` per
|
|
35894
|
+
* repository to match `aws:cdk:path` (CodeCommit uses the
|
|
35895
|
+
* `Record<string, string>` tag-map shape).
|
|
35896
|
+
*/
|
|
35897
|
+
async import(input) {
|
|
35898
|
+
const explicit = resolveExplicitPhysicalId(input, "RepositoryName");
|
|
35899
|
+
if (explicit) try {
|
|
35900
|
+
const resp = await this.getClient().send(new GetRepositoryCommand({ repositoryName: explicit }));
|
|
35901
|
+
return resp.repositoryMetadata?.repositoryName ? {
|
|
35902
|
+
physicalId: explicit,
|
|
35903
|
+
attributes: this.toAttributes(resp.repositoryMetadata)
|
|
35904
|
+
} : null;
|
|
35905
|
+
} catch (err) {
|
|
35906
|
+
if (err instanceof RepositoryDoesNotExistException) return null;
|
|
35907
|
+
throw err;
|
|
35908
|
+
}
|
|
35909
|
+
if (!input.cdkPath) return null;
|
|
35910
|
+
let nextToken;
|
|
35911
|
+
do {
|
|
35912
|
+
const list = await this.getClient().send(new ListRepositoriesCommand({ ...nextToken && { nextToken } }));
|
|
35913
|
+
for (const repo of list.repositories ?? []) {
|
|
35914
|
+
if (!repo.repositoryName) continue;
|
|
35915
|
+
let metadata;
|
|
35916
|
+
try {
|
|
35917
|
+
metadata = await this.getRepositoryMetadata(repo.repositoryName);
|
|
35918
|
+
} catch (err) {
|
|
35919
|
+
if (err instanceof RepositoryDoesNotExistException) continue;
|
|
35920
|
+
throw err;
|
|
35921
|
+
}
|
|
35922
|
+
if (!metadata?.Arn) continue;
|
|
35923
|
+
if ((await this.getClient().send(new ListTagsForResourceCommand$18({ resourceArn: metadata.Arn }))).tags?.["aws:cdk:path"] === input.cdkPath) return {
|
|
35924
|
+
physicalId: repo.repositoryName,
|
|
35925
|
+
attributes: this.toAttributes(metadata)
|
|
35926
|
+
};
|
|
35927
|
+
}
|
|
35928
|
+
nextToken = list.nextToken;
|
|
35929
|
+
} while (nextToken);
|
|
35930
|
+
return null;
|
|
35931
|
+
}
|
|
35932
|
+
/**
|
|
35933
|
+
* Fetch the repository's metadata via `GetRepository`. Throws
|
|
35934
|
+
* `RepositoryDoesNotExistException` through to the caller.
|
|
35935
|
+
*/
|
|
35936
|
+
async getRepositoryMetadata(repositoryName) {
|
|
35937
|
+
return (await this.getClient().send(new GetRepositoryCommand({ repositoryName }))).repositoryMetadata;
|
|
35938
|
+
}
|
|
35939
|
+
};
|
|
35940
|
+
|
|
35166
35941
|
//#endregion
|
|
35167
35942
|
//#region src/provisioning/providers/dlm-lifecycle-policy-provider.ts
|
|
35168
35943
|
/**
|
|
@@ -35295,7 +36070,7 @@ var DLMLifecyclePolicyProvider = class {
|
|
|
35295
36070
|
if (tagsChanged && arn) {
|
|
35296
36071
|
const removedKeys = Object.keys(oldTags).filter((k) => !(k in newTags));
|
|
35297
36072
|
if (removedKeys.length > 0) {
|
|
35298
|
-
await this.getClient().send(new UntagResourceCommand$
|
|
36073
|
+
await this.getClient().send(new UntagResourceCommand$17({
|
|
35299
36074
|
ResourceArn: arn,
|
|
35300
36075
|
TagKeys: removedKeys
|
|
35301
36076
|
}));
|
|
@@ -35304,7 +36079,7 @@ var DLMLifecyclePolicyProvider = class {
|
|
|
35304
36079
|
const tagsToAdd = {};
|
|
35305
36080
|
for (const [k, v] of Object.entries(newTags)) if (oldTags[k] !== v) tagsToAdd[k] = v;
|
|
35306
36081
|
if (Object.keys(tagsToAdd).length > 0) {
|
|
35307
|
-
await this.getClient().send(new TagResourceCommand$
|
|
36082
|
+
await this.getClient().send(new TagResourceCommand$17({
|
|
35308
36083
|
ResourceArn: arn,
|
|
35309
36084
|
Tags: tagsToAdd
|
|
35310
36085
|
}));
|
|
@@ -35507,11 +36282,11 @@ var S3VectorsProvider = class {
|
|
|
35507
36282
|
}
|
|
35508
36283
|
if (!resourceArn) throw new ProvisioningError(`Could not resolve ARN for S3 VectorBucket ${logicalId} (${physicalId}); cannot apply tag update.`, resourceType, logicalId, physicalId);
|
|
35509
36284
|
try {
|
|
35510
|
-
if (Object.keys(toSet).length > 0) await this.getClient().send(new TagResourceCommand$
|
|
36285
|
+
if (Object.keys(toSet).length > 0) await this.getClient().send(new TagResourceCommand$18({
|
|
35511
36286
|
resourceArn,
|
|
35512
36287
|
tags: toSet
|
|
35513
36288
|
}));
|
|
35514
|
-
if (toRemove.length > 0) await this.getClient().send(new UntagResourceCommand$
|
|
36289
|
+
if (toRemove.length > 0) await this.getClient().send(new UntagResourceCommand$18({
|
|
35515
36290
|
resourceArn,
|
|
35516
36291
|
tagKeys: toRemove
|
|
35517
36292
|
}));
|
|
@@ -35639,7 +36414,7 @@ var S3VectorsProvider = class {
|
|
|
35639
36414
|
if (Object.keys(enc).length > 0) result["EncryptionConfiguration"] = enc;
|
|
35640
36415
|
}
|
|
35641
36416
|
if (bucket?.vectorBucketArn) try {
|
|
35642
|
-
result["Tags"] = normalizeAwsTagsToCfn((await this.getClient().send(new ListTagsForResourceCommand$
|
|
36417
|
+
result["Tags"] = normalizeAwsTagsToCfn((await this.getClient().send(new ListTagsForResourceCommand$19({ resourceArn: bucket.vectorBucketArn }))).tags);
|
|
35643
36418
|
} catch (err) {
|
|
35644
36419
|
this.logger.debug(`S3Vectors ListTagsForResource(${bucket.vectorBucketArn}) failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
35645
36420
|
result["Tags"] = [];
|
|
@@ -35675,7 +36450,7 @@ var S3VectorsProvider = class {
|
|
|
35675
36450
|
for (const bucket of list.vectorBuckets ?? []) {
|
|
35676
36451
|
if (!bucket.vectorBucketName || !bucket.vectorBucketArn) continue;
|
|
35677
36452
|
try {
|
|
35678
|
-
if ((await this.getClient().send(new ListTagsForResourceCommand$
|
|
36453
|
+
if ((await this.getClient().send(new ListTagsForResourceCommand$19({ resourceArn: bucket.vectorBucketArn }))).tags?.["aws:cdk:path"] === input.cdkPath) return {
|
|
35679
36454
|
physicalId: bucket.vectorBucketName,
|
|
35680
36455
|
attributes: {}
|
|
35681
36456
|
};
|
|
@@ -36026,7 +36801,7 @@ var S3TablesProvider = class {
|
|
|
36026
36801
|
await this.getClient().send(new DeleteTableBucketCommand({ tableBucketARN: physicalId }));
|
|
36027
36802
|
this.logger.debug(`Successfully deleted S3 Table Bucket ${logicalId}`);
|
|
36028
36803
|
} catch (error) {
|
|
36029
|
-
if (error instanceof NotFoundException$
|
|
36804
|
+
if (error instanceof NotFoundException$6) {
|
|
36030
36805
|
assertRegionMatch(await this.getClient().config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
|
|
36031
36806
|
this.logger.debug(`S3 Table Bucket ${physicalId} does not exist, skipping deletion`);
|
|
36032
36807
|
return;
|
|
@@ -36067,7 +36842,7 @@ var S3TablesProvider = class {
|
|
|
36067
36842
|
name: table.name
|
|
36068
36843
|
}));
|
|
36069
36844
|
} catch (error) {
|
|
36070
|
-
if (!(error instanceof NotFoundException$
|
|
36845
|
+
if (!(error instanceof NotFoundException$6)) throw error;
|
|
36071
36846
|
}
|
|
36072
36847
|
}
|
|
36073
36848
|
tableContinuationToken = tablesResult.continuationToken;
|
|
@@ -36079,7 +36854,7 @@ var S3TablesProvider = class {
|
|
|
36079
36854
|
namespace: namespaceName
|
|
36080
36855
|
}));
|
|
36081
36856
|
} catch (error) {
|
|
36082
|
-
if (!(error instanceof NotFoundException$
|
|
36857
|
+
if (!(error instanceof NotFoundException$6)) throw error;
|
|
36083
36858
|
}
|
|
36084
36859
|
}
|
|
36085
36860
|
namespaceContinuationToken = namespacesResult.continuationToken;
|
|
@@ -36121,7 +36896,7 @@ var S3TablesProvider = class {
|
|
|
36121
36896
|
}));
|
|
36122
36897
|
this.logger.debug(`Successfully deleted S3 Tables Namespace ${logicalId}`);
|
|
36123
36898
|
} catch (error) {
|
|
36124
|
-
if (error instanceof NotFoundException$
|
|
36899
|
+
if (error instanceof NotFoundException$6) {
|
|
36125
36900
|
assertRegionMatch(await this.getClient().config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
|
|
36126
36901
|
this.logger.debug(`S3 Tables Namespace ${physicalId} does not exist, skipping deletion`);
|
|
36127
36902
|
return;
|
|
@@ -36195,7 +36970,7 @@ var S3TablesProvider = class {
|
|
|
36195
36970
|
try {
|
|
36196
36971
|
bucket = await this.getClient().send(new GetTableBucketCommand({ tableBucketARN: physicalId }));
|
|
36197
36972
|
} catch (err) {
|
|
36198
|
-
if (err instanceof NotFoundException$
|
|
36973
|
+
if (err instanceof NotFoundException$6) return void 0;
|
|
36199
36974
|
throw err;
|
|
36200
36975
|
}
|
|
36201
36976
|
const result = {};
|
|
@@ -36224,7 +36999,7 @@ var S3TablesProvider = class {
|
|
|
36224
36999
|
name
|
|
36225
37000
|
}));
|
|
36226
37001
|
} catch (err) {
|
|
36227
|
-
if (err instanceof NotFoundException$
|
|
37002
|
+
if (err instanceof NotFoundException$6) return void 0;
|
|
36228
37003
|
throw err;
|
|
36229
37004
|
}
|
|
36230
37005
|
const tableNameValue = resp.name ?? name;
|
|
@@ -36271,7 +37046,7 @@ var S3TablesProvider = class {
|
|
|
36271
37046
|
attributes: {}
|
|
36272
37047
|
};
|
|
36273
37048
|
} catch (err) {
|
|
36274
|
-
if (err instanceof NotFoundException$
|
|
37049
|
+
if (err instanceof NotFoundException$6) return null;
|
|
36275
37050
|
throw err;
|
|
36276
37051
|
}
|
|
36277
37052
|
const desiredName = typeof input.properties?.["TableBucketName"] === "string" ? input.properties["TableBucketName"] : void 0;
|
|
@@ -36285,12 +37060,12 @@ var S3TablesProvider = class {
|
|
|
36285
37060
|
attributes: {}
|
|
36286
37061
|
};
|
|
36287
37062
|
if (input.cdkPath) try {
|
|
36288
|
-
if ((await this.getClient().send(new ListTagsForResourceCommand$
|
|
37063
|
+
if ((await this.getClient().send(new ListTagsForResourceCommand$20({ resourceArn: bucket.arn }))).tags?.["aws:cdk:path"] === input.cdkPath) return {
|
|
36289
37064
|
physicalId: bucket.arn,
|
|
36290
37065
|
attributes: {}
|
|
36291
37066
|
};
|
|
36292
37067
|
} catch (err) {
|
|
36293
|
-
if (err instanceof NotFoundException$
|
|
37068
|
+
if (err instanceof NotFoundException$6) continue;
|
|
36294
37069
|
throw err;
|
|
36295
37070
|
}
|
|
36296
37071
|
}
|
|
@@ -36319,7 +37094,7 @@ var S3TablesProvider = class {
|
|
|
36319
37094
|
attributes: {}
|
|
36320
37095
|
};
|
|
36321
37096
|
} catch (err) {
|
|
36322
|
-
if (err instanceof NotFoundException$
|
|
37097
|
+
if (err instanceof NotFoundException$6) return null;
|
|
36323
37098
|
throw err;
|
|
36324
37099
|
}
|
|
36325
37100
|
return {
|
|
@@ -36352,12 +37127,12 @@ var S3TablesProvider = class {
|
|
|
36352
37127
|
for (const table of tables.tables ?? []) {
|
|
36353
37128
|
if (!table.name || !table.tableARN) continue;
|
|
36354
37129
|
try {
|
|
36355
|
-
if ((await this.getClient().send(new ListTagsForResourceCommand$
|
|
37130
|
+
if ((await this.getClient().send(new ListTagsForResourceCommand$20({ resourceArn: table.tableARN }))).tags?.["aws:cdk:path"] === input.cdkPath) return {
|
|
36356
37131
|
physicalId: `${bucket.arn}|${namespaceName}|${table.name}`,
|
|
36357
37132
|
attributes: {}
|
|
36358
37133
|
};
|
|
36359
37134
|
} catch (err) {
|
|
36360
|
-
if (err instanceof NotFoundException$
|
|
37135
|
+
if (err instanceof NotFoundException$6) continue;
|
|
36361
37136
|
throw err;
|
|
36362
37137
|
}
|
|
36363
37138
|
}
|
|
@@ -36386,7 +37161,7 @@ var S3TablesProvider = class {
|
|
|
36386
37161
|
}));
|
|
36387
37162
|
this.logger.debug(`Successfully deleted S3 Tables Table ${logicalId}`);
|
|
36388
37163
|
} catch (error) {
|
|
36389
|
-
if (error instanceof NotFoundException$
|
|
37164
|
+
if (error instanceof NotFoundException$6) {
|
|
36390
37165
|
assertRegionMatch(await this.getClient().config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
|
|
36391
37166
|
this.logger.debug(`S3 Tables Table ${physicalId} does not exist, skipping deletion`);
|
|
36392
37167
|
return;
|
|
@@ -36436,7 +37211,7 @@ var S3TablesProvider = class {
|
|
|
36436
37211
|
name
|
|
36437
37212
|
}))).tableARN ?? null;
|
|
36438
37213
|
} catch (err) {
|
|
36439
|
-
if (err instanceof NotFoundException$
|
|
37214
|
+
if (err instanceof NotFoundException$6) return null;
|
|
36440
37215
|
throw err;
|
|
36441
37216
|
}
|
|
36442
37217
|
}
|
|
@@ -36448,7 +37223,7 @@ var S3TablesProvider = class {
|
|
|
36448
37223
|
*/
|
|
36449
37224
|
async readTagsBestEffort(resourceArn) {
|
|
36450
37225
|
try {
|
|
36451
|
-
const tags = (await this.getClient().send(new ListTagsForResourceCommand$
|
|
37226
|
+
const tags = (await this.getClient().send(new ListTagsForResourceCommand$20({ resourceArn }))).tags ?? {};
|
|
36452
37227
|
const out = [];
|
|
36453
37228
|
for (const [Key, Value] of Object.entries(tags)) out.push({
|
|
36454
37229
|
Key,
|
|
@@ -36502,7 +37277,7 @@ var S3TablesProvider = class {
|
|
|
36502
37277
|
const resourceArn = await this.lookupTableArn(tableBucketARN, namespace, name);
|
|
36503
37278
|
if (!resourceArn) throw new ProvisioningError(`applyTableTagsDiff: GetTable returned no tableARN for ${physicalId} — table is gone or state is out-of-sync. Refusing to silently drop the tag update (run 'cdkd state orphan ${logicalId}' to clean up if the table was deleted out-of-band).`, resourceType, logicalId, physicalId);
|
|
36504
37279
|
if (removedKeys.length > 0) try {
|
|
36505
|
-
await this.getClient().send(new UntagResourceCommand$
|
|
37280
|
+
await this.getClient().send(new UntagResourceCommand$19({
|
|
36506
37281
|
resourceArn,
|
|
36507
37282
|
tagKeys: removedKeys
|
|
36508
37283
|
}));
|
|
@@ -36511,7 +37286,7 @@ var S3TablesProvider = class {
|
|
|
36511
37286
|
throw new ProvisioningError(`applyTableTagsDiff: UntagResource failed for ${resourceArn} (keys: ${removedKeys.join(", ")}): ${err instanceof Error ? err.message : String(err)}. State has NOT been updated so the next deploy will retry the tag-diff.`, resourceType, logicalId, physicalId, cause);
|
|
36512
37287
|
}
|
|
36513
37288
|
if (Object.keys(upserts).length > 0) try {
|
|
36514
|
-
await this.getClient().send(new TagResourceCommand$
|
|
37289
|
+
await this.getClient().send(new TagResourceCommand$19({
|
|
36515
37290
|
resourceArn,
|
|
36516
37291
|
tags: upserts
|
|
36517
37292
|
}));
|
|
@@ -36543,7 +37318,7 @@ var S3TablesProvider = class {
|
|
|
36543
37318
|
if (removedKeys.length === 0 && Object.keys(upserts).length === 0) return;
|
|
36544
37319
|
const resourceArn = physicalId;
|
|
36545
37320
|
if (removedKeys.length > 0) try {
|
|
36546
|
-
await this.getClient().send(new UntagResourceCommand$
|
|
37321
|
+
await this.getClient().send(new UntagResourceCommand$19({
|
|
36547
37322
|
resourceArn,
|
|
36548
37323
|
tagKeys: removedKeys
|
|
36549
37324
|
}));
|
|
@@ -36552,7 +37327,7 @@ var S3TablesProvider = class {
|
|
|
36552
37327
|
throw new ProvisioningError(`applyTableBucketTagsDiff: UntagResource failed for ${resourceArn} (keys: ${removedKeys.join(", ")}): ${err instanceof Error ? err.message : String(err)}. State has NOT been updated so the next deploy will retry the tag-diff.`, resourceType, logicalId, physicalId, cause);
|
|
36553
37328
|
}
|
|
36554
37329
|
if (Object.keys(upserts).length > 0) try {
|
|
36555
|
-
await this.getClient().send(new TagResourceCommand$
|
|
37330
|
+
await this.getClient().send(new TagResourceCommand$19({
|
|
36556
37331
|
resourceArn,
|
|
36557
37332
|
tags: upserts
|
|
36558
37333
|
}));
|
|
@@ -38199,6 +38974,7 @@ function registerAllProviders(registry) {
|
|
|
38199
38974
|
registry.register("AWS::KinesisFirehose::DeliveryStream", new FirehoseProvider());
|
|
38200
38975
|
registry.register("AWS::CloudTrail::Trail", new CloudTrailProvider());
|
|
38201
38976
|
registry.register("AWS::CodeBuild::Project", new CodeBuildProvider());
|
|
38977
|
+
registry.register("AWS::CodeCommit::Repository", new CodeCommitRepositoryProvider());
|
|
38202
38978
|
registry.register("AWS::DLM::LifecyclePolicy", new DLMLifecyclePolicyProvider());
|
|
38203
38979
|
registry.register("AWS::S3Vectors::VectorBucket", new S3VectorsProvider());
|
|
38204
38980
|
registry.register("AWS::ECR::Repository", new ECRProvider());
|
|
@@ -38207,6 +38983,7 @@ function registerAllProviders(registry) {
|
|
|
38207
38983
|
registry.register("AWS::S3Tables::TableBucket", s3TablesProvider);
|
|
38208
38984
|
registry.register("AWS::S3Tables::Namespace", s3TablesProvider);
|
|
38209
38985
|
registry.register("AWS::S3Tables::Table", s3TablesProvider);
|
|
38986
|
+
registry.register("AWS::Budgets::Budget", new BudgetsBudgetProvider());
|
|
38210
38987
|
registry.register("AWS::CloudFormation::Stack", new NestedStackProvider());
|
|
38211
38988
|
registry.register("AWS::CloudFormation::WaitConditionHandle", new WaitConditionHandleProvider());
|
|
38212
38989
|
}
|
|
@@ -57472,7 +58249,7 @@ function reorderArgs(argv) {
|
|
|
57472
58249
|
async function main() {
|
|
57473
58250
|
installPipeCloseHandler();
|
|
57474
58251
|
const program = new Command();
|
|
57475
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
58252
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.241.0");
|
|
57476
58253
|
program.addCommand(createBootstrapCommand());
|
|
57477
58254
|
program.addCommand(createSynthCommand());
|
|
57478
58255
|
program.addCommand(createListCommand());
|