<commit_before>package recorder

import (
	"os"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"net/url"

	"github.com/dnaeon/go-vcr/cassette"
)

// Recorder states
const (
	ModeRecording = iota
	ModeReplaying
)

type Recorder struct {
	// Operating mode of the recorder
	mode int

	// HTTP server used to mock requests
	server *httptest.Server

	// Cassette used by the recorder
	cassette *cassette.Cassette

	// Transport that can be used by clients to inject
	Transport *http.Transport
}

// Proxies client requests to their original destination
func requestHandler(r *http.Request, c *cassette.Cassette, mode int) (*cassette.Interaction, error) {
	// Return interaction from cassette if in replay mode
	if mode == ModeReplaying {
		return c.GetInteraction(r)
	}

	// Else, perform client request to their original
	// destination and record interactions
	req, err := http.NewRequest(r.Method, r.URL.String(), r.Body)
	if err != nil {
		return nil, err
	}

	req.Header = r.Header
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}

	// Record the interaction and add it to the cassette
	reqBody, err := ioutil.ReadAll(req.Body)
	if err != nil {
		return nil, err
	}

	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	// Add interaction to cassette
	interaction := &cassette.Interaction{
		Request: cassette.Request{
			Body:    string(reqBody),
			Headers: req.Header,
			URL:     req.URL.String(),
			Method:  req.Method,
		},
		Response: cassette.Response{
			Body:    string(respBody),
			Headers: resp.Header,
			Status:  resp.Status,
			Code:    resp.StatusCode,
		},
	}
	c.AddInteraction(interaction)

	return interaction, nil
}

// Creates a new recorder
func New(cassetteName string) (*Recorder, error) {
	var mode int
	var c *cassette.Cassette
	cassetteFile := fmt.Sprintf("%s.yaml", cassetteName)

	// Depending on whether the cassette file exists or not we
	// either create a new empty cassette or load from file
	if _, err := os.Stat(cassetteFile); os.IsNotExist(err) {
		// Create new cassette and enter in recording mode
		c = cassette.New(cassetteName)
		mode = ModeRecording
	} else {
		// Load cassette from file and enter replay mode
		c, err = cassette.Load(cassetteName)
		if err != nil {
			return nil, err
		}
		mode = ModeReplaying
	}

	// Handler for client requests
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Pass cassette and mode to handler, so that interactions can be
		// retrieved or recorded depending on the current recorder mode
		interaction, err := requestHandler(r, c, mode)

		// Handle known and recoverable errors
		if err != nil {
			switch err {
			case cassette.InteractionNotFound:
				// Interaction was not found in cassette
				http.NotFound(w, r)
			default:
				// Other error occurred
				log.Fatal(err)
			}
		}

		w.WriteHeader(interaction.Response.Code)
		fmt.Fprintln(w, interaction.Response.Body)
	})

	// HTTP server used to mock requests
	server := httptest.NewServer(handler)

	// A proxy function which routes all requests through our HTTP server
	// Can be used by clients to inject into their own transports
	proxyUrl, err := url.Parse(server.URL)
	if err != nil {
		return nil, err
	}

	// A transport which can be used by clients to inject
	transport := &http.Transport{
		Proxy: http.ProxyURL(proxyUrl),
	}

	r := &Recorder{
		mode:      mode,
		server:    server,
		cassette:  c,
		Transport: transport,
	}

	return r, nil
}

// Stops the recorder
func (r *Recorder) Stop() error {
	r.server.Close()

	if r.mode == ModeRecording {
		if err := r.cassette.Save(); err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>Display more details when processing a request fails<commit_after>package recorder

import (
	"os"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"net/url"

	"github.com/dnaeon/go-vcr/cassette"
)

// Recorder states
const (
	ModeRecording = iota
	ModeReplaying
)

type Recorder struct {
	// Operating mode of the recorder
	mode int

	// HTTP server used to mock requests
	server *httptest.Server

	// Cassette used by the recorder
	cassette *cassette.Cassette

	// Transport that can be used by clients to inject
	Transport *http.Transport
}

// Proxies client requests to their original destination
func requestHandler(r *http.Request, c *cassette.Cassette, mode int) (*cassette.Interaction, error) {
	// Return interaction from cassette if in replay mode
	if mode == ModeReplaying {
		return c.GetInteraction(r)
	}

	// Else, perform client request to their original
	// destination and record interactions
	req, err := http.NewRequest(r.Method, r.URL.String(), r.Body)
	if err != nil {
		return nil, err
	}

	req.Header = r.Header
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}

	// Record the interaction and add it to the cassette
	reqBody, err := ioutil.ReadAll(req.Body)
	if err != nil {
		return nil, err
	}

	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	// Add interaction to cassette
	interaction := &cassette.Interaction{
		Request: cassette.Request{
			Body:    string(reqBody),
			Headers: req.Header,
			URL:     req.URL.String(),
			Method:  req.Method,
		},
		Response: cassette.Response{
			Body:    string(respBody),
			Headers: resp.Header,
			Status:  resp.Status,
			Code:    resp.StatusCode,
		},
	}
	c.AddInteraction(interaction)

	return interaction, nil
}

// Creates a new recorder
func New(cassetteName string) (*Recorder, error) {
	var mode int
	var c *cassette.Cassette
	cassetteFile := fmt.Sprintf("%s.yaml", cassetteName)

	// Depending on whether the cassette file exists or not we
	// either create a new empty cassette or load from file
	if _, err := os.Stat(cassetteFile); os.IsNotExist(err) {
		// Create new cassette and enter in recording mode
		c = cassette.New(cassetteName)
		mode = ModeRecording
	} else {
		// Load cassette from file and enter replay mode
		c, err = cassette.Load(cassetteName)
		if err != nil {
			return nil, err
		}
		mode = ModeReplaying
	}

	// Handler for client requests
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Pass cassette and mode to handler, so that interactions can be
		// retrieved or recorded depending on the current recorder mode
		interaction, err := requestHandler(r, c, mode)

		if err != nil {
			log.Fatalf("Failed to process request for URL %s: %s", r.URL, err)
		}

		w.WriteHeader(interaction.Response.Code)
		fmt.Fprintln(w, interaction.Response.Body)
	})

	// HTTP server used to mock requests
	server := httptest.NewServer(handler)

	// A proxy function which routes all requests through our HTTP server
	// Can be used by clients to inject into their own transports
	proxyUrl, err := url.Parse(server.URL)
	if err != nil {
		return nil, err
	}

	// A transport which can be used by clients to inject
	transport := &http.Transport{
		Proxy: http.ProxyURL(proxyUrl),
	}

	r := &Recorder{
		mode:      mode,
		server:    server,
		cassette:  c,
		Transport: transport,
	}

	return r, nil
}

