<commit_before>//Package trakerr gives the client access to client side constructors for initializing
//and using trakerr.
package trakerr

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"time"
)

//TrakerrClient ...
type TrakerrClient struct {
	apiKey                  string
	url                     string
	contextAppVersion       string
	contextEnvName          string
	contextEnvVersion       string
	contextEnvHostname      string
	contextAppOS            string
	contextAppOSVersion     string
	contextDataCenter       string
	contextDataCenterRegion string
	eventsAPI               EventsAPI
	eventTraceBuilder       EventTraceBuilder
}

//NewTrakerrClientWithDefaults creates a new TrakerrClient and return it with the data.
// Most parameters are optional i.e. empty (pass "" to use defaults) with the exception of apiKey which is required.
func NewTrakerrClientWithDefaults(
	apiKey string,
	contextAppVersion string,
	contextEnvName string) *TrakerrClient {
	return NewTrakerrClient(apiKey, "", contextAppVersion, contextEnvName, "", "", "", "", "", "")
}

// NewTrakerrClient creates a new TrakerrClient and return it with the data.
// Most parameters are optional i.e. empty (pass "" to use defaults) with the exception of apiKey which is required.
// url is the location of the serverr service, if "" is passed it defaults to https://trakerr.io/api/v1
func NewTrakerrClient(
	apiKey string,
	url string,
	contextAppVersion string,
	contextEnvName string,
	contextEnvVersion string,
	contextEnvHostname string,
	contextAppOS string,
	contextAppOSVersion string,
	contextDataCenter string,
	contextDataCenterRegion string) *TrakerrClient {

	if contextEnvName == "" {
		contextEnvName = "development"
	}
	if contextAppVersion == "" {
		contextAppVersion = "1.0"
	}
	if contextEnvHostname == "" {
		contextEnvHostname, _ = os.Hostname()
	}

	if contextAppOS == "" {
		contextAppOS = runtime.GOOS
		switch contextAppOS {
		case "windows": // Keep an eye out for OS's using carrage returns
			//cmd1 := exec.Command("systeminfo")
			cmd := exec.Command("cmd", "/C", "systeminfo") //exec.Command("findstr", "/C:\"OS Name\"")
			var out bytes.Buffer
			cmd.Stdout = &out

			err1 := cmd.Run()
			if err1 != nil {
				contextAppOS = runtime.GOOS
				contextAppOSVersion = "N/A (arch:" + runtime.GOARCH + ")"
			} else {
				var output = out.String()
				contextAppOS = getTextFromLine(output, "OS Name:", "\n")
				contextAppOSVersion = getTextFromLine(output, "OS Version:", "\n")
			}

		case "darwin": // ...
			cmd := exec.Command("bash", "-c", "system_profiler SPSoftwareDataType") //exec.Command("findstr", "/C:\"OS Name\"")
			var out bytes.Buffer
			cmd.Stdout = &out

			err1 := cmd.Run()
			if err1 != nil {
				contextAppOS = runtime.GOOS
				contextAppOSVersion = "N/A (arch:" + runtime.GOARCH + ")"
			} else {
				var output = out.String()

				contextAppOS = getTextFromLine(output, "System Version:", "(")
				contextAppOSVersion = getTextFromLine(output, "Kernel Version:", "\n")
			}

		default:
			cmd := exec.Command("bash", "-c", "uname -s") //Uname -r and -s
			var out bytes.Buffer
			cmd.Stdout = &out

			err1 := cmd.Run()
			var output string
			if err1 != nil {
				contextAppOS = runtime.GOOS
			} else {
				output = out.String()

				contextAppOS = strings.Trim(output, " \r\n")
			}

			cmd = exec.Command("bash", "-c", "uname -r") //Uname -r and -s
			err1 = cmd.Run()
			if err1 != nil {
				contextAppOSVersion = "N/A (arch:" + runtime.GOARCH + ")"
			} else {
				output = out.String()

				contextAppOSVersion = strings.Trim(output, " \r\n")
			}

		}

	}
	var eventsAPI EventsAPI

	if url != "" {
		eventsAPI = *NewEventsAPIWithBasePath(url)
	} else {
		eventsAPI = *NewEventsAPI()
	}

	return &TrakerrClient{
		apiKey:                  apiKey,
		url:                     url,
		contextAppVersion:       contextAppVersion,
		contextEnvName:          contextEnvName,
		contextEnvVersion:       contextEnvVersion,
		contextEnvHostname:      contextEnvHostname,
		contextAppOS:            contextAppOS,
		contextAppOSVersion:     contextAppOSVersion,
		contextDataCenter:       contextDataCenter,
		contextDataCenterRegion: contextDataCenterRegion,
		eventsAPI:               eventsAPI,
		eventTraceBuilder:       EventTraceBuilder{}}
}

func getTextFromLine(text string, prefix string, suffix string) string {
	var startindex = strings.Index(text, prefix)
	if startindex == -1 {
		return ""
	}
	var newstringfromprefix = text[startindex+len(prefix):]
	var endindex = strings.Index(newstringfromprefix, suffix)
	if endindex == -1 {
		return ""
	}

	return strings.Trim(newstringfromprefix[0:endindex], " \n\r")
}

//NewAppEvent ...
func (trakerrClient *TrakerrClient) NewAppEvent(classification string, eventType string, eventMessage string) *AppEvent {
	if classification == "" {
		classification = "Error"
	}
	if eventType == "" {
		eventType = "unknown"
	}
	if eventMessage == "" {
		eventMessage = "unknown "
	}
	return trakerrClient.FillDefaults(&AppEvent{Classification: classification, EventType: eventType, EventMessage: eventMessage})
}

//SendEvent ...
func (trakerrClient *TrakerrClient) SendEvent(appEvent *AppEvent) (*APIResponse, error) {
	return trakerrClient.eventsAPI.EventsPost(*trakerrClient.FillDefaults(appEvent))
}

//SendError ...
func (trakerrClient *TrakerrClient) SendError(err interface{}, skip int) (*APIResponse, error) {
	appEvent := trakerrClient.CreateAppEventFromErrorWithSkip(err, skip+1)

	return trakerrClient.eventsAPI.EventsPost(*appEvent)
}

//CreateAppEventFromError ...
func (trakerrClient *TrakerrClient) CreateAppEventFromError(err interface{}) *AppEvent {
	return trakerrClient.CreateAppEventFromErrorWithSkip(err, 1)

}

//CreateAppEventFromErrorWithSkip ...
func (trakerrClient *TrakerrClient) CreateAppEventFromErrorWithSkip(err interface{}, skip int) *AppEvent {
	stacktrace := trakerrClient.eventTraceBuilder.GetEventTraces(err, 50, skip+1)
	event := AppEvent{}
	event.EventType = fmt.Sprintf("%T", err)
	event.EventMessage = fmt.Sprint(err)
	event.Classification = "Error"

	result := trakerrClient.FillDefaults(&event)
	event.EventStacktrace = stacktrace
	return result
}

//AddStackTraceToAppEvent ...
func (trakerrClient *TrakerrClient) AddStackTraceToAppEvent(appEvent *AppEvent, err interface{}, skip int) {
	stacktrace := trakerrClient.eventTraceBuilder.GetEventTraces(err, 50, skip+1)
	var event = appEvent
	event.EventType = fmt.Sprintf("%T", err)
	event.EventMessage = fmt.Sprint(err)
	event.Classification = "Error"

	event.EventStacktrace = stacktrace
}

//Recover recovers from a panic and sends the error to Trakerr.
//Use in a Defer statement.
func (trakerrClient *TrakerrClient) Recover() {
	if err := recover(); err != nil {
		trakerrClient.SendError(err, 1)
	}
}

//RecoverWithAppEvent ...
func (trakerrClient *TrakerrClient) RecoverWithAppEvent(appEvent *AppEvent) {
	if err := recover(); err != nil {
		trakerrClient.AddStackTraceToAppEvent(appEvent, err, 1)
		trakerrClient.SendEvent(appEvent)
	}
}

//Notify recovers from an error and then repanics after sending the error to Trakerr,
//so that the panic can be picked up by the program error handler.
//Use in a Defer statement.
func (trakerrClient *TrakerrClient) Notify() {
	if err := recover(); err != nil {
		trakerrClient.SendError(err, 1)
		panic(err)
	}
}

//FillDefaults ...
func (trakerrClient *TrakerrClient) FillDefaults(appEvent *AppEvent) *AppEvent {
	if appEvent.ApiKey == "" {
		appEvent.ApiKey = trakerrClient.apiKey
	}

	if appEvent.ContextAppVersion == "" {
		appEvent.ContextAppVersion = trakerrClient.contextAppVersion
	}

	if appEvent.ContextEnvName == "" {
		appEvent.ContextEnvName = trakerrClient.contextEnvName
	}
	if appEvent.ContextEnvVersion == "" {
		appEvent.ContextEnvVersion = trakerrClient.contextEnvVersion
	}
	if appEvent.ContextEnvHostname == "" {
		appEvent.ContextEnvHostname = trakerrClient.contextEnvHostname
	}

	if appEvent.ContextAppOS == "" {
		appEvent.ContextAppOS = trakerrClient.contextAppOS
		appEvent.ContextAppOSVersion = trakerrClient.contextAppOSVersion
	}

	if appEvent.ContextDataCenter == "" {
		appEvent.ContextDataCenter = trakerrClient.contextDataCenter
	}
	if appEvent.ContextDataCenterRegion == "" {
		appEvent.ContextDataCenterRegion = trakerrClient.contextDataCenterRegion
	}

	if appEvent.EventTime <= 0 {
		appEvent.EventTime = makeTimestamp()
	}
	return appEvent
}

func makeTimestamp() int64 {
	return time.Now().UnixNano() / int64(time.Millisecond)
}
<commit_msg>Fix to windows AppOSVersion formatting.<commit_after>//Package trakerr gives the client access to client side constructors for initializing
//and using trakerr.
package trakerr

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"time"
)

//TrakerrClient ...
type TrakerrClient struct {
	apiKey                  string
	url                     string
	contextAppVersion       string
	contextEnvName          string
	contextEnvVersion       string
	contextEnvHostname      string
	contextAppOS            string
	contextAppOSVersion     string
	contextDataCenter       string
	contextDataCenterRegion string
	eventsAPI               EventsAPI
	eventTraceBuilder       EventTraceBuilder
}

//NewTrakerrClientWithDefaults creates a new TrakerrClient and return it with the data.
// Most parameters are optional i.e. empty (pass "" to use defaults) with the exception of apiKey which is required.
func NewTrakerrClientWithDefaults(
	apiKey string,
	contextAppVersion string,
	contextEnvName string) *TrakerrClient {
	return NewTrakerrClient(apiKey, "", contextAppVersion, contextEnvName, "", "", "", "", "", "")
}

