Commit f7e39a77 authored by Péter Szilágyi's avatar Péter Szilágyi Committed by GitHub

Merge pull request #15000 from fjl/node-flock

node: fix instance dir locking and improve error message
parents 79cdbcfe 7e57fee3
......@@ -17,10 +17,28 @@
package node
import (
"errors"
"fmt"
"reflect"
"syscall"
)
var (
ErrDatadirUsed = errors.New("datadir already used by another process")
ErrNodeStopped = errors.New("node not started")
ErrNodeRunning = errors.New("node already running")
ErrServiceUnknown = errors.New("unknown service")
datadirInUseErrnos = map[uint]bool{11: true, 32: true, 35: true}
)
func convertFileLockError(err error) error {
if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] {
return ErrDatadirUsed
}
return err
}
// DuplicateServiceError is returned during Node startup if a registered service
// constructor returns a service of the same type that was already started.
type DuplicateServiceError struct {
......
......@@ -25,7 +25,6 @@ import (
"reflect"
"strings"
"sync"
"syscall"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/ethdb"
......@@ -34,16 +33,7 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rpc"
"github.com/syndtr/goleveldb/leveldb/storage"
)
var (
ErrDatadirUsed = errors.New("datadir already used")
ErrNodeStopped = errors.New("node not started")
ErrNodeRunning = errors.New("node already running")
ErrServiceUnknown = errors.New("unknown service")
datadirInUseErrnos = map[uint]bool{11: true, 32: true, 35: true}
"github.com/prometheus/prometheus/util/flock"
)
// Node is a container on which services can be registered.
......@@ -53,7 +43,7 @@ type Node struct {
accman *accounts.Manager
ephemeralKeystore string // if non-empty, the key directory that will be removed by Stop
instanceDirLock storage.Storage // prevents concurrent use of instance directory
instanceDirLock flock.Releaser // prevents concurrent use of instance directory
serverConfig p2p.Config
server *p2p.Server // Currently running P2P networking layer
......@@ -197,10 +187,7 @@ func (n *Node) Start() error {
running.Protocols = append(running.Protocols, service.Protocols()...)
}
if err := running.Start(); err != nil {
if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] {
return ErrDatadirUsed
}
return err
return convertFileLockError(err)
}
// Start each of the services
started := []reflect.Type{}
......@@ -242,14 +229,13 @@ func (n *Node) openDataDir() error {
if err := os.MkdirAll(instdir, 0700); err != nil {
return err
}
// Try to open the instance directory as LevelDB storage. This creates a lock file
// which prevents concurrent use by another instance as well as accidental use of the
// instance directory as a database.
storage, err := storage.OpenFile(instdir, true)
// Lock the instance directory to prevent concurrent use by another instance as well as
// accidental use of the instance directory as a database.
release, _, err := flock.New(filepath.Join(instdir, "LOCK"))
if err != nil {
return err
return convertFileLockError(err)
}
n.instanceDirLock = storage
n.instanceDirLock = release
return nil
}
......@@ -509,7 +495,9 @@ func (n *Node) Stop() error {
// Release instance directory lock.
if n.instanceDirLock != nil {
n.instanceDirLock.Close()
if err := n.instanceDirLock.Release(); err != nil {
log.Error("Can't release datadir lock", "err", err)
}
n.instanceDirLock = nil
}
......
This diff is collapsed.
The Prometheus systems and service monitoring server
Copyright 2012-2015 The Prometheus Authors
This product includes software developed at
SoundCloud Ltd. (http://soundcloud.com/).
The following components are included in this product:
Bootstrap
http://getbootstrap.com
Copyright 2011-2014 Twitter, Inc.
Licensed under the MIT License
bootstrap3-typeahead.js
https://github.com/bassjobsen/Bootstrap-3-Typeahead
Original written by @mdo and @fat
Copyright 2014 Bass Jobsen @bassjobsen
Licensed under the Apache License, Version 2.0
fuzzy
https://github.com/mattyork/fuzzy
Original written by @mattyork
Copyright 2012 Matt York
Licensed under the MIT License
bootstrap-datetimepicker.js
https://github.com/Eonasdan/bootstrap-datetimepicker
Copyright 2015 Jonathan Peterson (@Eonasdan)
Licensed under the MIT License
moment.js
https://github.com/moment/moment/
Copyright JS Foundation and other contributors
Licensed under the MIT License
Rickshaw
https://github.com/shutterstock/rickshaw
Copyright 2011-2014 by Shutterstock Images, LLC
See https://github.com/shutterstock/rickshaw/blob/master/LICENSE for license details
mustache.js
https://github.com/janl/mustache.js
Copyright 2009 Chris Wanstrath (Ruby)
Copyright 2010-2014 Jan Lehnardt (JavaScript)
Copyright 2010-2015 The mustache.js community
Licensed under the MIT License
jQuery
https://jquery.org
Copyright jQuery Foundation and other contributors
Licensed under the MIT License
Go support for Protocol Buffers - Google's data interchange format
http://github.com/golang/protobuf/
Copyright 2010 The Go Authors
See source code for license details.
Go support for leveled logs, analogous to
https://code.google.com/p/google-glog/
Copyright 2013 Google Inc.
Licensed under the Apache License, Version 2.0
Support for streaming Protocol Buffer messages for the Go language (golang).
https://github.com/matttproud/golang_protobuf_extensions
Copyright 2013 Matt T. Proud
Licensed under the Apache License, Version 2.0
DNS library in Go
http://miek.nl/posts/2014/Aug/16/go-dns-package/
Copyright 2009 The Go Authors, 2011 Miek Gieben
See https://github.com/miekg/dns/blob/master/LICENSE for license details.
LevelDB key/value database in Go
https://github.com/syndtr/goleveldb
Copyright 2012 Suryandaru Triandana
See https://github.com/syndtr/goleveldb/blob/master/LICENSE for license details.
gosnappy - a fork of code.google.com/p/snappy-go
https://github.com/syndtr/gosnappy
Copyright 2011 The Snappy-Go Authors
See https://github.com/syndtr/gosnappy/blob/master/LICENSE for license details.
go-zookeeper - Native ZooKeeper client for Go
https://github.com/samuel/go-zookeeper
Copyright (c) 2013, Samuel Stauffer <samuel@descolada.com>
See https://github.com/samuel/go-zookeeper/blob/master/LICENSE for license details.
// Copyright 2016 The Prometheus Authors
// 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 flock provides portable file locking. It is essentially ripped out
// from the code of github.com/syndtr/goleveldb. Strange enough that the
// standard library does not provide this functionality. Once this package has
// proven to work as expected, we should probably turn it into a separate
// general purpose package for humanity.
package flock
import (
"os"
"path/filepath"
)
// Releaser provides the Release method to release a file lock.
type Releaser interface {
Release() error
}
// New locks the file with the provided name. If the file does not exist, it is
// created. The returned Releaser is used to release the lock. existed is true
// if the file to lock already existed. A non-nil error is returned if the
// locking has failed. Neither this function nor the returned Releaser is
// goroutine-safe.
func New(fileName string) (r Releaser, existed bool, err error) {
if err = os.MkdirAll(filepath.Dir(fileName), 0755); err != nil {
return
}
_, err = os.Stat(fileName)
existed = err == nil
r, err = newLock(fileName)
return
}
// Copyright 2016 The Prometheus Authors
// 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 flock
import "os"
type plan9Lock struct {
f *os.File
}
func (l *plan9Lock) Release() error {
return l.f.Close()
}
func newLock(fileName string) (Releaser, error) {
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, os.ModeExclusive|0644)
if err != nil {
return nil, err
}
return &plan9Lock{f}, nil
}
// Copyright 2016 The Prometheus Authors
// 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.
// +build solaris
package flock
import (
"os"
"syscall"
)
type unixLock struct {
f *os.File
}
func (l *unixLock) Release() error {
if err := l.set(false); err != nil {
return err
}
return l.f.Close()
}
func (l *unixLock) set(lock bool) error {
flock := syscall.Flock_t{
Type: syscall.F_UNLCK,
Start: 0,
Len: 0,
Whence: 1,
}
if lock {
flock.Type = syscall.F_WRLCK
}
return syscall.FcntlFlock(l.f.Fd(), syscall.F_SETLK, &flock)
}
func newLock(fileName string) (Releaser, error) {
f, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
return nil, err
}
l := &unixLock{f}
err = l.set(true)
if err != nil {
f.Close()
return nil, err
}
return l, nil
}
// Copyright 2016 The Prometheus Authors
// 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.
// +build darwin dragonfly freebsd linux netbsd openbsd
package flock
import (
"os"
"syscall"
)
type unixLock struct {
f *os.File
}
func (l *unixLock) Release() error {
if err := l.set(false); err != nil {
return err
}
return l.f.Close()
}
func (l *unixLock) set(lock bool) error {
how := syscall.LOCK_UN
if lock {
how = syscall.LOCK_EX
}
return syscall.Flock(int(l.f.Fd()), how|syscall.LOCK_NB)
}
func newLock(fileName string) (Releaser, error) {
f, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
return nil, err
}
l := &unixLock{f}
err = l.set(true)
if err != nil {
f.Close()
return nil, err
}
return l, nil
}
// Copyright 2016 The Prometheus Authors
// 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 flock
import "syscall"
type windowsLock struct {
fd syscall.Handle
}
func (fl *windowsLock) Release() error {
return syscall.Close(fl.fd)
}
func newLock(fileName string) (Releaser, error) {
pathp, err := syscall.UTF16PtrFromString(fileName)
if err != nil {
return nil, err
}
fd, err := syscall.CreateFile(pathp, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.CREATE_ALWAYS, syscall.FILE_ATTRIBUTE_NORMAL, 0)
if err != nil {
return nil, err
}
return &windowsLock{fd}, nil
}
......@@ -255,6 +255,12 @@
"revision": "bf27d3ba8e1d9899d45a457ffac16c953eb2d647",
"revisionTime": "2017-02-11T19:53:22Z"
},
{
"checksumSHA1": "WbbxCn2jUYIL5viqLo0BKXEdPrQ=",
"path": "github.com/prometheus/prometheus/util/flock",
"revision": "3101606756c53221ed58ba94ecba6b26adf89dcc",
"revisionTime": "2017-08-14T17:01:13Z"
},
{
"checksumSHA1": "KAzbLjI9MzW2tjfcAsK75lVRp6I=",
"path": "github.com/rcrowley/go-metrics",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment