<commit_before>// Copyright (c) 2014 Stefan Schroeder, NY, 2014-04-13
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file

package main

import (
	"flag"
	"github.com/StefanSchroeder/gocal"
	"strconv"
	"time"
	"os"
	"fmt"
)

const VERSION = "0.9 the Unready"

var optFont = flag.String("font", "serif", "Font")
var optFontScale = flag.Float64("fontscale", 1.0, "Font")
var optFooter = flag.String("footer", "Gocal", "Footer note")
var optHideDOY = flag.Bool("nodoy", false, "Hide day of year (false)")
var optPlain = flag.Bool("plain", false, "Hide everything")
var optHideEvents = flag.Bool("noevents", false, "Hide events from config file (false)")
var optHideMoon = flag.Bool("nomoon", false, "Hide moon phases (false)")
var optHideWeek = flag.Bool("noweek", false, "Hide week number (false)")
var optLocale = flag.String("lang", "", "Language")
var optOrientation = flag.String("p", "P", "Orientation (L)andscape/(P)ortrait")
var optPaper = flag.String("paper", "A4", "Paper format (A3 A4 A5 Letter Legal)")
var optPhoto = flag.String("photo", "", "Show photo (single image PNG JPG GIF)")
var optConfig = flag.String("config", "gocal.xml", "Configuration file")
var optPhotos = flag.String("photos", "", "Show photos (directory PNG JPG GIF)")
var optWallpaper = flag.String("wall", "", "Show wallpaper PNG JPG GIF")
var outfilename = flag.String("o", "output.pdf", "Output filename")
var optSmall = flag.Bool("small", false, "Smaller fonts")
var optHideOtherMonths = flag.Bool("noother", false, "Hide neighboring month days")
var optNocolor = flag.Bool("nocolor", false, "Sundays and Saturdays in black, instead of red.")
var optYearA = flag.Bool("yearA", false, "Year calendar (design A)")
var optYearB = flag.Bool("yearB", false, "Year calendar (design B)")
var optCheckers = flag.Bool("checker", false, "Fill grid with checkerboard.")
var optFillpattern = flag.String("fill", "", "Set grid fill pattern.")
var optVersion = flag.Bool("v", false, "Version.")

func main() {
	flag.Parse()

  if *optVersion {
    fmt.Printf("# Gocal version %s\n", VERSION)
    os.Exit(0)
  }

	wantyear := int(time.Now().Year())
	beginmonth := 1
	endmonth := 12

	if flag.NArg() == 1 {
		dummyyear, _ := strconv.ParseInt(flag.Arg(0), 10, 32)
		wantyear = int(dummyyear)
	} else if flag.NArg() == 2 {
		dummymonth, _ := strconv.ParseInt(flag.Arg(0), 10, 32)
		dummyyear, _ := strconv.ParseInt(flag.Arg(1), 10, 32)
		beginmonth = int(dummymonth)
		endmonth = int(dummymonth)
		wantyear = int(dummyyear)
	} else if flag.NArg() == 3 {
		dummymonthBegin, _ := strconv.ParseInt(flag.Arg(0), 10, 32)
		dummymonthEnd, _ := strconv.ParseInt(flag.Arg(1), 10, 32)
		dummyyear, _ := strconv.ParseInt(flag.Arg(2), 10, 32)
		beginmonth = int(dummymonthBegin)
		endmonth = int(dummymonthEnd)
		wantyear = int(dummyyear)
	}

	g := gocal.New(beginmonth, endmonth, wantyear)
	g.SetFont(*optFont)
	g.SetOrientation(*optOrientation)
	g.SetPaperformat(*optPaper)
	g.SetLocale(*optLocale)
	g.SetConfig(*optConfig)
	if *optPlain == true {
		g.SetPlain()
	}
	if *optHideDOY == true {
		g.SetHideDOY()
	}
	if *optHideWeek == true {
		g.SetHideWeek()
	}
	if *optHideMoon == true {
		g.SetHideMoon()
	}
	if *optSmall == true {
		g.SetSmall()
	}
	if *optNocolor == true {
		g.SetNocolor()
	}
	if *optHideOtherMonths == true {
		g.SetHideOtherMonth()
	}
	g.SetFontScale(*optFontScale)
	g.SetWallpaper(*optWallpaper)
	g.SetPhotos(*optPhotos)
	g.SetPhoto(*optPhoto)
	g.SetFooter(*optFooter)
	g.SetFillpattern(*optFillpattern)
	/*
	  g.AddEvent(31, 1, "one", "")
	  g.AddEvent(28, 2, "two", "")
	  g.AddEvent(31, 3, "three", "")
	*/
  if *optYearA == true {
    g.CreateYearCalendar(*outfilename)
  } else if *optYearB == true {
    g.CreateYearCalendarInverse(*outfilename)
  } else {
	  g.CreateCalendar(*outfilename)
  }
}
<commit_msg>Suggestions of Goreportcard<commit_after>// Copyright (c) 2014 Stefan Schroeder, NY, 2014-04-13
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file

package main

import (
	"flag"
	"fmt"
	"github.com/StefanSchroeder/gocal"
	"os"
	"strconv"
	"time"
)

const VERSION = "0.9 the Unready"

var optFont = flag.String("font", "serif", "Font")
var optFontScale = flag.Float64("fontscale", 1.0, "Font")
var optFooter = flag.String("footer", "Gocal", "Footer note")
var optHideDOY = flag.Bool("nodoy", false, "Hide day of year (false)")
var optPlain = flag.Bool("plain", false, "Hide everything")
var optHideEvents = flag.Bool("noevents", false, "Hide events from config file (false)")
var optHideMoon = flag.Bool("nomoon", false, "Hide moon phases (false)")
var optHideWeek = flag.Bool("noweek", false, "Hide week number (false)")
var optLocale = flag.String("lang", "", "Language")
var optOrientation = flag.String("p", "P", "Orientation (L)andscape/(P)ortrait")
var optPaper = flag.String("paper", "A4", "Paper format (A3 A4 A5 Letter Legal)")
var optPhoto = flag.String("photo", "", "Show photo (single image PNG JPG GIF)")
var optConfig = flag.String("config", "gocal.xml", "Configuration file")
var optPhotos = flag.String("photos", "", "Show photos (directory PNG JPG GIF)")
var optWallpaper = flag.String("wall", "", "Show wallpaper PNG JPG GIF")
var outfilename = flag.String("o", "output.pdf", "Output filename")
var optSmall = flag.Bool("small", false, "Smaller fonts")
var optHideOtherMonths = flag.Bool("noother", false, "Hide neighboring month days")
var optNocolor = flag.Bool("nocolor", false, "Sundays and Saturdays in black, instead of red.")
var optYearA = flag.Bool("yearA", false, "Year calendar (design A)")
var optYearB = flag.Bool("yearB", false, "Year calendar (design B)")
var optCheckers = flag.Bool("checker", false, "Fill grid with checkerboard.")
var optFillpattern = flag.String("fill", "", "Set grid fill pattern.")
var optVersion = flag.Bool("v", false, "Version.")

func main() {
	flag.Parse()

	if *optVersion {
		fmt.Printf("# Gocal version %s\n", VERSION)
		os.Exit(0)
	}

	wantyear := int(time.Now().Year())
	beginmonth := 1
	endmonth := 12

	if flag.NArg() == 1 {
		dummyyear, _ := strconv.ParseInt(flag.Arg(0), 10, 32)
		wantyear = int(dummyyear)
	} else if flag.NArg() == 2 {
		dummymonth, _ := strconv.ParseInt(flag.Arg(0), 10, 32)
		dummyyear, _ := strconv.ParseInt(flag.Arg(1), 10, 32)
		beginmonth = int(dummymonth)
		endmonth = int(dummymonth)
		wantyear = int(dummyyear)
	} else if flag.NArg() == 3 {
		dummymonthBegin, _ := strconv.ParseInt(flag.Arg(0), 10, 32)
		dummymonthEnd, _ := strconv.ParseInt(flag.Arg(1), 10, 32)
		dummyyear, _ := strconv.ParseInt(flag.Arg(2), 10, 32)
		beginmonth = int(dummymonthBegin)
		endmonth = int(dummymonthEnd)
		wantyear = int(dummyyear)
	}

	g := gocal.New(beginmonth, endmonth, wantyear)
	g.SetFont(*optFont)
	g.SetOrientation(*optOrientation)
	g.SetPaperformat(*optPaper)
	g.SetLocale(*optLocale)
	g.SetConfig(*optConfig)
	if *optPlain == true {
		g.SetPlain()
	}
	if *optHideDOY == true {
		g.SetHideDOY()
	}
	if *optHideWeek == true {
		g.SetHideWeek()
	}
	if *optHideMoon == true {
		g.SetHideMoon()
	}
	if *optSmall == true {
		g.SetSmall()
	}
	if *optNocolor == true {
		g.SetNocolor()
	}
	if *optHideOtherMonths == true {
		g.SetHideOtherMonth()
	}
	g.SetFontScale(*optFontScale)
	g.SetWallpaper(*optWallpaper)
	g.SetPhotos(*optPhotos)
	g.SetPhoto(*optPhoto)
	g.SetFooter(*optFooter)
	g.SetFillpattern(*optFillpattern)
	/*
	  g.AddEvent(31, 1, "one", "")
	  g.AddEvent(28, 2, "two", "")
	  g.AddEvent(31, 3, "three", "")
	*/
	if *optYearA == true {
		g.CreateYearCalendar(*outfilename)
	} else if *optYearB == true {
		g.CreateYearCalendarInverse(*outfilename)
	} else {
		g.CreateCalendar(*outfilename)
	}
}
<|endoftext|>


<commit_before>// Copyright © 2017 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package router

import (
	"fmt"
	"math"
	"strings"
	"time"

	pb_broker "github.com/TheThingsNetwork/api/broker"
	pb_gateway "github.com/TheThingsNetwork/api/gateway"
	"github.com/TheThingsNetwork/api/logfields"
	pb_protocol "github.com/TheThingsNetwork/api/protocol"
	pb_lorawan "github.com/TheThingsNetwork/api/protocol/lorawan"
	pb "github.com/TheThingsNetwork/api/router"
	"github.com/TheThingsNetwork/api/trace"
	ttnlog "github.com/TheThingsNetwork/go-utils/log"
	"github.com/TheThingsNetwork/ttn/core/band"
	"github.com/TheThingsNetwork/ttn/core/router/gateway"
	"github.com/TheThingsNetwork/ttn/core/types"
	"github.com/TheThingsNetwork/ttn/utils/errors"
	"github.com/TheThingsNetwork/ttn/utils/toa"
)

func (r *router) SubscribeDownlink(gatewayID string, subscriptionID string) (<-chan *pb.DownlinkMessage, error) {
	ctx := r.Ctx.WithFields(ttnlog.Fields{
		"GatewayID": gatewayID,
	})

	gateway := r.getGateway(gatewayID)
	if fromSchedule := gateway.Schedule.Subscribe(subscriptionID); fromSchedule != nil {
		r.Discovery.AddGatewayID(gatewayID, gateway.Token())
		toGateway := make(chan *pb.DownlinkMessage)
		go func() {
			ctx.Debug("Activate downlink")
			for message := range fromSchedule {
				ctx.WithFields(logfields.ForMessage(message)).Debug("Send downlink")
				toGateway <- message
				if gateway.MonitorStream != nil {
					clone := *message // There can be multiple subscribers
					clone.Trace = clone.Trace.WithEvent(trace.SendEvent)
					gateway.MonitorStream.Send(&clone)
				}
			}
			ctx.Debug("Deactivate downlink")
			close(toGateway)
		}()
		return toGateway, nil
	}
	return nil, errors.NewErrInternal(fmt.Sprintf("Already subscribed to downlink for %s", gatewayID))
}

func (r *router) UnsubscribeDownlink(gatewayID string, subscriptionID string) error {
	gateway := r.getGateway(gatewayID)
	r.Discovery.RemoveGatewayID(gatewayID, gateway.Token())
	gateway.Schedule.Stop(subscriptionID)
	return nil
}

