<commit_before>// Copyright 2015 - 2016 Square Inc.
//
// 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 function

import (
	"fmt"
	"reflect"
	"sync"
	"time"

	"github.com/square/metrics/api"
)

// An OptionName identifies an option passed to MakeFunction
type OptionName int

const (
	InvalidOption OptionName = iota // InvalidOption represents an invalid option
	WidenBy                         // WidenBy indicates that the given duration Argument index, or the number of Slots should be used to extend the timerange in the query
	ShiftBy                         // ShiftBy acts as WidenBy, but with opposite sign
)

// String makes the option name human-readable.
func (n OptionName) String() string {
	switch n {
	case WidenBy:
		return "WidenBy"
	case ShiftBy:
		return "ShiftBy"
	default:
		return "Invalid"
	}
}

// An Option is an annotation used to add extra functionality to the generated MetricFunction.
type Option struct {
	Name  OptionName  // Name identifies the option type
	Value interface{} // Value is the value associated with the option
}

// Argument represents an argument index
type Argument int

// Slot represents a number of slots
type Slot int

// MakeFunction is a convenient way to use type-safe functions to
// construct MetricFunctions without manually checking parameters.
func MakeFunction(name string, function interface{}, options ...Option) MetricFunction {
	funcValue := reflect.ValueOf(function)
	if funcValue.Kind() != reflect.Func {
		panic(fmt.Sprintf("MakeFunction for function `%s` expects a function as second parameter.", name))
	}
	funcType := funcValue.Type()
	if funcType.IsVariadic() {
		panic(fmt.Sprintf("MakeFunction for function `%s`'s arguments cannot be variadic.", name))
	}
	if funcType.NumOut() == 0 {
		panic(fmt.Sprintf("MakeFunction for function `%s`'s argument function must return a value.", name))
	}
	if funcType.NumOut() > 2 {
		panic(fmt.Sprintf("MakeFunction for function `%s`'s argument function must return at most two values.", name))
	}
	if !funcType.Out(0).ConvertibleTo(valueType) && funcType.Out(0) != timeseriesType {
		panic(fmt.Sprintf("MakeFunction for function `%s`'s first return type must be convertible to `function.Value`.", name))
	}
	if funcType.NumOut() == 2 && !funcType.Out(1).ConvertibleTo(errorType) {
		panic(fmt.Sprintf("MakeFunction for function `%s`'s second return type must convertible be `error`.", name))
	}

	requiredArgumentCount := 0
	optionalArgumentCount := 0
	allowsGroupBy := false
	for i := 0; i < funcType.NumIn(); i++ {
		argType := funcType.In(i)
		switch argType {
		case contextType, timerangeType:
			// Asks for part of context.
		case groupsType:
			// asks for groups
			allowsGroupBy = true
		case stringType, scalarType, scalarSetType, durationType, timeseriesType, valueType, expressionType:
			// An ordinary argument.
			if optionalArgumentCount > 0 {
				panic(fmt.Sprintf("MakeFunction for function `%s` has non-optional arguments after optional ones.", name))
			}
			requiredArgumentCount++
		case reflect.PtrTo(stringType), reflect.PtrTo(scalarType), reflect.PtrTo(scalarSetType), reflect.PtrTo(durationType), reflect.PtrTo(timeseriesType), reflect.PtrTo(valueType), reflect.PtrTo(expressionType):
			// An optional argument
			optionalArgumentCount++
		default:
			panic(fmt.Sprintf("MakeFunction for function `%s` function argument asks for unsupported type: cannot supply argument %d of type %+v.", name, i, argType))
		}
	}
	// The function has been checked and inspected.
	// Now, generate the corresponding MetricFunction.

	resultFunction := MetricFunction{
		FunctionName:  name,
		MinArguments:  requiredArgumentCount,
		MaxArguments:  requiredArgumentCount + optionalArgumentCount,
		AllowsGroupBy: allowsGroupBy,
		// Compute does a lot of reflection to get this to work.
		Compute: func(context EvaluationContext, arguments []Expression, groups Groups) (Value, error) {

			// nextArgument will extract the next argument from the expression list `arguments`.
			// if there are not more to return, it will return nil.
			expressionArgument := 0
			nextArgument := func() Expression {
				if expressionArgument >= len(arguments) {
					return nil
				}
				arg := arguments[expressionArgument]
				expressionArgument++
				return arg
			}

			// evalTo takes an expression and a reflect.Type and evaluates to the appropriate type.
			// If an Expression is requested, it just returns it.
			evalTo := func(expression Expression, resultType reflect.Type) (interface{}, error) {
				switch resultType {
				case expressionType:
					return expression, nil
				case stringType:
					return EvaluateToString(expression, context)
				case scalarType:
					return EvaluateToScalar(expression, context)
				case scalarSetType:
					return EvaluateToScalarSet(expression, context)
				case durationType:
					return EvaluateToDuration(expression, context)
				case timeseriesType:
					return EvaluateToSeriesList(expression, context)
				case valueType:
					return expression.Evaluate(context)
				}
				panic(fmt.Sprintf("Unreachable :: Attempting to evaluate to unknown type %+v", resultType))
			}

			// argumentFuncs holds functions to obtain the Value arguments.
			argumentFuncs := make([]func() (interface{}, error), funcType.NumIn())

			// provideValue takes any value, and returns a function that returns it.
			provideValue := func(x interface{}) func() (interface{}, error) {
				return func() (interface{}, error) {
					return x, nil
				}
			}

			// provideZeroValue takes a type, and returns a function that returns the zero-value for that type.
			provideZeroValue := func(t reflect.Type) func() (interface{}, error) {
				return provideValue(reflect.Zero(t).Interface())
			}

			// ptrTo takes a value and returns a pointer to that value.
			ptrTo := func(x interface{}) interface{} {
				ptr := reflect.New(reflect.TypeOf(x))
				ptr.Elem().Set(reflect.ValueOf(x))
				return ptr.Interface()
			}

			for i := range argumentFuncs {
				argType := funcType.In(i)
				switch argType {
				case contextType:
					argumentFuncs[i] = provideValue(context)
				case timerangeType:
					argumentFuncs[i] = provideValue(context.Timerange())
				case groupsType:
					argumentFuncs[i] = provideValue(groups)
				case stringType, scalarType, scalarSetType, durationType, timeseriesType, valueType, expressionType:
					arg := nextArgument()
					argumentFuncs[i] = func() (interface{}, error) {
						return evalTo(arg, argType)
					}
				case reflect.PtrTo(stringType), reflect.PtrTo(scalarType), reflect.PtrTo(scalarSetType), reflect.PtrTo(durationType), reflect.PtrTo(timeseriesType), reflect.PtrTo(valueType), reflect.PtrTo(expressionType):
					arg := nextArgument()
					if arg == nil {
						argumentFuncs[i] = provideZeroValue(argType)
					} else {
						argumentFuncs[i] = func() (interface{}, error) {
							resultI, err := evalTo(arg, argType.Elem())
							if err != nil {
								return nil, err
							}
							return ptrTo(resultI), nil
						}
					}
				default:
					panic(fmt.Sprintf("Unreachable :: Argument to MakeFunction requests invalid type %+v.", argType))
				}
			}

			// Now we evaluate the functions in parallel.

			waiter := sync.WaitGroup{}
			argValues := make([]reflect.Value, funcType.NumIn())
			errors := make(chan error, funcType.NumIn())
			for i := range argValues {
				i := i
				waiter.Add(1)
				go func() {
					defer waiter.Done()
					arg, err := argumentFuncs[i]()
					if err != nil {
						errors <- err
						return
					}
					argValues[i] = reflect.ValueOf(arg)
				}()
			}
			waiter.Wait() // Wait for all the arguments to be evaluated.

			if len(errors) != 0 {
				return nil, <-errors
			}

			output := funcValue.Call(argValues)

			if len(output) == 2 && output[1].Interface() != nil {
				return nil, output[1].Interface().(error)
			}
			switch funcType.Out(0) {
			case stringType:
				return StringValue(output[0].Interface().(string)), nil
			case scalarType:
				return ScalarValue(output[0].Interface().(float64)), nil
			case scalarSetType:
				return output[0].Interface().(ScalarSet), nil
			case durationType:
				return DurationValue{"", output[0].Interface().(time.Duration)}, nil
			case timeseriesType:
				return SeriesListValue(output[0].Interface().(api.SeriesList)), nil
			default:
				return output[0].Interface().(Value), nil
			}
		},
	}

	setFlags := map[string]bool{}

	for _, option := range options {
		switch option.Name {
		case WidenBy, ShiftBy:
			if setFlags["Widen"] {
				panic(fmt.Sprintf("MakeFunction for function `%s` given option %s with parameter %v; but already had a WidenBy/ShiftBy Option", name, option.Name, option.Value))
			}
			setFlags["Widen"] = true
			sign := -1
			if option.Name == ShiftBy {
				sign = 1
			}
			switch value := option.Value.(type) {
			case Argument:
				resultFunction.Widen = func(widen WidestMode, arguments []Expression) time.Time {
					result := widen.Current
					if int(value) >= len(arguments) {
						return result
					}
					literalInterface, ok := arguments[int(value)].(LiteralExpression)
					if !ok {
						return result
					}
					literalValue := literalInterface.Literal()
					if literalValue == nil {
						return result
					}
					duration, ok := literalValue.(time.Duration)
					if !ok {
						return result
					}
					widen.AddTime(widen.Current.Add(time.Duration(sign) * duration))
					if option.Name == ShiftBy {
						return result.Add(duration)
					}
					return result
				}
			case Slot:
				resultFunction.Widen = func(widen WidestMode, arguments []Expression) time.Time {
					widen.AddTime(widen.Current.Add(-widen.Resolution))
					return widen.Current
				}
			default:
				panic(fmt.Sprintf("MakeFunction for function `%s` given option %s with value %v of unsupported type %T; must be either function.Argument or function.Slot", name, option.Name, option.Value, option.Value))
			}
		default:
			panic(fmt.Sprintf("MakeFunction for function `%s` given unrecognized option %s (with argument %v)", name, option.Name, option.Value))
		}
	}

	return resultFunction
}

var stringType = reflect.TypeOf("")
var scalarType = reflect.TypeOf(float64(0.0))
var scalarSetType = reflect.TypeOf(ScalarSet{})
var durationType = reflect.TypeOf(time.Duration(0))
var timeseriesType = reflect.TypeOf(api.SeriesList{})
var valueType = reflect.TypeOf((*Value)(nil)).Elem()
var expressionType = reflect.TypeOf((*Expression)(nil)).Elem()
var groupsType = reflect.TypeOf(Groups{})
var contextType = reflect.TypeOf(EvaluationContext{})
var timerangeType = reflect.TypeOf(api.Timerange{})

var errorType = reflect.TypeOf((*error)(nil)).Elem()
<commit_msg>clarify documentation comments<commit_after>// Copyright 2015 - 2016 Square Inc.
//
// 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 function

import (
	"fmt"
	"reflect"
	"sync"
	"time"

	"github.com/square/metrics/api"
)

// An OptionName identifies an option passed to MakeFunction
type OptionName int

const (
	InvalidOption OptionName = iota // InvalidOption represents an invalid option
	WidenBy                         // WidenBy indicates that the given duration Argument index, or the number of Slots should be used to extend the timerange in the query into the past by the given amount.
	ShiftBy                         // ShiftBy indicates that the given duration Argument index, or the number of Slots should be used to shift the timerange in the query (positive is forward in time into the future, negative is backward in time to the past)
)

// String makes the option name human-readable.
func (n OptionName) String() string {
	switch n {
	case WidenBy:
		return "WidenBy"
	case ShiftBy:
		return "ShiftBy"
	default:
		return "Invalid"
	}
}

// An Option is an annotation used to add extra functionality to the generated MetricFunction.
type Option struct {
	Name  OptionName  // Name identifies the option type
	Value interface{} // Value is the value associated with the option
}

// Argument represents an argument index
type Argument int

// Slot represents a number of slots
type Slot int

