Skip to content

Commit

Permalink
Octavia l7 rule support - part 1: create (#924)
Browse files Browse the repository at this point in the history
* Octavia l7 rule support - part 1: create

For #832

Octavia l7 rule create API implementation:
https://github.com/openstack/octavia/blob/06bf5c58d5845f684fcaf933605ed112586eefc3/octavia/api/v2/controllers/l7rule.py#L146

* Use project_id instead of tenant_id
  • Loading branch information
lingxiankong authored and jtopjian committed Apr 12, 2018
1 parent d8f9125 commit 3c0e6bd
Show file tree
Hide file tree
Showing 8 changed files with 212 additions and 16 deletions.
24 changes: 24 additions & 0 deletions acceptance/openstack/loadbalancer/v2/loadbalancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,30 @@ func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *l
return policy, nil
}

// CreateL7Rule creates a l7 rule for specified l7 policy.
func CreateL7Rule(t *testing.T, client *gophercloud.ServiceClient, policyID string, lb *loadbalancers.LoadBalancer) (*l7policies.Rule, error) {
t.Logf("Attempting to create l7 rule for policy %s", policyID)

createOpts := l7policies.CreateRuleOpts{
RuleType: l7policies.TypePath,
CompareType: l7policies.CompareTypeStartWith,
Value: "/api",
}

rule, err := l7policies.CreateRule(client, policyID, createOpts).Extract()
if err != nil {
return rule, err
}

t.Logf("Successfully created l7 rule for policy %s", policyID)

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

return rule, nil
}

// DeleteL7Policy will delete a specified l7 policy. A fatal error will occur if
// the l7 policy 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 @@ -128,6 +128,12 @@ func TestLoadbalancersCRUD(t *testing.T) {

tools.PrintResource(t, newPolicy)

// L7 rule
_, err = CreateL7Rule(t, lbClient, newPolicy.ID, lb)
if err != nil {
t.Fatalf("Unable to create l7 rule: %v", err)
}

// Pool
pool, err := CreatePool(t, lbClient, lb)
if err != nil {
Expand Down
13 changes: 13 additions & 0 deletions openstack/loadbalancer/v2/l7policies/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,18 @@ Example to Update a L7Policy
if err != nil {
panic(err)
}
Example to Create a Rule
l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64"
createOpts := l7policies.CreateRuleOpts{
RuleType: l7policies.TypePath,
CompareType: l7policies.CompareTypeRegex,
Value: "/images*",
}
rule, err := l7policies.CreateRule(lbClient, l7policyID, createOpts).Extract()
if err != nil {
panic(err)
}
*/
package l7policies
46 changes: 41 additions & 5 deletions openstack/loadbalancer/v2/l7policies/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,6 @@ type CreateOpts struct {
// 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"`
Expand Down Expand Up @@ -96,7 +92,7 @@ type ListOpts struct {
Name string `q:"name"`
ListenerID string `q:"listener_id"`
Action string `q:"action"`
TenantID string `q:"tenant_id"`
ProjectID string `q:"project_id"`
RedirectPoolID string `q:"redirect_pool_id"`
RedirectURL string `q:"redirect_url"`
ID string `q:"id"`
Expand Down Expand Up @@ -187,3 +183,43 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r
})
return
}

// CreateRuleOpts is the common options struct used in this package's CreateRule
// operation.
type CreateRuleOpts struct {
// The L7 rule type. One of COOKIE, FILE_TYPE, HEADER, HOST_NAME, or PATH.
RuleType RuleType `json:"type" required:"true"`

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

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

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

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

// 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,omitempty"`
}

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

// CreateRule will create and associate a Rule with a particular L7Policy.
func CreateRule(c *gophercloud.ServiceClient, policyID string, opts CreateRuleOpts) (r CreateRuleResult) {
b, err := opts.ToRuleCreateMap()
if err != nil {
r.Err = err
return
}
_, r.Err = c.Post(ruleRootURL(c, policyID), b, &r.Body, nil)
return
}
27 changes: 23 additions & 4 deletions openstack/loadbalancer/v2/l7policies/results.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ type L7Policy struct {
// 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.
// 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.
TenantID string `json:"tenant_id"`
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.
Expand Down Expand Up @@ -59,9 +59,9 @@ type Rule struct {
// 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.
// 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.
TenantID string `json:"tenant_id"`
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"`
Expand Down Expand Up @@ -147,3 +147,22 @@ type DeleteResult struct {
type UpdateResult struct {
commonResult
}

type commonRuleResult struct {
gophercloud.Result
}

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

// CreateRuleResult represents the result of a CreateRule operation.
// Call its Extract method to interpret it as a Rule.
type CreateRuleResult struct {
commonRuleResult
}
60 changes: 53 additions & 7 deletions openstack/loadbalancer/v2/l7policies/testing/fixtures.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const SingleL7PolicyBody = `
"redirect_url": "http://www.example.com",
"action": "REDIRECT_TO_URL",
"position": 1,
"tenant_id": "e3cd678b11784734bc366148aa37580e",
"project_id": "e3cd678b11784734bc366148aa37580e",
"id": "8a1412f0-4c32-4257-8b07-af4770b604fd",
"name": "redirect-example.com",
"rules": []
Expand All @@ -37,7 +37,7 @@ var (
Action: "REDIRECT_TO_URL",
Position: 1,
Description: "",
TenantID: "e3cd678b11784734bc366148aa37580e",
ProjectID: "e3cd678b11784734bc366148aa37580e",
RedirectPoolID: "",
RedirectURL: "http://www.example.com",
AdminStateUp: true,
Expand All @@ -50,7 +50,7 @@ var (
Action: "REDIRECT_TO_POOL",
Position: 1,
Description: "",
TenantID: "c1f7910086964990847dc6c8b128f63c",
ProjectID: "c1f7910086964990847dc6c8b128f63c",
RedirectPoolID: "bac433c6-5bea-4311-80da-bd1cd90fbd25",
RedirectURL: "",
AdminStateUp: true,
Expand All @@ -63,12 +63,22 @@ var (
Action: "REDIRECT_TO_URL",
Position: 1,
Description: "Redirect requests to example.com",
TenantID: "e3cd678b11784734bc366148aa37580e",
ProjectID: "e3cd678b11784734bc366148aa37580e",
RedirectPoolID: "",
RedirectURL: "http://www.new-example.com",
AdminStateUp: true,
Rules: []l7policies.Rule{},
}
RulePath = l7policies.Rule{
ID: "16621dbb-a736-4888-a57a-3ecd53df784c",
RuleType: "PATH",
CompareType: "REGEX",
Value: "/images*",
ProjectID: "e3cd678b11784734bc366148aa37580e",
Key: "",
Invert: false,
AdminStateUp: true,
}
)

// HandleL7PolicyCreationSuccessfully sets up the test server to respond to a l7policy creation request
Expand Down Expand Up @@ -101,7 +111,7 @@ const L7PoliciesListBody = `
"description": "",
"admin_state_up": true,
"rules": [],
"tenant_id": "e3cd678b11784734bc366148aa37580e",
"project_id": "e3cd678b11784734bc366148aa37580e",
"listener_id": "023f2e34-7806-443b-bfae-16c324569a3d",
"redirect_url": "http://www.example.com",
"action": "REDIRECT_TO_URL",
Expand All @@ -114,7 +124,7 @@ const L7PoliciesListBody = `
"description": "",
"admin_state_up": true,
"rules": [],
"tenant_id": "c1f7910086964990847dc6c8b128f63c",
"project_id": "c1f7910086964990847dc6c8b128f63c",
"listener_id": "be3138a3-5cf7-4513-a4c2-bb137e668bab",
"action": "REDIRECT_TO_POOL",
"position": 1,
Expand All @@ -136,7 +146,7 @@ const PostUpdateL7PolicyBody = `
"redirect_url": "http://www.new-example.com",
"action": "REDIRECT_TO_URL",
"position": 1,
"tenant_id": "e3cd678b11784734bc366148aa37580e",
"project_id": "e3cd678b11784734bc366148aa37580e",
"id": "8a1412f0-4c32-4257-8b07-af4770b604fd",
"name": "NewL7PolicyName",
"rules": []
Expand Down Expand Up @@ -203,3 +213,39 @@ func HandleL7PolicyUpdateSuccessfully(t *testing.T) {
fmt.Fprintf(w, PostUpdateL7PolicyBody)
})
}

// SingleRuleBody is the canned body of a Get request on an existing rule.
const SingleRuleBody = `
{
"rule": {
"compare_type": "REGEX",
"invert": false,
"admin_state_up": true,
"value": "/images*",
"key": null,
"project_id": "e3cd678b11784734bc366148aa37580e",
"type": "PATH",
"id": "16621dbb-a736-4888-a57a-3ecd53df784c"
}
}
`

// HandleRuleCreationSuccessfully sets up the test server to respond to a rule creation request
// with a given response.
func HandleRuleCreationSuccessfully(t *testing.T, response string) {
th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules", 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, `{
"rule": {
"compare_type": "REGEX",
"type": "PATH",
"value": "/images*"
}
}`)

w.WriteHeader(http.StatusAccepted)
w.Header().Add("Content-Type", "application/json")
fmt.Fprintf(w, response)
})
}
47 changes: 47 additions & 0 deletions openstack/loadbalancer/v2/l7policies/testing/requests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,50 @@ func TestUpdateL7Policy(t *testing.T) {

th.CheckDeepEquals(t, L7PolicyUpdated, *actual)
}

func TestCreateRule(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleRuleCreationSuccessfully(t, SingleRuleBody)

actual, err := l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{
RuleType: l7policies.TypePath,
CompareType: l7policies.CompareTypeRegex,
Value: "/images*",
}).Extract()
th.AssertNoErr(t, err)

th.CheckDeepEquals(t, RulePath, *actual)
}

func TestRequiredRuleCreateOpts(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()

res := l7policies.CreateRule(fake.ServiceClient(), "", l7policies.CreateRuleOpts{})
if res.Err == nil {
t.Fatalf("Expected error, got none")
}
res = l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{
RuleType: l7policies.TypePath,
})
if res.Err == nil {
t.Fatalf("Expected error, but got none")
}
res = l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{
RuleType: l7policies.RuleType("invalid"),
CompareType: l7policies.CompareTypeRegex,
Value: "/images*",
})
if res.Err == nil {
t.Fatalf("Expected error, but got none")
}
res = l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{
RuleType: l7policies.TypePath,
CompareType: l7policies.CompareType("invalid"),
Value: "/images*",
})
if res.Err == nil {
t.Fatalf("Expected error, but got none")
}
}
5 changes: 5 additions & 0 deletions openstack/loadbalancer/v2/l7policies/urls.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "github.com/gophercloud/gophercloud"
const (
rootPath = "lbaas"
resourcePath = "l7policies"
rulePath = "rules"
)

func rootURL(c *gophercloud.ServiceClient) string {
Expand All @@ -14,3 +15,7 @@ func rootURL(c *gophercloud.ServiceClient) string {
func resourceURL(c *gophercloud.ServiceClient, id string) string {
return c.ServiceURL(rootPath, resourcePath, id)
}

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

0 comments on commit 3c0e6bd

Please sign in to comment.