Skip to content

CRD Reference

All resources are cluster-scoped under drop.corewire.io/v1alpha1.

Quick Example

apiVersion: drop.corewire.io/v1alpha1
kind: CachedImage
metadata:
  name: nginx
spec:
  image: docker.io/library/nginx
  tag: latest
  nodeSelector:
    kubernetes.io/arch: amd64

CachedImage

CachedImage ensures a single container image is pre-cached on cluster nodes.

Controller: internal/controller/cachedimage_controller.go

Spec

FieldTypeRequiredDefaultDescription
imagestringYesImage is the fully qualified image reference without tag or digest. Example: “docker.io/library/nginx”, “registry.example.com/team/app”
tagstringNoTag to pull. Mutually exclusive with Digest. Example: “1.25-alpine”, “v2.4.1”, “latest”
digeststringNoDigest to pull as an immutable reference. Mutually exclusive with Tag. Use this for reproducible deployments where the exact image layer matters. Example: “sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4”
imagePullPolicycorev1.PullPolicyNoAlwaysImagePullPolicy controls when kubelet pulls the image on each node. - Always (default): check the registry for a newer digest even if the tag exists locally. - IfNotPresent: skip the registry check when the tag already exists on the node. - Never: never pull (only useful for pre-loaded images). (Always | IfNotPresent | Never)
imagePullSecrets[]corev1.LocalObjectReferenceNoImagePullSecrets are references to Secrets in the namespace where Drop creates pull Pods. The default namespace is “drop-system” unless the controller is started with a different –pod-namespace. The Secret must contain a .dockerconfigjson key. Example: [{name: “ghcr-creds”}, {name: “ecr-creds”}]
nodeSelectormap[string]stringNoNodeSelector restricts which nodes to cache the image on. Only nodes matching ALL key-value pairs will be targeted. Example: {“node-role.kubernetes.io/build”: “true”}
tolerations[]corev1.TolerationNoTolerations allow the pull pod to be scheduled on tainted nodes. Example: [{key: “node-role.kubernetes.io/build”, operator: “Exists”, effect: “NoSchedule”}]
priority*int32NoPriority is a pull ordering hint. Lower values are pulled first. Images with the same priority are pulled in alphabetical order. Default: 0 (no priority). Example: 10 (low priority), -10 (high priority)
policyRef*PolicyReferenceNoPolicyRef references a PullPolicy resource that controls pacing (concurrency, backoff, delays). If unset, the operator uses built-in defaults (1 concurrent node, 10s delay, 30s initial backoff). Example: {name: “conservative”}

Status

FieldTypeDescription
observedGenerationint64ObservedGeneration is the last generation reconciled.
phasestringPhase summarizes the overall state.
readystringReady is a human-readable “nodesReady/nodesTargeted” fraction for display.
resolvedDigeststringResolvedDigest is the sha256 digest of the image as reported by the container runtime after pull.
nodesTargetedint32NodesTargeted is the number of nodes that should have this image.
nodesReadyint32NodesReady is the number of nodes that have successfully pulled the image.
nodesPullingint32NodesPulling is the number of nodes currently pulling the image.
cachedNodes[]stringCachedNodes is the list of node names that have successfully cached the image.
consecutiveFailuresint32ConsecutiveFailures counts sequential reconcile failures for backoff calculation.
lastPulledAt*metav1.TimeLastPulledAt is the timestamp of the most recent successful pull.
lastAttemptedAt*metav1.TimeLastAttemptedAt is the timestamp of the most recent pull attempt (success or failure).
conditions[]metav1.ConditionConditions represent the latest available observations. Condition types: Ready, PullProgress.

CachedImageSet

CachedImageSet manages a group of images to cache, optionally backed by a DiscoveryPolicy.

Controller: internal/controller/cachedimageset_controller.go

Spec

