<commit_before>package monit

import (
	"fmt"
	"github.com/tolexo/aero/conf"
	"github.com/tolexo/aero/panik"
	"log"
	"net/http"
	"net/http/httptest"
	"os"
	"strconv"
	"strings"
	"time"
)

type MonitorParams struct {
	ServiceId    string
	RespTime     float64
	ResponseCode int64
	CacheHit     bool
}

func MonitorMe(params MonitorParams) {
	if params.ServiceId != "" {
		dataDogAgent := GetDataDogAgent()

		dataDogAgent.Count("throughput", 1, nil, 1)
		dataDogAgent.Count(params.ServiceId, 1, nil, 1)

		dataDogAgent.Histogram("resptime", params.RespTime, nil, 1)
		dataDogAgent.Histogram(params.ServiceId+".resptime", params.RespTime, nil, 1)

		intervalTag := GetTimeIntervalTag(params.RespTime)
		dataDogAgent.Histogram("resptimeinterval", params.RespTime, intervalTag, 1)
		dataDogAgent.Histogram(params.ServiceId+".resptimeinterval", params.RespTime, intervalTag, 1)

		if params.CacheHit {
			dataDogAgent.Count("cachehit", 1, nil, 1)
			dataDogAgent.Count(params.ServiceId+".cachehit", 1, nil, 1)
			dataDogAgent.Histogram("resptime.c", params.RespTime, nil, 1)
			dataDogAgent.Histogram(params.ServiceId+".resptime.c", params.RespTime, nil, 1)
		} else {
			dataDogAgent.Histogram("resptime.nc", params.RespTime, nil, 1)
			dataDogAgent.Histogram(params.ServiceId+".resptime.nc", params.RespTime, nil, 1)

		}

		if params.ResponseCode > 0 {
			statusCode := FormatHttpStatusCode(int64(params.ResponseCode))
			dataDogAgent.Count(statusCode, 1, nil, 1)
			dataDogAgent.Count(params.ServiceId+"."+statusCode, 1, nil, 1)
		}

		enablelog := conf.Bool("monitor.enablelog", false)
		logTiming := conf.Float64("monitor.logTiming", 0.0)
		if enablelog || (logTiming > 0.0 && params.RespTime > logTiming) {
			logfile := conf.String("monitor.filelog", "")
			if logfile != "" {
				t := time.Now()
				logfileSuffix := fmt.Sprintf("%d-%d-%d", t.Month(), t.Day(), t.Hour())
				f, err := os.OpenFile(logfile+logfileSuffix+".csv", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
				panik.On(err)
				l := log.New(f, "", log.LstdFlags)
				l.Printf("%s %f %s %d %t", params.ServiceId, params.RespTime, intervalTag, params.ResponseCode, params.CacheHit)
			}
		}
	}
}

func NotFound(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(404)
	fmt.Fprintf(w, "%s", "4041 page not found")

	dataDogAgent := GetDataDogAgent()
	var statusCode int64 = 4041
	dataDogAgent.Count(FormatHttpStatusCode(statusCode), 1, nil, 1)
}

func FormatHttpStatusCode(httpStatusCode int64) string {
	return "http_" + strconv.FormatInt(httpStatusCode, 10)
}

func GetTimeIntervalTag(respTime float64) (ret []string) {
	interval := conf.String("monitor.interval", "")
	if interval != "" {
		intervalArr := strings.Split(interval, ",")
		var lowerLimit, higherLimit float64
		var matched bool
		for i := range intervalArr {
			higherLimit, _ = strconv.ParseFloat(intervalArr[i], 64)
			if respTime <= higherLimit {
				matched = true
				break
			} else {
				lowerLimit = higherLimit
			}
		}
		var intervalGroup string
		if matched == true {
			intervalGroup = "from_" + strconv.FormatInt(int64(lowerLimit), 10) + "_to_" + strconv.FormatInt(int64(higherLimit), 10)
		} else {
			intervalGroup = "above_" + strconv.FormatInt(int64(higherLimit), 10)
		}
		ret = append(ret, intervalGroup)
	}
	return
}

func ModRecorder() func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			start := time.Now()
			rec := httptest.NewRecorder()
			next.ServeHTTP(rec, r)

			for k, v := range rec.Header() {
				w.Header()[k] = v
			}

			len := rec.Body.Len()
			w.Header().Set("Content-Length", strconv.Itoa(len))
			w.WriteHeader(rec.Code)
			w.Write(rec.Body.Bytes())

			go func() {
				dur := time.Since(start).Seconds() * 1000
				intervalTag := GetTimeIntervalTag(float64(dur))
				statusCode := FormatHttpStatusCode(int64(rec.Code))

				dataDogAgent := GetDataDogAgent()
				dataDogAgent.Count("throughput", 1, nil, 1)
				dataDogAgent.Count(statusCode, 1, nil, 1)
				dataDogAgent.Histogram("resptime", dur, nil, 1)
				dataDogAgent.Histogram("resptimeinterval", dur, intervalTag, 1)
			}()

		})
	}
}
<commit_msg>CP-1086 logTiming changed to slowlogTiming<commit_after>package monit

import (
	"fmt"
	"github.com/tolexo/aero/conf"
	"github.com/tolexo/aero/panik"
	"log"
	"net/http"
	"net/http/httptest"
	"os"
	"strconv"
	"strings"
	"time"
)

type MonitorParams struct {
	ServiceId    string
	RespTime     float64
	ResponseCode int64
	CacheHit     bool
}

func MonitorMe(params MonitorParams) {
	if params.ServiceId != "" {
		dataDogAgent := GetDataDogAgent()

		dataDogAgent.Count("throughput", 1, nil, 1)
		dataDogAgent.Count(params.ServiceId, 1, nil, 1)

		dataDogAgent.Histogram("resptime", params.RespTime, nil, 1)
		dataDogAgent.Histogram(params.ServiceId+".resptime", params.RespTime, nil, 1)

		intervalTag := GetTimeIntervalTag(params.RespTime)
		dataDogAgent.Histogram("resptimeinterval", params.RespTime, intervalTag, 1)
		dataDogAgent.Histogram(params.ServiceId+".resptimeinterval", params.RespTime, intervalTag, 1)

		if params.CacheHit {
			dataDogAgent.Count("cachehit", 1, nil, 1)
			dataDogAgent.Count(params.ServiceId+".cachehit", 1, nil, 1)
			dataDogAgent.Histogram("resptime.c", params.RespTime, nil, 1)
			dataDogAgent.Histogram(params.ServiceId+".resptime.c", params.RespTime, nil, 1)
		} else {
			dataDogAgent.Histogram("resptime.nc", params.RespTime, nil, 1)
			dataDogAgent.Histogram(params.ServiceId+".resptime.nc", params.RespTime, nil, 1)

		}

		if params.ResponseCode > 0 {
			statusCode := FormatHttpStatusCode(int64(params.ResponseCode))
			dataDogAgent.Count(statusCode, 1, nil, 1)
			dataDogAgent.Count(params.ServiceId+"."+statusCode, 1, nil, 1)
		}

		enablelog := conf.Bool("monitor.enablelog", false)
		slowLogTiming := conf.Float64("monitor.slowLogTiming", 0.0)
		if enablelog || (slowLogTiming > 0.0 && params.RespTime > slowLogTiming) {
			logfile := conf.String("monitor.filelog", "")
			if logfile != "" {
				t := time.Now()
				logfileSuffix := fmt.Sprintf("%d-%d-%d", t.Month(), t.Day(), t.Hour())
				f, err := os.OpenFile(logfile+logfileSuffix+".csv", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
				panik.On(err)
				l := log.New(f, "", log.LstdFlags)
				l.Printf("%s %f %s %d %t", params.ServiceId, params.RespTime, intervalTag, params.ResponseCode, params.CacheHit)
			}
		}
	}
}

func NotFound(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(404)
	fmt.Fprintf(w, "%s", "4041 page not found")

	dataDogAgent := GetDataDogAgent()
	var statusCode int64 = 4041
	dataDogAgent.Count(FormatHttpStatusCode(statusCode), 1, nil, 1)
}

func FormatHttpStatusCode(httpStatusCode int64) string {
	return "http_" + strconv.FormatInt(httpStatusCode, 10)
}

func GetTimeIntervalTag(respTime float64) (ret []string) {
	interval := conf.String("monitor.interval", "")
	if interval != "" {
		intervalArr := strings.Split(interval, ",")
		var lowerLimit, higherLimit float64
		var matched bool
		for i := range intervalArr {
			higherLimit, _ = strconv.ParseFloat(intervalArr[i], 64)
			if respTime <= higherLimit {
				matched = true
				break
			} else {
				lowerLimit = higherLimit
			}
		}
		var intervalGroup string
		if matched == true {
			intervalGroup = "from_" + strconv.FormatInt(int64(lowerLimit), 10) + "_to_" + strconv.FormatInt(int64(higherLimit), 10)
		} else {
			intervalGroup = "above_" + strconv.FormatInt(int64(higherLimit), 10)
		}
		ret = append(ret, intervalGroup)
	}
	return
}

func ModRecorder() func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			start := time.Now()
			rec := httptest.NewRecorder()
			next.ServeHTTP(rec, r)

			for k, v := range rec.Header() {
				w.Header()[k] = v
			}

			len := rec.Body.Len()
			w.Header().Set("Content-Length", strconv.Itoa(len))
			w.WriteHeader(rec.Code)
			w.Write(rec.Body.Bytes())

			go func() {
				dur := time.Since(start).Seconds() * 1000
				intervalTag := GetTimeIntervalTag(float64(dur))
				statusCode := FormatHttpStatusCode(int64(rec.Code))

				dataDogAgent := GetDataDogAgent()
				dataDogAgent.Count("throughput", 1, nil, 1)
				dataDogAgent.Count(statusCode, 1, nil, 1)
				dataDogAgent.Histogram("resptime", dur, nil, 1)
				dataDogAgent.Histogram("resptimeinterval", dur, intervalTag, 1)
			}()

		})
	}
}
<|endoftext|>


<commit_before>package opt

import (
	"sexp"
)

// InlineCalls inlines suitable functions calls inside form.
func InlineCalls(form sexp.Form) sexp.Form {
	return sexp.Rewrite(form, func(form sexp.Form) sexp.Form {
		if form, ok := form.(*sexp.Call); ok {
			return inlineCall(form.Fn, form.Args)
		}
		return nil
	})
}

func inlineCall(fn *sexp.Func, args []sexp.Form) sexp.Form {
	body := fn.Body
	expr := inlineableForm(body)
	if expr == nil {
		return nil
	}

	bindings := make([]*sexp.Bind, 0, len(fn.Params))
	for i, param := range fn.Params {
		arg := InlineCalls(args[i])
		if sexp.Cost(arg) == 1 {
			expr = injectValue(param, arg, expr)
		} else if countUsages(param, expr) == 1 {
			expr = injectValue(param, arg, expr)
		} else {
			bindings = append(bindings, &sexp.Bind{
				Name: param,
				Init: arg,
			})
		}
	}

	if len(bindings) == 0 {
		// Perfect inlining, no let wrapper is needed.
		return expr
	}
	return &sexp.Let{
		Bindings: bindings,
		Expr:     expr,
	}
}

func inlineableForm(body *sexp.Block) sexp.Form {
	if len(body.Forms) == 0 {
		return nil
	}

	worthToInline := func(form sexp.Form) bool {
		return sexp.Cost(form) <= 15
	}

	switch form := body.Forms[0].(type) {
	case *sexp.Return:
		if len(form.Results) != 1 {
			return nil
		}
		if worthToInline(form.Results[0]) {
			form.Results[0].Copy()
		}

	case *sexp.ExprStmt:
		if !worthToInline(form.Expr) {
			return nil
		}
		// Sole statement is inlineable.
		if len(body.Forms) == 1 {
			return form.Expr.Copy()
		}
		// Trailing "return" is permitted (auto-inserted for void functions).
		if len(body.Forms) == 2 {
			ret, ok := body.Forms[1].(*sexp.Return)
			if ok && len(ret.Results) == 0 {
				return form.Expr.Copy()
			}
		}
	}

	return nil
}
<commit_msg>fix bug in inliner; reduce max inlineable form cost<commit_after>package opt

import (
	"sexp"
)

// InlineCalls inlines suitable functions calls inside form.
func InlineCalls(form sexp.Form) sexp.Form {
	return sexp.Rewrite(form, func(form sexp.Form) sexp.Form {
		if form, ok := form.(*sexp.Call); ok {
			return inlineCall(form.Fn, form.Args)
		}
		return nil
	})
}

func inlineCall(fn *sexp.Func, args []sexp.Form) sexp.Form {
	body := fn.Body
	expr := inlineableForm(body)
	if expr == nil {
		return nil
	}

	bindings := make([]*sexp.Bind, 0, len(fn.Params))
	for i, param := range fn.Params {
		arg := InlineCalls(args[i])
		if sexp.Cost(arg) == 1 {
			expr = injectValue(param, arg, expr)
		} else if countUsages(param, expr) == 1 {
			expr = injectValue(param, arg, expr)
		} else {
			bindings = append(bindings, &sexp.Bind{
				Name: param,
				Init: arg,
			})
		}
	}

	if len(bindings) == 0 {
		// Perfect inlining, no let wrapper is needed.
		return expr
	}
	return &sexp.Let{
		Bindings: bindings,
		Expr:     expr,
	}
}

