Skip to content
Draft
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
23 changes: 16 additions & 7 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ module(
)

# Go rules for building Go code
bazel_dep(name = "rules_go", version = "0.56.1")
bazel_dep(name = "rules_go", version = "0.60.0")

# Gazelle for generating BUILD files automatically
bazel_dep(name = "gazelle", version = "0.44.0")
bazel_dep(name = "gazelle", version = "0.50.0")

# Rust rules for building Rust code
bazel_dep(name = "rules_rust", version = "0.67.0")
Expand All @@ -17,12 +17,12 @@ bazel_dep(name = "rules_rust", version = "0.67.0")
bazel_dep(name = "rules_rust_prost", version = "0.67.0")

# Proto rules for protocol buffers
bazel_dep(name = "rules_proto", version = "6.0.2")
bazel_dep(name = "protobuf", version = "29.0")
bazel_dep(name = "rules_proto", version = "7.1.0")
bazel_dep(name = "protobuf", version = "29.1")

# Configure Go SDK
go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk")
go_sdk.download(version = "1.24.6")
go_sdk.download(version = "1.24.13")

# Configure Go dependencies from go.work
go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
Expand All @@ -34,7 +34,16 @@ go_deps.config(
"GOPROXY": "https://proxy.golang.org,direct",
},
)
use_repo(go_deps, "org_golang_google_grpc")
use_repo(
go_deps,
"com_github_grpc_ecosystem_go_grpc_middleware_providers_prometheus",
"com_github_grpc_ecosystem_go_grpc_middleware_v2",
"com_github_open_feature_go_sdk",
"com_github_prometheus_client_golang",
"io_opencensus_go",
"org_golang_google_grpc",
"org_golang_google_protobuf",
)

