Skip to content

Commit

Permalink
Merge pull request #1541 from ministryofjustice/MLPAB-2408-run-schedu…
Browse files Browse the repository at this point in the history
…led-events

MLPAB-2408: Add scheduled task schedule runner
  • Loading branch information
acsauk authored Oct 16, 2024
2 parents d3f3f5f + 818938e commit 76accfb
Show file tree
Hide file tree
Showing 39 changed files with 907 additions and 382 deletions.
2 changes: 2 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ coverage:
- "./cmd/mock-notify/main.go"
- "./cmd/mock-onelogin/main.go"
- "./cmd/mock-os-api/main.go"
- "./cmd/schedule-runner/main.go"
- "./internal/identity/yoti*"
- "./internal/notify/email.go"
- "./internal/notify/sms.go"
- "./internal/page/fixtures"
- "./internal/telemetry"
- "./internal/validation/error.go"
- "./mocks/*"
- "./scripts/*.go"
status:
project:
default:
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/docker_job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ jobs:
path: ./docker/mock-pay/Dockerfile
trivyignores: ./docker/mock-pay/.trivyignore.yaml
platforms: linux/amd64
- ecr_repository: modernising-lpa/schedule-runner
name: schedule-runner
path: ./docker/schedule-runner/Dockerfile
trivyignores: ./docker/schedule-runner/.trivyignore.yaml
platforms: linux/amd64

runs-on: ubuntu-latest
name: ${{ matrix.ecr_repository }}
Expand Down
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,8 @@ set-uploads-infected: ##@events calls emit-object-tags-added-with-virus for all
key=$$k $(MAKE) emit-object-tags-added-with-virus ; \
done

tail-logs: ##@app tails logs for app mock-notify, events-lambda, mock-onelogin, mock-lpa-store and mock-uid and filters out noisy runner logs
docker compose --ansi=always -f docker/docker-compose.yml -f docker/docker-compose.dev.yml logs app mock-notify events-lambda mock-onelogin mock-lpa-store mock-uid -f | grep -v 'runner'
tail-logs: ##@app tails logs for app mock-notify, events-lambda, schedule-runner-lambda, mock-onelogin, mock-lpa-store and mock-uid
docker compose --ansi=always -f docker/docker-compose.yml -f docker/docker-compose.dev.yml logs app mock-notify events-lambda schedule-runner-lambda mock-onelogin mock-lpa-store mock-uid -f

terraform-update-docs: ##@terraform updates all terraform-docs managed documentation
terraform-docs --config terraform/environment/.terraform-docs.yml ./terraform/environment
Expand All @@ -173,3 +173,6 @@ delete-all-from-lpa-index: ##@opensearch clears all items from the lpa index

delete-lpa-index: ##@opensearch deletes the lpa index
curl -XDELETE "http://localhost:9200/lpas"