// MakeFunction is a convenient way to use type-safe functions to
// construct MetricFunctions without manually checking parameters.
func MakeFunction(name string, function interface{}, options ...Option) MetricFunction {
	funcValue := reflect.ValueOf(function)
	if funcValue.Kind() != reflect.Func {
		panic(fmt.Sprintf("MakeFunction for function `%s` expects a function as second parameter.", name))
	}
	funcType := funcValue.Type()
	if funcType.IsVariadic() {
		panic(fmt.Sprintf("MakeFunction for function `%s`'s arguments cannot be variadic.", name))
	}
	if funcType.NumOut() == 0 {
		panic(fmt.Sprintf("MakeFunction for function `%s`'s argument function must return a value.", name))
	}
	if funcType.NumOut() > 2 {
		panic(fmt.Sprintf("MakeFunction for function `%s`'s argument function must return at most two values.", name))
	}
	if !funcType.Out(0).ConvertibleTo(valueType) && funcType.Out(0) != timeseriesType {
		panic(fmt.Sprintf("MakeFunction for function `%s`'s first return type must be convertible to `function.Value`.", name))
	}
	if funcType.NumOut() == 2 && !funcType.Out(1).ConvertibleTo(errorType) {
		panic(fmt.Sprintf("MakeFunction for function `%s`'s second return type must convertible be `error`.", name))
	}

	requiredArgumentCount := 0
	optionalArgumentCount := 0
	allowsGroupBy := false
	for i := 0; i < funcType.NumIn(); i++ {
		argType := funcType.In(i)
		switch argType {
		case contextType, timerangeType:
			// Asks for part of context.
		case groupsType:
			// asks for groups
			allowsGroupBy = true
		case stringType, scalarType, scalarSetType, durationType, timeseriesType, valueType, expressionType:
			// An ordinary argument.
			if optionalArgumentCount > 0 {
				panic(fmt.Sprintf("MakeFunction for function `%s` has non-optional arguments after optional ones.", name))
			}
			requiredArgumentCount++
		case reflect.PtrTo(stringType), reflect.PtrTo(scalarType), reflect.PtrTo(scalarSetType), reflect.PtrTo(durationType), reflect.PtrTo(timeseriesType), reflect.PtrTo(valueType), reflect.PtrTo(expressionType):
			// An optional argument
			optionalArgumentCount++
		default:
			panic(fmt.Sprintf("MakeFunction for function `%s` function argument asks for unsupported type: cannot supply argument %d of type %+v.", name, i, argType))
		}
	}
	// The function has been checked and inspected.
	// Now, generate the corresponding MetricFunction.

	resultFunction := MetricFunction{
		FunctionName:  name,
		MinArguments:  requiredArgumentCount,
		MaxArguments:  requiredArgumentCount + optionalArgumentCount,
		AllowsGroupBy: allowsGroupBy,
		// Compute does a lot of reflection to get this to work.
		Compute: func(context EvaluationContext, arguments []Expression, groups Groups) (Value, error) {

			// nextArgument will extract the next argument from the expression list `arguments`.
			// if there are not more to return, it will return nil.
			expressionArgument := 0
			nextArgument := func() Expression {
				if expressionArgument >= len(arguments) {
					return nil
				}
				arg := arguments[expressionArgument]
				expressionArgument++
				return arg
			}

			// evalTo takes an expression and a reflect.Type and evaluates to the appropriate type.
			// If an Expression is requested, it just returns it.
			evalTo := func(expression Expression, resultType reflect.Type) (interface{}, error) {
				switch resultType {
				case expressionType:
					return expression, nil
				case stringType:
					return EvaluateToString(expression, context)
				case scalarType:
					return EvaluateToScalar(expression, context)
				case scalarSetType:
					return EvaluateToScalarSet(expression, context)
				case durationType:
					return EvaluateToDuration(expression, context)
				case timeseriesType:
					return EvaluateToSeriesList(expression, context)
				case valueType:
					return expression.Evaluate(context)
				}
				panic(fmt.Sprintf("Unreachable :: Attempting to evaluate to unknown type %+v", resultType))
			}

			// argumentFuncs holds functions to obtain the Value arguments.
			argumentFuncs := make([]func() (interface{}, error), funcType.NumIn())

			// provideValue takes any value, and returns a function that returns it.
			provideValue := func(x interface{}) func() (interface{}, error) {
				return func() (interface{}, error) {
					return x, nil
				}
			}

			// provideZeroValue takes a type, and returns a function that returns the zero-value for that type.
			provideZeroValue := func(t reflect.Type) func() (interface{}, error) {
				return provideValue(reflect.Zero(t).Interface())
			}

			// ptrTo takes a value and returns a pointer to that value.
			ptrTo := func(x interface{}) interface{} {
				ptr := reflect.New(reflect.TypeOf(x))
				ptr.Elem().Set(reflect.ValueOf(x))
				return ptr.Interface()
			}

			for i := range argumentFuncs {
				argType := funcType.In(i)
				switch argType {
				case contextType:
					argumentFuncs[i] = provideValue(context)
				case timerangeType:
					argumentFuncs[i] = provideValue(context.Timerange())
				case groupsType:
					argumentFuncs[i] = provideValue(groups)
				case stringType, scalarType, scalarSetType, durationType, timeseriesType, valueType, expressionType:
					arg := nextArgument()
					argumentFuncs[i] = func() (interface{}, error) {
						return evalTo(arg, argType)
					}
				case reflect.PtrTo(stringType), reflect.PtrTo(scalarType), reflect.PtrTo(scalarSetType), reflect.PtrTo(durationType), reflect.PtrTo(timeseriesType), reflect.PtrTo(valueType), reflect.PtrTo(expressionType):
					arg := nextArgument()
					if arg == nil {
						argumentFuncs[i] = provideZeroValue(argType)
					} else {
						argumentFuncs[i] = func() (interface{}, error) {
							resultI, err := evalTo(arg, argType.Elem())
							if err != nil {
								return nil, err
							}
							return ptrTo(resultI), nil
						}
					}
				default:
					panic(fmt.Sprintf("Unreachable :: Argument to MakeFunction requests invalid type %+v.", argType))
				}
			}

			// Now we evaluate the functions in parallel.

			waiter := sync.WaitGroup{}
			argValues := make([]reflect.Value, funcType.NumIn())
			errors := make(chan error, funcType.NumIn())
			for i := range argValues {
				i := i
				waiter.Add(1)
				go func() {
					defer waiter.Done()
					arg, err := argumentFuncs[i]()
					if err != nil {
						errors <- err
						return
					}
					argValues[i] = reflect.ValueOf(arg)
				}()
			}
			waiter.Wait() // Wait for all the arguments to be evaluated.

			if len(errors) != 0 {
				return nil, <-errors
			}

			output := funcValue.Call(argValues)

			if len(output) == 2 && output[1].Interface() != nil {
				return nil, output[1].Interface().(error)
			}
			switch funcType.Out(0) {
			case stringType:
				return StringValue(output[0].Interface().(string)), nil
			case scalarType:
				return ScalarValue(output[0].Interface().(float64)), nil
			case scalarSetType:
				return output[0].Interface().(ScalarSet), nil
			case durationType:
				return DurationValue{"", output[0].Interface().(time.Duration)}, nil
			case timeseriesType:
				return SeriesListValue(output[0].Interface().(api.SeriesList)), nil
			default:
				return output[0].Interface().(Value), nil
			}
		},
	}

	setFlags := map[string]bool{}

	for _, option := range options {
		switch option.Name {
		case WidenBy, ShiftBy:
			if setFlags["Widen"] {
				panic(fmt.Sprintf("MakeFunction for function `%s` given option %s with parameter %v; but already had a WidenBy/ShiftBy Option", name, option.Name, option.Value))
			}
			setFlags["Widen"] = true
			sign := -1
			if option.Name == ShiftBy {
				sign = 1
			}
			switch value := option.Value.(type) {
			case Argument:
				resultFunction.Widen = func(widen WidestMode, arguments []Expression) time.Time {
					result := widen.Current
					if int(value) >= len(arguments) {
						return result
					}
					literalInterface, ok := arguments[int(value)].(LiteralExpression)
					if !ok {
						return result
					}
					literalValue := literalInterface.Literal()
					if literalValue == nil {
						return result
					}
					duration, ok := literalValue.(time.Duration)
					if !ok {
						return result
					}
					widen.AddTime(widen.Current.Add(time.Duration(sign) * duration))
					if option.Name == ShiftBy {
						return result.Add(duration)
					}
					return result
				}
			case Slot:
				resultFunction.Widen = func(widen WidestMode, arguments []Expression) time.Time {
					widen.AddTime(widen.Current.Add(-widen.Resolution))
					return widen.Current
				}
			default:
				panic(fmt.Sprintf("MakeFunction for function `%s` given option %s with value %v of unsupported type %T; must be either function.Argument or function.Slot", name, option.Name, option.Value, option.Value))
			}
		default:
			panic(fmt.Sprintf("MakeFunction for function `%s` given unrecognized option %s (with argument %v)", name, option.Name, option.Value))
		}
	}

	return resultFunction
}

var stringType = reflect.TypeOf("")
var scalarType = reflect.TypeOf(float64(0.0))
var scalarSetType = reflect.TypeOf(ScalarSet{})
var durationType = reflect.TypeOf(time.Duration(0))
var timeseriesType = reflect.TypeOf(api.SeriesList{})
var valueType = reflect.TypeOf((*Value)(nil)).Elem()
var expressionType = reflect.TypeOf((*Expression)(nil)).Elem()
var groupsType = reflect.TypeOf(Groups{})
var contextType = reflect.TypeOf(EvaluationContext{})
var timerangeType = reflect.TypeOf(api.Timerange{})

var errorType = reflect.TypeOf((*error)(nil)).Elem()
<|endoftext|>


<commit_before>package sqlite_test

import (
	. "github.com/gwenn/gosqlite"
	"math/rand"
	"regexp"
	"testing"
)

func half(ctx *Context, nArg int) {
	nt := ctx.NumericType(0)
	if nt == Integer || nt == Float {
		ctx.ResultDouble(ctx.Double(0) / 2)
	} else {
		ctx.ResultNull()
	}
}

func TestScalarFunction(t *testing.T) {
	db, err := Open("")
	checkNoError(t, err, "couldn't open database file: %s")
	defer db.Close()
	err = db.CreateScalarFunction("half", 1, nil, half, nil)
	checkNoError(t, err, "couldn't create function: %s")
	d, err := db.OneValue("select half(6)")
	checkNoError(t, err, "couldn't retrieve result: %s")
	if d != 3.0 {
		t.Errorf("Expected %f but got %f", 3.0, d)
	}
	err = db.CreateScalarFunction("half", 1, nil, nil, nil)
	checkNoError(t, err, "couldn't destroy function: %s")
}

func re(ctx *Context, nArg int) {
	ad := ctx.GetAuxData(0)
	var re *regexp.Regexp
	if ad == nil {
		//println("Compile")
		var err error
		re, err = regexp.Compile(ctx.Text(0))
		if err != nil {
			ctx.ResultError(err.Error())
			return
		}
		ctx.SetAuxData(0, re)
	} else {
		//println("Reuse")
		var ok bool
		if re, ok = ad.(*regexp.Regexp); !ok {
			println(ad)
			ctx.ResultError("AuxData not a regexp")
			return
		}
	}
	m := re.MatchString(ctx.Text(1))
	ctx.ResultBool(m)
}

// Useless (just for test)
func reDestroy(ad interface{}) {
	//println("reDestroy")
}

func TestRegexpFunction(t *testing.T) {
	db, err := Open("")
	checkNoError(t, err, "couldn't open database file: %s")
	defer db.Close()
	err = db.CreateScalarFunction("regexp", 2, nil, re, reDestroy)
	checkNoError(t, err, "couldn't create function: %s")
	s, err := db.Prepare("select regexp('l.s[aeiouy]', name) from (select 'lisa' as name union all select 'bart' as name)")
	checkNoError(t, err, "couldn't prepare statement: %s")
	defer s.Finalize()
	if b := Must(s.Next()); !b {
		t.Fatalf("No result")
	}
	i, _, err := s.ScanInt(0)
	checkNoError(t, err, "couldn't scan result: %s")
	if i != 1 {
		t.Errorf("Expected %d but got %d", 1, i)
	}
	if b := Must(s.Next()); !b {
		t.Fatalf("No result")
	}
	i, _, err = s.ScanInt(0)
	checkNoError(t, err, "couldn't scan result: %s")
	if i != 0 {
		t.Errorf("Expected %d but got %d", 0, i)
	}
}

func sumStep(ctx *Context, nArg int) {
	nt := ctx.NumericType(0)
	if nt == Integer || nt == Float {
		var sum float64
		var ok bool
		if sum, ok = (ctx.AggregateContext).(float64); !ok {
			sum = 0
		}
		sum += ctx.Double(0)
		ctx.AggregateContext = sum
	}
}

func sumFinal(ctx *Context) {
	if sum, ok := (ctx.AggregateContext).(float64); ok {
		ctx.ResultDouble(sum)
	} else {
		ctx.ResultNull()
	}
}

/*
func TestSumFunction(t *testing.T) {
	db, err := Open("")
	checkNoError(t, err, "couldn't open database file: %s")
	defer db.Close()
	err = db.CreateAggregateFunction("sum", 1, nil, sumStep, sumFinal, nil)
	checkNoError(t, err, "couldn't create function: %s")
	i, err := db.OneValue("select sum(i) from (select 2 as i union all select 2 as i)")
	checkNoError(t, err, "couldn't execute statement: %s")
	if i != 4 {
		t.Errorf("Expected %d but got %d", 4, i)
	}
}
*/

