@kensio/yulin 1.21.14 → 1.21.16

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.
Files changed (55) hide show
  1. package/dist/service/cloudformation/changeset/sim-cfn-change-set-changes.js +1 -1
  2. package/dist/service/cloudformation/resource/factory/sim-cfn-resource-factory.type.d.ts +25 -1
  3. package/dist/service/cloudformation/resource/resolve/service/sim-cfn-resource-service-factory.d.ts +18 -0
  4. package/dist/service/cloudformation/resource/resolve/service/sim-cfn-resource-service-factory.js +30 -0
  5. package/dist/service/cloudformation/resource/sim-cfn-resource.type.d.ts +18 -0
  6. package/dist/service/cloudformation/resource/update/sim-cfn-resource-in-place-updater.d.ts +35 -0
  7. package/dist/service/cloudformation/resource/update/sim-cfn-resource-in-place-updater.js +61 -0
  8. package/dist/service/cloudformation/resource/update/sim-cfn-resource-update-error.d.ts +9 -0
  9. package/dist/service/cloudformation/resource/update/sim-cfn-resource-update-error.js +14 -0
  10. package/dist/service/cloudformation/resource/update/sim-cfn-resource-update-validator.js +5 -18
  11. package/dist/service/cloudformation/stack/sim-cfn-stack-resource-operations.d.ts +7 -2
  12. package/dist/service/cloudformation/stack/sim-cfn-stack-resource-operations.js +16 -12
  13. package/dist/service/cloudformation/stack/update/sim-cfn-stack-resource-change-pairs.d.ts +12 -0
  14. package/dist/service/cloudformation/stack/update/sim-cfn-stack-resource-change-pairs.js +18 -0
  15. package/dist/service/cloudformation/stack/update/sim-cfn-stack-resource-changes.d.ts +52 -0
  16. package/dist/service/cloudformation/stack/update/sim-cfn-stack-resource-changes.js +86 -0
  17. package/dist/service/cloudformation/stack/update/sim-cfn-stack-resource-updates.d.ts +42 -0
  18. package/dist/service/cloudformation/stack/update/sim-cfn-stack-resource-updates.js +70 -0
  19. package/dist/service/cloudformation/stack/update/sim-cfn-stack-update-plan.d.ts +25 -18
  20. package/dist/service/cloudformation/stack/update/sim-cfn-stack-update-plan.js +43 -32
  21. package/dist/service/cloudformation/stack/update/sim-cfn-stack-updater.d.ts +7 -5
  22. package/dist/service/cloudformation/stack/update/sim-cfn-stack-updater.js +12 -7
  23. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-change.d.ts +19 -0
  24. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-change.js +39 -0
  25. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-deleter.d.ts +23 -0
  26. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-deleter.js +23 -0
  27. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-template.test-support.d.ts +21 -0
  28. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-template.test-support.js +64 -0
  29. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-update.test-support.d.ts +18 -0
  30. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-update.test-support.js +39 -0
  31. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-updater.d.ts +37 -0
  32. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-updater.js +66 -0
  33. package/dist/service/secretsmanager/cfn/sim-cfn-secrets-manager-resource-factory.d.ts +20 -10
  34. package/dist/service/secretsmanager/cfn/sim-cfn-secrets-manager-resource-factory.js +41 -27
  35. package/dist/service/secretsmanager/cfn/sim-cfn-secrets-manager-resource-type.d.ts +12 -0
  36. package/dist/service/secretsmanager/cfn/sim-cfn-secrets-manager-resource-type.js +22 -0
  37. package/dist/util/background/background-clock-waits.d.ts +10 -3
  38. package/dist/util/background/background-clock-waits.js +11 -4
  39. package/dist/util/background/background-pending-tasks.d.ts +15 -9
  40. package/dist/util/background/background-pending-tasks.js +18 -12
  41. package/dist/util/background/background.d.ts +11 -3
  42. package/dist/util/background/background.js +14 -4
  43. package/dist/util/background/non-deterministic-background.d.ts +4 -0
  44. package/dist/util/background/non-deterministic-background.js +5 -1
  45. package/dist/util/clock/sim-clock.d.ts +16 -0
  46. package/dist/util/clock/sim-clock.js +4 -0
  47. package/dist/util/clock/sim-controllable-clock.d.ts +11 -0
  48. package/dist/util/clock/sim-controllable-clock.js +13 -0
  49. package/docs/services/cloudformation/README.md +21 -3
  50. package/docs/services/lambda/README.md +11 -0
  51. package/docs/services/secretsmanager/README.md +35 -1
  52. package/docs/time/README.md +7 -0
  53. package/package.json +1 -1
  54. package/dist/service/cloudformation/stack/update/sim-cfn-stack-replaced-resources.d.ts +0 -21
  55. package/dist/service/cloudformation/stack/update/sim-cfn-stack-replaced-resources.js +0 -51