func (r *router) HandleDownlink(downlink *pb_broker.DownlinkMessage) (err error) {
	var gateway *gateway.Gateway

	r.RegisterReceived(downlink)
	defer func() {
		if err != nil {
			downlink.Trace = downlink.Trace.WithEvent(trace.DropEvent, "reason", err)
			if gateway != nil && gateway.MonitorStream != nil {
				gateway.MonitorStream.Send(downlink)
			}
		} else {
			r.RegisterHandled(downlink)
		}
	}()
	r.status.downlink.Mark(1)

	downlink.Trace = downlink.Trace.WithEvent(trace.ReceiveEvent)

	option := downlink.DownlinkOption

	downlinkMessage := &pb.DownlinkMessage{
		Payload:               downlink.Payload,
		ProtocolConfiguration: option.ProtocolConfiguration,
		GatewayConfiguration:  option.GatewayConfiguration,
		Trace:                 downlink.Trace,
	}

	identifier := option.Identifier
	if r.Component != nil && r.Component.Identity != nil {
		identifier = strings.TrimPrefix(option.Identifier, fmt.Sprintf("%s:", r.Component.Identity.ID))
	}

	gateway = r.getGateway(downlink.DownlinkOption.GatewayID)
	return gateway.HandleDownlink(identifier, downlinkMessage)
}

// buildDownlinkOption builds a DownlinkOption with default values
func (r *router) buildDownlinkOption(gatewayID string, band band.FrequencyPlan) *pb_broker.DownlinkOption {
	dataRate, _ := types.ConvertDataRate(band.DataRates[band.RX2DataRate])
	return &pb_broker.DownlinkOption{
		GatewayID: gatewayID,
		ProtocolConfiguration: pb_protocol.TxConfiguration{Protocol: &pb_protocol.TxConfiguration_LoRaWAN{LoRaWAN: &pb_lorawan.TxConfiguration{
			Modulation: pb_lorawan.Modulation_LORA,
			DataRate:   dataRate.String(),
			CodingRate: "4/5",
		}}},
		GatewayConfiguration: pb_gateway.TxConfiguration{
			RfChain:               0,
			PolarizationInversion: true,
			Frequency:             uint64(band.RX2Frequency),
			Power:                 int32(band.DefaultTXPower),
		},
	}
}

func (r *router) buildDownlinkOptions(uplink *pb.UplinkMessage, isActivation bool, gateway *gateway.Gateway) (downlinkOptions []*pb_broker.DownlinkOption) {
	var options []*pb_broker.DownlinkOption

	gatewayStatus, _ := gateway.Status.Get() // This just returns empty if non-existing

	lorawanMetadata := uplink.ProtocolMetadata.GetLoRaWAN()
	if lorawanMetadata == nil {
		return // We can't handle any other protocols than LoRaWAN yet
	}

	frequencyPlan := gatewayStatus.FrequencyPlan
	if frequencyPlan == "" {
		frequencyPlan = band.Guess(uplink.GatewayMetadata.Frequency)
	}
	band, err := band.Get(frequencyPlan)
	if err != nil {
		return // We can't handle this frequency plan
	}
	if frequencyPlan == "EU_863_870" && isActivation {
		band.RX2DataRate = 0
	}

	dataRate, err := lorawanMetadata.GetLoRaWANDataRate()
	if err != nil {
		return
	}

	// Configuration for RX2
	buildRX2 := func() (*pb_broker.DownlinkOption, error) {
		option := r.buildDownlinkOption(gateway.ID, band)
		if frequencyPlan == "EU_863_870" {
			option.GatewayConfiguration.Power = 27 // The EU RX2 frequency allows up to 27dBm
		}
		if isActivation {
			option.GatewayConfiguration.Timestamp = uplink.GatewayMetadata.Timestamp + uint32(band.JoinAcceptDelay2/1000)
		} else {
			option.GatewayConfiguration.Timestamp = uplink.GatewayMetadata.Timestamp + uint32(band.ReceiveDelay2/1000)
		}
		option.ProtocolConfiguration.GetLoRaWAN().CodingRate = lorawanMetadata.CodingRate
		return option, nil
	}

	if option, err := buildRX2(); err == nil {
		options = append(options, option)
	}

	// Configuration for RX1
	buildRX1 := func() (*pb_broker.DownlinkOption, error) {
		option := r.buildDownlinkOption(gateway.ID, band)
		if isActivation {
			option.GatewayConfiguration.Timestamp = uplink.GatewayMetadata.Timestamp + uint32(band.JoinAcceptDelay1/1000)
		} else {
			option.GatewayConfiguration.Timestamp = uplink.GatewayMetadata.Timestamp + uint32(band.ReceiveDelay1/1000)
		}
		option.ProtocolConfiguration.GetLoRaWAN().CodingRate = lorawanMetadata.CodingRate

		freq, err := band.GetRX1Frequency(int(uplink.GatewayMetadata.Frequency))
		if err != nil {
			return nil, err
		}
		option.GatewayConfiguration.Frequency = uint64(freq)

		upDR, err := band.GetDataRate(dataRate)
		if err != nil {
			return nil, err
		}
		downDR, err := band.GetRX1DataRate(upDR, 0)
		if err != nil {
			return nil, err
		}

		if err := option.ProtocolConfiguration.GetLoRaWAN().SetDataRate(band.DataRates[downDR]); err != nil {
			return nil, err
		}
		option.GatewayConfiguration.FrequencyDeviation = uint32(option.ProtocolConfiguration.GetLoRaWAN().BitRate / 2)

		return option, nil
	}

	if option, err := buildRX1(); err == nil {
		options = append(options, option)
	}

	computeDownlinkScores(gateway, uplink, options)

	for _, option := range options {
		// Add router ID to downlink option
		if r.Component != nil && r.Component.Identity != nil {
			option.Identifier = fmt.Sprintf("%s:%s", r.Component.Identity.ID, option.Identifier)
		}

		// Filter all illegal options
		if option.Score < 1000 {
			downlinkOptions = append(downlinkOptions, option)
		}
	}

	return
}

// Calculating the score for each downlink option; lower is better, 0 is best
// If a score is over 1000, it may should not be used as feasible option.
// TODO: The weights of these parameters should be optimized. I'm sure someone
// can do some computer simulations to find the right values.
func computeDownlinkScores(gateway *gateway.Gateway, uplink *pb.UplinkMessage, options []*pb_broker.DownlinkOption) {
	gatewayStatus, _ := gateway.Status.Get() // This just returns empty if non-existing

	frequencyPlan := gatewayStatus.FrequencyPlan
	if frequencyPlan == "" {
		frequencyPlan = band.Guess(uplink.GatewayMetadata.Frequency)
	}

	gatewayRx, _ := gateway.Utilization.Get()
	for _, option := range options {

		// Invalid if no LoRaWAN
		conf := option.GetProtocolConfiguration()
		lorawan := conf.GetLoRaWAN()
		if lorawan == nil {
			option.Score = 1000
			continue
		}

		var time time.Duration

		if lorawan.Modulation == pb_lorawan.Modulation_LORA {
			// Calculate max ToA
			time, _ = toa.ComputeLoRa(
				51+13, // Max MACPayload plus LoRaWAN header, TODO: What is the length we should use?
				lorawan.DataRate,
				lorawan.CodingRate,
			)
		}

		if lorawan.Modulation == pb_lorawan.Modulation_FSK {
			// Calculate max ToA
			time, _ = toa.ComputeFSK(
				51+13, // Max MACPayload plus LoRaWAN header, TODO: What is the length we should use?
				int(lorawan.BitRate),
			)
		}

		// Invalid if time is zero
		if time == 0 {
			option.Score = 1000
			continue
		}

		timeScore := math.Min(time.Seconds()*5, 10) // 2 seconds will be 10 (max)

		signalScore := 0.0 // Between 0 and 20 (lower is better)
		{
			// Prefer high SNR
			if uplink.GatewayMetadata.SNR < 5 {
				signalScore += 10
			}
			// Prefer good RSSI
			signalScore += math.Min(float64(uplink.GatewayMetadata.RSSI*-0.1), 10)
		}

		utilizationScore := 0.0 // Between 0 and 40 (lower is better) will be over 100 if forbidden
		{
			// Avoid gateways that do more Rx
			utilizationScore += math.Min(gatewayRx*50, 20) / 2 // 40% utilization = 10 (max)

			// Avoid busy channels
			freq := option.GatewayConfiguration.Frequency
			channelRx, channelTx := gateway.Utilization.GetChannel(freq)
			utilizationScore += math.Min((channelTx+channelRx)*200, 20) / 2 // 10% utilization = 10 (max)

			// European Duty Cycle
			if frequencyPlan == "EU_863_870" {
				var duty float64
				switch {
				case freq >= 863000000 && freq < 868000000:
					duty = 0.01 // g 863.0 – 868.0 MHz 1%
				case freq >= 868000000 && freq < 868600000:
					duty = 0.01 // g1 868.0 – 868.6 MHz 1%
				case freq >= 868700000 && freq < 869200000:
					duty = 0.001 // g2 868.7 – 869.2 MHz 0.1%
				case freq >= 869400000 && freq < 869650000:
					duty = 0.1 // g3 869.4 – 869.65 MHz 10%
				case freq >= 869700000 && freq < 870000000:
					duty = 0.01 // g4 869.7 – 870.0 MHz 1%
				default:
					utilizationScore += 100 // Transmissions on this frequency are forbidden
				}
				if channelTx > duty {
					utilizationScore += 100 // Transmissions on this frequency are forbidden
				}
				if duty > 0 {
					utilizationScore += math.Min(time.Seconds()/duty/100, 20) // Impact on duty-cycle (in order to prefer RX2 for SF9BW125)
				}
			}
		}

		scheduleScore := 0.0 // Between 0 and 30 (lower is better) will be over 100 if forbidden
		{
			id, conflicts := gateway.Schedule.GetOption(option.GatewayConfiguration.Timestamp, uint32(time/1000))
			option.Identifier = id
			if conflicts >= 100 {
				scheduleScore += 100
			} else {
				scheduleScore += math.Min(float64(conflicts*10), 30) // max 30
			}
		}

		option.Score = uint32((timeScore + signalScore + utilizationScore + scheduleScore) * 10)
	}
}
<commit_msg>Only announce downlink gateway if it has a token<commit_after>// Copyright © 2017 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package router

import (
	"fmt"
	"math"
	"strings"
	"time"

	pb_broker "github.com/TheThingsNetwork/api/broker"
	pb_gateway "github.com/TheThingsNetwork/api/gateway"
	"github.com/TheThingsNetwork/api/logfields"
	pb_protocol "github.com/TheThingsNetwork/api/protocol"
	pb_lorawan "github.com/TheThingsNetwork/api/protocol/lorawan"
	pb "github.com/TheThingsNetwork/api/router"
	"github.com/TheThingsNetwork/api/trace"
	ttnlog "github.com/TheThingsNetwork/go-utils/log"
	"github.com/TheThingsNetwork/ttn/core/band"
	"github.com/TheThingsNetwork/ttn/core/router/gateway"
	"github.com/TheThingsNetwork/ttn/core/types"
	"github.com/TheThingsNetwork/ttn/utils/errors"
	"github.com/TheThingsNetwork/ttn/utils/toa"
)

func (r *router) SubscribeDownlink(gatewayID string, subscriptionID string) (<-chan *pb.DownlinkMessage, error) {
	ctx := r.Ctx.WithFields(ttnlog.Fields{
		"GatewayID": gatewayID,
	})

	gateway := r.getGateway(gatewayID)
	if fromSchedule := gateway.Schedule.Subscribe(subscriptionID); fromSchedule != nil {
		if token := gateway.Token(); gatewayID != "" && token != "" {
			r.Discovery.AddGatewayID(gatewayID, token)
		}
		toGateway := make(chan *pb.DownlinkMessage)
		go func() {
			ctx.Debug("Activate downlink")
			for message := range fromSchedule {
				ctx.WithFields(logfields.ForMessage(message)).Debug("Send downlink")
				toGateway <- message
				if gateway.MonitorStream != nil {
					clone := *message // There can be multiple subscribers
					clone.Trace = clone.Trace.WithEvent(trace.SendEvent)
					gateway.MonitorStream.Send(&clone)
				}
			}
			ctx.Debug("Deactivate downlink")
			close(toGateway)
		}()
		return toGateway, nil
	}
	return nil, errors.NewErrInternal(fmt.Sprintf("Already subscribed to downlink for %s", gatewayID))
}

