framework.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. package framework
  2. import (
  3. "bytes"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "regexp"
  8. "strings"
  9. "text/template"
  10. "github.com/fatedier/frp/test/e2e/mock/server"
  11. "github.com/fatedier/frp/test/e2e/pkg/port"
  12. "github.com/fatedier/frp/test/e2e/pkg/process"
  13. "github.com/onsi/ginkgo"
  14. "github.com/onsi/ginkgo/config"
  15. )
  16. type Options struct {
  17. TotalParallelNode int
  18. CurrentNodeIndex int
  19. FromPortIndex int
  20. ToPortIndex int
  21. }
  22. type Framework struct {
  23. TempDirectory string
  24. // ports used in this framework indexed by port name.
  25. usedPorts map[string]int
  26. // record ports alloced by this framework and release them after each test
  27. allocedPorts []int
  28. // portAllocator to alloc port for this test case.
  29. portAllocator *port.Allocator
  30. // Multiple default mock servers used for e2e testing.
  31. mockServers *MockServers
  32. // To make sure that this framework cleans up after itself, no matter what,
  33. // we install a Cleanup action before each test and clear it after. If we
  34. // should abort, the AfterSuite hook should run all Cleanup actions.
  35. cleanupHandle CleanupActionHandle
  36. // beforeEachStarted indicates that BeforeEach has started
  37. beforeEachStarted bool
  38. serverConfPaths []string
  39. serverProcesses []*process.Process
  40. clientConfPaths []string
  41. clientProcesses []*process.Process
  42. // Manual registered mock servers.
  43. servers []server.Server
  44. // used to generate unique config file name.
  45. configFileIndex int64
  46. // envs used to start processes, the form is `key=value`.
  47. osEnvs []string
  48. }
  49. func NewDefaultFramework() *Framework {
  50. options := Options{
  51. TotalParallelNode: config.GinkgoConfig.ParallelTotal,
  52. CurrentNodeIndex: config.GinkgoConfig.ParallelNode,
  53. FromPortIndex: 20000,
  54. ToPortIndex: 50000,
  55. }
  56. return NewFramework(options)
  57. }
  58. func NewFramework(opt Options) *Framework {
  59. f := &Framework{
  60. portAllocator: port.NewAllocator(opt.FromPortIndex, opt.ToPortIndex, opt.TotalParallelNode, opt.CurrentNodeIndex-1),
  61. usedPorts: make(map[string]int),
  62. }
  63. ginkgo.BeforeEach(f.BeforeEach)
  64. ginkgo.AfterEach(f.AfterEach)
  65. return f
  66. }
  67. // BeforeEach create a temp directory.
  68. func (f *Framework) BeforeEach() {
  69. f.beforeEachStarted = true
  70. f.cleanupHandle = AddCleanupAction(f.AfterEach)
  71. dir, err := os.MkdirTemp(os.TempDir(), "frp-e2e-test-*")
  72. ExpectNoError(err)
  73. f.TempDirectory = dir
  74. f.mockServers = NewMockServers(f.portAllocator)
  75. if err := f.mockServers.Run(); err != nil {
  76. Failf("%v", err)
  77. }
  78. params := f.mockServers.GetTemplateParams()
  79. for k, v := range params {
  80. switch t := v.(type) {
  81. case int:
  82. f.usedPorts[k] = int(t)
  83. }
  84. }
  85. }
  86. func (f *Framework) AfterEach() {
  87. if !f.beforeEachStarted {
  88. return
  89. }
  90. RemoveCleanupAction(f.cleanupHandle)
  91. // stop processor
  92. for _, p := range f.serverProcesses {
  93. p.Stop()
  94. if TestContext.Debug {
  95. fmt.Println(p.ErrorOutput())
  96. fmt.Println(p.StdOutput())
  97. }
  98. }
  99. for _, p := range f.clientProcesses {
  100. p.Stop()
  101. if TestContext.Debug {
  102. fmt.Println(p.ErrorOutput())
  103. fmt.Println(p.StdOutput())
  104. }
  105. }
  106. f.serverProcesses = nil
  107. f.clientProcesses = nil
  108. // close default mock servers
  109. f.mockServers.Close()
  110. // close manual registered mock servers
  111. for _, s := range f.servers {
  112. s.Close()
  113. }
  114. // clean directory
  115. os.RemoveAll(f.TempDirectory)
  116. f.TempDirectory = ""
  117. f.serverConfPaths = []string{}
  118. f.clientConfPaths = []string{}
  119. // release used ports
  120. for _, port := range f.usedPorts {
  121. f.portAllocator.Release(port)
  122. }
  123. f.usedPorts = make(map[string]int)
  124. // release alloced ports
  125. for _, port := range f.allocedPorts {
  126. f.portAllocator.Release(port)
  127. }
  128. f.allocedPorts = make([]int, 0)
  129. // clear os envs
  130. f.osEnvs = make([]string, 0)
  131. }
  132. var portRegex = regexp.MustCompile(`{{ \.Port.*? }}`)
  133. // RenderPortsTemplate render templates with ports.
  134. //
  135. // Local: {{ .Port1 }}
  136. // Target: {{ .Port2 }}
  137. //
  138. // return rendered content and all allocated ports.
  139. func (f *Framework) genPortsFromTemplates(templates []string) (ports map[string]int, err error) {
  140. ports = make(map[string]int)
  141. for _, t := range templates {
  142. arrs := portRegex.FindAllString(t, -1)
  143. for _, str := range arrs {
  144. str = strings.TrimPrefix(str, "{{ .")
  145. str = strings.TrimSuffix(str, " }}")
  146. str = strings.TrimSpace(str)
  147. ports[str] = 0
  148. }
  149. }
  150. defer func() {
  151. if err != nil {
  152. for _, port := range ports {
  153. f.portAllocator.Release(port)
  154. }
  155. }
  156. }()
  157. for name := range ports {
  158. port := f.portAllocator.GetByName(name)
  159. if port <= 0 {
  160. return nil, fmt.Errorf("can't allocate port")
  161. }
  162. ports[name] = port
  163. }
  164. return
  165. }
  166. // RenderTemplates alloc all ports for port names placeholder.
  167. func (f *Framework) RenderTemplates(templates []string) (outs []string, ports map[string]int, err error) {
  168. ports, err = f.genPortsFromTemplates(templates)
  169. if err != nil {
  170. return
  171. }
  172. params := f.mockServers.GetTemplateParams()
  173. for name, port := range ports {
  174. params[name] = port
  175. }
  176. for name, port := range f.usedPorts {
  177. params[name] = port
  178. }
  179. for _, t := range templates {
  180. tmpl, err := template.New("").Parse(t)
  181. if err != nil {
  182. return nil, nil, err
  183. }
  184. buffer := bytes.NewBuffer(nil)
  185. if err = tmpl.Execute(buffer, params); err != nil {
  186. return nil, nil, err
  187. }
  188. outs = append(outs, buffer.String())
  189. }
  190. return
  191. }
  192. func (f *Framework) PortByName(name string) int {
  193. return f.usedPorts[name]
  194. }
  195. func (f *Framework) AllocPort() int {
  196. port := f.portAllocator.Get()
  197. ExpectTrue(port > 0, "alloc port failed")
  198. f.allocedPorts = append(f.allocedPorts, port)
  199. return port
  200. }
  201. func (f *Framework) ReleasePort(port int) {
  202. f.portAllocator.Release(port)
  203. }
  204. func (f *Framework) RunServer(portName string, s server.Server) {
  205. f.servers = append(f.servers, s)
  206. if s.BindPort() > 0 && portName != "" {
  207. f.usedPorts[portName] = s.BindPort()
  208. }
  209. err := s.Run()
  210. ExpectNoError(err, "RunServer: with PortName %s", portName)
  211. }
  212. func (f *Framework) SetEnvs(envs []string) {
  213. f.osEnvs = envs
  214. }
  215. func (f *Framework) WriteTempFile(name string, content string) string {
  216. filePath := filepath.Join(f.TempDirectory, name)
  217. err := os.WriteFile(filePath, []byte(content), 0766)
  218. ExpectNoError(err)
  219. return filePath
  220. }