feat(config, images): Auto-clean stale images, introduce configs#133
Conversation
Code Review SummaryThis PR introduces a robust system for cleaning up stale Docker images after deployments and pulls. It also adds a generic configuration management API that allows updating agent settings at runtime with persistence. 🚀 Key Improvements
💡 Minor Suggestions
🚨 Critical Issues
|
After a successful pull (whether via the standalone pull endpoint or as part of a deploy with pull), the agent now removes images that this deployment previously used but no longer references. An image is only removed if no container references it and no other deployment's compose references it either, so cleanup never deletes anything still in use locally. The default is on; pass cleanup=false on the request to skip. Two new endpoints make the cleanup callable on demand: one scoped to a single deployment and one for host-wide pruning of dangling layers. A dry_run flag returns the would-be removals without deleting anything. Closes #124
Clients can now list, fetch, and update the agent's YAML configuration through dotted keys instead of one bespoke endpoint per setting. Sensitive keys are masked, string inputs are coerced to the field's real type, and runtime-applicable keys take effect immediately. The new endpoints sit behind dedicated config permissions granted only to admins, so the discovery surface is admin-only by default. A top-level default timeout setting is the fallback for any block that does not specify its own, used today by the image cleanup loop. The legacy /settings endpoints are unchanged.
14c1ce1 to
262c8aa
Compare
…hash tags Image refs are now parsed with the official Docker reference package, so edge cases like registries with ports and digest references are handled the same way Docker itself handles them. The agent and the daemon now normalize refs identically, which removes a class of false negatives in the cross-deployment match. The auto-cleanup is also gated by tag shape: an image is only removed if every tag on it looks like a content hash (sha-* prefixes, git shas, sha256 digests). Floating labels (latest, edge, stable, semver) are preserved even when otherwise eligible, since they often still mean something to a human operator.
| return | ||
| } | ||
|
|
||
| if s.configPath != "" { |
There was a problem hiding this comment.
The code currently returns a 500 error if the configuration is updated in memory but fails to persist to disk. Since the in-memory state and the file are now out of sync, it is safer to return the error and avoid proceeding to apply the configuration to the runtime.
| if s.configPath != "" { | |
| if s.configPath != "" { | |
| if err := config.Save(s.config, s.configPath); err != nil { | |
| c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to persist configuration: " + err.Error()}) | |
| return | |
| } | |
| } |
| } | ||
| if c.Image != "" { |
There was a problem hiding this comment.
When collecting image references from containers, using the Image field (which can be a tag or a name) is often redundant if ImageID is present. However, to ensure perfect matching against RepoTags, it's better to specifically check for and include the full tag reference if it's available in the container list to avoid missing matches during selectStaleImages.
| } | |
| if c.Image != "" { | |
| if c.Image != "" { | |
| out[normalizeRef(c.Image)] = true | |
| } |
| } | ||
| return resolveField(v.Field(i), f.Type, parts[1]) |
There was a problem hiding this comment.
When resolving nested struct fields via reflection, if an intermediate pointer is nil, the code will panic when calling v.Field(i) or f.Type recursively. Add a check to initialize pointers if they are nil during resolution.
| } | |
| return resolveField(v.Field(i), f.Type, parts[1]) | |
| if v.Field(i).Kind() == reflect.Ptr && v.Field(i).IsNil() { | |
| v.Field(i).Set(reflect.New(v.Field(i).Type().Elem())) | |
| } | |
| return resolveField(v.Field(i), f.Type, parts[1]) |
After a successful pull (whether via the standalone pull endpoint or as part of a deploy with pull), the agent now removes images that this deployment previously used but no longer references. An image is only removed if no container references it and no other deployment's compose references it either, so cleanup never deletes anything still in use locally. The default is on; pass cleanup=false on the request to skip.
Two new endpoints make the cleanup callable on demand: one scoped to a single deployment and one for host-wide pruning of dangling layers. A dry_run flag returns the would-be removals without deleting anything.
Closes #124