Skip to content

Commit ab7018b

Browse files
committed
cli/context/store: replace restrictedNamePattern for isValidName utility
The restrictedNamePattern was a basic regular expression. 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 ~18x faster: BenchmarkIsValidName_Regex_Valid-10 8516511 119.4 ns/op 0 B/op 0 allocs/op BenchmarkIsValidName_Manual_Valid-10 172426240 6.964 ns/op 0 B/op 0 allocs/op BenchmarkIsValidName_Regex_Invalid-10 34824540 34.22 ns/op 0 B/op 0 allocs/op BenchmarkIsValidName_Manual_Invalid-10 550804021 2.173 ns/op 0 B/op 0 allocs/op BenchmarkIsValidName_Regex_Parallel-10 69289900 17.30 ns/op 0 B/op 0 allocs/op BenchmarkIsValidName_Manual_Parallel-10 1000000000 0.9296 ns/op 0 B/op 0 allocs/op Signed-off-by: Sebastiaan van Stijn <[email protected]>
1 parent 6ec3266 commit ab7018b

File tree

1 file changed

+33
-7
lines changed

1 file changed

+33
-7
lines changed

cli/context/store/store.go

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,9 @@ import (
1818
"path/filepath"
1919
"strings"
2020

21-
"github.com/docker/cli/internal/lazyregexp"
2221
"github.com/opencontainers/go-digest"
2322
)
2423

25-
const restrictedNamePattern = "^[a-zA-Z0-9][a-zA-Z0-9_.+-]+$"
26-
27-
var restrictedNameRegEx = lazyregexp.New(restrictedNamePattern)
28-
2924
// Store provides a context store for easily remembering endpoints configuration
3025
type Store interface {
3126
Reader
@@ -225,12 +220,43 @@ func ValidateContextName(name string) error {
225220
if name == "default" {
226221
return errors.New(`"default" is a reserved context name`)
227222
}
228-
if !restrictedNameRegEx.MatchString(name) {
229-
return fmt.Errorf("context name %q is invalid, names are validated against regexp %q", name, restrictedNamePattern)
223+
if !isValidName(name) {
224+
return fmt.Errorf("context name %q is invalid, names are validated against regexp %q", name, validNameFormat)
230225
}
231226
return nil
232227
}
233228

229+
// validNameFormat is used as part of errors for invalid context-names.
230+
// We should consider making this less technical ("must start with "a-z",
231+
// and only consist of alphanumeric characters and separators").
232+
const validNameFormat = `^[a-zA-Z0-9][a-zA-Z0-9_.+-]+$`
233+
234+
// isValidName checks if the context-name is valid ("^[a-zA-Z0-9][a-zA-Z0-9_.+-]+$").
235+
//
236+
// Names must start with an alphanumeric character (a-zA-Z0-9), followed by
237+
// alphanumeric or separators ("_", ".", "+", "-").
238+
func isValidName(s string) bool {
239+
if len(s) < 2 || !isAlphaNum(s[0]) {
240+
return false
241+
}
242+
243+
for i := 1; i < len(s); i++ {
244+
c := s[i]
245+
if isAlphaNum(c) || c == '_' || c == '.' || c == '+' || c == '-' {
246+
continue
247+
}
248+
return false
249+
}
250+
251+
return true
252+
}
253+
254+
func isAlphaNum(c byte) bool {
255+
return (c >= 'a' && c <= 'z') ||
256+
(c >= 'A' && c <= 'Z') ||
257+
(c >= '0' && c <= '9')
258+
}
259+
234260
// Export exports an existing namespace into an opaque data stream
235261
// This stream is actually a tarball containing context metadata and TLS materials, but it does
236262
// not map 1:1 the layout of the context store (don't try to restore it manually without calling store.Import)

0 commit comments

Comments
 (0)