func (r *router) UnsubscribeDownlink(gatewayID string, subscriptionID string) error {
	gateway := r.getGateway(gatewayID)
	if token := gateway.Token(); gatewayID != "" && token != "" {
		r.Discovery.RemoveGatewayID(gatewayID, token)
	}
	gateway.Schedule.Stop(subscriptionID)
	return nil
}

func (r *router) HandleDownlink(downlink *pb_broker.DownlinkMessage) (err error) {
	var gateway *gateway.Gateway

	r.RegisterReceived(downlink)
	defer func() {
		if err != nil {
			downlink.Trace = downlink.Trace.WithEvent(trace.DropEvent, "reason", err)
			if gateway != nil && gateway.MonitorStream != nil {
				gateway.MonitorStream.Send(downlink)
			}
		} else {
			r.RegisterHandled(downlink)
		}
	}()
	r.status.downlink.Mark(1)

	downlink.Trace = downlink.Trace.WithEvent(trace.ReceiveEvent)

	option := downlink.DownlinkOption

	downlinkMessage := &pb.DownlinkMessage{
		Payload:               downlink.Payload,
		ProtocolConfiguration: option.ProtocolConfiguration,
		GatewayConfiguration:  option.GatewayConfiguration,
		Trace:                 downlink.Trace,
	}

	identifier := option.Identifier
	if r.Component != nil && r.Component.Identity != nil {
		identifier = strings.TrimPrefix(option.Identifier, fmt.Sprintf("%s:", r.Component.Identity.ID))
	}

	gateway = r.getGateway(downlink.DownlinkOption.GatewayID)
	return gateway.HandleDownlink(identifier, downlinkMessage)
}

// buildDownlinkOption builds a DownlinkOption with default values
func (r *router) buildDownlinkOption(gatewayID string, band band.FrequencyPlan) *pb_broker.DownlinkOption {
	dataRate, _ := types.ConvertDataRate(band.DataRates[band.RX2DataRate])
	return &pb_broker.DownlinkOption{
		GatewayID: gatewayID,
		ProtocolConfiguration: pb_protocol.TxConfiguration{Protocol: &pb_protocol.TxConfiguration_LoRaWAN{LoRaWAN: &pb_lorawan.TxConfiguration{
			Modulation: pb_lorawan.Modulation_LORA,
			DataRate:   dataRate.String(),
			CodingRate: "4/5",
		}}},
		GatewayConfiguration: pb_gateway.TxConfiguration{
			RfChain:               0,
			PolarizationInversion: true,
			Frequency:             uint64(band.RX2Frequency),
			Power:                 int32(band.DefaultTXPower),
		},
	}
}

func (r *router) buildDownlinkOptions(uplink *pb.UplinkMessage, isActivation bool, gateway *gateway.Gateway) (downlinkOptions []*pb_broker.DownlinkOption) {
	var options []*pb_broker.DownlinkOption

	gatewayStatus, _ := gateway.Status.Get() // This just returns empty if non-existing

	lorawanMetadata := uplink.ProtocolMetadata.GetLoRaWAN()
	if lorawanMetadata == nil {
		return // We can't handle any other protocols than LoRaWAN yet
	}

	frequencyPlan := gatewayStatus.FrequencyPlan
	if frequencyPlan == "" {
		frequencyPlan = band.Guess(uplink.GatewayMetadata.Frequency)
	}
	band, err := band.Get(frequencyPlan)
	if err != nil {
		return // We can't handle this frequency plan
	}
	if frequencyPlan == "EU_863_870" && isActivation {
		band.RX2DataRate = 0
	}

	dataRate, err := lorawanMetadata.GetLoRaWANDataRate()
	if err != nil {
		return
	}

	// Configuration for RX2
	buildRX2 := func() (*pb_broker.DownlinkOption, error) {
		option := r.buildDownlinkOption(gateway.ID, band)
		if frequencyPlan == "EU_863_870" {
			option.GatewayConfiguration.Power = 27 // The EU RX2 frequency allows up to 27dBm
		}
		if isActivation {
			option.GatewayConfiguration.Timestamp = uplink.GatewayMetadata.Timestamp + uint32(band.JoinAcceptDelay2/1000)
		} else {
			option.GatewayConfiguration.Timestamp = uplink.GatewayMetadata.Timestamp + uint32(band.ReceiveDelay2/1000)
		}
		option.ProtocolConfiguration.GetLoRaWAN().CodingRate = lorawanMetadata.CodingRate
		return option, nil
	}

	if option, err := buildRX2(); err == nil {
		options = append(options, option)
	}

	// Configuration for RX1
	buildRX1 := func() (*pb_broker.DownlinkOption, error) {
		option := r.buildDownlinkOption(gateway.ID, band)
		if isActivation {
			option.GatewayConfiguration.Timestamp = uplink.GatewayMetadata.Timestamp + uint32(band.JoinAcceptDelay1/1000)
		} else {
			option.GatewayConfiguration.Timestamp = uplink.GatewayMetadata.Timestamp + uint32(band.ReceiveDelay1/1000)
		}
		option.ProtocolConfiguration.GetLoRaWAN().CodingRate = lorawanMetadata.CodingRate

		freq, err := band.GetRX1Frequency(int(uplink.GatewayMetadata.Frequency))
		if err != nil {
			return nil, err
		}
		option.GatewayConfiguration.Frequency = uint64(freq)

		upDR, err := band.GetDataRate(dataRate)
		if err != nil {
			return nil, err
		}
		downDR, err := band.GetRX1DataRate(upDR, 0)
		if err != nil {
			return nil, err
		}

		if err := option.ProtocolConfiguration.GetLoRaWAN().SetDataRate(band.DataRates[downDR]); err != nil {
			return nil, err
		}
		option.GatewayConfiguration.FrequencyDeviation = uint32(option.ProtocolConfiguration.GetLoRaWAN().BitRate / 2)

		return option, nil
	}

	if option, err := buildRX1(); err == nil {
		options = append(options, option)
	}

	computeDownlinkScores(gateway, uplink, options)

	for _, option := range options {
		// Add router ID to downlink option
		if r.Component != nil && r.Component.Identity != nil {
			option.Identifier = fmt.Sprintf("%s:%s", r.Component.Identity.ID, option.Identifier)
		}

		// Filter all illegal options
		if option.Score < 1000 {
			downlinkOptions = append(downlinkOptions, option)
		}
	}

	return
}

// Calculating the score for each downlink option; lower is better, 0 is best
// If a score is over 1000, it may should not be used as feasible option.
// TODO: The weights of these parameters should be optimized. I'm sure someone
// can do some computer simulations to find the right values.
func computeDownlinkScores(gateway *gateway.Gateway, uplink *pb.UplinkMessage, options []*pb_broker.DownlinkOption) {
	gatewayStatus, _ := gateway.Status.Get() // This just returns empty if non-existing

	frequencyPlan := gatewayStatus.FrequencyPlan
	if frequencyPlan == "" {
		frequencyPlan = band.Guess(uplink.GatewayMetadata.Frequency)
	}

	gatewayRx, _ := gateway.Utilization.Get()
	for _, option := range options {

		// Invalid if no LoRaWAN
		conf := option.GetProtocolConfiguration()
		lorawan := conf.GetLoRaWAN()
		if lorawan == nil {
			option.Score = 1000
			continue
		}

		var time time.Duration

		if lorawan.Modulation == pb_lorawan.Modulation_LORA {
			// Calculate max ToA
			time, _ = toa.ComputeLoRa(
				51+13, // Max MACPayload plus LoRaWAN header, TODO: What is the length we should use?
				lorawan.DataRate,
				lorawan.CodingRate,
			)
		}

		if lorawan.Modulation == pb_lorawan.Modulation_FSK {
			// Calculate max ToA
			time, _ = toa.ComputeFSK(
				51+13, // Max MACPayload plus LoRaWAN header, TODO: What is the length we should use?
				int(lorawan.BitRate),
			)
		}

		// Invalid if time is zero
		if time == 0 {
			option.Score = 1000
			continue
		}

		timeScore := math.Min(time.Seconds()*5, 10) // 2 seconds will be 10 (max)

		signalScore := 0.0 // Between 0 and 20 (lower is better)
		{
			// Prefer high SNR
			if uplink.GatewayMetadata.SNR < 5 {
				signalScore += 10
			}
			// Prefer good RSSI
			signalScore += math.Min(float64(uplink.GatewayMetadata.RSSI*-0.1), 10)
		}

		utilizationScore := 0.0 // Between 0 and 40 (lower is better) will be over 100 if forbidden
		{
			// Avoid gateways that do more Rx
			utilizationScore += math.Min(gatewayRx*50, 20) / 2 // 40% utilization = 10 (max)

			// Avoid busy channels
			freq := option.GatewayConfiguration.Frequency
			channelRx, channelTx := gateway.Utilization.GetChannel(freq)
			utilizationScore += math.Min((channelTx+channelRx)*200, 20) / 2 // 10% utilization = 10 (max)

			// European Duty Cycle
			if frequencyPlan == "EU_863_870" {
				var duty float64
				switch {
				case freq >= 863000000 && freq < 868000000:
					duty = 0.01 // g 863.0 – 868.0 MHz 1%
				case freq >= 868000000 && freq < 868600000:
					duty = 0.01 // g1 868.0 – 868.6 MHz 1%
				case freq >= 868700000 && freq < 869200000:
					duty = 0.001 // g2 868.7 – 869.2 MHz 0.1%
				case freq >= 869400000 && freq < 869650000:
					duty = 0.1 // g3 869.4 – 869.65 MHz 10%
				case freq >= 869700000 && freq < 870000000:
					duty = 0.01 // g4 869.7 – 870.0 MHz 1%
				default:
					utilizationScore += 100 // Transmissions on this frequency are forbidden
				}
				if channelTx > duty {
					utilizationScore += 100 // Transmissions on this frequency are forbidden
				}
				if duty > 0 {
					utilizationScore += math.Min(time.Seconds()/duty/100, 20) // Impact on duty-cycle (in order to prefer RX2 for SF9BW125)
				}
			}
		}

		scheduleScore := 0.0 // Between 0 and 30 (lower is better) will be over 100 if forbidden
		{
			id, conflicts := gateway.Schedule.GetOption(option.GatewayConfiguration.Timestamp, uint32(time/1000))
			option.Identifier = id
			if conflicts >= 100 {
				scheduleScore += 100
			} else {
				scheduleScore += math.Min(float64(conflicts*10), 30) // max 30
			}
		}

		option.Score = uint32((timeScore + signalScore + utilizationScore + scheduleScore) * 10)
	}
}
<|endoftext|>


<commit_before>package scheduler

import (
	"fmt"
	"strconv"

	"github.com/ellcrys/crypto"
	"github.com/ellcrys/util"
	"github.com/franela/goreq"
	"github.com/ncodes/cocoon/core/common"
	logging "github.com/op/go-logging"
)

var log = logging.MustGetLogger("nomad")

// SupportedCocoonCodeLang defines the supported chaincode language
var SupportedCocoonCodeLang = []string{"go"}

// SupportedMemory represents the allowed cocoon memory choices
var SupportedMemory = map[string]int{
	"512m": 512,
	"1g":   1024,
	"2g":   2048,
}

// SupportedCPUShares represents the allowed cocoon cpu share choices
var SupportedCPUShares = map[string]int{
	"1x": 100,
	"2x": 200,
}

// SupportedDiskSpace represents the allowed cocoon disk space
var SupportedDiskSpace = map[string]int{
	"1x": 1024,
	"2x": 2048,
}

