diff --git a/disperser/dataapi/docs/docs.go b/disperser/dataapi/docs/docs.go index 183caaf0ef..ebc77488f5 100644 --- a/disperser/dataapi/docs/docs.go +++ b/disperser/dataapi/docs/docs.go @@ -15,43 +15,6 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { - "/eigenda/service-availability": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "ServiceAvailability" - ], - "summary": "Get status of EigenDA services.", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dataapi.ServiceAvailabilityResponse" - } - }, - "400": { - "description": "error: Bad request", - "schema": { - "$ref": "#/definitions/dataapi.ErrorResponse" - } - }, - "404": { - "description": "error: Not found", - "schema": { - "$ref": "#/definitions/dataapi.ErrorResponse" - } - }, - "500": { - "description": "error: Server error", - "schema": { - "$ref": "#/definitions/dataapi.ErrorResponse" - } - } - } - } - }, "/feed/blobs": { "get": { "produces": [ @@ -377,7 +340,7 @@ const docTemplate = `{ }, { "type": "string", - "description": "End time (UTC) to query for operators nonsigning percentage", + "description": "End time (2006-01-02T15:04:05Z) to query for operators nonsigning percentage [default: now]", "name": "end", "in": "query" } diff --git a/disperser/dataapi/docs/swagger.json b/disperser/dataapi/docs/swagger.json index 845a305293..c1ec725a7a 100644 --- a/disperser/dataapi/docs/swagger.json +++ b/disperser/dataapi/docs/swagger.json @@ -11,43 +11,6 @@ "version": "1" }, "paths": { - "/eigenda/service-availability": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "ServiceAvailability" - ], - "summary": "Get status of EigenDA services.", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/dataapi.ServiceAvailabilityResponse" - } - }, - "400": { - "description": "error: Bad request", - "schema": { - "$ref": "#/definitions/dataapi.ErrorResponse" - } - }, - "404": { - "description": "error: Not found", - "schema": { - "$ref": "#/definitions/dataapi.ErrorResponse" - } - }, - "500": { - "description": "error: Server error", - "schema": { - "$ref": "#/definitions/dataapi.ErrorResponse" - } - } - } - } - }, "/feed/blobs": { "get": { "produces": [ @@ -373,7 +336,7 @@ }, { "type": "string", - "description": "End time (UTC) to query for operators nonsigning percentage", + "description": "End time (2006-01-02T15:04:05Z) to query for operators nonsigning percentage [default: now]", "name": "end", "in": "query" } diff --git a/disperser/dataapi/docs/swagger.yaml b/disperser/dataapi/docs/swagger.yaml index 1554903fc2..1681cd13c1 100644 --- a/disperser/dataapi/docs/swagger.yaml +++ b/disperser/dataapi/docs/swagger.yaml @@ -225,30 +225,6 @@ info: title: EigenDA Data Access API version: "1" paths: - /eigenda/service-availability: - get: - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/dataapi.ServiceAvailabilityResponse' - "400": - description: 'error: Bad request' - schema: - $ref: '#/definitions/dataapi.ErrorResponse' - "404": - description: 'error: Not found' - schema: - $ref: '#/definitions/dataapi.ErrorResponse' - "500": - description: 'error: Server error' - schema: - $ref: '#/definitions/dataapi.ErrorResponse' - summary: Get status of EigenDA services. - tags: - - ServiceAvailability /feed/blobs: get: parameters: @@ -456,7 +432,8 @@ paths: in: query name: interval type: integer - - description: End time (UTC) to query for operators nonsigning percentage + - description: 'End time (2006-01-02T15:04:05Z) to query for operators nonsigning + percentage [default: now]' in: query name: end type: string diff --git a/disperser/dataapi/server.go b/disperser/dataapi/server.go index 65f2c01eff..a81e6e6ed4 100644 --- a/disperser/dataapi/server.go +++ b/disperser/dataapi/server.go @@ -215,10 +215,6 @@ func (s *server) Start() error { { operatorsInfo.GET("/deregistered-operators", s.FetchDeregisteredOperators) } - serviceAvailability := v1.Group("/eigenda") - { - serviceAvailability.GET("/service-availability", s.GetEigenDAServiceAvailability) - } metrics := v1.Group("/metrics") { metrics.GET("/", s.FetchMetricsHandler) @@ -556,69 +552,6 @@ func (s *server) FetchDeregisteredOperators(c *gin.Context) { }) } -// GetEigenDAServiceAvailability godoc -// -// @Summary Get status of EigenDA services. -// @Tags ServiceAvailability -// @Produce json -// @Success 200 {object} ServiceAvailabilityResponse -// @Failure 400 {object} ErrorResponse "error: Bad request" -// @Failure 404 {object} ErrorResponse "error: Not found" -// @Failure 500 {object} ErrorResponse "error: Server error" -// @Router /eigenda/service-availability [get] -func (s *server) GetEigenDAServiceAvailability(c *gin.Context) { - timer := prometheus.NewTimer(prometheus.ObserverFunc(func(f float64) { - s.metrics.ObserveLatency("GetEigenDAServiceAvailability", f*1000) // make milliseconds - })) - defer timer.ObserveDuration() - - // Get query parameters to filter services - serviceName := c.DefaultQuery("service-name", "") // If not specified, default to return all services - - // If service name is not specified, return all services - services := []string{} - - if serviceName == "disperser" { - services = append(services, "Disperser") - } else if serviceName == "churner" { - services = append(services, "Churner") - } else if serviceName == "" { - services = append(services, "Disperser", "Churner") - } - s.logger.Info("Getting service availability for", "services", strings.Join(services, ", ")) - - availabilityStatuses, err := s.getServiceAvailability(c.Request.Context(), services) - if err != nil { - s.metrics.IncrementFailedRequestNum("GetEigenDAServiceAvailability") - errorResponse(c, err) - return - } - - s.metrics.IncrementSuccessfulRequestNum("GetEigenDAServiceAvailability") - - // Set the status code to 503 if any of the services are not serving - availabilityStatus := http.StatusOK - for _, status := range availabilityStatuses { - if status.ServiceStatus == "NOT_SERVING" { - availabilityStatus = http.StatusServiceUnavailable - break - } - - if status.ServiceStatus == "UNKNOWN" { - availabilityStatus = http.StatusInternalServerError - break - } - - } - - c.JSON(availabilityStatus, ServiceAvailabilityResponse{ - Meta: Meta{ - Size: len(availabilityStatuses), - }, - Data: availabilityStatuses, - }) -} - // FetchDisperserServiceAvailability godoc // // @Summary Get status of EigenDA Disperser service. diff --git a/disperser/dataapi/server_test.go b/disperser/dataapi/server_test.go index 86c144902e..ad9978b942 100644 --- a/disperser/dataapi/server_test.go +++ b/disperser/dataapi/server_test.go @@ -12,7 +12,6 @@ import ( "net" "net/http" "net/http/httptest" - "sync" "testing" "time" @@ -1327,290 +1326,6 @@ func TestFetchDeregisteredOperatorsMultipleOfflineSameBlock(t *testing.T) { mockSubgraphApi.Calls = nil } -func TestGetServiceAvailability(t *testing.T) { - r := setUpRouter() - - mockHealthCheckService := NewMockHealthCheckService() - mockHealthCheckService.AddResponse("Disperser", &grpc_health_v1.HealthCheckResponse{ - Status: grpc_health_v1.HealthCheckResponse_SERVING, - }) - // Assuming "Churner" service is also expected to be SERVING for this test - mockHealthCheckService.AddResponse("Churner", &grpc_health_v1.HealthCheckResponse{ - Status: grpc_health_v1.HealthCheckResponse_SERVING, - }) - - testDataApiServer = dataapi.NewServer(config, blobstore, prometheusClient, dataapi.NewSubgraphClient(mockSubgraphApi, mockLogger), mockTx, mockChainState, mockLogger, dataapi.NewMetrics(nil, "9001", mockLogger), &MockGRPCConnection{}, mockHealthCheckService, nil) - r.GET("/v1/eigenda/service-availability", testDataApiServer.GetEigenDAServiceAvailability) - - w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/v1/eigenda/service-availability", nil) - r.ServeHTTP(w, req) - - res := w.Result() - defer res.Body.Close() - - data, err := io.ReadAll(res.Body) - assert.NoError(t, err) - - var response dataapi.ServiceAvailabilityResponse - err = json.Unmarshal(data, &response) - assert.NoError(t, err) - assert.NotNil(t, response) - - assert.Equal(t, http.StatusOK, res.StatusCode) - assert.Equal(t, 2, response.Meta.Size) - assert.Equal(t, 2, len(response.Data)) - - service1Data := response.Data[0] - service2Data := response.Data[1] - - assert.Equal(t, "Disperser", service1Data.ServiceName) - assert.Equal(t, grpc_health_v1.HealthCheckResponse_SERVING.String(), service1Data.ServiceStatus) - - assert.Equal(t, "Churner", service2Data.ServiceName) - assert.Equal(t, grpc_health_v1.HealthCheckResponse_SERVING.String(), service2Data.ServiceStatus) -} - -func TestGetServiceAvailability_QueryDisperser(t *testing.T) { - r := setUpRouter() - - mockHealthCheckService := NewMockHealthCheckService() - mockHealthCheckService.AddResponse("Disperser", &grpc_health_v1.HealthCheckResponse{ - Status: grpc_health_v1.HealthCheckResponse_SERVING, - }) - mockHealthCheckService.AddResponse("Churner", &grpc_health_v1.HealthCheckResponse{ - Status: grpc_health_v1.HealthCheckResponse_SERVING, - }) - - testDataApiServer = dataapi.NewServer(config, blobstore, prometheusClient, dataapi.NewSubgraphClient(mockSubgraphApi, mockLogger), mockTx, mockChainState, mockLogger, dataapi.NewMetrics(nil, "9001", mockLogger), &MockGRPCConnection{}, mockHealthCheckService, nil) - - // Initialize the gRPC client pools - r.GET("/v1/eigenda/service-availability", testDataApiServer.GetEigenDAServiceAvailability) - - w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/v1/eigenda/service-availability?service-name=disperser", nil) - r.ServeHTTP(w, req) - - res := w.Result() - defer res.Body.Close() - - data, err := io.ReadAll(res.Body) - assert.NoError(t, err) - - var response dataapi.ServiceAvailabilityResponse - err = json.Unmarshal(data, &response) - assert.NoError(t, err) - assert.NotNil(t, response) - - fmt.Printf("Response: %v\n", response) - - assert.Equal(t, http.StatusOK, res.StatusCode) - assert.Equal(t, 1, response.Meta.Size) - assert.Equal(t, 1, len(response.Data)) - - serviceData := response.Data[0] - assert.Equal(t, "Disperser", serviceData.ServiceName) - assert.Equal(t, grpc_health_v1.HealthCheckResponse_SERVING.String(), serviceData.ServiceStatus) -} - -func TestGetServiceAvailability_QueryInvalidService(t *testing.T) { - r := setUpRouter() - - mockHealthCheckService := NewMockHealthCheckService() - mockHealthCheckService.AddResponse("Disperser", &grpc_health_v1.HealthCheckResponse{ - Status: grpc_health_v1.HealthCheckResponse_SERVING, - }) - // Assuming "Churner" service is also expected to be SERVING for this test - mockHealthCheckService.AddResponse("Churner", &grpc_health_v1.HealthCheckResponse{ - Status: grpc_health_v1.HealthCheckResponse_SERVING, - }) - testDataApiServer = dataapi.NewServer(config, blobstore, prometheusClient, dataapi.NewSubgraphClient(mockSubgraphApi, mockLogger), mockTx, mockChainState, mockLogger, dataapi.NewMetrics(nil, "9001", mockLogger), &MockGRPCConnection{}, mockHealthCheckService, nil) - - r.GET("/v1/eigenda/service-availability", testDataApiServer.GetEigenDAServiceAvailability) - - w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/v1/eigenda/service-availability?service-name=encoder", nil) - r.ServeHTTP(w, req) - - res := w.Result() - defer res.Body.Close() - - data, err := io.ReadAll(res.Body) - assert.NoError(t, err) - - var response dataapi.ServiceAvailabilityResponse - err = json.Unmarshal(data, &response) - assert.NoError(t, err) - assert.NotNil(t, response) - - assert.Equal(t, http.StatusOK, res.StatusCode) - assert.Equal(t, 0, response.Meta.Size) - assert.Equal(t, 0, len(response.Data)) -} - -func TestGetServiceAvailability_HealthCheckError(t *testing.T) { - r := setUpRouter() - - mockHealthCheckService := NewMockHealthCheckService() - mockHealthCheckService.AddResponse("Disperser", &grpc_health_v1.HealthCheckResponse{ - Status: grpc_health_v1.HealthCheckResponse_NOT_SERVING, - }) - // Assuming "Churner" service is also expected to be SERVING for this test - mockHealthCheckService.AddResponse("Churner", &grpc_health_v1.HealthCheckResponse{ - Status: grpc_health_v1.HealthCheckResponse_NOT_SERVING, - }) - testDataApiServer = dataapi.NewServer(config, blobstore, prometheusClient, dataapi.NewSubgraphClient(mockSubgraphApi, mockLogger), mockTx, mockChainState, mockLogger, dataapi.NewMetrics(nil, "9001", mockLogger), &MockGRPCConnection{}, mockHealthCheckService, nil) - - r.GET("/v1/eigenda/service-availability", testDataApiServer.GetEigenDAServiceAvailability) - - w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/v1/eigenda/service-availability?service-name=disperser", nil) - r.ServeHTTP(w, req) - - res := w.Result() - defer res.Body.Close() - - data, err := io.ReadAll(res.Body) - assert.NoError(t, err) - - var response dataapi.ServiceAvailabilityResponse - err = json.Unmarshal(data, &response) - assert.NoError(t, err) - assert.NotNil(t, response) - - assert.Equal(t, http.StatusServiceUnavailable, res.StatusCode) - assert.Equal(t, 1, response.Meta.Size) - assert.Equal(t, 1, len(response.Data)) - - serviceData := response.Data[0] - assert.Equal(t, "Disperser", serviceData.ServiceName) - assert.Equal(t, grpc_health_v1.HealthCheckResponse_NOT_SERVING.String(), serviceData.ServiceStatus) -} - -func TestGetServiceAvailability_HealthyUnHealthyService(t *testing.T) { - r := setUpRouter() - mockHealthCheckService := NewMockHealthCheckService() - mockHealthCheckService.AddResponse("Disperser", &grpc_health_v1.HealthCheckResponse{ - Status: grpc_health_v1.HealthCheckResponse_NOT_SERVING, - }) - // Assuming "Churner" service is also expected to be SERVING for this test - mockHealthCheckService.AddResponse("Churner", &grpc_health_v1.HealthCheckResponse{ - Status: grpc_health_v1.HealthCheckResponse_NOT_SERVING, - }) - testDataApiServer = dataapi.NewServer(config, blobstore, prometheusClient, dataapi.NewSubgraphClient(mockSubgraphApi, mockLogger), mockTx, mockChainState, mockLogger, dataapi.NewMetrics(nil, "9001", mockLogger), &MockGRPCConnection{}, mockHealthCheckService, nil) - - // Initialize the gRPC client pools - r.GET("/v1/eigenda/service-availability", testDataApiServer.GetEigenDAServiceAvailability) - - w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/v1/eigenda/service-availability", nil) - r.ServeHTTP(w, req) - - res := w.Result() - defer res.Body.Close() - - data, err := io.ReadAll(res.Body) - assert.NoError(t, err) - - var response dataapi.ServiceAvailabilityResponse - err = json.Unmarshal(data, &response) - assert.NoError(t, err) - assert.NotNil(t, response) - - assert.Equal(t, http.StatusServiceUnavailable, res.StatusCode) - assert.Equal(t, 2, response.Meta.Size) - assert.Equal(t, 2, len(response.Data)) - - service1Data := response.Data[0] - service2Data := response.Data[1] - - assert.Equal(t, "Disperser", service1Data.ServiceName) - assert.Equal(t, grpc_health_v1.HealthCheckResponse_NOT_SERVING.String(), service1Data.ServiceStatus) - - assert.Equal(t, "Churner", service2Data.ServiceName) - assert.Equal(t, grpc_health_v1.HealthCheckResponse_NOT_SERVING.String(), service2Data.ServiceStatus) -} - -func TestGetServiceAvailability_QueryDisperser_MultipleRequests(t *testing.T) { - r := setUpRouter() - - mockHealthCheckService := NewMockHealthCheckService() - mockHealthCheckService.AddResponse("Disperser", &grpc_health_v1.HealthCheckResponse{ - Status: grpc_health_v1.HealthCheckResponse_SERVING, - }) - // Assuming "Churner" service is also expected to be SERVING for this test - mockHealthCheckService.AddResponse("Churner", &grpc_health_v1.HealthCheckResponse{ - Status: grpc_health_v1.HealthCheckResponse_SERVING, - }) - testDataApiServer = dataapi.NewServer(config, blobstore, prometheusClient, dataapi.NewSubgraphClient(mockSubgraphApi, mockLogger), mockTx, mockChainState, mockLogger, dataapi.NewMetrics(nil, "9001", mockLogger), &MockGRPCConnection{}, mockHealthCheckService, nil) - - r.GET("/v1/eigenda/service-availability", testDataApiServer.GetEigenDAServiceAvailability) - - var concurrentRequests sync.WaitGroup - responses := make(chan *http.Response, 12) // Channel to collect responses - - for i := 0; i < 12; i++ { - concurrentRequests.Add(1) - go func() { - defer concurrentRequests.Done() - w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/v1/eigenda/service-availability?service-name=disperser", nil) - r.ServeHTTP(w, req) - responses <- w.Result() - }() - } - - concurrentRequests.Wait() // Wait for all requests to be processed - close(responses) // Close the channel after all goroutines complete - - // Process responses - for res := range responses { - processResponse(t, res) - } -} - -func TestGetServiceAvailability_HealthCheckerNilConnection(t *testing.T) { - r := setUpRouter() - - mockHealthCheckService := NewMockHealthCheckService() - mockHealthCheckService.AddResponse("Disperser", &grpc_health_v1.HealthCheckResponse{ - Status: grpc_health_v1.HealthCheckResponse_SERVING, - }) - mockHealthCheckService.AddResponse("Churner", &grpc_health_v1.HealthCheckResponse{ - Status: grpc_health_v1.HealthCheckResponse_SERVING, - }) - - testDataApiServer = dataapi.NewServer(config, blobstore, prometheusClient, dataapi.NewSubgraphClient(mockSubgraphApi, mockLogger), mockTx, mockChainState, mockLogger, dataapi.NewMetrics(nil, "9001", mockLogger), &MockGRPNilConnection{}, nil, nil) - - // Initialize the gRPC client pools - r.GET("/v1/eigenda/service-availability", testDataApiServer.GetEigenDAServiceAvailability) - - w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/v1/eigenda/service-availability?service-name=disperser", nil) - r.ServeHTTP(w, req) - - res := w.Result() - defer res.Body.Close() - - data, err := io.ReadAll(res.Body) - assert.NoError(t, err) - - var response dataapi.ServiceAvailabilityResponse - err = json.Unmarshal(data, &response) - assert.NoError(t, err) - assert.NotNil(t, response) - - fmt.Printf("Response: %v\n", response) - - assert.Equal(t, http.StatusInternalServerError, res.StatusCode) - assert.Equal(t, 1, response.Meta.Size) - assert.Equal(t, 1, len(response.Data)) - - serviceData := response.Data[0] - assert.Equal(t, "Disperser", serviceData.ServiceName) - assert.Equal(t, grpc_health_v1.HealthCheckResponse_UNKNOWN.String(), serviceData.ServiceStatus) -} - func setUpRouter() *gin.Engine { return gin.Default() } @@ -1720,25 +1435,3 @@ func getOperatorData(operatorMetadtas []*dataapi.DeregisteredOperatorMetadata, o return dataapi.DeregisteredOperatorMetadata{} } - -// processResponse processes a single http.Response and closes its body. -func processResponse(t *testing.T, res *http.Response) { - defer res.Body.Close() - data, err := io.ReadAll(res.Body) - assert.NoError(t, err) - - var response dataapi.ServiceAvailabilityResponse - err = json.Unmarshal(data, &response) - assert.NoError(t, err) - assert.NotNil(t, response) - - assert.Equal(t, http.StatusOK, res.StatusCode) - assert.GreaterOrEqual(t, response.Meta.Size, 1) - assert.GreaterOrEqual(t, len(response.Data), 1) - - if len(response.Data) > 0 { - serviceData := response.Data[0] - assert.Equal(t, "Disperser", serviceData.ServiceName) - assert.Equal(t, grpc_health_v1.HealthCheckResponse_SERVING.String(), serviceData.ServiceStatus) - } -}