Skip to content

Commit

Permalink
LBaaS v2 l7 support: create l7policy
Browse files Browse the repository at this point in the history
For gophercloud#832

L7policy functionality in Octavia is backward compatible with
Neutron-LBaaS.

Octavia L7 policy create API:
https://developer.openstack.org/api-ref/load-balancer/v2/index.html#create-an-l7-policy
  • Loading branch information
lingxiankong committed Mar 16, 2018
1 parent 35000af commit e8992e1
Show file tree
Hide file tree
Showing 7 changed files with 356 additions and 0 deletions.
19 changes: 19 additions & 0 deletions openstack/networking/v2/extensions/lbaas_v2/l7policies/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
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/networking/v2/extensions/lbaas_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" xor:"RedirectURL"`

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

// 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
}
108 changes: 108 additions & 0 deletions openstack/networking/v2/extensions/lbaas_v2/l7policies/results.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
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 int `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"`

// 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"`

// 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"`

// The operating status of the L7 policy. This value is ONLINE or OFFLINE.
OperatingStatus string `json:"operating_status"`

// The provisioning status of the L7 policy.
// This value is ACTIVE, PENDING_CREATE or ERROR.
ProvisioningStatus string `json:"provisioning_status"`

// 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"`

// ProjectID is the UUID of the project who owns the rule in octavia.
// Only administrative users can specify a project UUID other than their own.
ProjectID string `json:"project_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
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// l7policies unit tests
package testing
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package testing

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

"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_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": "Redirect requests to example.com",
"admin_state_up": true,
"created_at": "2017-06-24T23:25:14",
"provisioning_status": "ACTIVE",
"updated_at": "2017-06-24T23:30:05",
"redirect_pool_id": null,
"redirect_url": "http://www.example.com",
"action": "REDIRECT_TO_URL",
"position": 1,
"project_id": "e3cd678b11784734bc366148aa37580e",
"id": "8a1412f0-4c32-4257-8b07-af4770b604fd",
"operating_status": "ONLINE",
"name": "redirect-example.com",
"rules": [
{
"id": "efd6a3f8-73bf-47f0-8ae6-503ebda57372"
}
]
}
}
`

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: "Redirect requests to example.com",
ProjectID: "e3cd678b11784734bc366148aa37580e",
RedirectPoolID: "",
RedirectURL: "http://www.example.com",
AdminStateUp: true,
ProvisioningStatus: "ACTIVE",
OperatingStatus: "ONLINE",
Rules: []l7policies.Rule{{ID: "efd6a3f8-73bf-47f0-8ae6-503ebda57372"}},
}
)

// 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": {
"description": "Redirect requests to example.com",
"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)
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package testing

import (
"testing"

fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/l7policies"
th "github.com/gophercloud/gophercloud/testhelper"
)

func TestCreateL7Policy(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleL7PolicyCreationSuccessfully(t, SingleL7PolicyBody)

actual, err := l7policies.Create(fake.ServiceClient(), l7policies.CreateOpts{
Name: "redirect-example.com",
Description: "Redirect requests to example.com",
ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d",
Action: l7policies.ActionRedirectToURL,
RedirectURL: "http://www.example.com",
}).Extract()

th.AssertNoErr(t, err)
th.CheckDeepEquals(t, L7PolicyToURL, *actual)
}

func TestRequiredL7PolicyCreateOpts(t *testing.T) {
// no param specified.
res := l7policies.Create(fake.ServiceClient(), l7policies.CreateOpts{})
if res.Err == nil {
t.Fatalf("Expected error, got none")
}

// RedirectPoolID or RedirectURL is missing.
res = l7policies.Create(fake.ServiceClient(), l7policies.CreateOpts{
ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d",
Action: l7policies.ActionRedirectToPool,
})
if res.Err == nil {
t.Fatalf("Expected error, but got none")
}

// Action is invalid.
res = l7policies.Create(fake.ServiceClient(), l7policies.CreateOpts{
ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d",
Action: l7policies.Action("invalid"),
RedirectPoolID: "bac433c6-5bea-4311-80da-bd1cd90fbd25",
})
if res.Err == nil {
t.Fatalf("Expected error, but got none")
}
}
12 changes: 12 additions & 0 deletions openstack/networking/v2/extensions/lbaas_v2/l7policies/urls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package l7policies

import "github.com/gophercloud/gophercloud"

const (
rootPath = "lbaas"
resourcePath = "l7policies"
)

func rootURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL(rootPath, resourcePath)
}

0 comments on commit e8992e1

Please sign in to comment.