// Nomad defines a nomad scheduler that implements
// scheduler.Scheduler interface. Every interaction with
// the scheduler is handled here.
type Nomad struct {
	schedulerAddr    string
	API              string
	ServiceDiscovery ServiceDiscovery
}

// NewNomad creates a nomad scheduler object
func NewNomad() *Nomad {
	return &Nomad{
		ServiceDiscovery: &NomadServiceDiscovery{
			ConsulAddr: util.Env("CONSUL_ADDR", "localhost:8500"),
			Protocol:   "http",
		},
	}
}

// GetName returns the scheduler name
func (sc *Nomad) GetName() string {
	return "nomad"
}

// SetAddr sets the nomad's API endpoint
func (sc *Nomad) SetAddr(addr string, https bool) {
	scheme := "http://"
	if https {
		scheme = "https://"
	}
	sc.API = scheme + addr
}

// deployJob registers a new job
func (sc *Nomad) deployJob(jobSpec string) (string, int, error) {

	res, err := goreq.Request{
		Method: "POST",
		Uri:    sc.API + "/v1/jobs",
		Body:   jobSpec,
	}.Do()
	if err != nil {
		return "", 0, err
	}
	defer res.Body.Close()
	respStr, _ := res.Body.ToString()
	return respStr, res.StatusCode, nil
}

// makeLinkToServiceTag creates a tag representing a link to a cocoon id.
// To be used as a service tag
func makeLinkToServiceTag(linkID string) string {
	return fmt.Sprintf("link_to:%s", linkID)
}

// Deploy a cocoon code to the scheduler
func (sc *Nomad) Deploy(jobID, lang, url, tag, buildParams, linkID, memory, cpuShare string) (*DeploymentInfo, error) {

	var err error

	if len(jobID) == 0 {
		return nil, fmt.Errorf("job id is required")
	}

	if err = common.ValidateDeployment(url, lang, buildParams); err != nil {
		return nil, err
	}

	log.Debugf("Deploying cocoon code with language=%s, url=%s, tag=%s", lang, url, tag)

	if len(buildParams) > 0 {
		buildParams = crypto.ToBase64([]byte(buildParams))
	}

	job := NewJob("master", jobID, 1)
	job.GetSpec().Region = "global"
	job.GetSpec().Datacenters = []string{"dc1"}
	job.GetSpec().TaskGroups[0].Tasks[0].Env["COCOON_CODE_URL"] = url
	job.GetSpec().TaskGroups[0].Tasks[0].Env["COCOON_CODE_TAG"] = tag
	job.GetSpec().TaskGroups[0].Tasks[0].Env["COCOON_CODE_LANG"] = lang
	job.GetSpec().TaskGroups[0].Tasks[0].Env["COCOON_BUILD_PARAMS"] = buildParams
	job.GetSpec().TaskGroups[0].Tasks[0].Env["COCOON_DISK_LIMIT"] = strconv.Itoa(SupportedDiskSpace[cpuShare])
	job.GetSpec().TaskGroups[0].Tasks[0].Env["ALLOC_MEMORY"] = strconv.Itoa(SupportedMemory[memory])
	job.GetSpec().TaskGroups[0].Tasks[0].Env["ALLOC_CPU_SHARE"] = strconv.Itoa(SupportedCPUShares[cpuShare])

	// if cocoon linkID is provided, set env variable and also add id to
	// the service tag. This will allow us use discover the link via consul service discovery.
	// Tag format is `link_to:the_id`
	if len(linkID) > 0 {
		job.GetSpec().TaskGroups[0].Tasks[0].Env["COCOON_LINK"] = linkID
		curTags := job.GetSpec().TaskGroups[0].Tasks[0].Services[0].Tags
		job.GetSpec().TaskGroups[0].Tasks[0].Services[0].Tags = append(curTags, makeLinkToServiceTag(linkID))
	}

	jobSpec, _ := util.ToJSON(job)
	resp, status, err := sc.deployJob(string(jobSpec))
	if err != nil {
		return nil, fmt.Errorf("system: failed to deploy job spec. %s", err)
	} else if status != 200 {
		return nil, fmt.Errorf("system: failed to deploy job spec. %s", resp)
	}

	var jobInfo map[string]interface{}
	if err = util.FromJSON([]byte(resp), &jobInfo); err != nil {
		return nil, fmt.Errorf("system: %s", resp)
	}

	return &DeploymentInfo{
		ID:     jobID,
		EvalID: jobInfo["EvalID"].(string),
	}, nil
}

// GetDeploymentStatus gets the status of a job
func (sc *Nomad) GetDeploymentStatus(jobID string) (string, error) {
	res, err := goreq.Request{
		Method: "GET",
		Uri:    sc.API + "/v1/job/" + jobID,
	}.Do()

	if err != nil {
		return "", err
	} else if res.StatusCode != 200 {
		respStr, _ := res.Body.ToString()
		res.Body.Close()
		if res.StatusCode == 404 {
			return "", fmt.Errorf("not found")
		}
		return "", fmt.Errorf(respStr)
	}

	var job map[string]interface{}
	err = res.Body.FromJsonTo(&job)
	if err != nil {
		return "", common.JSONCoerceErr("job", err)
	}

	defer res.Body.Close()
	if status, ok := job["Status"].(string); ok {
		return status, nil
	}

	return "", nil
}

// Stop stops a running cocoon job
func (sc *Nomad) Stop(jobID string) error {
	res, err := goreq.Request{
		Method: "DELETE",
		Uri:    sc.API + "/v1/job/" + jobID,
	}.Do()
	if err != nil {
		return err
	} else if res.StatusCode != 200 {
		respStr, _ := res.Body.ToString()
		res.Body.Close()
		return fmt.Errorf(respStr)
	}
	res.Body.Close()
	return nil
}

// Getenv returns an environment variable value based on the schedulers
// naming convention.
func Getenv(env, defaultVal string) string {
	return util.Env("NOMAD_"+env, defaultVal)
}

// GetServiceDiscoverer returns the schedulers service discoverer
func (sc *Nomad) GetServiceDiscoverer() ServiceDiscovery {
	return sc.ServiceDiscovery
}
<commit_msg>ensure reader is closed<commit_after>package scheduler

import (
	"fmt"
	"strconv"

	"github.com/ellcrys/crypto"
	"github.com/ellcrys/util"
	"github.com/franela/goreq"
	"github.com/ncodes/cocoon/core/common"
	logging "github.com/op/go-logging"
)

var log = logging.MustGetLogger("nomad")

// SupportedCocoonCodeLang defines the supported chaincode language
var SupportedCocoonCodeLang = []string{"go"}

// SupportedMemory represents the allowed cocoon memory choices
var SupportedMemory = map[string]int{
	"512m": 512,
	"1g":   1024,
	"2g":   2048,
}

// SupportedCPUShares represents the allowed cocoon cpu share choices
var SupportedCPUShares = map[string]int{
	"1x": 100,
	"2x": 200,
}

// SupportedDiskSpace represents the allowed cocoon disk space
var SupportedDiskSpace = map[string]int{
	"1x": 1024,
	"2x": 2048,
}

// Nomad defines a nomad scheduler that implements
// scheduler.Scheduler interface. Every interaction with
// the scheduler is handled here.
type Nomad struct {
	schedulerAddr    string
	API              string
	ServiceDiscovery ServiceDiscovery
}

// NewNomad creates a nomad scheduler object
func NewNomad() *Nomad {
	return &Nomad{
		ServiceDiscovery: &NomadServiceDiscovery{
			ConsulAddr: util.Env("CONSUL_ADDR", "localhost:8500"),
			Protocol:   "http",
		},
	}
}

// GetName returns the scheduler name
func (sc *Nomad) GetName() string {
	return "nomad"
}

// SetAddr sets the nomad's API endpoint
func (sc *Nomad) SetAddr(addr string, https bool) {
	scheme := "http://"
	if https {
		scheme = "https://"
	}
	sc.API = scheme + addr
}

// deployJob registers a new job
func (sc *Nomad) deployJob(jobSpec string) (string, int, error) {

	res, err := goreq.Request{
		Method: "POST",
		Uri:    sc.API + "/v1/jobs",
		Body:   jobSpec,
	}.Do()
	if err != nil {
		return "", 0, err
	}
	defer res.Body.Close()
	respStr, _ := res.Body.ToString()
	return respStr, res.StatusCode, nil
}

// makeLinkToServiceTag creates a tag representing a link to a cocoon id.
// To be used as a service tag
func makeLinkToServiceTag(linkID string) string {
	return fmt.Sprintf("link_to:%s", linkID)
}

// Deploy a cocoon code to the scheduler
func (sc *Nomad) Deploy(jobID, lang, url, tag, buildParams, linkID, memory, cpuShare string) (*DeploymentInfo, error) {

	var err error

	if len(jobID) == 0 {
		return nil, fmt.Errorf("job id is required")
	}

	if err = common.ValidateDeployment(url, lang, buildParams); err != nil {
		return nil, err
	}

	log.Debugf("Deploying cocoon code with language=%s, url=%s, tag=%s", lang, url, tag)

	if len(buildParams) > 0 {
		buildParams = crypto.ToBase64([]byte(buildParams))
	}

	job := NewJob("master", jobID, 1)
	job.GetSpec().Region = "global"
	job.GetSpec().Datacenters = []string{"dc1"}
	job.GetSpec().TaskGroups[0].Tasks[0].Env["COCOON_CODE_URL"] = url
	job.GetSpec().TaskGroups[0].Tasks[0].Env["COCOON_CODE_TAG"] = tag
	job.GetSpec().TaskGroups[0].Tasks[0].Env["COCOON_CODE_LANG"] = lang
	job.GetSpec().TaskGroups[0].Tasks[0].Env["COCOON_BUILD_PARAMS"] = buildParams
	job.GetSpec().TaskGroups[0].Tasks[0].Env["COCOON_DISK_LIMIT"] = strconv.Itoa(SupportedDiskSpace[cpuShare])
	job.GetSpec().TaskGroups[0].Tasks[0].Env["ALLOC_MEMORY"] = strconv.Itoa(SupportedMemory[memory])
	job.GetSpec().TaskGroups[0].Tasks[0].Env["ALLOC_CPU_SHARE"] = strconv.Itoa(SupportedCPUShares[cpuShare])

	// if cocoon linkID is provided, set env variable and also add id to
	// the service tag. This will allow us use discover the link via consul service discovery.
	// Tag format is `link_to:the_id`
	if len(linkID) > 0 {
		job.GetSpec().TaskGroups[0].Tasks[0].Env["COCOON_LINK"] = linkID
		curTags := job.GetSpec().TaskGroups[0].Tasks[0].Services[0].Tags
		job.GetSpec().TaskGroups[0].Tasks[0].Services[0].Tags = append(curTags, makeLinkToServiceTag(linkID))
	}

	jobSpec, _ := util.ToJSON(job)
	resp, status, err := sc.deployJob(string(jobSpec))
	if err != nil {
		return nil, fmt.Errorf("system: failed to deploy job spec. %s", err)
	} else if status != 200 {
		return nil, fmt.Errorf("system: failed to deploy job spec. %s", resp)
	}

	var jobInfo map[string]interface{}
	if err = util.FromJSON([]byte(resp), &jobInfo); err != nil {
		return nil, fmt.Errorf("system: %s", resp)
	}

	return &DeploymentInfo{
		ID:     jobID,
		EvalID: jobInfo["EvalID"].(string),
	}, nil
}

// GetDeploymentStatus gets the status of a job
func (sc *Nomad) GetDeploymentStatus(jobID string) (string, error) {
	res, err := goreq.Request{
		Method: "GET",
		Uri:    sc.API + "/v1/job/" + jobID,
	}.Do()

	if err != nil {
		return "", err
	} else if res.StatusCode != 200 {
		respStr, _ := res.Body.ToString()
		res.Body.Close()
		if res.StatusCode == 404 {
			return "", fmt.Errorf("not found")
		}
		return "", fmt.Errorf(respStr)
	}

	defer res.Body.Close()
	var job map[string]interface{}
	err = res.Body.FromJsonTo(&job)
	if err != nil {
		return "", common.JSONCoerceErr("job", err)
	}

	if status, ok := job["Status"].(string); ok {
		return status, nil
	}

	return "", nil
}

