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 @@ ...@@ -17,10 +17,28 @@
package node package node
import ( import (
"errors"
"fmt" "fmt"
"reflect" "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 // DuplicateServiceError is returned during Node startup if a registered service
// constructor returns a service of the same type that was already started. // constructor returns a service of the same type that was already started.
type DuplicateServiceError struct { type DuplicateServiceError struct {
......
...@@ -25,7 +25,6 @@ import ( ...@@ -25,7 +25,6 @@ import (
"reflect" "reflect"
"strings" "strings"
"sync" "sync"
"syscall"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
...@@ -34,16 +33,7 @@ import ( ...@@ -34,16 +33,7 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/syndtr/goleveldb/leveldb/storage" "github.com/prometheus/prometheus/util/flock"
)
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}
) )
// Node is a container on which services can be registered. // Node is a container on which services can be registered.
...@@ -52,8 +42,8 @@ type Node struct { ...@@ -52,8 +42,8 @@ type Node struct {
config *Config config *Config
accman *accounts.Manager accman *accounts.Manager
ephemeralKeystore string // if non-empty, the key directory that will be removed by Stop 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 serverConfig p2p.Config
server *p2p.Server // Currently running P2P networking layer server *p2p.Server // Currently running P2P networking layer
...@@ -197,10 +187,7 @@ func (n *Node) Start() error { ...@@ -197,10 +187,7 @@ func (n *Node) Start() error {
running.Protocols = append(running.Protocols, service.Protocols()...) running.Protocols = append(running.Protocols, service.Protocols()...)
} }
if err := running.Start(); err != nil { if err := running.Start(); err != nil {
if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] { return convertFileLockError(err)
return ErrDatadirUsed
}
return err
} }
// Start each of the services // Start each of the services
started := []reflect.Type{} started := []reflect.Type{}
...@@ -242,14 +229,13 @@ func (n *Node) openDataDir() error { ...@@ -242,14 +229,13 @@ func (n *Node) openDataDir() error {
if err := os.MkdirAll(instdir, 0700); err != nil { if err := os.MkdirAll(instdir, 0700); err != nil {
return err return err
} }
// Try to open the instance directory as LevelDB storage. This creates a lock file // Lock the instance directory to prevent concurrent use by another instance as well as
// which prevents concurrent use by another instance as well as accidental use of the // accidental use of the instance directory as a database.
// instance directory as a database. release, _, err := flock.New(filepath.Join(instdir, "LOCK"))
storage, err := storage.OpenFile(instdir, true)
if err != nil { if err != nil {
return err return convertFileLockError(err)
} }
n.instanceDirLock = storage n.instanceDirLock = release
return nil return nil
} }
...@@ -509,7 +495,9 @@ func (n *Node) Stop() error { ...@@ -509,7 +495,9 @@ func (n *Node) Stop() error {
// Release instance directory lock. // Release instance directory lock.
if n.instanceDirLock != nil { 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 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 @@ ...@@ -255,6 +255,12 @@
"revision": "bf27d3ba8e1d9899d45a457ffac16c953eb2d647", "revision": "bf27d3ba8e1d9899d45a457ffac16c953eb2d647",
"revisionTime": "2017-02-11T19:53:22Z" "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=", "checksumSHA1": "KAzbLjI9MzW2tjfcAsK75lVRp6I=",
"path": "github.com/rcrowley/go-metrics", "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