<commit_before>package lnwallet

import (
	"sync"

	"github.com/btcsuite/btcd/btcec"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil"
)

// ChannelReservation...
type ChannelReservation struct {
	sync.RWMutex // All fields below owned by the lnwallet.

	//for CLTV it is nLockTime, for CSV it's nSequence, for segwit it's not needed
	fundingLockTime uint32
	fundingAmount   btcutil.Amount

	//Current state of the channel, progesses through until complete
	//Makes sure we can't go backwards and only accept messages once
	channelState uint8

	theirInputs []*wire.TxIn
	ourInputs   []*wire.TxIn

	// NOTE(j): FundRequest assumes there is only one change (see ChangePkScript)
	theirChange []*wire.TxOut
	ourChange   []*wire.TxOut

	theirMultiSigKey *btcec.PublicKey

	// In order of sorted inputs. Sorting is done in accordance
	// to BIP-69: https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki.
	ourFundingSigs   [][]byte
	theirFundingSigs [][]byte

	ourCommitmentSig []byte

	partialState *OpenChannelState

	reservationID uint64
	wallet        *LightningWallet

	chanOpen chan *LightningChannel
}

// newChannelReservation...
func newChannelReservation(t FundingType, fundingAmt btcutil.Amount,
	minFeeRate btcutil.Amount, wallet *LightningWallet, id uint64) *ChannelReservation {
	return &ChannelReservation{
		fundingAmount: fundingAmt,
		// TODO(roasbeef): assumes balanced symmetric channels.
		partialState: &OpenChannelState{
			capacity:    fundingAmt * 2,
			fundingType: t,
		},
		wallet:        wallet,
		reservationID: id,
	}
}

/*//FundRequest serialize
//(reading from ChannelReservation directly to reduce the amount of copies)
//We can move this stuff to another file too if it's too big...
func (r *ChannelReservation) SerializeFundRequest() ([]byte, error) {
	var err error

	//Buffer to dump in the serialized data
	b := new(bytes.Buffer)

	//Fund Request
	err = b.WriteByte(0x30)
	if err != nil {
		return nil, err
	}

	//ChannelType (1)
	//Default to current type
	err = b.WriteByte(uint8(0))
	if err != nil {
		return nil, err
	}

	//RequesterFundingAmount - The amount we are going to fund (8)
	//check for positive values
	err = binary.Write(b, binary.BigEndian, r.FundingAmount)
	if err != nil {
		return nil, err
	}

	//RequesterChannelMinCapacity (8)
	//The amount needed to accept and sign the channel commit later
	err = binary.Write(b, binary.BigEndian, r.MinTotalFundingAmount)
	if err != nil {
		return nil, err
	}

	//RevocationHash (20)
	//Our revocation hash being contributed (for CLTV/CSV)
	_, err = b.Write(btcutil.Hash160(r.ourRevocation))
	if err != nil {
		return nil, err
	}

	//CommitPubkey (33)
	//Our public key being used for the commitment
	ourPubKey := r.ourKey.PubKey().SerializeCompressed()
	if len(ourPubKey) != 33 { //validation, can remove later? (NO UNCOMPRESSED KEYS!)
		return nil, fmt.Errorf("Serialize FundReq: our Pubkey length incorrect")
	}
	_, err = b.Write(ourPubKey)
	if err != nil {
		return nil, err
	}

	//DeliveryPkHash (20)
	//For now it's a P2PKH, but we will add an extra byte later for the
	//option for P2SH
	//This is the address to send funds to when complete or refunded
	_, err = b.Write(r.ourDeliveryAddress)
	if err != nil {
		return nil, err
	}

	//ReserveAmount (8)
	//Our own reserve amount
	err = binary.Write(b, binary.BigEndian, r.ReserveAmount)
	if err != nil {
		return nil, err
	}

	//Minimum transaction fee per kb (8)
	err = binary.Write(b, binary.BigEndian, r.MinFeePerKb)
	if err != nil {
		return nil, err
	}

	//LockTime (4)
	err = binary.Write(b, binary.BigEndian, r.FundingLockTime)
	if err != nil {
		return nil, err
	}

	//Fee payer (default to split currently) (1)
	err = binary.Write(b, binary.BigEndian, 0)
	if err != nil {
		return nil, err
	}

	//ChangePkScript
	//Length (1)
	changeScriptLength := len(r.ourChange[0].PkScript)
	if changeScriptLength > 255 {
		return nil, fmt.Errorf("Your changeScriptLength is too long!")
	}
	err = binary.Write(b, binary.BigEndian, uint8(changeScriptLength))
	if err != nil {
		return nil, err
	}
	//For now it's a P2PKH, but we will add an extra byte later for the
	//option for P2SH
	//This is the address to send change to (only allow one)
	//ChangePkScript (length of script)
	_, err = b.Write(r.ourChange[0].PkScript)
	if err != nil {
		return nil, err
	}

	//Append the unsigned(!!) txins
	//First one byte for the amount of txins (1)
	if len(r.ourInputs) > 127 {
		return nil, fmt.Errorf("Too many txins")
	}
	err = b.WriteByte(uint8(len(r.ourInputs)))
	if err != nil {
		return nil, err
	}
	//Append the actual Txins (NumOfTxins * 36)
	//Do not include the sequence number to eliminate funny business
	for _, in := range r.ourInputs {
		//Hash
		_, err = b.Write(in.PreviousOutPoint.Hash.Bytes())
		if err != nil {
			return nil, err
		}
		//Index
		err = binary.Write(b, binary.BigEndian, in.PreviousOutPoint.Index)
		if err != nil {
			return nil, err
		}
	}

	return b.Bytes(), err
}

func (r *ChannelReservation) DeserializeFundRequest(wireMsg []byte) error {
	//Make sure we're not overwriting stuff...
	//Update the channelState to 1 before progressing if you want to re-do it.
	//Assumes only one thread is writing at a time
	if r.channelState > 1 {
		return fmt.Errorf("FundRequest: Channel State Mismatch")
	}

	var err error

	b := bytes.NewBuffer(wireMsg)
	msgid, _ := b.ReadByte()
	if msgid != 0x30 {
		return fmt.Errorf("Cannot deserialize: not a funding request")
	}

	if err != nil {
		return err
	}
	//Update the channel state as complete
	r.channelState = 2

	return nil
}

//Validation on the data being supplied from the fund request
func (r *ChannelReservation) ValidateFundRequest(wireMsg []byte) error {
	return nil
}

//Serialize CSV Revocation
//After the Commitment Transaction has been created, send a message to revoke this tx
func (r *ChannelReservation) SerializeCSVRefundRevocation() ([]byte, error) {
	return nil, nil
}

//Deserialize CSV Revocation
//Validate the revocation, after this step, the channel is fully set up
func (r *ChannelReservation) DeserializeCSVRefundRevocation() error {
	return nil
}*/

// OurFunds...
func (r *ChannelReservation) OurFunds() ([]*wire.TxIn, []*wire.TxOut) {
	r.RLock()
	defer r.RUnlock()
	return r.ourInputs, r.ourChange
}

func (r *ChannelReservation) OurKeys() (*btcec.PrivateKey, *btcec.PrivateKey) {
	r.RLock()
	defer r.RUnlock()
	return r.partialState.multiSigKey, r.partialState.ourCommitKey
}

// AddFunds...
// TODO(roasbeef): add commitment txns, etc.
func (r *ChannelReservation) AddFunds(theirInputs []*wire.TxIn, theirChangeOutputs []*wire.TxOut, multiSigKey *btcec.PublicKey) error {
	errChan := make(chan error, 1)

	r.wallet.msgChan <- &addCounterPartyFundsMsg{
		pendingFundingID:   r.reservationID,
		theirInputs:        theirInputs,
		theirChangeOutputs: theirChangeOutputs,
		theirKey:           multiSigKey,
		err:                errChan,
	}

	return <-errChan
}

// OurFundingSigs...
func (r *ChannelReservation) OurFundingSigs() [][]byte {
	r.RLock()
	defer r.RUnlock()
	return r.ourFundingSigs
}

// OurCommitmentSig
func (r *ChannelReservation) OurCommitmentSig() []byte {
	r.RLock()
	defer r.RUnlock()
	return r.ourCommitmentSig
}

// TheirFunds...
// TODO(roasbeef): return error if accessors not yet populated?
func (r *ChannelReservation) TheirFunds() ([]*wire.TxIn, []*wire.TxOut) {
	r.RLock()
	defer r.RUnlock()
	return r.theirInputs, r.theirChange
}

func (r *ChannelReservation) TheirKeys() (*btcec.PublicKey, *btcec.PublicKey) {
	r.RLock()
	defer r.RUnlock()
	return r.theirMultiSigKey, r.partialState.theirCommitKey
}

// CompleteFundingReservation...
// TODO(roasbeef): add commit sig also
func (r *ChannelReservation) CompleteReservation(theirSigs [][]byte) error {
	errChan := make(chan error, 1)

	r.wallet.msgChan <- &addCounterPartySigsMsg{
		pendingFundingID: r.reservationID,
		theirSigs:        theirSigs,
		err:              errChan,
	}

	return <-errChan
}

// FinalFundingTransaction...
func (r *ChannelReservation) FundingTx() *wire.MsgTx {
	r.RLock()
	defer r.RUnlock()
	return r.partialState.fundingTx
}

// RequestFundingReserveCancellation...
// TODO(roasbeef): also return mutated state?
func (r *ChannelReservation) Cancel() error {
	errChan := make(chan error, 1)
	r.wallet.msgChan <- &fundingReserveCancelMsg{
		pendingFundingID: r.reservationID,
		err:              errChan,
	}

	return <-errChan
}

// WaitForChannelOpen...
func (r *ChannelReservation) WaitForChannelOpen() *LightningChannel {
	return nil
}

// * finish reset of tests
// * comment out stuff that'll need a node.
// * start on commitment side
//   * implement rusty's shachain
//   * set up logic to get notification from node when funding tx gets 6 deep.
//     * prob spawn into ChainNotifier struct
//   * create builder for initial funding transaction
//     * fascade through the wallet, for signing and such.
//   * channel should have active namespace to it's bucket, query at that point fo past commits etc
<commit_msg>lnwallet/reservation: fundingLockTime is int64 track their revoke hash<commit_after>package lnwallet

import (
	"sync"

	"github.com/btcsuite/btcd/btcec"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil"
)

// ChannelReservation...
type ChannelReservation struct {
	sync.RWMutex // All fields below owned by the lnwallet.

	//for CLTV it is nLockTime, for CSV it's nSequence, for segwit it's not needed
	fundingLockTime int64
	fundingAmount   btcutil.Amount

	//Current state of the channel, progesses through until complete
	//Makes sure we can't go backwards and only accept messages once
	channelState uint8

	theirInputs []*wire.TxIn
	ourInputs   []*wire.TxIn

	// NOTE(j): FundRequest assumes there is only one change (see ChangePkScript)
	theirChange []*wire.TxOut
	ourChange   []*wire.TxOut

	theirMultiSigKey *btcec.PublicKey

	// In order of sorted inputs. Sorting is done in accordance
	// to BIP-69: https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki.
	ourFundingSigs   [][]byte
	theirFundingSigs [][]byte

	ourRevokeHash    [wire.HashSize]byte
	ourCommitmentSig []byte

	partialState *OpenChannelState

	reservationID uint64
	wallet        *LightningWallet

	chanOpen chan *LightningChannel
}

// newChannelReservation...
func newChannelReservation(t FundingType, fundingAmt btcutil.Amount,
	minFeeRate btcutil.Amount, wallet *LightningWallet, id uint64) *ChannelReservation {
	return &ChannelReservation{
		fundingAmount: fundingAmt,
		// TODO(roasbeef): assumes balanced symmetric channels.
		partialState: &OpenChannelState{
			capacity:    fundingAmt * 2,
			fundingType: t,
		},
		wallet:        wallet,
		reservationID: id,
	}
}