// Stop stops a running cocoon job
func (sc *Nomad) Stop(jobID string) error {
	res, err := goreq.Request{
		Method: "DELETE",
		Uri:    sc.API + "/v1/job/" + jobID,
	}.Do()
	if err != nil {
		return err
	} else if res.StatusCode != 200 {
		respStr, _ := res.Body.ToString()
		res.Body.Close()
		return fmt.Errorf(respStr)
	}
	res.Body.Close()
	return nil
}

// Getenv returns an environment variable value based on the schedulers
// naming convention.
func Getenv(env, defaultVal string) string {
	return util.Env("NOMAD_"+env, defaultVal)
}

// GetServiceDiscoverer returns the schedulers service discoverer
func (sc *Nomad) GetServiceDiscoverer() ServiceDiscovery {
	return sc.ServiceDiscovery
}
<|endoftext|>


<commit_before><commit_msg>Create random mouse delay<commit_after><|endoftext|>


<commit_before>package physical

import "testing"

func TestInmem(t *testing.T) {
	inm := newInmem()
	testBackend(t, inm)
	testBackend_ListPrefix(t, inm)
}
<commit_msg>physical: fix failing test<commit_after>package physical

import "testing"

func TestInmem(t *testing.T) {
	inm := NewInmem()
	testBackend(t, inm)
	testBackend_ListPrefix(t, inm)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"os"
	"runtime/pprof"
)

func main() {
	defer func() {
		err := recover()
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v", err)
			os.Exit(3)
		}
	}()

	cpuprofile := flag.String("cpuprofile", "", "write cpu profiling data")
	flag.Parse()

	if *cpuprofile != "" {
		cpu, err := os.Create(*cpuprofile)
		if err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
			os.Exit(1)
		}
		pprof.StartCPUProfile(cpu)
		defer pprof.StopCPUProfile()
	}

	if flag.NArg() != 2 {
		fmt.Fprintf(os.Stderr, "%s source destination", os.Args[0])
		os.Exit(1)
	}
	src := flag.Arg(0)
	dst := flag.Arg(1)

	err := Sync(src, dst)

	if err != nil {
		fmt.Fprintf(os.Stderr, "%s", err.Error())
		os.Exit(2)
	}

}

type Op struct {
	Data   []byte
	Offset int64
}

func Sync(src, dst string) (err error) {

	sf, err := os.Open(src)
	if err != nil {
		return
	}
	defer sf.Close()

	df, err := os.OpenFile(dst, os.O_RDWR, 0)
	if err != nil {
		return
	}
	defer df.Close()

	si, err := sf.Stat()
	if err != nil {
		return
	}
	size := si.Size()
	blocks := size / BLOCK_SIZE
	if size%BLOCK_SIZE > 0 {
		blocks += 1
	}

	err = df.Truncate(size)
	if err != nil {
		return
	}

	sr := make(chan Op, BUFFER_SIZE)
	dr := make(chan Op, BUFFER_SIZE)

	sw := make(chan Op, BUFFER_SIZE)
	dw := make(chan Op, BUFFER_SIZE)

	go ReadWrite(sf, sr, sw)
	go ReadWrite(df, dr, dw)

	progress := Start(size, sr, dr, dw)
	defer progress.End()
	writes := int64(0)

	for reads := int64(1); reads <= blocks; reads++ {
		s, d := <-sr, <-dr
		if !Compare(s.Data, d.Data) {
			dw <- Op{s.Data, s.Offset}
			writes++
		}
		progress.Step(reads*BLOCK_SIZE, writes*BLOCK_SIZE)
	}

	close(sw)
	close(dw)

	<-dr
	<-sr

	err = df.Sync()
	if err != nil {
		panic(err)
	}

	return

}

func ReadWrite(file *os.File, read, write chan Op) {

	defer close(read)

	for offset := int64(0); ; {
		select {
		case w, ok := <-write:
			if !ok {
				return
			}
			_, err := file.WriteAt(w.Data, w.Offset)
			if err != nil {
				panic(err)
			}
		default:
			data := make([]byte, BLOCK_SIZE)
			n, err := file.Read(data)
			if err != nil && err != io.EOF {
				panic(err)
			}
			if n != 0 {
				read <- Op{data[:n], offset}
				offset += int64(n)
			}
		}
	}

}

func Compare(b1, b2 []byte) bool {
	if len(b1) != len(b2) {
		return false
	}
	for i := 0; i < len(b1); i++ {
		if b1[i] != b2[i] {
			return false
		}
	}
	return true
}
<commit_msg>removed redundant panic<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"os"
	"runtime/pprof"
)

func main() {
	defer func() {
		err := recover()
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v", err)
			os.Exit(3)
		}
	}()

	cpuprofile := flag.String("cpuprofile", "", "write cpu profiling data")
	flag.Parse()

	if *cpuprofile != "" {
		cpu, err := os.Create(*cpuprofile)
		if err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
			os.Exit(1)
		}
		pprof.StartCPUProfile(cpu)
		defer pprof.StopCPUProfile()
	}

	if flag.NArg() != 2 {
		fmt.Fprintf(os.Stderr, "%s source destination", os.Args[0])
		os.Exit(1)
	}
	src := flag.Arg(0)
	dst := flag.Arg(1)

	err := Sync(src, dst)

	if err != nil {
		fmt.Fprintf(os.Stderr, "%s", err.Error())
		os.Exit(2)
	}

}

type Op struct {
	Data   []byte
	Offset int64
}

func Sync(src, dst string) (err error) {

	sf, err := os.Open(src)
	if err != nil {
		return
	}
	defer sf.Close()

	df, err := os.OpenFile(dst, os.O_RDWR, 0)
	if err != nil {
		return
	}
	defer df.Close()

	si, err := sf.Stat()
	if err != nil {
		return
	}
	size := si.Size()
	blocks := size / BLOCK_SIZE
	if size%BLOCK_SIZE > 0 {
		blocks += 1
	}

	err = df.Truncate(size)
	if err != nil {
		return
	}

	sr := make(chan Op, BUFFER_SIZE)
	dr := make(chan Op, BUFFER_SIZE)

	sw := make(chan Op, BUFFER_SIZE)
	dw := make(chan Op, BUFFER_SIZE)

	go ReadWrite(sf, sr, sw)
	go ReadWrite(df, dr, dw)

	progress := Start(size, sr, dr, dw)
	defer progress.End()
	writes := int64(0)

	for reads := int64(1); reads <= blocks; reads++ {
		s, d := <-sr, <-dr
		if !Compare(s.Data, d.Data) {
			dw <- Op{s.Data, s.Offset}
			writes++
		}
		progress.Step(reads*BLOCK_SIZE, writes*BLOCK_SIZE)
	}

	close(sw)
	close(dw)

	<-dr
	<-sr

	err = df.Sync()
	if err != nil {
		return
	}

	return

}

func ReadWrite(file *os.File, read, write chan Op) {

	defer close(read)

	for offset := int64(0); ; {
		select {
		case w, ok := <-write:
			if !ok {
				return
			}
			_, err := file.WriteAt(w.Data, w.Offset)
			if err != nil {
				panic(err)
			}
		default:
			data := make([]byte, BLOCK_SIZE)
			n, err := file.Read(data)
			if err != nil && err != io.EOF {
				panic(err)
			}
			if n != 0 {
				read <- Op{data[:n], offset}
				offset += int64(n)
			}
		}
	}

}

