github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/nsadapter/handler/exposed_systems_handler.go (about) 1 package handler 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "net/http" 8 "strings" 9 10 "github.com/google/uuid" 11 12 "github.com/kyma-incubator/compass/components/director/internal/domain/tenant" 13 14 "github.com/kyma-incubator/compass/components/director/internal/labelfilter" 15 "github.com/kyma-incubator/compass/components/director/internal/model" 16 "github.com/kyma-incubator/compass/components/director/internal/nsadapter/httputil" 17 "github.com/kyma-incubator/compass/components/director/internal/nsadapter/nsmodel" 18 "github.com/kyma-incubator/compass/components/director/pkg/httputils" 19 "github.com/kyma-incubator/compass/components/director/pkg/log" 20 "github.com/kyma-incubator/compass/components/director/pkg/persistence" 21 "github.com/kyma-incubator/compass/components/director/pkg/str" 22 "github.com/pkg/errors" 23 ) 24 25 const ( 26 deltaReportType = "delta" 27 fullReportType = "full" 28 reportTypeQueryParam = "reportType" 29 notSubaccountMarker = "not subaccount" 30 ) 31 32 //go:generate mockery --exported --name=applicationService --output=automock --outpkg=automock --case=underscore --disable-version-string 33 type applicationService interface { 34 CreateFromTemplate(ctx context.Context, in model.ApplicationRegisterInput, appTemplateID *string) (string, error) 35 Upsert(ctx context.Context, in model.ApplicationRegisterInput) error 36 Update(ctx context.Context, id string, in model.ApplicationUpdateInput) error 37 GetSccSystem(ctx context.Context, sccSubaccount, locationID, virtualHost string) (*model.Application, error) 38 ListBySCC(ctx context.Context, filter *labelfilter.LabelFilter) ([]*model.ApplicationWithLabel, error) 39 SetLabel(ctx context.Context, label *model.LabelInput) error 40 GetLabel(ctx context.Context, applicationID string, key string) (*model.Label, error) 41 ListSCCs(ctx context.Context) ([]*model.SccMetadata, error) 42 } 43 44 //go:generate mockery --exported --name=applicationConverter --output=automock --outpkg=automock --case=underscore --disable-version-string 45 type applicationConverter interface { 46 CreateInputJSONToModel(ctx context.Context, in string) (model.ApplicationRegisterInput, error) 47 } 48 49 //go:generate mockery --exported --name=applicationTemplateService --output=automock --outpkg=automock --case=underscore --disable-version-string 50 type applicationTemplateService interface { 51 Get(ctx context.Context, id string) (*model.ApplicationTemplate, error) 52 PrepareApplicationCreateInputJSON(appTemplate *model.ApplicationTemplate, values model.ApplicationFromTemplateInputValues) (string, error) 53 } 54 55 //go:generate mockery --exported --name=tenantService --output=automock --outpkg=automock --case=underscore --disable-version-string 56 type tenantService interface { 57 ListsByExternalIDs(ctx context.Context, ids []string) ([]*model.BusinessTenantMapping, error) 58 } 59 60 // NewHandler returns new ns-adapter handler 61 func NewHandler(appSvc applicationService, appConverter applicationConverter, appTemplateSvc applicationTemplateService, tntSvc tenantService, transact persistence.Transactioner) *Handler { 62 return &Handler{appSvc: appSvc, appConverter: appConverter, appTemplateSvc: appTemplateSvc, tntSvc: tntSvc, transact: transact} 63 } 64 65 // Handler implements handler interface 66 type Handler struct { 67 appSvc applicationService 68 appConverter applicationConverter 69 appTemplateSvc applicationTemplateService 70 tntSvc tenantService 71 transact persistence.Transactioner 72 } 73 74 // Description - Bulk create-or-update operation on exposed on-premise systems. This handler supports two types of reports - full and delta. 75 // This handler takes a list of fully described SCCs together with the exposed systems. 76 // It creates new application for every exposed system for which CMP isn't aware of, and updates the metadata for the ones it is. 77 // - In case of full report: If there is SCC which was not reported, all exposed systems of this SCC are marked as unreachable. 78 // - In case of delta report: If there are missing exposed systems for a particular SCC, these systems are marked as unreachable. 79 // URL - /api/v1/notifications 80 // Query Params - reportType=[full, delta] 81 // HTTP Method - PUT 82 // Content-Type - application/json 83 // HTTP Codes: 84 // 204 No Content: 85 // - In case of delta report: if all systems are processed successfully 86 // - In case of full report: if the request was processed 87 // 200 OK: 88 // - In case of delta report: if update/create failed for some on-premise systems 89 // 400 Bad Request: 90 // - missing or invalid required report type query parameter 91 // - failed to parse request body 92 // - validating request body failed 93 // 500 Internal Server Error: 94 // - In case internal issue occurred. Example: db communication failed 95 func (a *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { 96 ctx := req.Context() 97 logger := log.C(ctx) 98 99 defer func() { 100 if err := req.Body.Close(); err != nil { 101 logger.Error("Got error on closing request body", err) 102 } 103 }() 104 105 reportHandlers := map[string]func(context.Context, []*nsmodel.SCC, []httputil.Detail, nsmodel.Report, http.ResponseWriter){ 106 fullReportType: a.fullReportHandler, 107 deltaReportType: a.deltaReportHandler, 108 } 109 110 reportType := req.URL.Query().Get(reportTypeQueryParam) 111 logger.Infof("New report of type %q received", reportType) 112 113 reportHandler, found := reportHandlers[reportType] 114 if !found { 115 httputil.RespondWithError(ctx, rw, http.StatusBadRequest, httputil.Error{ 116 Code: http.StatusBadRequest, 117 Message: "the query parameter 'reportType' is missing or invalid", 118 }) 119 return 120 } 121 122 var reportData nsmodel.Report 123 err := json.NewDecoder(req.Body).Decode(&reportData) 124 if err != nil { 125 logger.Warnf("Got error on parsing Request Body: %v\n", err) 126 httputil.RespondWithError(ctx, rw, http.StatusBadRequest, httputil.Error{ 127 Code: http.StatusBadRequest, 128 Message: "failed to parse request body", 129 }) 130 return 131 } 132 133 if err := reportData.Validate(); err != nil { 134 logger.Warnf("Got error while validating Request Body: %v\n", err) 135 httputil.RespondWithError(ctx, rw, http.StatusBadRequest, httputil.Error{ 136 Code: http.StatusBadRequest, 137 Message: err.Error(), 138 }) 139 return 140 } 141 142 sccs := make([]*nsmodel.SCC, 0, len(reportData.Value)) 143 externalIDs := make([]string, 0, len(reportData.Value)) 144 for _, scc := range reportData.Value { 145 // New object with the same data is created and added to the sccs slice instead of adding &scc to the slice 146 // because otherwise the slice is populated with copies of the last scc`s address 147 s := &nsmodel.SCC{ 148 ExternalSubaccountID: scc.ExternalSubaccountID, 149 InternalSubaccountID: scc.InternalSubaccountID, 150 LocationID: scc.LocationID, 151 ExposedSystems: scc.ExposedSystems, 152 } 153 externalIDs = append(externalIDs, scc.ExternalSubaccountID) 154 sccs = append(sccs, s) 155 } 156 157 tenants, err := a.listTenantsByExternalIDs(ctx, externalIDs) 158 if err != nil { 159 logger.Warnf("Got error while listing subaccounts: %v\n", err) 160 httputil.RespondWithError(ctx, rw, http.StatusInternalServerError, httputil.Error{ 161 Code: http.StatusInternalServerError, 162 Message: "Update failed", 163 }) 164 return 165 } 166 mapExternalToInternal(ctx, tenants, sccs) 167 details := make([]httputil.Detail, 0) 168 filteredSccs := filterSccsByInternalID(ctx, sccs, &details) 169 170 logger.Infof("Starting processing of %q report.", reportType) 171 reportHandler(ctx, filteredSccs, details, reportData, rw) 172 } 173 174 func (a *Handler) deltaReportHandler(ctx context.Context, filteredSccs []*nsmodel.SCC, details []httputil.Detail, _ nsmodel.Report, rw http.ResponseWriter) { 175 a.processDelta(ctx, filteredSccs, &details) 176 if len(details) == 0 { 177 httputils.RespondWithBody(ctx, rw, http.StatusNoContent, struct{}{}) 178 return 179 } 180 httputil.RespondWithError(ctx, rw, http.StatusOK, httputil.DetailedError{ 181 Code: http.StatusOK, 182 Message: "Update/create failed for some on-premise systems", 183 Details: details, 184 }) 185 } 186 187 func (a *Handler) fullReportHandler(ctx context.Context, filteredSccs []*nsmodel.SCC, details []httputil.Detail, reportData nsmodel.Report, rw http.ResponseWriter) { 188 a.processDelta(ctx, filteredSccs, &details) 189 a.handleUnreachableScc(ctx, reportData) 190 httputils.RespondWithBody(ctx, rw, http.StatusNoContent, struct{}{}) 191 } 192 193 func (a *Handler) listTenantsByExternalIDs(ctx context.Context, ids []string) ([]*model.BusinessTenantMapping, error) { 194 if len(ids) == 0 { 195 return make([]*model.BusinessTenantMapping, 0), nil 196 } 197 198 tx, err := a.transact.Begin() 199 if err != nil { 200 log.C(ctx).Warn(errors.Wrapf(err, "while openning transaction")) 201 return nil, err 202 } 203 defer a.transact.RollbackUnlessCommitted(ctx, tx) 204 205 ctxWithTransaction := persistence.SaveToContext(ctx, tx) 206 tenants, err := a.tntSvc.ListsByExternalIDs(ctxWithTransaction, ids) 207 if err != nil { 208 log.C(ctx).Warn(errors.Wrapf(err, "while listing tenants by external ids")) 209 return nil, err 210 } 211 212 if err := tx.Commit(); err != nil { 213 log.C(ctx).Warn(errors.Wrapf(err, "while committing transaction")) 214 return nil, err 215 } 216 217 return tenants, nil 218 } 219 220 func (a *Handler) listSCCs(ctx context.Context) ([]*model.SccMetadata, error) { 221 tx, err := a.transact.Begin() 222 if err != nil { 223 log.C(ctx).Warn(errors.Wrapf(err, "while openning transaction")) 224 return nil, err 225 } 226 defer a.transact.RollbackUnlessCommitted(ctx, tx) 227 228 ctxWithTransaction := persistence.SaveToContext(ctx, tx) 229 sccs, err := a.appSvc.ListSCCs(ctxWithTransaction) 230 if err != nil { 231 log.C(ctx).Warn(errors.Wrapf(err, "while listing all sccs")) 232 return nil, err 233 } 234 235 if err := tx.Commit(); err != nil { 236 log.C(ctx).Warn(errors.Wrapf(err, "while committing transaction")) 237 return nil, err 238 } 239 240 return sccs, nil 241 } 242 243 func (a *Handler) handleUnreachableScc(ctx context.Context, reportData nsmodel.Report) { 244 sccs, err := a.listSCCs(ctx) 245 if err != nil { 246 log.C(ctx).Warn(errors.Wrapf(err, "while listing sccs")) 247 return 248 } 249 250 if len(sccs) == len(reportData.Value) { 251 return 252 } 253 254 sccsFromNs := make([]*model.SccMetadata, 0, len(reportData.Value)) 255 for _, scc := range reportData.Value { 256 sccsFromNs = append(sccsFromNs, &model.SccMetadata{ 257 Subaccount: scc.ExternalSubaccountID, 258 LocationID: scc.LocationID, 259 }) 260 } 261 262 sccsToMarkAsUnreachable := difference(sccs, sccsFromNs) 263 264 externalSubaccounts := make([]string, 0, len(sccsToMarkAsUnreachable)) 265 for _, scc := range sccsToMarkAsUnreachable { 266 externalSubaccounts = append(externalSubaccounts, scc.Subaccount) 267 } 268 269 internalSubaccounts, err := a.listTenantsByExternalIDs(ctx, externalSubaccounts) 270 if err != nil { 271 log.C(ctx).Warn(errors.Wrapf(err, "while listing subaccounts")) 272 return 273 } 274 275 externalToInternalSub := make(map[string]string, len(internalSubaccounts)) 276 for _, subaccount := range internalSubaccounts { 277 externalToInternalSub[subaccount.ExternalTenant] = subaccount.ID 278 } 279 280 for _, scc := range sccsToMarkAsUnreachable { 281 scc.InternalSubaccountID = externalToInternalSub[scc.Subaccount] 282 } 283 284 for _, scc := range sccsToMarkAsUnreachable { 285 ctxWithSubaccount := tenant.SaveToContext(ctx, scc.InternalSubaccountID, scc.Subaccount) 286 appsWithLabels, ok := a.listAppsByScc(ctxWithSubaccount, scc.Subaccount, scc.LocationID) 287 if ok { 288 for _, appWithLabels := range appsWithLabels { 289 a.markSystemAsUnreachable(ctxWithSubaccount, appWithLabels.App) 290 } 291 } 292 } 293 } 294 295 func (a *Handler) processDelta(ctx context.Context, sccs []*nsmodel.SCC, details *[]httputil.Detail) { 296 for _, scc := range sccs { 297 ctxWithTenant := tenant.SaveToContext(ctx, scc.InternalSubaccountID, scc.ExternalSubaccountID) 298 if ok := a.handleSccSystems(ctxWithTenant, *scc); !ok { 299 addErrorDetailsMsg(details, scc, "Creation failed") 300 } 301 } 302 } 303 304 func (a *Handler) handleSccSystems(ctx context.Context, scc nsmodel.SCC) bool { 305 successfulUpsert := a.upsertSccSystems(ctx, scc) 306 successfulMark := a.markAsUnreachable(ctx, scc) 307 return successfulUpsert && successfulMark 308 } 309 310 func (a *Handler) upsertSccSystems(ctx context.Context, scc nsmodel.SCC) bool { 311 success := true 312 for _, system := range scc.ExposedSystems { 313 if len(system.TemplateID) == 0 { 314 log.C(ctx).Infof("Skipping processing of system with unsupported type %s", system.SystemType) 315 continue 316 } 317 318 tx, err := a.transact.Begin() 319 if err != nil { 320 log.C(ctx).Warn(errors.Wrapf(err, "while openning transaction")) 321 return false 322 } 323 ctxWithTransaction := persistence.SaveToContext(ctx, tx) 324 325 var txSucceeded bool 326 if system.SystemNumber != "" { 327 txSucceeded = a.upsertWithSystemNumber(ctxWithTransaction, scc, system) 328 } else { 329 txSucceeded = a.upsert(ctxWithTransaction, scc, system) 330 } 331 332 if txSucceeded { 333 if err := tx.Commit(); err != nil { 334 txSucceeded = false 335 log.C(ctx).Warn(errors.Wrapf(err, "while committing transaction")) 336 } 337 } 338 339 a.transact.RollbackUnlessCommitted(ctx, tx) 340 success = success && txSucceeded 341 } 342 return success 343 } 344 345 func (a *Handler) prepareAppInput(ctx context.Context, scc nsmodel.SCC, system nsmodel.System) (*model.ApplicationRegisterInput, error) { 346 template, err := a.appTemplateSvc.Get(ctx, system.TemplateID) 347 if err != nil { 348 return nil, errors.Wrapf(err, "while getting application template with id: %s", system.TemplateID) 349 } 350 351 values := model.ApplicationFromTemplateInputValues{ 352 { 353 Placeholder: "name", 354 Value: "on-premise-system" + uuid.New().String(), 355 }, 356 { 357 Placeholder: "description", 358 Value: system.Description, 359 }, 360 { 361 Placeholder: "subaccount", 362 Value: scc.ExternalSubaccountID, 363 }, 364 { 365 Placeholder: "location-id", 366 Value: scc.LocationID, 367 }, 368 { 369 Placeholder: "system-type", 370 Value: system.SystemType, 371 }, 372 { 373 Placeholder: "host", 374 Value: system.Host, 375 }, 376 { 377 Placeholder: "protocol", 378 Value: system.Protocol, 379 }, 380 { 381 Placeholder: "system-number", 382 Value: system.SystemNumber, 383 }, 384 { 385 Placeholder: "system-status", 386 Value: system.Status, 387 }, 388 } 389 390 appInputJSON, err := a.appTemplateSvc.PrepareApplicationCreateInputJSON(template, values) 391 if err != nil { 392 return nil, errors.Wrapf(err, "while preparing application create input from template with id:%s", system.TemplateID) 393 } 394 395 appInput, err := a.appConverter.CreateInputJSONToModel(ctx, appInputJSON) 396 if err != nil { 397 return nil, errors.Wrapf(err, "while preparing application create input from json") 398 } 399 400 if appInput.SystemNumber != nil && *appInput.SystemNumber == "" { 401 appInput.SystemNumber = nil 402 } 403 404 return &appInput, nil 405 } 406 407 func (a *Handler) upsertWithSystemNumber(ctx context.Context, scc nsmodel.SCC, system nsmodel.System) bool { 408 appInput, err := a.prepareAppInput(ctx, scc, system) 409 if err != nil { 410 log.C(ctx).Warn(errors.Wrapf(err, "while upserting Application")) 411 return false 412 } 413 414 if err := a.appSvc.Upsert(ctx, *appInput); err != nil { 415 log.C(ctx).Warn(errors.Wrapf(err, "while upserting Application")) 416 return false 417 } 418 419 return true 420 } 421 422 func (a *Handler) upsert(ctx context.Context, scc nsmodel.SCC, system nsmodel.System) bool { 423 app, err := a.appSvc.GetSccSystem(ctx, scc.ExternalSubaccountID, scc.LocationID, system.Host) 424 425 if err != nil && isNotFoundError(err) { 426 return a.createAppFromTemplate(ctx, scc, system) 427 } 428 429 if err != nil { 430 log.C(ctx).Warn(errors.Wrapf(err, "while getting Application")) 431 return false 432 } 433 434 return a.updateSystem(ctx, system, app) 435 } 436 437 func (a *Handler) createAppFromTemplate(ctx context.Context, scc nsmodel.SCC, system nsmodel.System) bool { 438 appInput, err := a.prepareAppInput(ctx, scc, system) 439 if err != nil { 440 log.C(ctx).Warn(errors.Wrapf(err, "while creating Application")) 441 return false 442 } 443 444 if _, err := a.appSvc.CreateFromTemplate(ctx, *appInput, str.Ptr(system.TemplateID)); err != nil { 445 log.C(ctx).Warn(errors.Wrapf(err, "while creating Application")) 446 return false 447 } 448 return true 449 } 450 451 func (a *Handler) updateSystem(ctx context.Context, system nsmodel.System, app *model.Application) bool { 452 if err := a.appSvc.Update(ctx, app.ID, nsmodel.ToAppUpdateInput(system)); err != nil { 453 log.C(ctx).Warn(errors.Wrapf(err, "while updating Application with id %s", app.ID)) 454 return false 455 } 456 457 if err := a.appSvc.SetLabel(ctx, &model.LabelInput{ 458 Key: "systemType", 459 Value: system.SystemType, 460 ObjectID: app.ID, 461 ObjectType: model.ApplicationLabelableObject, 462 }); err != nil { 463 log.C(ctx).Warn(errors.Wrapf(err, "while setting 'systemType' label for Application with id %s", app.ID)) 464 return false 465 } 466 467 if err := a.appSvc.SetLabel(ctx, &model.LabelInput{ 468 Key: "systemProtocol", 469 Value: system.Protocol, 470 ObjectID: app.ID, 471 ObjectType: model.ApplicationLabelableObject, 472 }); err != nil { 473 log.C(ctx).Warn(errors.Wrapf(err, "while setting 'systemProtocol' label for Application with id %s", app.ID)) 474 return false 475 } 476 477 return true 478 } 479 480 func (a *Handler) markAsUnreachable(ctx context.Context, scc nsmodel.SCC) bool { 481 apps, ok := a.listAppsByScc(ctx, scc.ExternalSubaccountID, scc.LocationID) 482 if !ok { 483 return false 484 } 485 486 success := true 487 unreachable := filterUnreachable(apps, scc.ExposedSystems) 488 for _, system := range unreachable { 489 success = a.markSystemAsUnreachable(ctx, system) && success 490 } 491 return success 492 } 493 494 func (a *Handler) markSystemAsUnreachable(ctx context.Context, system *model.Application) bool { 495 tx, err := a.transact.Begin() 496 if err != nil { 497 log.C(ctx).Warn(errors.Wrapf(err, "while openning transaction")) 498 return false 499 } 500 defer a.transact.RollbackUnlessCommitted(ctx, tx) 501 502 ctxWithTransaction := persistence.SaveToContext(ctx, tx) 503 504 if err := a.appSvc.Update(ctxWithTransaction, system.ID, model.ApplicationUpdateInput{SystemStatus: str.Ptr("unreachable")}); err != nil { 505 log.C(ctx).Warn(errors.Wrapf(err, "while marking application with id %s as unreachable", system.ID)) 506 return false 507 } 508 509 if err := tx.Commit(); err != nil { 510 log.C(ctx).Warn(errors.Wrapf(err, "while committing transaction")) 511 return false 512 } 513 514 return true 515 } 516 517 func (a *Handler) listAppsByScc(ctx context.Context, subaccount, locationID string) ([]*model.ApplicationWithLabel, bool) { 518 tx, err := a.transact.Begin() 519 if err != nil { 520 log.C(ctx).Warn(errors.Wrapf(err, "while openning transaction")) 521 return nil, false 522 } 523 defer a.transact.RollbackUnlessCommitted(ctx, tx) 524 525 ctxWithTransaction := persistence.SaveToContext(ctx, tx) 526 apps, err := a.appSvc.ListBySCC(ctxWithTransaction, labelfilter.NewForKeyWithQuery("scc", fmt.Sprintf("{\"LocationID\":\"%s\", \"Subaccount\":\"%s\"}", locationID, subaccount))) 527 if err != nil { 528 log.C(ctx).Warn(errors.Wrapf(err, "while listing all applications for scc with subaccount %s and location id %s", subaccount, locationID)) 529 return nil, false 530 } 531 532 if err := tx.Commit(); err != nil { 533 log.C(ctx).Warn(errors.Wrapf(err, "while committing transaction")) 534 return nil, false 535 } 536 537 return apps, true 538 } 539 540 func filterUnreachable(apps []*model.ApplicationWithLabel, systems []nsmodel.System) []*model.Application { 541 hostToSystem := make(map[string]interface{}, len(systems)) 542 543 for _, s := range systems { 544 hostToSystem[s.Host] = struct{}{} 545 } 546 unreachable := make([]*model.Application, 0, len(apps)) 547 548 for _, a := range apps { 549 result := a.SccLabel.Value.(map[string]interface{})["Host"] 550 _, ok := hostToSystem[result.(string)] 551 if !ok { 552 unreachable = append(unreachable, a.App) 553 } 554 } 555 return unreachable 556 } 557 558 func difference(a, b []*model.SccMetadata) (diff []*model.SccMetadata) { 559 m := make(map[model.SccMetadata]bool) 560 561 for _, item := range b { 562 m[*item] = true 563 } 564 565 for _, item := range a { 566 if _, ok := m[*item]; !ok { 567 diff = append(diff, item) 568 } 569 } 570 return 571 } 572 573 func addErrorDetailsMsg(details *[]httputil.Detail, scc *nsmodel.SCC, message string) { 574 *details = append(*details, httputil.Detail{ 575 Message: message, 576 Subaccount: scc.ExternalSubaccountID, 577 LocationID: scc.LocationID, 578 }) 579 } 580 581 func mapExternalToInternal(ctx context.Context, tenants []*model.BusinessTenantMapping, sccs []*nsmodel.SCC) { 582 externalToInternalTenants := make(map[string]*model.BusinessTenantMapping, len(tenants)) 583 for _, t := range tenants { 584 externalToInternalTenants[t.ExternalTenant] = t 585 } 586 587 for _, scc := range sccs { 588 t, exist := externalToInternalTenants[scc.ExternalSubaccountID] 589 if !exist { 590 continue 591 } 592 if t.Type == "subaccount" { 593 scc.InternalSubaccountID = t.ID 594 } else { 595 log.C(ctx).Warnf("Got tenant with id: %s which is not a subaccount", t.ID) 596 scc.InternalSubaccountID = notSubaccountMarker 597 } 598 } 599 } 600 601 func isNotFoundError(err error) bool { 602 return strings.Contains(err.Error(), "Object not found") 603 } 604 605 func filterSccsByInternalID(ctx context.Context, sccs []*nsmodel.SCC, details *[]httputil.Detail) []*nsmodel.SCC { 606 filteredSccs := make([]*nsmodel.SCC, 0, len(sccs)) 607 for _, scc := range sccs { 608 if scc.InternalSubaccountID == "" { 609 log.C(ctx).Warnf("Got SCC with external subaccount id: %s which has not associated internal tenant id", scc.ExternalSubaccountID) 610 addErrorDetailsMsg(details, scc, "Subaccount not found") 611 } else if scc.InternalSubaccountID == notSubaccountMarker { 612 addErrorDetailsMsg(details, scc, "Provided id is not subaccount") 613 } else { 614 filteredSccs = append(filteredSccs, scc) 615 } 616 } 617 return filteredSccs 618 }