// Stops the recorder
func (r *Recorder) Stop() error {
	r.server.Close()

	if r.mode == ModeRecording {
		if err := r.cassette.Save(); err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package rest

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
	"strings"

	"github.com/gorilla/mux"
	"gopkg.in/inconshreveable/log15.v2"

	"github.com/nsec/askgod/api"
)

func (r *rest) getTeam(writer http.ResponseWriter, request *http.Request, logger log15.Logger) {
	// Extract the client IP
	ip, err := r.getIP(request)
	if err != nil {
		logger.Error("Failed to get the client's IP", log15.Ctx{"error": err})
		r.errorResponse(500, "Internal Server Error", writer, request)
		return
	}

	// Look for a matching team
	record, err := r.db.GetTeamForIP(*ip)
	if err == sql.ErrNoRows {
		logger.Warn("No team found for IP", log15.Ctx{"ip": ip.String()})
		r.errorResponse(404, "No team found for IP", writer, request)
		return
	} else if err != nil {
		logger.Error("Failed to get the team", log15.Ctx{"error": err})
		r.errorResponse(500, "Internal Server Error", writer, request)
		return
	}

	// Convert to the team view of a team
	team := api.Team{}
	team.ID = record.ID
	team.Name = record.Name
	team.Country = record.Country
	team.Website = record.Website

	r.jsonResponse(team, writer, request)
}

func (r *rest) updateTeam(writer http.ResponseWriter, request *http.Request, logger log15.Logger) {
	// Configuration validation
	if !r.config.Teams.SelfRegister {
		logger.Warn("Unauthorized attempt to self-register")
		r.errorResponse(403, "Team self-registration disabled", writer, request)
		return
	}

	// Decode the provided JSON input
	newTeam := api.TeamPut{}
	err := json.NewDecoder(request.Body).Decode(&newTeam)
	if err != nil {
		logger.Warn("Malformed JSON provided", log15.Ctx{"error": err})
		r.errorResponse(400, "Malformed JSON provided", writer, request)
		return
	}

	// Sanity checks
	validName := func(name string) bool {
		// Validate the length
		if len(name) < 1 || len(name) > 30 {
			return false
		}

		// Validate the character set
		match, _ := regexp.MatchString("^[a-zA-Z0-9 /\\\\~!@#$%&*()\\-_+={}\\[\\];:',.?]*$", name)
		if !match {
			return false
		}

		return true
	}

	if !validName(newTeam.Name) {
		logger.Warn("Bad team name", log15.Ctx{"name": newTeam.Name})
		r.errorResponse(400, "Bad team name", writer, request)
		return
	}

	if len(newTeam.Country) > 2 {
		logger.Warn("Bad team country code", log15.Ctx{"country": newTeam.Country})
		r.errorResponse(400, "Bad team country code", writer, request)
		return
	}

	if newTeam.Website != "" {
		u, err := url.ParseRequestURI(newTeam.Website)
		if err != nil {
			logger.Warn("Bad team URL", log15.Ctx{"url": newTeam.Website})
			r.errorResponse(400, "Bad team URL", writer, request)
			return
		}

		newTeam.Website = u.String()
	}

	// Extract the client IP
	ip, err := r.getIP(request)
	if err != nil {
		logger.Error("Failed to get the client's IP", log15.Ctx{"error": err})
		r.errorResponse(500, "Internal Server Error", writer, request)
		return
	}

	// Look for a matching team
	team, err := r.db.GetTeamForIP(*ip)
	if err == sql.ErrNoRows {
		logger.Warn("No team found for IP", log15.Ctx{"ip": ip.String()})
		r.errorResponse(404, "No team found for IP", writer, request)
		return
	} else if err != nil {
		logger.Error("Failed to get the team", log15.Ctx{"error": err})
		r.errorResponse(500, "Internal Server Error", writer, request)
		return
	}

	// Validate the request
	if !r.config.Teams.SelfUpdate {
		if team.Name != "" && team.Name != newTeam.Name {
			logger.Debug("Unauthorized attempt to change already set team property", log15.Ctx{"property": "name"})
			r.errorResponse(400, "Team name is already set", writer, request)
			return
		}

		if team.Country != "" && team.Country != newTeam.Country {
			logger.Debug("Unauthorized attempt to change already set team property", log15.Ctx{"property": "country"})
			r.errorResponse(400, "Team country is already set", writer, request)
			return
		}

		if team.Website != "" && team.Website != newTeam.Website {
			logger.Debug("Unauthorized attempt to change already set team property", log15.Ctx{"property": "website"})
			r.errorResponse(400, "Team website is already set", writer, request)
			return
		}
	}

	// Setup the new record
	newRecord := api.AdminTeamPut{}
	newRecord.Name = newTeam.Name
	newRecord.Country = strings.ToUpper(newTeam.Country)
	newRecord.Website = newTeam.Website
	newRecord.Notes = team.Notes
	newRecord.Subnets = team.Subnets
	newRecord.Tags = team.Tags

	// Attempt to update the database
	err = r.db.UpdateTeam(team.ID, newRecord)
	if err != nil {
		logger.Error("Failed to update the team", log15.Ctx{"error": err})
		r.errorResponse(500, "Internal Server Error", writer, request)
		return
	}

	r.eventSend("timeline", api.EventTimeline{TeamID: team.ID, Team: &newRecord.TeamPut, Type: "team-updated"})
	logger.Info("Team updated", log15.Ctx{"id": team.ID, "name": newRecord.Name, "country": newRecord.Country, "website": newRecord.Website})
}

func (r *rest) adminGetTeams(writer http.ResponseWriter, request *http.Request, logger log15.Logger) {
	// Get all the teams from the database
	teams, err := r.db.GetTeams()
	if err != nil {
		logger.Error("Failed to query the team list", log15.Ctx{"error": err})
		r.errorResponse(500, fmt.Sprintf("%v", err), writer, request)
		return
	}

	r.jsonResponse(teams, writer, request)
}

func (r *rest) adminCreateTeam(writer http.ResponseWriter, request *http.Request, logger log15.Logger) {
	// Bulk create
	bulkVar := request.FormValue("bulk")
	if bulkVar == "1" {
		r.adminCreateTeams(writer, request, logger)
		return
	}

	// Decode the provided JSON input
	newTeam := api.AdminTeamPost{}
	err := json.NewDecoder(request.Body).Decode(&newTeam)
	if err != nil {
		logger.Warn("Malformed JSON provided", log15.Ctx{"error": err})
		r.errorResponse(400, "Malformed JSON provided", writer, request)
		return
	}

	// Attempt to create the database record
	id, err := r.db.CreateTeam(newTeam)
	if err != nil {
		logger.Error("Failed to create the team", log15.Ctx{"error": err})
		r.errorResponse(500, fmt.Sprintf("%v", err), writer, request)
		return
	}

	r.eventSend("timeline", api.EventTimeline{TeamID: id, Team: &newTeam.AdminTeamPut.TeamPut, Type: "team-added"})
	logger.Info("New team defined", log15.Ctx{"id": id, "subnets": newTeam.Subnets})
}

func (r *rest) adminCreateTeams(writer http.ResponseWriter, request *http.Request, logger log15.Logger) {
	// Decode the provided JSON input
	newTeams := []api.AdminTeamPost{}
	err := json.NewDecoder(request.Body).Decode(&newTeams)
	if err != nil {
		logger.Warn("Malformed JSON provided", log15.Ctx{"error": err})
		r.errorResponse(400, "Malformed JSON provided", writer, request)
		return
	}

	for _, team := range newTeams {
		// Attempt to create the database record
		id, err := r.db.CreateTeam(team)
		if err != nil {
			logger.Error("Failed to create the team", log15.Ctx{"error": err})
			r.errorResponse(500, fmt.Sprintf("%v", err), writer, request)
			return
		}

		r.eventSend("timeline", api.EventTimeline{TeamID: id, Team: &team.AdminTeamPut.TeamPut, Type: "team-added"})
		logger.Info("New team defined", log15.Ctx{"id": id, "subnets": team.Subnets})
	}
}

func (r *rest) adminGetTeam(writer http.ResponseWriter, request *http.Request, logger log15.Logger) {
	idVar := mux.Vars(request)["id"]

	// Convert the provided id to int
	id, err := strconv.ParseInt(idVar, 10, 64)
	if err != nil {
		logger.Warn("Invalid team ID provided", log15.Ctx{"id": idVar})
		r.errorResponse(400, "Invalid team ID provided", writer, request)
		return
	}

	// Attempt to get the DB record
	team, err := r.db.GetTeam(id)
	if err == sql.ErrNoRows {
		logger.Warn("Invalid team ID provided", log15.Ctx{"id": idVar})
		r.errorResponse(404, "Invalid team ID provided", writer, request)
		return
	} else if err != nil {
		logger.Error("Failed to get the team", log15.Ctx{"error": err})
		r.errorResponse(500, fmt.Sprintf("%v", err), writer, request)
		return
	}

	r.jsonResponse(team, writer, request)
}

func (r *rest) adminUpdateTeam(writer http.ResponseWriter, request *http.Request, logger log15.Logger) {
	idVar := mux.Vars(request)["id"]

	// Convert the provided id to int
	id, err := strconv.ParseInt(idVar, 10, 64)
	if err != nil {
		logger.Warn("Invalid team ID provided", log15.Ctx{"id": idVar})
		r.errorResponse(400, "Invalid team ID provided", writer, request)
		return
	}

	// Decode the provided JSON input
	newTeam := api.AdminTeamPut{}
	err = json.NewDecoder(request.Body).Decode(&newTeam)
	if err != nil {
		logger.Warn("Malformed JSON provided", log15.Ctx{"error": err})
		r.errorResponse(400, "Malformed JSON provided", writer, request)
		return
	}

	// Attempt to update the database
	err = r.db.UpdateTeam(id, newTeam)
	if err == sql.ErrNoRows {
		logger.Warn("Invalid team ID provided", log15.Ctx{"id": idVar})
		r.errorResponse(404, "Invalid team ID provided", writer, request)
		return
	} else if err != nil {
		logger.Error("Failed to update the team", log15.Ctx{"error": err})
		r.errorResponse(500, fmt.Sprintf("%v", err), writer, request)
		return
	}

	r.eventSend("timeline", api.EventTimeline{TeamID: id, Team: &newTeam.TeamPut, Type: "team-updated"})
	logger.Info("Team updated", log15.Ctx{"id": id, "name": newTeam.Name, "country": newTeam.Country, "website": newTeam.Website})
}

func (r *rest) adminDeleteTeam(writer http.ResponseWriter, request *http.Request, logger log15.Logger) {
	idVar := mux.Vars(request)["id"]

	// Convert the provided id to int
	id, err := strconv.ParseInt(idVar, 10, 64)
	if err != nil {
		logger.Warn("Invalid team ID provided", log15.Ctx{"id": idVar})
		r.errorResponse(400, "Invalid team ID provided", writer, request)
		return
	}

	// Attempt to get the DB record
	err = r.db.DeleteTeam(id)
	if err == sql.ErrNoRows {
		logger.Warn("Invalid team ID provided", log15.Ctx{"id": idVar})
		r.errorResponse(404, "Invalid team ID provided", writer, request)
		return
	} else if err != nil {
		logger.Error("Failed to delete the team", log15.Ctx{"error": err})
		r.errorResponse(500, fmt.Sprintf("%v", err), writer, request)
		return
	}

	r.eventSend("timeline", api.EventTimeline{TeamID: id, Type: "team-removed"})
	logger.Info("Team deleted", log15.Ctx{"id": id})
}

func (r *rest) adminClearTeams(writer http.ResponseWriter, request *http.Request, logger log15.Logger) {
	emptyVar := request.FormValue("empty")

	// Confirm the user is sure about it
	if emptyVar != "1" {
		logger.Warn("Teams clear requested without empty=1")
		r.errorResponse(400, "Teams clear requested without empty=1", writer, request)
		return
	}

	// Clear the database entries
	err := r.db.ClearTeams()
	if err != nil {
		logger.Error("Failed to clear all teams", log15.Ctx{"error": err})
		r.errorResponse(500, fmt.Sprintf("%v", err), writer, request)
		return
	}

	logger.Info("All teams deleted")
}
<commit_msg>askgod-server: Enforce proper country code<commit_after>package rest

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
	"strings"

	"github.com/gorilla/mux"
	"gopkg.in/inconshreveable/log15.v2"

	"github.com/nsec/askgod/api"
)

func (r *rest) getTeam(writer http.ResponseWriter, request *http.Request, logger log15.Logger) {
	// Extract the client IP
	ip, err := r.getIP(request)
	if err != nil {
		logger.Error("Failed to get the client's IP", log15.Ctx{"error": err})
		r.errorResponse(500, "Internal Server Error", writer, request)
		return
	}

	// Look for a matching team
	record, err := r.db.GetTeamForIP(*ip)
	if err == sql.ErrNoRows {
		logger.Warn("No team found for IP", log15.Ctx{"ip": ip.String()})
		r.errorResponse(404, "No team found for IP", writer, request)
		return
	} else if err != nil {
		logger.Error("Failed to get the team", log15.Ctx{"error": err})
		r.errorResponse(500, "Internal Server Error", writer, request)
		return
	}

	// Convert to the team view of a team
	team := api.Team{}
	team.ID = record.ID
	team.Name = record.Name
	team.Country = record.Country
	team.Website = record.Website

	r.jsonResponse(team, writer, request)
}

func (r *rest) updateTeam(writer http.ResponseWriter, request *http.Request, logger log15.Logger) {
	// Configuration validation
	if !r.config.Teams.SelfRegister {
		logger.Warn("Unauthorized attempt to self-register")
		r.errorResponse(403, "Team self-registration disabled", writer, request)
		return
	}

	// Decode the provided JSON input
	newTeam := api.TeamPut{}
	err := json.NewDecoder(request.Body).Decode(&newTeam)
	if err != nil {
		logger.Warn("Malformed JSON provided", log15.Ctx{"error": err})
		r.errorResponse(400, "Malformed JSON provided", writer, request)
		return
	}

	// Sanity checks
	validName := func(name string) bool {
		// Validate the length
		if len(name) < 1 || len(name) > 30 {
			return false
		}

		// Validate the character set
		match, _ := regexp.MatchString("^[a-zA-Z0-9 /\\\\~!@#$%&*()\\-_+={}\\[\\];:',.?]*$", name)
		if !match {
			return false
		}

		return true
	}

	if !validName(newTeam.Name) {
		logger.Warn("Bad team name", log15.Ctx{"name": newTeam.Name})
		r.errorResponse(400, "Bad team name", writer, request)
		return
	}

	match, _ := regexp.MatchString("^[A-Z]*$", newTeam.Country)
	if len(newTeam.Country) != 2 || !match {
		logger.Warn("Bad team country code", log15.Ctx{"country": newTeam.Country})
		r.errorResponse(400, "Bad team country code", writer, request)
		return
	}

	if newTeam.Website != "" {
		u, err := url.ParseRequestURI(newTeam.Website)
		if err != nil {
			logger.Warn("Bad team URL", log15.Ctx{"url": newTeam.Website})
			r.errorResponse(400, "Bad team URL", writer, request)
			return
		}

		newTeam.Website = u.String()
	}

	// Extract the client IP
	ip, err := r.getIP(request)
	if err != nil {
		logger.Error("Failed to get the client's IP", log15.Ctx{"error": err})
		r.errorResponse(500, "Internal Server Error", writer, request)
		return
	}

	// Look for a matching team
	team, err := r.db.GetTeamForIP(*ip)
	if err == sql.ErrNoRows {
		logger.Warn("No team found for IP", log15.Ctx{"ip": ip.String()})
		r.errorResponse(404, "No team found for IP", writer, request)
		return
	} else if err != nil {
		logger.Error("Failed to get the team", log15.Ctx{"error": err})
		r.errorResponse(500, "Internal Server Error", writer, request)
		return
	}

	// Validate the request
	if !r.config.Teams.SelfUpdate {
		if team.Name != "" && team.Name != newTeam.Name {
			logger.Debug("Unauthorized attempt to change already set team property", log15.Ctx{"property": "name"})
			r.errorResponse(400, "Team name is already set", writer, request)
			return
		}

		if team.Country != "" && team.Country != newTeam.Country {
			logger.Debug("Unauthorized attempt to change already set team property", log15.Ctx{"property": "country"})
			r.errorResponse(400, "Team country is already set", writer, request)
			return
		}

		if team.Website != "" && team.Website != newTeam.Website {
			logger.Debug("Unauthorized attempt to change already set team property", log15.Ctx{"property": "website"})
			r.errorResponse(400, "Team website is already set", writer, request)
			return
		}
	}

	// Setup the new record
	newRecord := api.AdminTeamPut{}
	newRecord.Name = newTeam.Name
	newRecord.Country = strings.ToUpper(newTeam.Country)
	newRecord.Website = newTeam.Website
	newRecord.Notes = team.Notes
	newRecord.Subnets = team.Subnets
	newRecord.Tags = team.Tags

	// Attempt to update the database
	err = r.db.UpdateTeam(team.ID, newRecord)
	if err != nil {
		logger.Error("Failed to update the team", log15.Ctx{"error": err})
		r.errorResponse(500, "Internal Server Error", writer, request)
		return
	}

	r.eventSend("timeline", api.EventTimeline{TeamID: team.ID, Team: &newRecord.TeamPut, Type: "team-updated"})
	logger.Info("Team updated", log15.Ctx{"id": team.ID, "name": newRecord.Name, "country": newRecord.Country, "website": newRecord.Website})
}

func (r *rest) adminGetTeams(writer http.ResponseWriter, request *http.Request, logger log15.Logger) {
	// Get all the teams from the database
	teams, err := r.db.GetTeams()
	if err != nil {
		logger.Error("Failed to query the team list", log15.Ctx{"error": err})
		r.errorResponse(500, fmt.Sprintf("%v", err), writer, request)
		return
	}

	r.jsonResponse(teams, writer, request)
}

