<commit_before>package main

import (
	"io"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/pkg/errors"
	"github.com/tomnomnom/linkheader"
	"golang.org/x/sync/errgroup"
)

func PubSubMakeCallbackURL(baseURL, id string) string {
	return baseURL + "/" + id
}

type PubSubSubscription struct {
	ID          string
	Hub         string
	Topic       string
	CallbackURL string
	ExpiresAt   *time.Time
}

func (s *PubSubSubscription) Remaining(t time.Time) time.Duration {
	if s.ExpiresAt == nil {
		return 0
	}

	if s.ExpiresAt.Before(t) {
		return 0
	}

	return s.ExpiresAt.Sub(t)
}

type PubSubState interface {
	All() (subscriptions []PubSubSubscription, err error)
	Add(hub, topic, baseURL string) (id, callbackURL string, existed bool, err error)
	Get(hub, topic string) (subscription *PubSubSubscription, err error)
	GetByID(id string) (subscription *PubSubSubscription, err error)
	Set(hub, topic string, expiresAt time.Time) (err error)
	Del(hub, topic string) (err error)
}

type PubSubMessageHandler func(id string, s *PubSubSubscription, rd io.ReadCloser)

type PubSubClient struct {
	CallbackURL string
	State       PubSubState
	OnMessage   PubSubMessageHandler
}

func NewPubSubClient(callbackURL string, state PubSubState, onMessage PubSubMessageHandler) *PubSubClient {
	return &PubSubClient{
		CallbackURL: callbackURL,
		State:       state,
		OnMessage:   onMessage,
	}
}

func (c *PubSubClient) Refresh(forceUpdate bool, interval time.Duration) error {
	a, err := c.State.All()
	if err != nil {
		return errors.Wrap(err, "PubSubClient.Refresh")
	}

	var g errgroup.Group

	for _, e := range a {
		e := e

		callbackURL := c.CallbackURL + "/" + e.ID

		if forceUpdate || e.Remaining(time.Now()) < interval || e.CallbackURL != callbackURL {
			logrus.WithFields(logrus.Fields{
				"id":               e.ID,
				"hub":              e.Hub,
				"topic":            e.Topic,
				"callback_url":     e.CallbackURL,
				"new_callback_url": callbackURL,
				"expires_at":       e.ExpiresAt,
				"force_update":     forceUpdate,
			}).Debug("pubsub: refreshing subscription")

			g.Go(func() error {
				return errors.Wrap(PubSubSubscribe(e.Hub, e.Topic, callbackURL), "PubSubClient.RefreshWorker")
			})
		}
	}

	return errors.Wrap(g.Wait(), "PubSubClient.Refresh")
}

func (c *PubSubClient) Subscribe(hub, topic string) error {
	_, callbackURL, existed, err := c.State.Add(hub, topic, c.CallbackURL)
	if err != nil {
		return errors.Wrap(err, "PubSubClient.Subscribe")
	}

	if !existed {
		if err := PubSubSubscribe(hub, topic, callbackURL); err != nil {
			return errors.Wrap(err, "PubSubClient.Subscribe")
		}
	}

	return nil
}

func (c *PubSubClient) Unsubscribe(hub, topic string) error {
	s, err := c.State.Get(hub, topic)
	if err != nil {
		return errors.Wrap(err, "PubSubClient.Unsubscribe")
	}

	if s != nil {
		if err := PubSubUnsubscribe(hub, topic, s.CallbackURL); err != nil {
			return errors.Wrap(err, "PubSubClient.Unsubscribe")
		}

		if err := c.State.Del(s.Hub, s.Topic); err != nil {
			return errors.Wrap(err, "PubSubClient.Unsubscribe")
		}
	}

	return nil
}

func (c *PubSubClient) Handler() *PubSubHandler {
	return &PubSubHandler{
		OnChallenge: func(id, topic, mode string, leaseTime time.Duration) error {
			logrus.WithFields(logrus.Fields{
				"id":         id,
				"topic":      topic,
				"mode":       mode,
				"lease_time": leaseTime,
			}).Debug("pubsub: received challenge")

			s, err := c.State.GetByID(id)
			if err != nil {
				return err
			}

			if s == nil {
				return errors.Errorf("PubSubClient.Handler: subscription not found")
			}

			return errors.Wrap(c.State.Set(s.Hub, s.Topic, time.Now().Add(leaseTime)), "PubSubClient.Handler")
		},
		OnMessage: func(id, topic string, rd io.ReadCloser) {
			logrus.WithFields(logrus.Fields{
				"id":    id,
				"topic": topic,
			}).Debug("pubsub: received message")

			defer rd.Close()

			if c.OnMessage != nil {
				s, err := c.State.GetByID(id)
				if err != nil {
					return
				}

				c.OnMessage(id, s, rd)
			}
		},
	}
}

func PubSubAlter(hub, topic, callbackURL, mode string) error {
	res, err := http.PostForm(hub, url.Values{
		"hub.mode":     []string{mode},
		"hub.topic":    []string{topic},
		"hub.callback": []string{callbackURL},
		"hub.verify":   []string{"sync"},
	})

	if err != nil {
		return errors.Wrap(err, "PubSubAlter")
	}

	if res.StatusCode < 200 || res.StatusCode >= 300 {
		return errors.Errorf("PubSubAlter: invalid status code; expected 2xx but got %d", res.StatusCode)
	}

	return nil
}

func PubSubSubscribe(hub, topic, callbackURL string) error {
	return errors.Wrap(PubSubAlter(hub, topic, callbackURL, "subscribe"), "PubSubSubscribe")
}

func PubSubUnsubscribe(hub, topic, callbackURL string) error {
	return errors.Wrap(PubSubAlter(hub, topic, callbackURL, "unsubscribe"), "PubSubSubscribe")
}

type PubSubHandler struct {
	OnChallenge func(id, topic, mode string, leaseTime time.Duration) error
	OnMessage   func(id, topic string, rd io.ReadCloser)
}

