Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
6705ccb
Initial plan
Copilot Jun 27, 2026
fe2f81a
feat(discovery): replace sources[] with query/signal/ranking pipeline…
Copilot Jun 27, 2026
d3757c2
feat(discovery): implement query/signal/ranking pipeline + restore re…
Copilot Jun 27, 2026
a00b969
feat(discovery): implement Loki query execution and eventPullTime sig…
Copilot Jun 27, 2026
61199bd
test(e2e): add Loki infrastructure and seed job for image-pull event …
Jun 27, 2026
cedd715
test(e2e): add discovery-loki Chainsaw test for Loki + eventPullTime
Jun 27, 2026
7a44c2d
test(e2e): drop Loki readiness probe to match prometheus/registry infra
Jun 27, 2026
769a38d
test(e2e): assert all three seeded images are discovered from Loki
Jun 27, 2026
44f271b
fix(e2e): restore Loki readiness probe, fix README, remove stale assert
Copilot Jun 27, 2026
0c52eff
Apply remaining changes
Copilot Jun 27, 2026
19db893
fix(devenv): fix tiltfile.
Breee Jun 28, 2026
260dfbe
feat(devenv): devsample up to date with current featureset
Breee Jun 28, 2026
480a13b
feat(crds): slim the crd status to not pollute etcd
Breee Jun 28, 2026
ecf9f63
feat(tests): rework tests
Breee Jun 28, 2026
35195ad
uidoc
Breee Jun 28, 2026
30cce4a
doocs
Breee Jun 28, 2026
5ca7d74
strats
Breee Jun 28, 2026
ec0006d
refactor fields
Breee Jun 29, 2026
d4b68d8
update samples
Breee Jun 29, 2026
7a53ed0
update tests
Breee Jun 29, 2026
c6e77ef
update make
Breee Jun 29, 2026
43e1155
gen
Breee Jun 29, 2026
bc3981d
ignore
Breee Jun 29, 2026
83193d3
dashboard
Breee Jun 29, 2026
d20ae80
alloy conform events
Breee Jun 29, 2026
7e40ea1
refactor loki parsing
Breee Jun 29, 2026
83c13c6
tests
Breee Jun 29, 2026
7c79325
infra
Breee Jun 29, 2026
71afb4b
docs
Breee Jun 29, 2026
0730a49
docs
Breee Jun 29, 2026
22ee5e3
tilt
Breee Jun 29, 2026
e8154c8
registry discovery / docs
Breee Jun 29, 2026
4d5fcc6
loki pull
Breee Jun 29, 2026
33ae6dc
twsts, gen
Breee Jun 29, 2026
5b07808
tests
Breee Jun 29, 2026
1a5f650
data
Breee Jun 30, 2026
2a4b80a
data
Breee Jun 30, 2026
1a75a82
docs, graphs
Breee Jun 30, 2026
aff01d6
graphs
Breee Jun 30, 2026
c09d06e
graphs
Breee Jun 30, 2026
b9810f3
nodeselectors
Breee Jun 30, 2026
b4d5ce8
nodeselectors
Breee Jun 30, 2026
f4fc141
update samples
Breee Jun 30, 2026
ae27c56
fix test
Breee Jun 30, 2026
11c8b5d
docs and resaerch
Breee Jul 1, 2026
299e8bd
figs
Breee Jul 5, 2026
f468588
fix
Breee Jul 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ jobs:
make controller-gen
make sync-crds
kubectl apply -f config/crd/bases/
- name: Deploy E2E infrastructure (Prometheus + Registry)
- name: Deploy E2E infrastructure (Prometheus, Loki, Registry)
run: make e2e-infra
- name: Deploy operator
run: |
Expand Down
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,16 @@ docs/.hugo_build.lock
# Generated CRD chart templates (produced by make sync-crds in CI)
charts/drop-crds/templates/drop.corewire.io_*.yaml
charts/drop/templates/crds-drop.corewire.io_*.yaml

# Imported research archives (always unpack; never commit zip bundles)
research/**/*.zip

# Python cache artifacts
__pycache__/
*.pyc

# Local Python virtual environments
.venv/

# Local docs image generation workspace artifacts
docs/static/images/src/usr/
87 changes: 86 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ uninstall: manifests kustomize ## Uninstall CRDs from cluster.
$(KUSTOMIZE) build config/crd | $(KUBECTL) delete --ignore-not-found -f -

