detached 62502c9

refactor(container): use FindRoutesByImage in ImagePushedHandler

commit
62502c992994d0ef11692b2fc94d1519d486bbb3
author
bnema · · 2026-02-09 20:22 UTC
parent
322f9c0
signature
unsigned
2 files · +9 -51
M internal/usecase/container/events.go +1 -33
@@ -3,7 +3,6 @@ package container
33 import (
44 "context"
55 "fmt"
6- "strings"
76
87 "github.com/bnema/zerowrap"
98
@@ -50,7 +49,7 @@ func (h *ImagePushedHandler) Handle(ctx context.Context, event domain.Event) err
5049
5150 log.Info().Str("image", fullImageName).Msg("processing image push event")
5251
53- routes := h.findRoutesForImage(fullImageName)
52+ routes := h.configSvc.FindRoutesByImage(ctx, fullImageName)
5453 if len(routes) == 0 {
5554 log.Debug().Str("image", fullImageName).Msg("no routes configured for pushed image")
5655 return nil
@@ -74,37 +73,6 @@ func (h *ImagePushedHandler) CanHandle(eventType domain.EventType) bool {
7473 return eventType == domain.EventImagePushed
7574 }
7675
77-func (h *ImagePushedHandler) findRoutesForImage(imageName string) []domain.Route {
78- var routes []domain.Route
79-
80- registryDomain := h.configSvc.GetRegistryDomain()
81- normalizedImage := normalizeRegistryImage(imageName, registryDomain)
82-
83- configuredRoutes := h.configSvc.GetRoutes(h.ctx)
84- for _, route := range configuredRoutes {
85- normalizedRouteImage := normalizeRegistryImage(route.Image, registryDomain)
86- if strings.EqualFold(normalizedRouteImage, normalizedImage) {
87- routes = append(routes, route)
88- }
89- }
90-
91- return routes
92-}
93-
94-func normalizeRegistryImage(imageName, registryDomain string) string {
95- registryDomain = strings.TrimSuffix(registryDomain, "/")
96- if registryDomain == "" {
97- return imageName
98- }
99-
100- prefix := registryDomain + "/"
101- if strings.HasPrefix(imageName, prefix) {
102- return strings.TrimPrefix(imageName, prefix)
103- }
104-
105- return imageName
106-}
107-
10876 // ConfigReloadHandler handles config.reload events.
10977 type ConfigReloadHandler struct {
11078 containerSvc in.ContainerService
@@ -3,7 +3,6 @@ package container
3 import (3 import (
4 "context"4 "context"
5 "fmt"5 "fmt"
6- "strings"
7 6
8 "github.com/bnema/zerowrap"7 "github.com/bnema/zerowrap"
9 8
@@ -50,7 +49,7 @@ func (h *ImagePushedHandler) Handle(ctx context.Context, event domain.Event) err
50 49
51 log.Info().Str("image", fullImageName).Msg("processing image push event")50 log.Info().Str("image", fullImageName).Msg("processing image push event")
52 51
53- routes := h.findRoutesForImage(fullImageName)52+ routes := h.configSvc.FindRoutesByImage(ctx, fullImageName)
54 if len(routes) == 0 {53 if len(routes) == 0 {
55 log.Debug().Str("image", fullImageName).Msg("no routes configured for pushed image")54 log.Debug().Str("image", fullImageName).Msg("no routes configured for pushed image")
56 return nil55 return nil
@@ -74,37 +73,6 @@ func (h *ImagePushedHandler) CanHandle(eventType domain.EventType) bool {
74 return eventType == domain.EventImagePushed73 return eventType == domain.EventImagePushed
75 }74 }
76 75
77-func (h *ImagePushedHandler) findRoutesForImage(imageName string) []domain.Route {
78- var routes []domain.Route
79-
80- registryDomain := h.configSvc.GetRegistryDomain()
81- normalizedImage := normalizeRegistryImage(imageName, registryDomain)
82-
83- configuredRoutes := h.configSvc.GetRoutes(h.ctx)
84- for _, route := range configuredRoutes {
85- normalizedRouteImage := normalizeRegistryImage(route.Image, registryDomain)
86- if strings.EqualFold(normalizedRouteImage, normalizedImage) {
87- routes = append(routes, route)
88- }
89- }
90-
91- return routes
92-}
93-
94-func normalizeRegistryImage(imageName, registryDomain string) string {
95- registryDomain = strings.TrimSuffix(registryDomain, "/")
96- if registryDomain == "" {
97- return imageName
98- }
99-
100- prefix := registryDomain + "/"
101- if strings.HasPrefix(imageName, prefix) {
102- return strings.TrimPrefix(imageName, prefix)
103- }
104-
105- return imageName
106-}
107-
108 // ConfigReloadHandler handles config.reload events.76 // ConfigReloadHandler handles config.reload events.
109 type ConfigReloadHandler struct {77 type ConfigReloadHandler struct {
110 containerSvc in.ContainerService78 containerSvc in.ContainerService
M internal/usecase/container/events_test.go +8 -18
@@ -36,13 +36,10 @@ func TestImagePushedHandler_Handle_DeploysMatchingRoutes(t *testing.T) {
3636
3737 handler := NewImagePushedHandler(testCtx(), containerSvc, configSvc)
3838
39- configSvc.EXPECT().GetRegistryDomain().Return("")
40-
41- // Configure routes that match the pushed image
42- configSvc.EXPECT().GetRoutes(mock.Anything).Return([]domain.Route{
39+ // FindRoutesByImage returns matching routes directly
40+ configSvc.EXPECT().FindRoutesByImage(mock.Anything, "myapp:latest").Return([]domain.Route{
4341 {Domain: "app1.example.com", Image: "myapp:latest"},
4442 {Domain: "app2.example.com", Image: "myapp:latest"},
45- {Domain: "other.example.com", Image: "otherapp:latest"},
4643 })
4744
4845 // Expect Deploy to be called for both matching routes
@@ -74,11 +71,8 @@ func TestImagePushedHandler_Handle_NoMatchingRoutes(t *testing.T) {
7471
7572 handler := NewImagePushedHandler(testCtx(), containerSvc, configSvc)
7673
77- configSvc.EXPECT().GetRegistryDomain().Return("")
78-
79- configSvc.EXPECT().GetRoutes(mock.Anything).Return([]domain.Route{
80- {Domain: "other.example.com", Image: "otherapp:latest"},
81- })
74+ // FindRoutesByImage returns no matching routes
75+ configSvc.EXPECT().FindRoutesByImage(mock.Anything, "myapp:latest").Return([]domain.Route{})
8276
8377 // No Deploy calls expected
8478
@@ -118,10 +112,8 @@ func TestImagePushedHandler_Handle_DefaultTag(t *testing.T) {
118112
119113 handler := NewImagePushedHandler(testCtx(), containerSvc, configSvc)
120114
121- configSvc.EXPECT().GetRegistryDomain().Return("")
122-
123115 // Image name "myapp" with empty tag should become "myapp:latest"
124- configSvc.EXPECT().GetRoutes(mock.Anything).Return([]domain.Route{
116+ configSvc.EXPECT().FindRoutesByImage(mock.Anything, "myapp:latest").Return([]domain.Route{
125117 {Domain: "app.example.com", Image: "myapp:latest"},
126118 })
127119
@@ -148,9 +140,7 @@ func TestImagePushedHandler_Handle_DeployError(t *testing.T) {
148140
149141 handler := NewImagePushedHandler(testCtx(), containerSvc, configSvc)
150142
151- configSvc.EXPECT().GetRegistryDomain().Return("")
152-
153- configSvc.EXPECT().GetRoutes(mock.Anything).Return([]domain.Route{
143+ configSvc.EXPECT().FindRoutesByImage(mock.Anything, "myapp:latest").Return([]domain.Route{
154144 {Domain: "app.example.com", Image: "myapp:latest"},
155145 })
156146
@@ -175,8 +165,8 @@ func TestImagePushedHandler_Handle_StripsRegistryDomain(t *testing.T) {
175165
176166 handler := NewImagePushedHandler(testCtx(), containerSvc, configSvc)
177167
178- configSvc.EXPECT().GetRegistryDomain().Return("registry.example.com")
179- configSvc.EXPECT().GetRoutes(mock.Anything).Return([]domain.Route{
168+ // FindRoutesByImage handles registry domain stripping internally
169+ configSvc.EXPECT().FindRoutesByImage(mock.Anything, "docker.io/library/nginx:latest").Return([]domain.Route{
180170 {Domain: "app.example.com", Image: "registry.example.com/docker.io/library/nginx:latest"},
181171 })
182172
@@ -36,13 +36,10 @@ func TestImagePushedHandler_Handle_DeploysMatchingRoutes(t *testing.T) {
36 36
37 handler := NewImagePushedHandler(testCtx(), containerSvc, configSvc)37 handler := NewImagePushedHandler(testCtx(), containerSvc, configSvc)
38 38
39- configSvc.EXPECT().GetRegistryDomain().Return("")39+ // FindRoutesByImage returns matching routes directly
40-40+ configSvc.EXPECT().FindRoutesByImage(mock.Anything, "myapp:latest").Return([]domain.Route{
41- // Configure routes that match the pushed image
42- configSvc.EXPECT().GetRoutes(mock.Anything).Return([]domain.Route{
43 {Domain: "app1.example.com", Image: "myapp:latest"},41 {Domain: "app1.example.com", Image: "myapp:latest"},
44 {Domain: "app2.example.com", Image: "myapp:latest"},42 {Domain: "app2.example.com", Image: "myapp:latest"},
45- {Domain: "other.example.com", Image: "otherapp:latest"},
46 })43 })
47 44
48 // Expect Deploy to be called for both matching routes45 // Expect Deploy to be called for both matching routes
@@ -74,11 +71,8 @@ func TestImagePushedHandler_Handle_NoMatchingRoutes(t *testing.T) {
74 71
75 handler := NewImagePushedHandler(testCtx(), containerSvc, configSvc)72 handler := NewImagePushedHandler(testCtx(), containerSvc, configSvc)
76 73
77- configSvc.EXPECT().GetRegistryDomain().Return("")74+ // FindRoutesByImage returns no matching routes
78-75+ configSvc.EXPECT().FindRoutesByImage(mock.Anything, "myapp:latest").Return([]domain.Route{})
79- configSvc.EXPECT().GetRoutes(mock.Anything).Return([]domain.Route{
80- {Domain: "other.example.com", Image: "otherapp:latest"},
81- })
82 76
83 // No Deploy calls expected77 // No Deploy calls expected
84 78
@@ -118,10 +112,8 @@ func TestImagePushedHandler_Handle_DefaultTag(t *testing.T) {
118 112
119 handler := NewImagePushedHandler(testCtx(), containerSvc, configSvc)113 handler := NewImagePushedHandler(testCtx(), containerSvc, configSvc)
120 114
121- configSvc.EXPECT().GetRegistryDomain().Return("")
122-
123 // Image name "myapp" with empty tag should become "myapp:latest"115 // Image name "myapp" with empty tag should become "myapp:latest"
124- configSvc.EXPECT().GetRoutes(mock.Anything).Return([]domain.Route{116+ configSvc.EXPECT().FindRoutesByImage(mock.Anything, "myapp:latest").Return([]domain.Route{
125 {Domain: "app.example.com", Image: "myapp:latest"},117 {Domain: "app.example.com", Image: "myapp:latest"},
126 })118 })
127 119
@@ -148,9 +140,7 @@ func TestImagePushedHandler_Handle_DeployError(t *testing.T) {
148 140
149 handler := NewImagePushedHandler(testCtx(), containerSvc, configSvc)141 handler := NewImagePushedHandler(testCtx(), containerSvc, configSvc)
150 142
151- configSvc.EXPECT().GetRegistryDomain().Return("")143+ configSvc.EXPECT().FindRoutesByImage(mock.Anything, "myapp:latest").Return([]domain.Route{
152-
153- configSvc.EXPECT().GetRoutes(mock.Anything).Return([]domain.Route{
154 {Domain: "app.example.com", Image: "myapp:latest"},144 {Domain: "app.example.com", Image: "myapp:latest"},
155 })145 })
156 146
@@ -175,8 +165,8 @@ func TestImagePushedHandler_Handle_StripsRegistryDomain(t *testing.T) {
175 165
176 handler := NewImagePushedHandler(testCtx(), containerSvc, configSvc)166 handler := NewImagePushedHandler(testCtx(), containerSvc, configSvc)
177 167
178- configSvc.EXPECT().GetRegistryDomain().Return("registry.example.com")168+ // FindRoutesByImage handles registry domain stripping internally
179- configSvc.EXPECT().GetRoutes(mock.Anything).Return([]domain.Route{169+ configSvc.EXPECT().FindRoutesByImage(mock.Anything, "docker.io/library/nginx:latest").Return([]domain.Route{
180 {Domain: "app.example.com", Image: "registry.example.com/docker.io/library/nginx:latest"},170 {Domain: "app.example.com", Image: "registry.example.com/docker.io/library/nginx:latest"},
181 })171 })
182 172

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