/*//FundRequest serialize
//(reading from ChannelReservation directly to reduce the amount of copies)
//We can move this stuff to another file too if it's too big...
func (r *ChannelReservation) SerializeFundRequest() ([]byte, error) {
	var err error

	//Buffer to dump in the serialized data
	b := new(bytes.Buffer)

	//Fund Request
	err = b.WriteByte(0x30)
	if err != nil {
		return nil, err
	}

	//ChannelType (1)
	//Default to current type
	err = b.WriteByte(uint8(0))
	if err != nil {
		return nil, err
	}

	//RequesterFundingAmount - The amount we are going to fund (8)
	//check for positive values
	err = binary.Write(b, binary.BigEndian, r.FundingAmount)
	if err != nil {
		return nil, err
	}

	//RequesterChannelMinCapacity (8)
	//The amount needed to accept and sign the channel commit later
	err = binary.Write(b, binary.BigEndian, r.MinTotalFundingAmount)
	if err != nil {
		return nil, err
	}

	//RevocationHash (20)
	//Our revocation hash being contributed (for CLTV/CSV)
	_, err = b.Write(btcutil.Hash160(r.ourRevocation))
	if err != nil {
		return nil, err
	}

	//CommitPubkey (33)
	//Our public key being used for the commitment
	ourPubKey := r.ourKey.PubKey().SerializeCompressed()
	if len(ourPubKey) != 33 { //validation, can remove later? (NO UNCOMPRESSED KEYS!)
		return nil, fmt.Errorf("Serialize FundReq: our Pubkey length incorrect")
	}
	_, err = b.Write(ourPubKey)
	if err != nil {
		return nil, err
	}

	//DeliveryPkHash (20)
	//For now it's a P2PKH, but we will add an extra byte later for the
	//option for P2SH
	//This is the address to send funds to when complete or refunded
	_, err = b.Write(r.ourDeliveryAddress)
	if err != nil {
		return nil, err
	}

	//ReserveAmount (8)
	//Our own reserve amount
	err = binary.Write(b, binary.BigEndian, r.ReserveAmount)
	if err != nil {
		return nil, err
	}

	//Minimum transaction fee per kb (8)
	err = binary.Write(b, binary.BigEndian, r.MinFeePerKb)
	if err != nil {
		return nil, err
	}

	//LockTime (4)
	err = binary.Write(b, binary.BigEndian, r.FundingLockTime)
	if err != nil {
		return nil, err
	}

	//Fee payer (default to split currently) (1)
	err = binary.Write(b, binary.BigEndian, 0)
	if err != nil {
		return nil, err
	}

	//ChangePkScript
	//Length (1)
	changeScriptLength := len(r.ourChange[0].PkScript)
	if changeScriptLength > 255 {
		return nil, fmt.Errorf("Your changeScriptLength is too long!")
	}
	err = binary.Write(b, binary.BigEndian, uint8(changeScriptLength))
	if err != nil {
		return nil, err
	}
	//For now it's a P2PKH, but we will add an extra byte later for the
	//option for P2SH
	//This is the address to send change to (only allow one)
	//ChangePkScript (length of script)
	_, err = b.Write(r.ourChange[0].PkScript)
	if err != nil {
		return nil, err
	}

	//Append the unsigned(!!) txins
	//First one byte for the amount of txins (1)
	if len(r.ourInputs) > 127 {
		return nil, fmt.Errorf("Too many txins")
	}
	err = b.WriteByte(uint8(len(r.ourInputs)))
	if err != nil {
		return nil, err
	}
	//Append the actual Txins (NumOfTxins * 36)
	//Do not include the sequence number to eliminate funny business
	for _, in := range r.ourInputs {
		//Hash
		_, err = b.Write(in.PreviousOutPoint.Hash.Bytes())
		if err != nil {
			return nil, err
		}
		//Index
		err = binary.Write(b, binary.BigEndian, in.PreviousOutPoint.Index)
		if err != nil {
			return nil, err
		}
	}

	return b.Bytes(), err
}

func (r *ChannelReservation) DeserializeFundRequest(wireMsg []byte) error {
	//Make sure we're not overwriting stuff...
	//Update the channelState to 1 before progressing if you want to re-do it.
	//Assumes only one thread is writing at a time
	if r.channelState > 1 {
		return fmt.Errorf("FundRequest: Channel State Mismatch")
	}

	var err error

	b := bytes.NewBuffer(wireMsg)
	msgid, _ := b.ReadByte()
	if msgid != 0x30 {
		return fmt.Errorf("Cannot deserialize: not a funding request")
	}

	if err != nil {
		return err
	}
	//Update the channel state as complete
	r.channelState = 2

	return nil
}

//Validation on the data being supplied from the fund request
func (r *ChannelReservation) ValidateFundRequest(wireMsg []byte) error {
	return nil
}

//Serialize CSV Revocation
//After the Commitment Transaction has been created, send a message to revoke this tx
func (r *ChannelReservation) SerializeCSVRefundRevocation() ([]byte, error) {
	return nil, nil
}

//Deserialize CSV Revocation
//Validate the revocation, after this step, the channel is fully set up
func (r *ChannelReservation) DeserializeCSVRefundRevocation() error {
	return nil
}*/

// OurFunds...
func (r *ChannelReservation) OurFunds() ([]*wire.TxIn, []*wire.TxOut) {
	r.RLock()
	defer r.RUnlock()
	return r.ourInputs, r.ourChange
}

func (r *ChannelReservation) OurKeys() (*btcec.PrivateKey, *btcec.PrivateKey) {
	r.RLock()
	defer r.RUnlock()
	return r.partialState.multiSigKey, r.partialState.ourCommitKey
}

// AddFunds...
// TODO(roasbeef): add commitment txns, etc.
func (r *ChannelReservation) AddFunds(theirInputs []*wire.TxIn, theirChangeOutputs []*wire.TxOut, multiSigKey *btcec.PublicKey) error {
	errChan := make(chan error, 1)

	r.wallet.msgChan <- &addCounterPartyFundsMsg{
		pendingFundingID:   r.reservationID,
		theirInputs:        theirInputs,
		theirChangeOutputs: theirChangeOutputs,
		theirKey:           multiSigKey,
		err:                errChan,
	}

	return <-errChan
}

// OurFundingSigs...
func (r *ChannelReservation) OurFundingSigs() [][]byte {
	r.RLock()
	defer r.RUnlock()
	return r.ourFundingSigs
}

// OurCommitmentSig
func (r *ChannelReservation) OurCommitmentSig() []byte {
	r.RLock()
	defer r.RUnlock()
	return r.ourCommitmentSig
}

// TheirFunds...
// TODO(roasbeef): return error if accessors not yet populated?
func (r *ChannelReservation) TheirFunds() ([]*wire.TxIn, []*wire.TxOut) {
	r.RLock()
	defer r.RUnlock()
	return r.theirInputs, r.theirChange
}

func (r *ChannelReservation) TheirKeys() (*btcec.PublicKey, *btcec.PublicKey) {
	r.RLock()
	defer r.RUnlock()
	return r.theirMultiSigKey, r.partialState.theirCommitKey
}

// CompleteFundingReservation...
// TODO(roasbeef): add commit sig also
func (r *ChannelReservation) CompleteReservation(theirSigs [][]byte) error {
	errChan := make(chan error, 1)

	r.wallet.msgChan <- &addCounterPartySigsMsg{
		pendingFundingID: r.reservationID,
		theirSigs:        theirSigs,
		err:              errChan,
	}

	return <-errChan
}

// FinalFundingTransaction...
func (r *ChannelReservation) FundingTx() *wire.MsgTx {
	r.RLock()
	defer r.RUnlock()
	return r.partialState.fundingTx
}

// RequestFundingReserveCancellation...
// TODO(roasbeef): also return mutated state?
func (r *ChannelReservation) Cancel() error {
	errChan := make(chan error, 1)
	r.wallet.msgChan <- &fundingReserveCancelMsg{
		pendingFundingID: r.reservationID,
		err:              errChan,
	}

	return <-errChan
}

// WaitForChannelOpen...
func (r *ChannelReservation) WaitForChannelOpen() *LightningChannel {
	return nil
}

// * finish reset of tests
// * comment out stuff that'll need a node.
// * start on commitment side
//   * implement rusty's shachain
//   * set up logic to get notification from node when funding tx gets 6 deep.
//     * prob spawn into ChainNotifier struct
//   * create builder for initial funding transaction
//     * fascade through the wallet, for signing and such.
//   * channel should have active namespace to it's bucket, query at that point fo past commits etc
<|endoftext|>


<commit_before>// (c) 2014 Rick Arnold. Licensed under the BSD license (see LICENSE).

package cal

import "time"

// Holidays in Germany
var (
	DENeujahr                = NewYear
	DEHeiligeDreiKoenige     = NewHoliday(time.January, 6)
	DEKarFreitag             = GoodFriday
	DEOstersonntag           = NewHolidayFunc(calculateOstersonntag)
	DEOstermontag            = EasterMonday
	DETagderArbeit           = NewHoliday(time.May, 1)
	DEChristiHimmelfahrt     = NewHolidayFunc(calculateHimmelfahrt)
	DEPfingstsonntag         = NewHolidayFunc(calculatePfingstSonntag)
	DEPfingstmontag          = NewHolidayFunc(calculatePfingstMontag)
	DEFronleichnam           = NewHolidayFunc(calculateFronleichnam)
	DEMariaHimmelfahrt       = NewHoliday(time.August, 15)
	DETagderDeutschenEinheit = NewHoliday(time.October, 3)
	DEReformationstag        = NewHoliday(time.October, 31)
	DEReformationstag2017    = NewHolidayExact(time.October, 31, 2017)
	DEAllerheiligen          = NewHoliday(time.November, 1)
	DEBußUndBettag           = NewHolidayFunc(calculateBußUndBettag)
	DEErsterWeihnachtstag    = Christmas
	DEZweiterWeihnachtstag   = Christmas2
)

// AddGermanHolidays adds all German holidays to the Calendar
func AddGermanHolidays(c *Calendar) {
	c.AddHoliday(
		DENeujahr,
		DEKarFreitag,
		DEOstermontag,
		DETagderArbeit,
		DEChristiHimmelfahrt,
		DEPfingstmontag,
		DETagderDeutschenEinheit,
		DEErsterWeihnachtstag,
		DEZweiterWeihnachtstag,
	)
}

// AddGermanyStateHolidays adds german state holidays to the calendar
func AddGermanyStateHolidays(c *Calendar, state string) {
	switch state {
	case "BB": // Brandenburg
		c.AddHoliday(
			DEOstersonntag,
			DEPfingstsonntag,
			DEReformationstag,
		)
	case "BW": // Baden-Württemberg
		c.AddHoliday(
			DEHeiligeDreiKoenige,
			DEFronleichnam,
			DEAllerheiligen,
			DEReformationstag2017,
		)
	case "BY": // Bayern
		c.AddHoliday(
			DEHeiligeDreiKoenige,
			DEFronleichnam,
			DEMariaHimmelfahrt,
			DEAllerheiligen,
			DEReformationstag2017,
		)
	case "HE": // Hessen
		c.AddHoliday(DEFronleichnam)
	case "MV": // Mecklenburg-Vorpommern
		c.AddHoliday(DEReformationstag)
	case "NW": // Nordrhein-Westfalen
		c.AddHoliday(
			DEFronleichnam,
			DEAllerheiligen,
			DEReformationstag2017,
		)
	case "RP": // Rheinland-Pfalz
		c.AddHoliday(
			DEFronleichnam,
			DEAllerheiligen,
			DEReformationstag2017,
		)
	case "SA": // Sachsen
		c.AddHoliday(
			DEFronleichnam,
			DEReformationstag,
			DEBußUndBettag,
		)
	case "SL": // Saarland
		c.AddHoliday(
			DEFronleichnam,
			DEAllerheiligen,
			DEMariaHimmelfahrt,
			DEReformationstag2017,
		)
	case "ST": // Sachen-Anhalt
		c.AddHoliday(
			DEHeiligeDreiKoenige,
			DEReformationstag,
		)
	case "TH": // Thüringen
		c.AddHoliday(
			DEFronleichnam,
			DEReformationstag,
		)
	}
}

