Skip to content

Commit

Permalink
Merge pull request #1605 from ministryofjustice/MLPAB-2633-replication
Browse files Browse the repository at this point in the history
MLPAB-2633 Rewrite create-s3-replication-job to golang
  • Loading branch information
hawx authored Nov 7, 2024
2 parents 0492dac + 25ff89a commit 6cd6f98
Show file tree
Hide file tree
Showing 15 changed files with 241 additions and 178 deletions.
1 change: 1 addition & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ coverage:
ignore:
- "**/*_test.go"
- "**/enum_*.go"
- "./cmd/create-s3-replication-job/main.go"
- "./cmd/event-logger/main.go"
- "./cmd/event-received/main.go"
- "./cmd/mlpa/main.go"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/docker_job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ jobs:
platforms: linux/amd64
- ecr_repository: modernising-lpa/create-s3-batch-replication-job
name: create-s3-batch-replication-job
path: ./lambda/create_s3_replication_job/Dockerfile
trivyignores: ./lambda/create_s3_replication_job/.trivyignore.yaml
path: ./docker/create-s3-replication-job/Dockerfile
trivyignores: ./docker/create-s3-replication-job/.trivyignore.yaml
platforms: linux/amd64
- ecr_repository: modernising-lpa/event-received
name: event-received
Expand Down
154 changes: 154 additions & 0 deletions cmd/create-s3-replication-job/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// Create S3 replication job is an AWS Lambda function used to create an S3
// Batch Replication Job to copy files from one S3 bucket to another.
//
// In this service, the source bucket is for uploads to the service and the
// destination bucket is for a case management system in another AWS account.
package main

import (
"context"
"encoding/json"
"fmt"
"log/slog"
"os"

"github.com/aws/aws-lambda-go/lambda"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3control"
"github.com/aws/aws-sdk-go-v2/service/s3control/types"
"github.com/aws/aws-sdk-go-v2/service/ssm"
"github.com/google/uuid"
"github.com/ministryofjustice/opg-modernising-lpa/internal/telemetry"
"go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-lambda-go/otellambda"
"go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-lambda-go/otellambda/xrayconfig"
)

var (
environment = os.Getenv("ENVIRONMENT")
logger *slog.Logger
cfg aws.Config
)

type configVars struct {
AccountID string `json:"aws_account_id"`
Environment string `json:'-"`
ReportAndManifestsBucket string `json:"report_and_manifests_bucket"`
RoleARN string `json:"role_arn"`
SourceBucket string `json:"source_bucket"`
}

func main() {
ctx := context.Background()

logger = slog.New(telemetry.NewSlogHandler(slog.
NewJSONHandler(os.Stdout, nil)).
WithAttrs([]slog.Attr{
slog.String("service_name", "opg-modernising-lpa/create-s3-replication-job"),
}))

var err error
cfg, err = config.LoadDefaultConfig(ctx)
if err != nil {
logger.ErrorContext(ctx, "failed to load default config", slog.Any("err", err))
return
}

tp, err := telemetry.SetupLambda(ctx, &cfg.APIOptions)
if err != nil {
logger.WarnContext(ctx, "error creating tracer provider", slog.Any("err", err))
}

if tp != nil {
defer func(ctx context.Context) {
if err := tp.Shutdown(ctx); err != nil {
logger.WarnContext(ctx, "error shutting down tracer provider", slog.Any("err", err))
}
}(ctx)

lambda.Start(otellambda.InstrumentHandler(handler, xrayconfig.WithRecommendedOptions(tp)...))
} else {
lambda.Start(handler)
}
}

func handler(ctx context.Context) error {
vars, err := getVars(ctx, cfg, environment)
if err != nil {
return fmt.Errorf("failed to get config vars: %w", err)
}

jobID, err := createJob(ctx, cfg, vars)
if err != nil {
return fmt.Errorf("failed to create job: %w", err)
}

logger.InfoContext(ctx, "job created", slog.Any("job_id", jobID))
return nil
}

