Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
3d86b90
Working on design of the origincache
plombardi89 May 5, 2026
6ab683f
Review round 1 rework
plombardi89 May 5, 2026
118c300
Update design.md with more review work
plombardi89 May 5, 2026
1f532fe
push further review changes
plombardi89 May 5, 2026
38a220f
more updates for consistency
plombardi89 May 5, 2026
d33d8bc
clarity updates to brief and design plus handle negative metadata case
plombardi89 May 5, 2026
2bbc6a5
handle gaps discovered on review pass
plombardi89 May 5, 2026
bea7935
simplify the spooler
plombardi89 May 6, 2026
0e89777
switch back to local token bucket rate limiting with notes about defe…
plombardi89 May 7, 2026
d1812e1
Add orca origin cache: implementation, integration tests, manifests
plombardi89 May 8, 2026
a712c2a
Make orca comments self-contained
plombardi89 May 9, 2026
f6ab552
Trim unused exported surface in orca packages
plombardi89 May 11, 2026
fb3b8a0
Add orca review doc
plombardi89 May 11, 2026
32bfa95
Fix singleflight stale-entry race in metadata.LookupOrFetch (B2)
plombardi89 May 11, 2026
8ca0b29
Preserve previous peer snapshot on DNS error (B3)
plombardi89 May 11, 2026
b670fa2
Tier 3 cleanups in orca
plombardi89 May 11, 2026
ed19297
Pass seekable readers through PutChunk without rebuffering (Q5)
plombardi89 May 11, 2026
3ec537e
Plumb info.Size through fetch and cluster RPC; validate origin body (…
plombardi89 May 11, 2026
1069a56
Set Content-Length on internal-fill responses (B7)
plombardi89 May 11, 2026
b1bfb3b
Peek first chunk before committing edge response headers (B4)
plombardi89 May 11, 2026
236119d
Lock orca block-blob and versioning gates unconditionally (B1)
plombardi89 May 11, 2026
a664ca8
Serve zero-byte objects with 200 + empty body (B2)
plombardi89 May 11, 2026
3eb3906
Remove cross-replica wall timeout; add WithHTTPClient seam (B9)
plombardi89 May 11, 2026
70cc6a3
Quote Azure If-Match header value (B7)
plombardi89 May 11, 2026
3d5736c
cachestore/s3: use HTTP status code for error mapping (B3, B4, B6)
plombardi89 May 11, 2026
5a7498b
origin/awss3: use HTTP status code for error mapping (Q10)
plombardi89 May 11, 2026
4c9898a
Inject slog.Logger into fetch.Coordinator (O4)
plombardi89 May 11, 2026
b166b6b
Add ops listener with /healthz and /readyz (O2)
plombardi89 May 11, 2026
d0f009b
Length-prefix metadata cache keys to remove pipe collision (C3)
plombardi89 May 11, 2026
29be167
Don't bump cluster refresh streak on ctx-canceled (B11)
plombardi89 May 11, 2026
65d1c69
Targeted orca cleanups (B5, B8, B10, Q1, Q5, Q8, Q9, S1, S2, doc)
plombardi89 May 11, 2026
c145e90
Update orca review doc with second-pass findings (docs)
plombardi89 May 11, 2026
7dbdeb8
Add cfg.Logging.Level + ORCA_LOG_LEVEL override + AddSource
plombardi89 May 11, 2026
c521376
Inject slog.Logger into metadata, chunkcatalog, cluster
plombardi89 May 11, 2026
47cc6b9
Inject slog.Logger into cachestore/s3, awss3, azureblob
plombardi89 May 11, 2026
6578bb3
Add debug-level tracing through fetch.Coordinator
plombardi89 May 11, 2026
f0536c4
Add debug-level tracing through metadata + chunkcatalog
plombardi89 May 11, 2026
f7cd967
Add debug-level tracing through cachestore/s3 driver
plombardi89 May 11, 2026
3cea7e2
Add debug-level tracing through origin/awss3 + azureblob
plombardi89 May 11, 2026
d75d48b
Add debug-level tracing through edge + internal server handlers
plombardi89 May 11, 2026
43b536b
Add debug-level tracing through cluster lifecycle
plombardi89 May 11, 2026
8ec6454
Add 'Observability' section to orca review doc
plombardi89 May 11, 2026
de5a260
Expose Azurite as NodePort for host-side seeder access
plombardi89 May 11, 2026
ac54893
Add hack/cmd/orcaseed dev tool for seeding the dev-cluster origin
plombardi89 May 11, 2026
8eda541
Add hack/orca/quickstart.md end-to-end dev cluster recipe
plombardi89 May 11, 2026
0d7ca94
Track hack/orca/ dev harness and apply cleanup pass
plombardi89 May 11, 2026
f891e51
Drop object_size==0 sentinel; tighten in/out validation (C2, C3, C4)
plombardi89 May 12, 2026
4cd81e3
Simplify chunkcatalog to presence-only (C1, H5)
plombardi89 May 12, 2026
ff4bb45
Re-order runFill to commit-after-serve (H1)
plombardi89 May 12, 2026
33bb51b
Reject origin Head responses with empty ETag (H7)
plombardi89 May 12, 2026
257fe34
orcaseed: per-blob seeding so --seed survives concurrency (C6)
plombardi89 May 12, 2026
6c59969
deploy-credentials.sh: gate Azurite dev-key fallback by account (C7)
plombardi89 May 12, 2026
2f7ecf1
cluster: bound connect / TLS handshake timeouts in HTTP client (H4)
plombardi89 May 12, 2026
3c61552
cachestore/s3.PutChunk: validate seekable reader length (H6)
plombardi89 May 12, 2026
0eef8de
app.Wait: symmetric errCh drain on both return paths (M4)
plombardi89 May 12, 2026
07d489c
Targeted cleanups + review-doc update (M1, M7, docs)
plombardi89 May 12, 2026
f34d597
design/orca: sync docs to shipped reality
plombardi89 May 12, 2026
7826deb
design/orca/design.md: fix mermaid Diagram 4 parse errors
plombardi89 May 12, 2026
25d4235
design/orca: drop Internal interfaces section; renumber
plombardi89 May 12, 2026
2a11061
design/orca: drop Azure adapter section; renumber
plombardi89 May 12, 2026
af654e2
design/orca: rewrite design.md and brief.md in plain English
plombardi89 May 12, 2026
3239e4a
design/orca/design.md: drop s8.2-s8.4; rename s8 to Atomic commit
plombardi89 May 12, 2026
e38f7a5
design/orca/brief.md: compress for fast orientation
plombardi89 May 12, 2026
0d14465
design/orca/design.md: define LP and LE64 before chunk path formula
plombardi89 May 12, 2026
991b791
orca: tighten error handling across server, fetch, cachestore, cluster
plombardi89 May 13, 2026
1fc8e7a
orca: dynamic chunk size tier ladder and read-ahead in edge GET
plombardi89 May 13, 2026
ec7a482
orca/app: make Wait ctx-cancel deterministic when errCh is non-empty
plombardi89 May 13, 2026
9c2478f
design/orca: document chunk tier ladder and edge read-ahead
plombardi89 May 13, 2026
c10d4e7
move orca from "design/orca" to designs/orca to be aligned with main
plombardi89 May 14, 2026
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: 28 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,34 @@ jobs:
retention-days: 7
if-no-files-found: ignore

# ---------- Orca Integration Tests ----------
# Spins up LocalStack and Azurite via testcontainers-go and runs the
# orca in-process integration suite (internal/orca/inttest). Docker
# is preinstalled on GitHub-hosted Ubuntu runners; no extra services:
# block is required.
orca-inttest:
name: Orca Integration Tests
needs: [frontend]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Download frontend dist
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: frontend-dist
path: internal/net/html/dist

- name: Set up Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
cache-dependency-path: go.sum

- name: Run orca-inttest
run: make orca-inttest

# ---------- Build ----------
build:
name: Build
Expand Down
72 changes: 72 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ STAMP_LDFLAGS=-X github.com/Azure/unbounded/internal/version.Version=$(VERSION)

METALMAN_IMAGE=$(CONTAINER_REGISTRY)/metalman:$(VERSION)

# Orca configuration
ORCA_BIN=bin/orca
ORCA_CMD=./cmd/orca
ORCA_IMAGE ?= $(CONTAINER_REGISTRY)/orca:$(VERSION)
ORCA_NAMESPACE ?= unbounded-kube
ORCA_MANIFEST_TEMPLATES_DIR := deploy/orca
ORCA_MANIFEST_RENDERED_DIR := deploy/orca/rendered

# kubectl-unbounded also stamps the metalman image reference.
KUBECTL_UNBOUNDED_LDFLAGS=$(STAMP_LDFLAGS) -X github.com/Azure/unbounded/cmd/kubectl-unbounded/app.MetalmanImage=$(METALMAN_IMAGE)

Expand Down Expand Up @@ -112,6 +120,7 @@ REACT_DEV ?= false
.PHONY: all help fmt lint test build vulncheck check-deps kubectl-unbounded kubectl-unbounded-build install-tools install-protoc generate kubectl-unbounded forge unbounded-agent machina machina-build machina-oci machina-oci-push machina-manifests machine-ops-controller machine-ops-controller-build machine-ops-controller-oci machine-ops-controller-oci-push machine-ops-manifests metalman metalman-build metalman-oci metalman-oci-push gomod docs-serve unbounded-net-controller unbounded-net-node unbounded-net-routeplan-debug unping unroute notice notice-check
.PHONY: net-frontend net-frontend-clean net-build-ebpf net-manifests release-manifests
.PHONY: image-machina-local image-machine-ops-controller-local image-metalman-local image-net-controller-local image-net-node-local images-local
.PHONY: orca orca-build orca-manifests orca-oci orca-oci-push orca-up orca-down orca-reset orca-inttest image-orca-local

##@ General

Expand Down Expand Up @@ -176,6 +185,8 @@ help: ## Show this help
@echo " machina-oci-push Build machina image and push"
@echo " machine-ops-controller-oci-push Build machine-ops-controller image and push"
@echo " metalman-oci-push Build metalman image and push"
@echo " image-orca-local Build orca image"
@echo " orca-oci-push Build orca image and push"
@echo ""
@echo "Net Frontend:"
@echo " net-frontend Build frontend into \$$(NET_FRONTEND_DIST_DIR) (cached)"
Expand All @@ -188,10 +199,19 @@ help: ## Show this help
@echo " machina-manifests Render machina manifests into deploy/machina/rendered"
@echo " machine-ops-manifests Render machine-ops manifests into deploy/machine-ops/rendered"
@echo " net-manifests Render net manifests into \$$(NET_MANIFEST_RENDERED_DIR)"
@echo " orca-manifests Render orca manifests into deploy/orca/rendered"
@echo ""
@echo "Net Kubernetes (apply to current kubectl context):"
@echo " See \`make -C hack/net help\` for cluster deploy/undeploy targets."
@echo ""
@echo "Orca Dev Harness (Kind cluster):"
@echo " orca | orca-build Build orca binary (with/without lint/test)"
@echo " orca-up Bring up Orca dev harness in Kind"
@echo " orca-down Tear down Orca dev harness Kind cluster"
@echo " orca-reset Rebuild image and rollout-restart deployment"
@echo " orca-inttest Run orca integration tests (Docker required)"
@echo " See \`make -C hack/orca help\` for full list."
@echo ""
@echo "Documentation:"
@echo " docs-serve Start local Hugo dev server"
@echo ""
Expand Down Expand Up @@ -570,6 +590,58 @@ metalman-oci: image-metalman-local ## Alias for image-metalman-local
metalman-oci-push: metalman-oci ## Build and push the metalman container image
$(CONTAINER_ENGINE) push $(METALMAN_IMAGE)

##@ Orca

orca-build: ## Build the orca binary (no lint/test)
$(GOBUILD) -ldflags '$(STAMP_LDFLAGS)' -o $(ORCA_BIN) $(ORCA_CMD)/main.go

orca: test orca-build ## Build the orca binary (implies test)

orca-manifests: ## Render orca deployment manifests into deploy/orca/rendered
@mkdir -p $(ORCA_MANIFEST_RENDERED_DIR)
@find $(ORCA_MANIFEST_RENDERED_DIR) -mindepth 1 -not -name .gitignore -delete 2>/dev/null || true
$(GOCMD) run ./hack/cmd/render-manifests \
--templates-dir $(ORCA_MANIFEST_TEMPLATES_DIR) \
--output-dir $(ORCA_MANIFEST_RENDERED_DIR) \
--set Namespace=$(ORCA_NAMESPACE) \
--set Image=$(ORCA_IMAGE)
@echo "Rendered orca manifests into $(ORCA_MANIFEST_RENDERED_DIR) (image: $(ORCA_IMAGE))"

image-orca-local: ## Build the orca container image locally (single-arch)
$(CONTAINER_ENGINE) build \
--build-arg VERSION=$(VERSION) \
--build-arg GIT_COMMIT=$(GIT_COMMIT) \
--build-arg BUILD_TIME=$(BUILD_TIME) \
-t orca:$(VERSION) -t $(ORCA_IMAGE) \
-f ./images/orca/Containerfile .

orca-oci: image-orca-local ## Alias for image-orca-local

orca-oci-push: orca-oci ## Build and push the orca container image
$(CONTAINER_ENGINE) push $(ORCA_IMAGE)

# Dev-cluster proxy targets. The actual implementations live in
# hack/orca/Makefile (see AGENTS.md convention; mirrors hack/net/).
orca-up: ## Bring up the Orca dev harness in a Kind cluster
$(MAKE) -C hack/orca up

orca-down: ## Tear down the Orca dev harness Kind cluster
$(MAKE) -C hack/orca down

orca-reset: ## Rebuild orca image and rolling-restart the dev deployment
$(MAKE) -C hack/orca reset

# orca-inttest mirrors the test/test-race pattern: race detector in CI
# (ubuntu-latest has gcc), no -race locally so developers without a C
# toolchain can still run integration tests.
ifdef CI
orca-inttest: ## Run orca integration tests (LocalStack + Azurite via testcontainers; requires Docker)
$(GOTEST) -tags=integrationtest -race -timeout 15m ./internal/orca/inttest/...
else
orca-inttest: ## Run orca integration tests (LocalStack + Azurite via testcontainers; requires Docker)
$(GOTEST) -tags=integrationtest -timeout 15m ./internal/orca/inttest/...
endif

image-net-controller-local: net-frontend resources/cni-plugins-linux-$(HOST_GOARCH)-$(CNI_PLUGINS_VERSION).tgz ## Build the unbounded-net-controller image locally (single-arch)
$(CONTAINER_ENGINE) build \
--target controller \
Expand Down
10 changes: 10 additions & 0 deletions cmd/orca/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

package main

import "github.com/Azure/unbounded/cmd/orca/orca"

func main() {
orca.Run()
}
134 changes: 134 additions & 0 deletions cmd/orca/orca/orca.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// Package orca wires the Orca cache binary together. It is invoked by
// cmd/orca/main.go and is responsible for parsing flags, loading the
// YAML config, and delegating to internal/orca/app for actual runtime
// wiring.
package orca

import (
"context"
"fmt"
"log/slog"
"os"
"os/signal"
"strings"
"syscall"
"time"

"github.com/spf13/cobra"

"github.com/Azure/unbounded/internal/orca/app"
"github.com/Azure/unbounded/internal/orca/config"
)

// Run is the entrypoint invoked by cmd/orca/main.go.
func Run() {
root := &cobra.Command{
Use: "orca",
Short: "Orca origin cache - S3-compatible read-only cache fronting Azure / S3 origins",
}
root.AddCommand(newServeCmd())

if err := root.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}

func newServeCmd() *cobra.Command {
var configPath string

cmd := &cobra.Command{
Use: "serve",
Short: "Run the Orca cache server",
RunE: func(cmd *cobra.Command, _ []string) error {
return serve(cmd.Context(), configPath)
},
}
cmd.Flags().StringVarP(&configPath, "config", "c", "/etc/orca/config.yaml",
"path to YAML config file")

return cmd
}

func serve(parent context.Context, configPath string) error {
cfg, err := config.Load(configPath)
if err != nil {
return fmt.Errorf("load config: %w", err)
}

level, err := resolveLogLevel(cfg.Logging.Level)
if err != nil {
return err
}

levelVar := new(slog.LevelVar)
levelVar.Set(level)

log := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: levelVar,
AddSource: true,
}))
slog.SetDefault(log)

