-
Notifications
You must be signed in to change notification settings - Fork 72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
adds layers to function #1835
adds layers to function #1835
Conversation
🦋 Changeset detectedLatest commit: 9f80bfe The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
/** | ||
* Attach Lambda layers to a function | ||
* @example | ||
* import { referenceFunctionLayer } from "@aws-amplify/backend"; | ||
* | ||
* layers: { | ||
* myModule: referenceFunctionLayer("arn:aws:lambda:<current-region>:<account-id>:layer:myLayer:1") | ||
* }, | ||
* | ||
* The object is keyed by the module name hosted on your existing layer and a value that references to an existing layer using an ARN. The keys will be externalized and available via your layer at runtime | ||
* Maximum of 5 layers can be attached to a function. Layers must be in the same region as the function. | ||
*/ | ||
layers?: FunctionLayerReferences; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need referenceFunctionLayer
?
Can't we just
layers: {
myModule: "arn:aws:lambda:<current-region>:<account-id>:layer:myLayer:1"
},
and have construct/factory validate and resolve necessary pieces inside ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this was built according to the RFC requirements
#1549
|
||
/** | ||
* Attach Lambda layers to a function | ||
* - The object is keyed by the module name hosted on your existing layer and a value that references to an existing layer using an ARN. The keys will be externalized and available via your layer at runtime |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this "module name hosted on your existing layer" mean? Do customers need to do something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated the jsdoc to be more verbose
type HydratedFunctionProps = Required<FunctionProps> & { | ||
layers: FunctionLayerReferences; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need this? Wouldn't layers
be automatically be present and required in HydratedFunctionProps
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed this
const resolvedLayers = resolveLayers( | ||
this.props.layers, | ||
scope, | ||
this.props.name | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be part of hydrateDefaults
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the layers requires the scope for the fromLayerVersionArn to convert to a ILayer.
export const arnPattern = new RegExp( | ||
'^(arn:(aws[a-zA-Z-]*)?:lambda:[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:\\d{12}:layer:[a-zA-Z0-9-_]+:[0-9]+)|(arn:[a-zA-Z0-9-]+:lambda:::awslayer:[a-zA-Z0-9-_]+)$' | ||
); | ||
|
||
/** | ||
* Checks if the provided ARN is valid. | ||
*/ | ||
export const isValidLayerArn = (arn: string): boolean => { | ||
return arnPattern.test(arn); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need these methods exported or used outside of this file? If not, can we bring them into the class as privates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
separated the logic and added them as private.
export const arnPattern = new RegExp( | ||
'^(arn:(aws[a-zA-Z-]*)?:lambda:[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:\\d{12}:layer:[a-zA-Z0-9-_]+:[0-9]+)|(arn:[a-zA-Z0-9-]+:lambda:::awslayer:[a-zA-Z0-9-_]+)$' | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add the source of this ARN in the documentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
got this regex from CFN error, but modified this now to be similar to the docs now and linked the documentation.
return new Set<FunctionLayerArn>( | ||
Array.from(uniqueArns).map(parseFunctionLayerArn) | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the method is called validate
it shouldn't do anything else but validate what it's supposed to do. This parsing/mapping shouldn't be part of it.
/** | ||
* Class to represent and handle Lambda Layer ARNs. | ||
*/ | ||
export class FunctionLayerArn { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need a class to represent the arn? It seems like it's just doing validation.
Recommendation: Create a class FunctionLayerParser
/FunctionLayerReferenceAdapter
(or something like that) with one public method parse
/adapt
that takes in the user input, i.e. Record<String, String>
and does several validations and finally produces ILayerVersion[]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
recreated this as a FunctionLayerParser class with parse as suggested
packages/backend-function/API.md
Outdated
// @public (undocumented) | ||
export type FunctionLayerReferences = Record<string, string>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're not inventing anything to constrain this to ARN we should inline this type and rely on jsdocs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inlined this. But to note there is a way we can add a template string literal this but would require a workaround wasnt comfortable using this as unsure if it will be removed: microsoft/TypeScript#29729 (comment)
.changeset/calm-buttons-sort.md
Outdated
'@aws-amplify/backend': minor | ||
--- | ||
|
||
adds referenceLayer to function |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this and some code comments don't seem to be accurate now.
.changeset/serious-dragons-attack.md
Outdated
'@aws-amplify/backend-function': minor | ||
--- | ||
|
||
adds layers to defineFunction |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
adds layers to defineFunction | |
adds support to reference existing layers in defineFunction |
const uniqueArns = new Set<string>(Object.values(layers)); | ||
this.validateLayerCount(uniqueArns); | ||
|
||
for (const [key, arn] of Object.entries(layers)) { | ||
if (!this.isValidLayerArn(arn)) { | ||
throw new AmplifyUserError('InvalidLayerArnFormatError', { | ||
message: `Invalid ARN format for layer: ${arn}`, | ||
resolution: `Update the layer ARN with the expected format: arn:aws:lambda:<current-region>:<account-id>:layer:<layer-name>:<version> for function: ${functionName}`, | ||
}); | ||
} | ||
validLayers[key] = arn; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are validating on the count of uniqueArns not the number of entries in the map. But later we are not deduping it later on. So for example if I provide a map of
{
key1: arn1,
key2: arn1,
key3: arn1,
key4: arn1,
key5: arn1,
key6: arn1,
}
our validation will succeed, but we will be passing/creating 6 reference layers to the NodeJSFunction
constructor which may fail later.
Ideally we should dedupe here on the ARN and provide any of the key here since the key is only used for generating the name of the layer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops thanks for the catch, updated this and also added a test to ensure unique Arn's are passed.
const uniqueArns = new Map<string, string>(); | ||
|
||
for (const [key, arn] of Object.entries(layers)) { | ||
if (!this.isValidLayerArn(arn)) { | ||
throw new AmplifyUserError('InvalidLayerArnFormatError', { | ||
message: `Invalid ARN format for layer: ${arn}`, | ||
resolution: `Update the layer ARN with the expected format: arn:aws:lambda:<current-region>:<account-id>:layer:<layer-name>:<version> for function: ${functionName}`, | ||
}); | ||
} | ||
// Add ARN to the Map with the first encountered key | ||
if (!uniqueArns.has(arn)) { | ||
uniqueArns.set(arn, key); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can simplify this a bit by using just a Set
instead of Map
and build your validLayers
directly in this method.
Problem
Issue number, if available:
RFC: #1549
Changes
adds ability to reference Lambda layers and attach to a function.
Can attach up to 5 layers on a function. Auto sets the layer keys as external modules on the function.
example:
resource.ts
handler.ts. keep using your dependancy without having to use
/opt
as theNODE_PATH
would point to/opt
: https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtimeValidation
Checklist
run-e2e
label set.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.