<commit_before>package bearychat

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"sort"
	"strings"

	"github.com/go-akka/configuration"
	"github.com/gogap/bearychat/internal"
)

const (
	OUTGOING = "gogap-outgoing"
)

var (
	triggerFuncs = make(map[string]NewTriggerFunc)
)

var (
	ErrTriggerDriverAlreadyRegistered = errors.New("trigger driver already registered")
	ErrNewTriggerFuncIsNil            = errors.New("trigger func is nil")
	ErrBreakOnly                      = errors.New("break only")
)

type ErrorHandlerFunc func(cause error) Message

type NewTriggerFunc func(word string, config *configuration.Config) (Trigger, error)

type Outgoing struct {
	triggers map[string]*internal.Command // map[word]Command tree

	settings *OutgoingSettings

	config       *configuration.Config
	errorHandler ErrorHandlerFunc
}

func init() {
	RegisterTriggerDriver(OUTGOING, NewOutgoingTrigger)
}

func TriggerDrivers() []string {
	var ret []string
	for k, _ := range triggerFuncs {
		ret = append(ret, k)
	}

	sort.Sort(sort.StringSlice(ret))

	return ret
}

func RegisterTriggerDriver(name string, fn NewTriggerFunc) {
	if fn == nil {
		panic(ErrNewTriggerFuncIsNil)
	}

	_, exist := triggerFuncs[name]
	if exist {
		panic(ErrTriggerDriverAlreadyRegistered)
	}
	triggerFuncs[name] = fn
}

func NewOutgoing(config *configuration.Config) (*Outgoing, error) {

	outgoing := &Outgoing{
		triggers: make(map[string]*internal.Command),
		config:   config,
		settings: NewOutgoingSettings(config),
	}

	outgoing.errorHandler = outgoing.handleError

	outgoing.autoBind(config)

	return outgoing, nil
}

func NewOutgoingTrigger(word string, config *configuration.Config) (Trigger, error) {
	return NewOutgoing(config)
}

func (p *Outgoing) BindTrigger(config *configuration.Config) *Outgoing {
	triggerWord := config.GetString("word")
	triggerWord = strings.TrimSpace(triggerWord)

	commands := config.GetStringList("commands")

	drivers := config.GetStringList("drivers")

	if len(triggerWord) == 0 {
		return p
	}

	if len(drivers) == 0 {
		return p
	}

	names := removeDuplicates(drivers)

	var triggers []interface{}

	for i := 0; i < len(names); i++ {
		triggerDriver, exist := triggerFuncs[names[i]]
		if !exist {
			panic(fmt.Errorf("the trigger of %s did not exist", names[i]))
		}

		trigger, err := triggerDriver(triggerWord, config.GetConfig(names[i]))
		if err != nil {
			panic(err)
		}

		triggers = append(triggers, trigger)
	}

	root, exist := p.triggers[triggerWord]
	if !exist {
		root = &internal.Command{}
	}

	node := root.Match(commands...)

	if len(node.Values) > 0 {
		panic(fmt.Errorf("command alrady has triggers: %s", strings.Join(commands, " ")))
	}

	subCommands := commands[len(node.Commands()):]

	for i := 0; i < len(subCommands); i++ {

		child := &internal.Command{
			Name: subCommands[i],
		}

		if i+1 == len(subCommands) {
			child.Values = triggers
		}

		node.AddChild(child)
		node = child
	}

	p.triggers[triggerWord] = root

	return p
}

func (p *Outgoing) SetErrorHandler(handler ErrorHandlerFunc) {
	p.errorHandler = handler
	if p.errorHandler == nil {
		p.errorHandler = p.handleError
	}
}

func (p *Outgoing) Handle(req *OutgoingRequest, msg *Message) error {

	word := strings.TrimSpace(req.TriggerWord)
	treeRoot, exist := p.triggers[word]

	if !exist {
		return fmt.Errorf("trigger of %s not exist!", word)
	}

	args := req.Args()

	node := treeRoot.Match(args...)

	if node == treeRoot {
		return fmt.Errorf("unknown sub-command: %s", strings.Join(args, " "))
	}

	if len(node.Values) == 0 {
		return fmt.Errorf("unfinished sub-command")
	}

	req.Commands = node.Commands()

	for i := 0; i < len(node.Values); i++ {
		if err := node.Values[i].(Trigger).Handle(req, msg); err != nil {
			return err
		}
	}

	return nil
}

func (p *Outgoing) HandleHttpRequest(rw http.ResponseWriter, req *http.Request) {

	if req.Method != "POST" {
		rw.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	decoder := json.NewDecoder(req.Body)
	decoder.UseNumber()

	triggerReq := &OutgoingRequest{}
	err := decoder.Decode(triggerReq)

	msg := Message{}
	if err != nil {
		msg = p.errorHandler(err)
	} else {
		err = p.Handle(triggerReq, &msg)
		if err == ErrBreakOnly {
			err = nil
		}

		if err != nil {
			msg = p.errorHandler(err)
		}
	}

	jsonMsg, _ := json.Marshal(msg)

	rw.Header().Set("Content-Type", "application/json")
	rw.Write(jsonMsg)
}

func (p *Outgoing) handleError(cause error) Message {
	return Message{
		Text: cause.Error(),
	}
}

func (p *Outgoing) autoBind(config *configuration.Config) {
	if config == nil {
		return
	}

	keys := p.config.Root().GetObject().GetKeys()

	for i := 0; i < len(keys); i++ {
		p.BindTrigger(p.config.GetConfig(keys[i]))
	}
}

func removeDuplicates(elements []string) []string {
	encountered := map[string]bool{}
	result := []string{}

	for _, v := range elements {
		if _, exist := encountered[v]; !exist {
			encountered[v] = true
			result = append(result, v)
		}
	}

	return result
}
<commit_msg>add no content support<commit_after>package bearychat

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"sort"
	"strings"

	"github.com/go-akka/configuration"
	"github.com/gogap/bearychat/internal"
)

const (
	OUTGOING = "gogap-outgoing"
)

var (
	triggerFuncs = make(map[string]NewTriggerFunc)
)

var (
	ErrTriggerDriverAlreadyRegistered = errors.New("trigger driver already registered")
	ErrNewTriggerFuncIsNil            = errors.New("trigger func is nil")
	ErrBreakOnly                      = errors.New("break only")
	ErrNoContent                      = errors.New("no content")
)

type ErrorHandlerFunc func(cause error) Message

type NewTriggerFunc func(word string, config *configuration.Config) (Trigger, error)

type Outgoing struct {
	triggers map[string]*internal.Command // map[word]Command tree

	settings *OutgoingSettings

	config       *configuration.Config
	errorHandler ErrorHandlerFunc
}

func init() {
	RegisterTriggerDriver(OUTGOING, NewOutgoingTrigger)
}

func TriggerDrivers() []string {
	var ret []string
	for k, _ := range triggerFuncs {
		ret = append(ret, k)
	}

	sort.Sort(sort.StringSlice(ret))

	return ret
}

func RegisterTriggerDriver(name string, fn NewTriggerFunc) {
	if fn == nil {
		panic(ErrNewTriggerFuncIsNil)
	}

	_, exist := triggerFuncs[name]
	if exist {
		panic(ErrTriggerDriverAlreadyRegistered)
	}
	triggerFuncs[name] = fn
}

func NewOutgoing(config *configuration.Config) (*Outgoing, error) {

	outgoing := &Outgoing{
		triggers: make(map[string]*internal.Command),
		config:   config,
		settings: NewOutgoingSettings(config),
	}

	outgoing.errorHandler = outgoing.handleError

	outgoing.autoBind(config)

	return outgoing, nil
}

func NewOutgoingTrigger(word string, config *configuration.Config) (Trigger, error) {
	return NewOutgoing(config)
}

func (p *Outgoing) BindTrigger(config *configuration.Config) *Outgoing {
	triggerWord := config.GetString("word")
	triggerWord = strings.TrimSpace(triggerWord)

	commands := config.GetStringList("commands")

	drivers := config.GetStringList("drivers")

	if len(triggerWord) == 0 {
		return p
	}

	if len(drivers) == 0 {
		return p
	}

	names := removeDuplicates(drivers)

	var triggers []interface{}

	for i := 0; i < len(names); i++ {
		triggerDriver, exist := triggerFuncs[names[i]]
		if !exist {
			panic(fmt.Errorf("the trigger of %s did not exist", names[i]))
		}

		trigger, err := triggerDriver(triggerWord, config.GetConfig(names[i]))
		if err != nil {
			panic(err)
		}

		triggers = append(triggers, trigger)
	}

	root, exist := p.triggers[triggerWord]
	if !exist {
		root = &internal.Command{}
	}

	node := root.Match(commands...)

	if len(node.Values) > 0 {
		panic(fmt.Errorf("command alrady has triggers: %s", strings.Join(commands, " ")))
	}

	subCommands := commands[len(node.Commands()):]

	for i := 0; i < len(subCommands); i++ {

		child := &internal.Command{
			Name: subCommands[i],
		}

		if i+1 == len(subCommands) {
			child.Values = triggers
		}

		node.AddChild(child)
		node = child
	}

	p.triggers[triggerWord] = root

	return p
}

func (p *Outgoing) SetErrorHandler(handler ErrorHandlerFunc) {
	p.errorHandler = handler
	if p.errorHandler == nil {
		p.errorHandler = p.handleError
	}
}

func (p *Outgoing) Handle(req *OutgoingRequest, msg *Message) error {

	word := strings.TrimSpace(req.TriggerWord)
	treeRoot, exist := p.triggers[word]

	if !exist {
		return fmt.Errorf("trigger of %s not exist!", word)
	}

	args := req.Args()

	node := treeRoot.Match(args...)

	if node == treeRoot {
		return fmt.Errorf("unknown sub-command: %s", strings.Join(args, " "))
	}

	if len(node.Values) == 0 {
		return fmt.Errorf("unfinished sub-command")
	}

	req.Commands = node.Commands()

	for i := 0; i < len(node.Values); i++ {
		if err := node.Values[i].(Trigger).Handle(req, msg); err != nil {
			return err
		}
	}

	return nil
}