func calculateOstersonntag(year int, loc *time.Location) (time.Month, int) {
	easter := calculateEaster(year, loc)
	return easter.Month(), easter.Day()
}

func calculateHimmelfahrt(year int, loc *time.Location) (time.Month, int) {
	easter := calculateEaster(year, loc)
	// 39 days after Easter Sunday
	em := easter.AddDate(0, 0, +39)
	return em.Month(), em.Day()
}

func calculatePfingstSonntag(year int, loc *time.Location) (time.Month, int) {
	easter := calculateEaster(year, loc)
	// 50 days after Easter Sunday
	em := easter.AddDate(0, 0, +49)
	return em.Month(), em.Day()
}

func calculatePfingstMontag(year int, loc *time.Location) (time.Month, int) {
	easter := calculateEaster(year, loc)
	// 50 days after Easter Sunday
	em := easter.AddDate(0, 0, +50)
	return em.Month(), em.Day()
}

func calculateFronleichnam(year int, loc *time.Location) (time.Month, int) {
	easter := calculateEaster(year, loc)
	// 50 days after Easter Sunday
	em := easter.AddDate(0, 0, +60)
	return em.Month(), em.Day()
}

func calculateBußUndBettag(year int, loc *time.Location) (time.Month, int) {
	t := time.Date(year, 11, 23, 0, 0, 0, 0, loc)

	for i := -1; i > -10; i-- {
		d := t.Add(time.Hour * 24 * time.Duration(i))
		if d.Weekday() == time.Wednesday {
			t = d
			break
		}
	}
	return t.Month(), t.Day()
}
<commit_msg>add Internationaler Frauentag for Berlin<commit_after>// (c) 2014 Rick Arnold. Licensed under the BSD license (see LICENSE).

package cal

import "time"

// Holidays in Germany
var (
	DENeujahr                  = NewYear
	DEHeiligeDreiKoenige       = NewHoliday(time.January, 6)
	DEInternationalerFrauentag = NewHoliday(time.March, 8)
	DEKarFreitag               = GoodFriday
	DEOstersonntag             = NewHolidayFunc(calculateOstersonntag)
	DEOstermontag              = EasterMonday
	DETagderArbeit             = NewHoliday(time.May, 1)
	DEChristiHimmelfahrt       = NewHolidayFunc(calculateHimmelfahrt)
	DEPfingstsonntag           = NewHolidayFunc(calculatePfingstSonntag)
	DEPfingstmontag            = NewHolidayFunc(calculatePfingstMontag)
	DEFronleichnam             = NewHolidayFunc(calculateFronleichnam)
	DEMariaHimmelfahrt         = NewHoliday(time.August, 15)
	DETagderDeutschenEinheit   = NewHoliday(time.October, 3)
	DEReformationstag          = NewHoliday(time.October, 31)
	DEReformationstag2017      = NewHolidayExact(time.October, 31, 2017)
	DEAllerheiligen            = NewHoliday(time.November, 1)
	DEBußUndBettag             = NewHolidayFunc(calculateBußUndBettag)
	DEErsterWeihnachtstag      = Christmas
	DEZweiterWeihnachtstag     = Christmas2
)

// AddGermanHolidays adds all German holidays to the Calendar
func AddGermanHolidays(c *Calendar) {
	c.AddHoliday(
		DENeujahr,
		DEKarFreitag,
		DEOstermontag,
		DETagderArbeit,
		DEChristiHimmelfahrt,
		DEPfingstmontag,
		DETagderDeutschenEinheit,
		DEErsterWeihnachtstag,
		DEZweiterWeihnachtstag,
	)
}

// AddGermanyStateHolidays adds german state holidays to the calendar
func AddGermanyStateHolidays(c *Calendar, state string) {
	switch state {
	case "BB": // Brandenburg
		c.AddHoliday(
			DEOstersonntag,
			DEPfingstsonntag,
			DEReformationstag,
		)
	case "BE": // Berlin
		c.AddHoliday(
			DEInternationalerFrauentag,
		)
	case "BW": // Baden-Württemberg
		c.AddHoliday(
			DEHeiligeDreiKoenige,
			DEFronleichnam,
			DEAllerheiligen,
			DEReformationstag2017,
		)
	case "BY": // Bayern
		c.AddHoliday(
			DEHeiligeDreiKoenige,
			DEFronleichnam,
			DEMariaHimmelfahrt,
			DEAllerheiligen,
			DEReformationstag2017,
		)
	case "HE": // Hessen
		c.AddHoliday(DEFronleichnam)
	case "MV": // Mecklenburg-Vorpommern
		c.AddHoliday(DEReformationstag)
	case "NW": // Nordrhein-Westfalen
		c.AddHoliday(
			DEFronleichnam,
			DEAllerheiligen,
			DEReformationstag2017,
		)
	case "RP": // Rheinland-Pfalz
		c.AddHoliday(
			DEFronleichnam,
			DEAllerheiligen,
			DEReformationstag2017,
		)
	case "SA": // Sachsen
		c.AddHoliday(
			DEFronleichnam,
			DEReformationstag,
			DEBußUndBettag,
		)
	case "SL": // Saarland
		c.AddHoliday(
			DEFronleichnam,
			DEAllerheiligen,
			DEMariaHimmelfahrt,
			DEReformationstag2017,
		)
	case "ST": // Sachen-Anhalt
		c.AddHoliday(
			DEHeiligeDreiKoenige,
			DEReformationstag,
		)
	case "TH": // Thüringen
		c.AddHoliday(
			DEFronleichnam,
			DEReformationstag,
		)
	}
}

func calculateOstersonntag(year int, loc *time.Location) (time.Month, int) {
	easter := calculateEaster(year, loc)
	return easter.Month(), easter.Day()
}

func calculateHimmelfahrt(year int, loc *time.Location) (time.Month, int) {
	easter := calculateEaster(year, loc)
	// 39 days after Easter Sunday
	em := easter.AddDate(0, 0, +39)
	return em.Month(), em.Day()
}

func calculatePfingstSonntag(year int, loc *time.Location) (time.Month, int) {
	easter := calculateEaster(year, loc)
	// 50 days after Easter Sunday
	em := easter.AddDate(0, 0, +49)
	return em.Month(), em.Day()
}

func calculatePfingstMontag(year int, loc *time.Location) (time.Month, int) {
	easter := calculateEaster(year, loc)
	// 50 days after Easter Sunday
	em := easter.AddDate(0, 0, +50)
	return em.Month(), em.Day()
}

func calculateFronleichnam(year int, loc *time.Location) (time.Month, int) {
	easter := calculateEaster(year, loc)
	// 50 days after Easter Sunday
	em := easter.AddDate(0, 0, +60)
	return em.Month(), em.Day()
}

func calculateBußUndBettag(year int, loc *time.Location) (time.Month, int) {
	t := time.Date(year, 11, 23, 0, 0, 0, 0, loc)

	for i := -1; i > -10; i-- {
		d := t.Add(time.Hour * 24 * time.Duration(i))
		if d.Weekday() == time.Wednesday {
			t = d
			break
		}
	}
	return t.Month(), t.Day()
}
<|endoftext|>


<commit_before>package httpjson

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/internal"
	"github.com/influxdata/telegraf/plugins/inputs"
	"github.com/influxdata/telegraf/plugins/parsers"
)

// HttpJson struct
type HttpJson struct {
	Name            string
	Namespace       string
	Servers         []string
	Method          string
	TagKeys         []string
	ResponseTimeout internal.Duration
	Parameters      map[string]string
	Headers         map[string]string

	// Path to CA file
	SSLCA string `toml:"ssl_ca"`
	// Path to host cert file
	SSLCert string `toml:"ssl_cert"`
	// Path to cert key file
	SSLKey string `toml:"ssl_key"`
	// Use SSL but skip chain & host verification
	InsecureSkipVerify bool

	client HTTPClient
}

type HTTPClient interface {
	// Returns the result of an http request
	//
	// Parameters:
	// req: HTTP request object
	//
	// Returns:
	// http.Response:  HTTP respons object
	// error        :  Any error that may have occurred
	MakeRequest(req *http.Request) (*http.Response, error)

	SetHTTPClient(client *http.Client)
	HTTPClient() *http.Client
}

type RealHTTPClient struct {
	client *http.Client
}

func (c *RealHTTPClient) MakeRequest(req *http.Request) (*http.Response, error) {
	return c.client.Do(req)
}

func (c *RealHTTPClient) SetHTTPClient(client *http.Client) {
	c.client = client
}

func (c *RealHTTPClient) HTTPClient() *http.Client {
	return c.client
}

var sampleConfig = `
  ## NOTE This plugin only reads numerical measurements, strings and booleans
  ## will be ignored.

  ## a name for the service being polled
  name = "webserver_stats"
  ## a namespace used as as extra tag in measurement
  namespace = "bbox"

  ## URL of each server in the service's cluster
  servers = [
    "http://localhost:9999/stats/",
    "http://localhost:9998/stats/",
  ]
  ## Set response_timeout (default 5 seconds)
  response_timeout = "5s"

  ## HTTP method to use: GET or POST (case-sensitive)
  method = "GET"

  ## List of tag names to extract from top-level of JSON server response
  # tag_keys = [
  #   "my_tag_1",
  #   "my_tag_2"
  # ]

  ## HTTP parameters (all values must be strings)
  [inputs.httpjson.parameters]
    event_type = "cpu_spike"
    threshold = "0.75"

  ## HTTP Header parameters (all values must be strings)
  # [inputs.httpjson.headers]
  #   X-Auth-Token = "my-xauth-token"
  #   apiVersion = "v1"

  ## Optional SSL Config
  # ssl_ca = "/etc/telegraf/ca.pem"
  # ssl_cert = "/etc/telegraf/cert.pem"
  # ssl_key = "/etc/telegraf/key.pem"
  ## Use SSL but skip chain & host verification
  # insecure_skip_verify = false
`

func (h *HttpJson) SampleConfig() string {
	return sampleConfig
}

func (h *HttpJson) Description() string {
	return "Read flattened metrics from one or more JSON HTTP endpoints"
}

// Gathers data for all servers.
func (h *HttpJson) Gather(acc telegraf.Accumulator) error {
	var wg sync.WaitGroup

	if h.client.HTTPClient() == nil {
		tlsCfg, err := internal.GetTLSConfig(
			h.SSLCert, h.SSLKey, h.SSLCA, h.InsecureSkipVerify)
		if err != nil {
			return err
		}
		tr := &http.Transport{
			ResponseHeaderTimeout: h.ResponseTimeout.Duration,
			TLSClientConfig:       tlsCfg,
		}
		client := &http.Client{
			Transport: tr,
			Timeout:   h.ResponseTimeout.Duration,
		}
		h.client.SetHTTPClient(client)
	}

	errorChannel := make(chan error, len(h.Servers))

	for _, server := range h.Servers {
		wg.Add(1)
		go func(server string) {
			defer wg.Done()
			if err := h.gatherServer(acc, server); err != nil {
				errorChannel <- err
			}
		}(server)
	}

	wg.Wait()
	close(errorChannel)

	// Get all errors and return them as one giant error
	errorStrings := []string{}
	for err := range errorChannel {
		errorStrings = append(errorStrings, err.Error())
	}

	if len(errorStrings) == 0 {
		return nil
	}
	return errors.New(strings.Join(errorStrings, "\n"))
}