func (r *rest) adminCreateTeam(writer http.ResponseWriter, request *http.Request, logger log15.Logger) {
	// Bulk create
	bulkVar := request.FormValue("bulk")
	if bulkVar == "1" {
		r.adminCreateTeams(writer, request, logger)
		return
	}

	// Decode the provided JSON input
	newTeam := api.AdminTeamPost{}
	err := json.NewDecoder(request.Body).Decode(&newTeam)
	if err != nil {
		logger.Warn("Malformed JSON provided", log15.Ctx{"error": err})
		r.errorResponse(400, "Malformed JSON provided", writer, request)
		return
	}

	// Attempt to create the database record
	id, err := r.db.CreateTeam(newTeam)
	if err != nil {
		logger.Error("Failed to create the team", log15.Ctx{"error": err})
		r.errorResponse(500, fmt.Sprintf("%v", err), writer, request)
		return
	}

	r.eventSend("timeline", api.EventTimeline{TeamID: id, Team: &newTeam.AdminTeamPut.TeamPut, Type: "team-added"})
	logger.Info("New team defined", log15.Ctx{"id": id, "subnets": newTeam.Subnets})
}

func (r *rest) adminCreateTeams(writer http.ResponseWriter, request *http.Request, logger log15.Logger) {
	// Decode the provided JSON input
	newTeams := []api.AdminTeamPost{}
	err := json.NewDecoder(request.Body).Decode(&newTeams)
	if err != nil {
		logger.Warn("Malformed JSON provided", log15.Ctx{"error": err})
		r.errorResponse(400, "Malformed JSON provided", writer, request)
		return
	}

	for _, team := range newTeams {
		// Attempt to create the database record
		id, err := r.db.CreateTeam(team)
		if err != nil {
			logger.Error("Failed to create the team", log15.Ctx{"error": err})
			r.errorResponse(500, fmt.Sprintf("%v", err), writer, request)
			return
		}

		r.eventSend("timeline", api.EventTimeline{TeamID: id, Team: &team.AdminTeamPut.TeamPut, Type: "team-added"})
		logger.Info("New team defined", log15.Ctx{"id": id, "subnets": team.Subnets})
	}
}

func (r *rest) adminGetTeam(writer http.ResponseWriter, request *http.Request, logger log15.Logger) {
	idVar := mux.Vars(request)["id"]

	// Convert the provided id to int
	id, err := strconv.ParseInt(idVar, 10, 64)
	if err != nil {
		logger.Warn("Invalid team ID provided", log15.Ctx{"id": idVar})
		r.errorResponse(400, "Invalid team ID provided", writer, request)
		return
	}

	// Attempt to get the DB record
	team, err := r.db.GetTeam(id)
	if err == sql.ErrNoRows {
		logger.Warn("Invalid team ID provided", log15.Ctx{"id": idVar})
		r.errorResponse(404, "Invalid team ID provided", writer, request)
		return
	} else if err != nil {
		logger.Error("Failed to get the team", log15.Ctx{"error": err})
		r.errorResponse(500, fmt.Sprintf("%v", err), writer, request)
		return
	}

	r.jsonResponse(team, writer, request)
}

func (r *rest) adminUpdateTeam(writer http.ResponseWriter, request *http.Request, logger log15.Logger) {
	idVar := mux.Vars(request)["id"]

	// Convert the provided id to int
	id, err := strconv.ParseInt(idVar, 10, 64)
	if err != nil {
		logger.Warn("Invalid team ID provided", log15.Ctx{"id": idVar})
		r.errorResponse(400, "Invalid team ID provided", writer, request)
		return
	}

	// Decode the provided JSON input
	newTeam := api.AdminTeamPut{}
	err = json.NewDecoder(request.Body).Decode(&newTeam)
	if err != nil {
		logger.Warn("Malformed JSON provided", log15.Ctx{"error": err})
		r.errorResponse(400, "Malformed JSON provided", writer, request)
		return
	}

	// Attempt to update the database
	err = r.db.UpdateTeam(id, newTeam)
	if err == sql.ErrNoRows {
		logger.Warn("Invalid team ID provided", log15.Ctx{"id": idVar})
		r.errorResponse(404, "Invalid team ID provided", writer, request)
		return
	} else if err != nil {
		logger.Error("Failed to update the team", log15.Ctx{"error": err})
		r.errorResponse(500, fmt.Sprintf("%v", err), writer, request)
		return
	}

	r.eventSend("timeline", api.EventTimeline{TeamID: id, Team: &newTeam.TeamPut, Type: "team-updated"})
	logger.Info("Team updated", log15.Ctx{"id": id, "name": newTeam.Name, "country": newTeam.Country, "website": newTeam.Website})
}

func (r *rest) adminDeleteTeam(writer http.ResponseWriter, request *http.Request, logger log15.Logger) {
	idVar := mux.Vars(request)["id"]

	// Convert the provided id to int
	id, err := strconv.ParseInt(idVar, 10, 64)
	if err != nil {
		logger.Warn("Invalid team ID provided", log15.Ctx{"id": idVar})
		r.errorResponse(400, "Invalid team ID provided", writer, request)
		return
	}

	// Attempt to get the DB record
	err = r.db.DeleteTeam(id)
	if err == sql.ErrNoRows {
		logger.Warn("Invalid team ID provided", log15.Ctx{"id": idVar})
		r.errorResponse(404, "Invalid team ID provided", writer, request)
		return
	} else if err != nil {
		logger.Error("Failed to delete the team", log15.Ctx{"error": err})
		r.errorResponse(500, fmt.Sprintf("%v", err), writer, request)
		return
	}

	r.eventSend("timeline", api.EventTimeline{TeamID: id, Type: "team-removed"})
	logger.Info("Team deleted", log15.Ctx{"id": id})
}

func (r *rest) adminClearTeams(writer http.ResponseWriter, request *http.Request, logger log15.Logger) {
	emptyVar := request.FormValue("empty")

	// Confirm the user is sure about it
	if emptyVar != "1" {
		logger.Warn("Teams clear requested without empty=1")
		r.errorResponse(400, "Teams clear requested without empty=1", writer, request)
		return
	}

	// Clear the database entries
	err := r.db.ClearTeams()
	if err != nil {
		logger.Error("Failed to clear all teams", log15.Ctx{"error": err})
		r.errorResponse(500, fmt.Sprintf("%v", err), writer, request)
		return
	}

	logger.Info("All teams deleted")
}
<|endoftext|>


<commit_before>package version

import (
	"fmt"
	"runtime"
)

const Binary = "1.2.0"

func String(app string) string {
	return fmt.Sprintf("%s v%s (built w/%s)", app, Binary, runtime.Version())
}
<commit_msg>bump v1.2.1-alpha<commit_after>package version

import (
	"fmt"
	"runtime"
)

const Binary = "1.2.1-alpha"

func String(app string) string {
	return fmt.Sprintf("%s v%s (built w/%s)", app, Binary, runtime.Version())
}
<|endoftext|>


<commit_before>package adhier

import (
	"fmt"
	"math"
	"reflect"
	"testing"

	"github.com/gomath/numan/basis/linhat"
	"github.com/gomath/numan/grid/newcot"
)

func assertEqual(actual, expected interface{}, t *testing.T) {
	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("got '%v' instead of '%v'", actual, expected)
	}
}

const epsilon = 1e-8

func assertAlmostEqual(actual, expected []float64, t *testing.T) {
	if len(actual) != len(expected) {
		goto error
	}

	for i := range actual {
		if math.Abs(actual[i]-expected[i]) > epsilon {
			goto error
		}
	}

	return

error:
	t.Fatalf("got '%v' instead of '%v'", actual, expected)
}

func TestConstructStep(t *testing.T) {
	algorithm := New(newcot.New(1), linhat.New(1), 1)
	algorithm.maxLevel = stepFixture.surrogate.level

	surrogate := algorithm.Construct(step)

	assertEqual(surrogate, stepFixture.surrogate, t)
}

func TestEvaluateStep(t *testing.T) {
	algorithm := New(newcot.New(1), linhat.New(1), 1)

	values := algorithm.Evaluate(stepFixture.surrogate, stepFixture.points)

	assertEqual(values, stepFixture.values, t)
}

func TestConstructCube(t *testing.T) {
	algorithm := New(newcot.New(2), linhat.New(2), 1)
	algorithm.maxLevel = cubeFixture.surrogate.level

	surrogate := algorithm.Construct(cube)

	assertEqual(surrogate, cubeFixture.surrogate, t)
}

func TestConstructBox(t *testing.T) {
	algorithm := New(newcot.New(2), linhat.New(2), 3)
	algorithm.maxLevel = boxFixture.surrogate.level

	surrogate := algorithm.Construct(box)

	assertEqual(surrogate, boxFixture.surrogate, t)
}

func TestEvaluateBox(t *testing.T) {
	algorithm := New(newcot.New(2), linhat.New(2), 3)

	values := algorithm.Evaluate(boxFixture.surrogate, boxFixture.points)

	assertAlmostEqual(values, boxFixture.values, t)
}

func BenchmarkHat(b *testing.B) {
	algorithm := New(newcot.New(1), linhat.New(1), 1)

	for i := 0; i < b.N; i++ {
		_ = algorithm.Construct(hat)
	}
}

func BenchmarkCube(b *testing.B) {
	algorithm := New(newcot.New(2), linhat.New(2), 1)

	for i := 0; i < b.N; i++ {
		_ = algorithm.Construct(cube)
	}
}

func BenchmarkBox(b *testing.B) {
	algorithm := New(newcot.New(2), linhat.New(2), 3)

	for i := 0; i < b.N; i++ {
		_ = algorithm.Construct(box)
	}
}

func BenchmarkMany(b *testing.B) {
	const (
		inCount  = 2
		outCount = 1000
	)

	algorithm := New(newcot.New(inCount), linhat.New(inCount), outCount)
	function := many(inCount, outCount)

	for i := 0; i < b.N; i++ {
		_ = algorithm.Construct(function)
	}
}

// A one-input-one-output scenario with a non-smooth function.
func ExampleSelf_step() {
	const (
		inCount  = 1
		outCount = 1
	)

	grid := newcot.New(inCount)
	basis := linhat.New(inCount)

	algorithm := New(grid, basis, outCount)
	algorithm.maxLevel = 19

	surrogate := algorithm.Construct(step)
	fmt.Println(surrogate)

	// Output:
	// Surrogate{ inputs: 1, outputs: 1, levels: 19, nodes: 38 }
}

// A one-input-one-output scenario with a smooth function.
func ExampleSelf_hat() {
	const (
		inCount  = 1
		outCount = 1
	)

	grid := newcot.New(inCount)
	basis := linhat.New(inCount)

	algorithm := New(grid, basis, outCount)
	algorithm.maxLevel = 9

	surrogate := algorithm.Construct(hat)
	fmt.Println(surrogate)

	// Output:
	// Surrogate{ inputs: 1, outputs: 1, levels: 9, nodes: 305 }
}

// A multiple-input-one-output scenario with a non-smooth function.
func ExampleSelf_cube() {
	const (
		inCount  = 2
		outCount = 1
	)

	grid := newcot.New(inCount)
	basis := linhat.New(inCount)

	algorithm := New(grid, basis, outCount)
	algorithm.maxLevel = 9

	surrogate := algorithm.Construct(cube)
	fmt.Println(surrogate)

	// Output:
	// Surrogate{ inputs: 2, outputs: 1, levels: 9, nodes: 377 }
}

// A multiple-input-many-output scenario with a non-smooth function.
func ExampleSelf_many() {
	const (
		inCount  = 2
		outCount = 1000
	)

	grid := newcot.New(inCount)
	basis := linhat.New(inCount)

	algorithm := New(grid, basis, outCount)

	surrogate := algorithm.Construct(many(inCount, outCount))
	fmt.Println(surrogate)

	// Output:
	// Surrogate{ inputs: 2, outputs: 1000, levels: 9, nodes: 362 }
}
<commit_msg>Updated links<commit_after>package adhier

import (
	"fmt"
	"math"
	"reflect"
	"testing"

	"github.com/go-math/numan/basis/linhat"
	"github.com/go-math/numan/grid/newcot"
)

func assertEqual(actual, expected interface{}, t *testing.T) {
	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("got '%v' instead of '%v'", actual, expected)
	}
}

const epsilon = 1e-8

func assertAlmostEqual(actual, expected []float64, t *testing.T) {
	if len(actual) != len(expected) {
		goto error
	}

	for i := range actual {
		if math.Abs(actual[i]-expected[i]) > epsilon {
			goto error
		}
	}

	return

error:
	t.Fatalf("got '%v' instead of '%v'", actual, expected)
}

func TestConstructStep(t *testing.T) {
	algorithm := New(newcot.New(1), linhat.New(1), 1)
	algorithm.maxLevel = stepFixture.surrogate.level

	surrogate := algorithm.Construct(step)

	assertEqual(surrogate, stepFixture.surrogate, t)
}

