Unverified Commit f49464e8 authored by k8s-ci-robot's avatar k8s-ci-robot Committed by GitHub

Merge pull request #70811 from humblec/heketi-vendor-update

Update heketi dependencies to sha@558b29266ce0a873991ecfb3edc41a668a998514
parents c8b3d0cf 92fa290e
......@@ -1640,6 +1640,16 @@
"Rev": "d2eab7d93009e9215fc85b2faa2c2f2a98c2af48"
},
{
"ImportPath": "github.com/go-ozzo/ozzo-validation",
"Comment": "v3.5.0",
"Rev": "106681dbb37bfa3e7683c4c8129cb7f5925ea3e9"
},
{
"ImportPath": "github.com/go-ozzo/ozzo-validation/is",
"Comment": "v3.5.0",
"Rev": "106681dbb37bfa3e7683c4c8129cb7f5925ea3e9"
},
{
"ImportPath": "github.com/go-sql-driver/mysql",
"Comment": "v1.3.0",
"Rev": "a0583e0143b1624142adab07e0e97fe106d99561"
......@@ -2285,18 +2295,18 @@
},
{
"ImportPath": "github.com/heketi/heketi/client/api/go-client",
"Comment": "v4.0.0-95-gaaf40619d85fda",
"Rev": "aaf40619d85fda757e7a1c1ea1b5118cea65594b"
"Comment": "v8.0.0-49-g558b29266ce0a8",
"Rev": "558b29266ce0a873991ecfb3edc41a668a998514"
},
{
"ImportPath": "github.com/heketi/heketi/pkg/glusterfs/api",
"Comment": "v4.0.0-95-gaaf40619d85fda",
"Rev": "aaf40619d85fda757e7a1c1ea1b5118cea65594b"
"Comment": "v8.0.0-49-g558b29266ce0a8",
"Rev": "558b29266ce0a873991ecfb3edc41a668a998514"
},
{
"ImportPath": "github.com/heketi/heketi/pkg/utils",
"Comment": "v4.0.0-95-gaaf40619d85fda",
"Rev": "aaf40619d85fda757e7a1c1ea1b5118cea65594b"
"Comment": "v8.0.0-49-g558b29266ce0a8",
"Rev": "558b29266ce0a873991ecfb3edc41a668a998514"
},
{
"ImportPath": "github.com/imdario/mergo",
......@@ -2409,11 +2419,6 @@
"Rev": "093a0c3888753c2056e7373183693d670c6bba01"
},
{
"ImportPath": "github.com/lpabon/godbc",
"Comment": "v1.0-1-g9577782540c139",
"Rev": "9577782540c1398b710ddae1b86268ba03a19b0c"
},
{
"ImportPath": "github.com/magiconair/properties",
"Comment": "v1.7.0-4-g61b492c03cf472",
"Rev": "61b492c03cf472e0c6419be5899b8e0dc28b1b88"
......
......@@ -207,6 +207,7 @@ filegroup(
"//vendor/github.com/go-openapi/strfmt:all-srcs",
"//vendor/github.com/go-openapi/swag:all-srcs",
"//vendor/github.com/go-openapi/validate:all-srcs",
"//vendor/github.com/go-ozzo/ozzo-validation:all-srcs",
"//vendor/github.com/go-sql-driver/mysql:all-srcs",
"//vendor/github.com/godbus/dbus:all-srcs",
"//vendor/github.com/gogo/protobuf/gogoproto:all-srcs",
......@@ -296,7 +297,6 @@ filegroup(
"//vendor/github.com/libopenstorage/openstorage/pkg/parser:all-srcs",
"//vendor/github.com/libopenstorage/openstorage/pkg/units:all-srcs",
"//vendor/github.com/libopenstorage/openstorage/volume:all-srcs",
"//vendor/github.com/lpabon/godbc:all-srcs",
"//vendor/github.com/magiconair/properties:all-srcs",
"//vendor/github.com/mailru/easyjson/buffer:all-srcs",
"//vendor/github.com/mailru/easyjson/jlexer:all-srcs",
......
language: go
go:
- 1.8
- 1.9
- tip
install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
- go list -f '{{range .Imports}}{{.}} {{end}}' ./... | xargs go get -v
- go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs go get -v
script:
- go test -v -covermode=count -coverprofile=coverage.out
- $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"date.go",
"error.go",
"in.go",
"length.go",
"match.go",
"minmax.go",
"multipleof.go",
"not_in.go",
"not_nil.go",
"required.go",
"string.go",
"struct.go",
"util.go",
"validation.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/go-ozzo/ozzo-validation",
importpath = "github.com/go-ozzo/ozzo-validation",
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/github.com/go-ozzo/ozzo-validation/is:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
The MIT License (MIT)
Copyright (c) 2016, Qiang Xue
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.
# Upgrade Instructions
## Upgrade from 2.x to 3.x
* Instead of using `StructRules` to define struct validation rules, use `ValidateStruct()` to declare and perform
struct validation. The following code snippet shows how to modify your code:
```go
// 2.x usage
err := validation.StructRules{}.
Add("Street", validation.Required, validation.Length(5, 50)).
Add("City", validation.Required, validation.Length(5, 50)).
Add("State", validation.Required, validation.Match(regexp.MustCompile("^[A-Z]{2}$"))).
Add("Zip", validation.Required, validation.Match(regexp.MustCompile("^[0-9]{5}$"))).
Validate(a)
// 3.x usage
err := validation.ValidateStruct(&a,
validation.Field(&a.Street, validation.Required, validation.Length(5, 50)),
validation.Field(&a.City, validation.Required, validation.Length(5, 50)),
validation.Field(&a.State, validation.Required, validation.Match(regexp.MustCompile("^[A-Z]{2}$"))),
validation.Field(&a.Zip, validation.Required, validation.Match(regexp.MustCompile("^[0-9]{5}$"))),
)
```
* Instead of using `Rules` to declare a rule list and use it to validate a value, call `Validate()` with the rules directly.
```go
data := "example"
// 2.x usage
rules := validation.Rules{
validation.Required,
validation.Length(5, 100),
is.URL,
}
err := rules.Validate(data)
// 3.x usage
err := validation.Validate(data,
validation.Required,
validation.Length(5, 100),
is.URL,
)
```
* The default struct tags used for determining error keys is changed from `validation` to `json`. You may modify
`validation.ErrorTag` to change it back.
\ No newline at end of file
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import (
"errors"
"time"
)
type DateRule struct {
layout string
min, max time.Time
message string
rangeMessage string
}
// Date returns a validation rule that checks if a string value is in a format that can be parsed into a date.
// The format of the date should be specified as the layout parameter which accepts the same value as that for time.Parse.
// For example,
// validation.Date(time.ANSIC)
// validation.Date("02 Jan 06 15:04 MST")
// validation.Date("2006-01-02")
//
// By calling Min() and/or Max(), you can let the Date rule to check if a parsed date value is within
// the specified date range.
//
// An empty value is considered valid. Use the Required rule to make sure a value is not empty.
func Date(layout string) *DateRule {
return &DateRule{
layout: layout,
message: "must be a valid date",
rangeMessage: "the data is out of range",
}
}
// Error sets the error message that is used when the value being validated is not a valid date.
func (r *DateRule) Error(message string) *DateRule {
r.message = message
return r
}
// RangeError sets the error message that is used when the value being validated is out of the specified Min/Max date range.
func (r *DateRule) RangeError(message string) *DateRule {
r.rangeMessage = message
return r
}
// Min sets the minimum date range. A zero value means skipping the minimum range validation.
func (r *DateRule) Min(min time.Time) *DateRule {
r.min = min
return r
}
// Max sets the maximum date range. A zero value means skipping the maximum range validation.
func (r *DateRule) Max(max time.Time) *DateRule {
r.max = max
return r
}
// Validate checks if the given value is a valid date.
func (r *DateRule) Validate(value interface{}) error {
value, isNil := Indirect(value)
if isNil || IsEmpty(value) {
return nil
}
str, err := EnsureString(value)
if err != nil {
return err
}
date, err := time.Parse(r.layout, str)
if err != nil {
return errors.New(r.message)
}
if !r.min.IsZero() && r.min.After(date) || !r.max.IsZero() && date.After(r.max) {
return errors.New(r.rangeMessage)
}
return nil
}
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import (
"encoding/json"
"fmt"
"sort"
)
type (
// Errors represents the validation errors that are indexed by struct field names, map or slice keys.
Errors map[string]error
// InternalError represents an error that should NOT be treated as a validation error.
InternalError interface {
error
InternalError() error
}
internalError struct {
error
}
)
// NewInternalError wraps a given error into an InternalError.
func NewInternalError(err error) InternalError {
return &internalError{error: err}
}
// InternalError returns the actual error that it wraps around.
func (e *internalError) InternalError() error {
return e.error
}
// Error returns the error string of Errors.
func (es Errors) Error() string {
if len(es) == 0 {
return ""
}
keys := []string{}
for key := range es {
keys = append(keys, key)
}
sort.Strings(keys)
s := ""
for i, key := range keys {
if i > 0 {
s += "; "
}
if errs, ok := es[key].(Errors); ok {
s += fmt.Sprintf("%v: (%v)", key, errs)
} else {
s += fmt.Sprintf("%v: %v", key, es[key].Error())
}
}
return s + "."
}
// MarshalJSON converts the Errors into a valid JSON.
func (es Errors) MarshalJSON() ([]byte, error) {
errs := map[string]interface{}{}
for key, err := range es {
if ms, ok := err.(json.Marshaler); ok {
errs[key] = ms
} else {
errs[key] = err.Error()
}
}
return json.Marshal(errs)
}
// Filter removes all nils from Errors and returns back the updated Errors as an error.
// If the length of Errors becomes 0, it will return nil.
func (es Errors) Filter() error {
for key, value := range es {
if value == nil {
delete(es, key)
}
}
if len(es) == 0 {
return nil
}
return es
}
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import "errors"
// In returns a validation rule that checks if a value can be found in the given list of values.
// Note that the value being checked and the possible range of values must be of the same type.
// An empty value is considered valid. Use the Required rule to make sure a value is not empty.
func In(values ...interface{}) *InRule {
return &InRule{
elements: values,
message: "must be a valid value",
}
}
type InRule struct {
elements []interface{}
message string
}
// Validate checks if the given value is valid or not.
func (r *InRule) Validate(value interface{}) error {
value, isNil := Indirect(value)
if isNil || IsEmpty(value) {
return nil
}
for _, e := range r.elements {
if e == value {
return nil
}
}
return errors.New(r.message)
}
// Error sets the error message for the rule.
func (r *InRule) Error(message string) *InRule {
r.message = message
return r
}
......@@ -2,10 +2,14 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["godbc.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/lpabon/godbc",
importpath = "github.com/lpabon/godbc",
srcs = ["rules.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/go-ozzo/ozzo-validation/is",
importpath = "github.com/go-ozzo/ozzo-validation/is",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/asaskevich/govalidator:go_default_library",
"//vendor/github.com/go-ozzo/ozzo-validation:go_default_library",
],
)
filegroup(
......
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import (
"errors"
"fmt"
"unicode/utf8"
)
// Length returns a validation rule that checks if a value's length is within the specified range.
// If max is 0, it means there is no upper bound for the length.
// This rule should only be used for validating strings, slices, maps, and arrays.
// An empty value is considered valid. Use the Required rule to make sure a value is not empty.
func Length(min, max int) *LengthRule {
message := "the value must be empty"
if min == 0 && max > 0 {
message = fmt.Sprintf("the length must be no more than %v", max)
} else if min > 0 && max == 0 {
message = fmt.Sprintf("the length must be no less than %v", min)
} else if min > 0 && max > 0 {
if min == max {
message = fmt.Sprintf("the length must be exactly %v", min)
} else {
message = fmt.Sprintf("the length must be between %v and %v", min, max)
}
}
return &LengthRule{
min: min,
max: max,
message: message,
}
}
// RuneLength returns a validation rule that checks if a string's rune length is within the specified range.
// If max is 0, it means there is no upper bound for the length.
// This rule should only be used for validating strings, slices, maps, and arrays.
// An empty value is considered valid. Use the Required rule to make sure a value is not empty.
// If the value being validated is not a string, the rule works the same as Length.
func RuneLength(min, max int) *LengthRule {
r := Length(min, max)
r.rune = true
return r
}
type LengthRule struct {
min, max int
message string
rune bool
}
// Validate checks if the given value is valid or not.
func (v *LengthRule) Validate(value interface{}) error {
value, isNil := Indirect(value)
if isNil || IsEmpty(value) {
return nil
}
var (
l int
err error
)
if s, ok := value.(string); ok && v.rune {
l = utf8.RuneCountInString(s)
} else if l, err = LengthOfValue(value); err != nil {
return err
}
if v.min > 0 && l < v.min || v.max > 0 && l > v.max {
return errors.New(v.message)
}
return nil
}
// Error sets the error message for the rule.
func (v *LengthRule) Error(message string) *LengthRule {
v.message = message
return v
}
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import (
"errors"
"regexp"
)
// Match returns a validation rule that checks if a value matches the specified regular expression.
// This rule should only be used for validating strings and byte slices, or a validation error will be reported.
// An empty value is considered valid. Use the Required rule to make sure a value is not empty.
func Match(re *regexp.Regexp) *MatchRule {
return &MatchRule{
re: re,
message: "must be in a valid format",
}
}
type MatchRule struct {
re *regexp.Regexp
message string
}
// Validate checks if the given value is valid or not.
func (v *MatchRule) Validate(value interface{}) error {
value, isNil := Indirect(value)
if isNil {
return nil
}
isString, str, isBytes, bs := StringOrBytes(value)
if isString && (str == "" || v.re.MatchString(str)) {
return nil
} else if isBytes && (len(bs) == 0 || v.re.Match(bs)) {
return nil
}
return errors.New(v.message)
}
// Error sets the error message for the rule.
func (v *MatchRule) Error(message string) *MatchRule {
v.message = message
return v
}
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import (
"errors"
"fmt"
"reflect"
"time"
)
type ThresholdRule struct {
threshold interface{}
operator int
message string
}
const (
greaterThan = iota
greaterEqualThan
lessThan
lessEqualThan
)
// Min is a validation rule that checks if a value is greater or equal than the specified value.
// By calling Exclusive, the rule will check if the value is strictly greater than the specified value.
// Note that the value being checked and the threshold value must be of the same type.
// Only int, uint, float and time.Time types are supported.
// An empty value is considered valid. Please use the Required rule to make sure a value is not empty.
func Min(min interface{}) *ThresholdRule {
return &ThresholdRule{
threshold: min,
operator: greaterEqualThan,
message: fmt.Sprintf("must be no less than %v", min),
}
}
// Max is a validation rule that checks if a value is less or equal than the specified value.
// By calling Exclusive, the rule will check if the value is strictly less than the specified value.
// Note that the value being checked and the threshold value must be of the same type.
// Only int, uint, float and time.Time types are supported.
// An empty value is considered valid. Please use the Required rule to make sure a value is not empty.
func Max(max interface{}) *ThresholdRule {
return &ThresholdRule{
threshold: max,
operator: lessEqualThan,
message: fmt.Sprintf("must be no greater than %v", max),
}
}
// Exclusive sets the comparison to exclude the boundary value.
func (r *ThresholdRule) Exclusive() *ThresholdRule {
if r.operator == greaterEqualThan {
r.operator = greaterThan
r.message = fmt.Sprintf("must be greater than %v", r.threshold)
} else if r.operator == lessEqualThan {
r.operator = lessThan
r.message = fmt.Sprintf("must be less than %v", r.threshold)
}
return r
}
// Validate checks if the given value is valid or not.
func (r *ThresholdRule) Validate(value interface{}) error {
value, isNil := Indirect(value)
if isNil || IsEmpty(value) {
return nil
}
rv := reflect.ValueOf(r.threshold)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
v, err := ToInt(value)
if err != nil {
return err
}
if r.compareInt(rv.Int(), v) {
return nil
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
v, err := ToUint(value)
if err != nil {
return err
}
if r.compareUint(rv.Uint(), v) {
return nil
}
case reflect.Float32, reflect.Float64:
v, err := ToFloat(value)
if err != nil {
return err
}
if r.compareFloat(rv.Float(), v) {
return nil
}
case reflect.Struct:
t, ok := r.threshold.(time.Time)
if !ok {
return fmt.Errorf("type not supported: %v", rv.Type())
}
v, ok := value.(time.Time)
if !ok {
return fmt.Errorf("cannot convert %v to time.Time", reflect.TypeOf(value))
}
if v.IsZero() || r.compareTime(t, v) {
return nil
}
default:
return fmt.Errorf("type not supported: %v", rv.Type())
}
return errors.New(r.message)
}
// Error sets the error message for the rule.
func (r *ThresholdRule) Error(message string) *ThresholdRule {
r.message = message
return r
}
func (r *ThresholdRule) compareInt(threshold, value int64) bool {
switch r.operator {
case greaterThan:
return value > threshold
case greaterEqualThan:
return value >= threshold
case lessThan:
return value < threshold
default:
return value <= threshold
}
}
func (r *ThresholdRule) compareUint(threshold, value uint64) bool {
switch r.operator {
case greaterThan:
return value > threshold
case greaterEqualThan:
return value >= threshold
case lessThan:
return value < threshold
default:
return value <= threshold
}
}
func (r *ThresholdRule) compareFloat(threshold, value float64) bool {
switch r.operator {
case greaterThan:
return value > threshold
case greaterEqualThan:
return value >= threshold
case lessThan:
return value < threshold
default:
return value <= threshold
}
}
func (r *ThresholdRule) compareTime(threshold, value time.Time) bool {
switch r.operator {
case greaterThan:
return value.After(threshold)
case greaterEqualThan:
return value.After(threshold) || value.Equal(threshold)
case lessThan:
return value.Before(threshold)
default:
return value.Before(threshold) || value.Equal(threshold)
}
}
package validation
import (
"errors"
"fmt"
"reflect"
)
func MultipleOf(threshold interface{}) *multipleOfRule {
return &multipleOfRule{
threshold,
fmt.Sprintf("must be multiple of %v", threshold),
}
}
type multipleOfRule struct {
threshold interface{}
message string
}
// Error sets the error message for the rule.
func (r *multipleOfRule) Error(message string) *multipleOfRule {
r.message = message
return r
}
func (r *multipleOfRule) Validate(value interface{}) error {
rv := reflect.ValueOf(r.threshold)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
v, err := ToInt(value)
if err != nil {
return err
}
if v%rv.Int() == 0 {
return nil
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
v, err := ToUint(value)
if err != nil {
return err
}
if v%rv.Uint() == 0 {
return nil
}
default:
return fmt.Errorf("type not supported: %v", rv.Type())
}
return errors.New(r.message)
}
// Copyright 2018 Qiang Xue, Google LLC. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import (
"errors"
)
// NotIn returns a validation rule that checks if a value os absent from, the given list of values.
// Note that the value being checked and the possible range of values must be of the same type.
// An empty value is considered valid. Use the Required rule to make sure a value is not empty.
func NotIn(values ...interface{}) *NotInRule {
return &NotInRule{
elements: values,
message: "must not be in list",
}
}
type NotInRule struct {
elements []interface{}
message string
}
// Validate checks if the given value is valid or not.
func (r *NotInRule) Validate(value interface{}) error {
value, isNil := Indirect(value)
if isNil || IsEmpty(value) {
return nil
}
for _, e := range r.elements {
if e == value {
return errors.New(r.message)
}
}
return nil
}
// Error sets the error message for the rule.
func (r *NotInRule) Error(message string) *NotInRule {
r.message = message
return r
}
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import "errors"
// NotNil is a validation rule that checks if a value is not nil.
// NotNil only handles types including interface, pointer, slice, and map.
// All other types are considered valid.
var NotNil = &notNilRule{message: "is required"}
type notNilRule struct {
message string
}
// Validate checks if the given value is valid or not.
func (r *notNilRule) Validate(value interface{}) error {
_, isNil := Indirect(value)
if isNil {
return errors.New(r.message)
}
return nil
}
// Error sets the error message for the rule.
func (r *notNilRule) Error(message string) *notNilRule {
return &notNilRule{
message: message,
}
}
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import "errors"
// Required is a validation rule that checks if a value is not empty.
// A value is considered not empty if
// - integer, float: not zero
// - bool: true
// - string, array, slice, map: len() > 0
// - interface, pointer: not nil and the referenced value is not empty
// - any other types
var Required = &requiredRule{message: "cannot be blank", skipNil: false}
// NilOrNotEmpty checks if a value is a nil pointer or a value that is not empty.
// NilOrNotEmpty differs from Required in that it treats a nil pointer as valid.
var NilOrNotEmpty = &requiredRule{message: "cannot be blank", skipNil: true}
type requiredRule struct {
message string
skipNil bool
}
// Validate checks if the given value is valid or not.
func (v *requiredRule) Validate(value interface{}) error {
value, isNil := Indirect(value)
if v.skipNil && !isNil && IsEmpty(value) || !v.skipNil && (isNil || IsEmpty(value)) {
return errors.New(v.message)
}
return nil
}
// Error sets the error message for the rule.
func (v *requiredRule) Error(message string) *requiredRule {
return &requiredRule{
message: message,
skipNil: v.skipNil,
}
}
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import "errors"
type stringValidator func(string) bool
// StringRule is a rule that checks a string variable using a specified stringValidator.
type StringRule struct {
validate stringValidator
message string
}
// NewStringRule creates a new validation rule using a function that takes a string value and returns a bool.
// The rule returned will use the function to check if a given string or byte slice is valid or not.
// An empty value is considered to be valid. Please use the Required rule to make sure a value is not empty.
func NewStringRule(validator stringValidator, message string) *StringRule {
return &StringRule{
validate: validator,
message: message,
}
}
// Error sets the error message for the rule.
func (v *StringRule) Error(message string) *StringRule {
return NewStringRule(v.validate, message)
}
// Validate checks if the given value is valid or not.
func (v *StringRule) Validate(value interface{}) error {
value, isNil := Indirect(value)
if isNil || IsEmpty(value) {
return nil
}
str, err := EnsureString(value)
if err != nil {
return err
}
if v.validate(str) {
return nil
}
return errors.New(v.message)
}
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import (
"errors"
"fmt"
"reflect"
"strings"
)
var (
// ErrStructPointer is the error that a struct being validated is not specified as a pointer.
ErrStructPointer = errors.New("only a pointer to a struct can be validated")
)
type (
// ErrFieldPointer is the error that a field is not specified as a pointer.
ErrFieldPointer int
// ErrFieldNotFound is the error that a field cannot be found in the struct.
ErrFieldNotFound int
// FieldRules represents a rule set associated with a struct field.
FieldRules struct {
fieldPtr interface{}
rules []Rule
}
)
// Error returns the error string of ErrFieldPointer.
func (e ErrFieldPointer) Error() string {
return fmt.Sprintf("field #%v must be specified as a pointer", int(e))
}
// Error returns the error string of ErrFieldNotFound.
func (e ErrFieldNotFound) Error() string {
return fmt.Sprintf("field #%v cannot be found in the struct", int(e))
}
// ValidateStruct validates a struct by checking the specified struct fields against the corresponding validation rules.
// Note that the struct being validated must be specified as a pointer to it. If the pointer is nil, it is considered valid.
// Use Field() to specify struct fields that need to be validated. Each Field() call specifies a single field which
// should be specified as a pointer to the field. A field can be associated with multiple rules.
// For example,
//
// value := struct {
// Name string
// Value string
// }{"name", "demo"}
// err := validation.ValidateStruct(&value,
// validation.Field(&a.Name, validation.Required),
// validation.Field(&a.Value, validation.Required, validation.Length(5, 10)),
// )
// fmt.Println(err)
// // Value: the length must be between 5 and 10.
//
// An error will be returned if validation fails.
func ValidateStruct(structPtr interface{}, fields ...*FieldRules) error {
value := reflect.ValueOf(structPtr)
if value.Kind() != reflect.Ptr || !value.IsNil() && value.Elem().Kind() != reflect.Struct {
// must be a pointer to a struct
return NewInternalError(ErrStructPointer)
}
if value.IsNil() {
// treat a nil struct pointer as valid
return nil
}
value = value.Elem()
errs := Errors{}
for i, fr := range fields {
fv := reflect.ValueOf(fr.fieldPtr)
if fv.Kind() != reflect.Ptr {
return NewInternalError(ErrFieldPointer(i))
}
ft := findStructField(value, fv)
if ft == nil {
return NewInternalError(ErrFieldNotFound(i))
}
if err := Validate(fv.Elem().Interface(), fr.rules...); err != nil {
if ie, ok := err.(InternalError); ok && ie.InternalError() != nil {
return err
}
if ft.Anonymous {
// merge errors from anonymous struct field
if es, ok := err.(Errors); ok {
for name, value := range es {
errs[name] = value
}
continue
}
}
errs[getErrorFieldName(ft)] = err
}
}
if len(errs) > 0 {
return errs
}
return nil
}
// Field specifies a struct field and the corresponding validation rules.
// The struct field must be specified as a pointer to it.
func Field(fieldPtr interface{}, rules ...Rule) *FieldRules {
return &FieldRules{
fieldPtr: fieldPtr,
rules: rules,
}
}
// findStructField looks for a field in the given struct.
// The field being looked for should be a pointer to the actual struct field.
// If found, the field info will be returned. Otherwise, nil will be returned.
func findStructField(structValue reflect.Value, fieldValue reflect.Value) *reflect.StructField {
ptr := fieldValue.Pointer()
for i := structValue.NumField() - 1; i >= 0; i-- {
sf := structValue.Type().Field(i)
if ptr == structValue.Field(i).UnsafeAddr() {
// do additional type comparison because it's possible that the address of
// an embedded struct is the same as the first field of the embedded struct
if sf.Type == fieldValue.Elem().Type() {
return &sf
}
}
if sf.Anonymous {
// delve into anonymous struct to look for the field
fi := structValue.Field(i)
if sf.Type.Kind() == reflect.Ptr {
fi = fi.Elem()
}
if fi.Kind() == reflect.Struct {
if f := findStructField(fi, fieldValue); f != nil {
return f
}
}
}
}
return nil
}
// getErrorFieldName returns the name that should be used to represent the validation error of a struct field.
func getErrorFieldName(f *reflect.StructField) string {
if tag := f.Tag.Get(ErrorTag); tag != "" {
if cps := strings.SplitN(tag, ",", 2); cps[0] != "" {
return cps[0]
}
}
return f.Name
}
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import (
"database/sql/driver"
"errors"
"fmt"
"reflect"
"time"
)
var (
bytesType = reflect.TypeOf([]byte(nil))
valuerType = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
)
// EnsureString ensures the given value is a string.
// If the value is a byte slice, it will be typecast into a string.
// An error is returned otherwise.
func EnsureString(value interface{}) (string, error) {
v := reflect.ValueOf(value)
if v.Kind() == reflect.String {
return v.String(), nil
}
if v.Type() == bytesType {
return string(v.Interface().([]byte)), nil
}
return "", errors.New("must be either a string or byte slice")
}
// StringOrBytes typecasts a value into a string or byte slice.
// Boolean flags are returned to indicate if the typecasting succeeds or not.
func StringOrBytes(value interface{}) (isString bool, str string, isBytes bool, bs []byte) {
v := reflect.ValueOf(value)
if v.Kind() == reflect.String {
str = v.String()
isString = true
} else if v.Kind() == reflect.Slice && v.Type() == bytesType {
bs = v.Interface().([]byte)
isBytes = true
}
return
}
// LengthOfValue returns the length of a value that is a string, slice, map, or array.
// An error is returned for all other types.
func LengthOfValue(value interface{}) (int, error) {
v := reflect.ValueOf(value)
switch v.Kind() {
case reflect.String, reflect.Slice, reflect.Map, reflect.Array:
return v.Len(), nil
}
return 0, fmt.Errorf("cannot get the length of %v", v.Kind())
}
// ToInt converts the given value to an int64.
// An error is returned for all incompatible types.
func ToInt(value interface{}) (int64, error) {
v := reflect.ValueOf(value)
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int(), nil
}
return 0, fmt.Errorf("cannot convert %v to int64", v.Kind())
}
// ToUint converts the given value to an uint64.
// An error is returned for all incompatible types.
func ToUint(value interface{}) (uint64, error) {
v := reflect.ValueOf(value)
switch v.Kind() {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint(), nil
}
return 0, fmt.Errorf("cannot convert %v to uint64", v.Kind())
}
// ToFloat converts the given value to a float64.
// An error is returned for all incompatible types.
func ToFloat(value interface{}) (float64, error) {
v := reflect.ValueOf(value)
switch v.Kind() {
case reflect.Float32, reflect.Float64:
return v.Float(), nil
}
return 0, fmt.Errorf("cannot convert %v to float64", v.Kind())
}
// IsEmpty checks if a value is empty or not.
// A value is considered empty if
// - integer, float: zero
// - bool: false
// - string, array: len() == 0
// - slice, map: nil or len() == 0
// - interface, pointer: nil or the referenced value is empty
func IsEmpty(value interface{}) bool {
v := reflect.ValueOf(value)
switch v.Kind() {
case reflect.String, reflect.Array, reflect.Map, reflect.Slice:
return v.Len() == 0
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Invalid:
return true
case reflect.Interface, reflect.Ptr:
if v.IsNil() {
return true
}
return IsEmpty(v.Elem().Interface())
case reflect.Struct:
v, ok := value.(time.Time)
if ok && v.IsZero() {
return true
}
}
return false
}
// Indirect returns the value that the given interface or pointer references to.
// If the value implements driver.Valuer, it will deal with the value returned by
// the Value() method instead. A boolean value is also returned to indicate if
// the value is nil or not (only applicable to interface, pointer, map, and slice).
// If the value is neither an interface nor a pointer, it will be returned back.
func Indirect(value interface{}) (interface{}, bool) {
rv := reflect.ValueOf(value)
kind := rv.Kind()
switch kind {
case reflect.Invalid:
return nil, true
case reflect.Ptr, reflect.Interface:
if rv.IsNil() {
return nil, true
}
return Indirect(rv.Elem().Interface())
case reflect.Slice, reflect.Map, reflect.Func, reflect.Chan:
if rv.IsNil() {
return nil, true
}
}
if rv.Type().Implements(valuerType) {
return indirectValuer(value.(driver.Valuer))
}
return value, false
}
func indirectValuer(valuer driver.Valuer) (interface{}, bool) {
if value, err := valuer.Value(); value != nil && err == nil {
return Indirect(value)
}
return nil, true
}
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
// Package validation provides configurable and extensible rules for validating data of various types.
package validation
import (
"fmt"
"reflect"
"strconv"
)
type (
// Validatable is the interface indicating the type implementing it supports data validation.
Validatable interface {
// Validate validates the data and returns an error if validation fails.
Validate() error
}
// Rule represents a validation rule.
Rule interface {
// Validate validates a value and returns a value if validation fails.
Validate(value interface{}) error
}
// RuleFunc represents a validator function.
// You may wrap it as a Rule by calling By().
RuleFunc func(value interface{}) error
)
var (
// ErrorTag is the struct tag name used to customize the error field name for a struct field.
ErrorTag = "json"
// Skip is a special validation rule that indicates all rules following it should be skipped.
Skip = &skipRule{}
validatableType = reflect.TypeOf((*Validatable)(nil)).Elem()
)
// Validate validates the given value and returns the validation error, if any.
//
// Validate performs validation using the following steps:
// - validate the value against the rules passed in as parameters
// - if the value is a map and the map values implement `Validatable`, call `Validate` of every map value
// - if the value is a slice or array whose values implement `Validatable`, call `Validate` of every element
func Validate(value interface{}, rules ...Rule) error {
for _, rule := range rules {
if _, ok := rule.(*skipRule); ok {
return nil
}
if err := rule.Validate(value); err != nil {
return err
}
}
rv := reflect.ValueOf(value)
if (rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface) && rv.IsNil() {
return nil
}
if v, ok := value.(Validatable); ok {
return v.Validate()
}
switch rv.Kind() {
case reflect.Map:
if rv.Type().Elem().Implements(validatableType) {
return validateMap(rv)
}
case reflect.Slice, reflect.Array:
if rv.Type().Elem().Implements(validatableType) {
return validateSlice(rv)
}
case reflect.Ptr, reflect.Interface:
return Validate(rv.Elem().Interface())
}
return nil
}
// validateMap validates a map of validatable elements
func validateMap(rv reflect.Value) error {
errs := Errors{}
for _, key := range rv.MapKeys() {
if mv := rv.MapIndex(key).Interface(); mv != nil {
if err := mv.(Validatable).Validate(); err != nil {
errs[fmt.Sprintf("%v", key.Interface())] = err
}
}
}
if len(errs) > 0 {
return errs
}
return nil
}
// validateMap validates a slice/array of validatable elements
func validateSlice(rv reflect.Value) error {
errs := Errors{}
l := rv.Len()
for i := 0; i < l; i++ {
if ev := rv.Index(i).Interface(); ev != nil {
if err := ev.(Validatable).Validate(); err != nil {
errs[strconv.Itoa(i)] = err
}
}
}
if len(errs) > 0 {
return errs
}
return nil
}
type skipRule struct{}
func (r *skipRule) Validate(interface{}) error {
return nil
}
type inlineRule struct {
f RuleFunc
}
func (r *inlineRule) Validate(value interface{}) error {
return r.f(value)
}
// By wraps a RuleFunc into a Rule.
func By(f RuleFunc) Rule {
return &inlineRule{f}
}
......@@ -3,11 +3,16 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"admin.go",
"backup.go",
"block_volume.go",
"client.go",
"cluster.go",
"db.go",
"device.go",
"logging.go",
"node.go",
"operations.go",
"topology.go",
"volume.go",
],
......
//
// Copyright (c) 2018 The heketi Authors
//
// This file is licensed to you under your choice of the GNU Lesser
// General Public License, version 3 or any later version (LGPLv3 or
// later), as published by the Free Software Foundation,
// or under the Apache License, Version 2.0 <LICENSE-APACHE2 or
// http://www.apache.org/licenses/LICENSE-2.0>.
//
// You may not use this file except in compliance with those terms.
//
package client
import (
"bytes"
"encoding/json"
"net/http"
"github.com/heketi/heketi/pkg/glusterfs/api"
"github.com/heketi/heketi/pkg/utils"
)
func (c *Client) AdminStatusGet() (*api.AdminStatus, error) {
req, err := http.NewRequest("GET", c.host+"/admin", nil)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
// Set token
err = c.setToken(req)
if err != nil {
return nil, err
}
// Send request
r, err := c.do(req)
if err != nil {
return nil, err
}
var as api.AdminStatus
err = utils.GetJsonFromResponse(r, &as)
if err != nil {
return nil, err
}
return &as, nil
}
func (c *Client) AdminStatusSet(request *api.AdminStatus) error {
// Marshal request to JSON
buffer, err := json.Marshal(request)
if err != nil {
return err
}
req, err := http.NewRequest("POST", c.host+"/admin", bytes.NewBuffer(buffer))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
// Set token
err = c.setToken(req)
if err != nil {
return err
}
// Send request
r, err := c.do(req)
if err != nil {
return err
}
if r.StatusCode == http.StatusOK || r.StatusCode == http.StatusNoContent {
return nil
}
return utils.GetErrorFromResponse(r)
}
......@@ -37,12 +37,12 @@ func (c *Client) BackupDb(w io.Writer) error {
if err != nil {
return err
}
defer r.Body.Close()
if r.StatusCode != http.StatusOK {
return utils.GetErrorFromResponse(r)
}
// Read data from response
defer r.Body.Close()
_, err = io.Copy(w, r.Body)
return err
......
//
// Copyright (c) 2015 The heketi Authors
//
// This file is licensed to you under your choice of the GNU Lesser
// General Public License, version 3 or any later version (LGPLv3 or
// later), as published by the Free Software Foundation,
// or under the Apache License, Version 2.0 <LICENSE-APACHE2 or
// http://www.apache.org/licenses/LICENSE-2.0>.
//
// You may not use this file except in compliance with those terms.
//
package client
import (
"bytes"
"encoding/json"
"net/http"
"time"
"github.com/heketi/heketi/pkg/glusterfs/api"
"github.com/heketi/heketi/pkg/utils"
)
func (c *Client) BlockVolumeCreate(request *api.BlockVolumeCreateRequest) (
*api.BlockVolumeInfoResponse, error) {
buffer, err := json.Marshal(request)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST",
c.host+"/blockvolumes",
bytes.NewBuffer(buffer))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
err = c.setToken(req)
if err != nil {
return nil, err
}
r, err := c.do(req)
if err != nil {
return nil, err
}
defer r.Body.Close()
if r.StatusCode != http.StatusAccepted {
return nil, utils.GetErrorFromResponse(r)
}
r, err = c.waitForResponseWithTimer(r, time.Second)
if err != nil {
return nil, err
}
if r.StatusCode != http.StatusOK {
return nil, utils.GetErrorFromResponse(r)
}
var blockvolume api.BlockVolumeInfoResponse
err = utils.GetJsonFromResponse(r, &blockvolume)
if err != nil {
return nil, err
}
return &blockvolume, nil
}
func (c *Client) BlockVolumeList() (*api.BlockVolumeListResponse, error) {
req, err := http.NewRequest("GET", c.host+"/blockvolumes", nil)
if err != nil {
return nil, err
}
err = c.setToken(req)
if err != nil {
return nil, err
}
r, err := c.do(req)
if err != nil {
return nil, err
}
defer r.Body.Close()
if r.StatusCode != http.StatusOK {
return nil, utils.GetErrorFromResponse(r)
}
var blockvolumes api.BlockVolumeListResponse
err = utils.GetJsonFromResponse(r, &blockvolumes)
if err != nil {
return nil, err
}
return &blockvolumes, nil
}
func (c *Client) BlockVolumeInfo(id string) (*api.BlockVolumeInfoResponse, error) {
req, err := http.NewRequest("GET", c.host+"/blockvolumes/"+id, nil)
if err != nil {
return nil, err
}
err = c.setToken(req)
if err != nil {
return nil, err
}
r, err := c.do(req)
if err != nil {
return nil, err
}
defer r.Body.Close()
if r.StatusCode != http.StatusOK {
return nil, utils.GetErrorFromResponse(r)
}
var blockvolume api.BlockVolumeInfoResponse
err = utils.GetJsonFromResponse(r, &blockvolume)
if err != nil {
return nil, err
}
return &blockvolume, nil
}
func (c *Client) BlockVolumeDelete(id string) error {
req, err := http.NewRequest("DELETE", c.host+"/blockvolumes/"+id, nil)
if err != nil {
return err
}
err = c.setToken(req)
if err != nil {
return err
}
r, err := c.do(req)
if err != nil {
return err
}
defer r.Body.Close()
if r.StatusCode != http.StatusAccepted {
return utils.GetErrorFromResponse(r)
}
r, err = c.waitForResponseWithTimer(r, time.Second)
if err != nil {
return err
}
if r.StatusCode != http.StatusNoContent {
return utils.GetErrorFromResponse(r)
}
return nil
}
......@@ -13,9 +13,16 @@
package client
import (
"bytes"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"encoding/hex"
"fmt"
"io/ioutil"
"math/rand"
"net/http"
"strconv"
"time"
jwt "github.com/dgrijalva/jwt-go"
......@@ -24,35 +31,118 @@ import (
const (
MAX_CONCURRENT_REQUESTS = 32
RETRY_COUNT = 6
// default delay values
MIN_DELAY = 10
MAX_DELAY = 30
)
type ClientTLSOptions struct {
// directly borrow the field names from crypto/tls
InsecureSkipVerify bool
// one or more cert file paths (best for self-signed certs)
VerifyCerts []string
}
// Client configuration options
type ClientOptions struct {
RetryEnabled bool
RetryCount int
// control waits between retries
RetryMinDelay, RetryMaxDelay int
}
// Client object
type Client struct {
host string
key string
user string
throttle chan bool
// configuration for TLS support
tlsClientConfig *tls.Config
// general behavioral options
opts ClientOptions
// allow plugging in custom do wrappers
do func(*http.Request) (*http.Response, error)
}
var defaultClientOptions = ClientOptions{
RetryEnabled: true,
RetryCount: RETRY_COUNT,
RetryMinDelay: MIN_DELAY,
RetryMaxDelay: MAX_DELAY,
}
// Creates a new client to access a Heketi server
// NewClient creates a new client to access a Heketi server
func NewClient(host, user, key string) *Client {
return NewClientWithOptions(host, user, key, defaultClientOptions)
}
// NewClientWithOptions creates a new client to access a Heketi server
// with a user specified suite of options.
func NewClientWithOptions(host, user, key string, opts ClientOptions) *Client {
c := &Client{}
c.key = key
c.host = host
c.user = user
c.opts = opts
// Maximum concurrent requests
c.throttle = make(chan bool, MAX_CONCURRENT_REQUESTS)
if opts.RetryEnabled {
c.do = c.retryOperationDo
} else {
c.do = c.doBasic
}
return c
}
func NewClientTLS(host, user, key string, tlsOpts *ClientTLSOptions) (*Client, error) {
c := NewClient(host, user, key)
if err := c.SetTLSOptions(tlsOpts); err != nil {
return nil, err
}
return c, nil
}
// Create a client to access a Heketi server without authentication enabled
func NewClientNoAuth(host string) *Client {
return NewClient(host, "", "")
}
// SetTLSOptions configures an existing heketi client for
// TLS support based on the ClientTLSOptions.
func (c *Client) SetTLSOptions(o *ClientTLSOptions) error {
if o == nil {
c.tlsClientConfig = nil
return nil
}
tlsConfig := &tls.Config{}
tlsConfig.InsecureSkipVerify = o.InsecureSkipVerify
if len(o.VerifyCerts) > 0 {
tlsConfig.RootCAs = x509.NewCertPool()
for _, path := range o.VerifyCerts {
pem, err := ioutil.ReadFile(path)
if err != nil {
return fmt.Errorf("failed to read cert file %v: %v",
path, err)
}
if ok := tlsConfig.RootCAs.AppendCertsFromPEM(pem); !ok {
return fmt.Errorf("failed to load PEM encoded cert from %s",
path)
}
}
}
c.tlsClientConfig = tlsConfig
return nil
}
// Simple Hello test to check if the server is up
func (c *Client) Hello() error {
// Create request
......@@ -72,6 +162,7 @@ func (c *Client) Hello() error {
if err != nil {
return err
}
defer r.Body.Close()
if r.StatusCode != http.StatusOK {
return utils.GetErrorFromResponse(r)
}
......@@ -79,14 +170,20 @@ func (c *Client) Hello() error {
return nil
}
// doBasic performs the core http transaction.
// Make sure we do not run out of fds by throttling the requests
func (c *Client) do(req *http.Request) (*http.Response, error) {
func (c *Client) doBasic(req *http.Request) (*http.Response, error) {
c.throttle <- true
defer func() {
<-c.throttle
}()
httpClient := &http.Client{}
if c.tlsClientConfig != nil {
httpClient.Transport = &http.Transport{
TLSClientConfig: c.tlsClientConfig,
}
}
httpClient.CheckRedirect = c.checkRedirect
return httpClient.Do(req)
}
......@@ -122,7 +219,7 @@ func (c *Client) waitForResponseWithTimer(r *http.Response,
}
// Wait for response
r, err = c.do(req)
r, err = c.doBasic(req)
if err != nil {
return nil, err
}
......@@ -132,6 +229,11 @@ func (c *Client) waitForResponseWithTimer(r *http.Response,
if r.StatusCode != http.StatusOK {
return nil, utils.GetErrorFromResponse(r)
}
if r != nil {
//Read Response Body
ioutil.ReadAll(r.Body)
r.Body.Close()
}
time.Sleep(waitTime)
} else {
return r, nil
......@@ -174,3 +276,66 @@ func (c *Client) setToken(r *http.Request) error {
return nil
}
// retryOperationDo performs the http request and internally
// handles http 429 codes up to the number of retries specified
// by the Client.
func (c *Client) retryOperationDo(req *http.Request) (*http.Response, error) {
var (
requestBody []byte
err error
)
if req.Body != nil {
requestBody, err = ioutil.ReadAll(req.Body)
if err != nil {
return nil, err
}
}
// Send request
var r *http.Response
for i := 0; i <= c.opts.RetryCount; i++ {
req.Body = ioutil.NopCloser(bytes.NewReader(requestBody))
r, err = c.doBasic(req)
if err != nil {
return nil, err
}
switch r.StatusCode {
case http.StatusTooManyRequests:
if r != nil {
//Read Response Body
// I don't like discarding error here, but I cant
// think of something better atm
b, _ := ioutil.ReadAll(r.Body)
r.Body.Close()
r.Body = ioutil.NopCloser(bytes.NewReader(b))
}
//sleep before continue
time.Sleep(c.opts.retryDelay(r))
continue
default:
return r, err
}
}
return r, err
}
// retryDelay returns a duration for which a retry should wait
// (after failure) before continuing.
func (c *ClientOptions) retryDelay(r *http.Response) time.Duration {
var (
min = c.RetryMinDelay
max = c.RetryMaxDelay
)
if ra := r.Header.Get("Retry-After"); ra != "" {
// TODO: support http date
if i, err := strconv.Atoi(ra); err == nil {
s := rand.Intn(min) + i
return time.Second * time.Duration(s)
}
}
s := rand.Intn(max-min) + min
return time.Second * time.Duration(s)
}
......@@ -14,16 +14,23 @@ package client
import (
"bytes"
"encoding/json"
"net/http"
"github.com/heketi/heketi/pkg/glusterfs/api"
"github.com/heketi/heketi/pkg/utils"
)
func (c *Client) ClusterCreate() (*api.ClusterInfoResponse, error) {
func (c *Client) ClusterCreate(request *api.ClusterCreateRequest) (*api.ClusterInfoResponse, error) {
buffer, err := json.Marshal(request)
if err != nil {
return nil, err
}
// Create a request
req, err := http.NewRequest("POST", c.host+"/clusters", bytes.NewBuffer([]byte(`{}`)))
req, err := http.NewRequest("POST", c.host+"/clusters",
bytes.NewBuffer(buffer))
if err != nil {
return nil, err
}
......@@ -40,6 +47,7 @@ func (c *Client) ClusterCreate() (*api.ClusterInfoResponse, error) {
if err != nil {
return nil, err
}
defer r.Body.Close()
if r.StatusCode != http.StatusCreated {
return nil, utils.GetErrorFromResponse(r)
}
......@@ -47,7 +55,6 @@ func (c *Client) ClusterCreate() (*api.ClusterInfoResponse, error) {
// Read JSON response
var cluster api.ClusterInfoResponse
err = utils.GetJsonFromResponse(r, &cluster)
r.Body.Close()
if err != nil {
return nil, err
}
......@@ -55,6 +62,40 @@ func (c *Client) ClusterCreate() (*api.ClusterInfoResponse, error) {
return &cluster, nil
}
func (c *Client) ClusterSetFlags(id string, request *api.ClusterSetFlagsRequest) error {
buffer, err := json.Marshal(request)
if err != nil {
return err
}
// Create a request
req, err := http.NewRequest("POST", c.host+"/clusters/"+id+"/flags",
bytes.NewBuffer(buffer))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
// Set token
err = c.setToken(req)
if err != nil {
return err
}
// Send request
r, err := c.do(req)
if err != nil {
return err
}
defer r.Body.Close()
if r.StatusCode != http.StatusOK {
return utils.GetErrorFromResponse(r)
}
return nil
}
func (c *Client) ClusterInfo(id string) (*api.ClusterInfoResponse, error) {
// Create request
......@@ -74,6 +115,7 @@ func (c *Client) ClusterInfo(id string) (*api.ClusterInfoResponse, error) {
if err != nil {
return nil, err
}
defer r.Body.Close()
if r.StatusCode != http.StatusOK {
return nil, utils.GetErrorFromResponse(r)
}
......@@ -81,7 +123,6 @@ func (c *Client) ClusterInfo(id string) (*api.ClusterInfoResponse, error) {
// Read JSON response
var cluster api.ClusterInfoResponse
err = utils.GetJsonFromResponse(r, &cluster)
r.Body.Close()
if err != nil {
return nil, err
}
......@@ -108,6 +149,7 @@ func (c *Client) ClusterList() (*api.ClusterListResponse, error) {
if err != nil {
return nil, err
}
defer r.Body.Close()
if r.StatusCode != http.StatusOK {
return nil, utils.GetErrorFromResponse(r)
}
......@@ -141,6 +183,7 @@ func (c *Client) ClusterDelete(id string) error {
if err != nil {
return err
}
defer r.Body.Close()
if r.StatusCode != http.StatusOK {
return utils.GetErrorFromResponse(r)
}
......
//
// Copyright (c) 2018 The heketi Authors
//
// This file is licensed to you under your choice of the GNU Lesser
// General Public License, version 3 or any later version (LGPLv3 or
// later), as published by the Free Software Foundation,
// or under the Apache License, Version 2.0 <LICENSE-APACHE2 or
// http://www.apache.org/licenses/LICENSE-2.0>.
//
// You may not use this file except in compliance with those terms.
//
package client
import (
"io/ioutil"
"net/http"
"github.com/heketi/heketi/pkg/utils"
)
// DbDump provides a JSON representation of current state of DB
func (c *Client) DbDump() (string, error) {
req, err := http.NewRequest("GET", c.host+"/db/dump", nil)
if err != nil {
return "", err
}
// Set token
err = c.setToken(req)
if err != nil {
return "", err
}
// Send request
r, err := c.do(req)
if err != nil {
return "", err
}
defer r.Body.Close()
if r.StatusCode != http.StatusOK {
return "", utils.GetErrorFromResponse(r)
}
respBytes, err := ioutil.ReadAll(r.Body)
if err != nil {
return "", err
}
respJSON := string(respBytes)
return respJSON, nil
}
......@@ -15,6 +15,7 @@ package client
import (
"bytes"
"encoding/json"
"io"
"net/http"
"time"
......@@ -47,6 +48,7 @@ func (c *Client) DeviceAdd(request *api.DeviceAddRequest) error {
if err != nil {
return err
}
defer r.Body.Close()
if r.StatusCode != http.StatusAccepted {
return utils.GetErrorFromResponse(r)
}
......@@ -82,6 +84,7 @@ func (c *Client) DeviceInfo(id string) (*api.DeviceInfoResponse, error) {
if err != nil {
return nil, err
}
defer r.Body.Close()
if r.StatusCode != http.StatusOK {
return nil, utils.GetErrorFromResponse(r)
}
......@@ -89,7 +92,6 @@ func (c *Client) DeviceInfo(id string) (*api.DeviceInfoResponse, error) {
// Read JSON response
var device api.DeviceInfoResponse
err = utils.GetJsonFromResponse(r, &device)
r.Body.Close()
if err != nil {
return nil, err
}
......@@ -98,9 +100,23 @@ func (c *Client) DeviceInfo(id string) (*api.DeviceInfoResponse, error) {
}
func (c *Client) DeviceDelete(id string) error {
return c.DeviceDeleteWithOptions(id, nil)
}
func (c *Client) DeviceDeleteWithOptions(
id string, request *api.DeviceDeleteOptions) error {
var buf io.Reader
if request != nil {
b, err := json.Marshal(request)
if err != nil {
return err
}
buf = bytes.NewBuffer(b)
}
// Create a request
req, err := http.NewRequest("DELETE", c.host+"/devices/"+id, nil)
req, err := http.NewRequest("DELETE", c.host+"/devices/"+id, buf)
if err != nil {
return err
}
......@@ -116,6 +132,7 @@ func (c *Client) DeviceDelete(id string) error {
if err != nil {
return err
}
defer r.Body.Close()
if r.StatusCode != http.StatusAccepted {
return utils.GetErrorFromResponse(r)
}
......@@ -161,6 +178,7 @@ func (c *Client) DeviceState(id string,
if err != nil {
return err
}
defer r.Body.Close()
if r.StatusCode != http.StatusAccepted {
return utils.GetErrorFromResponse(r)
}
......@@ -176,3 +194,71 @@ func (c *Client) DeviceState(id string,
return nil
}
func (c *Client) DeviceResync(id string) error {
// Create a request
req, err := http.NewRequest("GET", c.host+"/devices/"+id+"/resync", nil)
if err != nil {
return err
}
// Set token
err = c.setToken(req)
if err != nil {
return err
}
// Send request
r, err := c.do(req)
if err != nil {
return err
}
defer r.Body.Close()
if r.StatusCode != http.StatusAccepted {
return utils.GetErrorFromResponse(r)
}
// Wait for response
r, err = c.waitForResponseWithTimer(r, time.Millisecond*250)
if err != nil {
return err
}
if r.StatusCode != http.StatusNoContent {
return utils.GetErrorFromResponse(r)
}
return nil
}
func (c *Client) DeviceSetTags(id string, request *api.TagsChangeRequest) error {
buffer, err := json.Marshal(request)
if err != nil {
return err
}
req, err := http.NewRequest("POST",
c.host+"/devices/"+id+"/tags",
bytes.NewBuffer(buffer))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
// Set token
err = c.setToken(req)
if err != nil {
return err
}
// Get info
r, err := c.do(req)
if err != nil {
return err
}
defer r.Body.Close()
if r.StatusCode != http.StatusOK {
return utils.GetErrorFromResponse(r)
}
return nil
}
//
// Copyright (c) 2018 The heketi Authors
//
// This file is licensed to you under your choice of the GNU Lesser
// General Public License, version 3 or any later version (LGPLv3 or
// later), as published by the Free Software Foundation,
// or under the Apache License, Version 2.0 <LICENSE-APACHE2 or
// http://www.apache.org/licenses/LICENSE-2.0>.
//
// You may not use this file except in compliance with those terms.
//
package client
import (
"bytes"
"encoding/json"
"net/http"
"github.com/heketi/heketi/pkg/glusterfs/api"
"github.com/heketi/heketi/pkg/utils"
)
func (c *Client) LogLevelGet() (*api.LogLevelInfo, error) {
req, err := http.NewRequest("GET", c.host+"/internal/logging", nil)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
// Set token
err = c.setToken(req)
if err != nil {
return nil, err
}
// Send request
r, err := c.do(req)
if err != nil {
return nil, err
}
var lli api.LogLevelInfo
err = utils.GetJsonFromResponse(r, &lli)
if err != nil {
return nil, err
}
return &lli, nil
}
func (c *Client) LogLevelSet(request *api.LogLevelInfo) error {
// Marshal request to JSON
buffer, err := json.Marshal(request)
if err != nil {
return err
}
req, err := http.NewRequest("POST", c.host+"/internal/logging", bytes.NewBuffer(buffer))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
// Set token
err = c.setToken(req)
if err != nil {
return err
}
// Send request
r, err := c.do(req)
if err != nil {
return err
}
if r.StatusCode != http.StatusOK {
return utils.GetErrorFromResponse(r)
}
return nil
}
......@@ -48,6 +48,7 @@ func (c *Client) NodeAdd(request *api.NodeAddRequest) (*api.NodeInfoResponse, er
if err != nil {
return nil, err
}
defer r.Body.Close()
if r.StatusCode != http.StatusAccepted {
return nil, utils.GetErrorFromResponse(r)
}
......@@ -64,7 +65,6 @@ func (c *Client) NodeAdd(request *api.NodeAddRequest) (*api.NodeInfoResponse, er
// Read JSON response
var node api.NodeInfoResponse
err = utils.GetJsonFromResponse(r, &node)
r.Body.Close()
if err != nil {
return nil, err
}
......@@ -91,6 +91,7 @@ func (c *Client) NodeInfo(id string) (*api.NodeInfoResponse, error) {
if err != nil {
return nil, err
}
defer r.Body.Close()
if r.StatusCode != http.StatusOK {
return nil, utils.GetErrorFromResponse(r)
}
......@@ -98,7 +99,6 @@ func (c *Client) NodeInfo(id string) (*api.NodeInfoResponse, error) {
// Read JSON response
var node api.NodeInfoResponse
err = utils.GetJsonFromResponse(r, &node)
r.Body.Close()
if err != nil {
return nil, err
}
......@@ -125,6 +125,7 @@ func (c *Client) NodeDelete(id string) error {
if err != nil {
return err
}
defer r.Body.Close()
if r.StatusCode != http.StatusAccepted {
return utils.GetErrorFromResponse(r)
}
......@@ -168,6 +169,7 @@ func (c *Client) NodeState(id string, request *api.StateRequest) error {
if err != nil {
return err
}
defer r.Body.Close()
if r.StatusCode != http.StatusAccepted {
return utils.GetErrorFromResponse(r)
}
......@@ -183,3 +185,35 @@ func (c *Client) NodeState(id string, request *api.StateRequest) error {
return nil
}
func (c *Client) NodeSetTags(id string, request *api.TagsChangeRequest) error {
buffer, err := json.Marshal(request)
if err != nil {
return err
}
req, err := http.NewRequest("POST",
c.host+"/nodes/"+id+"/tags",
bytes.NewBuffer(buffer))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
// Set token
err = c.setToken(req)
if err != nil {
return err
}
// Get info
r, err := c.do(req)
if err != nil {
return err
}
defer r.Body.Close()
if r.StatusCode != http.StatusOK {
return utils.GetErrorFromResponse(r)
}
return nil
}
//
// Copyright (c) 2018 The heketi Authors
//
// This file is licensed to you under your choice of the GNU Lesser
// General Public License, version 3 or any later version (LGPLv3 or
// later), as published by the Free Software Foundation,
// or under the Apache License, Version 2.0 <LICENSE-APACHE2 or
// http://www.apache.org/licenses/LICENSE-2.0>.
//
// You may not use this file except in compliance with those terms.
//
package client
import (
"net/http"
"github.com/heketi/heketi/pkg/glusterfs/api"
"github.com/heketi/heketi/pkg/utils"
)
func (c *Client) OperationsInfo() (*api.OperationsInfo, error) {
req, err := http.NewRequest("GET", c.host+"/operations", nil)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
// Set token
err = c.setToken(req)
if err != nil {
return nil, err
}
// Send request
r, err := c.do(req)
if err != nil {
return nil, err
}
var oi api.OperationsInfo
err = utils.GetJsonFromResponse(r, &oi)
if err != nil {
return nil, err
}
return &oi, nil
}
......@@ -33,6 +33,10 @@ func (c *Client) TopologyInfo() (*api.TopologyInfoResponse, error) {
Id: clusteri.Id,
Volumes: make([]api.VolumeInfoResponse, 0),
Nodes: make([]api.NodeInfoResponse, 0),
ClusterFlags: api.ClusterFlags{
Block: clusteri.Block,
File: clusteri.File,
},
}
cluster.Id = clusteri.Id
......
......@@ -51,6 +51,61 @@ func (c *Client) VolumeCreate(request *api.VolumeCreateRequest) (
if err != nil {
return nil, err
}
defer r.Body.Close()
if r.StatusCode != http.StatusAccepted {
return nil, utils.GetErrorFromResponse(r)
}
// Wait for response
r, err = c.waitForResponseWithTimer(r, time.Second)
if err != nil {
return nil, err
}
if r.StatusCode != http.StatusOK {
return nil, utils.GetErrorFromResponse(r)
}
// Read JSON response
var volume api.VolumeInfoResponse
err = utils.GetJsonFromResponse(r, &volume)
if err != nil {
return nil, err
}
return &volume, nil
}
func (c *Client) VolumeSetBlockRestriction(id string, request *api.VolumeBlockRestrictionRequest) (
*api.VolumeInfoResponse, error) {
// Marshal request to JSON
buffer, err := json.Marshal(request)
if err != nil {
return nil, err
}
// Create a request
req, err := http.NewRequest("POST",
c.host+"/volumes/"+id+"/block-restriction",
bytes.NewBuffer(buffer))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
// Set token
err = c.setToken(req)
if err != nil {
return nil, err
}
// Send request
r, err := c.do(req)
if err != nil {
return nil, err
}
defer r.Body.Close()
if r.StatusCode != http.StatusAccepted {
return nil, utils.GetErrorFromResponse(r)
}
......@@ -67,7 +122,6 @@ func (c *Client) VolumeCreate(request *api.VolumeCreateRequest) (
// Read JSON response
var volume api.VolumeInfoResponse
err = utils.GetJsonFromResponse(r, &volume)
r.Body.Close()
if err != nil {
return nil, err
}
......@@ -105,6 +159,7 @@ func (c *Client) VolumeExpand(id string, request *api.VolumeExpandRequest) (
if err != nil {
return nil, err
}
defer r.Body.Close()
if r.StatusCode != http.StatusAccepted {
return nil, utils.GetErrorFromResponse(r)
}
......@@ -121,7 +176,6 @@ func (c *Client) VolumeExpand(id string, request *api.VolumeExpandRequest) (
// Read JSON response
var volume api.VolumeInfoResponse
err = utils.GetJsonFromResponse(r, &volume)
r.Body.Close()
if err != nil {
return nil, err
}
......@@ -149,6 +203,7 @@ func (c *Client) VolumeList() (*api.VolumeListResponse, error) {
if err != nil {
return nil, err
}
defer r.Body.Close()
if r.StatusCode != http.StatusOK {
return nil, utils.GetErrorFromResponse(r)
}
......@@ -182,6 +237,7 @@ func (c *Client) VolumeInfo(id string) (*api.VolumeInfoResponse, error) {
if err != nil {
return nil, err
}
defer r.Body.Close()
if r.StatusCode != http.StatusOK {
return nil, utils.GetErrorFromResponse(r)
}
......@@ -189,7 +245,6 @@ func (c *Client) VolumeInfo(id string) (*api.VolumeInfoResponse, error) {
// Read JSON response
var volume api.VolumeInfoResponse
err = utils.GetJsonFromResponse(r, &volume)
r.Body.Close()
if err != nil {
return nil, err
}
......@@ -216,6 +271,7 @@ func (c *Client) VolumeDelete(id string) error {
if err != nil {
return err
}
defer r.Body.Close()
if r.StatusCode != http.StatusAccepted {
return utils.GetErrorFromResponse(r)
}
......@@ -231,3 +287,52 @@ func (c *Client) VolumeDelete(id string) error {
return nil
}
func (c *Client) VolumeClone(id string, request *api.VolumeCloneRequest) (*api.VolumeInfoResponse, error) {
// Marshal request to JSON
buffer, err := json.Marshal(request)
if err != nil {
return nil, err
}
// Create a request
req, err := http.NewRequest("POST", c.host+"/volumes/"+id+"/clone", bytes.NewBuffer(buffer))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
// Set token
err = c.setToken(req)
if err != nil {
return nil, err
}
// Send request
r, err := c.do(req)
if err != nil {
return nil, err
}
defer r.Body.Close()
if r.StatusCode != http.StatusAccepted {
return nil, utils.GetErrorFromResponse(r)
}
// Wait for response
r, err = c.waitForResponseWithTimer(r, time.Second)
if err != nil {
return nil, err
}
if r.StatusCode != http.StatusOK {
return nil, utils.GetErrorFromResponse(r)
}
// Read JSON response
var volume api.VolumeInfoResponse
err = utils.GetJsonFromResponse(r, &volume)
if err != nil {
return nil, err
}
return &volume, nil
}
......@@ -6,6 +6,10 @@ go_library(
importmap = "k8s.io/kubernetes/vendor/github.com/heketi/heketi/pkg/glusterfs/api",
importpath = "github.com/heketi/heketi/pkg/glusterfs/api",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/go-ozzo/ozzo-validation:go_default_library",
"//vendor/github.com/go-ozzo/ozzo-validation/is:go_default_library",
],
)
filegroup(
......
......@@ -5,17 +5,11 @@ go_library(
srcs = [
"bodystring.go",
"jsonutils.go",
"log.go",
"sortedstrings.go",
"statusgroup.go",
"stringset.go",
"stringstack.go",
"uuid.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/heketi/heketi/pkg/utils",
importpath = "github.com/heketi/heketi/pkg/utils",
visibility = ["//visibility:public"],
deps = ["//vendor/github.com/lpabon/godbc:go_default_library"],
)
filegroup(
......
......@@ -3,14 +3,18 @@
//
// This file is licensed to you under your choice of the GNU Lesser
// General Public License, version 3 or any later version (LGPLv3 or
// later), or the GNU General Public License, version 2 (GPLv2), in all
// cases as published by the Free Software Foundation.
// later), as published by the Free Software Foundation,
// or under the Apache License, Version 2.0 <LICENSE-APACHE2 or
// http://www.apache.org/licenses/LICENSE-2.0>.
//
// You may not use this file except in compliance with those terms.
//
package utils
import (
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
......@@ -20,10 +24,10 @@ import (
// Return the body from a response as a string
func GetStringFromResponse(r *http.Response) (string, error) {
body, err := ioutil.ReadAll(io.LimitReader(r.Body, r.ContentLength))
defer r.Body.Close()
if err != nil {
return "", err
}
r.Body.Close()
return string(body), nil
}
......@@ -33,5 +37,10 @@ func GetErrorFromResponse(r *http.Response) error {
if err != nil {
return err
}
return errors.New(strings.TrimSpace(s))
s = strings.TrimSpace(s)
if len(s) == 0 {
return fmt.Errorf("server did not provide a message (status %v: %v)", r.StatusCode, http.StatusText(r.StatusCode))
}
return errors.New(s)
}
......@@ -3,8 +3,11 @@
//
// This file is licensed to you under your choice of the GNU Lesser
// General Public License, version 3 or any later version (LGPLv3 or
// later), or the GNU General Public License, version 2 (GPLv2), in all
// cases as published by the Free Software Foundation.
// later), as published by the Free Software Foundation,
// or under the Apache License, Version 2.0 <LICENSE-APACHE2 or
// http://www.apache.org/licenses/LICENSE-2.0>.
//
// You may not use this file except in compliance with those terms.
//
package utils
......
//
// Copyright (c) 2015 The heketi Authors
//
// This file is licensed to you under your choice of the GNU Lesser
// General Public License, version 3 or any later version (LGPLv3 or
// later), or the GNU General Public License, version 2 (GPLv2), in all
// cases as published by the Free Software Foundation.
//
package utils
import (
"fmt"
"io"
"log"
"os"
"runtime"
"strings"
"github.com/lpabon/godbc"
)
type LogLevel int
// Log levels
const (
LEVEL_NOLOG LogLevel = iota
LEVEL_CRITICAL
LEVEL_ERROR
LEVEL_WARNING
LEVEL_INFO
LEVEL_DEBUG
)
var (
stderr io.Writer = os.Stderr
stdout io.Writer = os.Stdout
)
type Logger struct {
critlog, errorlog, infolog *log.Logger
debuglog, warninglog *log.Logger
level LogLevel
}
func logWithLongFile(l *log.Logger, format string, v ...interface{}) {
_, file, line, _ := runtime.Caller(2)
// Shorten the path.
// From
// /builddir/build/BUILD/heketi-3f4a5b1b6edff87232e8b24533c53b4151ebd9c7/src/github.com/heketi/heketi/apps/glusterfs/volume_entry.go
// to
// src/github.com/heketi/heketi/apps/glusterfs/volume_entry.go
i := strings.Index(file, "/src/")
if i == -1 {
i = 0
}
l.Print(fmt.Sprintf("%v:%v: ", file[i:], line) +
fmt.Sprintf(format, v...))
}
// Create a new logger
func NewLogger(prefix string, level LogLevel) *Logger {
godbc.Require(level >= 0, level)
godbc.Require(level <= LEVEL_DEBUG, level)
l := &Logger{}
if level == LEVEL_NOLOG {
l.level = LEVEL_DEBUG
} else {
l.level = level
}
l.critlog = log.New(stderr, prefix+" CRITICAL ", log.LstdFlags)
l.errorlog = log.New(stderr, prefix+" ERROR ", log.LstdFlags)
l.warninglog = log.New(stdout, prefix+" WARNING ", log.LstdFlags)
l.infolog = log.New(stdout, prefix+" INFO ", log.LstdFlags)
l.debuglog = log.New(stdout, prefix+" DEBUG ", log.LstdFlags)
godbc.Ensure(l.critlog != nil)
godbc.Ensure(l.errorlog != nil)
godbc.Ensure(l.warninglog != nil)
godbc.Ensure(l.infolog != nil)
godbc.Ensure(l.debuglog != nil)
return l
}
// Return current level
func (l *Logger) Level() LogLevel {
return l.level
}
// Set level
func (l *Logger) SetLevel(level LogLevel) {
l.level = level
}
// Log critical information
func (l *Logger) Critical(format string, v ...interface{}) {
if l.level >= LEVEL_CRITICAL {
logWithLongFile(l.critlog, format, v...)
}
}
// Log error string
func (l *Logger) LogError(format string, v ...interface{}) error {
if l.level >= LEVEL_ERROR {
logWithLongFile(l.errorlog, format, v...)
}
return fmt.Errorf(format, v...)
}
// Log error variable
func (l *Logger) Err(err error) error {
if l.level >= LEVEL_ERROR {
logWithLongFile(l.errorlog, "%v", err)
}
return err
}
// Log warning information
func (l *Logger) Warning(format string, v ...interface{}) {
if l.level >= LEVEL_WARNING {
l.warninglog.Printf(format, v...)
}
}
// Log error variable as a warning
func (l *Logger) WarnErr(err error) error {
if l.level >= LEVEL_WARNING {
logWithLongFile(l.warninglog, "%v", err)
}
return err
}
// Log string
func (l *Logger) Info(format string, v ...interface{}) {
if l.level >= LEVEL_INFO {
l.infolog.Printf(format, v...)
}
}
// Log string as debug
func (l *Logger) Debug(format string, v ...interface{}) {
if l.level >= LEVEL_DEBUG {
logWithLongFile(l.debuglog, format, v...)
}
}
//
// Copyright (c) 2015 The heketi Authors
//
// This file is licensed to you under your choice of the GNU Lesser
// General Public License, version 3 or any later version (LGPLv3 or
// later), or the GNU General Public License, version 2 (GPLv2), in all
// cases as published by the Free Software Foundation.
//
package utils
import (
"sort"
)
// Check if a sorted string list has a string
func SortedStringHas(s sort.StringSlice, x string) bool {
index := s.Search(x)
if index == len(s) {
return false
}
return s[s.Search(x)] == x
}
// Delete a string from a sorted string list
func SortedStringsDelete(s sort.StringSlice, x string) sort.StringSlice {
index := s.Search(x)
if len(s) != index && s[index] == x {
s = append(s[:index], s[index+1:]...)
}
return s
}
......@@ -3,8 +3,11 @@
//
// This file is licensed to you under your choice of the GNU Lesser
// General Public License, version 3 or any later version (LGPLv3 or
// later), or the GNU General Public License, version 2 (GPLv2), in all
// cases as published by the Free Software Foundation.
// later), as published by the Free Software Foundation,
// or under the Apache License, Version 2.0 <LICENSE-APACHE2 or
// http://www.apache.org/licenses/LICENSE-2.0>.
//
// You may not use this file except in compliance with those terms.
//
package utils
......
//
// Copyright (c) 2015 The heketi Authors
//
// This file is licensed to you under your choice of the GNU Lesser
// General Public License, version 3 or any later version (LGPLv3 or
// later), or the GNU General Public License, version 2 (GPLv2), in all
// cases as published by the Free Software Foundation.
//
package utils
import (
"sort"
)
type StringSet struct {
Set sort.StringSlice
}
// Create a string set.
//
// A string set is a list where each element appears only once
func NewStringSet() *StringSet {
return &StringSet{
Set: make(sort.StringSlice, 0),
}
}
// Add a string to the string set
func (s *StringSet) Add(v string) {
if !SortedStringHas(s.Set, v) {
s.Set = append(s.Set, v)
s.Set.Sort()
}
}
// Return string list
func (s *StringSet) Strings() []string {
return s.Set
}
func (s *StringSet) Len() int {
return len(s.Set)
}
//
// Copyright (c) 2015 The heketi Authors
//
// This file is licensed to you under your choice of the GNU Lesser
// General Public License, version 3 or any later version (LGPLv3 or
// later), or the GNU General Public License, version 2 (GPLv2), in all
// cases as published by the Free Software Foundation.
//
package utils
type StringStack struct {
list []string
}
func NewStringStack() *StringStack {
a := &StringStack{}
a.list = make([]string, 0)
return a
}
func (a *StringStack) IsEmpty() bool {
return len(a.list) == 0
}
func (a *StringStack) Pop() (x string) {
x, a.list = a.list[0], a.list[1:len(a.list)]
return
}
func (a *StringStack) Push(x string) {
a.list = append(a.list, x)
}
//
// Copyright (c) 2015 The heketi Authors
//
// This file is licensed to you under your choice of the GNU Lesser
// General Public License, version 3 or any later version (LGPLv3 or
// later), or the GNU General Public License, version 2 (GPLv2), in all
// cases as published by the Free Software Foundation.
package utils
// From http://www.ashishbanerjee.com/home/go/go-generate-uuid
import (
"crypto/rand"
"encoding/hex"
"github.com/lpabon/godbc"
)
// Return a 16-byte uuid
func GenUUID() string {
uuid := make([]byte, 16)
n, err := rand.Read(uuid)
godbc.Check(n == len(uuid), n, len(uuid))
godbc.Check(err == nil, err)
return hex.EncodeToString(uuid)
}
language: go
go:
- 1.1
- 1.2
- tip
install:
- go get github.com/stretchr/testify
script:
- go test
- go test -tags 'prod'
lpabon@redhat.com
\ No newline at end of file
[![Build Status](https://travis-ci.org/lpabon/godbc.svg?branch=master)](https://travis-ci.org/lpabon/godbc)
# godbc
Design by contract for Go
# Installation
To install godbc, use `go get`:
go get github.com/lpabon/godbc
Import the `godbc` package into your code using this template:
import (
"github.com/lpabon/godbc"
)
# Documentation
Documentation is available at https://godoc.org/github.com/lpabon/godbc
//+build !prod
//
// Copyright (c) 2014 The godbc Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Design-by-Contract for Go
//
// Design by Contract is a programming methodology
// which binds the caller and the function called to a
// contract. The contract is represented using Hoare Triple:
// {P} C {Q}
// where {P} is the precondition before executing command C,
// and {Q} is the postcondition.
//
// See Also
//
// * http://en.wikipedia.org/wiki/Design_by_contract
// * http://en.wikipedia.org/wiki/Hoare_logic
// * http://dlang.org/dbc.html
//
// Usage
//
// Godbc is enabled by default, but can be disabled for production
// builds by using the tag 'prod' in builds and tests as follows:
// go build -tags 'prod'
// or
// go test -tags 'prod'
//
package godbc
import (
"errors"
"fmt"
"runtime"
)
// InvariantSimpleTester is an interface which provides a receiver to
// test the object
type InvariantSimpleTester interface {
Invariant() bool
}
// InvariantTester is an interface which provides not only an Invariant(),
// but also a receiver to print the structure
type InvariantTester interface {
InvariantSimpleTester
String() string
}
// dbc_panic prints to the screen information of the failure followed
// by a call to panic()
func dbc_panic(dbc_func_name string, b bool, message ...interface{}) {
if !b {
// Get caller information which is the caller
// of the caller of this function
pc, file, line, _ := runtime.Caller(2)
caller_func_info := runtime.FuncForPC(pc)
error_string := fmt.Sprintf("%s:\n\r\tfunc (%s) 0x%x\n\r\tFile %s:%d",
dbc_func_name,
caller_func_info.Name(),
pc,
file,
line)
if len(message) > 0 {
error_string += fmt.Sprintf("\n\r\tInfo: %+v", message)
}
err := errors.New(error_string)
// Finally panic
panic(err)
}
}
// Require checks that the preconditions are satisfied before
// executing the function
//
// Example Code
//
// func Divide(a, b int) int {
// godbc.Require(b != 0)
// return a/b
// }
//
func Require(b bool, message ...interface{}) {
dbc_panic("REQUIRE", b, message...)
}
// Ensure checks the postconditions are satisfied before returning
// to the caller.
//
// Example Code
//
// type Data struct {
// a int
// }
//
// func (*d Data) Set(a int) {
// d.a = a
// godbc.Ensure(d.a == a)
// }
//
func Ensure(b bool, message ...interface{}) {
dbc_panic("ENSURE", b, message...)
}
// Check provides a simple assert
func Check(b bool, message ...interface{}) {
dbc_panic("CHECK", b, message...)
}
// InvariantSimple calls the objects Invariant() receiver to test
// the object for correctness.
//
// The caller object must provide an object that supports the
// interface InvariantSimpleTester and does not need to provide
// a String() receiver
func InvariantSimple(obj InvariantSimpleTester, message ...interface{}) {
dbc_panic("INVARIANT", obj.Invariant(), message...)
}
// Invariant calls the objects Invariant() receiver to test
// the object for correctness.
//
// The caller object must provide an object that supports the
// interface InvariantTester
//
// To see an example, please take a look at the godbc_test.go
func Invariant(obj InvariantTester, message ...interface{}) {
m := append(message, obj)
dbc_panic("INVARIANT", obj.Invariant(), m)
}
//+build prod
//
// Copyright (c) 2014 The godbc Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package godbc
type InvariantSimpleTester interface {
Invariant() bool
}
type InvariantTester interface {
InvariantSimpleTester
String() string
}
func Require(b bool, message ...interface{}) {
}
func Ensure(b bool, message ...interface{}) {
}
func Check(b bool, message ...interface{}) {
}
func InvariantSimple(obj InvariantSimpleTester, message ...interface{}) {
}
func Invariant(obj InvariantTester, message ...interface{}) {
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment