aws / aws-extensions-for-dotnet-cli

Extensions to the dotnet CLI to simplify the process of building and publishing .NET Core applications to AWS services
Apache License 2.0
369 stars 86 forks source link

project-location is relative to the folder of the template, not the working directory #214

Open hauntingEcho opened 2 years ago

hauntingEcho commented 2 years ago

Describe the bug

project-location is documented as: "The location of the project, if not set the current directory will be assumed."

It actually is relative to, and defaults to, the folder of your template file rather than your current working directory.

Expected Behavior

project location is relative to, and defaults to, the current working directory

Current Behavior

project location actually is relative to, and defaults to, the folder of your template file.

Reproduction Steps

Commands assume you're using git-bash on Windows 10:

  1. in an empty folder, run dotnet new lambda.EmptyFunction && touch serverless.yaml
  2. copy the template below into the serverless.yaml file you've just created
  3. cd src/lambda-example
  4. run dotnet lambda package-ci -t ../../serverless.yaml -sb "${bucket}" --output-template compiled.yaml using a bucket in your default region to which you have write-access
  5. see the bug - it'll zip everything in your current folder without building anything
  6. cp ../../serverless.yaml . && cd ../.. && dotnet lambda package-ci -t src/lambda-example/serverless.yaml -sb "${bucket}" --output-template compiled.yaml
  7. see the bug - failure should be expected, but it works

serverless.yaml:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Resources:
  LambdaAuthorizer:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: dotnet6
      Handler: lambda-example::lambda_example.Function::FunctionHandler

Possible Solution

Update the documentation - this would be a breaking change, and probably not worth actually changing the behavior.

Additional Information/Context

No response

Targeted .NET platform

$ dotnet --version 6.0.202

CLI extension version

$ dotnet tool list -g Package Id Version Commands

amazon.lambda.testtool-3.1 0.12.1 dotnet-lambda-test-tool-3.1 amazon.lambda.testtool-6.0 0.12.1 dotnet-lambda-test-tool-6.0 amazon.lambda.tools 5.1.4 dotnet-lambda dotnet-ef 5.0.9 dotnet-ef dotnet-reportgenerator-globaltool 4.8.12 reportgenerator

Environment details (OS name and version, etc.)

Windows 10, git bash

ashishdhingra commented 2 years ago

@hauntingEcho Could you please point to documentation source which has the issue?

hauntingEcho commented 2 years ago

It looks like it's defined as a common option here. I don't know whether it's incorrect for anything other than package-ci, though

CLI help message:

Matt@DESKTOP-C7GV3UB MINGW64 ~/test/lambda-example/src/lambda-example
$ dotnet lambda package-ci --help
Amazon Lambda Tools for .NET Core applications (5.1.4)
Project Home: https://github.com/aws/aws-extensions-for-dotnet-cli, https://github.com/aws/aws-lambda-dotnet

package-ci:
   Command to use as part of a continuous integration system.

   dotnet  lambda package-ci [options]
   Options:
      --disable-interactive                   When set to true missing required parameters will not be prompted for.
      --region                                The region to connect to AWS services, if not set region will be detected from the environment.
      --profile                               Profile to use to look up AWS credentials, if not set environment credentials will be used.
      --profile-location                      Optional override to the search location for Profiles, points at a shared credentials file.
      --aws-access-key-id                     The AWS access key id. Used when setting credentials explicitly instead of using --profile.
      --aws-secret-key                        The AWS secret key. Used when setting credentials explicitly instead of using --profile.
      --aws-session-token                     The AWS session token. Used when setting credentials explicitly instead of using --profile.
      -pl    | --project-location             The location of the project, if not set the current directory will be assumed.
      -cfg   | --config-file                  Configuration file storing default values for command line arguments.
      -pcfg  | --persist-config-file          If true the arguments used for a successful deployment are persisted to a config file.
      -c     | --configuration                Configuration to build with, for example Release or Debug.
      -f     | --framework                    Target framework to compile, for example netcoreapp3.1.
      --msbuild-parameters                    Additional msbuild parameters passed to the 'dotnet publish' command. Add quotes around the value if the value contains spaces.
      -t     | --template                     Path to the CloudFormation template
      -ts    | --template-substitutions       JSON based CloudFormation template substitutions. Format is <JSONPath>=<Substitution>;<JSONPath>=...
      -ot    | --output-template              Path to write updated serverless template with CodeURI fields updated to the location of the packaged build artifacts in S3.
      -sb    | --s3-bucket                    S3 bucket to upload the build output
      -sp    | --s3-prefix                    S3 prefix for for the build output
      -dvc   | --disable-version-check        Disable the .NET Core version check. Only for advanced usage.
