Skip to content

Commit

Permalink
LBaaS v2 l7 policy support [Part 1]: create l7policy
Browse files Browse the repository at this point in the history
  • Loading branch information
lingxiankong committed Apr 3, 2018
1 parent b675512 commit 29ddf1d
Show file tree
Hide file tree
Showing 18 changed files with 707 additions and 0 deletions.
30 changes: 30 additions & 0 deletions acceptance/openstack/loadbalancer/v2/loadbalancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies"
"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners"
"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers"
"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors"
Expand Down Expand Up @@ -172,6 +173,35 @@ func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalance
return pool, nil
}

// CreateL7Policy will create a l7 policy with a random name with a specified listener
// and loadbalancer. An error will be returned if the l7 policy could not be
// created.
func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *listeners.Listener, lb *loadbalancers.LoadBalancer) (*l7policies.L7Policy, error) {
policyName := tools.RandomString("TESTACCT-", 8)

t.Logf("Attempting to create l7 policy %s", policyName)

createOpts := l7policies.CreateOpts{
Name: policyName,
ListenerID: listener.ID,
Action: l7policies.ActionRedirectToURL,
RedirectURL: "http://www.example.com",
}

policy, err := l7policies.Create(client, createOpts).Extract()
if err != nil {
return policy, err
}

t.Logf("Successfully created l7 policy %s", policyName)

if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
return policy, fmt.Errorf("Timed out waiting for loadbalancer to become active")
}

return policy, nil
}

// DeleteListener will delete a specified listener. A fatal error will occur if
// the listener could not be deleted. This works best when used as a deferred
// function.
Expand Down
6 changes: 6 additions & 0 deletions acceptance/openstack/loadbalancer/v2/loadbalancers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ func TestLoadbalancersCRUD(t *testing.T) {

tools.PrintResource(t, newListener)

// L7 policy
policy, err := CreateL7Policy(t, client, listener, lb)
if err != nil {
t.Fatalf("Unable to create l7 policy: %v", err)
}

// Pool
pool, err := CreatePool(t, lbClient, lb)
if err != nil {
Expand Down
30 changes: 30 additions & 0 deletions acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/l7policies"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
Expand Down Expand Up @@ -172,6 +173,35 @@ func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalance
return pool, nil
}

// CreateL7Policy will create a l7 policy with a random name with a specified listener
// and loadbalancer. An error will be returned if the l7 policy could not be
// created.
func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *listeners.Listener, lb *loadbalancers.LoadBalancer) (*l7policies.L7Policy, error) {
policyName := tools.RandomString("TESTACCT-", 8)

t.Logf("Attempting to create l7 policy %s", policyName)

createOpts := l7policies.CreateOpts{
Name: policyName,
ListenerID: listener.ID,
Action: l7policies.ActionRedirectToURL,
RedirectURL: "http://www.example.com",
}

policy, err := l7policies.Create(client, createOpts).Extract()
if err != nil {
return policy, err
}

t.Logf("Successfully created l7 policy %s", policyName)

if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
return policy, fmt.Errorf("Timed out waiting for loadbalancer to become active")
}

return policy, nil
}

// DeleteListener will delete a specified listener. A fatal error will occur if
// the listener could not be deleted. This works best when used as a deferred
// function.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ func TestLoadbalancersCRUD(t *testing.T) {

tools.PrintResource(t, newListener)

// L7 policy
policy, err := CreateL7Policy(t, client, listener, lb)
if err != nil {
t.Fatalf("Unable to create l7 policy: %v", err)
}

