Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,8 @@ Run with config file:
| `DUCKGRES_HANDOVER_DRAIN_TIMEOUT` | Max time to drain planned shutdowns and upgrades before forcing exit | `24h` in process mode, `15m` in remote K8s mode |
| `DUCKGRES_SNI_ROUTING_MODE` | Multi-tenant managed-hostname routing: `off`, `passthrough`, or `enforce`. Postgres uses the requested dbname first; managed SNI must resolve to the same org, and SNI supplies the database only when dbname is empty. | `off` |
| `DUCKGRES_MANAGED_HOSTNAME_SUFFIXES` | Comma-separated managed hostname suffixes such as `.dw.us.postwh.com` | - |
| `DUCKGRES_K8S_SHARED_WARM_TARGET` | Global neutral shared warm-worker target for K8s multi-tenant mode (`0` disables prewarm; subject to `DUCKGRES_K8S_MAX_WORKERS`) | `0` |
| `DUCKGRES_K8S_MAX_WORKERS` | Global cap for shared K8s workers (`0` means Duckgres does not impose a cap) | `0` |
| `DUCKGRES_K8S_SHARED_WARM_TARGET` | Default-image neutral shared warm-worker target for K8s multi-tenant mode (`0` disables prewarm; subject to `DUCKGRES_K8S_MAX_WORKERS`) | `0` |
| `DUCKGRES_DUCKLAKE_METADATA_STORE` | DuckLake metadata connection string | - |
| `DUCKGRES_DUCKLAKE_DELTA_CATALOG_ENABLED` | Attach a Delta Lake catalog/table during worker boot/activation | `false` |
| `DUCKGRES_DUCKLAKE_DELTA_CATALOG_PATH` | Delta Lake catalog/table path; defaults to sibling `delta/` prefix at the DuckLake object-store root when enabled | Derived |
Expand Down
40 changes: 23 additions & 17 deletions cmd/duckgres-controlplane/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,23 +216,29 @@ func main() {
ManagedHostnameSuffixes: resolved.ManagedHostnameSuffixes,
DuckLakeDefaultSpecVersion: resolved.DuckLakeDefaultSpecVersion,
K8s: controlplane.K8sConfig{
WorkerImage: resolved.K8sWorkerImage,
WorkerNamespace: resolved.K8sWorkerNamespace,
ControlPlaneID: resolved.K8sControlPlaneID,
WorkerPort: resolved.K8sWorkerPort,
WorkerSecret: resolved.K8sWorkerSecret,
WorkerConfigMap: resolved.K8sWorkerConfigMap,
ImagePullPolicy: resolved.K8sWorkerImagePullPolicy,
ServiceAccount: resolved.K8sWorkerServiceAccount,
MaxWorkers: resolved.K8sMaxWorkers,
SharedWarmTarget: resolved.K8sSharedWarmTarget,
WorkerCPURequest: resolved.K8sWorkerCPURequest,
WorkerMemoryRequest: resolved.K8sWorkerMemoryRequest,
WorkerNodeSelector: resolved.K8sWorkerNodeSelector,
WorkerTolerationKey: resolved.K8sWorkerTolerationKey,
WorkerTolerationValue: resolved.K8sWorkerTolerationValue,
WorkerExclusiveNode: resolved.K8sWorkerExclusiveNode,
AWSRegion: resolved.AWSRegion,
WorkerImage: resolved.K8sWorkerImage,
WorkerNamespace: resolved.K8sWorkerNamespace,
ControlPlaneID: resolved.K8sControlPlaneID,
WorkerPort: resolved.K8sWorkerPort,
WorkerSecret: resolved.K8sWorkerSecret,
WorkerConfigMap: resolved.K8sWorkerConfigMap,
ImagePullPolicy: resolved.K8sWorkerImagePullPolicy,
ServiceAccount: resolved.K8sWorkerServiceAccount,
MaxWorkers: resolved.K8sMaxWorkers,
SharedWarmTarget: resolved.K8sSharedWarmTarget,
DynamicWarmCapacityEnabled: resolved.K8sDynamicWarmCapacityEnabled,
WarmCapacityMissWindow: resolved.K8sWarmCapacityMissWindow,
WarmCapacityMissesPerWorker: resolved.K8sWarmCapacityMissesPerWorker,
WarmCapacityDemandTTL: resolved.K8sWarmCapacityDemandTTL,
WarmCapacityDynamicImageCeiling: resolved.K8sWarmCapacityDynamicImageCeiling,
WarmCapacityDynamicTotalCeiling: resolved.K8sWarmCapacityDynamicTotalCeiling,
WorkerCPURequest: resolved.K8sWorkerCPURequest,
WorkerMemoryRequest: resolved.K8sWorkerMemoryRequest,
WorkerNodeSelector: resolved.K8sWorkerNodeSelector,
WorkerTolerationKey: resolved.K8sWorkerTolerationKey,
WorkerTolerationValue: resolved.K8sWorkerTolerationValue,
WorkerExclusiveNode: resolved.K8sWorkerExclusiveNode,
AWSRegion: resolved.AWSRegion,
},
}
controlplane.RunControlPlane(cpCfg)
Expand Down
26 changes: 16 additions & 10 deletions configloader/file_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,22 @@ type ProcessFileConfig struct {

// K8sFileConfig holds Kubernetes worker configuration from YAML.
type K8sFileConfig struct {
WorkerImage string `yaml:"worker_image"`
WorkerNamespace string `yaml:"worker_namespace"`
ControlPlaneID string `yaml:"control_plane_id"`
WorkerPort int `yaml:"worker_port"`
WorkerSecret string `yaml:"worker_secret"`
WorkerConfigMap string `yaml:"worker_configmap"`
WorkerImagePullPolicy string `yaml:"worker_image_pull_policy"`
WorkerServiceAccount string `yaml:"worker_service_account"`
MaxWorkers int `yaml:"max_workers"`
SharedWarmTarget int `yaml:"shared_warm_target"`
WorkerImage string `yaml:"worker_image"`
WorkerNamespace string `yaml:"worker_namespace"`
ControlPlaneID string `yaml:"control_plane_id"`
WorkerPort int `yaml:"worker_port"`
WorkerSecret string `yaml:"worker_secret"`
WorkerConfigMap string `yaml:"worker_configmap"`
WorkerImagePullPolicy string `yaml:"worker_image_pull_policy"`
WorkerServiceAccount string `yaml:"worker_service_account"`
MaxWorkers int `yaml:"max_workers"`
SharedWarmTarget int `yaml:"shared_warm_target"`
DynamicWarmCapacityEnabled *bool `yaml:"dynamic_warm_capacity_enabled"`
WarmCapacityMissWindow string `yaml:"warm_capacity_miss_window"`
WarmCapacityMissesPerWorker int `yaml:"warm_capacity_misses_per_worker"`
WarmCapacityDemandTTL string `yaml:"warm_capacity_demand_ttl"`
WarmCapacityDynamicImageCeiling int `yaml:"warm_capacity_dynamic_image_ceiling"`
WarmCapacityDynamicTotalCeiling int `yaml:"warm_capacity_dynamic_total_ceiling"`
}

type QueryLogFileConfig struct {
Expand Down
2 changes: 1 addition & 1 deletion configresolve/cliflags.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func RegisterCLIInputsFlags(fs *flag.FlagSet) func() CLIInputs {
k8sWorkerConfigMap := fs.String("k8s-worker-configmap", "", "ConfigMap name for worker duckgres.yaml (env: DUCKGRES_K8S_WORKER_CONFIGMAP)")
k8sWorkerImagePullPolicy := fs.String("k8s-worker-image-pull-policy", "", "Image pull policy for K8s worker pods: Always, IfNotPresent, Never (env: DUCKGRES_K8S_WORKER_IMAGE_PULL_POLICY)")
k8sWorkerServiceAccount := fs.String("k8s-worker-service-account", "", "Neutral ServiceAccount name for K8s worker pods (default: duckgres-worker) (env: DUCKGRES_K8S_WORKER_SERVICE_ACCOUNT)")
k8sMaxWorkers := fs.Int("k8s-max-workers", 0, "Max K8s workers in the shared pool, 0=auto-derived (env: DUCKGRES_K8S_MAX_WORKERS)")
k8sMaxWorkers := fs.Int("k8s-max-workers", 0, "Max K8s workers in the shared pool, 0=unbounded (env: DUCKGRES_K8S_MAX_WORKERS)")
k8sSharedWarmTarget := fs.Int("k8s-shared-warm-target", 0, "Neutral shared warm-worker target for K8s multi-tenant mode, 0=disabled (env: DUCKGRES_K8S_SHARED_WARM_TARGET)")
awsRegion := fs.String("aws-region", "", "AWS region for STS client (env: DUCKGRES_AWS_REGION)")
queryLog := fs.Bool("query-log", true, "Enable/disable DuckLake query log (use --query-log=false to disable; env: DUCKGRES_QUERY_LOG_ENABLED)")
Expand Down
14 changes: 6 additions & 8 deletions configresolve/cliflags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@ import (
"testing"
)

// envOnlyCLIInputsFields lists CLIInputs struct fields that are intentionally
// not registered as CLI flags — they are populated from env vars only by
// design. CLAUDE.md ("K8s pod scheduling knobs are env-only") owns this list.
// If you add a flag for one of these, remove it from the set; if you add a
// new env-only field to CLIInputs, add it here.
var envOnlyCLIInputsFields = map[string]bool{
// nonFlagCLIInputsFields lists CLIInputs struct fields that are intentionally
// not registered as CLI flags. The K8s pod scheduling knobs are env-only by
// design.
var nonFlagCLIInputsFields = map[string]bool{
"K8sWorkerCPURequest": true,
"K8sWorkerMemoryRequest": true,
"K8sWorkerNodeSelector": true,
Expand Down Expand Up @@ -108,14 +106,14 @@ func TestRegisterCLIInputsFlagsCoversEveryCLIBackedField(t *testing.T) {
registered[f.Name] = true
})

// Walk CLIInputs; every non-Set, non-env-only field must be registered.
// Walk CLIInputs; every non-Set, CLI-backed field must be registered.
cliType := reflect.TypeOf(CLIInputs{})
for i := 0; i < cliType.NumField(); i++ {
field := cliType.Field(i)
if field.Name == "Set" {
continue
}
if envOnlyCLIInputsFields[field.Name] {
if nonFlagCLIInputsFields[field.Name] {
continue
}
flagName := fieldNameToFlagName(field.Name)
Expand Down
Loading
Loading