FieldTypeRequiredDefaultDescription
policyRef*PolicyReferenceNoPolicyRef references a PullPolicy for pacing controls. Propagated to all child CachedImages. Example: {name: “conservative”}
discoveryPolicyRef*DiscoveryPolicyReferenceNoDiscoveryPolicyRef references a DiscoveryPolicy that provides a dynamic image list. When set, the operator reads status.discoveredImages from the referenced DiscoveryPolicy and creates/deletes child CachedImages accordingly. Can be combined with static images. Example: {name: “popular-build-images”}
imagePullPolicycorev1.PullPolicyNoAlwaysImagePullPolicy controls when kubelet pulls images. Propagated to all child CachedImages. Default: “Always”. See CachedImage.spec.imagePullPolicy for details. (Always | IfNotPresent | Never)
imagePullSecrets[]corev1.LocalObjectReferenceNoImagePullSecrets for private registries. Propagated to all child CachedImages. Secrets must exist in the namespace where Drop creates pull Pods (default: “drop-system”). Example: [{name: “ghcr-creds”}]
nodeSelectormap[string]stringNoNodeSelector restricts which nodes to cache images on. Propagated to all child CachedImages. Example: {“node-role.kubernetes.io/build”: “true”}
tolerations[]corev1.TolerationNoTolerations for tainted nodes. Propagated to all child CachedImages. Example: [{key: “node-role.kubernetes.io/build”, operator: “Exists”, effect: “NoSchedule”}]
images[]ImageEntryNoImages is a static list of images to cache. Each entry creates one child CachedImage. Can be used alone or combined with discoveryPolicyRef (both lists are merged).

Status

FieldTypeDescription
observedGenerationint64ObservedGeneration is the last generation reconciled.
phasestringPhase summarizes the overall state.
imagesManagedint32ImagesManaged is the number of CachedImage children managed by this set.
imagesReadyint32ImagesReady is the number of children in Ready phase.
conditions[]metav1.ConditionConditions represent the latest available observations.

DiscoveryPolicy

DiscoveryPolicy automatically discovers images from registries or Prometheus metrics.

Controller: internal/controller/discoverypolicy_controller.go

Spec

FieldTypeRequiredDefaultDescription
queries[]DiscoveryQueryNoQueries is the list of named raw-data sources. Each query is referenced by name from signals.
signals[]DiscoverySignalNoSignals is the list of named per-image metrics derived from query results. Each signal is referenced by name from the ranking configuration.
ranking*DiscoveryRankingNoRanking defines how signals are combined into a final ordered image list.
imageFilterstringNoImageFilter is a regex applied to discovered image references. Only matching images are kept. Example: “registry.example.com/team/.*” (only keep images from that registry path)
syncIntervalmetav1.DurationNo30mSyncInterval is how often the operator re-runs the pipeline and updates status.discoveredImages. Default: “30m”. Example: “1h”, “15m”
maxImagesint32No50MaxImages caps the total number of images stored in status.discoveredImages. Images are ranked by score; lowest-scoring images are dropped when the cap is exceeded. Default: 50. Example: 30, 100

Status

FieldTypeDescription
lastSyncTime*metav1.TimeLastSyncTime is the timestamp of the last reconciliation attempt.
queryResults[]QueryResultQueryResults reports the outcome of each named query execution.
discoveredImages[]DiscoveredImageDiscoveredImages is the ordered list of discovered and ranked images.
imageCountint32ImageCount is the number of discovered images.
conditions[]metav1.ConditionConditions represent the latest available observations.

PullPolicy

PullPolicy controls the pacing and retry behavior for image pulls across cluster nodes. It is a configuration-only resource with no status.

Spec

