-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
SAM deploy
doesn't modify uploaded CodeUri
if !Sub
is used even with AWS::LanguageExtensions
#5249
Comments
CodeUri
if !Sub
is used even with AWS::LanguageExtensionsCodeUri
if !Sub
is used even with AWS::LanguageExtensions
CodeUri
if !Sub
is used even with AWS::LanguageExtensions
deploy
doesn't modify uploaded CodeUri
if !Sub
is used even with AWS::LanguageExtensions
Thanks for reporting this! Moving over to SAM CLI repository since this is related to local intrinsic function resolution; similar to #4767. Meanwhile, while not pretty you could use |
Thanks for the thorough summary, indeed it looks like when So ideally SAM CLI could resolve the local path before deployment, and then package the corresponding files as usual. |
@garretwilson I understand what you are trying to do but I don't understand the flow you have. Are you using Can't you generate a jar without the version and then not need this at all? Generally the issue here: SAM CLI has had to implement intrinsic resolution on the client side and we do this in some area but
If you don't want |
No. I'm only using Please read Building serverless Java applications with the AWS SAM CLI, in particular the section, "Deploying the application without building with AWS SAM". It's a JAR file.
I'm not using
But why? Why? Why? Are we merging Merkle trees with Bloom filters in LSM trees? No, we're … doing simple string interpolation. Why do I have to add workarounds for something so simple and straightforward? SAM, CloudFormation, and CI/CD are built on the foundation of immutable, versioned artifacts. Why should SAM force me to start un-versioning my artifacts? This project will likely be deploying the actual JAR files to a registry such a Maven Central. Another project will pull them down using the normal Maven dependency resolution. All these JAR files will be versioned. Why should I have to build a workaround layer just to un-version things? Why should SAM force me to hack and undo industry best practices? In CloudFormation when I define my ECS Fargate task container definition, I can easily use parameter interpolation for the name of the container image. Why should I expect the same thing to break just because I "stepped up" to use SAM? Perhaps I'm not explaining the scenario well, because in my mind this could hardly be simpler, and is probably just a straightforward bug for a situation the developer didn't consider. Let me ask a few questions:
Maybe in all of this you respond, "but interpolation of parameters is not supposed to take place until the template is read from S3 and used to create a stack". But step back a second:
Where in the source code does the SAM CLI convert |
An even better design would avoid the need for interpolation altogether in the modified template: simply keep the original name of the artifact Then someone might say, "But then what if we overwrite an existing artifact? How will we tell them apart without renaming them to hashes?" And that is the whole point of version numbers! That is the whole point of immutable, versioned builds. That is the whole point of Docker and Amazon Elastic Container Registry (ECR) and ECS Fargate using version numbers for artifacts. In this case SAM CLI is attempting to do a quick-and-dirty implementation of an artifact repository and rely on hashes to keep versions apart. But it makes it a pain for the industry best-practices approach of immutable builds based upon versioned artifacts. If you add a flag "don't rename artifact when uploading", that would solve this problem more elegantly and be really easy to implement—although you would need to dereference the The other approach is to write a dereferenced value in I'm still looking for where this happens in the code. If you happen to know, pass it along and I'll try to give more code-oriented suggestions. |
I just thought of a potential workaround based upon my last idea: if I specify an S3 URI for CodeUri: !Sub "s3://aws-sam-cli-managed-default-samclisourcebucket-…/foo-bar-functions-${Ver}.jar" Then I could do something like this in the script that invokes SAM (to which the version is initially passed), manually uploading the JAR file to S3 so that it won't be renamed by SAM: aws s3 cp target/foo-bar-functions-$ver s3://aws-sam-cli-managed-default-samclisourcebucket-…/
sam deploy … The idea here is to sidestep SAM CLI's implementation of the JAR upload because SAM gets confused with interpolation. Can the CLI figure out that it doesn't need to upload the JAR file if I specify an S3 URI, and just continue normally with deployment? Once the template is uploaded and get processed, I'm assuming that This workaround has some downsides. Now the JAR file is no longer managed by SAM, so when I use |
@garretwilson There is a lot here. So this will likely be a longer post and will probably not respond to each thing above. I am going to focus on 1) getting mutual understanding of SAM CLI and what happens 2) your direct request 3) side affects like Mutual understanding of SAM CLI and what happensI am going to ignore some of the commands (like Uploading Artifacts (aka
|
@jfuss thank you for taking the time to provide this well-organized explanation. I have some strong feelings about both the design decisions that were made as well as the current decision not to address the shortcomings in the tool itself, but I'll just leave it at that for now. I have great news: the workaround idea I had yesterday is working marvelously—in fact better than I had even envisioned. First let's take a step back and think about why any of this even matters. Let's say I'm deploying container images in AWS ECS Fargate. One day I upload and deploy Now let's start using SAM. What if we have this in our SAM template (which is basically what the SAM problem in this ticket forces us to do) and we find out we have to roll back to last week's version?
The hashes That would be a mess. That would not be DevOps. That would not be appropriate for 2023. So now for the workaround. The SAM template still requires Transform:
- AWS::LanguageExtensions
- AWS::Serverless-2016-10-31 The template has a Parameters:
Ver:
Description: The version of the functions, such as "1.2.3".
Type: String
AllowedPattern: '[\w.+-]+'
ConstraintDescription: The service version must use only word characters, dots, plus signs, and dashes. The template simply uses the JAR file from S3 as-is, but a substitution for the version parameter is passed, like this: Resources:
TestIngestFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: !Sub "s3://my-sam-bucket/foo-bar-functions-${Ver}.jar"
Handler: com.example.FooBar::foo That works like a charm. Now I can use
Your next question may be, "But how do you get the artifacts to S3 if you don't rely on aws s3 cp $artifactPath s3://${s3BucketName}/${s3ObjectName} --profile $awsProfile "So you re-upload the file each time? Isn't that a lot of needless bandwidth? With the hash filename method, it wouldn't upload the file again if it hadn't changed." Right. We can do the same thing here, but it's even better because of version numbers. The most standards-based best-practices modern approach is to use versioned, immutable artifacts. Thus if an artifact is uploaded already with the same version number, you simply don't upload it again—you don't need to, because you can be confident that it hasn't changed! It looks like this: s3BucketName=my-sam-bucket
sourceDirectory=target
artifactBaseName=foo-bar-functions
artifactName=$artifactBaseName-${ver}.jar
artifactPath=$sourceDirectory/${artifactName}
# SNAPSHOT processing goes here; see below
s3ObjectName=$artifactBaseName-${ver}.jar
if (( $(aws s3api head-object --bucket $s3BucketName --key $s3ObjectName --profile $awsProfile \
>/dev/null 2>/dev/null; echo $?) == 254 )); then
echo "Uploading $artifactName ..."
aws s3 cp $artifactPath s3://${s3BucketName}/${s3ObjectName} --profile $awsProfile
else
echo "$s3ObjectName already exists in S3 bucket; skipping upload."
fi Oh, but it gets better. What if you're working with a So what do we do? Do we simply overwrite "Ah ha! That's why we used hashes for the filenames in In other words, we use hashes only with if [[ $ver == *-SNAPSHOT ]]; then
hash=($(shasum -a 256 $artifactPath))
ver="${ver}+${hash:0:16}"
echo "Using modified snapshot version: ${ver}"
fi Things to note:
And there you have it. Versions such as
It's elegant. It works like a modern CD/CI tool should. It works like developers expect who are used to working with Maven, Docker, Node.js, AWS ECS+ECR, etc. The only downside I can think of is that I had to spend my morning writing it. Otherwise I have no complaints, and it makes no difference to me now whether this ticket is addressed. Again thanks for reading the ticket, and thanks for taking the time to respond. Just please keep |
@garretwilson Going to close this as the solution is the best way forward if you want control over the template at deploy time. |
|
I'm using SAM 1.84.0 on Windows 10.
Let's say I'm deploying some lambda function I built with Maven:
SAM recognizes that
CodeUri
points to a local file, so it uploads JAR file to S3 and also uploads the template itself to S3. But first it modifies the template to reference the new JAR file location. The template uploaded winds up looking like this when it gets to S3:Obviously I don't want to hard-code the version
1.2.3
in my SAM template. In pure CloudFormation it is no problem for me to simply pass in aVer
parameter from the command line, indicating the version to use (e.g. the latest Maven version, or some version in the past, or whatever):But because of this aws/serverless-application-model#2533, SAM gives me an error:
This general problem has been discussed in aws/serverless-application-model#2533. It seems that SAM gets all confused if there are functions used in the values that SAM needs right away. So the workaround noted in aws/serverless-application-model#2533 was to add
AWS::LanguageExtensions
.That gets me a little further: SAM will at least upload my JAR to the S3 bucket. But then when it fails with:
The template uploaded has:
I believe this is not simply the same as aws/serverless-application-model#2533 ; rather, it would seem to be a SAM bug. If SAM with
AWS::LanguageExtensions
can figure out the JAR file that!Sub "target/foo-bar-functions-${Ver}.jar"
references, it should be able to realize that the value forCodeUri
should be updated, just as if it were a literal string.In other words, this is not just a problem of not having values available at the time of deployment. As you can see, with
AWS::LanguageExtensions
SAM already knows what the correct value is forCodeUri
not an S3 bucket but a local file, because it stopped giving the error'CodeUri' requires Bucket and Key properties to be specified.
The problem is that when SAM then uploads the template to S3, it doesn't realize that theCodeUri
still needs updating. Something about using!Sub
withCodeUri
is preventing SAM from replacing the local reference as it would if the value were a literal string.(I would guess that somewhere in the code there is an erroneous "sanity-check" line that say something like "if (
CodeUri
is a literal string andCodeUri
points to a local value) {updateCodeUri
value to S3 form}". This bug fix may be a simple as removing the erroneous "CodeUri
is a literal string" check. This is all just a guess based upon behavior; I haven't looked at the code.)This is severe; it's forcing me to hard-code versions into my template, and I haven't yet found a workaround.
The text was updated successfully, but these errors were encountered: