public visit()

in src/experimental/patterns/ec2-app.ts [131:193]


  public visit(construct: IConstruct) {
    if (construct instanceof CfnScalingPolicy) {
      const { node } = construct;
      const { scopes, path } = node;

      /*
      Requiring a `CfnScalingPolicy` to be created in the scope of a `GuAutoScalingGroup`
      is the most reliable way to associate the two together.

      Though the `autoScalingGroupName` property is passed to a `CfnScalingPolicy` when instantiating,
      this does not create a concrete link as AWS CDK sets the value to be the ASG's `Ref`.
      That is, it is a `Token` until it is synthesised.
      This is even if the ASG has an explicit name set.

      See also:
        - https://docs.aws.amazon.com/cdk/v2/guide/tokens.html
        - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-autoscaling-autoscalinggroup.html#aws-resource-autoscaling-autoscalinggroup-return-values
        - https://github.com/aws/aws-cdk/blob/f6b649d47f8bc30ca741fbb7a4852d51e8275002/packages/aws-cdk-lib/aws-autoscaling/lib/auto-scaling-group.ts#L1560
       */
      const autoScalingGroup = scopes.find((_): _ is GuAutoScalingGroup => _ instanceof GuAutoScalingGroup);

      if (!autoScalingGroup) {
        throw new Error(
          `Failed to detect the autoscaling group relating to the scaling policy on path ${path}. Was it created in the scope of a GuAutoScalingGroup?`,
        );
      }

      const cfnAutoScalingGroup = autoScalingGroup.node.defaultChild as CfnAutoScalingGroup;
      const currentRollingUpdate = cfnAutoScalingGroup.cfnOptions.updatePolicy?.autoScalingRollingUpdate;

      if (currentRollingUpdate) {
        /*
        An autoscaling group that horizontally scales should not explicitly set `Desired`,
        as a rolling update will set the current `Desired` back to the template version,
        undoing any changes that a scale-out event may have done.
         */
        cfnAutoScalingGroup.desiredCapacity = undefined;

        const asgNodeId = autoScalingGroup.node.id;

        if (!this.asgToParamMap.has(asgNodeId)) {
          const cfnParameterName = getAsgRollingUpdateCfnParameterName(autoScalingGroup);
          this.asgToParamMap.set(
            asgNodeId,
            new CfnParameter(this.stack, cfnParameterName, {
              type: "Number",
              default: parseInt(cfnAutoScalingGroup.minSize),
              maxValue: parseInt(cfnAutoScalingGroup.maxSize) - 1,
            }),
          );
        }

        const minInstancesInService = this.asgToParamMap.get(asgNodeId)!;

        cfnAutoScalingGroup.cfnOptions.updatePolicy = {
          autoScalingRollingUpdate: {
            ...currentRollingUpdate,
            minInstancesInService: minInstancesInService.valueAsNumber,
          },
        };
      }
    }
  }