func inlineableForm(body *sexp.Block) sexp.Form {
	if len(body.Forms) == 0 {
		return nil
	}

	worthToInline := func(form sexp.Form) bool {
		return sexp.Cost(form) <= 12
	}

	switch form := body.Forms[0].(type) {
	case *sexp.Return:
		if len(form.Results) != 1 {
			return nil
		}
		if worthToInline(form.Results[0]) {
			return form.Results[0].Copy()
		}

	case *sexp.ExprStmt:
		if !worthToInline(form.Expr) {
			return nil
		}
		// Sole statement is inlineable.
		if len(body.Forms) == 1 {
			return form.Expr.Copy()
		}
		// Trailing "return" is permitted (auto-inserted for void functions).
		if len(body.Forms) == 2 {
			ret, ok := body.Forms[1].(*sexp.Return)
			if ok && len(ret.Results) == 0 {
				return form.Expr.Copy()
			}
		}
	}

	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.

package strconv

import "errors"

// ErrRange indicates that a value is out of range for the target type.
var ErrRange = errors.New("value out of range")

// ErrSyntax indicates that a value does not have the right syntax for the target type.
var ErrSyntax = errors.New("invalid syntax")

// A NumError records a failed conversion.
type NumError struct {
	Func string // the failing function (ParseBool, ParseInt, ParseUint, ParseFloat)
	Num  string // the input
	Err  error  // the reason the conversion failed (ErrRange, ErrSyntax)
}

func (e *NumError) Error() string {
	return "strconv." + e.Func + ": " + "parsing " + Quote(e.Num) + ": " + e.Err.Error()
}

func syntaxError(fn, str string) *NumError {
	return &NumError{fn, str, ErrSyntax}
}

func rangeError(fn, str string) *NumError {
	return &NumError{fn, str, ErrRange}
}

const intSize = 32 << uint(^uint(0)>>63)

// IntSize is the size in bits of an int or uint value.
const IntSize = intSize

// Return the first number n such that n*base >= 1<<64.
func cutoff64(base int) uint64 {
	if base < 2 {
		return 0
	}
	return (1<<64-1)/uint64(base) + 1
}

// ParseUint is like ParseInt but for unsigned numbers.
func ParseUint(s string, base int, bitSize int) (n uint64, err error) {
	var cutoff, maxVal uint64

	if bitSize == 0 {
		bitSize = int(IntSize)
	}

	s0 := s
	switch {
	case len(s) < 1:
		err = ErrSyntax
		goto Error

	case 2 <= base && base <= 36:
		// valid base; nothing to do

	case base == 0:
		// Look for octal, hex prefix.
		switch {
		case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
			base = 16
			s = s[2:]
			if len(s) < 1 {
				err = ErrSyntax
				goto Error
			}
		case s[0] == '0':
			base = 8
		default:
			base = 10
		}

	default:
		err = errors.New("invalid base " + Itoa(base))
		goto Error
	}

	n = 0
	cutoff = cutoff64(base)
	maxVal = 1<<uint(bitSize) - 1

	for i := 0; i < len(s); i++ {
		var v byte
		d := s[i]
		switch {
		case '0' <= d && d <= '9':
			v = d - '0'
		case 'a' <= d && d <= 'z':
			v = d - 'a' + 10
		case 'A' <= d && d <= 'Z':
			v = d - 'A' + 10
		default:
			n = 0
			err = ErrSyntax
			goto Error
		}
		if int(v) >= base {
			n = 0
			err = ErrSyntax
			goto Error
		}

		if n >= cutoff {
			// n*base overflows
			n = 1<<64 - 1
			err = ErrRange
			goto Error
		}
		n *= uint64(base)

		n1 := n + uint64(v)
		if n1 < n || n1 > maxVal {
			// n+v overflows
			n = 1<<64 - 1
			err = ErrRange
			goto Error
		}
		n = n1
	}

	return n, nil

Error:
	return n, &NumError{"ParseUint", s0, err}
}

// ParseInt interprets a string s in the given base (2 to 36) and
// returns the corresponding value i.  If base == 0, the base is
// implied by the string's prefix: base 16 for "0x", base 8 for
// "0", and base 10 otherwise.
//
// The bitSize argument specifies the integer type
// that the result must fit into.  Bit sizes 0, 8, 16, 32, and 64
// correspond to int, int8, int16, int32, and int64.
//
// The errors that ParseInt returns have concrete type *NumError
// and include err.Num = s.  If s is empty or contains invalid
// digits, err.Err = ErrSyntax; if the value corresponding
// to s cannot be represented by a signed integer of the
// given size, err.Err = ErrRange.
func ParseInt(s string, base int, bitSize int) (i int64, err error) {
	const fnParseInt = "ParseInt"

	if bitSize == 0 {
		bitSize = int(IntSize)
	}

	// Empty string bad.
	if len(s) == 0 {
		return 0, syntaxError(fnParseInt, s)
	}

	// Pick off leading sign.
	s0 := s
	neg := false
	if s[0] == '+' {
		s = s[1:]
	} else if s[0] == '-' {
		neg = true
		s = s[1:]
	}

	// Convert unsigned and check range.
	var un uint64
	un, err = ParseUint(s, base, bitSize)
	if err != nil && err.(*NumError).Err != ErrRange {
		err.(*NumError).Func = fnParseInt
		err.(*NumError).Num = s0
		return 0, err
	}
	cutoff := uint64(1 << uint(bitSize-1))
	if !neg && un >= cutoff {
		return int64(cutoff - 1), rangeError(fnParseInt, s0)
	}
	if neg && un > cutoff {
		return -int64(cutoff), rangeError(fnParseInt, s0)
	}
	n := int64(un)
	if neg {
		n = -n
	}
	return n, nil
}

// Atoi is shorthand for ParseInt(s, 10, 0).
func Atoi(s string) (i int, err error) {
	i64, err := ParseInt(s, 10, 0)
	return int(i64), err
}
<commit_msg>strconv: document value returned by ParseInt when there is an error Documenting existing behavior; new commentary only. Fixes #7105.<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.

package strconv

import "errors"

// ErrRange indicates that a value is out of range for the target type.
var ErrRange = errors.New("value out of range")

// ErrSyntax indicates that a value does not have the right syntax for the target type.
var ErrSyntax = errors.New("invalid syntax")

// A NumError records a failed conversion.
type NumError struct {
	Func string // the failing function (ParseBool, ParseInt, ParseUint, ParseFloat)
	Num  string // the input
	Err  error  // the reason the conversion failed (ErrRange, ErrSyntax)
}

func (e *NumError) Error() string {
	return "strconv." + e.Func + ": " + "parsing " + Quote(e.Num) + ": " + e.Err.Error()
}

func syntaxError(fn, str string) *NumError {
	return &NumError{fn, str, ErrSyntax}
}

func rangeError(fn, str string) *NumError {
	return &NumError{fn, str, ErrRange}
}

const intSize = 32 << uint(^uint(0)>>63)

// IntSize is the size in bits of an int or uint value.
const IntSize = intSize

// Return the first number n such that n*base >= 1<<64.
func cutoff64(base int) uint64 {
	if base < 2 {
		return 0
	}
	return (1<<64-1)/uint64(base) + 1
}

// ParseUint is like ParseInt but for unsigned numbers.
func ParseUint(s string, base int, bitSize int) (n uint64, err error) {
	var cutoff, maxVal uint64

	if bitSize == 0 {
		bitSize = int(IntSize)
	}

	s0 := s
	switch {
	case len(s) < 1:
		err = ErrSyntax
		goto Error

	case 2 <= base && base <= 36:
		// valid base; nothing to do

	case base == 0:
		// Look for octal, hex prefix.
		switch {
		case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
			base = 16
			s = s[2:]
			if len(s) < 1 {
				err = ErrSyntax
				goto Error
			}
		case s[0] == '0':
			base = 8
		default:
			base = 10
		}

	default:
		err = errors.New("invalid base " + Itoa(base))
		goto Error
	}

	n = 0
	cutoff = cutoff64(base)
	maxVal = 1<<uint(bitSize) - 1

	for i := 0; i < len(s); i++ {
		var v byte
		d := s[i]
		switch {
		case '0' <= d && d <= '9':
			v = d - '0'
		case 'a' <= d && d <= 'z':
			v = d - 'a' + 10
		case 'A' <= d && d <= 'Z':
			v = d - 'A' + 10
		default:
			n = 0
			err = ErrSyntax
			goto Error
		}
		if int(v) >= base {
			n = 0
			err = ErrSyntax
			goto Error
		}

		if n >= cutoff {
			// n*base overflows
			n = 1<<64 - 1
			err = ErrRange
			goto Error
		}
		n *= uint64(base)

		n1 := n + uint64(v)
		if n1 < n || n1 > maxVal {
			// n+v overflows
			n = 1<<64 - 1
			err = ErrRange
			goto Error
		}
		n = n1
	}

	return n, nil

Error:
	return n, &NumError{"ParseUint", s0, err}
}

// ParseInt interprets a string s in the given base (2 to 36) and
// returns the corresponding value i.  If base == 0, the base is
// implied by the string's prefix: base 16 for "0x", base 8 for
// "0", and base 10 otherwise.
//
// The bitSize argument specifies the integer type
// that the result must fit into.  Bit sizes 0, 8, 16, 32, and 64
// correspond to int, int8, int16, int32, and int64.
//
// The errors that ParseInt returns have concrete type *NumError
// and include err.Num = s.  If s is empty or contains invalid
// digits, err.Err = ErrSyntax and the returned value is 0;
// if the value corresponding to s cannot be represented by a
// signed integer of the given size, err.Err = ErrRange and the
// returned value is the maximum magnitude integer of the
// appropriate bitSize and sign.
func ParseInt(s string, base int, bitSize int) (i int64, err error) {
	const fnParseInt = "ParseInt"

	if bitSize == 0 {
		bitSize = int(IntSize)
	}

	// Empty string bad.
	if len(s) == 0 {
		return 0, syntaxError(fnParseInt, s)
	}

	// Pick off leading sign.
	s0 := s
	neg := false
	if s[0] == '+' {
		s = s[1:]
	} else if s[0] == '-' {
		neg = true
		s = s[1:]
	}

	// Convert unsigned and check range.
	var un uint64
	un, err = ParseUint(s, base, bitSize)
	if err != nil && err.(*NumError).Err != ErrRange {
		err.(*NumError).Func = fnParseInt
		err.(*NumError).Num = s0
		return 0, err
	}
	cutoff := uint64(1 << uint(bitSize-1))
	if !neg && un >= cutoff {
		return int64(cutoff - 1), rangeError(fnParseInt, s0)
	}
	if neg && un > cutoff {
		return -int64(cutoff), rangeError(fnParseInt, s0)
	}
	n := int64(un)
	if neg {
		n = -n
	}
	return n, nil
}

// Atoi is shorthand for ParseInt(s, 10, 0).
func Atoi(s string) (i int, err error) {
	i64, err := ParseInt(s, 10, 0)
	return int(i64), err
}
<|endoftext|>


<commit_before>//Command to test application without deploy:
//goapp serve app.yaml
//Command to deploy/update application:
//goapp deploy -application golangnode0 -version 0

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"time"

	"appengine"
	"appengine/urlfetch"
)

//predefined parameters
var maxNodes int = 10
var isAliveCheckPeriod int = 500 //in millisecs

//changeable parameters
var statusContent string = "Default status"
var statusLog string = ""

//nodesStates := make(map[int]map[string]string)
/*
example for this map
var nodesStates map[int]map[string]string{
	1: map[string]string{
		"alive":"1",
		"hasTask":"true",
		"taskStatus":"completed",
		"taskResult":"some_result_for_node"
	},
}
*/

type webPage struct {
	Title string
}

type nodeStats struct {
	NodeID           int    `json:"ID"`
	NodeCount        int    `json:"nodeCount"`
	HasTask          bool   `json:"hasTask"`
	TaskStatus       string `json:"taskStatus"` //running-copleted-loaded
	TaskResult       string `json:"taskResult"`
	TaskFragmentBody string `json:"taskFragmentBody"`
	TaskBody         string `json:"taskBody"`
}

type echoMessage struct {
	Title   string `json:"title"`
	Content string `json:"content"`
}

//types for periodical functions
type pFunc func()

//wrong func for Google App Engine deployment. Need to use appengine libs...=(
func echo() {

	url := "http://golangappnode1.appspot.com/status"

	var jsonStr = []byte(`{"msg":"Hello!"}`)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}

	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	statusContent = string(body)

}

func helloWorld(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello World!")
}

