README
¶
GitHub Path Reconciler Module
This module creates a GitHub path reconciliation system that monitors file paths in a GitHub repository and reconciles them when they change. It combines a regional-go-reconciler with both periodic (cron-based) and event-driven (push-based) reconciliation.
Usage
module "path-reconciler" {
source = "chainguard-dev/terraform-infra-common//modules/github-path-reconciler"
project_id = var.project_id
name = "my-path-reconciler"
primary-region = "us-central1"
regions = var.regions
service_account = google_service_account.reconciler.email
# Container configuration
containers = {
reconciler = {
source = {
working_dir = path.module
importpath = "./cmd/reconciler"
}
ports = [{
container_port = 8080
}]
env = [{
name = "OCTO_IDENTITY"
value = "my-reconciler"
}]
}
}
# Path patterns to match (with exactly one capture group each)
path_patterns = [
"^configs/(.+\\.yaml)$", # Match YAML files in configs/
"^deployments/(.+)$", # Match everything in deployments/
]
# Repository configuration
github_owner = "my-org"
github_repo = "my-repo"
octo_sts_identity = "my-reconciler"
# Event broker for push notifications
broker = var.github_events_broker
# Resync every 6 hours
resync_period_hours = 6
notification_channels = var.notification_channels
squad = "platform"
product = "infrastructure"
}
Features
- Path Pattern Matching: Define regex patterns to match specific file paths
- Dual Reconciliation Modes:
- Event-Driven: Responds immediately to push events with high priority
- Periodic: Full repository scan on a configurable schedule
- Built-in Workqueue: Integrated workqueue with priority support
- Regional Deployment: Deploy reconciler services across multiple regions
- Pausable: Single control to pause both cron and push listeners
Architecture
The module creates:
-
Reconciler Service (via
regional-go-reconciler):- Implements the workqueue service protocol
- Processes path reconciliation requests
- Deployed across all configured regions
-
Cron Job (periodic reconciliation):
- Runs on a schedule (configurable in hours)
- Fetches all files from the repository at HEAD
- Matches files against path patterns
- Enqueues matched paths with time-bucketed delays (priority 0)
-
Push Listener (event-driven reconciliation):
- Subscribes to GitHub push events via CloudEvents
- Compares commits to find changed files
- Matches changed files against path patterns
- Enqueues matched paths immediately (priority 100)
Path Patterns
Path patterns are regular expressions with exactly one capture group. The captured portion becomes the path in the resource URL.
Note: Patterns are automatically anchored with ^ and $, ensuring full-path matching. Do not include these anchors in your patterns.
Examples:
path_patterns = [
# Match all files (entire path)
"(.+)",
# Match only YAML files (entire path)
"(.+\\.yaml)",
# Match files in a specific directory (entire path)
"(infrastructure/.+)",
]
The module will create resource URLs in the format:
https://github.com/{owner}/{repo}/blob/{branch}/{captured_path}
Reconciler Implementation
Your reconciler should implement the workqueue protocol. The key will be a GitHub URL to the file path:
import (
"github.com/chainguard-dev/terraform-infra-common/pkg/githubreconciler"
"github.com/chainguard-dev/terraform-infra-common/pkg/workqueue"
)
func (r *Reconciler) Process(ctx context.Context, req *workqueue.ProcessRequest) (*workqueue.ProcessResponse, error) {
log := clog.FromContext(ctx)
// Parse the GitHub URL from the key
res, err := githubreconciler.ParseResource(req.Key)
if err != nil {
return nil, err
}
log.Infof("Reconciling path: %s in %s/%s", res.Path, res.Owner, res.Repo)
// Your reconciliation logic here
// ...
return &workqueue.ProcessResponse{}, nil
}
Reconciliation Triggers
Periodic (Cron)
- Runs every
resync_period_hours(1-744 hours) - Fetches complete repository tree at HEAD
- Uses time-bucketed delays to spread load across the period
- Priority: 0 (normal)
Push Events
- Triggers on GitHub push events
- Uses
CompareCommitsAPI to get all changed files - Handles all merge strategies (merge commits, squash, rebase)
- Priority: 100 (immediate)
Safe Rollout Process
To safely deploy a new path reconciler, follow these steps:
-
Initial Deployment - Deploy with
paused = trueanddeletion_protection = false:module "my-reconciler" { # ... other configuration ... paused = true deletion_protection = false } -
Create Octo STS Identity - After applying, use the service account's
unique_idoutput to create the Octo STS identity in the GitHub organization. This grants the reconciler access to the GitHub API. -
Unpause - Once the Octo STS identity is configured, set
paused = falseand apply:paused = false -
Enable Protection - After verifying the reconciler works correctly and you're confident you won't need to tear it down quickly, enable deletion protection:
deletion_protection = true
Variables
See variables.tf for all available configuration options.
Key variables:
path_patterns: List of regex patterns (each with one capture group)github_owner,github_repo: Repository to monitorocto_sts_identity: Octo STS identity for GitHub authenticationbroker: Map of region to CloudEvents broker topicresync_period_hours: How often to run full reconciliation (1-744)paused: Pause both cron and push listenersdeletion_protection: Prevent accidental deletion (disable during initial rollout)
Requirements
No requirements.
Providers
No providers.
Modules
| Name | Source | Version |
|---|---|---|
| authorize-receiver-per-region | ../authorize-private-service | n/a |
| cron | ../cron | n/a |
| push-listener | ../regional-go-service | n/a |
| push-subscription | ../cloudevent-trigger | n/a |
| reconciler | ../regional-go-reconciler | n/a |
Resources
No resources.
Inputs
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| batch-size | Optional cap on how much work to launch per dispatcher pass. | number |
null |
no |
| broker | A map from each of the input region names to the name of the Broker topic in that region. | map(string) |
n/a | yes |
| concurrent-work | The amount of concurrent work to dispatch at a given time. | number |
20 |
no |
| containers | The containers to run in the service. Each container will be run in each region. | map(object({ |
{} |
no |
| deletion_protection | Whether to enable delete protection for the service. | bool |
true |
no |
| egress | Which type of egress traffic to send through the VPC. - ALL_TRAFFIC sends all traffic through regional VPC network. This should be used if service is not expected to egress to the Internet. - PRIVATE_RANGES_ONLY sends only traffic to private IP addresses through regional VPC network |
string |
"ALL_TRAFFIC" |
no |
| enable_dead_letter_alerting | Whether to enable alerting for dead-lettered keys. | bool |
true |
no |
| enable_profiler | Enable continuous profiling for the service. This has a small performance impact, which shouldn't matter for production services. | bool |
true |
no |
| execution_environment | The execution environment for the service (options: EXECUTION_ENVIRONMENT_GEN1, EXECUTION_ENVIRONMENT_GEN2). | string |
"EXECUTION_ENVIRONMENT_GEN2" |
no |
| github_owner | GitHub organization or user | string |
n/a | yes |
| github_repo | GitHub repository name | string |
n/a | yes |
| labels | Additional labels to add to all resources. | map(string) |
{} |
no |
| max-retry | The maximum number of times a task will be retried before being moved to the dead-letter queue. Set to 0 for unlimited retries. | number |
100 |
no |
| multi_regional_location | The multi-regional location for the global workqueue bucket. Options: US, EU, ASIA. | string |
"US" |
no |
| name | n/a | string |
n/a | yes |
| notification_channels | The channels to send notifications to. List of channel IDs | list(string) |
[] |
no |
| octo_sts_identity | Octo STS identity for GitHub authentication | string |
n/a | yes |
| otel_resources | Resources to add to the OpenTelemetry resource. | map(string) |
{} |
no |
| path_patterns | List of regex patterns with one capture group each for matching paths | list(string) |
n/a | yes |
| paused | Whether to pause both the cron service and push listener | bool |
false |
no |
| primary-region | The primary region to run the cron job in | string |
n/a | yes |
| product | The product that this service belongs to. | string |
"" |
no |
| project_id | n/a | string |
n/a | yes |
| regional-volumes | The volumes to make available to the containers in the service for mounting. | list(object({ |
[] |
no |
| regions | A map from region names to a network and subnetwork. A service will be created in each region configured to egress the specified traffic via the specified subnetwork. | map(object({ |
n/a | yes |
| request_timeout_seconds | The request timeout for the service in seconds. | number |
300 |
no |
| resync_period_hours | How often to resync all paths (in hours, must be between 1 and 744 (31 days), and a multiple of 24 if greater than 24) | number |
n/a | yes |
| scaling | The scaling configuration for the service. | object({ |
{} |
no |
| service_account | The service account as which to run the reconciler service. | string |
n/a | yes |
| slo | Configuration for setting up SLO for the cloud run service | object({ |
{} |
no |
| team | Team label to apply to resources (replaces deprecated 'squad'). | string |
n/a | yes |
| volumes | The volumes to attach to the service. | list(object({ |
[] |
no |
| workqueue_cpu_idle | Set to false for a region in order to use instance-based billing for workqueue services (dispatcher and receiver). Defaults to true. To control reconciler cpu_idle, use the 'regional-cpu-idle' field in the 'containers' variable. | map(map(bool)) |
{ |
no |
Outputs
| Name | Description |
|---|---|
| receiver | The workqueue receiver object for connecting triggers. |