Skip to content

Commit

Permalink
Merge pull request #65 from rails-lambda/LambdaConsole
Browse files Browse the repository at this point in the history
The Lambda Console Changes
  • Loading branch information
metaskills committed Jun 17, 2023
2 parents dc4c3d2 + 3a27e7b commit 642c9f0
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 60 deletions.
81 changes: 81 additions & 0 deletions blog/2023-06-17-lambda-console-specification-proposal.mdx
Original file line number Diff line number Diff line change
@@ -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";

<a href="https://github.com/rails-lambda/lambda-console">
<img src={Header} />
</a>

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.

<!--truncate-->

<a href="https://github.com/rails-lambda/lambda-console">
<ThemedImage
alt="Lamby: Simple Rails & AWS Lambda Integration using Rack"
sources={{
light: useBaseUrl("/img/docs/lambda-console-cli-light.png"),
dark: useBaseUrl("/img/docs/lambda-console-cli-dark.png"),
}}
/>
</a>

## 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?

97 changes: 37 additions & 60 deletions docs/running-tasks.mdx
Original file line number Diff line number Diff line change
@@ -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
<a href="https://github.com/rails-lambda/lambda-console">
<ThemedImage
alt="Lamby: Simple Rails & AWS Lambda Integration using Rack"
sources={{
light: useBaseUrl("/img/docs/lambda-console-cli-light.png"),
dark: useBaseUrl("/img/docs/lambda-console-cli-dark.png"),
}}
/>
</a>

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 <DocLink id="quick-start" /> 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.

Binary file added static/img/blog/console/header.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/img/docs/lambda-console-cli-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/img/docs/lambda-console-cli-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 642c9f0

Please sign in to comment.