func TestEvaluateStep(t *testing.T) {
	algorithm := New(newcot.New(1), linhat.New(1), 1)

	values := algorithm.Evaluate(stepFixture.surrogate, stepFixture.points)

	assertEqual(values, stepFixture.values, t)
}

func TestConstructCube(t *testing.T) {
	algorithm := New(newcot.New(2), linhat.New(2), 1)
	algorithm.maxLevel = cubeFixture.surrogate.level

	surrogate := algorithm.Construct(cube)

	assertEqual(surrogate, cubeFixture.surrogate, t)
}

func TestConstructBox(t *testing.T) {
	algorithm := New(newcot.New(2), linhat.New(2), 3)
	algorithm.maxLevel = boxFixture.surrogate.level

	surrogate := algorithm.Construct(box)

	assertEqual(surrogate, boxFixture.surrogate, t)
}

func TestEvaluateBox(t *testing.T) {
	algorithm := New(newcot.New(2), linhat.New(2), 3)

	values := algorithm.Evaluate(boxFixture.surrogate, boxFixture.points)

	assertAlmostEqual(values, boxFixture.values, t)
}

func BenchmarkHat(b *testing.B) {
	algorithm := New(newcot.New(1), linhat.New(1), 1)

	for i := 0; i < b.N; i++ {
		_ = algorithm.Construct(hat)
	}
}

func BenchmarkCube(b *testing.B) {
	algorithm := New(newcot.New(2), linhat.New(2), 1)

	for i := 0; i < b.N; i++ {
		_ = algorithm.Construct(cube)
	}
}

func BenchmarkBox(b *testing.B) {
	algorithm := New(newcot.New(2), linhat.New(2), 3)

	for i := 0; i < b.N; i++ {
		_ = algorithm.Construct(box)
	}
}

func BenchmarkMany(b *testing.B) {
	const (
		inCount  = 2
		outCount = 1000
	)

	algorithm := New(newcot.New(inCount), linhat.New(inCount), outCount)
	function := many(inCount, outCount)

	for i := 0; i < b.N; i++ {
		_ = algorithm.Construct(function)
	}
}

// A one-input-one-output scenario with a non-smooth function.
func ExampleSelf_step() {
	const (
		inCount  = 1
		outCount = 1
	)

	grid := newcot.New(inCount)
	basis := linhat.New(inCount)

	algorithm := New(grid, basis, outCount)
	algorithm.maxLevel = 19

	surrogate := algorithm.Construct(step)
	fmt.Println(surrogate)

	// Output:
	// Surrogate{ inputs: 1, outputs: 1, levels: 19, nodes: 38 }
}

// A one-input-one-output scenario with a smooth function.
func ExampleSelf_hat() {
	const (
		inCount  = 1
		outCount = 1
	)

	grid := newcot.New(inCount)
	basis := linhat.New(inCount)

	algorithm := New(grid, basis, outCount)
	algorithm.maxLevel = 9

	surrogate := algorithm.Construct(hat)
	fmt.Println(surrogate)

	// Output:
	// Surrogate{ inputs: 1, outputs: 1, levels: 9, nodes: 305 }
}

// A multiple-input-one-output scenario with a non-smooth function.
func ExampleSelf_cube() {
	const (
		inCount  = 2
		outCount = 1
	)

	grid := newcot.New(inCount)
	basis := linhat.New(inCount)

	algorithm := New(grid, basis, outCount)
	algorithm.maxLevel = 9

	surrogate := algorithm.Construct(cube)
	fmt.Println(surrogate)

	// Output:
	// Surrogate{ inputs: 2, outputs: 1, levels: 9, nodes: 377 }
}

// A multiple-input-many-output scenario with a non-smooth function.
func ExampleSelf_many() {
	const (
		inCount  = 2
		outCount = 1000
	)

	grid := newcot.New(inCount)
	basis := linhat.New(inCount)

	algorithm := New(grid, basis, outCount)

	surrogate := algorithm.Construct(many(inCount, outCount))
	fmt.Println(surrogate)

	// Output:
	// Surrogate{ inputs: 2, outputs: 1000, levels: 9, nodes: 362 }
}
<|endoftext|>


<commit_before>/* https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/description/
Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list.

For example,
Given 1->2->3->3->4->4->5, return 1->2->5.
Given 1->1->1->2->3, return 2->3.
*/

package leetcode

func deleteDuplicates2(head *ListNode) *ListNode {
	var res, resCur *ListNode
	cur, curCount := head, 1
	for cur != nil {
		if cur.Next == nil || cur.Val != cur.Next.Val {
			if curCount == 1 {
				tmp := &ListNode{Val: cur.Val}
				if res == nil {
					res, resCur = tmp, tmp
				} else {
					resCur.Next, resCur = tmp, tmp
				}
			}
			curCount = 0
		}
		curCount++
		cur = cur.Next
	}
	return res
}
<commit_msg>add another thinking<commit_after>/* https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/description/
Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list.

For example,
Given 1->2->3->3->4->4->5, return 1->2->5.
Given 1->1->1->2->3, return 2->3.
*/

package leetcode

func deleteDuplicates2(head *ListNode) *ListNode {
	fakeHead := &ListNode{0, head}
	for cur := fakeHead; cur != nil; cur = cur.Next {
		for cur.Next != nil && cur.Next.Next != nil && cur.Next.Val == cur.Next.Next.Val {
			duplicateVal := cur.Next.Val
			// ignore duplicateVal
			for cur.Next != nil && cur.Next.Val == duplicateVal {
				// find no-repeat Next.Val
				cur.Next = cur.Next.Next
			}
		}
	}
	return fakeHead.Next
}

/*
func deleteDuplicates2(head *ListNode) *ListNode {
	var res, resCur *ListNode
	cur, curCount := head, 1
	for cur != nil {
		if cur.Next == nil || cur.Val != cur.Next.Val {
			if curCount == 1 {
				tmp := &ListNode{Val: cur.Val}
				if res == nil {
					res, resCur = tmp, tmp
				} else {
					resCur.Next, resCur = tmp, tmp
				}
			}
			curCount = 0
		}
		curCount++
		cur = cur.Next
	}
	return res
}
*/
<|endoftext|>


<commit_before>// weedo.go
package weedo

import (
	"bytes"
	"encoding/json"
	"errors"
	//"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"strconv"
	"strings"
)

const (
	DefaultMasterUrl = "http://localhost:9333"
	AssignUri        = "/dir/assign"
	UploadUri        = "/submit"
	LookupUri        = "/dir/lookup?volumeId="
)

type Client struct {
	Url     string
	volumes map[uint64]string
}

type assignResp struct {
	Count     int
	Fid       string
	Url       string
	PublicUrl string
	Size      int
	Error     string
}

type lookupResp struct {
	Locations []location
	Error     string
}

type location struct {
	Url       string
	PublicUrl string
}

type uploadResp struct {
	Fid      string
	FileName string
	FileUrl  string
	Size     int
	Error    string
}

var defaultClient Client

func init() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	defaultClient = Client{DefaultMasterUrl, make(map[uint64]string)}
}

func ParseFid(fid string) (id, key, cookie uint64, err error) {
	s := strings.Split(fid, ",")
	if len(s) != 2 {
		err = errors.New("Fid format invalid")
		log.Println(err)
		return
	}
	if id, err = strconv.ParseUint(s[0], 10, 32); err != nil {
		log.Println(err)
		return
	}

	return
}

func getUrl(c *Client, fid string) (url string, err error) {
	id, _, _, err := ParseFid(fid)
	if err != nil {
		log.Println(err)
		return
	}

	if url, err = c.lookup(id); err != nil {
		log.Println(err)
		return
	}

	url = "http://" + url + "/" + fid

	return
}

func GetUrl(fid string) (url string, err error) {
	return getUrl(&defaultClient, fid)
}

func assign(c *Client) (r *assignResp, err error) {
	resp, err := http.Get(c.Url + AssignUri)
	if err != nil {
		return
	}

	defer resp.Body.Close()

	assign := new(assignResp)
	decoder := json.NewDecoder(resp.Body)
	if err = decoder.Decode(assign); err != nil {
		log.Println(err)
		return
	}
	if assign.Error != "" {
		err = errors.New(assign.Error)
		log.Println(err)
		return
	}

	r = assign

	return
}

func upload(c *Client, filename string, content io.Reader) (r *uploadResp, err error) {
	buf := new(bytes.Buffer)
	writer := multipart.NewWriter(buf)

	part, err := writer.CreateFormFile("file", filename)
	if err != nil {
		log.Println(err)
		return
	}
	_, err = io.Copy(part, content)
	if err != nil {
		log.Println(err)
		return
	}

	contentType := writer.FormDataContentType()
	writer.Close()
	//fmt.Println(contentType)

	resp, err := http.Post(c.Url+UploadUri, contentType, buf)
	if err != nil {
		log.Println(err)
		return
	}
	defer resp.Body.Close()

	upload := new(uploadResp)
	decoder := json.NewDecoder(resp.Body)
	if err = decoder.Decode(upload); err != nil {
		log.Println(err)
		return
	}

	if upload.Error != "" {
		err = errors.New(upload.Error)
		log.Println(err)
		return
	}

	r = upload

	return
}

func Upload(filename string, content io.Reader) (fid string, size int, err error) {
	resp, err := upload(&defaultClient, filename, content)
	if err == nil {
		fid = resp.Fid
		size = resp.Size
	}
	return
}

func download(c *Client, fid string) (file io.ReadCloser, err error) {
	url, err := c.GetUrl(fid)
	if err != nil {
		return
	}

	resp, err := http.Get(url)
	if err != nil {
		return
	}

	//log.Println(resp.Header.Get("Content-Type"))
	if resp.ContentLength == 0 {
		return nil, errors.New("File Not Found")
	}

	return resp.Body, nil
}

func Download(fid string) (file io.ReadCloser, err error) {
	return download(&defaultClient, fid)
}

func delete(c *Client, fid string) (err error) {
	url, err := c.GetUrl(fid)
	if err != nil {
		return
	}

	request, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return
	}
	client := http.Client{}

	_, err = client.Do(request)
	if err != nil {
		return
	}
	return
}

func Delete(fid string) (err error) {
	return delete(&defaultClient, fid)
}

func NewClient(ip string, port int) *Client {
	masterUrl := "http://" + ip + ":" + strconv.Itoa(port)
	return &Client{masterUrl, make(map[uint64]string)}
}

func (c *Client) Upload(filename string, content io.Reader) (fid string, size int, err error) {
	resp, err := upload(c, filename, content)
	if err == nil {
		fid = resp.Fid
		size = resp.Size
	}
	return
}

func (c *Client) lookup(volumeId uint64) (url string, err error) {
	if v, ok := c.volumes[volumeId]; ok {
		return v, nil
	}

	resp, err := http.Get(c.Url + LookupUri + strconv.FormatUint(volumeId, 10))
	//log.Println(c.Url + LookupUri + strconv.FormatUint(volumeId, 10))
	if err != nil {
		return
	}
	defer resp.Body.Close()

	lookup := new(lookupResp)
	decoder := json.NewDecoder(resp.Body)
	if err = decoder.Decode(lookup); err != nil {
		log.Println(err)
		return
	}

	if lookup.Error != "" {
		err = errors.New(lookup.Error)
		log.Println(err)
		return
	}

	c.volumes[volumeId] = lookup.Locations[0].PublicUrl

	return lookup.Locations[0].PublicUrl, nil
}

func (c *Client) GetUrl(fid string) (url string, err error) {
	return getUrl(c, fid)
}

func (c *Client) Download(fid string) (file io.ReadCloser, err error) {
	return download(c, fid)
}

func (c *Client) Delete(fid string) (err error) {
	return delete(c, fid)
}
<commit_msg>add assign function<commit_after>// weedo.go
package weedo

import (
	"bytes"
	"encoding/json"
	"errors"
	//"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"strconv"
	"strings"
)

const (
	DefaultMasterUrl = "http://localhost:9333"
	AssignUri        = "/dir/assign"
	UploadUri        = "/submit"
	LookupUri        = "/dir/lookup?volumeId="
)

type Client struct {
	Url     string
	volumes map[uint64]string
}

type assignResp struct {
	Count     int
	Fid       string
	Url       string
	PublicUrl string
	Size      int
	Error     string
}

type lookupResp struct {
	Locations []location
	Error     string
}

type location struct {
	Url       string
	PublicUrl string
}

type uploadResp struct {
	Fid      string
	FileName string
	FileUrl  string
	Size     int
	Error    string
}

var defaultClient Client