func startPage(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		templatePage, _ := template.ParseFiles("start.html")
		templatePage.Execute(w, &webPage{"simplePage"})
	case "POST":
		r.ParseForm()
		//go echo()
		//fmt.Fprintf(w, "Successful read command/input from web-interface! Input contains - "+r.FormValue("nodeId")+" "+r.FormValue("echoContent"))
	}
}

func statusServer(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		fmt.Fprintf(w, "Get status - "+statusContent)
	case "POST":
		buf := new(bytes.Buffer)
		buf.ReadFrom(r.Body)
		newStr := buf.String()

		/*inputMsg := echoMessage{}
		err2 := json.NewDecoder(r.Body).Decode(&inputMsg)
		if err2 != nil {
			panic(err2)
		}*/

		thisNodeStats := nodeStats{
			1,
			2,
			false,
			"not running",
			"empty",
			"empty fragment",
			"empty",
		}

		jsonNodeStats, err1 := json.Marshal(thisNodeStats)
		if err1 != nil {
			panic(err1)
		}

		fmt.Fprintf(w, "Get data by params in POST - OK "+string(jsonNodeStats))
		//statusContent = "POST request handled, " + "Node id: " + string(nodeSends.id) + ", Echo content: " + nodeSends.content
		statusContent = "POST request handled, " + newStr //+ "Input message object content: " + inputMsg.Title + inputMsg.Content
	}
}

//Functions for isAlive checking realization
func checkIsAlive(nodeId int) {
	nodeUrl := "http://goappnode" + string(nodeId) + "0.appspot.com/"
	resp, err := http.Get(nodeUrl)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	if resp.StatusCode == 200 {
		statusLog += "Node #" + string(nodeId) + " - online"
	} else {
		statusLog += "Node #" + string(nodeId) + " - offline"
	}
}

func periodicTask(period time.Duration, task pFunc) {
	for {
		task()
		time.Sleep(period * time.Millisecond)
	}
}

/*
func checkAliveNodes(t time.Tick) {
	resp, err := http.Get("http://goappnode1.appspot.com/isalive")
	if err != nil {
		panic(err)
	}

}
*/

func isAliveServer(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, 1)
}

func checkAliveStart(w http.ResponseWriter, r *http.Request) {
	go periodicTask(30000, checkAlive(1))
}

/*
func checkAliveStop(w http.ResponseWriter, r *http.Request) {

}
*/

func testEcho(w http.ResponseWriter, r *http.Request) {
	msg := echoMessage{
		"Message is",
		"",
	}

	r.ParseForm()
	c := appengine.NewContext(r)
	msg.Content = r.FormValue("echoContent")

	jsonMessage, err2 := json.Marshal(msg)
	if err2 != nil {
		panic(err2)
	}

	//jsonStr := []byte(`{"message":"` + r.FormValue("echoContent") + `"}`)
	jsonStr := []byte(jsonMessage)
	buf := bytes.NewBuffer(jsonStr)
	client := http.Client{Transport: &urlfetch.Transport{Context: c}}
	resp, err := client.Post("http://goappnode"+r.FormValue("nodeId")+".appspot.com/status", "application/octet-stream", buf)
	if err != nil {
		statusContent = err.Error()
		fmt.Println(err)
	}
	respBody, _ := ioutil.ReadAll(resp.Body)
	statusContent = "Response from node - " + string(respBody)
}

func showInfo(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Information page for test project.")
	fmt.Fprintln(w, "Language - Go;")
	fmt.Fprintln(w, "Platform - Google Application Engine;")
}

func init() {
	//view pages
	http.HandleFunc("/", startPage)
	http.HandleFunc("/helloworld", helloWorld)
	http.HandleFunc("/showinfo", showInfo)
	//service pages
	http.HandleFunc("/echo", testEcho)
	http.HandleFunc("/status", statusServer)
	http.HandleFunc("/isalive", isAliveServer)
	http.HandleFunc("/startcheck", checkAliveStart)

	//Wrong code for App Enine - server cant understand what it need to show
	//http.ListenAndServe(":80", nil)
}

//this func not needed for deploy on Google App Engine, init() func replace main()
/*
func main() {
	//fmt.Println("Hello, test server started on 8080 port.\n - /helloworld - show title page\n - /showinfo - show information about this thing")
	//http.ListenAndServe(":8080", nil)
	go sender()
}*/
<commit_msg>fix...<commit_after>//Command to test application without deploy:
//goapp serve app.yaml
//Command to deploy/update application:
//goapp deploy -application golangnode0 -version 0

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"time"

	"appengine"
	"appengine/urlfetch"
)

//predefined parameters
var maxNodes int = 10
var isAliveCheckPeriod int = 500 //in millisecs

//changeable parameters
var statusContent string = "Default status"
var statusLog string = ""

//nodesStates := make(map[int]map[string]string)
/*
example for this map
var nodesStates map[int]map[string]string{
	1: map[string]string{
		"alive":"1",
		"hasTask":"true",
		"taskStatus":"completed",
		"taskResult":"some_result_for_node"
	},
}
*/

type webPage struct {
	Title string
}

type nodeStats struct {
	NodeID           int    `json:"ID"`
	NodeCount        int    `json:"nodeCount"`
	HasTask          bool   `json:"hasTask"`
	TaskStatus       string `json:"taskStatus"` //running-copleted-loaded
	TaskResult       string `json:"taskResult"`
	TaskFragmentBody string `json:"taskFragmentBody"`
	TaskBody         string `json:"taskBody"`
}

type echoMessage struct {
	Title   string `json:"title"`
	Content string `json:"content"`
}

//types for periodical functions
type pFunc func()

//wrong func for Google App Engine deployment. Need to use appengine libs...=(
func echo() {

	url := "http://golangappnode1.appspot.com/status"

	var jsonStr = []byte(`{"msg":"Hello!"}`)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}

	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	statusContent = string(body)

}

func helloWorld(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello World!")
}

func startPage(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		templatePage, _ := template.ParseFiles("start.html")
		templatePage.Execute(w, &webPage{"simplePage"})
	case "POST":
		r.ParseForm()
		//go echo()
		//fmt.Fprintf(w, "Successful read command/input from web-interface! Input contains - "+r.FormValue("nodeId")+" "+r.FormValue("echoContent"))
	}
}

func statusServer(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		fmt.Fprintf(w, "Get status - "+statusContent)
	case "POST":
		buf := new(bytes.Buffer)
		buf.ReadFrom(r.Body)
		newStr := buf.String()

		/*inputMsg := echoMessage{}
		err2 := json.NewDecoder(r.Body).Decode(&inputMsg)
		if err2 != nil {
			panic(err2)
		}*/

		thisNodeStats := nodeStats{
			1,
			2,
			false,
			"not running",
			"empty",
			"empty fragment",
			"empty",
		}

		jsonNodeStats, err1 := json.Marshal(thisNodeStats)
		if err1 != nil {
			panic(err1)
		}

		fmt.Fprintf(w, "Get data by params in POST - OK "+string(jsonNodeStats))
		//statusContent = "POST request handled, " + "Node id: " + string(nodeSends.id) + ", Echo content: " + nodeSends.content
		statusContent = "POST request handled, " + newStr //+ "Input message object content: " + inputMsg.Title + inputMsg.Content
	}
}

//Functions for isAlive checking realization
func checkIsAlive(nodeId int) {
	nodeUrl := "http://goappnode" + string(nodeId) + "0.appspot.com/"
	resp, err := http.Get(nodeUrl)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	if resp.StatusCode == 200 {
		statusLog += "Node #" + string(nodeId) + " - online"
	} else {
		statusLog += "Node #" + string(nodeId) + " - offline"
	}
}

func periodicTask(period time.Duration, task pFunc) {
	for {
		task()
		time.Sleep(period * time.Millisecond)
	}
}

/*
func checkAliveNodes(t time.Tick) {
	resp, err := http.Get("http://goappnode1.appspot.com/isalive")
	if err != nil {
		panic(err)
	}

}
*/

func isAliveServer(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, 1)
}

func checkAliveStart(w http.ResponseWriter, r *http.Request) {
	go periodicTask(30000, checkIsAlive(1))
}

/*
func checkAliveStop(w http.ResponseWriter, r *http.Request) {

}
*/

func testEcho(w http.ResponseWriter, r *http.Request) {
	msg := echoMessage{
		"Message is",
		"",
	}

	r.ParseForm()
	c := appengine.NewContext(r)
	msg.Content = r.FormValue("echoContent")

	jsonMessage, err2 := json.Marshal(msg)
	if err2 != nil {
		panic(err2)
	}

	//jsonStr := []byte(`{"message":"` + r.FormValue("echoContent") + `"}`)
	jsonStr := []byte(jsonMessage)
	buf := bytes.NewBuffer(jsonStr)
	client := http.Client{Transport: &urlfetch.Transport{Context: c}}
	resp, err := client.Post("http://goappnode"+r.FormValue("nodeId")+".appspot.com/status", "application/octet-stream", buf)
	if err != nil {
		statusContent = err.Error()
		fmt.Println(err)
	}
	respBody, _ := ioutil.ReadAll(resp.Body)
	statusContent = "Response from node - " + string(respBody)
}

func showInfo(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Information page for test project.")
	fmt.Fprintln(w, "Language - Go;")
	fmt.Fprintln(w, "Platform - Google Application Engine;")
}

func init() {
	//view pages
	http.HandleFunc("/", startPage)
	http.HandleFunc("/helloworld", helloWorld)
	http.HandleFunc("/showinfo", showInfo)
	//service pages
	http.HandleFunc("/echo", testEcho)
	http.HandleFunc("/status", statusServer)
	http.HandleFunc("/isalive", isAliveServer)
	http.HandleFunc("/startcheck", checkAliveStart)

	//Wrong code for App Enine - server cant understand what it need to show
	//http.ListenAndServe(":80", nil)
}

//this func not needed for deploy on Google App Engine, init() func replace main()
/*
func main() {
	//fmt.Println("Hello, test server started on 8080 port.\n - /helloworld - show title page\n - /showinfo - show information about this thing")
	//http.ListenAndServe(":8080", nil)
	go sender()
}*/
<|endoftext|>


<commit_before>package main

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

	"github.com/codegangsta/cli"
	"github.com/toorop/govh"
	"github.com/toorop/govh/ip"
)

// getIpCmds return commands for Ip section
func getIPCmds(OVHClient *govh.OVHClient) (cmds []cli.Command) {
	IPClient, err := ip.New(OVHClient)
	if err != nil {
		return
	}

	// Ip commands
	cmds = []cli.Command{
		// IPBLock
		{
			Name:        "block",
			Description: "commands concerning IP block",
			Subcommands: []cli.Command{
				// List Ip Blocks
				{
					Name:        "list",
					Description: "List your IP blocks.",
					Usage:       "ovh ip block list [flag...]" + NLTAB + "Example: ovh ip block list --type vps",
					Flags: []cli.Flag{
						cli.StringFlag{Name: "desc", Value: "", Usage: "Filter: by description (like)."},
						cli.StringFlag{Name: "ip", Value: "", Usage: "Filter: by IP (contains or equals)."},
						cli.StringFlag{Name: "routedTo", Value: "", Usage: "Filter: by routing."},
						cli.StringFlag{Name: "type", Value: "all", Usage: "Filter: by IP block type: all|cdn|dedicated|failover|hosted_ssl|housing|loadBalancing|mail|pcc|pci|private|vps|vpn|vrack|xdsl"},
						cli.BoolFlag{Name: "json", Usage: "output as JSON"},
					},
					Action: func(c *cli.Context) {
						fDesc := strings.ToLower(c.String("desc"))
						fIP := strings.ToLower(c.String("ip"))
						fRoutedTo := strings.ToLower(c.String("routedto"))
						fType := strings.ToLower(c.String("type"))
						if fType == "all" {
							fType = ""
						}
						IPBlocks, err := IPClient.List(fDesc, fIP, fRoutedTo, fType)
						dieOnError(err)
						// output as json ?
						if c.Bool("json") {
							buf, err := json.Marshal(IPBlocks)
							dieOnError(err)
							fmt.Println(string(buf))
						}
						for _, i := range IPBlocks {
							fmt.Println(i)
						}
						dieOk()
					},
				},
				// Get properties of a block
				{
					Name:        "properties",
					Description: "Get properties of an IP block.",
					Usage:       "ovh ip block properties IPBLOCK" + NLTAB + "Example: ovh ip block properties 91.121.228.135/32",
					Flags: []cli.Flag{
						cli.BoolFlag{Name: "json", Usage: "output as JSON"},
					},
					Action: func(c *cli.Context) {
						dieIfArgsMiss(len(c.Args()), 1)
						block := ip.IPBlock(c.Args().First())
						properties, err := IPClient.GetBlockProperties(block)
						dieOnError(err)
						if c.Bool("json") {
							buf, err := json.Marshal(properties)
							dieOnError(err)
							fmt.Println(string(buf))
						} else {
							fmt.Println(properties.String())
						}
						dieOk()
						//dieOk(fmt.Sprintf("IP: %s%sType: %s%sDescription: %s%sRouted to: %s", properties.Ip, NL, properties.Type, NL, properties.Description, NL, properties.RoutedTo.ServiceName))
					},
				},
			}, // end of block subCommands
		}, // end of ip subcommands

		/*
			// Update properties
			{
				Name:        "updateProperties",
				Usage:       "Update properties of an IP",
				Description: `ovh ip updateProperties IPBLOCK --desc "description"` + NLTAB + `Example: ovh ip updateProperties 37.187.0.144/32 --desc "IP routed to lunar base server"`,
				Flags: []cli.Flag{
					cli.StringFlag{"desc", "", "Update description", ""},
				},
				Action: func(c *cli.Context) {
					dieIfArgsMiss(len(c.Args()), 1)
					fDesc := c.String("desc")
					// check if there is something to update
					if len(fDesc) == 0 {
						dieDone()
					}
					err := ipr.UpdateProperties(c.Args().First(), fDesc)
					if err != nil {
						dieError(err)
					}
					dieDone()
				},
			},
			// Reverse
			{
				Name:        "reverse",
				Description: "commnands to interact with IP reverse",
				Subcommands: []cli.Command{
					{
						Name:        "get",
						Usage:       "Return the reverse of IP",
						Description: "ovh ip reverse XXX.XXX.XXX.XXX",
						Flags: []cli.Flag{
							cli.BoolFlag{Name: "json", Usage: "output as JSON"},
						},
						Action: func(c *cli.Context) {
							dieIfArgsMiss(len(c.Args()), 1)
							RIP, err := ipr.GetReverse(c.Args().First())
							if err != nil {
								dieError(err)
							}
							if c.Bool("json") {
								t, err := json.Marshal(RIP)
								if err != nil {
									dieError(err)
								}
								fmt.Println(string(t))
							} else {
								fmt.Println(RIP.String())
							}
							dieOk()
						},
					},
					{
						Name:        "set",
						Usage:       "Set the reverse of IP",
						Description: "ovh ip reverse set IP REVERSE",
						Action: func(c *cli.Context) {
							dieIfArgsMiss(len(c.Args()), 2)
							err := ipr.SetReverse(c.Args().First(), c.Args()[1])
							if err != nil {
								dieError(err)
							}
							dieOk()
						},
					},
				},
			},*/
	}
	return

}
<commit_msg>ip reverser && setreverse<commit_after>package main

