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 new file mode 100644 index 0000000..94b034c --- /dev/null +++ b/api/vpc_connect.go @@ -0,0 +1,163 @@ +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 + } + + switch response.StatusCode { + case 204: + return api.waitForEnableVpcConnectWithRetry(instanceID, 1, sleep, timeout) + default: + 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 + } + + switch response.StatusCode { + case 200: + return data, nil + default: + 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 + } + + switch response.StatusCode { + case 204: + return nil + default: + 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 + } + + switch response.StatusCode { + case 204: + return nil + default: + 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) +} + +// 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 + } + + switch response.StatusCode { + case 200: + log.Printf("[DEBUG] VPC-Connect: VPC features enabled") + return nil + default: + return fmt.Errorf("enable VPC failed, status: %v, message: %s", + response.StatusCode, failed) + } + } + + log.Printf("[DEBUG] VPC-Connect: VPC features already enabled") + return nil +}