// Gathers data from a particular server
// Parameters:
//     acc      : The telegraf Accumulator to use
//     serverURL: endpoint to send request to
//     service  : the service being queried
//
// Returns:
//     error: Any error that may have occurred
func (h *HttpJson) gatherServer(
	acc telegraf.Accumulator,
	serverURL string,
) error {
	resp, _, err := h.sendRequest(serverURL)

	if err != nil {
		return err
	}

	var msrmnt_name string
	if h.Name == "" {
		msrmnt_name = "httpjson"
	} else {
		msrmnt_name = "httpjson_" + h.Name
	}
	tags := map[string]string{
		"server":    serverURL,
		"namespace": h.Namespace,
	}

	parser, err := parsers.NewJSONParser(msrmnt_name, h.TagKeys, tags)
	if err != nil {
		return err
	}

	metrics, err := parser.Parse([]byte(resp))
	if err != nil {
		return err
	}

	for _, metric := range metrics {
		metricGroups := make(map[string]map[string]interface{})

		for fieldName, fieldValue := range metric.Fields() {
			lastDot := strings.LastIndex(fieldName, ".")
			newMetricGroupName := fieldName
			newFieldName := "value"

			if lastDot > 0 {
				newMetricGroupName = metric.Name() + "." + fieldName[:lastDot]
				newFieldName = fieldName[lastDot+1 : len(fieldName)]
			} else {
				newMetricGroupName = metric.Name() + "." + newFieldName
			}

			if newFieldName == "time" {
				newFieldName = "timeValue"
			}

			add(metricGroups, newMetricGroupName, newFieldName, fieldValue)
		}

		for metricGroupName, fields := range metricGroups {
			acc.AddFields(metricGroupName, fields, metric.Tags())
		}

	}
	return nil
}

func add(m map[string]map[string]interface{}, metricGroupName, fieldName string, fieldValue interface{}) {
	mm, ok := m[metricGroupName]
	if !ok {
		mm = make(map[string]interface{})
		m[metricGroupName] = mm
	}
	mm[fieldName] = fieldValue
}

// Sends an HTTP request to the server using the HttpJson object's HTTPClient.
// This request can be either a GET or a POST.
// Parameters:
//     serverURL: endpoint to send request to
//
// Returns:
//     string: body of the response
//     error : Any error that may have occurred
func (h *HttpJson) sendRequest(serverURL string) (string, float64, error) {
	// Prepare URL
	requestURL, err := url.Parse(serverURL)
	if err != nil {
		return "", -1, fmt.Errorf("Invalid server URL \"%s\"", serverURL)
	}

	data := url.Values{}
	switch {
	case h.Method == "GET":
		params := requestURL.Query()
		for k, v := range h.Parameters {
			params.Add(k, v)
		}
		requestURL.RawQuery = params.Encode()

	case h.Method == "POST":
		requestURL.RawQuery = ""
		for k, v := range h.Parameters {
			data.Add(k, v)
		}
	}

	// Create + send request
	req, err := http.NewRequest(h.Method, requestURL.String(),
		strings.NewReader(data.Encode()))
	if err != nil {
		return "", -1, err
	}

	// Add header parameters
	for k, v := range h.Headers {
		if strings.ToLower(k) == "host" {
			req.Host = v
		} else {
			req.Header.Add(k, v)
		}
	}

	start := time.Now()
	resp, err := h.client.MakeRequest(req)
	if err != nil {
		return "", -1, err
	}

	defer resp.Body.Close()
	responseTime := time.Since(start).Seconds()

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

	// Process response
	if resp.StatusCode != http.StatusOK {
		err = fmt.Errorf("Response from url \"%s\" has status code %d (%s), expected %d (%s)",
			requestURL.String(),
			resp.StatusCode,
			http.StatusText(resp.StatusCode),
			http.StatusOK,
			http.StatusText(http.StatusOK))
		return string(body), responseTime, err
	}

	return string(body), responseTime, err
}

func init() {
	inputs.Add("httpjson", func() telegraf.Input {
		return &HttpJson{
			client: &RealHTTPClient{},
			ResponseTimeout: internal.Duration{
				Duration: 5 * time.Second,
			},
		}
	})
}
<commit_msg>use original fieldname<commit_after>package httpjson

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/internal"
	"github.com/influxdata/telegraf/plugins/inputs"
	"github.com/influxdata/telegraf/plugins/parsers"
)

// HttpJson struct
type HttpJson struct {
	Name            string
	Namespace       string
	Servers         []string
	Method          string
	TagKeys         []string
	ResponseTimeout internal.Duration
	Parameters      map[string]string
	Headers         map[string]string

	// Path to CA file
	SSLCA string `toml:"ssl_ca"`
	// Path to host cert file
	SSLCert string `toml:"ssl_cert"`
	// Path to cert key file
	SSLKey string `toml:"ssl_key"`
	// Use SSL but skip chain & host verification
	InsecureSkipVerify bool

	client HTTPClient
}

type HTTPClient interface {
	// Returns the result of an http request
	//
	// Parameters:
	// req: HTTP request object
	//
	// Returns:
	// http.Response:  HTTP respons object
	// error        :  Any error that may have occurred
	MakeRequest(req *http.Request) (*http.Response, error)

	SetHTTPClient(client *http.Client)
	HTTPClient() *http.Client
}

type RealHTTPClient struct {
	client *http.Client
}

func (c *RealHTTPClient) MakeRequest(req *http.Request) (*http.Response, error) {
	return c.client.Do(req)
}

func (c *RealHTTPClient) SetHTTPClient(client *http.Client) {
	c.client = client
}

func (c *RealHTTPClient) HTTPClient() *http.Client {
	return c.client
}

var sampleConfig = `
  ## NOTE This plugin only reads numerical measurements, strings and booleans
  ## will be ignored.

  ## a name for the service being polled
  name = "webserver_stats"
  ## a namespace used as as extra tag in measurement
  namespace = "bbox"

  ## URL of each server in the service's cluster
  servers = [
    "http://localhost:9999/stats/",
    "http://localhost:9998/stats/",
  ]
  ## Set response_timeout (default 5 seconds)
  response_timeout = "5s"

  ## HTTP method to use: GET or POST (case-sensitive)
  method = "GET"

  ## List of tag names to extract from top-level of JSON server response
  # tag_keys = [
  #   "my_tag_1",
  #   "my_tag_2"
  # ]

  ## HTTP parameters (all values must be strings)
  [inputs.httpjson.parameters]
    event_type = "cpu_spike"
    threshold = "0.75"

  ## HTTP Header parameters (all values must be strings)
  # [inputs.httpjson.headers]
  #   X-Auth-Token = "my-xauth-token"
  #   apiVersion = "v1"

  ## Optional SSL Config
  # ssl_ca = "/etc/telegraf/ca.pem"
  # ssl_cert = "/etc/telegraf/cert.pem"
  # ssl_key = "/etc/telegraf/key.pem"
  ## Use SSL but skip chain & host verification
  # insecure_skip_verify = false
`

func (h *HttpJson) SampleConfig() string {
	return sampleConfig
}

func (h *HttpJson) Description() string {
	return "Read flattened metrics from one or more JSON HTTP endpoints"
}

// Gathers data for all servers.
func (h *HttpJson) Gather(acc telegraf.Accumulator) error {
	var wg sync.WaitGroup

	if h.client.HTTPClient() == nil {
		tlsCfg, err := internal.GetTLSConfig(
			h.SSLCert, h.SSLKey, h.SSLCA, h.InsecureSkipVerify)
		if err != nil {
			return err
		}
		tr := &http.Transport{
			ResponseHeaderTimeout: h.ResponseTimeout.Duration,
			TLSClientConfig:       tlsCfg,
		}
		client := &http.Client{
			Transport: tr,
			Timeout:   h.ResponseTimeout.Duration,
		}
		h.client.SetHTTPClient(client)
	}

	errorChannel := make(chan error, len(h.Servers))

	for _, server := range h.Servers {
		wg.Add(1)
		go func(server string) {
			defer wg.Done()
			if err := h.gatherServer(acc, server); err != nil {
				errorChannel <- err
			}
		}(server)
	}

	wg.Wait()
	close(errorChannel)

	// Get all errors and return them as one giant error
	errorStrings := []string{}
	for err := range errorChannel {
		errorStrings = append(errorStrings, err.Error())
	}

	if len(errorStrings) == 0 {
		return nil
	}
	return errors.New(strings.Join(errorStrings, "\n"))
}

// Gathers data from a particular server
// Parameters:
//     acc      : The telegraf Accumulator to use
//     serverURL: endpoint to send request to
//     service  : the service being queried
//
// Returns:
//     error: Any error that may have occurred
func (h *HttpJson) gatherServer(
	acc telegraf.Accumulator,
	serverURL string,
) error {
	resp, _, err := h.sendRequest(serverURL)

	if err != nil {
		return err
	}

	var msrmnt_name string
	if h.Name == "" {
		msrmnt_name = "httpjson"
	} else {
		msrmnt_name = "httpjson_" + h.Name
	}
	tags := map[string]string{
		"server":    serverURL,
		"namespace": h.Namespace,
	}

	parser, err := parsers.NewJSONParser(msrmnt_name, h.TagKeys, tags)
	if err != nil {
		return err
	}

	metrics, err := parser.Parse([]byte(resp))
	if err != nil {
		return err
	}

	for _, metric := range metrics {
		metricGroups := make(map[string]map[string]interface{})

		for fieldName, fieldValue := range metric.Fields() {
			lastDot := strings.LastIndex(fieldName, ".")
			newMetricGroupName := fieldName
			newFieldName := "value"

			if lastDot > 0 {
				newMetricGroupName = metric.Name() + "." + fieldName[:lastDot]
				newFieldName = fieldName[lastDot+1 : len(fieldName)]
			} else {
				newMetricGroupName = metric.Name() + "." + fieldName
			}

			if newFieldName == "time" {
				newFieldName = "timeValue"
			}

			add(metricGroups, newMetricGroupName, newFieldName, fieldValue)
		}

		for metricGroupName, fields := range metricGroups {
			acc.AddFields(metricGroupName, fields, metric.Tags())
		}

	}
	return nil
}

func add(m map[string]map[string]interface{}, metricGroupName, fieldName string, fieldValue interface{}) {
	mm, ok := m[metricGroupName]
	if !ok {
		mm = make(map[string]interface{})
		m[metricGroupName] = mm
	}
	mm[fieldName] = fieldValue
}

// Sends an HTTP request to the server using the HttpJson object's HTTPClient.
// This request can be either a GET or a POST.
// Parameters:
//     serverURL: endpoint to send request to
//
// Returns:
//     string: body of the response
//     error : Any error that may have occurred
func (h *HttpJson) sendRequest(serverURL string) (string, float64, error) {
	// Prepare URL
	requestURL, err := url.Parse(serverURL)
	if err != nil {
		return "", -1, fmt.Errorf("Invalid server URL \"%s\"", serverURL)
	}

	data := url.Values{}
	switch {
	case h.Method == "GET":
		params := requestURL.Query()
		for k, v := range h.Parameters {
			params.Add(k, v)
		}
		requestURL.RawQuery = params.Encode()

	case h.Method == "POST":
		requestURL.RawQuery = ""
		for k, v := range h.Parameters {
			data.Add(k, v)
		}
	}

	// Create + send request
	req, err := http.NewRequest(h.Method, requestURL.String(),
		strings.NewReader(data.Encode()))
	if err != nil {
		return "", -1, err
	}

	// Add header parameters
	for k, v := range h.Headers {
		if strings.ToLower(k) == "host" {
			req.Host = v
		} else {
			req.Header.Add(k, v)
		}
	}

	start := time.Now()
	resp, err := h.client.MakeRequest(req)
	if err != nil {
		return "", -1, err
	}

	defer resp.Body.Close()
	responseTime := time.Since(start).Seconds()

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

	// Process response
	if resp.StatusCode != http.StatusOK {
		err = fmt.Errorf("Response from url \"%s\" has status code %d (%s), expected %d (%s)",
			requestURL.String(),
			resp.StatusCode,
			http.StatusText(resp.StatusCode),
			http.StatusOK,
			http.StatusText(http.StatusOK))
		return string(body), responseTime, err
	}

	return string(body), responseTime, err
}