FieldTypeRequiredDefaultDescription
maxConcurrentNodesint32No1MaxConcurrentNodes is the maximum number of nodes pulling simultaneously for images that reference this policy. Increase for large clusters; keep low for bandwidth-constrained nodes. Default: 1. Example: 3 (pull on up to 3 nodes at once)
minDelayBetweenPullsmetav1.DurationNo10sMinDelayBetweenPulls is the minimum wait time between starting a pull on one node and starting the next pull on another node. Prevents burst traffic to the registry. Default: “10s”. Example: “30s”, “1m”
failureBackoff*BackoffConfigNoFailureBackoff configures exponential retry delays when a pull fails. If unset, defaults to initial=30s, max=5m.
repullInterval*metav1.DurationNoRepullInterval defines how often to re-pull already-cached images to pick up digest changes. Unset or zero means never re-pull (rely on imagePullPolicy=Always on the CachedImage instead). Example: “24h” (re-pull daily), “6h”
nodeSelectormap[string]stringNoNodeSelector scopes this policy to a specific node pool. Only relevant when the same PullPolicy should only pace pulls on a subset of nodes. Example: {“node-role.kubernetes.io/build”: “true”}
tolerations[]corev1.TolerationNoTolerations allow the pull pods created under this policy to schedule on tainted nodes. Example: [{key: “dedicated”, value: “ci”, effect: “NoSchedule”}]

Helper Types

AggregateSignalConfig

AggregateSignalConfig configures the aggregate signal type.

FieldTypeRequiredDefaultDescription
methodAggregationMethodYesMethod is the aggregation function applied to all samples per image.

BackoffConfig

BackoffConfig defines exponential retry backoff behavior for failed pulls.

FieldTypeRequiredDefaultDescription
initialmetav1.DurationNo30sInitial delay before the first retry attempt after a failure. Default: “30s”. Example: “1m”
maxmetav1.DurationNo5mMax is the upper bound on backoff delay. Retries will never wait longer than this. Default: “5m”. Example: “10m”

DiscoveredImage

DiscoveredImage represents a single discovered and ranked image.

FieldTypeRequiredDefaultDescription
imagestringYesImage is the fully qualified image reference.
rankint32YesRank is the position of this image in the final ordered list (1 = highest score).
finalScorestringYesFinalScore is the computed ranking score as a decimal string.

DiscoveryLokiQuery

DiscoveryLokiQuery defines the Loki-specific query parameters.

FieldTypeRequiredDefaultDescription
endpointstringYesEndpoint is the Loki API URL. Example: “https://loki.example.com
querystringYesQuery is the LogQL expression.
queryTypeLokiQueryTypeNorangeQueryType controls how the query is executed. Currently only “range” is supported.
lookback*metav1.DurationNoLookback is the time window for the query (start=now-lookback, end=now). Example: “168h” (7 days), “24h”
parser*LokiParserNoParser configures how log lines are parsed into structured event records.

DiscoveryPolicyReference

DiscoveryPolicyReference is a reference to a DiscoveryPolicy resource.

FieldTypeRequiredDefaultDescription
namestringYesName of the DiscoveryPolicy resource.

DiscoveryPrometheusQuery

DiscoveryPrometheusQuery defines the Prometheus-specific query parameters. The PromQL result MUST carry an “image” label; that label value is the image reference.

FieldTypeRequiredDefaultDescription
endpointstringYesEndpoint is the Prometheus-compatible API URL (Prometheus, Thanos, Mimir, VictoriaMetrics). Example: “http://prometheus.monitoring.svc:9090”, “https://mimir.example.com
querystringYesQuery is the PromQL expression. Must return results with an “image” label. Example: count(container_memory_working_set_bytes{namespace=“gitlab-runner”}) by (image)
queryTypeQueryTypeNorangeQueryType controls how the query is executed: “range” or “instant”. Default: “range”.
lookback*metav1.DurationNoLookback is the time window for range queries (start=now-lookback, end=now). Required when queryType is “range”. Ignored when queryType is “instant”. Example: “168h” (7 days), “24h”, “72h”
step*metav1.DurationNoStep is the resolution step for range queries. Smaller steps increase data-point density but also increase Prometheus load. Default: 5m. Example: “1m”, “15m”