func (h *PubSubHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()

	bits := strings.Split(r.URL.Path, "/")
	id := bits[len(bits)-1]

	if r.Method == http.MethodGet && q.Get("hub.challenge") != "" {
		if h.OnChallenge != nil {
			leaseTime := time.Hour * 12
			if n, err := strconv.ParseInt(q.Get("hub.lease_seconds"), 10, 64); err == nil {
				leaseTime = time.Second * time.Duration(n)
			}

			if err := h.OnChallenge(id, q.Get("hub.topic"), q.Get("hub.mode"), leaseTime); err != nil {
				rw.WriteHeader(http.StatusNotFound)
				return
			}
		}

		rw.Write([]byte(r.URL.Query().Get("hub.challenge")))
		return
	}

	if r.Method != http.MethodPost {
		rw.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	links := linkheader.ParseMultiple(r.Header["Link"])

	var topic string
	if a := links.FilterByRel("self"); len(a) > 0 {
		topic = a[0].URL
	}

	rw.WriteHeader(http.StatusAccepted)

	if h.OnMessage != nil {
		h.OnMessage(id, topic, r.Body)
	}
}
<commit_msg>better logging when subscriptions fail<commit_after>package main

import (
	"io"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/pkg/errors"
	"github.com/tomnomnom/linkheader"
	"golang.org/x/sync/errgroup"
)

func PubSubMakeCallbackURL(baseURL, id string) string {
	return baseURL + "/" + id
}

type PubSubSubscription struct {
	ID          string
	Hub         string
	Topic       string
	CallbackURL string
	ExpiresAt   *time.Time
}

func (s *PubSubSubscription) Remaining(t time.Time) time.Duration {
	if s.ExpiresAt == nil {
		return 0
	}

	if s.ExpiresAt.Before(t) {
		return 0
	}

	return s.ExpiresAt.Sub(t)
}

type PubSubState interface {
	All() (subscriptions []PubSubSubscription, err error)
	Add(hub, topic, baseURL string) (id, callbackURL string, existed bool, err error)
	Get(hub, topic string) (subscription *PubSubSubscription, err error)
	GetByID(id string) (subscription *PubSubSubscription, err error)
	Set(hub, topic string, expiresAt time.Time) (err error)
	Del(hub, topic string) (err error)
}

type PubSubMessageHandler func(id string, s *PubSubSubscription, rd io.ReadCloser)

type PubSubClient struct {
	CallbackURL string
	State       PubSubState
	OnMessage   PubSubMessageHandler
}

func NewPubSubClient(callbackURL string, state PubSubState, onMessage PubSubMessageHandler) *PubSubClient {
	return &PubSubClient{
		CallbackURL: callbackURL,
		State:       state,
		OnMessage:   onMessage,
	}
}

func (c *PubSubClient) Refresh(forceUpdate bool, interval time.Duration) error {
	a, err := c.State.All()
	if err != nil {
		return errors.Wrap(err, "PubSubClient.Refresh")
	}

	var g errgroup.Group

	for _, e := range a {
		e := e

		callbackURL := c.CallbackURL + "/" + e.ID

		if forceUpdate || e.Remaining(time.Now()) < interval || e.CallbackURL != callbackURL {
			l := logrus.WithFields(logrus.Fields{
				"id":               e.ID,
				"hub":              e.Hub,
				"topic":            e.Topic,
				"callback_url":     e.CallbackURL,
				"new_callback_url": callbackURL,
				"expires_at":       e.ExpiresAt,
				"force_update":     forceUpdate,
			})

			l.Debug("pubsub: refreshing subscription")

			g.Go(func() error {
				if err := PubSubSubscribe(e.Hub, e.Topic, callbackURL); err != nil {
					l.WithError(err).Warn("pubsub: couldn't subscribe to topic")
					return errors.Wrap(err, "PubSubClient.RefreshWorker")
				}

				l.Debug("pubsub: subscribed successfully")
				return nil
			})
		}
	}

	return errors.Wrap(g.Wait(), "PubSubClient.Refresh")
}

func (c *PubSubClient) Subscribe(hub, topic string) error {
	_, callbackURL, existed, err := c.State.Add(hub, topic, c.CallbackURL)
	if err != nil {
		return errors.Wrap(err, "PubSubClient.Subscribe")
	}

	if !existed {
		if err := PubSubSubscribe(hub, topic, callbackURL); err != nil {
			return errors.Wrap(err, "PubSubClient.Subscribe")
		}
	}

	return nil
}

func (c *PubSubClient) Unsubscribe(hub, topic string) error {
	s, err := c.State.Get(hub, topic)
	if err != nil {
		return errors.Wrap(err, "PubSubClient.Unsubscribe")
	}

	if s != nil {
		if err := PubSubUnsubscribe(hub, topic, s.CallbackURL); err != nil {
			return errors.Wrap(err, "PubSubClient.Unsubscribe")
		}

		if err := c.State.Del(s.Hub, s.Topic); err != nil {
			return errors.Wrap(err, "PubSubClient.Unsubscribe")
		}
	}

	return nil
}

func (c *PubSubClient) Handler() *PubSubHandler {
	return &PubSubHandler{
		OnChallenge: func(id, topic, mode string, leaseTime time.Duration) error {
			logrus.WithFields(logrus.Fields{
				"id":         id,
				"topic":      topic,
				"mode":       mode,
				"lease_time": leaseTime,
			}).Debug("pubsub: received challenge")

			s, err := c.State.GetByID(id)
			if err != nil {
				return err
			}

			if s == nil {
				return errors.Errorf("PubSubClient.Handler: subscription not found")
			}

			return errors.Wrap(c.State.Set(s.Hub, s.Topic, time.Now().Add(leaseTime)), "PubSubClient.Handler")
		},
		OnMessage: func(id, topic string, rd io.ReadCloser) {
			logrus.WithFields(logrus.Fields{
				"id":    id,
				"topic": topic,
			}).Debug("pubsub: received message")

			defer rd.Close()

			if c.OnMessage != nil {
				s, err := c.State.GetByID(id)
				if err != nil {
					return
				}

				c.OnMessage(id, s, rd)
			}
		},
	}
}

func PubSubAlter(hub, topic, callbackURL, mode string) error {
	res, err := http.PostForm(hub, url.Values{
		"hub.mode":     []string{mode},
		"hub.topic":    []string{topic},
		"hub.callback": []string{callbackURL},
		"hub.verify":   []string{"sync"},
	})

	if err != nil {
		return errors.Wrap(err, "PubSubAlter")
	}

	if res.StatusCode < 200 || res.StatusCode >= 300 {
		return errors.Errorf("PubSubAlter: invalid status code; expected 2xx but got %d", res.StatusCode)
	}

	return nil
}

func PubSubSubscribe(hub, topic, callbackURL string) error {
	return errors.Wrap(PubSubAlter(hub, topic, callbackURL, "subscribe"), "PubSubSubscribe")
}

func PubSubUnsubscribe(hub, topic, callbackURL string) error {
	return errors.Wrap(PubSubAlter(hub, topic, callbackURL, "unsubscribe"), "PubSubSubscribe")
}

type PubSubHandler struct {
	OnChallenge func(id, topic, mode string, leaseTime time.Duration) error
	OnMessage   func(id, topic string, rd io.ReadCloser)
}

func (h *PubSubHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()

	bits := strings.Split(r.URL.Path, "/")
	id := bits[len(bits)-1]

	if r.Method == http.MethodGet && q.Get("hub.challenge") != "" {
		if h.OnChallenge != nil {
			leaseTime := time.Hour * 12
			if n, err := strconv.ParseInt(q.Get("hub.lease_seconds"), 10, 64); err == nil {
				leaseTime = time.Second * time.Duration(n)
			}

			if err := h.OnChallenge(id, q.Get("hub.topic"), q.Get("hub.mode"), leaseTime); err != nil {
				rw.WriteHeader(http.StatusNotFound)
				return
			}
		}

		rw.Write([]byte(r.URL.Query().Get("hub.challenge")))
		return
	}

	if r.Method != http.MethodPost {
		rw.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	links := linkheader.ParseMultiple(r.Header["Link"])

	var topic string
	if a := links.FilterByRel("self"); len(a) > 0 {
		topic = a[0].URL
	}

	rw.WriteHeader(http.StatusAccepted)

	if h.OnMessage != nil {
		h.OnMessage(id, topic, r.Body)
	}
}
<|endoftext|>


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

package gocql

import (
	"io"
	"net"
	"strings"
	"sync"
	"sync/atomic"
	"testing"
	"time"
)

type TestServer struct {
	Address  string
	t        *testing.T
	nreq     uint64
	listen   net.Listener
	nKillReq uint64
}

func TestSimple(t *testing.T) {
	srv := NewTestServer(t)
	defer srv.Stop()

	db, err := NewCluster(srv.Address).CreateSession()
	if err != nil {
		t.Errorf("NewCluster: %v", err)
	}

	if err := db.Query("void").Exec(); err != nil {
		t.Error(err)
	}
}

func TestClosed(t *testing.T) {
	srv := NewTestServer(t)
	defer srv.Stop()

	session, err := NewCluster(srv.Address).CreateSession()
	if err != nil {
		t.Errorf("NewCluster: %v", err)
	}
	session.Close()

	if err := session.Query("void").Exec(); err != ErrSessionClosed {
		t.Errorf("expected %#v, got %#v", ErrSessionClosed, err)
	}
}

func TestTimeout(t *testing.T) {
	srv := NewTestServer(t)
	defer srv.Stop()

	db, err := NewCluster(srv.Address).CreateSession()
	if err != nil {
		t.Errorf("NewCluster: %v", err)
	}

	go func() {
		<-time.After(2 * time.Second)
		t.Fatal("no timeout")
	}()

	if err := db.Query("kill").Exec(); err == nil {
		t.Fatal("expected error")
	}
}

// TestQueryRetry will test to make sure that gocql will execute
// the exact amount of retry queries designated by the user.
func TestQueryRetry(t *testing.T) {
	srv := NewTestServer(t)
	defer srv.Stop()

	db, err := NewCluster(srv.Address).CreateSession()
	if err != nil {
		t.Errorf("NewCluster: %v", err)
	}

	go func() {
		<-time.After(5 * time.Second)
		t.Fatal("no timeout")
	}()
	rt := RetryPolicy{NumRetries: 1}

	if err := db.Query("kill").RetryPolicy(rt).Exec(); err == nil {
		t.Fatal("expected error")
	}
	//Minus 1 from the nKillReq variable since there is the initial query attempt
	if srv.nKillReq-1 != uint64(rt.NumRetries) {
		t.Fatalf("failed to retry the query %v time(s). Query executed %v times", rt.NumRetries, srv.nKillReq-1)
	}
}

func TestSlowQuery(t *testing.T) {
	srv := NewTestServer(t)
	defer srv.Stop()

	db, err := NewCluster(srv.Address).CreateSession()
	if err != nil {
		t.Errorf("NewCluster: %v", err)
	}

	if err := db.Query("slow").Exec(); err != nil {
		t.Fatal(err)
	}
}

func TestRoundRobin(t *testing.T) {
	servers := make([]*TestServer, 5)
	addrs := make([]string, len(servers))
	for i := 0; i < len(servers); i++ {
		servers[i] = NewTestServer(t)
		addrs[i] = servers[i].Address
		defer servers[i].Stop()
	}
	cluster := NewCluster(addrs...)
	db, err := cluster.CreateSession()
	time.Sleep(1 * time.Second) //Sleep to allow the Cluster.fillPool to complete

	if err != nil {
		t.Errorf("NewCluster: %v", err)
	}

	var wg sync.WaitGroup
	wg.Add(5)
	for i := 0; i < 5; i++ {
		go func() {
			for j := 0; j < 5; j++ {
				if err := db.Query("void").Exec(); err != nil {
					t.Fatal(err)
				}
			}
			wg.Done()
		}()
	}
	wg.Wait()

	diff := 0
	for i := 1; i < len(servers); i++ {
		d := 0
		if servers[i].nreq > servers[i-1].nreq {
			d = int(servers[i].nreq - servers[i-1].nreq)
		} else {
			d = int(servers[i-1].nreq - servers[i].nreq)
		}
		if d > diff {
			diff = d
		}
	}

	if diff > 0 {
		t.Fatal("diff:", diff)
	}
}

func TestConnClosing(t *testing.T) {
	srv := NewTestServer(t)
	defer srv.Stop()

	db, err := NewCluster(srv.Address).CreateSession()
	if err != nil {
		t.Errorf("NewCluster: %v", err)
	}
	defer db.Close()

	numConns := db.cfg.NumConns
	count := db.cfg.NumStreams * numConns

	wg := &sync.WaitGroup{}
	wg.Add(count)
	for i := 0; i < count; i++ {
		go func(wg *sync.WaitGroup) {
			wg.Done()
			db.Query("kill").Exec()
		}(wg)
	}

	wg.Wait()

	time.Sleep(1 * time.Second) //Sleep so the fillPool can complete.
	pool := db.Pool.(ConnectionPool)
	conns := pool.Size()

	if conns != numConns {
		t.Fatalf("Expected to have %d connections but have %d", numConns, conns)
	}
}

func TestConnectHander(t *testing.T) {
	srv := NewTestServer(t)
	defer srv.Stop()

	called := false

	cluster_conf := NewCluster(srv.Address)
	cluster_conf.ConnectHandler = func(p ConnectionPool) {
		called = true
		if p.Size() <= 0 {
			t.Errorf("No new connection detected in pool: %v", p)
		}
	}

	_, err := cluster_conf.CreateSession()
	if err != nil {
		t.Errorf("NewCluster: %v", err)
	}

	if !called {
		t.Errorf("Connect handler not called")
	}
}

func NewTestServer(t *testing.T) *TestServer {
	laddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	listen, err := net.ListenTCP("tcp", laddr)
	if err != nil {
		t.Fatal(err)
	}
	srv := &TestServer{Address: listen.Addr().String(), listen: listen, t: t}
	go srv.serve()
	return srv
}

func (srv *TestServer) serve() {
	defer srv.listen.Close()
	for {
		conn, err := srv.listen.Accept()
		if err != nil {
			break
		}
		go func(conn net.Conn) {
			defer conn.Close()
			for {
				frame := srv.readFrame(conn)
				atomic.AddUint64(&srv.nreq, 1)
				srv.process(frame, conn)
			}
		}(conn)
	}
}

func (srv *TestServer) Stop() {
	srv.listen.Close()
}

func (srv *TestServer) process(frame frame, conn net.Conn) {
	switch frame[3] {
	case opStartup:
		frame = frame[:headerSize]
		frame.setHeader(protoResponse, 0, frame[2], opReady)
	case opQuery:
		input := frame
		input.skipHeader()
		query := strings.TrimSpace(input.readLongString())
		frame = frame[:headerSize]
		frame.setHeader(protoResponse, 0, frame[2], opResult)
		first := query
		if n := strings.Index(query, " "); n > 0 {
			first = first[:n]
		}
		switch strings.ToLower(first) {
		case "kill":
			atomic.AddUint64(&srv.nKillReq, 1)
			select {}
		case "slow":
			go func() {
				<-time.After(1 * time.Second)
				frame.writeInt(resultKindVoid)
				frame.setLength(len(frame) - headerSize)
				if _, err := conn.Write(frame); err != nil {
					return
				}
			}()
			return
		case "use":
			frame.writeInt(3)
			frame.writeString(strings.TrimSpace(query[3:]))
		case "void":
			frame.writeInt(resultKindVoid)
		default:
			frame.writeInt(resultKindVoid)
		}
	default:
		frame = frame[:headerSize]
		frame.setHeader(protoResponse, 0, frame[2], opError)
		frame.writeInt(0)
		frame.writeString("not supported")
	}
	frame.setLength(len(frame) - headerSize)
	if _, err := conn.Write(frame); err != nil {
		return
	}
}

func (srv *TestServer) readFrame(conn net.Conn) frame {
	frame := make(frame, headerSize, headerSize+512)
	if _, err := io.ReadFull(conn, frame); err != nil {
		srv.t.Fatal(err)
	}
	if n := frame.Length(); n > 0 {
		frame.grow(n)
		if _, err := io.ReadFull(conn, frame[headerSize:]); err != nil {
			srv.t.Fatal(err)
		}
	}
	return frame
}
<commit_msg>add test for disconnect handler<commit_after>// Copyright (c) 2012 The gocql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gocql

import (
	"io"
	"net"
	"strings"
	"sync"
	"sync/atomic"
	"testing"
	"time"
)

type TestServer struct {
	Address  string
	t        *testing.T
	nreq     uint64
	listen   net.Listener
	nKillReq uint64
}

func TestSimple(t *testing.T) {
	srv := NewTestServer(t)
	defer srv.Stop()

	db, err := NewCluster(srv.Address).CreateSession()
	if err != nil {
		t.Errorf("NewCluster: %v", err)
	}

	if err := db.Query("void").Exec(); err != nil {
		t.Error(err)
	}
}

func TestClosed(t *testing.T) {
	srv := NewTestServer(t)
	defer srv.Stop()

	session, err := NewCluster(srv.Address).CreateSession()
	if err != nil {
		t.Errorf("NewCluster: %v", err)
	}
	session.Close()

	if err := session.Query("void").Exec(); err != ErrSessionClosed {
		t.Errorf("expected %#v, got %#v", ErrSessionClosed, err)
	}
}

func TestTimeout(t *testing.T) {
	srv := NewTestServer(t)
	defer srv.Stop()

	db, err := NewCluster(srv.Address).CreateSession()
	if err != nil {
		t.Errorf("NewCluster: %v", err)
	}

	go func() {
		<-time.After(2 * time.Second)
		t.Fatal("no timeout")
	}()

	if err := db.Query("kill").Exec(); err == nil {
		t.Fatal("expected error")
	}
}

// TestQueryRetry will test to make sure that gocql will execute
// the exact amount of retry queries designated by the user.
func TestQueryRetry(t *testing.T) {
	srv := NewTestServer(t)
	defer srv.Stop()

	db, err := NewCluster(srv.Address).CreateSession()
	if err != nil {
		t.Errorf("NewCluster: %v", err)
	}

	go func() {
		<-time.After(5 * time.Second)
		t.Fatal("no timeout")
	}()
	rt := RetryPolicy{NumRetries: 1}

	if err := db.Query("kill").RetryPolicy(rt).Exec(); err == nil {
		t.Fatal("expected error")
	}
	//Minus 1 from the nKillReq variable since there is the initial query attempt
	if srv.nKillReq-1 != uint64(rt.NumRetries) {
		t.Fatalf("failed to retry the query %v time(s). Query executed %v times", rt.NumRetries, srv.nKillReq-1)
	}
}

func TestSlowQuery(t *testing.T) {
	srv := NewTestServer(t)
	defer srv.Stop()

	db, err := NewCluster(srv.Address).CreateSession()
	if err != nil {
		t.Errorf("NewCluster: %v", err)
	}

	if err := db.Query("slow").Exec(); err != nil {
		t.Fatal(err)
	}
}

func TestRoundRobin(t *testing.T) {
	servers := make([]*TestServer, 5)
	addrs := make([]string, len(servers))
	for i := 0; i < len(servers); i++ {
		servers[i] = NewTestServer(t)
		addrs[i] = servers[i].Address
		defer servers[i].Stop()
	}
	cluster := NewCluster(addrs...)
	db, err := cluster.CreateSession()
	time.Sleep(1 * time.Second) //Sleep to allow the Cluster.fillPool to complete

	if err != nil {
		t.Errorf("NewCluster: %v", err)
	}

	var wg sync.WaitGroup
	wg.Add(5)
	for i := 0; i < 5; i++ {
		go func() {
			for j := 0; j < 5; j++ {
				if err := db.Query("void").Exec(); err != nil {
					t.Fatal(err)
				}
			}
			wg.Done()
		}()
	}
	wg.Wait()

	diff := 0
	for i := 1; i < len(servers); i++ {
		d := 0
		if servers[i].nreq > servers[i-1].nreq {
			d = int(servers[i].nreq - servers[i-1].nreq)
		} else {
			d = int(servers[i-1].nreq - servers[i].nreq)
		}
		if d > diff {
			diff = d
		}
	}

	if diff > 0 {
		t.Fatal("diff:", diff)
	}
}

func TestConnClosing(t *testing.T) {
	srv := NewTestServer(t)
	defer srv.Stop()

	db, err := NewCluster(srv.Address).CreateSession()
	if err != nil {
		t.Errorf("NewCluster: %v", err)
	}
	defer db.Close()

	numConns := db.cfg.NumConns
	count := db.cfg.NumStreams * numConns

	wg := &sync.WaitGroup{}
	wg.Add(count)
	for i := 0; i < count; i++ {
		go func(wg *sync.WaitGroup) {
			wg.Done()
			db.Query("kill").Exec()
		}(wg)
	}

	wg.Wait()

	time.Sleep(1 * time.Second) //Sleep so the fillPool can complete.
	pool := db.Pool.(ConnectionPool)
	conns := pool.Size()

	if conns != numConns {
		t.Fatalf("Expected to have %d connections but have %d", numConns, conns)
	}
}

func TestConnectHander(t *testing.T) {
	srv := NewTestServer(t)
	defer srv.Stop()

	called := false

	cluster_conf := NewCluster(srv.Address)
	cluster_conf.ConnectHandler = func(p ConnectionPool) {
		called = true
		if p.Size() <= 0 {
			t.Errorf("No new connection detected in pool: %v", p)
		}
	}

	_, err := cluster_conf.CreateSession()
	if err != nil {
		t.Errorf("NewCluster: %v", err)
	}

	if !called {
		t.Errorf("Connect handler not called")
	}
}

func TestDisconnectHander(t *testing.T) {
	srv := NewTestServer(t)
	defer srv.Stop()

	called := false

	cluster_conf := NewCluster(srv.Address)
	cluster_conf.DisconnectHandler = func(p ConnectionPool) {
		called = true
	}

	sess, err := cluster_conf.CreateSession()
	if err != nil {
		t.Errorf("NewCluster: %v", err)
	}

	sess.Close()

	if !called {
		t.Errorf("Disconnect handler not called")
	}
}

func NewTestServer(t *testing.T) *TestServer {
	laddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	listen, err := net.ListenTCP("tcp", laddr)
	if err != nil {
		t.Fatal(err)
	}
	srv := &TestServer{Address: listen.Addr().String(), listen: listen, t: t}
	go srv.serve()
	return srv
}

func (srv *TestServer) serve() {
	defer srv.listen.Close()
	for {
		conn, err := srv.listen.Accept()
		if err != nil {
			break
		}
		go func(conn net.Conn) {
			defer conn.Close()
			for {
				frame := srv.readFrame(conn)
				atomic.AddUint64(&srv.nreq, 1)
				srv.process(frame, conn)
			}
		}(conn)
	}
}

func (srv *TestServer) Stop() {
	srv.listen.Close()
}

func (srv *TestServer) process(frame frame, conn net.Conn) {
	switch frame[3] {
	case opStartup:
		frame = frame[:headerSize]
		frame.setHeader(protoResponse, 0, frame[2], opReady)
	case opQuery:
		input := frame
		input.skipHeader()
		query := strings.TrimSpace(input.readLongString())
		frame = frame[:headerSize]
		frame.setHeader(protoResponse, 0, frame[2], opResult)
		first := query
		if n := strings.Index(query, " "); n > 0 {
			first = first[:n]
		}
		switch strings.ToLower(first) {
		case "kill":
			atomic.AddUint64(&srv.nKillReq, 1)
			select {}
		case "slow":
			go func() {
				<-time.After(1 * time.Second)
				frame.writeInt(resultKindVoid)
				frame.setLength(len(frame) - headerSize)
				if _, err := conn.Write(frame); err != nil {
					return
				}
			}()
			return
		case "use":
			frame.writeInt(3)
			frame.writeString(strings.TrimSpace(query[3:]))
		case "void":
			frame.writeInt(resultKindVoid)
		default:
			frame.writeInt(resultKindVoid)
		}
	default:
		frame = frame[:headerSize]
		frame.setHeader(protoResponse, 0, frame[2], opError)
		frame.writeInt(0)
		frame.writeString("not supported")
	}
	frame.setLength(len(frame) - headerSize)
	if _, err := conn.Write(frame); err != nil {
		return
	}
}

func (srv *TestServer) readFrame(conn net.Conn) frame {
	frame := make(frame, headerSize, headerSize+512)
	if _, err := io.ReadFull(conn, frame); err != nil {
		srv.t.Fatal(err)
	}
	if n := frame.Length(); n > 0 {
		frame.grow(n)
		if _, err := io.ReadFull(conn, frame[headerSize:]); err != nil {
			srv.t.Fatal(err)
		}
	}
	return frame
}
<|endoftext|>


<commit_before>// Copyright 2015 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

// fetchValues converts literal constants (e.g. O_APPEND) or any other C expressions
// into their respective numeric values. It does so by builting and executing a C program
// that prints values of the provided expressions.
func fetchValues(arch string, vals []string, includes []string, defines map[string]string) []string {
	includeText := ""
	for _, inc := range includes {
		includeText += fmt.Sprintf("#include <%v>\n", inc)
	}
	definesText := ""
	for k, v := range defines {
		definesText += fmt.Sprintf("#ifndef %v\n#define %v %v\n#endif\n", k, k, v)
	}
	src := strings.Replace(fetchSrc, "[[INCLUDES]]", includeText, 1)
	src = strings.Replace(src, "[[DEFAULTS]]", definesText, 1)
	src = strings.Replace(src, "[[VALS]]", strings.Join(vals, ","), 1)
	bin, err := ioutil.TempFile("", "")
	if err != nil {
		failf("failed to create temp file: %v", err)
	}
	bin.Close()
	defer os.Remove(bin.Name())

	args := []string{"-x", "c", "-", "-o", bin.Name()}
	args = append(args, []string{
		// This would be useful to ensure that we don't include any host headers,
		// but kernel includes at least <stdarg.h>
		// "-nostdinc",
		"-I.",
		"-D__KERNEL__",
		"-DKBUILD_MODNAME=\"-\"",
		"-I" + *flagLinux + "/arch/" + arch + "/include",
		"-I" + *flagLinux + "/arch/" + arch + "/include/generated/uapi",
		"-I" + *flagLinux + "/arch/" + arch + "/include/generated",
		"-I" + *flagLinux + "/include",
		"-I" + *flagLinux + "/arch/" + arch + "/include/uapi",
		"-I" + *flagLinux + "/arch/" + arch + "/include/generated/uapi",
		"-I" + *flagLinux + "/include/uapi",
		"-I" + *flagLinux + "/include/generated/uapi",
		"-I" + *flagLinux,
		"-include", *flagLinux + "/include/linux/kconfig.h",
	}...)

	cmd := exec.Command("gcc", args...)
	cmd.Stdin = strings.NewReader(src)
	out, err := cmd.CombinedOutput()
	if err != nil {
		failf("failed to run gcc: %v\n%v", err, string(out))
	}

	out, err = exec.Command(bin.Name()).CombinedOutput()
	if err != nil {
		failf("failed to flags binary: %v\n%v", err, string(out))
	}

	flagVals := strings.Split(string(out), " ")
	if len(flagVals) != len(vals) {
		failf("fetched wrong number of values")
	}
	for _, v := range flagVals {
		_, err := strconv.ParseUint(v, 10, 64)
		if err != nil {
			failf("failed to parse value: %v (%v)", err, v)
		}
	}
	return flagVals
}

var fetchSrc = `
[[INCLUDES]]
[[DEFAULTS]]
int printf(const char *format, ...);
unsigned long phys_base;
int main() {
	int i;
	unsigned long vals[] = {[[VALS]]};
	for (i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) {
		if (i != 0)
			printf(" ");
		printf("%lu", vals[i]);
	}
	return 0;
}
`
<commit_msg>sysgen: fix build for CONFIG_DEBUG_VIRTUAL=y<commit_after>// Copyright 2015 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

// fetchValues converts literal constants (e.g. O_APPEND) or any other C expressions
// into their respective numeric values. It does so by builting and executing a C program
// that prints values of the provided expressions.
func fetchValues(arch string, vals []string, includes []string, defines map[string]string) []string {
	includeText := ""
	for _, inc := range includes {
		includeText += fmt.Sprintf("#include <%v>\n", inc)
	}
	definesText := ""
	for k, v := range defines {
		definesText += fmt.Sprintf("#ifndef %v\n#define %v %v\n#endif\n", k, k, v)
	}
	src := strings.Replace(fetchSrc, "[[INCLUDES]]", includeText, 1)
	src = strings.Replace(src, "[[DEFAULTS]]", definesText, 1)
	src = strings.Replace(src, "[[VALS]]", strings.Join(vals, ","), 1)
	bin, err := ioutil.TempFile("", "")
	if err != nil {
		failf("failed to create temp file: %v", err)
	}
	bin.Close()
	defer os.Remove(bin.Name())

	args := []string{"-x", "c", "-", "-o", bin.Name()}
	args = append(args, []string{
		// This would be useful to ensure that we don't include any host headers,
		// but kernel includes at least <stdarg.h>
		// "-nostdinc",
		"-I.",
		"-D__KERNEL__",
		"-DKBUILD_MODNAME=\"-\"",
		"-I" + *flagLinux + "/arch/" + arch + "/include",
		"-I" + *flagLinux + "/arch/" + arch + "/include/generated/uapi",
		"-I" + *flagLinux + "/arch/" + arch + "/include/generated",
		"-I" + *flagLinux + "/include",
		"-I" + *flagLinux + "/arch/" + arch + "/include/uapi",
		"-I" + *flagLinux + "/arch/" + arch + "/include/generated/uapi",
		"-I" + *flagLinux + "/include/uapi",
		"-I" + *flagLinux + "/include/generated/uapi",
		"-I" + *flagLinux,
		"-include", *flagLinux + "/include/linux/kconfig.h",
	}...)

	cmd := exec.Command("gcc", args...)
	cmd.Stdin = strings.NewReader(src)
	out, err := cmd.CombinedOutput()
	if err != nil {
		failf("failed to run gcc: %v\n%v", err, string(out))
	}

	out, err = exec.Command(bin.Name()).CombinedOutput()
	if err != nil {
		failf("failed to flags binary: %v\n%v", err, string(out))
	}

	flagVals := strings.Split(string(out), " ")
	if len(flagVals) != len(vals) {
		failf("fetched wrong number of values")
	}
	for _, v := range flagVals {
		_, err := strconv.ParseUint(v, 10, 64)
		if err != nil {
			failf("failed to parse value: %v (%v)", err, v)
		}
	}
	return flagVals
}

var fetchSrc = `
[[INCLUDES]]
[[DEFAULTS]]
int printf(const char *format, ...);
unsigned long phys_base;
#ifndef __phys_addr
unsigned long __phys_addr(unsigned long addr) { return 0; }
#endif
int main() {
	int i;
	unsigned long vals[] = {[[VALS]]};
	for (i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) {
		if (i != 0)
			printf(" ");
		printf("%lu", vals[i]);
	}
	return 0;
}
`
<|endoftext|>


<commit_before>package fastly

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"runtime"
	"strings"
	"time"

	"github.com/ajg/form"
	"github.com/hashicorp/cleanhttp"
	"github.com/mitchellh/mapstructure"
)

// APIKeyEnvVar is the name of the environment variable where the Fastly API
// key should be read from.
const APIKeyEnvVar = "FASTLY_API_KEY"

// APIKeyHeader is the name of the header that contains the Fastly API key.
const APIKeyHeader = "Fastly-Key"

// DefaultEndpoint is the default endpoint for Fastly. Since Fastly does not
// support an on-premise solution, this is likely to always be the default.
const DefaultEndpoint = "https://api.fastly.com"

// ProjectURL is the url for this library.
var ProjectURL = "github.com/sethvargo/fastly"

// ProjectVersion is the version of this library.
var ProjectVersion = "0.1"

// UserAgent is the user agent for this particular client.
var UserAgent = fmt.Sprintf("FastlyGo/%s (+%s; %s)",
	ProjectVersion, ProjectURL, runtime.Version())

// Client is the main entrypoint to the Fastly golang API library.
type Client struct {
	// Address is the address of Fastly's API endpoint.
	Address string

	// HTTPClient is the HTTP client to use. If one is not provided, a default
	// client will be used.
	HTTPClient *http.Client

	// apiKey is the Fastly API key to authenticate requests.
	apiKey string

	// url is the parsed URL from Address
	url *url.URL
}

// DefaultClient instantiates a new Fastly API client. This function requires
// the environment variable `FASTLY_API_KEY` is set and contains a valid API key
// to authenticate with Fastly.
func DefaultClient() *Client {
	client, err := NewClient(os.Getenv(APIKeyEnvVar))
	if err != nil {
		panic(err)
	}
	return client
}

// NewClient creates a new API client with the given key. Because Fastly allows
// some requests without an API key, this function will not error if the API
// token is not supplied. Attempts to make a request that requires an API key
// will return a 403 response.
func NewClient(key string) (*Client, error) {
	client := &Client{apiKey: key}
	return client.init()
}

func (c *Client) init() (*Client, error) {
	if len(c.Address) == 0 {
		c.Address = DefaultEndpoint
	}

	u, err := url.Parse(c.Address)
	if err != nil {
		return nil, err
	}
	c.url = u

	if c.HTTPClient == nil {
		c.HTTPClient = cleanhttp.DefaultClient()
	}

	return c, nil
}

// Get issues an HTTP GET request.
func (c *Client) Get(p string, ro *RequestOptions) (*http.Response, error) {
	return c.Request("GET", p, ro)
}

// Head issues an HTTP HEAD request.
func (c *Client) Head(p string, ro *RequestOptions) (*http.Response, error) {
	return c.Request("HEAD", p, ro)
}

// Post issues an HTTP POST request.
func (c *Client) Post(p string, ro *RequestOptions) (*http.Response, error) {
	return c.Request("POST", p, ro)
}

// PostForm issues an HTTP POST request with the given interface form-encoded.
func (c *Client) PostForm(p string, i interface{}, ro *RequestOptions) (*http.Response, error) {
	return c.RequestForm("POST", p, i, ro)
}

// Put issues an HTTP PUT request.
func (c *Client) Put(p string, ro *RequestOptions) (*http.Response, error) {
	return c.Request("PUT", p, ro)
}

// PutForm issues an HTTP PUT request with the given interface form-encoded.
func (c *Client) PutForm(p string, i interface{}, ro *RequestOptions) (*http.Response, error) {
	return c.RequestForm("PUT", p, i, ro)
}

// Delete issues an HTTP DELETE request.
func (c *Client) Delete(p string, ro *RequestOptions) (*http.Response, error) {
	return c.Request("DELETE", p, ro)
}

// Request makes an HTTP request against the HTTPClient using the given verb,
// Path, and request options.
func (c *Client) Request(verb, p string, ro *RequestOptions) (*http.Response, error) {
	req, err := c.RawRequest(verb, p, ro)
	if err != nil {
		return nil, err
	}

	resp, err := checkResp(c.HTTPClient.Do(req))
	if err != nil {
		return resp, err
	}

	return resp, nil
}

// RequestForm makes an HTTP request with the given interface being encoded as
// form data.
func (c *Client) RequestForm(verb, p string, i interface{}, ro *RequestOptions) (*http.Response, error) {
	values, err := form.EncodeToValues(i)
	if err != nil {
		return nil, err
	}

	if ro == nil {
		ro = new(RequestOptions)
	}

	if ro.Headers == nil {
		ro.Headers = make(map[string]string)
	}
	ro.Headers["Content-Type"] = "application/x-www-form-urlencoded"

	// There is a super-jank implementation in the form library where fields with
	// a "dot" are replaced with "/.". That is then URL encoded and Fastly just
	// dies. We fix that here.
	body := strings.Replace(values.Encode(), "%5C.", ".", -1)

	ro.Body = strings.NewReader(body)
	ro.BodyLength = int64(len(body))

	return c.Request(verb, p, ro)
}

// checkResp wraps an HTTP request from the default client and verifies that the
// request was successful. A non-200 request returns an error formatted to
// included any validation problems or otherwise.
func checkResp(resp *http.Response, err error) (*http.Response, error) {
	// If the err is already there, there was an error higher up the chain, so
	// just return that.
	if err != nil {
		return resp, err
	}

	switch resp.StatusCode {
	case 200, 201, 202, 204, 205, 206:
		return resp, nil
	default:
		return resp, NewHTTPError(resp)
	}
}

// decodeJSON is used to decode an HTTP response body into an interface as JSON.
func decodeJSON(out interface{}, body io.ReadCloser) error {
	defer body.Close()

	var parsed interface{}
	dec := json.NewDecoder(body)
	if err := dec.Decode(&parsed); err != nil {
		return err
	}

	decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
		DecodeHook: mapstructure.ComposeDecodeHookFunc(
			StringToTimeHookFunc(),
		),
		WeaklyTypedInput: true,
		Result:           out,
	})
	if err != nil {
		return err
	}
	return decoder.Decode(parsed)
}