import (
	"strings"

	"github.com/codegangsta/cli"
	"github.com/toorop/govh"
	"github.com/toorop/govh/ip"
)

// getIpCmds return commands for Ip section
func getIPCmds(OVHClient *govh.OVHClient) (cmds []cli.Command) {
	IPClient, err := ip.New(OVHClient)
	if err != nil {
		return
	}

	// Ip commands
	cmds = []cli.Command{
		// IP
		// Get IP reverse
		{
			Name:        "reverse",
			Description: "Returns the reverse of the IP",
			Usage:       "ovh ip reverse [--json]" + NLTAB + "Example: ovh ip reverse --json",
			Flags: []cli.Flag{
				cli.BoolFlag{Name: "json", Usage: "output as JSON"},
			},
			Action: func(c *cli.Context) {
				dieIfArgsMiss(len(c.Args()), 1)
				RIP, err := IPClient.GetReverse(c.Args().First())
				dieOnError(err)
				println(formatOutput(RIP, c.Bool("json")))
				dieOk()
			},
		}, {
			Name:        "setreverse",
			Description: "Update the reverse of the IP",
			Usage:       "ovh ip setreverse IP REVERSE [--json]" + NLTAB + "Example: ovh ip setreverse 8.8.8.8 www.ovh.com",
			Flags: []cli.Flag{
				cli.StringFlag{Name: "reverse", Value: "", Usage: "new reverse for IP"},
				cli.BoolFlag{Name: "json", Usage: "output as JSON"},
			},
			Action: func(c *cli.Context) {
				dieIfArgsMiss(len(c.Args()), 2)
				dieOnError(IPClient.SetReverse(c.Args().First(), c.Args()[1]))
			},
		},

		// IPBLock
		{
			Name:        "block",
			Description: "commands concerning IP block",
			Subcommands: []cli.Command{
				// List Ip Blocks
				{
					Name:        "list",
					Description: "List your IP blocks.",
					Usage:       "ovh ip block list [flag...]" + NLTAB + "Example: ovh ip block list --type vps",
					Flags: []cli.Flag{
						cli.StringFlag{Name: "desc", Value: "", Usage: "Filter: by description (like)."},
						cli.StringFlag{Name: "ip", Value: "", Usage: "Filter: by IP (contains or equals)."},
						cli.StringFlag{Name: "routedTo", Value: "", Usage: "Filter: by routing."},
						cli.StringFlag{Name: "type", Value: "all", Usage: "Filter: by IP block type: all|cdn|dedicated|failover|hosted_ssl|housing|loadBalancing|mail|pcc|pci|private|vps|vpn|vrack|xdsl"},
						cli.BoolFlag{Name: "json", Usage: "output as JSON"},
					},
					Action: func(c *cli.Context) {
						fDesc := strings.ToLower(c.String("desc"))
						fIP := strings.ToLower(c.String("ip"))
						fRoutedTo := strings.ToLower(c.String("routedto"))
						fType := strings.ToLower(c.String("type"))
						if fType == "all" {
							fType = ""
						}
						IPBlocks, err := IPClient.List(fDesc, fIP, fRoutedTo, fType)
						dieOnError(err)
						println(formatOutput(IPBlocks, c.Bool("json")))
						dieOk()
					},
				},
				// Get properties of a block
				{
					Name:        "properties",
					Description: "Get properties of an IP block.",
					Usage:       "ovh ip block properties IPBLOCK" + NLTAB + "Example: ovh ip block properties 91.121.228.135/32",
					Flags: []cli.Flag{
						cli.BoolFlag{Name: "json", Usage: "output as JSON"},
					},
					Action: func(c *cli.Context) {
						dieIfArgsMiss(len(c.Args()), 1)
						block := ip.IPBlock(c.Args().First())
						properties, err := IPClient.GetBlockProperties(block)
						dieOnError(err)
						println(formatOutput(properties, c.Bool("json")))
						dieOk()
					},
				},
				// Update properties of a block
				// for know tou can only update description
				{
					Name:        "updateproperties",
					Usage:       "Update properties of an IP Block",
					Description: `ovh ip block updateproperties IPBLOCK --desc "description"` + NLTAB + `Example: ovh ip block updateproperties 37.187.0.144/32 --desc "Block routed to lunar base server"`,
					Flags: []cli.Flag{
						cli.StringFlag{Name: "desc", Value: "", Usage: "Update block description"},
						cli.BoolFlag{Name: "json", Usage: "output as JSON"},
					},
					Action: func(c *cli.Context) {
						dieIfArgsMiss(len(c.Args()), 1)
						dieOnError(IPClient.UpdateBlockProperties(c.Args().First(), c.String("desc")))
						dieOk()
					},
				}, /*{
					Name:        "getreverses",
					Usage:       "Return the reverse of IP",
					Description: "ovh ip reverse XXX.XXX.XXX.XXX",
					Flags: []cli.Flag{
						cli.BoolFlag{Name: "json", Usage: "output as JSON"},
					},
					Action: func(c *cli.Context) {
						dieIfArgsMiss(len(c.Args()), 1)
						RIP, err := IPClient.GetReverse(c.Args().First())
						if err != nil {
							dieError(err)
						}
						if c.Bool("json") {
							t, err := json.Marshal(RIP)
							if err != nil {
								dieError(err)
							}
							fmt.Println(string(t))
						} else {
							fmt.Println(RIP.String())
						}
						dieOk()
					},
				},*/
			},
		}, // end of block subCommands

		/*
			// Reverse
			{
				Name:        "reverse",
				Description: "commnands to interact with IP reverse",
				Subcommands: []cli.Command{
					{
						Name:        "get",
						Usage:       "Return the reverse of IP",
						Description: "ovh ip reverse XXX.XXX.XXX.XXX",
						Flags: []cli.Flag{
							cli.BoolFlag{Name: "json", Usage: "output as JSON"},
						},
						Action: func(c *cli.Context) {
							dieIfArgsMiss(len(c.Args()), 1)
							RIP, err := IPClient.GetReverse(c.Args().First())
							if err != nil {
								dieError(err)
							}
							if c.Bool("json") {
								t, err := json.Marshal(RIP)
								if err != nil {
									dieError(err)
								}
								fmt.Println(string(t))
							} else {
								fmt.Println(RIP.String())
							}
							dieOk()
						},
					},
					{
						Name:        "set",
						Usage:       "Set the reverse of IP",
						Description: "ovh ip reverse set IP REVERSE",
						Action: func(c *cli.Context) {
							dieIfArgsMiss(len(c.Args()), 2)
							err := ipr.SetReverse(c.Args().First(), c.Args()[1])
							if err != nil {
								dieError(err)
							}
							dieOk()
						},
					},
				},
			},*/
	}
	return
}
<|endoftext|>


<commit_before>package manet

import (
	"bytes"

	ma "github.com/multiformats/go-multiaddr"
)

// Loopback Addresses
var (
	// IP4Loopback is the ip4 loopback multiaddr
	IP4Loopback = ma.StringCast("/ip4/127.0.0.1")

	// IP6Loopback is the ip6 loopback multiaddr
	IP6Loopback = ma.StringCast("/ip6/::1")

	// IP6LinkLocalLoopback is the ip6 link-local loopback multiaddr
	IP6LinkLocalLoopback = ma.StringCast("/ip6/fe80::1")
)

// Unspecified Addresses (used for )
var (
	IP4Unspecified = ma.StringCast("/ip4/0.0.0.0")
	IP6Unspecified = ma.StringCast("/ip6/::")
)

// IsThinWaist returns whether a Multiaddr starts with "Thin Waist" Protocols.
// This means: /{IP4, IP6}[/{TCP, UDP}]
func IsThinWaist(m ma.Multiaddr) bool {
	p := m.Protocols()

	// nothing? not even a waist.
	if len(p) == 0 {
		return false
	}

	if p[0].Code != ma.P_IP4 && p[0].Code != ma.P_IP6 {
		return false
	}

	// only IP? still counts.
	if len(p) == 1 {
		return true
	}

	switch p[1].Code {
	case ma.P_TCP, ma.P_UDP, ma.P_IP4, ma.P_IP6:
		return true
	default:
		return false
	}
}

// IsIPLoopback returns whether a Multiaddr is a "Loopback" IP address
// This means either /ip4/127.0.0.1 or /ip6/::1
// TODO: differentiate IsIPLoopback and OverIPLoopback
func IsIPLoopback(m ma.Multiaddr) bool {
	b := m.Bytes()

	// /ip4/127 prefix (_entire_ /8 is loopback...)
	if bytes.HasPrefix(b, []byte{ma.P_IP4, 127}) {
		return true
	}

	// /ip6/::1
	if !m.Decapsulate(IP6Loopback).Equal(m) || !m.Decapsulate(IP6LinkLocalLoopback).Equal(m) {
		return true
	}

	return false
}

// IsIP6LinkLocal returns if a multiaddress is an IPv6 local link. These
// addresses are non routable. The prefix is technically
// fe80::/10, but we test fe80::/16 for simplicity (no need to mask).
// So far, no hardware interfaces exist long enough to use those 2 bits.
// Send a PR if there is.
func IsIP6LinkLocal(m ma.Multiaddr) bool {
	return bytes.HasPrefix(m.Bytes(), []byte{ma.P_IP6, 0xfe, 0x80})
}

// IsIPUnspecified returns whether a Multiaddr is am Unspecified IP address
// This means either /ip4/0.0.0.0 or /ip6/::
func IsIPUnspecified(m ma.Multiaddr) bool {
	return IP4Unspecified.Equal(m) || IP6Unspecified.Equal(m)
}
<commit_msg>Make the IP loopback check faster<commit_after>package manet

import (
	"bytes"

	ma "github.com/multiformats/go-multiaddr"
)

// Loopback Addresses
var (
	// IP4Loopback is the ip4 loopback multiaddr
	IP4Loopback = ma.StringCast("/ip4/127.0.0.1")

	// IP6Loopback is the ip6 loopback multiaddr
	IP6Loopback = ma.StringCast("/ip6/::1")

	// IP6LinkLocalLoopback is the ip6 link-local loopback multiaddr
	IP6LinkLocalLoopback = ma.StringCast("/ip6/fe80::1")
)

// Unspecified Addresses (used for )
var (
	IP4Unspecified = ma.StringCast("/ip4/0.0.0.0")
	IP6Unspecified = ma.StringCast("/ip6/::")
)

// IsThinWaist returns whether a Multiaddr starts with "Thin Waist" Protocols.
// This means: /{IP4, IP6}[/{TCP, UDP}]
func IsThinWaist(m ma.Multiaddr) bool {
	p := m.Protocols()

	// nothing? not even a waist.
	if len(p) == 0 {
		return false
	}

	if p[0].Code != ma.P_IP4 && p[0].Code != ma.P_IP6 {
		return false
	}

	// only IP? still counts.
	if len(p) == 1 {
		return true
	}

	switch p[1].Code {
	case ma.P_TCP, ma.P_UDP, ma.P_IP4, ma.P_IP6:
		return true
	default:
		return false
	}
}

