From bb599ae3430712136ad292db5c70092c9fee9278 Mon Sep 17 00:00:00 2001 From: davidmdm Date: Fri, 28 May 2021 09:36:48 -0400 Subject: [PATCH] more robust container names --- src/cmd/tidy/tidy.go | 8 +--- src/internal/context_test.go | 12 ++--- src/internal/yey.go | 43 +++++++++++++++-- src/internal/yey_test.go | 89 ++++++++++++++++++++++++++++++++++++ 4 files changed, 134 insertions(+), 18 deletions(-) create mode 100644 src/internal/yey_test.go diff --git a/src/cmd/tidy/tidy.go b/src/cmd/tidy/tidy.go index 159b4c2..b7bea9b 100644 --- a/src/cmd/tidy/tidy.go +++ b/src/cmd/tidy/tidy.go @@ -2,8 +2,6 @@ package tidy import ( "context" - "fmt" - "path/filepath" "strings" "github.com/spf13/cobra" @@ -39,11 +37,7 @@ func run(ctx context.Context) error { validNames[yey.ContainerName(contexts.Path, ctx)] = struct{}{} } - prefix := fmt.Sprintf( - "yey-%s-%s", - filepath.Base(filepath.Dir(contexts.Path)), - yey.Hash(contexts.Path), - ) + prefix := yey.ContainerPathPrefix(contexts.Path) names, err := docker.ListContainers(ctx) if err != nil { diff --git a/src/internal/context_test.go b/src/internal/context_test.go index 09d20ac..f683461 100644 --- a/src/internal/context_test.go +++ b/src/internal/context_test.go @@ -84,32 +84,32 @@ func TestMerge(t *testing.T) { func TestSameHashesForSameContexts(t *testing.T) { ctx1 := getCtx1() - hash1 := Hash(ctx1.String()) + hash1 := hash(ctx1.String()) ctx2 := getCtx1() - hash2 := Hash(ctx2.String()) + hash2 := hash(ctx2.String()) assert.Equal(t, hash1, hash2) } func TestDifferentHashesForDifferentEnvs(t *testing.T) { ctx1 := getCtx1() - hash1 := Hash(ctx1.String()) + hash1 := hash(ctx1.String()) ctx2 := getCtx1() ctx2.Env["ENV1"] = "value1b" - hash2 := Hash(ctx2.String()) + hash2 := hash(ctx2.String()) assert.NotEqual(t, hash1, hash2) } func TestDifferentHashesForDifferentMounts(t *testing.T) { ctx1 := getCtx1() - hash1 := Hash(ctx1.String()) + hash1 := hash(ctx1.String()) ctx2 := getCtx1() ctx2.Mounts["/local/mount1"] = "/container/mount1b" - hash2 := Hash(ctx2.String()) + hash2 := hash(ctx2.String()) assert.NotEqual(t, hash1, hash2) } diff --git a/src/internal/yey.go b/src/internal/yey.go index 1c80084..e502c2f 100644 --- a/src/internal/yey.go +++ b/src/internal/yey.go @@ -6,9 +6,18 @@ import ( "hash/crc64" "io" "path/filepath" + "regexp" ) -func Hash(value string) string { +var ( + spaces = regexp.MustCompile(`\s+`) + special = regexp.MustCompile(`[^a-zA-Z0-9.\-_]`) + dashes = regexp.MustCompile(`-+`) + alphaNumericStart = regexp.MustCompile(`^[a-zA-Z0-9]`) + trailingDashes = regexp.MustCompile(`-+$`) +) + +func hash(value string) string { hasher := crc64.New(crc64.MakeTable(crc64.ECMA)) io.WriteString(hasher, value) return hex.EncodeToString(hasher.Sum(nil)) @@ -16,10 +25,34 @@ func Hash(value string) string { func ContainerName(path string, context Context) string { return fmt.Sprintf( - "yey-%s-%s-%s-%s", - filepath.Base(filepath.Dir(path)), - Hash(path), + "%s-%s-%s", + ContainerPathPrefix(path), context.Name, - Hash(context.String()), + hash(context.String()), ) } + +func sanitizePathName(value string) string { + value = spaces.ReplaceAllString(value, "_") + value = special.ReplaceAllString(value, "") + value = dashes.ReplaceAllString(value, "-") + value = trailingDashes.ReplaceAllString(value, "") + + if value == "" { + return "" + } + + if !alphaNumericStart.MatchString(value) { + value = "0" + value + } + + return value +} + +func ContainerPathPrefix(path string) string { + pathBase := sanitizePathName(filepath.Base(filepath.Dir(path))) + if pathBase == "" { + return fmt.Sprintf("yey-%s", hash(path)) + } + return fmt.Sprintf("yey-%s-%s", pathBase, hash(path)) +} diff --git a/src/internal/yey_test.go b/src/internal/yey_test.go new file mode 100644 index 0000000..3cb4471 --- /dev/null +++ b/src/internal/yey_test.go @@ -0,0 +1,89 @@ +package yey + +import "testing" + +func TestSanitizePathName(t *testing.T) { + testCases := []struct { + Name string + Value string + Expected string + }{ + { + Name: "removes space from name", + Value: "my folder with spaces", + Expected: "my_folder_with_spaces", + }, + { + Name: "strips invalid characters", + Value: "my&folder#cool*stuff!", + Expected: "myfoldercoolstuff", + }, + { + Name: "padds with 0 if does not start with alphanumeric character", + Value: "_valid_if_not_for_underscore_start", + Expected: "0_valid_if_not_for_underscore_start", + }, + { + Name: "reduces multidashes to single dash", + Value: "my---folder----path", + Expected: "my-folder-path", + }, + { + Name: "strips trailing dashes from name", + Value: "my_folder----", + Expected: "my_folder", + }, + { + Name: "empty string", + Value: "", + Expected: "", + }, + { + Name: "full strip returns empty string", + Value: "$%^&", + Expected: "", + }, + } + + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + actual := sanitizePathName(tc.Value) + if actual != tc.Expected { + t.Fatalf("expected %s but got: %s", tc.Expected, actual) + } + }) + } +} + +func TestContainerPathPrefix(t *testing.T) { + testCases := []struct { + Name string + Value string + Expected string + }{ + { + Name: "simple base name with hash prefix", + Value: "/root/projectName/.yeyrc.yaml", + Expected: "yey-projectName-45c6afaff136ad78", + }, + { + Name: "root path", + Value: "/.yeyrc.yaml", + Expected: "yey-8767bf26c174b05d", + }, + { + Name: "path base that sanitizes to empty string", + Value: "/specialBase/#%!/.yeyrc.yaml", + Expected: "yey-19bd96cb89cf189c", + }, + } + + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + actual := ContainerPathPrefix(tc.Value) + if actual != tc.Expected { + t.Fatalf("expected %s but got: %s", tc.Expected, actual) + } + }) + } +}