@@ -1,28 +1,33 @@
1
1
  import type { SimCfnResource } from "../../resource/sim-cfn-resource.js";
2
+ import { SimCfnResourceRetention } from "../../resource/delete/sim-cfn-resource-retention.js";
3
+ import { type SimCfnStackInPlaceUpdates } from "./sim-cfn-stack-resource-changes.js";
4
+ import { type SimCfnStackResourceChange } from "./sim-cfn-stack-resource-change-pairs.js";
2
5
  interface SimCfnStackUpdatePlanProperties {
3
6
  readonly current: ReadonlyMap<string, SimCfnResource>;
4
7
  readonly updated: ReadonlyMap<string, SimCfnResource>;
5
- }
6
- export interface SimCfnStackResourceReplacement {
7
- readonly current: SimCfnResource;
8
- readonly updated: SimCfnResource;
8
+ readonly inPlaceUpdates?: SimCfnStackInPlaceUpdates | undefined;
9
9
  }
10
10
  /**
11
11
  * What an update has to do to a Stack's Resources, worked out before any of it
12
12
  * happens.
13
13
  *
14
14
  * The plan compares the Resources the Stack has with the ones its new template
15
- * describes, and says which to delete, which to create, and what the Stack
16
- * holds afterwards. A Resource the template still describes unchanged is left
17
- * alone, keeping whatever it holds in simulated AWS.
15
+ * describes, and says which to update where they are, which to delete, which to
16
+ * create, and what the Stack holds afterwards. A Resource the template still
17
+ * describes unchanged is left alone, keeping whatever it holds in simulated
18
+ * AWS.
18
19
  *
19
- * It does not delete or create anything, order the work, or decide what counts
20
- * as a changed Resource. SimCfnStackUpdater runs the plan, and
21
- * simCfnStackReplacedLogicalIds decides what changed.
20
+ * It does not delete, create or update anything, order the work, or decide what
21
+ * counts as a changed Resource. SimCfnStackUpdater runs the plan, and
22
+ * simCfnStackResourceChanges decides what changed and how.
22
23
  */
23
24
  export declare class SimCfnStackUpdatePlan {
24
25
  /** The current and updated halves of each Resource replacement. */
25
- readonly replacements: readonly SimCfnStackResourceReplacement[];
26
+ readonly replacements: readonly SimCfnStackResourceChange[];
27
+ /**
28
+ * The Resources to change where they are, rather than by replacing them.
29
+ */
30
+ readonly updates: readonly SimCfnStackResourceChange[];
26
31
  /**
27
32
  * The deployed Resources to delete: the ones the new template drops, and the
28
33
  * deployed halves of the ones it replaces.
@@ -36,14 +41,14 @@ export declare class SimCfnStackUpdatePlan {
36
41
  readonly updated: ReadonlyMap<string, SimCfnResource>;
37
42
  constructor(properties: SimCfnStackUpdatePlanProperties);
38
43
  /**
39
- * For each logical ID the update is replacing, whether the definition taking
40
- * its place says to keep the deployed Resource.
44
+ * What the update's deletions are to leave in simulated AWS.
41
45
  *
42
- * The new half answers it because CloudFormation reads UpdateReplacePolicy
43
- * off the template it is applying. An update that adds the attribute keeps
44
- * the Resource it replaces, and one that drops it deletes that Resource.
46
+ * A replacement's new half answers for it, because CloudFormation reads
47
+ * UpdateReplacePolicy off the template it is applying. An update that adds
48
+ * the attribute keeps the Resource it replaces, and one that drops it
49
+ * deletes that Resource.
45
50
  */
46
- replacementRetentions(): ReadonlyMap<string, boolean>;
51
+ retention(): SimCfnResourceRetention;
47
52
  /**
48
53
  * Whether the update changes any Resource at all.
49
54
  *
@@ -57,7 +62,9 @@ export declare class SimCfnStackUpdatePlan {
57
62
  * The map is changed in place rather than replaced, so anything already
58
63
  * holding the Stack's Resources sees the update. A Resource the template
59
64
  * leaves alone keeps the object it already had, which is what keeps its
60
- * simulated AWS Resource and its creation status.
65
+ * simulated AWS Resource and its creation status. A Resource updated where it
66
+ * is takes the new record, which the update has already moved on to the
67
+ * deployed simulated AWS Resource.
61
68
  */
62
69
  applyTo(resources: Map<string, SimCfnResource>): void;
63
70
  }