DiscoveryQuery

DiscoveryQuery defines a named raw-data source referenced by signals.

FieldTypeRequiredDefaultDescription
namestringYesName is the unique identifier for this query within the policy. Signals reference queries by this name via query.
typeDiscoveryQueryTypeYesType selects the backend. Must be “prometheus”, “loki”, or “registry”.
prometheus*DiscoveryPrometheusQueryNoPrometheus contains the configuration when type=prometheus.
loki*DiscoveryLokiQueryNoLoki contains the configuration when type=loki.
registry*DiscoveryRegistryQueryNoRegistry contains the configuration when type=registry.
secretRef*corev1.LocalObjectReferenceNoSecretRef references a Secret in the pod namespace (default “drop-system”) for auth/TLS. Supported Secret keys: token, username, password, ca.crt, tls.crt, tls.key, headers..

DiscoveryRanking

DiscoveryRanking defines how signals are combined into the final ordered image list.

FieldTypeRequiredDefaultDescription
strategyRankingStrategyYesStrategy selects the ranking algorithm.
signalstringNoSignal is the name of the signal whose values determine image rank. Must match a signals[].name within the same policy. Required when strategy=signal.
weightedSum*WeightedSumRankingConfigNoWeightedSum is required when strategy=weightedSum.
modelExposure*ModelExposureRankingConfigNoModelExposure is required when strategy=modelExposure.

DiscoveryRegistryQuery

DiscoveryRegistryQuery defines OCI registry tag listing configuration for image discovery.

FieldTypeRequiredDefaultDescription
urlstringYesURL is the registry base URL (without repository path). Example: “https://registry.example.com”, “https://ghcr.io
repositories[]stringYesRepositories is the list of repository paths to list tags from. Example: [“team/app”, “team/worker”, “infra/tools”]
tagFilterstringNoTagFilter is a regex applied to tag names. Only matching tags are discovered. Example: “^v[0-9]+\.” (semver tags only), “^main-” (main branch builds)
tagSeekstringNoTagSeek is a pagination cursor passed to the registry as the last query parameter. The registry lists tags lexically after this value, letting you skip large numbers of irrelevant earlier tags without fetching them. It is not a real tag name — any string works. Example: “x86_64-u~” jumps straight to the “x86_64-v*” tags on a repo with tens of thousands of digest tags (GitLab runner helper).
topXint32NoTopX limits the number of tags kept per repository after tagFilter is applied. Tags are sorted newest-first (by version) before this cap is applied, so the newest N tags are kept. Example: 3 (keep the 3 newest matching tags per repo)
maxScanint32NoMaxScan caps how many tags are fetched per repository before filtering. Registries can hold tens of thousands of tags; this bounds the work. Pair it with tagSeek to fetch only the relevant range. Defaults to 1000 when unset. Example: 500
versionPatternstringNoVersionPattern is a regex with a single capture group that extracts the version substring from each tag for newest-first sorting. Use it when tags carry a prefix/suffix around the version, e.g. GitLab runner helper tags like “x86_64-v17.5.0” (pattern “x86_64-v(.+)”). When unset, Drop tries a strict semver parse, then falls back to extracting an embedded semver substring. Tags with no parseable version keep registry push order and sort after versioned tags. Example: “x86_64-v(.+)”
imageTemplatestringNoImageTemplate is a Go text/template for constructing the full image reference from discovered tags. Available variables: {{.Registry}}, {{.Repository}}, {{.Tag}} Default (when unset): “{{.Registry}}/{{.Repository}}:{{.Tag}}” Example: “registry.example.com/{{.Repository}}:{{.Tag}}”

DiscoverySignal

DiscoverySignal defines a named per-image metric derived from a single query.

