@kensio/yulin 1.21.4 → 1.21.5

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 (36) hide show
  1. package/dist/service/cloudformation/cdk/assets/sim-cdk-assets-publisher.d.ts +18 -13
  2. package/dist/service/cloudformation/cdk/assets/sim-cdk-assets-publisher.js +16 -24
  3. package/dist/service/cloudformation/cdk/sim-cdk-cloud-assembly.factory.d.ts +12 -0
  4. package/dist/service/cloudformation/cdk/sim-cdk-cloud-assembly.factory.js +16 -7
  5. package/dist/service/cloudformation/command/create-stack/create-stack.handler.d.ts +3 -0
  6. package/dist/service/cloudformation/command/create-stack/create-stack.handler.js +4 -1
  7. package/dist/service/cloudformation/command/update-stack/update-stack.handler.d.ts +6 -0
  8. package/dist/service/cloudformation/command/update-stack/update-stack.handler.js +7 -1
  9. package/dist/service/cloudformation/deploy/sim-cfn-cdk-out-deployer.js +2 -1
  10. package/dist/service/cloudformation/deploy/sim-cfn-cdk-out-options-deployed.d.ts +10 -0
  11. package/dist/service/cloudformation/deploy/sim-cfn-cdk-out-options-deployed.js +14 -0
  12. package/dist/service/cloudformation/deploy/sim-cfn-cdk-out-plan.d.ts +11 -1
  13. package/dist/service/cloudformation/deploy/sim-cfn-cdk-out-plan.js +3 -2
  14. package/dist/service/cloudformation/deploy/sim-cfn-cdk-out-stack-options.d.ts +5 -8
  15. package/dist/service/cloudformation/deploy/sim-cfn-cdk-out-stack-options.js +0 -14
  16. package/dist/service/cloudformation/deploy/sim-cfn-template-deployer.d.ts +9 -0
  17. package/dist/service/cloudformation/deploy/sim-cfn-template-deployer.js +2 -0
  18. package/dist/service/cloudformation/deploy/sim-cfn-template-file-loader.d.ts +10 -0
  19. package/dist/service/cloudformation/deploy/sim-cfn-template-file-loader.js +2 -1
  20. package/dist/service/cloudformation/deploy/sim-cfn-template-file-updater.js +1 -0
  21. package/dist/service/cloudformation/stack/sim-cfn-stack-deploy-context.d.ts +47 -0
  22. package/dist/service/cloudformation/stack/sim-cfn-stack-deploy-context.js +46 -0
  23. package/dist/service/cloudformation/stack/sim-cfn-stack-resource-operations.d.ts +9 -19
  24. package/dist/service/cloudformation/stack/sim-cfn-stack-resource-operations.js +12 -26
  25. package/dist/service/cloudformation/stack/sim-cfn-stack.js +2 -1
  26. package/dist/service/cloudformation/stack/sim-cfn-stack.type.d.ts +14 -0
  27. package/dist/service/s3/bucket/sim-s3-bucket-maker.d.ts +31 -0
  28. package/dist/service/s3/bucket/sim-s3-bucket-maker.js +41 -0
  29. package/dist/service/s3/command/create-bucket/create-bucket.handler.d.ts +2 -2
  30. package/dist/service/s3/command/create-bucket/create-bucket.handler.js +4 -12
  31. package/dist/service/s3/sim-s3-commands.d.ts +6 -0
  32. package/dist/service/s3/sim-s3-commands.js +7 -0
  33. package/dist/service/s3/sim-s3.d.ts +16 -0
  34. package/dist/service/s3/sim-s3.js +25 -0
  35. package/docs/services/cloudformation/README.md +56 -2
  36. package/package.json +2 -2
@@ -8,12 +8,16 @@ interface SimCdkAssetsPublisherProperties {
8
8
  readonly stackName?: string | undefined;
9
9
  readonly cdkOutContext?: SimCdkOutContext | undefined;
10
10
  /**
11
- * The principal the deployment runs as, which the staging Bucket is created
12
- * and written to as. A real `cdk deploy` publishes assets before
13
- * CloudFormation sees the template, and it publishes them as somebody; a
14
- * deployment that names a caller is what says who.
11
+ * The principal the assets are published as.
12
+ *
13
+ * A real `cdk deploy` publishes assets before CloudFormation sees the
14
+ * template, as the file publishing Role the assets manifest names in each
15
+ * destination's `assumeRoleArn`. CloudFormation then processes the template
16
+ * as the execution Role. A deployment that names an assets caller is what
17
+ * tells the two apart here, and one that names none publishes as whoever
18
+ * deploys.
15
19
  */
16
- readonly caller?: SimAwsCaller | undefined;
20
+ readonly assetsCaller?: SimAwsCaller | undefined;
17
21
  }
18
22
  /**
19
23
  * Publishes a CDK cloud assembly's file assets into simulated S3.
@@ -25,7 +29,7 @@ interface SimCdkAssetsPublisherProperties {
25
29
  * Lambda function resolves its code through the ordinary sim S3 fetch with no
26
30
  * Lambda-specific asset handling.
27
31
  *
28
- * Publishing is byte movement only; nothing here runs asset code. Functions
32
+ * Publishing is byte movement only. Nothing here runs asset code. Functions
29
33
  * sim Lambda cannot run, such as a CDK BucketDeployment provider written in
30
34
  * Python, are declined later by the Lambda resource creator on their Runtime.
31
35
  */
@@ -42,15 +46,16 @@ export declare class SimCdkAssetsPublisher {
42
46
  publish(): Promise<void>;
43
47
  private publishAsset;
44
48
  /**
45
- * Create the staging Bucket if this is the first asset published into it,
49
+ * Make the staging Bucket if this is the first asset published into it,
46
50
  * standing in for the CDK bootstrap stack that provisions it for real.
47
51
  *
48
- * Stacks deployed concurrently share one staging Bucket, and creating it is
49
- * not atomic: sim S3 reaches a sequencing point before registering the
50
- * Bucket, so both can pass the check above and the loser is refused. A
51
- * Bucket that exists by the time the failure lands is the outcome this
52
- * wanted, whoever created it. Anything else, such as the name already being
53
- * taken in another scope, is a real failure and is reported.
52
+ * The bootstrap stack runs long before the deployment, and neither the file
53
+ * publishing Role nor the execution Role creates this Bucket. It comes
54
+ * through `makeSimBucket` for that reason, which asks IAM nothing. A
55
+ * deployment scoped to the permissions its real execution Role holds would
56
+ * otherwise be refused `s3:CreateBucket` before creating a Resource.
57
+ *
58
+ * A name already taken in another scope is a real failure, and is reported.
54
59
  */
55
60
  private ensureBucket;
56
61
  private stagingS3;
@@ -1,7 +1,7 @@
1
1
  import { SimCfnPseudoParameters } from "../../parameters/pseudo/sim-cfn-pseudo-parameters.js";
2
2
  import { SimCdkAssetPublications, } from "./sim-cdk-asset-publications.js";
3
3
  import { readSimCdkAssetBytes } from "./sim-cdk-asset-bytes.js";
4
- import { simCfnResourceCallerOptions, } from "../../resource/caller/sim-cfn-resource-caller-options.js";
4
+ import { simCfnResourceCallerOptions } from "../../resource/caller/sim-cfn-resource-caller-options.js";
5
5
  /**
6
6
  * Publishes a CDK cloud assembly's file assets into simulated S3.
7
7
  *
@@ -12,7 +12,7 @@ import { simCfnResourceCallerOptions, } from "../../resource/caller/sim-cfn-reso
12
12
  * Lambda function resolves its code through the ordinary sim S3 fetch with no
13
13
  * Lambda-specific asset handling.
14
14
  *
15
- * Publishing is byte movement only; nothing here runs asset code. Functions
15
+ * Publishing is byte movement only. Nothing here runs asset code. Functions
16
16
  * sim Lambda cannot run, such as a CDK BucketDeployment provider written in
17
17
  * Python, are declined later by the Lambda resource creator on their Runtime.
18
18
  */
@@ -42,7 +42,7 @@ export class SimCdkAssetsPublisher {
42
42
  }),