func init() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	defaultClient = Client{DefaultMasterUrl, make(map[uint64]string)}
}

func ParseFid(fid string) (id, key, cookie uint64, err error) {
	s := strings.Split(fid, ",")
	if len(s) != 2 {
		err = errors.New("Fid format invalid")
		log.Println(err)
		return
	}
	if id, err = strconv.ParseUint(s[0], 10, 32); err != nil {
		log.Println(err)
		return
	}

	return
}

func getUrl(c *Client, fid string) (url string, err error) {
	id, _, _, err := ParseFid(fid)
	if err != nil {
		log.Println(err)
		return
	}

	if url, err = c.lookup(id); err != nil {
		log.Println(err)
		return
	}

	url = "http://" + url + "/" + fid

	return
}

func GetUrl(fid string) (url string, err error) {
	return getUrl(&defaultClient, fid)
}

func (c *Client) assign() (r *assignResp, err error) {
	resp, err := http.Get(c.Url + AssignUri)
	if err != nil {
		return
	}

	defer resp.Body.Close()

	assign := new(assignResp)
	decoder := json.NewDecoder(resp.Body)
	if err = decoder.Decode(assign); err != nil {
		log.Println(err)
		return
	}
	if assign.Error != "" {
		err = errors.New(assign.Error)
		log.Println(err)
		return
	}

	r = assign
	log.Printf("assign fid: %s at %s", r.Fid, r.Url)

	return
}

func AssignUpload(filename string, file io.Reader) (fid string, size int, err error) {
	r, err := defaultClient.assign()
	if err != nil {
		return
	}

	data, contentType, err := makeUploadContent(filename, file)
	if err != nil {
		return
	}

	url := "http://" + r.Url + "/" + r.Fid
	log.Println(url)

	resp, err := upload(&defaultClient, url, contentType, data)
	if err == nil {
		fid = r.Fid
		size = resp.Size
	}

	return
}

func makeUploadContent(filename string, content io.Reader) (data io.Reader, contentType string, err error) {
	buf := new(bytes.Buffer)
	writer := multipart.NewWriter(buf)

	part, err := writer.CreateFormFile("file", filename)
	if err != nil {
		log.Println(err)
		return
	}
	_, err = io.Copy(part, content)
	if err != nil {
		log.Println(err)
		return
	}

	data = buf
	contentType = writer.FormDataContentType()
	writer.Close()

	return
}

func upload(c *Client, url string, contentType string, data io.Reader) (r *uploadResp, err error) {
	resp, err := http.Post(url, contentType, data)
	if err != nil {
		log.Println(err)
		return
	}
	defer resp.Body.Close()

	upload := new(uploadResp)
	decoder := json.NewDecoder(resp.Body)
	if err = decoder.Decode(upload); err != nil {
		log.Println(err)
		return
	}

	if upload.Error != "" {
		err = errors.New(upload.Error)
		log.Println(err)
		return
	}

	r = upload

	return
}

func Upload(filename string, file io.Reader) (fid string, size int, err error) {
	data, contentType, err := makeUploadContent(filename, file)
	if err != nil {
		return
	}
	resp, err := upload(&defaultClient, defaultClient.Url+UploadUri, contentType, data)
	if err == nil {
		fid = resp.Fid
		size = resp.Size
	}
	return
}

func download(c *Client, fid string) (file io.ReadCloser, err error) {
	url, err := c.GetUrl(fid)
	if err != nil {
		return
	}

	resp, err := http.Get(url)
	if err != nil {
		return
	}

	//log.Println(resp.Header.Get("Content-Type"))
	if resp.ContentLength == 0 {
		return nil, errors.New("File Not Found")
	}

	return resp.Body, nil
}

func Download(fid string) (file io.ReadCloser, err error) {
	return download(&defaultClient, fid)
}

func delete(c *Client, fid string) (err error) {
	url, err := c.GetUrl(fid)
	if err != nil {
		return
	}

	request, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return
	}
	client := http.Client{}

	_, err = client.Do(request)
	if err != nil {
		return
	}
	return
}

func Delete(fid string) (err error) {
	return delete(&defaultClient, fid)
}

func NewClient(ip string, port int) *Client {
	masterUrl := "http://" + ip + ":" + strconv.Itoa(port)
	return &Client{masterUrl, make(map[uint64]string)}
}

func (c *Client) Upload(filename string, file io.Reader) (fid string, size int, err error) {
	data, contentType, err := makeUploadContent(filename, file)
	if err != nil {
		return
	}
	resp, err := upload(c, c.Url+UploadUri, contentType, data)
	if err == nil {
		fid = resp.Fid
		size = resp.Size
	}
	return
}

func (c *Client) lookup(volumeId uint64) (url string, err error) {
	if v, ok := c.volumes[volumeId]; ok {
		return v, nil
	}

	resp, err := http.Get(c.Url + LookupUri + strconv.FormatUint(volumeId, 10))
	//log.Println(c.Url + LookupUri + strconv.FormatUint(volumeId, 10))
	if err != nil {
		return
	}
	defer resp.Body.Close()

	lookup := new(lookupResp)
	decoder := json.NewDecoder(resp.Body)
	if err = decoder.Decode(lookup); err != nil {
		log.Println(err)
		return
	}

	if lookup.Error != "" {
		err = errors.New(lookup.Error)
		log.Println(err)
		return
	}

	c.volumes[volumeId] = lookup.Locations[0].Url

	return lookup.Locations[0].Url, nil
}

func (c *Client) GetUrl(fid string) (url string, err error) {
	return getUrl(c, fid)
}

func (c *Client) Download(fid string) (file io.ReadCloser, err error) {
	return download(c, fid)
}

func (c *Client) Delete(fid string) (err error) {
	return delete(c, fid)
}
<|endoftext|>


<commit_before>package main

import (
	"../GoLang/bubblesort"
	"../GoLang/exponenciacao"
	"../GoLang/fatorial"
	"../GoLang/fibonacci"
	"../GoLang/hanoi"
	"../GoLang/heapsort"
	"../GoLang/insertionsort"
	"../GoLang/mergesort"
	"../GoLang/selectionsort"
	"../GoLang/shellsort"
	"../GoLang/radixsort"
	"../GoLang/quicksort"
	"fmt"
)

func main() {
	fmt.Println("Fibonacci : ", fibonacci.Fibonacci(9))
	fmt.Println("Exponenciacao : ", exponenciacao.Exponenciacao(5, 5))
	fmt.Println("Fatorial : ", fatorial.Fatorial(6))
	fmt.Println("Hanoi : ")
	hanoi.Hanoi(0, 2, 1, 3)

	slice := []int{5, 2, 1, 6, 9, 8, 7, 3, 4}
	fmt.Println("Slice : ", slice)
  bubblesort.BubbleSort(slice)
	fmt.Println("BubbleSort : ", slice, "\n")

	slice = []int{1, 3, 2, 9, 7, 8, 5, 6, 4}
	fmt.Println("Slice : ", slice)
  selectionsort.SelectionSort(slice)
	fmt.Println("SelectionSort : ", slice, "\n")

	slice = []int{9, 8, 7, 5, 1, 2, 4, 3, 6}
	fmt.Println("Slice : ", slice)
  insertionsort.InsertionSort(slice)
	fmt.Println("InsertionSort : ", slice, "\n")

	slice = []int{4, 6, 7, 1, 2, 3, 5, 9, 8}
	fmt.Println("Slice : ", slice)
  mergesort.MergeSort(slice)
	fmt.Println("MergeSort : ", slice, "\n")

	slice = []int{5, 1, 4, 2, 3, 6, 9, 7, 8}
	fmt.Println("Slice : ", slice)
  shellsort.ShellSort(slice)
	fmt.Println("ShellSort : ", slice, "\n")

	slice = []int{6, 8, 7, 9, 1, 5, 4, 2, 3}
	fmt.Println("Slice : ", slice)
	heapsort.HeapSort1(slice)
	fmt.Println("HeapSort1 : ", slice, "\n")

	slice = []int{6, 8, 7, 9, 1, 5, 4, 2, 3}
	fmt.Println("Slice : ", slice)
  heapsort.HeapSort2(slice)
	fmt.Println("HeapSort2 : ", slice, "\n")

	slice = []int{3, 1, 5, 4, 2, 9, 6, 8, 7}
	fmt.Println("Slice : ", slice)
  radixsort.RadixSort(slice)
	fmt.Println("RadixSort : ", slice, "\n")

	slice = []int{2, 1, 8, 4, 3, 7, 6, 5, 9}
	fmt.Println("Slice : ", slice)
  quicksort.QuickSort(slice)
	fmt.Println("QuickSort : ", slice, "\n")
}
<commit_msg>Adicionado exemplo do CombSort em Go<commit_after>package main

import (
	"../GoLang/bubblesort"
	"../GoLang/exponenciacao"
	"../GoLang/fatorial"
	"../GoLang/fibonacci"
	"../GoLang/hanoi"
	"../GoLang/heapsort"
	"../GoLang/insertionsort"
	"../GoLang/mergesort"
	"../GoLang/selectionsort"
	"../GoLang/shellsort"
	"../GoLang/radixsort"
	"../GoLang/quicksort"
	"../GoLang/combsort"
	"fmt"
)

func main() {
	fmt.Println("Fibonacci : ", fibonacci.Fibonacci(9))
	fmt.Println("Exponenciacao : ", exponenciacao.Exponenciacao(5, 5))
	fmt.Println("Fatorial : ", fatorial.Fatorial(6))
	fmt.Println("Hanoi : ")
	hanoi.Hanoi(0, 2, 1, 3)

	slice := []int{5, 2, 1, 6, 9, 8, 7, 3, 4}
	fmt.Println("Slice : ", slice)
  bubblesort.BubbleSort(slice)
	fmt.Println("BubbleSort : ", slice, "\n")

	slice = []int{1, 3, 2, 9, 7, 8, 5, 6, 4}
	fmt.Println("Slice : ", slice)
  selectionsort.SelectionSort(slice)
	fmt.Println("SelectionSort : ", slice, "\n")

	slice = []int{9, 8, 7, 5, 1, 2, 4, 3, 6}
	fmt.Println("Slice : ", slice)
  insertionsort.InsertionSort(slice)
	fmt.Println("InsertionSort : ", slice, "\n")

	slice = []int{4, 6, 7, 1, 2, 3, 5, 9, 8}
	fmt.Println("Slice : ", slice)
  mergesort.MergeSort(slice)
	fmt.Println("MergeSort : ", slice, "\n")

	slice = []int{5, 1, 4, 2, 3, 6, 9, 7, 8}
	fmt.Println("Slice : ", slice)
  shellsort.ShellSort(slice)
	fmt.Println("ShellSort : ", slice, "\n")

	slice = []int{6, 8, 7, 9, 1, 5, 4, 2, 3}
	fmt.Println("Slice : ", slice)
	heapsort.HeapSort1(slice)
	fmt.Println("HeapSort1 : ", slice, "\n")

	slice = []int{6, 8, 7, 9, 1, 5, 4, 2, 3}
	fmt.Println("Slice : ", slice)
  heapsort.HeapSort2(slice)
	fmt.Println("HeapSort2 : ", slice, "\n")

	slice = []int{3, 1, 5, 4, 2, 9, 6, 8, 7}
	fmt.Println("Slice : ", slice)
  radixsort.RadixSort(slice)
	fmt.Println("RadixSort : ", slice, "\n")

	slice = []int{2, 1, 8, 4, 3, 7, 6, 5, 9}
	fmt.Println("Slice : ", slice)
  quicksort.QuickSort(slice)
	fmt.Println("QuickSort : ", slice, "\n")

	slice = []int{6, 5, 8, 7, 3, 4, 2, 1, 9}
	fmt.Println("Slice : ", slice)
  combsort.CombSort(slice)
	fmt.Println("CombSort : ", slice, "\n")
}
<|endoftext|>


<commit_before>// Package directory provides the GetAnnotator function, which returns an appropriate annotator for
// requests with a particular target date.
package directory

// A directory entry points to an appropriate CompositeAnnotator.
// The composite annotators will have:
//  1. An ASN annotator
//  2. Either
//     a. A Geolite2 annotator
//     b. A legacy v4 and legacy v6 annotator
//
// Once the ASN annotators are available, we will have a different CA for every date, but
// until then, we only have a different CA for each date where a new v4 or v6, or a new GL2
// annotator is available.
//
// To construct the directory, we begin with lists of Annotator objects for each type of annotation.
// We first merge the v4 and v6 annotators into a list of CompositeAnnotators, using MergeAnnotators.
// We then append all the GeoLite2 annotators to this list.
// Then, we merge the Geo annotation list with the ASN annotator list.
// Finally, we use BuildDirectory to create a Directory based on this list.