// Pool
pool, err := CreatePool(t, client, lb)
if err != nil {
Expand Down
16 changes: 16 additions & 0 deletions openstack/loadbalancer/v2/l7policies/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
Package l7policies provides information and interaction with L7Policies and
Rules of the LBaaS v2 extension for the OpenStack Networking service.
Example to Create a L7Policy
createOpts := l7policies.CreateOpts{
Name: "redirect-example.com",
ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d",
Action: l7policies.ActionRedirectToURL,
RedirectURL: "http://www.example.com",
}
l7policy, err := l7policies.Create(networkClient, createOpts).Extract()
if err != nil {
panic(err)
}
*/
package l7policies
84 changes: 84 additions & 0 deletions openstack/loadbalancer/v2/l7policies/requests.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package l7policies

import (
"github.com/gophercloud/gophercloud"
)

// CreateOptsBuilder allows extensions to add additional parameters to the
// Create request.
type CreateOptsBuilder interface {
ToL7PolicyCreateMap() (map[string]interface{}, error)
}

type Action string
type RuleType string
type CompareType string

const (
ActionRedirectToPool Action = "REDIRECT_TO_POOL"
ActionRedirectToURL Action = "REDIRECT_TO_URL"
ActionReject Action = "REJECT"

TypeCookie RuleType = "COOKIE"
TypeFileType RuleType = "FILE_TYPE"
TypeHeader RuleType = "HEADER"
TypeHostName RuleType = "HOST_NAME"
TypePath RuleType = "PATH"

CompareTypeContains CompareType = "CONTAINS"
CompareTypeEndWith CompareType = "ENDS_WITH"
CompareTypeEqual CompareType = "EQUAL_TO"
CompareTypeRegex CompareType = "REGEX"
CompareTypeStartWith CompareType = "STARTS_WITH"
)

// CreateOpts is the common options struct used in this package's Create
// operation.
type CreateOpts struct {
// Name of the L7 policy.
Name string `json:"name,omitempty"`

// The ID of the listener.
ListenerID string `json:"listener_id" required:"true"`

// The L7 policy action. One of REDIRECT_TO_POOL, REDIRECT_TO_URL, or REJECT.
Action Action `json:"action" required:"true"`

// The position of this policy on the listener.
Position int32 `json:"position,omitempty"`

// A human-readable description for the resource.
Description string `json:"description,omitempty"`

// TenantID is the UUID of the project who owns the L7 policy in neutron-lbaas.
// Only administrative users can specify a project UUID other than their own.
TenantID string `json:"tenant_id,omitempty"`

// ProjectID is the UUID of the project who owns the L7 policy in octavia.
// Only administrative users can specify a project UUID other than their own.
ProjectID string `json:"project_id,omitempty"`

// Requests matching this policy will be redirected to the pool with this ID.
// Only valid if action is REDIRECT_TO_POOL.
RedirectPoolID string `json:"redirect_pool_id,omitempty"`

// Requests matching this policy will be redirected to this URL.
// Only valid if action is REDIRECT_TO_URL.
RedirectURL string `json:"redirect_url,omitempty"`
}

// ToL7PolicyCreateMap builds a request body from CreateOpts.
func (opts CreateOpts) ToL7PolicyCreateMap() (map[string]interface{}, error) {
return gophercloud.BuildRequestBody(opts, "l7policy")
}

// Create accepts a CreateOpts struct and uses the values to create a new l7policy.
func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
b, err := opts.ToL7PolicyCreateMap()
if err != nil {
r.Err = err
return
}
_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
return
}
93 changes: 93 additions & 0 deletions openstack/loadbalancer/v2/l7policies/results.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package l7policies

import (
"github.com/gophercloud/gophercloud"
)

// L7Policy is a collection of L7 rules associated with a Listener, and which
// may also have an association to a back-end pool.
type L7Policy struct {
// The unique ID for the L7 policy.
ID string `json:"id"`

// Name of the L7 policy.
Name string `json:"name"`

// The ID of the listener.
ListenerID string `json:"listener_id"`

// The L7 policy action. One of REDIRECT_TO_POOL, REDIRECT_TO_URL, or REJECT.
Action Action `json:"action"`

// The position of this policy on the listener.
Position int32 `json:"position"`

// A human-readable description for the resource.
Description string `json:"description"`

// TenantID is the UUID of the project who owns the L7 policy in neutron-lbaas.
// Only administrative users can specify a project UUID other than their own.
TenantID string `json:"tenant_id"`

// Requests matching this policy will be redirected to the pool with this ID.
// Only valid if action is REDIRECT_TO_POOL.
RedirectPoolID string `json:"redirect_pool_id"`

// Requests matching this policy will be redirected to this URL.
// Only valid if action is REDIRECT_TO_URL.
RedirectURL string `json:"redirect_url"`

// The administrative state of the L7 policy, which is up (true) or down (false).
AdminStateUp bool `json:"admin_state_up"`

// Rules are List of associated L7 rule IDs.
Rules []Rule `json:"rules"`
}