@@ -1,20 +1,27 @@
1
- import { simCfnStackReplacedLogicalIds } from "./sim-cfn-stack-replaced-resources.js";
1
+ import { SimCfnResourceRetention } from "../../resource/delete/sim-cfn-resource-retention.js";
2
+ import { simCfnStackResourceChanges, } from "./sim-cfn-stack-resource-changes.js";
3
+ import { simCfnStackResourceChangePairs, } from "./sim-cfn-stack-resource-change-pairs.js";
2
4
  /**
3
5
  * What an update has to do to a Stack's Resources, worked out before any of it
4
6
  * happens.
5
7
  *
6
8
  * The plan compares the Resources the Stack has with the ones its new template
7
- * describes, and says which to delete, which to create, and what the Stack
8
- * holds afterwards. A Resource the template still describes unchanged is left
9
- * alone, keeping whatever it holds in simulated AWS.
9
+ * describes, and says which to update where they are, which to delete, which to
10
+ * create, and what the Stack holds afterwards. A Resource the template still
11
+ * describes unchanged is left alone, keeping whatever it holds in simulated
12
+ * AWS.
10
13
  *
11
- * It does not delete or create anything, order the work, or decide what counts
12
- * as a changed Resource. SimCfnStackUpdater runs the plan, and
13
- * simCfnStackReplacedLogicalIds decides what changed.
14
+ * It does not delete, create or update anything, order the work, or decide what
15
+ * counts as a changed Resource. SimCfnStackUpdater runs the plan, and
16
+ * simCfnStackResourceChanges decides what changed and how.
14
17
  */