// NewTrakerrClient creates a new TrakerrClient and return it with the data.
// Most parameters are optional i.e. empty (pass "" to use defaults) with the exception of apiKey which is required.
// url is the location of the serverr service, if "" is passed it defaults to https://trakerr.io/api/v1
func NewTrakerrClient(
	apiKey string,
	url string,
	contextAppVersion string,
	contextEnvName string,
	contextEnvVersion string,
	contextEnvHostname string,
	contextAppOS string,
	contextAppOSVersion string,
	contextDataCenter string,
	contextDataCenterRegion string) *TrakerrClient {

	if contextEnvName == "" {
		contextEnvName = "development"
	}
	if contextAppVersion == "" {
		contextAppVersion = "1.0"
	}
	if contextEnvHostname == "" {
		contextEnvHostname, _ = os.Hostname()
	}

	if contextAppOS == "" {
		contextAppOS = runtime.GOOS
		switch contextAppOS {
		case "windows": // Keep an eye out for OS's using carrage returns
			//cmd1 := exec.Command("systeminfo")
			cmd := exec.Command("cmd", "/C", "systeminfo") //exec.Command("findstr", "/C:\"OS Name\"")
			var out bytes.Buffer
			cmd.Stdout = &out

			err1 := cmd.Run()
			if err1 != nil {
				contextAppOS = runtime.GOOS
				contextAppOSVersion = "N/A (arch:" + runtime.GOARCH + ")"
			} else {
				var output = out.String()
				contextAppOS = getTextFromLine(output, "OS Name:", "\n")
				version := getTextFromLine(output, "OS Version:", "\n")
				versionstringsplit := strings.Split(version, " ")

				if len(versionstringsplit) >= 1 {
					contextAppOSVersion = versionstringsplit[0]
				} else {
					contextAppOSVersion = version
				}
			}

		case "darwin": // ...
			cmd := exec.Command("bash", "-c", "system_profiler SPSoftwareDataType") //exec.Command("findstr", "/C:\"OS Name\"")
			var out bytes.Buffer
			cmd.Stdout = &out

			err1 := cmd.Run()
			if err1 != nil {
				contextAppOS = runtime.GOOS
				contextAppOSVersion = "N/A (arch:" + runtime.GOARCH + ")"
			} else {
				var output = out.String()

				contextAppOS = getTextFromLine(output, "System Version:", "(")
				contextAppOSVersion = getTextFromLine(output, "Kernel Version:", "\n")
			}

		default:
			cmd := exec.Command("bash", "-c", "uname -s") //Uname -r and -s
			var out bytes.Buffer
			cmd.Stdout = &out

			err1 := cmd.Run()
			var output string
			if err1 != nil {
				contextAppOS = runtime.GOOS
			} else {
				output = out.String()

				contextAppOS = strings.Trim(output, " \r\n")
			}

			cmd = exec.Command("bash", "-c", "uname -r") //Uname -r and -s
			err1 = cmd.Run()
			if err1 != nil {
				contextAppOSVersion = "N/A (arch:" + runtime.GOARCH + ")"
			} else {
				output = out.String()

				contextAppOSVersion = strings.Trim(output, " \r\n")
			}

		}

	}
	var eventsAPI EventsAPI

	if url != "" {
		eventsAPI = *NewEventsAPIWithBasePath(url)
	} else {
		eventsAPI = *NewEventsAPI()
	}

	return &TrakerrClient{
		apiKey:                  apiKey,
		url:                     url,
		contextAppVersion:       contextAppVersion,
		contextEnvName:          contextEnvName,
		contextEnvVersion:       contextEnvVersion,
		contextEnvHostname:      contextEnvHostname,
		contextAppOS:            contextAppOS,
		contextAppOSVersion:     contextAppOSVersion,
		contextDataCenter:       contextDataCenter,
		contextDataCenterRegion: contextDataCenterRegion,
		eventsAPI:               eventsAPI,
		eventTraceBuilder:       EventTraceBuilder{}}
}

func getTextFromLine(text string, prefix string, suffix string) string {
	var startindex = strings.Index(text, prefix)
	if startindex == -1 {
		return ""
	}
	var newstringfromprefix = text[startindex+len(prefix):]
	var endindex = strings.Index(newstringfromprefix, suffix)
	if endindex == -1 {
		return ""
	}

	return strings.Trim(newstringfromprefix[0:endindex], " \n\r")
}

//NewAppEvent ...
func (trakerrClient *TrakerrClient) NewAppEvent(classification string, eventType string, eventMessage string) *AppEvent {
	if classification == "" {
		classification = "Error"
	}
	if eventType == "" {
		eventType = "unknown"
	}
	if eventMessage == "" {
		eventMessage = "unknown "
	}
	return trakerrClient.FillDefaults(&AppEvent{Classification: classification, EventType: eventType, EventMessage: eventMessage})
}

//SendEvent ...
func (trakerrClient *TrakerrClient) SendEvent(appEvent *AppEvent) (*APIResponse, error) {
	return trakerrClient.eventsAPI.EventsPost(*trakerrClient.FillDefaults(appEvent))
}

//SendError ...
func (trakerrClient *TrakerrClient) SendError(err interface{}, skip int) (*APIResponse, error) {
	appEvent := trakerrClient.CreateAppEventFromErrorWithSkip(err, skip+1)

	return trakerrClient.eventsAPI.EventsPost(*appEvent)
}

//CreateAppEventFromError ...
func (trakerrClient *TrakerrClient) CreateAppEventFromError(err interface{}) *AppEvent {
	return trakerrClient.CreateAppEventFromErrorWithSkip(err, 1)

}

//CreateAppEventFromErrorWithSkip ...
func (trakerrClient *TrakerrClient) CreateAppEventFromErrorWithSkip(err interface{}, skip int) *AppEvent {
	stacktrace := trakerrClient.eventTraceBuilder.GetEventTraces(err, 50, skip+1)
	event := AppEvent{}
	event.EventType = fmt.Sprintf("%T", err)
	event.EventMessage = fmt.Sprint(err)
	event.Classification = "Error"

	result := trakerrClient.FillDefaults(&event)
	event.EventStacktrace = stacktrace
	return result
}

//AddStackTraceToAppEvent ...
func (trakerrClient *TrakerrClient) AddStackTraceToAppEvent(appEvent *AppEvent, err interface{}, skip int) {
	stacktrace := trakerrClient.eventTraceBuilder.GetEventTraces(err, 50, skip+1)
	var event = appEvent
	event.EventType = fmt.Sprintf("%T", err)
	event.EventMessage = fmt.Sprint(err)
	event.Classification = "Error"

	event.EventStacktrace = stacktrace
}

//Recover recovers from a panic and sends the error to Trakerr.
//Use in a Defer statement.
func (trakerrClient *TrakerrClient) Recover() {
	if err := recover(); err != nil {
		trakerrClient.SendError(err, 1)
	}
}

//RecoverWithAppEvent ...
func (trakerrClient *TrakerrClient) RecoverWithAppEvent(appEvent *AppEvent) {
	if err := recover(); err != nil {
		trakerrClient.AddStackTraceToAppEvent(appEvent, err, 1)
		trakerrClient.SendEvent(appEvent)
	}
}

//Notify recovers from an error and then repanics after sending the error to Trakerr,
//so that the panic can be picked up by the program error handler.
//Use in a Defer statement.
func (trakerrClient *TrakerrClient) Notify() {
	if err := recover(); err != nil {
		trakerrClient.SendError(err, 1)
		panic(err)
	}
}

//FillDefaults ...
func (trakerrClient *TrakerrClient) FillDefaults(appEvent *AppEvent) *AppEvent {
	if appEvent.ApiKey == "" {
		appEvent.ApiKey = trakerrClient.apiKey
	}

	if appEvent.ContextAppVersion == "" {
		appEvent.ContextAppVersion = trakerrClient.contextAppVersion
	}

	if appEvent.ContextEnvName == "" {
		appEvent.ContextEnvName = trakerrClient.contextEnvName
	}
	if appEvent.ContextEnvVersion == "" {
		appEvent.ContextEnvVersion = trakerrClient.contextEnvVersion
	}
	if appEvent.ContextEnvHostname == "" {
		appEvent.ContextEnvHostname = trakerrClient.contextEnvHostname
	}

	if appEvent.ContextAppOS == "" {
		appEvent.ContextAppOS = trakerrClient.contextAppOS
		appEvent.ContextAppOSVersion = trakerrClient.contextAppOSVersion
	}

	if appEvent.ContextDataCenter == "" {
		appEvent.ContextDataCenter = trakerrClient.contextDataCenter
	}
	if appEvent.ContextDataCenterRegion == "" {
		appEvent.ContextDataCenterRegion = trakerrClient.contextDataCenterRegion
	}

	if appEvent.EventTime <= 0 {
		appEvent.EventTime = makeTimestamp()
	}
	return appEvent
}

func makeTimestamp() int64 {
	return time.Now().UnixNano() / int64(time.Millisecond)
}
<|endoftext|>


<commit_before>package tests

import (
	"fmt"
	"math/rand"
	"os"
	"os/signal"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"testing"
	"time"

	"github.com/gomodule/redigo/redis"
	"github.com/tidwall/limiter"
	"go.uber.org/atomic"
)

const (
	clear   = "\x1b[0m"
	bright  = "\x1b[1m"
	dim     = "\x1b[2m"
	black   = "\x1b[30m"
	red     = "\x1b[31m"
	green   = "\x1b[32m"
	yellow  = "\x1b[33m"
	blue    = "\x1b[34m"
	magenta = "\x1b[35m"
	cyan    = "\x1b[36m"
	white   = "\x1b[37m"
)

func TestIntegration(t *testing.T) {

	mockCleanup(true)
	defer mockCleanup(true)

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-ch
		mockCleanup(false)
		os.Exit(1)
	}()

	regTestGroup("keys", subTestKeys)
	regTestGroup("json", subTestJSON)
	regTestGroup("search", subTestSearch)
	regTestGroup("testcmd", subTestTestCmd)
	regTestGroup("client", subTestClient)
	regTestGroup("scripts", subTestScripts)
	regTestGroup("fence", subTestFence)
	regTestGroup("info", subTestInfo)
	regTestGroup("timeouts", subTestTimeout)
	regTestGroup("metrics", subTestMetrics)
	regTestGroup("follower", subTestFollower)
	regTestGroup("aof", subTestAOF)
	regTestGroup("monitor", subTestMonitor)
	runTestGroups(t)
}

var allGroups []*testGroup

func runTestGroups(t *testing.T) {
	limit := runtime.NumCPU()
	if limit > 16 {
		limit = 16
	}
	l := limiter.New(limit)

	// Initialize all stores as "skipped", but they'll be unset if the test is
	// not actually skipped.
	for _, g := range allGroups {
		for _, s := range g.subs {
			s.skipped.Store(true)
		}
	}
	for _, g := range allGroups {
		func(g *testGroup) {
			t.Run(g.name, func(t *testing.T) {
				for _, s := range g.subs {
					func(s *testGroupSub) {
						t.Run(s.name, func(t *testing.T) {
							s.skipped.Store(false)
							var wg sync.WaitGroup
							wg.Add(1)
							var err error
							go func() {
								l.Begin()
								defer func() {
									l.End()
									wg.Done()
								}()
								err = s.run()
							}()
							if false {
								t.Parallel()
								t.Run("bg", func(t *testing.T) {
									wg.Wait()
									if err != nil {
										t.Fatal(err)
									}
								})
							}
						})
					}(s)
				}
			})
		}(g)
	}

	done := make(chan bool)
	go func() {
		defer func() { done <- true }()
		for {
			finished := true
			for _, g := range allGroups {
				skipped := true
				for _, s := range g.subs {
					if !s.skipped.Load() {
						skipped = false
						break
					}
				}
				if !skipped && !g.printed.Load() {
					fmt.Printf(bright+"Testing %s\n"+clear, g.name)
					g.printed.Store(true)
				}
				const frtmp = "[" + magenta + "  " + clear + "] %s (running) "
				for _, s := range g.subs {
					if !s.skipped.Load() && !s.printedName.Load() {
						fmt.Printf(frtmp, s.name)
						s.printedName.Store(true)
					}
					if s.done.Load() && !s.printedResult.Load() {
						fmt.Printf("\r")
						msg := fmt.Sprintf(frtmp, s.name)
						fmt.Print(strings.Repeat(" ", len(msg)))
						fmt.Printf("\r")
						if s.err != nil {
							fmt.Printf("["+red+"fail"+clear+"] %s\n", s.name)
						} else {
							fmt.Printf("["+green+"ok"+clear+"] %s\n", s.name)
						}
						s.printedResult.Store(true)
					}
					if !s.skipped.Load() && !s.done.Load() {
						finished = false
						break
					}
				}
				if !finished {
					break
				}
			}
			if finished {
				break
			}
			time.Sleep(time.Second / 4)
		}
	}()
	<-done
	var fail bool
	for _, g := range allGroups {
		for _, s := range g.subs {
			if s.err != nil {
				t.Errorf("%s/%s/%s\n%s", t.Name(), g.name, s.name, s.err)
				fail = true
			}
		}
	}
	if fail {
		t.Fail()
	}

}

type testGroup struct {
	name    string
	subs    []*testGroupSub
	printed atomic.Bool
}

type testGroupSub struct {
	g             *testGroup
	name          string
	fn            func(mc *mockServer) error
	err           error
	skipped       atomic.Bool
	done          atomic.Bool
	printedName   atomic.Bool
	printedResult atomic.Bool
}

func regTestGroup(name string, fn func(g *testGroup)) {
	g := &testGroup{name: name}
	allGroups = append(allGroups, g)
	fn(g)
}

func (g *testGroup) regSubTest(name string, fn func(mc *mockServer) error) {
	s := &testGroupSub{g: g, name: name, fn: fn}
	g.subs = append(g.subs, s)
}

func (s *testGroupSub) run() (err error) {
	// This all happens in a background routine.
	defer func() {
		s.err = err
		s.done.Store(true)
	}()
	return func() error {
		mc, err := mockOpenServer(MockServerOptions{
			Silent:  true,
			Metrics: true,
		})
		if err != nil {
			return err
		}
		defer mc.Close()
		return s.fn(mc)
	}()
}

