@kensio/yulin 1.21.15 → 1.21.17
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/sdk/router/sim-sdk-command-router.type.d.ts +9 -0
- package/dist/sdk/sim-sdk-command-dispatcher.js +1 -1
- package/dist/service/cloudformation/changeset/sim-cfn-change-set-changes.js +1 -1
- package/dist/service/cloudformation/resource/factory/sim-cfn-resource-factory.type.d.ts +25 -1
- package/dist/service/cloudformation/resource/resolve/service/sim-cfn-resource-service-factory.d.ts +18 -0
- package/dist/service/cloudformation/resource/resolve/service/sim-cfn-resource-service-factory.js +30 -0
- package/dist/service/cloudformation/resource/sim-cfn-resource.type.d.ts +18 -0
- package/dist/service/cloudformation/resource/update/sim-cfn-resource-in-place-updater.d.ts +35 -0
- package/dist/service/cloudformation/resource/update/sim-cfn-resource-in-place-updater.js +61 -0
- package/dist/service/cloudformation/resource/update/sim-cfn-resource-update-error.d.ts +9 -0
- package/dist/service/cloudformation/resource/update/sim-cfn-resource-update-error.js +14 -0
- package/dist/service/cloudformation/resource/update/sim-cfn-resource-update-validator.js +5 -18
- package/dist/service/cloudformation/stack/sim-cfn-stack-resource-operations.d.ts +7 -2
- package/dist/service/cloudformation/stack/sim-cfn-stack-resource-operations.js +16 -12
- package/dist/service/cloudformation/stack/update/sim-cfn-stack-resource-change-pairs.d.ts +12 -0
- package/dist/service/cloudformation/stack/update/sim-cfn-stack-resource-change-pairs.js +18 -0
- package/dist/service/cloudformation/stack/update/sim-cfn-stack-resource-changes.d.ts +52 -0
- package/dist/service/cloudformation/stack/update/sim-cfn-stack-resource-changes.js +86 -0
- package/dist/service/cloudformation/stack/update/sim-cfn-stack-resource-updates.d.ts +42 -0
- package/dist/service/cloudformation/stack/update/sim-cfn-stack-resource-updates.js +70 -0
- package/dist/service/cloudformation/stack/update/sim-cfn-stack-update-plan.d.ts +25 -18
- package/dist/service/cloudformation/stack/update/sim-cfn-stack-update-plan.js +43 -32
- package/dist/service/cloudformation/stack/update/sim-cfn-stack-updater.d.ts +7 -5
- package/dist/service/cloudformation/stack/update/sim-cfn-stack-updater.js +12 -7
- package/dist/service/dynamodb/document/sim-dynamodb-document-marshall-options.d.ts +42 -0
- package/dist/service/dynamodb/document/sim-dynamodb-document-marshall-options.js +36 -0
- package/dist/service/dynamodb/document/sim-dynamodb-document-marshall.d.ts +6 -5
- package/dist/service/dynamodb/document/sim-dynamodb-document-marshall.js +37 -43
- package/dist/service/dynamodb/document/sim-dynamodb-document-number.d.ts +4 -2
- package/dist/service/dynamodb/document/sim-dynamodb-document-number.js +5 -3
- package/dist/service/dynamodb/document/sim-dynamodb-document-path.d.ts +0 -4
- package/dist/service/dynamodb/document/sim-dynamodb-document-path.js +17 -10
- package/dist/service/dynamodb/document/sim-dynamodb-document-route.js +5 -1
- package/dist/service/dynamodb/document/sim-dynamodb-document-scalar.d.ts +13 -0
- package/dist/service/dynamodb/document/sim-dynamodb-document-scalar.js +46 -0
- package/dist/service/dynamodb/document/sim-dynamodb-document-set.d.ts +8 -3
- package/dist/service/dynamodb/document/sim-dynamodb-document-set.js +21 -14
- package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-change.d.ts +19 -0
- package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-change.js +39 -0
- package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-deleter.d.ts +23 -0
- package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-deleter.js +23 -0
- package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-template.test-support.d.ts +21 -0
- package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-template.test-support.js +64 -0
- package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-update.test-support.d.ts +18 -0
- package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-update.test-support.js +39 -0
- package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-updater.d.ts +37 -0
- package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-updater.js +66 -0
- package/dist/service/secretsmanager/cfn/sim-cfn-secrets-manager-resource-factory.d.ts +20 -10
- package/dist/service/secretsmanager/cfn/sim-cfn-secrets-manager-resource-factory.js +41 -27
- package/dist/service/secretsmanager/cfn/sim-cfn-secrets-manager-resource-type.d.ts +12 -0
- package/dist/service/secretsmanager/cfn/sim-cfn-secrets-manager-resource-type.js +22 -0
- package/docs/sdk/README.md +5 -4
- package/docs/services/cloudformation/README.md +21 -3
- package/docs/services/dynamodb/README.md +81 -2
- package/docs/services/secretsmanager/README.md +35 -1
- package/package.json +1 -1
- package/dist/service/cloudformation/stack/update/sim-cfn-stack-replaced-resources.d.ts +0 -21
- package/dist/service/cloudformation/stack/update/sim-cfn-stack-replaced-resources.js +0 -51
|
@@ -1,51 +1,65 @@
|
|
|
1
1
|
import { simCfnResourceCallerOptions } from "../../cloudformation/resource/caller/sim-cfn-resource-caller-options.js";
|
|
2
2
|
import { SimCfnSecretsManagerSecretCreator } from "./secret/sim-cfn-secrets-manager-secret-creator.js";
|
|
3
|
-
import {
|
|
3
|
+
import { SimCfnSecretsManagerSecretDeleter } from "./secret/sim-cfn-secrets-manager-secret-deleter.js";
|
|
4
|
+
import { SimCfnSecretsManagerSecretUpdater } from "./secret/sim-cfn-secrets-manager-secret-updater.js";
|
|
5
|
+
import { simCfnSecretsManagerSecretReplaced } from "./secret/sim-cfn-secrets-manager-secret-change.js";
|
|
6
|
+
import { isSimCfnSecretsManagerSecret, requireSimCfnSecretsManagerSecret, } from "./sim-cfn-secrets-manager-resource-type.js";
|
|
4
7
|
/**
|
|
5
8
|
* CloudFormation Resource factory for simulated Secrets Manager resources.
|
|
9
|
+
*
|
|
10
|
+
* Only `Secret` is deployed. Rotation, resource policies and target
|
|
11
|
+
* attachments are not simulated, so their Resource types are reported as
|
|
12
|
+
* unsupported and skipped rather than quietly treated as deployed.
|
|
6
13
|
*/
|
|
7
14
|
export class SimSecretsManagerCfnResourceFactory {
|
|
8
|
-
secretsManager;
|
|
9
15
|
secretCreator;
|
|
16
|
+
secretUpdater;
|
|
17
|
+
secretDeleter;
|
|
10
18
|
constructor(properties) {
|
|
11
|
-
|
|
19
|
+
const { secretsManager } = properties;
|
|
12
20
|
this.secretCreator = new SimCfnSecretsManagerSecretCreator({
|
|
13
|
-
secretsManager
|
|
21
|
+
secretsManager,
|
|
22
|
+
});
|
|
23
|
+
this.secretUpdater = new SimCfnSecretsManagerSecretUpdater({
|
|
24
|
+
secretsManager,
|
|
25
|
+
});
|
|
26
|
+
this.secretDeleter = new SimCfnSecretsManagerSecretDeleter({
|
|
27
|
+
secretsManager,
|
|
14
28
|
});
|
|
15
29
|
}
|
|
16
30
|
/**
|
|
17
31
|
* Create a simulated Secrets Manager resource from a CloudFormation
|
|
18
32
|
* Resource.
|
|
19
|
-
*
|
|
20
|
-
* Rotation, resource policies and target attachments are not simulated, so
|
|
21
|
-
* their Resource types are reported as unsupported and skipped rather than
|
|
22
|
-
* quietly treated as deployed.
|
|
23
33
|
*/
|
|
24
34
|
async create(resourceTypeName, resource, context) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
requireSimCfnSecretsManagerSecret(resourceTypeName);
|
|
36
|
+
return await this.secretCreator.create(resource, context.resolvedProperties ?? resource.properties, simCfnResourceCallerOptions(context.caller));
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Whether a changed secret can be applied to the deployed one.
|
|
40
|
+
*
|
|
41
|
+
* Name is the only property real CloudFormation replaces a secret for.
|
|
42
|
+
* Everything else is applied with no interruption, which is what keeps a
|
|
43
|
+
* generated value across a change to the secret's description or tags.
|
|
44
|
+
*/
|
|
45
|
+
updatesInPlace(resourceTypeName, current, updated) {
|
|
46
|
+
return (isSimCfnSecretsManagerSecret(resourceTypeName) &&
|
|
47
|
+
!simCfnSecretsManagerSecretReplaced(current, updated));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Apply a changed AWS::SecretsManager::Secret Resource to the deployed
|
|
51
|
+
* secret.
|
|
52
|
+
*/
|
|
53
|
+
async updateInPlace(resourceTypeName, current, updated, context) {
|
|
54
|
+
requireSimCfnSecretsManagerSecret(resourceTypeName, "update");
|
|
55
|
+
return await this.secretUpdater.update(current, updated, context);
|
|
33
56
|
}
|
|
34
57
|
/**
|
|
35
58
|
* Delete a simulated Secrets Manager resource created from a CloudFormation
|
|
36
59
|
* Resource.
|
|
37
|
-
*
|
|
38
|
-
* DeleteSecret schedules the deletion rather than carrying it out, so a torn
|
|
39
|
-
* down Stack leaves a secret waiting out its recovery window. That is what
|
|
40
|
-
* CloudFormation does: the secret is recoverable afterwards, which is the
|
|
41
|
-
* point of the window.
|
|
42
60
|
*/
|
|
43
61
|
async delete(resourceTypeName, resource, context) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
const secret = resource.simResource;
|
|
48
|
-
assertDefined(secret, `sim Secrets Manager secret for CloudFormation Resource ${resource.logicalId}`);
|
|
49
|
-
await this.secretsManager.deleteSecret({ input: { SecretId: secret.arn.value } }, simCfnResourceCallerOptions(context.caller));
|
|
62
|
+
requireSimCfnSecretsManagerSecret(resourceTypeName, "deletion");
|
|
63
|
+
await this.secretDeleter.delete(resource, simCfnResourceCallerOptions(context.caller));
|
|
50
64
|
}
|
|
51
65
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a Resource type name is the secret this simulation deploys.
|
|
3
|
+
*/
|
|
4
|
+
export declare function isSimCfnSecretsManagerSecret(resourceTypeName: string): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Refuse an operation on a Resource type this simulation does not deploy.
|
|
7
|
+
*
|
|
8
|
+
* The refusal reads as an unsupported Resource, so the Stack records it and
|
|
9
|
+
* carries on rather than failing. Creation names no operation, since a
|
|
10
|
+
* Resource type nothing creates is the plain case a reader meets first.
|
|
11
|
+
*/
|
|
12
|
+
export declare function requireSimCfnSecretsManagerSecret(resourceTypeName: string, operation?: string): void;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** The one AWS::SecretsManager::* Resource type this simulation deploys. */
|
|
2
|
+
const secretResourceTypeName = "Secret";
|
|
3
|
+
/**
|
|
4
|
+
* Whether a Resource type name is the secret this simulation deploys.
|
|
5
|
+
*/
|
|
6
|
+
export function isSimCfnSecretsManagerSecret(resourceTypeName) {
|
|
7
|
+
return resourceTypeName === secretResourceTypeName;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Refuse an operation on a Resource type this simulation does not deploy.
|
|
11
|
+
*
|
|
12
|
+
* The refusal reads as an unsupported Resource, so the Stack records it and
|
|
13
|
+
* carries on rather than failing. Creation names no operation, since a
|
|
14
|
+
* Resource type nothing creates is the plain case a reader meets first.
|
|
15
|
+
*/
|
|
16
|
+
export function requireSimCfnSecretsManagerSecret(resourceTypeName, operation) {
|
|
17
|
+
if (isSimCfnSecretsManagerSecret(resourceTypeName)) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const refused = operation === undefined ? "" : ` ${operation}`;
|
|
21
|
+
throw new Error(`Unsupported sim Secrets Manager CloudFormation Resource ${resourceTypeName}${refused}`);
|
|
22
|
+
}
|
package/docs/sdk/README.md
CHANGED
|
@@ -282,7 +282,8 @@ service throws `SimSdkUnknownServiceError`.
|
|
|
282
282
|
- Simulated errors have SDK-shaped `name` and `$metadata` fields. They are separate classes from the
|
|
283
283
|
SDK exceptions, so match them by `error.name` instead of `instanceof`.
|
|
284
284
|
- The callback form of `send(command, callback)` is not supported. Use the promise form.
|
|
285
|
-
- Yulin
|
|
286
|
-
`DynamoDBDocumentClient.from(client, { marshallOptions, unmarshallOptions })
|
|
287
|
-
|
|
288
|
-
|
|
285
|
+
- Yulin reads the `marshallOptions` in
|
|
286
|
+
`DynamoDBDocumentClient.from(client, { marshallOptions, unmarshallOptions })` and ignores the
|
|
287
|
+
`unmarshallOptions`. A stored value comes back the way a document client built with no options of
|
|
288
|
+
its own reads it. See
|
|
289
|
+
[the DynamoDB docs](https://yulinsim.dev/services/dynamodb/#marshalling-options).
|
|
@@ -446,6 +446,22 @@ Replacement also affects dependencies and retention policies:
|
|
|
446
446
|
The deployed one was deleted to make room for the replacement, and there is nothing left to put
|
|
447
447
|
back. See [rolling back a failed update](#rolling-back-a-failed-update) below.
|
|
448
448
|
|
|
449
|
+
### Resources a service changes where they are
|
|
450
|
+
|
|
451
|
+
A service can claim a change for the properties real CloudFormation applies with no interruption.
|
|
452
|
+
The resource is then left where it is. It keeps its physical name and everything it holds, and
|
|
453
|
+
every resource naming it is left standing too.
|
|
454
|
+
|
|
455
|
+
`AWS::SecretsManager::Secret` is the one resource type that does this so far. `Name` is the only
|
|
456
|
+
property real CloudFormation replaces a secret for. A change to anything else is applied to the
|
|
457
|
+
deployed secret, which keeps its ARN and its versions across the update. See the
|
|
458
|
+
[simulated Secrets Manager docs](https://yulinsim.dev/services/secretsmanager/ "Simulated Secrets Manager usage docs")
|
|
459
|
+
for which changes write a new secret version and which leave the value alone.
|
|
460
|
+
|
|
461
|
+
These changes are applied first, while everything the stack had is still deployed. An update that
|
|
462
|
+
fails on one has deleted nothing yet. A claimed resource the replacement spread reaches is replaced
|
|
463
|
+
after all, and the claim is taken back, since its dependency is being deleted and created again.
|
|
464
|
+
|
|
449
465
|
### Rolling back a failed update
|
|
450
466
|
|
|
451
467
|
A failed update is rolled back onto the template the stack was deployed from. The stack moves to
|
|
@@ -4393,13 +4409,15 @@ Each service's own docs describe what its resource types support.
|
|
|
4393
4409
|
[properties a Resource was created without](#properties-a-resource-was-created-without) for what
|
|
4394
4410
|
is still refused outright.
|
|
4395
4411
|
- A stack update replaces a changed resource rather than updating it in place, so what the resource
|
|
4396
|
-
held is lost.
|
|
4412
|
+
held is lost. `AWS::SecretsManager::Secret` is the exception. See
|
|
4413
|
+
[changed resources are replaced](#changed-resources-are-replaced) and
|
|
4414
|
+
[resources a service changes where they are](#resources-a-service-changes-where-they-are).
|
|
4397
4415
|
- A watched template file updates its stack in place. That makes the update itself no gentler. A
|
|
4398
4416
|
changed resource is still replaced and loses what it holds, the same as any other update.
|
|
4399
4417
|
- Yulin never synthesizes a CDK app. It watches the synthesized output template. A change to the app
|
|
4400
4418
|
itself reaches the stack once something has run `cdk synth` over it.
|
|
4401
|
-
- A change set reports every `Modify` as `Replacement: True`,
|
|
4402
|
-
|
|
4419
|
+
- A change set reports every `Modify` as `Replacement: True`, even for a resource an update would
|
|
4420
|
+
change where it is. `ChangeSetType: IMPORT` is refused, and so is a `CREATE` change set
|
|
4403
4421
|
naming a stack that is already there, where CloudFormation allows a second one against a stack
|
|
4404
4422
|
still in review. Drift detection is outside the simulation.
|
|
4405
4423
|
- A rolled-back update recreates a resource it had already replaced, and the resource comes back
|
|
@@ -3040,6 +3040,84 @@ Intercept the document client itself. `DynamoDBDocumentClient.from(client)` buil
|
|
|
3040
3040
|
outside the `DynamoDBClient` class, so intercepting the base client leaves Commands sent through the
|
|
3041
3041
|
document one untouched. See [the SDK docs](https://yulinsim.dev/sdk/#the-dynamodb-document-client).
|
|
3042
3042
|
|
|
3043
|
+
### Marshalling options
|
|
3044
|
+
|
|
3045
|
+
`DynamoDBDocumentClient.from(client, { marshallOptions })` changes what the conversion does with a
|
|
3046
|
+
value the defaults refuse. Yulin reads those options off the client each Command was sent through.
|
|
3047
|
+
Two document clients over one simulation each convert by their own.
|
|
3048
|
+
|
|
3049
|
+
- `removeUndefinedValues` drops an `undefined` out of a map, a list or a `Set`. Without it, an
|
|
3050
|
+
`undefined` in any of the three is refused. The real client refuses it in the same place.
|
|
3051
|
+
- `convertEmptyValues` writes an empty string, an empty binary value and an empty `Set` as `NULL`.
|
|
3052
|
+
- `convertClassInstanceToMap` reads an object with behaviour as a map of its own properties.
|
|
3053
|
+
- `allowImpreciseNumbers` writes a number past `Number.MAX_SAFE_INTEGER` with the digits it has
|
|
3054
|
+
already been rounded to.
|
|
3055
|
+
|
|
3056
|
+
An `undefined` attribute of an item, a key or a set of expression values is left out whatever the
|
|
3057
|
+
client was built with. `removeUndefinedValues` governs the values held inside an attribute, and the
|
|
3058
|
+
attributes of an item sit a level above that (the real client drops an undefined one there without
|
|
3059
|
+
being asked to).
|
|
3060
|
+
|
|
3061
|
+
```typescript sim-dynamodb-document-marshall-options
|
|
3062
|
+
/**
|
|
3063
|
+
* Writing a partly filled object through a document client that drops
|
|
3064
|
+
* undefined values.
|
|
3065
|
+
*/
|
|
3066
|
+
|
|
3067
|
+
import { CreateTableCommand, DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
3068
|
+
import {
|
|
3069
|
+
DynamoDBDocumentClient,
|
|
3070
|
+
GetCommand,
|
|
3071
|
+
PutCommand,
|
|
3072
|
+
} from "@aws-sdk/lib-dynamodb";
|
|
3073
|
+
|
|
3074
|
+
import { SimSdk } from "@kensio/yulin/sdk";
|
|
3075
|
+
|
|
3076
|
+
using simSdk = new SimSdk();
|
|
3077
|
+
|
|
3078
|
+
const documents = DynamoDBDocumentClient.from(
|
|
3079
|
+
new DynamoDBClient({ region: "eu-west-2" }),
|
|
3080
|
+
{ marshallOptions: { removeUndefinedValues: true } },
|
|
3081
|
+
);
|
|
3082
|
+
simSdk.intercept(documents);
|
|
3083
|
+
|
|
3084
|
+
await documents.send(
|
|
3085
|
+
new CreateTableCommand({
|
|
3086
|
+
TableName: "PagesTable",
|
|
3087
|
+
KeySchema: [{ AttributeName: "pageId", KeyType: "HASH" }],
|
|
3088
|
+
AttributeDefinitions: [{ AttributeName: "pageId", AttributeType: "S" }],
|
|
3089
|
+
BillingMode: "PAY_PER_REQUEST",
|
|
3090
|
+
}),
|
|
3091
|
+
);
|
|
3092
|
+
await simSdk.simAws.backgroundTasksComplete();
|
|
3093
|
+
|
|
3094
|
+
// The summary was never filled in, and one section is still to be written.
|
|
3095
|
+
await documents.send(
|
|
3096
|
+
new PutCommand({
|
|
3097
|
+
TableName: "PagesTable",
|
|
3098
|
+
Item: {
|
|
3099
|
+
pageId: "page-1",
|
|
3100
|
+
meta: { title: "Home", summary: undefined },
|
|
3101
|
+
sections: ["intro", undefined, "outro"],
|
|
3102
|
+
},
|
|
3103
|
+
}),
|
|
3104
|
+
);
|
|
3105
|
+
|
|
3106
|
+
const read = await documents.send(
|
|
3107
|
+
new GetCommand({ TableName: "PagesTable", Key: { pageId: "page-1" } }),
|
|
3108
|
+
);
|
|
3109
|
+
|
|
3110
|
+
const meta = read.Item?.["meta"] as Record<string, string>;
|
|
3111
|
+
console.log(Object.keys(meta)); // [ 'title' ]
|
|
3112
|
+
|
|
3113
|
+
// A dropped member takes its position with it.
|
|
3114
|
+
const sections = read.Item?.["sections"] as string[];
|
|
3115
|
+
console.log(sections); // [ 'intro', 'outro' ]
|
|
3116
|
+
```
|
|
3117
|
+
|
|
3118
|
+
`unmarshallOptions` are ignored. A stored value comes back the way a document client built with no
|
|
3119
|
+
options of its own reads it.
|
|
3120
|
+
|
|
3043
3121
|
### Querying and scanning through the document client
|
|
3044
3122
|
|
|
3045
3123
|
`@aws-sdk/lib-dynamodb` names its `QueryCommand` and `ScanCommand` exactly as
|
|
@@ -3152,8 +3230,9 @@ A simulated table holds a number's digits exactly, but the document client conve
|
|
|
3152
3230
|
JavaScript numbers, and that is where digits are lost. It is the same loss AWS has. A test that
|
|
3153
3231
|
passes here is telling you something true about the real thing.
|
|
3154
3232
|
|
|
3155
|
-
- Writing a `number` outside the safe integer range is refused
|
|
3156
|
-
a `bigint`, or a `NumberValue` from `@aws-sdk/lib-dynamodb`, to keep
|
|
3233
|
+
- Writing a `number` outside the safe integer range is refused unless the client was built with
|
|
3234
|
+
`allowImpreciseNumbers`. Write a `bigint`, or a `NumberValue` from `@aws-sdk/lib-dynamodb`, to keep
|
|
3235
|
+
the digits.
|
|
3157
3236
|
- Reading a stored number outside the safe integer range gives a `bigint`.
|
|
3158
3237
|
- Reading a stored decimal with more digits than a JavaScript number carries gives a rounded
|
|
3159
3238
|
`number`. The table still holds every digit, and the rounding is the document client's. Read
|
|
@@ -428,6 +428,37 @@ console.log(credentials.password?.length); // 24
|
|
|
428
428
|
Generated passwords are random. Read the deployed value through Secrets Manager instead of asserting
|
|
429
429
|
on an exact password.
|
|
430
430
|
|
|
431
|
+
## Updating a deployed secret
|
|
432
|
+
|
|
433
|
+
`Name` is the only property real CloudFormation replaces a secret for. A stack update that changes
|
|
434
|
+
anything else applies it to the deployed secret, which keeps its ARN and its versions.
|
|
435
|
+
|
|
436
|
+
A change to the description, the tags or the KMS key leaves the value alone. That is what carries a
|
|
437
|
+
generated password across an update, and it keeps a resource that read the secret as the stack
|
|
438
|
+
deployed, such as a CloudFront origin custom header, holding the value the secret still has.
|
|
439
|
+
|
|
440
|
+
A new version is written when the template asks for a different value, which is a changed
|
|
441
|
+
`SecretString` or a changed `GenerateSecretString`. A changed `GenerateSecretString` generates a new
|
|
442
|
+
password, as real CloudFormation writes a new version for one. **A resource that resolved the old
|
|
443
|
+
value keeps it.** Its own template entry is unchanged, so the update leaves it alone, and it goes on
|
|
444
|
+
holding what it read at deploy time. A consumer that has to follow the value must read the secret
|
|
445
|
+
when it runs, the way a Lambda function given the secret's ARN does, rather than take a copy through
|
|
446
|
+
a `{{resolve:secretsmanager:...}}` reference.
|
|
447
|
+
|
|
448
|
+
The template is the desired state. A property the new template leaves out is cleared. A dropped
|
|
449
|
+
`Description` is emptied, and a dropped `KmsKeyId` puts the secret back on the `aws/secretsmanager`
|
|
450
|
+
key. Versions already written keep the key they were made with and stay
|
|
451
|
+
readable.
|
|
452
|
+
|
|
453
|
+
Changing the `Name` replaces the secret. The new one is created under the new name and the old one
|
|
454
|
+
is scheduled for deletion, waiting out its recovery window.
|
|
455
|
+
|
|
456
|
+
A secret is also replaced when a resource it names is replaced, because the update deletes and
|
|
457
|
+
recreates that resource and the secret would otherwise be applied against the one on its way out.
|
|
458
|
+
That replacement then fails, since the name is held for the recovery window. Real CloudFormation
|
|
459
|
+
updates the secret in place and hands it the new physical name. Nothing is applied to the deployed
|
|
460
|
+
secret before the failure.
|
|
461
|
+
|
|
431
462
|
## Reading a secret with a dynamic reference
|
|
432
463
|
|
|
433
464
|
A `{{resolve:secretsmanager:...}}` dynamic reference reads an existing secret while CloudFormation
|
|
@@ -641,7 +672,10 @@ code into the simulation, served in process. See
|
|
|
641
672
|
empty page.
|
|
642
673
|
- Tags are stored and reported by `DescribeSecret` and `ListSecrets`, but `TagResource` and
|
|
643
674
|
`UntagResource` are absent, and the `secretsmanager:ResourceTag` and `aws:ResourceTag` condition
|
|
644
|
-
keys are left underived.
|
|
675
|
+
keys are left underived. A stack update applies the tags its template declares to the deployed
|
|
676
|
+
secret, since there is no command to ask for it with.
|
|
677
|
+
- A stack update authorizes the whole change as `secretsmanager:UpdateSecret`. Real CloudFormation
|
|
678
|
+
needs `secretsmanager:TagResource` as well for a change to the tags.
|
|
645
679
|
- Other Secrets Manager condition keys, such as `secretsmanager:SecretId` and
|
|
646
680
|
`secretsmanager:VersionStage`, are left underived too, and a policy relying on them fails to match.
|
|
647
681
|
Ordinary condition operators on values sim IAM does supply work as usual.
|
package/package.json
CHANGED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { SimCfnResource } from "../../resource/sim-cfn-resource.js";
|
|
2
|
-
interface SimCfnStackReplacedResourcesProperties {
|
|
3
|
-
readonly current: ReadonlyMap<string, SimCfnResource>;
|
|
4
|
-
readonly updated: ReadonlyMap<string, SimCfnResource>;
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* The logical IDs an update has to replace, given the Stack's deployed
|
|
8
|
-
* Resources and the ones its new template describes.
|
|
9
|
-
*
|
|
10
|
-
* A Resource is replaced when its resolved template entry changed. Sim
|
|
11
|
-
* CloudFormation has no in-place update, so replacement means deleting the
|
|
12
|
-
* Resource and creating it again from the new template.
|
|
13
|
-
*
|
|
14
|
-
* A Resource that names a replaced Resource is replaced too, all the way up the
|
|
15
|
-
* dependency chain. Real CloudFormation hands the dependent the new physical
|
|
16
|
-
* name instead, but nothing here can rewrite an already created simulated
|
|
17
|
-
* Resource, and leaving the dependent alone would leave it pointing at a
|
|
18
|
-
* Resource that has gone.
|
|
19
|
-
*/
|
|
20
|
-
export declare function simCfnStackReplacedLogicalIds(properties: SimCfnStackReplacedResourcesProperties): ReadonlySet<string>;
|
|
21
|
-
export {};
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { simCfnTemplateSignature } from "./sim-cfn-template-signature.js";
|
|
2
|
-
/**
|
|
3
|
-
* The logical IDs an update has to replace, given the Stack's deployed
|
|
4
|
-
* Resources and the ones its new template describes.
|
|
5
|
-
*
|
|
6
|
-
* A Resource is replaced when its resolved template entry changed. Sim
|
|
7
|
-
* CloudFormation has no in-place update, so replacement means deleting the
|
|
8
|
-
* Resource and creating it again from the new template.
|
|
9
|
-
*
|
|
10
|
-
* A Resource that names a replaced Resource is replaced too, all the way up the
|
|
11
|
-
* dependency chain. Real CloudFormation hands the dependent the new physical
|
|
12
|
-
* name instead, but nothing here can rewrite an already created simulated
|
|
13
|
-
* Resource, and leaving the dependent alone would leave it pointing at a
|
|
14
|
-
* Resource that has gone.
|
|
15
|
-
*/
|
|
16
|
-
export function simCfnStackReplacedLogicalIds(properties) {
|
|
17
|
-
const { current, updated } = properties;
|
|
18
|
-
const replaced = changedLogicalIds({ current, updated });
|
|
19
|
-
// Each pass replaces the Resources naming one already being replaced, until a
|
|
20
|
-
// pass finds none, which is the end of the dependency chain.
|
|
21
|
-
let spreading = true;
|
|
22
|
-
while (spreading) {
|
|
23
|
-
spreading = false;
|
|
24
|
-
for (const [logicalId, resource] of updated) {
|
|
25
|
-
const spreads = current.has(logicalId) &&
|
|
26
|
-
!replaced.has(logicalId) &&
|
|
27
|
-
resource.dependencies().some((dependency) => replaced.has(dependency));
|
|
28
|
-
if (spreads) {
|
|
29
|
-
replaced.add(logicalId);
|
|
30
|
-
spreading = true;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return replaced;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* The logical IDs whose resolved template entry differs from the deployed one.
|
|
38
|
-
*/
|
|
39
|
-
function changedLogicalIds(properties) {
|
|
40
|
-
const { current, updated } = properties;
|
|
41
|
-
const changed = new Set();
|
|
42
|
-
for (const [logicalId, resource] of updated) {
|
|
43
|
-
const deployed = current.get(logicalId);
|
|
44
|
-
if (deployed !== undefined &&
|
|
45
|
-
simCfnTemplateSignature(deployed.template) !==
|
|
46
|
-
simCfnTemplateSignature(resource.template)) {
|
|
47
|
-
changed.add(logicalId);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return changed;
|
|
51
|
-
}
|