15
18
  export class SimCfnStackUpdatePlan {
16
19
  /** The current and updated halves of each Resource replacement. */
17
20
  replacements;
21
+ /**
22
+ * The Resources to change where they are, rather than by replacing them.
23
+ */
24
+ updates;
18
25
  /**
19
26
  * The deployed Resources to delete: the ones the new template drops, and the
20
27
  * deployed halves of the ones it replaces.
@@ -27,20 +34,15 @@ export class SimCfnStackUpdatePlan {
27
34
  creations;
28
35
  updated;
29
36
  constructor(properties) {
30
- const { current, updated } = properties;
31
- const replaced = simCfnStackReplacedLogicalIds({ current, updated });
37
+ const { current, updated, inPlaceUpdates } = properties;
38
+ const { replaced, updatedInPlace } = simCfnStackResourceChanges({
39
+ current,
40
+ updated,
41
+ inPlaceUpdates,
42
+ });
32
43
  this.updated = updated;
33
- this.replacements = replaced
34
- .values()
35
- .map((logicalId) => {
36
- const currentResource = current.get(logicalId);
37
- const updatedResource = updated.get(logicalId);
38
- if (currentResource === undefined || updatedResource === undefined) {
39
- throw new Error(`CloudFormation replacement ${logicalId} is missing one of its Resource definitions`);
40
- }
41
- return { current: currentResource, updated: updatedResource };
42
- })
43
- .toArray();
44
+ this.replacements = simCfnStackResourceChangePairs(current, updated, replaced);
45
+ this.updates = simCfnStackResourceChangePairs(current, updated, updatedInPlace);
44
46
  // Both lists are worked out now rather than on demand, because the Stack's
45
47
  // Resource map is the current one and applying the plan changes it.
46
48
  this.deletions = current
@@ -57,18 +59,20 @@ export class SimCfnStackUpdatePlan {
57
59
  .toArray();
58
60
  }
59
61
  /**
60
- * For each logical ID the update is replacing, whether the definition taking
61
- * its place says to keep the deployed Resource.
62
+ * What the update's deletions are to leave in simulated AWS.
62
63
  *
63
- * The new half answers it because CloudFormation reads UpdateReplacePolicy
64
- * off the template it is applying. An update that adds the attribute keeps
65
- * the Resource it replaces, and one that drops it deletes that Resource.
64
+ * A replacement's new half answers for it, because CloudFormation reads
65
+ * UpdateReplacePolicy off the template it is applying. An update that adds
66
+ * the attribute keeps the Resource it replaces, and one that drops it
67
+ * deletes that Resource.
66
68
  */
67
- replacementRetentions() {
68
- return new Map(this.replacements.map(({ current, updated }) => [
69
- current.logicalId,
70
- updated.retainedOnReplace,
71
- ]));
69
+ retention() {
70
+ return new SimCfnResourceRetention({
71
+ replaced: new Map(this.replacements.map(({ current, updated }) => [
72
+ current.logicalId,
73
+ updated.retainedOnReplace,
74
+ ])),
75
+ });
72
76
  }
73
77
  /**
74
78
  * Whether the update changes any Resource at all.
@@ -77,7 +81,9 @@ export class SimCfnStackUpdatePlan {
77
81
  * updates the Stack, so this is not the whole no-op question.
78
82
  */
79
83
  get changesResources() {
80
- return this.deletions.length > 0 || this.creations.length > 0;
84
+ return (this.deletions.length > 0 ||
85
+ this.creations.length > 0 ||
86
+ this.updates.length > 0);
81
87
  }
82
88
  /**
83
89
  * Move the Stack's Resource map on to the new template.
@@ -85,7 +91,9 @@ export class SimCfnStackUpdatePlan {
85
91
  * The map is changed in place rather than replaced, so anything already
86
92
  * holding the Stack's Resources sees the update. A Resource the template
87
93
  * leaves alone keeps the object it already had, which is what keeps its
88
- * simulated AWS Resource and its creation status.
94
+ * simulated AWS Resource and its creation status. A Resource updated where it
95
+ * is takes the new record, which the update has already moved on to the
96
+ * deployed simulated AWS Resource.
89
97
  */
90
98
  applyTo(resources) {
91
99
  const dropped = resources
@@ -98,5 +106,8 @@ export class SimCfnStackUpdatePlan {
98
106
  for (const resource of this.creations) {
99
107
  resources.set(resource.logicalId, resource);
100
108
  }
109
+ for (const { updated } of this.updates) {
110
+ resources.set(updated.logicalId, updated);
111
+ }
101
112
  }
102
113
  }
@@ -22,11 +22,13 @@ interface SimCfnStackUpdaterProperties {
22
22
  /**
23
23
  * Applies a changed template to a Stack that is already deployed.
24
24
  *
25
- * The Resources the new template drops or replaces go first, in the reverse of
26
- * the order they were created in, and the ones it adds or replaces are created
27
- * after, in dependency order. Everything else is left where it is, which is the
28
- * point of updating a Stack rather than replacing it: what a Bucket holds and
29
- * what a Table has in it survive a change elsewhere in the template.
25
+ * The Resources a service can change where they are go first, while everything
26
+ * the Stack had is still deployed. Then the Resources the new template drops or
27
+ * replaces, in the reverse of the order they were created in, and last the ones
28
+ * it adds or replaces, in dependency order. Everything else is left where it
29
+ * is, which is the point of updating a Stack rather than replacing it: what a
30
+ * Bucket holds and what a Table has in it survive a change elsewhere in the
31
+ * template.
30
32
  *
31
33
  * It does not own Stack status or the Stack's visible template.
32
34
  * SimCfnStackUpdateLifecycle owns the first and SimCfnStack the second.
@@ -2,7 +2,6 @@ import { SimCloudFormationValidationError } from "../../error/sim-cloudformation
2
2
  import { makeSimCfnStackResourceMap } from "../resource-map/sim-cfn-stack-resource-map.js";
3
3
  import { SimCfnStackUpdatePlan } from "./sim-cfn-stack-update-plan.js";
4
4
  import { simCfnStackTemplateChanged } from "./sim-cfn-stack-template-changes.js";
5
- import { SimCfnResourceRetention } from "../../resource/delete/sim-cfn-resource-retention.js";
6
5
  /**
7
6
  * What CloudFormation answers an update that would change nothing.
8
7
  *
@@ -14,11 +13,13 @@ export const simCfnNoUpdatesMessage = "No updates are to be performed.";
14
13
  /**
15
14
  * Applies a changed template to a Stack that is already deployed.
16
15
  *
17
- * The Resources the new template drops or replaces go first, in the reverse of
18
- * the order they were created in, and the ones it adds or replaces are created
19
- * after, in dependency order. Everything else is left where it is, which is the
20
- * point of updating a Stack rather than replacing it: what a Bucket holds and
21
- * what a Table has in it survive a change elsewhere in the template.
16
+ * The Resources a service can change where they are go first, while everything
17
+ * the Stack had is still deployed. Then the Resources the new template drops or
18
+ * replaces, in the reverse of the order they were created in, and last the ones
19
+ * it adds or replaces, in dependency order. Everything else is left where it
20
+ * is, which is the point of updating a Stack rather than replacing it: what a
21
+ * Bucket holds and what a Table has in it survive a change elsewhere in the
22
+ * template.
22
23
  *
23
24
  * It does not own Stack status or the Stack's visible template.
24
25
  * SimCfnStackUpdateLifecycle owns the first and SimCfnStack the second.
@@ -36,6 +37,7 @@ export class SimCfnStackUpdater {
36
37
  background,
37
38
  template: updated,
38
39
  }),
40
+ inPlaceUpdates: properties.operations,
39
41
  });
40
42
  }
41
43
  /**
@@ -59,7 +61,10 @@ export class SimCfnStackUpdater {
59
61
  const { resources, operations } = this.properties;
60
62
  const { plan } = this;
61
63
  await operations.assertUpdatesAllowed(resources, plan.updated, plan.replacements);
62
- await operations.delete(resources, plan.deletions, new SimCfnResourceRetention({ replaced: plan.replacementRetentions() }));
64
+ // Before anything comes down. An update that fails here has changed some
65
+ // of the Stack's Resources and deleted none of them.
66
+ await operations.updateInPlace(resources, plan.updates);
67
+ await operations.delete(resources, plan.deletions, plan.retention());
63
68
  plan.applyTo(resources);
64
69
  // Once the Stack has stopped holding them, and before the replacements are
65
70
  // created. An update that keeps a Resource and then fails to create the one
@@ -0,0 +1,19 @@
1
+ import type { SimCfnResource } from "../../../cloudformation/resource/sim-cfn-resource.js";
2
+ import type { SimCfnTemplateValueRecord } from "../../../cloudformation/template/value/sim-cfn-template-value.js";
3
+ /**
4
+ * Whether the change between two definitions of a secret replaces it.
5
+ *
6
+ * `Name` is the only property real CloudFormation replaces a secret for. It is
7
+ * read as the template writes it, before intrinsic resolution, which is all the
8
+ * update plan has to go on. Two different expressions resolving to the same
9
+ * name replace the secret, which is the safe way round to be wrong.
10
+ */
11
+ export declare function simCfnSecretsManagerSecretReplaced(current: SimCfnResource, updated: SimCfnResource): boolean;
12
+ /**
13
+ * Whether the new template asks the secret to hold a different value.
14
+ *
15
+ * A secret takes its value from `SecretString` or `GenerateSecretString`, so a
16
+ * change to either is a new version. A change to anything else leaves the
17
+ * versions alone, which is what keeps a generated password across an update.
18
+ */
19
+ export declare function simCfnSecretsManagerSecretValueChanged(currentProperties: SimCfnTemplateValueRecord, updatedProperties: SimCfnTemplateValueRecord): boolean;
@@ -0,0 +1,39 @@
1
+ import { simCfnTemplateSignature } from "../../../cloudformation/stack/update/sim-cfn-template-signature.js";
2
+ /**
3
+ * Whether the change between two definitions of a secret replaces it.
4
+ *
5
+ * `Name` is the only property real CloudFormation replaces a secret for. It is
6
+ * read as the template writes it, before intrinsic resolution, which is all the
7
+ * update plan has to go on. Two different expressions resolving to the same
8
+ * name replace the secret, which is the safe way round to be wrong.
9
+ */
10
+ export function simCfnSecretsManagerSecretReplaced(current, updated) {
11
+ return nameSignature(current) !== nameSignature(updated);
12
+ }
13
+ /**
14
+ * Whether the new template asks the secret to hold a different value.
15
+ *
16
+ * A secret takes its value from `SecretString` or `GenerateSecretString`, so a
17
+ * change to either is a new version. A change to anything else leaves the
18
+ * versions alone, which is what keeps a generated password across an update.
19
+ */
20
+ export function simCfnSecretsManagerSecretValueChanged(currentProperties, updatedProperties) {
21
+ return (valueSignature(currentProperties) !== valueSignature(updatedProperties));
22
+ }
23
+ /**
24
+ * A stable string for the two properties a secret's value comes from.
25
+ *
26
+ * A property the template leaves out signs as null, which no declared value can
27
+ * collide with. `SecretString` is a string and `GenerateSecretString` is an
28
+ * object.
29
+ */
30
+ function valueSignature(properties) {
31
+ return simCfnTemplateSignature([
32
+ properties["SecretString"] ?? null,
33
+ properties["GenerateSecretString"] ?? null,
34
+ ]);
35
+ }
36
+ function nameSignature(resource) {
37
+ const name = resource.properties["Name"];
38
+ return name === undefined ? "" : simCfnTemplateSignature(name);
39
+ }
@@ -0,0 +1,23 @@
1
+ import type { SimCfnResource } from "../../../cloudformation/resource/sim-cfn-resource.js";
2
+ import type { SimCfnResourceCallerOptions } from "../../../cloudformation/resource/caller/sim-cfn-resource-caller-options.js";
3
+ import type { SimSecretsManager } from "../../sim-secrets-manager.js";
4
+ interface SimCfnSecretsManagerSecretDeleterProperties {
5
+ readonly secretsManager: SimSecretsManager;
6
+ }
7
+ /**
8
+ * Removes the secret an AWS::SecretsManager::Secret Resource deployed.
9
+ *
10
+ * DeleteSecret schedules the deletion rather than carrying it out, so a torn
11
+ * down Stack leaves a secret waiting out its recovery window. That is what
12
+ * CloudFormation does, and the point of the window is that the secret is
13
+ * recoverable afterwards.
14
+ */
15
+ export declare class SimCfnSecretsManagerSecretDeleter {
16
+ private readonly secretsManager;
17
+ constructor(properties: SimCfnSecretsManagerSecretDeleterProperties);
18
+ /**
19
+ * Schedule the deployed secret's deletion.
20
+ */
21
+ delete(resource: SimCfnResource, options?: SimCfnResourceCallerOptions): Promise<void>;
22
+ }
23
+ export {};
@@ -0,0 +1,23 @@
1
+ import { assertDefined } from "../../../../util/type-guard/defined.js";
2
+ /**
3
+ * Removes the secret an AWS::SecretsManager::Secret Resource deployed.
4
+ *
5
+ * DeleteSecret schedules the deletion rather than carrying it out, so a torn
6
+ * down Stack leaves a secret waiting out its recovery window. That is what
7
+ * CloudFormation does, and the point of the window is that the secret is
8
+ * recoverable afterwards.
9
+ */
10
+ export class SimCfnSecretsManagerSecretDeleter {
11
+ secretsManager;
12
+ constructor(properties) {
13
+ this.secretsManager = properties.secretsManager;
14
+ }
15
+ /**
16
+ * Schedule the deployed secret's deletion.
17
+ */
18
+ async delete(resource, options) {
19
+ const secret = resource.simResource;
20
+ assertDefined(secret, `sim Secrets Manager secret for CloudFormation Resource ${resource.logicalId}`);
21
+ await this.secretsManager.deleteSecret({ input: { SecretId: secret.arn.value } }, options);
22
+ }
23
+ }
@@ -0,0 +1,21 @@
1
+ import type { CfnTemplateBodyRecord } from "../../../cloudformation/template/sim-cfn-template.js";
2
+ import type { SimCfnTemplateValueRecord } from "../../../cloudformation/template/value/sim-cfn-template-value.js";
3
+ interface SecretTemplateProperties {
4
+ readonly name?: string;
5
+ readonly description?: string;
6
+ readonly kmsKeyId?: string;
7
+ readonly tags?: readonly SimCfnTemplateValueRecord[];
8
+ readonly secretString?: string;
9
+ readonly passwordLength?: number;
10
+ /** A second Resource reading the secret through a dynamic reference. */
11
+ readonly reader?: boolean;
12
+ }
13
+ /**
14
+ * A template holding one secret, and optionally a parameter reading it.
15
+ *
16
+ * The parameter stands in for anything taking a copy of the value when the
17
+ * Stack deploys, such as a CloudFront origin custom header. It reads the secret
18
+ * through the `Fn::Join` shape CDK emits for a secret in the same Stack.
19
+ */
20
+ export declare function secretTemplate(properties?: SecretTemplateProperties): CfnTemplateBodyRecord;
21
+ export {};
@@ -0,0 +1,64 @@
1
+ /**
2
+ * A template holding one secret, and optionally a parameter reading it.
3
+ *
4
+ * The parameter stands in for anything taking a copy of the value when the
5
+ * Stack deploys, such as a CloudFront origin custom header. It reads the secret
6
+ * through the `Fn::Join` shape CDK emits for a secret in the same Stack.
7
+ */
8
+ export function secretTemplate(properties = {}) {
9
+ const secret = {
10
+ Type: "AWS::SecretsManager::Secret",
11
+ Properties: secretProperties(properties),
12
+ };
13
+ return {
14
+ Resources: properties.reader === true
15
+ ? { EdgeCredential: secret, Reader: readerResource() }
16
+ : { EdgeCredential: secret },
17
+ };
18
+ }
19
+ function secretProperties(properties) {
20
+ const { name, description, kmsKeyId, tags, secretString } = properties;
21
+ const declared = {};
22
+ if (name !== undefined) {
23
+ declared["Name"] = name;
24
+ }
25
+ if (description !== undefined) {
26
+ declared["Description"] = description;
27
+ }
28
+ if (kmsKeyId !== undefined) {
29
+ declared["KmsKeyId"] = kmsKeyId;
30
+ }
31
+ if (tags !== undefined) {
32
+ declared["Tags"] = [...tags];
33
+ }
34
+ if (secretString === undefined) {
35
+ declared["GenerateSecretString"] = {
36
+ SecretStringTemplate: "{}",
37
+ GenerateStringKey: "current",
38
+ PasswordLength: properties.passwordLength ?? 64,
39
+ };
40
+ }
41
+ else {
42
+ declared["SecretString"] = secretString;
43
+ }
44
+ return declared;
45
+ }
46
+ function readerResource() {
47
+ return {
48
+ Type: "AWS::SSM::Parameter",
49
+ Properties: {
50
+ Name: "edge-credential-copy",
51
+ Type: "String",
52
+ Value: {
53
+ "Fn::Join": [
54
+ "",
55
+ [
56
+ "{{resolve:secretsmanager:",
57
+ { Ref: "EdgeCredential" },
58
+ ":SecretString:current::}}",
59
+ ],
60
+ ],
61
+ },
62
+ },
63
+ };
64
+ }
@@ -0,0 +1,18 @@
1
+ import { SimAws } from "../../../aws/sim-aws.js";
2
+ import type { CfnTemplateBodyRecord } from "../../../cloudformation/template/sim-cfn-template.js";
3
+ /**
4
+ * A simulated AWS in one account and region, for a secret update test.
5
+ */
6
+ export declare function secretUpdateSimAws(): SimAws;
7
+ /**
8
+ * Deploy a template and wait for its Resources.
9
+ */
10
+ export declare function deploySecretStack(simAws: SimAws, stackName: string, template: CfnTemplateBodyRecord): Promise<void>;
11
+ /**
12
+ * Apply a template to a deployed stack and wait for the update.
13
+ */
14
+ export declare function updateSecretStack(simAws: SimAws, stackName: string, template: CfnTemplateBodyRecord): Promise<void>;
15
+ /**
16
+ * The ARN of the secret a deployed stack holds.
17
+ */
18
+ export declare function deployedSecretArn(simAws: SimAws, stackName: string): string;
@@ -0,0 +1,39 @@
1
+ import { assertDefined } from "../../../../util/type-guard/defined.js";
2
+ import { SimAws } from "../../../aws/sim-aws.js";
3
+ import { jsonStringify } from "../../../../util/type-guard/json.js";
4
+ /**
5
+ * A simulated AWS in one account and region, for a secret update test.
6
+ */
7
+ export function secretUpdateSimAws() {
8
+ return new SimAws({
9
+ defaultAccountId: "111111111111",
10
+ defaultRegionName: "eu-west-2",
11
+ });
12
+ }
13
+ /**
14
+ * Deploy a template and wait for its Resources.
15
+ */
16
+ export async function deploySecretStack(simAws, stackName, template) {
17
+ const stack = await simAws
18
+ .cloudFormation()
19
+ .deployTemplate({ stackName, template });
20
+ await stack.waitForDeployComplete();
21
+ }
22
+ /**
23
+ * Apply a template to a deployed stack and wait for the update.
24
+ */
25
+ export async function updateSecretStack(simAws, stackName, template) {
26
+ await simAws.cloudFormation().updateStack({
27
+ input: { StackName: stackName, TemplateBody: jsonStringify(template) },
28
+ });
29
+ await simAws.cloudFormation().waitForStackUpdateComplete(stackName);
30
+ }
31
+ /**
32
+ * The ARN of the secret a deployed stack holds.
33
+ */
34
+ export function deployedSecretArn(simAws, stackName) {
35
+ const stack = simAws.cloudFormation().getStackByName(stackName);
36
+ const secret = stack?.getResource("EdgeCredential")?.simResource;
37
+ assertDefined(secret, `deployed EdgeCredential secret in Stack ${stackName}`);
38
+ return secret.arn.value;
39
+ }
@@ -0,0 +1,37 @@
1
+ import type { SimCfnResource } from "../../../cloudformation/resource/sim-cfn-resource.js";
2
+ import type { SimCloudFormationResourceInPlaceUpdateContext } from "../../../cloudformation/resource/sim-cfn-resource.type.js";
3
+ import type { SimSecretsManagerSecret } from "../../secret/sim-secrets-manager-secret.js";
4
+ import type { SimSecretsManager } from "../../sim-secrets-manager.js";
5
+ interface SimCfnSecretsManagerSecretUpdaterProperties {
6
+ readonly secretsManager: SimSecretsManager;
7
+ }
8
+ /**
9
+ * Applies a changed AWS::SecretsManager::Secret Resource to the deployed
10
+ * secret.
11
+ *
12
+ * Every property but `Name` is applied with no interruption, as real
13
+ * CloudFormation applies it. That matters most for the value. A secret keeps
14
+ * its versions across a description or a tag change, and the things holding a
15
+ * copy of the value stay in step with it.
16
+ *
17
+ * Replacing instead would also fail. DeleteSecret schedules the deletion and
18
+ * leaves the name held for the recovery window. The secret taking its place
19
+ * would then ask for a name the deleted one still has.
20
+ */
21
+ export declare class SimCfnSecretsManagerSecretUpdater {
22
+ private readonly secretsManager;
23
+ constructor(properties: SimCfnSecretsManagerSecretUpdaterProperties);
24
+ /**
25
+ * Apply the new Resource definition to the deployed secret.
26
+ */
27
+ update(current: SimCfnResource, updated: SimCfnResource, context: SimCloudFormationResourceInPlaceUpdateContext): Promise<SimSecretsManagerSecret>;
28
+ /**
29
+ * The value to write, where the new template asks for a different one.
30
+ *
31
+ * Nothing is written for an unchanged value. Regenerating a password for a
32
+ * description change would leave every copy of it taken at deployment
33
+ * holding the old one.
34
+ */
35
+ private changedValue;
36
+ }
37
+ export {};
@@ -0,0 +1,66 @@
1
+ import { assertDefined } from "../../../../util/type-guard/defined.js";
2
+ import { simCfnResourceCallerOptions } from "../../../cloudformation/resource/caller/sim-cfn-resource-caller-options.js";
3
+ import { simCfnSecretsManagerSecretValueChanged } from "./sim-cfn-secrets-manager-secret-change.js";
4
+ import { SimCfnSecretsManagerSecretProperties } from "./sim-cfn-secrets-manager-secret-properties.js";
5
+ /**
6
+ * Applies a changed AWS::SecretsManager::Secret Resource to the deployed
7
+ * secret.
8
+ *
9
+ * Every property but `Name` is applied with no interruption, as real
10
+ * CloudFormation applies it. That matters most for the value. A secret keeps
11
+ * its versions across a description or a tag change, and the things holding a
12
+ * copy of the value stay in step with it.
13
+ *
14
+ * Replacing instead would also fail. DeleteSecret schedules the deletion and
15
+ * leaves the name held for the recovery window. The secret taking its place
16
+ * would then ask for a name the deleted one still has.
17
+ */
18
+ export class SimCfnSecretsManagerSecretUpdater {
19
+ secretsManager;
20
+ constructor(properties) {
21
+ this.secretsManager = properties.secretsManager;
22
+ }
23
+ /**
24
+ * Apply the new Resource definition to the deployed secret.
25
+ */
26
+ async update(current, updated, context) {
27
+ const secret = current.simResource;
28
+ assertDefined(secret, `sim Secrets Manager secret for CloudFormation Resource ${current.logicalId}`);
29
+ const properties = new SimCfnSecretsManagerSecretProperties({
30
+ resource: updated,
31
+ properties: context.updatedResolvedProperties,
32
+ });
33
+ const kmsKeyId = properties.kmsKeyId();
34
+ await this.secretsManager.updateSecret({
35
+ input: {
36
+ SecretId: secret.arn.value,
37
+ // CloudFormation applies the template as the desired state, so a
38
+ // Description the new template leaves out is cleared rather than
39
+ // left as it was. An empty one is how UpdateSecret clears it.
40
+ Description: properties.description() ?? "",
41
+ KmsKeyId: kmsKeyId,
42
+ SecretString: this.changedValue(properties, context),
43
+ },
44
+ }, simCfnResourceCallerOptions(context.caller));
45
+ // The parts UpdateSecret has no way to ask for. Real Secrets Manager
46
+ // changes tags with TagResource and UntagResource, which are not
47
+ // simulated, and UpdateSecret cannot put a secret back on the
48
+ // aws/secretsmanager key.
49
+ if (kmsKeyId === undefined) {
50
+ secret.kmsKeyId = undefined;
51
+ }
52
+ secret.tags = properties.tags() ?? [];
53
+ return secret;
54
+ }
55
+ /**
56
+ * The value to write, where the new template asks for a different one.
57
+ *
58
+ * Nothing is written for an unchanged value. Regenerating a password for a
59
+ * description change would leave every copy of it taken at deployment
60
+ * holding the old one.
61
+ */
62
+ changedValue(properties, context) {
63
+ const changed = simCfnSecretsManagerSecretValueChanged(context.currentResolvedProperties, context.updatedResolvedProperties);
64
+ return changed ? properties.secretString() : undefined;
65
+ }
66
+ }