func BenchmarkAll(b *testing.B) {
	mockCleanup(true)
	defer mockCleanup(true)

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-ch
		mockCleanup(true)
		os.Exit(1)
	}()

	mc, err := mockOpenServer(MockServerOptions{
		Silent: true, Metrics: true,
	})
	if err != nil {
		b.Fatal(err)
	}
	defer mc.Close()
	runSubBenchmark(b, "search", mc, subBenchSearch)
}

func loadBenchmarkPoints(b *testing.B, mc *mockServer) (err error) {
	const nPoints = 200000
	rand.Seed(time.Now().UnixNano())

	// add a bunch of points
	for i := 0; i < nPoints; i++ {
		val := fmt.Sprintf("val:%d", i)
		var resp string
		var lat, lon, fval float64
		fval = rand.Float64()
		lat = rand.Float64()*180 - 90
		lon = rand.Float64()*360 - 180
		resp, err = redis.String(mc.conn.Do("SET",
			"mykey", val,
			"FIELD", "foo", fval,
			"POINT", lat, lon))
		if err != nil {
			return
		}
		if resp != "OK" {
			err = fmt.Errorf("expected 'OK', got '%s'", resp)
			return
		}
	}
	return
}

func runSubBenchmark(b *testing.B, name string, mc *mockServer, bench func(t *testing.B, mc *mockServer)) {
	b.Run(name, func(b *testing.B) {
		bench(b, mc)
	})
}

func runBenchStep(b *testing.B, mc *mockServer, name string, step func(mc *mockServer) error) {
	b.Helper()
	b.Run(name, func(b *testing.B) {
		b.Helper()
		if err := func() error {
			// reset the current server
			mc.ResetConn()
			defer mc.ResetConn()
			// clear the database so the test is consistent
			if err := mc.DoBatch([][]interface{}{
				{"OUTPUT", "resp"}, {"OK"},
				{"FLUSHDB"}, {"OK"},
			}); err != nil {
				return err
			}
			err := loadBenchmarkPoints(b, mc)
			if err != nil {
				return err
			}
			b.ResetTimer()
			for i := 0; i < b.N; i++ {
				if err := step(mc); err != nil {
					return err
				}
			}
			return nil
		}(); err != nil {
			b.Fatal(err)
		}
	})
}
<commit_msg>Clean up test output<commit_after>package tests

import (
	"fmt"
	"math/rand"
	"os"
	"os/signal"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"testing"
	"time"

	"github.com/gomodule/redigo/redis"
	"github.com/tidwall/limiter"
	"go.uber.org/atomic"
)

const (
	clear   = "\x1b[0m"
	bright  = "\x1b[1m"
	dim     = "\x1b[2m"
	black   = "\x1b[30m"
	red     = "\x1b[31m"
	green   = "\x1b[32m"
	yellow  = "\x1b[33m"
	blue    = "\x1b[34m"
	magenta = "\x1b[35m"
	cyan    = "\x1b[36m"
	white   = "\x1b[37m"
)

func TestIntegration(t *testing.T) {

	mockCleanup(true)
	defer mockCleanup(true)

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-ch
		mockCleanup(false)
		os.Exit(1)
	}()

	regTestGroup("keys", subTestKeys)
	regTestGroup("json", subTestJSON)
	regTestGroup("search", subTestSearch)
	regTestGroup("testcmd", subTestTestCmd)
	regTestGroup("client", subTestClient)
	regTestGroup("scripts", subTestScripts)
	regTestGroup("fence", subTestFence)
	regTestGroup("info", subTestInfo)
	regTestGroup("timeouts", subTestTimeout)
	regTestGroup("metrics", subTestMetrics)
	regTestGroup("follower", subTestFollower)
	regTestGroup("aof", subTestAOF)
	regTestGroup("monitor", subTestMonitor)
	runTestGroups(t)
}

var allGroups []*testGroup

func runTestGroups(t *testing.T) {
	limit := runtime.NumCPU()
	if limit > 16 {
		limit = 16
	}
	l := limiter.New(limit)

	// Initialize all stores as "skipped", but they'll be unset if the test is
	// not actually skipped.
	for _, g := range allGroups {
		for _, s := range g.subs {
			s.skipped.Store(true)
		}
	}
	for _, g := range allGroups {
		func(g *testGroup) {
			t.Run(g.name, func(t *testing.T) {
				for _, s := range g.subs {
					func(s *testGroupSub) {
						t.Run(s.name, func(t *testing.T) {
							s.skipped.Store(false)
							var wg sync.WaitGroup
							wg.Add(1)
							var err error
							go func() {
								l.Begin()
								defer func() {
									l.End()
									wg.Done()
								}()
								err = s.run()
							}()
							if false {
								t.Parallel()
								t.Run("bg", func(t *testing.T) {
									wg.Wait()
									if err != nil {
										t.Fatal(err)
									}
								})
							}
						})
					}(s)
				}
			})
		}(g)
	}

	done := make(chan bool)
	go func() {
		defer func() { done <- true }()
		// count the largest sub test name
		var largest int
		for _, g := range allGroups {
			for _, s := range g.subs {
				if !s.skipped.Load() {
					if len(s.name) > largest {
						largest = len(s.name)
					}
				}
			}
		}

		for {
			finished := true
			for _, g := range allGroups {
				skipped := true
				for _, s := range g.subs {
					if !s.skipped.Load() {
						skipped = false
						break
					}
				}
				if !skipped && !g.printed.Load() {
					fmt.Printf("\n"+bright+"Testing %s"+clear+"\n", g.name)
					g.printed.Store(true)
				}
				const frtmp = "%s ... "
				for _, s := range g.subs {
					if !s.skipped.Load() && !s.printedName.Load() {
						pref := fmt.Sprintf(frtmp, s.name)
						nspaces := largest - len(pref) + 5
						if nspaces < 0 {
							nspaces = 0
						}
						spaces := strings.Repeat(" ", nspaces)
						fmt.Printf("%s%s", pref, spaces)
						s.printedName.Store(true)
					}
					if s.done.Load() && !s.printedResult.Load() {
						if s.err != nil {
							fmt.Printf("[" + red + "fail" + clear + "]\n")
						} else {
							fmt.Printf("[" + green + "ok" + clear + "]\n")
						}
						s.printedResult.Store(true)
					}
					if !s.skipped.Load() && !s.done.Load() {
						finished = false
						break
					}
				}
				if !finished {
					break
				}
			}
			if finished {
				break
			}
			time.Sleep(time.Second / 4)
		}
	}()
	<-done
	var fail bool
	for _, g := range allGroups {
		for _, s := range g.subs {
			if s.err != nil {
				t.Errorf("%s/%s/%s\n%s", t.Name(), g.name, s.name, s.err)
				fail = true
			}
		}
	}
	if fail {
		t.Fail()
	}

}

type testGroup struct {
	name    string
	subs    []*testGroupSub
	printed atomic.Bool
}

type testGroupSub struct {
	g             *testGroup
	name          string
	fn            func(mc *mockServer) error
	err           error
	skipped       atomic.Bool
	done          atomic.Bool
	printedName   atomic.Bool
	printedResult atomic.Bool
}

func regTestGroup(name string, fn func(g *testGroup)) {
	g := &testGroup{name: name}
	allGroups = append(allGroups, g)
	fn(g)
}

func (g *testGroup) regSubTest(name string, fn func(mc *mockServer) error) {
	s := &testGroupSub{g: g, name: name, fn: fn}
	g.subs = append(g.subs, s)
}

func (s *testGroupSub) run() (err error) {
	// This all happens in a background routine.
	defer func() {
		s.err = err
		s.done.Store(true)
	}()
	return func() error {
		mc, err := mockOpenServer(MockServerOptions{
			Silent:  true,
			Metrics: true,
		})
		if err != nil {
			return err
		}
		defer mc.Close()
		return s.fn(mc)
	}()
}

func BenchmarkAll(b *testing.B) {
	mockCleanup(true)
	defer mockCleanup(true)

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-ch
		mockCleanup(true)
		os.Exit(1)
	}()

	mc, err := mockOpenServer(MockServerOptions{
		Silent: true, Metrics: true,
	})
	if err != nil {
		b.Fatal(err)
	}
	defer mc.Close()
	runSubBenchmark(b, "search", mc, subBenchSearch)
}

func loadBenchmarkPoints(b *testing.B, mc *mockServer) (err error) {
	const nPoints = 200000
	rand.Seed(time.Now().UnixNano())

	// add a bunch of points
	for i := 0; i < nPoints; i++ {
		val := fmt.Sprintf("val:%d", i)
		var resp string
		var lat, lon, fval float64
		fval = rand.Float64()
		lat = rand.Float64()*180 - 90
		lon = rand.Float64()*360 - 180
		resp, err = redis.String(mc.conn.Do("SET",
			"mykey", val,
			"FIELD", "foo", fval,
			"POINT", lat, lon))
		if err != nil {
			return
		}
		if resp != "OK" {
			err = fmt.Errorf("expected 'OK', got '%s'", resp)
			return
		}
	}
	return
}

func runSubBenchmark(b *testing.B, name string, mc *mockServer, bench func(t *testing.B, mc *mockServer)) {
	b.Run(name, func(b *testing.B) {
		bench(b, mc)
	})
}

func runBenchStep(b *testing.B, mc *mockServer, name string, step func(mc *mockServer) error) {
	b.Helper()
	b.Run(name, func(b *testing.B) {
		b.Helper()
		if err := func() error {
			// reset the current server
			mc.ResetConn()
			defer mc.ResetConn()
			// clear the database so the test is consistent
			if err := mc.DoBatch([][]interface{}{
				{"OUTPUT", "resp"}, {"OK"},
				{"FLUSHDB"}, {"OK"},
			}); err != nil {
				return err
			}
			err := loadBenchmarkPoints(b, mc)
			if err != nil {
				return err
			}
			b.ResetTimer()
			for i := 0; i < b.N; i++ {
				if err := step(mc); err != nil {
					return err
				}
			}
			return nil
		}(); err != nil {
			b.Fatal(err)
		}
	})
}
<|endoftext|>


<commit_before>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 state_test

import (
	"errors"
	"github.com/jacobsa/comeback/backup"
	"github.com/jacobsa/comeback/backup/mock"
	"github.com/jacobsa/comeback/blob"
	"github.com/jacobsa/comeback/fs"
	"github.com/jacobsa/comeback/fs/mock"
	"github.com/jacobsa/comeback/state"
	. "github.com/jacobsa/oglematchers"
	"github.com/jacobsa/oglemock"
	. "github.com/jacobsa/ogletest"
	"testing"
	"time"
)

func TestScoreMapSaver(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type ScoreMapSaverTest struct {
	sourceMap  state.ScoreMap
	sinkMap    state.ScoreMap
	fileSystem mock_fs.MockFileSystem
	wrapped    mock_backup.MockFileSaver
	saver      backup.FileSaver

	path   string
	scores []blob.Score
	err    error
}

func init() { RegisterTestSuite(&ScoreMapSaverTest{}) }

func (t *ScoreMapSaverTest) SetUp(i *TestInfo) {
	t.sourceMap = state.NewScoreMap()
	t.sinkMap = state.NewScoreMap()
	t.fileSystem = mock_fs.NewMockFileSystem(i.MockController, "fileSystem")
	t.wrapped = mock_backup.NewMockFileSaver(i.MockController, "wrapped")

	t.saver = state.NewScoreMapFileSaver(
		t.sourceMap,
		t.sinkMap,
		t.fileSystem,
		t.wrapped,
	)
}

func (t *ScoreMapSaverTest) call() {
	t.scores, t.err = t.saver.Save(t.path)
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *ScoreMapSaverTest) CallsStat() {
	t.path = "taco"

	// File system
	ExpectCall(t.fileSystem, "Stat")("taco").
		WillOnce(oglemock.Return(fs.DirectoryEntry{}, errors.New("")))

	// Call
	t.call()
}

func (t *ScoreMapSaverTest) StatReturnsError() {
	// File system
	ExpectCall(t.fileSystem, "Stat")(Any()).
		WillOnce(oglemock.Return(fs.DirectoryEntry{}, errors.New("taco")))

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("Stat")))
	ExpectThat(t.err, Error(HasSubstr("taco")))
}

