github.com/vnpaycloud-console/gophercloud/v2@v2.0.5/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go (about)

     1  package v2
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/vnpaycloud-console/gophercloud/v2"
    11  	"github.com/vnpaycloud-console/gophercloud/v2/internal/acceptance/clients"
    12  	"github.com/vnpaycloud-console/gophercloud/v2/internal/acceptance/tools"
    13  	"github.com/vnpaycloud-console/gophercloud/v2/openstack/loadbalancer/v2/flavorprofiles"
    14  	"github.com/vnpaycloud-console/gophercloud/v2/openstack/loadbalancer/v2/flavors"
    15  	"github.com/vnpaycloud-console/gophercloud/v2/openstack/loadbalancer/v2/l7policies"
    16  	"github.com/vnpaycloud-console/gophercloud/v2/openstack/loadbalancer/v2/listeners"
    17  	"github.com/vnpaycloud-console/gophercloud/v2/openstack/loadbalancer/v2/loadbalancers"
    18  	"github.com/vnpaycloud-console/gophercloud/v2/openstack/loadbalancer/v2/monitors"
    19  	"github.com/vnpaycloud-console/gophercloud/v2/openstack/loadbalancer/v2/pools"
    20  	th "github.com/vnpaycloud-console/gophercloud/v2/testhelper"
    21  )
    22  
    23  // CreateListener will create a listener for a given load balancer on a random
    24  // port with a random name. An error will be returned if the listener could not
    25  // be created.
    26  func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*listeners.Listener, error) {
    27  	listenerName := tools.RandomString("TESTACCT-", 8)
    28  	listenerDescription := tools.RandomString("TESTACCT-DESC-", 8)
    29  	listenerPort := tools.RandomInt(1, 100)
    30  
    31  	t.Logf("Attempting to create listener %s on port %d", listenerName, listenerPort)
    32  
    33  	createOpts := listeners.CreateOpts{
    34  		Name:           listenerName,
    35  		Description:    listenerDescription,
    36  		LoadbalancerID: lb.ID,
    37  		Protocol:       listeners.ProtocolTCP,
    38  		ProtocolPort:   listenerPort,
    39  	}
    40  
    41  	listener, err := listeners.Create(context.TODO(), client, createOpts).Extract()
    42  	if err != nil {
    43  		return listener, err
    44  	}
    45  
    46  	t.Logf("Successfully created listener %s", listenerName)
    47  
    48  	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
    49  		return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
    50  	}
    51  
    52  	th.AssertEquals(t, listener.Name, listenerName)
    53  	th.AssertEquals(t, listener.Description, listenerDescription)
    54  	th.AssertEquals(t, listener.Loadbalancers[0].ID, lb.ID)
    55  	th.AssertEquals(t, listener.Protocol, string(listeners.ProtocolTCP))
    56  	th.AssertEquals(t, listener.ProtocolPort, listenerPort)
    57  
    58  	return listener, nil
    59  }
    60  
    61  // CreateListenerHTTP will create an HTTP-based listener for a given load
    62  // balancer on a random port with a random name. An error will be returned
    63  // if the listener could not be created.
    64  func CreateListenerHTTP(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*listeners.Listener, error) {
    65  	tlsVersions := []listeners.TLSVersion{}
    66  	tlsVersionsExp := []string(nil)
    67  	listenerName := tools.RandomString("TESTACCT-", 8)
    68  	listenerDescription := tools.RandomString("TESTACCT-DESC-", 8)
    69  	listenerPort := tools.RandomInt(1, 100)
    70  
    71  	t.Logf("Attempting to create listener %s on port %d", listenerName, listenerPort)
    72  
    73  	headers := map[string]string{
    74  		"X-Forwarded-For": "true",
    75  	}
    76  
    77  	// tls_version is only supported in microversion v2.17 introduced in victoria
    78  	if clients.IsCurrentAbove(t, "stable/ussuri") {
    79  		tlsVersions = []listeners.TLSVersion{"TLSv1.2", "TLSv1.3"}
    80  		tlsVersionsExp = []string{"TLSv1.2", "TLSv1.3"}
    81  	}
    82  
    83  	createOpts := listeners.CreateOpts{
    84  		Name:           listenerName,
    85  		Description:    listenerDescription,
    86  		LoadbalancerID: lb.ID,
    87  		InsertHeaders:  headers,
    88  		Protocol:       listeners.ProtocolHTTP,
    89  		ProtocolPort:   listenerPort,
    90  		TLSVersions:    tlsVersions,
    91  	}
    92  
    93  	listener, err := listeners.Create(context.TODO(), client, createOpts).Extract()
    94  	if err != nil {
    95  		return listener, err
    96  	}
    97  
    98  	t.Logf("Successfully created listener %s", listenerName)
    99  
   100  	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
   101  		return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
   102  	}
   103  
   104  	th.AssertEquals(t, listener.Name, listenerName)
   105  	th.AssertEquals(t, listener.Description, listenerDescription)
   106  	th.AssertEquals(t, listener.Loadbalancers[0].ID, lb.ID)
   107  	th.AssertEquals(t, listener.Protocol, string(listeners.ProtocolHTTP))
   108  	th.AssertEquals(t, listener.ProtocolPort, listenerPort)
   109  	th.AssertDeepEquals(t, listener.InsertHeaders, headers)
   110  	th.AssertDeepEquals(t, listener.TLSVersions, tlsVersionsExp)
   111  
   112  	return listener, nil
   113  }
   114  
   115  // CreateLoadBalancer will create a load balancer with a random name on a given
   116  // subnet. An error will be returned if the loadbalancer could not be created.
   117  func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetID string, tags []string, policyID string, additionalVips []loadbalancers.AdditionalVip) (*loadbalancers.LoadBalancer, error) {
   118  	lbName := tools.RandomString("TESTACCT-", 8)
   119  	lbDescription := tools.RandomString("TESTACCT-DESC-", 8)
   120  
   121  	t.Logf("Attempting to create loadbalancer %s on subnet %s", lbName, subnetID)
   122  
   123  	createOpts := loadbalancers.CreateOpts{
   124  		Name:           lbName,
   125  		Description:    lbDescription,
   126  		VipSubnetID:    subnetID,
   127  		AdminStateUp:   gophercloud.Enabled,
   128  		AdditionalVips: additionalVips,
   129  	}
   130  	if len(tags) > 0 {
   131  		createOpts.Tags = tags
   132  	}
   133  
   134  	if len(policyID) > 0 {
   135  		createOpts.VipQosPolicyID = policyID
   136  	}
   137  
   138  	lb, err := loadbalancers.Create(context.TODO(), client, createOpts).Extract()
   139  	if err != nil {
   140  		return lb, err
   141  	}
   142  
   143  	t.Logf("Successfully created loadbalancer %s on subnet %s", lbName, subnetID)
   144  	t.Logf("Waiting for loadbalancer %s to become active", lbName)
   145  
   146  	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
   147  		return lb, err
   148  	}
   149  
   150  	t.Logf("LoadBalancer %s is active", lbName)
   151  
   152  	th.AssertEquals(t, lb.Name, lbName)
   153  	th.AssertEquals(t, lb.Description, lbDescription)
   154  	th.AssertEquals(t, lb.VipSubnetID, subnetID)
   155  	th.AssertEquals(t, lb.AdminStateUp, true)
   156  
   157  	if len(tags) > 0 {
   158  		th.AssertDeepEquals(t, lb.Tags, tags)
   159  	}
   160  
   161  	if len(policyID) > 0 {
   162  		th.AssertEquals(t, lb.VipQosPolicyID, policyID)
   163  	}
   164  
   165  	return lb, nil
   166  }
   167  
   168  // CreateLoadBalancerFullyPopulated will create a  fully populated load balancer with a random name on a given
   169  // subnet. It will contain a listener, l7policy, l7rule, pool, member and health monitor.
   170  // An error will be returned if the loadbalancer could not be created.
   171  func CreateLoadBalancerFullyPopulated(t *testing.T, client *gophercloud.ServiceClient, subnetID string, tags []string) (*loadbalancers.LoadBalancer, error) {
   172  	lbName := tools.RandomString("TESTACCT-", 8)
   173  	lbDescription := tools.RandomString("TESTACCT-DESC-", 8)
   174  	listenerName := tools.RandomString("TESTACCT-", 8)
   175  	listenerDescription := tools.RandomString("TESTACCT-DESC-", 8)
   176  	listenerPort := tools.RandomInt(1, 100)
   177  	policyName := tools.RandomString("TESTACCT-", 8)
   178  	policyDescription := tools.RandomString("TESTACCT-DESC-", 8)
   179  	poolName := tools.RandomString("TESTACCT-", 8)
   180  	poolDescription := tools.RandomString("TESTACCT-DESC-", 8)
   181  	memberName := tools.RandomString("TESTACCT-", 8)
   182  	memberPort := tools.RandomInt(100, 1000)
   183  	memberWeight := tools.RandomInt(1, 10)
   184  
   185  	t.Logf("Attempting to create fully populated loadbalancer %s on subnet %s which contains listener: %s, l7Policy: %s, pool %s, member %s",
   186  		lbName, subnetID, listenerName, policyName, poolName, memberName)
   187  
   188  	createOpts := loadbalancers.CreateOpts{
   189  		Name:         lbName,
   190  		Description:  lbDescription,
   191  		VipSubnetID:  subnetID,
   192  		AdminStateUp: gophercloud.Enabled,
   193  		Listeners: []listeners.CreateOpts{{
   194  			Name:         listenerName,
   195  			Description:  listenerDescription,
   196  			Protocol:     listeners.ProtocolHTTP,
   197  			ProtocolPort: listenerPort,
   198  			DefaultPool: &pools.CreateOpts{
   199  				Name:        poolName,
   200  				Description: poolDescription,
   201  				Protocol:    pools.ProtocolHTTP,
   202  				LBMethod:    pools.LBMethodLeastConnections,
   203  				Members: []pools.CreateMemberOpts{{
   204  					Name:         memberName,
   205  					ProtocolPort: memberPort,
   206  					Weight:       &memberWeight,
   207  					Address:      "1.2.3.4",
   208  					SubnetID:     subnetID,
   209  				}},
   210  				Monitor: &monitors.CreateOpts{
   211  					Delay:          10,
   212  					Timeout:        5,
   213  					MaxRetries:     5,
   214  					MaxRetriesDown: 4,
   215  					Type:           monitors.TypeHTTP,
   216  				},
   217  			},
   218  			L7Policies: []l7policies.CreateOpts{{
   219  				Name:        policyName,
   220  				Description: policyDescription,
   221  				Action:      l7policies.ActionRedirectToURL,
   222  				RedirectURL: "http://www.example.com",
   223  				Rules: []l7policies.CreateRuleOpts{{
   224  					RuleType:    l7policies.TypePath,
   225  					CompareType: l7policies.CompareTypeStartWith,
   226  					Value:       "/api",
   227  				}},
   228  			}},
   229  		}},
   230  	}
   231  	if len(tags) > 0 {
   232  		createOpts.Tags = tags
   233  	}
   234  
   235  	lb, err := loadbalancers.Create(context.TODO(), client, createOpts).Extract()
   236  	if err != nil {
   237  		return lb, err
   238  	}
   239  
   240  	t.Logf("Successfully created loadbalancer %s on subnet %s", lbName, subnetID)
   241  	t.Logf("Waiting for loadbalancer %s to become active", lbName)
   242  
   243  	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
   244  		return lb, err
   245  	}
   246  
   247  	t.Logf("LoadBalancer %s is active", lbName)
   248  
   249  	th.AssertEquals(t, lb.Name, lbName)
   250  	th.AssertEquals(t, lb.Description, lbDescription)
   251  	th.AssertEquals(t, lb.VipSubnetID, subnetID)
   252  	th.AssertEquals(t, lb.AdminStateUp, true)
   253  
   254  	th.AssertEquals(t, len(lb.Listeners), 1)
   255  	th.AssertEquals(t, lb.Listeners[0].Name, listenerName)
   256  	th.AssertEquals(t, lb.Listeners[0].Description, listenerDescription)
   257  	th.AssertEquals(t, lb.Listeners[0].ProtocolPort, listenerPort)
   258  
   259  	th.AssertEquals(t, len(lb.Listeners[0].L7Policies), 1)
   260  	th.AssertEquals(t, lb.Listeners[0].L7Policies[0].Name, policyName)
   261  	th.AssertEquals(t, lb.Listeners[0].L7Policies[0].Description, policyDescription)
   262  	th.AssertEquals(t, lb.Listeners[0].L7Policies[0].Description, policyDescription)
   263  	th.AssertEquals(t, len(lb.Listeners[0].L7Policies[0].Rules), 1)
   264  
   265  	th.AssertEquals(t, len(lb.Pools), 1)
   266  	th.AssertEquals(t, lb.Pools[0].Name, poolName)
   267  	th.AssertEquals(t, lb.Pools[0].Description, poolDescription)
   268  
   269  	th.AssertEquals(t, len(lb.Pools[0].Members), 1)
   270  	th.AssertEquals(t, lb.Pools[0].Members[0].Name, memberName)
   271  	th.AssertEquals(t, lb.Pools[0].Members[0].ProtocolPort, memberPort)
   272  	th.AssertEquals(t, lb.Pools[0].Members[0].Weight, memberWeight)
   273  
   274  	if len(tags) > 0 {
   275  		th.AssertDeepEquals(t, lb.Tags, tags)
   276  	}
   277  
   278  	return lb, nil
   279  }
   280  
   281  // CreateMember will create a member with a random name, port, address, and
   282  // weight. An error will be returned if the member could not be created.
   283  func CreateMember(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer, pool *pools.Pool, subnetID, subnetCIDR string) (*pools.Member, error) {
   284  	memberName := tools.RandomString("TESTACCT-", 8)
   285  	memberPort := tools.RandomInt(100, 1000)
   286  	memberWeight := tools.RandomInt(1, 10)
   287  
   288  	cidrParts := strings.Split(subnetCIDR, "/")
   289  	subnetParts := strings.Split(cidrParts[0], ".")
   290  	memberAddress := fmt.Sprintf("%s.%s.%s.%d", subnetParts[0], subnetParts[1], subnetParts[2], tools.RandomInt(10, 100))
   291  
   292  	t.Logf("Attempting to create member %s", memberName)
   293  
   294  	createOpts := pools.CreateMemberOpts{
   295  		Name:         memberName,
   296  		ProtocolPort: memberPort,
   297  		Weight:       &memberWeight,
   298  		Address:      memberAddress,
   299  		SubnetID:     subnetID,
   300  	}
   301  
   302  	t.Logf("Member create opts: %#v", createOpts)
   303  
   304  	member, err := pools.CreateMember(context.TODO(), client, pool.ID, createOpts).Extract()
   305  	if err != nil {
   306  		return member, err
   307  	}
   308  
   309  	t.Logf("Successfully created member %s", memberName)
   310  
   311  	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
   312  		return member, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
   313  	}
   314  
   315  	th.AssertEquals(t, member.Name, memberName)
   316  
   317  	return member, nil
   318  }
   319  
   320  // CreateMonitor will create a monitor with a random name for a specific pool.
   321  // An error will be returned if the monitor could not be created.
   322  func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer, pool *pools.Pool) (*monitors.Monitor, error) {
   323  	monitorName := tools.RandomString("TESTACCT-", 8)
   324  
   325  	t.Logf("Attempting to create monitor %s", monitorName)
   326  
   327  	createOpts := monitors.CreateOpts{
   328  		PoolID:         pool.ID,
   329  		Name:           monitorName,
   330  		Delay:          10,
   331  		Timeout:        5,
   332  		MaxRetries:     5,
   333  		MaxRetriesDown: 4,
   334  		Type:           monitors.TypePING,
   335  	}
   336  
   337  	monitor, err := monitors.Create(context.TODO(), client, createOpts).Extract()
   338  	if err != nil {
   339  		return monitor, err
   340  	}
   341  
   342  	t.Logf("Successfully created monitor: %s", monitorName)
   343  
   344  	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
   345  		return monitor, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
   346  	}
   347  
   348  	th.AssertEquals(t, monitor.Name, monitorName)
   349  	th.AssertEquals(t, monitor.Type, monitors.TypePING)
   350  	th.AssertEquals(t, monitor.Delay, 10)
   351  	th.AssertEquals(t, monitor.Timeout, 5)
   352  	th.AssertEquals(t, monitor.MaxRetries, 5)
   353  	th.AssertEquals(t, monitor.MaxRetriesDown, 4)
   354  
   355  	return monitor, nil
   356  }
   357  
   358  // CreatePool will create a pool with a random name with a specified listener
   359  // and loadbalancer. An error will be returned if the pool could not be
   360  // created.
   361  func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*pools.Pool, error) {
   362  	poolName := tools.RandomString("TESTACCT-", 8)
   363  	poolDescription := tools.RandomString("TESTACCT-DESC-", 8)
   364  
   365  	t.Logf("Attempting to create pool %s", poolName)
   366  
   367  	createOpts := pools.CreateOpts{
   368  		Name:           poolName,
   369  		Description:    poolDescription,
   370  		Protocol:       pools.ProtocolTCP,
   371  		LoadbalancerID: lb.ID,
   372  		LBMethod:       pools.LBMethodLeastConnections,
   373  	}
   374  
   375  	pool, err := pools.Create(context.TODO(), client, createOpts).Extract()
   376  	if err != nil {
   377  		return pool, err
   378  	}
   379  
   380  	t.Logf("Successfully created pool %s", poolName)
   381  
   382  	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
   383  		return pool, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
   384  	}
   385  
   386  	th.AssertEquals(t, pool.Name, poolName)
   387  	th.AssertEquals(t, pool.Description, poolDescription)
   388  	th.AssertEquals(t, pool.Protocol, string(pools.ProtocolTCP))
   389  	th.AssertEquals(t, pool.Loadbalancers[0].ID, lb.ID)
   390  	th.AssertEquals(t, pool.LBMethod, string(pools.LBMethodLeastConnections))
   391  
   392  	return pool, nil
   393  }
   394  
   395  // CreatePoolHTTP will create an HTTP-based pool with a random name with a
   396  // specified listener and loadbalancer. An error will be returned if the pool
   397  // could not be created.
   398  func CreatePoolHTTP(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*pools.Pool, error) {
   399  	poolName := tools.RandomString("TESTACCT-", 8)
   400  	poolDescription := tools.RandomString("TESTACCT-DESC-", 8)
   401  
   402  	t.Logf("Attempting to create pool %s", poolName)
   403  
   404  	createOpts := pools.CreateOpts{
   405  		Name:           poolName,
   406  		Description:    poolDescription,
   407  		Protocol:       pools.ProtocolHTTP,
   408  		LoadbalancerID: lb.ID,
   409  		LBMethod:       pools.LBMethodLeastConnections,
   410  	}
   411  
   412  	pool, err := pools.Create(context.TODO(), client, createOpts).Extract()
   413  	if err != nil {
   414  		return pool, err
   415  	}
   416  
   417  	t.Logf("Successfully created pool %s", poolName)
   418  
   419  	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
   420  		return pool, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
   421  	}
   422  
   423  	th.AssertEquals(t, pool.Name, poolName)
   424  	th.AssertEquals(t, pool.Description, poolDescription)
   425  	th.AssertEquals(t, pool.Protocol, string(pools.ProtocolHTTP))
   426  	th.AssertEquals(t, pool.Loadbalancers[0].ID, lb.ID)
   427  	th.AssertEquals(t, pool.LBMethod, string(pools.LBMethodLeastConnections))
   428  
   429  	return pool, nil
   430  }
   431  
   432  // CreateL7Policy will create a l7 policy with a random name with a specified listener
   433  // and loadbalancer. An error will be returned if the l7 policy could not be
   434  // created.
   435  func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *listeners.Listener, lb *loadbalancers.LoadBalancer, tags []string) (*l7policies.L7Policy, error) {
   436  	policyName := tools.RandomString("TESTACCT-", 8)
   437  	policyDescription := tools.RandomString("TESTACCT-DESC-", 8)
   438  
   439  	t.Logf("Attempting to create l7 policy %s", policyName)
   440  
   441  	createOpts := l7policies.CreateOpts{
   442  		Name:        policyName,
   443  		Description: policyDescription,
   444  		ListenerID:  listener.ID,
   445  		Action:      l7policies.ActionRedirectToURL,
   446  		RedirectURL: "http://www.example.com",
   447  		Tags:        tags,
   448  	}
   449  
   450  	policy, err := l7policies.Create(context.TODO(), client, createOpts).Extract()
   451  	if err != nil {
   452  		return policy, err
   453  	}
   454  
   455  	t.Logf("Successfully created l7 policy %s", policyName)
   456  
   457  	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
   458  		return policy, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
   459  	}
   460  
   461  	th.AssertEquals(t, policy.Name, policyName)
   462  	th.AssertEquals(t, policy.Description, policyDescription)
   463  	th.AssertEquals(t, policy.ListenerID, listener.ID)
   464  	th.AssertEquals(t, policy.Action, string(l7policies.ActionRedirectToURL))
   465  	th.AssertEquals(t, policy.RedirectURL, "http://www.example.com")
   466  	th.AssertDeepEquals(t, policy.Tags, tags)
   467  
   468  	return policy, nil
   469  }
   470  
   471  // CreateL7Rule creates a l7 rule for specified l7 policy.
   472  func CreateL7Rule(t *testing.T, client *gophercloud.ServiceClient, policyID string, lb *loadbalancers.LoadBalancer, tags []string) (*l7policies.Rule, error) {
   473  	t.Logf("Attempting to create l7 rule for policy %s", policyID)
   474  
   475  	createOpts := l7policies.CreateRuleOpts{
   476  		RuleType:    l7policies.TypePath,
   477  		CompareType: l7policies.CompareTypeStartWith,
   478  		Value:       "/api",
   479  		Tags:        tags,
   480  	}
   481  
   482  	rule, err := l7policies.CreateRule(context.TODO(), client, policyID, createOpts).Extract()
   483  	if err != nil {
   484  		return rule, err
   485  	}
   486  
   487  	t.Logf("Successfully created l7 rule for policy %s", policyID)
   488  
   489  	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
   490  		return rule, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
   491  	}
   492  
   493  	th.AssertEquals(t, rule.RuleType, string(l7policies.TypePath))
   494  	th.AssertEquals(t, rule.CompareType, string(l7policies.CompareTypeStartWith))
   495  	th.AssertEquals(t, rule.Value, "/api")
   496  	th.AssertDeepEquals(t, rule.Tags, tags)
   497  
   498  	return rule, nil
   499  }
   500  
   501  // DeleteL7Policy will delete a specified l7 policy. A fatal error will occur if
   502  // the l7 policy could not be deleted. This works best when used as a deferred
   503  // function.
   504  func DeleteL7Policy(t *testing.T, client *gophercloud.ServiceClient, lbID, policyID string) {
   505  	t.Logf("Attempting to delete l7 policy %s", policyID)
   506  
   507  	if err := l7policies.Delete(context.TODO(), client, policyID).ExtractErr(); err != nil {
   508  		if !gophercloud.ResponseCodeIs(err, http.StatusNotFound) {
   509  			t.Fatalf("Unable to delete l7 policy: %v", err)
   510  		}
   511  	}
   512  
   513  	if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil {
   514  		t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err)
   515  	}
   516  
   517  	t.Logf("Successfully deleted l7 policy %s", policyID)
   518  }
   519  
   520  // DeleteL7Rule will delete a specified l7 rule. A fatal error will occur if
   521  // the l7 rule could not be deleted. This works best when used as a deferred
   522  // function.
   523  func DeleteL7Rule(t *testing.T, client *gophercloud.ServiceClient, lbID, policyID, ruleID string) {
   524  	t.Logf("Attempting to delete l7 rule %s", ruleID)
   525  
   526  	if err := l7policies.DeleteRule(context.TODO(), client, policyID, ruleID).ExtractErr(); err != nil {
   527  		if !gophercloud.ResponseCodeIs(err, http.StatusNotFound) {
   528  			t.Fatalf("Unable to delete l7 rule: %v", err)
   529  		}
   530  	}
   531  
   532  	if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil {
   533  		t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err)
   534  	}
   535  
   536  	t.Logf("Successfully deleted l7 rule %s", ruleID)
   537  }
   538  
   539  // DeleteListener will delete a specified listener. A fatal error will occur if
   540  // the listener could not be deleted. This works best when used as a deferred
   541  // function.
   542  func DeleteListener(t *testing.T, client *gophercloud.ServiceClient, lbID, listenerID string) {
   543  	t.Logf("Attempting to delete listener %s", listenerID)
   544  
   545  	if err := listeners.Delete(context.TODO(), client, listenerID).ExtractErr(); err != nil {
   546  		if !gophercloud.ResponseCodeIs(err, http.StatusNotFound) {
   547  			t.Fatalf("Unable to delete listener: %v", err)
   548  		}
   549  	}
   550  
   551  	if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil {
   552  		t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err)
   553  	}
   554  
   555  	t.Logf("Successfully deleted listener %s", listenerID)
   556  }
   557  
   558  // DeleteMember will delete a specified member. A fatal error will occur if the
   559  // member could not be deleted. This works best when used as a deferred
   560  // function.
   561  func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID, memberID string) {
   562  	t.Logf("Attempting to delete member %s", memberID)
   563  
   564  	if err := pools.DeleteMember(context.TODO(), client, poolID, memberID).ExtractErr(); err != nil {
   565  		if !gophercloud.ResponseCodeIs(err, http.StatusNotFound) {
   566  			t.Fatalf("Unable to delete member: %s", memberID)
   567  		}
   568  	}
   569  
   570  	if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil {
   571  		t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err)
   572  	}
   573  
   574  	t.Logf("Successfully deleted member %s", memberID)
   575  }
   576  
   577  // DeleteLoadBalancer will delete a specified loadbalancer. A fatal error will
   578  // occur if the loadbalancer could not be deleted. This works best when used
   579  // as a deferred function.
   580  func DeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID string) {
   581  	t.Logf("Attempting to delete loadbalancer %s", lbID)
   582  
   583  	deleteOpts := loadbalancers.DeleteOpts{
   584  		Cascade: false,
   585  	}
   586  
   587  	if err := loadbalancers.Delete(context.TODO(), client, lbID, deleteOpts).ExtractErr(); err != nil {
   588  		if !gophercloud.ResponseCodeIs(err, http.StatusNotFound) {
   589  			t.Fatalf("Unable to delete loadbalancer: %v", err)
   590  		}
   591  	}
   592  
   593  	t.Logf("Waiting for loadbalancer %s to delete", lbID)
   594  
   595  	if err := WaitForLoadBalancerState(client, lbID, "DELETED"); err != nil {
   596  		t.Fatalf("Loadbalancer did not delete in time: %s", err)
   597  	}
   598  
   599  	t.Logf("Successfully deleted loadbalancer %s", lbID)
   600  }
   601  
   602  // CascadeDeleteLoadBalancer will perform a cascading delete on a loadbalancer.
   603  // A fatal error will occur if the loadbalancer could not be deleted. This works
   604  // best when used as a deferred function.
   605  func CascadeDeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID string) {
   606  	t.Logf("Attempting to cascade delete loadbalancer %s", lbID)
   607  
   608  	deleteOpts := loadbalancers.DeleteOpts{
   609  		Cascade: true,
   610  	}
   611  
   612  	if err := loadbalancers.Delete(context.TODO(), client, lbID, deleteOpts).ExtractErr(); err != nil {
   613  		t.Fatalf("Unable to cascade delete loadbalancer: %v", err)
   614  	}
   615  
   616  	t.Logf("Waiting for loadbalancer %s to cascade delete", lbID)
   617  
   618  	if err := WaitForLoadBalancerState(client, lbID, "DELETED"); err != nil {
   619  		t.Fatalf("Loadbalancer did not delete in time.")
   620  	}
   621  
   622  	t.Logf("Successfully deleted loadbalancer %s", lbID)
   623  }
   624  
   625  // DeleteMonitor will delete a specified monitor. A fatal error will occur if
   626  // the monitor could not be deleted. This works best when used as a deferred
   627  // function.
   628  func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID, monitorID string) {
   629  	t.Logf("Attempting to delete monitor %s", monitorID)
   630  
   631  	if err := monitors.Delete(context.TODO(), client, monitorID).ExtractErr(); err != nil {
   632  		if !gophercloud.ResponseCodeIs(err, http.StatusNotFound) {
   633  			t.Fatalf("Unable to delete monitor: %v", err)
   634  		}
   635  	}
   636  
   637  	if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil {
   638  		t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err)
   639  	}
   640  
   641  	t.Logf("Successfully deleted monitor %s", monitorID)
   642  }
   643  
   644  // DeletePool will delete a specified pool. A fatal error will occur if the
   645  // pool could not be deleted. This works best when used as a deferred function.
   646  func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID string) {
   647  	t.Logf("Attempting to delete pool %s", poolID)
   648  
   649  	if err := pools.Delete(context.TODO(), client, poolID).ExtractErr(); err != nil {
   650  		if !gophercloud.ResponseCodeIs(err, http.StatusNotFound) {
   651  			t.Fatalf("Unable to delete pool: %v", err)
   652  		}
   653  	}
   654  
   655  	if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil {
   656  		t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err)
   657  	}
   658  
   659  	t.Logf("Successfully deleted pool %s", poolID)
   660  }
   661  
   662  // WaitForLoadBalancerState will wait until a loadbalancer reaches a given state.
   663  func WaitForLoadBalancerState(client *gophercloud.ServiceClient, lbID, status string) error {
   664  	return tools.WaitFor(func(ctx context.Context) (bool, error) {
   665  		current, err := loadbalancers.Get(ctx, client, lbID).Extract()
   666  		if err != nil {
   667  			if gophercloud.ResponseCodeIs(err, http.StatusNotFound) && status == "DELETED" {
   668  				return true, nil
   669  			}
   670  			return false, err
   671  		}
   672  
   673  		if current.ProvisioningStatus == status {
   674  			return true, nil
   675  		}
   676  
   677  		if current.ProvisioningStatus == "ERROR" {
   678  			return false, fmt.Errorf("Load balancer is in ERROR state")
   679  		}
   680  
   681  		return false, nil
   682  	})
   683  }
   684  
   685  func CreateFlavorProfile(t *testing.T, client *gophercloud.ServiceClient) (*flavorprofiles.FlavorProfile, error) {
   686  	flavorProfileName := tools.RandomString("TESTACCT-", 8)
   687  	flavorProfileDriver := "amphora"
   688  	flavorProfileData := "{\"loadbalancer_topology\": \"SINGLE\"}"
   689  
   690  	createOpts := flavorprofiles.CreateOpts{
   691  		Name:         flavorProfileName,
   692  		ProviderName: flavorProfileDriver,
   693  		FlavorData:   flavorProfileData,
   694  	}
   695  
   696  	flavorProfile, err := flavorprofiles.Create(context.TODO(), client, createOpts).Extract()
   697  	if err != nil {
   698  		return flavorProfile, err
   699  	}
   700  
   701  	t.Logf("Successfully created flavorprofile %s", flavorProfileName)
   702  
   703  	th.AssertEquals(t, flavorProfileName, flavorProfile.Name)
   704  	th.AssertEquals(t, flavorProfileDriver, flavorProfile.ProviderName)
   705  	th.AssertEquals(t, flavorProfileData, flavorProfile.FlavorData)
   706  
   707  	return flavorProfile, nil
   708  }
   709  
   710  func DeleteFlavorProfile(t *testing.T, client *gophercloud.ServiceClient, flavorProfile *flavorprofiles.FlavorProfile) {
   711  	err := flavorprofiles.Delete(context.TODO(), client, flavorProfile.ID).ExtractErr()
   712  	if err != nil {
   713  		t.Fatalf("Unable to delete flavorprofile: %v", err)
   714  	}
   715  
   716  	t.Logf("Successfully deleted flavorprofile %s", flavorProfile.Name)
   717  }
   718  
   719  func CreateFlavor(t *testing.T, client *gophercloud.ServiceClient, flavorProfile *flavorprofiles.FlavorProfile) (*flavors.Flavor, error) {
   720  	flavorName := tools.RandomString("TESTACCT-", 8)
   721  	description := tools.RandomString("TESTACCT-desc-", 32)
   722  
   723  	createOpts := flavors.CreateOpts{
   724  		Name:            flavorName,
   725  		Description:     description,
   726  		FlavorProfileId: flavorProfile.ID,
   727  		Enabled:         true,
   728  	}
   729  
   730  	flavor, err := flavors.Create(context.TODO(), client, createOpts).Extract()
   731  	if err != nil {
   732  		return flavor, err
   733  	}
   734  
   735  	t.Logf("Successfully created flavor %s with flavorprofile %s", flavor.Name, flavorProfile.Name)
   736  
   737  	th.AssertEquals(t, flavorName, flavor.Name)
   738  	th.AssertEquals(t, description, flavor.Description)
   739  	th.AssertEquals(t, flavorProfile.ID, flavor.FlavorProfileId)
   740  	th.AssertEquals(t, true, flavor.Enabled)
   741  
   742  	return flavor, nil
   743  }
   744  
   745  func DeleteFlavor(t *testing.T, client *gophercloud.ServiceClient, flavor *flavors.Flavor) {
   746  	err := flavors.Delete(context.TODO(), client, flavor.ID).ExtractErr()
   747  	if err != nil {
   748  		t.Fatalf("Unable to delete flavor: %v", err)
   749  	}
   750  
   751  	t.Logf("Successfully deleted flavor %s", flavor.Name)
   752  }