import (
	"errors"
	"time"

	"github.com/m-lab/annotation-service/api"
)

var (
	ErrEmptyDirectory = errors.New("Directory is empty")
	ErrNilAnnotator   = errors.New("Annotator is nil")
)

// Directory maintains a list of Annotators, indexed by date.
type Directory struct {
	// fields are immutable after construction using BuildDirectory()
	startDate  time.Time
	annotators []api.Annotator
}

func daysSince(ref time.Time, date time.Time) int {
	i := int(date.Unix()-ref.Unix()) / (24 * 3600)
	if i < 0 {
		return 0
	}
	return i
}

// GetAnnotator returns an appropriate api.Annotator for a given date.
func (d *Directory) GetAnnotator(date time.Time) (api.Annotator, error) {
	if len(d.annotators) < 1 {
		return nil, ErrEmptyDirectory
	}

	index := daysSince(d.startDate, date)
	if index >= len(d.annotators) {
		index = len(d.annotators) - 1
	}
	if d.annotators[index] == nil {
		return nil, ErrNilAnnotator
	}
	return d.annotators[index], nil
}

func (d *Directory) replace(ann api.Annotator) {
	date := ann.AnnotatorDate()

	// Use this for any date strictly after the AnnotatorDate...
	replaceAfter := daysSince(d.startDate, date)
	for i := replaceAfter; i < len(d.annotators); i++ {
		old := d.annotators[i]
		if old == nil {
			d.annotators[i] = ann
		} else {
			oldDate := old.AnnotatorDate()
			if oldDate.Before(date) {
				d.annotators[i] = ann
			}
		}

	}
}

// BuildDirectory builds a Directory object from a list of Annotators.
// TODO - how do we handle multiple lists of Annotators that should be merged?
func BuildDirectory(all []api.Annotator) *Directory {
	start := time.Now()

	for i := range all {
		if all[i] != nil && all[i].AnnotatorDate().Before(start) {
			start = all[i].AnnotatorDate()
		}
	}

	annotators := make([]api.Annotator, daysSince(start, time.Now()))

	dir := Directory{startDate: start, annotators: annotators}
	// NOTE: this would be slightly more efficient if done in reverse order.
	for i := range all {
		if all[i] != nil {
			dir.replace(all[i])
		}
	}

	return &dir
}

// Advance advances to the next date among the list elements.
func advance(lists [][]api.Annotator) ([][]api.Annotator, bool) {
	// Start far in the future.
	date := time.Now().Add(1000000 * time.Hour)
	first := -1
	for l, list := range lists {
		if len(list) > 1 {
			d := list[1].AnnotatorDate()
			if d.Before(date) {
				first = l
				date = d
			}
		}
	}
	if first == -1 {
		return nil, false
	}

	// Now advance any list that has the same target date.
	for l, list := range lists {
		if len(list) > 1 && list[1].AnnotatorDate().Equal(date) {
			lists[l] = list[1:]
		}
	}
	return lists, true
}

// MergeAnnotators merges multiple lists of annotators, and returns a list of CompositeAnnotators, each
// containing an appropriate annotator from each list.
func MergeAnnotators(lists [][]api.Annotator) []api.Annotator {
	listCount := len(lists)
	if listCount == 0 {
		return nil
	}
	if listCount == 1 {
		return lists[0]
	}

	groups := make([][]api.Annotator, 0, 100)

	// For each step, add a group, then advance the list(s) with earliest dates at second index.
	for more := true; more; {
		// Create and add group with first annotator from each list
		group := make([]api.Annotator, len(lists))
		for l, list := range lists {
			group[l] = list[0]
		}
		groups = append(groups, group)
		// Advance the lists that have earliest next elements.
		lists, more = advance(lists)
	}

	result := make([]api.Annotator, len(groups))
	for i, group := range groups {
		result[i] = api.NewCompositeAnnotator(group)
	}
	return result
}
<commit_msg>tweak comments<commit_after>// Package directory provides the GetAnnotator function, which returns an appropriate annotator for
// requests with a particular target date.
package directory

// A directory entry points to an appropriate CompositeAnnotator.
// The composite annotators will have:
//  1. An ASN annotator
//  2. Either
//     a. A Geolite2 annotator
//     b. A legacy v4 and legacy v6 annotator
//
// Once the ASN annotators are available, we will have a different CA for every date, but
// until then, we only have a different CA for each date where a new v4 or v6, or a new GL2
// annotator is available.
//
// To construct the directory, we begin with lists of Annotator objects for each type of annotation.
// We first merge the v4 and v6 annotators into a list of CompositeAnnotators, using MergeAnnotators.
// We then append all the GeoLite2 annotators to this list.
// Then, we merge the Geo annotation list with the ASN annotator list.
// Finally, we use BuildDirectory to create a Directory based on this list.

import (
	"errors"
	"time"

	"github.com/m-lab/annotation-service/api"
)

var (
	// These errors should never happen, unless there is a bug in our implementation.

	// ErrEmptyDirectory is returned by GetAnnotator if a Directory has no entries.
	ErrEmptyDirectory = errors.New("Directory is empty")
	// ErrNilAnnotator is returned if GetAnnotator encounters a nil Directory entry.
	ErrNilAnnotator = errors.New("Annotator is nil")
)

// Directory maintains a list of Annotators, indexed by date.
type Directory struct {
	// fields are immutable after construction using BuildDirectory()
	startDate  time.Time
	annotators []api.Annotator
}

func daysSince(ref time.Time, date time.Time) int {
	i := int(date.Unix()-ref.Unix()) / (24 * 3600)
	if i < 0 {
		return 0
	}
	return i
}

// GetAnnotator returns an appropriate api.Annotator for a given date.
func (d *Directory) GetAnnotator(date time.Time) (api.Annotator, error) {
	if len(d.annotators) < 1 {
		return nil, ErrEmptyDirectory
	}

	index := daysSince(d.startDate, date)
	if index >= len(d.annotators) {
		index = len(d.annotators) - 1
	}
	if d.annotators[index] == nil {
		return nil, ErrNilAnnotator
	}
	return d.annotators[index], nil
}

func (d *Directory) replace(ann api.Annotator) {
	date := ann.AnnotatorDate()

	// Use this for any date strictly after the AnnotatorDate...
	replaceAfter := daysSince(d.startDate, date)
	for i := replaceAfter; i < len(d.annotators); i++ {
		old := d.annotators[i]
		if old == nil {
			d.annotators[i] = ann
		} else {
			oldDate := old.AnnotatorDate()
			if oldDate.Before(date) {
				d.annotators[i] = ann
			}
		}

	}
}

// BuildDirectory builds a Directory object from a list of Annotators.
// TODO - how do we handle multiple lists of Annotators that should be merged?
func BuildDirectory(all []api.Annotator) *Directory {
	start := time.Now()

	for i := range all {
		if all[i] != nil && all[i].AnnotatorDate().Before(start) {
			start = all[i].AnnotatorDate()
		}
	}

	annotators := make([]api.Annotator, daysSince(start, time.Now()))

	dir := Directory{startDate: start, annotators: annotators}
	// NOTE: this would be slightly more efficient if done in reverse order.
	for i := range all {
		if all[i] != nil {
			dir.replace(all[i])
		}
	}

	return &dir
}

// Advance advances to the next date among the list elements.
func advance(lists [][]api.Annotator) ([][]api.Annotator, bool) {
	// Start far in the future.
	date := time.Now().Add(1000000 * time.Hour)
	first := -1
	for l, list := range lists {
		if len(list) > 1 {
			d := list[1].AnnotatorDate()
			if d.Before(date) {
				first = l
				date = d
			}
		}
	}
	if first == -1 {
		return nil, false
	}

	// Now advance any list that has the same target date.
	for l, list := range lists {
		if len(list) > 1 && list[1].AnnotatorDate().Equal(date) {
			lists[l] = list[1:]
		}
	}
	return lists, true
}

// MergeAnnotators merges multiple lists of annotators, and returns a list of CompositeAnnotators, each
// containing an appropriate annotator from each list.
func MergeAnnotators(lists [][]api.Annotator) []api.Annotator {
	listCount := len(lists)
	if listCount == 0 {
		return nil
	}
	if listCount == 1 {
		return lists[0]
	}

	groups := make([][]api.Annotator, 0, 100)

	// For each step, add a group, then advance the list(s) with earliest dates at second index.
	for more := true; more; {
		// Create and add group with first annotator from each list
		group := make([]api.Annotator, len(lists))
		for l, list := range lists {
			group[l] = list[0]
		}
		groups = append(groups, group)
		// Advance the lists that have earliest next elements.
		lists, more = advance(lists)
	}

	result := make([]api.Annotator, len(groups))
	for i, group := range groups {
		result[i] = api.NewCompositeAnnotator(group)
	}
	return result
}
<|endoftext|>


<commit_before>package gumbleffmpeg

import (
	"encoding/binary"
	"errors"
	"io"
	"os/exec"
	"strconv"
	"sync"
	"sync/atomic"
	"time"

	"github.com/layeh/gumble/gumble"
)

// State represents the state of a Stream.
type State int32

// Valid states of Stream.
const (
	StateInitial State = iota + 1
	StatePlaying
	StatePaused
	StateStopped
)

// Stream is an audio stream that encodes media through ffmpeg and sends it to
// the server.
//
// A stream can only be used once; it cannot be started after it is stopped.
type Stream struct {
	// Command to execute to play the file. Defaults to "ffmpeg".
	Command string
	// Playback volume (can be changed while the source is playing).
	Volume float32
	// Audio source (cannot be changed after stream starts).
	Source Source
	// Starting offset.
	Offset time.Duration

	client  *gumble.Client
	cmd     *exec.Cmd
	pipe    io.ReadCloser
	pause   chan struct{}
	elapsed int64

	state State

	l  sync.Mutex
	wg sync.WaitGroup
}

// New returns a new Stream for the given gumble Client and Source.
func New(client *gumble.Client, source Source) *Stream {
	return &Stream{
		client:  client,
		Volume:  1.0,
		Source:  source,
		Command: "ffmpeg",
		pause:   make(chan struct{}),
	}
}

// Play begins playing
func (s *Stream) Play() error {
	s.l.Lock()
	defer s.l.Unlock()

	switch s.state {
	case StatePaused:
		s.state = StatePlaying
		go s.process()
		return nil
	case StatePlaying:
		return errors.New("gumbleffmpeg: stream already playing")
	case StateStopped:
		return errors.New("gumbleffmpeg: stream has stopped")
	}

	// fresh stream
	if s.Source == nil {
		return errors.New("gumbleffmpeg: nil source")
	}

	args := s.Source.arguments()
	if s.Offset > 0 {
		args = append([]string{"-ss", strconv.FormatFloat(s.Offset.Seconds(), 'f', -1, 64)}, args...)
	}
	args = append(args, "-ac", strconv.Itoa(gumble.AudioChannels), "-ar", strconv.Itoa(gumble.AudioSampleRate), "-f", "s16le", "-")
	cmd := exec.Command(s.Command, args...)
	var err error
	s.pipe, err = cmd.StdoutPipe()
	if err != nil {
		return err
	}
	if err := s.Source.start(cmd); err != nil {
		return err
	}
	if err := cmd.Start(); err != nil {
		s.Source.done()
		return err
	}
	s.wg.Add(1)
	s.cmd = cmd
	go s.process()
	return nil
}

// State returns the state of the stream.
func (s *Stream) State() State {
	s.l.Lock()
	defer s.l.Unlock()
	return s.state
}

// Pause pauses a playing stream.
func (s *Stream) Pause() error {
	s.l.Lock()
	if s.state != StatePlaying {
		s.l.Unlock()
		return errors.New("gumbleffmpeg: stream is not playing")
	}
	s.state = StatePaused
	s.l.Unlock()
	s.pause <- struct{}{}
	return nil
}

// Stop stops the stream.
func (s *Stream) Stop() error {
	s.l.Lock()
	switch s.state {
	case StateStopped, StateInitial:
		s.l.Unlock()
		return errors.New("gumbleffmpeg: stream is not playing nor paused")
	}
	s.cleanup()
	s.Wait()
	return nil
}

// Wait returns once the stream has stopped playing.
func (s *Stream) Wait() {
	s.wg.Wait()
}

// Elapsed returns the amount of audio that has been played by the stream.
func (s *Stream) Elapsed() time.Duration {
	return time.Duration(atomic.LoadInt64(&s.elapsed))
}

