@guardian/cdk 64.0.0 → 64.1.0

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 (102) hide show
  1. package/lib/aspects/cfn-include-reporter.js.map +1 -1
  2. package/lib/aspects/cfn-parameter-reporter.js.map +1 -1
  3. package/lib/aspects/metadata.js.map +1 -1
  4. package/lib/bin/aws-credential-provider.d.ts.map +1 -1
  5. package/lib/bin/commands/account-readiness/index.d.ts.map +1 -1
  6. package/lib/bin/commands/account-readiness/index.js.map +1 -1
  7. package/lib/bin/commands/account-readiness/ssm.d.ts.map +1 -1
  8. package/lib/bin/commands/account-readiness/ssm.js.map +1 -1
  9. package/lib/bin/commands/aws-cdk-version.d.ts.map +1 -1
  10. package/lib/bin/commands/new-project/index.d.ts.map +1 -1
  11. package/lib/bin/commands/new-project/index.js.map +1 -1
  12. package/lib/bin/commands/new-project/utils/app.d.ts.map +1 -1
  13. package/lib/bin/commands/new-project/utils/app.js.map +1 -1
  14. package/lib/bin/commands/new-project/utils/imports.js.map +1 -1
  15. package/lib/bin/commands/new-project/utils/init.d.ts.map +1 -1
  16. package/lib/bin/commands/new-project/utils/init.js.map +1 -1
  17. package/lib/bin/commands/new-project/utils/snapshot.d.ts.map +1 -1
  18. package/lib/bin/commands/new-project/utils/snapshot.js.map +1 -1
  19. package/lib/bin/commands/new-project/utils/stack.d.ts.map +1 -1
  20. package/lib/bin/commands/new-project/utils/stack.js.map +1 -1
  21. package/lib/bin/commands/new-project/utils/utils.js +1 -2
  22. package/lib/bin/commands/new-project/utils/utils.js.map +1 -1
  23. package/lib/bin/index.js.map +1 -1
  24. package/lib/constants/library-info.js +3 -3
  25. package/lib/constants/library-info.js.map +1 -1
  26. package/lib/constructs/autoscaling/asg.js.map +1 -1
  27. package/lib/constructs/autoscaling/user-data.js.map +1 -1
  28. package/lib/constructs/cloudwatch/alarm.js.map +1 -1
  29. package/lib/constructs/core/identity.d.ts.map +1 -1
  30. package/lib/constructs/core/parameters/anghammarad.js.map +1 -1
  31. package/lib/constructs/core/parameters/fastly.js.map +1 -1
  32. package/lib/constructs/core/parameters/log-shipping.js.map +1 -1
  33. package/lib/constructs/core/parameters/s3.js.map +1 -1
  34. package/lib/constructs/core/parameters/vpc.js.map +1 -1
  35. package/lib/constructs/core/stack.js.map +1 -1
  36. package/lib/constructs/dns/dns-records.js.map +1 -1
  37. package/lib/constructs/ec2/security-groups/base.js.map +1 -1
  38. package/lib/constructs/ec2/vpc.d.ts.map +1 -1
  39. package/lib/constructs/ec2/vpc.js.map +1 -1
  40. package/lib/constructs/ecs/ecs-task.js +1 -1
  41. package/lib/constructs/ecs/ecs-task.js.map +1 -1
  42. package/lib/constructs/iam/policies/anghammarad.js.map +1 -1
  43. package/lib/constructs/iam/policies/describe-ec2.js.map +1 -1
  44. package/lib/constructs/iam/policies/kcl.js +1 -1
  45. package/lib/constructs/iam/policies/kcl.js.map +1 -1
  46. package/lib/constructs/iam/policies/log-shipping.js.map +1 -1
  47. package/lib/constructs/iam/policies/parameter-store-read.js.map +1 -1
  48. package/lib/constructs/iam/policies/ssm-ssh.js.map +1 -1
  49. package/lib/constructs/lambda/lambda.js.map +1 -1
  50. package/lib/constructs/loadbalancing/alb/application-load-balancer.js.map +1 -1
  51. package/lib/constructs/loadbalancing/alb/application-target-group.js +1 -1
  52. package/lib/constructs/loadbalancing/alb/application-target-group.js.map +1 -1
  53. package/lib/constructs/ses/index.js.map +1 -1
  54. package/lib/constructs/vpc/vpc.js.map +1 -1
  55. package/lib/experimental/constructs/iam/policies/developer-policy.js.map +1 -1
  56. package/lib/experimental/constructs/riff-raff-deployment-id.js.map +1 -1
  57. package/lib/experimental/patterns/ec2-app.js.map +1 -1
  58. package/lib/experimental/patterns/gu-load-balanced-app.d.ts.map +1 -1
  59. package/lib/experimental/patterns/gu-load-balanced-app.js +21 -4
  60. package/lib/experimental/patterns/gu-load-balanced-app.js.map +1 -1
  61. package/lib/patterns/api-lambda.js.map +1 -1
  62. package/lib/patterns/api-multiple-lambdas.js.map +1 -1
  63. package/lib/patterns/ec2-app/base.js +2 -2
  64. package/lib/patterns/ec2-app/base.js.map +1 -1
  65. package/lib/riff-raff-yaml-file/deployments/autoscaling.js +2 -3
  66. package/lib/riff-raff-yaml-file/deployments/autoscaling.js.map +1 -1
  67. package/lib/riff-raff-yaml-file/deployments/cloudformation.js +3 -4
  68. package/lib/riff-raff-yaml-file/deployments/cloudformation.js.map +1 -1
  69. package/lib/riff-raff-yaml-file/deployments/lambda.js +2 -3
  70. package/lib/riff-raff-yaml-file/deployments/lambda.js.map +1 -1
  71. package/lib/riff-raff-yaml-file/deployments/update-parameters.js +1 -2
  72. package/lib/riff-raff-yaml-file/deployments/update-parameters.js.map +1 -1
  73. package/lib/riff-raff-yaml-file/group-by.js +5 -6
  74. package/lib/riff-raff-yaml-file/group-by.js.map +1 -1
  75. package/lib/riff-raff-yaml-file/index.js +2 -2
  76. package/lib/riff-raff-yaml-file/index.js.map +1 -1
  77. package/lib/types/access.js.map +1 -1
  78. package/lib/utils/array.js +1 -2
  79. package/lib/utils/array.js.map +1 -1
  80. package/lib/utils/cli/vpc.d.ts.map +1 -1
  81. package/lib/utils/cli/vpc.js.map +1 -1
  82. package/lib/utils/cognito/cognito.d.ts.map +1 -1
  83. package/lib/utils/exec.js +2 -3
  84. package/lib/utils/exec.js.map +1 -1
  85. package/lib/utils/git.js +2 -3
  86. package/lib/utils/git.js.map +1 -1
  87. package/lib/utils/lambda/event-source.js +2 -2
  88. package/lib/utils/lambda/event-source.js.map +1 -1
  89. package/lib/utils/math.d.ts.map +1 -1
  90. package/lib/utils/mixin/app-aware-construct.js +1 -2
  91. package/lib/utils/mixin/app-aware-construct.js.map +1 -1
  92. package/lib/utils/security-groups/helpers.d.ts.map +1 -1
  93. package/lib/utils/singleton.d.ts +1 -1
  94. package/lib/utils/singleton.d.ts.map +1 -1
  95. package/lib/utils/string.d.ts.map +1 -1
  96. package/lib/utils/test/assertions.js.map +1 -1
  97. package/lib/utils/test/attach-policy-to-test-role.d.ts.map +1 -1
  98. package/lib/utils/test/simple-gu-stack.js +1 -2
  99. package/lib/utils/test/simple-gu-stack.js.map +1 -1
  100. package/lib/utils/test/template.js +1 -2
  101. package/lib/utils/test/template.js.map +1 -1
  102. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"application-target-group.js","sourceRoot":"","sources":["../../../../src/constructs/loadbalancing/alb/application-target-group.ts"],"names":[],"mappings":";;;AAAA,6CAAoD;AACpD,uFAA+G;AAE/G,kFAA+E;AAK/E;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAa,wBAAyB,SAAQ,IAAA,yCAAmB,EAAC,mDAAsB,CAAC;IAavF,YAAY,KAAc,EAAE,EAAU,EAAE,KAAoC;QAC1E,MAAM,WAAW,GAAgC;YAC/C,QAAQ,EAAE,gDAAmB,CAAC,IAAI;YAClC,mBAAmB,EAAE,sBAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACzC,GAAG,KAAK;YACR,WAAW,EAAE,EAAE,GAAG,wBAAwB,CAAC,kBAAkB,EAAE,GAAG,KAAK,CAAC,WAAW,EAAE;SACtF,CAAC;QAEF,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,WAAW,CAAC,CAAC;QAE9B,MAAM,QAAQ,GAAG,WAAW,CAAC,WAAW,EAAE,QAAQ,IAAI,wBAAwB,CAAC,0BAA0B,CAAC;QAC1G,MAAM,OAAO,GAAG,WAAW,CAAC,WAAW,EAAE,OAAO,IAAI,wBAAwB,CAAC,yBAAyB,CAAC;QAEvG;;;WAGG;QACH,IAAI,OAAO,CAAC,SAAS,EAAE,IAAI,QAAQ,CAAC,SAAS,EAAE,EAAE;YAC/C,MAAM,OAAO,GAAG,+CAA+C,OAAO,CAAC,SAAS,EAAE,kCAAkC,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC;YAC5I,yBAAW,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,wDAAwD;YAChG,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;SAC1B;IACH,CAAC;;AAnCH,4DAoCC;AAnCgB,mDAA0B,GAAG,sBAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAClD,kDAAyB,GAAG,sBAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAExD,2CAAkB,GAAgB;IACvC,IAAI,EAAE,cAAc;IACpB,QAAQ,EAAE,qCAAQ,CAAC,IAAI;IACvB,qBAAqB,EAAE,CAAC;IACxB,uBAAuB,EAAE,CAAC;IAC1B,QAAQ,EAAE,wBAAwB,CAAC,0BAA0B;IAC7D,OAAO,EAAE,wBAAwB,CAAC,yBAAyB;CAC5D,CAAC","sourcesContent":["import { Annotations, Duration } from \"aws-cdk-lib\";\nimport { ApplicationProtocol, ApplicationTargetGroup, Protocol } from \"aws-cdk-lib/aws-elasticloadbalancingv2\";\nimport type { ApplicationTargetGroupProps, HealthCheck } from \"aws-cdk-lib/aws-elasticloadbalancingv2\";\nimport { GuAppAwareConstruct } from \"../../../utils/mixin/app-aware-construct\";\nimport type { AppIdentity, GuStack } from \"../../core\";\n\nexport interface GuApplicationTargetGroupProps extends ApplicationTargetGroupProps, AppIdentity {}\n\n/**\n * Construct which creates a Target Group.\n *\n * This construct should be used in conjunction with [[`GuApplicationLoadBalancer`]] and [[`GuApplicationListener`]]\n * to route traffic to your application. For more details on these three components, see the\n * [AWS documentation](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html#application-load-balancer-components).\n *\n * By default, Target Groups created via this construct will perform a healthcheck against `/healthcheck` on your application's\n * traffic port (as specified via the `port` prop). All healthcheck defaults can be overridden via the `healthcheck` prop.\n *\n * For example, to use `/test` for the healthcheck path use:\n *\n * ```typescript\n * new GuApplicationTargetGroup(stack, \"TargetGroup\", {\n * // other props\n * healthCheck: {\n * path: \"/test\",\n * },\n * });\n * ```\n *\n * This resource is stateful.\n * @see https://github.com/guardian/cdk/blob/main/docs/stateful-resources.md\n */\nexport class GuApplicationTargetGroup extends GuAppAwareConstruct(ApplicationTargetGroup) {\n private static defaultHealthcheckInterval = Duration.seconds(10);\n private static defaultHealthcheckTimeout = Duration.seconds(5);\n\n static DefaultHealthCheck: HealthCheck = {\n path: \"/healthcheck\",\n protocol: Protocol.HTTP,\n healthyThresholdCount: 5,\n unhealthyThresholdCount: 2,\n interval: GuApplicationTargetGroup.defaultHealthcheckInterval,\n timeout: GuApplicationTargetGroup.defaultHealthcheckTimeout,\n };\n\n constructor(scope: GuStack, id: string, props: GuApplicationTargetGroupProps) {\n const mergedProps: ApplicationTargetGroupProps = {\n protocol: ApplicationProtocol.HTTP, // We terminate HTTPS at the load balancer level, so load balancer to ASG/EC2 traffic can be over HTTP\n deregistrationDelay: Duration.seconds(30),\n ...props,\n healthCheck: { ...GuApplicationTargetGroup.DefaultHealthCheck, ...props.healthCheck },\n };\n\n super(scope, id, mergedProps);\n\n const interval = mergedProps.healthCheck?.interval ?? GuApplicationTargetGroup.defaultHealthcheckInterval;\n const timeout = mergedProps.healthCheck?.timeout ?? GuApplicationTargetGroup.defaultHealthcheckTimeout;\n\n /*\n The healthcheck `timeout` must be lower than `interval`\n See https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-elb-health-check.html#cfn-elb-healthcheck-timeout\n */\n if (timeout.toSeconds() >= interval.toSeconds()) {\n const message = `Illegal healthcheck configuration: timeout (${timeout.toSeconds()}) must be lower than interval (${interval.toSeconds()})`;\n Annotations.of(this).addError(message); // adds a useful message to the console to aid debugging\n throw new Error(message);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"application-target-group.js","sourceRoot":"","sources":["../../../../src/constructs/loadbalancing/alb/application-target-group.ts"],"names":[],"mappings":";;;AAAA,6CAAoD;AACpD,uFAA+G;AAE/G,kFAA+E;AAK/E;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAa,wBAAyB,SAAQ,IAAA,yCAAmB,EAAC,mDAAsB,CAAC;IAavF,YAAY,KAAc,EAAE,EAAU,EAAE,KAAoC;QAC1E,MAAM,WAAW,GAAgC;YAC/C,QAAQ,EAAE,gDAAmB,CAAC,IAAI,EAAE,sGAAsG;YAC1I,mBAAmB,EAAE,sBAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACzC,GAAG,KAAK;YACR,WAAW,EAAE,EAAE,GAAG,wBAAwB,CAAC,kBAAkB,EAAE,GAAG,KAAK,CAAC,WAAW,EAAE;SACtF,CAAC;QAEF,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,WAAW,CAAC,CAAC;QAE9B,MAAM,QAAQ,GAAG,WAAW,CAAC,WAAW,EAAE,QAAQ,IAAI,wBAAwB,CAAC,0BAA0B,CAAC;QAC1G,MAAM,OAAO,GAAG,WAAW,CAAC,WAAW,EAAE,OAAO,IAAI,wBAAwB,CAAC,yBAAyB,CAAC;QAEvG;;;WAGG;QACH,IAAI,OAAO,CAAC,SAAS,EAAE,IAAI,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC;YAChD,MAAM,OAAO,GAAG,+CAA+C,OAAO,CAAC,SAAS,EAAE,kCAAkC,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC;YAC5I,yBAAW,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,wDAAwD;YAChG,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;;AAnCH,4DAoCC;AAnCgB,mDAA0B,GAAG,sBAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAClD,kDAAyB,GAAG,sBAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAExD,2CAAkB,GAAgB;IACvC,IAAI,EAAE,cAAc;IACpB,QAAQ,EAAE,qCAAQ,CAAC,IAAI;IACvB,qBAAqB,EAAE,CAAC;IACxB,uBAAuB,EAAE,CAAC;IAC1B,QAAQ,EAAE,wBAAwB,CAAC,0BAA0B;IAC7D,OAAO,EAAE,wBAAwB,CAAC,yBAAyB;CAC5D,CAAC","sourcesContent":["import { Annotations, Duration } from \"aws-cdk-lib\";\nimport { ApplicationProtocol, ApplicationTargetGroup, Protocol } from \"aws-cdk-lib/aws-elasticloadbalancingv2\";\nimport type { ApplicationTargetGroupProps, HealthCheck } from \"aws-cdk-lib/aws-elasticloadbalancingv2\";\nimport { GuAppAwareConstruct } from \"../../../utils/mixin/app-aware-construct\";\nimport type { AppIdentity, GuStack } from \"../../core\";\n\nexport interface GuApplicationTargetGroupProps extends ApplicationTargetGroupProps, AppIdentity {}\n\n/**\n * Construct which creates a Target Group.\n *\n * This construct should be used in conjunction with [[`GuApplicationLoadBalancer`]] and [[`GuApplicationListener`]]\n * to route traffic to your application. For more details on these three components, see the\n * [AWS documentation](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html#application-load-balancer-components).\n *\n * By default, Target Groups created via this construct will perform a healthcheck against `/healthcheck` on your application's\n * traffic port (as specified via the `port` prop). All healthcheck defaults can be overridden via the `healthcheck` prop.\n *\n * For example, to use `/test` for the healthcheck path use:\n *\n * ```typescript\n * new GuApplicationTargetGroup(stack, \"TargetGroup\", {\n * // other props\n * healthCheck: {\n * path: \"/test\",\n * },\n * });\n * ```\n *\n * This resource is stateful.\n * @see https://github.com/guardian/cdk/blob/main/docs/stateful-resources.md\n */\nexport class GuApplicationTargetGroup extends GuAppAwareConstruct(ApplicationTargetGroup) {\n private static defaultHealthcheckInterval = Duration.seconds(10);\n private static defaultHealthcheckTimeout = Duration.seconds(5);\n\n static DefaultHealthCheck: HealthCheck = {\n path: \"/healthcheck\",\n protocol: Protocol.HTTP,\n healthyThresholdCount: 5,\n unhealthyThresholdCount: 2,\n interval: GuApplicationTargetGroup.defaultHealthcheckInterval,\n timeout: GuApplicationTargetGroup.defaultHealthcheckTimeout,\n };\n\n constructor(scope: GuStack, id: string, props: GuApplicationTargetGroupProps) {\n const mergedProps: ApplicationTargetGroupProps = {\n protocol: ApplicationProtocol.HTTP, // We terminate HTTPS at the load balancer level, so load balancer to ASG/EC2 traffic can be over HTTP\n deregistrationDelay: Duration.seconds(30),\n ...props,\n healthCheck: { ...GuApplicationTargetGroup.DefaultHealthCheck, ...props.healthCheck },\n };\n\n super(scope, id, mergedProps);\n\n const interval = mergedProps.healthCheck?.interval ?? GuApplicationTargetGroup.defaultHealthcheckInterval;\n const timeout = mergedProps.healthCheck?.timeout ?? GuApplicationTargetGroup.defaultHealthcheckTimeout;\n\n /*\n The healthcheck `timeout` must be lower than `interval`\n See https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-elb-health-check.html#cfn-elb-healthcheck-timeout\n */\n if (timeout.toSeconds() >= interval.toSeconds()) {\n const message = `Illegal healthcheck configuration: timeout (${timeout.toSeconds()}) must be lower than interval (${interval.toSeconds()})`;\n Annotations.of(this).addError(message); // adds a useful message to the console to aid debugging\n throw new Error(message);\n }\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/constructs/ses/index.ts"],"names":[],"mappings":";;;AAAA,6CAAuC;AAEvC,iDAA8D;AAC9D,+EAA4E;AAE5E,gCAAiC;AAYjC,gDAAgD;AAChD,MAAM,YAAY,GAAG;IACnB,eAAe;IACf,mBAAmB;IACnB,kBAAkB;IAClB,sBAAsB;IACtB,oBAAoB;CACrB,CAAC;AAEF;;;;;;;;;;;IAWI;AACJ,MAAa,eAAgB,SAAQ,IAAA,yCAAmB,EAAC,uBAAa,CAAC;IACrE,YAAY,KAAc,EAAE,EAAU,EAAE,KAA2B;QACjE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC;QAElC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,EAAE;YACzE,MAAM,IAAI,KAAK,CAAC,4DAA4D,UAAU,oBAAoB,CAAC,CAAC;SAC7G;QAED,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE;YACf,QAAQ,EAAE,kBAAQ,CAAC,MAAM,CAAC,UAAU,CAAC;YACrC,GAAG,KAAK;SACT,CAAC,CAAC;QAEH,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE;YAClD,IAAI,aAAO,CAAC,KAAK,EAAE,qBAAqB,GAAG,IAAI,KAAK,EAAE,EAAE;gBACtD,GAAG;gBACH,UAAU,EAAE,IAAI;gBAChB,cAAc,EAAE,KAAK;gBACrB,GAAG,EAAE,sBAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;aACvB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,KAAK,YAAY;QACrB,OAAO,YAAY,CAAC;IACtB,CAAC;CACF;AA1BD,0CA0BC","sourcesContent":["import { Duration } from \"aws-cdk-lib\";\nimport type { EmailIdentityProps } from \"aws-cdk-lib/aws-ses\";\nimport { EmailIdentity, Identity } from \"aws-cdk-lib/aws-ses\";\nimport { GuAppAwareConstruct } from \"../../utils/mixin/app-aware-construct\";\nimport type { AppIdentity, GuStack } from \"../core\";\nimport { GuCname } from \"../dns\";\n\n/**\n * Properties for the GuEmailIdentity construct\n * domainName: The domain name to use for this identity\n * The domain name must be one of GuEmailIdentity.validDomains\n * @see https://github.com/guardian/dns-validation-lambda/blob/main/README.md\n */\nexport interface GuEmailIdentityProps extends Omit<EmailIdentityProps, \"identity\">, AppIdentity {\n domainName: string;\n}\n\n// TODO: Expand this list as we add more domains\nconst validDomains = [\n \"gutools.co.uk\",\n \"dev-gutools.co.uk\",\n \"guardianapis.com\",\n \"dev-guardianapis.com\",\n \"guardianapps.co.uk\",\n];\n\n/**\n * A construct to create an SES email identity.\n * It also creates the required DNS records to verify the identity.\n * @see https://docs.aws.amazon.com/ses/latest/DeveloperGuide/verify-domains.html\n * @see https://docs.aws.amazon.com/ses/latest/DeveloperGuide/send-email-identity.html\n *\n * @example\n * new GuEmailIdentity(stack, \"MyEmailIdentity\", {\n * domainName: \"my-service.gutools.co.uk\",\n * app: \"test\",\n * });\n **/\nexport class GuEmailIdentity extends GuAppAwareConstruct(EmailIdentity) {\n constructor(scope: GuStack, id: string, props: GuEmailIdentityProps) {\n const { app, domainName } = props;\n\n if (!validDomains.some((validDomain) => domainName.endsWith(validDomain))) {\n throw new Error(`Auto verification is only supported for certain domains. ${domainName} is not supported.`);\n }\n\n super(scope, id, {\n identity: Identity.domain(domainName),\n ...props,\n });\n\n this.dkimRecords.forEach(({ name, value }, index) => {\n new GuCname(scope, `EmailIdentityDkim-${app}-${index}`, {\n app,\n domainName: name,\n resourceRecord: value,\n ttl: Duration.hours(1),\n });\n });\n }\n\n static get validDomains(): string[] {\n return validDomains;\n }\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/constructs/ses/index.ts"],"names":[],"mappings":";;;AAAA,6CAAuC;AAEvC,iDAA8D;AAC9D,+EAA4E;AAE5E,gCAAiC;AAYjC,gDAAgD;AAChD,MAAM,YAAY,GAAG;IACnB,eAAe;IACf,mBAAmB;IACnB,kBAAkB;IAClB,sBAAsB;IACtB,oBAAoB;CACrB,CAAC;AAEF;;;;;;;;;;;IAWI;AACJ,MAAa,eAAgB,SAAQ,IAAA,yCAAmB,EAAC,uBAAa,CAAC;IACrE,YAAY,KAAc,EAAE,EAAU,EAAE,KAA2B;QACjE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC;QAElC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;YAC1E,MAAM,IAAI,KAAK,CAAC,4DAA4D,UAAU,oBAAoB,CAAC,CAAC;QAC9G,CAAC;QAED,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE;YACf,QAAQ,EAAE,kBAAQ,CAAC,MAAM,CAAC,UAAU,CAAC;YACrC,GAAG,KAAK;SACT,CAAC,CAAC;QAEH,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE;YAClD,IAAI,aAAO,CAAC,KAAK,EAAE,qBAAqB,GAAG,IAAI,KAAK,EAAE,EAAE;gBACtD,GAAG;gBACH,UAAU,EAAE,IAAI;gBAChB,cAAc,EAAE,KAAK;gBACrB,GAAG,EAAE,sBAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;aACvB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,KAAK,YAAY;QACrB,OAAO,YAAY,CAAC;IACtB,CAAC;CACF;AA1BD,0CA0BC","sourcesContent":["import { Duration } from \"aws-cdk-lib\";\nimport type { EmailIdentityProps } from \"aws-cdk-lib/aws-ses\";\nimport { EmailIdentity, Identity } from \"aws-cdk-lib/aws-ses\";\nimport { GuAppAwareConstruct } from \"../../utils/mixin/app-aware-construct\";\nimport type { AppIdentity, GuStack } from \"../core\";\nimport { GuCname } from \"../dns\";\n\n/**\n * Properties for the GuEmailIdentity construct\n * domainName: The domain name to use for this identity\n * The domain name must be one of GuEmailIdentity.validDomains\n * @see https://github.com/guardian/dns-validation-lambda/blob/main/README.md\n */\nexport interface GuEmailIdentityProps extends Omit<EmailIdentityProps, \"identity\">, AppIdentity {\n domainName: string;\n}\n\n// TODO: Expand this list as we add more domains\nconst validDomains = [\n \"gutools.co.uk\",\n \"dev-gutools.co.uk\",\n \"guardianapis.com\",\n \"dev-guardianapis.com\",\n \"guardianapps.co.uk\",\n];\n\n/**\n * A construct to create an SES email identity.\n * It also creates the required DNS records to verify the identity.\n * @see https://docs.aws.amazon.com/ses/latest/DeveloperGuide/verify-domains.html\n * @see https://docs.aws.amazon.com/ses/latest/DeveloperGuide/send-email-identity.html\n *\n * @example\n * new GuEmailIdentity(stack, \"MyEmailIdentity\", {\n * domainName: \"my-service.gutools.co.uk\",\n * app: \"test\",\n * });\n **/\nexport class GuEmailIdentity extends GuAppAwareConstruct(EmailIdentity) {\n constructor(scope: GuStack, id: string, props: GuEmailIdentityProps) {\n const { app, domainName } = props;\n\n if (!validDomains.some((validDomain) => domainName.endsWith(validDomain))) {\n throw new Error(`Auto verification is only supported for certain domains. ${domainName} is not supported.`);\n }\n\n super(scope, id, {\n identity: Identity.domain(domainName),\n ...props,\n });\n\n this.dkimRecords.forEach(({ name, value }, index) => {\n new GuCname(scope, `EmailIdentityDkim-${app}-${index}`, {\n app,\n domainName: name,\n resourceRecord: value,\n ttl: Duration.hours(1),\n });\n });\n }\n\n static get validDomains(): string[] {\n return validDomains;\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"vpc.js","sourceRoot":"","sources":["../../../src/constructs/vpc/vpc.ts"],"names":[],"mappings":";;;AAAA,6CAAoC;AAEpC,iDAAoF;AAKpF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAa,KAAM,SAAQ,aAAG;IAC5B;;;;;;;OAOG;IACK,MAAM,CAAC,0BAA0B,CAAC,EAAE,OAAO,EAAE,IAAI,EAAW;QAClE,IAAI,mBAAK,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE;YAC/B,MAAM,IAAI,KAAK,CACb,8JAA8J,CAC/J,CAAC;SACH;QAED,MAAM,UAAU,GAAG,8BAA8B,OAAO,mBAAmB,CAAC;QAE5E,IAAI,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,KAAK,SAAS,EAAE;YAChD,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC;SACzE;IACH,CAAC;IAED,YAAY,KAAc,EAAE,EAAU,EAAE,KAAkB;QACxD,MAAM,eAAe,GAAa;YAChC,gBAAgB,EAAE;gBAChB,EAAE,EAAE;oBACF,OAAO,EAAE,sCAA4B,CAAC,EAAE;iBACzC;gBACD,QAAQ,EAAE;oBACR,OAAO,EAAE,sCAA4B,CAAC,QAAQ;iBAC/C;aACF;YAED,mBAAmB,EAAE;gBACnB;oBACE,IAAI,EAAE,SAAS;oBACf,UAAU,EAAE,oBAAU,CAAC,MAAM;iBAC9B;gBACD;oBACE,IAAI,EAAE,aAAa;oBACnB,UAAU,EAAE,oBAAU,CAAC,mBAAmB;iBAC3C;aACF;SACF,CAAC;QAEF,8GAA8G;QAC9G,KAAK,CAAC,0BAA0B,CAAC,KAAK,CAAC,CAAC;QAExC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,eAAe,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;IACrD,CAAC;CACF;AAnDD,sBAmDC","sourcesContent":["import { Token } from \"aws-cdk-lib\";\nimport type { VpcProps } from \"aws-cdk-lib/aws-ec2\";\nimport { GatewayVpcEndpointAwsService, SubnetType, Vpc } from \"aws-cdk-lib/aws-ec2\";\nimport type { GuStack } from \"../core\";\n\nexport type GuVpcProps = VpcProps;\n\n/**\n * Construct which creates a Virtual Private Cloud.\n *\n * NOTE: This construct requires an\n * [environment](https://docs.aws.amazon.com/cdk/latest/guide/environments.html)\n * to be set to function correctly. Without this, an environment-agnostic\n * template will be produced, which will only use two AZs even if the region\n * contains more than that. To set this, set the `env` prop when instantiating\n * your stack.\n *\n * NOTE: If using this construct outside eu-west-1, you'll need to commit the\n * `cdk.context.json` file that's created after synthesising locally.\n *\n * Be aware that account IDs are considered sensitive information and should NOT\n * be committed to public repos.\n *\n * The VPC is provisioned with a public and private subnet for each availability\n * zone, with IPs spread evenly across these. Instances in private subnets\n * cannot be accessed directly over the internet. You should locate services in\n * instances in the private subnets, and provide HTTP(S) as required via load\n * balancers living in the public subnets.\n *\n * A CIDR block is a combined IP and network mask that specifies the IP\n * addresses in your VPC. For testing purposes any (AWS supported) CIDR block\n * will do, but for production you should:\n *\n * - use a /21 network mask (providing up to 2048 IPs, which is plenty for most\n * accounts)\n * - ask Enterprise Tech for an IP address to go with this range (so that\n * peering with other company VPCs is possible without IP clashes)\n *\n * A managed NAT is created for each private subnet to allow access to the\n * internet.\n *\n * For recommendations on how best to configure your VPC see:\n * https://github.com/guardian/recommendations/blob/main/AWS.md#vpc\n *\n * For more information on VPCs and AWS see:\n * https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Subnets.html.\n */\nexport class GuVpc extends Vpc {\n /**\n * Programmatically sets the CDK context with a list of AZs for eu-west-1.\n * This means consuming stacks do NOT have to commit a `cdk.context.json`\n * file when using this construct in eu-west-1.\n *\n * @throws {Error} if the account ID has not been explicitly set on the parent GuStack\n * @private\n */\n private static setAvailabilityZoneContext({ account, node }: GuStack) {\n if (Token.isUnresolved(account)) {\n throw new Error(\n `Account ID not set - the resulting VPC might not be shaped how you'd expect. See https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.StackProps.html#env`,\n );\n }\n\n const contextKey = `availability-zones:account=${account}:region=eu-west-1`;\n\n if (node.tryGetContext(contextKey) === undefined) {\n node.setContext(contextKey, [\"eu-west-1a\", \"eu-west-1b\", \"eu-west-1c\"]);\n }\n }\n\n constructor(scope: GuStack, id: string, props?: GuVpcProps) {\n const defaultVpcProps: VpcProps = {\n gatewayEndpoints: {\n s3: {\n service: GatewayVpcEndpointAwsService.S3,\n },\n dynamodb: {\n service: GatewayVpcEndpointAwsService.DYNAMODB,\n },\n },\n\n subnetConfiguration: [\n {\n name: \"ingress\",\n subnetType: SubnetType.PUBLIC,\n },\n {\n name: \"application\",\n subnetType: SubnetType.PRIVATE_WITH_EGRESS,\n },\n ],\n };\n\n // Set the context BEFORE the `super` call to avoid `Error: Cannot set context after children have been added`\n GuVpc.setAvailabilityZoneContext(scope);\n\n super(scope, id, { ...defaultVpcProps, ...props });\n }\n}\n"]}
