Skip to content

Commit 7f4d8ae

Browse files
committed
feat: full control over prune options
1 parent 2735b0b commit 7f4d8ae

36 files changed

+1661
-435
lines changed

backend/internal/configschema/schema.go

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,20 +102,61 @@ var overrideDocRules = map[string]overrideDocRule{
102102
requires: "SCHEDULED_PRUNE_ENABLED=true to have effect at runtime.",
103103
},
104104
"scheduledPruneContainers": {
105-
requires: "SCHEDULED_PRUNE_ENABLED=true to have effect at runtime.",
105+
deprecated: true,
106+
requires: "SCHEDULED_PRUNE_ENABLED=true to have effect at runtime.",
107+
note: "Legacy boolean prune flag retained for migration compatibility. Prefer PRUNE_CONTAINER_MODE.",
106108
},
107109
"scheduledPruneImages": {
108-
requires: "SCHEDULED_PRUNE_ENABLED=true to have effect at runtime.",
110+
deprecated: true,
111+
requires: "SCHEDULED_PRUNE_ENABLED=true to have effect at runtime.",
112+
note: "Legacy boolean prune flag retained for migration compatibility. Prefer PRUNE_IMAGE_MODE.",
109113
},
110114
"scheduledPruneVolumes": {
111-
requires: "SCHEDULED_PRUNE_ENABLED=true to have effect at runtime.",
115+
deprecated: true,
116+
requires: "SCHEDULED_PRUNE_ENABLED=true to have effect at runtime.",
117+
note: "Legacy boolean prune flag retained for migration compatibility. Prefer PRUNE_VOLUME_MODE.",
112118
},
113119
"scheduledPruneNetworks": {
114-
requires: "SCHEDULED_PRUNE_ENABLED=true to have effect at runtime.",
120+
deprecated: true,
121+
requires: "SCHEDULED_PRUNE_ENABLED=true to have effect at runtime.",
122+
note: "Legacy boolean prune flag retained for migration compatibility. Prefer PRUNE_NETWORK_MODE.",
115123
},
116124
"scheduledPruneBuildCache": {
125+
deprecated: true,
126+
requires: "SCHEDULED_PRUNE_ENABLED=true to have effect at runtime.",
127+
note: "Legacy boolean prune flag retained for migration compatibility. Prefer PRUNE_BUILD_CACHE_MODE.",
128+
},
129+
"pruneContainerMode": {
130+
requires: "SCHEDULED_PRUNE_ENABLED=true to have effect at runtime.",
131+
},
132+
"pruneContainerUntil": {
133+
requires: "SCHEDULED_PRUNE_ENABLED=true and PRUNE_CONTAINER_MODE=olderThan to have effect at runtime.",
134+
},
135+
"pruneImageMode": {
136+
requires: "SCHEDULED_PRUNE_ENABLED=true to have effect at runtime.",
137+
},
138+
"pruneImageUntil": {
139+
requires: "SCHEDULED_PRUNE_ENABLED=true and PRUNE_IMAGE_MODE=olderThan to have effect at runtime.",
140+
},
141+
"pruneVolumeMode": {
142+
requires: "SCHEDULED_PRUNE_ENABLED=true to have effect at runtime.",
143+
},
144+
"pruneNetworkMode": {
117145
requires: "SCHEDULED_PRUNE_ENABLED=true to have effect at runtime.",
118146
},
147+
"pruneNetworkUntil": {
148+
requires: "SCHEDULED_PRUNE_ENABLED=true and PRUNE_NETWORK_MODE=olderThan to have effect at runtime.",
149+
},
150+
"pruneBuildCacheMode": {
151+
requires: "SCHEDULED_PRUNE_ENABLED=true to have effect at runtime.",
152+
},
153+
"pruneBuildCacheUntil": {
154+
requires: "SCHEDULED_PRUNE_ENABLED=true and PRUNE_BUILD_CACHE_MODE=olderThan to have effect at runtime.",
155+
},
156+
"dockerPruneMode": {
157+
deprecated: true,
158+
note: "Legacy prune mode retained for migration compatibility. Prefer the granular scheduled prune mode settings.",
159+
},
119160
"vulnerabilityScanInterval": {
120161
requires: "VULNERABILITY_SCAN_ENABLED=true to have effect at runtime.",
121162
},