func init() {
	inputs.Add("httpjson", func() telegraf.Input {
		return &HttpJson{
			client: &RealHTTPClient{},
			ResponseTimeout: internal.Duration{
				Duration: 5 * time.Second,
			},
		}
	})
}
<|endoftext|>


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

package restplugin

import (
	aclplugin "github.com/ligato/vpp-agent/plugins/defaultplugins/aclplugin/vppcalls"
	ifplugin "github.com/ligato/vpp-agent/plugins/defaultplugins/ifplugin/vppdump"
	l2plugin "github.com/ligato/vpp-agent/plugins/defaultplugins/l2plugin/vppdump"
	//l3plugin "github.com/ligato/vpp-agent/plugins/defaultplugins/l3plugin/vppdump"
	"git.fd.io/govpp.git/core/bin_api/vpe"
	"github.com/unrolled/render"
	"net/http"
	"strconv"
	//"github.com/ligato/vpp-agent/plugins/defaultplugins/l3plugin"
	"encoding/json"
	"io/ioutil"
)

//interfaceGetHandler - used to get list of all interfaces
func (plugin *RESTAPIPlugin) interfacesGetHandler(formatter *render.Render) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {

		plugin.Deps.Log.Info("Getting list of all interfaces")

		// create an API channel
		ch, err := plugin.Deps.GoVppmux.NewAPIChannel()
		if err != nil {
			plugin.Deps.Log.Errorf("Error: %v", err)
			formatter.JSON(w, http.StatusInternalServerError, nil)
		} else {
			res, err := ifplugin.DumpInterfaces(plugin.Deps.Log, ch, nil)
			if err != nil {
				plugin.Deps.Log.Errorf("Error: %v", err)
				formatter.JSON(w, http.StatusInternalServerError, nil)
			} else {
				plugin.Deps.Log.Debug(res)
				formatter.JSON(w, http.StatusOK, res)
			}
		}
		defer ch.Close()
	}
}

//bridgeDomainGetHandler - used to get list of all bridge domains
func (plugin *RESTAPIPlugin) bridgeDomainIdsGetHandler(formatter *render.Render) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {

		plugin.Deps.Log.Info("Getting list of all bridge domain ids")

		// create an API channel
		ch, err := plugin.Deps.GoVppmux.NewAPIChannel()
		if err != nil {
			plugin.Deps.Log.Errorf("Error: %v", err)
			formatter.JSON(w, http.StatusInternalServerError, nil)
		} else {
			res, err := l2plugin.DumpBridgeDomainIDs(plugin.Deps.Log, ch, nil)
			if err != nil {
				plugin.Deps.Log.Errorf("Error: %v", err)
				formatter.JSON(w, http.StatusInternalServerError, nil)
			} else {
				plugin.Deps.Log.Debug(res)
				formatter.JSON(w, http.StatusOK, res)
			}
		}
		defer ch.Close()
	}
}

//bridgeDomainGetHandler - used to get list of all bridge domains
func (plugin *RESTAPIPlugin) bridgeDomainsGetHandler(formatter *render.Render) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {

		plugin.Deps.Log.Info("Getting list of all bridge domains")

		// create an API channel
		ch, err := plugin.Deps.GoVppmux.NewAPIChannel()
		if err != nil {
			plugin.Deps.Log.Errorf("Error: %v", err)
			formatter.JSON(w, http.StatusInternalServerError, nil)
		} else {
			res, err := l2plugin.DumpBridgeDomains(plugin.Deps.Log, ch, nil)
			if err != nil {
				plugin.Deps.Log.Errorf("Error: %v", err)
				formatter.JSON(w, http.StatusInternalServerError, nil)
			} else {
				plugin.Deps.Log.Debug(res)
				formatter.JSON(w, http.StatusOK, res)
			}
		}
		defer ch.Close()
	}
}

//fibTableEntriesGetHandler - used to get list of all fib entries
func (plugin *RESTAPIPlugin) fibTableEntriesGetHandler(formatter *render.Render) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {

		plugin.Deps.Log.Info("Getting list of all fibs")

		// create an API channel
		ch, err := plugin.Deps.GoVppmux.NewAPIChannel()
		if err != nil {
			plugin.Deps.Log.Errorf("Error: %v", err)
			formatter.JSON(w, http.StatusInternalServerError, nil)
		} else {
			res, err := l2plugin.DumpFIBTableEntries(plugin.Deps.Log, ch, nil)
			if err != nil {
				plugin.Deps.Log.Errorf("Error: %v", err)
				formatter.JSON(w, http.StatusInternalServerError, nil)
			} else {
				plugin.Deps.Log.Debug(res)
				formatter.JSON(w, http.StatusOK, res)
			}
		}
		defer ch.Close()
	}
}

//xconnectPairsGetHandler - used to get list of all connect pairs (transmit and receive interfaces)
func (plugin *RESTAPIPlugin) xconnectPairsGetHandler(formatter *render.Render) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {

		plugin.Deps.Log.Info("Getting list of all xconnect pairs")

		// create an API channel
		ch, err := plugin.Deps.GoVppmux.NewAPIChannel()
		if err != nil {
			plugin.Deps.Log.Errorf("Error: %v", err)
			formatter.JSON(w, http.StatusInternalServerError, nil)
		} else {
			res, err := l2plugin.DumpXConnectPairs(plugin.Deps.Log, ch, nil)
			if err != nil {
				plugin.Deps.Log.Errorf("Error: %v", err)
				formatter.JSON(w, http.StatusInternalServerError, nil)
			} else {
				plugin.Deps.Log.Debug(res)
				formatter.JSON(w, http.StatusOK, res)
			}
		}
		defer ch.Close()
	}
}

//staticRoutesGetHandler - used to get list of all static routes
/*
func (plugin *RESTAPIPlugin) staticRoutesGetHandler(formatter *render.Render) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {

		plugin.Deps.Log.Info("Getting list of all static routes")

		// create an API channel
		ch, err := plugin.Deps.GoVppmux.NewAPIChannel()
		if err != nil {
			plugin.Deps.Log.Errorf("Error: %v", err)
			formatter.JSON(w, http.StatusInternalServerError, nil)
		} else {
			res, err := l3plugin.DumpStaticRoutes(plugin.Deps.Log, ch, nil)
			if err != nil {
				plugin.Deps.Log.Errorf("Error: %v", err)
				formatter.JSON(w, http.StatusInternalServerError, nil)
			} else {
				plugin.Deps.Log.Debug(res)
				formatter.JSON(w, http.StatusOK, res)
			}
		}
		defer ch.Close()
	}
}
*/

//interfaceAclPostHandler - used to get acl configuration for a particular interface
func (plugin *RESTAPIPlugin) interfaceAclPostHandler(formatter *render.Render) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {

		plugin.Deps.Log.Info("Getting acl configuration of interface")

		var reqParam map[string]string
		body, err := ioutil.ReadAll(req.Body)
		if err != nil {
			plugin.Deps.Log.Error("Failed to parse request body.")
			formatter.JSON(w, http.StatusInternalServerError, err)
			return
		}

		plugin.Deps.Log.Infof("request body = %v", body)
		err = json.Unmarshal(body, &reqParam)
		if err != nil {
			plugin.Deps.Log.Error("Failed to unmarshal request body.")
			formatter.JSON(w, http.StatusInternalServerError, err)
			return
		}

		swIndexStr, ok := reqParam["swIndex"]

		if !ok {
			plugin.Deps.Log.Error("swIndex paramenter not included.")
			formatter.JSON(w, http.StatusInternalServerError, err)
			return
		}

		plugin.Deps.Log.Infof("Received request for swIndex :: %v ", swIndexStr)

		if swIndexStr != "" {
			swIndexuInt64, err := strconv.ParseUint(swIndexStr, 10, 32)
			swIndex := uint32(swIndexuInt64)
			if err != nil {
				// create an API channel
				ch, err := plugin.Deps.GoVppmux.NewAPIChannel()
				if err != nil {
					plugin.Deps.Log.Errorf("Error: %v", err)
					formatter.JSON(w, http.StatusInternalServerError, err)
				} else {
					res, err := aclplugin.DumpInterface(swIndex, ch, nil)
					if err != nil {
						plugin.Deps.Log.Errorf("Error: %v", err)
						formatter.JSON(w, http.StatusInternalServerError, err)
					} else {
						plugin.Deps.Log.Debug(res)
						formatter.JSON(w, http.StatusOK, res)
					}
				}
				defer ch.Close()
			}
		} else {
			formatter.JSON(w, http.StatusBadRequest, "swIndex parameter not found")
		}
	}
}

//showCommandHandler - used to execute VPP CLI commands
func (plugin *RESTAPIPlugin) showCommandHandler(formatter *render.Render) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {

		var reqParam map[string]string
		body, err := ioutil.ReadAll(req.Body)
		if err != nil {
			plugin.Deps.Log.Error("Failed to parse request body.")
			formatter.JSON(w, http.StatusInternalServerError, err)
			return
		}

		plugin.Deps.Log.Infof("request body = %v", body)
		err = json.Unmarshal(body, &reqParam)
		if err != nil {
			plugin.Deps.Log.Error("Failed to unmarshal request body.")
			formatter.JSON(w, http.StatusInternalServerError, err)
			return
		}

		command, ok := reqParam["vppclicommand"]

		if !ok {
			plugin.Deps.Log.Error("command paramenter not included.")
			formatter.JSON(w, http.StatusInternalServerError, err)
			return
		}

		plugin.Deps.Log.Infof("Received request to execute command :: %v ", command)
		plugin.Deps.Log.WithField("VPPCLI command", command).Infof("Received command :: %v", command)

		if command != "" {
			ch, err := plugin.Deps.GoVppmux.NewAPIChannel()
			if err != nil {
				plugin.Deps.Log.Errorf("Error: %v", err)
				formatter.JSON(w, http.StatusInternalServerError, err)
			} else {
				req := &vpe.CliInband{}
				req.Length = uint32(len(command))
				req.Cmd = []byte(command)

				reply := &vpe.CliInbandReply{}
				err = ch.SendRequest(req).ReceiveReply(reply)
				if err != nil {
					plugin.Deps.Log.Errorf("Error: %v", err)
					formatter.JSON(w, http.StatusInternalServerError, err)
				}

				if 0 != reply.Retval {
					plugin.Deps.Log.Errorf("Command returned code :: %v", reply.Retval)
				}
				plugin.Deps.Log.WithField("VPPCLI response", string(reply.Reply)).Infof("Command returned reply :: %v", string(reply.Reply))
				formatter.JSON(w, http.StatusOK, reply)
			}
			defer ch.Close()
		} else {
			formatter.JSON(w, http.StatusBadRequest, "showCommand parameter is empty")
		}
	}
}
<commit_msg>SPOPT-1690 - REST API for VPP<commit_after>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package restplugin

