Skip to content

Commit

Permalink
Add ovh_iploadbalancing_http_route
Browse files Browse the repository at this point in the history
  • Loading branch information
Elie CHARRA committed Jun 1, 2018
1 parent 4631eb4 commit d47a944
Show file tree
Hide file tree
Showing 5 changed files with 352 additions and 0 deletions.
14 changes: 14 additions & 0 deletions ovh/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package ovh
import (
"bytes"
"fmt"

"github.com/hashicorp/terraform/helper/schema"
"github.com/ovh/go-ovh/ovh"
)

func validateStringEnum(value string, enum []string) error {
Expand Down Expand Up @@ -62,3 +65,14 @@ func conditionalAttributeBool(buff *bytes.Buffer, name string, val *bool) {
buff.WriteString(fmt.Sprintf(" %s = %v\n", name, *val))
}
}

// CheckDeleted checks the error to see if it's a 404 (Not Found) and, if so,
// sets the resource ID to the empty string instead of throwing an error.
func CheckDeleted(d *schema.ResourceData, err error, endpoint string) error {
if err.(*ovh.APIError).Code == 404 {
d.SetId("")
return nil
}

return fmt.Errorf("calling %s:\n\t %s", endpoint, err.Error())
}
1 change: 1 addition & 0 deletions ovh/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func Provider() terraform.ResourceProvider {
ResourcesMap: map[string]*schema.Resource{
"ovh_iploadbalancing_tcp_farm": resourceIpLoadbalancingTcpFarm(),
"ovh_iploadbalancing_tcp_farm_server": resourceIpLoadbalancingTcpFarmServer(),
"ovh_iploadbalancing_http_route": resourceIPLoadbalancingRouteHTTP(),
"ovh_domain_zone_record": resourceOvhDomainZoneRecord(),
"ovh_domain_zone_redirection": resourceOvhDomainZoneRedirection(),
// New naming schema (issue #23)
Expand Down
167 changes: 167 additions & 0 deletions ovh/resource_ovh_iploadbalancing_http_route.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package ovh

import (
"fmt"

"github.com/hashicorp/terraform/helper/schema"
)

// IPLoadbalancingRouteHTTPAction Action triggered when all rules match
type IPLoadbalancingRouteHTTPAction struct {
Target string `json:"target,omitempty"` // Farm ID for "farm" action type or URL template for "redirect" action. You may use ${uri}, ${protocol}, ${host}, ${port} and ${path} variables in redirect target
Status int `json:"status,omitempty"` // HTTP status code for "redirect" and "reject" actions
Type string `json:"type,omitempty"` // Action to trigger if all the rules of this route matches
}

//IPLoadbalancingRouteHTTP HTTP Route
type IPLoadbalancingRouteHTTP struct {
Status string `json:"status,omitempty"` //Route status. Routes in "ok" state are ready to operate
Weight int `json:"weight,omitempty"` //Route priority ([0..255]). 0 if null. Highest priority routes are evaluated first. Only the first matching route will trigger an action
Action *IPLoadbalancingRouteHTTPAction `json:"action,omitempty"` //Action triggered when all rules match
RouteID int `json:"routeId,omitempty"` //Id of your route
DisplayName string `json:"displayName,omitempty"` //Human readable name for your route, this field is for you
FrontendID int `json:"frontendId,omitempty"` //Route traffic for this frontend
}

func resourceIPLoadbalancingRouteHTTP() *schema.Resource {
return &schema.Resource{
Create: resourceIPLoadbalancingRouteHTTPCreate,
Read: resourceIPLoadbalancingRouteHTTPRead,
Update: resourceIPLoadbalancingRouteHTTPUpdate,
Delete: resourceIPLoadbalancingRouteHTTPDelete,

Schema: map[string]*schema.Schema{
"service_name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"action": &schema.Schema{
Type: schema.TypeSet,
Required: true,
ForceNew: false,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"status": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},
"target": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"type": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
},
},
},
"display_name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"frontend_id": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"weight": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},
},
}
}

func resourceIPLoadbalancingRouteHTTPCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

action := &IPLoadbalancingRouteHTTPAction{}
actionSet := d.Get("action").(*schema.Set).List()[0].(map[string]interface{})

