reconcileService handles a single service: scale down, recreate diverged, start stopped, scale up.
(service types.ServiceConfig)
| 421 | // reconcileService handles a single service: scale down, recreate diverged, |
| 422 | // start stopped, scale up. |
| 423 | func (r *reconciler) reconcileService(service types.ServiceConfig) error { |
| 424 | if service.Provider != nil && r.options.SkipProviders { |
| 425 | return nil |
| 426 | } |
| 427 | if service.Provider != nil { |
| 428 | svc := service |
| 429 | deps := r.infrastructureDeps(service) |
| 430 | node := r.plan.addNode(Operation{ |
| 431 | Type: OpRunProvider, |
| 432 | ResourceID: fmt.Sprintf("provider:%s", service.Name), |
| 433 | Cause: "provider service", |
| 434 | Service: &svc, |
| 435 | }, "", deps...) |
| 436 | r.serviceNodes[service.Name] = node |
| 437 | return nil |
| 438 | } |
| 439 | |
| 440 | expected, err := getScale(service) |
| 441 | if err != nil { |
| 442 | return err |
| 443 | } |
| 444 | |
| 445 | containers := r.observed.Containers[service.Name] |
| 446 | actual := len(containers) |
| 447 | |
| 448 | strategy := r.options.RecreateDependencies |
| 449 | if slices.Contains(r.options.Services, service.Name) || len(r.options.Services) == 0 { |
| 450 | strategy = r.options.Recreate |
| 451 | } |
| 452 | |
| 453 | // Precompute once per service: mustRecreate is called twice per container |
| 454 | // (sortContainers + main loop) and the hash/cascade inputs depend on the |
| 455 | // service, not the container. |
| 456 | expectedHash, err := serviceHashWithResolvedRefs(service, r.observedContainersByService) |
| 457 | if err != nil { |
| 458 | return err |
| 459 | } |
| 460 | parentRecreated := r.parentNamespaceRecreated(service) |
| 461 | |
| 462 | // Sort containers: obsolete first, then by number descending, then reverse |
| 463 | // to get the same ordering as the existing convergence code. |
| 464 | r.sortContainers(containers, service, expectedHash, parentRecreated, strategy) |
| 465 | |
| 466 | // Collect dependency nodes that container creation should depend on |
| 467 | infraDeps := r.infrastructureDeps(service) |
| 468 | |
| 469 | var lastNode *PlanNode |
| 470 | |
| 471 | // Process existing containers |
| 472 | for i, oc := range containers { |
| 473 | if i >= expected { |
| 474 | // Scale down: stop + remove excess containers. Track the remove |
| 475 | // node so dependent services wait for the scale-down to finish |
| 476 | // even when no other operation runs on this service. |
| 477 | stopNode := r.plan.addNode(Operation{ |
| 478 | Type: OpStopContainer, |
| 479 | ResourceID: fmt.Sprintf("service:%s:%d", service.Name, oc.Number), |
| 480 | Cause: "scale down", |
no test coverage detected