func randomFill(db *Conn, n int) {
	db.Exec("DROP TABLE IF EXISTS test")
	db.Exec("CREATE TABLE test (name TEXT, rank int)")
	s, _ := db.Prepare("INSERT INTO test (name, rank) VALUES (?, ?)")
	defer s.Finalize()

	names := []string{"Bart", "Homer", "Lisa", "Maggie", "Marge"}

	db.Begin()
	for i := 0; i < n; i++ {
		s.Exec(names[rand.Intn(len(names))], rand.Intn(100))
	}
	db.Commit()
}

func BenchmarkLike(b *testing.B) {
	b.StopTimer()
	db, _ := Open("")
	defer db.Close()
	randomFill(db, 1000)

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		cs, _ := db.Prepare("SELECT count(1) FROM test where name like 'lisa'")
		Must(cs.Next())
		cs.Finalize()
	}
}

func BenchmarkHalf(b *testing.B) {
	b.StopTimer()
	db, _ := Open("")
	defer db.Close()
	randomFill(db, 1000)
	db.CreateScalarFunction("half", 1, nil, half, nil)

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		cs, _ := db.Prepare("SELECT count(1) FROM test where half(rank) > 20")
		Must(cs.Next())
		cs.Finalize()
	}
}

func BenchmarkRegexp(b *testing.B) {
	b.StopTimer()
	db, _ := Open("")
	defer db.Close()
	randomFill(db, 1000)
	db.CreateScalarFunction("regexp", 2, nil, re, reDestroy)

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		cs, _ := db.Prepare("SELECT count(1) FROM test where name regexp  '(?i)\\blisa\\b'")
		Must(cs.Next())
		cs.Finalize()
	}
}
<commit_msg>Make sum test pass with the sqlite implementation.<commit_after>package sqlite_test

import (
	. "github.com/gwenn/gosqlite"
	"math/rand"
	"regexp"
	"testing"
)

func half(ctx *Context, nArg int) {
	nt := ctx.NumericType(0)
	if nt == Integer || nt == Float {
		ctx.ResultDouble(ctx.Double(0) / 2)
	} else {
		ctx.ResultNull()
	}
}

func TestScalarFunction(t *testing.T) {
	db, err := Open("")
	checkNoError(t, err, "couldn't open database file: %s")
	defer db.Close()
	err = db.CreateScalarFunction("half", 1, nil, half, nil)
	checkNoError(t, err, "couldn't create function: %s")
	d, err := db.OneValue("select half(6)")
	checkNoError(t, err, "couldn't retrieve result: %s")
	if d != 3.0 {
		t.Errorf("Expected %f but got %f", 3.0, d)
	}
	err = db.CreateScalarFunction("half", 1, nil, nil, nil)
	checkNoError(t, err, "couldn't destroy function: %s")
}

func re(ctx *Context, nArg int) {
	ad := ctx.GetAuxData(0)
	var re *regexp.Regexp
	if ad == nil {
		//println("Compile")
		var err error
		re, err = regexp.Compile(ctx.Text(0))
		if err != nil {
			ctx.ResultError(err.Error())
			return
		}
		ctx.SetAuxData(0, re)
	} else {
		//println("Reuse")
		var ok bool
		if re, ok = ad.(*regexp.Regexp); !ok {
			println(ad)
			ctx.ResultError("AuxData not a regexp")
			return
		}
	}
	m := re.MatchString(ctx.Text(1))
	ctx.ResultBool(m)
}

// Useless (just for test)
func reDestroy(ad interface{}) {
	//println("reDestroy")
}

func TestRegexpFunction(t *testing.T) {
	db, err := Open("")
	checkNoError(t, err, "couldn't open database file: %s")
	defer db.Close()
	err = db.CreateScalarFunction("regexp", 2, nil, re, reDestroy)
	checkNoError(t, err, "couldn't create function: %s")
	s, err := db.Prepare("select regexp('l.s[aeiouy]', name) from (select 'lisa' as name union all select 'bart')")
	checkNoError(t, err, "couldn't prepare statement: %s")
	defer s.Finalize()
	if b := Must(s.Next()); !b {
		t.Fatalf("No result")
	}
	i, _, err := s.ScanInt(0)
	checkNoError(t, err, "couldn't scan result: %s")
	if i != 1 {
		t.Errorf("Expected %d but got %d", 1, i)
	}
	if b := Must(s.Next()); !b {
		t.Fatalf("No result")
	}
	i, _, err = s.ScanInt(0)
	checkNoError(t, err, "couldn't scan result: %s")
	if i != 0 {
		t.Errorf("Expected %d but got %d", 0, i)
	}
}

func sumStep(ctx *Context, nArg int) {
	nt := ctx.NumericType(0)
	if nt == Integer || nt == Float {
		var sum float64
		var ok bool
		if sum, ok = (ctx.AggregateContext).(float64); !ok {
			sum = 0
		}
		sum += ctx.Double(0)
		ctx.AggregateContext = sum
	}
}

func sumFinal(ctx *Context) {
	if sum, ok := (ctx.AggregateContext).(float64); ok {
		ctx.ResultDouble(sum)
	} else {
		ctx.ResultNull()
	}
}

func TestSumFunction(t *testing.T) {
	db, err := Open("")
	checkNoError(t, err, "couldn't open database file: %s")
	defer db.Close()
	err = db.CreateAggregateFunction("mysum", 1, nil, sumStep, sumFinal, nil)
	checkNoError(t, err, "couldn't create function: %s")
	i, err := db.OneValue("select sum(i) from (select 2 as i union all select 2)")
	checkNoError(t, err, "couldn't execute statement: %s")
	if i != int64(4) {
		t.Errorf("Expected %d but got %d", 4, i)
	}
}

func randomFill(db *Conn, n int) {
	db.Exec("DROP TABLE IF EXISTS test")
	db.Exec("CREATE TABLE test (name TEXT, rank int)")
	s, _ := db.Prepare("INSERT INTO test (name, rank) VALUES (?, ?)")
	defer s.Finalize()

	names := []string{"Bart", "Homer", "Lisa", "Maggie", "Marge"}

	db.Begin()
	for i := 0; i < n; i++ {
		s.Exec(names[rand.Intn(len(names))], rand.Intn(100))
	}
	db.Commit()
}

func BenchmarkLike(b *testing.B) {
	b.StopTimer()
	db, _ := Open("")
	defer db.Close()
	randomFill(db, 1000)

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		cs, _ := db.Prepare("SELECT count(1) FROM test where name like 'lisa'")
		Must(cs.Next())
		cs.Finalize()
	}
}

func BenchmarkHalf(b *testing.B) {
	b.StopTimer()
	db, _ := Open("")
	defer db.Close()
	randomFill(db, 1000)
	db.CreateScalarFunction("half", 1, nil, half, nil)

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		cs, _ := db.Prepare("SELECT count(1) FROM test where half(rank) > 20")
		Must(cs.Next())
		cs.Finalize()
	}
}

func BenchmarkRegexp(b *testing.B) {
	b.StopTimer()
	db, _ := Open("")
	defer db.Close()
	randomFill(db, 1000)
	db.CreateScalarFunction("regexp", 2, nil, re, reDestroy)

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		cs, _ := db.Prepare("SELECT count(1) FROM test where name regexp  '(?i)\\blisa\\b'")
		Must(cs.Next())
		cs.Finalize()
	}
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/rds"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsRDSClusterInstance() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsRDSClusterInstanceCreate,
		Read:   resourceAwsRDSClusterInstanceRead,
		Update: resourceAwsRDSClusterInstanceUpdate,
		Delete: resourceAwsRDSClusterInstanceDelete,

		Schema: map[string]*schema.Schema{
			"identifier": &schema.Schema{
				Type:         schema.TypeString,
				Optional:     true,
				ForceNew:     true,
				ValidateFunc: validateRdsId,
			},

			"db_subnet_group_name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Computed: true,
			},

			"writer": &schema.Schema{
				Type:     schema.TypeBool,
				Computed: true,
			},

			"cluster_identifier": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"endpoint": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"port": &schema.Schema{
				Type:     schema.TypeInt,
				Computed: true,
			},

			"publicly_accessible": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},

			"instance_class": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},

			"db_parameter_group_name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"status": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			// apply_immediately is used to determine when the update modifications
			// take place.
			// See http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.DBInstance.Modifying.html
			"apply_immediately": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceAwsRDSClusterInstanceCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).rdsconn
	tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))

	createOpts := &rds.CreateDBInstanceInput{
		DBInstanceClass:     aws.String(d.Get("instance_class").(string)),
		DBClusterIdentifier: aws.String(d.Get("cluster_identifier").(string)),
		Engine:              aws.String("aurora"),
		PubliclyAccessible:  aws.Bool(d.Get("publicly_accessible").(bool)),
		Tags:                tags,
	}

	if attr, ok := d.GetOk("db_parameter_group_name"); ok {
		createOpts.DBParameterGroupName = aws.String(attr.(string))
	}

	if v := d.Get("identifier").(string); v != "" {
		createOpts.DBInstanceIdentifier = aws.String(v)
	} else {
		createOpts.DBInstanceIdentifier = aws.String(resource.UniqueId())
	}

	if attr, ok := d.GetOk("db_subnet_group_name"); ok {
		createOpts.DBSubnetGroupName = aws.String(attr.(string))
	}

	log.Printf("[DEBUG] Creating RDS DB Instance opts: %s", createOpts)
	resp, err := conn.CreateDBInstance(createOpts)
	if err != nil {
		return err
	}

	d.SetId(*resp.DBInstance.DBInstanceIdentifier)

	// reuse db_instance refresh func
	stateConf := &resource.StateChangeConf{
		Pending:    []string{"creating", "backing-up", "modifying"},
		Target:     []string{"available"},
		Refresh:    resourceAwsDbInstanceStateRefreshFunc(d, meta),
		Timeout:    40 * time.Minute,
		MinTimeout: 10 * time.Second,
		Delay:      10 * time.Second,
	}

	// Wait, catching any errors
	_, err = stateConf.WaitForState()
	if err != nil {
		return err
	}

	return resourceAwsRDSClusterInstanceRead(d, meta)
}

func resourceAwsRDSClusterInstanceRead(d *schema.ResourceData, meta interface{}) error {
	db, err := resourceAwsDbInstanceRetrieve(d, meta)
	// Errors from this helper are always reportable
	if err != nil {
		return fmt.Errorf("[WARN] Error on retrieving RDS Cluster Instance (%s): %s", d.Id(), err)
	}
	// A nil response means "not found"
	if db == nil {
		log.Printf("[WARN] RDS Cluster Instance (%s): not found, removing from state.", d.Id())
		d.SetId("")
		return nil
	}

	// Retreive DB Cluster information, to determine if this Instance is a writer
	conn := meta.(*AWSClient).rdsconn
	resp, err := conn.DescribeDBClusters(&rds.DescribeDBClustersInput{
		DBClusterIdentifier: db.DBClusterIdentifier,
	})

	var dbc *rds.DBCluster
	for _, c := range resp.DBClusters {
		if *c.DBClusterIdentifier == *db.DBClusterIdentifier {
			dbc = c
		}
	}

	if dbc == nil {
		return fmt.Errorf("[WARN] Error finding RDS Cluster (%s) for Cluster Instance (%s): %s",
			*db.DBClusterIdentifier, *db.DBInstanceIdentifier, err)
	}

	for _, m := range dbc.DBClusterMembers {
		if *db.DBInstanceIdentifier == *m.DBInstanceIdentifier {
			if *m.IsClusterWriter == true {
				d.Set("writer", true)
			} else {
				d.Set("writer", false)
			}
		}
	}

	if db.Endpoint != nil {
		d.Set("endpoint", db.Endpoint.Address)
		d.Set("port", db.Endpoint.Port)
	}

	d.Set("publicly_accessible", db.PubliclyAccessible)

	if len(db.DBParameterGroups) > 0 {
		d.Set("parameter_group_name", db.DBParameterGroups[0].DBParameterGroupName)
	}

	// Fetch and save tags
	arn, err := buildRDSARN(d.Id(), meta)
	if err != nil {
		log.Printf("[DEBUG] Error building ARN for RDS Cluster Instance (%s), not setting Tags", *db.DBInstanceIdentifier)
	} else {
		if err := saveTagsRDS(conn, d, arn); err != nil {
			log.Printf("[WARN] Failed to save tags for RDS Cluster Instance (%s): %s", *db.DBClusterIdentifier, err)
		}
	}

	return nil
}

func resourceAwsRDSClusterInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).rdsconn
	requestUpdate := false

	req := &rds.ModifyDBInstanceInput{
		ApplyImmediately:     aws.Bool(d.Get("apply_immediately").(bool)),
		DBInstanceIdentifier: aws.String(d.Id()),
	}

	if d.HasChange("db_parameter_group_name") {
		req.DBParameterGroupName = aws.String(d.Get("db_parameter_group_name").(string))
		requestUpdate = true

	}

	if d.HasChange("instance_class") {
		req.DBInstanceClass = aws.String(d.Get("instance_class").(string))
		requestUpdate = true

	}

	log.Printf("[DEBUG] Send DB Instance Modification request: %#v", requestUpdate)
	if requestUpdate {
		log.Printf("[DEBUG] DB Instance Modification request: %#v", req)
		_, err := conn.ModifyDBInstance(req)
		if err != nil {
			return fmt.Errorf("Error modifying DB Instance %s: %s", d.Id(), err)
		}

		// reuse db_instance refresh func
		stateConf := &resource.StateChangeConf{
			Pending:    []string{"creating", "backing-up", "modifying"},
			Target:     []string{"available"},
			Refresh:    resourceAwsDbInstanceStateRefreshFunc(d, meta),
			Timeout:    40 * time.Minute,
			MinTimeout: 10 * time.Second,
			Delay:      10 * time.Second,
		}

		// Wait, catching any errors
		_, err = stateConf.WaitForState()
		if err != nil {
			return err
		}

	}

	if arn, err := buildRDSARN(d.Id(), meta); err == nil {
		if err := setTagsRDS(conn, d, arn); err != nil {
			return err
		}
	}

	return resourceAwsRDSClusterInstanceRead(d, meta)
}

func resourceAwsRDSClusterInstanceDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).rdsconn

	log.Printf("[DEBUG] RDS Cluster Instance destroy: %v", d.Id())

	opts := rds.DeleteDBInstanceInput{DBInstanceIdentifier: aws.String(d.Id())}

	log.Printf("[DEBUG] RDS Cluster Instance destroy configuration: %s", opts)
	if _, err := conn.DeleteDBInstance(&opts); err != nil {
		return err
	}

	// re-uses db_instance refresh func
	log.Println("[INFO] Waiting for RDS Cluster Instance to be destroyed")
	stateConf := &resource.StateChangeConf{
		Pending:    []string{"modifying", "deleting"},
		Target:     []string{},
		Refresh:    resourceAwsDbInstanceStateRefreshFunc(d, meta),
		Timeout:    40 * time.Minute,
		MinTimeout: 10 * time.Second,
	}

	if _, err := stateConf.WaitForState(); err != nil {
		return err
	}

	return nil

}
<commit_msg>remove status attribute<commit_after>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/rds"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsRDSClusterInstance() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsRDSClusterInstanceCreate,
		Read:   resourceAwsRDSClusterInstanceRead,
		Update: resourceAwsRDSClusterInstanceUpdate,
		Delete: resourceAwsRDSClusterInstanceDelete,

		Schema: map[string]*schema.Schema{
			"identifier": &schema.Schema{
				Type:         schema.TypeString,
				Optional:     true,
				ForceNew:     true,
				ValidateFunc: validateRdsId,
			},

			"db_subnet_group_name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Computed: true,
			},

			"writer": &schema.Schema{
				Type:     schema.TypeBool,
				Computed: true,
			},

			"cluster_identifier": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"endpoint": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"port": &schema.Schema{
				Type:     schema.TypeInt,
				Computed: true,
			},

			"publicly_accessible": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},

			"instance_class": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},

			"db_parameter_group_name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			// apply_immediately is used to determine when the update modifications
			// take place.
			// See http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.DBInstance.Modifying.html
			"apply_immediately": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceAwsRDSClusterInstanceCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).rdsconn
	tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))

	createOpts := &rds.CreateDBInstanceInput{
		DBInstanceClass:     aws.String(d.Get("instance_class").(string)),
		DBClusterIdentifier: aws.String(d.Get("cluster_identifier").(string)),
		Engine:              aws.String("aurora"),
		PubliclyAccessible:  aws.Bool(d.Get("publicly_accessible").(bool)),
		Tags:                tags,
	}

	if attr, ok := d.GetOk("db_parameter_group_name"); ok {
		createOpts.DBParameterGroupName = aws.String(attr.(string))
	}

	if v := d.Get("identifier").(string); v != "" {
		createOpts.DBInstanceIdentifier = aws.String(v)
	} else {
		createOpts.DBInstanceIdentifier = aws.String(resource.UniqueId())
	}

	if attr, ok := d.GetOk("db_subnet_group_name"); ok {
		createOpts.DBSubnetGroupName = aws.String(attr.(string))
	}

	log.Printf("[DEBUG] Creating RDS DB Instance opts: %s", createOpts)
	resp, err := conn.CreateDBInstance(createOpts)
	if err != nil {
		return err
	}

	d.SetId(*resp.DBInstance.DBInstanceIdentifier)

	// reuse db_instance refresh func
	stateConf := &resource.StateChangeConf{
		Pending:    []string{"creating", "backing-up", "modifying"},
		Target:     []string{"available"},
		Refresh:    resourceAwsDbInstanceStateRefreshFunc(d, meta),
		Timeout:    40 * time.Minute,
		MinTimeout: 10 * time.Second,
		Delay:      10 * time.Second,
	}

	// Wait, catching any errors
	_, err = stateConf.WaitForState()
	if err != nil {
		return err
	}

	return resourceAwsRDSClusterInstanceRead(d, meta)
}

func resourceAwsRDSClusterInstanceRead(d *schema.ResourceData, meta interface{}) error {
	db, err := resourceAwsDbInstanceRetrieve(d, meta)
	// Errors from this helper are always reportable
	if err != nil {
		return fmt.Errorf("[WARN] Error on retrieving RDS Cluster Instance (%s): %s", d.Id(), err)
	}
	// A nil response means "not found"
	if db == nil {
		log.Printf("[WARN] RDS Cluster Instance (%s): not found, removing from state.", d.Id())
		d.SetId("")
		return nil
	}

	// Retreive DB Cluster information, to determine if this Instance is a writer
	conn := meta.(*AWSClient).rdsconn
	resp, err := conn.DescribeDBClusters(&rds.DescribeDBClustersInput{
		DBClusterIdentifier: db.DBClusterIdentifier,
	})

	var dbc *rds.DBCluster
	for _, c := range resp.DBClusters {
		if *c.DBClusterIdentifier == *db.DBClusterIdentifier {
			dbc = c
		}
	}

	if dbc == nil {
		return fmt.Errorf("[WARN] Error finding RDS Cluster (%s) for Cluster Instance (%s): %s",
			*db.DBClusterIdentifier, *db.DBInstanceIdentifier, err)
	}

	for _, m := range dbc.DBClusterMembers {
		if *db.DBInstanceIdentifier == *m.DBInstanceIdentifier {
			if *m.IsClusterWriter == true {
				d.Set("writer", true)
			} else {
				d.Set("writer", false)
			}
		}
	}

	if db.Endpoint != nil {
		d.Set("endpoint", db.Endpoint.Address)
		d.Set("port", db.Endpoint.Port)
	}

	d.Set("publicly_accessible", db.PubliclyAccessible)

	if len(db.DBParameterGroups) > 0 {
		d.Set("parameter_group_name", db.DBParameterGroups[0].DBParameterGroupName)
	}

	// Fetch and save tags
	arn, err := buildRDSARN(d.Id(), meta)
	if err != nil {
		log.Printf("[DEBUG] Error building ARN for RDS Cluster Instance (%s), not setting Tags", *db.DBInstanceIdentifier)
	} else {
		if err := saveTagsRDS(conn, d, arn); err != nil {
			log.Printf("[WARN] Failed to save tags for RDS Cluster Instance (%s): %s", *db.DBClusterIdentifier, err)
		}
	}

	return nil
}

func resourceAwsRDSClusterInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).rdsconn
	requestUpdate := false

	req := &rds.ModifyDBInstanceInput{
		ApplyImmediately:     aws.Bool(d.Get("apply_immediately").(bool)),
		DBInstanceIdentifier: aws.String(d.Id()),
	}

	if d.HasChange("db_parameter_group_name") {
		req.DBParameterGroupName = aws.String(d.Get("db_parameter_group_name").(string))
		requestUpdate = true

	}

	if d.HasChange("instance_class") {
		req.DBInstanceClass = aws.String(d.Get("instance_class").(string))
		requestUpdate = true

	}

	log.Printf("[DEBUG] Send DB Instance Modification request: %#v", requestUpdate)
	if requestUpdate {
		log.Printf("[DEBUG] DB Instance Modification request: %#v", req)
		_, err := conn.ModifyDBInstance(req)
		if err != nil {
			return fmt.Errorf("Error modifying DB Instance %s: %s", d.Id(), err)
		}

		// reuse db_instance refresh func
		stateConf := &resource.StateChangeConf{
			Pending:    []string{"creating", "backing-up", "modifying"},
			Target:     []string{"available"},
			Refresh:    resourceAwsDbInstanceStateRefreshFunc(d, meta),
			Timeout:    40 * time.Minute,
			MinTimeout: 10 * time.Second,
			Delay:      10 * time.Second,
		}

		// Wait, catching any errors
		_, err = stateConf.WaitForState()
		if err != nil {
			return err
		}

	}

	if arn, err := buildRDSARN(d.Id(), meta); err == nil {
		if err := setTagsRDS(conn, d, arn); err != nil {
			return err
		}
	}

	return resourceAwsRDSClusterInstanceRead(d, meta)
}

func resourceAwsRDSClusterInstanceDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).rdsconn

	log.Printf("[DEBUG] RDS Cluster Instance destroy: %v", d.Id())

	opts := rds.DeleteDBInstanceInput{DBInstanceIdentifier: aws.String(d.Id())}

	log.Printf("[DEBUG] RDS Cluster Instance destroy configuration: %s", opts)
	if _, err := conn.DeleteDBInstance(&opts); err != nil {
		return err
	}

	// re-uses db_instance refresh func
	log.Println("[INFO] Waiting for RDS Cluster Instance to be destroyed")
	stateConf := &resource.StateChangeConf{
		Pending:    []string{"modifying", "deleting"},
		Target:     []string{},
		Refresh:    resourceAwsDbInstanceStateRefreshFunc(d, meta),
		Timeout:    40 * time.Minute,
		MinTimeout: 10 * time.Second,
	}

	if _, err := stateConf.WaitForState(); err != nil {
		return err
	}

	return nil

}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Portions copyright 2013 James Pirruccello <james@carbocation.com>

// Package binarytree implements a binary tree, derived from the container/list code
//
// To iterate over a tree (where l is a *Tree):
//	for e := l.Front(); e != nil; e = e.Next() {
//		// do something with e.Value
//	}
//
package binarytree

// Element is an element in the tree.
type Element struct {
	// Next and previous pointers in the doubly-linked tree of elements.
	// The root of the tree has parent = nil, and the leaf of a tree has child = nil.
	child, parent *Element

	// The tree to which this element belongs.
	tree *Tree

	// The contents of this tree element.
	Value interface{}
}

// Next returns the child tree element or nil.
func (e *Element) Next() *Element { return e.child }

// Prev returns the previous tree element or nil.
func (e *Element) Prev() *Element { return e.parent }

// Tree represents a doubly linked tree.
// The zero value for Tree is an empty tree ready to use.
type Tree struct {
	front, back *Element
	len         int
}

// Init initializes or clears a Tree.
func (l *Tree) Init() *Tree {
	l.front = nil
	l.back = nil
	l.len = 0
	return l
}

// New returns an initialized tree.
func New() *Tree { return new(Tree) }

// Front returns the first element in the tree.
func (l *Tree) Front() *Element { return l.front }

// Back returns the last element in the tree.
func (l *Tree) Back() *Element { return l.back }

// Remove removes the element from the tree
// and returns its Value.
func (l *Tree) Remove(e *Element) interface{} {
	l.remove(e)
	e.tree = nil // do what remove does not
	return e.Value
}

// remove the element from the tree, but do not clear the Element's tree field.
// This is so that other Tree methods may use remove when relocating Elements
// without needing to restore the tree field.
func (l *Tree) remove(e *Element) {
	if e.tree != l {
		return
	}
	if e.parent == nil {
		l.front = e.child
	} else {
		e.parent.child = e.child
	}
	if e.child == nil {
		l.back = e.parent
	} else {
		e.child.parent = e.parent
	}

	e.parent = nil
	e.child = nil
	l.len--
}

func (l *Tree) insertBefore(e *Element, mark *Element) {
	if mark.parent == nil {
		// new front of the tree
		l.front = e
	} else {
		mark.parent.child = e
	}
	e.parent = mark.parent
	mark.parent = e
	e.child = mark
	l.len++
}

func (l *Tree) insertAfter(e *Element, mark *Element) {
	if mark.child == nil {
		// new back of the tree
		l.back = e
	} else {
		mark.child.parent = e
	}
	e.child = mark.child
	mark.child = e
	e.parent = mark
	l.len++
}

func (l *Tree) insertFront(e *Element) {
	if l.front == nil {
		// empty tree
		l.front, l.back = e, e
		e.parent, e.child = nil, nil
		l.len = 1
		return
	}
	l.insertBefore(e, l.front)
}

func (l *Tree) insertBack(e *Element) {
	if l.back == nil {
		// empty tree
		l.front, l.back = e, e
		e.parent, e.child = nil, nil
		l.len = 1
		return
	}
	l.insertAfter(e, l.back)
}