action.Status = actionSet["status"].(int)
action.Target = actionSet["target"].(string)
action.Type = actionSet["type"].(string)

route := &IPLoadbalancingRouteHTTP{
Action: action,
DisplayName: d.Get("display_name").(string),
FrontendID: d.Get("frontend_id").(int),
Weight: d.Get("weight").(int),
}

service := d.Get("service_name").(string)
resp := &IPLoadbalancingRouteHTTP{}
endpoint := fmt.Sprintf("/ipLoadbalancing/%s/http/route", service)

err := config.OVHClient.Post(endpoint, route, resp)
if err != nil {
return fmt.Errorf("calling POST %s :\n\t %s", endpoint, err.Error())
}

d.SetId(fmt.Sprintf("%d", resp.RouteID))

return nil
}

func resourceIPLoadbalancingRouteHTTPRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
service := d.Get("service_name").(string)
r := &IPLoadbalancingRouteHTTP{}
endpoint := fmt.Sprintf("/ipLoadbalancing/%s/http/route/%s", service, d.Id())

err := config.OVHClient.Get(endpoint, &r)
if err != nil {
return CheckDeleted(d, err, endpoint)
}

d.Set("status", r.Status)
d.Set("weight", r.Weight)
d.Set("display_name", r.DisplayName)
d.Set("frontend_id", r.FrontendID)

return nil
}

func resourceIPLoadbalancingRouteHTTPUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
service := d.Get("service_name").(string)
endpoint := fmt.Sprintf("/ipLoadbalancing/%s/http/route/%s", service, d.Id())

action := &IPLoadbalancingRouteHTTPAction{}
actionSet := d.Get("action").(*schema.Set).List()[0].(map[string]interface{})

action.Status = actionSet["status"].(int)
action.Target = actionSet["target"].(string)
action.Type = actionSet["type"].(string)

route := &IPLoadbalancingRouteHTTP{
Action: action,
DisplayName: d.Get("display_name").(string),
FrontendID: d.Get("frontend_id").(int),
Weight: d.Get("weight").(int),
}

err := config.OVHClient.Put(endpoint, route, nil)
if err != nil {
return fmt.Errorf("calling %s:\n\t %s", endpoint, err.Error())
}

return nil
}

func resourceIPLoadbalancingRouteHTTPDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

service := d.Get("service_name").(string)
r := &IPLoadbalancingRouteHTTP{}
endpoint := fmt.Sprintf("/ipLoadbalancing/%s/http/route/%s", service, d.Id())

err := config.OVHClient.Delete(endpoint, &r)
if err != nil {
return fmt.Errorf("Error calling %s: %s \n", endpoint, err.Error())
}

return nil
}
117 changes: 117 additions & 0 deletions ovh/resource_ovh_iploadbalancing_http_route_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package ovh

