cli-plugins/manager: replace pluginNameRe for isValidPluginName utility
The pluginNameRe was a basic regular expression, effectively only checking
if the name consisted of lowercase alphanumeric characters. Replace it
with a minimal utility to do the same, without having to use regular
expressions (or the "lazyregexp" package).
Some quick benchmarking (not committed) show that the non-regex approach
is ~25x faster:
BenchmarkIsValidPluginName_Regex_Valid-10 13956240 81.39 ns/op 0 B/op 0 allocs/op
BenchmarkIsValidPluginName_Manual_Valid-10 360003060 3.318 ns/op 0 B/op 0 allocs/op
BenchmarkIsValidPluginName_Regex_Invalid-10 35281794 33.74 ns/op 0 B/op 0 allocs/op
BenchmarkIsValidPluginName_Manual_Invalid-10 906072663 1.320 ns/op 0 B/op 0 allocs/op
BenchmarkIsValidPluginName_Regex_Parallel-10 96595677 12.04 ns/op 0 B/op 0 allocs/op
BenchmarkIsValidPluginName_Manual_Parallel-10 1000000000 0.4541 ns/op 0 B/op 0 allocs/op
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
@ -169,7 +169,7 @@ func PluginRunCommand(dockerCli config.Provider, name string, rootcmd *cobra.Com
|
||||
// have been provided by cobra to our caller. This is because
|
||||
// they lack e.g. global options which we must propagate here.
|
||||
args := os.Args[1:]
|
||||
if !pluginNameRe.MatchString(name) {
|
||||
if !isValidPluginName(name) {
|
||||
// We treat this as "not found" so that callers will
|
||||
// fallback to their "invalid" command path.
|
||||
return nil, errPluginNotFound(name)
|
||||
|
||||
@ -12,12 +12,9 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
"github.com/docker/cli/internal/lazyregexp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var pluginNameRe = lazyregexp.New("^[a-z][a-z0-9]*$")
|
||||
|
||||
// Plugin represents a potential plugin with all it's metadata.
|
||||
type Plugin struct {
|
||||
metadata.Metadata
|
||||
@ -85,8 +82,8 @@ func newPlugin(c pluginCandidate, cmds []*cobra.Command) (Plugin, error) {
|
||||
}
|
||||
|
||||
// Now apply the candidate tests, so these update p.Err.
|
||||
if !pluginNameRe.MatchString(p.Name) {
|
||||
p.Err = newPluginError("plugin candidate %q did not match %q", p.Name, pluginNameRe.String())
|
||||
if !isValidPluginName(p.Name) {
|
||||
p.Err = newPluginError("plugin candidate %q did not match %q", p.Name, pluginNameFormat)
|
||||
return p, nil
|
||||
}
|
||||
|
||||
@ -147,3 +144,26 @@ func (p *Plugin) RunHook(ctx context.Context, hookData HookPluginData) ([]byte,
|
||||
|
||||
return hookCmdOutput, nil
|
||||
}
|
||||
|
||||
// pluginNameFormat is used as part of errors for invalid plugin-names.
|
||||
// We should consider making this less technical ("must start with "a-z",
|
||||
// and only consist of lowercase alphanumeric characters").
|
||||
const pluginNameFormat = `^[a-z][a-z0-9]*$`
|
||||
|
||||
func isValidPluginName(s string) bool {
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
// first character must be a-z
|
||||
if c := s[0]; c < 'a' || c > 'z' {
|
||||
return false
|
||||
}
|
||||
// followed by a-z or 0-9
|
||||
for i := 1; i < len(s); i++ {
|
||||
c := s[i]
|
||||
if (c < 'a' || c > 'z') && (c < '0' || c > '9') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user