Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 28 additions & 5 deletions pkg/ddevapp/traefik.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,12 +330,35 @@ func PushGlobalTraefikConfig(activeApps []*DdevApp) error {
}
}

// Copy to volume (adds new files, overwrites existing)
err = dockerutil.CopyIntoVolume(globalTraefikDir, "ddev-global-cache", "traefik", uid, "custom-global-config", false)
if err != nil {
return fmt.Errorf("failed to copy global Traefik config into Docker volume ddev-global-cache/traefik: %v", err)
// Copy to router container if running, otherwise copy to volume
// Copying directly to the router ensures Traefik's fsnotify detects changes reliably
router, err := FindDdevRouter()
if err == nil && router != nil {
// Router is running - copy directly to it using container name without leading slash
// This triggers fsnotify reliably because changes happen inside the container that's watching
containerName := strings.TrimPrefix(router.Names[0], "/")
err = dockerutil.CopyIntoContainer(globalTraefikDir, containerName, "/mnt/ddev-global-cache/traefik", "custom-global-config")
if err != nil {
return fmt.Errorf("failed to copy global Traefik config to router container: %v", err)
}

// Fix ownership of copied files to match what CopyIntoVolume does
chownCmd := fmt.Sprintf("chown -R %s /mnt/ddev-global-cache/traefik", uid)
stdout, stderr, err := dockerutil.Exec(router.ID, chownCmd, "0")
if err != nil {
util.Debug("Failed to chown traefik files (stdout=%s, stderr=%s): %v", stdout, stderr, err)
return fmt.Errorf("failed to set ownership on traefik files: %v", err)
}

util.Debug("Copied global Traefik config in %s directly to router container %s", globalTraefikDir, containerName)
} else {
// Router not running yet - copy to volume as fallback
err = dockerutil.CopyIntoVolume(globalTraefikDir, "ddev-global-cache", "traefik", uid, "custom-global-config", false)
if err != nil {
return fmt.Errorf("failed to copy global Traefik config into Docker volume ddev-global-cache/traefik: %v", err)
}
util.Debug("Copied global Traefik config in %s to ddev-global-cache/traefik volume", globalTraefikDir)
}
util.Debug("Copied global Traefik config in %s to ddev-global-cache/traefik", globalTraefikDir)

// Sync config directory - remove stale project configs from the volume
actualConfigs, err := dockerutil.ListFilesInVolume("ddev-global-cache", "traefik/config")
Expand Down
19 changes: 17 additions & 2 deletions pkg/ddevapp/traefik_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,9 +329,12 @@ func TestCustomGlobalConfig(t *testing.T) {
"Response should contain the custom header from global middleware")
}

// TestCustomProjectConfig tests that custom project-level Traefik configuration
// TestCustomProjectTraefikConfig tests that custom project-level Traefik configuration
// (after removing #ddev-generated) is properly deployed and affects behavior
func TestCustomProjectConfig(t *testing.T) {
func TestCustomProjectTraefikConfig(t *testing.T) {
//if dockerutil.IsRancherDesktop() {
// t.Skip("Skipping on Rancher Desktop because it seems to be too slow to pick up fsnotify on changed traefik config files")
//}
origDir, _ := os.Getwd()
site := TestSites[0]
app, err := ddevapp.NewApp(site.Dir, true)
Expand All @@ -351,10 +354,22 @@ func TestCustomProjectConfig(t *testing.T) {
ddevapp.PowerOff()
})

// We need a clean set of ports for this test because we're doing a specific alteration
// of the traefik config that won't work if the port changes, so avoid ephemeral port use
ddevapp.PowerOff()

// Start the project to generate initial Traefik config
err = app.Start()
require.NoError(t, err)

// Skip if ephemeral ports are in use due to port conflicts
// This happens on Lima-based providers when ports 80/443 are already in use
httpPort := app.GetPrimaryRouterHTTPPort()
httpsPort := app.GetPrimaryRouterHTTPSPort()
if httpPort != "80" || httpsPort != "443" {
t.Skipf("Skipping because non-standard ports are in use (HTTP=%s, HTTPS=%s) - likely due to port conflicts; this breaks the assumption in the altered traefik config", httpPort, httpsPort)
}

// Path to the project's Traefik config file
projectTraefikConfigFile := filepath.Join(projectTraefikConfigDir, app.Name+".yaml")

Expand Down
Loading