add-scheduled-events:
go run ./scripts/add-scheduled-items.go
19 changes: 0 additions & 19 deletions cmd/mlpa/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"github.com/ministryofjustice/opg-go-common/template"
"github.com/ministryofjustice/opg-modernising-lpa/internal/actor"
"github.com/ministryofjustice/opg-modernising-lpa/internal/app"
"github.com/ministryofjustice/opg-modernising-lpa/internal/donor"
"github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo"
"github.com/ministryofjustice/opg-modernising-lpa/internal/event"
"github.com/ministryofjustice/opg-modernising-lpa/internal/lambda"
Expand All @@ -35,7 +34,6 @@ import (
"github.com/ministryofjustice/opg-modernising-lpa/internal/pay"
"github.com/ministryofjustice/opg-modernising-lpa/internal/place"
"github.com/ministryofjustice/opg-modernising-lpa/internal/s3"
"github.com/ministryofjustice/opg-modernising-lpa/internal/scheduled"
"github.com/ministryofjustice/opg-modernising-lpa/internal/search"
"github.com/ministryofjustice/opg-modernising-lpa/internal/secrets"
"github.com/ministryofjustice/opg-modernising-lpa/internal/sesh"
Expand Down Expand Up @@ -116,14 +114,8 @@ func run(ctx context.Context, logger *slog.Logger) error {
searchEndpoint = os.Getenv("SEARCH_ENDPOINT")
searchIndexName = cmp.Or(os.Getenv("SEARCH_INDEX_NAME"), "lpas")
searchIndexingEnabled = os.Getenv("SEARCH_INDEXING_DISABLED") != "1"
scheduledRunnerPeriod = cmp.Or(os.Getenv("SCHEDULED_RUNNER_PERIOD"), "6h")
)

scheduledRunnerPeriodDur, err := time.ParseDuration(scheduledRunnerPeriod)
if err != nil {
return err
}

staticHash, err := dirhash.HashDir(webDir+"/static", webDir, dirhash.DefaultHash)
if err != nil {
return err
Expand Down Expand Up @@ -343,17 +335,6 @@ func run(ctx context.Context, logger *slog.Logger) error {
handler = telemetry.WrapHandler(mux)
}

donorStore := donor.NewStore(lpasDynamoClient, eventClient, logger, searchClient)
scheduledStore := scheduled.NewStore(lpasDynamoClient)

runner := scheduled.NewRunner(logger, scheduledStore, donorStore, notifyClient, scheduledRunnerPeriodDur)
go func() {
if err := runner.Run(ctx); err != nil {
logger.Error("runner error", slog.Any("err", err))
os.Exit(1)
}
}()

server := &http.Server{
Addr: ":" + port,
Handler: page.Recover(tmpls.Get("error-500.gohtml"), logger, bundle, handler),
Expand Down
92 changes: 92 additions & 0 deletions cmd/schedule-runner/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package main

import (
"cmp"
"context"
"fmt"
"log/slog"
"net/http"
"os"
"time"

"github.com/aws/aws-lambda-go/lambda"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/ministryofjustice/opg-modernising-lpa/internal/donor"
"github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo"
"github.com/ministryofjustice/opg-modernising-lpa/internal/event"
"github.com/ministryofjustice/opg-modernising-lpa/internal/localize"
"github.com/ministryofjustice/opg-modernising-lpa/internal/notify"
"github.com/ministryofjustice/opg-modernising-lpa/internal/scheduled"
"github.com/ministryofjustice/opg-modernising-lpa/internal/search"
"github.com/ministryofjustice/opg-modernising-lpa/internal/secrets"
)

func handleRunSchedule(ctx context.Context) error {
var (
eventBusName = cmp.Or(os.Getenv("EVENT_BUS_NAME"), "default")
notifyBaseURL = os.Getenv("GOVUK_NOTIFY_BASE_URL")
notifyIsProduction = os.Getenv("GOVUK_NOTIFY_IS_PRODUCTION") == "1"
searchEndpoint = os.Getenv("SEARCH_ENDPOINT")
searchIndexName = cmp.Or(os.Getenv("SEARCH_INDEX_NAME"), "lpas")
searchIndexingEnabled = os.Getenv("SEARCH_INDEXING_DISABLED") != "1"
tableName = os.Getenv("LPAS_TABLE")
)

logger := slog.New(slog.NewJSONHandler(os.Stdout, nil).
WithAttrs([]slog.Attr{
slog.String("service_name", "opg-modernising-lpa/schedule-runner"),
}))

cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
return fmt.Errorf("failed to load default config: %w", err)
}

secretsClient, err := secrets.NewClient(cfg, time.Hour)
if err != nil {
return err
}

notifyApiKey, err := secretsClient.Secret(ctx, secrets.GovUkNotify)
if err != nil {
return fmt.Errorf("failed to get notify API secret: %w", err)
}

bundle, err := localize.NewBundle("./lang/en.json", "./lang/cy.json")
if err != nil {
return err
}

notifyClient, err := notify.New(logger, notifyIsProduction, notifyBaseURL, notifyApiKey, http.DefaultClient, event.NewClient(cfg, eventBusName), bundle)
if err != nil {
return err
}

dynamoClient, err := dynamo.NewClient(cfg, tableName)
if err != nil {
return fmt.Errorf("failed to create dynamodb client: %w", err)
}

eventClient := event.NewClient(cfg, eventBusName)

searchClient, err := search.NewClient(cfg, searchEndpoint, searchIndexName, searchIndexingEnabled)
if err != nil {
return err
}

donorStore := donor.NewStore(dynamoClient, eventClient, logger, searchClient)
scheduledStore := scheduled.NewStore(dynamoClient)

runner := scheduled.NewRunner(logger, scheduledStore, donorStore, notifyClient)

if err := runner.Run(ctx); err != nil {
logger.Error("runner error", slog.Any("err", err))
return err
}

return nil
}

func main() {
lambda.Start(handleRunSchedule)
}
File renamed without changes.
23 changes: 23 additions & 0 deletions docker/docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ services:
container_name: app-dev
depends_on:
- events-lambda
- schedule-runner-lambda
security_opt:
- "seccomp:unconfined"
volumes:
Expand Down Expand Up @@ -49,6 +50,28 @@ services:
- "9000:8080"
entrypoint: aws-lambda-rie /var/task/event-received

schedule-runner-lambda:
build:
context: ..
dockerfile: docker/schedule-runner/Dockerfile
target: dev
platforms:
- "linux/amd64"
- "linux/arm64"
container_name: schedule-runner
environment:
- AWS_ACCESS_KEY_ID=fakeKeyId
- AWS_BASE_URL=http://localstack:4566
- AWS_REGION=eu-west-1
- AWS_SECRET_ACCESS_KEY=fakeAccessKey
- LPAS_TABLE=lpas
- GOVUK_NOTIFY_IS_PRODUCTION=0
- GOVUK_NOTIFY_BASE_URL=http://mock-notify:8080
- SEARCH_ENDPOINT=http://my-domain.eu-west-1.opensearch.localhost.localstack.cloud:4566
ports:
- "9002:8080"
entrypoint: aws-lambda-rie /var/task/schedule-runner

mock-pay:
image: outofcoffee/imposter:latest
container_name: mock-pay
Expand Down
2 changes: 1 addition & 1 deletion docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ services:
- "/var/run/docker.sock:/var/run/docker.sock"
environment:
- DOCKER_HOST=unix:///var/run/docker.sock
- SERVICES=s3,secretsmanager,sqs,dynamodb,events,kms,lambda,opensearch
- SERVICES=s3,secretsmanager,sqs,dynamodb,events,kms,lambda,opensearch,scheduler,logs
- DATA_DIR=/tmp/localstack/data
- DEBUG=1
- AWS_ACCESS_KEY_ID=fakeKeyId
Expand Down
8 changes: 4 additions & 4 deletions docker/event-received/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ RUN go mod download
COPY --link cmd/event-received ./cmd/event-received
COPY --link internal ./internal

RUN GOOS=linux GOOS=${TARGETOS} GOARCH=${TARGETARCH} CGO_ENABLED=0 go build -tags lambda.norpc -o event-received ./cmd/event-received
RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} CGO_ENABLED=0 go build -o event-received ./cmd/event-received

FROM public.ecr.aws/lambda/provided:al2023.2024.10.14.12 AS dev

WORKDIR /app

COPY --from=build /app/event-received /var/task/event-received
COPY --link lang ./lang
COPY --link docker/event-received/aws-lambda-rie ./aws-lambda-rie
COPY --link lang /var/task/lang
COPY --link docker/aws-lambda-rie ./aws-lambda-rie

ENTRYPOINT ["./event-received"]

FROM public.ecr.aws/lambda/provided:al2023.2024.10.14.12 AS production

WORKDIR /app
COPY --link docker/event-received/install_lambda_insights.sh /app/
COPY --link docker/install_lambda_insights.sh /app/

RUN chmod +x /app/install_lambda_insights.sh \
&& /app/install_lambda_insights.sh "${TARGETPLATFORM}"
Expand Down
File renamed without changes.
13 changes: 10 additions & 3 deletions docker/localstack/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,22 @@ COPY --link go.mod go.sum ./
RUN go mod download

COPY --link cmd/event-received ./cmd/event-received
COPY --link cmd/schedule-runner ./cmd/schedule-runner
COPY --link internal ./internal
COPY --link lang ./lang

RUN GOOS=linux GOOS=${TARGETOS} GOARCH=${TARGETARCH} CGO_ENABLED=0 go build -tags lambda.norpc -o event-received ./cmd/event-received \
&& zip event-received.zip event-received
RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} CGO_ENABLED=0 go build -o cmd/event-received/bootstrap ./cmd/event-received \
&& chmod 755 cmd/event-received/bootstrap \
&& zip -j event-received.zip cmd/event-received/bootstrap && zip -r event-received.zip lang