log.Info("orca starting",
"config_path", configPath,
"log_level", level.String(),
)

log.Info("config loaded",
"origin_id", cfg.Origin.ID,
"replicas_target", cfg.Cluster.TargetReplicas,
"target_global", cfg.Origin.TargetGlobal,
"internal_tls", cfg.Cluster.InternalTLS.Enabled,
"client_auth", cfg.Server.Auth.Enabled,
)

ctx, cancel := signal.NotifyContext(parent, os.Interrupt, syscall.SIGTERM)
defer cancel()

a, err := app.Start(ctx, cfg, app.WithLogger(log))
if err != nil {
return err
}

if waitErr := a.Wait(ctx); waitErr != nil {
log.Error("listener exited with error", "err", waitErr)
cancel()
} else {
log.Info("shutdown signal received")
}

shutdownCtx, shCancel := context.WithTimeout(context.Background(), 10*time.Second)
defer shCancel()

// Propagate Shutdown errors to the process exit code so that
// failed-shutdown signals (kubelet probes, init systems) match
// reality. App.Shutdown also logs each individual error
// internally, so this only governs the exit-code semantics.
shutdownErr := a.Shutdown(shutdownCtx)

log.Info("orca stopped")

return shutdownErr
}

// resolveLogLevel determines the effective slog.Level by consulting
// the ORCA_LOG_LEVEL environment variable first; if unset or empty,
// falls back to the YAML-configured value. An unrecognised value
// (from either source) returns a parse error so misconfiguration is
// surfaced at startup rather than silently degrading to info.
func resolveLogLevel(yamlLevel string) (slog.Level, error) {
if env := strings.TrimSpace(os.Getenv("ORCA_LOG_LEVEL")); env != "" {
level, err := config.ParseLogLevel(env)
if err != nil {
return 0, fmt.Errorf("ORCA_LOG_LEVEL: %w", err)
}

return level, nil
}

return config.ParseLogLevel(yamlLevel)
}
58 changes: 58 additions & 0 deletions cmd/orca/orca/orca_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