var localPrefixes = [][]byte{
	{ma.P_IP4, 127}, // 127.*
	IP6LinkLocalLoopback.Bytes(),
	IP6Loopback.Bytes(),
}

// IsIPLoopback returns whether a Multiaddr is a "Loopback" IP address
// This means either /ip4/127.*.*.*, /ip6/::1, or /ip6/fe80::1
func IsIPLoopback(m ma.Multiaddr) bool {
	b := m.Bytes()
	for _, prefix := range localPrefixes {
		if bytes.HasPrefix(b, prefix) {
			return true
		}
	}
	return false
}

// IsIP6LinkLocal returns if a multiaddress is an IPv6 local link. These
// addresses are non routable. The prefix is technically
// fe80::/10, but we test fe80::/16 for simplicity (no need to mask).
// So far, no hardware interfaces exist long enough to use those 2 bits.
// Send a PR if there is.
func IsIP6LinkLocal(m ma.Multiaddr) bool {
	return bytes.HasPrefix(m.Bytes(), []byte{ma.P_IP6, 0xfe, 0x80})
}

// IsIPUnspecified returns whether a Multiaddr is am Unspecified IP address
// This means either /ip4/0.0.0.0 or /ip6/::
func IsIPUnspecified(m ma.Multiaddr) bool {
	return IP4Unspecified.Equal(m) || IP6Unspecified.Equal(m)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"

	"github.com/jmespath/jp/Godeps/_workspace/src/github.com/codegangsta/cli"
	"github.com/jmespath/jp/Godeps/_workspace/src/github.com/jmespath/go-jmespath"
)

const version = "0.1.1"

func main() {
	app := cli.NewApp()
	app.Name = "jp"
	app.Version = version
	app.Usage = "jp [<options>] <expression>"
	app.Author = ""
	app.Email = ""
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "filename, f",
			Usage: "Read input JSON from a file instead of stdin.",
		},
		cli.StringFlag{
			Name:  "expr-file, e",
			Usage: "Read JMESPath expression from the specified file.",
		},
		cli.BoolFlag{
			Name:   "unquoted, u",
			Usage:  "If the final result is a string, it will be printed without quotes.",
			EnvVar: "JP_UNQUOTED",
		},
		cli.BoolFlag{
			Name:  "ast",
			Usage: "Only print the AST of the parsed expression.  Do not rely on this output, only useful for debugging purposes.",
		},
	}
	app.Action = runMainAndExit

	app.Run(os.Args)
}

func runMainAndExit(c *cli.Context) {
	os.Exit(runMain(c))
}

func errMsg(msg string, a ...interface{}) int {
	fmt.Fprintf(os.Stderr, msg, a...)
	fmt.Fprintln(os.Stderr)
	return 1
}

func runMain(c *cli.Context) int {
	var expression string
	if c.String("expr-file") != "" {
		byteExpr, err := ioutil.ReadFile(c.String("expr-file"))
		expression = string(byteExpr)
		if err != nil {
			return errMsg("Error opening expression file: %s", err)
		}
	} else {
		if len(c.Args()) == 0 {
			return errMsg("Must provide at least one argument.")
		}
		expression = c.Args()[0]
	}
	if c.Bool("ast") {
		parser := jmespath.NewParser()
		parsed, err := parser.Parse(expression)
		if err != nil {
			if syntaxError, ok := err.(jmespath.SyntaxError); ok {
				return errMsg("%s\n%s\n",
					syntaxError,
					syntaxError.HighlightLocation())
			}
			return errMsg("%s", err)
		}
		fmt.Println("")
		fmt.Printf("%s\n", parsed)
		return 0
	}
	var input interface{}
	var jsonParser *json.Decoder
	if c.String("filename") != "" {
		f, err := os.Open(c.String("filename"))
		if err != nil {
			return errMsg("Error opening input file: %s", err)
		}
		jsonParser = json.NewDecoder(f)

	} else {
		jsonParser = json.NewDecoder(os.Stdin)
	}
	if err := jsonParser.Decode(&input); err != nil {
		errMsg("Error parsing input json: %s\n", err)
		return 2
	}
	result, err := jmespath.Search(expression, input)
	if err != nil {
		if syntaxError, ok := err.(jmespath.SyntaxError); ok {
			return errMsg("%s\n%s\n",
				syntaxError,
				syntaxError.HighlightLocation())
		}
		return errMsg("Error evaluating JMESPath expression: %s", err)
	}
	converted, isString := result.(string)
	if c.Bool("unquoted") && isString {
		os.Stdout.WriteString(converted)
	} else {
		toJSON, err := json.MarshalIndent(result, "", "  ")
		if err != nil {
			errMsg("Error marshalling result to JSON: %s\n", err)
			return 3
		}
		os.Stdout.Write(toJSON)
	}
	os.Stdout.WriteString("\n")
	return 0
}
<commit_msg>Bumping version to 0.1.2<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"

	"github.com/jmespath/jp/Godeps/_workspace/src/github.com/codegangsta/cli"
	"github.com/jmespath/jp/Godeps/_workspace/src/github.com/jmespath/go-jmespath"
)

const version = "0.1.2"

func main() {
	app := cli.NewApp()
	app.Name = "jp"
	app.Version = version
	app.Usage = "jp [<options>] <expression>"
	app.Author = ""
	app.Email = ""
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "filename, f",
			Usage: "Read input JSON from a file instead of stdin.",
		},
		cli.StringFlag{
			Name:  "expr-file, e",
			Usage: "Read JMESPath expression from the specified file.",
		},
		cli.BoolFlag{
			Name:   "unquoted, u",
			Usage:  "If the final result is a string, it will be printed without quotes.",
			EnvVar: "JP_UNQUOTED",
		},
		cli.BoolFlag{
			Name:  "ast",
			Usage: "Only print the AST of the parsed expression.  Do not rely on this output, only useful for debugging purposes.",
		},
	}
	app.Action = runMainAndExit

	app.Run(os.Args)
}

func runMainAndExit(c *cli.Context) {
	os.Exit(runMain(c))
}

func errMsg(msg string, a ...interface{}) int {
	fmt.Fprintf(os.Stderr, msg, a...)
	fmt.Fprintln(os.Stderr)
	return 1
}

func runMain(c *cli.Context) int {
	var expression string
	if c.String("expr-file") != "" {
		byteExpr, err := ioutil.ReadFile(c.String("expr-file"))
		expression = string(byteExpr)
		if err != nil {
			return errMsg("Error opening expression file: %s", err)
		}
	} else {
		if len(c.Args()) == 0 {
			return errMsg("Must provide at least one argument.")
		}
		expression = c.Args()[0]
	}
	if c.Bool("ast") {
		parser := jmespath.NewParser()
		parsed, err := parser.Parse(expression)
		if err != nil {
			if syntaxError, ok := err.(jmespath.SyntaxError); ok {
				return errMsg("%s\n%s\n",
					syntaxError,
					syntaxError.HighlightLocation())
			}
			return errMsg("%s", err)
		}
		fmt.Println("")
		fmt.Printf("%s\n", parsed)
		return 0
	}
	var input interface{}
	var jsonParser *json.Decoder
	if c.String("filename") != "" {
		f, err := os.Open(c.String("filename"))
		if err != nil {
			return errMsg("Error opening input file: %s", err)
		}
		jsonParser = json.NewDecoder(f)

	} else {
		jsonParser = json.NewDecoder(os.Stdin)
	}
	if err := jsonParser.Decode(&input); err != nil {
		errMsg("Error parsing input json: %s\n", err)
		return 2
	}
	result, err := jmespath.Search(expression, input)
	if err != nil {
		if syntaxError, ok := err.(jmespath.SyntaxError); ok {
			return errMsg("%s\n%s\n",
				syntaxError,
				syntaxError.HighlightLocation())
		}
		return errMsg("Error evaluating JMESPath expression: %s", err)
	}
	converted, isString := result.(string)
	if c.Bool("unquoted") && isString {
		os.Stdout.WriteString(converted)
	} else {
		toJSON, err := json.MarshalIndent(result, "", "  ")
		if err != nil {
			errMsg("Error marshalling result to JSON: %s\n", err)
			return 3
		}
		os.Stdout.Write(toJSON)
	}
	os.Stdout.WriteString("\n")
	return 0
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"runtime"
	"strings"
	"sync"
	"syscall"

	"github.com/cubicdaiya/nginx-build/builder"
	"github.com/cubicdaiya/nginx-build/command"
	"github.com/cubicdaiya/nginx-build/configure"
	"github.com/cubicdaiya/nginx-build/module3rd"
	"github.com/cubicdaiya/nginx-build/openssl"
	"github.com/cubicdaiya/nginx-build/util"
)

const (
	NginxBuildVersion = "0.9.3"
)

var (
	nginxBuildOptions Options
)

func init() {
	nginxBuildOptions = makeNginxBuildOptions()
}