func Compare(b1, b2 []byte) bool {
	if len(b1) != len(b2) {
		return false
	}
	for i := 0; i < len(b1); i++ {
		if b1[i] != b2[i] {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2016 Maciek Borzecki <maciek.borzecki@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package main

import (
	"bufio"
	"os"
	"strings"

	log "github.com/Sirupsen/logrus"
)

type Mirrors struct {
	List []string
}

func (m *Mirrors) LoadFile(path string) error {
	log.Debugf("loading mirror list from file %v", path)

	f, err := os.Open(path)
	if err != nil {
		log.Errorf("failed to open mirrors file: %v", err)
		return err
	}
	defer f.Close()

	scan := bufio.NewScanner(f)
	cnt := 0
	for scan.Scan() {
		if err := scan.Err(); err != nil {
			log.Errorf("failed to read line from mirrors file: %v", err)
			return err
		}

		line := scan.Text()
		mirror := strings.TrimSpace(line)
		m.List = append(m.List, mirror)
		cnt += 1
	}

	log.Infof("got %v mirrors", cnt)
	return nil
}
<commit_msg>mirrors: skip commented out mirrors<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2016 Maciek Borzecki <maciek.borzecki@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package main

import (
	"bufio"
	"os"
	"strings"

	log "github.com/Sirupsen/logrus"
)

type Mirrors struct {
	List []string
}

func (m *Mirrors) LoadFile(path string) error {
	log.Debugf("loading mirror list from file %v", path)

	f, err := os.Open(path)
	if err != nil {
		log.Errorf("failed to open mirrors file: %v", err)
		return err
	}
	defer f.Close()

	scan := bufio.NewScanner(f)
	cnt := 0
	for scan.Scan() {
		if err := scan.Err(); err != nil {
			log.Errorf("failed to read line from mirrors file: %v", err)
			return err
		}

		line := scan.Text()

		if strings.HasPrefix(line, "#") {
			continue
		}
		mirror := strings.TrimSpace(line)

		if len(mirror) == 0 {
			continue
		}
		m.List = append(m.List, mirror)
		cnt += 1
	}

	log.Infof("got %v mirrors", cnt)
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/spf13/cobra"

	"arvika.pulcy.com/pulcy/yard/systemd"
	"arvika.pulcy.com/pulcy/yard/topics"
	"arvika.pulcy.com/pulcy/yard/topics/iptables"
)

var (
	cmdCluster = &cobra.Command{
		Use: "cluster",
		Run: showUsage,
	}
	cmdClusterUpdate = &cobra.Command{
		Use: "update",
		Run: runClusterUpdate,
	}
	clusterUpdateFlags = &topics.TopicFlags{}
)

func init() {
	// Etcd
	cmdClusterUpdate.Flags().StringVar(&clusterUpdateFlags.DiscoveryUrl, "discovery-url", "", "Full URL for setting up etcd member lists")
	cmdClusterUpdate.Flags().StringVar(&setupFlags.PrivateClusterDevice, "private-cluster-device", defaultPrivateClusterDevice, "Network device connected to the private IP")

	cmdMain.AddCommand(cmdCluster)
	cmdCluster.AddCommand(cmdClusterUpdate)
}

func runClusterUpdate(cmd *cobra.Command, args []string) {
	if clusterUpdateFlags.DiscoveryUrl == "" {
		Exitf("discovery-url missing\n")
	}
	if setupFlags.PrivateClusterDevice == "" {
		Exitf("private-cluster-device missing\n")
	}

	deps := &topics.TopicDependencies{
		Systemd: systemd.NewSystemdClient(log),
		Logger:  log,
	}

	if err := iptables.UpdatePrivateCluster(deps, clusterUpdateFlags); err != nil {
		Exitf("Update private cluster failed: %#v\n", err)
	}
}
<commit_msg>Fixed update cluster wrt PrivateClusterDevice flag<commit_after>package main

import (
	"github.com/spf13/cobra"

	"arvika.pulcy.com/pulcy/yard/systemd"
	"arvika.pulcy.com/pulcy/yard/topics"
	"arvika.pulcy.com/pulcy/yard/topics/iptables"
)

var (
	cmdCluster = &cobra.Command{
		Use: "cluster",
		Run: showUsage,
	}
	cmdClusterUpdate = &cobra.Command{
		Use: "update",
		Run: runClusterUpdate,
	}
	clusterUpdateFlags = &topics.TopicFlags{}
)

func init() {
	// Etcd
	cmdClusterUpdate.Flags().StringVar(&clusterUpdateFlags.DiscoveryUrl, "discovery-url", "", "Full URL for setting up etcd member lists")
	cmdClusterUpdate.Flags().StringVar(&clusterUpdateFlags.PrivateClusterDevice, "private-cluster-device", defaultPrivateClusterDevice, "Network device connected to the private IP")

	cmdMain.AddCommand(cmdCluster)
	cmdCluster.AddCommand(cmdClusterUpdate)
}

func runClusterUpdate(cmd *cobra.Command, args []string) {
	if clusterUpdateFlags.DiscoveryUrl == "" {
		Exitf("discovery-url missing\n")
	}
	if clusterUpdateFlags.PrivateClusterDevice == "" {
		Exitf("private-cluster-device missing\n")
	}

	deps := &topics.TopicDependencies{
		Systemd: systemd.NewSystemdClient(log),
		Logger:  log,
	}

	if err := iptables.UpdatePrivateCluster(deps, clusterUpdateFlags); err != nil {
		Exitf("Update private cluster failed: %#v\n", err)
	}
}
<|endoftext|>


<commit_before>package libgobuster

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"os/signal"
	"strconv"
	"strings"
)

func PrepareSignalHandler(s *State) {
	s.SignalChan = make(chan os.Signal, 1)
	signal.Notify(s.SignalChan, os.Interrupt)
	go func() {
		for _ = range s.SignalChan {
			// caught CTRL+C
			if !s.Quiet {
				fmt.Println("[!] Keyboard interrupt detected, terminating.")
				s.Terminate = true
			}
		}
	}()
}

func Ruler(s *State) {
	if !s.Quiet {
		fmt.Println("=====================================================")
	}
}

func Banner(s *State) {
	if s.Quiet {
		return
	}

	fmt.Println("")
	fmt.Println("Gobuster v1.4.1              OJ Reeves (@TheColonial)")
	Ruler(s)
}

func ShowConfig(s *State) {
	if s.Quiet {
		return
	}

	if s != nil {
		fmt.Printf("[+] Mode         : %s\n", s.Mode)
		fmt.Printf("[+] Url/Domain   : %s\n", s.URL)
		fmt.Printf("[+] Threads      : %d\n", s.Threads)

		wordlist := "stdin (pipe)"
		if !s.StdIn {
			wordlist = s.Wordlist
		}
		fmt.Printf("[+] Wordlist     : %s\n", wordlist)

		if s.OutputFileName != "" {
			fmt.Printf("[+] Output file  : %s\n", s.OutputFileName)
		}

		if s.Mode == "dir" {
			fmt.Printf("[+] Status codes : %s\n", s.StatusCodes.Stringify())

			if s.ProxyURL != nil {
				fmt.Printf("[+] Proxy        : %s\n", s.ProxyURL)
			}

			if s.Cookies != "" {
				fmt.Printf("[+] Cookies      : %s\n", s.Cookies)
			}

			if s.UserAgent != "" {
				fmt.Printf("[+] User Agent   : %s\n", s.UserAgent)
			}

			if s.IncludeLength {
				fmt.Printf("[+] Show length  : true\n")
			}

			if s.Username != "" {
				fmt.Printf("[+] Auth User    : %s\n", s.Username)
			}

			if len(s.Extensions) > 0 {
				fmt.Printf("[+] Extensions   : %s\n", strings.Join(s.Extensions, ","))
			}

			if s.UseSlash {
				fmt.Printf("[+] Add Slash    : true\n")
			}

			if s.FollowRedirect {
				fmt.Printf("[+] Follow Redir : true\n")
			}

			if s.Expanded {
				fmt.Printf("[+] Expanded     : true\n")
			}

			if s.NoStatus {
				fmt.Printf("[+] No status    : true\n")
			}

			if s.Verbose {
				fmt.Printf("[+] Verbose      : true\n")
			}
		}

		Ruler(s)
	}
}

// Add an element to a set
func (set *StringSet) Add(s string) bool {
	_, found := set.Set[s]
	set.Set[s] = true
	return !found
}

// Add a list of elements to a set
func (set *StringSet) AddRange(ss []string) {
	for _, s := range ss {
		set.Set[s] = true
	}
}

// Test if an element is in a set
func (set *StringSet) Contains(s string) bool {
	_, found := set.Set[s]
	return found
}

// Check if any of the elements exist
func (set *StringSet) ContainsAny(ss []string) bool {
	for _, s := range ss {
		if set.Set[s] {
			return true
		}
	}
	return false
}

// Stringify the set
func (set *StringSet) Stringify() string {
	values := []string{}
	for s := range set.Set {
		values = append(values, s)
	}
	return strings.Join(values, ",")
}

// Add an element to a set
func (set *IntSet) Add(i int) bool {
	_, found := set.Set[i]
	set.Set[i] = true
	return !found
}

// Test if an element is in a set
func (set *IntSet) Contains(i int) bool {
	_, found := set.Set[i]
	return found
}

// Stringify the set
func (set *IntSet) Stringify() string {
	values := []string{}
	for s := range set.Set {
		values = append(values, strconv.Itoa(s))
	}
	return strings.Join(values, ",")
}

func lineCounter(r io.Reader) (int, error) {
	buf := make([]byte, 32*1024)
	count := 0
	lineSep := []byte{'\n'}

	for {
		c, err := r.Read(buf)
		count += bytes.Count(buf[:c], lineSep)

		switch {
		case err == io.EOF:
			return count, nil

		case err != nil:
			return count, err
		}
	}
}
<commit_msg>sort status codes<commit_after>package libgobuster

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"os/signal"
	"sort"
	"strings"
)

func PrepareSignalHandler(s *State) {
	s.SignalChan = make(chan os.Signal, 1)
	signal.Notify(s.SignalChan, os.Interrupt)
	go func() {
		for _ = range s.SignalChan {
			// caught CTRL+C
			if !s.Quiet {
				fmt.Println("[!] Keyboard interrupt detected, terminating.")
				s.Terminate = true
			}
		}
	}()
}

func Ruler(s *State) {
	if !s.Quiet {
		fmt.Println("=====================================================")
	}
}

func Banner(s *State) {
	if s.Quiet {
		return
	}

	fmt.Println("")
	fmt.Println("Gobuster v1.4.1              OJ Reeves (@TheColonial)")
	Ruler(s)
}

func ShowConfig(s *State) {
	if s.Quiet {
		return
	}

	if s != nil {
		fmt.Printf("[+] Mode         : %s\n", s.Mode)
		fmt.Printf("[+] Url/Domain   : %s\n", s.URL)
		fmt.Printf("[+] Threads      : %d\n", s.Threads)

		wordlist := "stdin (pipe)"
		if !s.StdIn {
			wordlist = s.Wordlist
		}
		fmt.Printf("[+] Wordlist     : %s\n", wordlist)

		if s.OutputFileName != "" {
			fmt.Printf("[+] Output file  : %s\n", s.OutputFileName)
		}

		if s.Mode == "dir" {
			fmt.Printf("[+] Status codes : %s\n", s.StatusCodes.Stringify())

			if s.ProxyURL != nil {
				fmt.Printf("[+] Proxy        : %s\n", s.ProxyURL)
			}

			if s.Cookies != "" {
				fmt.Printf("[+] Cookies      : %s\n", s.Cookies)
			}

			if s.UserAgent != "" {
				fmt.Printf("[+] User Agent   : %s\n", s.UserAgent)
			}

			if s.IncludeLength {
				fmt.Printf("[+] Show length  : true\n")
			}

			if s.Username != "" {
				fmt.Printf("[+] Auth User    : %s\n", s.Username)
			}

			if len(s.Extensions) > 0 {
				fmt.Printf("[+] Extensions   : %s\n", strings.Join(s.Extensions, ","))
			}

			if s.UseSlash {
				fmt.Printf("[+] Add Slash    : true\n")
			}

			if s.FollowRedirect {
				fmt.Printf("[+] Follow Redir : true\n")
			}

			if s.Expanded {
				fmt.Printf("[+] Expanded     : true\n")
			}

			if s.NoStatus {
				fmt.Printf("[+] No status    : true\n")
			}

			if s.Verbose {
				fmt.Printf("[+] Verbose      : true\n")
			}
		}

		Ruler(s)
	}
}

// Add an element to a set
func (set *StringSet) Add(s string) bool {
	_, found := set.Set[s]
	set.Set[s] = true
	return !found
}

// Add a list of elements to a set
func (set *StringSet) AddRange(ss []string) {
	for _, s := range ss {
		set.Set[s] = true
	}
}

// Test if an element is in a set
func (set *StringSet) Contains(s string) bool {
	_, found := set.Set[s]
	return found
}

// Check if any of the elements exist
func (set *StringSet) ContainsAny(ss []string) bool {
	for _, s := range ss {
		if set.Set[s] {
			return true
		}
	}
	return false
}

// Stringify the set
func (set *StringSet) Stringify() string {
	values := []string{}
	for s := range set.Set {
		values = append(values, s)
	}
	return strings.Join(values, ",")
}

// Add an element to a set
func (set *IntSet) Add(i int) bool {
	_, found := set.Set[i]
	set.Set[i] = true
	return !found
}

// Test if an element is in a set
func (set *IntSet) Contains(i int) bool {
	_, found := set.Set[i]
	return found
}

// Stringify the set
func (set *IntSet) Stringify() string {
	values := []int{}
	for s := range set.Set {
		values = append(values, s)
	}
	sort.Ints(values)

	delim := ","
	return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(values)), delim), "[]")
}

func lineCounter(r io.Reader) (int, error) {
	buf := make([]byte, 32*1024)
	count := 0
	lineSep := []byte{'\n'}

	for {
		c, err := r.Read(buf)
		count += bytes.Count(buf[:c], lineSep)

		switch {
		case err == io.EOF:
			return count, nil

		case err != nil:
			return count, err
		}
	}
}
<|endoftext|>


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

package gocql

import (
	"errors"
	"time"
)

// ClusterConfig is a struct to configure the default cluster implementation
// of gocoql. It has a varity of attributes that can be used to modify the
// behavior to fit the most common use cases. Applications that requre a
// different setup must implement their own cluster.
type ClusterConfig struct {
	Hosts           []string      // addresses for the initial connections
	CQLVersion      string        // CQL version (default: 3.0.0)
	ProtoVersion    int           // version of the native protocol (default: 2)
	Timeout         time.Duration // connection timeout (default: 600ms)
	DefaultPort     int           // default port (default: 9042)
	Keyspace        string        // initial keyspace (optional)
	NumConns        int           // number of connections per host (default: 2)
	NumStreams      int           // number of streams per connection (default: 128)
	Consistency     Consistency   // default consistency level (default: Quorum)
	Compressor      Compressor    // compression algorithm (default: nil)
	Authenticator   Authenticator // authenticator (default: nil)
	RetryPolicy     RetryPolicy   // Default retry policy to use for queries (default: 0)
	SocketKeepalive time.Duration // The keepalive period to use, enabled if > 0 (default: 0)
	ConnPoolType    NewPoolFunc   // The function used to create the connection pool for the session (default: NewSimplePool)
}

// NewCluster generates a new config for the default cluster implementation.
func NewCluster(hosts ...string) *ClusterConfig {
	cfg := &ClusterConfig{
		Hosts:        hosts,
		CQLVersion:   "3.0.0",
		ProtoVersion: 2,
		Timeout:      600 * time.Millisecond,
		DefaultPort:  9042,
		NumConns:     2,
		NumStreams:   128,
		Consistency:  Quorum,
		ConnPoolType: NewSimplePool,
	}
	return cfg
}

// CreateSession initializes the cluster based on this config and returns a
// session object that can be used to interact with the database.
func (cfg *ClusterConfig) CreateSession() (*Session, error) {

	//Check that hosts in the ClusterConfig is not empty
	if len(cfg.Hosts) < 1 {
		return nil, ErrNoHosts
	}
	pool := cfg.ConnPoolType(cfg)

	//See if there are any connections in the pool
	if pool.Size() > 0 {
		s := NewSession(pool, *cfg)
		s.SetConsistency(cfg.Consistency)

		//Fill out cfg.Hosts
		query := "SELECT peer FROM system.peers"
		peers := s.Query(query).Iter()

		var ip string
		for peers.Scan(&ip) {
			exists := false
			for ii := 0; ii < len(cfg.Hosts); ii++ {
				if cfg.Hosts[ii] == ip {
					exists = true
				}
			}
			if !exists {
				cfg.Hosts = append(cfg.Hosts, ip)
			}
		}

		if err := peers.Close(); err != nil {
			return s, ErrHostQueryFailed
		}

		return s, nil
	}

	pool.Close()
	return nil, ErrNoConnectionsStarted

}