func (p *Outgoing) HandleHttpRequest(rw http.ResponseWriter, req *http.Request) {

	if req.Method != "POST" {
		rw.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	decoder := json.NewDecoder(req.Body)
	decoder.UseNumber()

	triggerReq := &OutgoingRequest{}
	err := decoder.Decode(triggerReq)

	statusCode := 200

	msg := Message{}
	if err != nil {
		msg = p.errorHandler(err)
	} else {
		err = p.Handle(triggerReq, &msg)
		if err == ErrBreakOnly {
			err = nil
		} else if err == ErrNoContent {
			err = nil
			statusCode = 204
		}

		if err != nil {
			msg = p.errorHandler(err)
		}
	}

	jsonMsg, _ := json.Marshal(msg)

	rw.Header().Set("Content-Type", "application/json")

	if statusCode != 200 {
		rw.WriteHeader(statusCode)
	} else {
		rw.Write(jsonMsg)
	}
}

func (p *Outgoing) handleError(cause error) Message {
	return Message{
		Text: cause.Error(),
	}
}

func (p *Outgoing) autoBind(config *configuration.Config) {
	if config == nil {
		return
	}

	keys := p.config.Root().GetObject().GetKeys()

	for i := 0; i < len(keys); i++ {
		p.BindTrigger(p.config.GetConfig(keys[i]))
	}
}

func removeDuplicates(elements []string) []string {
	encountered := map[string]bool{}
	result := []string{}

	for _, v := range elements {
		if _, exist := encountered[v]; !exist {
			encountered[v] = true
			result = append(result, v)
		}
	}

	return result
}
<|endoftext|>


<commit_before>package formatters

import (
	"log"

	"github.com/mitchellh/mapstructure"
)

var EventProcessors = map[string]Initializer{}

var EventProcessorTypes = []string{
	"event-add-tag",
	"event-convert",
	"event-date-string",
	"event-delete",
	"event-drop",
	"event-override-ts",
	"event-strings",
	"event-to-tag",
	"event-write",
}

type Initializer func() EventProcessor

func Register(name string, initFn Initializer) {
	EventProcessors[name] = initFn
}

type EventProcessor interface {
	Init(interface{}, *log.Logger) error
	Apply(*EventMsg)
}

func DecodeConfig(src, dst interface{}) error {
	decoder, err := mapstructure.NewDecoder(
		&mapstructure.DecoderConfig{
			DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
			Result:     dst,
		},
	)
	if err != nil {
		return err
	}
	return decoder.Decode(src)
}
<commit_msg>update processor interface{}<commit_after>package formatters

import (
	"log"

	"github.com/mitchellh/mapstructure"
)

var EventProcessors = map[string]Initializer{}

var EventProcessorTypes = []string{
	"event-add-tag",
	"event-convert",
	"event-date-string",
	"event-delete",
	"event-drop",
	"event-override-ts",
	"event-strings",
	"event-to-tag",
	"event-write",
}

type Initializer func() EventProcessor

func Register(name string, initFn Initializer) {
	EventProcessors[name] = initFn
}

type EventProcessor interface {
	Init(interface{}, *log.Logger) error
	Apply(...*EventMsg) []*EventMsg
}

func DecodeConfig(src, dst interface{}) error {
	decoder, err := mapstructure.NewDecoder(
		&mapstructure.DecoderConfig{
			DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
			Result:     dst,
		},
	)
	if err != nil {
		return err
	}
	return decoder.Decode(src)
}
<|endoftext|>


<commit_before>package requesttree

import (
	"golang.org/x/net/context"

	"github.com/mondough/mercury"
	terrors "github.com/mondough/typhon/errors"
)

const (
	parentIdHeader = "Parent-Request-ID"
	reqIdCtxKey    = "Request-ID"
)

type requestTreeMiddleware struct{}

func (m requestTreeMiddleware) ProcessClientRequest(req mercury.Request) mercury.Request {
	if req.Headers()[parentIdHeader] == "" { // Don't overwrite an exiting header
		if parentId, ok := req.Context().Value(reqIdCtxKey).(string); ok && parentId != "" {
			req.SetHeader(parentIdHeader, parentId)
		}
	}

	// Pass through the current service and endpoint as the origin of this request
	if svc, ok := req.Value("Current-Service").(string); ok {
		req.SetHeader("Origin-Service", svc)
	}
	if ept, ok := req.Value("Current-Endpoint").(string); ok {
		req.SetHeader("Origin-Endpoint", ept)
	}

	return req
}

func (m requestTreeMiddleware) ProcessClientResponse(rsp mercury.Response, ctx context.Context) mercury.Response {
	return rsp
}

func (m requestTreeMiddleware) ProcessClientError(err *terrors.Error, ctx context.Context) {}

func (m requestTreeMiddleware) ProcessServerRequest(req mercury.Request) (mercury.Request, mercury.Response) {
	req.SetContext(context.WithValue(req.Context(), reqIdCtxKey, req.Id()))
	if v := req.Headers()[parentIdHeader]; v != "" {
		req.SetContext(context.WithValue(req.Context(), parentIdCtxKey, v))
	}

	// Set the current service and endpoint into the context
	req.SetContext(context.WithValue(req.Context(), "Current-Service", req.Service()))
	req.SetContext(context.WithValue(req.Context(), "Current-Endpoint", req.Endpoint()))

	// Set the originator into the context
	req.SetContext(context.WithValue(req.Context(), "Origin-Service", req.Headers()["Origin-Service"]))
	req.SetContext(context.WithValue(req.Context(), "Origin-Endpoint", req.Headers()["Origin-Endpoint"]))

	return req, nil
}

func (m requestTreeMiddleware) ProcessServerResponse(rsp mercury.Response, ctx context.Context) mercury.Response {
	if v, ok := ctx.Value(parentIdCtxKey).(string); ok && v != "" && rsp != nil {
		rsp.SetHeader(parentIdHeader, v)
	}
	return rsp
}

func Middleware() requestTreeMiddleware {
	return requestTreeMiddleware{}
}
<commit_msg>Pull out context keys to consts in requesttree<commit_after>package requesttree

import (
	"golang.org/x/net/context"

	"github.com/mondough/mercury"
	terrors "github.com/mondough/typhon/errors"
)

const (
	parentIdHeader = "Parent-Request-ID"
	reqIdCtxKey    = "Request-ID"

	currentServiceHeader  = "Current-Service"
	currentEndpointHeader = "Current-Endpoint"
	originServiceHeader   = "Origin-Service"
	originEndpointHeader  = "Origin-Endpoint"
)

type requestTreeMiddleware struct{}

func (m requestTreeMiddleware) ProcessClientRequest(req mercury.Request) mercury.Request {
	if req.Headers()[parentIdHeader] == "" { // Don't overwrite an exiting header
		if parentId, ok := req.Context().Value(reqIdCtxKey).(string); ok && parentId != "" {
			req.SetHeader(parentIdHeader, parentId)
		}
	}

	// Pass through the current service and endpoint as the origin of this request
	if svc, ok := req.Value(currentServiceHeader).(string); ok {
		req.SetHeader(originServiceHeader, svc)
	}
	if ept, ok := req.Value(currentEndpointHeader).(string); ok {
		req.SetHeader(originEndpointHeader, ept)
	}

	return req
}

func (m requestTreeMiddleware) ProcessClientResponse(rsp mercury.Response, ctx context.Context) mercury.Response {
	return rsp
}

func (m requestTreeMiddleware) ProcessClientError(err *terrors.Error, ctx context.Context) {}

func (m requestTreeMiddleware) ProcessServerRequest(req mercury.Request) (mercury.Request, mercury.Response) {
	req.SetContext(context.WithValue(req.Context(), reqIdCtxKey, req.Id()))
	if v := req.Headers()[parentIdHeader]; v != "" {
		req.SetContext(context.WithValue(req.Context(), parentIdCtxKey, v))
	}

	// Set the current service and endpoint into the context
	req.SetContext(context.WithValue(req.Context(), currentServiceHeader, req.Service()))
	req.SetContext(context.WithValue(req.Context(), currentEndpointHeader, req.Endpoint()))

	// Set the originator into the context
	req.SetContext(context.WithValue(req.Context(), originServiceHeader, req.Headers()[originServiceHeader]))
	req.SetContext(context.WithValue(req.Context(), originEndpointHeader, req.Headers()[originEndpointHeader]))

	return req, nil
}

func (m requestTreeMiddleware) ProcessServerResponse(rsp mercury.Response, ctx context.Context) mercury.Response {
	if v, ok := ctx.Value(parentIdCtxKey).(string); ok && v != "" && rsp != nil {
		rsp.SetHeader(parentIdHeader, v)
	}
	return rsp
}

func Middleware() requestTreeMiddleware {
	return requestTreeMiddleware{}
}
<|endoftext|>


<commit_before>package requesttree

import (
	"golang.org/x/net/context"

	"github.com/obeattie/mercury"
	"github.com/obeattie/mercury/client"
	"github.com/obeattie/mercury/server"
)

const (
	parentIdHeader = "Parent-Request-ID"
	reqIdCtxKey    = "Request-ID"
)

var sharedMiddleware = &rtm{}

type ParentRequestIdMiddleware interface {
	server.ServerMiddleware
	client.ClientMiddleware
}

type rtm struct{}

func (m *rtm) ProcessClientRequest(req mercury.Request) mercury.Request {
	if req.Headers()[parentIdHeader] == "" { // Don't overwrite an exiting header
		if parentId, ok := req.Context().Value(reqIdCtxKey).(string); ok && parentId != "" {
			req.SetHeader(parentIdHeader, parentId)
		}
	}
	return req
}

func (m *rtm) ProcessClientResponse(rsp mercury.Response, ctx context.Context) mercury.Response {
	return rsp
}

func (m *rtm) ProcessServerRequest(req mercury.Request) mercury.Request {
	req.SetContext(context.WithValue(req.Context(), reqIdCtxKey, req.Id()))
	if v := req.Headers()[parentIdHeader]; v != "" {
		req.SetContext(context.WithValue(req.Context(), parentIdCtxKey, v))
	}
	return req
}

func (m *rtm) ProcessServerResponse(rsp mercury.Response, ctx context.Context) mercury.Response {
	if v, ok := ctx.Value(parentIdCtxKey).(string); ok && v != "" {
		rsp.SetHeader(parentIdHeader, v)
	}
	return rsp
}

func Middleware() ParentRequestIdMiddleware {
	return sharedMiddleware
}
<commit_msg>RequestTree… can just be a struct<commit_after>package requesttree

import (
	"golang.org/x/net/context"

	"github.com/obeattie/mercury"
)

const (
	parentIdHeader = "Parent-Request-ID"
	reqIdCtxKey    = "Request-ID"
)

type requestTreeMiddleware struct{}

func (m requestTreeMiddleware) ProcessClientRequest(req mercury.Request) mercury.Request {
	if req.Headers()[parentIdHeader] == "" { // Don't overwrite an exiting header
		if parentId, ok := req.Context().Value(reqIdCtxKey).(string); ok && parentId != "" {
			req.SetHeader(parentIdHeader, parentId)
		}
	}
	return req
}

func (m requestTreeMiddleware) ProcessClientResponse(rsp mercury.Response, ctx context.Context) mercury.Response {
	return rsp
}

func (m requestTreeMiddleware) ProcessServerRequest(req mercury.Request) mercury.Request {
	req.SetContext(context.WithValue(req.Context(), reqIdCtxKey, req.Id()))
	if v := req.Headers()[parentIdHeader]; v != "" {
		req.SetContext(context.WithValue(req.Context(), parentIdCtxKey, v))
	}
	return req
}

func (m requestTreeMiddleware) ProcessServerResponse(rsp mercury.Response, ctx context.Context) mercury.Response {
	if v, ok := ctx.Value(parentIdCtxKey).(string); ok && v != "" {
		rsp.SetHeader(parentIdHeader, v)
	}
	return rsp
}

func Middleware() requestTreeMiddleware {
	return requestTreeMiddleware{}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strconv"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/gorilla/context"

	pg "github.com/mozilla/tls-observatory/database"
	"github.com/mozilla/tls-observatory/logger"
)

var scanRefreshRate float64

type scanResponse struct {
	ID int64 `json:"scan_id"`
}

// ScanHandler handles the /scans endpoint of the api
// It initiates new scans and returns created scans ids to be used against other endpoints.
func ScanHandler(w http.ResponseWriter, r *http.Request) {
	var (
		status int
		err    error
	)

	defer func() {
		if nil != err {
			http.Error(w, err.Error(), status)
		}
	}()

	log := logger.GetLogger()
	status = http.StatusInternalServerError

	log.WithFields(logrus.Fields{
		"form values": r.Form,
		"headers":     r.Header,
	}).Debug("Scan endpoint received request")

	val, ok := context.GetOk(r, dbKey)
	if !ok {
		log.Error("Could not find db in request context")
		err = errors.New("Could not access database.")
		return
	}

	db := val.(*pg.DB)

	domain := r.FormValue("target")
	if !validateDomain(domain) {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, "")
	}

	rescan := false
	if r.FormValue("rescan") == "true" {
		rescan = true
	}

	previd, prevtime, err := db.GetLastScanTimeForTarget(domain)
	if err != nil {
		log.WithFields(logrus.Fields{
			"domain": domain,
			"error":  err.Error(),
		}).Error("Could not get last scan for target")
		err = errors.New("Could not get last scan for target")
		return
	}

	now := time.Now().UTC()

	if previd != -1 { // check if previous scan exists
		if now.Sub(prevtime).Hours() <= scanRefreshRate {
			if !rescan {
				// no rescan requested so return previous scan in any case
				// this includes the rate limiting with no rescan case
				sr := scanResponse{
					ID: previd,
				}
				respBody, _ := json.Marshal(sr)
				w.Header().Set("Content-Type", "application/json")
				w.WriteHeader(http.StatusOK)
				w.Write(respBody)
				return
			}

			// forced rescan has been requested
			if now.Sub(prevtime).Minutes() <= 3 { // rate limit scan requests for same target
				if rescan {
					w.WriteHeader(429) // 429 http status code is not exported ( https://codereview.appspot.com/7678043/ )
					w.Header().Set("Content-Type", "text/html")
					fmt.Fprint(w, fmt.Sprintf("Last scan for target %s initiated %s ago.\nPlease try again in %s.\n", domain, now.Sub(prevtime), 3*time.Minute-now.Sub(prevtime)))
					return
				}
			}
		}
	}

	//initiating a new scan
	scan, err := db.NewScan(domain, -1) //no replay
	if err != nil {
		log.WithFields(logrus.Fields{
			"domain": domain,
			"error":  err.Error(),
		}).Error("Could not create new scan")
		err = errors.New("Could not create new scan")
		return
	}
	sr := scanResponse{
		ID: scan.ID,
	}
	respBody, err := json.Marshal(sr)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": scan.ID,
			"error":   err.Error(),
		}).Error("Could not Marshal scan")

		err = errors.New("Could not process the requested scan")
		return
	}
	setResponseHeader(w)
	w.WriteHeader(http.StatusOK)
	w.Write(respBody)
}

