Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Steven Hollingsworth committed Jul 8, 2022
0 parents commit 5febe83
Show file tree
Hide file tree
Showing 49 changed files with 7,546 additions and 0 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**/node_modules
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.secrets.json
**/.serverless
**/*.tfstate
tf/.terraform/
backend/layer/
node_modules/
.in_secret
**/*.pyc
backend/.warmup

aws_resources.json
serverless.json
.in
.out
64 changes: 64 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
.DEFAULT_GOAL := help
.EXPORT_ALL_VARIABLES:
UID = $(shell id -u)
AWS_REGION = us-east-2
PROJECT_NAME = $(shell basename $(PWD))
PREFIX = $(shell jq -r '.application_namespace' < config.json)

help:
@echo "Available commands:"
@echo
@cat Makefile | grep '^\w.*:$$' | cut -d ':' -f 1 | grep -v '^help$$'

list:
cd backend/ && serverless deploy list functions --stage production --region ${REGION}

serverless_output:
aws cloudformation describe-stacks --stack-name ${PREFIX}-backend --query Stacks[0].Outputs | \
jq '. | map({(.OutputKey) : (.OutputValue)}) | add' | tee serverless.json

logs:
cd backend/ && serverless logs --stage production --region ${REGION} \
--function web -t --startTime 20m

destroy:
cd backend/ && serverless remove \
--stage production --region ${REGION}

diff:
cd backend/ && serverless diff \
--stage production --region ${REGION}

print:
cd backend/ && serverless print --stage production --region ${REGION} --verbose

deploy_only_serverless:
cd backend/ && \
serverless deploy --stage production --region ${REGION} --verbose

deploy_all: backend_layer deploy_only_serverless

gen_slack_manifest: serverless_output
./bin/gen_slack_manifest.sh > slack/manifest.yaml

build_image:
docker-compose \
-p ${PROJECT_NAME} \
--profile build \
--file ./localdev/docker-compose.yaml up \
--build

backend_layer: build_image
docker-compose \
-p ${PROJECT_NAME} \
--profile layer \
--file ./localdev/docker-compose.yaml up \
--force-recreate \
layer

local: build_image
docker-compose \
-p ${PROJECT_NAME} \
--profile local \
--file ./localdev/docker-compose.yaml up \
--remove-orphans
149 changes: 149 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# Demo Slack Bot app

## Required software
* gnu make
* terraform
* tfswitch
* docker
* docker-compose


# Pre Setup (making this your own)
## Terraform
edit [./tf/main.tf](./tf/main.tf) and adjust the region in the aws provider
block .

## Edit config.sjon
edit [./config.json](./config.json) and adjust the values as needed


## Initialize Environment

### Set your AWS REGION
```
export AWS_REGION="us-east-x"
```

### Poetry
`poetry upgrade`

## Run terraform
```
cd ./tf
tfswitch
terraform init
terraform plan -out plan.out
terraform apply plan.out
```

## initialize a blank ssm secret
```
./bin/crypt.sh create
```

## perform first deploy of of backend (needed to create slack app manifest)
```
make deploy_all
```

## Generate slack manifest
```
make gen_slack_manifest
```

## Setup Slack App
* Create two channels in your workspace, one as a primary channel for your app,
and another for exceptions the application may throw (for monitoring)
* ![Create Two Channels for app in your workspace](./media/create_slack_channels_for_app.png)

