Skip to content

Commit

Permalink
marlowe playground lambda
Browse files Browse the repository at this point in the history
We have moved to AWS lambda for everything that we can in the marlowe
playground, we are also using Lambda proxy features so that the lambda
part is just a wrapper around a servant API. This means we can run a
warp server for local development.

We still have some work to do going forward but I want to avoid a bigger
PR:
* move from API gateway to AWS ALB since it is better for dealing with
websockets and som other stuff that we might need soon
* create a server that has permissions to run terraform so we can start
continuous delivery again
  • Loading branch information
shmish111 committed Sep 21, 2020
1 parent d33cf87 commit 901df98
Show file tree
Hide file tree
Showing 57 changed files with 706 additions and 1,011 deletions.
1 change: 1 addition & 0 deletions cabal.project
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ packages: plutus-core
playground-common
marlowe-actus
marlowe-playground-server
marlowe-playground-lambda
marlowe-symbolic
plutus-contract
deployment-server
Expand Down
4 changes: 3 additions & 1 deletion default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,10 @@ in rec {
});

marlowe-symbolic-lambda = pkgsMusl.callPackage ./marlowe-symbolic/lambda.nix { haskellPackages = haskell.muslPackages; };

marlowe-playground-lambda = pkgsMusl.callPackage ./marlowe-playground-server/lambda.nix { haskellPackages = haskell.muslPackages; };

deployment = pkgs.callPackage ./deployment { inherit marlowe-playground marlowe-symbolic-lambda; };
deployment = pkgs.callPackage ./deployment { inherit marlowe-playground marlowe-symbolic-lambda marlowe-playground-lambda; };

inherit (haskell.packages.plutus-scb.components.exes) plutus-game plutus-currency;

Expand Down
6 changes: 4 additions & 2 deletions deployment/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ A website is served from AWS API Gateway which will proxy to the following parts

### Getting Started

If you are using OSX then you cannot build the lambdas locally, therefore if you want to update the infrastructure you will need to build the lambdas on a linux machine and then copy them to a location on your machine. Then you can set the env var `export TF_VAR_symbolic_lambda_file=/path/to/marlowe-symbolic.zip`.
If you are using OSX then you cannot build the lambdas locally, therefore if you want to update the infrastructure you will need to build the lambdas on a linux machine and then copy them to a location on your machine. Then you can set the env vars `export TF_VAR_symbolic_lambda_file=/path/to/marlowe-symbolic.zip` and `export TF_VAR_playground_lambda_file=/path/to/marlowe-playground-lambda.zip`

If you have not setup AWS authentication but you have enabled MFA then you can run `$(nix-build -A deployment.david.getCreds) user.name 123456` (where 123456 is the current MFA code) before you run any other command to setup temporary credentials that are valid for 24 hours.
If you have not setup AWS authentication but you have enabled MFA then you can run `eval $($(nix-build -A deployment.getCreds) user.name 123456)` (where 123456 is the current MFA code) before you run any other command to setup temporary credentials that are valid for 24 hours. Notice that you use `$()` to evaluate the result of the nix build (which is a shell script) and then you use `eval $()` around that result to evaluate the output of the script.

The scripts produce files for use with nixops (until we get rid of the legacy infra) and so you should provide the location where you want these files to go by setting another terraform variable, e.g. `export TF_VAR_nixops_root=$(pwd)/deployment/nixops`.

