fix(provisioner): seed configs before container start
Some checks failed
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 15s
E2E Staging SaaS (full lifecycle) / E2E Staging SaaS (pull_request) Has been skipped
Harness Replays / detect-changes (pull_request) Successful in 19s
CI / Detect changes (pull_request) Successful in 47s
E2E Staging SaaS (full lifecycle) / pr-validate (pull_request) Successful in 43s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 20s
E2E API Smoke Test / detect-changes (pull_request) Successful in 55s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 56s
qa-review / approved (pull_request) Failing after 28s
sop-checklist / na-declarations (pull_request) awaiting /sop-n/a declaration for: qa-review, security-review
Handlers Postgres Integration / detect-changes (pull_request) Successful in 1m1s
sop-checklist / all-items-acked (pull_request) Successful in 23s
security-review / approved (pull_request) Failing after 26s
gate-check-v3 / gate-check (pull_request) Failing after 41s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 59s
sop-tier-check / tier-check (pull_request) Successful in 15s
Harness Replays / Harness Replays (pull_request) Successful in 5s
CI / Canvas (Next.js) (pull_request) Successful in 7s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 5s
CI / Python Lint & Test (pull_request) Successful in 7s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m17s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 9s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 7s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 1m40s
E2E Staging External Runtime / E2E Staging External Runtime (pull_request) Successful in 5m26s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 4m13s
CI / Platform (Go) (pull_request) Failing after 7m52s
CI / all-required (pull_request) Successful in 5s

This commit is contained in:
hongming-codex-laptop 2026-05-14 09:23:33 -07:00
parent b5308e3dbe
commit 927281ae3e
2 changed files with 34 additions and 14 deletions

View File

@ -481,6 +481,22 @@ func (p *Provisioner) Start(ctx context.Context, cfg WorkspaceConfig) (string, e
return "", fmt.Errorf("failed to create container: %w", err)
}
// Seed /configs before the entrypoint starts. molecule-runtime reads
// /configs/config.yaml immediately; post-start copy races fast runtimes
// into a FileNotFoundError crash loop.
if cfg.TemplatePath != "" {
if err := p.CopyTemplateToContainer(ctx, resp.ID, cfg.TemplatePath); err != nil {
_ = p.cli.ContainerRemove(ctx, resp.ID, container.RemoveOptions{Force: true})
return "", fmt.Errorf("failed to copy template to container %s before start: %w", name, err)
}
}
if len(cfg.ConfigFiles) > 0 {
if err := p.WriteFilesToContainer(ctx, resp.ID, cfg.ConfigFiles); err != nil {
_ = p.cli.ContainerRemove(ctx, resp.ID, container.RemoveOptions{Force: true})
return "", fmt.Errorf("failed to write config files to container %s before start: %w", name, err)
}
}
if err := p.cli.ContainerStart(ctx, resp.ID, container.StartOptions{}); err != nil {
// Clean up created container on start failure
_ = p.cli.ContainerRemove(ctx, resp.ID, container.RemoveOptions{Force: true})
@ -496,20 +512,6 @@ func (p *Provisioner) Start(ctx context.Context, cfg WorkspaceConfig) (string, e
// /configs and /workspace, then drops to agent via gosu). No per-start
// chown needed here.
// Copy template files into /configs if TemplatePath is set
if cfg.TemplatePath != "" {
if err := p.CopyTemplateToContainer(ctx, resp.ID, cfg.TemplatePath); err != nil {
log.Printf("Provisioner: warning — failed to copy template to container %s: %v", name, err)
}
}
// Write generated config files into /configs if ConfigFiles is set
if len(cfg.ConfigFiles) > 0 {
if err := p.WriteFilesToContainer(ctx, resp.ID, cfg.ConfigFiles); err != nil {
log.Printf("Provisioner: warning — failed to write config files to container %s: %v", name, err)
}
}
// Resolve the host-mapped port. Retry inspect up to 3 times if Docker hasn't
// bound the ephemeral port yet (rare race under heavy load).
hostURL := InternalURL(cfg.WorkspaceID) // fallback to Docker-internal

View File

@ -62,6 +62,24 @@ func TestValidateConfigSource_TemplateIsDirName(t *testing.T) {
}
}
func TestStartSeedsConfigsBeforeContainerStart(t *testing.T) {
src, err := os.ReadFile("provisioner.go")
if err != nil {
t.Fatalf("read provisioner.go: %v", err)
}
text := string(src)
copyTemplate := strings.Index(text, "p.CopyTemplateToContainer(ctx, resp.ID, cfg.TemplatePath)")
writeFiles := strings.Index(text, "p.WriteFilesToContainer(ctx, resp.ID, cfg.ConfigFiles)")
start := strings.Index(text, "p.cli.ContainerStart(ctx, resp.ID, container.StartOptions{})")
if copyTemplate < 0 || writeFiles < 0 || start < 0 {
t.Fatalf("expected Start to copy template, write config files, and start container")
}
if !(copyTemplate < start && writeFiles < start) {
t.Fatalf("config seeding must happen before ContainerStart: copyTemplate=%d writeFiles=%d start=%d", copyTemplate, writeFiles, start)
}
}
// baseHostConfig returns a fresh HostConfig with typical pre-tier binds,
// mimicking what Start() builds before calling ApplyTierConfig.
func baseHostConfig(pluginsPath string) *container.HostConfig {