github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/storage/postsql/read.go (about) 1 package postsql 2 3 import ( 4 "fmt" 5 "strings" 6 "time" 7 8 "github.com/kyma-project/kyma-environment-broker/common/events" 9 "github.com/kyma-project/kyma-environment-broker/common/orchestration" 10 "github.com/kyma-project/kyma-environment-broker/internal" 11 "github.com/kyma-project/kyma-environment-broker/internal/storage/dberr" 12 "github.com/kyma-project/kyma-environment-broker/internal/storage/dbmodel" 13 "github.com/kyma-project/kyma-environment-broker/internal/storage/predicate" 14 "golang.org/x/exp/slices" 15 16 "github.com/gocraft/dbr" 17 "github.com/pivotal-cf/brokerapi/v8/domain" 18 ) 19 20 type readSession struct { 21 session *dbr.Session 22 } 23 24 func (r readSession) getInstancesJoinedWithOperationStatement() *dbr.SelectStmt { 25 join := fmt.Sprintf("%s.instance_id = %s.instance_id", InstancesTableName, OperationTableName) 26 stmt := r.session. 27 Select("instances.instance_id, instances.runtime_id, instances.global_account_id, instances.subscription_global_account_id, instances.service_id,"+ 28 " instances.service_plan_id, instances.dashboard_url, instances.provisioning_parameters, instances.created_at,"+ 29 " instances.updated_at, instances.deleted_at, instances.sub_account_id, instances.service_name, instances.service_plan_name,"+ 30 " instances.provider_region, instances.provider, operations.state, operations.description, operations.type, operations.created_at AS operation_created_at, operations.data"). 31 From(InstancesTableName). 32 LeftJoin(OperationTableName, join) 33 return stmt 34 } 35 36 func (r readSession) FindAllInstancesJoinedWithOperation(prct ...predicate.Predicate) ([]dbmodel.InstanceWithOperationDTO, dberr.Error) { 37 var instances []dbmodel.InstanceWithOperationDTO 38 39 stmt := r.getInstancesJoinedWithOperationStatement() 40 for _, p := range prct { 41 p.ApplyToPostgres(stmt) 42 } 43 44 if _, err := stmt.Load(&instances); err != nil { 45 return nil, dberr.Internal("Failed to fetch all instances: %s", err) 46 } 47 48 return instances, nil 49 } 50 51 func (r readSession) GetInstanceByID(instanceID string) (dbmodel.InstanceDTO, dberr.Error) { 52 var instance dbmodel.InstanceDTO 53 54 err := r.session. 55 Select("*"). 56 From(InstancesTableName). 57 Where(dbr.Eq("instance_id", instanceID)). 58 LoadOne(&instance) 59 60 if err != nil { 61 if err == dbr.ErrNotFound { 62 return dbmodel.InstanceDTO{}, dberr.NotFound("Cannot find Instance for instanceID:'%s'", instanceID) 63 } 64 return dbmodel.InstanceDTO{}, dberr.Internal("Failed to get Instance: %s", err) 65 } 66 67 return instance, nil 68 } 69 70 func (r readSession) FindAllInstancesForRuntimes(runtimeIdList []string) ([]dbmodel.InstanceDTO, dberr.Error) { 71 var instances []dbmodel.InstanceDTO 72 73 err := r.session. 74 Select("*"). 75 From(InstancesTableName). 76 Where("runtime_id IN ?", runtimeIdList). 77 LoadOne(&instances) 78 79 if err != nil { 80 if err == dbr.ErrNotFound { 81 return []dbmodel.InstanceDTO{}, dberr.NotFound("Cannot find Instances for runtime ID list: '%v'", runtimeIdList) 82 } 83 return []dbmodel.InstanceDTO{}, dberr.Internal("Failed to get Instances: %s", err) 84 } 85 return instances, nil 86 } 87 88 func (r readSession) FindAllInstancesForSubAccounts(subAccountslist []string) ([]dbmodel.InstanceDTO, dberr.Error) { 89 var instances []dbmodel.InstanceDTO 90 91 err := r.session. 92 Select("*"). 93 From(InstancesTableName). 94 Where("sub_account_id IN ?", subAccountslist). 95 LoadOne(&instances) 96 97 if err != nil { 98 if err == dbr.ErrNotFound { 99 return []dbmodel.InstanceDTO{}, nil 100 } 101 return []dbmodel.InstanceDTO{}, dberr.Internal("Failed to get Instances: %s", err) 102 } 103 return instances, nil 104 } 105 106 func (r readSession) GetLastOperation(instanceID string) (dbmodel.OperationDTO, dberr.Error) { 107 inst := dbr.Eq("instance_id", instanceID) 108 state := dbr.Neq("state", []string{orchestration.Pending, orchestration.Canceled}) 109 condition := dbr.And(inst, state) 110 operation, err := r.getLastOperation(condition) 111 if err != nil { 112 switch { 113 case dberr.IsNotFound(err): 114 return dbmodel.OperationDTO{}, dberr.NotFound("for instance ID: %s %s", instanceID, err) 115 default: 116 return dbmodel.OperationDTO{}, err 117 } 118 } 119 return operation, nil 120 } 121 122 func (r readSession) GetOperationByInstanceID(instanceId string) (dbmodel.OperationDTO, dberr.Error) { 123 condition := dbr.Eq("instance_id", instanceId) 124 operation, err := r.getOperation(condition) 125 if err != nil { 126 switch { 127 case dberr.IsNotFound(err): 128 return dbmodel.OperationDTO{}, dberr.NotFound("for instance_id: %s %s", instanceId, err) 129 default: 130 return dbmodel.OperationDTO{}, err 131 } 132 } 133 return operation, nil 134 } 135 136 func (r readSession) GetOperationByID(opID string) (dbmodel.OperationDTO, dberr.Error) { 137 condition := dbr.Eq("id", opID) 138 operation, err := r.getOperation(condition) 139 if err != nil { 140 switch { 141 case dberr.IsNotFound(err): 142 return dbmodel.OperationDTO{}, dberr.NotFound("for ID: %s %s", opID, err) 143 default: 144 return dbmodel.OperationDTO{}, err 145 } 146 } 147 return operation, nil 148 } 149 150 func (r readSession) ListOperations(filter dbmodel.OperationFilter) ([]dbmodel.OperationDTO, int, int, error) { 151 var operations []dbmodel.OperationDTO 152 153 stmt := r.session.Select("o.*"). 154 From(dbr.I(OperationTableName).As("o")). 155 OrderBy("o.created_at") 156 157 // Add pagination if provided 158 if filter.Page > 0 && filter.PageSize > 0 { 159 stmt.Paginate(uint64(filter.Page), uint64(filter.PageSize)) 160 } 161 162 // Apply filtering if provided 163 addOperationFilters(stmt, filter) 164 165 _, err := stmt.Load(&operations) 166 167 totalCount, err := r.getOperationCount(filter) 168 if err != nil { 169 return nil, -1, -1, err 170 } 171 172 return operations, 173 len(operations), 174 totalCount, 175 nil 176 } 177 178 func (r readSession) GetOrchestrationByID(oID string) (dbmodel.OrchestrationDTO, dberr.Error) { 179 condition := dbr.Eq("orchestration_id", oID) 180 operation, err := r.getOrchestration(condition) 181 if err != nil { 182 switch { 183 case dberr.IsNotFound(err): 184 return dbmodel.OrchestrationDTO{}, dberr.NotFound("for ID: %s %s", oID, err) 185 default: 186 return dbmodel.OrchestrationDTO{}, err 187 } 188 } 189 return operation, nil 190 } 191 192 func (r readSession) ListOrchestrations(filter dbmodel.OrchestrationFilter) ([]dbmodel.OrchestrationDTO, int, int, error) { 193 var orchestrations []dbmodel.OrchestrationDTO 194 195 stmt := r.session.Select("*"). 196 From(OrchestrationTableName). 197 OrderBy(CreatedAtField) 198 199 // Add pagination if provided 200 if filter.Page > 0 && filter.PageSize > 0 { 201 stmt.Paginate(uint64(filter.Page), uint64(filter.PageSize)) 202 } 203 204 // Apply filtering if provided 205 addOrchestrationFilters(stmt, filter) 206 207 _, err := stmt.Load(&orchestrations) 208 209 totalCount, err := r.getOrchestrationCount(filter) 210 if err != nil { 211 return nil, -1, -1, err 212 } 213 214 return orchestrations, 215 len(orchestrations), 216 totalCount, 217 nil 218 } 219 220 func (r readSession) CountNotFinishedOperationsByInstanceID(instanceID string) (int, dberr.Error) { 221 stateInProgress := dbr.Eq("state", domain.InProgress) 222 statePending := dbr.Eq("state", orchestration.Pending) 223 stateCondition := dbr.Or(statePending, stateInProgress) 224 instanceIDCondition := dbr.Eq("instance_id", instanceID) 225 226 var res struct { 227 Total int 228 } 229 err := r.session.Select("count(*) as total"). 230 From(OperationTableName). 231 Where(stateCondition). 232 Where(instanceIDCondition). 233 LoadOne(&res) 234 235 if err != nil { 236 return 0, dberr.Internal("Failed to count operations: %s", err) 237 } 238 return res.Total, nil 239 } 240 241 func (r readSession) GetNotFinishedOperationsByType(operationType internal.OperationType) ([]dbmodel.OperationDTO, dberr.Error) { 242 stateInProgress := dbr.Eq("state", domain.InProgress) 243 statePending := dbr.Eq("state", orchestration.Pending) 244 stateCondition := dbr.Or(statePending, stateInProgress) 245 typeCondition := dbr.Eq("type", operationType) 246 var operations []dbmodel.OperationDTO 247 248 _, err := r.session. 249 Select("*"). 250 From(OperationTableName). 251 Where(stateCondition). 252 Where(typeCondition). 253 Load(&operations) 254 if err != nil { 255 return nil, dberr.Internal("Failed to get operations: %s", err) 256 } 257 return operations, nil 258 } 259 260 func (r readSession) GetOperationByTypeAndInstanceID(inID string, opType internal.OperationType) (dbmodel.OperationDTO, dberr.Error) { 261 idCondition := dbr.Eq("instance_id", inID) 262 typeCondition := dbr.Eq("type", string(opType)) 263 var operation dbmodel.OperationDTO 264 265 err := r.session. 266 Select("*"). 267 From(OperationTableName). 268 Where(idCondition). 269 Where(typeCondition). 270 OrderDesc(CreatedAtField). 271 LoadOne(&operation) 272 273 if err != nil { 274 if err == dbr.ErrNotFound { 275 return dbmodel.OperationDTO{}, dberr.NotFound("cannot find operation: %s", err) 276 } 277 return dbmodel.OperationDTO{}, dberr.Internal("Failed to get operation: %s", err) 278 } 279 return operation, nil 280 } 281 282 func (r readSession) GetOperationsByTypeAndInstanceID(inID string, opType internal.OperationType) ([]dbmodel.OperationDTO, dberr.Error) { 283 idCondition := dbr.Eq("instance_id", inID) 284 typeCondition := dbr.Eq("type", string(opType)) 285 var operations []dbmodel.OperationDTO 286 287 _, err := r.session. 288 Select("*"). 289 From(OperationTableName). 290 Where(idCondition). 291 Where(typeCondition). 292 OrderDesc(CreatedAtField). 293 Load(&operations) 294 295 if err != nil { 296 return []dbmodel.OperationDTO{}, dberr.Internal("Failed to get operations: %s", err) 297 } 298 return operations, nil 299 } 300 301 func (r readSession) GetOperationsByInstanceID(inID string) ([]dbmodel.OperationDTO, dberr.Error) { 302 idCondition := dbr.Eq("instance_id", inID) 303 var operations []dbmodel.OperationDTO 304 305 _, err := r.session. 306 Select("*"). 307 From(OperationTableName). 308 Where(idCondition). 309 OrderDesc(CreatedAtField). 310 Load(&operations) 311 312 if err != nil { 313 return []dbmodel.OperationDTO{}, dberr.Internal("Failed to get operations: %s", err) 314 } 315 return operations, nil 316 } 317 318 func (r readSession) GetOperationsForIDs(opIDlist []string) ([]dbmodel.OperationDTO, dberr.Error) { 319 var operations []dbmodel.OperationDTO 320 321 _, err := r.session. 322 Select("*"). 323 From(OperationTableName). 324 Where("id IN ?", opIDlist). 325 Load(&operations) 326 if err != nil { 327 return nil, dberr.Internal("Failed to get operations: %s", err) 328 } 329 return operations, nil 330 } 331 332 func (r readSession) ListOperationsByType(operationType internal.OperationType) ([]dbmodel.OperationDTO, dberr.Error) { 333 typeCondition := dbr.Eq("type", operationType) 334 var operations []dbmodel.OperationDTO 335 336 _, err := r.session. 337 Select("*"). 338 From(OperationTableName). 339 Where(typeCondition). 340 Load(&operations) 341 if err != nil { 342 return nil, dberr.Internal("Failed to get operations: %s", err) 343 } 344 return operations, nil 345 } 346 347 func (r readSession) ListOperationsByOrchestrationID(orchestrationID string, filter dbmodel.OperationFilter) ([]dbmodel.OperationDTO, int, int, error) { 348 var ops []dbmodel.OperationDTO 349 condition := dbr.Eq("orchestration_id", orchestrationID) 350 351 stmt := r.session. 352 Select("o.*"). 353 From(dbr.I(OperationTableName).As("o")). 354 Where(condition). 355 OrderBy("o.created_at") 356 357 // Add pagination if provided 358 if filter.Page > 0 && filter.PageSize > 0 { 359 stmt.Paginate(uint64(filter.Page), uint64(filter.PageSize)) 360 } 361 362 // Apply filtering if provided 363 addOperationFilters(stmt, filter) 364 365 _, err := stmt.Load(&ops) 366 if err != nil { 367 return nil, -1, -1, dberr.Internal("Failed to get operations: %s", err) 368 } 369 370 totalCount, err := r.getUpgradeOperationCount(orchestrationID, filter) 371 if err != nil { 372 return nil, -1, -1, err 373 } 374 375 return ops, 376 len(ops), 377 totalCount, 378 nil 379 } 380 381 func (r readSession) ListOperationsInTimeRange(from, to time.Time) ([]dbmodel.OperationDTO, error) { 382 var ops []dbmodel.OperationDTO 383 condition := dbr.Or( 384 dbr.And(dbr.Gte("created_at", from), dbr.Lte("created_at", to)), 385 dbr.And(dbr.Gte("updated_at", from), dbr.Lte("updated_at", to)), 386 ) 387 388 stmt := r.session. 389 Select("*"). 390 From(OperationTableName). 391 Where(condition) 392 393 _, err := stmt.Load(&ops) 394 if err != nil { 395 return nil, dberr.Internal("Failed to get operations: %s", err) 396 } 397 398 return ops, nil 399 } 400 401 func (r readSession) GetRuntimeStateByOperationID(operationID string) (dbmodel.RuntimeStateDTO, dberr.Error) { 402 var state dbmodel.RuntimeStateDTO 403 404 err := r.session. 405 Select("*"). 406 From(RuntimeStateTableName). 407 Where(dbr.Eq("operation_id", operationID)). 408 LoadOne(&state) 409 410 if err != nil { 411 if err == dbr.ErrNotFound { 412 return dbmodel.RuntimeStateDTO{}, dberr.NotFound("cannot find runtime state: %s", err) 413 } 414 return dbmodel.RuntimeStateDTO{}, dberr.Internal("Failed to get runtime state: %s", err) 415 } 416 return state, nil 417 } 418 419 func (r readSession) ListRuntimeStateByRuntimeID(runtimeID string) ([]dbmodel.RuntimeStateDTO, dberr.Error) { 420 stateCondition := dbr.Eq("runtime_id", runtimeID) 421 var states []dbmodel.RuntimeStateDTO 422 423 _, err := r.session. 424 Select("*"). 425 From(RuntimeStateTableName). 426 Where(stateCondition). 427 OrderDesc(CreatedAtField). 428 Load(&states) 429 if err != nil { 430 return nil, dberr.Internal("Failed to get states: %s", err) 431 } 432 return states, nil 433 } 434 435 func (r readSession) GetLatestRuntimeStateByRuntimeID(runtimeID string) (dbmodel.RuntimeStateDTO, dberr.Error) { 436 var state dbmodel.RuntimeStateDTO 437 438 count, err := r.session. 439 Select("*"). 440 From(RuntimeStateTableName). 441 Where(dbr.Eq("runtime_id", runtimeID)). 442 OrderDesc(CreatedAtField). 443 Limit(1). 444 Load(&state) 445 if err != nil { 446 if err == dbr.ErrNotFound { 447 return dbmodel.RuntimeStateDTO{}, dberr.NotFound("cannot find runtime state: %s", err) 448 } 449 return dbmodel.RuntimeStateDTO{}, dberr.Internal("Failed to get the latest runtime state: %s", err) 450 } 451 if count == 0 { 452 return dbmodel.RuntimeStateDTO{}, dberr.NotFound("cannot find runtime state: %s", err) 453 } 454 return state, nil 455 } 456 457 func (r readSession) GetLatestRuntimeStateWithReconcilerInputByRuntimeID(runtimeID string) (dbmodel.RuntimeStateDTO, dberr.Error) { 458 var state dbmodel.RuntimeStateDTO 459 runtimeIDIsEqual := dbr.Eq("runtime_id", runtimeID) 460 reconcilerInputIsNotEmptyString := dbr.Neq("cluster_setup", "") 461 reconcilerInputIsNotNil := dbr.Neq("cluster_setup", nil) 462 innerCondition := dbr.And(reconcilerInputIsNotEmptyString, reconcilerInputIsNotNil) 463 condition := dbr.And(runtimeIDIsEqual, innerCondition) 464 465 count, err := r.session. 466 Select("*"). 467 From(RuntimeStateTableName). 468 Where(condition). 469 OrderDesc(CreatedAtField). 470 Limit(1). 471 Load(&state) 472 if err != nil { 473 if err == dbr.ErrNotFound { 474 return dbmodel.RuntimeStateDTO{}, dberr.NotFound("cannot find runtime state: %s", err) 475 } 476 return dbmodel.RuntimeStateDTO{}, dberr.Internal("Failed to get the latest runtime state with reconciler input: %s", err) 477 } 478 if count == 0 { 479 return dbmodel.RuntimeStateDTO{}, dberr.NotFound("cannot find runtime state with reconciler input: %s", err) 480 } 481 return state, nil 482 } 483 484 func (r readSession) GetLatestRuntimeStateWithKymaVersionByRuntimeID(runtimeID string) (dbmodel.RuntimeStateDTO, dberr.Error) { 485 var state dbmodel.RuntimeStateDTO 486 condition := dbr.And(dbr.Eq("runtime_id", runtimeID), 487 dbr.And(dbr.Neq("kyma_version", nil), dbr.Neq("kyma_version", "")), 488 ) 489 490 count, err := r.session. 491 Select("*"). 492 From(RuntimeStateTableName). 493 Where(condition). 494 OrderDesc(CreatedAtField). 495 Limit(1). 496 Load(&state) 497 if err != nil { 498 if err == dbr.ErrNotFound { 499 return state, dberr.NotFound("cannot find latest runtime state with kyma version: %s", err) 500 } 501 return state, dberr.Internal("Failed to get the latest runtime state with kyma version: %s", err) 502 } 503 if count == 0 { 504 return state, dberr.NotFound("found 0 latest runtime states with kyma version: %s", err) 505 } 506 return state, nil 507 } 508 509 func (r readSession) GetLatestRuntimeStateWithOIDCConfigByRuntimeID(runtimeID string) (dbmodel.RuntimeStateDTO, dberr.Error) { 510 var state dbmodel.RuntimeStateDTO 511 condition := dbr.And(dbr.Eq("runtime_id", runtimeID), 512 dbr.Expr("cluster_config::json->>'oidcConfig' != ?", "null"), 513 ) 514 515 count, err := r.session. 516 Select("*"). 517 From(RuntimeStateTableName). 518 Where(condition). 519 OrderDesc(CreatedAtField). 520 Limit(1). 521 Load(&state) 522 if err != nil { 523 if err == dbr.ErrNotFound { 524 return state, dberr.NotFound("cannot find latest runtime state with OIDC config: %s", err) 525 } 526 return state, dberr.Internal("failed to get the latest runtime state with OIDC config: %s", err) 527 } 528 if count == 0 { 529 return state, dberr.NotFound("found 0 latest runtime states with OIDC config: %s", err) 530 } 531 return state, nil 532 } 533 534 func (r readSession) getOperation(condition dbr.Builder) (dbmodel.OperationDTO, dberr.Error) { 535 var operation dbmodel.OperationDTO 536 537 err := r.session. 538 Select("*"). 539 From(OperationTableName). 540 Where(condition). 541 LoadOne(&operation) 542 543 if err != nil { 544 if err == dbr.ErrNotFound { 545 return dbmodel.OperationDTO{}, dberr.NotFound("cannot find operation: %s", err) 546 } 547 return dbmodel.OperationDTO{}, dberr.Internal("Failed to get operation: %s", err) 548 } 549 return operation, nil 550 } 551 552 func (r readSession) getLastOperation(condition dbr.Builder) (dbmodel.OperationDTO, dberr.Error) { 553 var operation dbmodel.OperationDTO 554 555 count, err := r.session. 556 Select("*"). 557 From(OperationTableName). 558 Where(condition). 559 OrderDesc(CreatedAtField). 560 Limit(1). 561 Load(&operation) 562 if err != nil { 563 if err == dbr.ErrNotFound { 564 return dbmodel.OperationDTO{}, dberr.NotFound("cannot find operation: %s", err) 565 } 566 return dbmodel.OperationDTO{}, dberr.Internal("Failed to get operation: %s", err) 567 } 568 if count == 0 { 569 return dbmodel.OperationDTO{}, dberr.NotFound("cannot find operation: %s", err) 570 } 571 572 return operation, nil 573 } 574 575 func (r readSession) getOrchestration(condition dbr.Builder) (dbmodel.OrchestrationDTO, dberr.Error) { 576 var operation dbmodel.OrchestrationDTO 577 578 err := r.session. 579 Select("*"). 580 From(OrchestrationTableName). 581 Where(condition). 582 LoadOne(&operation) 583 584 if err != nil { 585 if err == dbr.ErrNotFound { 586 return dbmodel.OrchestrationDTO{}, dberr.NotFound("cannot find operation: %s", err) 587 } 588 return dbmodel.OrchestrationDTO{}, dberr.Internal("Failed to get operation: %s", err) 589 } 590 return operation, nil 591 } 592 593 func (r readSession) GetOperationStats() ([]dbmodel.OperationStatEntry, error) { 594 var rows []dbmodel.OperationStatEntry 595 _, err := r.session.SelectBySql(fmt.Sprintf("select type, state, provisioning_parameters ->> 'plan_id' AS plan_id from %s", 596 OperationTableName)).Load(&rows) 597 return rows, err 598 } 599 600 func (r readSession) GetOperationStatsForOrchestration(orchestrationID string) ([]dbmodel.OperationStatEntry, error) { 601 var rows []dbmodel.OperationStatEntry 602 _, err := r.session.SelectBySql(fmt.Sprintf("select type, state, instance_id, provisioning_parameters ->> 'plan_id' AS plan_id from %s where orchestration_id='%s'", 603 OperationTableName, orchestrationID)).Load(&rows) 604 return rows, err 605 } 606 607 func (r readSession) GetInstanceStats() ([]dbmodel.InstanceByGlobalAccountIDStatEntry, error) { 608 var rows []dbmodel.InstanceByGlobalAccountIDStatEntry 609 _, err := r.session.SelectBySql(fmt.Sprintf("select global_account_id, count(*) as total from %s where deleted_at = '0001-01-01T00:00:00.000Z' group by global_account_id", 610 InstancesTableName)).Load(&rows) 611 return rows, err 612 } 613 614 func (r readSession) GetERSContextStats() ([]dbmodel.InstanceERSContextStatsEntry, error) { 615 var rows []dbmodel.InstanceERSContextStatsEntry 616 // group existing instances by license_Type from the last operation that is not pending or canceled 617 _, err := r.session.SelectBySql(` 618 SELECT license_type, count(1) as total 619 FROM ( 620 SELECT DISTINCT ON (instances.instance_id) instances.instance_id, operations.id, state, type, (operations.provisioning_parameters->'ers_context'->'license_type')::VARCHAR AS license_type 621 FROM operations 622 INNER JOIN instances 623 ON operations.instance_id = instances.instance_id 624 WHERE (operations.state != 'pending' OR operations.state != 'canceled') AND deleted_at = '0001-01-01T00:00:00.000Z' 625 ORDER BY instance_id, operations.created_at DESC 626 ) t 627 GROUP BY license_type; 628 `).Load(&rows) 629 return rows, err 630 } 631 632 func (r readSession) GetNumberOfInstancesForGlobalAccountID(globalAccountID string) (int, error) { 633 var res struct { 634 Total int 635 } 636 err := r.session.Select("count(*) as total"). 637 From(InstancesTableName). 638 Where(dbr.Eq("global_account_id", globalAccountID)). 639 Where(dbr.Eq("deleted_at", "0001-01-01T00:00:00.000Z")). 640 LoadOne(&res) 641 642 return res.Total, err 643 } 644 645 func (r readSession) ListInstances(filter dbmodel.InstanceFilter) ([]dbmodel.InstanceDTO, int, int, error) { 646 var instances []dbmodel.InstanceDTO 647 648 // Base select and order by created at 649 var stmt *dbr.SelectStmt 650 // Find and join the last operation for each instance matching the state filter(s). 651 // Last operation is found with the greatest-n-per-group problem solved with OUTER JOIN, followed by a (INNER) JOIN to get instance columns. 652 stmt = r.session. 653 Select(fmt.Sprintf("%s.*", InstancesTableName)). 654 From(InstancesTableName). 655 Join(dbr.I(OperationTableName).As("o1"), fmt.Sprintf("%s.instance_id = o1.instance_id", InstancesTableName)). 656 LeftJoin(dbr.I(OperationTableName).As("o2"), fmt.Sprintf("%s.instance_id = o2.instance_id AND o1.created_at < o2.created_at AND o2.state NOT IN ('%s', '%s')", InstancesTableName, orchestration.Pending, orchestration.Canceled)). 657 Where("o2.created_at IS NULL"). 658 Where(fmt.Sprintf("o1.state NOT IN ('%s', '%s')", orchestration.Pending, orchestration.Canceled)). 659 OrderBy(fmt.Sprintf("%s.%s", InstancesTableName, CreatedAtField)) 660 661 if len(filter.States) > 0 { 662 stateFilters := buildInstanceStateFilters("o1", filter) 663 stmt.Where(stateFilters) 664 } 665 666 // Add pagination 667 if filter.Page > 0 && filter.PageSize > 0 { 668 stmt = stmt.Paginate(uint64(filter.Page), uint64(filter.PageSize)) 669 } 670 671 addInstanceFilters(stmt, filter) 672 673 _, err := stmt.Load(&instances) 674 if err != nil { 675 return nil, -1, -1, fmt.Errorf("while fetching instances: %w", err) 676 } 677 678 totalCount, err := r.getInstanceCount(filter) 679 if err != nil { 680 return nil, -1, -1, err 681 } 682 683 return instances, 684 len(instances), 685 totalCount, 686 nil 687 } 688 689 func (r readSession) ListEvents(filter events.EventFilter) ([]events.EventDTO, error) { 690 var events []events.EventDTO 691 stmt := r.session.Select("*").From("events") 692 if len(filter.InstanceIDs) != 0 { 693 stmt.Where(dbr.Eq("instance_id", filter.InstanceIDs)) 694 } 695 if len(filter.OperationIDs) != 0 { 696 stmt.Where(dbr.Eq("operation_id", filter.OperationIDs)) 697 } 698 stmt.OrderBy("created_at") 699 _, err := stmt.Load(&events) 700 return events, err 701 } 702 703 func (r readSession) getInstanceCount(filter dbmodel.InstanceFilter) (int, error) { 704 var res struct { 705 Total int 706 } 707 var stmt *dbr.SelectStmt 708 stmt = r.session. 709 Select("count(*) as total"). 710 From(InstancesTableName). 711 Join(dbr.I(OperationTableName).As("o1"), fmt.Sprintf("%s.instance_id = o1.instance_id", InstancesTableName)). 712 LeftJoin(dbr.I(OperationTableName).As("o2"), fmt.Sprintf("%s.instance_id = o2.instance_id AND o1.created_at < o2.created_at AND o2.state NOT IN ('%s', '%s')", InstancesTableName, orchestration.Pending, orchestration.Canceled)). 713 Where("o2.created_at IS NULL"). 714 Where(fmt.Sprintf("o1.state NOT IN ('%s', '%s')", orchestration.Pending, orchestration.Canceled)) 715 716 if len(filter.States) > 0 { 717 stateFilters := buildInstanceStateFilters("o1", filter) 718 stmt.Where(stateFilters) 719 } 720 721 addInstanceFilters(stmt, filter) 722 err := stmt.LoadOne(&res) 723 724 return res.Total, err 725 } 726 727 func buildInstanceStateFilters(table string, filter dbmodel.InstanceFilter) dbr.Builder { 728 var exprs []dbr.Builder 729 for _, s := range filter.States { 730 switch s { 731 case dbmodel.InstanceSucceeded: 732 exprs = append(exprs, dbr.And( 733 dbr.Eq(fmt.Sprintf("%s.state", table), domain.Succeeded), 734 dbr.Neq(fmt.Sprintf("%s.type", table), internal.OperationTypeDeprovision), 735 )) 736 case dbmodel.InstanceFailed: 737 exprs = append(exprs, dbr.And( 738 dbr.Or( 739 dbr.Eq(fmt.Sprintf("%s.type", table), internal.OperationTypeProvision), 740 dbr.Eq(fmt.Sprintf("%s.type", table), internal.OperationTypeDeprovision), 741 ), 742 dbr.Eq(fmt.Sprintf("%s.state", table), domain.Failed), 743 )) 744 case dbmodel.InstanceError: 745 exprs = append(exprs, dbr.And( 746 dbr.Neq(fmt.Sprintf("%s.type", table), internal.OperationTypeProvision), 747 dbr.Neq(fmt.Sprintf("%s.type", table), internal.OperationTypeDeprovision), 748 dbr.Eq(fmt.Sprintf("%s.state", table), domain.Failed), 749 )) 750 case dbmodel.InstanceProvisioning: 751 exprs = append(exprs, dbr.And( 752 dbr.Eq(fmt.Sprintf("%s.type", table), internal.OperationTypeProvision), 753 dbr.Eq(fmt.Sprintf("%s.state", table), domain.InProgress), 754 )) 755 case dbmodel.InstanceDeprovisioning: 756 exprs = append(exprs, dbr.And( 757 dbr.Eq(fmt.Sprintf("%s.type", table), internal.OperationTypeDeprovision), 758 dbr.Eq(fmt.Sprintf("%s.state", table), domain.InProgress), 759 )) 760 case dbmodel.InstanceUpgrading: 761 exprs = append(exprs, dbr.And( 762 dbr.Like(fmt.Sprintf("%s.type", table), "upgrade%"), 763 dbr.Eq(fmt.Sprintf("%s.state", table), domain.InProgress), 764 )) 765 case dbmodel.InstanceUpdating: 766 exprs = append(exprs, dbr.And( 767 dbr.Eq(fmt.Sprintf("%s.type", table), internal.OperationTypeUpdate), 768 dbr.Eq(fmt.Sprintf("%s.state", table), domain.InProgress), 769 )) 770 case dbmodel.InstanceDeprovisioned: 771 exprs = append(exprs, dbr.And( 772 dbr.Eq(fmt.Sprintf("%s.type", table), internal.OperationTypeDeprovision), 773 dbr.Eq(fmt.Sprintf("%s.state", table), domain.Succeeded), 774 )) 775 case dbmodel.InstanceNotDeprovisioned: 776 exprs = append(exprs, dbr.Or( 777 dbr.Neq(fmt.Sprintf("%s.type", table), internal.OperationTypeDeprovision), 778 dbr.Neq(fmt.Sprintf("%s.state", table), domain.Succeeded), 779 )) 780 } 781 } 782 783 return dbr.Or(exprs...) 784 } 785 786 func addInstanceFilters(stmt *dbr.SelectStmt, filter dbmodel.InstanceFilter) { 787 if len(filter.GlobalAccountIDs) > 0 { 788 stmt.Where("instances.global_account_id IN ?", filter.GlobalAccountIDs) 789 } 790 if len(filter.SubAccountIDs) > 0 { 791 stmt.Where("instances.sub_account_id IN ?", filter.SubAccountIDs) 792 } 793 if len(filter.InstanceIDs) > 0 { 794 stmt.Where("instances.instance_id IN ?", filter.InstanceIDs) 795 } 796 if len(filter.RuntimeIDs) > 0 { 797 stmt.Where("instances.runtime_id IN ?", filter.RuntimeIDs) 798 } 799 if len(filter.Regions) > 0 { 800 stmt.Where("instances.provider_region IN ?", filter.Regions) 801 } 802 if len(filter.Plans) > 0 { 803 stmt.Where("instances.service_plan_name IN ?", filter.Plans) 804 } 805 if len(filter.PlanIDs) > 0 { 806 stmt.Where("instances.service_plan_id IN ?", filter.PlanIDs) 807 } 808 if len(filter.Shoots) > 0 { 809 shootNameMatch := fmt.Sprintf(`^(%s)$`, strings.Join(filter.Shoots, "|")) 810 stmt.Where("o1.data::json->>'shoot_name' ~ ?", shootNameMatch) 811 } 812 813 if filter.Expired != nil { 814 if *filter.Expired { 815 stmt.Where("instances.expired_at IS NOT NULL") 816 } 817 if !*filter.Expired { 818 stmt.Where("instances.expired_at IS NULL") 819 } 820 } 821 822 if filter.DeletionAttempted != nil { 823 if *filter.DeletionAttempted { 824 stmt.Where("instances.deleted_at != '0001-01-01T00:00:00.000Z'") 825 } 826 if !*filter.DeletionAttempted { 827 stmt.Where("instances.deleted_at = '0001-01-01T00:00:00.000Z'") 828 } 829 } 830 } 831 832 func addOrchestrationFilters(stmt *dbr.SelectStmt, filter dbmodel.OrchestrationFilter) { 833 if len(filter.Types) > 0 { 834 stmt.Where("type IN ?", filter.Types) 835 } 836 if len(filter.States) > 0 { 837 stmt.Where("state IN ?", filter.States) 838 } 839 } 840 841 func addOperationFilters(stmt *dbr.SelectStmt, filter dbmodel.OperationFilter) { 842 if len(filter.States) > 0 { 843 stmt.Where("o.state IN ?", filter.States) 844 } 845 if filter.InstanceFilter != nil { 846 fi := filter.InstanceFilter 847 if slices.Contains(filter.States, string(dbmodel.InstanceDeprovisioned)) { 848 stmt.LeftJoin(dbr.I(InstancesTableName).As("i"), "i.instance_id = o.instance_id"). 849 Where("i.instance_id IS NULL") 850 } 851 if len(fi.InstanceIDs) != 0 { 852 stmt.Where("o.instance_id IN ?", fi.InstanceIDs) 853 } 854 } 855 } 856 857 func (r readSession) getOperationCount(filter dbmodel.OperationFilter) (int, error) { 858 var res struct { 859 Total int 860 } 861 stmt := r.session.Select("count(1) as total"). 862 From(dbr.I(OperationTableName).As("o")) 863 addOperationFilters(stmt, filter) 864 err := stmt.LoadOne(&res) 865 866 return res.Total, err 867 } 868 869 func (r readSession) getUpgradeOperationCount(orchestrationID string, filter dbmodel.OperationFilter) (int, error) { 870 var res struct { 871 Total int 872 } 873 stmt := r.session.Select("count(1) as total"). 874 From(dbr.I(OperationTableName).As("o")). 875 Where(dbr.Eq("o.orchestration_id", orchestrationID)) 876 addOperationFilters(stmt, filter) 877 err := stmt.LoadOne(&res) 878 879 return res.Total, err 880 } 881 882 func (r readSession) getOrchestrationCount(filter dbmodel.OrchestrationFilter) (int, error) { 883 var res struct { 884 Total int 885 } 886 stmt := r.session.Select("count(*) as total").From(OrchestrationTableName) 887 addOrchestrationFilters(stmt, filter) 888 err := stmt.LoadOne(&res) 889 890 return res.Total, err 891 }