detached 511705f

Merge pull request #149 from bnema/fix/preview-orphan-gc-network

fix(preview): move network removal after container removal
commit
511705f7166173244d352d7a1816130e5aff71c9
author
bnema · · 2026-03-25 18:34 UTC
committer
GitHub · · 2026-03-25 18:34 UTC
merge of
7568a1c + a7406bc · diff against the first parent
signature
GPG UnverifiedB5690EEEBB952194 The key is unknown
refs
tag: v2.30.0-pre6
1 file · +21 -6
M internal/app/run.go +21 -6
@@ -645,8 +645,9 @@ func gcOrphanedPreviews(ctx context.Context, svc *services, previewConfig domain
645645 log.Warn().Str("container", c.Name).Str("image", c.Image).Str("domain", orphanDomain).
646646 Time("created", c.Created).Msg("orphaned preview container detected, cleaning up")
647647
648- // Stop container first, then clean up domain resources while container
649- // still exists (so future scans can rediscover it on failure), then remove.
648+ // Order: stop → remove volumes/route → remove container → remove network.
649+ // Network removal must happen after container removal because the runtime
650+ // refuses to delete a network that still has containers attached.
650651 if err := svc.runtime.StopContainer(ctx, c.Name); err != nil {
651652 log.Warn().Err(err).Str("container", c.Name).Msg("failed to stop orphan container")
652653 }
@@ -663,20 +664,34 @@ func gcOrphanedPreviews(ctx context.Context, svc *services, previewConfig domain
663664 log.Warn().Err(err).Str("container", c.Name).Msg("failed to remove orphan container")
664665 }
665666
667+ // Remove network after container is gone.
668+ if orphanDomain != "" {
669+ if err := removeOrphanNetwork(ctx, svc, strings.ReplaceAll(orphanDomain, ".", "-"), log); err != nil {
670+ log.Warn().Err(err).Str("container", c.Name).Msg("failed to remove orphan network after container removal")
671+ }
672+ }
673+
666674 log.Info().Str("container", c.Name).Str("domain", orphanDomain).Msg("orphaned preview container cleaned up")
667675 }
676+
677+ // Re-sync container state so stale routes disappear from routes list.
678+ if len(orphans) > 0 {
679+ if err := svc.containerSvc.SyncContainers(ctx); err != nil {
680+ log.Warn().Err(err).Msg("failed to sync containers after orphan GC")
681+ }
682+ }
668683 }
669684
670-// cleanupOrphanDomainResources removes volumes, network, and route for an
671-// orphaned preview domain. Returns an error if any step fails so the caller
672-// can defer container removal until the next scan.
685+// cleanupOrphanDomainResources removes volumes and route for an orphaned
686+// preview domain. Network removal is handled separately in gcOrphanedPreviews
687+// after the container is removed. Returns an error if any step fails so the
688+// caller can defer container removal until the next scan.
673689 func cleanupOrphanDomainResources(ctx context.Context, svc *services, orphanDomain string) error {
674690 log := zerowrap.FromCtx(ctx)
675691 domainSanitized := strings.ReplaceAll(orphanDomain, ".", "-")
676692 var errs []error
677693
678694 errs = append(errs, removeOrphanVolumes(ctx, svc, domainSanitized, log)...)
679- errs = append(errs, removeOrphanNetwork(ctx, svc, domainSanitized, log))
680695 errs = append(errs, removeOrphanRoute(ctx, svc, orphanDomain, log))
681696 svc.proxySvc.InvalidateTarget(ctx, orphanDomain)
682697
@@ -645,8 +645,9 @@ func gcOrphanedPreviews(ctx context.Context, svc *services, previewConfig domain
645 log.Warn().Str("container", c.Name).Str("image", c.Image).Str("domain", orphanDomain).645 log.Warn().Str("container", c.Name).Str("image", c.Image).Str("domain", orphanDomain).
646 Time("created", c.Created).Msg("orphaned preview container detected, cleaning up")646 Time("created", c.Created).Msg("orphaned preview container detected, cleaning up")
647 647
648- // Stop container first, then clean up domain resources while container648+ // Order: stop → remove volumes/route → remove container → remove network.
649- // still exists (so future scans can rediscover it on failure), then remove.649+ // Network removal must happen after container removal because the runtime
650+ // refuses to delete a network that still has containers attached.
650 if err := svc.runtime.StopContainer(ctx, c.Name); err != nil {651 if err := svc.runtime.StopContainer(ctx, c.Name); err != nil {
651 log.Warn().Err(err).Str("container", c.Name).Msg("failed to stop orphan container")652 log.Warn().Err(err).Str("container", c.Name).Msg("failed to stop orphan container")
652 }653 }
@@ -663,20 +664,34 @@ func gcOrphanedPreviews(ctx context.Context, svc *services, previewConfig domain
663 log.Warn().Err(err).Str("container", c.Name).Msg("failed to remove orphan container")664 log.Warn().Err(err).Str("container", c.Name).Msg("failed to remove orphan container")
664 }665 }
665 666
667+ // Remove network after container is gone.
668+ if orphanDomain != "" {
669+ if err := removeOrphanNetwork(ctx, svc, strings.ReplaceAll(orphanDomain, ".", "-"), log); err != nil {
670+ log.Warn().Err(err).Str("container", c.Name).Msg("failed to remove orphan network after container removal")
671+ }
672+ }
673+
666 log.Info().Str("container", c.Name).Str("domain", orphanDomain).Msg("orphaned preview container cleaned up")674 log.Info().Str("container", c.Name).Str("domain", orphanDomain).Msg("orphaned preview container cleaned up")
667 }675 }
676+
677+ // Re-sync container state so stale routes disappear from routes list.
678+ if len(orphans) > 0 {
679+ if err := svc.containerSvc.SyncContainers(ctx); err != nil {
680+ log.Warn().Err(err).Msg("failed to sync containers after orphan GC")
681+ }
682+ }
668 }683 }
669 684
670-// cleanupOrphanDomainResources removes volumes, network, and route for an685+// cleanupOrphanDomainResources removes volumes and route for an orphaned
671-// orphaned preview domain. Returns an error if any step fails so the caller686+// preview domain. Network removal is handled separately in gcOrphanedPreviews
672-// can defer container removal until the next scan.687+// after the container is removed. Returns an error if any step fails so the
688+// caller can defer container removal until the next scan.
673 func cleanupOrphanDomainResources(ctx context.Context, svc *services, orphanDomain string) error {689 func cleanupOrphanDomainResources(ctx context.Context, svc *services, orphanDomain string) error {
674 log := zerowrap.FromCtx(ctx)690 log := zerowrap.FromCtx(ctx)
675 domainSanitized := strings.ReplaceAll(orphanDomain, ".", "-")691 domainSanitized := strings.ReplaceAll(orphanDomain, ".", "-")
676 var errs []error692 var errs []error
677 693
678 errs = append(errs, removeOrphanVolumes(ctx, svc, domainSanitized, log)...)694 errs = append(errs, removeOrphanVolumes(ctx, svc, domainSanitized, log)...)
679- errs = append(errs, removeOrphanNetwork(ctx, svc, domainSanitized, log))
680 errs = append(errs, removeOrphanRoute(ctx, svc, orphanDomain, log))695 errs = append(errs, removeOrphanRoute(ctx, svc, orphanDomain, log))
681 svc.proxySvc.InvalidateTarget(ctx, orphanDomain)696 svc.proxySvc.InvalidateTarget(ctx, orphanDomain)
682 697

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close