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  }