import (
	aclplugin "github.com/ligato/vpp-agent/plugins/defaultplugins/aclplugin/vppcalls"
	ifplugin "github.com/ligato/vpp-agent/plugins/defaultplugins/ifplugin/vppdump"
	l2plugin "github.com/ligato/vpp-agent/plugins/defaultplugins/l2plugin/vppdump"
	//l3plugin "github.com/ligato/vpp-agent/plugins/defaultplugins/l3plugin/vppdump"
	"git.fd.io/govpp.git/core/bin_api/vpe"
	"github.com/unrolled/render"
	"net/http"
	"strconv"
	//"github.com/ligato/vpp-agent/plugins/defaultplugins/l3plugin"
	"encoding/json"
	"io/ioutil"
)

//interfaceGetHandler - used to get list of all interfaces
func (plugin *RESTAPIPlugin) interfacesGetHandler(formatter *render.Render) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {

		plugin.Deps.Log.Info("Getting list of all interfaces")

		// create an API channel
		ch, err := plugin.Deps.GoVppmux.NewAPIChannel()
		if err != nil {
			plugin.Deps.Log.Errorf("Error: %v", err)
			formatter.JSON(w, http.StatusInternalServerError, nil)
		} else {
			res, err := ifplugin.DumpInterfaces(plugin.Deps.Log, ch, nil)
			if err != nil {
				plugin.Deps.Log.Errorf("Error: %v", err)
				formatter.JSON(w, http.StatusInternalServerError, nil)
			} else {
				plugin.Deps.Log.Debug(res)
				formatter.JSON(w, http.StatusOK, res)
			}
		}
		defer ch.Close()
	}
}

//bridgeDomainGetHandler - used to get list of all bridge domains
func (plugin *RESTAPIPlugin) bridgeDomainIdsGetHandler(formatter *render.Render) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {

		plugin.Deps.Log.Info("Getting list of all bridge domain ids")

		// create an API channel
		ch, err := plugin.Deps.GoVppmux.NewAPIChannel()
		if err != nil {
			plugin.Deps.Log.Errorf("Error: %v", err)
			formatter.JSON(w, http.StatusInternalServerError, nil)
		} else {
			res, err := l2plugin.DumpBridgeDomainIDs(plugin.Deps.Log, ch, nil)
			if err != nil {
				plugin.Deps.Log.Errorf("Error: %v", err)
				formatter.JSON(w, http.StatusInternalServerError, nil)
			} else {
				plugin.Deps.Log.Debug(res)
				formatter.JSON(w, http.StatusOK, res)
			}
		}
		defer ch.Close()
	}
}

//bridgeDomainGetHandler - used to get list of all bridge domains
func (plugin *RESTAPIPlugin) bridgeDomainsGetHandler(formatter *render.Render) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {

		plugin.Deps.Log.Info("Getting list of all bridge domains")

		// create an API channel
		ch, err := plugin.Deps.GoVppmux.NewAPIChannel()
		if err != nil {
			plugin.Deps.Log.Errorf("Error: %v", err)
			formatter.JSON(w, http.StatusInternalServerError, nil)
		} else {
			res, err := l2plugin.DumpBridgeDomains(plugin.Deps.Log, ch, nil)
			if err != nil {
				plugin.Deps.Log.Errorf("Error: %v", err)
				formatter.JSON(w, http.StatusInternalServerError, nil)
			} else {
				plugin.Deps.Log.Debug(res)
				formatter.JSON(w, http.StatusOK, res)
			}
		}
		defer ch.Close()
	}
}

//fibTableEntriesGetHandler - used to get list of all fib entries
func (plugin *RESTAPIPlugin) fibTableEntriesGetHandler(formatter *render.Render) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {

		plugin.Deps.Log.Info("Getting list of all fibs")

		// create an API channel
		ch, err := plugin.Deps.GoVppmux.NewAPIChannel()
		if err != nil {
			plugin.Deps.Log.Errorf("Error: %v", err)
			formatter.JSON(w, http.StatusInternalServerError, nil)
		} else {
			res, err := l2plugin.DumpFIBTableEntries(plugin.Deps.Log, ch, nil)
			if err != nil {
				plugin.Deps.Log.Errorf("Error: %v", err)
				formatter.JSON(w, http.StatusInternalServerError, nil)
			} else {
				plugin.Deps.Log.Debug(res)
				formatter.JSON(w, http.StatusOK, res)
			}
		}
		defer ch.Close()
	}
}

//xconnectPairsGetHandler - used to get list of all connect pairs (transmit and receive interfaces)
func (plugin *RESTAPIPlugin) xconnectPairsGetHandler(formatter *render.Render) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {

		plugin.Deps.Log.Info("Getting list of all xconnect pairs")

		// create an API channel
		ch, err := plugin.Deps.GoVppmux.NewAPIChannel()
		if err != nil {
			plugin.Deps.Log.Errorf("Error: %v", err)
			formatter.JSON(w, http.StatusInternalServerError, nil)
		} else {
			res, err := l2plugin.DumpXConnectPairs(plugin.Deps.Log, ch, nil)
			if err != nil {
				plugin.Deps.Log.Errorf("Error: %v", err)
				formatter.JSON(w, http.StatusInternalServerError, nil)
			} else {
				plugin.Deps.Log.Debug(res)
				formatter.JSON(w, http.StatusOK, res)
			}
		}
		defer ch.Close()
	}
}

//staticRoutesGetHandler - used to get list of all static routes
/*
func (plugin *RESTAPIPlugin) staticRoutesGetHandler(formatter *render.Render) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {

		plugin.Deps.Log.Info("Getting list of all static routes")

		// create an API channel
		ch, err := plugin.Deps.GoVppmux.NewAPIChannel()
		if err != nil {
			plugin.Deps.Log.Errorf("Error: %v", err)
			formatter.JSON(w, http.StatusInternalServerError, nil)
		} else {
			res, err := l3plugin.DumpStaticRoutes(plugin.Deps.Log, ch, nil)
			if err != nil {
				plugin.Deps.Log.Errorf("Error: %v", err)
				formatter.JSON(w, http.StatusInternalServerError, nil)
			} else {
				plugin.Deps.Log.Debug(res)
				formatter.JSON(w, http.StatusOK, res)
			}
		}
		defer ch.Close()
	}
}
*/

//interfaceAclPostHandler - used to get acl configuration for a particular interface
func (plugin *RESTAPIPlugin) interfaceAclPostHandler(formatter *render.Render) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {

		plugin.Deps.Log.Info("Getting acl configuration of interface")

		var reqParam map[string]string
		body, err := ioutil.ReadAll(req.Body)
		if err != nil {
			plugin.Deps.Log.Error("Failed to parse request body.")
			formatter.JSON(w, http.StatusInternalServerError, err)
			return
		}

		plugin.Deps.Log.Infof("request body = %v", body)
		err = json.Unmarshal(body, &reqParam)
		if err != nil {
			plugin.Deps.Log.Error("Failed to unmarshal request body.")
			formatter.JSON(w, http.StatusInternalServerError, err)
			return
		}

		swIndexStr, ok := reqParam["swIndex"]

		if !ok {
			plugin.Deps.Log.Error("swIndex paramenter not included.")
			formatter.JSON(w, http.StatusInternalServerError, err)
			return
		}

		plugin.Deps.Log.Infof("Received request for swIndex :: %v ", swIndexStr)

		if swIndexStr != "" {
			swIndexuInt64, err := strconv.ParseUint(swIndexStr, 10, 32)
			swIndex := uint32(swIndexuInt64)
			if err != nil {
				// create an API channel
				ch, err := plugin.Deps.GoVppmux.NewAPIChannel()
				if err != nil {
					plugin.Deps.Log.Errorf("Error: %v", err)
					formatter.JSON(w, http.StatusInternalServerError, err)
				} else {
					res, err := aclplugin.DumpInterface(swIndex, ch, nil)
					if err != nil {
						plugin.Deps.Log.Errorf("Error: %v", err)
						formatter.JSON(w, http.StatusInternalServerError, err)
					} else {
						plugin.Deps.Log.Debug(res)
						formatter.JSON(w, http.StatusOK, res)
					}
				}
				defer ch.Close()
			}
		} else {
			formatter.JSON(w, http.StatusBadRequest, "swIndex parameter not found")
		}
	}
}

//showCommandHandler - used to execute VPP CLI commands
func (plugin *RESTAPIPlugin) showCommandHandler(formatter *render.Render) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {

		var reqParam map[string]string
		body, err := ioutil.ReadAll(req.Body)
		if err != nil {
			plugin.Deps.Log.Error("Failed to parse request body.")
			formatter.JSON(w, http.StatusInternalServerError, err)
			return
		}

		err = json.Unmarshal(body, &reqParam)
		if err != nil {
			plugin.Deps.Log.Error("Failed to unmarshal request body.")
			formatter.JSON(w, http.StatusInternalServerError, err)
			return
		}

		command, ok := reqParam["vppclicommand"]

		if !ok {
			plugin.Deps.Log.Error("command parameter not included.")
			formatter.JSON(w, http.StatusInternalServerError, err)
			return
		}

		if command != "" {

			plugin.Deps.Log.WithField("VPPCLI command", command).Infof("Received command: %v", command)

			ch, err := plugin.Deps.GoVppmux.NewAPIChannel()
			if err != nil {
				plugin.Deps.Log.Errorf("Error creating channel: %v", err)
				formatter.JSON(w, http.StatusInternalServerError, err)
			} else {
				req := &vpe.CliInband{}
				req.Length = uint32(len(command))
				req.Cmd = []byte(command)

				reply := &vpe.CliInbandReply{}
				err = ch.SendRequest(req).ReceiveReply(reply)
				if err != nil {
					plugin.Deps.Log.Errorf("Error processing request: %v", err)
					formatter.JSON(w, http.StatusInternalServerError, err)
				}

				if reply.Retval > 0 {
					plugin.Deps.Log.Errorf("Command returned code: %v", reply.Retval)
				}

				plugin.Deps.Log.WithField("VPPCLI response", string(reply.Reply)).Infof("Command returned reply :: %v", string(reply.Reply))

				formatter.JSON(w, http.StatusOK, string(reply.Reply))
			}
			defer ch.Close()
		} else {
			formatter.JSON(w, http.StatusBadRequest, "showCommand parameter is empty")
		}
	}
}
<|endoftext|>


<commit_before>package handlers

import (
	"container/heap"
	"encoding/json"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"time"

	"github.com/boltdb/bolt"
	"github.com/dustin/go-humanize"
)

func add(x, y int) int {
	return x + y
}

func formatScore(x float64) string {
	return fmt.Sprintf("%.2f", x)
}

// HighScoresHandler handles the stats page
func HighScoresHandler(w http.ResponseWriter, r *http.Request) {
	// write to boltdb
	db, err := bolt.Open(DBPath, 0755, &bolt.Options{Timeout: 1 * time.Second})
	if err != nil {
		log.Println("Failed to open bolt database: ", err)
		return
	}
	defer db.Close()

	count, scores := 0, &scoreHeap{}
	err = db.View(func(tx *bolt.Tx) error {
		hsb := tx.Bucket([]byte(MetaBucket))
		if hsb == nil {
			return fmt.Errorf("high score bucket not found")
		}
		scoreBytes := hsb.Get([]byte("scores"))
		if scoreBytes == nil {
			scoreBytes, _ = json.Marshal([]scoreHeap{})
		}
		json.Unmarshal(scoreBytes, scores)

		heap.Init(scores)

		total := hsb.Get([]byte("total_repos"))
		if total == nil {
			count = 0
		} else {
			json.Unmarshal(total, &count)
		}
		return nil
	})

	if err != nil {
		log.Println("ERROR: Failed to load high scores from bolt database: ", err)
		http.Error(w, err.Error(), 500)
		return
	}

	funcs := template.FuncMap{"add": add, "formatScore": formatScore}
	t := template.Must(template.New("high_scores.html").Funcs(funcs).ParseFiles("templates/high_scores.html"))

	sortedScores := make([]scoreItem, len(*scores))
	for i := range sortedScores {
		sortedScores[len(sortedScores)-i-1] = heap.Pop(scores).(scoreItem)
	}

	t.Execute(w, map[string]interface{}{"HighScores": sortedScores, "Count": humanize.Comma(int64(count))})
}
<commit_msg>check errors<commit_after>package handlers