var (
	ErrNoHosts              = errors.New("no hosts provided")
	ErrNoConnectionsStarted = errors.New("no connections were made when creating the session")
	ErrHostQueryFailed			= errors.New("unable to populate Hosts")
)
<commit_msg>changed !exists to exists == false<commit_after>// Copyright (c) 2012 The gocql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gocql

import (
	"errors"
	"time"
)

// ClusterConfig is a struct to configure the default cluster implementation
// of gocoql. It has a varity of attributes that can be used to modify the
// behavior to fit the most common use cases. Applications that requre a
// different setup must implement their own cluster.
type ClusterConfig struct {
	Hosts           []string      // addresses for the initial connections
	CQLVersion      string        // CQL version (default: 3.0.0)
	ProtoVersion    int           // version of the native protocol (default: 2)
	Timeout         time.Duration // connection timeout (default: 600ms)
	DefaultPort     int           // default port (default: 9042)
	Keyspace        string        // initial keyspace (optional)
	NumConns        int           // number of connections per host (default: 2)
	NumStreams      int           // number of streams per connection (default: 128)
	Consistency     Consistency   // default consistency level (default: Quorum)
	Compressor      Compressor    // compression algorithm (default: nil)
	Authenticator   Authenticator // authenticator (default: nil)
	RetryPolicy     RetryPolicy   // Default retry policy to use for queries (default: 0)
	SocketKeepalive time.Duration // The keepalive period to use, enabled if > 0 (default: 0)
	ConnPoolType    NewPoolFunc   // The function used to create the connection pool for the session (default: NewSimplePool)
}

// NewCluster generates a new config for the default cluster implementation.
func NewCluster(hosts ...string) *ClusterConfig {
	cfg := &ClusterConfig{
		Hosts:        hosts,
		CQLVersion:   "3.0.0",
		ProtoVersion: 2,
		Timeout:      600 * time.Millisecond,
		DefaultPort:  9042,
		NumConns:     2,
		NumStreams:   128,
		Consistency:  Quorum,
		ConnPoolType: NewSimplePool,
	}
	return cfg
}

// CreateSession initializes the cluster based on this config and returns a
// session object that can be used to interact with the database.
func (cfg *ClusterConfig) CreateSession() (*Session, error) {

	//Check that hosts in the ClusterConfig is not empty
	if len(cfg.Hosts) < 1 {
		return nil, ErrNoHosts
	}
	pool := cfg.ConnPoolType(cfg)

	//See if there are any connections in the pool
	if pool.Size() > 0 {
		s := NewSession(pool, *cfg)
		s.SetConsistency(cfg.Consistency)

		//Fill out cfg.Hosts
		query := "SELECT peer FROM system.peers"
		peers := s.Query(query).Iter()

		var ip string
		for peers.Scan(&ip) {
			exists := false
			for ii := 0; ii < len(cfg.Hosts); ii++ {
				if cfg.Hosts[ii] == ip {
					exists = true
				}
			}
			if exists == false{
				cfg.Hosts = append(cfg.Hosts, ip)
			}
		}

		if err := peers.Close(); err != nil {
			return s, ErrHostQueryFailed
		}

		return s, nil
	}

	pool.Close()
	return nil, ErrNoConnectionsStarted

}

var (
	ErrNoHosts              = errors.New("no hosts provided")
	ErrNoConnectionsStarted = errors.New("no connections were made when creating the session")
	ErrHostQueryFailed			= errors.New("unable to populate Hosts")
)
<|endoftext|>


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

package gocql

import (
	"errors"
	"sync"
	"time"

	"github.com/gocql/gocql/internal/lru"
)

const defaultMaxPreparedStmts = 1000

//preparedLRU is the prepared statement cache
type preparedLRU struct {
	sync.RWMutex
	lru *lru.Cache
}

//Max adjusts the maximum size of the cache and cleans up the oldest records if
//the new max is lower than the previous value. Not concurrency safe.
func (p *preparedLRU) max(max int) {
	p.Lock()
	defer p.Unlock()

	for p.lru.Len() > max {
		p.lru.RemoveOldest()
	}
	p.lru.MaxEntries = max
}

// PoolConfig configures the connection pool used by the driver, it defaults to
// using a round robbin host selection policy and a round robbin connection selection
// policy for each host.
type PoolConfig struct {
	// HostSelectionPolicy sets the policy for selecting which host to use for a
	// given query (default: RoundRobinHostPolicy())
	HostSelectionPolicy HostSelectionPolicy

	// ConnSelectionPolicy sets the policy factory for selecting a connection to use for
	// each host for a query (default: RoundRobinConnPolicy())
	ConnSelectionPolicy func() ConnSelectionPolicy
}

func (p PoolConfig) buildPool(session *Session) *policyConnPool {
	hostSelection := p.HostSelectionPolicy
	if hostSelection == nil {
		hostSelection = RoundRobinHostPolicy()
	}

	connSelection := p.ConnSelectionPolicy
	if connSelection == nil {
		connSelection = RoundRobinConnPolicy()
	}

	return newPolicyConnPool(session, hostSelection, connSelection)
}

type DiscoveryConfig struct {
	// If not empty will filter all discoverred hosts to a single Data Centre (default: "")
	DcFilter string
	// If not empty will filter all discoverred hosts to a single Rack (default: "")
	RackFilter string
	// ignored
	Sleep time.Duration
}

func (d DiscoveryConfig) matchFilter(host *HostInfo) bool {
	if d.DcFilter != "" && d.DcFilter != host.DataCenter() {
		return false
	}

	if d.RackFilter != "" && d.RackFilter != host.Rack() {
		return false
	}

	return true
}

// ClusterConfig is a struct to configure the default cluster implementation
// of gocoql. It has a varity of attributes that can be used to modify the
// behavior to fit the most common use cases. Applications that requre a
// different setup must implement their own cluster.
type ClusterConfig struct {
	Hosts             []string          // addresses for the initial connections
	CQLVersion        string            // CQL version (default: 3.0.0)
	ProtoVersion      int               // version of the native protocol (default: 2)
	Timeout           time.Duration     // connection timeout (default: 600ms)
	Port              int               // port (default: 9042)
	Keyspace          string            // initial keyspace (optional)
	NumConns          int               // number of connections per host (default: 2)
	Consistency       Consistency       // default consistency level (default: Quorum)
	Compressor        Compressor        // compression algorithm (default: nil)
	Authenticator     Authenticator     // authenticator (default: nil)
	RetryPolicy       RetryPolicy       // Default retry policy to use for queries (default: 0)
	SocketKeepalive   time.Duration     // The keepalive period to use, enabled if > 0 (default: 0)
	MaxPreparedStmts  int               // Sets the maximum cache size for prepared statements globally for gocql (default: 1000)
	MaxRoutingKeyInfo int               // Sets the maximum cache size for query info about statements for each session (default: 1000)
	PageSize          int               // Default page size to use for created sessions (default: 5000)
	SerialConsistency SerialConsistency // Sets the consistency for the serial part of queries, values can be either SERIAL or LOCAL_SERIAL (default: unset)
	SslOpts           *SslOptions
	DefaultTimestamp  bool // Sends a client side timestamp for all requests which overrides the timestamp at which it arrives at the server. (default: true, only enabled for protocol 3 and above)
	// PoolConfig configures the underlying connection pool, allowing the
	// configuration of host selection and connection selection policies.
	PoolConfig PoolConfig

	Discovery DiscoveryConfig

	// The maximum amount of time to wait for schema agreement in a cluster after
	// receiving a schema change frame. (deault: 60s)
	MaxWaitSchemaAgreement time.Duration

	// HostFilter will filter all incoming events for host, any which dont pass
	// the filter will be ignored. If set will take precedence over any options set
	// via Discovery
	HostFilter HostFilter

	// If IgnorePeerAddr is true and the address in system.peers does not match
	// the supplied host by either initial hosts or discovered via events then the
	// host will be replaced with the supplied address.
	//
	// For example if an event comes in with host=10.0.0.1 but when looking up that
	// address in system.local or system.peers returns 127.0.0.1, the peer will be
	// set to 10.0.0.1 which is what will be used to connect to.
	IgnorePeerAddr bool

	// If DisableInitialHostLookup then the driver will not attempt to get host info
	// from the system.peers table, this will mean that the driver will connect to
	// hosts supplied and will not attempt to lookup the hosts information, this will
	// mean that data_centre, rack and token information will not be available and as
	// such host filtering and token aware query routing will not be available.
	DisableInitialHostLookup bool

	// Configure events the driver will register for
	Events struct {
		// disable registering for status events (node up/down)
		DisableNodeStatusEvents bool
		// disable registering for topology events (node added/removed/moved)
		DisableTopologyEvents bool
	}

	// internal config for testing
	disableControlConn bool
}

// NewCluster generates a new config for the default cluster implementation.
func NewCluster(hosts ...string) *ClusterConfig {
	cfg := &ClusterConfig{
		Hosts:                  hosts,
		CQLVersion:             "3.0.0",
		ProtoVersion:           2,
		Timeout:                600 * time.Millisecond,
		Port:                   9042,
		NumConns:               2,
		Consistency:            Quorum,
		MaxPreparedStmts:       defaultMaxPreparedStmts,
		MaxRoutingKeyInfo:      1000,
		PageSize:               5000,
		DefaultTimestamp:       true,
		MaxWaitSchemaAgreement: 60 * time.Second,
	}
	return cfg
}

// CreateSession initializes the cluster based on this config and returns a
// session object that can be used to interact with the database.
func (cfg *ClusterConfig) CreateSession() (*Session, error) {
	return NewSession(*cfg)
}

var (
	ErrNoHosts              = errors.New("no hosts provided")
	ErrNoConnectionsStarted = errors.New("no connections were made when creating the session")
	ErrHostQueryFailed      = errors.New("unable to populate Hosts")
)
<commit_msg>didnt mean to change the mutex<commit_after>// Copyright (c) 2012 The gocql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gocql

import (
	"errors"
	"sync"
	"time"

	"github.com/gocql/gocql/internal/lru"
)

const defaultMaxPreparedStmts = 1000

//preparedLRU is the prepared statement cache
type preparedLRU struct {
	sync.Mutex
	lru *lru.Cache
}

//Max adjusts the maximum size of the cache and cleans up the oldest records if
//the new max is lower than the previous value. Not concurrency safe.
func (p *preparedLRU) max(max int) {
	p.Lock()
	defer p.Unlock()

	for p.lru.Len() > max {
		p.lru.RemoveOldest()
	}
	p.lru.MaxEntries = max
}

// PoolConfig configures the connection pool used by the driver, it defaults to
// using a round robbin host selection policy and a round robbin connection selection
// policy for each host.
type PoolConfig struct {
	// HostSelectionPolicy sets the policy for selecting which host to use for a
	// given query (default: RoundRobinHostPolicy())
	HostSelectionPolicy HostSelectionPolicy

	// ConnSelectionPolicy sets the policy factory for selecting a connection to use for
	// each host for a query (default: RoundRobinConnPolicy())
	ConnSelectionPolicy func() ConnSelectionPolicy
}

func (p PoolConfig) buildPool(session *Session) *policyConnPool {
	hostSelection := p.HostSelectionPolicy
	if hostSelection == nil {
		hostSelection = RoundRobinHostPolicy()
	}

	connSelection := p.ConnSelectionPolicy
	if connSelection == nil {
		connSelection = RoundRobinConnPolicy()
	}

	return newPolicyConnPool(session, hostSelection, connSelection)
}