# Configure Rust toolchain
rust = use_extension("@rules_rust//rust:extensions.bzl", "rust")
Expand All @@ -57,4 +66,4 @@ rust.toolchain(edition = "2024")
# crate.from_specs(
# cargo_lockfile = "//:Cargo.Bazel.lock",
# )
# use_repo(crate, "crates")
# use_repo(crate, "crates")
3,802 changes: 2,944 additions & 858 deletions MODULE.bazel.lock

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@
- Go client and server
- Rust client and server
- Shared protobuf definitions with automatic code generation
- **foundry/**: Production-style Go/Kubernetes service chassis
- gRPC service with health, reflection, channelz, and OpenFeature experiments
- JSON logs, Prometheus metrics, pprof, expvar, and zPage-style debugging
- Kubernetes Deployment, Service, ServiceMonitor, and canary examples
- **Bazel configuration**: Pre-configured rules for Go (`rules_go`), Rust (`rules_rust`), and protobuf code generation

## Use Cases

- Starting a polyglot microservices project
- Learning how to structure multi-language Bazel workspaces
- Understanding gRPC service implementations across different languages
- Evaluating Bazel for multi-language projects
- Starting a Go service on Kubernetes with practical observability and debugging defaults
- Evaluating Bazel for multi-language projects
5 changes: 5 additions & 0 deletions foundry/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
alias(
name = "foundryd",
actual = "//foundry/cmd/foundryd:foundryd",
visibility = ["//visibility:public"],
)
14 changes: 14 additions & 0 deletions foundry/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM golang:1.24 AS build

WORKDIR /src
COPY foundry/go.mod foundry/go.sum ./
RUN go mod download
COPY foundry/ ./
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" -o /out/foundryd ./cmd/foundryd

FROM gcr.io/distroless/static-debian12:nonroot

COPY --from=build /out/foundryd /foundryd
USER nonroot:nonroot
EXPOSE 50051 8080
ENTRYPOINT ["/foundryd"]
132 changes: 132 additions & 0 deletions foundry/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Foundry

Foundry is a Go/Kubernetes service chassis: a small, explicit reference implementation for running a production-style gRPC service with debugging, metrics, structured logs, health, and feature flags.

It intentionally keeps the middleware stack visible instead of hiding it behind a framework. The goal is to show the operating model a team can copy into a real service.

## What It Includes

- gRPC API: `foundry.v1.FoundryService`
- Example RPCs: `SayHello`, `GetExperiment`, `GetRuntimeInfo`
- gRPC health, reflection, and channelz
- JSON `slog` access logs with request IDs, status codes, latency, service metadata, and flag decisions
- Prometheus metrics for Go/process runtime and gRPC golden signals
- HTTP admin/debug port with `/admin`, `/metrics`, `/debug/pprof/`, `/debug/vars`, `/debug/rpcz`, and `/debug/tracez`
- OpenFeature local feature flag provider with `foundry.new_greeting`
- Kubernetes manifests with probes, resources, security context, ServiceMonitor, and stable/canary examples
- A feature matrix comparing Foundry with Kratos, Go kit, go-zero, Encore.go, Service Weaver, Spring Boot Actuator, App Engine, Cloud Run, Dapr, Envoy, Istio, and Linkerd

## Run Locally

From this directory:

```bash
go run ./cmd/foundryd
```

The service listens on:

- gRPC: `localhost:50051`
- Admin/debug HTTP: `localhost:8080`

Example calls:

```bash
grpcurl -plaintext -d '{"name":"Ada"}' localhost:50051 foundry.v1.FoundryService/SayHello
grpcurl -plaintext -d '{}' localhost:50051 foundry.v1.FoundryService/GetExperiment
grpcurl -plaintext -d '{}' localhost:50051 foundry.v1.FoundryService/GetRuntimeInfo
grpcurl -plaintext localhost:50051 grpc.health.v1.Health/Check
```

Enable the sample experiment:

```bash
FEATURE_FOUNDRY_NEW_GREETING=true go run ./cmd/foundryd
```

## Debug Surface

The admin port is designed for internal access and port-forwarding, not public ingress.

```bash
curl localhost:8080/admin
curl localhost:8080/admin/info
curl localhost:8080/admin/ready
curl localhost:8080/metrics
go tool pprof http://localhost:8080/debug/pprof/profile
```

Legacy OpenCensus zPages are available at:

- `http://localhost:8080/debug/rpcz`
- `http://localhost:8080/debug/tracez`

They are included as a compatibility/debug reference because the request explicitly asked for a zPage-like interface. New instrumentation should prefer Prometheus/OpenTelemetry-compatible metrics and gRPC channelz/reflection.

## Configuration

| Environment variable | Default | Purpose |
| --- | --- | --- |
| `FOUNDRY_SERVICE_NAME` | `foundry` | Service name in logs, metrics, and runtime info |
| `FOUNDRY_VERSION` | `dev` | Version label |
| `FOUNDRY_COMMIT` | `unknown` | Build commit label |
| `FOUNDRY_ENVIRONMENT` | `local` | Environment label |
| `FOUNDRY_REVISION` | `local` | Revision/canary label |
| `FOUNDRY_GRPC_ADDR` | `:50051` | gRPC listen address |
| `FOUNDRY_HTTP_ADDR` | `:8080` | Admin/debug HTTP listen address |
| `FOUNDRY_LOG_LEVEL` | `info` | `debug`, `info`, `warn`, or `error` |
| `FEATURE_FOUNDRY_NEW_GREETING` | `false` | OpenFeature-backed sample experiment |
| `FOUNDRY_SHUTDOWN_GRACE_PERIOD` | `20s` | Graceful shutdown timeout |

## Kubernetes

Apply the reference manifests:

```bash
kubectl apply -f k8s/configmap.yaml
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
```

If Prometheus Operator is installed:

```bash
kubectl apply -f k8s/servicemonitor.yaml
```

Canary example:

```bash
kubectl apply -f k8s/canary.yaml
```

Port-forward the internal admin port:

```bash
kubectl port-forward deploy/foundry 8080:8080
```

## Development

Regenerate protobuf bindings after editing `proto/foundry/v1/foundry.proto`:

```bash
PATH="$(go env GOPATH)/bin:$PATH" buf generate
```

Run tests:

```bash
go test ./...
```

Bazel target:

```bash
bazel build //foundry:foundryd
```

## Related Docs

- [Feature matrix](docs/feature-matrix.md)
- [Research notes](docs/research-notes.md)
10 changes: 10 additions & 0 deletions foundry/buf.gen.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: v1
plugins:
- plugin: go
out: .
opt:
- paths=source_relative
- plugin: go-grpc
out: .
opt:
- paths=source_relative
9 changes: 9 additions & 0 deletions foundry/buf.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
version: v1
lint:
use:
- DEFAULT
except:
- PACKAGE_DIRECTORY_MATCH
breaking:
use:
- FILE
31 changes: 31 additions & 0 deletions foundry/cmd/foundryd/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
load("@rules_go//go:def.bzl", "go_binary", "go_library")

go_library(
name = "foundryd_lib",
srcs = ["main.go"],
importpath = "github.com/bazelment/hajime/foundry/cmd/foundryd",
visibility = ["//visibility:private"],
deps = [
"//foundry/internal/admin",
"//foundry/internal/config",
"//foundry/internal/feature",
"//foundry/internal/observability",
"//foundry/internal/service",
"//foundry/proto/foundry/v1:foundry_go_proto",
"@com_github_grpc_ecosystem_go_grpc_middleware_v2//interceptors/logging",
"@com_github_grpc_ecosystem_go_grpc_middleware_v2//interceptors/recovery",
"@org_golang_google_grpc//:grpc",
"@org_golang_google_grpc//channelz/service",
"@org_golang_google_grpc//codes",
"@org_golang_google_grpc//health",
"@org_golang_google_grpc//health/grpc_health_v1",
"@org_golang_google_grpc//reflection",
"@org_golang_google_grpc//status",
],
)

go_binary(
name = "foundryd",
embed = [":foundryd_lib"],
visibility = ["//visibility:public"],
)
Loading