FROM localstack/localstack:3.8.1 AS localstack
RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} CGO_ENABLED=0 go build -o cmd/schedule-runner/bootstrap ./cmd/schedule-runner \
&& chmod 755 cmd/schedule-runner/bootstrap \
&& zip -j schedule-runner.zip cmd/schedule-runner/bootstrap && zip -r schedule-runner.zip lang

FROM --platform=${TARGETARCH} localstack/localstack:3.8.1 AS localstack

COPY --from=build /app/event-received.zip /etc/event-received.zip
COPY --from=build /app/schedule-runner.zip /etc/schedule-runner.zip

COPY ./docker/localstack/localstack-init.sh /etc/localstack/init/ready.d/localstack-init.sh

Expand Down
61 changes: 55 additions & 6 deletions docker/localstack/localstack-init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,61 @@ awslocal s3api create-bucket --bucket evidence --create-bucket-configuration Loc
echo 'configuring events'
awslocal sqs create-queue --region eu-west-1 --queue-name event-queue
awslocal events create-event-bus --region eu-west-1 --name default
awslocal events put-rule --region eu-west-1 --name send-events-to-queue-rule --event-bus-name default --event-pattern '{}'
awslocal events put-targets --region eu-west-1 --event-bus-name default --rule send-events-to-queue-rule --targets "Id"="event-queue","Arn"="arn:aws:sqs:eu-west-1:000000000000:event-queue"

