未验证 提交 8d3fed07 编写于 作者: T Thomas Strömberg 提交者: GitHub

Merge pull request #6059 from tstromberg/clock

lock names: Remove uid suffix & hash entire path
...@@ -70,7 +70,7 @@ func SetupCerts(cmd command.Runner, k8s config.KubernetesConfig) error { ...@@ -70,7 +70,7 @@ func SetupCerts(cmd command.Runner, k8s config.KubernetesConfig) error {
// //
// If another process updates the shared certificate, it's invalid. // If another process updates the shared certificate, it's invalid.
// TODO: Instead of racey manipulation of a shared certificate, use per-profile certs // TODO: Instead of racey manipulation of a shared certificate, use per-profile certs
spec := lock.UserMutexSpec(filepath.Join(localPath, "certs")) spec := lock.PathMutexSpec(filepath.Join(localPath, "certs"))
glog.Infof("acquiring lock: %+v", spec) glog.Infof("acquiring lock: %+v", spec)
releaser, err := mutex.Acquire(spec) releaser, err := mutex.Acquire(spec)
if err != nil { if err != nil {
......
...@@ -119,7 +119,7 @@ func PopulateFromSettings(cfg *Settings, apiCfg *api.Config) error { ...@@ -119,7 +119,7 @@ func PopulateFromSettings(cfg *Settings, apiCfg *api.Config) error {
// activeContext is true when minikube is the CurrentContext // activeContext is true when minikube is the CurrentContext
// If no CurrentContext is set, the given name will be used. // If no CurrentContext is set, the given name will be used.
func Update(kcs *Settings) error { func Update(kcs *Settings) error {
spec := lock.UserMutexSpec(filepath.Join(kcs.filePath(), "settings.Update")) spec := lock.PathMutexSpec(filepath.Join(kcs.filePath(), "settings.Update"))
glog.Infof("acquiring lock: %+v", spec) glog.Infof("acquiring lock: %+v", spec)
releaser, err := mutex.Acquire(spec) releaser, err := mutex.Acquire(spec)
if err != nil { if err != nil {
......
...@@ -17,12 +17,10 @@ limitations under the License. ...@@ -17,12 +17,10 @@ limitations under the License.
package lock package lock
import ( import (
"crypto/sha1"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"os/user"
"regexp"
"strings"
"time" "time"
"github.com/golang/glog" "github.com/golang/glog"
...@@ -31,42 +29,27 @@ import ( ...@@ -31,42 +29,27 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
var (
// nonString is characters to strip from lock names
nonString = regexp.MustCompile(`[\W_]+`)
// forceID is a user id for consistent testing
forceID = ""
)
// WriteFile decorates ioutil.WriteFile with a file lock and retry // WriteFile decorates ioutil.WriteFile with a file lock and retry
func WriteFile(filename string, data []byte, perm os.FileMode) error { func WriteFile(filename string, data []byte, perm os.FileMode) error {
spec := UserMutexSpec(filename) spec := PathMutexSpec(filename)
glog.Infof("acquiring lock for %s: %+v", filename, spec) glog.Infof("WriteFile acquiring %s: %+v", filename, spec)
releaser, err := mutex.Acquire(spec) releaser, err := mutex.Acquire(spec)
if err != nil { if err != nil {
return errors.Wrapf(err, "error acquiring lock for %s", filename) return errors.Wrapf(err, "failed to acquire lock for %s: %+v", filename, spec)
} }
defer releaser.Release() defer releaser.Release()
if err = ioutil.WriteFile(filename, data, perm); err != nil { if err = ioutil.WriteFile(filename, data, perm); err != nil {
return errors.Wrapf(err, "error writing file %s", filename) return errors.Wrapf(err, "writefile failed for %s", filename)
} }
return err return err
} }
// UserMutexSpec returns a mutex spec that will not collide with other users // PathMutexSpec returns a mutex spec for a path
func UserMutexSpec(path string) mutex.Spec { func PathMutexSpec(path string) mutex.Spec {
id := forceID
if forceID == "" {
u, err := user.Current()
if err == nil {
id = u.Uid
}
}
s := mutex.Spec{ s := mutex.Spec{
Name: getMutexNameForPath(fmt.Sprintf("%s-%s", path, id)), Name: fmt.Sprintf("mk%x", sha1.Sum([]byte(path)))[0:40],
Clock: clock.WallClock, Clock: clock.WallClock,
// Poll the lock twice a second // Poll the lock twice a second
Delay: 500 * time.Millisecond, Delay: 500 * time.Millisecond,
...@@ -75,16 +58,3 @@ func UserMutexSpec(path string) mutex.Spec { ...@@ -75,16 +58,3 @@ func UserMutexSpec(path string) mutex.Spec {
} }
return s return s
} }
func getMutexNameForPath(path string) string {
// juju requires that names match ^[a-zA-Z][a-zA-Z0-9-]*$", and be under 40 chars long.
n := strings.Trim(nonString.ReplaceAllString(path, "-"), "-")
// we need to always guarantee an alphanumeric prefix
prefix := "m"
// Prefer the last 40 chars, as paths tend get more specific toward the end
if len(n) >= 40 {
return prefix + n[len(n)-39:]
}
return prefix + n
}
...@@ -16,11 +16,13 @@ limitations under the License. ...@@ -16,11 +16,13 @@ limitations under the License.
package lock package lock
import "testing" import (
"testing"
func TestUserMutexSpec(t *testing.T) { "github.com/juju/mutex"
forceID = "test" )
func TestUserMutexSpec(t *testing.T) {
var tests = []struct { var tests = []struct {
description string description string
path string path string
...@@ -29,41 +31,53 @@ func TestUserMutexSpec(t *testing.T) { ...@@ -29,41 +31,53 @@ func TestUserMutexSpec(t *testing.T) {
{ {
description: "standard", description: "standard",
path: "/foo/bar", path: "/foo/bar",
expected: "mfoo-bar-test",
}, },
{ {
description: "deep directory", description: "deep directory",
path: "/foo/bar/baz/bat", path: "/foo/bar/baz/bat",
expected: "mfoo-bar-baz-bat-test",
}, },
{ {
description: "underscores", description: "underscores",
path: "/foo_bar/baz", path: "/foo_bar/baz",
expected: "mfoo-bar-baz-test",
}, },
{ {
description: "starts with number", description: "starts with number",
path: "/foo/2bar/baz", path: "/foo/2bar/baz",
expected: "mfoo-2bar-baz-test",
}, },
{ {
description: "starts with punctuation", description: "starts with punctuation",
path: "/.foo/bar", path: "/.foo/bar",
expected: "mfoo-bar-test",
}, },
{ {
description: "long filename", description: "long filename",
path: "/very-very-very-very-very-very-very-very-long/bar", path: "/very-very-very-very-very-very-very-very-long/bar",
expected: "m-very-very-very-very-very-long-bar-test", },
{
description: "Windows kubeconfig",
path: `C:\Users\admin/.kube/config`,
},
{
description: "Windows json",
path: `C:\Users\admin\.minikube\profiles\containerd-20191210T212325.7356633-8584\config.json`,
}, },
} }
seen := map[string]string{}
for _, tc := range tests { for _, tc := range tests {
t.Run(tc.description, func(t *testing.T) { t.Run(tc.description, func(t *testing.T) {
got := UserMutexSpec(tc.path) got := PathMutexSpec(tc.path)
if got.Name != tc.expected { if len(got.Name) != 40 {
t.Errorf("%s mutex name = %q, expected %q", tc.path, got.Name, tc.expected) t.Errorf("%s is not 40 chars long", got.Name)
}
if seen[got.Name] != "" {
t.Fatalf("lock name collision between %s and %s", tc.path, seen[got.Name])
}
m, err := mutex.Acquire(got)
if err != nil {
t.Errorf("acquire for spec %+v failed: %v", got, err)
} }
m.Release()
}) })
} }
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册