43
43
  }).resolve(cdkOutContext.assetsManifest);
44
44
  for (const publication of publications) {
45
- // oxlint-disable-next-line no-await-in-loop -- assets sharing a staging Bucket must not race to create it
45
+ // oxlint-disable-next-line no-await-in-loop -- published in manifest order, as cdk-assets publishes them
46
46
  await this.publishAsset(publication);
47
47
  }
48
48
  }
@@ -52,38 +52,30 @@ export class SimCdkAssetsPublisher {
52
52
  return;
53
53
  }
54
54
  const s3 = this.stagingS3();
55
- const options = simCfnResourceCallerOptions(this.properties.caller);
56
- await this.ensureBucket(s3, publication.bucketName, options);
55
+ this.ensureBucket(s3, publication.bucketName);
57
56
  await s3.putObject({
58
57
  input: {
59
58
  Bucket: publication.bucketName,
60
59
  Key: publication.objectKey,
61
60
  Body: assetBytes,
62
61
  },
63
- }, options);
62
+ }, simCfnResourceCallerOptions(this.properties.assetsCaller));
64
63
  }
65
64
  /**
66
- * Create the staging Bucket if this is the first asset published into it,
65
+ * Make the staging Bucket if this is the first asset published into it,
67
66
  * standing in for the CDK bootstrap stack that provisions it for real.
68
67
  *
69
- * Stacks deployed concurrently share one staging Bucket, and creating it is
70
- * not atomic: sim S3 reaches a sequencing point before registering the
71
- * Bucket, so both can pass the check above and the loser is refused. A
72
- * Bucket that exists by the time the failure lands is the outcome this
73
- * wanted, whoever created it. Anything else, such as the name already being
74
- * taken in another scope, is a real failure and is reported.
68
+ * The bootstrap stack runs long before the deployment, and neither the file
69
+ * publishing Role nor the execution Role creates this Bucket. It comes
70
+ * through `makeSimBucket` for that reason, which asks IAM nothing. A
71
+ * deployment scoped to the permissions its real execution Role holds would
72
+ * otherwise be refused `s3:CreateBucket` before creating a Resource.
73
+ *
74
+ * A name already taken in another scope is a real failure, and is reported.
75
75
  */
