<commit_before>package flightid

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

	"google.golang.org/appengine"
	"google.golang.org/appengine/urlfetch"
	//"golang.org/x/net/context"

	"github.com/skypies/geo"
	"github.com/skypies/util/date"

	"github.com/skypies/pi/airspace"
	fdb "github.com/skypies/flightdb2"

	"github.com/skypies/complaints/fr24"
)

func init() {
	http.HandleFunc("/cdb/airspace", airspaceHandler)
}

// {{{ airspaceHandler

func airspaceHandler(w http.ResponseWriter, r *http.Request) {
	client := urlfetch.Client(appengine.NewContext(r))

	str := ""

	f,err,debug := FindOverhead(client, geo.Latlong{37.060312,-121.990814}, 0, true)
	str += fmt.Sprintf("======/// FindOverhead ///======\nerr=%v\nf=%s\n\n%s", err, f, debug)

/*
	as, err := FetchAirspace(client)
	if err != nil {
		http.Error(w, fmt.Sprintf("FetchAirspace: %v", err), http.StatusInternalServerError)
		return
	}
	str += "======/// Airspace ///=====\n\n" + as.String()

	snaps := AirspaceToSnapshots(as)
	str += "\n\n=====/// Snaps ///=====\n\n"
	for _,fs := range snaps {
		str += fmt.Sprintf("* %s\n", fs)
	}
*/

	w.Header().Set("Content-Type", "text/plain")
	w.Write([]byte("OK\n\n"+str))
}

// }}}

// {{{ AirspaceToSnapshots

func AirspaceToSnapshots(as *airspace.Airspace) []fdb.FlightSnapshot {
	ret := []fdb.FlightSnapshot{}

	for _,ad := range as.Aircraft {
		fs := fdb.FlightSnapshot{
			Trackpoint: fdb.TrackpointFromADSB(ad.Msg),
			Flight: fdb.BlankFlight(),
		}
		fs.Airframe = ad.Airframe
		fs.Identity.Schedule = ad.Schedule
		fs.Identity.IcaoId = string(ad.Msg.Icao24)
		fs.Identity.Callsign = ad.Msg.Callsign
		ret = append(ret, fs)
	}

	return ret
}

// }}}
// {{{ FlightSnapshotToAircraft

func FlightSnapshotToAircraft(snap *fdb.FlightSnapshot) *fr24.Aircraft {
	if snap == nil { return nil }

	return &fr24.Aircraft{
		Dist: snap.DistToReferenceKM,
		Dist3: snap.Dist3ToReferenceKM,
		BearingFromObserver: snap.BearingToReference,
		// Fr24Url string
		Id: snap.Flight.IdSpecString(),
		Id2: snap.Flight.IcaoId,
		Lat: snap.Trackpoint.Lat,
		Long: snap.Trackpoint.Long,
		Track: snap.Trackpoint.Heading,
		Altitude: snap.Trackpoint.Altitude,
		Speed: snap.Trackpoint.GroundSpeed,
		// Squawk string
		// Radar string
		EquipType: snap.Flight.EquipmentType,
		Registration: snap.Flight.Registration,
		// Epoch float64
		Origin: snap.Flight.Origin,
		Destination: snap.Flight.Destination,
		FlightNumber: snap.Flight.BestFlightNumber(),
		// Unknown float64
		VerticalSpeed: snap.Trackpoint.VerticalRate,
		Callsign: snap.Flight.Callsign,
		// Unknown2 float64
	}
}

// }}}

// {{{ FetchAirspace

func FetchAirspace(client *http.Client) (*airspace.Airspace, error) {
	as := airspace.Airspace{}
	url := "http://fdb.serfr1.org/?json=1"

	if resp,err := client.Get(url); err != nil {
		return nil, err
	} else if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf ("Bad status: %v", resp.Status)
	} else if err := json.NewDecoder(resp.Body).Decode(&as); err != nil {
		return nil, err
	}

	return &as, nil
}

// }}}
// {{{ FilterSnapshots

func FilterSnapshots(in []fdb.FlightSnapshot) []fdb.FlightSnapshot {
	out := []fdb.FlightSnapshot{}

	for _,snap := range in {
		if snap.Flight.BestFlightNumber() == "" { continue }
		if snap.Trackpoint.Altitude > 28000 { continue }
		if snap.Trackpoint.Altitude <   500 { continue }
		if snap.Dist3ToReferenceKM > 80 { continue }

		out = append(out, snap)
/*
		if a.Radar == "T-F5M" { continue }    // 5m delayed data; not what's overhead
		if a.FlightNumber == "" {continue}
		// if a.BestIdent() == "" { continue }  // No ID info; not much interesting to say
		if a.Altitude > 28000 { continue }    // Too high to be the problem
		if a.Altitude <   500 { continue }    // Too low to be the problem

		// Strip out little planes
		skip := false
		for _,e := range kBlacklistEquipmentTypes {
			if a.EquipType == e { skip = true }
		}
		if skip { continue }
*/
	}

	return out
}

// }}}
// {{{ SelectOverhead

func SelectOverhead(snaps []fdb.FlightSnapshot) (*fdb.FlightSnapshot, string) {
	if len(snaps) == 0 { return nil, "** empty list\n" }

	// closest plane has to be within 12 km to be 'overhead', and it has
	// to be 4km away from the next-closest
	if (snaps[0].Dist3ToReferenceKM < 12.0) {
		if (len(snaps) == 1) || (snaps[1].Dist3ToReferenceKM - snaps[0].Dist3ToReferenceKM) > 4.0 {
			return &snaps[0], "** selected 1st\n"

		} else {
			return nil, "** 2nd was too close to 1st\n"
		}

	} else {
		return nil, "** 1st was too far away\n"
	}

}

// }}}

// {{{ FindOverhead

func FindOverhead(client *http.Client, pos geo.Latlong, elev float64, grabAny bool) (*fr24.Aircraft, error, string) {
	str := fmt.Sprintf("*** FindOverhead for %s@%.0fft, at %s\n", pos, elev, date.NowInPdt())

	as, err := FetchAirspace(client)
	if err != nil { return nil, err, str }

	snaps := AirspaceToSnapshots(as)
	for i,_ := range snaps {
		snaps[i].LocalizeTo(pos, elev)
	}
	sort.Sort(fdb.FlightSnapshotsByDist3(snaps))
	str += "** nearby list:-\n"+fdb.DebugFlightSnapshotList(snaps)

	filtered := FilterSnapshots(snaps)
	str += "** filtered:-\n"+fdb.DebugFlightSnapshotList(filtered)
	if len(filtered) == 0 { return nil, nil, str }

	overheadSnap,debug := SelectOverhead(filtered)
	if overheadSnap == nil && grabAny {
		overheadSnap = &filtered[0]
		debug += "** grabbing first anyway\n"
	}
	str += debug

	return FlightSnapshotToAircraft(overheadSnap), nil, str
}

// }}}

// {{{ -------------------------={ E N D }=----------------------------------

// Local variables:
// folded-file: t
// end:

// }}}
<commit_msg>Apply a bounding box when fetching overhead from fdb.serfr1.org<commit_after>package flightid

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

	"google.golang.org/appengine"
	"google.golang.org/appengine/urlfetch"
	//"golang.org/x/net/context"

	"github.com/skypies/geo"
	"github.com/skypies/util/date"

	"github.com/skypies/pi/airspace"
	fdb "github.com/skypies/flightdb2"

	"github.com/skypies/complaints/fr24"
)

func init() {
	http.HandleFunc("/cdb/airspace", airspaceHandler)
}

// {{{ airspaceHandler

func airspaceHandler(w http.ResponseWriter, r *http.Request) {
	client := urlfetch.Client(appengine.NewContext(r))

	str := ""

	f,err,debug := FindOverhead(client, geo.Latlong{37.060312,-121.990814}, 0, true)
	str += fmt.Sprintf("======/// FindOverhead ///======\nerr=%v\nf=%s\n\n%s", err, f, debug)

/*
	as, err := FetchAirspace(client)
	if err != nil {
		http.Error(w, fmt.Sprintf("FetchAirspace: %v", err), http.StatusInternalServerError)
		return
	}
	str += "======/// Airspace ///=====\n\n" + as.String()

	snaps := AirspaceToSnapshots(as)
	str += "\n\n=====/// Snaps ///=====\n\n"
	for _,fs := range snaps {
		str += fmt.Sprintf("* %s\n", fs)
	}
*/

	w.Header().Set("Content-Type", "text/plain")
	w.Write([]byte("OK\n\n"+str))
}

// }}}

// {{{ AirspaceToSnapshots

func AirspaceToSnapshots(as *airspace.Airspace) []fdb.FlightSnapshot {
	ret := []fdb.FlightSnapshot{}

	for _,ad := range as.Aircraft {
		fs := fdb.FlightSnapshot{
			Trackpoint: fdb.TrackpointFromADSB(ad.Msg),
			Flight: fdb.BlankFlight(),
		}
		fs.Airframe = ad.Airframe
		fs.Identity.Schedule = ad.Schedule
		fs.Identity.IcaoId = string(ad.Msg.Icao24)
		fs.Identity.Callsign = ad.Msg.Callsign
		ret = append(ret, fs)
	}

	return ret
}

// }}}
// {{{ FlightSnapshotToAircraft

func FlightSnapshotToAircraft(snap *fdb.FlightSnapshot) *fr24.Aircraft {
	if snap == nil { return nil }

	return &fr24.Aircraft{
		Dist: snap.DistToReferenceKM,
		Dist3: snap.Dist3ToReferenceKM,
		BearingFromObserver: snap.BearingToReference,
		// Fr24Url string
		Id: snap.Flight.IdSpecString(),
		Id2: snap.Flight.IcaoId,
		Lat: snap.Trackpoint.Lat,
		Long: snap.Trackpoint.Long,
		Track: snap.Trackpoint.Heading,
		Altitude: snap.Trackpoint.Altitude,
		Speed: snap.Trackpoint.GroundSpeed,
		// Squawk string
		// Radar string
		EquipType: snap.Flight.EquipmentType,
		Registration: snap.Flight.Registration,
		// Epoch float64
		Origin: snap.Flight.Origin,
		Destination: snap.Flight.Destination,
		FlightNumber: snap.Flight.BestFlightNumber(),
		// Unknown float64
		VerticalSpeed: snap.Trackpoint.VerticalRate,
		Callsign: snap.Flight.Callsign,
		// Unknown2 float64
	}
}

// }}}

// {{{ FetchAirspace

func FetchAirspace(client *http.Client, bbox geo.LatlongBox) (*airspace.Airspace, string, error) {
	as := airspace.Airspace{}
	url := "http://fdb.serfr1.org/?json=1&"+bbox.ToCGIArgs("box")

	str := fmt.Sprintf("* FetchAirspace(%s)\n* %s\n", bbox, url)

	if resp,err := client.Get(url); err != nil {
		return nil, str, err
	} else if resp.StatusCode != http.StatusOK {
		return nil, str, fmt.Errorf ("Bad status: %v", resp.Status)
	} else if err := json.NewDecoder(resp.Body).Decode(&as); err != nil {
		return nil, str, err
	}

	return &as, str, nil
}

// }}}
// {{{ FilterSnapshots

func FilterSnapshots(in []fdb.FlightSnapshot) []fdb.FlightSnapshot {
	out := []fdb.FlightSnapshot{}

	for _,snap := range in {
		if snap.Flight.BestFlightNumber() == "" { continue }
		if snap.Trackpoint.Altitude > 28000 { continue }
		if snap.Trackpoint.Altitude <   500 { continue }
		if snap.Dist3ToReferenceKM > 80 { continue }

		out = append(out, snap)
/*
		if a.Radar == "T-F5M" { continue }    // 5m delayed data; not what's overhead
		if a.FlightNumber == "" {continue}
		// if a.BestIdent() == "" { continue }  // No ID info; not much interesting to say
		if a.Altitude > 28000 { continue }    // Too high to be the problem
		if a.Altitude <   500 { continue }    // Too low to be the problem

		// Strip out little planes
		skip := false
		for _,e := range kBlacklistEquipmentTypes {
			if a.EquipType == e { skip = true }
		}
		if skip { continue }
*/
	}

	return out
}

// }}}
// {{{ SelectOverhead

func SelectOverhead(snaps []fdb.FlightSnapshot) (*fdb.FlightSnapshot, string) {
	if len(snaps) == 0 { return nil, "** empty list\n" }

	// closest plane has to be within 12 km to be 'overhead', and it has
	// to be 4km away from the next-closest
	if (snaps[0].Dist3ToReferenceKM < 12.0) {
		if (len(snaps) == 1) || (snaps[1].Dist3ToReferenceKM - snaps[0].Dist3ToReferenceKM) > 4.0 {
			return &snaps[0], "** selected 1st\n"

		} else {
			return nil, "** 2nd was too close to 1st\n"
		}

	} else {
		return nil, "** 1st was too far away\n"
	}

}

// }}}

// {{{ FindOverhead

func FindOverhead(client *http.Client, pos geo.Latlong, elev float64, grabAny bool) (*fr24.Aircraft, error, string) {
	str := fmt.Sprintf("*** FindOverhead for %s@%.0fft, at %s\n", pos, elev, date.NowInPdt())

	if pos.IsNil() {
		return nil, fmt.Errorf("flightid.FindOverhead needs a non-nil position"), str
	}

	bbox := pos.Box(64,64) // A box with sides ~40 miles, centered on the observer

	as, deb, err := FetchAirspace(client, bbox)
	str += deb
	if err != nil { return nil, err, str }

	snaps := AirspaceToSnapshots(as)
	for i,_ := range snaps {
		snaps[i].LocalizeTo(pos, elev)
	}
	sort.Sort(fdb.FlightSnapshotsByDist3(snaps))
	str += "** nearby list:-\n"+fdb.DebugFlightSnapshotList(snaps)

	filtered := FilterSnapshots(snaps)
	str += "** filtered:-\n"+fdb.DebugFlightSnapshotList(filtered)
	if len(filtered) == 0 { return nil, nil, str }

	overheadSnap,debug := SelectOverhead(filtered)
	if overheadSnap == nil && grabAny {
		overheadSnap = &filtered[0]
		debug += "** grabbing first anyway\n"
	}
	str += debug

	return FlightSnapshotToAircraft(overheadSnap), nil, str
}