func (t *ScoreMapSaverTest) ScoreMapContainsEntry() {
	t.path = "taco"

	// Score map
	expectedKey := state.ScoreMapKey{
		Path:        "taco",
		Permissions: 0644,
		Uid:         17,
		Gid:         19,
		MTime:       time.Now(),
		Inode:       23,
		Size:        29,
	}

	expectedScores := []blob.Score{
		blob.ComputeScore([]byte("foo")),
		blob.ComputeScore([]byte("bar")),
	}

	t.sourceMap.Set(expectedKey, expectedScores)

	// File system
	entry := fs.DirectoryEntry{
		Permissions: 0644,
		Uid:         17,
		Gid:         19,
		MTime:       expectedKey.MTime,
		Inode:       23,
		Size:        29,
	}

	ExpectCall(t.fileSystem, "Stat")(Any()).
		WillOnce(oglemock.Return(entry, nil))

	// Call
	t.call()

	AssertEq(nil, t.err)
	ExpectThat(t.scores, DeepEquals(expectedScores))
}

func (t *ScoreMapSaverTest) CallsWrapped() {
	t.path = "taco"

	// File system
	ExpectCall(t.fileSystem, "Stat")(Any()).
		WillOnce(oglemock.Return(fs.DirectoryEntry{}, nil))

	// Wrapped
	ExpectCall(t.wrapped, "Save")("taco").
		WillOnce(oglemock.Return(nil, errors.New("")))

	// Call
	t.call()
}

func (t *ScoreMapSaverTest) WrappedReturnsError() {
	ExpectEq("TODO", "")
}

func (t *ScoreMapSaverTest) WrappedReturnsScores() {
	ExpectEq("TODO", "")
}
<commit_msg>ScoreMapSaverTest.WrappedReturnsError<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 state_test

import (
	"errors"
	"github.com/jacobsa/comeback/backup"
	"github.com/jacobsa/comeback/backup/mock"
	"github.com/jacobsa/comeback/blob"
	"github.com/jacobsa/comeback/fs"
	"github.com/jacobsa/comeback/fs/mock"
	"github.com/jacobsa/comeback/state"
	. "github.com/jacobsa/oglematchers"
	"github.com/jacobsa/oglemock"
	. "github.com/jacobsa/ogletest"
	"testing"
	"time"
)

func TestScoreMapSaver(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type ScoreMapSaverTest struct {
	sourceMap  state.ScoreMap
	sinkMap    state.ScoreMap
	fileSystem mock_fs.MockFileSystem
	wrapped    mock_backup.MockFileSaver
	saver      backup.FileSaver

	path   string
	scores []blob.Score
	err    error
}

func init() { RegisterTestSuite(&ScoreMapSaverTest{}) }

func (t *ScoreMapSaverTest) SetUp(i *TestInfo) {
	t.sourceMap = state.NewScoreMap()
	t.sinkMap = state.NewScoreMap()
	t.fileSystem = mock_fs.NewMockFileSystem(i.MockController, "fileSystem")
	t.wrapped = mock_backup.NewMockFileSaver(i.MockController, "wrapped")

	t.saver = state.NewScoreMapFileSaver(
		t.sourceMap,
		t.sinkMap,
		t.fileSystem,
		t.wrapped,
	)
}

func (t *ScoreMapSaverTest) call() {
	t.scores, t.err = t.saver.Save(t.path)
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *ScoreMapSaverTest) CallsStat() {
	t.path = "taco"

	// File system
	ExpectCall(t.fileSystem, "Stat")("taco").
		WillOnce(oglemock.Return(fs.DirectoryEntry{}, errors.New("")))

	// Call
	t.call()
}

func (t *ScoreMapSaverTest) StatReturnsError() {
	// File system
	ExpectCall(t.fileSystem, "Stat")(Any()).
		WillOnce(oglemock.Return(fs.DirectoryEntry{}, errors.New("taco")))

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("Stat")))
	ExpectThat(t.err, Error(HasSubstr("taco")))
}

func (t *ScoreMapSaverTest) ScoreMapContainsEntry() {
	t.path = "taco"

	expectedKey := state.ScoreMapKey{
		Path:        "taco",
		Permissions: 0644,
		Uid:         17,
		Gid:         19,
		MTime:       time.Now(),
		Inode:       23,
		Size:        29,
	}

	// Source map
	expectedScores := []blob.Score{
		blob.ComputeScore([]byte("foo")),
		blob.ComputeScore([]byte("bar")),
	}

	t.sourceMap.Set(expectedKey, expectedScores)

	// File system
	entry := fs.DirectoryEntry{
		Permissions: 0644,
		Uid:         17,
		Gid:         19,
		MTime:       expectedKey.MTime,
		Inode:       23,
		Size:        29,
	}

	ExpectCall(t.fileSystem, "Stat")(Any()).
		WillOnce(oglemock.Return(entry, nil))

	// Call
	t.call()

	AssertEq(nil, t.err)
	ExpectThat(t.scores, DeepEquals(expectedScores))
}

func (t *ScoreMapSaverTest) CallsWrapped() {
	t.path = "taco"

	// File system
	ExpectCall(t.fileSystem, "Stat")(Any()).
		WillOnce(oglemock.Return(fs.DirectoryEntry{}, nil))

	// Wrapped
	ExpectCall(t.wrapped, "Save")("taco").
		WillOnce(oglemock.Return(nil, errors.New("")))

	// Call
	t.call()
}

func (t *ScoreMapSaverTest) WrappedReturnsError() {
	expectedKey := state.ScoreMapKey{
		Path:        "taco",
		Permissions: 0644,
		Uid:         17,
		Gid:         19,
		MTime:       time.Now(),
		Inode:       23,
		Size:        29,
	}

	// File system
	entry := fs.DirectoryEntry{
		Permissions: 0644,
		Uid:         17,
		Gid:         19,
		MTime:       expectedKey.MTime,
		Inode:       23,
		Size:        29,
	}

	ExpectCall(t.fileSystem, "Stat")(Any()).
		WillOnce(oglemock.Return(entry, nil))

	// Wrapped
	ExpectCall(t.wrapped, "Save")(Any()).
		WillOnce(oglemock.Return(nil, errors.New("taco")))

	// Call
	t.call()

	AssertThat(t.err, Error(Equals("taco")))
	ExpectEq(nil, t.sinkMap.Get(expectedKey))
}

func (t *ScoreMapSaverTest) WrappedReturnsScores() {
	ExpectEq("TODO", "")
}
<|endoftext|>


<commit_before>// definitions and functions for interacting with backends
package statsdproxy

import (
	"log"
	"net"
	"fmt"
  "time"
)

const (
  PING_CHECK_INTERVAL = 10
)

type StatsDBackend struct {
	Host string
	Port int
	ManagementPort int
	conn net.Conn
	RingID HashRingID
	Status struct {
	  Alive bool
	  LastPingTime int64
  }
}

func NewStatsDBackend(host string, port int, managementPort int) *StatsDBackend {
  client := StatsDBackend{Host: host, Port: port, ManagementPort:
  managementPort}
  client.RingID, _ = GetHashRingPosition(fmt.Sprintf("%s:%s", host, port))
	client.Open()
	return &client
}

// Method to open udp connection, called by default client factory
func (client *StatsDBackend) Open() {
	connectionString := fmt.Sprintf("%s:%d", client.Host, client.Port)
	conn, err := net.Dial("udp", connectionString)
	if err != nil {
		log.Println(err)
	}
	client.conn = conn
}

// Method to close udp connection
func (client *StatsDBackend) Close() {
	client.conn.Close()
}

func (client *StatsDBackend) Send(data string) {
		update_string := fmt.Sprintf(data)
		_, err := fmt.Fprintf(client.conn, update_string)
		if err != nil {
			log.Println(err)
		}
}

func (client *StatsDBackend) CheckAliveStatus() bool {
  // TODO: check management console of client
  return true
}

// function to figure out whether or not a backend is still up
//
// this queries the management interface of the backend to determine health
//
// returns false or true
func (client *StatsDBackend) Alive() bool {
  now := time.Now().Unix()
  if ( now - client.Status.LastPingTime) > PING_CHECK_INTERVAL {
    client.Status.Alive = client.CheckAliveStatus()
    client.Status.LastPingTime = now
  }
  return client.Status.Alive
}
<commit_msg>use configured health check interval<commit_after>// definitions and functions for interacting with backends
package statsdproxy

import (
	"fmt"
	"log"
	"net"
	"time"
)

var healthCheckInterval int

type StatsDBackend struct {
	Host           string
	Port           int
	ManagementPort int
	conn           net.Conn
	RingID         HashRingID
	Status         struct {
		Alive        bool
		LastPingTime int64
	}
}

func NewStatsDBackend(host string, port int,
	managementPort int, check_interval int) *StatsDBackend {
	healthCheckInterval = check_interval
	client := StatsDBackend{Host: host, Port: port, ManagementPort: managementPort}
	client.RingID, _ = GetHashRingPosition(fmt.Sprintf("%s:%s", host, port))
	client.Open()
	return &client
}

// Method to open udp connection, called by default client factory
func (client *StatsDBackend) Open() {
	connectionString := fmt.Sprintf("%s:%d", client.Host, client.Port)
	conn, err := net.Dial("udp", connectionString)
	if err != nil {
		log.Println(err)
	}
	client.conn = conn
}

// Method to close udp connection
func (client *StatsDBackend) Close() {
	client.conn.Close()
}

func (client *StatsDBackend) Send(data string) {
	update_string := fmt.Sprintf(data)
	_, err := fmt.Fprintf(client.conn, update_string)
	if err != nil {
		log.Println(err)
	}
}

func (client *StatsDBackend) CheckAliveStatus() bool {
	// TODO: check management console of client
	return true
}

// function to figure out whether or not a backend is still up
//
// this queries the management interface of the backend to determine health
//
// returns false or true
func (client *StatsDBackend) Alive() bool {
	now := time.Now().Unix()
	if (now - client.Status.LastPingTime) > int64(healthCheckInterval) {
		client.Status.Alive = client.CheckAliveStatus()
		client.Status.LastPingTime = now
	}
	return client.Status.Alive
}
<|endoftext|>


<commit_before>// Copyright 2017 Stratumn SAS. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package storetestcases

import (
	"encoding/json"
	"reflect"
	"testing"

	"bytes"

	"github.com/stratumn/sdk/cs/cstesting"
	"github.com/stratumn/sdk/store"
	"github.com/stratumn/sdk/testutil"
)

// TestBatchSaveSegment tests what happens
// when you write a segment in a Batch
func (f Factory) TestBatchSaveSegment(t *testing.T) {
	a, b := f.initBatch(t)
	defer f.free(a)

	s1 := cstesting.RandomSegment()
	b.SaveSegment(s1)

	s2, err := a.GetSegment(s1.GetLinkHash())
	if err != nil {
		t.Fatalf("a.GetSegment(): err: %s", err)
	}

	if got := s2; got != nil {
		t.Error("s2 != nil want nil")
	}
}

// TestBatchSaveValue tests what happens
// when you write a value in a Batch
func (f Factory) TestBatchSaveValue(t *testing.T) {
	a, b := f.initBatch(t)
	defer f.free(a)

	k := testutil.RandomKey()
	v1 := testutil.RandomValue()
	err := b.SaveValue(k, v1)
	if err != nil {
		t.Fatalf("b.SaveValue(): err: %s", err)
	}

	v2, err := a.GetValue(k)
	if err != nil {
		t.Fatalf("a.GetValue(): err: %s", err)
	}

	if got := v2; got != nil {
		t.Error("v2 != nil want nil")
	}
}

// TestBatchDeleteSegment tests what happens when you delete a segment from
// a batch.
func (f Factory) TestBatchDeleteSegment(t *testing.T) {
	a, b := f.initBatch(t)
	defer f.free(a)

	s1 := cstesting.RandomSegment()
	a.SaveSegment(s1)

	linkHash := s1.GetLinkHash()

	s2, err := b.DeleteSegment(linkHash)
	if err != nil {
		t.Fatalf("b.DeleteSegment(): err: %s", err)
	}

	if got := s2; got == nil {
		t.Fatal("s2 = nil want *cs.Segment")
	}

	delete(s2.Meta, "evidence")

	if got, want := s2, s1; !reflect.DeepEqual(want, got) {
		gotJS, _ := json.MarshalIndent(got, "", "  ")
		wantJS, _ := json.MarshalIndent(want, "", "  ")
		t.Errorf("s2 = %s\n want%s", gotJS, wantJS)
	}

	s2, err = a.GetSegment(linkHash)
	if err != nil {
		t.Fatalf("a.GetSegment(): err: %s", err)
	}
	if got := s2; got == nil {
		gotJS, _ := json.MarshalIndent(got, "", "  ")
		t.Fatalf("s2 = %s\n want %s", gotJS, s2)
	}
}

