github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/runtime/resolver.go (about) 1 package runtime 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 8 "github.com/kyma-incubator/compass/components/director/internal/domain/scenarioassignment" 9 "github.com/kyma-incubator/compass/components/director/internal/domain/tenant" 10 11 "github.com/kyma-incubator/compass/components/director/internal/selfregmanager" 12 "github.com/kyma-incubator/compass/components/director/pkg/resource" 13 14 "github.com/google/uuid" 15 dataloader "github.com/kyma-incubator/compass/components/director/internal/dataloaders" 16 "github.com/kyma-incubator/compass/components/director/internal/domain/eventing" 17 labelPkg "github.com/kyma-incubator/compass/components/director/internal/domain/label" 18 "github.com/kyma-incubator/compass/components/director/internal/labelfilter" 19 "github.com/kyma-incubator/compass/components/director/internal/model" 20 "github.com/kyma-incubator/compass/components/director/internal/timestamp" 21 "github.com/kyma-incubator/compass/components/director/pkg/apperrors" 22 "github.com/kyma-incubator/compass/components/director/pkg/graphql" 23 "github.com/kyma-incubator/compass/components/director/pkg/inputvalidation" 24 "github.com/kyma-incubator/compass/components/director/pkg/log" 25 pkgmodel "github.com/kyma-incubator/compass/components/director/pkg/model" 26 "github.com/kyma-incubator/compass/components/director/pkg/persistence" 27 "github.com/kyma-incubator/compass/components/director/pkg/str" 28 "github.com/pkg/errors" 29 ) 30 31 // EventingService missing godoc 32 //go:generate mockery --name=EventingService --output=automock --outpkg=automock --case=underscore --disable-version-string 33 type EventingService interface { 34 GetForRuntime(ctx context.Context, runtimeID uuid.UUID) (*model.RuntimeEventingConfiguration, error) 35 } 36 37 // OAuth20Service missing godoc 38 //go:generate mockery --name=OAuth20Service --output=automock --outpkg=automock --case=underscore --disable-version-string 39 type OAuth20Service interface { 40 DeleteMultipleClientCredentials(ctx context.Context, auths []pkgmodel.SystemAuth) error 41 } 42 43 // RuntimeService missing godoc 44 //go:generate mockery --name=RuntimeService --output=automock --outpkg=automock --case=underscore --disable-version-string 45 type RuntimeService interface { 46 CreateWithMandatoryLabels(ctx context.Context, in model.RuntimeRegisterInput, id string, mandatoryLabels map[string]interface{}) error 47 Update(ctx context.Context, id string, in model.RuntimeUpdateInput) error 48 Get(ctx context.Context, id string) (*model.Runtime, error) 49 GetByTokenIssuer(ctx context.Context, issuer string) (*model.Runtime, error) 50 GetByFilters(ctx context.Context, filters []*labelfilter.LabelFilter) (*model.Runtime, error) 51 Delete(ctx context.Context, id string) error 52 List(ctx context.Context, filter []*labelfilter.LabelFilter, pageSize int, cursor string) (*model.RuntimePage, error) 53 SetLabel(ctx context.Context, label *model.LabelInput) error 54 GetLabel(ctx context.Context, runtimeID string, key string) (*model.Label, error) 55 ListLabels(ctx context.Context, runtimeID string) (map[string]*model.Label, error) 56 DeleteLabel(ctx context.Context, runtimeID string, key string) error 57 UnsafeExtractModifiableLabels(labels map[string]interface{}) (map[string]interface{}, error) 58 } 59 60 // ScenarioAssignmentService missing godoc 61 //go:generate mockery --name=ScenarioAssignmentService --output=automock --outpkg=automock --case=underscore --disable-version-string 62 type ScenarioAssignmentService interface { 63 GetForScenarioName(ctx context.Context, scenarioName string) (model.AutomaticScenarioAssignment, error) 64 } 65 66 //go:generate mockery --exported --name=formationService --output=automock --outpkg=automock --case=underscore --disable-version-string 67 type formationService interface { 68 MergeScenariosFromInputLabelsAndAssignments(ctx context.Context, inputLabels map[string]interface{}, runtimeID string) ([]interface{}, error) 69 AssignFormation(ctx context.Context, tnt, objectID string, objectType graphql.FormationObjectType, formation model.Formation) (*model.Formation, error) 70 UnassignFormation(ctx context.Context, tnt, objectID string, objectType graphql.FormationObjectType, formation model.Formation) (*model.Formation, error) 71 DeleteAutomaticScenarioAssignment(ctx context.Context, in model.AutomaticScenarioAssignment) error 72 } 73 74 // RuntimeConverter missing godoc 75 //go:generate mockery --name=RuntimeConverter --output=automock --outpkg=automock --case=underscore --disable-version-string 76 type RuntimeConverter interface { 77 ToGraphQL(in *model.Runtime) *graphql.Runtime 78 MultipleToGraphQL(in []*model.Runtime) []*graphql.Runtime 79 RegisterInputFromGraphQL(in graphql.RuntimeRegisterInput) (model.RuntimeRegisterInput, error) 80 UpdateInputFromGraphQL(in graphql.RuntimeUpdateInput) model.RuntimeUpdateInput 81 } 82 83 // SystemAuthConverter missing godoc 84 //go:generate mockery --name=SystemAuthConverter --output=automock --outpkg=automock --case=underscore --disable-version-string 85 type SystemAuthConverter interface { 86 ToGraphQL(in *pkgmodel.SystemAuth) (graphql.SystemAuth, error) 87 } 88 89 // SystemAuthService missing godoc 90 //go:generate mockery --name=SystemAuthService --output=automock --outpkg=automock --case=underscore --disable-version-string 91 type SystemAuthService interface { 92 ListForObject(ctx context.Context, objectType pkgmodel.SystemAuthReferenceObjectType, objectID string) ([]pkgmodel.SystemAuth, error) 93 } 94 95 // BundleInstanceAuthService missing godoc 96 //go:generate mockery --name=BundleInstanceAuthService --output=automock --outpkg=automock --case=underscore --disable-version-string 97 type BundleInstanceAuthService interface { 98 ListByRuntimeID(ctx context.Context, runtimeID string) ([]*model.BundleInstanceAuth, error) 99 Update(ctx context.Context, instanceAuth *model.BundleInstanceAuth) error 100 } 101 102 // SelfRegisterManager missing godoc 103 //go:generate mockery --name=SelfRegisterManager --output=automock --outpkg=automock --case=underscore --disable-version-string 104 type SelfRegisterManager interface { 105 PrepareForSelfRegistration(ctx context.Context, resourceType resource.Type, labels map[string]interface{}, id string, validate func() error) (map[string]interface{}, error) 106 CleanupSelfRegistration(ctx context.Context, selfRegisterLabelValue, region string) error 107 GetSelfRegDistinguishingLabelKey() string 108 } 109 110 // SubscriptionService is responsible for service layer operations for subscribing a tenant to a runtime 111 //go:generate mockery --name=SubscriptionService --output=automock --outpkg=automock --case=underscore --disable-version-string 112 type SubscriptionService interface { 113 SubscribeTenantToRuntime(ctx context.Context, providerID, subaccountTenantID, providerSubaccountID, consumerTenantID, region, subscriptionAppName string) (bool, error) 114 UnsubscribeTenantFromRuntime(ctx context.Context, providerID, subaccountTenantID, providerSubaccountID, consumerTenantID, region string) (bool, error) 115 } 116 117 // TenantFetcher calls an API which fetches details for the given tenant from an external tenancy service, stores the tenant in the Compass DB and returns 200 OK if the tenant was successfully created. 118 //go:generate mockery --name=TenantFetcher --output=automock --outpkg=automock --case=underscore --disable-version-string 119 type TenantFetcher interface { 120 FetchOnDemand(tenant, parentTenant string) error 121 } 122 123 // RuntimeContextService missing godoc 124 //go:generate mockery --name=RuntimeContextService --output=automock --outpkg=automock --case=underscore --disable-version-string 125 type RuntimeContextService interface { 126 GetForRuntime(ctx context.Context, id, runtimeID string) (*model.RuntimeContext, error) 127 ListByRuntimeIDs(ctx context.Context, runtimeIDs []string, pageSize int, cursor string) ([]*model.RuntimeContextPage, error) 128 ListAllForRuntime(ctx context.Context, runtimeID string) ([]*model.RuntimeContext, error) 129 Delete(ctx context.Context, id string) error 130 } 131 132 // RuntimeContextConverter missing godoc 133 //go:generate mockery --name=RuntimeContextConverter --output=automock --outpkg=automock --case=underscore --disable-version-string 134 type RuntimeContextConverter interface { 135 ToGraphQL(in *model.RuntimeContext) *graphql.RuntimeContext 136 MultipleToGraphQL(in []*model.RuntimeContext) []*graphql.RuntimeContext 137 } 138 139 // WebhookService missing godoc 140 //go:generate mockery --name=WebhookService --output=automock --outpkg=automock --case=underscore --disable-version-string 141 type WebhookService interface { 142 ListForRuntime(ctx context.Context, runtimeID string) ([]*model.Webhook, error) 143 Create(ctx context.Context, owningResourceID string, in model.WebhookInput, objectType model.WebhookReferenceObjectType) (string, error) 144 } 145 146 // WebhookConverter missing godoc 147 //go:generate mockery --name=WebhookConverter --output=automock --outpkg=automock --case=underscore --disable-version-string 148 type WebhookConverter interface { 149 MultipleToGraphQL(in []*model.Webhook) ([]*graphql.Webhook, error) 150 MultipleInputFromGraphQL(in []*graphql.WebhookInput) ([]*model.WebhookInput, error) 151 } 152 153 // Resolver missing godoc 154 type Resolver struct { 155 transact persistence.Transactioner 156 runtimeService RuntimeService 157 scenarioAssignmentService ScenarioAssignmentService 158 sysAuthSvc SystemAuthService 159 converter RuntimeConverter 160 sysAuthConv SystemAuthConverter 161 oAuth20Svc OAuth20Service 162 eventingSvc EventingService 163 bundleInstanceAuthSvc BundleInstanceAuthService 164 selfRegManager SelfRegisterManager 165 uidService uidService 166 subscriptionSvc SubscriptionService 167 runtimeContextService RuntimeContextService 168 runtimeContextConverter RuntimeContextConverter 169 webhookService WebhookService 170 webhookConverter WebhookConverter 171 fetcher TenantFetcher 172 formationSvc formationService 173 } 174 175 // NewResolver missing godoc 176 func NewResolver(transact persistence.Transactioner, runtimeService RuntimeService, scenarioAssignmentService ScenarioAssignmentService, 177 sysAuthSvc SystemAuthService, oAuthSvc OAuth20Service, conv RuntimeConverter, sysAuthConv SystemAuthConverter, 178 eventingSvc EventingService, bundleInstanceAuthSvc BundleInstanceAuthService, selfRegManager SelfRegisterManager, 179 uidService uidService, subscriptionSvc SubscriptionService, runtimeContextService RuntimeContextService, runtimeContextConverter RuntimeContextConverter, webhookService WebhookService, webhookConverter WebhookConverter, fetcher TenantFetcher, formationSvc formationService) *Resolver { 180 return &Resolver{ 181 transact: transact, 182 runtimeService: runtimeService, 183 scenarioAssignmentService: scenarioAssignmentService, 184 sysAuthSvc: sysAuthSvc, 185 oAuth20Svc: oAuthSvc, 186 converter: conv, 187 sysAuthConv: sysAuthConv, 188 eventingSvc: eventingSvc, 189 bundleInstanceAuthSvc: bundleInstanceAuthSvc, 190 selfRegManager: selfRegManager, 191 uidService: uidService, 192 subscriptionSvc: subscriptionSvc, 193 runtimeContextService: runtimeContextService, 194 runtimeContextConverter: runtimeContextConverter, 195 webhookService: webhookService, 196 webhookConverter: webhookConverter, 197 fetcher: fetcher, 198 formationSvc: formationSvc, 199 } 200 } 201 202 // Runtimes missing godoc 203 // TODO: Proper error handling 204 func (r *Resolver) Runtimes(ctx context.Context, filter []*graphql.LabelFilter, first *int, after *graphql.PageCursor) (*graphql.RuntimePage, error) { 205 labelFilter := labelfilter.MultipleFromGraphQL(filter) 206 var cursor string 207 if after != nil { 208 cursor = string(*after) 209 } 210 211 tx, err := r.transact.Begin() 212 if err != nil { 213 return nil, err 214 } 215 defer r.transact.RollbackUnlessCommitted(ctx, tx) 216 217 ctx = persistence.SaveToContext(ctx, tx) 218 219 if first == nil { 220 return nil, apperrors.NewInvalidDataError("missing required parameter 'first'") 221 } 222 223 runtimesPage, err := r.runtimeService.List(ctx, labelFilter, *first, cursor) 224 if err != nil { 225 return nil, err 226 } 227 228 if err = tx.Commit(); err != nil { 229 return nil, err 230 } 231 232 gqlRuntimes := r.converter.MultipleToGraphQL(runtimesPage.Data) 233 234 return &graphql.RuntimePage{ 235 Data: gqlRuntimes, 236 TotalCount: runtimesPage.TotalCount, 237 PageInfo: &graphql.PageInfo{ 238 StartCursor: graphql.PageCursor(runtimesPage.PageInfo.StartCursor), 239 EndCursor: graphql.PageCursor(runtimesPage.PageInfo.EndCursor), 240 HasNextPage: runtimesPage.PageInfo.HasNextPage, 241 }, 242 }, nil 243 } 244 245 // Runtime missing godoc 246 func (r *Resolver) Runtime(ctx context.Context, id string) (*graphql.Runtime, error) { 247 tx, err := r.transact.Begin() 248 if err != nil { 249 return nil, err 250 } 251 defer r.transact.RollbackUnlessCommitted(ctx, tx) 252 253 ctx = persistence.SaveToContext(ctx, tx) 254 255 runtime, err := r.runtimeService.Get(ctx, id) 256 if err != nil { 257 if apperrors.IsNotFoundError(err) { 258 return nil, tx.Commit() 259 } 260 return nil, err 261 } 262 263 if err = tx.Commit(); err != nil { 264 return nil, err 265 } 266 267 return r.converter.ToGraphQL(runtime), nil 268 } 269 270 // RuntimeByTokenIssuer returns a Runtime by a token issuer 271 func (r *Resolver) RuntimeByTokenIssuer(ctx context.Context, issuer string) (*graphql.Runtime, error) { 272 tx, err := r.transact.Begin() 273 if err != nil { 274 return nil, err 275 } 276 defer r.transact.RollbackUnlessCommitted(ctx, tx) 277 278 ctx = persistence.SaveToContext(ctx, tx) 279 280 runtime, err := r.runtimeService.GetByTokenIssuer(ctx, issuer) 281 if err != nil { 282 if apperrors.IsNotFoundError(err) { 283 return nil, tx.Commit() 284 } 285 return nil, err 286 } 287 288 if err = tx.Commit(); err != nil { 289 return nil, err 290 } 291 292 return r.converter.ToGraphQL(runtime), nil 293 } 294 295 // RegisterRuntime missing godoc 296 func (r *Resolver) RegisterRuntime(ctx context.Context, in graphql.RuntimeRegisterInput) (*graphql.Runtime, error) { 297 convertedIn, err := r.converter.RegisterInputFromGraphQL(in) 298 if err != nil { 299 return nil, err 300 } 301 302 if convertedIn.Labels, err = r.runtimeService.UnsafeExtractModifiableLabels(convertedIn.Labels); err != nil { 303 return nil, err 304 } 305 306 id := r.uidService.Generate() 307 308 validate := func() error { return nil } 309 310 selfRegLabels, err := r.selfRegManager.PrepareForSelfRegistration(ctx, resource.Runtime, convertedIn.Labels, id, validate) 311 if err != nil { 312 return nil, err 313 } 314 315 if saVal, ok := in.Labels[scenarioassignment.SubaccountIDKey]; ok { 316 sa, err := convertLabelValue(saVal) 317 if err != nil { 318 return nil, errors.Wrapf(err, "while converting %s label", scenarioassignment.SubaccountIDKey) 319 } 320 321 parentTenant, err := tenant.LoadFromContext(ctx) 322 if err != nil { 323 return nil, err 324 } 325 if err := r.fetcher.FetchOnDemand(sa, parentTenant); err != nil { 326 return nil, errors.Wrapf(err, "while trying to create if not exists subaccount %s", sa) 327 } 328 } 329 330 tx, err := r.transact.Begin() 331 if err != nil { 332 return nil, err 333 } 334 335 defer func() { 336 didRollback := r.transact.RollbackUnlessCommitted(ctx, tx) 337 if didRollback { 338 labelVal := str.CastOrEmpty(convertedIn.Labels[r.selfRegManager.GetSelfRegDistinguishingLabelKey()]) 339 if labelVal != "" { 340 label, ok := selfRegLabels[selfregmanager.RegionLabel].(string) 341 if !ok { 342 log.C(ctx).Errorf("An error occurred while casting region label value to string") 343 } else { 344 r.cleanupAndLogOnError(ctx, id, label) 345 } 346 } 347 } 348 }() 349 350 ctx = persistence.SaveToContext(ctx, tx) 351 352 if err = r.checkProviderRuntimeExistence(ctx, selfRegLabels); err != nil { 353 return nil, err 354 } 355 356 log.C(ctx).Debugf("Creating a Runtime with name %q and id %q", in.Name, id) 357 if err = r.runtimeService.CreateWithMandatoryLabels(ctx, convertedIn, id, selfRegLabels); err != nil { 358 return nil, err 359 } 360 361 runtime, err := r.runtimeService.Get(ctx, id) 362 if err != nil { 363 return nil, err 364 } 365 366 if err = tx.Commit(); err != nil { 367 return nil, err 368 } 369 370 return r.converter.ToGraphQL(runtime), nil 371 } 372 373 // UpdateRuntime missing godoc 374 func (r *Resolver) UpdateRuntime(ctx context.Context, id string, in graphql.RuntimeUpdateInput) (*graphql.Runtime, error) { 375 convertedIn := r.converter.UpdateInputFromGraphQL(in) 376 377 tx, err := r.transact.Begin() 378 if err != nil { 379 return nil, err 380 } 381 defer r.transact.RollbackUnlessCommitted(ctx, tx) 382 383 ctx = persistence.SaveToContext(ctx, tx) 384 385 log.C(ctx).Debugf("Updating a Runtime with id %q", id) 386 err = r.runtimeService.Update(ctx, id, convertedIn) 387 if err != nil { 388 return nil, err 389 } 390 391 runtime, err := r.runtimeService.Get(ctx, id) 392 if err != nil { 393 return nil, err 394 } 395 396 if err = tx.Commit(); err != nil { 397 return nil, err 398 } 399 400 return r.converter.ToGraphQL(runtime), nil 401 } 402 403 // DeleteRuntime missing godoc 404 func (r *Resolver) DeleteRuntime(ctx context.Context, id string) (*graphql.Runtime, error) { 405 tx, err := r.transact.Begin() 406 if err != nil { 407 return nil, err 408 } 409 defer func() { 410 r.transact.RollbackUnlessCommitted(ctx, tx) 411 }() 412 413 ctx = persistence.SaveToContext(ctx, tx) 414 415 runtime, err := r.runtimeService.Get(ctx, id) 416 if err != nil { 417 return nil, err 418 } 419 420 bundleInstanceAuths, err := r.bundleInstanceAuthSvc.ListByRuntimeID(ctx, runtime.ID) 421 if err != nil { 422 return nil, err 423 } 424 425 _, err = r.runtimeService.GetLabel(ctx, runtime.ID, r.selfRegManager.GetSelfRegDistinguishingLabelKey()) 426 if err != nil { 427 if !apperrors.IsNotFoundError(err) { 428 return nil, errors.Wrapf(err, "while getting self register info label") 429 } 430 } else { 431 regionLabel, err := r.runtimeService.GetLabel(ctx, runtime.ID, selfregmanager.RegionLabel) 432 if err != nil { 433 return nil, errors.Wrapf(err, "while getting region label") 434 } 435 436 // Committing transaction as the cleanup sends request to external service 437 if err = tx.Commit(); err != nil { 438 return nil, err 439 } 440 441 regionValue, ok := regionLabel.Value.(string) 442 if !ok { 443 return nil, errors.Wrap(err, "while casting region label value to string") 444 } 445 446 log.C(ctx).Infof("Executing clean-up for self-registered runtime with id %q", runtime.ID) 447 if err := r.selfRegManager.CleanupSelfRegistration(ctx, runtime.ID, regionValue); err != nil { 448 return nil, errors.Wrap(err, "An error occurred during cleanup of self-registered runtime: ") 449 } 450 451 tx, err = r.transact.Begin() 452 if err != nil { 453 return nil, err 454 } 455 ctx = persistence.SaveToContext(ctx, tx) 456 } 457 458 currentTimestamp := timestamp.DefaultGenerator 459 for _, auth := range bundleInstanceAuths { 460 if auth.Status.Condition != model.BundleInstanceAuthStatusConditionUnused { 461 if err := auth.SetDefaultStatus(model.BundleInstanceAuthStatusConditionUnused, currentTimestamp()); err != nil { 462 log.C(ctx).WithError(err).Errorf("while update bundle instance auth status condition: %v", err) 463 return nil, err 464 } 465 if err := r.bundleInstanceAuthSvc.Update(ctx, auth); err != nil { 466 log.C(ctx).WithError(err).Errorf("Unable to update bundle instance auth with ID: %s for corresponding bundle with ID: %s: %v", auth.ID, auth.BundleID, err) 467 return nil, err 468 } 469 } 470 } 471 472 auths, err := r.sysAuthSvc.ListForObject(ctx, pkgmodel.RuntimeReference, runtime.ID) 473 if err != nil { 474 return nil, err 475 } 476 477 if err = r.deleteAssociatedScenarioAssignments(ctx, runtime.ID); err != nil { 478 return nil, err 479 } 480 481 deletedRuntime := r.converter.ToGraphQL(runtime) 482 483 log.C(ctx).Debugf("Deleting a Runtime with id %q", id) 484 if err = r.runtimeService.Delete(ctx, id); err != nil { 485 return nil, err 486 } 487 488 if err = r.oAuth20Svc.DeleteMultipleClientCredentials(ctx, auths); err != nil { 489 return nil, err 490 } 491 492 if err = tx.Commit(); err != nil { 493 return nil, err 494 } 495 496 return deletedRuntime, nil 497 } 498 499 // GetLabel missing godoc 500 func (r *Resolver) GetLabel(ctx context.Context, runtimeID string, key string) (*graphql.Labels, error) { 501 if runtimeID == "" { 502 return nil, apperrors.NewInternalError("Runtime cannot be empty") 503 } 504 if key == "" { 505 return nil, apperrors.NewInternalError("Runtime label key cannot be empty") 506 } 507 508 tx, err := r.transact.Begin() 509 if err != nil { 510 return nil, err 511 } 512 defer r.transact.RollbackUnlessCommitted(ctx, tx) 513 514 ctx = persistence.SaveToContext(ctx, tx) 515 516 label, err := r.runtimeService.GetLabel(ctx, runtimeID, key) 517 if err != nil { 518 if apperrors.IsNotFoundError(err) { 519 return nil, tx.Commit() 520 } 521 return nil, err 522 } 523 524 if err = tx.Commit(); err != nil { 525 return nil, err 526 } 527 528 resultLabels := make(map[string]interface{}) 529 resultLabels[key] = label.Value 530 531 var gqlLabels graphql.Labels = resultLabels 532 return &gqlLabels, nil 533 } 534 535 // SetRuntimeLabel missing godoc 536 func (r *Resolver) SetRuntimeLabel(ctx context.Context, runtimeID string, key string, value interface{}) (*graphql.Label, error) { 537 // TODO: Use @validation directive on input type instead, after resolving https://github.com/kyma-incubator/compass/issues/515 538 gqlLabel := graphql.LabelInput{Key: key, Value: value} 539 if err := inputvalidation.Validate(&gqlLabel); err != nil { 540 return nil, errors.Wrap(err, "validation error for type LabelInput") 541 } 542 543 tx, err := r.transact.Begin() 544 if err != nil { 545 return nil, err 546 } 547 defer r.transact.RollbackUnlessCommitted(ctx, tx) 548 549 ctx = persistence.SaveToContext(ctx, tx) 550 551 err = r.runtimeService.SetLabel(ctx, &model.LabelInput{ 552 Key: key, 553 Value: value, 554 ObjectType: model.RuntimeLabelableObject, 555 ObjectID: runtimeID, 556 }) 557 if err != nil { 558 return nil, err 559 } 560 561 label, err := r.runtimeService.GetLabel(ctx, runtimeID, key) 562 if err != nil { 563 return nil, errors.Wrapf(err, "while getting label with key: [%s]", key) 564 } 565 566 if err = tx.Commit(); err != nil { 567 return nil, err 568 } 569 570 return &graphql.Label{ 571 Key: label.Key, 572 Value: label.Value, 573 }, nil 574 } 575 576 // DeleteRuntimeLabel missing godoc 577 func (r *Resolver) DeleteRuntimeLabel(ctx context.Context, runtimeID string, key string) (*graphql.Label, error) { 578 tx, err := r.transact.Begin() 579 if err != nil { 580 return nil, err 581 } 582 defer r.transact.RollbackUnlessCommitted(ctx, tx) 583 584 ctx = persistence.SaveToContext(ctx, tx) 585 586 label, err := r.runtimeService.GetLabel(ctx, runtimeID, key) 587 if err != nil { 588 return nil, err 589 } 590 591 if err = r.runtimeService.DeleteLabel(ctx, runtimeID, key); err != nil { 592 return nil, err 593 } 594 595 if err = tx.Commit(); err != nil { 596 return nil, err 597 } 598 599 return &graphql.Label{ 600 Key: key, 601 Value: label.Value, 602 }, nil 603 } 604 605 // Webhooks missing godoc 606 func (r *Resolver) Webhooks(ctx context.Context, obj *graphql.Runtime) ([]*graphql.Webhook, error) { 607 if obj == nil { 608 return nil, apperrors.NewInternalError("Runtime cannot be empty") 609 } 610 611 tx, err := r.transact.Begin() 612 if err != nil { 613 return nil, err 614 } 615 defer r.transact.RollbackUnlessCommitted(ctx, tx) 616 617 ctx = persistence.SaveToContext(ctx, tx) 618 619 webhooks, err := r.webhookService.ListForRuntime(ctx, obj.ID) 620 if err != nil { 621 return nil, err 622 } 623 624 if err = tx.Commit(); err != nil { 625 return nil, err 626 } 627 628 gqlWebhooks, err := r.webhookConverter.MultipleToGraphQL(webhooks) 629 if err != nil { 630 return nil, err 631 } 632 633 return gqlWebhooks, nil 634 } 635 636 // Labels missing godoc 637 func (r *Resolver) Labels(ctx context.Context, obj *graphql.Runtime, key *string) (graphql.Labels, error) { 638 if obj == nil { 639 return nil, apperrors.NewInternalError("Runtime cannot be empty") 640 } 641 642 tx, err := r.transact.Begin() 643 if err != nil { 644 return nil, err 645 } 646 defer r.transact.RollbackUnlessCommitted(ctx, tx) 647 648 ctx = persistence.SaveToContext(ctx, tx) 649 650 itemMap, err := r.runtimeService.ListLabels(ctx, obj.ID) 651 if err != nil { 652 if strings.Contains(err.Error(), "doesn't exist") { // TODO: Use custom error and check its type 653 return nil, tx.Commit() 654 } 655 return nil, err 656 } 657 658 err = tx.Commit() 659 if err != nil { 660 return nil, err 661 } 662 663 resultLabels := make(map[string]interface{}) 664 665 for _, label := range itemMap { 666 if key == nil || label.Key == *key { 667 resultLabels[label.Key] = label.Value 668 } 669 } 670 671 var gqlLabels graphql.Labels = resultLabels 672 return gqlLabels, nil 673 } 674 675 // RuntimeContexts retrieves a page of RuntimeContexts for the specified Runtime 676 func (r *Resolver) RuntimeContexts(ctx context.Context, obj *graphql.Runtime, first *int, after *graphql.PageCursor) (*graphql.RuntimeContextPage, error) { 677 param := dataloader.ParamRuntimeContext{ID: obj.ID, Ctx: ctx, First: first, After: after} 678 return dataloader.RuntimeContextFor(ctx).RuntimeContextByID.Load(param) 679 } 680 681 // RuntimeContextsDataLoader retrieves a page of RuntimeContexts for each Runtime ID in the keys argument 682 func (r *Resolver) RuntimeContextsDataLoader(keys []dataloader.ParamRuntimeContext) ([]*graphql.RuntimeContextPage, []error) { 683 if len(keys) == 0 { 684 return nil, []error{apperrors.NewInternalError("No Runtimes found")} 685 } 686 687 ctx := keys[0].Ctx 688 runtimeIDs := make([]string, 0, len(keys)) 689 for _, key := range keys { 690 runtimeIDs = append(runtimeIDs, key.ID) 691 } 692 693 var cursor string 694 if keys[0].After != nil { 695 cursor = string(*keys[0].After) 696 } 697 698 if keys[0].First == nil { 699 return nil, []error{apperrors.NewInvalidDataError("missing required parameter 'first'")} 700 } 701 702 tx, err := r.transact.Begin() 703 if err != nil { 704 return nil, []error{err} 705 } 706 defer r.transact.RollbackUnlessCommitted(ctx, tx) 707 708 ctx = persistence.SaveToContext(ctx, tx) 709 710 runtimeContextPages, err := r.runtimeContextService.ListByRuntimeIDs(ctx, runtimeIDs, *keys[0].First, cursor) 711 if err != nil { 712 return nil, []error{err} 713 } 714 715 gqlRtmCtxs := make([]*graphql.RuntimeContextPage, 0, len(runtimeContextPages)) 716 for _, page := range runtimeContextPages { 717 rtmCtxs := r.runtimeContextConverter.MultipleToGraphQL(page.Data) 718 719 gqlRtmCtxs = append(gqlRtmCtxs, &graphql.RuntimeContextPage{Data: rtmCtxs, TotalCount: page.TotalCount, PageInfo: &graphql.PageInfo{ 720 StartCursor: graphql.PageCursor(page.PageInfo.StartCursor), 721 EndCursor: graphql.PageCursor(page.PageInfo.EndCursor), 722 HasNextPage: page.PageInfo.HasNextPage, 723 }}) 724 } 725 726 err = tx.Commit() 727 if err != nil { 728 return nil, []error{err} 729 } 730 731 return gqlRtmCtxs, nil 732 } 733 734 // RuntimeContext missing godoc 735 func (r *Resolver) RuntimeContext(ctx context.Context, obj *graphql.Runtime, id string) (*graphql.RuntimeContext, error) { 736 if obj == nil { 737 return nil, apperrors.NewInternalError("Runtime cannot be empty") 738 } 739 740 tx, err := r.transact.Begin() 741 if err != nil { 742 return nil, err 743 } 744 defer r.transact.RollbackUnlessCommitted(ctx, tx) 745 746 ctx = persistence.SaveToContext(ctx, tx) 747 748 runtimeContext, err := r.runtimeContextService.GetForRuntime(ctx, id, obj.ID) 749 if err != nil { 750 if apperrors.IsNotFoundError(err) { 751 return nil, tx.Commit() 752 } 753 return nil, err 754 } 755 756 err = tx.Commit() 757 if err != nil { 758 return nil, err 759 } 760 761 return r.runtimeContextConverter.ToGraphQL(runtimeContext), nil 762 } 763 764 // Auths missing godoc 765 func (r *Resolver) Auths(ctx context.Context, obj *graphql.Runtime) ([]*graphql.RuntimeSystemAuth, error) { 766 if obj == nil { 767 return nil, apperrors.NewInternalError("Runtime cannot be empty") 768 } 769 770 tx, err := r.transact.Begin() 771 if err != nil { 772 return nil, err 773 } 774 defer r.transact.RollbackUnlessCommitted(ctx, tx) 775 776 ctx = persistence.SaveToContext(ctx, tx) 777 778 sysAuths, err := r.sysAuthSvc.ListForObject(ctx, pkgmodel.RuntimeReference, obj.ID) 779 if err != nil { 780 return nil, err 781 } 782 783 err = tx.Commit() 784 if err != nil { 785 return nil, err 786 } 787 788 out := make([]*graphql.RuntimeSystemAuth, 0, len(sysAuths)) 789 for _, sa := range sysAuths { 790 c, err := r.sysAuthConv.ToGraphQL(&sa) 791 if err != nil { 792 return nil, err 793 } 794 out = append(out, c.(*graphql.RuntimeSystemAuth)) 795 } 796 797 return out, nil 798 } 799 800 // EventingConfiguration missing godoc 801 func (r *Resolver) EventingConfiguration(ctx context.Context, obj *graphql.Runtime) (*graphql.RuntimeEventingConfiguration, error) { 802 if obj == nil { 803 return nil, apperrors.NewInternalError("Runtime cannot be empty") 804 } 805 806 runtimeID, err := uuid.Parse(obj.ID) 807 if err != nil { 808 return nil, errors.Wrap(err, "while parsing runtime ID as UUID") 809 } 810 811 tx, err := r.transact.Begin() 812 if err != nil { 813 return nil, errors.Wrap(err, "while opening the transaction") 814 } 815 defer r.transact.RollbackUnlessCommitted(ctx, tx) 816 817 ctx = persistence.SaveToContext(ctx, tx) 818 819 eventingCfg, err := r.eventingSvc.GetForRuntime(ctx, runtimeID) 820 if err != nil { 821 return nil, errors.Wrap(err, "while fetching eventing configuration for runtime") 822 } 823 824 if err = tx.Commit(); err != nil { 825 return nil, errors.Wrap(err, "while committing the transaction") 826 } 827 828 return eventing.RuntimeEventingConfigurationToGraphQL(eventingCfg), nil 829 } 830 831 // deleteAssociatedScenarioAssignments ensures that scenario assignments which are responsible for creation of certain runtime labels are deleted, 832 // if runtime doesn't have the scenarios label or is part of a scenario for which no scenario assignment exists => noop 833 func (r *Resolver) deleteAssociatedScenarioAssignments(ctx context.Context, runtimeID string) error { 834 scenariosLbl, err := r.runtimeService.GetLabel(ctx, runtimeID, model.ScenariosKey) 835 notFound := apperrors.IsNotFoundError(err) 836 if err != nil && !notFound { 837 return err 838 } 839 840 if notFound { 841 return nil 842 } 843 844 scenarios, err := labelPkg.ValueToStringsSlice(scenariosLbl.Value) 845 if err != nil { 846 return err 847 } 848 849 for _, scenario := range scenarios { 850 scenarioAssignment, err := r.scenarioAssignmentService.GetForScenarioName(ctx, scenario) 851 notFound := apperrors.IsNotFoundError(err) 852 if err != nil && !notFound { 853 return err 854 } 855 856 if notFound { 857 continue 858 } 859 860 if err := r.formationSvc.DeleteAutomaticScenarioAssignment(ctx, scenarioAssignment); err != nil { 861 return err 862 } 863 } 864 865 return nil 866 } 867 868 func (r *Resolver) checkProviderRuntimeExistence(ctx context.Context, labels map[string]interface{}) error { 869 distinguishLabelKey := r.selfRegManager.GetSelfRegDistinguishingLabelKey() 870 regionLabelKey := selfregmanager.RegionLabel 871 872 distinguishLabelValue, distinguishLabelExists := labels[distinguishLabelKey] 873 region, regionExists := labels[regionLabelKey] 874 875 if distinguishLabelExists && regionExists { 876 filters := []*labelfilter.LabelFilter{ 877 labelfilter.NewForKeyWithQuery(distinguishLabelKey, fmt.Sprintf("\"%s\"", distinguishLabelValue)), 878 labelfilter.NewForKeyWithQuery(regionLabelKey, fmt.Sprintf("\"%s\"", region)), 879 } 880 881 log.C(ctx).Infof("Getting runtime for labels %q: %q and %q: %q", regionLabelKey, region, distinguishLabelKey, distinguishLabelValue) 882 _, err := r.runtimeService.GetByFilters(ctx, filters) 883 if err != nil { 884 if apperrors.IsNotFoundError(err) { 885 return nil 886 } 887 return errors.Wrap(err, fmt.Sprintf("failed to get runtime for labels %q: %q and %q: %q", regionLabelKey, region, distinguishLabelKey, distinguishLabelValue)) 888 } 889 890 log.C(ctx).Errorf("Cannot have more than one runtime with labels %q: %q and %q: %q", regionLabelKey, region, distinguishLabelKey, distinguishLabelValue) 891 return errors.New(fmt.Sprintf("cannot have more than one runtime with labels %q: %q and %q: %q", regionLabelKey, region, distinguishLabelKey, distinguishLabelValue)) 892 } 893 return nil 894 } 895 896 func (r *Resolver) cleanupAndLogOnError(ctx context.Context, runtimeID, region string) { 897 if err := r.selfRegManager.CleanupSelfRegistration(ctx, runtimeID, region); err != nil { 898 log.C(ctx).Errorf("An error occurred during cleanup of self-registered runtime: %v", err) 899 } 900 }