github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/formation/status_service.go (about) 1 package formation 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 8 "github.com/kyma-incubator/compass/components/director/internal/domain/formationassignment" 9 "github.com/kyma-incubator/compass/components/director/internal/domain/labeldef" 10 "github.com/kyma-incubator/compass/components/director/internal/model" 11 "github.com/kyma-incubator/compass/components/director/pkg/formationconstraint" 12 "github.com/kyma-incubator/compass/components/director/pkg/log" 13 "github.com/pkg/errors" 14 ) 15 16 // formationStatusService service encapsulates all the specifics around persisting the state reported by notification receiver for a formation 17 type formationStatusService struct { 18 formationRepository FormationRepository 19 labelDefRepository labelDefRepository 20 labelDefService labelDefService 21 notificationsService NotificationsService 22 constraintEngine constraintEngine 23 } 24 25 // NewFormationStatusService creates formation status service 26 func NewFormationStatusService(formationRepository FormationRepository, labelDefRepository labelDefRepository, labelDefService labelDefService, notificationsService NotificationsService, constraintEngine constraintEngine) *formationStatusService { 27 return &formationStatusService{ 28 formationRepository: formationRepository, 29 labelDefRepository: labelDefRepository, 30 labelDefService: labelDefService, 31 notificationsService: notificationsService, 32 constraintEngine: constraintEngine, 33 } 34 } 35 36 // UpdateWithConstraints updates formation and enforces NotificationStatusReturned constraints before and after update. 37 func (s *formationStatusService) UpdateWithConstraints(ctx context.Context, formation *model.Formation, operation model.FormationOperation) error { 38 joinPointDetails, err := s.notificationsService.PrepareDetailsForNotificationStatusReturned(ctx, formation, operation) 39 if err != nil { 40 return errors.Wrap(err, "while preparing details for NotificationStatusReturned") 41 } 42 joinPointDetails.Location = formationconstraint.PreNotificationStatusReturned 43 if err := s.constraintEngine.EnforceConstraints(ctx, formationconstraint.PreNotificationStatusReturned, joinPointDetails, joinPointDetails.Formation.FormationTemplateID); err != nil { 44 return errors.Wrapf(err, "while enforcing constraints for target operation %q and constraint type %q", model.NotificationStatusReturned, model.PreOperation) 45 } 46 47 if err := s.formationRepository.Update(ctx, formation); err != nil { 48 log.C(ctx).Errorf("An error occurred while updating formation with ID: %q", formation.ID) 49 return errors.Wrapf(err, "An error occurred while updating formation with ID: %q", formation.ID) 50 } 51 52 joinPointDetails.Location = formationconstraint.PostNotificationStatusReturned 53 if err := s.constraintEngine.EnforceConstraints(ctx, formationconstraint.PostNotificationStatusReturned, joinPointDetails, joinPointDetails.Formation.FormationTemplateID); err != nil { 54 return errors.Wrapf(err, "while enforcing constraints for target operation %q and constraint type %q", model.NotificationStatusReturned, model.PostOperation) 55 } 56 57 return nil 58 } 59 60 // DeleteFormationEntityAndScenariosWithConstraints removes the formation name from scenarios label definitions and deletes the formation entity from the DB and enforces NotificationStatusReturned constraints before and after delete. 61 func (s *formationStatusService) DeleteFormationEntityAndScenariosWithConstraints(ctx context.Context, tnt string, formation *model.Formation) error { 62 joinPointDetails, err := s.notificationsService.PrepareDetailsForNotificationStatusReturned(ctx, formation, model.DeleteFormation) 63 if err != nil { 64 return errors.Wrap(err, "while preparing details for NotificationStatusReturned") 65 } 66 joinPointDetails.Location = formationconstraint.PreNotificationStatusReturned 67 if err := s.constraintEngine.EnforceConstraints(ctx, formationconstraint.PreNotificationStatusReturned, joinPointDetails, joinPointDetails.Formation.FormationTemplateID); err != nil { 68 return errors.Wrapf(err, "while enforcing constraints for target operation %q and constraint type %q", model.NotificationStatusReturned, model.PreOperation) 69 } 70 71 if err := s.deleteFormationFromLabelDef(ctx, tnt, formation.Name); err != nil { 72 return err 73 } 74 75 // TODO:: Currently we need to support both mechanisms of formation creation/deletion(through label definitions and Formations entity) for backwards compatibility 76 if err := s.formationRepository.DeleteByName(ctx, tnt, formation.Name); err != nil { 77 log.C(ctx).Errorf("An error occurred while deleting formation with name: %q", formation.Name) 78 return errors.Wrapf(err, "An error occurred while deleting formation with name: %q", formation.Name) 79 } 80 81 joinPointDetails.Location = formationconstraint.PostNotificationStatusReturned 82 if err := s.constraintEngine.EnforceConstraints(ctx, formationconstraint.PostNotificationStatusReturned, joinPointDetails, joinPointDetails.Formation.FormationTemplateID); err != nil { 83 return errors.Wrapf(err, "while enforcing constraints for target operation %q and constraint type %q", model.NotificationStatusReturned, model.PostOperation) 84 } 85 86 return nil 87 } 88 89 // SetFormationToErrorStateWithConstraints sets formation to error state and enforces NotificationStatusReturned constraints before and after update 90 func (s *formationStatusService) SetFormationToErrorStateWithConstraints(ctx context.Context, formation *model.Formation, errorMessage string, errorCode formationassignment.AssignmentErrorCode, state model.FormationState, operation model.FormationOperation) error { 91 log.C(ctx).Infof("Setting formation with ID: %q to state: %q", formation.ID, state) 92 formation.State = state 93 94 formationError := formationassignment.AssignmentError{ 95 Message: errorMessage, 96 ErrorCode: errorCode, 97 } 98 99 marshaledErr, err := json.Marshal(formationError) 100 if err != nil { 101 return errors.Wrapf(err, "While preparing error message for formation with ID: %q", formation.ID) 102 } 103 formation.Error = marshaledErr 104 105 if err := s.UpdateWithConstraints(ctx, formation, operation); err != nil { 106 return err 107 } 108 return nil 109 } 110 111 func (s *formationStatusService) deleteFormationFromLabelDef(ctx context.Context, tnt, formationName string) error { 112 def, err := s.labelDefRepository.GetByKey(ctx, tnt, model.ScenariosKey) 113 if err != nil { 114 return errors.Wrapf(err, "while getting `%s` label definition", model.ScenariosKey) 115 } 116 if def.Schema == nil { 117 return fmt.Errorf("missing schema for `%s` label definition", model.ScenariosKey) 118 } 119 120 formationNames, err := labeldef.ParseFormationsFromSchema(def.Schema) 121 if err != nil { 122 return err 123 } 124 125 schema, err := labeldef.NewSchemaForFormations(deleteFormation(formationNames, formationName)) 126 if err != nil { 127 return errors.Wrap(err, "while parsing scenarios") 128 } 129 130 if err = s.labelDefService.ValidateExistingLabelsAgainstSchema(ctx, schema, tnt, model.ScenariosKey); err != nil { 131 return err 132 } 133 if err = s.labelDefService.ValidateAutomaticScenarioAssignmentAgainstSchema(ctx, schema, tnt, model.ScenariosKey); err != nil { 134 return errors.Wrap(err, "while validating Scenario Assignments against a new schema") 135 } 136 137 return s.labelDefRepository.UpdateWithVersion(ctx, model.LabelDefinition{ 138 ID: def.ID, 139 Tenant: tnt, 140 Key: model.ScenariosKey, 141 Schema: &schema, 142 Version: def.Version, 143 }) 144 }