func (s *Stream) process() {
	// s.state has been set to StatePlaying

	interval := s.client.Config.AudioInterval
	frameSize := s.client.Config.AudioFrameSize()

	int16Buffer := make([]int16, frameSize)
	byteBuffer := make([]byte, frameSize*2)

	outgoing := s.client.AudioOutgoing()
	defer close(outgoing)

	ticker := time.NewTicker(interval)
	defer ticker.Stop()

	for {
		select {
		case <-s.pause:
			return
		case <-ticker.C:
			// TODO: read an extra frame ahead so we know when to send terminating
			// bit.  This does not always work. we may need to just send a "flush"
			// packet.
			if _, err := io.ReadFull(s.pipe, byteBuffer); err != nil {
				s.l.Lock()
				s.cleanup()
				return
			}
			for i := range int16Buffer {
				float := float32(int16(binary.LittleEndian.Uint16(byteBuffer[i*2 : (i+1)*2])))
				int16Buffer[i] = int16(s.Volume * float)
			}
			atomic.AddInt64(&s.elapsed, int64(interval))
			outgoing <- gumble.AudioBuffer(int16Buffer)
		}
	}
}

func (s *Stream) cleanup() {
	defer s.l.Unlock()
	// s.l has been acquired
	if s.state == StateStopped {
		return
	}
	s.cmd.Process.Kill()
	s.cmd.Wait()
	s.Source.done()
	for len(s.pause) > 0 {
		<-s.pause
	}
	s.state = StateStopped
	s.wg.Done()
}
<commit_msg>fix missing state change to gumbleffmpeg<commit_after>package gumbleffmpeg

import (
	"encoding/binary"
	"errors"
	"io"
	"os/exec"
	"strconv"
	"sync"
	"sync/atomic"
	"time"

	"github.com/layeh/gumble/gumble"
)

// State represents the state of a Stream.
type State int32

// Valid states of Stream.
const (
	StateInitial State = iota + 1
	StatePlaying
	StatePaused
	StateStopped
)

// Stream is an audio stream that encodes media through ffmpeg and sends it to
// the server.
//
// A stream can only be used once; it cannot be started after it is stopped.
type Stream struct {
	// Command to execute to play the file. Defaults to "ffmpeg".
	Command string
	// Playback volume (can be changed while the source is playing).
	Volume float32
	// Audio source (cannot be changed after stream starts).
	Source Source
	// Starting offset.
	Offset time.Duration

	client  *gumble.Client
	cmd     *exec.Cmd
	pipe    io.ReadCloser
	pause   chan struct{}
	elapsed int64

	state State

	l  sync.Mutex
	wg sync.WaitGroup
}

// New returns a new Stream for the given gumble Client and Source.
func New(client *gumble.Client, source Source) *Stream {
	return &Stream{
		client:  client,
		Volume:  1.0,
		Source:  source,
		Command: "ffmpeg",
		pause:   make(chan struct{}),
	}
}

// Play begins playing
func (s *Stream) Play() error {
	s.l.Lock()
	defer s.l.Unlock()

	switch s.state {
	case StatePaused:
		s.state = StatePlaying
		go s.process()
		return nil
	case StatePlaying:
		return errors.New("gumbleffmpeg: stream already playing")
	case StateStopped:
		return errors.New("gumbleffmpeg: stream has stopped")
	}

	// fresh stream
	if s.Source == nil {
		return errors.New("gumbleffmpeg: nil source")
	}

	args := s.Source.arguments()
	if s.Offset > 0 {
		args = append([]string{"-ss", strconv.FormatFloat(s.Offset.Seconds(), 'f', -1, 64)}, args...)
	}
	args = append(args, "-ac", strconv.Itoa(gumble.AudioChannels), "-ar", strconv.Itoa(gumble.AudioSampleRate), "-f", "s16le", "-")
	cmd := exec.Command(s.Command, args...)
	var err error
	s.pipe, err = cmd.StdoutPipe()
	if err != nil {
		return err
	}
	if err := s.Source.start(cmd); err != nil {
		return err
	}
	if err := cmd.Start(); err != nil {
		s.Source.done()
		return err
	}
	s.wg.Add(1)
	s.cmd = cmd
	s.state = StatePlaying
	go s.process()
	return nil
}

// State returns the state of the stream.
func (s *Stream) State() State {
	s.l.Lock()
	defer s.l.Unlock()
	return s.state
}

// Pause pauses a playing stream.
func (s *Stream) Pause() error {
	s.l.Lock()
	if s.state != StatePlaying {
		s.l.Unlock()
		return errors.New("gumbleffmpeg: stream is not playing")
	}
	s.state = StatePaused
	s.l.Unlock()
	s.pause <- struct{}{}
	return nil
}

// Stop stops the stream.
func (s *Stream) Stop() error {
	s.l.Lock()
	switch s.state {
	case StateStopped, StateInitial:
		s.l.Unlock()
		return errors.New("gumbleffmpeg: stream is not playing nor paused")
	}
	s.cleanup()
	s.Wait()
	return nil
}

// Wait returns once the stream has stopped playing.
func (s *Stream) Wait() {
	s.wg.Wait()
}

// Elapsed returns the amount of audio that has been played by the stream.
func (s *Stream) Elapsed() time.Duration {
	return time.Duration(atomic.LoadInt64(&s.elapsed))
}

func (s *Stream) process() {
	// s.state has been set to StatePlaying

	interval := s.client.Config.AudioInterval
	frameSize := s.client.Config.AudioFrameSize()

	int16Buffer := make([]int16, frameSize)
	byteBuffer := make([]byte, frameSize*2)

	outgoing := s.client.AudioOutgoing()
	defer close(outgoing)

	ticker := time.NewTicker(interval)
	defer ticker.Stop()

	for {
		select {
		case <-s.pause:
			return
		case <-ticker.C:
			// TODO: read an extra frame ahead so we know when to send terminating
			// bit.  This does not always work. we may need to just send a "flush"
			// packet.
			if _, err := io.ReadFull(s.pipe, byteBuffer); err != nil {
				s.l.Lock()
				s.cleanup()
				return
			}
			for i := range int16Buffer {
				float := float32(int16(binary.LittleEndian.Uint16(byteBuffer[i*2 : (i+1)*2])))
				int16Buffer[i] = int16(s.Volume * float)
			}
			atomic.AddInt64(&s.elapsed, int64(interval))
			outgoing <- gumble.AudioBuffer(int16Buffer)
		}
	}
}

func (s *Stream) cleanup() {
	defer s.l.Unlock()
	// s.l has been acquired
	if s.state == StateStopped {
		return
	}
	s.cmd.Process.Kill()
	s.cmd.Wait()
	s.Source.done()
	for len(s.pause) > 0 {
		<-s.pause
	}
	s.state = StateStopped
	s.wg.Done()
}
<|endoftext|>


<commit_before>package benchmarkbbs_test

import (
	"crypto/rand"
	"database/sql"
	"errors"
	"flag"
	"fmt"
	"io"
	"log"
	"net/url"
	"os"
	"runtime"
	"strings"
	"time"

	"code.cloudfoundry.org/bbs"
	"code.cloudfoundry.org/bbs/db"
	"code.cloudfoundry.org/bbs/db/sqldb"
	"code.cloudfoundry.org/bbs/encryption"
	"code.cloudfoundry.org/bbs/format"
	"code.cloudfoundry.org/bbs/guidprovider"
	benchmarkconfig "code.cloudfoundry.org/benchmarkbbs/config"
	"code.cloudfoundry.org/benchmarkbbs/generator"
	"code.cloudfoundry.org/benchmarkbbs/reporter"
	"code.cloudfoundry.org/cfhttp"
	"code.cloudfoundry.org/clock"
	fakes "code.cloudfoundry.org/go-loggregator/testhelpers/fakes/v1"
	"code.cloudfoundry.org/lager"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3manager"
	"github.com/zorkian/go-datadog-api"

	_ "github.com/go-sql-driver/mysql"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"testing"
)

var (
	expectedLRPCount int

	expectedActualLRPCounts map[string]int
	config                  benchmarkconfig.BenchmarkBBSConfig

	logger          lager.Logger
	sqlDB           *sqldb.SQLDB
	activeDB        db.DB
	bbsClient       bbs.InternalClient
	dataDogClient   *datadog.Client
	dataDogReporter reporter.DataDogReporter
	reporters       []Reporter
)

const (
	DEBUG = "debug"
	INFO  = "info"
	ERROR = "error"
	FATAL = "fatal"
)

func init() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	configFile := flag.String("config", "", "config file")
	flag.Parse()

	var err error
	config, err = benchmarkconfig.NewBenchmarkBBSConfig(*configFile)
	if err != nil {
		panic(err)
	}

	if config.BBSAddress == "" {
		log.Fatal("bbsAddress is required")
	}

	BenchmarkTests(config.NumReps, config.NumTrials, config.LocalRouteEmitters)
}

