Skip to content

Commit 17c66a9

Browse files
authored
feat(cloud): add gateway ressource (#571)
* feat(cloud): add gateway ressource Signed-off-by: DrummyFloyd <[email protected]> Related: #358
1 parent b46e887 commit 17c66a9

5 files changed

+492
-0
lines changed

ovh/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ func Provider() *schema.Provider {
173173
"ovh_cloud_project_database_redis_user": resourceCloudProjectDatabaseRedisUser(),
174174
"ovh_cloud_project_database_user": resourceCloudProjectDatabaseUser(),
175175
"ovh_cloud_project_failover_ip_attach": resourceCloudProjectFailoverIpAttach(),
176+
"ovh_cloud_project_gateway": resourceCloudProjectGateway(),
176177
"ovh_cloud_project_kube": resourceCloudProjectKube(),
177178
"ovh_cloud_project_kube_nodepool": resourceCloudProjectKubeNodePool(),
178179
"ovh_cloud_project_kube_oidc": resourceCloudProjectKubeOIDC(),

ovh/resource_cloud_project_gateway.go

+286
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
package ovh
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"net/url"
7+
"strings"
8+
"time"
9+
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
11+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
12+
"github.com/ovh/terraform-provider-ovh/ovh/helpers"
13+
14+
"github.com/ovh/go-ovh/ovh"
15+
)
16+
17+
func resourceOvhCloudProjectGatewayImportState(
18+
d *schema.ResourceData,
19+
meta interface{},
20+
) ([]*schema.ResourceData, error) {
21+
givenId := d.Id()
22+
splitId := strings.SplitN(givenId, "/", 2)
23+
if len(splitId) != 2 {
24+
return nil, fmt.Errorf("Import Id is not OVH_CLOUD_PROJECT/network_id formatted")
25+
}
26+
d.SetId(splitId[1])
27+
d.Set("service_name", splitId[0])
28+
results := make([]*schema.ResourceData, 1)
29+
results[0] = d
30+
return results, nil
31+
}
32+
33+
func resourceCloudProjectGateway() *schema.Resource {
34+
return &schema.Resource{
35+
Create: resourceCloudProjectGatewayCreate,
36+
Read: resourceCloudProjectGatewayRead,
37+
Update: resourceCloudProjectGatewayUpdate,
38+
Delete: resourceCloudProjectGatewayDelete,
39+
Importer: &schema.ResourceImporter{
40+
State: resourceOvhCloudProjectGatewayImportState,
41+
},
42+
43+
Schema: map[string]*schema.Schema{
44+
"service_name": {
45+
Type: schema.TypeString,
46+
Required: true,
47+
ForceNew: true,
48+
DefaultFunc: schema.EnvDefaultFunc("OVH_CLOUD_PROJECT_SERVICE", nil),
49+
Description: "Service name of the resource representing the id of the cloud project.",
50+
},
51+
"name": {
52+
Type: schema.TypeString,
53+
Required: true,
54+
},
55+
"model": {
56+
Type: schema.TypeString,
57+
Required: true,
58+
},
59+
"region": {
60+
Type: schema.TypeString,
61+
Required: true,
62+
ForceNew: true,
63+
},
64+
"network_id": {
65+
Type: schema.TypeString,
66+
Required: true,
67+
ForceNew: true,
68+
},
69+
"subnet_id": {
70+
Type: schema.TypeString,
71+
Required: true,
72+
ForceNew: true,
73+
},
74+
"status": {
75+
Type: schema.TypeString,
76+
Computed: true,
77+
},
78+
},
79+
}
80+
}
81+
82+
func resourceCloudProjectGatewayCreate(d *schema.ResourceData, meta interface{}) error {
83+
config := meta.(*Config)
84+
85+
serviceName := d.Get("service_name").(string)
86+
region := d.Get("region").(string)
87+
network := d.Get("network_id").(string)
88+
subnet := d.Get("subnet_id").(string)
89+
90+
params := &CloudProjectGatewayCreateOpts{
91+
Name: d.Get("name").(string),
92+
Model: d.Get("model").(string),
93+
}
94+
95+
r := &CloudProjectGatewayResponse{}
96+
97+
log.Printf("[DEBUG] Will create public cloud gateway: %s", params)
98+
99+
endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/network/%s/subnet/%s/gateway",
100+
url.PathEscape(serviceName),
101+
url.PathEscape(region),
102+
url.PathEscape(network),
103+
url.PathEscape(subnet))
104+
105+
if err := config.OVHClient.Post(endpoint, params, r); err != nil {
106+
return fmt.Errorf("calling %s with params %s:\n\t %q", endpoint, params, err)
107+
}
108+
109+
log.Printf("[DEBUG] Waiting for Gateway %+v:", r)
110+
111+
stateConf := &resource.StateChangeConf{
112+
Pending: []string{"in-progress"},
113+
Target: []string{"active"},
114+
Refresh: waitForCloudProjectGatewayActive(config.OVHClient, serviceName, r.Id),
115+
Timeout: 10 * time.Minute,
116+
Delay: 10 * time.Second,
117+
MinTimeout: 3 * time.Second,
118+
}
119+
120+
if _, err := stateConf.WaitForState(); err != nil {
121+
return fmt.Errorf("waiting for gateway (%s): %s", params, err)
122+
}
123+
124+
ro := &CloudProjectOperationResponse{}
125+
endpointo := fmt.Sprintf("/cloud/project/%s/operation/%s",
126+
url.PathEscape(serviceName),
127+
url.PathEscape(r.Id))
128+
if err := config.OVHClient.Get(endpointo, ro); err != nil {
129+
return nil
130+
}
131+
log.Printf("[DEBUG] Created Gateway %+v", ro)
132+
133+
gatewayId := ro.ResourceId
134+
135+
// set id
136+
d.SetId(*gatewayId)
137+
138+
return resourceCloudProjectGatewayRead(d, meta)
139+
}
140+
141+
func resourceCloudProjectGatewayRead(d *schema.ResourceData, meta interface{}) error {
142+
config := meta.(*Config)
143+
144+
serviceName := d.Get("service_name").(string)
145+
region := d.Get("region").(string)
146+
147+
r := &CloudProjectGatewayResponse{}
148+
149+
log.Printf("[DEBUG] Will read public cloud gateway for project: %s, region: %s, id: %s", serviceName, region, d.Id())
150+
151+
endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/gateway/%s",
152+
url.PathEscape(serviceName),
153+
url.PathEscape(region),
154+
url.PathEscape(d.Id()))
155+
156+
if err := config.OVHClient.Get(endpoint, r); err != nil {
157+
return helpers.CheckDeleted(d, err, endpoint)
158+
}
159+
160+
d.Set("name", r.Name)
161+
d.Set("model", r.Model)
162+
d.Set("status", r.Status)
163+
d.Set("region", region)
164+
d.SetId(r.Id)
165+
d.Set("service_name", serviceName)
166+
167+
// TODO : add response fields "externalInformation" and "interfaces"
168+
169+
log.Printf("[DEBUG] Read Public Cloud Gateway %+v", r)
170+
return nil
171+
}
172+
173+
func resourceCloudProjectGatewayUpdate(d *schema.ResourceData, meta interface{}) error {
174+
config := meta.(*Config)
175+
serviceName := d.Get("service_name").(string)
176+
params := &CloudProjectGatewayUpdateOpts{
177+
Name: d.Get("name").(string),
178+
Model: d.Get("model").(string),
179+
}
180+
region := d.Get("region").(string)
181+
182+
log.Printf("[DEBUG] Will update public cloud gateway: %s", params)
183+
184+
endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/gateway/%s",
185+
url.PathEscape(serviceName),
186+
url.PathEscape(region),
187+
url.PathEscape(d.Id()))
188+
189+
if err := config.OVHClient.Put(endpoint, params, nil); err != nil {
190+
return fmt.Errorf("calling %s with params %s:\n\t %q", endpoint, params, err)
191+
}
192+
193+
log.Printf("[DEBUG] Updated Public cloud %s Gateway %s:", serviceName, d.Id())
194+
195+
return resourceCloudProjectGatewayRead(d, meta)
196+
}
197+
198+
func resourceCloudProjectGatewayDelete(d *schema.ResourceData, meta interface{}) error {
199+
config := meta.(*Config)
200+
serviceName := d.Get("service_name").(string)
201+
region := d.Get("region").(string)
202+
203+
id := d.Id()
204+
205+
log.Printf("[DEBUG] Will delete public cloud gateway for project: %s, region: %s, id: %s", serviceName, region, id)
206+
207+
endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/gateway/%s",
208+
url.PathEscape(serviceName),
209+
url.PathEscape(region),
210+
url.PathEscape(id))
211+
212+
r := &CloudProjectOperationResponse{}
213+
if err := config.OVHClient.Delete(endpoint, r); err != nil {
214+
return fmt.Errorf("calling %s:\n\t %q", endpoint, err)
215+
}
216+
217+
stateConf := &resource.StateChangeConf{
218+
Pending: []string{"in-progress"},
219+
Target: []string{"completed"},
220+
Refresh: waitForCloudProjectGatewayDelete(config.OVHClient, serviceName, r.Id),
221+
Timeout: 10 * time.Minute,
222+
Delay: 10 * time.Second,
223+
MinTimeout: 3 * time.Second,
224+
}
225+
226+
if _, err := stateConf.WaitForState(); err != nil {
227+
return fmt.Errorf("deleting for gateway (%s): %s", id, err)
228+
}
229+
230+
d.SetId("")
231+
232+
log.Printf("[DEBUG] Deleted Public Cloud %s Gateway %s", serviceName, id)
233+
return nil
234+
}
235+
236+
// AttachmentStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
237+
// an Attachment Task.
238+
func waitForCloudProjectGatewayActive(c *ovh.Client, serviceName, operationId string) resource.StateRefreshFunc {
239+
return func() (interface{}, string, error) {
240+
ro := &CloudProjectOperationResponse{}
241+
endpoint := fmt.Sprintf("/cloud/project/%s/operation/%s",
242+
url.PathEscape(serviceName),
243+
url.PathEscape(operationId))
244+
if err := c.Get(endpoint, ro); err != nil {
245+
return ro, "", err
246+
}
247+
248+
log.Printf("[DEBUG] Pending Operation: %+v", ro)
249+
250+
if ro.ResourceId != nil {
251+
rg := &CloudProjectGatewayResponse{}
252+
gatewayId := ro.ResourceId
253+
endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/gateway/%s",
254+
url.PathEscape(serviceName),
255+
url.PathEscape(ro.Regions[0]),
256+
url.PathEscape(*gatewayId))
257+
if err := c.Get(endpoint, rg); err != nil {
258+
return rg, "", err
259+
}
260+
return rg, rg.Status, nil
261+
}
262+
263+
return ro, ro.Status, nil
264+
}
265+
}
266+
267+
// AttachmentStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
268+
// an Attachment Task.
269+
func waitForCloudProjectGatewayDelete(c *ovh.Client, serviceName, OperationId string) resource.StateRefreshFunc {
270+
return func() (interface{}, string, error) {
271+
r := &CloudProjectOperationResponse{}
272+
endpoint := fmt.Sprintf("/cloud/project/%s/operation/%s",
273+
url.PathEscape(serviceName),
274+
url.PathEscape(OperationId))
275+
if err := c.Get(endpoint, r); err != nil {
276+
if errOvh, ok := err.(*ovh.APIError); ok && errOvh.Code == 404 {
277+
log.Printf("[DEBUG] gateway id %s on project %s deleted", OperationId, serviceName)
278+
return r, "DELETED", nil
279+
} else {
280+
return r, "", err
281+
}
282+
}
283+
log.Printf("[DEBUG] Pending Gateway: %+v", r)
284+
return r, r.Status, nil
285+
}
286+
}
+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package ovh
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"testing"
7+
8+
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
9+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
10+
)
11+
12+
var testAccCloudProjectGatewayConfig = `
13+
resource "ovh_vrack_cloudproject" "attach" {
14+
service_name = "%s"
15+
project_id = "%s"
16+
}
17+
18+
data "ovh_cloud_project_vrack" "vrack" {
19+
service_name = "%s"
20+
depends_on = [ovh_vrack_cloudproject.attach]
21+
}
22+
23+
resource "ovh_cloud_project_network_private" "mypriv" {
24+
depends_on = [ovh_vrack_cloudproject.attach]
25+
service_name = ovh_vrack_cloudproject.attach.service_name
26+
vlan_id = "%d"
27+
name = "%s"
28+
regions = ["%s"]
29+
}
30+
31+
resource "ovh_cloud_project_network_private_subnet" "myprivsub" {
32+
service_name = ovh_cloud_project_network_private.mypriv.service_name
33+
network_id = ovh_cloud_project_network_private.mypriv.id
34+
region = "%s"
35+
start = "10.0.0.2"
36+
end = "10.0.255.254"
37+
network = "10.0.0.0/16"
38+
dhcp = true
39+
}
40+
41+
resource "ovh_cloud_project_gateway" "gateway" {
42+
service_name = ovh_cloud_project_network_private.mypriv.service_name
43+
name = "%s"
44+
model = "s"
45+
region = ovh_cloud_project_network_private_subnet.myprivsub.region
46+
network_id = tolist(ovh_cloud_project_network_private.mypriv.regions_attributes[*].openstackid)[0]
47+
subnet_id = ovh_cloud_project_network_private_subnet.myprivsub.id
48+
}
49+
`
50+
51+
func TestAccCloudProjectGateway(t *testing.T) {
52+
name := acctest.RandomWithPrefix(test_prefix)
53+
gatewayName := acctest.RandomWithPrefix(test_prefix)
54+
vlanId := acctest.RandIntRange(100, 200)
55+
region := os.Getenv("OVH_CLOUD_PROJECT_KUBE_REGION_TEST")
56+
57+
config := fmt.Sprintf(
58+
testAccCloudProjectGatewayConfig,
59+
os.Getenv("OVH_VRACK_SERVICE_TEST"),
60+
os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST"),
61+
os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST"),
62+
vlanId,
63+
name,
64+
region,
65+
region,
66+
gatewayName,
67+
)
68+
69+
resource.Test(t, resource.TestCase{
70+
PreCheck: func() {
71+
testAccPreCheckCloud(t)
72+
testAccCheckCloudProjectExists(t)
73+
},
74+
75+
Providers: testAccProviders,
76+
Steps: []resource.TestStep{
77+
{
78+
Config: config,
79+
Check: resource.ComposeTestCheckFunc(
80+
resource.TestCheckResourceAttrSet("ovh_cloud_project_gateway.gateway", "service_name"),
81+
resource.TestCheckResourceAttrSet("ovh_cloud_project_gateway.gateway", "network_id"),
82+
resource.TestCheckResourceAttrSet("ovh_cloud_project_gateway.gateway", "subnet_id"),
83+
resource.TestCheckResourceAttrSet("ovh_cloud_project_gateway.gateway", "model"),
84+
resource.TestCheckResourceAttr("ovh_cloud_project_gateway.gateway", "region", region),
85+
resource.TestCheckResourceAttr("ovh_cloud_project_gateway.gateway", "name", gatewayName),
86+
resource.TestCheckResourceAttr("ovh_cloud_project_gateway.gateway", "model", "s"),
87+
),
88+
},
89+
},
90+
})
91+
}

0 commit comments

Comments
 (0)