// ResultHandler handles the results endpoint of the api.
// It has a scan id as input and returns its results ( if available )
func ResultHandler(w http.ResponseWriter, r *http.Request) {
	var (
		status int
		err    error
	)

	defer func() {
		if nil != err {
			http.Error(w, err.Error(), status)
		}
	}()

	log := logger.GetLogger()
	status = http.StatusInternalServerError

	log.WithFields(logrus.Fields{
		"form values": r.Form,
		"headers":     r.Header,
	}).Debug("Results endpoint received request")

	val, ok := context.GetOk(r, dbKey)
	if !ok {
		log.Error("Could not find db in request context")
		err = errors.New("Could not access database.")
		return
	}

	db := val.(*pg.DB)

	idStr := r.FormValue("id")

	id, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": idStr,
			"error":   err.Error(),
		}).Error("Could not parse scanid")
		err = errors.New("Could not parse provided scan id")
		status = http.StatusBadRequest
		return
	}

	scan, err := db.GetScanByID(id)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": id,
			"error":   err.Error(),
		}).Error("Could not get scan from database")
		err = errors.New("Could not access database to get requested scan.")
		return
	}

	if scan.ID == -1 {
		log.WithFields(logrus.Fields{
			"scan_id": id,
		}).Debug("Did not find scan in database")

		err = errors.New("Could not find a scan with the id you provided.")
		status = http.StatusNotFound
		return
	}

	jsScan, err := json.Marshal(scan)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": id,
			"error":   err.Error(),
		}).Error("Could not Marshal scan")

		err = errors.New("Could not process the requested scan")
		return
	}
	setResponseHeader(w)
	w.WriteHeader(http.StatusOK)
	fmt.Fprint(w, string(jsScan))
}

// CertificateHandler handles the /certificate endpoint of the api.
// It queries the database for the provided cert ids and returns results in JSON.
func CertificateHandler(w http.ResponseWriter, r *http.Request) {
	var (
		status int
		err    error
	)

	defer func() {
		if nil != err {
			http.Error(w, err.Error(), status)
		}
	}()

	log := logger.GetLogger()
	status = http.StatusInternalServerError

	log.WithFields(logrus.Fields{
		"form values": r.Form.Encode(),
		"headers":     r.Header,
	}).Debug("Certificate Endpoint received request")

	val, ok := context.GetOk(r, dbKey)
	if !ok {
		log.Error("Could not find db in request context")
		err = errors.New("Could not access database.")
		return
	}

	db := val.(*pg.DB)

	idStr := r.FormValue("id")

	id, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		log.WithFields(logrus.Fields{
			"cert_id": id,
			"error":   err.Error(),
		}).Error("Could not parse certificate id")

		status = http.StatusBadRequest
		err = errors.New("Could not parse provided certificate id")
		return
	}

	cert, err := db.GetCertByID(id)
	if err != nil {
		log.WithFields(logrus.Fields{
			"cert_id": id,
			"error":   err.Error(),
		}).Error("Could not get cert from database")

		err = errors.New("Could not access database to get requested certificate")
		return
	}

	jsScan, err := json.Marshal(cert)
	if err != nil {
		log.WithFields(logrus.Fields{
			"cert_id": id,
			"error":   err.Error(),
		}).Error("Could not Marshal cert")

		err = errors.New("Could not process requested certificate")
		return
	}
	setResponseHeader(w)
	w.WriteHeader(http.StatusOK)
	fmt.Fprint(w, string(jsScan))
}

func PreflightHandler(w http.ResponseWriter, r *http.Request) {
	setResponseHeader(w)
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("preflighted"))
}

func setResponseHeader(w http.ResponseWriter) {
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS, POST")
	w.Header().Set("Access-Control-Max-Age", "86400")
	w.Header().Set("Content-Type", "application/json")
}

func validateDomain(domain string) bool {
	if domain == "" {
		return false
	}
	return true
}
<commit_msg>Set CORS header on all responses<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strconv"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/gorilla/context"

	pg "github.com/mozilla/tls-observatory/database"
	"github.com/mozilla/tls-observatory/logger"
)

var scanRefreshRate float64

type scanResponse struct {
	ID int64 `json:"scan_id"`
}

// ScanHandler handles the /scans endpoint of the api
// It initiates new scans and returns created scans ids to be used against other endpoints.
func ScanHandler(w http.ResponseWriter, r *http.Request) {
	var (
		status int
		err    error
	)
	setResponseHeader(w)

	defer func() {
		if nil != err {
			http.Error(w, err.Error(), status)
		}
	}()

	log := logger.GetLogger()
	status = http.StatusInternalServerError

	log.WithFields(logrus.Fields{
		"form values": r.Form,
		"headers":     r.Header,
	}).Debug("Scan endpoint received request")

	val, ok := context.GetOk(r, dbKey)
	if !ok {
		log.Error("Could not find db in request context")
		err = errors.New("Could not access database.")
		return
	}

	db := val.(*pg.DB)

	domain := r.FormValue("target")
	if !validateDomain(domain) {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, "")
	}

	rescan := false
	if r.FormValue("rescan") == "true" {
		rescan = true
	}

	previd, prevtime, err := db.GetLastScanTimeForTarget(domain)
	if err != nil {
		log.WithFields(logrus.Fields{
			"domain": domain,
			"error":  err.Error(),
		}).Error("Could not get last scan for target")
		err = errors.New("Could not get last scan for target")
		return
	}

	now := time.Now().UTC()

	if previd != -1 { // check if previous scan exists
		if now.Sub(prevtime).Hours() <= scanRefreshRate {
			if !rescan {
				// no rescan requested so return previous scan in any case
				// this includes the rate limiting with no rescan case
				sr := scanResponse{
					ID: previd,
				}
				respBody, _ := json.Marshal(sr)
				w.Header().Set("Content-Type", "application/json")
				w.WriteHeader(http.StatusOK)
				w.Write(respBody)
				return
			}

			// forced rescan has been requested
			if now.Sub(prevtime).Minutes() <= 3 { // rate limit scan requests for same target
				if rescan {
					w.WriteHeader(429) // 429 http status code is not exported ( https://codereview.appspot.com/7678043/ )
					w.Header().Set("Content-Type", "text/html")
					fmt.Fprint(w, fmt.Sprintf("Last scan for target %s initiated %s ago.\nPlease try again in %s.\n", domain, now.Sub(prevtime), 3*time.Minute-now.Sub(prevtime)))
					return
				}
			}
		}
	}

	//initiating a new scan
	scan, err := db.NewScan(domain, -1) //no replay
	if err != nil {
		log.WithFields(logrus.Fields{
			"domain": domain,
			"error":  err.Error(),
		}).Error("Could not create new scan")
		err = errors.New("Could not create new scan")
		return
	}
	sr := scanResponse{
		ID: scan.ID,
	}
	respBody, err := json.Marshal(sr)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": scan.ID,
			"error":   err.Error(),
		}).Error("Could not Marshal scan")

		err = errors.New("Could not process the requested scan")
		return
	}
	w.WriteHeader(http.StatusOK)
	w.Write(respBody)
}

// ResultHandler handles the results endpoint of the api.
// It has a scan id as input and returns its results ( if available )
func ResultHandler(w http.ResponseWriter, r *http.Request) {
	var (
		status int
		err    error
	)
	setResponseHeader(w)

	defer func() {
		if nil != err {
			http.Error(w, err.Error(), status)
		}
	}()

	log := logger.GetLogger()
	status = http.StatusInternalServerError

	log.WithFields(logrus.Fields{
		"form values": r.Form,
		"headers":     r.Header,
	}).Debug("Results endpoint received request")

	val, ok := context.GetOk(r, dbKey)
	if !ok {
		log.Error("Could not find db in request context")
		err = errors.New("Could not access database.")
		return
	}

	db := val.(*pg.DB)

	idStr := r.FormValue("id")

	id, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": idStr,
			"error":   err.Error(),
		}).Error("Could not parse scanid")
		err = errors.New("Could not parse provided scan id")
		status = http.StatusBadRequest
		return
	}

	scan, err := db.GetScanByID(id)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": id,
			"error":   err.Error(),
		}).Error("Could not get scan from database")
		err = errors.New("Could not access database to get requested scan.")
		return
	}

	if scan.ID == -1 {
		log.WithFields(logrus.Fields{
			"scan_id": id,
		}).Debug("Did not find scan in database")

		err = errors.New("Could not find a scan with the id you provided.")
		status = http.StatusNotFound
		return
	}

	jsScan, err := json.Marshal(scan)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": id,
			"error":   err.Error(),
		}).Error("Could not Marshal scan")

		err = errors.New("Could not process the requested scan")
		return
	}
	w.WriteHeader(http.StatusOK)
	fmt.Fprint(w, string(jsScan))
}

// CertificateHandler handles the /certificate endpoint of the api.
// It queries the database for the provided cert ids and returns results in JSON.
func CertificateHandler(w http.ResponseWriter, r *http.Request) {
	var (
		status int
		err    error
	)
	setResponseHeader(w)

	defer func() {
		if nil != err {
			http.Error(w, err.Error(), status)
		}
	}()

	log := logger.GetLogger()
	status = http.StatusInternalServerError

	log.WithFields(logrus.Fields{
		"form values": r.Form.Encode(),
		"headers":     r.Header,
	}).Debug("Certificate Endpoint received request")

	val, ok := context.GetOk(r, dbKey)
	if !ok {
		log.Error("Could not find db in request context")
		err = errors.New("Could not access database.")
		return
	}

	db := val.(*pg.DB)

	idStr := r.FormValue("id")

	id, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		log.WithFields(logrus.Fields{
			"cert_id": id,
			"error":   err.Error(),
		}).Error("Could not parse certificate id")

		status = http.StatusBadRequest
		err = errors.New("Could not parse provided certificate id")
		return
	}

	cert, err := db.GetCertByID(id)
	if err != nil {
		log.WithFields(logrus.Fields{
			"cert_id": id,
			"error":   err.Error(),
		}).Error("Could not get cert from database")

		err = errors.New("Could not access database to get requested certificate")
		return
	}

	jsScan, err := json.Marshal(cert)
	if err != nil {
		log.WithFields(logrus.Fields{
			"cert_id": id,
			"error":   err.Error(),
		}).Error("Could not Marshal cert")

		err = errors.New("Could not process requested certificate")
		return
	}
	w.WriteHeader(http.StatusOK)
	fmt.Fprint(w, string(jsScan))
}