func main() {
	// Parse flags
	for k, v := range nginxBuildOptions.Bools {
		v.Enabled = flag.Bool(k, false, v.Desc)
		nginxBuildOptions.Bools[k] = v
	}
	for k, v := range nginxBuildOptions.Values {
		v.Value = flag.String(k, v.Default, v.Desc)
		nginxBuildOptions.Values[k] = v
	}
	for k, v := range nginxBuildOptions.Numbers {
		v.Value = flag.Int(k, v.Default, v.Desc)
		nginxBuildOptions.Numbers[k] = v
	}

	// fake flag for --with-xxx=dynamic
	for i, arg := range os.Args {
		if strings.Contains(arg, "with-http_xslt_module=dynamic") {
			os.Args[i] = "--with-http_xslt_module_dynamic"
		}
		if strings.Contains(arg, "with-http_image_filter_module=dynamic") {
			os.Args[i] = "--with-http_image_filter_module_dynamic"
		}
		if strings.Contains(arg, "with-http_geoip_module=dynamic") {
			os.Args[i] = "--with-http_geoip_module_dynamic"
		}
		if strings.Contains(arg, "with-http_perl_module=dynamic") {
			os.Args[i] = "--with-http_perl_module_dynamic"
		}
		if strings.Contains(arg, "with-mail=dynamic") {
			os.Args[i] = "--with-mail_dynamic"
		}
		if strings.Contains(arg, "with-stream=dynamic") {
			os.Args[i] = "--with-stream_dynamic"
		}
	}

	var (
		configureOptions configure.Options
		multiflag        StringFlag
		multiflagDynamic StringFlag
	)

	argsString := configure.MakeArgsString()
	for k, v := range argsString {
		if k == "add-module" {
			flag.Var(&multiflag, k, v.Desc)
		} else if k == "add-dynamic-module" {
			flag.Var(&multiflagDynamic, k, v.Desc)
		} else {
			v.Value = flag.String(k, "", v.Desc)
			argsString[k] = v
		}
	}

	argsBool := configure.MakeArgsBool()
	for k, v := range argsBool {
		v.Enabled = flag.Bool(k, false, v.Desc)
		argsBool[k] = v
	}

	flag.CommandLine.SetOutput(os.Stdout)
	// The output of original flag.Usage() is too long
	defaultUsage := flag.Usage
	flag.Usage = usage
	flag.Parse()

	jobs := nginxBuildOptions.Numbers["j"].Value

	verbose := nginxBuildOptions.Bools["verbose"].Enabled
	pcreStatic := nginxBuildOptions.Bools["pcre"].Enabled
	openSSLStatic := nginxBuildOptions.Bools["openssl"].Enabled
	zlibStatic := nginxBuildOptions.Bools["zlib"].Enabled
	clear := nginxBuildOptions.Bools["clear"].Enabled
	versionPrint := nginxBuildOptions.Bools["version"].Enabled
	versionsPrint := nginxBuildOptions.Bools["versions"].Enabled
	openResty := nginxBuildOptions.Bools["openresty"].Enabled
	tengine := nginxBuildOptions.Bools["tengine"].Enabled
	configureOnly := nginxBuildOptions.Bools["configureonly"].Enabled
	helpAll := nginxBuildOptions.Bools["help-all"].Enabled

	version := nginxBuildOptions.Values["v"].Value
	nginxConfigurePath := nginxBuildOptions.Values["c"].Value
	modulesConfPath := nginxBuildOptions.Values["m"].Value
	workParentDir := nginxBuildOptions.Values["d"].Value
	pcreVersion := nginxBuildOptions.Values["pcreversion"].Value
	openSSLVersion := nginxBuildOptions.Values["opensslversion"].Value
	zlibVersion := nginxBuildOptions.Values["zlibversion"].Value
	openRestyVersion := nginxBuildOptions.Values["openrestyversion"].Value
	tengineVersion := nginxBuildOptions.Values["tengineversion"].Value
	patchPath := nginxBuildOptions.Values["patch"].Value
	patchOption := nginxBuildOptions.Values["patch-opt"].Value

	// Allow multiple flags for `--add-module`
	{
		tmp := argsString["add-module"]
		tmp_ := multiflag.String()
		tmp.Value = &tmp_
		argsString["add-module"] = tmp
	}

	// Allow multiple flags for `--add-dynamic-module`
	{
		tmp := argsString["add-dynamic-module"]
		tmp_ := multiflagDynamic.String()
		tmp.Value = &tmp_
		argsString["add-dynamic-module"] = tmp
	}

	configureOptions.Values = argsString
	configureOptions.Bools = argsBool

	if *helpAll {
		defaultUsage()
		return
	}

	if *versionPrint {
		printNginxBuildVersion()
		return
	}

	if *versionsPrint {
		printNginxVersions()
		return
	}

	printFirstMsg()

	// set verbose mode
	command.VerboseEnabled = *verbose

	var nginxBuilder builder.Builder
	if *openResty && *tengine {
		log.Fatal("select one between '-openresty' and '-tengine'.")
	}
	if *openResty {
		nginxBuilder = builder.MakeBuilder(builder.COMPONENT_OPENRESTY, *openRestyVersion)
	} else if *tengine {
		nginxBuilder = builder.MakeBuilder(builder.COMPONENT_TENGINE, *tengineVersion)
	} else {
		nginxBuilder = builder.MakeBuilder(builder.COMPONENT_NGINX, *version)
	}
	pcreBuilder := builder.MakeBuilder(builder.COMPONENT_PCRE, *pcreVersion)
	openSSLBuilder := builder.MakeBuilder(builder.COMPONENT_OPENSSL, *openSSLVersion)
	zlibBuilder := builder.MakeBuilder(builder.COMPONENT_ZLIB, *zlibVersion)

	// change default umask
	_ = syscall.Umask(0)

	versionCheck(*version)

	nginxConfigure, err := util.FileGetContents(*nginxConfigurePath)
	if err != nil {
		log.Fatal(err)
	}
	nginxConfigure = configure.Normalize(nginxConfigure)

	modules3rd, err := module3rd.Load(*modulesConfPath)
	if err != nil {
		log.Fatal(err)
	}

	if len(*workParentDir) == 0 {
		log.Fatal("set working directory with -d")
	}

	if !util.FileExists(*workParentDir) {
		err := os.Mkdir(*workParentDir, 0755)
		if err != nil {
			log.Fatalf("Failed to create working directory(%s) does not exist.", *workParentDir)
		}
	}

	var workDir string
	if *openResty {
		workDir = *workParentDir + "/openresty/" + *openRestyVersion
	} else if *tengine {
		workDir = *workParentDir + "/tengine/" + *tengineVersion
	} else {
		workDir = *workParentDir + "/nginx/" + *version
	}

	if *clear {
		err := util.ClearWorkDir(workDir)
		if err != nil {
			log.Fatal(err)
		}
	}

	if !util.FileExists(workDir) {
		err := os.MkdirAll(workDir, 0755)
		if err != nil {
			log.Fatalf("Failed to create working directory(%s) does not exist.", workDir)
		}
	}

	rootDir := util.SaveCurrentDir()
	err = os.Chdir(workDir)
	if err != nil {
		log.Fatal(err)
	}

	// remove nginx source code applyed patch
	if *patchPath != "" && util.FileExists(nginxBuilder.SourcePath()) {
		err := os.RemoveAll(nginxBuilder.SourcePath())
		if err != nil {
			log.Fatal(err)
		}
	}

	wg := new(sync.WaitGroup)
	if *pcreStatic {
		wg.Add(1)
		go func() {
			downloadAndExtractParallel(&pcreBuilder)
			wg.Done()
		}()
	}

	if *openSSLStatic {
		wg.Add(1)
		go func () {
			downloadAndExtractParallel(&openSSLBuilder)
			wg.Done()
		}()
	}

	if *zlibStatic {
		wg.Add(1)
		go func () {
			downloadAndExtractParallel(&zlibBuilder)
			wg.Done()
		}()
	}

	wg.Add(1)
	go func() {
		downloadAndExtractParallel(&nginxBuilder)
		wg.Done()
	}()

	if len(modules3rd) > 0 {
		wg.Add(len(modules3rd))
		for _, m := range modules3rd {
			go func() {
				module3rd.DownloadAndExtractParallel(m)
				wg.Done()
			}()
		}

	}

	// wait until all downloading processes by goroutine finish
	wg.Wait()

	if len(modules3rd) > 0 {
		for _, m := range modules3rd {
			if err := module3rd.Provide(&m); err != nil {
				log.Fatal(err)
			}
		}
	}

	// cd workDir/nginx-${version}
	os.Chdir(nginxBuilder.SourcePath())

	var dependencies []builder.StaticLibrary
	if *pcreStatic {
		dependencies = append(dependencies, builder.MakeStaticLibrary(&pcreBuilder))
	}

	if *openSSLStatic {
		dependencies = append(dependencies, builder.MakeStaticLibrary(&openSSLBuilder))
	}

	if *zlibStatic {
		dependencies = append(dependencies, builder.MakeStaticLibrary(&zlibBuilder))
	}

	log.Printf("Generate configure script for %s.....", nginxBuilder.SourcePath())

	if *pcreStatic && pcreBuilder.IsIncludeWithOption(nginxConfigure) {
		log.Println(pcreBuilder.WarnMsgWithLibrary())
	}

	if *openSSLStatic && openSSLBuilder.IsIncludeWithOption(nginxConfigure) {
		log.Println(openSSLBuilder.WarnMsgWithLibrary())

	}

	if *zlibStatic && zlibBuilder.IsIncludeWithOption(nginxConfigure) {
		log.Println(zlibBuilder.WarnMsgWithLibrary())
	}

	configureScript := configure.Generate(nginxConfigure, modules3rd, dependencies, configureOptions, rootDir, *openResty, *jobs)

	err = ioutil.WriteFile("./nginx-configure", []byte(configureScript), 0655)
	if err != nil {
		log.Fatalf("Failed to generate configure script for %s", nginxBuilder.SourcePath())
	}

	util.Patch(*patchPath, *patchOption, rootDir, false)

	// reverts source code with patch -R when the build was interrupted.
	if *patchPath != "" {
		sigChannel := make(chan os.Signal, 1)
		signal.Notify(sigChannel, os.Interrupt)
		go func() {
			<-sigChannel
			util.Patch(*patchPath, *patchOption, rootDir, true)
		}()
	}

	log.Printf("Configure %s.....", nginxBuilder.SourcePath())

	err = configure.Run()
	if err != nil {
		log.Printf("Failed to configure %s\n", nginxBuilder.SourcePath())
		util.Patch(*patchPath, *patchOption, rootDir, true)
		util.PrintFatalMsg(err, "nginx-configure.log")
	}

	if *configureOnly {
		util.Patch(*patchPath, *patchOption, rootDir, true)
		printLastMsg(workDir, nginxBuilder.SourcePath(), *openResty, *configureOnly)
		return
	}

	log.Printf("Build %s.....", nginxBuilder.SourcePath())

	if *openSSLStatic {
		// Workarounds for protecting a failure of building nginx with static-linked OpenSSL.

		// The build of old OpenSSL fails when multi-CPUs are used.
		// It is fixed in the commit below.
		//
		// https://github.com/openssl/openssl/commit/8e6bb99979b95ee8b878e22e043ceb78d79c32a1
		//
		// And backported to 1.0.1p and 1.0.2d.
		if !openssl.ParallelBuildAvailable(*openSSLVersion) {
			*jobs = 1
		}

		// Sometimes machine hardware name('uname -m') is different
		// from machine processor architecture name('uname -p') on Mac.
		// Specifically, `uname -p` is 'i386' and `uname -m` is 'x86_64'.
		// In this case, a build of OpenSSL fails.
		// So it needs to convince OpenSSL with KERNEL_BITS.
		if runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" {
			os.Setenv("KERNEL_BITS", "64")
		}
	}

	err = builder.BuildNginx(*jobs)
	if err != nil {
		log.Printf("Failed to build %s\n", nginxBuilder.SourcePath())
		util.Patch(*patchPath, *patchOption, rootDir, true)
		util.PrintFatalMsg(err, "nginx-build.log")
	}

	printLastMsg(workDir, nginxBuilder.SourcePath(), *openResty, *configureOnly)

	// cd rootDir
	os.Chdir(rootDir)
}
<commit_msg>bugfix: fixed race condition for the loop with goroutine.<commit_after>package main

import (
	"flag"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"runtime"
	"strings"
	"sync"
	"syscall"

	"github.com/cubicdaiya/nginx-build/builder"
	"github.com/cubicdaiya/nginx-build/command"
	"github.com/cubicdaiya/nginx-build/configure"
	"github.com/cubicdaiya/nginx-build/module3rd"
	"github.com/cubicdaiya/nginx-build/openssl"
	"github.com/cubicdaiya/nginx-build/util"
)

const (
	NginxBuildVersion = "0.9.3"
)

var (
	nginxBuildOptions Options
)

func init() {
	nginxBuildOptions = makeNginxBuildOptions()
}