// StringToTimeHookFunc returns a function that converts strings to a time.Time
// value.
func StringToTimeHookFunc() mapstructure.DecodeHookFunc {
	return func(
		f reflect.Type,
		t reflect.Type,
		data interface{}) (interface{}, error) {
		if f.Kind() != reflect.String {
			return data, nil
		}
		if t != reflect.TypeOf(time.Now()) {
			return data, nil
		}

		// Convert it by parsing
		return time.Parse(time.RFC3339, data.(string))
	}
}
<commit_msg>Make decode function private<commit_after>package fastly

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"runtime"
	"strings"
	"time"

	"github.com/ajg/form"
	"github.com/hashicorp/cleanhttp"
	"github.com/mitchellh/mapstructure"
)

// APIKeyEnvVar is the name of the environment variable where the Fastly API
// key should be read from.
const APIKeyEnvVar = "FASTLY_API_KEY"

// APIKeyHeader is the name of the header that contains the Fastly API key.
const APIKeyHeader = "Fastly-Key"

// DefaultEndpoint is the default endpoint for Fastly. Since Fastly does not
// support an on-premise solution, this is likely to always be the default.
const DefaultEndpoint = "https://api.fastly.com"

// ProjectURL is the url for this library.
var ProjectURL = "github.com/sethvargo/fastly"