import (
	"container/heap"
	"encoding/json"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"time"

	"github.com/boltdb/bolt"
	"github.com/dustin/go-humanize"
)

func add(x, y int) int {
	return x + y
}

func formatScore(x float64) string {
	return fmt.Sprintf("%.2f", x)
}

// HighScoresHandler handles the stats page
func HighScoresHandler(w http.ResponseWriter, r *http.Request) {
	// write to boltdb
	db, err := bolt.Open(DBPath, 0755, &bolt.Options{Timeout: 1 * time.Second})
	if err != nil {
		log.Println("Failed to open bolt database: ", err)
		return
	}
	defer db.Close()

	count, scores := 0, &scoreHeap{}
	err = db.View(func(tx *bolt.Tx) error {
		hsb := tx.Bucket([]byte(MetaBucket))
		if hsb == nil {
			return fmt.Errorf("high score bucket not found")
		}
		scoreBytes := hsb.Get([]byte("scores"))
		if scoreBytes == nil {
			scoreBytes, err = json.Marshal([]scoreHeap{})
			if err != nil {
				return err
			}
		}
		json.Unmarshal(scoreBytes, scores)

		heap.Init(scores)

		total := hsb.Get([]byte("total_repos"))
		if total == nil {
			count = 0
			return nil
		}
		return json.Unmarshal(total, &count)
	})

	if err != nil {
		log.Println("ERROR: Failed to load high scores from bolt database: ", err)
		http.Error(w, err.Error(), 500)
		return
	}

	funcs := template.FuncMap{"add": add, "formatScore": formatScore}
	t := template.Must(template.New("high_scores.html").Funcs(funcs).ParseFiles("templates/high_scores.html"))

	sortedScores := make([]scoreItem, len(*scores))
	for i := range sortedScores {
		sortedScores[len(sortedScores)-i-1] = heap.Pop(scores).(scoreItem)
	}

	t.Execute(w, map[string]interface{}{"HighScores": sortedScores, "Count": humanize.Comma(int64(count))})
}
<|endoftext|>


<commit_before>package actions

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/deis/helm/log"
	"github.com/gobuffalo/buffalo"
	"github.com/gobuffalo/buffalo-pop/pop/popmw"
	popmw "github.com/gobuffalo/buffalo-pop/pop/popmw"
	"github.com/gobuffalo/envy"
	ssl "github.com/gobuffalo/mw-forcessl"
	mp "github.com/gobuffalo/mw-paramlogger"
	"github.com/gobuffalo/pop"
	"github.com/gobuffalo/x/sessions"
	"github.com/rs/cors"
	"github.com/unrolled/secure"
	jose "gopkg.in/square/go-jose.v2"
	jwt "gopkg.in/square/go-jose.v2/jwt"

	"github.com/kindlyops/havengrc/havenapi/models"
)

// ENV is used to help switch settings based on where the
// application is being run. Default is "development".
var ENV = envy.Get("GO_ENV", "development")

// KEY gets the haven jwk path
var KEY = envy.Get("HAVEN_JWK_PATH", "")

var key jose.JSONWebKey
var app *buffalo.App

// App is where all routes and middleware for buffalo
// should be defined. This is the nerve center of your
// application.
func App() *buffalo.App {
	if app == nil {
		app = buffalo.New(buffalo.Options{
			Env:          ENV,
			SessionStore: sessions.Null{},
			PreWares: []buffalo.PreWare{
				cors.Default().Handler,
			},
			SessionName: "_havenapi_session",
		})

		rawKey, err := ioutil.ReadFile(KEY)
		if err != nil {
			panic("could not read the JWK")
		}

		err = key.UnmarshalJSON(rawKey)
		if err != nil {
			panic("could not unmarshal the JWK")
		}

		if ENV == "development" {
			app.Use(mp.ParameterLogger)
		}

		app.GET("/healthz", HealthzHandler)

		api := app.Group("/api/")
		// Automatically redirect to SSL
		api.Use(ssl.Middleware(secure.Options{
			SSLRedirect:     ENV == "production",
			SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "https"},
		}))

		// TODO refactor to use dependency injection instead of a package global
		api.Use(popmw.Transaction(models.DB))
		api.Use(JwtMiddleware)
		api.Middleware.Skip(JwtMiddleware, RegistrationHandler)
		api.POST("files", UploadHandler)
		api.POST("registration_funnel", RegistrationHandler)

	}

	return app
}

// Token is for un marshalling {"resource_access":{"havendev":{"roles":["member"]}}
type Token struct {
	ResourceAccess struct {
		Client struct {
			Roles []string `json:"roles,omitempty"`
		} `json:"havendev,omitempty"`
	} `json:"resource_access,omitempty"`
}

func getRole(allClaims map[string]interface{}) string {
	// look for the role that we should assume
	// {"resource_access":{"havendev":{"roles":["member"]}}
	access := allClaims["resource_access"].(map[string]interface{})
	havendev := access["havendev"].(map[string]interface{})
	roles := havendev["roles"].([]interface{})
	var role string
	for _, r := range roles {
		switch r.(string) {
		case "member":
			role = "member"
		case "admin":
			role = "admin"
		default:
			log.Info("Got unexpected role %s", r)
			role = "anonymous"
		}
	}
	return role
}

// JwtMiddleware validates JWT and set context compatible with PostgREST
func JwtMiddleware(next buffalo.Handler) buffalo.Handler {

	return func(c buffalo.Context) error {
		header := c.Request().Header.Get("Authorization")
		parts := strings.Split(header, "Bearer ")

		if len(parts) < 2 {
			return c.Error(http.StatusUnauthorized, fmt.Errorf("Must provide Authorization token"))
		}

		token := parts[1]
		if len(token) == 0 {
			return c.Error(http.StatusUnauthorized, fmt.Errorf("Must provide Authorization token"))
		}

		tok, err := jwt.ParseSigned(token)
		if err != nil {
			return c.Error(http.StatusUnauthorized, err)
		}

		// build up a set of claims to set locally in DB transaction
		// at a minimum set 'request.jwt.claim.email' and 'request.jwt.claim.sub'
		validClaims := jwt.Claims{}
		allClaims := make(map[string]interface{})
		if err := tok.Claims(key, &validClaims, &allClaims); err != nil {
			return c.Error(http.StatusUnauthorized, err)
		}

		// check if token is expired and from a valid issuer
		// TODO: the issuer needs to be configurable to handle on-prem deployments
		iss := "http://localhost:2015/auth/realms/havendev"
		err = validClaims.Validate(jwt.Expected{Issuer: iss})
		if err != nil {
			return c.Error(401, fmt.Errorf("invalid token: %s", err.Error()))
		}

		sub := allClaims["sub"]
		c.Set("sub", sub)

		email := allClaims["email"]
		c.Set("email", email)

		org := allClaims["org"]
		c.Set("org", org)
		enc, _ := json.Marshal(org)

		role := getRole(allClaims)

		tx := c.Value("tx").(*pop.Connection)
		err = tx.RawQuery(models.Q["setemailclaim"], email).Exec()
		if err != nil {
			return c.Error(500, fmt.Errorf("error setting JWT claims email in GUC: %s", err.Error()))
		}

		err = tx.RawQuery("set local search_path to mappa, public").Exec()
		if err != nil {
			return c.Error(500, fmt.Errorf("Database error setting search path: %s", err.Error()))
		}

		err = tx.RawQuery(models.Q["setsubclaim"], sub).Exec()
		if err != nil {
			return c.Error(500, fmt.Errorf("error setting JWT claims sub in GUC: %s", err.Error()))
		}

		err = tx.RawQuery(models.Q["setorgclaim"], string(enc)).Exec()
		if err != nil {
			return c.Error(500, fmt.Errorf("error setting JWT claims org in GUC: %s", err.Error()))
		}

		err = tx.RawQuery(models.Q["setrole"], role).Exec()
		if err != nil {
			return c.Error(500, fmt.Errorf("error setting PostgreSQL role: %s", err.Error()))
		}

		return next(c)
	}
}
<commit_msg>Fix havenapi build error with duplicate import.<commit_after>package actions

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/deis/helm/log"
	"github.com/gobuffalo/buffalo"
	popmw "github.com/gobuffalo/buffalo-pop/pop/popmw"
	"github.com/gobuffalo/envy"
	ssl "github.com/gobuffalo/mw-forcessl"
	mp "github.com/gobuffalo/mw-paramlogger"
	"github.com/gobuffalo/pop"
	"github.com/gobuffalo/x/sessions"
	"github.com/rs/cors"
	"github.com/unrolled/secure"
	jose "gopkg.in/square/go-jose.v2"
	jwt "gopkg.in/square/go-jose.v2/jwt"

	"github.com/kindlyops/havengrc/havenapi/models"
)

// ENV is used to help switch settings based on where the
// application is being run. Default is "development".
var ENV = envy.Get("GO_ENV", "development")

// KEY gets the haven jwk path
var KEY = envy.Get("HAVEN_JWK_PATH", "")

var key jose.JSONWebKey
var app *buffalo.App

// App is where all routes and middleware for buffalo
// should be defined. This is the nerve center of your
// application.
func App() *buffalo.App {
	if app == nil {
		app = buffalo.New(buffalo.Options{
			Env:          ENV,
			SessionStore: sessions.Null{},
			PreWares: []buffalo.PreWare{
				cors.Default().Handler,
			},
			SessionName: "_havenapi_session",
		})

		rawKey, err := ioutil.ReadFile(KEY)
		if err != nil {
			panic("could not read the JWK")
		}

		err = key.UnmarshalJSON(rawKey)
		if err != nil {
			panic("could not unmarshal the JWK")
		}

		if ENV == "development" {
			app.Use(mp.ParameterLogger)
		}

		app.GET("/healthz", HealthzHandler)

		api := app.Group("/api/")
		// Automatically redirect to SSL
		api.Use(ssl.Middleware(secure.Options{
			SSLRedirect:     ENV == "production",
			SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "https"},
		}))

		// TODO refactor to use dependency injection instead of a package global
		api.Use(popmw.Transaction(models.DB))
		api.Use(JwtMiddleware)
		api.Middleware.Skip(JwtMiddleware, RegistrationHandler)
		api.POST("files", UploadHandler)
		api.POST("registration_funnel", RegistrationHandler)

	}

	return app
}

// Token is for un marshalling {"resource_access":{"havendev":{"roles":["member"]}}
type Token struct {
	ResourceAccess struct {
		Client struct {
			Roles []string `json:"roles,omitempty"`
		} `json:"havendev,omitempty"`
	} `json:"resource_access,omitempty"`
}

func getRole(allClaims map[string]interface{}) string {
	// look for the role that we should assume
	// {"resource_access":{"havendev":{"roles":["member"]}}
	access := allClaims["resource_access"].(map[string]interface{})
	havendev := access["havendev"].(map[string]interface{})
	roles := havendev["roles"].([]interface{})
	var role string
	for _, r := range roles {
		switch r.(string) {
		case "member":
			role = "member"
		case "admin":
			role = "admin"
		default:
			log.Info("Got unexpected role %s", r)
			role = "anonymous"
		}
	}
	return role
}