// }}}

// {{{ -------------------------={ E N D }=----------------------------------

// Local variables:
// folded-file: t
// end:

// }}}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2017 Ludovic Fauvet
// Licensed under the MIT license

package scan

import (
	"bufio"
	"errors"
	"fmt"
	"github.com/etix/mirrorbits/utils"
	"github.com/garyburd/redigo/redis"
	"io"
	"net/url"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
)

var (
	rsyncOutputLine = regexp.MustCompile(`^.+\s+([0-9,]+)\s+([0-9/]+)\s+([0-9:]+)\s+(.*)$`)
)

type RsyncScanner struct {
	scan *scan
}

func (r *RsyncScanner) Scan(rsyncURL, identifier string, conn redis.Conn, stop chan bool) error {
	var env []string

	if !strings.HasPrefix(rsyncURL, "rsync://") {
		return fmt.Errorf("%s does not start with rsync://", rsyncURL)
	}

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

	// Extract the credentials
	if u.User != nil {
		if u.User.Username() != "" {
			env = append(env, fmt.Sprintf("USER=%s", u.User.Username()))
		}
		if password, ok := u.User.Password(); ok {
			env = append(env, fmt.Sprintf("RSYNC_PASSWORD=%s", password))
		}

		// Remove the credentials from the URL as we pass them through the environnement
		u.User = nil
	}

	cmd := exec.Command("rsync", "-r", "--no-motd", "--timeout=30", "--contimeout=30", u.String())

	// Setup the environnement
	cmd.Env = env

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}

	// Pipe stdout
	reader := bufio.NewReader(stdout)

	if utils.IsStopped(stop) {
		return ScanAborted
	}

	// Start the process
	if err := cmd.Start(); err != nil {
		return err
	}

	log.Infof("[%s] Requesting file list via rsync...", identifier)

	scanfinished := make(chan bool)
	go func() {
		select {
		case <-stop:
			cmd.Process.Kill()
			return
		case <-scanfinished:
			return
		}
	}()
	defer close(scanfinished)

	count := 0

	line, err := readln(reader)
	for err == nil {
		var size int64
		var f filedata

		if utils.IsStopped(stop) {
			return ScanAborted
		}

		// Parse one line returned by rsync
		ret := rsyncOutputLine.FindStringSubmatch(line)
		if ret[0][0] == 'd' || ret[0][0] == 'l' {
			// Skip directories and links
			goto cont
		}

		// Add the leading slash
		if ret[4][0] != '/' {
			ret[4] = "/" + ret[4]
		}

		// Remove the commas in the file size
		ret[1] = strings.Replace(ret[1], ",", "", -1)
		// Convert the size to int
		size, err = strconv.ParseInt(ret[1], 10, 64)
		if err != nil {
			log.Errorf("[%s] ScanRsync: Invalid size: %s", identifier, ret[1])
			goto cont
		}

		// Fill the struct
		f.size = size
		f.path = ret[4]

		r.scan.ScannerAddFile(f)

		count++
	cont:
		line, err = readln(reader)
	}

	if err1 := cmd.Wait(); err1 != nil {
		switch err1.Error() {
		case "exit status 5":
			err1 = errors.New("rsync: Error starting client-server protocol")
			break
		case "exit status 10":
			err1 = errors.New("rsync: Error in socket I/O")
			break
		case "exit status 11":
			err1 = errors.New("rsync: Error in file I/O")
			break
		case "exit status 30":
			err1 = errors.New("rsync: Timeout in data send/receive")
			break
		default:
			if utils.IsStopped(stop) {
				err1 = ScanAborted
			} else {
				err1 = errors.New("rsync: " + err1.Error())
			}
		}
		return err1
	}

	if err != io.EOF {
		return err
	}

	return nil
}

func readln(r *bufio.Reader) (string, error) {
	var (
		isPrefix bool = true
		err      error
		line, ln []byte
	)
	for isPrefix && err == nil {
		line, isPrefix, err = r.ReadLine()
		ln = append(ln, line...)
	}
	return string(ln), err
}
<commit_msg>rsync: handle error code 35<commit_after>// Copyright (c) 2014-2017 Ludovic Fauvet
// Licensed under the MIT license

package scan

import (
	"bufio"
	"errors"
	"fmt"
	"github.com/etix/mirrorbits/utils"
	"github.com/garyburd/redigo/redis"
	"io"
	"net/url"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
)

var (
	rsyncOutputLine = regexp.MustCompile(`^.+\s+([0-9,]+)\s+([0-9/]+)\s+([0-9:]+)\s+(.*)$`)
)

type RsyncScanner struct {
	scan *scan
}

func (r *RsyncScanner) Scan(rsyncURL, identifier string, conn redis.Conn, stop chan bool) error {
	var env []string

	if !strings.HasPrefix(rsyncURL, "rsync://") {
		return fmt.Errorf("%s does not start with rsync://", rsyncURL)
	}

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

	// Extract the credentials
	if u.User != nil {
		if u.User.Username() != "" {
			env = append(env, fmt.Sprintf("USER=%s", u.User.Username()))
		}
		if password, ok := u.User.Password(); ok {
			env = append(env, fmt.Sprintf("RSYNC_PASSWORD=%s", password))
		}

		// Remove the credentials from the URL as we pass them through the environnement
		u.User = nil
	}

	cmd := exec.Command("rsync", "-r", "--no-motd", "--timeout=30", "--contimeout=30", u.String())

	// Setup the environnement
	cmd.Env = env

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}

	// Pipe stdout
	reader := bufio.NewReader(stdout)

	if utils.IsStopped(stop) {
		return ScanAborted
	}

	// Start the process
	if err := cmd.Start(); err != nil {
		return err
	}

	log.Infof("[%s] Requesting file list via rsync...", identifier)

	scanfinished := make(chan bool)
	go func() {
		select {
		case <-stop:
			cmd.Process.Kill()
			return
		case <-scanfinished:
			return
		}
	}()
	defer close(scanfinished)

	count := 0

	line, err := readln(reader)
	for err == nil {
		var size int64
		var f filedata

		if utils.IsStopped(stop) {
			return ScanAborted
		}

		// Parse one line returned by rsync
		ret := rsyncOutputLine.FindStringSubmatch(line)
		if ret[0][0] == 'd' || ret[0][0] == 'l' {
			// Skip directories and links
			goto cont
		}

		// Add the leading slash
		if ret[4][0] != '/' {
			ret[4] = "/" + ret[4]
		}

		// Remove the commas in the file size
		ret[1] = strings.Replace(ret[1], ",", "", -1)
		// Convert the size to int
		size, err = strconv.ParseInt(ret[1], 10, 64)
		if err != nil {
			log.Errorf("[%s] ScanRsync: Invalid size: %s", identifier, ret[1])
			goto cont
		}

		// Fill the struct
		f.size = size
		f.path = ret[4]

		r.scan.ScannerAddFile(f)

		count++
	cont:
		line, err = readln(reader)
	}

	if err1 := cmd.Wait(); err1 != nil {
		switch err1.Error() {
		case "exit status 5":
			err1 = errors.New("rsync: Error starting client-server protocol")
			break
		case "exit status 10":
			err1 = errors.New("rsync: Error in socket I/O")
			break
		case "exit status 11":
			err1 = errors.New("rsync: Error in file I/O")
			break
		case "exit status 30":
			err1 = errors.New("rsync: Timeout in data send/receive")
			break
		case "exit status 35":
			err1 = errors.New("Timeout waiting for daemon connection")
			break
		default:
			if utils.IsStopped(stop) {
				err1 = ScanAborted
			} else {
				err1 = errors.New("rsync: " + err1.Error())
			}
		}
		return err1
	}

	if err != io.EOF {
		return err
	}

	return nil
}

func readln(r *bufio.Reader) (string, error) {
	var (
		isPrefix bool = true
		err      error
		line, ln []byte
	)
	for isPrefix && err == nil {
		line, isPrefix, err = r.ReadLine()
		ln = append(ln, line...)
	}
	return string(ln), err
}
<|endoftext|>


<commit_before>package scard

import (
	"testing"
)

// FIXME: all tests assume pcscd is running, and there is a single reader with a
// card present

type testCard struct {
	ctx  *Context
	card *Card
}

func setup(t *testing.T) *testCard {
	var ctx *Context
	var card *Card

	ctx, err := EstablishContext()
	if err != nil {
		t.Error(err)
		t.FailNow()
	}

	defer func() {
		if card == nil {
			ctx.Release()
		}
	}()

	readers, err := ctx.ListReaders()
	if err != nil {
		t.Error(err)
		t.FailNow()
	}

	if len(readers) == 0 {
		t.Logf("No readers")
		t.FailNow()
	}

	card, err = ctx.Connect(readers[0], SHARE_EXCLUSIVE, PROTOCOL_ANY)
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	var status *CardStatus
	status, err = card.Status()
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	t.Logf("CardState: %+#v\n", status)
	if status.State.String() != PRESENT.String() {

		//t.Errorf("unexpected state: %s\n", status.State.String())
		//	t.FailNow()
	}

	return &testCard{ctx: ctx, card: card}
}

func teardown(c *testCard) {
	if c.card != nil {
		c.card.Disconnect(LEAVE_CARD)
	}

	if c.ctx != nil {
		c.ctx.Release()
	}
}

func TestListReaders(t *testing.T) {
	ctx, err := EstablishContext()
	if err != nil {
		t.Fatal(err)
	}
	defer ctx.Release()
	readers, err := ctx.ListReaders()
	if err != nil {
		t.Fatal(err)
	}
	for _, reader := range readers {
		t.Log(reader)
	}
}

func TestListReaderGroups(t *testing.T) {
	ctx, err := EstablishContext()
	if err != nil {
		t.Fatal(err)
	}
	defer ctx.Release()
	groups, err := ctx.ListReaderGroups()
	if err != nil {
		t.Fatal(err)
	}
	for _, group := range groups {
		t.Log(group)
	}
}

func TestGetAttrib(t *testing.T) {
	c := setup(t)
	defer teardown(c)

	atr, err := c.card.GetAttrib(ATTR_ATR_STRING)
	if err != nil {
		t.Error(err)
	} else {
		t.Logf("ATTR_ATR_STRING: % x\n", atr)
	}

	vendor, err := c.card.GetAttrib(ATTR_VENDOR_NAME)
	if err != nil {
		t.Error(err)
	} else {
		t.Logf("ATTR_VENDOR_NAME: %s [% x]\n", string(vendor), vendor)
	}
}
<commit_msg>skip tests if pcscd or card is not available<commit_after>package scard

import (
	"testing"
)

// FIXME: all tests assume pcscd is running, and there is a single reader with a
// card present

type testCard struct {
	ctx  *Context
	card *Card
}

func setup(t *testing.T) *testCard {
	var ctx *Context
	var card *Card

	ctx, err := EstablishContext()
	if err != nil {
		t.Skipf("EstablishContext: %s", err)
		t.SkipNow()
	}

	defer func() {
		if card == nil {
			ctx.Release()
		}
	}()

	readers, err := ctx.ListReaders()
	if err != nil {
		t.Error(err)
		t.FailNow()
	}

	if len(readers) == 0 {
		t.Skip("no smartcard reader found")
		t.SkipNow()
	}

	card, err = ctx.Connect(readers[0], SHARE_EXCLUSIVE, PROTOCOL_ANY)
	if err != nil {
		t.Skip("no smartcard found")
		t.SkipNow()
	}
	return &testCard{ctx: ctx, card: card}
}

func teardown(c *testCard) {
	if c.card != nil {
		c.card.Disconnect(LEAVE_CARD)
	}

	if c.ctx != nil {
		c.ctx.Release()
	}
}

func TestListReaders(t *testing.T) {
	ctx, err := EstablishContext()
	if err != nil {
		t.Skipf("EstablishContext: %s", err)
		t.SkipNow()
	}
	defer ctx.Release()
	readers, err := ctx.ListReaders()
	if err != nil {
		t.Fatal(err)
	}
	for _, reader := range readers {
		t.Log(reader)
	}
}

func TestListReaderGroups(t *testing.T) {
	ctx, err := EstablishContext()
	if err != nil {
		t.Skipf("EstablishContext: %s", err)
		t.SkipNow()
	}
	defer ctx.Release()
	groups, err := ctx.ListReaderGroups()
	if err != nil {
		t.Fatal(err)
	}
	for _, group := range groups {
		t.Log(group)
	}
}

func TestGetAttrib(t *testing.T) {
	c := setup(t)
	defer teardown(c)

	atr, err := c.card.GetAttrib(ATTR_ATR_STRING)
	if err != nil {
		t.Error(err)
	} else {
		t.Logf("ATTR_ATR_STRING: % x\n", atr)
	}

	vendor, err := c.card.GetAttrib(ATTR_VENDOR_NAME)
	if err != nil {
		t.Error(err)
	} else {
		t.Logf("ATTR_VENDOR_NAME: %s [% x]\n", string(vendor), vendor)
	}
}
<|endoftext|>


<commit_before>package sandbox

import (
	"golang.org/x/sys/unix"
	"testing"
)