.PHONY: e2e-infra
e2e-infra: ## Deploy Prometheus + Registry for E2E/dev.
e2e-infra: ## Deploy Prometheus, Loki, and Registry for E2E/dev.
@chmod +x hack/e2e-infra/setup.sh && hack/e2e-infra/setup.sh

##@ Docker
Expand All @@ -122,6 +122,9 @@ kind-load: docker-build ## Build and load image into kind.

##@ Helm & Docs

DOCS_IMG_SRC_DIR ?= docs/static/images/src
DOCS_IMG_VENV ?= $(DOCS_IMG_SRC_DIR)/.venv

.PHONY: helm-lint
helm-lint: ## Lint the Helm chart.
helm lint charts/drop
Expand All @@ -143,6 +146,88 @@ docs-gen-check: docs-gen ## Verify generated AI docs are up to date.
@git diff --exit-code knowledge.yaml llms.txt llms-full.txt docs/static/llms-full.txt .github/copilot-instructions.md .cursorrules AGENTS.md docs/content/docs/reference/_generated_*.md || \
(echo "ERROR: generated docs are out of date — run 'make docs-gen'" && exit 1)

.PHONY: docs-images-setup
docs-images-setup: ## Create docs image venv and install chart dependencies.
@cd $(DOCS_IMG_SRC_DIR) && \
python3 -m venv .venv && \
. .venv/bin/activate && \
pip install -r requirements.txt

.PHONY: docs-images-gen
docs-images-gen: docs-images-setup ## Generate docs SVG charts from sample data.
@cd $(DOCS_IMG_SRC_DIR) && \
. .venv/bin/activate && \
python signal-eventpulltime.py && \
python discovery-charts.py && \
python research-doc-assets.py && \
python research-tikz-assets.py

.PHONY: static
static: docs-images-gen ## Generate all maintained static docs graphics.

##@ Research

RESEARCH_TEX_DIR ?= research/tex
RESEARCH_TEX_FILE ?= paper.tex
RESEARCH_BENCH_DIR ?= research/benchmark/evaluator
RESEARCH_BENCH_VENV ?= $(RESEARCH_BENCH_DIR)/.venv
RESEARCH_BENCH_RESULTS_DIR ?= research/benchmark/results
RESEARCH_BENCH_RESULTS_DISCOVERY_20RUNS ?= $(RESEARCH_BENCH_RESULTS_DIR)/discovery-strategy-20runs
RESEARCH_BENCH_RESULTS_ORACLE_20RUNS ?= $(RESEARCH_BENCH_RESULTS_DIR)/oracle-gap-strategy-20runs
RESEARCH_BENCH_RESULTS_CACHE_20RUNS ?= $(RESEARCH_BENCH_RESULTS_DIR)/ci-image-cache-20runs

.PHONY: research-tex-build
research-tex-build: ## Build research PDF from TeX source (override RESEARCH_TEX_FILE=<file.tex>).
@cd $(RESEARCH_TEX_DIR) && \
if command -v latexmk >/dev/null 2>&1; then \
latexmk -pdf -interaction=nonstopmode -halt-on-error $(RESEARCH_TEX_FILE); \
elif command -v pdflatex >/dev/null 2>&1; then \
pdflatex -interaction=nonstopmode -halt-on-error $(RESEARCH_TEX_FILE) && \
pdflatex -interaction=nonstopmode -halt-on-error $(RESEARCH_TEX_FILE); \
else \
echo "ERROR: latexmk/pdflatex not found"; exit 1; \
fi

.PHONY: research-bench-setup
research-bench-setup: ## Create benchmark venv and install Python dependencies.
@cd $(RESEARCH_BENCH_DIR) && \
python3 -m venv .venv && \
. .venv/bin/activate && \
pip install -r requirements.txt

.PHONY: research-bench-generate
research-bench-generate: ## Generate synthetic benchmark dataset.
@cd $(RESEARCH_BENCH_DIR) && \
. .venv/bin/activate && \
python generate_synthetic_day.py --out data --jobs 25000 --nodes 100 --images 30 --seed 20260621

.PHONY: research-bench-replay
research-bench-replay: ## Run replay policy evaluation from benchmark data.
@cd $(RESEARCH_BENCH_DIR) && \
. .venv/bin/activate && \
python evaluate_replay.py --data data --out outputs

.PHONY: research-bench-discovery
research-bench-discovery: ## Evaluate discovery strategies from benchmark data.
@cd $(RESEARCH_BENCH_DIR) && \
. .venv/bin/activate && \
python evaluate_discovery_strategies.py --data data --out outputs/strategy_eval

