constructor()

in source/5-unicornPics/loadtesting/lib/loadtesting.ts [12:126]


  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    //Import Ids of resources created in UnicornPics stack
    const userPoolClientId = cdk.Fn.importValue('UserPoolClientId');
    const userPoolId = cdk.Fn.importValue('UserPoolId');
    const postsApiUrl = cdk.Fn.importValue('apiEndpoint');

    //Deploy unicorn pic to be uploaded by virtual users
    const unicornPic = new assets.Asset(this, 'UnicornPic', {
      path: path.resolve(__dirname, 'unicorn.png'),
    });

    //Role for lambdas that need to create/delete Cognito users
    const lambdaCognitoPowerUserRole = new iam.Role(this, 'createUserRole', {
      assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
      roleName: "createUserRole"
    });
    lambdaCognitoPowerUserRole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName("service-role/AWSLambdaBasicExecutionRole"));
    lambdaCognitoPowerUserRole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonCognitoPowerUser"));

    //Create lambda function - Create User Ids
    const createUserIds = new lambda.NodejsFunction(this, 'createUserIds');

    //Create lambda function - Create User
    const createUsers = new lambda.NodejsFunction(this, 'createUsers', {
      environment: {
        CLIENT_ID: userPoolClientId,
        DEFAULT_PASSWORD: DEFAULT_PASSWORD,
      },
      role: lambdaCognitoPowerUserRole,
      timeout: cdk.Duration.minutes(5),
    });

    //Create lambda function - Trigger Load Test
    const triggerLoadTestRole = new iam.Role(this, 'triggerLoadTestRole', {
      assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
      roleName: "TriggerLoadTestRole"
    });
    triggerLoadTestRole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName("service-role/AWSLambdaBasicExecutionRole"));
    triggerLoadTestRole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonCognitoPowerUser"));
    triggerLoadTestRole.addToPolicy(new iam.PolicyStatement({
      actions: ["s3:GetObject", "s3:GetObjectVersion"],
      resources: [unicornPic.bucket.arnForObjects(unicornPic.s3ObjectKey)],
    }));

    const triggerLoadTest = new lambda.NodejsFunction(this, 'triggerLoadTest', {
      environment: {
        USER_POOL_ID: userPoolId,
        CLIENT_ID: userPoolClientId,
        API_URL: postsApiUrl,
        PICTURE_BUCKET: unicornPic.s3BucketName,
        PICTURE_KEY: unicornPic.s3ObjectKey,
        DEFAULT_PASSWORD: DEFAULT_PASSWORD,
      },
      role: triggerLoadTestRole,
      timeout: cdk.Duration.minutes(15),
    });

    //Clean up load testing resources
    const cleanUpUsers = new lambda.NodejsFunction(this, 'cleanUpUsers', {
      environment: {
        USER_POOL_ID: userPoolId,
      },
      role: lambdaCognitoPowerUserRole,
      timeout: cdk.Duration.minutes(5),
    });
    const cleanUpTask = new tasks.LambdaInvoke(this, 'Clean Up', {
      lambdaFunction: cleanUpUsers,
      retryOnServiceExceptions: true,
    });

    //StepFunction to coordination load testing steps
    const inputValidationFailed = new sfn.Fail(this, 'Input Validation Failed', {
      cause: 'Input Validation Failed. Please ensure parameters do not exceed limits.',
      error: "NumberOfUsers > 1000 OR NumberOfLikesPerUser > 1000 OR TestDurationMinutes <= 14",
    });
    const testComplete = new sfn.Pass(this, 'Test Complete');
    const createTestUserIdsTask = new tasks.LambdaInvoke(this, 'Create User Ids', {
      lambdaFunction: createUserIds,
      inputPath: '$',
      outputPath: '$.Payload.userNames',
    });
    const createTestUsersTask = new tasks.LambdaInvoke(this, 'Create Users', {
      lambdaFunction: createUsers,
      resultPath: sfn.JsonPath.DISCARD,
    });
    const triggerLoadTask = new tasks.LambdaInvoke(this, 'Trigger Load', {
      lambdaFunction: triggerLoadTest,
      retryOnServiceExceptions: true,
      resultPath: sfn.JsonPath.DISCARD,
    });
    const triggerAllLoadTask = new sfn.Map(this, 'Trigger All Load', {
      maxConcurrency: 0,
    }).iterator(triggerLoadTask);

    const loadtestDefinition = new sfn.Choice(this, 'Check Input Params')
      .when(sfn.Condition.numberGreaterThan('$.users.NumberOfUsers', 1000), inputValidationFailed)
      .when(sfn.Condition.numberGreaterThan('$.users.NumberOfLikesPerUser', 1000), inputValidationFailed)
      .when(sfn.Condition.numberGreaterThanEquals('$.TestDurationMinutes', 14), inputValidationFailed)
      .otherwise(
        createTestUserIdsTask
          .next(createTestUsersTask)
          .next(triggerAllLoadTask
            .addCatch(cleanUpTask, {
              resultPath: sfn.JsonPath.DISCARD,
            }))
          .next(cleanUpTask)
          .next(testComplete)
      );
    new sfn.StateMachine(this, 'Load Test StateMachine', {
      definition: loadtestDefinition,
      stateMachineName:"LoadTest",
    });
  }