Functionality
Overview
The Porch API Server provides the Kubernetes API interface for Porch resources. It implements custom REST storage backends that delegate to the Engine, enforces validation and admission policies through strategies, and manages real-time watch streams for clients.
High-Level Architecture
┌─────────────────────────────────────────────────────────┐
│ API Server Functionality │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ REST Storage │ │ Strategies │ │
│ │ │ ───> │ │ │
│ │ • CRUD Ops │ │ • Validation │ │
│ │ • Watch Streams │ │ • Admission │ │
│ │ • Engine Deleg │ │ • Table Conv │ │
│ └──────────────────┘ └──────────────────┘ │
│ │ │ │
│ └────────┬────────────────┘ │
│ ↓ │
│ ┌──────────────────┐ │
│ │ Background │ │
│ │ Operations │ │
│ │ │ │
│ │ • Repo Sync │ │
│ │ • Cleanup │ │
│ └──────────────────┘ │
└─────────────────────────────────────────────────────────┘
REST Storage Implementation
See Design for the design rationale behind custom REST storage.
The API Server implements custom REST storage for each Porch resource type:
PackageRevision Storage
CRUD operations:
- Create: Validates spec, calls Engine to create package revision, returns created resource
- Get: Filters Engine package revision list by name, returns single resource
- List: Calls Engine to list package revisions with filters, returns resource list
- Update: Validates resource version, calls Engine to update package revision, returns updated resource
- Delete: Calls Engine to delete package revision, returns delete status
Watch support:
- Delegates to Engine cache for package revision watches
- Filters events based on watch criteria
- Delivers real-time change notifications
- Automatically cleans up on client disconnect
Storage characteristics:
- No etcd storage - delegates to Engine
- Engine manages package data in Git via Cache
- Supports all standard Kubernetes operations
- Implements storage.Interface fully
PackageRevisionResources Storage
Operations:
- Get: Retrieves package content via Engine
- List: Lists resources for package revisions
- Update: Updates package content via Engine
Content handling:
- Resources stored as map of filename to content
- Supports large package content (not limited by etcd)
- Updates trigger render pipeline execution
- Returns RenderStatus with function results
Storage characteristics:
- Read-only for most operations
- Update only allowed on Draft packages
- Content retrieved on-demand (not cached in API server)
- Delegates to Engine for all operations
Package Storage
Operations:
- Get: Filters Engine package list by name
- List: Calls Engine to list packages with filters
Package aggregation:
- Represents package across all revisions
- Tracks latest revision
- Provides package-level metadata
Validation Strategies
See Design for the design rationale behind validation strategies.
Strategies enforce validation rules before Engine operations:
Create Validation
Validation rules:
- Required fields present (package name, repository)
- Lifecycle constraints (cannot create Published/DeletionProposed)
- Task validation (maximum one task)
- Workspace name format
- Package path validity
Validation process:
- Strategy validation called before Engine operation
- Returns field errors for invalid specifications
- Prevents invalid resources from reaching Engine
- Provides clear error messages to clients
Update Validation
Validation rules:
- Resource version required (optimistic locking)
- Lifecycle transition validity
- Immutability constraints (Published packages)
- Task append-only (cannot remove tasks)
- Metadata update restrictions
Validation process:
- Strategy validation called before Engine update operation
- Compares old and new objects
- Validates changes are allowed
- Returns field errors for invalid updates
Status Validation
Validation rules:
- Status subresource updates validated separately
- Conditions format validation
- RenderStatus structure validation
- DownstreamTargets validation
Validation process:
- Strategy.ValidateStatusUpdate called for status updates
- Ensures status updates don’t modify spec
- Validates status structure
- Returns field errors for invalid status
Admission Control
See Design for the design rationale behind admission control.
Strategies apply admission policies and defaults:
PrepareForCreate
Operations:
- Generate name if not provided
- Set default lifecycle (Draft)
- Initialize status conditions
- Set creation timestamp
- Add default labels/annotations
Admission process:
- Called before validation
- Modifies resource in-place
- Ensures consistent initial state
- Prepares resource for Engine
PrepareForUpdate
Operations:
- Validate resource version
- Enforce immutability rules
- Preserve status on spec updates
- Update modification timestamp
- Merge labels/annotations
Admission process:
- Called before validation
- Checks current vs desired state
- Enforces business rules
- Prepares resource for Engine
Canonicalization
Operations:
- Normalize resource representation
- Remove redundant fields
- Apply default values
- Ensure consistent format
Canonicalization process:
- Called after validation
- Ensures consistent storage format
- Simplifies comparison operations
- Improves cache efficiency
Table Conversion
See Design for the design rationale behind table conversion.
Strategies convert resources to table format for kubectl:
Column Definitions
PackageRevision columns:
- Name: Resource name
- Package: Package name
- WorkspaceName: Workspace identifier
- Revision: Revision number
- Lifecycle: Current lifecycle state
- Repository: Source repository
Table format:
- Follows Kubernetes table conventions
- Supports sorting and filtering
- Provides human-readable output
- Consistent with kubectl expectations
Conversion Process
Conversion flow:
- kubectl requests table format
- REST storage calls Strategy.ConvertToTable
- Strategy extracts column values
- Returns metav1.Table with rows
- kubectl formats for display
Conversion characteristics:
- Supports both list and individual resources
- Handles missing fields gracefully
- Provides consistent formatting
- Enables kubectl get commands
Watch Stream Management
See Interactions for how watch streams integrate with other components.
The API Server provides real-time watch streams:
Watch Registration
Registration process:
- Client sends watch request with filters
- REST storage calls Engine cache to watch package revisions
- WatcherManager registers watcher with filters
- Returns watch interface to client
- Client receives events as they occur
Filter support:
- Namespace filtering
- Label selectors
- Field selectors
- Resource version (resume from point)
Event Delivery
Event types:
- Added: New package revision created
- Modified: Package revision updated
- Deleted: Package revision removed
Delivery characteristics:
- Events delivered in real-time
- Filtered based on watch criteria
- Ordered per resource
- Best-effort delivery (network failures may drop events)
Watch Lifecycle
Lifecycle stages:
- Registration: Client subscribes with filters
- Active: Events delivered as changes occur
- Disconnection: Client closes connection or timeout
- Cleanup: WatcherManager removes watcher automatically
Cleanup triggers:
- Client disconnects
- Context cancellation
- Watch timeout
- Error during event delivery
Background Operations
The API Server runs background tasks for maintenance:
Repository Synchronization
Sync process:
- Background goroutine discovers repositories
- Creates SyncManager for each repository
- SyncManager triggers periodic sync
- Cache updates from Git repository
- Watch notifications sent to clients
Sync configuration:
- Default frequency set at server startup
- Per-repository frequency via Repository CR
- Manual sync via Repository CR update
- Cron schedule support
Sync coordination:
- One SyncManager per repository
- Syncs run independently
- Errors logged and reported in Repository status
- Automatic retry on next cycle
Resource Cleanup
Cleanup operations:
- Remove PackageRev CRs for deleted repositories
- Clean up orphaned cache entries
- Remove stale watch registrations
- Garbage collect expired resources
Cleanup triggers:
- Repository deletion
- Cache eviction
- Watch disconnection
- Periodic maintenance
Cleanup coordination:
- Triggered by lifecycle events
- Runs asynchronously
- Ensures consistency
- Prevents resource leaks
Performance Optimization
The API Server employs several optimization strategies:
List Operation Optimization
Optimization techniques:
- Concurrent repository listing (configurable max concurrency)
- Per-repository timeout (prevents slow repos from blocking)
- Early termination on context cancellation
- Efficient filtering at Engine level
Configuration:
- MaxConcurrentLists: Maximum concurrent repository operations
- ListTimeoutPerRepository: Timeout per repository
- Prevents slow repositories from impacting overall performance
Watch Stream Efficiency
Efficiency mechanisms:
- WatcherManager provides efficient fan-out
- Events filtered at source (not delivered then filtered)
- Automatic cleanup of inactive watchers
- No polling overhead
Scalability:
- Supports many concurrent watchers
- Minimal overhead per watcher
- Efficient event delivery
- Scales with number of clients
Cache Integration
Cache benefits:
- Engine caches repository data
- Reduces Git operations
- Faster list operations
- Consistent performance
Cache coordination:
- Background sync keeps cache fresh
- Watch notifications on cache updates
- Automatic cache invalidation
- Transparent to API clients
Error Handling
See Interactions for error handling across component boundaries.
The API Server handles errors at multiple levels:
Validation Errors
Error handling:
- Strategy validation returns field errors
- Converted to 400 Bad Request
- Includes field path and error message
- Client receives detailed error information
Error examples:
- “spec.lifecycle: cannot create Published package”
- “spec.tasks: maximum one task allowed”
- “metadata.name: invalid format”
Engine Errors
Error handling:
- Engine returns typed errors
- REST storage translates to Kubernetes status
- Appropriate HTTP status code
- Error details in status message
Error types:
- NotFound → 404 Not Found
- Conflict → 409 Conflict
- Validation → 400 Bad Request
- Internal → 500 Internal Server Error
Watch Errors
Error handling:
- Registration errors returned immediately
- Delivery errors close watch stream
- Client receives error event
- Automatic cleanup on error
Error recovery:
- Client can re-establish watch
- Resume from last resource version
- No data loss on transient errors
- Graceful degradation
Concurrency Control
See Interactions for concurrency patterns across component interactions.
The API Server handles concurrent operations:
Request Concurrency
Concurrency characteristics:
- Multiple clients can make requests concurrently
- Each request processed independently
- Engine provides concurrency control
- Optimistic locking prevents conflicts
Concurrency patterns:
- Read operations fully concurrent
- Write operations serialized per package (Engine mutex)
- Watch streams independent
- Background jobs concurrent
Optimistic Locking
Locking mechanism:
- Clients provide resource version on updates
- API Server validates version before Engine call
- Engine compares with current version
- Conflict returned if mismatch
- Client must re-read and retry
Locking benefits:
- Prevents lost updates
- No distributed locks needed
- Scales well
- Standard Kubernetes pattern
Watch Stream Safety
Safety guarantees:
- Each watch stream independent
- No shared state between watchers
- Thread-safe event delivery
- Automatic cleanup prevents leaks