@go-to-k/cdkd 0.56.0 → 0.57.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -13565,7 +13565,7 @@ var SNSTopicProvider = class {
13565
13565
  if (properties["DeliveryStatusLogging"]) {
13566
13566
  const loggingConfigs = properties["DeliveryStatusLogging"];
13567
13567
  for (const config of loggingConfigs) {
13568
- const protocol = config["Protocol"];
13568
+ const protocol = normalizeDeliveryStatusProtocolOrThrow(config["Protocol"], logicalId);
13569
13569
  if (config["SuccessFeedbackRoleArn"]) {
13570
13570
  await this.snsClient.send(
13571
13571
  new SetTopicAttributesCommand({
@@ -13658,7 +13658,7 @@ var SNSTopicProvider = class {
13658
13658
  if (JSON.stringify(properties["DeliveryStatusLogging"]) !== JSON.stringify(previousProperties["DeliveryStatusLogging"])) {
13659
13659
  const loggingConfigs = properties["DeliveryStatusLogging"] || [];
13660
13660
  for (const config of loggingConfigs) {
13661
- const protocol = config["Protocol"];
13661
+ const protocol = normalizeDeliveryStatusProtocolOrThrow(config["Protocol"], logicalId);
13662
13662
  if (config["SuccessFeedbackRoleArn"]) {
13663
13663
  await this.snsClient.send(
13664
13664
  new SetTopicAttributesCommand({
@@ -13798,16 +13798,22 @@ var SNSTopicProvider = class {
13798
13798
  * FailureFeedbackRoleArn?}]`. Walks the known protocol prefix list
13799
13799
  * (`HTTP` / `HTTPS` / `SQS` / `Lambda` / `Firehose` / `Application`); a
13800
13800
  * protocol is included in the result iff at least one of its three
13801
- * sub-attributes is set on the topic. Entries are sorted by `Protocol`
13802
- * for stable positional compare (AWS does not preserve template order
13803
- * across `GetTopicAttributes` calls).
13801
+ * sub-attributes is set on the topic. Entries are sorted by canonical
13802
+ * PascalCase `Protocol` for stable positional compare (AWS does not
13803
+ * preserve template order across `GetTopicAttributes` calls).
13804
+ *
13805
+ * The emitted `Protocol` value preserves state's case when known
13806
+ * (CDK templates emit lowercase `'lambda'` / `'sqs'` / ...; AWS's
13807
+ * attribute prefix is PascalCase). Without case preservation the
13808
+ * comparator would fire false drift on every clean run for any
13809
+ * lowercase-`Protocol` template.
13804
13810
  *
13805
13811
  * `Subscription` is omitted because CDK manages it via separate
13806
13812
  * `AWS::SNS::Subscription` resources, not as a Topic property.
13807
13813
  *
13808
13814
  * Returns `undefined` when the topic is gone (`NotFoundException`).
13809
13815
  */
13810
- async readCurrentState(physicalId, _logicalId, _resourceType) {
13816
+ async readCurrentState(physicalId, _logicalId, _resourceType, properties) {
13811
13817
  let attrs;
13812
13818
  try {
13813
13819
  const resp = await this.snsClient.send(
@@ -13849,7 +13855,10 @@ var SNSTopicProvider = class {
13849
13855
  }
13850
13856
  }
13851
13857
  }
13852
- result["DeliveryStatusLogging"] = mapDeliveryStatusLogging(attrs);
13858
+ result["DeliveryStatusLogging"] = mapDeliveryStatusLogging(
13859
+ attrs,
13860
+ stateProtocolCaseMap(properties?.["DeliveryStatusLogging"])
13861
+ );
13853
13862
  try {
13854
13863
  const tagsResp = await this.snsClient.send(
13855
13864
  new ListTagsForResourceCommand({ ResourceArn: physicalId })
@@ -13938,7 +13947,55 @@ var SNS_DELIVERY_STATUS_PROTOCOLS = [
13938
13947
  "Lambda",
13939
13948
  "SQS"
13940
13949
  ];
13941
- function mapDeliveryStatusLogging(attrs) {
13950
+ function normalizeDeliveryStatusProtocol(input) {
13951
+ if (typeof input !== "string")
13952
+ return void 0;
13953
+ const lower = input.toLowerCase();
13954
+ switch (lower) {
13955
+ case "application":
13956
+ return "Application";
13957
+ case "firehose":
13958
+ return "Firehose";
13959
+ case "http":
13960
+ return "HTTP";
13961
+ case "https":
13962
+ return "HTTPS";
13963
+ case "lambda":
13964
+ return "Lambda";
13965
+ case "sqs":
13966
+ return "SQS";
13967
+ default:
13968
+ return void 0;
13969
+ }
13970
+ }
13971
+ function normalizeDeliveryStatusProtocolOrThrow(input, logicalId) {
13972
+ const normalized = normalizeDeliveryStatusProtocol(input);
13973
+ if (normalized === void 0) {
13974
+ throw new Error(
13975
+ `SNS topic ${logicalId}: unsupported DeliveryStatusLogging protocol ${JSON.stringify(input)}. Expected one of ${SNS_DELIVERY_STATUS_PROTOCOLS.join(", ")} (case-insensitive).`
13976
+ );
13977
+ }
13978
+ return normalized;
13979
+ }
13980
+ function stateProtocolCaseMap(stateLogging) {
13981
+ const map = /* @__PURE__ */ new Map();
13982
+ if (!Array.isArray(stateLogging))
13983
+ return map;
13984
+ for (const entry of stateLogging) {
13985
+ if (!entry || typeof entry !== "object")
13986
+ continue;
13987
+ const raw = entry["Protocol"];
13988
+ if (typeof raw !== "string")
13989
+ continue;
13990
+ const normalized = normalizeDeliveryStatusProtocol(raw);
13991
+ if (!normalized)
13992
+ continue;
13993
+ if (!map.has(normalized))
13994
+ map.set(normalized, raw);
13995
+ }
13996
+ return map;
13997
+ }
13998
+ function mapDeliveryStatusLogging(attrs, stateCaseMap = /* @__PURE__ */ new Map()) {
13942
13999
  const result = [];
13943
14000
  for (const protocol of SNS_DELIVERY_STATUS_PROTOCOLS) {
13944
14001
  const success = attrs[`${protocol}SuccessFeedbackRoleArn`];
@@ -13946,7 +14003,9 @@ function mapDeliveryStatusLogging(attrs) {
13946
14003
  const failure = attrs[`${protocol}FailureFeedbackRoleArn`];
13947
14004
  if (success === void 0 && sample === void 0 && failure === void 0)
13948
14005
  continue;
13949
- const entry = { Protocol: protocol };
14006
+ const entry = {
14007
+ Protocol: stateCaseMap.get(protocol) ?? protocol
14008
+ };
13950
14009
  if (success !== void 0)
13951
14010
  entry["SuccessFeedbackRoleArn"] = success;
13952
14011
  if (sample !== void 0)
@@ -27595,7 +27654,18 @@ var ECSProvider = class {
27595
27654
  let resp;
27596
27655
  try {
27597
27656
  resp = await this.getClient().send(
27598
- new DescribeClustersCommand({ clusters: [physicalId], include: ["TAGS"] })
27657
+ // AWS DescribeClusters omits `settings` / `configuration` from the
27658
+ // response unless they are explicitly requested via `include`. Without
27659
+ // SETTINGS / CONFIGURATIONS the readCurrentState round-trip silently
27660
+ // surfaces empty `ClusterSettings: []` even when the cluster has
27661
+ // containerInsights enabled — a console-side toggle then can't be
27662
+ // detected as drift because both the deploy-time observedProperties
27663
+ // baseline AND the drift-time AWS read would identically miss the
27664
+ // field. Discovered by the drift-revert integ test (PR #201).
27665
+ new DescribeClustersCommand({
27666
+ clusters: [physicalId],
27667
+ include: ["TAGS", "SETTINGS", "CONFIGURATIONS"]
27668
+ })
27599
27669
  );
27600
27670
  } catch {
27601
27671
  return void 0;
@@ -27859,6 +27929,10 @@ import {
27859
27929
  DeleteLoadBalancerCommand,
27860
27930
  DescribeLoadBalancersCommand as DescribeLoadBalancersCommand2,
27861
27931
  DescribeLoadBalancerAttributesCommand,
27932
+ ModifyLoadBalancerAttributesCommand,
27933
+ SetSubnetsCommand,
27934
+ SetSecurityGroupsCommand,
27935
+ SetIpAddressTypeCommand,
27862
27936
  CreateTargetGroupCommand,
27863
27937
  DeleteTargetGroupCommand,
27864
27938
  ModifyTargetGroupCommand,
@@ -27919,7 +27993,9 @@ var ELBv2Provider = class {
27919
27993
  "DefaultActions",
27920
27994
  "Port",
27921
27995
  "Protocol",
27922
- "SslPolicy"
27996
+ "SslPolicy",
27997
+ "AlpnPolicy",
27998
+ "MutualAuthentication"
27923
27999
  ])
27924
28000
  ]
27925
28001
  ]);
@@ -28027,7 +28103,6 @@ var ELBv2Provider = class {
28027
28103
  this.logger.debug(`Successfully created LoadBalancer ${logicalId}: ${lb.LoadBalancerArn}`);
28028
28104
  const lbAttributes = properties["LoadBalancerAttributes"];
28029
28105
  if (lbAttributes && lbAttributes.length > 0) {
28030
- const { ModifyLoadBalancerAttributesCommand } = await import("@aws-sdk/client-elastic-load-balancing-v2");
28031
28106
  await this.getClient().send(
28032
28107
  new ModifyLoadBalancerAttributesCommand({
28033
28108
  LoadBalancerArn: lb.LoadBalancerArn,
@@ -28063,42 +28138,95 @@ var ELBv2Provider = class {
28063
28138
  }
28064
28139
  }
28065
28140
  async updateLoadBalancer(logicalId, physicalId, _resourceType, properties, previousProperties) {
28066
- const newAttrs = properties["LoadBalancerAttributes"] ?? [];
28067
- const oldAttrs = previousProperties["LoadBalancerAttributes"] ?? [];
28068
- const stripAttrs = (p) => {
28069
- const { LoadBalancerAttributes: _, ...rest } = p;
28070
- return rest;
28141
+ const handledKeys = /* @__PURE__ */ new Set([
28142
+ "LoadBalancerAttributes",
28143
+ "Subnets",
28144
+ "SubnetMappings",
28145
+ "SecurityGroups",
28146
+ "IpAddressType",
28147
+ "Tags"
28148
+ ]);
28149
+ const stripHandled = (p) => {
28150
+ const out = {};
28151
+ for (const [k, v] of Object.entries(p)) {
28152
+ if (!handledKeys.has(k))
28153
+ out[k] = v;
28154
+ }
28155
+ return out;
28071
28156
  };
28072
- if (JSON.stringify(stripAttrs(properties)) !== JSON.stringify(stripAttrs(previousProperties))) {
28157
+ if (JSON.stringify(stripHandled(properties)) !== JSON.stringify(stripHandled(previousProperties))) {
28073
28158
  throw new ResourceUpdateNotSupportedError(
28074
28159
  "AWS::ElasticLoadBalancingV2::LoadBalancer",
28075
28160
  logicalId,
28076
- "ELBv2 LoadBalancer in-place updates are only supported for LoadBalancerAttributes; for Name / Type / Scheme / Subnets / SecurityGroups / IpAddressType / Tags, re-deploy with cdkd deploy --replace, or destroy + redeploy the stack"
28161
+ "ELBv2 LoadBalancer in-place updates are supported for LoadBalancerAttributes / Subnets / SubnetMappings / SecurityGroups / IpAddressType / Tags only; for Name / Type / Scheme, re-deploy with cdkd deploy --replace, or destroy + redeploy the stack"
28077
28162
  );
28078
28163
  }
28079
- const newMap = new Map(newAttrs.map((a) => [a.Key, a.Value]));
28080
- const oldMap = new Map(oldAttrs.map((a) => [a.Key, a.Value]));
28081
- const submitted = [];
28082
- for (const [k, v] of newMap) {
28083
- if (oldMap.get(k) !== v)
28084
- submitted.push({ Key: k, Value: v });
28085
- }
28086
- for (const [k] of oldMap) {
28087
- if (!newMap.has(k))
28088
- submitted.push({ Key: k, Value: "" });
28089
- }
28090
- if (submitted.length > 0) {
28091
- const { ModifyLoadBalancerAttributesCommand } = await import("@aws-sdk/client-elastic-load-balancing-v2");
28164
+ const newAttrs = properties["LoadBalancerAttributes"] ?? [];
28165
+ const oldAttrs = previousProperties["LoadBalancerAttributes"] ?? [];
28166
+ const newAttrMap = new Map(newAttrs.map((a) => [a.Key, a.Value]));
28167
+ const oldAttrMap = new Map(oldAttrs.map((a) => [a.Key, a.Value]));
28168
+ const submittedAttrs = [];
28169
+ for (const [k, v] of newAttrMap) {
28170
+ if (oldAttrMap.get(k) !== v)
28171
+ submittedAttrs.push({ Key: k, Value: v });
28172
+ }
28173
+ for (const [k] of oldAttrMap) {
28174
+ if (!newAttrMap.has(k))
28175
+ submittedAttrs.push({ Key: k, Value: "" });
28176
+ }
28177
+ if (submittedAttrs.length > 0) {
28092
28178
  await this.getClient().send(
28093
28179
  new ModifyLoadBalancerAttributesCommand({
28094
28180
  LoadBalancerArn: physicalId,
28095
- Attributes: submitted
28181
+ Attributes: submittedAttrs
28096
28182
  })
28097
28183
  );
28098
28184
  this.logger.debug(
28099
- `Applied ${submitted.length} LoadBalancerAttributes change(s) for ${logicalId}`
28185
+ `Applied ${submittedAttrs.length} LoadBalancerAttributes change(s) for ${logicalId}`
28186
+ );
28187
+ }
28188
+ const newSubnets = properties["Subnets"];
28189
+ const oldSubnets = previousProperties["Subnets"];
28190
+ const newMappings = properties["SubnetMappings"];
28191
+ const oldMappings = previousProperties["SubnetMappings"];
28192
+ const subnetsChanged = JSON.stringify(newSubnets) !== JSON.stringify(oldSubnets);
28193
+ const mappingsChanged = JSON.stringify(newMappings) !== JSON.stringify(oldMappings);
28194
+ if (subnetsChanged || mappingsChanged) {
28195
+ await this.getClient().send(
28196
+ new SetSubnetsCommand({
28197
+ LoadBalancerArn: physicalId,
28198
+ ...newMappings && newMappings.length > 0 ? { SubnetMappings: newMappings } : { Subnets: newSubnets }
28199
+ })
28200
+ );
28201
+ this.logger.debug(`Updated Subnets / SubnetMappings for ${logicalId}`);
28202
+ }
28203
+ const newSGs = properties["SecurityGroups"];
28204
+ const oldSGs = previousProperties["SecurityGroups"];
28205
+ if (JSON.stringify(newSGs) !== JSON.stringify(oldSGs)) {
28206
+ await this.getClient().send(
28207
+ new SetSecurityGroupsCommand({
28208
+ LoadBalancerArn: physicalId,
28209
+ SecurityGroups: newSGs ?? []
28210
+ })
28100
28211
  );
28212
+ this.logger.debug(`Updated SecurityGroups for ${logicalId}`);
28101
28213
  }
28214
+ const newIpType = properties["IpAddressType"];
28215
+ const oldIpType = previousProperties["IpAddressType"];
28216
+ if (newIpType !== void 0 && newIpType !== oldIpType) {
28217
+ await this.getClient().send(
28218
+ new SetIpAddressTypeCommand({
28219
+ LoadBalancerArn: physicalId,
28220
+ IpAddressType: newIpType
28221
+ })
28222
+ );
28223
+ this.logger.debug(`Updated IpAddressType for ${logicalId}`);
28224
+ }
28225
+ await this.applyTagDiff(
28226
+ physicalId,
28227
+ previousProperties["Tags"],
28228
+ properties["Tags"]
28229
+ );
28102
28230
  return { physicalId, wasReplaced: false };
28103
28231
  }
28104
28232
  async deleteLoadBalancer(logicalId, physicalId, resourceType, context) {
@@ -28270,6 +28398,8 @@ var ELBv2Provider = class {
28270
28398
  const certificates = this.convertCertificates(
28271
28399
  properties["Certificates"]
28272
28400
  );
28401
+ const alpnPolicy = properties["AlpnPolicy"];
28402
+ const mutualAuth = properties["MutualAuthentication"];
28273
28403
  const response = await this.getClient().send(
28274
28404
  new CreateListenerCommand({
28275
28405
  LoadBalancerArn: properties["LoadBalancerArn"],
@@ -28278,6 +28408,8 @@ var ELBv2Provider = class {
28278
28408
  SslPolicy: properties["SslPolicy"],
28279
28409
  DefaultActions: defaultActions ?? [],
28280
28410
  ...certificates && { Certificates: certificates },
28411
+ ...alpnPolicy && alpnPolicy.length > 0 && { AlpnPolicy: alpnPolicy },
28412
+ ...mutualAuth !== void 0 && { MutualAuthentication: mutualAuth },
28281
28413
  ...tags.length > 0 && { Tags: tags }
28282
28414
  })
28283
28415
  );
@@ -28312,6 +28444,8 @@ var ELBv2Provider = class {
28312
28444
  const certificates = this.convertCertificates(
28313
28445
  properties["Certificates"]
28314
28446
  );
28447
+ const alpnPolicy = properties["AlpnPolicy"];
28448
+ const mutualAuth = properties["MutualAuthentication"];
28315
28449
  await this.getClient().send(
28316
28450
  new ModifyListenerCommand({
28317
28451
  ListenerArn: physicalId,
@@ -28319,7 +28453,15 @@ var ELBv2Provider = class {
28319
28453
  Protocol: properties["Protocol"],
28320
28454
  SslPolicy: properties["SslPolicy"],
28321
28455
  ...defaultActions && { DefaultActions: defaultActions },
28322
- ...certificates && { Certificates: certificates }
28456
+ ...certificates && { Certificates: certificates },
28457
+ // AlpnPolicy is a TLS-listener-only field; only forward it
28458
+ // when the diff actually carries values (CFn template-side it
28459
+ // is an array of one entry). An empty array would be rejected
28460
+ // by AWS on non-TLS listeners.
28461
+ ...alpnPolicy && alpnPolicy.length > 0 && { AlpnPolicy: alpnPolicy },
28462
+ // MutualAuthentication is HTTPS-listener-only. Forward when
28463
+ // the user templated it; AWS will reject on non-HTTPS.
28464
+ ...mutualAuth !== void 0 && { MutualAuthentication: mutualAuth }
28323
28465
  })
28324
28466
  );
28325
28467
  await this.applyTagDiff(
@@ -28606,6 +28748,8 @@ var ELBv2Provider = class {
28606
28748
  result["DefaultActions"] = (listener.DefaultActions ?? []).map(
28607
28749
  (a) => a
28608
28750
  );
28751
+ result["AlpnPolicy"] = listener.AlpnPolicy ?? [];
28752
+ result["MutualAuthentication"] = listener.MutualAuthentication ?? {};
28609
28753
  await this.attachTags(result, physicalId);
28610
28754
  return result;
28611
28755
  }
@@ -32268,7 +32412,10 @@ var ServiceDiscoveryProvider = class {
32268
32412
  providerRegion = process.env["AWS_REGION"];
32269
32413
  logger = getLogger().child("ServiceDiscoveryProvider");
32270
32414
  handledProperties = /* @__PURE__ */ new Map([
32271
- ["AWS::ServiceDiscovery::PrivateDnsNamespace", /* @__PURE__ */ new Set(["Name", "Vpc", "Description", "Tags"])],
32415
+ [
32416
+ "AWS::ServiceDiscovery::PrivateDnsNamespace",
32417
+ /* @__PURE__ */ new Set(["Name", "Vpc", "Description", "Tags", "Properties"])
32418
+ ],
32272
32419
  [
32273
32420
  "AWS::ServiceDiscovery::Service",
32274
32421
  /* @__PURE__ */ new Set([
@@ -32370,13 +32517,18 @@ var ServiceDiscoveryProvider = class {
32370
32517
  logicalId
32371
32518
  );
32372
32519
  }
32520
+ const propsBag = properties["Properties"];
32521
+ const dnsProps = propsBag?.["DnsProperties"];
32522
+ const soa = dnsProps?.["SOA"];
32523
+ const inputProperties = soa?.TTL !== void 0 ? { DnsProperties: { SOA: { TTL: Number(soa.TTL) } } } : void 0;
32373
32524
  try {
32374
32525
  const response = await client.send(
32375
32526
  new CreatePrivateDnsNamespaceCommand({
32376
32527
  Name: name,
32377
32528
  Vpc: vpc,
32378
32529
  ...description && { Description: description },
32379
- ...tags && tags.length > 0 && { Tags: tags }
32530
+ ...tags && tags.length > 0 && { Tags: tags },
32531
+ ...inputProperties && { Properties: inputProperties }
32380
32532
  })
32381
32533
  );
32382
32534
  const operationId = response.OperationId;
@@ -32766,6 +32918,12 @@ var ServiceDiscoveryProvider = class {
32766
32918
  if (ns.Name !== void 0)
32767
32919
  result["Name"] = ns.Name;
32768
32920
  result["Description"] = ns.Description ?? "";
32921
+ const soa = ns.Properties?.DnsProperties?.SOA;
32922
+ if (soa?.TTL !== void 0) {
32923
+ result["Properties"] = { DnsProperties: { SOA: { TTL: soa.TTL } } };
32924
+ } else {
32925
+ result["Properties"] = {};
32926
+ }
32769
32927
  if (ns.Arn)
32770
32928
  await this.attachTags(result, ns.Arn);
32771
32929
  return result;
@@ -46176,7 +46334,7 @@ function reorderArgs(argv) {
46176
46334
  }
46177
46335
  async function main() {
46178
46336
  const program = new Command14();
46179
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.56.0");
46337
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.57.1");
46180
46338
  program.addCommand(createBootstrapCommand());
46181
46339
  program.addCommand(createSynthCommand());
46182
46340
  program.addCommand(createListCommand());