Commit 169c3c0f authored by Cristian Maglie's avatar Cristian Maglie

Updated arduino-modules

parent 1dab88d0
...@@ -88,15 +88,15 @@ ...@@ -88,15 +88,15 @@
version = "v1.12.70" version = "v1.12.70"
[[projects]] [[projects]]
branch = "master" digest = "1:3e4acbb4ac485fafdff3f474a16ebb8787b157234971294ec40a48a4c0695c26"
digest = "1:6b3d113c33b20e4ad6fa722464d236342c8dded8a1de319b6fd0e7efc496ea3a"
name = "github.com/bcmi-labs/arduino-modules" name = "github.com/bcmi-labs/arduino-modules"
packages = [ packages = [
"fs", "fs",
"sketches", "sketches",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "61f5789e8c68edfe71adb6e2c7f529b43dbf3ce4" revision = "70b03afb360cbd7b6790aa831fdd92b30d1333fb"
version = "v1.1.5"
[[projects]] [[projects]]
branch = "master" branch = "master"
...@@ -211,6 +211,14 @@ ...@@ -211,6 +211,14 @@
revision = "a41e3c4b706f6ae8dfbff342b06e40fa4d2d0506" revision = "a41e3c4b706f6ae8dfbff342b06e40fa4d2d0506"
version = "v1.2.1" version = "v1.2.1"
[[projects]]
digest = "1:70f34ee6d1b188cbd2dfcde62b0e2c600262eb5a03c8a3f3435ecf7c74790470"
name = "github.com/gofrs/flock"
packages = ["."]
pruneopts = "UT"
revision = "7f43ea2e6a643ad441fc12d0ecc0d3388b300c53"
version = "v0.7.0"
[[projects]] [[projects]]
digest = "1:ffc060c551980d37ee9e428ef528ee2813137249ccebb0bfc412ef83071cac91" digest = "1:ffc060c551980d37ee9e428ef528ee2813137249ccebb0bfc412ef83071cac91"
name = "github.com/golang/protobuf" name = "github.com/golang/protobuf"
...@@ -255,11 +263,12 @@ ...@@ -255,11 +263,12 @@
revision = "0b12d6b5" revision = "0b12d6b5"
[[projects]] [[projects]]
digest = "1:70107cf7ee5eb9e3c3dabe65bcb220bff22ee42e32d9b7fca988e16b8727cacc" branch = "master"
digest = "1:7d1a655e1f16ccaf1adb026bdeece28499ecb0ead32d32accc6ed6f2c40cacee"
name = "github.com/juju/errors" name = "github.com/juju/errors"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "c7d06af17c68cd34c835053720b21f6549d9b0ee" revision = "089d3ea4e4d597bd98acac068193d341983326a3"
[[projects]] [[projects]]
digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67" digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67"
......
...@@ -44,8 +44,8 @@ ...@@ -44,8 +44,8 @@
name = "github.com/gosuri/uitable" name = "github.com/gosuri/uitable"
[[constraint]] [[constraint]]
branch = "master"
name = "github.com/bcmi-labs/arduino-modules" name = "github.com/bcmi-labs/arduino-modules"
version = "1.1.5"
[[constraint]] [[constraint]]
branch = "master" branch = "master"
......
...@@ -2,6 +2,7 @@ package fs ...@@ -2,6 +2,7 @@ package fs
import ( import (
"io/ioutil" "io/ioutil"
"mime"
"os" "os"
"path/filepath" "path/filepath"
...@@ -109,3 +110,29 @@ func (d *Disk) replaceCharacters(filename string) string { ...@@ -109,3 +110,29 @@ func (d *Disk) replaceCharacters(filename string) string {
} }
return filename return filename
} }
// ReadDir returns a list of file and directory in a directory
func (d *Disk) ReadDir(path string) ([]File, error) {
path = d.replaceCharacters(path)
path = filepath.Join(d.Base, path)
list := []File{}
iofiles, err := ioutil.ReadDir(path)
if err != nil && os.IsNotExist(err) {
return nil, errors.NewNotFound(err, path)
}
for _, f := range iofiles {
file := File{
Path: strings.Replace(path, d.Base+"/", "", 1),
Name: f.Name(),
Size: f.Size(),
LastModified: f.ModTime(),
IsDir: f.IsDir(),
Mime: mime.TypeByExtension(filepath.Ext(f.Name())),
}
list = append(list, file)
}
return list, err
}
...@@ -22,6 +22,7 @@ type File struct { ...@@ -22,6 +22,7 @@ type File struct {
Mime string `json:"mimetype,omitempty" gorethink:"mime"` Mime string `json:"mimetype,omitempty" gorethink:"mime"`
Size int64 `json:"size,omitempty" gorethink:"size"` Size int64 `json:"size,omitempty" gorethink:"size"`
LastModified time.Time `json:"last_modified,omitempty" gorethink:"last_modified"` LastModified time.Time `json:"last_modified,omitempty" gorethink:"last_modified"`
IsDir bool `json:"-"`
} }
// Reader allows to read the content of a file on a filesystem // Reader allows to read the content of a file on a filesystem
...@@ -70,10 +71,16 @@ type Lister interface { ...@@ -70,10 +71,16 @@ type Lister interface {
List(prefix string) ([]File, error) List(prefix string) ([]File, error)
} }
// DirReader allows to get a list of file or directory under a path
type DirReader interface {
ReadDir(path string) ([]File, error)
}
// Manager combines all the interfaces // Manager combines all the interfaces
type Manager interface { type Manager interface {
Reader Reader
Writer Writer
Remover Remover
Lister Lister
DirReader
} }
package fs
import (
"context"
"io/ioutil"
"mime"
"os"
"path/filepath"
"time"
"strings"
"github.com/gofrs/flock"
"github.com/juju/errors"
)
// HashDisk is a disk that spreads files in directories according to their name in order not to have a single folder with millions of subdirectories.
// For example the filename test/banana will be spread in te/st/test/banana
// Normally you would end up with an unbalanced distribution of folders, but since in real application filenames are already hashed (eg 6bf5ee0aea544986e84bf7abd5c96bde:matteosuppo/sketches/f7aca8a9-29f4-4420-9c7b-61d4e4064b84/sketch_sep25a/ReadMe.adoc), the problem is negligible
type HashDisk struct {
Base string
NameOverride string
CharacterMapping map[string]string
}
func (d *HashDisk) Name() string {
if d.NameOverride != "" {
return d.NameOverride
}
return "HashDisk"
}
// ReadFile reads the file named by filename and returns the contents.
// A successful call returns err == nil, not err == EOF.
// Because ReadFile reads the whole file, it does not treat an EOF from Read
// as an error to be reported.
// It returns a notfound error if the file is missing
func (d *HashDisk) ReadFile(filename string) ([]byte, error) {
hash := strings.Replace(filename, string(filepath.Separator), "", -1)
filename = filepath.Join(hash[0:2], hash[2:4], filename)
filename = d.replaceCharacters(filename)
filename = filepath.Join(d.Base, filename)
_, err := os.Stat(filename)
if err != nil {
return nil, errors.NewNotFound(err, filename)
}
fileLock, err := lock(filename)
if err != nil {
return nil, errors.Annotatef(err, "trylock %s", filename)
}
defer unlock(fileLock)
data, err := ioutil.ReadFile(filename)
if err != nil && os.IsNotExist(err) {
return nil, errors.NewNotFound(err, filename)
}
return data, err
}
// WriteFile writes data to a file named by filename.
// If the file does not exist, WriteFile creates it with permissions perm;
// otherwise WriteFile truncates it before writing.
func (d *HashDisk) WriteFile(filename string, data []byte, perm os.FileMode) error {
hash := strings.Replace(filename, string(filepath.Separator), "", -1)
filename = filepath.Join(hash[0:2], hash[2:4], filename)
filename = d.replaceCharacters(filename)
filename = filepath.Join(d.Base, filename)
fileLock, err := rlock(filename)
if err != nil {
return errors.Annotatef(err, "trylock %s", filename)
}
defer unlock(fileLock)
err = ioutil.WriteFile(filename, data, perm)
if err != nil {
return errors.Annotatef(err, "write %s", filename)
}
return nil
}
// MkdirAll creates a directory named path, along with any necessary parents,
// and returns nil, or else returns an error.
// The permission bits perm are used for all directories that MkdirAll creates.
// If path is already a directory, MkdirAll does nothing and returns nil.
func (d *HashDisk) MkdirAll(path string, perm os.FileMode) error {
hash := strings.Replace(path, string(filepath.Separator), "", -1)
path = filepath.Join(hash[0:2], hash[2:4], path)
path = d.replaceCharacters(path)
path = filepath.Join(d.Base, path)
return os.MkdirAll(path, perm)
}
// Remove removes the named file or directory (children included).
// It fails silently if the file doesn't exist
func (d *HashDisk) Remove(name string) error {
hash := strings.Replace(name, string(filepath.Separator), "", -1)
name = filepath.Join(hash[0:2], hash[2:4], name)
name = d.replaceCharacters(name)
name = filepath.Join(d.Base, name)
stat, err := os.Stat(name)
if err != nil {
return nil // If there's no file, there's no reason to fail
}
lockfile := name
if stat.IsDir() {
lockfile = lockfile + ".lock"
}
fileLock, err := lock(lockfile)
if err != nil {
return errors.Annotatef(err, "trylock %s", name)
}
defer unlock(fileLock)
defer os.RemoveAll(lockfile)
err = os.RemoveAll(name)
if err != nil && os.IsNotExist(err) {
return nil
}
return err
}
// List returns a list of files in a directory
func (d *HashDisk) List(prefix string) ([]File, error) {
hash := strings.Replace(prefix, string(filepath.Separator), "", -1)
prefix = filepath.Join(hash[0:2], hash[2:4], prefix)
prefix = d.replaceCharacters(prefix)
prefix = filepath.Join(d.Base, prefix)
list := []File{}
err := filepath.Walk(prefix, func(path string, f os.FileInfo, err error) error {
if err != nil {
return nil
}
if f.IsDir() {
return nil
}
list = append(list, File{
Path: strings.Replace(path, d.Base+"/", "", 1)[6:],
Name: filepath.Base(path),
Size: f.Size(),
})
return nil
})
return list, err
}
// replaceCharacters replaces the characters in the CharacterMapping
// from the filename/path given
func (d *HashDisk) replaceCharacters(filename string) string {
if d.CharacterMapping != nil {
for k, v := range d.CharacterMapping {
filename = strings.Replace(filename, k, v, -1)
}
}
return filename
}
// ReadDir returns a list of file contained under a path
func (d *HashDisk) ReadDir(path string) ([]File, error) {
hash := strings.Replace(path, string(filepath.Separator), "", -1)
path = filepath.Join(hash[0:2], hash[2:4], path)
path = d.replaceCharacters(path)
path = filepath.Join(d.Base, path)
iofiles, err := ioutil.ReadDir(path)
if err != nil && os.IsNotExist(err) {
return nil, errors.NewNotFound(err, path)
}
var files []File
for _, f := range iofiles {
file := File{
Path: strings.Replace(path, d.Base+"/", "", 1)[6:] + "/" + f.Name(),
Name: f.Name(),
Size: f.Size(),
LastModified: f.ModTime(),
IsDir: f.IsDir(),
Mime: mime.TypeByExtension(filepath.Ext(f.Name())),
}
files = append(files, file)
}
return files, err
}
// lock attempts to lock the file for 10 seconds. If 10 seconds pass without success it returns an error
func lock(filename string) (*flock.Flock, error) {
fileLock := flock.NewFlock(filename)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
locked, err := fileLock.TryLockContext(ctx, 100*time.Millisecond)
if err != nil {
return nil, err
}
if !locked {
return nil, errors.New("could not lock")
}
return fileLock, nil
}
// rlock attempts to gain a shared lock for the given file for 10 seconds. If 10 seconds pass without success it returns an error
func rlock(filename string) (*flock.Flock, error) {
fileLock := flock.NewFlock(filename)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
locked, err := fileLock.TryRLockContext(ctx, 100*time.Millisecond)
if err != nil {
return nil, err
}
if !locked {
return nil, errors.New("could not rlock")
}
return fileLock, nil
}
// unlock panics so that the error is not swallowed by the defer
// if it panics it means something is very wrong indeed anyway
func unlock(lock *flock.Flock) {
err := lock.Unlock()
if err != nil {
panic(err)
}
}
...@@ -135,3 +135,8 @@ func (s *S3) List(prefix string) ([]File, error) { ...@@ -135,3 +135,8 @@ func (s *S3) List(prefix string) ([]File, error) {
} }
return list, nil return list, nil
} }
// ReadDir is just a empty code in order to implement fs.Manager interface, until we deprecate S3
func (s *S3) ReadDir(path string) ([]File, error) {
return nil, nil
}
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
language: go
go:
- 1.10.x
- 1.11.x
script: go test -v -check.vv -race ./...
sudo: false
notifications:
email:
on_success: never
on_failure: always
Copyright (c) 2015, Tim Heckman
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of linode-netint nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# flock
[![TravisCI Build Status](https://img.shields.io/travis/gofrs/flock/master.svg?style=flat)](https://travis-ci.org/gofrs/flock)
[![GoDoc](https://img.shields.io/badge/godoc-go--flock-blue.svg?style=flat)](https://godoc.org/github.com/gofrs/flock)
[![License](https://img.shields.io/badge/license-BSD_3--Clause-brightgreen.svg?style=flat)](https://github.com/gofrs/flock/blob/master/LICENSE)
`flock` implements a thread-safe sync.Locker interface for file locking. It also
includes a non-blocking TryLock() function to allow locking without blocking execution.
## License
`flock` is released under the BSD 3-Clause License. See the `LICENSE` file for more details.
## Go Compatibility
This package makes use of the `context` package that was introduced in Go 1.7. As such, this
package has an implicit dependency on Go 1.7+.
## Installation
```
go get -u github.com/gofrs/flock
```
## Usage
```Go
import "github.com/gofrs/flock"
fileLock := flock.New("/var/lock/go-lock.lock")
locked, err := fileLock.TryLock()
if err != nil {
// handle locking error
}
if locked {
// do work
fileLock.Unlock()
}
```
For more detailed usage information take a look at the package API docs on
[GoDoc](https://godoc.org/github.com/gofrs/flock).
version: '{build}'
build: false
deploy: false
clone_folder: 'c:\gopath\src\github.com\gofrs\flock'
environment:
GOPATH: 'c:\gopath'
GOVERSION: '1.11'
init:
- git config --global core.autocrlf input
install:
- rmdir c:\go /s /q
- appveyor DownloadFile https://storage.googleapis.com/golang/go%GOVERSION%.windows-amd64.msi
- msiexec /i go%GOVERSION%.windows-amd64.msi /q
- set Path=c:\go\bin;c:\gopath\bin;%Path%
- go version
- go env
test_script:
- go get -t ./...
- go test -race -v ./...
// Copyright 2015 Tim Heckman. All rights reserved.
// Use of this source code is governed by the BSD 3-Clause
// license that can be found in the LICENSE file.
// Package flock implements a thread-safe sync.Locker interface for file locking.
// It also includes a non-blocking TryLock() function to allow locking
// without blocking execution.
//
// Package flock is released under the BSD 3-Clause License. See the LICENSE file
// for more details.
//
// While using this library, remember that the locking behaviors are not
// guaranteed to be the same on each platform. For example, some UNIX-like
// operating systems will transparently convert a shared lock to an exclusive
// lock. If you Unlock() the flock from a location where you believe that you
// have the shared lock, you may accidently drop the exclusive lock.
package flock
import (
"context"
"os"
"sync"
"time"
)
// Flock is the struct type to handle file locking. All fields are unexported,
// with access to some of the fields provided by getter methods (Path() and Locked()).
type Flock struct {
path string
m sync.RWMutex
fh *os.File
l bool
r bool
}
// New returns a new instance of *Flock. The only parameter
// it takes is the path to the desired lockfile.
func New(path string) *Flock {
return &Flock{path: path}
}
// NewFlock returns a new instance of *Flock. The only parameter
// it takes is the path to the desired lockfile.
//
// Deprecated: Use New instead.
func NewFlock(path string) *Flock {
return New(path)
}
// Close is equivalent to calling Unlock.
//
// This will release the lock and close the underlying file descriptor.
// It will not remove the file from disk, that's up to your application.
func (f *Flock) Close() error {
return f.Unlock()
}
// Path returns the path as provided in NewFlock().
func (f *Flock) Path() string {
return f.path
}
// Locked returns the lock state (locked: true, unlocked: false).
//
// Warning: by the time you use the returned value, the state may have changed.
func (f *Flock) Locked() bool {
f.m.RLock()
defer f.m.RUnlock()
return f.l
}
// RLocked returns the read lock state (locked: true, unlocked: false).
//
// Warning: by the time you use the returned value, the state may have changed.
func (f *Flock) RLocked() bool {
f.m.RLock()
defer f.m.RUnlock()
return f.r
}
func (f *Flock) String() string {
return f.path
}
// TryLockContext repeatedly tries to take an exclusive lock until one of the
// conditions is met: TryLock succeeds, TryLock fails with error, or Context
// Done channel is closed.
func (f *Flock) TryLockContext(ctx context.Context, retryDelay time.Duration) (bool, error) {
return tryCtx(f.TryLock, ctx, retryDelay)
}
// TryRLockContext repeatedly tries to take a shared lock until one of the
// conditions is met: TryRLock succeeds, TryRLock fails with error, or Context
// Done channel is closed.
func (f *Flock) TryRLockContext(ctx context.Context, retryDelay time.Duration) (bool, error) {
return tryCtx(f.TryRLock, ctx, retryDelay)
}
func tryCtx(fn func() (bool, error), ctx context.Context, retryDelay time.Duration) (bool, error) {
if ctx.Err() != nil {
return false, ctx.Err()
}
for {
if ok, err := fn(); ok || err != nil {
return ok, err
}
select {
case <-ctx.Done():
return false, ctx.Err()
case <-time.After(retryDelay):
// try again
}
}
}
func (f *Flock) setFh() error {
// open a new os.File instance
// create it if it doesn't exist, and open the file read-only.
fh, err := os.OpenFile(f.path, os.O_CREATE|os.O_RDONLY, os.FileMode(0600))
if err != nil {
return err
}
// set the filehandle on the struct
f.fh = fh
return nil
}
// Copyright 2015 Tim Heckman. All rights reserved.
// Use of this source code is governed by the BSD 3-Clause
// license that can be found in the LICENSE file.
// +build !windows
package flock
import (
"os"
"syscall"
)
// Lock is a blocking call to try and take an exclusive file lock. It will wait
// until it is able to obtain the exclusive file lock. It's recommended that
// TryLock() be used over this function. This function may block the ability to
// query the current Locked() or RLocked() status due to a RW-mutex lock.
//
// If we are already exclusive-locked, this function short-circuits and returns
// immediately assuming it can take the mutex lock.
//
// If the *Flock has a shared lock (RLock), this may transparently replace the
// shared lock with an exclusive lock on some UNIX-like operating systems. Be
// careful when using exclusive locks in conjunction with shared locks
// (RLock()), because calling Unlock() may accidentally release the exclusive
// lock that was once a shared lock.
func (f *Flock) Lock() error {
return f.lock(&f.l, syscall.LOCK_EX)
}
// RLock is a blocking call to try and take a shared file lock. It will wait
// until it is able to obtain the shared file lock. It's recommended that
// TryRLock() be used over this function. This function may block the ability to
// query the current Locked() or RLocked() status due to a RW-mutex lock.
//
// If we are already shared-locked, this function short-circuits and returns
// immediately assuming it can take the mutex lock.
func (f *Flock) RLock() error {
return f.lock(&f.r, syscall.LOCK_SH)
}
func (f *Flock) lock(locked *bool, flag int) error {
f.m.Lock()
defer f.m.Unlock()
if *locked {
return nil
}
if f.fh == nil {
if err := f.setFh(); err != nil {
return err
}
}
if err := syscall.Flock(int(f.fh.Fd()), flag); err != nil {
shouldRetry, reopenErr := f.reopenFDOnError(err)
if reopenErr != nil {
return reopenErr
}
if !shouldRetry {
return err
}
if err = syscall.Flock(int(f.fh.Fd()), flag); err != nil {
return err
}
}
*locked = true
return nil
}
// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so
// while it is running the Locked() and RLocked() functions will be blocked.
//
// This function short-circuits if we are unlocked already. If not, it calls
// syscall.LOCK_UN on the file and closes the file descriptor. It does not
// remove the file from disk. It's up to your application to do.
//
// Please note, if your shared lock became an exclusive lock this may
// unintentionally drop the exclusive lock if called by the consumer that
// believes they have a shared lock. Please see Lock() for more details.
func (f *Flock) Unlock() error {
f.m.Lock()
defer f.m.Unlock()
// if we aren't locked or if the lockfile instance is nil
// just return a nil error because we are unlocked
if (!f.l && !f.r) || f.fh == nil {
return nil
}
// mark the file as unlocked
if err := syscall.Flock(int(f.fh.Fd()), syscall.LOCK_UN); err != nil {
return err
}
f.fh.Close()
f.l = false
f.r = false
f.fh = nil
return nil
}
// TryLock is the preferred function for taking an exclusive file lock. This
// function takes an RW-mutex lock before it tries to lock the file, so there is
// the possibility that this function may block for a short time if another
// goroutine is trying to take any action.
//
// The actual file lock is non-blocking. If we are unable to get the exclusive
// file lock, the function will return false instead of waiting for the lock. If
// we get the lock, we also set the *Flock instance as being exclusive-locked.
func (f *Flock) TryLock() (bool, error) {
return f.try(&f.l, syscall.LOCK_EX)
}
// TryRLock is the preferred function for taking a shared file lock. This
// function takes an RW-mutex lock before it tries to lock the file, so there is
// the possibility that this function may block for a short time if another
// goroutine is trying to take any action.
//
// The actual file lock is non-blocking. If we are unable to get the shared file
// lock, the function will return false instead of waiting for the lock. If we
// get the lock, we also set the *Flock instance as being share-locked.
func (f *Flock) TryRLock() (bool, error) {
return f.try(&f.r, syscall.LOCK_SH)
}
func (f *Flock) try(locked *bool, flag int) (bool, error) {
f.m.Lock()
defer f.m.Unlock()
if *locked {
return true, nil
}
if f.fh == nil {
if err := f.setFh(); err != nil {
return false, err
}
}
var retried bool
retry:
err := syscall.Flock(int(f.fh.Fd()), flag|syscall.LOCK_NB)
switch err {
case syscall.EWOULDBLOCK:
return false, nil
case nil:
*locked = true
return true, nil
}
if !retried {
if shouldRetry, reopenErr := f.reopenFDOnError(err); reopenErr != nil {
return false, reopenErr
} else if shouldRetry {
retried = true
goto retry
}
}
return false, err
}
// reopenFDOnError determines whether we should reopen the file handle
// in readwrite mode and try again. This comes from util-linux/sys-utils/flock.c:
// Since Linux 3.4 (commit 55725513)
// Probably NFSv4 where flock() is emulated by fcntl().
func (f *Flock) reopenFDOnError(err error) (bool, error) {
if err != syscall.EIO && err != syscall.EBADF {
return false, nil
}
if st, err := f.fh.Stat(); err == nil {
// if the file is able to be read and written
if st.Mode()&0600 == 0600 {
f.fh.Close()
f.fh = nil
// reopen in read-write mode and set the filehandle
fh, err := os.OpenFile(f.path, os.O_CREATE|os.O_RDWR, os.FileMode(0600))
if err != nil {
return false, err
}
f.fh = fh
return true, nil
}
}
return false, nil
}
// Copyright 2015 Tim Heckman. All rights reserved.
// Use of this source code is governed by the BSD 3-Clause
// license that can be found in the LICENSE file.
// +build windows
package flock
import (
"syscall"
"unsafe"
)
var (
kernel32, _ = syscall.LoadLibrary("kernel32.dll")
procLockFileEx, _ = syscall.GetProcAddress(kernel32, "LockFileEx")
procUnlockFileEx, _ = syscall.GetProcAddress(kernel32, "UnlockFileEx")
)
const (
winLockfileFailImmediately = 0x00000001
winLockfileExclusiveLock = 0x00000002
winLockfileSharedLock = 0x00000000
)
// Use of 0x00000000 for the shared lock is a guess based on some the MS Windows
// `LockFileEX` docs, which document the `LOCKFILE_EXCLUSIVE_LOCK` flag as:
//
// > The function requests an exclusive lock. Otherwise, it requests a shared
// > lock.
//
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx
func lockFileEx(handle syscall.Handle, flags uint32, reserved uint32, numberOfBytesToLockLow uint32, numberOfBytesToLockHigh uint32, offset *syscall.Overlapped) (bool, syscall.Errno) {
r1, _, errNo := syscall.Syscall6(
uintptr(procLockFileEx),
6,
uintptr(handle),
uintptr(flags),
uintptr(reserved),
uintptr(numberOfBytesToLockLow),
uintptr(numberOfBytesToLockHigh),
uintptr(unsafe.Pointer(offset)))
if r1 != 1 {
if errNo == 0 {
return false, syscall.EINVAL
}
return false, errNo
}
return true, 0
}
func unlockFileEx(handle syscall.Handle, reserved uint32, numberOfBytesToLockLow uint32, numberOfBytesToLockHigh uint32, offset *syscall.Overlapped) (bool, syscall.Errno) {
r1, _, errNo := syscall.Syscall6(
uintptr(procUnlockFileEx),
5,
uintptr(handle),
uintptr(reserved),
uintptr(numberOfBytesToLockLow),
uintptr(numberOfBytesToLockHigh),
uintptr(unsafe.Pointer(offset)),
0)
if r1 != 1 {
if errNo == 0 {
return false, syscall.EINVAL
}
return false, errNo
}
return true, 0
}
// Copyright 2015 Tim Heckman. All rights reserved.
// Use of this source code is governed by the BSD 3-Clause
// license that can be found in the LICENSE file.
package flock
import (
"syscall"
)
// ErrorLockViolation is the error code returned from the Windows syscall when a
// lock would block and you ask to fail immediately.
const ErrorLockViolation syscall.Errno = 0x21 // 33
// Lock is a blocking call to try and take an exclusive file lock. It will wait
// until it is able to obtain the exclusive file lock. It's recommended that
// TryLock() be used over this function. This function may block the ability to
// query the current Locked() or RLocked() status due to a RW-mutex lock.
//
// If we are already locked, this function short-circuits and returns
// immediately assuming it can take the mutex lock.
func (f *Flock) Lock() error {
return f.lock(&f.l, winLockfileExclusiveLock)
}
// RLock is a blocking call to try and take a shared file lock. It will wait
// until it is able to obtain the shared file lock. It's recommended that
// TryRLock() be used over this function. This function may block the ability to
// query the current Locked() or RLocked() status due to a RW-mutex lock.
//
// If we are already locked, this function short-circuits and returns
// immediately assuming it can take the mutex lock.
func (f *Flock) RLock() error {
return f.lock(&f.r, winLockfileSharedLock)
}
func (f *Flock) lock(locked *bool, flag uint32) error {
f.m.Lock()
defer f.m.Unlock()
if *locked {
return nil
}
if f.fh == nil {
if err := f.setFh(); err != nil {
return err
}
}
if _, errNo := lockFileEx(syscall.Handle(f.fh.Fd()), flag, 0, 1, 0, &syscall.Overlapped{}); errNo > 0 {
return errNo
}
*locked = true
return nil
}
// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so
// while it is running the Locked() and RLocked() functions will be blocked.
//
// This function short-circuits if we are unlocked already. If not, it calls
// UnlockFileEx() on the file and closes the file descriptor. It does not remove
// the file from disk. It's up to your application to do.
func (f *Flock) Unlock() error {
f.m.Lock()
defer f.m.Unlock()
// if we aren't locked or if the lockfile instance is nil
// just return a nil error because we are unlocked
if (!f.l && !f.r) || f.fh == nil {
return nil
}
// mark the file as unlocked
if _, errNo := unlockFileEx(syscall.Handle(f.fh.Fd()), 0, 1, 0, &syscall.Overlapped{}); errNo > 0 {
return errNo
}
f.fh.Close()
f.l = false
f.r = false
f.fh = nil
return nil
}
// TryLock is the preferred function for taking an exclusive file lock. This
// function does take a RW-mutex lock before it tries to lock the file, so there
// is the possibility that this function may block for a short time if another
// goroutine is trying to take any action.
//
// The actual file lock is non-blocking. If we are unable to get the exclusive
// file lock, the function will return false instead of waiting for the lock. If
// we get the lock, we also set the *Flock instance as being exclusive-locked.
func (f *Flock) TryLock() (bool, error) {
return f.try(&f.l, winLockfileExclusiveLock)
}
// TryRLock is the preferred function for taking a shared file lock. This
// function does take a RW-mutex lock before it tries to lock the file, so there
// is the possibility that this function may block for a short time if another
// goroutine is trying to take any action.
//
// The actual file lock is non-blocking. If we are unable to get the shared file
// lock, the function will return false instead of waiting for the lock. If we
// get the lock, we also set the *Flock instance as being shared-locked.
func (f *Flock) TryRLock() (bool, error) {
return f.try(&f.r, winLockfileSharedLock)
}
func (f *Flock) try(locked *bool, flag uint32) (bool, error) {
f.m.Lock()
defer f.m.Unlock()
if *locked {
return true, nil
}
if f.fh == nil {
if err := f.setFh(); err != nil {
return false, err
}
}
_, errNo := lockFileEx(syscall.Handle(f.fh.Fd()), flag|winLockfileFailImmediately, 0, 1, 0, &syscall.Overlapped{})
if errNo > 0 {
if errNo == ErrorLockViolation || errNo == syscall.ERROR_IO_PENDING {
return false, nil
}
return false, errNo
}
*locked = true
return true, nil
}
default: check PROJECT := github.com/juju/errors
check: .PHONY: check-licence check-go check docs
go test && go test -compiler gccgo
check: check-licence check-go
go test $(PROJECT)/...
check-licence:
@(fgrep -rl "Licensed under the LGPLv3" --exclude *.s .;\
fgrep -rl "MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT" --exclude *.s .;\
find . -name "*.go") | sed -e 's,\./,,' | sort | uniq -u | \
xargs -I {} echo FAIL: licence missed: {}
check-go:
$(eval GOFMT := $(strip $(shell gofmt -l .| sed -e "s/^/ /g")))
@(if [ x$(GOFMT) != x"" ]; then \
echo go fmt is sad: $(GOFMT); \
exit 1; \
fi )
@(go tool vet -all -composites=false -copylocks=false .)
docs: docs:
godoc2md github.com/juju/errors > README.md godoc2md github.com/juju/errors > README.md
sed -i 's|\[godoc-link-here\]|[![GoDoc](https://godoc.org/github.com/juju/errors?status.svg)](https://godoc.org/github.com/juju/errors)|' README.md sed -i 's|\[godoc-link-here\]|[![GoDoc](https://godoc.org/github.com/juju/errors?status.svg)](https://godoc.org/github.com/juju/errors)|' README.md
.PHONY: default check docs
...@@ -129,6 +129,13 @@ For example: ...@@ -129,6 +129,13 @@ For example:
} }
## func BadRequestf
``` go
func BadRequestf(format string, args ...interface{}) error
```
BadRequestf returns an error which satisfies IsBadRequest().
## func Cause ## func Cause
``` go ``` go
func Cause(err error) error func Cause(err error) error
...@@ -202,6 +209,13 @@ For example: ...@@ -202,6 +209,13 @@ For example:
return errors.Errorf("validation failed: %s", message) return errors.Errorf("validation failed: %s", message)
## func Forbiddenf
``` go
func Forbiddenf(format string, args ...interface{}) error
```
Forbiddenf returns an error which satistifes IsForbidden()
## func IsAlreadyExists ## func IsAlreadyExists
``` go ``` go
func IsAlreadyExists(err error) bool func IsAlreadyExists(err error) bool
...@@ -210,6 +224,38 @@ IsAlreadyExists reports whether the error was created with ...@@ -210,6 +224,38 @@ IsAlreadyExists reports whether the error was created with
AlreadyExistsf() or NewAlreadyExists(). AlreadyExistsf() or NewAlreadyExists().
## func IsBadRequest
``` go
func IsBadRequest(err error) bool
```
IsBadRequest reports whether err was created with BadRequestf() or
NewBadRequest().
## func IsForbidden
``` go
func IsForbidden(err error) bool
```
IsForbidden reports whether err was created with Forbiddenf() or
NewForbidden().
## func IsMethodNotAllowed
``` go
func IsMethodNotAllowed(err error) bool
```
IsMethodNotAllowed reports whether err was created with MethodNotAllowedf() or
NewMethodNotAllowed().
## func IsNotAssigned
``` go
func IsNotAssigned(err error) bool
```
IsNotAssigned reports whether err was created with NotAssignedf() or
NewNotAssigned().
## func IsNotFound ## func IsNotFound
``` go ``` go
func IsNotFound(err error) bool func IsNotFound(err error) bool
...@@ -226,6 +272,14 @@ IsNotImplemented reports whether err was created with ...@@ -226,6 +272,14 @@ IsNotImplemented reports whether err was created with
NotImplementedf() or NewNotImplemented(). NotImplementedf() or NewNotImplemented().
## func IsNotProvisioned
``` go
func IsNotProvisioned(err error) bool
```
IsNotProvisioned reports whether err was created with NotProvisionedf() or
NewNotProvisioned().
## func IsNotSupported ## func IsNotSupported
``` go ``` go
func IsNotSupported(err error) bool func IsNotSupported(err error) bool
...@@ -250,6 +304,14 @@ IsUnauthorized reports whether err was created with Unauthorizedf() or ...@@ -250,6 +304,14 @@ IsUnauthorized reports whether err was created with Unauthorizedf() or
NewUnauthorized(). NewUnauthorized().
## func IsUserNotFound
``` go
func IsUserNotFound(err error) bool
```
IsUserNotFound reports whether err was created with UserNotFoundf() or
NewUserNotFound().
## func Mask ## func Mask
``` go ``` go
func Mask(other error) error func Mask(other error) error
...@@ -267,11 +329,18 @@ hides the underlying error type. The error string still contains the full ...@@ -267,11 +329,18 @@ hides the underlying error type. The error string still contains the full
annotations. If you want to hide the annotations, call Wrap. annotations. If you want to hide the annotations, call Wrap.
## func MethodNotAllowedf
``` go
func MethodNotAllowedf(format string, args ...interface{}) error
```
MethodNotAllowedf returns an error which satisfies IsMethodNotAllowed().
## func New ## func New
``` go ``` go
func New(message string) error func New(message string) error
``` ```
New is a drop in replacement for the standard libary errors module that records New is a drop in replacement for the standard library errors module that records
the location that the error is created. the location that the error is created.
For example: For example:
...@@ -288,6 +357,38 @@ NewAlreadyExists returns an error which wraps err and satisfies ...@@ -288,6 +357,38 @@ NewAlreadyExists returns an error which wraps err and satisfies
IsAlreadyExists(). IsAlreadyExists().
## func NewBadRequest
``` go
func NewBadRequest(err error, msg string) error
```
NewBadRequest returns an error which wraps err that satisfies
IsBadRequest().
## func NewForbidden
``` go
func NewForbidden(err error, msg string) error
```
NewForbidden returns an error which wraps err that satisfies
IsForbidden().
## func NewMethodNotAllowed
``` go
func NewMethodNotAllowed(err error, msg string) error
```
NewMethodNotAllowed returns an error which wraps err that satisfies
IsMethodNotAllowed().
## func NewNotAssigned
``` go
func NewNotAssigned(err error, msg string) error
```
NewNotAssigned returns an error which wraps err that satisfies
IsNotAssigned().
## func NewNotFound ## func NewNotFound
``` go ``` go
func NewNotFound(err error, msg string) error func NewNotFound(err error, msg string) error
...@@ -304,6 +405,14 @@ NewNotImplemented returns an error which wraps err and satisfies ...@@ -304,6 +405,14 @@ NewNotImplemented returns an error which wraps err and satisfies
IsNotImplemented(). IsNotImplemented().
## func NewNotProvisioned
``` go
func NewNotProvisioned(err error, msg string) error
```
NewNotProvisioned returns an error which wraps err that satisfies
IsNotProvisioned().
## func NewNotSupported ## func NewNotSupported
``` go ``` go
func NewNotSupported(err error, msg string) error func NewNotSupported(err error, msg string) error
...@@ -327,6 +436,21 @@ NewUnauthorized returns an error which wraps err and satisfies ...@@ -327,6 +436,21 @@ NewUnauthorized returns an error which wraps err and satisfies
IsUnauthorized(). IsUnauthorized().
## func NewUserNotFound
``` go
func NewUserNotFound(err error, msg string) error
```
NewUserNotFound returns an error which wraps err and satisfies
IsUserNotFound().
## func NotAssignedf
``` go
func NotAssignedf(format string, args ...interface{}) error
```
NotAssignedf returns an error which satisfies IsNotAssigned().
## func NotFoundf ## func NotFoundf
``` go ``` go
func NotFoundf(format string, args ...interface{}) error func NotFoundf(format string, args ...interface{}) error
...@@ -341,6 +465,13 @@ func NotImplementedf(format string, args ...interface{}) error ...@@ -341,6 +465,13 @@ func NotImplementedf(format string, args ...interface{}) error
NotImplementedf returns an error which satisfies IsNotImplemented(). NotImplementedf returns an error which satisfies IsNotImplemented().
## func NotProvisionedf
``` go
func NotProvisionedf(format string, args ...interface{}) error
```
NotProvisionedf returns an error which satisfies IsNotProvisioned().
## func NotSupportedf ## func NotSupportedf
``` go ``` go
func NotSupportedf(format string, args ...interface{}) error func NotSupportedf(format string, args ...interface{}) error
...@@ -378,11 +509,11 @@ func Unauthorizedf(format string, args ...interface{}) error ...@@ -378,11 +509,11 @@ func Unauthorizedf(format string, args ...interface{}) error
Unauthorizedf returns an error which satisfies IsUnauthorized(). Unauthorizedf returns an error which satisfies IsUnauthorized().
## func Forbiddenf ## func UserNotFoundf
``` go ``` go
func Forbiddenf(format string, args ...interface{}) error func UserNotFoundf(format string, args ...interface{}) error
``` ```
Forbiddenf returns an error which satisfies IsForbidden(). UserNotFoundf returns an error which satisfies IsUserNotFound().
## func Wrap ## func Wrap
...@@ -460,6 +591,29 @@ For example: ...@@ -460,6 +591,29 @@ For example:
} }
### func NewErrWithCause
``` go
func NewErrWithCause(other error, format string, args ...interface{}) Err
```
NewErrWithCause is used to return an Err with cause by other error for the purpose of embedding in other
structures. The location is not specified, and needs to be set with a call
to SetLocation.
For example:
type FooError struct {
errors.Err
code int
}
func (e *FooError) Annotate(format string, args ...interface{}) error {
err := &FooError{errors.NewErrWithCause(e.Err, format, args...), e.code}
err.SetLocation(1)
return err
})
### func (\*Err) Cause ### func (\*Err) Cause
...@@ -483,6 +637,16 @@ Error implements error.Error. ...@@ -483,6 +637,16 @@ Error implements error.Error.
### func (\*Err) Format
``` go
func (e *Err) Format(s fmt.State, verb rune)
```
Format implements fmt.Formatter
When printing errors with %+v it also prints the stack trace.
%#v unsurprisingly will print the real underlying type.
### func (\*Err) Location ### func (\*Err) Location
``` go ``` go
func (e *Err) Location() (filename string, line int) func (e *Err) Location() (filename string, line int)
......
github.com/juju/loggo git 8232ab8918d91c72af1a9fb94d3edbe31d88b790 2017-06-05T01:46:07Z
github.com/juju/testing git 72703b1e95eb8ce4737fd8a3d8496c6b0be280a6 2018-05-17T13:41:05Z
gopkg.in/check.v1 git 4f90aeace3a26ad7021961c297b22c42160c7b25 2016-01-05T16:49:36Z
gopkg.in/mgo.v2 git f2b6f6c918c452ad107eec89615f074e3bd80e33 2016-08-18T01:52:18Z
gopkg.in/yaml.v2 git 1be3d31502d6eabc0dd7ce5b0daab022e14a5538 2017-07-12T05:45:46Z
...@@ -52,7 +52,7 @@ func NewErr(format string, args ...interface{}) Err { ...@@ -52,7 +52,7 @@ func NewErr(format string, args ...interface{}) Err {
} }
} }
// NewErrWithCause is used to return an Err with case by other error for the purpose of embedding in other // NewErrWithCause is used to return an Err with cause by other error for the purpose of embedding in other
// structures. The location is not specified, and needs to be set with a call // structures. The location is not specified, and needs to be set with a call
// to SetLocation. // to SetLocation.
// //
...@@ -143,6 +143,10 @@ func (e *Err) Format(s fmt.State, verb rune) { ...@@ -143,6 +143,10 @@ func (e *Err) Format(s fmt.State, verb rune) {
fallthrough fallthrough
case 's': case 's':
fmt.Fprintf(s, "%s", e.Error()) fmt.Fprintf(s, "%s", e.Error())
case 'q':
fmt.Fprintf(s, "%q", e.Error())
default:
fmt.Fprintf(s, "%%!%c(%T=%s)", verb, e, e.Error())
} }
} }
......
...@@ -17,6 +17,30 @@ func wrap(err error, format, suffix string, args ...interface{}) Err { ...@@ -17,6 +17,30 @@ func wrap(err error, format, suffix string, args ...interface{}) Err {
return newErr return newErr
} }
// timeout represents an error on timeout.
type timeout struct {
Err
}
// Timeoutf returns an error which satisfies IsTimeout().
func Timeoutf(format string, args ...interface{}) error {
return &timeout{wrap(nil, format, " timeout", args...)}
}
// NewTimeout returns an error which wraps err that satisfies
// IsTimeout().
func NewTimeout(err error, msg string) error {
return &timeout{wrap(err, msg, "")}
}
// IsTimeout reports whether err was created with Timeoutf() or
// NewTimeout().
func IsTimeout(err error) bool {
err = Cause(err)
_, ok := err.(*timeout)
return ok
}
// notFound represents an error when something has not been found. // notFound represents an error when something has not been found.
type notFound struct { type notFound struct {
Err Err
......
...@@ -4,35 +4,16 @@ ...@@ -4,35 +4,16 @@
package errors package errors
import ( import (
"runtime" "fmt"
"go/build"
"os"
"path/filepath"
"strings" "strings"
) )
// prefixSize is used internally to trim the user specific path from the var goPath = build.Default.GOPATH
// front of the returned filenames from the runtime call stack. var srcDir = filepath.Join(goPath, "src")
var prefixSize int
// goPath is the deduced path based on the location of this file as compiled.
var goPath string
func init() {
_, file, _, ok := runtime.Caller(0)
if file == "?" {
return
}
if ok {
// We know that the end of the file should be:
// github.com/juju/errors/path.go
size := len(file)
suffix := len("github.com/juju/errors/path.go")
goPath = file[:size-suffix]
prefixSize = len(goPath)
}
}
func trimGoPath(filename string) string { func trimGoPath(filename string) string {
if strings.HasPrefix(filename, goPath) { return strings.TrimPrefix(filename, fmt.Sprintf("%s%s", srcDir, string(os.PathSeparator)))
return filename[prefixSize:]
}
return filename
} }
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