UNPKG

@aws-amplify/amplify-category-api

Version:
261 lines (221 loc) 9.46 kB
import * as path from 'path'; import { $TSContext, createDefaultCustomPoliciesFile, pathManager } from '@aws-amplify/amplify-cli-core'; import { printer } from '@aws-amplify/amplify-prompts'; import * as fs from 'fs-extra'; import { v4 as uuid } from 'uuid'; import { NETWORK_STACK_LOGICAL_ID } from '../../category-constants'; import { DEPLOYMENT_MECHANISM } from './base-api-stack'; import { GitHubSourceActionInfo } from './pipeline-with-awaiter'; import { API_TYPE, IMAGE_SOURCE_TYPE, ResourceDependency, ServiceConfiguration } from './service-walkthroughs/containers-walkthrough'; import { ApiResource, generateContainersArtifacts } from './utils/containers-artifacts'; export const addResource = async ( serviceWalkthroughPromise: Promise<ServiceConfiguration>, context: $TSContext, category: string, service, options, apiType: API_TYPE, ) => { const walkthroughOptions = await serviceWalkthroughPromise; const { resourceName, restrictAccess, imageSource, gitHubPath, gitHubToken, deploymentMechanism, categoryPolicies, environmentMap, dependsOn = [], mutableParametersState, } = walkthroughOptions; const resourceDirPath = pathManager.getResourceDirectoryPath(undefined, category, resourceName); let [authName, updatedDependsOn] = await getResourceDependencies({ dependsOn, restrictAccess, category, resourceName, context }); let gitHubInfo: GitHubSourceActionInfo; if (deploymentMechanism === DEPLOYMENT_MECHANISM.INDENPENDENTLY_MANAGED) { const { StackName } = context.amplify.getProjectDetails().amplifyMeta.providers['awscloudformation']; const secretName = `${StackName}-${category}-${resourceName}-github-token`; const { ARN: secretArn } = await context.amplify.executeProviderUtils(context, 'awscloudformation', 'newSecret', { secret: gitHubToken, description: 'GitHub OAuth token', name: secretName, version: secretName, }); const gitHubTokenSecretArn = secretArn; gitHubInfo = { path: gitHubPath, tokenSecretArn: gitHubTokenSecretArn, }; } const build = deploymentMechanism === DEPLOYMENT_MECHANISM.FULLY_MANAGED; options = { resourceName, dependsOn: updatedDependsOn, deploymentMechanism, imageSource, restrictAccess, build, providerPlugin: 'awscloudformation', service: 'ElasticContainer', gitHubInfo, authName, environmentMap, categoryPolicies, mutableParametersState, skipHashing: false, apiType, iamAccessUnavailable: true, // this is because we dont support IAM access to the API yet }; await context.amplify.updateamplifyMetaAfterResourceAdd(category, resourceName, options); const apiResource = (await context.amplify.getProjectMeta().api[resourceName]) as ApiResource; apiResource.category = category; fs.ensureDirSync(resourceDirPath); fs.ensureDirSync(path.join(resourceDirPath, 'src')); if (imageSource.type === IMAGE_SOURCE_TYPE.TEMPLATE) { fs.copySync( path.join(__dirname, '..', '..', '..', 'resources', 'awscloudformation/container-templates', imageSource.template), path.join(resourceDirPath, 'src'), { recursive: true }, ); const { exposedContainer } = await generateContainersArtifacts(context, apiResource); await context.amplify.updateamplifyMetaAfterResourceUpdate(category, options.resourceName, 'exposedContainer', exposedContainer); } createDefaultCustomPoliciesFile(category, resourceName); const customPoliciesPath = pathManager.getCustomPoliciesPath(category, resourceName); printer.success(`Successfully added resource ${resourceName} locally.`); printer.info(''); printer.success('Next steps:'); if (deploymentMechanism === DEPLOYMENT_MECHANISM.FULLY_MANAGED) { printer.info( `- Place your Dockerfile, docker-compose.yml and any related container source files in "amplify/backend/api/${resourceName}/src"`, ); } else if (deploymentMechanism === DEPLOYMENT_MECHANISM.INDENPENDENTLY_MANAGED) { printer.info( `- Ensure you have the Dockerfile, docker-compose.yml and any related container source files in your Github path: ${gitHubInfo.path}`, ); } printer.info( `- Amplify CLI infers many configuration settings from the "docker-compose.yaml" file. Learn more: docs.amplify.aws/cli/usage/containers`, ); printer.info(`- To access AWS resources outside of this Amplify app, edit the ${customPoliciesPath}`); printer.info('- Run "amplify push" to build and deploy your image'); return resourceName; }; const getResourceDependencies = async ({ restrictAccess, dependsOn, context, resourceName, category, }: { restrictAccess: boolean; dependsOn: ResourceDependency[]; context: $TSContext; category: string; resourceName: string; }) => { let authName; const updatedDependsOn: ResourceDependency[] = [].concat(dependsOn); updatedDependsOn.push({ category: '', resourceName: NETWORK_STACK_LOGICAL_ID, attributes: ['ClusterName', 'VpcId', 'VpcCidrBlock', 'SubnetIds', 'VpcLinkId', 'CloudMapNamespaceId'], }); if (restrictAccess) { const apiRequirements = { authSelections: 'identityPoolAndUserPool' }; // getting requirement satisfaction map const satisfiedRequirements = await context.amplify.invokePluginMethod(context, 'auth', undefined, 'checkRequirements', [ apiRequirements, context, 'api', resourceName, ]); // checking to see if any requirements are unsatisfied const foundUnmetRequirements = Object.values(satisfiedRequirements).includes(false); if (foundUnmetRequirements) { try { authName = await context.amplify.invokePluginMethod(context, 'auth', undefined, 'externalAuthEnable', [ context, 'api', resourceName, apiRequirements, ]); } catch (e) { printer.error(e); throw e; } } else { [authName] = Object.keys(context.amplify.getProjectDetails().amplifyMeta.auth); } // get auth dependency if exists to avoid duplication const authDependency = updatedDependsOn.find((dependency) => dependency.category === 'auth'); if (authDependency === undefined) { updatedDependsOn.push({ category: 'auth', resourceName: authName, attributes: ['UserPoolId', 'AppClientIDWeb'], }); } else { const existingAttributes = authDependency.attributes; const newAttributes = new Set([...existingAttributes, 'UserPoolId', 'AppClientIDWeb']); authDependency.attributes = Array.from(newAttributes); } } return [authName, updatedDependsOn]; }; export const updateResource = async (serviceWalkthroughPromise: Promise<ServiceConfiguration>, context: $TSContext, category: string) => { const options = await serviceWalkthroughPromise; const { dependsOn, restrictAccess, resourceName, gitHubPath, gitHubToken, gitHubInfo, mutableParametersState, categoryPolicies, environmentMap, deploymentMechanism, } = options; let [authResourceName, updatedDependsOn] = await getResourceDependencies({ dependsOn, restrictAccess, category, resourceName, context }); let newGithubInfo: GitHubSourceActionInfo = { path: gitHubPath, tokenSecretArn: gitHubInfo && gitHubInfo.tokenSecretArn, }; if (gitHubToken) { // #region Add token to secrets manager and get arn const { StackName } = context.amplify.getProjectDetails().amplifyMeta.providers['awscloudformation']; const secretName = `${StackName}-${category}-${resourceName}-github-token`; const { ARN: secretArn } = await context.amplify.executeProviderUtils(context, 'awscloudformation', 'updateSecret', { secret: gitHubToken, description: 'GitHub OAuth token', name: secretName, version: uuid(), }); newGithubInfo.tokenSecretArn = secretArn; // #endregion } if (deploymentMechanism === DEPLOYMENT_MECHANISM.INDENPENDENTLY_MANAGED) { await context.amplify.updateamplifyMetaAfterResourceUpdate(category, options.resourceName, 'gitHubInfo', newGithubInfo); } await context.amplify.updateamplifyMetaAfterResourceUpdate(category, options.resourceName, 'restrictAccess', restrictAccess); await context.amplify.updateamplifyMetaAfterResourceUpdate(category, options.resourceName, 'authName', authResourceName); await context.amplify.updateamplifyMetaAfterResourceUpdate(category, options.resourceName, 'environmentMap', environmentMap); await context.amplify.updateamplifyMetaAfterResourceUpdate(category, options.resourceName, 'dependsOn', updatedDependsOn); await context.amplify.updateamplifyMetaAfterResourceUpdate( category, options.resourceName, 'mutableParametersState', mutableParametersState, ); await context.amplify.updateamplifyMetaAfterResourceUpdate(category, options.resourceName, 'categoryPolicies', categoryPolicies); const apiResource = (await context.amplify.getProjectMeta().api[options.resourceName]) as ApiResource; apiResource.category = category; try { const askForExposedContainer = true; const { exposedContainer } = await generateContainersArtifacts(context, apiResource, askForExposedContainer); await context.amplify.updateamplifyMetaAfterResourceUpdate(category, options.resourceName, 'exposedContainer', exposedContainer); } catch (err) { // Best effort to create templates } };