* Navigate to the slack apps [page](https://api.slack.com/apps/)
* Select "Create New App"
* ![Create New App](./media/create_new_app.png)
* Select "Create App from Manifest"
![Create app from manifest](./media/create_new_from_manifest.png)

* Select your desired workspace
* ![](./media/create_new_select_workspace.png)

* Copy the text in `./slack/manifest.yaml` generated from the previous command,
and past into the window
* ![Copy paste generated manifest data](./media/create_new_paste_manifest.png)

* Finalize your app creation
* ![Finalize](./media/create_new_finalize.png)

* Click "Install to Workspace"
* ![install to workspace](./media/install_app_start.png)

* Authorize App, and select your primary channel you created in a previous step
* ![select primary channel](./media/install_app_authorize.png)

* Go to "Incoming Webhooks" and click "Add New Webhook to workspace"
* ![add new webhook](./media/wh_add_new_webhook.png)

* Select the exception webhook you created earlier
* ![select exception webhook](./media/wh_create_exception_webhook.png)

* Edit your .secrets.json file, and populate with values from the various parts
of the application. You secrets json file should look like the first image.
* ![secrets edit](./media/creds_secrets_json_edit.png)
* ![webhooks](./media/creds_webhook_copy_paste.png)
* ![application creds](./media/creds_app.png)
* ![bot token](./media/creds_bot_token.png)

* Once all these are updated save the file and issue the command
*
```
./bin/crypt.sh save
```

* Once they are saved, go your "App Manifest" section, and click the verify
location
* ![verify](./media/verify_endpoint.png)

* At this point you should be done, save your source control, and follow the
steps below to maintain / update the application.



## Maintenance

# Deploy instructions

## Serverless

### updating / adding packages
```
## Upgrade existing packages
poetry upgrade
## add new direct packages (will be in layer)
poetry add xyz
## Add development package
poetry add -D foo
```

### Deploying layer changes
```
make deploy_all
```

### Deploying only code changes
```
make deploy_only_serverless
```

## Tailing logs
```
# Tail lambda web
make logs
```

## Updating Terraform

```
cd ./tf
terraform plan -out plan.out
terraform apply plan.out
```
Empty file added backend/api/__init__.py
Empty file.
47 changes: 47 additions & 0 deletions backend/api/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Configuration
"""
import json
import os

from aws_lambda_powertools.logging import Logger
import boto3
from botocore.exceptions import UnauthorizedSSOTokenError

NAMESPACE = os.environ["NAMESPACE"]
logger = Logger(service=NAMESPACE)

SSM_NAME=f"/{NAMESPACE}/secrets.json"
ENV_SLASH_COMMAND = os.environ["SLACK_COMMAND"]
SLACK_SLASH_COMMAND = f"/{ENV_SLASH_COMMAND}"

LOG_LEVEL = os.environ.get("LOG_LEVEL", "INFO")
DEBUG = os.environ["DEBUG"] == "true"
BUCKET = os.environ["BUCKET"]
MOCK = os.environ.get("MOCK") == "true"


def _get_jdata():
return (
boto3.Session()
.client("ssm")
.get_parameter(
Name=SSM_NAME, WithDecryption=True
)["Parameter"]["Value"]
)

try:
_SECRET_DATA = json.loads(_get_jdata())
except UnauthorizedSSOTokenError as err:
raise SystemExit("Unauthorized SSO token, please re-authenticate") from err


SLACK_APP_ID = _SECRET_DATA["SLACK_APP_ID"]
SLACK_CLIENT_SECRET = _SECRET_DATA["SLACK_CLIENT_SECRET"]
SLACK_CLIENT_ID = _SECRET_DATA["SLACK_CLIENT_ID"]
SLACK_SIGNING_SECRET = _SECRET_DATA["SLACK_SIGNING_SECRET"]
SLACK_EXCEPTIONS_WEBHOOK = _SECRET_DATA["SLACK_EXCEPTIONS_WEBHOOK"]
SLACK_BOT_TOKEN = _SECRET_DATA["SLACK_BOT_TOKEN"]
SLACK_WEBHOOK = _SECRET_DATA["SLACK_WEBHOOK"]
Empty file added backend/api/lib/__init__.py
Empty file.
37 changes: 37 additions & 0 deletions backend/api/lib/s3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""S3 lib."""
import json
import time

from aws_lambda_powertools.logging import Logger
import boto3
from api import config
from mypy_boto3_s3.service_resource import Bucket

LOG = Logger(service="mangum")


def get_bucket() -> Bucket:
session = boto3.Session()
return session.resource("s3").Bucket(config.BUCKET) # type: ignore


def upload_github_event_json_to_s3(
event: str,
action: str,
headers: dict,
payload: dict,
):
bucket = get_bucket()
ts = time.time()
key = f"payload/{ts}_{event}_{action}.json"
json_data = {
"headers": headers,
"payload": payload,
}
bucket.put_object(
Key=key,
Body=json.dumps(json_data),
)
LOG.info(f"Uploaded {key} to S3")
Loading

0 comments on commit 5febe83

Please sign in to comment.