76
- async ensureBucket(s3, bucketName, options) {
77
- if (s3.getSimBucketByName(bucketName) !== undefined) {
78
- return;
79
- }
80
- try {
81
- await s3.createBucket({ input: { Bucket: bucketName } }, options);
82
- }
83
- catch (error) {
84
- if (s3.getSimBucketByName(bucketName) === undefined) {
85
- throw error;
86
- }
76
+ ensureBucket(s3, bucketName) {
77
+ if (s3.getSimBucketByName(bucketName) === undefined) {
78
+ s3.makeSimBucket(bucketName);
87
79
  }
88
80
  }
89
81
  stagingS3() {
@@ -1,6 +1,7 @@
1
1
  import { AsyncMappedFactory } from "@kensio/part-factory";
2
2
  import { TemporaryDirectory } from "../../../util/filesystem/temporary-directory.js";
3
3
  import type { SimCfnTemplateValueRecord } from "../template/value/sim-cfn-template-value.js";
4
+ import type { SimCdkAssetsManifest } from "./sim-cdk-out-context.js";
4
5
  /**
5
6
  * One synthesized Stack in a cloud assembly a test writes.
6
7
  */
@@ -25,9 +26,20 @@ export interface SimCdkCloudAssemblyStackInput {
25
26
  readonly resources?: SimCfnTemplateValueRecord | undefined;
26
27
  /** The template's Outputs, which a Stack sharing a value needs. */
27
28
  readonly outputs?: SimCfnTemplateValueRecord | undefined;
29
+ /**
30
+ * The assets manifest `cdk synth` writes beside the template, for a Stack
31
+ * that stages file assets. A Stack given none has no manifest file at all,
32
+ * which is what a Stack with nothing to stage synthesizes.
33
+ */
34
+ readonly assets?: SimCdkAssetsManifest | undefined;
28
35
  }
29
36
  export interface SimCdkCloudAssemblyInput {
30
37
  readonly stacks: readonly SimCdkCloudAssemblyStackInput[];
38
+ /**
39
+ * Staged asset files to write into the assembly, keyed by the path an assets
40
+ * manifest source names.
41
+ */
42
+ readonly assetFiles?: Record<string, string> | undefined;
31
43
  }
32
44
  /** The Bucket a Stack in a written assembly creates, unless it is given others. */
33
45
  export declare function assemblyStackBucketName(artifactId: string): string;
@@ -23,13 +23,19 @@ export function assemblyStackBucketName(artifactId) {
23
23
  export const simCdkCloudAssemblyFactory = new AsyncMappedFactory(() => ({ stacks: [{ artifactId: "SiteStack", regionName: "eu-west-2" }] }), async (input) => {
24
24
  const directory = new TemporaryDirectory();
25
25
  await directory.writeFile(["cdk.out", "manifest.json"], jsonStringify(assemblyManifest(input.stacks)));
26
- await Promise.all(input.stacks.map(async (stack) => directory.writeFile(["cdk.out", templateFileName(stack)],
27
- // JSON.stringify drops an undefined value, so a Stack given no
28
- // Outputs writes a template with none rather than a null one.
29
- jsonStringify({
30
- Resources: stackResources(stack),
31
- Outputs: stack.outputs,
32
- }))));
26
+ await Promise.all([
27
+ ...input.stacks.map(async (stack) => directory.writeFile(["cdk.out", templateFileName(stack)],
28
+ // JSON.stringify drops an undefined value, so a Stack given no
29
+ // Outputs writes a template with none rather than a null one.
30
+ jsonStringify({
31
+ Resources: stackResources(stack),
32
+ Outputs: stack.outputs,
33
+ }))),
34
+ ...input.stacks
35
+ .filter((stack) => stack.assets !== undefined)
36
+ .map(async (stack) => directory.writeFile(["cdk.out", assetsFileName(stack)], jsonStringify(stack.assets))),
37
+ ...Object.entries(input.assetFiles ?? {}).map(async ([assetPath, content]) => directory.writeFile(["cdk.out", assetPath], content)),
38
+ ]);
33
39
  return directory;
34
40
  });
35
41
  function assemblyManifest(stacks) {
@@ -56,6 +62,9 @@ function assemblyManifest(stacks) {
56
62
  function templateFileName(stack) {
57
63
  return `${stack.artifactId}.template.json`;
58
64
  }
65
+ function assetsFileName(stack) {
66
+ return `${stack.artifactId}.assets.json`;
67
+ }
59
68
  function stackResources(stack) {
60
69
  return (stack.resources ?? {
61
70
  [`${stack.artifactId}Bucket`]: {
@@ -18,6 +18,8 @@ interface CreateStackCommandHandlerProperties {
18
18
  readonly bindings?: readonly SimCfnBinding[] | undefined;
19
19
  /** The principal the Stack's Resources are created as. */
20
20
  readonly caller?: SimAwsCaller | undefined;
21
+ /** The principal the Stack's CDK file assets are published as. */
22
+ readonly assetsCaller?: SimAwsCaller | undefined;
21
23
  /** The order Resources with no dependency between them are started in. */
22
24
  readonly resourceOrder?: SimCfnResourceOrder | undefined;
23
25
  readonly exports?: SimCfnExports | undefined;
@@ -35,6 +37,7 @@ export declare class CreateStackCommandHandler implements CommandHandler<SimCrea
35
37
  private readonly cdkOutContext;
36
38
  private readonly bindings;
37
39
  private readonly caller;
40
+ private readonly assetsCaller;
38
41
  private readonly resourceOrder;
39
42
  private readonly exports;
40
43
  constructor(properties: CreateStackCommandHandlerProperties);
@@ -16,10 +16,11 @@ export class CreateStackCommandHandler {
16
16
  cdkOutContext;
17
17
  bindings;
18
18
  caller;
19
+ assetsCaller;
19
20
  resourceOrder;
20
21
  exports;
21
22
  constructor(properties) {
22
- const { simAws, accountRegionScope, stacks, background, cdkOutContext, bindings, caller, resourceOrder, exports, } = properties;
23
+ const { simAws, accountRegionScope, stacks, background, cdkOutContext, bindings, caller, assetsCaller, resourceOrder, exports, } = properties;
23
24
  this.simAws = simAws;
24
25
  this.accountRegionScope = accountRegionScope;
25
26
  this.stacks = stacks;
@@ -27,6 +28,7 @@ export class CreateStackCommandHandler {
27
28
  this.cdkOutContext = cdkOutContext;
28
29
  this.bindings = bindings;
29
30
  this.caller = caller;
31
+ this.assetsCaller = assetsCaller;
30
32
  this.resourceOrder = resourceOrder;
31
33
  this.exports = exports;
32
34
  }
@@ -65,6 +67,7 @@ export class CreateStackCommandHandler {
65
67
  cdkOutContext: this.cdkOutContext,
66
68
  bindings: this.bindings,
67
69
  caller: this.caller,
70
+ assetsCaller: this.assetsCaller,
68
71
  resourceOrder: this.resourceOrder,
69
72
  exports: this.exports,
70
73
  });
@@ -22,6 +22,11 @@ interface UpdateStackCommandHandlerProperties {
22
22
  * one. Left out, the Stack goes on running as whoever deployed it.
23
23
  */
24
24
  readonly caller?: SimAwsCaller | undefined;
25
+ /**
26
+ * The principal to publish the re-synthesized CDK file assets as, for an
27
+ * update that names one. Left out, they are published as `caller`.
28
+ */
29
+ readonly assetsCaller?: SimAwsCaller | undefined;
25
30
  /** The export names published in this Account and Region. */
26
31
  readonly exports?: SimCfnExports | undefined;
27
32
  }
@@ -37,6 +42,7 @@ export declare class UpdateStackCommandHandler implements CommandHandler<SimUpda
37
42
  private readonly background;
38
43
  private readonly cdkOutContext;
39
44
  private readonly caller;
45
+ private readonly assetsCaller;
40
46
  private readonly exports;
41
47
  constructor(properties: UpdateStackCommandHandlerProperties);
42
48
  /**
@@ -14,6 +14,7 @@ export class UpdateStackCommandHandler {
14
14
  background;
15
15
  cdkOutContext;
16
16
  caller;
17
+ assetsCaller;
17
18
  exports;
18
19
  constructor(properties) {
19
20
  this.simAws = properties.simAws;
@@ -22,6 +23,7 @@ export class UpdateStackCommandHandler {
22
23
  this.background = properties.background;
23
24
  this.cdkOutContext = properties.cdkOutContext;
24
25
  this.caller = properties.caller;
26
+ this.assetsCaller = properties.assetsCaller;
25
27
  this.exports = properties.exports;
26
28
  }
27
29
  /**
@@ -51,7 +53,11 @@ export class UpdateStackCommandHandler {
51
53
  stack,
52
54
  input: command.input,
53
55
  exports: this.exports,
54
- }), { cdkOutContext: this.cdkOutContext, caller: this.caller });
56
+ }), {
57
+ cdkOutContext: this.cdkOutContext,
58
+ caller: this.caller,
59
+ assetsCaller: this.assetsCaller,
60
+ });
55
61
  return {
56
62
  StackId: stack.stackId,
57
63
  $metadata: {},
@@ -27,7 +27,7 @@ export class SimCfnCdkOutDeployer {
27
27
  }
28
28
  async deployStack(stack, plan, deployed) {
29
29
  const { simAws, accountRegionScope } = this.scope;
30
- const { transform, caller, resourceOrder, ...options } = cdkOutOptionsFor(stack, plan.stackOptions);
30
+ const { transform, caller, assetsCaller, resourceOrder, ...options } = cdkOutOptionsFor(stack, plan.stackOptions);
31
31
  return await simAws
32
32
  .accountRegionScope(accountRegionScope.accountId, stack.regionName)
33
33
  .cloudFormation()
@@ -36,6 +36,7 @@ export class SimCfnCdkOutDeployer {
36
36
  stackName: stack.stackName,
37
37
  ...options,
38
38
  caller: caller ?? plan.caller,
39
+ assetsCaller: assetsCaller ?? plan.assetsCaller,
39
40
  resourceOrder: resourceOrder ?? plan.resourceOrder,
40
41
  transform: cdkOutBoundTransform(transform, deployed),
41
42
  });
@@ -0,0 +1,10 @@
1
+ import type { SimCdkAssemblyStack } from "../cdk/sim-cdk-assembly-manifest.js";
2
+ import type { SimCfnCdkOutStackOptionsByName } from "./sim-cfn-cdk-out-stack-options.js";
3
+ /**
4
+ * Refuse options keyed against a Stack that is not being deployed.
5
+ *
6
+ * A renamed Stack would otherwise take its bindings, its caller and its
7
+ * transform with it without saying anything, and the deployment that lost them
8
+ * looks like one that never had them.
9
+ */
10
+ export declare function assertCdkOutOptionsAreDeployed(optionsByName: SimCfnCdkOutStackOptionsByName | undefined, deploying: readonly SimCdkAssemblyStack[]): void;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Refuse options keyed against a Stack that is not being deployed.
3
+ *
4
+ * A renamed Stack would otherwise take its bindings, its caller and its
5
+ * transform with it without saying anything, and the deployment that lost them
6
+ * looks like one that never had them.
7
+ */
8
+ export function assertCdkOutOptionsAreDeployed(optionsByName, deploying) {
9
+ const names = new Set(deploying.flatMap((stack) => [stack.stackName, stack.artifactId]));
10
+ const unmatched = Object.keys(optionsByName ?? {}).filter((name) => !names.has(name));
11
+ if (unmatched.length > 0) {
12
+ throw new Error(`Options were given for ${unmatched.join(", ")}, which no Stack being deployed is named. The Stacks being deployed are ${deploying.map((stack) => stack.stackName).join(", ")}.`);
13
+ }
14
+ }
@@ -1,7 +1,7 @@
1
1
  import type { AwsRegionName } from "../../aws/sim-aws-region.js";
2
2
  import type { SimAwsCaller } from "../../aws/caller/sim-aws-caller.js";
3
3
  import { type SimCdkAssemblyStack } from "../cdk/sim-cdk-assembly-manifest.js";
4
- import { type SimCfnCdkOutStackOptionsByName } from "./sim-cfn-cdk-out-stack-options.js";
4
+ import type { SimCfnCdkOutStackOptionsByName } from "./sim-cfn-cdk-out-stack-options.js";
5
5
  import type { SimCfnResourceOrder } from "../stack/deploy/sim-cfn-resource-order.js";
6
6
  export interface SimCloudFormationDeployCdkOutProperties {
7
7
  /** The `cdk.out` directory to deploy, holding a `manifest.json`. */
@@ -23,6 +23,15 @@ export interface SimCloudFormationDeployCdkOutProperties {
23
23
  * in `stackOptions` overrides for its own Stack.
24
24
  */
25
25
  readonly caller?: SimAwsCaller | undefined;
26
+ /**
27
+ * The principal every Stack in the assembly publishes its CDK file assets
28
+ * as, which one named in `stackOptions` overrides for its own Stack.
29
+ *
30
+ * A real `cdk deploy` publishes assets as the file publishing Role and
31
+ * processes each template as the execution Role. Left out, an assembly
32
+ * publishes its assets as `caller`.
33
+ */
34
+ readonly assetsCaller?: SimAwsCaller | undefined;
26
35
  /**
27
36
  * The order every Stack in the assembly creates Resources with no dependency
28
37
  * between them in, which one named in `stackOptions` overrides for its own
@@ -43,6 +52,7 @@ export interface SimCfnCdkOutPlannedStack extends SimCdkAssemblyStack {
43
52
  export interface SimCfnCdkOutPlan {
44
53
  readonly stacks: readonly SimCfnCdkOutPlannedStack[];
45
54
  readonly caller?: SimAwsCaller | undefined;
55
+ readonly assetsCaller?: SimAwsCaller | undefined;
46
56
  readonly resourceOrder?: SimCfnResourceOrder | undefined;
47
57
  readonly stackOptions?: SimCfnCdkOutStackOptionsByName | undefined;
48
58
  }
@@ -2,7 +2,7 @@ import path from "node:path";
2
2
  import { loadCdkAssemblyStacks, } from "../cdk/sim-cdk-assembly-manifest.js";
3
3
  import { orderCdkAssemblyStacks } from "../cdk/sim-cdk-assembly-order.js";
4
4
  import { selectCdkAssemblyStacks } from "../cdk/sim-cdk-assembly-selection.js";
5
- import { assertCdkOutOptionsAreDeployed, } from "./sim-cfn-cdk-out-stack-options.js";
5
+ import { assertCdkOutOptionsAreDeployed } from "./sim-cfn-cdk-out-options-deployed.js";
6
6
  /**
7
7
  * Work out which Stacks to deploy, in what order, and into which region.
8
8
  *
@@ -10,7 +10,7 @@ import { assertCdkOutOptionsAreDeployed, } from "./sim-cfn-cdk-out-stack-options
10
10
  * an assembly that cannot be deployed says so before any Stack is created.
11
11
  */
12
12
  export async function planCdkOutDeployment(properties) {
13
- const { directoryPath, stackNames, caller, resourceOrder, stackOptions } = cdkOutDeployment(properties.request);
13
+ const { directoryPath, stackNames, caller, assetsCaller, resourceOrder, stackOptions, } = cdkOutDeployment(properties.request);
14
14
  const selected = selectCdkAssemblyStacks({
15
15
  stacks: await loadCdkAssemblyStacks(directoryPath),
16
16
  stackNames,
@@ -23,6 +23,7 @@ export async function planCdkOutDeployment(properties) {
23
23
  regionName: stack.regionName ?? properties.defaultRegionName,
24
24
  })),
25
25
  caller,
26
+ assetsCaller,
26
27
  resourceOrder,
27
28
  stackOptions,
28
29
  };
@@ -34,6 +34,11 @@ export interface SimCfnCdkOutStackOptions {
34
34
  * application Stack deployed by another is what this is for.
35
35
  */
36
36
  readonly caller?: SimAwsCaller | undefined;
37
+ /**
38
+ * The principal this Stack publishes its CDK file assets as, where the
39
+ * assembly's own assets caller is not the right one for it.
40
+ */
41
+ readonly assetsCaller?: SimAwsCaller | undefined;
37
42
  /**
38
43
  * The order this Stack creates Resources with no dependency between them in,
39
44
  * where the assembly's own order is not the right one for it.
@@ -53,11 +58,3 @@ export declare function cdkOutOptionsFor(stack: SimCdkAssemblyStack, optionsByNa
53
58
  * keeps the deployment it was handed while the call goes on deploying.
54
59
  */
55
60
  export declare function cdkOutBoundTransform(transform: SimCfnCdkOutTemplateTransform | undefined, deployed: ReadonlyMap<string, SimCfnDeployedStack>): SimCfnTemplateFileTransform | undefined;
56
- /**
57
- * Refuse options keyed against a Stack that is not being deployed.
58
- *
59
- * A renamed Stack would otherwise take its bindings, its caller and its
60
- * transform with it without saying anything, and the deployment that lost them
61
- * looks like one that never had them.
62
- */
63
- export declare function assertCdkOutOptionsAreDeployed(optionsByName: SimCfnCdkOutStackOptionsByName | undefined, deploying: readonly SimCdkAssemblyStack[]): void;
@@ -17,17 +17,3 @@ export function cdkOutBoundTransform(transform, deployed) {
17
17
  const deployedSoFar = new Map(deployed);
18
18
  return (template) => transform(template, deployedSoFar);
19
19
  }
20
- /**
21
- * Refuse options keyed against a Stack that is not being deployed.
22
- *
23
- * A renamed Stack would otherwise take its bindings, its caller and its
24
- * transform with it without saying anything, and the deployment that lost them
25
- * looks like one that never had them.
26
- */
27
- export function assertCdkOutOptionsAreDeployed(optionsByName, deploying) {
28
- const names = new Set(deploying.flatMap((stack) => [stack.stackName, stack.artifactId]));
29
- const unmatched = Object.keys(optionsByName ?? {}).filter((name) => !names.has(name));
30
- if (unmatched.length > 0) {
31
- throw new Error(`Options were given for ${unmatched.join(", ")}, which no Stack being deployed is named. The Stacks being deployed are ${deploying.map((stack) => stack.stackName).join(", ")}.`);
32
- }
33
- }
@@ -24,6 +24,15 @@ export interface SimCloudFormationCreateStackProperties {
24
24
  * policy denying an Account's root principal then denies.
25
25
  */
26
26
  readonly caller?: SimAwsCaller | undefined;
27
+ /**
28
+ * The principal the CDK file assets staged beside the template are published
29
+ * as.
30
+ *
31
+ * A real `cdk deploy` publishes them as the file publishing Role and only
32
+ * then processes the template as the execution Role, which is why the two
33
+ * are named apart. Left out, assets are published as `caller`.
34
+ */
35
+ readonly assetsCaller?: SimAwsCaller | undefined;
27
36
  /**
28
37
  * The order Resources with no dependency between them are created in.
29
38
  *
@@ -59,6 +59,7 @@ export class SimCloudFormationTemplateDeployer {
59
59
  parameters: properties.parameters,
60
60
  bindings: properties.bindings,
61
61
  caller: properties.caller,
62
+ assetsCaller: properties.assetsCaller,
62
63
  resourceOrder: properties.resourceOrder,
63
64
  cdkOutContext: await cdkOutContextForTemplatePath(properties.templatePath),
64
65
  });
@@ -118,6 +119,7 @@ export class SimCloudFormationTemplateDeployer {
118
119
  cdkOutContext: deployment.cdkOutContext,
119
120
  bindings: deployment.bindings,
120
121
  caller: deployment.caller,
122
+ assetsCaller: deployment.assetsCaller,
121
123
  resourceOrder: deployment.resourceOrder,
122
124
  exports: this.exports,
123
125
  });
@@ -20,6 +20,15 @@ export interface SimCloudFormationDeployTemplateFileProperties {
20
20
  * policy denying an Account's root principal then denies.
21
21
  */
22
22
  readonly caller?: SimAwsCaller | undefined;
23
+ /**
24
+ * The principal the CDK file assets staged beside the template are published
25
+ * as.
26
+ *
27
+ * A real `cdk deploy` publishes them as the file publishing Role and only
28
+ * then processes the template as the execution Role, which is why the two
29
+ * are named apart. Left out, assets are published as `caller`.
30
+ */
31
+ readonly assetsCaller?: SimAwsCaller | undefined;
23
32
  /**
24
33
  * The order Resources with no dependency between them are created in.
25
34
  *
@@ -64,6 +73,7 @@ export interface SimCfnLoadedTemplateFile {
64
73
  readonly parameters?: Record<string, string> | undefined;
65
74
  readonly bindings?: readonly SimCfnBinding[] | undefined;
66
75
  readonly caller?: SimAwsCaller | undefined;
76
+ readonly assetsCaller?: SimAwsCaller | undefined;
67
77
  readonly resourceOrder?: SimCfnResourceOrder | undefined;
68
78
  readonly cdkOutContext?: SimCdkOutContext | undefined;
69
79
  }
@@ -16,7 +16,7 @@ export class SimCfnTemplateFileLoader {
16
16
  */
17
17
  async load(properties) {
18
18
  const deployment = simCfnTemplateFileDeployment(properties);
19
- const { templatePath, parameters, bindings, caller, resourceOrder } = deployment;
19
+ const { templatePath, parameters, bindings, caller, assetsCaller, resourceOrder, } = deployment;
20
20
  const stackName = deployment.stackName ?? stackNameFromTemplatePath(templatePath);
21
21
  // The template is named to a `yulin watch` supervisor, so re-synthing a
22
22
  // stack restarts the process that deployed it. Nothing happens outside
@@ -32,6 +32,7 @@ export class SimCfnTemplateFileLoader {
32
32
  parameters,
33
33
  bindings,
34
34
  caller,
35
+ assetsCaller,
35
36
  resourceOrder,
36
37
  cdkOutContext,
37
38
  };
@@ -43,6 +43,7 @@ export class SimCfnTemplateFileUpdater {
43
43
  background: this.background,
44
44
  cdkOutContext: loaded.cdkOutContext,
45
45
  caller: loaded.caller,
46
+ assetsCaller: loaded.assetsCaller,
46
47
  });
47
48
  await handler.handle({
48
49
  input: {
@@ -0,0 +1,47 @@
1
+ import type { SimAwsCaller } from "../../aws/caller/sim-aws-caller.js";
2
+ import type { SimCdkOutContext } from "../cdk/sim-cdk-out-context.js";
3
+ /**
4
+ * What an update can replace on a Stack's deploy context. Anything it leaves
5
+ * out, the Stack goes on using.
6
+ */
7
+ export interface SimCfnStackUpdateContext {
8
+ readonly cdkOutContext?: SimCdkOutContext | undefined;
9
+ readonly caller?: SimAwsCaller | undefined;
10
+ readonly assetsCaller?: SimAwsCaller | undefined;
11
+ }
12
+ /**
13
+ * What a Stack is deploying from, and who it is deploying as.
14
+ *
15
+ * These three travel together and are the only things an update replaces, so
16
+ * they are held apart from the rest of a Stack's Resource operations. An
17
+ * operation reads them here whenever it runs, rather than closing over the
18
+ * values the deployment started with.
19
+ */
20
+ export declare class SimCfnStackDeployContext {
21
+ /** The CDK cloud assembly the Stack's template was synthesized into. */
22
+ cdkOutContext: SimCdkOutContext | undefined;
23
+ /** The principal the Stack's Resource work is authorized as. */
24
+ caller: SimAwsCaller | undefined;
25
+ /** The principal the Stack's CDK file assets are published as. */
26
+ assetsCaller: SimAwsCaller | undefined;
27
+ constructor(properties: SimCfnStackUpdateContext);
28
+ /**
29
+ * Take on what an update brings with it, for this and every later operation.
30
+ *
31
+ * A synthesis writes the template and the assets manifest beside it together,
32
+ * so a Stack updated from a re-synthesized template needs the manifest that
33
+ * came with it. Keeping the one the Stack was deployed from would look up the
34
+ * asset a replaced Resource names in a manifest written before it existed.
35
+ *
36
+ * An update naming a caller runs as that one, and the Stack is torn down as
37
+ * it afterwards. An update that names none keeps the deployment's caller, so
38
+ * a Stack updated from a plain template path goes on running as whoever
39
+ * deployed it.
40
+ */
41
+ useUpdate(properties: SimCfnStackUpdateContext): void;
42
+ /**
43
+ * The principal file assets are published as, which is the deployment's own
44
+ * caller unless the deployment named one for publishing.
45
+ */
46
+ publishingCaller(): SimAwsCaller | undefined;
47
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * What a Stack is deploying from, and who it is deploying as.
3
+ *
4
+ * These three travel together and are the only things an update replaces, so
5
+ * they are held apart from the rest of a Stack's Resource operations. An
6
+ * operation reads them here whenever it runs, rather than closing over the
7
+ * values the deployment started with.
8
+ */
9
+ export class SimCfnStackDeployContext {
10
+ /** The CDK cloud assembly the Stack's template was synthesized into. */
11
+ cdkOutContext;
12
+ /** The principal the Stack's Resource work is authorized as. */
13
+ caller;
14
+ /** The principal the Stack's CDK file assets are published as. */
15
+ assetsCaller;
16
+ constructor(properties) {
17
+ this.cdkOutContext = properties.cdkOutContext;
18
+ this.caller = properties.caller;
19
+ this.assetsCaller = properties.assetsCaller;
20
+ }
21
+ /**
22
+ * Take on what an update brings with it, for this and every later operation.
23
+ *
24
+ * A synthesis writes the template and the assets manifest beside it together,
25
+ * so a Stack updated from a re-synthesized template needs the manifest that
26
+ * came with it. Keeping the one the Stack was deployed from would look up the
27
+ * asset a replaced Resource names in a manifest written before it existed.
28
+ *
29
+ * An update naming a caller runs as that one, and the Stack is torn down as
30
+ * it afterwards. An update that names none keeps the deployment's caller, so
31
+ * a Stack updated from a plain template path goes on running as whoever
32
+ * deployed it.
33
+ */
34
+ useUpdate(properties) {
35
+ this.cdkOutContext = properties.cdkOutContext ?? this.cdkOutContext;
36
+ this.caller = properties.caller ?? this.caller;
37
+ this.assetsCaller = properties.assetsCaller ?? this.assetsCaller;
38
+ }
39
+ /**
40
+ * The principal file assets are published as, which is the deployment's own
41
+ * caller unless the deployment named one for publishing.
42
+ */
43
+ publishingCaller() {
44
+ return this.assetsCaller ?? this.caller;
45
+ }
46
+ }
@@ -4,6 +4,7 @@ import type { SimAwsCaller } from "../../aws/caller/sim-aws-caller.js";
4
4
  import type { SimAwsAccountRegionScope } from "../../aws/sim-aws-account-region-scope.js";
5
5
  import type { SimCfnBinding } from "../bind/sim-cfn-binding.js";
6
6
  import type { SimCdkOutContext } from "../cdk/sim-cdk-out-context.js";
7
+ import { type SimCfnStackUpdateContext } from "./sim-cfn-stack-deploy-context.js";
7
8
  import type { SimCfnResource } from "../resource/sim-cfn-resource.js";
8
9
  import { SimCfnResourceRetention } from "../resource/delete/sim-cfn-resource-retention.js";
9
10
  import type { SimCfnTemplate } from "../template/sim-cfn-template.js";
@@ -28,6 +29,11 @@ interface SimCfnStackResourceOperationsProperties {
28
29
  * caller leaves each service to decide the request as the Account root.
29
30
  */
30
31
  readonly caller?: SimAwsCaller | undefined;
32
+ /**
33
+ * The principal the Stack's CDK file assets are published as, where that is
34
+ * somebody other than the caller its Resources are created as.
35
+ */
36
+ readonly assetsCaller?: SimAwsCaller | undefined;
31
37
  /**
32
38
  * The order Resources with no dependency between them are started in.
33
39
  */
@@ -52,8 +58,7 @@ export declare class SimCfnStackResourceOperations {
52
58
  private readonly stackName;
53
59
  private readonly bindings;
54
60
  private readonly resourceOrder;
55
- private cdkOutContext;
56
- private caller;
61
+ private readonly context;
57
62
  /**
58
63
  * Resources an update kept in simulated AWS.
59
64
  *
@@ -64,23 +69,8 @@ export declare class SimCfnStackResourceOperations {
64
69
  */
65
70
  private readonly retainedByUpdates;
66
71
  constructor(properties: SimCfnStackResourceOperationsProperties);
67
- /**
68
- * Take on what an update brings with it, for this and every later operation.
69
- *
70
- * A synthesis writes the template and the assets manifest beside it together,
71
- * so a Stack updated from a re-synthesized template needs the manifest that
72
- * came with it. Keeping the one the Stack was deployed from would look up the
73
- * asset a replaced Resource names in a manifest written before it existed.
74
- *
75
- * An update naming a caller runs as that one, and the Stack is torn down as
76
- * it afterwards. An update that names none keeps the deployment's caller, so
77
- * a Stack updated from a plain template path goes on running as whoever
78
- * deployed it.
79
- */
80
- useUpdate(properties: {
81
- readonly cdkOutContext?: SimCdkOutContext | undefined;
82
- readonly caller?: SimAwsCaller | undefined;
83
- }): void;
72
+ /** Take on what an update brings with it, for every later operation. */
73
+ useUpdate(properties: SimCfnStackUpdateContext): void;
84
74
  /**
85
75
  * Create every Resource in the given Stack Resource map.
86
76
  */
@@ -1,3 +1,4 @@
1
+ import { SimCfnStackDeployContext, } from "./sim-cfn-stack-deploy-context.js";
1
2
  import { SimCdkAssetsPublisher } from "../cdk/assets/sim-cdk-assets-publisher.js";
2
3
  import { SimCfnResourceRetention } from "../resource/delete/sim-cfn-resource-retention.js";
3
4
  import { simCfnStackRetainedLogicalIds } from "./teardown/sim-cfn-stack-retained-logical-ids.js";
@@ -24,8 +25,7 @@ export class SimCfnStackResourceOperations {
24
25
  stackName;
25
26
  bindings;
26
27
  resourceOrder;
27
- cdkOutContext;
28
- caller;
28
+ context;
29
29
  /**
30
30
  * Resources an update kept in simulated AWS.
31
31
  *
@@ -36,31 +36,17 @@ export class SimCfnStackResourceOperations {
36
36
  */
37
37
  retainedByUpdates = [];
38
38
  constructor(properties) {
39
- const { simAws, accountRegionScope, stackName, cdkOutContext, bindings, caller, resourceOrder, } = properties;
39
+ const { simAws, accountRegionScope, stackName, bindings, resourceOrder } = properties;
40
40
  this.simAws = simAws;
41
41
  this.accountRegionScope = accountRegionScope;
42
42
  this.stackName = stackName;
43
- this.cdkOutContext = cdkOutContext;
44
43
  this.bindings = bindings;
45
- this.caller = caller;
46
44
  this.resourceOrder = resourceOrder;
45
+ this.context = new SimCfnStackDeployContext(properties);
47
46
  }
48
- /**
49
- * Take on what an update brings with it, for this and every later operation.
50
- *
51
- * A synthesis writes the template and the assets manifest beside it together,
52
- * so a Stack updated from a re-synthesized template needs the manifest that
53
- * came with it. Keeping the one the Stack was deployed from would look up the
54
- * asset a replaced Resource names in a manifest written before it existed.
55
- *
56
- * An update naming a caller runs as that one, and the Stack is torn down as
57
- * it afterwards. An update that names none keeps the deployment's caller, so
58
- * a Stack updated from a plain template path goes on running as whoever
59
- * deployed it.
60
- */
47
+ /** Take on what an update brings with it, for every later operation. */
61
48
  useUpdate(properties) {
62
- this.cdkOutContext = properties.cdkOutContext ?? this.cdkOutContext;
63
- this.caller = properties.caller ?? this.caller;
49
+ this.context.useUpdate(properties);
64
50
  }
65
51
  /**
66
52
  * Create every Resource in the given Stack Resource map.
@@ -120,7 +106,7 @@ export class SimCfnStackResourceOperations {
120
106
  simAws: this.simAws,
121
107
  currentResources,
122
108
  updatedResources,
123
- caller: this.caller,
109
+ caller: this.context.caller,
124
110
  });
125
111
  }));
126
112
  }
@@ -173,8 +159,8 @@ export class SimCfnStackResourceOperations {
173
159
  simAws: this.simAws,
174
160
  accountRegionScope: this.accountRegionScope,
175
161
  stackName: this.stackName,
176
- cdkOutContext: this.cdkOutContext,
177
- caller: this.caller,
162
+ cdkOutContext: this.context.cdkOutContext,
163
+ assetsCaller: this.context.publishingCaller(),
178
164
  }).publish();
179
165
  }
180
166
  creator(resources) {
@@ -182,9 +168,9 @@ export class SimCfnStackResourceOperations {
182
168
  simAws: this.simAws,
183
169
  resources,
184
170
  stackName: this.stackName,
185
- cdkOutContext: this.cdkOutContext,
171
+ cdkOutContext: this.context.cdkOutContext,
186
172
  bindings: this.bindings,
187
- caller: this.caller,
173
+ caller: this.context.caller,
188
174
  resourceOrder: this.resourceOrder,
189
175
  });
190
176
  }
@@ -193,7 +179,7 @@ export class SimCfnStackResourceOperations {
193
179
  simAws: this.simAws,
194
180
  resources,
195
181
  stackName: this.stackName,
196
- caller: this.caller,
182
+ caller: this.context.caller,
197
183
  retention,
198
184
  });
199
185
  }
@@ -38,7 +38,7 @@ export class SimCfnStack {
38
38
  stackOutputs;
39
39
  cfnTemplate;
40
40
  constructor(properties) {
41
- const { simAws, accountRegionScope, background, stackName, stackId, template, cdkOutContext, bindings, caller, resourceOrder, exports, } = properties;
41
+ const { simAws, accountRegionScope, background, stackName, stackId, template, cdkOutContext, bindings, caller, assetsCaller, resourceOrder, exports, } = properties;
42
42
  this.background = background;
43
43
  this.stackName = stackName;
44
44
  this.stackId = stackId;
@@ -61,6 +61,7 @@ export class SimCfnStack {
61
61
  cdkOutContext,
62
62
  bindings,
63
63
  caller,
64
+ assetsCaller,
64
65
  resourceOrder,
65
66
  });
66
67
  this.lifecycle = new SimCfnStackDeploymentLifecycle({
@@ -24,6 +24,11 @@ export interface SimCfnStackUpdateProperties {
24
24
  * The Stack goes on using it, as it does the cloud assembly.
25
25
  */
26
26
  readonly caller?: SimAwsCaller | undefined;
27
+ /**
28
+ * The principal to publish the re-synthesized file assets as, for an update
29
+ * that names one. The Stack goes on using it too.
30
+ */
31
+ readonly assetsCaller?: SimAwsCaller | undefined;
27
32
  }
28
33
  export interface SimCloudFormationStackProperties {
29
34
  readonly simAws: SimAws;
@@ -44,6 +49,15 @@ export interface SimCloudFormationStackProperties {
44
49
  * simulation.
45
50
  */
46
51
  readonly caller?: SimAwsCaller | undefined;
52
+ /**
53
+ * The principal the CDK file assets staged beside the template are published
54
+ * as.
55
+ *
56
+ * A real `cdk deploy` publishes them as the file publishing Role and only
57
+ * then processes the template as the execution Role, which is why the two
58
+ * are named apart. Left out, assets are published as `caller`.
59
+ */
60
+ readonly assetsCaller?: SimAwsCaller | undefined;
47
61
  /**
48
62
  * The order this Stack starts Resources with no dependency between them in.
49
63
  * The template's own order unless the deployment asked for another one.
@@ -0,0 +1,31 @@
1
+ import type { BackgroundScheduler } from "../../../util/background/background.js";
2
+ import type { SimAwsAccountRegionScope } from "../../aws/sim-aws-account-region-scope.js";
3
+ import { SimS3Bucket, type SimS3BucketName } from "./sim-s3-bucket.js";
4
+ import type { SimS3GlobalRegistry } from "../sim-s3-global-registry.js";
5
+ interface SimS3BucketMakerProperties {
6
+ readonly accountRegionScope: SimAwsAccountRegionScope;
7
+ readonly buckets: Map<SimS3BucketName, SimS3Bucket>;
8
+ readonly s3GlobalRegistry: SimS3GlobalRegistry;
9
+ readonly background: BackgroundScheduler;
10
+ }
11
+ /**
12
+ * Puts a Bucket into a simulated S3 scope, once something has decided it may
13
+ * be there.
14
+ *
15
+ * `CreateBucketCommand` decides that by authorizing the caller. A Bucket real
16
+ * AWS provisions outside the deployment being simulated, such as the CDK
17
+ * bootstrap staging Bucket, has no caller to authorize and comes through
18
+ * `SimS3.makeSimBucket` instead. Both arrive at the same Bucket registered in
19
+ * the same two places, because the name check and the registration are here
20
+ * rather than in the command handler.
21
+ */
22
+ export declare class SimS3BucketMaker {
23
+ private readonly properties;
24
+ constructor(properties: SimS3BucketMakerProperties);
25
+ /**
26
+ * Make a Bucket of the given name, reporting a name this scope cannot take
27
+ * the way `CreateBucketCommand` reports it.
28
+ */
29
+ make(bucketName: string): SimS3Bucket;
30
+ }
31
+ export {};
@@ -0,0 +1,41 @@
1
+ import { SimS3BucketNameAvailability } from "./name-availability/sim-s3-bucket-name-availability.js";
2
+ import { validateS3BucketName } from "./validate/validate-s3-bucket-name.js";
3
+ import { SimS3Bucket } from "./sim-s3-bucket.js";
4
+ /**
5
+ * Puts a Bucket into a simulated S3 scope, once something has decided it may
6
+ * be there.
7
+ *
8
+ * `CreateBucketCommand` decides that by authorizing the caller. A Bucket real
9
+ * AWS provisions outside the deployment being simulated, such as the CDK
10
+ * bootstrap staging Bucket, has no caller to authorize and comes through
11
+ * `SimS3.makeSimBucket` instead. Both arrive at the same Bucket registered in
12
+ * the same two places, because the name check and the registration are here
13
+ * rather than in the command handler.
14
+ */
15
+ export class SimS3BucketMaker {
16
+ properties;
17
+ constructor(properties) {
18
+ this.properties = properties;
19
+ }
20
+ /**
21
+ * Make a Bucket of the given name, reporting a name this scope cannot take
22
+ * the way `CreateBucketCommand` reports it.
23
+ */
24
+ make(bucketName) {
25
+ const { accountRegionScope, buckets, s3GlobalRegistry, background } = this.properties;
26
+ validateS3BucketName(bucketName);
27
+ new SimS3BucketNameAvailability({
28
+ accountRegionScope,
29
+ buckets,
30
+ s3GlobalRegistry,
31
+ }).ensureCanCreateBucketNamed(bucketName);
32
+ const bucket = new SimS3Bucket({
33
+ bucketName,
34
+ accountRegionScope,
35
+ clock: background,
36
+ });
37
+ buckets.set(bucketName, bucket);
38
+ s3GlobalRegistry.registerBucket(bucketName, accountRegionScope);
39
+ return bucket;
40
+ }
41
+ }
@@ -1,6 +1,6 @@
1
1
  import type { CommandHandler } from "../../../../command/command-handler.js";
2
2
  import type { SimCreateBucketCommand, SimCreateBucketCommandOutput } from "./create-bucket.command.js";
3
- import { SimS3Bucket } from "../../bucket/sim-s3-bucket.js";
3
+ import type { SimS3Bucket, SimS3BucketName } from "../../bucket/sim-s3-bucket.js";
4
4
  import type { SimS3GlobalRegistry } from "../../sim-s3-global-registry.js";
5
5
  import type { SimAwsAccountRegionScope } from "../../../aws/sim-aws-account-region-scope.js";
6
6
  import { type BackgroundScheduler } from "../../../../util/background/background.js";
@@ -8,7 +8,7 @@ import { type SimIamInterServiceAuthZ } from "../../../iam/authorize/sim-iam-int
8
8
  import type { SimS3RequestOptions } from "../sim-s3-request-options.js";
9
9
  interface CreateBucketCommandHandlerProperties {
10
10
  readonly accountRegionScope: SimAwsAccountRegionScope;
11
- readonly buckets: Map<string, SimS3Bucket>;
11
+ readonly buckets: Map<SimS3BucketName, SimS3Bucket>;
12
12
  readonly s3GlobalRegistry: SimS3GlobalRegistry;
13
13
  readonly iam?: SimIamInterServiceAuthZ;
14
14
  readonly background?: BackgroundScheduler;
@@ -1,7 +1,6 @@
1
- import { SimS3Bucket } from "../../bucket/sim-s3-bucket.js";
2
1
  import { assertDefined } from "../../../../util/type-guard/defined.js";
3
2
  import { BackgroundTasks, } from "../../../../util/background/background.js";
4
- import { SimS3BucketNameAvailability } from "../../bucket/name-availability/sim-s3-bucket-name-availability.js";
3
+ import { SimS3BucketMaker } from "../../bucket/sim-s3-bucket-maker.js";
5
4
  import { validateS3BucketName } from "../../bucket/validate/validate-s3-bucket-name.js";
6
5
  import { SimIamAllowAllAuth, } from "../../../iam/authorize/sim-iam-inter-service-auth-z.js";
7
6
  import { SimIamAccessDenied } from "../../../iam/error/sim-iam.error.js";
@@ -49,19 +48,12 @@ export class CreateBucketCommandHandler {
49
48
  resource,
50
49
  });
51
50
  }
52
- const simS3BucketNameAvailability = new SimS3BucketNameAvailability({
51
+ new SimS3BucketMaker({
53
52
  accountRegionScope: this.accountRegionScope,
54
53
  buckets: this.buckets,
55
54
  s3GlobalRegistry: this.s3GlobalRegistry,
56
- });
57
- simS3BucketNameAvailability.ensureCanCreateBucketNamed(bucketName);
58
- const bucket = new SimS3Bucket({
59
- bucketName,
60
- accountRegionScope: this.accountRegionScope,
61
- clock: this.background,
62
- });
63
- this.buckets.set(bucketName, bucket);
64
- this.s3GlobalRegistry.registerBucket(bucketName, this.accountRegionScope);
55
+ background: this.background,
56
+ }).make(bucketName);
65
57
  return {
66
58
  BucketArn: resource,
67
59
  Location: `/${bucketName}`,
@@ -63,6 +63,12 @@ export declare class SimS3Commands {
63
63
  readonly objectTagging: SimS3ObjectTaggingCommands;
64
64
  readonly multipartUploads: SimS3MultipartCommands;
65
65
  readonly objectNotifier: SimS3ObjectNotifier;
66
+ /**
67
+ * The scheduler and clock every command area here shares, which a
68
+ * simulator-only control on the service facade needs to hand a Bucket it
69
+ * makes outside the commands.
70
+ */
71
+ readonly background: BackgroundScheduler;
66
72
  constructor(properties: SimS3CommandsProperties);
67
73
  }
68
74
  export {};
@@ -35,8 +35,15 @@ export class SimS3Commands {
35
35
  objectTagging;
36
36
  multipartUploads;
37
37
  objectNotifier;
38
+ /**
39
+ * The scheduler and clock every command area here shares, which a
40
+ * simulator-only control on the service facade needs to hand a Bucket it
41
+ * makes outside the commands.
42
+ */
43
+ background;
38
44
  constructor(properties) {
39
45
  const { background = new BackgroundTasks(), notificationDestinations = new SimS3NoNotificationDestinations(), } = properties;
46
+ this.background = background;
40
47
  const iam = simIamInRegion(properties.iam, properties.accountRegionScope.regionName);
41
48
  this.objectNotifier = new SimS3ObjectNotifier({
42
49
  destinations: notificationDestinations,
@@ -18,9 +18,25 @@ import type { SimS3MountFilesystemOptions } from "./mount/sim-s3-mount.type.js";
18
18
  */
19
19
  export declare class SimS3 extends SimS3Operations {
20
20
  private readonly bucketAccess;
21
+ private readonly bucketMaker;
21
22
  private readonly cfnFactory;
22
23
  private readonly sdkRouter;
23
24
  constructor(properties?: SimS3Properties);
25
+ /**
26
+ * Make a Bucket in this scope without a `CreateBucketCommand`, and so
27
+ * without a principal to authorize.
28
+ *
29
+ * This is for a Bucket real AWS has provisioned before the code under test
30
+ * runs, which nothing in the simulation is deploying. The CDK bootstrap
31
+ * staging Bucket is the one the simulator makes for itself: `cdk deploy`
32
+ * publishes assets into a Bucket the bootstrap stack created, and a
33
+ * deployment that had to create it would be charged permissions no real
34
+ * deployment needs.
35
+ *
36
+ * A name this scope cannot take is reported the way `CreateBucketCommand`
37
+ * reports it.
38
+ */
39
+ makeSimBucket(bucketName: SimS3BucketName | string): SimS3Bucket;
24
40
  /**
25
41
  * Change how many keys one page of an Object listing holds, which real S3
26
42
  * fixes at a thousand.
@@ -1,5 +1,6 @@
1
1
  import { SimS3GlobalRegistry } from "./sim-s3-global-registry.js";
2
2
  import { SimS3BucketAccess } from "./bucket/sim-s3-bucket-access.js";
3
+ import { SimS3BucketMaker } from "./bucket/sim-s3-bucket-maker.js";
3
4
  import { SimS3CloudFormationResourceFactory } from "./cfn/sim-cfn-s3-resource-factory.js";
4
5
  import { simAwsAccountRegionScopeFactory } from "../aws/sim-aws-account-region-scope.factory.js";
5
6
  import { SimS3Commands } from "./sim-s3-commands.js";
@@ -16,6 +17,7 @@ import { SimS3SdkCommandRouter } from "./sdk/sim-s3-sdk-command-router.js";
16
17
  */
17
18
  export class SimS3 extends SimS3Operations {
18
19
  bucketAccess;
20
+ bucketMaker;
19
21
  cfnFactory = new SimS3CloudFormationResourceFactory(this);
20
22
  sdkRouter = new SimS3SdkCommandRouter(this);
21
23
  constructor(properties = {}) {
@@ -32,6 +34,29 @@ export class SimS3 extends SimS3Operations {
32
34
  accountRegionScope,
33
35
  s3GlobalRegistry,
34
36
  });
37
+ this.bucketMaker = new SimS3BucketMaker({
38
+ accountRegionScope,
39
+ buckets,
40
+ s3GlobalRegistry,
41
+ background: this.commands.background,
42
+ });
43
+ }
44
+ /**
45
+ * Make a Bucket in this scope without a `CreateBucketCommand`, and so
46
+ * without a principal to authorize.
47
+ *
48
+ * This is for a Bucket real AWS has provisioned before the code under test
49
+ * runs, which nothing in the simulation is deploying. The CDK bootstrap
50
+ * staging Bucket is the one the simulator makes for itself: `cdk deploy`
51
+ * publishes assets into a Bucket the bootstrap stack created, and a
52
+ * deployment that had to create it would be charged permissions no real
53
+ * deployment needs.
54
+ *
55
+ * A name this scope cannot take is reported the way `CreateBucketCommand`
56
+ * reports it.
57
+ */
58
+ makeSimBucket(bucketName) {
59
+ return this.bucketMaker.make(bucketName);
35
60
  }
36
61
  /**
37
62
  * Change how many keys one page of an Object listing holds, which real S3
@@ -2115,8 +2115,62 @@ const stacks = await simAws.cloudFormation().deployCdkOut({
2115
2115
  console.log(stacks.get("PipelineStack")?.stackName);
2116
2116
  ```
2117
2117
 
2118
- A staged CDK asset is published under the same principal, before CloudFormation reads the template
2119
- that points at it.
2118
+ ### The principal a CDK asset is published as
2119
+
2120
+ A real `cdk deploy` runs in two phases under two Roles. `cdk-assets` publishes every staged file
2121
+ asset to the bootstrap staging bucket as the file publishing Role, and CloudFormation then processes
2122
+ the template as the execution Role. `assetsCaller` names the first of those, beside the `caller`
2123
+ naming the second.
2124
+
2125
+ ```typescript sim-cloudformation-assets-caller
2126
+ /**
2127
+ * Publishing a Stack's CDK file assets as the file publishing Role.
2128
+ */
2129
+
2130
+ import { SimAws } from "@kensio/yulin";
2131
+
2132
+ const simAws = new SimAws({
2133
+ defaultAccountId: "123456789012",
2134
+ defaultRegionName: "eu-west-2",
2135
+ });
2136
+
2137
+ const publisher = await simAws.iam().makeDeployRole({
2138
+ roleName: "cdk-file-publishing",
2139
+ policyDocument: {
2140
+ Version: "2012-10-17",
2141
+ Statement: {
2142
+ Effect: "Allow",
2143
+ Action: ["s3:GetObject*", "s3:GetBucket*", "s3:List*", "s3:PutObject"],
2144
+ Resource: "arn:aws:s3:::cdk-hnb659fds-assets-*",
2145
+ },
2146
+ },
2147
+ });
2148
+
2149
+ const executor = await simAws.iam().makeDeployRole({
2150
+ roleName: "cdk-exec",
2151
+ policyDocument: {
2152
+ Version: "2012-10-17",
2153
+ Statement: { Effect: "Allow", Action: "s3:*", Resource: "*" },
2154
+ },
2155
+ });
2156
+
2157
+ const stacks = await simAws.cloudFormation().deployCdkOut({
2158
+ directoryPath: "cdk.out",
2159
+ caller: executor,
2160
+ assetsCaller: publisher,
2161
+ });
2162
+
2163
+ console.log(stacks.size);
2164
+ ```
2165
+
2166
+ `deployTemplate(...)` and `deployTemplateFile(...)` take the same option, and a Stack in an assembly
2167
+ names its own in `stackOptions`. A deployment that names no `assetsCaller` publishes its assets as
2168
+ `caller`, the way every deployment did before the two could be told apart.
2169
+
2170
+ The CDK bootstrap stack provisions the staging bucket, long before either Role runs. Yulin stands in
2171
+ for that and makes the bucket outside IAM when the first asset is published into it. So a Role
2172
+ holding only what a scoped execution policy grants on `cdk-hnb659fds-assets-*` deploys a cloud
2173
+ assembly with file assets, with no `s3:CreateBucket` or `s3:PutObject` added to it.
2120
2174
 
2121
2175
  ## Editing a synthesized template before deploying it
2122
2176
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kensio/yulin",
3
- "version": "1.21.4",
3
+ "version": "1.21.5",
4
4
  "description": "AWS system behaviour simulation for isolated unit testing",
5
5
  "repository": "https://github.com/KensioSoftware/yulin",
6
6
  "homepage": "https://yulinsim.dev/",
@@ -293,7 +293,7 @@
293
293
  "eslint-plugin-jsdoc": "^63.3.2",
294
294
  "eslint-plugin-no-secrets": "^2.3.3",
295
295
  "eslint-plugin-security": "^4.0.1",
296
- "eslint-plugin-unicorn": "^72.0.0",
296
+ "eslint-plugin-unicorn": "^73.0.0",
297
297
  "execa": "^10.0.0",
298
298
  "fta-cli": "^3.0.0",
299
299
  "hyparquet": "^1.29.2",