Skip to content

Conversation

@thaJeztah
Copy link
Member

@thaJeztah thaJeztah commented Mar 11, 2025

Before this patch, a broken symlink would print a warning;

docker info > /dev/null
WARNING: Plugin "/Users/thajeztah/.docker/cli-plugins/docker-feedback" is not valid: failed to fetch metadata: fork/exec /Users/thajeztah/.docker/cli-plugins/docker-feedback: no such file or directory

After this patch, such symlinks are ignored:

docker info > /dev/null

With debug enabled, we don't ignore the faulty plugin, which will
make the warning shown on docker info;

mkdir -p ~/.docker/cli-plugins
ln -s nosuchplugin ~/.docker/cli-plugins/docker-brokenplugin
docker --debug info
Client:
 Version:    29.0.0-dev
 Context:    default
 Debug Mode: true
 Plugins:
  buildx: Docker Buildx (Docker Inc.)
    Version:  v0.25.0
    Path:     /usr/libexec/docker/cli-plugins/docker-buildx
WARNING: Plugin "/Users/thajeztah/.docker/cli-plugins/docker-brokenplugin" is not valid: failed to fetch metadata: fork/exec /Users/thajeztah/.docker/cli-plugins/docker-brokenplugin: no such file or directory

    # ...

We should als consider passing a "seen" map to de-duplicate entries. Entries can be either a direct symlink or in a symlinked path (for which we can filepath.EvalSymlinks). We need to benchmark the overhead of resolving the symlink vs possibly calling the plugin (to get their metadata) further down the line.

- Human readable description for the release notes

- A picture of a cute animal (not mandatory but encouraged)

@codecov-commenter
Copy link

codecov-commenter commented Mar 11, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@thaJeztah
Copy link
Member Author

cc @rumpl @Benehiko @vvoland @laurazard let me know if you think this is a good idea, or if we shouldn't ignore these broken links

@Benehiko
Copy link
Member

On the one hand some users might not even know they are executing a plugin since it's hidden under the docker command so the error might be confusing - "I didn't install any plugins? what is docker-feedback?"
On the other hand, printing an error is nice to debug issues caused by the user or us.

Perhaps we should rather update the error message with a clearer, "these are the next steps to get rid of this error"?

@thaJeztah
Copy link
Member Author