// Test prlimit64
func Test_prlimit64(t *testing.T) {
	rlimit1 := &unix.Rlimit{Cur:131072, Max:1048576}
	myself := unix.Getpid()
	err := prlimit64(myself, unix.RLIMIT_FSIZE, rlimit1, nil)
	if err != nil {
		t.Errorf("Test #1 failed. Error: %s", err.Error())
	}

	// Use unix.Getrlimit to check result
	rlimit1Copy1 := &unix.Rlimit{}
	err = unix.Getrlimit(unix.RLIMIT_FSIZE, rlimit1Copy1);
	if err != nil {
		t.Fatalf("Getrlimit failed: %s", err.Error());
	}
	if (*rlimit1 != *rlimit1Copy1) {
		t.Errorf("Test #1 failed. NewLimit has not been set.")
	}

	rlimit2 := &unix.Rlimit{Cur:262144, Max:524288}
	rlimit1Copy2 := &unix.Rlimit{}
	err = prlimit64(myself, unix.RLIMIT_FSIZE, rlimit2, rlimit1Copy2)
	if err != nil {
		t.Errorf("Test #2 failed. Error: %s", err.Error());
	} else if *rlimit1Copy2 != *rlimit1 {
		t.Errorf("Test #2 failed. OldLimit contains wrong value.");
	}

	uid := unix.Getuid()
	if uid != 0 {
		rlimit3 := &unix.Rlimit{Cur:262144, Max:1048576}
		err = prlimit64(myself, unix.RLIMIT_FSIZE, rlimit3, nil)
		if err == nil {
			t.Errorf("Test #3 failed. Error expected.")
		} else {
			t.Logf("Test #3 produced error: %s", err.Error());
		}
	} else {
		t.Logf("Test #3 must be run without root permittion.")
	}
}
<commit_msg>[sandbox] Add prlimit64 test with invalid argument<commit_after>package sandbox

import (
	"golang.org/x/sys/unix"
	"testing"
)

// Test prlimit64
func Test_prlimit64(t *testing.T) {
	rlimit1 := &unix.Rlimit{Cur:131072, Max:1048576}
	myself := unix.Getpid()
	err := prlimit64(myself, unix.RLIMIT_FSIZE, rlimit1, nil)
	if err != nil {
		t.Errorf("Test #1 failed. Error: %s", err.Error())
	}

	// Use unix.Getrlimit to check result
	rlimit1Copy1 := &unix.Rlimit{}
	err = unix.Getrlimit(unix.RLIMIT_FSIZE, rlimit1Copy1);
	if err != nil {
		t.Fatalf("Getrlimit failed: %s", err.Error());
	}
	if (*rlimit1 != *rlimit1Copy1) {
		t.Errorf("Test #1 failed. NewLimit has not been set.")
	}

	rlimit2 := &unix.Rlimit{Cur:262144, Max:524288}
	rlimit1Copy2 := &unix.Rlimit{}
	err = prlimit64(myself, unix.RLIMIT_FSIZE, rlimit2, rlimit1Copy2)
	if err != nil {
		t.Errorf("Test #2 failed. Error: %s", err.Error());
	} else if *rlimit1Copy2 != *rlimit1 {
		t.Errorf("Test #2 failed. OldLimit contains wrong value.");
	}

	uid := unix.Getuid()
	if uid != 0 {
		rlimit3 := &unix.Rlimit{Cur:262144, Max:1048576}
		err = prlimit64(myself, unix.RLIMIT_FSIZE, rlimit3, nil)
		if err == nil {
			t.Errorf("Test #3 failed. Error expected.")
		} else {
			t.Logf("Test #3 produced error: %s", err.Error());
		}
	} else {
		t.Logf("Test #3 must be run without root permittion.")
	}

	// Test with a strange argument
	err = prlimit64(myself, 233333, rlimit1, nil)
	if err == nil {
		t.Errorf("Test #4 failed. Error expected.")
	} else {
		t.Logf("Test #4 produced error: %s", err.Error());
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Pikkpoiss
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"../lib/twodee"
	"fmt"
	"image/color"
	"io/ioutil"
	"strconv"
	"time"
)

type HudLayer struct {
	textCamera     *twodee.Camera
	spriteCamera   *twodee.Camera
	textRenderer   *twodee.TextRenderer
	regfont        *twodee.FontFace
	spriteSheet    *twodee.Spritesheet
	spriteTexture  *twodee.Texture
	spriteRenderer *twodee.SpriteRenderer
	state          *State
	app            *Application
}

func NewHudLayer(state *State, grid *Grid, app *Application) (layer *HudLayer, err error) {
	var (
		regfont      *twodee.FontFace
		bg           = color.Transparent
		font         = "resources/fonts/Prototype.ttf"
		textCamera   *twodee.Camera
		spriteCamera *twodee.Camera
	)
	if textCamera, err = twodee.NewCamera(
		twodee.Rect(0, 0, ScreenWidth, ScreenHeight),
		twodee.Rect(0, 0, ScreenWidth, ScreenHeight),
	); err != nil {
		return
	}
	if spriteCamera, err = twodee.NewCamera(
		twodee.Rect(0, 0, float32(grid.Width()), float32(grid.Height())),
		twodee.Rect(0, 0, ScreenWidth, ScreenHeight),
	); err != nil {
		return
	}
	if regfont, err = twodee.NewFontFace(font, 32, color.RGBA{100, 100, 100, 255}, bg); err != nil {
		return
	}
	layer = &HudLayer{
		textCamera:   textCamera,
		spriteCamera: spriteCamera,
		regfont:      regfont,
		state:        state,
		app:          app,
	}
	err = layer.Reset()
	return
}

func (h *HudLayer) Delete() {
	h.textRenderer.Delete()
	h.spriteRenderer.Delete()
}

func (h *HudLayer) Render() {
	hudItems := []string{strconv.Itoa(h.state.Rating), "YARPS", strconv.Itoa(h.state.Geld), "GELD"}

	var (
		configs   = []twodee.SpriteConfig{}
		textcache *twodee.TextCache
		texture   *twodee.Texture
		xText     = h.textCamera.WorldBounds.Max.X()
		yText     = h.textCamera.WorldBounds.Max.Y()
		ySprite   = h.spriteCamera.WorldBounds.Max.Y()
		blockNum  float32
	)

	h.textRenderer.Bind()
	textcache = twodee.NewTextCache(h.regfont)

	// Render text for toolbar
	textcache.SetText("Toolbar")
	texture = textcache.Texture
	if texture != nil {
		h.textRenderer.Draw(texture, 5, yText-float32(texture.Height))
	}

	// Render text for 'Geld', <Geld Amount>, 'Rating', <Rating Amount>
	for i, elem := range hudItems {
		textcache.SetText(elem)
		texture = textcache.Texture
		if texture != nil {
			if i%2 == 0 {
				xText = xText - (float32(texture.Width) + 10)
			} else {
				xText = xText - float32(texture.Width)
			}
			h.textRenderer.Draw(texture, xText, yText-float32(texture.Height))
		}
	}
	h.textRenderer.Unbind()

	// Render toolbar for selecting blocks to place
	h.spriteTexture.Bind()
	for blockNum = 0; blockNum < 3; blockNum++ {
		configs = append(configs, h.toolbarSpriteConfig(h.spriteSheet, blockNum, ySprite))
	}
	h.spriteRenderer.Draw(configs)
	h.spriteTexture.Unbind()

}

func (h *HudLayer) HandleEvent(evt twodee.Event) bool {
	return true
}

func (h *HudLayer) Reset() (err error) {
	if h.textRenderer != nil {
		h.textRenderer.Delete()
	}
	if h.textRenderer, err = twodee.NewTextRenderer(h.textCamera); err != nil {
		return
	}
	if h.spriteRenderer, err = twodee.NewSpriteRenderer(h.spriteCamera); err != nil {
		return
	}
	if err = h.loadSpritesheet(); err != nil {
		return
	}
	return
}

func (h *HudLayer) Update(elapsed time.Duration) {

}

func (h *HudLayer) loadSpritesheet() (err error) {
	var (
		data []byte
	)
	if data, err = ioutil.ReadFile("resources/spritesheet.json"); err != nil {
		return
	}
	if h.spriteSheet, err = twodee.ParseTexturePackerJSONArrayString(
		string(data),
		PxPerUnit,
	); err != nil {
		return
	}
	if h.spriteTexture, err = twodee.LoadTexture(
		"resources/"+h.spriteSheet.TexturePath,
		twodee.Nearest,
	); err != nil {
		return
	}
	return
}

func (h *HudLayer) toolbarSpriteConfig(sheet *twodee.Spritesheet, block float32, y float32) twodee.SpriteConfig {
	var frame *twodee.SpritesheetFrame
	frame = sheet.GetFrame(fmt.Sprintf("numbered_squares_%02v", block))
	xPosition := (frame.Width / 2.0) + 0.75
	yPosition := y - (block * (frame.Height + 0.5)) - 2.5
	return twodee.SpriteConfig{
		View: twodee.ModelViewConfig{
			xPosition, yPosition, 0,
			0, 0, 0,
			1.0, 1.0, 1.0,
		},
		Frame: frame.Frame,
	}
}
<commit_msg>Add keyboard shortcut numbers next to sprites in toolbar.<commit_after>// Copyright 2015 Pikkpoiss
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"../lib/twodee"
	"fmt"
	"image/color"
	"io/ioutil"
	"strconv"
	"time"
)

type HudLayer struct {
	textCamera     *twodee.Camera
	spriteCamera   *twodee.Camera
	textRenderer   *twodee.TextRenderer
	regfont        *twodee.FontFace
	spriteSheet    *twodee.Spritesheet
	spriteTexture  *twodee.Texture
	spriteRenderer *twodee.SpriteRenderer
	state          *State
	app            *Application
}

func NewHudLayer(state *State, grid *Grid, app *Application) (layer *HudLayer, err error) {
	var (
		regfont      *twodee.FontFace
		bg           = color.Transparent
		font         = "resources/fonts/Prototype.ttf"
		textCamera   *twodee.Camera
		spriteCamera *twodee.Camera
	)
	if textCamera, err = twodee.NewCamera(
		twodee.Rect(0, 0, ScreenWidth, ScreenHeight),
		twodee.Rect(0, 0, ScreenWidth, ScreenHeight),
	); err != nil {
		return
	}
	if spriteCamera, err = twodee.NewCamera(
		twodee.Rect(0, 0, float32(grid.Width()), float32(grid.Height())),
		twodee.Rect(0, 0, ScreenWidth, ScreenHeight),
	); err != nil {
		return
	}
	if regfont, err = twodee.NewFontFace(font, 32, color.RGBA{0, 0, 0, 200}, bg); err != nil {
		return
	}
	layer = &HudLayer{
		textCamera:   textCamera,
		spriteCamera: spriteCamera,
		regfont:      regfont,
		state:        state,
		app:          app,
	}
	err = layer.Reset()
	return
}

func (h *HudLayer) Delete() {
	h.textRenderer.Delete()
	h.spriteRenderer.Delete()
}

func (h *HudLayer) Render() {
	hudItems := []string{strconv.Itoa(h.state.Rating), "RATING", strconv.Itoa(h.state.Geld), "GELD"}

	var (
		configs         = []twodee.SpriteConfig{}
		textcache       *twodee.TextCache
		texture         *twodee.Texture
		xText           = h.textCamera.WorldBounds.Max.X()
		yText           = h.textCamera.WorldBounds.Max.Y()
		ySprite         = h.spriteCamera.WorldBounds.Max.Y()
		verticalSpacing = 90
		blockNum        float32
	)

	h.textRenderer.Bind()
	textcache = twodee.NewTextCache(h.regfont)

	// Render text for toolbar
	textcache.SetText("Toolbar")
	texture = textcache.Texture
	if texture != nil {
		h.textRenderer.Draw(texture, 5, yText-float32(texture.Height))
	}

	// Render text for 'Geld', <Geld Amount>, 'Rating', <Rating Amount>
	for i, elem := range hudItems {
		textcache.SetText(elem)
		texture = textcache.Texture
		if texture != nil {
			if i%2 == 0 {
				xText = xText - (float32(texture.Width) + 10)
			} else {
				xText = xText - float32(texture.Width)
			}
			h.textRenderer.Draw(texture, xText, yText-float32(texture.Height))
		}
	}

	// Render text for each of the available blocks to purchase
	for i := 1; i < 4; i++ {
		textcache.SetText(strconv.Itoa(i))
		texture = textcache.Texture
		if texture != nil {
			h.textRenderer.Draw(texture, 5, yText-float32(verticalSpacing*i))
		}
	}

	h.textRenderer.Unbind()

	// Render toolbar for selecting blocks to place
	h.spriteTexture.Bind()
	for blockNum = 0; blockNum < 3; blockNum++ {
		configs = append(configs, h.toolbarSpriteConfig(h.spriteSheet, blockNum, ySprite))
	}
	h.spriteRenderer.Draw(configs)
	h.spriteTexture.Unbind()

}

func (h *HudLayer) HandleEvent(evt twodee.Event) bool {
	return true
}

func (h *HudLayer) Reset() (err error) {
	if h.textRenderer != nil {
		h.textRenderer.Delete()
	}
	if h.textRenderer, err = twodee.NewTextRenderer(h.textCamera); err != nil {
		return
	}
	if h.spriteRenderer, err = twodee.NewSpriteRenderer(h.spriteCamera); err != nil {
		return
	}
	if err = h.loadSpritesheet(); err != nil {
		return
	}
	return
}

func (h *HudLayer) Update(elapsed time.Duration) {

}

func (h *HudLayer) loadSpritesheet() (err error) {
	var (
		data []byte
	)
	if data, err = ioutil.ReadFile("resources/spritesheet.json"); err != nil {
		return
	}
	if h.spriteSheet, err = twodee.ParseTexturePackerJSONArrayString(
		string(data),
		PxPerUnit,
	); err != nil {
		return
	}
	if h.spriteTexture, err = twodee.LoadTexture(
		"resources/"+h.spriteSheet.TexturePath,
		twodee.Nearest,
	); err != nil {
		return
	}
	return
}

