Skip to content
Merged
21 changes: 11 additions & 10 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,17 @@ QuickPizza is a demonstration web application that generates pizza recommendatio
### Microservices Architecture
The application is designed as a modular monolith that can be deployed as separate microservices. Services are controlled by environment variables:

- **Frontend** (`QUICKPIZZA_FRONTEND`) - Serves SvelteKit UI
- **Catalog** (`QUICKPIZZA_CATALOG`) - Manages ingredients, tools, doughs, users, ratings
- **Copy** (`QUICKPIZZA_COPY`) - Handles quotes, names, adjectives for pizza generation
- **Recommendations** (`QUICKPIZZA_RECOMMENDATIONS`) - Core pizza recommendation logic
- **WebSocket** (`QUICKPIZZA_WS`) - Real-time communication
- **PublicAPI** (`QUICKPIZZA_ENABLE_PUBLIC_API_SERVICE`) - Serves Frontend and Gateway
- **Frontend** - Serves SvelteKit UI
- **Gateway** - Routes requests between services in microservice deployments
- **gRPC** (`QUICKPIZZA_GRPC`) - gRPC service on ports 3334/3335
- **Config** (`QUICKPIZZA_CONFIG`) - Configuration endpoint
- **HTTP Testing** (`QUICKPIZZA_HTTP_TESTING`) - HTTP testing utilities
- **Test K6 IO** (`QUICKPIZZA_TEST_K6_IO`) - Legacy test.k6.io replacement endpoints
- **Catalog** (`QUICKPIZZA_ENABLE_CATALOG_SERVICE`) - Manages ingredients, tools, doughs, users, ratings
- **Copy** (`QUICKPIZZA_ENABLE_COPY_SERVICE`) - Handles quotes, names, adjectives for pizza generation
- **Recommendations** (`QUICKPIZZA_ENABLE_RECOMMENDATIONS_SERVICE`) - Core pizza recommendation logic
- **WebSocket** (`QUICKPIZZA_ENABLE_WS_SERVICE`) - Real-time communication
- **gRPC** (`QUICKPIZZA_ENABLE_GRPC_SERVICE`) - gRPC service on ports 3334/3335
- **Config** (`QUICKPIZZA_ENABLE_CONFIG_SERVICE`) - Configuration endpoint
- **HTTP Testing** (`QUICKPIZZA_ENABLE_HTTP_TESTING_SERVICE`) - HTTP testing utilities
- **Test K6 IO** (`QUICKPIZZA_ENABLE_TEST_K6_IO_SERVICE`) - Legacy test.k6.io replacement endpoints

### Key Packages
- `pkg/http/` - Main HTTP server and route handlers
Expand All @@ -73,7 +74,7 @@ Comprehensive observability built-in:
- **Frontend Observability**: Grafana Faro support