// TestBatchDeleteValue tests what happens when you delete a value from
// a batch.
func (f Factory) TestBatchDeleteValue(t *testing.T) {
	a, b := f.initBatch(t)
	defer f.free(a)

	k := testutil.RandomKey()
	v1 := testutil.RandomValue()
	a.SaveValue(k, v1)

	v2, err := b.DeleteValue(k)
	if err != nil {
		t.Fatalf("b.DeleteValue(): err: %s", err)
	}

	if got := v2; got == nil {
		t.Fatal("s2 = nil want []byte")
	}

	if got, want := v2, v1; bytes.Compare(got, want) != 0 {
		t.Errorf("s2 = %s\n want%s", got, want)
	}

	v2, err = a.GetValue(k)
	if err != nil {
		t.Fatalf("a.GetValue(): err: %s", err)
	}
	if got := v2; got == nil {
		t.Fatalf("s2 = %s\n want %s", got, v2)
	}
}

// TestBatchWriteSaveSegment tests what happens when you write a Batch with a saved segment.
func (f Factory) TestBatchWriteSaveSegment(t *testing.T) {
	a, b := f.initBatch(t)
	defer f.free(a)

	s1 := cstesting.RandomSegment()
	err := b.SaveSegment(s1)
	if err != nil {
		t.Fatalf("b.SaveSegment(): err: %s", err)
	}

	err = b.Write()
	if err != nil {
		t.Fatalf("b.Write(): err: %s", err)
	}

	s2, err := a.GetSegment(s1.GetLinkHash())
	if err != nil {
		t.Fatalf("a.GetSegment(): err: %s", err)
	}

	if got := s2; got == nil {
		t.Fatal("s2 = nil want *cs.Segment")
	}

	delete(s2.Meta, "evidence")

	if got, want := s2, s1; !reflect.DeepEqual(want, got) {
		gotJS, _ := json.MarshalIndent(got, "", "  ")
		wantJS, _ := json.MarshalIndent(want, "", "  ")
		t.Errorf("s2 = %s\n want%s", gotJS, wantJS)
	}
}

// TestBatchWriteSaveValue tests what happens when you write a Batch with a saved segment.
func (f Factory) TestBatchWriteSaveValue(t *testing.T) {
	a, b := f.initBatch(t)
	defer f.free(a)

	k := testutil.RandomKey()
	v1 := testutil.RandomValue()
	err := b.SaveValue(k, v1)
	if err != nil {
		t.Fatalf("b.SaveValue(): err: %s", err)
	}

	err = b.Write()
	if err != nil {
		t.Fatalf("b.Write(): err: %s", err)
	}

	v2, err := a.GetValue(k)
	if err != nil {
		t.Fatalf("a.GetValue(): err: %s", err)
	}

	if got := v2; got == nil {
		t.Fatal("s2 = nil want []byte")
	}

	if got, want := v2, v1; bytes.Compare(got, want) != 0 {
		t.Errorf("s2 = %s\n want%s", got, want)
	}
}

// TestBatchWriteDeleteSegment tests what happens when you write a Batch with a deleted segment.
func (f Factory) TestBatchWriteDeleteSegment(t *testing.T) {
	a, b := f.initBatch(t)
	defer f.free(a)

	s1 := cstesting.RandomSegment()
	a.SaveSegment(s1)

	linkHash := s1.GetLinkHash()
	s2, err := b.DeleteSegment(linkHash)
	if err != nil {
		t.Fatalf("a.DeleteSegment(): err: %s", err)
	}
	err = b.Write()
	if err != nil {
		t.Fatalf("b.Write(): err: %s", err)
	}

	s2, err = a.GetSegment(linkHash)
	if err != nil {
		t.Fatalf("a.GetSegment(): err: %s", err)
	}
	if got := s2; got != nil {
		gotJS, _ := json.MarshalIndent(got, "", "  ")
		t.Errorf("s2 = %s\n want nil", gotJS)
	}
}

// TestBatchWriteDeleteValue tests what happens when you write a Batch with a deleted value.
func (f Factory) TestBatchWriteDeleteValue(t *testing.T) {
	a, b := f.initBatch(t)
	defer f.free(a)

	k := testutil.RandomKey()
	v1 := testutil.RandomValue()
	err := a.SaveValue(k, v1)
	if err != nil {
		t.Fatalf("b.SaveValue(): err: %s", err)
	}

	v2, err := b.DeleteValue(k)
	if err != nil {
		t.Fatalf("a.DeleteValue(): err: %s", err)
	}
	err = b.Write()
	if err != nil {
		t.Fatalf("b.Write(): err: %s", err)
	}

	v2, err = a.GetValue(k)
	if err != nil {
		t.Fatalf("a.GetValue(): err: %s", err)
	}
	if got := v2; got != nil {
		t.Errorf("s2 = %s\n want nil", got)
	}
}

func (f Factory) initBatch(t *testing.T) (store.Adapter, store.Batch) {
	a := f.initAdapter(t)

	b, err := a.NewBatch()
	if err != nil {
		t.Fatalf("a.NewBatch(): err: %s", err)
	}
	if b == nil {
		t.Fatal("b = nil want store.Batch")
	}

	return a, b
}
<commit_msg>add error check when saving segment in TestBatchDeleteSegment<commit_after>// Copyright 2017 Stratumn SAS. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package storetestcases

import (
	"encoding/json"
	"reflect"
	"testing"

	"bytes"

	"github.com/stratumn/sdk/cs/cstesting"
	"github.com/stratumn/sdk/store"
	"github.com/stratumn/sdk/testutil"
)

// TestBatchSaveSegment tests what happens
// when you write a segment in a Batch
func (f Factory) TestBatchSaveSegment(t *testing.T) {
	a, b := f.initBatch(t)
	defer f.free(a)

	s1 := cstesting.RandomSegment()
	b.SaveSegment(s1)

	s2, err := a.GetSegment(s1.GetLinkHash())
	if err != nil {
		t.Fatalf("a.GetSegment(): err: %s", err)
	}

	if got := s2; got != nil {
		t.Error("s2 != nil want nil")
	}
}

// TestBatchSaveValue tests what happens
// when you write a value in a Batch
func (f Factory) TestBatchSaveValue(t *testing.T) {
	a, b := f.initBatch(t)
	defer f.free(a)

	k := testutil.RandomKey()
	v1 := testutil.RandomValue()
	err := b.SaveValue(k, v1)
	if err != nil {
		t.Fatalf("b.SaveValue(): err: %s", err)
	}

	v2, err := a.GetValue(k)
	if err != nil {
		t.Fatalf("a.GetValue(): err: %s", err)
	}

	if got := v2; got != nil {
		t.Error("v2 != nil want nil")
	}
}

// TestBatchDeleteSegment tests what happens when you delete a segment from
// a batch.
func (f Factory) TestBatchDeleteSegment(t *testing.T) {
	a, b := f.initBatch(t)
	defer f.free(a)

	s1 := cstesting.RandomSegment()
	if err := a.SaveSegment(s1); err != nil {
		t.Fatalf("a.SaveSegment(): err: %s", err)
	}

	linkHash := s1.GetLinkHash()

	s2, err := b.DeleteSegment(linkHash)
	if err != nil {
		t.Fatalf("b.DeleteSegment(): err: %s", err)
	}

	if got := s2; got == nil {
		t.Fatal("s2 = nil want *cs.Segment")
	}

	delete(s2.Meta, "evidence")

	if got, want := s2, s1; !reflect.DeepEqual(want, got) {
		gotJS, _ := json.MarshalIndent(got, "", "  ")
		wantJS, _ := json.MarshalIndent(want, "", "  ")
		t.Errorf("s2 = %s\n want%s", gotJS, wantJS)
	}

	s2, err = a.GetSegment(linkHash)
	if err != nil {
		t.Fatalf("a.GetSegment(): err: %s", err)
	}
	if got := s2; got == nil {
		gotJS, _ := json.MarshalIndent(got, "", "  ")
		t.Fatalf("s2 = %s\n want %s", gotJS, s2)
	}
}

// TestBatchDeleteValue tests what happens when you delete a value from
// a batch.
func (f Factory) TestBatchDeleteValue(t *testing.T) {
	a, b := f.initBatch(t)
	defer f.free(a)

	k := testutil.RandomKey()
	v1 := testutil.RandomValue()
	a.SaveValue(k, v1)

	v2, err := b.DeleteValue(k)
	if err != nil {
		t.Fatalf("b.DeleteValue(): err: %s", err)
	}

	if got := v2; got == nil {
		t.Fatal("s2 = nil want []byte")
	}

	if got, want := v2, v1; bytes.Compare(got, want) != 0 {
		t.Errorf("s2 = %s\n want%s", got, want)
	}

	v2, err = a.GetValue(k)
	if err != nil {
		t.Fatalf("a.GetValue(): err: %s", err)
	}
	if got := v2; got == nil {
		t.Fatalf("s2 = %s\n want %s", got, v2)
	}
}

// TestBatchWriteSaveSegment tests what happens when you write a Batch with a saved segment.
func (f Factory) TestBatchWriteSaveSegment(t *testing.T) {
	a, b := f.initBatch(t)
	defer f.free(a)

	s1 := cstesting.RandomSegment()
	err := b.SaveSegment(s1)
	if err != nil {
		t.Fatalf("b.SaveSegment(): err: %s", err)
	}

	err = b.Write()
	if err != nil {
		t.Fatalf("b.Write(): err: %s", err)
	}

	s2, err := a.GetSegment(s1.GetLinkHash())
	if err != nil {
		t.Fatalf("a.GetSegment(): err: %s", err)
	}

	if got := s2; got == nil {
		t.Fatal("s2 = nil want *cs.Segment")
	}

	delete(s2.Meta, "evidence")

	if got, want := s2, s1; !reflect.DeepEqual(want, got) {
		gotJS, _ := json.MarshalIndent(got, "", "  ")
		wantJS, _ := json.MarshalIndent(want, "", "  ")
		t.Errorf("s2 = %s\n want%s", gotJS, wantJS)
	}
}

// TestBatchWriteSaveValue tests what happens when you write a Batch with a saved segment.
func (f Factory) TestBatchWriteSaveValue(t *testing.T) {
	a, b := f.initBatch(t)
	defer f.free(a)

	k := testutil.RandomKey()
	v1 := testutil.RandomValue()
	err := b.SaveValue(k, v1)
	if err != nil {
		t.Fatalf("b.SaveValue(): err: %s", err)
	}

	err = b.Write()
	if err != nil {
		t.Fatalf("b.Write(): err: %s", err)
	}

	v2, err := a.GetValue(k)
	if err != nil {
		t.Fatalf("a.GetValue(): err: %s", err)
	}

	if got := v2; got == nil {
		t.Fatal("s2 = nil want []byte")
	}

	if got, want := v2, v1; bytes.Compare(got, want) != 0 {
		t.Errorf("s2 = %s\n want%s", got, want)
	}
}

// TestBatchWriteDeleteSegment tests what happens when you write a Batch with a deleted segment.
func (f Factory) TestBatchWriteDeleteSegment(t *testing.T) {
	a, b := f.initBatch(t)
	defer f.free(a)

	s1 := cstesting.RandomSegment()
	a.SaveSegment(s1)

	linkHash := s1.GetLinkHash()
	s2, err := b.DeleteSegment(linkHash)
	if err != nil {
		t.Fatalf("a.DeleteSegment(): err: %s", err)
	}
	err = b.Write()
	if err != nil {
		t.Fatalf("b.Write(): err: %s", err)
	}

	s2, err = a.GetSegment(linkHash)
	if err != nil {
		t.Fatalf("a.GetSegment(): err: %s", err)
	}
	if got := s2; got != nil {
		gotJS, _ := json.MarshalIndent(got, "", "  ")
		t.Errorf("s2 = %s\n want nil", gotJS)
	}
}

