WithStartupCommand silently swallows non-zero exit codes from the inner command

Đang mở Phù hợp với người mới
#155 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

@locker95 đang làm issue này rồi.

Từ ngày 28/10/2026.

  • #174 của @locker95 — đang mở
  • #175 của @kw-00 — đang mở

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
76/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Ít trao đổi
Công nghệ
docker, go

Hướng nghiên cứu

Bắt đầu trong container/options.go tại createExecutableHooks, sau đó kiểm tra Container.Exec trong container/container.exec.go để xác nhận cách mã thoát và lỗi được trả về. Thay đổi được hoàn tất khi các mã khác không từ WithStartupCommand và WithAfterReadyCommand được báo cáo là lỗi, trong khi các mã không và lỗi thực thi vẫn giữ nguyên hành vi như mong đợi.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

Summary

container.WithStartupCommand registers a PostStarts lifecycle hook that runs each Executable via Container.Exec. When the inner command exits non-zero, the hook returns nil and container.Run reports success. The container ends up in a "running but the startup command failed" state with no visible signal to the caller.

Where

  • The hook is built in container/options.go (createExecutableHooks):

    execFn := func(ctx context.Context, c ContainerInfo) error {
        if executor, ok := c.(ContainerExecutor); ok {
            _, _, err := executor.Exec(ctx, exec.AsCommand(), exec.Options()...)
            return err
        }
        return errors.New("container does not support execution")
    }
    
  • Container.Exec in container/container.exec.go returns (int, io.Reader, error). The first return is the inner process's exit code — a value, not an error. The hook discards it.

Reproduction

c, err := container.Run(ctx,
    container.WithImage("alpine:latest"),
    container.WithEntrypoint("tail", "-f", "/dev/null"),
    container.WithStartupCommand(exec.NewRawCommand([]string{"false"})),
)
// err == nil; c is up; the failing startup command is invisible.

This bites consumers in two ways:

  1. First-create coverage doesn't fail loudly. A startup command that errors is silently ignored at create time. The caller has no way to discover the failure short of checking side effects.
  2. The same trap applies to WithAfterReadyCommand (same hook factory).

I hit this while writing tests for WithDurableStartupCommand (#154): a WithUser("nonexistent") case where su aborts, the rendered dispatcher exits non-zero, but Run still succeeds. I had to invoke the dispatcher directly via c.Exec and check the exit code value to observe the failure.

Suggested fix

Have createExecutableHooks (or a thin helper) treat a non-zero exit code as an error:

execFn := func(ctx context.Context, c ContainerInfo) error {
    executor, ok := c.(ContainerExecutor)
    if !ok {
        return errors.New("container does not support execution")
    }
    code, _, err := executor.Exec(ctx, exec.AsCommand(), exec.Options()...)
    if err != nil {
        return err
    }
    if code != 0 {
        return fmt.Errorf("startup command %v exited with code %d", exec.AsCommand(), code)
    }
    return nil
}

This is a behavioural change for any caller that today relies on startup-command failures being invisible — but I'd expect that population to be small, and the silent-failure mode is more dangerous than the loud one.

If a strictly backwards-compatible variant is preferred, an opt-in option (WithStrictStartupCommand?) could keep the current behaviour as the default.

Out of scope (but related)

  • Container.Exec's signature returning (int, io.Reader, error) makes the exit code easy to ignore. A future API revision could fold non-zero into the error return; for now, fixing the hook is the smaller change.
Ngôn ngữ chính
Go
Star
133
Fork
28
Merge trung bình
3 giờ 55 phút
Pull request đã merge (30 ngày)
4

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của docker/go-sdk

Tất cả issue của docker/go-sdk

Issue tương tự

Thêm issue về Go

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.