<commit_before>package docker

import (
	"bytes"
	"fmt"

	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/schema"
	"regexp"
)

func resourceDockerContainer() *schema.Resource {
	return &schema.Resource{
		Create: resourceDockerContainerCreate,
		Read:   resourceDockerContainerRead,
		Update: resourceDockerContainerUpdate,
		Delete: resourceDockerContainerDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			// Indicates whether the container must be running.
			//
			// An assumption is made that configured containers
			// should be running; if not, they should not be in
			// the configuration. Therefore a stopped container
			// should be started. Set to false to have the
			// provider leave the container alone.
			//
			// Actively-debugged containers are likely to be
			// stopped and started manually, and Docker has
			// some provisions for restarting containers that
			// stop. The utility here comes from the fact that
			// this will delete and re-create the container
			// following the principle that the containers
			// should be pristine when started.
			"must_run": &schema.Schema{
				Type:     schema.TypeBool,
				Default:  true,
				Optional: true,
			},

			// ForceNew is not true for image because we need to
			// sane this against Docker image IDs, as each image
			// can have multiple names/tags attached do it.
			"image": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

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

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

			"command": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},

			"entrypoint": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},

			"dns": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      stringSetHash,
			},

			"publish_all_ports": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				ForceNew: true,
			},

			"restart": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Default:  "no",
				ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
					value := v.(string)
					if !regexp.MustCompile(`^(no|on-failure|always)$`).MatchString(value) {
						es = append(es, fmt.Errorf(
							"%q must be one of \"no\", \"on-failure\", or \"always\"", k))
					}
					return
				},
			},

			"max_retry_count": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				ForceNew: true,
			},

			"volumes": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: true,
				Elem:     getVolumesElem(),
				Set:      resourceDockerVolumesHash,
			},

			"ports": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: true,
				Elem:     getPortsElem(),
				Set:      resourceDockerPortsHash,
			},

			"hosts": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"ip": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							ForceNew: true,
						},

						"host": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							ForceNew: true,
						},
					},
				},
				Set: resourceDockerHostsHash,
			},

			"env": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      stringSetHash,
			},

			"links": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      stringSetHash,
			},

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

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

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

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

			"privileged": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				ForceNew: true,
			},

			"labels": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
			},

			"memory": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				ForceNew: true,
				ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
					value := v.(int)
					if value < 0 {
						es = append(es, fmt.Errorf("%q must be greater than or equal to 0", k))
					}
					return
				},
			},

			"memory_swap": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				ForceNew: true,
				ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
					value := v.(int)
					if value < -1 {
						es = append(es, fmt.Errorf("%q must be greater than or equal to -1", k))
					}
					return
				},
			},

			"cpu_shares": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				ForceNew: true,
				ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
					value := v.(int)
					if value < 0 {
						es = append(es, fmt.Errorf("%q must be greater than or equal to 0", k))
					}
					return
				},
			},

			"log_driver": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Default:  "json-file",
				ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
					value := v.(string)
					if !regexp.MustCompile(`^(json-file|syslog|journald|gelf|fluentd)$`).MatchString(value) {
						es = append(es, fmt.Errorf(
							"%q must be one of \"json-file\", \"syslog\", \"journald\", \"gelf\", or \"fluentd\"", k))
					}
					return
				},
			},

			"log_opts": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
			},
		},
	}
}

func getVolumesElem() *schema.Resource {
	return &schema.Resource{
		Schema: map[string]*schema.Schema{
			"from_container": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},

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

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

			"read_only": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				ForceNew: true,
			},
		},
	}
}

func getPortsElem() *schema.Resource {
	return &schema.Resource{
		Schema: map[string]*schema.Schema{
			"internal": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: true,
			},

			"external": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				ForceNew: true,
			},

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

			"protocol": &schema.Schema{
				Type:     schema.TypeString,
				Default:  "tcp",
				Optional: true,
				ForceNew: true,
			},
		},
	}
}

func resourceDockerPortsHash(v interface{}) int {
	var buf bytes.Buffer
	m := v.(map[string]interface{})

	buf.WriteString(fmt.Sprintf("%v-", m["internal"].(int)))

	if v, ok := m["external"]; ok {
		buf.WriteString(fmt.Sprintf("%v-", v.(int)))
	}

	if v, ok := m["ip"]; ok {
		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
	}

	if v, ok := m["protocol"]; ok {
		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
	}

	return hashcode.String(buf.String())
}

func resourceDockerHostsHash(v interface{}) int {
	var buf bytes.Buffer
	m := v.(map[string]interface{})

	if v, ok := m["ip"]; ok {
		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
	}

	if v, ok := m["host"]; ok {
		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
	}

	return hashcode.String(buf.String())
}

func resourceDockerVolumesHash(v interface{}) int {
	var buf bytes.Buffer
	m := v.(map[string]interface{})

	if v, ok := m["from_container"]; ok {
		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
	}

	if v, ok := m["container_path"]; ok {
		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
	}

	if v, ok := m["host_path"]; ok {
		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
	}

	if v, ok := m["read_only"]; ok {
		buf.WriteString(fmt.Sprintf("%v-", v.(bool)))
	}

	return hashcode.String(buf.String())
}

func stringSetHash(v interface{}) int {
	return hashcode.String(v.(string))
}
<commit_msg>provider/docker: Inline ports and volumes schemas for consistency<commit_after>package docker

import (
	"bytes"
	"fmt"

	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/schema"
	"regexp"
)

func resourceDockerContainer() *schema.Resource {
	return &schema.Resource{
		Create: resourceDockerContainerCreate,
		Read:   resourceDockerContainerRead,
		Update: resourceDockerContainerUpdate,
		Delete: resourceDockerContainerDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			// Indicates whether the container must be running.
			//
			// An assumption is made that configured containers
			// should be running; if not, they should not be in
			// the configuration. Therefore a stopped container
			// should be started. Set to false to have the
			// provider leave the container alone.
			//
			// Actively-debugged containers are likely to be
			// stopped and started manually, and Docker has
			// some provisions for restarting containers that
			// stop. The utility here comes from the fact that
			// this will delete and re-create the container
			// following the principle that the containers
			// should be pristine when started.
			"must_run": &schema.Schema{
				Type:     schema.TypeBool,
				Default:  true,
				Optional: true,
			},

			// ForceNew is not true for image because we need to
			// sane this against Docker image IDs, as each image
			// can have multiple names/tags attached do it.
			"image": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

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

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

			"command": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},

			"entrypoint": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},

			"dns": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      stringSetHash,
			},

			"publish_all_ports": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				ForceNew: true,
			},

			"restart": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Default:  "no",
				ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
					value := v.(string)
					if !regexp.MustCompile(`^(no|on-failure|always)$`).MatchString(value) {
						es = append(es, fmt.Errorf(
							"%q must be one of \"no\", \"on-failure\", or \"always\"", k))
					}
					return
				},
			},

			"max_retry_count": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				ForceNew: true,
			},

			"volumes": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"from_container": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							ForceNew: true,
						},

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

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

						"read_only": &schema.Schema{
							Type:     schema.TypeBool,
							Optional: true,
							ForceNew: true,
						},
					},
				},
				Set: resourceDockerVolumesHash,
			},

			"ports": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"internal": &schema.Schema{
							Type:     schema.TypeInt,
							Required: true,
							ForceNew: true,
						},

						"external": &schema.Schema{
							Type:     schema.TypeInt,
							Optional: true,
							ForceNew: true,
						},

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

						"protocol": &schema.Schema{
							Type:     schema.TypeString,
							Default:  "tcp",
							Optional: true,
							ForceNew: true,
						},
					},
				},
				Set: resourceDockerPortsHash,
			},

			"hosts": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"ip": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							ForceNew: true,
						},

						"host": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							ForceNew: true,
						},
					},
				},
				Set: resourceDockerHostsHash,
			},

			"env": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      stringSetHash,
			},

			"links": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      stringSetHash,
			},

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

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

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

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

			"privileged": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				ForceNew: true,
			},

			"labels": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
			},

			"memory": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				ForceNew: true,
				ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
					value := v.(int)
					if value < 0 {
						es = append(es, fmt.Errorf("%q must be greater than or equal to 0", k))
					}
					return
				},
			},

			"memory_swap": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				ForceNew: true,
				ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
					value := v.(int)
					if value < -1 {
						es = append(es, fmt.Errorf("%q must be greater than or equal to -1", k))
					}
					return
				},
			},

			"cpu_shares": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				ForceNew: true,
				ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
					value := v.(int)
					if value < 0 {
						es = append(es, fmt.Errorf("%q must be greater than or equal to 0", k))
					}
					return
				},
			},

			"log_driver": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Default:  "json-file",
				ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
					value := v.(string)
					if !regexp.MustCompile(`^(json-file|syslog|journald|gelf|fluentd)$`).MatchString(value) {
						es = append(es, fmt.Errorf(
							"%q must be one of \"json-file\", \"syslog\", \"journald\", \"gelf\", or \"fluentd\"", k))
					}
					return
				},
			},

			"log_opts": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
			},
		},
	}
}

func resourceDockerPortsHash(v interface{}) int {
	var buf bytes.Buffer
	m := v.(map[string]interface{})

	buf.WriteString(fmt.Sprintf("%v-", m["internal"].(int)))

	if v, ok := m["external"]; ok {
		buf.WriteString(fmt.Sprintf("%v-", v.(int)))
	}

	if v, ok := m["ip"]; ok {
		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
	}

	if v, ok := m["protocol"]; ok {
		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
	}

	return hashcode.String(buf.String())
}

func resourceDockerHostsHash(v interface{}) int {
	var buf bytes.Buffer
	m := v.(map[string]interface{})

	if v, ok := m["ip"]; ok {
		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
	}

	if v, ok := m["host"]; ok {
		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
	}

	return hashcode.String(buf.String())
}

func resourceDockerVolumesHash(v interface{}) int {
	var buf bytes.Buffer
	m := v.(map[string]interface{})

	if v, ok := m["from_container"]; ok {
		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
	}

	if v, ok := m["container_path"]; ok {
		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
	}

	if v, ok := m["host_path"]; ok {
		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
	}

	if v, ok := m["read_only"]; ok {
		buf.WriteString(fmt.Sprintf("%v-", v.(bool)))
	}

	return hashcode.String(buf.String())
}

func stringSetHash(v interface{}) int {
	return hashcode.String(v.(string))
}
<|endoftext|>


<commit_before>package handlers

import (
	"net/http"

	log "github.com/Sirupsen/logrus"
	"github.com/mchudgins/go-service-helper/correlationID"
	"github.com/mchudgins/go-service-helper/httpWriter"
	"github.com/mchudgins/go-service-helper/hystrix"
	"github.com/mchudgins/go-service-helper/zipkin"
	opentracing "github.com/opentracing/opentracing-go"
	"github.com/opentracing/opentracing-go/ext"
	openzipkin "github.com/openzipkin/zipkin-go-opentracing"
	//zlog "github.com/opentracing/opentracing-go/log"
)

var (
	debugMode          = true
	serviceHostPort    = "localhost:8080"
	zipkinHTTPEndpoint = "http://localhost:9411/api/v1/spans"
)

type traceLogger struct{}

func (logger traceLogger) Log(keyval ...interface{}) error {
	fields := make(log.Fields)
	len := len(keyval)

	for i := 0; i < len; i = +2 {
		if key, ok := keyval[i].(string); ok {
			fields[key] = keyval[i+1]
		} else {
			log.WithField("field", keyval[i]).
				Error("key name is not of type 'string'")
		}
	}

	log.WithFields(fields).Info("opentracer.go")

	return nil
}