func PreflightHandler(w http.ResponseWriter, r *http.Request) {
	setResponseHeader(w)
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("preflighted"))
}

func setResponseHeader(w http.ResponseWriter) {
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS, POST")
	w.Header().Set("Access-Control-Max-Age", "86400")
	w.Header().Set("Content-Type", "application/json")
}

func validateDomain(domain string) bool {
	if domain == "" {
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"runtime"
	"time"

	"github.com/Sirupsen/logrus"

	"github.com/mozilla/tls-observatory/config"
	"github.com/mozilla/tls-observatory/connection"
	pg "github.com/mozilla/tls-observatory/database"
	"github.com/mozilla/tls-observatory/logger"
	"github.com/mozilla/tls-observatory/worker"
)

var db *pg.DB
var log = logger.GetLogger()

func main() {
	var (
		cfgFile, cipherscan string
		debug               bool
	)
	flag.StringVar(&cfgFile, "c", "/etc/tls-observatory/scanner.cfg", "Configuration file")
	flag.StringVar(&cipherscan, "b", "/opt/cipherscan/cipherscan", "Cipherscan binary location")
	flag.BoolVar(&debug, "debug", false, "Set debug logging")
	flag.Parse()

	if debug {
		logger.SetLevelToDebug()
	}

	conf, err := config.Load(cfgFile)
	if err != nil {
		log.Fatal(fmt.Sprintf("Failed to load configuration: %v", err))
	}
	if !conf.General.Enable && os.Getenv("TLSOBS_SCANNER_ENABLE") != "on" {
		log.Fatal("Scanner is disabled in configuration")
	}

	_, err = os.Stat(cipherscan)
	if err != nil {
		log.WithFields(logrus.Fields{
			"error": err.Error(),
		}).Error("Could not locate cipherscan executable. TLS connection capabilities will not be available.")
	}

	// increase the n
	runtime.GOMAXPROCS(conf.General.MaxProc)

	dbtls := "disable"
	if conf.General.PostgresUseTLS {
		dbtls = "verify-full"
	}
	db, err = pg.RegisterConnection(
		conf.General.PostgresDB,
		conf.General.PostgresUser,
		conf.General.PostgresPass,
		conf.General.Postgres,
		dbtls)
	defer db.Close()
	if err != nil {
		log.WithFields(logrus.Fields{
			"error": err.Error(),
		}).Fatal("Failed to connect to database")
	}
	db.SetMaxOpenConns(conf.General.MaxProc)
	db.SetMaxIdleConns(10)
	incomingScans := db.RegisterScanListener(
		conf.General.PostgresDB,
		conf.General.PostgresUser,
		conf.General.PostgresPass,
		conf.General.Postgres,
		"disable")
	Setup(conf)

	activeScanners := 0
	for scanID := range incomingScans {
		// wait until we have an available scanner
		for {
			if activeScanners >= conf.General.MaxProc {
				time.Sleep(time.Second)
			} else {
				break
			}
		}
		go func() {
			activeScanners++
			scan(scanID, cipherscan)
			activeScanners--
		}()
	}
}

func scan(scanID int64, cipherscan string) {
	log.WithFields(logrus.Fields{
		"scan_id": scanID,
	}).Info("Received new scan")

	db.Exec("UPDATE scans SET attempts = attempts + 1 WHERE id=$1", scanID)

	scan, err := db.GetScanByID(scanID)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": scanID,
			"error":   err.Error(),
		}).Error("Could not find/decode scan")
		return
	}
	var completion int

	// Retrieve the certificate from the target
	certID, trustID, err := handleCert(scan.Target)
	if err != nil {
		db.Exec("UPDATE scans SET has_tls=FALSE, completion_perc=100 WHERE id=$1", scanID)
		log.WithFields(logrus.Fields{
			"scan_id":     scanID,
			"scan_Target": scan.Target,
			"error":       err.Error(),
		}).Error("Could not get certificate info")
		return
	}
	log.WithFields(logrus.Fields{
		"scan_id":  scanID,
		"cert_id":  certID,
		"trust_id": trustID,
	}).Debug("Retrieved certs")

	isTrustValid, err := db.IsTrustValid(trustID)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": scanID,
			"cert_id": certID,
			"error":   err.Error(),
		}).Error("Could not get if trust is valid")
		return
	}
	completion += 20
	_, err = db.Exec(`UPDATE scans
			SET cert_id=$1, trust_id=$2, has_tls=TRUE, is_valid=$3, completion_perc=$4
			WHERE id=$5`, certID, trustID, isTrustValid, completion, scanID)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": scanID,
			"cert_id": certID,
			"error":   err.Error(),
		}).Error("Could not update scans for cert")
		return
	}

	// Cipherscan the target
	js, err := connection.Connect(scan.Target, cipherscan)
	if err != nil {
		err, ok := err.(connection.NoTLSConnErr)
		if ok {
			//does not implement TLS
			db.Exec("UPDATE scans SET has_tls=FALSE, completion_perc=100 WHERE id=$1", scanID)
		} else {
			log.WithFields(logrus.Fields{
				"scan_id": scanID,
				"error":   err.Error(),
			}).Error("Could not get TLS connection info")
		}
		return
	}
	completion += 20
	_, err = db.Exec("UPDATE scans SET conn_info=$1, completion_perc=$2 WHERE id=$3",
		js, completion, scanID)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": scanID,
			"error":   err.Error(),
		}).Error("Could not update connection information for scan")
	}

	// Prepare worker input
	cert, err := db.GetCertByID(certID)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": scanID,
			"cert_id": certID,
		}).Error("Could not get certificate from db to pass to workers")
		return
	}
	var conn_info connection.Stored
	err = json.Unmarshal(js, &conn_info)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": scanID,
		}).Error("Could not parse connection info to pass to workers")
		return
	}
	workerInput := worker.Input{
		DBHandle:    db,
		Scanid:      scanID,
		Certificate: *cert,
		Connection:  conn_info,
	}
	// launch workers that evaluate the results
	resChan := make(chan worker.Result)
	totalWorkers := 0
	for _, wrkInfo := range worker.AvailableWorkers {
		go wrkInfo.Runner.(worker.Worker).Run(workerInput, resChan)
		totalWorkers++
	}
	log.WithFields(logrus.Fields{
		"scan_id": scanID,
		"count":   totalWorkers,
	}).Info("Running workers")

	// read the results from the results chan in a loop until all workers have ran or expired
	for endedWorkers := 0; endedWorkers < totalWorkers; endedWorkers++ {
		select {
		case <-time.After(30 * time.Second):
			log.WithFields(logrus.Fields{
				"scan_id": scanID,
			}).Error("Analysis workers timed out after 30 seconds")
			return
		case res := <-resChan:
			endedWorkers += endedWorkers
			completion = ((endedWorkers/totalWorkers)*60 + completion)
			log.WithFields(logrus.Fields{
				"scan_id":     scanID,
				"worker_name": res.WorkerName,
				"success":     res.Success,
				"result":      string(res.Result),
			}).Debug("Received results from worker")

			err = db.UpdateScanCompletionPercentage(scanID, completion)
			if err != nil {
				log.WithFields(logrus.Fields{
					"scan_id": scanID,
					"error":   err.Error(),
				}).Error("Could not update completion percentage")
				continue
			}
			if !res.Success {
				log.WithFields(logrus.Fields{
					"worker_name": res.WorkerName,
					"errors":      res.Errors,
				}).Error("Worker returned with errors")
				continue
			}
			_, err = db.Exec("INSERT INTO analysis(scan_id,worker_name,output) VALUES($1,$2,$3)",
				scanID, res.WorkerName, res.Result)
			if err != nil {
				log.WithFields(logrus.Fields{
					"scan_id": scanID,
					"error":   err.Error(),
				}).Error("Could not insert worker results in database")
				continue
			}
			log.WithFields(logrus.Fields{
				"scan_id":     scanID,
				"worker_name": res.WorkerName,
			}).Info("Results from worker stored in database")
		}
	}
	err = db.UpdateScanCompletionPercentage(scanID, 100)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": scanID,
			"error":   err.Error(),
		}).Error("Could not update completion percentage")
	}
	return
}
<commit_msg>Always complete scans when workers timeout<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"runtime"
	"time"

	"github.com/Sirupsen/logrus"

	"github.com/mozilla/tls-observatory/config"
	"github.com/mozilla/tls-observatory/connection"
	pg "github.com/mozilla/tls-observatory/database"
	"github.com/mozilla/tls-observatory/logger"
	"github.com/mozilla/tls-observatory/worker"
)

var db *pg.DB
var log = logger.GetLogger()

func main() {
	var (
		cfgFile, cipherscan string
		debug               bool
	)
	flag.StringVar(&cfgFile, "c", "/etc/tls-observatory/scanner.cfg", "Configuration file")
	flag.StringVar(&cipherscan, "b", "/opt/cipherscan/cipherscan", "Cipherscan binary location")
	flag.BoolVar(&debug, "debug", false, "Set debug logging")
	flag.Parse()

	if debug {
		logger.SetLevelToDebug()
	}

	conf, err := config.Load(cfgFile)
	if err != nil {
		log.Fatal(fmt.Sprintf("Failed to load configuration: %v", err))
	}
	if !conf.General.Enable && os.Getenv("TLSOBS_SCANNER_ENABLE") != "on" {
		log.Fatal("Scanner is disabled in configuration")
	}

	_, err = os.Stat(cipherscan)
	if err != nil {
		log.WithFields(logrus.Fields{
			"error": err.Error(),
		}).Error("Could not locate cipherscan executable. TLS connection capabilities will not be available.")
	}

	// increase the n
	runtime.GOMAXPROCS(conf.General.MaxProc)

	dbtls := "disable"
	if conf.General.PostgresUseTLS {
		dbtls = "verify-full"
	}
	db, err = pg.RegisterConnection(
		conf.General.PostgresDB,
		conf.General.PostgresUser,
		conf.General.PostgresPass,
		conf.General.Postgres,
		dbtls)
	defer db.Close()
	if err != nil {
		log.WithFields(logrus.Fields{
			"error": err.Error(),
		}).Fatal("Failed to connect to database")
	}
	db.SetMaxOpenConns(conf.General.MaxProc)
	db.SetMaxIdleConns(10)
	incomingScans := db.RegisterScanListener(
		conf.General.PostgresDB,
		conf.General.PostgresUser,
		conf.General.PostgresPass,
		conf.General.Postgres,
		"disable")
	Setup(conf)

	activeScanners := 0
	for scanID := range incomingScans {
		// wait until we have an available scanner
		for {
			if activeScanners >= conf.General.MaxProc {
				time.Sleep(time.Second)
			} else {
				break
			}
		}
		go func() {
			activeScanners++
			scan(scanID, cipherscan)
			activeScanners--
		}()
	}
}

