Skip to content

Commit

Permalink
fix: use check statuses in the service layer
Browse files Browse the repository at this point in the history
  • Loading branch information
shipperizer committed Aug 23, 2023
1 parent 4b3fe14 commit 1d89f0f
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 136 deletions.
53 changes: 9 additions & 44 deletions pkg/status/handlers.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package status

import (
"context"
"encoding/json"
"net/http"
"sync"
"time"

"github.com/canonical/identity-platform-login-ui/internal/logging"
"github.com/canonical/identity-platform-login-ui/internal/monitoring"
Expand All @@ -20,9 +17,9 @@ type Status struct {
BuildInfo *BuildInfo `json:"buildInfo"`
}

type DeepCheckStatus struct {
KratosStatus bool `json:"kratos_status"`
HydraStatus bool `json:"hydra_status"`
type Health struct {
Kratos bool `json:"kratos"`
Hydra bool `json:"hydra"`
}

type API struct {
Expand All @@ -37,7 +34,7 @@ type API struct {
func (a *API) RegisterEndpoints(mux *chi.Mux) {
mux.Get("/api/v0/status", a.alive)
mux.Get("/api/v0/version", a.version)
mux.Get("/api/v0/deepcheck", a.deepCheck)
mux.Get("/api/v0/health", a.health)
}

func (a *API) alive(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -73,47 +70,15 @@ func (a *API) version(w http.ResponseWriter, r *http.Request) {

}

func (a *API) deepCheck(w http.ResponseWriter, r *http.Request) {
func (a *API) health(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")

wg := sync.WaitGroup{}
wg.Add(2)

kratosOK := false
hydraOK := false

ctx, cancel := context.WithTimeout(
r.Context(),
time.Duration(5*time.Second))
defer cancel()

go func(ctx context.Context) {
res, err := a.service.CheckKratosReady(ctx)
if err != nil {
a.logger.Errorf("error when checking kratos status: %s", err)
}
kratosOK = res
wg.Done()
}(ctx)

go func(ctx context.Context) {
res, err := a.service.CheckHydraReady(ctx)
if err != nil {
a.logger.Errorf("error when checking hydra status: %s", err)
}
hydraOK = res
wg.Done()
}(ctx)

wg.Wait()

ds := DeepCheckStatus{
KratosStatus: kratosOK,
HydraStatus: hydraOK,
}
health := new(Health)
health.Hydra = a.service.HydraStatus(r.Context())
health.Kratos = a.service.KratosStatus(r.Context())

w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(ds)
json.NewEncoder(w).Encode(health)
}

func NewAPI(service ServiceInterface, tracer trace.Tracer, monitor monitoring.MonitorInterface, logger logging.LoggerInterface) *API {
Expand Down
40 changes: 19 additions & 21 deletions pkg/status/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,21 @@ func TestAliveOK(t *testing.T) {
}
}

func TestDeepCheckSuccess(t *testing.T) {
func TestHealthSuccess(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

mockLogger := NewMockLoggerInterface(ctrl)
mockMonitor := NewMockMonitorInterface(ctrl)

mockTracer := NewMockTracer(ctrl)
mockService := NewMockServiceInterface(ctrl)

req := httptest.NewRequest(http.MethodGet, "/api/v0/deepcheck", nil)
req := httptest.NewRequest(http.MethodGet, "/api/v0/health", nil)
w := httptest.NewRecorder()

mockService.EXPECT().CheckKratosReady(gomock.Any()).Times(1).Return(true, nil)
mockService.EXPECT().CheckHydraReady(gomock.Any()).Times(1).Return(true, nil)
mockService.EXPECT().KratosStatus(gomock.Any()).Times(1).Return(true)
mockService.EXPECT().HydraStatus(gomock.Any()).Times(1).Return(true)

mux := chi.NewMux()
NewAPI(mockService, mockTracer, mockMonitor, mockLogger).RegisterEndpoints(mux)
Expand All @@ -77,19 +78,19 @@ func TestDeepCheckSuccess(t *testing.T) {
if err != nil {
t.Fatalf("expected error to be nil got %v", err)
}
receivedStatus := new(DeepCheckStatus)
receivedStatus := new(Health)
if err := json.Unmarshal(data, receivedStatus); err != nil {
t.Fatalf("expected error to be nil got %v", err)
}
if !receivedStatus.KratosStatus {
t.Fatalf("expected KratosStatus to be %v not %v", true, receivedStatus.KratosStatus)
if !receivedStatus.Kratos {
t.Fatalf("expected Kratos to be true not %v", receivedStatus.Kratos)
}
if !receivedStatus.HydraStatus {
t.Fatalf("expected HydraStatus to be %v not %v", true, receivedStatus.HydraStatus)
if !receivedStatus.Hydra {
t.Fatalf("expected Hydra to be true not %v", receivedStatus.Hydra)
}
}

func TestDeepCheckFailure(t *testing.T) {
func TestHealthFailure(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

Expand All @@ -98,13 +99,11 @@ func TestDeepCheckFailure(t *testing.T) {
mockTracer := NewMockTracer(ctrl)
mockService := NewMockServiceInterface(ctrl)

req := httptest.NewRequest(http.MethodGet, "/api/v0/deepcheck", nil)
req := httptest.NewRequest(http.MethodGet, "/api/v0/health", nil)
w := httptest.NewRecorder()

mockService.EXPECT().CheckKratosReady(gomock.Any()).Times(1).Return(false, nil)
mockService.EXPECT().CheckHydraReady(gomock.Any()).Times(1).Return(false, nil)
mockLogger.EXPECT().Errorf(gomock.Any(), gomock.Any()).AnyTimes()

mockService.EXPECT().KratosStatus(gomock.Any()).Times(1).Return(false)
mockService.EXPECT().HydraStatus(gomock.Any()).Times(1).Return(false)
mux := chi.NewMux()
NewAPI(mockService, mockTracer, mockMonitor, mockLogger).RegisterEndpoints(mux)

Expand All @@ -115,15 +114,14 @@ func TestDeepCheckFailure(t *testing.T) {
if err != nil {
t.Fatalf("expected error to be nil got %v", err)
}
receivedStatus := new(DeepCheckStatus)
receivedStatus := new(Health)
if err := json.Unmarshal(data, receivedStatus); err != nil {
t.Fatalf("expected error to be nil got %v", err)
}

if receivedStatus.KratosStatus {
t.Fatalf("expected KratosStatus to be %v not %v", false, receivedStatus.KratosStatus)
if receivedStatus.Kratos {
t.Fatalf("expected Kratos to be false not %v", receivedStatus.Kratos)
}
if receivedStatus.HydraStatus {
t.Fatalf("expected HydraStatus to be %v not %v", false, receivedStatus.HydraStatus)
if receivedStatus.Hydra {
t.Fatalf("expected Hydra to be false not %v", receivedStatus.Hydra)
}
}
4 changes: 2 additions & 2 deletions pkg/status/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ import (
)

type ServiceInterface interface {
CheckKratosReady(context.Context) (bool, error)
CheckHydraReady(context.Context) (bool, error)
KratosStatus(context.Context) bool
HydraStatus(context.Context) bool
}
40 changes: 33 additions & 7 deletions pkg/status/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package status
import (
"context"

"github.com/canonical/identity-platform-login-ui/internal/healthcheck"
"github.com/canonical/identity-platform-login-ui/internal/logging"
"github.com/canonical/identity-platform-login-ui/internal/monitoring"
"go.opentelemetry.io/otel/trace"
Expand All @@ -15,15 +16,33 @@ type Service struct {
kratos kClient.MetadataApi
hydra hClient.MetadataApi

hydraStatus healthcheck.CheckerInterface
kratosStatus healthcheck.CheckerInterface

tracer trace.Tracer
monitor monitoring.MonitorInterface
logger logging.LoggerInterface
}

func (s *Service) CheckKratosReady(ctx context.Context) (bool, error) {
ctx, span := s.tracer.Start(ctx, "status.Service.CheckKratosReady")
func (s *Service) KratosStatus(ctx context.Context) bool {
ctx, span := s.tracer.Start(ctx, "status.Service.KratosStatus")
defer span.End()

return s.kratosStatus.Status()
}

func (s *Service) HydraStatus(ctx context.Context) bool {
ctx, span := s.tracer.Start(ctx, "status.Service.HydraStatus")
defer span.End()

return s.hydraStatus.Status()
}

func (s *Service) kratosReady(ctx context.Context) (bool, error) {
ctx, span := s.tracer.Start(ctx, "status.Service.kratosReady")
defer span.End()

// IsReady only checks the status of specific instance called, not the cluster status
ok, _, err := s.kratos.IsReady(ctx).Execute()

var available float64
Expand All @@ -39,8 +58,8 @@ func (s *Service) CheckKratosReady(ctx context.Context) (bool, error) {
return ok != nil, err
}

func (s *Service) CheckHydraReady(ctx context.Context) (bool, error) {
ctx, span := s.tracer.Start(ctx, "status.Service.CheckHydraReady")
func (s *Service) hydraReady(ctx context.Context) (bool, error) {
ctx, span := s.tracer.Start(ctx, "status.Service.hydraReady")
defer span.End()

// IsReady only checks the status of specific instance called, not the cluster status
Expand All @@ -59,15 +78,22 @@ func (s *Service) CheckHydraReady(ctx context.Context) (bool, error) {
return ok != nil, err
}

func NewService(kmeta kClient.MetadataApi, hmeta hClient.MetadataApi, tracer trace.Tracer, monitor monitoring.MonitorInterface, logger logging.LoggerInterface) *Service {
func NewService(kratos kClient.MetadataApi, hydra hClient.MetadataApi, tracer trace.Tracer, monitor monitoring.MonitorInterface, logger logging.LoggerInterface) *Service {
s := new(Service)

s.kratos = kmeta
s.hydra = hmeta
s.kratos = kratos
s.hydra = hydra

s.hydraStatus = healthcheck.NewChecker(s.hydraReady, tracer, logger)
s.kratosStatus = healthcheck.NewChecker(s.kratosReady, tracer, logger)

s.monitor = monitor
s.tracer = tracer
s.logger = logger

// TOOO @shipperizer hook up the Stop methods for each checker
s.hydraStatus.Start()
s.kratosStatus.Start()

return s
}
Loading

0 comments on commit 1d89f0f

Please sign in to comment.