func (h *HudLayer) toolbarSpriteConfig(sheet *twodee.Spritesheet, block float32, y float32) twodee.SpriteConfig {
	var toolbarSpriteSpacing float32 = 0.8
	var toolbarSpriteVerticalOffset float32 = 2.4
	var frame *twodee.SpritesheetFrame
	frame = sheet.GetFrame(fmt.Sprintf("numbered_squares_%02v", block))
	xPosition := (frame.Width / 2.0) + 1
	yPosition := y - (block * (frame.Height + toolbarSpriteSpacing)) - toolbarSpriteVerticalOffset
	return twodee.SpriteConfig{
		View: twodee.ModelViewConfig{
			xPosition, yPosition, 0,
			0, 0, 0,
			1.0, 1.0, 1.0,
		},
		Frame: frame.Frame,
	}
}
<|endoftext|>


<commit_before>package authdatamanager

import (
	"github.com/lfq7413/tomato/errs"
	"github.com/lfq7413/tomato/types"
	"github.com/lfq7413/tomato/utils"
)

type google struct{}

func (a google) ValidateAuthData(authData types.M, options types.M) error {
	var err error
	if utils.S(authData["id_token"]) != "" {
		err = a.validateIDToken(utils.S(authData["id"]), utils.S(authData["id_token"]))
	} else {
		err = a.validateAuthToken(utils.S(authData["id"]), utils.S(authData["access_token"]))
		if err != nil {
			err = a.validateIDToken(utils.S(authData["id"]), utils.S(authData["access_token"]))
		}
	}
	return err
}

func (a google) validateIDToken(id, token string) error {
	host := "https://www.googleapis.com/oauth2/v3/"
	path := "tokeninfo?id_token=" + token
	data, err := request(host+path, nil)
	if err != nil {
		return errs.E(errs.ObjectNotFound, "Failed to validate this access token with Google.")
	}
	if data["sub"] != nil && utils.S(data["sub"]) == id {
		return nil
	}
	return errs.E(errs.ObjectNotFound, "Google auth is invalid for this user.")
}

func (a google) validateAuthToken(id, token string) error {
	host := "https://www.googleapis.com/oauth2/v3/"
	path := "tokeninfo?access_token=" + token
	data, err := request(host+path, nil)
	if err != nil {
		return errs.E(errs.ObjectNotFound, "Failed to validate this access token with Google.")
	}
	if data["user_id"] != nil && utils.S(data["user_id"]) == id {
		return nil
	}
	return errs.E(errs.ObjectNotFound, "Google auth is invalid for this user.")
}
<commit_msg>验证 sub 或 user_id<commit_after>package authdatamanager

import (
	"github.com/lfq7413/tomato/errs"
	"github.com/lfq7413/tomato/types"
	"github.com/lfq7413/tomato/utils"
)

type google struct{}

func (a google) ValidateAuthData(authData types.M, options types.M) error {
	var err error
	if utils.S(authData["id_token"]) != "" {
		err = a.validateIDToken(utils.S(authData["id"]), utils.S(authData["id_token"]))
	} else {
		err = a.validateAuthToken(utils.S(authData["id"]), utils.S(authData["access_token"]))
		if err != nil {
			err = a.validateIDToken(utils.S(authData["id"]), utils.S(authData["access_token"]))
		}
	}
	return err
}

func (a google) validateIDToken(id, token string) error {
	host := "https://www.googleapis.com/oauth2/v3/"
	path := "tokeninfo?id_token=" + token
	data, err := request(host+path, nil)
	if err != nil {
		return errs.E(errs.ObjectNotFound, "Failed to validate this access token with Google.")
	}
	if data != nil && (utils.S(data["sub"]) == id || utils.S(data["user_id"]) == id) {
		return nil
	}
	return errs.E(errs.ObjectNotFound, "Google auth is invalid for this user.")
}

func (a google) validateAuthToken(id, token string) error {
	host := "https://www.googleapis.com/oauth2/v3/"
	path := "tokeninfo?access_token=" + token
	data, err := request(host+path, nil)
	if err != nil {
		return errs.E(errs.ObjectNotFound, "Failed to validate this access token with Google.")
	}
	if data != nil && (utils.S(data["sub"]) == id || utils.S(data["user_id"]) == id) {
		return nil
	}
	return errs.E(errs.ObjectNotFound, "Google auth is invalid for this user.")
}
<|endoftext|>


<commit_before>package api

import (
	"github.com/denkhaus/logging"
	"github.com/juju/errors"
	deadlock "github.com/sasha-s/go-deadlock"
	"github.com/tevino/abool"
)

type ClientProvider interface {
	OnError(fn ErrorFunc)
	Connect() error
	OnNotify(subscriberID int, fn NotifyFunc) error
	CallAPI(apiID int, method string, args ...interface{}) (interface{}, error)
	Close() error
}

type SimpleClientProvider struct {
	WebsocketClient
	api BitsharesAPI
}

func NewSimpleClientProvider(endpointURL string, api BitsharesAPI) ClientProvider {
	wsc := NewWebsocketClient(endpointURL)
	sim := SimpleClientProvider{
		api:             api,
		WebsocketClient: wsc,
	}

	return &sim
}

func (p *SimpleClientProvider) CallAPI(apiID int, method string, args ...interface{}) (interface{}, error) {
	if !p.WebsocketClient.IsConnected() {
		if err := p.api.Connect(); err != nil {
			return nil, errors.Annotate(err, "Connect [api]")
		}
	}

	return p.WebsocketClient.CallAPI(apiID, method, args...)
}

type BestNodeClientProvider struct {
	WebsocketClient
	mu          deadlock.RWMutex
	nodeChanged *abool.AtomicBool
	api         BitsharesAPI
	tester      LatencyTester
}

func NewBestNodeClientProvider(endpointURL string, api BitsharesAPI) (ClientProvider, error) {
	tester, err := NewLatencyTester(endpointURL)
	if err != nil {
		return nil, errors.Annotate(err, "NewLatencyTester")
	}

	pr := &BestNodeClientProvider{
		api:             api,
		tester:          tester,
		nodeChanged:     abool.NewBool(false),
		WebsocketClient: tester.TopNodeClient(),
	}

	tester.OnTopNodeChanged(pr.onTopNodeChanged)
	tester.Start()

	return pr, nil
}

func (p *BestNodeClientProvider) onTopNodeChanged(newEndpoint string) error {
	logging.Debugf("change top node client -> %s\n", newEndpoint)
	p.nodeChanged.Set()
	return nil
}

func (p *BestNodeClientProvider) renewClient() error {
	p.mu.Lock()
	defer p.mu.Unlock()

	if p.WebsocketClient.IsConnected() {
		logging.Debug("close [client]")
		if err := p.WebsocketClient.Close(); err != nil {
			return errors.Annotate(err, "Close [client]")
		}
	}

	p.WebsocketClient = p.tester.TopNodeClient()
	return nil
}

func (p *BestNodeClientProvider) handleReconnect() error {
	if err := p.renewClient(); err != nil {
		return errors.Annotate(err, "renewClient")
	}

	logging.Debug("reconnect api")
	if err := p.api.Connect(); err != nil {
		return errors.Annotate(err, "Connect [api]")
	}

	return nil
}

func (p *BestNodeClientProvider) CallAPI(apiID int, method string, args ...interface{}) (interface{}, error) {
	if p.nodeChanged.IsSet() {
		p.nodeChanged.UnSet()
		if err := p.handleReconnect(); err != nil {
			return nil, errors.Annotate(err, "handleReconnect")
		}
	}

	p.mu.RLock()
	defer p.mu.RUnlock()
	return p.WebsocketClient.CallAPI(apiID, method, args...)
}

func (p *BestNodeClientProvider) Close() error {
	p.mu.Lock()
	defer p.mu.Unlock()

	logging.Debug("close provider")
	if p.WebsocketClient.IsConnected() {
		logging.Debug("close [client]")
		if err := p.WebsocketClient.Close(); err != nil {
			return errors.Annotate(err, "Close [client]")
		}
	}

	logging.Debug("close [tester]")
	if err := p.tester.Close(); err != nil {
		return errors.Annotate(err, "Close [tester]")
	}

	return nil
}
<commit_msg>change node changed output<commit_after>package api

import (
	"github.com/denkhaus/logging"
	"github.com/juju/errors"
	deadlock "github.com/sasha-s/go-deadlock"
	"github.com/tevino/abool"
)

type ClientProvider interface {
	OnError(fn ErrorFunc)
	Connect() error
	OnNotify(subscriberID int, fn NotifyFunc) error
	CallAPI(apiID int, method string, args ...interface{}) (interface{}, error)
	Close() error
}

type SimpleClientProvider struct {
	WebsocketClient
	api BitsharesAPI
}

func NewSimpleClientProvider(endpointURL string, api BitsharesAPI) ClientProvider {
	wsc := NewWebsocketClient(endpointURL)
	sim := SimpleClientProvider{
		api:             api,
		WebsocketClient: wsc,
	}

	return &sim
}

func (p *SimpleClientProvider) CallAPI(apiID int, method string, args ...interface{}) (interface{}, error) {
	if !p.WebsocketClient.IsConnected() {
		if err := p.api.Connect(); err != nil {
			return nil, errors.Annotate(err, "Connect [api]")
		}
	}

	return p.WebsocketClient.CallAPI(apiID, method, args...)
}

type BestNodeClientProvider struct {
	WebsocketClient
	mu          deadlock.RWMutex
	nodeChanged *abool.AtomicBool
	api         BitsharesAPI
	tester      LatencyTester
}

func NewBestNodeClientProvider(endpointURL string, api BitsharesAPI) (ClientProvider, error) {
	tester, err := NewLatencyTester(endpointURL)
	if err != nil {
		return nil, errors.Annotate(err, "NewLatencyTester")
	}

	pr := &BestNodeClientProvider{
		api:             api,
		tester:          tester,
		nodeChanged:     abool.NewBool(false),
		WebsocketClient: tester.TopNodeClient(),
	}

	tester.OnTopNodeChanged(pr.onTopNodeChanged)
	tester.Start()

	return pr, nil
}

func (p *BestNodeClientProvider) onTopNodeChanged(newEndpoint string) error {
	logging.Infof("change top node client -> %s\n", newEndpoint)
	p.nodeChanged.Set()
	return nil
}

func (p *BestNodeClientProvider) renewClient() error {
	p.mu.Lock()
	defer p.mu.Unlock()

	if p.WebsocketClient.IsConnected() {
		logging.Debug("close [client]")
		if err := p.WebsocketClient.Close(); err != nil {
			return errors.Annotate(err, "Close [client]")
		}
	}

	p.WebsocketClient = p.tester.TopNodeClient()
	return nil
}

func (p *BestNodeClientProvider) handleReconnect() error {
	if err := p.renewClient(); err != nil {
		return errors.Annotate(err, "renewClient")
	}

	logging.Debug("reconnect api")
	if err := p.api.Connect(); err != nil {
		return errors.Annotate(err, "Connect [api]")
	}

	return nil
}

func (p *BestNodeClientProvider) CallAPI(apiID int, method string, args ...interface{}) (interface{}, error) {
	if p.nodeChanged.IsSet() {
		p.nodeChanged.UnSet()
		if err := p.handleReconnect(); err != nil {
			return nil, errors.Annotate(err, "handleReconnect")
		}
	}

	p.mu.RLock()
	defer p.mu.RUnlock()
	return p.WebsocketClient.CallAPI(apiID, method, args...)
}

func (p *BestNodeClientProvider) Close() error {
	p.mu.Lock()
	defer p.mu.Unlock()

	logging.Debug("close provider")
	if p.WebsocketClient.IsConnected() {
		logging.Debug("close [client]")
		if err := p.WebsocketClient.Close(); err != nil {
			return errors.Annotate(err, "Close [client]")
		}
	}

	logging.Debug("close [tester]")
	if err := p.tester.Close(); err != nil {
		return errors.Annotate(err, "Close [tester]")
	}

	return nil
}
<|endoftext|>


<commit_before>package airlift

import (
	"crypto/rand"
	"encoding/hex"
	"errors"
	"strconv"
	"strings"
	"time"

	r "github.com/dancannon/gorethink"
)

// Paper represents a user uploaded paper
type Paper struct {
	ID            string    `gorethink:"id"`
	Title         string    `gorethink:"title"`
	Year          int       `gorethink:"year"`
	Subject       string    `gorethink:"subject"`
	Author        string    `gorethink:"author"`
	Public        bool      `gorethink:"public"`
	Completed     []string  `gorethink:"completed"`
	Uploader      string    `gorethink:"uploader"`
	SolutionsSize uint64    `gorethink:"solutions_size,omitempty"`
	SourceSize    uint64    `gorethink:"source_size,omitempty"`
	QuestionsSize uint64    `gorethink:"questions_size,omitempty"`
	HasCompleted  bool      `gorethink:"has_completed,omitempty"`
	NumCompleted  int       `gorethink:"num_completed,omitempty"`
	UpdatedTime   time.Time `gorethink:"updated_time,omitempty"`
	UploadTime    time.Time `gorethink:"upload_time,omitempty"`
}

type updatePaper struct {
	ID            string    `gorethink:"id"`
	Title         string    `gorethink:"title"`
	Year          int       `gorethink:"year"`
	Subject       string    `gorethink:"subject"`
	Author        string    `gorethink:"author"`
	Public        bool      `gorethink:"public"`
	Uploader      string    `gorethink:"uploader"`
	Completed     []string  `gorethink:"completed,omitempty"`
	SolutionsSize uint64    `gorethink:"solutions_size,omitempty"`
	SourceSize    uint64    `gorethink:"source_size,omitempty"`
	QuestionsSize uint64    `gorethink:"questions_size,omitempty"`
	UpdatedTime   time.Time `gorethink:"updated_time,omitempty"`
}

