diff --git a/blog/2023-06-17-lambda-console-specification-proposal.mdx b/blog/2023-06-17-lambda-console-specification-proposal.mdx new file mode 100644 index 0000000..8f19dcf --- /dev/null +++ b/blog/2023-06-17-lambda-console-specification-proposal.mdx @@ -0,0 +1,81 @@ +--- +slug: lambda-console-a-specification-proposal +title: The Elusive Lambda Console; A Specification Proposal. +authors: [kcollins] +tags: [rails, lambda, console, specification, runner, tasks, interaction] +--- + +import Header from '@site/static/img/blog/console/header.png'; +import ThemedImage from "@theme/ThemedImage"; +import useBaseUrl from "@docusaurus/useBaseUrl"; + + + + + +After years of smashing Cloud & Rails together, I've come up with an idea. Better than an idea, a working specification! One where us [Rails & Lambda](https://lamby.cloud) enthusiasts can once again "console into" our "servers" and execute CLI tasks like migrations or interact via our beloved IRB friend, the Rails console. Today, I would like to present, the [Lambda Console](https://github.com/rails-lambda/lambda-console) project. An open specification proposal for any AWS Lambda runtime to adopt. + + + + + + + +## Lambda Console + +```shell +npm install -g lambda-console-cli +``` + +The Lambda Console is a CLI written in Node.js that will interactively create an AWS SDK session for you to invoke your Lambda functions with two types of modes. + +1. CLI Runner +2. Interactive Commands + +Think of the CLI Runner as a bash prompt. You can run any process command or interact with the filesystem or environment. For Rails users, running rake tasks or DB migrations. Running these tasks automatically assume the Lambda task root as the present working directory. + +Interactive commands are evaluated in the context of your running application. For Ruby and Rails applications, this simulates IRB (Interactive Ruby Shell). For [Lamby](https://lamby.cloud) users, this mode simulates the Rails console. Making it easy for users to query their DB or poke their models and code. + +## The Proposal + +There is nothing about the [Lambda Console](https://github.com/rails-lambda/lambda-console) that is coupled to Ruby or Rails. The idea is simple, as a Lambda community, could do the following? + +1. Finalize a Lambda Console request/response specification. +2. Create more runtime-specific language implementations. +2. Build an amazing CLI client for any runtime. + +Here is what we have today, the request specification is very simple, the [event structure](https://github.com/rails-lambda/lambda-console#event-structure) is only a few dozen lines of JSON schema. + +```json +{ "X_LAMBDA_CONSOLE": { "run": "cat /etc/os-release" } } +``` + +```json +{ "X_LAMBDA_CONSOLE": { "interact": "User.find(1)" } } +``` + +Any Lambda runtime code or framework could implment handling these event in their own language-specific pakages. Ruby makes these really easy and you can find them in the Lambda Console's first reference implementations. + +* Ruby: The [lambda-console-ruby](https://github.com/rails-lambda/lambda-console-ruby) gem for any Ruby Lambda. +* Rails: Integrated into the [Lamby](https://github.com/rails-lambda/lamby) v5.0.0 for Rails on Lambda. + +## The Possibilities + +What I really want is an amazing CLI client. The current Lambda Console CLI was hacked together in a few days using some amazing Node.js tools that make building interactive CLIs so so easy. But I've never done this before. If this type of tooling sounds interesting to you and you like Node.js, let me know! Here are some ideas on where I could see this going. + +**Live STDOUT & STDERR:** We could take advantage of Lambda's new [Response Streaming](https://aws.amazon.com/blogs/compute/introducing-aws-lambda-response-streaming/) and send output buffers as they happen. + +**Pseudo TTY:** Is there a way to better simulate a real TTY session? Could this even include ANSI colors? + +**Quality of Life Improvements:** Everything from, Allowing the CLI tool to switch modes without restarting it; Creating a command buffer to up arrow navigate history; Prettier UI. + +**Formal Response JSON Schema:** As the features grow, should the response JSON be standardized? For example, if the client wanted to syntax highlight interactive language commands, how would it know what language was being used? We could have a `X_LAMBDA_CONSOLE_LANG` response header. + +What else would you like to see in a Lambda Console client? + diff --git a/docs/running-tasks.mdx b/docs/running-tasks.mdx index 189e12b..bd9fe1b 100644 --- a/docs/running-tasks.mdx +++ b/docs/running-tasks.mdx @@ -1,78 +1,55 @@ --- id: running-tasks -title: Running Tasks -toc_max_heading_level: 2 +title: Running Tasks & Console +toc_max_heading_level: 3 --- -# Running Tasks +import ThemedImage from "@theme/ThemedImage"; +import useBaseUrl from "@docusaurus/useBaseUrl"; -It can be common for Rails engineers to fire up the [Rails console](https://guides.rubyonrails.org/command_line.html#bin-rails-console) for some quick debugging or to run code like a Rake task. That said, console'ing into a Lambda function is simply not an option and requires a different solution for on-demand tasks. +It can be common for Rails engineers to fire up the [Rails console](https://guides.rubyonrails.org/command_line.html#bin-rails-console) for some quick debugging or to run code like a Rake task. That said, console'ing into a Lambda function (typically done via SSH) is not possible and requires an event-driven & stateless solution. For this, we have the [Lambda Console](https://github.com/rails-lambda/lambda-console) tool. -## Lamby Runner Event + + + -Running a task on your Rails application can be done by sending your Lambda an event in the following format. This event can happen in any way that makes sense for you. For example, you could use the [AWS CLI's invoke](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/invoke.html) feature in a deploy pipeline. Another simple method would be to use the [test feature for Lambda](https://aws.amazon.com/blogs/compute/improved-testing-on-the-aws-lambda-console/) within the AWS Console. You cold even use a [GitHub Action](#github-actions-invoke-example) +💁‍♂️ https://github.com/rails-lambda/lambda-console -```json -{ - "lamby": { - "runner": "./bin/rails db:migrate" - } -} -``` +Lamby leverages the Lambda Console using our Ruby implementation of the spec via the [lambda-console-ruby](https://github.com/rails-lambda/lambda-console-ruby) gem. Here is a quick overview on how to use it for common Rails tasks. Please see the [Lambda Console](https://github.com/rails-lambda/lambda-console) project for complete documentation on the CLI installation and usage. -## Customizing Runner Patterns +:::caution +To use the Lambda Console, please make sure you are using Lamby v5.0.0 or higher. +::: -The runner has a single default expression check of `%r{\A\./bin/(rails|rake) db:migrate.*}` which limits the type of commands executed. This can be cleared to deny any command(s) or you can add addition pattern matchers via the Lamby config. To do so, add lines like these in your `app.rb` file below the `require 'lamby'`. For example, the first line would clear all patterns and allow no commands, the second would allow a simple bin file to be added. +## Common Considerations -```ruby -Lamby.config.runner_patterns.clear -Lamby.config.runner_patterns.push %r{\A/bin/foo.*} -``` +Here are some common considerations when using the [Lambda Console](https://github.com/rails-lambda/lambda-console) to run tasks or interactive commands. -## Function Timeout Property +### Function Timeout -You may want to consider changing the `Timeout` property of your `RailsLambda` resource in your `template.yaml` from 30s to something longer. A Lambda function can have a maximum of 15m execution time. Just remember that API Gateway integration will always be limited to 30s under the function's timeout. So these timeouts can operate independently. +Each `run` or `interact` event sent will need to respond within your function's timeout. Since HTTP interactions via most AWS services are limited to 30s, so too is your function's default timeout set to that. If your task takes longer than this, consider temporarily increasing the value in your Cloud Formation template or duplicating your function (copy paste) to a new Lambda Function resource dedicated for running console tasks. A Lambda function can have a maximum of 15m execution time. Just remember that API Gateway integration will always be limited to 30s under the function's timeout. So these timeouts can operate independently. -## GitHub Actions Invoke Example +### IAM Security & Permissions -Similar to the [Deploy with GitHub Actions](/docs/quick-start) example in our guide, here is an `invoke.yml` workflow example that uses the AWS CLI to invoke your function with the needed JSON event format detailed above. Here is an example `payload` option you could paste into the workflow dispatch window. Note that quotes have to be escaped to flow through to GitHub Actions. +The [Lambda Console](https://github.com/rails-lambda/lambda-console) leverages AWS SDKs to send invoke events to your function(s). This means you are in full control of the security of your function and whom can invoke it with the following IAM actions for your user or role: -```json -{"lamby":{"runner":"./bin/rails runner \\"puts(\'HELLO\')\\""}} -``` +- `lambda:ListFunctions` +- `lambda:InvokeFunction` + +### Customizing Runner Patterns -This example above uses [Rails runner](https://guides.rubyonrails.org/command_line.html#bin-rails-runner) to invoke arbitrary code in a non-interactive way. ⚠️ Please ensure the `--stack-name` value below matches the same value in your `bin/_deploy` file and replace the `myapp-production` value with that. - -```yaml -name: Invoke -on: - workflow_dispatch: - inputs: - payload: - description: JSON Event Payload - required: true -jobs: - invoke: - runs-on: ubuntu-latest - steps: - - name: Install AWS CLI - uses: unfor19/install-aws-cli-action@v1 - - name: Configure AWS - uses: aws-actions/configure-aws-credentials@v1 - with: - aws-access-key-id: { { "${{ secrets.AWS_ACCESS_KEY_ID }}" } } - aws-secret-access-key: { { "${{ secrets.AWS_SECRET_ACCESS_KEY }}" } } - aws-region: us-east-1 - - name: Invoke - run: | - echo $'${{ github.event.inputs.payload }}' > payload.json - FUNCTION_NAME=$(aws cloudformation describe-stack-resources \ - --stack-name "myapp-production" \ - --query "StackResources[?LogicalResourceId=='RailsLambda'].PhysicalResourceId" \ - --output text) - aws lambda invoke \ - --function-name "$FUNCTION_NAME" \ - --cli-binary-format raw-in-base64-out \ - --payload file://./payload.json \ - /dev/stdout | jq -r .body +By default, Lamby v5 and higher allows any command to be run. If you want to enforce which commands can be run at the application layer, please use the Lamby config in your `production.rb` environment file. + +```ruby +config.lamby.runner_patterns.clear +config.lamby.runner_patterns.push %r{\A/bin/foo.*} ``` + +Here are are clearning/removing the deafault expression pattern of `/.*/` in favor of one that allows any `/bin/foo` command to be run. + diff --git a/static/img/blog/console/header.png b/static/img/blog/console/header.png new file mode 100644 index 0000000..ba82b09 Binary files /dev/null and b/static/img/blog/console/header.png differ diff --git a/static/img/docs/lambda-console-cli-dark.png b/static/img/docs/lambda-console-cli-dark.png new file mode 100644 index 0000000..920855a Binary files /dev/null and b/static/img/docs/lambda-console-cli-dark.png differ diff --git a/static/img/docs/lambda-console-cli-light.png b/static/img/docs/lambda-console-cli-light.png new file mode 100644 index 0000000..c67e4af Binary files /dev/null and b/static/img/docs/lambda-console-cli-light.png differ