// TestBatchWriteDeleteValue tests what happens when you write a Batch with a deleted value.
func (f Factory) TestBatchWriteDeleteValue(t *testing.T) {
	a, b := f.initBatch(t)
	defer f.free(a)

	k := testutil.RandomKey()
	v1 := testutil.RandomValue()
	err := a.SaveValue(k, v1)
	if err != nil {
		t.Fatalf("b.SaveValue(): err: %s", err)
	}

	v2, err := b.DeleteValue(k)
	if err != nil {
		t.Fatalf("a.DeleteValue(): err: %s", err)
	}
	err = b.Write()
	if err != nil {
		t.Fatalf("b.Write(): err: %s", err)
	}

	v2, err = a.GetValue(k)
	if err != nil {
		t.Fatalf("a.GetValue(): err: %s", err)
	}
	if got := v2; got != nil {
		t.Errorf("s2 = %s\n want nil", got)
	}
}

func (f Factory) initBatch(t *testing.T) (store.Adapter, store.Batch) {
	a := f.initAdapter(t)

	b, err := a.NewBatch()
	if err != nil {
		t.Fatalf("a.NewBatch(): err: %s", err)
	}
	if b == nil {
		t.Fatal("b = nil want store.Batch")
	}

	return a, b
}
<|endoftext|>


<commit_before>package handlers

import (
	"fmt"
	"regexp"
	"sort"
	"strconv"
	"strings"

	"github.com/nelsonleduc/calmanbot/cache"
	"github.com/nelsonleduc/calmanbot/handlers/models"
	"github.com/nelsonleduc/calmanbot/service"
)

const currentCalmanBotVersion string = "v2.11.3"

type builtinDescription struct {
	trigger     string
	description string
}

type builtInParams struct {
	bot     models.Bot
	cache   cache.QueryCache
	repo    models.Repo
	service service.Service
}

type builtin struct {
	builtinDescription
	handler func([]string, builtInParams) string
}

// Descriptions
var helpDescription = builtinDescription{
	"(help)",
	"See this",
}
var topDescription = builtinDescription{
	"(top)",
	"List top 10 liked posts",
}
var showDescription = builtinDescription{
	"show (10|[1-9])(?:$| )",
	"Repost nth top post",
}

var versionDescription = builtinDescription{
	"(version)",
	"Display current version",
}

var descriptions = []builtinDescription{
	helpDescription,
	topDescription,
	showDescription,
	versionDescription,
}
var builtins = []builtin{
	builtin{
		helpDescription,
		responseForHelp,
	},
	builtin{
		topDescription,
		responseForLeaderboard,
	},
	builtin{
		showDescription,
		responseForShow,
	},
	builtin{
		versionDescription,
		responseForVersion,
	},
}

// Handlers
func responseForLeaderboard(matched []string, params builtInParams) string {
	entries := params.cache.LeaderboardEntries(params.bot.GroupID, 10)
	leaderboardAccumulatr := "Top posts:"
	for _, e := range entries {
		leaderboardAccumulatr += "\n" + strconv.Itoa(e.LikeCount) + "    " + e.Query
	}

	return leaderboardAccumulatr
}

func responseForShow(matched []string, params builtInParams) string {
	entries := params.cache.LeaderboardEntries(params.bot.GroupID, 10)
	num, error := strconv.Atoi(matched[1])
	num--
	if len(entries) <= num || error != nil {
		return "There is nothing to display"
	}

	return entries[num].Result
}

func responseForVersion(matched []string, params builtInParams) string {
	return "I'm currently running " + currentCalmanBotVersion
}

func responseForHelp(matched []string, params builtInParams) string {
	_, e := params.service.ServiceTriggerWrangler()
	actions, _ := params.repo.FetchActions(true, e == nil)
	sort.Sort(models.ByPriority(actions))
	botName := params.bot.SanitizedBotNames()[0]

	helpAccumulator := "```Commands:"
	longest := 0
	for _, a := range actions {
		if a.Description == nil || a.Pattern == nil {
			continue
		}

		if len(*a.Pattern) > longest {
			longest = len(*a.Pattern)
		}
	}
	for _, b := range descriptions {
		length := len("@" + botName + " !" + b.trigger)
		if length > longest {
			longest = length
		}
	}
	paddingFmt := fmt.Sprintf("%%-%ds", longest+2)

	for _, a := range actions {
		if a.Description == nil || a.Pattern == nil {
			continue
		}
		printablePattern := *a.Pattern
		printablePattern = strings.Replace(printablePattern, "{_botname_}", botName, -1)
		re := regexp.MustCompile("^\\[(.)\\]")
		matched := re.FindStringSubmatch(printablePattern)
		thing := ""
		if len(matched) > 1 && matched[1] != "" {
			thing = matched[1]
		}
		printablePattern = re.ReplaceAllLiteralString(printablePattern, thing)

		helpAccumulator += "\n" + fmt.Sprintf(paddingFmt, printablePattern) + "\n\t" + *a.Description
	}
	for _, b := range descriptions {
		printablePattern := "@" + botName + " !" + b.trigger
		helpAccumulator += "\n" + fmt.Sprintf(paddingFmt, printablePattern) + "\n\t" + b.description
	}

	return helpAccumulator + "```"
}
<commit_msg>Update version to v2.11.4<commit_after>package handlers

import (
	"fmt"
	"regexp"
	"sort"
	"strconv"
	"strings"

	"github.com/nelsonleduc/calmanbot/cache"
	"github.com/nelsonleduc/calmanbot/handlers/models"
	"github.com/nelsonleduc/calmanbot/service"
)

const currentCalmanBotVersion string = "v2.11.4"

type builtinDescription struct {
	trigger     string
	description string
}

type builtInParams struct {
	bot     models.Bot
	cache   cache.QueryCache
	repo    models.Repo
	service service.Service
}

type builtin struct {
	builtinDescription
	handler func([]string, builtInParams) string
}

// Descriptions
var helpDescription = builtinDescription{
	"(help)",
	"See this",
}
var topDescription = builtinDescription{
	"(top)",
	"List top 10 liked posts",
}
var showDescription = builtinDescription{
	"show (10|[1-9])(?:$| )",
	"Repost nth top post",
}

var versionDescription = builtinDescription{
	"(version)",
	"Display current version",
}

var descriptions = []builtinDescription{
	helpDescription,
	topDescription,
	showDescription,
	versionDescription,
}
var builtins = []builtin{
	builtin{
		helpDescription,
		responseForHelp,
	},
	builtin{
		topDescription,
		responseForLeaderboard,
	},
	builtin{
		showDescription,
		responseForShow,
	},
	builtin{
		versionDescription,
		responseForVersion,
	},
}

// Handlers
func responseForLeaderboard(matched []string, params builtInParams) string {
	entries := params.cache.LeaderboardEntries(params.bot.GroupID, 10)
	leaderboardAccumulatr := "Top posts:"
	for _, e := range entries {
		leaderboardAccumulatr += "\n" + strconv.Itoa(e.LikeCount) + "    " + e.Query
	}

	return leaderboardAccumulatr
}

func responseForShow(matched []string, params builtInParams) string {
	entries := params.cache.LeaderboardEntries(params.bot.GroupID, 10)
	num, error := strconv.Atoi(matched[1])
	num--
	if len(entries) <= num || error != nil {
		return "There is nothing to display"
	}

	return entries[num].Result
}

func responseForVersion(matched []string, params builtInParams) string {
	return "I'm currently running " + currentCalmanBotVersion
}

func responseForHelp(matched []string, params builtInParams) string {
	_, e := params.service.ServiceTriggerWrangler()
	actions, _ := params.repo.FetchActions(true, e == nil)
	sort.Sort(models.ByPriority(actions))
	botName := params.bot.SanitizedBotNames()[0]

	helpAccumulator := "```Commands:"
	longest := 0
	for _, a := range actions {
		if a.Description == nil || a.Pattern == nil {
			continue
		}

		if len(*a.Pattern) > longest {
			longest = len(*a.Pattern)
		}
	}
	for _, b := range descriptions {
		length := len("@" + botName + " !" + b.trigger)
		if length > longest {
			longest = length
		}
	}
	paddingFmt := fmt.Sprintf("%%-%ds", longest+2)

	for _, a := range actions {
		if a.Description == nil || a.Pattern == nil {
			continue
		}
		printablePattern := *a.Pattern
		printablePattern = strings.Replace(printablePattern, "{_botname_}", botName, -1)
		re := regexp.MustCompile("^\\[(.)\\]")
		matched := re.FindStringSubmatch(printablePattern)
		thing := ""
		if len(matched) > 1 && matched[1] != "" {
			thing = matched[1]
		}
		printablePattern = re.ReplaceAllLiteralString(printablePattern, thing)

		helpAccumulator += "\n" + fmt.Sprintf(paddingFmt, printablePattern) + "\n\t" + *a.Description
	}
	for _, b := range descriptions {
		printablePattern := "@" + botName + " !" + b.trigger
		helpAccumulator += "\n" + fmt.Sprintf(paddingFmt, printablePattern) + "\n\t" + b.description
	}

	return helpAccumulator + "```"
}
<|endoftext|>


<commit_before>package healthcheck_test

import (
	"net"
	"net/http"
	"strconv"
	"time"

	"code.cloudfoundry.org/healthcheck"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/ghttp"
)

var _ = Describe("HealthCheck", func() {
	itReturnsHealthCheckError := func(healthCheck func() error, code int, reason string) {
		It("returns healthcheck error with code "+strconv.Itoa(code)+" with an appropriate message", func() {
			err := healthCheck()
			Expect(err).To(HaveOccurred())
			Expect(err).To(BeAssignableToTypeOf(healthcheck.HealthCheckError{}))
			hErr := err.(healthcheck.HealthCheckError)
			Expect(hErr.Code).To(Equal(code))
			Expect(hErr.Message).To(ContainSubstring(reason))
		})
	}

	var (
		server     *ghttp.Server
		serverAddr string

		ip string

		uri         string
		port        string
		timeout     time.Duration
		serverDelay time.Duration

		hc healthcheck.HealthCheck
	)

	BeforeEach(func() {
		ip = getNonLoopbackIP()
		server = ghttp.NewUnstartedServer()

		listener, err := net.Listen("tcp", ip+":0")
		Expect(err).NotTo(HaveOccurred())

		timeout = 100 * time.Millisecond
		serverDelay = 0

		server.HTTPTestServer.Listener = listener
		serverAddr = listener.Addr().String()
		_, port, err = net.SplitHostPort(serverAddr)
		Expect(err).NotTo(HaveOccurred())
	})

	JustBeforeEach(func() {
		server.RouteToHandler("GET", "/api/_ping",
			func(http.ResponseWriter, *http.Request) {
				time.Sleep(serverDelay)
			})
		server.Start()

		hc = healthcheck.NewHealthCheck("tcp", uri, port, timeout)
	})

	Describe("check interfaces", func() {
		It("succeeds when there are healthy interfaces", func() {
			interfaces, err := net.Interfaces()
			Expect(err).NotTo(HaveOccurred())

			err = hc.CheckInterfaces(interfaces)
			Expect(err).NotTo(HaveOccurred())
		})

		It("fails appropriately when there are unhealthy interfaces", func() {
			server.Close()

			interfaces, err := net.Interfaces()
			Expect(err).NotTo(HaveOccurred())

			err = hc.CheckInterfaces(interfaces)
			Expect(err).To(HaveOccurred())
			Expect(err).To(BeAssignableToTypeOf(healthcheck.HealthCheckError{}))

			hErr := err.(healthcheck.HealthCheckError)
			// fails with different error codes on Linux (4) or OSX (64)
			// check to see it was not the NO interfaces error (3)
			Expect(hErr.Code).ToNot(Equal(3))
		})

		It("fails appropriately when there are no interfaces", func() {
			err := hc.CheckInterfaces(nil)
			Expect(err).To(HaveOccurred())
			Expect(err).To(BeAssignableToTypeOf(healthcheck.HealthCheckError{}))

			hErr := err.(healthcheck.HealthCheckError)
			Expect(hErr.Code).To(Equal(3))
			Expect(hErr.Message).To(ContainSubstring("failure to find suitable interface"))
		})
	})

	Describe("port healthcheck", func() {
		portHealthCheck := func() error {
			return hc.PortHealthCheck(ip)
		}

		BeforeEach(func() {
			uri = ""
		})

		Context("when the address is listening", func() {
			It("succeeds", func() {
				Expect(portHealthCheck()).To(Succeed())
			})
		})

		Context("when the address is not listening", func() {
			BeforeEach(func() {
				port = "-1"
			})

			itReturnsHealthCheckError(portHealthCheck, 4, "failure to make TCP connection")
		})

		Context("when the server is slow in responding", func() {
			BeforeEach(func() {
				timeout = time.Nanosecond
				serverDelay = time.Second
			})

			itReturnsHealthCheckError(portHealthCheck, 64, "timeout when making TCP connection")
		})
	})

	Describe("http healthcheck", func() {
		httpHealthCheck := func() error {
			return hc.HTTPHealthCheck(ip)
		}

		BeforeEach(func() {
			uri = "/api/_ping"
		})

		Context("when the healthcheck is properly invoked", func() {
			Context("when the address is listening", func() {
				It("succeeds", func() {
					Expect(httpHealthCheck()).To(Succeed())
				})
			})

			Context("when the address returns error http code", func() {
				JustBeforeEach(func() {
					server.RouteToHandler("GET", "/api/_ping", ghttp.RespondWith(500, ""))
				})

				itReturnsHealthCheckError(httpHealthCheck, 6, "failure to get valid HTTP status code: 500")
			})

			Context("when the address is not listening", func() {
				BeforeEach(func() {
					port = "-1"
				})

				itReturnsHealthCheckError(httpHealthCheck, 5, "failure to make HTTP request")
			})

			Context("when the server is too slow to respond", func() {
				BeforeEach(func() {
					timeout = time.Nanosecond
					serverDelay = time.Second
				})

				itReturnsHealthCheckError(httpHealthCheck, 65, "timeout when making HTTP request")
			})
		})
	})
})

