Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adds ovh_iploadbalancing_tcp_farm_server resource #33

Merged
merged 1 commit into from
May 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion ovh/helpers.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package ovh

import "fmt"
import (
"bytes"
"fmt"
)

func validateStringEnum(value string, enum []string) error {
missing := true
Expand All @@ -14,3 +17,48 @@ func validateStringEnum(value string, enum []string) error {
}
return nil
}

func getNilBoolPointer(val interface{}) *bool {
if val == nil {
return nil
}
value := val.(bool)
return &value
}

func getNilStringPointer(val interface{}) *string {
if val == nil {
return nil
}
value := val.(string)
if len(value) == 0 {
return nil
}
return &value
}

func getNilIntPointer(val interface{}) *int {
if val == nil {
return nil
}
value := val.(int)
return &value
}

func conditionalAttributeInt(buff *bytes.Buffer, name string, val *int) {
if val != nil {
buff.WriteString(fmt.Sprintf(" %s = %d\n", name, *val))
}
}

func conditionalAttributeString(buff *bytes.Buffer, name string, val *string) {
if val != nil {
buff.WriteString(fmt.Sprintf(" %s = \"%s\"\n", name, *val))
}
}

func conditionalAttributeBool(buff *bytes.Buffer, name string, val *bool) {
if val != nil {
buff.WriteString(fmt.Sprintf(" %s = %v\n", name, *val))
}
}
7 changes: 4 additions & 3 deletions ovh/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@ func Provider() terraform.ResourceProvider {
},

ResourcesMap: map[string]*schema.Resource{
"ovh_iploadbalancing_tcp_farm": resourceIpLoadbalancingTcpFarm(),
"ovh_domain_zone_record": resourceOvhDomainZoneRecord(),
"ovh_domain_zone_redirection": resourceOvhDomainZoneRedirection(),
"ovh_iploadbalancing_tcp_farm": resourceIpLoadbalancingTcpFarm(),
"ovh_iploadbalancing_tcp_farm_server": resourceIpLoadbalancingTcpFarmServer(),
"ovh_domain_zone_record": resourceOvhDomainZoneRecord(),
"ovh_domain_zone_redirection": resourceOvhDomainZoneRedirection(),
// New naming schema (issue #23)
"ovh_cloud_network_private": resourcePublicCloudPrivateNetwork(),
"ovh_cloud_network_private_subnet": resourcePublicCloudPrivateNetworkSubnet(),
Expand Down
234 changes: 234 additions & 0 deletions ovh/resource_ovh_iploadbalancing_tcp_farm_server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
package ovh

import (
"encoding/json"
"fmt"
"log"
"net"

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

type IpLoadbalancingTcpFarmServer struct {
BackendId int `json:"backendId,omitempty"`
ServerId int `json:"serverId,omitempty"`
FarmId int `json:"farmId,omitempty"`
DisplayName *string `json:"displayName,omitempty"`
Address *string `json:"address"`
Cookie *string `json:"cookie,omitempty"`
Port *int `json:"port"`
ProxyProtocolVersion *string `json:"proxyProtocolVersion"`
Chain *string `json:"chain"`
Weight *int `json:"weight"`
Probe *bool `json:"probe"`
Ssl *bool `json:"ssl"`
Backup *bool `json:"backup"`
Status *string `json:"status"`
}

func resourceIpLoadbalancingTcpFarmServer() *schema.Resource {
return &schema.Resource{
Create: resourceIpLoadbalancingTcpFarmServerCreate,
Read: resourceIpLoadbalancingTcpFarmServerRead,
Update: resourceIpLoadbalancingTcpFarmServerUpdate,
Delete: resourceIpLoadbalancingTcpFarmServerDelete,
Schema: map[string]*schema.Schema{
"service_name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"farm_id": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
"display_name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"address": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
ip := v.(string)
if net.ParseIP(ip).To4() == nil {
errors = append(errors, fmt.Errorf("Address %s is not an IPv4", ip))
}
return
},
},
"ssl": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"cookie": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"port": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},
"proxy_protocol_version": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
err := validateStringEnum(v.(string), []string{"v1", "v2", "v2-ssl", "v2-ssl-cn"})
if err != nil {
errors = append(errors, err)
}
return
},
},
"chain": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"weight": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Default: 1,
},
"probe": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"backup": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"status": &schema.Schema{
Type: schema.TypeString,
Required: true,
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
err := validateStringEnum(v.(string), []string{"active", "inactive"})
if err != nil {
errors = append(errors, err)
}
return
},
},
},
}
}

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