func NewTracer(serviceName string) opentracing.Tracer {
	collector, err := zipkin.NewHTTPCollector(zipkinHTTPEndpoint,
		zipkin.HTTPLogger(traceLogger{}),
		zipkin.HTTPClient(hystrix.NewClient("zipkin")),
		zipkin.HTTPBatchSize(100))
	if err != nil {
		log.WithError(err).Fatal("zipkin.NewHTTPCollector failed")
	}

	tracer, err := openzipkin.NewTracer(
		openzipkin.NewRecorder(collector, debugMode, serviceHostPort, serviceName),
		openzipkin.WithLogger(traceLogger{}),
		//		zipkin.DebugMode(true),
		//		zipkin.ClientServerSameSpan(true),
	)

	if err != nil {
		log.WithError(err).Warn("unable to construct zipkin.Tracer")
	}

	opentracing.InitGlobalTracer(tracer)

	return tracer
}

// HandlerFunc is a middleware function for incoming HTTP requests.
type HandlerFunc func(next http.Handler) http.Handler

// FromHTTPRequest returns a Middleware HandlerFunc that tries to join with an
// OpenTracing trace found in the HTTP request headers and starts a new Span
// called `operationName`. If no trace could be found in the HTTP request
// headers, the Span will be a trace root. The Span is incorporated in the
// HTTP Context object and can be retrieved with
// opentracing.SpanFromContext(ctx).
func TracerFromHTTPRequest(tracer opentracing.Tracer, operationName string,
) HandlerFunc {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			span, ctx := opentracing.StartSpanFromContext(req.Context(), operationName)
			defer span.Finish()

			// tag this request with a correlation ID, so we can troubleshoot it later, if necessary
			req, corrID := correlationID.FromRequest(req)
			w.Header().Set(correlationID.CORRID, corrID)
			span.SetTag(correlationID.CORRID, corrID)
			ext.HTTPUrl.Set(span, req.URL.Path)

			// store span in context
			ctx = opentracing.ContextWithSpan(req.Context(), span)

			// update request context to include our new span
			req = req.WithContext(ctx)

			// we want the status code from the handler chain,
			// so inject an HTTPWriter, if one doesn't exist

			if _, ok := w.(*httpWriter.HTTPWriter); !ok {
				w = httpWriter.NewHTTPWriter(w)
			}

			// next middleware or actual request handler
			next.ServeHTTP(w, req)

			if hw, ok := w.(*httpWriter.HTTPWriter); ok {
				span.SetTag(string(ext.HTTPStatusCode), hw.StatusCode())
			}
		})
	}
}

func TracerFromInternalHTTPRequest(tracer opentracing.Tracer, operationName string,
) HandlerFunc {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {

			// tag this request with a correlation ID, so we can troubleshoot it later, if necessary
			req, corrID := correlationID.FromRequest(req)
			w.Header().Set(correlationID.CORRID, corrID)

			var serverSpan opentracing.Span
			appSpecificOperationName := operationName
			wireContext, err := opentracing.GlobalTracer().Extract(
				opentracing.HTTPHeaders,
				opentracing.HTTPHeadersCarrier(req.Header))
			if err != nil {
				log.WithError(err).Error("unable to extract wire context")
			}

			// Create the span referring to the RPC client if available.
			// If wireContext == nil, a root span will be created.
			serverSpan = opentracing.StartSpan(
				appSpecificOperationName,
				ext.RPCServerOption(wireContext))

			defer serverSpan.Finish()

			ext.HTTPUrl.Set(serverSpan, req.URL.Path)

			/*
				serverSpan.LogFields(
					zlog.String(string(ext.HTTPUrl), req.URL.Path),
				)
			*/

			ctx := opentracing.ContextWithSpan(req.Context(), serverSpan)

			// update request context to include our new span
			req = req.WithContext(ctx)

			// we want the status code from the handler chain,
			// so inject an HTTPWriter, if one doesn't exist

			if _, ok := w.(*httpWriter.HTTPWriter); !ok {
				w = httpWriter.NewHTTPWriter(w)
			}
			defer func() {
				if hw, ok := w.(*httpWriter.HTTPWriter); ok {
					serverSpan.SetTag(string(ext.HTTPStatusCode), hw.StatusCode())
				}
			}()

			// next middleware or actual request handler
			next.ServeHTTP(w, req)
		})
	}
}
<commit_msg>remove TracerFromInternalHTTPRequest<commit_after>package handlers

import (
	"net/http"

	log "github.com/Sirupsen/logrus"
	"github.com/mchudgins/go-service-helper/correlationID"
	"github.com/mchudgins/go-service-helper/httpWriter"
	"github.com/mchudgins/go-service-helper/hystrix"
	"github.com/mchudgins/go-service-helper/zipkin"
	opentracing "github.com/opentracing/opentracing-go"
	"github.com/opentracing/opentracing-go/ext"
	openzipkin "github.com/openzipkin/zipkin-go-opentracing"
	//zlog "github.com/opentracing/opentracing-go/log"
)

var (
	debugMode          = true
	serviceHostPort    = "localhost:8080"
	zipkinHTTPEndpoint = "http://localhost:9411/api/v1/spans"
)

type traceLogger struct{}

func (logger traceLogger) Log(keyval ...interface{}) error {
	fields := make(log.Fields)
	len := len(keyval)

	for i := 0; i < len; i = +2 {
		if key, ok := keyval[i].(string); ok {
			fields[key] = keyval[i+1]
		} else {
			log.WithField("field", keyval[i]).
				Error("key name is not of type 'string'")
		}
	}

	log.WithFields(fields).Info("opentracer.go")

	return nil
}

func NewTracer(serviceName string) opentracing.Tracer {
	collector, err := zipkin.NewHTTPCollector(zipkinHTTPEndpoint,
		zipkin.HTTPLogger(traceLogger{}),
		zipkin.HTTPClient(hystrix.NewClient("zipkin")),
		zipkin.HTTPBatchSize(100))
	if err != nil {
		log.WithError(err).Fatal("zipkin.NewHTTPCollector failed")
	}

	tracer, err := openzipkin.NewTracer(
		openzipkin.NewRecorder(collector, debugMode, serviceHostPort, serviceName),
		openzipkin.WithLogger(traceLogger{}),
		//		zipkin.DebugMode(true),
		//		zipkin.ClientServerSameSpan(true),
	)

	if err != nil {
		log.WithError(err).Warn("unable to construct zipkin.Tracer")
	}

	opentracing.InitGlobalTracer(tracer)

	return tracer
}

// HandlerFunc is a middleware function for incoming HTTP requests.
type HandlerFunc func(next http.Handler) http.Handler

// FromHTTPRequest returns a Middleware HandlerFunc that tries to join with an
// OpenTracing trace found in the HTTP request headers and starts a new Span
// called `operationName`. If no trace could be found in the HTTP request
// headers, the Span will be a trace root. The Span is incorporated in the
// HTTP Context object and can be retrieved with
// opentracing.SpanFromContext(ctx).
/*
func TracerFromHTTPRequest(tracer opentracing.Tracer, operationName string,
) HandlerFunc {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			span, ctx := opentracing.StartSpanFromContext(req.Context(), operationName)
			defer span.Finish()

			// tag this request with a correlation ID, so we can troubleshoot it later, if necessary
			req, corrID := correlationID.FromRequest(req)
			w.Header().Set(correlationID.CORRID, corrID)
			span.SetTag(correlationID.CORRID, corrID)
			ext.HTTPUrl.Set(span, req.URL.Path)

			// store span in context
			ctx = opentracing.ContextWithSpan(req.Context(), span)

			// update request context to include our new span
			req = req.WithContext(ctx)

			// we want the status code from the handler chain,
			// so inject an HTTPWriter, if one doesn't exist

			if _, ok := w.(*httpWriter.HTTPWriter); !ok {
				w = httpWriter.NewHTTPWriter(w)
			}

			// next middleware or actual request handler
			next.ServeHTTP(w, req)

			if hw, ok := w.(*httpWriter.HTTPWriter); ok {
				span.SetTag(string(ext.HTTPStatusCode), hw.StatusCode())
			}
		})
	}
}
*/

func TracerFromHTTPRequest(tracer opentracing.Tracer, operationName string,
) HandlerFunc {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {

			// tag this request with a correlation ID, so we can troubleshoot it later, if necessary
			req, corrID := correlationID.FromRequest(req)
			w.Header().Set(correlationID.CORRID, corrID)

			var serverSpan opentracing.Span
			appSpecificOperationName := operationName
			wireContext, err := opentracing.GlobalTracer().Extract(
				opentracing.HTTPHeaders,
				opentracing.HTTPHeadersCarrier(req.Header))
			if err != nil {
				// no need to handle, we'll just create a parent span later
				//log.WithError(err).Error("unable to extract wire context")
			}

			// Create the span referring to the RPC client if available.
			// If wireContext == nil, a root span will be created.
			serverSpan = opentracing.StartSpan(
				appSpecificOperationName,
				ext.RPCServerOption(wireContext))

			defer serverSpan.Finish()

			ext.HTTPUrl.Set(serverSpan, req.URL.Path)

			/*
				serverSpan.LogFields(
					zlog.String(string(ext.HTTPUrl), req.URL.Path),
				)
			*/

			ctx := opentracing.ContextWithSpan(req.Context(), serverSpan)

			// update request context to include our new span
			req = req.WithContext(ctx)

			// we want the status code from the handler chain,
			// so inject an HTTPWriter, if one doesn't exist

			if _, ok := w.(*httpWriter.HTTPWriter); !ok {
				w = httpWriter.NewHTTPWriter(w)
			}
			defer func() {
				if hw, ok := w.(*httpWriter.HTTPWriter); ok {
					serverSpan.SetTag(string(ext.HTTPStatusCode), hw.StatusCode())
				}
			}()

			// next middleware or actual request handler
			next.ServeHTTP(w, req)
		})
	}
}
<|endoftext|>


<commit_before>package resource

import (
	"fmt"

	"github.com/hashicorp/terraform/terraform"
)

// Map is a map of resources that are supported, and provides helpers for
// more easily implementing a ResourceProvider.
type Map struct {
	Mapping map[string]Resource
}

// Apply performs a create or update depending on the diff, and calls
// the proper function on the matching Resource.
func (m *Map) Apply(
	s *terraform.ResourceState,
	d *terraform.ResourceDiff,
	meta interface{}) (*terraform.ResourceState, error) {
	r, ok := m.Mapping[s.Type]
	if !ok {
		return nil, fmt.Errorf("Unknown resource type: %s", s.Type)
	}

	if d.Destroy {
		// If we're destroying a resource that doesn't exist, then we're
		// already done.
		if s.ID == "" {
			return nil, nil
		}

		// Otherwise call the destroy function
		err := r.Destroy(s, meta)
		if err == nil {
			s = nil
		}
		return s, err
	}

	if s.ID == "" {
		return r.Create(s, d, meta)
	} else {
		panic("update no implemented yet")
		//return r.Update(s, d, meta)
	}
}

// Diff peforms a diff on the proper resource type.
func (m *Map) Diff(
	s *terraform.ResourceState,
	c *terraform.ResourceConfig,
	meta interface{}) (*terraform.ResourceDiff, error) {
	r, ok := m.Mapping[s.Type]
	if !ok {
		return nil, fmt.Errorf("Unknown resource type: %s", s.Type)
	}

	return r.Diff(s, c, meta)
}

// Refresh performs a Refresh on the proper resource type.
//
// Refresh on the Resource won't be called if the state represents a
// non-created resource (ID is blank).
//
// An error is returned if the resource isn't registered.
func (m *Map) Refresh(
	s *terraform.ResourceState,
	meta interface{}) (*terraform.ResourceState, error) {
	// If the resource isn't created, don't refresh.
	if s.ID == "" {
		return s, nil
	}

	r, ok := m.Mapping[s.Type]
	if !ok {
		return nil, fmt.Errorf("Unknown resource type: %s", s.Type)
	}

	return r.Refresh(s, meta)
}

// Resources returns all the resources that are supported by this
// resource map and can be used to satisfy the Resources method of
// a ResourceProvider.
func (m *Map) Resources() []terraform.ResourceType {
	rs := make([]terraform.ResourceType, 0, len(m.Mapping))
	for k, _ := range m.Mapping {
		rs = append(rs, terraform.ResourceType{
			Name: k,
		})
	}

	return rs
}
<commit_msg>helper/resource: clearer destroy logic<commit_after>package resource