func main() {
	// Parse flags
	for k, v := range nginxBuildOptions.Bools {
		v.Enabled = flag.Bool(k, false, v.Desc)
		nginxBuildOptions.Bools[k] = v
	}
	for k, v := range nginxBuildOptions.Values {
		v.Value = flag.String(k, v.Default, v.Desc)
		nginxBuildOptions.Values[k] = v
	}
	for k, v := range nginxBuildOptions.Numbers {
		v.Value = flag.Int(k, v.Default, v.Desc)
		nginxBuildOptions.Numbers[k] = v
	}

	// fake flag for --with-xxx=dynamic
	for i, arg := range os.Args {
		if strings.Contains(arg, "with-http_xslt_module=dynamic") {
			os.Args[i] = "--with-http_xslt_module_dynamic"
		}
		if strings.Contains(arg, "with-http_image_filter_module=dynamic") {
			os.Args[i] = "--with-http_image_filter_module_dynamic"
		}
		if strings.Contains(arg, "with-http_geoip_module=dynamic") {
			os.Args[i] = "--with-http_geoip_module_dynamic"
		}
		if strings.Contains(arg, "with-http_perl_module=dynamic") {
			os.Args[i] = "--with-http_perl_module_dynamic"
		}
		if strings.Contains(arg, "with-mail=dynamic") {
			os.Args[i] = "--with-mail_dynamic"
		}
		if strings.Contains(arg, "with-stream=dynamic") {
			os.Args[i] = "--with-stream_dynamic"
		}
	}

	var (
		configureOptions configure.Options
		multiflag        StringFlag
		multiflagDynamic StringFlag
	)

	argsString := configure.MakeArgsString()
	for k, v := range argsString {
		if k == "add-module" {
			flag.Var(&multiflag, k, v.Desc)
		} else if k == "add-dynamic-module" {
			flag.Var(&multiflagDynamic, k, v.Desc)
		} else {
			v.Value = flag.String(k, "", v.Desc)
			argsString[k] = v
		}
	}

	argsBool := configure.MakeArgsBool()
	for k, v := range argsBool {
		v.Enabled = flag.Bool(k, false, v.Desc)
		argsBool[k] = v
	}

	flag.CommandLine.SetOutput(os.Stdout)
	// The output of original flag.Usage() is too long
	defaultUsage := flag.Usage
	flag.Usage = usage
	flag.Parse()

	jobs := nginxBuildOptions.Numbers["j"].Value

	verbose := nginxBuildOptions.Bools["verbose"].Enabled
	pcreStatic := nginxBuildOptions.Bools["pcre"].Enabled
	openSSLStatic := nginxBuildOptions.Bools["openssl"].Enabled
	zlibStatic := nginxBuildOptions.Bools["zlib"].Enabled
	clear := nginxBuildOptions.Bools["clear"].Enabled
	versionPrint := nginxBuildOptions.Bools["version"].Enabled
	versionsPrint := nginxBuildOptions.Bools["versions"].Enabled
	openResty := nginxBuildOptions.Bools["openresty"].Enabled
	tengine := nginxBuildOptions.Bools["tengine"].Enabled
	configureOnly := nginxBuildOptions.Bools["configureonly"].Enabled
	helpAll := nginxBuildOptions.Bools["help-all"].Enabled

	version := nginxBuildOptions.Values["v"].Value
	nginxConfigurePath := nginxBuildOptions.Values["c"].Value
	modulesConfPath := nginxBuildOptions.Values["m"].Value
	workParentDir := nginxBuildOptions.Values["d"].Value
	pcreVersion := nginxBuildOptions.Values["pcreversion"].Value
	openSSLVersion := nginxBuildOptions.Values["opensslversion"].Value
	zlibVersion := nginxBuildOptions.Values["zlibversion"].Value
	openRestyVersion := nginxBuildOptions.Values["openrestyversion"].Value
	tengineVersion := nginxBuildOptions.Values["tengineversion"].Value
	patchPath := nginxBuildOptions.Values["patch"].Value
	patchOption := nginxBuildOptions.Values["patch-opt"].Value

	// Allow multiple flags for `--add-module`
	{
		tmp := argsString["add-module"]
		tmp_ := multiflag.String()
		tmp.Value = &tmp_
		argsString["add-module"] = tmp
	}

	// Allow multiple flags for `--add-dynamic-module`
	{
		tmp := argsString["add-dynamic-module"]
		tmp_ := multiflagDynamic.String()
		tmp.Value = &tmp_
		argsString["add-dynamic-module"] = tmp
	}

	configureOptions.Values = argsString
	configureOptions.Bools = argsBool

	if *helpAll {
		defaultUsage()
		return
	}

	if *versionPrint {
		printNginxBuildVersion()
		return
	}

	if *versionsPrint {
		printNginxVersions()
		return
	}

	printFirstMsg()

	// set verbose mode
	command.VerboseEnabled = *verbose

	var nginxBuilder builder.Builder
	if *openResty && *tengine {
		log.Fatal("select one between '-openresty' and '-tengine'.")
	}
	if *openResty {
		nginxBuilder = builder.MakeBuilder(builder.COMPONENT_OPENRESTY, *openRestyVersion)
	} else if *tengine {
		nginxBuilder = builder.MakeBuilder(builder.COMPONENT_TENGINE, *tengineVersion)
	} else {
		nginxBuilder = builder.MakeBuilder(builder.COMPONENT_NGINX, *version)
	}
	pcreBuilder := builder.MakeBuilder(builder.COMPONENT_PCRE, *pcreVersion)
	openSSLBuilder := builder.MakeBuilder(builder.COMPONENT_OPENSSL, *openSSLVersion)
	zlibBuilder := builder.MakeBuilder(builder.COMPONENT_ZLIB, *zlibVersion)

	// change default umask
	_ = syscall.Umask(0)

	versionCheck(*version)

	nginxConfigure, err := util.FileGetContents(*nginxConfigurePath)
	if err != nil {
		log.Fatal(err)
	}
	nginxConfigure = configure.Normalize(nginxConfigure)

	modules3rd, err := module3rd.Load(*modulesConfPath)
	if err != nil {
		log.Fatal(err)
	}

	if len(*workParentDir) == 0 {
		log.Fatal("set working directory with -d")
	}

	if !util.FileExists(*workParentDir) {
		err := os.Mkdir(*workParentDir, 0755)
		if err != nil {
			log.Fatalf("Failed to create working directory(%s) does not exist.", *workParentDir)
		}
	}

	var workDir string
	if *openResty {
		workDir = *workParentDir + "/openresty/" + *openRestyVersion
	} else if *tengine {
		workDir = *workParentDir + "/tengine/" + *tengineVersion
	} else {
		workDir = *workParentDir + "/nginx/" + *version
	}

	if *clear {
		err := util.ClearWorkDir(workDir)
		if err != nil {
			log.Fatal(err)
		}
	}

	if !util.FileExists(workDir) {
		err := os.MkdirAll(workDir, 0755)
		if err != nil {
			log.Fatalf("Failed to create working directory(%s) does not exist.", workDir)
		}
	}

	rootDir := util.SaveCurrentDir()
	err = os.Chdir(workDir)
	if err != nil {
		log.Fatal(err)
	}

	// remove nginx source code applyed patch
	if *patchPath != "" && util.FileExists(nginxBuilder.SourcePath()) {
		err := os.RemoveAll(nginxBuilder.SourcePath())
		if err != nil {
			log.Fatal(err)
		}
	}

	wg := new(sync.WaitGroup)
	if *pcreStatic {
		wg.Add(1)
		go func() {
			downloadAndExtractParallel(&pcreBuilder)
			wg.Done()
		}()
	}

	if *openSSLStatic {
		wg.Add(1)
		go func () {
			downloadAndExtractParallel(&openSSLBuilder)
			wg.Done()
		}()
	}

	if *zlibStatic {
		wg.Add(1)
		go func () {
			downloadAndExtractParallel(&zlibBuilder)
			wg.Done()
		}()
	}

	wg.Add(1)
	go func() {
		downloadAndExtractParallel(&nginxBuilder)
		wg.Done()
	}()

	if len(modules3rd) > 0 {
		wg.Add(len(modules3rd))
		for _, m := range modules3rd {
			go func(m module3rd.Module3rd) {
				module3rd.DownloadAndExtractParallel(m)
				wg.Done()
			}(m)
		}

	}

	// wait until all downloading processes by goroutine finish
	wg.Wait()

	if len(modules3rd) > 0 {
		for _, m := range modules3rd {
			if err := module3rd.Provide(&m); err != nil {
				log.Fatal(err)
			}
		}
	}

	// cd workDir/nginx-${version}
	os.Chdir(nginxBuilder.SourcePath())

	var dependencies []builder.StaticLibrary
	if *pcreStatic {
		dependencies = append(dependencies, builder.MakeStaticLibrary(&pcreBuilder))
	}

	if *openSSLStatic {
		dependencies = append(dependencies, builder.MakeStaticLibrary(&openSSLBuilder))
	}

	if *zlibStatic {
		dependencies = append(dependencies, builder.MakeStaticLibrary(&zlibBuilder))
	}

	log.Printf("Generate configure script for %s.....", nginxBuilder.SourcePath())

	if *pcreStatic && pcreBuilder.IsIncludeWithOption(nginxConfigure) {
		log.Println(pcreBuilder.WarnMsgWithLibrary())
	}

	if *openSSLStatic && openSSLBuilder.IsIncludeWithOption(nginxConfigure) {
		log.Println(openSSLBuilder.WarnMsgWithLibrary())

	}

	if *zlibStatic && zlibBuilder.IsIncludeWithOption(nginxConfigure) {
		log.Println(zlibBuilder.WarnMsgWithLibrary())
	}

	configureScript := configure.Generate(nginxConfigure, modules3rd, dependencies, configureOptions, rootDir, *openResty, *jobs)

	err = ioutil.WriteFile("./nginx-configure", []byte(configureScript), 0655)
	if err != nil {
		log.Fatalf("Failed to generate configure script for %s", nginxBuilder.SourcePath())
	}

	util.Patch(*patchPath, *patchOption, rootDir, false)

	// reverts source code with patch -R when the build was interrupted.
	if *patchPath != "" {
		sigChannel := make(chan os.Signal, 1)
		signal.Notify(sigChannel, os.Interrupt)
		go func() {
			<-sigChannel
			util.Patch(*patchPath, *patchOption, rootDir, true)
		}()
	}

	log.Printf("Configure %s.....", nginxBuilder.SourcePath())

	err = configure.Run()
	if err != nil {
		log.Printf("Failed to configure %s\n", nginxBuilder.SourcePath())
		util.Patch(*patchPath, *patchOption, rootDir, true)
		util.PrintFatalMsg(err, "nginx-configure.log")
	}

	if *configureOnly {
		util.Patch(*patchPath, *patchOption, rootDir, true)
		printLastMsg(workDir, nginxBuilder.SourcePath(), *openResty, *configureOnly)
		return
	}

	log.Printf("Build %s.....", nginxBuilder.SourcePath())

	if *openSSLStatic {
		// Workarounds for protecting a failure of building nginx with static-linked OpenSSL.

		// The build of old OpenSSL fails when multi-CPUs are used.
		// It is fixed in the commit below.
		//
		// https://github.com/openssl/openssl/commit/8e6bb99979b95ee8b878e22e043ceb78d79c32a1
		//
		// And backported to 1.0.1p and 1.0.2d.
		if !openssl.ParallelBuildAvailable(*openSSLVersion) {
			*jobs = 1
		}

		// Sometimes machine hardware name('uname -m') is different
		// from machine processor architecture name('uname -p') on Mac.
		// Specifically, `uname -p` is 'i386' and `uname -m` is 'x86_64'.
		// In this case, a build of OpenSSL fails.
		// So it needs to convince OpenSSL with KERNEL_BITS.
		if runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" {
			os.Setenv("KERNEL_BITS", "64")
		}
	}

	err = builder.BuildNginx(*jobs)
	if err != nil {
		log.Printf("Failed to build %s\n", nginxBuilder.SourcePath())
		util.Patch(*patchPath, *patchOption, rootDir, true)
		util.PrintFatalMsg(err, "nginx-build.log")
	}

	printLastMsg(workDir, nginxBuilder.SourcePath(), *openResty, *configureOnly)

	// cd rootDir
	os.Chdir(rootDir)
}
<|endoftext|>


<commit_before>// Copyright 2018, OpenCensus 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 ocagent

import (
	"context"
	"errors"
	"fmt"
	"sync"
	"time"

	"google.golang.org/api/support/bundler"
	"google.golang.org/grpc"

	"go.opencensus.io/trace"

	agentcommonpb "github.com/census-instrumentation/opencensus-proto/gen-go/agent/common/v1"
	agenttracepb "github.com/census-instrumentation/opencensus-proto/gen-go/agent/trace/v1"
	tracepb "github.com/census-instrumentation/opencensus-proto/gen-go/trace/v1"
)

var startupMu sync.Mutex
var startTime time.Time

func init() {
	startupMu.Lock()
	startTime = time.Now()
	startupMu.Unlock()
}

var _ trace.Exporter = (*Exporter)(nil)

type Exporter struct {
	// mu protects the non-atomic and non-channel variables
	mu              sync.RWMutex
	started         bool
	stopped         bool
	agentPort       uint16
	agentAddress    string
	serviceName     string
	canDialInsecure bool
	traceSvcClient  agenttracepb.TraceServiceClient
	traceExporter   agenttracepb.TraceService_ExportClient
	nodeInfo        *agentcommonpb.Node
	grpcClientConn  *grpc.ClientConn

	traceBundler *bundler.Bundler
}

func NewExporter(opts ...ExporterOption) (*Exporter, error) {
	exp, err := NewUnstartedExporter(opts...)
	if err != nil {
		return nil, err
	}
	if err := exp.Start(); err != nil {
		return nil, err
	}
	return exp, nil
}

const spanDataBufferSize = 300

func NewUnstartedExporter(opts ...ExporterOption) (*Exporter, error) {
	e := new(Exporter)
	for _, opt := range opts {
		opt.withExporter(e)
	}
	if e.agentPort <= 0 {
		e.agentPort = DefaultAgentPort
	}
	traceBundler := bundler.NewBundler((*trace.SpanData)(nil), func(bundle interface{}) {
		e.uploadTraces(bundle.([]*trace.SpanData))
	})
	traceBundler.DelayThreshold = 2 * time.Second
	traceBundler.BundleCountThreshold = spanDataBufferSize
	e.traceBundler = traceBundler
	e.nodeInfo = createNodeInfo(e.serviceName)
	return e, nil
}

const (
	maxInitialConfigRetries = 10
	maxInitialTracesRetries = 10
)

// Start dials to the agent, establishing a connection to it. It also
// initiates the Config and Trace services by sending over the initial
// messages that consist of the node identifier. Start performs a best case
// attempt to try to send the initial messages, by applying exponential
// backoff at most 10 times.
func (ae *Exporter) Start() error {
	ae.mu.Lock()
	defer ae.mu.Unlock()

	err := ae.doStartLocked()
	if err == nil {
		ae.started = true
		return nil
	}

	// Otherwise we have an error and should clean up to avoid leaking resources.
	ae.started = false
	if ae.grpcClientConn != nil {
		ae.grpcClientConn.Close()
	}

	return err
}

func (ae *Exporter) prepareAgentAddress() string {
	if ae.agentAddress != "" {
		return ae.agentAddress
	}
	port := DefaultAgentPort
	if ae.agentPort > 0 {
		port = ae.agentPort
	}
	return fmt.Sprintf("%s:%d", DefaultAgentHost, port)
}

func (ae *Exporter) doStartLocked() error {
	if ae.started {
		return nil
	}

	// Now start it
	cc, err := ae.dialToAgent()
	if err != nil {
		return err
	}
	ae.grpcClientConn = cc

	// Initiate the trace service by sending over node identifier info.
	traceSvcClient := agenttracepb.NewTraceServiceClient(cc)
	traceExporter, err := traceSvcClient.Export(context.Background())
	if err != nil {
		return fmt.Errorf("Exporter.Start:: TraceServiceClient: %v", err)
	}

	firstTraceMessage := &agenttracepb.ExportTraceServiceRequest{Node: ae.nodeInfo}
	err = nTriesWithExponentialBackoff(maxInitialTracesRetries, 200*time.Microsecond, func() error {
		return traceExporter.Send(firstTraceMessage)
	})
	if err != nil {
		return fmt.Errorf("Exporter.Start:: Failed to initiate the Config service: %v", err)
	}
	ae.traceExporter = traceExporter

	// Initiate the config service by sending over node identifier info.
	configStream, err := traceSvcClient.Config(context.Background())
	if err != nil {
		return fmt.Errorf("Exporter.Start:: ConfigStream: %v", err)
	}
	firstCfgMessage := &agenttracepb.CurrentLibraryConfig{Node: ae.nodeInfo}
	err = nTriesWithExponentialBackoff(maxInitialConfigRetries, 200*time.Microsecond, func() error {
		return configStream.Send(firstCfgMessage)
	})
	if err != nil {
		return fmt.Errorf("Exporter.Start:: Failed to initiate the Config service: %v", err)
	}

	// In the background, handle trace configurations that are beamed down
	// by the agent, but also reply to it with the applied configuration.
	go ae.handleConfigStreaming(configStream)

	return nil
}