newBackendServer := &IpLoadbalancingTcpFarmServer{
DisplayName: getNilStringPointer(d.Get("display_name").(string)),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't see the use of "getNilStringPointer"

AFAIK, there's no such thing elswhere in terraform providers. what are we trying to do here ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assuming "display_name" is not set, d.Get("display_name").(string) returns an empty string "", but a string none the less. gos json.Marshal method correctly interprets it as an empty string rather then a not set string. This is why the struct uses pointers instead of string values, so they can have nil value. getNilStringPointer then gets the string value and turns it into a pointer to string value, that in case of len(<string>) == 0 returns nil, so that json.Marshal() instead of generating "displayName": "" generates "displayName": null which OVH API expects (ie. it will complain if you will have a zero value field instead of null for attributes that can not be changed after resource creation

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have you tried with d.Get("display_name").(*string) ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in fact I did :) it did not work. Don't think type assertion is meant to work like that at all, and even if it did, it would have no way to distinct between nil and zero value, so instead of recovering nil, it would recover *"" but as I said, it's not doing even that.

Copy link
Contributor Author

@goblain goblain May 25, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok then

Address: getNilStringPointer(d.Get("address").(string)),
Port: getNilIntPointer(d.Get("port").(int)),
ProxyProtocolVersion: getNilStringPointer(d.Get("proxy_protocol_version").(string)),
Chain: getNilStringPointer(d.Get("chain").(string)),
Weight: getNilIntPointer(d.Get("weight").(int)),
Probe: getNilBoolPointer(d.Get("probe").(bool)),
Ssl: getNilBoolPointer(d.Get("ssl").(bool)),
Backup: getNilBoolPointer(d.Get("backup").(bool)),
Status: getNilStringPointer(d.Get("status").(string)),
}

service := d.Get("service_name").(string)
farmid := d.Get("farm_id").(int)
r := &IpLoadbalancingTcpFarmServer{}
endpoint := fmt.Sprintf("/ipLoadbalancing/%s/tcp/farm/%d/server", service, farmid)

err := config.OVHClient.Post(endpoint, newBackendServer, r)
if err != nil {
return fmt.Errorf("calling %s with %d:\n\t %s", endpoint, farmid, err.Error())
}

//set id
d.SetId(fmt.Sprintf("%d", r.ServerId))

return nil
}

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

service := d.Get("service_name").(string)
farmid := d.Get("farm_id").(int)
r := &IpLoadbalancingTcpFarmServer{}

endpoint := fmt.Sprintf("/ipLoadbalancing/%s/tcp/farm/%d/server/%s", service, farmid, d.Id())

err := config.OVHClient.Get(endpoint, r)
if err != nil {
return fmt.Errorf("calling %s :\n\t %q", endpoint, err)
}
log.Printf("[DEBUG] Response object from OVH : %v", r)

d.Set("probe", *r.Probe)
d.Set("ssl", *r.Ssl)
d.Set("backup", *r.Backup)
d.Set("address", *r.Address)
if r.DisplayName != nil {
d.Set("display_name", *r.DisplayName)
}
if r.Cookie != nil {
d.Set("cookie", *r.Cookie)
}
d.Set("port", *r.Port)
if r.ProxyProtocolVersion != nil {
d.Set("proxy_protocol_version", *r.ProxyProtocolVersion)
}
if r.Chain != nil {
d.Set("chain", *r.Chain)
}
d.Set("weight", *r.Weight)
d.Set("status", *r.Status)

return nil
}

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

update := &IpLoadbalancingTcpFarmServer{
DisplayName: getNilStringPointer(d.Get("display_name").(string)),
Address: getNilStringPointer(d.Get("address").(string)),
Port: getNilIntPointer(d.Get("port").(int)),
ProxyProtocolVersion: getNilStringPointer(d.Get("proxy_protocol_version").(string)),
Chain: getNilStringPointer(d.Get("chain").(string)),
Weight: getNilIntPointer(d.Get("weight").(int)),
Probe: getNilBoolPointer(d.Get("probe").(bool)),
Ssl: getNilBoolPointer(d.Get("ssl").(bool)),
Backup: getNilBoolPointer(d.Get("backup").(bool)),
Status: getNilStringPointer(d.Get("status").(string)),
}

service := d.Get("service_name").(string)
farmid := d.Get("farm_id").(int)
r := &IpLoadbalancingTcpFarmServer{}
endpoint := fmt.Sprintf("/ipLoadbalancing/%s/tcp/farm/%d/server/%s", service, farmid, d.Id())
js, _ := json.Marshal(update)
log.Printf("[DEBUG] PUT %s : %v", endpoint, string(js))
err := config.OVHClient.Put(endpoint, update, r)
if err != nil {
return fmt.Errorf("calling %s with %d:\n\t %s", endpoint, farmid, err.Error())
}
return nil
}

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

service := d.Get("service_name").(string)
farmid := d.Get("farm_id").(int)

r := &IpLoadbalancingTcpFarmServer{}
endpoint := fmt.Sprintf("/ipLoadbalancing/%s/tcp/farm/%d/server/%s", service, farmid, d.Id())

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

return nil
}
Loading