1
+ {"version":3,"file":"vpc.js","sourceRoot":"","sources":["../../../src/constructs/vpc/vpc.ts"],"names":[],"mappings":";;;AAAA,6CAAoC;AAEpC,iDAAoF;AAKpF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAa,KAAM,SAAQ,aAAG;IAC5B;;;;;;;OAOG;IACK,MAAM,CAAC,0BAA0B,CAAC,EAAE,OAAO,EAAE,IAAI,EAAW;QAClE,IAAI,mBAAK,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACb,8JAA8J,CAC/J,CAAC;QACJ,CAAC;QAED,MAAM,UAAU,GAAG,8BAA8B,OAAO,mBAAmB,CAAC;QAE5E,IAAI,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,KAAK,SAAS,EAAE,CAAC;YACjD,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;IAED,YAAY,KAAc,EAAE,EAAU,EAAE,KAAkB;QACxD,MAAM,eAAe,GAAa;YAChC,gBAAgB,EAAE;gBAChB,EAAE,EAAE;oBACF,OAAO,EAAE,sCAA4B,CAAC,EAAE;iBACzC;gBACD,QAAQ,EAAE;oBACR,OAAO,EAAE,sCAA4B,CAAC,QAAQ;iBAC/C;aACF;YAED,mBAAmB,EAAE;gBACnB;oBACE,IAAI,EAAE,SAAS;oBACf,UAAU,EAAE,oBAAU,CAAC,MAAM;iBAC9B;gBACD;oBACE,IAAI,EAAE,aAAa;oBACnB,UAAU,EAAE,oBAAU,CAAC,mBAAmB;iBAC3C;aACF;SACF,CAAC;QAEF,8GAA8G;QAC9G,KAAK,CAAC,0BAA0B,CAAC,KAAK,CAAC,CAAC;QAExC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,eAAe,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;IACrD,CAAC;CACF;AAnDD,sBAmDC","sourcesContent":["import { Token } from \"aws-cdk-lib\";\nimport type { VpcProps } from \"aws-cdk-lib/aws-ec2\";\nimport { GatewayVpcEndpointAwsService, SubnetType, Vpc } from \"aws-cdk-lib/aws-ec2\";\nimport type { GuStack } from \"../core\";\n\nexport type GuVpcProps = VpcProps;\n\n/**\n * Construct which creates a Virtual Private Cloud.\n *\n * NOTE: This construct requires an\n * [environment](https://docs.aws.amazon.com/cdk/latest/guide/environments.html)\n * to be set to function correctly. Without this, an environment-agnostic\n * template will be produced, which will only use two AZs even if the region\n * contains more than that. To set this, set the `env` prop when instantiating\n * your stack.\n *\n * NOTE: If using this construct outside eu-west-1, you'll need to commit the\n * `cdk.context.json` file that's created after synthesising locally.\n *\n * Be aware that account IDs are considered sensitive information and should NOT\n * be committed to public repos.\n *\n * The VPC is provisioned with a public and private subnet for each availability\n * zone, with IPs spread evenly across these. Instances in private subnets\n * cannot be accessed directly over the internet. You should locate services in\n * instances in the private subnets, and provide HTTP(S) as required via load\n * balancers living in the public subnets.\n *\n * A CIDR block is a combined IP and network mask that specifies the IP\n * addresses in your VPC. For testing purposes any (AWS supported) CIDR block\n * will do, but for production you should:\n *\n * - use a /21 network mask (providing up to 2048 IPs, which is plenty for most\n * accounts)\n * - ask Enterprise Tech for an IP address to go with this range (so that\n * peering with other company VPCs is possible without IP clashes)\n *\n * A managed NAT is created for each private subnet to allow access to the\n * internet.\n *\n * For recommendations on how best to configure your VPC see:\n * https://github.com/guardian/recommendations/blob/main/AWS.md#vpc\n *\n * For more information on VPCs and AWS see:\n * https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Subnets.html.\n */\nexport class GuVpc extends Vpc {\n /**\n * Programmatically sets the CDK context with a list of AZs for eu-west-1.\n * This means consuming stacks do NOT have to commit a `cdk.context.json`\n * file when using this construct in eu-west-1.\n *\n * @throws {Error} if the account ID has not been explicitly set on the parent GuStack\n * @private\n */\n private static setAvailabilityZoneContext({ account, node }: GuStack) {\n if (Token.isUnresolved(account)) {\n throw new Error(\n `Account ID not set - the resulting VPC might not be shaped how you'd expect. See https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.StackProps.html#env`,\n );\n }\n\n const contextKey = `availability-zones:account=${account}:region=eu-west-1`;\n\n if (node.tryGetContext(contextKey) === undefined) {\n node.setContext(contextKey, [\"eu-west-1a\", \"eu-west-1b\", \"eu-west-1c\"]);\n }\n }\n\n constructor(scope: GuStack, id: string, props?: GuVpcProps) {\n const defaultVpcProps: VpcProps = {\n gatewayEndpoints: {\n s3: {\n service: GatewayVpcEndpointAwsService.S3,\n },\n dynamodb: {\n service: GatewayVpcEndpointAwsService.DYNAMODB,\n },\n },\n\n subnetConfiguration: [\n {\n name: \"ingress\",\n subnetType: SubnetType.PUBLIC,\n },\n {\n name: \"application\",\n subnetType: SubnetType.PRIVATE_WITH_EGRESS,\n },\n ],\n };\n\n // Set the context BEFORE the `super` call to avoid `Error: Cannot set context after children have been added`\n GuVpc.setAvailabilityZoneContext(scope);\n\n super(scope, id, { ...defaultVpcProps, ...props });\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"developer-policy.js","sourceRoot":"","sources":["../../../../../src/experimental/constructs/iam/policies/developer-policy.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IAspect } from \"aws-cdk-lib\";\nimport { Annotations, Aspects } from \"aws-cdk-lib\";\nimport { Effect, type PolicyDocument, type PolicyStatement } from \"aws-cdk-lib/aws-iam\";\nimport { CfnManagedPolicy, ManagedPolicy } from \"aws-cdk-lib/aws-iam\";\nimport type { IConstruct } from \"constructs\";\nimport type { GuStack } from \"../../../../constructs/core\";\n\nexport type GuDeveloperPolicyExperimentalProps = {\n /**\n * IAM policy statements to include in the developer policy.\n *\n * At least one statement is required.\n */\n readonly statements: [PolicyStatement, ...PolicyStatement[]];\n /**\n * Unique identifier of the developer policy grant.\n * This must match the grant ID used in Janus DeveloperPolicyGrant!\n */\n readonly grantId: string;\n /**\n * A brief description to help choose the correct developer policy in Janus or elsewhere.\n *\n * In AWS this will appear as the managed policy description.\n */\n readonly friendlyName: string;\n /**\n * An optional marker which suppresses the check and warnings on overbroad permissions\n */\n readonly withoutPolicyChecks?: boolean;\n};\n\n/**\n * Creates a structured `AWS::IAM::ManagedPolicy` resource to manage arbitrary permissions on general account\n * resources which can then be used to create limited permission credentials for use with specific activities.\n *\n * The permission scope is not controlled. This class should be used with care to create minimal permissions.\n * To that end, broad ALLOW permissions can be pruned with narrower optional DENY permissions.\n *\n * `permission` is prefixed with `/developer-policy/` and postfixed with `/` to construct the path. This will\n * be used for discovery and display. It is restricted to the same character set as AWS `path`.\n *\n * `description` is optionally used to construct the AWS Managed Policy description and used for display.\n *\n * ```yaml\n * TestingECAE2E87:\n * Type: AWS::IAM::ManagedPolicy\n * Properties:\n * Description: This is testing stuff\n * Path: /developer-policy/read-from-mybucket-under-mypath/\n * PolicyDocument:\n * Statement:\n * - Action: s3:GetObject\n * Effect: Allow\n * Resource: arn:aws:s3:::mybucket/mypath\n * - Action: s3:GetObject\n * Effect: Deny\n * Resource: arn:aws:s3:::mybucket/mypath/butnotthispath\n * Version: \"2012-10-17\"\n * Metadata:\n * aws:cdk:path: janus-resources-for-testing-managed-policy-tagging/justin-testing/Resource* ```\n * ```\n *\n * @experimental\n */\nexport class GuDeveloperPolicyExperimental extends ManagedPolicy {\n constructor(scope: GuStack, id: string, props: GuDeveloperPolicyExperimentalProps) {\n super(scope, id, {\n ...props,\n // Bear in mind that path has max length 512 chars\n path: `/developer-policy/${scope.repositoryName}/${scope.stack}/${scope.stage}/${props.grantId}/`,\n description: props.friendlyName,\n });\n\n Aspects.of(this).add(new GuDeveloperPolicyExperimentalRequiredChecker());\n\n if (!props.withoutPolicyChecks) {\n // Later, apply to the stack and check for specific errors\n Aspects.of(this).add(new GuDeveloperPolicyExperimentalOptionalChecker());\n }\n }\n}\n\n// Add checks here that we require and don't allow users to opt out of.\nclass GuDeveloperPolicyExperimentalRequiredChecker implements IAspect {\n public visit(node: IConstruct): void {\n if (node instanceof CfnManagedPolicy) {\n const description = node.description;\n const maxLength = 60;\n\n if (!description?.length) {\n Annotations.of(node).addError(\"friendlyName must be filled in\");\n } else if (description.length > maxLength) {\n Annotations.of(node).addError(\n `friendlyName must be no more than ${maxLength} characters long, but was ${description.length} characters`,\n );\n }\n }\n }\n}\n\n// Add checks here that we recommend for most scenarios but don't require.\nclass GuDeveloperPolicyExperimentalOptionalChecker implements IAspect {\n public visit(node: IConstruct): void {\n if (node instanceof CfnManagedPolicy) {\n const policyDocumentJson: unknown = (node.policyDocument as PolicyDocument).toJSON();\n\n // These conditions will result in failures from the AWS classes themselves, so we don't need to validate them.\n if (policyDocumentJson === null || typeof policyDocumentJson !== \"object\") {\n return;\n }\n if (!(\"Statement\" in policyDocumentJson)) {\n return;\n }\n if (!Array.isArray(policyDocumentJson.Statement)) {\n return;\n }\n if (policyDocumentJson.Statement.length === 0) {\n return;\n }\n\n // For the following conditions, we want to make a strong assertions: you cannot \"Allow\" with wildcards.\n for (const statement of policyDocumentJson.Statement) {\n if (!(\"Effect\" in statement) || (statement as { Effect: unknown }).Effect === Effect.ALLOW) {\n if (!(\"Action\" in statement)) {\n Annotations.of(node).addError(\"Statement is missing an Action\");\n } else {\n const action = (statement as { Action: unknown }).Action;\n\n if (typeof action === \"string\") {\n this.checkAction(action, node);\n } else if (Array.isArray(action)) {\n const actionArray = action as string[];\n actionArray.forEach((action) => this.checkAction(action, node));\n }\n }\n\n if (!(\"Resource\" in statement)) {\n // Typescript requires us to check this, because the data model doesn't guarantee it\n Annotations.of(node).addError(\"Statement is missing a Resource\");\n } else {\n const resource = (statement as { Resource: unknown }).Resource;\n\n if (typeof resource === \"string\") {\n this.checkResource(resource, node);\n } else if (Array.isArray(resource)) {\n const resourceArray = resource as string[];\n resourceArray.forEach((resource) => {\n this.checkResource(resource, node);\n });\n }\n }\n }\n }\n }\n }\n\n /**\n * Ensure that we don't have either actions or resources of the following forms:\n *\n * *\n * s3:*\n * arn:aws:dynamodb:us-east-2:account-ID-without-hyphens:table/*\n * arn:aws:s3:::*\n *\n * @param checkString Action or resource value to validate.\n * @param checkType Label used in validation error messages (for example, Action or Resource).\n * @param node Policy node used to attach validation annotations.\n * @private\n */\n private check(checkString: string, checkType: string, node: CfnManagedPolicy) {\n if (checkString === \"*\" || checkString.includes(\":*\") || checkString.includes(\"/*\")) {\n Annotations.of(node).addError(\n `Statement ${checkType} is too broad: ${checkString}. If this is necessary and intended, use withoutPolicyChecks: true in properties to turn off this check`,\n );\n }\n }\n private checkAction(checkString: string, node: CfnManagedPolicy) {\n this.check(checkString, \"Action\", node);\n }\n private checkResource(checkString: string, node: CfnManagedPolicy) {\n this.check(checkString, \"Resource\", node);\n }\n}\n"]}
1
+ {"version":3,"file":"developer-policy.js","sourceRoot":"","sources":["../../../../../src/experimental/constructs/iam/policies/developer-policy.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IAspect } from \"aws-cdk-lib\";\nimport { Annotations, Aspects } from \"aws-cdk-lib\";\nimport { Effect, type PolicyDocument, type PolicyStatement } from \"aws-cdk-lib/aws-iam\";\nimport { CfnManagedPolicy, ManagedPolicy } from \"aws-cdk-lib/aws-iam\";\nimport type { IConstruct } from \"constructs\";\nimport type { GuStack } from \"../../../../constructs/core\";\n\nexport type GuDeveloperPolicyExperimentalProps = {\n /**\n * IAM policy statements to include in the developer policy.\n *\n * At least one statement is required.\n */\n readonly statements: [PolicyStatement, ...PolicyStatement[]];\n /**\n * Unique identifier of the developer policy grant.\n * This must match the grant ID used in Janus DeveloperPolicyGrant!\n */\n readonly grantId: string;\n /**\n * A brief description to help choose the correct developer policy in Janus or elsewhere.\n *\n * In AWS this will appear as the managed policy description.\n */\n readonly friendlyName: string;\n /**\n * An optional marker which suppresses the check and warnings on overbroad permissions\n */\n readonly withoutPolicyChecks?: boolean;\n};\n\n/**\n * Creates a structured `AWS::IAM::ManagedPolicy` resource to manage arbitrary permissions on general account\n * resources which can then be used to create limited permission credentials for use with specific activities.\n *\n * The permission scope is not controlled. This class should be used with care to create minimal permissions.\n * To that end, broad ALLOW permissions can be pruned with narrower optional DENY permissions.\n *\n * `permission` is prefixed with `/developer-policy/` and postfixed with `/` to construct the path. This will\n * be used for discovery and display. It is restricted to the same character set as AWS `path`.\n *\n * `description` is optionally used to construct the AWS Managed Policy description and used for display.\n *\n * ```yaml\n * TestingECAE2E87:\n * Type: AWS::IAM::ManagedPolicy\n * Properties:\n * Description: This is testing stuff\n * Path: /developer-policy/read-from-mybucket-under-mypath/\n * PolicyDocument:\n * Statement:\n * - Action: s3:GetObject\n * Effect: Allow\n * Resource: arn:aws:s3:::mybucket/mypath\n * - Action: s3:GetObject\n * Effect: Deny\n * Resource: arn:aws:s3:::mybucket/mypath/butnotthispath\n * Version: \"2012-10-17\"\n * Metadata:\n * aws:cdk:path: janus-resources-for-testing-managed-policy-tagging/justin-testing/Resource* ```\n * ```\n *\n * @experimental\n */\nexport class GuDeveloperPolicyExperimental extends ManagedPolicy {\n constructor(scope: GuStack, id: string, props: GuDeveloperPolicyExperimentalProps) {\n super(scope, id, {\n ...props,\n // Bear in mind that path has max length 512 chars\n path: `/developer-policy/${scope.repositoryName}/${scope.stack}/${scope.stage}/${props.grantId}/`,\n description: props.friendlyName,\n });\n\n Aspects.of(this).add(new GuDeveloperPolicyExperimentalRequiredChecker());\n\n if (!props.withoutPolicyChecks) {\n // Later, apply to the stack and check for specific errors\n Aspects.of(this).add(new GuDeveloperPolicyExperimentalOptionalChecker());\n }\n }\n}\n\n// Add checks here that we require and don't allow users to opt out of.\nclass GuDeveloperPolicyExperimentalRequiredChecker implements IAspect {\n public visit(node: IConstruct): void {\n if (node instanceof CfnManagedPolicy) {\n const description = node.description;\n const maxLength = 60;\n\n if (!description?.length) {\n Annotations.of(node).addError(\"friendlyName must be filled in\");\n } else if (description.length > maxLength) {\n Annotations.of(node).addError(\n `friendlyName must be no more than ${maxLength} characters long, but was ${description.length} characters`,\n );\n }\n }\n }\n}\n\n// Add checks here that we recommend for most scenarios but don't require.\nclass GuDeveloperPolicyExperimentalOptionalChecker implements IAspect {\n public visit(node: IConstruct): void {\n if (node instanceof CfnManagedPolicy) {\n const policyDocumentJson: unknown = (node.policyDocument as PolicyDocument).toJSON();\n\n // These conditions will result in failures from the AWS classes themselves, so we don't need to validate them.\n if (policyDocumentJson === null || typeof policyDocumentJson !== \"object\") {\n return;\n }\n if (!(\"Statement\" in policyDocumentJson)) {\n return;\n }\n if (!Array.isArray(policyDocumentJson.Statement)) {\n return;\n }\n if (policyDocumentJson.Statement.length === 0) {\n return;\n }\n\n // For the following conditions, we want to make a strong assertions: you cannot \"Allow\" with wildcards.\n for (const statement of policyDocumentJson.Statement) {\n if (!(\"Effect\" in statement) || (statement as { Effect: unknown }).Effect === Effect.ALLOW) {\n if (!(\"Action\" in statement)) {\n Annotations.of(node).addError(\"Statement is missing an Action\");\n } else {\n const action = (statement as { Action: unknown }).Action;\n\n if (typeof action === \"string\") {\n this.checkAction(action, node);\n } else if (Array.isArray(action)) {\n const actionArray = action as string[];\n actionArray.forEach((action) => this.checkAction(action, node));\n }\n }\n\n if (!(\"Resource\" in statement)) {\n // Typescript requires us to check this, because the data model doesn't guarantee it\n Annotations.of(node).addError(\"Statement is missing a Resource\");\n } else {\n const resource = (statement as { Resource: unknown }).Resource;\n\n if (typeof resource === \"string\") {\n this.checkResource(resource, node);\n } else if (Array.isArray(resource)) {\n const resourceArray = resource as string[];\n resourceArray.forEach((resource) => {\n this.checkResource(resource, node);\n });\n }\n }\n }\n }\n }\n }\n\n /**\n * Ensure that we don't have either actions or resources of the following forms:\n *\n * *\n * s3:*\n * arn:aws:dynamodb:us-east-2:account-ID-without-hyphens:table/*\n * arn:aws:s3:::*\n *\n * @param checkString Action or resource value to validate.\n * @param checkType Label used in validation error messages (for example, Action or Resource).\n * @param node Policy node used to attach validation annotations.\n * @private\n */\n private check(checkString: string, checkType: string, node: CfnManagedPolicy) {\n if (checkString === \"*\" || checkString.includes(\":*\") || checkString.includes(\"/*\")) {\n Annotations.of(node).addError(\n `Statement ${checkType} is too broad: ${checkString}. If this is necessary and intended, use withoutPolicyChecks: true in properties to turn off this check`,\n );\n }\n }\n private checkAction(checkString: string, node: CfnManagedPolicy) {\n this.check(checkString, \"Action\", node);\n }\n private checkResource(checkString: string, node: CfnManagedPolicy) {\n this.check(checkString, \"Resource\", node);\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"riff-raff-deployment-id.js","sourceRoot":"","sources":["../../../src/experimental/constructs/riff-raff-deployment-id.ts"],"names":[],"mappings":";;;AAAA,6CAA2C;AAE3C,qDAAkE;AAElE,MAAa,2CAA4C,SAAQ,0BAAY;IAG3E,YAAoB,KAAc;QAChC,KAAK,CAAC,KAAK,EAAE,sBAAsB,EAAE;YACnC,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,gDAAgD;SAC9D,CAAC,CAAC;IACL,CAAC;IAEM,MAAM,CAAC,WAAW,CAAC,KAAc;QACtC,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAA,qCAAyB,EAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE;YACtE,IAAI,CAAC,QAAQ,GAAG,IAAI,2CAA2C,CAAC,KAAK,CAAC,CAAC;SACxE;QAED,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;CACF;AAjBD,kGAiBC","sourcesContent":["import { CfnParameter } from \"aws-cdk-lib\";\nimport type { GuStack } from \"../../constructs/core\";\nimport { isSingletonPresentInStack } from \"../../utils/singleton\";\n\nexport class GuRiffRaffDeploymentIdParameterExperimental extends CfnParameter {\n private static instance: GuRiffRaffDeploymentIdParameterExperimental | undefined;\n\n private constructor(scope: GuStack) {\n super(scope, \"RiffRaffDeploymentId\", {\n type: \"String\",\n description: \"Used by Riff-Raff to inject the deployment ID.\",\n });\n }\n\n public static getInstance(stack: GuStack): GuRiffRaffDeploymentIdParameterExperimental {\n if (!this.instance || !isSingletonPresentInStack(stack, this.instance)) {\n this.instance = new GuRiffRaffDeploymentIdParameterExperimental(stack);\n }\n\n return this.instance;\n }\n}\n"]}
1
+ {"version":3,"file":"riff-raff-deployment-id.js","sourceRoot":"","sources":["../../../src/experimental/constructs/riff-raff-deployment-id.ts"],"names":[],"mappings":";;;AAAA,6CAA2C;AAE3C,qDAAkE;AAElE,MAAa,2CAA4C,SAAQ,0BAAY;IAG3E,YAAoB,KAAc;QAChC,KAAK,CAAC,KAAK,EAAE,sBAAsB,EAAE;YACnC,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,gDAAgD;SAC9D,CAAC,CAAC;IACL,CAAC;IAEM,MAAM,CAAC,WAAW,CAAC,KAAc;QACtC,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAA,qCAAyB,EAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACvE,IAAI,CAAC,QAAQ,GAAG,IAAI,2CAA2C,CAAC,KAAK,CAAC,CAAC;QACzE,CAAC;QAED,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;CACF;AAjBD,kGAiBC","sourcesContent":["import { CfnParameter } from \"aws-cdk-lib\";\nimport type { GuStack } from \"../../constructs/core\";\nimport { isSingletonPresentInStack } from \"../../utils/singleton\";\n\nexport class GuRiffRaffDeploymentIdParameterExperimental extends CfnParameter {\n private static instance: GuRiffRaffDeploymentIdParameterExperimental | undefined;\n\n private constructor(scope: GuStack) {\n super(scope, \"RiffRaffDeploymentId\", {\n type: \"String\",\n description: \"Used by Riff-Raff to inject the deployment ID.\",\n });\n }\n\n public static getInstance(stack: GuStack): GuRiffRaffDeploymentIdParameterExperimental {\n if (!this.instance || !isSingletonPresentInStack(stack, this.instance)) {\n this.instance = new GuRiffRaffDeploymentIdParameterExperimental(stack);\n }\n\n return this.instance;\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"ec2-app.js","sourceRoot":"","sources":["../../../src/experimental/patterns/ec2-app.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IAspect } from \"aws-cdk-lib\";\nimport { Aspects, CfnParameter, Duration, Tags } from \"aws-cdk-lib\";\nimport { CfnAutoScalingGroup, CfnScalingPolicy, ScalingProcess, UpdatePolicy } from \"aws-cdk-lib/aws-autoscaling\";\nimport type { CfnPolicy } from \"aws-cdk-lib/aws-iam\";\nimport { Effect, Policy, PolicyStatement } from \"aws-cdk-lib/aws-iam\";\nimport type { IConstruct } from \"constructs\";\nimport { MetadataKeys } from \"../../constants\";\nimport { GuAutoScalingGroup } from \"../../constructs/autoscaling\";\nimport { GuStack } from \"../../constructs/core\";\nimport type { GuApplicationTargetGroup } from \"../../constructs/loadbalancing\";\nimport type { GuEc2AppProps } from \"../../patterns\";\nimport { GuEc2App } from \"../../patterns\";\nimport { isSingletonPresentInStack } from \"../../utils/singleton\";\nimport { GuRiffRaffDeploymentIdParameterExperimental } from \"../constructs/riff-raff-deployment-id\";\n\ninterface AutoScalingRollingUpdateDurations {\n /**\n * The time between each check of an instance's health within the target group.\n */\n sleep: Duration;\n\n /**\n * Additional time to cover the time spent polling an instance's health within the target group before sending the CloudFormation signal.\n */\n buffer: Duration;\n}\n\nexport const RollingUpdateDurations: AutoScalingRollingUpdateDurations = {\n sleep: Duration.seconds(5),\n buffer: Duration.minutes(1),\n};\n\n/**\n * Ensures the `AutoScalingRollingUpdate` of an AutoScaling Group has a `PauseTime` matching the healthcheck grace period.\n * It also ensures the `CreationPolicy` resource signal `Timeout` matches the healthcheck grace period.\n *\n * @note\n * Be sure to instantiate this only once for an entire {@link GuStack}, else the properties of the ASG will be incorrectly updated multiple times.\n *\n * @internal\n *\n * @privateRemarks\n * The ASG healthcheck grace period is hard-coded by {@link GuEc2App}.\n * Customisation of this value is performed via an escape hatch.\n * An `Aspect` is the only way to observe any customisation.\n *\n * TODO Expose the healthcheck grace period as a property on {@link GuEc2App} and remove this `Aspect`.\n */\nexport class GuAutoScalingRollingUpdateTimeoutExperimental implements IAspect {\n public visit(construct: IConstruct) {\n if (construct instanceof CfnAutoScalingGroup) {\n const currentRollingUpdate = construct.cfnOptions.updatePolicy?.autoScalingRollingUpdate;\n const currentCreationPolicy = construct.cfnOptions.creationPolicy;\n\n if (!construct.healthCheckGracePeriod) {\n throw new Error(`The healthcheck grace period not set for autoscaling group ${construct.node.id}.`);\n }\n\n const signalTimeoutSeconds = construct.healthCheckGracePeriod + RollingUpdateDurations.buffer.toSeconds();\n\n if (currentRollingUpdate) {\n construct.cfnOptions.updatePolicy = {\n autoScalingRollingUpdate: {\n ...currentRollingUpdate,\n pauseTime: Duration.seconds(\n Duration.parse(currentRollingUpdate.pauseTime ?? \"0\").toSeconds() + signalTimeoutSeconds,\n ).toIsoString(),\n },\n };\n }\n\n if (currentCreationPolicy) {\n construct.cfnOptions.creationPolicy = {\n ...currentCreationPolicy,\n resourceSignal: {\n ...currentCreationPolicy.resourceSignal,\n timeout: Duration.seconds(signalTimeoutSeconds).toIsoString(),\n },\n };\n }\n }\n }\n}\n\n/**\n * An `Aspect` that adjusts the properties of an AutoScaling Group using an `AutoScalingRollingUpdate` update policy.\n *\n * It'll unset the `DesiredCapacity` of the ASG as a rolling update sets `Desired` back to the template version,\n * undoing any changes that a scale-out event may have done.\n * Having `DesiredCapacity` unset ensures the service remains at-capacity at all times.\n *\n * It'll also make the `MinInstancesInService` property dynamic via a CFN Parameter that Riff-Raff will set.\n * The value depends on the current capacity of the ASG:\n * - If the service is running normally, it'll be set to the `MinSize` capacity\n * - If the service is partially scaled, it'll be set to the current `DesiredCapacity`\n * - If the service is fully scaled, it'll be set to (at least) `MaxSize` - 1\n *\n * @privateRemarks\n * - Once out of experimental, instantiate this `Aspect` directly in {@link GuStack}.\n *\n * @see https://github.com/guardian/testing-asg-rolling-update\n */\nexport class GuHorizontallyScalingDeploymentPropertiesExperimental implements IAspect {\n public visit(construct: IConstruct) {\n if (CfnScalingPolicy.isCfnScalingPolicy(construct)) {\n const { node } = construct;\n const { scopes, path } = node;\n\n /*\n Requiring a `CfnScalingPolicy` to be created in the scope of a `GuAutoScalingGroup`\n is the most reliable way to associate the two together.\n\n Though the `autoScalingGroupName` property is passed to a `CfnScalingPolicy` when instantiating,\n this does not create a concrete link as AWS CDK sets the value to be the ASG's `Ref`.\n That is, it is a `Token` until it is synthesised.\n This is even if the ASG has an explicit name set.\n\n See also:\n - https://docs.aws.amazon.com/cdk/v2/guide/tokens.html\n - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-autoscaling-autoscalinggroup.html#aws-resource-autoscaling-autoscalinggroup-return-values\n - https://github.com/aws/aws-cdk/blob/f6b649d47f8bc30ca741fbb7a4852d51e8275002/packages/aws-cdk-lib/aws-autoscaling/lib/auto-scaling-group.ts#L1560\n */\n const autoScalingGroup = scopes.find((_): _ is GuAutoScalingGroup => _ instanceof GuAutoScalingGroup);\n\n if (!autoScalingGroup) {\n throw new Error(\n `Failed to detect the autoscaling group relating to the scaling policy on path ${path}. Was it created in the scope of a GuAutoScalingGroup?`,\n );\n }\n\n const cfnAutoScalingGroup = autoScalingGroup.node.defaultChild as CfnAutoScalingGroup;\n const currentRollingUpdate = cfnAutoScalingGroup.cfnOptions.updatePolicy?.autoScalingRollingUpdate;\n\n if (currentRollingUpdate) {\n /*\n An autoscaling group that horizontally scales should not explicitly set `Desired`,\n as a rolling update will set the current `Desired` back to the template version,\n undoing any changes that a scale-out event may have done.\n */\n cfnAutoScalingGroup.desiredCapacity = undefined;\n\n const minInstancesInService = GuAsgMinInstancesInServiceParameterExperimental.getInstance(autoScalingGroup);\n\n cfnAutoScalingGroup.cfnOptions.updatePolicy = {\n autoScalingRollingUpdate: {\n ...currentRollingUpdate,\n minInstancesInService: minInstancesInService.valueAsNumber,\n },\n };\n }\n }\n }\n}\n\n/**\n * A CloudFormation parameter used to make the `MinInstancesInService` property of an ASG dynamic during deployment with Riff-Raff.\n * This shouldn't be instantiated directly, but via {@link GuHorizontallyScalingDeploymentPropertiesExperimental}.\n */\nexport class GuAsgMinInstancesInServiceParameterExperimental extends CfnParameter {\n public readonly asg: GuAutoScalingGroup;\n\n private constructor(scope: GuStack, id: string, autoScalingGroup: GuAutoScalingGroup) {\n super(scope, id, { type: \"Number\" });\n this.asg = autoScalingGroup;\n }\n\n public static getInstance(autoScalingGroup: GuAutoScalingGroup): GuAsgMinInstancesInServiceParameterExperimental {\n const stack = GuStack.of(autoScalingGroup) as GuStack;\n const { app } = autoScalingGroup;\n const id = `MinInstancesInServiceFor${app.replaceAll(\"-\", \"\")}`;\n\n const maybeParameter = stack.parameters[id];\n\n if (!maybeParameter) {\n return new GuAsgMinInstancesInServiceParameterExperimental(stack, id, autoScalingGroup);\n }\n\n // This shouldn't happen\n if (!(maybeParameter instanceof GuAsgMinInstancesInServiceParameterExperimental)) {\n throw new Error(\n `CFN parameter ${id} exists but is of type ${maybeParameter.constructor.name} instead of ${GuAsgMinInstancesInServiceParameterExperimental.name}.`,\n );\n }\n\n return maybeParameter;\n }\n}\n\n/**\n * An IAM Policy allowing the sending of a CloudFormation signal.\n *\n * @privateRemarks\n * Implemented as a singleton as the resources can only be tightened at most to the CloudFormation stack.\n */\nclass AsgRollingUpdatePolicy extends Policy {\n private static instance: AsgRollingUpdatePolicy | undefined;\n\n private constructor(scope: GuStack) {\n const { stackId } = scope;\n\n super(scope, \"AsgRollingUpdatePolicy\", {\n statements: [\n // Allow usage of command `cfn-signal`.\n new PolicyStatement({\n actions: [\"cloudformation:SignalResource\"],\n effect: Effect.ALLOW,\n resources: [stackId],\n }),\n\n /*\n Allow usage of command `aws elbv2 describe-target-health`.\n AWS Elastic Load Balancing does not support resource based policies, so the resource has to be `*` (any) here.\n See https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html\n */\n new PolicyStatement({\n actions: [\"elasticloadbalancing:DescribeTargetHealth\"],\n effect: Effect.ALLOW,\n resources: [\"*\"],\n }),\n ],\n });\n }\n\n public static getInstance(stack: GuStack): AsgRollingUpdatePolicy {\n if (!this.instance || !isSingletonPresentInStack(stack, this.instance)) {\n this.instance = new AsgRollingUpdatePolicy(stack);\n }\n\n return this.instance;\n }\n}\n\nexport interface GuEc2AppExperimentalProps extends Omit<GuEc2AppProps, \"updatePolicy\"> {\n /**\n * Which application build to run.\n * This will typically match the build number provided by CI.\n *\n * @example\n * process.env.GITHUB_RUN_NUMBER\n */\n buildIdentifier: string;\n /**\n * The amount of time that newly launched instances will spend warming-up / serving fewer requests than other instances.\n * The range is 30-900 seconds (15 minutes). The default is 0 seconds (disabled).\n *\n * See https://docs.aws.amazon.com/elasticloadbalancing/latest/application/edit-target-group-attributes.html#slow-start-mode\n * for more details.\n *\n * We recommend enabling this setting if you run a high-traffic service, particularly if it is JVM-based.\n *\n * Note that there is a trade-off between reliability and speed here; a longer duration will give new instances\n * more time to warm-up, but it will also slow down deployments and scale-up events.\n */\n slowStartDuration?: Duration;\n}\n\n/**\n * Configures the rolling update policy for the autoscaling group.\n *\n * Most applications should not need to instantiate this class directly as this policy is set by GuEc2AppExperimental.\n *\n * If you need to use this class for a service other than MAPI, please speak to DevX about your use-case first.\n */\nexport class GuRollingUpdatePolicyExperimental {\n static getPolicy(\n maximumInstances: number,\n minimumInstances?: number,\n slowStartDuration: Duration = Duration.seconds(0),\n ) {\n return UpdatePolicy.rollingUpdate({\n maxBatchSize: maximumInstances,\n minInstancesInService: minimumInstances,\n minSuccessPercentage: 100,\n waitOnResourceSignals: true,\n /*\n All of these processes can launch and/or terminate instances and if this happens during a deployment it might\n cause the rolling update to fail: https://repost.aws/knowledge-center/auto-scaling-group-rolling-updates\n The full list of processes can be found here: https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-suspend-resume-processes.html#process-types\n */\n suspendProcesses: [\n ScalingProcess.ALARM_NOTIFICATION,\n ScalingProcess.AZ_REBALANCE,\n ScalingProcess.HEALTH_CHECK,\n ScalingProcess.INSTANCE_REFRESH,\n ScalingProcess.REPLACE_UNHEALTHY,\n ScalingProcess.SCHEDULED_ACTIONS,\n ],\n /*\n Note: this is increased via an Aspect which also takes healthcheck grace period into account.\n\n It was easier to pass the slow start duration through here rather than trying to do this via the\n GuAutoScalingRollingUpdateTimeoutExperimental aspect.\n\n If we do this via the aspect then we need to find all mappings between ASGs and target groups and then\n get the relevant slow start duration for each. This is probably possible but would be reasonably complex.\n However, it's worth mentioning that by taking this approach we open up a small risk that things will not work\n as expected if users enable slow start via a different mechanism (e.g. by modifying the target group outside\n the pattern).\n */\n pauseTime: slowStartDuration,\n });\n }\n}\n\nexport interface GuRoleForRollingUpdateExperimentalProps {\n autoScalingGroup: GuAutoScalingGroup;\n}\n\n/**\n * Grants the role associated with your autoscaling group the necessary permissions for the rolling update deployment\n * mechanism.\n *\n * Most applications should not need to instantiate this class directly as these permission changes are granted\n * by GuEc2AppExperimental.\n *\n * If you need to use this class for a service other than MAPI, please speak to DevX about your use-case first.\n */\nexport class GuRoleForRollingUpdateExperimental {\n constructor(scope: GuStack, props: GuRoleForRollingUpdateExperimentalProps) {\n const policy = AsgRollingUpdatePolicy.getInstance(scope);\n policy.attachToRole(props.autoScalingGroup.role);\n\n // Create the Policy with necessary permissions first.\n // Then create the ASG that requires the permissions.\n const cfnPolicy = policy.node.defaultChild as CfnPolicy;\n const cfnAutoScalingGroup = props.autoScalingGroup.node.defaultChild as CfnAutoScalingGroup;\n cfnAutoScalingGroup.addDependency(cfnPolicy);\n }\n}\n\nexport interface GuUserDataForRollingUpdateExperimentalProps {\n autoScalingGroup: GuAutoScalingGroup;\n targetGroup: GuApplicationTargetGroup;\n applicationPort: number;\n buildIdentifier: string;\n slowStartDuration?: Duration;\n}\n\n/**\n * Modifies the user-data script (which runs whenever an instance is launched) to make it compatible with the new\n * rolling update deployment mechanism.\n *\n * Most applications should not need to instantiate this class directly as these user-data changes are already made\n * by GuEc2AppExperimental.\n *\n * If you need to use this class for a service other than MAPI, please speak to DevX about your use-case first.\n */\nexport class GuUserDataForRollingUpdateExperimental {\n constructor(scope: GuStack, props: GuUserDataForRollingUpdateExperimentalProps) {\n const { region, stackId } = scope;\n const { autoScalingGroup, targetGroup, applicationPort, buildIdentifier, slowStartDuration } = props;\n const cfnAutoScalingGroup = autoScalingGroup.node.defaultChild as CfnAutoScalingGroup;\n\n const deploymentId = GuRiffRaffDeploymentIdParameterExperimental.getInstance(scope);\n\n // This allows redeployment of the same build to cycle instances\n autoScalingGroup.userData.addCommands(\n `echo \"Launch template last updated by Riff-Raff deployment ID: ${deploymentId.valueAsString}\"`,\n );\n\n /*\n `aws` is available via AMIgo baked AMIs.\n See https://github.com/guardian/amigo/tree/main/roles/aws-tools.\n */\n autoScalingGroup.userData.addCommands(\n `# ${GuEc2AppExperimental.name} Instance Health Check Start`,\n `\n TOKEN=$(curl -X PUT \"http://169.254.169.254/latest/api/token\" -H \"X-aws-ec2-metadata-token-ttl-seconds: 21600\")\n INSTANCE_ID=$(curl -H \"X-aws-ec2-metadata-token: $TOKEN\" \"http://169.254.169.254/latest/meta-data/instance-id\")\n\n STATE=$(aws elbv2 describe-target-health \\\n --target-group-arn ${targetGroup.targetGroupArn} \\\n --region ${region} \\\n --targets Id=$INSTANCE_ID,Port=${applicationPort} \\\n --query \"TargetHealthDescriptions[0].TargetHealth.State\")\n\n until [ \"$STATE\" == \"\\\\\"healthy\\\\\"\" ]; do\n echo \"Instance running build ${buildIdentifier} not yet healthy within target group. Current state $STATE. Sleeping...\"\n sleep ${RollingUpdateDurations.sleep.toSeconds()}\n STATE=$(aws elbv2 describe-target-health \\\n --target-group-arn ${targetGroup.targetGroupArn} \\\n --region ${region} \\\n --targets Id=$INSTANCE_ID,Port=${applicationPort} \\\n --query \"TargetHealthDescriptions[0].TargetHealth.State\")\n done\n\n echo \"Instance running build ${buildIdentifier} is healthy in target group.\"\n `,\n `# ${GuEc2AppExperimental.name} Instance Health Check End`,\n );\n\n if (slowStartDuration) {\n const slowStartInSeconds = slowStartDuration.toSeconds().toString();\n targetGroup.setAttribute(\"slow_start.duration_seconds\", slowStartInSeconds);\n autoScalingGroup.userData.addCommands(\n `# ${GuEc2AppExperimental.name} SlowStart Wait Period Start`,\n `\n echo \"Sleeping for ${slowStartInSeconds} seconds while instance running build ${buildIdentifier} warms up...\"\n sleep ${slowStartInSeconds}s\n echo \"Instance running build ${buildIdentifier} should have warmed up by now...\"\n\n STATE=$(aws elbv2 describe-target-health \\\n --target-group-arn ${targetGroup.targetGroupArn} \\\n --region ${region} \\\n --targets Id=$INSTANCE_ID,Port=${applicationPort} \\\n --query \"TargetHealthDescriptions[0].TargetHealth.State\")\n\n if [ \"$STATE\" == \"\\\\\"healthy\\\\\"\" ]; then\n echo \"Instance running build ${buildIdentifier} is ready to start serving a normal percentage of requests\"\n else\n echo \"Instance running build ${buildIdentifier} was not healthy after warm-up period; a failure signal will be sent\"\n exit 1\n fi\n `,\n `# ${GuEc2AppExperimental.name} SlowStart Wait Period End`,\n );\n }\n\n autoScalingGroup.userData.addOnExitCommands(\n `\n cfn-signal --stack ${stackId} \\\n --resource ${cfnAutoScalingGroup.logicalId} \\\n --region ${region} \\\n --exit-code $exitCode || echo 'Failed to send Cloudformation Signal'\n `,\n );\n\n // If https://github.com/guardian/devx-logs is used, this tag will be added as a marker to logs in Central ELK.\n Tags.of(autoScalingGroup.instanceLaunchTemplate).add(MetadataKeys.BUILD_IDENTIFIER, buildIdentifier, {\n applyToLaunchedInstances: true,\n });\n }\n}\n\n/**\n * An experimental pattern to instantiate an EC2 application that is updated entirely via CloudFormation.\n * It sets the update policy of the `AWS::AutoScaling::AutoScalingGroup` to `AutoScalingRollingUpdate`.\n * When a CloudFormation update is applied, the current instances in the ASG will be replaced.\n *\n * This pattern also updates the User Data, running some commands AFTER yours.\n * These changes are wrapped in start and end marking comments.\n *\n * This pattern does not enable for scaling events to trigger _during_ a deployment\n * as during testing it was found that scale-in events during deployment led to unpredictable service availability.\n *\n * If a deployment fails, for example when the healthcheck fails, CloudFormation will automatically rollback the changes.\n * This improves on the behaviour of Riff-Raff's `autoscaling` deployment which requires manual intervention on deployment failure.\n *\n * To update the application's code, a CloudFormation update must be triggered.\n * The best way to do this is to include the build number in the application artifact.\n * TODO test User Data includes a build number.\n *\n * @remarks\n * - The rate at which instances are replaced are conditional based on how scaled up the service is.\n * The more an application is scaled, the longer it'll take to deploy.\n * - It is not necessary to use the `autoscaling` Riff-Raff deployment type in conjunction with this.\n * Doing so won't break anything, however it'll lengthen the deployment time as instances will be rotated twice.\n * Please remove it from any manually created `riff-raff.yaml` file.\n * - Your application should include a build number in its filename.\n * This value will change across builds, and therefore create a CloudFormation template difference to be deployed.\n * - Requires the AWS CLI and `cfn-signal` binaries to be available on the instance, and on the `PATH`.\n * AMIgo adds these via the `aws-tools` role.\n *\n * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-updatepolicy.html#cfn-attributes-updatepolicy-rollingupdate\n * @see https://github.com/guardian/testing-asg-rolling-update\n *\n * @experimental\n */\nexport class GuEc2AppExperimental extends GuEc2App {\n constructor(scope: GuStack, props: GuEc2AppExperimentalProps) {\n const { minimumInstances, maximumInstances = minimumInstances * 2 } = props.scaling;\n const { applicationPort, buildIdentifier, slowStartDuration } = props;\n\n const slowStartDurationInSeconds = slowStartDuration?.toSeconds();\n if (slowStartDurationInSeconds && (slowStartDurationInSeconds < 30 || slowStartDurationInSeconds > 900)) {\n throw new Error(\"Slow start duration must be between 30 and 900 seconds\");\n }\n\n super(scope, {\n ...props,\n // Note: if the service uses horizontal scaling, minimumInstances is overridden by an Aspect (to prevent accidental scale downs!)\n updatePolicy: GuRollingUpdatePolicyExperimental.getPolicy(maximumInstances, minimumInstances, slowStartDuration),\n });\n\n const { autoScalingGroup, targetGroup } = this;\n const cfnAutoScalingGroup = autoScalingGroup.node.defaultChild as CfnAutoScalingGroup;\n\n // Note: if the service uses horizontal scaling, this property is unset by an Aspect (to prevent accidental scale downs!)\n cfnAutoScalingGroup.desiredCapacity = minimumInstances.toString();\n\n // This is used when an ASG is first created; it is not needed to support the new deployment mechanism\n cfnAutoScalingGroup.cfnOptions.creationPolicy = {\n autoScalingCreationPolicy: {\n minSuccessfulInstancesPercent: 100,\n },\n resourceSignal: {\n count: minimumInstances,\n },\n };\n\n new GuRoleForRollingUpdateExperimental(scope, { autoScalingGroup });\n new GuUserDataForRollingUpdateExperimental(scope, {\n autoScalingGroup,\n targetGroup,\n applicationPort,\n buildIdentifier,\n slowStartDuration,\n });\n\n /*\n Instantiate the `GuAutoScalingRollingUpdateTimeoutExperimental` exactly once for a GuStack.\n Whilst multiple instances are syntactically valid, it will cause ASG nodes to be incorrectly evaluated/mutated multiple times.\n TODO Once out of experimental, instantiate these `Aspect`s directly in `GuStack`.\n */\n const allAspects = Aspects.of(scope).all;\n const maybeRollingUpdateTimeoutAspect = allAspects.find(\n (_) => _ instanceof GuAutoScalingRollingUpdateTimeoutExperimental,\n );\n if (!maybeRollingUpdateTimeoutAspect) {\n Aspects.of(scope).add(new GuAutoScalingRollingUpdateTimeoutExperimental());\n }\n\n Aspects.of(scope).add(new GuHorizontallyScalingDeploymentPropertiesExperimental());\n }\n}\n"]}
1
+ {"version":3,"file":"ec2-app.js","sourceRoot":"","sources":["../../../src/experimental/patterns/ec2-app.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IAspect } from \"aws-cdk-lib\";\nimport { Aspects, CfnParameter, Duration, Tags } from \"aws-cdk-lib\";\nimport { CfnAutoScalingGroup, CfnScalingPolicy, ScalingProcess, UpdatePolicy } from \"aws-cdk-lib/aws-autoscaling\";\nimport type { CfnPolicy } from \"aws-cdk-lib/aws-iam\";\nimport { Effect, Policy, PolicyStatement } from \"aws-cdk-lib/aws-iam\";\nimport type { IConstruct } from \"constructs\";\nimport { MetadataKeys } from \"../../constants\";\nimport { GuAutoScalingGroup } from \"../../constructs/autoscaling\";\nimport { GuStack } from \"../../constructs/core\";\nimport type { GuApplicationTargetGroup } from \"../../constructs/loadbalancing\";\nimport type { GuEc2AppProps } from \"../../patterns\";\nimport { GuEc2App } from \"../../patterns\";\nimport { isSingletonPresentInStack } from \"../../utils/singleton\";\nimport { GuRiffRaffDeploymentIdParameterExperimental } from \"../constructs/riff-raff-deployment-id\";\n\ninterface AutoScalingRollingUpdateDurations {\n /**\n * The time between each check of an instance's health within the target group.\n */\n sleep: Duration;\n\n /**\n * Additional time to cover the time spent polling an instance's health within the target group before sending the CloudFormation signal.\n */\n buffer: Duration;\n}\n\nexport const RollingUpdateDurations: AutoScalingRollingUpdateDurations = {\n sleep: Duration.seconds(5),\n buffer: Duration.minutes(1),\n};\n\n/**\n * Ensures the `AutoScalingRollingUpdate` of an AutoScaling Group has a `PauseTime` matching the healthcheck grace period.\n * It also ensures the `CreationPolicy` resource signal `Timeout` matches the healthcheck grace period.\n *\n * @note\n * Be sure to instantiate this only once for an entire {@link GuStack}, else the properties of the ASG will be incorrectly updated multiple times.\n *\n * @internal\n *\n * @privateRemarks\n * The ASG healthcheck grace period is hard-coded by {@link GuEc2App}.\n * Customisation of this value is performed via an escape hatch.\n * An `Aspect` is the only way to observe any customisation.\n *\n * TODO Expose the healthcheck grace period as a property on {@link GuEc2App} and remove this `Aspect`.\n */\nexport class GuAutoScalingRollingUpdateTimeoutExperimental implements IAspect {\n public visit(construct: IConstruct) {\n if (construct instanceof CfnAutoScalingGroup) {\n const currentRollingUpdate = construct.cfnOptions.updatePolicy?.autoScalingRollingUpdate;\n const currentCreationPolicy = construct.cfnOptions.creationPolicy;\n\n if (!construct.healthCheckGracePeriod) {\n throw new Error(`The healthcheck grace period not set for autoscaling group ${construct.node.id}.`);\n }\n\n const signalTimeoutSeconds = construct.healthCheckGracePeriod + RollingUpdateDurations.buffer.toSeconds();\n\n if (currentRollingUpdate) {\n construct.cfnOptions.updatePolicy = {\n autoScalingRollingUpdate: {\n ...currentRollingUpdate,\n pauseTime: Duration.seconds(\n Duration.parse(currentRollingUpdate.pauseTime ?? \"0\").toSeconds() + signalTimeoutSeconds,\n ).toIsoString(),\n },\n };\n }\n\n if (currentCreationPolicy) {\n construct.cfnOptions.creationPolicy = {\n ...currentCreationPolicy,\n resourceSignal: {\n ...currentCreationPolicy.resourceSignal,\n timeout: Duration.seconds(signalTimeoutSeconds).toIsoString(),\n },\n };\n }\n }\n }\n}\n\n/**\n * An `Aspect` that adjusts the properties of an AutoScaling Group using an `AutoScalingRollingUpdate` update policy.\n *\n * It'll unset the `DesiredCapacity` of the ASG as a rolling update sets `Desired` back to the template version,\n * undoing any changes that a scale-out event may have done.\n * Having `DesiredCapacity` unset ensures the service remains at-capacity at all times.\n *\n * It'll also make the `MinInstancesInService` property dynamic via a CFN Parameter that Riff-Raff will set.\n * The value depends on the current capacity of the ASG:\n * - If the service is running normally, it'll be set to the `MinSize` capacity\n * - If the service is partially scaled, it'll be set to the current `DesiredCapacity`\n * - If the service is fully scaled, it'll be set to (at least) `MaxSize` - 1\n *\n * @privateRemarks\n * - Once out of experimental, instantiate this `Aspect` directly in {@link GuStack}.\n *\n * @see https://github.com/guardian/testing-asg-rolling-update\n */\nexport class GuHorizontallyScalingDeploymentPropertiesExperimental implements IAspect {\n public visit(construct: IConstruct) {\n if (CfnScalingPolicy.isCfnScalingPolicy(construct)) {\n const { node } = construct;\n const { scopes, path } = node;\n\n /*\n Requiring a `CfnScalingPolicy` to be created in the scope of a `GuAutoScalingGroup`\n is the most reliable way to associate the two together.\n\n Though the `autoScalingGroupName` property is passed to a `CfnScalingPolicy` when instantiating,\n this does not create a concrete link as AWS CDK sets the value to be the ASG's `Ref`.\n That is, it is a `Token` until it is synthesised.\n This is even if the ASG has an explicit name set.\n\n See also:\n - https://docs.aws.amazon.com/cdk/v2/guide/tokens.html\n - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-autoscaling-autoscalinggroup.html#aws-resource-autoscaling-autoscalinggroup-return-values\n - https://github.com/aws/aws-cdk/blob/f6b649d47f8bc30ca741fbb7a4852d51e8275002/packages/aws-cdk-lib/aws-autoscaling/lib/auto-scaling-group.ts#L1560\n */\n const autoScalingGroup = scopes.find((_): _ is GuAutoScalingGroup => _ instanceof GuAutoScalingGroup);\n\n if (!autoScalingGroup) {\n throw new Error(\n `Failed to detect the autoscaling group relating to the scaling policy on path ${path}. Was it created in the scope of a GuAutoScalingGroup?`,\n );\n }\n\n const cfnAutoScalingGroup = autoScalingGroup.node.defaultChild as CfnAutoScalingGroup;\n const currentRollingUpdate = cfnAutoScalingGroup.cfnOptions.updatePolicy?.autoScalingRollingUpdate;\n\n if (currentRollingUpdate) {\n /*\n An autoscaling group that horizontally scales should not explicitly set `Desired`,\n as a rolling update will set the current `Desired` back to the template version,\n undoing any changes that a scale-out event may have done.\n */\n cfnAutoScalingGroup.desiredCapacity = undefined;\n\n const minInstancesInService = GuAsgMinInstancesInServiceParameterExperimental.getInstance(autoScalingGroup);\n\n cfnAutoScalingGroup.cfnOptions.updatePolicy = {\n autoScalingRollingUpdate: {\n ...currentRollingUpdate,\n minInstancesInService: minInstancesInService.valueAsNumber,\n },\n };\n }\n }\n }\n}\n\n/**\n * A CloudFormation parameter used to make the `MinInstancesInService` property of an ASG dynamic during deployment with Riff-Raff.\n * This shouldn't be instantiated directly, but via {@link GuHorizontallyScalingDeploymentPropertiesExperimental}.\n */\nexport class GuAsgMinInstancesInServiceParameterExperimental extends CfnParameter {\n public readonly asg: GuAutoScalingGroup;\n\n private constructor(scope: GuStack, id: string, autoScalingGroup: GuAutoScalingGroup) {\n super(scope, id, { type: \"Number\" });\n this.asg = autoScalingGroup;\n }\n\n public static getInstance(autoScalingGroup: GuAutoScalingGroup): GuAsgMinInstancesInServiceParameterExperimental {\n const stack = GuStack.of(autoScalingGroup) as GuStack;\n const { app } = autoScalingGroup;\n const id = `MinInstancesInServiceFor${app.replaceAll(\"-\", \"\")}`;\n\n const maybeParameter = stack.parameters[id];\n\n if (!maybeParameter) {\n return new GuAsgMinInstancesInServiceParameterExperimental(stack, id, autoScalingGroup);\n }\n\n // This shouldn't happen\n if (!(maybeParameter instanceof GuAsgMinInstancesInServiceParameterExperimental)) {\n throw new Error(\n `CFN parameter ${id} exists but is of type ${maybeParameter.constructor.name} instead of ${GuAsgMinInstancesInServiceParameterExperimental.name}.`,\n );\n }\n\n return maybeParameter;\n }\n}\n\n/**\n * An IAM Policy allowing the sending of a CloudFormation signal.\n *\n * @privateRemarks\n * Implemented as a singleton as the resources can only be tightened at most to the CloudFormation stack.\n */\nclass AsgRollingUpdatePolicy extends Policy {\n private static instance: AsgRollingUpdatePolicy | undefined;\n\n private constructor(scope: GuStack) {\n const { stackId } = scope;\n\n super(scope, \"AsgRollingUpdatePolicy\", {\n statements: [\n // Allow usage of command `cfn-signal`.\n new PolicyStatement({\n actions: [\"cloudformation:SignalResource\"],\n effect: Effect.ALLOW,\n resources: [stackId],\n }),\n\n /*\n Allow usage of command `aws elbv2 describe-target-health`.\n AWS Elastic Load Balancing does not support resource based policies, so the resource has to be `*` (any) here.\n See https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html\n */\n new PolicyStatement({\n actions: [\"elasticloadbalancing:DescribeTargetHealth\"],\n effect: Effect.ALLOW,\n resources: [\"*\"],\n }),\n ],\n });\n }\n\n public static getInstance(stack: GuStack): AsgRollingUpdatePolicy {\n if (!this.instance || !isSingletonPresentInStack(stack, this.instance)) {\n this.instance = new AsgRollingUpdatePolicy(stack);\n }\n\n return this.instance;\n }\n}\n\nexport interface GuEc2AppExperimentalProps extends Omit<GuEc2AppProps, \"updatePolicy\"> {\n /**\n * Which application build to run.\n * This will typically match the build number provided by CI.\n *\n * @example\n * process.env.GITHUB_RUN_NUMBER\n */\n buildIdentifier: string;\n /**\n * The amount of time that newly launched instances will spend warming-up / serving fewer requests than other instances.\n * The range is 30-900 seconds (15 minutes). The default is 0 seconds (disabled).\n *\n * See https://docs.aws.amazon.com/elasticloadbalancing/latest/application/edit-target-group-attributes.html#slow-start-mode\n * for more details.\n *\n * We recommend enabling this setting if you run a high-traffic service, particularly if it is JVM-based.\n *\n * Note that there is a trade-off between reliability and speed here; a longer duration will give new instances\n * more time to warm-up, but it will also slow down deployments and scale-up events.\n */\n slowStartDuration?: Duration;\n}\n\n/**\n * Configures the rolling update policy for the autoscaling group.\n *\n * Most applications should not need to instantiate this class directly as this policy is set by GuEc2AppExperimental.\n *\n * If you need to use this class for a service other than MAPI, please speak to DevX about your use-case first.\n */\nexport class GuRollingUpdatePolicyExperimental {\n static getPolicy(\n maximumInstances: number,\n minimumInstances?: number,\n slowStartDuration: Duration = Duration.seconds(0),\n ) {\n return UpdatePolicy.rollingUpdate({\n maxBatchSize: maximumInstances,\n minInstancesInService: minimumInstances,\n minSuccessPercentage: 100,\n waitOnResourceSignals: true,\n /*\n All of these processes can launch and/or terminate instances and if this happens during a deployment it might\n cause the rolling update to fail: https://repost.aws/knowledge-center/auto-scaling-group-rolling-updates\n The full list of processes can be found here: https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-suspend-resume-processes.html#process-types\n */\n suspendProcesses: [\n ScalingProcess.ALARM_NOTIFICATION,\n ScalingProcess.AZ_REBALANCE,\n ScalingProcess.HEALTH_CHECK,\n ScalingProcess.INSTANCE_REFRESH,\n ScalingProcess.REPLACE_UNHEALTHY,\n ScalingProcess.SCHEDULED_ACTIONS,\n ],\n /*\n Note: this is increased via an Aspect which also takes healthcheck grace period into account.\n\n It was easier to pass the slow start duration through here rather than trying to do this via the\n GuAutoScalingRollingUpdateTimeoutExperimental aspect.\n\n If we do this via the aspect then we need to find all mappings between ASGs and target groups and then\n get the relevant slow start duration for each. This is probably possible but would be reasonably complex.\n However, it's worth mentioning that by taking this approach we open up a small risk that things will not work\n as expected if users enable slow start via a different mechanism (e.g. by modifying the target group outside\n the pattern).\n */\n pauseTime: slowStartDuration,\n });\n }\n}\n\nexport interface GuRoleForRollingUpdateExperimentalProps {\n autoScalingGroup: GuAutoScalingGroup;\n}\n\n/**\n * Grants the role associated with your autoscaling group the necessary permissions for the rolling update deployment\n * mechanism.\n *\n * Most applications should not need to instantiate this class directly as these permission changes are granted\n * by GuEc2AppExperimental.\n *\n * If you need to use this class for a service other than MAPI, please speak to DevX about your use-case first.\n */\nexport class GuRoleForRollingUpdateExperimental {\n constructor(scope: GuStack, props: GuRoleForRollingUpdateExperimentalProps) {\n const policy = AsgRollingUpdatePolicy.getInstance(scope);\n policy.attachToRole(props.autoScalingGroup.role);\n\n // Create the Policy with necessary permissions first.\n // Then create the ASG that requires the permissions.\n const cfnPolicy = policy.node.defaultChild as CfnPolicy;\n const cfnAutoScalingGroup = props.autoScalingGroup.node.defaultChild as CfnAutoScalingGroup;\n cfnAutoScalingGroup.addDependency(cfnPolicy);\n }\n}\n\nexport interface GuUserDataForRollingUpdateExperimentalProps {\n autoScalingGroup: GuAutoScalingGroup;\n targetGroup: GuApplicationTargetGroup;\n applicationPort: number;\n buildIdentifier: string;\n slowStartDuration?: Duration;\n}\n\n/**\n * Modifies the user-data script (which runs whenever an instance is launched) to make it compatible with the new\n * rolling update deployment mechanism.\n *\n * Most applications should not need to instantiate this class directly as these user-data changes are already made\n * by GuEc2AppExperimental.\n *\n * If you need to use this class for a service other than MAPI, please speak to DevX about your use-case first.\n */\nexport class GuUserDataForRollingUpdateExperimental {\n constructor(scope: GuStack, props: GuUserDataForRollingUpdateExperimentalProps) {\n const { region, stackId } = scope;\n const { autoScalingGroup, targetGroup, applicationPort, buildIdentifier, slowStartDuration } = props;\n const cfnAutoScalingGroup = autoScalingGroup.node.defaultChild as CfnAutoScalingGroup;\n\n const deploymentId = GuRiffRaffDeploymentIdParameterExperimental.getInstance(scope);\n\n // This allows redeployment of the same build to cycle instances\n autoScalingGroup.userData.addCommands(\n `echo \"Launch template last updated by Riff-Raff deployment ID: ${deploymentId.valueAsString}\"`,\n );\n\n /*\n `aws` is available via AMIgo baked AMIs.\n See https://github.com/guardian/amigo/tree/main/roles/aws-tools.\n */\n autoScalingGroup.userData.addCommands(\n `# ${GuEc2AppExperimental.name} Instance Health Check Start`,\n `\n TOKEN=$(curl -X PUT \"http://169.254.169.254/latest/api/token\" -H \"X-aws-ec2-metadata-token-ttl-seconds: 21600\")\n INSTANCE_ID=$(curl -H \"X-aws-ec2-metadata-token: $TOKEN\" \"http://169.254.169.254/latest/meta-data/instance-id\")\n\n STATE=$(aws elbv2 describe-target-health \\\n --target-group-arn ${targetGroup.targetGroupArn} \\\n --region ${region} \\\n --targets Id=$INSTANCE_ID,Port=${applicationPort} \\\n --query \"TargetHealthDescriptions[0].TargetHealth.State\")\n\n until [ \"$STATE\" == \"\\\\\"healthy\\\\\"\" ]; do\n echo \"Instance running build ${buildIdentifier} not yet healthy within target group. Current state $STATE. Sleeping...\"\n sleep ${RollingUpdateDurations.sleep.toSeconds()}\n STATE=$(aws elbv2 describe-target-health \\\n --target-group-arn ${targetGroup.targetGroupArn} \\\n --region ${region} \\\n --targets Id=$INSTANCE_ID,Port=${applicationPort} \\\n --query \"TargetHealthDescriptions[0].TargetHealth.State\")\n done\n\n echo \"Instance running build ${buildIdentifier} is healthy in target group.\"\n `,\n `# ${GuEc2AppExperimental.name} Instance Health Check End`,\n );\n\n if (slowStartDuration) {\n const slowStartInSeconds = slowStartDuration.toSeconds().toString();\n targetGroup.setAttribute(\"slow_start.duration_seconds\", slowStartInSeconds);\n autoScalingGroup.userData.addCommands(\n `# ${GuEc2AppExperimental.name} SlowStart Wait Period Start`,\n `\n echo \"Sleeping for ${slowStartInSeconds} seconds while instance running build ${buildIdentifier} warms up...\"\n sleep ${slowStartInSeconds}s\n echo \"Instance running build ${buildIdentifier} should have warmed up by now...\"\n\n STATE=$(aws elbv2 describe-target-health \\\n --target-group-arn ${targetGroup.targetGroupArn} \\\n --region ${region} \\\n --targets Id=$INSTANCE_ID,Port=${applicationPort} \\\n --query \"TargetHealthDescriptions[0].TargetHealth.State\")\n\n if [ \"$STATE\" == \"\\\\\"healthy\\\\\"\" ]; then\n echo \"Instance running build ${buildIdentifier} is ready to start serving a normal percentage of requests\"\n else\n echo \"Instance running build ${buildIdentifier} was not healthy after warm-up period; a failure signal will be sent\"\n exit 1\n fi\n `,\n `# ${GuEc2AppExperimental.name} SlowStart Wait Period End`,\n );\n }\n\n autoScalingGroup.userData.addOnExitCommands(\n `\n cfn-signal --stack ${stackId} \\\n --resource ${cfnAutoScalingGroup.logicalId} \\\n --region ${region} \\\n --exit-code $exitCode || echo 'Failed to send Cloudformation Signal'\n `,\n );\n\n // If https://github.com/guardian/devx-logs is used, this tag will be added as a marker to logs in Central ELK.\n Tags.of(autoScalingGroup.instanceLaunchTemplate).add(MetadataKeys.BUILD_IDENTIFIER, buildIdentifier, {\n applyToLaunchedInstances: true,\n });\n }\n}\n\n/**\n * An experimental pattern to instantiate an EC2 application that is updated entirely via CloudFormation.\n * It sets the update policy of the `AWS::AutoScaling::AutoScalingGroup` to `AutoScalingRollingUpdate`.\n * When a CloudFormation update is applied, the current instances in the ASG will be replaced.\n *\n * This pattern also updates the User Data, running some commands AFTER yours.\n * These changes are wrapped in start and end marking comments.\n *\n * This pattern does not enable for scaling events to trigger _during_ a deployment\n * as during testing it was found that scale-in events during deployment led to unpredictable service availability.\n *\n * If a deployment fails, for example when the healthcheck fails, CloudFormation will automatically rollback the changes.\n * This improves on the behaviour of Riff-Raff's `autoscaling` deployment which requires manual intervention on deployment failure.\n *\n * To update the application's code, a CloudFormation update must be triggered.\n * The best way to do this is to include the build number in the application artifact.\n * TODO test User Data includes a build number.\n *\n * @remarks\n * - The rate at which instances are replaced are conditional based on how scaled up the service is.\n * The more an application is scaled, the longer it'll take to deploy.\n * - It is not necessary to use the `autoscaling` Riff-Raff deployment type in conjunction with this.\n * Doing so won't break anything, however it'll lengthen the deployment time as instances will be rotated twice.\n * Please remove it from any manually created `riff-raff.yaml` file.\n * - Your application should include a build number in its filename.\n * This value will change across builds, and therefore create a CloudFormation template difference to be deployed.\n * - Requires the AWS CLI and `cfn-signal` binaries to be available on the instance, and on the `PATH`.\n * AMIgo adds these via the `aws-tools` role.\n *\n * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-updatepolicy.html#cfn-attributes-updatepolicy-rollingupdate\n * @see https://github.com/guardian/testing-asg-rolling-update\n *\n * @experimental\n */\nexport class GuEc2AppExperimental extends GuEc2App {\n constructor(scope: GuStack, props: GuEc2AppExperimentalProps) {\n const { minimumInstances, maximumInstances = minimumInstances * 2 } = props.scaling;\n const { applicationPort, buildIdentifier, slowStartDuration } = props;\n\n const slowStartDurationInSeconds = slowStartDuration?.toSeconds();\n if (slowStartDurationInSeconds && (slowStartDurationInSeconds < 30 || slowStartDurationInSeconds > 900)) {\n throw new Error(\"Slow start duration must be between 30 and 900 seconds\");\n }\n\n super(scope, {\n ...props,\n // Note: if the service uses horizontal scaling, minimumInstances is overridden by an Aspect (to prevent accidental scale downs!)\n updatePolicy: GuRollingUpdatePolicyExperimental.getPolicy(maximumInstances, minimumInstances, slowStartDuration),\n });\n\n const { autoScalingGroup, targetGroup } = this;\n const cfnAutoScalingGroup = autoScalingGroup.node.defaultChild as CfnAutoScalingGroup;\n\n // Note: if the service uses horizontal scaling, this property is unset by an Aspect (to prevent accidental scale downs!)\n cfnAutoScalingGroup.desiredCapacity = minimumInstances.toString();\n\n // This is used when an ASG is first created; it is not needed to support the new deployment mechanism\n cfnAutoScalingGroup.cfnOptions.creationPolicy = {\n autoScalingCreationPolicy: {\n minSuccessfulInstancesPercent: 100,\n },\n resourceSignal: {\n count: minimumInstances,\n },\n };\n\n new GuRoleForRollingUpdateExperimental(scope, { autoScalingGroup });\n new GuUserDataForRollingUpdateExperimental(scope, {\n autoScalingGroup,\n targetGroup,\n applicationPort,\n buildIdentifier,\n slowStartDuration,\n });\n\n /*\n Instantiate the `GuAutoScalingRollingUpdateTimeoutExperimental` exactly once for a GuStack.\n Whilst multiple instances are syntactically valid, it will cause ASG nodes to be incorrectly evaluated/mutated multiple times.\n TODO Once out of experimental, instantiate these `Aspect`s directly in `GuStack`.\n */\n const allAspects = Aspects.of(scope).all;\n const maybeRollingUpdateTimeoutAspect = allAspects.find(\n (_) => _ instanceof GuAutoScalingRollingUpdateTimeoutExperimental,\n );\n if (!maybeRollingUpdateTimeoutAspect) {\n Aspects.of(scope).add(new GuAutoScalingRollingUpdateTimeoutExperimental());\n }\n\n Aspects.of(scope).add(new GuHorizontallyScalingDeploymentPropertiesExperimental());\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"gu-load-balanced-app.d.ts","sourceRoot":"","sources":["../../../src/experimental/patterns/gu-load-balanced-app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,QAAQ,EAAqB,MAAM,aAAa,CAAC;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAuB,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAQlG,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAI/C,OAAO,EAGL,cAAc,EAMf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,WAAW,IAAI,cAAc,EAAE,MAAM,wCAAwC,CAAC;AAQ5F,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAMhE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGpD,OAAO,KAAK,EAAuB,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAM1E,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,0BAA0B,EAC1B,KAAK,QAAQ,EACd,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,MAAM,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAEtE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAWpD,MAAM,WAAW,kCAAmC,SAAQ,WAAW;IACrE;;;;;OAKG;IACH,MAAM,EAAE,SAAS,CAAC;IAClB;;OAEG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;OAEG;IACH,uBAAuB,EAAE,MAAM,GAAG,YAAY,CAAC;IAC/C;;OAEG;IACH,gBAAgB,CAAC,EAAE,YAAY,CAAC;IAChC;;;;OAIG;IACH,GAAG,CAAC,EAAE,IAAI,CAAC;IACX;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,EAAE,CAAC;IAE3B;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,EAAE,CAAC;IAC1B;;OAEG;IACH,UAAU,CAAC,EAAE;QACX;;;;;;;;;;;;;;;;WAgBG;QACH,OAAO,EAAE,IAAI,CAAC;QACd;;;;WAIG;QACH,MAAM,EAAE,MAAM,CAAC;QACf;;;;;;;;;;WAUG;QACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;QACzB;;;;;;;;;WASG;QACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;QACjC;;;;;;;;;;;;;;;;;;WAkBG;QACH,6BAA6B,CAAC,EAAE,MAAM,CAAC;QAEvC;;;;;;WAMG;QACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;IAEF;;OAEG;IACH,WAAW,CAAC,EAAE,cAAc,CAAC;IAE7B;;OAEG;IACH,kBAAkB,CAAC,EAAE,QAAQ,EAAE,CAAC;IAEhC;;;;;;;;;;;;;;;;;;;;;;;QAuBI;IACJ,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf;;;;;OAKG;IACH,QAAQ,CAAC,EAAE;QACT;;WAEG;QACH,QAAQ,EAAE,eAAe,GAAG,QAAQ,CAAC;QACrC;;;WAGG;QACH,YAAY,EAAE,YAAY,CAAC;QAC3B;;WAEG;QACH,kBAAkB,CAAC,EAAE,uBAAuB,CAAC;QAE7C;;WAEG;QACH,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;QAC7B;;WAEG;QACH,OAAO,EAAE,aAAa,CAAC;QACvB;;WAEG;QACH,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;QAClC;;;;WAIG;QACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;QAElC;;;;;;;WAOG;QACH,YAAY,CAAC,EAAE,YAAY,CAAC;QAC5B;;;;;;;;WAQG;QACH,oBAAoB,CAAC,EAAE;YACrB,OAAO,EAAE,OAAO,CAAC;YACjB,eAAe,EAAE,MAAM,CAAC;YACxB,iBAAiB,CAAC,EAAE,QAAQ,CAAC;SAC9B,CAAC;QACF;;;;;;;WAOG;QACH,qBAAqB,CAAC,EAAE,QAAQ,CAAC;QACjC;;;;;;;;WAQG;QACH,yBAAyB,EAAE,SAAS,GAAG,SAAS,CAAC;KAClD,CAAC;IACF;;;;;OAKG;IACH,QAAQ,CAAC,EAAE;QACT;;;;;WAKG;QACH,eAAe,EAAE,MAAM,CAAC;QACxB,GAAG,EAAE,MAAM,CAAC;QACZ,cAAc,EAAE,MAAM,CAAC;QACvB;;;WAGG;QACH,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB;;;WAGG;QACH,OAAO,EAAE;YACP;;;eAGG;YACH,YAAY,EAAE,MAAM,CAAC;YACrB;;;;;;eAMG;YACH,YAAY,EAAE,MAAM,CAAC;SACtB,CAAC;KACH,CAAC;IACF;;;;;;;;;;;;OAYG;IACH,kBAAkB,CAAC,EAAE;QACnB,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;CACH;AAED,UAAU,YAAY;IACpB,GAAG,CAAC,EAAE,wBAAwB,CAAC;IAC/B,GAAG,CAAC,EAAE,wBAAwB,CAAC;CAChC;AAED,qBAAa,6BAA8B,SAAQ,SAAS;IAC1D;;OAEG;IACH,SAAgB,GAAG,EAAE,IAAI,CAAC;IAC1B;;;OAGG;IACH,SAAgB,WAAW,CAAC,EAAE,aAAa,CAAC;IAC5C;;OAEG;IACH,SAAgB,YAAY,EAAE,yBAAyB,CAAC;IACxD;;OAEG;IACH,SAAgB,gBAAgB,CAAC,EAAE,kBAAkB,CAAC;IACtD;;OAEG;IACH,SAAgB,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5C;;OAEG;IACH,SAAgB,QAAQ,EAAE,0BAA0B,CAAC;IACrD;;;;;;;OAOG;IACH,SAAgB,YAAY,EAAE,YAAY,CAAC;gBAE/B,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,kCAAkC;CAujBtE"}
1
+ {"version":3,"file":"gu-load-balanced-app.d.ts","sourceRoot":"","sources":["../../../src/experimental/patterns/gu-load-balanced-app.ts"],"names":[],"mappings":"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"}
@@ -9,7 +9,6 @@ const aws_ecr_1 = require("aws-cdk-lib/aws-ecr");
9
9
  const aws_ecs_1 = require("aws-cdk-lib/aws-ecs");
10
10
  const aws_ecs_2 = require("aws-cdk-lib/aws-ecs");
11
11
  const aws_elasticloadbalancingv2_1 = require("aws-cdk-lib/aws-elasticloadbalancingv2");
12
- const aws_elasticloadbalancingv2_2 = require("aws-cdk-lib/aws-elasticloadbalancingv2");
13
12
  const aws_elasticloadbalancingv2_actions_1 = require("aws-cdk-lib/aws-elasticloadbalancingv2-actions");
14
13
  const aws_iam_1 = require("aws-cdk-lib/aws-iam");
15
14
  const aws_lambda_1 = require("aws-cdk-lib/aws-lambda");
@@ -307,6 +306,9 @@ class GuLoadBalancedAppExperimental extends constructs_1.Construct {
307
306
  // When open=true, AWS will create a security group which allows all inbound traffic over HTTPS
308
307
  open: access.scope === constants_1.AccessScope.PUBLIC && typeof certificate !== "undefined",
309
308
  });
309
+ if (targetGroups.ec2 && targetGroups.ecs) {
310
+ configureDeterministicRouting(listener, targetGroups.ec2, targetGroups.ecs);
311
+ }
310
312
  // Since AWS won't create a security group automatically when open=false, we need to add our own
311
313
  if (access.scope !== constants_1.AccessScope.PUBLIC) {
312
314
  loadBalancer.addSecurityGroup(new ec2_1.GuSecurityGroup(scope, `${access.scope}IngressSecurityGroup`, {
@@ -470,11 +472,11 @@ exports.GuLoadBalancedAppExperimental = GuLoadBalancedAppExperimental;
470
472
  function configureListenerActions(targetGroups, targetGroupWeights) {
471
473
  // No ECS target, so forward all traffic to EC2
472
474
  if (targetGroups.ec2 && !targetGroups.ecs) {
473
- return aws_elasticloadbalancingv2_2.ListenerAction.forward([targetGroups.ec2]);
475
+ return aws_elasticloadbalancingv2_1.ListenerAction.forward([targetGroups.ec2]);
474
476
  }
475
477
  // No EC2 target, so forward all traffic to ECS
476
478
  if (targetGroups.ecs && !targetGroups.ec2) {
477
- return aws_elasticloadbalancingv2_2.ListenerAction.forward([targetGroups.ecs]);
479
+ return aws_elasticloadbalancingv2_1.ListenerAction.forward([targetGroups.ecs]);
478
480
  }
479
481
  if (targetGroups.ec2 && targetGroups.ecs) {
480
482
  if (!targetGroupWeights) {
@@ -488,7 +490,7 @@ function configureListenerActions(targetGroups, targetGroupWeights) {
488
490
  if (!isBetween(targetGroupWeights.ec2, 0, 999)) {
489
491
  throw new Error("targetGroupWeights.ec2 must be between 0 and 999");
490
492
  }
491
- return aws_elasticloadbalancingv2_2.ListenerAction.weightedForward([
493
+ return aws_elasticloadbalancingv2_1.ListenerAction.weightedForward([
492
494
  { targetGroup: targetGroups.ec2, weight: targetGroupWeights.ec2 },
493
495
  { targetGroup: targetGroups.ecs, weight: targetGroupWeights.ecs },
494
496
  ]);
@@ -497,4 +499,19 @@ function configureListenerActions(targetGroups, targetGroupWeights) {
497
499
  throw new Error("At least one of 'ec2Props' or 'ecsProps' must be specified");
498
500
  }
499
501
  }
502
+ function configureDeterministicRouting(listener, ec2TargetGroup, ecsTargetGroup) {
503
+ const headerName = "X-Gu-Target-Group";
504
+ const ec2HeaderValue = "ec2";
505
+ const ecsHeaderValue = "ecs";
506
+ listener.addAction("DeterministicRouteToEc2", {
507
+ priority: 10,
508
+ conditions: [aws_elasticloadbalancingv2_1.ListenerCondition.httpHeader(headerName, [ec2HeaderValue])],
509
+ action: aws_elasticloadbalancingv2_1.ListenerAction.forward([ec2TargetGroup]),
510
+ });
511
+ listener.addAction("DeterministicRouteToEcs", {
512
+ priority: 11,
513
+ conditions: [aws_elasticloadbalancingv2_1.ListenerCondition.httpHeader(headerName, [ecsHeaderValue])],
514
+ action: aws_elasticloadbalancingv2_1.ListenerAction.forward([ecsTargetGroup]),
515
+ });
516
+ }
500
517
  //# sourceMappingURL=gu-load-balanced-app.js.map