### Environment Configuration
- `QUICKPIZZA_ALL_SERVICES` - Enable all services (default: true)
- `QUICKPIZZA_ENABLE_ALL_SERVICES` - Enable all services (default: true)
- `QUICKPIZZA_LOG_LEVEL` - Set logging level (default: info)
- `QUICKPIZZA_OTLP_ENDPOINT` - OpenTelemetry collector endpoint
- `QUICKPIZZA_PYROSCOPE_ENDPOINT` - Pyroscope server for profiling
Expand Down
53 changes: 27 additions & 26 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,47 +73,47 @@ func main() {
server.AddPrometheusHandler()

// Enable services in this instance. Services are enabled with the following logic:
// If QUICKPIZZA_ALL_SERVICES is either _not set_ or set to a truthy value, all services are enabled. This is the
// If QUICKPIZZA_ENABLE_ALL_SERVICES is either _not set_ or set to a truthy value, all services are enabled. This is the
// default behavior.
// If QUICKPIZZA_ALL_SERVICES is set to a falsy values, services are opted-in by setting the environment variables
// If QUICKPIZZA_ENABLE_ALL_SERVICES is set to a falsy values, services are opted-in by setting the environment variables
// below to a truty value.

if envServe("QUICKPIZZA_HTTP_TESTING") {
if envServe("QUICKPIZZA_ENABLE_HTTP_TESTING_SERVICE") {
server.AddHTTPTesting()
}

if envServe("QUICKPIZZA_TEST_K6_IO") {
if envServe("QUICKPIZZA_ENABLE_TEST_K6_IO_SERVICE") {
server.AddTestK6IO()
}

if envServe("QUICKPIZZA_CONFIG") {
if envServe("QUICKPIZZA_ENABLE_CONFIG_SERVICE") {
// Prefix for env vars is QUICKPIZZA_CONF_ instead of QUICKPIZZA_CONFIG_ to avoid picking up variables
// generated by K8s from the pod name.
server.AddConfigHandler(envConfig("QUICKPIZZA_CONF_"))
}

if envServe("QUICKPIZZA_FRONTEND") {
if envServe("QUICKPIZZA_ENABLE_PUBLIC_API_SERVICE") {
// Serve frontend static assets
server.AddFrontend()

// If running as a microservice (not all services in one instance),
// also act as a gateway to proxy public-facing endpoints
if !envServeAll() {
server.AddGateway(
envEndpoint("QUICKPIZZA_CATALOG"),
envEndpoint("QUICKPIZZA_COPY"),
envEndpoint("QUICKPIZZA_WS"),
envEndpoint("QUICKPIZZA_RECOMMENDATIONS"),
envEndpoint("QUICKPIZZA_CONFIG"),
envEndpoint("QUICKPIZZA_ENABLE_CATALOG_SERVICE", "QUICKPIZZA_CATALOG_ENDPOINT"),
envEndpoint("QUICKPIZZA_ENABLE_COPY_SERVICE", "QUICKPIZZA_COPY_ENDPOINT"),
envEndpoint("QUICKPIZZA_ENABLE_WS_SERVICE", "QUICKPIZZA_WS_ENDPOINT"),
envEndpoint("QUICKPIZZA_ENABLE_RECOMMENDATIONS_SERVICE", "QUICKPIZZA_RECOMMENDATIONS_ENDPOINT"),
envEndpoint("QUICKPIZZA_ENABLE_CONFIG_SERVICE", "QUICKPIZZA_CONFIG_ENDPOINT"),
)
}
}

if envServe("QUICKPIZZA_WS") {
if envServe("QUICKPIZZA_ENABLE_WS_SERVICE") {
server.AddWebSocket()
}

if envServe("QUICKPIZZA_CATALOG") {
if envServe("QUICKPIZZA_ENABLE_CATALOG_SERVICE") {
db, err := database.NewCatalog(envDBConnString())
if err != nil {
slog.Error("setting up database connection", "err", err)
Expand All @@ -122,7 +122,7 @@ func main() {
server.AddCatalogHandler(db)
}

if envServe("QUICKPIZZA_COPY") {
if envServe("QUICKPIZZA_ENABLE_COPY_SERVICE") {
db, err := database.NewCopy(envDBConnString())
if err != nil {
slog.Error("setting up database connection", "err", err)
Expand All @@ -134,14 +134,14 @@ func main() {
// Recommendations service needs to know the URL where the Catalog and Copy services are located.
// This URL is automatically set to `localhost` if Recommendations is enabled at the same time as either of those.
// If they are not, URLs are sourced from QUICKPIZZA_CATALOG_ENDPOINT and QUICKPIZZA_COPY_ENDPOINT.
if envServe("QUICKPIZZA_RECOMMENDATIONS") {
catalogClient := qphttp.NewCatalogClient(envEndpoint("QUICKPIZZA_CATALOG")).WithClient(httpCli)
copyClient := qphttp.NewCopyClient(envEndpoint("QUICKPIZZA_COPY")).WithClient(httpCli)
if envServe("QUICKPIZZA_ENABLE_RECOMMENDATIONS_SERVICE") {
catalogClient := qphttp.NewCatalogClient(envEndpoint("QUICKPIZZA_ENABLE_CATALOG_SERVICE", "QUICKPIZZA_CATALOG_ENDPOINT")).WithClient(httpCli)
copyClient := qphttp.NewCopyClient(envEndpoint("QUICKPIZZA_ENABLE_COPY_SERVICE", "QUICKPIZZA_COPY_ENDPOINT")).WithClient(httpCli)

server.AddRecommendations(catalogClient, copyClient)
}

if envServe("QUICKPIZZA_GRPC") {
if envServe("QUICKPIZZA_ENABLE_GRPC_SERVICE") {
grpcServer := qpgrpc.NewServer(":3334", ":3335")
go func() {
err := grpcServer.ListenAndServe()
Expand Down Expand Up @@ -263,15 +263,15 @@ func envPyroscopeConfig() (pyroscope.Config, bool) {
}

func envServeAll() bool {
allSvcs, present := os.LookupEnv("QUICKPIZZA_ALL_SERVICES")
allSvcs, present := os.LookupEnv("QUICKPIZZA_ENABLE_ALL_SERVICES")
allSvcsB, _ := strconv.ParseBool(allSvcs)

// If QUICKPIZZA_ALL_SERVICES is not defined (default), serve everything.
// If QUICKPIZZA_ENABLE_ALL_SERVICES is not defined (default), serve everything.
if !present {
return true
}

// Otherwise, serve all if QUICKPIZZA_ALL_SERVICES is truthy.
// Otherwise, serve all if QUICKPIZZA_ENABLE_ALL_SERVICES is truthy.
return allSvcsB
}

Expand All @@ -280,14 +280,15 @@ func envServe(name string) bool {
return envServeAll() || envBool(name)
}

// envEndpoint returns the endpoint for a given service. If the service is enabled in this instance, it returns
// `localhost`. If it isn't, it returns the value of QUICKPIZZA_SERVICENAME_ENDPOINT.fs
func envEndpoint(name string) string {
if envServe(name) {
// envEndpoint returns the endpoint URL for a service.
// If the service is enabled (envServe(svcEnv) == true), it returns "http://localhost:3333".
// Otherwise, it returns the value of the endpointEnv environment variable.
func envEndpoint(svcEnv, endpointEnv string) string {
if envServe(svcEnv) {
return "http://localhost:3333"
}

endpoint, _ := os.LookupEnv(name + "_ENDPOINT")
endpoint, _ := os.LookupEnv(endpointEnv)
return endpoint
}

Expand Down
14 changes: 7 additions & 7 deletions compose.grafana-cloud.microservices.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
x-quickpizza-env-common: &quickpizza-env-common
QUICKPIZZA_OTLP_ENDPOINT: http://alloy:4318
QUICKPIZZA_TRUST_CLIENT_TRACEID: true
QUICKPIZZA_ALL_SERVICES: 0
QUICKPIZZA_ENABLE_ALL_SERVICES: 0 # 0 for microservice mode
QUICKPIZZA_CATALOG_ENDPOINT: http://catalog:3333
QUICKPIZZA_COPY_ENDPOINT: http://copy:3333
QUICKPIZZA_WS_ENDPOINT: http://ws:3333
Expand Down Expand Up @@ -51,7 +51,7 @@ services:
- "service.type=application"
environment:
<<: *quickpizza-env-common
QUICKPIZZA_CATALOG: "1"
QUICKPIZZA_ENABLE_CATALOG_SERVICE: "1"
QUICKPIZZA_OTEL_SERVICE_INSTANCE_ID: "catalog"
QUICKPIZZA_OTEL_SERVICE_NAME: "catalog"

Expand All @@ -62,7 +62,7 @@ services:
- "service.type=application"
environment:
<<: *quickpizza-env-common
QUICKPIZZA_CONFIG: "1"
QUICKPIZZA_ENABLE_CONFIG_SERVICE: "1"
QUICKPIZZA_OTEL_SERVICE_INSTANCE_ID: "config"
QUICKPIZZA_OTEL_SERVICE_NAME: "config"
copy:
Expand All @@ -72,7 +72,7 @@ services:
- "service.type=application"
environment:
<<: *quickpizza-env-common
QUICKPIZZA_COPY: "1"
QUICKPIZZA_ENABLE_COPY_SERVICE: "1"
QUICKPIZZA_OTEL_SERVICE_INSTANCE_ID: "copy"
QUICKPIZZA_OTEL_SERVICE_NAME: "copy"

Expand All @@ -85,7 +85,7 @@ services:
- "3333:3333"
environment:
<<: *quickpizza-env-common
QUICKPIZZA_FRONTEND: "1"
QUICKPIZZA_ENABLE_PUBLIC_API_SERVICE: "1"
QUICKPIZZA_OTEL_SERVICE_INSTANCE_ID: "public-api"
QUICKPIZZA_OTEL_SERVICE_NAME: "public-api"

Expand All @@ -96,7 +96,7 @@ services:
- "service.type=application"
environment:
<<: *quickpizza-env-common
QUICKPIZZA_RECOMMENDATIONS: "1"
QUICKPIZZA_ENABLE_RECOMMENDATIONS_SERVICE: "1"
QUICKPIZZA_OTEL_SERVICE_INSTANCE_ID: "recommendations"
QUICKPIZZA_OTEL_SERVICE_NAME: "recommendations"

Expand All @@ -107,7 +107,7 @@ services:
- "service.type=application"
environment:
<<: *quickpizza-env-common
QUICKPIZZA_WS: "1"
QUICKPIZZA_ENABLE_WS_SERVICE: "1"
QUICKPIZZA_OTEL_SERVICE_INSTANCE_ID: "ws"
QUICKPIZZA_OTEL_SERVICE_NAME: "ws"

2 changes: 1 addition & 1 deletion compose.grafana-cloud.monolithic.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ services:
environment:
QUICKPIZZA_OTLP_ENDPOINT: http://alloy:4318
QUICKPIZZA_TRUST_CLIENT_TRACEID: 1
QUICKPIZZA_ALL_SERVICES: 1 # 1 for monolithic mode
QUICKPIZZA_ENABLE_ALL_SERVICES: 1 # 1 for monolithic mode
QUICKPIZZA_OTEL_SERVICE_INSTANCE_ID: "quickpizza"
QUICKPIZZA_OTEL_SERVICE_NAME: "quickpizza"

2 changes: 1 addition & 1 deletion kubernetes/base/quickpizza/catalog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ spec:
name: quickpizza-env
optional: true
env:
- name: QUICKPIZZA_CATALOG
- name: QUICKPIZZA_ENABLE_CATALOG_SERVICE
value: "1"
- name: QUICKPIZZA_OTEL_SERVICE_INSTANCE_ID
valueFrom:
Expand Down
2 changes: 1 addition & 1 deletion kubernetes/base/quickpizza/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ spec:
name: quickpizza-env
optional: true
env:
- name: QUICKPIZZA_CONFIG
- name: QUICKPIZZA_ENABLE_CONFIG_SERVICE
value: "1"
- name: QUICKPIZZA_OTEL_SERVICE_INSTANCE_ID
valueFrom:
Expand Down
2 changes: 1 addition & 1 deletion kubernetes/base/quickpizza/copy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ spec:
name: quickpizza-env
optional: true
env:
- name: QUICKPIZZA_COPY
- name: QUICKPIZZA_ENABLE_COPY_SERVICE
value: "1"
- name: QUICKPIZZA_OTEL_SERVICE_INSTANCE_ID
valueFrom:
Expand Down
2 changes: 1 addition & 1 deletion kubernetes/base/quickpizza/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ configMapGenerator:
# Trust all incoming TraceIDs for demo purposes
- QUICKPIZZA_TRUST_CLIENT_TRACEID=true
# Microservice mode: Set to 0 to run services in separate pods
- QUICKPIZZA_ALL_SERVICES=0
- QUICKPIZZA_ENABLE_ALL_SERVICES=0
- QUICKPIZZA_CATALOG_ENDPOINT=http://quickpizza-catalog:3333
- QUICKPIZZA_COPY_ENDPOINT=http://quickpizza-copy:3333
- QUICKPIZZA_WS_ENDPOINT=http://quickpizza-ws:3333
Expand Down
2 changes: 1 addition & 1 deletion kubernetes/base/quickpizza/public-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ spec:
name: quickpizza-env
optional: true
env:
- name: QUICKPIZZA_FRONTEND
- name: QUICKPIZZA_ENABLE_PUBLIC_API_SERVICE
value: "1"
- name: QUICKPIZZA_OTEL_SERVICE_INSTANCE_ID
valueFrom:
Expand Down
2 changes: 1 addition & 1 deletion kubernetes/base/quickpizza/recommendations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ spec:
name: quickpizza-env
optional: true
env:
- name: QUICKPIZZA_RECOMMENDATIONS
- name: QUICKPIZZA_ENABLE_RECOMMENDATIONS_SERVICE
value: "1"
- name: QUICKPIZZA_OTEL_SERVICE_INSTANCE_ID
valueFrom:
Expand Down
2 changes: 1 addition & 1 deletion kubernetes/base/quickpizza/ws.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ spec:
name: quickpizza-env
optional: true
env:
- name: QUICKPIZZA_WS
- name: QUICKPIZZA_ENABLE_WS_SERVICE
value: "1"
- name: QUICKPIZZA_OTEL_SERVICE_INSTANCE_ID
valueFrom:
Expand Down