fix(cli): clarify route diagnose cleanup output
- commit
- ea2fb16968ac711b60027cbac3ea9d6f621872c5
- author
- brice · · 2026-05-22 07:13 UTC
- parent
- 6507981
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +49 -2
M internal/adapters/in/cli/routes.go +6 -2
| @@ -1191,7 +1191,7 @@ func finalizeRouteDiagnosis(diag *routeDiagnosis) { | |||
| 1191 | 1191 | diag.Hints = append(diag.Hints, "persistent volumes are preserved by default") | |
| 1192 | 1192 | } | |
| 1193 | 1193 | if len(diag.OrphanedAttachments) > 0 { | |
| 1194 | - | diag.Hints = append(diag.Hints, "run 'gordon attachments prune --stop' to stop orphaned attachment containers while preserving volumes") | |
| 1194 | + | diag.Hints = append(diag.Hints, fmt.Sprintf("run 'gordon routes purge %s --attachments' to review orphaned attachment cleanup for this route", diag.Domain)) | |
| 1195 | 1195 | } | |
| 1196 | 1196 | } | |
| 1197 | 1197 | ||
| @@ -1323,8 +1323,12 @@ func writeRouteDiagnosisSummary(out io.Writer, diag *routeDiagnosis) error { | |||
| 1323 | 1323 | } | |
| 1324 | 1324 | ||
| 1325 | 1325 | func writeRouteDiagnosisLeftovers(out io.Writer, diag *routeDiagnosis) error { | |
| 1326 | + | volumeLabel := "Volume:" | |
| 1327 | + | if !diag.Configured { | |
| 1328 | + | volumeLabel = "Preserved volume:" | |
| 1329 | + | } | |
| 1326 | 1330 | for _, volume := range diag.Volumes { | |
| 1327 | - | if err := cliWriteLine(out, cliRenderMeta("Preserved volume:", volume.Name)); err != nil { | |
| 1331 | + | if err := cliWriteLine(out, cliRenderMeta(volumeLabel, volume.Name)); err != nil { | |
| 1328 | 1332 | return err | |
| 1329 | 1333 | } | |
| 1330 | 1334 | } | |
| @@ -1191,7 +1191,7 @@ func finalizeRouteDiagnosis(diag *routeDiagnosis) { | |||||
| 1191 | diag.Hints = append(diag.Hints, "persistent volumes are preserved by default") | 1191 | diag.Hints = append(diag.Hints, "persistent volumes are preserved by default") | ||
| 1192 | } | 1192 | } | ||
| 1193 | if len(diag.OrphanedAttachments) > 0 { | 1193 | if len(diag.OrphanedAttachments) > 0 { | ||
| 1194 | - | diag.Hints = append(diag.Hints, "run 'gordon attachments prune --stop' to stop orphaned attachment containers while preserving volumes") | 1194 | + | diag.Hints = append(diag.Hints, fmt.Sprintf("run 'gordon routes purge %s --attachments' to review orphaned attachment cleanup for this route", diag.Domain)) |
| 1195 | } | 1195 | } | ||
| 1196 | } | 1196 | } | ||
| 1197 | 1197 | ||||
| @@ -1323,8 +1323,12 @@ func writeRouteDiagnosisSummary(out io.Writer, diag *routeDiagnosis) error { | |||||
| 1323 | } | 1323 | } | ||
| 1324 | 1324 | ||||
| 1325 | func writeRouteDiagnosisLeftovers(out io.Writer, diag *routeDiagnosis) error { | 1325 | func writeRouteDiagnosisLeftovers(out io.Writer, diag *routeDiagnosis) error { | ||
| 1326 | + | volumeLabel := "Volume:" | |||
| 1327 | + | if !diag.Configured { | |||
| 1328 | + | volumeLabel = "Preserved volume:" | |||
| 1329 | + | } | |||
| 1326 | for _, volume := range diag.Volumes { | 1330 | for _, volume := range diag.Volumes { | ||
| 1327 | - | if err := cliWriteLine(out, cliRenderMeta("Preserved volume:", volume.Name)); err != nil { | 1331 | + | if err := cliWriteLine(out, cliRenderMeta(volumeLabel, volume.Name)); err != nil { |
| 1328 | return err | 1332 | return err | ||
| 1329 | } | 1333 | } | ||
| 1330 | } | 1334 | } | ||
M internal/adapters/in/cli/routes_test.go +43 -0
| @@ -980,6 +980,49 @@ func TestBuildRouteDiagnosis_RemoteMissingRouteDoesNotWarnOnExpectedNotFound(t * | |||
| 980 | 980 | assert.Empty(t, diag.Warnings) | |
| 981 | 981 | } | |
| 982 | 982 | ||
| 983 | + | func TestBuildRouteDiagnosis_UsesRouteScopedReviewHintForOrphanedAttachments(t *testing.T) { | |
| 984 | + | cpMock := climocks.NewMockControlPlane(t) | |
| 985 | + | cpMock.EXPECT().GetRoute(context.Background(), "app.example.test").Return(&domain.Route{Domain: "app.example.test", Image: "app:latest"}, nil).Once() | |
| 986 | + | cpMock.EXPECT().ListRoutesWithDetails(context.Background()).Return([]remote.RouteInfo{{ | |
| 987 | + | Domain: "app.example.test", | |
| 988 | + | ContainerID: "route-1", | |
| 989 | + | ContainerStatus: "running", | |
| 990 | + | }}, nil).Once() | |
| 991 | + | cpMock.EXPECT().GetHealth(context.Background()).Return(nil, nil).Once() | |
| 992 | + | cpMock.EXPECT().ListVolumes(context.Background()).Return(nil, nil).Once() | |
| 993 | + | cpMock.EXPECT().ListOrphanedAttachments(context.Background()).Return([]domain.CleanupAttachment{{ | |
| 994 | + | Name: "old-postgres", | |
| 995 | + | ContainerID: "attachment-1", | |
| 996 | + | Owner: "app.example.test", | |
| 997 | + | }}, nil).Once() | |
| 998 | + | ||
| 999 | + | diag, err := buildRouteDiagnosis(context.Background(), cpMock, "app.example.test") | |
| 1000 | + | ||
| 1001 | + | require.NoError(t, err) | |
| 1002 | + | assert.Contains(t, diag.Hints, "run 'gordon routes purge app.example.test --attachments' to review orphaned attachment cleanup for this route") | |
| 1003 | + | } | |
| 1004 | + | ||
| 1005 | + | func TestWriteRouteDiagnosisText_UsesNeutralVolumeLabelForConfiguredRoutes(t *testing.T) { | |
| 1006 | + | var out bytes.Buffer | |
| 1007 | + | diag := &routeDiagnosis{ | |
| 1008 | + | Domain: "app.example.test", | |
| 1009 | + | Configured: true, | |
| 1010 | + | Route: &domain.Route{Domain: "app.example.test", Image: "app:latest"}, | |
| 1011 | + | Runtime: &remote.RouteInfo{ | |
| 1012 | + | Domain: "app.example.test", | |
| 1013 | + | ContainerID: "route-1", | |
| 1014 | + | ContainerStatus: "running", | |
| 1015 | + | }, | |
| 1016 | + | Volumes: []dto.Volume{{Name: "gordon-app-example-test-data"}}, | |
| 1017 | + | } | |
| 1018 | + | ||
| 1019 | + | err := writeRouteDiagnosisText(&out, diag) | |
| 1020 | + | require.NoError(t, err) | |
| 1021 | + | rendered := stripANSI(out.String()) | |
| 1022 | + | assert.Contains(t, rendered, "Volume: gordon-app-example-test-data") | |
| 1023 | + | assert.NotContains(t, rendered, "Preserved volume:") | |
| 1024 | + | } | |
| 1025 | + | ||
| 983 | 1026 | func TestWriteRouteDiagnosisText_ShowsActiveAttachmentsSeparatelyFromOrphans(t *testing.T) { | |
| 984 | 1027 | var out bytes.Buffer | |
| 985 | 1028 | diag := &routeDiagnosis{ | |
| @@ -980,6 +980,49 @@ func TestBuildRouteDiagnosis_RemoteMissingRouteDoesNotWarnOnExpectedNotFound(t * | |||||
| 980 | assert.Empty(t, diag.Warnings) | 980 | assert.Empty(t, diag.Warnings) | ||
| 981 | } | 981 | } | ||
| 982 | 982 | ||||
| 983 | + | func TestBuildRouteDiagnosis_UsesRouteScopedReviewHintForOrphanedAttachments(t *testing.T) { | |||
| 984 | + | cpMock := climocks.NewMockControlPlane(t) | |||
| 985 | + | cpMock.EXPECT().GetRoute(context.Background(), "app.example.test").Return(&domain.Route{Domain: "app.example.test", Image: "app:latest"}, nil).Once() | |||
| 986 | + | cpMock.EXPECT().ListRoutesWithDetails(context.Background()).Return([]remote.RouteInfo{{ | |||
| 987 | + | Domain: "app.example.test", | |||
| 988 | + | ContainerID: "route-1", | |||
| 989 | + | ContainerStatus: "running", | |||
| 990 | + | }}, nil).Once() | |||
| 991 | + | cpMock.EXPECT().GetHealth(context.Background()).Return(nil, nil).Once() | |||
| 992 | + | cpMock.EXPECT().ListVolumes(context.Background()).Return(nil, nil).Once() | |||
| 993 | + | cpMock.EXPECT().ListOrphanedAttachments(context.Background()).Return([]domain.CleanupAttachment{{ | |||
| 994 | + | Name: "old-postgres", | |||
| 995 | + | ContainerID: "attachment-1", | |||
| 996 | + | Owner: "app.example.test", | |||
| 997 | + | }}, nil).Once() | |||
| 998 | + | ||||
| 999 | + | diag, err := buildRouteDiagnosis(context.Background(), cpMock, "app.example.test") | |||
| 1000 | + | ||||
| 1001 | + | require.NoError(t, err) | |||
| 1002 | + | assert.Contains(t, diag.Hints, "run 'gordon routes purge app.example.test --attachments' to review orphaned attachment cleanup for this route") | |||
| 1003 | + | } | |||
| 1004 | + | ||||
| 1005 | + | func TestWriteRouteDiagnosisText_UsesNeutralVolumeLabelForConfiguredRoutes(t *testing.T) { | |||
| 1006 | + | var out bytes.Buffer | |||
| 1007 | + | diag := &routeDiagnosis{ | |||
| 1008 | + | Domain: "app.example.test", | |||
| 1009 | + | Configured: true, | |||
| 1010 | + | Route: &domain.Route{Domain: "app.example.test", Image: "app:latest"}, | |||
| 1011 | + | Runtime: &remote.RouteInfo{ | |||
| 1012 | + | Domain: "app.example.test", | |||
| 1013 | + | ContainerID: "route-1", | |||
| 1014 | + | ContainerStatus: "running", | |||
| 1015 | + | }, | |||
| 1016 | + | Volumes: []dto.Volume{{Name: "gordon-app-example-test-data"}}, | |||
| 1017 | + | } | |||
| 1018 | + | ||||
| 1019 | + | err := writeRouteDiagnosisText(&out, diag) | |||
| 1020 | + | require.NoError(t, err) | |||
| 1021 | + | rendered := stripANSI(out.String()) | |||
| 1022 | + | assert.Contains(t, rendered, "Volume: gordon-app-example-test-data") | |||
| 1023 | + | assert.NotContains(t, rendered, "Preserved volume:") | |||
| 1024 | + | } | |||
| 1025 | + | ||||
| 983 | func TestWriteRouteDiagnosisText_ShowsActiveAttachmentsSeparatelyFromOrphans(t *testing.T) { | 1026 | func TestWriteRouteDiagnosisText_ShowsActiveAttachmentsSeparatelyFromOrphans(t *testing.T) { | ||
| 984 | var out bytes.Buffer | 1027 | var out bytes.Buffer | ||
| 985 | diag := &routeDiagnosis{ | 1028 | diag := &routeDiagnosis{ | ||