// FullPaper represents a user uploaded paper with additional data
type FullPaper struct {
	Paper
	UploaderName string `gorethink:"uploader_name"`
	SubjectName  string `gorethink:"subject_name"`
}

var rowFullPaperTitle = func(row r.Term) interface{} {
	return row.Field("year").CoerceTo("string").
		Add(" ", row.Field("subject"), " ", row.Field("title"))
}

// GetPaper returns a paper given its ID
func GetPaper(id string) (Paper, error) {
	var paper Paper
	err := getOne(r.Table("papers").Get(id).Default(map[string]string{}), &paper)
	if err != nil {
		return Paper{}, err
	}

	return paper, nil
}

func idFromPaper(paper Paper) (string, error) {
	title := strings.ToLower(strings.Replace(paper.Title, " ", "_", -1))
	title = strconv.Itoa(paper.Year) + "_" + paper.Subject + "_" + title
	nonce := make([]byte, 2)
	_, err := rand.Read(nonce)
	if err != nil {
		return "", err
	}

	return title + "_" + hex.EncodeToString(nonce), nil
}

// UpdatePaper updates the information of a paper
func UpdatePaper(paper Paper) error {
	updatedPaper := updatePaper{
		ID:            paper.ID,
		Title:         paper.Title,
		Year:          paper.Year,
		Subject:       paper.Subject,
		Author:        paper.Author,
		Uploader:      paper.Uploader,
		Public:        paper.Public,
		Completed:     paper.Completed,
		SolutionsSize: paper.SolutionsSize,
		SourceSize:    paper.SourceSize,
		QuestionsSize: paper.QuestionsSize,
		UpdatedTime:   time.Now(),
	}

	result, err := r.Table("papers").Get(paper.ID).Update(updatedPaper).RunWrite(session)
	if err != nil {
		return err
	}

	if result.Errors > 0 {
		return errors.New("airlift: " + result.FirstError)
	}

	return nil
}

// DeletePaper deletes a paper
func DeletePaper(id string) error {
	result, err := r.Table("papers").Get(id).Delete().RunWrite(session)
	if err != nil {
		return err
	}

	if result.Errors > 0 {
		return errors.New("airlift: " + result.FirstError)
	}

	return nil
}

// NewPaper creates a new paper and returns a unique human friendly ID.
// Data must be populated using UpdatePaper.
func NewPaper(title, subject string, year int) (string, error) {
	var paper Paper

	paper.Title = title
	paper.UploadTime = time.Now()
	paper.Subject = subject
	paper.Year = year

	for {
		var err error
		paper.ID, err = idFromPaper(paper)
		if err != nil {
			return "", err
		}

		result, err := r.Table("papers").Insert(paper).RunWrite(session)
		if err != nil {
			return "", err
		}

		if result.Errors == 0 {
			break
		}
	}

	return paper.ID, nil
}

// SetPaperCompleted marks a practice paper as completed for a user.
func SetPaperCompleted(id, username string, completed bool) error {
	var query r.Term
	if completed {
		query = r.Table("papers").Get(id).Update(map[string]interface{}{
			"completed": r.Row.Field("completed").SetInsert(username),
		})
	} else {
		query = r.Table("papers").Get(id).Update(map[string]interface{}{
			"completed": r.Row.Field("completed").SetDifference([]string{username}),
		})
	}

	result, err := query.RunWrite(session)
	if err != nil {
		return err
	}

	if result.Errors > 0 {
		return errors.New("airlift: " + result.FirstError)
	}

	return nil
}

// GetCompletedPapers returns the papers completed by a user.
func GetCompletedPapers(username string) ([]FullPaper, error) {
	var papers []FullPaper
	err := getAll(r.Table("papers").
		GetAllByIndex("completed", username).
		OrderBy(r.Desc(rowFullPaperTitle)).
		EqJoin("uploader", r.Table("users"), r.EqJoinOpts{Index: "uploader"}).
		EqJoin(r.Row.Field("left").Field("subject"), r.Table("subjects"), r.EqJoinOpts{Index: "id"}).
		Map(func(row r.Term) interface{} {
			return row.Field("left").Field("left").Merge(map[string]interface{}{
				"has_completed": row.Field("left").Field("left").Field("completed").Contains(username),
				"uploader_name": row.Field("left").Field("right").Field("name"),
				"subject_name":  row.Field("right").Field("name"),
			})
		}),
		&papers)
	return papers, err
}

// GetUploadedPapers returns the papers uploaded by a user.
func GetUploadedPapers(username string) ([]FullPaper, error) {
	var papers []FullPaper
	err := getAll(r.Table("papers").
		GetAllByIndex("uploader", username).
		OrderBy(r.Desc(rowFullPaperTitle)).
		EqJoin("uploader", r.Table("users"), r.EqJoinOpts{Index: "uploader"}).
		EqJoin(r.Row.Field("left").Field("subject"), r.Table("subjects"), r.EqJoinOpts{Index: "id"}).
		Map(func(row r.Term) interface{} {
			return row.Field("left").Field("left").Merge(map[string]interface{}{
				"has_completed": row.Field("left").Field("left").Field("completed").Contains(username),
				"uploader_name": row.Field("left").Field("right").Field("name"),
				"subject_name":  row.Field("right").Field("name"),
			})
		}),
		&papers)
	return papers, err
}

// GetFullPaper returns the paper with additional data uploaded by a user.
func GetFullPaper(id, username string) (FullPaper, error) {
	var paper FullPaper
	err := getOne(r.Table("papers").Get(id).
		Merge(func(row r.Term) interface{} {
			return map[string]interface{}{
				"has_completed": row.Field("completed").Contains(username),
				"uploader_name": r.Table("users").Get(row.Field("uploader")).
					Field("name").Default("Unknown"),
				"subject_name": r.Table("subjects").Get(row.Field("subject")).
					Field("name").Default("Unknown"),
			}
		}), &paper)
	return paper, err
}
<commit_msg>Fix index used for query<commit_after>package airlift

import (
	"crypto/rand"
	"encoding/hex"
	"errors"
	"strconv"
	"strings"
	"time"

	r "github.com/dancannon/gorethink"
)

// Paper represents a user uploaded paper
type Paper struct {
	ID            string    `gorethink:"id"`
	Title         string    `gorethink:"title"`
	Year          int       `gorethink:"year"`
	Subject       string    `gorethink:"subject"`
	Author        string    `gorethink:"author"`
	Public        bool      `gorethink:"public"`
	Completed     []string  `gorethink:"completed"`
	Uploader      string    `gorethink:"uploader"`
	SolutionsSize uint64    `gorethink:"solutions_size,omitempty"`
	SourceSize    uint64    `gorethink:"source_size,omitempty"`
	QuestionsSize uint64    `gorethink:"questions_size,omitempty"`
	HasCompleted  bool      `gorethink:"has_completed,omitempty"`
	NumCompleted  int       `gorethink:"num_completed,omitempty"`
	UpdatedTime   time.Time `gorethink:"updated_time,omitempty"`
	UploadTime    time.Time `gorethink:"upload_time,omitempty"`
}

type updatePaper struct {
	ID            string    `gorethink:"id"`
	Title         string    `gorethink:"title"`
	Year          int       `gorethink:"year"`
	Subject       string    `gorethink:"subject"`
	Author        string    `gorethink:"author"`
	Public        bool      `gorethink:"public"`
	Uploader      string    `gorethink:"uploader"`
	Completed     []string  `gorethink:"completed,omitempty"`
	SolutionsSize uint64    `gorethink:"solutions_size,omitempty"`
	SourceSize    uint64    `gorethink:"source_size,omitempty"`
	QuestionsSize uint64    `gorethink:"questions_size,omitempty"`
	UpdatedTime   time.Time `gorethink:"updated_time,omitempty"`
}

// FullPaper represents a user uploaded paper with additional data
type FullPaper struct {
	Paper
	UploaderName string `gorethink:"uploader_name"`
	SubjectName  string `gorethink:"subject_name"`
}

var rowFullPaperTitle = func(row r.Term) interface{} {
	return row.Field("year").CoerceTo("string").
		Add(" ", row.Field("subject"), " ", row.Field("title"))
}

// GetPaper returns a paper given its ID
func GetPaper(id string) (Paper, error) {
	var paper Paper
	err := getOne(r.Table("papers").Get(id).Default(map[string]string{}), &paper)
	if err != nil {
		return Paper{}, err
	}

	return paper, nil
}

func idFromPaper(paper Paper) (string, error) {
	title := strings.ToLower(strings.Replace(paper.Title, " ", "_", -1))
	title = strconv.Itoa(paper.Year) + "_" + paper.Subject + "_" + title
	nonce := make([]byte, 2)
	_, err := rand.Read(nonce)
	if err != nil {
		return "", err
	}

	return title + "_" + hex.EncodeToString(nonce), nil
}

// UpdatePaper updates the information of a paper
func UpdatePaper(paper Paper) error {
	updatedPaper := updatePaper{
		ID:            paper.ID,
		Title:         paper.Title,
		Year:          paper.Year,
		Subject:       paper.Subject,
		Author:        paper.Author,
		Uploader:      paper.Uploader,
		Public:        paper.Public,
		Completed:     paper.Completed,
		SolutionsSize: paper.SolutionsSize,
		SourceSize:    paper.SourceSize,
		QuestionsSize: paper.QuestionsSize,
		UpdatedTime:   time.Now(),
	}

	result, err := r.Table("papers").Get(paper.ID).Update(updatedPaper).RunWrite(session)
	if err != nil {
		return err
	}

	if result.Errors > 0 {
		return errors.New("airlift: " + result.FirstError)
	}

	return nil
}

// DeletePaper deletes a paper
func DeletePaper(id string) error {
	result, err := r.Table("papers").Get(id).Delete().RunWrite(session)
	if err != nil {
		return err
	}

	if result.Errors > 0 {
		return errors.New("airlift: " + result.FirstError)
	}

	return nil
}

// NewPaper creates a new paper and returns a unique human friendly ID.
// Data must be populated using UpdatePaper.
func NewPaper(title, subject string, year int) (string, error) {
	var paper Paper

	paper.Title = title
	paper.UploadTime = time.Now()
	paper.Subject = subject
	paper.Year = year

	for {
		var err error
		paper.ID, err = idFromPaper(paper)
		if err != nil {
			return "", err
		}

		result, err := r.Table("papers").Insert(paper).RunWrite(session)
		if err != nil {
			return "", err
		}

		if result.Errors == 0 {
			break
		}
	}

	return paper.ID, nil
}

// SetPaperCompleted marks a practice paper as completed for a user.
func SetPaperCompleted(id, username string, completed bool) error {
	var query r.Term
	if completed {
		query = r.Table("papers").Get(id).Update(map[string]interface{}{
			"completed": r.Row.Field("completed").SetInsert(username),
		})
	} else {
		query = r.Table("papers").Get(id).Update(map[string]interface{}{
			"completed": r.Row.Field("completed").SetDifference([]string{username}),
		})
	}

	result, err := query.RunWrite(session)
	if err != nil {
		return err
	}

	if result.Errors > 0 {
		return errors.New("airlift: " + result.FirstError)
	}

	return nil
}

// GetCompletedPapers returns the papers completed by a user.
func GetCompletedPapers(username string) ([]FullPaper, error) {
	var papers []FullPaper
	err := getAll(r.Table("papers").
		GetAllByIndex("completed", username).
		OrderBy(r.Desc(rowFullPaperTitle)).
		EqJoin("uploader", r.Table("users"), r.EqJoinOpts{Index: "username"}).
		EqJoin(r.Row.Field("left").Field("subject"), r.Table("subjects"), r.EqJoinOpts{Index: "id"}).
		Map(func(row r.Term) interface{} {
			return row.Field("left").Field("left").Merge(map[string]interface{}{
				"has_completed": row.Field("left").Field("left").Field("completed").Contains(username),
				"uploader_name": row.Field("left").Field("right").Field("name"),
				"subject_name":  row.Field("right").Field("name"),
			})
		}),
		&papers)
	return papers, err
}

// GetUploadedPapers returns the papers uploaded by a user.
func GetUploadedPapers(username string) ([]FullPaper, error) {
	var papers []FullPaper
	err := getAll(r.Table("papers").
		GetAllByIndex("uploader", username).
		OrderBy(r.Desc(rowFullPaperTitle)).
		EqJoin("uploader", r.Table("users"), r.EqJoinOpts{Index: "username"}).
		EqJoin(r.Row.Field("left").Field("subject"), r.Table("subjects"), r.EqJoinOpts{Index: "id"}).
		Map(func(row r.Term) interface{} {
			return row.Field("left").Field("left").Merge(map[string]interface{}{
				"has_completed": row.Field("left").Field("left").Field("completed").Contains(username),
				"uploader_name": row.Field("left").Field("right").Field("name"),
				"subject_name":  row.Field("right").Field("name"),
			})
		}),
		&papers)
	return papers, err
}

// GetFullPaper returns the paper with additional data uploaded by a user.
func GetFullPaper(id, username string) (FullPaper, error) {
	var paper FullPaper
	err := getOne(r.Table("papers").Get(id).
		Merge(func(row r.Term) interface{} {
			return map[string]interface{}{
				"has_completed": row.Field("completed").Contains(username),
				"uploader_name": r.Table("users").Get(row.Field("uploader")).
					Field("name").Default("Unknown"),
				"subject_name": r.Table("subjects").Get(row.Field("subject")).
					Field("name").Default("Unknown"),
			}
		}), &paper)
	return paper, err
}
<|endoftext|>


<commit_before>package restapi

