text: #!/usr/bin/env node
  
  'use strict';
  
  require('essentials');
  require('log-node')();
  
  const log = require('log').get('serverless');
  const awsRequest = require('@serverless/test/aws-request');
  const fsp = require('fs').promises;
  const path = require('path');
  const {
    SHARED_INFRA_TESTS_CLOUDFORMATION_STACK,
    SHARED_INFRA_TESTS_ACTIVE_MQ_CREDENTIALS_NAME,
    SHARED_INFRA_TESTS_RABBITMQ_CREDENTIALS_NAME,
  } = require('../../../test/utils/cloudformation');
  
  const ensureActiveMQCredentialsSecret = async () => {
    const ssmMqCredentials = {
      username: process.env.SLS_INTEGRATION_TESTS_ACTIVE_MQ_USER,
      password: process.env.SLS_INTEGRATION_TESTS_ACTIVE_MQ_PASSWORD,
    };
    log.notice('Creating SecretsManager ActiveMQ Credentials secret...');
    try {
      await awsRequest('SecretsManager', 'createSecret', {
        Name: SHARED_INFRA_TESTS_ACTIVE_MQ_CREDENTIALS_NAME,
        SecretString: JSON.stringify(ssmMqCredentials),
      });
    } catch (e) {
      if (!e.code === 'ResourceExistsException') {
        throw e;
      }
    }
  };
  
  const ensureRabbitMQCredentialsSecret = async () => {
    const ssmMqCredentials = {
      username: process.env.SLS_INTEGRATION_TESTS_RABBITMQ_USER,
      password: process.env.SLS_INTEGRATION_TESTS_RABBITMQ_PASSWORD,
    };
    log.notice('Creating SecretsManager RabbitMQ Credentials secret...');
    try {
      await awsRequest('SecretsManager', 'createSecret', {
        Name: SHARED_INFRA_TESTS_RABBITMQ_CREDENTIALS_NAME,
        SecretString: JSON.stringify(ssmMqCredentials),
      });
    } catch (e) {
      if (!e.code === 'ResourceExistsException') {
        throw e;
      }
    }
  };
  
  const activeMqBrokerName = 'integration-tests-activemq-broker';
  const rabbitMqBrokerName = 'integration-tests-rabbitmq-broker';
  
  async function handleInfrastructureCreation() {
    const [cfnTemplate, kafkaServerProperties] = await Promise.all([
      fsp.readFile(path.join(__dirname, 'cloudformation.yml'), 'utf8'),
      fsp.readFile(path.join(__dirname, 'kafka.server.properties')),
    ]);
  
    await ensureActiveMQCredentialsSecret();
    await ensureRabbitMQCredentialsSecret();
  
    const clusterName = 'integration-tests-msk-cluster';
    const clusterConfName = 'integration-tests-msk-cluster-configuration';
  
    log.notice('Creating MSK Cluster configuration...');
    const clusterConfResponse = await awsRequest('Kafka', 'createConfiguration', {
      Name: clusterConfName,
      ServerProperties: kafkaServerProperties,
      KafkaVersions: ['2.2.1'],
    });
  
    const clusterConfigurationArn = clusterConfResponse.Arn;
    const clusterConfigurationRevision = clusterConfResponse.LatestRevision.Revision.toString();
  
    log.notice('Deploying integration tests CloudFormation stack...');
    await awsRequest('CloudFormation', 'createStack', {
      StackName: SHARED_INFRA_TESTS_CLOUDFORMATION_STACK,
      TemplateBody: cfnTemplate,
      Parameters: [
        { ParameterKey: 'ClusterName', ParameterValue: clusterName },
        { ParameterKey: 'ActiveMQBrokerName', ParameterValue: activeMqBrokerName },
        {
          ParameterKey: 'ActiveMQUser',
          ParameterValue: process.env.SLS_INTEGRATION_TESTS_ACTIVE_MQ_USER,
        },
        {
          ParameterKey: 'ActiveMQPassword',
          ParameterValue: process.env.SLS_INTEGRATION_TESTS_ACTIVE_MQ_PASSWORD,
        },
        { ParameterKey: 'RabbitMQBrokerName', ParameterValue: rabbitMqBrokerName },
        {
          ParameterKey: 'RabbitMQUser',
          ParameterValue: process.env.SLS_INTEGRATION_TESTS_RABBITMQ_USER,
        },
        {
          ParameterKey: 'RabbitMQPassword',
          ParameterValue: process.env.SLS_INTEGRATION_TESTS_RABBITMQ_PASSWORD,
        },
        { ParameterKey: 'ClusterConfigurationArn', ParameterValue: clusterConfigurationArn },
        {
          ParameterKey: 'ClusterConfigurationRevision',
          ParameterValue: clusterConfigurationRevision,
        },
      ],
    });
  
    await awsRequest('CloudFormation', 'waitFor', 'stackCreateComplete', {
      StackName: SHARED_INFRA_TESTS_CLOUDFORMATION_STACK,
    });
    log.notice('Deployed integration tests CloudFormation stack!');
  }
  
  async function handleInfrastructureUpdate() {
    log.notice('Updating integration tests CloudFormation stack...');
  
    await ensureActiveMQCredentialsSecret();
    await ensureRabbitMQCredentialsSecret();
  
    const cfnTemplate = await fsp.readFile(path.join(__dirname, 'cloudformation.yml'), 'utf8');
  
    try {
      await awsRequest('CloudFormation', 'updateStack', {
        StackName: SHARED_INFRA_TESTS_CLOUDFORMATION_STACK,
        TemplateBody: cfnTemplate,
        Parameters: [
          { ParameterKey: 'ClusterName', UsePreviousValue: true },
          { ParameterKey: 'ActiveMQBrokerName', ParameterValue: activeMqBrokerName },
          {
            ParameterKey: 'ActiveMQUser',
            ParameterValue: process.env.SLS_INTEGRATION_TESTS_ACTIVE_MQ_USER,
          },
          {
            ParameterKey: 'ActiveMQPassword',
            ParameterValue: process.env.SLS_INTEGRATION_TESTS_ACTIVE_MQ_PASSWORD,
          },
          { ParameterKey: 'RabbitMQBrokerName', ParameterValue: rabbitMqBrokerName },
          {
            ParameterKey: 'RabbitMQUser',
            ParameterValue: process.env.SLS_INTEGRATION_TESTS_RABBITMQ_USER,
          },
          {
            ParameterKey: 'RabbitMQPassword',
            ParameterValue: process.env.SLS_INTEGRATION_TESTS_RABBITMQ_PASSWORD,
          },
          { ParameterKey: 'ClusterConfigurationArn', UsePreviousValue: true },
          {
            ParameterKey: 'ClusterConfigurationRevision',
            UsePreviousValue: true,
          },
        ],
      });
    } catch (e) {
      if (e.message === 'No updates are to be performed.') {
        log.notice('No changes detected. Integration tests CloudFormation stack is up to date.');
        return;
      }
      throw e;
    }
  
    await awsRequest('CloudFormation', 'waitFor', 'stackUpdateComplete', {
      StackName: SHARED_INFRA_TESTS_CLOUDFORMATION_STACK,
    });
    log.notice('Updated integration tests CloudFormation stack!');
  }
  
  (async () => {
    log.notice('Starting setup of integration infrastructure...');
  
    if (!process.env.SLS_INTEGRATION_TESTS_ACTIVE_MQ_USER) {
      log.error(
        '"SLS_INTEGRATION_TESTS_ACTIVE_MQ_USER" env variable has to be set when provisioning integration infrastructure'
      );
      process.exitCode = 1;
      return;
    }
  
    if (!process.env.SLS_INTEGRATION_TESTS_ACTIVE_MQ_PASSWORD) {
      log.error(
        '"SLS_INTEGRATION_TESTS_ACTIVE_MQ_PASSWORD" env variable has to be set when provisioning integration infrastructure'
      );
      process.exitCode = 1;
      return;
    }
  
    if (!process.env.SLS_INTEGRATION_TESTS_RABBITMQ_USER) {
      log.error(
        '"SLS_INTEGRATION_TESTS_RABBITMQ_USER" env variable has to be set when provisioning integration infrastructure'
      );
      process.exitCode = 1;
      return;
    }
  
    if (!process.env.SLS_INTEGRATION_TESTS_RABBITMQ_PASSWORD) {
      log.error(
        '"SLS_INTEGRATION_TESTS_RABBITMQ_PASSWORD" env variable has to be set when provisioning integration infrastructure'
      );
      process.exitCode = 1;
      return;
    }
  
    let describeResponse;
  
    log.notice('Checking if integration tests CloudFormation stack already exists...');
    try {
      describeResponse = await awsRequest('CloudFormation', 'describeStacks', {
        StackName: SHARED_INFRA_TESTS_CLOUDFORMATION_STACK,
      });
      log.notice('Integration tests CloudFormation stack already exists');
    } catch (e) {
      if (e.code !== 'ValidationError') {
        throw e;
      }
      log.notice('Integration tests CloudFormation does not exist');
    }
  
    if (describeResponse) {
      const stackStatus = describeResponse.Stacks[0].StackStatus;
  
      if (['CREATE_COMPLETE', 'UPDATE_COMPLETE', 'UPDATE_ROLLBACK_COMPLETE'].includes(stackStatus)) {
        await handleInfrastructureUpdate();
      } else {
        log.error(`Existing stack has status: ${stackStatus} and it cannot be updated.`);
        process.exitCode = 1;
      }
    } else {
      await handleInfrastructureCreation();
    }
  
    log.notice('Setup of integration infrastructure finished');
  })();
  