213 lines
6.4 KiB
Go
213 lines
6.4 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
|
|
"go.infratographer.com/x/events"
|
|
"go.infratographer.com/x/gidx"
|
|
|
|
"go.equinixmetal.net/infra9-metal-bridge/internal/permissions"
|
|
)
|
|
|
|
// processRelationships determines the changes between what is wanted and what is live and executes on the differences.
|
|
// Relationship creations use events.
|
|
// Relationship deletions use the api, as delete events delete all related resources and not just the provided ones.
|
|
func (s *service) processRelationships(ctx context.Context, eventType string, relationships Relationships) int {
|
|
rlogger := s.logger.With("resource.id", relationships.Resource.PrefixedID())
|
|
|
|
wantParentRelationship, wantSubjectRelationships := s.mapRelationWants(relationships)
|
|
|
|
liveParentRelationships, liveSubjectRelationships, err := s.getRelationshipMap(ctx, relationships.Resource, relationships.SubjectType)
|
|
if err != nil {
|
|
rlogger.Errorw("failed to get relationship map",
|
|
"relationships.subject_type", relationships.SubjectType,
|
|
"error", err,
|
|
)
|
|
|
|
return 0
|
|
}
|
|
|
|
var (
|
|
createParentRelationship *Relation
|
|
deleteParentRelationships []gidx.PrefixedID
|
|
|
|
foundParent bool
|
|
|
|
createSubjectRelationships []Relation
|
|
deleteSubjectRelationships []Relation
|
|
)
|
|
|
|
if wantParentRelationship != nil {
|
|
for subjID := range liveParentRelationships {
|
|
if subjID == wantParentRelationship.Resource.PrefixedID() {
|
|
foundParent = true
|
|
|
|
continue
|
|
}
|
|
|
|
deleteParentRelationships = append(deleteParentRelationships, subjID)
|
|
}
|
|
|
|
if !foundParent {
|
|
createParentRelationship = wantParentRelationship
|
|
}
|
|
} else {
|
|
for subjID := range liveParentRelationships {
|
|
deleteParentRelationships = append(deleteParentRelationships, subjID)
|
|
}
|
|
}
|
|
|
|
for resID, relation := range wantSubjectRelationships {
|
|
if _, ok := liveSubjectRelationships[resID]; ok {
|
|
continue
|
|
}
|
|
|
|
createSubjectRelationships = append(createSubjectRelationships, Relation{
|
|
Resource: prefixedID{resID},
|
|
Relation: relation,
|
|
})
|
|
}
|
|
|
|
for resID, relation := range liveSubjectRelationships {
|
|
if _, ok := wantSubjectRelationships[resID]; ok {
|
|
continue
|
|
}
|
|
|
|
deleteSubjectRelationships = append(deleteSubjectRelationships, Relation{
|
|
Resource: prefixedID{resID},
|
|
Relation: relation,
|
|
})
|
|
}
|
|
|
|
var processEvents []events.ChangeMessage
|
|
|
|
rlogger.Debugw("processing relationships",
|
|
"parent.create", createParentRelationship != nil,
|
|
"parent.delete", len(deleteParentRelationships),
|
|
"subject.create", len(createSubjectRelationships),
|
|
"subject.delete", len(deleteSubjectRelationships),
|
|
)
|
|
|
|
if createParentRelationship != nil {
|
|
processEvents = append(processEvents, events.ChangeMessage{
|
|
SubjectID: relationships.Resource.PrefixedID(),
|
|
EventType: string(events.CreateChangeType),
|
|
AdditionalSubjectIDs: []gidx.PrefixedID{
|
|
createParentRelationship.Resource.PrefixedID(),
|
|
},
|
|
})
|
|
}
|
|
|
|
for _, relation := range createSubjectRelationships {
|
|
processEvents = append(processEvents, events.ChangeMessage{
|
|
SubjectID: relation.Resource.PrefixedID(),
|
|
EventType: string(events.CreateChangeType),
|
|
AdditionalSubjectIDs: []gidx.PrefixedID{
|
|
relationships.Resource.PrefixedID(),
|
|
},
|
|
})
|
|
}
|
|
|
|
for _, relatedResourceID := range deleteParentRelationships {
|
|
err = s.perms.DeleteResourceRelationship(ctx, relationships.Resource.PrefixedID(), string(RelateParent), relatedResourceID)
|
|
if err != nil {
|
|
rlogger.Errorw("error deleting parent relationship",
|
|
"parent.resource.id", relatedResourceID.String(),
|
|
)
|
|
}
|
|
}
|
|
|
|
for _, relation := range deleteSubjectRelationships {
|
|
err = s.perms.DeleteResourceRelationship(ctx, relation.Resource.PrefixedID(), string(relation.Relation), relationships.Resource.PrefixedID())
|
|
if err != nil {
|
|
rlogger.Errorw("error deleting relationship",
|
|
"relation", relation.Relation,
|
|
"subject.id", relation.Resource.PrefixedID().String(),
|
|
)
|
|
}
|
|
}
|
|
|
|
for _, event := range processEvents {
|
|
err = s.publisher.PublishChange(ctx, eventType, event)
|
|
|
|
if err != nil {
|
|
rlogger.Errorw("error publishing change",
|
|
"subject_type", eventType,
|
|
"subject.id", event.SubjectID,
|
|
"event.type", event.EventType,
|
|
"additional_subject_ids", event.AdditionalSubjectIDs,
|
|
"error", err,
|
|
)
|
|
}
|
|
}
|
|
|
|
rlogger.Debugw("relationships processed",
|
|
"parent.create", createParentRelationship != nil,
|
|
"parent.delete", len(deleteParentRelationships),
|
|
"subject.create", len(createSubjectRelationships),
|
|
"subject.delete", len(deleteSubjectRelationships),
|
|
)
|
|
|
|
changes := len(deleteParentRelationships) + len(createSubjectRelationships) + len(deleteSubjectRelationships)
|
|
|
|
if createParentRelationship != nil {
|
|
changes++
|
|
}
|
|
|
|
return changes
|
|
}
|
|
|
|
// mapRelationWants returns the parent relation if provided and a map of Subjects -> relation.
|
|
func (s *service) mapRelationWants(relationships Relationships) (*Relation, map[gidx.PrefixedID]RelationshipType) {
|
|
var wantParent *Relation
|
|
|
|
wantSubject := make(map[gidx.PrefixedID]RelationshipType)
|
|
|
|
if relationships.Parent.Resource != nil {
|
|
wantParent = &relationships.Parent
|
|
}
|
|
|
|
for _, relationship := range relationships.SubjectRelationships {
|
|
wantSubject[relationship.Resource.PrefixedID()] = relationship.Relation
|
|
}
|
|
|
|
return wantParent, wantSubject
|
|
}
|
|
|
|
// getRelationshipMap fetches the provided resources relationships, as the source resource and the destination subject.
|
|
// Returned are two maps, the first maps Subject IDs -> Relationship
|
|
// The second map, maps Resource IDs -> relationship
|
|
func (s *service) getRelationshipMap(ctx context.Context, resource IDPrefixableResource, relatedObjectType ObjectType) (map[gidx.PrefixedID]RelationshipType, map[gidx.PrefixedID]RelationshipType, error) {
|
|
liveResource, err := s.perms.ListResourceRelationships(ctx, resource.PrefixedID(), "")
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
var liveSubject []permissions.ResourceRelationship
|
|
|
|
if relatedObjectType != "" {
|
|
liveSubject, err = s.perms.ListResourceRelationships(ctx, resource.PrefixedID(), relatedObjectType.Prefix())
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
|
|
parents := make(map[gidx.PrefixedID]RelationshipType, len(liveResource))
|
|
|
|
for _, relationship := range liveResource {
|
|
if relationship.Relation != string(RelateParent) {
|
|
continue
|
|
}
|
|
|
|
parents[relationship.SubjectID] = RelationshipType(relationship.Relation)
|
|
}
|
|
|
|
subject := make(map[gidx.PrefixedID]RelationshipType, len(liveSubject))
|
|
|
|
for _, relationship := range liveSubject {
|
|
subject[relationship.ResourceID] = RelationshipType(relationship.Relation)
|
|
}
|
|
|
|
return parents, subject, nil
|
|
}
|