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
28 changes: 24 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,18 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4

- name: Build Docker image
run: docker build -f Containerfile .
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build multi-arch Docker image
uses: docker/build-push-action@v5
with:
context: .
file: Containerfile
platforms: linux/amd64,linux/arm64
push: false
cache-from: type=gha
cache-to: type=gha,mode=max

container-scan:
name: Container Scan
Expand All @@ -77,8 +87,18 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4

- name: Build Docker image
run: docker build -f Containerfile -t local/app:latest .
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build Docker image for scanning
uses: docker/build-push-action@v5
with:
context: .
file: Containerfile
platforms: linux/amd64
load: true
tags: local/app:latest
cache-from: type=gha

- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@0.28.0
Expand Down
43 changes: 0 additions & 43 deletions .github/workflows/publish.yml

This file was deleted.

41 changes: 39 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ permissions:
contents: write
issues: write
pull-requests: write
packages: write

jobs:
release:
Expand All @@ -26,9 +27,45 @@ jobs:
node-version: 'lts/*'

- name: Install dependencies
run: npm install --no-save semantic-release@^24.2.0 @semantic-release/commit-analyzer@^13.0.0 @semantic-release/release-notes-generator@^14.0.1 @semantic-release/github@^11.0.1
run: npm install --no-save semantic-release@^24.2.0 @semantic-release/commit-analyzer@^13.0.0 @semantic-release/release-notes-generator@^14.0.1 @semantic-release/github@^11.0.1 @semantic-release/exec@^6.0.3

- name: Semantic Release
uses: cycjimmy/semantic-release-action@v4
id: semantic
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx semantic-release@^24.2.0

- name: Docker meta
id: meta
if: steps.semantic.outputs.new_release_published == 'true'
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=raw,value=${{ steps.semantic.outputs.new_release_version }}
type=raw,value=latest

- name: Set up Docker Buildx
if: steps.semantic.outputs.new_release_published == 'true'
uses: docker/setup-buildx-action@v3

- name: Log in to the Container registry
if: steps.semantic.outputs.new_release_published == 'true'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push multi-arch Docker image
if: steps.semantic.outputs.new_release_published == 'true'
uses: docker/build-push-action@v5
with:
context: .
file: Containerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
18 changes: 18 additions & 0 deletions .kiro/steering/product.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Product Overview

Archy is a Kubernetes mutating admission webhook that automatically ensures Pods are scheduled on nodes with compatible architectures in multi-architecture clusters.

## Core Functionality

- **Architecture Detection**: Inspects container image manifests to determine supported platforms (amd64, arm64, etc.)
- **Automatic Pod Mutation**: Adds `kubernetes.io/arch` nodeSelector to Pods when a single common architecture is found
- **Multi-Arch Support**: Allows Kubernetes scheduler to handle placement when images support multiple architectures
- **Private Registry Support**: Authenticates with private registries using Pod's imagePullSecrets and ServiceAccount credentials
- **Safety First**: Rejects Pods when images have no common supported architecture

## Key Behaviors

- Skips Pods that already have a nodeSelector defined
- Fails closed (rejects Pod) if architecture inspection fails or no common platform exists
- Allows scheduler flexibility when multiple common architectures are available
- Excludes system namespaces (kube-system, kube-public) and self (archy-webhook) from processing
54 changes: 54 additions & 0 deletions .kiro/steering/structure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Project Structure

## Directory Layout

```
archy/
├── cmd/webhook/ # Application entry point
│ └── main.go # HTTP server setup, signal handling, dependency injection
├── pkg/ # Core business logic packages
│ ├── inspector/ # Container registry inspection
│ │ ├── inspector.go # Platform detection interface and implementation
│ │ └── auth.go # Kubernetes authentication for private registries
│ └── webhook/ # Admission webhook logic
│ ├── handler.go # HTTP handler, AdmissionReview processing, Pod mutation
│ └── handler_test.go # Unit tests with mock inspector
├── deploy/ # Kubernetes manifests
│ ├── deployment.yaml # Webhook deployment and service
│ └── webhook-config.yaml # MutatingWebhookConfiguration
├── certs/ # TLS certificates for webhook
├── scripts/ # Build and setup scripts
└── bin/ # Build output directory
```