// ProjectVersion is the version of this library.
var ProjectVersion = "0.1"

// UserAgent is the user agent for this particular client.
var UserAgent = fmt.Sprintf("FastlyGo/%s (+%s; %s)",
	ProjectVersion, ProjectURL, runtime.Version())

// Client is the main entrypoint to the Fastly golang API library.
type Client struct {
	// Address is the address of Fastly's API endpoint.
	Address string

	// HTTPClient is the HTTP client to use. If one is not provided, a default
	// client will be used.
	HTTPClient *http.Client

	// apiKey is the Fastly API key to authenticate requests.
	apiKey string

	// url is the parsed URL from Address
	url *url.URL
}

// DefaultClient instantiates a new Fastly API client. This function requires
// the environment variable `FASTLY_API_KEY` is set and contains a valid API key
// to authenticate with Fastly.
func DefaultClient() *Client {
	client, err := NewClient(os.Getenv(APIKeyEnvVar))
	if err != nil {
		panic(err)
	}
	return client
}

// NewClient creates a new API client with the given key. Because Fastly allows
// some requests without an API key, this function will not error if the API
// token is not supplied. Attempts to make a request that requires an API key
// will return a 403 response.
func NewClient(key string) (*Client, error) {
	client := &Client{apiKey: key}
	return client.init()
}

func (c *Client) init() (*Client, error) {
	if len(c.Address) == 0 {
		c.Address = DefaultEndpoint
	}

	u, err := url.Parse(c.Address)
	if err != nil {
		return nil, err
	}
	c.url = u

	if c.HTTPClient == nil {
		c.HTTPClient = cleanhttp.DefaultClient()
	}

	return c, nil
}

// Get issues an HTTP GET request.
func (c *Client) Get(p string, ro *RequestOptions) (*http.Response, error) {
	return c.Request("GET", p, ro)
}

// Head issues an HTTP HEAD request.
func (c *Client) Head(p string, ro *RequestOptions) (*http.Response, error) {
	return c.Request("HEAD", p, ro)
}

// Post issues an HTTP POST request.
func (c *Client) Post(p string, ro *RequestOptions) (*http.Response, error) {
	return c.Request("POST", p, ro)
}

// PostForm issues an HTTP POST request with the given interface form-encoded.
func (c *Client) PostForm(p string, i interface{}, ro *RequestOptions) (*http.Response, error) {
	return c.RequestForm("POST", p, i, ro)
}

// Put issues an HTTP PUT request.
func (c *Client) Put(p string, ro *RequestOptions) (*http.Response, error) {
	return c.Request("PUT", p, ro)
}

// PutForm issues an HTTP PUT request with the given interface form-encoded.
func (c *Client) PutForm(p string, i interface{}, ro *RequestOptions) (*http.Response, error) {
	return c.RequestForm("PUT", p, i, ro)
}

// Delete issues an HTTP DELETE request.
func (c *Client) Delete(p string, ro *RequestOptions) (*http.Response, error) {
	return c.Request("DELETE", p, ro)
}

// Request makes an HTTP request against the HTTPClient using the given verb,
// Path, and request options.
func (c *Client) Request(verb, p string, ro *RequestOptions) (*http.Response, error) {
	req, err := c.RawRequest(verb, p, ro)
	if err != nil {
		return nil, err
	}

	resp, err := checkResp(c.HTTPClient.Do(req))
	if err != nil {
		return resp, err
	}

	return resp, nil
}

// RequestForm makes an HTTP request with the given interface being encoded as
// form data.
func (c *Client) RequestForm(verb, p string, i interface{}, ro *RequestOptions) (*http.Response, error) {
	values, err := form.EncodeToValues(i)
	if err != nil {
		return nil, err
	}

	if ro == nil {
		ro = new(RequestOptions)
	}

	if ro.Headers == nil {
		ro.Headers = make(map[string]string)
	}
	ro.Headers["Content-Type"] = "application/x-www-form-urlencoded"

	// There is a super-jank implementation in the form library where fields with
	// a "dot" are replaced with "/.". That is then URL encoded and Fastly just
	// dies. We fix that here.
	body := strings.Replace(values.Encode(), "%5C.", ".", -1)

	ro.Body = strings.NewReader(body)
	ro.BodyLength = int64(len(body))

	return c.Request(verb, p, ro)
}

// checkResp wraps an HTTP request from the default client and verifies that the
// request was successful. A non-200 request returns an error formatted to
// included any validation problems or otherwise.
func checkResp(resp *http.Response, err error) (*http.Response, error) {
	// If the err is already there, there was an error higher up the chain, so
	// just return that.
	if err != nil {
		return resp, err
	}

	switch resp.StatusCode {
	case 200, 201, 202, 204, 205, 206:
		return resp, nil
	default:
		return resp, NewHTTPError(resp)
	}
}

// decodeJSON is used to decode an HTTP response body into an interface as JSON.
func decodeJSON(out interface{}, body io.ReadCloser) error {
	defer body.Close()

	var parsed interface{}
	dec := json.NewDecoder(body)
	if err := dec.Decode(&parsed); err != nil {
		return err
	}

	decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
		DecodeHook: mapstructure.ComposeDecodeHookFunc(
			stringToTimeHookFunc(),
		),
		WeaklyTypedInput: true,
		Result:           out,
	})
	if err != nil {
		return err
	}
	return decoder.Decode(parsed)
}

// stringToTimeHookFunc returns a function that converts strings to a time.Time
// value.
func stringToTimeHookFunc() mapstructure.DecodeHookFunc {
	return func(
		f reflect.Type,
		t reflect.Type,
		data interface{}) (interface{}, error) {
		if f.Kind() != reflect.String {
			return data, nil
		}
		if t != reflect.TypeOf(time.Now()) {
			return data, nil
		}

		// Convert it by parsing
		return time.Parse(time.RFC3339, data.(string))
	}
}
<|endoftext|>


<commit_before>package fbtron

import (
  "encoding/csv"
  "fmt"
  "io"
  "math/rand"
  "os"
  "strconv"
  "strings"
)

type Player struct {
  ID              string
  Firstname       string
  Lastname        string
  Positions       []string
  stats           map[string]float64
  Num_seasons     int
  Total_wins      int
}

func (p *Player) SetStat(name string, value float64) {
  if p.stats == nil {
    p.stats = make(map[string]float64, len(stat_types))
  }
  p.stats[name] = value
}

func (p *Player) GetStat(name string) float64 {
  return p.stats[name]
}

func (p *Player) GetName() string {
  if p.Lastname == "" {
    return p.Firstname
  }
  return fmt.Sprintf("%s %s", p.Firstname, p.Lastname)
}

func (p *Player) WinsPerDraft() float64 {
  if p.Total_wins == 0 {
    return 0.0
  }
  return float64(p.Total_wins) / float64(p.Num_seasons)
}

func (p *Player) ResetWins() {
  p.Total_wins = 0
  p.Num_seasons = 0
}

// BuildPlayersFromCsv reads a CSV file and returns an array of player objects,
// one for each row. Assumes the first row is labels. Every column whose label
// is defined in StatType has its value set.
func BuildPlayersFromCsv(filename string, default_position string) []*Player {
  file, err := os.Open(filename)
  if err != nil {
    fmt.Println("Error:", err)
    return []*Player {}
  }
  defer file.Close()
  reader := csv.NewReader(file)

  var header []string
  var players []*Player
  for {
    record, err := reader.Read()
    if err == io.EOF {
      break
    } else if err != nil {
      fmt.Printf("Error reading %s: %s\n", filename, err)
      continue
    }

    if header == nil {
      header = record
    } else {
      newplayer := BuildPlayerFromCsvRecord(header, record, default_position)
      if newplayer != nil {
        if len(players) == cap(players) {
          newplayers := make([]*Player, len(players), (len(players)+1)*2)
          copy(newplayers, players)
          players = newplayers
        }
        players = players[:len(players)+1]
        players[len(players)-1] = newplayer
      }
    }
  }

  // Assume the caller will close the file handle
  return players
}

func BuildPlayerFromCsvRecord(
    header []string, record []string, default_position string) *Player {
  columns := len(header)
  if len(record) < columns {
    columns = len(record)
  }
  if columns == 0 {
    return nil
  }

  p := new(Player)
  for n := 0; n < columns; n ++ {
    val, err := strconv.ParseFloat(record[n], 64)

    switch header[n] {
    case "steamerid":
      p.ID = record[n]
    case "firstname":
      p.Firstname = record[n]
    case "lastname":
      p.Lastname = record[n]
    case "position":
      p.Positions = []string { strings.ToUpper(record[n]) }
    case "start_percent":
      // TODO: I don't like having to do this, but since the 2013 steamer files
      // don't have eligible positions, we've gotta fake it. The overlap is so
      // that some players qualify as both SP and RP.
      p.Positions = []string { }
      if val >= 0.25 {
        p.Positions = append(p.Positions, "SP")
      }
      if val <= 0.75 {
        p.Positions = append(p.Positions, "RP")
      }
    case "IP":
      if err == nil {
        p.SetStat("P_IP", val)
      }
    case "AB":
      if err == nil {
        p.SetStat("B_AB", val)
      }
    default:
      stat_name, stat_type := GetStatNameAndType(default_position, header[n])
      if stat_type != -1 {
        if err == nil {
          p.SetStat(stat_name, val)
        }
      }
    }
  }

  // Add default position if it's non-blank and isn't already a position
  if default_position != "" {
    for n := range p.Positions {
      if p.Positions[n] == default_position {
        return p
      }
    }
    p.Positions = append(p.Positions, default_position)
  }

  // If the player doesn't have an id, generate one randomly (there is no
  // guarantee these won't collide, but chances are 1/2^64).
  if p.ID == "" {
    p.ID = fmt.Sprintf("%x", rand.Int63())
  }

  return p
}
<commit_msg>Collapse outfielder positions<commit_after>package fbtron

import (
  "encoding/csv"
  "fmt"
  "io"
  "math/rand"
  "os"
  "strconv"
  "strings"
)