.PHONY: research-bench-plot
research-bench-plot: ## Render example pipeline Gantt figure.
@cd $(RESEARCH_BENCH_DIR) && \
. .venv/bin/activate && \
python plot_pipeline_gantt.py --modeled-jobs outputs/modeled_jobs_no_prewarming.csv --out figures/example_gantt.png

.PHONY: research-bench-20runs
research-bench-20runs: ## Run 20-run discovery strategy benchmark batch.
@cd $(RESEARCH_BENCH_DIR) && \
. .venv/bin/activate && \
python run_discovery_strategy_20runs.py

.PHONY: research-bench-all
research-bench-all: research-bench-generate research-bench-replay research-bench-discovery research-bench-plot ## Run full synthetic benchmark workflow.

.PHONY: tools
tools: ## Install local tooling and check optional docs/chart binaries.
@$(MAKE) kustomize controller-gen setup-envtest golangci-lint chainsaw
Expand Down
134 changes: 87 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</p>


A Kubernetes operator that pre-pulls container images onto nodes — safely, with pacing, and with automatic discovery.
A Kubernetes operator that pre-pulls container images onto nodes — safely, with pacing, and with automatic discovery.

## Why

Expand Down Expand Up @@ -45,6 +45,8 @@ VERSION="$(curl -fsSL https://api.github.com/repos/corewire/drop/releases/latest

# Install CRDs first so upgrades stay predictable
helm install drop-crds oci://ghcr.io/corewire/charts/drop-crds \
--namespace drop-system \
--create-namespace \
--version "$VERSION"

