From 78d947995f88a6290b6984200ac0da9efa2b82af Mon Sep 17 00:00:00 2001 From: Attila Hajdrik Date: Wed, 16 Jun 2021 23:17:03 -0700 Subject: [PATCH 1/3] fix: layer CFN generation for multiple layers --- .../service-walkthroughs/lambdaLayerWalkthrough.ts | 2 +- .../service-walkthroughs/removeLayerWalkthrough.ts | 2 +- .../utils/addLayerToFunctionUtils.ts | 2 +- .../utils/lambda-layer-cloudformation-template.ts | 4 ++-- .../awscloudformation/utils/layerCloudState.ts | 12 ++++++------ .../awscloudformation/utils/packageLayer.ts | 6 +++--- .../awscloudformation/utils/storeResources.ts | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/amplify-category-function/src/provider-utils/awscloudformation/service-walkthroughs/lambdaLayerWalkthrough.ts b/packages/amplify-category-function/src/provider-utils/awscloudformation/service-walkthroughs/lambdaLayerWalkthrough.ts index 9f120aa6ea3..81a09d28c50 100644 --- a/packages/amplify-category-function/src/provider-utils/awscloudformation/service-walkthroughs/lambdaLayerWalkthrough.ts +++ b/packages/amplify-category-function/src/provider-utils/awscloudformation/service-walkthroughs/lambdaLayerWalkthrough.ts @@ -113,7 +113,7 @@ export async function updateLayerWalkthrough( // select layer version if (layerHasDeployed) { - const layerCloudState = LayerCloudState.getInstance(); + const layerCloudState = LayerCloudState.getInstance(parameters.layerName); const layerVersions = await layerCloudState.getLayerVersionsFromCloud(context, parameters.layerName); const latestVersionText = 'Future layer versions'; const layerVersionChoices = [ diff --git a/packages/amplify-category-function/src/provider-utils/awscloudformation/service-walkthroughs/removeLayerWalkthrough.ts b/packages/amplify-category-function/src/provider-utils/awscloudformation/service-walkthroughs/removeLayerWalkthrough.ts index 3230ffdaf39..51f48bd642c 100644 --- a/packages/amplify-category-function/src/provider-utils/awscloudformation/service-walkthroughs/removeLayerWalkthrough.ts +++ b/packages/amplify-category-function/src/provider-utils/awscloudformation/service-walkthroughs/removeLayerWalkthrough.ts @@ -11,7 +11,7 @@ import { updateLayerArtifacts } from '../utils/storeResources'; const removeLayerQuestion = 'Choose the Layer versions you want to remove.'; export async function removeWalkthrough(context: $TSContext, layerName: string): Promise { - const layerCloudState = LayerCloudState.getInstance(); + const layerCloudState = LayerCloudState.getInstance(layerName); const layerVersionList = await layerCloudState.getLayerVersionsFromCloud(context, layerName); // if the layer hasn't been pushed return and remove it diff --git a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/addLayerToFunctionUtils.ts b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/addLayerToFunctionUtils.ts index b0e51fd6182..3d9fb4c6489 100644 --- a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/addLayerToFunctionUtils.ts +++ b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/addLayerToFunctionUtils.ts @@ -65,7 +65,7 @@ export const askLayerSelection = async ( layerSelections = layerSelections.filter(selection => selection !== provideExistingARNsPrompt); for (const layerName of layerSelections) { - const layerCloudState = LayerCloudState.getInstance(); + const layerCloudState = LayerCloudState.getInstance(layerName); const layerVersions = await layerCloudState.getLayerVersionsFromCloud(context, layerName); const layerVersionChoices = layerVersions.map(mapVersionNumberToChoice); diff --git a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/lambda-layer-cloudformation-template.ts b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/lambda-layer-cloudformation-template.ts index a6a64b37dea..da34fa3b10e 100644 --- a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/lambda-layer-cloudformation-template.ts +++ b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/lambda-layer-cloudformation-template.ts @@ -24,8 +24,8 @@ export function generateLayerCfnObj(isNewVersion: boolean, parameters: LayerPara if (isNewVersion) { const [shortId] = uuid().split('-'); logicalName = `${LayerCfnLogicalNamePrefix.LambdaLayerVersion}${shortId}`; - const layerCloudState = LayerCloudState.getInstance(); - layerCloudState.latestVersionLogicalId = logicalName; // Store in singleton so it can be used in zipfile name + const layerCloudState = LayerCloudState.getInstance(parameters.layerName); + layerCloudState.latestVersionLogicalIds[parameters.layerName] = logicalName; // Store in singleton so it can be used in zipfile name versionList.unshift({ LogicalName: logicalName, legacyLayer: false }); } else { logicalName = _.first(versionList).LogicalName; diff --git a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/layerCloudState.ts b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/layerCloudState.ts index 01c4bec8122..6042e4e8ce7 100644 --- a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/layerCloudState.ts +++ b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/layerCloudState.ts @@ -6,17 +6,17 @@ import { LegacyPermissionEnum } from './layerMigrationUtils'; import { LayerVersionMetadata, PermissionEnum } from './layerParams'; export class LayerCloudState { - private static instance: LayerCloudState; + private static instances: Record = {}; private layerVersionsMetadata: LayerVersionMetadata[]; - public latestVersionLogicalId: string; + public latestVersionLogicalIds: Record = {}; private constructor() {} - static getInstance(): LayerCloudState { - if (!LayerCloudState.instance) { - LayerCloudState.instance = new LayerCloudState(); + static getInstance(layerName: string): LayerCloudState { + if (!LayerCloudState.instances[layerName]) { + LayerCloudState.instances[layerName] = new LayerCloudState(); } - return LayerCloudState.instance; + return LayerCloudState.instances[layerName]; } private async loadLayerDataFromCloud(context: $TSContext, layerName: string): Promise { diff --git a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/packageLayer.ts b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/packageLayer.ts index 82291b51990..8259b94f4d0 100644 --- a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/packageLayer.ts +++ b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/packageLayer.ts @@ -60,13 +60,13 @@ export const packageLayer: Packager = async (context, resource) => { await zipPackage(packageResult.zipEntries, destination); } - const layerCloudState = LayerCloudState.getInstance(); - if (!layerCloudState.latestVersionLogicalId) { + const layerCloudState = LayerCloudState.getInstance(resource.resourceName); + if (!layerCloudState.latestVersionLogicalIds[resource.resourceName]) { // "Should" never be reachable, but sanity check just in case throw new Error('LogicalId missing for new layer version.'); } - const zipFilename = createLayerZipFilename(resource.resourceName, layerCloudState.latestVersionLogicalId); + const zipFilename = createLayerZipFilename(resource.resourceName, layerCloudState.latestVersionLogicalIds[resource.resourceName]); // check zip size is less than 250MB if (validFilesize(context, destination)) { context.amplify.updateAmplifyMetaAfterPackage(resource, zipFilename, { resourceKey: versionHash, hashValue: currentHash }); diff --git a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/storeResources.ts b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/storeResources.ts index 9e6751a031d..ecc099ce271 100644 --- a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/storeResources.ts +++ b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/storeResources.ts @@ -220,7 +220,7 @@ async function updateLayerCfnFile(context: $TSContext, parameters: LayerParamete let layerVersionList: LayerVersionMetadata[] = []; if (loadPreviousLayerHash(parameters.layerName)) { - const layerCloudState = LayerCloudState.getInstance(); + const layerCloudState = LayerCloudState.getInstance(parameters.layerName); layerVersionList = await layerCloudState.getLayerVersionsFromCloud(context, parameters.layerName); } From 158b4a95e0eeeed091781e57143035791a1a79ff Mon Sep 17 00:00:00 2001 From: Attila Hajdrik Date: Thu, 17 Jun 2021 08:05:28 -0700 Subject: [PATCH 2/3] refactor: change latestVersionLogicalId handling --- .../utils/lambda-layer-cloudformation-template.ts | 2 +- .../provider-utils/awscloudformation/utils/layerCloudState.ts | 2 +- .../provider-utils/awscloudformation/utils/packageLayer.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/lambda-layer-cloudformation-template.ts b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/lambda-layer-cloudformation-template.ts index da34fa3b10e..53e84d3c353 100644 --- a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/lambda-layer-cloudformation-template.ts +++ b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/lambda-layer-cloudformation-template.ts @@ -25,7 +25,7 @@ export function generateLayerCfnObj(isNewVersion: boolean, parameters: LayerPara const [shortId] = uuid().split('-'); logicalName = `${LayerCfnLogicalNamePrefix.LambdaLayerVersion}${shortId}`; const layerCloudState = LayerCloudState.getInstance(parameters.layerName); - layerCloudState.latestVersionLogicalIds[parameters.layerName] = logicalName; // Store in singleton so it can be used in zipfile name + layerCloudState.latestVersionLogicalId = logicalName; // Store in the given layer's layerCloudState instance so it can be used in zipfile name versionList.unshift({ LogicalName: logicalName, legacyLayer: false }); } else { logicalName = _.first(versionList).LogicalName; diff --git a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/layerCloudState.ts b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/layerCloudState.ts index 6042e4e8ce7..c45ad6ca85a 100644 --- a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/layerCloudState.ts +++ b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/layerCloudState.ts @@ -8,7 +8,7 @@ import { LayerVersionMetadata, PermissionEnum } from './layerParams'; export class LayerCloudState { private static instances: Record = {}; private layerVersionsMetadata: LayerVersionMetadata[]; - public latestVersionLogicalIds: Record = {}; + public latestVersionLogicalId: string; private constructor() {} diff --git a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/packageLayer.ts b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/packageLayer.ts index 8259b94f4d0..538baeb1913 100644 --- a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/packageLayer.ts +++ b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/packageLayer.ts @@ -61,12 +61,12 @@ export const packageLayer: Packager = async (context, resource) => { } const layerCloudState = LayerCloudState.getInstance(resource.resourceName); - if (!layerCloudState.latestVersionLogicalIds[resource.resourceName]) { + if (!layerCloudState.latestVersionLogicalId) { // "Should" never be reachable, but sanity check just in case throw new Error('LogicalId missing for new layer version.'); } - const zipFilename = createLayerZipFilename(resource.resourceName, layerCloudState.latestVersionLogicalIds[resource.resourceName]); + const zipFilename = createLayerZipFilename(resource.resourceName, layerCloudState.latestVersionLogicalId); // check zip size is less than 250MB if (validFilesize(context, destination)) { context.amplify.updateAmplifyMetaAfterPackage(resource, zipFilename, { resourceKey: versionHash, hashValue: currentHash }); From db84bf2955558616e8c05938e480e87dd258df56 Mon Sep 17 00:00:00 2001 From: Attila Hajdrik Date: Thu, 17 Jun 2021 10:34:13 -0700 Subject: [PATCH 3/3] fix: update function fails when a previously assigned layer does not exists --- .../awscloudformation/utils/addLayerToFunctionUtils.ts | 8 +++++++- .../awscloudformation/utils/packageLayer.ts | 5 ++++- .../awscloudformation/utils/storeResources.ts | 5 ++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/addLayerToFunctionUtils.ts b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/addLayerToFunctionUtils.ts index 3d9fb4c6489..4cca83a9113 100644 --- a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/addLayerToFunctionUtils.ts +++ b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/addLayerToFunctionUtils.ts @@ -83,10 +83,16 @@ export const askLayerSelection = async ( const previousLayerSelection = _.first(filterProjectLayers(previousSelections).filter(prev => prev.resourceName === layerName)); let defaultLayerSelection: string; + if (previousLayerSelection === undefined || previousLayerSelection.isLatestVersionSelected) { defaultLayerSelection = defaultLayerVersionPrompt; } else { - defaultLayerSelection = mapVersionNumberToChoice(_.first(layerVersions.filter(v => v.Version === previousLayerSelection.version))); + const previouslySelectedLayerVersion = _.first(layerVersions.filter(v => v.Version === previousLayerSelection.version)); + + // Fallback to defaultLayerVersionPrompt as it is possible that a function is associated with a non-existent layer version + defaultLayerSelection = previouslySelectedLayerVersion + ? mapVersionNumberToChoice(previouslySelectedLayerVersion) + : defaultLayerVersionPrompt; } const versionSelection = ( diff --git a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/packageLayer.ts b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/packageLayer.ts index 538baeb1913..79989056de8 100644 --- a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/packageLayer.ts +++ b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/packageLayer.ts @@ -63,7 +63,7 @@ export const packageLayer: Packager = async (context, resource) => { const layerCloudState = LayerCloudState.getInstance(resource.resourceName); if (!layerCloudState.latestVersionLogicalId) { // "Should" never be reachable, but sanity check just in case - throw new Error('LogicalId missing for new layer version.'); + throw new Error(`LogicalId missing for new layer version: ${resource.resourceName}.`); } const zipFilename = createLayerZipFilename(resource.resourceName, layerCloudState.latestVersionLogicalId); @@ -112,6 +112,9 @@ export async function checkContentChanges(context: $TSContext, layerResources: A for (const layer of changedLayerResources) { let { parameters } = layer; if (!accepted) { + context.print.info(''); + context.print.info(`Change options layer: ${layer.resourceName}`); + context.print.info(''); parameters = await lambdaLayerNewVersionWalkthrough(parameters, timestampString); } else { parameters.description = `Updated layer version ${timestampString}`; diff --git a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/storeResources.ts b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/storeResources.ts index ecc099ce271..568ff21b958 100644 --- a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/storeResources.ts +++ b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/storeResources.ts @@ -213,7 +213,10 @@ function ensureLayerRuntimeFolder(layerDirPath: string, runtime: LayerRuntime) { } function createLayerCfnFile(parameters: LayerParameters, layerDirPath: string) { - JSONUtilities.writeJson(path.join(layerDirPath, getCfnTemplateFileName(parameters.layerName)), generateLayerCfnObj(true, parameters)); + const layerCfnObj = generateLayerCfnObj(true, parameters); + const layerCfnFilePath = path.join(layerDirPath, getCfnTemplateFileName(parameters.layerName)); + + JSONUtilities.writeJson(layerCfnFilePath, layerCfnObj); } async function updateLayerCfnFile(context: $TSContext, parameters: LayerParameters, layerDirPath: string): Promise<$TSObject> {