type Player struct {
  ID              string
  Firstname       string
  Lastname        string
  Positions       []string
  stats           map[string]float64
  Num_seasons     int
  Total_wins      int
}

func (p *Player) SetStat(name string, value float64) {
  if p.stats == nil {
    p.stats = make(map[string]float64, len(stat_types))
  }
  p.stats[name] = value
}

func (p *Player) GetStat(name string) float64 {
  return p.stats[name]
}

func (p *Player) GetName() string {
  if p.Lastname == "" {
    return p.Firstname
  }
  return fmt.Sprintf("%s %s", p.Firstname, p.Lastname)
}

func (p *Player) WinsPerDraft() float64 {
  if p.Total_wins == 0 {
    return 0.0
  }
  return float64(p.Total_wins) / float64(p.Num_seasons)
}

func (p *Player) ResetWins() {
  p.Total_wins = 0
  p.Num_seasons = 0
}

// BuildPlayersFromCsv reads a CSV file and returns an array of player objects,
// one for each row. Assumes the first row is labels. Every column whose label
// is defined in StatType has its value set.
func BuildPlayersFromCsv(filename string, default_position string) []*Player {
  file, err := os.Open(filename)
  if err != nil {
    fmt.Println("Error:", err)
    return []*Player {}
  }
  defer file.Close()
  reader := csv.NewReader(file)

  var header []string
  var players []*Player
  for {
    record, err := reader.Read()
    if err == io.EOF {
      break
    } else if err != nil {
      fmt.Printf("Error reading %s: %s\n", filename, err)
      continue
    }

    if header == nil {
      header = record
    } else {
      newplayer := BuildPlayerFromCsvRecord(header, record, default_position)
      if newplayer != nil {
        if len(players) == cap(players) {
          newplayers := make([]*Player, len(players), (len(players)+1)*2)
          copy(newplayers, players)
          players = newplayers
        }
        players = players[:len(players)+1]
        players[len(players)-1] = newplayer
      }
    }
  }

  // Assume the caller will close the file handle
  return players
}

func BuildPlayerFromCsvRecord(
    header []string, record []string, default_position string) *Player {
  columns := len(header)
  if len(record) < columns {
    columns = len(record)
  }
  if columns == 0 {
    return nil
  }

  p := new(Player)
  for n := 0; n < columns; n ++ {
    val, err := strconv.ParseFloat(record[n], 64)

    switch header[n] {
    case "steamerid":
      p.ID = record[n]
    case "firstname":
      p.Firstname = record[n]
    case "lastname":
      p.Lastname = record[n]
    case "position":
      position := strings.ToUpper(record[n])
      if position == "LF" || position == "CF" || position == "RF" {
        position = "OF"
      }
      p.Positions = []string { position }
    case "start_percent":
      // TODO: I don't like having to do this, but since the 2013 steamer files
      // don't have eligible positions, we've gotta fake it. The overlap is so
      // that some players qualify as both SP and RP.
      p.Positions = []string { }
      if val >= 0.25 {
        p.Positions = append(p.Positions, "SP")
      }
      if val <= 0.75 {
        p.Positions = append(p.Positions, "RP")
      }
    case "IP":
      if err == nil {
        p.SetStat("P_IP", val)
      }
    case "AB":
      if err == nil {
        p.SetStat("B_AB", val)
      }
    default:
      stat_name, stat_type := GetStatNameAndType(default_position, header[n])
      if stat_type != -1 {
        if err == nil {
          p.SetStat(stat_name, val)
        }
      }
    }
  }

  // Add default position if it's non-blank and isn't already a position
  if default_position != "" {
    for n := range p.Positions {
      if p.Positions[n] == default_position {
        return p
      }
    }
    p.Positions = append(p.Positions, default_position)
  }

  // If the player doesn't have an id, generate one randomly (there is no
  // guarantee these won't collide, but chances are 1/2^64).
  if p.ID == "" {
    p.ID = fmt.Sprintf("%x", rand.Int63())
  }

  return p
}
<|endoftext|>


<commit_before>package ping

import (
	"bytes"
	"time"

	"github.com/hsheth2/gonet/icmp"

	"github.com/hsheth2/gonet/ipv4"

	"github.com/hsheth2/logs"
	"sync/atomic"
)

const DATA_56_BYTES = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd"

func (pm *Ping_Manager) ping_response_dealer() {
	for {
		ping := <-pm.reply
		identNum := uint16(ping.Header.Opt >> 16)
		if _, ok := pm.identifiers[identNum]; !ok {
			/*logs*/ logs.Info.Println("Dropped something from response dealer, identnum=", identNum, "options=", ping.Header.Opt)
			continue
		}
		pm.identifiers[identNum] <- ping
	}
}

func sendSinglePing(writer ipv4.Writer, id uint32, seq uint16, timeout time.Duration, reply chan *icmp.Packet) {
	// prepare packet
	packet := &icmp.Header{
		Tp:   icmp.EchoRequest,
		Code: PING_ICMP_CODE,
		Opt:  id<<16 | uint32(seq),
		Data: []byte(DATA_56_BYTES), // TODO make legit by putting the timestamp in the data
	}

	// make data
	icmp.SendPacket(writer, packet)

	time1 := time.Now()
	timeoutTimer := time.NewTimer(timeout)
	go func(seqChan chan *icmp.Packet, header *icmp.Header, time1 *time.Time, timer *time.Timer) {
		defer close(seqChan)
		for {
			select {
			case pingResponse := <-seqChan:
				if !bytes.Equal(pingResponse.Header.Data, header.Data) {
					/*logs*/ logs.Info.Println("Dropped packet because header data not equal to ping sent")
					continue
				}
				time2 := time.Now()
				logs.Info.Printf("%d bytes from %v: icmp_seq=%d time=%f ms",
					len(header.Data)+icmp.HeaderMinSize,
					pingResponse.RIP.IP,
					uint16(header.Opt),
					float32(time2.Sub(*time1).Nanoseconds())/1000000) // put ttl
				return
			case <-timer.C:
				logs.Info.Println("Seq num of", uint16(header.Opt), "timed out")
				return
			}
		}
	}(reply, packet, &time1, timeoutTimer)
}

func (pm *Ping_Manager) initIdentifier(terminate chan bool, numPings uint16, pingCount *uint32) (uint16, [](chan *icmp.Packet), error) {
	// get identifier
	pm.currentIdentifier++
	id := pm.currentIdentifier

	// setup sequence number dealer
	pm.identifiers[id] = make(chan *icmp.Packet)
	seqChannel := make([](chan *icmp.Packet), numPings)

	// create go routine function to deal packets
	go sequenceDealer(pm.identifiers[id], seqChannel, terminate, pingCount)

	return id, seqChannel, nil
}

func sequenceDealer(idInput chan *icmp.Packet, seqChan [](chan *icmp.Packet), terminate chan bool, pingCount *uint32) {
	// TODO verify IPs
	for {
		select {
		case <-terminate:
			//			/*logs*/logs.Info.Println("Terminating seq dealer")
			return
		case packet := <-idInput:
			// /*logs*/logs.Info.Println("icmp in =", packet.Header.Opt)
			seqNum := uint16(packet.Header.Opt)
			if seqNum < pingCount - 1 {
				seqChan[seqNum] <- packet
			} else {
				/*logs*/ logs.Info.Println("Dropping bad seq num packet with existing identifier")
			}
		}
	}
}

const FLOOD_INTERVAL = 0 // TODO add a flood option

func (pm *Ping_Manager) SendPing(ip *ipv4.Address, interval, timeout time.Duration, numPings uint16) error {
	terminate := make(chan bool)
	pingCount := uint32(1)
	id, seqChannel, err := pm.initIdentifier(terminate, numPings, &pingCount)
	if err != nil {
		logs.Error.Println(err)
		return err
	}
	defer func() { terminate <- true }()

	// get ip writer
	writer, err := pm.getIP_Writer(ip)
	if err != nil {
		logs.Error.Println(err)
		return err
	}

	for ;pingCount <= numPings; atomic.AddInt32(&pingCount, 1) {
		seqChannel[pingCount] = make(chan *icmp.Packet)

		sendSinglePing(writer, id, pingCount, timeout, seqChannel[pingCount]) // function is non-blocking

		// not last
		if pingCount != numPings {
			time.Sleep(interval)
		}
	}

	time.Sleep(timeout)
	return nil
}
<commit_msg>Fixed built errors<commit_after>package ping

import (
	"bytes"
	"time"

	"github.com/hsheth2/gonet/icmp"

	"github.com/hsheth2/gonet/ipv4"

	"github.com/hsheth2/logs"
	"sync/atomic"
)

const DATA_56_BYTES = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd"

func (pm *Ping_Manager) ping_response_dealer() {
	for {
		ping := <-pm.reply
		identNum := uint16(ping.Header.Opt >> 16)
		if _, ok := pm.identifiers[identNum]; !ok {
			/*logs*/ logs.Info.Println("Dropped something from response dealer, identnum=", identNum, "options=", ping.Header.Opt)
			continue
		}
		pm.identifiers[identNum] <- ping
	}
}

func sendSinglePing(writer ipv4.Writer, id uint16, seq uint16, timeout time.Duration, reply chan *icmp.Packet) {
	// prepare packet
	packet := &icmp.Header{
		Tp:   icmp.EchoRequest,
		Code: PING_ICMP_CODE,
		Opt:  uint32(id)<<16 | uint32(seq),
		Data: []byte(DATA_56_BYTES), // TODO make legit by putting the timestamp in the data
	}

	// make data
	icmp.SendPacket(writer, packet)

	time1 := time.Now()
	timeoutTimer := time.NewTimer(timeout)
	go func(seqChan chan *icmp.Packet, header *icmp.Header, time1 *time.Time, timer *time.Timer) {
		defer close(seqChan)
		for {
			select {
			case pingResponse := <-seqChan:
				if !bytes.Equal(pingResponse.Header.Data, header.Data) {
					/*logs*/ logs.Info.Println("Dropped packet because header data not equal to ping sent")
					continue
				}
				time2 := time.Now()
				logs.Info.Printf("%d bytes from %v: icmp_seq=%d time=%f ms",
					len(header.Data)+icmp.HeaderMinSize,
					pingResponse.RIP.IP,
					uint16(header.Opt),
					float32(time2.Sub(*time1).Nanoseconds())/1000000) // put ttl
				return
			case <-timer.C:
				logs.Info.Println("Seq num of", uint16(header.Opt), "timed out")
				return
			}
		}
	}(reply, packet, &time1, timeoutTimer)
}

func (pm *Ping_Manager) initIdentifier(terminate chan bool, numPings uint16, pingCount *uint32) (uint16, [](chan *icmp.Packet), error) {
	// get identifier
	pm.currentIdentifier++
	id := pm.currentIdentifier

	// setup sequence number dealer
	pm.identifiers[id] = make(chan *icmp.Packet)
	seqChannel := make([](chan *icmp.Packet), numPings)

	// create go routine function to deal packets
	go sequenceDealer(pm.identifiers[id], seqChannel, terminate, pingCount)

	return id, seqChannel, nil
}

func sequenceDealer(idInput chan *icmp.Packet, seqChan [](chan *icmp.Packet), terminate chan bool, pingCount *uint32) {
	// TODO verify IPs
	for {
		select {
		case <-terminate:
			//			/*logs*/logs.Info.Println("Terminating seq dealer")
			return
		case packet := <-idInput:
			// /*logs*/logs.Info.Println("icmp in =", packet.Header.Opt)
			seqNum := uint32(packet.Header.Opt)
			if seqNum < atomic.LoadUint32(pingCount) - uint32(1) {
				seqChan[seqNum] <- packet
			} else {
				/*logs*/ logs.Info.Println("Dropping bad seq num packet with existing identifier")
			}
		}
	}
}

const FLOOD_INTERVAL = 0 // TODO add a flood option

func (pm *Ping_Manager) SendPing(ip *ipv4.Address, interval, timeout time.Duration, numPings uint16) error {
	terminate := make(chan bool)
	pingCount := uint32(1)
	id, seqChannel, err := pm.initIdentifier(terminate, numPings, &pingCount)
	if err != nil {
		logs.Error.Println(err)
		return err
	}
	defer func() { terminate <- true }()

	// get ip writer
	writer, err := pm.getIP_Writer(ip)
	if err != nil {
		logs.Error.Println(err)
		return err
	}

	for ;pingCount <= uint32(numPings); atomic.AddUint32(&pingCount, 1) {
		seqChannel[pingCount] = make(chan *icmp.Packet)

		sendSinglePing(writer, id, uint16(pingCount), timeout, seqChannel[pingCount]) // function is non-blocking

		// not last
		if uint16(pingCount) != numPings {
			time.Sleep(interval)
		}
	}

	time.Sleep(timeout)
	return nil
}
<|endoftext|>


<commit_before>package terraform

import (
	"fmt"

	"github.com/hashicorp/go-version"
)

// The main version number that is being run at the moment.
const Version = "0.10.7"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
var VersionPrerelease = "dev"

// SemVersion is an instance of version.Version. This has the secondary
// benefit of verifying during tests and init time that our version is a
// proper semantic version, which should always be the case.
var SemVersion = version.Must(version.NewVersion(Version))

// VersionHeader is the header name used to send the current terraform version
// in http requests.
const VersionHeader = "Terraform-Version"

func VersionString() string {
	if VersionPrerelease != "" {
		return fmt.Sprintf("%s-%s", Version, VersionPrerelease)
	}
	return Version
}
<commit_msg>update version<commit_after>package terraform

import (
	"fmt"

	"github.com/hashicorp/go-version"
)