func getVars(ctx context.Context, cfg aws.Config, environment string) (configVars, error) {
ssmClient := ssm.NewFromConfig(cfg)

param, err := ssmClient.GetParameter(ctx, &ssm.GetParameterInput{
Name: aws.String("/modernising-lpa/s3-batch-configuration/" + environment + "/s3_batch_configuration"),
})
if err != nil {
return configVars{}, fmt.Errorf("failed to retrieve parameter: %w", err)
}

var vars configVars
if err := json.Unmarshal([]byte(*param.Parameter.Value), &vars); err != nil {
return configVars{}, fmt.Errorf("failed to unmarshal parameter: %w", err)
}

vars.Environment = environment
return vars, nil
}

func createJob(ctx context.Context, cfg aws.Config, vars configVars) (string, error) {
controlClient := s3control.NewFromConfig(cfg)
requestToken := uuid.NewString()

resp, err := controlClient.CreateJob(ctx, &s3control.CreateJobInput{
AccountId: aws.String(vars.AccountID),
ConfirmationRequired: aws.Bool(false),
Operation: &types.JobOperation{
S3ReplicateObject: &types.S3ReplicateObjectOperation{},
},
Report: &types.JobReport{
Enabled: true,
Bucket: aws.String(vars.ReportAndManifestsBucket),
Format: types.JobReportFormatReportCsv20180820,
ReportScope: types.JobReportScopeAllTasks,
},
ClientRequestToken: aws.String(requestToken),
Description: aws.String("S3 replication " + vars.Environment + " - golang"),
Priority: aws.Int32(10),
RoleArn: aws.String(vars.RoleARN),
ManifestGenerator: &types.JobManifestGeneratorMemberS3JobManifestGenerator{
Value: types.S3JobManifestGenerator{
EnableManifestOutput: true,
ExpectedBucketOwner: aws.String(vars.AccountID),
SourceBucket: aws.String(vars.SourceBucket),
Filter: &types.JobManifestGeneratorFilter{
EligibleForReplication: aws.Bool(true),
ObjectReplicationStatuses: []types.ReplicationStatus{types.ReplicationStatusFailed, types.ReplicationStatusNone},
},
ManifestOutputLocation: &types.S3ManifestOutputLocation{
ExpectedManifestBucketOwner: aws.String(vars.AccountID),
Bucket: aws.String(vars.ReportAndManifestsBucket),
ManifestEncryption: &types.GeneratedManifestEncryption{
SSES3: &types.SSES3Encryption{},
},
ManifestFormat: types.GeneratedManifestFormatS3InventoryReportCsv20211130,
},
},
},
})
if err != nil {
return "", err
}

return *resp.JobId, nil
}
5 changes: 1 addition & 4 deletions cmd/event-received/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import (
"github.com/ministryofjustice/opg-modernising-lpa/internal/telemetry"
"go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-lambda-go/otellambda"
"go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-lambda-go/otellambda/xrayconfig"
"go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel/sdk/trace"
)
Expand Down Expand Up @@ -246,15 +245,13 @@ func main() {

var tp *trace.TracerProvider
if xrayEnabled {
tp, err = telemetry.SetupLambda(ctx)
tp, err = telemetry.SetupLambda(ctx, &cfg.APIOptions)
if err != nil {
logger.WarnContext(ctx, "error creating tracer provider", slog.Any("err", err))
}
}

if tp != nil {
otelaws.AppendMiddlewares(&cfg.APIOptions)
telemetry.AppendMiddlewares(&cfg.APIOptions)
httpClient.Transport = otelhttp.NewTransport(httpClient.Transport)

defer func(ctx context.Context) {
Expand Down
24 changes: 10 additions & 14 deletions cmd/mlpa/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import (
"github.com/ministryofjustice/opg-modernising-lpa/internal/templatefn"
"github.com/ministryofjustice/opg-modernising-lpa/internal/uid"
"go.opentelemetry.io/contrib/detectors/aws/ecs"
"go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"golang.org/x/mod/sumdb/dirhash"
)
Expand Down Expand Up @@ -124,13 +123,22 @@ func run(ctx context.Context, logger *slog.Logger) error {

httpClient := &http.Client{Timeout: 30 * time.Second}

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

if len(awsBaseURL) > 0 {
cfg.BaseEndpoint = aws.String(awsBaseURL)
}

if xrayEnabled {
resource, err := ecs.NewResourceDetector().Detect(ctx)
if err != nil {
return err
}

shutdown, err := telemetry.Setup(ctx, resource)
shutdown, err := telemetry.Setup(ctx, resource, &cfg.APIOptions)
if err != nil {
return err
}
Expand Down Expand Up @@ -195,18 +203,6 @@ func run(ctx context.Context, logger *slog.Logger) error {
return err
}

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

if len(awsBaseURL) > 0 {
cfg.BaseEndpoint = aws.String(awsBaseURL)
}

otelaws.AppendMiddlewares(&cfg.APIOptions)
telemetry.AppendMiddlewares(&cfg.APIOptions)

lpasDynamoClient, err := dynamo.NewClient(cfg, dynamoTableLpas)
if err != nil {
return err
Expand Down
5 changes: 1 addition & 4 deletions cmd/schedule-runner/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"github.com/ministryofjustice/opg-modernising-lpa/internal/telemetry"
"go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-lambda-go/otellambda"
"go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-lambda-go/otellambda/xrayconfig"
"go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel/sdk/trace"
)
Expand Down Expand Up @@ -128,15 +127,13 @@ func main() {

var tp *trace.TracerProvider
if xrayEnabled {
tp, err = telemetry.SetupLambda(ctx)
tp, err = telemetry.SetupLambda(ctx, &cfg.APIOptions)
if err != nil {
logger.WarnContext(ctx, "error creating tracer provider", slog.Any("err", err))
}
}

if tp != nil {
otelaws.AppendMiddlewares(&cfg.APIOptions)
telemetry.AppendMiddlewares(&cfg.APIOptions)
httpClient.Transport = otelhttp.NewTransport(httpClient.Transport)

defer func(ctx context.Context) {
Expand Down
44 changes: 44 additions & 0 deletions docker/create-s3-replication-job/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
FROM golang:1.23.2-alpine AS build

WORKDIR /app

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

COPY --link cmd/create-s3-replication-job ./cmd/create-s3-replication-job
COPY --link internal ./internal

RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} CGO_ENABLED=0 go build -o create-s3-replication-job ./cmd/create-s3-replication-job

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

WORKDIR /app

COPY --from=build /app/create-s3-replication-job /var/task/create-s3-replication-job
COPY --link lang ./lang
COPY --link ./docker/adot-collector/ /opt
COPY --link docker/aws-lambda-rie ./aws-lambda-rie

ENV AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-handler
ENV OPENTELEMETRY_COLLECTOR_CONFIG_FILE="/opt/config/config.yaml"

ENTRYPOINT ["./create-s3-replication-job"]

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/create-s3-replication-job ./create-s3-replication-job
COPY --link lang ./lang
COPY --link ./docker/adot-collector/ /opt

RUN chmod 755 /opt/config/config.yaml

ENV AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-handler
ENV OPENTELEMETRY_COLLECTOR_CONFIG_FILE="/opt/config/config.yaml"

ENTRYPOINT ["./create-s3-replication-job"]
10 changes: 6 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/MicahParks/jwkset v0.5.20
github.com/MicahParks/keyfunc/v3 v3.3.5
github.com/aws/aws-lambda-go v1.47.0
github.com/aws/aws-sdk-go-v2 v1.32.3
github.com/aws/aws-sdk-go-v2 v1.32.4
github.com/aws/aws-sdk-go-v2/config v1.28.1
github.com/aws/aws-sdk-go-v2/credentials v1.17.42
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.15.13
Expand Down Expand Up @@ -49,16 +49,18 @@ require (
require (
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.18 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.23 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.23 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.22 // indirect
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.24.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.4 // indirect
github.com/aws/aws-sdk-go-v2/service/s3control v1.50.0 // indirect
github.com/aws/aws-sdk-go-v2/service/ssm v1.55.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.3 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.32.3 // indirect
Expand Down
Loading

0 comments on commit 6cd6f98

Please sign in to comment.