refactor(container): use FindRoutesByImage in ImagePushedHandler
- commit
- 2fed5bda697c762ce08b9c6ccf4b0446a250bc92
- author
- bnema · · 2026-02-09 20:22 UTC
- parent
- 9c32c7d
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +9 -51
M internal/usecase/container/events.go +1 -33
| @@ -3,7 +3,6 @@ package container | |||
| 3 | 3 | import ( | |
| 4 | 4 | "context" | |
| 5 | 5 | "fmt" | |
| 6 | - | "strings" | |
| 7 | 6 | ||
| 8 | 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 | 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 | 53 | if len(routes) == 0 { | |
| 55 | 54 | log.Debug().Str("image", fullImageName).Msg("no routes configured for pushed image") | |
| 56 | 55 | return nil | |
| @@ -74,37 +73,6 @@ func (h *ImagePushedHandler) CanHandle(eventType domain.EventType) bool { | |||
| 74 | 73 | 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 | 76 | // ConfigReloadHandler handles config.reload events. | |
| 109 | 77 | type ConfigReloadHandler struct { | |
| 110 | 78 | 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 nil | 55 | return nil | ||
| @@ -74,37 +73,6 @@ func (h *ImagePushedHandler) CanHandle(eventType domain.EventType) bool { | |||||
| 74 | return eventType == domain.EventImagePushed | 73 | 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.ContainerService | 78 | containerSvc in.ContainerService | ||
M internal/usecase/container/events_test.go +8 -18
| @@ -36,13 +36,10 @@ func TestImagePushedHandler_Handle_DeploysMatchingRoutes(t *testing.T) { | |||
| 36 | 36 | ||
| 37 | 37 | handler := NewImagePushedHandler(testCtx(), containerSvc, configSvc) | |
| 38 | 38 | ||
| 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{ | |
| 43 | 41 | {Domain: "app1.example.com", Image: "myapp:latest"}, | |
| 44 | 42 | {Domain: "app2.example.com", Image: "myapp:latest"}, | |
| 45 | - | {Domain: "other.example.com", Image: "otherapp:latest"}, | |
| 46 | 43 | }) | |
| 47 | 44 | ||
| 48 | 45 | // Expect Deploy to be called for both matching routes | |
| @@ -74,11 +71,8 @@ func TestImagePushedHandler_Handle_NoMatchingRoutes(t *testing.T) { | |||
| 74 | 71 | ||
| 75 | 72 | handler := NewImagePushedHandler(testCtx(), containerSvc, configSvc) | |
| 76 | 73 | ||
| 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{}) | |
| 82 | 76 | ||
| 83 | 77 | // No Deploy calls expected | |
| 84 | 78 | ||
| @@ -118,10 +112,8 @@ func TestImagePushedHandler_Handle_DefaultTag(t *testing.T) { | |||
| 118 | 112 | ||
| 119 | 113 | handler := NewImagePushedHandler(testCtx(), containerSvc, configSvc) | |
| 120 | 114 | ||
| 121 | - | configSvc.EXPECT().GetRegistryDomain().Return("") | |
| 122 | - | ||
| 123 | 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 | 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 | 141 | handler := NewImagePushedHandler(testCtx(), containerSvc, configSvc) | |
| 150 | 142 | ||
| 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{ | |
| 154 | 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 | 166 | handler := NewImagePushedHandler(testCtx(), containerSvc, configSvc) | |
| 177 | 167 | ||
| 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{ | |
| 180 | 170 | {Domain: "app.example.com", Image: "registry.example.com/docker.io/library/nginx:latest"}, | |
| 181 | 171 | }) | |
| 182 | 172 | ||
| @@ -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 routes | 45 | // 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 expected | 77 | // 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 | ||||