feat: Enhance Gordon container auto-detection in proxy routing
- Add `detectGordonContainer()` method to automatically find Gordon container - Support multiple detection strategies: hostname, container processes, and container names - Improve container routing by dynamically identifying the correct container - Add debug logging for container detection process - Remove hardcoded "gordon-server" container ID - Enhance flexibility for different container runtime environments
- commit
- 6b4468ef1be619136bfc247c46a10ea5f22d9e08
- author
- bamen · · 2025-03-10 14:00 UTC
- parent
- de0c4ec
- signature
- unsigned
2 files · +93 -5
M internal/proxy/routes.go +1 -1
| @@ -152,7 +152,7 @@ func (p *Proxy) configureRoutes() { | |||
| 152 | 152 | if err != nil { | |
| 153 | 153 | // Connection failed, check if container exists but has a new IP | |
| 154 | 154 | containerID := route.ContainerID | |
| 155 | - | if containerID != "" && containerID != "gordon-server" { | |
| 155 | + | if containerID != "" { | |
| 156 | 156 | // Try to get container info and check if it has a different IP | |
| 157 | 157 | containerInfo, err := docker.GetContainerInfo(containerID) | |
| 158 | 158 | if err == nil && containerInfo.NetworkSettings != nil { | |
| @@ -152,7 +152,7 @@ func (p *Proxy) configureRoutes() { | |||||
| 152 | if err != nil { | 152 | if err != nil { | ||
| 153 | // Connection failed, check if container exists but has a new IP | 153 | // Connection failed, check if container exists but has a new IP | ||
| 154 | containerID := route.ContainerID | 154 | containerID := route.ContainerID | ||
| 155 | - | if containerID != "" && containerID != "gordon-server" { | 155 | + | if containerID != "" { |
| 156 | // Try to get container info and check if it has a different IP | 156 | // Try to get container info and check if it has a different IP | ||
| 157 | containerInfo, err := docker.GetContainerInfo(containerID) | 157 | containerInfo, err := docker.GetContainerInfo(containerID) | ||
| 158 | if err == nil && containerInfo.NetworkSettings != nil { | 158 | if err == nil && containerInfo.NetworkSettings != nil { | ||
M internal/proxy/server.go +92 -4
| @@ -6,6 +6,7 @@ import ( | |||
| 6 | 6 | "fmt" | |
| 7 | 7 | "net/http" | |
| 8 | 8 | "os" | |
| 9 | + | "strings" | |
| 9 | 10 | "time" | |
| 10 | 11 | ||
| 11 | 12 | "github.com/bnema/gordon/pkg/docker" | |
| @@ -95,17 +96,21 @@ func (p *Proxy) Start() error { | |||
| 95 | 96 | p.mu.Lock() | |
| 96 | 97 | // Only add if it doesn't already exist | |
| 97 | 98 | if _, exists := p.routes[adminDomain]; !exists { | |
| 99 | + | // Auto-detect the Gordon container name | |
| 100 | + | containerName := p.detectGordonContainer() | |
| 101 | + | ||
| 98 | 102 | p.routes[adminDomain] = &ProxyRouteInfo{ | |
| 99 | 103 | Domain: adminDomain, | |
| 100 | 104 | ContainerIP: containerIP, | |
| 101 | 105 | ContainerPort: p.app.GetConfig().Http.Port, | |
| 102 | - | ContainerID: "gordon-server", | |
| 103 | - | Protocol: "http", // Gordon server uses HTTP internally | |
| 106 | + | ContainerID: containerName, // Use auto-detected container name | |
| 107 | + | Protocol: "http", // Gordon server uses HTTP internally | |
| 104 | 108 | Path: "/", | |
| 105 | 109 | Active: true, | |
| 106 | 110 | } | |
| 107 | 111 | log.Info("Added special route for admin domain", | |
| 108 | 112 | "domain", adminDomain, | |
| 113 | + | "container", containerName, | |
| 109 | 114 | "target", fmt.Sprintf("http://%s:%s", containerIP, p.app.GetConfig().Http.Port)) | |
| 110 | 115 | } | |
| 111 | 116 | ||
| @@ -113,12 +118,15 @@ func (p *Proxy) Start() error { | |||
| 113 | 118 | rootDomain := p.app.GetConfig().Http.Domain | |
| 114 | 119 | if rootDomain != "" && rootDomain != adminDomain { | |
| 115 | 120 | if _, exists := p.routes[rootDomain]; !exists { | |
| 121 | + | // Use the same auto-detected container name | |
| 122 | + | containerName := p.detectGordonContainer() | |
| 123 | + | ||
| 116 | 124 | p.routes[rootDomain] = &ProxyRouteInfo{ | |
| 117 | 125 | Domain: rootDomain, | |
| 118 | 126 | ContainerIP: containerIP, | |
| 119 | 127 | ContainerPort: p.app.GetConfig().Http.Port, | |
| 120 | - | ContainerID: "gordon-server", | |
| 121 | - | Protocol: "http", // Gordon server uses HTTP internally | |
| 128 | + | ContainerID: containerName, // Use auto-detected container name | |
| 129 | + | Protocol: "http", // Gordon server uses HTTP internally | |
| 122 | 130 | Path: "/", | |
| 123 | 131 | Active: true, | |
| 124 | 132 | } | |
| @@ -389,3 +397,83 @@ func (p *Proxy) checkExternalPortAccess() { | |||
| 389 | 397 | log.Info("External HTTPS port 443 is accessible") | |
| 390 | 398 | } | |
| 391 | 399 | } | |
| 400 | + | ||
| 401 | + | // detectGordonContainer attempts to find the Gordon container automatically | |
| 402 | + | func (p *Proxy) detectGordonContainer() string { | |
| 403 | + | // Check if we're running inside a container | |
| 404 | + | if docker.IsRunningInContainer() { | |
| 405 | + | log.Debug("Detected we're running in a container") | |
| 406 | + | ||
| 407 | + | // Get our hostname which should be the container ID in Docker/Podman | |
| 408 | + | hostname := os.Getenv("HOSTNAME") | |
| 409 | + | if hostname != "" { | |
| 410 | + | // Check if there's a container with this hostname | |
| 411 | + | containers, err := docker.ListRunningContainers() | |
| 412 | + | if err == nil { | |
| 413 | + | for _, container := range containers { | |
| 414 | + | // Check if this container is us by comparing hostname with container ID | |
| 415 | + | if strings.HasPrefix(hostname, container.ID) { | |
| 416 | + | // This is our container | |
| 417 | + | containerName := strings.TrimLeft(container.Names[0], "/") | |
| 418 | + | log.Debug("Auto-detected Gordon container name from hostname", "container_name", containerName) | |
| 419 | + | return containerName | |
| 420 | + | } | |
| 421 | + | } | |
| 422 | + | } else { | |
| 423 | + | log.Debug("Could not list containers when checking hostname", "error", err) | |
| 424 | + | } | |
| 425 | + | } | |
| 426 | + | } | |
| 427 | + | ||
| 428 | + | // Try to find Gordon by examining container processes | |
| 429 | + | containers, err := docker.ListRunningContainers() | |
| 430 | + | if err == nil { | |
| 431 | + | for _, container := range containers { | |
| 432 | + | containerID := container.ID | |
| 433 | + | ||
| 434 | + | // Check container processes for Gordon | |
| 435 | + | containerLogs, err := docker.GetContainerLogs(containerID) | |
| 436 | + | if err == nil && (strings.Contains(containerLogs, "/gordon") || strings.Contains(containerLogs, "serve")) { | |
| 437 | + | containerName := strings.TrimLeft(container.Names[0], "/") | |
| 438 | + | log.Debug("Auto-detected Gordon container by process", "container_name", containerName) | |
| 439 | + | return containerName | |
| 440 | + | } else if err != nil { | |
| 441 | + | log.Debug("Could not get container logs", "container_id", containerID, "error", err) | |
| 442 | + | } | |
| 443 | + | ||
| 444 | + | // Check container command for Gordon | |
| 445 | + | if len(container.Command) > 0 && strings.Contains(container.Command, "gordon") { | |
| 446 | + | containerName := strings.TrimLeft(container.Names[0], "/") | |
| 447 | + | log.Debug("Auto-detected Gordon container by command", "container_name", containerName) | |
| 448 | + | return containerName | |
| 449 | + | } | |
| 450 | + | } | |
| 451 | + | } else { | |
| 452 | + | log.Debug("Could not list containers when checking processes", "error", err) | |
| 453 | + | } | |
| 454 | + | ||
| 455 | + | // If we couldn't detect from processes, try to find a container with gordon in the name or image | |
| 456 | + | if err == nil { | |
| 457 | + | for _, container := range containers { | |
| 458 | + | // Check container names for "gordon" | |
| 459 | + | for _, name := range container.Names { | |
| 460 | + | if strings.Contains(strings.ToLower(name), "gordon") { | |
| 461 | + | containerName := strings.TrimLeft(name, "/") | |
| 462 | + | log.Debug("Auto-detected Gordon container by name", "container_name", containerName) | |
| 463 | + | return containerName | |
| 464 | + | } | |
| 465 | + | } | |
| 466 | + | ||
| 467 | + | // Check image name for "gordon" | |
| 468 | + | if strings.Contains(strings.ToLower(container.Image), "gordon") { | |
| 469 | + | containerName := strings.TrimLeft(container.Names[0], "/") | |
| 470 | + | log.Debug("Auto-detected Gordon container by image", "container_name", containerName, "image", container.Image) | |
| 471 | + | return containerName | |
| 472 | + | } | |
| 473 | + | } | |
| 474 | + | } | |
| 475 | + | ||
| 476 | + | // If all else fails, default to "gordon" | |
| 477 | + | log.Debug("Could not auto-detect Gordon container, using default name", "container_name", "gordon") | |
| 478 | + | return "gordon" | |
| 479 | + | } | |
| @@ -6,6 +6,7 @@ import ( | |||||
| 6 | "fmt" | 6 | "fmt" | ||
| 7 | "net/http" | 7 | "net/http" | ||
| 8 | "os" | 8 | "os" | ||
| 9 | + | "strings" | |||
| 9 | "time" | 10 | "time" | ||
| 10 | 11 | ||||
| 11 | "github.com/bnema/gordon/pkg/docker" | 12 | "github.com/bnema/gordon/pkg/docker" | ||
| @@ -95,17 +96,21 @@ func (p *Proxy) Start() error { | |||||
| 95 | p.mu.Lock() | 96 | p.mu.Lock() | ||
| 96 | // Only add if it doesn't already exist | 97 | // Only add if it doesn't already exist | ||
| 97 | if _, exists := p.routes[adminDomain]; !exists { | 98 | if _, exists := p.routes[adminDomain]; !exists { | ||
| 99 | + | // Auto-detect the Gordon container name | |||
| 100 | + | containerName := p.detectGordonContainer() | |||
| 101 | + | ||||
| 98 | p.routes[adminDomain] = &ProxyRouteInfo{ | 102 | p.routes[adminDomain] = &ProxyRouteInfo{ | ||
| 99 | Domain: adminDomain, | 103 | Domain: adminDomain, | ||
| 100 | ContainerIP: containerIP, | 104 | ContainerIP: containerIP, | ||
| 101 | ContainerPort: p.app.GetConfig().Http.Port, | 105 | ContainerPort: p.app.GetConfig().Http.Port, | ||
| 102 | - | ContainerID: "gordon-server", | 106 | + | ContainerID: containerName, // Use auto-detected container name |
| 103 | - | Protocol: "http", // Gordon server uses HTTP internally | 107 | + | Protocol: "http", // Gordon server uses HTTP internally |
| 104 | Path: "/", | 108 | Path: "/", | ||
| 105 | Active: true, | 109 | Active: true, | ||
| 106 | } | 110 | } | ||
| 107 | log.Info("Added special route for admin domain", | 111 | log.Info("Added special route for admin domain", | ||
| 108 | "domain", adminDomain, | 112 | "domain", adminDomain, | ||
| 113 | + | "container", containerName, | |||
| 109 | "target", fmt.Sprintf("http://%s:%s", containerIP, p.app.GetConfig().Http.Port)) | 114 | "target", fmt.Sprintf("http://%s:%s", containerIP, p.app.GetConfig().Http.Port)) | ||
| 110 | } | 115 | } | ||
| 111 | 116 | ||||
| @@ -113,12 +118,15 @@ func (p *Proxy) Start() error { | |||||
| 113 | rootDomain := p.app.GetConfig().Http.Domain | 118 | rootDomain := p.app.GetConfig().Http.Domain | ||
| 114 | if rootDomain != "" && rootDomain != adminDomain { | 119 | if rootDomain != "" && rootDomain != adminDomain { | ||
| 115 | if _, exists := p.routes[rootDomain]; !exists { | 120 | if _, exists := p.routes[rootDomain]; !exists { | ||
| 121 | + | // Use the same auto-detected container name | |||
| 122 | + | containerName := p.detectGordonContainer() | |||
| 123 | + | ||||
| 116 | p.routes[rootDomain] = &ProxyRouteInfo{ | 124 | p.routes[rootDomain] = &ProxyRouteInfo{ | ||
| 117 | Domain: rootDomain, | 125 | Domain: rootDomain, | ||
| 118 | ContainerIP: containerIP, | 126 | ContainerIP: containerIP, | ||
| 119 | ContainerPort: p.app.GetConfig().Http.Port, | 127 | ContainerPort: p.app.GetConfig().Http.Port, | ||
| 120 | - | ContainerID: "gordon-server", | 128 | + | ContainerID: containerName, // Use auto-detected container name |
| 121 | - | Protocol: "http", // Gordon server uses HTTP internally | 129 | + | Protocol: "http", // Gordon server uses HTTP internally |
| 122 | Path: "/", | 130 | Path: "/", | ||
| 123 | Active: true, | 131 | Active: true, | ||
| 124 | } | 132 | } | ||
| @@ -389,3 +397,83 @@ func (p *Proxy) checkExternalPortAccess() { | |||||
| 389 | log.Info("External HTTPS port 443 is accessible") | 397 | log.Info("External HTTPS port 443 is accessible") | ||
| 390 | } | 398 | } | ||
| 391 | } | 399 | } | ||
| 400 | + | ||||
| 401 | + | // detectGordonContainer attempts to find the Gordon container automatically | |||
| 402 | + | func (p *Proxy) detectGordonContainer() string { | |||
| 403 | + | // Check if we're running inside a container | |||
| 404 | + | if docker.IsRunningInContainer() { | |||
| 405 | + | log.Debug("Detected we're running in a container") | |||
| 406 | + | ||||
| 407 | + | // Get our hostname which should be the container ID in Docker/Podman | |||
| 408 | + | hostname := os.Getenv("HOSTNAME") | |||
| 409 | + | if hostname != "" { | |||
| 410 | + | // Check if there's a container with this hostname | |||
| 411 | + | containers, err := docker.ListRunningContainers() | |||
| 412 | + | if err == nil { | |||
| 413 | + | for _, container := range containers { | |||
| 414 | + | // Check if this container is us by comparing hostname with container ID | |||
| 415 | + | if strings.HasPrefix(hostname, container.ID) { | |||
| 416 | + | // This is our container | |||
| 417 | + | containerName := strings.TrimLeft(container.Names[0], "/") | |||
| 418 | + | log.Debug("Auto-detected Gordon container name from hostname", "container_name", containerName) | |||
| 419 | + | return containerName | |||
| 420 | + | } | |||
| 421 | + | } | |||
| 422 | + | } else { | |||
| 423 | + | log.Debug("Could not list containers when checking hostname", "error", err) | |||
| 424 | + | } | |||
| 425 | + | } | |||
| 426 | + | } | |||
| 427 | + | ||||
| 428 | + | // Try to find Gordon by examining container processes | |||
| 429 | + | containers, err := docker.ListRunningContainers() | |||
| 430 | + | if err == nil { | |||
| 431 | + | for _, container := range containers { | |||
| 432 | + | containerID := container.ID | |||
| 433 | + | ||||
| 434 | + | // Check container processes for Gordon | |||
| 435 | + | containerLogs, err := docker.GetContainerLogs(containerID) | |||
| 436 | + | if err == nil && (strings.Contains(containerLogs, "/gordon") || strings.Contains(containerLogs, "serve")) { | |||
| 437 | + | containerName := strings.TrimLeft(container.Names[0], "/") | |||
| 438 | + | log.Debug("Auto-detected Gordon container by process", "container_name", containerName) | |||
| 439 | + | return containerName | |||
| 440 | + | } else if err != nil { | |||
| 441 | + | log.Debug("Could not get container logs", "container_id", containerID, "error", err) | |||
| 442 | + | } | |||
| 443 | + | ||||
| 444 | + | // Check container command for Gordon | |||
| 445 | + | if len(container.Command) > 0 && strings.Contains(container.Command, "gordon") { | |||
| 446 | + | containerName := strings.TrimLeft(container.Names[0], "/") | |||
| 447 | + | log.Debug("Auto-detected Gordon container by command", "container_name", containerName) | |||
| 448 | + | return containerName | |||
| 449 | + | } | |||
| 450 | + | } | |||
| 451 | + | } else { | |||
| 452 | + | log.Debug("Could not list containers when checking processes", "error", err) | |||
| 453 | + | } | |||
| 454 | + | ||||
| 455 | + | // If we couldn't detect from processes, try to find a container with gordon in the name or image | |||
| 456 | + | if err == nil { | |||
| 457 | + | for _, container := range containers { | |||
| 458 | + | // Check container names for "gordon" | |||
| 459 | + | for _, name := range container.Names { | |||
| 460 | + | if strings.Contains(strings.ToLower(name), "gordon") { | |||
| 461 | + | containerName := strings.TrimLeft(name, "/") | |||
| 462 | + | log.Debug("Auto-detected Gordon container by name", "container_name", containerName) | |||
| 463 | + | return containerName | |||
| 464 | + | } | |||
| 465 | + | } | |||
| 466 | + | ||||
| 467 | + | // Check image name for "gordon" | |||
| 468 | + | if strings.Contains(strings.ToLower(container.Image), "gordon") { | |||
| 469 | + | containerName := strings.TrimLeft(container.Names[0], "/") | |||
| 470 | + | log.Debug("Auto-detected Gordon container by image", "container_name", containerName, "image", container.Image) | |||
| 471 | + | return containerName | |||
| 472 | + | } | |||
| 473 | + | } | |||
| 474 | + | } | |||
| 475 | + | ||||
| 476 | + | // If all else fails, default to "gordon" | |||
| 477 | + | log.Debug("Could not auto-detect Gordon container, using default name", "container_name", "gordon") | |||
| 478 | + | return "gordon" | |||
| 479 | + | } | |||