func TestBenchmarkBbs(t *testing.T) {
	var lagerLogLevel lager.LogLevel
	switch config.LogLevel {
	case DEBUG:
		lagerLogLevel = lager.DEBUG
	case INFO:
		lagerLogLevel = lager.INFO
	case ERROR:
		lagerLogLevel = lager.ERROR
	case FATAL:
		lagerLogLevel = lager.FATAL
	default:
		panic(fmt.Errorf("unknown log level: %s", config.LogLevel))
	}

	var logWriter io.Writer
	if config.LogFilename == "" {
		logWriter = GinkgoWriter
	} else {
		logFile, err := os.OpenFile(config.LogFilename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
		if err != nil {
			panic("Error opening file, err: " + err.Error())
		}
		defer logFile.Close()

		logWriter = logFile
	}

	logger = lager.NewLogger("bbs-benchmarks-test")
	logger.RegisterSink(lager.NewWriterSink(logWriter, lagerLogLevel))

	reporters = []Reporter{}

	if config.DataDogAPIKey != "" && config.DataDogAppKey != "" {
		dataDogClient = datadog.NewClient(config.DataDogAPIKey, config.DataDogAppKey)
		dataDogReporter = reporter.NewDataDogReporter(logger, config.MetricPrefix, dataDogClient)
		reporters = append(reporters, &dataDogReporter)
	}

	if config.AwsAccessKeyID != "" && config.AwsSecretAccessKey != "" && config.AwsBucketName != "" {
		creds := credentials.NewStaticCredentials(config.AwsAccessKeyID, config.AwsSecretAccessKey, "")
		s3Client := s3.New(&aws.Config{
			Region:      &config.AwsRegion,
			Credentials: creds,
		})
		uploader := s3manager.NewUploader(&s3manager.UploadOptions{S3: s3Client})
		reporter := reporter.NewS3Reporter(logger, config.AwsBucketName, uploader)
		reporters = append(reporters, &reporter)
	}

	RegisterFailHandler(Fail)
	RunSpecsWithDefaultAndCustomReporters(t, "Benchmark BBS Suite", reporters)
}

type expectedLRPCounts struct {
	DesiredLRPCount int

	ActualLRPCounts map[string]int
}

func initializeActiveDB() *sql.DB {
	if activeDB != nil {
		return nil
	}

	if config.DatabaseConnectionString == "" {
		logger.Fatal("no-sql-configuration", errors.New("no-sql-configuration"))
	}

	if config.DatabaseDriver == "postgres" && !strings.Contains(config.DatabaseConnectionString, "sslmode") {
		config.DatabaseConnectionString = fmt.Sprintf("%s?sslmode=disable", config.DatabaseConnectionString)
	}

	sqlConn, err := sql.Open(config.DatabaseDriver, config.DatabaseConnectionString)
	if err != nil {
		logger.Fatal("failed-to-open-sql", err)
	}
	sqlConn.SetMaxOpenConns(1)
	sqlConn.SetMaxIdleConns(1)

	err = sqlConn.Ping()
	Expect(err).NotTo(HaveOccurred())

	sqlDB = initializeSQLDB(logger, sqlConn)
	activeDB = sqlDB
	return sqlConn
}

var _ = BeforeSuite(func() {
	bbsClient = initializeBBSClient(logger, time.Duration(config.BBSClientHTTPTimeout))

	conn := initializeActiveDB()

	if config.ReseedDatabase {
		var err error

		cleanupSQLDB(conn)

		if config.DesiredLRPs > 0 {
			desiredLRPGenerator := generator.NewDesiredLRPGenerator(
				config.ErrorTolerance,
				config.MetricPrefix,
				config.NumPopulateWorkers,
				bbsClient,
				dataDogClient,
			)

			expectedLRPCount, expectedActualLRPCounts, err = desiredLRPGenerator.Generate(logger, config.NumReps, config.DesiredLRPs)
			Expect(err).NotTo(HaveOccurred())
		}
	} else {
		expectedActualLRPCounts = make(map[string]int)
		query := `
			SELECT
				COUNT(*)
			FROM desired_lrps
		`
		res := conn.QueryRow(query)
		err := res.Scan(&expectedLRPCount)
		Expect(err).NotTo(HaveOccurred())

		query = `
			SELECT
				cell_id, COUNT(*)
			FROM actual_lrps
			GROUP BY cell_id
		`
		rows, err := conn.Query(query)
		Expect(err).NotTo(HaveOccurred())

		defer rows.Close()
		for rows.Next() {
			var cellId string
			var count int
			err = rows.Scan(&cellId, &count)
			Expect(err).NotTo(HaveOccurred())

			expectedActualLRPCounts[cellId] = count
		}
	}

	if float64(expectedLRPCount) < float64(config.DesiredLRPs)*config.ErrorTolerance {
		Fail(fmt.Sprintf("Error rate of %.3f for actuals exceeds tolerance of %.3f", float64(expectedLRPCount)/float64(config.DesiredLRPs), config.ErrorTolerance))
	}
})

func initializeSQLDB(logger lager.Logger, sqlConn *sql.DB) *sqldb.SQLDB {
	key, keys, err := config.EncryptionConfig.Parse()
	if err != nil {
		logger.Fatal("cannot-setup-encryption", err)
	}
	keyManager, err := encryption.NewKeyManager(key, keys)
	if err != nil {
		logger.Fatal("cannot-setup-encryption", err)
	}
	cryptor := encryption.NewCryptor(keyManager, rand.Reader)

	return sqldb.NewSQLDB(
		sqlConn,
		1000,
		1000,
		format.ENCODED_PROTO,
		cryptor,
		guidprovider.DefaultGuidProvider,
		clock.NewClock(),
		config.DatabaseDriver,
		&fakes.FakeIngressClient{},
	)
}

func initializeBBSClient(logger lager.Logger, bbsClientHTTPTimeout time.Duration) bbs.InternalClient {
	bbsURL, err := url.Parse(config.BBSAddress)
	if err != nil {
		logger.Fatal("Invalid BBS URL", err)
	}

	if bbsURL.Scheme != "https" {
		return bbs.NewClient(config.BBSAddress)
	}

	cfhttp.Initialize(bbsClientHTTPTimeout)
	var bbsClient bbs.InternalClient
	if config.SkipCertVerify {
		bbsClient, err = bbs.NewSecureSkipVerifyClient(
			config.BBSAddress,
			config.BBSClientCert,
			config.BBSClientKey,
			1,
			25000,
		)
	} else {
		bbsClient, err = bbs.NewSecureClient(
			config.BBSAddress,
			config.BBSCACert,
			config.BBSClientCert,
			config.BBSClientKey,
			1,
			25000,
		)
	}
	if err != nil {
		logger.Fatal("Failed to configure secure BBS client", err)
	}
	return bbsClient
}

func cleanupSQLDB(conn *sql.DB) {
	_, err := conn.Exec("TRUNCATE actual_lrps")
	Expect(err).NotTo(HaveOccurred())
	_, err = conn.Exec("TRUNCATE desired_lrps")
	Expect(err).NotTo(HaveOccurred())
}
<commit_msg>Update package name for fakes<commit_after>package benchmarkbbs_test

import (
	"crypto/rand"
	"database/sql"
	"errors"
	"flag"
	"fmt"
	"io"
	"log"
	"net/url"
	"os"
	"runtime"
	"strings"
	"time"

	"code.cloudfoundry.org/bbs"
	"code.cloudfoundry.org/bbs/db"
	"code.cloudfoundry.org/bbs/db/sqldb"
	"code.cloudfoundry.org/bbs/encryption"
	"code.cloudfoundry.org/bbs/format"
	"code.cloudfoundry.org/bbs/guidprovider"
	benchmarkconfig "code.cloudfoundry.org/benchmarkbbs/config"
	"code.cloudfoundry.org/benchmarkbbs/generator"
	"code.cloudfoundry.org/benchmarkbbs/reporter"
	"code.cloudfoundry.org/cfhttp"
	"code.cloudfoundry.org/clock"
	fakes "code.cloudfoundry.org/diego-logging-client/testhelpers"
	"code.cloudfoundry.org/lager"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3manager"
	"github.com/zorkian/go-datadog-api"

	_ "github.com/go-sql-driver/mysql"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"testing"
)

var (
	expectedLRPCount int

	expectedActualLRPCounts map[string]int
	config                  benchmarkconfig.BenchmarkBBSConfig

	logger          lager.Logger
	sqlDB           *sqldb.SQLDB
	activeDB        db.DB
	bbsClient       bbs.InternalClient
	dataDogClient   *datadog.Client
	dataDogReporter reporter.DataDogReporter
	reporters       []Reporter
)

const (
	DEBUG = "debug"
	INFO  = "info"
	ERROR = "error"
	FATAL = "fatal"
)

func init() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	configFile := flag.String("config", "", "config file")
	flag.Parse()

	var err error
	config, err = benchmarkconfig.NewBenchmarkBBSConfig(*configFile)
	if err != nil {
		panic(err)
	}

	if config.BBSAddress == "" {
		log.Fatal("bbsAddress is required")
	}

	BenchmarkTests(config.NumReps, config.NumTrials, config.LocalRouteEmitters)
}

func TestBenchmarkBbs(t *testing.T) {
	var lagerLogLevel lager.LogLevel
	switch config.LogLevel {
	case DEBUG:
		lagerLogLevel = lager.DEBUG
	case INFO:
		lagerLogLevel = lager.INFO
	case ERROR:
		lagerLogLevel = lager.ERROR
	case FATAL:
		lagerLogLevel = lager.FATAL
	default:
		panic(fmt.Errorf("unknown log level: %s", config.LogLevel))
	}

	var logWriter io.Writer
	if config.LogFilename == "" {
		logWriter = GinkgoWriter
	} else {
		logFile, err := os.OpenFile(config.LogFilename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
		if err != nil {
			panic("Error opening file, err: " + err.Error())
		}
		defer logFile.Close()

		logWriter = logFile
	}

	logger = lager.NewLogger("bbs-benchmarks-test")
	logger.RegisterSink(lager.NewWriterSink(logWriter, lagerLogLevel))

	reporters = []Reporter{}

	if config.DataDogAPIKey != "" && config.DataDogAppKey != "" {
		dataDogClient = datadog.NewClient(config.DataDogAPIKey, config.DataDogAppKey)
		dataDogReporter = reporter.NewDataDogReporter(logger, config.MetricPrefix, dataDogClient)
		reporters = append(reporters, &dataDogReporter)
	}

	if config.AwsAccessKeyID != "" && config.AwsSecretAccessKey != "" && config.AwsBucketName != "" {
		creds := credentials.NewStaticCredentials(config.AwsAccessKeyID, config.AwsSecretAccessKey, "")
		s3Client := s3.New(&aws.Config{
			Region:      &config.AwsRegion,
			Credentials: creds,
		})
		uploader := s3manager.NewUploader(&s3manager.UploadOptions{S3: s3Client})
		reporter := reporter.NewS3Reporter(logger, config.AwsBucketName, uploader)
		reporters = append(reporters, &reporter)
	}

	RegisterFailHandler(Fail)
	RunSpecsWithDefaultAndCustomReporters(t, "Benchmark BBS Suite", reporters)
}

type expectedLRPCounts struct {
	DesiredLRPCount int

	ActualLRPCounts map[string]int
}

func initializeActiveDB() *sql.DB {
	if activeDB != nil {
		return nil
	}

	if config.DatabaseConnectionString == "" {
		logger.Fatal("no-sql-configuration", errors.New("no-sql-configuration"))
	}

	if config.DatabaseDriver == "postgres" && !strings.Contains(config.DatabaseConnectionString, "sslmode") {
		config.DatabaseConnectionString = fmt.Sprintf("%s?sslmode=disable", config.DatabaseConnectionString)
	}

	sqlConn, err := sql.Open(config.DatabaseDriver, config.DatabaseConnectionString)
	if err != nil {
		logger.Fatal("failed-to-open-sql", err)
	}
	sqlConn.SetMaxOpenConns(1)
	sqlConn.SetMaxIdleConns(1)

	err = sqlConn.Ping()
	Expect(err).NotTo(HaveOccurred())

	sqlDB = initializeSQLDB(logger, sqlConn)
	activeDB = sqlDB
	return sqlConn
}

var _ = BeforeSuite(func() {
	bbsClient = initializeBBSClient(logger, time.Duration(config.BBSClientHTTPTimeout))

	conn := initializeActiveDB()

	if config.ReseedDatabase {
		var err error

		cleanupSQLDB(conn)

		if config.DesiredLRPs > 0 {
			desiredLRPGenerator := generator.NewDesiredLRPGenerator(
				config.ErrorTolerance,
				config.MetricPrefix,
				config.NumPopulateWorkers,
				bbsClient,
				dataDogClient,
			)

			expectedLRPCount, expectedActualLRPCounts, err = desiredLRPGenerator.Generate(logger, config.NumReps, config.DesiredLRPs)
			Expect(err).NotTo(HaveOccurred())
		}
	} else {
		expectedActualLRPCounts = make(map[string]int)
		query := `
			SELECT
				COUNT(*)
			FROM desired_lrps
		`
		res := conn.QueryRow(query)
		err := res.Scan(&expectedLRPCount)
		Expect(err).NotTo(HaveOccurred())

		query = `
			SELECT
				cell_id, COUNT(*)
			FROM actual_lrps
			GROUP BY cell_id
		`
		rows, err := conn.Query(query)
		Expect(err).NotTo(HaveOccurred())

		defer rows.Close()
		for rows.Next() {
			var cellId string
			var count int
			err = rows.Scan(&cellId, &count)
			Expect(err).NotTo(HaveOccurred())

			expectedActualLRPCounts[cellId] = count
		}
	}

	if float64(expectedLRPCount) < float64(config.DesiredLRPs)*config.ErrorTolerance {
		Fail(fmt.Sprintf("Error rate of %.3f for actuals exceeds tolerance of %.3f", float64(expectedLRPCount)/float64(config.DesiredLRPs), config.ErrorTolerance))
	}
})

func initializeSQLDB(logger lager.Logger, sqlConn *sql.DB) *sqldb.SQLDB {
	key, keys, err := config.EncryptionConfig.Parse()
	if err != nil {
		logger.Fatal("cannot-setup-encryption", err)
	}
	keyManager, err := encryption.NewKeyManager(key, keys)
	if err != nil {
		logger.Fatal("cannot-setup-encryption", err)
	}
	cryptor := encryption.NewCryptor(keyManager, rand.Reader)

	return sqldb.NewSQLDB(
		sqlConn,
		1000,
		1000,
		format.ENCODED_PROTO,
		cryptor,
		guidprovider.DefaultGuidProvider,
		clock.NewClock(),
		config.DatabaseDriver,
		&fakes.FakeIngressClient{},
	)
}

func initializeBBSClient(logger lager.Logger, bbsClientHTTPTimeout time.Duration) bbs.InternalClient {
	bbsURL, err := url.Parse(config.BBSAddress)
	if err != nil {
		logger.Fatal("Invalid BBS URL", err)
	}

	if bbsURL.Scheme != "https" {
		return bbs.NewClient(config.BBSAddress)
	}

	cfhttp.Initialize(bbsClientHTTPTimeout)
	var bbsClient bbs.InternalClient
	if config.SkipCertVerify {
		bbsClient, err = bbs.NewSecureSkipVerifyClient(
			config.BBSAddress,
			config.BBSClientCert,
			config.BBSClientKey,
			1,
			25000,
		)
	} else {
		bbsClient, err = bbs.NewSecureClient(
			config.BBSAddress,
			config.BBSCACert,
			config.BBSClientCert,
			config.BBSClientKey,
			1,
			25000,
		)
	}
	if err != nil {
		logger.Fatal("Failed to configure secure BBS client", err)
	}
	return bbsClient
}

func cleanupSQLDB(conn *sql.DB) {
	_, err := conn.Exec("TRUNCATE actual_lrps")
	Expect(err).NotTo(HaveOccurred())
	_, err = conn.Exec("TRUNCATE desired_lrps")
	Expect(err).NotTo(HaveOccurred())
}
<|endoftext|>