Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions cf-custom-resources/lib/unique-json-values.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,20 @@ const deadlineExpired = function () {
* {
* "svc1": ["svc1.com", "example.com"],
* "svc2": ["example.com"]
* "svc3": ["svc3.com"]
* }
*
* This function returns a list of unique values found in that list.
* From the previous example, UniqueValues would be:
* The input event.ResourceProperties.FilterFor is a comma delimated list
* of keys in event.ResourceProperties.Aliases to filter for.
* Taking the above map, and event.ResourceProperties.FilterFor = "svc1,svc2"
* The services considered for uniqueness is:
* {
* "svc1": ["svc1.com", "example.com"],
* "svc2": ["example.com"]
* }
*
* This function returns a list of unique values found in this map.
* For this example, UniqueValues would be:
* ["svc1.com", "example.com"]
*/
exports.handler = async function (event, context) {
Expand All @@ -96,17 +106,24 @@ exports.handler = async function (event, context) {
case "Create":
case "Update":
const aliasesForService = JSON.parse(event.ResourceProperties.Aliases || "{}");
const unique = new Set(Object.values(aliasesForService).flat());
const filterFor = new Set(event.ResourceProperties.FilterFor.split(","));
const filteredAliasesForService = Object.fromEntries(
Object.entries(aliasesForService).filter(
([key]) => filterFor.has(key)
)
);
const unique = new Set(Object.values(filteredAliasesForService).flat());
responseData.UniqueValues = Array.from(unique).sort();
break;
case "Delete":
// Do nothing on delete, since this isn't a "real" resource.
break;
default:
throw new Error(`Unsupported request type ${event.RequestType}`);
}
};
};


try {
await Promise.race([deadlineExpired(), handler()]);
await report(event, context, "SUCCESS", physicalResourceId, responseData);
Expand Down
92 changes: 76 additions & 16 deletions cf-custom-resources/test/unique-json-values-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ describe("Unique Aliases", () => {
});
});