// The main version number that is being run at the moment.
const Version = "0.10.8"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
var VersionPrerelease = "dev"

// SemVersion is an instance of version.Version. This has the secondary
// benefit of verifying during tests and init time that our version is a
// proper semantic version, which should always be the case.
var SemVersion = version.Must(version.NewVersion(Version))

// VersionHeader is the header name used to send the current terraform version
// in http requests.
const VersionHeader = "Terraform-Version"

func VersionString() string {
	if VersionPrerelease != "" {
		return fmt.Sprintf("%s-%s", Version, VersionPrerelease)
	}
	return Version
}
<|endoftext|>


<commit_before>package inputoutput

import (
	"log"

	"github.com/djhworld/gomeboycolor/components"
	"github.com/djhworld/gomeboycolor/constants"
	"github.com/djhworld/gomeboycolor/types"

	"github.com/go-gl/gl/v2.1/gl"
	"github.com/go-gl/glfw/v3.2/glfw"
)

const PREFIX string = "IO"
const ROW_1 byte = 0x10
const ROW_2 byte = 0x20
const SCREEN_WIDTH int = 160
const SCREEN_HEIGHT int = 144

var DefaultControlScheme ControlScheme = ControlScheme{
	glfw.KeyUp,
	glfw.KeyDown,
	glfw.KeyLeft,
	glfw.KeyRight,
	glfw.KeyZ,
	glfw.KeyX,
	glfw.KeyA,
	glfw.KeyS,
}

type ControlScheme struct {
	UP     glfw.Key
	DOWN   glfw.Key
	LEFT   glfw.Key
	RIGHT  glfw.Key
	A      glfw.Key
	B      glfw.Key
	START  glfw.Key
	SELECT glfw.Key
}

type KeyHandler struct {
	controlScheme ControlScheme
	colSelect     byte
	rows          [2]byte
	irqHandler    components.IRQHandler
}

func (k *KeyHandler) Init(cs ControlScheme) {
	k.controlScheme = cs
	k.Reset()
}

func (k *KeyHandler) Name() string {
	return PREFIX + "-KEYB"
}

func (k *KeyHandler) Reset() {
	log.Printf("%s: Resetting", k.Name())
	k.rows[0], k.rows[1] = 0x0F, 0x0F
	k.colSelect = 0x00
}

func (k *KeyHandler) LinkIRQHandler(m components.IRQHandler) {
	k.irqHandler = m
	log.Printf("%s: Linked IRQ Handler to Keyboard Handler", k.Name())
}

func (k *KeyHandler) Read(addr types.Word) byte {
	var value byte

	switch k.colSelect {
	case ROW_1:
		value = k.rows[1]
	case ROW_2:
		value = k.rows[0]
	default:
		value = 0x00
	}

	return value
}

func (k *KeyHandler) Write(addr types.Word, value byte) {
	k.colSelect = value & 0x30
}

//released sets bit for key to 0
func (k *KeyHandler) KeyDown(key glfw.Key) {
	k.irqHandler.RequestInterrupt(constants.JOYP_HILO_IRQ)
	switch key {
	case k.controlScheme.UP:
		k.rows[0] &= 0xB
	case k.controlScheme.DOWN:
		k.rows[0] &= 0x7
	case k.controlScheme.LEFT:
		k.rows[0] &= 0xD
	case k.controlScheme.RIGHT:
		k.rows[0] &= 0xE
	case k.controlScheme.A:
		k.rows[1] &= 0xE
	case k.controlScheme.B:
		k.rows[1] &= 0xD
	case k.controlScheme.START:
		k.rows[1] &= 0x7
	case k.controlScheme.SELECT:
		k.rows[1] &= 0xB
	}
}

//released sets bit for key to 1
func (k *KeyHandler) KeyUp(key glfw.Key) {
	switch key {
	case k.controlScheme.UP:
		k.rows[0] |= 0x4
	case k.controlScheme.DOWN:
		k.rows[0] |= 0x8
	case k.controlScheme.LEFT:
		k.rows[0] |= 0x2
	case k.controlScheme.RIGHT:
		k.rows[0] |= 0x1
	case k.controlScheme.A:
		k.rows[1] |= 0x1
	case k.controlScheme.B:
		k.rows[1] |= 0x2
	case k.controlScheme.START:
		k.rows[1] |= 0x8
	case k.controlScheme.SELECT:
		k.rows[1] |= 0x4
	}
}

type IO struct {
	KeyHandler          *KeyHandler
	Display             *Display
	ScreenOutputChannel chan *types.Screen
	AudioOutputChannel  chan int
	headless            bool
}

func NewIO() *IO {
	var i *IO = new(IO)
	i.KeyHandler = new(KeyHandler)
	i.Display = new(Display)
	i.ScreenOutputChannel = make(chan *types.Screen)
	i.AudioOutputChannel = make(chan int)
	i.headless = false
	return i
}

func (i *IO) Init(title string, screenSize int, headless bool, onCloseHandler func()) error {
	i.headless = headless
	var err error = nil

	if !i.headless {
		err = i.Display.init(title, screenSize, onCloseHandler)
		if err != nil {
			return err
		}

		i.KeyHandler.Init(DefaultControlScheme) //TODO: allow user to define controlscheme
		i.Display.window.SetKeyCallback(func(w *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
			if action == glfw.Repeat {
				i.KeyHandler.KeyDown(key)
				return
			}

			if action == glfw.Press {
				i.KeyHandler.KeyDown(key)
			} else {
				i.KeyHandler.KeyUp(key)
			}
		})
	}

	return err
}

//This will wait for updates to the display or audio and dispatch them accordingly
func (i *IO) Run() {
	for {
		select {
		case data := <-i.ScreenOutputChannel:
			if !i.headless {
				i.Display.drawFrame(data)
			}
		case data := <-i.AudioOutputChannel:
			if !i.headless {
				log.Println("Writing %d to audio!", data)
			}
		}
	}
}

type Display struct {
	Name                 string
	ScreenSizeMultiplier int
	window               *glfw.Window
}

func (s *Display) init(title string, screenSizeMultiplier int, onCloseHandler func()) error {
	var err error

	if err := glfw.Init(); err != nil {
		log.Fatalln("failed to initialize glfw:", err)
	}

	s.Name = PREFIX + "-SCREEN"

	log.Printf("%s: Initialising display", s.Name)

	s.ScreenSizeMultiplier = screenSizeMultiplier
	log.Printf("%s: Set screen size multiplier to %dx", s.Name, s.ScreenSizeMultiplier)

	glfw.WindowHint(glfw.Resizable, glfw.False)
	window, err := glfw.CreateWindow(SCREEN_WIDTH*s.ScreenSizeMultiplier, SCREEN_HEIGHT*s.ScreenSizeMultiplier, "Testing", nil, nil)
	if err != nil {
		return err
	}

	window.SetTitle(title)

	//TODO fix desktop mode
	window.SetPos((s.ScreenSizeMultiplier)/2, (s.ScreenSizeMultiplier)/2)

	window.SetCloseCallback(func(w *glfw.Window) {
		w.Destroy()
		glfw.Terminate()
		onCloseHandler()
	})

	window.MakeContextCurrent()

	if err := gl.Init(); err != nil {
		return err
	}

	gl.ClearColor(0.255, 0.255, 0.255, 0)

	//resize functionTrue
	/*
		onResize := func(window *glfw.Window, w, h int) {
			gl.Viewport(0, 0, int32(w), int32(h))
			gl.MatrixMode(gl.PROJECTION)
			gl.LoadIdentity()
			gl.Ortho(0, float64(w), float64(h), 0, -1, 1)
			gl.ClearColor(0.255, 0.255, 0.255, 0)
			gl.Clear(gl.COLOR_BUFFER_BIT)
			gl.MatrixMode(gl.MODELVIEW)
			gl.LoadIdentity()
		}

		window.SetSizeCallback(onResize)
	*/

	s.window = window

	return nil

}

func (s *Display) drawFrame(screenData *types.Screen) {
	gl.Viewport(0, 0, int32(SCREEN_WIDTH*s.ScreenSizeMultiplier)*2, int32(SCREEN_HEIGHT*s.ScreenSizeMultiplier)*2)
	gl.MatrixMode(gl.PROJECTION)
	gl.LoadIdentity()
	gl.Ortho(0, float64(SCREEN_WIDTH*s.ScreenSizeMultiplier), float64(SCREEN_HEIGHT*s.ScreenSizeMultiplier), 0, -1, 1)
	gl.ClearColor(0.255, 0.255, 0.255, 0)
	gl.Clear(gl.COLOR_BUFFER_BIT)
	gl.MatrixMode(gl.MODELVIEW)
	gl.LoadIdentity()

	gl.Clear(gl.COLOR_BUFFER_BIT)
	gl.Disable(gl.DEPTH_TEST)
	gl.PointSize(float32(s.ScreenSizeMultiplier) + 1.0)
	gl.Begin(gl.POINTS)
	for y := 0; y < SCREEN_HEIGHT; y++ {
		for x := 0; x < SCREEN_WIDTH; x++ {
			var pixel types.RGB = screenData[y][x]
			gl.Color3ub(pixel.Red, pixel.Green, pixel.Blue)
			gl.Vertex2i(int32(x*s.ScreenSizeMultiplier), int32(y*s.ScreenSizeMultiplier))
		}
	}

	gl.End()
	glfw.PollEvents()
	s.window.SwapBuffers()
}
<commit_msg>Fixed pixel scaling issue<commit_after>package inputoutput

import (
	"log"

	"github.com/djhworld/gomeboycolor/components"
	"github.com/djhworld/gomeboycolor/constants"
	"github.com/djhworld/gomeboycolor/types"

	"github.com/go-gl/gl/v2.1/gl"
	"github.com/go-gl/glfw/v3.2/glfw"
)

const PREFIX string = "IO"
const ROW_1 byte = 0x10
const ROW_2 byte = 0x20
const SCREEN_WIDTH int = 160
const SCREEN_HEIGHT int = 144

var DefaultControlScheme ControlScheme = ControlScheme{
	glfw.KeyUp,
	glfw.KeyDown,
	glfw.KeyLeft,
	glfw.KeyRight,
	glfw.KeyZ,
	glfw.KeyX,
	glfw.KeyA,
	glfw.KeyS,
}

type ControlScheme struct {
	UP     glfw.Key
	DOWN   glfw.Key
	LEFT   glfw.Key
	RIGHT  glfw.Key
	A      glfw.Key
	B      glfw.Key
	START  glfw.Key
	SELECT glfw.Key
}

type KeyHandler struct {
	controlScheme ControlScheme
	colSelect     byte
	rows          [2]byte
	irqHandler    components.IRQHandler
}

func (k *KeyHandler) Init(cs ControlScheme) {
	k.controlScheme = cs
	k.Reset()
}

func (k *KeyHandler) Name() string {
	return PREFIX + "-KEYB"
}

func (k *KeyHandler) Reset() {
	log.Printf("%s: Resetting", k.Name())
	k.rows[0], k.rows[1] = 0x0F, 0x0F
	k.colSelect = 0x00
}

func (k *KeyHandler) LinkIRQHandler(m components.IRQHandler) {
	k.irqHandler = m
	log.Printf("%s: Linked IRQ Handler to Keyboard Handler", k.Name())
}

func (k *KeyHandler) Read(addr types.Word) byte {
	var value byte

	switch k.colSelect {
	case ROW_1:
		value = k.rows[1]
	case ROW_2:
		value = k.rows[0]
	default:
		value = 0x00
	}

	return value
}

func (k *KeyHandler) Write(addr types.Word, value byte) {
	k.colSelect = value & 0x30
}

//released sets bit for key to 0
func (k *KeyHandler) KeyDown(key glfw.Key) {
	k.irqHandler.RequestInterrupt(constants.JOYP_HILO_IRQ)
	switch key {
	case k.controlScheme.UP:
		k.rows[0] &= 0xB
	case k.controlScheme.DOWN:
		k.rows[0] &= 0x7
	case k.controlScheme.LEFT:
		k.rows[0] &= 0xD
	case k.controlScheme.RIGHT:
		k.rows[0] &= 0xE
	case k.controlScheme.A:
		k.rows[1] &= 0xE
	case k.controlScheme.B:
		k.rows[1] &= 0xD
	case k.controlScheme.START:
		k.rows[1] &= 0x7
	case k.controlScheme.SELECT:
		k.rows[1] &= 0xB
	}
}

//released sets bit for key to 1
func (k *KeyHandler) KeyUp(key glfw.Key) {
	switch key {
	case k.controlScheme.UP:
		k.rows[0] |= 0x4
	case k.controlScheme.DOWN:
		k.rows[0] |= 0x8
	case k.controlScheme.LEFT:
		k.rows[0] |= 0x2
	case k.controlScheme.RIGHT:
		k.rows[0] |= 0x1
	case k.controlScheme.A:
		k.rows[1] |= 0x1
	case k.controlScheme.B:
		k.rows[1] |= 0x2
	case k.controlScheme.START:
		k.rows[1] |= 0x8
	case k.controlScheme.SELECT:
		k.rows[1] |= 0x4
	}
}

type IO struct {
	KeyHandler          *KeyHandler
	Display             *Display
	ScreenOutputChannel chan *types.Screen
	AudioOutputChannel  chan int
	headless            bool
}

func NewIO() *IO {
	var i *IO = new(IO)
	i.KeyHandler = new(KeyHandler)
	i.Display = new(Display)
	i.ScreenOutputChannel = make(chan *types.Screen)
	i.AudioOutputChannel = make(chan int)
	i.headless = false
	return i
}