## Code Organization Patterns

### Package Structure
- **cmd/**: Application entry points only, minimal logic
- **pkg/**: Reusable packages with clear interfaces
- **deploy/**: Infrastructure as code, Kubernetes manifests

### Interface Design
- `Inspector` interface in `pkg/inspector` enables testing and future extensibility
- Dependency injection pattern in main.go for clean separation

### Error Handling
- Fail-safe approach: reject Pods when architecture cannot be determined
- Structured error messages in AdmissionResponse
- Context propagation for request timeouts

### Testing Patterns
- Mock implementations for external dependencies (registries, Kubernetes API)
- Table-driven tests covering edge cases
- Unit tests focus on business logic, not HTTP plumbing

### Configuration
- Command-line flags for server configuration (port, TLS certs)
- Environment-based configuration for Kubernetes client (in-cluster config)
- Kubernetes-native configuration via MutatingWebhookConfiguration

### Naming Conventions
- Go standard naming (PascalCase for exported, camelCase for unexported)
- Package names are lowercase, single word when possible
- Interface names end with -er suffix (Inspector)
- Test files use `_test.go` suffix with same package name
54 changes: 54 additions & 0 deletions .kiro/steering/tech.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Technology Stack

## Language & Runtime
- **Go 1.25.0** - Primary language
- **Kubernetes API** - Built for Kubernetes admission webhook pattern

## Key Dependencies
- `github.com/google/go-containerregistry` - Container registry inspection and authentication
- `k8s.io/api` & `k8s.io/apimachinery` - Kubernetes API types and utilities
- `k8s.io/client-go` - Kubernetes client library for accessing secrets/ServiceAccounts

## Architecture Pattern
- **Admission Webhook**: HTTP server that receives AdmissionReview requests from Kubernetes API server
- **Interface-based Design**: `Inspector` interface allows for testing with mocks
- **Graceful Shutdown**: Proper signal handling and server shutdown

## Build System

### Makefile Targets
```bash
# Build for current platform
make build

# Cross-compile for specific architectures
make build-amd64 # Linux AMD64
make build-arm64 # Linux ARM64

# Clean build artifacts
make clean
```

### Build Configuration
- Binary output: `bin/webhook`
- CGO disabled for static binaries
- Linux target for container deployment

## Development & Testing

### Running Tests
```bash
go test ./... # Run all tests
go test ./pkg/webhook -v # Run webhook tests with verbose output
```

### Local Development
- Uses Tilt for local Kubernetes development
- TLS certificates generated via `scripts/gen-certs.sh`
- Health check endpoint at `/healthz`

## Deployment
- **Container-based**: Containerfile for building images
- **Kubernetes native**: Deployed as Deployment + Service + MutatingWebhookConfiguration
- **TLS required**: Admission webhooks must use HTTPS
- **RBAC**: Requires access to secrets in target namespaces for private registry authentication
10 changes: 7 additions & 3 deletions Containerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
FROM golang:1.25-alpine AS builder
FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS builder
ARG TARGETPLATFORM
ARG BUILDPLATFORM
ARG TARGETOS
ARG TARGETARCH
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
# CGO_ENABLED=0 for static binary
RUN CGO_ENABLED=0 go build -o webhook ./cmd/webhook
# CGO_ENABLED=0 for static binary, build for target architecture
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o webhook ./cmd/webhook

FROM scratch
WORKDIR /app
Expand Down
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
.PHONY: build build-amd64 build-arm64 clean
.PHONY: build build-amd64 build-arm64 build-multiarch build-multiarch-push clean

BINARY_NAME=webhook
BUILD_DIR=bin
IMAGE_TAG?=archy-webhook:latest

build:
go build -o $(BUILD_DIR)/$(BINARY_NAME) ./cmd/webhook
Expand All @@ -12,5 +13,11 @@ build-amd64:
build-arm64:
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o $(BUILD_DIR)/$(BINARY_NAME)-arm64 ./cmd/webhook

build-multiarch:
docker buildx build --platform linux/amd64,linux/arm64 -t archy-webhook:latest .

build-multiarch-push:
docker buildx build --platform linux/amd64,linux/arm64 -t $(IMAGE_TAG) --push .

clean:
rm -rf $(BUILD_DIR)
Loading