It's tricky; so in normal cases, this would not happen. In this specific case, it's not even an issue introduced by the user but a bug in Docker Desktop's installer/updater to not cleanup things it created. Ultimately, Docker Desktop should not create these symlinks at all because the CLI plugin search-paths were designed for that; we're currently doing more work than needed, and the CLI is also slower because of that (as it's iterating multiple times over plugins that it will find in multiple places!).

If you look at docker info, you'll see that it doesn't even use these symlinks, but found the actual ones because they had a higher priority;

docker info
Client:
 Version:    28.0.1
 Context:    desktop-linux
 Debug Mode: false
 Plugins:
  ...
  buildx: Docker Buildx (Docker Inc.)
    Version:  v0.21.1-desktop.1
    Path:     /Applications/Docker.app/Contents/Resources/cli-plugins/docker-buildx

The ~/.docker/cli-plugins/ directory should be empty, and be fore users to install custom plugins, or versions of plugins to override the defaults ()

It's als not something "solvable" in all cases; for example, bind-mounting the ~/.docker/ directory into a container will have all CLI plugins "found", but broken (because their symlinks are broken). They wouldn't work in either case, as they'd be macOS binaries in a Linux contianer (in Docker Desktop for Mac / Windows cases)

docker run --rm -v /var/run/docker.sock.raw:/var/run/docker.sock -v ~/.docker/:/root/.docker/ docker:cli info > /dev/null
WARNING: Plugin "/root/.docker/cli-plugins/docker-ai" is not valid: failed to fetch metadata: fork/exec /root/.docker/cli-plugins/docker-ai: no such file or directory
WARNING: Plugin "/root/.docker/cli-plugins/docker-buildx" is not valid: failed to fetch metadata: fork/exec /root/.docker/cli-plugins/docker-buildx: no such file or directory
WARNING: Plugin "/root/.docker/cli-plugins/docker-compose" is not valid: failed to fetch metadata: fork/exec /root/.docker/cli-plugins/docker-compose: no such file or directory
WARNING: Plugin "/root/.docker/cli-plugins/docker-debug" is not valid: failed to fetch metadata: fork/exec /root/.docker/cli-plugins/docker-debug: no such file or directory
WARNING: Plugin "/root/.docker/cli-plugins/docker-desktop" is not valid: failed to fetch metadata: fork/exec /root/.docker/cli-plugins/docker-desktop: no such file or directory
WARNING: Plugin "/root/.docker/cli-plugins/docker-dev" is not valid: failed to fetch metadata: fork/exec /root/.docker/cli-plugins/docker-dev: no such file or directory
WARNING: Plugin "/root/.docker/cli-plugins/docker-extension" is not valid: failed to fetch metadata: fork/exec /root/.docker/cli-plugins/docker-extension: no such file or directory
WARNING: Plugin "/root/.docker/cli-plugins/docker-feedback" is not valid: failed to fetch metadata: fork/exec /root/.docker/cli-plugins/docker-feedback: no such file or directory
WARNING: Plugin "/root/.docker/cli-plugins/docker-init" is not valid: failed to fetch metadata: fork/exec /root/.docker/cli-plugins/docker-init: no such file or directory
WARNING: Plugin "/root/.docker/cli-plugins/docker-sbom" is not valid: failed to fetch metadata: fork/exec /root/.docker/cli-plugins/docker-sbom: no such file or directory
WARNING: Plugin "/root/.docker/cli-plugins/docker-scout" is not valid: failed to fetch metadata: fork/exec /root/.docker/cli-plugins/docker-scout: no such file or directory

@thaJeztah
Copy link
Member Author

thaJeztah commented Mar 11, 2025

Having a quick peek at the situation in Docker Desktop, it looks like we make the CLI traverse the same plugins three times;

  • We have the actual location that we add as custom cliPluginsExtraDirs configuration in ~/.docker/config.json (/Applications/Docker.app/Contents/Resources/cli-plugins/)
  • We have symlinks for each plugin in ~/.docker/cli-plugins (which we likely shouldn't have, because this is the location where users could install their own plugins, and this location is not-unlikely to be bind-mounted into a container)
  • ☝️ as these are symlinks for each plugin, these are also the ones we tend to leave behind, causing warnings to be shown to the user.
  • We have /usr/local/lib/docker/cli-plugins that is symlinked to /Applications/Docker.app/Contents/Resources/cli-plugins/
./build/docker info > /dev/null
Listing plugins in /Applications/Docker.app/Contents/Resources/cli-plugins/
 - plugin:  docker-ai
 - plugin:  docker-buildx
 - plugin:  docker-cloud
 - plugin:  docker-compose
 - plugin:  docker-debug
 - plugin:  docker-desktop
 - plugin:  docker-dev
 - plugin:  docker-extension
 - plugin:  docker-init
 - plugin:  docker-sbom
 - plugin:  docker-scout
Listing plugins in /Users/thajeztah/.docker/cli-plugins
 - plugin: docker-ai 			(symlink to /Applications/Docker.app/Contents/Resources/cli-plugins/docker-ai)
 - plugin: docker-buildx 		(symlink to /Applications/Docker.app/Contents/Resources/cli-plugins/docker-buildx)
 - plugin: docker-compose 		(symlink to /Applications/Docker.app/Contents/Resources/cli-plugins/docker-compose)
 - plugin: docker-debug 		(symlink to /Applications/Docker.app/Contents/Resources/cli-plugins/docker-debug)
 - plugin: docker-desktop 		(symlink to /Applications/Docker.app/Contents/Resources/cli-plugins/docker-desktop)
 - plugin: docker-dev 			(symlink to /Applications/Docker.app/Contents/Resources/cli-plugins/docker-dev)
 - plugin: docker-extension 	(symlink to /Applications/Docker.app/Contents/Resources/cli-plugins/docker-extension)
 - plugin:  docker-feedback
 - plugin: docker-init 			(symlink to /Applications/Docker.app/Contents/Resources/cli-plugins/docker-init)
 - plugin: docker-sbom 			(symlink to /Applications/Docker.app/Contents/Resources/cli-plugins/docker-sbom)
 - plugin: docker-scout 		(symlink to /Applications/Docker.app/Contents/Resources/cli-plugins/docker-scout)
 - plugin:  docker-shell
Listing plugins in /usr/local/lib/docker/cli-plugins 	(symlink to /Applications/Docker.app/Contents/Resources/cli-plugins)
 - plugin: docker-ai 			(actual path /Applications/Docker.app/Contents/Resources/cli-plugins/docker-ai)
 - plugin: docker-buildx 		(actual path /Applications/Docker.app/Contents/Resources/cli-plugins/docker-buildx)
 - plugin: docker-cloud 		(actual path /Applications/Docker.app/Contents/Resources/cli-plugins/docker-cloud)
 - plugin: docker-compose 		(actual path /Applications/Docker.app/Contents/Resources/cli-plugins/docker-compose)
 - plugin: docker-debug 		(actual path /Applications/Docker.app/Contents/Resources/cli-plugins/docker-debug)
 - plugin: docker-desktop 		(actual path /Applications/Docker.app/Contents/Resources/cli-plugins/docker-desktop)
 - plugin: docker-dev 			(actual path /Applications/Docker.app/Contents/Resources/cli-plugins/docker-dev)
 - plugin: docker-extension 		(actual path /Applications/Docker.app/Contents/Resources/cli-plugins/docker-extension)
 - plugin: docker-init 			(actual path /Applications/Docker.app/Contents/Resources/cli-plugins/docker-init)
 - plugin: docker-sbom 			(actual path /Applications/Docker.app/Contents/Resources/cli-plugins/docker-sbom)
 - plugin: docker-scout 		(actual path /Applications/Docker.app/Contents/Resources/cli-plugins/docker-scout)
Listing plugins in /usr/local/libexec/docker/cli-plugins
Listing plugins in /usr/lib/docker/cli-plugins
Listing plugins in /usr/libexec/docker/cli-plugins
WARNING: Plugin "/Users/thajeztah/.docker/cli-plugins/docker-feedback" is not valid: failed to fetch metadata: fork/exec /Users/thajeztah/.docker/cli-plugins/docker-feedback: no such file or directory

Which gets used in order of priority;

Plugin: docker desktop
  1. /Applications/Docker.app/Contents/Resources/cli-plugins/docker-desktop
  2. /Users/thajeztah/.docker/cli-plugins/docker-desktop
  3. /usr/local/lib/docker/cli-plugins/docker-desktop
Plugin: docker sbom
  1. /Applications/Docker.app/Contents/Resources/cli-plugins/docker-sbom
  2. /Users/thajeztah/.docker/cli-plugins/docker-sbom
  3. /usr/local/lib/docker/cli-plugins/docker-sbom
Plugin: docker feedback
  1. /Users/thajeztah/.docker/cli-plugins/docker-feedback
Plugin: docker ai
  1. /Applications/Docker.app/Contents/Resources/cli-plugins/docker-ai
  2. /Users/thajeztah/.docker/cli-plugins/docker-ai
  3. /usr/local/lib/docker/cli-plugins/docker-ai
Plugin: docker cloud
  1. /Applications/Docker.app/Contents/Resources/cli-plugins/docker-cloud
  2. /usr/local/lib/docker/cli-plugins/docker-cloud
Plugin: docker debug
  1. /Applications/Docker.app/Contents/Resources/cli-plugins/docker-debug
  2. /Users/thajeztah/.docker/cli-plugins/docker-debug
  3. /usr/local/lib/docker/cli-plugins/docker-debug
Plugin: docker dev
  1. /Applications/Docker.app/Contents/Resources/cli-plugins/docker-dev
  2. /Users/thajeztah/.docker/cli-plugins/docker-dev
  3. /usr/local/lib/docker/cli-plugins/docker-dev
Plugin: docker extension
  1. /Applications/Docker.app/Contents/Resources/cli-plugins/docker-extension
  2. /Users/thajeztah/.docker/cli-plugins/docker-extension
  3. /usr/local/lib/docker/cli-plugins/docker-extension
Plugin: docker init
  1. /Applications/Docker.app/Contents/Resources/cli-plugins/docker-init
  2. /Users/thajeztah/.docker/cli-plugins/docker-init
  3. /usr/local/lib/docker/cli-plugins/docker-init
Plugin: docker scout
  1. /Applications/Docker.app/Contents/Resources/cli-plugins/docker-scout
  2. /Users/thajeztah/.docker/cli-plugins/docker-scout
  3. /usr/local/lib/docker/cli-plugins/docker-scout
Plugin: docker shell
  1. /Users/thajeztah/.docker/cli-plugins/docker-shell
Plugin: docker buildx
  1. /Applications/Docker.app/Contents/Resources/cli-plugins/docker-buildx
  2. /Users/thajeztah/.docker/cli-plugins/docker-buildx
  3. /usr/local/lib/docker/cli-plugins/docker-buildx
Plugin: docker compose
  1. /Applications/Docker.app/Contents/Resources/cli-plugins/docker-compose
  2. /Users/thajeztah/.docker/cli-plugins/docker-compose
  3. /usr/local/lib/docker/cli-plugins/docker-compose
WARNING: Plugin "/Users/thajeztah/.docker/cli-plugins/docker-feedback" is not valid: failed to fetch metadata: fork/exec /Users/thajeztah/.docker/cli-plugins/docker-feedback: no such file or directory

@thaJeztah thaJeztah force-pushed the ignore_broken_symlinks branch from d231cf6 to 68164c4 Compare March 22, 2025 11:41
@thaJeztah thaJeztah force-pushed the ignore_broken_symlinks branch 2 times, most recently from e3204f6 to 3f0d4de Compare September 1, 2025 19:04
@thaJeztah thaJeztah added this to the 29.0.0 milestone Sep 1, 2025
Comment on lines 80 to 70
case os.ModeSymlink:
if _, err := os.Stat(filepath.Join(d, dentry.Name())); err != nil {
// Skip broken symlinks unless debug is enabled. With debug
// enabled, this will print a warning in "docker info".
if errors.Is(err, os.ErrNotExist) && !debug.IsEnabled() {
continue
}
}
Copy link
Member Author

Choose a reason for hiding this comment

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

@Benehiko @vvoland I updated this to ignore the broken symlink by default, but with --debug enabled, it returns to the old behavior, which will produce an error that's printed as a warning

@thaJeztah thaJeztah force-pushed the ignore_broken_symlinks branch from 3f0d4de to 2ea69ac Compare September 1, 2025 21:32
Comment on lines 84 to 69
if _, err := os.Stat(filepath.Join(d, dentry.Name())); errors.Is(err, os.ErrNotExist) {
if !debug.IsEnabled() {
continue
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

We're already in a if !debug.IsEnabled() block

Suggested change
if _, err := os.Stat(filepath.Join(d, dentry.Name())); errors.Is(err, os.ErrNotExist) {
if !debug.IsEnabled() {
continue
}
}
if _, err := os.Stat(filepath.Join(d, dentry.Name())); errors.Is(err, os.ErrNotExist) {
continue
}

Copy link
Member Author

Choose a reason for hiding this comment

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

Doh! I initially had the check here, then decided "why do the stat at all in debug more?", and forgot to remove.

Should be fixed now; PAL @vvoland

Before this patch, a broken symlink would print a warning;

    docker info > /dev/null
    WARNING: Plugin "/Users/thajeztah/.docker/cli-plugins/docker-feedback" is not valid: failed to fetch metadata: fork/exec /Users/thajeztah/.docker/cli-plugins/docker-feedback: no such file or directory

After this patch, such symlinks are ignored:

    docker info > /dev/null

With debug enabled, we don't ignore the faulty plugin, which will
make the warning shown on docker info;

    mkdir -p ~/.docker/cli-plugins
    ln -s nosuchplugin ~/.docker/cli-plugins/docker-brokenplugin
    docker --debug info
    Client:
     Version:    29.0.0-dev
     Context:    default
     Debug Mode: true
     Plugins:
      buildx: Docker Buildx (Docker Inc.)
        Version:  v0.25.0
        Path:     /usr/libexec/docker/cli-plugins/docker-buildx
    WARNING: Plugin "/Users/thajeztah/.docker/cli-plugins/docker-brokenplugin" is not valid: failed to fetch metadata: fork/exec /Users/thajeztah/.docker/cli-plugins/docker-brokenplugin: no such file or directory

    # ...

We should als consider passing a "seen" map to de-duplicate entries.
Entries can be either a direct symlink or in a symlinked path (for
which we can filepath.EvalSymlinks). We need to benchmark the overhead
of resolving the symlink vs possibly calling the plugin (to get their
metadata) further down the line.

Signed-off-by: Sebastiaan van Stijn <[email protected]>
@vvoland vvoland merged commit f3687d8 into docker:master Sep 24, 2025
100 of 101 checks passed
@thaJeztah thaJeztah deleted the ignore_broken_symlinks branch September 24, 2025 13:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants