github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/runtime_context/service.go (about) 1 package runtimectx 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/kyma-incubator/compass/components/director/pkg/log" 8 9 "github.com/kyma-incubator/compass/components/director/pkg/graphql" 10 11 "github.com/kyma-incubator/compass/components/director/internal/labelfilter" 12 "github.com/kyma-incubator/compass/components/director/internal/model" 13 "github.com/kyma-incubator/compass/components/director/pkg/apperrors" 14 15 "github.com/kyma-incubator/compass/components/director/internal/domain/tenant" 16 "github.com/pkg/errors" 17 ) 18 19 // RuntimeContextRepository missing godoc 20 //go:generate mockery --name=RuntimeContextRepository --output=automock --outpkg=automock --case=underscore --disable-version-string 21 type RuntimeContextRepository interface { 22 Exists(ctx context.Context, tenant, id string) (bool, error) 23 GetByID(ctx context.Context, tenant, id string) (*model.RuntimeContext, error) 24 GetForRuntime(ctx context.Context, tenant, id, runtimeID string) (*model.RuntimeContext, error) 25 List(ctx context.Context, runtimeID string, tenant string, filter []*labelfilter.LabelFilter, pageSize int, cursor string) (*model.RuntimeContextPage, error) 26 ListAllForRuntime(ctx context.Context, tenant, runtimeID string) ([]*model.RuntimeContext, error) 27 ListByRuntimeIDs(ctx context.Context, tenantID string, runtimeIDs []string, pageSize int, cursor string) ([]*model.RuntimeContextPage, error) 28 Create(ctx context.Context, tenant string, item *model.RuntimeContext) error 29 Update(ctx context.Context, tenant string, item *model.RuntimeContext) error 30 Delete(ctx context.Context, tenant, id string) error 31 } 32 33 // LabelRepository missing godoc 34 //go:generate mockery --name=LabelRepository --output=automock --outpkg=automock --case=underscore --disable-version-string 35 type LabelRepository interface { 36 ListForObject(ctx context.Context, tenant string, objectType model.LabelableObject, objectID string) (map[string]*model.Label, error) 37 } 38 39 // RuntimeRepository is responsible for the repo-layer Runtime operations. 40 //go:generate mockery --name=RuntimeRepository --output=automock --outpkg=automock --case=underscore --disable-version-string 41 type RuntimeRepository interface { 42 OwnerExists(ctx context.Context, tenant, id string) (bool, error) 43 } 44 45 // LabelUpsertService missing godoc 46 //go:generate mockery --name=LabelUpsertService --output=automock --outpkg=automock --case=underscore --disable-version-string 47 type LabelUpsertService interface { 48 UpsertMultipleLabels(ctx context.Context, tenant string, objectType model.LabelableObject, objectID string, labels map[string]interface{}) error 49 UpsertLabel(ctx context.Context, tenant string, labelInput *model.LabelInput) error 50 } 51 52 //go:generate mockery --exported --name=formationService --output=automock --outpkg=automock --case=underscore --disable-version-string 53 type formationService interface { 54 GetScenariosFromMatchingASAs(ctx context.Context, objectID string, objType graphql.FormationObjectType) ([]string, error) 55 GetFormationsForObject(ctx context.Context, tnt string, objType model.LabelableObject, objID string) ([]string, error) 56 AssignFormation(ctx context.Context, tnt, objectID string, objectType graphql.FormationObjectType, formation model.Formation) (*model.Formation, error) 57 UnassignFormation(ctx context.Context, tnt, objectID string, objectType graphql.FormationObjectType, formation model.Formation) (*model.Formation, error) 58 } 59 60 //go:generate mockery --exported --name=tenantService --output=automock --outpkg=automock --case=underscore --disable-version-string 61 type tenantService interface { 62 GetTenantByID(ctx context.Context, id string) (*model.BusinessTenantMapping, error) 63 } 64 65 // UIDService missing godoc 66 //go:generate mockery --name=UIDService --output=automock --outpkg=automock --case=underscore --disable-version-string 67 type UIDService interface { 68 Generate() string 69 } 70 71 type service struct { 72 repo RuntimeContextRepository 73 labelRepo LabelRepository 74 runtimeRepo RuntimeRepository 75 formationService formationService 76 tenantService tenantService 77 labelUpsertService LabelUpsertService 78 uidService UIDService 79 } 80 81 // NewService missing godoc 82 func NewService(repo RuntimeContextRepository, 83 labelRepo LabelRepository, 84 runtimeRepo RuntimeRepository, 85 labelUpsertService LabelUpsertService, 86 formationService formationService, 87 tenantService tenantService, 88 uidService UIDService) *service { 89 return &service{ 90 repo: repo, 91 labelRepo: labelRepo, 92 runtimeRepo: runtimeRepo, 93 formationService: formationService, 94 tenantService: tenantService, 95 labelUpsertService: labelUpsertService, 96 uidService: uidService, 97 } 98 } 99 100 // Exist checks if RuntimeContext with ID `id` exists 101 func (s *service) Exist(ctx context.Context, id string) (bool, error) { 102 rtmCtxTenant, err := tenant.LoadFromContext(ctx) 103 if err != nil { 104 return false, errors.Wrapf(err, "while loading tenant from context") 105 } 106 107 exist, err := s.repo.Exists(ctx, rtmCtxTenant, id) 108 if err != nil { 109 return false, errors.Wrapf(err, "while getting Runtime Context with ID %s", id) 110 } 111 112 return exist, nil 113 } 114 115 // Create creates RuntimeContext using `in`. Retrieves all formations from ASAs matching the RuntimeContext 116 // and assigns it to each formation 117 func (s *service) Create(ctx context.Context, in model.RuntimeContextInput) (string, error) { 118 log.C(ctx).Infof("Creating runtime context for runtime with ID: %q and key: %q and value: %q", in.RuntimeID, in.Key, in.Value) 119 rtmCtxTenant, err := tenant.LoadFromContext(ctx) 120 if err != nil { 121 return "", errors.Wrapf(err, "while loading tenant from context") 122 } 123 id := s.uidService.Generate() 124 rtmCtx := in.ToRuntimeContext(id) 125 126 if err = s.repo.Create(ctx, rtmCtxTenant, rtmCtx); err != nil { 127 return "", errors.Wrapf(err, "while creating Runtime Context") 128 } 129 130 tnt, err := s.tenantService.GetTenantByID(ctx, rtmCtxTenant) 131 if err != nil { 132 return "", errors.Wrapf(err, "while getting tenant with id %s", rtmCtxTenant) 133 } 134 135 if len(tnt.Parent) != 0 { 136 ctxWithParentTenant := tenant.SaveToContext(ctx, tnt.Parent, "") 137 scenariosFromAssignments, err := s.formationService.GetScenariosFromMatchingASAs(ctxWithParentTenant, id, graphql.FormationObjectTypeRuntimeContext) 138 if err != nil { 139 return "", errors.Wrapf(err, "while getting formations from automatic scenario assignments") 140 } 141 142 if len(scenariosFromAssignments) == 0 { 143 log.C(ctx).Infof("No scenarios found for runtime context with ID: %q that matched ASA", id) 144 return id, nil 145 } 146 147 // If we have a runtime with runtime context(s) we need to assign only the runtime context(s) to the formation. 148 // But if we create ASA in the provider account before registering runtime, the runtime will be assigned to the formation. 149 // And then if we register runtime context for this runtime, the runtime context will be assigned to the formation as well. 150 ownedRuntimeExists, err := s.runtimeRepo.OwnerExists(ctx, rtmCtxTenant, in.RuntimeID) 151 if err != nil { 152 return "", errors.Wrapf(err, "while checking if runtime with id %q exists", in.RuntimeID) 153 } 154 155 for _, scenario := range scenariosFromAssignments { 156 if _, err := s.formationService.AssignFormation(ctxWithParentTenant, tnt.Parent, id, graphql.FormationObjectTypeRuntimeContext, model.Formation{Name: scenario}); err != nil { 157 return "", errors.Wrapf(err, "while assigning formation with name %q for runtime context", scenario) 158 } 159 160 if ownedRuntimeExists { 161 if _, err := s.formationService.UnassignFormation(ctxWithParentTenant, tnt.Parent, in.RuntimeID, graphql.FormationObjectTypeRuntime, model.Formation{Name: scenario}); err != nil { 162 return "", errors.Wrapf(err, "while assigning formation with name %q for runtime context", scenario) 163 } 164 } 165 } 166 } 167 return id, nil 168 } 169 170 // Update updates RuntimeContext with ID `id` using `in` 171 func (s *service) Update(ctx context.Context, id string, in model.RuntimeContextInput) error { 172 rtmCtxTenant, err := tenant.LoadFromContext(ctx) 173 if err != nil { 174 return errors.Wrapf(err, "while loading tenant from context") 175 } 176 177 if _, err = s.repo.GetByID(ctx, rtmCtxTenant, id); err != nil { 178 return errors.Wrapf(err, "while getting Runtime Context with id %s", id) 179 } 180 181 rtmCtx := in.ToRuntimeContext(id) 182 183 if err = s.repo.Update(ctx, rtmCtxTenant, rtmCtx); err != nil { 184 return errors.Wrapf(err, "while updating Runtime Context with id %s", id) 185 } 186 187 return nil 188 } 189 190 // Delete unassigns the RuntimeContext from each formation that it is part of and then deletes it 191 func (s *service) Delete(ctx context.Context, id string) error { 192 rtmTenant, err := tenant.LoadFromContext(ctx) 193 if err != nil { 194 return errors.Wrapf(err, "while loading tenant from context") 195 } 196 197 formations, err := s.formationService.GetFormationsForObject(ctx, rtmTenant, model.RuntimeContextLabelableObject, id) 198 if err != nil && !apperrors.IsNotFoundError(err) { 199 return errors.Wrapf(err, "while listing formations for runtime context with id %q", id) 200 } 201 202 for _, f := range formations { 203 if _, err := s.formationService.UnassignFormation(ctx, rtmTenant, id, graphql.FormationObjectTypeRuntimeContext, model.Formation{Name: f}); err != nil { 204 return errors.Wrapf(err, "while unassigning formation with name %q for runtime context", f) 205 } 206 } 207 208 err = s.repo.Delete(ctx, rtmTenant, id) 209 if err != nil { 210 return errors.Wrapf(err, "while deleting Runtime Context with id %s", id) 211 } 212 213 return nil 214 } 215 216 // GetByID retrieves the RuntimeContext with the provided `id` 217 func (s *service) GetByID(ctx context.Context, id string) (*model.RuntimeContext, error) { 218 rtmCtxTenant, err := tenant.LoadFromContext(ctx) 219 if err != nil { 220 return nil, errors.Wrapf(err, "while loading tenant from context") 221 } 222 223 runtimeCtx, err := s.repo.GetByID(ctx, rtmCtxTenant, id) 224 if err != nil { 225 return nil, errors.Wrapf(err, "while getting Runtime Context with ID %s", id) 226 } 227 228 return runtimeCtx, nil 229 } 230 231 // GetForRuntime retrieves the RuntimeContext with the provided `id` associated with Runtime with id `runtimeID` 232 func (s *service) GetForRuntime(ctx context.Context, id, runtimeID string) (*model.RuntimeContext, error) { 233 rtmCtxTenant, err := tenant.LoadFromContext(ctx) 234 if err != nil { 235 return nil, errors.Wrapf(err, "while loading tenant from context") 236 } 237 238 runtimeCtx, err := s.repo.GetForRuntime(ctx, rtmCtxTenant, id, runtimeID) 239 if err != nil { 240 return nil, errors.Wrapf(err, "while getting Runtime Context with ID %s", id) 241 } 242 243 return runtimeCtx, nil 244 } 245 246 // ListAllForRuntime retrieves all RuntimeContexts for Runtime with id `runtimeID` 247 func (s *service) ListAllForRuntime(ctx context.Context, runtimeID string) ([]*model.RuntimeContext, error) { 248 rtmCtxTenant, err := tenant.LoadFromContext(ctx) 249 if err != nil { 250 return nil, errors.Wrapf(err, "while loading tenant from context") 251 } 252 253 runtimeCtxs, err := s.repo.ListAllForRuntime(ctx, rtmCtxTenant, runtimeID) 254 if err != nil { 255 return nil, errors.Wrapf(err, "while listing Runtime Contexts fot Runtime with ID %s", runtimeID) 256 } 257 258 return runtimeCtxs, nil 259 } 260 261 // ListByFilter retrieves a page of RuntimeContext objects associated to Runtime with id `runtimeID` that are matching the provided filters 262 func (s *service) ListByFilter(ctx context.Context, runtimeID string, filter []*labelfilter.LabelFilter, pageSize int, cursor string) (*model.RuntimeContextPage, error) { 263 rtmCtxTenant, err := tenant.LoadFromContext(ctx) 264 if err != nil { 265 return nil, errors.Wrapf(err, "while loading tenant from context") 266 } 267 268 if pageSize < 1 || pageSize > 200 { 269 return nil, apperrors.NewInvalidDataError("page size must be between 1 and 200") 270 } 271 272 return s.repo.List(ctx, runtimeID, rtmCtxTenant, filter, pageSize, cursor) 273 } 274 275 // ListByRuntimeIDs retrieves a page of RuntimeContext objects for each runtimeID 276 func (s *service) ListByRuntimeIDs(ctx context.Context, runtimeIDs []string, pageSize int, cursor string) ([]*model.RuntimeContextPage, error) { 277 tnt, err := tenant.LoadFromContext(ctx) 278 if err != nil { 279 return nil, errors.Wrapf(err, "while loading tenant from context") 280 } 281 282 if pageSize < 1 || pageSize > 200 { 283 return nil, apperrors.NewInvalidDataError("page size must be between 1 and 200") 284 } 285 286 return s.repo.ListByRuntimeIDs(ctx, tnt, runtimeIDs, pageSize, cursor) 287 } 288 289 // ListLabels lists Labels for RuntimeContext with ID `runtimeCtxID` 290 func (s *service) ListLabels(ctx context.Context, runtimeCtxID string) (map[string]*model.Label, error) { 291 rtmCtxTenant, err := tenant.LoadFromContext(ctx) 292 if err != nil { 293 return nil, errors.Wrapf(err, "while loading tenant from context") 294 } 295 296 rtmCtxExists, err := s.repo.Exists(ctx, rtmCtxTenant, runtimeCtxID) 297 if err != nil { 298 return nil, errors.Wrapf(err, "while checking Runtime Context existence with id %s", runtimeCtxID) 299 } 300 301 if !rtmCtxExists { 302 return nil, fmt.Errorf("runtime Context with ID %s doesn't exist", runtimeCtxID) 303 } 304 305 labels, err := s.labelRepo.ListForObject(ctx, rtmCtxTenant, model.RuntimeContextLabelableObject, runtimeCtxID) 306 if err != nil { 307 return nil, errors.Wrapf(err, "while getting label for Runtime Context with id %s", runtimeCtxID) 308 } 309 310 return labels, nil 311 }