@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,33 +1,43 @@
1
1
  import type { SimCfnServiceResourceFactory } from "../../cloudformation/resource/factory/sim-cfn-resource-factory.type.js";
2
2
  import type { SimCfnResource, SimCloudFormationResourceCreateContext, SimCloudFormationResourceDeleteContext } from "../../cloudformation/resource/sim-cfn-resource.js";
3
+ import type { SimCloudFormationResourceInPlaceUpdateContext } from "../../cloudformation/resource/sim-cfn-resource.type.js";
3
4
  import type { SimSecretsManager } from "../sim-secrets-manager.js";
4
5
  interface SimSecretsManagerCfnResourceFactoryProperties {
5
6
  readonly secretsManager: SimSecretsManager;
6
7
  }
7
8
  /**
8
9
  * CloudFormation Resource factory for simulated Secrets Manager resources.
10
+ *
11
+ * Only `Secret` is deployed. Rotation, resource policies and target
12
+ * attachments are not simulated, so their Resource types are reported as
13
+ * unsupported and skipped rather than quietly treated as deployed.
9
14
  */
10
15
  export declare class SimSecretsManagerCfnResourceFactory implements SimCfnServiceResourceFactory {
11
- private readonly secretsManager;
12
16
  private readonly secretCreator;
17
+ private readonly secretUpdater;
18
+ private readonly secretDeleter;
13
19
  constructor(properties: SimSecretsManagerCfnResourceFactoryProperties);
14
20
  /**
15
21
  * Create a simulated Secrets Manager resource from a CloudFormation
16
22
  * Resource.
17
- *
18
- * Rotation, resource policies and target attachments are not simulated, so
19
- * their Resource types are reported as unsupported and skipped rather than
20
- * quietly treated as deployed.
21
23
  */
22
24
  create(resourceTypeName: string, resource: SimCfnResource, context: SimCloudFormationResourceCreateContext): Promise<object | undefined>;
25
+ /**
26
+ * Whether a changed secret can be applied to the deployed one.
27
+ *
28
+ * Name is the only property real CloudFormation replaces a secret for.
29
+ * Everything else is applied with no interruption, which is what keeps a
30
+ * generated value across a change to the secret's description or tags.
31
+ */
32
+ updatesInPlace(resourceTypeName: string, current: SimCfnResource, updated: SimCfnResource): boolean;
33
+ /**
34
+ * Apply a changed AWS::SecretsManager::Secret Resource to the deployed
35
+ * secret.
36
+ */
37
+ updateInPlace(resourceTypeName: string, current: SimCfnResource, updated: SimCfnResource, context: SimCloudFormationResourceInPlaceUpdateContext): Promise<object | undefined>;
23
38
  /**
24
39
  * Delete a simulated Secrets Manager resource created from a CloudFormation
25
40
  * Resource.
26
- *
27
- * DeleteSecret schedules the deletion rather than carrying it out, so a torn
28
- * down Stack leaves a secret waiting out its recovery window. That is what
29
- * CloudFormation does: the secret is recoverable afterwards, which is the
30
- * point of the window.
31
41
  */
32
42
  delete(resourceTypeName: string, resource: SimCfnResource, context: SimCloudFormationResourceDeleteContext): Promise<void>;
33
43
  }
