Skip to content

Commit 8a40810

Browse files
committed
r/ovh_iploadbalancing_tcp_frontend: new resource
1 parent ea5fb62 commit 8a40810

8 files changed

+542
-14
lines changed

ovh/helpers.go

+13
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,16 @@ func CheckDeleted(d *schema.ResourceData, err error, endpoint string) error {
105105

106106
return fmt.Errorf("calling %s:\n\t %s", endpoint, err.Error())
107107
}
108+
109+
func stringsFromSchema(d *schema.ResourceData, id string) []string {
110+
var xs []string
111+
if v := d.Get(id); v != nil {
112+
rs := v.(*schema.Set).List()
113+
if len(rs) > 0 {
114+
for _, v := range v.(*schema.Set).List() {
115+
xs = append(xs, v.(string))
116+
}
117+
}
118+
}
119+
return xs
120+
}

ovh/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ func Provider() terraform.ResourceProvider {
5959
ResourcesMap: map[string]*schema.Resource{
6060
"ovh_iploadbalancing_tcp_farm": resourceIpLoadbalancingTcpFarm(),
6161
"ovh_iploadbalancing_tcp_farm_server": resourceIpLoadbalancingTcpFarmServer(),
62+
"ovh_iploadbalancing_tcp_frontend": resourceIpLoadbalancingTcpFrontend(),
6263
"ovh_iploadbalancing_http_route": resourceIPLoadbalancingRouteHTTP(),
6364
"ovh_iploadbalancing_http_route_rule": resourceIPLoadbalancingRouteHTTPRule(),
6465
"ovh_domain_zone_record": resourceOvhDomainZoneRecord(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package ovh
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"time"
7+
8+
"github.com/hashicorp/terraform/helper/resource"
9+
"github.com/ovh/go-ovh/ovh"
10+
)
11+
12+
type ipLoadbalancingRefresh struct {
13+
Zone string `json:"zone"`
14+
}
15+
16+
type ipLoadbalancingTask struct {
17+
CreationDate string `json:"creationDate"`
18+
Status string `json:"status"`
19+
Progress int `json:"progress"`
20+
Action string `json:"action"`
21+
Id int `json:"id"`
22+
DoneDate string `json:"doneDate"`
23+
Zones []string `json:"zones"`
24+
}
25+
26+
func iploadbalancingRefresh(c *ovh.Client, id, zone string) error {
27+
log.Printf("[INFO] Refresh OVH IpLoadbalancing: %s", id)
28+
refresh := &ipLoadbalancingRefresh{
29+
Zone: zone,
30+
}
31+
task := &ipLoadbalancingTask{}
32+
err := resource.Retry(5*time.Minute, func() *resource.RetryError {
33+
err := c.Post(
34+
fmt.Sprintf("/ipLoadbalancing/%s/refresh", id),
35+
refresh,
36+
task,
37+
)
38+
39+
if err != nil {
40+
if e, ok := err.(*ovh.APIError); ok && e.Code == 409 {
41+
return resource.RetryableError(fmt.Errorf("OVH IpLoadbalancing is already refreshing %s will retry", id))
42+
43+
} else {
44+
return resource.NonRetryableError(err)
45+
}
46+
}
47+
// Successful refresh
48+
return nil
49+
})
50+
51+
if err != nil {
52+
if e, ok := err.(*ovh.APIError); ok && e.Code == 403 {
53+
log.Printf("[WARN] OVH IpLoadbalancing %s cannot be refreshed: %v", id, e)
54+
return nil
55+
} else {
56+
return fmt.Errorf("Refresh OVH IpLoadbalancing %s failed: %v", id, err)
57+
}
58+
}
59+
60+
stateConf := &resource.StateChangeConf{
61+
Pending: []string{"todo", "doing"},
62+
Target: []string{"done"},
63+
Refresh: waitForIploadbalancingTask(c, id, task.Id),
64+
Timeout: 10 * time.Minute,
65+
Delay: 10 * time.Second,
66+
MinTimeout: 3 * time.Second,
67+
}
68+
69+
if _, err := stateConf.WaitForState(); err != nil {
70+
return err
71+
}
72+
73+
return nil
74+
75+
}
76+
77+
func waitForIploadbalancingTask(c *ovh.Client, id string, taskId int) resource.StateRefreshFunc {
78+
return func() (interface{}, string, error) {
79+
r := &ipLoadbalancingTask{}
80+
endpoint := fmt.Sprintf("/ipLoadbalancing/%s/task/%d", id, taskId)
81+
err := c.Get(endpoint, r)
82+
if err != nil {
83+
if err.(*ovh.APIError).Code == 404 {
84+
log.Printf("[DEBUG] iplb id %s has no task %d", id, taskId)
85+
return r, "notfound", nil
86+
} else {
87+
return r, "", err
88+
}
89+
}
90+
log.Printf("[DEBUG] Pending IPLB task: %v", r)
91+
return r, r.Status, nil
92+
}
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
package ovh
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/hashicorp/terraform/helper/schema"
7+
)
8+
9+
type IpLoadbalancingTcpFrontend struct {
10+
FrontendId int `json:"frontendId,omitempty"`
11+
Port string `json:"port"`
12+
Zone string `json:"zone"`
13+
AllowedSource []string `json:"allowedSource,omitempty"`
14+
DedicatedIpFo []string `json:"dedicatedIpfo,omitempty"`
15+
DefaultFarmId int `json:"defaultFarmId,omitempty"`
16+
DefaultSslId int `json:"defaultSslId,omitempty"`
17+
Disabled bool `json:"disabled,omitempty"`
18+
Ssl bool `json:"ssl,omitempty"`
19+
DisplayName string `json:"displayName,omitempty"`
20+
}
21+
22+
func resourceIpLoadbalancingTcpFrontend() *schema.Resource {
23+
return &schema.Resource{
24+
Create: resourceIpLoadbalancingTcpFrontendCreate,
25+
Read: resourceIpLoadbalancingTcpFrontendRead,
26+
Update: resourceIpLoadbalancingTcpFrontendUpdate,
27+
Delete: resourceIpLoadbalancingTcpFrontendDelete,
28+
29+
Schema: map[string]*schema.Schema{
30+
"service_name": &schema.Schema{
31+
Type: schema.TypeString,
32+
Required: true,
33+
ForceNew: true,
34+
},
35+
"port": &schema.Schema{
36+
Type: schema.TypeString,
37+
Required: true,
38+
ForceNew: false,
39+
},
40+
"zone": &schema.Schema{
41+
Type: schema.TypeString,
42+
Required: true,
43+
ForceNew: false,
44+
},
45+
"allowed_source": {
46+
Type: schema.TypeSet,
47+
Optional: true,
48+
Computed: true,
49+
Elem: &schema.Schema{Type: schema.TypeString},
50+
Set: schema.HashString,
51+
},
52+
"dedicated_ipfo": {
53+
Type: schema.TypeSet,
54+
Optional: true,
55+
Computed: true,
56+
Elem: &schema.Schema{Type: schema.TypeString},
57+
Set: schema.HashString,
58+
},
59+
"default_farm_id": &schema.Schema{
60+
Type: schema.TypeInt,
61+
Optional: true,
62+
Computed: true,
63+
ForceNew: false,
64+
},
65+
"default_ssl_id": &schema.Schema{
66+
Type: schema.TypeInt,
67+
Optional: true,
68+
Computed: true,
69+
ForceNew: false,
70+
},
71+
"disabled": &schema.Schema{
72+
Type: schema.TypeBool,
73+
Computed: true,
74+
},
75+
"ssl": &schema.Schema{
76+
Type: schema.TypeBool,
77+
Computed: true,
78+
},
79+
"display_name": &schema.Schema{
80+
Type: schema.TypeString,
81+
Optional: true,
82+
Computed: true,
83+
ForceNew: false,
84+
},
85+
},
86+
}
87+
}
88+
89+
func resourceIpLoadbalancingTcpFrontendCreate(d *schema.ResourceData, meta interface{}) error {
90+
config := meta.(*Config)
91+
92+
allowedSources := stringsFromSchema(d, "allowed_source")
93+
dedicatedIpFo := stringsFromSchema(d, "dedicated_ipfo")
94+
95+
for _, s := range allowedSources {
96+
if err := validateIpBlock(s); err != nil {
97+
return fmt.Errorf("Error validating `allowed_source` value: %s", err)
98+
}
99+
}
100+
101+
for _, s := range dedicatedIpFo {
102+
if err := validateIpBlock(s); err != nil {
103+
return fmt.Errorf("Error validating `dedicated_ipfo` value: %s", err)
104+
}
105+
}
106+
107+
frontend := &IpLoadbalancingTcpFrontend{
108+
Port: d.Get("port").(string),
109+
Zone: d.Get("zone").(string),
110+
AllowedSource: allowedSources,
111+
DedicatedIpFo: dedicatedIpFo,
112+
DefaultFarmId: d.Get("default_farm_id").(int),
113+
DefaultSslId: d.Get("default_ssl_id").(int),
114+
Disabled: d.Get("disabled").(bool),
115+
Ssl: d.Get("ssl").(bool),
116+
DisplayName: d.Get("display_name").(string),
117+
}
118+
119+
service := d.Get("service_name").(string)
120+
resp := &IpLoadbalancingTcpFrontend{}
121+
endpoint := fmt.Sprintf("/ipLoadbalancing/%s/tcp/frontend", service)
122+
123+
err := config.OVHClient.Post(endpoint, frontend, resp)
124+
if err != nil {
125+
return fmt.Errorf("calling POST %s:\n\t %s", endpoint, err.Error())
126+
}
127+
128+
if err := iploadbalancingRefresh(config.OVHClient, service, resp.Zone); err != nil {
129+
return err
130+
}
131+
d.SetId(fmt.Sprintf("%d", resp.FrontendId))
132+
133+
return nil
134+
}
135+
136+
func resourceIpLoadbalancingTcpFrontendRead(d *schema.ResourceData, meta interface{}) error {
137+
config := meta.(*Config)
138+
service := d.Get("service_name").(string)
139+
r := &IpLoadbalancingTcpFrontend{}
140+
endpoint := fmt.Sprintf("/ipLoadbalancing/%s/tcp/frontend/%s", service, d.Id())
141+
142+
err := config.OVHClient.Get(endpoint, &r)
143+
if err != nil {
144+
return fmt.Errorf("calling %s:\n\t %s", endpoint, err.Error())
145+
}
146+
147+
d.Set("display_name", r.DisplayName)
148+
d.Set("port", r.Port)
149+
d.Set("zone", r.Zone)
150+
151+
allowedSources := make([]string, 0)
152+
for _, s := range r.AllowedSource {
153+
allowedSources = append(allowedSources, s)
154+
}
155+
d.Set("allowed_source", allowedSources)
156+
157+
dedicatedIpFos := make([]string, 0)
158+
for _, s := range r.DedicatedIpFo {
159+
dedicatedIpFos = append(dedicatedIpFos, s)
160+
}
161+
d.Set("dedicated_ipfo", dedicatedIpFos)
162+
163+
d.Set("default_farm_id", r.DefaultFarmId)
164+
d.Set("default_ssl_id", r.DefaultSslId)
165+
d.Set("disabled", r.Disabled)
166+
d.Set("ssl", r.Ssl)
167+
168+
return nil
169+
}
170+
171+
func resourceIpLoadbalancingTcpFrontendUpdate(d *schema.ResourceData, meta interface{}) error {
172+
config := meta.(*Config)
173+
service := d.Get("service_name").(string)
174+
endpoint := fmt.Sprintf("/ipLoadbalancing/%s/tcp/frontend/%s", service, d.Id())
175+
176+
allowedSources := stringsFromSchema(d, "allowed_source")
177+
dedicatedIpFo := stringsFromSchema(d, "dedicated_ipfo")
178+
179+
for _, s := range allowedSources {
180+
if err := validateIpBlock(s); err != nil {
181+
return fmt.Errorf("Error validating `allowed_source` value: %s", err)
182+
}
183+
}
184+
185+
for _, s := range dedicatedIpFo {
186+
if err := validateIpBlock(s); err != nil {
187+
return fmt.Errorf("Error validating `dedicated_ipfo` value: %s", err)
188+
}
189+
}
190+
191+
frontend := &IpLoadbalancingTcpFrontend{
192+
Port: d.Get("port").(string),
193+
Zone: d.Get("zone").(string),
194+
AllowedSource: allowedSources,
195+
DedicatedIpFo: dedicatedIpFo,
196+
DefaultFarmId: d.Get("default_farm_id").(int),
197+
DefaultSslId: d.Get("default_ssl_id").(int),
198+
Disabled: d.Get("disabled").(bool),
199+
Ssl: d.Get("ssl").(bool),
200+
DisplayName: d.Get("display_name").(string),
201+
}
202+
203+
err := config.OVHClient.Put(endpoint, frontend, nil)
204+
if err != nil {
205+
return fmt.Errorf("calling %s:\n\t %s", endpoint, err.Error())
206+
}
207+
208+
return nil
209+
}
210+
211+
func resourceIpLoadbalancingTcpFrontendDelete(d *schema.ResourceData, meta interface{}) error {
212+
config := meta.(*Config)
213+
214+
service := d.Get("service_name").(string)
215+
r := &IpLoadbalancingTcpFrontend{}
216+
endpoint := fmt.Sprintf("/ipLoadbalancing/%s/tcp/frontend/%s", service, d.Id())
217+
218+
err := config.OVHClient.Delete(endpoint, &r)
219+
if err != nil {
220+
return fmt.Errorf("Error calling %s: %s \n", endpoint, err.Error())
221+
}
222+
223+
return nil
224+
}

0 commit comments

Comments
 (0)