// JwtMiddleware validates JWT and set context compatible with PostgREST
func JwtMiddleware(next buffalo.Handler) buffalo.Handler {

	return func(c buffalo.Context) error {
		header := c.Request().Header.Get("Authorization")
		parts := strings.Split(header, "Bearer ")

		if len(parts) < 2 {
			return c.Error(http.StatusUnauthorized, fmt.Errorf("Must provide Authorization token"))
		}

		token := parts[1]
		if len(token) == 0 {
			return c.Error(http.StatusUnauthorized, fmt.Errorf("Must provide Authorization token"))
		}

		tok, err := jwt.ParseSigned(token)
		if err != nil {
			return c.Error(http.StatusUnauthorized, err)
		}

		// build up a set of claims to set locally in DB transaction
		// at a minimum set 'request.jwt.claim.email' and 'request.jwt.claim.sub'
		validClaims := jwt.Claims{}
		allClaims := make(map[string]interface{})
		if err := tok.Claims(key, &validClaims, &allClaims); err != nil {
			return c.Error(http.StatusUnauthorized, err)
		}

		// check if token is expired and from a valid issuer
		// TODO: the issuer needs to be configurable to handle on-prem deployments
		iss := "http://localhost:2015/auth/realms/havendev"
		err = validClaims.Validate(jwt.Expected{Issuer: iss})
		if err != nil {
			return c.Error(401, fmt.Errorf("invalid token: %s", err.Error()))
		}

		sub := allClaims["sub"]
		c.Set("sub", sub)

		email := allClaims["email"]
		c.Set("email", email)

		org := allClaims["org"]
		c.Set("org", org)
		enc, _ := json.Marshal(org)

		role := getRole(allClaims)

		tx := c.Value("tx").(*pop.Connection)
		err = tx.RawQuery(models.Q["setemailclaim"], email).Exec()
		if err != nil {
			return c.Error(500, fmt.Errorf("error setting JWT claims email in GUC: %s", err.Error()))
		}

		err = tx.RawQuery("set local search_path to mappa, public").Exec()
		if err != nil {
			return c.Error(500, fmt.Errorf("Database error setting search path: %s", err.Error()))
		}

		err = tx.RawQuery(models.Q["setsubclaim"], sub).Exec()
		if err != nil {
			return c.Error(500, fmt.Errorf("error setting JWT claims sub in GUC: %s", err.Error()))
		}

		err = tx.RawQuery(models.Q["setorgclaim"], string(enc)).Exec()
		if err != nil {
			return c.Error(500, fmt.Errorf("error setting JWT claims org in GUC: %s", err.Error()))
		}

		err = tx.RawQuery(models.Q["setrole"], role).Exec()
		if err != nil {
			return c.Error(500, fmt.Errorf("error setting PostgreSQL role: %s", err.Error()))
		}

		return next(c)
	}
}
<|endoftext|>


<commit_before>package consul

import (
	"math/rand"
	"os"
	"testing"
	"time"

	"github.com/hashicorp/consul/consul/structs"
	"github.com/hashicorp/consul/testutil"
	"github.com/hashicorp/serf/coordinate"
)

// getRandomCoordinate generates a random coordinate.
func getRandomCoordinate() *coordinate.Coordinate {
	config := coordinate.DefaultConfig()
	// Randomly apply updates between n clients
	n := 5
	clients := make([]*coordinate.Client, n)
	for i := 0; i < n; i++ {
		clients[i] = coordinate.NewClient(config)
	}

	for i := 0; i < n*100; i++ {
		k1 := rand.Intn(n)
		k2 := rand.Intn(n)
		if k1 == k2 {
			continue
		}
		clients[k1].Update(clients[k2].GetCoordinate(), time.Duration(rand.Int63())*time.Microsecond)
	}
	return clients[rand.Intn(n)].GetCoordinate()
}

func coordinatesEqual(a, b *coordinate.Coordinate) bool {
	config := coordinate.DefaultConfig()
	dist, err := a.DistanceTo(b, config)
	if err != nil {
		panic(err)
	}
	return dist < 0.00001
}

func TestCoordinate_Update(t *testing.T) {
	dir1, s1 := testServer(t)
	defer os.RemoveAll(dir1)
	defer s1.Shutdown()
	client := rpcClient(t, s1)
	defer client.Close()

	testutil.WaitForLeader(t, client.Call, "dc1")

	arg := structs.CoordinateUpdateRequest{
		NodeSpecificRequest: structs.NodeSpecificRequest{
			Datacenter: "dc1",
			Node:       "node1",
		},
		Op:    structs.CoordinateSet,
		Coord: getRandomCoordinate(),
	}

	var out struct{}
	if err := client.Call("Coordinate.Update", &arg, &out); err != nil {
		t.Fatalf("err: %v", err)
	}

	// Verify
	state := s1.fsm.State()
	_, d, err := state.CoordinateGet("node1")
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if coordinatesEqual(d.Coord, arg.Coord) {
		t.Fatalf("should be equal\n%v\n%v", d.Coord, arg.Coord)
	}
}
<commit_msg>Fix tests<commit_after>package consul

import (
	"fmt"
	"math/rand"
	"os"
	"testing"
	"time"

	"github.com/hashicorp/consul/consul/structs"
	"github.com/hashicorp/consul/testutil"
	"github.com/hashicorp/serf/coordinate"
)

// getRandomCoordinate generates a random coordinate.
func getRandomCoordinate() *coordinate.Coordinate {
	config := coordinate.DefaultConfig()
	// Randomly apply updates between n clients
	n := 5
	clients := make([]*coordinate.Client, n)
	for i := 0; i < n; i++ {
		clients[i] = coordinate.NewClient(config)
	}

	for i := 0; i < n*100; i++ {
		k1 := rand.Intn(n)
		k2 := rand.Intn(n)
		if k1 == k2 {
			continue
		}
		clients[k1].Update(clients[k2].GetCoordinate(), time.Duration(rand.Int63())*time.Microsecond)
	}
	return clients[rand.Intn(n)].GetCoordinate()
}

func coordinatesEqual(a, b *coordinate.Coordinate) bool {
	config := coordinate.DefaultConfig()
	dist, err := a.DistanceTo(b, config)
	if err != nil {
		panic(err)
	}
	fmt.Printf("dist: %v", dist)
	return dist < 0.1
}

func TestCoordinate_Update(t *testing.T) {
	dir1, s1 := testServer(t)
	defer os.RemoveAll(dir1)
	defer s1.Shutdown()
	client := rpcClient(t, s1)
	defer client.Close()

	testutil.WaitForLeader(t, client.Call, "dc1")

	arg := structs.CoordinateUpdateRequest{
		NodeSpecificRequest: structs.NodeSpecificRequest{
			Datacenter: "dc1",
			Node:       "node1",
		},
		Op:    structs.CoordinateSet,
		Coord: getRandomCoordinate(),
	}

	var out struct{}
	if err := client.Call("Coordinate.Update", &arg, &out); err != nil {
		t.Fatalf("err: %v", err)
	}

	// Verify
	state := s1.fsm.State()
	_, d, err := state.CoordinateGet("node1")
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if !coordinatesEqual(d.Coord, arg.Coord) {
		t.Fatalf("should be equal\n%v\n%v", d.Coord, arg.Coord)
	}

	// Get via RPC
	var out2 *structs.Coordinate
	arg2 := structs.NodeSpecificRequest{
		Datacenter: "dc1",
		Node:       "node1",
	}
	if err := client.Call("Coordinate.Get", &arg2, &out2); err != nil {
		t.Fatalf("err: %v", err)
	}
	if !coordinatesEqual(out2.Coord, arg.Coord) {
		t.Fatalf("should be equal\n%v\n%v", out2.Coord, arg.Coord)
	}
}
<|endoftext|>


<commit_before>package datatype

var lengthTypes = []*DataType{
	&DataType{
		Group:       GroupLength,
		Names:       []string{"angstrom"},
		DisplayName: "angstrom",
		Factor:      10000000000,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"centimeter", "cm"},
		DisplayName: "cm",
		Factor:      100,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"chain"},
		DisplayName: "chain",
		Factor:      0.049709695378987,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"decimeter", "dm"},
		DisplayName: "dm",
		Factor:      10,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"foot", "ft"},
		DisplayName: "ft",
		Factor:      0.54680664916885,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"fathom"},
		DisplayName: "fathom",
		Factor:      3.2808398950131,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"furlong"},
		DisplayName: "furlong",
		Factor:      0.0049709695378987,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"inch", "in"},
		DisplayName: "in",
		Factor:      39.370078740157,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"kilometer", "km"},
		DisplayName: "km",
		Factor:      0.001,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"league"},
		DisplayName: "league",
		Factor:      0.00020712373074577,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"meter", "m"},
		DisplayName: "m",
		Factor:      1,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"mile", "mi"},
		DisplayName: "mi",
		Factor:      0.00062137119223733,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"millimeter", "mm"},
		DisplayName: "mm",
		Factor:      1000,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"micrometer", "µm"},
		DisplayName: "µm",
		Factor:      1000000,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"micron", "µ"},
		DisplayName: "µ",
		Factor:      1000000,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"nanometer", "nm"},
		DisplayName: "nm",
		Factor:      1000000000,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"parsec"},
		DisplayName: "parsec",
		Factor:      3.2407792896393E-17,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"rod"},
		DisplayName: "rod",
		Factor:      0.19883878151595,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"yard", "yd"},
		DisplayName: "yd",
		Factor:      1.0936132983377,
	},
}
<commit_msg>added plural names<commit_after>package datatype

var lengthTypes = []*DataType{
	&DataType{
		Group:       GroupLength,
		Names:       []string{"angstrom", "angstroms"},
		DisplayName: "angstrom",
		Factor:      10000000000,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"centimeter", "centimetre", "centimeters", "centimetres", "cm"},
		DisplayName: "cm",
		Factor:      100,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"chain", "chains"},
		DisplayName: "chain",
		Factor:      0.049709695378987,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"decimeter", "decimetre", "decimeters", "decimetres", "dm"},
		DisplayName: "dm",
		Factor:      10,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"foot", "feet", "ft"},
		DisplayName: "ft",
		Factor:      0.54680664916885,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"fathom", "fathoms"},
		DisplayName: "fathom",
		Factor:      3.2808398950131,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"furlong", "furlongs"},
		DisplayName: "furlong",
		Factor:      0.0049709695378987,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"inch", "inches", "in"},
		DisplayName: "in",
		Factor:      39.370078740157,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"kilometer", "kilometre", "kilometers", "kilometres", "km"},
		DisplayName: "km",
		Factor:      0.001,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"league", "leagues"},
		DisplayName: "league",
		Factor:      0.00020712373074577,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"meter", "metre", "meters", "metres", "m"},
		DisplayName: "m",
		Factor:      1,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"mile", "miles", "mi"},
		DisplayName: "mi",
		Factor:      0.00062137119223733,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"millimeter", "millimetre", "millimeters", "millimetres", "mm"},
		DisplayName: "mm",
		Factor:      1000,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"micrometer", "micrometre", "micrometers", "micrometres", "µm"},
		DisplayName: "µm",
		Factor:      1000000,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"micron", "microns", "µ"},
		DisplayName: "µ",
		Factor:      1000000,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"nanometer", "nanometre", "nanometers", "nanometres", "nm"},
		DisplayName: "nm",
		Factor:      1000000000,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"parsec", "parsecs"},
		DisplayName: "parsec",
		Factor:      3.2407792896393E-17,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"rod", "rods"},
		DisplayName: "rod",
		Factor:      0.19883878151595,
	},
	&DataType{
		Group:       GroupLength,
		Names:       []string{"yard", "yards", "yd"},
		DisplayName: "yd",
		Factor:      1.0936132983377,
	},
}
<|endoftext|>