From 07b40ab3d38c1e1df155ea392b72975f25a9953f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Brod=C3=A9n?= Date: Fri, 24 Nov 2023 10:30:52 +0100 Subject: [PATCH 1/4] Add new endpoint for VPC Connect --- api/vpc_connect.go | 130 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 api/vpc_connect.go diff --git a/api/vpc_connect.go b/api/vpc_connect.go new file mode 100644 index 0000000..02f4b90 --- /dev/null +++ b/api/vpc_connect.go @@ -0,0 +1,130 @@ +package api + +import ( + "fmt" + "log" + "time" +) + +// EnableVpcConnect: Enable VPC Connect and wait until finished. +// Need to enable VPC for an instance, if no standalone VPC used. +// Wait until finished with configureable sleep and timeout. +func (api *API) EnableVpcConnect(instanceID int, params map[string][]interface{}, + sleep, timeout int) error { + + var ( + failed map[string]interface{} + path = fmt.Sprintf("/api/instances/%d/vpc-connect", instanceID) + ) + + if err := api.enableVPC(instanceID); err != nil { + return err + } + + response, err := api.sling.New().Post(path).BodyJSON(params).Receive(nil, &failed) + if err != nil { + return err + } + + if response.StatusCode == 204 { + return api.waitForEnableVpcConnectWithRetry(instanceID, 1, sleep, timeout) + } else { + return fmt.Errorf("enable VPC Connect failed, status: %v, message: %s", + response.StatusCode, failed) + } +} + +// ReadVpcConnect: Reads VPC Connect information +func (api *API) ReadVpcConnect(instanceID int) (map[string]interface{}, error) { + var ( + data map[string]interface{} + failed map[string]interface{} + path = fmt.Sprintf("/api/instances/%d/vpc-connect", instanceID) + ) + + response, err := api.sling.New().Get(path).Receive(&data, &failed) + if err != nil { + return nil, err + } + + if response.StatusCode == 200 { + return data, nil + } else { + return nil, fmt.Errorf("read VPC Connect failed, status: %v, message: %s", + response.StatusCode, failed) + } +} + +// UpdateVpcConnect: Update allowlist for the VPC Connect +func (api *API) UpdateVpcConnect(instanceID int, params map[string][]interface{}) error { + var ( + failed map[string]interface{} + path = fmt.Sprintf("/api/instances/%d/vpc-connect", instanceID) + ) + + response, err := api.sling.New().Put(path).BodyJSON(params).Receive(nil, &failed) + if err != nil { + return err + } + + if response.StatusCode == 204 { + return nil + } else { + return fmt.Errorf("update VPC connect failed, status: %v, message: %s", + response.StatusCode, failed) + } +} + +// DisableVpcConnect: Disable the VPC Connect feature +func (api *API) DisableVpcConnect(instanceID int) error { + var ( + failed map[string]interface{} + path = fmt.Sprintf("/api/instances/%d/vpc-connect", instanceID) + ) + + response, err := api.sling.New().Delete(path).Receive(nil, &failed) + if err != nil { + return err + } + + if response.StatusCode == 204 { + return nil + } else { + return fmt.Errorf("disable VPC Connect failed, status: %v, message: %s", + response.StatusCode, failed) + } +} + +// waitForEnableVpcConnectWithRetry: Wait until status change from pending to enable +func (api *API) waitForEnableVpcConnectWithRetry(instanceID, attempt, sleep, timeout int) error { + var ( + data map[string]interface{} + failed map[string]interface{} + path = fmt.Sprintf("/api/instances/%d/vpc-connect", instanceID) + ) + + response, err := api.sling.New().Get(path).Receive(&data, &failed) + if err != nil { + return err + } else if attempt*sleep > timeout { + return fmt.Errorf("enable VPC Connect failed, reached timeout of %d seconds", timeout) + } + log.Printf("[DEBUG] VPC-Connect: waitForEnableVpcConnectWithRetry data: %v", data) + + switch response.StatusCode { + case 200: + switch data["status"].(string) { + case "enabled": + return nil + case "pending": + log.Printf("[DEBUG] go-api::vpc-connect::enable not finished and will retry, "+ + "attempt: %d, until timeout: %d", attempt, (timeout - (attempt * sleep))) + attempt++ + time.Sleep(time.Duration(sleep) * time.Second) + return api.waitForEnableVpcConnectWithRetry(instanceID, attempt, sleep, timeout) + } + } + + return fmt.Errorf("wait for enable VPC Connect failed, status: %v, message: %s", + response.StatusCode, failed) +} From f69fb07cf5e4b5a45912d70d3eb1e96bfbb22c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Brod=C3=A9n?= Date: Fri, 24 Nov 2023 10:31:37 +0100 Subject: [PATCH 2/4] Move EnableVPC func to VPC Connect --- api/privatelink.go | 30 +----------------------------- api/vpc_connect.go | 30 +++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/api/privatelink.go b/api/privatelink.go index 92bc10d..2ffd821 100644 --- a/api/privatelink.go +++ b/api/privatelink.go @@ -15,7 +15,7 @@ func (api *API) EnablePrivatelink(instanceID int, params map[string][]interface{ path = fmt.Sprintf("/api/instances/%d/privatelink", instanceID) ) - if err := api.enableVPC(instanceID); err != nil { + if err := api.EnableVPC(instanceID); err != nil { return err } @@ -126,31 +126,3 @@ func (api *API) waitForEnablePrivatelinkWithRetry(instanceID, attempt, sleep, ti return fmt.Errorf("wait for enable PrivateLink failed, status: %v, message: %s", response.StatusCode, failed) } - -// enableVPC: Enable VPC for an instance -// Check if the instance already have a standalone VPC -func (api *API) enableVPC(instanceID int) error { - var ( - failed map[string]interface{} - path = fmt.Sprintf("/api/instances/%d/vpc", instanceID) - ) - - data, _ := api.ReadInstance(fmt.Sprintf("%d", instanceID)) - if data["vpc"] == nil { - response, err := api.sling.New().Put(path).Receive(nil, &failed) - if err != nil { - return err - } - - if response.StatusCode == 200 { - log.Printf("[DEBUG] PrivateLink: VPC features enabled") - return nil - } else { - return fmt.Errorf("enable VPC failed, status: %v, message: %s", - response.StatusCode, failed) - } - } - - log.Printf("[DEBUG] PrivateLink: VPC features already enabled") - return nil -} diff --git a/api/vpc_connect.go b/api/vpc_connect.go index 02f4b90..66d94a6 100644 --- a/api/vpc_connect.go +++ b/api/vpc_connect.go @@ -17,7 +17,7 @@ func (api *API) EnableVpcConnect(instanceID int, params map[string][]interface{} path = fmt.Sprintf("/api/instances/%d/vpc-connect", instanceID) ) - if err := api.enableVPC(instanceID); err != nil { + if err := api.EnableVPC(instanceID); err != nil { return err } @@ -128,3 +128,31 @@ func (api *API) waitForEnableVpcConnectWithRetry(instanceID, attempt, sleep, tim return fmt.Errorf("wait for enable VPC Connect failed, status: %v, message: %s", response.StatusCode, failed) } + +// enableVPC: Enable VPC for an instance +// Check if the instance already have a standalone VPC +func (api *API) EnableVPC(instanceID int) error { + var ( + failed map[string]interface{} + path = fmt.Sprintf("/api/instances/%d/vpc", instanceID) + ) + + data, _ := api.ReadInstance(fmt.Sprintf("%d", instanceID)) + if data["vpc"] == nil { + response, err := api.sling.New().Put(path).Receive(nil, &failed) + if err != nil { + return err + } + + if response.StatusCode == 200 { + log.Printf("[DEBUG] VPC-Connect: VPC features enabled") + return nil + } else { + return fmt.Errorf("enable VPC failed, status: %v, message: %s", + response.StatusCode, failed) + } + } + + log.Printf("[DEBUG] VPC-Connect: VPC features already enabled") + return nil +} From e04ca3c5b1b34527a9955a1521d286589c4860ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Brod=C3=A9n?= Date: Wed, 6 Dec 2023 16:46:26 +0100 Subject: [PATCH 3/4] Use switch/case when handling response status codes --- api/vpc_connect.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/api/vpc_connect.go b/api/vpc_connect.go index 66d94a6..438dc1e 100644 --- a/api/vpc_connect.go +++ b/api/vpc_connect.go @@ -26,9 +26,10 @@ func (api *API) EnableVpcConnect(instanceID int, params map[string][]interface{} return err } - if response.StatusCode == 204 { + switch response.StatusCode { + case 204: return api.waitForEnableVpcConnectWithRetry(instanceID, 1, sleep, timeout) - } else { + default: return fmt.Errorf("enable VPC Connect failed, status: %v, message: %s", response.StatusCode, failed) } @@ -47,9 +48,10 @@ func (api *API) ReadVpcConnect(instanceID int) (map[string]interface{}, error) { return nil, err } - if response.StatusCode == 200 { + switch response.StatusCode { + case 200: return data, nil - } else { + default: return nil, fmt.Errorf("read VPC Connect failed, status: %v, message: %s", response.StatusCode, failed) } @@ -67,9 +69,10 @@ func (api *API) UpdateVpcConnect(instanceID int, params map[string][]interface{} return err } - if response.StatusCode == 204 { + switch response.StatusCode { + case 204: return nil - } else { + default: return fmt.Errorf("update VPC connect failed, status: %v, message: %s", response.StatusCode, failed) } @@ -87,9 +90,10 @@ func (api *API) DisableVpcConnect(instanceID int) error { return err } - if response.StatusCode == 204 { + switch response.StatusCode { + case 204: return nil - } else { + default: return fmt.Errorf("disable VPC Connect failed, status: %v, message: %s", response.StatusCode, failed) } From e74213c0b06503bd90c0997b4023976c6aa9a84f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Brod=C3=A9n?= Date: Wed, 6 Dec 2023 16:49:46 +0100 Subject: [PATCH 4/4] Update EnableVPC with switch/case too --- api/vpc_connect.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/api/vpc_connect.go b/api/vpc_connect.go index 438dc1e..94b034c 100644 --- a/api/vpc_connect.go +++ b/api/vpc_connect.go @@ -148,10 +148,11 @@ func (api *API) EnableVPC(instanceID int) error { return err } - if response.StatusCode == 200 { + switch response.StatusCode { + case 200: log.Printf("[DEBUG] VPC-Connect: VPC features enabled") return nil - } else { + default: return fmt.Errorf("enable VPC failed, status: %v, message: %s", response.StatusCode, failed) }