Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(traefik): improve router port discovery and optimize YAML writes, fixes ddev/ddev-mongo#24#7507

Merged
rfay merged 6 commits intoddev:mainfrom
stasadev:20250804_stasadev_traefik_entrypoint
Aug 8, 2025
Merged

fix(traefik): improve router port discovery and optimize YAML writes, fixes ddev/ddev-mongo#24#7507
rfay merged 6 commits intoddev:mainfrom
stasadev:20250804_stasadev_traefik_entrypoint

Conversation

@stasadev
Copy link
Member

@stasadev stasadev commented Aug 4, 2025

The Issue

Router doesn't become healthy because of missing Traefik entrypoints for optional services.

How This PR Solves The Issue

  • Adds host port detection for Traefik entrypoint by reading .ddev/.ddev-docker-compose-full.yaml
  • Removes hardcoded XHGui ports, they are now read from YAML.
  • Optimizes file writes for .ddev/.ddev-docker-compose-base.yaml and .ddev/.ddev-docker-compose-full.yaml if the content is not changed.

Manual Testing Instructions

Start a project with optional profiles:

ddev add-on get ddev/ddev-mongo
ddev start

Check ~/.ddev/traefik/.static_config.yaml:

entryPoints:
    http-80:
        address: :80
    http-443:
        address: :443
    http-8025:
        address: :8025
    http-8026:
        address: :8026
    http-8142:                 <= comes from xhgui
        address: :8142
    http-8143::                <= comes from xhgui
        address: :8143
    http-9091:                 <= comes from mongo-express
        address: :9091
    http-9092:                 <= comes from mongo-express
        address: :9092
    traefik:
        address: :10999

Automated Testing Overview

Release/Deployment Notes

@github-actions github-actions bot added the bugfix label Aug 4, 2025
@rfay
Copy link
Member

rfay commented Aug 4, 2025

Oh, I expected the fix to be in ddev-mongo! So wonderful that we have tests using this from so many angles.

@github-actions
Copy link

github-actions bot commented Aug 4, 2025

Comment on lines -352 to -355
// Add potential ports that might not be in use by current containers
// 8142/8143 are for xhgui, which might not be enabled, but we don't want to
// have to rebuild traefik when it does get enabled.
routerPorts = []string{nodeps.DdevDefaultXHGuiHTTPSPort, nodeps.DdevDefaultXHGuiHTTPPort}
Copy link
Member Author

@stasadev stasadev Aug 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR makes port detection dynamic, so we no longer need to hardcode XHGui.

Though if the service isn't started, it's unclear why the ports should be occupied.

But this comment gives some justification:

we don't want to have to rebuild traefik when it does get enabled.

What I see is that if we don't add xhgui all the time, then the project configuration in .ddev/traefik/config/<project>.yaml won't work as expected for people removing #ddev-generated from that file.

I didn't look deep into it.

@stasadev stasadev marked this pull request as ready for review August 4, 2025 18:24
@stasadev stasadev requested a review from a team as a code owner August 4, 2025 18:24
Copy link
Member

@rfay rfay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't studied this carefully, but this seems like an important improvement that needs a test?

Copy link
Member

@rfay rfay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I spent some time with this and stepped through and it all seems good to me.

The one TODO if you think it's appropriate is to see if a test can be added. If you think the xhgui stuff is testing it adequately, that's OK.

Below is Claude's opinion. Seems all fine.

Overview

This PR addresses router health issues with optional services by improving Traefik entrypoint port discovery and optimizing YAML file writes. The changes fix the issue where the router wasn't becoming healthy due to missing entrypoints for services defined in project configurations but not yet running.

Key Changes

1. Enhanced Port Discovery (pkg/ddevapp/router.go:341-418)

Strengths:

  • Comprehensive port detection: Now reads from both project configuration files AND running containers, fixing the core issue where optional services weren't detected
  • Cleaner code structure: Extracted processExposePorts() helper function for better maintainability
  • Robust error handling: Continues processing other projects if one fails to read
  • Better documentation: Clear comments explaining the port mapping logic

Technical details:

  • Calls ReadDockerComposeYAML() for all active projects to extract HTTP_EXPOSE/HTTPS_EXPOSE from service environments
  • Properly handles pointer dereferencing for environment variables
  • Maintains existing behavior for running containers while adding configuration-based discovery

2. YAML Write Optimization (pkg/ddevapp/compose_yaml.go:19-132)

Strengths:

  • Performance improvement: Avoids unnecessary file writes when content is unchanged using bytes.Equal()
  • Maintains atomicity: Still uses proper file creation/closing patterns
  • Added ReadDockerComposeYAML(): New function to read compose files, supporting the router port discovery

Technical details:

  • Implements content comparison before writing both base and full YAML files
  • Uses bytes.Equal() instead of string comparison for better performance
  • Preserves existing error handling and file closing patterns

3. File Utility Enhancement (pkg/fileutil/embed.go:46-49)

Strengths:

  • Consistency: Uses same bytes.Equal() optimization pattern
  • Minor but effective: Removes unnecessary string conversion

Code Quality Assessment

