PluginRunCommand returns an [os/exec.Cmd] which when [os/exec.Cmd.Run] will execute the named plugin. The rootcmd argument is referenced to determine the set of builtin commands in order to detect conflicts. The error returned satisfies the [errdefs.IsNotFound] predicate if no plugin was found or if
(dockerCli config.Provider, name string, rootcmd *cobra.Command)
| 175 | // The rootcmd argument is referenced to determine the set of builtin commands in order to detect conflicts. |
| 176 | // The error returned satisfies the [errdefs.IsNotFound] predicate if no plugin was found or if the first candidate plugin was invalid somehow. |
| 177 | func PluginRunCommand(dockerCli config.Provider, name string, rootcmd *cobra.Command) (*exec.Cmd, error) { |
| 178 | // This uses the full original args, not the args which may |
| 179 | // have been provided by cobra to our caller. This is because |
| 180 | // they lack e.g. global options which we must propagate here. |
| 181 | args := os.Args[1:] |
| 182 | if !isValidPluginName(name) { |
| 183 | // We treat this as "not found" so that callers will |
| 184 | // fallback to their "invalid" command path. |
| 185 | return nil, errPluginNotFound(name) |
| 186 | } |
| 187 | exename := addExeSuffix(metadata.NamePrefix + name) |
| 188 | pluginDirs := getPluginDirs(dockerCli.ConfigFile()) |
| 189 | |
| 190 | for _, d := range pluginDirs { |
| 191 | path := filepath.Join(d, exename) |
| 192 | |
| 193 | // We stat here rather than letting the exec tell us |
| 194 | // ENOENT because the latter does not distinguish a |
| 195 | // file not existing from its dynamic loader or one of |
| 196 | // its libraries not existing. |
| 197 | if _, err := os.Stat(path); os.IsNotExist(err) { |
| 198 | continue |
| 199 | } |
| 200 | |
| 201 | c := &candidate{path: path} |
| 202 | plugin, err := newPlugin(c, rootcmd.Commands()) |
| 203 | if err != nil { |
| 204 | return nil, err |
| 205 | } |
| 206 | if plugin.Err != nil { |
| 207 | // TODO: why are we not returning plugin.Err? |
| 208 | return nil, errPluginNotFound(name) |
| 209 | } |
| 210 | cmd := exec.Command(plugin.Path, args...) // #nosec G204 -- ignore "Subprocess launched with a potential tainted input or cmd arguments" |
| 211 | |
| 212 | // Using dockerCli.{In,Out,Err}() here results in a hang until something is input. |
| 213 | // See: - https://github.com/golang/go/issues/10338 |
| 214 | // - https://github.com/golang/go/commit/d000e8742a173aa0659584aa01b7ba2834ba28ab |
| 215 | // os.Stdin is a *os.File which avoids this behaviour. We don't need the functionality |
| 216 | // of the wrappers here anyway. |
| 217 | cmd.Stdin = os.Stdin |
| 218 | cmd.Stdout = os.Stdout |
| 219 | cmd.Stderr = os.Stderr |
| 220 | |
| 221 | cmd.Env = append(cmd.Environ(), metadata.ReexecEnvvar+"="+os.Args[0]) |
| 222 | cmd.Env = appendPluginResourceAttributesEnvvar(cmd.Env, rootcmd, plugin) |
| 223 | |
| 224 | return cmd, nil |
| 225 | } |
| 226 | return nil, errPluginNotFound(name) |
| 227 | } |
| 228 | |
| 229 | // IsPluginCommand checks if the given cmd is a plugin-stub. |
| 230 | func IsPluginCommand(cmd *cobra.Command) bool { |
no test coverage detected
searching dependent graphs…