From d147340c6a282b939fd012e94751ce4ed2437741 Mon Sep 17 00:00:00 2001 From: Carlos Lapao Date: Thu, 28 Nov 2024 08:15:54 +0000 Subject: [PATCH] Reverse Proxy endpoint changes for VSCode - Added better error management - Fixed an issue where some error messages were not displayed correctly - Added an endpoint for the orchestrator to get the reverse proxy config --- src/controllers/common.go | 12 +-- src/controllers/orchestrator.go | 48 +++++++++++- src/data/models/reverse_proxy.go | 6 +- src/data/orchestrator.go | 13 ++++ src/errors/main.go | 64 +++++++++++---- src/errors/nested_error.go | 8 -- src/errors/stack_item.go | 25 ++++++ src/models/api_error_response.go | 77 ++++++++++++------- .../get_host_reverse_proxy_config.go | 26 +++++++ src/reverse_proxy/main.go | 1 + 10 files changed, 215 insertions(+), 65 deletions(-) delete mode 100644 src/errors/nested_error.go create mode 100644 src/errors/stack_item.go create mode 100644 src/orchestrator/get_host_reverse_proxy_config.go diff --git a/src/controllers/common.go b/src/controllers/common.go index bbdbf16..4332d95 100644 --- a/src/controllers/common.go +++ b/src/controllers/common.go @@ -2,7 +2,6 @@ package controllers import ( "encoding/json" - "fmt" "net/http" "runtime/debug" @@ -23,15 +22,10 @@ func GetBaseContext(r *http.Request) *basecontext.BaseContext { func Recover(ctx basecontext.ApiContext, r *http.Request, w http.ResponseWriter) { if err := recover(); err != nil { - ctx.LogErrorf("Recovered from panic: %v\n%v", err, debug.Stack()) + ctx.LogErrorf("Recovered from panic: %v\n%v", err, string(debug.Stack())) sysErr := errors.NewWithCodef(http.StatusInternalServerError, "internal server error") - sysErr.NestedError = make([]errors.NestedError, 0) - sysErr.NestedError = append(sysErr.NestedError, errors.NestedError{ - Message: fmt.Sprintf("%v", err.(error)), - }, errors.NestedError{ - Message: string(debug.Stack()), - }) - + sysErr.Stack = make([]errors.StackItem, 0) + sysErr.AddStackMessage(string(debug.Stack())) ReturnApiError(ctx, w, models.NewFromErrorWithCode(sysErr, http.StatusInternalServerError)) } } diff --git a/src/controllers/orchestrator.go b/src/controllers/orchestrator.go index d7d00e1..e3e973b 100644 --- a/src/controllers/orchestrator.go +++ b/src/controllers/orchestrator.go @@ -304,9 +304,17 @@ func registerOrchestratorHostsHandlers(ctx basecontext.ApiContext, version strin WithRequiredClaim(constants.SUPER_USER_ROLE). WithHandler(DeleteOrchestratorHostCatalogCacheItemVersionHandler()). Register() - // endregion + // region Reverse Proxy + restapi.NewController(). + WithMethod(restapi.GET). + WithVersion(version). + WithPath("/orchestrator/hosts/{id}/reverse-proxy"). + WithRequiredClaim(constants.LIST_REVERSE_PROXY_HOSTS_CLAIM). + WithHandler(GetOrchestratorHostReverseProxyConfigHandler()). + Register() + restapi.NewController(). WithMethod(restapi.GET). WithVersion(version). @@ -394,6 +402,7 @@ func registerOrchestratorHostsHandlers(ctx basecontext.ApiContext, version strin WithRequiredClaim(constants.CONFIGURE_REVERSE_PROXY_CLAIM). WithHandler(DisableOrchestratorHostReverseProxyHandler()). Register() + // endregion } // @Summary Gets all hosts from the orchestrator @@ -1820,6 +1829,43 @@ func CreateOrchestratorVirtualMachineHandler() restapi.ControllerHandler { // region Orchestrator Reverse Proxy +// @Summary Gets orchestrator host reverse proxy configuration +// @Description This endpoint returns orchestrator host reverse proxy configuration +// @Tags Orchestrator +// @Produce json +// @Param id path string true "Host ID" +// @Success 200 {object} models.ReverseProxy +// @Failure 400 {object} models.ApiErrorResponse +// @Failure 401 {object} models.OAuthErrorResponse +// @Security ApiKeyAuth +// @Security BearerAuth +// @Router /v1/orchestrator/hosts/{id}/reverse-proxy [get] +func GetOrchestratorHostReverseProxyConfigHandler() restapi.ControllerHandler { + return func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + ctx := GetBaseContext(r) + defer Recover(ctx, r, w) + + vars := mux.Vars(r) + id := vars["id"] + noCache := false + if r.Header.Get("X-No-Cache") == "true" { + noCache = true + } + + orchestratorSvc := orchestrator.NewOrchestratorService(ctx) + response, err := orchestratorSvc.GetHostReverseProxyConfig(ctx, id, "", noCache) + if err != nil { + ReturnApiError(ctx, w, models.NewFromError(err)) + return + } + + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(response) + ctx.LogInfof("Successfully got orchestrator host %s reverse proxy config", id) + } +} + // @Summary Gets orchestrator host reverse proxy hosts // @Description This endpoint returns orchestrator host reverse proxy hosts // @Tags Orchestrator diff --git a/src/data/models/reverse_proxy.go b/src/data/models/reverse_proxy.go index 058b166..c7bba20 100644 --- a/src/data/models/reverse_proxy.go +++ b/src/data/models/reverse_proxy.go @@ -6,11 +6,11 @@ import ( ) type ReverseProxy struct { - ID string `json:"id"` + ID string `json:"id,omitempty"` HostID string `json:"host_id,omitempty"` Enabled bool `json:"enabled"` - Host string `json:"host"` - Port string `json:"port"` + Host string `json:"host,omitempty"` + Port string `json:"port,omitempty"` } func (o *ReverseProxy) Diff(source ReverseProxy) bool { diff --git a/src/data/orchestrator.go b/src/data/orchestrator.go index bab3280..209986f 100644 --- a/src/data/orchestrator.go +++ b/src/data/orchestrator.go @@ -532,3 +532,16 @@ func (j *JsonDatabase) GetOrchestratorReverseProxyHost(ctx basecontext.ApiContex return nil, ErrOrchestratorReverseProxyHostNotFound } + +func (j *JsonDatabase) GetOrchestratorReverseProxyConfig(ctx basecontext.ApiContext, hostId string) (*models.ReverseProxy, error) { + if !j.IsConnected() { + return nil, ErrDatabaseNotConnected + } + + host, err := j.GetOrchestratorHost(ctx, hostId) + if err != nil { + return nil, err + } + + return host.ReverseProxy, nil +} diff --git a/src/errors/main.go b/src/errors/main.go index 74b90f6..3b52275 100644 --- a/src/errors/main.go +++ b/src/errors/main.go @@ -2,11 +2,12 @@ package errors import ( "fmt" + "strings" ) type SystemError struct { message string - NestedError []NestedError + Stack []StackItem Path string code int description string @@ -25,13 +26,39 @@ func (e SystemError) Error() string { if e.Path != "" { msg = fmt.Sprintf("%v, path: %v", msg, e.Path) } - if len(e.NestedError) > 0 { - msg = fmt.Sprintf("%v, nested errors: %v", msg, e.NestedError) + if len(e.Stack) > 0 { + msg = fmt.Sprintf("%v, nested errors: %v", msg, e.Stack) } return msg } +func (e *SystemError) AddStackError(err error) { + if e.Stack == nil { + e.Stack = make([]StackItem, 0) + } + + e.AddStackMessage(err.Error()) +} + +func (e *SystemError) AddStackMessage(msg string) { + if e.Stack == nil { + e.Stack = make([]StackItem, 0) + } + + errorMsg := strings.ReplaceAll(msg, "\t", "") + lines := strings.Split(errorMsg, "\n") + for _, line := range lines { + if line == "" { + continue + } + + e.Stack = append(e.Stack, StackItem{ + Error: line, + }) + } +} + func (e SystemError) Message() string { return e.message } @@ -143,24 +170,31 @@ func NewWithCodeAndNestedErrorf(sysError SystemError, code int, format string, a message: fmt.Sprintf(format, a...), code: code, } - err.NestedError = make([]NestedError, 0) - nestedError := NestedError{ - Message: sysError.Message(), - Code: sysError.Code(), - Path: sysError.Path, - Description: sysError.Description(), + err.Stack = make([]StackItem, 0) + + stackErrors := StackItem{ + Error: sysError.Message(), + } + if sysError.Code() != 0 { + stackErrors.Code = &sysError.code + } + if sysError.Description() != "" { + stackErrors.Description = &sysError.description + } + if sysError.Path != "" { + stackErrors.Path = &sysError.Path } - err.NestedError = append(err.NestedError, nestedError) - if len(sysError.NestedError) > 0 { - for _, nestedError := range sysError.NestedError { - nestedError := NestedError{ - Message: nestedError.Message, + err.Stack = append(err.Stack, stackErrors) + if len(sysError.Stack) > 0 { + for _, nestedError := range sysError.Stack { + nestedError := StackItem{ + Error: nestedError.Error, Code: nestedError.Code, Path: nestedError.Path, Description: nestedError.Description, } - err.NestedError = append(err.NestedError, nestedError) + err.Stack = append(err.Stack, nestedError) } } diff --git a/src/errors/nested_error.go b/src/errors/nested_error.go deleted file mode 100644 index 994ebac..0000000 --- a/src/errors/nested_error.go +++ /dev/null @@ -1,8 +0,0 @@ -package errors - -type NestedError struct { - Message string - Path string - Code int - Description string -} diff --git a/src/errors/stack_item.go b/src/errors/stack_item.go new file mode 100644 index 0000000..b6fd90f --- /dev/null +++ b/src/errors/stack_item.go @@ -0,0 +1,25 @@ +package errors + +type StackItem struct { + Error string + Path *string + Code *int + Description *string +} + +func NewStackItem(err SystemError) StackItem { + stackItem := StackItem{ + Error: err.Message(), + } + if err.Code() != 0 { + stackItem.Code = &err.code + } + if err.Description() != "" { + stackItem.Description = &err.description + } + if err.Path != "" { + stackItem.Path = &err.Path + } + + return stackItem +} diff --git a/src/models/api_error_response.go b/src/models/api_error_response.go index 8078352..d52a49e 100644 --- a/src/models/api_error_response.go +++ b/src/models/api_error_response.go @@ -5,16 +5,16 @@ import ( ) type ApiErrorStack struct { - Message string `json:"message"` + Error string `json:"error"` Description string `json:"description,omitempty"` Path string `json:"path,omitempty"` - Code int `json:"code"` + Code int `json:"code,omitempty"` } type ApiErrorResponse struct { Message string `json:"message"` Stack []ApiErrorStack `json:"stack,omitempty"` - Code int `json:"code"` + Code int `json:"code,omitempty"` } func IsSystemError(err error) bool { @@ -69,26 +69,24 @@ func NewFromErrorWithCode(err error, code int) ApiErrorResponse { Code: code, } if IsSystemError(err) { - sysError, ok := err.(errors.SystemError) - if ok { - message.Message = sysError.Message() - if len(sysError.NestedError) > 0 { - for _, nestedError := range sysError.NestedError { - stack := ApiErrorStack{ - Message: nestedError.Message, - } - if nestedError.Path != "" { - stack.Path = nestedError.Path - } - if nestedError.Code != 0 { - stack.Code = nestedError.Code - } - if nestedError.Description != "" { - stack.Description = nestedError.Description - } - - message.Stack = append(message.Stack, stack) + sysError := extractSystemError(err) + message.Message = sysError.Message() + if len(sysError.Stack) > 0 { + for _, nestedError := range sysError.Stack { + stack := ApiErrorStack{ + Error: nestedError.Error, + } + if nestedError.Path != nil { + stack.Path = *nestedError.Path + } + if nestedError.Code != nil { + stack.Code = *nestedError.Code + } + if nestedError.Description != nil { + stack.Description = *nestedError.Description } + + message.Stack = append(message.Stack, stack) } } } else { @@ -101,17 +99,38 @@ func NewFromErrorWithCode(err error, code int) ApiErrorResponse { func (r *ApiErrorResponse) ToError() *errors.SystemError { err := errors.NewWithCode(r.Message, r.Code) if len(r.Stack) > 0 { - err.NestedError = make([]errors.NestedError, 0) + err.Stack = make([]errors.StackItem, 0) for _, stack := range r.Stack { - nestedError := errors.NestedError{ - Message: stack.Message, - Code: stack.Code, - Path: stack.Path, - Description: stack.Description, + nestedError := errors.StackItem{ + Error: stack.Error, + } + if stack.Path != "" { + nestedError.Path = &stack.Path + } + if stack.Code != 0 { + nestedError.Code = &stack.Code + } + if stack.Description != "" { + nestedError.Description = &stack.Description } - err.NestedError = append(err.NestedError, nestedError) + err.Stack = append(err.Stack, nestedError) } } return err } + +func extractSystemError(err error) *errors.SystemError { + if IsSystemError(err) { + sysError, ok := err.(errors.SystemError) + if !ok { + sysErrorP, ok := err.(*errors.SystemError) + if ok { + sysError = *sysErrorP + } + } + return &sysError + } + + return nil +} diff --git a/src/orchestrator/get_host_reverse_proxy_config.go b/src/orchestrator/get_host_reverse_proxy_config.go new file mode 100644 index 0000000..22b1e4a --- /dev/null +++ b/src/orchestrator/get_host_reverse_proxy_config.go @@ -0,0 +1,26 @@ +package orchestrator + +import ( + "github.com/Parallels/prl-devops-service/basecontext" + "github.com/Parallels/prl-devops-service/data/models" + "github.com/Parallels/prl-devops-service/serviceprovider" +) + +func (s *OrchestratorService) GetHostReverseProxyConfig(ctx basecontext.ApiContext, hostId string, filter string, noCache bool) (*models.ReverseProxy, error) { + if noCache { + ctx.LogDebugf("[Orchestrator] No cache set, refreshing all hosts...") + s.Refresh() + } + + dbService, err := serviceprovider.GetDatabaseService(ctx) + if err != nil { + return nil, err + } + + hosts, err := dbService.GetOrchestratorReverseProxyConfig(ctx, hostId) + if err != nil { + return nil, err + } + + return hosts, nil +} diff --git a/src/reverse_proxy/main.go b/src/reverse_proxy/main.go index 5655366..0f49fba 100644 --- a/src/reverse_proxy/main.go +++ b/src/reverse_proxy/main.go @@ -74,6 +74,7 @@ func GetConfig() models.ReverseProxyConfig { if cfg == nil || globalReverseProxyService == nil { result.Enabled = false + return result } result.Host = cfg.ReverseProxyHost()