import (
	"fmt"

	"github.com/hashicorp/terraform/terraform"
)

// Map is a map of resources that are supported, and provides helpers for
// more easily implementing a ResourceProvider.
type Map struct {
	Mapping map[string]Resource
}

// Apply performs a create or update depending on the diff, and calls
// the proper function on the matching Resource.
func (m *Map) Apply(
	s *terraform.ResourceState,
	d *terraform.ResourceDiff,
	meta interface{}) (*terraform.ResourceState, error) {
	r, ok := m.Mapping[s.Type]
	if !ok {
		return nil, fmt.Errorf("Unknown resource type: %s", s.Type)
	}

	if d.Destroy {
		if s.ID != "" {
			// Destroy the resource if it is created
			err := r.Destroy(s, meta)
			if err != nil {
				return s, err
			}

			s.ID = ""
		}

		// If we're only destroying, and not creating, then return now.
		// Otherwise, we continue so that we can create a new resource.
		if !d.RequiresNew() {
			return nil, nil
		}
	}

	if s.ID == "" {
		return r.Create(s, d, meta)
	} else {
		panic("update no implemented yet")
		//return r.Update(s, d, meta)
	}
}

// Diff peforms a diff on the proper resource type.
func (m *Map) Diff(
	s *terraform.ResourceState,
	c *terraform.ResourceConfig,
	meta interface{}) (*terraform.ResourceDiff, error) {
	r, ok := m.Mapping[s.Type]
	if !ok {
		return nil, fmt.Errorf("Unknown resource type: %s", s.Type)
	}

	return r.Diff(s, c, meta)
}

// Refresh performs a Refresh on the proper resource type.
//
// Refresh on the Resource won't be called if the state represents a
// non-created resource (ID is blank).
//
// An error is returned if the resource isn't registered.
func (m *Map) Refresh(
	s *terraform.ResourceState,
	meta interface{}) (*terraform.ResourceState, error) {
	// If the resource isn't created, don't refresh.
	if s.ID == "" {
		return s, nil
	}

	r, ok := m.Mapping[s.Type]
	if !ok {
		return nil, fmt.Errorf("Unknown resource type: %s", s.Type)
	}

	return r.Refresh(s, meta)
}

// Resources returns all the resources that are supported by this
// resource map and can be used to satisfy the Resources method of
// a ResourceProvider.
func (m *Map) Resources() []terraform.ResourceType {
	rs := make([]terraform.ResourceType, 0, len(m.Mapping))
	for k, _ := range m.Mapping {
		rs = append(rs, terraform.ResourceType{
			Name: k,
		})
	}

	return rs
}
<|endoftext|>


<commit_before>package octokit

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestFollowersService_AllFollowers(t *testing.T) {
	setup()
	defer tearDown()

	mux.HandleFunc("/users/obsc/followers", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		header := w.Header()
		link := fmt.Sprintf(`<%s>; rel="next", <%s>; rel="last"`, testURLOf("/users/obsc/followers?page=2"), testURLOf("/users/obsc/followers?page=3"))
		header.Set("Link", link)

		respondWithJSON(w, loadFixture("followers.json"))
	})

	url, _ := FollowerUrl.Expand(M{"user": "obsc"})
	followers, result := client.Followers(url).All()

	assert.False(t, result.HasError())

	validateUser(t, followers)

	assert.Equal(t, testURLStringOf("/users/obsc/followers?page=2"), string(*result.NextPage))
	assert.Equal(t, testURLStringOf("/users/obsc/followers?page=3"), string(*result.LastPage))

	validateNextPage(t, result)
}

func TestFollowersService_AllFollowersCurrent(t *testing.T) {
	setup()
	defer tearDown()

	mux.HandleFunc("/user/followers", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		header := w.Header()
		link := fmt.Sprintf(`<%s>; rel="next", <%s>; rel="last"`, testURLOf("/user/followers?page=2"), testURLOf("/user/followers?page=3"))
		header.Set("Link", link)

		respondWithJSON(w, loadFixture("followers.json"))
	})

	url, _ := CurrentFollowerUrl.Expand(nil)
	followers, result := client.Followers(url).All()

	assert.False(t, result.HasError())

	validateUser(t, followers)

	assert.Equal(t, testURLStringOf("/user/followers?page=2"), string(*result.NextPage))
	assert.Equal(t, testURLStringOf("/user/followers?page=3"), string(*result.LastPage))

	validateNextPage(t, result)
}

func TestFollowersService_AllFollowing(t *testing.T) {
	setup()
	defer tearDown()

	mux.HandleFunc("/users/obsc/following", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		header := w.Header()
		link := fmt.Sprintf(`<%s>; rel="next", <%s>; rel="last"`, testURLOf("/users/obsc/following?page=2"), testURLOf("/users/obsc/following?page=3"))
		header.Set("Link", link)

		respondWithJSON(w, loadFixture("followers.json"))
	})

	url, _ := FollowingUrl.Expand(M{"user": "obsc"})
	allFollowing, result := client.Followers(url).All()

	assert.False(t, result.HasError())

	validateUser(t, allFollowing)

	assert.Equal(t, testURLStringOf("/users/obsc/following?page=2"), string(*result.NextPage))
	assert.Equal(t, testURLStringOf("/users/obsc/following?page=3"), string(*result.LastPage))

	validateNextPage(t, result)
}

func TestFollowersService_AllFollowingCurrent(t *testing.T) {
	setup()
	defer tearDown()

	mux.HandleFunc("/user/following", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		header := w.Header()
		link := fmt.Sprintf(`<%s>; rel="next", <%s>; rel="last"`, testURLOf("/user/following?page=2"), testURLOf("/user/following?page=3"))
		header.Set("Link", link)

		respondWithJSON(w, loadFixture("followers.json"))
	})

	url, _ := CurrentFollowingUrl.Expand(nil)
	allFollowing, result := client.Followers(url).All()

	assert.False(t, result.HasError())

	validateUser(t, allFollowing)

	assert.Equal(t, testURLStringOf("/user/following?page=2"), string(*result.NextPage))
	assert.Equal(t, testURLStringOf("/user/following?page=3"), string(*result.LastPage))

	validateNextPage(t, result)
}

func TestFollowersService_CheckFollowing(t *testing.T) {
	setup()
	defer tearDown()

	mux.HandleFunc("/users/harrisonzhao/following/obsc", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		header := w.Header()
		header.Set("Content-Type", "application/json")

		respondWithStatus(w, 204)
	})

	url, _ := CheckFollowingUrl.Expand(M{"user": "harrisonzhao", "target": "obsc"})
	result := client.Followers(url).Check()

	assert.False(t, result.HasError())
	assert.Equal(t, 204, result.Response.StatusCode)
}

func TestFollowersService_CheckCurrentFollowing(t *testing.T) {
	setup()
	defer tearDown()

	mux.HandleFunc("/user/following/obsc", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		header := w.Header()
		header.Set("Content-Type", "application/json")

		respondWithStatus(w, 204)
	})

	url, _ := CheckCurrentFollowingUrl.Expand(M{"target": "obsc"})
	result := client.Followers(url).Check()

	assert.False(t, result.HasError())
	assert.Equal(t, 204, result.Response.StatusCode)
}

func validateUser(t *testing.T, followers []User) {
	assert.Len(t, followers, 1)
	first := followers[0]

	assert.Equal(t, "harrisonzhao", first.Login)
	assert.Equal(t, 5186533, first.ID)
	assert.Equal(t, "https://avatars.githubusercontent.com/u/5186533?v=3", first.AvatarURL)
	assert.Equal(t, "", first.GravatarID)
	assert.Equal(t, "https://api.github.com/users/harrisonzhao", first.URL)
	assert.Equal(t, "https://github.com/harrisonzhao", first.HTMLURL)
	assert.Equal(t, "https://api.github.com/users/harrisonzhao/followers", first.FollowersURL)
	assert.Equal(t, "https://api.github.com/users/harrisonzhao/following{/other_user}", first.FollowingURL)
	assert.Equal(t, "https://api.github.com/users/harrisonzhao/gists{/gist_id}", first.GistsURL)
	assert.Equal(t, "https://api.github.com/users/harrisonzhao/starred{/owner}{/repo}", first.StarredURL)
	assert.Equal(t, "https://api.github.com/users/harrisonzhao/subscriptions", first.SubscriptionsURL)
	assert.Equal(t, "https://api.github.com/users/harrisonzhao/orgs", first.OrganizationsURL)
	assert.Equal(t, "https://api.github.com/users/harrisonzhao/repos", first.ReposURL)
	assert.Equal(t, "https://api.github.com/users/harrisonzhao/events{/privacy}", first.EventsURL)
	assert.Equal(t, "https://api.github.com/users/harrisonzhao/received_events", first.ReceivedEventsURL)
	assert.Equal(t, "User", first.Type)
	assert.Equal(t, false, first.SiteAdmin)
}

func validateNextPage(t *testing.T, result *Result) {
	nextPageURL, err := result.NextPage.Expand(nil)
	assert.NoError(t, err)

	followers, result := client.Followers(nextPageURL).All()
	assert.False(t, result.HasError())
	assert.Len(t, followers, 1)
}
<commit_msg>Add NoError assertions for url expanding in followers test<commit_after>package octokit

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestFollowersService_AllFollowers(t *testing.T) {
	setup()
	defer tearDown()

	mux.HandleFunc("/users/obsc/followers", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		header := w.Header()
		link := fmt.Sprintf(`<%s>; rel="next", <%s>; rel="last"`, testURLOf("/users/obsc/followers?page=2"), testURLOf("/users/obsc/followers?page=3"))
		header.Set("Link", link)

		respondWithJSON(w, loadFixture("followers.json"))
	})

	url, err := FollowerUrl.Expand(M{"user": "obsc"})
	assert.NoError(t, err)

	followers, result := client.Followers(url).All()
	assert.False(t, result.HasError())

	validateUser(t, followers)

	assert.Equal(t, testURLStringOf("/users/obsc/followers?page=2"), string(*result.NextPage))
	assert.Equal(t, testURLStringOf("/users/obsc/followers?page=3"), string(*result.LastPage))

	validateNextPage(t, result)
}

func TestFollowersService_AllFollowersCurrent(t *testing.T) {
	setup()
	defer tearDown()

	mux.HandleFunc("/user/followers", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		header := w.Header()
		link := fmt.Sprintf(`<%s>; rel="next", <%s>; rel="last"`, testURLOf("/user/followers?page=2"), testURLOf("/user/followers?page=3"))
		header.Set("Link", link)

		respondWithJSON(w, loadFixture("followers.json"))
	})

	url, _ := CurrentFollowerUrl.Expand(nil)

	followers, result := client.Followers(url).All()
	assert.False(t, result.HasError())

	validateUser(t, followers)

	assert.Equal(t, testURLStringOf("/user/followers?page=2"), string(*result.NextPage))
	assert.Equal(t, testURLStringOf("/user/followers?page=3"), string(*result.LastPage))

	validateNextPage(t, result)
}

func TestFollowersService_AllFollowing(t *testing.T) {
	setup()
	defer tearDown()

	mux.HandleFunc("/users/obsc/following", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		header := w.Header()
		link := fmt.Sprintf(`<%s>; rel="next", <%s>; rel="last"`, testURLOf("/users/obsc/following?page=2"), testURLOf("/users/obsc/following?page=3"))
		header.Set("Link", link)

		respondWithJSON(w, loadFixture("followers.json"))
	})

	url, err := FollowingUrl.Expand(M{"user": "obsc"})
	assert.NoError(t, err)

	allFollowing, result := client.Followers(url).All()
	assert.False(t, result.HasError())

	validateUser(t, allFollowing)

	assert.Equal(t, testURLStringOf("/users/obsc/following?page=2"), string(*result.NextPage))
	assert.Equal(t, testURLStringOf("/users/obsc/following?page=3"), string(*result.LastPage))

	validateNextPage(t, result)
}

