Task Coordination
Overview
The Engine coordinates task execution by delegating to the Task Handler. Tasks represent operations that transform package content (init, clone, edit, upgrade, render). The Engine orchestrates when and how tasks are executed, while the Task Handler implements the actual transformations.
High-Level Architecture
┌─────────────────────────────────────────────────────────┐
│ Task Coordination System │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Engine │ │ Task Handler │ │
│ │ Orchestration │ ───> │ Execution │ │
│ │ │ │ │ │
│ │ • When to Run │ │ • Init │ │
│ │ • Draft Mgmt │ │ • Clone │ │
│ │ • Error Handle │ │ • Edit │ │
│ └──────────────────┘ │ • Upgrade │ │
│ │ │ • Render │ │
│ │ └──────────────────┘ │
│ ↓ │ │
│ ┌──────────────────┐ ↓ │
│ │ Function │ ┌──────────────────┐ │
│ │ Runtime │ <─── │ Builtin Funcs │ │
│ │ │ │ │ │
│ │ • gRPC │ │ • set-namespace │ │
│ │ • Builtin │ │ • ensure-context│ │
│ └──────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────┘
Task Handler Integration
The Engine integrates with the Task Handler through three main operations:
Integration Points
Engine Task Handler
↓ ↓
CreatePackageRevision ──────> ApplyTask
↓ ↓
UpdatePackageRevision ──────> DoPRMutations
↓ ↓
UpdatePackageResources ─────> DoPRResourceMutations
Three integration points:
- ApplyTask: Execute task during package revision creation
- DoPRMutations: Apply mutations during package revision update
- DoPRResourceMutations: Apply resource mutations during resource update
Engine responsibilities:
- Determine when to invoke task handler
- Provide draft workspace for modifications
- Handle errors and rollback
- Manage lifecycle transitions
Task Handler responsibilities:
- Execute task transformations
- Modify draft resources
- Apply builtin functions
- Return results or errors
ApplyTask - Creation Task Execution
The Engine invokes ApplyTask during package revision creation:
ApplyTask Flow
CreatePackageRevision
↓
Create Draft
↓
ApplyTask(draft, repo, pr, config)
↓
Task Handler
↓
┌────┴────┬────────┬─────────┐
↓ ↓ ↓ ↓
Init Clone Edit Upgrade
↓ ↓ ↓ ↓
└────┬────┴────────┴─────────┘
↓
Apply Builtin Functions
↓
Return Modified Draft
↓
Engine
↓
Update Lifecycle
↓
Close Draft
Process:
- Engine creates draft (mutable workspace)
- Engine invokes ApplyTask with:
- Draft to modify
- Repository object for context
- PackageRevision spec with task definition
- Package configuration (path, name, etc.)
- Task Handler executes task based on type
- Task Handler applies builtin functions (package context generation)
- Task Handler returns modified draft or error
- Engine updates lifecycle if successful
- Engine closes draft to create package revision
ApplyTask Parameters
Draft:
- Mutable workspace for modifications
- Provides UpdateResources, GetResources methods
- Isolated from other revisions
Repository Object:
- Repository CR specification
- Used for context (repository name, namespace)
- Passed to task implementations
PackageRevision:
- Contains task specification in Spec.Tasks
- First task in list executed
- Task type determines which implementation runs
Package Config:
- Package path, name, workspace
- Upstream reference (for clone/upgrade)
- Additional metadata
Task Execution
Task Handler executes the task:
- Task type determines which implementation runs (init, clone, edit, upgrade)
- Task handler modifies draft resources based on task logic
- Builtin functions applied after task execution
- Modified draft returned to Engine
Task types:
- init: Create new package from scratch
- clone: Copy package from upstream
- edit: Create new revision from existing package
- upgrade: Merge changes from new upstream version
Handback to Engine:
- Success: Returns modified draft
- Error: Returns error, triggers rollback
DoPRMutations - Update Mutations
The Engine invokes DoPRMutations during package revision updates:
DoPRMutations Flow
UpdatePackageRevision
↓
Open Draft from Existing
↓
DoPRMutations(repoPR, oldObj, newObj, draft)
↓
Task Handler
↓
Compare Task Lists
↓
New Tasks? ──No──> Return Draft
│
Yes
↓
Apply New Tasks
↓
Return Modified Draft
↓
Engine
↓
Update Lifecycle
↓
Close Draft
Process:
- Engine opens draft from existing package revision
- Engine invokes DoPRMutations with:
- Repository package revision (for context)
- Old PackageRevision spec
- New PackageRevision spec
- Draft to modify
- Task Handler compares old and new task lists
- Task Handler applies any new tasks
- Task Handler returns modified draft or error
- Engine updates lifecycle if changed
- Engine closes draft to persist changes
DoPRMutations Parameters
Repository PackageRevision:
- Current package revision from repository
- Provides context for mutations
- Not directly modified (draft is modified)
Old PackageRevision:
- Previous PackageRevision spec
- Used for comparison
- Identifies what changed
New PackageRevision:
- Desired PackageRevision spec
- Contains new tasks to apply
- Target state
Draft:
- Mutable workspace for modifications
- Already contains current package content
- Modified by new tasks
Task Comparison
Compare Task Lists
↓
Old Tasks: [init, clone]
New Tasks: [init, clone, render]
↓
Identify New Tasks: [render]
↓
Apply New Tasks
Comparison logic:
- Tasks are append-only (never removed)
- Compare task list lengths
- New tasks are those beyond old list length
- Apply new tasks in order
Task application:
- Each new task executed sequentially
- Draft modified by each task
- Errors stop processing and return
DoPRResourceMutations - Resource Updates
The Engine invokes DoPRResourceMutations during resource updates:
DoPRResourceMutations Flow
UpdatePackageResources
↓
Open Draft from Existing
↓
DoPRResourceMutations(pr, draft, oldRes, newRes)
↓
Task Handler
↓
Update Package Resources
↓
Execute Render Task
↓
Run Function Pipeline
↓
Return RenderStatus
↓
Engine
↓
Close Draft (no lifecycle change)
Process:
- Engine opens draft from existing package revision
- Engine invokes DoPRResourceMutations with:
- Package revision (for context)
- Draft to modify
- Old PackageRevisionResources
- New PackageRevisionResources
- Task Handler updates package resources in draft
- Task Handler executes render (runs function pipeline)
- Task Handler returns RenderStatus with function results
- Engine closes draft without lifecycle change
DoPRResourceMutations Parameters
PackageRevision:
- Current package revision from repository
- Provides context for mutations
- Contains function pipeline configuration
Draft:
- Mutable workspace for modifications
- Resources updated directly
- Modified by render task
Old PackageRevisionResources:
- Previous resource content
- Used for comparison (not currently used)
- Audit trail
New PackageRevisionResources:
- Desired resource content
- Applied to draft
- Target state
Render Execution
Task Handler executes render:
- Updates package resources in draft
- Executes render task (runs function pipeline)
- Returns RenderStatus with function results
Handback to Engine:
- RenderStatus: Contains function execution results
- Result: Overall success/failure
- Error: Error message if failed
- Exit code: Function exit codes
- Function details: Per-function results
- Modified draft: Resources updated by render task
Task Handler Configuration
The Engine configures the Task Handler during initialization:
Task Handler Setup
NewCaDEngine(opts...)
↓
Create cadEngine
↓
taskHandler = GetDefaultTaskHandler()
↓
Apply Options:
↓
• Function Runtimes
• Credential Resolvers
• Reference Resolvers
↓
Return Configured Engine
Configuration options:
- Function runtimes: Builtin, gRPC, or multi-runtime
- Credential resolver: For accessing upstream packages
- Reference resolver: For resolving package references
- User info provider: For audit trails
Function Runtime Configuration
Runtime types:
- Builtin Runtime: For built-in functions (set-namespace, etc.)
- gRPC Runtime: For external function runner service
- Multi-Runtime: Chains multiple runtimes together
Runtime selection:
- Configured at Porch server startup
- Passed to task handler during engine initialization
- Task handler uses runtime for function execution
For details on function runtime implementations, see Function Runner.
Error Handling
The Engine handles task execution errors:
Task Error Flow
ApplyTask/DoPRMutations/DoPRResourceMutations
↓
Task Handler Executes
↓
Error? ──No──> Return Success
│
Yes
↓
Return Error
↓
Engine
↓
Rollback (if creation)
↓
Return Error to Client
Error handling:
- Task errors returned to Engine
- Engine triggers rollback (for creation)
- Error propagated to client
- No partial package revisions created
Error Types
Task execution errors:
- Invalid task configuration
- Upstream package not found
- Merge conflicts (upgrade)
- Function execution failures
Function errors:
- Function image not found
- Function execution timeout
- Function validation failures
- Function runtime errors
Resource errors:
- Invalid YAML syntax
- Missing required fields
- Schema validation failures
Error Recovery
Client retry:
- Fix task configuration
- Resolve upstream references
- Fix resource syntax
- Retry operation
Automatic recovery:
- Rollback on creation errors
- Draft cleanup
- No manual intervention needed
Task Coordination Patterns
The Engine uses specific patterns for task coordination:
Task Execution
Task List: [init/clone/edit/upgrade]
↓
Execute [init/clone/edit/upgrade]
↓
Success? ──No──> Return Error
│
Yes
↓
Return Success
Sequential execution:
- Typically one task (init, clone, edit, or upgrade)
- Each task must succeed before next
- First error stops execution
- No parallel task execution
Rationale:
- Tasks may depend on previous tasks
- Simplifies error handling
- Maintains consistent state
Task List Pattern
Create: [init]
↓
Update: [clone/edit/upgrade]
↓
Update: [render]
Task list pattern:
- Single persistent task indicating [init/clone/edit/upgrade] method
- Task history shows package origin
Draft Isolation
Package Revision A Package Revision B
↓ ↓
Draft A Draft B
↓ ↓
Task Execution Task Execution
↓ ↓
Independent Independent
Isolation guarantees:
- Each draft has a separate context
- Task execution doesn’t affect other drafts
- Concurrent task execution possible (different packages)
- No shared state between tasks
Task Handler Interface
The Engine interacts with Task Handler through a defined interface:
Interface Methods
ApplyTask:
- Accepts: context, draft workspace, repository object, package revision spec, package configuration
- Returns: error on failure
- Purpose: Execute task during package revision creation
DoPRMutations:
- Accepts: context, repository package revision, old spec, new spec, draft workspace
- Returns: error on failure
- Purpose: Apply mutations during package revision update
DoPRResourceMutations:
- Accepts: context, package revision, draft workspace, old resources, new resources
- Returns: render status and error
- Purpose: Apply resource mutations and execute render task
Interface Characteristics
Context-aware:
- All methods accept context for cancellation
- Timeout and deadline support
- Tracing and logging context
Draft-based:
- All methods work with draft workspaces
- No direct repository modification
- Isolation and atomicity
Error-returning:
- Errors indicate task failure
- Engine handles rollback
- Clear error messages for debugging
Task Coordination Benefits
The task coordination pattern provides several benefits:
Separation of Concerns
Engine:
- Orchestrates workflow
- Manages drafts and lifecycle
- Handles errors and rollback
- Enforces business rules
Task Handler:
- Implements task logic
- Transforms package content
- Executes functions
- Returns results
Benefits:
- Clear responsibilities
- Easier testing and maintenance
- Pluggable task implementations
- Independent evolution
Extensibility
New task types:
- Implement in task handler
- Engine unchanged
- Register with task handler
- Available for use
New function runtimes:
- Implement runtime interface
- Configure at startup
- Task handler uses new runtime
- No CaDEngine changes
Testability
Engine testing:
- Mock task handler
- Test orchestration logic
- Test error handling
- Test rollback mechanism
Task Handler testing:
- Mock draft interface
- Test task implementations
- Test function execution
- Independent of CaDEngine