// PushFront inserts the value at the front of the tree and returns a new Element containing the value.
func (l *Tree) PushFront(value interface{}) *Element {
	e := &Element{nil, nil, l, value}
	l.insertFront(e)
	return e
}

// PushBack inserts the value at the back of the tree and returns a new Element containing the value.
func (l *Tree) PushBack(value interface{}) *Element {
	e := &Element{nil, nil, l, value}
	l.insertBack(e)
	return e
}

// InsertBefore inserts the value immediately before mark and returns a new Element containing the value.
func (l *Tree) InsertBefore(value interface{}, mark *Element) *Element {
	if mark.tree != l {
		return nil
	}
	e := &Element{nil, nil, l, value}
	l.insertBefore(e, mark)
	return e
}

// InsertAfter inserts the value immediately after mark and returns a new Element containing the value.
func (l *Tree) InsertAfter(value interface{}, mark *Element) *Element {
	if mark.tree != l {
		return nil
	}
	e := &Element{nil, nil, l, value}
	l.insertAfter(e, mark)
	return e
}

// MoveToFront moves the element to the front of the tree.
func (l *Tree) MoveToFront(e *Element) {
	if e.tree != l || l.front == e {
		return
	}
	l.remove(e)
	l.insertFront(e)
}

// MoveToBack moves the element to the back of the tree.
func (l *Tree) MoveToBack(e *Element) {
	if e.tree != l || l.back == e {
		return
	}
	l.remove(e)
	l.insertBack(e)
}

// Len returns the number of elements in the tree.
func (l *Tree) Len() int { return l.len }

// PushBackTree inserts each element of ol at the back of the tree.
func (l *Tree) PushBackTree(ol *Tree) {
	last := ol.Back()
	for e := ol.Front(); e != nil; e = e.Next() {
		l.PushBack(e.Value)
		if e == last {
			break
		}
	}
}

// PushFrontTree inserts each element of ol at the front of the tree. The ordering of the passed tree is preserved.
func (l *Tree) PushFrontTree(ol *Tree) {
	first := ol.Front()
	for e := ol.Back(); e != nil; e = e.Prev() {
		l.PushFront(e.Value)
		if e == first {
			break
		}
	}
}
<commit_msg>Renamed methods for tree logic.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Portions copyright 2013 James Pirruccello <james@carbocation.com>

// Package binarytree implements a binary tree, derived from the container/list code
//
// To iterate over a tree (where l is a *Tree):
//	for e := l.Front(); e != nil; e = e.Left() {
//		// do something with e.Value
//	}
//
package binarytree

// Element is an element in the tree.
type Element struct {
	// Parent, left, and right pointers in the tree of elements.
	// The root of the tree has parent = nil
    // Each leaf of a tree has left = nil.
	parent, left, right *Element

	// The tree to which this element belongs.
	tree *Tree

	// The contents of this tree element.
	Value interface{}
}

// Left returns the left tree element or nil.
func (e *Element) Left() *Element { return e.left }

// Right returns the right tree element or nil.
func (e *Element) Right() *Element { return e.right }

// Parent returns the parent tree element or nil.
func (e *Element) Parent() *Element { return e.parent }

// Tree represents a doubly linked tree.
// The zero value for Tree is an empty tree ready to use.
type Tree struct {
	front, back *Element
	len         int
}

// Init initializes or clears a Tree.
func (l *Tree) Init() *Tree {
	l.front = nil
	l.back = nil
	l.len = 0
	return l
}

// New returns an initialized tree.
func New() *Tree { return new(Tree) }

// Front returns the first element in the tree.
func (l *Tree) Front() *Element { return l.front }

// Back returns the last element in the tree.
func (l *Tree) Back() *Element { return l.back }

// Remove removes the element from the tree
// and returns its Value.
func (l *Tree) Remove(e *Element) interface{} {
	l.remove(e)
	e.tree = nil // do what remove does not
	return e.Value
}

// remove the element from the tree, but do not clear the Element's tree field.
// This is so that other Tree methods may use remove when relocating Elements
// without needing to restore the tree field.
func (l *Tree) remove(e *Element) {
	if e.tree != l {
		return
	}
	if e.parent == nil {
		l.front = e.left
	} else {
		e.parent.left = e.left
	}
	if e.left == nil {
		l.back = e.parent
	} else {
		e.left.parent = e.parent
	}

	e.parent = nil
	e.left = nil
	l.len--
}

func (l *Tree) insertBefore(e *Element, mark *Element) {
	if mark.parent == nil {
		// new front of the tree
		l.front = e
	} else {
		mark.parent.left = e
	}
	e.parent = mark.parent
	mark.parent = e
	e.left = mark
	l.len++
}

func (l *Tree) insertAfter(e *Element, mark *Element) {
	if mark.left == nil {
		// new back of the tree
		l.back = e
	} else {
		mark.left.parent = e
	}
	e.left = mark.left
	mark.left = e
	e.parent = mark
	l.len++
}

func (l *Tree) insertFront(e *Element) {
	if l.front == nil {
		// empty tree
		l.front, l.back = e, e
		e.parent, e.left = nil, nil
		l.len = 1
		return
	}
	l.insertBefore(e, l.front)
}

func (l *Tree) insertBack(e *Element) {
	if l.back == nil {
		// empty tree
		l.front, l.back = e, e
		e.parent, e.left = nil, nil
		l.len = 1
		return
	}
	l.insertAfter(e, l.back)
}

// PushFront inserts the value at the front of the tree and returns a new Element containing the value.
func (l *Tree) PushFront(value interface{}) *Element {
	e := &Element{nil, nil, nil, l, value}
	l.insertFront(e)
	return e
}

// PushBack inserts the value at the back of the tree and returns a new Element containing the value.
func (l *Tree) PushBack(value interface{}) *Element {
	e := &Element{nil, nil, nil, l, value}
	l.insertBack(e)
	return e
}

// InsertBefore inserts the value immediately before mark and returns a new Element containing the value.
func (l *Tree) InsertBefore(value interface{}, mark *Element) *Element {
	if mark.tree != l {
		return nil
	}
	e := &Element{nil, nil, nil, l, value}
	l.insertBefore(e, mark)
	return e
}

// InsertAfter inserts the value immediately after mark and returns a new Element containing the value.
func (l *Tree) InsertAfter(value interface{}, mark *Element) *Element {
	if mark.tree != l {
		return nil
	}
	e := &Element{nil, nil, nil, l, value}
	l.insertAfter(e, mark)
	return e
}

// MoveToFront moves the element to the front of the tree.
func (l *Tree) MoveToFront(e *Element) {
	if e.tree != l || l.front == e {
		return
	}
	l.remove(e)
	l.insertFront(e)
}

// MoveToBack moves the element to the back of the tree.
func (l *Tree) MoveToBack(e *Element) {
	if e.tree != l || l.back == e {
		return
	}
	l.remove(e)
	l.insertBack(e)
}

// Len returns the number of elements in the tree.
func (l *Tree) Len() int { return l.len }

// PushBackTree inserts each element of ol at the back of the tree.
func (l *Tree) PushBackTree(ol *Tree) {
	last := ol.Back()
	for e := ol.Front(); e != nil; e = e.Left() {
		l.PushBack(e.Value)
		if e == last {
			break
		}
	}
}