func scan(scanID int64, cipherscan string) {
	log.WithFields(logrus.Fields{
		"scan_id": scanID,
	}).Info("Received new scan")

	db.Exec("UPDATE scans SET attempts = attempts + 1 WHERE id=$1", scanID)

	scan, err := db.GetScanByID(scanID)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": scanID,
			"error":   err.Error(),
		}).Error("Could not find/decode scan")
		return
	}
	var completion int

	// Retrieve the certificate from the target
	certID, trustID, err := handleCert(scan.Target)
	if err != nil {
		db.Exec("UPDATE scans SET has_tls=FALSE, completion_perc=100 WHERE id=$1", scanID)
		log.WithFields(logrus.Fields{
			"scan_id":     scanID,
			"scan_Target": scan.Target,
			"error":       err.Error(),
		}).Error("Could not get certificate info")
		return
	}
	log.WithFields(logrus.Fields{
		"scan_id":  scanID,
		"cert_id":  certID,
		"trust_id": trustID,
	}).Debug("Retrieved certs")

	isTrustValid, err := db.IsTrustValid(trustID)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": scanID,
			"cert_id": certID,
			"error":   err.Error(),
		}).Error("Could not get if trust is valid")
		return
	}
	completion += 20
	_, err = db.Exec(`UPDATE scans
			SET cert_id=$1, trust_id=$2, has_tls=TRUE, is_valid=$3, completion_perc=$4
			WHERE id=$5`, certID, trustID, isTrustValid, completion, scanID)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": scanID,
			"cert_id": certID,
			"error":   err.Error(),
		}).Error("Could not update scans for cert")
		return
	}

	// Cipherscan the target
	js, err := connection.Connect(scan.Target, cipherscan)
	if err != nil {
		err, ok := err.(connection.NoTLSConnErr)
		if ok {
			//does not implement TLS
			db.Exec("UPDATE scans SET has_tls=FALSE, completion_perc=100 WHERE id=$1", scanID)
		} else {
			log.WithFields(logrus.Fields{
				"scan_id": scanID,
				"error":   err.Error(),
			}).Error("Could not get TLS connection info")
		}
		return
	}
	completion += 20
	_, err = db.Exec("UPDATE scans SET conn_info=$1, completion_perc=$2 WHERE id=$3",
		js, completion, scanID)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": scanID,
			"error":   err.Error(),
		}).Error("Could not update connection information for scan")
	}

	// Prepare worker input
	cert, err := db.GetCertByID(certID)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": scanID,
			"cert_id": certID,
		}).Error("Could not get certificate from db to pass to workers")
		return
	}
	var conn_info connection.Stored
	err = json.Unmarshal(js, &conn_info)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": scanID,
		}).Error("Could not parse connection info to pass to workers")
		return
	}
	workerInput := worker.Input{
		DBHandle:    db,
		Scanid:      scanID,
		Certificate: *cert,
		Connection:  conn_info,
	}
	// launch workers that evaluate the results
	resChan := make(chan worker.Result)
	totalWorkers := 0
	for _, wrkInfo := range worker.AvailableWorkers {
		go wrkInfo.Runner.(worker.Worker).Run(workerInput, resChan)
		totalWorkers++
	}
	log.WithFields(logrus.Fields{
		"scan_id": scanID,
		"count":   totalWorkers,
	}).Info("Running workers")

	// read the results from the results chan in a loop until all workers have ran or expired
	for endedWorkers := 0; endedWorkers < totalWorkers; endedWorkers++ {
		select {
		case <-time.After(30 * time.Second):
			log.WithFields(logrus.Fields{
				"scan_id": scanID,
			}).Error("Analysis workers timed out after 30 seconds")
			goto updatecompletion
		case res := <-resChan:
			endedWorkers += endedWorkers
			completion = ((endedWorkers/totalWorkers)*60 + completion)
			log.WithFields(logrus.Fields{
				"scan_id":     scanID,
				"worker_name": res.WorkerName,
				"success":     res.Success,
				"result":      string(res.Result),
			}).Debug("Received results from worker")

			err = db.UpdateScanCompletionPercentage(scanID, completion)
			if err != nil {
				log.WithFields(logrus.Fields{
					"scan_id": scanID,
					"error":   err.Error(),
				}).Error("Could not update completion percentage")
				continue
			}
			if !res.Success {
				log.WithFields(logrus.Fields{
					"worker_name": res.WorkerName,
					"errors":      res.Errors,
				}).Error("Worker returned with errors")
				continue
			}
			_, err = db.Exec("INSERT INTO analysis(scan_id,worker_name,output) VALUES($1,$2,$3)",
				scanID, res.WorkerName, res.Result)
			if err != nil {
				log.WithFields(logrus.Fields{
					"scan_id": scanID,
					"error":   err.Error(),
				}).Error("Could not insert worker results in database")
				continue
			}
			log.WithFields(logrus.Fields{
				"scan_id":     scanID,
				"worker_name": res.WorkerName,
			}).Info("Results from worker stored in database")
		}
	}
updatecompletion:
	err = db.UpdateScanCompletionPercentage(scanID, 100)
	if err != nil {
		log.WithFields(logrus.Fields{
			"scan_id": scanID,
			"error":   err.Error(),
		}).Error("Could not update completion percentage")
	}
	return
}
<|endoftext|>


<commit_before>package undef

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestMapObject(t *testing.T) {
	type Input struct {
		Object Object
		Path   string
		Paths  []string
	}
	type Expect struct {
		Object Object
		Err    error
	}
	type Test struct {
		Title  string
		Input  Input
		Expect Expect
	}

	table := []Test{
		Test{
			Title: "success",
			Input: Input{
				Object: MapObject(map[string]Object{
					"a": DummyObject{1},
				}),
				Path:  "a",
				Paths: nil,
			},
			Expect: Expect{
				Object: DummyObject{1},
				Err:    nil,
			},
		},
		Test{
			Title: "success nested",
			Input: Input{
				Object: MapObject(map[string]Object{
					"a": MapObject(map[string]Object{
						"b": MapObject(map[string]Object{
							"c": DummyObject{1},
						}),
					}),
				}),
				Path:  "a",
				Paths: []string{"b", "c"},
			},
			Expect: Expect{
				Object: DummyObject{1},
				Err:    nil,
			},
		},
		Test{
			Title: "path error",
			Input: Input{
				Object: MapObject(map[string]Object{
					"a": DummyObject{1},
				}),
				Path:  "x",
				Paths: nil,
			},
			Expect: Expect{
				Object: nil,
				Err: &PathError{
					Message: "no such path",
					Path:    "x",
					Stack:   nil,
				},
			},
		},
		Test{
			Title: "path error nested",
			Input: Input{
				Object: MapObject(map[string]Object{
					"a": MapObject(map[string]Object{
						"b": MapObject(map[string]Object{
							"c": DummyObject{1},
						}),
					}),
				}),
				Path:  "a",
				Paths: []string{"b", "x"},
			},
			Expect: Expect{
				Object: nil,
				Err: &PathError{
					Message: "no such path",
					Path:    "x",
					Stack:   []string{"b", "a"},
				},
			},
		},
	}

	for _, testCase := range table {
		t.Run(testCase.Title, func(t *testing.T) {
			assert := assert.New(t)

			obj, err := testCase.Input.Object.Get(testCase.Input.Path, testCase.Input.Paths...)

			assert.Equal(testCase.Expect.Object, obj)
			assert.Equal(testCase.Expect.Err, err)
		})
	}
}
<commit_msg>Add test of MapObject.Set<commit_after>package undef

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestMapObject_Get(t *testing.T) {
	type Input struct {
		Object Object
		Path   string
		Paths  []string
	}
	type Expect struct {
		Object Object
		Err    error
	}
	type Test struct {
		Title  string
		Input  Input
		Expect Expect
	}

	table := []Test{
		Test{
			Title: "success",
			Input: Input{
				Object: MapObject(map[string]Object{
					"a": DummyObject{1},
				}),
				Path:  "a",
				Paths: nil,
			},
			Expect: Expect{
				Object: DummyObject{1},
				Err:    nil,
			},
		},
		Test{
			Title: "success nested",
			Input: Input{
				Object: MapObject(map[string]Object{
					"a": MapObject(map[string]Object{
						"b": MapObject(map[string]Object{
							"c": DummyObject{1},
						}),
					}),
				}),
				Path:  "a",
				Paths: []string{"b", "c"},
			},
			Expect: Expect{
				Object: DummyObject{1},
				Err:    nil,
			},
		},
		Test{
			Title: "path error",
			Input: Input{
				Object: MapObject(map[string]Object{
					"a": DummyObject{1},
				}),
				Path:  "x",
				Paths: nil,
			},
			Expect: Expect{
				Object: nil,
				Err: &PathError{
					Message: "no such path",
					Path:    "x",
					Stack:   nil,
				},
			},
		},
		Test{
			Title: "path error nested",
			Input: Input{
				Object: MapObject(map[string]Object{
					"a": MapObject(map[string]Object{
						"b": MapObject(map[string]Object{
							"c": DummyObject{1},
						}),
					}),
				}),
				Path:  "a",
				Paths: []string{"b", "x"},
			},
			Expect: Expect{
				Object: nil,
				Err: &PathError{
					Message: "no such path",
					Path:    "x",
					Stack:   []string{"b", "a"},
				},
			},
		},
	}

	for _, testCase := range table {
		t.Run(testCase.Title, func(t *testing.T) {
			assert := assert.New(t)

			obj, err := testCase.Input.Object.Get(testCase.Input.Path, testCase.Input.Paths...)

			assert.Equal(testCase.Expect.Object, obj)
			assert.Equal(testCase.Expect.Err, err)
		})
	}
}

func TestMapObject_Set(t *testing.T) {
	type Input struct {
		Object Object
		Path   string
		Paths  []string
		BeSet  Object
	}
	type Expect struct {
		Object Object
		Err    error
	}
	type Test struct {
		Title  string
		Input  Input
		Expect Expect
	}

	table := []Test{
		Test{
			Title: "success",
			Input: Input{
				Object: MapObject(map[string]Object{
					"a": DummyObject{1},
				}),
				Path:  "a",
				Paths: nil,
				BeSet: DummyObject{2},
			},
			Expect: Expect{
				Object: MapObject(map[string]Object{
					"a": DummyObject{2},
				}),
				Err: nil,
			},
		},
		Test{
			Title: "success nested",
			Input: Input{
				Object: MapObject(map[string]Object{
					"a": MapObject(map[string]Object{
						"b": MapObject(map[string]Object{
							"c": DummyObject{1},
						}),
					}),
				}),
				Path:  "a",
				Paths: []string{"b"},
				BeSet: DummyObject{2},
			},
			Expect: Expect{
				Object: MapObject(map[string]Object{
					"a": MapObject(map[string]Object{
						"b": DummyObject{2},
					}),
				}),
				Err: nil,
			},
		},
		Test{
			Title: "path error",
			Input: Input{
				Object: MapObject(map[string]Object{
					"a": DummyObject{1},
				}),
				Path:  "x",
				Paths: nil,
				BeSet: DummyObject{2},
			},
			Expect: Expect{
				Object: MapObject(map[string]Object{
					"a": DummyObject{1},
				}),
				Err: &PathError{
					Message: "no such path",
					Path:    "x",
					Stack:   nil,
				},
			},
		},
		Test{
			Title: "path error nested",
			Input: Input{
				Object: MapObject(map[string]Object{
					"a": MapObject(map[string]Object{
						"b": MapObject(map[string]Object{
							"c": DummyObject{1},
						}),
					}),
				}),
				Path:  "a",
				Paths: []string{"b", "x"},
				BeSet: DummyObject{2},
			},
			Expect: Expect{
				Object: MapObject(map[string]Object{
					"a": MapObject(map[string]Object{
						"b": MapObject(map[string]Object{
							"c": DummyObject{1},
						}),
					}),
				}),
				Err: &PathError{
					Message: "no such path",
					Path:    "x",
					Stack:   []string{"b", "a"},
				},
			},
		},
	}

	for _, testCase := range table {
		t.Run(testCase.Title, func(t *testing.T) {
			assert := assert.New(t)

			obj := testCase.Input.Object
			err := obj.Set(testCase.Input.BeSet, testCase.Input.Path, testCase.Input.Paths...)

			assert.Equal(testCase.Expect.Object, obj)
			assert.Equal(testCase.Expect.Err, err)
		})
	}
}
<|endoftext|>


<commit_before>package terminal

import (
	"bytes"
	"errors"
	"fmt"
	"os/exec"
	"strings"
)

const (
	sessionPrefix     = "koding"
	defaultScreenPath = "/usr/bin/screen"
)

