diff --git a/platform/os_linux.go b/platform/os_linux.go index facdf8701c..0bbe78893a 100644 --- a/platform/os_linux.go +++ b/platform/os_linux.go @@ -20,6 +20,8 @@ const ( CNMRuntimePath = "/var/lib/azure-network/" // CNIRuntimePath is the path where CNI state files are stored. CNIRuntimePath = "/var/run/" + // CNILockPath is the path where CNI lock files are stored. + CNILockPath = "/var/lock/azure-vnet/" // CNSRuntimePath is the path where CNS state files are stored. CNSRuntimePath = "/var/run/" // CNI runtime path on a Kubernetes cluster diff --git a/platform/os_windows.go b/platform/os_windows.go index 3a2e256243..051a32a6c5 100644 --- a/platform/os_windows.go +++ b/platform/os_windows.go @@ -23,6 +23,9 @@ const ( // CNIRuntimePath is the path where CNI state files are stored. CNIRuntimePath = "" + // CNILockPath is the path where CNI lock files are stored. + CNILockPath = "" + // CNI runtime path on a Kubernetes cluster K8SCNIRuntimePath = "C:\\k\\azurecni\\bin" diff --git a/store/json.go b/store/json.go index ca1a9f65d4..6531b2b36a 100644 --- a/store/json.go +++ b/store/json.go @@ -34,6 +34,7 @@ const ( // jsonFileStore is an implementation of KeyValueStore using a local JSON file. type jsonFileStore struct { fileName string + lockFileName string data map[string]*json.RawMessage inSync bool locked bool @@ -46,8 +47,16 @@ func NewJsonFileStore(fileName string) (KeyValueStore, error) { fileName = defaultFileName } + if platform.CNILockPath != "" { + err := os.MkdirAll(platform.CNILockPath, os.FileMode(0664)) + if err != nil { + return nil, err + } + } + kvs := &jsonFileStore{ fileName: fileName, + lockFileName: platform.CNILockPath + filepath.Base(fileName) + lockExtension, data: make(map[string]*json.RawMessage), } @@ -166,7 +175,6 @@ func (kvs *jsonFileStore) Lock(block bool) error { var lockFile *os.File var err error - lockName := kvs.fileName + lockExtension lockPerm := os.FileMode(0664) + os.FileMode(os.ModeExclusive) // Try to acquire the lock file. @@ -174,7 +182,7 @@ func (kvs *jsonFileStore) Lock(block bool) error { var modTimeCur time.Time var modTimePrev time.Time for lockRetryCount < lockMaxRetries { - lockFile, err = os.OpenFile(lockName, os.O_CREATE|os.O_EXCL|os.O_RDWR, lockPerm) + lockFile, err = os.OpenFile(kvs.lockFileName, os.O_CREATE|os.O_EXCL|os.O_RDWR, lockPerm) if err == nil { break } @@ -184,7 +192,7 @@ func (kvs *jsonFileStore) Lock(block bool) error { } // Reset the lock retry count if the timestamp for the lock file changes. - if fileInfo, err := os.Stat(lockName); err == nil { + if fileInfo, err := os.Stat(kvs.lockFileName); err == nil { modTimeCur = fileInfo.ModTime() if !modTimeCur.Equal(modTimePrev) { lockRetryCount = 0 @@ -222,7 +230,7 @@ func (kvs *jsonFileStore) Unlock(forceUnlock bool) error { return ErrStoreNotLocked } - err := os.Remove(kvs.fileName + lockExtension) + err := os.Remove(kvs.lockFileName) if err != nil { return err } @@ -252,19 +260,17 @@ func (kvs *jsonFileStore) GetLockFileModificationTime() (time.Time, error) { kvs.Mutex.Lock() defer kvs.Mutex.Unlock() - lockFileName := kvs.fileName + lockExtension - // Check if the file exists. - file, err := os.Open(lockFileName) + file, err := os.Open(kvs.lockFileName) if err != nil { return time.Time{}.UTC(), err } defer file.Close() - info, err := os.Stat(lockFileName) + info, err := os.Stat(kvs.lockFileName) if err != nil { - log.Printf("os.stat() for file %v failed: %v", lockFileName, err) + log.Printf("os.stat() for file %v failed: %v", kvs.lockFileName, err) return time.Time{}.UTC(), err } @@ -272,7 +278,7 @@ func (kvs *jsonFileStore) GetLockFileModificationTime() (time.Time, error) { } func (kvs *jsonFileStore) GetLockFileName() string { - return kvs.fileName + lockExtension + return kvs.lockFileName } func (kvs *jsonFileStore) Remove() { diff --git a/store/json_test.go b/store/json_test.go index 39c8a8af64..aa6cfc36d3 100644 --- a/store/json_test.go +++ b/store/json_test.go @@ -5,6 +5,7 @@ package store import ( "os" + "runtime" "strings" "testing" ) @@ -12,7 +13,7 @@ import ( const ( // File name used for test store. testFileName = "test.json" - + testLockFileName = "locktest.json" // Keys used during tests. testKey1 = "key1" testKey2 = "key2" @@ -206,3 +207,36 @@ func TestLockingStoreGivesExclusiveAccess(t *testing.T) { // Cleanup. os.Remove(testFileName) } + +// test case for testing newjsonfilestore idempotent +func TestNewJsonFileStoreIdempotent(t *testing.T) { + _, err := NewJsonFileStore(testLockFileName) + if err != nil { + t.Errorf("Failed to initialize store: %v", err) + } + + _, err = NewJsonFileStore(testLockFileName) + if err != nil { + t.Errorf("Failed to initialize same store second time: %v", err) + } +} + +// test case for checking if lockfilepath is expected +func TestLockFilePath(t *testing.T) { + store, err := NewJsonFileStore(testLockFileName) + if err != nil { + t.Errorf("Failed to initialize store: %v", err) + } + + lockFileName := store.GetLockFileName() + + if runtime.GOOS == "linux" { + if lockFileName != "/var/lock/azure-vnet/" + testLockFileName + ".lock" { + t.Errorf("Not expected file lock name: %v", lockFileName) + } + } else { + if lockFileName != testLockFileName + ".lock" { + t.Errorf("Not expected lockfilename: %v", lockFileName) + } + } +}