138 lines
3.9 KiB
Go
138 lines
3.9 KiB
Go
package core
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// RunInstanceOperationUniversal runs an available operation (modify/suspend/delete/resume) if possible.
|
|
func (c *UniversalClient) RunInstanceOperationUniversal(ctx context.Context, instanceUid string, action string, params map[int]string) error {
|
|
state, err := c.GetInstanceState(ctx, instanceUid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var opId int
|
|
for _, op := range state.AvailableOperations {
|
|
if strings.EqualFold(op.Operation, action) {
|
|
opId = op.SvcOperationId
|
|
break
|
|
}
|
|
}
|
|
if opId == 0 {
|
|
return fmt.Errorf("action %s not available for instance %s", action, instanceUid)
|
|
}
|
|
|
|
payload := map[string]interface{}{
|
|
"instanceUid": instanceUid,
|
|
"svcOperationId": opId,
|
|
"operation": action,
|
|
}
|
|
|
|
opUid, err := c.postIgnoreResponse(ctx, "/instanceOperations", payload, true)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create %s operation: %w", action, err)
|
|
}
|
|
if opUid == "" {
|
|
return fmt.Errorf("failed to get operation UID for %s", action)
|
|
}
|
|
|
|
for paramId, value := range params {
|
|
pPayload := genericParamReq{
|
|
InstanceOperationUid: opUid,
|
|
SvcOperationCfsParamId: paramId,
|
|
ParamValue: value,
|
|
}
|
|
_, _, err := c.doRequest(ctx, "POST", "/instanceOperationCfsParams", pPayload)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to set param %d: %w", paramId, err)
|
|
}
|
|
}
|
|
|
|
_, _, err = c.doRequest(ctx, "POST", fmt.Sprintf("/instanceOperations/%s/run", opUid), map[string]interface{}{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return c.waitForOperationFinish(ctx, opUid, defaultOperationTimeout)
|
|
}
|
|
|
|
const defaultOperationTimeout = 30 * time.Minute
|
|
|
|
type operationStatusResponse struct {
|
|
InstanceOperation struct {
|
|
DtFinish *string `json:"dtFinish"`
|
|
IsSuccessful *bool `json:"isSuccessful"`
|
|
ErrorLog *string `json:"errorLog"`
|
|
IsInProgress bool `json:"isInProgress"`
|
|
IsPending bool `json:"isPending"`
|
|
} `json:"instanceOperation"`
|
|
}
|
|
|
|
func (c *UniversalClient) waitForOperationFinish(ctx context.Context, opUid string, timeout time.Duration) error {
|
|
deadline := time.Now().Add(timeout)
|
|
ticker := time.NewTicker(5 * time.Second)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return fmt.Errorf("operation %s cancelled", opUid)
|
|
case <-ticker.C:
|
|
if time.Now().After(deadline) {
|
|
return fmt.Errorf("timeout waiting for operation %s to finish", opUid)
|
|
}
|
|
|
|
respBody, _, err := c.doRequest(ctx, "GET", fmt.Sprintf("/instanceOperations/%s", opUid), nil)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to check operation %s status: %w", opUid, err)
|
|
}
|
|
|
|
var status operationStatusResponse
|
|
if err := json.Unmarshal(respBody, &status); err != nil {
|
|
return fmt.Errorf("failed to parse operation %s status: %w", opUid, err)
|
|
}
|
|
|
|
if status.InstanceOperation.DtFinish != nil && strings.TrimSpace(*status.InstanceOperation.DtFinish) != "" {
|
|
if status.InstanceOperation.IsSuccessful != nil && !*status.InstanceOperation.IsSuccessful {
|
|
if status.InstanceOperation.ErrorLog != nil && strings.TrimSpace(*status.InstanceOperation.ErrorLog) != "" {
|
|
return fmt.Errorf("operation %s failed: %s", opUid, *status.InstanceOperation.ErrorLog)
|
|
}
|
|
return fmt.Errorf("operation %s failed", opUid)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *UniversalClient) postIgnoreResponse(ctx context.Context, path string, payload interface{}, returnLocation bool) (string, error) {
|
|
respBody, headers, err := c.doRequest(ctx, "POST", path, payload)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if returnLocation {
|
|
if loc := headers.Get("Location"); loc != "" {
|
|
return extractUIDFromLocation(loc), nil
|
|
}
|
|
}
|
|
|
|
var justId string
|
|
if err := json.Unmarshal(respBody, &justId); err == nil && justId != "" {
|
|
return justId, nil
|
|
}
|
|
|
|
return "", nil
|
|
}
|
|
|
|
func extractUIDFromLocation(loc string) string {
|
|
if loc == "" {
|
|
return ""
|
|
}
|
|
return strings.TrimPrefix(loc, "./")
|
|
}
|