add DHCP IPAM plugin
The plugin binary actually functions in two modes. The first mode is a regular CNI plugin. The second mode (when stared with "daemon" arg) runs a DHCP client daemon. When executed as a CNI plugin, it issues an RPC request to the daemon for actual processing. The daemon is required since a DHCP lease needs to be maintained by periodically renewing it. One instance of the daemon can server arbitrary number of containers/leases.
This commit is contained in:
190
Godeps/_workspace/src/github.com/coreos/rkt/pkg/lock/dir.go
generated
vendored
190
Godeps/_workspace/src/github.com/coreos/rkt/pkg/lock/dir.go
generated
vendored
@ -1,190 +0,0 @@
|
||||
// Copyright 2014 CoreOS, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package lock implements simple locking primitives on a
|
||||
// regular file or directory using flock
|
||||
package lock
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrLocked = errors.New("file already locked")
|
||||
ErrNotExist = errors.New("file does not exist")
|
||||
ErrPermission = errors.New("permission denied")
|
||||
ErrNotRegular = errors.New("not a regular file")
|
||||
)
|
||||
|
||||
// FileLock represents a lock on a regular file or a directory
|
||||
type FileLock struct {
|
||||
path string
|
||||
fd int
|
||||
}
|
||||
|
||||
type LockType int
|
||||
|
||||
const (
|
||||
Dir LockType = iota
|
||||
RegFile
|
||||
)
|
||||
|
||||
// TryExclusiveLock takes an exclusive lock without blocking.
|
||||
// This is idempotent when the Lock already represents an exclusive lock,
|
||||
// and tries promote a shared lock to exclusive atomically.
|
||||
// It will return ErrLocked if any lock is already held.
|
||||
func (l *FileLock) TryExclusiveLock() error {
|
||||
err := syscall.Flock(l.fd, syscall.LOCK_EX|syscall.LOCK_NB)
|
||||
if err == syscall.EWOULDBLOCK {
|
||||
err = ErrLocked
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// TryExclusiveLock takes an exclusive lock on a file/directory without blocking.
|
||||
// It will return ErrLocked if any lock is already held on the file/directory.
|
||||
func TryExclusiveLock(path string, lockType LockType) (*FileLock, error) {
|
||||
l, err := NewLock(path, lockType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = l.TryExclusiveLock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l, err
|
||||
}
|
||||
|
||||
// ExclusiveLock takes an exclusive lock.
|
||||
// This is idempotent when the Lock already represents an exclusive lock,
|
||||
// and promotes a shared lock to exclusive atomically.
|
||||
// It will block if an exclusive lock is already held.
|
||||
func (l *FileLock) ExclusiveLock() error {
|
||||
return syscall.Flock(l.fd, syscall.LOCK_EX)
|
||||
}
|
||||
|
||||
// ExclusiveLock takes an exclusive lock on a file/directory.
|
||||
// It will block if an exclusive lock is already held on the file/directory.
|
||||
func ExclusiveLock(path string, lockType LockType) (*FileLock, error) {
|
||||
l, err := NewLock(path, lockType)
|
||||
if err == nil {
|
||||
err = l.ExclusiveLock()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// TrySharedLock takes a co-operative (shared) lock without blocking.
|
||||
// This is idempotent when the Lock already represents a shared lock,
|
||||
// and tries demote an exclusive lock to shared atomically.
|
||||
// It will return ErrLocked if an exclusive lock already exists.
|
||||
func (l *FileLock) TrySharedLock() error {
|
||||
err := syscall.Flock(l.fd, syscall.LOCK_SH|syscall.LOCK_NB)
|
||||
if err == syscall.EWOULDBLOCK {
|
||||
err = ErrLocked
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// TrySharedLock takes a co-operative (shared) lock on a file/directory without blocking.
|
||||
// It will return ErrLocked if an exclusive lock already exists on the file/directory.
|
||||
func TrySharedLock(path string, lockType LockType) (*FileLock, error) {
|
||||
l, err := NewLock(path, lockType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = l.TrySharedLock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// SharedLock takes a co-operative (shared) lock on.
|
||||
// This is idempotent when the Lock already represents a shared lock,
|
||||
// and demotes an exclusive lock to shared atomically.
|
||||
// It will block if an exclusive lock is already held.
|
||||
func (l *FileLock) SharedLock() error {
|
||||
return syscall.Flock(l.fd, syscall.LOCK_SH)
|
||||
}
|
||||
|
||||
// SharedLock takes a co-operative (shared) lock on a file/directory.
|
||||
// It will block if an exclusive lock is already held on the file/directory.
|
||||
func SharedLock(path string, lockType LockType) (*FileLock, error) {
|
||||
l, err := NewLock(path, lockType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = l.SharedLock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// Unlock unlocks the lock
|
||||
func (l *FileLock) Unlock() error {
|
||||
return syscall.Flock(l.fd, syscall.LOCK_UN)
|
||||
}
|
||||
|
||||
// Fd returns the lock's file descriptor, or an error if the lock is closed
|
||||
func (l *FileLock) Fd() (int, error) {
|
||||
var err error
|
||||
if l.fd == -1 {
|
||||
err = errors.New("lock closed")
|
||||
}
|
||||
return l.fd, err
|
||||
}
|
||||
|
||||
// Close closes the lock which implicitly unlocks it as well
|
||||
func (l *FileLock) Close() error {
|
||||
fd := l.fd
|
||||
l.fd = -1
|
||||
return syscall.Close(fd)
|
||||
}
|
||||
|
||||
// NewLock opens a new lock on a file without acquisition
|
||||
func NewLock(path string, lockType LockType) (*FileLock, error) {
|
||||
l := &FileLock{path: path, fd: -1}
|
||||
|
||||
mode := syscall.O_RDONLY | syscall.O_CLOEXEC
|
||||
if lockType == Dir {
|
||||
mode |= syscall.O_DIRECTORY
|
||||
}
|
||||
lfd, err := syscall.Open(l.path, mode, 0)
|
||||
if err != nil {
|
||||
if err == syscall.ENOENT {
|
||||
err = ErrNotExist
|
||||
} else if err == syscall.EACCES {
|
||||
err = ErrPermission
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
l.fd = lfd
|
||||
|
||||
var stat syscall.Stat_t
|
||||
err = syscall.Fstat(lfd, &stat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Check if the file is a regular file
|
||||
if lockType == RegFile && !(stat.Mode&syscall.S_IFMT == syscall.S_IFREG) {
|
||||
return nil, ErrNotRegular
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
156
Godeps/_workspace/src/github.com/coreos/rkt/pkg/lock/dir_test.go
generated
vendored
156
Godeps/_workspace/src/github.com/coreos/rkt/pkg/lock/dir_test.go
generated
vendored
@ -1,156 +0,0 @@
|
||||
// Copyright 2014 CoreOS, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package lock
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewLock(t *testing.T) {
|
||||
f, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating tmpfile: %v", err)
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
f.Close()
|
||||
|
||||
l, err := NewLock(f.Name(), RegFile)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating NewFileLock: %v", err)
|
||||
}
|
||||
l.Close()
|
||||
|
||||
d, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating tmpdir: %v", err)
|
||||
}
|
||||
defer os.Remove(d)
|
||||
|
||||
l, err = NewLock(d, Dir)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating NewLock: %v", err)
|
||||
}
|
||||
|
||||
err = l.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("error unlocking lock: %v", err)
|
||||
}
|
||||
|
||||
if err = os.Remove(d); err != nil {
|
||||
t.Fatalf("error removing tmpdir: %v", err)
|
||||
}
|
||||
|
||||
l, err = NewLock(d, Dir)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error creating lock on nonexistent path")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExclusiveLock(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating tmpdir: %v", err)
|
||||
}
|
||||
defer os.Remove(dir)
|
||||
|
||||
// Set up the initial exclusive lock
|
||||
l, err := ExclusiveLock(dir, Dir)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating lock: %v", err)
|
||||
}
|
||||
|
||||
// reacquire the exclusive lock using the receiver interface
|
||||
err = l.TryExclusiveLock()
|
||||
if err != nil {
|
||||
t.Fatalf("error reacquiring exclusive lock: %v", err)
|
||||
}
|
||||
|
||||
// Now try another exclusive lock, should fail
|
||||
_, err = TryExclusiveLock(dir, Dir)
|
||||
if err == nil {
|
||||
t.Fatalf("expected err trying exclusive lock")
|
||||
}
|
||||
|
||||
// Unlock the original lock
|
||||
err = l.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("error closing lock: %v", err)
|
||||
}
|
||||
|
||||
// Now another exclusive lock should succeed
|
||||
_, err = TryExclusiveLock(dir, Dir)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating lock: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSharedLock(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating tmpdir: %v", err)
|
||||
}
|
||||
defer os.Remove(dir)
|
||||
|
||||
// Set up the initial shared lock
|
||||
l1, err := SharedLock(dir, Dir)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating new shared lock: %v", err)
|
||||
}
|
||||
|
||||
err = l1.TrySharedLock()
|
||||
if err != nil {
|
||||
t.Fatalf("error reacquiring shared lock: %v", err)
|
||||
}
|
||||
|
||||
// Subsequent shared locks should succeed
|
||||
l2, err := TrySharedLock(dir, Dir)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating shared lock: %v", err)
|
||||
}
|
||||
l3, err := TrySharedLock(dir, Dir)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating shared lock: %v", err)
|
||||
}
|
||||
|
||||
// But an exclusive lock should fail
|
||||
_, err = TryExclusiveLock(dir, Dir)
|
||||
if err == nil {
|
||||
t.Fatal("expected exclusive lock to fail")
|
||||
}
|
||||
|
||||
// Close the locks
|
||||
err = l1.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("error closing lock: %v", err)
|
||||
}
|
||||
err = l2.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("error closing lock: %v", err)
|
||||
}
|
||||
|
||||
// Only unlock one of them
|
||||
err = l3.Unlock()
|
||||
if err != nil {
|
||||
t.Fatalf("error unlocking lock: %v", err)
|
||||
}
|
||||
|
||||
// Now try an exclusive lock, should succeed
|
||||
_, err = TryExclusiveLock(dir, Dir)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating lock: %v", err)
|
||||
}
|
||||
}
|
272
Godeps/_workspace/src/github.com/coreos/rkt/pkg/lock/keylock.go
generated
vendored
272
Godeps/_workspace/src/github.com/coreos/rkt/pkg/lock/keylock.go
generated
vendored
@ -1,272 +0,0 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package lock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultDirPerm os.FileMode = 0660
|
||||
defaultFilePerm os.FileMode = 0660
|
||||
defaultLockRetries = 3
|
||||
)
|
||||
|
||||
type keyLockMode uint
|
||||
|
||||
const (
|
||||
keyLockExclusive keyLockMode = 1 << iota
|
||||
keyLockShared
|
||||
keyLockNonBlocking
|
||||
)
|
||||
|
||||
// KeyLock is a lock for a specific key. The lock file is created inside a
|
||||
// directory using the key name.
|
||||
// This is useful when multiple processes want to take a lock but cannot use
|
||||
// FileLock as they don't have a well defined file on the filesystem.
|
||||
// key value must be a valid file name (as the lock file is named after the key
|
||||
// value).
|
||||
type KeyLock struct {
|
||||
lockDir string
|
||||
key string
|
||||
// The lock on the key
|
||||
keyLock *FileLock
|
||||
}
|
||||
|
||||
// NewKeyLock returns a KeyLock for the specified key without acquisition.
|
||||
// lockdir is the directory where the lock file will be created. If lockdir
|
||||
// doesn't exists it will be created.
|
||||
// key value must be a valid file name (as the lock file is named after the key
|
||||
// value).
|
||||
func NewKeyLock(lockDir string, key string) (*KeyLock, error) {
|
||||
err := os.MkdirAll(lockDir, defaultDirPerm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyLockFile := filepath.Join(lockDir, key)
|
||||
// create the file if it doesn't exists
|
||||
f, err := os.OpenFile(keyLockFile, os.O_RDONLY|os.O_CREATE, defaultFilePerm)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating key lock file: %v", err)
|
||||
}
|
||||
f.Close()
|
||||
keyLock, err := NewLock(keyLockFile, RegFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error opening key lock file: %v", err)
|
||||
}
|
||||
return &KeyLock{lockDir: lockDir, key: key, keyLock: keyLock}, nil
|
||||
}
|
||||
|
||||
// Close closes the key lock which implicitly unlocks it as well
|
||||
func (l *KeyLock) Close() {
|
||||
l.keyLock.Close()
|
||||
}
|
||||
|
||||
// TryExclusiveLock takes an exclusive lock on a key without blocking.
|
||||
// This is idempotent when the KeyLock already represents an exclusive lock,
|
||||
// and tries promote a shared lock to exclusive atomically.
|
||||
// It will return ErrLocked if any lock is already held on the key.
|
||||
func (l *KeyLock) TryExclusiveKeyLock() error {
|
||||
return l.lock(keyLockExclusive|keyLockNonBlocking, defaultLockRetries)
|
||||
}
|
||||
|
||||
// TryExclusiveLock takes an exclusive lock on the key without blocking.
|
||||
// lockDir is the directory where the lock file will be created.
|
||||
// It will return ErrLocked if any lock is already held.
|
||||
func TryExclusiveKeyLock(lockDir string, key string) (*KeyLock, error) {
|
||||
return createAndLock(lockDir, key, keyLockExclusive|keyLockNonBlocking)
|
||||
}
|
||||
|
||||
// ExclusiveLock takes an exclusive lock on a key.
|
||||
// This is idempotent when the KeyLock already represents an exclusive lock,
|
||||
// and promotes a shared lock to exclusive atomically.
|
||||
// It will block if an exclusive lock is already held on the key.
|
||||
func (l *KeyLock) ExclusiveKeyLock() error {
|
||||
return l.lock(keyLockExclusive, defaultLockRetries)
|
||||
}
|
||||
|
||||
// ExclusiveLock takes an exclusive lock on a key.
|
||||
// lockDir is the directory where the lock file will be created.
|
||||
// It will block if an exclusive lock is already held on the key.
|
||||
func ExclusiveKeyLock(lockDir string, key string) (*KeyLock, error) {
|
||||
return createAndLock(lockDir, key, keyLockExclusive)
|
||||
}
|
||||
|
||||
// TrySharedLock takes a co-operative (shared) lock on the key without blocking.
|
||||
// This is idempotent when the KeyLock already represents a shared lock,
|
||||
// and tries demote an exclusive lock to shared atomically.
|
||||
// It will return ErrLocked if an exclusive lock already exists on the key.
|
||||
func (l *KeyLock) TrySharedKeyLock() error {
|
||||
return l.lock(keyLockShared|keyLockNonBlocking, defaultLockRetries)
|
||||
}
|
||||
|
||||
// TrySharedLock takes a co-operative (shared) lock on a key without blocking.
|
||||
// lockDir is the directory where the lock file will be created.
|
||||
// It will return ErrLocked if an exclusive lock already exists on the key.
|
||||
func TrySharedKeyLock(lockDir string, key string) (*KeyLock, error) {
|
||||
return createAndLock(lockDir, key, keyLockShared|keyLockNonBlocking)
|
||||
}
|
||||
|
||||
// SharedLock takes a co-operative (shared) lock on a key.
|
||||
// This is idempotent when the KeyLock already represents a shared lock,
|
||||
// and demotes an exclusive lock to shared atomically.
|
||||
// It will block if an exclusive lock is already held on the key.
|
||||
func (l *KeyLock) SharedKeyLock() error {
|
||||
return l.lock(keyLockShared, defaultLockRetries)
|
||||
}
|
||||
|
||||
// SharedLock takes a co-operative (shared) lock on a key.
|
||||
// lockDir is the directory where the lock file will be created.
|
||||
// It will block if an exclusive lock is already held on the key.
|
||||
func SharedKeyLock(lockDir string, key string) (*KeyLock, error) {
|
||||
return createAndLock(lockDir, key, keyLockShared)
|
||||
}
|
||||
|
||||
func createAndLock(lockDir string, key string, mode keyLockMode) (*KeyLock, error) {
|
||||
keyLock, err := NewKeyLock(lockDir, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = keyLock.lock(mode, defaultLockRetries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return keyLock, nil
|
||||
}
|
||||
|
||||
// lock is the base function to take a lock and handle changed lock files
|
||||
// As there's the need to remove unused (see CleanKeyLocks) lock files without
|
||||
// races, a changed file detection is needed.
|
||||
//
|
||||
// Without changed file detection this can happen:
|
||||
//
|
||||
// Process A takes exclusive lock on file01
|
||||
// Process B waits for exclusive lock on file01.
|
||||
// Process A deletes file01 and then releases the lock.
|
||||
// Process B takes the lock on the removed file01 as it has the fd opened
|
||||
// Process C comes, creates the file as it doesn't exists, and it also takes an exclusive lock.
|
||||
// Now B and C thinks to own an exclusive lock.
|
||||
//
|
||||
// maxRetries can be passed, useful for testing.
|
||||
func (l *KeyLock) lock(mode keyLockMode, maxRetries int) error {
|
||||
retries := 0
|
||||
for {
|
||||
var err error
|
||||
var isExclusive bool
|
||||
var isNonBlocking bool
|
||||
if mode&keyLockExclusive != 0 {
|
||||
isExclusive = true
|
||||
}
|
||||
if mode&keyLockNonBlocking != 0 {
|
||||
isNonBlocking = true
|
||||
}
|
||||
switch {
|
||||
case isExclusive && !isNonBlocking:
|
||||
err = l.keyLock.ExclusiveLock()
|
||||
case isExclusive && isNonBlocking:
|
||||
err = l.keyLock.TryExclusiveLock()
|
||||
case !isExclusive && !isNonBlocking:
|
||||
err = l.keyLock.SharedLock()
|
||||
case !isExclusive && isNonBlocking:
|
||||
err = l.keyLock.TrySharedLock()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check that the file referenced by the lock fd is the same as
|
||||
// the current file on the filesystem
|
||||
var lockStat, curStat syscall.Stat_t
|
||||
lfd, err := l.keyLock.Fd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = syscall.Fstat(lfd, &lockStat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
keyLockFile := filepath.Join(l.lockDir, l.key)
|
||||
fd, err := syscall.Open(keyLockFile, syscall.O_RDONLY, 0)
|
||||
// If there's an error opening the file return an error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = syscall.Fstat(fd, &curStat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if lockStat.Ino == curStat.Ino && lockStat.Dev == curStat.Dev {
|
||||
return nil
|
||||
}
|
||||
if retries >= maxRetries {
|
||||
return fmt.Errorf("cannot acquire lock after %d retries", retries)
|
||||
}
|
||||
|
||||
// If the file has changed discard this lock and try to take another lock.
|
||||
l.keyLock.Close()
|
||||
nl, err := NewKeyLock(l.lockDir, l.key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.keyLock = nl.keyLock
|
||||
|
||||
retries++
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock unlocks the key lock.
|
||||
func (l *KeyLock) Unlock() error {
|
||||
err := l.keyLock.Unlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanKeyLocks remove lock files from the lockDir.
|
||||
// For every key it tries to take an Exclusive lock on it and skip it if it
|
||||
// fails with ErrLocked
|
||||
func CleanKeyLocks(lockDir string) error {
|
||||
f, err := os.Open(lockDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening lockDir: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
files, err := f.Readdir(0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting lock files list: %v", err)
|
||||
}
|
||||
for _, f := range files {
|
||||
filename := filepath.Join(lockDir, f.Name())
|
||||
keyLock, err := TryExclusiveKeyLock(lockDir, f.Name())
|
||||
if err == ErrLocked {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Remove(filename)
|
||||
if err != nil {
|
||||
keyLock.Close()
|
||||
return fmt.Errorf("error removing lock file: %v", err)
|
||||
}
|
||||
keyLock.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
203
Godeps/_workspace/src/github.com/coreos/rkt/pkg/lock/keylock_test.go
generated
vendored
203
Godeps/_workspace/src/github.com/coreos/rkt/pkg/lock/keylock_test.go
generated
vendored
@ -1,203 +0,0 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package lock
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExclusiveKeyLock(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating tmpdir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
l1, err := ExclusiveKeyLock(dir, "key01")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating key lock: %v", err)
|
||||
}
|
||||
|
||||
_, err = TryExclusiveKeyLock(dir, "key01")
|
||||
if err == nil {
|
||||
t.Fatalf("expected err trying exclusive key lock")
|
||||
}
|
||||
|
||||
l1.Close()
|
||||
}
|
||||
|
||||
func TestCleanKeyLocks(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating tmpdir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
l1, err := ExclusiveKeyLock(dir, "key01")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating keyLock: %v", err)
|
||||
}
|
||||
|
||||
err = CleanKeyLocks(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
filesnum, err := countFiles(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if filesnum != 1 {
|
||||
t.Fatalf("expected 1 file in lock dir. found %d files", filesnum)
|
||||
}
|
||||
|
||||
l2, err := SharedKeyLock(dir, "key02")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating keyLock: %v", err)
|
||||
}
|
||||
|
||||
l1.Close()
|
||||
l2.Close()
|
||||
|
||||
err = CleanKeyLocks(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
filesnum, err = countFiles(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if filesnum != 0 {
|
||||
t.Fatalf("expected empty lock dir. found %d files", filesnum)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileChangedLock(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating tmpdir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
l1, err := ExclusiveKeyLock(dir, "key01")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating keyLock: %v", err)
|
||||
}
|
||||
|
||||
l2, err := NewKeyLock(dir, "key01")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating keyLock: %v", err)
|
||||
}
|
||||
|
||||
// Simulate that l1 owner removes the actual key1 lock file
|
||||
err = os.Remove(filepath.Join(dir, "key01"))
|
||||
if err != nil {
|
||||
t.Fatalf("error creating NewLock: %v", err)
|
||||
}
|
||||
l1.Close()
|
||||
|
||||
// Now l2 owner takes a lock, using the fd of the removed file
|
||||
err = l2.lock(keyLockShared, 0)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error")
|
||||
}
|
||||
l2.Close()
|
||||
|
||||
// Do the same with a new file created after removal
|
||||
dir, err = ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating tmpdir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
l1, err = ExclusiveKeyLock(dir, "key01")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating keyLock: %v", err)
|
||||
}
|
||||
|
||||
l2, err = NewKeyLock(dir, "key01")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating keyLock: %v", err)
|
||||
}
|
||||
|
||||
// Simulate that l1 owner removes the actual key1 lock file
|
||||
err = os.Remove(filepath.Join(dir, "key01"))
|
||||
if err != nil {
|
||||
t.Fatalf("error creating NewLock: %v", err)
|
||||
}
|
||||
l1.Close()
|
||||
|
||||
// Simulate that another user comes and takes a lock, this will create
|
||||
// a new lock file as it was removed.
|
||||
l3, err := ExclusiveKeyLock(dir, "key01")
|
||||
l3.Close()
|
||||
|
||||
// Now l2 owner takes a lock, using the fd of the old file
|
||||
err = l2.lock(keyLockShared, 0)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error")
|
||||
}
|
||||
|
||||
// Do the same but with a retry so if should work.
|
||||
dir, err = ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating tmpdir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
l1, err = ExclusiveKeyLock(dir, "key01")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating keyLock: %v", err)
|
||||
}
|
||||
|
||||
l2, err = NewKeyLock(dir, "key01")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating keyLock: %v", err)
|
||||
}
|
||||
|
||||
// Simulate that l1 owner removes the actual key1 lock file
|
||||
err = os.Remove(filepath.Join(dir, "key01"))
|
||||
if err != nil {
|
||||
t.Fatalf("error creating NewLock: %v", err)
|
||||
}
|
||||
l1.Close()
|
||||
|
||||
// Simulate that another user comes and takes a lock, this will create
|
||||
// a new lock file as it was removed.
|
||||
l3, err = ExclusiveKeyLock(dir, "key01")
|
||||
l3.Close()
|
||||
|
||||
// Now l2 owner takes a lock, using the fd of the old file
|
||||
err = l2.lock(keyLockShared, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func countFiles(dir string) (int, error) {
|
||||
f, err := os.Open(dir)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
defer f.Close()
|
||||
files, err := f.Readdir(0)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return len(files), nil
|
||||
}
|
Reference in New Issue
Block a user