const aliasTest = (name, input, expectedOutput) => {
const tt = (name, reqType, input, expectedOutput) => {
const aliasTest = (name, props, expectedOutput) => {
const tt = (name, reqType, props, expectedOutput) => {
test(name, () => {
const request = nock(responseURL)
.put("/", (body) => {
Expand All @@ -95,9 +95,7 @@ describe("Unique Aliases", () => {
ResponseURL: responseURL,
RequestType: reqType,
RequestId: testRequestId,
ResourceProperties: {
Aliases: JSON.stringify(input), // aliases get passed as a string
},
ResourceProperties: props,
LogicalResourceId: "mockID",
})
.expectResolve(() => {
Expand All @@ -106,28 +104,90 @@ describe("Unique Aliases", () => {
});
};

tt(`Create/${name}`, "Create", input, expectedOutput);
tt(`Update/${name}`, "Update", input, expectedOutput);
tt(`Create/${name}`, "Create", props, expectedOutput);
tt(`Update/${name}`, "Update", props, expectedOutput);
};

aliasTest("no aliases", {}, []);
aliasTest("no aliases", {
Aliases: "",
FilterFor: ""
}, []);

aliasTest("one service", {
"svc1": ["svc1.com", "example.com"],
Aliases: JSON.stringify({
"svc1": ["svc1.com", "example.com"]
}),
FilterFor: "svc1",
}, ["example.com", "svc1.com"]);

aliasTest("one service excluded", {
Aliases: JSON.stringify({
"svc1": ["svc1.com", "example.com"]
}),
FilterFor: "svc2",
}, []);

aliasTest("one service empty filter for", {
Aliases: JSON.stringify({
"svc1": ["svc1.com", "example.com"]
}),
FilterFor: "",
}, []);

aliasTest("two services no common aliases", {
"svc1": ["svc1.com"],
"svc2": ["svc2.com"]
Aliases: JSON.stringify({
"svc1": ["svc1.com"],
"svc2": ["svc2.com"]
}),
FilterFor: "svc1,svc2",
}, ["svc1.com", "svc2.com"]);

aliasTest("two services, one with multiple common aliases", {
"svc1": ["svc1.com"],
"svc2": ["svc2.com", "example.com"]
Aliases: JSON.stringify({
"svc1": ["svc1.com"],
"svc2": ["svc2.com", "example.com"]
}),
FilterFor: "svc1,svc2",
}, ["example.com", "svc1.com", "svc2.com"]);

aliasTest("two services with a common alias", {
"svc1": ["svc1.com", "example.com"],
"svc2": ["svc2.com", "example.com"]
Aliases: JSON.stringify({
"svc1": ["svc1.com", "example.com"],
"svc2": ["svc2.com", "example.com"]
}),
FilterFor: "svc1,svc2",
}, ["example.com", "svc1.com", "svc2.com"]);
});

aliasTest("three services with a common alias one service filtered out", {
Aliases: JSON.stringify({
"svc1": ["svc1.com", "example.com"],
"svc2": ["svc2.com", "example.com", "example2.com"],
"svc3": ["svc3.com", "example.com", "example2.com"]
}),
FilterFor: "svc2,svc1",
}, ["example.com", "example2.com", "svc1.com", "svc2.com"]);

aliasTest("bunch of services with single alias, some filtered out, out of order", {
Aliases: JSON.stringify({
"lbws3": ["three.lbws.com"],
"backend1": ["one.backend.internal"],
"lbws1": ["one.lbws.com"],
"lbws4": ["four.lbws.com"],
"backend2": ["two.backend.internal"],
"lbws2": ["two.lbws.com"],
"lbws6": ["lbws.com"],
"lbws5": ["lbws.com"],
}),
FilterFor: "lbws2,lbws3,lbws4,lbws1,lbws5,lbws6",
}, ["four.lbws.com", "lbws.com", "one.lbws.com", "three.lbws.com", "two.lbws.com"]);

aliasTest("bunch of services with single alias, some FilterFor services don't exist", {
Aliases: JSON.stringify({
"lbws1": ["lbws1.com"],
"lbws2": ["lbws2.com"],
"lbws3": ["lbws3.com"],
"lbws4": ["lbws4.com"],
}),
FilterFor: "lbws1,lbws5,lbws6,lbws3",
}, ["lbws1.com", "lbws3.com"]);
});
40 changes: 11 additions & 29 deletions e2e/apprunner/back-end/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,33 +1,15 @@
# We specify the base image we need for our
# go application
FROM golang:1.14 AS builder
# We create an /app directory within our
# image that will hold our application source
# files
RUN mkdir /app
# We copy everything in the root directory
# into our /app directory
ADD . /app
# We specify that we now wish to execute
# any further commands inside our /app
# directory
WORKDIR /app
# Avoid the GoProxy
FROM public.ecr.aws/docker/library/golang:1.19 as builder

ENV GOPROXY=direct
# we run go build to compile the binary
# executable of our Go program
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o e2e-service ./
WORKDIR /go/src/app

COPY . .

RUN go mod download
RUN CGO_ENABLED=0 go build -o /go/bin/app ./

# To make our images smaller, use alpine and copy in the service binary.
FROM alpine:latest
# Install certs
RUN apk --no-cache add ca-certificates
# Copy the binary from the builder image
COPY --from=builder /app ./
# Make the binary executable
RUN chmod +x ./e2e-service
FROM gcr.io/distroless/static

# Start the service
ENTRYPOINT ["./e2e-service"]
# The service runs on port 80
COPY --from=builder /go/bin/app /
EXPOSE 80
ENTRYPOINT ["/app"]
4 changes: 1 addition & 3 deletions e2e/apprunner/back-end/go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
module github.com/aws/copilot-cli/e2e/appprunner/back-end

go 1.14

require github.com/julienschmidt/httprouter v1.3.0 // indirect
go 1.19
2 changes: 0 additions & 2 deletions e2e/apprunner/back-end/go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
19 changes: 9 additions & 10 deletions e2e/apprunner/back-end/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,32 @@
package main

import (
"errors"
"log"
"net/http"

"github.com/julienschmidt/httprouter"
)

var message = "hello world"

// HealthCheck just returns true if the service is up.
func HealthCheck(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
func HealthCheck(w http.ResponseWriter, req *http.Request) {
log.Println("🚑 healthcheck ok!")
w.WriteHeader(http.StatusOK)
}

// ServiceDiscoveryGet just returns true no matter what
func ServiceDiscoveryGet(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
func ServiceDiscoveryGet(w http.ResponseWriter, req *http.Request) {
log.Printf("Get on ServiceDiscovery endpoint Succeeded with message %s\n", message)
w.WriteHeader(http.StatusOK)
w.Write([]byte(message))
}

func main() {
router := httprouter.New()
router.GET("/service-discovery/", ServiceDiscoveryGet)

// Health Check
router.GET("/", HealthCheck)
http.Handle("/", http.HandlerFunc(HealthCheck))
http.Handle("/service-discovery", http.HandlerFunc(ServiceDiscoveryGet))

log.Fatal(http.ListenAndServe(":80", router))
err := http.ListenAndServe(":80", nil)
if !errors.Is(err, http.ErrServerClosed) {
log.Fatalf("listen and serve: %s", err)
}
}
40 changes: 11 additions & 29 deletions e2e/apprunner/front-end/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,33 +1,15 @@
# We specify the base image we need for our
# go application
FROM golang:1.14 AS builder
# We create an /app directory within our
# image that will hold our application source
# files
RUN mkdir /app
# We copy everything in the root directory
# into our /app directory
ADD . /app
# We specify that we now wish to execute
# any further commands inside our /app
# directory
WORKDIR /app
# Avoid the GoProxy
FROM public.ecr.aws/docker/library/golang:1.19 as builder

ENV GOPROXY=direct
# we run go build to compile the binary
# executable of our Go program
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o e2e-service ./
WORKDIR /go/src/app

COPY . .

RUN go mod download
RUN CGO_ENABLED=0 go build -o /go/bin/app ./

# To make our images smaller, use alpine and copy in the service binary.
FROM alpine:latest
# Install certs
RUN apk --no-cache add ca-certificates
# Copy the binary from the builder image
COPY --from=builder /app ./
# Make the binary executable
RUN chmod +x ./e2e-service
FROM gcr.io/distroless/static

# Start the service
ENTRYPOINT ["./e2e-service"]
# The service runs on port 80
COPY --from=builder /go/bin/app /
EXPOSE 80
ENTRYPOINT ["/app"]
20 changes: 17 additions & 3 deletions e2e/apprunner/front-end/go.mod
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
module github.com/aws/copilot-cli/e2e/apprunner/front-end

go 1.14
go 1.19

require (
github.com/aws/aws-sdk-go v1.43.6
github.com/julienschmidt/httprouter v1.3.0
github.com/aws/aws-sdk-go-v2 v1.16.16
github.com/aws/aws-sdk-go-v2/config v1.17.8
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.2
github.com/lib/pq v1.10.4
)

require (
github.com/aws/aws-sdk-go-v2/credentials v1.12.21 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.17 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.24 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.17 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.11.23 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.6 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.16.19 // indirect
github.com/aws/smithy-go v1.13.3 // indirect
)
Loading