backend/internal/configschema/schema_test.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,6 @@ var expectedSettingOverrideKeys = []string{
271271
"dockerApiTimeout",
272272
"dockerHost",
273273
"dockerImagePullTimeout",
274-
"dockerPruneMode",
275274
"enableGravatar",
276275
"environmentHealthInterval",
277276
"eventCleanupInterval",
@@ -308,6 +307,15 @@ var expectedSettingOverrideKeys = []string{
308307
"pollingInterval",
309308
"projectsDirectory",
310309
"proxyRequestTimeout",
310+
"pruneBuildCacheMode",
311+
"pruneBuildCacheUntil",
312+
"pruneContainerMode",
313+
"pruneContainerUntil",
314+
"pruneImageMode",
315+
"pruneImageUntil",
316+
"pruneNetworkMode",
317+
"pruneNetworkUntil",
318+
"pruneVolumeMode",
311319
"registryTimeout",
312320
"scheduledPruneBuildCache",
313321
"scheduledPruneContainers",

backend/internal/huma/handlers/dashboard_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ func TestDashboardHandlerGetDashboardReturnsSnapshot(t *testing.T) {
124124
require.Len(t, snapshot.Images.Data, 2)
125125
require.Equal(t, 1, snapshot.Containers.Counts.RunningContainers)
126126
require.Equal(t, 1, snapshot.Containers.Counts.StoppedContainers)
127-
require.Equal(t, "dangling", snapshot.Settings.DockerPruneMode)
127+
require.Equal(t, dashboardtypes.SnapshotSettings{}, snapshot.Settings)
128128
require.ElementsMatch(t, []dashboardtypes.ActionItem{
129129
{Kind: dashboardtypes.ActionItemKindStoppedContainers, Count: 1, Severity: dashboardtypes.ActionItemSeverityWarning},
130130
{Kind: dashboardtypes.ActionItemKindImageUpdates, Count: 1, Severity: dashboardtypes.ActionItemSeverityWarning},

backend/internal/huma/handlers/images.go

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/getarcaneapp/arcane/backend/pkg/pagination"
1616
"github.com/getarcaneapp/arcane/types/base"
1717
"github.com/getarcaneapp/arcane/types/image"
18+
"github.com/getarcaneapp/arcane/types/system"
1819
"gorm.io/gorm"
1920
)
2021

@@ -114,6 +115,8 @@ type PruneImagesInput struct {
114115
EnvironmentID string `path:"id" doc:"Environment ID"`
115116
Dangling bool `query:"dangling" doc:"Only remove dangling images"`
116117
Body *struct {
118+
Mode *string `json:"mode,omitempty"`
119+
Until *string `json:"until,omitempty"`
117120
Dangling *bool `json:"dangling,omitempty"`
118121
Filters map[string][]string `json:"filters,omitempty"`
119122
}
@@ -549,21 +552,13 @@ func (h *ImageHandler) PruneImages(ctx context.Context, input *PruneImagesInput)
549552
return nil, huma.Error500InternalServerError("service not available")
550553
}
551554

552-
dangling := input.Dangling
553-
if input.Body != nil {
554-
if input.Body.Dangling != nil {
555-
dangling = *input.Body.Dangling
556-
} else if vals, ok := input.Body.Filters["dangling"]; ok {
557-
for _, v := range vals {
558-
if v == "true" || v == "1" {
559-
dangling = true
560-
break
561-
}
562-
}
563-
}
564-
}
555+
mode := resolvePruneImageModeInternal(input)
556+
until := resolvePruneImageUntilInternal(input)
565557

566-
report, err := h.imageService.PruneImages(ctx, dangling)
558+
report, err := h.imageService.PruneImages(ctx, system.PruneImagesOptions{
559+
Mode: system.PruneImageMode(mode),
560+
Until: until,
561+
})
567562
if err != nil {
568563
return nil, huma.Error500InternalServerError((&common.ImagePruneError{Err: err}).Error())
569564
}
@@ -578,6 +573,58 @@ func (h *ImageHandler) PruneImages(ctx context.Context, input *PruneImagesInput)
578573
}, nil
579574
}
580575

576+
func resolvePruneImageModeInternal(input *PruneImagesInput) string {
577+
mode := resolveLegacyPruneImageModeInternal(input.Dangling)
578+
if input.Body == nil {
579+
return mode
580+
}
581+
582+
if input.Body.Mode != nil && strings.TrimSpace(*input.Body.Mode) != "" {
583+
return strings.TrimSpace(*input.Body.Mode)
584+
}
585+
586+
if input.Body.Dangling != nil {
587+
return resolveLegacyPruneImageModeInternal(*input.Body.Dangling)
588+
}
589+
590+
if vals, ok := input.Body.Filters["dangling"]; ok {
591+
for _, value := range vals {
592+
switch value {
593+
case "true", "1":
594+
return "dangling"
595+
case "false", "0":
596+
return "all"
597+
}
598+
}
599+
}
600+
601+
return mode
602+
}
603+
604+
func resolvePruneImageUntilInternal(input *PruneImagesInput) string {
605+
if input.Body == nil {
606+
return ""
607+
}
608+
609+
if input.Body.Until != nil {
610+
return strings.TrimSpace(*input.Body.Until)
611+
}
612+
613+
if vals, ok := input.Body.Filters["until"]; ok && len(vals) > 0 {
614+
return strings.TrimSpace(vals[0])
615+
}
616+
617+
return ""
618+
}
619+
620+
func resolveLegacyPruneImageModeInternal(dangling bool) string {
621+
if dangling {
622+
return "dangling"
623+
}
624+
625+
return "all"
626+
}
627+
581628
// GetImageUsageCounts returns counts of images by usage status.
582629
func (h *ImageHandler) GetImageUsageCounts(ctx context.Context, input *GetImageUsageCountsInput) (*GetImageUsageCountsOutput, error) {
583630
if h.dockerService == nil || h.imageService == nil {

backend/internal/huma/handlers/system.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,8 +360,7 @@ func (h *SystemHandler) PruneAll(ctx context.Context, input *PruneAllInput) (*Pr
360360
"images", input.Body.Images,
361361
"volumes", input.Body.Volumes,
362362
"networks", input.Body.Networks,
363-
"build_cache", input.Body.BuildCache,
364-
"dangling", input.Body.Dangling)
363+
"build_cache", input.Body.BuildCache)
365364

366365
result, err := h.systemService.PruneAll(ctx, input.Body)
367366
if err != nil {

0 commit comments

Comments
 (0)