func getNonLoopbackIP() string {
	interfaces, err := net.Interfaces()
	Expect(err).NotTo(HaveOccurred())
	for _, intf := range interfaces {
		addrs, err := intf.Addrs()
		if err != nil {
			continue
		}

		for _, a := range addrs {
			if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
				if ipnet.IP.To4() != nil {
					return ipnet.IP.String()
				}
			}
		}
	}
	Fail("no non-loopback address found")
	panic("non-reachable")
}
<commit_msg>cleanup the testserver in the after each<commit_after>package healthcheck_test

import (
	"net"
	"net/http"
	"strconv"
	"time"

	"code.cloudfoundry.org/healthcheck"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/ghttp"
)

var _ = Describe("HealthCheck", func() {
	itReturnsHealthCheckError := func(healthCheck func() error, code int, reason string) {
		It("returns healthcheck error with code "+strconv.Itoa(code)+" with an appropriate message", func() {
			err := healthCheck()
			Expect(err).To(HaveOccurred())
			Expect(err).To(BeAssignableToTypeOf(healthcheck.HealthCheckError{}))
			hErr := err.(healthcheck.HealthCheckError)
			Expect(hErr.Code).To(Equal(code))
			Expect(hErr.Message).To(ContainSubstring(reason))
		})
	}

	var (
		server     *ghttp.Server
		serverAddr string

		ip string

		uri         string
		port        string
		timeout     time.Duration
		serverDelay time.Duration

		hc healthcheck.HealthCheck
	)

	BeforeEach(func() {
		ip = getNonLoopbackIP()
		server = ghttp.NewUnstartedServer()

		listener, err := net.Listen("tcp", ip+":0")
		Expect(err).NotTo(HaveOccurred())

		timeout = 100 * time.Millisecond
		serverDelay = 0

		server.HTTPTestServer.Listener = listener
		serverAddr = listener.Addr().String()
		_, port, err = net.SplitHostPort(serverAddr)
		Expect(err).NotTo(HaveOccurred())
	})

	JustBeforeEach(func() {
		server.RouteToHandler("GET", "/api/_ping",
			func(http.ResponseWriter, *http.Request) {
				time.Sleep(serverDelay)
			})
		server.Start()

		hc = healthcheck.NewHealthCheck("tcp", uri, port, timeout)
	})

	AfterEach(func() {
		if server != nil {
			server.CloseClientConnections()
			server.Close()
		}
	})

	Describe("check interfaces", func() {
		It("succeeds when there are healthy interfaces", func() {
			interfaces, err := net.Interfaces()
			Expect(err).NotTo(HaveOccurred())

			err = hc.CheckInterfaces(interfaces)
			Expect(err).NotTo(HaveOccurred())
		})

		Context("when the server is failing", func() {
			AfterEach(func() {
				server = nil
			})

			It("fails appropriately when there are unhealthy interfaces", func() {
				server.Close()

				interfaces, err := net.Interfaces()
				Expect(err).NotTo(HaveOccurred())

				err = hc.CheckInterfaces(interfaces)
				Expect(err).To(HaveOccurred())
				Expect(err).To(BeAssignableToTypeOf(healthcheck.HealthCheckError{}))

				hErr := err.(healthcheck.HealthCheckError)
				// fails with different error codes on Linux (4) or OSX (64)
				// check to see it was not the NO interfaces error (3)
				Expect(hErr.Code).ToNot(Equal(3))
			})
		})

		It("fails appropriately when there are no interfaces", func() {
			err := hc.CheckInterfaces(nil)
			Expect(err).To(HaveOccurred())
			Expect(err).To(BeAssignableToTypeOf(healthcheck.HealthCheckError{}))

			hErr := err.(healthcheck.HealthCheckError)
			Expect(hErr.Code).To(Equal(3))
			Expect(hErr.Message).To(ContainSubstring("failure to find suitable interface"))
		})
	})

	Describe("port healthcheck", func() {
		portHealthCheck := func() error {
			return hc.PortHealthCheck(ip)
		}

		BeforeEach(func() {
			uri = ""
		})

		Context("when the address is listening", func() {
			It("succeeds", func() {
				Expect(portHealthCheck()).To(Succeed())
			})
		})

		Context("when the address is not listening", func() {
			BeforeEach(func() {
				port = "-1"
			})

			itReturnsHealthCheckError(portHealthCheck, 4, "failure to make TCP connection")
		})

		Context("when the server is slow in responding", func() {
			BeforeEach(func() {
				timeout = time.Nanosecond
				serverDelay = time.Second
			})

			itReturnsHealthCheckError(portHealthCheck, 64, "timeout when making TCP connection")
		})
	})

	Describe("http healthcheck", func() {
		httpHealthCheck := func() error {
			return hc.HTTPHealthCheck(ip)
		}

		BeforeEach(func() {
			uri = "/api/_ping"
		})

		Context("when the healthcheck is properly invoked", func() {
			Context("when the address is listening", func() {
				It("succeeds", func() {
					Expect(httpHealthCheck()).To(Succeed())
				})
			})

			Context("when the address returns error http code", func() {
				JustBeforeEach(func() {
					server.RouteToHandler("GET", "/api/_ping", ghttp.RespondWith(500, ""))
				})

				itReturnsHealthCheckError(httpHealthCheck, 6, "failure to get valid HTTP status code: 500")
			})

			Context("when the address is not listening", func() {
				BeforeEach(func() {
					port = "-1"
				})

				itReturnsHealthCheckError(httpHealthCheck, 5, "failure to make HTTP request")
			})

			Context("when the server is too slow to respond", func() {
				BeforeEach(func() {
					timeout = time.Nanosecond
					serverDelay = time.Second
				})

				itReturnsHealthCheckError(httpHealthCheck, 65, "timeout when making HTTP request")
			})
		})
	})
})

func getNonLoopbackIP() string {
	interfaces, err := net.Interfaces()
	Expect(err).NotTo(HaveOccurred())
	for _, intf := range interfaces {
		addrs, err := intf.Addrs()
		if err != nil {
			continue
		}

		for _, a := range addrs {
			if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
				if ipnet.IP.To4() != nil {
					return ipnet.IP.String()
				}
			}
		}
	}
	Fail("no non-loopback address found")
	panic("non-reachable")
}
<|endoftext|>


<commit_before>package network

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
	dns "google.golang.org/api/dns/v1"
)

// DNSClient is a wrapper around Google Cloud DNS client.
type DNSClient struct {
	project string
	zone    string
	client  *dns.Service
}

// NewDNSClient connects new DNSClient and prepopulates some fields.
func NewDNSClient(ctx context.Context, zone string) (*DNSClient, error) {
	http, err := google.DefaultClient(ctx, dns.CloudPlatformScope)
	if err != nil {
		return nil, err
	}

	/* TODO(dotdoom): use this code once github.com/golang/oauth2/google f6093e3 is released
	var credentials *google.DefaultCredentials
	credentials, err = google.FindDefaultCredentials(ctx)
	*/
	var credentialsData []byte
	credentialsData, err = ioutil.ReadFile(".config/gcloud/application_default_credentials.json")
	if err != nil {
		return nil, err
	}
	var credentials struct {
		ProjectID string `json:"project_id"`
	}
	err = json.Unmarshal(credentialsData, &credentials)

	if err != nil {
		return nil, err
	}
	c := &DNSClient{
		project: credentials.ProjectID,
		zone:    zone,
	}
	c.client, err = dns.New(http)
	return c, err
}

func (c *DNSClient) pollCompletion(ctx context.Context, chg *dns.Change) error {
	startedAt, err := time.Parse(time.RFC3339Nano, chg.StartTime)
	if err != nil {
		log.Println("Failed to parse time:", err)
	}
	log.Printf("Change #%s started at %s with status: %s\n", chg.Id, startedAt.Local(), chg.Status)
	ticker := time.NewTicker(500 * time.Millisecond).C
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-ticker:
			chg, err = c.client.Changes.Get(c.project, c.zone, chg.Id).Context(ctx).Do()
			if err != nil {
				return err
			}
			log.Printf("Change #%s: %s\n", chg.Id, chg.Status)
			if chg.Status == "done" {
				return nil
			}
		}
	}
}

// UpdateDNS adds or replaces DNS record in Google Cloud DNS according to the arguments specified.
func (c *DNSClient) UpdateDNS(ctx context.Context, rrs *dns.ResourceRecordSet,
	waitPropagation bool) error {
	chg := &dns.Change{
		Additions: []*dns.ResourceRecordSet{rrs},
	}
	err := c.client.ResourceRecordSets.List(c.project, c.zone).Pages(ctx,
		func(page *dns.ResourceRecordSetsListResponse) error {
			for _, v := range page.Rrsets {
				if v.Name == rrs.Name && v.Type == rrs.Type {
					// TODO(dotdoom): early return if the record is unchanged.
					log.Println("Delete:", v.Rrdatas)
					chg.Deletions = append(chg.Deletions, v)
				}
			}
			return nil
		})
	if err != nil {
		return err
	}

	log.Println("Add:", rrs.Rrdatas)
	chg, err = c.client.Changes.Create(c.project, c.zone, chg).Context(ctx).Do()
	if err != nil {
		return err
	}

	if waitPropagation {
		// TODO(dotdoom): check propagation via DNS
		// TODO(dotdoom): use old record's TTL instead
		time.Sleep(time.Duration(rrs.Ttl) * time.Second)
	}

	return c.pollCompletion(ctx, chg)
}
<commit_msg>Do not delete DNS records that are what we need<commit_after>package network

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"reflect"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
	dns "google.golang.org/api/dns/v1"
)

// DNSClient is a wrapper around Google Cloud DNS client.
type DNSClient struct {
	project string
	zone    string
	client  *dns.Service
}

// NewDNSClient connects new DNSClient and prepopulates some fields.
func NewDNSClient(ctx context.Context, zone string) (*DNSClient, error) {
	http, err := google.DefaultClient(ctx, dns.CloudPlatformScope)
	if err != nil {
		return nil, err
	}

	/* TODO(dotdoom): use this code once github.com/golang/oauth2/google f6093e3 is released
	var credentials *google.DefaultCredentials
	credentials, err = google.FindDefaultCredentials(ctx)
	*/
	var credentialsData []byte
	credentialsData, err = ioutil.ReadFile(".config/gcloud/application_default_credentials.json")
	if err != nil {
		return nil, err
	}
	var credentials struct {
		ProjectID string `json:"project_id"`
	}
	err = json.Unmarshal(credentialsData, &credentials)

	if err != nil {
		return nil, err
	}
	c := &DNSClient{
		project: credentials.ProjectID,
		zone:    zone,
	}
	c.client, err = dns.New(http)
	return c, err
}

