Image Factory Workflow
This document describes how the image factory tool and cdk8s app work together.
Overview
The image factory system manages container images in two categories:
- Managed Images: Images we build and maintain (have source repo and Dockerfile)
- External Images: Third-party images we track but don’t build (base images, dependencies)
Data Flow
images.yaml (source of truth)
↓
tool.py (discovers dependencies, generates state)
↓
state/images/*.yaml + state/base-images/*.yaml
↓
cdk8s/image-factory/main.py (generates Kubernetes manifests)
↓
Kargo Warehouse resources
Key Principles
- images.yaml is the source of truth for enrollment configuration
- State files preserve runtime data (digests, build history, etc.)
- Tool merges config with state, preferring images.yaml for config
- External images can become managed by adding source info
- Base images are auto-discovered from Dockerfiles
Usage
1. Enroll a New Managed Image
Add to images.yaml:
- name: myapp
registry: ghcr.io
repository: owner/myapp
source:
provider: github
repo: owner/repo
branch: main
dockerfile: apps/myapp/Dockerfile
workflow: build.yml
rebuildDelay: 7d
autoRebuild: trueRun the tool:
cd image-factory
./tool.pyThis will:
- Create
state/images/myapp.yaml - Parse the Dockerfile to find base images
- Create state files for any new base images in
state/base-images/
2. Enroll an External Image
Add to images.yaml:
- name: postgres
registry: docker.io
repository: library/postgres
allowTags: ^16-alpine$
imageSelectionStrategy: Lexical
rebuildDelay: 30d
autoRebuild: falseRun the tool - it will create state/images/postgres.yaml with warehouse config.
3. Convert External to Managed
Simply add the source section to the image in images.yaml and run the tool.
The state file will be updated to reflect the new managed status.
4. Generate Kubernetes Manifests
cd cdk8s/image-factory
cdk8s synthThis reads the state files and generates Kargo Warehouse resources in dist/.
State File Structure
Managed Image State (state/images/*.yaml)
name: backstage
enrolledAt: "2024-12-04T10:00:00Z"
lastDiscovery: "2024-12-04T15:30:00Z"
discoveryStatus: pending # or: success, failed, external
enrollment:
registry: ghcr.io
repository: owner/backstage
source:
provider: github
repo: owner/repo
dockerfile: apps/backstage/Dockerfile
rebuildDelay: 7d
autoRebuild: true
baseImages:
- node-22-bookworm-slim
currentVersion: "0.6.5"
currentDigest: sha256:abc123...
lastBuilt: "2024-12-03T12:00:00Z"
rebuildState:
status: monitoring
pendingRebuild: falseExternal Image State (state/images/*.yaml)
name: postgres
enrolledAt: "2024-12-04T10:00:00Z"
discoveryStatus: external
enrollment:
registry: docker.io
repository: library/postgres
rebuildDelay: 30d
autoRebuild: false
# Warehouse configuration (for cdk8s)
repoURL: docker.io/library/postgres
allowTags: ^16-alpine$
imageSelectionStrategy: Lexical
baseImages: []Base Image State (state/base-images/*.yaml)
name: node-22-bookworm-slim
fullImage: node:22-bookworm-slim
registry: docker.io
repository: library/node
tag: 22-bookworm-slim
# Warehouse configuration (for cdk8s)
repoURL: docker.io/library/node
allowTags: ^22-bookworm-slim$
imageSelectionStrategy: Lexical
firstDiscovered: "2024-12-04T10:00:00Z"
lastChecked: "2024-12-04T15:30:00Z"
dependentImages:
- backstage
- frontend
currentDigest: sha256:def456...
lastUpdated: nullCDK8s Integration
The cdk8s app (cdk8s/image-factory/main.py):
- Loads
images.yaml - Loads all state files from
state/images/andstate/base-images/ - Merges them (images.yaml takes precedence)
- Creates Kargo Warehouse resources for images with
repoURLandallowTags
Only base images and external images get Warehouse resources, since managed images are built by CI/CD and pushed to registries.
Testing
Unit Tests
# Test the tool
cd image-factory
pytest test_tool.py
# Test the cdk8s app
cd cdk8s/image-factory
pytest test_main.pyIntegration Tests
cd image-factory
pytest test_integration.pyThis verifies the complete workflow from images.yaml → tool → state files → cdk8s.
Workflow Scenarios
Scenario 1: New Base Image Version
- Kargo detects new
node:22-bookworm-slimdigest - Updates
state/base-images/node-22-bookworm-slim.yaml - Checks
dependentImageslist - For each dependent, checks if
rebuildDelayhas passed - Triggers rebuilds for eligible images
Scenario 2: Adding a New Image
- Developer adds image to
images.yaml - Runs
./tool.py - Tool creates state file and discovers base images
- Runs
cdk8s synthto generate Warehouse for base images - Applies manifests to cluster
- Kargo starts tracking base images
Scenario 3: Changing Image Configuration
- Developer updates
images.yaml(e.g., changes registry) - Runs
./tool.py - Tool merges new config with existing state
- Runtime data (digests, history) is preserved
- Configuration is updated
Best Practices
- Always run tool.py after editing images.yaml
- Commit state files to git (they contain important tracking data)
- Don’t manually edit state files (use images.yaml instead)
- Run tests before committing to ensure consistency
- Review generated manifests before applying to cluster