Skip to content
Open
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
70 changes: 70 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -669,3 +669,73 @@ ci: lint test-all test-acceptance docs-validate ## Run full CI pipeline (lint, t

pre-commit: fmt lint test ## Run pre-commit checks (format, lint, test)
$(call print_green,Pre-commit checks passed)

##@ Quick Development Iteration

quick-check: lint-parallel-critical build test-unit-fast ## Quick pre-commit check (critical lint + build + fast unit tests) ~30s
$(call print_green,Quick check complete - ready to commit!)

test-unit-fast: ## Run only unit tests (no acceptance, no testcontainers) ~10s
$(call print_blue,Running fast unit tests...)
@uv run tooling/parallel_test.py --categories unit,lint --log-dir $(TEST_LOGS_DIR)
$(call print_green,Fast unit tests complete)

quick-pattern: ## Quick test for current pattern directory (auto-detects pattern)
$(call print_blue,Quick pattern test...)
@if [ -f "go.mod" ]; then \
go test -v -short -timeout 30s ./...; \
elif [ -f "../../go.mod" ] && [ -d "cmd" ]; then \
cd cmd && go build && cd .. && go test -v -short -timeout 30s ./...; \
else \
echo "Not in a pattern directory"; \
exit 1; \
fi
$(call print_green,Pattern quick test complete)

quick-run-pattern: ## Quick build and run current pattern runner
$(call print_blue,Building and running pattern...)
@if [ -d "cmd" ]; then \
RUNNER_NAME=$$(basename $(CURDIR))-runner; \
echo "Building $$RUNNER_NAME..."; \
cd cmd/$$RUNNER_NAME && go build -o $(BINARIES_DIR)/$$RUNNER_NAME . && \
echo "✓ Built: $(BINARIES_DIR)/$$RUNNER_NAME" && \
echo "Run with: ./$(BINARIES_DIR)/$$RUNNER_NAME --help"; \
else \
echo "Not in a pattern directory with cmd/"; \
exit 1; \
fi

test-this: ## Test current directory (auto-detects Go, Rust, or Python)
$(call print_blue,Testing current directory...)
@if [ -f "Cargo.toml" ]; then \
cargo test; \
elif [ -f "go.mod" ]; then \
go test -v -timeout 30s ./...; \
elif [ -f "pyproject.toml" ]; then \
uv run pytest; \
else \
echo "No recognized test framework in current directory"; \
exit 1; \
fi

build-this: ## Build current directory (auto-detects Go, Rust)
$(call print_blue,Building current directory...)
@if [ -f "Cargo.toml" ]; then \
cargo build; \
elif [ -f "go.mod" ]; then \
go build ./...; \
elif [ -d "cmd" ] && [ -f "../go.mod" ]; then \
for d in cmd/*; do \
if [ -d "$$d" ]; then \
echo "Building $$d..."; \
cd $$d && go build && cd ../..; \
fi \
done; \
else \
echo "No recognized build system in current directory"; \
exit 1; \
fi

quick-verify-pattern: quick-pattern quick-run-pattern ## Verify pattern (test + build + show how to run)
$(call print_green,Pattern verified and ready!)

53 changes: 48 additions & 5 deletions cmd/pattern-launcher/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,30 @@ import (
)

var (
grpcPort = flag.Int("grpc-port", 8982, "gRPC server port")
metricsPort = flag.Int("metrics-port", 9092, "Metrics server port")
healthPort = flag.Int("health-port", 9093, "Health server port")
patternsDir = flag.String("patterns-dir", "./patterns", "Patterns directory")
isolationStr = flag.String("isolation", "namespace", "Default isolation level (none, namespace, session)")
grpcPort = flag.Int("grpc-port", 8982, "gRPC server port")
metricsPort = flag.Int("metrics-port", 9092, "Metrics server port")
healthPort = flag.Int("health-port", 9093, "Health server port")
patternsDir = flag.String("patterns-dir", "./patterns", "Patterns directory")
isolationStr = flag.String("isolation", "namespace", "Default isolation level (none, namespace, session)")
adminEndpoint = flag.String("admin-endpoint", "", "Admin control plane endpoint (e.g., localhost:8981)")
launcherID = flag.String("launcher-id", "launcher-01", "Unique launcher identifier")
region = flag.String("region", "local", "Deployment region")
maxProcs = flag.Int("max-processes", 20, "Maximum concurrent processes")
)

func main() {
flag.Parse()

log.Printf("Starting Pattern Launcher")
log.Printf(" Launcher ID: %s", *launcherID)
log.Printf(" Region: %s", *region)
log.Printf(" Admin endpoint: %s", *adminEndpoint)
log.Printf(" gRPC port: %d", *grpcPort)
log.Printf(" Metrics port: %d", *metricsPort)
log.Printf(" Health port: %d", *healthPort)
log.Printf(" Patterns directory: %s", *patternsDir)
log.Printf(" Default isolation: %s", *isolationStr)
log.Printf(" Max processes: %d", *maxProcs)

// Parse isolation level
isolationLevel := parseIsolationLevel(*isolationStr)
Expand All @@ -56,6 +64,41 @@ func main() {
log.Fatalf("Failed to create launcher service: %v", err)
}

// Connect to admin control plane if endpoint provided
var adminClient *launcher.AdminClient
if *adminEndpoint != "" {
adminCfg := &launcher.AdminClientConfig{
AdminEndpoint: *adminEndpoint,
LauncherID: *launcherID,
Address: fmt.Sprintf("localhost:%d", *grpcPort),
Region: *region,
MaxProcesses: int32(*maxProcs),
}

adminClient, err = launcher.NewAdminClient(adminCfg)
if err != nil {
log.Fatalf("Failed to create admin client: %v", err)
}
defer adminClient.Close()

// Register with admin
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
ack, err := adminClient.Register(ctx)
cancel()

if err != nil {
log.Printf("Warning: Failed to register with admin: %v", err)
log.Printf("Continuing without admin connectivity...")
} else {
log.Printf("Successfully registered with admin: %s", ack.Message)

// Start heartbeat loop
go adminClient.StartHeartbeatLoop(context.Background(), 30*time.Second)
}
} else {
log.Printf("No admin endpoint configured, running standalone")
}

// Create gRPC server
grpcServer := grpc.NewServer()
pb.RegisterPatternLauncherServer(grpcServer, service)
Expand Down
Loading
Loading