feat: show attachment network in routes list for debugging
Add Network field to Attachment struct to display each attachment's actual network, allowing verification that attachments are correctly connected to their parent route's network.
- commit
- 494c50da9aa439117464e8196651c0534ef5489e
- author
- brice · · 2026-01-17 14:41 UTC
- parent
- 210d0fa
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
6 files · +26 -3
M internal/adapters/dto/admin_route.go +1 -0
| @@ -24,6 +24,7 @@ type Attachment struct { | |||
| 24 | 24 | Image string `json:"image"` | |
| 25 | 25 | ContainerID string `json:"container_id"` | |
| 26 | 26 | Status string `json:"status"` | |
| 27 | + | Network string `json:"network"` | |
| 27 | 28 | } | |
| 28 | 29 | ||
| 29 | 30 | // RoutesResponse represents a list of routes. | |
| @@ -24,6 +24,7 @@ type Attachment struct { | |||||
| 24 | Image string `json:"image"` | 24 | Image string `json:"image"` | ||
| 25 | ContainerID string `json:"container_id"` | 25 | ContainerID string `json:"container_id"` | ||
| 26 | Status string `json:"status"` | 26 | Status string `json:"status"` | ||
| 27 | + | Network string `json:"network"` | |||
| 27 | } | 28 | } | ||
| 28 | 29 | ||||
| 29 | // RoutesResponse represents a list of routes. | 30 | // RoutesResponse represents a list of routes. | ||
M internal/adapters/in/cli/routes.go +3 -1
| @@ -181,7 +181,9 @@ func runRoutesListRemote(ctx context.Context, client *remote.Client) error { | |||
| 181 | 181 | } | |
| 182 | 182 | attachmentStatus := components.ContainerStatusBadge(attachment.Status) | |
| 183 | 183 | attachmentImage := truncateImage(attachment.Image, imageColWidth) | |
| 184 | - | rows = append(rows, []string{attachmentName, attachmentImage, "-", attachmentStatus, "-"}) | |
| 184 | + | // Show attachment's actual network for debugging/verification | |
| 185 | + | attachmentNetwork := truncateNetwork(attachment.Network, networkColWidth) | |
| 186 | + | rows = append(rows, []string{attachmentName, attachmentImage, attachmentNetwork, attachmentStatus, "-"}) | |
| 185 | 187 | } | |
| 186 | 188 | } | |
| 187 | 189 | ||
| @@ -181,7 +181,9 @@ func runRoutesListRemote(ctx context.Context, client *remote.Client) error { | |||||
| 181 | } | 181 | } | ||
| 182 | attachmentStatus := components.ContainerStatusBadge(attachment.Status) | 182 | attachmentStatus := components.ContainerStatusBadge(attachment.Status) | ||
| 183 | attachmentImage := truncateImage(attachment.Image, imageColWidth) | 183 | attachmentImage := truncateImage(attachment.Image, imageColWidth) | ||
| 184 | - | rows = append(rows, []string{attachmentName, attachmentImage, "-", attachmentStatus, "-"}) | 184 | + | // Show attachment's actual network for debugging/verification |
| 185 | + | attachmentNetwork := truncateNetwork(attachment.Network, networkColWidth) | |||
| 186 | + | rows = append(rows, []string{attachmentName, attachmentImage, attachmentNetwork, attachmentStatus, "-"}) | |||
| 185 | } | 187 | } | ||
| 186 | } | 188 | } | ||
| 187 | 189 | ||||
M internal/adapters/in/http/admin/handler.go +1 -0
| @@ -47,6 +47,7 @@ func toAttachmentResponse(a domain.Attachment) dto.Attachment { | |||
| 47 | 47 | Image: a.Image, | |
| 48 | 48 | ContainerID: a.ContainerID, | |
| 49 | 49 | Status: a.Status, | |
| 50 | + | Network: a.Network, | |
| 50 | 51 | } | |
| 51 | 52 | } | |
| 52 | 53 | ||
| @@ -47,6 +47,7 @@ func toAttachmentResponse(a domain.Attachment) dto.Attachment { | |||||
| 47 | Image: a.Image, | 47 | Image: a.Image, | ||
| 48 | ContainerID: a.ContainerID, | 48 | ContainerID: a.ContainerID, | ||
| 49 | Status: a.Status, | 49 | Status: a.Status, | ||
| 50 | + | Network: a.Network, | |||
| 50 | } | 51 | } | ||
| 51 | } | 52 | } | ||
| 52 | 53 | ||||
M internal/domain/container.go +1 -0
| @@ -27,6 +27,7 @@ type Attachment struct { | |||
| 27 | 27 | Image string | |
| 28 | 28 | ContainerID string | |
| 29 | 29 | Status string | |
| 30 | + | Network string | |
| 30 | 31 | } | |
| 31 | 32 | ||
| 32 | 33 | // RouteInfo combines route configuration with runtime state. | |
| @@ -27,6 +27,7 @@ type Attachment struct { | |||||
| 27 | Image string | 27 | Image string | ||
| 28 | ContainerID string | 28 | ContainerID string | ||
| 29 | Status string | 29 | Status string | ||
| 30 | + | Network string | |||
| 30 | } | 31 | } | ||
| 31 | 32 | ||||
| 32 | // RouteInfo combines route configuration with runtime state. | 33 | // RouteInfo combines route configuration with runtime state. | ||
M internal/usecase/container/routes_test.go +6 -0
| @@ -25,6 +25,9 @@ func TestService_ListRoutesWithDetails(t *testing.T) { | |||
| 25 | 25 | } | |
| 26 | 26 | ||
| 27 | 27 | runtime.EXPECT().GetContainerNetwork(mock.Anything, "container-1").Return("gordon-app-example-com", nil) | |
| 28 | + | // getAllAttachments calls GetContainerNetwork for each attachment | |
| 29 | + | runtime.EXPECT().GetContainerNetwork(mock.Anything, "attach-1").Return("gordon-app-example-com", nil) | |
| 30 | + | runtime.EXPECT().GetContainerNetwork(mock.Anything, "attach-2").Return("gordon-other-example-com", nil) | |
| 28 | 31 | runtime.EXPECT().ListContainers(mock.Anything, true).Return([]*domain.Container{ | |
| 29 | 32 | { | |
| 30 | 33 | ID: "attach-1", | |
| @@ -71,6 +74,7 @@ func TestService_ListRoutesWithDetails(t *testing.T) { | |||
| 71 | 74 | assert.Equal(t, "postgres", results[0].Attachments[0].Name) | |
| 72 | 75 | assert.Equal(t, "postgres:15", results[0].Attachments[0].Image) | |
| 73 | 76 | assert.Equal(t, "attach-1", results[0].Attachments[0].ContainerID) | |
| 77 | + | assert.Equal(t, "gordon-app-example-com", results[0].Attachments[0].Network) | |
| 74 | 78 | } | |
| 75 | 79 | } | |
| 76 | 80 | ||
| @@ -136,6 +140,7 @@ func TestService_ListAttachments(t *testing.T) { | |||
| 136 | 140 | }, | |
| 137 | 141 | }, | |
| 138 | 142 | }, nil) | |
| 143 | + | runtime.EXPECT().GetContainerNetwork(mock.Anything, "attach-1").Return("gordon-app-example-com", nil) | |
| 139 | 144 | ||
| 140 | 145 | attachments := svc.ListAttachments(ctx, "app.example.com") | |
| 141 | 146 | ||
| @@ -143,6 +148,7 @@ func TestService_ListAttachments(t *testing.T) { | |||
| 143 | 148 | assert.Equal(t, "postgres", attachments[0].Name) | |
| 144 | 149 | assert.Equal(t, "postgres:15", attachments[0].Image) | |
| 145 | 150 | assert.Equal(t, "attach-1", attachments[0].ContainerID) | |
| 151 | + | assert.Equal(t, "gordon-app-example-com", attachments[0].Network) | |
| 146 | 152 | } | |
| 147 | 153 | ||
| 148 | 154 | func TestService_ListNetworks(t *testing.T) { | |
| @@ -25,6 +25,9 @@ func TestService_ListRoutesWithDetails(t *testing.T) { | |||||
| 25 | } | 25 | } | ||
| 26 | 26 | ||||
| 27 | runtime.EXPECT().GetContainerNetwork(mock.Anything, "container-1").Return("gordon-app-example-com", nil) | 27 | runtime.EXPECT().GetContainerNetwork(mock.Anything, "container-1").Return("gordon-app-example-com", nil) | ||
| 28 | + | // getAllAttachments calls GetContainerNetwork for each attachment | |||
| 29 | + | runtime.EXPECT().GetContainerNetwork(mock.Anything, "attach-1").Return("gordon-app-example-com", nil) | |||
| 30 | + | runtime.EXPECT().GetContainerNetwork(mock.Anything, "attach-2").Return("gordon-other-example-com", nil) | |||
| 28 | runtime.EXPECT().ListContainers(mock.Anything, true).Return([]*domain.Container{ | 31 | runtime.EXPECT().ListContainers(mock.Anything, true).Return([]*domain.Container{ | ||
| 29 | { | 32 | { | ||
| 30 | ID: "attach-1", | 33 | ID: "attach-1", | ||
| @@ -71,6 +74,7 @@ func TestService_ListRoutesWithDetails(t *testing.T) { | |||||
| 71 | assert.Equal(t, "postgres", results[0].Attachments[0].Name) | 74 | assert.Equal(t, "postgres", results[0].Attachments[0].Name) | ||
| 72 | assert.Equal(t, "postgres:15", results[0].Attachments[0].Image) | 75 | assert.Equal(t, "postgres:15", results[0].Attachments[0].Image) | ||
| 73 | assert.Equal(t, "attach-1", results[0].Attachments[0].ContainerID) | 76 | assert.Equal(t, "attach-1", results[0].Attachments[0].ContainerID) | ||
| 77 | + | assert.Equal(t, "gordon-app-example-com", results[0].Attachments[0].Network) | |||
| 74 | } | 78 | } | ||
| 75 | } | 79 | } | ||
| 76 | 80 | ||||
| @@ -136,6 +140,7 @@ func TestService_ListAttachments(t *testing.T) { | |||||
| 136 | }, | 140 | }, | ||
| 137 | }, | 141 | }, | ||
| 138 | }, nil) | 142 | }, nil) | ||
| 143 | + | runtime.EXPECT().GetContainerNetwork(mock.Anything, "attach-1").Return("gordon-app-example-com", nil) | |||
| 139 | 144 | ||||
| 140 | attachments := svc.ListAttachments(ctx, "app.example.com") | 145 | attachments := svc.ListAttachments(ctx, "app.example.com") | ||
| 141 | 146 | ||||
| @@ -143,6 +148,7 @@ func TestService_ListAttachments(t *testing.T) { | |||||
| 143 | assert.Equal(t, "postgres", attachments[0].Name) | 148 | assert.Equal(t, "postgres", attachments[0].Name) | ||
| 144 | assert.Equal(t, "postgres:15", attachments[0].Image) | 149 | assert.Equal(t, "postgres:15", attachments[0].Image) | ||
| 145 | assert.Equal(t, "attach-1", attachments[0].ContainerID) | 150 | assert.Equal(t, "attach-1", attachments[0].ContainerID) | ||
| 151 | + | assert.Equal(t, "gordon-app-example-com", attachments[0].Network) | |||
| 146 | } | 152 | } | ||
| 147 | 153 | ||||
| 148 | func TestService_ListNetworks(t *testing.T) { | 154 | func TestService_ListNetworks(t *testing.T) { | ||
M internal/usecase/container/service.go +14 -2
| @@ -958,7 +958,7 @@ func (s *Service) getAttachmentsForDomain(ctx context.Context, domainName string | |||
| 958 | 958 | return nil | |
| 959 | 959 | } | |
| 960 | 960 | ||
| 961 | - | return s.filterAttachments(containers, domainName) | |
| 961 | + | return s.filterAttachments(ctx, containers, domainName) | |
| 962 | 962 | } | |
| 963 | 963 | ||
| 964 | 964 | // getAllAttachments fetches all containers once and returns attachments grouped by domain. | |
| @@ -987,11 +987,17 @@ func (s *Service) getAllAttachments(ctx context.Context) map[string][]domain.Att | |||
| 987 | 987 | image = labelImage | |
| 988 | 988 | serviceName = extractServiceName(labelImage) | |
| 989 | 989 | } | |
| 990 | + | // Get the container's network for debugging/verification | |
| 991 | + | network := "" | |
| 992 | + | if networkName, err := s.runtime.GetContainerNetwork(ctx, container.ID); err == nil { | |
| 993 | + | network = networkName | |
| 994 | + | } | |
| 990 | 995 | attachment := domain.Attachment{ | |
| 991 | 996 | Name: serviceName, | |
| 992 | 997 | Image: image, | |
| 993 | 998 | ContainerID: container.ID, | |
| 994 | 999 | Status: container.Status, | |
| 1000 | + | Network: network, | |
| 995 | 1001 | } | |
| 996 | 1002 | result[ownerDomain] = append(result[ownerDomain], attachment) | |
| 997 | 1003 | } | |
| @@ -1000,7 +1006,7 @@ func (s *Service) getAllAttachments(ctx context.Context) map[string][]domain.Att | |||
| 1000 | 1006 | } | |
| 1001 | 1007 | ||
| 1002 | 1008 | // filterAttachments extracts attachments for a specific domain from a container list. | |
| 1003 | - | func (s *Service) filterAttachments(containers []*domain.Container, domainName string) []domain.Attachment { | |
| 1009 | + | func (s *Service) filterAttachments(ctx context.Context, containers []*domain.Container, domainName string) []domain.Attachment { | |
| 1004 | 1010 | attachments := make([]domain.Attachment, 0) | |
| 1005 | 1011 | for _, container := range containers { | |
| 1006 | 1012 | if container.Labels == nil { | |
| @@ -1018,11 +1024,17 @@ func (s *Service) filterAttachments(containers []*domain.Container, domainName s | |||
| 1018 | 1024 | image = labelImage | |
| 1019 | 1025 | serviceName = extractServiceName(labelImage) | |
| 1020 | 1026 | } | |
| 1027 | + | // Get the container's network for debugging/verification | |
| 1028 | + | network := "" | |
| 1029 | + | if networkName, err := s.runtime.GetContainerNetwork(ctx, container.ID); err == nil { | |
| 1030 | + | network = networkName | |
| 1031 | + | } | |
| 1021 | 1032 | attachment := domain.Attachment{ | |
| 1022 | 1033 | Name: serviceName, | |
| 1023 | 1034 | Image: image, | |
| 1024 | 1035 | ContainerID: container.ID, | |
| 1025 | 1036 | Status: container.Status, | |
| 1037 | + | Network: network, | |
| 1026 | 1038 | } | |
| 1027 | 1039 | attachments = append(attachments, attachment) | |
| 1028 | 1040 | } | |
| @@ -958,7 +958,7 @@ func (s *Service) getAttachmentsForDomain(ctx context.Context, domainName string | |||||
| 958 | return nil | 958 | return nil | ||
| 959 | } | 959 | } | ||
| 960 | 960 | ||||
| 961 | - | return s.filterAttachments(containers, domainName) | 961 | + | return s.filterAttachments(ctx, containers, domainName) |
| 962 | } | 962 | } | ||
| 963 | 963 | ||||
| 964 | // getAllAttachments fetches all containers once and returns attachments grouped by domain. | 964 | // getAllAttachments fetches all containers once and returns attachments grouped by domain. | ||
| @@ -987,11 +987,17 @@ func (s *Service) getAllAttachments(ctx context.Context) map[string][]domain.Att | |||||
| 987 | image = labelImage | 987 | image = labelImage | ||
| 988 | serviceName = extractServiceName(labelImage) | 988 | serviceName = extractServiceName(labelImage) | ||
| 989 | } | 989 | } | ||
| 990 | + | // Get the container's network for debugging/verification | |||
| 991 | + | network := "" | |||
| 992 | + | if networkName, err := s.runtime.GetContainerNetwork(ctx, container.ID); err == nil { | |||
| 993 | + | network = networkName | |||
| 994 | + | } | |||
| 990 | attachment := domain.Attachment{ | 995 | attachment := domain.Attachment{ | ||
| 991 | Name: serviceName, | 996 | Name: serviceName, | ||
| 992 | Image: image, | 997 | Image: image, | ||
| 993 | ContainerID: container.ID, | 998 | ContainerID: container.ID, | ||
| 994 | Status: container.Status, | 999 | Status: container.Status, | ||
| 1000 | + | Network: network, | |||
| 995 | } | 1001 | } | ||
| 996 | result[ownerDomain] = append(result[ownerDomain], attachment) | 1002 | result[ownerDomain] = append(result[ownerDomain], attachment) | ||
| 997 | } | 1003 | } | ||
| @@ -1000,7 +1006,7 @@ func (s *Service) getAllAttachments(ctx context.Context) map[string][]domain.Att | |||||
| 1000 | } | 1006 | } | ||
| 1001 | 1007 | ||||
| 1002 | // filterAttachments extracts attachments for a specific domain from a container list. | 1008 | // filterAttachments extracts attachments for a specific domain from a container list. | ||
| 1003 | - | func (s *Service) filterAttachments(containers []*domain.Container, domainName string) []domain.Attachment { | 1009 | + | func (s *Service) filterAttachments(ctx context.Context, containers []*domain.Container, domainName string) []domain.Attachment { |
| 1004 | attachments := make([]domain.Attachment, 0) | 1010 | attachments := make([]domain.Attachment, 0) | ||
| 1005 | for _, container := range containers { | 1011 | for _, container := range containers { | ||
| 1006 | if container.Labels == nil { | 1012 | if container.Labels == nil { | ||
| @@ -1018,11 +1024,17 @@ func (s *Service) filterAttachments(containers []*domain.Container, domainName s | |||||
| 1018 | image = labelImage | 1024 | image = labelImage | ||
| 1019 | serviceName = extractServiceName(labelImage) | 1025 | serviceName = extractServiceName(labelImage) | ||
| 1020 | } | 1026 | } | ||
| 1027 | + | // Get the container's network for debugging/verification | |||
| 1028 | + | network := "" | |||
| 1029 | + | if networkName, err := s.runtime.GetContainerNetwork(ctx, container.ID); err == nil { | |||
| 1030 | + | network = networkName | |||
| 1031 | + | } | |||
| 1021 | attachment := domain.Attachment{ | 1032 | attachment := domain.Attachment{ | ||
| 1022 | Name: serviceName, | 1033 | Name: serviceName, | ||
| 1023 | Image: image, | 1034 | Image: image, | ||
| 1024 | ContainerID: container.ID, | 1035 | ContainerID: container.ID, | ||
| 1025 | Status: container.Status, | 1036 | Status: container.Status, | ||
| 1037 | + | Network: network, | |||
| 1026 | } | 1038 | } | ||
| 1027 | attachments = append(attachments, attachment) | 1039 | attachments = append(attachments, attachment) | ||
| 1028 | } | 1040 | } | ||