// dialToAgent performs a best case attempt to dial to the agent.
// It retries failed dials with:
//  * gRPC dialTimeout of 1s
//  * exponential backoff, 5 times with a period of 50ms
// hence in the worst case of (no agent actually available), it
// will take at least:
//      (5 * 1s) + ((1<<5)-1) * 0.01 s = 5s + 1.55s = 6.55s
func (ae *Exporter) dialToAgent() (*grpc.ClientConn, error) {
	addr := ae.prepareAgentAddress()
	dialOpts := []grpc.DialOption{grpc.WithBlock()}
	if ae.canDialInsecure {
		dialOpts = append(dialOpts, grpc.WithInsecure())
	}

	var cc *grpc.ClientConn
	dialOpts = append(dialOpts, grpc.WithTimeout(1*time.Second))
	dialBackoffWaitPeriod := 50 * time.Millisecond
	err := nTriesWithExponentialBackoff(5, dialBackoffWaitPeriod, func() error {
		var err error
		cc, err = grpc.Dial(addr, dialOpts...)
		return err
	})
	return cc, err
}

func (ae *Exporter) handleConfigStreaming(configStream agenttracepb.TraceService_ConfigClient) error {
	for {
		recv, err := configStream.Recv()
		if err != nil {
			// TODO: Check if this is a transient error or exponential backoff-able.
			return err
		}
		cfg := recv.Config
		if cfg == nil {
			continue
		}

		// Otherwise now apply the trace configuration sent down from the agent
		if psamp := cfg.GetProbabilitySampler(); psamp != nil {
			trace.ApplyConfig(trace.Config{DefaultSampler: trace.ProbabilitySampler(psamp.SamplingProbability)})
		} else if csamp := cfg.GetConstantSampler(); csamp != nil {
			alwaysSample := csamp.Decision == true
			if alwaysSample {
				trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
			} else {
				trace.ApplyConfig(trace.Config{DefaultSampler: trace.NeverSample()})
			}
		} else { // TODO: Add the rate limiting sampler here
		}

		// Then finally send back to upstream the newly applied configuration
		err = configStream.Send(&agenttracepb.CurrentLibraryConfig{Config: &tracepb.TraceConfig{Sampler: cfg.Sampler}})
		if err != nil {
			return err
		}
	}
}

var (
	errNotStarted = errors.New("not started")
)

// Stop shuts down all the connections and resources
// related to the exporter.
func (ae *Exporter) Stop() error {
	ae.mu.Lock()
	defer ae.mu.Unlock()

	if !ae.started {
		return errNotStarted
	}
	if ae.stopped {
		// TODO: tell the user that we've already stopped, so perhaps a sentinel error?
		return nil
	}

	ae.Flush()

	// Now close the underlying gRPC connection.
	var err error
	if ae.grpcClientConn != nil {
		err = ae.grpcClientConn.Close()
	}

	// At this point we can change the state variables: started and stopped
	ae.started = false
	ae.stopped = true

	return err
}

func (ae *Exporter) ExportSpan(sd *trace.SpanData) {
	if sd == nil {
		return
	}
	_ = ae.traceBundler.Add(sd, -1)
}

func (ae *Exporter) uploadTraces(sdl []*trace.SpanData) {
	if len(sdl) == 0 {
		return
	}
	protoSpans := make([]*tracepb.Span, 0, len(sdl))
	for _, sd := range sdl {
		if sd != nil {
			protoSpans = append(protoSpans, ocSpanToProtoSpan(sd))
		}
	}

	if len(protoSpans) > 0 {
		_ = ae.traceExporter.Send(&agenttracepb.ExportTraceServiceRequest{
			Spans: protoSpans,
		})
	}
}

func (ae *Exporter) Flush() {
	ae.traceBundler.Flush()
}
<commit_msg>dialToAgent: fix typo in comment from 10ms to 50ms<commit_after>// Copyright 2018, OpenCensus 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 ocagent

import (
	"context"
	"errors"
	"fmt"
	"sync"
	"time"

	"google.golang.org/api/support/bundler"
	"google.golang.org/grpc"

	"go.opencensus.io/trace"

	agentcommonpb "github.com/census-instrumentation/opencensus-proto/gen-go/agent/common/v1"
	agenttracepb "github.com/census-instrumentation/opencensus-proto/gen-go/agent/trace/v1"
	tracepb "github.com/census-instrumentation/opencensus-proto/gen-go/trace/v1"
)

var startupMu sync.Mutex
var startTime time.Time

func init() {
	startupMu.Lock()
	startTime = time.Now()
	startupMu.Unlock()
}

var _ trace.Exporter = (*Exporter)(nil)

type Exporter struct {
	// mu protects the non-atomic and non-channel variables
	mu              sync.RWMutex
	started         bool
	stopped         bool
	agentPort       uint16
	agentAddress    string
	serviceName     string
	canDialInsecure bool
	traceSvcClient  agenttracepb.TraceServiceClient
	traceExporter   agenttracepb.TraceService_ExportClient
	nodeInfo        *agentcommonpb.Node
	grpcClientConn  *grpc.ClientConn

	traceBundler *bundler.Bundler
}

func NewExporter(opts ...ExporterOption) (*Exporter, error) {
	exp, err := NewUnstartedExporter(opts...)
	if err != nil {
		return nil, err
	}
	if err := exp.Start(); err != nil {
		return nil, err
	}
	return exp, nil
}

const spanDataBufferSize = 300

func NewUnstartedExporter(opts ...ExporterOption) (*Exporter, error) {
	e := new(Exporter)
	for _, opt := range opts {
		opt.withExporter(e)
	}
	if e.agentPort <= 0 {
		e.agentPort = DefaultAgentPort
	}
	traceBundler := bundler.NewBundler((*trace.SpanData)(nil), func(bundle interface{}) {
		e.uploadTraces(bundle.([]*trace.SpanData))
	})
	traceBundler.DelayThreshold = 2 * time.Second
	traceBundler.BundleCountThreshold = spanDataBufferSize
	e.traceBundler = traceBundler
	e.nodeInfo = createNodeInfo(e.serviceName)
	return e, nil
}

const (
	maxInitialConfigRetries = 10
	maxInitialTracesRetries = 10
)

// Start dials to the agent, establishing a connection to it. It also
// initiates the Config and Trace services by sending over the initial
// messages that consist of the node identifier. Start performs a best case
// attempt to try to send the initial messages, by applying exponential
// backoff at most 10 times.
func (ae *Exporter) Start() error {
	ae.mu.Lock()
	defer ae.mu.Unlock()

	err := ae.doStartLocked()
	if err == nil {
		ae.started = true
		return nil
	}

	// Otherwise we have an error and should clean up to avoid leaking resources.
	ae.started = false
	if ae.grpcClientConn != nil {
		ae.grpcClientConn.Close()
	}

	return err
}

func (ae *Exporter) prepareAgentAddress() string {
	if ae.agentAddress != "" {
		return ae.agentAddress
	}
	port := DefaultAgentPort
	if ae.agentPort > 0 {
		port = ae.agentPort
	}
	return fmt.Sprintf("%s:%d", DefaultAgentHost, port)
}

func (ae *Exporter) doStartLocked() error {
	if ae.started {
		return nil
	}

	// Now start it
	cc, err := ae.dialToAgent()
	if err != nil {
		return err
	}
	ae.grpcClientConn = cc

	// Initiate the trace service by sending over node identifier info.
	traceSvcClient := agenttracepb.NewTraceServiceClient(cc)
	traceExporter, err := traceSvcClient.Export(context.Background())
	if err != nil {
		return fmt.Errorf("Exporter.Start:: TraceServiceClient: %v", err)
	}

	firstTraceMessage := &agenttracepb.ExportTraceServiceRequest{Node: ae.nodeInfo}
	err = nTriesWithExponentialBackoff(maxInitialTracesRetries, 200*time.Microsecond, func() error {
		return traceExporter.Send(firstTraceMessage)
	})
	if err != nil {
		return fmt.Errorf("Exporter.Start:: Failed to initiate the Config service: %v", err)
	}
	ae.traceExporter = traceExporter

	// Initiate the config service by sending over node identifier info.
	configStream, err := traceSvcClient.Config(context.Background())
	if err != nil {
		return fmt.Errorf("Exporter.Start:: ConfigStream: %v", err)
	}
	firstCfgMessage := &agenttracepb.CurrentLibraryConfig{Node: ae.nodeInfo}
	err = nTriesWithExponentialBackoff(maxInitialConfigRetries, 200*time.Microsecond, func() error {
		return configStream.Send(firstCfgMessage)
	})
	if err != nil {
		return fmt.Errorf("Exporter.Start:: Failed to initiate the Config service: %v", err)
	}

	// In the background, handle trace configurations that are beamed down
	// by the agent, but also reply to it with the applied configuration.
	go ae.handleConfigStreaming(configStream)

	return nil
}

// dialToAgent performs a best case attempt to dial to the agent.
// It retries failed dials with:
//  * gRPC dialTimeout of 1s
//  * exponential backoff, 5 times with a period of 50ms
// hence in the worst case of (no agent actually available), it
// will take at least:
//      (5 * 1s) + ((1<<5)-1) * 0.05 s = 5s + 1.55s = 6.55s
func (ae *Exporter) dialToAgent() (*grpc.ClientConn, error) {
	addr := ae.prepareAgentAddress()
	dialOpts := []grpc.DialOption{grpc.WithBlock()}
	if ae.canDialInsecure {
		dialOpts = append(dialOpts, grpc.WithInsecure())
	}

	var cc *grpc.ClientConn
	dialOpts = append(dialOpts, grpc.WithTimeout(1*time.Second))
	dialBackoffWaitPeriod := 50 * time.Millisecond
	err := nTriesWithExponentialBackoff(5, dialBackoffWaitPeriod, func() error {
		var err error
		cc, err = grpc.Dial(addr, dialOpts...)
		return err
	})
	return cc, err
}

func (ae *Exporter) handleConfigStreaming(configStream agenttracepb.TraceService_ConfigClient) error {
	for {
		recv, err := configStream.Recv()
		if err != nil {
			// TODO: Check if this is a transient error or exponential backoff-able.
			return err
		}
		cfg := recv.Config
		if cfg == nil {
			continue
		}

		// Otherwise now apply the trace configuration sent down from the agent
		if psamp := cfg.GetProbabilitySampler(); psamp != nil {
			trace.ApplyConfig(trace.Config{DefaultSampler: trace.ProbabilitySampler(psamp.SamplingProbability)})
		} else if csamp := cfg.GetConstantSampler(); csamp != nil {
			alwaysSample := csamp.Decision == true
			if alwaysSample {
				trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
			} else {
				trace.ApplyConfig(trace.Config{DefaultSampler: trace.NeverSample()})
			}
		} else { // TODO: Add the rate limiting sampler here
		}

		// Then finally send back to upstream the newly applied configuration
		err = configStream.Send(&agenttracepb.CurrentLibraryConfig{Config: &tracepb.TraceConfig{Sampler: cfg.Sampler}})
		if err != nil {
			return err
		}
	}
}

var (
	errNotStarted = errors.New("not started")
)

// Stop shuts down all the connections and resources
// related to the exporter.
func (ae *Exporter) Stop() error {
	ae.mu.Lock()
	defer ae.mu.Unlock()

	if !ae.started {
		return errNotStarted
	}
	if ae.stopped {
		// TODO: tell the user that we've already stopped, so perhaps a sentinel error?
		return nil
	}

	ae.Flush()

	// Now close the underlying gRPC connection.
	var err error
	if ae.grpcClientConn != nil {
		err = ae.grpcClientConn.Close()
	}

	// At this point we can change the state variables: started and stopped
	ae.started = false
	ae.stopped = true

	return err
}

func (ae *Exporter) ExportSpan(sd *trace.SpanData) {
	if sd == nil {
		return
	}
	_ = ae.traceBundler.Add(sd, -1)
}

func (ae *Exporter) uploadTraces(sdl []*trace.SpanData) {
	if len(sdl) == 0 {
		return
	}
	protoSpans := make([]*tracepb.Span, 0, len(sdl))
	for _, sd := range sdl {
		if sd != nil {
			protoSpans = append(protoSpans, ocSpanToProtoSpan(sd))
		}
	}

	if len(protoSpans) > 0 {
		_ = ae.traceExporter.Send(&agenttracepb.ExportTraceServiceRequest{
			Spans: protoSpans,
		})
	}
}

func (ae *Exporter) Flush() {
	ae.traceBundler.Flush()
}
<|endoftext|>