import (
	"crypto/tls"
	"log"
	"net/http"
	"strings"

	errors "github.com/go-openapi/errors"
	runtime "github.com/go-openapi/runtime"
	middleware "github.com/go-openapi/runtime/middleware"
	"github.com/go-openapi/swag"
	"golang.org/x/net/context"

	"github.com/influxdata/mrfusion"
	"github.com/influxdata/mrfusion/dist"
	"github.com/influxdata/mrfusion/handlers"
	"github.com/influxdata/mrfusion/influx"
	"github.com/influxdata/mrfusion/mock"
	"github.com/influxdata/mrfusion/restapi/operations"
)

// This file is safe to edit. Once it exists it will not be overwritten

//go:generate swagger generate server --target .. --name  --spec ../swagger.yaml --with-context

var devFlags = struct {
	Develop bool `short:"d" long:"develop" description:"Run server in develop mode."`
}{}

var influxFlags = struct {
	Server string `short:"s" long:"server" description:"Full URL of InfluxDB server (http://localhost:8086)" env:"INFLUX_HOST"`
}{}

func configureFlags(api *operations.MrFusionAPI) {
	api.CommandLineOptionsGroups = []swag.CommandLineOptionsGroup{
		swag.CommandLineOptionsGroup{
			ShortDescription: "Develop Mode server",
			LongDescription:  "Server will use the ui/build directory directly.",
			Options:          &devFlags,
		},
		swag.CommandLineOptionsGroup{
			ShortDescription: "Default Time Series Backend",
			LongDescription:  "Specify the url of an InfxluDB server",
			Options:          &influxFlags,
		},
	}
}

func assets() mrfusion.Assets {
	if devFlags.Develop {
		return &dist.DebugAssets{
			Dir:     "ui/build",
			Default: "ui/build/index.html",
		}
	}
	return &dist.BindataAssets{
		Prefix:  "ui/build",
		Default: "index.html",
	}
}

func configureAPI(api *operations.MrFusionAPI) http.Handler {
	// configure the api here
	api.ServeError = errors.ServeError

	// Set your custom logger if needed. Default one is log.Printf
	// Expected interface func(string, ...interface{})
	//
	// Example:
	// s.api.Logger = log.Printf

	api.JSONConsumer = runtime.JSONConsumer()

	api.JSONProducer = runtime.JSONProducer()

	mockHandler := mock.NewHandler()

	api.DeleteDashboardsIDHandler = operations.DeleteDashboardsIDHandlerFunc(func(ctx context.Context, params operations.DeleteDashboardsIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .DeleteDashboardsID has not yet been implemented")
	})
	api.DeleteSourcesIDHandler = operations.DeleteSourcesIDHandlerFunc(func(ctx context.Context, params operations.DeleteSourcesIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .DeleteSourcesID has not yet been implemented")
	})
	api.DeleteSourcesIDRolesRoleIDHandler = operations.DeleteSourcesIDRolesRoleIDHandlerFunc(func(ctx context.Context, params operations.DeleteSourcesIDRolesRoleIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .DeleteSourcesIDRolesRoleID has not yet been implemented")
	})
	api.DeleteSourcesIDUsersUserIDExplorationsExplorationIDHandler = operations.DeleteSourcesIDUsersUserIDExplorationsExplorationIDHandlerFunc(func(ctx context.Context, params operations.DeleteSourcesIDUsersUserIDExplorationsExplorationIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .DeleteSourcesIDUsersUserIDExplorationsExplorationID has not yet been implemented")
	})
	api.DeleteSourcesIDUsersUserIDHandler = operations.DeleteSourcesIDUsersUserIDHandlerFunc(func(ctx context.Context, params operations.DeleteSourcesIDUsersUserIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .DeleteSourcesIDUsersUserID has not yet been implemented")
	})
	api.GetHandler = operations.GetHandlerFunc(func(ctx context.Context, params operations.GetParams) middleware.Responder {
		return middleware.NotImplemented("operation .Get has not yet been implemented")
	})
	api.GetDashboardsHandler = operations.GetDashboardsHandlerFunc(func(ctx context.Context, params operations.GetDashboardsParams) middleware.Responder {
		return middleware.NotImplemented("operation .GetDashboards has not yet been implemented")
	})
	api.GetDashboardsIDHandler = operations.GetDashboardsIDHandlerFunc(func(ctx context.Context, params operations.GetDashboardsIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .GetDashboardsID has not yet been implemented")
	})

	api.GetSourcesHandler = operations.GetSourcesHandlerFunc(mockHandler.Sources)
	api.GetSourcesIDHandler = operations.GetSourcesIDHandlerFunc(mockHandler.SourcesID)

	api.GetSourcesIDPermissionsHandler = operations.GetSourcesIDPermissionsHandlerFunc(func(ctx context.Context, params operations.GetSourcesIDPermissionsParams) middleware.Responder {
		return middleware.NotImplemented("operation .GetSourcesIDPermissions has not yet been implemented")
	})
	api.GetSourcesIDRolesHandler = operations.GetSourcesIDRolesHandlerFunc(func(ctx context.Context, params operations.GetSourcesIDRolesParams) middleware.Responder {
		return middleware.NotImplemented("operation .GetSourcesIDRoles has not yet been implemented")
	})
	api.GetSourcesIDRolesRoleIDHandler = operations.GetSourcesIDRolesRoleIDHandlerFunc(func(ctx context.Context, params operations.GetSourcesIDRolesRoleIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .GetSourcesIDRolesRoleID has not yet been implemented")
	})

	api.GetSourcesIDUsersUserIDExplorationsExplorationIDHandler = operations.GetSourcesIDUsersUserIDExplorationsExplorationIDHandlerFunc(mockHandler.Exploration)

	api.GetSourcesIDUsersHandler = operations.GetSourcesIDUsersHandlerFunc(func(ctx context.Context, params operations.GetSourcesIDUsersParams) middleware.Responder {
		return middleware.NotImplemented("operation .GetSourcesIDUsers has not yet been implemented")
	})
	api.GetSourcesIDUsersUserIDHandler = operations.GetSourcesIDUsersUserIDHandlerFunc(func(ctx context.Context, params operations.GetSourcesIDUsersUserIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .GetSourcesIDUsersUserID has not yet been implemented")
	})

	api.GetSourcesIDUsersUserIDExplorationsHandler = operations.GetSourcesIDUsersUserIDExplorationsHandlerFunc(mockHandler.Explorations)
	api.PatchSourcesIDHandler = operations.PatchSourcesIDHandlerFunc(func(ctx context.Context, params operations.PatchSourcesIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .PatchSourcesID has not yet been implemented")
	})
	api.PatchSourcesIDRolesRoleIDHandler = operations.PatchSourcesIDRolesRoleIDHandlerFunc(func(ctx context.Context, params operations.PatchSourcesIDRolesRoleIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .PatchSourcesIDRolesRoleID has not yet been implemented")
	})
	api.PatchSourcesIDUsersUserIDExplorationsExplorationIDHandler = operations.PatchSourcesIDUsersUserIDExplorationsExplorationIDHandlerFunc(func(ctx context.Context, params operations.PatchSourcesIDUsersUserIDExplorationsExplorationIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .PatchSourcesIDUsersUserIDExplorationsExplorationID has not yet been implemented")
	})
	api.PatchSourcesIDUsersUserIDHandler = operations.PatchSourcesIDUsersUserIDHandlerFunc(func(ctx context.Context, params operations.PatchSourcesIDUsersUserIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .PatchSourcesIDUsersUserID has not yet been implemented")
	})
	api.PostDashboardsHandler = operations.PostDashboardsHandlerFunc(func(ctx context.Context, params operations.PostDashboardsParams) middleware.Responder {
		return middleware.NotImplemented("operation .PostDashboards has not yet been implemented")
	})
	api.PostSourcesHandler = operations.PostSourcesHandlerFunc(mockHandler.NewSource)

	if len(influxFlags.Server) > 0 {
		c, err := influx.NewClient(influxFlags.Server)
		if err != nil {
			panic(err)
		}
		h := handlers.InfluxProxy{
			TimeSeries: c,
		}
		api.PostSourcesIDProxyHandler = operations.PostSourcesIDProxyHandlerFunc(h.Proxy)
	} else {
		api.PostSourcesIDProxyHandler = operations.PostSourcesIDProxyHandlerFunc(mockHandler.Proxy)
	}

	api.PostSourcesIDRolesHandler = operations.PostSourcesIDRolesHandlerFunc(func(ctx context.Context, params operations.PostSourcesIDRolesParams) middleware.Responder {
		return middleware.NotImplemented("operation .PostSourcesIDRoles has not yet been implemented")
	})
	api.PostSourcesIDUsersHandler = operations.PostSourcesIDUsersHandlerFunc(func(ctx context.Context, params operations.PostSourcesIDUsersParams) middleware.Responder {
		return middleware.NotImplemented("operation .PostSourcesIDUsers has not yet been implemented")
	})
	api.PostSourcesIDUsersUserIDExplorationsHandler = operations.PostSourcesIDUsersUserIDExplorationsHandlerFunc(func(ctx context.Context, params operations.PostSourcesIDUsersUserIDExplorationsParams) middleware.Responder {
		return middleware.NotImplemented("operation .PostSourcesIDUsersUserIDExplorations has not yet been implemented")
	})
	api.PutDashboardsIDHandler = operations.PutDashboardsIDHandlerFunc(func(ctx context.Context, params operations.PutDashboardsIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .PutDashboardsID has not yet been implemented")
	})

	api.GetSourcesIDMonitoredHandler = operations.GetSourcesIDMonitoredHandlerFunc(mockHandler.MonitoredServices)

	api.ServerShutdown = func() {}

	handler := setupGlobalMiddleware(api.Serve(setupMiddlewares))
	return handler
}

// The TLS configuration before HTTPS server starts.
func configureTLS(tlsConfig *tls.Config) {
	// Make all necessary changes to the TLS configuration here.
}

// The middleware configuration is for the handler executors. These do not apply to the swagger.json document.
// The middleware executes after routing but before authentication, binding and validation
func setupMiddlewares(handler http.Handler) http.Handler {
	return handler
}

// The middleware configuration happens before anything, this middleware also applies to serving the swagger.json document.
// So this is a good place to plug in a panic handling middleware, logging and metrics
func setupGlobalMiddleware(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Printf("%s %s %s\n", r.RemoteAddr, r.Method, r.URL)
		if strings.Contains(r.URL.Path, "/chronograf/v1") {
			handler.ServeHTTP(w, r)
			return
		} else if r.URL.Path == "//" {
			http.Redirect(w, r, "/index.html", http.StatusFound)
		} else {
			assets().Handler().ServeHTTP(w, r)
			return
		}
	})
}
<commit_msg>Add mock explorations handlers to server.<commit_after>package restapi

import (
	"crypto/tls"
	"log"
	"net/http"
	"strings"

	errors "github.com/go-openapi/errors"
	runtime "github.com/go-openapi/runtime"
	middleware "github.com/go-openapi/runtime/middleware"
	"github.com/go-openapi/swag"
	"golang.org/x/net/context"

	"github.com/influxdata/mrfusion"
	"github.com/influxdata/mrfusion/dist"
	"github.com/influxdata/mrfusion/handlers"
	"github.com/influxdata/mrfusion/influx"
	"github.com/influxdata/mrfusion/mock"
	"github.com/influxdata/mrfusion/restapi/operations"
)

// This file is safe to edit. Once it exists it will not be overwritten

//go:generate swagger generate server --target .. --name  --spec ../swagger.yaml --with-context

var devFlags = struct {
	Develop bool `short:"d" long:"develop" description:"Run server in develop mode."`
}{}

var influxFlags = struct {
	Server string `short:"s" long:"server" description:"Full URL of InfluxDB server (http://localhost:8086)" env:"INFLUX_HOST"`
}{}

func configureFlags(api *operations.MrFusionAPI) {
	api.CommandLineOptionsGroups = []swag.CommandLineOptionsGroup{
		swag.CommandLineOptionsGroup{
			ShortDescription: "Develop Mode server",
			LongDescription:  "Server will use the ui/build directory directly.",
			Options:          &devFlags,
		},
		swag.CommandLineOptionsGroup{
			ShortDescription: "Default Time Series Backend",
			LongDescription:  "Specify the url of an InfxluDB server",
			Options:          &influxFlags,
		},
	}
}

func assets() mrfusion.Assets {
	if devFlags.Develop {
		return &dist.DebugAssets{
			Dir:     "ui/build",
			Default: "ui/build/index.html",
		}
	}
	return &dist.BindataAssets{
		Prefix:  "ui/build",
		Default: "index.html",
	}
}