func (i *IO) Init(title string, screenSize int, headless bool, onCloseHandler func()) error {
	i.headless = headless
	var err error = nil

	if !i.headless {
		err = i.Display.init(title, screenSize, onCloseHandler)
		if err != nil {
			return err
		}

		i.KeyHandler.Init(DefaultControlScheme) //TODO: allow user to define controlscheme
		i.Display.window.SetKeyCallback(func(w *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
			if action == glfw.Repeat {
				i.KeyHandler.KeyDown(key)
				return
			}

			if action == glfw.Press {
				i.KeyHandler.KeyDown(key)
			} else {
				i.KeyHandler.KeyUp(key)
			}
		})
	}

	return err
}

//This will wait for updates to the display or audio and dispatch them accordingly
func (i *IO) Run() {
	for {
		select {
		case data := <-i.ScreenOutputChannel:
			if !i.headless {
				i.Display.drawFrame(data)
			}
		case data := <-i.AudioOutputChannel:
			if !i.headless {
				log.Println("Writing %d to audio!", data)
			}
		}
	}
}

type Display struct {
	Name                 string
	ScreenSizeMultiplier int
	window               *glfw.Window
}

func (s *Display) init(title string, screenSizeMultiplier int, onCloseHandler func()) error {
	var err error

	if err := glfw.Init(); err != nil {
		log.Fatalln("failed to initialize glfw:", err)
	}

	s.Name = PREFIX + "-SCREEN"

	log.Printf("%s: Initialising display", s.Name)

	s.ScreenSizeMultiplier = screenSizeMultiplier
	log.Printf("%s: Set screen size multiplier to %dx", s.Name, s.ScreenSizeMultiplier)

	glfw.WindowHint(glfw.Resizable, glfw.False)
	window, err := glfw.CreateWindow(SCREEN_WIDTH*s.ScreenSizeMultiplier, SCREEN_HEIGHT*s.ScreenSizeMultiplier, "Testing", nil, nil)
	if err != nil {
		return err
	}

	window.SetTitle(title)

	//TODO fix desktop mode
	window.SetPos((s.ScreenSizeMultiplier)/2, (s.ScreenSizeMultiplier)/2)

	window.SetCloseCallback(func(w *glfw.Window) {
		w.Destroy()
		glfw.Terminate()
		onCloseHandler()
	})

	window.MakeContextCurrent()

	if err := gl.Init(); err != nil {
		return err
	}

	gl.ClearColor(0.255, 0.255, 0.255, 0)

	//resize functionTrue
	/*
		onResize := func(window *glfw.Window, w, h int) {
			gl.Viewport(0, 0, int32(w), int32(h))
			gl.MatrixMode(gl.PROJECTION)
			gl.LoadIdentity()
			gl.Ortho(0, float64(w), float64(h), 0, -1, 1)
			gl.ClearColor(0.255, 0.255, 0.255, 0)
			gl.Clear(gl.COLOR_BUFFER_BIT)
			gl.MatrixMode(gl.MODELVIEW)
			gl.LoadIdentity()
		}

		window.SetSizeCallback(onResize)
	*/

	s.window = window

	return nil

}

func (s *Display) drawFrame(screenData *types.Screen) {
	gl.Viewport(0, 0, int32(SCREEN_WIDTH*s.ScreenSizeMultiplier)*2, int32(SCREEN_HEIGHT*s.ScreenSizeMultiplier)*2)
	gl.MatrixMode(gl.PROJECTION)
	gl.LoadIdentity()
	gl.Ortho(0, float64(SCREEN_WIDTH*s.ScreenSizeMultiplier), float64(SCREEN_HEIGHT*s.ScreenSizeMultiplier), 0, -1, 1)
	gl.ClearColor(0.255, 0.255, 0.255, 0)
	gl.Clear(gl.COLOR_BUFFER_BIT)
	gl.MatrixMode(gl.MODELVIEW)
	gl.LoadIdentity()

	gl.Clear(gl.COLOR_BUFFER_BIT)
	gl.Disable(gl.DEPTH_TEST)
	gl.PointSize(float32(s.ScreenSizeMultiplier) * 2.0)
	//gl.PointSize(float32(s.ScreenSizeMultiplier))
	gl.Begin(gl.POINTS)
	for y := 0; y < SCREEN_HEIGHT; y++ {
		for x := 0; x < SCREEN_WIDTH; x++ {
			var pixel types.RGB = screenData[y][x]
			gl.Color3ub(pixel.Red, pixel.Green, pixel.Blue)
			gl.Vertex2i(int32(x*s.ScreenSizeMultiplier), int32(y*s.ScreenSizeMultiplier))
		}
	}

	gl.End()
	glfw.PollEvents()
	s.window.SwapBuffers()
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package client

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"os"
	fancypath "path"
	"path/filepath"

	"github.com/kubernetes/helm/pkg/common"
)

// ListDeployments lists the deployments in DM.
func (c *Client) ListDeployments() ([]string, error) {
	var l []string
	_, err := c.Get("deployments", &l)
	return l, err
}

// PostChart sends a chart to DM for deploying.
//
// This returns the location for the new chart, typically of the form
// `helm:repo/bucket/name-version.tgz`.
func (c *Client) PostChart(filename, deployname string) (string, error) {
	f, err := os.Open(filename)
	if err != nil {
		return "", err
	}

	u, err := c.url("/v2/charts")
	request, err := http.NewRequest("POST", u, f)
	if err != nil {
		f.Close()
		return "", err
	}

	// There is an argument to be made for using the legacy x-octet-stream for
	// this. But since we control both sides, we should use the standard one.
	// Also, gzip (x-compress) is usually treated as a content encoding. In this
	// case it probably is not, but it makes more sense to follow the standard,
	// even though we don't assume the remote server will strip it off.
	request.Header.Add("Content-Type", "application/x-tar")
	request.Header.Add("Content-Encoding", "gzip")
	request.Header.Add("X-Deployment-Name", deployname)
	request.Header.Add("X-Chart-Name", filepath.Base(filename))
	request.Header.Set("User-Agent", c.agent())

	client := &http.Client{
		Timeout:   c.HTTPTimeout,
		Transport: c.transport(),
	}

	response, err := client.Do(request)
	if err != nil {
		return "", err
	}

	// We only want 201 CREATED. Admittedly, we could accept 200 and 202.
	if response.StatusCode != http.StatusCreated {
		body, err := ioutil.ReadAll(response.Body)
		response.Body.Close()
		if err != nil {
			return "", err
		}
		return "", &HTTPError{StatusCode: response.StatusCode, Message: string(body), URL: request.URL}
	}

	loc := response.Header.Get("Location")
	return loc, nil
}

// GetDeployment retrieves the supplied deployment
func (c *Client) GetDeployment(name string) (*common.Deployment, error) {
	var deployment *common.Deployment
	_, err := c.Get(fancypath.Join("deployments", name), &deployment)
	return deployment, err
}

// DeleteDeployment deletes the supplied deployment
func (c *Client) DeleteDeployment(name string) (*common.Deployment, error) {
	var deployment *common.Deployment
	_, err := c.Delete(filepath.Join("deployments", name), &deployment)
	return deployment, err
}

// DescribeDeployment describes the kubernetes resources of the supplied deployment
func (c *Client) DescribeDeployment(name string) (*common.Deployment, error) {
	var deployment *common.Deployment
	//TODO: implement
	return deployment, nil
}

// PostDeployment posts a deployment object to the manager service.
func (c *Client) PostDeployment(res *common.Resource) error {
	// This is a stop-gap until we get this API cleaned up.
	t := common.DeploymentRequest{
		Configuration: common.Configuration{Resources: []*common.Resource{res}},
		Name:          res.Name,
	}

	data, err := json.Marshal(t)
	if err != nil {
		return err
	}

	var out struct{}
	_, err = c.Post("/deployments", data, &out)
	return err
}
<commit_msg>fix(client): fix append deployments url<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package client

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"os"
	fancypath "path"
	"path/filepath"

	"github.com/kubernetes/helm/pkg/common"
)

// ListDeployments lists the deployments in DM.
func (c *Client) ListDeployments() ([]string, error) {
	var l []string
	_, err := c.Get("deployments", &l)
	return l, err
}

// PostChart sends a chart to DM for deploying.
//
// This returns the location for the new chart, typically of the form
// `helm:repo/bucket/name-version.tgz`.
func (c *Client) PostChart(filename, deployname string) (string, error) {
	f, err := os.Open(filename)
	if err != nil {
		return "", err
	}

	u, err := c.url("/v2/charts")
	request, err := http.NewRequest("POST", u, f)
	if err != nil {
		f.Close()
		return "", err
	}

	// There is an argument to be made for using the legacy x-octet-stream for
	// this. But since we control both sides, we should use the standard one.
	// Also, gzip (x-compress) is usually treated as a content encoding. In this
	// case it probably is not, but it makes more sense to follow the standard,
	// even though we don't assume the remote server will strip it off.
	request.Header.Add("Content-Type", "application/x-tar")
	request.Header.Add("Content-Encoding", "gzip")
	request.Header.Add("X-Deployment-Name", deployname)
	request.Header.Add("X-Chart-Name", filepath.Base(filename))
	request.Header.Set("User-Agent", c.agent())

	client := &http.Client{
		Timeout:   c.HTTPTimeout,
		Transport: c.transport(),
	}

	response, err := client.Do(request)
	if err != nil {
		return "", err
	}

	// We only want 201 CREATED. Admittedly, we could accept 200 and 202.
	if response.StatusCode != http.StatusCreated {
		body, err := ioutil.ReadAll(response.Body)
		response.Body.Close()
		if err != nil {
			return "", err
		}
		return "", &HTTPError{StatusCode: response.StatusCode, Message: string(body), URL: request.URL}
	}

	loc := response.Header.Get("Location")
	return loc, nil
}

// GetDeployment retrieves the supplied deployment
func (c *Client) GetDeployment(name string) (*common.Deployment, error) {
	var deployment *common.Deployment
	_, err := c.Get(fancypath.Join("deployments", name), &deployment)
	return deployment, err
}

// DeleteDeployment deletes the supplied deployment
func (c *Client) DeleteDeployment(name string) (*common.Deployment, error) {
	var deployment *common.Deployment
	_, err := c.Delete(filepath.Join("deployments", name), &deployment)
	return deployment, err
}

// DescribeDeployment describes the kubernetes resources of the supplied deployment
func (c *Client) DescribeDeployment(name string) (*common.Deployment, error) {
	var deployment *common.Deployment
	//TODO: implement
	return deployment, nil
}

// PostDeployment posts a deployment object to the manager service.
func (c *Client) PostDeployment(res *common.Resource) error {
	// This is a stop-gap until we get this API cleaned up.
	t := common.DeploymentRequest{
		Configuration: common.Configuration{Resources: []*common.Resource{res}},
		Name:          res.Name,
	}

	data, err := json.Marshal(t)
	if err != nil {
		return err
	}

	var out struct{}
	_, err = c.Post("deployments", data, &out)
	return err
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"time"
)

// Typed errors
var (
	ErrTeamMemberAlreadyAdded = errors.New("User is already added to this team")
)

// TeamMember model
type TeamMember struct {
	Id         int64
	OrgId      int64
	TeamId     int64
	UserId     int64
	External   bool
	Permission PermissionType

	Created time.Time
	Updated time.Time
}

// ---------------------
// COMMANDS

type AddTeamMemberCommand struct {
	UserId     int64          `json:"userId" binding:"Required"`
	OrgId      int64          `json:"-"`
	TeamId     int64          `json:"-"`
	External   bool           `json:"-"`
	Permission PermissionType `json:"-"`
}

type UpdateTeamMemberCommand struct {
	UserId           int64          `json:"-"`
	OrgId            int64          `json:"-"`
	TeamId           int64          `json:"-"`
	Permission       PermissionType `json:"permission"`
	ProtectLastAdmin bool           `json:"-"`
}

type RemoveTeamMemberCommand struct {
	OrgId            int64 `json:"-"`
	UserId           int64
	TeamId           int64
	ProtectLastAdmin bool `json:"-"`
}

// ----------------------
// QUERIES

type GetTeamMembersQuery struct {
	OrgId    int64
	TeamId   int64
	UserId   int64
	External bool
	Result   []*TeamMemberDTO
}

// ----------------------
// Projections and DTOs

type TeamMemberDTO struct {
	OrgId      int64          `json:"orgId"`
	TeamId     int64          `json:"teamId"`
	UserId     int64          `json:"userId"`
	External   bool           `json:"-"`
	Email      string         `json:"email"`
	Login      string         `json:"login"`
	AvatarUrl  string         `json:"avatarUrl"`
	Labels     []string       `json:"labels"`
	Permission PermissionType `json:"permission"`
}
<commit_msg>teams: explains the external property of a team membership.<commit_after>package models

import (
	"errors"
	"time"
)

// Typed errors
var (
	ErrTeamMemberAlreadyAdded = errors.New("User is already added to this team")
)

// TeamMember model
type TeamMember struct {
	Id         int64
	OrgId      int64
	TeamId     int64
	UserId     int64
	External   bool // Signals that the membership has been created by an external systems, such as LDAP
	Permission PermissionType

	Created time.Time
	Updated time.Time
}

// ---------------------
// COMMANDS

type AddTeamMemberCommand struct {
	UserId     int64          `json:"userId" binding:"Required"`
	OrgId      int64          `json:"-"`
	TeamId     int64          `json:"-"`
	External   bool           `json:"-"`
	Permission PermissionType `json:"-"`
}