func TestFollowersService_AllFollowingCurrent(t *testing.T) {
	setup()
	defer tearDown()

	mux.HandleFunc("/user/following", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		header := w.Header()
		link := fmt.Sprintf(`<%s>; rel="next", <%s>; rel="last"`, testURLOf("/user/following?page=2"), testURLOf("/user/following?page=3"))
		header.Set("Link", link)

		respondWithJSON(w, loadFixture("followers.json"))
	})

	url, _ := CurrentFollowingUrl.Expand(nil)

	allFollowing, result := client.Followers(url).All()
	assert.False(t, result.HasError())

	validateUser(t, allFollowing)

	assert.Equal(t, testURLStringOf("/user/following?page=2"), string(*result.NextPage))
	assert.Equal(t, testURLStringOf("/user/following?page=3"), string(*result.LastPage))

	validateNextPage(t, result)
}

func TestFollowersService_CheckFollowing(t *testing.T) {
	setup()
	defer tearDown()

	mux.HandleFunc("/users/harrisonzhao/following/obsc", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		header := w.Header()
		header.Set("Content-Type", "application/json")

		respondWithStatus(w, 204)
	})

	url, err := CheckFollowingUrl.Expand(M{"user": "harrisonzhao", "target": "obsc"})
	assert.NoError(t, err)

	result := client.Followers(url).Check()
	assert.False(t, result.HasError())

	assert.Equal(t, 204, result.Response.StatusCode)
}

func TestFollowersService_CheckCurrentFollowing(t *testing.T) {
	setup()
	defer tearDown()

	mux.HandleFunc("/user/following/obsc", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		header := w.Header()
		header.Set("Content-Type", "application/json")

		respondWithStatus(w, 204)
	})

	url, err := CheckCurrentFollowingUrl.Expand(M{"target": "obsc"})
	assert.NoError(t, err)

	result := client.Followers(url).Check()
	assert.False(t, result.HasError())

	assert.Equal(t, 204, result.Response.StatusCode)
}

func validateUser(t *testing.T, followers []User) {
	assert.Len(t, followers, 1)
	first := followers[0]

	assert.Equal(t, "harrisonzhao", first.Login)
	assert.Equal(t, 5186533, first.ID)
	assert.Equal(t, "https://avatars.githubusercontent.com/u/5186533?v=3", first.AvatarURL)
	assert.Equal(t, "", first.GravatarID)
	assert.Equal(t, "https://api.github.com/users/harrisonzhao", first.URL)
	assert.Equal(t, "https://github.com/harrisonzhao", first.HTMLURL)
	assert.Equal(t, "https://api.github.com/users/harrisonzhao/followers", first.FollowersURL)
	assert.Equal(t, "https://api.github.com/users/harrisonzhao/following{/other_user}", first.FollowingURL)
	assert.Equal(t, "https://api.github.com/users/harrisonzhao/gists{/gist_id}", first.GistsURL)
	assert.Equal(t, "https://api.github.com/users/harrisonzhao/starred{/owner}{/repo}", first.StarredURL)
	assert.Equal(t, "https://api.github.com/users/harrisonzhao/subscriptions", first.SubscriptionsURL)
	assert.Equal(t, "https://api.github.com/users/harrisonzhao/orgs", first.OrganizationsURL)
	assert.Equal(t, "https://api.github.com/users/harrisonzhao/repos", first.ReposURL)
	assert.Equal(t, "https://api.github.com/users/harrisonzhao/events{/privacy}", first.EventsURL)
	assert.Equal(t, "https://api.github.com/users/harrisonzhao/received_events", first.ReceivedEventsURL)
	assert.Equal(t, "User", first.Type)
	assert.Equal(t, false, first.SiteAdmin)
}

func validateNextPage(t *testing.T, result *Result) {
	nextPageURL, err := result.NextPage.Expand(nil)
	assert.NoError(t, err)

	followers, result := client.Followers(nextPageURL).All()
	assert.False(t, result.HasError())
	assert.Len(t, followers, 1)
}
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"errors"
	"io"
)

// ast is an abstract syntax tree for a configuration object. The configuration
// format should be section based( or you can say namespacing).
type ast struct {
	sections []*nodeSection
}

//Section returns the section named name or an error if the section is not found
//in the ast
func (a *ast) Section(name string) (*nodeSection, error) {
	for _, v := range a.sections {
		if v.name == name {
			return v, nil
		}
	}
	return nil, errors.New("section not found")
}

func (a *ast) ToJSON(dst io.Writer) error {
	o := make(map[string]interface{})
	for _, v := range a.sections {
		sec := make(map[string]interface{})
		for _, value := range v.values {
			sec[value.key] = value.value
		}
		o[v.name] = sec
	}
	return json.NewEncoder(dst).Encode(o)
}

//nodeSection represent a section in the configuration object. Sections are name
//spaces that contains configurations definitions under them.
type nodeSection struct {
	name   string
	line   int
	values []*nodeIdent
}

//Get access the key definition and returns its value or an error if the key is
//not part of the section.
func (n *nodeSection) Get(key string) (string, error) {
	for _, v := range n.values {
		if v.key == key {
			return v.value, nil
		}
	}
	return "", errors.New("key not found")
}

//nodeIdent represents a configuration definition, which can be the key value
//definition.
type nodeIdent struct {
	key   string
	value string
	line  int
}

// parser is a parser for configuration files. It supports utf-8 encoded
// configuration files.
//
// Only modem configuration files are supported for the momment.
type parser struct {
	tokens  []*Token
	ast     *ast
	currPos int
}

func newParser(src io.Reader) (*parser, error) {
	s := NewScanner(src)
	var toks []*Token
	var err error
	var tok *Token
	for err == nil {
		tok, err = s.Scan()
		if err != nil {
			if err.Error() != io.EOF.Error() {
				return nil, err
			}
			break
		}
		if tok != nil {
			switch tok.Type {
			case WhiteSpace, Comment:
				continue
			default:
				toks = append(toks, tok)
			}

		}
	}
	return &parser{tokens: toks, ast: &ast{}}, nil
}

func (p *parser) parse() (*ast, error) {
	var err error
	if err != nil {
		return nil, err
	}
	mainSec := &nodeSection{name: "main"}
END:
	for {
		tok := p.next()
		if tok.Type == EOF {
			break END
		}
		switch tok.Type {
		case OpenBrace:
			p.rewind()
			err = p.parseSection()
			if err != nil {
				break END
			}
		case Ident:
			p.rewind()
			err = p.parseIdent(mainSec)
			if err != nil {
				break END
			}

		}
	}
	if err != nil {
		return nil, err
	}
	p.ast.sections = append([]*nodeSection{mainSec}, p.ast.sections...)
	return p.ast, err
}

func (p *parser) next() *Token {
	if p.currPos >= len(p.tokens)-1 {
		return &Token{Type: EOF}
	}
	t := p.tokens[p.currPos]
	p.currPos++
	return t
}

func (p *parser) seek(at int) {
	p.currPos = at
}

func (p *parser) parseSection() (err error) {
	left := p.next()
	if left.Type != OpenBrace {
		return errors.New("bad token")
	}
	ns := &nodeSection{}
	completeName := false
END:
	for {
	BEGIN:
		tok := p.next()
		if tok.Type == EOF {
			p.rewind()
			break END
		}

		if !completeName {
			switch tok.Type {
			case Ident:
				ns.name = ns.name + tok.Text
				goto BEGIN
			case ClosingBrace:
				completeName = true
				goto BEGIN
			}
		}
		switch tok.Type {
		case NewLine:
			n1 := p.next()
			if n1.Type == NewLine {
				n2 := p.next()
				if n2.Type == NewLine {
					break END
				}
				p.rewind()
				goto BEGIN
			}
			goto BEGIN
		case Ident:
			p.rewind()
			err = p.parseIdent(ns)
			if err != nil {
				break END
			}
		default:
			break END
		}
	}
	if err == nil {
		p.ast.sections = append(p.ast.sections, ns)
	}
	return
}

func (p *parser) rewind() {
	p.currPos--
}

func (p *parser) parseIdent(sec *nodeSection) (err error) {
	n := &nodeIdent{}
	doneKey := false
END:
	for {
	BEGIN:
		tok := p.next()
		if tok.Type == EOF {
			p.rewind()
			break END
		}

		if !doneKey {
			switch tok.Type {
			case Ident:
				n.key = n.key + tok.Text
				goto BEGIN
			case Operand:
				doneKey = true
				goto BEGIN
			default:
				err = errors.New("some fish")
				break END
			}

		}
		switch tok.Type {
		case Ident:
			n.value = n.value + tok.Text
			goto BEGIN
		case NewLine:
			break END
		default:
			err = errors.New("some fish")
			break END
		}
	}
	if err == nil {
		sec.values = append(sec.values, n)
	}

	return
}
<commit_msg>Add documentation for *ast.ToJSON<commit_after>package config

import (
	"encoding/json"
	"errors"
	"io"
)

// ast is an abstract syntax tree for a configuration object. The configuration
// format should be section based( or you can say namespacing).
type ast struct {
	sections []*nodeSection
}

//Section returns the section named name or an error if the section is not found
//in the ast
func (a *ast) Section(name string) (*nodeSection, error) {
	for _, v := range a.sections {
		if v.name == name {
			return v, nil
		}
	}
	return nil, errors.New("section not found")
}

//ToJSON marhalls *ast to a json string and writes the result to dst
func (a *ast) ToJSON(dst io.Writer) error {
	o := make(map[string]interface{})
	for _, v := range a.sections {
		sec := make(map[string]interface{})
		for _, value := range v.values {
			sec[value.key] = value.value
		}
		o[v.name] = sec
	}
	return json.NewEncoder(dst).Encode(o)
}

//nodeSection represent a section in the configuration object. Sections are name
//spaces that contains configurations definitions under them.
type nodeSection struct {
	name   string
	line   int
	values []*nodeIdent
}

//Get access the key definition and returns its value or an error if the key is
//not part of the section.
func (n *nodeSection) Get(key string) (string, error) {
	for _, v := range n.values {
		if v.key == key {
			return v.value, nil
		}
	}
	return "", errors.New("key not found")
}

//nodeIdent represents a configuration definition, which can be the key value
//definition.
type nodeIdent struct {
	key   string
	value string
	line  int
}

// parser is a parser for configuration files. It supports utf-8 encoded
// configuration files.
//
// Only modem configuration files are supported for the momment.
type parser struct {
	tokens  []*Token
	ast     *ast
	currPos int
}

func newParser(src io.Reader) (*parser, error) {
	s := NewScanner(src)
	var toks []*Token
	var err error
	var tok *Token
	for err == nil {
		tok, err = s.Scan()
		if err != nil {
			if err.Error() != io.EOF.Error() {
				return nil, err
			}
			break
		}
		if tok != nil {
			switch tok.Type {
			case WhiteSpace, Comment:
				continue
			default:
				toks = append(toks, tok)
			}

		}
	}
	return &parser{tokens: toks, ast: &ast{}}, nil
}

func (p *parser) parse() (*ast, error) {
	var err error
	if err != nil {
		return nil, err
	}
	mainSec := &nodeSection{name: "main"}
END:
	for {
		tok := p.next()
		if tok.Type == EOF {
			break END
		}
		switch tok.Type {
		case OpenBrace:
			p.rewind()
			err = p.parseSection()
			if err != nil {
				break END
			}
		case Ident:
			p.rewind()
			err = p.parseIdent(mainSec)
			if err != nil {
				break END
			}

		}
	}
	if err != nil {
		return nil, err
	}
	p.ast.sections = append([]*nodeSection{mainSec}, p.ast.sections...)
	return p.ast, err
}

func (p *parser) next() *Token {
	if p.currPos >= len(p.tokens)-1 {
		return &Token{Type: EOF}
	}
	t := p.tokens[p.currPos]
	p.currPos++
	return t
}

