/acceptance/openstack/loadbalancer/v2/loadbalancer.go

https://github.com/gophercloud/gophercloud · Go · 546 lines · 382 code · 118 blank · 46 comment · 88 complexity · ef639a7c75e5fcd7c0d18919da325396 MD5 · raw file

  1. package v2
  2. import (
  3. "fmt"
  4. "strings"
  5. "testing"
  6. "github.com/gophercloud/gophercloud"
  7. "github.com/gophercloud/gophercloud/acceptance/tools"
  8. "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies"
  9. "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners"
  10. "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers"
  11. "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors"
  12. "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools"
  13. th "github.com/gophercloud/gophercloud/testhelper"
  14. )
  15. // CreateListener will create a listener for a given load balancer on a random
  16. // port with a random name. An error will be returned if the listener could not
  17. // be created.
  18. func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*listeners.Listener, error) {
  19. listenerName := tools.RandomString("TESTACCT-", 8)
  20. listenerDescription := tools.RandomString("TESTACCT-DESC-", 8)
  21. listenerPort := tools.RandomInt(1, 100)
  22. t.Logf("Attempting to create listener %s on port %d", listenerName, listenerPort)
  23. createOpts := listeners.CreateOpts{
  24. Name: listenerName,
  25. Description: listenerDescription,
  26. LoadbalancerID: lb.ID,
  27. Protocol: listeners.ProtocolTCP,
  28. ProtocolPort: listenerPort,
  29. }
  30. listener, err := listeners.Create(client, createOpts).Extract()
  31. if err != nil {
  32. return listener, err
  33. }
  34. t.Logf("Successfully created listener %s", listenerName)
  35. if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
  36. return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
  37. }
  38. th.AssertEquals(t, listener.Name, listenerName)
  39. th.AssertEquals(t, listener.Description, listenerDescription)
  40. th.AssertEquals(t, listener.Loadbalancers[0].ID, lb.ID)
  41. th.AssertEquals(t, listener.Protocol, string(listeners.ProtocolTCP))
  42. th.AssertEquals(t, listener.ProtocolPort, listenerPort)
  43. return listener, nil
  44. }
  45. // CreateListenerHTTP will create an HTTP-based listener for a given load
  46. // balancer on a random port with a random name. An error will be returned
  47. // if the listener could not be created.
  48. func CreateListenerHTTP(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*listeners.Listener, error) {
  49. listenerName := tools.RandomString("TESTACCT-", 8)
  50. listenerDescription := tools.RandomString("TESTACCT-DESC-", 8)
  51. listenerPort := tools.RandomInt(1, 100)
  52. t.Logf("Attempting to create listener %s on port %d", listenerName, listenerPort)
  53. headers := map[string]string{
  54. "X-Forwarded-For": "true",
  55. }
  56. createOpts := listeners.CreateOpts{
  57. Name: listenerName,
  58. Description: listenerDescription,
  59. LoadbalancerID: lb.ID,
  60. InsertHeaders: headers,
  61. Protocol: listeners.ProtocolHTTP,
  62. ProtocolPort: listenerPort,
  63. }
  64. listener, err := listeners.Create(client, createOpts).Extract()
  65. if err != nil {
  66. return listener, err
  67. }
  68. t.Logf("Successfully created listener %s", listenerName)
  69. if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
  70. return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
  71. }
  72. th.AssertEquals(t, listener.Name, listenerName)
  73. th.AssertEquals(t, listener.Description, listenerDescription)
  74. th.AssertEquals(t, listener.Loadbalancers[0].ID, lb.ID)
  75. th.AssertEquals(t, listener.Protocol, string(listeners.ProtocolHTTP))
  76. th.AssertEquals(t, listener.ProtocolPort, listenerPort)
  77. th.AssertDeepEquals(t, listener.InsertHeaders, headers)
  78. return listener, nil
  79. }
  80. // CreateLoadBalancer will create a load balancer with a random name on a given
  81. // subnet. An error will be returned if the loadbalancer could not be created.
  82. func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetID string, tags []string) (*loadbalancers.LoadBalancer, error) {
  83. lbName := tools.RandomString("TESTACCT-", 8)
  84. lbDescription := tools.RandomString("TESTACCT-DESC-", 8)
  85. t.Logf("Attempting to create loadbalancer %s on subnet %s", lbName, subnetID)
  86. createOpts := loadbalancers.CreateOpts{
  87. Name: lbName,
  88. Description: lbDescription,
  89. VipSubnetID: subnetID,
  90. AdminStateUp: gophercloud.Enabled,
  91. }
  92. if len(tags) > 0 {
  93. createOpts.Tags = tags
  94. }
  95. lb, err := loadbalancers.Create(client, createOpts).Extract()
  96. if err != nil {
  97. return lb, err
  98. }
  99. t.Logf("Successfully created loadbalancer %s on subnet %s", lbName, subnetID)
  100. t.Logf("Waiting for loadbalancer %s to become active", lbName)
  101. if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
  102. return lb, err
  103. }
  104. t.Logf("LoadBalancer %s is active", lbName)
  105. th.AssertEquals(t, lb.Name, lbName)
  106. th.AssertEquals(t, lb.Description, lbDescription)
  107. th.AssertEquals(t, lb.VipSubnetID, subnetID)
  108. th.AssertEquals(t, lb.AdminStateUp, true)
  109. if len(tags) > 0 {
  110. th.AssertDeepEquals(t, lb.Tags, tags)
  111. }
  112. return lb, nil
  113. }
  114. // CreateMember will create a member with a random name, port, address, and
  115. // weight. An error will be returned if the member could not be created.
  116. func CreateMember(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer, pool *pools.Pool, subnetID, subnetCIDR string) (*pools.Member, error) {
  117. memberName := tools.RandomString("TESTACCT-", 8)
  118. memberPort := tools.RandomInt(100, 1000)
  119. memberWeight := tools.RandomInt(1, 10)
  120. cidrParts := strings.Split(subnetCIDR, "/")
  121. subnetParts := strings.Split(cidrParts[0], ".")
  122. memberAddress := fmt.Sprintf("%s.%s.%s.%d", subnetParts[0], subnetParts[1], subnetParts[2], tools.RandomInt(10, 100))
  123. t.Logf("Attempting to create member %s", memberName)
  124. createOpts := pools.CreateMemberOpts{
  125. Name: memberName,
  126. ProtocolPort: memberPort,
  127. Weight: &memberWeight,
  128. Address: memberAddress,
  129. SubnetID: subnetID,
  130. }
  131. t.Logf("Member create opts: %#v", createOpts)
  132. member, err := pools.CreateMember(client, pool.ID, createOpts).Extract()
  133. if err != nil {
  134. return member, err
  135. }
  136. t.Logf("Successfully created member %s", memberName)
  137. if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
  138. return member, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
  139. }
  140. th.AssertEquals(t, member.Name, memberName)
  141. return member, nil
  142. }
  143. // CreateMonitor will create a monitor with a random name for a specific pool.
  144. // An error will be returned if the monitor could not be created.
  145. func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer, pool *pools.Pool) (*monitors.Monitor, error) {
  146. monitorName := tools.RandomString("TESTACCT-", 8)
  147. t.Logf("Attempting to create monitor %s", monitorName)
  148. createOpts := monitors.CreateOpts{
  149. PoolID: pool.ID,
  150. Name: monitorName,
  151. Delay: 10,
  152. Timeout: 5,
  153. MaxRetries: 5,
  154. MaxRetriesDown: 4,
  155. Type: monitors.TypePING,
  156. }
  157. monitor, err := monitors.Create(client, createOpts).Extract()
  158. if err != nil {
  159. return monitor, err
  160. }
  161. t.Logf("Successfully created monitor: %s", monitorName)
  162. if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
  163. return monitor, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
  164. }
  165. th.AssertEquals(t, monitor.Name, monitorName)
  166. th.AssertEquals(t, monitor.Type, monitors.TypePING)
  167. th.AssertEquals(t, monitor.Delay, 10)
  168. th.AssertEquals(t, monitor.Timeout, 5)
  169. th.AssertEquals(t, monitor.MaxRetries, 5)
  170. th.AssertEquals(t, monitor.MaxRetriesDown, 4)
  171. return monitor, nil
  172. }
  173. // CreatePool will create a pool with a random name with a specified listener
  174. // and loadbalancer. An error will be returned if the pool could not be
  175. // created.
  176. func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*pools.Pool, error) {
  177. poolName := tools.RandomString("TESTACCT-", 8)
  178. poolDescription := tools.RandomString("TESTACCT-DESC-", 8)
  179. t.Logf("Attempting to create pool %s", poolName)
  180. createOpts := pools.CreateOpts{
  181. Name: poolName,
  182. Description: poolDescription,
  183. Protocol: pools.ProtocolTCP,
  184. LoadbalancerID: lb.ID,
  185. LBMethod: pools.LBMethodLeastConnections,
  186. }
  187. pool, err := pools.Create(client, createOpts).Extract()
  188. if err != nil {
  189. return pool, err
  190. }
  191. t.Logf("Successfully created pool %s", poolName)
  192. if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
  193. return pool, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
  194. }
  195. th.AssertEquals(t, pool.Name, poolName)
  196. th.AssertEquals(t, pool.Description, poolDescription)
  197. th.AssertEquals(t, pool.Protocol, string(pools.ProtocolTCP))
  198. th.AssertEquals(t, pool.Loadbalancers[0].ID, lb.ID)
  199. th.AssertEquals(t, pool.LBMethod, string(pools.LBMethodLeastConnections))
  200. return pool, nil
  201. }
  202. // CreatePoolHTTP will create an HTTP-based pool with a random name with a
  203. // specified listener and loadbalancer. An error will be returned if the pool
  204. // could not be created.
  205. func CreatePoolHTTP(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*pools.Pool, error) {
  206. poolName := tools.RandomString("TESTACCT-", 8)
  207. poolDescription := tools.RandomString("TESTACCT-DESC-", 8)
  208. t.Logf("Attempting to create pool %s", poolName)
  209. createOpts := pools.CreateOpts{
  210. Name: poolName,
  211. Description: poolDescription,
  212. Protocol: pools.ProtocolHTTP,
  213. LoadbalancerID: lb.ID,
  214. LBMethod: pools.LBMethodLeastConnections,
  215. }
  216. pool, err := pools.Create(client, createOpts).Extract()
  217. if err != nil {
  218. return pool, err
  219. }
  220. t.Logf("Successfully created pool %s", poolName)
  221. if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
  222. return pool, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
  223. }
  224. th.AssertEquals(t, pool.Name, poolName)
  225. th.AssertEquals(t, pool.Description, poolDescription)
  226. th.AssertEquals(t, pool.Protocol, string(pools.ProtocolHTTP))
  227. th.AssertEquals(t, pool.Loadbalancers[0].ID, lb.ID)
  228. th.AssertEquals(t, pool.LBMethod, string(pools.LBMethodLeastConnections))
  229. return pool, nil
  230. }
  231. // CreateL7Policy will create a l7 policy with a random name with a specified listener
  232. // and loadbalancer. An error will be returned if the l7 policy could not be
  233. // created.
  234. func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *listeners.Listener, lb *loadbalancers.LoadBalancer) (*l7policies.L7Policy, error) {
  235. policyName := tools.RandomString("TESTACCT-", 8)
  236. policyDescription := tools.RandomString("TESTACCT-DESC-", 8)
  237. t.Logf("Attempting to create l7 policy %s", policyName)
  238. createOpts := l7policies.CreateOpts{
  239. Name: policyName,
  240. Description: policyDescription,
  241. ListenerID: listener.ID,
  242. Action: l7policies.ActionRedirectToURL,
  243. RedirectURL: "http://www.example.com",
  244. }
  245. policy, err := l7policies.Create(client, createOpts).Extract()
  246. if err != nil {
  247. return policy, err
  248. }
  249. t.Logf("Successfully created l7 policy %s", policyName)
  250. if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
  251. return policy, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
  252. }
  253. th.AssertEquals(t, policy.Name, policyName)
  254. th.AssertEquals(t, policy.Description, policyDescription)
  255. th.AssertEquals(t, policy.ListenerID, listener.ID)
  256. th.AssertEquals(t, policy.Action, string(l7policies.ActionRedirectToURL))
  257. th.AssertEquals(t, policy.RedirectURL, "http://www.example.com")
  258. return policy, nil
  259. }
  260. // CreateL7Rule creates a l7 rule for specified l7 policy.
  261. func CreateL7Rule(t *testing.T, client *gophercloud.ServiceClient, policyID string, lb *loadbalancers.LoadBalancer) (*l7policies.Rule, error) {
  262. t.Logf("Attempting to create l7 rule for policy %s", policyID)
  263. createOpts := l7policies.CreateRuleOpts{
  264. RuleType: l7policies.TypePath,
  265. CompareType: l7policies.CompareTypeStartWith,
  266. Value: "/api",
  267. }
  268. rule, err := l7policies.CreateRule(client, policyID, createOpts).Extract()
  269. if err != nil {
  270. return rule, err
  271. }
  272. t.Logf("Successfully created l7 rule for policy %s", policyID)
  273. if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
  274. return rule, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
  275. }
  276. th.AssertEquals(t, rule.RuleType, string(l7policies.TypePath))
  277. th.AssertEquals(t, rule.CompareType, string(l7policies.CompareTypeStartWith))
  278. th.AssertEquals(t, rule.Value, "/api")
  279. return rule, nil
  280. }
  281. // DeleteL7Policy will delete a specified l7 policy. A fatal error will occur if
  282. // the l7 policy could not be deleted. This works best when used as a deferred
  283. // function.
  284. func DeleteL7Policy(t *testing.T, client *gophercloud.ServiceClient, lbID, policyID string) {
  285. t.Logf("Attempting to delete l7 policy %s", policyID)
  286. if err := l7policies.Delete(client, policyID).ExtractErr(); err != nil {
  287. if _, ok := err.(gophercloud.ErrDefault404); !ok {
  288. t.Fatalf("Unable to delete l7 policy: %v", err)
  289. }
  290. }
  291. if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil {
  292. t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err)
  293. }
  294. t.Logf("Successfully deleted l7 policy %s", policyID)
  295. }
  296. // DeleteL7Rule will delete a specified l7 rule. A fatal error will occur if
  297. // the l7 rule could not be deleted. This works best when used as a deferred
  298. // function.
  299. func DeleteL7Rule(t *testing.T, client *gophercloud.ServiceClient, lbID, policyID, ruleID string) {
  300. t.Logf("Attempting to delete l7 rule %s", ruleID)
  301. if err := l7policies.DeleteRule(client, policyID, ruleID).ExtractErr(); err != nil {
  302. if _, ok := err.(gophercloud.ErrDefault404); !ok {
  303. t.Fatalf("Unable to delete l7 rule: %v", err)
  304. }
  305. }
  306. if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil {
  307. t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err)
  308. }
  309. t.Logf("Successfully deleted l7 rule %s", ruleID)
  310. }
  311. // DeleteListener will delete a specified listener. A fatal error will occur if
  312. // the listener could not be deleted. This works best when used as a deferred
  313. // function.
  314. func DeleteListener(t *testing.T, client *gophercloud.ServiceClient, lbID, listenerID string) {
  315. t.Logf("Attempting to delete listener %s", listenerID)
  316. if err := listeners.Delete(client, listenerID).ExtractErr(); err != nil {
  317. if _, ok := err.(gophercloud.ErrDefault404); !ok {
  318. t.Fatalf("Unable to delete listener: %v", err)
  319. }
  320. }
  321. if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil {
  322. t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err)
  323. }
  324. t.Logf("Successfully deleted listener %s", listenerID)
  325. }
  326. // DeleteMember will delete a specified member. A fatal error will occur if the
  327. // member could not be deleted. This works best when used as a deferred
  328. // function.
  329. func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID, memberID string) {
  330. t.Logf("Attempting to delete member %s", memberID)
  331. if err := pools.DeleteMember(client, poolID, memberID).ExtractErr(); err != nil {
  332. if _, ok := err.(gophercloud.ErrDefault404); !ok {
  333. t.Fatalf("Unable to delete member: %s", memberID)
  334. }
  335. }
  336. if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil {
  337. t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err)
  338. }
  339. t.Logf("Successfully deleted member %s", memberID)
  340. }
  341. // DeleteLoadBalancer will delete a specified loadbalancer. A fatal error will
  342. // occur if the loadbalancer could not be deleted. This works best when used
  343. // as a deferred function.
  344. func DeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID string) {
  345. t.Logf("Attempting to delete loadbalancer %s", lbID)
  346. deleteOpts := loadbalancers.DeleteOpts{
  347. Cascade: false,
  348. }
  349. if err := loadbalancers.Delete(client, lbID, deleteOpts).ExtractErr(); err != nil {
  350. if _, ok := err.(gophercloud.ErrDefault404); !ok {
  351. t.Fatalf("Unable to delete loadbalancer: %v", err)
  352. }
  353. }
  354. t.Logf("Waiting for loadbalancer %s to delete", lbID)
  355. if err := WaitForLoadBalancerState(client, lbID, "DELETED"); err != nil {
  356. t.Fatalf("Loadbalancer did not delete in time: %s", err)
  357. }
  358. t.Logf("Successfully deleted loadbalancer %s", lbID)
  359. }
  360. // CascadeDeleteLoadBalancer will perform a cascading delete on a loadbalancer.
  361. // A fatal error will occur if the loadbalancer could not be deleted. This works
  362. // best when used as a deferred function.
  363. func CascadeDeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID string) {
  364. t.Logf("Attempting to cascade delete loadbalancer %s", lbID)
  365. deleteOpts := loadbalancers.DeleteOpts{
  366. Cascade: true,
  367. }
  368. if err := loadbalancers.Delete(client, lbID, deleteOpts).ExtractErr(); err != nil {
  369. t.Fatalf("Unable to cascade delete loadbalancer: %v", err)
  370. }
  371. t.Logf("Waiting for loadbalancer %s to cascade delete", lbID)
  372. if err := WaitForLoadBalancerState(client, lbID, "DELETED"); err != nil {
  373. t.Fatalf("Loadbalancer did not delete in time.")
  374. }
  375. t.Logf("Successfully deleted loadbalancer %s", lbID)
  376. }
  377. // DeleteMonitor will delete a specified monitor. A fatal error will occur if
  378. // the monitor could not be deleted. This works best when used as a deferred
  379. // function.
  380. func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID, monitorID string) {
  381. t.Logf("Attempting to delete monitor %s", monitorID)
  382. if err := monitors.Delete(client, monitorID).ExtractErr(); err != nil {
  383. if _, ok := err.(gophercloud.ErrDefault404); !ok {
  384. t.Fatalf("Unable to delete monitor: %v", err)
  385. }
  386. }
  387. if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil {
  388. t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err)
  389. }
  390. t.Logf("Successfully deleted monitor %s", monitorID)
  391. }
  392. // DeletePool will delete a specified pool. A fatal error will occur if the
  393. // pool could not be deleted. This works best when used as a deferred function.
  394. func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID string) {
  395. t.Logf("Attempting to delete pool %s", poolID)
  396. if err := pools.Delete(client, poolID).ExtractErr(); err != nil {
  397. if _, ok := err.(gophercloud.ErrDefault404); !ok {
  398. t.Fatalf("Unable to delete pool: %v", err)
  399. }
  400. }
  401. if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil {
  402. t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err)
  403. }
  404. t.Logf("Successfully deleted pool %s", poolID)
  405. }
  406. // WaitForLoadBalancerState will wait until a loadbalancer reaches a given state.
  407. func WaitForLoadBalancerState(client *gophercloud.ServiceClient, lbID, status string) error {
  408. return tools.WaitFor(func() (bool, error) {
  409. current, err := loadbalancers.Get(client, lbID).Extract()
  410. if err != nil {
  411. if httpStatus, ok := err.(gophercloud.ErrDefault404); ok {
  412. if httpStatus.Actual == 404 {
  413. if status == "DELETED" {
  414. return true, nil
  415. }
  416. }
  417. }
  418. return false, err
  419. }
  420. if current.ProvisioningStatus == status {
  421. return true, nil
  422. }
  423. if current.ProvisioningStatus == "ERROR" {
  424. return false, fmt.Errorf("Load balancer is in ERROR state")
  425. }
  426. return false, nil
  427. })
  428. }