type UpdateTeamMemberCommand struct {
	UserId           int64          `json:"-"`
	OrgId            int64          `json:"-"`
	TeamId           int64          `json:"-"`
	Permission       PermissionType `json:"permission"`
	ProtectLastAdmin bool           `json:"-"`
}

type RemoveTeamMemberCommand struct {
	OrgId            int64 `json:"-"`
	UserId           int64
	TeamId           int64
	ProtectLastAdmin bool `json:"-"`
}

// ----------------------
// QUERIES

type GetTeamMembersQuery struct {
	OrgId    int64
	TeamId   int64
	UserId   int64
	External bool
	Result   []*TeamMemberDTO
}

// ----------------------
// Projections and DTOs

type TeamMemberDTO struct {
	OrgId      int64          `json:"orgId"`
	TeamId     int64          `json:"teamId"`
	UserId     int64          `json:"userId"`
	External   bool           `json:"-"`
	Email      string         `json:"email"`
	Login      string         `json:"login"`
	AvatarUrl  string         `json:"avatarUrl"`
	Labels     []string       `json:"labels"`
	Permission PermissionType `json:"permission"`
}
<|endoftext|>


<commit_before>package common

import (
	"fmt"
	"net"
	"sync"
	"time"

	"github.com/miekg/dns"

	kerrors "k8s.io/apimachinery/pkg/util/errors"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/sets"
)

const (
	defaultTTL = 30 * time.Minute
)

type dnsValue struct {
	// All IPv4 addresses for a given domain name
	ips []net.IP
	// Time-to-live value from non-authoritative/cached name server for the domain
	ttl time.Duration
	// Holds (last dns lookup time + ttl), tells when to refresh IPs next time
	nextQueryTime time.Time
}

type DNS struct {
	// Protects dnsMap operations
	lock sync.Mutex
	// Holds dns name and its corresponding information
	dnsMap map[string]dnsValue

	// DNS resolvers
	nameservers []string
	// DNS port
	port string
}

func NewDNS(resolverConfigFile string) (*DNS, error) {
	config, err := dns.ClientConfigFromFile(resolverConfigFile)
	if err != nil || config == nil {
		return nil, fmt.Errorf("cannot initialize the resolver: %v", err)
	}

	return &DNS{
		dnsMap:      map[string]dnsValue{},
		nameservers: filterIPv4Servers(config.Servers),
		port:        config.Port,
	}, nil
}

func (d *DNS) Size() int {
	d.lock.Lock()
	defer d.lock.Unlock()

	return len(d.dnsMap)
}

func (d *DNS) Get(dns string) dnsValue {
	d.lock.Lock()
	defer d.lock.Unlock()

	data := dnsValue{}
	if res, ok := d.dnsMap[dns]; ok {
		data.ips = make([]net.IP, len(res.ips))
		copy(data.ips, res.ips)
		data.ttl = res.ttl
		data.nextQueryTime = res.nextQueryTime
	}
	return data
}

func (d *DNS) Add(dns string) error {
	d.lock.Lock()
	defer d.lock.Unlock()

	d.dnsMap[dns] = dnsValue{}
	err, _ := d.updateOne(dns)
	if err != nil {
		delete(d.dnsMap, dns)
	}
	return err
}

func (d *DNS) Update() (error, bool) {
	d.lock.Lock()
	defer d.lock.Unlock()

	errList := []error{}
	changed := false
	for dns := range d.dnsMap {
		err, updated := d.updateOne(dns)
		if err != nil {
			errList = append(errList, err)
			continue
		}
		if updated {
			changed = true
		}
	}
	return kerrors.NewAggregate(errList), changed
}

func (d *DNS) updateOne(dns string) (error, bool) {
	res, ok := d.dnsMap[dns]
	if !ok {
		// Should not happen, all operations on dnsMap are synchronized by d.lock
		return fmt.Errorf("DNS value not found in dnsMap for domain: %q", dns), false
	}

	ips, minTTL, err := d.getIPsAndMinTTL(dns)
	if err != nil {
		return err, false
	}

	changed := false
	if !ipsEqual(res.ips, ips) {
		changed = true
	}
	res.ips = ips
	res.ttl = minTTL
	res.nextQueryTime = time.Now().Add(res.ttl)
	d.dnsMap[dns] = res
	return nil, changed
}

func (d *DNS) getIPsAndMinTTL(domain string) ([]net.IP, time.Duration, error) {
	ips := []net.IP{}
	var minTTL uint32

	for _, server := range d.nameservers {
		msg := new(dns.Msg)
		msg.SetQuestion(dns.Fqdn(domain), dns.TypeA)

		dialServer := server
		if _, _, err := net.SplitHostPort(server); err != nil {
			dialServer = net.JoinHostPort(server, d.port)
		}
		c := new(dns.Client)
		c.Timeout = 2 * time.Second
		in, _, err := c.Exchange(msg, dialServer)
		if err != nil {
			return nil, defaultTTL, err
		}
		if in != nil && in.Rcode != dns.RcodeSuccess {
			return nil, defaultTTL, fmt.Errorf("failed to get a valid answer: %v", in)
		}

		if in != nil && len(in.Answer) > 0 {
			for _, a := range in.Answer {
				switch t := a.(type) {
				case *dns.A:
					ips = append(ips, t.A)

					if minTTL == 0 || t.Hdr.Ttl < minTTL {
						minTTL = t.Hdr.Ttl
					}
				}
			}
		}
	}

	ttl, err := time.ParseDuration(fmt.Sprintf("%ds", minTTL))
	if err != nil || minTTL == 0 {
		utilruntime.HandleError(fmt.Errorf("Invalid TTL value for domain: %q, err: %v, defaulting ttl=%s", domain, err, defaultTTL.String()))
		ttl = defaultTTL
	}

	return removeDuplicateIPs(ips), ttl, nil
}

func (d *DNS) GetMinQueryTime() (time.Time, bool) {
	d.lock.Lock()
	defer d.lock.Unlock()

	timeSet := false
	var minTime time.Time
	for _, res := range d.dnsMap {
		if (timeSet == false) || res.nextQueryTime.Before(minTime) {
			timeSet = true
			minTime = res.nextQueryTime
		}
	}

	return minTime, timeSet
}

func ipsEqual(oldips, newips []net.IP) bool {
	if len(oldips) != len(newips) {
		return false
	}

	for _, oldip := range oldips {
		found := false
		for _, newip := range newips {
			if oldip.Equal(newip) {
				found = true
				break
			}
		}
		if !found {
			return false
		}
	}
	return true
}

func filterIPv4Servers(servers []string) []string {
	ipv4Servers := []string{}
	for _, server := range servers {
		ipString := server
		if host, _, err := net.SplitHostPort(server); err == nil {
			ipString = host
		}

		if ip := net.ParseIP(ipString); ip != nil {
			if ip.To4() != nil {
				ipv4Servers = append(ipv4Servers, server)
			}
		}
	}

	return ipv4Servers
}

func removeDuplicateIPs(ips []net.IP) []net.IP {
	ipSet := sets.NewString()
	for _, ip := range ips {
		ipSet.Insert(ip.String())
	}

	uniqueIPs := []net.IP{}
	for _, str := range ipSet.List() {
		ip := net.ParseIP(str)
		if ip != nil {
			uniqueIPs = append(uniqueIPs, ip)
		}
	}

	return uniqueIPs
}
<commit_msg>Do not throw spurious error when minTTL=0 for the domain in egress network policy<commit_after>package common

import (
	"fmt"
	"net"
	"sync"
	"time"

	"github.com/miekg/dns"

	kerrors "k8s.io/apimachinery/pkg/util/errors"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/sets"
)

const (
	defaultTTL = 30 * time.Minute
)

type dnsValue struct {
	// All IPv4 addresses for a given domain name
	ips []net.IP
	// Time-to-live value from non-authoritative/cached name server for the domain
	ttl time.Duration
	// Holds (last dns lookup time + ttl), tells when to refresh IPs next time
	nextQueryTime time.Time
}

type DNS struct {
	// Protects dnsMap operations
	lock sync.Mutex
	// Holds dns name and its corresponding information
	dnsMap map[string]dnsValue

	// DNS resolvers
	nameservers []string
	// DNS port
	port string
}

func NewDNS(resolverConfigFile string) (*DNS, error) {
	config, err := dns.ClientConfigFromFile(resolverConfigFile)
	if err != nil || config == nil {
		return nil, fmt.Errorf("cannot initialize the resolver: %v", err)
	}

	return &DNS{
		dnsMap:      map[string]dnsValue{},
		nameservers: filterIPv4Servers(config.Servers),
		port:        config.Port,
	}, nil
}

func (d *DNS) Size() int {
	d.lock.Lock()
	defer d.lock.Unlock()

	return len(d.dnsMap)
}

func (d *DNS) Get(dns string) dnsValue {
	d.lock.Lock()
	defer d.lock.Unlock()

	data := dnsValue{}
	if res, ok := d.dnsMap[dns]; ok {
		data.ips = make([]net.IP, len(res.ips))
		copy(data.ips, res.ips)
		data.ttl = res.ttl
		data.nextQueryTime = res.nextQueryTime
	}
	return data
}

func (d *DNS) Add(dns string) error {
	d.lock.Lock()
	defer d.lock.Unlock()

	d.dnsMap[dns] = dnsValue{}
	err, _ := d.updateOne(dns)
	if err != nil {
		delete(d.dnsMap, dns)
	}
	return err
}

func (d *DNS) Update() (error, bool) {
	d.lock.Lock()
	defer d.lock.Unlock()

	errList := []error{}
	changed := false
	for dns := range d.dnsMap {
		err, updated := d.updateOne(dns)
		if err != nil {
			errList = append(errList, err)
			continue
		}
		if updated {
			changed = true
		}
	}
	return kerrors.NewAggregate(errList), changed
}

func (d *DNS) updateOne(dns string) (error, bool) {
	res, ok := d.dnsMap[dns]
	if !ok {
		// Should not happen, all operations on dnsMap are synchronized by d.lock
		return fmt.Errorf("DNS value not found in dnsMap for domain: %q", dns), false
	}

	ips, minTTL, err := d.getIPsAndMinTTL(dns)
	if err != nil {
		return err, false
	}

	changed := false
	if !ipsEqual(res.ips, ips) {
		changed = true
	}
	res.ips = ips
	res.ttl = minTTL
	res.nextQueryTime = time.Now().Add(res.ttl)
	d.dnsMap[dns] = res
	return nil, changed
}

func (d *DNS) getIPsAndMinTTL(domain string) ([]net.IP, time.Duration, error) {
	ips := []net.IP{}
	ttlSet := false
	var minTTL uint32

	for _, server := range d.nameservers {
		msg := new(dns.Msg)
		msg.SetQuestion(dns.Fqdn(domain), dns.TypeA)

		dialServer := server
		if _, _, err := net.SplitHostPort(server); err != nil {
			dialServer = net.JoinHostPort(server, d.port)
		}
		c := new(dns.Client)
		c.Timeout = 2 * time.Second
		in, _, err := c.Exchange(msg, dialServer)
		if err != nil {
			return nil, defaultTTL, err
		}
		if in != nil && in.Rcode != dns.RcodeSuccess {
			return nil, defaultTTL, fmt.Errorf("failed to get a valid answer: %v", in)
		}

		if in != nil && len(in.Answer) > 0 {
			for _, a := range in.Answer {
				switch t := a.(type) {
				case *dns.A:
					ips = append(ips, t.A)

					if !ttlSet || t.Hdr.Ttl < minTTL {
						minTTL = t.Hdr.Ttl
						ttlSet = true
					}
				}
			}
		}
	}

	if !ttlSet {
		return nil, defaultTTL, fmt.Errorf("IPv4 addr not found for domain: %q, nameservers: %v", domain, d.nameservers)
	}

	ttl, err := time.ParseDuration(fmt.Sprintf("%ds", minTTL))
	if err != nil {
		utilruntime.HandleError(fmt.Errorf("Invalid TTL value for domain: %q, err: %v, defaulting ttl=%s", domain, err, defaultTTL.String()))
		ttl = defaultTTL
	}

	return removeDuplicateIPs(ips), ttl, nil
}

func (d *DNS) GetMinQueryTime() (time.Time, bool) {
	d.lock.Lock()
	defer d.lock.Unlock()

	timeSet := false
	var minTime time.Time
	for _, res := range d.dnsMap {
		if (timeSet == false) || res.nextQueryTime.Before(minTime) {
			timeSet = true
			minTime = res.nextQueryTime
		}
	}

	return minTime, timeSet
}

func ipsEqual(oldips, newips []net.IP) bool {
	if len(oldips) != len(newips) {
		return false
	}

	for _, oldip := range oldips {
		found := false
		for _, newip := range newips {
			if oldip.Equal(newip) {
				found = true
				break
			}
		}
		if !found {
			return false
		}
	}
	return true
}

func filterIPv4Servers(servers []string) []string {
	ipv4Servers := []string{}
	for _, server := range servers {
		ipString := server
		if host, _, err := net.SplitHostPort(server); err == nil {
			ipString = host
		}

		if ip := net.ParseIP(ipString); ip != nil {
			if ip.To4() != nil {
				ipv4Servers = append(ipv4Servers, server)
			}
		}
	}

	return ipv4Servers
}

func removeDuplicateIPs(ips []net.IP) []net.IP {
	ipSet := sets.NewString()
	for _, ip := range ips {
		ipSet.Insert(ip.String())
	}

	uniqueIPs := []net.IP{}
	for _, str := range ipSet.List() {
		ip := net.ParseIP(str)
		if ip != nil {
			uniqueIPs = append(uniqueIPs, ip)
		}
	}

	return uniqueIPs
}
<|endoftext|>