echo 'creating lambda'
awslocal lambda create-function --environment Variables="{LPAS_TABLE=lpas,GOVUK_NOTIFY_IS_PRODUCTION=0,APP_PUBLIC_URL=localhost:5050,GOVUK_NOTIFY_BASE_URL=http://mock-notify:8080,UPLOADS_S3_BUCKET_NAME=evidence,UID_BASE_URL=http://mock-uid:8080,SEARCH_ENDPOINT=http://my-domain.eu-west-1.opensearch.localhost.localstack.cloud:4566,SEARCH_INDEXING_ENABLED=1}" --region eu-west-1 --function-name event-received --handler event-received --runtime go1.x --role arn:aws:iam::000000000000:role/lambda-role --zip-file fileb:///etc/event-received.zip
awslocal events put-rule \
--region eu-west-1 \
--name send-events-to-queue-rule \
--event-bus-name default \
--event-pattern '{}'

awslocal events put-targets \
--region eu-west-1 \
--event-bus-name default \
--rule send-events-to-queue-rule \
--targets "Id"="event-queue","Arn"="arn:aws:sqs:eu-west-1:000000000000:event-queue"

echo 'creating event-received lambda'
awslocal lambda create-function \
--environment Variables="{LPAS_TABLE=lpas,GOVUK_NOTIFY_IS_PRODUCTION=0,APP_PUBLIC_URL=localhost:5050,GOVUK_NOTIFY_BASE_URL=http://mock-notify:8080,UPLOADS_S3_BUCKET_NAME=evidence,UID_BASE_URL=http://mock-uid:8080,SEARCH_ENDPOINT=http://my-domain.eu-west-1.opensearch.localhost.localstack.cloud:4566,SEARCH_INDEXING_ENABLED=1}" \
--region eu-west-1 \
--function-name event-received \
--handler bootstrap \
--runtime provided.al2023 \
--role arn:aws:iam::000000000000:role/lambda-role \
--zip-file fileb:///etc/event-received.zip