normj commented 2 years ago

Hi @hauntingEcho

My thought process here is for deploy-serverless and package-ci the CloudFormation template is the "project". The Template can be used to deploy multiple .NET projects so tying project location to a specific .NET project doesn't make sense. I see where you are coming from and recognize my leap of thinking the CloudFormation template is the project is not clear and we should update the docs. As you found out the --project-location is a common switch so I don't have a mechanism in the tool to have special description for just those commands but I'll think on it if there is a solution we can do.

hauntingEcho commented 2 years ago

@normj a little off-topic, but is there any documentation you could link on using it with multiple .NET projects? That would massively simplify a few of our builds.

hauntingEcho commented 2 years ago

Digging into this, it looks like things just generally don't work right when the project is not in the same directory as the template:

Taking just the first two steps of the setup (rm src/lambda-example/serverless.yaml if needed from the rest) and running dotnet lambda package-ci -t serverless.yaml -sb ${bucket} -ot compiled.yml -pl src/lambda-example/ -t serverless.yaml gives "Template file C:\Users\Matt\test\lambda-example\src/lambda-example\serverless.yaml cannot be found.".

Following that with dotnet lambda package-ci -t serverless.yaml -sb ${bucket} -ot compiled.yml -pl src/lambda-example/ -t ../../serverless.yaml zips the whole root directory without building.

So there's a good chance my initial diagnosis was wrong too. It looks like:

if I'm understanding correctly?

normj commented 2 years ago

Here is the post talking about how to use multiple projects with the CloudFormation template. https://aws.amazon.com/blogs/developer/aws-serverless-applications-with-multiple-net-core-projects/

From what I understand your Lambda code is in src/lambda-example and the you want the CloudFormation template to be at src/serverless.yaml. Then in theory you could also have a second Lambda project here src/lambda-second-project that would be deployed with your CloudFormation template.

I would run the package-ci command like this which says the project location is the directory where the CloudFormation template is at and look in that directory for a file called serverless.yaml.

dotnet lambda package-ci -pl src/ -t serverless.yaml -ot compiled.yml

But then you need to make sure the functions in declared in the CloudFormation template use the CodeUri property to point to the local project that contains the .NET code. The path in the CodeUri is relative to this CloudFormation template. Here is an example of a CloudFormation template that defines 2 functions and the CodeUri points different project directories for each function.

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: An AWS Serverless Application.
Resources:
  lambda_example:
    Type: AWS::Serverless::Function
    Properties:
      Handler: LambdaExample::LambdaExample.Functions::Handler
      Runtime: dotnet6
      CodeUri: "./lambda_example"
      MemorySize: 256
      Timeout: 30
      Role:
      Policies:
      - AWSLambdaBasicExecutionRole
      Events:
        RootGet:
          Type: Api
          Properties:
            Path: "/foo"
            Method: GET
  lambda_second_project:
    Type: AWS::Serverless::Function
    Properties:
      Handler: LambdaSecondProject::LambdaSecondProject.Functions::Handler
      Runtime: dotnet6
      CodeUri: "./lambda_second_project"
      MemorySize: 256
      Timeout: 30
      Role:
      Policies:
      - AWSLambdaBasicExecutionRole
      Events:
        RootGet:
          Type: Api
          Properties:
            Path: "/bar"
            Method: GET
hauntingEcho commented 2 years ago

Ah! Using CodeUri like that should resolve my use-case. I hadn't re-read the spec since local folders became supported.

Does package-ci support zip file paths there? It looks like that's supported in some tools and not others, but that would resolve both my use case here and for #143 EDIT: it looks like CodeUri as a path to a zip is not supported by package-ci, but I can probably just use the SAM CLI for that. EDIT2: Nope, but aws cloudformation package will do it

ashishdhingra commented 2 years ago

@hauntingEcho Please confirm if guidance provided by @normj resolves your issue.

hauntingEcho commented 2 years ago

it gives me a workaround in which I don't need to use the project-location flag for my use-case, but doesn't resolve the issue with the help message