Documentation
¶
Overview ¶
Package promotion provides the controller for automatic PR promotion across environments. The controller orchestrates the complete promotion workflow by watching PullRequestTracker CRDs, loading promotion policies, triggering tests, and promoting on success.
Package promotion provides the controller and domain logic for automatic PR promotion across environments. It orchestrates the complete promotion workflow: discover synced PRs → load policies → trigger tests → monitor results → promote on success.
The promotion controller operates on PullRequestTracker CRDs that have reached the "synced" state (successfully deployed in an environment). It loads the appropriate PromotionPolicy based on the current environment, triggers external tests as defined in the policy, monitors their completion, and promotes to the next environment if all required tests pass.
Architecture follows hexagonal principles:
- Controller orchestrates workflow and manages CRD lifecycle
- Domain types are defined here (no SDK leakage)
- External dependencies injected via interfaces (TestRunner, ArgoCDClient, GitHubClient)
- Policy evaluation is deterministic and stateless
- Test execution is idempotent and restartable
Index ¶
- Variables
- func BuildPromotionRecord(result PromotionResult, sourceEnv string, testRuns []v1alpha1.TestRun) v1alpha1.PromotionRecord
- func ConvertToV1Alpha1TestRuns(executions []TestExecution, results map[string]*external.TestStatus) []v1alpha1.TestRun
- func ValidatePolicy(policy *v1alpha1.PromotionPolicy) error
- type ArgoCDClient
- type EventRecorder
- type GitHubClient
- type Logger
- type PolicyCache
- type PromotionExecutor
- type PromotionResult
- type Reconciler
- type TestAggregateResult
- type TestExecution
- type TestMonitor
- type TestRunner
- type TriggerManager
Constants ¶
This section is empty.
Variables ¶
var ErrPolicyNotFound = fmt.Errorf("promotion policy not found")
ErrPolicyNotFound is returned when no promotion policy matches the query.
Functions ¶
func BuildPromotionRecord ¶
func BuildPromotionRecord( result PromotionResult, sourceEnv string, testRuns []v1alpha1.TestRun, ) v1alpha1.PromotionRecord
BuildPromotionRecord creates a PromotionRecord from a promotion result for CRD status. This provides the audit trail of promotions in the PullRequestTracker status.
Parameters:
- result: PromotionResult from Execute()
- sourceEnv: Source environment (from policy)
- testRuns: Test runs that gated the promotion
Returns v1alpha1.PromotionRecord for CRD status.
func ConvertToV1Alpha1TestRuns ¶
func ConvertToV1Alpha1TestRuns( executions []TestExecution, results map[string]*external.TestStatus, ) []v1alpha1.TestRun
ConvertToV1Alpha1TestRuns converts TestExecution records to v1alpha1.TestRun format for storing in CRD status. This bridges the domain layer with the API layer.
Parameters:
- executions: Domain test execution records
- results: Current test status results (optional, may be nil)
Returns v1alpha1.TestRun slice suitable for CRD status.
func ValidatePolicy ¶
func ValidatePolicy(policy *v1alpha1.PromotionPolicy) error
ValidatePolicy checks if a promotion policy has valid configuration. This should be called before using the policy to trigger tests or promotion.
Validation checks:
- Source and target environments are non-empty and different
- External tests have unique names
- External tests have valid provider and job names
- Timeout is positive
Parameters:
- policy: Policy to validate
Returns error describing the validation failure, or nil if valid.
Types ¶
type ArgoCDClient ¶
type ArgoCDClient interface {
// GetApplicationStatus retrieves deployment status from ArgoCD.
GetApplicationStatus(ctx context.Context, cluster, application string) (*deployment.DeploymentStatus, error)
// UpdateApplication updates an ArgoCD application configuration.
// Used to promote a PR by updating the target environment.
UpdateApplication(ctx context.Context, cluster, application, targetEnv string) error
}
ArgoCDClient defines the interface for ArgoCD operations. Abstracts ArgoCD API calls to allow testing with mocks.
type EventRecorder ¶
type EventRecorder interface {
// Event records an event for an object.
Event(object interface{}, eventType, reason, message string)
}
EventRecorder defines the interface for recording Kubernetes events. Used for audit trail of promotions.
type GitHubClient ¶
type GitHubClient interface {
// AddLabel adds a label to a PR.
AddLabel(ctx context.Context, owner, repo string, number int, label string) error
// RemoveLabel removes a label from a PR.
RemoveLabel(ctx context.Context, owner, repo string, number int, label string) error
}
GitHubClient defines the interface for GitHub operations. Abstracts GitHub API calls to allow testing with mocks.
type Logger ¶
type Logger interface {
Info(msg string, keysAndValues ...interface{})
Error(err error, msg string, keysAndValues ...interface{})
V(level int) Logger
}
Logger defines the logging interface used by the trigger manager.
type PolicyCache ¶
type PolicyCache struct {
// contains filtered or unexported fields
}
PolicyCache provides thread-safe caching of PromotionPolicy CRDs. Reduces Kubernetes API calls by maintaining an in-memory cache that can be refreshed periodically or on-demand. Policies are indexed by source environment for efficient lookup during promotion reconciliation.
func NewPolicyCache ¶
func NewPolicyCache(client client.Client) *PolicyCache
NewPolicyCache creates a new policy cache with the given Kubernetes client. The cache starts empty and must be populated via Refresh() before use.
Parameters:
- client: Kubernetes client for fetching PromotionPolicy CRDs
Returns an initialized PolicyCache.
func (*PolicyCache) GetPolicy ¶
func (c *PolicyCache) GetPolicy(ctx context.Context, sourceEnv string) (*v1alpha1.PromotionPolicy, error)
GetPolicy retrieves a promotion policy for the given source environment. If the policy is not in cache, it queries Kubernetes to find it. Returns ErrPolicyNotFound if no matching policy exists.
Parameters:
- ctx: Context for cancellation and timeout
- sourceEnv: Source environment to find policy for (e.g., "dev", "staging")
Returns:
- Policy matching the source environment
- ErrPolicyNotFound if no policy matches
- Wrapped error for Kubernetes API failures
func (*PolicyCache) Refresh ¶
func (c *PolicyCache) Refresh(ctx context.Context) error
Refresh reloads all PromotionPolicy CRDs from Kubernetes into the cache. This is an expensive operation and should be called judiciously (e.g., on cache misses or periodically in the background).
Parameters:
- ctx: Context for cancellation and timeout
Returns error if Kubernetes API call fails.
type PromotionExecutor ¶
type PromotionExecutor struct {
// contains filtered or unexported fields
}
PromotionExecutor handles the execution of promotion operations. It coordinates with ArgoCD to update applications and GitHub to update labels, providing a complete audit trail of the promotion.
func NewPromotionExecutor ¶
func NewPromotionExecutor( argoCDClient ArgoCDClient, githubClient GitHubClient, eventRecorder EventRecorder, logger Logger, ) *PromotionExecutor
NewPromotionExecutor creates a new promotion executor with the given dependencies.
Parameters:
- argoCDClient: ArgoCD client for updating applications
- githubClient: GitHub client for updating PR labels
- eventRecorder: Kubernetes event recorder for audit trail
- logger: Logger for diagnostic output
Returns an initialized PromotionExecutor.
func (*PromotionExecutor) Execute ¶
func (e *PromotionExecutor) Execute( ctx context.Context, prt *v1alpha1.PullRequestTracker, policy *v1alpha1.PromotionPolicy, testResults TestAggregateResult, ) (PromotionResult, error)
Execute performs the promotion by updating ArgoCD application and GitHub labels. This is the final step in the promotion workflow after all tests have passed.
Steps:
- Update ArgoCD application to target environment
- Add promotion label to GitHub PR
- Record promotion in CRD status
- Emit Kubernetes event for audit
Parameters:
- ctx: Context for cancellation and timeout
- prt: PullRequestTracker CRD to promote
- policy: PromotionPolicy that governed this promotion
- testResults: Final test results for audit trail
Returns:
- PromotionResult with success/failure details
- Error if promotion failed
type PromotionResult ¶
type PromotionResult struct {
// Promoted indicates if the promotion was successfully executed.
Promoted bool
// TargetEnv is the environment the PR was promoted to.
TargetEnv string
// TestResults contains the final test outcomes that gated promotion.
TestResults TestAggregateResult
// Error contains any error encountered during promotion execution.
// Nil if Promoted is true.
Error error
// PromotedAt is the timestamp when promotion completed.
PromotedAt time.Time
// PromotedBy indicates who/what triggered the promotion.
PromotedBy string
// PolicyName is the name of the PromotionPolicy that governed this promotion.
PolicyName string
}
PromotionResult represents the outcome of a promotion attempt. Returned by PromotionExecutor.Execute() to indicate success or failure.
type Reconciler ¶
type Reconciler struct {
client.Client
Scheme *runtime.Scheme
TestRunner TestRunner
ArgoCDClient ArgoCDClient
GitHubClient GitHubClient
EventRecorder EventRecorder
PolicyCache *PolicyCache
Logger Logger
}
Reconciler reconciles PullRequestTracker resources for promotion. It implements the complete promotion workflow: discover synced PRs → load policies → trigger tests → monitor results → promote on success.
func NewReconciler ¶
func NewReconciler( client client.Client, scheme *runtime.Scheme, testRunner TestRunner, argoCDClient ArgoCDClient, githubClient GitHubClient, eventRecorder EventRecorder, logger Logger, ) *Reconciler
NewReconciler creates a new promotion reconciler with the given dependencies.
Parameters:
- client: Kubernetes client for CRD operations
- scheme: Runtime scheme for type registration
- testRunner: TestRunner for triggering and monitoring external tests
- argoCDClient: ArgoCD client for application updates
- githubClient: GitHub client for PR label management
- eventRecorder: Event recorder for Kubernetes audit trail
- logger: Logger for diagnostic output
Returns a configured Reconciler ready for controller-runtime.
func (*Reconciler) Reconcile ¶
Reconcile implements the main reconciliation logic for promotion. It is called by controller-runtime whenever a PullRequestTracker is created, updated, or periodically re-queued.
Reconciliation workflow:
- Fetch PullRequestTracker CRD
- Check if PR is ready for promotion (synced state)
- Load promotion policy for current environment
- If tests not triggered: trigger external tests
- If tests triggered: monitor test status
- If all tests passed: execute promotion
- Update CRD status and conditions
- Requeue based on current state
Parameters:
- ctx: Context for cancellation and timeout
- req: Reconcile request with namespaced name
Returns:
- Result: Controls requeue behavior (interval or no requeue)
- Error: Causes exponential backoff retry if non-nil
func (*Reconciler) SetupWithManager ¶
func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error
SetupWithManager sets up the controller with the controller manager. Configures watches and reconciliation options.
Parameters:
- mgr: Controller manager to register with
Returns error if setup fails.
type TestAggregateResult ¶
type TestAggregateResult struct {
// AllPassed indicates all required tests passed successfully.
// If true, promotion can proceed (if auto-promote is enabled).
AllPassed bool
// AnyFailed indicates at least one required test failed.
// If true, promotion is blocked until tests pass.
AnyFailed bool
// AnyRunning indicates at least one test is still executing.
// If true, controller should continue polling.
AnyRunning bool
// AnyTimeout indicates at least one test exceeded its timeout.
// Timeouts are treated as failures for required tests.
AnyTimeout bool
// Results maps test name to its current status.
// Includes all tests (required and optional).
Results map[string]*external.TestStatus
// FailureReasons contains human-readable explanations for failures.
// Empty if AllPassed is true.
FailureReasons []string
// OptionalTestsFailed indicates non-required tests failed.
// Logged for visibility but doesn't block promotion.
OptionalTestsFailed bool
}
TestAggregateResult summarizes the outcome of all external tests for a promotion. Used to determine if promotion should proceed (all required tests passed).
type TestExecution ¶
type TestExecution struct {
// Name is the test name from the PromotionPolicy spec.
Name string
// Provider identifies the CI system (jenkins, github-actions, gitlab).
Provider external.Provider
// RunID is the unique identifier in the CI system for status polling.
RunID string
// StartedAt is when the test was triggered by the controller.
StartedAt time.Time
// Required indicates if this test must pass for promotion to proceed.
// If false, test failure is logged but doesn't block promotion.
Required bool
// JobName is the CI job/workflow that was triggered (for reference).
JobName string
// URL is the web link to view the test run in the CI system.
URL string
}
TestExecution represents a triggered external test run with tracking metadata. Stored in CRD status to maintain state across reconciliation loops.
type TestMonitor ¶
type TestMonitor struct {
// contains filtered or unexported fields
}
TestMonitor handles monitoring of external test execution status. It polls test runners for status updates, handles timeouts, and aggregates results to determine if promotion should proceed.
func NewTestMonitor ¶
func NewTestMonitor(runner TestRunner, logger Logger) *TestMonitor
NewTestMonitor creates a new test monitor with the given test runner.
Parameters:
- runner: TestRunner implementation for polling test status
- logger: Logger for diagnostic output
Returns an initialized TestMonitor.
func (*TestMonitor) MonitorTests ¶
func (m *TestMonitor) MonitorTests( ctx context.Context, executions []TestExecution, timeout time.Duration, ) (TestAggregateResult, error)
MonitorTests checks the status of all test executions and returns aggregated results. It queries the TestRunner for each test's current status, checks for timeouts, and determines if all required tests have passed.
Parameters:
- ctx: Context for cancellation and timeout
- executions: Test executions to monitor (from TriggerTests)
- timeout: Maximum time to wait for tests to complete
Returns:
- Aggregated test results indicating overall status
- Error if status polling fails
type TestRunner ¶
type TestRunner = external.TestRunner
TestRunner defines the interface for triggering and monitoring external tests. Abstracts the external test system (Jenkins, GitHub Actions, GitLab CI). This is an alias for pkg/external.TestRunner to avoid SDK leakage while providing a clean domain boundary.
type TriggerManager ¶
type TriggerManager struct {
// contains filtered or unexported fields
}
TriggerManager handles triggering of external tests for promotion gates. It builds test configurations from policy specs, performs variable substitution, and triggers tests in parallel across multiple CI systems.
func NewTriggerManager ¶
func NewTriggerManager(runner TestRunner, logger Logger) *TriggerManager
NewTriggerManager creates a new trigger manager with the given test runner.
Parameters:
- runner: TestRunner implementation for triggering tests
- logger: Logger for diagnostic output
Returns an initialized TriggerManager.
func (*TriggerManager) TriggerTests ¶
func (m *TriggerManager) TriggerTests( ctx context.Context, prt *v1alpha1.PullRequestTracker, policy *v1alpha1.PromotionPolicy, ) ([]TestExecution, error)
TriggerTests triggers all external tests defined in the promotion policy. Tests are triggered in parallel to minimize latency. Variable substitution is performed on test parameters using context from the PR and environment.
Parameters:
- ctx: Context for cancellation and timeout
- prt: PullRequestTracker CRD containing PR metadata
- policy: PromotionPolicy defining tests to trigger
Returns:
- Slice of TestExecution records (one per test)
- Error if any test failed to trigger (partial failure)