#!/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'); })();