Positive aspects:

  • ✅ Passes all static analysis (make staticrequired)
  • ✅ Follows existing code patterns and conventions
  • ✅ Proper error handling throughout
  • ✅ Clear, descriptive function and variable names
  • ✅ Good separation of concerns with the new helper function

Areas that could be improved:

  • The determineRouterPorts() function is getting quite long (70+ lines). Consider splitting the config-based and container-based logic into separate functions
  • Could benefit from unit tests for processExposePorts() function
  • The error handling for ReadDockerComposeYAML() uses util.Warning() but continues - consider if this is the right approach

Testing Considerations

The PR description provides good manual testing instructions. However, automated tests would strengthen confidence:

Suggested tests:

  • Unit test for processExposePorts() with various port formats
  • Integration test verifying port discovery from stopped services
  • Test for YAML write optimization (verify files aren't rewritten when unchanged)

Impact Analysis

Positive impacts:

  • ✅ Fixes router health issues with optional services (addresses ddev/ddev-mongo#24)
  • ✅ Reduces unnecessary disk I/O through YAML write optimization
  • ✅ More reliable port discovery for Traefik configuration
  • ✅ Better user experience with optional add-ons

Risk assessment:

  • 🟡 Low risk: Changes are primarily additive, maintaining backward compatibility
  • 🟡 Performance: Additional file reads for all active projects during router startup (acceptable trade-off)

Recommendation

✅ APPROVE with minor suggestions

This is a well-implemented fix that solves a real problem. The code quality is good, follows established patterns, and the optimizations are beneficial. The approach of reading project configurations in addition to running containers is the right solution for detecting optional services.

Minor suggestions for follow-up:

  1. Consider splitting determineRouterPorts() for better maintainability
  2. Add unit tests for the new processExposePorts() function
  3. Consider caching the project configuration reads if this becomes a performance concern

The PR successfully addresses the core issue and includes valuable optimizations as a bonus.

@stasadev stasadev marked this pull request as draft August 5, 2025 17:19
@stasadev stasadev changed the title fix: improve router port discovery and optimize YAML writes, fixes ddev/ddev-mongo#24 fix(traefik): improve router port discovery and optimize YAML writes, fixes ddev/ddev-mongo#24 Aug 7, 2025
@stasadev stasadev changed the title fix(traefik): improve router port discovery and optimize YAML writes, fixes ddev/ddev-mongo#24 fix(traefik): improve router port discovery and optimize YAML writes, fixes ddev/ddev-mongo#24 Aug 7, 2025
@stasadev stasadev changed the title fix(traefik): improve router port discovery and optimize YAML writes, fixes ddev/ddev-mongo#24 fix(traefik): improve router port discovery and optimize YAML writes, fixes ddev/ddev-mongo#24 Aug 7, 2025
@stasadev stasadev force-pushed the 20250804_stasadev_traefik_entrypoint branch from b77b4ca to 426b0b4 Compare August 7, 2025 18:33
@stasadev
Copy link
Member Author

stasadev commented Aug 7, 2025

Consider splitting determineRouterPorts() for better maintainability
Add unit tests for the new processExposePorts() function

Done with Claude Code.

Integration test verifying port discovery from stopped services

I modified TestCmdStartOptionalProfiles() for this.
It now "successfully" fails for both:

  • DDEV v1.24.7 (while running Traefik API validation)
  • DDEV HEAD (while running ddev start).

@stasadev stasadev marked this pull request as ready for review August 7, 2025 18:36
@rfay
Copy link
Member

rfay commented Aug 7, 2025

It's picking up the goroutines=3 stuff in tests:


Messages:   	failed to parse JSON response for http router: {"entryPoints":["http-18125"],"service":"TestCmdWordpress-busybox1-80","rule":"HostRegexp(`^testcmdwordpress\\.ddev\\.site$`)","ruleSyntax":"v3","priority":44,"observability":{"accessLogs":true,"metrics":true,"tracing":true,"traceVerbosity":"minimal"},"status":"enabled","using":["http-18125"],"name":"TestCmdWordpress-busybox1-80-http@file","provider":"file"}
--
  | goroutines=3 at exit of main()

stasadev and others added 6 commits August 7, 2025 16:10
I should have added this years ago, as I often run using
the ddev from the path. But for this particular setup I needed
things to work with direnv and GoLand, and I couldn't know
what was working without this change.
@rfay rfay force-pushed the 20250804_stasadev_traefik_entrypoint branch from 426b0b4 to ecd1220 Compare August 7, 2025 22:11
Copy link
Member

@rfay rfay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's still great, thanks, and thanks for understanding, studying, and solving this, and caring for ddev-mongo.

I wonder if this means that we could now start/restart traefik before everything else is up and ready, which might slightly improve user experience. (Related idea: Most people in most situations don't really need to wait for everything to be fully ready. We might want to give them an out like "press return to get to work")

@rfay rfay merged commit 29901e3 into ddev:main Aug 8, 2025
18 checks passed
@rfay rfay deleted the 20250804_stasadev_traefik_entrypoint branch August 8, 2025 22:50
@stasadev
Copy link
Member Author

I wonder if this means that we could now start/restart traefik before everything else is up and ready, which might slightly improve user experience.

This is possible, most likely requires moving all dynamic Traefik configuration to the start of ddev start.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments