Skip to content

Commit

Permalink
RHCLOUD-34985 Relationships lifecycle
Browse files Browse the repository at this point in the history
  • Loading branch information
josejulio committed Oct 23, 2024
1 parent 81afada commit c6276ba
Show file tree
Hide file tree
Showing 16 changed files with 939 additions and 91 deletions.
4 changes: 2 additions & 2 deletions cmd/serve/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,8 @@ func NewCommand(
pb.RegisterKesselK8SPolicyServiceHTTPServer(server.HttpServer, k8spolicies_service)

// wire together relationships handling
relationships_repo := relationshipsrepo.New(db, eventingManager)
relationships_controller := relationshipsctl.New(relationships_repo, log.With(logger, "subsystem", "relationships_controller"))
relationships_repo := relationshipsrepo.New(db)
relationships_controller := relationshipsctl.New(relationships_repo, eventingManager, log.With(logger, "subsystem", "relationships_controller"))
relationships_service := relationshipssvc.New(relationships_controller)
rel.RegisterKesselK8SPolicyIsPropagatedToK8SClusterServiceServer(server.GrpcServer, relationships_service)
rel.RegisterKesselK8SPolicyIsPropagatedToK8SClusterServiceHTTPServer(server.HttpServer, relationships_service)
Expand Down
2 changes: 1 addition & 1 deletion data/k8spolicy_ispropagatedto_k8scluster.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"reporter_type": "OCM",
"reporter_version": "0.1",
"subject_local_resource_id": "1",
"object_local_resource_id": "2"
"object_local_resource_id": "1"
},
"relationship_data": {
"status": "NO_VIOLATIONS"
Expand Down
2 changes: 1 addition & 1 deletion data/relationship_reporter_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
"reporter_type": "OCM",
"reporter_version": "0.1",
"subject_local_resource_id": "1",
"object_local_resource_id": "2"
"object_local_resource_id": "1"
}
}
7 changes: 7 additions & 0 deletions data/reporter_data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"reporter_data": {
"reporter_type": "OCM",
"reporter_version": "0.1",
"local_resource_id": "1"
}
}
6 changes: 3 additions & 3 deletions internal/biz/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ func DefaultResourceSendEvent(ctx context.Context, model *model.Resource, evente
return nil
}

func DefaultRelationshipSendEvent(ctx context.Context, model *model.Relationship, eventer eventingapi.Manager, reportedTime time.Time, operationType eventingapi.OperationType) error {
func DefaultRelationshipSendEvent(ctx context.Context, m *model.Relationship, eventer eventingapi.Manager, reportedTime time.Time, operationType eventingapi.OperationType) error {
identity, err := middleware.GetIdentity(ctx)
if err != nil {
return err
}

producer, _ := eventer.Lookup(identity, model.RelationshipType, model.ID)
evt, err := eventingapi.NewRelationshipEvent(operationType, model, reportedTime)
producer, _ := eventer.Lookup(identity, m.RelationshipType, m.ID)
evt, err := eventingapi.NewRelationshipEvent(operationType, m, reportedTime)
if err != nil {
return err
}
Expand Down
32 changes: 32 additions & 0 deletions internal/biz/model/localinventorytoresource.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ type ReporterResourceId struct {
ReporterType string `gorm:"primarykey"`
}

type ReporterRelationshipId struct {
// Reporter identification
ReporterId string
ReporterType string

// Relationship data
RelationshipType string

ObjectId ReporterResourceId
SubjectId ReporterResourceId
}

func ReporterResourceIdFromResource(resource *Resource) ReporterResourceId {
return ReporterResourceId{
LocalResourceId: resource.Reporter.LocalResourceId,
Expand All @@ -28,3 +40,23 @@ func ReporterResourceIdFromResource(resource *Resource) ReporterResourceId {
ReporterType: resource.Reporter.ReporterType,
}
}

func ReporterRelationshipIdFromRelationship(relationship *Relationship) ReporterRelationshipId {
return ReporterRelationshipId{
ReporterId: relationship.Reporter.ReporterId,
ReporterType: relationship.Reporter.ReporterType,
RelationshipType: relationship.RelationshipType,
SubjectId: ReporterResourceId{
LocalResourceId: relationship.Reporter.SubjectLocalResourceId,
ResourceType: relationship.Reporter.SubjectResourceType,
ReporterId: relationship.Reporter.ReporterId,
ReporterType: relationship.Reporter.ReporterType,
},
ObjectId: ReporterResourceId{
LocalResourceId: relationship.Reporter.ObjectLocalResourceId,
ResourceType: relationship.Reporter.ObjectResourceType,
ReporterId: relationship.Reporter.ReporterId,
ReporterType: relationship.Reporter.ReporterType,
},
}
}
10 changes: 8 additions & 2 deletions internal/biz/model/relationships.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,23 @@ type Relationship struct {
OrgId string `gorm:"index"`
RelationshipData JsonObject
RelationshipType string
SubjectId uint64 `gorm:"index"`
ObjectId uint64 `gorm:"index"`
SubjectId uint64 `gorm:"index;not null"`
ObjectId uint64 `gorm:"index;not null"`
Reporter RelationshipReporter
CreatedAt *time.Time
UpdatedAt *time.Time

// Used to create FKs
Subject Resource `gorm:"foreignKey:SubjectId"`
Object Resource `gorm:"foreignKey:ObjectId"`
}

type RelationshipReporter struct {
Reporter
SubjectLocalResourceId string `json:"subject_local_resource_id"`
SubjectResourceType string `json:"subject_resource_type"`
ObjectLocalResourceId string `json:"object_local_resource_id"`
ObjectResourceType string `json:"object_resource_type"`
}

func (RelationshipReporter) GormDBDataType(db *gorm.DB, field *schema.Field) string {
Expand Down
125 changes: 103 additions & 22 deletions internal/biz/relationships/relationships.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,70 +2,151 @@ package resources

import (
"context"
"errors"
"github.com/go-kratos/kratos/v2/log"
"github.com/project-kessel/inventory-api/internal/biz"
"github.com/project-kessel/inventory-api/internal/biz/model"
eventingapi "github.com/project-kessel/inventory-api/internal/eventing/api"
"gorm.io/gorm"
"time"
)

type ResourceRepository interface {
Save(ctx context.Context, resource *model.Relationship) (*model.Relationship, error)
Update(context.Context, *model.Relationship, uint64) (*model.Relationship, error)
Delete(context.Context, uint64) error
Delete(context.Context, uint64) (*model.Relationship, error)
FindByID(context.Context, uint64) (*model.Relationship, error)
FindRelationship(ctx context.Context, subjectId, objectId uint64, relationshipType string) (*model.Relationship, error)
FindResourceIdByReporterResourceId(ctx context.Context, id model.ReporterResourceId) (uint64, error)
ListAll(context.Context) ([]*model.Relationship, error)
}

type Usecase struct {
repository ResourceRepository
eventer eventingapi.Manager
log *log.Helper
}

func New(repository ResourceRepository, logger log.Logger) *Usecase {
var (
ErrSubjectNotFound = errors.New("subject not found")
ErrObjectNotFound = errors.New("object not found")
ErrRelationshipExists = errors.New("relationship already exists")
ErrRelationshipNotFound = errors.New("relationship not found")
)

func New(repository ResourceRepository, eventer eventingapi.Manager, logger log.Logger) *Usecase {
return &Usecase{
repository: repository,
eventer: eventer,
log: log.NewHelper(logger),
}
}

func (uc *Usecase) Create(ctx context.Context, m *model.Relationship) (*model.Relationship, error) {
relationshipId := model.ReporterRelationshipIdFromRelationship(m)

subjectId, err := uc.repository.FindResourceIdByReporterResourceId(ctx, relationshipId.SubjectId)
if err != nil {
return nil, ErrSubjectNotFound
}

objectId, err := uc.repository.FindResourceIdByReporterResourceId(ctx, relationshipId.ObjectId)
if err != nil {
return nil, ErrObjectNotFound
}

// check if the relationship already exists
_, err = uc.repository.FindRelationship(ctx, subjectId, objectId, relationshipId.RelationshipType)

if err == nil || !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrRelationshipExists
}

m.SubjectId = subjectId
m.ObjectId = objectId

if ret, err := uc.repository.Save(ctx, m); err != nil {
return nil, err
} else {
if uc.eventer != nil {
err := biz.DefaultRelationshipSendEvent(ctx, m, uc.eventer, *m.CreatedAt, eventingapi.OperationTypeCreated)

if err != nil {
return nil, err
}
}

uc.log.WithContext(ctx).Infof("Created Relationship: %v(%v)", m.ID, m.RelationshipType)
return ret, nil
}
}

// Update updates a model in the database, updates related tuples in the relations-api, and issues an update event.
// The `id` is possibly of the form <reporter_type:local_resource_id>.
func (uc *Usecase) Update(ctx context.Context, m *model.Relationship, id string) (*model.Relationship, error) {
if ret, err := uc.repository.Update(ctx, m, 0); err != nil {
func (uc *Usecase) Update(ctx context.Context, m *model.Relationship, id model.ReporterRelationshipId) (*model.Relationship, error) {
subjectId, err := uc.repository.FindResourceIdByReporterResourceId(ctx, id.SubjectId)
if err != nil {
return nil, ErrSubjectNotFound
}

objectId, err := uc.repository.FindResourceIdByReporterResourceId(ctx, id.ObjectId)
if err != nil {
return nil, ErrObjectNotFound
}

// check if the relationship already exists
existingResource, err := uc.repository.FindRelationship(ctx, subjectId, objectId, id.RelationshipType)

if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
return uc.Create(ctx, m)
}

m.SubjectId = existingResource.SubjectId
m.ObjectId = existingResource.ObjectId

if ret, err := uc.repository.Update(ctx, m, existingResource.ID); err != nil {
return nil, err
} else {
if uc.eventer != nil {
err := biz.DefaultRelationshipSendEvent(ctx, m, uc.eventer, *m.UpdatedAt, eventingapi.OperationTypeUpdated)

if err != nil {
return nil, err
}
}

uc.log.WithContext(ctx).Infof("Updated Relationship: %v(%v)", m.ID, m.RelationshipType)
return ret, nil
}
}

// Delete deletes a model from the database, removes related tuples from the relations-api, and issues a delete event.
// The `id` is possibly of the form <reporter_type:local_resource_id>.
func (uc *Usecase) Delete(ctx context.Context, id string) error {
if err := uc.repository.Delete(ctx, 0); err != nil {
func (uc *Usecase) Delete(ctx context.Context, id model.ReporterRelationshipId) error {
subjectId, err := uc.repository.FindResourceIdByReporterResourceId(ctx, id.SubjectId)
if err != nil {
return ErrSubjectNotFound
}

objectId, err := uc.repository.FindResourceIdByReporterResourceId(ctx, id.ObjectId)
if err != nil {
return ErrObjectNotFound
}

// check if the relationship already exists
existingResource, err := uc.repository.FindRelationship(ctx, subjectId, objectId, id.RelationshipType)

if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
return ErrRelationshipNotFound
}

if m, err := uc.repository.Delete(ctx, existingResource.ID); err != nil {
return err
} else {
// TODO: Retrieve data from inventory so we have something to publish
m := &model.Relationship{
ID: 0,
OrgId: "",
RelationshipData: nil,
RelationshipType: "",
SubjectId: 0,
ObjectId: 0,
Reporter: model.RelationshipReporter{},
CreatedAt: nil,
UpdatedAt: nil,
if uc.eventer != nil {
err := biz.DefaultRelationshipSendEvent(ctx, m, uc.eventer, time.Now(), eventingapi.OperationTypeDeleted)

if err != nil {
return err
}
}

// TODO: delete the model from inventory
uc.log.WithContext(ctx).Infof("Deleted Relationship: %v(%v)", m.ID, m.RelationshipType)
return nil
}
Expand Down
Loading

0 comments on commit c6276ba

Please sign in to comment.