/provider/provider_test.go

https://github.com/EmileVauge/traefik · Go · 399 lines · 373 code · 21 blank · 5 comment · 66 complexity · 9cc363dd57b7ad4c0d14bef0beb06274 MD5 · raw file

  1. package provider
  2. import (
  3. "io/ioutil"
  4. "os"
  5. "strings"
  6. "testing"
  7. "text/template"
  8. "github.com/containous/traefik/types"
  9. )
  10. type myProvider struct {
  11. BaseProvider
  12. TLS *ClientTLS
  13. }
  14. func (p *myProvider) Foo() string {
  15. return "bar"
  16. }
  17. func TestConfigurationErrors(t *testing.T) {
  18. templateErrorFile, err := ioutil.TempFile("", "provider-configuration-error")
  19. if err != nil {
  20. t.Fatal(err)
  21. }
  22. defer os.RemoveAll(templateErrorFile.Name())
  23. data := []byte("Not a valid template {{ Bar }}")
  24. err = ioutil.WriteFile(templateErrorFile.Name(), data, 0700)
  25. if err != nil {
  26. t.Fatal(err)
  27. }
  28. templateInvalidTOMLFile, err := ioutil.TempFile("", "provider-configuration-error")
  29. if err != nil {
  30. t.Fatal(err)
  31. }
  32. defer os.RemoveAll(templateInvalidTOMLFile.Name())
  33. data = []byte(`Hello {{ .Name }}
  34. {{ Foo }}`)
  35. err = ioutil.WriteFile(templateInvalidTOMLFile.Name(), data, 0700)
  36. if err != nil {
  37. t.Fatal(err)
  38. }
  39. invalids := []struct {
  40. provider *myProvider
  41. defaultTemplate string
  42. expectedError string
  43. funcMap template.FuncMap
  44. templateObjects interface{}
  45. }{
  46. {
  47. provider: &myProvider{
  48. BaseProvider{
  49. Filename: "/non/existent/template.tmpl",
  50. },
  51. nil,
  52. },
  53. expectedError: "open /non/existent/template.tmpl: no such file or directory",
  54. },
  55. {
  56. provider: &myProvider{},
  57. defaultTemplate: "non/existent/template.tmpl",
  58. expectedError: "Asset non/existent/template.tmpl not found",
  59. },
  60. {
  61. provider: &myProvider{
  62. BaseProvider{
  63. Filename: templateErrorFile.Name(),
  64. },
  65. nil,
  66. },
  67. expectedError: `function "Bar" not defined`,
  68. },
  69. {
  70. provider: &myProvider{
  71. BaseProvider{
  72. Filename: templateInvalidTOMLFile.Name(),
  73. },
  74. nil,
  75. },
  76. expectedError: "Near line 1 (last key parsed 'Hello'): expected key separator '=', but got '<' instead",
  77. funcMap: template.FuncMap{
  78. "Foo": func() string {
  79. return "bar"
  80. },
  81. },
  82. templateObjects: struct{ Name string }{Name: "bar"},
  83. },
  84. }
  85. for _, invalid := range invalids {
  86. configuration, err := invalid.provider.GetConfiguration(invalid.defaultTemplate, invalid.funcMap, nil)
  87. if err == nil || !strings.Contains(err.Error(), invalid.expectedError) {
  88. t.Fatalf("should have generate an error with %q, got %v", invalid.expectedError, err)
  89. }
  90. if configuration != nil {
  91. t.Fatalf("shouldn't have return a configuration object : %v", configuration)
  92. }
  93. }
  94. }
  95. func TestGetConfiguration(t *testing.T) {
  96. templateFile, err := ioutil.TempFile("", "provider-configuration")
  97. if err != nil {
  98. t.Fatal(err)
  99. }
  100. defer os.RemoveAll(templateFile.Name())
  101. data := []byte(`[backends]
  102. [backends.backend1]
  103. [backends.backend1.circuitbreaker]
  104. expression = "NetworkErrorRatio() > 0.5"
  105. [backends.backend1.servers.server1]
  106. url = "http://172.17.0.2:80"
  107. weight = 10
  108. [backends.backend1.servers.server2]
  109. url = "http://172.17.0.3:80"
  110. weight = 1
  111. [frontends]
  112. [frontends.frontend1]
  113. backend = "backend1"
  114. passHostHeader = true
  115. [frontends.frontend11.routes.test_2]
  116. rule = "Path"
  117. value = "/test"`)
  118. err = ioutil.WriteFile(templateFile.Name(), data, 0700)
  119. if err != nil {
  120. t.Fatal(err)
  121. }
  122. provider := &myProvider{
  123. BaseProvider{
  124. Filename: templateFile.Name(),
  125. },
  126. nil,
  127. }
  128. configuration, err := provider.GetConfiguration(templateFile.Name(), nil, nil)
  129. if err != nil {
  130. t.Fatalf("Shouldn't have error out, got %v", err)
  131. }
  132. if configuration == nil {
  133. t.Fatal("Configuration should not be nil, but was")
  134. }
  135. }
  136. func TestGetConfigurationReturnsCorrectMaxConnConfiguration(t *testing.T) {
  137. templateFile, err := ioutil.TempFile("", "provider-configuration")
  138. if err != nil {
  139. t.Fatal(err)
  140. }
  141. defer os.RemoveAll(templateFile.Name())
  142. data := []byte(`[backends]
  143. [backends.backend1]
  144. [backends.backend1.maxconn]
  145. amount = 10
  146. extractorFunc = "request.host"`)
  147. err = ioutil.WriteFile(templateFile.Name(), data, 0700)
  148. if err != nil {
  149. t.Fatal(err)
  150. }
  151. provider := &myProvider{
  152. BaseProvider{
  153. Filename: templateFile.Name(),
  154. },
  155. nil,
  156. }
  157. configuration, err := provider.GetConfiguration(templateFile.Name(), nil, nil)
  158. if err != nil {
  159. t.Fatalf("Shouldn't have error out, got %v", err)
  160. }
  161. if configuration == nil {
  162. t.Fatal("Configuration should not be nil, but was")
  163. }
  164. if configuration.Backends["backend1"].MaxConn.Amount != 10 {
  165. t.Fatal("Configuration did not parse MaxConn.Amount properly")
  166. }
  167. if configuration.Backends["backend1"].MaxConn.ExtractorFunc != "request.host" {
  168. t.Fatal("Configuration did not parse MaxConn.ExtractorFunc properly")
  169. }
  170. }
  171. func TestNilClientTLS(t *testing.T) {
  172. provider := &myProvider{
  173. BaseProvider{
  174. Filename: "",
  175. },
  176. nil,
  177. }
  178. _, err := provider.TLS.CreateTLSConfig()
  179. if err != nil {
  180. t.Fatal("CreateTLSConfig should assume that consumer does not want a TLS configuration if input is nil")
  181. }
  182. }
  183. func TestMatchingConstraints(t *testing.T) {
  184. cases := []struct {
  185. constraints types.Constraints
  186. tags []string
  187. expected bool
  188. }{
  189. // simple test: must match
  190. {
  191. constraints: types.Constraints{
  192. {
  193. Key: "tag",
  194. MustMatch: true,
  195. Regex: "us-east-1",
  196. },
  197. },
  198. tags: []string{
  199. "us-east-1",
  200. },
  201. expected: true,
  202. },
  203. // simple test: must match but does not match
  204. {
  205. constraints: types.Constraints{
  206. {
  207. Key: "tag",
  208. MustMatch: true,
  209. Regex: "us-east-1",
  210. },
  211. },
  212. tags: []string{
  213. "us-east-2",
  214. },
  215. expected: false,
  216. },
  217. // simple test: must not match
  218. {
  219. constraints: types.Constraints{
  220. {
  221. Key: "tag",
  222. MustMatch: false,
  223. Regex: "us-east-1",
  224. },
  225. },
  226. tags: []string{
  227. "us-east-1",
  228. },
  229. expected: false,
  230. },
  231. // complex test: globbing
  232. {
  233. constraints: types.Constraints{
  234. {
  235. Key: "tag",
  236. MustMatch: true,
  237. Regex: "us-east-*",
  238. },
  239. },
  240. tags: []string{
  241. "us-east-1",
  242. },
  243. expected: true,
  244. },
  245. // complex test: multiple constraints
  246. {
  247. constraints: types.Constraints{
  248. {
  249. Key: "tag",
  250. MustMatch: true,
  251. Regex: "us-east-*",
  252. },
  253. {
  254. Key: "tag",
  255. MustMatch: false,
  256. Regex: "api",
  257. },
  258. },
  259. tags: []string{
  260. "api",
  261. "us-east-1",
  262. },
  263. expected: false,
  264. },
  265. }
  266. for i, c := range cases {
  267. provider := myProvider{
  268. BaseProvider{
  269. Constraints: c.constraints,
  270. },
  271. nil,
  272. }
  273. actual, _ := provider.MatchConstraints(c.tags)
  274. if actual != c.expected {
  275. t.Fatalf("test #%v: expected %t, got %t, for %#v", i, c.expected, actual, c.constraints)
  276. }
  277. }
  278. }
  279. func TestDefaultFuncMap(t *testing.T) {
  280. templateFile, err := ioutil.TempFile("", "provider-configuration")
  281. if err != nil {
  282. t.Fatal(err)
  283. }
  284. defer os.RemoveAll(templateFile.Name())
  285. data := []byte(`
  286. [backends]
  287. [backends.{{ "backend-1" | replace "-" "" }}]
  288. [backends.{{ "BACKEND1" | tolower }}.circuitbreaker]
  289. expression = "NetworkErrorRatio() > 0.5"
  290. [backends.servers.server1]
  291. url = "http://172.17.0.2:80"
  292. weight = 10
  293. [backends.backend1.servers.server2]
  294. url = "http://172.17.0.3:80"
  295. weight = 1
  296. [frontends]
  297. [frontends.{{normalize "frontend/1"}}]
  298. {{ $backend := "backend1/test/value" | split "/" }}
  299. {{ $backendid := index $backend 1 }}
  300. {{ if "backend1" | contains "backend" }}
  301. backend = "backend1"
  302. {{end}}
  303. passHostHeader = true
  304. [frontends.frontend-1.routes.test_2]
  305. rule = "Path"
  306. value = "/test"`)
  307. err = ioutil.WriteFile(templateFile.Name(), data, 0700)
  308. if err != nil {
  309. t.Fatal(err)
  310. }
  311. provider := &myProvider{
  312. BaseProvider{
  313. Filename: templateFile.Name(),
  314. },
  315. nil,
  316. }
  317. configuration, err := provider.GetConfiguration(templateFile.Name(), nil, nil)
  318. if err != nil {
  319. t.Fatalf("Shouldn't have error out, got %v", err)
  320. }
  321. if configuration == nil {
  322. t.Fatal("Configuration should not be nil, but was")
  323. }
  324. if _, ok := configuration.Backends["backend1"]; !ok {
  325. t.Fatal("backend1 should exists, but it not")
  326. }
  327. if _, ok := configuration.Frontends["frontend-1"]; !ok {
  328. t.Fatal("Frontend frontend-1 should exists, but it not")
  329. }
  330. }
  331. func TestSprigFunctions(t *testing.T) {
  332. templateFile, err := ioutil.TempFile("", "provider-configuration")
  333. if err != nil {
  334. t.Fatal(err)
  335. }
  336. defer os.RemoveAll(templateFile.Name())
  337. data := []byte(`
  338. {{$backend_name := trimAll "-" uuidv4}}
  339. [backends]
  340. [backends.{{$backend_name}}]
  341. [backends.{{$backend_name}}.circuitbreaker]
  342. [backends.{{$backend_name}}.servers.server2]
  343. url = "http://172.17.0.3:80"
  344. weight = 1
  345. [frontends]
  346. [frontends.{{normalize "frontend/1"}}]
  347. backend = "{{$backend_name}}"
  348. passHostHeader = true
  349. [frontends.frontend-1.routes.test_2]
  350. rule = "Path"
  351. value = "/test"`)
  352. err = ioutil.WriteFile(templateFile.Name(), data, 0700)
  353. if err != nil {
  354. t.Fatal(err)
  355. }
  356. provider := &myProvider{
  357. BaseProvider{
  358. Filename: templateFile.Name(),
  359. },
  360. nil,
  361. }
  362. configuration, err := provider.GetConfiguration(templateFile.Name(), nil, nil)
  363. if err != nil {
  364. t.Fatalf("Shouldn't have error out, got %v", err)
  365. }
  366. if configuration == nil {
  367. t.Fatal("Configuration should not be nil, but was")
  368. }
  369. if len(configuration.Backends) != 1 {
  370. t.Fatal("one backend should be defined, but it's not")
  371. }
  372. if _, ok := configuration.Frontends["frontend-1"]; !ok {
  373. t.Fatal("Frontend frontend-1 should exists, but it not")
  374. }
  375. }