awslocal lambda wait function-active-v2 --region eu-west-1 --function-name event-received

awslocal events put-rule --region eu-west-1 --name receive-events-mlpa --event-bus-name default --event-pattern '{"source":["opg.poas.makeregister"],"detail-type":["uid-requested"]}'
awslocal events put-targets --region eu-west-1 --event-bus-name default --rule receive-events-mlpa --targets "Id"="receive-events-sirius","Arn"="arn:aws:lambda:eu-west-1:000000000000:function:event-received"
awslocal events put-rule \
--region eu-west-1 \
--name receive-events-mlpa \
--event-bus-name default \
--event-pattern '{"source":["opg.poas.makeregister"],"detail-type":["uid-requested"]}'

awslocal events put-targets \
--region eu-west-1 \
--event-bus-name default \
--rule receive-events-mlpa \
--targets "Id"="receive-events-sirius","Arn"="arn:aws:lambda:eu-west-1:000000000000:function:event-received"

echo 'creating schedule-runner lambda'
awslocal lambda create-function \
--environment Variables="{LPAS_TABLE=lpas,GOVUK_NOTIFY_IS_PRODUCTION=0,GOVUK_NOTIFY_BASE_URL=http://mock-notify:8080,SEARCH_ENDPOINT=http://my-domain.eu-west-1.opensearch.localhost.localstack.cloud:4566,SEARCH_INDEXING_ENABLED=1,SEARCH_INDEX_NAME=lpas}" \
--region eu-west-1 \
--function-name schedule-runner \
--handler bootstrap \
--runtime provided.al2023 \
--role arn:aws:iam::000000000000:role/lambda-role \
--zip-file fileb:///etc/schedule-runner.zip \
--timeout 900

awslocal lambda wait function-active-v2 --region eu-west-1 --function-name schedule-runner

echo 'create and associate scheduler'
awslocal scheduler create-schedule \
--region eu-west-1 \
--name schedule-runner-minutely \
--schedule-expression 'rate(1 minute)' \
--description "Runs every minute (to aid testing - deployed infra will run less frequently)" \
--target '{"RoleArn": "arn:aws:iam::000000000000:role/lambda-role", "Arn":"arn:aws:lambda:eu-west-1:000000000000:function:schedule-runner" }' \
--flexible-time-window '{ "Mode": "OFF"}'
3 changes: 3 additions & 0 deletions docker/schedule-runner/.trivyignore.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
misconfigurations:
- id: AVD-DS-0002
statement: Lambda creates a docker USER with least-privilege permissions.
34 changes: 34 additions & 0 deletions docker/schedule-runner/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
FROM golang:1.23.1-alpine AS build

WORKDIR /app

COPY --link go.mod go.sum ./
RUN go mod download

COPY --link cmd/schedule-runner ./cmd/schedule-runner
COPY --link internal ./internal

RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} CGO_ENABLED=0 go build -o schedule-runner ./cmd/schedule-runner

FROM public.ecr.aws/lambda/provided:al2023.2024.10.14.12 AS dev

WORKDIR /app

COPY --from=build /app/schedule-runner /var/task/schedule-runner
COPY --link lang /var/task/lang
COPY --link docker/aws-lambda-rie ./aws-lambda-rie

ENTRYPOINT ["./schedule-runner"]

FROM public.ecr.aws/lambda/provided:al2023.2024.10.14.12 AS production

WORKDIR /app
COPY --link docker/install_lambda_insights.sh /app/

RUN chmod +x "/app/install_lambda_insights.sh" \
&& /app/install_lambda_insights.sh "${TARGETPLATFORM}"

COPY --from=build /app/schedule-runner ./schedule-runner
COPY --link lang ./lang

ENTRYPOINT ["./schedule-runner"]
Loading

0 comments on commit 76accfb

Please sign in to comment.