FieldTypeRequiredDefaultDescription
namestringYesName is the unique identifier for this signal within the policy. Ranking configurations reference signals by this name.
querystringYesQuery is the name of the query that provides raw data for this signal. Must match a queries[].name within the same policy.
typeSignalTypeYesType selects the signal derivation method.
aggregate*AggregateSignalConfigNoAggregate is required when type=aggregate.
timeWeightedAggregate*TimeWeightedAggregateSignalConfigNoTimeWeightedAggregate is required when type=timeWeightedAggregate.
windowAggregate*WindowAggregateSignalConfigNoWindowAggregate is required when type=windowAggregate.
eventPullTime*EventPullTimeSignalConfigNoEventPullTime is required when type=eventPullTime.

EventPullTimeSignalConfig

EventPullTimeSignalConfig configures the eventPullTime signal type. The referenced query must be a Loki query. Pull duration and image size are extracted from the same Pulled events; metric selects which one to rank on.

FieldTypeRequiredDefaultDescription
metricEventMetricNopullTimeMetric selects which per-image quantity to aggregate. Defaults to pullTime, which correlates strongly with cold-start cost. Use imageSize to rank by bytes.
statisticEventStatisticYesStatistic selects how the metric’s samples are aggregated per image.

ImageEntry

ImageEntry defines a single image to include in a set.

FieldTypeRequiredDefaultDescription
imagestringYesImage is the fully qualified image reference without tag or digest. Example: “docker.io/library/nginx”, “registry.example.com/team/app”
tagstringNoTag to pull. Mutually exclusive with Digest. Example: “1.25-alpine”, “v2.4.1”
digeststringNoDigest to pull as an immutable reference. Mutually exclusive with Tag. Example: “sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4”

LokiParser

LokiParser configures structured parsing of Loki log entries.

FieldTypeRequiredDefaultDescription
typeLokiParserTypeYesType selects the parser. Currently only “kubernetesEvents” is supported.
podFieldstringNoPodField is the log label or field that contains the pod name. Example: “involvedObject_name”
reasonFieldstringNoReasonField is the log label or field that contains the event reason. Example: “reason”
messageFieldstringNoMessageField is the log label or field that contains the event message. Example: “message”
imageFieldstringNoImageField is the log label or field from which the image reference is extracted. For kubernetesEvents, the image is parsed out of the message text. Example: “message”

ModelExposureRankingConfig

ModelExposureRankingConfig configures the modelExposure ranking strategy. Score = J_target(I) * (1 - 1/N)^J_pre(I) * p_hat(I) where N is the node count (see NodeCountConfig), J_pre is pre-window usage, J_target is target-window usage, and p_hat is the pull-time signal value.

FieldTypeRequiredDefaultDescription
nodes*NodeCountConfigNoNodes determines N (the eligible node count) in the exposure formula, either as a static count or dynamically via a label selector.
preWindowUsageSignalstringYesPreWindowUsageSignal is the name of the signal representing usage before the target window. Must match a signals[].name within the same policy.
targetWindowUsageSignalstringYesTargetWindowUsageSignal is the name of the signal representing usage during the target window. Must match a signals[].name within the same policy.
pullTimeSignalstringYesPullTimeSignal is the name of the signal providing per-image pull-time estimates. Must match a signals[].name within the same policy.

NodeCountConfig

NodeCountConfig determines N (the eligible node count) for the modelExposure formula. Provide a static Count, a dynamic Selector, or both (Selector wins; Count is the fallback).

FieldTypeRequiredDefaultDescription
count*int32NoCount is the static number of eligible nodes. Used when Selector is unset, or as a fallback if node discovery fails.
selector*corev1.NodeSelectorNoSelector dynamically determines N by counting Ready nodes that match it via the Kubernetes API at each sync. This is a standard node selector (the same shape used by node affinity): nodeSelectorTerms are ORed, and within a term matchExpressions (node labels) and matchFields (e.g. metadata.name) are ANDed. A nil selector counts all Ready nodes. When set, it takes precedence over Count.