Expand All @@ -30,6 +30,8 @@ The infrastructure is based around multiple environments, for example `alpha`, `
* `deployment.env.terraform-locals` will produce `generated.tf.json` which contains locals such as `env`
* `deployment.env.terraform-vars` will produce `env.tfvars` which contains variables such as `symbolic_lambda_file` if you are not on OSX

The scripts require some secrets which are stored encrypted in this repository. To access them you will need to provide your gpg public key to someone who already has access to the secrets.

## Legacy Infrastructure

The legacy infrastructure is comprised of 2 parts, terraform and nixops:
Expand Down
30 changes: 21 additions & 9 deletions deployment/default.nix
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{ pkgs, marlowe-playground, marlowe-symbolic-lambda }:
{ pkgs, marlowe-playground, marlowe-symbolic-lambda, marlowe-playground-lambda }:
with pkgs;
let
# 20.03 version of terraform is broken on some versions of OSX so I have copied the last 0_12 version from nixpkgs
Expand All @@ -14,9 +14,8 @@ let
unset AWS_SECRET_ACCESS_KEY
unset AWS_ACCESS_KEY_ID
result=$(${awscli}/bin/aws sts get-session-token --serial-number arn:aws:iam::454236594309:mfa/$1 --output text --duration-seconds 86400 --token-code $2 \
| awk '{printf("export AWS_ACCESS_KEY_ID=\"%s\"\nexport AWS_SECRET_ACCESS_KEY=\"%s\"\nexport AWS_SESSION_TOKEN=\"%s\"\n",$2,$4,$5)}')
eval $result
${awscli}/bin/aws sts get-session-token --serial-number arn:aws:iam::454236594309:mfa/$1 --output text --duration-seconds 86400 --token-code $2 \
| awk '{printf("export AWS_ACCESS_KEY_ID=%s\nexport AWS_SECRET_ACCESS_KEY=\"%s\"\nexport AWS_SESSION_TOKEN=\"%s\"\n",$2,$4,$5)}'
'';

terraform-locals = env:
Expand All @@ -36,10 +35,14 @@ let
env="${env}"
aws_region="${region}"
'' + (if pkgs.stdenv.isDarwin then
# On OSX you need to set the env vars with the locations of the lambda zip files e.g.
# export TF_VAR_symbolic_lambda_file=/path/to/marlowe-symbolic.zip
# export TF_VAR_playground_lambda_file=/path/to/marlowe-playground-lambda.zip
""
else
''
symbolic_lambda_file="${marlowe-symbolic-lambda}/marlowe-symbolic.zip"'');
symbolic_lambda_file="${marlowe-symbolic-lambda}/marlowe-symbolic.zip"
playground_lambda_file="${marlowe-playground-lambda}/marlowe-playground-lambda.zip"'');
};

runTerraform = env: region:
Expand All @@ -49,14 +52,20 @@ let
ln -s ${terraform-locals env}/* $tmp_dir
ln -s ${terraform-vars env region}/* $tmp_dir
cd $tmp_dir
export TF_VAR_marlowe_github_client_id=$(pass ${env}/marlowe/githubClientId)
export TF_VAR_marlowe_github_client_secret=$(pass ${env}/marlowe/githubClientSecret)
export TF_VAR_marlowe_jwt_signature=$(pass ${env}/marlowe/jwtSignature)
${terraform}/bin/terraform init
${terraform}/bin/terraform workspace select ${env}
${terraform}/bin/terraform apply -var-file=${env}.tfvars
'';

syncS3 = env:
writeShellScript "syncs3"
"${awscli}/bin/aws s3 sync ${marlowe-playground.client} s3://marlowe-playground-website-${env}/";
writeShellScript "syncs3" ''
${awscli}/bin/aws s3 sync --delete ${marlowe-playground.client} s3://marlowe-playground-website-${env}/
# We do a sync to delete any files that have been removed, just to keep things clean, then we do a recursive cp
# because sync doesn't update files with the same file size and timestamp
${awscli}/bin/aws s3 cp --recursive ${marlowe-playground.client} s3://marlowe-playground-website-${env}/'';

deploy = env: region:
writeShellScript "deploy" ''
Expand All @@ -74,6 +83,9 @@ let
cd $tmp_dir
echo "apply terraform"
export TF_VAR_marlowe_github_client_id=$(pass ${env}/marlowe/githubClientId)
export TF_VAR_marlowe_github_client_secret=$(pass ${env}/marlowe/githubClientSecret)
export TF_VAR_marlowe_jwt_signature=$(pass ${env}/marlowe/jwtSignature)
${terraform}/bin/terraform init
${terraform}/bin/terraform workspace select ${env}
${terraform}/bin/terraform apply -var-file=${env}.tfvars
Expand All @@ -90,7 +102,7 @@ let
'';

mkEnv = env: region: {
inherit getCreds terraform-vars terraform-locals terraform;
inherit terraform-vars terraform-locals terraform;
syncS3 = (syncS3 env);
runTerraform = (runTerraform env region);
deploy = (deploy env region);
Expand All @@ -100,4 +112,4 @@ let
david = mkEnv "david" "eu-west-1";
alpha = mkEnv "alpha" "eu-west-2";
};
in envs
in envs // { inherit getCreds; }
90 changes: 82 additions & 8 deletions deployment/terraform/gateway.tf
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,17 @@ resource "aws_lambda_permission" "marlowe_symbolic_lambda_api_gw" {
source_arn = "${aws_api_gateway_rest_api.marlowe_symbolic_lambda.execution_arn}/*/*"
}

resource "aws_lambda_permission" "marlowe_playground_lambda_api_gw" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.marlowe_playground.function_name
principal = "apigateway.amazonaws.com"

# The "/*/*" portion grants access from any method on any resource
# within the API Gateway REST API.
source_arn = "${aws_api_gateway_rest_api.marlowe_symbolic_lambda.execution_arn}/*/*"
}

resource "aws_api_gateway_usage_plan" "marlowe_symbolic_lambda" {
name = "marlowe_symbolic_lambda_${var.env}"

Expand All @@ -86,25 +97,88 @@ resource "aws_api_gateway_usage_plan" "marlowe_symbolic_lambda" {
}

resource "aws_api_gateway_domain_name" "marlowe_symbolic_lambda" {
# AWS certificate wildcards can only have one level so we cannot use lambda.alpha.marlowe.iohk.io
# we must use lambda-alpha.marlowe.iohk.io instead
domain_name = "lambda-${local.marlowe_domain_name}"
domain_name = local.marlowe_domain_name

regional_certificate_arn = "${data.aws_acm_certificate.marlowe_private.arn}"
regional_certificate_arn = data.aws_acm_certificate.marlowe_private.arn
endpoint_configuration {
types = ["REGIONAL"]
}
}

resource "aws_api_gateway_base_path_mapping" "marlowe_symbolic_lambda" {
# The path, if not specified, is `/` by default
api_id = "${aws_api_gateway_rest_api.marlowe_symbolic_lambda.id}"
stage_name = "${aws_api_gateway_deployment.marlowe_symbolic_lambda.stage_name}"
domain_name = "${aws_api_gateway_domain_name.marlowe_symbolic_lambda.domain_name}"
api_id = aws_api_gateway_rest_api.marlowe_symbolic_lambda.id
stage_name = aws_api_gateway_deployment.marlowe_symbolic_lambda.stage_name
domain_name = aws_api_gateway_domain_name.marlowe_symbolic_lambda.domain_name
}

resource "aws_api_gateway_resource" "api" {
rest_api_id = aws_api_gateway_rest_api.marlowe_symbolic_lambda.id
parent_id = aws_api_gateway_rest_api.marlowe_symbolic_lambda.root_resource_id
path_part = "api"
}

resource "aws_api_gateway_resource" "proxy" {
depends_on = [
aws_api_gateway_resource.api,
]
rest_api_id = aws_api_gateway_rest_api.marlowe_symbolic_lambda.id
parent_id = aws_api_gateway_resource.api.id
path_part = "{proxy+}"
}

resource "aws_api_gateway_method" "proxy" {
rest_api_id = aws_api_gateway_rest_api.marlowe_symbolic_lambda.id
resource_id = aws_api_gateway_resource.proxy.id
http_method = "ANY"
authorization = "NONE"
}

resource "aws_api_gateway_integration" "proxy" {
rest_api_id = aws_api_gateway_rest_api.marlowe_symbolic_lambda.id
resource_id = aws_api_gateway_method.proxy.resource_id
http_method = aws_api_gateway_method.proxy.http_method

integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.marlowe_playground.invoke_arn

request_parameters = {
}
}

# runghc proxy
resource "aws_api_gateway_resource" "runghc" {
depends_on = [
aws_api_gateway_resource.api,
]
rest_api_id = aws_api_gateway_rest_api.marlowe_symbolic_lambda.id
parent_id = aws_api_gateway_rest_api.marlowe_symbolic_lambda.root_resource_id
path_part = "runghc"
}

resource "aws_api_gateway_method" "runghc" {
rest_api_id = aws_api_gateway_rest_api.marlowe_symbolic_lambda.id
resource_id = aws_api_gateway_resource.runghc.id
http_method = "POST"
authorization = "NONE"
}

resource "aws_api_gateway_integration" "runghc" {
rest_api_id = aws_api_gateway_rest_api.marlowe_symbolic_lambda.id
resource_id = aws_api_gateway_method.runghc.resource_id
http_method = aws_api_gateway_method.runghc.http_method

integration_http_method = "POST"
type = "HTTP_PROXY"
uri = "http://${aws_alb.plutus.dns_name}/runghc"

request_parameters = {
}
}

resource "aws_route53_record" "lambda" {
name = "lambda-${local.marlowe_domain_name}"
name = local.marlowe_domain_name
type = "A"
zone_id = "${var.marlowe_public_zone}"
alias {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ resource "aws_iam_role_policy_attachment" "marlowe_symbolic_lambda" {
resource "aws_lambda_function" "marlowe_symbolic" {
function_name = "marlowe_symbolic_${var.env}"
role = "${aws_iam_role.marlowe_symbolic_lambda.arn}"
handler = "src/App.handler"
handler = "src/Marlowe/Symbolic/Lambda.handler"

runtime = "provided"

Expand All @@ -72,4 +72,31 @@ resource "aws_lambda_function" "marlowe_symbolic" {
PATH = "/usr/local/bin:/usr/bin/:/bin:/opt/bin:."
}
}
}

resource "aws_lambda_function" "marlowe_playground" {
function_name = "marlowe_playground_${var.env}"
role = "${aws_iam_role.marlowe_symbolic_lambda.arn}"
handler = "src/Lambda.handler"

runtime = "provided"

# The lambda zip needs to be built and placed on your local filesystem
# TODO: This is only a temporary requirement and will be moved to the CD server soon
filename = "${var.playground_lambda_file}"
source_code_hash = filebase64sha256(var.playground_lambda_file)

memory_size = 3008
timeout = 120
publish = true

environment {
variables = {
PATH = "/usr/local/bin:/usr/bin/:/bin:/opt/bin:."
GITHUB_CLIENT_ID = var.marlowe_github_client_id
GITHUB_CLIENT_SECRET = var.marlowe_github_client_secret
JWT_SIGNATURE = var.marlowe_jwt_signature
GITHUB_REDIRECT_URL = "https://${var.env}.${var.marlowe_tld}"
}
}
}
Loading

0 comments on commit 901df98

Please sign in to comment.