docker_test.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. package main
  2. import (
  3. "context"
  4. "os"
  5. "path/filepath"
  6. "testing"
  7. "time"
  8. C "github.com/sagernet/sing-box/constant"
  9. "github.com/sagernet/sing/common/debug"
  10. F "github.com/sagernet/sing/common/format"
  11. "github.com/sagernet/sing/common/rw"
  12. "github.com/docker/docker/api/types"
  13. "github.com/docker/docker/api/types/container"
  14. "github.com/docker/docker/client"
  15. "github.com/docker/docker/pkg/stdcopy"
  16. "github.com/docker/go-connections/nat"
  17. "github.com/stretchr/testify/require"
  18. )
  19. type DockerOptions struct {
  20. Image string
  21. EntryPoint string
  22. Ports []uint16
  23. Cmd []string
  24. Env []string
  25. Bind map[string]string
  26. Stdin []byte
  27. Cap []string
  28. }
  29. func startDockerContainer(t *testing.T, options DockerOptions) {
  30. dockerClient, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
  31. require.NoError(t, err)
  32. defer dockerClient.Close()
  33. writeStdin := len(options.Stdin) > 0
  34. var containerOptions container.Config
  35. if writeStdin {
  36. containerOptions.OpenStdin = true
  37. containerOptions.StdinOnce = true
  38. }
  39. containerOptions.Image = options.Image
  40. if options.EntryPoint != "" {
  41. containerOptions.Entrypoint = []string{options.EntryPoint}
  42. }
  43. containerOptions.Cmd = options.Cmd
  44. containerOptions.Env = options.Env
  45. containerOptions.ExposedPorts = make(nat.PortSet)
  46. var hostOptions container.HostConfig
  47. if !C.IsDarwin {
  48. hostOptions.NetworkMode = "host"
  49. }
  50. hostOptions.CapAdd = options.Cap
  51. hostOptions.PortBindings = make(nat.PortMap)
  52. for _, port := range options.Ports {
  53. containerOptions.ExposedPorts[nat.Port(F.ToString(port, "/tcp"))] = struct{}{}
  54. containerOptions.ExposedPorts[nat.Port(F.ToString(port, "/udp"))] = struct{}{}
  55. hostOptions.PortBindings[nat.Port(F.ToString(port, "/tcp"))] = []nat.PortBinding{
  56. {HostPort: F.ToString(port), HostIP: "0.0.0.0"},
  57. }
  58. hostOptions.PortBindings[nat.Port(F.ToString(port, "/udp"))] = []nat.PortBinding{
  59. {HostPort: F.ToString(port), HostIP: "0.0.0.0"},
  60. }
  61. }
  62. if len(options.Bind) > 0 {
  63. hostOptions.Binds = []string{}
  64. for path, internalPath := range options.Bind {
  65. if !rw.FileExists(path) {
  66. path = filepath.Join("config", path)
  67. }
  68. path, _ = filepath.Abs(path)
  69. hostOptions.Binds = append(hostOptions.Binds, path+":"+internalPath)
  70. }
  71. }
  72. dockerContainer, err := dockerClient.ContainerCreate(context.Background(), &containerOptions, &hostOptions, nil, nil, "")
  73. require.NoError(t, err)
  74. t.Cleanup(func() {
  75. cleanContainer(dockerContainer.ID)
  76. })
  77. require.NoError(t, dockerClient.ContainerStart(context.Background(), dockerContainer.ID, types.ContainerStartOptions{}))
  78. if writeStdin {
  79. stdinAttach, err := dockerClient.ContainerAttach(context.Background(), dockerContainer.ID, types.ContainerAttachOptions{
  80. Stdin: writeStdin,
  81. Stream: true,
  82. })
  83. require.NoError(t, err)
  84. _, err = stdinAttach.Conn.Write(options.Stdin)
  85. require.NoError(t, err)
  86. stdinAttach.Close()
  87. }
  88. if debug.Enabled {
  89. attach, err := dockerClient.ContainerAttach(context.Background(), dockerContainer.ID, types.ContainerAttachOptions{
  90. Stdout: true,
  91. Stderr: true,
  92. Logs: true,
  93. Stream: true,
  94. })
  95. require.NoError(t, err)
  96. go func() {
  97. stdcopy.StdCopy(os.Stderr, os.Stderr, attach.Reader)
  98. }()
  99. }
  100. time.Sleep(time.Second)
  101. }
  102. func cleanContainer(id string) error {
  103. dockerClient, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
  104. if err != nil {
  105. return err
  106. }
  107. defer dockerClient.Close()
  108. return dockerClient.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{Force: true})
  109. }