diff --git a/api/qan/v1/json/client/qan_service/health_check_parameters.go b/api/qan/v1/json/client/qan_service/health_check_parameters.go new file mode 100644 index 00000000000..d7e15d2d7bb --- /dev/null +++ b/api/qan/v1/json/client/qan_service/health_check_parameters.go @@ -0,0 +1,127 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package qan_service + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewHealthCheckParams creates a new HealthCheckParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewHealthCheckParams() *HealthCheckParams { + return &HealthCheckParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewHealthCheckParamsWithTimeout creates a new HealthCheckParams object +// with the ability to set a timeout on a request. +func NewHealthCheckParamsWithTimeout(timeout time.Duration) *HealthCheckParams { + return &HealthCheckParams{ + timeout: timeout, + } +} + +// NewHealthCheckParamsWithContext creates a new HealthCheckParams object +// with the ability to set a context for a request. +func NewHealthCheckParamsWithContext(ctx context.Context) *HealthCheckParams { + return &HealthCheckParams{ + Context: ctx, + } +} + +// NewHealthCheckParamsWithHTTPClient creates a new HealthCheckParams object +// with the ability to set a custom HTTPClient for a request. +func NewHealthCheckParamsWithHTTPClient(client *http.Client) *HealthCheckParams { + return &HealthCheckParams{ + HTTPClient: client, + } +} + +/* +HealthCheckParams contains all the parameters to send to the API endpoint + + for the health check operation. + + Typically these are written to a http.Request. +*/ +type HealthCheckParams struct { + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the health check params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *HealthCheckParams) WithDefaults() *HealthCheckParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the health check params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *HealthCheckParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the health check params +func (o *HealthCheckParams) WithTimeout(timeout time.Duration) *HealthCheckParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the health check params +func (o *HealthCheckParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the health check params +func (o *HealthCheckParams) WithContext(ctx context.Context) *HealthCheckParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the health check params +func (o *HealthCheckParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the health check params +func (o *HealthCheckParams) WithHTTPClient(client *http.Client) *HealthCheckParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the health check params +func (o *HealthCheckParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WriteToRequest writes these params to a swagger request +func (o *HealthCheckParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/api/qan/v1/json/client/qan_service/health_check_responses.go b/api/qan/v1/json/client/qan_service/health_check_responses.go new file mode 100644 index 00000000000..7ba2cf47eb3 --- /dev/null +++ b/api/qan/v1/json/client/qan_service/health_check_responses.go @@ -0,0 +1,414 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package qan_service + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + stderrors "errors" + "fmt" + "io" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// HealthCheckReader is a Reader for the HealthCheck structure. +type HealthCheckReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *HealthCheckReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (any, error) { + switch response.Code() { + case 200: + result := NewHealthCheckOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + result := NewHealthCheckDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result + } +} + +// NewHealthCheckOK creates a HealthCheckOK with default headers values +func NewHealthCheckOK() *HealthCheckOK { + return &HealthCheckOK{} +} + +/* +HealthCheckOK describes a response with status code 200, with default header values. + +A successful response. +*/ +type HealthCheckOK struct { + Payload any +} + +// IsSuccess returns true when this health check Ok response has a 2xx status code +func (o *HealthCheckOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this health check Ok response has a 3xx status code +func (o *HealthCheckOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this health check Ok response has a 4xx status code +func (o *HealthCheckOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this health check Ok response has a 5xx status code +func (o *HealthCheckOK) IsServerError() bool { + return false +} + +// IsCode returns true when this health check Ok response a status code equal to that given +func (o *HealthCheckOK) IsCode(code int) bool { + return code == 200 +} + +// Code gets the status code for the health check Ok response +func (o *HealthCheckOK) Code() int { + return 200 +} + +func (o *HealthCheckOK) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /v1/qan/health][%d] healthCheckOk %s", 200, payload) +} + +func (o *HealthCheckOK) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /v1/qan/health][%d] healthCheckOk %s", 200, payload) +} + +func (o *HealthCheckOK) GetPayload() any { + return o.Payload +} + +func (o *HealthCheckOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && !stderrors.Is(err, io.EOF) { + return err + } + + return nil +} + +// NewHealthCheckDefault creates a HealthCheckDefault with default headers values +func NewHealthCheckDefault(code int) *HealthCheckDefault { + return &HealthCheckDefault{ + _statusCode: code, + } +} + +/* +HealthCheckDefault describes a response with status code -1, with default header values. + +An unexpected error response. +*/ +type HealthCheckDefault struct { + _statusCode int + + Payload *HealthCheckDefaultBody +} + +// IsSuccess returns true when this health check default response has a 2xx status code +func (o *HealthCheckDefault) IsSuccess() bool { + return o._statusCode/100 == 2 +} + +// IsRedirect returns true when this health check default response has a 3xx status code +func (o *HealthCheckDefault) IsRedirect() bool { + return o._statusCode/100 == 3 +} + +// IsClientError returns true when this health check default response has a 4xx status code +func (o *HealthCheckDefault) IsClientError() bool { + return o._statusCode/100 == 4 +} + +// IsServerError returns true when this health check default response has a 5xx status code +func (o *HealthCheckDefault) IsServerError() bool { + return o._statusCode/100 == 5 +} + +// IsCode returns true when this health check default response a status code equal to that given +func (o *HealthCheckDefault) IsCode(code int) bool { + return o._statusCode == code +} + +// Code gets the status code for the health check default response +func (o *HealthCheckDefault) Code() int { + return o._statusCode +} + +func (o *HealthCheckDefault) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /v1/qan/health][%d] HealthCheck default %s", o._statusCode, payload) +} + +func (o *HealthCheckDefault) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /v1/qan/health][%d] HealthCheck default %s", o._statusCode, payload) +} + +func (o *HealthCheckDefault) GetPayload() *HealthCheckDefaultBody { + return o.Payload +} + +func (o *HealthCheckDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + o.Payload = new(HealthCheckDefaultBody) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && !stderrors.Is(err, io.EOF) { + return err + } + + return nil +} + +/* +HealthCheckDefaultBody health check default body +swagger:model HealthCheckDefaultBody +*/ +type HealthCheckDefaultBody struct { + // code + Code int32 `json:"code,omitempty"` + + // message + Message string `json:"message,omitempty"` + + // details + Details []*HealthCheckDefaultBodyDetailsItems0 `json:"details"` +} + +// Validate validates this health check default body +func (o *HealthCheckDefaultBody) Validate(formats strfmt.Registry) error { + var res []error + + if err := o.validateDetails(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (o *HealthCheckDefaultBody) validateDetails(formats strfmt.Registry) error { + if swag.IsZero(o.Details) { // not required + return nil + } + + for i := 0; i < len(o.Details); i++ { + if swag.IsZero(o.Details[i]) { // not required + continue + } + + if o.Details[i] != nil { + if err := o.Details[i].Validate(formats); err != nil { + ve := new(errors.Validation) + if stderrors.As(err, &ve) { + return ve.ValidateName("HealthCheck default" + "." + "details" + "." + strconv.Itoa(i)) + } + ce := new(errors.CompositeError) + if stderrors.As(err, &ce) { + return ce.ValidateName("HealthCheck default" + "." + "details" + "." + strconv.Itoa(i)) + } + + return err + } + } + + } + + return nil +} + +// ContextValidate validate this health check default body based on the context it is used +func (o *HealthCheckDefaultBody) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := o.contextValidateDetails(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (o *HealthCheckDefaultBody) contextValidateDetails(ctx context.Context, formats strfmt.Registry) error { + for i := 0; i < len(o.Details); i++ { + if o.Details[i] != nil { + + if swag.IsZero(o.Details[i]) { // not required + return nil + } + + if err := o.Details[i].ContextValidate(ctx, formats); err != nil { + ve := new(errors.Validation) + if stderrors.As(err, &ve) { + return ve.ValidateName("HealthCheck default" + "." + "details" + "." + strconv.Itoa(i)) + } + ce := new(errors.CompositeError) + if stderrors.As(err, &ce) { + return ce.ValidateName("HealthCheck default" + "." + "details" + "." + strconv.Itoa(i)) + } + + return err + } + } + } + + return nil +} + +// MarshalBinary interface implementation +func (o *HealthCheckDefaultBody) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *HealthCheckDefaultBody) UnmarshalBinary(b []byte) error { + var res HealthCheckDefaultBody + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} + +/* +HealthCheckDefaultBodyDetailsItems0 health check default body details items0 +swagger:model HealthCheckDefaultBodyDetailsItems0 +*/ +type HealthCheckDefaultBodyDetailsItems0 struct { + // at type + AtType string `json:"@type,omitempty"` + + // health check default body details items0 + HealthCheckDefaultBodyDetailsItems0 map[string]any `json:"-"` +} + +// UnmarshalJSON unmarshals this object with additional properties from JSON +func (o *HealthCheckDefaultBodyDetailsItems0) UnmarshalJSON(data []byte) error { + // stage 1, bind the properties + var stage1 struct { + // at type + AtType string `json:"@type,omitempty"` + } + if err := json.Unmarshal(data, &stage1); err != nil { + return err + } + var rcv HealthCheckDefaultBodyDetailsItems0 + + rcv.AtType = stage1.AtType + *o = rcv + + // stage 2, remove properties and add to map + stage2 := make(map[string]json.RawMessage) + if err := json.Unmarshal(data, &stage2); err != nil { + return err + } + + delete(stage2, "@type") + // stage 3, add additional properties values + if len(stage2) > 0 { + result := make(map[string]any) + for k, v := range stage2 { + var toadd any + if err := json.Unmarshal(v, &toadd); err != nil { + return err + } + result[k] = toadd + } + o.HealthCheckDefaultBodyDetailsItems0 = result + } + + return nil +} + +// MarshalJSON marshals this object with additional properties into a JSON object +func (o HealthCheckDefaultBodyDetailsItems0) MarshalJSON() ([]byte, error) { + var stage1 struct { + // at type + AtType string `json:"@type,omitempty"` + } + + stage1.AtType = o.AtType + + // make JSON object for known properties + props, err := json.Marshal(stage1) + if err != nil { + return nil, err + } + + if len(o.HealthCheckDefaultBodyDetailsItems0) == 0 { // no additional properties + return props, nil + } + + // make JSON object for the additional properties + additional, err := json.Marshal(o.HealthCheckDefaultBodyDetailsItems0) + if err != nil { + return nil, err + } + + if len(props) < 3 { // "{}": only additional properties + return additional, nil + } + + // concatenate the 2 objects + return swag.ConcatJSON(props, additional), nil +} + +// Validate validates this health check default body details items0 +func (o *HealthCheckDefaultBodyDetailsItems0) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this health check default body details items0 based on context it is used +func (o *HealthCheckDefaultBodyDetailsItems0) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (o *HealthCheckDefaultBodyDetailsItems0) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *HealthCheckDefaultBodyDetailsItems0) UnmarshalBinary(b []byte) error { + var res HealthCheckDefaultBodyDetailsItems0 + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} diff --git a/api/qan/v1/json/client/qan_service/qan_service_client.go b/api/qan/v1/json/client/qan_service/qan_service_client.go index 855a5bef66e..583f9eabad3 100644 --- a/api/qan/v1/json/client/qan_service/qan_service_client.go +++ b/api/qan/v1/json/client/qan_service/qan_service_client.go @@ -72,6 +72,8 @@ type ClientService interface { GetReport(params *GetReportParams, opts ...ClientOption) (*GetReportOK, error) + HealthCheck(params *HealthCheckParams, opts ...ClientOption) (*HealthCheckOK, error) + QueryExists(params *QueryExistsParams, opts ...ClientOption) (*QueryExistsOK, error) SchemaByQueryID(params *SchemaByQueryIDParams, opts ...ClientOption) (*SchemaByQueryIDOK, error) @@ -475,6 +477,50 @@ func (a *Client) GetReport(params *GetReportParams, opts ...ClientOption) (*GetR return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) } +/* +HealthCheck healths check + +Returns readiness of QAN API2 service. +*/ +func (a *Client) HealthCheck(params *HealthCheckParams, opts ...ClientOption) (*HealthCheckOK, error) { + // NOTE: parameters are not validated before sending + if params == nil { + params = NewHealthCheckParams() + } + op := &runtime.ClientOperation{ + ID: "HealthCheck", + Method: "GET", + PathPattern: "/v1/qan/health", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http", "https"}, + Params: params, + Reader: &HealthCheckReader{formats: a.formats}, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + + // only one success response has to be checked + success, ok := result.(*HealthCheckOK) + if ok { + return success, nil + } + + // unexpected success response. + // + // a default response is provided: fill this and return an error + unexpectedSuccess := result.(*HealthCheckDefault) + + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + /* QueryExists checks query existence diff --git a/api/qan/v1/json/v1.json b/api/qan/v1/json/v1.json index d436f318df4..ffdd64b89e6 100644 --- a/api/qan/v1/json/v1.json +++ b/api/qan/v1/json/v1.json @@ -15,6 +15,56 @@ "version": "v1" }, "paths": { + "/v1/qan/health": { + "get": { + "description": "Returns readiness of QAN API2 service.", + "tags": [ + "QANService" + ], + "summary": "Health Check", + "operationId": "HealthCheck", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "description": "HealthCheckResponse is empty, based on returned error qan-api2 is ready or not.", + "type": "object" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32", + "x-order": 0 + }, + "message": { + "type": "string", + "x-order": 1 + }, + "details": { + "type": "array", + "items": { + "type": "object", + "properties": { + "@type": { + "type": "string", + "x-order": 0 + } + }, + "additionalProperties": {} + }, + "x-order": 2 + } + } + } + } + } + } + }, "/v1/qan/metrics:getFilters": { "post": { "description": "Provides a filtered map of metrics names.", diff --git a/api/qan/v1/service.pb.go b/api/qan/v1/service.pb.go index 33891137af9..729cc032940 100644 --- a/api/qan/v1/service.pb.go +++ b/api/qan/v1/service.pb.go @@ -108,6 +108,80 @@ func (x *GetMetricsNamesResponse) GetData() map[string]string { return nil } +// HealthCheckRequest is empty. +type HealthCheckRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *HealthCheckRequest) Reset() { + *x = HealthCheckRequest{} + mi := &file_qan_v1_service_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *HealthCheckRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HealthCheckRequest) ProtoMessage() {} + +func (x *HealthCheckRequest) ProtoReflect() protoreflect.Message { + mi := &file_qan_v1_service_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HealthCheckRequest.ProtoReflect.Descriptor instead. +func (*HealthCheckRequest) Descriptor() ([]byte, []int) { + return file_qan_v1_service_proto_rawDescGZIP(), []int{2} +} + +// HealthCheckResponse is empty, based on returned error qan-api2 is ready or not. +type HealthCheckResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *HealthCheckResponse) Reset() { + *x = HealthCheckResponse{} + mi := &file_qan_v1_service_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *HealthCheckResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HealthCheckResponse) ProtoMessage() {} + +func (x *HealthCheckResponse) ProtoReflect() protoreflect.Message { + mi := &file_qan_v1_service_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HealthCheckResponse.ProtoReflect.Descriptor instead. +func (*HealthCheckResponse) Descriptor() ([]byte, []int) { + return file_qan_v1_service_proto_rawDescGZIP(), []int{3} +} + var File_qan_v1_service_proto protoreflect.FileDescriptor const file_qan_v1_service_proto_rawDesc = "" + @@ -118,7 +192,9 @@ const file_qan_v1_service_proto_rawDesc = "" + "\x04data\x18\x01 \x03(\v2).qan.v1.GetMetricsNamesResponse.DataEntryR\x04data\x1a7\n" + "\tDataEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x012\x94\x10\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x14\n" + + "\x12HealthCheckRequest\"\x15\n" + + "\x13HealthCheckResponse2\xae\x11\n" + "\n" + "QANService\x12\xb8\x01\n" + "\tGetReport\x12\x18.qan.v1.GetReportRequest\x1a\x19.qan.v1.GetReportResponse\"v\x92AO\x12\n" + @@ -135,7 +211,8 @@ const file_qan_v1_service_proto_rawDesc = "" + "\vQueryExists\x12\x1a.qan.v1.QueryExistsRequest\x1a\x1b.qan.v1.QueryExistsResponse\"`\x92A>\x12\x15Check Query Existence\x1a%Checks if query exists in clickhouse.\x82\xd3\xe4\x93\x02\x19:\x01*\"\x14/v1/qan/query:exists\x12\xbd\x01\n" + "\x0fSchemaByQueryID\x12\x1e.qan.v1.SchemaByQueryIDRequest\x1a\x1f.qan.v1.SchemaByQueryIDResponse\"i\x92AD\x12\n" + "Get Schema\x1a6Provides the schema for a given queryID and serviceID.\x82\xd3\xe4\x93\x02\x1c:\x01*\"\x17/v1/qan/query:getSchema\x12\xb1\x01\n" + - "\x0fGetQueryExample\x12\x1e.qan.v1.GetQueryExampleRequest\x1a\x1f.qan.v1.GetQueryExampleResponse\"]\x92A7\x12\x11Get Query Example\x1a\"Provides a list of query examples.\x82\xd3\xe4\x93\x02\x1d:\x01*\"\x18/v1/qan/query:getExampleB|\n" + + "\x0fGetQueryExample\x12\x1e.qan.v1.GetQueryExampleRequest\x1a\x1f.qan.v1.GetQueryExampleResponse\"]\x92A7\x12\x11Get Query Example\x1a\"Provides a list of query examples.\x82\xd3\xe4\x93\x02\x1d:\x01*\"\x18/v1/qan/query:getExample\x12\x97\x01\n" + + "\vHealthCheck\x12\x1a.qan.v1.HealthCheckRequest\x1a\x1b.qan.v1.HealthCheckResponse\"O\x92A6\x12\fHealth Check\x1a&Returns readiness of QAN API2 service.\x82\xd3\xe4\x93\x02\x10\x12\x0e/v1/qan/healthB|\n" + "\n" + "com.qan.v1B\fServiceProtoP\x01Z'github.com/percona/pmm/api/qan/v1;qanv1\xa2\x02\x03QXX\xaa\x02\x06Qan.V1\xca\x02\x06Qan\\V1\xe2\x02\x12Qan\\V1\\GPBMetadata\xea\x02\aQan::V1b\x06proto3" @@ -152,60 +229,64 @@ func file_qan_v1_service_proto_rawDescGZIP() []byte { } var ( - file_qan_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 3) + file_qan_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 5) file_qan_v1_service_proto_goTypes = []any{ (*GetMetricsNamesRequest)(nil), // 0: qan.v1.GetMetricsNamesRequest (*GetMetricsNamesResponse)(nil), // 1: qan.v1.GetMetricsNamesResponse - nil, // 2: qan.v1.GetMetricsNamesResponse.DataEntry - (*GetReportRequest)(nil), // 3: qan.v1.GetReportRequest - (*GetFilteredMetricsNamesRequest)(nil), // 4: qan.v1.GetFilteredMetricsNamesRequest - (*GetMetricsRequest)(nil), // 5: qan.v1.GetMetricsRequest - (*GetLabelsRequest)(nil), // 6: qan.v1.GetLabelsRequest - (*GetHistogramRequest)(nil), // 7: qan.v1.GetHistogramRequest - (*ExplainFingerprintByQueryIDRequest)(nil), // 8: qan.v1.ExplainFingerprintByQueryIDRequest - (*GetQueryPlanRequest)(nil), // 9: qan.v1.GetQueryPlanRequest - (*QueryExistsRequest)(nil), // 10: qan.v1.QueryExistsRequest - (*SchemaByQueryIDRequest)(nil), // 11: qan.v1.SchemaByQueryIDRequest - (*GetQueryExampleRequest)(nil), // 12: qan.v1.GetQueryExampleRequest - (*GetReportResponse)(nil), // 13: qan.v1.GetReportResponse - (*GetFilteredMetricsNamesResponse)(nil), // 14: qan.v1.GetFilteredMetricsNamesResponse - (*GetMetricsResponse)(nil), // 15: qan.v1.GetMetricsResponse - (*GetLabelsResponse)(nil), // 16: qan.v1.GetLabelsResponse - (*GetHistogramResponse)(nil), // 17: qan.v1.GetHistogramResponse - (*ExplainFingerprintByQueryIDResponse)(nil), // 18: qan.v1.ExplainFingerprintByQueryIDResponse - (*GetQueryPlanResponse)(nil), // 19: qan.v1.GetQueryPlanResponse - (*QueryExistsResponse)(nil), // 20: qan.v1.QueryExistsResponse - (*SchemaByQueryIDResponse)(nil), // 21: qan.v1.SchemaByQueryIDResponse - (*GetQueryExampleResponse)(nil), // 22: qan.v1.GetQueryExampleResponse + (*HealthCheckRequest)(nil), // 2: qan.v1.HealthCheckRequest + (*HealthCheckResponse)(nil), // 3: qan.v1.HealthCheckResponse + nil, // 4: qan.v1.GetMetricsNamesResponse.DataEntry + (*GetReportRequest)(nil), // 5: qan.v1.GetReportRequest + (*GetFilteredMetricsNamesRequest)(nil), // 6: qan.v1.GetFilteredMetricsNamesRequest + (*GetMetricsRequest)(nil), // 7: qan.v1.GetMetricsRequest + (*GetLabelsRequest)(nil), // 8: qan.v1.GetLabelsRequest + (*GetHistogramRequest)(nil), // 9: qan.v1.GetHistogramRequest + (*ExplainFingerprintByQueryIDRequest)(nil), // 10: qan.v1.ExplainFingerprintByQueryIDRequest + (*GetQueryPlanRequest)(nil), // 11: qan.v1.GetQueryPlanRequest + (*QueryExistsRequest)(nil), // 12: qan.v1.QueryExistsRequest + (*SchemaByQueryIDRequest)(nil), // 13: qan.v1.SchemaByQueryIDRequest + (*GetQueryExampleRequest)(nil), // 14: qan.v1.GetQueryExampleRequest + (*GetReportResponse)(nil), // 15: qan.v1.GetReportResponse + (*GetFilteredMetricsNamesResponse)(nil), // 16: qan.v1.GetFilteredMetricsNamesResponse + (*GetMetricsResponse)(nil), // 17: qan.v1.GetMetricsResponse + (*GetLabelsResponse)(nil), // 18: qan.v1.GetLabelsResponse + (*GetHistogramResponse)(nil), // 19: qan.v1.GetHistogramResponse + (*ExplainFingerprintByQueryIDResponse)(nil), // 20: qan.v1.ExplainFingerprintByQueryIDResponse + (*GetQueryPlanResponse)(nil), // 21: qan.v1.GetQueryPlanResponse + (*QueryExistsResponse)(nil), // 22: qan.v1.QueryExistsResponse + (*SchemaByQueryIDResponse)(nil), // 23: qan.v1.SchemaByQueryIDResponse + (*GetQueryExampleResponse)(nil), // 24: qan.v1.GetQueryExampleResponse } ) var file_qan_v1_service_proto_depIdxs = []int32{ - 2, // 0: qan.v1.GetMetricsNamesResponse.data:type_name -> qan.v1.GetMetricsNamesResponse.DataEntry - 3, // 1: qan.v1.QANService.GetReport:input_type -> qan.v1.GetReportRequest - 4, // 2: qan.v1.QANService.GetFilteredMetricsNames:input_type -> qan.v1.GetFilteredMetricsNamesRequest + 4, // 0: qan.v1.GetMetricsNamesResponse.data:type_name -> qan.v1.GetMetricsNamesResponse.DataEntry + 5, // 1: qan.v1.QANService.GetReport:input_type -> qan.v1.GetReportRequest + 6, // 2: qan.v1.QANService.GetFilteredMetricsNames:input_type -> qan.v1.GetFilteredMetricsNamesRequest 0, // 3: qan.v1.QANService.GetMetricsNames:input_type -> qan.v1.GetMetricsNamesRequest - 5, // 4: qan.v1.QANService.GetMetrics:input_type -> qan.v1.GetMetricsRequest - 6, // 5: qan.v1.QANService.GetLabels:input_type -> qan.v1.GetLabelsRequest - 7, // 6: qan.v1.QANService.GetHistogram:input_type -> qan.v1.GetHistogramRequest - 8, // 7: qan.v1.QANService.ExplainFingerprintByQueryID:input_type -> qan.v1.ExplainFingerprintByQueryIDRequest - 9, // 8: qan.v1.QANService.GetQueryPlan:input_type -> qan.v1.GetQueryPlanRequest - 10, // 9: qan.v1.QANService.QueryExists:input_type -> qan.v1.QueryExistsRequest - 11, // 10: qan.v1.QANService.SchemaByQueryID:input_type -> qan.v1.SchemaByQueryIDRequest - 12, // 11: qan.v1.QANService.GetQueryExample:input_type -> qan.v1.GetQueryExampleRequest - 13, // 12: qan.v1.QANService.GetReport:output_type -> qan.v1.GetReportResponse - 14, // 13: qan.v1.QANService.GetFilteredMetricsNames:output_type -> qan.v1.GetFilteredMetricsNamesResponse - 1, // 14: qan.v1.QANService.GetMetricsNames:output_type -> qan.v1.GetMetricsNamesResponse - 15, // 15: qan.v1.QANService.GetMetrics:output_type -> qan.v1.GetMetricsResponse - 16, // 16: qan.v1.QANService.GetLabels:output_type -> qan.v1.GetLabelsResponse - 17, // 17: qan.v1.QANService.GetHistogram:output_type -> qan.v1.GetHistogramResponse - 18, // 18: qan.v1.QANService.ExplainFingerprintByQueryID:output_type -> qan.v1.ExplainFingerprintByQueryIDResponse - 19, // 19: qan.v1.QANService.GetQueryPlan:output_type -> qan.v1.GetQueryPlanResponse - 20, // 20: qan.v1.QANService.QueryExists:output_type -> qan.v1.QueryExistsResponse - 21, // 21: qan.v1.QANService.SchemaByQueryID:output_type -> qan.v1.SchemaByQueryIDResponse - 22, // 22: qan.v1.QANService.GetQueryExample:output_type -> qan.v1.GetQueryExampleResponse - 12, // [12:23] is the sub-list for method output_type - 1, // [1:12] is the sub-list for method input_type + 7, // 4: qan.v1.QANService.GetMetrics:input_type -> qan.v1.GetMetricsRequest + 8, // 5: qan.v1.QANService.GetLabels:input_type -> qan.v1.GetLabelsRequest + 9, // 6: qan.v1.QANService.GetHistogram:input_type -> qan.v1.GetHistogramRequest + 10, // 7: qan.v1.QANService.ExplainFingerprintByQueryID:input_type -> qan.v1.ExplainFingerprintByQueryIDRequest + 11, // 8: qan.v1.QANService.GetQueryPlan:input_type -> qan.v1.GetQueryPlanRequest + 12, // 9: qan.v1.QANService.QueryExists:input_type -> qan.v1.QueryExistsRequest + 13, // 10: qan.v1.QANService.SchemaByQueryID:input_type -> qan.v1.SchemaByQueryIDRequest + 14, // 11: qan.v1.QANService.GetQueryExample:input_type -> qan.v1.GetQueryExampleRequest + 2, // 12: qan.v1.QANService.HealthCheck:input_type -> qan.v1.HealthCheckRequest + 15, // 13: qan.v1.QANService.GetReport:output_type -> qan.v1.GetReportResponse + 16, // 14: qan.v1.QANService.GetFilteredMetricsNames:output_type -> qan.v1.GetFilteredMetricsNamesResponse + 1, // 15: qan.v1.QANService.GetMetricsNames:output_type -> qan.v1.GetMetricsNamesResponse + 17, // 16: qan.v1.QANService.GetMetrics:output_type -> qan.v1.GetMetricsResponse + 18, // 17: qan.v1.QANService.GetLabels:output_type -> qan.v1.GetLabelsResponse + 19, // 18: qan.v1.QANService.GetHistogram:output_type -> qan.v1.GetHistogramResponse + 20, // 19: qan.v1.QANService.ExplainFingerprintByQueryID:output_type -> qan.v1.ExplainFingerprintByQueryIDResponse + 21, // 20: qan.v1.QANService.GetQueryPlan:output_type -> qan.v1.GetQueryPlanResponse + 22, // 21: qan.v1.QANService.QueryExists:output_type -> qan.v1.QueryExistsResponse + 23, // 22: qan.v1.QANService.SchemaByQueryID:output_type -> qan.v1.SchemaByQueryIDResponse + 24, // 23: qan.v1.QANService.GetQueryExample:output_type -> qan.v1.GetQueryExampleResponse + 3, // 24: qan.v1.QANService.HealthCheck:output_type -> qan.v1.HealthCheckResponse + 13, // [13:25] is the sub-list for method output_type + 1, // [1:13] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name @@ -225,7 +306,7 @@ func file_qan_v1_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_qan_v1_service_proto_rawDesc), len(file_qan_v1_service_proto_rawDesc)), NumEnums: 0, - NumMessages: 3, + NumMessages: 5, NumExtensions: 0, NumServices: 1, }, diff --git a/api/qan/v1/service.pb.gw.go b/api/qan/v1/service.pb.gw.go index 9ee6eb2d083..3b2061fed6d 100644 --- a/api/qan/v1/service.pb.gw.go +++ b/api/qan/v1/service.pb.gw.go @@ -344,6 +344,27 @@ func local_request_QANService_GetQueryExample_0(ctx context.Context, marshaler r return msg, metadata, err } +func request_QANService_HealthCheck_0(ctx context.Context, marshaler runtime.Marshaler, client QANServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq HealthCheckRequest + metadata runtime.ServerMetadata + ) + if req.Body != nil { + _, _ = io.Copy(io.Discard, req.Body) + } + msg, err := client.HealthCheck(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_QANService_HealthCheck_0(ctx context.Context, marshaler runtime.Marshaler, server QANServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq HealthCheckRequest + metadata runtime.ServerMetadata + ) + msg, err := server.HealthCheck(ctx, &protoReq) + return msg, metadata, err +} + // RegisterQANServiceHandlerServer registers the http handlers for service QANService to "mux". // UnaryRPC :call QANServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -570,6 +591,26 @@ func RegisterQANServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, } forward_QANService_GetQueryExample_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) + mux.Handle(http.MethodGet, pattern_QANService_HealthCheck_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/qan.v1.QANService/HealthCheck", runtime.WithHTTPPathPattern("/v1/qan/health")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_QANService_HealthCheck_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_QANService_HealthCheck_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) return nil } @@ -797,6 +838,23 @@ func RegisterQANServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, } forward_QANService_GetQueryExample_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) + mux.Handle(http.MethodGet, pattern_QANService_HealthCheck_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/qan.v1.QANService/HealthCheck", runtime.WithHTTPPathPattern("/v1/qan/health")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_QANService_HealthCheck_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_QANService_HealthCheck_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) return nil } @@ -812,6 +870,7 @@ var ( pattern_QANService_QueryExists_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "qan", "query"}, "exists")) pattern_QANService_SchemaByQueryID_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "qan", "query"}, "getSchema")) pattern_QANService_GetQueryExample_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "qan", "query"}, "getExample")) + pattern_QANService_HealthCheck_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "qan", "health"}, "")) ) var ( @@ -826,4 +885,5 @@ var ( forward_QANService_QueryExists_0 = runtime.ForwardResponseMessage forward_QANService_SchemaByQueryID_0 = runtime.ForwardResponseMessage forward_QANService_GetQueryExample_0 = runtime.ForwardResponseMessage + forward_QANService_HealthCheck_0 = runtime.ForwardResponseMessage ) diff --git a/api/qan/v1/service.pb.validate.go b/api/qan/v1/service.pb.validate.go index ce719a826ee..611ba4ecddd 100644 --- a/api/qan/v1/service.pb.validate.go +++ b/api/qan/v1/service.pb.validate.go @@ -240,3 +240,207 @@ var _ interface { Cause() error ErrorName() string } = GetMetricsNamesResponseValidationError{} + +// Validate checks the field values on HealthCheckRequest with the rules +// defined in the proto definition for this message. If any rules are +// violated, the first error encountered is returned, or nil if there are no violations. +func (m *HealthCheckRequest) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on HealthCheckRequest with the rules +// defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// HealthCheckRequestMultiError, or nil if none found. +func (m *HealthCheckRequest) ValidateAll() error { + return m.validate(true) +} + +func (m *HealthCheckRequest) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if len(errors) > 0 { + return HealthCheckRequestMultiError(errors) + } + + return nil +} + +// HealthCheckRequestMultiError is an error wrapping multiple validation errors +// returned by HealthCheckRequest.ValidateAll() if the designated constraints +// aren't met. +type HealthCheckRequestMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m HealthCheckRequestMultiError) Error() string { + msgs := make([]string, 0, len(m)) + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m HealthCheckRequestMultiError) AllErrors() []error { return m } + +// HealthCheckRequestValidationError is the validation error returned by +// HealthCheckRequest.Validate if the designated constraints aren't met. +type HealthCheckRequestValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e HealthCheckRequestValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e HealthCheckRequestValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e HealthCheckRequestValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e HealthCheckRequestValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e HealthCheckRequestValidationError) ErrorName() string { + return "HealthCheckRequestValidationError" +} + +// Error satisfies the builtin error interface +func (e HealthCheckRequestValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sHealthCheckRequest.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = HealthCheckRequestValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = HealthCheckRequestValidationError{} + +// Validate checks the field values on HealthCheckResponse with the rules +// defined in the proto definition for this message. If any rules are +// violated, the first error encountered is returned, or nil if there are no violations. +func (m *HealthCheckResponse) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on HealthCheckResponse with the rules +// defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// HealthCheckResponseMultiError, or nil if none found. +func (m *HealthCheckResponse) ValidateAll() error { + return m.validate(true) +} + +func (m *HealthCheckResponse) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if len(errors) > 0 { + return HealthCheckResponseMultiError(errors) + } + + return nil +} + +// HealthCheckResponseMultiError is an error wrapping multiple validation +// errors returned by HealthCheckResponse.ValidateAll() if the designated +// constraints aren't met. +type HealthCheckResponseMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m HealthCheckResponseMultiError) Error() string { + msgs := make([]string, 0, len(m)) + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m HealthCheckResponseMultiError) AllErrors() []error { return m } + +// HealthCheckResponseValidationError is the validation error returned by +// HealthCheckResponse.Validate if the designated constraints aren't met. +type HealthCheckResponseValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e HealthCheckResponseValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e HealthCheckResponseValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e HealthCheckResponseValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e HealthCheckResponseValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e HealthCheckResponseValidationError) ErrorName() string { + return "HealthCheckResponseValidationError" +} + +// Error satisfies the builtin error interface +func (e HealthCheckResponseValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sHealthCheckResponse.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = HealthCheckResponseValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = HealthCheckResponseValidationError{} diff --git a/api/qan/v1/service.proto b/api/qan/v1/service.proto index 5de0404d8c8..2e991f27c15 100644 --- a/api/qan/v1/service.proto +++ b/api/qan/v1/service.proto @@ -18,6 +18,12 @@ message GetMetricsNamesResponse { map data = 1; } +// HealthCheckRequest is empty. +message HealthCheckRequest {} + +// HealthCheckResponse is empty, based on returned error qan-api2 is ready or not. +message HealthCheckResponse {} + // QANService provides an API to interact with PMM Query Analytics. service QANService { // GetReport returns a list of metrics grouped by queryid or other dimensions. @@ -138,4 +144,13 @@ service QANService { description: "Provides a list of query examples." }; } + + // HealthCheck returns readiness of QAN API2 service. + rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse) { + option (google.api.http) = {get: "/v1/qan/health"}; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + summary: "Health Check" + description: "Returns readiness of QAN API2 service." + }; + } } diff --git a/api/qan/v1/service_grpc.pb.go b/api/qan/v1/service_grpc.pb.go index 75fc7b8118c..8aaa68b95eb 100644 --- a/api/qan/v1/service_grpc.pb.go +++ b/api/qan/v1/service_grpc.pb.go @@ -31,6 +31,7 @@ const ( QANService_QueryExists_FullMethodName = "/qan.v1.QANService/QueryExists" QANService_SchemaByQueryID_FullMethodName = "/qan.v1.QANService/SchemaByQueryID" QANService_GetQueryExample_FullMethodName = "/qan.v1.QANService/GetQueryExample" + QANService_HealthCheck_FullMethodName = "/qan.v1.QANService/HealthCheck" ) // QANServiceClient is the client API for QANService service. @@ -61,6 +62,8 @@ type QANServiceClient interface { SchemaByQueryID(ctx context.Context, in *SchemaByQueryIDRequest, opts ...grpc.CallOption) (*SchemaByQueryIDResponse, error) // GetQueryExample returns a list of query examples. GetQueryExample(ctx context.Context, in *GetQueryExampleRequest, opts ...grpc.CallOption) (*GetQueryExampleResponse, error) + // HealthCheck returns readiness of QAN API2 service. + HealthCheck(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error) } type qANServiceClient struct { @@ -181,6 +184,16 @@ func (c *qANServiceClient) GetQueryExample(ctx context.Context, in *GetQueryExam return out, nil } +func (c *qANServiceClient) HealthCheck(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(HealthCheckResponse) + err := c.cc.Invoke(ctx, QANService_HealthCheck_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // QANServiceServer is the server API for QANService service. // All implementations must embed UnimplementedQANServiceServer // for forward compatibility. @@ -209,6 +222,8 @@ type QANServiceServer interface { SchemaByQueryID(context.Context, *SchemaByQueryIDRequest) (*SchemaByQueryIDResponse, error) // GetQueryExample returns a list of query examples. GetQueryExample(context.Context, *GetQueryExampleRequest) (*GetQueryExampleResponse, error) + // HealthCheck returns readiness of QAN API2 service. + HealthCheck(context.Context, *HealthCheckRequest) (*HealthCheckResponse, error) mustEmbedUnimplementedQANServiceServer() } @@ -262,6 +277,10 @@ func (UnimplementedQANServiceServer) SchemaByQueryID(context.Context, *SchemaByQ func (UnimplementedQANServiceServer) GetQueryExample(context.Context, *GetQueryExampleRequest) (*GetQueryExampleResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetQueryExample not implemented") } + +func (UnimplementedQANServiceServer) HealthCheck(context.Context, *HealthCheckRequest) (*HealthCheckResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method HealthCheck not implemented") +} func (UnimplementedQANServiceServer) mustEmbedUnimplementedQANServiceServer() {} func (UnimplementedQANServiceServer) testEmbeddedByValue() {} @@ -481,6 +500,24 @@ func _QANService_GetQueryExample_Handler(srv interface{}, ctx context.Context, d return interceptor(ctx, in, info, handler) } +func _QANService_HealthCheck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HealthCheckRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QANServiceServer).HealthCheck(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: QANService_HealthCheck_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QANServiceServer).HealthCheck(ctx, req.(*HealthCheckRequest)) + } + return interceptor(ctx, in, info, handler) +} + // QANService_ServiceDesc is the grpc.ServiceDesc for QANService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -532,6 +569,10 @@ var QANService_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetQueryExample", Handler: _QANService_GetQueryExample_Handler, }, + { + MethodName: "HealthCheck", + Handler: _QANService_HealthCheck_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "qan/v1/service.proto", diff --git a/api/swagger/swagger-dev.json b/api/swagger/swagger-dev.json index 5151a8cec36..f69d37633f5 100644 --- a/api/swagger/swagger-dev.json +++ b/api/swagger/swagger-dev.json @@ -27042,6 +27042,56 @@ } } }, + "/v1/qan/health": { + "get": { + "description": "Returns readiness of QAN API2 service.", + "tags": [ + "QANService" + ], + "summary": "Health Check", + "operationId": "HealthCheck", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "description": "HealthCheckResponse is empty, based on returned error qan-api2 is ready or not.", + "type": "object" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32", + "x-order": 0 + }, + "message": { + "type": "string", + "x-order": 1 + }, + "details": { + "type": "array", + "items": { + "type": "object", + "properties": { + "@type": { + "type": "string", + "x-order": 0 + } + }, + "additionalProperties": {} + }, + "x-order": 2 + } + } + } + } + } + } + }, "/v1/qan/metrics:getFilters": { "post": { "description": "Provides a filtered map of metrics names.", diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index 5d4d24e5f33..bc07f17f829 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -26084,6 +26084,56 @@ } } }, + "/v1/qan/health": { + "get": { + "description": "Returns readiness of QAN API2 service.", + "tags": [ + "QANService" + ], + "summary": "Health Check", + "operationId": "HealthCheck", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "description": "HealthCheckResponse is empty, based on returned error qan-api2 is ready or not.", + "type": "object" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32", + "x-order": 0 + }, + "message": { + "type": "string", + "x-order": 1 + }, + "details": { + "type": "array", + "items": { + "type": "object", + "properties": { + "@type": { + "type": "string", + "x-order": 0 + } + }, + "additionalProperties": {} + }, + "x-order": 2 + } + } + } + } + } + } + }, "/v1/qan/metrics:getFilters": { "post": { "description": "Provides a filtered map of metrics names.", diff --git a/build/docker/server/Dockerfile.el9 b/build/docker/server/Dockerfile.el9 index 1eb0c4cd647..9128ce0e2f0 100644 --- a/build/docker/server/Dockerfile.el9 +++ b/build/docker/server/Dockerfile.el9 @@ -45,6 +45,6 @@ VOLUME [ "/srv" ] EXPOSE 8080 8443 -HEALTHCHECK --interval=3s --timeout=2s --start-period=10s --retries=3 CMD curl -sf http://127.0.0.1:8080/v1/server/readyz +HEALTHCHECK --interval=4s --timeout=2s --start-period=10s --retries=3 CMD curl -sf http://127.0.0.1:8080/v1/server/readyz CMD ["/opt/entrypoint.sh"] diff --git a/managed/cmd/pmm-managed/main.go b/managed/cmd/pmm-managed/main.go index 714ed0d0a8a..0105367cdc8 100644 --- a/managed/cmd/pmm-managed/main.go +++ b/managed/cmd/pmm-managed/main.go @@ -1010,6 +1010,7 @@ func main() { //nolint:maintidx,cyclop Dus: dus, HAService: haService, Nomad: nomad, + QANClient: qanClient, } server, err := server.NewServer(serverParams) diff --git a/managed/services/qan/client.go b/managed/services/qan/client.go index 5c268f4c1f1..1e6d5b88cfa 100644 --- a/managed/services/qan/client.go +++ b/managed/services/qan/client.go @@ -24,7 +24,6 @@ import ( "time" "github.com/AlekSi/pointer" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "google.golang.org/grpc" "gopkg.in/reform.v1" @@ -128,6 +127,16 @@ func (c *Client) QueryExists(ctx context.Context, serviceID, query string) error return nil } +// IsReady verifies that qan-api2 works. +func (c *Client) IsReady(ctx context.Context) error { + _, err := c.qsc.HealthCheck(ctx, nil) + if err != nil { + return err + } + + return nil +} + // ExplainFingerprintByQueryID get query for given query ID. // This avoid receiving custom queries. func (c *Client) ExplainFingerprintByQueryID(ctx context.Context, serviceID, queryID string) (*qanv1.ExplainFingerprintByQueryIDResponse, error) { @@ -311,7 +320,7 @@ func (c *Client) Collect(ctx context.Context, metricsBuckets []*agentv1.MetricsB c.l.Debugf("%+v", qanReq) res, err := c.c.Collect(ctx, qanReq) if err != nil { - return errors.Wrap(err, "failed to send CollectRequest to QAN") + return fmt.Errorf("failed to send CollectRequest to QAN: %w", err) } c.l.Debugf("%+v", res) diff --git a/managed/services/server/server.go b/managed/services/server/server.go index e25e9867a6d..218f135fbd0 100644 --- a/managed/services/server/server.go +++ b/managed/services/server/server.go @@ -61,6 +61,7 @@ type Server struct { supervisord supervisordService telemetryService telemetryService grafanaClient grafanaClient + qanClient healthChecker haService haService updater *Updater nomad nomadService @@ -94,6 +95,7 @@ type Params struct { Supervisord supervisordService TelemetryService telemetryService GrafanaClient grafanaClient + QANClient healthChecker Updater *Updater Dus *distribution.Service HAService haService @@ -119,6 +121,7 @@ func NewServer(params *Params) (*Server, error) { supervisord: params.Supervisord, telemetryService: params.TelemetryService, grafanaClient: params.GrafanaClient, + qanClient: params.QANClient, updater: params.Updater, haService: params.HAService, nomad: params.Nomad, @@ -220,6 +223,7 @@ func (s *Server) Readiness(ctx context.Context, req *serverv1.ReadinessRequest) "grafana": s.grafanaClient, "victoriametrics": s.vmdb, "vmalert": s.vmalert, + "qan": s.qanClient, } { if err := svc.IsReady(ctx); err != nil { s.l.Errorf("%s readiness check failed: %+v", n, err) diff --git a/qan-api2/main.go b/qan-api2/main.go index c258729c3e9..eaf78a94e38 100644 --- a/qan-api2/main.go +++ b/qan-api2/main.go @@ -94,7 +94,7 @@ func runGRPCServer(ctx context.Context, db *sqlx.DB, mbm *models.MetricsBucket, grpc_validator.StreamServerInterceptor())), ) - aserv := aservice.NewService(rm, mm) + aserv := aservice.NewService(db, rm, mm) qanv1.RegisterCollectorServiceServer(grpcServer, rservice.NewService(mbm)) qanv1.RegisterQANServiceServer(grpcServer, aserv) reflection.Register(grpcServer) diff --git a/qan-api2/services/analytics/base.go b/qan-api2/services/analytics/base.go index c1b72d75eb2..b6d2ba77884 100644 --- a/qan-api2/services/analytics/base.go +++ b/qan-api2/services/analytics/base.go @@ -16,12 +16,18 @@ package analytics import ( + "context" + "fmt" + + "github.com/jmoiron/sqlx" + qanpb "github.com/percona/pmm/api/qan/v1" "github.com/percona/pmm/qan-api2/models" ) // Service implements gRPC service to communicate with QAN-APP. type Service struct { + db *sqlx.DB rm models.Reporter mm models.Metrics @@ -29,8 +35,21 @@ type Service struct { } // NewService create new insstance of Service. -func NewService(rm models.Reporter, mm models.Metrics) *Service { - return &Service{rm: rm, mm: mm} +func NewService(db *sqlx.DB, rm models.Reporter, mm models.Metrics) *Service { + return &Service{db: db, rm: rm, mm: mm} +} + +// HealthCheck implements gRPC health check endpoint. +func (s *Service) HealthCheck(ctx context.Context, _ *qanpb.HealthCheckRequest) (*qanpb.HealthCheckResponse, error) { + // Use DB ping as readiness check + if s.db == nil { + return nil, fmt.Errorf("DB not initialized") + } + if err := s.db.PingContext(ctx); err != nil { + return nil, err + } + + return &qanpb.HealthCheckResponse{}, nil } var standartDimensions = map[string]struct{}{