docker_test.go 3.4 KB

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