@@ -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 { assertDefined } from "../../../util/type-guard/defined.js";
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
- this.secretsManager = properties.secretsManager;
19
+ const { secretsManager } = properties;
12
20
  this.secretCreator = new SimCfnSecretsManagerSecretCreator({
13
- secretsManager: properties.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
- switch (resourceTypeName) {
26
- case "Secret": {
27
- return await this.secretCreator.create(resource, context.resolvedProperties ?? resource.properties, simCfnResourceCallerOptions(context.caller));
28
- }
29
- default: {
30
- throw new Error(`Unsupported sim Secrets Manager CloudFormation Resource ${resourceTypeName}`);
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
- if (resourceTypeName !== "Secret") {
45
- throw new Error(`Unsupported sim Secrets Manager CloudFormation Resource ${resourceTypeName} deletion`);
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
+ }
@@ -26,10 +26,17 @@ export declare class BackgroundClockWaits {
26
26
  */
27
27
  around<T>(wait: BackgroundClockWait, work: () => Promise<T>): Promise<T>;
28
28
  /**
29
- * The work in a set that is neither waiting on the clock nor the work asking
30
- * about it.
29
+ * The work in a set that completion has anything to wait for.
30
+ *
31
+ * The work asking is always left out. Waiting for the invocation the asking
32
+ * code is part of would be waiting for itself.
33
+ *
34
+ * Work waiting on the clock is left out where simulated time stands still,
35
+ * since only a caller moving the clock brings the instant it waits for, and
36
+ * that caller is the one waiting here. A running clock reaches the instant on
37
+ * its own, and this waits for the work to get there.
31
38
  */
32
- runnable(held: ReadonlyMap<Promise<unknown>, BackgroundClockWait>): Promise<unknown>[];
39
+ runnable(held: ReadonlyMap<Promise<unknown>, BackgroundClockWait>, clockIsRunning?: boolean): Promise<unknown>[];
33
40
  /**
34
41
  * Whether the code running now is already inside outstanding work, and so
35
42
  * already counted.
@@ -23,14 +23,21 @@ export class BackgroundClockWaits {
23
23
  return await this.storage.run(wait, work);
24
24
  }
25
25
  /**
26
- * The work in a set that is neither waiting on the clock nor the work asking
27
- * about it.
26
+ * The work in a set that completion has anything to wait for.
27
+ *
28
+ * The work asking is always left out. Waiting for the invocation the asking
29
+ * code is part of would be waiting for itself.
30
+ *
31
+ * Work waiting on the clock is left out where simulated time stands still,
32
+ * since only a caller moving the clock brings the instant it waits for, and
33
+ * that caller is the one waiting here. A running clock reaches the instant on
34
+ * its own, and this waits for the work to get there.
28
35
  */
29
- runnable(held) {
36
+ runnable(held, clockIsRunning = false) {
30
37
  const own = this.storage.getStore();
31
38
  return held
32
39
  .entries()
33
- .filter(([, wait]) => wait.count === 0 && wait !== own)
40
+ .filter(([, wait]) => wait !== own && (clockIsRunning || wait.count === 0))
34
41
  .map(([promise]) => promise)
35
42
  .toArray();
36
43
  }
@@ -35,21 +35,27 @@ export declare class BackgroundPendingTasks {
35
35
  */
36
36
  get size(): number;
37
37
  /**
38
- * Wait until nothing is outstanding but work that is waiting on the clock.
38
+ * Wait until nothing is outstanding that can still get somewhere.
39
39
  *
40
- * Work that parks part way through ends the wait rather than extending it:
41
- * only moving the clock releases it, and moving the clock is what waits
42
- * here. The loop then looks again at what is left running, so work that
43
- * carries on is waited for again.
40
+ * Where simulated time stands still, work that parks on the clock ends the
41
+ * wait rather than extending it. Only moving the clock releases it, and
42
+ * moving the clock is what waits here. The loop then looks again at what is
43
+ * left running, so work that carries on is waited for again.
44
+ *
45
+ * A running clock reaches those instants by itself, and the work parked on
46
+ * them is waited for like any other. What bounds the wait is the instant
47
+ * itself, which arrives in the real time between here and there.
44
48
  */
45
- complete(): Promise<void>;
49
+ complete(clockIsRunning?: boolean): Promise<void>;
46
50
  /**
47
- * The work completion has anything to wait for, which is whatever is
48
- * neither waiting on the clock nor waiting for completion itself.
51
+ * The work completion has anything to wait for.
49
52
  *
50
53
  * Work that asks for the simulation to settle from inside itself is left
51
54
  * out. A handler advancing the clock is the ordinary case, and waiting for
52
55
  * the invocation it is part of would be waiting for itself.
56
+ *
57
+ * Work waiting on the clock joins it once simulated time is running, since
58
+ * a running clock is what brings the instant it waits for.
53
59
  */
54
- running(): Promise<unknown>[];
60
+ running(clockIsRunning?: boolean): Promise<unknown>[];
55
61
  }
@@ -52,15 +52,19 @@ export class BackgroundPendingTasks {
52
52
  return this.#held.size;
53
53
  }
54
54
  /**
55
- * Wait until nothing is outstanding but work that is waiting on the clock.
55
+ * Wait until nothing is outstanding that can still get somewhere.
56
56
  *
57
- * Work that parks part way through ends the wait rather than extending it:
58
- * only moving the clock releases it, and moving the clock is what waits
59
- * here. The loop then looks again at what is left running, so work that
60
- * carries on is waited for again.
57
+ * Where simulated time stands still, work that parks on the clock ends the
58
+ * wait rather than extending it. Only moving the clock releases it, and
59
+ * moving the clock is what waits here. The loop then looks again at what is
60
+ * left running, so work that carries on is waited for again.
61
+ *
62
+ * A running clock reaches those instants by itself, and the work parked on
63
+ * them is waited for like any other. What bounds the wait is the instant
64
+ * itself, which arrives in the real time between here and there.
61
65
  */
62
- async complete() {
63
- let running = this.running();
66
+ async complete(clockIsRunning = false) {
67
+ let running = this.running(clockIsRunning);
64
68
  while (running.length > 0) {
65
69
  // oxlint-disable-next-line no-await-in-loop
66
70
  const settled = await Promise.race([
@@ -68,19 +72,21 @@ export class BackgroundPendingTasks {
68
72
  this.#clockWaits.next,
69
73
  ]);
70
74
  new BackgroundSettledTasks(settled).throwFirstFailure();
71
- running = this.running();
75
+ running = this.running(clockIsRunning);
72
76
  }
73
77
  }
74
78
  /**
75
- * The work completion has anything to wait for, which is whatever is
76
- * neither waiting on the clock nor waiting for completion itself.
79
+ * The work completion has anything to wait for.
77
80
  *
78
81
  * Work that asks for the simulation to settle from inside itself is left
79
82
  * out. A handler advancing the clock is the ordinary case, and waiting for
80
83
  * the invocation it is part of would be waiting for itself.
84
+ *
85
+ * Work waiting on the clock joins it once simulated time is running, since
86
+ * a running clock is what brings the instant it waits for.
81
87
  */
82
- running() {
83
- return this.#clockWaits.runnable(this.#held);
88
+ running(clockIsRunning = false) {
89
+ return this.#clockWaits.runnable(this.#held, clockIsRunning);
84
90
  }
85
91
  /**
86
92
  * Keep work outstanding under its own record until it settles.
@@ -141,9 +141,11 @@ export declare class BackgroundTasks implements BackgroundScheduler, BackgroundC
141
141
  * Wait until all tasks currently scheduled have finished.
142
142
  * If tasks schedule more tasks, this will continue draining until idle.
143
143
  *
144
- * Work waiting on the clock is not waited for: it is not outstanding, it is
145
- * scheduled for a simulated instant that has not arrived. Only moving the
146
- * clock releases it.
144
+ * What happens to work waiting on the clock follows the clock. Where
145
+ * simulated time stands still the work stays where it is, scheduled for an
146
+ * instant only a caller can bring, and this returns without it. Where
147
+ * simulated time runs, the instant arrives by itself and this waits for the
148
+ * work to get through it.
147
149
  */
148
150
  complete(): Promise<void>;
149
151
  /**
@@ -154,5 +156,11 @@ export declare class BackgroundTasks implements BackgroundScheduler, BackgroundC
154
156
  * See how many tasks are waiting for simulated time to reach them.
155
157
  */
156
158
  get dueTaskCount(): number;
159
+ /**
160
+ * Whether simulated time reaches a scheduled instant on its own.
161
+ *
162
+ * A clock with nothing to say about it advances, as the host clock does.
163
+ */
164
+ private get clockIsRunning();
157
165
  }
158
166
  export {};
@@ -79,12 +79,14 @@ export class BackgroundTasks {
79
79
  * Wait until all tasks currently scheduled have finished.
80
80
  * If tasks schedule more tasks, this will continue draining until idle.
81
81
  *
82
- * Work waiting on the clock is not waited for: it is not outstanding, it is
83
- * scheduled for a simulated instant that has not arrived. Only moving the
84
- * clock releases it.
82
+ * What happens to work waiting on the clock follows the clock. Where
83
+ * simulated time stands still the work stays where it is, scheduled for an
84
+ * instant only a caller can bring, and this returns without it. Where
85
+ * simulated time runs, the instant arrives by itself and this waits for the
86
+ * work to get through it.
85
87
  */
86
88
  async complete() {
87
- await this.pending.complete();
89
+ await this.pending.complete(this.clockIsRunning);
88
90
  }
89
91
  /**
90
92
  * See how many outstanding background tasks are scheduled.
@@ -98,4 +100,12 @@ export class BackgroundTasks {
98
100
  get dueTaskCount() {
99
101
  return this.dueTasks.size;
100
102
  }
103
+ /**
104
+ * Whether simulated time reaches a scheduled instant on its own.
105
+ *
106
+ * A clock with nothing to say about it advances, as the host clock does.
107
+ */
108
+ get clockIsRunning() {
109
+ return this.clock.advances !== false;
110
+ }
101
111
  }
@@ -55,6 +55,10 @@ export declare class NonDeterministicBackgroundTasks implements BackgroundSchedu
55
55
  /**
56
56
  * Wait until all tasks currently scheduled have finished.
57
57
  * If tasks schedule more tasks, this will continue draining until idle.
58
+ *
59
+ * Work waiting on the clock is waited for where simulated time runs, and
60
+ * left where it is where simulated time stands still, exactly as it is on
61
+ * the deterministic scheduler.
58
62
  */
59
63
  complete(): Promise<void>;
60
64
  /**
@@ -79,9 +79,13 @@ export class NonDeterministicBackgroundTasks {
79
79
  /**
80
80
  * Wait until all tasks currently scheduled have finished.
81
81
  * If tasks schedule more tasks, this will continue draining until idle.
82
+ *
83
+ * Work waiting on the clock is waited for where simulated time runs, and
84
+ * left where it is where simulated time stands still, exactly as it is on
85
+ * the deterministic scheduler.
82
86
  */
83
87
  async complete() {
84
- await this.pending.complete();
88
+ await this.pending.complete(this.clock.advances !== false);
85
89
  }
86
90
  /**
87
91
  * See how many outstanding background tasks are scheduled.
@@ -11,6 +11,18 @@ export interface SimClock {
11
11
  * Get the current time in this simulation.
12
12
  */
13
13
  now(): Date;
14
+ /**
15
+ * Whether simulated time moves on this clock by itself.
16
+ *
17
+ * A scheduler reads this to decide what waiting for a simulation to settle
18
+ * means. Time that advances brings the instant a task is scheduled for on
19
+ * its own, and settling waits for that task. Time that stands still leaves
20
+ * the instant to whoever moves the clock, and settling leaves the task where
21
+ * it is.
22
+ *
23
+ * A clock that says nothing advances, as the host clock does.
24
+ */
25
+ readonly advances?: boolean;
14
26
  }
15
27
  /**
16
28
  * Clock that reports the real system time.
@@ -19,6 +31,8 @@ export interface SimClock {
19
31
  * something deliberately replaces it.
20
32
  */
21
33
  export declare class SimRealClock implements SimClock {
34
+ /** Real time moves by itself. */
35
+ readonly advances = true;
22
36
  /**
23
37
  * Get the current real system time.
24
38
  */
@@ -31,6 +45,8 @@ export declare class SimRealClock implements SimClock {
31
45
  * reaching for a library that replaces the clock for the whole process.
32
46
  */
33
47
  export declare class SimFixedClock implements SimClock {
48
+ /** A fixed instant is the whole of this clock. Time stands still on it. */
49
+ readonly advances = false;
34
50
  private readonly instant;
35
51
  constructor(instant: Date);
36
52
  /**
@@ -5,6 +5,8 @@
5
5
  * something deliberately replaces it.
6
6
  */
7
7
  export class SimRealClock {
8
+ /** Real time moves by itself. */
9
+ advances = true;
8
10
  /**
9
11
  * Get the current real system time.
10
12
  */
@@ -19,6 +21,8 @@ export class SimRealClock {
19
21
  * reaching for a library that replaces the clock for the whole process.
20
22
  */
21
23
  export class SimFixedClock {
24
+ /** A fixed instant is the whole of this clock. Time stands still on it. */
25
+ advances = false;
22
26
  instant;
23
27
  constructor(instant) {
24
28
  this.instant = new Date(instant);
@@ -33,6 +33,17 @@ export declare class SimControllableClock implements SimClock {
33
33
  * Whether simulated time is currently standing still.
34
34
  */
35
35
  get isFrozen(): boolean;
36
+ /**
37
+ * Whether simulated time moves here by itself.
38
+ *
39
+ * A frozen clock moves only where something moves it. So does a running
40
+ * clock over a base that stands still. The offset a running mode applies is
41
+ * fixed, so simulated time here moves exactly as far as the clock underneath
42
+ * moves, and a fixed clock underneath moves nowhere. Freezing and resuming
43
+ * such a clock changes which instant it reports and leaves it as still as it
44
+ * was.
45
+ */
46
+ get advances(): boolean;
36
47
  /**
37
48
  * Stop simulated time where it currently reads.
38
49
  */
@@ -32,6 +32,19 @@ export class SimControllableClock {
32
32
  get isFrozen() {
33
33
  return this.mode.isFrozen;
34
34
  }
35
+ /**
36
+ * Whether simulated time moves here by itself.
37
+ *
38
+ * A frozen clock moves only where something moves it. So does a running
39
+ * clock over a base that stands still. The offset a running mode applies is
40
+ * fixed, so simulated time here moves exactly as far as the clock underneath
41
+ * moves, and a fixed clock underneath moves nowhere. Freezing and resuming
42
+ * such a clock changes which instant it reports and leaves it as still as it
43
+ * was.
44
+ */
45
+ get advances() {
46
+ return !this.mode.isFrozen && this.base.advances !== false;
47
+ }
35
48
  /**
36
49
  * Stop simulated time where it currently reads.
37
50
  */
@@ -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. See [changed resources are replaced](#changed-resources-are-replaced).
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`, because a changed resource is replaced
4402
- rather than updated in place. `ChangeSetType: IMPORT` is refused, and so is a `CREATE` change set
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
@@ -3269,6 +3269,17 @@ An interval runs once for each period an advance covers. Advancing eleven second
3269
3269
  interval runs it five times. A delay of zero, or none at all, is due at the instant it was asked
3270
3270
  for, and a handler yielding with `setTimeout(resolve, 0)` gets going again without the clock moving.
3271
3271
 
3272
+ Where the clock is left running, the delay passes in real time and nothing has to move it.
3273
+ `simAws.backgroundTasksComplete()` waits for a handler sleeping on a clock that is moving. An S3
3274
+ event notification, a stream record or an asynchronous invocation whose handler uses a timer has
3275
+ finished by the time the drain returns, and the function's `Timeout` bounds how long that wait can
3276
+ last.
3277
+
3278
+ Two things leave a clock standing still. `freeze()`, `advanceBy(...)` and `setTo(...)` all leave it
3279
+ frozen, and a `SimAws` built on a `SimFixedClock` reports one instant however long the host runs.
3280
+ Under either the drain comes back while the handler sleeps, and moving the clock is what releases
3281
+ it.
3282
+
3272
3283
  The function's `Timeout` is a deadline on the same clock. Where it arrives before the handler
3273
3284
  answers, the invocation ends in the error the real runtime reports.
3274
3285
 
@@ -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.
@@ -63,6 +63,13 @@ example, a simulation advanced by one hour continues to run one hour ahead of th
63
63
 
64
64
  Read `simAws.clock().isFrozen` to check the current mode.
65
65
 
66
+ The mode also decides what waiting for the simulation to settle means.
67
+ `simAws.backgroundTasksComplete()` waits for background work sleeping on a clock that is moving,
68
+ because the instant that work sleeps until arrives by itself. Under a clock standing still the same
69
+ work is left where it is, and `advanceBy(...)` or a forward `setTo(...)` is what brings the instant
70
+ and waits for what follows from it. A simulation built on a `SimFixedClock` stands still under a
71
+ running mode too, since simulated time here moves only as far as the clock underneath moves.
72
+
66
73
  ## Advancing time
67
74
 
68
75
  `advanceBy(...)` accepts days, hours, minutes, seconds, and milliseconds. The values are added