package orca

import (
"log/slog"
"testing"
)

// TestResolveLogLevel_PrecedenceAndDefault covers the resolution
// order documented on resolveLogLevel: ORCA_LOG_LEVEL wins when
// set and non-empty (after trim), otherwise the YAML-configured
// value is used, otherwise the empty string defaults through
// config.ParseLogLevel to info.
func TestResolveLogLevel_PrecedenceAndDefault(t *testing.T) {
tests := []struct {
name string
yamlLevel string
envLevel string // "" -> simulate unset via Setenv with ""
want slog.Level
wantErr bool
}{
{"empty yaml, no env -> info", "", "", slog.LevelInfo, false},
{"yaml info, no env", "info", "", slog.LevelInfo, false},
{"yaml debug, no env", "debug", "", slog.LevelDebug, false},
{"yaml info overridden by env debug", "info", "debug", slog.LevelDebug, false},
{"yaml debug overridden by env warn", "debug", "warn", slog.LevelWarn, false},
{"whitespace env falls back to yaml", "warn", " ", slog.LevelWarn, false},
{"invalid yaml fails", "trace", "", 0, true},
{"invalid env fails even when yaml valid", "info", "trace", 0, true},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Setenv("ORCA_LOG_LEVEL", tt.envLevel)

got, err := resolveLogLevel(tt.yamlLevel)
if tt.wantErr {
if err == nil {
t.Errorf("resolveLogLevel(%q) = %v, want error", tt.yamlLevel, got)
}

return
}

if err != nil {
t.Errorf("resolveLogLevel(%q) unexpected err: %v", tt.yamlLevel, err)
return
}

if got != tt.want {
t.Errorf("resolveLogLevel(yaml=%q, env=%q) = %v, want %v",
tt.yamlLevel, tt.envLevel, got, tt.want)
}
})
}
}
6 changes: 6 additions & 0 deletions deploy/orca/01-namespace.yaml.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: {{ default "unbounded-kube" .Namespace }}
labels:
app.kubernetes.io/name: orca
8 changes: 8 additions & 0 deletions deploy/orca/02-rbac.yaml.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: orca
namespace: {{ default "unbounded-kube" .Namespace }}
labels:
app.kubernetes.io/name: orca
Loading
Loading