type Command struct {
	// Name is used for starting the terminal instance, it's the program path
	// usually
	Name string

	// Args is passed to the program name
	Args []string
}

var (
	ErrNoSession      = errors.New("ErrNoSession")
	ErrInvalidSession = errors.New("ErrInvalidSession")
)

// newCmd returns a new command instance that is used to start the terminal.
// The command line is created differently based on the incoming mode.
func newCommand(mode, session, username string) (*Command, error) {
	// let's assume by default its Screen
	name := defaultScreenPath
	args := []string{"-S"}

	switch mode {
	case "shared", "resume":
		if session == "" {
			return nil, errors.New("session is needed for 'shared' or 'resume' mode")
		}

		if !sessionExists(session, username) {
			return nil, ErrNoSession
		}

		args = append(args, sessionPrefix+"."+session)
		if mode == "shared" {
			args = append(args, "-x") // multiuser mode
		} else if mode == "resume" {
			args = append(args, "-raAd") // resume
		}
	case "noscreen":
		name = "/bin/bash"
		args = []string{}
	case "create":
		args = append(args, sessionPrefix+"."+randomString())
	default:
		return nil, fmt.Errorf("mode '%s' is unknown. Valid modes are:  [shared|noscreen|resume|create]", mode)
	}

	c := &Command{
		Name: name,
		Args: args,
	}

	fmt.Printf("c %#v\n", c)

	return c, nil
}

// screenSessions returns a list of sessions that belongs to the given
// username.  The sessions are in the form of ["k7sdjv12344", "askIj12sas12",
// ...]
func screenSessions(username string) []string {
	// Do not include dead sessions in our result
	exec.Command(defaultScreenPath, "-wipe").Run()

	// We need to use ls here, because /var/run/screen mount is only
	// visible from inside of container. Errors are ignored.
	out, _ := exec.Command("ls", "/var/run/screen/S-"+username).Output()
	shellOut := string(bytes.TrimSpace(out))
	if shellOut == "" {
		return []string{}
	}

	names := strings.Split(shellOut, "\n")
	sessions := make([]string, len(names))

	prefix := sessionPrefix + "."
	for i, name := range names {
		segments := strings.SplitN(name, ".", 2)
		sessions[i] = strings.TrimPrefix(segments[1], prefix)
	}

	return sessions
}

// screenExists checks whether the given session exists in the running list of
// screen sessions.
func sessionExists(session, username string) bool {
	for _, s := range screenSessions(username) {
		if s == session {
			return true
		}
	}

	return false
}

// killSession kills the given SessionID
func killSession(session string) error {
	out, err := exec.Command(defaultScreenPath, "-X", "-S", sessionPrefix+"."+session, "kill").Output()
	if err != nil {
		return commandError("screen kill failed", err, out)
	}

	return nil
}

func commandError(message string, err error, out []byte) error {
	return fmt.Errorf("%s\n%s\n%s", message, err.Error(), string(out))
}
<commit_msg>terminal: add note about darwin<commit_after>package terminal

import (
	"bytes"
	"errors"
	"fmt"
	"os/exec"
	"strings"
)

const (
	sessionPrefix     = "koding"
	defaultScreenPath = "/usr/bin/screen"
)

type Command struct {
	// Name is used for starting the terminal instance, it's the program path
	// usually
	Name string

	// Args is passed to the program name
	Args []string
}

var (
	ErrNoSession      = errors.New("ErrNoSession")
	ErrInvalidSession = errors.New("ErrInvalidSession")
)

// newCmd returns a new command instance that is used to start the terminal.
// The command line is created differently based on the incoming mode.
func newCommand(mode, session, username string) (*Command, error) {
	// let's assume by default its Screen
	name := defaultScreenPath
	args := []string{"-S"}

	switch mode {
	case "shared", "resume":
		if session == "" {
			return nil, errors.New("session is needed for 'shared' or 'resume' mode")
		}

		if !sessionExists(session, username) {
			return nil, ErrNoSession
		}

		args = append(args, sessionPrefix+"."+session)
		if mode == "shared" {
			args = append(args, "-x") // multiuser mode
		} else if mode == "resume" {
			args = append(args, "-raAd") // resume
		}
	case "noscreen":
		name = "/bin/bash"
		args = []string{}
	case "create":
		args = append(args, sessionPrefix+"."+randomString())
	default:
		return nil, fmt.Errorf("mode '%s' is unknown. Valid modes are:  [shared|noscreen|resume|create]", mode)
	}

	c := &Command{
		Name: name,
		Args: args,
	}

	fmt.Printf("c %#v\n", c)

	return c, nil
}

// screenSessions returns a list of sessions that belongs to the given
// username.  The sessions are in the form of ["k7sdjv12344", "askIj12sas12",
// ...]
// TODO: socket directory is different under darwin, it will not work probably
func screenSessions(username string) []string {
	// Do not include dead sessions in our result
	exec.Command(defaultScreenPath, "-wipe").Run()

	// We need to use ls here, because /var/run/screen mount is only
	// visible from inside of container. Errors are ignored.
	out, _ := exec.Command("ls", "/var/run/screen/S-"+username).Output()
	shellOut := string(bytes.TrimSpace(out))
	if shellOut == "" {
		return []string{}
	}

	names := strings.Split(shellOut, "\n")
	sessions := make([]string, len(names))

	prefix := sessionPrefix + "."
	for i, name := range names {
		segments := strings.SplitN(name, ".", 2)
		sessions[i] = strings.TrimPrefix(segments[1], prefix)
	}

	return sessions
}

// screenExists checks whether the given session exists in the running list of
// screen sessions.
func sessionExists(session, username string) bool {
	for _, s := range screenSessions(username) {
		if s == session {
			return true
		}
	}

	return false
}

// killSession kills the given SessionID
func killSession(session string) error {
	out, err := exec.Command(defaultScreenPath, "-X", "-S", sessionPrefix+"."+session, "kill").Output()
	if err != nil {
		return commandError("screen kill failed", err, out)
	}

	return nil
}

func commandError(message string, err error, out []byte) error {
	return fmt.Errorf("%s\n%s\n%s", message, err.Error(), string(out))
}
<|endoftext|>


<commit_before>package cluster

import (
	log "code.google.com/p/log4go"
	"protocol"
	"time"
)

// Acts as a buffer for writes
type WriteBuffer struct {
	writer        Writer
	wal           WAL
	serverId      uint32
	writes        chan *protocol.Request
	stoppedWrites chan uint32
	bufferSize    int
	shardIds      map[uint32]bool
}

type Writer interface {
	Write(request *protocol.Request) error
}

func NewWriteBuffer(writer Writer, wal WAL, serverId uint32, bufferSize int) *WriteBuffer {
	buff := &WriteBuffer{
		writer:        writer,
		wal:           wal,
		serverId:      serverId,
		writes:        make(chan *protocol.Request, bufferSize),
		stoppedWrites: make(chan uint32, 1),
		bufferSize:    bufferSize,
		shardIds:      make(map[uint32]bool),
	}
	go buff.handleWrites()
	return buff
}

// This method never blocks. It'll buffer writes until they fill the buffer then drop the on the
// floor and let the background goroutine replay from the WAL
func (self *WriteBuffer) Write(request *protocol.Request) {
	select {
	case self.writes <- request:
		return
	default:
		select {
		case self.stoppedWrites <- *request.RequestNumber:
			return
		default:
			return
		}
	}
}

func (self *WriteBuffer) handleWrites() {
	for {
		select {
		case requestDropped := <-self.stoppedWrites:
			self.replayAndRecover(requestDropped)
		case request := <-self.writes:
			self.write(request)
		}
	}
}

func (self *WriteBuffer) write(request *protocol.Request) {
	attempts := 0
	for {
		self.shardIds[*request.ShardId] = true
		requestNumber := request.GetRequestNumber()
		err := self.writer.Write(request)
		if err == nil {
			self.wal.Commit(requestNumber, self.serverId)
			return
		}
		if attempts%100 == 0 {
			log.Error("WriteBuffer: error on write to server %d: %s", self.serverId, err)
		}
		attempts += 1
		// backoff happens in the writer, just sleep for a small fixed amount of time before retrying
		time.Sleep(time.Millisecond * 100)
	}
}

func (self *WriteBuffer) replayAndRecover(missedRequest uint32) {
	for {
		// empty out the buffer before the replay so new writes can buffer while we're replaying
		channelLen := len(self.writes)
		for i := 0; i < channelLen; i++ {
			<-self.writes
		}
		shardIds := make([]uint32, 0)
		for shardId, _ := range self.shardIds {
			shardIds = append(shardIds, shardId)
		}
		self.wal.RecoverServerFromRequestNumber(missedRequest, shardIds, func(request *protocol.Request, shardId uint32) error {
			request.ShardId = &shardId
			self.write(request)
			return nil
		})

		// now make sure that no new writes were dropped. If so, do the replay again from this place.
		select {
		case missedRequest = <-self.stoppedWrites:
			continue
		default:
			return
		}
	}
}
<commit_msg>Fix write buffer replays to properly play requests in order and not drop any mid-replay.<commit_after>package cluster

import (
	log "code.google.com/p/log4go"
	"protocol"
	"time"
)

// Acts as a buffer for writes
type WriteBuffer struct {
	writer        Writer
	wal           WAL
	serverId      uint32
	writes        chan *protocol.Request
	stoppedWrites chan uint32
	bufferSize    int
	shardIds      map[uint32]bool
}

type Writer interface {
	Write(request *protocol.Request) error
}

func NewWriteBuffer(writer Writer, wal WAL, serverId uint32, bufferSize int) *WriteBuffer {
	log.Info("Initializing write buffer with buffer size of %d", bufferSize)
	buff := &WriteBuffer{
		writer:        writer,
		wal:           wal,
		serverId:      serverId,
		writes:        make(chan *protocol.Request, bufferSize),
		stoppedWrites: make(chan uint32, 1),
		bufferSize:    bufferSize,
		shardIds:      make(map[uint32]bool),
	}
	go buff.handleWrites()
	return buff
}

// This method never blocks. It'll buffer writes until they fill the buffer then drop the on the
// floor and let the background goroutine replay from the WAL
func (self *WriteBuffer) Write(request *protocol.Request) {
	select {
	case self.writes <- request:
		return
	default:
		log.Info("Write buffer full, pausing that shit")
		select {
		case self.stoppedWrites <- *request.RequestNumber:
			return
		default:
			return
		}
	}
}

func (self *WriteBuffer) handleWrites() {
	for {
		select {
		case requestDropped := <-self.stoppedWrites:
			self.replayAndRecover(requestDropped)
		case request := <-self.writes:
			self.write(request)
		}
	}
}

func (self *WriteBuffer) write(request *protocol.Request) {
	attempts := 0
	for {
		self.shardIds[*request.ShardId] = true
		requestNumber := *request.RequestNumber
		err := self.writer.Write(request)
		if err == nil {
			self.wal.Commit(requestNumber, self.serverId)
			return
		}
		if attempts%100 == 0 {
			log.Error("WriteBuffer: error on write to server %d: %s", self.serverId, err)
		}
		attempts += 1
		// backoff happens in the writer, just sleep for a small fixed amount of time before retrying
		time.Sleep(time.Millisecond * 100)
	}
}

