@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.
- 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/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/dist/util/background/background-clock-waits.d.ts +10 -3
- package/dist/util/background/background-clock-waits.js +11 -4
- package/dist/util/background/background-pending-tasks.d.ts +15 -9
- package/dist/util/background/background-pending-tasks.js +18 -12
- package/dist/util/background/background.d.ts +11 -3
- package/dist/util/background/background.js +14 -4
- package/dist/util/background/non-deterministic-background.d.ts +4 -0
- package/dist/util/background/non-deterministic-background.js +5 -1
- package/dist/util/clock/sim-clock.d.ts +16 -0
- package/dist/util/clock/sim-clock.js +4 -0
- package/dist/util/clock/sim-controllable-clock.d.ts +11 -0
- package/dist/util/clock/sim-controllable-clock.js +13 -0
- package/docs/services/cloudformation/README.md +21 -3
- package/docs/services/lambda/README.md +11 -0
- package/docs/services/secretsmanager/README.md +35 -1
- package/docs/time/README.md +7 -0
- 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,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
|
|
16
|
-
* holds afterwards. A Resource the template still
|
|
17
|
-
* alone, keeping whatever it holds in simulated
|
|
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
|
|
20
|
-
* as a changed Resource. SimCfnStackUpdater runs the plan, and
|
|
21
|
-
*
|
|
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
|
|
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
|
-
*
|
|
40
|
-
* its place says to keep the deployed Resource.
|
|
44
|
+
* What the update's deletions are to leave in simulated AWS.
|
|
41
45
|
*
|
|
42
|
-
*
|
|
43
|
-
* off the template it is applying. An update that adds
|
|
44
|
-
* the Resource it replaces, and one that drops it
|
|
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
|
-
|
|
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 {
|
|
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
|
|
8
|
-
* holds afterwards. A Resource the template still
|
|
9
|
-
* alone, keeping whatever it holds in simulated
|
|
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
|
|
12
|
-
* as a changed Resource. SimCfnStackUpdater runs the plan, and
|
|
13
|
-
*
|
|
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 =
|
|
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
|
-
|
|
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
|
-
*
|
|
61
|
-
* its place says to keep the deployed Resource.
|
|
62
|
+
* What the update's deletions are to leave in simulated AWS.
|
|
62
63
|
*
|
|
63
|
-
*
|
|
64
|
-
* off the template it is applying. An update that adds
|
|
65
|
-
* the Resource it replaces, and one that drops it
|
|
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
|
-
|
|
68
|
-
return new
|
|
69
|
-
current
|
|
70
|
-
|
|
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 ||
|
|
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
|
|
26
|
-
* the
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
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
|
|
18
|
-
* the
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
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
|
-
|
|
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;
|
package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-update.test-support.js
ADDED
|
@@ -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
|
+
}
|