// Rule represents layer 7 load balancing rule.
type Rule struct {
// The unique ID for the L7 rule.
ID string `json:"id"`

// The L7 rule type. One of COOKIE, FILE_TYPE, HEADER, HOST_NAME, or PATH.
RuleType RuleType `json:"type"`

// The comparison type for the L7 rule. One of CONTAINS, ENDS_WITH, EQUAL_TO, REGEX, or STARTS_WITH.
CompareType CompareType `json:"compare_type"`

// The value to use for the comparison. For example, the file type to compare.
Value string `json:"value"`

// TenantID is the UUID of the project who owns the rule in neutron-lbaas.
// Only administrative users can specify a project UUID other than their own.
TenantID string `json:"tenant_id"`

// The key to use for the comparison. For example, the name of the cookie to evaluate.
Key string `json:"key"`

// When true the logic of the rule is inverted. For example, with invert true,
// equal to would become not equal to. Default is false.
Invert bool `json:"invert"`

// The administrative state of the L7 rule, which is up (true) or down (false).
AdminStateUp bool `json:"admin_state_up"`
}

type commonResult struct {
gophercloud.Result
}

// Extract is a function that accepts a result and extracts a l7policy.
func (r commonResult) Extract() (*L7Policy, error) {
var s struct {
L7Policy *L7Policy `json:"l7policy"`
}
err := r.ExtractInto(&s)
return s.L7Policy, err
}

// CreateResult represents the result of a Create operation. Call its Extract
// method to interpret the result as a L7Policy.
type CreateResult struct {
commonResult
}
2 changes: 2 additions & 0 deletions openstack/loadbalancer/v2/l7policies/testing/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// l7policies unit tests
package testing
67 changes: 67 additions & 0 deletions openstack/loadbalancer/v2/l7policies/testing/fixtures.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package testing

import (
"fmt"
"net/http"
"testing"

"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies"
th "github.com/gophercloud/gophercloud/testhelper"
"github.com/gophercloud/gophercloud/testhelper/client"
)

// SingleL7PolicyBody is the canned body of a Get request on an existing l7policy.
const SingleL7PolicyBody = `
{
"l7policy": {
"listener_id": "023f2e34-7806-443b-bfae-16c324569a3d",
"description": "",
"admin_state_up": true,
"redirect_pool_id": null,
"redirect_url": "http://www.example.com",
"action": "REDIRECT_TO_URL",
"position": 1,
"tenant_id": "e3cd678b11784734bc366148aa37580e",
"id": "8a1412f0-4c32-4257-8b07-af4770b604fd",
"name": "redirect-example.com",
"rules": []
}
}
`

var (
L7PolicyToURL = l7policies.L7Policy{
ID: "8a1412f0-4c32-4257-8b07-af4770b604fd",
Name: "redirect-example.com",
ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d",
Action: l7policies.ActionRedirectToURL,
Position: 1,
Description: "",
TenantID: "e3cd678b11784734bc366148aa37580e",
RedirectPoolID: "",
RedirectURL: "http://www.example.com",
AdminStateUp: true,
Rules: []l7policies.Rule{},
}
)

// HandleL7PolicyCreationSuccessfully sets up the test server to respond to a l7policy creation request
// with a given response.
func HandleL7PolicyCreationSuccessfully(t *testing.T, response string) {
th.Mux.HandleFunc("/v2.0/lbaas/l7policies", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "POST")
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
th.TestJSONRequest(t, r, `{
"l7policy": {
"listener_id": "023f2e34-7806-443b-bfae-16c324569a3d",
"redirect_url": "http://www.example.com",
"name": "redirect-example.com",
"action": "REDIRECT_TO_URL"
}
}`)

w.WriteHeader(http.StatusAccepted)
w.Header().Add("Content-Type", "application/json")
fmt.Fprintf(w, response)
})
}

0 comments on commit 29ddf1d

Please sign in to comment.