// PushFrontTree inserts each element of ol at the front of the tree. The ordering of the passed tree is preserved.
func (l *Tree) PushFrontTree(ol *Tree) {
	first := ol.Front()
	for e := ol.Back(); e != nil; e = e.Parent() {
		l.PushFront(e.Value)
		if e == first {
			break
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Copyright 2013 James Pirruccello <james@carbocation.com>
// Use of this source code is governed by the MIT license

// Package binarytree implements a binary tree, derived from the container/list code
//
// To iterate over a tree (where tree is a *Tree):
//	for e := tree.Root(); e != nil; e = e.Left() {
//		// do something with e.Value
//	}
//
package binarytree

// Tree is an element in the tree.
type Tree struct {
	// Parent, left, and right pointers in the tree of elements.
	// The root of the tree has parent = nil
    // Each leaf of a tree has left = nil.
	parent, left, right *Tree

	// The contents of this tree element.
	Value interface{}
}

func New(value interface{}) *Tree {
    return &Tree{Value: value}
}

// Left returns the left tree element or nil.
func (e *Tree) Left() *Tree { return e.left }

// Right returns the right tree element or nil.
func (e *Tree) Right() *Tree { return e.right }

// Parent returns the parent tree element or nil.
func (e *Tree) Parent() *Tree { return e.parent }

// PushLeft inserts a left node's value under the specified Tree and returns a new Tree containing the value.
func (e *Tree) PushLeft(value interface{}) *Tree {
    return e.pushTree("left", value)
}

// PushRight inserts a right node's value under the specified Tree and returns a new Tree containing the value.
func (e *Tree) PushRight(value interface{}) *Tree {
    return e.pushTree("right", value)
}

// Wrapped
func (e *Tree) pushTree(direction string, value interface{}) *Tree {
    // Create the new element el, record that the parent is e
    el := &Tree{e, nil, nil, value}

    // SWITCH/CASE to append to left or right depending on correct output
    // Update the parent element e, record that its left value is el
    switch {
    case direction == "left":
        e.left = el

    case direction == "right":
        e.right = el
    }

    // Return the new element
    return el
}

// Traverse the tree and send output to a channel
func Walk(el *Tree, ch chan interface{}) {
    if el == nil {
        return
    }

    ch <- el.Value
    Walk(el.left, ch)
    Walk(el.right, ch)
}

// Wrapper for the Walk method that creates a string channel for output
func Walker(e *Tree) <-chan interface{} {
	ch := make(chan interface{})
	go func() {
		Walk(e, ch)
		close(ch)
	}()
	return ch
}
<commit_msg>Commenting Walk<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Copyright 2013 James Pirruccello <james@carbocation.com>
// Use of this source code is governed by the MIT license

// Package binarytree implements a binary tree, derived from the container/list code
//
// To iterate over a tree (where tree is a *Tree):
//	for e := tree.Root(); e != nil; e = e.Left() {
//		// do something with e.Value
//	}
//
package binarytree

// Tree is an element in the tree.
type Tree struct {
	// Parent, left, and right pointers in the tree of elements.
	// The root of the tree has parent = nil
    // Each leaf of a tree has left = nil.
	parent, left, right *Tree

	// The contents of this tree element.
	Value interface{}
}

func New(value interface{}) *Tree {
    return &Tree{Value: value}
}

// Left returns the left tree element or nil.
func (e *Tree) Left() *Tree { return e.left }

// Right returns the right tree element or nil.
func (e *Tree) Right() *Tree { return e.right }

// Parent returns the parent tree element or nil.
func (e *Tree) Parent() *Tree { return e.parent }

// PushLeft inserts a left node's value under the specified Tree and returns a new Tree containing the value.
func (e *Tree) PushLeft(value interface{}) *Tree {
    return e.pushTree("left", value)
}

// PushRight inserts a right node's value under the specified Tree and returns a new Tree containing the value.
func (e *Tree) PushRight(value interface{}) *Tree {
    return e.pushTree("right", value)
}

// Wrapped
func (e *Tree) pushTree(direction string, value interface{}) *Tree {
    // Create the new element el, record that the parent is e
    el := &Tree{e, nil, nil, value}

    // SWITCH/CASE to append to left or right depending on correct output
    // Update the parent element e, record that its left value is el
    switch {
    case direction == "left":
        e.left = el

    case direction == "right":
        e.right = el
    }

    // Return the new element
    return el
}

// Traverse the tree and send output to a channel
// Note that this is simply one reference implementation. For some applications,
// e.g., threaded discussion, something like this would be implemented in the view.
func Walk(el *Tree, ch chan interface{}) {
    if el == nil {
        return
    }

    ch <- el.Value
    Walk(el.left, ch)
    Walk(el.right, ch)
}

// Wrapper for the Walk method that creates a string channel for output
func Walker(e *Tree) <-chan interface{} {
	ch := make(chan interface{})
	go func() {
		Walk(e, ch)
		close(ch)
	}()
	return ch
}
<|endoftext|>


<commit_before>// Package speaker implements playback of beep.Streamer values through physical speakers.
package speaker

import (
	"sync"

	"github.com/faiface/beep"
	"github.com/hajimehoshi/oto"
	"github.com/pkg/errors"
)

var (
	mu       sync.Mutex
	mixer    beep.Mixer
	samples  [][2]float64
	buf      []byte
	player   *oto.Player
	underrun func()
	done     chan struct{}
)

// Init initializes audio playback through speaker. Must be called before using this package.
//
// The bufferSize argument specifies the number of samples of the speaker's buffer. Bigger
// bufferSize means lower CPU usage and more reliable playback. Lower bufferSize means better
// responsiveness and less delay.
func Init(sampleRate beep.SampleRate, bufferSize int) error {
	mu.Lock()
	defer mu.Unlock()

	if player != nil {
		done <- struct{}{}
		player.Close()
	}

	mixer = beep.Mixer{}

	numBytes := bufferSize * 4
	samples = make([][2]float64, bufferSize)
	buf = make([]byte, numBytes)

	var err error
	player, err = oto.NewPlayer(int(sampleRate), 2, 2, numBytes)
	if err != nil {
		return errors.Wrap(err, "failed to initialize speaker")
	}

	if underrun != nil {
		player.SetUnderrunCallback(underrun)
	}

	done = make(chan struct{})

	go func() {
		for {
			select {
			default:
				update()
			case <-done:
				return
			}
		}
	}()

	return nil
}

// UnderrunCallback sets a function which will be called when an underrun occurs. This is useful for
// debugging and optimization purposes.
//
// Underrun happens when program doesn't keep up with the audio playback and doesn't supply audio
// data quickly enough. To fix an underrun, you either need to optimize your audio processing code,
// or increase the buffer size.
func UnderrunCallback(f func()) {
	mu.Lock()
	underrun = f
	if player != nil {
		player.SetUnderrunCallback(underrun)
	}
	mu.Unlock()
}

// Lock locks the speaker. While locked, speaker won't pull new data from the playing Stramers. Lock
// if you want to modify any currently playing Streamers to avoid race conditions.
//
// Always lock speaker for as little time as possible, to avoid playback glitches.
func Lock() {
	mu.Lock()
}

// Unlock unlocks the speaker. Call after modifying any currently playing Streamer.
func Unlock() {
	mu.Unlock()
}

// Play starts playing all provided Streamers through the speaker.
func Play(s ...beep.Streamer) {
	mu.Lock()
	mixer.Play(s...)
	mu.Unlock()
}

// update pulls new data from the playing Streamers and sends it to the speaker. Blocks until the
// data is sent and started playing.
func update() {
	mu.Lock()
	mixer.Stream(samples)
	mu.Unlock()

	for i := range samples {
		for c := range samples[i] {
			val := samples[i][c]
			if val < -1 {
				val = -1
			}
			if val > +1 {
				val = +1
			}
			valInt16 := int16(val * (1<<15 - 1))
			low := byte(valInt16)
			high := byte(valInt16 >> 8)
			buf[i*4+c*2+0] = low
			buf[i*4+c*2+1] = high
		}
	}

	player.Write(buf)
}
<commit_msg>speaker: add note about OS support to UnderrunCallback doc<commit_after>// Package speaker implements playback of beep.Streamer values through physical speakers.
package speaker

import (
	"sync"

	"github.com/faiface/beep"
	"github.com/hajimehoshi/oto"
	"github.com/pkg/errors"
)

var (
	mu       sync.Mutex
	mixer    beep.Mixer
	samples  [][2]float64
	buf      []byte
	player   *oto.Player
	underrun func()
	done     chan struct{}
)

// Init initializes audio playback through speaker. Must be called before using this package.
//
// The bufferSize argument specifies the number of samples of the speaker's buffer. Bigger
// bufferSize means lower CPU usage and more reliable playback. Lower bufferSize means better
// responsiveness and less delay.
func Init(sampleRate beep.SampleRate, bufferSize int) error {
	mu.Lock()
	defer mu.Unlock()

	if player != nil {
		done <- struct{}{}
		player.Close()
	}

	mixer = beep.Mixer{}

	numBytes := bufferSize * 4
	samples = make([][2]float64, bufferSize)
	buf = make([]byte, numBytes)

	var err error
	player, err = oto.NewPlayer(int(sampleRate), 2, 2, numBytes)
	if err != nil {
		return errors.Wrap(err, "failed to initialize speaker")
	}

	if underrun != nil {
		player.SetUnderrunCallback(underrun)
	}

	done = make(chan struct{})

	go func() {
		for {
			select {
			default:
				update()
			case <-done:
				return
			}
		}
	}()

	return nil
}

// UnderrunCallback sets a function which will be called when an underrun occurs. This is useful for
// debugging and optimization purposes.
//
// Underrun happens when program doesn't keep up with the audio playback and doesn't supply audio
// data quickly enough. To fix an underrun, you either need to optimize your audio processing code,
// or increase the buffer size.
//
// Underrun detection currently works on Linux.
func UnderrunCallback(f func()) {
	mu.Lock()
	underrun = f
	if player != nil {
		player.SetUnderrunCallback(underrun)
	}
	mu.Unlock()
}

// Lock locks the speaker. While locked, speaker won't pull new data from the playing Stramers. Lock
// if you want to modify any currently playing Streamers to avoid race conditions.
//
// Always lock speaker for as little time as possible, to avoid playback glitches.
func Lock() {
	mu.Lock()
}

// Unlock unlocks the speaker. Call after modifying any currently playing Streamer.
func Unlock() {
	mu.Unlock()
}

// Play starts playing all provided Streamers through the speaker.
func Play(s ...beep.Streamer) {
	mu.Lock()
	mixer.Play(s...)
	mu.Unlock()
}

// update pulls new data from the playing Streamers and sends it to the speaker. Blocks until the
// data is sent and started playing.
func update() {
	mu.Lock()
	mixer.Stream(samples)
	mu.Unlock()

	for i := range samples {
		for c := range samples[i] {
			val := samples[i][c]
			if val < -1 {
				val = -1
			}
			if val > +1 {
				val = +1
			}
			valInt16 := int16(val * (1<<15 - 1))
			low := byte(valInt16)
			high := byte(valInt16 >> 8)
			buf[i*4+c*2+0] = low
			buf[i*4+c*2+1] = high
		}
	}

	player.Write(buf)
}
<|endoftext|>


<commit_before>// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.
//

package ghttp

import (
    "sync"
    "net/http"
    "gitee.com/johng/gf/g/util/gconv"
    "gitee.com/johng/gf/g/encoding/gparser"
    "strconv"
)

// 服务端请求返回对象
type Response struct {
    ResponseWriter
    Writer  *ResponseWriter // io.Writer
    mu      sync.RWMutex    // 缓冲区互斥锁
    buffer  []byte          // 每个请求的返回数据缓冲区
    request *Request        // 关联的Request请求对象
}

// 自定义的ResponseWriter，用于写入流的控制
type ResponseWriter struct {
    http.ResponseWriter
    Status int // http status
    Length int // response length
}

// 创建一个ghttp.Response对象指针
func newResponse(w http.ResponseWriter) *Response {
    r := &Response {
        ResponseWriter : ResponseWriter{w, http.StatusOK, 0},
    }
    r.Writer = &r.ResponseWriter
    return r
}

// 覆盖父级的WriteHeader方法
func (w *ResponseWriter) Write(buffer []byte) (int, error) {
    n, e := w.ResponseWriter.Write(buffer)
    w.Length += n
    return n, e
}

// 覆盖父级的WriteHeader方法
func (w *ResponseWriter) WriteHeader(code int) {
    w.Status = code
    w.ResponseWriter.WriteHeader(code)
}

// 返回信息，任何变量自动转换为bytes
func (r *Response) Write(content ... interface{}) {
    if len(content) == 0 {
        return
    }
    r.mu.Lock()
    for _, v := range content {
        switch v.(type) {
            case []byte:
                // 如果是二进制数据，那么返回二进制数据
                r.buffer = append(r.buffer, gconv.Bytes(v)...)
            default:
                // 否则一律按照可显示的字符串进行转换
                r.buffer = append(r.buffer, gconv.String(v)...)
        }
    }
    r.mu.Unlock()
}

// 返回信息，末尾增加换行标识符"\n"
func (r *Response) Writeln(content ... interface{}) {
    if len(content) == 0 {
        return
    }
    content = append(content, "\n")
    r.Write(content...)
}

// 返回JSON
func (r *Response) WriteJson(content interface{}) error {
    if b, err := gparser.VarToJson(content); err != nil {
        return err
    } else {
        r.Header().Set("Content-Type", "application/json")
        r.Write(b)
    }
    return nil
}

// 返回JSONP
func (r *Response) WriteJsonP(content interface{}) error {
    if b, err := gparser.VarToJson(content); err != nil {
        return err
    } else {
        //r.Header().Set("Content-Type", "application/json")
        if callback := r.request.Get("callback"); callback != "" {
            buffer := []byte(callback)
            buffer  = append(buffer, byte('('))
            buffer  = append(buffer, b...)
            buffer  = append(buffer, byte(')'))
            r.Write(buffer)
        } else {
            r.Write(b)
        }
    }
    return nil
}

// 返回XML
func (r *Response) WriteXml(content interface{}, rootTag...string) error {
    if b, err := gparser.VarToXml(content, rootTag...); err != nil {
        return err
    } else {
        r.Header().Set("Content-Type", "application/xml")
        r.Write(b)
    }
    return nil
}

// 允许AJAX跨域访问
func (r *Response) SetAllowCrossDomainRequest(allowOrigin string, allowMethods string, maxAge...int) {
    age := 3628800
    if len(maxAge) > 0 {
        age = maxAge[0]
    }
    r.Header().Set("Access-Control-Allow-Origin",  allowOrigin);
    r.Header().Set("Access-Control-Allow-Methods", allowMethods);
    r.Header().Set("Access-Control-Max-Age",       strconv.Itoa(age));
}

// 返回HTTP Code状态码
func (r *Response) WriteStatus(status int, content...string) {
    if len(r.buffer) == 0 {
        // 状态码注册回调函数处理
        if status != http.StatusOK {
            if f := r.request.Server.getStatusHandler(status, r.request); f != nil {
                f(r.request)
                r.WriteHeader(status)
                return
            }
        }
        r.Header().Set("Content-Type", "text/plain; charset=utf-8")
        r.Header().Set("X-Content-Type-Options", "nosniff")
        if len(content) > 0 {
            r.Write(content[0])
        } else {
            r.Write(http.StatusText(status))
        }
    }
    r.WriteHeader(status)
}

// 返回location标识，引导客户端跳转
func (r *Response) RedirectTo(location string) {
    r.Header().Set("Location", location)
    r.WriteHeader(http.StatusFound)
}

// 获取当前缓冲区中的数据
func (r *Response) Buffer() []byte {
    r.mu.RLock()
    defer r.mu.RUnlock()
    return r.buffer
}

// 获取当前缓冲区中的数据大小
func (r *Response) BufferLength() int {
    r.mu.RLock()
    defer r.mu.RUnlock()
    return len(r.buffer)
}

// 手动设置缓冲区内容
func (r *Response) SetBuffer(buffer []byte) {
    r.mu.Lock()
    r.buffer = buffer
    r.mu.Unlock()
}

// 清空缓冲区内容
func (r *Response) ClearBuffer() {
    r.mu.Lock()
    r.buffer = make([]byte, 0)
    r.mu.Unlock()
}

// 输出缓冲区数据到客户端
func (r *Response) OutputBuffer() {
    if len(r.buffer) > 0 {
        r.mu.Lock()
        r.ResponseWriter.Write(r.buffer)
        r.buffer = make([]byte, 0)
        r.mu.Unlock()
    }

}
<commit_msg>改进ghttp.Server自定义错误状态码返回<commit_after>// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.
//

package ghttp

import (
    "sync"
    "net/http"
    "gitee.com/johng/gf/g/util/gconv"
    "gitee.com/johng/gf/g/encoding/gparser"
    "strconv"
)

// 服务端请求返回对象
type Response struct {
    ResponseWriter
    Writer  *ResponseWriter // io.Writer
    mu      sync.RWMutex    // 缓冲区互斥锁
    buffer  []byte          // 每个请求的返回数据缓冲区
    request *Request        // 关联的Request请求对象
}

// 自定义的ResponseWriter，用于写入流的控制
type ResponseWriter struct {
    http.ResponseWriter
    Status int // http status
    Length int // response length
}

// 创建一个ghttp.Response对象指针
func newResponse(w http.ResponseWriter) *Response {
    r := &Response {
        ResponseWriter : ResponseWriter{w, http.StatusOK, 0},
    }
    r.Writer = &r.ResponseWriter
    return r
}

// 覆盖父级的WriteHeader方法
func (w *ResponseWriter) Write(buffer []byte) (int, error) {
    n, e := w.ResponseWriter.Write(buffer)
    w.Length += n
    return n, e
}

// 覆盖父级的WriteHeader方法
func (w *ResponseWriter) WriteHeader(code int) {
    w.Status = code
    w.ResponseWriter.WriteHeader(code)
}

// 返回信息，任何变量自动转换为bytes
func (r *Response) Write(content ... interface{}) {
    if len(content) == 0 {
        return
    }
    r.mu.Lock()
    for _, v := range content {
        switch v.(type) {
            case []byte:
                // 如果是二进制数据，那么返回二进制数据
                r.buffer = append(r.buffer, gconv.Bytes(v)...)
            default:
                // 否则一律按照可显示的字符串进行转换
                r.buffer = append(r.buffer, gconv.String(v)...)
        }
    }
    r.mu.Unlock()
}

// 返回信息，末尾增加换行标识符"\n"
func (r *Response) Writeln(content ... interface{}) {
    if len(content) == 0 {
        return
    }
    content = append(content, "\n")
    r.Write(content...)
}

// 返回JSON
func (r *Response) WriteJson(content interface{}) error {
    if b, err := gparser.VarToJson(content); err != nil {
        return err
    } else {
        r.Header().Set("Content-Type", "application/json")
        r.Write(b)
    }
    return nil
}

// 返回JSONP
func (r *Response) WriteJsonP(content interface{}) error {
    if b, err := gparser.VarToJson(content); err != nil {
        return err
    } else {
        //r.Header().Set("Content-Type", "application/json")
        if callback := r.request.Get("callback"); callback != "" {
            buffer := []byte(callback)
            buffer  = append(buffer, byte('('))
            buffer  = append(buffer, b...)
            buffer  = append(buffer, byte(')'))
            r.Write(buffer)
        } else {
            r.Write(b)
        }
    }
    return nil
}

// 返回XML
func (r *Response) WriteXml(content interface{}, rootTag...string) error {
    if b, err := gparser.VarToXml(content, rootTag...); err != nil {
        return err
    } else {
        r.Header().Set("Content-Type", "application/xml")
        r.Write(b)
    }
    return nil
}

// 允许AJAX跨域访问
func (r *Response) SetAllowCrossDomainRequest(allowOrigin string, allowMethods string, maxAge...int) {
    age := 3628800
    if len(maxAge) > 0 {
        age = maxAge[0]
    }
    r.Header().Set("Access-Control-Allow-Origin",  allowOrigin);
    r.Header().Set("Access-Control-Allow-Methods", allowMethods);
    r.Header().Set("Access-Control-Max-Age",       strconv.Itoa(age));
}

// 返回HTTP Code状态码
func (r *Response) WriteStatus(status int, content...string) {
    if len(r.buffer) == 0 {
        // 状态码注册回调函数处理
        if status != http.StatusOK {
            if f := r.request.Server.getStatusHandler(status, r.request); f != nil {
                f(r.request)
                // 如果是http.StatusOK那么表示回调函数内部没有设置header status，
                // 那么这里就可以设置status，防止多次设置(http: multiple response.WriteHeader calls)
                if r.Status == http.StatusOK {
                    r.WriteHeader(status)
                }
                return
            }
        }
        r.Header().Set("Content-Type", "text/plain; charset=utf-8")
        r.Header().Set("X-Content-Type-Options", "nosniff")
        if len(content) > 0 {
            r.Write(content[0])
        } else {
            r.Write(http.StatusText(status))
        }
    }
    r.WriteHeader(status)
}

// 返回location标识，引导客户端跳转
func (r *Response) RedirectTo(location string) {
    r.Header().Set("Location", location)
    r.WriteHeader(http.StatusFound)
}

// 获取当前缓冲区中的数据
func (r *Response) Buffer() []byte {
    r.mu.RLock()
    defer r.mu.RUnlock()
    return r.buffer
}

// 获取当前缓冲区中的数据大小
func (r *Response) BufferLength() int {
    r.mu.RLock()
    defer r.mu.RUnlock()
    return len(r.buffer)
}

// 手动设置缓冲区内容
func (r *Response) SetBuffer(buffer []byte) {
    r.mu.Lock()
    r.buffer = buffer
    r.mu.Unlock()
}

// 清空缓冲区内容
func (r *Response) ClearBuffer() {
    r.mu.Lock()
    r.buffer = make([]byte, 0)
    r.mu.Unlock()
}

// 输出缓冲区数据到客户端
func (r *Response) OutputBuffer() {
    if len(r.buffer) > 0 {
        r.mu.Lock()
        r.ResponseWriter.Write(r.buffer)
        r.buffer = make([]byte, 0)
        r.mu.Unlock()
    }

}
<|endoftext|>


<commit_before>// Copyright 2016-2021 The Libsacloud 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 test

import (
	"context"
	"os"
	"testing"

	"github.com/sacloud/libsacloud/v2/sacloud"
	"github.com/sacloud/libsacloud/v2/sacloud/accessor"
	"github.com/sacloud/libsacloud/v2/sacloud/testutil"
	"github.com/sacloud/libsacloud/v2/sacloud/types"
)

func TestMain(m *testing.M) {
	testZone = testutil.TestZone()

	ret := m.Run()

	skipCleanup := os.Getenv("SKIP_CLEANUP")
	if skipCleanup == "" {
		if err := testutil.CleanupTestResources(context.Background(), singletonAPICaller()); err != nil {
			panic(err)
		}
	}

	os.Exit(ret)
}

var testZone string
var testIconID = types.ID(112901627749) // テスト用のアイコンID(shared icon)

func singletonAPICaller() sacloud.APICaller {
	return testutil.SingletonAPICaller()
}

func isAccTest() bool {
	return testutil.IsAccTest()
}

func setupSwitchFunc(targetResource string, dests ...accessor.SwitchID) func(*testutil.CRUDTestContext, sacloud.APICaller) error {
	return func(testContext *testutil.CRUDTestContext, caller sacloud.APICaller) error {
		swClient := sacloud.NewSwitchOp(caller)
		sw, err := swClient.Create(context.Background(), testZone, &sacloud.SwitchCreateRequest{
			Name: testutil.ResourceName("switch-for-" + targetResource),
		})
		if err != nil {
			return err
		}

		testContext.Values[targetResource+"/switch"] = sw.ID
		for _, dest := range dests {
			dest.SetSwitchID(sw.ID)
		}
		return nil
	}
}

func cleanupSwitchFunc(targetResource string) func(*testutil.CRUDTestContext, sacloud.APICaller) error {
	return func(testContext *testutil.CRUDTestContext, caller sacloud.APICaller) error {
		switchID, ok := testContext.Values[targetResource+"/switch"]
		if !ok {
			return nil
		}

		swClient := sacloud.NewSwitchOp(caller)
		return swClient.Delete(context.Background(), testZone, switchID.(types.ID))
	}
}
<commit_msg>Remove os.Exit() from TestMain<commit_after>// Copyright 2016-2021 The Libsacloud 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 test

import (
	"context"
	"os"
	"testing"

	"github.com/sacloud/libsacloud/v2/sacloud"
	"github.com/sacloud/libsacloud/v2/sacloud/accessor"
	"github.com/sacloud/libsacloud/v2/sacloud/testutil"
	"github.com/sacloud/libsacloud/v2/sacloud/types"
)

func TestMain(m *testing.M) {
	testZone = testutil.TestZone()

	m.Run()

	skipCleanup := os.Getenv("SKIP_CLEANUP")
	if skipCleanup == "" {
		if err := testutil.CleanupTestResources(context.Background(), singletonAPICaller()); err != nil {
			panic(err)
		}
	}
}

var testZone string
var testIconID = types.ID(112901627749) // テスト用のアイコンID(shared icon)

func singletonAPICaller() sacloud.APICaller {
	return testutil.SingletonAPICaller()
}

func isAccTest() bool {
	return testutil.IsAccTest()
}

func setupSwitchFunc(targetResource string, dests ...accessor.SwitchID) func(*testutil.CRUDTestContext, sacloud.APICaller) error {
	return func(testContext *testutil.CRUDTestContext, caller sacloud.APICaller) error {
		swClient := sacloud.NewSwitchOp(caller)
		sw, err := swClient.Create(context.Background(), testZone, &sacloud.SwitchCreateRequest{
			Name: testutil.ResourceName("switch-for-" + targetResource),
		})
		if err != nil {
			return err
		}

		testContext.Values[targetResource+"/switch"] = sw.ID
		for _, dest := range dests {
			dest.SetSwitchID(sw.ID)
		}
		return nil
	}
}

func cleanupSwitchFunc(targetResource string) func(*testutil.CRUDTestContext, sacloud.APICaller) error {
	return func(testContext *testutil.CRUDTestContext, caller sacloud.APICaller) error {
		switchID, ok := testContext.Values[targetResource+"/switch"]
		if !ok {
			return nil
		}

		swClient := sacloud.NewSwitchOp(caller)
		return swClient.Delete(context.Background(), testZone, switchID.(types.ID))
	}
}
<|endoftext|>


<commit_before>// +build darwin

package darwin

import (
	"bufio"
	"bytes"
	"os/exec"
	"strconv"
	"strings"

	"github.com/mackerelio/mackerel-agent/logging"
)

// CPUGenerator Collects CPU specs
type CPUGenerator struct {
}

// Key XXX
func (g *CPUGenerator) Key() string {
	return "cpu"
}

var cpuLogger = logging.GetLogger("spec.cpu")

type cpuSpec map[string]interface{}

var sysCtlKeyMap = map[string]string{
	"core_count":   "cores",
	"brand_string": "model_name",
	"model":        "model",
	"vendor":       "vendor_id",
	"family":       "family",
	"stepping":     "stepping",
}

func (g *CPUGenerator) parseSysCtlBytes(res []byte) (interface{}, error) {
	scanner := bufio.NewScanner(bytes.NewBuffer(res))

	results := cpuSpec{}
	var coreCount int

	for scanner.Scan() {
		line := scanner.Text()
		kv := strings.SplitN(line, ":", 2)
		if len(kv) < 2 {
			continue
		}
		key := strings.TrimPrefix(strings.TrimSpace(kv[0]), "machdep.cpu.")
		val := strings.TrimSpace(kv[1])
		if label, ok := sysCtlKeyMap[key]; ok {
			results[label] = val
		}
		if key == "core_count" {
			var err error
			coreCount, err = strconv.Atoi(val)
			if err != nil {
				cpuLogger.Errorf("while parsing %q: %s", val, err)
				return nil, err
			}
		}
	}

	if err := scanner.Err(); err != nil {
		cpuLogger.Errorf("Failed (skip this spec): %s", err)
		return nil, err
	}

	wholeResults := make([]cpuSpec, 0, coreCount)
	for i := 0; i < coreCount; i++ {
		wholeResults = append(wholeResults, results)
	}
	return wholeResults, nil
}

// MEMO: sysctl -a machdep.cpu.brand_string

// Generate collects CPU specs.
// Returns an array of cpuSpec.
// Each spec is expected to have keys below:
// - model_name (used in Web)
// - vendor_id
// - family
// - model
// - stepping
// - physical_id
// - core_id
// - cores
// - mhz
// - cache_size
// - flags
func (g *CPUGenerator) Generate() (interface{}, error) {
	cpuInfoBytes, err := exec.Command("sysctl", "-a", "machdep.cpu").Output()
	if err != nil {
		cpuLogger.Errorf("Failed: %s", err)
		return nil, err
	}
	return g.parseSysCtlBytes(cpuInfoBytes)
}
<commit_msg>use assignment<commit_after>// +build darwin

package darwin

import (
	"bufio"
	"bytes"
	"os/exec"
	"strconv"
	"strings"

	"github.com/mackerelio/mackerel-agent/logging"
)

// CPUGenerator Collects CPU specs
type CPUGenerator struct {
}

// Key XXX
func (g *CPUGenerator) Key() string {
	return "cpu"
}

var cpuLogger = logging.GetLogger("spec.cpu")

type cpuSpec map[string]interface{}

var sysCtlKeyMap = map[string]string{
	"core_count":   "cores",
	"brand_string": "model_name",
	"model":        "model",
	"vendor":       "vendor_id",
	"family":       "family",
	"stepping":     "stepping",
}

func (g *CPUGenerator) parseSysCtlBytes(res []byte) (interface{}, error) {
	scanner := bufio.NewScanner(bytes.NewBuffer(res))

	results := cpuSpec{}
	var coreCount int

	for scanner.Scan() {
		line := scanner.Text()
		kv := strings.SplitN(line, ":", 2)
		if len(kv) < 2 {
			continue
		}
		key := strings.TrimPrefix(strings.TrimSpace(kv[0]), "machdep.cpu.")
		val := strings.TrimSpace(kv[1])
		if label, ok := sysCtlKeyMap[key]; ok {
			results[label] = val
		}
		if key == "core_count" {
			var err error
			coreCount, err = strconv.Atoi(val)
			if err != nil {
				cpuLogger.Errorf("while parsing %q: %s", val, err)
				return nil, err
			}
		}
	}

	if err := scanner.Err(); err != nil {
		cpuLogger.Errorf("Failed (skip this spec): %s", err)
		return nil, err
	}

	wholeResults := make([]cpuSpec, 0, coreCount)
	for i := 0; i < coreCount; i++ {
		wholeResults[i] = results
	}
	return wholeResults, nil
}

// MEMO: sysctl -a machdep.cpu.brand_string

// Generate collects CPU specs.
// Returns an array of cpuSpec.
// Each spec is expected to have keys below:
// - model_name (used in Web)
// - vendor_id
// - family
// - model
// - stepping
// - physical_id
// - core_id
// - cores
// - mhz
// - cache_size
// - flags
func (g *CPUGenerator) Generate() (interface{}, error) {
	cpuInfoBytes, err := exec.Command("sysctl", "-a", "machdep.cpu").Output()
	if err != nil {
		cpuLogger.Errorf("Failed: %s", err)
		return nil, err
	}
	return g.parseSysCtlBytes(cpuInfoBytes)
}
<|endoftext|>


<commit_before>package gaurun

import (
	"fmt"
	"strings"
	"sync"
	"sync/atomic"

	"github.com/RobotsAndPencils/buford/push"
)

var (
	// PusherCountAll is the shared value between workers
	PusherCountAll int64

	// PusherWg is global wait group for pusher worker.
	// It increments when new pusher is swapned and decrements when job is done.
	//
	// This is used to block main process to shutdown while pusher is still working.
	PusherWg sync.WaitGroup

	// Pushers supports Dependency Injection
	PushNotificationIosFunc     = pushNotificationIos
	PushNotificationAndroidFunc = pushNotificationAndroid
)

func init() {
	PusherCountAll = 0
}

func StartPushWorkers(workerNum, queueNum int64) {
	QueueNotification = make(chan RequestGaurunNotification, queueNum)
	for i := int64(0); i < workerNum; i++ {
		go pushNotificationWorker()
	}
}

func isExternalServerError(err error, platform int) bool {
	switch platform {
	case PlatFormIos:
		if err == push.ErrIdleTimeout || err == push.ErrShutdown || err == push.ErrInternalServerError || err == push.ErrServiceUnavailable {
			return true
		}
	case PlatFormAndroid:
		if err.Error() == "Unavailable" || err.Error() == "InternalServerError" || strings.Contains(err.Error(), "Timeout") {
			return true
		}
	default:
		// not through
	}
	return false
}

func pushSync(pusher func(req RequestGaurunNotification) error, req RequestGaurunNotification, retryMax int) {
	PusherWg.Add(1)
	defer PusherWg.Done()
Retry:
	err := pusher(req)
	if err != nil && req.Retry < retryMax && isExternalServerError(err, req.Platform) {
		req.Retry++
		goto Retry
	}
}

func pushAsync(pusher func(req RequestGaurunNotification) error, req RequestGaurunNotification, retryMax int, pusherCount *int64) {
	defer PusherWg.Done()
Retry:
	err := pusher(req)
	if err != nil && req.Retry < retryMax && isExternalServerError(err, req.Platform) {
		req.Retry++
		goto Retry
	}

	atomic.AddInt64(pusherCount, -1)
	atomic.AddInt64(&PusherCountAll, -1)
}

func pushNotificationWorker() {
	var (
		retryMax    int
		pusher      func(req RequestGaurunNotification) error
		pusherCount int64
	)

	// pusherCount is the independent value between workers
	pusherCount = 0

	for {
		notification := <-QueueNotification

		switch notification.Platform {
		case PlatFormIos:
			pusher = PushNotificationIosFunc
			retryMax = ConfGaurun.Ios.RetryMax
		case PlatFormAndroid:
			pusher = PushNotificationAndroidFunc
			retryMax = ConfGaurun.Android.RetryMax
		default:
			LogError.Warn(fmt.Sprintf("invalid platform: %d", notification.Platform))
			continue
		}

		if atomic.LoadInt64(&ConfGaurun.Core.PusherMax) <= 0 {
			pushSync(pusher, notification, retryMax)
			continue
		}

		if atomic.LoadInt64(&pusherCount) < atomic.LoadInt64(&ConfGaurun.Core.PusherMax) {
			// Do not increment pusherCount and PusherCountAll in pushAsync().
			// Because pusherCount and PusherCountAll are sometimes over pusherMax
			// as the increment in goroutine runs asynchronously.
			atomic.AddInt64(&pusherCount, 1)
			atomic.AddInt64(&PusherCountAll, 1)
			PusherWg.Add(1)
			go pushAsync(pusher, notification, retryMax, &pusherCount)
			continue
		} else {
			pushSync(pusher, notification, retryMax)
			continue
		}
	}
}
<commit_msg>Remove unused variables<commit_after>package gaurun

import (
	"fmt"
	"strings"
	"sync"
	"sync/atomic"

	"github.com/RobotsAndPencils/buford/push"
)

var (
	// PusherCountAll is the shared value between workers
	PusherCountAll int64

	// PusherWg is global wait group for pusher worker.
	// It increments when new pusher is swapned and decrements when job is done.
	//
	// This is used to block main process to shutdown while pusher is still working.
	PusherWg sync.WaitGroup
)

func init() {
	PusherCountAll = 0
}

func StartPushWorkers(workerNum, queueNum int64) {
	QueueNotification = make(chan RequestGaurunNotification, queueNum)
	for i := int64(0); i < workerNum; i++ {
		go pushNotificationWorker()
	}
}

func isExternalServerError(err error, platform int) bool {
	switch platform {
	case PlatFormIos:
		if err == push.ErrIdleTimeout || err == push.ErrShutdown || err == push.ErrInternalServerError || err == push.ErrServiceUnavailable {
			return true
		}
	case PlatFormAndroid:
		if err.Error() == "Unavailable" || err.Error() == "InternalServerError" || strings.Contains(err.Error(), "Timeout") {
			return true
		}
	default:
		// not through
	}
	return false
}

func pushSync(pusher func(req RequestGaurunNotification) error, req RequestGaurunNotification, retryMax int) {
	PusherWg.Add(1)
	defer PusherWg.Done()
Retry:
	err := pusher(req)
	if err != nil && req.Retry < retryMax && isExternalServerError(err, req.Platform) {
		req.Retry++
		goto Retry
	}
}

func pushAsync(pusher func(req RequestGaurunNotification) error, req RequestGaurunNotification, retryMax int, pusherCount *int64) {
	defer PusherWg.Done()
Retry:
	err := pusher(req)
	if err != nil && req.Retry < retryMax && isExternalServerError(err, req.Platform) {
		req.Retry++
		goto Retry
	}

	atomic.AddInt64(pusherCount, -1)
	atomic.AddInt64(&PusherCountAll, -1)
}

func pushNotificationWorker() {
	var (
		retryMax    int
		pusher      func(req RequestGaurunNotification) error
		pusherCount int64
	)

	// pusherCount is the independent value between workers
	pusherCount = 0

	for {
		notification := <-QueueNotification

		switch notification.Platform {
		case PlatFormIos:
			pusher = pushNotificationIos
			retryMax = ConfGaurun.Ios.RetryMax
		case PlatFormAndroid:
			pusher = pushNotificationAndroid
			retryMax = ConfGaurun.Android.RetryMax
		default:
			LogError.Warn(fmt.Sprintf("invalid platform: %d", notification.Platform))
			continue
		}

		if atomic.LoadInt64(&ConfGaurun.Core.PusherMax) <= 0 {
			pushSync(pusher, notification, retryMax)
			continue
		}

		if atomic.LoadInt64(&pusherCount) < atomic.LoadInt64(&ConfGaurun.Core.PusherMax) {
			// Do not increment pusherCount and PusherCountAll in pushAsync().
			// Because pusherCount and PusherCountAll are sometimes over pusherMax
			// as the increment in goroutine runs asynchronously.
			atomic.AddInt64(&pusherCount, 1)
			atomic.AddInt64(&PusherCountAll, 1)
			PusherWg.Add(1)
			go pushAsync(pusher, notification, retryMax, &pusherCount)
			continue
		} else {
			pushSync(pusher, notification, retryMax)
			continue
		}
	}
}
<|endoftext|>


<commit_before>package dockerVolumeRbd

import (
	"github.com/Sirupsen/logrus"
	"github.com/hashicorp/consul/api"
	"encoding/json"
	"os"
)

const KEY_PREFIX = "docker/volume/rbd/	"

func (d *rbdDriver) setVolume(v *Volume) error {

	err, kv := getConnection()
	if err != nil {
		return err
	}

	data, err := json.Marshal(v)
	if err != nil {
		logrus.WithField("consul", "saveVolume").Error(err)
		return err
	}

	p := &api.KVPair{Key: getKeyFromName(v.Name), Value: data}
	_, _, err = kv.CAS(p, nil)
	if err != nil {
		logrus.WithField("consul", "saveVolume").Error(err)
		panic(err)
	}

	return nil

}

func (d *rbdDriver) deleteVolume(name string) (error) {

	err, kv := getConnection()
	if err != nil {
		return err
	}

	_, err = kv.Delete(getKeyFromName(name), nil)
	if err != nil {
		logrus.WithField("consul", "deleteVolume").Error(err)
		return err
	}

	return nil
}

func (d *rbdDriver) getVolume(name string) (error, *Volume) {

	err, kv := getConnection()
	if err != nil {
		return err, nil
	}

	pair, _, err := kv.Get(getKeyFromName(name), nil)
	if err != nil {
		logrus.WithField("consul", "getVolume").Error(err)
		return err, nil
	}

	v := Volume{}

	logrus.WithField("consul.go", "getVolume").Debugf("pair: %s=%s ", pair.Key, pair.Value)

	if err := json.Unmarshal(pair.Value, &v); err != nil {
		logrus.WithField("consul", "getVolume").Error(err)
		return err, nil
	}

	return nil, &v
}

func (d *rbdDriver) getVolumes() (error, *map[string]*Volume) {

	err, kv := getConnection()
	if err != nil {
		return err, nil
	}

	pairs, _, err := kv.List(getKeyFromName(""), nil)
	if err != nil {
		logrus.WithField("consul", "getVolumes").Error(err)
		return err, nil
	}

	volumes := map[string]*Volume{}

	for _, pair := range pairs {

		v := Volume{}

		if err := json.Unmarshal(pair.Value, &v); err != nil {
			logrus.WithField("consul", "getVolumes").Error(err)
			return err, nil
		}

		volumes[v.Name] = &v
	}

	return nil, &volumes
}

func getConnection() (error, *api.KV) {

	config := api.DefaultConfig()

	config.Address = os.Getenv("CONSUL_ADDRESS")

	if (config.Address == "") {
		config.Address = "localhost:8500"
	}

	client, err := api.NewClient(config)
	if err != nil {
		logrus.WithField("consul", "getConnection").Error(err)
		return err, nil
	}

	// Get a handle to the KV API
	return nil, client.KV()

}

func getKeyFromName(name string) string {
	return KEY_PREFIX + name
}
<commit_msg>debug<commit_after>package dockerVolumeRbd

import (
	"github.com/Sirupsen/logrus"
	"github.com/hashicorp/consul/api"
	"encoding/json"
	"os"
)

const KEY_PREFIX = "docker/volume/rbd/	"

func (d *rbdDriver) setVolume(v *Volume) error {

	err, kv := getConnection()
	if err != nil {
		return err
	}

	data, err := json.Marshal(v)
	if err != nil {
		logrus.WithField("consul", "saveVolume").Error(err)
		return err
	}

	p := &api.KVPair{Key: getKeyFromName(v.Name), Value: data}
	_, _, err = kv.CAS(p, nil)
	if err != nil {
		logrus.WithField("consul", "saveVolume").Error(err)
		panic(err)
	}

	return nil

}

func (d *rbdDriver) deleteVolume(name string) (error) {

	err, kv := getConnection()
	if err != nil {
		return err
	}

	_, err = kv.Delete(getKeyFromName(name), nil)
	if err != nil {
		logrus.WithField("consul", "deleteVolume").Error(err)
		return err
	}

	return nil
}

func (d *rbdDriver) getVolume(name string) (error, *Volume) {

	err, kv := getConnection()
	if err != nil {
		return err, nil
	}

	pair, _, err := kv.Get(getKeyFromName(name), nil)
	if err != nil {
		logrus.WithField("consul", "getVolume").Error(err)
		return err, nil
	}

	v := Volume{}

	if (pair) {
		logrus.WithField("consul.go", "getVolume").Debugf("pair: %s=%s ", pair.Key, pair.Value)

		if err := json.Unmarshal(pair.Value, &v); err != nil {
			logrus.WithField("consul", "getVolume").Error(err)
			return err, nil
		}
	}

	return nil, &v
}

func (d *rbdDriver) getVolumes() (error, *map[string]*Volume) {

	err, kv := getConnection()
	if err != nil {
		return err, nil
	}

	pairs, _, err := kv.List(getKeyFromName(""), nil)
	if err != nil {
		logrus.WithField("consul", "getVolumes").Error(err)
		return err, nil
	}

	volumes := map[string]*Volume{}

	for _, pair := range pairs {

		v := Volume{}

		if err := json.Unmarshal(pair.Value, &v); err != nil {
			logrus.WithField("consul", "getVolumes").Error(err)
			return err, nil
		}

		volumes[v.Name] = &v
	}

	return nil, &volumes
}

func getConnection() (error, *api.KV) {

	config := api.DefaultConfig()

	config.Address = os.Getenv("CONSUL_ADDRESS")

	if (config.Address == "") {
		config.Address = "localhost:8500"
	}

	client, err := api.NewClient(config)
	if err != nil {
		logrus.WithField("consul", "getConnection").Error(err)
		return err, nil
	}

	// Get a handle to the KV API
	return nil, client.KV()

}

func getKeyFromName(name string) string {
	return KEY_PREFIX + name
}
<|endoftext|>