import (
"fmt"
"os"
"reflect"
"testing"

"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)

type TestAccIPLoadbalancingRouteHTTPActionResponse struct {
Target string `json:"target,omitempty"`
Status int `json:"status,omitempty"`
Type string `json:"type"`
}

type TestAccIPLoadbalancingRouteHTTPResponse struct {
Weight int `json:"weight"`
Action TestAccIPLoadbalancingRouteHTTPActionResponse `json:"action"`
RouteID int `json:"routeId"`
DisplayName string `json:"displayName"`
FrontendID int `json:"frontendId"`
}

func (r *TestAccIPLoadbalancingRouteHTTPResponse) Equals(c *TestAccIPLoadbalancingRouteHTTPResponse) bool {
r.RouteID = 0
if reflect.DeepEqual(r, c) {
return true
}
return false
}

func testAccIPLoadbalancingRouteHTTPTestStep(name string, weight int, actionStatus int, actionTarget string, actionType string) resource.TestStep {
expected := &TestAccIPLoadbalancingRouteHTTPResponse{
Weight: weight,
DisplayName: name,
Action: TestAccIPLoadbalancingRouteHTTPActionResponse{
Target: actionTarget,
Status: actionStatus,
Type: actionType,
},
}

config := fmt.Sprintf(`
resource "ovh_iploadbalancing_http_route" "testroute" {
service_name = "%s"
display_name = "%s"
weight = %d
action {
status = %d
target = "%s"
type = "%s"
}
}
`, os.Getenv("OVH_IPLB_SERVICE"), name, weight, actionStatus, actionTarget, actionType)

return resource.TestStep{
Config: config,
Check: resource.ComposeTestCheckFunc(
testAccCheckIPLoadbalancingRouteHTTPMatches(expected),
),
}
}

func TestAccIPLoadbalancingRouteHTTPBasicCreate(t *testing.T) {
resource.Test(t, resource.TestCase{
Providers: testAccProviders,
CheckDestroy: testAccCheckIPLoadbalancingRouteHTTPDestroy,
Steps: []resource.TestStep{
testAccIPLoadbalancingRouteHTTPTestStep("test-route-redirect-https", 0, 302, "https://${host}${path}${arguments}", "redirect"),
},
})
}

func testAccCheckIPLoadbalancingRouteHTTPMatches(expected *TestAccIPLoadbalancingRouteHTTPResponse) resource.TestCheckFunc {
return func(state *terraform.State) error {
name := "ovh_iploadbalancing_http_route.testroute"
resource, ok := state.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Not found: %s", name)
}
config := testAccProvider.Meta().(*Config)
endpoint := fmt.Sprintf("/ipLoadbalancing/%s/http/route/%s", os.Getenv("OVH_IPLB_SERVICE"), resource.Primary.ID)
response := &TestAccIPLoadbalancingRouteHTTPResponse{}
err := config.OVHClient.Get(endpoint, response)
if err != nil {
return fmt.Errorf("calling GET %s :\n\t %s", endpoint, err.Error())
}
if !response.Equals(expected) {
return fmt.Errorf("%s %s state differs from expected", name, resource.Primary.ID)
}
return nil
}
}

func testAccCheckIPLoadbalancingRouteHTTPDestroy(state *terraform.State) error {
leftovers := false
for _, resource := range state.RootModule().Resources {
if resource.Type != "ovh_iploadbalancing_http_route" {
continue
}

config := testAccProvider.Meta().(*Config)
endpoint := fmt.Sprintf("/ipLoadbalancing/%s/http/route/%s", os.Getenv("OVH_IPLB_SERVICE"), resource.Primary.ID)
err := config.OVHClient.Get(endpoint, nil)
if err == nil {
leftovers = true
}
}
if leftovers {
return fmt.Errorf("IpLoadbalancing route still exists")
}
return nil
}
53 changes: 53 additions & 0 deletions website/docs/r/resource_ovh_iploadbalancing_http_route.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
layout: "ovh"
page_title: "OVH: ovh_iploadbalancing_http_route"
sidebar_current: "docs-ovh-resource-iploadbalancing-http-route"
description: |-
Manage http route for a loadbalancer service.
---

# ovh_iploadbalancing_http_route

Manage http route for a loadbalancer service

## Example Usage

Route which redirect all url to https.

```hcl
resource "ovh_iploadbalancing_http_route" "httpsredirect" {
service_name = "loadbalancer-xxxxxxxxxxxxxxxxxx"
display_name = "Redirect to HTTPS"
weight = 1
action {
status = 302
target = "https://${host}${path}${arguments}"
type = "redirect"
}
}
```

## Argument Reference

The following arguments are supported:

* `service_name` - (Required) The internal name of your IP load balancing
* `display_name` - Human readable name for your route, this field is for you
* `weight` - Route priority ([0..255]). 0 if null. Highest priority routes are evaluated first. Only the first matching route will trigger an action
* `action.status` - HTTP status code for "redirect" and "reject" actions
* `action.target` - Farm ID for "farm" action type or URL template for "redirect" action. You may use ${uri}, ${protocol}, ${host}, ${port} and ${path} variables in redirect target
* `action.type` - (Required) Action to trigger if all the rules of this route matches
* `frontend_id` - Route traffic for this frontend

## Attributes Reference

The following attributes are exported:

* `service_name` - See Argument Reference above.
* `display_name` - See Argument Reference above.
* `weight` - See Argument Reference above.
* `action.status` - See Argument Reference above.
* `action.target` - See Argument Reference above.
* `action.type` - See Argument Reference above.
* `frontend_id` - See Argument Reference above.

0 comments on commit d47a944

Please sign in to comment.