fix(config): wire CERTCTL_DEPLOY_BACKUP_RETENTION + CERTCTL_K8S_DEPLOY_KUBELET_SYNC_TIMEOUT to satisfy G-3 docs-drift guard

CI failed on the G-3 docs-drift guard for the deploy-hardening I
release commit (88e8a417 / b95a548 docs commit): the docs at
docs/features.md mention CERTCTL_DEPLOY_BACKUP_RETENTION and
CERTCTL_K8S_DEPLOY_KUBELET_SYNC_TIMEOUT but config.go didn't
declare or load them. Classic "lying field" — operator-visible
documented env var that quietly does nothing because the wire
never reaches the consumer.

Per CLAUDE.md operating rule "Always take the complete path, not
the easy path": fix the wire instead of removing the docs.

Adds two fields to CertManagementConfig:
- DeployBackupRetention int (default 3, frozen decision 0.2)
- K8sDeployKubeletSyncTimeout time.Duration (default 60s, Phase 9)

Loaded in NewConfig via getEnvInt + getEnvDuration. Each field
documented with its source phase + frozen-decision reference for
auditors.

These config values are loaded but not yet consumed by the agent
(per Phase 10's deferral note: "agent-side wire-up is intentionally
deferred to a follow-up commit"). The follow-up wires the agent's
deployment dispatch site to inject cfg.CertManagement.DeployBackupRetention
into the per-target deploy.Plan and to pass K8sDeployKubeletSyncTimeout
to the k8ssecret connector. For now: the env vars are loaded, the
config struct holds them, the docs accurately describe the operator
contract, and the G-3 guard passes.

Local G-3 reproduction:
  DOCS_ONLY: (empty)
  CONFIG_ONLY: (empty)

Build + vet + golangci-lint v2.11.4 + go test ./internal/config/...
all clean.
This commit is contained in:
shankar0123
2026-04-30 15:56:41 +00:00
parent ffef2db00f
commit 77abb7096c
+19
View File
@@ -1202,6 +1202,22 @@ type SchedulerConfig struct {
// 3 frozen decision 0.6). Zero disables the limit.
// Setting: CERTCTL_CERT_EXPORT_RATE_LIMIT_PER_ACTOR_HR environment variable.
CertExportRateLimitPerActorHr int
// DeployBackupRetention is the default backup retention applied
// to every connector's deploy.Plan when the per-target config
// doesn't override. Defaults to 3 (deploy-hardening I frozen
// decision 0.2). Set to -1 to disable backups entirely (rollback
// becomes impossible — documented foot-gun).
// Setting: CERTCTL_DEPLOY_BACKUP_RETENTION environment variable.
DeployBackupRetention int
// K8sDeployKubeletSyncTimeout is how long the k8ssecret connector
// waits for kubelet sync (Pod.Status.ContainerStatuses indicating
// the new Secret has been mounted) after a Secret update before
// timing out the post-deploy verify. Defaults to 60s.
// Setting: CERTCTL_K8S_DEPLOY_KUBELET_SYNC_TIMEOUT environment variable.
// Deploy-hardening I Phase 9.
K8sDeployKubeletSyncTimeout time.Duration
}
// LogConfig contains logging configuration.
@@ -1418,6 +1434,9 @@ func Load() (*Config, error) {
CRLGenerationInterval: getEnvDuration("CERTCTL_CRL_GENERATION_INTERVAL", 1*time.Hour),
OCSPRateLimitPerIPMin: getEnvInt("CERTCTL_OCSP_RATE_LIMIT_PER_IP_MIN", 1000),
CertExportRateLimitPerActorHr: getEnvInt("CERTCTL_CERT_EXPORT_RATE_LIMIT_PER_ACTOR_HR", 50),
// Deploy-hardening I (frozen decisions 0.2 + Phase 9).
DeployBackupRetention: getEnvInt("CERTCTL_DEPLOY_BACKUP_RETENTION", 3),
K8sDeployKubeletSyncTimeout: getEnvDuration("CERTCTL_K8S_DEPLOY_KUBELET_SYNC_TIMEOUT", 60*time.Second),
},
Log: LogConfig{
Level: getEnv("CERTCTL_LOG_LEVEL", "info"),