Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
77 changes: 57 additions & 20 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Gordon v2 - Production Dockerfile
# Gordon v3 - Production Dockerfile
# Multi-stage build for optimized container image
# Supports 4 component modes: core, proxy, registry, secrets

# Build stage
FROM golang:1.25-alpine AS builder
Expand Down Expand Up @@ -29,15 +30,24 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
FROM alpine:latest

# Install runtime dependencies
# - docker-cli: For core component to manage containers
# - pass, gnupg: For secrets component (password-store backend)
# - ca-certificates: TLS support
# - curl, wget: Health checks and debugging
# - tzdata: Timezone support
# - grpc-health-probe: For gRPC health checks
RUN apk add --no-cache \
ca-certificates \
docker-cli \
curl \
wget \
tzdata \
pass \
gnupg \
grpc-health-probe \
&& rm -rf /var/cache/apk/*
Comment on lines 39 to 48
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

grpc-health-probe alpine linux apk package

💡 Result:

grpc-health-probe (the grpc_health_probe binary from grpc-ecosystem/grpc-health-probe) is not packaged as an official Alpine apk (there’s no grpc-health-probe entry in Alpine’s package index; the closest similarly-named package is grpc-health-check, which is a different project). [1]

Recommended options on Alpine:

  1. Download the prebuilt static binary from GitHub Releases (common in Alpine-based images): the project publishes grpc_health_probe-linux-amd64, ...-arm64, etc. [2]
    Example (amd64):
apk add --no-cache wget ca-certificates
VER=v0.4.24
wget -qO /usr/local/bin/grpc_health_probe \
  https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/$VER/grpc_health_probe-linux-amd64
chmod +x /usr/local/bin/grpc_health_probe
  1. Build/install with Go:
apk add --no-cache go git
go install github.com/grpc-ecosystem/grpc-health-probe@latest

(This is the project’s documented “install from source” path.) [2]

Note: If you specifically meant an Alpine apk for something that does gRPC health checks, Alpine does have grpc-health-check in edge/testing, but it is not grpc-health-probe. [1]

Sources: Alpine package index (grpc-health-check) [1]; grpc-health-probe upstream install/release info [2].

[1] (pkgs.alpinelinux.org)
[2] (github.com)

Citations:


🏁 Script executed:

# Check if there are any references to grpc-health-probe in the codebase
rg "grpc-health-probe" --type-list | head -20
rg "grpc-health-probe"

Repository: bnema/gordon

Length of output: 616


🌐 Web query:

grpc-health-probe github releases download binary latest version

💡 Result:

Latest grpc-health-probe release on GitHub is v0.4.42 (published 2025-11-12). [1]

Download the latest binary (direct URL pattern)

Binaries are published as release assets named like:

grpc_health_probe-<os>-<arch>

Direct download URL pattern:

https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/v0.4.42/grpc_health_probe-<os>-<arch>

Examples:

  • Linux x86_64:
curl -L -o grpc_health_probe \
  https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/v0.4.42/grpc_health_probe-linux-amd64
chmod +x grpc_health_probe
  • macOS Apple Silicon:
curl -L -o grpc_health_probe \
  https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/v0.4.42/grpc_health_probe-darwin-arm64
chmod +x grpc_health_probe

The release also includes a checksums.txt asset for verifying downloads. [1]

If you just want the “latest” release page (to pick your OS/arch asset), it’s here: Releases → v0.4.42. [1]

Sources: GitHub Releases page for grpc-ecosystem/grpc-health-probe. [1]


🏁 Script executed:

cat -n Dockerfile | head -60

Repository: bnema/gordon

Length of output: 1888


Fix build break: grpc-health-probe isn't in Alpine repos.
CI reports apk add failing because the package doesn't exist. Download the binary instead (and respect multi-arch).

🛠️ Suggested fix (download grpc-health-probe binary)
 RUN apk add --no-cache \
     ca-certificates \
     docker-cli \
     curl \
     wget \
     tzdata \
     pass \
-    gnupg \
-    grpc-health-probe \
+    gnupg \
     && rm -rf /var/cache/apk/*
+
+ARG GRPC_HEALTH_PROBE_VERSION=v0.4.42
+ARG TARGETARCH
+RUN wget -qO /usr/local/bin/grpc_health_probe \
+    "https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-${TARGETARCH}" \
+ && chmod +x /usr/local/bin/grpc_health_probe
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
RUN apk add --no-cache \
ca-certificates \
docker-cli \
curl \
wget \
tzdata \
pass \
gnupg \
grpc-health-probe \
&& rm -rf /var/cache/apk/*
RUN apk add --no-cache \
ca-certificates \
docker-cli \
curl \
wget \
tzdata \
pass \
gnupg \
&& rm -rf /var/cache/apk/*
ARG GRPC_HEALTH_PROBE_VERSION=v0.4.42
ARG TARGETARCH
RUN wget -qO /usr/local/bin/grpc_health_probe \
"https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-${TARGETARCH}" \
&& chmod +x /usr/local/bin/grpc_health_probe
🧰 Tools
🪛 GitHub Actions: CI

[error] 39-39: Docker build failed: APK package 'grpc-health-probe' not found. Command in Dockerfile RUN apk add --no-cache ca-certificates docker-cli curl wget tzdata pass gnupg grpc-health-probe failed with exit code 1.

🪛 Hadolint (2.14.0)

[warning] 39-39: Pin versions in apk add. Instead of apk add <package> use apk add <package>=<version>

(DL3018)

🤖 Prompt for AI Agents
In `@Dockerfile` around lines 39 - 48, The Dockerfile RUN line currently installs
"grpc-health-probe" via apk which doesn't exist; change the Dockerfile so the
RUN step removes "grpc-health-probe" from the apk add list and instead downloads
the multi-arch grpc-health-probe binary (detecting architecture via uname -m or
TARGETARCH) from the GitHub releases, verifies it (e.g., make it executable and
optionally checksum), places it into /usr/local/bin, and cleans up; update the
RUN sequence that installs ca-certificates, docker-cli, curl, wget, tzdata,
pass, gnupg to no longer include grpc-health-probe and add the download+install
commands for grpc-health-probe in the same Dockerfile RUN layer to keep image
size minimal.


# Create non-root user
# Create non-root user (not used by default - components run as root for Docker socket access)
RUN adduser -D -s /bin/sh gordon

# Set working directory
Expand All @@ -49,24 +59,51 @@ COPY --from=builder /app/gordon .
# Create data directory
RUN mkdir -p /data && chown gordon:gordon /data

# Copy default configuration (optional)
COPY --chown=gordon:gordon gordon.toml.example /app/gordon.toml.example

# Switch to non-root user
USER gordon

# Expose ports
EXPOSE 8080 5000

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD wget --quiet --tries=1 --spider http://localhost:8080/health || exit 1

# Default command
CMD ["./gordon", "serve"]
# Copy default configuration
COPY --chown=gordon:gordon gordon.toml.example /app/gordon.toml

# Environment variables
# GORDON_COMPONENT: Which component to run (core|proxy|registry|secrets)
# GORDON_IMAGE: Self-image reference for sub-container deployment
# HEALTHCHECK_PORT: Port for health checks (varies by component)
ENV GORDON_COMPONENT="" \
GORDON_IMAGE="" \
HEALTHCHECK_PORT="5000" \
GORDON_LOG_LEVEL="info"

# Expose ports by component:
# - gordon-core: 5000 (admin API), 9090 (gRPC)
# - gordon-proxy: 80 (HTTP)
# - gordon-registry: 5000 (registry HTTP), 9092 (gRPC)
# - gordon-secrets: 9091 (gRPC)
EXPOSE 80 5000 9090 9091 9092

# Health check (component-aware)
# Core/Registry use HTTP on port 5000 by default
# Proxy uses HTTP on port 80
# Secrets uses gRPC health on 9091
HEALTHCHECK --interval=15s --timeout=5s --start-period=30s --retries=3 \
CMD \
if [ "$GORDON_COMPONENT" = "secrets" ]; then \
grpc-health-probe -addr=:9091 || exit 1; \
elif [ -n "$GORDON_COMPONENT" ]; then \
wget --quiet --tries=1 --spider http://localhost:${HEALTHCHECK_PORT}/v2/health || \
wget --quiet --tries=1 --spider http://localhost:${HEALTHCHECK_PORT}/health || \
exit 1; \
else \
wget --quiet --tries=1 --spider http://localhost:${HEALTHCHECK_PORT}/v2/health || \
wget --quiet --tries=1 --spider http://localhost:${HEALTHCHECK_PORT}/health || \
exit 1; \
fi

# Use ENTRYPOINT so we can pass arguments (like --component) after the command
# For v3 sub-containers, use: docker run gordon:v3-test --component=core
ENTRYPOINT ["./gordon", "serve"]
CMD []

# Metadata
LABEL maintainer="bnemam"
LABEL version="2.0"
LABEL description="Event-driven container deployment platform"
LABEL org.opencontainers.image.source="https://github.com/bnema/gordon"
LABEL version="3.0"
LABEL description="Event-driven container deployment platform - v3 sub-container architecture"
LABEL org.opencontainers.image.source="https://github.com/bnema/gordon"
LABEL gordon.components="core,proxy,registry,secrets"
39 changes: 35 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# This Makefile is used for dev purposes
# Variables
REPO := ghcr.io/bnema/gordon
TAG := v2-dev
DEV_TAG := v2-dev-$(shell date +%Y%m%d-%H%M%S)
TAG := v3-dev
DEV_TAG := v3-dev-$(shell date +%Y%m%d-%H%M%S)
DIST_DIR := ./dist
ENGINE := podman
ENGINE ?= podman

# Version information
VERSION := $(shell git describe --tags --always --dirty)
Expand All @@ -22,7 +22,8 @@ ARCHS := amd64 arm64

# Phony targets
.PHONY: all build build-push clean dev-release \
test test-short test-race test-coverage \
test test-short test-race test-coverage test-integration test-integration-quick \
test-integration-build test-integration-clean \
lint fmt check mocks clean-test help

# Default target
Expand All @@ -46,6 +47,11 @@ mocks: ## Generate mocks using mockery
@mockery
@echo "Mocks generated successfully"

proto: ## Generate Go code from protobuf definitions
@echo "Generating protobuf code..."
@buf generate
@echo "Protobuf code generated successfully"

check: lint test ## Run lint and tests

##@ Testing
Expand Down Expand Up @@ -77,6 +83,31 @@ test-usecase: ## Run usecase layer tests only
test-adapter: ## Run adapter layer tests only
@go test -v ./internal/adapters/...

test-integration-clean: ## Clean up leftover test containers from previous runs
@echo "Cleaning up test containers..."
@-docker stop gordon-core-test gordon-secrets gordon-registry gordon-proxy 2>/dev/null || true
@-docker rm -f gordon-core-test gordon-secrets gordon-registry gordon-proxy 2>/dev/null || true
@-docker network rm gordon-internal 2>/dev/null || true
@-docker network rm -f testcontainers 2>/dev/null || true
@-docker network prune -f 2>/dev/null || true
@-docker ps -a --filter "label=gordon.component" --format "{{.ID}}" | xargs -r docker rm -f 2>/dev/null || true
@echo "Cleanup complete"

test-integration-build: build-local ## Build Gordon test image for integration tests
@echo "Building Gordon test image..."
@docker build -t gordon:v3-test .
@echo "Test image built: gordon:v3-test"

test-integration: test-integration-clean test-integration-build ## Run integration tests (max 10min)
@echo "Running integration tests..."
@docker pull ghcr.io/bnema/go-hello-world-http:latest || true
@go test -v -timeout 10m ./tests/integration/... 2>&1 | tee test-integration.log
@echo "Integration tests complete. Log: test-integration.log"

test-integration-quick: test-integration-clean test-integration-build ## Run quick integration tests (startup + gRPC only, ~3min)
@echo "Running quick integration tests..."
@go test -v -timeout 5m -run "Test01|Test02" ./tests/integration/...

Comment on lines +86 to +110
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

git ls-files | grep -i makefile

Repository: bnema/gordon

Length of output: 64


🏁 Script executed:

cat -n Makefile | head -150

Repository: bnema/gordon

Length of output: 6160


Integration targets hardcode docker instead of using $(ENGINE), breaking podman setups.

The ENGINE variable is defined as ?= podman (line 7) and used consistently in other targets (e.g., build-push), but all integration test targets hardcode docker commands. This breaks integration tests when ENGINE is set to podman or any alternative container engine.

Replace all docker calls in test-integration-clean, test-integration-build, test-integration, and test-integration-quick targets with $(ENGINE).

Diff
 test-integration-clean: ## Clean up leftover test containers from previous runs
 	`@echo` "Cleaning up test containers..."
-	`@-docker` stop gordon-core-test gordon-secrets gordon-registry gordon-proxy 2>/dev/null || true
-	`@-docker` rm -f gordon-core-test gordon-secrets gordon-registry gordon-proxy 2>/dev/null || true
-	`@-docker` network rm gordon-internal 2>/dev/null || true
-	`@-docker` network rm -f testcontainers 2>/dev/null || true
-	`@-docker` network prune -f 2>/dev/null || true
-	`@-docker` ps -a --filter "label=gordon.component" --format "{{.ID}}" | xargs -r docker rm -f 2>/dev/null || true
+	`@-`$(ENGINE) stop gordon-core-test gordon-secrets gordon-registry gordon-proxy 2>/dev/null || true
+	`@-`$(ENGINE) rm -f gordon-core-test gordon-secrets gordon-registry gordon-proxy 2>/dev/null || true
+	`@-`$(ENGINE) network rm gordon-internal 2>/dev/null || true
+	`@-`$(ENGINE) network rm -f testcontainers 2>/dev/null || true
+	`@-`$(ENGINE) network prune -f 2>/dev/null || true
+	`@-`$(ENGINE) ps -a --filter "label=gordon.component" --format "{{.ID}}" | xargs -r $(ENGINE) rm -f 2>/dev/null || true
 	`@echo` "Cleanup complete"
 
 test-integration-build: build-local ## Build Gordon test image for integration tests
 	`@echo` "Building Gordon test image..."
-	`@docker` build -t gordon:v3-test .
+	@$(ENGINE) build -t gordon:v3-test .
 	`@echo` "Test image built: gordon:v3-test"
 
 test-integration: test-integration-clean test-integration-build ## Run integration tests (max 10min)
 	`@echo` "Running integration tests..."
-	`@docker` pull ghcr.io/bnema/go-hello-world-http:latest || true
+	@$(ENGINE) pull ghcr.io/bnema/go-hello-world-http:latest || true
 	`@go` test -v -timeout 10m ./tests/integration/... 2>&1 | tee test-integration.log
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test-integration-clean: ## Clean up leftover test containers from previous runs
@echo "Cleaning up test containers..."
@-docker stop gordon-core-test gordon-secrets gordon-registry gordon-proxy 2>/dev/null || true
@-docker rm -f gordon-core-test gordon-secrets gordon-registry gordon-proxy 2>/dev/null || true
@-docker network rm gordon-internal 2>/dev/null || true
@-docker network rm -f testcontainers 2>/dev/null || true
@-docker network prune -f 2>/dev/null || true
@-docker ps -a --filter "label=gordon.component" --format "{{.ID}}" | xargs -r docker rm -f 2>/dev/null || true
@echo "Cleanup complete"
test-integration-build: build-local ## Build Gordon test image for integration tests
@echo "Building Gordon test image..."
@docker build -t gordon:v3-test .
@echo "Test image built: gordon:v3-test"
test-integration: test-integration-clean test-integration-build ## Run integration tests (max 10min)
@echo "Running integration tests..."
@docker pull ghcr.io/bnema/go-hello-world-http:latest || true
@go test -v -timeout 10m ./tests/integration/... 2>&1 | tee test-integration.log
@echo "Integration tests complete. Log: test-integration.log"
test-integration-quick: test-integration-clean test-integration-build ## Run quick integration tests (startup + gRPC only, ~3min)
@echo "Running quick integration tests..."
@go test -v -timeout 5m -run "Test01|Test02" ./tests/integration/...
test-integration-clean: ## Clean up leftover test containers from previous runs
`@echo` "Cleaning up test containers..."
`@-`$(ENGINE) stop gordon-core-test gordon-secrets gordon-registry gordon-proxy 2>/dev/null || true
`@-`$(ENGINE) rm -f gordon-core-test gordon-secrets gordon-registry gordon-proxy 2>/dev/null || true
`@-`$(ENGINE) network rm gordon-internal 2>/dev/null || true
`@-`$(ENGINE) network rm -f testcontainers 2>/dev/null || true
`@-`$(ENGINE) network prune -f 2>/dev/null || true
`@-`$(ENGINE) ps -a --filter "label=gordon.component" --format "{{.ID}}" | xargs -r $(ENGINE) rm -f 2>/dev/null || true
`@echo` "Cleanup complete"
test-integration-build: build-local ## Build Gordon test image for integration tests
`@echo` "Building Gordon test image..."
@$(ENGINE) build -t gordon:v3-test .
`@echo` "Test image built: gordon:v3-test"
test-integration: test-integration-clean test-integration-build ## Run integration tests (max 10min)
`@echo` "Running integration tests..."
@$(ENGINE) pull ghcr.io/bnema/go-hello-world-http:latest || true
`@go` test -v -timeout 10m ./tests/integration/... 2>&1 | tee test-integration.log
`@echo` "Integration tests complete. Log: test-integration.log"
test-integration-quick: test-integration-clean test-integration-build ## Run quick integration tests (startup + gRPC only, ~3min)
`@echo` "Running quick integration tests..."
`@go` test -v -timeout 5m -run "Test01|Test02" ./tests/integration/...
🤖 Prompt for AI Agents
In `@Makefile` around lines 86 - 110, The integration Makefile targets use the
literal "docker" binary which breaks non-docker engines; update all container
engine invocations in the targets test-integration-clean,
test-integration-build, test-integration, and test-integration-quick to use the
existing variable $(ENGINE) instead of hardcoded "docker" (e.g., replace docker
stop/rm/network/prune/ps/build/pull with $(ENGINE)
stop/rm/network/prune/ps/build/pull) so the targets respect the configurable
ENGINE (defined ?= podman).

##@ Build

build: ## Build binaries for linux (amd64 and arm64)
Expand Down
19 changes: 19 additions & 0 deletions buf.gen.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
version: v2

managed:
enabled: true
override:
- file_option: go_package
value: github.com/bnema/gordon/internal/grpc

plugins:
- remote: buf.build/protocolbuffers/go:v1.36.4
out: internal/grpc
opt:
- paths=import

- remote: buf.build/grpc/go:v1.5.1
out: internal/grpc
opt:
- paths=import
- require_unimplemented_servers=false
Comment on lines +9 to +19
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

buf.build protocolbuffers go latest version 2025 2026

💡 Result:

As of February 1, 2026, the latest upstream Go Protobuf release (the protoc-gen-go / google.golang.org/protobuf tool+runtime that Buf’s buf.build/protocolbuffers/go tracks) is v1.36.11 (released December 12, 2025). [1]

So, on Buf Schema Registry:

  • Latest remote plugin version to pin: buf.build/protocolbuffers/go:v1.36.11 (use this in buf.gen.yaml). [2][1]
  • If you omit :version, Buf uses the latest: remote: buf.build/protocolbuffers/go. [2]

Sources: [1] [2]


🌐 Web query:

buf.build grpc go latest version 2025 2026

💡 Result:

As of February 1, 2026, the latest Buf Schema Registry (BSR) remote plugin for generating gRPC-Go stubs is referenced as:

  • buf.build/grpc/go (latest, unpinned)

Buf’s docs note that if you omit the version, Buf will use the latest plugin version automatically. [1]

If you want to pin the plugin version, the most recent upstream protoc-gen-go-grpc release tag shown in the gRPC-Go repo is:

  • cmd/protoc-gen-go-grpc/v1.6.0 (dated Nov 27, year shown on the releases page listing) [2]

The gRPC-Go library itself shows a newer overall release tag:

  • v1.78.0 (dated Dec 23, year shown on the releases page listing) [2]

Typical buf.gen.yaml (use latest):

version: v2
plugins:
  - remote: buf.build/protocolbuffers/go
    out: gen/go
    opt: paths=source_relative
  - remote: buf.build/grpc/go
    out: gen/go
    opt: paths=source_relative

[1]

Pinned form (example):

- remote: buf.build/grpc/go:v1.6.0
  out: gen/go
  opt: paths=source_relative

(version number based on the latest protoc-gen-go-grpc tag listed above) [2]

Sources: [1] [2]


Update plugin versions to latest stable releases.

The configuration uses buf.build/protocolbuffers/go:v1.36.4 and buf.build/grpc/go:v1.5.1, which are outdated. Update to buf.build/protocolbuffers/go:v1.36.11 (released December 2025) and buf.build/grpc/go:v1.6.0 (released November 2025) to include the latest bug fixes and improvements.

🤖 Prompt for AI Agents
In `@buf.gen.yaml` around lines 9 - 19, Update the two plugin remote version
strings under the plugins block: change "buf.build/protocolbuffers/go:v1.36.4"
to "buf.build/protocolbuffers/go:v1.36.11" and change "buf.build/grpc/go:v1.5.1"
to "buf.build/grpc/go:v1.6.0", keeping the existing out and opt entries (e.g.,
the paths=import and require_unimplemented_servers=false options) unchanged so
only the remote versions are bumped.

18 changes: 18 additions & 0 deletions buf.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
version: v2

modules:
- path: api/proto
name: buf.build/bnema/gordon

deps:
- buf.build/googleapis/googleapis

lint:
use:
- DEFAULT
except:
- PACKAGE_VERSION_SUFFIX

breaking:
use:
- FILE
Loading
Loading