planRecreateVolume adds the full sequence for a diverged volume: stop affected containers → remove containers → remove volume → create volume. Containers must be removed (not just stopped) because Docker does not allow removing a volume that is referenced by any container, even a stopped one. nolin
(key string, vol *types.VolumeConfig)
| 274 | // |
| 275 | //nolint:unused // see reconciler.prompt field doc — seam consolidation. |
| 276 | func (r *reconciler) planRecreateVolume(key string, vol *types.VolumeConfig) { |
| 277 | observed := r.observed.Volumes[key] |
| 278 | affectedServices := r.servicesUsingVolume(key) |
| 279 | affectedContainers := r.containersForServices(affectedServices) |
| 280 | |
| 281 | // Stop all affected containers |
| 282 | var stopNodes []*PlanNode |
| 283 | for i := range affectedContainers { |
| 284 | oc := &affectedContainers[i] |
| 285 | node := r.plan.addNode(Operation{ |
| 286 | Type: OpStopContainer, |
| 287 | ResourceID: fmt.Sprintf("service:%s:%d", oc.Summary.Labels[api.ServiceLabel], oc.Number), |
| 288 | Cause: fmt.Sprintf("volume %s config changed", key), |
| 289 | Container: &oc.Summary, |
| 290 | }, "") |
| 291 | stopNodes = append(stopNodes, node) |
| 292 | } |
| 293 | |
| 294 | // Remove all affected containers (each depends on its own stop) |
| 295 | var removeNodes []*PlanNode |
| 296 | for i, oc := range affectedContainers { |
| 297 | node := r.plan.addNode(Operation{ |
| 298 | Type: OpRemoveContainer, |
| 299 | ResourceID: fmt.Sprintf("service:%s:%d", oc.Summary.Labels[api.ServiceLabel], oc.Number), |
| 300 | Cause: fmt.Sprintf("volume %s config changed", key), |
| 301 | Container: &affectedContainers[i].Summary, |
| 302 | }, "", stopNodes[i]) |
| 303 | removeNodes = append(removeNodes, node) |
| 304 | } |
| 305 | |
| 306 | // Remove the *observed* volume (depends on all container removals) |
| 307 | removeVolNode := r.plan.addNode(Operation{ |
| 308 | Type: OpRemoveVolume, |
| 309 | ResourceID: fmt.Sprintf("volume:%s", key), |
| 310 | Cause: "config hash diverged", |
| 311 | Name: observed.Name, |
| 312 | }, "", removeNodes...) |
| 313 | |
| 314 | // Create volume (depends on remove) |
| 315 | createNode := r.plan.addNode(Operation{ |
| 316 | Type: OpCreateVolume, |
| 317 | ResourceID: fmt.Sprintf("volume:%s", key), |
| 318 | Cause: "recreate after config change", |
| 319 | Name: vol.Name, |
| 320 | Volume: vol, |
| 321 | }, "", removeVolNode) |
| 322 | r.volumeNodes[key] = createNode |
| 323 | } |
| 324 | |
| 325 | // servicesUsingNetwork returns the names of services that reference the given |
| 326 | // compose network key, sorted for deterministic plan output. |
nothing calls this directly
no test coverage detected