func (c *DNSClient) pollCompletion(ctx context.Context, chg *dns.Change) error {
	startedAt, err := time.Parse(time.RFC3339Nano, chg.StartTime)
	if err != nil {
		log.Println("Failed to parse time:", err)
	}
	log.Printf("Change #%s started at %s with status: %s\n", chg.Id, startedAt.Local(), chg.Status)
	ticker := time.NewTicker(500 * time.Millisecond).C
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-ticker:
			chg, err = c.client.Changes.Get(c.project, c.zone, chg.Id).Context(ctx).Do()
			if err != nil {
				return err
			}
			log.Printf("Change #%s: %s\n", chg.Id, chg.Status)
			if chg.Status == "done" {
				return nil
			}
		}
	}
}

// UpdateDNS adds or replaces DNS record in Google Cloud DNS according to the arguments specified.
func (c *DNSClient) UpdateDNS(ctx context.Context, rrs *dns.ResourceRecordSet,
	waitPropagation bool) error {
	chg := &dns.Change{
		Additions: []*dns.ResourceRecordSet{rrs},
	}
	maxTtl := rrs.Ttl
	err := c.client.ResourceRecordSets.List(c.project, c.zone).Pages(ctx,
		func(page *dns.ResourceRecordSetsListResponse) error {
			for _, v := range page.Rrsets {
				if v.Name == rrs.Name && v.Type == rrs.Type {
					if v.Ttl > maxTtl {
						maxTtl = v.Ttl
					}
					if len(chg.Additions) == 1 &&
						chg.Additions[0].Ttl == v.Ttl &&
						reflect.DeepEqual(chg.Additions[0].Rrdatas, v.Rrdatas) {
						log.Println("Keeping existing record:", v.Rrdatas)
						chg.Additions = chg.Additions[:0]
					} else {
						log.Println("Deleting existing record:", v.Rrdatas)
						chg.Deletions = append(chg.Deletions, v)
					}
				}
			}
			return nil
		})
	if err != nil {
		return err
	}

	if len(chg.Additions) == 0 && len(chg.Deletions) == 0 {
		return nil
	}

	if len(chg.Additions) == 1 {
		log.Println("Adding new record:", chg.Additions[0].Rrdatas)
	}

	chg, err = c.client.Changes.Create(c.project, c.zone, chg).Context(ctx).Do()
	if err != nil {
		return err
	}

	err = c.pollCompletion(ctx, chg)
	if err != nil {
		return err
	}

	if waitPropagation {
		time.Sleep(time.Duration(maxTtl) * time.Second)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"runtime"
	"syscall"
)

func main() {
	var dependencies []StaticLibrary

	// Parse flags
	version := flag.String("v", NGINX_VERSION, "nginx version")
	nginxConfPath := flag.String("c", "", "configuration file for building nginx")
	modulesConfPath := flag.String("m", "", "configuration file for 3rd party modules")
	workParentDir := flag.String("d", "", "working directory")
	verbose := flag.Bool("verbose", false, "verbose mode")
	pcreStatic := flag.Bool("pcre", false, "embedded PCRE statically")
	pcreVersion := flag.String("pcreversion", PCRE_VERSION, "PCRE version")
	openSSLStatic := flag.Bool("openssl", false, "embedded PCRE statically")
	openSSLVersion := flag.String("zlibversion", OPENSSL_VERSION, "OpenSSL version")
	zlibStatic := flag.Bool("zlib", false, "embedded zlib statically")
	zlibVersion := flag.String("opensslversion", ZLIB_VERSION, "zlib version")
	clear := flag.Bool("clear", false, "remove entries in working directory")
	jobs := flag.Int("j", runtime.NumCPU(), "number of jobs for buiding nginx")
	flag.Parse()

	// set verbose mode
	VerboseEnabled = *verbose

	nginxBuilder := makeBuilder(COMPONENT_NGINX, *version)
	pcreBuilder := makeBuilder(COMPONENT_PCRE, *pcreVersion)
	openSSLBuilder := makeBuilder(COMPONENT_OPENSSL, *openSSLVersion)
	zlibBuilder := makeBuilder(COMPONENT_ZLIB, *zlibVersion)

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

	versionCheck(*version)

	nginxConf, err := fileGetContents(*nginxConfPath)
	if err != nil {
		log.Fatal(err.Error())
	}

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

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

	if !fileExists(*workParentDir) {
		log.Fatal(fmt.Sprintf("working directory(%s) does not exist.", *workParentDir))
	}

	workDir := *workParentDir + "/" + *version
	if *clear {
		err := os.RemoveAll(workDir)
		if err != nil {
			log.Fatal(err.Error())
		}
	}

	if !fileExists(workDir) {
		err := os.Mkdir(workDir, 0755)
		if err != nil {
			log.Fatal("Failed to create working directory(%s) does not exist.", workDir)
		}
	}

	rootDir := saveCurrentDir()
	// cd workDir
	err = os.Chdir(workDir)
	if err != nil {
		log.Fatal(err.Error())
	}

	if *pcreStatic {
		err = downloadAndExtract(&pcreBuilder)
		if err != nil {
			log.Fatal(err.Error())
		}
		dependencies = append(dependencies,
			StaticLibrary{
				Name:    pcreBuilder.name(),
				Version: *pcreVersion,
				Option:  "--with-pcre"})
	}

	if *openSSLStatic {
		err := downloadAndExtract(&openSSLBuilder)
		if err != nil {
			log.Fatal(err.Error())
		}
		dependencies = append(dependencies,
			StaticLibrary{
				Name:    openSSLBuilder.name(),
				Version: *openSSLVersion,
				Option:  "--with-openssl"})
	}

	if *zlibStatic {
		err := downloadAndExtract(&zlibBuilder)
		if err != nil {
			log.Fatal(err.Error())
		}
		dependencies = append(dependencies,
			StaticLibrary{
				Name:    zlibBuilder.name(),
				Version: *zlibVersion,
				Option:  "--with-zlib"})
	}

	err = downloadAndExtract(&nginxBuilder)
	if err != nil {
		log.Fatal(err.Error())
	}
	if len(modules3rd) > 0 {
		log.Println("Download 3rd Party Modules.....")
		for _, m := range modules3rd {
			if fileExists(m.Name) {
				log.Printf("%s already exists.", m.Name)
				continue
			}
			log.Printf("Download %s.....", m.Name)
			err = downloadModule3rd(m)
			if err != nil {
				log.Fatalf("Failed to download %s", m.Name)
			}

			fmt.Println(m.Rev)
			if m.Rev != "" {
				dir := saveCurrentDir()
				os.Chdir(m.Name)
				switchRev(m.Rev)
				prevShell(m.PrevSh)
				os.Chdir(dir)
			}
		}
	}

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

	log.Println("Generate configure script for nginx.....")
	err = nginxBuilder.configureGen(nginxConf, modules3rd, dependencies)
	if err != nil {
		log.Fatal("Failed to generate configure script for nginx")
	}
	log.Println("Configure nginx.....")
	err = configure()
	if err != nil {
		log.Fatal("Failed to configure nginx")
	}

	log.Println("Build nginx.....")
	if *openSSLStatic {
		// This is for protecting a failure of building nginx with OpenSSL
		// Unfortunately build of nginx with static OpenSSL fails
		// by multi-CPUs
		*jobs = 1
	}
	err = make(*jobs)
	if err != nil {
		fmt.Println(err.Error())
		log.Fatal("Failed to build nginx")
	}

	// cd rootDir
	os.Chdir(rootDir)

	printLastMsg(workDir, nginxBuilder.sourcePath())
}
<commit_msg>add option -versions<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"runtime"
	"syscall"
)

func main() {
	var dependencies []StaticLibrary

	// Parse flags
	version := flag.String("v", NGINX_VERSION, "nginx version")
	nginxConfPath := flag.String("c", "", "configuration file for building nginx")
	modulesConfPath := flag.String("m", "", "configuration file for 3rd party modules")
	workParentDir := flag.String("d", "", "working directory")
	verbose := flag.Bool("verbose", false, "verbose mode")
	pcreStatic := flag.Bool("pcre", false, "embedded PCRE statically")
	pcreVersion := flag.String("pcreversion", PCRE_VERSION, "PCRE version")
	openSSLStatic := flag.Bool("openssl", false, "embedded PCRE statically")
	openSSLVersion := flag.String("zlibversion", OPENSSL_VERSION, "OpenSSL version")
	zlibStatic := flag.Bool("zlib", false, "embedded zlib statically")
	zlibVersion := flag.String("opensslversion", ZLIB_VERSION, "zlib version")
	clear := flag.Bool("clear", false, "remove entries in working directory")
	jobs := flag.Int("j", runtime.NumCPU(), "number of jobs for buiding nginx")
	versions := flag.Bool("versions", false, "show nginx versions")
	flag.Parse()

	if *versions {
                showVersions()
		os.Exit(0)
	}

	// set verbose mode
	VerboseEnabled = *verbose

	nginxBuilder := makeBuilder(COMPONENT_NGINX, *version)
	pcreBuilder := makeBuilder(COMPONENT_PCRE, *pcreVersion)
	openSSLBuilder := makeBuilder(COMPONENT_OPENSSL, *openSSLVersion)
	zlibBuilder := makeBuilder(COMPONENT_ZLIB, *zlibVersion)

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

	versionCheck(*version)

	nginxConf, err := fileGetContents(*nginxConfPath)
	if err != nil {
		log.Fatal(err.Error())
	}

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

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

	if !fileExists(*workParentDir) {
		log.Fatal(fmt.Sprintf("working directory(%s) does not exist.", *workParentDir))
	}

	workDir := *workParentDir + "/" + *version
	if *clear {
		err := os.RemoveAll(workDir)
		if err != nil {
			log.Fatal(err.Error())
		}
	}

	if !fileExists(workDir) {
		err := os.Mkdir(workDir, 0755)
		if err != nil {
			log.Fatal("Failed to create working directory(%s) does not exist.", workDir)
		}
	}

	rootDir := saveCurrentDir()
	// cd workDir
	err = os.Chdir(workDir)
	if err != nil {
		log.Fatal(err.Error())
	}

	if *pcreStatic {
		err = downloadAndExtract(&pcreBuilder)
		if err != nil {
			log.Fatal(err.Error())
		}
		dependencies = append(dependencies,
			StaticLibrary{
				Name:    pcreBuilder.name(),
				Version: *pcreVersion,
				Option:  "--with-pcre"})
	}

	if *openSSLStatic {
		err := downloadAndExtract(&openSSLBuilder)
		if err != nil {
			log.Fatal(err.Error())
		}
		dependencies = append(dependencies,
			StaticLibrary{
				Name:    openSSLBuilder.name(),
				Version: *openSSLVersion,
				Option:  "--with-openssl"})
	}

	if *zlibStatic {
		err := downloadAndExtract(&zlibBuilder)
		if err != nil {
			log.Fatal(err.Error())
		}
		dependencies = append(dependencies,
			StaticLibrary{
				Name:    zlibBuilder.name(),
				Version: *zlibVersion,
				Option:  "--with-zlib"})
	}

	err = downloadAndExtract(&nginxBuilder)
	if err != nil {
		log.Fatal(err.Error())
	}
	if len(modules3rd) > 0 {
		log.Println("Download 3rd Party Modules.....")
		for _, m := range modules3rd {
			if fileExists(m.Name) {
				log.Printf("%s already exists.", m.Name)
				continue
			}
			log.Printf("Download %s.....", m.Name)
			err = downloadModule3rd(m)
			if err != nil {
				log.Fatalf("Failed to download %s", m.Name)
			}

			fmt.Println(m.Rev)
			if m.Rev != "" {
				dir := saveCurrentDir()
				os.Chdir(m.Name)
				switchRev(m.Rev)
				prevShell(m.PrevSh)
				os.Chdir(dir)
			}
		}
	}

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

	log.Println("Generate configure script for nginx.....")
	err = nginxBuilder.configureGen(nginxConf, modules3rd, dependencies)
	if err != nil {
		log.Fatal("Failed to generate configure script for nginx")
	}
	log.Println("Configure nginx.....")
	err = configure()
	if err != nil {
		log.Fatal("Failed to configure nginx")
	}

	log.Println("Build nginx.....")
	if *openSSLStatic {
		// This is for protecting a failure of building nginx with OpenSSL
		// Unfortunately build of nginx with static OpenSSL fails
		// by multi-CPUs
		*jobs = 1
	}
	err = make(*jobs)
	if err != nil {
		fmt.Println(err.Error())
		log.Fatal("Failed to build nginx")
	}

	// cd rootDir
	os.Chdir(rootDir)

	printLastMsg(workDir, nginxBuilder.sourcePath())
}
<|endoftext|>