# Install the operator
Expand Down Expand Up @@ -115,18 +117,19 @@ spec:
maxImages: 20
# Only keep images from your internal registry (regex filter, optional)
imageFilter: "registry.example.com/.*"
sources:
- type: prometheus
queries:
- name: runner-image-usage
type: prometheus
prometheus:
# Any Prometheus-compatible API (Prometheus, Thanos, Mimir, VictoriaMetrics)
endpoint: https://mimir.example.com
# Aggregate over the last 7 days using query_range; counts container
# instances per image across the window to produce a usage score
queryType: range
lookback: 168h
# Resolution step for range queries (default: 5m)
step: 5m
# PromQL query — MUST return results with an "image" label.
# The result value becomes the ranking score (higher = cached first).
query: |
count(
container_memory_working_set_bytes{
Expand All @@ -135,9 +138,18 @@ spec:
}
) by (image)
# Optional: Secret in the Drop pod namespace (default: drop-system)
# Supported keys: token, username, password, ca.crt, tls.crt, tls.key
# Supported keys: token, username, password, ca.crt, tls.crt, tls.key, headers.<name>
secretRef:
name: prometheus-creds
signals:
- name: total-usage
query: runner-image-usage
type: aggregate
aggregate:
method: sum
ranking:
strategy: signal
signal: total-usage
---
# --- 3. CachedImageSet: ties discovery + policy together, targets nodes ---
apiVersion: drop.corewire.io/v1alpha1
Expand Down Expand Up @@ -304,18 +316,19 @@ spec:
maxImages: 30
# Only keep images matching this regex (optional)
imageFilter: "registry.example.com/.*"
sources:
- type: prometheus
queries:
- name: runner-image-usage
type: prometheus
prometheus:
# Any Prometheus-compatible API (Prometheus, Thanos, Mimir, VictoriaMetrics)
endpoint: https://mimir.example.com
# Aggregate over the last 7 days (uses query_range, sums values per image)
# Omit for a point-in-time instant query instead
queryType: range
lookback: 168h
# Resolution step for range queries (default: 5m)
step: 5m
# PromQL query — MUST return results with an "image" label.
# The result value becomes the ranking score (higher = cached first).
query: |
count(
container_memory_working_set_bytes{
Expand All @@ -327,6 +340,15 @@ spec:
# Supported keys: token, username, password, ca.crt, tls.crt, tls.key, headers.<name>
secretRef:
name: prometheus-creds
signals:
- name: total-usage
query: runner-image-usage
type: aggregate
aggregate:
method: sum
ranking:
strategy: signal
signal: total-usage
---
apiVersion: drop.corewire.io/v1alpha1
kind: CachedImageSet
Expand All @@ -342,7 +364,11 @@ spec:
tag: "3.19"
```

### Use case: discover and cache application tags from a registry
### Use case: discover and cache GitLab runner helper images from a registry

GitLab runner helper tags carry an arch/flavor prefix (e.g. `x86_64-v17.5.0`).
Drop extracts the embedded version automatically; `versionPattern` is shown for
clarity but is optional here.

```yaml
apiVersion: v1
Expand All @@ -362,24 +388,30 @@ metadata:
spec:
syncInterval: 15m
maxImages: 10
sources:
- type: registry
queries:
- name: registry-tags
type: registry
registry:
# Registry base URL
url: https://registry.example.com
url: https://registry.gitlab.com
# Repositories to list tags from
repositories:
- team/frontend
- team/backend
- team/worker
# Only discover semver tags (regex on tag name)
tagFilter: "^v[0-9]+\\."
# Keep only the last 3 matching tags returned by the registry
- gitlab-org/gitlab-runner/gitlab-runner-helper
# Only discover x86_64 semver tags (regex on tag name)
tagFilter: "^x86_64-v[0-9]+\\."
# Optional: pin where the version lives in the tag (capture group 1)
versionPattern: "x86_64-v(.+)"
# Optional: skip straight to the x86_64-v* tags (registry `last` cursor)
tagSeek: "x86_64-u~"
# Optional: cap tags fetched per repo before filtering (default 1000)
maxScan: 2000
# Keep only the 3 newest matching tags (newest first)
topX: 3
# Optional: Secret in the Drop pod namespace (default: drop-system)
# Supported keys: token, username, password, ca.crt, tls.crt, tls.key, headers.<name>
secretRef:
name: registry-api-creds
# No signals/ranking needed: registry tags are returned newest-first.
---
apiVersion: drop.corewire.io/v1alpha1
kind: CachedImageSet
Expand All @@ -390,27 +422,6 @@ spec:
name: latest-app-tags
```

## Quick Start

```bash
# Install CRDs and operator via Helm
helm install drop charts/drop -n drop-system --create-namespace

# Cache a single image
kubectl apply -f - <<YAML
apiVersion: drop.corewire.io/v1alpha1
kind: CachedImage
metadata:
name: nginx
spec:
image: docker.io/library/nginx
tag: 1.25-alpine
YAML

# Check status
kubectl get cachedimage nginx -o wide
```

## CRDs

All resources are **cluster-scoped** under `drop.corewire.io/v1alpha1`.
Expand Down Expand Up @@ -442,24 +453,53 @@ dev-set AllReady 3/3 3 dev-registry 1h
web-apps Degraded 1/3 3 10m

$ kubectl get discoverypolicies
NAME STATUS SOURCES IMAGES LASTSYNC AGE
dev-registry Synced 1 3 30s 1h
broken-prom ConnectionRefused 1 0 5m
bad-auth Unauthorized 1 0 2m
NAME STATUS IMAGES LASTSYNC AGE
dev-registry Synced 3 30s 1h
broken-prom ConnectionRefused 0 5m
bad-auth Unauthorized 0 2m
```

## Research And Benchmark

The repo includes the replay benchmark used in the research docs and paper.

```bash
# Synthetic benchmark day + replay
make research-bench-setup
make research-bench-generate
make research-bench-replay
make research-bench-discovery

# Or fetch real-cluster inputs from Prometheus + Loki
cd research/benchmark/evaluator
. .venv/bin/activate
python fetch_cluster_data.py \
--prometheus-url http://localhost:9090 \
--loki-url http://localhost:3100 \
--out data
```

See `research/benchmark/evaluator/README.md` for the full input schema and live-cluster workflow.

## Development

```bash
# Prerequisites: Go 1.23+, Kind, Tilt, Helm
make generate # deepcopy
make manifests # CRDs + RBAC
# Prerequisites: Go 1.26+, Kind, Tilt, Helm
make codegen # deepcopy + CRDs + generated docs
go build ./... # compile
make test # unit tests
make lint # golangci-lint

# Local dev loop (Kind + Tilt)
# Local dev loop: creates kind, deploys Drop, Prometheus, Loki, Grafana, docs
tilt up
```

## Docs

Full documentation at **[corewire.github.io/drop/](https://corewire.github.io/drop/)** (GitHub Pages).

```bash
make static # regenerate maintained docs graphics
make docs-gen # regenerate AI-facing docs
cd docs && hugo server --buildDrafts --port 1313
```
Loading