func configureAPI(api *operations.MrFusionAPI) http.Handler {
	// configure the api here
	api.ServeError = errors.ServeError

	// Set your custom logger if needed. Default one is log.Printf
	// Expected interface func(string, ...interface{})
	//
	// Example:
	// s.api.Logger = log.Printf

	api.JSONConsumer = runtime.JSONConsumer()

	api.JSONProducer = runtime.JSONProducer()

	mockHandler := mock.NewHandler()

	api.DeleteSourcesIDUsersUserIDExplorationsExplorationIDHandler = operations.DeleteSourcesIDUsersUserIDExplorationsExplorationIDHandlerFunc(mockHandler.DeleteExploration)
	api.GetSourcesIDUsersUserIDExplorationsExplorationIDHandler = operations.GetSourcesIDUsersUserIDExplorationsExplorationIDHandlerFunc(mockHandler.Exploration)
	api.GetSourcesIDUsersUserIDExplorationsHandler = operations.GetSourcesIDUsersUserIDExplorationsHandlerFunc(mockHandler.Explorations)
	api.PatchSourcesIDUsersUserIDExplorationsExplorationIDHandler = operations.PatchSourcesIDUsersUserIDExplorationsExplorationIDHandlerFunc(mockHandler.UpdateExploration)
	api.PostSourcesIDUsersUserIDExplorationsHandler = operations.PostSourcesIDUsersUserIDExplorationsHandlerFunc(mockHandler.NewExploration)

	api.DeleteDashboardsIDHandler = operations.DeleteDashboardsIDHandlerFunc(func(ctx context.Context, params operations.DeleteDashboardsIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .DeleteDashboardsID has not yet been implemented")
	})
	api.DeleteSourcesIDHandler = operations.DeleteSourcesIDHandlerFunc(func(ctx context.Context, params operations.DeleteSourcesIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .DeleteSourcesID has not yet been implemented")
	})
	api.DeleteSourcesIDRolesRoleIDHandler = operations.DeleteSourcesIDRolesRoleIDHandlerFunc(func(ctx context.Context, params operations.DeleteSourcesIDRolesRoleIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .DeleteSourcesIDRolesRoleID has not yet been implemented")
	})

	api.DeleteSourcesIDUsersUserIDHandler = operations.DeleteSourcesIDUsersUserIDHandlerFunc(func(ctx context.Context, params operations.DeleteSourcesIDUsersUserIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .DeleteSourcesIDUsersUserID has not yet been implemented")
	})
	api.GetHandler = operations.GetHandlerFunc(func(ctx context.Context, params operations.GetParams) middleware.Responder {
		return middleware.NotImplemented("operation .Get has not yet been implemented")
	})
	api.GetDashboardsHandler = operations.GetDashboardsHandlerFunc(func(ctx context.Context, params operations.GetDashboardsParams) middleware.Responder {
		return middleware.NotImplemented("operation .GetDashboards has not yet been implemented")
	})
	api.GetDashboardsIDHandler = operations.GetDashboardsIDHandlerFunc(func(ctx context.Context, params operations.GetDashboardsIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .GetDashboardsID has not yet been implemented")
	})

	api.GetSourcesHandler = operations.GetSourcesHandlerFunc(mockHandler.Sources)
	api.GetSourcesIDHandler = operations.GetSourcesIDHandlerFunc(mockHandler.SourcesID)

	api.GetSourcesIDPermissionsHandler = operations.GetSourcesIDPermissionsHandlerFunc(func(ctx context.Context, params operations.GetSourcesIDPermissionsParams) middleware.Responder {
		return middleware.NotImplemented("operation .GetSourcesIDPermissions has not yet been implemented")
	})
	api.GetSourcesIDRolesHandler = operations.GetSourcesIDRolesHandlerFunc(func(ctx context.Context, params operations.GetSourcesIDRolesParams) middleware.Responder {
		return middleware.NotImplemented("operation .GetSourcesIDRoles has not yet been implemented")
	})
	api.GetSourcesIDRolesRoleIDHandler = operations.GetSourcesIDRolesRoleIDHandlerFunc(func(ctx context.Context, params operations.GetSourcesIDRolesRoleIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .GetSourcesIDRolesRoleID has not yet been implemented")
	})

	api.GetSourcesIDUsersHandler = operations.GetSourcesIDUsersHandlerFunc(func(ctx context.Context, params operations.GetSourcesIDUsersParams) middleware.Responder {
		return middleware.NotImplemented("operation .GetSourcesIDUsers has not yet been implemented")
	})
	api.GetSourcesIDUsersUserIDHandler = operations.GetSourcesIDUsersUserIDHandlerFunc(func(ctx context.Context, params operations.GetSourcesIDUsersUserIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .GetSourcesIDUsersUserID has not yet been implemented")
	})

	api.PatchSourcesIDHandler = operations.PatchSourcesIDHandlerFunc(func(ctx context.Context, params operations.PatchSourcesIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .PatchSourcesID has not yet been implemented")
	})
	api.PatchSourcesIDRolesRoleIDHandler = operations.PatchSourcesIDRolesRoleIDHandlerFunc(func(ctx context.Context, params operations.PatchSourcesIDRolesRoleIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .PatchSourcesIDRolesRoleID has not yet been implemented")
	})

	api.PatchSourcesIDUsersUserIDHandler = operations.PatchSourcesIDUsersUserIDHandlerFunc(func(ctx context.Context, params operations.PatchSourcesIDUsersUserIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .PatchSourcesIDUsersUserID has not yet been implemented")
	})
	api.PostDashboardsHandler = operations.PostDashboardsHandlerFunc(func(ctx context.Context, params operations.PostDashboardsParams) middleware.Responder {
		return middleware.NotImplemented("operation .PostDashboards has not yet been implemented")
	})
	api.PostSourcesHandler = operations.PostSourcesHandlerFunc(mockHandler.NewSource)

	if len(influxFlags.Server) > 0 {
		c, err := influx.NewClient(influxFlags.Server)
		if err != nil {
			panic(err)
		}
		h := handlers.InfluxProxy{
			TimeSeries: c,
		}
		api.PostSourcesIDProxyHandler = operations.PostSourcesIDProxyHandlerFunc(h.Proxy)
	} else {
		api.PostSourcesIDProxyHandler = operations.PostSourcesIDProxyHandlerFunc(mockHandler.Proxy)
	}

	api.PostSourcesIDRolesHandler = operations.PostSourcesIDRolesHandlerFunc(func(ctx context.Context, params operations.PostSourcesIDRolesParams) middleware.Responder {
		return middleware.NotImplemented("operation .PostSourcesIDRoles has not yet been implemented")
	})
	api.PostSourcesIDUsersHandler = operations.PostSourcesIDUsersHandlerFunc(func(ctx context.Context, params operations.PostSourcesIDUsersParams) middleware.Responder {
		return middleware.NotImplemented("operation .PostSourcesIDUsers has not yet been implemented")
	})

	api.PutDashboardsIDHandler = operations.PutDashboardsIDHandlerFunc(func(ctx context.Context, params operations.PutDashboardsIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .PutDashboardsID has not yet been implemented")
	})

	api.GetSourcesIDMonitoredHandler = operations.GetSourcesIDMonitoredHandlerFunc(mockHandler.MonitoredServices)

	api.ServerShutdown = func() {}

	handler := setupGlobalMiddleware(api.Serve(setupMiddlewares))
	return handler
}

// The TLS configuration before HTTPS server starts.
func configureTLS(tlsConfig *tls.Config) {
	// Make all necessary changes to the TLS configuration here.
}

// The middleware configuration is for the handler executors. These do not apply to the swagger.json document.
// The middleware executes after routing but before authentication, binding and validation
func setupMiddlewares(handler http.Handler) http.Handler {
	return handler
}

// The middleware configuration happens before anything, this middleware also applies to serving the swagger.json document.
// So this is a good place to plug in a panic handling middleware, logging and metrics
func setupGlobalMiddleware(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Printf("%s %s %s\n", r.RemoteAddr, r.Method, r.URL)
		if strings.Contains(r.URL.Path, "/chronograf/v1") {
			handler.ServeHTTP(w, r)
			return
		} else if r.URL.Path == "//" {
			http.Redirect(w, r, "/index.html", http.StatusFound)
		} else {
			assets().Handler().ServeHTTP(w, r)
			return
		}
	})
}
<|endoftext|>


