docker_test.go 3.4 KB

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