mirror of
https://github.com/serverless/serverless.git
synced 2026-01-18 14:58:43 +00:00
187 lines
6.3 KiB
JavaScript
187 lines
6.3 KiB
JavaScript
'use strict';
|
|
|
|
const wait = require('timers-ext/promise/sleep');
|
|
const { log, style, progress } = require('@serverless/utils/log');
|
|
const resolveAuthMode = require('@serverless/utils/auth/resolve-mode');
|
|
const apiRequest = require('@serverless/utils/api-request');
|
|
const promptWithHistory = require('@serverless/utils/inquirer/prompt-with-history');
|
|
const { awsRequest } = require('./utils');
|
|
|
|
const iamRoleStackName = 'Serverless-Inc-Role-Stack';
|
|
const cloudFormationServiceConfig = { name: 'CloudFormation', params: { region: 'us-east-1' } };
|
|
|
|
const waitUntilStackIsCreated = async (context) => {
|
|
await wait(2000);
|
|
const stackEvents = (
|
|
await awsRequest(context, cloudFormationServiceConfig, 'describeStackEvents', {
|
|
StackName: iamRoleStackName,
|
|
})
|
|
).StackEvents;
|
|
const failedStatusReasons = stackEvents
|
|
.filter(({ ResourceStatus: status }) => {
|
|
return status && status.endsWith('_FAILED');
|
|
})
|
|
.map(({ ResourceStatusReason: reason }) => reason);
|
|
|
|
if (failedStatusReasons.length) {
|
|
log.error(`Creating IAM Role failed:\n - ${failedStatusReasons.join('\n - ')}`);
|
|
return false;
|
|
}
|
|
const statusEvent = stackEvents.find(
|
|
({ ResourceType: resourceType }) => resourceType === 'AWS::CloudFormation::Stack'
|
|
);
|
|
const status = statusEvent ? statusEvent.ResourceStatus : null;
|
|
if (status && status.endsWith('_COMPLETE')) {
|
|
if (status === 'CREATE_COMPLETE') return true;
|
|
log.error('Creating IAM Role failed');
|
|
return false;
|
|
}
|
|
return waitUntilStackIsCreated(context);
|
|
};
|
|
|
|
const waitUntilIntegrationIsReady = async (context) => {
|
|
await wait(2000);
|
|
const { integrations } = await apiRequest(`/api/integrations/?orgId=${context.org.orgId}`, {
|
|
urlName: 'integrationsBackend',
|
|
});
|
|
const integration = integrations.find(
|
|
({ vendorAccount }) => vendorAccount === context.awsAccountId
|
|
);
|
|
if (integration && integration.status === 'alive' && integration.syncStatus !== 'pending') return;
|
|
await waitUntilIntegrationIsReady(context);
|
|
return;
|
|
};
|
|
|
|
module.exports = {
|
|
async isApplicable(context) {
|
|
const { isConsole, isConsoleDevMode } = context;
|
|
|
|
if (!isConsole) {
|
|
context.inapplicabilityReasonCode = 'NON_CONSOLE_CONTEXT';
|
|
return false;
|
|
}
|
|
|
|
if (!(await resolveAuthMode())) {
|
|
context.inapplicabilityReasonCode = 'NOT_LOGGED_IN';
|
|
return false;
|
|
}
|
|
|
|
if (!context.org) {
|
|
context.inapplicabilityReasonCode = 'UNRESOLVED_ORG';
|
|
return false;
|
|
}
|
|
|
|
const { integrations } = await apiRequest(`/api/integrations/?orgId=${context.org.orgId}`, {
|
|
urlName: 'integrationsBackend',
|
|
});
|
|
|
|
const integration = integrations.find(
|
|
({ vendorAccount }) => vendorAccount === context.awsAccountId
|
|
);
|
|
|
|
if (integration) {
|
|
if (integration.status !== 'alive' || integration.syncStatus === 'pending') {
|
|
const integrationSetupProgress = progress.get('integration-setup');
|
|
try {
|
|
integrationSetupProgress.notice('Setting up Serverless Console Integration');
|
|
await waitUntilIntegrationIsReady(context);
|
|
} finally {
|
|
integrationSetupProgress.remove();
|
|
}
|
|
}
|
|
|
|
log.notice();
|
|
if (!isConsoleDevMode) {
|
|
log.notice.success('Your AWS account is integrated with Serverless Console');
|
|
}
|
|
context.inapplicabilityReasonCode = 'INTEGRATED';
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
await awsRequest(context, cloudFormationServiceConfig, 'describeStacks', {
|
|
StackName: iamRoleStackName,
|
|
});
|
|
log.error(
|
|
'Cannot integrate with Serverless Console: ' +
|
|
'AWS account is already integrated with another org. ' +
|
|
'You can set the AWS_PROFILE environment variable to use a different AWS Profile.'
|
|
);
|
|
context.isConsole = false;
|
|
context.isConsoleDevMode = false;
|
|
context.inapplicabilityReasonCode = 'AWS_ACCOUNT_ALREADY_INTEGRATED';
|
|
return false;
|
|
} catch (error) {
|
|
if (error.Code === 'ValidationError') return true;
|
|
if (error.providerErrorCodeExtension === 'VALIDATION_ERROR') return true;
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
async run(context) {
|
|
const { stepHistory, isConsoleDevMode } = context;
|
|
|
|
if (
|
|
!(await promptWithHistory({
|
|
message: `Press [Enter] to enable Serverless Console's next-generation monitoring.\n\n${style.aside(
|
|
[
|
|
'This will create an IAM Role in your AWS account with the following permissions:',
|
|
'• Subscribe to CloudWatch logs and metrics',
|
|
'• Update Lambda layers and env vars to add tracing and real-time logging',
|
|
'• Read resource info for security alerts',
|
|
`See the IAM Permissions transparently here: ${style.link(
|
|
'https://slss.io/iam-role-permissions'
|
|
)}`,
|
|
'Would you like to proceed?',
|
|
]
|
|
)}`,
|
|
type: 'confirm',
|
|
name: 'shouldSetupConsoleIamRole',
|
|
stepHistory,
|
|
}))
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
log.notice();
|
|
|
|
const integrationSetupProgress = progress.get('integration-setup');
|
|
integrationSetupProgress.notice('Creating IAM Role for Serverless Console');
|
|
|
|
try {
|
|
const { cfnTemplateUrl, params } = await apiRequest(
|
|
`/api/integrations/aws/initial?orgId=${context.org.orgId}`,
|
|
{ urlName: 'integrationsBackend' }
|
|
);
|
|
|
|
await awsRequest(context, cloudFormationServiceConfig, 'createStack', {
|
|
Capabilities: ['CAPABILITY_NAMED_IAM'],
|
|
StackName: iamRoleStackName,
|
|
TemplateURL: cfnTemplateUrl,
|
|
Parameters: [
|
|
{ ParameterKey: 'AccountId', ParameterValue: params.accountId },
|
|
{ ParameterKey: 'ReportServiceToken', ParameterValue: params.reportServiceToken },
|
|
{ ParameterKey: 'ExternalId', ParameterValue: params.externalId },
|
|
{ ParameterKey: 'Version', ParameterValue: params.version },
|
|
],
|
|
});
|
|
|
|
if (!(await waitUntilStackIsCreated(context))) return false;
|
|
|
|
integrationSetupProgress.notice(
|
|
'Setting up Serverless Console Integration (this may take 5-10 minutes)'
|
|
);
|
|
|
|
await waitUntilIntegrationIsReady(context);
|
|
|
|
if (!isConsoleDevMode) {
|
|
log.notice.success('Your AWS account is integrated with Serverless Console');
|
|
}
|
|
} finally {
|
|
integrationSetupProgress.remove();
|
|
}
|
|
return true;
|
|
},
|
|
configuredQuestions: ['shouldSetupConsoleIamRole'],
|
|
};
|