Skip to content

Commit 4321293

Browse files
committed
move cli-plugins metadata types/consts to a separate package
This prevents cli-plugins having to import the plugin-manager. Signed-off-by: Sebastiaan van Stijn <[email protected]>
1 parent ceef542 commit 4321293

File tree

13 files changed

+94
-57
lines changed

13 files changed

+94
-57
lines changed

cli-plugins/examples/helloworld/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"fmt"
66
"os"
77

8-
"github.com/docker/cli/cli-plugins/manager"
8+
"github.com/docker/cli/cli-plugins/metadata"
99
"github.com/docker/cli/cli-plugins/plugin"
1010
"github.com/docker/cli/cli/command"
1111
"github.com/spf13/cobra"
@@ -97,7 +97,7 @@ func main() {
9797
cmd.AddCommand(goodbye, apiversion, exitStatus2)
9898
return cmd
9999
},
100-
manager.Metadata{
100+
metadata.Metadata{
101101
SchemaVersion: "0.1.0",
102102
Vendor: "Docker Inc.",
103103
Version: "testing",

cli-plugins/manager/candidate.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package manager
22

3-
import "os/exec"
3+
import (
4+
"os/exec"
5+
6+
"github.com/docker/cli/cli-plugins/metadata"
7+
)
48

59
// Candidate represents a possible plugin candidate, for mocking purposes
610
type Candidate interface {
@@ -17,5 +21,5 @@ func (c *candidate) Path() string {
1721
}
1822

1923
func (c *candidate) Metadata() ([]byte, error) {
20-
return exec.Command(c.path, MetadataSubcommandName).Output() // #nosec G204 -- ignore "Subprocess launched with a potential tainted input or cmd arguments"
24+
return exec.Command(c.path, metadata.MetadataSubcommandName).Output() // #nosec G204 -- ignore "Subprocess launched with a potential tainted input or cmd arguments"
2125
}

cli-plugins/manager/candidate_test.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"strings"
77
"testing"
88

9+
"github.com/docker/cli/cli-plugins/metadata"
910
"github.com/spf13/cobra"
1011
"gotest.tools/v3/assert"
1112
"gotest.tools/v3/assert/cmp"
@@ -30,10 +31,10 @@ func (c *fakeCandidate) Metadata() ([]byte, error) {
3031

3132
func TestValidateCandidate(t *testing.T) {
3233
const (
33-
goodPluginName = NamePrefix + "goodplugin"
34+
goodPluginName = metadata.NamePrefix + "goodplugin"
3435

35-
builtinName = NamePrefix + "builtin"
36-
builtinAlias = NamePrefix + "alias"
36+
builtinName = metadata.NamePrefix + "builtin"
37+
builtinAlias = metadata.NamePrefix + "alias"
3738

3839
badPrefixPath = "/usr/local/libexec/cli-plugins/wobble"
3940
badNamePath = "/usr/local/libexec/cli-plugins/docker-123456"
@@ -43,9 +44,9 @@ func TestValidateCandidate(t *testing.T) {
4344

4445
fakeroot := &cobra.Command{Use: "docker"}
4546
fakeroot.AddCommand(&cobra.Command{
46-
Use: strings.TrimPrefix(builtinName, NamePrefix),
47+
Use: strings.TrimPrefix(builtinName, metadata.NamePrefix),
4748
Aliases: []string{
48-
strings.TrimPrefix(builtinAlias, NamePrefix),
49+
strings.TrimPrefix(builtinAlias, metadata.NamePrefix),
4950
},
5051
})
5152

@@ -59,7 +60,7 @@ func TestValidateCandidate(t *testing.T) {
5960
}{
6061
/* Each failing one of the tests */
6162
{name: "empty path", c: &fakeCandidate{path: ""}, err: "plugin candidate path cannot be empty"},
62-
{name: "bad prefix", c: &fakeCandidate{path: badPrefixPath}, err: fmt.Sprintf("does not have %q prefix", NamePrefix)},
63+
{name: "bad prefix", c: &fakeCandidate{path: badPrefixPath}, err: fmt.Sprintf("does not have %q prefix", metadata.NamePrefix)},
6364
{name: "bad path", c: &fakeCandidate{path: badNamePath}, invalid: "did not match"},
6465
{name: "builtin command", c: &fakeCandidate{path: builtinName}, invalid: `plugin "builtin" duplicates builtin command`},
6566
{name: "builtin alias", c: &fakeCandidate{path: builtinAlias}, invalid: `plugin "alias" duplicates an alias of builtin command "builtin"`},
@@ -84,7 +85,7 @@ func TestValidateCandidate(t *testing.T) {
8485
assert.ErrorContains(t, p.Err, tc.invalid)
8586
default:
8687
assert.NilError(t, err)
87-
assert.Equal(t, NamePrefix+p.Name, goodPluginName)
88+
assert.Equal(t, metadata.NamePrefix+p.Name, goodPluginName)
8889
assert.Equal(t, p.SchemaVersion, "0.1.0")
8990
assert.Equal(t, p.Vendor, "e2e-testing")
9091
}

cli-plugins/manager/manager.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"strings"
1010
"sync"
1111

12+
"github.com/docker/cli/cli-plugins/metadata"
1213
"github.com/docker/cli/cli/config"
1314
"github.com/docker/cli/cli/config/configfile"
1415
"github.com/fvbommel/sortorder"
@@ -21,7 +22,7 @@ const (
2122
// used to originally invoke the docker CLI when executing a
2223
// plugin. Assuming $PATH and $CWD remain unchanged this should allow
2324
// the plugin to re-execute the original CLI.
24-
ReexecEnvvar = "DOCKER_CLI_PLUGIN_ORIGINAL_CLI_COMMAND"
25+
ReexecEnvvar = metadata.ReexecEnvvar
2526

2627
// ResourceAttributesEnvvar is the name of the envvar that includes additional
2728
// resource attributes for OTEL.
@@ -92,10 +93,10 @@ func addPluginCandidatesFromDir(res map[string][]string, d string) {
9293
continue
9394
}
9495
name := dentry.Name()
95-
if !strings.HasPrefix(name, NamePrefix) {
96+
if !strings.HasPrefix(name, metadata.NamePrefix) {
9697
continue
9798
}
98-
name = strings.TrimPrefix(name, NamePrefix)
99+
name = strings.TrimPrefix(name, metadata.NamePrefix)
99100
var err error
100101
if name, err = trimExeSuffix(name); err != nil {
101102
continue
@@ -200,7 +201,7 @@ func PluginRunCommand(dockerCli config.Provider, name string, rootcmd *cobra.Com
200201
// fallback to their "invalid" command path.
201202
return nil, errPluginNotFound(name)
202203
}
203-
exename := addExeSuffix(NamePrefix + name)
204+
exename := addExeSuffix(metadata.NamePrefix + name)
204205
pluginDirs, err := getPluginDirs(dockerCli.ConfigFile())
205206
if err != nil {
206207
return nil, err
@@ -237,7 +238,7 @@ func PluginRunCommand(dockerCli config.Provider, name string, rootcmd *cobra.Com
237238
cmd.Stdout = os.Stdout
238239
cmd.Stderr = os.Stderr
239240

240-
cmd.Env = append(cmd.Environ(), ReexecEnvvar+"="+os.Args[0])
241+
cmd.Env = append(cmd.Environ(), metadata.ReexecEnvvar+"="+os.Args[0])
241242
cmd.Env = appendPluginResourceAttributesEnvvar(cmd.Env, rootcmd, plugin)
242243

243244
return cmd, nil

cli-plugins/manager/metadata.go

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,23 @@
11
package manager
22

3+
import (
4+
"github.com/docker/cli/cli-plugins/metadata"
5+
)
6+
37
const (
48
// NamePrefix is the prefix required on all plugin binary names
5-
NamePrefix = "docker-"
9+
NamePrefix = metadata.NamePrefix
610

711
// MetadataSubcommandName is the name of the plugin subcommand
812
// which must be supported by every plugin and returns the
913
// plugin metadata.
10-
MetadataSubcommandName = "docker-cli-plugin-metadata"
14+
MetadataSubcommandName = metadata.MetadataSubcommandName
1115

1216
// HookSubcommandName is the name of the plugin subcommand
1317
// which must be implemented by plugins declaring support
1418
// for hooks in their metadata.
15-
HookSubcommandName = "docker-cli-plugin-hooks"
19+
HookSubcommandName = metadata.HookSubcommandName
1620
)
1721

1822
// Metadata provided by the plugin.
19-
type Metadata struct {
20-
// SchemaVersion describes the version of this struct. Mandatory, must be "0.1.0"
21-
SchemaVersion string `json:",omitempty"`
22-
// Vendor is the name of the plugin vendor. Mandatory
23-
Vendor string `json:",omitempty"`
24-
// Version is the optional version of this plugin.
25-
Version string `json:",omitempty"`
26-
// ShortDescription should be suitable for a single line help message.
27-
ShortDescription string `json:",omitempty"`
28-
// URL is a pointer to the plugin's homepage.
29-
URL string `json:",omitempty"`
30-
}
23+
type Metadata = metadata.Metadata

cli-plugins/manager/plugin.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"regexp"
1010
"strings"
1111

12+
"github.com/docker/cli/cli-plugins/metadata"
1213
"github.com/pkg/errors"
1314
"github.com/spf13/cobra"
1415
)
@@ -17,7 +18,7 @@ var pluginNameRe = regexp.MustCompile("^[a-z][a-z0-9]*$")
1718

1819
// Plugin represents a potential plugin with all it's metadata.
1920
type Plugin struct {
20-
Metadata
21+
metadata.Metadata
2122

2223
Name string `json:",omitempty"`
2324
Path string `json:",omitempty"`
@@ -50,12 +51,12 @@ func newPlugin(c Candidate, cmds []*cobra.Command) (Plugin, error) {
5051
if fullname, err = trimExeSuffix(fullname); err != nil {
5152
return Plugin{}, errors.Wrapf(err, "plugin candidate %q", path)
5253
}
53-
if !strings.HasPrefix(fullname, NamePrefix) {
54-
return Plugin{}, errors.Errorf("plugin candidate %q: does not have %q prefix", path, NamePrefix)
54+
if !strings.HasPrefix(fullname, metadata.NamePrefix) {
55+
return Plugin{}, errors.Errorf("plugin candidate %q: does not have %q prefix", path, metadata.NamePrefix)
5556
}
5657

5758
p := Plugin{
58-
Name: strings.TrimPrefix(fullname, NamePrefix),
59+
Name: strings.TrimPrefix(fullname, metadata.NamePrefix),
5960
Path: path,
6061
}
6162

@@ -112,9 +113,9 @@ func (p *Plugin) RunHook(ctx context.Context, hookData HookPluginData) ([]byte,
112113
return nil, wrapAsPluginError(err, "failed to marshall hook data")
113114
}
114115

115-
pCmd := exec.CommandContext(ctx, p.Path, p.Name, HookSubcommandName, string(hDataBytes)) // #nosec G204 -- ignore "Subprocess launched with a potential tainted input or cmd arguments"
116+
pCmd := exec.CommandContext(ctx, p.Path, p.Name, metadata.HookSubcommandName, string(hDataBytes)) // #nosec G204 -- ignore "Subprocess launched with a potential tainted input or cmd arguments"
116117
pCmd.Env = os.Environ()
117-
pCmd.Env = append(pCmd.Env, ReexecEnvvar+"="+os.Args[0])
118+
pCmd.Env = append(pCmd.Env, metadata.ReexecEnvvar+"="+os.Args[0])
118119
hookCmdOutput, err := pCmd.Output()
119120
if err != nil {
120121
return nil, wrapAsPluginError(err, "failed to execute plugin hook subcommand")

cli-plugins/metadata/metadata.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package metadata
2+
3+
const (
4+
// NamePrefix is the prefix required on all plugin binary names
5+
NamePrefix = "docker-"
6+
7+
// MetadataSubcommandName is the name of the plugin subcommand
8+
// which must be supported by every plugin and returns the
9+
// plugin metadata.
10+
MetadataSubcommandName = "docker-cli-plugin-metadata"
11+
12+
// HookSubcommandName is the name of the plugin subcommand
13+
// which must be implemented by plugins declaring support
14+
// for hooks in their metadata.
15+
HookSubcommandName = "docker-cli-plugin-hooks"
16+
17+
// ReexecEnvvar is the name of an ennvar which is set to the command
18+
// used to originally invoke the docker CLI when executing a
19+
// plugin. Assuming $PATH and $CWD remain unchanged this should allow
20+
// the plugin to re-execute the original CLI.
21+
ReexecEnvvar = "DOCKER_CLI_PLUGIN_ORIGINAL_CLI_COMMAND"
22+
)
23+
24+
// Metadata provided by the plugin.
25+
type Metadata struct {
26+
// SchemaVersion describes the version of this struct. Mandatory, must be "0.1.0"
27+
SchemaVersion string `json:",omitempty"`
28+
// Vendor is the name of the plugin vendor. Mandatory
29+
Vendor string `json:",omitempty"`
30+
// Version is the optional version of this plugin.
31+
Version string `json:",omitempty"`
32+
// ShortDescription should be suitable for a single line help message.
33+
ShortDescription string `json:",omitempty"`
34+
// URL is a pointer to the plugin's homepage.
35+
URL string `json:",omitempty"`
36+
}

cli-plugins/plugin/plugin.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"sync"
1010

1111
"github.com/docker/cli/cli"
12-
"github.com/docker/cli/cli-plugins/manager"
12+
"github.com/docker/cli/cli-plugins/metadata"
1313
"github.com/docker/cli/cli-plugins/socket"
1414
"github.com/docker/cli/cli/command"
1515
"github.com/docker/cli/cli/connhelper"
@@ -30,7 +30,7 @@ import (
3030
var PersistentPreRunE func(*cobra.Command, []string) error
3131

3232
// RunPlugin executes the specified plugin command
33-
func RunPlugin(dockerCli *command.DockerCli, plugin *cobra.Command, meta manager.Metadata) error {
33+
func RunPlugin(dockerCli *command.DockerCli, plugin *cobra.Command, meta metadata.Metadata) error {
3434
tcmd := newPluginCommand(dockerCli, plugin, meta)
3535

3636
var persistentPreRunOnce sync.Once
@@ -81,7 +81,7 @@ func RunPlugin(dockerCli *command.DockerCli, plugin *cobra.Command, meta manager
8181
}
8282

8383
// Run is the top-level entry point to the CLI plugin framework. It should be called from your plugin's `main()` function.
84-
func Run(makeCmd func(command.Cli) *cobra.Command, meta manager.Metadata) {
84+
func Run(makeCmd func(command.Cli) *cobra.Command, meta metadata.Metadata) {
8585
otel.SetErrorHandler(debug.OTELErrorHandler)
8686

8787
dockerCli, err := command.NewDockerCli()
@@ -111,7 +111,7 @@ func Run(makeCmd func(command.Cli) *cobra.Command, meta manager.Metadata) {
111111
func withPluginClientConn(name string) command.CLIOption {
112112
return command.WithInitializeClient(func(dockerCli *command.DockerCli) (client.APIClient, error) {
113113
cmd := "docker"
114-
if x := os.Getenv(manager.ReexecEnvvar); x != "" {
114+
if x := os.Getenv(metadata.ReexecEnvvar); x != "" {
115115
cmd = x
116116
}
117117
var flags []string
@@ -140,9 +140,9 @@ func withPluginClientConn(name string) command.CLIOption {
140140
})
141141
}
142142

143-
func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta manager.Metadata) *cli.TopLevelCommand {
143+
func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta metadata.Metadata) *cli.TopLevelCommand {
144144
name := plugin.Name()
145-
fullname := manager.NamePrefix + name
145+
fullname := metadata.NamePrefix + name
146146

147147
cmd := &cobra.Command{
148148
Use: fmt.Sprintf("docker [OPTIONS] %s [ARG...]", name),
@@ -177,12 +177,12 @@ func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta
177177
return cli.NewTopLevelCommand(cmd, dockerCli, opts, cmd.Flags())
178178
}
179179

180-
func newMetadataSubcommand(plugin *cobra.Command, meta manager.Metadata) *cobra.Command {
180+
func newMetadataSubcommand(plugin *cobra.Command, meta metadata.Metadata) *cobra.Command {
181181
if meta.ShortDescription == "" {
182182
meta.ShortDescription = plugin.Short
183183
}
184184
cmd := &cobra.Command{
185-
Use: manager.MetadataSubcommandName,
185+
Use: metadata.MetadataSubcommandName,
186186
Hidden: true,
187187
// Suppress the global/parent PersistentPreRunE, which
188188
// needlessly initializes the client and tries to
@@ -200,8 +200,8 @@ func newMetadataSubcommand(plugin *cobra.Command, meta manager.Metadata) *cobra.
200200

201201
// RunningStandalone tells a CLI plugin it is run standalone by direct execution
202202
func RunningStandalone() bool {
203-
if os.Getenv(manager.ReexecEnvvar) != "" {
203+
if os.Getenv(metadata.ReexecEnvvar) != "" {
204204
return false
205205
}
206-
return len(os.Args) < 2 || os.Args[1] != manager.MetadataSubcommandName
206+
return len(os.Args) < 2 || os.Args[1] != metadata.MetadataSubcommandName
207207
}

cli/command/system/info_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"time"
88

99
pluginmanager "github.com/docker/cli/cli-plugins/manager"
10+
"github.com/docker/cli/cli-plugins/metadata"
1011
"github.com/docker/cli/internal/test"
1112
registrytypes "github.com/docker/docker/api/types/registry"
1213
"github.com/docker/docker/api/types/swarm"
@@ -201,7 +202,7 @@ var samplePluginsInfo = []pluginmanager.Plugin{
201202
{
202203
Name: "goodplugin",
203204
Path: "/path/to/docker-goodplugin",
204-
Metadata: pluginmanager.Metadata{
205+
Metadata: metadata.Metadata{
205206
SchemaVersion: "0.1.0",
206207
ShortDescription: "unit test is good",
207208
Vendor: "ACME Corp",
@@ -211,7 +212,7 @@ var samplePluginsInfo = []pluginmanager.Plugin{
211212
{
212213
Name: "unversionedplugin",
213214
Path: "/path/to/docker-unversionedplugin",
214-
Metadata: pluginmanager.Metadata{
215+
Metadata: metadata.Metadata{
215216
SchemaVersion: "0.1.0",
216217
ShortDescription: "this plugin has no version",
217218
Vendor: "ACME Corp",

e2e/cli-plugins/dial_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"path/filepath"
66
"testing"
77

8-
"github.com/docker/cli/cli-plugins/manager"
8+
"github.com/docker/cli/cli-plugins/metadata"
99
"gotest.tools/v3/icmd"
1010
)
1111

@@ -21,7 +21,7 @@ func TestCLIPluginDialStdio(t *testing.T) {
2121
// the connhelper stuff.
2222
helloworld := filepath.Join(os.Getenv("DOCKER_CLI_E2E_PLUGINS_EXTRA_DIRS"), "docker-helloworld")
2323
cmd := icmd.Command(helloworld, "--config=blah", "--log-level", "debug", "helloworld", "--who=foo")
24-
res := icmd.RunCmd(cmd, icmd.WithEnv(manager.ReexecEnvvar+"=/bin/true"))
24+
res := icmd.RunCmd(cmd, icmd.WithEnv(metadata.ReexecEnvvar+"=/bin/true"))
2525
res.Assert(t, icmd.Expected{
2626
ExitCode: 0,
2727
Err: `msg="commandconn: starting /bin/true with [--config=blah --log-level debug system dial-stdio]"`,

0 commit comments

Comments
 (0)