@go-to-k/cdkd 0.50.13 → 0.51.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/README.md +7 -5
- package/dist/cli.js +520 -111
- package/dist/cli.js.map +3 -3
- package/dist/go-to-k-cdkd-0.51.1.tgz +0 -0
- package/dist/index.js +164 -26
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
- package/dist/go-to-k-cdkd-0.50.13.tgz +0 -0
|
Binary file
|
package/dist/index.js
CHANGED
|
@@ -7636,6 +7636,7 @@ import {
|
|
|
7636
7636
|
UpdateAssumeRolePolicyCommand,
|
|
7637
7637
|
DeleteRoleCommand,
|
|
7638
7638
|
GetRoleCommand,
|
|
7639
|
+
GetRolePolicyCommand,
|
|
7639
7640
|
PutRolePolicyCommand,
|
|
7640
7641
|
DeleteRolePolicyCommand,
|
|
7641
7642
|
ListRolePoliciesCommand,
|
|
@@ -8248,8 +8249,14 @@ var IAMRoleProvider = class {
|
|
|
8248
8249
|
* against state's already-parsed object.
|
|
8249
8250
|
*
|
|
8250
8251
|
* Coverage and shape decisions:
|
|
8251
|
-
* - `RoleName`, `Description`, `MaxSessionDuration`, `Path
|
|
8252
|
-
*
|
|
8252
|
+
* - `RoleName`, `Description`, `MaxSessionDuration`, `Path` — straight
|
|
8253
|
+
* from `Role.*`.
|
|
8254
|
+
* - `PermissionsBoundary` — emitted as `'' ` placeholder when AWS has
|
|
8255
|
+
* none, so a console-side ADD on a role that was deployed without a
|
|
8256
|
+
* boundary surfaces as drift. (The drift comparator's top-level walk
|
|
8257
|
+
* is state-keys-only; without the always-emit placeholder a fresh
|
|
8258
|
+
* `PermissionsBoundary` on the AWS side would never enter
|
|
8259
|
+
* `observedProperties` and the comparator would silently ignore it.)
|
|
8253
8260
|
* - `AssumeRolePolicyDocument` — `Role.AssumeRolePolicyDocument` is a
|
|
8254
8261
|
* URL-encoded JSON string; we URL-decode + JSON-parse so cdkd state's
|
|
8255
8262
|
* object form compares cleanly. (Both shapes — string and object — are
|
|
@@ -8257,20 +8264,23 @@ var IAMRoleProvider = class {
|
|
|
8257
8264
|
* after intrinsic resolution.)
|
|
8258
8265
|
* - `ManagedPolicyArns` — array of ARN strings from
|
|
8259
8266
|
* `ListAttachedRolePolicies`.
|
|
8260
|
-
* - `Policies`
|
|
8261
|
-
*
|
|
8262
|
-
*
|
|
8263
|
-
*
|
|
8264
|
-
*
|
|
8265
|
-
*
|
|
8267
|
+
* - `Policies` — inline policies surfaced as `[{PolicyName, PolicyDocument}]`.
|
|
8268
|
+
* `ListRolePolicies` for names + `GetRolePolicy` per name for the
|
|
8269
|
+
* body (URL-decoded + JSON-parsed). Ordering is reconciled against
|
|
8270
|
+
* state's `Policies` array (when supplied via the `properties`
|
|
8271
|
+
* parameter) so a state-vs-AWS positional compare doesn't fire false
|
|
8272
|
+
* drift purely from `ListRolePolicies` returning lexicographic order;
|
|
8273
|
+
* AWS-only policies (added via console) are appended at the end so
|
|
8274
|
+
* they still surface as drift via length / content mismatch.
|
|
8266
8275
|
* - `Tags` is surfaced via `ListRoleTags` (paginated). CDK's `aws:*`
|
|
8267
8276
|
* auto-tags are filtered out by `normalizeAwsTagsToCfn` so they don't
|
|
8268
|
-
* fire false-positive drift;
|
|
8269
|
-
*
|
|
8277
|
+
* fire false-positive drift; always emitted (even when empty) so a
|
|
8278
|
+
* console-side tag ADD on an originally-untagged role surfaces as
|
|
8279
|
+
* drift on the v3 observedProperties baseline.
|
|
8270
8280
|
*
|
|
8271
8281
|
* Returns `undefined` when the role is gone (`NoSuchEntityException`).
|
|
8272
8282
|
*/
|
|
8273
|
-
async readCurrentState(physicalId, _logicalId, _resourceType) {
|
|
8283
|
+
async readCurrentState(physicalId, _logicalId, _resourceType, properties) {
|
|
8274
8284
|
let role;
|
|
8275
8285
|
try {
|
|
8276
8286
|
const resp = await this.iamClient.send(new GetRoleCommand({ RoleName: physicalId }));
|
|
@@ -8291,9 +8301,7 @@ var IAMRoleProvider = class {
|
|
|
8291
8301
|
}
|
|
8292
8302
|
if (role.Path !== void 0)
|
|
8293
8303
|
result["Path"] = role.Path;
|
|
8294
|
-
|
|
8295
|
-
result["PermissionsBoundary"] = role.PermissionsBoundary.PermissionsBoundaryArn;
|
|
8296
|
-
}
|
|
8304
|
+
result["PermissionsBoundary"] = role.PermissionsBoundary?.PermissionsBoundaryArn ?? "";
|
|
8297
8305
|
if (role.AssumeRolePolicyDocument) {
|
|
8298
8306
|
try {
|
|
8299
8307
|
result["AssumeRolePolicyDocument"] = JSON.parse(
|
|
@@ -8313,6 +8321,59 @@ var IAMRoleProvider = class {
|
|
|
8313
8321
|
if (!(err instanceof NoSuchEntityException))
|
|
8314
8322
|
throw err;
|
|
8315
8323
|
}
|
|
8324
|
+
try {
|
|
8325
|
+
const policyNames = [];
|
|
8326
|
+
let policyMarker;
|
|
8327
|
+
while (true) {
|
|
8328
|
+
const listResp = await this.iamClient.send(
|
|
8329
|
+
new ListRolePoliciesCommand({
|
|
8330
|
+
RoleName: physicalId,
|
|
8331
|
+
...policyMarker ? { Marker: policyMarker } : {}
|
|
8332
|
+
})
|
|
8333
|
+
);
|
|
8334
|
+
for (const name of listResp.PolicyNames ?? [])
|
|
8335
|
+
policyNames.push(name);
|
|
8336
|
+
if (!listResp.IsTruncated)
|
|
8337
|
+
break;
|
|
8338
|
+
policyMarker = listResp.Marker;
|
|
8339
|
+
}
|
|
8340
|
+
const bodies = /* @__PURE__ */ new Map();
|
|
8341
|
+
await Promise.all(
|
|
8342
|
+
policyNames.map(async (name) => {
|
|
8343
|
+
const resp = await this.iamClient.send(
|
|
8344
|
+
new GetRolePolicyCommand({ RoleName: physicalId, PolicyName: name })
|
|
8345
|
+
);
|
|
8346
|
+
if (!resp.PolicyDocument)
|
|
8347
|
+
return;
|
|
8348
|
+
let parsed;
|
|
8349
|
+
try {
|
|
8350
|
+
parsed = JSON.parse(decodeURIComponent(resp.PolicyDocument));
|
|
8351
|
+
} catch {
|
|
8352
|
+
parsed = resp.PolicyDocument;
|
|
8353
|
+
}
|
|
8354
|
+
bodies.set(name, parsed);
|
|
8355
|
+
})
|
|
8356
|
+
);
|
|
8357
|
+
const statePolicies = properties?.["Policies"] ?? [];
|
|
8358
|
+
const remaining = new Set(bodies.keys());
|
|
8359
|
+
const inline = [];
|
|
8360
|
+
for (const sp of statePolicies) {
|
|
8361
|
+
const name = sp?.PolicyName;
|
|
8362
|
+
if (typeof name !== "string")
|
|
8363
|
+
continue;
|
|
8364
|
+
if (bodies.has(name)) {
|
|
8365
|
+
inline.push({ PolicyName: name, PolicyDocument: bodies.get(name) });
|
|
8366
|
+
remaining.delete(name);
|
|
8367
|
+
}
|
|
8368
|
+
}
|
|
8369
|
+
for (const name of [...remaining].sort()) {
|
|
8370
|
+
inline.push({ PolicyName: name, PolicyDocument: bodies.get(name) });
|
|
8371
|
+
}
|
|
8372
|
+
result["Policies"] = inline;
|
|
8373
|
+
} catch (err) {
|
|
8374
|
+
if (!(err instanceof NoSuchEntityException))
|
|
8375
|
+
throw err;
|
|
8376
|
+
}
|
|
8316
8377
|
try {
|
|
8317
8378
|
const collected = [];
|
|
8318
8379
|
let marker;
|
|
@@ -8340,18 +8401,6 @@ var IAMRoleProvider = class {
|
|
|
8340
8401
|
}
|
|
8341
8402
|
return result;
|
|
8342
8403
|
}
|
|
8343
|
-
/**
|
|
8344
|
-
* `Policies` (inline policy bodies) are intentionally omitted from
|
|
8345
|
-
* `readCurrentState`: surfacing the names without bodies would
|
|
8346
|
-
* guarantee a `PolicyDocument`-shaped drift on every role, and
|
|
8347
|
-
* fetching every body costs one extra `GetRolePolicy` per inline
|
|
8348
|
-
* policy. Tell the drift comparator to skip the whole subtree until a
|
|
8349
|
-
* dedicated PR adds proper inline-policy drift via per-name
|
|
8350
|
-
* `GetRolePolicy`.
|
|
8351
|
-
*/
|
|
8352
|
-
getDriftUnknownPaths() {
|
|
8353
|
-
return ["Policies"];
|
|
8354
|
-
}
|
|
8355
8404
|
/**
|
|
8356
8405
|
* Adopt an existing IAM role into cdkd state.
|
|
8357
8406
|
*
|
|
@@ -8807,6 +8856,65 @@ var DeployEngine = class {
|
|
|
8807
8856
|
}
|
|
8808
8857
|
}
|
|
8809
8858
|
}
|
|
8859
|
+
/**
|
|
8860
|
+
* Kick off `provider.readCurrentState` for every resource in the
|
|
8861
|
+
* loaded state that lacks `observedProperties` (e.g. state written
|
|
8862
|
+
* by a pre-v3 binary, or a v3 record where a NO_CHANGE-skipped
|
|
8863
|
+
* resource's baseline never landed). Calls go through
|
|
8864
|
+
* `kickOffObservedCapture`, so they share the same fire-and-forget
|
|
8865
|
+
* pipeline, error swallowing, and final-drain wiring that the
|
|
8866
|
+
* post-CREATE / post-UPDATE captures use.
|
|
8867
|
+
*
|
|
8868
|
+
* The deploy critical path does NOT wait on these; the cost is
|
|
8869
|
+
* bounded by `max(per-resource readCurrentState latency)` (typically
|
|
8870
|
+
* ~200-300ms in practice) once at the end-of-deploy drain. Any
|
|
8871
|
+
* resource that subsequently goes through CREATE / UPDATE in the
|
|
8872
|
+
* same deploy will overwrite this entry via the `Map.set` keyed by
|
|
8873
|
+
* `logicalId` (latest-wins) — so there's no double-write to state,
|
|
8874
|
+
* just a wasted SDK call for the (rare) UPDATE / DELETE intersection.
|
|
8875
|
+
*
|
|
8876
|
+
* Resources whose provider lookup throws (e.g. unsupported type) or
|
|
8877
|
+
* lacks `readCurrentState` are silently skipped — same policy as the
|
|
8878
|
+
* manual `cdkd state refresh-observed` command.
|
|
8879
|
+
*/
|
|
8880
|
+
kickOffAutoRefreshObservedProperties(stateResources) {
|
|
8881
|
+
if (this.options.captureObservedState !== true)
|
|
8882
|
+
return;
|
|
8883
|
+
if (this.options.dryRun === true)
|
|
8884
|
+
return;
|
|
8885
|
+
let toRefresh = 0;
|
|
8886
|
+
const candidates = [];
|
|
8887
|
+
for (const [logicalId, resource] of Object.entries(stateResources)) {
|
|
8888
|
+
if (resource.observedProperties !== void 0)
|
|
8889
|
+
continue;
|
|
8890
|
+
candidates.push({ logicalId, resource });
|
|
8891
|
+
}
|
|
8892
|
+
if (candidates.length === 0)
|
|
8893
|
+
return;
|
|
8894
|
+
for (const { logicalId, resource } of candidates) {
|
|
8895
|
+
let provider;
|
|
8896
|
+
try {
|
|
8897
|
+
provider = this.providerRegistry.getProvider(resource.resourceType);
|
|
8898
|
+
} catch {
|
|
8899
|
+
continue;
|
|
8900
|
+
}
|
|
8901
|
+
if (!provider.readCurrentState)
|
|
8902
|
+
continue;
|
|
8903
|
+
this.kickOffObservedCapture(
|
|
8904
|
+
provider,
|
|
8905
|
+
logicalId,
|
|
8906
|
+
resource.physicalId,
|
|
8907
|
+
resource.resourceType,
|
|
8908
|
+
resource.properties ?? {}
|
|
8909
|
+
);
|
|
8910
|
+
toRefresh++;
|
|
8911
|
+
}
|
|
8912
|
+
if (toRefresh > 0) {
|
|
8913
|
+
this.logger.warn(
|
|
8914
|
+
`cdkd state schema upgrade detected \u2014 refreshing observed-properties baseline for ${toRefresh} resource(s) (one-time, runs in parallel with deploy)`
|
|
8915
|
+
);
|
|
8916
|
+
}
|
|
8917
|
+
}
|
|
8810
8918
|
async doDeploy(stackName, template) {
|
|
8811
8919
|
const startTime = Date.now();
|
|
8812
8920
|
this.logger.debug(`Starting deployment for stack: ${stackName}`);
|
|
@@ -8838,6 +8946,7 @@ var DeployEngine = class {
|
|
|
8838
8946
|
this.logger.debug(
|
|
8839
8947
|
`Loaded current state: ${Object.keys(currentState.resources).length} resources`
|
|
8840
8948
|
);
|
|
8949
|
+
this.kickOffAutoRefreshObservedProperties(currentState.resources);
|
|
8841
8950
|
this.logger.debug(`Template has ${Object.keys(template.Resources || {}).length} resources`);
|
|
8842
8951
|
const parameterValues = await this.resolver.resolveParameters(
|
|
8843
8952
|
template,
|
|
@@ -8882,6 +8991,35 @@ var DeployEngine = class {
|
|
|
8882
8991
|
const hasChanges = this.diffCalculator.hasChanges(changes);
|
|
8883
8992
|
if (!hasChanges) {
|
|
8884
8993
|
this.logger.info("No changes detected. Stack is up to date.");
|
|
8994
|
+
if (!this.options.dryRun && this.observedCaptureTasks.size > 0) {
|
|
8995
|
+
await this.drainObservedCaptures(currentState.resources);
|
|
8996
|
+
try {
|
|
8997
|
+
const refreshedState = {
|
|
8998
|
+
version: STATE_SCHEMA_VERSION_CURRENT,
|
|
8999
|
+
region: this.stackRegion,
|
|
9000
|
+
stackName: currentState.stackName,
|
|
9001
|
+
resources: currentState.resources,
|
|
9002
|
+
outputs: currentState.outputs,
|
|
9003
|
+
lastModified: Date.now()
|
|
9004
|
+
};
|
|
9005
|
+
const saveOptions = {};
|
|
9006
|
+
if (currentEtag !== void 0)
|
|
9007
|
+
saveOptions.expectedEtag = currentEtag;
|
|
9008
|
+
if (migrationPending)
|
|
9009
|
+
saveOptions.migrateLegacy = true;
|
|
9010
|
+
await this.stateBackend.saveState(
|
|
9011
|
+
stackName,
|
|
9012
|
+
this.stackRegion,
|
|
9013
|
+
refreshedState,
|
|
9014
|
+
saveOptions
|
|
9015
|
+
);
|
|
9016
|
+
this.logger.debug("Persisted refreshed observedProperties (no-change path)");
|
|
9017
|
+
} catch (saveError) {
|
|
9018
|
+
this.logger.warn(
|
|
9019
|
+
`Failed to persist refreshed observedProperties: ${saveError instanceof Error ? saveError.message : String(saveError)} \u2014 drift baseline will be re-fetched on next deploy.`
|
|
9020
|
+
);
|
|
9021
|
+
}
|
|
9022
|
+
}
|
|
8885
9023
|
return {
|
|
8886
9024
|
stackName,
|
|
8887
9025
|
created: 0,
|