<commit_before>package utils

import (
	"fmt"
	"io"
	"os"
	"path"
	"time"

	"github.com/pkg/errors"
)

func FileExists(path string) (bool, error) {
	fi, err := os.Stat(path)
	if err == nil {
		if fi.Mode()&os.ModeType == 0 {
			return true, nil
		}
		return false, errors.New(path + " exists but is not regular file")
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, errors.WithStack(err)
}

func GetLogFilePath(binpath string) string {
	dir, _ := path.Split(binpath)
	y, m, d := time.Now().Date()
	filename := fmt.Sprintf("easyss-%d%d%d.log", y, m, d)
	return path.Join(dir, filename)
}

func GetLogFileWriter(binpath string) (io.Writer, error) {
	logfile := GetLogFilePath(binpath)
	return os.OpenFile(logfile, os.O_APPEND|os.O_CREATE, os.ModeAppend)
}
<commit_msg>fix: cat logs failed on linux<commit_after>package utils

import (
	"fmt"
	"io"
	"os"
	"path"
	"time"

	"github.com/pkg/errors"
)

func FileExists(path string) (bool, error) {
	fi, err := os.Stat(path)
	if err == nil {
		if fi.Mode()&os.ModeType == 0 {
			return true, nil
		}
		return false, errors.New(path + " exists but is not regular file")
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, errors.WithStack(err)
}

func GetLogFilePath(binpath string) string {
	dir, _ := path.Split(binpath)
	y, m, d := time.Now().Date()
	filename := fmt.Sprintf("easyss-%d%d%d.log", y, m, d)
	return path.Join(dir, filename)
}

func GetLogFileWriter(binpath string) (io.Writer, error) {
	logfile := GetLogFilePath(binpath)
	return os.OpenFile(logfile, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
}
<|endoftext|>


<commit_before>package gobot

import (
	"fmt"
	"testing"
	"time"
)

func TestEvery(t *testing.T) {
	i := 0
	Every(2*time.Millisecond, func() {
		i++
	})
	<-time.After(5 * time.Millisecond)
	Assert(t, i, 2)
}

func TestAfter(t *testing.T) {
	i := 0
	After(1*time.Millisecond, func() {
		i++
	})
	<-time.After(2 * time.Millisecond)
	Assert(t, i, 1)
}

func TestPublish(t *testing.T) {
	e := &Event{Chan: make(chan interface{}, 1)}
	Publish(e, 1)
	Publish(e, 2)
	Publish(e, 3)
	Publish(e, 4)
	i := <-e.Chan
	Assert(t, i, 1)
}

func TestOn(t *testing.T) {
	var i int
	e := NewEvent()
	On(e, func(data interface{}) {
		i = data.(int)
	})
	Publish(e, 10)
	<-time.After(1 * time.Millisecond)
	Assert(t, i, 10)
}
func TestOnce(t *testing.T) {
	i := 0
	e := NewEvent()
	Once(e, func(data interface{}) {
		i += data.(int)
	})
	On(e, func(data interface{}) {
		i += data.(int)
	})
	Publish(e, 10)
	<-time.After(1 * time.Millisecond)
	Publish(e, 10)
	<-time.After(1 * time.Millisecond)
	Assert(t, i, 30)
}

func TestFromScale(t *testing.T) {
	Assert(t, FromScale(5, 0, 10), 0.5)
}

func TestToScale(t *testing.T) {
	Assert(t, ToScale(500, 0, 10), 10.0)
	Assert(t, ToScale(-1, 0, 10), 0.0)
	Assert(t, ToScale(0.5, 0, 10), 5.0)
}

func TestRand(t *testing.T) {
	a := Rand(1000)
	b := Rand(1000)
	if a == b {
		t.Error(fmt.Sprintf("%v should not equal %v", a, b))
	}
}
<commit_msg>Fix randomly failing test<commit_after>package gobot

import (
	"fmt"
	"testing"
	"time"
)

func TestEvery(t *testing.T) {
	i := 0
	begin := time.Now().UnixNano()
	sem := make(chan int64, 1)
	Every(2*time.Millisecond, func() {
		i++
		if i == 2 {
			sem <- time.Now().UnixNano()
		}
	})
	end := <-sem
	if end-begin < 4000000 {
		t.Error("Test should have taken at least 4 milliseconds")
	}
}

func TestAfter(t *testing.T) {
	i := 0
	After(1*time.Millisecond, func() {
		i++
	})
	<-time.After(2 * time.Millisecond)
	Assert(t, i, 1)
}

func TestPublish(t *testing.T) {
	e := &Event{Chan: make(chan interface{}, 1)}
	Publish(e, 1)
	Publish(e, 2)
	Publish(e, 3)
	Publish(e, 4)
	i := <-e.Chan
	Assert(t, i, 1)
}

func TestOn(t *testing.T) {
	var i int
	e := NewEvent()
	On(e, func(data interface{}) {
		i = data.(int)
	})
	Publish(e, 10)
	<-time.After(1 * time.Millisecond)
	Assert(t, i, 10)
}
func TestOnce(t *testing.T) {
	i := 0
	e := NewEvent()
	Once(e, func(data interface{}) {
		i += data.(int)
	})
	On(e, func(data interface{}) {
		i += data.(int)
	})
	Publish(e, 10)
	<-time.After(1 * time.Millisecond)
	Publish(e, 10)
	<-time.After(1 * time.Millisecond)
	Assert(t, i, 30)
}

func TestFromScale(t *testing.T) {
	Assert(t, FromScale(5, 0, 10), 0.5)
}

func TestToScale(t *testing.T) {
	Assert(t, ToScale(500, 0, 10), 10.0)
	Assert(t, ToScale(-1, 0, 10), 0.0)
	Assert(t, ToScale(0.5, 0, 10), 5.0)
}

func TestRand(t *testing.T) {
	a := Rand(1000)
	b := Rand(1000)
	if a == b {
		t.Error(fmt.Sprintf("%v should not equal %v", a, b))
	}
}
<|endoftext|>


<commit_before>/*******************************************************************************
The MIT License (MIT)

Copyright (c) 2013-2014 Hajime Nakagami

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*******************************************************************************/

package firebirdsql

import (
	"errors"
	"fmt"
	"testing"
)

func TestDSNParse(t *testing.T) {
	var testDSNs = []struct {
		dsn            string
		addr           string
		dbName         string
		user           string
		passwd         string
		role           string
		authPluginName string
		wireCrypt      bool
	}{
		{"user:password@localhost:3000/dbname", "localhost:3000", "dbname", "user", "password", "", "Srp", true},
		{"user:password@localhost/dbname", "localhost:3050", "dbname", "user", "password", "", "Srp", true},
		{"user:password@localhost/dir/dbname", "localhost:3050", "/dir/dbname", "user", "password", "", "Srp", true},
		{"user:password@localhost/c:\\fbdata\\database.fdb", "localhost:3050", "c:\\fbdata\\database.fdb", "user", "password", "", "Srp", true},
		{"user:password@localhost/dbname?role=role", "localhost:3050", "dbname", "user", "password", "role", "Srp", true},
		{"user:password@localhost/dbname?auth_plugin_name=Legacy_Auth", "localhost:3050", "dbname", "user", "password", "", "Legacy_Auth", true},
		{"user:password@localhost/dbname?auth_plugin_name=Legacy_Auth&wire_crypt=false", "localhost:3050", "dbname", "user", "password", "", "Legacy_Auth", false},
	}

	for _, d := range testDSNs {
		addr, dbName, user, passwd, role, authPluginName, wireCrypt, err := parseDSN(d.dsn)
		if addr != d.addr {
			err = errors.New(fmt.Sprintf("parse DSN fail:%s(%s != %s)", d.dsn, addr, d.addr))
		} else if dbName != d.dbName {
			err = errors.New(fmt.Sprintf("parse DSN fail:%s(%s != %s)", d.dsn, dbName, d.dbName))
		} else if user != d.user {
			err = errors.New(fmt.Sprintf("parse DSN fail:%s(%s != %s)", d.dsn, user, d.user))
		} else if passwd != d.passwd {
			err = errors.New(fmt.Sprintf("parse DSN fail:%s(%s != %s)", d.dsn, passwd, d.passwd))
		} else if role != d.role {
			err = errors.New(fmt.Sprintf("parse DSN fail:%s(%s != %s)", d.dsn, role, d.role))
		} else if authPluginName != d.authPluginName {
			err = errors.New(fmt.Sprintf("parse DSN fail:%s(%s != %s)", d.dsn, authPluginName, d.authPluginName))
		} else if wireCrypt != d.wireCrypt {
			err = errors.New(fmt.Sprintf("parse DSN fail:%s(%v != %v)", d.dsn, wireCrypt, d.wireCrypt))
		}

		if err != nil {
			t.Error(err.Error())
		}
	}
}
<commit_msg>add test data for issue #27<commit_after>/*******************************************************************************
The MIT License (MIT)

Copyright (c) 2013-2014 Hajime Nakagami

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*******************************************************************************/

package firebirdsql

import (
	"errors"
	"fmt"
	"testing"
)

func TestDSNParse(t *testing.T) {
	var testDSNs = []struct {
		dsn            string
		addr           string
		dbName         string
		user           string
		passwd         string
		role           string
		authPluginName string
		wireCrypt      bool
	}{
		{"user:password@localhost:3000/dbname", "localhost:3000", "dbname", "user", "password", "", "Srp", true},
		{"user:password@localhost/dbname", "localhost:3050", "dbname", "user", "password", "", "Srp", true},
		{"user:password@localhost/dir/dbname", "localhost:3050", "/dir/dbname", "user", "password", "", "Srp", true},
		{"user:password@localhost/c:\\fbdata\\database.fdb", "localhost:3050", "c:\\fbdata\\database.fdb", "user", "password", "", "Srp", true},
		{"user:password@localhost/c:/fbdata/database.fdb", "localhost:3050", "c:/fbdata/database.fdb", "user", "password", "", "Srp", true},
		{"user:password@localhost/dbname?role=role", "localhost:3050", "dbname", "user", "password", "role", "Srp", true},
		{"user:password@localhost/dbname?auth_plugin_name=Legacy_Auth", "localhost:3050", "dbname", "user", "password", "", "Legacy_Auth", true},
		{"user:password@localhost/dbname?auth_plugin_name=Legacy_Auth&wire_crypt=false", "localhost:3050", "dbname", "user", "password", "", "Legacy_Auth", false},
	}

	for _, d := range testDSNs {
		addr, dbName, user, passwd, role, authPluginName, wireCrypt, err := parseDSN(d.dsn)
		if addr != d.addr {
			err = errors.New(fmt.Sprintf("parse DSN fail:%s(%s != %s)", d.dsn, addr, d.addr))
		} else if dbName != d.dbName {
			err = errors.New(fmt.Sprintf("parse DSN fail:%s(%s != %s)", d.dsn, dbName, d.dbName))
		} else if user != d.user {
			err = errors.New(fmt.Sprintf("parse DSN fail:%s(%s != %s)", d.dsn, user, d.user))
		} else if passwd != d.passwd {
			err = errors.New(fmt.Sprintf("parse DSN fail:%s(%s != %s)", d.dsn, passwd, d.passwd))
		} else if role != d.role {
			err = errors.New(fmt.Sprintf("parse DSN fail:%s(%s != %s)", d.dsn, role, d.role))
		} else if authPluginName != d.authPluginName {
			err = errors.New(fmt.Sprintf("parse DSN fail:%s(%s != %s)", d.dsn, authPluginName, d.authPluginName))
		} else if wireCrypt != d.wireCrypt {
			err = errors.New(fmt.Sprintf("parse DSN fail:%s(%v != %v)", d.dsn, wireCrypt, d.wireCrypt))
		}

		if err != nil {
			t.Error(err.Error())
		}
	}
}
<|endoftext|>


<commit_before>package v8

/*
#include "v8_wrap.h"
#include <stdlib.h>
*/
import "C"
import "unsafe"
import "runtime"

//import "reflect"

// A sandboxed execution context with its own set of built-in objects
// and functions.
type Context struct {
	self   unsafe.Pointer
	engine *Engine
}

func (e *Engine) NewContext() *Context {
	self := C.V8_NewContext(e.self)

	if self == nil {
		return nil
	}

	result := &Context{
		self:   self,
		engine: e,
	}

	runtime.SetFinalizer(result, func(c *Context) {
		if traceDispose {
			println("v8.Context.Dispose()", c.self)
		}
		C.V8_DisposeContext(c.self)
	})

	return result
}

//export try_catch_callback
func try_catch_callback(callback unsafe.Pointer) {
	(*(*func())(callback))()
}

func (c *Context) ThrowException(err string) {
	Default.Compile([]byte(`throw "`+err+`"`), nil, nil).Run(c)
	//
	// TODO: use ThrowException() will make function template GetFunction() return NULL, why?
	//
	//errPtr := unsafe.Pointer((*reflect.StringHeader)(unsafe.Pointer(&err)).Data)
	//C.V8_Context_ThrowException(c.self, (*C.char)(errPtr), C.int(len(err)))
}

func (c *Context) TryCatch(simple bool, callback func()) string {
	isSimple := 0
	if simple {
		isSimple = 1
	}
	creport := C.V8_Context_TryCatch(c.self, unsafe.Pointer(&callback), C.int(isSimple))
	if creport == nil {
		return ""
	}
	report := C.GoString(creport)
	C.free(unsafe.Pointer(creport))
	return report
}
<commit_msg>edit comment<commit_after>package v8

/*
#include "v8_wrap.h"
#include <stdlib.h>
*/
import "C"
import "unsafe"
import "runtime"

//import "reflect"

// A sandboxed execution context with its own set of built-in objects
// and functions.
type Context struct {
	self   unsafe.Pointer
	engine *Engine
}

func (e *Engine) NewContext() *Context {
	self := C.V8_NewContext(e.self)

	if self == nil {
		return nil
	}

	result := &Context{
		self:   self,
		engine: e,
	}

	runtime.SetFinalizer(result, func(c *Context) {
		if traceDispose {
			println("v8.Context.Dispose()", c.self)
		}
		C.V8_DisposeContext(c.self)
	})

	return result
}

//export try_catch_callback
func try_catch_callback(callback unsafe.Pointer) {
	(*(*func())(callback))()
}

func (c *Context) ThrowException(err string) {
	Default.Compile([]byte(`throw "`+err+`"`), nil, nil).Run(c)
	//
	// TODO: use Isolate::ThrowException() will make FunctionTemplate::GetFunction() returns NULL, why?
	//
	//errPtr := unsafe.Pointer((*reflect.StringHeader)(unsafe.Pointer(&err)).Data)
	//C.V8_Context_ThrowException(c.self, (*C.char)(errPtr), C.int(len(err)))
}

func (c *Context) TryCatch(simple bool, callback func()) string {
	isSimple := 0
	if simple {
		isSimple = 1
	}
	creport := C.V8_Context_TryCatch(c.self, unsafe.Pointer(&callback), C.int(isSimple))
	if creport == nil {
		return ""
	}
	report := C.GoString(creport)
	C.free(unsafe.Pointer(creport))
	return report
}
<|endoftext|>


<commit_before>package money

import (
	"testing"

	"math/rand"
	"time"
)

func generateTests() []struct{String string} {
	// Initialize.
	randomness := rand.New( rand.NewSource( time.Now().UTC().UnixNano() ) )

	// We want to make sure we test the empty string!
	tests := []struct{
		String string
	}{
		{
			String: "",
		},
	}

	// Create some specific tests, that should generate errors.
	tests = append(tests, []struct{
		String string
	}{
		{
			String: "-",
		},
		{
			String: "--",
		},
		{
			String: "---",
		},

		{
			String:  "$", // normal dollar sign
		},
		{
			String: "-$", // normal dollar sign
		},

		{
			String:  "\uFE69", // small dollar sign
		},
		{
			String: "-\uFE69", // small dollar sign
		},

		{
			String:  "\uFF04", // full-width dollar sign
		},
		{
			String: "-\uFF04", // full-width dollar sign
		},

		{
			String:  "\U0001F4B2", // heavy dollar sign
		},
		{
			String: "-\U0001F4B2", // heavy dollar sign
		},

		{
			String:  "\u00A2", // cent sign
		},
		{
			String: "-\u00A2", // cent sign
		},

		{
			String:  "\uFFE0", // full-width cent sign
		},
		{
			String: "-\uFFE0", // full-width cent sign
		},

		{
			String:  "-$\uFE69\uFF04\U0001F4B2\u00A2\uFFE0", // normal dollar sign
		},

	}...)

	// Create some tests with just white space.
	//
	// Note we are only using the "traditional" (i.e., ASCII)
	// whitespace characters here.
	//
	//	\t             horizontal tab (== \011)
	//	\n             newline (== \012)
	//	\v             vertical tab character (== \013)
	//	\f             form feed (== \014)
	//	\r             carriage return (== \015)
	//
	whitespaceCharacters := []rune("\t\n\v\f\r")

	for _,character := range whitespaceCharacters {
		datum := struct{String string}{String: string(character)}

		tests = append(tests, datum)
	}

	for _,character := range whitespaceCharacters {
		datum := struct{String string}{String: string([]rune{character, character})}

		tests = append(tests, datum)
	}

	for _,character := range whitespaceCharacters {
		datum := struct{String string}{String: string([]rune{character, character, character})}

		tests = append(tests, datum)
	}

	const numMoreRandomWhiteSpace = 30
	for i:=0; i<numMoreRandomWhiteSpace; i++ {

		// Randomly choose a length for the string.
		strLen := 1 + randomness.Intn(50)

		// Create a buffer to construct the string.
		buffer := make([]rune, strLen)

		// Construct the string in the buffer.
		for ii := range buffer {
			buffer[ii] = whitespaceCharacters[randomness.Intn(len(whitespaceCharacters))]
		}

		// Add it to the tests.
		datum := struct{String string}{String: string(buffer)}

		tests = append(tests, datum)
	}

	// Create some tests with all sorts of characters,
	// that won't result in something that looks like money.
	variousCharacters := []rune("!\"#%&'()*,/:;<=>?@{|}~")
	variousCharacters = append(variousCharacters, []rune("\U0001f600\U0001f601\U0001f602\U0001f603\U0001f604\U0001f605\U0001f606\U0001f607\U0001f608\U0001f609\U0001f60a\U0001f60b\U0001f60c\U0001f60d\U0001f60e\U0001f60f\U0001f610\U0001f611\U0001f612\U0001f613\U0001f614\U0001f615\U0001f616\U0001f617\U0001f618\U0001f619\U0001f61a\U0001f61b\U0001f61c\U0001f61d\U0001f61e\U0001f61f\U0001f620\U0001f621\U0001f622\U0001f623\U0001f624\U0001f625\U0001f626\U0001f627\U0001f628\U0001f629\U0001f62a\U0001f62b\U0001f62c\U0001f62d\U0001f62e\U0001f62f\U0001f630\U0001f631\U0001f632\U0001f633\U0001f634\U0001f635\U0001f636\U0001f637")...)

	for _,character := range variousCharacters {
		datum := struct{String string}{String: string(character)}

		tests = append(tests, datum)
	}

	for _,character := range variousCharacters {
		datum := struct{String string}{String: string([]rune{character, character})}

		tests = append(tests, datum)
	}

	for _,character := range variousCharacters {
		datum := struct{String string}{String: string([]rune{character, character, character})}

		tests = append(tests, datum)
	}

	const numMoreRandomVarious = 30
	for i:=0; i<numMoreRandomVarious; i++ {

		// Randomly choose a length for the string.
		strLen := 1 + randomness.Intn(50)

		// Create a buffer to construct the string.
		buffer := make([]rune, strLen)

		// Construct the string in the buffer.
		for ii := range buffer {
			buffer[ii] = variousCharacters[randomness.Intn(len(variousCharacters))]
		}

		// Add it to the tests.
		datum := struct{String string}{String: string(buffer)}

		tests = append(tests, datum)
	}

	// Return
	return tests
}

func TestParseCADErr(t *testing.T) {

	tests := generateTests()

	for testNumber, test := range tests {

		actual, err := ParseCAD(test.String)
		if nil == err {
			t.Errorf("For test #%d, did not receive an error but expected one: err = %v and actual = %v", testNumber, err, actual)
		}
	}
}

func TestParseUSDErr(t *testing.T) {

	tests := generateTests()

	for testNumber, test := range tests {

		actual, err := ParseUSD(test.String)
		if nil == err {
			t.Errorf("For test #%d, did not receive an error but expected one: err = %v and actual = %v", testNumber, err, actual)
		}
	}
}
<commit_msg>updated error message in test.<commit_after>package money

import (
	"testing"

	"math/rand"
	"time"
)

func generateTests() []struct{String string} {
	// Initialize.
	randomness := rand.New( rand.NewSource( time.Now().UTC().UnixNano() ) )

	// We want to make sure we test the empty string!
	tests := []struct{
		String string
	}{
		{
			String: "",
		},
	}

	// Create some specific tests, that should generate errors.
	tests = append(tests, []struct{
		String string
	}{
		{
			String: "-",
		},
		{
			String: "--",
		},
		{
			String: "---",
		},

		{
			String:  "$", // normal dollar sign
		},
		{
			String: "-$", // normal dollar sign
		},

		{
			String:  "\uFE69", // small dollar sign
		},
		{
			String: "-\uFE69", // small dollar sign
		},

		{
			String:  "\uFF04", // full-width dollar sign
		},
		{
			String: "-\uFF04", // full-width dollar sign
		},

		{
			String:  "\U0001F4B2", // heavy dollar sign
		},
		{
			String: "-\U0001F4B2", // heavy dollar sign
		},

		{
			String:  "\u00A2", // cent sign
		},
		{
			String: "-\u00A2", // cent sign
		},

		{
			String:  "\uFFE0", // full-width cent sign
		},
		{
			String: "-\uFFE0", // full-width cent sign
		},

		{
			String:  "-$\uFE69\uFF04\U0001F4B2\u00A2\uFFE0", // normal dollar sign
		},

	}...)

	// Create some tests with just white space.
	//
	// Note we are only using the "traditional" (i.e., ASCII)
	// whitespace characters here.
	//
	//	\t             horizontal tab (== \011)
	//	\n             newline (== \012)
	//	\v             vertical tab character (== \013)
	//	\f             form feed (== \014)
	//	\r             carriage return (== \015)
	//
	whitespaceCharacters := []rune("\t\n\v\f\r")

	for _,character := range whitespaceCharacters {
		datum := struct{String string}{String: string(character)}

		tests = append(tests, datum)
	}

	for _,character := range whitespaceCharacters {
		datum := struct{String string}{String: string([]rune{character, character})}

		tests = append(tests, datum)
	}

	for _,character := range whitespaceCharacters {
		datum := struct{String string}{String: string([]rune{character, character, character})}

		tests = append(tests, datum)
	}

	const numMoreRandomWhiteSpace = 30
	for i:=0; i<numMoreRandomWhiteSpace; i++ {

		// Randomly choose a length for the string.
		strLen := 1 + randomness.Intn(50)

		// Create a buffer to construct the string.
		buffer := make([]rune, strLen)

		// Construct the string in the buffer.
		for ii := range buffer {
			buffer[ii] = whitespaceCharacters[randomness.Intn(len(whitespaceCharacters))]
		}

		// Add it to the tests.
		datum := struct{String string}{String: string(buffer)}

		tests = append(tests, datum)
	}

	// Create some tests with all sorts of characters,
	// that won't result in something that looks like money.
	variousCharacters := []rune("!\"#%&'()*,/:;<=>?@{|}~")
	variousCharacters = append(variousCharacters, []rune("\U0001f600\U0001f601\U0001f602\U0001f603\U0001f604\U0001f605\U0001f606\U0001f607\U0001f608\U0001f609\U0001f60a\U0001f60b\U0001f60c\U0001f60d\U0001f60e\U0001f60f\U0001f610\U0001f611\U0001f612\U0001f613\U0001f614\U0001f615\U0001f616\U0001f617\U0001f618\U0001f619\U0001f61a\U0001f61b\U0001f61c\U0001f61d\U0001f61e\U0001f61f\U0001f620\U0001f621\U0001f622\U0001f623\U0001f624\U0001f625\U0001f626\U0001f627\U0001f628\U0001f629\U0001f62a\U0001f62b\U0001f62c\U0001f62d\U0001f62e\U0001f62f\U0001f630\U0001f631\U0001f632\U0001f633\U0001f634\U0001f635\U0001f636\U0001f637")...)

	for _,character := range variousCharacters {
		datum := struct{String string}{String: string(character)}

		tests = append(tests, datum)
	}

	for _,character := range variousCharacters {
		datum := struct{String string}{String: string([]rune{character, character})}

		tests = append(tests, datum)
	}

	for _,character := range variousCharacters {
		datum := struct{String string}{String: string([]rune{character, character, character})}

		tests = append(tests, datum)
	}

	const numMoreRandomVarious = 30
	for i:=0; i<numMoreRandomVarious; i++ {

		// Randomly choose a length for the string.
		strLen := 1 + randomness.Intn(50)

		// Create a buffer to construct the string.
		buffer := make([]rune, strLen)

		// Construct the string in the buffer.
		for ii := range buffer {
			buffer[ii] = variousCharacters[randomness.Intn(len(variousCharacters))]
		}

		// Add it to the tests.
		datum := struct{String string}{String: string(buffer)}

		tests = append(tests, datum)
	}

	// Return
	return tests
}

func TestParseCADErr(t *testing.T) {

	tests := generateTests()

	for testNumber, test := range tests {

		actual, err := ParseCAD(test.String)
		if nil == err {
			t.Errorf("For test #%d, for %q did not receive an error but expected one: err = %v and actual = %v", testNumber, test.String, err, actual)
		}
	}
}

func TestParseUSDErr(t *testing.T) {

	tests := generateTests()

	for testNumber, test := range tests {

		actual, err := ParseUSD(test.String)
		if nil == err {
			t.Errorf("For test #%d, for %q did not receive an error but expected one: err = %v and actual = %v", testNumber, test.String, err, actual)
		}
	}
}
<|endoftext|>


<commit_before>package main

/*
 * Copyright 2015 Albert P. Tobey <atobey@datastax.com> @AlTobey
 *
 * 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.
 *
 * pcstat.go - page cache stat
 *
 * uses the mincore(2) syscall to find out which pages (almost always 4k)
 * of a file are currently cached in memory
 *
 */

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/tobert/pcstat"
)

type PcStatusList []pcstat.PcStatus

func (stats PcStatusList) formatText() {
	maxName := stats.maxName()

	// create horizontal grid line
	pad := strings.Repeat("-", len(maxName)+2)
	hr := fmt.Sprintf("|%s+----------------+------------+-----------+---------|", pad)

	fmt.Println(hr)

	// -nohdr may be chosen to save 2 lines of precious vertical space
	if !nohdrFlag {
		pad = strings.Repeat(" ", len(maxName)-4)
		fmt.Printf("| Name%s | Size           | Pages      | Cached    | Percent |\n", pad)
		fmt.Println(hr)
	}

	for _, pcs := range stats {
		pad = strings.Repeat(" ", len(maxName)-len(pcs.Name))

		// %07.3f was chosen to make it easy to scan the percentages vertically
		// I tried a few different formats only this one kept the decimals aligned
		fmt.Printf("| %s%s | %-15d| %-11d| %-10d| %07.3f |\n",
			pcs.Name, pad, pcs.Size, pcs.Pages, pcs.Cached, pcs.Percent)
	}

	fmt.Println(hr)
}

func (stats PcStatusList) formatTerse() {
	if !nohdrFlag {
		fmt.Println("name,size,timestamp,mtime,pages,cached,percent")
	}
	for _, pcs := range stats {
		time := pcs.Timestamp.Unix()
		mtime := pcs.Mtime.Unix()
		fmt.Printf("%s,%d,%d,%d,%d,%d,%g\n",
			pcs.Name, pcs.Size, time, mtime, pcs.Pages, pcs.Cached, pcs.Percent)
	}
}

func (stats PcStatusList) formatJson(clearpps bool) {
	// clear the per-page status when requested
	// emits an empty "status": [] field in the JSON when disabled, but NBD.
	if clearpps {
		for _, pcs := range stats {
			pcs.PPStat = nil
		}
	}

	b, err := json.Marshal(stats)
	if err != nil {
		log.Fatalf("JSON formatting failed: %s\n", err)
	}
	os.Stdout.Write(b)
	fmt.Println("")
}

// references:
// http://www.unicode.org/charts/PDF/U2580.pdf
// https://github.com/puppetlabs/mcollective-puppet-agent/blob/master/application/puppet.rb#L143
// https://github.com/holman/spark
func (stats PcStatusList) formatHistogram() {
	ws := getwinsize()
	maxName := stats.maxName()

	// block elements are wider than characters, so only use 1/2 the available columns
	buckets := (int(ws.ws_col)-len(maxName))/2 - 10

	for _, pcs := range stats {
		pad := strings.Repeat(" ", len(maxName)-len(pcs.Name))
		fmt.Printf("%s%s % 8d ", pcs.Name, pad, pcs.Pages)

		// when there is enough room display on/off for every page
		if buckets > pcs.Pages {
			for _, v := range pcs.PPStat {
				if v {
					fmt.Print("\u2588") // full block = 100%
				} else {
					fmt.Print("\u2581") // lower 1/8 block
				}
			}
		} else {
			bsz := pcs.Pages / buckets
			fbsz := float64(bsz)
			total := 0.0
			for i, v := range pcs.PPStat {
				if v {
					total++
				}

				if (i+1)%bsz == 0 {
					avg := total / fbsz
					if total == 0 {
						fmt.Print("\u2581") // lower 1/8 block = 0
					} else if avg < 0.16 {
						fmt.Print("\u2582") // lower 2/8 block
					} else if avg < 0.33 {
						fmt.Print("\u2583") // lower 3/8 block
					} else if avg < 0.50 {
						fmt.Print("\u2584") // lower 4/8 block
					} else if avg < 0.66 {
						fmt.Print("\u2585") // lower 5/8 block
					} else if avg < 0.83 {
						fmt.Print("\u2586") // lower 6/8 block
					} else if avg < 1.00 {
						fmt.Print("\u2587") // lower 7/8 block
					} else {
						fmt.Print("\u2588") // full block = 100%
					}

					total = 0
				}
			}
		}
		fmt.Println("")
	}
}

/*

	// convert long paths to their basename with the -bname flag
	// this overwrites the original filename in pcs but it doesn't matter since
	// it's not used to access the file again -- and should not be!
	if bnameFlag {
		pcs.Name = path.Base(fname)
	}
*/

// getLongestName returns the len of longest filename in the stat list
// if the bnameFlag is set, this will return the max basename len
func (stats PcStatusList) maxName() string {
	var maxName string
	for _, pcs := range stats {
		if len(pcs.Name) > len(maxName) {
			maxName = pcs.Name
		}
	}
	return maxName
}
<commit_msg>use a default max name length in formats<commit_after>package main

/*
 * Copyright 2015 Albert P. Tobey <atobey@datastax.com> @AlTobey
 *
 * 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.
 *
 * pcstat.go - page cache stat
 *
 * uses the mincore(2) syscall to find out which pages (almost always 4k)
 * of a file are currently cached in memory
 *
 */

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/tobert/pcstat"
)

type PcStatusList []pcstat.PcStatus

func (stats PcStatusList) formatText() {
	maxName := stats.maxNameLen()

	// create horizontal grid line
	pad := strings.Repeat("-", maxName+2)
	hr := fmt.Sprintf("|%s+----------------+------------+-----------+---------|", pad)

	fmt.Println(hr)

	// -nohdr may be chosen to save 2 lines of precious vertical space
	if !nohdrFlag {
		pad = strings.Repeat(" ", maxName-4)
		fmt.Printf("| Name%s | Size           | Pages      | Cached    | Percent |\n", pad)
		fmt.Println(hr)
	}

	for _, pcs := range stats {
		pad = strings.Repeat(" ", maxName-len(pcs.Name))

		// %07.3f was chosen to make it easy to scan the percentages vertically
		// I tried a few different formats only this one kept the decimals aligned
		fmt.Printf("| %s%s | %-15d| %-11d| %-10d| %07.3f |\n",
			pcs.Name, pad, pcs.Size, pcs.Pages, pcs.Cached, pcs.Percent)
	}

	fmt.Println(hr)
}

func (stats PcStatusList) formatTerse() {
	if !nohdrFlag {
		fmt.Println("name,size,timestamp,mtime,pages,cached,percent")
	}
	for _, pcs := range stats {
		time := pcs.Timestamp.Unix()
		mtime := pcs.Mtime.Unix()
		fmt.Printf("%s,%d,%d,%d,%d,%d,%g\n",
			pcs.Name, pcs.Size, time, mtime, pcs.Pages, pcs.Cached, pcs.Percent)
	}
}

func (stats PcStatusList) formatJson(clearpps bool) {
	// clear the per-page status when requested
	// emits an empty "status": [] field in the JSON when disabled, but NBD.
	if clearpps {
		for _, pcs := range stats {
			pcs.PPStat = nil
		}
	}

	b, err := json.Marshal(stats)
	if err != nil {
		log.Fatalf("JSON formatting failed: %s\n", err)
	}
	os.Stdout.Write(b)
	fmt.Println("")
}

// references:
// http://www.unicode.org/charts/PDF/U2580.pdf
// https://github.com/puppetlabs/mcollective-puppet-agent/blob/master/application/puppet.rb#L143
// https://github.com/holman/spark
func (stats PcStatusList) formatHistogram() {
	ws := getwinsize()
	maxName := stats.maxNameLen()

	// block elements are wider than characters, so only use 1/2 the available columns
	buckets := (int(ws.ws_col)-maxName)/2 - 10

	for _, pcs := range stats {
		pad := strings.Repeat(" ", maxName-len(pcs.Name))
		fmt.Printf("%s%s % 8d ", pcs.Name, pad, pcs.Pages)

		// when there is enough room display on/off for every page
		if buckets > pcs.Pages {
			for _, v := range pcs.PPStat {
				if v {
					fmt.Print("\u2588") // full block = 100%
				} else {
					fmt.Print("\u2581") // lower 1/8 block
				}
			}
		} else {
			bsz := pcs.Pages / buckets
			fbsz := float64(bsz)
			total := 0.0
			for i, v := range pcs.PPStat {
				if v {
					total++
				}

				if (i+1)%bsz == 0 {
					avg := total / fbsz
					if total == 0 {
						fmt.Print("\u2581") // lower 1/8 block = 0
					} else if avg < 0.16 {
						fmt.Print("\u2582") // lower 2/8 block
					} else if avg < 0.33 {
						fmt.Print("\u2583") // lower 3/8 block
					} else if avg < 0.50 {
						fmt.Print("\u2584") // lower 4/8 block
					} else if avg < 0.66 {
						fmt.Print("\u2585") // lower 5/8 block
					} else if avg < 0.83 {
						fmt.Print("\u2586") // lower 6/8 block
					} else if avg < 1.00 {
						fmt.Print("\u2587") // lower 7/8 block
					} else {
						fmt.Print("\u2588") // full block = 100%
					}

					total = 0
				}
			}
		}
		fmt.Println("")
	}
}

/*

	// convert long paths to their basename with the -bname flag
	// this overwrites the original filename in pcs but it doesn't matter since
	// it's not used to access the file again -- and should not be!
	if bnameFlag {
		pcs.Name = path.Base(fname)
	}
*/

// maxNameLen returns the len of longest filename in the stat list
// if the bnameFlag is set, this will return the max basename len
func (stats PcStatusList) maxNameLen() int {
	var maxName int
	for _, pcs := range stats {
		if len(pcs.Name) > maxName {
			maxName = len(pcs.Name)
		}
	}

	if maxName < 5 {
		maxName = 5
	}
	return maxName
}
<|endoftext|>


<commit_before>package peer

import (
	"sync"

	ic "github.com/jbenet/go-ipfs/crypto"
	u "github.com/jbenet/go-ipfs/util"
	errors "github.com/jbenet/go-ipfs/util/debugerror"
)

// Peerstore provides a threadsafe collection for peers.
type Peerstore interface {
	FindOrCreate(ID) (Peer, error)
	Add(Peer) (Peer, error)
	Delete(ID) error
	All() (*Map, error)

	WithKeyPair(sk ic.PrivKey, pk ic.PubKey) (Peer, error)
	WithID(id ID) Peer
	WithIDString(id string) Peer
}

type peerstore struct {
	sync.RWMutex
	data map[string]Peer // key is string(ID)
}

// NewPeerstore creates a threadsafe collection of peers.
func NewPeerstore() Peerstore {
	return &peerstore{
		data: make(map[string]Peer),
	}
}

func (ps *peerstore) FindOrCreate(i ID) (Peer, error) {
	ps.Lock()
	defer ps.Unlock()

	if i == nil {
		panic("wat")
	}

	p, ok := ps.data[i.String()]
	if !ok { // not found, construct it ourselves, add it to datastore, and return.

		// TODO(brian) kinda dangerous, no? If ID is invalid and doesn't
		// correspond to an actual valid peer ID, this peerstore will return an
		// instantiated peer value, allowing the error to propagate. It might
		// be better to nip this at the bud by returning nil and making the
		// client manually add a Peer. To keep the peerstore in control, this
		// can even be a peerstore method that performs cursory validation.
		//
		// Potential bad case: Suppose values arrive from untrusted providers
		// in the DHT.
		p = &peer{id: i}
		ps.data[i.String()] = p
	}

	// no error, got it back fine
	return p, nil
}

func (p *peerstore) Add(peer Peer) (Peer, error) {
	p.Lock()
	defer p.Unlock()

	existing, ok := p.data[peer.Key().String()]
	if !ok { // not found? just add and return.
		p.data[peer.Key().String()] = peer
		return peer, nil
	}
	// already here.
	if peer == existing {
		return peer, nil
	}
	existing.Update(peer) // must do some merging.
	return existing, nil
}

func (p *peerstore) Delete(i ID) error {
	p.Lock()
	defer p.Unlock()

	delete(p.data, i.String())
	return nil
}

func (p *peerstore) All() (*Map, error) {
	p.Lock()
	defer p.Unlock()

	ps := Map{}
	for k, v := range p.data {
		ps[u.Key(k)] = v
	}
	return &ps, nil
}

// WithKeyPair returns a Peer object with given keys.
func (ps *peerstore) WithKeyPair(sk ic.PrivKey, pk ic.PubKey) (Peer, error) {
	if sk == nil && pk == nil {
		return nil, errors.Errorf("PeerWithKeyPair nil keys")
	}

	pk2 := sk.GetPublic()
	if pk == nil {
		pk = pk2
	} else if !pk.Equals(pk2) {
		return nil, errors.Errorf("key mismatch. pubkey is not privkey's pubkey")
	}

	pkid, err := IDFromPubKey(pk)
	if err != nil {
		return nil, errors.Errorf("Failed to hash public key: %v", err)
	}

	return &peer{id: pkid, pubKey: pk, privKey: sk}, nil
}

// WithID constructs a peer with given ID.
func (ps *peerstore) WithID(id ID) Peer {
	return &peer{id: id}
}

// WithIDString constructs a peer with given ID (string).
func (ps *peerstore) WithIDString(id string) Peer {
	return ps.WithID(ID(id))
}
<commit_msg>feat(peerstore) add peer to peerstore upon instantiation<commit_after>package peer

import (
	"sync"

	ic "github.com/jbenet/go-ipfs/crypto"
	u "github.com/jbenet/go-ipfs/util"
	errors "github.com/jbenet/go-ipfs/util/debugerror"
)

// Peerstore provides a threadsafe collection for peers.
type Peerstore interface {
	FindOrCreate(ID) (Peer, error)
	Add(Peer) (Peer, error)
	Delete(ID) error
	All() (*Map, error)

	WithKeyPair(sk ic.PrivKey, pk ic.PubKey) (Peer, error)
	WithID(id ID) Peer
	WithIDString(id string) Peer
}

type peerstore struct {
	sync.RWMutex
	data map[string]Peer // key is string(ID)
}

// NewPeerstore creates a threadsafe collection of peers.
func NewPeerstore() Peerstore {
	return &peerstore{
		data: make(map[string]Peer),
	}
}

func (ps *peerstore) FindOrCreate(i ID) (Peer, error) {
	ps.Lock()
	defer ps.Unlock()

	if i == nil {
		panic("wat")
	}

	p, ok := ps.data[i.String()]
	if !ok { // not found, construct it ourselves, add it to datastore, and return.

		// TODO(brian) kinda dangerous, no? If ID is invalid and doesn't
		// correspond to an actual valid peer ID, this peerstore will return an
		// instantiated peer value, allowing the error to propagate. It might
		// be better to nip this at the bud by returning nil and making the
		// client manually add a Peer. To keep the peerstore in control, this
		// can even be a peerstore method that performs cursory validation.
		//
		// Potential bad case: Suppose values arrive from untrusted providers
		// in the DHT.
		p = &peer{id: i}
		ps.data[i.String()] = p
	}

	// no error, got it back fine
	return p, nil
}

func (p *peerstore) Add(peer Peer) (Peer, error) {
	p.Lock()
	defer p.Unlock()

	existing, ok := p.data[peer.Key().String()]
	if !ok { // not found? just add and return.
		p.data[peer.Key().String()] = peer
		return peer, nil
	}
	// already here.
	if peer == existing {
		return peer, nil
	}
	existing.Update(peer) // must do some merging.
	return existing, nil
}

func (p *peerstore) Delete(i ID) error {
	p.Lock()
	defer p.Unlock()

	delete(p.data, i.String())
	return nil
}

func (p *peerstore) All() (*Map, error) {
	p.Lock()
	defer p.Unlock()

	ps := Map{}
	for k, v := range p.data {
		ps[u.Key(k)] = v
	}
	return &ps, nil
}

// WithKeyPair returns a Peer object with given keys.
func (ps *peerstore) WithKeyPair(sk ic.PrivKey, pk ic.PubKey) (Peer, error) {
	if sk == nil && pk == nil {
		return nil, errors.Errorf("PeerWithKeyPair nil keys")
	}

	pk2 := sk.GetPublic()
	if pk == nil {
		pk = pk2
	} else if !pk.Equals(pk2) {
		return nil, errors.Errorf("key mismatch. pubkey is not privkey's pubkey")
	}

	pkid, err := IDFromPubKey(pk)
	if err != nil {
		return nil, errors.Errorf("Failed to hash public key: %v", err)
	}

	p := &peer{id: pkid, pubKey: pk, privKey: sk}
	ps.Add(p)
	return p, nil
}

// WithID constructs a peer with given ID.
func (ps *peerstore) WithID(id ID) Peer {
	p := &peer{id: id}
	ps.Add(p)
	return p
}

// WithIDString constructs a peer with given ID (string).
func (ps *peerstore) WithIDString(id string) Peer {
	return ps.WithID(ID(id))
}
<|endoftext|>


<commit_before>/*
Package vala is a simple, extensible, library to make argument
validation in Go palatable.

This package uses the fluent programming style to provide
simultaneously more robust and more terse parameter validation.

	BeginValidation().Validate(
		IsNotNil(a, "a"),
		IsNotNil(b, "b"),
		IsNotNil(c, "c"),
	).CheckAndPanic().Validate( // Panic will occur here if a, b, or c are nil.
		HasLen(a.Items, 50, "a.Items"),
		GreaterThan(b.UserCount, 0, "b.UserCount"),
		Equals(c.Name, "Vala", "c.name"),
		Not(Equals(c.FriendlyName, "Foo", "c.FriendlyName")),
	).Check()

Notice how checks can be tiered.

Vala is also extensible. As long as a function conforms to the Checker
specification, you can pass it into the Validate method:

	func ReportFitsRepository(report *Report, repository *Repository) Checker {
		return func() (passes bool, err error) {

			err = fmt.Errorf("A %s report does not belong in a %s repository.", report.Type, repository.Type)
			passes = (repository.Type == report.Type)
			return passes, err
		}
	}

	func AuthorCanUpload(authorName string, repository *Repository) Checker {
		return func() (passes bool, err error) {
			err = fmt.Errorf("%s does not have access to this repository.", authorName)
			passes = !repository.AuthorCanUpload(authorName)
			return passes, err
		}
	}

	func AuthorIsCollaborator(authorName string, report *Report) Checker {
		return func() (passes bool, err error) {

			err = fmt.Errorf("The given author was not one of the collaborators for this report.")
			for _, collaboratorName := range report.Collaborators() {
				if collaboratorName == authorName {
					passes = true
					break
				}
			}

			return passes, err
		}
	}

	func HandleReport(authorName string, report *Report, repository *Repository) {

		BeginValidation().Validate(
			AuthorIsCollaborator(authorName, report),
			AuthorCanUpload(authorName, repository),
			ReportFitsRepository(report, repository),
		).CheckAndPanic()
	}
*/
package vala

import (
	"fmt"
	"reflect"
	"strings"
)

// Validation contains all the errors from performing Checkers, and is
// the fluent type off which all Validation methods hang.
type Validation struct {
	Errors []string
}

// BeginValidation begins a validation check.
func BeginValidation() *Validation {
	return nil
}

// Check aggregates all checker errors into a single error and returns
// this error.
func (val *Validation) Check() error {
	if val == nil || len(val.Errors) <= 0 {
		return nil
	}

	return val.constructErrorMessage()
}

// CheckAndPanic aggregates all checker errors into a single error and
// panics with this error.
func (val *Validation) CheckAndPanic() *Validation {
	if val == nil || len(val.Errors) <= 0 {
		return val
	}

	panic(val.constructErrorMessage())
}

// CheckSetErrorAndPanic aggregates any Errors produced by the
// Checkers into a single error, and sets the address of retError to
// this, and panics. The canonical use-case of this is to pass in the
// address of an error you would like to return, and then to catch the
// panic and do nothing.
func (val *Validation) CheckSetErrorAndPanic(retError *error) *Validation {
	if val == nil || len(val.Errors) <= 0 {
		return val
	}

	*retError = val.constructErrorMessage()
	panic(*retError)
}

// Validate runs all of the checkers passed in and collects errors
// into an internal collection. To take action on these errors, call
// one of the Check* methods.
func (val *Validation) Validate(checkers ...Checker) *Validation {

	for _, checker := range checkers {
		if pass, msg := checker(); !pass {
			if val == nil {
				val = &Validation{}
			}

			val.Errors = append(val.Errors, msg)
		}
	}

	return val
}

func (val *Validation) constructErrorMessage() error {
	return fmt.Errorf(
		"parameter validation failed:\t%s",
		strings.Join(val.Errors, "\n\t"),
	)
}

//
// Checker functions
//

// Checker defines the type of function which can represent a Vala
// checker.  If the Checker fails, returns false with a corresponding
// error message. If the Checker succeeds, returns true, but _also_
// returns an error message. This helps to support the Not function.
type Checker func() (checkerIsTrue bool, errorMessage string)

// Not returns the inverse of any Checker passed in.
func Not(checker Checker) Checker {

	return func() (passed bool, errorMessage string) {
		if passed, errorMessage = checker(); passed {
			return false, fmt.Sprintf("Not(%s)", errorMessage)
		}

		return true, ""
	}
}

// Equals performs a basic == on the given parameters and fails if
// they are not equal.
func Equals(param, value interface{}, paramName string) Checker {

	return func() (pass bool, errMsg string) {
		return (param == value), fmt.Sprintf("Parameters were not equal: %s(%v) != %v",
			paramName,
			param,
			value)
	}
}

// IsNotNil checks to see if the value passed in is nil. This Checker
// attempts to check the most performant things first, and then
// degrade into the less-performant, but accurate checks for nil.
func IsNotNil(obtained interface{}, paramName string) Checker {
	return func() (isNotNil bool, errMsg string) {

		if obtained == nil {
			isNotNil = false
		} else if str, ok := obtained.(string); ok {
			isNotNil = str != ""
		} else {
			switch v := reflect.ValueOf(obtained); v.Kind() {
			case
				reflect.Chan,
				reflect.Func,
				reflect.Interface,
				reflect.Map,
				reflect.Ptr,
				reflect.Slice:
				isNotNil = !v.IsNil()
			default:
				panic("Vala is unable to check this type for nilability at this time.")
			}
		}

		return isNotNil, "Parameter was nil: " + paramName
	}
}

// HasLen checks to ensure the given argument is the desired length.
func HasLen(param interface{}, desiredLength int, paramName string) Checker {

	return func() (hasLen bool, errMsg string) {
		hasLen = desiredLength == reflect.ValueOf(param).Len()
		return hasLen, "Parameter did not contain the correct number of elements: " + paramName
	}
}

// GreaterThan checks to ensure the given argument is greater than the
// given value.
func GreaterThan(param int, comparativeVal int, paramName string) Checker {

	return func() (isGreaterThan bool, errMsg string) {
		if isGreaterThan = param > comparativeVal; !isGreaterThan {
			errMsg = fmt.Sprintf(
				"Parameter's length was not greater than:  %s(%d) < %d",
				paramName,
				param,
				comparativeVal)
		}

		return isGreaterThan, errMsg
	}
}

// StringNotEmpty checks to ensure the given string is not empty.
func StringNotEmpty(obtained, paramName string) Checker {
	return func() (isNotEmpty bool, errMsg string) {
		isNotEmpty = obtained != ""
		errMsg = fmt.Sprintf("Parameter is an empty string: %s", paramName)
		return
	}
}
<commit_msg>Format error messages better<commit_after>/*
Package vala is a simple, extensible, library to make argument
validation in Go palatable.

This package uses the fluent programming style to provide
simultaneously more robust and more terse parameter validation.

	BeginValidation().Validate(
		IsNotNil(a, "a"),
		IsNotNil(b, "b"),
		IsNotNil(c, "c"),
	).CheckAndPanic().Validate( // Panic will occur here if a, b, or c are nil.
		HasLen(a.Items, 50, "a.Items"),
		GreaterThan(b.UserCount, 0, "b.UserCount"),
		Equals(c.Name, "Vala", "c.name"),
		Not(Equals(c.FriendlyName, "Foo", "c.FriendlyName")),
	).Check()

Notice how checks can be tiered.

Vala is also extensible. As long as a function conforms to the Checker
specification, you can pass it into the Validate method:

	func ReportFitsRepository(report *Report, repository *Repository) Checker {
		return func() (passes bool, err error) {

			err = fmt.Errorf("A %s report does not belong in a %s repository.", report.Type, repository.Type)
			passes = (repository.Type == report.Type)
			return passes, err
		}
	}

	func AuthorCanUpload(authorName string, repository *Repository) Checker {
		return func() (passes bool, err error) {
			err = fmt.Errorf("%s does not have access to this repository.", authorName)
			passes = !repository.AuthorCanUpload(authorName)
			return passes, err
		}
	}

	func AuthorIsCollaborator(authorName string, report *Report) Checker {
		return func() (passes bool, err error) {

			err = fmt.Errorf("The given author was not one of the collaborators for this report.")
			for _, collaboratorName := range report.Collaborators() {
				if collaboratorName == authorName {
					passes = true
					break
				}
			}

			return passes, err
		}
	}

	func HandleReport(authorName string, report *Report, repository *Repository) {

		BeginValidation().Validate(
			AuthorIsCollaborator(authorName, report),
			AuthorCanUpload(authorName, repository),
			ReportFitsRepository(report, repository),
		).CheckAndPanic()
	}
*/
package vala

import (
	"fmt"
	"reflect"
	"strings"
)

// Validation contains all the errors from performing Checkers, and is
// the fluent type off which all Validation methods hang.
type Validation struct {
	Errors []string
}

// BeginValidation begins a validation check.
func BeginValidation() *Validation {
	return nil
}

// Check aggregates all checker errors into a single error and returns
// this error.
func (val *Validation) Check() error {
	if val == nil || len(val.Errors) <= 0 {
		return nil
	}

	return val.constructErrorMessage()
}

// CheckAndPanic aggregates all checker errors into a single error and
// panics with this error.
func (val *Validation) CheckAndPanic() *Validation {
	if val == nil || len(val.Errors) <= 0 {
		return val
	}

	panic(val.constructErrorMessage())
}

// CheckSetErrorAndPanic aggregates any Errors produced by the
// Checkers into a single error, and sets the address of retError to
// this, and panics. The canonical use-case of this is to pass in the
// address of an error you would like to return, and then to catch the
// panic and do nothing.
func (val *Validation) CheckSetErrorAndPanic(retError *error) *Validation {
	if val == nil || len(val.Errors) <= 0 {
		return val
	}

	*retError = val.constructErrorMessage()
	panic(*retError)
}

// Validate runs all of the checkers passed in and collects errors
// into an internal collection. To take action on these errors, call
// one of the Check* methods.
func (val *Validation) Validate(checkers ...Checker) *Validation {

	for _, checker := range checkers {
		if pass, msg := checker(); !pass {
			if val == nil {
				val = &Validation{}
			}

			val.Errors = append(val.Errors, msg)
		}
	}

	return val
}

func (val *Validation) constructErrorMessage() error {
	if len(val.Errors) == 1 {
		return fmt.Errorf(
			"parameter validation failed: %s",
			val.Errors[0],
		)
	}

	return fmt.Errorf(
		"parameter validation failed:\n\t%s",
		strings.Join(val.Errors, "\n\t"),
	)
}

//
// Checker functions
//

// Checker defines the type of function which can represent a Vala
// checker.  If the Checker fails, returns false with a corresponding
// error message. If the Checker succeeds, returns true, but _also_
// returns an error message. This helps to support the Not function.
type Checker func() (checkerIsTrue bool, errorMessage string)

// Not returns the inverse of any Checker passed in.
func Not(checker Checker) Checker {

	return func() (passed bool, errorMessage string) {
		if passed, errorMessage = checker(); passed {
			return false, fmt.Sprintf("Not(%s)", errorMessage)
		}

		return true, ""
	}
}

// Equals performs a basic == on the given parameters and fails if
// they are not equal.
func Equals(param, value interface{}, paramName string) Checker {

	return func() (pass bool, errMsg string) {
		return (param == value), fmt.Sprintf("Parameters were not equal: %s(%v) != %v",
			paramName,
			param,
			value)
	}
}

// IsNotNil checks to see if the value passed in is nil. This Checker
// attempts to check the most performant things first, and then
// degrade into the less-performant, but accurate checks for nil.
func IsNotNil(obtained interface{}, paramName string) Checker {
	return func() (isNotNil bool, errMsg string) {

		if obtained == nil {
			isNotNil = false
		} else if str, ok := obtained.(string); ok {
			isNotNil = str != ""
		} else {
			switch v := reflect.ValueOf(obtained); v.Kind() {
			case
				reflect.Chan,
				reflect.Func,
				reflect.Interface,
				reflect.Map,
				reflect.Ptr,
				reflect.Slice:
				isNotNil = !v.IsNil()
			default:
				panic("Vala is unable to check this type for nilability at this time.")
			}
		}

		return isNotNil, "Parameter was nil: " + paramName
	}
}

// HasLen checks to ensure the given argument is the desired length.
func HasLen(param interface{}, desiredLength int, paramName string) Checker {

	return func() (hasLen bool, errMsg string) {
		hasLen = desiredLength == reflect.ValueOf(param).Len()
		return hasLen, "Parameter did not contain the correct number of elements: " + paramName
	}
}

// GreaterThan checks to ensure the given argument is greater than the
// given value.
func GreaterThan(param int, comparativeVal int, paramName string) Checker {

	return func() (isGreaterThan bool, errMsg string) {
		if isGreaterThan = param > comparativeVal; !isGreaterThan {
			errMsg = fmt.Sprintf(
				"Parameter's length was not greater than:  %s(%d) < %d",
				paramName,
				param,
				comparativeVal)
		}

		return isGreaterThan, errMsg
	}
}

// StringNotEmpty checks to ensure the given string is not empty.
func StringNotEmpty(obtained, paramName string) Checker {
	return func() (isNotEmpty bool, errMsg string) {
		isNotEmpty = obtained != ""
		errMsg = fmt.Sprintf("Parameter is an empty string: %s", paramName)
		return
	}
}
<|endoftext|>


<commit_before>package ach

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// Validator is common validation and formating of golang types to ach type strings
type Validator struct{}

// iServiceClass returns true if a valid service class code
func (v *Validator) isServiceClass(code int) bool {
	switch code {
	case
		// ACH Mixed Debits and Credits
		200,
		// ACH Credits Only
		220,
		// ACH Debits Only
		225,
		// ACH Automated Accounting Advices
		280:
		return true
	}
	return false
}

// isSECCode returns true if a SEC Code is found
func (v *Validator) isSECCode(code string) bool {
	switch code {
	case
		"ACK", "ADV", "ARC", "ATX", "BOC", "CCD", "CIE", "COR", "CTX", "DNE", "ENR",
		"IAT", "MTE", "POS", "PPD", "POP", "RCK", "SHR", "TEL", "TRC", "TRX", "WEB", "XCK":
		return true
	}
	return false
}

// isTypeCode returns true if a valid type code is found
func (v *Validator) isTypeCode(code string) bool {
	switch code {
	case
		// For POS, SHR or MTE Entries
		"02",
		// Addenda Record (ACK, ATX, CCD, CIE, CTX, DNE, ENR, PPD, TRX and WEB Entries
		"08",
		// Notification of Change and Refused Notification of Change Entry
		"98",
		// Return, Dishonored Return and Contested Dishonored Return Entries
		"99",
		//  IAT forward Entries and IAT Returns
		"10", "11", "12", "13", "14", "15", "16", "17",
		// CCD Addenda Record
		"05":
		return true
	}
	return false
}

// isTransactionCode ensures TransactionCode code is valid
func (v *Validator) isTransactionCode(code int) bool {
	switch code {
	// TransactionCode if the recievers account is:
	case
		// Credit (deposit) to checking account ‘22’
		22,
		// Prenote for credit to checking account ‘23’
		23,
		// Debit (withdrawal) to checking account ‘27’
		27,
		// Prenote for debit to checking account ‘28’
		28,
		// Credit to savings account ‘32’
		32,
		// Prenote for credit to savings account ‘33’
		33,
		// Debit to savings account ‘37’
		37,
		// Prenote for debit to savings account ‘38’
		38:
		return true
	}
	return false
}

// isOriginatorStatusCode ensures status code is valid
func (v *Validator) isOriginatorStatusCode(code int) bool {
	switch code {
	case
		// ADV file - prepared by an ACH Operator
		0,
		//Originator is a financial institution
		1,
		// Originator is a Government Agency or other agency not subject to ACH Rules
		2:
		return true
	}
	return false
}

func (v *Validator) parseNumField(r string) (s int) {
	s, err := strconv.Atoi(strings.TrimSpace(r))
	if err != nil {
		// TODO: This is horrible
		fmt.Printf("%v", err)
		return
	}
	return s
}

// formatSimpleDate takes a time.Time and returns a string of YYMMDD
func (v *Validator) formatSimpleDate(t time.Time) string {
	return t.Format("060102")
}

// parseSimpleDate returns a time.Time when passed time as YYMMDD
func (v *Validator) parseSimpleDate(s string) time.Time {
	t, _ := time.Parse("060102", s)
	return t
}

// formatSimpleTime returns a string of HHMM when  passed a time.Time
func (v *Validator) formatSimpleTime(t time.Time) string {
	return t.Format("1504")
}

// parseSimpleTime returns a time.Time when passed a string of HHMM
func (v *Validator) parseSimpleTime(s string) time.Time {
	t, _ := time.Parse("1504", s)
	return t
}

// isUpperAlphanumeric checks if string only contains ASCII alphanumeric upper case characters
func (v *Validator) isUpperAlphanumeric(s string) (b bool) {
	if regexp.MustCompile(`[^A-Z0-9]+`).MatchString(s) {
		return false
	}
	return true
}

// isAlphanumeric checks if a string only contains ASCII alphanumeric characters
func (v *Validator) isAlphanumeric(s string) (b bool) {
	if regexp.MustCompile(`[^ a-zA-Z0-9_*\/]+`).MatchString(s) {
		// ^[ A-Za-z0-9_@./#&+-]*$/
		return false
	}
	return true
}

// isLetter checks if a string contains only ASCII letters
func (v *Validator) isLetter(s string) bool {
	fmt.Println(s)
	for _, r := range s {
		fmt.Printf("%v ", r)
		if (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') {
			return false
		}
	}
	return true
}

// rightPad takes a string and padds the left side of s to overallLen with padStr.
func (v *Validator) rightPad(s string, padStr string, overallLen int) string {
	var padCountInt int
	padCountInt = 1 + (overallLen - len(padStr))
	var retStr = s + strings.Repeat(padStr, padCountInt)
	return retStr[:overallLen]
}

// leftPad takes a string and padds the right side of s to overallLen with padStr.
func (v *Validator) leftPad(s string, padStr string, overallLen int) string {
	var padCountInt int
	padCountInt = 1 + (overallLen - len(padStr))
	var retStr = strings.Repeat(padStr, padCountInt) + s
	return retStr[(len(retStr) - overallLen):]
}
<commit_msg>Current Dead code<commit_after>package ach

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// Validator is common validation and formating of golang types to ach type strings
type Validator struct{}

// iServiceClass returns true if a valid service class code
func (v *Validator) isServiceClass(code int) bool {
	switch code {
	case
		// ACH Mixed Debits and Credits
		200,
		// ACH Credits Only
		220,
		// ACH Debits Only
		225,
		// ACH Automated Accounting Advices
		280:
		return true
	}
	return false
}

// isSECCode returns true if a SEC Code is found
func (v *Validator) isSECCode(code string) bool {
	switch code {
	case
		"ACK", "ADV", "ARC", "ATX", "BOC", "CCD", "CIE", "COR", "CTX", "DNE", "ENR",
		"IAT", "MTE", "POS", "PPD", "POP", "RCK", "SHR", "TEL", "TRC", "TRX", "WEB", "XCK":
		return true
	}
	return false
}

// isTypeCode returns true if a valid type code is found
func (v *Validator) isTypeCode(code string) bool {
	switch code {
	case
		// For POS, SHR or MTE Entries
		"02",
		// Addenda Record (ACK, ATX, CCD, CIE, CTX, DNE, ENR, PPD, TRX and WEB Entries
		"08",
		// Notification of Change and Refused Notification of Change Entry
		"98",
		// Return, Dishonored Return and Contested Dishonored Return Entries
		"99",
		//  IAT forward Entries and IAT Returns
		"10", "11", "12", "13", "14", "15", "16", "17",
		// CCD Addenda Record
		"05":
		return true
	}
	return false
}

// isTransactionCode ensures TransactionCode code is valid
func (v *Validator) isTransactionCode(code int) bool {
	switch code {
	// TransactionCode if the recievers account is:
	case
		// Credit (deposit) to checking account ‘22’
		22,
		// Prenote for credit to checking account ‘23’
		23,
		// Debit (withdrawal) to checking account ‘27’
		27,
		// Prenote for debit to checking account ‘28’
		28,
		// Credit to savings account ‘32’
		32,
		// Prenote for credit to savings account ‘33’
		33,
		// Debit to savings account ‘37’
		37,
		// Prenote for debit to savings account ‘38’
		38:
		return true
	}
	return false
}

// isOriginatorStatusCode ensures status code is valid
func (v *Validator) isOriginatorStatusCode(code int) bool {
	switch code {
	case
		// ADV file - prepared by an ACH Operator
		0,
		//Originator is a financial institution
		1,
		// Originator is a Government Agency or other agency not subject to ACH Rules
		2:
		return true
	}
	return false
}

func (v *Validator) parseNumField(r string) (s int) {
	s, err := strconv.Atoi(strings.TrimSpace(r))
	if err != nil {
		// TODO: This is horrible
		fmt.Printf("%v", err)
		return
	}
	return s
}

// formatSimpleDate takes a time.Time and returns a string of YYMMDD
func (v *Validator) formatSimpleDate(t time.Time) string {
	return t.Format("060102")
}

// parseSimpleDate returns a time.Time when passed time as YYMMDD
func (v *Validator) parseSimpleDate(s string) time.Time {
	t, _ := time.Parse("060102", s)
	return t
}

// formatSimpleTime returns a string of HHMM when  passed a time.Time
func (v *Validator) formatSimpleTime(t time.Time) string {
	return t.Format("1504")
}

// parseSimpleTime returns a time.Time when passed a string of HHMM
func (v *Validator) parseSimpleTime(s string) time.Time {
	t, _ := time.Parse("1504", s)
	return t
}

// isUpperAlphanumeric checks if string only contains ASCII alphanumeric upper case characters
func (v *Validator) isUpperAlphanumeric(s string) (b bool) {
	if regexp.MustCompile(`[^A-Z0-9]+`).MatchString(s) {
		return false
	}
	return true
}

// isAlphanumeric checks if a string only contains ASCII alphanumeric characters
// func (v *Validator) isAlphanumeric(s string) (b bool) {
// 	if regexp.MustCompile(`[^ a-zA-Z0-9_*\/]+`).MatchString(s) {
// 		// ^[ A-Za-z0-9_@./#&+-]*$/
// 		return false
// 	}
// 	return true
// }
//
// // isLetter checks if a string contains only ASCII letters
// func (v *Validator) isLetter(s string) bool {
// 	fmt.Println(s)
// 	for _, r := range s {
// 		fmt.Printf("%v ", r)
// 		if (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') {
// 			return false
// 		}
// 	}
// 	return true
// }

// rightPad takes a string and padds the left side of s to overallLen with padStr.
func (v *Validator) rightPad(s string, padStr string, overallLen int) string {
	var padCountInt int
	padCountInt = 1 + (overallLen - len(padStr))
	var retStr = s + strings.Repeat(padStr, padCountInt)
	return retStr[:overallLen]
}

// leftPad takes a string and padds the right side of s to overallLen with padStr.
func (v *Validator) leftPad(s string, padStr string, overallLen int) string {
	var padCountInt int
	padCountInt = 1 + (overallLen - len(padStr))
	var retStr = strings.Repeat(padStr, padCountInt) + s
	return retStr[(len(retStr) - overallLen):]
}
<|endoftext|>


<commit_before>package drivers

import (
	"fmt"
	"os/exec"
	"strings"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/migration"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/logger"
)

type common struct {
	name        string
	config      map[string]string
	getVolID    func(volType VolumeType, volName string) (int64, error)
	commonRules *Validators
	state       *state.State
	logger      logger.Logger
	patches     map[string]func() error
}

func (d *common) init(state *state.State, name string, config map[string]string, logger logger.Logger, volIDFunc func(volType VolumeType, volName string) (int64, error), commonRules *Validators) {
	d.name = name
	d.config = config
	d.getVolID = volIDFunc
	d.commonRules = commonRules
	d.state = state
	d.logger = logger
}

// isRemote returns false indicating this driver does not use remote storage.
func (d *common) isRemote() bool {
	return false
}

// validatePool validates a pool config against common rules and optional driver specific rules.
func (d *common) validatePool(config map[string]string, driverRules map[string]func(value string) error) error {
	checkedFields := map[string]struct{}{}

	// Get rules common for all drivers.
	rules := d.commonRules.PoolRules()

	// Merge driver specific rules into common rules.
	for field, validator := range driverRules {
		rules[field] = validator
	}

	// Run the validator against each field.
	for k, validator := range rules {
		checkedFields[k] = struct{}{} //Mark field as checked.
		err := validator(config[k])
		if err != nil {
			return errors.Wrapf(err, "Invalid value for pool %q option %q", d.name, k)
		}
	}

	// Look for any unchecked fields, as these are unknown fields and validation should fail.
	for k := range config {
		_, checked := checkedFields[k]
		if checked {
			continue
		}

		// User keys are not validated.
		if strings.HasPrefix(k, "user.") {
			continue
		}

		return fmt.Errorf("Invalid option for pool %q option %q", d.name, k)
	}

	return nil
}

// FillVolumeConfig populate volume with default config.
func (d *common) FillVolumeConfig(vol Volume) error {
	return nil
}

// validateVolume validates a volume config against common rules and optional driver specific rules.
// This functions has a removeUnknownKeys option that if set to true will remove any unknown fields
// (excluding those starting with "user.") which can be used when translating a volume config to a
// different storage driver that has different options.
func (d *common) validateVolume(vol Volume, driverRules map[string]func(value string) error, removeUnknownKeys bool) error {
	checkedFields := map[string]struct{}{}

	// Get rules common for all drivers.
	rules := d.commonRules.VolumeRules(vol)

	// Merge driver specific rules into common rules.
	for field, validator := range driverRules {
		rules[field] = validator
	}

	// Run the validator against each field.
	for k, validator := range rules {
		checkedFields[k] = struct{}{} //Mark field as checked.
		err := validator(vol.config[k])
		if err != nil {
			return errors.Wrapf(err, "Invalid value for volume %q option %q", vol.name, k)
		}
	}

	// Look for any unchecked fields, as these are unknown fields and validation should fail.
	for k := range vol.config {
		_, checked := checkedFields[k]
		if checked {
			continue
		}

		// User keys are not validated.
		if strings.HasPrefix(k, "user.") {
			continue
		}

		if removeUnknownKeys {
			delete(vol.config, k)
		} else {
			return fmt.Errorf("Invalid option for volume %q option %q", vol.name, k)
		}
	}

	// If volume type is not custom, don't allow "size" property.
	if vol.volType != VolumeTypeCustom && vol.config["size"] != "" {
		return fmt.Errorf("Volume %q property is only valid for custom volume types", "size")
	}

	return nil
}

// MigrationType returns the type of transfer methods to be used when doing migrations between pools
// in preference order.
func (d *common) MigrationTypes(contentType ContentType, refresh bool) []migration.Type {
	var transportType migration.MigrationFSType
	var rsyncFeatures []string

	// Do not pass compression argument to rsync if the associated
	// config key, that is rsync.compression, is set to false.
	if d.Config()["rsync.compression"] != "" && !shared.IsTrue(d.Config()["rsync.compression"]) {
		rsyncFeatures = []string{"xattrs", "delete", "bidirectional"}
	} else {
		rsyncFeatures = []string{"xattrs", "delete", "compress", "bidirectional"}
	}

	if contentType == ContentTypeBlock {
		transportType = migration.MigrationFSType_BLOCK_AND_RSYNC
	} else {
		transportType = migration.MigrationFSType_RSYNC
	}

	return []migration.Type{
		{
			FSType:   transportType,
			Features: rsyncFeatures,
		},
	}
}

// Name returns the pool name.
func (d *common) Name() string {
	return d.name
}

// Logger returns the current logger.
func (d *common) Logger() logger.Logger {
	return d.logger
}

// Config returns the storage pool config (as a copy, so not modifiable).
func (d *common) Config() map[string]string {
	confCopy := make(map[string]string, len(d.config))
	for k, v := range d.config {
		confCopy[k] = v
	}

	return confCopy
}

// ApplyPatch looks for a suitable patch and runs it.
func (d *common) ApplyPatch(name string) error {
	if d.patches == nil {
		return fmt.Errorf("The patch mechanism isn't implemented on pool %q", d.name)
	}

	// Locate the patch.
	patch, ok := d.patches[name]
	if !ok {
		return fmt.Errorf("Patch %q isn't implemented on pool %q", name, d.name)
	}

	// Handle cases where a patch isn't needed.
	if patch == nil {
		return nil
	}

	return patch()
}

// moveGPTAltHeader moves the GPT alternative header to the end of the disk device supplied.
// If the device supplied is not detected as not being a GPT disk then no action is taken and nil is returned.
// If the required sgdisk command is not available a warning is logged, but no error is returned, as really it is
// the job of the VM quest to ensure the partitions are resized to the size of the disk (as LXD does not dicatate
// what partition structure (if any) the disk should have. However we do attempt to move the GPT alternative
// header where possible so that the backup header is where it is expected in case of any corruption with the
// primary header.
func (d *common) moveGPTAltHeader(devPath string) error {
	path, err := exec.LookPath("sgdisk")
	if err != nil {
		d.logger.Warn("Skipped moving GPT alternative header to end of disk as sgdisk command not found", log.Ctx{"dev": devPath})
		return nil
	}

	_, err = shared.RunCommand(path, "--move-second-header", devPath)
	if err == nil {
		d.logger.Debug("Moved GPT alternative header to end of disk", log.Ctx{"dev": devPath})
		return nil
	}

	runErr, ok := err.(shared.RunError)
	if ok {
		exitError, ok := runErr.Err.(*exec.ExitError)
		if ok {
			// sgdisk manpage says exit status 3 means:
			// "Non-GPT disk detected and no -g option, but operation requires a write action".
			if exitError.ExitCode() == 3 {
				return nil // Non-error as non-GPT disk specified.
			}
		}
	}

	return err
}

// runFiller runs the supplied filler, and setting the returned volume size back into filler.
func (d *common) runFiller(vol Volume, devPath string, filler *VolumeFiller) error {
	if filler == nil || filler.Fill == nil {
		return nil
	}

	// Allow filler to resize initial image volume as needed. Some storage drivers don't normally allow
	// image volumes to be resized due to them having read-only snapshots that cannot be resized. However
	// when creating the initial image volume and filling it before the snapshot is taken resizing can be
	// allowed and is required in order to support unpacking images larger than the default volume size.
	// The filler function is still expected to obey any volume size restrictions configured on the pool.
	if vol.Type() == VolumeTypeImage {
		vol.allowUnsafeResize = true
	}

	vol.driver.Logger().Debug("Running filler function", log.Ctx{"dev": devPath, "path": vol.MountPath()})
	volSize, err := filler.Fill(vol, devPath)
	if err != nil {
		return err
	}

	filler.Size = volSize
	return nil
}

// createVolumeFromBackupInstancePostHookResize provides a common post-hook that resizes the an volume to the size
// specified in the volume's config. Can be used as the post hook function returned from createVolumeFromBackup
// to allow the restored instance volume to be sized correctly after the DB records have been recreated.
func (d *common) createVolumeFromBackupInstancePostHookResize(driver Driver, vol Volume, op *operations.Operation) error {
	size := vol.ExpandedConfig("size")
	if size != "" {
		d.logger.Debug("Applying volume quota from root disk config", log.Ctx{"size": size})
		err := driver.SetVolumeQuota(vol, size, op)
		if err != nil {
			// The restored volume can end up being larger than the root disk config's size
			// property due to the block boundary rounding some storage drivers use. As such
			// if the restored volume is larger than the config's size and it cannot be shrunk
			// to the equivalent size on the target storage driver, don't fail as the backup
			// has still been restored successfully.
			if errors.Cause(err) == ErrCannotBeShrunk {
				d.logger.Warn("Could not apply volume quota from root disk config as restored volume cannot be shrunk", log.Ctx{"size": size})
			} else {
				return errors.Wrapf(err, "Failed applying volume quota to root disk")
			}
		}
	}

	return nil
}
<commit_msg>lxd/storage/drivers/driver/common: runFiller comment improvement<commit_after>package drivers

import (
	"fmt"
	"os/exec"
	"strings"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/migration"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/logger"
)

type common struct {
	name        string
	config      map[string]string
	getVolID    func(volType VolumeType, volName string) (int64, error)
	commonRules *Validators
	state       *state.State
	logger      logger.Logger
	patches     map[string]func() error
}

func (d *common) init(state *state.State, name string, config map[string]string, logger logger.Logger, volIDFunc func(volType VolumeType, volName string) (int64, error), commonRules *Validators) {
	d.name = name
	d.config = config
	d.getVolID = volIDFunc
	d.commonRules = commonRules
	d.state = state
	d.logger = logger
}

// isRemote returns false indicating this driver does not use remote storage.
func (d *common) isRemote() bool {
	return false
}

// validatePool validates a pool config against common rules and optional driver specific rules.
func (d *common) validatePool(config map[string]string, driverRules map[string]func(value string) error) error {
	checkedFields := map[string]struct{}{}

	// Get rules common for all drivers.
	rules := d.commonRules.PoolRules()

	// Merge driver specific rules into common rules.
	for field, validator := range driverRules {
		rules[field] = validator
	}

	// Run the validator against each field.
	for k, validator := range rules {
		checkedFields[k] = struct{}{} //Mark field as checked.
		err := validator(config[k])
		if err != nil {
			return errors.Wrapf(err, "Invalid value for pool %q option %q", d.name, k)
		}
	}

	// Look for any unchecked fields, as these are unknown fields and validation should fail.
	for k := range config {
		_, checked := checkedFields[k]
		if checked {
			continue
		}

		// User keys are not validated.
		if strings.HasPrefix(k, "user.") {
			continue
		}

		return fmt.Errorf("Invalid option for pool %q option %q", d.name, k)
	}

	return nil
}

// FillVolumeConfig populate volume with default config.
func (d *common) FillVolumeConfig(vol Volume) error {
	return nil
}

// validateVolume validates a volume config against common rules and optional driver specific rules.
// This functions has a removeUnknownKeys option that if set to true will remove any unknown fields
// (excluding those starting with "user.") which can be used when translating a volume config to a
// different storage driver that has different options.
func (d *common) validateVolume(vol Volume, driverRules map[string]func(value string) error, removeUnknownKeys bool) error {
	checkedFields := map[string]struct{}{}

	// Get rules common for all drivers.
	rules := d.commonRules.VolumeRules(vol)

	// Merge driver specific rules into common rules.
	for field, validator := range driverRules {
		rules[field] = validator
	}

	// Run the validator against each field.
	for k, validator := range rules {
		checkedFields[k] = struct{}{} //Mark field as checked.
		err := validator(vol.config[k])
		if err != nil {
			return errors.Wrapf(err, "Invalid value for volume %q option %q", vol.name, k)
		}
	}

	// Look for any unchecked fields, as these are unknown fields and validation should fail.
	for k := range vol.config {
		_, checked := checkedFields[k]
		if checked {
			continue
		}

		// User keys are not validated.
		if strings.HasPrefix(k, "user.") {
			continue
		}

		if removeUnknownKeys {
			delete(vol.config, k)
		} else {
			return fmt.Errorf("Invalid option for volume %q option %q", vol.name, k)
		}
	}

	// If volume type is not custom, don't allow "size" property.
	if vol.volType != VolumeTypeCustom && vol.config["size"] != "" {
		return fmt.Errorf("Volume %q property is only valid for custom volume types", "size")
	}

	return nil
}

// MigrationType returns the type of transfer methods to be used when doing migrations between pools
// in preference order.
func (d *common) MigrationTypes(contentType ContentType, refresh bool) []migration.Type {
	var transportType migration.MigrationFSType
	var rsyncFeatures []string

	// Do not pass compression argument to rsync if the associated
	// config key, that is rsync.compression, is set to false.
	if d.Config()["rsync.compression"] != "" && !shared.IsTrue(d.Config()["rsync.compression"]) {
		rsyncFeatures = []string{"xattrs", "delete", "bidirectional"}
	} else {
		rsyncFeatures = []string{"xattrs", "delete", "compress", "bidirectional"}
	}

	if contentType == ContentTypeBlock {
		transportType = migration.MigrationFSType_BLOCK_AND_RSYNC
	} else {
		transportType = migration.MigrationFSType_RSYNC
	}

	return []migration.Type{
		{
			FSType:   transportType,
			Features: rsyncFeatures,
		},
	}
}

// Name returns the pool name.
func (d *common) Name() string {
	return d.name
}

// Logger returns the current logger.
func (d *common) Logger() logger.Logger {
	return d.logger
}

// Config returns the storage pool config (as a copy, so not modifiable).
func (d *common) Config() map[string]string {
	confCopy := make(map[string]string, len(d.config))
	for k, v := range d.config {
		confCopy[k] = v
	}

	return confCopy
}

// ApplyPatch looks for a suitable patch and runs it.
func (d *common) ApplyPatch(name string) error {
	if d.patches == nil {
		return fmt.Errorf("The patch mechanism isn't implemented on pool %q", d.name)
	}

	// Locate the patch.
	patch, ok := d.patches[name]
	if !ok {
		return fmt.Errorf("Patch %q isn't implemented on pool %q", name, d.name)
	}

	// Handle cases where a patch isn't needed.
	if patch == nil {
		return nil
	}

	return patch()
}

// moveGPTAltHeader moves the GPT alternative header to the end of the disk device supplied.
// If the device supplied is not detected as not being a GPT disk then no action is taken and nil is returned.
// If the required sgdisk command is not available a warning is logged, but no error is returned, as really it is
// the job of the VM quest to ensure the partitions are resized to the size of the disk (as LXD does not dicatate
// what partition structure (if any) the disk should have. However we do attempt to move the GPT alternative
// header where possible so that the backup header is where it is expected in case of any corruption with the
// primary header.
func (d *common) moveGPTAltHeader(devPath string) error {
	path, err := exec.LookPath("sgdisk")
	if err != nil {
		d.logger.Warn("Skipped moving GPT alternative header to end of disk as sgdisk command not found", log.Ctx{"dev": devPath})
		return nil
	}

	_, err = shared.RunCommand(path, "--move-second-header", devPath)
	if err == nil {
		d.logger.Debug("Moved GPT alternative header to end of disk", log.Ctx{"dev": devPath})
		return nil
	}

	runErr, ok := err.(shared.RunError)
	if ok {
		exitError, ok := runErr.Err.(*exec.ExitError)
		if ok {
			// sgdisk manpage says exit status 3 means:
			// "Non-GPT disk detected and no -g option, but operation requires a write action".
			if exitError.ExitCode() == 3 {
				return nil // Non-error as non-GPT disk specified.
			}
		}
	}

	return err
}

// runFiller runs the supplied filler, and setting the returned volume size back into filler.
func (d *common) runFiller(vol Volume, devPath string, filler *VolumeFiller) error {
	if filler == nil || filler.Fill == nil {
		return nil
	}

	// Allow filler to resize initial image volume as needed. Some storage drivers don't normally allow
	// image volumes to be resized due to them having read-only snapshots that cannot be resized. However
	// when creating the initial image volume and filling it before the snapshot is taken resizing can be
	// allowed and is required in order to support unpacking images larger than the default volume size.
	// The filler function is still expected to obey any volume size restrictions configured on the pool.
	// Also needed allow unsafe resize to disable filesystem resize safety checks. This is safe because if for
	// some reason an error occurs the volume will be discarded rather than leaving a corrupt filesystem.
	if vol.Type() == VolumeTypeImage {
		vol.allowUnsafeResize = true
	}

	vol.driver.Logger().Debug("Running filler function", log.Ctx{"dev": devPath, "path": vol.MountPath()})
	volSize, err := filler.Fill(vol, devPath)
	if err != nil {
		return err
	}

	filler.Size = volSize
	return nil
}

// createVolumeFromBackupInstancePostHookResize provides a common post-hook that resizes the an volume to the size
// specified in the volume's config. Can be used as the post hook function returned from createVolumeFromBackup
// to allow the restored instance volume to be sized correctly after the DB records have been recreated.
func (d *common) createVolumeFromBackupInstancePostHookResize(driver Driver, vol Volume, op *operations.Operation) error {
	size := vol.ExpandedConfig("size")
	if size != "" {
		d.logger.Debug("Applying volume quota from root disk config", log.Ctx{"size": size})
		err := driver.SetVolumeQuota(vol, size, op)
		if err != nil {
			// The restored volume can end up being larger than the root disk config's size
			// property due to the block boundary rounding some storage drivers use. As such
			// if the restored volume is larger than the config's size and it cannot be shrunk
			// to the equivalent size on the target storage driver, don't fail as the backup
			// has still been restored successfully.
			if errors.Cause(err) == ErrCannotBeShrunk {
				d.logger.Warn("Could not apply volume quota from root disk config as restored volume cannot be shrunk", log.Ctx{"size": size})
			} else {
				return errors.Wrapf(err, "Failed applying volume quota to root disk")
			}
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package drivers

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/lxc/lxd/lxd/migration"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
)

type common struct {
	name           string
	config         map[string]string
	getVolID       func(volType VolumeType, volName string) (int64, error)
	getCommonRules func() map[string]func(string) error
	state          *state.State
	logger         logger.Logger
}

func (d *common) init(state *state.State, name string, config map[string]string, logger logger.Logger, volIDFunc func(volType VolumeType, volName string) (int64, error), commonRulesFunc func() map[string]func(string) error) error {
	d.name = name
	d.config = config
	d.getVolID = volIDFunc
	d.getCommonRules = commonRulesFunc
	d.state = state
	d.logger = logger

	return d.load()
}

func (d *common) load() error {
	return nil
}

// validateVolume validates a volume config against common rules and optional driver specific rules.
// This functions has a removeUnknownKeys option that if set to true will remove any unknown fields
// (excluding those starting with "user.") which can be used when translating a volume config to a
// different storage driver that has different options.
func (d *common) validateVolume(vol Volume, driverRules map[string]func(value string) error, removeUnknownKeys bool) error {
	checkedFields := map[string]struct{}{}

	// Get rules common for all drivers.
	rules := d.getCommonRules()

	// Merge driver specific rules into common rules.
	for field, validator := range driverRules {
		rules[field] = validator
	}

	// Run the validator against each field.
	for k, validator := range rules {
		checkedFields[k] = struct{}{} //Mark field as checked.
		err := validator(vol.config[k])
		if err != nil {
			return fmt.Errorf("Invalid value for volume option %s: %v", k, err)
		}
	}

	// Look for any unchecked fields, as these are unknown fields and validation should fail.
	for k := range vol.config {
		_, checked := checkedFields[k]
		if checked {
			continue
		}

		// User keys are not validated.
		if strings.HasPrefix(k, "user.") {
			continue
		}

		if removeUnknownKeys {
			delete(vol.config, k)
		} else {
			return fmt.Errorf("Invalid volume option: %s", k)
		}
	}

	// If volume type is not custom, don't allow "size" property.
	if vol.volType != VolumeTypeCustom && vol.config["size"] != "" {
		return fmt.Errorf("Volume 'size' property is only valid for custom volume types")
	}

	return nil
}

// MigrationType returns the type of transfer methods to be used when doing migrations between pools
// in preference order.
func (d *common) MigrationTypes(contentType ContentType, refresh bool) []migration.Type {
	if contentType != ContentTypeFS {
		return nil
	}

	return []migration.Type{
		{
			FSType:   migration.MigrationFSType_RSYNC,
			Features: []string{"xattrs", "delete", "compress", "bidirectional"},
		},
	}
}

// Config returns the storage pool config (as a copy, so not modifiable).
func (d *common) Config() map[string]string {
	confCopy := make(map[string]string, len(d.config))
	for k, v := range d.config {
		confCopy[k] = v
	}

	return confCopy
}

// vfsGetResources is a generic GetResources implementation for VFS-only drivers.
func (d *common) vfsGetResources() (*api.ResourcesStoragePool, error) {
	// Get the VFS information
	st, err := shared.Statvfs(GetPoolMountPath(d.name))
	if err != nil {
		return nil, err
	}

	// Fill in the struct
	res := api.ResourcesStoragePool{}
	res.Space.Total = st.Blocks * uint64(st.Bsize)
	res.Space.Used = (st.Blocks - st.Bfree) * uint64(st.Bsize)

	// Some filesystems don't report inodes since they allocate them
	// dynamically e.g. btrfs.
	if st.Files > 0 {
		res.Inodes.Total = st.Files
		res.Inodes.Used = st.Files - st.Ffree
	}

	return &res, nil
}

// vfsRenameVolume is a generic RenameVolume implementation for VFS-only drivers.
func (d *common) vfsRenameVolume(vol Volume, newVolName string, op *operations.Operation) error {
	// Create new snapshots directory.
	snapshotDir := GetVolumeSnapshotDir(d.name, vol.volType, newVolName)

	err := os.MkdirAll(snapshotDir, 0711)
	if err != nil {
		return err
	}

	type volRevert struct {
		oldPath string
		newPath string
	}

	// Create slice to record paths renamed if revert needed later.
	revertPaths := []volRevert{}
	defer func() {
		// Remove any paths rename if we are reverting.
		for _, vol := range revertPaths {
			os.Rename(vol.newPath, vol.oldPath)
		}

		// Remove the new snapshot directory if we are reverting.
		if len(revertPaths) > 0 {
			err = os.RemoveAll(snapshotDir)
		}
	}()

	// Rename any snapshots of the volume too.
	snapshots, err := vol.Snapshots(op)
	if err != nil {
		return err
	}

	for _, snapshot := range snapshots {
		oldPath := snapshot.MountPath()
		_, snapName, _ := shared.InstanceGetParentAndSnapshotName(snapshot.name)
		newPath := GetVolumeMountPath(d.name, vol.volType, GetSnapshotVolumeName(newVolName, snapName))

		err := os.Rename(oldPath, newPath)
		if err != nil {
			return err
		}

		revertPaths = append(revertPaths, volRevert{
			oldPath: oldPath,
			newPath: newPath,
		})
	}

	oldPath := GetVolumeMountPath(d.name, vol.volType, vol.name)
	newPath := GetVolumeMountPath(d.name, vol.volType, newVolName)
	err = os.Rename(oldPath, newPath)
	if err != nil {
		return err
	}

	revertPaths = append(revertPaths, volRevert{
		oldPath: oldPath,
		newPath: newPath,
	})

	// Remove old snapshots directory.
	oldSnapshotDir := GetVolumeSnapshotDir(d.name, vol.volType, vol.name)

	err = os.RemoveAll(oldSnapshotDir)
	if err != nil {
		return err
	}

	revertPaths = nil
	return nil
}

// vfsVolumeSnapshots is a generic VolumeSnapshots implementation for VFS-only drivers.
func (d *common) vfsVolumeSnapshots(vol Volume, op *operations.Operation) ([]string, error) {
	snapshotDir := GetVolumeSnapshotDir(d.name, vol.volType, vol.name)
	snapshots := []string{}

	ents, err := ioutil.ReadDir(snapshotDir)
	if err != nil {
		// If the snapshots directory doesn't exist, there are no snapshots.
		if os.IsNotExist(err) {
			return snapshots, nil
		}

		return nil, err
	}

	for _, ent := range ents {
		fileInfo, err := os.Stat(filepath.Join(snapshotDir, ent.Name()))
		if err != nil {
			return nil, err
		}

		if !fileInfo.IsDir() {
			continue
		}

		snapshots = append(snapshots, ent.Name())
	}

	return snapshots, nil
}

// vfsRenameVolumeSnapshot is a generic RenameVolumeSnapshot implementation for VFS-only drivers.
func (d *common) vfsRenameVolumeSnapshot(snapVol Volume, newSnapshotName string, op *operations.Operation) error {
	parentName, _, _ := shared.InstanceGetParentAndSnapshotName(snapVol.name)
	oldPath := snapVol.MountPath()
	newPath := GetVolumeMountPath(d.name, snapVol.volType, GetSnapshotVolumeName(parentName, newSnapshotName))

	err := os.Rename(oldPath, newPath)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>lxd/storage/common: Simplify vfsRenameVolume<commit_after>package drivers

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/lxc/lxd/lxd/migration"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
)

type common struct {
	name           string
	config         map[string]string
	getVolID       func(volType VolumeType, volName string) (int64, error)
	getCommonRules func() map[string]func(string) error
	state          *state.State
	logger         logger.Logger
}

func (d *common) init(state *state.State, name string, config map[string]string, logger logger.Logger, volIDFunc func(volType VolumeType, volName string) (int64, error), commonRulesFunc func() map[string]func(string) error) error {
	d.name = name
	d.config = config
	d.getVolID = volIDFunc
	d.getCommonRules = commonRulesFunc
	d.state = state
	d.logger = logger

	return d.load()
}

func (d *common) load() error {
	return nil
}

// validateVolume validates a volume config against common rules and optional driver specific rules.
// This functions has a removeUnknownKeys option that if set to true will remove any unknown fields
// (excluding those starting with "user.") which can be used when translating a volume config to a
// different storage driver that has different options.
func (d *common) validateVolume(vol Volume, driverRules map[string]func(value string) error, removeUnknownKeys bool) error {
	checkedFields := map[string]struct{}{}

	// Get rules common for all drivers.
	rules := d.getCommonRules()

	// Merge driver specific rules into common rules.
	for field, validator := range driverRules {
		rules[field] = validator
	}

	// Run the validator against each field.
	for k, validator := range rules {
		checkedFields[k] = struct{}{} //Mark field as checked.
		err := validator(vol.config[k])
		if err != nil {
			return fmt.Errorf("Invalid value for volume option %s: %v", k, err)
		}
	}

	// Look for any unchecked fields, as these are unknown fields and validation should fail.
	for k := range vol.config {
		_, checked := checkedFields[k]
		if checked {
			continue
		}

		// User keys are not validated.
		if strings.HasPrefix(k, "user.") {
			continue
		}

		if removeUnknownKeys {
			delete(vol.config, k)
		} else {
			return fmt.Errorf("Invalid volume option: %s", k)
		}
	}

	// If volume type is not custom, don't allow "size" property.
	if vol.volType != VolumeTypeCustom && vol.config["size"] != "" {
		return fmt.Errorf("Volume 'size' property is only valid for custom volume types")
	}

	return nil
}

// MigrationType returns the type of transfer methods to be used when doing migrations between pools
// in preference order.
func (d *common) MigrationTypes(contentType ContentType, refresh bool) []migration.Type {
	if contentType != ContentTypeFS {
		return nil
	}

	return []migration.Type{
		{
			FSType:   migration.MigrationFSType_RSYNC,
			Features: []string{"xattrs", "delete", "compress", "bidirectional"},
		},
	}
}

// Config returns the storage pool config (as a copy, so not modifiable).
func (d *common) Config() map[string]string {
	confCopy := make(map[string]string, len(d.config))
	for k, v := range d.config {
		confCopy[k] = v
	}

	return confCopy
}

// vfsGetResources is a generic GetResources implementation for VFS-only drivers.
func (d *common) vfsGetResources() (*api.ResourcesStoragePool, error) {
	// Get the VFS information
	st, err := shared.Statvfs(GetPoolMountPath(d.name))
	if err != nil {
		return nil, err
	}

	// Fill in the struct
	res := api.ResourcesStoragePool{}
	res.Space.Total = st.Blocks * uint64(st.Bsize)
	res.Space.Used = (st.Blocks - st.Bfree) * uint64(st.Bsize)

	// Some filesystems don't report inodes since they allocate them
	// dynamically e.g. btrfs.
	if st.Files > 0 {
		res.Inodes.Total = st.Files
		res.Inodes.Used = st.Files - st.Ffree
	}

	return &res, nil
}

// vfsRenameVolume is a generic RenameVolume implementation for VFS-only drivers.
func (d *common) vfsRenameVolume(vol Volume, newVolName string, op *operations.Operation) error {
	// Rename the volume itself.
	srcVolumePath := GetVolumeMountPath(d.name, vol.volType, vol.name)
	dstVolumePath := GetVolumeMountPath(d.name, vol.volType, newVolName)

	err := os.Rename(srcVolumePath, dstVolumePath)
	if err != nil {
		return err
	}

	revertRename := true
	defer func() {
		if !revertRename {
			return
		}

		os.Rename(dstVolumePath, srcVolumePath)
	}()

	// And if present, the snapshots too.
	srcSnapshotDir := GetVolumeSnapshotDir(d.name, vol.volType, vol.name)
	dstSnapshotDir := GetVolumeSnapshotDir(d.name, vol.volType, newVolName)

	if shared.PathExists(srcSnapshotDir) {
		err = os.Rename(srcSnapshotDir, dstSnapshotDir)
		if err != nil {
			return err
		}
	}

	revertRename = false
	return nil
}

// vfsVolumeSnapshots is a generic VolumeSnapshots implementation for VFS-only drivers.
func (d *common) vfsVolumeSnapshots(vol Volume, op *operations.Operation) ([]string, error) {
	snapshotDir := GetVolumeSnapshotDir(d.name, vol.volType, vol.name)
	snapshots := []string{}

	ents, err := ioutil.ReadDir(snapshotDir)
	if err != nil {
		// If the snapshots directory doesn't exist, there are no snapshots.
		if os.IsNotExist(err) {
			return snapshots, nil
		}

		return nil, err
	}

	for _, ent := range ents {
		fileInfo, err := os.Stat(filepath.Join(snapshotDir, ent.Name()))
		if err != nil {
			return nil, err
		}

		if !fileInfo.IsDir() {
			continue
		}

		snapshots = append(snapshots, ent.Name())
	}

	return snapshots, nil
}

// vfsRenameVolumeSnapshot is a generic RenameVolumeSnapshot implementation for VFS-only drivers.
func (d *common) vfsRenameVolumeSnapshot(snapVol Volume, newSnapshotName string, op *operations.Operation) error {
	parentName, _, _ := shared.InstanceGetParentAndSnapshotName(snapVol.name)
	oldPath := snapVol.MountPath()
	newPath := GetVolumeMountPath(d.name, snapVol.volType, GetSnapshotVolumeName(parentName, newSnapshotName))

	err := os.Rename(oldPath, newPath)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>