type DiscoveryConfig struct {
	// If not empty will filter all discoverred hosts to a single Data Centre (default: "")
	DcFilter string
	// If not empty will filter all discoverred hosts to a single Rack (default: "")
	RackFilter string
	// ignored
	Sleep time.Duration
}

func (d DiscoveryConfig) matchFilter(host *HostInfo) bool {
	if d.DcFilter != "" && d.DcFilter != host.DataCenter() {
		return false
	}

	if d.RackFilter != "" && d.RackFilter != host.Rack() {
		return false
	}

	return true
}

// ClusterConfig is a struct to configure the default cluster implementation
// of gocoql. It has a varity of attributes that can be used to modify the
// behavior to fit the most common use cases. Applications that requre a
// different setup must implement their own cluster.
type ClusterConfig struct {
	Hosts             []string          // addresses for the initial connections
	CQLVersion        string            // CQL version (default: 3.0.0)
	ProtoVersion      int               // version of the native protocol (default: 2)
	Timeout           time.Duration     // connection timeout (default: 600ms)
	Port              int               // port (default: 9042)
	Keyspace          string            // initial keyspace (optional)
	NumConns          int               // number of connections per host (default: 2)
	Consistency       Consistency       // default consistency level (default: Quorum)
	Compressor        Compressor        // compression algorithm (default: nil)
	Authenticator     Authenticator     // authenticator (default: nil)
	RetryPolicy       RetryPolicy       // Default retry policy to use for queries (default: 0)
	SocketKeepalive   time.Duration     // The keepalive period to use, enabled if > 0 (default: 0)
	MaxPreparedStmts  int               // Sets the maximum cache size for prepared statements globally for gocql (default: 1000)
	MaxRoutingKeyInfo int               // Sets the maximum cache size for query info about statements for each session (default: 1000)
	PageSize          int               // Default page size to use for created sessions (default: 5000)
	SerialConsistency SerialConsistency // Sets the consistency for the serial part of queries, values can be either SERIAL or LOCAL_SERIAL (default: unset)
	SslOpts           *SslOptions
	DefaultTimestamp  bool // Sends a client side timestamp for all requests which overrides the timestamp at which it arrives at the server. (default: true, only enabled for protocol 3 and above)
	// PoolConfig configures the underlying connection pool, allowing the
	// configuration of host selection and connection selection policies.
	PoolConfig PoolConfig

	Discovery DiscoveryConfig

	// The maximum amount of time to wait for schema agreement in a cluster after
	// receiving a schema change frame. (deault: 60s)
	MaxWaitSchemaAgreement time.Duration

	// HostFilter will filter all incoming events for host, any which dont pass
	// the filter will be ignored. If set will take precedence over any options set
	// via Discovery
	HostFilter HostFilter

	// If IgnorePeerAddr is true and the address in system.peers does not match
	// the supplied host by either initial hosts or discovered via events then the
	// host will be replaced with the supplied address.
	//
	// For example if an event comes in with host=10.0.0.1 but when looking up that
	// address in system.local or system.peers returns 127.0.0.1, the peer will be
	// set to 10.0.0.1 which is what will be used to connect to.
	IgnorePeerAddr bool

	// If DisableInitialHostLookup then the driver will not attempt to get host info
	// from the system.peers table, this will mean that the driver will connect to
	// hosts supplied and will not attempt to lookup the hosts information, this will
	// mean that data_centre, rack and token information will not be available and as
	// such host filtering and token aware query routing will not be available.
	DisableInitialHostLookup bool

	// Configure events the driver will register for
	Events struct {
		// disable registering for status events (node up/down)
		DisableNodeStatusEvents bool
		// disable registering for topology events (node added/removed/moved)
		DisableTopologyEvents bool
	}

	// internal config for testing
	disableControlConn bool
}

// NewCluster generates a new config for the default cluster implementation.
func NewCluster(hosts ...string) *ClusterConfig {
	cfg := &ClusterConfig{
		Hosts:                  hosts,
		CQLVersion:             "3.0.0",
		ProtoVersion:           2,
		Timeout:                600 * time.Millisecond,
		Port:                   9042,
		NumConns:               2,
		Consistency:            Quorum,
		MaxPreparedStmts:       defaultMaxPreparedStmts,
		MaxRoutingKeyInfo:      1000,
		PageSize:               5000,
		DefaultTimestamp:       true,
		MaxWaitSchemaAgreement: 60 * time.Second,
	}
	return cfg
}

// CreateSession initializes the cluster based on this config and returns a
// session object that can be used to interact with the database.
func (cfg *ClusterConfig) CreateSession() (*Session, error) {
	return NewSession(*cfg)
}

var (
	ErrNoHosts              = errors.New("no hosts provided")
	ErrNoConnectionsStarted = errors.New("no connections were made when creating the session")
	ErrHostQueryFailed      = errors.New("unable to populate Hosts")
)
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/go.net/html"
	"code.google.com/p/go.net/html/atom"

	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/user"
	"regexp"
	"strings"
)

var barMap = map[string]string{}

type beerInfo struct {
	brewery string
	brew    string
}

func findBeer(node *html.Node, beers *[]beerInfo) {
	if node.DataAtom == atom.Div {
		for _, attr := range node.Attr {
			if attr.Key == "id" && strings.HasPrefix(attr.Val, "beer-") {
				brewery, brew := "", ""
				findBrewery(node, &brewery)
				findBrew(node, &brew)
				*beers = append(*beers, beerInfo{brewery, brew})
			}
		}
	}
	for kid := node.FirstChild; kid != nil; kid = kid.NextSibling {
		findBeer(kid, beers)
	}
}

func findBrewery(node *html.Node, brewery *string) bool {
	if node.DataAtom == atom.H4 {
		if content := node.FirstChild; content != nil {
			*brewery = content.Data
			return true
		}
	}
	for kid := node.FirstChild; kid != nil; kid = kid.NextSibling {
		if findBrewery(kid, brewery) {
			return true
		}
	}
	return false
}

func findBrew(node *html.Node, brew *string) bool {
	for _, attr := range node.Attr {
		if attr.Key == "class" && attr.Val == "beer-name" {
			if content := node.FirstChild; content != nil {
				*brew = content.Data
				return true
			}
		}
	}
	for kid := node.FirstChild; kid != nil; kid = kid.NextSibling {
		if findBrew(kid, brew) {
			return true
		}
	}
	return false
}

func checkId(id string) bool {
	ok, err := regexp.MatchString("^[[:xdigit:]]{24}$", id)
	if err != nil {
		return false
	}
	return ok
}

func readRc() {
	usr, err := user.Current()
	if err != nil {
		return
	}
	data, err := ioutil.ReadFile(usr.HomeDir + "/.taplistrc")
	if err != nil {
		return
	}
	lines := strings.Split(string(data), "\n")
	for _, line := range lines {
		line = strings.TrimSpace(line)
		idx := strings.IndexAny(line, " \t")
		if idx < len(line)-1 {
			id, name := line[:idx], strings.TrimSpace(line[idx:])
			barMap[id] = name
		}
	}
}

func findBar(arg string) (string, string) {
	for id, name := range barMap {
		if strings.Contains(strings.ToLower(name), strings.ToLower(arg)) {
			return id, name
		}
	}
	return "", ""
}

func main() {
	log.SetFlags(0)
	log.SetPrefix("taplist: ")

	if len(os.Args) != 2 {
		log.Fatalln("usage: taplist <id> | <name>")
	}
	readRc()
	arg := strings.ToLower(os.Args[1])
	id, name := "", ""
	if checkId(arg) {
		id, name = arg, arg
	} else {
		id, name = findBar(arg)
	}
	if id == "" {
		log.Fatalln(arg + " doesn't look like a valid name or taplister bar id")
	}

	resp, err := http.Get("http://www.taplister.com/bars/" + id)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	doc, err := html.Parse(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	beers := []beerInfo{}
	findBeer(doc, &beers)
	fmt.Println("On tap at " + name + ":\n")
	for _, beer := range beers {
		fmt.Printf("%-38.38s  %s\n", beer.brewery, beer.brew)
	}
}
<commit_msg>Pull bar description from HTML if available.<commit_after>package main

import (
	"code.google.com/p/go.net/html"
	"code.google.com/p/go.net/html/atom"

	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/user"
	"regexp"
	"strings"
)

var barMap = map[string]string{}

type beerInfo struct {
	brewery string
	brew    string
}

func findBeer(node *html.Node, beers *[]beerInfo) {
	if node.DataAtom == atom.Div {
		for _, attr := range node.Attr {
			if attr.Key == "id" && strings.HasPrefix(attr.Val, "beer-") {
				brewery, brew := "", ""
				findBrewery(node, &brewery)
				findBrew(node, &brew)
				*beers = append(*beers, beerInfo{brewery, brew})
			}
		}
	}
	for kid := node.FirstChild; kid != nil; kid = kid.NextSibling {
		findBeer(kid, beers)
	}
}

func findBrewery(node *html.Node, brewery *string) bool {
	if node.DataAtom == atom.H4 {
		if content := node.FirstChild; content != nil {
			*brewery = content.Data
			return true
		}
	}
	for kid := node.FirstChild; kid != nil; kid = kid.NextSibling {
		if findBrewery(kid, brewery) {
			return true
		}
	}
	return false
}

func findBrew(node *html.Node, brew *string) bool {
	for _, attr := range node.Attr {
		if attr.Key == "class" && attr.Val == "beer-name" {
			if content := node.FirstChild; content != nil {
				*brew = content.Data
				return true
			}
		}
	}
	for kid := node.FirstChild; kid != nil; kid = kid.NextSibling {
		if findBrew(kid, brew) {
			return true
		}
	}
	return false
}

func findBarDesc(node *html.Node, desc *string) bool {
	if node.DataAtom == atom.Meta {
		isDesc := false
		for _, attr := range node.Attr {
			if attr.Key == "name" && attr.Val == "description" {
				isDesc = true
				break
			}
		}
		if isDesc {
			for _, attr := range node.Attr {
				if attr.Key == "content" {
					*desc = attr.Val
					break
				}
			}
		}
	}
	for kid := node.FirstChild; kid != nil; kid = kid.NextSibling {
		if findBarDesc(kid, desc) {
			return true
		}
	}
	return false
}

func checkId(id string) bool {
	ok, err := regexp.MatchString("^[[:xdigit:]]{24}$", id)
	if err != nil {
		return false
	}
	return ok
}

func readRc() {
	usr, err := user.Current()
	if err != nil {
		return
	}
	data, err := ioutil.ReadFile(usr.HomeDir + "/.taplistrc")
	if err != nil {
		return
	}
	lines := strings.Split(string(data), "\n")
	for _, line := range lines {
		line = strings.TrimSpace(line)
		idx := strings.IndexAny(line, " \t")
		if idx < len(line)-1 {
			id, name := line[:idx], strings.TrimSpace(line[idx:])
			barMap[id] = name
		}
	}
}

func lookupBar(arg string) (string, string) {
	for id, name := range barMap {
		if strings.Contains(strings.ToLower(name), strings.ToLower(arg)) {
			return id, name
		}
	}
	return "", ""
}

func main() {
	log.SetFlags(0)
	log.SetPrefix("taplist: ")

	if len(os.Args) != 2 {
		log.Fatalln("usage: taplist <id> | <name>")
	}
	readRc()
	arg := strings.ToLower(os.Args[1])
	id, name := "", ""
	if checkId(arg) {
		id, name = arg, arg
	} else {
		id, name = lookupBar(arg)
	}
	if id == "" {
		log.Fatalln(arg + " doesn't look like a valid name or taplister bar id")
	}

	resp, err := http.Get("http://www.taplister.com/bars/" + id)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	doc, err := html.Parse(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	desc, beers := "", []beerInfo{}
	findBarDesc(doc, &desc)
	findBeer(doc, &beers)
	if desc != "" {
		fmt.Println(desc + "\n")
	} else {
		fmt.Printf("%d beers on tap at "+name+"\n\n", len(beers))
	}
	for _, beer := range beers {
		fmt.Printf("%-38.38s  %s\n", beer.brewery, beer.brew)
	}
}
<|endoftext|>