From d47a944f0a035cfe86b59d9ed3c57e67d09ef33f Mon Sep 17 00:00:00 2001 From: Elie CHARRA Date: Tue, 22 May 2018 20:28:55 +0200 Subject: [PATCH 1/5] Add ovh_iploadbalancing_http_route --- ovh/helpers.go | 14 ++ ovh/provider.go | 1 + ...resource_ovh_iploadbalancing_http_route.go | 167 ++++++++++++++++++ ...rce_ovh_iploadbalancing_http_route_test.go | 117 ++++++++++++ ...ce_ovh_iploadbalancing_http_route.markdown | 53 ++++++ 5 files changed, 352 insertions(+) create mode 100644 ovh/resource_ovh_iploadbalancing_http_route.go create mode 100644 ovh/resource_ovh_iploadbalancing_http_route_test.go create mode 100644 website/docs/r/resource_ovh_iploadbalancing_http_route.markdown diff --git a/ovh/helpers.go b/ovh/helpers.go index 3c2252da0..e5924bc73 100644 --- a/ovh/helpers.go +++ b/ovh/helpers.go @@ -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 { @@ -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()) +} diff --git a/ovh/provider.go b/ovh/provider.go index 88222280a..24c7ce939 100644 --- a/ovh/provider.go +++ b/ovh/provider.go @@ -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) diff --git a/ovh/resource_ovh_iploadbalancing_http_route.go b/ovh/resource_ovh_iploadbalancing_http_route.go new file mode 100644 index 000000000..e529b0588 --- /dev/null +++ b/ovh/resource_ovh_iploadbalancing_http_route.go @@ -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 +} diff --git a/ovh/resource_ovh_iploadbalancing_http_route_test.go b/ovh/resource_ovh_iploadbalancing_http_route_test.go new file mode 100644 index 000000000..2b1893ce1 --- /dev/null +++ b/ovh/resource_ovh_iploadbalancing_http_route_test.go @@ -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 +} diff --git a/website/docs/r/resource_ovh_iploadbalancing_http_route.markdown b/website/docs/r/resource_ovh_iploadbalancing_http_route.markdown new file mode 100644 index 000000000..c8c143c80 --- /dev/null +++ b/website/docs/r/resource_ovh_iploadbalancing_http_route.markdown @@ -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. From 845a89101225536cc9c3becd07573f85adfd9aea Mon Sep 17 00:00:00 2001 From: Elie CHARRA Date: Wed, 23 May 2018 19:25:21 +0200 Subject: [PATCH 2/5] Add ovh_iploadbalancing_http_route_rule --- ovh/provider.go | 1 + ...rce_ovh_iploadbalancing_http_route_rule.go | 155 ++++++++++++++ ...vh_iploadbalancing_http_route_rule_test.go | 192 ++++++++++++++++++ ...h_iploadbalancing_http_route_rule.markdown | 81 ++++++++ 4 files changed, 429 insertions(+) create mode 100644 ovh/resource_ovh_iploadbalancing_http_route_rule.go create mode 100644 ovh/resource_ovh_iploadbalancing_http_route_rule_test.go create mode 100644 website/docs/r/resource_ovh_iploadbalancing_http_route_rule.markdown diff --git a/ovh/provider.go b/ovh/provider.go index 24c7ce939..e569084b6 100644 --- a/ovh/provider.go +++ b/ovh/provider.go @@ -55,6 +55,7 @@ func Provider() terraform.ResourceProvider { "ovh_iploadbalancing_tcp_farm": resourceIpLoadbalancingTcpFarm(), "ovh_iploadbalancing_tcp_farm_server": resourceIpLoadbalancingTcpFarmServer(), "ovh_iploadbalancing_http_route": resourceIPLoadbalancingRouteHTTP(), + "ovh_iploadbalancing_http_route_rule": resourceIPLoadbalancingRouteHTTPRule(), "ovh_domain_zone_record": resourceOvhDomainZoneRecord(), "ovh_domain_zone_redirection": resourceOvhDomainZoneRedirection(), // New naming schema (issue #23) diff --git a/ovh/resource_ovh_iploadbalancing_http_route_rule.go b/ovh/resource_ovh_iploadbalancing_http_route_rule.go new file mode 100644 index 000000000..c65bb3aa3 --- /dev/null +++ b/ovh/resource_ovh_iploadbalancing_http_route_rule.go @@ -0,0 +1,155 @@ +package ovh + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" +) + +//IPLoadbalancingRouteHTTPRule HTTP Route Rule +type IPLoadbalancingRouteHTTPRule struct { + RuleID int `json:"ruleId,omitempty"` //Id of your rule + RouteID int `json:"routeId,omitempty"` //Id of your route + DisplayName string `json:"displayName,omitempty"` //Human readable name for your rule + Field string `json:"field,omitempty"` //Name of the field to match like "protocol" or "host". See "/ipLoadbalancing/{serviceName}/availableRouteRules" for a list of available rules + Match string `json:"match,omitempty"` //Matching operator. Not all operators are available for all fields. See "/ipLoadbalancing/{serviceName}/availableRouteRules" + Negate bool `json:"negate,omitempty"` //Invert the matching operator effect + Pattern string `json:"pattern,omitempty"` //Value to match against this match. Interpretation if this field depends on the match and field + SubField string `json:"subField,omitempty"` //Name of sub-field, if applicable. This may be a Cookie or Header name for instance +} + +func resourceIPLoadbalancingRouteHTTPRule() *schema.Resource { + return &schema.Resource{ + Create: resourceIPLoadbalancingRouteHTTPRuleCreate, + Read: resourceIPLoadbalancingRouteHTTPRuleRead, + Update: resourceIPLoadbalancingRouteHTTPRuleUpdate, + Delete: resourceIPLoadbalancingRouteHTTPRuleDelete, + + Schema: map[string]*schema.Schema{ + "service_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "route_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "display_name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "field": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "match": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + err := validateStringEnum(v.(string), []string{"contains", "endswith", "exists", "in", "internal", "is", "matches", "startswith"}) + if err != nil { + errors = append(errors, err) + } + return + }, + }, + "negate": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + }, + "pattern": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "sub_field": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + } +} + +func resourceIPLoadbalancingRouteHTTPRuleCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + rule := &IPLoadbalancingRouteHTTPRule{ + DisplayName: d.Get("display_name").(string), + Field: d.Get("field").(string), + Match: d.Get("match").(string), + Negate: d.Get("negate").(bool), + Pattern: d.Get("pattern").(string), + SubField: d.Get("sub_field").(string), + } + + service := d.Get("service_name").(string) + routeID := d.Get("route_id").(string) + resp := &IPLoadbalancingRouteHTTPRule{} + endpoint := fmt.Sprintf("/ipLoadbalancing/%s/http/route/%s/rule", service, routeID) + + err := config.OVHClient.Post(endpoint, rule, resp) + if err != nil { + return fmt.Errorf("calling POST %s :\n\t %s", endpoint, err.Error()) + } + + d.SetId(fmt.Sprintf("%d", resp.RuleID)) + + return nil +} + +func resourceIPLoadbalancingRouteHTTPRuleRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + service := d.Get("service_name").(string) + routeID := d.Get("route_id").(string) + r := &IPLoadbalancingRouteHTTPRule{} + endpoint := fmt.Sprintf("/ipLoadbalancing/%s/http/route/%s/rule/%s", service, routeID, d.Id()) + + err := config.OVHClient.Get(endpoint, &r) + if err != nil { + return CheckDeleted(d, err, endpoint) + } + + return nil +} + +func resourceIPLoadbalancingRouteHTTPRuleUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + service := d.Get("service_name").(string) + routeID := d.Get("route_id").(string) + + endpoint := fmt.Sprintf("/ipLoadbalancing/%s/http/route/%s/rule/%s", service, routeID, d.Id()) + + rule := &IPLoadbalancingRouteHTTPRule{ + DisplayName: d.Get("display_name").(string), + Field: d.Get("field").(string), + Match: d.Get("match").(string), + Negate: d.Get("negate").(bool), + Pattern: d.Get("pattern").(string), + SubField: d.Get("sub_field").(string), + } + + err := config.OVHClient.Put(endpoint, rule, nil) + if err != nil { + return fmt.Errorf("calling %s:\n\t %s", endpoint, err.Error()) + } + + return nil +} + +func resourceIPLoadbalancingRouteHTTPRuleDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + service := d.Get("service_name").(string) + routeID := d.Get("route_id").(string) + + r := &IPLoadbalancingRouteHTTPRule{} + endpoint := fmt.Sprintf("/ipLoadbalancing/%s/http/route/%s/rule/%s", service, routeID, d.Id()) + + err := config.OVHClient.Delete(endpoint, &r) + if err != nil { + return fmt.Errorf("Error calling %s: %s \n", endpoint, err.Error()) + } + + return nil +} diff --git a/ovh/resource_ovh_iploadbalancing_http_route_rule_test.go b/ovh/resource_ovh_iploadbalancing_http_route_rule_test.go new file mode 100644 index 000000000..f7dbbef71 --- /dev/null +++ b/ovh/resource_ovh_iploadbalancing_http_route_rule_test.go @@ -0,0 +1,192 @@ +package ovh + +import ( + "bytes" + "fmt" + "os" + "reflect" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +var TestAccIPLoadbalancingRouteHTTPRulePlan = [][]map[string]interface{}{ + { + {"DisplayName": "Test rule", "Field": "header", "Match": "is", "Negate": false, "Pattern": "example.com", "SubField": "Host"}, + }, +} + +type TestAccIPLoadbalancingRouteHTTPRule struct { + ServiceName string + RuleID int `json:"ruleId"` + RouteID int `json:"routeId"` + DisplayName string `json:"displayName"` + Field string `json:"field"` + Match string `json:"match"` + Negate bool `json:"negate"` + Pattern string `json:"pattern"` + SubField string `json:"subField"` +} + +type TestAccIPLoadbalancingRouteHTTPRuleWrapper struct { + Expected *TestAccIPLoadbalancingRouteHTTPRule +} + +func (w *TestAccIPLoadbalancingRouteHTTPRuleWrapper) Config() string { + var config bytes.Buffer + config.WriteString(fmt.Sprintf(` + resource "ovh_iploadbalancing_http_route" "testroute" { + service_name = "%s" + display_name = "Test route" + weight = 0 + + action { + status = 302 + target = "http://example.com" + type = "redirect" + } + } + + resource "ovh_iploadbalancing_http_route_rule" "testrule" { + service_name = "%s" + route_id = "${ovh_iploadbalancing_http_route.testroute.id}" + display_name = "%s" + field = "%s" + match = "%s" + negate = %t + pattern = "%s" + sub_field = "%s" + } + `, w.Expected.ServiceName, + w.Expected.ServiceName, + w.Expected.DisplayName, + w.Expected.Field, + w.Expected.Match, + w.Expected.Negate, + w.Expected.Pattern, + w.Expected.SubField)) + + return config.String() +} + +func (rule *TestAccIPLoadbalancingRouteHTTPRule) MustEqual(compared *TestAccIPLoadbalancingRouteHTTPRule) error { + if !reflect.DeepEqual(rule.DisplayName, compared.DisplayName) { + return fmt.Errorf("DisplayName differs") + } + if !reflect.DeepEqual(rule.Field, compared.Field) { + return fmt.Errorf("Field differs") + } + if !reflect.DeepEqual(rule.Match, compared.Match) { + return fmt.Errorf("Match differs") + } + if !reflect.DeepEqual(rule.Negate, compared.Negate) { + return fmt.Errorf("Negate differs") + } + if !reflect.DeepEqual(rule.Pattern, compared.Pattern) { + return fmt.Errorf("Pattern differs") + } + if !reflect.DeepEqual(rule.SubField, compared.SubField) { + return fmt.Errorf("SubField differs") + } + + return nil +} + +type TestAccIPLoadbalancingRouteHTTPRuleStep struct { + Response *TestAccIPLoadbalancingRouteHTTPRule + Expected *TestAccIPLoadbalancingRouteHTTPRule +} + +func (w *TestAccIPLoadbalancingRouteHTTPRuleWrapper) TestStep(c map[string]interface{}) resource.TestStep { + if val, ok := c["DisplayName"]; ok { + w.Expected.DisplayName = val.(string) + } + if val, ok := c["Field"]; ok { + w.Expected.Field = val.(string) + } + if val, ok := c["Match"]; ok { + w.Expected.Match = val.(string) + } + if val, ok := c["Negate"]; ok { + w.Expected.Negate = val.(bool) + } + if val, ok := c["Pattern"]; ok { + w.Expected.Pattern = val.(string) + } + if val, ok := c["SubField"]; ok { + w.Expected.SubField = val.(string) + } + expected := *w.Expected + + return resource.TestStep{ + Config: w.Config(), + Check: resource.ComposeTestCheckFunc( + w.TestCheck(expected), + ), + } +} + +func (w *TestAccIPLoadbalancingRouteHTTPRuleWrapper) TestCheck(expected TestAccIPLoadbalancingRouteHTTPRule) resource.TestCheckFunc { + return func(state *terraform.State) error { + response := &TestAccIPLoadbalancingRouteHTTPRule{} + name := "ovh_iploadbalancing_http_route_rule.testrule" + 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/rule/%s", os.Getenv("OVH_IPLB_SERVICE"), resource.Primary.Attributes["route_id"], resource.Primary.ID) + err := config.OVHClient.Get(endpoint, response) + if err != nil { + return fmt.Errorf("calling GET %s :\n\t %s", endpoint, err.Error()) + } + + err = expected.MustEqual(response) + if err != nil { + return fmt.Errorf("%s %s state differs from expected : %s", name, resource.Primary.ID, err.Error()) + } + return nil + } +} + +func (w *TestAccIPLoadbalancingRouteHTTPRuleWrapper) TestDestroy(state *terraform.State) error { + leftovers := false + for _, resource := range state.RootModule().Resources { + if resource.Type != "ovh_iploadbalancing_http_route_rule" { + continue + } + + config := testAccProvider.Meta().(*Config) + endpoint := fmt.Sprintf("/ipLoadbalancing/%s/http/route/%d/rule/%s", os.Getenv("OVH_IPLB_SERVICE"), w.Expected.RouteID, resource.Primary.ID) + err := config.OVHClient.Get(endpoint, nil) + if err == nil { + leftovers = true + } + } + if leftovers { + return fmt.Errorf("IpLoadbalancing http route rule still exists") + } + return nil +} + +func newTestAccIPLoadbalancingRouteHTTPRuleWrapper() *TestAccIPLoadbalancingRouteHTTPRuleWrapper { + return &TestAccIPLoadbalancingRouteHTTPRuleWrapper{ + Expected: &TestAccIPLoadbalancingRouteHTTPRule{ServiceName: os.Getenv("OVH_IPLB_SERVICE")}, + } +} + +func TestAccIpLoadbalancingRouteHTTPRuleBasicCreate(t *testing.T) { + for _, plan := range TestAccIPLoadbalancingRouteHTTPRulePlan { + w := newTestAccIPLoadbalancingRouteHTTPRuleWrapper() + var steps []resource.TestStep + for _, tcase := range plan { + steps = append(steps, w.TestStep(tcase)) + } + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + CheckDestroy: w.TestDestroy, + Steps: steps, + }) + } +} diff --git a/website/docs/r/resource_ovh_iploadbalancing_http_route_rule.markdown b/website/docs/r/resource_ovh_iploadbalancing_http_route_rule.markdown new file mode 100644 index 000000000..f08fcbacb --- /dev/null +++ b/website/docs/r/resource_ovh_iploadbalancing_http_route_rule.markdown @@ -0,0 +1,81 @@ +--- +layout: "ovh" +page_title: "OVH: ovh_iploadbalancing_http_route_rule" +sidebar_current: "docs-ovh-resource-iploadbalancing-http-route" +description: |- + Manage rules for HTTP route. +--- + +# ovh_iploadbalancing_http_route_rule + +Manage rules for HTTP route. + +## Example Usage + +Route which redirect all url to https for example.com (Vhost). + +```hcl +resource "ovh_iploadbalancing_http_route" "httpsredirect" { + service_name = "loadbalancer-xxxxxxxxxxxxxxxxxx" + display_name = "Redirect to HTTPS" + weight = 1 + frontend_id = 11111 + + action { + status = 302 + target = "https://$${host}$${path}$${arguments}" + type = "redirect" + } +} + +resource "ovh_iploadbalancing_http_route_rule" "examplerule" { + service_name = "loadbalancer-xxxxxxxxxxxxxxxxxx" + route_id = "${ovh_iploadbalancing_http_route.httpsredirect.id}" + display_name = "Match example.com host" + field = "host" + match = "is" + negate = false + pattern = "example.com" +} +``` + +Rule which match a specific header (same effect as the host match above). + +```hcl +resource "ovh_iploadbalancing_http_route_rule" "examplerule" { + service_name = "loadbalancer-xxxxxxxxxxxxxxxxxx" + route_id = "${ovh_iploadbalancing_http_route.httpsredirect.id}" + display_name = "Match example.com Host header" + field = "headers" + match = "is" + negate = false + pattern = "example.com" + sub_field = "Host" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `service_name` - (Required) The internal name of your IP load balancing +* `route_id` - (Required) The route to apply this rule +* `display_name` - Human readable name for your rule, this field is for you +* `field` - (Required) Name of the field to match like "protocol" or "host". See "/ipLoadbalancing/{serviceName}/availableRouteRules" for a list of available rules +* `match` - (Required) Matching operator. Not all operators are available for all fields. See "/ipLoadbalancing/{serviceName}/availableRouteRules" +* `negate` - Invert the matching operator effect +* `pattern` - Value to match against this match. Interpretation if this field depends on the match and field +* `sub_field` - Name of sub-field, if applicable. This may be a Cookie or Header name for instance + +## Attributes Reference + +The following attributes are exported: + +* `service_name` - See Argument Reference above. +* `route_id` - See Argument Reference above. +* `display_name` - See Argument Reference above. +* `field` - See Argument Reference above. +* `match` - See Argument Reference above. +* `negate` - See Argument Reference above. +* `pattern` - See Argument Reference above. +* `sub_field` - See Argument Reference above. From 5d4f0eefb4a557a6e2dff5d68b54fc99ce9af5fa Mon Sep 17 00:00:00 2001 From: Yann DEGAT Date: Mon, 11 Jun 2018 13:23:36 +0200 Subject: [PATCH 3/5] Add provider test check for iplb --- ovh/provider_test.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/ovh/provider_test.go b/ovh/provider_test.go index 6d513ea7f..ae75f3aa9 100644 --- a/ovh/provider_test.go +++ b/ovh/provider_test.go @@ -70,6 +70,11 @@ func testAccPreCheck(t *testing.T) { t.Fatal("OVH_ZONE must be set for acceptance tests") } + v = os.Getenv("OVH_IPLB_SERVICE") + if v == "" { + t.Fatal("OVH_IPLB_SERVICE must be set for acceptance tests") + } + if testAccOVHClient == nil { config := Config{ Endpoint: os.Getenv("OVH_ENDPOINT"), @@ -122,3 +127,20 @@ func testAccCheckPublicCloudExists(t *testing.T) { t.Logf("Read Cloud Project %s -> status: '%s', desc: '%s'", endpoint, r.Status, r.Description) } + +func testAccCheckIpLoadbalancingExists(t *testing.T) { + type iplbResponse struct { + ServiceName string `json:"serviceName"` + State string `json:"state"` + } + + r := iplbResponse{} + + endpoint := fmt.Sprintf("/ipLoadbalancing/%s", os.Getenv("OVH_IPLB_SERVICE")) + + err := testAccOVHClient.Get(endpoint, &r) + if err != nil { + t.Fatalf("Error: %q\n", err) + } + t.Logf("Read IPLB service %s -> state: '%s', serviceName: '%s'", endpoint, r.State, r.ServiceName) +} From 1af9600a084bd78ed5ffd6500a2f5a3bdd49fc89 Mon Sep 17 00:00:00 2001 From: Yann DEGAT Date: Mon, 11 Jun 2018 13:33:04 +0200 Subject: [PATCH 4/5] Follow conventions on iplb http resources - read resource after create/update - resource schema as first go definition --- ...resource_ovh_iploadbalancing_http_route.go | 38 +++++++++---------- ...rce_ovh_iploadbalancing_http_route_rule.go | 28 +++++++------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/ovh/resource_ovh_iploadbalancing_http_route.go b/ovh/resource_ovh_iploadbalancing_http_route.go index e529b0588..493c2d1dc 100644 --- a/ovh/resource_ovh_iploadbalancing_http_route.go +++ b/ovh/resource_ovh_iploadbalancing_http_route.go @@ -6,23 +6,6 @@ import ( "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, @@ -74,6 +57,23 @@ func resourceIPLoadbalancingRouteHTTP() *schema.Resource { } } +// 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 resourceIPLoadbalancingRouteHTTPCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) @@ -102,7 +102,7 @@ func resourceIPLoadbalancingRouteHTTPCreate(d *schema.ResourceData, meta interfa d.SetId(fmt.Sprintf("%d", resp.RouteID)) - return nil + return resourceIPLoadbalancingRouteHTTPRead(d, meta) } func resourceIPLoadbalancingRouteHTTPRead(d *schema.ResourceData, meta interface{}) error { @@ -148,7 +148,7 @@ func resourceIPLoadbalancingRouteHTTPUpdate(d *schema.ResourceData, meta interfa return fmt.Errorf("calling %s:\n\t %s", endpoint, err.Error()) } - return nil + return resourceIPLoadbalancingRouteHTTPRead(d, meta) } func resourceIPLoadbalancingRouteHTTPDelete(d *schema.ResourceData, meta interface{}) error { diff --git a/ovh/resource_ovh_iploadbalancing_http_route_rule.go b/ovh/resource_ovh_iploadbalancing_http_route_rule.go index c65bb3aa3..874bd1c78 100644 --- a/ovh/resource_ovh_iploadbalancing_http_route_rule.go +++ b/ovh/resource_ovh_iploadbalancing_http_route_rule.go @@ -6,18 +6,6 @@ import ( "github.com/hashicorp/terraform/helper/schema" ) -//IPLoadbalancingRouteHTTPRule HTTP Route Rule -type IPLoadbalancingRouteHTTPRule struct { - RuleID int `json:"ruleId,omitempty"` //Id of your rule - RouteID int `json:"routeId,omitempty"` //Id of your route - DisplayName string `json:"displayName,omitempty"` //Human readable name for your rule - Field string `json:"field,omitempty"` //Name of the field to match like "protocol" or "host". See "/ipLoadbalancing/{serviceName}/availableRouteRules" for a list of available rules - Match string `json:"match,omitempty"` //Matching operator. Not all operators are available for all fields. See "/ipLoadbalancing/{serviceName}/availableRouteRules" - Negate bool `json:"negate,omitempty"` //Invert the matching operator effect - Pattern string `json:"pattern,omitempty"` //Value to match against this match. Interpretation if this field depends on the match and field - SubField string `json:"subField,omitempty"` //Name of sub-field, if applicable. This may be a Cookie or Header name for instance -} - func resourceIPLoadbalancingRouteHTTPRule() *schema.Resource { return &schema.Resource{ Create: resourceIPLoadbalancingRouteHTTPRuleCreate, @@ -71,6 +59,18 @@ func resourceIPLoadbalancingRouteHTTPRule() *schema.Resource { } } +//IPLoadbalancingRouteHTTPRule HTTP Route Rule +type IPLoadbalancingRouteHTTPRule struct { + RuleID int `json:"ruleId,omitempty"` //Id of your rule + RouteID int `json:"routeId,omitempty"` //Id of your route + DisplayName string `json:"displayName,omitempty"` //Human readable name for your rule + Field string `json:"field,omitempty"` //Name of the field to match like "protocol" or "host". See "/ipLoadbalancing/{serviceName}/availableRouteRules" for a list of available rules + Match string `json:"match,omitempty"` //Matching operator. Not all operators are available for all fields. See "/ipLoadbalancing/{serviceName}/availableRouteRules" + Negate bool `json:"negate,omitempty"` //Invert the matching operator effect + Pattern string `json:"pattern,omitempty"` //Value to match against this match. Interpretation if this field depends on the match and field + SubField string `json:"subField,omitempty"` //Name of sub-field, if applicable. This may be a Cookie or Header name for instance +} + func resourceIPLoadbalancingRouteHTTPRuleCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) @@ -95,7 +95,7 @@ func resourceIPLoadbalancingRouteHTTPRuleCreate(d *schema.ResourceData, meta int d.SetId(fmt.Sprintf("%d", resp.RuleID)) - return nil + return resourceIPLoadbalancingRouteHTTPRuleRead(d, meta) } func resourceIPLoadbalancingRouteHTTPRuleRead(d *schema.ResourceData, meta interface{}) error { @@ -134,7 +134,7 @@ func resourceIPLoadbalancingRouteHTTPRuleUpdate(d *schema.ResourceData, meta int return fmt.Errorf("calling %s:\n\t %s", endpoint, err.Error()) } - return nil + return resourceIPLoadbalancingRouteHTTPRuleRead(d, meta) } func resourceIPLoadbalancingRouteHTTPRuleDelete(d *schema.ResourceData, meta interface{}) error { From af137a65b66f7abaa18999798033775950e2d93d Mon Sep 17 00:00:00 2001 From: Yann DEGAT Date: Mon, 11 Jun 2018 13:34:53 +0200 Subject: [PATCH 5/5] Fix iplb http route acc tests --- ...vh_iploadbalancing_http_route_rule_test.go | 242 ++++++------------ ...rce_ovh_iploadbalancing_http_route_test.go | 135 ++++------ 2 files changed, 137 insertions(+), 240 deletions(-) diff --git a/ovh/resource_ovh_iploadbalancing_http_route_rule_test.go b/ovh/resource_ovh_iploadbalancing_http_route_rule_test.go index f7dbbef71..7273a431f 100644 --- a/ovh/resource_ovh_iploadbalancing_http_route_rule_test.go +++ b/ovh/resource_ovh_iploadbalancing_http_route_rule_test.go @@ -1,192 +1,114 @@ package ovh import ( - "bytes" "fmt" "os" - "reflect" "testing" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" ) -var TestAccIPLoadbalancingRouteHTTPRulePlan = [][]map[string]interface{}{ - { - {"DisplayName": "Test rule", "Field": "header", "Match": "is", "Negate": false, "Pattern": "example.com", "SubField": "Host"}, - }, +func TestAccIPLoadbalancingRouteHTTPRuleBasicCreate(t *testing.T) { + serviceName := os.Getenv("OVH_IPLB_SERVICE") + displayName := "Test rule" + field := "header" + match := "is" + negate := "false" + pattern := "example.com" + subField := "Host" + + config := fmt.Sprintf( + testAccCheckOvhIpLoadbalancingHttpRouteRuleConfig_basic, + serviceName, + displayName, + field, + match, + negate, + pattern, + subField, + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccCheckIpLoadbalancingRouteHTTPRulePreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckIPLoadbalancingRouteHTTPRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "ovh_iploadbalancing_http_route.testroute", "service_name", serviceName), + resource.TestCheckResourceAttr( + "ovh_iploadbalancing_http_route.testroute", "display_name", displayName), + resource.TestCheckResourceAttr( + "ovh_iploadbalancing_http_route_rule.testrule", "service_name", serviceName), + resource.TestCheckResourceAttr( + "ovh_iploadbalancing_http_route_rule.testrule", "display_name", displayName), + resource.TestCheckResourceAttr( + "ovh_iploadbalancing_http_route_rule.testrule", "field", field), + resource.TestCheckResourceAttr( + "ovh_iploadbalancing_http_route_rule.testrule", "match", match), + resource.TestCheckResourceAttr( + "ovh_iploadbalancing_http_route_rule.testrule", "negate", negate), + resource.TestCheckResourceAttr( + "ovh_iploadbalancing_http_route_rule.testrule", "pattern", pattern), + resource.TestCheckResourceAttr( + "ovh_iploadbalancing_http_route_rule.testrule", "sub_field", subField), + ), + }, + }, + }) } -type TestAccIPLoadbalancingRouteHTTPRule struct { - ServiceName string - RuleID int `json:"ruleId"` - RouteID int `json:"routeId"` - DisplayName string `json:"displayName"` - Field string `json:"field"` - Match string `json:"match"` - Negate bool `json:"negate"` - Pattern string `json:"pattern"` - SubField string `json:"subField"` +func testAccCheckIpLoadbalancingRouteHTTPRulePreCheck(t *testing.T) { + testAccPreCheck(t) + testAccCheckIpLoadbalancingExists(t) } -type TestAccIPLoadbalancingRouteHTTPRuleWrapper struct { - Expected *TestAccIPLoadbalancingRouteHTTPRule -} - -func (w *TestAccIPLoadbalancingRouteHTTPRuleWrapper) Config() string { - var config bytes.Buffer - config.WriteString(fmt.Sprintf(` - resource "ovh_iploadbalancing_http_route" "testroute" { - service_name = "%s" - display_name = "Test route" - weight = 0 - - action { - status = 302 - target = "http://example.com" - type = "redirect" - } - } - - resource "ovh_iploadbalancing_http_route_rule" "testrule" { - service_name = "%s" - route_id = "${ovh_iploadbalancing_http_route.testroute.id}" - display_name = "%s" - field = "%s" - match = "%s" - negate = %t - pattern = "%s" - sub_field = "%s" - } - `, w.Expected.ServiceName, - w.Expected.ServiceName, - w.Expected.DisplayName, - w.Expected.Field, - w.Expected.Match, - w.Expected.Negate, - w.Expected.Pattern, - w.Expected.SubField)) - - return config.String() -} - -func (rule *TestAccIPLoadbalancingRouteHTTPRule) MustEqual(compared *TestAccIPLoadbalancingRouteHTTPRule) error { - if !reflect.DeepEqual(rule.DisplayName, compared.DisplayName) { - return fmt.Errorf("DisplayName differs") - } - if !reflect.DeepEqual(rule.Field, compared.Field) { - return fmt.Errorf("Field differs") - } - if !reflect.DeepEqual(rule.Match, compared.Match) { - return fmt.Errorf("Match differs") - } - if !reflect.DeepEqual(rule.Negate, compared.Negate) { - return fmt.Errorf("Negate differs") - } - if !reflect.DeepEqual(rule.Pattern, compared.Pattern) { - return fmt.Errorf("Pattern differs") - } - if !reflect.DeepEqual(rule.SubField, compared.SubField) { - return fmt.Errorf("SubField differs") - } - - return nil -} - -type TestAccIPLoadbalancingRouteHTTPRuleStep struct { - Response *TestAccIPLoadbalancingRouteHTTPRule - Expected *TestAccIPLoadbalancingRouteHTTPRule -} - -func (w *TestAccIPLoadbalancingRouteHTTPRuleWrapper) TestStep(c map[string]interface{}) resource.TestStep { - if val, ok := c["DisplayName"]; ok { - w.Expected.DisplayName = val.(string) - } - if val, ok := c["Field"]; ok { - w.Expected.Field = val.(string) - } - if val, ok := c["Match"]; ok { - w.Expected.Match = val.(string) - } - if val, ok := c["Negate"]; ok { - w.Expected.Negate = val.(bool) - } - if val, ok := c["Pattern"]; ok { - w.Expected.Pattern = val.(string) - } - if val, ok := c["SubField"]; ok { - w.Expected.SubField = val.(string) - } - expected := *w.Expected - - return resource.TestStep{ - Config: w.Config(), - Check: resource.ComposeTestCheckFunc( - w.TestCheck(expected), - ), - } -} - -func (w *TestAccIPLoadbalancingRouteHTTPRuleWrapper) TestCheck(expected TestAccIPLoadbalancingRouteHTTPRule) resource.TestCheckFunc { - return func(state *terraform.State) error { - response := &TestAccIPLoadbalancingRouteHTTPRule{} - name := "ovh_iploadbalancing_http_route_rule.testrule" - 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/rule/%s", os.Getenv("OVH_IPLB_SERVICE"), resource.Primary.Attributes["route_id"], resource.Primary.ID) - err := config.OVHClient.Get(endpoint, response) - if err != nil { - return fmt.Errorf("calling GET %s :\n\t %s", endpoint, err.Error()) - } - - err = expected.MustEqual(response) - if err != nil { - return fmt.Errorf("%s %s state differs from expected : %s", name, resource.Primary.ID, err.Error()) - } - return nil - } -} - -func (w *TestAccIPLoadbalancingRouteHTTPRuleWrapper) TestDestroy(state *terraform.State) error { - leftovers := false +func testAccCheckIPLoadbalancingRouteHTTPRuleDestroy(state *terraform.State) error { for _, resource := range state.RootModule().Resources { if resource.Type != "ovh_iploadbalancing_http_route_rule" { continue } config := testAccProvider.Meta().(*Config) - endpoint := fmt.Sprintf("/ipLoadbalancing/%s/http/route/%d/rule/%s", os.Getenv("OVH_IPLB_SERVICE"), w.Expected.RouteID, resource.Primary.ID) + endpoint := fmt.Sprintf( + "/ipLoadbalancing/%s/http/route/%d/rule/%s", + os.Getenv("OVH_IPLB_SERVICE"), + resource.Primary.Attributes["route_id"], + resource.Primary.ID, + ) + err := config.OVHClient.Get(endpoint, nil) if err == nil { - leftovers = true + return fmt.Errorf("IpLoadbalancing http route rule still exists") } } - if leftovers { - return fmt.Errorf("IpLoadbalancing http route rule still exists") - } return nil } -func newTestAccIPLoadbalancingRouteHTTPRuleWrapper() *TestAccIPLoadbalancingRouteHTTPRuleWrapper { - return &TestAccIPLoadbalancingRouteHTTPRuleWrapper{ - Expected: &TestAccIPLoadbalancingRouteHTTPRule{ServiceName: os.Getenv("OVH_IPLB_SERVICE")}, +const testAccCheckOvhIpLoadbalancingHttpRouteRuleConfig_basic = ` +resource "ovh_iploadbalancing_http_route" "testroute" { + service_name = "%s" + display_name = "%s" + weight = 0 + + action { + status = 302 + target = "http://example.com" + type = "redirect" } } -func TestAccIpLoadbalancingRouteHTTPRuleBasicCreate(t *testing.T) { - for _, plan := range TestAccIPLoadbalancingRouteHTTPRulePlan { - w := newTestAccIPLoadbalancingRouteHTTPRuleWrapper() - var steps []resource.TestStep - for _, tcase := range plan { - steps = append(steps, w.TestStep(tcase)) - } - resource.Test(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: w.TestDestroy, - Steps: steps, - }) - } +resource "ovh_iploadbalancing_http_route_rule" "testrule" { + service_name = "${ovh_iploadbalancing_http_route.testroute.service_name}" + route_id = "${ovh_iploadbalancing_http_route.testroute.id}" + display_name = "${ovh_iploadbalancing_http_route.testroute.display_name}" + field = "%s" + match = "%s" + negate = %s + pattern = "%s" + sub_field = "%s" } +` diff --git a/ovh/resource_ovh_iploadbalancing_http_route_test.go b/ovh/resource_ovh_iploadbalancing_http_route_test.go index 2b1893ce1..ce23880ed 100644 --- a/ovh/resource_ovh_iploadbalancing_http_route_test.go +++ b/ovh/resource_ovh_iploadbalancing_http_route_test.go @@ -3,101 +3,65 @@ package ovh import ( "fmt" "os" - "reflect" + "strings" "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) +func TestAccIPLoadbalancingRouteHTTPBasicCreate(t *testing.T) { + serviceName := os.Getenv("OVH_IPLB_SERVICE") + name := "test-route-redirect-https" + weight := "0" + actionStatus := "302" + actionTarget := "https://$${host}$${path}$${arguments}" + actionType := "redirect" - return resource.TestStep{ - Config: config, - Check: resource.ComposeTestCheckFunc( - testAccCheckIPLoadbalancingRouteHTTPMatches(expected), - ), - } -} + config := fmt.Sprintf( + testAccCheckOvhIpLoadbalancingHttpRouteConfig_basic, + serviceName, + name, + weight, + actionStatus, + actionTarget, + actionType, + ) -func TestAccIPLoadbalancingRouteHTTPBasicCreate(t *testing.T) { resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccCheckIpLoadbalancingRouteHTTPPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckIPLoadbalancingRouteHTTPDestroy, Steps: []resource.TestStep{ - testAccIPLoadbalancingRouteHTTPTestStep("test-route-redirect-https", 0, 302, "https://${host}${path}${arguments}", "redirect"), + resource.TestStep{ + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "ovh_iploadbalancing_http_route.testroute", "service_name", serviceName), + resource.TestCheckResourceAttr( + "ovh_iploadbalancing_http_route.testroute", "display_name", name), + resource.TestCheckResourceAttr( + "ovh_iploadbalancing_http_route.testroute", "weight", weight), + resource.TestCheckResourceAttr( + "ovh_iploadbalancing_http_route.testroute", "action.#", "1"), + resource.TestCheckResourceAttr( + "ovh_iploadbalancing_http_route.testroute", "action.859787636.status", actionStatus), + resource.TestCheckResourceAttr( + "ovh_iploadbalancing_http_route.testroute", "action.859787636.target", strings.Replace(actionTarget, "$$", "$", -1)), + resource.TestCheckResourceAttr( + "ovh_iploadbalancing_http_route.testroute", "action.859787636.type", actionType), + ), + }, }, }) } -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 testAccCheckIpLoadbalancingRouteHTTPPreCheck(t *testing.T) { + testAccPreCheck(t) + testAccCheckIpLoadbalancingExists(t) } func testAccCheckIPLoadbalancingRouteHTTPDestroy(state *terraform.State) error { - leftovers := false for _, resource := range state.RootModule().Resources { if resource.Type != "ovh_iploadbalancing_http_route" { continue @@ -107,11 +71,22 @@ func testAccCheckIPLoadbalancingRouteHTTPDestroy(state *terraform.State) error { 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 + return fmt.Errorf("IpLoadbalancing route still exists") } } - if leftovers { - return fmt.Errorf("IpLoadbalancing route still exists") - } return nil } + +const testAccCheckOvhIpLoadbalancingHttpRouteConfig_basic = ` +resource "ovh_iploadbalancing_http_route" "testroute" { + service_name = "%s" + display_name = "%s" + weight = %s + + action { + status = %s + target = "%s" + type = "%s" + } +} +`