<commit_before>/*
 *
 * Copyright 2017 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package grpc

import (
	"fmt"
	"sync"

	"google.golang.org/grpc/balancer"
	"google.golang.org/grpc/connectivity"
	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/resolver"
)

// scStateUpdate contains the subConn and the new state it changed to.
type scStateUpdate struct {
	sc    balancer.SubConn
	state connectivity.State
}

// scStateUpdateBuffer is an unbounded channel for scStateChangeTuple.
// TODO make a general purpose buffer that uses interface{}.
type scStateUpdateBuffer struct {
	c       chan *scStateUpdate
	mu      sync.Mutex
	backlog []*scStateUpdate
}

func newSCStateUpdateBuffer() *scStateUpdateBuffer {
	return &scStateUpdateBuffer{
		c: make(chan *scStateUpdate, 1),
	}
}

func (b *scStateUpdateBuffer) put(t *scStateUpdate) {
	b.mu.Lock()
	defer b.mu.Unlock()
	if len(b.backlog) == 0 {
		select {
		case b.c <- t:
			return
		default:
		}
	}
	b.backlog = append(b.backlog, t)
}

func (b *scStateUpdateBuffer) load() {
	b.mu.Lock()
	defer b.mu.Unlock()
	if len(b.backlog) > 0 {
		select {
		case b.c <- b.backlog[0]:
			b.backlog[0] = nil
			b.backlog = b.backlog[1:]
		default:
		}
	}
}

// get returns the channel that the scStateUpdate will be sent to.
//
// Upon receiving, the caller should call load to send another
// scStateChangeTuple onto the channel if there is any.
func (b *scStateUpdateBuffer) get() <-chan *scStateUpdate {
	return b.c
}

// resolverUpdate contains the new resolved addresses or error if there's
// any.
type resolverUpdate struct {
	addrs []resolver.Address
	err   error
}

// ccBalancerWrapper is a wrapper on top of cc for balancers.
// It implements balancer.ClientConn interface.
type ccBalancerWrapper struct {
	cc               *ClientConn
	balancer         balancer.Balancer
	stateChangeQueue *scStateUpdateBuffer
	resolverUpdateCh chan *resolverUpdate
	done             chan struct{}

	mu       sync.RWMutex
	subConns map[*acBalancerWrapper]struct{}
}

func newCCBalancerWrapper(cc *ClientConn, b balancer.Builder, bopts balancer.BuildOptions) *ccBalancerWrapper {
	ccb := &ccBalancerWrapper{
		cc:               cc,
		stateChangeQueue: newSCStateUpdateBuffer(),
		resolverUpdateCh: make(chan *resolverUpdate, 1),
		done:             make(chan struct{}),
		subConns:         make(map[*acBalancerWrapper]struct{}),
	}
	go ccb.watcher()
	ccb.balancer = b.Build(ccb, bopts)
	return ccb
}

// watcher balancer functions sequencially, so the balancer can be implemeneted
// lock-free.
func (ccb *ccBalancerWrapper) watcher() {
	for {
		select {
		case t := <-ccb.stateChangeQueue.get():
			ccb.stateChangeQueue.load()
			select {
			case <-ccb.done:
				ccb.balancer.Close()
				return
			default:
			}
			ccb.balancer.HandleSubConnStateChange(t.sc, t.state)
		case t := <-ccb.resolverUpdateCh:
			select {
			case <-ccb.done:
				ccb.balancer.Close()
				return
			default:
			}
			ccb.balancer.HandleResolvedAddrs(t.addrs, t.err)
		case <-ccb.done:
		}

		select {
		case <-ccb.done:
			ccb.balancer.Close()
			ccb.mu.RLock()
			for acbw := range ccb.subConns {
				ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain)
			}
			ccb.mu.RUnlock()
			return
		default:
		}
	}
}

func (ccb *ccBalancerWrapper) close() {
	close(ccb.done)
}

func (ccb *ccBalancerWrapper) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State) {
	// When updating addresses for a SubConn, if the address in use is not in
	// the new addresses, the old ac will be tearDown() and a new ac will be
	// created. tearDown() generates a state change with Shutdown state, we
	// don't want the balancer to receive this state change. So before
	// tearDown() on the old ac, ac.acbw (acWrapper) will be set to nil, and
	// this function will be called with (nil, Shutdown). We don't need to call
	// balancer method in this case.
	if sc == nil {
		return
	}
	ccb.stateChangeQueue.put(&scStateUpdate{
		sc:    sc,
		state: s,
	})
}

func (ccb *ccBalancerWrapper) handleResolvedAddrs(addrs []resolver.Address, err error) {
	select {
	case <-ccb.resolverUpdateCh:
	default:
	}
	ccb.resolverUpdateCh <- &resolverUpdate{
		addrs: addrs,
		err:   err,
	}
}

func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) {
	if len(addrs) <= 0 {
		return nil, fmt.Errorf("grpc: cannot create SubConn with empty address list")
	}
	ac, err := ccb.cc.newAddrConn(addrs)
	if err != nil {
		return nil, err
	}
	acbw := &acBalancerWrapper{ac: ac}
	acbw.ac.mu.Lock()
	ac.acbw = acbw
	acbw.ac.mu.Unlock()
	ccb.mu.Lock()
	ccb.subConns[acbw] = struct{}{}
	ccb.mu.Unlock()
	return acbw, nil
}

func (ccb *ccBalancerWrapper) RemoveSubConn(sc balancer.SubConn) {
	acbw, ok := sc.(*acBalancerWrapper)
	if !ok {
		return
	}
	ccb.mu.Lock()
	delete(ccb.subConns, acbw)
	ccb.mu.Unlock()
	ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain)
}

func (ccb *ccBalancerWrapper) UpdateBalancerState(s connectivity.State, p balancer.Picker) {
	ccb.cc.csMgr.updateState(s)
	ccb.cc.blockingpicker.updatePicker(p)
}

func (ccb *ccBalancerWrapper) Target() string {
	return ccb.cc.target
}

// acBalancerWrapper is a wrapper on top of ac for balancers.
// It implements balancer.SubConn interface.
type acBalancerWrapper struct {
	mu sync.Mutex
	ac *addrConn
}

func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) {
	acbw.mu.Lock()
	defer acbw.mu.Unlock()
	if len(addrs) <= 0 {
		acbw.ac.tearDown(errConnDrain)
		return
	}
	if !acbw.ac.tryUpdateAddrs(addrs) {
		cc := acbw.ac.cc
		acbw.ac.mu.Lock()
		// Set old ac.acbw to nil so the Shutdown state update will be ignored
		// by balancer.
		//
		// TODO(bar) the state transition could be wrong when tearDown() old ac
		// and creating new ac, fix the transition.
		acbw.ac.acbw = nil
		acbw.ac.mu.Unlock()
		acState := acbw.ac.getState()
		acbw.ac.tearDown(errConnDrain)

		if acState == connectivity.Shutdown {
			return
		}

		ac, err := cc.newAddrConn(addrs)
		if err != nil {
			grpclog.Warningf("acBalancerWrapper: UpdateAddresses: failed to newAddrConn: %v", err)
			return
		}
		acbw.ac = ac
		ac.mu.Lock()
		ac.acbw = acbw
		ac.mu.Unlock()
		if acState != connectivity.Idle {
			ac.connect()
		}
	}
}

func (acbw *acBalancerWrapper) Connect() {
	acbw.mu.Lock()
	defer acbw.mu.Unlock()
	acbw.ac.connect()
}

func (acbw *acBalancerWrapper) getAddrConn() *addrConn {
	acbw.mu.Lock()
	defer acbw.mu.Unlock()
	return acbw.ac
}
<commit_msg>Disable ccBalancerWrapper when it is closed (#1698)<commit_after>/*
 *
 * Copyright 2017 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package grpc

import (
	"fmt"
	"sync"

	"google.golang.org/grpc/balancer"
	"google.golang.org/grpc/connectivity"
	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/resolver"
)

// scStateUpdate contains the subConn and the new state it changed to.
type scStateUpdate struct {
	sc    balancer.SubConn
	state connectivity.State
}

// scStateUpdateBuffer is an unbounded channel for scStateChangeTuple.
// TODO make a general purpose buffer that uses interface{}.
type scStateUpdateBuffer struct {
	c       chan *scStateUpdate
	mu      sync.Mutex
	backlog []*scStateUpdate
}

func newSCStateUpdateBuffer() *scStateUpdateBuffer {
	return &scStateUpdateBuffer{
		c: make(chan *scStateUpdate, 1),
	}
}

func (b *scStateUpdateBuffer) put(t *scStateUpdate) {
	b.mu.Lock()
	defer b.mu.Unlock()
	if len(b.backlog) == 0 {
		select {
		case b.c <- t:
			return
		default:
		}
	}
	b.backlog = append(b.backlog, t)
}

func (b *scStateUpdateBuffer) load() {
	b.mu.Lock()
	defer b.mu.Unlock()
	if len(b.backlog) > 0 {
		select {
		case b.c <- b.backlog[0]:
			b.backlog[0] = nil
			b.backlog = b.backlog[1:]
		default:
		}
	}
}

// get returns the channel that the scStateUpdate will be sent to.
//
// Upon receiving, the caller should call load to send another
// scStateChangeTuple onto the channel if there is any.
func (b *scStateUpdateBuffer) get() <-chan *scStateUpdate {
	return b.c
}

// resolverUpdate contains the new resolved addresses or error if there's
// any.
type resolverUpdate struct {
	addrs []resolver.Address
	err   error
}

// ccBalancerWrapper is a wrapper on top of cc for balancers.
// It implements balancer.ClientConn interface.
type ccBalancerWrapper struct {
	cc               *ClientConn
	balancer         balancer.Balancer
	stateChangeQueue *scStateUpdateBuffer
	resolverUpdateCh chan *resolverUpdate
	done             chan struct{}

	mu       sync.Mutex
	subConns map[*acBalancerWrapper]struct{}
}

func newCCBalancerWrapper(cc *ClientConn, b balancer.Builder, bopts balancer.BuildOptions) *ccBalancerWrapper {
	ccb := &ccBalancerWrapper{
		cc:               cc,
		stateChangeQueue: newSCStateUpdateBuffer(),
		resolverUpdateCh: make(chan *resolverUpdate, 1),
		done:             make(chan struct{}),
		subConns:         make(map[*acBalancerWrapper]struct{}),
	}
	go ccb.watcher()
	ccb.balancer = b.Build(ccb, bopts)
	return ccb
}

// watcher balancer functions sequencially, so the balancer can be implemeneted
// lock-free.
func (ccb *ccBalancerWrapper) watcher() {
	for {
		select {
		case t := <-ccb.stateChangeQueue.get():
			ccb.stateChangeQueue.load()
			select {
			case <-ccb.done:
				ccb.balancer.Close()
				return
			default:
			}
			ccb.balancer.HandleSubConnStateChange(t.sc, t.state)
		case t := <-ccb.resolverUpdateCh:
			select {
			case <-ccb.done:
				ccb.balancer.Close()
				return
			default:
			}
			ccb.balancer.HandleResolvedAddrs(t.addrs, t.err)
		case <-ccb.done:
		}

		select {
		case <-ccb.done:
			ccb.balancer.Close()
			ccb.mu.Lock()
			scs := ccb.subConns
			ccb.subConns = nil
			ccb.mu.Unlock()
			for acbw := range scs {
				ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain)
			}
			return
		default:
		}
	}
}

func (ccb *ccBalancerWrapper) close() {
	close(ccb.done)
}

func (ccb *ccBalancerWrapper) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State) {
	// When updating addresses for a SubConn, if the address in use is not in
	// the new addresses, the old ac will be tearDown() and a new ac will be
	// created. tearDown() generates a state change with Shutdown state, we
	// don't want the balancer to receive this state change. So before
	// tearDown() on the old ac, ac.acbw (acWrapper) will be set to nil, and
	// this function will be called with (nil, Shutdown). We don't need to call
	// balancer method in this case.
	if sc == nil {
		return
	}
	ccb.stateChangeQueue.put(&scStateUpdate{
		sc:    sc,
		state: s,
	})
}

func (ccb *ccBalancerWrapper) handleResolvedAddrs(addrs []resolver.Address, err error) {
	select {
	case <-ccb.resolverUpdateCh:
	default:
	}
	ccb.resolverUpdateCh <- &resolverUpdate{
		addrs: addrs,
		err:   err,
	}
}

func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) {
	if len(addrs) <= 0 {
		return nil, fmt.Errorf("grpc: cannot create SubConn with empty address list")
	}
	ccb.mu.Lock()
	defer ccb.mu.Unlock()
	if ccb.subConns == nil {
		return nil, fmt.Errorf("grpc: ClientConn balancer wrapper was closed")
	}
	ac, err := ccb.cc.newAddrConn(addrs)
	if err != nil {
		return nil, err
	}
	acbw := &acBalancerWrapper{ac: ac}
	acbw.ac.mu.Lock()
	ac.acbw = acbw
	acbw.ac.mu.Unlock()
	ccb.subConns[acbw] = struct{}{}
	return acbw, nil
}

func (ccb *ccBalancerWrapper) RemoveSubConn(sc balancer.SubConn) {
	acbw, ok := sc.(*acBalancerWrapper)
	if !ok {
		return
	}
	ccb.mu.Lock()
	defer ccb.mu.Unlock()
	if ccb.subConns == nil {
		return
	}
	delete(ccb.subConns, acbw)
	ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain)
}

func (ccb *ccBalancerWrapper) UpdateBalancerState(s connectivity.State, p balancer.Picker) {
	ccb.mu.Lock()
	defer ccb.mu.Unlock()
	if ccb.subConns == nil {
		return
	}
	ccb.cc.csMgr.updateState(s)
	ccb.cc.blockingpicker.updatePicker(p)
}

func (ccb *ccBalancerWrapper) Target() string {
	return ccb.cc.target
}

// acBalancerWrapper is a wrapper on top of ac for balancers.
// It implements balancer.SubConn interface.
type acBalancerWrapper struct {
	mu sync.Mutex
	ac *addrConn
}

func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) {
	acbw.mu.Lock()
	defer acbw.mu.Unlock()
	if len(addrs) <= 0 {
		acbw.ac.tearDown(errConnDrain)
		return
	}
	if !acbw.ac.tryUpdateAddrs(addrs) {
		cc := acbw.ac.cc
		acbw.ac.mu.Lock()
		// Set old ac.acbw to nil so the Shutdown state update will be ignored
		// by balancer.
		//
		// TODO(bar) the state transition could be wrong when tearDown() old ac
		// and creating new ac, fix the transition.
		acbw.ac.acbw = nil
		acbw.ac.mu.Unlock()
		acState := acbw.ac.getState()
		acbw.ac.tearDown(errConnDrain)

		if acState == connectivity.Shutdown {
			return
		}

		ac, err := cc.newAddrConn(addrs)
		if err != nil {
			grpclog.Warningf("acBalancerWrapper: UpdateAddresses: failed to newAddrConn: %v", err)
			return
		}
		acbw.ac = ac
		ac.mu.Lock()
		ac.acbw = acbw
		ac.mu.Unlock()
		if acState != connectivity.Idle {
			ac.connect()
		}
	}
}

func (acbw *acBalancerWrapper) Connect() {
	acbw.mu.Lock()
	defer acbw.mu.Unlock()
	acbw.ac.connect()
}

func (acbw *acBalancerWrapper) getAddrConn() *addrConn {
	acbw.mu.Lock()
	defer acbw.mu.Unlock()
	return acbw.ac
}
<|endoftext|>


<commit_before>package honeybadger

import (
	"code.google.com/p/go-uuid/uuid"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestDefaultConfig(t *testing.T) {
	if Config.APIKey != "" {
		t.Errorf("Expected config.APIKey to be empty by default. expected=%#v result=%#v", "", Config.APIKey)
	}
}

func TestConfigure(t *testing.T) {
	Configure(Configuration{APIKey: "badgers"})
	if Config.APIKey != "badgers" {
		t.Errorf("Expected Configure to override config.APIKey. expected=%#v actual=%#v", "badgers", Config.APIKey)
	}
}

func TestNotify(t *testing.T) {
	mux := http.NewServeMux()
	ts := httptest.NewServer(mux)

	var requests []*HTTPRequest
	mux.HandleFunc("/v1/notices",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, "POST")
			requests = append(requests, newHTTPRequest(r))
			w.WriteHeader(201)
			fmt.Fprint(w, `{"id":"87ded4b4-63cc-480a-b50c-8abe1376d972"}`)
		},
	)

	Configure(Configuration{APIKey: "badgers", Endpoint: ts.URL})

	err := errors.New("Cobras!")
	var res string
	res = Notify(err)
	if uuid.Parse(res) == nil {
		t.Errorf("Expected Notify() to return a UUID. actual=%#v", res)
	}

	Flush()

	if len(requests) != 1 {
		t.Errorf("Expected 1 request to have been made. actual=%#v", len(requests))
	}

	payload := requests[0].decodeJSON()

	if payload["api_key"] != "badgers" {
		t.Errorf("Expected payload to include API key. expected=%#v actual=%#v", "badgers", payload["api_key"])
	}

	for _, key := range []string{"notifier", "error", "request", "server"} {
		switch payload[key].(type) {
		case map[string]interface{}:
			// OK
		default:
			t.Errorf("Expected payload to include %v hash.", key)
		}
	}
}

// Helper functions.

type HTTPRequest struct {
	Request *http.Request
	Body    []byte
}

func (h *HTTPRequest) decodeJSON() hash {
	var dat hash
	err := json.Unmarshal(h.Body, &dat)
	if err != nil {
		panic(err)
	}
	return dat
}

func newHTTPRequest(r *http.Request) *HTTPRequest {
	body, _ := ioutil.ReadAll(r.Body)
	return &HTTPRequest{r, body}
}

func testMethod(t *testing.T, r *http.Request, method string) {
	if r.Method != method {
		t.Errorf("Unexpected request method. actual=%#v expected=%#v", r.Method, method)
	}
}
<commit_msg>Dry up the notify test a bit.<commit_after>package honeybadger

import (
	"code.google.com/p/go-uuid/uuid"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"
)

var (
	mux      *http.ServeMux
	ts       *httptest.Server
	requests []*HTTPRequest
)

type HTTPRequest struct {
	Request *http.Request
	Body    []byte
}

func (h *HTTPRequest) decodeJSON() hash {
	var dat hash
	err := json.Unmarshal(h.Body, &dat)
	if err != nil {
		panic(err)
	}
	return dat
}

func newHTTPRequest(r *http.Request) *HTTPRequest {
	body, _ := ioutil.ReadAll(r.Body)
	return &HTTPRequest{r, body}
}

func setup(t *testing.T) {
	mux = http.NewServeMux()
	ts = httptest.NewServer(mux)
	requests = []*HTTPRequest{}
	mux.HandleFunc("/v1/notices",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, "POST")
			requests = append(requests, newHTTPRequest(r))
			w.WriteHeader(201)
			fmt.Fprint(w, `{"id":"87ded4b4-63cc-480a-b50c-8abe1376d972"}`)
		},
	)

	client.Config = newConfig(Configuration{APIKey: "badgers", Endpoint: ts.URL})
}

func TestDefaultConfig(t *testing.T) {
	if Config.APIKey != "" {
		t.Errorf("Expected config.APIKey to be empty by default. expected=%#v result=%#v", "", Config.APIKey)
	}
}

func TestConfigure(t *testing.T) {
	Configure(Configuration{APIKey: "badgers"})
	if Config.APIKey != "badgers" {
		t.Errorf("Expected Configure to override config.APIKey. expected=%#v actual=%#v", "badgers", Config.APIKey)
	}
}

func TestNotify(t *testing.T) {
	setup(t)

	res := Notify(errors.New("Cobras!"))
	if uuid.Parse(res) == nil {
		t.Errorf("Expected Notify() to return a UUID. actual=%#v", res)
	}

	Flush()

	testRequestCount(t, 1)

	payload := requests[0].decodeJSON()

	testNoticePayload(t, payload)
}

// Helper functions.

func testRequestCount(t *testing.T, num int) {
	if len(requests) != num {
		t.Errorf("Expected %v request to have been made. expected=%#v actual=%#v", num, num, len(requests))
	}
}

func testNoticePayload(t *testing.T, payload hash) {
	for _, key := range []string{"notifier", "error", "request", "server"} {
		switch payload[key].(type) {
		case map[string]interface{}:
			// OK
		default:
			t.Errorf("Expected payload to include %v hash.", key)
		}
	}
}

func testMethod(t *testing.T, r *http.Request, method string) {
	if r.Method != method {
		t.Errorf("Unexpected request method. actual=%#v expected=%#v", r.Method, method)
	}
}
<|endoftext|>