MCPcopy
hub / github.com/docker/compose / planRecreateContainer

Method planRecreateContainer

pkg/compose/reconcile.go:654–732  ·  view source on GitHub ↗

planRecreateContainer decomposes container recreation into 4 atomic operations: CreateContainer(tmpName) → StopContainer → RemoveContainer → RenameContainer

(service types.ServiceConfig, oc *ObservedContainer, infraDeps []*PlanNode)

Source from the content-addressed store, hash-verified

652// planRecreateContainer decomposes container recreation into 4 atomic operations:
653// CreateContainer(tmpName) → StopContainer → RemoveContainer → RenameContainer
654func (r *reconciler) planRecreateContainer(service types.ServiceConfig, oc *ObservedContainer, infraDeps []*PlanNode) *PlanNode {
655 resID := fmt.Sprintf("service:%s:%d", service.Name, oc.Number)
656 group := fmt.Sprintf("recreate:%s:%d", service.Name, oc.Number)
657 tmpName := fmt.Sprintf("%s_%s", oc.ID[:min(12, len(oc.ID))], getContainerName(r.project.Name, service, oc.Number))
658 svc := service // copy for pointer stability
659
660 // Stop dependents first
661 depStopNodes := r.planStopDependents(service)
662
663 // All deps: infrastructure + dependent stops
664 allDeps := append(slices.Clone(infraDeps), depStopNodes...)
665
666 var inherited *container.Summary
667 if r.options.Inherit {
668 inherited = &oc.Summary
669 }
670
671 // 1. Create new container with temporary name
672 createNode := r.plan.addNode(Operation{
673 Type: OpCreateContainer,
674 ResourceID: resID,
675 Cause: "config changed (tmpName)",
676 Service: &svc,
677 Inherited: inherited,
678 Number: oc.Number,
679 Name: tmpName,
680 }, group, allDeps...)
681
682 // 2. Stop old container. If an earlier stage of the plan (e.g.
683 // planRecreateNetwork) already scheduled a Stop for this container,
684 // reuse it instead of emitting a second one against an already-stopped
685 // container. The reused node carries no group, which is fine: the
686 // recreate's group tracker still drives Working/Done from the create.
687 stopNode, alreadyStopped := r.stoppedByPlan[oc.ID]
688 if !alreadyStopped {
689 stopNode = r.plan.addNode(Operation{
690 Type: OpStopContainer,
691 ResourceID: resID,
692 Cause: fmt.Sprintf("replaced by #%d", createNode.ID),
693 Container: &oc.Summary,
694 Timeout: r.options.Timeout,
695 }, group, createNode)
696 r.stoppedByPlan[oc.ID] = stopNode
697 }
698
699 // 3. Remove old container. The invariant is "new container exists before
700 // old one is removed":
701 // - !alreadyStopped: stopNode is the one we just created with
702 // DependsOn=[createNode], so the chain remove → stop → create
703 // guarantees the invariant transitively.
704 // - alreadyStopped: stopNode comes from planRecreateNetwork and was
705 // emitted before createNode. The transitive guarantee no longer
706 // holds, so we add createNode to removeDeps explicitly. If anyone
707 // ever drops the stop → create edge from the !alreadyStopped case,
708 // they must add createNode here unconditionally.
709 removeDeps := []*PlanNode{stopNode}
710 if alreadyStopped {
711 removeDeps = append(removeDeps, createNode)

Callers 1

reconcileServiceMethod · 0.95

Calls 3

planStopDependentsMethod · 0.95
getContainerNameFunction · 0.85
addNodeMethod · 0.80

Tested by

no test coverage detected