func (self *WriteBuffer) replayAndRecover(missedRequest uint32) {
	for {
		log.Info("REPLAY: Replaying dropped requests...")
		// empty out the buffer before the replay so new writes can buffer while we're replaying
		channelLen := len(self.writes)
		var req *protocol.Request

		// if req is nil, this is the first run through the replay. Start from the start of the write queue
		if req == nil {
			for i := 0; i < channelLen; i++ {
				r := <-self.writes
				if req == nil {
					req = r
				}
			}
		}
		log.Info("REPLAY: Emptied out channel")
		shardIds := make([]uint32, 0)
		for shardId, _ := range self.shardIds {
			shardIds = append(shardIds, shardId)
		}

		log.Info("REPLAY: Shards: ", shardIds)
		self.wal.RecoverServerFromRequestNumber(*req.RequestNumber, shardIds, func(request *protocol.Request, shardId uint32) error {
			req = request
			request.ShardId = &shardId
			self.write(request)
			return nil
		})

		log.Info("REPLAY: Emptying out reqeusts from buffer that we've already replayed")
	RequestLoop:
		for {
			select {
			case newReq := <-self.writes:
				if *newReq.RequestNumber == *req.RequestNumber {
					break RequestLoop
				}
			default:
				log.Error("REPLAY: Got to the end of the write buffer without getting to the last written request.")
				break RequestLoop
			}
		}

		log.Info("REPLAY: done.")

		// now make sure that no new writes were dropped. If so, do the replay again from this place.
		select {
		case <-self.stoppedWrites:
			log.Info("REPLAY: Buffer backed up while replaying, going again.")
			continue
		default:
			return
		}
	}
}
<|endoftext|>


<commit_before>package google

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"

	"google.golang.org/api/compute/v1"
)

func TestAccComputeGlobalAddress_basic(t *testing.T) {
	t.Parallel()

	var addr compute.Address

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckComputeGlobalAddressDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccComputeGlobalAddress_basic(),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckComputeGlobalAddressExists(
						"google_compute_global_address.foobar", &addr),

					// implicitly IPV4 - if we don't send an ip_version, we don't get one back.
					testAccCheckComputeGlobalAddressIpVersion("google_compute_global_address.foobar", ""),
				),
			},
			resource.TestStep{
				ResourceName:      "google_compute_global_address.foobar",
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccComputeGlobalAddress_ipv6(t *testing.T) {
	t.Parallel()

	var addr compute.Address

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckComputeGlobalAddressDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccComputeGlobalAddress_ipv6(),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckComputeGlobalAddressExists(
						"google_compute_global_address.foobar", &addr),
					testAccCheckComputeGlobalAddressIpVersion("google_compute_global_address.foobar", "IPV6"),
				),
			},
			resource.TestStep{
				ResourceName:      "google_compute_global_address.foobar",
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccComputeGlobalAddress_internal(t *testing.T) {
	t.Parallel()

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckComputeGlobalAddressDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccComputeGlobalAddress_internal(),
			},
			resource.TestStep{
				ResourceName:      "google_compute_global_address.foobar",
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func testAccCheckComputeGlobalAddressExists(n string, addr *compute.Address) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No ID is set")
		}

		config := testAccProvider.Meta().(*Config)

		found, err := config.clientCompute.GlobalAddresses.Get(
			config.Project, rs.Primary.ID).Do()
		if err != nil {
			return err
		}

		if found.Name != rs.Primary.ID {
			return fmt.Errorf("Addr not found")
		}

		*addr = *found

		return nil
	}
}

func testAccCheckComputeGlobalAddressIpVersion(n, version string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No ID is set")
		}

		config := testAccProvider.Meta().(*Config)

		addr, err := config.clientCompute.GlobalAddresses.Get(config.Project, rs.Primary.ID).Do()
		if err != nil {
			return err
		}

		if addr.IpVersion != version {
			return fmt.Errorf("Expected IP version to be %s, got %s", version, addr.IpVersion)
		}

		return nil
	}
}

func testAccComputeGlobalAddress_basic() string {
	return fmt.Sprintf(`
resource "google_compute_global_address" "foobar" {
	name = "address-test-%s"
	description = "Created for Terraform acceptance testing"
}`, acctest.RandString(10))
}

func testAccComputeGlobalAddress_ipv6() string {
	return fmt.Sprintf(`
resource "google_compute_global_address" "foobar" {
	name = "address-test-%s"
	description = "Created for Terraform acceptance testing"
	ip_version = "IPV6"
}`, acctest.RandString(10))
}

func testAccComputeGlobalAddress_internal() string {
	return fmt.Sprintf(`
resource "google_compute_network" "foobar" {
  name = "address-test-%s"
}

resource "google_compute_global_address" "foobar" {
  name = "address-test-%s"
  address_type = "INTERNAL"
  purpose = "VPC_PEERING"
  prefix_length = 24
  network = "${google_compute_network.foobar.self_link}"
}`, acctest.RandString(10), acctest.RandString(10))
}
<commit_msg>Magic Modules changes.<commit_after>package google

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"

	"google.golang.org/api/compute/v1"
)

func TestAccComputeGlobalAddress_basic(t *testing.T) {
	t.Parallel()

	var addr compute.Address

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckComputeGlobalAddressDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccComputeGlobalAddress_basic(),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckComputeGlobalAddressExists(
						"google_compute_global_address.foobar", &addr),

					// implicitly IPV4 - if we don't send an ip_version, we don't get one back.
					testAccCheckComputeGlobalAddressIpVersion("google_compute_global_address.foobar", ""),
				),
			},
			resource.TestStep{
				ResourceName:      "google_compute_global_address.foobar",
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccComputeGlobalAddress_ipv6(t *testing.T) {
	t.Parallel()

	var addr compute.Address

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckComputeGlobalAddressDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccComputeGlobalAddress_ipv6(),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckComputeGlobalAddressExists(
						"google_compute_global_address.foobar", &addr),
					testAccCheckComputeGlobalAddressIpVersion("google_compute_global_address.foobar", "IPV6"),
				),
			},
			resource.TestStep{
				ResourceName:      "google_compute_global_address.foobar",
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func testAccCheckComputeGlobalAddressExists(n string, addr *compute.Address) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No ID is set")
		}

		config := testAccProvider.Meta().(*Config)

		found, err := config.clientCompute.GlobalAddresses.Get(
			config.Project, rs.Primary.ID).Do()
		if err != nil {
			return err
		}

		if found.Name != rs.Primary.ID {
			return fmt.Errorf("Addr not found")
		}

		*addr = *found

		return nil
	}
}

func testAccCheckComputeGlobalAddressIpVersion(n, version string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No ID is set")
		}

		config := testAccProvider.Meta().(*Config)

		addr, err := config.clientCompute.GlobalAddresses.Get(config.Project, rs.Primary.ID).Do()
		if err != nil {
			return err
		}

		if addr.IpVersion != version {
			return fmt.Errorf("Expected IP version to be %s, got %s", version, addr.IpVersion)
		}

		return nil
	}
}

func testAccComputeGlobalAddress_basic() string {
	return fmt.Sprintf(`
resource "google_compute_global_address" "foobar" {
	name = "address-test-%s"
	description = "Created for Terraform acceptance testing"
}`, acctest.RandString(10))
}

func testAccComputeGlobalAddress_ipv6() string {
	return fmt.Sprintf(`
resource "google_compute_global_address" "foobar" {
	name = "address-test-%s"
	description = "Created for Terraform acceptance testing"
	ip_version = "IPV6"
}`, acctest.RandString(10))
}
<|endoftext|>


<commit_before>// Copyright 2016 The etcd 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 main is the entry point for the local tester network bridge.
package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net"
	"sync"
	"time"
)

type bridgeConn struct {
	in  net.Conn
	out net.Conn
	d   dispatcher
}

func newBridgeConn(in net.Conn, d dispatcher) (*bridgeConn, error) {
	out, err := net.Dial("tcp", flag.Args()[1])
	if err != nil {
		in.Close()
		return nil, err
	}
	return &bridgeConn{in, out, d}, nil
}

func (b *bridgeConn) String() string {
	return fmt.Sprintf("%v <-> %v", b.in.RemoteAddr(), b.out.RemoteAddr())
}

func (b *bridgeConn) Close() {
	b.in.Close()
	b.out.Close()
}

func bridge(b *bridgeConn) {
	log.Println("bridging", b.String())
	go b.d.Copy(b.out, makeFetch(b.in))
	b.d.Copy(b.in, makeFetch(b.out))
}

func timeBridge(b *bridgeConn) {
	go func() {
		t := time.Duration(rand.Intn(5)+1) * time.Second
		time.Sleep(t)
		log.Printf("killing connection %s after %v\n", b.String(), t)
		b.Close()
	}()
	bridge(b)
}

func blackhole(b *bridgeConn) {
	log.Println("blackholing connection", b.String())
	io.Copy(ioutil.Discard, b.in)
	b.Close()
}

func readRemoteOnly(b *bridgeConn) {
	log.Println("one way (<-)", b.String())
	b.d.Copy(b.in, makeFetch(b.out))
}

func writeRemoteOnly(b *bridgeConn) {
	log.Println("one way (->)", b.String())
	b.d.Copy(b.out, makeFetch(b.in))
}

func corruptReceive(b *bridgeConn) {
	log.Println("corruptReceive", b.String())
	go b.d.Copy(b.in, makeFetchCorrupt(makeFetch(b.out)))
	b.d.Copy(b.out, makeFetch(b.in))
}

func corruptSend(b *bridgeConn) {
	log.Println("corruptSend", b.String())
	go b.d.Copy(b.out, makeFetchCorrupt(makeFetch(b.in)))
	b.d.Copy(b.in, makeFetch(b.out))
}

func makeFetch(c io.Reader) fetchFunc {
	return func() ([]byte, error) {
		b := make([]byte, 4096)
		n, err := c.Read(b)
		if err != nil {
			return nil, err
		}
		return b[:n], nil
	}
}

func makeFetchCorrupt(f func() ([]byte, error)) fetchFunc {
	return func() ([]byte, error) {
		b, err := f()
		if err != nil {
			return nil, err
		}
		// corrupt one byte approximately every 16K
		for i := 0; i < len(b); i++ {
			if rand.Intn(16*1024) == 0 {
				b[i] = b[i] + 1
			}
		}
		return b, nil
	}
}

func makeFetchRand(f func() ([]byte, error)) fetchFunc {
	return func() ([]byte, error) {
		if rand.Intn(10) == 0 {
			return nil, fmt.Errorf("fetchRand: done")
		}
		b, err := f()
		if err != nil {
			return nil, err
		}
		return b, nil
	}
}

func randomBlackhole(b *bridgeConn) {
	log.Println("random blackhole: connection", b.String())

	var wg sync.WaitGroup
	wg.Add(2)
	go func() {
		b.d.Copy(b.in, makeFetchRand(makeFetch(b.out)))
		wg.Done()
	}()
	go func() {
		b.d.Copy(b.out, makeFetchRand(makeFetch(b.in)))
		wg.Done()
	}()
	wg.Wait()
	b.Close()
}

type config struct {
	delayAccept bool
	resetListen bool

	connFaultRate   float64
	immediateClose  bool
	blackhole       bool
	timeClose       bool
	writeRemoteOnly bool
	readRemoteOnly  bool
	randomBlackhole bool
	corruptSend     bool
	corruptReceive  bool
	reorder         bool
}

type acceptFaultFunc func()
type connFaultFunc func(*bridgeConn)

