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
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()
}
99 changes: 99 additions & 0 deletions cmd/orca/orca/orca.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// 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"
"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 {
log := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))
slog.SetDefault(log)

log.Info("orca starting", "config_path", configPath)

cfg, err := config.Load(configPath)
if err != nil {
return fmt.Errorf("load config: %w", err)
}

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()

_ = a.Shutdown(shutdownCtx) //nolint:errcheck // shutdown errors already logged inside App.Shutdown

log.Info("orca stopped")

return nil
}
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
71 changes: 71 additions & 0 deletions deploy/orca/03-config.yaml.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: orca-config
namespace: {{ default "unbounded-kube" .Namespace }}
labels:
app.kubernetes.io/name: orca
data:
config.yaml: |
# Orca origin cache configuration.
# Secret values (account keys, S3 access/secret) are sourced from
# environment variables ORCA_AZUREBLOB_ACCOUNT_KEY,
# ORCA_CACHESTORE_S3_ACCESS_KEY, ORCA_CACHESTORE_S3_SECRET_KEY,
# populated by the orca-credentials Secret via envFrom.

server:
listen: "0.0.0.0:8443"
auth:
# Dev: disabled. Production: enable bearer or mtls.
enabled: {{ default "false" .ServerAuthEnabled }}

origin:
id: {{ default "azureblob-default" .OriginID | quote }}
driver: {{ default "azureblob" .OriginDriver }}
target_global: {{ default "192" .TargetGlobal }}
queue_timeout: 5s
retry:
attempts: 3
backoff_initial: 100ms
backoff_max: 2s
max_total_duration: 5s
azureblob:
account: {{ default "" .AzureAccount | quote }}
container: {{ default "" .AzureContainer | quote }}
endpoint: {{ default "" .AzureEndpoint | quote }}
enforce_block_blob_only: true
awss3:
endpoint: {{ default "" .OriginAWSS3Endpoint | quote }}
region: {{ default "us-east-1" .OriginAWSS3Region | quote }}
bucket: {{ default "" .OriginAWSS3Bucket | quote }}
use_path_style: {{ default "false" .OriginAWSS3UsePathStyle }}

cachestore:
driver: s3
s3:
endpoint: {{ default "http://localstack.unbounded-kube.svc.cluster.local:4566" .CachestoreEndpoint | quote }}
bucket: {{ default "orca-cache" .CachestoreBucket | quote }}
region: {{ default "us-east-1" .CachestoreRegion | quote }}
use_path_style: true
require_unversioned_bucket: true

cluster:
service: {{ default "orca-peers.unbounded-kube.svc.cluster.local" .ClusterService | quote }}
membership_refresh: 5s
internal_listen: "0.0.0.0:8444"
target_replicas: {{ default "3" .TargetReplicas }}
internal_tls:
# Dev: disabled (plain HTTP/2 between peers). Production: true.
enabled: {{ default "false" .InternalTLSEnabled }}

chunk_catalog:
max_entries: 100000

metadata:
ttl: 5m
negative_ttl: 60s
max_entries: 10000

chunking:
size: 8388608
Loading
Loading