func (p *parser) seek(at int) {
	p.currPos = at
}

func (p *parser) parseSection() (err error) {
	left := p.next()
	if left.Type != OpenBrace {
		return errors.New("bad token")
	}
	ns := &nodeSection{}
	completeName := false
END:
	for {
	BEGIN:
		tok := p.next()
		if tok.Type == EOF {
			p.rewind()
			break END
		}

		if !completeName {
			switch tok.Type {
			case Ident:
				ns.name = ns.name + tok.Text
				goto BEGIN
			case ClosingBrace:
				completeName = true
				goto BEGIN
			}
		}
		switch tok.Type {
		case NewLine:
			n1 := p.next()
			if n1.Type == NewLine {
				n2 := p.next()
				if n2.Type == NewLine {
					break END
				}
				p.rewind()
				goto BEGIN
			}
			goto BEGIN
		case Ident:
			p.rewind()
			err = p.parseIdent(ns)
			if err != nil {
				break END
			}
		default:
			break END
		}
	}
	if err == nil {
		p.ast.sections = append(p.ast.sections, ns)
	}
	return
}

func (p *parser) rewind() {
	p.currPos--
}

func (p *parser) parseIdent(sec *nodeSection) (err error) {
	n := &nodeIdent{}
	doneKey := false
END:
	for {
	BEGIN:
		tok := p.next()
		if tok.Type == EOF {
			p.rewind()
			break END
		}

		if !doneKey {
			switch tok.Type {
			case Ident:
				n.key = n.key + tok.Text
				goto BEGIN
			case Operand:
				doneKey = true
				goto BEGIN
			default:
				err = errors.New("some fish")
				break END
			}

		}
		switch tok.Type {
		case Ident:
			n.value = n.value + tok.Text
			goto BEGIN
		case NewLine:
			break END
		default:
			err = errors.New("some fish")
			break END
		}
	}
	if err == nil {
		sec.values = append(sec.values, n)
	}

	return
}
<|endoftext|>


<commit_before>package optmem

import (
	"testing"

	"time"

	"github.com/chihaya/chihaya/server/store"
)

var (
	peerStoreTester      = store.PreparePeerStoreTester(&peerStoreDriver{})
	peerStoreBenchmarker = store.PreparePeerStoreBenchmarker(&peerStoreDriver{})
	peerStoreTestConfig  = &store.DriverConfig{Config: &peerStoreConfig{ShardCountBits: 10, GCInterval: time.Duration(1000000000), GCCutoff: time.Duration(1000000000)}}
)

func init() {
	peerStoreTester.CompareEndpoints()
}