PolicyReference

PolicyReference is a reference to a PullPolicy resource.

FieldTypeRequiredDefaultDescription
namestringYesName of the PullPolicy resource.

QueryResult

QueryResult reports the outcome of a single named query execution.

FieldTypeRequiredDefaultDescription
namestringYesName matches the queries[].name that produced this result.
typeDiscoveryQueryTypeYesType is the query backend type (prometheus, loki, or registry).
statusQueryResultStatusYesStatus is “success” or “failed”.
messagestringNoMessage describes the failure reason when status=failed.

TimeOfDayWindow

TimeOfDayWindow defines a fixed wall-clock time range within each day.

FieldTypeRequiredDefaultDescription
startstringYesStart is the inclusive start time in “HH:MM” format (24-hour, local time). Example: “09:00”
endstringYesEnd is the exclusive end time in “HH:MM” format (24-hour, local time). Example: “17:00”

TimeWeightedAggregateSignalConfig

TimeWeightedAggregateSignalConfig configures the timeWeightedAggregate signal type. Each sample value is multiplied by the weight of the matching time window before aggregation.

FieldTypeRequiredDefaultDescription
methodAggregationMethodYesMethod is the aggregation function applied after weighting (currently only “sum” is meaningful).
timezonestringYesTimezone is the IANA time zone used to evaluate window boundaries (wall-clock hours). Example: “Europe/Berlin”, “America/New_York”, “UTC”
defaultWeightresource.QuantityYesDefaultWeight is applied to samples that do not fall in any configured window. Use “0” to exclude off-hours samples entirely.
windows[]TimeWeightedWindowYesWindows is the list of hour-of-day windows with associated weights.

TimeWeightedWindow

TimeWeightedWindow defines a wall-clock hour range and its weight factor.

FieldTypeRequiredDefaultDescription
startHourint32YesStartHour is the inclusive start of the window in local time (0–23).
endHourint32YesEndHour is the exclusive end of the window in local time (1–24).
weightresource.QuantityYesWeight is the factor applied to sample values within this window. Use “1.0” for full weight, “0.3” for partial, “0” to exclude.

WeightedSumRankingConfig

WeightedSumRankingConfig configures the weightedSum ranking strategy. Score = Σ weight_k * normalize(signal_k(image)).

FieldTypeRequiredDefaultDescription
normalizeNormalizeMethodYesminMaxNormalize selects the normalization method applied to each signal before weighting. Currently only “minMax” is supported.
missingSignalMissingSignalBehaviorYeszeroMissingSignal controls behavior when an image has no value for a required signal. “zero” treats missing as 0; “drop” removes the image from ranking.
terms[]WeightedSumTermYesTerms is the list of signals and their weights.

WeightedSumTerm

WeightedSumTerm defines one signal contribution in a weightedSum ranking.

FieldTypeRequiredDefaultDescription
signalstringYesSignal is the name of the signal to include in the weighted sum. Must match a signals[].name within the same policy.
weightresource.QuantityYesWeight is the factor applied to the normalized signal value. All weights should be non-negative; they do not need to sum to 1. Example: “0.7”

WindowAggregateSignalConfig

WindowAggregateSignalConfig configures the windowAggregate signal type. Exactly one of relativeWindow or (window + timezone) must be set.

FieldTypeRequiredDefaultDescription
methodAggregationMethodYesMethod is the aggregation function applied to the windowed samples.
relativeWindow*metav1.DurationNoRelativeWindow aggregates only samples from the last N duration before now. Mutually exclusive with window + timezone. Example: “2h” (last 2 hours)
timezonestringNoTimezone is the IANA time zone for evaluating wall-clock window boundaries. Required when window is set.
window*TimeOfDayWindowNoWindow defines fixed wall-clock start/end times within each day. Mutually exclusive with relativeWindow.
Last updated on