func main() {
	var cfg config

	flag.BoolVar(&cfg.delayAccept, "delay-accept", true, "delays accepting new connections")
	flag.BoolVar(&cfg.resetListen, "reset-listen", true, "resets the listening port")

	flag.Float64Var(&cfg.connFaultRate, "conn-fault-rate", 0.25, "rate of faulty connections")
	flag.BoolVar(&cfg.immediateClose, "immediate-close", true, "close after accept")
	flag.BoolVar(&cfg.blackhole, "blackhole", true, "reads nothing, writes go nowhere")
	flag.BoolVar(&cfg.timeClose, "time-close", true, "close after random time")
	flag.BoolVar(&cfg.writeRemoteOnly, "write-remote-only", true, "only write, no read")
	flag.BoolVar(&cfg.readRemoteOnly, "read-remote-only", true, "only read, no write")
	flag.BoolVar(&cfg.randomBlackhole, "random-blackhole", true, "blackhole after data xfer")
	flag.BoolVar(&cfg.corruptReceive, "corrupt-receive", true, "corrupt packets received from destination")
	flag.BoolVar(&cfg.corruptSend, "corrupt-send", true, "corrupt packets sent to destination")
	flag.BoolVar(&cfg.reorder, "reorder", true, "reorder packet delivery")
	flag.Parse()

	lAddr := flag.Args()[0]
	fwdAddr := flag.Args()[1]
	log.Println("listening on ", lAddr)
	log.Println("forwarding to ", fwdAddr)
	l, err := net.Listen("tcp", lAddr)
	if err != nil {
		log.Fatal(err)
	}
	defer l.Close()

	acceptFaults := []acceptFaultFunc{func() {}}
	if cfg.delayAccept {
		f := func() {
			log.Println("delaying accept")
			time.Sleep(3 * time.Second)
		}
		acceptFaults = append(acceptFaults, f)
	}
	if cfg.resetListen {
		f := func() {
			log.Println("reset listen port")
			l.Close()
			newListener, err := net.Listen("tcp", lAddr)
			if err != nil {
				log.Fatal(err)
			}
			l = newListener

		}
		acceptFaults = append(acceptFaults, f)
	}

	connFaults := []connFaultFunc{func(b *bridgeConn) { bridge(b) }}
	if cfg.immediateClose {
		f := func(b *bridgeConn) {
			log.Printf("terminating connection %s immediately", b.String())
			b.Close()
		}
		connFaults = append(connFaults, f)
	}
	if cfg.blackhole {
		connFaults = append(connFaults, blackhole)
	}
	if cfg.timeClose {
		connFaults = append(connFaults, timeBridge)
	}
	if cfg.writeRemoteOnly {
		connFaults = append(connFaults, writeRemoteOnly)
	}
	if cfg.readRemoteOnly {
		connFaults = append(connFaults, readRemoteOnly)
	}
	if cfg.randomBlackhole {
		connFaults = append(connFaults, randomBlackhole)
	}
	if cfg.corruptSend {
		connFaults = append(connFaults, corruptSend)
	}
	if cfg.corruptReceive {
		connFaults = append(connFaults, corruptReceive)
	}

	var disp dispatcher
	if cfg.reorder {
		disp = newDispatcherPool()
	} else {
		disp = newDispatcherImmediate()
	}

	for {
		acceptFaults[rand.Intn(len(acceptFaults))]()
		conn, err := l.Accept()
		if err != nil {
			log.Fatal(err)
		}

		r := rand.Intn(len(connFaults))
		if rand.Intn(100) > int(100.0*cfg.connFaultRate) {
			r = 0
		}

		bc, err := newBridgeConn(conn, disp)
		if err != nil {
			log.Printf("oops %v", err)
			continue
		}
		go connFaults[r](bc)
	}
}
<commit_msg>bridge: add tx-delay and rx-delay<commit_after>// Copyright 2016 The etcd 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 main is the entry point for the local tester network bridge.
package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net"
	"sync"
	"time"
)

type bridgeConn struct {
	in  net.Conn
	out net.Conn
	d   dispatcher
}

func newBridgeConn(in net.Conn, d dispatcher) (*bridgeConn, error) {
	out, err := net.Dial("tcp", flag.Args()[1])
	if err != nil {
		in.Close()
		return nil, err
	}
	return &bridgeConn{in, out, d}, nil
}

func (b *bridgeConn) String() string {
	return fmt.Sprintf("%v <-> %v", b.in.RemoteAddr(), b.out.RemoteAddr())
}

func (b *bridgeConn) Close() {
	b.in.Close()
	b.out.Close()
}

func bridge(b *bridgeConn) {
	log.Println("bridging", b.String())
	go b.d.Copy(b.out, makeFetch(b.in))
	b.d.Copy(b.in, makeFetch(b.out))
}

func delayBridge(b *bridgeConn, txDelay, rxDelay time.Duration) {
	go b.d.Copy(b.out, makeFetchDelay(makeFetch(b.in), txDelay))
	b.d.Copy(b.in, makeFetchDelay(makeFetch(b.out), rxDelay))
}

func timeBridge(b *bridgeConn) {
	go func() {
		t := time.Duration(rand.Intn(5)+1) * time.Second
		time.Sleep(t)
		log.Printf("killing connection %s after %v\n", b.String(), t)
		b.Close()
	}()
	bridge(b)
}

func blackhole(b *bridgeConn) {
	log.Println("blackholing connection", b.String())
	io.Copy(ioutil.Discard, b.in)
	b.Close()
}

func readRemoteOnly(b *bridgeConn) {
	log.Println("one way (<-)", b.String())
	b.d.Copy(b.in, makeFetch(b.out))
}

func writeRemoteOnly(b *bridgeConn) {
	log.Println("one way (->)", b.String())
	b.d.Copy(b.out, makeFetch(b.in))
}

func corruptReceive(b *bridgeConn) {
	log.Println("corruptReceive", b.String())
	go b.d.Copy(b.in, makeFetchCorrupt(makeFetch(b.out)))
	b.d.Copy(b.out, makeFetch(b.in))
}

func corruptSend(b *bridgeConn) {
	log.Println("corruptSend", b.String())
	go b.d.Copy(b.out, makeFetchCorrupt(makeFetch(b.in)))
	b.d.Copy(b.in, makeFetch(b.out))
}

func makeFetch(c io.Reader) fetchFunc {
	return func() ([]byte, error) {
		b := make([]byte, 4096)
		n, err := c.Read(b)
		if err != nil {
			return nil, err
		}
		return b[:n], nil
	}
}

func makeFetchCorrupt(f func() ([]byte, error)) fetchFunc {
	return func() ([]byte, error) {
		b, err := f()
		if err != nil {
			return nil, err
		}
		// corrupt one byte approximately every 16K
		for i := 0; i < len(b); i++ {
			if rand.Intn(16*1024) == 0 {
				b[i] = b[i] + 1
			}
		}
		return b, nil
	}
}

func makeFetchRand(f func() ([]byte, error)) fetchFunc {
	return func() ([]byte, error) {
		if rand.Intn(10) == 0 {
			return nil, fmt.Errorf("fetchRand: done")
		}
		b, err := f()
		if err != nil {
			return nil, err
		}
		return b, nil
	}
}

func makeFetchDelay(f fetchFunc, delay time.Duration) fetchFunc {
	return func() ([]byte, error) {
		b, err := f()
		if err != nil {
			return nil, err
		}
		time.Sleep(delay)
		return b, nil
	}
}

func randomBlackhole(b *bridgeConn) {
	log.Println("random blackhole: connection", b.String())

	var wg sync.WaitGroup
	wg.Add(2)
	go func() {
		b.d.Copy(b.in, makeFetchRand(makeFetch(b.out)))
		wg.Done()
	}()
	go func() {
		b.d.Copy(b.out, makeFetchRand(makeFetch(b.in)))
		wg.Done()
	}()
	wg.Wait()
	b.Close()
}

type config struct {
	delayAccept bool
	resetListen bool

	connFaultRate   float64
	immediateClose  bool
	blackhole       bool
	timeClose       bool
	writeRemoteOnly bool
	readRemoteOnly  bool
	randomBlackhole bool
	corruptSend     bool
	corruptReceive  bool
	reorder         bool

	txDelay string
	rxDelay string
}

type acceptFaultFunc func()
type connFaultFunc func(*bridgeConn)

func main() {
	var cfg config

	flag.BoolVar(&cfg.delayAccept, "delay-accept", true, "delays accepting new connections")
	flag.BoolVar(&cfg.resetListen, "reset-listen", true, "resets the listening port")

	flag.Float64Var(&cfg.connFaultRate, "conn-fault-rate", 0.25, "rate of faulty connections")
	flag.BoolVar(&cfg.immediateClose, "immediate-close", true, "close after accept")
	flag.BoolVar(&cfg.blackhole, "blackhole", true, "reads nothing, writes go nowhere")
	flag.BoolVar(&cfg.timeClose, "time-close", true, "close after random time")
	flag.BoolVar(&cfg.writeRemoteOnly, "write-remote-only", true, "only write, no read")
	flag.BoolVar(&cfg.readRemoteOnly, "read-remote-only", true, "only read, no write")
	flag.BoolVar(&cfg.randomBlackhole, "random-blackhole", true, "blackhole after data xfer")
	flag.BoolVar(&cfg.corruptReceive, "corrupt-receive", true, "corrupt packets received from destination")
	flag.BoolVar(&cfg.corruptSend, "corrupt-send", true, "corrupt packets sent to destination")
	flag.BoolVar(&cfg.reorder, "reorder", true, "reorder packet delivery")

	flag.StringVar(&cfg.txDelay, "tx-delay", "0", "duration to delay client transmission to server")
	flag.StringVar(&cfg.rxDelay, "rx-delay", "0", "duration to delay client receive from server")

	flag.Parse()

	lAddr := flag.Args()[0]
	fwdAddr := flag.Args()[1]
	log.Println("listening on ", lAddr)
	log.Println("forwarding to ", fwdAddr)
	l, err := net.Listen("tcp", lAddr)
	if err != nil {
		log.Fatal(err)
	}
	defer l.Close()

	acceptFaults := []acceptFaultFunc{func() {}}
	if cfg.delayAccept {
		f := func() {
			log.Println("delaying accept")
			time.Sleep(3 * time.Second)
		}
		acceptFaults = append(acceptFaults, f)
	}
	if cfg.resetListen {
		f := func() {
			log.Println("reset listen port")
			l.Close()
			newListener, err := net.Listen("tcp", lAddr)
			if err != nil {
				log.Fatal(err)
			}
			l = newListener

		}
		acceptFaults = append(acceptFaults, f)
	}

	connFaults := []connFaultFunc{func(b *bridgeConn) { bridge(b) }}
	if cfg.immediateClose {
		f := func(b *bridgeConn) {
			log.Printf("terminating connection %s immediately", b.String())
			b.Close()
		}
		connFaults = append(connFaults, f)
	}
	if cfg.blackhole {
		connFaults = append(connFaults, blackhole)
	}
	if cfg.timeClose {
		connFaults = append(connFaults, timeBridge)
	}
	if cfg.writeRemoteOnly {
		connFaults = append(connFaults, writeRemoteOnly)
	}
	if cfg.readRemoteOnly {
		connFaults = append(connFaults, readRemoteOnly)
	}
	if cfg.randomBlackhole {
		connFaults = append(connFaults, randomBlackhole)
	}
	if cfg.corruptSend {
		connFaults = append(connFaults, corruptSend)
	}
	if cfg.corruptReceive {
		connFaults = append(connFaults, corruptReceive)
	}

	txd, txdErr := time.ParseDuration(cfg.txDelay)
	if txdErr != nil {
		log.Fatal(txdErr)
	}
	rxd, rxdErr := time.ParseDuration(cfg.rxDelay)
	if rxdErr != nil {
		log.Fatal(rxdErr)
	}
	if txd != 0 || rxd != 0 {
		f := func(b *bridgeConn) { delayBridge(b, txd, rxd) }
		connFaults = append(connFaults, f)
	}

	var disp dispatcher
	if cfg.reorder {
		disp = newDispatcherPool()
	} else {
		disp = newDispatcherImmediate()
	}

	for {
		acceptFaults[rand.Intn(len(acceptFaults))]()
		conn, err := l.Accept()
		if err != nil {
			log.Fatal(err)
		}

		r := rand.Intn(len(connFaults))
		if rand.Intn(100) > int(100.0*cfg.connFaultRate) {
			r = 0
		}

		bc, err := newBridgeConn(conn, disp)
		if err != nil {
			log.Printf("oops %v", err)
			continue
		}
		go connFaults[r](bc)
	}
}
<|endoftext|>