func TestPeerStore(t *testing.T) {
	peerStoreTester.TestPeerStore(t, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutSeeder(b *testing.B) {
	peerStoreBenchmarker.PutSeeder(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutSeeder1KInfohash(b *testing.B) {
	peerStoreBenchmarker.PutSeeder1KInfohash(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutSeeder1KSeeders(b *testing.B) {
	peerStoreBenchmarker.PutSeeder1KSeeders(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutSeeder1KInfohash1KSeeders(b *testing.B) {
	peerStoreBenchmarker.PutSeeder1KInfohash1KSeeders(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutDeleteSeeder(b *testing.B) {
	peerStoreBenchmarker.PutDeleteSeeder(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutDeleteSeeder1KInfohash(b *testing.B) {
	peerStoreBenchmarker.PutDeleteSeeder1KInfohash(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutDeleteSeeder1KSeeders(b *testing.B) {
	peerStoreBenchmarker.PutDeleteSeeder1KSeeders(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutDeleteSeeder1KInfohash1KSeeders(b *testing.B) {
	peerStoreBenchmarker.PutDeleteSeeder1KInfohash1KSeeders(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_DeleteSeederNonExist(b *testing.B) {
	peerStoreBenchmarker.DeleteSeederNonExist(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_DeleteSeederNonExist1KInfohash(b *testing.B) {
	peerStoreBenchmarker.DeleteSeederNonExist1KInfohash(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_DeleteSeederNonExist1KSeeders(b *testing.B) {
	peerStoreBenchmarker.DeleteSeederNonExist1KSeeders(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_DeleteSeederNonExist1KInfohash1KSeeders(b *testing.B) {
	peerStoreBenchmarker.DeleteSeederNonExist1KInfohash1KSeeders(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutGraduateDeleteLeecher(b *testing.B) {
	peerStoreBenchmarker.PutGraduateDeleteLeecher(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutGraduateDeleteLeecher1KInfohash(b *testing.B) {
	peerStoreBenchmarker.PutGraduateDeleteLeecher1KInfohash(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutGraduateDeleteLeecher1KSeeders(b *testing.B) {
	peerStoreBenchmarker.PutGraduateDeleteLeecher1KLeechers(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutGraduateDeleteLeecher1KInfohash1KSeeders(b *testing.B) {
	peerStoreBenchmarker.PutGraduateDeleteLeecher1KInfohash1KLeechers(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_GraduateLeecherNonExist(b *testing.B) {
	peerStoreBenchmarker.GraduateLeecherNonExist(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_GraduateLeecherNonExist1KInfohash(b *testing.B) {
	peerStoreBenchmarker.GraduateLeecherNonExist1KInfohash(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_GraduateLeecherNonExist1KSeeders(b *testing.B) {
	peerStoreBenchmarker.GraduateLeecherNonExist1KLeechers(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_GraduateLeecherNonExist1KInfohash1KSeeders(b *testing.B) {
	peerStoreBenchmarker.GraduateLeecherNonExist1KInfohash1KLeechers(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_AnnouncePeers(b *testing.B) {
	peerStoreBenchmarker.AnnouncePeers(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_AnnouncePeers1KInfohash(b *testing.B) {
	peerStoreBenchmarker.AnnouncePeers1KInfohash(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_AnnouncePeersSeeder(b *testing.B) {
	peerStoreBenchmarker.AnnouncePeersSeeder(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_AnnouncePeersSeeder1KInfohash(b *testing.B) {
	peerStoreBenchmarker.AnnouncePeersSeeder1KInfohash(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_GetSeeders(b *testing.B) {
	peerStoreBenchmarker.GetSeeders(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_GetSeeders1KInfohash(b *testing.B) {
	peerStoreBenchmarker.GetSeeders1KInfohash(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_NumSeeders(b *testing.B) {
	peerStoreBenchmarker.NumSeeders(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_NumSeeders1KInfohash(b *testing.B) {
	peerStoreBenchmarker.NumSeeders1KInfohash(b, peerStoreTestConfig)
}
<commit_msg>tests: add more tests<commit_after>package optmem

import (
	"testing"

	"time"

	"net"

	"github.com/chihaya/chihaya"
	"github.com/chihaya/chihaya/server/store"
	"github.com/stretchr/testify/require"
)

var (
	peerStoreTester      = store.PreparePeerStoreTester(&peerStoreDriver{})
	peerStoreBenchmarker = store.PreparePeerStoreBenchmarker(&peerStoreDriver{})
	peerStoreTestConfig  = &store.DriverConfig{Config: &peerStoreConfig{ShardCountBits: 10, GCInterval: time.Duration(1000000000), GCCutoff: time.Duration(1000000000)}}
)

var (
	ih = chihaya.InfoHashFromString("00000000000000000000")
	p1 = chihaya.Peer{
		IP:   net.ParseIP("1.2.3.4"),
		Port: 1234,
	}
	p2 = chihaya.Peer{
		IP:   net.ParseIP("2.3.4.5"),
		Port: 2345,
	}
)

func init() {
	peerStoreTester.CompareEndpoints()
}

func TestPutNumGetSeeder(t *testing.T) {
	ps, err := (&peerStoreDriver{}).New(peerStoreTestConfig)
	require.Nil(t, err)
	require.NotNil(t, ps)

	err = ps.PutSeeder(ih, p1)
	require.Nil(t, err)

	require.Equal(t, 1, ps.NumSeeders(ih))

	seeders4, _, err := ps.GetSeeders(ih)
	require.Nil(t, err)
	require.NotNil(t, seeders4)

	require.Equal(t, 1, len(seeders4))
	require.Equal(t, p1.Port, seeders4[0].Port)
	require.True(t, p1.IP.Equal(seeders4[0].IP))

	leechers4, leechers6, err := ps.GetLeechers(ih)
	require.Nil(t, err)
	if leechers4 != nil {
		require.Equal(t, 0, len(leechers4))
	}
	if leechers6 != nil {
		require.Equal(t, 0, len(leechers6))
	}

	e := ps.Stop()
	err = <-e
	require.Nil(t, err)
}

func TestPutNumGetLeecher(t *testing.T) {
	ps, err := (&peerStoreDriver{}).New(peerStoreTestConfig)
	require.Nil(t, err)
	require.NotNil(t, ps)

	err = ps.PutLeecher(ih, p1)
	require.Nil(t, err)

	require.Equal(t, 1, ps.NumLeechers(ih))

	leechers4, _, err := ps.GetLeechers(ih)
	require.Nil(t, err)
	require.NotNil(t, leechers4)

	require.Equal(t, 1, len(leechers4))
	require.Equal(t, p1.Port, leechers4[0].Port)
	require.True(t, p1.IP.Equal(leechers4[0].IP))

	seeders4, seeders6, err := ps.GetSeeders(ih)
	require.Nil(t, err)
	if seeders4 != nil {
		require.Equal(t, 0, len(seeders4))
	}
	if seeders6 != nil {
		require.Equal(t, 0, len(seeders6))
	}

	e := ps.Stop()
	err = <-e
	require.Nil(t, err)
}

func TestDeleteSeeder(t *testing.T) {
	ps, err := (&peerStoreDriver{}).New(peerStoreTestConfig)
	require.Nil(t, err)
	require.NotNil(t, ps)

	err = ps.PutSeeder(ih, p1)
	require.Nil(t, err)

	err = ps.PutSeeder(ih, p2)
	require.Nil(t, err)

	require.Equal(t, 2, ps.NumSeeders(ih))

	err = ps.DeleteSeeder(ih, p2)
	require.Nil(t, err)

	require.Equal(t, 1, ps.NumSeeders(ih))

	seeders4, _, err := ps.GetSeeders(ih)
	require.Nil(t, err)
	require.NotNil(t, seeders4)

	require.Equal(t, 1, len(seeders4))
	require.Equal(t, p1.Port, seeders4[0].Port)
	require.True(t, p1.IP.Equal(seeders4[0].IP))

	leechers4, leechers6, err := ps.GetLeechers(ih)
	require.Nil(t, err)
	if leechers4 != nil {
		require.Equal(t, 0, len(leechers4))
	}
	if leechers6 != nil {
		require.Equal(t, 0, len(leechers6))
	}

	e := ps.Stop()
	err = <-e
	require.Nil(t, err)
}

func TestDeleteLastSeeder(t *testing.T) {
	ps, err := (&peerStoreDriver{}).New(peerStoreTestConfig)
	require.Nil(t, err)
	require.NotNil(t, ps)

	err = ps.PutSeeder(ih, p1)
	require.Nil(t, err)

	err = ps.DeleteSeeder(ih, p1)
	require.Nil(t, err)

	require.Equal(t, 0, ps.NumSeeders(ih))

	_, _, err = ps.GetSeeders(ih)
	require.Equal(t, store.ErrResourceDoesNotExist, err)

	e := ps.Stop()
	err = <-e
	require.Nil(t, err)
}

func TestDeleteLeecher(t *testing.T) {
	ps, err := (&peerStoreDriver{}).New(peerStoreTestConfig)
	require.Nil(t, err)
	require.NotNil(t, ps)

	err = ps.PutLeecher(ih, p1)
	require.Nil(t, err)

	err = ps.PutLeecher(ih, p2)
	require.Nil(t, err)

	require.Equal(t, 2, ps.NumLeechers(ih))

	err = ps.DeleteLeecher(ih, p2)
	require.Nil(t, err)

	require.Equal(t, 1, ps.NumLeechers(ih))

	leechers4, _, err := ps.GetLeechers(ih)
	require.Nil(t, err)
	require.NotNil(t, leechers4)

	require.Equal(t, 1, len(leechers4))
	require.Equal(t, p1.Port, leechers4[0].Port)
	require.True(t, p1.IP.Equal(leechers4[0].IP))

	seeders4, seeders6, err := ps.GetSeeders(ih)
	require.Nil(t, err)
	if seeders4 != nil {
		require.Equal(t, 0, len(seeders4))
	}
	if seeders6 != nil {
		require.Equal(t, 0, len(seeders6))
	}

	e := ps.Stop()
	err = <-e
	require.Nil(t, err)
}

func TestDeleteLastLeecher(t *testing.T) {
	ps, err := (&peerStoreDriver{}).New(peerStoreTestConfig)
	require.Nil(t, err)
	require.NotNil(t, ps)

	err = ps.PutLeecher(ih, p1)
	require.Nil(t, err)

	err = ps.DeleteLeecher(ih, p1)
	require.Nil(t, err)

	require.Equal(t, 0, ps.NumLeechers(ih))

	_, _, err = ps.GetLeechers(ih)
	require.Equal(t, store.ErrResourceDoesNotExist, err)

	e := ps.Stop()
	err = <-e
	require.Nil(t, err)
}

func TestPeerStore(t *testing.T) {
	peerStoreTester.TestPeerStore(t, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutSeeder(b *testing.B) {
	peerStoreBenchmarker.PutSeeder(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutSeeder1KInfohash(b *testing.B) {
	peerStoreBenchmarker.PutSeeder1KInfohash(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutSeeder1KSeeders(b *testing.B) {
	peerStoreBenchmarker.PutSeeder1KSeeders(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutSeeder1KInfohash1KSeeders(b *testing.B) {
	peerStoreBenchmarker.PutSeeder1KInfohash1KSeeders(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutDeleteSeeder(b *testing.B) {
	peerStoreBenchmarker.PutDeleteSeeder(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutDeleteSeeder1KInfohash(b *testing.B) {
	peerStoreBenchmarker.PutDeleteSeeder1KInfohash(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutDeleteSeeder1KSeeders(b *testing.B) {
	peerStoreBenchmarker.PutDeleteSeeder1KSeeders(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutDeleteSeeder1KInfohash1KSeeders(b *testing.B) {
	peerStoreBenchmarker.PutDeleteSeeder1KInfohash1KSeeders(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_DeleteSeederNonExist(b *testing.B) {
	peerStoreBenchmarker.DeleteSeederNonExist(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_DeleteSeederNonExist1KInfohash(b *testing.B) {
	peerStoreBenchmarker.DeleteSeederNonExist1KInfohash(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_DeleteSeederNonExist1KSeeders(b *testing.B) {
	peerStoreBenchmarker.DeleteSeederNonExist1KSeeders(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_DeleteSeederNonExist1KInfohash1KSeeders(b *testing.B) {
	peerStoreBenchmarker.DeleteSeederNonExist1KInfohash1KSeeders(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutGraduateDeleteLeecher(b *testing.B) {
	peerStoreBenchmarker.PutGraduateDeleteLeecher(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutGraduateDeleteLeecher1KInfohash(b *testing.B) {
	peerStoreBenchmarker.PutGraduateDeleteLeecher1KInfohash(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutGraduateDeleteLeecher1KSeeders(b *testing.B) {
	peerStoreBenchmarker.PutGraduateDeleteLeecher1KLeechers(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_PutGraduateDeleteLeecher1KInfohash1KSeeders(b *testing.B) {
	peerStoreBenchmarker.PutGraduateDeleteLeecher1KInfohash1KLeechers(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_GraduateLeecherNonExist(b *testing.B) {
	peerStoreBenchmarker.GraduateLeecherNonExist(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_GraduateLeecherNonExist1KInfohash(b *testing.B) {
	peerStoreBenchmarker.GraduateLeecherNonExist1KInfohash(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_GraduateLeecherNonExist1KSeeders(b *testing.B) {
	peerStoreBenchmarker.GraduateLeecherNonExist1KLeechers(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_GraduateLeecherNonExist1KInfohash1KSeeders(b *testing.B) {
	peerStoreBenchmarker.GraduateLeecherNonExist1KInfohash1KLeechers(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_AnnouncePeers(b *testing.B) {
	peerStoreBenchmarker.AnnouncePeers(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_AnnouncePeers1KInfohash(b *testing.B) {
	peerStoreBenchmarker.AnnouncePeers1KInfohash(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_AnnouncePeersSeeder(b *testing.B) {
	peerStoreBenchmarker.AnnouncePeersSeeder(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_AnnouncePeersSeeder1KInfohash(b *testing.B) {
	peerStoreBenchmarker.AnnouncePeersSeeder1KInfohash(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_GetSeeders(b *testing.B) {
	peerStoreBenchmarker.GetSeeders(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_GetSeeders1KInfohash(b *testing.B) {
	peerStoreBenchmarker.GetSeeders1KInfohash(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_NumSeeders(b *testing.B) {
	peerStoreBenchmarker.NumSeeders(b, peerStoreTestConfig)
}

func BenchmarkPeerStore_NumSeeders1KInfohash(b *testing.B) {
	peerStoreBenchmarker.NumSeeders1KInfohash(b, peerStoreTestConfig)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/koding/kite"
	"fmt"
	"os"
	"sync"
	"log"
	"github.com/bitly/go-nsq"
	"runtime"
        "github.com/nsabine/microservices/controller/controllerlib"

)

var client *kite.Client
var k 	   *kite.Kite

func main() {
	fmt.Println("Starting Robber")

	k = kite.New("robber", "1.0.0")
	k.Config.Port = 6001
	k.Config.DisableAuthentication = true

        client = k.NewClient("http://" + os.Getenv("CONTROLLER_SERVICE_HOST")  + ":" + os.Getenv("CONTROLLER_SERVICE_PORT") + "/kite")
        connected, err := client.DialForever()
        if err != nil {
                k.Log.Fatal(err.Error())
        }

        // Wait until connected
        <-connected

	runtime.GOMAXPROCS(2)
	wg := &sync.WaitGroup{}
	wg.Add(2)
	go startKite()
	go startMessaging()
	wg.Wait()
}

func startKite() {
	k.HandleFunc("hello", hello)
	fmt.Println("Robber starting kite")
	k.Run()
}

func startMessaging() {
	fmt.Println("Robber configuring NSQ")

	config := nsq.NewConfig()
	q, _ := nsq.NewConsumer("tick", "ch", config)
	q.AddHandler(nsq.HandlerFunc(func(message *nsq.Message) error {
		log.Printf("Got a message: %s", string(message.Body[:]))
		tick()
		return nil
	}))

	fmt.Println("Trying to connect to NSQ: " + os.Getenv("MESSAGING_SERVICE_HOST") + ":4150")

	err := q.ConnectToNSQD(os.Getenv("MESSAGING_SERVICE_HOST") + ":4150")
	if err != nil {
		log.Panic("Could not connect")
	}
	fmt.Println("Robber starting NSQ")
}

func hello(r *kite.Request) (interface{}, error) {

	fmt.Println("Robber got hello")

	// You can return anything as result, as long as it is JSON marshalable.
	return nil, nil
}

func tick() {
	result, err := client.Tell("getState")
        var GameState [][]controllerlib.UpdateRequest
        if err := result.Unmarshal(&GameState); err != nil {
                panic(err)
        }

	x,y := controllerlib.WhereAmI(GameState, controllerlib.UpdateRequest{
		MyName: os.Getenv("HOSTNAME"),
		Type: "Robber",
		XPos: -1,
		YPos: -1,
	})
	if (x<controllerlib.XSize) {
		x++
	} else {
		x--
	}
	if (y<controllerlib.YSize) {
		y++
	} else {
		y--
	}
	_, err = client.Tell("update", &controllerlib.UpdateRequest{
		MyName: os.Getenv("HOSTNAME"),
		Type: "Robber",
		XPos: x,
		YPos: y,
	})
	if err != nil {
		panic(err)
	}
}
<commit_msg>debug<commit_after>package main

import (
	"github.com/koding/kite"
	"fmt"
	"os"
	"sync"
	"log"
	"github.com/bitly/go-nsq"
	"runtime"
        "github.com/nsabine/microservices/controller/controllerlib"

)

var client *kite.Client
var k 	   *kite.Kite

func main() {
	fmt.Println("Starting Robber")

	k = kite.New("robber", "1.0.0")
	k.Config.Port = 6001
	k.Config.DisableAuthentication = true

        client = k.NewClient("http://" + os.Getenv("CONTROLLER_SERVICE_HOST")  + ":" + os.Getenv("CONTROLLER_SERVICE_PORT") + "/kite")
        connected, err := client.DialForever()
        if err != nil {
                k.Log.Fatal(err.Error())
        }

        // Wait until connected
        <-connected

	runtime.GOMAXPROCS(2)
	wg := &sync.WaitGroup{}
	wg.Add(2)
	go startKite()
	go startMessaging()
	wg.Wait()
}

func startKite() {
	k.HandleFunc("hello", hello)
	fmt.Println("Robber starting kite")
	k.Run()
}

func startMessaging() {
	fmt.Println("Robber configuring NSQ")

	config := nsq.NewConfig()
	q, _ := nsq.NewConsumer("tick", "ch", config)
	q.AddHandler(nsq.HandlerFunc(func(message *nsq.Message) error {
		log.Printf("Got a message: %s", string(message.Body[:]))
		tick()
		return nil
	}))

	fmt.Println("Trying to connect to NSQ: " + os.Getenv("MESSAGING_SERVICE_HOST") + ":4150")

	err := q.ConnectToNSQD(os.Getenv("MESSAGING_SERVICE_HOST") + ":4150")
	if err != nil {
		log.Panic("Could not connect")
	}
	fmt.Println("Robber starting NSQ")
}

func hello(r *kite.Request) (interface{}, error) {

	fmt.Println("Robber got hello")

	// You can return anything as result, as long as it is JSON marshalable.
	return nil, nil
}

func tick() {
	result, err := client.Tell("getState")
        var GameState [][]controllerlib.UpdateRequest
        if err := result.Unmarshal(&GameState); err != nil {
                panic(err)
        }

	x,y := controllerlib.WhereAmI(GameState, controllerlib.UpdateRequest{
		MyName: os.Getenv("HOSTNAME"),
		Type: "Robber",
		XPos: -1,
		YPos: -1,
	})
        fmt.Println("I am here:", x, y)
	if (x<controllerlib.XSize) {
		x++
	} else {
		x--
	}
	if (y<controllerlib.YSize) {
		y++
	} else {
		y--
	}
	_, err = client.Tell("update", &controllerlib.UpdateRequest{
		MyName: os.Getenv("HOSTNAME"),
		Type: "Robber",
		XPos: x,
		YPos: y,
	})
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package main

//
// An example main for the router process.
//
import (
    "log"
    "github.com/trendrr/goshire/cheshire"
    "github.com/trendrr/goshire-shards/router"

)

func main() {

    bootstrap := cheshire.NewBootstrapFile("router_config.yaml")

    matcher := router.NewMatcher()
    bootstrap.Conf.Router = matcher

    //now add the services.
    matcher.AddService("http://localhost:8010")

    log.Println("Starting")
    //starts listening on all configured interfaces
    bootstrap.Start()
}
<commit_msg>update router main<commit_after>package main

//
// An example main for the router process.
//
import (
    "log"
    // "github.com/trendrr/goshire/cheshire"
    "github.com/trendrr/goshire-shards/router"
    // "os"
)

func main() {

    // lf, err := os.Create("router.log")
    // if err != nil {
    //     log.Panicf("Coulding open log file -- %s", err)
    // }
    // //log to a file
    // log.SetOutput(lf)

    r := router.NewServerFile("router_config.yaml")

    //now add the services.
    err := r.AddService("http://localhost:8010")
    if err != nil {
        log.Panic(err)
    }

    log.Println("Starting")
    //starts listening on all configured interfaces
    r.Start()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"regexp"
	"strings"
	"time"

	"github.com/Seklfreak/Robyul2/cache"
	"github.com/Seklfreak/Robyul2/emojis"
	"github.com/Seklfreak/Robyul2/helpers"
	"github.com/Seklfreak/Robyul2/metrics"
	"github.com/Seklfreak/Robyul2/modules"
	"github.com/Seklfreak/Robyul2/ratelimits"
	"github.com/bwmarrin/discordgo"
	"github.com/getsentry/raven-go"
)

// BotOnReady gets called after the gateway connected
func BotOnReady(session *discordgo.Session, event *discordgo.Ready) {
	log := cache.GetLogger()

	log.WithField("module", "bot").Info("Connected to discord!")
	log.WithField("module", "bot").Info("Invite link: " + fmt.Sprintf(
		"https://discordapp.com/oauth2/authorize?client_id=%s&scope=bot&permissions=%s",
		helpers.GetConfig().Path("discord.id").Data().(string),
		helpers.GetConfig().Path("discord.perms").Data().(string),
	))

	// Cache the session
	cache.SetSession(session)

	// Load and init all modules
	modules.Init(session)

	// Run async worker for guild changes
	go helpers.GuildSettingsUpdater()

	// Run async game-changer
	go changeGameInterval(session)

	// request guild members from the gateway
	go func() {
		for _, guild := range session.State.Guilds {
			err := session.RequestGuildMembers(guild.ID, "", 0)
			if err != nil {
				log.WithField("module", "bot").Error(fmt.Sprintf("Failed to request Members for Guild %s #%s: %s",
					guild.Name, guild.ID, err.Error()))
			}
		}
	}()

	// Run auto-leaver for non-beta guilds
	//go autoLeaver(session)

	// Run ratelimiter
	ratelimits.Container.Init()

	go func() {
		time.Sleep(3 * time.Second)

		configName := helpers.GetConfig().Path("bot.name").Data().(string)
		configAvatar := helpers.GetConfig().Path("bot.avatar").Data().(string)

		// Change avatar if desired
		if configAvatar != "" && configAvatar != session.State.User.Avatar {
			session.UserUpdate(
				"",
				"",
				session.State.User.Username,
				configAvatar,
				"",
			)
		}

		// Change name if desired
		if configName != "" && configName != session.State.User.Username {
			session.UserUpdate(
				"",
				"",
				configName,
				session.State.User.Avatar,
				"",
			)
		}
	}()

	// Run async game-changer
	//go changeGameInterval(session)

	// Run auto-leaver for non-beta guilds
	//go autoLeaver(session)
}

func BotOnMemberListChunk(session *discordgo.Session, members *discordgo.GuildMembersChunk) {
	for _, member := range members.Members {
		session.State.MemberAdd(member)
	}
}

func BotOnGuildMemberAdd(session *discordgo.Session, member *discordgo.GuildMemberAdd) {
	modules.CallExtendedPluginOnGuildMemberAdd(
		member.Member,
	)
}

func BotOnGuildMemberRemove(session *discordgo.Session, member *discordgo.GuildMemberRemove) {
	modules.CallExtendedPluginOnGuildMemberRemove(
		member.Member,
	)
}

func BotOnGuildBanAdd(session *discordgo.Session, user *discordgo.GuildBanAdd) {
	modules.CallExtendedPluginOnGuildBanAdd(
		user,
	)
}

func BotOnGuildBanRemove(session *discordgo.Session, user *discordgo.GuildBanRemove) {
	modules.CallExtendedPluginOnGuildBanRemove(
		user,
	)
}

// BotOnMessageCreate gets called after a new message was sent
// This will be called after *every* message on *every* server so it should die as soon as possible
// or spawn costly work inside of coroutines.
func BotOnMessageCreate(session *discordgo.Session, message *discordgo.MessageCreate) {
	// Ignore other bots and @everyone/@here
	if message.Author.Bot || message.MentionEveryone {
		return
	}

	if helpers.IsBlacklisted(message.Author.ID) {
		return
	}

	// Get the channel
	// Ignore the event if we cannot resolve the channel
	channel, err := cache.Channel(message.ChannelID)
	if err != nil {
		go raven.CaptureError(err, map[string]string{})
		return
	}

	if channel.Type == discordgo.ChannelTypeDM {
		// Track usage
		metrics.CleverbotRequests.Add(1)

		// Mark typing
		session.ChannelTyping(message.ChannelID)

		// Prepare content for editing
		msg := message.Content

		/// Remove our @mention
		msg = strings.Replace(msg, "<@"+session.State.User.ID+">", "", -1)

		// Trim message
		msg = strings.TrimSpace(msg)

		// Resolve other @mentions before sending the message
		for _, user := range message.Mentions {
			msg = strings.Replace(msg, "<@"+user.ID+">", user.Username, -1)
		}

		// Remove smileys
		msg = regexp.MustCompile(`:\w+:`).ReplaceAllString(msg, "")

		// Send to cleverbot
		helpers.CleverbotSend(session, channel.ID, msg)
		return
	}

	// Check if the message contains @mentions for us
	if strings.HasPrefix(message.Content, "<@") && len(message.Mentions) > 0 && message.Mentions[0].ID == session.State.User.ID {
		// Consume a key for this action
		e := ratelimits.Container.Drain(1, message.Author.ID)
		if e != nil {
			return
		}

		// Prepare content for editing
		msg := message.Content

		/// Remove our @mention
		msg = strings.Replace(msg, "<@"+session.State.User.ID+">", "", -1)

		// Trim message
		msg = strings.TrimSpace(msg)

		// Convert to []byte before matching
		bmsg := []byte(msg)

		// Match against common task patterns
		// Send to cleverbot if nothing matches
		switch {
		case regexp.MustCompile("(?i)^HELP.*").Match(bmsg):
			metrics.CommandsExecuted.Add(1)
			sendHelp(message)
			return

		case regexp.MustCompile("(?i)^PREFIX.*").Match(bmsg):
			metrics.CommandsExecuted.Add(1)
			prefix := helpers.GetPrefixForServer(channel.GuildID)
			if prefix == "" {
				cache.GetSession().ChannelMessageSend(
					channel.ID,
					helpers.GetText("bot.prefix.not-set"),
				)
			}

			cache.GetSession().ChannelMessageSend(
				channel.ID,
				helpers.GetTextF("bot.prefix.is", prefix),
			)
			return

		case regexp.MustCompile("(?i)^REFRESH CHAT SESSION$").Match(bmsg):
			metrics.CommandsExecuted.Add(1)
			helpers.RequireAdmin(message.Message, func() {
				// Refresh cleverbot session
				helpers.CleverbotRefreshSession(channel.ID)
				cache.GetSession().ChannelMessageSend(channel.ID, helpers.GetText("bot.cleverbot.refreshed"))
			})
			return

		case regexp.MustCompile("(?i)^SET PREFIX (.){1,25}$").Match(bmsg):
			metrics.CommandsExecuted.Add(1)
			helpers.RequireAdmin(message.Message, func() {
				// Extract prefix
				prefix := strings.Fields(regexp.MustCompile("(?i)^SET PREFIX\\s").ReplaceAllString(msg, ""))[0]

				// Set new prefix
				err := helpers.SetPrefixForServer(
					channel.GuildID,
					prefix,
				)

				if err != nil {
					helpers.SendError(message.Message, err)
				} else {
					cache.GetSession().ChannelMessageSend(channel.ID, helpers.GetTextF("bot.prefix.saved", prefix))
				}
			})
			return

		default:
			// Track usage
			metrics.CleverbotRequests.Add(1)

			// Mark typing
			session.ChannelTyping(message.ChannelID)

			// Resolve other @mentions before sending the message
			for _, user := range message.Mentions {
				msg = strings.Replace(msg, "<@"+user.ID+">", user.Username, -1)
			}

			// Remove smileys
			msg = regexp.MustCompile(`:\w+:`).ReplaceAllString(msg, "")

			// Send to cleverbot
			helpers.CleverbotSend(session, channel.ID, msg)
			return
		}
	}

	modules.CallExtendedPlugin(
		message.Content,
		message.Message,
	)

	// Only continue if a prefix is set
	prefix := helpers.GetPrefixForServer(channel.GuildID)
	if prefix == "" {
		return
	}

	// Check if the message is prefixed for us
	// If not exit
	if !strings.HasPrefix(message.Content, prefix) {
		return
	}

	// Check if the user is allowed to request commands
	if !ratelimits.Container.HasKeys(message.Author.ID) && !helpers.IsBotAdmin(message.Author.ID) {
		session.ChannelMessageSend(message.ChannelID, helpers.GetTextF("bot.ratelimit.hit", message.Author.ID))

		ratelimits.Container.Set(message.Author.ID, -1)
		return
	}

	// Split the message into parts
	parts := strings.Fields(message.Content)

	// Save a sanitized version of the command (no prefix)
	cmd := strings.Replace(parts[0], prefix, "", 1)

	// Check if the user calls for help
	if cmd == "h" || cmd == "help" {
		metrics.CommandsExecuted.Add(1)
		sendHelp(message)
		return
	}

	// Separate arguments from the command
	content := strings.TrimSpace(strings.Replace(message.Content, prefix+cmd, "", -1))

	// Log commands
	// TODO: add guild id to log
	cache.GetLogger().WithField("module", "bot").Debug(fmt.Sprintf("%s (#%s): %s",
		message.Author.Username, message.Author.ID, message.Content))

	// Check if a module matches said command
	modules.CallBotPlugin(cmd, content, message.Message)

	// Check if a trigger matches
	modules.CallTriggerPlugin(cmd, content, message.Message)
}

// BotOnReactionAdd gets called after a reaction is added
// This will be called after *every* reaction added on *every* server so it
// should die as soon as possible or spawn costly work inside of coroutines.
// This is currently used for the *poll* plugin.
func BotOnReactionAdd(session *discordgo.Session, reaction *discordgo.MessageReactionAdd) {
	modules.CallExtendedPluginOnReactionAdd(reaction)

	if reaction.UserID == session.State.User.ID {
		return
	}

	channel, err := helpers.GetChannel(reaction.ChannelID)
	if err != nil {
		return
	}
	if emojis.ToNumber(reaction.Emoji.Name) == -1 {
		//session.MessageReactionRemove(reaction.ChannelID, reaction.MessageID, reaction.Emoji.Name, reaction.UserID)
		return
	}
	if helpers.VotePollIfItsOne(channel.GuildID, reaction.MessageReaction) {
		helpers.UpdatePollMsg(channel.GuildID, reaction.MessageID)
	}

}

func BotOnReactionRemove(session *discordgo.Session, reaction *discordgo.MessageReactionRemove) {
	modules.CallExtendedPluginOnReactionRemove(reaction)
}

func sendHelp(message *discordgo.MessageCreate) {
	channel, err := helpers.GetChannel(message.ChannelID)
	if err != nil {
		channel.GuildID = ""
	}

	cache.GetSession().ChannelMessageSend(
		message.ChannelID,
		helpers.GetTextF("bot.help", message.Author.ID, channel.GuildID),
	)
}

// Changes the game interval every ten minutes after called
func changeGameInterval(session *discordgo.Session) {
	for {
		users := make(map[string]string)
		guilds := session.State.Guilds

		for _, guild := range guilds {
			for _, u := range guild.Members {
				users[u.User.ID] = u.User.Username
			}
		}

		err := session.UpdateStatus(0, fmt.Sprintf("%d users on %d servers | robyul.chat | _help", len(users), len(guilds)))
		if err != nil {
			raven.CaptureError(err, map[string]string{})
		}

		time.Sleep(1 * time.Hour)
	}
}
<commit_msg>[core] logs member list chunk events<commit_after>package main

import (
	"fmt"
	"regexp"
	"strings"
	"time"

	"github.com/Seklfreak/Robyul2/cache"
	"github.com/Seklfreak/Robyul2/emojis"
	"github.com/Seklfreak/Robyul2/helpers"
	"github.com/Seklfreak/Robyul2/metrics"
	"github.com/Seklfreak/Robyul2/modules"
	"github.com/Seklfreak/Robyul2/ratelimits"
	"github.com/bwmarrin/discordgo"
	"github.com/getsentry/raven-go"
)

// BotOnReady gets called after the gateway connected
func BotOnReady(session *discordgo.Session, event *discordgo.Ready) {
	log := cache.GetLogger()

	log.WithField("module", "bot").Info("Connected to discord!")
	log.WithField("module", "bot").Info("Invite link: " + fmt.Sprintf(
		"https://discordapp.com/oauth2/authorize?client_id=%s&scope=bot&permissions=%s",
		helpers.GetConfig().Path("discord.id").Data().(string),
		helpers.GetConfig().Path("discord.perms").Data().(string),
	))

	// Cache the session
	cache.SetSession(session)

	// Load and init all modules
	modules.Init(session)

	// Run async worker for guild changes
	go helpers.GuildSettingsUpdater()

	// Run async game-changer
	go changeGameInterval(session)

	// request guild members from the gateway
	go func() {
		for _, guild := range session.State.Guilds {
			err := session.RequestGuildMembers(guild.ID, "", 0)
			if err != nil {
				log.WithField("module", "bot").Error(fmt.Sprintf("Failed to request Members for Guild %s #%s: %s",
					guild.Name, guild.ID, err.Error()))
			}
		}
	}()

	// Run auto-leaver for non-beta guilds
	//go autoLeaver(session)

	// Run ratelimiter
	ratelimits.Container.Init()

	go func() {
		time.Sleep(3 * time.Second)

		configName := helpers.GetConfig().Path("bot.name").Data().(string)
		configAvatar := helpers.GetConfig().Path("bot.avatar").Data().(string)

		// Change avatar if desired
		if configAvatar != "" && configAvatar != session.State.User.Avatar {
			session.UserUpdate(
				"",
				"",
				session.State.User.Username,
				configAvatar,
				"",
			)
		}

		// Change name if desired
		if configName != "" && configName != session.State.User.Username {
			session.UserUpdate(
				"",
				"",
				configName,
				session.State.User.Avatar,
				"",
			)
		}
	}()

	// Run async game-changer
	//go changeGameInterval(session)

	// Run auto-leaver for non-beta guilds
	//go autoLeaver(session)
}

func BotOnMemberListChunk(session *discordgo.Session, members *discordgo.GuildMembersChunk) {
	cache.GetLogger().WithField("module", "bot").Debug(
		fmt.Sprintf("received guild member chunk for guild: %s (%d members)",
			members.GuildID, len(members.Members)))
	var err error
	for _, member := range members.Members {
		err = session.State.MemberAdd(member)
		if err != nil {
			raven.CaptureError(fmt.Errorf("%#v", err), map[string]string{})
		}
	}
}

func BotOnGuildMemberAdd(session *discordgo.Session, member *discordgo.GuildMemberAdd) {
	modules.CallExtendedPluginOnGuildMemberAdd(
		member.Member,
	)
}

func BotOnGuildMemberRemove(session *discordgo.Session, member *discordgo.GuildMemberRemove) {
	modules.CallExtendedPluginOnGuildMemberRemove(
		member.Member,
	)
}

func BotOnGuildBanAdd(session *discordgo.Session, user *discordgo.GuildBanAdd) {
	modules.CallExtendedPluginOnGuildBanAdd(
		user,
	)
}

func BotOnGuildBanRemove(session *discordgo.Session, user *discordgo.GuildBanRemove) {
	modules.CallExtendedPluginOnGuildBanRemove(
		user,
	)
}

// BotOnMessageCreate gets called after a new message was sent
// This will be called after *every* message on *every* server so it should die as soon as possible
// or spawn costly work inside of coroutines.
func BotOnMessageCreate(session *discordgo.Session, message *discordgo.MessageCreate) {
	// Ignore other bots and @everyone/@here
	if message.Author.Bot || message.MentionEveryone {
		return
	}

	if helpers.IsBlacklisted(message.Author.ID) {
		return
	}

	// Get the channel
	// Ignore the event if we cannot resolve the channel
	channel, err := cache.Channel(message.ChannelID)
	if err != nil {
		go raven.CaptureError(err, map[string]string{})
		return
	}

	if channel.Type == discordgo.ChannelTypeDM {
		// Track usage
		metrics.CleverbotRequests.Add(1)

		// Mark typing
		session.ChannelTyping(message.ChannelID)

		// Prepare content for editing
		msg := message.Content

		/// Remove our @mention
		msg = strings.Replace(msg, "<@"+session.State.User.ID+">", "", -1)

		// Trim message
		msg = strings.TrimSpace(msg)

		// Resolve other @mentions before sending the message
		for _, user := range message.Mentions {
			msg = strings.Replace(msg, "<@"+user.ID+">", user.Username, -1)
		}

		// Remove smileys
		msg = regexp.MustCompile(`:\w+:`).ReplaceAllString(msg, "")

		// Send to cleverbot
		helpers.CleverbotSend(session, channel.ID, msg)
		return
	}

	// Check if the message contains @mentions for us
	if strings.HasPrefix(message.Content, "<@") && len(message.Mentions) > 0 && message.Mentions[0].ID == session.State.User.ID {
		// Consume a key for this action
		e := ratelimits.Container.Drain(1, message.Author.ID)
		if e != nil {
			return
		}

		// Prepare content for editing
		msg := message.Content

		/// Remove our @mention
		msg = strings.Replace(msg, "<@"+session.State.User.ID+">", "", -1)

		// Trim message
		msg = strings.TrimSpace(msg)

		// Convert to []byte before matching
		bmsg := []byte(msg)

		// Match against common task patterns
		// Send to cleverbot if nothing matches
		switch {
		case regexp.MustCompile("(?i)^HELP.*").Match(bmsg):
			metrics.CommandsExecuted.Add(1)
			sendHelp(message)
			return

		case regexp.MustCompile("(?i)^PREFIX.*").Match(bmsg):
			metrics.CommandsExecuted.Add(1)
			prefix := helpers.GetPrefixForServer(channel.GuildID)
			if prefix == "" {
				cache.GetSession().ChannelMessageSend(
					channel.ID,
					helpers.GetText("bot.prefix.not-set"),
				)
			}

			cache.GetSession().ChannelMessageSend(
				channel.ID,
				helpers.GetTextF("bot.prefix.is", prefix),
			)
			return

		case regexp.MustCompile("(?i)^REFRESH CHAT SESSION$").Match(bmsg):
			metrics.CommandsExecuted.Add(1)
			helpers.RequireAdmin(message.Message, func() {
				// Refresh cleverbot session
				helpers.CleverbotRefreshSession(channel.ID)
				cache.GetSession().ChannelMessageSend(channel.ID, helpers.GetText("bot.cleverbot.refreshed"))
			})
			return

		case regexp.MustCompile("(?i)^SET PREFIX (.){1,25}$").Match(bmsg):
			metrics.CommandsExecuted.Add(1)
			helpers.RequireAdmin(message.Message, func() {
				// Extract prefix
				prefix := strings.Fields(regexp.MustCompile("(?i)^SET PREFIX\\s").ReplaceAllString(msg, ""))[0]

				// Set new prefix
				err := helpers.SetPrefixForServer(
					channel.GuildID,
					prefix,
				)

				if err != nil {
					helpers.SendError(message.Message, err)
				} else {
					cache.GetSession().ChannelMessageSend(channel.ID, helpers.GetTextF("bot.prefix.saved", prefix))
				}
			})
			return

		default:
			// Track usage
			metrics.CleverbotRequests.Add(1)

			// Mark typing
			session.ChannelTyping(message.ChannelID)

			// Resolve other @mentions before sending the message
			for _, user := range message.Mentions {
				msg = strings.Replace(msg, "<@"+user.ID+">", user.Username, -1)
			}

			// Remove smileys
			msg = regexp.MustCompile(`:\w+:`).ReplaceAllString(msg, "")

			// Send to cleverbot
			helpers.CleverbotSend(session, channel.ID, msg)
			return
		}
	}

	modules.CallExtendedPlugin(
		message.Content,
		message.Message,
	)

	// Only continue if a prefix is set
	prefix := helpers.GetPrefixForServer(channel.GuildID)
	if prefix == "" {
		return
	}

	// Check if the message is prefixed for us
	// If not exit
	if !strings.HasPrefix(message.Content, prefix) {
		return
	}

	// Check if the user is allowed to request commands
	if !ratelimits.Container.HasKeys(message.Author.ID) && !helpers.IsBotAdmin(message.Author.ID) {
		session.ChannelMessageSend(message.ChannelID, helpers.GetTextF("bot.ratelimit.hit", message.Author.ID))

		ratelimits.Container.Set(message.Author.ID, -1)
		return
	}

	// Split the message into parts
	parts := strings.Fields(message.Content)

	// Save a sanitized version of the command (no prefix)
	cmd := strings.Replace(parts[0], prefix, "", 1)

	// Check if the user calls for help
	if cmd == "h" || cmd == "help" {
		metrics.CommandsExecuted.Add(1)
		sendHelp(message)
		return
	}

	// Separate arguments from the command
	content := strings.TrimSpace(strings.Replace(message.Content, prefix+cmd, "", -1))

	// Log commands
	// TODO: add guild id to log
	cache.GetLogger().WithField("module", "bot").Debug(fmt.Sprintf("%s (#%s): %s",
		message.Author.Username, message.Author.ID, message.Content))

	// Check if a module matches said command
	modules.CallBotPlugin(cmd, content, message.Message)

	// Check if a trigger matches
	modules.CallTriggerPlugin(cmd, content, message.Message)
}

// BotOnReactionAdd gets called after a reaction is added
// This will be called after *every* reaction added on *every* server so it
// should die as soon as possible or spawn costly work inside of coroutines.
// This is currently used for the *poll* plugin.
func BotOnReactionAdd(session *discordgo.Session, reaction *discordgo.MessageReactionAdd) {
	modules.CallExtendedPluginOnReactionAdd(reaction)

	if reaction.UserID == session.State.User.ID {
		return
	}

	channel, err := helpers.GetChannel(reaction.ChannelID)
	if err != nil {
		return
	}
	if emojis.ToNumber(reaction.Emoji.Name) == -1 {
		//session.MessageReactionRemove(reaction.ChannelID, reaction.MessageID, reaction.Emoji.Name, reaction.UserID)
		return
	}
	if helpers.VotePollIfItsOne(channel.GuildID, reaction.MessageReaction) {
		helpers.UpdatePollMsg(channel.GuildID, reaction.MessageID)
	}

}

func BotOnReactionRemove(session *discordgo.Session, reaction *discordgo.MessageReactionRemove) {
	modules.CallExtendedPluginOnReactionRemove(reaction)
}

func sendHelp(message *discordgo.MessageCreate) {
	channel, err := helpers.GetChannel(message.ChannelID)
	if err != nil {
		channel.GuildID = ""
	}

	cache.GetSession().ChannelMessageSend(
		message.ChannelID,
		helpers.GetTextF("bot.help", message.Author.ID, channel.GuildID),
	)
}

// Changes the game interval every ten minutes after called
func changeGameInterval(session *discordgo.Session) {
	for {
		users := make(map[string]string)
		guilds := session.State.Guilds

		for _, guild := range guilds {
			for _, u := range guild.Members {
				users[u.User.ID] = u.User.Username
			}
		}

		err := session.UpdateStatus(0, fmt.Sprintf("%d users on %d servers | robyul.chat | _help", len(users), len(guilds)))
		if err != nil {
			raven.CaptureError(err, map[string]string{})
		}

		time.Sleep(1 * time.Hour)
	}
}
<|endoftext|>