[MM-56348] system/ping: add new method with options (#26079)

This commit is contained in:
Ibrahim Serdar Acikgoz 2024-02-20 14:22:28 +01:00 committed by GitHub
parent 0aaa047ea3
commit 7d8a56019b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 83 additions and 25 deletions

View File

@ -47,6 +47,11 @@
__Minimum server version__: 6.5 __Minimum server version__: 6.5
If "use_rest_semantics" is set to true in the query, the endpoint will not return
an error status code in the header if the request is somehow completed successfully.
__Minimum server version__: 9.6
##### Permissions ##### Permissions
None. None.
@ -64,6 +69,12 @@
required: false required: false
schema: schema:
type: string type: string
- name: use_rest_semantics
in: query
description: Returns 200 status code even if the server status is unhealthy.
required: false
schema:
type: boolean
responses: responses:
"200": "200":
description: Status of the system description: Status of the system

View File

@ -179,6 +179,7 @@ func getSystemPing(c *Context, w http.ResponseWriter, r *http.Request) {
s[filestoreStatusKey] = model.StatusOk s[filestoreStatusKey] = model.StatusOk
appErr := c.App.TestFileStoreConnection() appErr := c.App.TestFileStoreConnection()
if appErr != nil { if appErr != nil {
c.Logger.Warn("Unable to test filestore connection.", mlog.Err(appErr))
s[filestoreStatusKey] = model.StatusUnhealthy s[filestoreStatusKey] = model.StatusUnhealthy
s[model.STATUS] = model.StatusUnhealthy s[model.STATUS] = model.StatusUnhealthy
} }
@ -194,7 +195,7 @@ func getSystemPing(c *Context, w http.ResponseWriter, r *http.Request) {
s["ActiveSearchBackend"] = c.App.ActiveSearchBackend() s["ActiveSearchBackend"] = c.App.ActiveSearchBackend()
if s[model.STATUS] != model.StatusOk { if s[model.STATUS] != model.StatusOk && r.FormValue("use_rest_semantics") != "true" {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
} }
w.Write([]byte(model.MapToJSON(s))) w.Write([]byte(model.MapToJSON(s)))

View File

@ -119,6 +119,7 @@ type Client interface {
MigrateAuthToSaml(ctx context.Context, fromAuthService string, usersMap map[string]string, auto bool) (*model.Response, error) MigrateAuthToSaml(ctx context.Context, fromAuthService string, usersMap map[string]string, auto bool) (*model.Response, error)
GetPing(ctx context.Context) (string, *model.Response, error) GetPing(ctx context.Context) (string, *model.Response, error)
GetPingWithFullServerStatus(ctx context.Context) (map[string]string, *model.Response, error) GetPingWithFullServerStatus(ctx context.Context) (map[string]string, *model.Response, error)
GetPingWithOptions(ctx context.Context, options model.SystemPingOptions) (map[string]string, *model.Response, error)
CreateUpload(ctx context.Context, us *model.UploadSession) (*model.UploadSession, *model.Response, error) CreateUpload(ctx context.Context, us *model.UploadSession) (*model.UploadSession, *model.Response, error)
GetUpload(ctx context.Context, uploadID string) (*model.UploadSession, *model.Response, error) GetUpload(ctx context.Context, uploadID string) (*model.UploadSession, *model.Response, error)
GetUploadsForUser(ctx context.Context, userID string) ([]*model.UploadSession, *model.Response, error) GetUploadsForUser(ctx context.Context, userID string) ([]*model.UploadSession, *model.Response, error)

View File

@ -12,6 +12,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/v8/cmd/mmctl/client" "github.com/mattermost/mattermost/server/v8/cmd/mmctl/client"
"github.com/mattermost/mattermost/server/v8/cmd/mmctl/printer" "github.com/mattermost/mattermost/server/v8/cmd/mmctl/printer"
) )
@ -150,7 +151,10 @@ func systemVersionCmdF(c client.Client, cmd *cobra.Command, _ []string) error {
func systemStatusCmdF(c client.Client, cmd *cobra.Command, _ []string) error { func systemStatusCmdF(c client.Client, cmd *cobra.Command, _ []string) error {
printer.SetSingle(true) printer.SetSingle(true)
status, _, err := c.GetPingWithFullServerStatus(context.TODO()) status, _, err := c.GetPingWithOptions(context.TODO(), model.SystemPingOptions{
FullStatus: true,
RESTSemantics: true,
})
if err != nil { if err != nil {
return fmt.Errorf("unable to fetch server status: %w", err) return fmt.Errorf("unable to fetch server status: %w", err)
} }

View File

@ -188,7 +188,10 @@ func (s *MmctlUnitTestSuite) TestServerStatusCmd() {
expectedStatus := map[string]string{"status": "OK"} expectedStatus := map[string]string{"status": "OK"}
s.client. s.client.
EXPECT(). EXPECT().
GetPingWithFullServerStatus(context.Background()). GetPingWithOptions(context.Background(), model.SystemPingOptions{
FullStatus: true,
RESTSemantics: true,
}).
Return(expectedStatus, &model.Response{}, nil). Return(expectedStatus, &model.Response{}, nil).
Times(1) Times(1)
@ -204,7 +207,10 @@ func (s *MmctlUnitTestSuite) TestServerStatusCmd() {
s.client. s.client.
EXPECT(). EXPECT().
GetPingWithFullServerStatus(context.Background()). GetPingWithOptions(context.Background(), model.SystemPingOptions{
FullStatus: true,
RESTSemantics: true,
}).
Return(nil, &model.Response{}, errors.New("mock error")). Return(nil, &model.Response{}, errors.New("mock error")).
Times(1) Times(1)

View File

@ -1054,6 +1054,22 @@ func (mr *MockClientMockRecorder) GetPingWithFullServerStatus(arg0 interface{})
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPingWithFullServerStatus", reflect.TypeOf((*MockClient)(nil).GetPingWithFullServerStatus), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPingWithFullServerStatus", reflect.TypeOf((*MockClient)(nil).GetPingWithFullServerStatus), arg0)
} }
// GetPingWithOptions mocks base method.
func (m *MockClient) GetPingWithOptions(arg0 context.Context, arg1 model.SystemPingOptions) (map[string]string, *model.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetPingWithOptions", arg0, arg1)
ret0, _ := ret[0].(map[string]string)
ret1, _ := ret[1].(*model.Response)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
// GetPingWithOptions indicates an expected call of GetPingWithOptions.
func (mr *MockClientMockRecorder) GetPingWithOptions(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPingWithOptions", reflect.TypeOf((*MockClient)(nil).GetPingWithOptions), arg0, arg1)
}
// GetPlugins mocks base method. // GetPlugins mocks base method.
func (m *MockClient) GetPlugins(arg0 context.Context) (*model.PluginsResponse, *model.Response, error) { func (m *MockClient) GetPlugins(arg0 context.Context) (*model.PluginsResponse, *model.Response, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()

View File

@ -4607,38 +4607,46 @@ func (c *Client4) GenerateSupportPacket(ctx context.Context) ([]byte, *Response,
} }
// GetPing will return ok if the running goRoutines are below the threshold and unhealthy for above. // GetPing will return ok if the running goRoutines are below the threshold and unhealthy for above.
// DEPRECATED: Use GetPingWithOptions method instead.
func (c *Client4) GetPing(ctx context.Context) (string, *Response, error) { func (c *Client4) GetPing(ctx context.Context) (string, *Response, error) {
r, err := c.DoAPIGet(ctx, c.systemRoute()+"/ping", "") ping, resp, err := c.GetPingWithOptions(ctx, SystemPingOptions{})
if r != nil && r.StatusCode == 500 { status := ""
defer r.Body.Close() if ping != nil {
return StatusUnhealthy, BuildResponse(r), err status = ping["status"]
} }
if err != nil { return status, resp, err
return "", BuildResponse(r), err
}
defer closeBody(r)
return MapFromJSON(r.Body)["status"], BuildResponse(r), nil
} }
// GetPingWithServerStatus will return ok if several basic server health checks // GetPingWithServerStatus will return ok if several basic server health checks
// all pass successfully. // all pass successfully.
// DEPRECATED: Use GetPingWithOptions method instead.
func (c *Client4) GetPingWithServerStatus(ctx context.Context) (string, *Response, error) { func (c *Client4) GetPingWithServerStatus(ctx context.Context) (string, *Response, error) {
r, err := c.DoAPIGet(ctx, c.systemRoute()+"/ping?get_server_status="+c.boolString(true), "") ping, resp, err := c.GetPingWithOptions(ctx, SystemPingOptions{FullStatus: true})
if r != nil && r.StatusCode == 500 { status := ""
defer r.Body.Close() if ping != nil {
return StatusUnhealthy, BuildResponse(r), err status = ping["status"]
} }
if err != nil { return status, resp, err
return "", BuildResponse(r), err
}
defer closeBody(r)
return MapFromJSON(r.Body)["status"], BuildResponse(r), nil
} }
// GetPingWithFullServerStatus will return the full status if several basic server // GetPingWithFullServerStatus will return the full status if several basic server
// health checks all pass successfully. // health checks all pass successfully.
// DEPRECATED: Use GetPingWithOptions method instead.
func (c *Client4) GetPingWithFullServerStatus(ctx context.Context) (map[string]string, *Response, error) { func (c *Client4) GetPingWithFullServerStatus(ctx context.Context) (map[string]string, *Response, error) {
r, err := c.DoAPIGet(ctx, c.systemRoute()+"/ping?get_server_status="+c.boolString(true), "") return c.GetPingWithOptions(ctx, SystemPingOptions{FullStatus: true})
}
// GetPingWithOptions will return the status according to the options
func (c *Client4) GetPingWithOptions(ctx context.Context, options SystemPingOptions) (map[string]string, *Response, error) {
pingURL, err := url.Parse(c.systemRoute() + "/ping")
if err != nil {
return nil, nil, fmt.Errorf("could not parse query: %w", err)
}
values := pingURL.Query()
values.Set("get_server_status", c.boolString(options.FullStatus))
values.Set("use_rest_semantics", c.boolString(options.RESTSemantics))
pingURL.RawQuery = values.Encode()
r, err := c.DoAPIGet(ctx, pingURL.String(), "")
if r != nil && r.StatusCode == 500 { if r != nil && r.StatusCode == 500 {
defer r.Body.Close() defer r.Body.Close()
return map[string]string{"status": StatusUnhealthy}, BuildResponse(r), err return map[string]string{"status": StatusUnhealthy}, BuildResponse(r), err

View File

@ -245,3 +245,14 @@ type LogEntry struct {
Timestamp string Timestamp string
Level string Level string
} }
// SystemPingOptions is the options for setting contents of the system ping
// response.
type SystemPingOptions struct {
// FullStatus allows server to set the detailed information about
// the system status.
FullStatus bool
// RestSemantics allows server to return 200 code even if the server
// status is unhealthy.
RESTSemantics bool
}

View File

@ -2456,8 +2456,8 @@ export default class Client4 {
database_status: string; database_status: string;
filestore_status: string; filestore_status: string;
}>( }>(
`${this.getBaseRoute()}/system/ping${buildQueryString({get_server_status: getServerStatus, device_id: deviceId})}`, `${this.getBaseRoute()}/system/ping${buildQueryString({get_server_status: getServerStatus, device_id: deviceId, use_rest_semantics: true})}`,
{method: 'get', ignoreStatus: true}, {method: 'get'},
); );
}; };