diff --git a/internal/biz/health.go b/internal/biz/health.go new file mode 100644 index 0000000..f802a5d --- /dev/null +++ b/internal/biz/health.go @@ -0,0 +1,13 @@ +package biz + +type IsBackendAvaliableUsecase struct { + repo ZanzibarRepository +} + +func NewIsBackendAvailableUsecase(repo ZanzibarRepository) *IsBackendAvaliableUsecase { + return &IsBackendAvaliableUsecase{repo: repo} +} + +func (rc *IsBackendAvaliableUsecase) IsBackendAvailable() error { + return rc.repo.IsBackendAvailable() +} diff --git a/internal/biz/relationships.go b/internal/biz/relationships.go index 9751781..684d8ef 100644 --- a/internal/biz/relationships.go +++ b/internal/biz/relationships.go @@ -33,7 +33,7 @@ type ZanzibarRepository interface { DeleteRelationships(context.Context, *v0.RelationTupleFilter) error LookupSubjects(ctx context.Context, subjectType *v0.ObjectType, subject_relation, relation string, resource *v0.ObjectReference, limit uint32, continuation ContinuationToken) (chan *SubjectResult, chan error, error) LookupResources(ctx context.Context, resouce_type *v0.ObjectType, relation string, subject *v0.SubjectReference, limit uint32, continuation ContinuationToken) (chan *ResourceResult, chan error, error) - IsBackendAvaliable() error + IsBackendAvailable() error } type CheckUsecase struct { @@ -49,18 +49,6 @@ func (rc *CheckUsecase) Check(ctx context.Context, check *v0.CheckRequest) (*v0. return rc.repo.Check(ctx, check) } -type IsBackendAvaliableUsecase struct { - repo ZanzibarRepository -} - -func NewIsBackendAvailableUsecase(repo ZanzibarRepository) *IsBackendAvaliableUsecase { - return &IsBackendAvaliableUsecase{repo: repo} -} - -func (rc *IsBackendAvaliableUsecase) IsBackendAvailable() error { - return rc.repo.IsBackendAvaliable() -} - type CreateRelationshipsUsecase struct { repo ZanzibarRepository log *log.Helper diff --git a/internal/data/spicedb.go b/internal/data/spicedb.go index 44bdff8..7af0bb5 100644 --- a/internal/data/spicedb.go +++ b/internal/data/spicedb.go @@ -13,10 +13,6 @@ import ( "github.com/project-kessel/relations-api/internal/biz" "github.com/project-kessel/relations-api/internal/conf" - apiV0 "github.com/project-kessel/relations-api/api/relations/v0" - "github.com/project-kessel/relations-api/internal/biz" - "github.com/project-kessel/relations-api/internal/conf" - v1 "github.com/authzed/authzed-go/proto/authzed/api/v1" "github.com/authzed/authzed-go/v1" "github.com/authzed/grpcutil" @@ -73,20 +69,6 @@ func NewSpiceDbRepository(c *conf.Data, logger log.Logger) (*SpiceDbRepository, return nil, nil, fmt.Errorf("error creating spicedb client: %w", err) } - // Create health client for readyz - conn, err := grpc.NewClient( - c.SpiceDb.Endpoint, - opts..., - ) - if err != nil { - return nil, nil, fmt.Errorf("error creating grpc health client: %w", err) - } - healthClient := grpc_health_v1.NewHealthClient(conn) - _, err = client.ReadSchema(context.TODO(), &v1.ReadSchemaRequest{}) - if err != nil { - return nil, nil, fmt.Errorf("error testing connection to SpiceDB: %w", err) - } - // Create health client for readyz conn, err := grpc.NewClient( c.SpiceDb.Endpoint, @@ -357,7 +339,7 @@ func (s *SpiceDbRepository) Check(ctx context.Context, check *apiV0.CheckRequest return &apiV0.CheckResponse{Allowed: apiV0.CheckResponse_ALLOWED_FALSE}, nil } -func (s *SpiceDbRepository) IsBackendAvaliable() error { +func (s *SpiceDbRepository) IsBackendAvailable() error { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() @@ -368,11 +350,11 @@ func (s *SpiceDbRepository) IsBackendAvaliable() error { switch resp.Status { case grpc_health_v1.HealthCheckResponse_NOT_SERVING, grpc_health_v1.HealthCheckResponse_SERVICE_UNKNOWN: - return err + return fmt.Errorf("error connecting to backend: %v", resp.Status.Descriptor()) case grpc_health_v1.HealthCheckResponse_SERVING: return nil } - return err + return fmt.Errorf("error connecting to backend") } func createSpiceDbRelationshipFilter(filter *apiV0.RelationTupleFilter) *v1.RelationshipFilter { diff --git a/internal/data/spicedb_test.go b/internal/data/spicedb_test.go index 4518214..55e9189 100644 --- a/internal/data/spicedb_test.go +++ b/internal/data/spicedb_test.go @@ -8,6 +8,7 @@ import ( apiV0 "github.com/project-kessel/relations-api/api/relations/v0" "github.com/project-kessel/relations-api/internal/biz" + "github.com/project-kessel/relations-api/internal/conf" "github.com/go-kratos/kratos/v2/log" "github.com/go-kratos/kratos/v2/middleware/tracing" @@ -124,10 +125,25 @@ func TestIsBackendAvailable(t *testing.T) { spiceDbrepo, err := container.CreateSpiceDbRepository() assert.NoError(t, err) - err = spiceDbrepo.IsBackendAvaliable() + err = spiceDbrepo.IsBackendAvailable() assert.NoError(t, err) } +func TestIsBackendUnavailable(t *testing.T) { + t.Parallel() + + spiceDBRepo, _, err := NewSpiceDbRepository(&conf.Data{ + SpiceDb: &conf.Data_SpiceDb{ + Endpoint: "-1", + Token: "foobar", + UseTLS: true, + }}, log.GetLogger()) + assert.NoError(t, err) + + err = spiceDBRepo.IsBackendAvailable() + assert.Error(t, err) +} + func TestCreateRelationshipFailsWithBadSubjectType(t *testing.T) { t.Parallel() diff --git a/internal/service/health_test.go b/internal/service/health_test.go index ca2ba41..e5e9100 100644 --- a/internal/service/health_test.go +++ b/internal/service/health_test.go @@ -2,6 +2,7 @@ package service import ( "context" + "fmt" "testing" pb "github.com/project-kessel/relations-api/api/health/v1" @@ -39,6 +40,31 @@ func TestHealthService_GetReadyz_SpiceDBAvailable(t *testing.T) { assert.Equal(t, resp, &pb.GetReadyzReply{Status: "OK", Code: 200}) } +func TestHealthService_GetReadyz_SpiceDBUnavailable(t *testing.T) { + t.Parallel() + + ctx := context.TODO() + + d := &DummyZanzibar{} + service := createDummyHealthService(d) + resp, err := service.GetReadyz(ctx, &pb.GetReadyzRequest{}) + + assert.NoError(t, err) + assert.Equal(t, resp, &pb.GetReadyzReply{Status: "Unavailable", Code: 503}) +} + +type DummyZanzibar struct { + biz.ZanzibarRepository +} + +func (dz *DummyZanzibar) IsBackendAvailable() error { + return fmt.Errorf("Unavailable") +} + +func createDummyHealthService(d *DummyZanzibar) *HealthService { + return NewHealthService(biz.NewIsBackendAvailableUsecase(d)) +} + func createHealthService(spicedb *data.SpiceDbRepository) *HealthService { return NewHealthService(biz.NewIsBackendAvailableUsecase(spicedb)) } diff --git a/openapi.yaml b/openapi.yaml index e660a4e..c88cc1f 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -363,10 +363,20 @@ components: description: A reference to a Subject or, if a `relation` is provided, a Subject Set. kessel.relations.v1.GetLivezReply: type: object - properties: {} + properties: + status: + type: string + code: + type: integer + format: uint32 kessel.relations.v1.GetReadyzReply: type: object - properties: {} + properties: + status: + type: string + code: + type: integer + format: uint32 tags: - name: KesselCheckService - name: KesselHealth