@go-to-k/cdkd 0.91.2 → 0.91.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli.js +401 -82
- package/dist/cli.js.map +2 -2
- package/dist/go-to-k-cdkd-0.91.4.tgz +0 -0
- package/package.json +1 -1
- package/dist/go-to-k-cdkd-0.91.2.tgz +0 -0
package/dist/cli.js
CHANGED
|
@@ -47837,22 +47837,32 @@ var ECSProvider = class {
|
|
|
47837
47837
|
essential: def["Essential"],
|
|
47838
47838
|
command: def["Command"],
|
|
47839
47839
|
entryPoint: def["EntryPoint"],
|
|
47840
|
-
environment:
|
|
47841
|
-
|
|
47842
|
-
|
|
47840
|
+
environment: this.convertEnvironment(
|
|
47841
|
+
def["Environment"]
|
|
47842
|
+
),
|
|
47843
|
+
environmentFiles: this.convertEnvironmentFiles(
|
|
47844
|
+
def["EnvironmentFiles"]
|
|
47845
|
+
),
|
|
47846
|
+
secrets: this.convertSecrets(def["Secrets"]),
|
|
47843
47847
|
portMappings: this.convertPortMappings(
|
|
47844
47848
|
def["PortMappings"]
|
|
47845
47849
|
),
|
|
47846
|
-
mountPoints:
|
|
47847
|
-
|
|
47848
|
-
|
|
47850
|
+
mountPoints: this.convertMountPoints(
|
|
47851
|
+
def["MountPoints"]
|
|
47852
|
+
),
|
|
47853
|
+
volumesFrom: this.convertVolumesFrom(
|
|
47854
|
+
def["VolumesFrom"]
|
|
47855
|
+
),
|
|
47856
|
+
dependsOn: this.convertDependsOn(
|
|
47857
|
+
def["DependsOn"]
|
|
47858
|
+
),
|
|
47849
47859
|
links: def["Links"],
|
|
47850
47860
|
workingDirectory: def["WorkingDirectory"],
|
|
47851
47861
|
disableNetworking: def["DisableNetworking"],
|
|
47852
47862
|
privileged: def["Privileged"],
|
|
47853
47863
|
readonlyRootFilesystem: def["ReadonlyRootFilesystem"],
|
|
47854
47864
|
user: def["User"],
|
|
47855
|
-
ulimits: def["Ulimits"],
|
|
47865
|
+
ulimits: this.convertUlimits(def["Ulimits"]),
|
|
47856
47866
|
logConfiguration: this.convertLogConfiguration(
|
|
47857
47867
|
def["LogConfiguration"]
|
|
47858
47868
|
),
|
|
@@ -47881,6 +47891,103 @@ var ECSProvider = class {
|
|
|
47881
47891
|
name: m["Name"]
|
|
47882
47892
|
}));
|
|
47883
47893
|
}
|
|
47894
|
+
/**
|
|
47895
|
+
* Convert CFn Environment (KeyValuePair) to ECS SDK format.
|
|
47896
|
+
* CFn template emits `{Name, Value}` (PascalCase); ECS SDK requires
|
|
47897
|
+
* `{name, value}` (camelCase). Pre-fix the cast `as KeyValuePair[]`
|
|
47898
|
+
* silently dropped both fields and AWS rejected RegisterTaskDefinition
|
|
47899
|
+
* with a generic null/empty validation error.
|
|
47900
|
+
*/
|
|
47901
|
+
convertEnvironment(entries) {
|
|
47902
|
+
if (!entries)
|
|
47903
|
+
return void 0;
|
|
47904
|
+
return entries.map((e) => ({
|
|
47905
|
+
name: e["Name"],
|
|
47906
|
+
value: e["Value"]
|
|
47907
|
+
}));
|
|
47908
|
+
}
|
|
47909
|
+
/**
|
|
47910
|
+
* Convert CFn EnvironmentFiles (S3-backed env-var files) to ECS SDK format.
|
|
47911
|
+
* CFn: `{Type, Value}` → SDK: `{type, value}`.
|
|
47912
|
+
*/
|
|
47913
|
+
convertEnvironmentFiles(entries) {
|
|
47914
|
+
if (!entries)
|
|
47915
|
+
return void 0;
|
|
47916
|
+
return entries.map((e) => ({
|
|
47917
|
+
type: e["Type"],
|
|
47918
|
+
value: e["Value"]
|
|
47919
|
+
}));
|
|
47920
|
+
}
|
|
47921
|
+
/**
|
|
47922
|
+
* Convert CFn Secrets to ECS SDK format.
|
|
47923
|
+
* CFn: `{Name, ValueFrom}` → SDK: `{name, valueFrom}`.
|
|
47924
|
+
* Same PascalCase→camelCase trap as convertEnvironment — discovered
|
|
47925
|
+
* end-to-end via the local-run-task-from-state integ on 2026-05-12
|
|
47926
|
+
* (issue #291 fixture).
|
|
47927
|
+
*/
|
|
47928
|
+
convertSecrets(entries) {
|
|
47929
|
+
if (!entries)
|
|
47930
|
+
return void 0;
|
|
47931
|
+
return entries.map((e) => ({
|
|
47932
|
+
name: e["Name"],
|
|
47933
|
+
valueFrom: e["ValueFrom"]
|
|
47934
|
+
}));
|
|
47935
|
+
}
|
|
47936
|
+
/**
|
|
47937
|
+
* Convert CFn MountPoints to ECS SDK format.
|
|
47938
|
+
* CFn: `{SourceVolume, ContainerPath, ReadOnly}` → SDK: `{sourceVolume,
|
|
47939
|
+
* containerPath, readOnly}`.
|
|
47940
|
+
*/
|
|
47941
|
+
convertMountPoints(entries) {
|
|
47942
|
+
if (!entries)
|
|
47943
|
+
return void 0;
|
|
47944
|
+
return entries.map((e) => ({
|
|
47945
|
+
sourceVolume: e["SourceVolume"],
|
|
47946
|
+
containerPath: e["ContainerPath"],
|
|
47947
|
+
readOnly: e["ReadOnly"]
|
|
47948
|
+
}));
|
|
47949
|
+
}
|
|
47950
|
+
/**
|
|
47951
|
+
* Convert CFn VolumesFrom to ECS SDK format.
|
|
47952
|
+
* CFn: `{SourceContainer, ReadOnly}` → SDK: `{sourceContainer, readOnly}`.
|
|
47953
|
+
*/
|
|
47954
|
+
convertVolumesFrom(entries) {
|
|
47955
|
+
if (!entries)
|
|
47956
|
+
return void 0;
|
|
47957
|
+
return entries.map((e) => ({
|
|
47958
|
+
sourceContainer: e["SourceContainer"],
|
|
47959
|
+
readOnly: e["ReadOnly"]
|
|
47960
|
+
}));
|
|
47961
|
+
}
|
|
47962
|
+
/**
|
|
47963
|
+
* Convert CFn DependsOn to ECS SDK format.
|
|
47964
|
+
* CFn: `{ContainerName, Condition}` → SDK: `{containerName, condition}`.
|
|
47965
|
+
* The pre-existing local-run-task-multi-container integ was relying
|
|
47966
|
+
* on ECS SDK being lenient about the dependsOn key casing on input,
|
|
47967
|
+
* but per the SDK type definition the input is camelCase so this
|
|
47968
|
+
* converter brings the actual wire shape in line with the contract.
|
|
47969
|
+
*/
|
|
47970
|
+
convertDependsOn(entries) {
|
|
47971
|
+
if (!entries)
|
|
47972
|
+
return void 0;
|
|
47973
|
+
return entries.map((e) => ({
|
|
47974
|
+
containerName: e["ContainerName"],
|
|
47975
|
+
condition: e["Condition"]
|
|
47976
|
+
}));
|
|
47977
|
+
}
|
|
47978
|
+
/**
|
|
47979
|
+
* Convert CFn Ulimits to ECS SDK format.
|
|
47980
|
+
* CFn: `{Name, SoftLimit, HardLimit}` → SDK: `{name, softLimit, hardLimit}`.
|
|
47981
|
+
*/
|
|
47982
|
+
convertUlimits(entries) {
|
|
47983
|
+
if (!entries)
|
|
47984
|
+
return void 0;
|
|
47985
|
+
return entries.map((e) => ({
|
|
47986
|
+
name: e["Name"],
|
|
47987
|
+
softLimit: e["SoftLimit"],
|
|
47988
|
+
hardLimit: e["HardLimit"]
|
|
47989
|
+
}));
|
|
47990
|
+
}
|
|
47884
47991
|
/**
|
|
47885
47992
|
* Convert CFn LogConfiguration to ECS SDK format
|
|
47886
47993
|
*/
|
|
@@ -70791,7 +70898,8 @@ function isLiteralEnvValue(value) {
|
|
|
70791
70898
|
}
|
|
70792
70899
|
|
|
70793
70900
|
// src/local/state-resolver.ts
|
|
70794
|
-
function substituteAgainstState(value,
|
|
70901
|
+
function substituteAgainstState(value, contextOrResources) {
|
|
70902
|
+
const context = isContext(contextOrResources) ? contextOrResources : { resources: contextOrResources };
|
|
70795
70903
|
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
70796
70904
|
return { kind: "literal", value };
|
|
70797
70905
|
}
|
|
@@ -70812,24 +70920,40 @@ function substituteAgainstState(value, resources) {
|
|
|
70812
70920
|
const intrinsic = keys[0];
|
|
70813
70921
|
const arg = obj[intrinsic];
|
|
70814
70922
|
if (intrinsic === "Ref") {
|
|
70815
|
-
return resolveRef(arg,
|
|
70923
|
+
return resolveRef(arg, context);
|
|
70816
70924
|
}
|
|
70817
70925
|
if (intrinsic === "Fn::GetAtt") {
|
|
70818
|
-
return resolveGetAtt(arg,
|
|
70926
|
+
return resolveGetAtt(arg, context);
|
|
70819
70927
|
}
|
|
70820
70928
|
if (intrinsic === "Fn::Sub") {
|
|
70821
|
-
return resolveSub(arg,
|
|
70929
|
+
return resolveSub(arg, context);
|
|
70930
|
+
}
|
|
70931
|
+
if (intrinsic === "Fn::Join") {
|
|
70932
|
+
return resolveJoin(arg, context);
|
|
70822
70933
|
}
|
|
70823
70934
|
return {
|
|
70824
70935
|
kind: "unresolved",
|
|
70825
|
-
reason: `unsupported intrinsic '${intrinsic}' (
|
|
70936
|
+
reason: `unsupported intrinsic '${intrinsic}' (supported: Ref, Fn::GetAtt, Fn::Sub, Fn::Join)`
|
|
70826
70937
|
};
|
|
70827
70938
|
}
|
|
70828
|
-
function
|
|
70939
|
+
function isContext(v) {
|
|
70940
|
+
if (typeof v !== "object" || v === null)
|
|
70941
|
+
return false;
|
|
70942
|
+
const r = v["resources"];
|
|
70943
|
+
if (r === void 0)
|
|
70944
|
+
return false;
|
|
70945
|
+
if (typeof r !== "object" || r === null)
|
|
70946
|
+
return false;
|
|
70947
|
+
return !("physicalId" in r);
|
|
70948
|
+
}
|
|
70949
|
+
function resolveRef(arg, context) {
|
|
70829
70950
|
if (typeof arg !== "string" || arg.length === 0) {
|
|
70830
70951
|
return { kind: "unresolved", reason: `Ref expects a non-empty logical ID, got ${typeof arg}` };
|
|
70831
70952
|
}
|
|
70832
|
-
|
|
70953
|
+
if (arg.startsWith("AWS::")) {
|
|
70954
|
+
return resolvePseudoParameter(arg, context.pseudoParameters);
|
|
70955
|
+
}
|
|
70956
|
+
const resource = context.resources[arg];
|
|
70833
70957
|
if (!resource) {
|
|
70834
70958
|
return {
|
|
70835
70959
|
kind: "unresolved",
|
|
@@ -70838,7 +70962,39 @@ function resolveRef(arg, resources) {
|
|
|
70838
70962
|
}
|
|
70839
70963
|
return { kind: "literal", value: resource.physicalId };
|
|
70840
70964
|
}
|
|
70841
|
-
function
|
|
70965
|
+
function resolvePseudoParameter(name, pseudo) {
|
|
70966
|
+
if (!pseudo) {
|
|
70967
|
+
return {
|
|
70968
|
+
kind: "unresolved",
|
|
70969
|
+
reason: `Ref '${name}': pseudo parameter not supplied (need --from-state context)`
|
|
70970
|
+
};
|
|
70971
|
+
}
|
|
70972
|
+
switch (name) {
|
|
70973
|
+
case "AWS::AccountId":
|
|
70974
|
+
if (pseudo.accountId !== void 0)
|
|
70975
|
+
return { kind: "literal", value: pseudo.accountId };
|
|
70976
|
+
break;
|
|
70977
|
+
case "AWS::Region":
|
|
70978
|
+
if (pseudo.region !== void 0)
|
|
70979
|
+
return { kind: "literal", value: pseudo.region };
|
|
70980
|
+
break;
|
|
70981
|
+
case "AWS::Partition":
|
|
70982
|
+
if (pseudo.partition !== void 0)
|
|
70983
|
+
return { kind: "literal", value: pseudo.partition };
|
|
70984
|
+
break;
|
|
70985
|
+
case "AWS::URLSuffix":
|
|
70986
|
+
if (pseudo.urlSuffix !== void 0)
|
|
70987
|
+
return { kind: "literal", value: pseudo.urlSuffix };
|
|
70988
|
+
break;
|
|
70989
|
+
default:
|
|
70990
|
+
return {
|
|
70991
|
+
kind: "unresolved",
|
|
70992
|
+
reason: `Ref '${name}': pseudo parameter not supported (supported: AWS::AccountId, AWS::Region, AWS::Partition, AWS::URLSuffix)`
|
|
70993
|
+
};
|
|
70994
|
+
}
|
|
70995
|
+
return { kind: "unresolved", reason: `Ref '${name}': pseudo parameter value not resolved` };
|
|
70996
|
+
}
|
|
70997
|
+
function resolveGetAtt(arg, context) {
|
|
70842
70998
|
let logicalId;
|
|
70843
70999
|
let attr;
|
|
70844
71000
|
if (Array.isArray(arg) && arg.length === 2 && typeof arg[0] === "string") {
|
|
@@ -70846,7 +71002,7 @@ function resolveGetAtt(arg, resources) {
|
|
|
70846
71002
|
if (typeof arg[1] !== "string") {
|
|
70847
71003
|
return {
|
|
70848
71004
|
kind: "unresolved",
|
|
70849
|
-
reason: `Fn::GetAtt's second arg must be a string attribute name, got ${typeof arg[1]} (nested intrinsics in attribute names are not supported
|
|
71005
|
+
reason: `Fn::GetAtt's second arg must be a string attribute name, got ${typeof arg[1]} (nested intrinsics in attribute names are not supported)`
|
|
70850
71006
|
};
|
|
70851
71007
|
}
|
|
70852
71008
|
attr = arg[1];
|
|
@@ -70866,7 +71022,7 @@ function resolveGetAtt(arg, resources) {
|
|
|
70866
71022
|
reason: `Fn::GetAtt expects [LogicalId, Attribute] or 'LogicalId.Attribute', got ${Array.isArray(arg) ? `array of length ${arg.length}` : typeof arg}`
|
|
70867
71023
|
};
|
|
70868
71024
|
}
|
|
70869
|
-
const resource = resources[logicalId];
|
|
71025
|
+
const resource = context.resources[logicalId];
|
|
70870
71026
|
if (!resource) {
|
|
70871
71027
|
return {
|
|
70872
71028
|
kind: "unresolved",
|
|
@@ -70885,7 +71041,7 @@ function resolveGetAtt(arg, resources) {
|
|
|
70885
71041
|
}
|
|
70886
71042
|
return { kind: "literal", value: JSON.stringify(cached) };
|
|
70887
71043
|
}
|
|
70888
|
-
function resolveSub(arg,
|
|
71044
|
+
function resolveSub(arg, context) {
|
|
70889
71045
|
let template;
|
|
70890
71046
|
let bindings = {};
|
|
70891
71047
|
if (typeof arg === "string") {
|
|
@@ -70910,7 +71066,18 @@ function resolveSub(arg, resources) {
|
|
|
70910
71066
|
if (resolutions.has(placeholder))
|
|
70911
71067
|
continue;
|
|
70912
71068
|
if (placeholder in bindings) {
|
|
70913
|
-
const sub = substituteAgainstState(bindings[placeholder],
|
|
71069
|
+
const sub = substituteAgainstState(bindings[placeholder], context);
|
|
71070
|
+
if (sub.kind !== "literal") {
|
|
71071
|
+
return {
|
|
71072
|
+
kind: "unresolved",
|
|
71073
|
+
reason: `Fn::Sub placeholder '\${${placeholder}}': ${sub.reason}`
|
|
71074
|
+
};
|
|
71075
|
+
}
|
|
71076
|
+
resolutions.set(placeholder, String(sub.value));
|
|
71077
|
+
continue;
|
|
71078
|
+
}
|
|
71079
|
+
if (placeholder.startsWith("AWS::")) {
|
|
71080
|
+
const sub = resolvePseudoParameter(placeholder, context.pseudoParameters);
|
|
70914
71081
|
if (sub.kind !== "literal") {
|
|
70915
71082
|
return {
|
|
70916
71083
|
kind: "unresolved",
|
|
@@ -70922,7 +71089,7 @@ function resolveSub(arg, resources) {
|
|
|
70922
71089
|
}
|
|
70923
71090
|
const dot = placeholder.indexOf(".");
|
|
70924
71091
|
if (dot === -1) {
|
|
70925
|
-
const sub = resolveRef(placeholder,
|
|
71092
|
+
const sub = resolveRef(placeholder, context);
|
|
70926
71093
|
if (sub.kind !== "literal") {
|
|
70927
71094
|
return {
|
|
70928
71095
|
kind: "unresolved",
|
|
@@ -70931,7 +71098,7 @@ function resolveSub(arg, resources) {
|
|
|
70931
71098
|
}
|
|
70932
71099
|
resolutions.set(placeholder, String(sub.value));
|
|
70933
71100
|
} else {
|
|
70934
|
-
const sub = resolveGetAtt(placeholder,
|
|
71101
|
+
const sub = resolveGetAtt(placeholder, context);
|
|
70935
71102
|
if (sub.kind !== "literal") {
|
|
70936
71103
|
return {
|
|
70937
71104
|
kind: "unresolved",
|
|
@@ -70946,17 +71113,45 @@ function resolveSub(arg, resources) {
|
|
|
70946
71113
|
});
|
|
70947
71114
|
return { kind: "literal", value: out };
|
|
70948
71115
|
}
|
|
70949
|
-
function
|
|
71116
|
+
function resolveJoin(arg, context) {
|
|
71117
|
+
if (!Array.isArray(arg) || arg.length !== 2 || !Array.isArray(arg[1])) {
|
|
71118
|
+
return {
|
|
71119
|
+
kind: "unresolved",
|
|
71120
|
+
reason: `Fn::Join expects [delimiter, [elements]], got ${Array.isArray(arg) ? `array of length ${arg.length}` : typeof arg}`
|
|
71121
|
+
};
|
|
71122
|
+
}
|
|
71123
|
+
const [delimiterRaw, elements] = arg;
|
|
71124
|
+
if (typeof delimiterRaw !== "string") {
|
|
71125
|
+
return {
|
|
71126
|
+
kind: "unresolved",
|
|
71127
|
+
reason: `Fn::Join delimiter must be a string, got ${typeof delimiterRaw}`
|
|
71128
|
+
};
|
|
71129
|
+
}
|
|
71130
|
+
const parts = [];
|
|
71131
|
+
for (let i = 0; i < elements.length; i += 1) {
|
|
71132
|
+
const sub = substituteAgainstState(elements[i], context);
|
|
71133
|
+
if (sub.kind !== "literal") {
|
|
71134
|
+
return {
|
|
71135
|
+
kind: "unresolved",
|
|
71136
|
+
reason: `Fn::Join element [${i}]: ${sub.reason}`
|
|
71137
|
+
};
|
|
71138
|
+
}
|
|
71139
|
+
parts.push(String(sub.value));
|
|
71140
|
+
}
|
|
71141
|
+
return { kind: "literal", value: parts.join(delimiterRaw) };
|
|
71142
|
+
}
|
|
71143
|
+
function substituteEnvVarsFromState(templateEnv, contextOrResources) {
|
|
70950
71144
|
const env = {};
|
|
70951
71145
|
const audit = { resolvedKeys: [], unresolved: [] };
|
|
70952
71146
|
if (!templateEnv)
|
|
70953
71147
|
return { env, audit };
|
|
71148
|
+
const context = isContext(contextOrResources) ? contextOrResources : { resources: contextOrResources };
|
|
70954
71149
|
for (const [key, value] of Object.entries(templateEnv)) {
|
|
70955
71150
|
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
70956
71151
|
env[key] = value;
|
|
70957
71152
|
continue;
|
|
70958
71153
|
}
|
|
70959
|
-
const result = substituteAgainstState(value,
|
|
71154
|
+
const result = substituteAgainstState(value, context);
|
|
70960
71155
|
if (result.kind === "literal") {
|
|
70961
71156
|
env[key] = result.value;
|
|
70962
71157
|
audit.resolvedKeys.push(key);
|
|
@@ -77063,6 +77258,7 @@ function detectEcsImageResolutionNeeds(stack) {
|
|
|
77063
77258
|
const resources = stack.template.Resources ?? {};
|
|
77064
77259
|
let needsPseudoParameters = false;
|
|
77065
77260
|
let needsStateResources = false;
|
|
77261
|
+
let needsEnvOrSecretSubstitution = false;
|
|
77066
77262
|
for (const res of Object.values(resources)) {
|
|
77067
77263
|
if (res.Type !== "AWS::ECS::TaskDefinition")
|
|
77068
77264
|
continue;
|
|
@@ -77071,19 +77267,42 @@ function detectEcsImageResolutionNeeds(stack) {
|
|
|
77071
77267
|
for (const c of containers) {
|
|
77072
77268
|
if (!c || typeof c !== "object")
|
|
77073
77269
|
continue;
|
|
77074
|
-
const
|
|
77270
|
+
const co = c;
|
|
77271
|
+
const image = co["Image"];
|
|
77075
77272
|
const need = inspectImageForSubstitutions(image, resources);
|
|
77076
77273
|
if (need.pseudo)
|
|
77077
77274
|
needsPseudoParameters = true;
|
|
77078
77275
|
if (need.state)
|
|
77079
77276
|
needsStateResources = true;
|
|
77080
|
-
if (
|
|
77081
|
-
|
|
77277
|
+
if (containerHasIntrinsicEnvOrSecret(co))
|
|
77278
|
+
needsEnvOrSecretSubstitution = true;
|
|
77279
|
+
}
|
|
77280
|
+
}
|
|
77281
|
+
return { needsPseudoParameters, needsStateResources, needsEnvOrSecretSubstitution };
|
|
77282
|
+
}
|
|
77283
|
+
function containerHasIntrinsicEnvOrSecret(c) {
|
|
77284
|
+
const env = c["Environment"];
|
|
77285
|
+
if (Array.isArray(env)) {
|
|
77286
|
+
for (const entry of env) {
|
|
77287
|
+
if (!entry || typeof entry !== "object")
|
|
77288
|
+
continue;
|
|
77289
|
+
const v = entry["Value"];
|
|
77290
|
+
if (v !== void 0 && typeof v !== "string" && typeof v !== "number" && typeof v !== "boolean") {
|
|
77291
|
+
return true;
|
|
77292
|
+
}
|
|
77082
77293
|
}
|
|
77083
|
-
if (needsPseudoParameters && needsStateResources)
|
|
77084
|
-
break;
|
|
77085
77294
|
}
|
|
77086
|
-
|
|
77295
|
+
const secrets = c["Secrets"];
|
|
77296
|
+
if (Array.isArray(secrets)) {
|
|
77297
|
+
for (const entry of secrets) {
|
|
77298
|
+
if (!entry || typeof entry !== "object")
|
|
77299
|
+
continue;
|
|
77300
|
+
const v = entry["ValueFrom"];
|
|
77301
|
+
if (v !== void 0 && typeof v !== "string")
|
|
77302
|
+
return true;
|
|
77303
|
+
}
|
|
77304
|
+
}
|
|
77305
|
+
return false;
|
|
77087
77306
|
}
|
|
77088
77307
|
function inspectImageForSubstitutions(image, resources) {
|
|
77089
77308
|
if (!image || typeof image !== "object")
|
|
@@ -77284,6 +77503,11 @@ function extractTaskDefinitionProperties(stack, logicalId, resource, context) {
|
|
|
77284
77503
|
const containers = rawContainers.map(
|
|
77285
77504
|
(c, idx) => parseContainerDefinition(c, idx, logicalId, resources, stack, context)
|
|
77286
77505
|
);
|
|
77506
|
+
for (const ctr of containers) {
|
|
77507
|
+
for (const w of ctr.warnings) {
|
|
77508
|
+
warnings.push(`Container '${ctr.name}': ${w}`);
|
|
77509
|
+
}
|
|
77510
|
+
}
|
|
77287
77511
|
const rawVolumes = props["Volumes"];
|
|
77288
77512
|
const volumes = Array.isArray(rawVolumes) ? rawVolumes.map((v, idx) => parseVolume(v, idx, logicalId)) : [];
|
|
77289
77513
|
const containerNames = new Set(containers.map((c) => c.name));
|
|
@@ -77345,7 +77569,9 @@ function parseContainerDefinition(raw, idx, taskLogicalId, resources, stack, con
|
|
|
77345
77569
|
const command = pickStringArray2(c["Command"]);
|
|
77346
77570
|
const entryPoint = pickStringArray2(c["EntryPoint"]);
|
|
77347
77571
|
const workingDirectory = pickString(c["WorkingDirectory"]);
|
|
77572
|
+
const subContext = buildSubstitutionContextFromImageContext(context);
|
|
77348
77573
|
const environment = {};
|
|
77574
|
+
const droppedEnvKeys = [];
|
|
77349
77575
|
if (Array.isArray(c["Environment"])) {
|
|
77350
77576
|
for (const entry of c["Environment"]) {
|
|
77351
77577
|
if (!entry || typeof entry !== "object")
|
|
@@ -77357,19 +77583,54 @@ function parseContainerDefinition(raw, idx, taskLogicalId, resources, stack, con
|
|
|
77357
77583
|
continue;
|
|
77358
77584
|
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
77359
77585
|
environment[key] = String(value);
|
|
77586
|
+
continue;
|
|
77587
|
+
}
|
|
77588
|
+
if (subContext) {
|
|
77589
|
+
const sub = substituteAgainstState(value, subContext);
|
|
77590
|
+
if (sub.kind === "literal") {
|
|
77591
|
+
environment[key] = String(sub.value);
|
|
77592
|
+
continue;
|
|
77593
|
+
}
|
|
77594
|
+
droppedEnvKeys.push({ key, reason: sub.reason });
|
|
77595
|
+
} else {
|
|
77596
|
+
droppedEnvKeys.push({
|
|
77597
|
+
key,
|
|
77598
|
+
reason: "intrinsic-valued; pass --from-state to substitute against deployed state"
|
|
77599
|
+
});
|
|
77360
77600
|
}
|
|
77361
77601
|
}
|
|
77362
77602
|
}
|
|
77363
77603
|
const secrets = [];
|
|
77604
|
+
const droppedSecretKeys = [];
|
|
77364
77605
|
if (Array.isArray(c["Secrets"])) {
|
|
77365
77606
|
for (const entry of c["Secrets"]) {
|
|
77366
77607
|
if (!entry || typeof entry !== "object")
|
|
77367
77608
|
continue;
|
|
77368
77609
|
const e = entry;
|
|
77369
77610
|
const sName = pickString(e["Name"]);
|
|
77370
|
-
const
|
|
77371
|
-
if (sName
|
|
77372
|
-
|
|
77611
|
+
const valueFromRaw = e["ValueFrom"];
|
|
77612
|
+
if (!sName)
|
|
77613
|
+
continue;
|
|
77614
|
+
if (typeof valueFromRaw === "string" && valueFromRaw.length > 0) {
|
|
77615
|
+
secrets.push({ name: sName, valueFrom: valueFromRaw });
|
|
77616
|
+
continue;
|
|
77617
|
+
}
|
|
77618
|
+
if (subContext) {
|
|
77619
|
+
const sub = substituteAgainstState(valueFromRaw, subContext);
|
|
77620
|
+
if (sub.kind === "literal" && typeof sub.value === "string" && sub.value.length > 0) {
|
|
77621
|
+
secrets.push({ name: sName, valueFrom: sub.value });
|
|
77622
|
+
continue;
|
|
77623
|
+
}
|
|
77624
|
+
droppedSecretKeys.push({
|
|
77625
|
+
key: sName,
|
|
77626
|
+
reason: sub.kind === "literal" ? "resolved to non-string / empty value" : sub.reason
|
|
77627
|
+
});
|
|
77628
|
+
} else {
|
|
77629
|
+
droppedSecretKeys.push({
|
|
77630
|
+
key: sName,
|
|
77631
|
+
reason: "intrinsic-valued ValueFrom; pass --from-state to resolve the deployed ARN"
|
|
77632
|
+
});
|
|
77633
|
+
}
|
|
77373
77634
|
}
|
|
77374
77635
|
}
|
|
77375
77636
|
const portMappings = [];
|
|
@@ -77459,6 +77720,13 @@ function parseContainerDefinition(raw, idx, taskLogicalId, resources, stack, con
|
|
|
77459
77720
|
ulimits.push({ name: uName, softLimit: soft, hardLimit: hard });
|
|
77460
77721
|
}
|
|
77461
77722
|
}
|
|
77723
|
+
const warnings = [];
|
|
77724
|
+
for (const d of droppedEnvKeys) {
|
|
77725
|
+
warnings.push(`Environment '${d.key}' dropped: ${d.reason}`);
|
|
77726
|
+
}
|
|
77727
|
+
for (const d of droppedSecretKeys) {
|
|
77728
|
+
warnings.push(`Secret '${d.key}' dropped: ${d.reason}`);
|
|
77729
|
+
}
|
|
77462
77730
|
const out = {
|
|
77463
77731
|
name,
|
|
77464
77732
|
image,
|
|
@@ -77469,7 +77737,8 @@ function parseContainerDefinition(raw, idx, taskLogicalId, resources, stack, con
|
|
|
77469
77737
|
dependsOn,
|
|
77470
77738
|
links,
|
|
77471
77739
|
essential,
|
|
77472
|
-
ulimits
|
|
77740
|
+
ulimits,
|
|
77741
|
+
warnings
|
|
77473
77742
|
};
|
|
77474
77743
|
if (command !== void 0)
|
|
77475
77744
|
out.command = command;
|
|
@@ -77487,6 +77756,15 @@ function parseContainerDefinition(raw, idx, taskLogicalId, resources, stack, con
|
|
|
77487
77756
|
out.readonlyRootFilesystem = readonlyRootFilesystem;
|
|
77488
77757
|
return out;
|
|
77489
77758
|
}
|
|
77759
|
+
function buildSubstitutionContextFromImageContext(context) {
|
|
77760
|
+
if (!context?.stateResources)
|
|
77761
|
+
return void 0;
|
|
77762
|
+
const subContext = { resources: context.stateResources };
|
|
77763
|
+
if (context.pseudoParameters) {
|
|
77764
|
+
subContext.pseudoParameters = { ...context.pseudoParameters };
|
|
77765
|
+
}
|
|
77766
|
+
return subContext;
|
|
77767
|
+
}
|
|
77490
77768
|
function parseContainerImage(raw, containerName, taskLogicalId, resources, _stack, context) {
|
|
77491
77769
|
const getAttImage = tryResolveImageGetAtt(raw, resources, context);
|
|
77492
77770
|
if (getAttImage) {
|
|
@@ -78681,14 +78959,16 @@ async function buildEcsImageResolutionContext(target, stacks, options) {
|
|
|
78681
78959
|
if (!candidate)
|
|
78682
78960
|
return void 0;
|
|
78683
78961
|
const needs = detectEcsImageResolutionNeeds(candidate);
|
|
78684
|
-
if (!needs.needsPseudoParameters && !needs.needsStateResources)
|
|
78962
|
+
if (!needs.needsPseudoParameters && !needs.needsStateResources && !needs.needsEnvOrSecretSubstitution) {
|
|
78685
78963
|
return void 0;
|
|
78964
|
+
}
|
|
78686
78965
|
const ctx = {};
|
|
78687
|
-
|
|
78966
|
+
const wantsPseudoForEnvOrSecret = options.fromState && needs.needsEnvOrSecretSubstitution;
|
|
78967
|
+
if (needs.needsPseudoParameters || wantsPseudoForEnvOrSecret) {
|
|
78688
78968
|
const region = options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? candidate.region;
|
|
78689
78969
|
if (!region) {
|
|
78690
78970
|
logger.warn(
|
|
78691
|
-
"
|
|
78971
|
+
"Resolver references ${AWS::Region} but cdkd could not determine the target region. Pass --region, set AWS_REGION, or declare env.region on the CDK stack."
|
|
78692
78972
|
);
|
|
78693
78973
|
}
|
|
78694
78974
|
let accountId;
|
|
@@ -78696,7 +78976,7 @@ async function buildEcsImageResolutionContext(target, stacks, options) {
|
|
|
78696
78976
|
accountId = await resolveCallerAccountId(region);
|
|
78697
78977
|
} catch (err) {
|
|
78698
78978
|
logger.warn(
|
|
78699
|
-
`
|
|
78979
|
+
`Resolver needs \${AWS::AccountId} but STS GetCallerIdentity failed: ${err instanceof Error ? err.message : String(err)}. Substitution will be skipped; affected env / secret entries will be dropped with per-key warnings.`
|
|
78700
78980
|
);
|
|
78701
78981
|
}
|
|
78702
78982
|
const partitionAndSuffix = region ? derivePartitionAndUrlSuffix(region) : void 0;
|
|
@@ -78709,7 +78989,8 @@ async function buildEcsImageResolutionContext(target, stacks, options) {
|
|
|
78709
78989
|
}
|
|
78710
78990
|
};
|
|
78711
78991
|
}
|
|
78712
|
-
|
|
78992
|
+
const wantsState = needs.needsStateResources || needs.needsEnvOrSecretSubstitution;
|
|
78993
|
+
if (options.fromState && wantsState) {
|
|
78713
78994
|
const loaded = await loadStateForStack(candidate.stackName, candidate.region, {
|
|
78714
78995
|
...options.stackRegion !== void 0 && { stackRegion: options.stackRegion },
|
|
78715
78996
|
...options.stateBucket !== void 0 && { stateBucket: options.stateBucket },
|
|
@@ -78724,6 +79005,10 @@ async function buildEcsImageResolutionContext(target, stacks, options) {
|
|
|
78724
79005
|
logger.warn(
|
|
78725
79006
|
"Container Image references a same-stack AWS::ECR::Repository. Pass --from-state to substitute the deployed repository URI (requires the stack to have been deployed via cdkd deploy). Otherwise the resolver will surface its existing error."
|
|
78726
79007
|
);
|
|
79008
|
+
} else if (!options.fromState && needs.needsEnvOrSecretSubstitution) {
|
|
79009
|
+
logger.warn(
|
|
79010
|
+
"Container Environment / Secrets entries contain CloudFormation intrinsics (Ref / Fn::GetAtt / Fn::Sub / Fn::Join). Pass --from-state to substitute them against the deployed cdkd state. Without --from-state these entries are dropped (per-key warnings will follow)."
|
|
79011
|
+
);
|
|
78727
79012
|
}
|
|
78728
79013
|
return ctx;
|
|
78729
79014
|
}
|
|
@@ -79330,6 +79615,7 @@ import { Command as Command17 } from "commander";
|
|
|
79330
79615
|
import {
|
|
79331
79616
|
CreateChangeSetCommand,
|
|
79332
79617
|
DescribeChangeSetCommand,
|
|
79618
|
+
DescribeStackEventsCommand,
|
|
79333
79619
|
ExecuteChangeSetCommand,
|
|
79334
79620
|
DescribeStacksCommand as DescribeStacksCommand2,
|
|
79335
79621
|
DescribeTypeCommand,
|
|
@@ -79346,10 +79632,13 @@ var NEVER_IMPORTABLE_TYPES = /* @__PURE__ */ new Set([
|
|
|
79346
79632
|
function isNeverImportableType(resourceType) {
|
|
79347
79633
|
if (NEVER_IMPORTABLE_TYPES.has(resourceType))
|
|
79348
79634
|
return true;
|
|
79349
|
-
if (resourceType
|
|
79635
|
+
if (isCustomResourceType(resourceType))
|
|
79350
79636
|
return true;
|
|
79351
79637
|
return false;
|
|
79352
79638
|
}
|
|
79639
|
+
function isCustomResourceType(resourceType) {
|
|
79640
|
+
return resourceType === "AWS::CloudFormation::CustomResource" || resourceType.startsWith("Custom::");
|
|
79641
|
+
}
|
|
79353
79642
|
var PRIMARY_IDENTIFIER_FALLBACK = {
|
|
79354
79643
|
"AWS::S3::Bucket": "BucketName",
|
|
79355
79644
|
"AWS::IAM::Role": "RoleName",
|
|
@@ -79613,6 +79902,12 @@ async function exportCommand(stackArg, options) {
|
|
|
79613
79902
|
);
|
|
79614
79903
|
}
|
|
79615
79904
|
const phase1Template = filterTemplateForImport(template, phase1Imports);
|
|
79905
|
+
const injectedCount = injectDeletionPolicyForImport(phase1Template);
|
|
79906
|
+
if (injectedCount > 0) {
|
|
79907
|
+
logger.info(
|
|
79908
|
+
`Injected DeletionPolicy: Retain on ${injectedCount} resource(s) without an explicit DeletionPolicy (required by CFn IMPORT). The first \`cdk deploy\` after export will reset each to your CDK-declared value.`
|
|
79909
|
+
);
|
|
79910
|
+
}
|
|
79616
79911
|
await executeImportChangeSet(
|
|
79617
79912
|
awsClients.cloudFormation,
|
|
79618
79913
|
cfnStackName,
|
|
@@ -79744,7 +80039,7 @@ async function assertCfnStackAbsent(cfnClient, stackName) {
|
|
|
79744
80039
|
}
|
|
79745
80040
|
}
|
|
79746
80041
|
function isPhase2CreatableType(resourceType) {
|
|
79747
|
-
return resourceType
|
|
80042
|
+
return isCustomResourceType(resourceType);
|
|
79748
80043
|
}
|
|
79749
80044
|
async function buildImportPlan(state, template, cfnClient) {
|
|
79750
80045
|
const templateResources = template["Resources"];
|
|
@@ -79926,32 +80221,49 @@ function resolveTemplateParameters(template, userOverrides) {
|
|
|
79926
80221
|
}
|
|
79927
80222
|
return { parameters, missing };
|
|
79928
80223
|
}
|
|
80224
|
+
function injectDeletionPolicyForImport(template) {
|
|
80225
|
+
const resources = template["Resources"];
|
|
80226
|
+
if (!resources || typeof resources !== "object" || Array.isArray(resources)) {
|
|
80227
|
+
return 0;
|
|
80228
|
+
}
|
|
80229
|
+
let injected = 0;
|
|
80230
|
+
for (const [, resource] of Object.entries(resources)) {
|
|
80231
|
+
if (!resource || typeof resource !== "object" || Array.isArray(resource))
|
|
80232
|
+
continue;
|
|
80233
|
+
const r = resource;
|
|
80234
|
+
if (r["DeletionPolicy"] === void 0) {
|
|
80235
|
+
r["DeletionPolicy"] = "Retain";
|
|
80236
|
+
injected++;
|
|
80237
|
+
}
|
|
80238
|
+
}
|
|
80239
|
+
return injected;
|
|
80240
|
+
}
|
|
79929
80241
|
function filterTemplateForImport(template, plan) {
|
|
79930
|
-
const allow = new
|
|
80242
|
+
const allow = new Map(plan.map((p) => [p.logicalId, p]));
|
|
79931
80243
|
const original = template["Resources"];
|
|
79932
80244
|
const filteredResources = {};
|
|
79933
80245
|
for (const [logicalId, resource] of Object.entries(original)) {
|
|
79934
|
-
|
|
79935
|
-
|
|
79936
|
-
|
|
80246
|
+
const entry = allow.get(logicalId);
|
|
80247
|
+
if (!entry)
|
|
80248
|
+
continue;
|
|
80249
|
+
filteredResources[logicalId] = overlayResourceIdentifierOnProperties(resource, entry);
|
|
79937
80250
|
}
|
|
79938
80251
|
const result = { ...template, Resources: filteredResources };
|
|
79939
|
-
|
|
79940
|
-
if (outputs && typeof outputs === "object" && !Array.isArray(outputs)) {
|
|
79941
|
-
const filteredOutputs = {};
|
|
79942
|
-
for (const [name, output] of Object.entries(outputs)) {
|
|
79943
|
-
if (referencesOnly(output, allow)) {
|
|
79944
|
-
filteredOutputs[name] = output;
|
|
79945
|
-
}
|
|
79946
|
-
}
|
|
79947
|
-
if (Object.keys(filteredOutputs).length > 0) {
|
|
79948
|
-
result["Outputs"] = filteredOutputs;
|
|
79949
|
-
} else {
|
|
79950
|
-
delete result["Outputs"];
|
|
79951
|
-
}
|
|
79952
|
-
}
|
|
80252
|
+
delete result["Outputs"];
|
|
79953
80253
|
return result;
|
|
79954
80254
|
}
|
|
80255
|
+
function overlayResourceIdentifierOnProperties(resource, entry) {
|
|
80256
|
+
if (!resource || typeof resource !== "object" || Array.isArray(resource)) {
|
|
80257
|
+
return resource;
|
|
80258
|
+
}
|
|
80259
|
+
const r = resource;
|
|
80260
|
+
const existingProperties = r["Properties"];
|
|
80261
|
+
const properties = existingProperties && typeof existingProperties === "object" && !Array.isArray(existingProperties) ? { ...existingProperties } : {};
|
|
80262
|
+
for (const [field, value] of Object.entries(entry.resourceIdentifier)) {
|
|
80263
|
+
properties[field] = value;
|
|
80264
|
+
}
|
|
80265
|
+
return { ...r, Properties: properties };
|
|
80266
|
+
}
|
|
79955
80267
|
function reportDriftBaselineGaps(state, logger) {
|
|
79956
80268
|
const entries = Object.entries(state.resources ?? {});
|
|
79957
80269
|
if (entries.length === 0)
|
|
@@ -80022,29 +80334,6 @@ function walkForGetStackOutput(node, path3, emit) {
|
|
|
80022
80334
|
walkForGetStackOutput(value, path3 ? `${path3}.${key}` : key, emit);
|
|
80023
80335
|
}
|
|
80024
80336
|
}
|
|
80025
|
-
function referencesOnly(node, allow) {
|
|
80026
|
-
if (!node || typeof node !== "object")
|
|
80027
|
-
return true;
|
|
80028
|
-
if (Array.isArray(node)) {
|
|
80029
|
-
return node.every((item) => referencesOnly(item, allow));
|
|
80030
|
-
}
|
|
80031
|
-
for (const [key, value] of Object.entries(node)) {
|
|
80032
|
-
if (key === "Ref" && typeof value === "string") {
|
|
80033
|
-
if (!allow.has(value))
|
|
80034
|
-
return false;
|
|
80035
|
-
continue;
|
|
80036
|
-
}
|
|
80037
|
-
if (key === "Fn::GetAtt") {
|
|
80038
|
-
const target = Array.isArray(value) && typeof value[0] === "string" ? value[0] : typeof value === "string" ? value.split(".")[0] : void 0;
|
|
80039
|
-
if (target && !allow.has(target))
|
|
80040
|
-
return false;
|
|
80041
|
-
continue;
|
|
80042
|
-
}
|
|
80043
|
-
if (!referencesOnly(value, allow))
|
|
80044
|
-
return false;
|
|
80045
|
-
}
|
|
80046
|
-
return true;
|
|
80047
|
-
}
|
|
80048
80337
|
function printPlan(plan, cfnStackName) {
|
|
80049
80338
|
const logger = getLogger();
|
|
80050
80339
|
logger.info("");
|
|
@@ -80122,11 +80411,41 @@ async function executeImportChangeSet(cfnClient, stackName, template, plan, para
|
|
|
80122
80411
|
{ StackName: stackName }
|
|
80123
80412
|
);
|
|
80124
80413
|
} catch (err) {
|
|
80414
|
+
const failureSummary = await collectImportFailureSummary(cfnClient, stackName).catch(() => "");
|
|
80125
80415
|
await cfnClient.send(new DeleteChangeSetCommand({ StackName: stackName, ChangeSetName: changeSetName })).catch(() => {
|
|
80126
80416
|
});
|
|
80417
|
+
if (failureSummary) {
|
|
80418
|
+
throw new Error(`IMPORT changeset failed:
|
|
80419
|
+
${failureSummary}`, { cause: err });
|
|
80420
|
+
}
|
|
80127
80421
|
throw err;
|
|
80128
80422
|
}
|
|
80129
80423
|
}
|
|
80424
|
+
async function collectImportFailureSummary(cfnClient, stackName) {
|
|
80425
|
+
const resp = await cfnClient.send(new DescribeStackEventsCommand({ StackName: stackName }));
|
|
80426
|
+
const events = resp.StackEvents ?? [];
|
|
80427
|
+
const failures = [];
|
|
80428
|
+
const seen = /* @__PURE__ */ new Set();
|
|
80429
|
+
for (const e of events) {
|
|
80430
|
+
if (!e.ResourceStatus || !e.ResourceStatus.endsWith("FAILED"))
|
|
80431
|
+
continue;
|
|
80432
|
+
if (!e.LogicalResourceId)
|
|
80433
|
+
continue;
|
|
80434
|
+
if (seen.has(e.LogicalResourceId))
|
|
80435
|
+
continue;
|
|
80436
|
+
seen.add(e.LogicalResourceId);
|
|
80437
|
+
failures.push({
|
|
80438
|
+
logicalId: e.LogicalResourceId,
|
|
80439
|
+
type: e.ResourceType ?? "<unknown>",
|
|
80440
|
+
reason: e.ResourceStatusReason ?? "<no reason reported>"
|
|
80441
|
+
});
|
|
80442
|
+
if (failures.length >= 5)
|
|
80443
|
+
break;
|
|
80444
|
+
}
|
|
80445
|
+
if (failures.length === 0)
|
|
80446
|
+
return "";
|
|
80447
|
+
return failures.map((f) => ` - ${f.logicalId} (${f.type}): ${f.reason}`).join("\n");
|
|
80448
|
+
}
|
|
80130
80449
|
async function executeUpdateChangeSet(cfnClient, stackName, template, parameters) {
|
|
80131
80450
|
const logger = getLogger();
|
|
80132
80451
|
const changeSetName = `cdkd-phase2-${Date.now()}`;
|
|
@@ -80308,7 +80627,7 @@ function reorderArgs(argv) {
|
|
|
80308
80627
|
}
|
|
80309
80628
|
async function main() {
|
|
80310
80629
|
const program = new Command18();
|
|
80311
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.91.
|
|
80630
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.91.4");
|
|
80312
80631
|
program.addCommand(createBootstrapCommand());
|
|
80313
80632
|
program.addCommand(createSynthCommand());
|
|
80314
80633
|
program.addCommand(createListCommand());
|